【更新】人员管理模块下增加导入导出以及该模块对应的国际化配置调整

pull/89/MERGE
小诺 2023-03-11 23:43:28 +08:00 committed by 俞宝山
parent 87f43b5f17
commit b51180bfbf
6 changed files with 324 additions and 98 deletions

View File

@ -69,5 +69,15 @@ export default {
// 给人员授权角色 // 给人员授权角色
grantRole(data) { grantRole(data) {
return request('grantRole', data) return request('grantRole', data)
},
// 用户导入
userImport(data) {
return request('import', data)
},
// 用户导出
userExport(data) {
return request('export', data, 'get', {
responseType: 'blob'
})
} }
} }

View File

@ -18,8 +18,9 @@ export default {
batchRemoveButton: 'batch Remove', batchRemoveButton: 'batch Remove',
detailButton: 'detail', detailButton: 'detail',
searchKey: 'Search Key', searchKey: 'Search Key',
imports: 'import', imports: 'Import',
more: 'More' more: 'More',
export: 'Export',
}, },
model: { model: {
user: 'user', user: 'user',
@ -58,5 +59,14 @@ export default {
userStatus: 'User Status', userStatus: 'User Status',
resetPassword: 'Reset Password', resetPassword: 'Reset Password',
role: 'Role', role: 'Role',
batchExportButton: 'Batch Export',
grantRole: 'Grant Role',
grantResource: 'Grant Resource',
grantPermission: 'Grant Permission',
exportUserInfo: 'Export UserInfo',
placeholderNameAndSearchKey: 'Please enter your name or keyword',
placeholderUserStatus: 'Please select status',
popconfirmDeleteUser: 'Are you sure you want to delete it',
popconfirmResatUserPwd: 'Are you sure you want to reset'
} }
} }

View File

@ -22,6 +22,7 @@ export default {
searchKey: '关键词', searchKey: '关键词',
imports: '导入', imports: '导入',
more: '更多', more: '更多',
export: '导出',
}, },
model: { model: {
user: '用户', user: '用户',
@ -60,5 +61,14 @@ export default {
userStatus: '用户状态', userStatus: '用户状态',
resetPassword: '重置密码', resetPassword: '重置密码',
role: '角色', role: '角色',
batchExportButton: '批量导出',
grantRole: '授权角色',
grantResource: '授权资源',
grantPermission: '授权权限',
exportUserInfo: '导出信息',
placeholderNameAndSearchKey: '请输入姓名或关键词',
placeholderUserStatus: '请选择状态',
popconfirmDeleteUser: '确定要删除吗?',
popconfirmResatUserPwd: '确定要重置吗?'
} }
} }

View File

@ -0,0 +1,105 @@
<template>
<a-drawer
title="导入导出"
:width="620"
:visible="visible"
:destroy-on-close="true"
:footer-style="{ textAlign: 'right' }"
@close="onClose"
>
<span
>导入数据格式严格按照系统模板进行数据录入请点击
<a-button type="primary" size="small" @click="downloadImportUserTemplate"></a-button>
</span>
<a-divider dashed />
<div>
<a-spin :spinning="impUploadLoading">
<a-upload-dragger :show-upload-list="false" :custom-request="customRequestLocal">
<p class="ant-upload-drag-icon">
<inbox-outlined></inbox-outlined>
</p>
<p class="ant-upload-text">单击或拖动文件到此区域进行上传</p>
<p class="ant-upload-hint">仅支持xlsxlsx格式文件</p>
</a-upload-dragger>
</a-spin>
</div>
<a-alert v-if="impAlertStatus" type="info" :show-icon="false" banner closable @close="onImpClose" class="mt-3">
<template #description>
<p>导入总数{{ impResultData.totalCount }} </p>
<p>导入成功{{ impResultData.successCount }} </p>
<div v-if="impResultData.errorCount > 0">
<p><span style="color: red">失败条数</span>{{ impResultData.errorCount }} </p>
<a-table :dataSource="impResultErrorDataSource" :columns="impErrorColumns" size="small" />
</div>
</template>
</a-alert>
</a-drawer>
</template>
<script setup name="bizUserImpExp">
import userApi from '@/api/sys/userApi'
import bizUserApi from '@/api/biz/bizUserApi'
import downloadUtil from '@/utils/downloadUtil'
const impUploadLoading = ref(false)
const impAlertStatus = ref(false)
const impResultData = ref({})
const impResultErrorDataSource = ref([])
//
const customRequestLocal = (data) => {
impUploadLoading.value = true
const fileData = new FormData()
fileData.append('file', data.file)
return bizUserApi
.userImport(fileData)
.then((res) => {
impAlertStatus.value = true
impResultData.value = res
impResultErrorDataSource.value = res.errorDetail
})
.finally(() => {
impUploadLoading.value = false
})
}
//
const onImpClose = () => {
impAlertStatus.value = false
}
const impErrorColumns = [
{
title: '索引',
dataIndex: 'index',
width: '80px'
},
{
title: '原因',
dataIndex: 'msg'
}
]
// emit
const emit = defineEmits({ successful: null })
//
let visible = ref(false)
const submitLoading = ref(false)
//
const onOpen = () => {
visible.value = true
}
//
const onClose = () => {
visible.value = false
//
onImpClose()
}
//
const downloadImportUserTemplate = () => {
userApi.userDownloadImportUserTemplate().then((res) => {
downloadUtil.resultDownload(res)
})
}
//
defineExpose({
onOpen
})
</script>

View File

@ -14,6 +14,39 @@
</a-card> </a-card>
</a-col> </a-col>
<a-col :span="19"> <a-col :span="19">
<a-card :bordered="false" style="margin-bottom: 10px">
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
<a-row :gutter="24">
<a-col :span="8">
<a-form-item name="searchKey" :label="$t('common.searchKey')">
<a-input
v-model:value="searchFormState.searchKey"
:placeholder="$t('user.placeholderNameAndSearchKey')"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item name="userStatus" :label="$t('user.userStatus')">
<a-select v-model:value="searchFormState.userStatus" :placeholder="$t('user.placeholderUserStatus')">
<a-select-option v-for="item in statusData" :key="item.dictValue" :value="item.dictValue">{{
item.name
}}</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="table.refresh(true)">
<template #icon><SearchOutlined /></template>
{{ $t('common.searchButton') }}
</a-button>
<a-button class="snowy-buttom-left" @click="() => searchFormRef.resetFields()">
<template #icon><redo-outlined /></template>
{{ $t('common.resetButton') }}
</a-button>
</a-col>
</a-row>
</a-form>
</a-card>
<a-card :bordered="false"> <a-card :bordered="false">
<s-table <s-table
ref="table" ref="table"
@ -26,43 +59,31 @@
:row-selection="options.rowSelection" :row-selection="options.rowSelection"
> >
<template #operator class="table-operator"> <template #operator class="table-operator">
<a-form <a-space>
ref="searchFormRef" <a-button type="primary" @click="form.onOpen()" v-if="hasPerm('bizUserAdd')">
name="advanced_search" <template #icon><plus-outlined /></template>
class="ant-advanced-search-form" <span>{{ $t('common.addButton') }}{{ $t('model.user') }}</span>
:model="searchFormState"
>
<a-row :gutter="24">
<a-col :span="6">
<a-form-item name="searchKey">
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入姓名或账号"></a-input>
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item name="userStatus">
<a-select v-model:value="searchFormState.userStatus" placeholder="请选择状态">
<a-select-option v-for="item in statusData" :key="item.dictValue" :value="item.dictValue">{{
item.name
}}</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="6">
<a-button type="primary" @click="table.refresh(true)">{{ $t('common.searchButton') }}</a-button>
<a-button class="snowy-buttom-left" @click="() => searchFormRef.resetFields()">{{
$t('common.resetButton')
}}</a-button>
</a-col>
<a-col :span="6">
<a-button type="primary" class="primaryAdd" @click="form.onOpen()" v-if="hasPerm('bizUserAdd')">
<span>{{ $t('common.addButton') }}{{ $t('model.bizUser') }}</span>
</a-button> </a-button>
<a-button danger @click="removeBatchUser()" v-if="hasPerm('bizUserBatchDelete')">{{ <a-button @click="ImpExpRef.onOpen()" v-if="hasPerm('bizUserImport')">
$t('common.batchRemoveButton') <template #icon><import-outlined /></template>
}}</a-button> <span>{{ $t('common.imports') }}</span>
</a-col> </a-button>
</a-row> <a-button @click="exportBatchUserVerify" v-if="hasPerm('bizUserBatchExport')">
</a-form> <template #icon><delete-outlined /></template>
{{ $t('user.batchExportButton') }}
</a-button>
<a-popconfirm
title="删除此信息?"
:visible="deleteVisible"
@visibleChange="deleteVisibleChange"
@confirm="deleteBatchUser"
>
<a-button danger v-if="hasPerm('bizUserBatchDelete')">
<template #icon><delete-outlined /></template>
{{ $t('common.batchRemoveButton') }}
</a-button>
</a-popconfirm>
</a-space>
</template> </template>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'avatar'"> <template v-if="column.dataIndex === 'avatar'">
@ -82,18 +103,35 @@
</template> </template>
<template v-if="column.dataIndex === 'action'"> <template v-if="column.dataIndex === 'action'">
<a @click="form.onOpen(record)" v-if="hasPerm('bizUserEdit')">{{ $t('common.editButton') }}</a> <a @click="form.onOpen(record)" v-if="hasPerm('bizUserEdit')">{{ $t('common.editButton') }}</a>
<a-divider type="vertical" v-if="hasPerm(['bizUserEdit', 'bizUserGrantRole'], 'and')" /> <a-divider type="vertical" v-if="hasPerm(['bizUserEdit', 'bizUserDelete'], 'and')" />
<a @click="selectRole(record)" v-if="hasPerm('bizUserGrantRole')"></a> <a-popconfirm :title="$t('user.popconfirmDeleteUser')" @confirm="removeUser(record)">
<a-divider type="vertical" v-if="hasPerm(['bizUserGrantRole', 'bizUserPwdReset'], 'and')" />
<a-popconfirm title="确定重置此人员密码?" @confirm="resetPassword(record)">
<a v-if="hasPerm('bizUserPwdReset')"></a>
</a-popconfirm>
<a-divider type="vertical" v-if="hasPerm(['bizUserPwdReset', 'bizUserDelete'], 'and')" />
<a-popconfirm title="确定要删除此人员吗?" @confirm="removeUser(record)">
<a-button type="link" danger size="small" v-if="hasPerm('bizUserDelete')">{{ <a-button type="link" danger size="small" v-if="hasPerm('bizUserDelete')">{{
$t('common.removeButton') $t('common.removeButton')
}}</a-button> }}</a-button>
</a-popconfirm> </a-popconfirm>
<a-divider type="vertical" v-if="hasPerm(['bizUserGrantRole', 'bizUserPwdReset'], 'and')" />
<a-dropdown v-if="hasPerm(['bizUserGrantRole', 'bizUserPwdReset'], 'and')">
<a class="ant-dropdown-link">
{{ $t('common.more') }}
<DownOutlined />
</a>
<template #overlay>
<a-menu>
<a-menu-item v-if="hasPerm('bizUserPwdReset')">
<a-popconfirm
:title="$t('user.popconfirmResatUserPwd')"
placement="topRight"
@confirm="resetPassword(record)"
>
<a>{{ $t('user.resetPassword') }}</a>
</a-popconfirm>
</a-menu-item>
<a-menu-item v-if="hasPerm('bizUserGrantRole')">
<a @click="selectRole(record)">{{ $t('user.grantRole') }}</a>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template> </template>
</template> </template>
</s-table> </s-table>
@ -108,13 +146,16 @@
:role-global="false" :role-global="false"
@onBack="roleBack" @onBack="roleBack"
/> />
<ImpExp ref="ImpExpRef" />
</template> </template>
<script setup name="bizUser"> <script setup name="bizUser">
import { message, Empty } from 'ant-design-vue' import { message, Empty } from 'ant-design-vue'
import { getCurrentInstance } from 'vue' import tool from '@/utils/tool'
import downloadUtil from '@/utils/downloadUtil'
import bizUserApi from '@/api/biz/bizUserApi' import bizUserApi from '@/api/biz/bizUserApi'
import roleSelectorPlus from '@/components/Selector/roleSelectorPlus.vue' import roleSelectorPlus from '@/components/Selector/roleSelectorPlus.vue'
import Form from './form.vue' import Form from './form.vue'
import ImpExp from './impExp.vue'
const columns = [ const columns = [
{ {
@ -163,11 +204,10 @@
title: '操作', title: '操作',
dataIndex: 'action', dataIndex: 'action',
align: 'center', align: 'center',
width: '240px' width: '220px'
}) })
} }
const { proxy } = getCurrentInstance() const statusData = tool.dictTypeList('COMMON_STATUS')
const statusData = proxy.$TOOL.dictTypeList('COMMON_STATUS')
const searchFormRef = ref() const searchFormRef = ref()
let defaultExpandedKeys = ref([]) let defaultExpandedKeys = ref([])
let searchFormState = reactive({}) let searchFormState = reactive({})
@ -181,7 +221,8 @@
const selectedRecord = ref({}) const selectedRecord = ref({})
const loading = ref(false) const loading = ref(false)
const cardLoading = ref(true) const cardLoading = ref(true)
const ImpExpRef = ref()
const deleteVisible = ref(false)
// Promise // Promise
const loadData = (parameter) => { const loadData = (parameter) => {
return bizUserApi.userPage(Object.assign(parameter, searchFormState)).then((res) => { return bizUserApi.userPage(Object.assign(parameter, searchFormState)).then((res) => {
@ -189,7 +230,9 @@
}) })
} }
// //
bizUserApi.userOrgTreeSelector().then((res) => { bizUserApi
.userOrgTreeSelector()
.then((res) => {
cardLoading.value = false cardLoading.value = false
if (res !== null) { if (res !== null) {
treeData.value = res treeData.value = res
@ -238,14 +281,18 @@
const editStatus = (record) => { const editStatus = (record) => {
loading.value = true loading.value = true
if (record.userStatus === 'ENABLE') { if (record.userStatus === 'ENABLE') {
bizUserApi.userDisableUser(record).then(() => { bizUserApi
.userDisableUser(record)
.then(() => {
table.value.refresh() table.value.refresh()
}) })
.finally(() => { .finally(() => {
loading.value = false loading.value = false
}) })
} else { } else {
bizUserApi.userEnableUser(record).then(() => { bizUserApi
.userEnableUser(record)
.then(() => {
table.value.refresh() table.value.refresh()
}) })
.finally(() => { .finally(() => {
@ -264,12 +311,49 @@
table.value.refresh() table.value.refresh()
}) })
} }
// //
const removeBatchUser = () => { const exportBatchUserVerify = () => {
if (selectedRowKeys.value.length < 1) { if ((selectedRowKeys.value.length < 1) & !searchFormState.searchKey & !searchFormState.userStatus) {
message.warning('请选择一条或多条数据') message.warning('请输入查询条件或勾选要导出的信息')
}
if (selectedRowKeys.value.length > 0) {
const params = selectedRowKeys.value.map((m) => {
return m
})
exportBatchUser(params)
return return
} }
if (searchFormState.searchKey || searchFormState.userStatus) {
const params = {
searchKey: searchFormState.searchKey,
userStatus: searchFormState.userStatus
}
exportBatchUser(params)
return
}
}
//
const exportBatchUser = (params) => {
bizUserApi.userExport(params).then((res) => {
downloadUtil.resultDownload(res)
})
}
//
const deleteVisibleChange = () => {
if (deleteVisible.value) {
deleteVisible.value = false
return false
}
if (selectedRowKeys.value.length < 1) {
message.warning('请选择一条或多条数据')
deleteVisible.value = false
return false
} else {
deleteVisible.value = true
}
}
//
const deleteBatchUser = () => {
const params = selectedRowKeys.value.map((m) => { const params = selectedRowKeys.value.map((m) => {
return { return {
id: m id: m

View File

@ -19,12 +19,15 @@
<a-row :gutter="24"> <a-row :gutter="24">
<a-col :span="8"> <a-col :span="8">
<a-form-item name="searchKey" :label="$t('common.searchKey')"> <a-form-item name="searchKey" :label="$t('common.searchKey')">
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入姓名或账号关键词"></a-input> <a-input
v-model:value="searchFormState.searchKey"
:placeholder="$t('user.placeholderNameAndSearchKey')"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="8"> <a-col :span="8">
<a-form-item name="userStatus" :label="$t('user.userStatus')"> <a-form-item name="userStatus" :label="$t('user.userStatus')">
<a-select v-model:value="searchFormState.userStatus" placeholder="请选择状态"> <a-select v-model:value="searchFormState.userStatus" :placeholder="$t('user.placeholderUserStatus')">
<a-select-option v-for="item in statusData" :key="item.dictValue" :value="item.dictValue">{{ <a-select-option v-for="item in statusData" :key="item.dictValue" :value="item.dictValue">{{
item.name item.name
}}</a-select-option> }}</a-select-option>
@ -67,7 +70,7 @@
</a-button> </a-button>
<a-button @click="exportBatchUserVerify"> <a-button @click="exportBatchUserVerify">
<template #icon><delete-outlined /></template> <template #icon><delete-outlined /></template>
批量导出 {{ $t('user.batchExportButton') }}
</a-button> </a-button>
<a-popconfirm <a-popconfirm
title="删除此信息?" title="删除此信息?"
@ -95,7 +98,7 @@
<template v-if="column.dataIndex === 'action'"> <template v-if="column.dataIndex === 'action'">
<a @click="form.onOpen(record)">{{ $t('common.editButton') }}</a> <a @click="form.onOpen(record)">{{ $t('common.editButton') }}</a>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a-popconfirm title="确定要删除此用户吗?" placement="topRight" @confirm="removeUser(record)"> <a-popconfirm :title="$t('user.popconfirmDeleteUser')" placement="topRight" @confirm="removeUser(record)">
<a-button type="link" danger size="small"> <a-button type="link" danger size="small">
{{ $t('common.removeButton') }} {{ $t('common.removeButton') }}
</a-button> </a-button>
@ -109,21 +112,25 @@
<template #overlay> <template #overlay>
<a-menu> <a-menu>
<a-menu-item> <a-menu-item>
<a-popconfirm title="确定重置此用户密码?" placement="topRight" @confirm="resetPassword(record)"> <a-popconfirm
:title="$t('user.popconfirmResatUserPwd')"
placement="topRight"
@confirm="resetPassword(record)"
>
<a>{{ $t('user.resetPassword') }}</a> <a>{{ $t('user.resetPassword') }}</a>
</a-popconfirm> </a-popconfirm>
</a-menu-item> </a-menu-item>
<a-menu-item> <a-menu-item>
<a @click="selectRole(record)"></a> <a @click="selectRole(record)">{{ $t('user.grantRole') }}</a>
</a-menu-item> </a-menu-item>
<a-menu-item> <a-menu-item>
<a @click="grantResourceFormRef.onOpen(record)"></a> <a @click="grantResourceFormRef.onOpen(record)">{{ $t('user.grantResource') }}</a>
</a-menu-item> </a-menu-item>
<a-menu-item> <a-menu-item>
<a @click="grantPermissionFormRef.onOpen(record)"></a> <a @click="grantPermissionFormRef.onOpen(record)">{{ $t('user.grantPermission') }}</a>
</a-menu-item> </a-menu-item>
<a-menu-item> <a-menu-item>
<a @click="exportUserInfo(record)"></a> <a @click="exportUserInfo(record)">{{ $t('user.exportUserInfo') }}</a>
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
</template> </template>