feat: 用户管理新增导入
This commit is contained in:
parent
5de60e2a83
commit
62460bba90
@ -75,7 +75,7 @@ function handleExport() {
|
|||||||
</NPopconfirm>
|
</NPopconfirm>
|
||||||
<NButton v-if="showExport" size="small" ghost @click="handleExport">
|
<NButton v-if="showExport" size="small" ghost @click="handleExport">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-ic-round-download class="text-icon" />
|
<icon-material-symbols:download-2-rounded class="text-icon" />
|
||||||
</template>
|
</template>
|
||||||
导出
|
导出
|
||||||
</NButton>
|
</NButton>
|
||||||
|
@ -11,6 +11,8 @@ defineOptions({
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
action?: string;
|
action?: string;
|
||||||
|
data?: Record<string, any>;
|
||||||
|
defaultUpload?: boolean;
|
||||||
showTip?: boolean;
|
showTip?: boolean;
|
||||||
max?: number;
|
max?: number;
|
||||||
accept?: string;
|
accept?: string;
|
||||||
@ -20,6 +22,8 @@ interface Props {
|
|||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
action: `/resource/oss/upload`,
|
action: `/resource/oss/upload`,
|
||||||
|
data: undefined,
|
||||||
|
defaultUpload: true,
|
||||||
showTip: true,
|
showTip: true,
|
||||||
max: 5,
|
max: 5,
|
||||||
accept: '.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.pdf',
|
accept: '.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.pdf',
|
||||||
@ -29,6 +33,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
|
|
||||||
const attrs: UploadProps = useAttrs();
|
const attrs: UploadProps = useAttrs();
|
||||||
|
|
||||||
|
const value = defineModel<CommonType.IdType[]>('value', { required: false, default: [] });
|
||||||
|
|
||||||
let fileNum = 0;
|
let fileNum = 0;
|
||||||
const fileList = ref<UploadFileInfo[]>([]);
|
const fileList = ref<UploadFileInfo[]>([]);
|
||||||
const needRelaodData = defineModel<boolean>('needRelaodData', {
|
const needRelaodData = defineModel<boolean>('needRelaodData', {
|
||||||
@ -39,6 +45,7 @@ watch(
|
|||||||
() => fileList.value,
|
() => fileList.value,
|
||||||
newValue => {
|
newValue => {
|
||||||
needRelaodData.value = newValue.length > 0;
|
needRelaodData.value = newValue.length > 0;
|
||||||
|
value.value = newValue.map(item => item.id);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -126,11 +133,13 @@ async function handleRemove(file: UploadFileInfo) {
|
|||||||
v-bind="attrs"
|
v-bind="attrs"
|
||||||
v-model:file-list="fileList"
|
v-model:file-list="fileList"
|
||||||
:action="`${baseURL}${action}`"
|
:action="`${baseURL}${action}`"
|
||||||
|
:data="data"
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
:max="max"
|
:max="max"
|
||||||
:accept="accept"
|
:accept="accept"
|
||||||
multiple
|
:multiple="max > 1"
|
||||||
directory-dnd
|
directory-dnd
|
||||||
|
:default-upload="defaultUpload"
|
||||||
:list-type="uploadType === 'image' ? 'image-card' : 'text'"
|
:list-type="uploadType === 'image' ? 'image-card' : 'text'"
|
||||||
:is-error-state="isErrorState"
|
:is-error-state="isErrorState"
|
||||||
@finish="handleFinish"
|
@finish="handleFinish"
|
||||||
@ -145,12 +154,12 @@ async function handleRemove(file: UploadFileInfo) {
|
|||||||
<NText class="text-16px">点击或者拖动文件到该区域来上传</NText>
|
<NText class="text-16px">点击或者拖动文件到该区域来上传</NText>
|
||||||
<NP v-if="showTip" depth="3" class="mt-8px text-center">
|
<NP v-if="showTip" depth="3" class="mt-8px text-center">
|
||||||
请上传
|
请上传
|
||||||
<template v-if="max">
|
<template v-if="fileSize">
|
||||||
大小不超过
|
大小不超过
|
||||||
<b class="text-red-500">{{ max }}MB</b>
|
<b class="text-red-500">{{ fileSize }}MB</b>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="accept">
|
<template v-if="accept">
|
||||||
格式为
|
,且格式为
|
||||||
<b class="text-red-500">{{ accept.replaceAll(',', '/') }}</b>
|
<b class="text-red-500">{{ accept.replaceAll(',', '/') }}</b>
|
||||||
</template>
|
</template>
|
||||||
的文件
|
的文件
|
||||||
@ -159,12 +168,12 @@ async function handleRemove(file: UploadFileInfo) {
|
|||||||
</NUpload>
|
</NUpload>
|
||||||
<NP v-if="showTip && uploadType === 'image'" depth="3" class="mt-12px">
|
<NP v-if="showTip && uploadType === 'image'" depth="3" class="mt-12px">
|
||||||
请上传
|
请上传
|
||||||
<template v-if="max">
|
<template v-if="fileSize">
|
||||||
大小不超过
|
大小不超过
|
||||||
<b class="text-red-500">{{ max }}MB</b>
|
<b class="text-red-500">{{ fileSize }}MB</b>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="accept">
|
<template v-if="accept">
|
||||||
格式为
|
,且格式为
|
||||||
<b class="text-red-500">{{ accept.replaceAll(',', '/') }}</b>
|
<b class="text-red-500">{{ accept.replaceAll(',', '/') }}</b>
|
||||||
</template>
|
</template>
|
||||||
的文件
|
的文件
|
||||||
|
@ -58,6 +58,32 @@ export function fetchGetDeptTree() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 重置用户密码 */
|
||||||
|
export function fetchResetUserPassword(userId: CommonType.IdType, password: string) {
|
||||||
|
return request<boolean>({
|
||||||
|
url: '/system/user/resetPwd',
|
||||||
|
method: 'put',
|
||||||
|
data: { userId, password }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 根据用户编号获取授权角色 */
|
||||||
|
export function fetchGetAuthRole(userId: CommonType.IdType) {
|
||||||
|
return request<Api.System.AuthRole>({
|
||||||
|
url: `/system/user/authRole/${userId}`,
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 用户授权角色 */
|
||||||
|
export function fetchAuthUserRole(userId: CommonType.IdType, roleIds: CommonType.IdType[]) {
|
||||||
|
return request<boolean>({
|
||||||
|
url: '/system/user/authRole',
|
||||||
|
method: 'put',
|
||||||
|
data: { userId, roleIds }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 修改用户基本信息 */
|
/** 修改用户基本信息 */
|
||||||
export function fetchUpdateUserProfile(data: Api.System.UserProfileOperateParams) {
|
export function fetchUpdateUserProfile(data: Api.System.UserProfileOperateParams) {
|
||||||
return request<boolean>({
|
return request<boolean>({
|
||||||
|
@ -6,7 +6,6 @@ import { fetchGetUserInfo, fetchLogin, fetchLogout } from '@/service/api';
|
|||||||
import { useRouterPush } from '@/hooks/common/router';
|
import { useRouterPush } from '@/hooks/common/router';
|
||||||
import { localStg } from '@/utils/storage';
|
import { localStg } from '@/utils/storage';
|
||||||
import { SetupStoreId } from '@/enum';
|
import { SetupStoreId } from '@/enum';
|
||||||
import { $t } from '@/locales';
|
|
||||||
import { useRouteStore } from '../route';
|
import { useRouteStore } from '../route';
|
||||||
import { useTabStore } from '../tab';
|
import { useTabStore } from '../tab';
|
||||||
import { clearAuthStorage, getToken } from './shared';
|
import { clearAuthStorage, getToken } from './shared';
|
||||||
@ -81,11 +80,11 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
|||||||
|
|
||||||
if (pass) {
|
if (pass) {
|
||||||
await redirectFromLogin(redirect);
|
await redirectFromLogin(redirect);
|
||||||
window.$notification?.success({
|
// window.$notification?.success({
|
||||||
title: $t('page.login.common.loginSuccess'),
|
// title: $t('page.login.common.loginSuccess'),
|
||||||
content: $t('page.login.common.welcomeBack', { userName: userInfo.user?.nickName }),
|
// content: $t('page.login.common.welcomeBack', { userName: userInfo.user?.nickName }),
|
||||||
duration: 4500
|
// duration: 4500
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resetStore();
|
resetStore();
|
||||||
|
8
src/typings/api/system.api.d.ts
vendored
8
src/typings/api/system.api.d.ts
vendored
@ -23,7 +23,7 @@ declare namespace Api {
|
|||||||
/** 备注 */
|
/** 备注 */
|
||||||
remark?: string;
|
remark?: string;
|
||||||
/** 角色ID */
|
/** 角色ID */
|
||||||
roleId: number;
|
roleId: CommonType.IdType;
|
||||||
/** 角色权限字符串 */
|
/** 角色权限字符串 */
|
||||||
roleKey: string;
|
roleKey: string;
|
||||||
/** 角色名称 */
|
/** 角色名称 */
|
||||||
@ -144,6 +144,12 @@ declare namespace Api {
|
|||||||
/** user list */
|
/** user list */
|
||||||
type UserList = Common.PaginatingQueryRecord<User>;
|
type UserList = Common.PaginatingQueryRecord<User>;
|
||||||
|
|
||||||
|
/** auth role */
|
||||||
|
type AuthRole = {
|
||||||
|
user: User;
|
||||||
|
roles: Role[];
|
||||||
|
};
|
||||||
|
|
||||||
/** social */
|
/** social */
|
||||||
type Social = Common.CommonRecord<{
|
type Social = Common.CommonRecord<{
|
||||||
/** 用户ID */
|
/** 用户ID */
|
||||||
|
2
src/typings/components.d.ts
vendored
2
src/typings/components.d.ts
vendored
@ -47,7 +47,9 @@ declare module 'vue' {
|
|||||||
IconIcRoundUpload: typeof import('~icons/ic/round-upload')['default']
|
IconIcRoundUpload: typeof import('~icons/ic/round-upload')['default']
|
||||||
IconLocalBanner: typeof import('~icons/local/banner')['default']
|
IconLocalBanner: typeof import('~icons/local/banner')['default']
|
||||||
IconLocalLogo: typeof import('~icons/local/logo')['default']
|
IconLocalLogo: typeof import('~icons/local/logo')['default']
|
||||||
|
'IconMaterialSymbols:download2Rounded': typeof import('~icons/material-symbols/download2-rounded')['default']
|
||||||
'IconMaterialSymbols:syncRounded': typeof import('~icons/material-symbols/sync-rounded')['default']
|
'IconMaterialSymbols:syncRounded': typeof import('~icons/material-symbols/sync-rounded')['default']
|
||||||
|
'IconMaterialSymbols:upload2Rounded': typeof import('~icons/material-symbols/upload2-rounded')['default']
|
||||||
IconMaterialSymbolsHelpOutline: typeof import('~icons/material-symbols/help-outline')['default']
|
IconMaterialSymbolsHelpOutline: typeof import('~icons/material-symbols/help-outline')['default']
|
||||||
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
||||||
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
||||||
|
@ -50,7 +50,7 @@ export function createServiceConfig(env: Env.ImportMeta) {
|
|||||||
* @param env - the current env
|
* @param env - the current env
|
||||||
* @param isProxy - if use proxy
|
* @param isProxy - if use proxy
|
||||||
*/
|
*/
|
||||||
export function getServiceBaseURL(env: Env.ImportMeta, isProxy: boolean) {
|
export function getServiceBaseURL(env: Env.ImportMeta, isProxy: boolean = env.DEV && env.VITE_HTTP_PROXY === 'Y') {
|
||||||
const { baseURL, other, proxyPattern } = createServiceConfig(env);
|
const { baseURL, other, proxyPattern } = createServiceConfig(env);
|
||||||
|
|
||||||
const otherBaseURL = {} as Record<App.Service.OtherBaseURLKey, string>;
|
const otherBaseURL = {} as Record<App.Service.OtherBaseURLKey, string>;
|
||||||
|
@ -44,7 +44,13 @@ async function unbindSsoAccount(socialId: string) {
|
|||||||
endBtnLoading();
|
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: 'wechat_open', icon: 'ic:outline-wechat', color: '#44b549', name: '微信' },
|
||||||
{ key: 'topiam', localIcon: 'topiam', color: '', name: 'TopIAM' },
|
{ key: 'topiam', localIcon: 'topiam', color: '', name: 'TopIAM' },
|
||||||
{ key: 'maxkey', localIcon: 'maxkey', color: '', name: 'MaxKey' },
|
{ key: 'maxkey', localIcon: 'maxkey', color: '', name: 'MaxKey' },
|
||||||
@ -92,9 +98,7 @@ function getSocial(key: string) {
|
|||||||
:style="{ color: source.color }"
|
:style="{ color: source.color }"
|
||||||
/>
|
/>
|
||||||
<div class="text-16px font-medium">{{ source.name }}</div>
|
<div class="text-16px font-medium">{{ source.name }}</div>
|
||||||
<NButton type="primary" size="small" @click="bindSsoAccount(source.key as Api.System.SocialSource)">
|
<NButton type="primary" size="small" @click="bindSsoAccount(source.key)">绑定</NButton>
|
||||||
绑定
|
|
||||||
</NButton>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</NCard>
|
</NCard>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { NButton } from 'naive-ui';
|
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 { fetchBatchDeleteUser, fetchGetDeptTree, fetchGetUserList } from '@/service/api/system';
|
||||||
import { useAppStore } from '@/store/modules/app';
|
import { useAppStore } from '@/store/modules/app';
|
||||||
import { useTable, useTableOperate } from '@/hooks/common/table';
|
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 DictTag from '@/components/custom/dict-tag.vue';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import UserOperateDrawer from './modules/user-operate-drawer.vue';
|
import UserOperateDrawer from './modules/user-operate-drawer.vue';
|
||||||
|
import UserImportModal from './modules/user-import-modal.vue';
|
||||||
import UserSearch from './modules/user-search.vue';
|
import UserSearch from './modules/user-search.vue';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@ -23,6 +24,8 @@ useDict('sys_normal_disable');
|
|||||||
const { hasAuth } = useAuth();
|
const { hasAuth } = useAuth();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
const { bool: importVisible, setTrue: openImportModal } = useBoolean();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
columns,
|
columns,
|
||||||
columnChecks,
|
columnChecks,
|
||||||
@ -198,6 +201,10 @@ function handleResetTreeData() {
|
|||||||
deptPattern.value = undefined;
|
deptPattern.value = undefined;
|
||||||
getTreeData();
|
getTreeData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleImport() {
|
||||||
|
openImportModal();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -247,7 +254,16 @@ function handleResetTreeData() {
|
|||||||
@add="handleAdd"
|
@add="handleAdd"
|
||||||
@delete="handleBatchDelete"
|
@delete="handleBatchDelete"
|
||||||
@refresh="getData"
|
@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>
|
</template>
|
||||||
<NDataTable
|
<NDataTable
|
||||||
v-model:checked-row-keys="checkedRowKeys"
|
v-model:checked-row-keys="checkedRowKeys"
|
||||||
@ -262,6 +278,7 @@ function handleResetTreeData() {
|
|||||||
:pagination="mobilePagination"
|
:pagination="mobilePagination"
|
||||||
class="h-full"
|
class="h-full"
|
||||||
/>
|
/>
|
||||||
|
<UserImportModal v-model:visible="importVisible" @submitted="getDataByPage" />
|
||||||
<UserOperateDrawer
|
<UserOperateDrawer
|
||||||
v-model:visible="drawerVisible"
|
v-model:visible="drawerVisible"
|
||||||
:operate-type="operateType"
|
: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[]> = {
|
const rules: Record<RuleKey, App.Global.FormRule[]> = {
|
||||||
userName: [createRequiredRule('用户名称不能为空')],
|
userName: [createRequiredRule('用户名称不能为空')],
|
||||||
nickName: [createRequiredRule('用户昵称不能为空')],
|
nickName: [createRequiredRule('用户昵称不能为空')],
|
||||||
password: [{ ...patternRules.pwd, required: true }],
|
password: [{ ...patternRules.pwd, required: props.operateType === 'add' }],
|
||||||
phonenumber: [patternRules.phone],
|
phonenumber: [patternRules.phone],
|
||||||
status: [createRequiredRule('帐号状态不能为空')]
|
status: [createRequiredRule('帐号状态不能为空')]
|
||||||
};
|
};
|
||||||
@ -95,6 +95,7 @@ function handleUpdateModelWhenEdit() {
|
|||||||
if (props.operateType === 'edit' && props.rowData) {
|
if (props.operateType === 'edit' && props.rowData) {
|
||||||
startDeptLoading();
|
startDeptLoading();
|
||||||
Object.assign(model, props.rowData);
|
Object.assign(model, props.rowData);
|
||||||
|
model.password = '';
|
||||||
getUserInfo();
|
getUserInfo();
|
||||||
endDeptLoading();
|
endDeptLoading();
|
||||||
}
|
}
|
||||||
@ -107,9 +108,10 @@ function closeDrawer() {
|
|||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
await validate();
|
await validate();
|
||||||
|
|
||||||
|
const { userId, deptId, userName, nickName, email, phonenumber, sex, password, status, remark } = model;
|
||||||
|
|
||||||
// request
|
// request
|
||||||
if (props.operateType === 'add') {
|
if (props.operateType === 'add') {
|
||||||
const { deptId, userName, nickName, email, phonenumber, sex, password, status, remark } = model;
|
|
||||||
const { error } = await fetchCreateUser({
|
const { error } = await fetchCreateUser({
|
||||||
deptId,
|
deptId,
|
||||||
userName,
|
userName,
|
||||||
@ -125,7 +127,6 @@ async function handleSubmit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (props.operateType === 'edit') {
|
if (props.operateType === 'edit') {
|
||||||
const { userId, deptId, userName, nickName, email, phonenumber, sex, password, status, remark } = model;
|
|
||||||
const { error } = await fetchUpdateUser({
|
const { error } = await fetchUpdateUser({
|
||||||
userId,
|
userId,
|
||||||
deptId,
|
deptId,
|
||||||
@ -183,7 +184,7 @@ watch(visible, () => {
|
|||||||
<NFormItem v-if="operateType === 'add'" label="用户名称" path="userName">
|
<NFormItem v-if="operateType === 'add'" label="用户名称" path="userName">
|
||||||
<NInput v-model:value="model.userName" placeholder="请输入用户名称" />
|
<NInput v-model:value="model.userName" placeholder="请输入用户名称" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
<NFormItem v-if="operateType === 'add'" label="用户密码" path="password">
|
<NFormItem label="用户密码" path="password">
|
||||||
<NInput
|
<NInput
|
||||||
v-model:value="model.password"
|
v-model:value="model.password"
|
||||||
type="password"
|
type="password"
|
||||||
|
Loading…
Reference in New Issue
Block a user