/** * 表头通过标签管理配置的模块的表格数据的混入 */ import { downFile, downloadFile, postAction } from '@api/manage' import Vue from 'vue' import { ACCESS_TOKEN } from '@/store/mutation-types' import store from '@/store' import { isHasPermission } from '../utils/hasPermission.js' import { FieldType } from '../enums/commonEnums.js' import { filterObj } from '../utils/util.js' import { mapGetters } from 'vuex' let importModalLoading // 需要数据字段翻译的属性类型 const NEED_DICT_FIELD_TYPE = [ FieldType.USER_SINGLE.value, FieldType.ORGAN_SINGLE.value, FieldType.ORGAN_MORE.value, FieldType.FILE_UP.value, FieldType.OPTION_SINGLE.value, FieldType.OPTION_MORE.value, FieldType.TREE.value, FieldType.TREE_SINGLE.value, FieldType.TREE_MODAL_SELECT.value, FieldType.TREE_MODAL_SELECT_SEARCH_DIFF.value, FieldType.TREE_MODAL_SELECT_SEARCH_CHILD.value ] const ZH = 'zh-cn' export const ConfigurableTableMixin = { data () { return { /* 分页参数 */ ipagination: { current: 1, pageSize: 10, pageSizeOptions: ['10', '30', '50', '100', '150', '200'], showTotal: (total, range) => { return range[0] + '-' + range[1] + ' ' + this.$t('total') + ' ' + total + ' ' + this.$t('strip') }, showQuickJumper: true, showSizeChanger: true, total: 0 }, /* 排序参数 */ isorter: { column: '', order: 'desc' }, columns: [], selectedRowKeys: [], selectionRows: [], dataSource: [], isTrue: false, // 是否显示 searchParams: {}, loading: false, orderBy: '1', orderByField: '', operateMaxNum: 1, // 操作列的column operateColumn: { title: this.$t('operation'), align: 'center', fixed: 'right', scopedSlots: { customRender: 'operation' } }, // type: '', // 这个字段不知道什么含义 filters: {}, // 固定的搜索条件 needFilterTableBtn: true, // 是否需要过滤没有权限的按钮 getTableHeaderFunc: null, // 获取表头的方法 tableSlotField: [], // 表格需要使用插槽的字段,dbFieldName disabledMixinsCreate: false, isBackTitle: false, // 是否是详情页有返回的标题 defaultSearchParams: {} } }, computed: { ...mapGetters(['defaultHeight']), yScrollHeight () { /** * 前几个使用变量是为了配合iframe嵌套,算出页面显示范围的高度 * 48px:分页器的高度 * 48px:a-card的padding(上24+下24) * 54px:表头行的高度 */ return `calc(100vh - ${this.defaultHeight['user-info-height']} - ${this.defaultHeight['breadcrumb-height']} - ${this.defaultHeight.oneLineSearchHeight} - ${this.defaultHeight.oneLineOperationHeight} - 48px - 48px - 54px - ${this.isBackTitle ? '56px' : '0px'})` }, importExcelUrl () { return `${window._CONFIG.domianURL}${this.url.importUrl}` }, tokenHeader () { const head = { 'X-Access-Token': Vue.ls.get(ACCESS_TOKEN) } let language = localStorage.getItem('language') || '' if (!language) { language = 'zh-cn' } const cut = language === 'zh-cn' ? 'zh' : 'en' head.Language = cut return head }, }, mounted () { if (this.disabledMixinsCreate) { return } this.searchParams = Object.assign({}, this.defaultSearchParams) this.getTableHeader() this.loadData() // 过滤没有权限的按钮 if (this.needFilterTableBtn) { this.operationList = this.operationList.filter(item => !item.has || isHasPermission(item.has)) console.log(this.operationList) } }, methods: { /** * 查询,通过search组件$emit事件获取查询参数 * @param queryParam */ searchQuery (queryParam) { Object.keys(queryParam).forEach(res => { if (queryParam[res] instanceof Array) { queryParam[res] = queryParam[res].join(',') } }) this.searchParams = queryParam this.getTableHeader() this.loadData(1) }, /** * 重置查询 */ searchReset () { this.searchParams = {} this.selectedRowKeys = [] this.getTableHeader() this.loadData(1) }, /** * 获取表头数据 */ getTableHeader () { if (!this.getTableHeaderFunc && typeof this.getTableHeaderFunc !== 'function') { return } const params = { module: this.flag } this.getTableHeaderFunc(params).then(res => { if (res.success) { this.columns = this.dealColumns(res.result || []) console.log(this.columns) this.isTrue = false this.$nextTick(() => { this.isTrue = true }) } }) }, /** * 处理表头数据 * @param columns * @returns {*[]} */ dealColumns (columns) { const tempColumns = [] columns.forEach(item => { // 可点击项加入插槽 if (item.urlClick === 'true') { item.scopedSlots = { customRender: 'urlClick' } } // 是否可排序 item.sorter = item.sortFlag === '1' item.width = 215 item.title = this.isChinese() ? item.dbFieldTxt : item.dbFieldEnName if (NEED_DICT_FIELD_TYPE.includes(item.fieldShowType)) { item.dataIndex = this.isChinese() ? item.dbFieldName + '_dictText' : item.dbFieldName + '_dictTextEn' } else { item.dataIndex = item.dbFieldName } // 在各自页面写此页面需要使用插槽的字段 if (this.tableSlotField && this.tableSlotField.includes(item.dbFieldName)) { item.scopedSlots = { customRender: item.dbFieldName } } tempColumns.push(item) }) if (this.showAction && this.operateColumn) { let width = 0 if (this.operationList && this.operationList.length > this.operateMaxNum + 1) { width += this.getTextWith(this.operationList[0].text) + this.getTextWith(this.$t('more')) } else if (this.operationList && this.operationList.length > 1) { width += this.getTextWith(this.operationList[0].text) + this.getTextWith(this.operationList[1].text) console.log(width) } const operateColumn = JSON.parse(JSON.stringify(this.operateColumn)) operateColumn.width = width || 100 tempColumns.push(operateColumn) } return tempColumns }, /** * 通过文字获取列宽 * @param text * @param fontStyle * @returns {number} */ getTextWith (text, fontStyle) { const canvas = document.createElement('canvas') const context = canvas.getContext('2d') context.font = fontStyle || '14px' // 设置字体样式 const dimension = context.measureText(text) return dimension.width + 60 }, getQueryParams () { const param = Object.assign({}, this.searchParams, this.isorter, this.filters) param.pageNo = this.ipagination.current param.pageSize = this.ipagination.pageSize console.log(param) return filterObj(param) }, loadData (arg) { if (!this.url.list) { this.$message.warning('请设置url.list属性!') return } // 加载数据 若传入参数1则加载第一页的内容 if (arg === 1) { this.ipagination.current = 1 } const params = this.getQueryParams() this.loading = true postAction(this.url.list, params).then((res) => { if (res.success) { if (res.result.current > 1 && res.result.records.length === 0) { this.ipagination.current = res.result.current - 1 this.loadData() return } this.dataSource = res.result.records || [] this.ipagination.total = res.result.total } else { this.$message.warn(res.message) } }).finally(() => { this.loading = false }) }, tableOnChange (pagination, filters, sorter) { if (this.$refs.tableRef && this.$refs.tableRef.resizing) { // 解决表格调整列宽触发排序的问题 const classNameOne = window.event.target.parentNode.children[0].children[0].children[1].children[0].children[0].className const classNameTwo = window.event.target.parentNode.children[0].children[0].children[1].children[0].children[1].className const target = window.event.target this.$nextTick(() => { target.parentNode.children[0].children[0].children[1].children[0].children[0].className = classNameOne target.parentNode.children[0].children[0].children[1].children[0].children[1].className = classNameTwo }) return } console.log(sorter) if (Object.keys(sorter).length > 0) { if (sorter.field.split('_')[sorter.field.split('_').length - 1] === 'dictText') { this.isorter.column = sorter.order ? sorter.field.slice(0, -9) : '' } else { this.isorter.column = sorter.order ? sorter.field : '' } this.isorter.order = sorter.order === 'ascend' ? 'asc' : 'desc' } this.ipagination = pagination this.loadData() }, onSelectChange (selectedRowKeys, selectionRows) { this.selectedRowKeys = selectedRowKeys this.selectionRows = selectionRows }, /* 导入 */ handleImportExcel (info) { // 限制导入大于 importMaxSize, 各个模块可以单独设置大小 if (this.importMaxSize && info.file.size > 1024 * 1024 * this.importMaxSize) { this.$message.error(this.$t('importMaxMsg', { size: this.importMaxSize })) info.fileList.pop() return } // 限制导入大于500MB if (info.file.size > 1024 * 1024 * 500) { this.$message.error('导入文件最大500MB!') info.fileList.pop() return } // 加一个大的提示 if (!this.loading) { importModalLoading = this.$info({ title: this.$t('tips'), content: {this.$t('importTip')} , keyboard: false }) } this.loading = true // 把知道了这个按钮去掉 this.$nextTick(() => { if (document.getElementsByClassName('ant-modal-confirm-btns')[0]) { document.getElementsByClassName('ant-modal-confirm-btns')[0].style = 'display:none' } }) if (info.file.status !== 'uploading') { console.log(info.file, info.fileList) } if (info.file.status === 'done') { // 销毁这个提示 importModalLoading && importModalLoading.destroy() this.loading = false if (info.file.response.success) { // this.$message.success(`${info.file.name} 文件上传成功`); if (info.file.response.code === 201) { const { message, result: { msg, fileUrl, fileName } } = info.file.response const href = window._CONFIG.domianURL + fileUrl this.$warning({ title: message, content: (
{msg}
具体详情请 点击下载
) }) } else { this.$message.success(info.file.response.message || `${info.file.name} ${this.$t('fileUploadSuccess')}`) } this.loadData() } else { if (Array.isArray(info.file.response.result) && info.file.response.result.length > 0) { this.messageLineFeed(this.$t('fileImportError'), info.file.response.result) } else if (info.file.response.message.indexOf('
') !== -1) { this.messageLineFeedByBr(this.$t('fileImportError'), info.file.response.message) } else { this.$message.warning(`${info.file.name} ${info.file.response.message}`) } } } else if (info.file.status === 'error') { // 销毁这个提示 importModalLoading && importModalLoading.destroy() this.loading = false if (info.file.response.status === 500) { const data = info.file.response const token = Vue.ls.get(ACCESS_TOKEN) if (token && data.message.includes('Token失效')) { this.error({ title: this.$t('loginExpired'), content: this.$t('loginExpiredMessage'), okText: this.$t('logInAgain'), mask: false, onOk: () => { store.dispatch('Logout').then(() => { Vue.ls.remove(ACCESS_TOKEN) window.location.reload() }) } }) } } else { this.$message.warning(`${this.$t('fileUploadFailed')}: ${info.file.msg} `) } } }, /** * 导出 * @param fileName * @param fileSuffix */ handleExportXls (fileName, fileSuffix = '.xls') { if (!fileName || typeof fileName !== 'string') { fileName = this.$t('exportFile') } const param = this.getQueryParams() if (this.selectedRowKeys && this.selectedRowKeys.length > 0) { param.selections = this.selectedRowKeys.join(',') } // 加一个大的提示 const modalLoading = this.$info({ title: this.$t('tips'), content: {this.$t('exportTip')} , keyboard: false }) // 把知道了这个按钮去掉 this.$nextTick(() => { document.getElementsByClassName('ant-modal-confirm-btns')[0].style = 'display:none' }) downFile(this.url.exportXlsUrl, param).then((data) => { if (!data) { this.$message.warning(this.$t('fileDownloadFail')) return } if (typeof window.navigator.msSaveBlob !== 'undefined') { window.navigator.msSaveBlob(new Blob([data], { type: 'application/vnd.ms-excel' }), fileName + fileSuffix) } else { const url = window.URL.createObjectURL(new Blob([data], { type: 'application/vnd.ms-excel' })) const link = document.createElement('a') link.style.display = 'none' link.href = url link.setAttribute('download', fileName + fileSuffix) document.body.appendChild(link) link.click() document.body.removeChild(link) // 下载完成移除元素 window.URL.revokeObjectURL(url) // 释放掉blob对象 } }).finally(() => { // 销毁这个提示 modalLoading.destroy() }) }, /** * 带文件导出 * @param fileName */ handleFileExport (fileName) { if (!fileName || typeof fileName !== 'string') { fileName = this.$t('exportWithFiles') } const param = this.getQueryParams() if (this.selectedRowKeys && this.selectedRowKeys.length > 0) { param.selections = this.selectedRowKeys.join(',') } // 加一个大的提示 const modalLoading = this.$info({ title: this.$t('tips'), content: {this.$t('exportTip')} , keyboard: false }) // 把知道了这个按钮去掉 this.$nextTick(() => { document.getElementsByClassName('ant-modal-confirm-btns')[0].style = 'display:none' }) downFile(this.url.exportZipUrl, param).then((data) => { if (!data) { this.$message.warning(this.$t('fileDownloadFail')) } }).finally(() => { // 销毁这个提示 modalLoading.destroy() }) }, // 下载导入模板 downTemplate (fileName, fileSuffix = '.xls') { if (!fileName || typeof fileName !== 'string') { fileName = this.$t('templateFile') } downloadFile(this.url.downTemplateUrl, fileName + fileSuffix) }, /** * 批量删除 */ batchDel () { if (!this.url.delete) { this.$message.warning('请设置url.deleteBatch属性!') return } if (this.selectedRowKeys.length <= 0) { this.$message.warning(this.$t('selectARecord')) return } const ids = this.selectedRowKeys.join(',') this.$confirm({ title: this.$t('confirmBatchDeletion'), content: this.$t('deleteAData'), onOk: () => { this.loading = true postAction(this.url.deleteBatch, { ids: ids }).then((res) => { if (res.success) { // 重新计算分页问题 this.reCalculatePage(this.selectedRowKeys.length) this.$message.success(res.message) this.loadData() this.onClearSelected() } else { this.$message.warning(res.message) } }).finally(() => { this.loading = false }) } }) }, reCalculatePage (count) { // 总数量-count const total = this.ipagination.total - count // 获取删除后的分页数 const currentIndex = Math.ceil(total / this.ipagination.pageSize) // 删除后的分页数<所在当前页 if (currentIndex < this.ipagination.current) { this.ipagination.current = currentIndex } console.log('currentIndex', currentIndex) }, /** * 删除 * @param id */ handleDelete (id) { if (!this.url.delete) { this.$message.warning('请设置url.delete属性!') return } const that = this this.$confirm({ title: this.$t('confirmDeletion'), content: this.$t('areYouSure'), onOk: () => { postAction(that.url.delete, { ids: id }).then((res) => { if (res.success) { that.$message.success(res.message) // 判断当前删除的数据是否是最后一页的最后一条数据,如果是的话页码减一 if (that.ipagination.current > 1 && ((that.ipagination.current - 1) * that.ipagination.pageSize) + 1 === that.ipagination.total) { that.ipagination.current -= 1 } that.loadData() } else { that.$message.warning(res.message) } }) } }) }, handleEdit (record) { this.$refs.modalForm.edit(record) this.$refs.modalForm.title = this.$t('edit') this.$refs.modalForm.disableSubmit = false }, handleAdd () { this.$refs.modalForm.add() this.$refs.modalForm.title = this.$t('newlyAdded') this.$refs.modalForm.disableSubmit = false }, modalFormOk () { // 新增/修改 成功时,重载列表 this.loadData() // 清空列表选中 this.onClearSelected() }, onClearSelected () { this.selectedRowKeys = [] this.selectionRows = [] }, handleImportByFileId (fileId) { // TODO 完善通过文件id上传文件的方法 }, /** * 循环操作按钮的操作 * @param operation * @param record */ operationClick (operation, record) { if (operation.clickEvent === 'handleDelete') { this.handleDelete(record.id) } else { this[operation.clickEvent](record) } }, /** * 对数组形式后端错误信息进行处理 * @param title * @param content */ messageLineFeed (title, content) { const htmlDom = [] content.map(tt => { if (tt) { htmlDom.push((
{tt}
)) } }) this.$warning({ title: title, closable: true, width: 800, content: () =>
{htmlDom}
}) this.$nextTick(() => { document.getElementsByClassName('ant-modal-confirm-btns')[0].style = 'display: inline-block' document.getElementsByClassName('ant-modal-confirm-content')[0].style = 'max-height: calc(100vh - 300px);overflow-y: auto;' }) }, /** * 对包含
的后端错误信息进行处理 * @param title * @param content */ messageLineFeedByBr (title, content) { // 可以用分号和
换行 content = content.replaceAll(`
`, ';') const messageArr = content.split(';') const htmlDom = [] messageArr.map(tt => { if (tt) { htmlDom.push((
{tt}
)) } }) this.$warning({ title: title, closable: true, width: 800, content: () =>
{htmlDom}
}) this.$nextTick(() => { document.getElementsByClassName('ant-modal-confirm-btns')[0].style = 'display: inline-block' document.getElementsByClassName('ant-modal-confirm-content')[0].style = 'max-height: calc(100vh - 300px);overflow-y: auto;' }) } } }