You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

690 lines
21 KiB

<template>
<div class="app-container" v-loading="state.loading">
<el-card class="search-container" v-if="!(props.hideSearch && props.hideSetColums)">
<el-form :inline="true" v-if="!props.hideSearch">
<el-form-item
v-auth="(props.authName || props.apiName) + state.searchBtnOptions['search'].auth"
v-for="(item,index) in props.searchOptions"
:key="index"
:label="item.label">
<!-- 文本 -->
<el-input
v-if="item.type == 'input' && !item.hide"
v-model="props.searchFilter[item.prop]"
:placeholder="item.label"
:clearable="!item.noClear"
/>
<!-- 数字 -->
<el-input-number
v-if="item.type == 'number' && !item.hide"
v-model="props.searchFilter[item.prop]"
:min="item.min"
:max="item.max"
/>
<!-- 时间区域 -->
<el-date-picker
v-if="item.type == 'datetimerange' && !item.hide"
v-model="props.searchFilter[item.prop]"
type="datetimerange"
start-placeholder="起始时间"
end-placeholder="结束时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
style="width:370px"
/>
<!-- 选择框 -->
<el-select
v-if="item.type == 'select' && !item.hide"
v-model="props.searchFilter[item.prop]"
:filterable="!item.noSearch"
placeholder="请选择"
style="width: 240px"
:clearable="!item.noClear"
>
<el-option
v-for="(op,op_index) in item.options"
:key="op_index"
:label="op.label"
:value="op.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<div style="margin-right:10px" v-for="(btn,btn_key) in props.searchButtons" :key="btn_key">
<!-- 导出 -->
<el-dropdown
v-auth="(props.authName || props.apiName) + state.searchBtnOptions[btn].auth"
:hide-on-click="false"
v-if="state.searchBtnOptions[btn].auth == ':export'"
>
<el-button
:icon="state.searchBtnOptions[btn].icon"
:type="state.searchBtnOptions[btn].type">
{{state.searchBtnOptions[btn].label}}
<el-icon class="el-icon--right"><arrow-down /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="exportHandle()">按条件导出【当前页】</el-dropdown-item>
<el-dropdown-item divided @click="exportHandle(true)">按条件导出【全部】</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<!-- 其他按钮 -->
<el-button
v-else
:icon="state.searchBtnOptions[btn].icon"
v-auth="(props.authName || props.apiName) + state.searchBtnOptions[btn].auth"
:type="state.searchBtnOptions[btn].type"
@click="searchBtnHandle(btn)"
>{{state.searchBtnOptions[btn].label}}</el-button>
</div>
</el-form-item>
</el-form>
<!-- 字段设置 -->
<setColumsPop
style="margin-left: auto;"
v-if="!props.hideSetColums && state.innerTableColumns"
:localTableColumnsName="state.localTableColumnsName"
:initTableColums="state.initTableColums"
:tableColumns="state.innerTableColumns"
></setColumsPop>
</el-card>
<el-card class="paged-table-container">
<elTable
v-if="state.innerTableColumns"
:specialLocalColumnName="props.specialLocalColumnName"
:columnWidth="props.columnWidth"
:columnHeaderAlign="props.columnHeaderAlign"
:columnAlign="props.columnAlign"
:tableData="state.tableData"
:tableColumns="state.innerTableColumns"
@sortChange="sortChange"
:leftOperation="props.leftOperation"
@leftOperationHadel="leftOperationHadel"
:leftOperationColumnWidth="props.leftOperationColumnWidth"
:rightOperation="getRightOperation()"
@rightOperationHadel="rightOperationHadel"
:multipleTable="props.multipleTable"
@tableSelectionHandle="tableSelectionHandle"
:tableRowClassName="props.tableRowClassName"
:tableCellClassName="props.tableCellClassName"
></elTable>
<elPager
style="margin-top: 15px;float:right"
:pager="state.pager"
@pageSizeChange="pageSizeChange"
@pageCurrentChange="pageCurrentChange"
></elPager>
</el-card>
<!-- 导入弹窗 -->
<importPop
ref="importPopRef"
:apiName="props.apiName"
@success="importSuccess"
/>
<!-- 编辑弹窗 -->
<apiEditPop
ref="apiEditPopRef"
:apiName="props.apiName"
@submitEditForm="submitEditForm"
:formRules="props.apiEditFormRules"
/>
</div>
</template>
<script setup>
defineOptions({ name: 'tablePage' })
import store from '@/stores'
import apiTableColumns from '@/utils/common/apiTableColumns'
import { reactive, ref, onMounted,computed,defineExpose } from 'vue'
import {
getCommonPost,
getCommonPaged,
getCommonDeatailPaged,
postCommonExport,
postCommonCreate,
putCommonUpdate,
deleteCommonApi,
getCommonCustominvoke
} from '@/api/common/index'
import { ElMessageBox, ElMessage,ElTable, ElTableColumn } from 'element-plus'
import elTable from '@/components/elTable/index.vue'
import elPager from '@/components/elPager/index.vue'
import setColumsPop from '@/components/setColumsPop/index.vue'
import { getPageParamsForFilter,getLocalTableColumnsName } from '@/utils/common/index'
import { downloadByData } from '@/utils/download'
import importPop from '@/components/importPop/index.vue'
import apiEditPop from '@/components/apiEditPop/index.vue'
import { formatDate } from '@/utils/formatTime'
import apiServeNames from '@/utils/common/apiServeNames'
import { useRoute } from 'vue-router'
const route = useRoute()
const userStore = store.userStore()
const userInfo = userStore.state
const state = reactive({
loading:false,
searchBtnOptions:{
search:{icon:'Search',auth:':page',label:'查询',type:null},
create:{icon:'Plus',auth:':create',label:'新增',type:'primary'},
import:{icon:'BottomRight',auth:':import',label:'导入',type:'warning'},
export:{icon:'TopRight',auth:':export',label:'导出',type:'success'},
custominvoke:{icon:'Position',auth:':custominvoke',label:'手动开关',type:'primary'},
},
innerTableColumns:null,
tableData:[],
// table排序处理
sortFilter:{
sortBy:undefined,
isAscending:undefined
},
pager:{
page: 1,
pageSize: 10,
total: 1,
},
tableSelectList:[],
initTableColums:[],//初始化表头,未从缓存获取之前
localTableColumnsName:null
})
const props = defineProps({
// api名称
apiName: {
type: String,
default: null
},
// 特殊的分页api
specialPageApi:{
type: String,
default: null
},
// 特殊的column名称(没有默认apiName)
specialColumnName:{
type: String,
default: null
},
// 特殊的存储column名称(没有默认apiName)
specialLocalColumnName:{
type: String,
default: null
},
// 特殊权限前缀(如果没有默认使用apiName)
authName:{
type: String,
default: null
},
// api类型 detailApi:走getdetail接口,不传或者pageApi:走getdatapaged接口
apiType: {
type: String,
default: null
},
// 隐藏表头搜索
hideSearch:{
type: Boolean,
default: false
},
// 行class
tableRowClassName:{
type: Function,
default: null
},
// 单元格class
tableCellClassName:{
type: Function,
default: null
},
// 多选
multipleTable:{
type: Boolean,
default: false
},
// 左侧操作列
leftOperation:{
type: Object,
default: null
},
// 左侧操作列宽度
leftOperationColumnWidth:{
type: Number,
default: 120
},
// 右侧操作列
rightOperation:{
type: [Object,String],
default: null
},
// 右侧操作列,特殊自定义格式下,包含api操作
showApiRightOperation:{
type: Object,
default: null
},
// 右侧通用按钮特殊字段判断隐藏规则,默认为编辑删除,writeState=true不可操作,
// 如有特殊规则,则使用该方法特殊处理,当前判断方式为“=”,如后期有其他需求再进行封装
apiRightHideConfig:{
type: Object,
default: {
apiUpdate:{prop:'writeState',ruleValue:true},
apiDelete:{prop:'writeState',ruleValue:true},
}
},
// table表头
tableColumns: {
type: Object,
default: null
},
// 查询配置
searchOptions: {
type: Object,
default: []
},
// 查询按钮
searchButtons: {
type: Object,
default: ['search','export']
},
// table查询数据filter
searchFilter: {
type: Object,
default: {}
},
// table查询数据filter的特殊条件,如果没有则走colum中的配置
// 示例:infoSearchFilterOptions:{ TableName:{action:'=='}},
searchFilterOptions: {
type: Object,
default: {}
},
// 表头宽度
columnWidth:{
type: Number,
default: 120
},
// 表头对齐
columnHeaderAlign:{
type: String,
default: 'center'
},
// 表内容对齐
columnAlign:{
type: String,
default: 'center'
},
// 表单规则
apiEditFormRules:{
type: Object,
default: null
},
// 隐藏字段设置
hideSetColums:{
type: Boolean,
default: false
}
})
// 获取表头 noFilter 不需要处理字段设置(用于编辑和新增)
function getTableColumns(noFilter){
if(noFilter){
return props.tableColumns || apiTableColumns[props.apiName]
}else{
// 字段设置中,存入location的名字
let _localColumName = props.specialLocalColumnName || useRoute().name
state.localTableColumnsName = getLocalTableColumnsName(_localColumName)
let _local = JSON.parse(localStorage.getItem(state.localTableColumnsName))
// 获取列表配置
let _apiColums = props.specialColumnName || props.apiName
state.initTableColums = props.tableColumns || apiTableColumns[_apiColums]
let _list = (_local && _local != null && _local != undefined) ? _local : JSON.parse(JSON.stringify(state.initTableColums))
state.innerTableColumns = _list
return _list
}
}
const emits = defineEmits([
'leftOperationHadel',
'rightOperationHadel',
'tableSelectionHandle'
])
// table多选
function tableSelectionHandle (val){
state.tableSelectList = val
emits('tableSelectionHandle',val)
}
// 左侧操作列
function leftOperationHadel(btn,scope) {
emits('leftOperationHadel',btn,scope)
}
// 获取右侧操作列
function getRightOperation() {
// 自定义右侧列,且不需要默认api通用操作
if(typeof props.rightOperation == 'object' && !props.showApiRightOperation){
return props.rightOperation
}
// 无自定义操作,或者有自定义且需要默认api操作
else if(
(typeof props.rightOperation == 'object' && props.showApiRightOperation)
|| typeof props.rightOperation == 'string'
){
// 格式化默认api按钮合集
let _apiArr = props.showApiRightOperation || props.rightOperation.split(',')
let _config = {
apiUpdate:{label:'编辑',type:'warning'},
apiDelete:{label:'删除',type:'danger'},
}
let _btns = []
if(_apiArr && _apiArr.length > 0){
_apiArr.forEach(item => {
_btns.push({
label:_config[item].label,
name:item,
link:true,
type:_config[item].type,
auth:(props.authName || props.apiName)+':'+item,
hide:(row,scope) => {return row[props.apiRightHideConfig[item].prop] == props.apiRightHideConfig[item].ruleValue}
})
});
}
// 如果有自定义按钮,合并默认api按钮
if(typeof props.rightOperation == 'object'){
_btns = [..._btns,...props.rightOperation]
}
return _btns
}
}
// 右侧操作列操作
const apiEditPopRef = ref()
function rightOperationHadel(btn,scope) {
// 通用编辑
if(btn.name == 'apiUpdate'){
let _tableColums = props.tableColumns || apiTableColumns[props.apiName]
let _list = _tableColums.filter(item => !item.noEdit)
apiEditPopRef.value.open(_list,scope.row)
}
// 通用删除
if(btn.name == 'apiDelete'){
ElMessageBox.confirm(`是否确定删除?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
state.loading = true
deleteCommonApi(props.apiName,scope.row.uId)
.then(res=>{
ElMessage.success('操作成功!')
getTableData(1);
})
.finally(()=>{state.loading = false})
})
}
emits('rightOperationHadel',btn,scope)
}
// 编辑表单提交
const submitEditForm = async (type,formData,formConfig) => {
apiEditPopRef.value.validate((valid) => {
if(valid){
// 新增
if(type == 'create'){
if(formData.hasOwnProperty('createUser')){formData.createUser = userInfo.realName}
if(formData.hasOwnProperty('createByUser')){formData.createByUser = userInfo.realName}
if(formData.hasOwnProperty('createTime')){formData.createTime = formatDate(new Date(), "YYYY-mm-dd HH:MM:SS")}
apiEditPopRef.value.changeLoading(true)
postCommonCreate(props.apiName,formData)
.then(res=>{
apiEditPopRef.value.close()
ElMessage.success('操作成功!')
getTableData(1);
})
.catch(err=>{ElMessage.error('操作失败!')})
.finally(()=>{apiEditPopRef.value.changeLoading(false)})
}else{
// 修改人信息处理
if(
(formData.hasOwnProperty('updateByUser') && formData.hasOwnProperty('updateTime'))
|| (formData.hasOwnProperty('updateUser') && formData.hasOwnProperty('updateTime'))
){
if(formData.hasOwnProperty('updateByUser')){formData.updateByUser = userInfo.realName}
if(formData.hasOwnProperty('updateUser')){formData.updateUser = userInfo.realName}
if(formData.hasOwnProperty('updateTime')){formData.updateTime = formatDate(new Date(), "YYYY-mm-dd HH:MM:SS")}
}else{
// 特殊不处理页面
let _notChange=['taskconifgure','customlog']
if(_notChange.indexOf(props.apiName) < 0){
formData.remark= `修改信息:${userInfo.realName} ${formatDate(new Date(), "YYYY-mm-dd HH:MM:SS")}`
}
}
apiEditPopRef.value.changeLoading(true)
putCommonUpdate(props.apiName,formData)
.then(res=>{
apiEditPopRef.value.close()
ElMessage.success('操作成功!')
getTableData(1);
})
.catch(err=>{ElMessage.error('操作失败!')})
.finally(()=>{apiEditPopRef.value.changeLoading(false)})
}
}
})
}
// 格式化页面传参
function getPageParams(pageSize){
let _filters = []
if(props.hideSearch){
_filters = props.searchFilter
}else{
function __getAction (prop,action){
if(props.searchFilterOptions && props.searchFilterOptions[prop] && props.searchFilterOptions[prop].action){
return props.searchFilterOptions[prop].action
}else{
return action
}
}
for(let i in props.searchFilter){
let _item = props.searchOptions.filter(item=>item.prop == i)
let _type = (_item && _item.length > 0) ? _item[0].type : null
if((props.searchFilter[i] || props.searchFilter[i] == 0) && props.searchFilter[i] != ""){
// 时间区域格式
if(_type == 'datetimerange'){
_filters.push(
{
logic: "And",
column: i,
action: __getAction(i,'>='),
value: props.searchFilter[i][0]
}
)
_filters.push(
{
logic: "And",
column: i,
action: __getAction(i,'<='),
value: props.searchFilter[i][1]
}
)
}else{
let _action = 'like'
let _EqualTypes = ['tagFilter','filter','number','select']//等于情况的类型
if(_EqualTypes.indexOf(_type) >= 0){
_action = '=='
}
_filters.push(
{
logic: "And",
column: i,
action: __getAction(i,_action),
value: props.searchFilter[i]
}
)
}
}
}
}
let _pageParams = getPageParamsForFilter({
pageNumber:state.pager.page,
pageSize:pageSize || state.pager.pageSize,
sortBy:state.sortFilter.sortBy,
isAscending:state.sortFilter.isAscending,
condition:{
filters:_filters
}
})
return _pageParams
}
// 获取页面数据
function getTableData(page,callback) {
state.loading = true
if(!page)page = state.pager.page
if(page)state.pager.page = page
if(props.apiType == 'detailApi'){
getCommonDeatailPaged(props.apiName,getPageParams())
.then((resp) => {
state.tableData = resp.data.data
state.pager.total = resp.data.totalCount
if(callback)callback(resp)
})
.catch(err=>{ElMessage.error('数据获取失败!')})
.finally(() => (state.loading = false))
}else if(props.specialPageApi){
getCommonPost(props.specialPageApi,getPageParams())
.then((resp) => {
state.tableData = resp.data.data
state.pager.total = resp.data.totalCount
if(callback)callback(resp)
})
.catch(err=>{ElMessage.error('数据获取失败!')})
.finally(() => (state.loading = false))
}
else{
getCommonPaged(props.apiName,getPageParams())
.then((resp) => {
state.tableData = resp.data.data
state.pager.total = resp.data.totalCount
if(callback)callback(resp)
})
.catch(err=>{ElMessage.error('数据获取失败!')})
.finally(() => (state.loading = false))
}
}
// 导出
function exportHandle(isAll){
state.loading = true
//同步数据查询
getTableData(1,(res=>{
let _params = getPageParams()
if(isAll){_params = getPageParams(res.data.totalCount)}
postCommonExport(props.apiName,_params)
.then((res) => {
let _str = isAll ? '全部' : '当页'
downloadByData(res.data,route.meta.title+`_按条件导出${_str}.xlsx`)
})
.catch(err=>{ElMessage.error('操作失败!')})
.finally(() => (state.loading = false))
}))
}
const importPopRef = ref()
// 按钮功能
function searchBtnHandle(btn){
// 查询
if(btn == 'search'){
getTableData(1)
}
// 新增
else if (btn == 'create'){
let _tableColums = props.tableColumns || apiTableColumns[props.apiName]
let _list = _tableColums.filter(item => !item.noEdit)
apiEditPopRef.value.open(_list)
}
// 导入
else if (btn == 'import'){
importPopRef.value.open()
}
// 导出(按条件导出当前页)
// else if (btn == 'export'){
// exportHandle()
// }
// 手动开关
else if (btn == 'custominvoke'){
ElMessageBox.confirm('是否确定操作手动开关?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
state.loading = true
let _data = {
taskName:apiServeNames[props.apiName].taskName,
client:'Chery'
}
getCommonCustominvoke(_data)
.then((res) => {
ElMessage.success('操作成功!')
getTableData(1)//同步数据查询
})
.finally(() => (state.loading = false))
})
}
}
// 排序
function sortChange(data) {
const { prop, order } = data;
if (!prop || !order) {
state.sortFilter.sortBy = undefined;
state.sortFilter.isAscending = undefined;
getTableData(1);
return;
}
state.sortFilter.sortBy = prop;
state.sortFilter.isAscending = (order == "ascending");
getTableData(1);
}
// 导入成功之后todo
function importSuccess(response,importDate){
getTableData()
}
// size-change
function pageSizeChange(pageSize){
state.pager.pageSize = pageSize
getTableData(1)
}
// current-change
function pageCurrentChange(page){
getTableData(page)
}
onMounted(() => {
getTableColumns()
getTableData()
})
defineExpose({
state,
getTableData
});
</script>
<style scoped lang="scss">
::v-deep .search-container {
.el-card__body{
display:flex;
justify-content: space-between;
}
}
</style>