mirror of
https://github.com/m-xlsea/ruoyi-plus-soybean.git
synced 2025-09-24 07:49:47 +08:00
feat: 用户管理新增导入
This commit is contained in:
@ -44,7 +44,13 @@ async function unbindSsoAccount(socialId: string) {
|
||||
endBtnLoading();
|
||||
}
|
||||
|
||||
const socialSources = [
|
||||
const socialSources: {
|
||||
key: Api.System.SocialSource;
|
||||
icon?: string;
|
||||
localIcon?: string;
|
||||
color: string;
|
||||
name: string;
|
||||
}[] = [
|
||||
{ key: 'wechat_open', icon: 'ic:outline-wechat', color: '#44b549', name: '微信' },
|
||||
{ key: 'topiam', localIcon: 'topiam', color: '', name: 'TopIAM' },
|
||||
{ key: 'maxkey', localIcon: 'maxkey', color: '', name: 'MaxKey' },
|
||||
@ -92,9 +98,7 @@ function getSocial(key: string) {
|
||||
:style="{ color: source.color }"
|
||||
/>
|
||||
<div class="text-16px font-medium">{{ source.name }}</div>
|
||||
<NButton type="primary" size="small" @click="bindSsoAccount(source.key as Api.System.SocialSource)">
|
||||
绑定
|
||||
</NButton>
|
||||
<NButton type="primary" size="small" @click="bindSsoAccount(source.key)">绑定</NButton>
|
||||
</div>
|
||||
</template>
|
||||
</NCard>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script setup lang="tsx">
|
||||
import { ref } from 'vue';
|
||||
import { NButton } from 'naive-ui';
|
||||
import { useLoading } from '@sa/hooks';
|
||||
import { useBoolean, useLoading } from '@sa/hooks';
|
||||
import { fetchBatchDeleteUser, fetchGetDeptTree, fetchGetUserList } from '@/service/api/system';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
||||
@ -11,6 +11,7 @@ import ButtonIcon from '@/components/custom/button-icon.vue';
|
||||
import DictTag from '@/components/custom/dict-tag.vue';
|
||||
import { $t } from '@/locales';
|
||||
import UserOperateDrawer from './modules/user-operate-drawer.vue';
|
||||
import UserImportModal from './modules/user-import-modal.vue';
|
||||
import UserSearch from './modules/user-search.vue';
|
||||
|
||||
defineOptions({
|
||||
@ -23,6 +24,8 @@ useDict('sys_normal_disable');
|
||||
const { hasAuth } = useAuth();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const { bool: importVisible, setTrue: openImportModal } = useBoolean();
|
||||
|
||||
const {
|
||||
columns,
|
||||
columnChecks,
|
||||
@ -198,6 +201,10 @@ function handleResetTreeData() {
|
||||
deptPattern.value = undefined;
|
||||
getTreeData();
|
||||
}
|
||||
|
||||
function handleImport() {
|
||||
openImportModal();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -247,7 +254,16 @@ function handleResetTreeData() {
|
||||
@add="handleAdd"
|
||||
@delete="handleBatchDelete"
|
||||
@refresh="getData"
|
||||
/>
|
||||
>
|
||||
<template #after>
|
||||
<NButton v-if="hasAuth('system:user:import')" size="small" ghost @click="handleImport">
|
||||
<template #icon>
|
||||
<icon-material-symbols:upload-2-rounded class="text-icon" />
|
||||
</template>
|
||||
导入
|
||||
</NButton>
|
||||
</template>
|
||||
</TableHeaderOperation>
|
||||
</template>
|
||||
<NDataTable
|
||||
v-model:checked-row-keys="checkedRowKeys"
|
||||
@ -262,6 +278,7 @@ function handleResetTreeData() {
|
||||
:pagination="mobilePagination"
|
||||
class="h-full"
|
||||
/>
|
||||
<UserImportModal v-model:visible="importVisible" @submitted="getDataByPage" />
|
||||
<UserOperateDrawer
|
||||
v-model:visible="drawerVisible"
|
||||
:operate-type="operateType"
|
||||
|
153
src/views/system/user/modules/user-import-modal.vue
Normal file
153
src/views/system/user/modules/user-import-modal.vue
Normal file
@ -0,0 +1,153 @@
|
||||
<script setup lang="ts">
|
||||
// import { fetchUserResetPwd } from '@/service/api/system';
|
||||
import { ref, watch } from 'vue';
|
||||
import type { UploadFileInfo } from 'naive-ui';
|
||||
import { getToken } from '@/store/modules/auth/shared';
|
||||
import { useDownload } from '@/hooks/business/download';
|
||||
import { getServiceBaseURL } from '@/utils/service';
|
||||
import type FileUpload from '@/components/custom/file-upload.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'UserImportModal'
|
||||
});
|
||||
|
||||
interface Emits {
|
||||
(e: 'submitted'): void;
|
||||
}
|
||||
|
||||
const { download } = useDownload();
|
||||
|
||||
const { baseURL } = getServiceBaseURL(import.meta.env);
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: `Bearer ${getToken()}`,
|
||||
clientid: import.meta.env.VITE_APP_CLIENT_ID!
|
||||
};
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const uploadRef = ref<typeof FileUpload>();
|
||||
const message = ref<string>('');
|
||||
const success = ref<boolean>(false);
|
||||
|
||||
const visible = defineModel<boolean>('visible', {
|
||||
default: false
|
||||
});
|
||||
|
||||
const data = ref<Record<string, any>>({
|
||||
updateSupport: false
|
||||
});
|
||||
|
||||
const fileList = ref<UploadFileInfo[]>([]);
|
||||
|
||||
function closeDrawer() {
|
||||
visible.value = false;
|
||||
if (success.value) {
|
||||
emit('submitted');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
fileList.value.forEach(item => {
|
||||
item.status = 'pending';
|
||||
});
|
||||
uploadRef.value?.submit();
|
||||
}
|
||||
|
||||
function isErrorState(xhr: XMLHttpRequest) {
|
||||
const responseText = xhr?.responseText;
|
||||
const response = JSON.parse(responseText);
|
||||
return response.code !== 200;
|
||||
}
|
||||
|
||||
function handleFinish(options: { file: UploadFileInfo; event?: ProgressEvent }) {
|
||||
const { file, event } = options;
|
||||
// @ts-expect-error Ignore type errors
|
||||
const responseText = event?.target?.responseText;
|
||||
const response = JSON.parse(responseText);
|
||||
message.value = response.msg;
|
||||
window.$message?.success('导入成功');
|
||||
success.value = true;
|
||||
return file;
|
||||
}
|
||||
|
||||
function handleError(options: { file: UploadFileInfo; event?: ProgressEvent }) {
|
||||
const { event } = options;
|
||||
// @ts-expect-error Ignore type errors
|
||||
const responseText = event?.target?.responseText;
|
||||
const msg = JSON.parse(responseText).msg;
|
||||
message.value = msg;
|
||||
window.$message?.error(msg || '导入失败');
|
||||
success.value = false;
|
||||
}
|
||||
|
||||
function handleDownloadTemplate() {
|
||||
download('/system/user/importTemplate', {}, `user_template_${new Date().getTime()}.xlsx`);
|
||||
}
|
||||
|
||||
watch(visible, () => {
|
||||
if (visible.value) {
|
||||
fileList.value = [];
|
||||
success.value = false;
|
||||
message.value = '';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NModal
|
||||
v-model:show="visible"
|
||||
title="导入用户"
|
||||
preset="card"
|
||||
:bordered="false"
|
||||
display-directive="show"
|
||||
class="max-w-90% w-600px"
|
||||
@close="closeDrawer"
|
||||
>
|
||||
<NUpload
|
||||
ref="uploadRef"
|
||||
v-model:file-list="fileList"
|
||||
:action="`${baseURL}/system/user/importData`"
|
||||
:headers="headers"
|
||||
:data="data"
|
||||
:max="1"
|
||||
:file-size="50"
|
||||
accept=".xls,.xlsx"
|
||||
:multiple="false"
|
||||
directory-dnd
|
||||
:default-upload="false"
|
||||
list-type="text"
|
||||
:is-error-state="isErrorState"
|
||||
@finish="handleFinish"
|
||||
@error="handleError"
|
||||
>
|
||||
<NUploadDragger>
|
||||
<div class="mb-12px flex-center">
|
||||
<SvgIcon icon="material-symbols:unarchive-outline" class="text-58px color-#d8d8db dark:color-#a1a1a2" />
|
||||
</div>
|
||||
<NText class="text-16px">点击或者拖动文件到该区域来上传</NText>
|
||||
<NP depth="3" class="mt-8px text-center">
|
||||
请上传大小不超过
|
||||
<b class="text-red-500">50MB</b>
|
||||
,且格式为
|
||||
<b class="text-red-500">xls/xlsx</b>
|
||||
的文件
|
||||
</NP>
|
||||
</NUploadDragger>
|
||||
</NUpload>
|
||||
<div class="flex-center">
|
||||
<NCheckbox v-model="data.updateSupport">是否更新已经存在的用户数据</NCheckbox>
|
||||
</div>
|
||||
<NAlert v-if="message" title="导入结果" :type="success ? 'success' : 'error'" :bordered="false">
|
||||
{{ message }}
|
||||
</NAlert>
|
||||
<template #footer>
|
||||
<NSpace justify="end" :size="16">
|
||||
<NButton @click="handleDownloadTemplate">下载模板</NButton>
|
||||
<NButton type="primary" @click="handleSubmit">导入用户</NButton>
|
||||
</NSpace>
|
||||
</template>
|
||||
</NModal>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
@ -70,7 +70,7 @@ type RuleKey = Extract<keyof Model, 'userName' | 'nickName' | 'password' | 'stat
|
||||
const rules: Record<RuleKey, App.Global.FormRule[]> = {
|
||||
userName: [createRequiredRule('用户名称不能为空')],
|
||||
nickName: [createRequiredRule('用户昵称不能为空')],
|
||||
password: [{ ...patternRules.pwd, required: true }],
|
||||
password: [{ ...patternRules.pwd, required: props.operateType === 'add' }],
|
||||
phonenumber: [patternRules.phone],
|
||||
status: [createRequiredRule('帐号状态不能为空')]
|
||||
};
|
||||
@ -95,6 +95,7 @@ function handleUpdateModelWhenEdit() {
|
||||
if (props.operateType === 'edit' && props.rowData) {
|
||||
startDeptLoading();
|
||||
Object.assign(model, props.rowData);
|
||||
model.password = '';
|
||||
getUserInfo();
|
||||
endDeptLoading();
|
||||
}
|
||||
@ -107,9 +108,10 @@ function closeDrawer() {
|
||||
async function handleSubmit() {
|
||||
await validate();
|
||||
|
||||
const { userId, deptId, userName, nickName, email, phonenumber, sex, password, status, remark } = model;
|
||||
|
||||
// request
|
||||
if (props.operateType === 'add') {
|
||||
const { deptId, userName, nickName, email, phonenumber, sex, password, status, remark } = model;
|
||||
const { error } = await fetchCreateUser({
|
||||
deptId,
|
||||
userName,
|
||||
@ -125,7 +127,6 @@ async function handleSubmit() {
|
||||
}
|
||||
|
||||
if (props.operateType === 'edit') {
|
||||
const { userId, deptId, userName, nickName, email, phonenumber, sex, password, status, remark } = model;
|
||||
const { error } = await fetchUpdateUser({
|
||||
userId,
|
||||
deptId,
|
||||
@ -183,7 +184,7 @@ watch(visible, () => {
|
||||
<NFormItem v-if="operateType === 'add'" label="用户名称" path="userName">
|
||||
<NInput v-model:value="model.userName" placeholder="请输入用户名称" />
|
||||
</NFormItem>
|
||||
<NFormItem v-if="operateType === 'add'" label="用户密码" path="password">
|
||||
<NFormItem label="用户密码" path="password">
|
||||
<NInput
|
||||
v-model:value="model.password"
|
||||
type="password"
|
||||
|
Reference in New Issue
Block a user