mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-10-28 16:57:08 +08:00
Merge branch 'dev' of https://github.com/continew-org/continew-admin-ui into dev
This commit is contained in:
@@ -25,6 +25,15 @@ export type UserDetailResp = UserResp & {
|
|||||||
pwdResetTime?: string
|
pwdResetTime?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UserImportResp {
|
||||||
|
importKey: string
|
||||||
|
totalRows: number
|
||||||
|
validRows: number
|
||||||
|
duplicateUserRows: number
|
||||||
|
duplicateEmailRows: number
|
||||||
|
duplicatePhoneRows: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface UserQuery {
|
export interface UserQuery {
|
||||||
description?: string
|
description?: string
|
||||||
status?: number
|
status?: number
|
||||||
|
|||||||
@@ -30,10 +30,25 @@ export function deleteUser(ids: string | Array<string>) {
|
|||||||
|
|
||||||
/** @desc 导出用户 */
|
/** @desc 导出用户 */
|
||||||
export function exportUser(query: System.UserQuery) {
|
export function exportUser(query: System.UserQuery) {
|
||||||
return http.download<any>(`${BASE_URL}/export`, query)
|
return http.download(`${BASE_URL}/export`, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @desc 重置密码 */
|
/** @desc 重置密码 */
|
||||||
export function resetUserPwd(data: any, id: string) {
|
export function resetUserPwd(data: any, id: string) {
|
||||||
return http.patch(`${BASE_URL}/${id}/password`, data)
|
return http.patch(`${BASE_URL}/${id}/password`, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @desc 下载用户导入模板 */
|
||||||
|
export function downloadImportUserTemplate() {
|
||||||
|
return http.download(`${BASE_URL}/downloadImportUserTemplate`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 解析用户导入数据 */
|
||||||
|
export function parseImportUser(data: FormData) {
|
||||||
|
return http.post(`${BASE_URL}/parseImportUser`, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 导入用户 */
|
||||||
|
export function importUser(data: any) {
|
||||||
|
return http.post(`${BASE_URL}/import`, data)
|
||||||
|
}
|
||||||
|
|||||||
196
src/views/system/user/UserImportModal.vue
Normal file
196
src/views/system/user/UserImportModal.vue
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
<template>
|
||||||
|
<a-drawer
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="导入用户"
|
||||||
|
:mask-closable="false"
|
||||||
|
:esc-to-close="false"
|
||||||
|
:width="width >= 600 ? 600 : '100%'"
|
||||||
|
ok-text="确认导入"
|
||||||
|
cancel-text="取消导入"
|
||||||
|
@before-ok="save"
|
||||||
|
@close="reset"
|
||||||
|
>
|
||||||
|
<a-form ref="formRef" :model="form" size="large" auto-label-width>
|
||||||
|
<a-alert v-if="!form.disabled" :show-icon="false" style="margin-bottom: 15px">
|
||||||
|
数据导入请严格按照模板填写,格式要求和新增一致!
|
||||||
|
<template #action>
|
||||||
|
<a-button size="small" type="primary" @click="downloadTemplate">下载模板</a-button>
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
<fieldset>
|
||||||
|
<legend>1.上传解析文件</legend>
|
||||||
|
<div class="file-box">
|
||||||
|
<a-upload draggable
|
||||||
|
:custom-request="handleUpload"
|
||||||
|
:limit="1"
|
||||||
|
:show-retry-butto="false"
|
||||||
|
:show-cancel-button="false" tip="仅支持xls、xlsx格式"
|
||||||
|
:file-list="uploadFile"
|
||||||
|
accept=".xls, .xlsx, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="dataResult.importKey">
|
||||||
|
<div class="file-box">
|
||||||
|
<a-space size="large">
|
||||||
|
<a-statistic title="总计行数" :value="dataResult.totalRows" />
|
||||||
|
<a-statistic title="正常行数" :value="dataResult.validRows" />
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="file-box">
|
||||||
|
<a-space size="large">
|
||||||
|
<a-statistic title="已存在用户" :value="dataResult.duplicateUserRows" />
|
||||||
|
<a-statistic title="已存在邮箱" :value="dataResult.duplicateEmailRows" />
|
||||||
|
<a-statistic title="已存在手机" :value="dataResult.duplicatePhoneRows" />
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>2.导入策略</legend>
|
||||||
|
<a-form-item label="用户已存在" field="duplicateUser">
|
||||||
|
<a-radio-group v-model="form.duplicateUser" type="button">
|
||||||
|
<a-radio :value="1">跳过该行</a-radio>
|
||||||
|
<a-radio :value="3">停止导入</a-radio>
|
||||||
|
<a-radio :value="2">修改数据</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="邮箱已存在" field="duplicateEmail">
|
||||||
|
<a-radio-group v-model="form.duplicateEmail" type="button">
|
||||||
|
<a-radio :value="1">跳过该行</a-radio>
|
||||||
|
<a-radio :value="3">停止导入</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="手机已存在" field="duplicatePhone">
|
||||||
|
<a-radio-group v-model="form.duplicatePhone" type="button">
|
||||||
|
<a-radio :value="1">跳过该行</a-radio>
|
||||||
|
<a-radio :value="3">停止导入</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="默认状态" field="defaultStatus">
|
||||||
|
<a-switch
|
||||||
|
v-model="form.defaultStatus"
|
||||||
|
:checked-value="1"
|
||||||
|
:unchecked-value="2"
|
||||||
|
checked-text="启用"
|
||||||
|
unchecked-text="禁用"
|
||||||
|
type="round"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</fieldset>
|
||||||
|
</a-form>
|
||||||
|
</a-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { type FormInstance, Message, type RequestOption } from '@arco-design/web-vue'
|
||||||
|
import { useWindowSize } from '@vueuse/core'
|
||||||
|
import { type UserImportResp, downloadImportUserTemplate, importUser, parseImportUser } from '@/apis'
|
||||||
|
import { useDownload, useForm } from '@/hooks'
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'save-success'): void
|
||||||
|
}>()
|
||||||
|
const { width } = useWindowSize()
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
const uploadFile = ref([])
|
||||||
|
|
||||||
|
const { form, resetForm } = useForm({
|
||||||
|
errorPolicy: 1,
|
||||||
|
duplicateUser: 1,
|
||||||
|
duplicateEmail: 1,
|
||||||
|
duplicatePhone: 1,
|
||||||
|
defaultStatus: 1
|
||||||
|
})
|
||||||
|
|
||||||
|
const dataResult = ref<UserImportResp>({
|
||||||
|
importKey: '',
|
||||||
|
totalRows: 0,
|
||||||
|
validRows: 0,
|
||||||
|
duplicateUserRows: 0,
|
||||||
|
duplicateEmailRows: 0,
|
||||||
|
duplicatePhoneRows: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
dataResult.value.importKey = ''
|
||||||
|
uploadFile.value = []
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const onImport = () => {
|
||||||
|
reset()
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载模板
|
||||||
|
const downloadTemplate = () => {
|
||||||
|
useDownload(() => downloadImportUserTemplate())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传解析导入数据
|
||||||
|
const handleUpload = (options: RequestOption) => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
(async function requestWrap() {
|
||||||
|
const { onProgress, onError, onSuccess, fileItem, name = 'file' } = options
|
||||||
|
onProgress(20)
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append(name as string, fileItem.file as Blob)
|
||||||
|
try {
|
||||||
|
const res = await parseImportUser(formData)
|
||||||
|
dataResult.value = res.data
|
||||||
|
Message.success('上传解析成功')
|
||||||
|
onSuccess(res)
|
||||||
|
} catch (error) {
|
||||||
|
onError(error)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
return {
|
||||||
|
abort() {
|
||||||
|
controller.abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行导入
|
||||||
|
const save = async () => {
|
||||||
|
try {
|
||||||
|
if (!dataResult.value.importKey) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
form.importKey = dataResult.value.importKey
|
||||||
|
const res = await importUser(form)
|
||||||
|
Message.success(`导入成功,新增${res.data.insertRows},修改${res.data.updateRows}`)
|
||||||
|
emit('save-success')
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ onImport })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
fieldset {
|
||||||
|
padding: 15px 15px 0 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border: 1px solid var(--color-neutral-3);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset legend {
|
||||||
|
color: rgb(var(--gray-10));
|
||||||
|
padding: 2px 5px 2px 5px;
|
||||||
|
border: 1px solid var(--color-neutral-3);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-box {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -13,21 +13,31 @@
|
|||||||
</a-col>
|
</a-col>
|
||||||
<a-col :xs="24" :sm="16" :md="17" :lg="18" :xl="19" :xxl="20" flex="1" class="h-full ov-hidden">
|
<a-col :xs="24" :sm="16" :md="17" :lg="18" :xl="19" :xxl="20" flex="1" class="h-full ov-hidden">
|
||||||
<GiTable row-key="id" :data="dataList" :columns="columns" :loading="loading"
|
<GiTable row-key="id" :data="dataList" :columns="columns" :loading="loading"
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1500 }" :pagination="pagination" :disabled-tools="['size']"
|
:scroll="{ x: '100%', y: '100%', minWidth: 1500 }" :pagination="pagination" :disabled-tools="['size']"
|
||||||
:disabled-column-keys="['username']" @refresh="search">
|
:disabled-column-keys="['username']" @refresh="search">
|
||||||
<template #custom-left>
|
<template #custom-left>
|
||||||
<a-input v-model="queryForm.description" placeholder="请输入关键词" allow-clear @change="search">
|
<a-input v-model="queryForm.description" placeholder="请输入关键词" allow-clear @change="search">
|
||||||
<template #prefix><icon-search /></template>
|
<template #prefix>
|
||||||
|
<icon-search />
|
||||||
|
</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
<a-select v-model="queryForm.status" :options="DisEnableStatusList" placeholder="请选择状态" allow-clear
|
<a-select v-model="queryForm.status" :options="DisEnableStatusList" placeholder="请选择状态" allow-clear
|
||||||
style="width: 150px" @change="search" />
|
style="width: 150px" @change="search" />
|
||||||
<a-button @click="reset">重置</a-button>
|
<a-button @click="reset">重置</a-button>
|
||||||
</template>
|
</template>
|
||||||
<template #custom-right>
|
<template #custom-right>
|
||||||
<a-button v-permission="['system:user:add']" type="primary" @click="onAdd">
|
<a-button v-permission="['system:user:add']" type="primary" @click="onAdd">
|
||||||
<template #icon><icon-plus /></template>
|
<template #icon>
|
||||||
|
<icon-plus />
|
||||||
|
</template>
|
||||||
<span>新增</span>
|
<span>新增</span>
|
||||||
</a-button>
|
</a-button>
|
||||||
|
<a-button v-permission="['system:user:import']" @click="onImport">
|
||||||
|
<template #icon>
|
||||||
|
<icon-upload />
|
||||||
|
</template>
|
||||||
|
<span>导入</span>
|
||||||
|
</a-button>
|
||||||
<a-tooltip content="导出">
|
<a-tooltip content="导出">
|
||||||
<a-button v-permission="['system:user:export']" class="gi_hover_btn-border" @click="onExport">
|
<a-button v-permission="['system:user:export']" class="gi_hover_btn-border" @click="onExport">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
@@ -38,7 +48,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #username="{ record }">
|
<template #username="{ record }">
|
||||||
<GiCellAvatar :avatar="getAvatar(record.avatar, record.gender)" :name="record.username" is-link
|
<GiCellAvatar :avatar="getAvatar(record.avatar, record.gender)" :name="record.username" is-link
|
||||||
@click="onDetail(record)" />
|
@click="onDetail(record)" />
|
||||||
</template>
|
</template>
|
||||||
<template #gender="{ record }">
|
<template #gender="{ record }">
|
||||||
<GiCellGender :gender="record.gender" />
|
<GiCellGender :gender="record.gender" />
|
||||||
@@ -57,7 +67,8 @@
|
|||||||
<a-space>
|
<a-space>
|
||||||
<a-link v-permission="['system:user:update']" @click="onUpdate(record)">修改</a-link>
|
<a-link v-permission="['system:user:update']" @click="onUpdate(record)">修改</a-link>
|
||||||
<a-link v-permission="['system:user:delete']" status="danger"
|
<a-link v-permission="['system:user:delete']" status="danger"
|
||||||
:title="record.isSystem ? '系统内置数据不能删除' : '删除'" :disabled="record.disabled" @click="onDelete(record)">
|
:title="record.isSystem ? '系统内置数据不能删除' : '删除'" :disabled="record.disabled"
|
||||||
|
@click="onDelete(record)">
|
||||||
删除
|
删除
|
||||||
</a-link>
|
</a-link>
|
||||||
<a-dropdown>
|
<a-dropdown>
|
||||||
@@ -73,6 +84,7 @@
|
|||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<UserAddModal ref="UserAddModalRef" @save-success="search" />
|
<UserAddModal ref="UserAddModalRef" @save-success="search" />
|
||||||
|
<UserImportModal ref="UserImportModalRef" @save-success="search" />
|
||||||
<UserDetailDrawer ref="UserDetailDrawerRef" />
|
<UserDetailDrawer ref="UserDetailDrawerRef" />
|
||||||
<UserResetPwdModal ref="UserResetPwdModalRef" />
|
<UserResetPwdModal ref="UserResetPwdModalRef" />
|
||||||
</div>
|
</div>
|
||||||
@@ -81,6 +93,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import DeptTree from './dept/index.vue'
|
import DeptTree from './dept/index.vue'
|
||||||
import UserAddModal from './UserAddModal.vue'
|
import UserAddModal from './UserAddModal.vue'
|
||||||
|
import UserImportModal from './UserImportModal.vue'
|
||||||
import UserDetailDrawer from './UserDetailDrawer.vue'
|
import UserDetailDrawer from './UserDetailDrawer.vue'
|
||||||
import UserResetPwdModal from './UserResetPwdModal.vue'
|
import UserResetPwdModal from './UserResetPwdModal.vue'
|
||||||
import { type UserQuery, type UserResp, deleteUser, exportUser, listUser } from '@/apis'
|
import { type UserQuery, type UserResp, deleteUser, exportUser, listUser } from '@/apis'
|
||||||
@@ -176,6 +189,12 @@ const onAdd = () => {
|
|||||||
UserAddModalRef.value?.onAdd()
|
UserAddModalRef.value?.onAdd()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const UserImportModalRef = ref<InstanceType<typeof UserImportModal>>()
|
||||||
|
// 导入
|
||||||
|
const onImport = () => {
|
||||||
|
UserImportModalRef.value?.onImport()
|
||||||
|
}
|
||||||
|
|
||||||
// 修改
|
// 修改
|
||||||
const onUpdate = (item: UserResp) => {
|
const onUpdate = (item: UserResp) => {
|
||||||
UserAddModalRef.value?.onUpdate(item.id)
|
UserAddModalRef.value?.onUpdate(item.id)
|
||||||
|
|||||||
Reference in New Issue
Block a user