diff --git a/src/components/advanced/table-header-operation.vue b/src/components/advanced/table-header-operation.vue
index c1f5cad1..5c7a4643 100644
--- a/src/components/advanced/table-header-operation.vue
+++ b/src/components/advanced/table-header-operation.vue
@@ -75,7 +75,7 @@ function handleExport() {
-
+
导出
diff --git a/src/components/custom/file-upload.vue b/src/components/custom/file-upload.vue
index d5693fd7..ba0dd975 100644
--- a/src/components/custom/file-upload.vue
+++ b/src/components/custom/file-upload.vue
@@ -11,6 +11,8 @@ defineOptions({
interface Props {
action?: string;
+ data?: Record;
+ defaultUpload?: boolean;
showTip?: boolean;
max?: number;
accept?: string;
@@ -20,6 +22,8 @@ interface Props {
const props = withDefaults(defineProps(), {
action: `/resource/oss/upload`,
+ data: undefined,
+ defaultUpload: true,
showTip: true,
max: 5,
accept: '.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.pdf',
@@ -29,6 +33,8 @@ const props = withDefaults(defineProps(), {
const attrs: UploadProps = useAttrs();
+const value = defineModel('value', { required: false, default: [] });
+
let fileNum = 0;
const fileList = ref([]);
const needRelaodData = defineModel('needRelaodData', {
@@ -39,6 +45,7 @@ watch(
() => fileList.value,
newValue => {
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-model:file-list="fileList"
:action="`${baseURL}${action}`"
+ :data="data"
:headers="headers"
:max="max"
:accept="accept"
- multiple
+ :multiple="max > 1"
directory-dnd
+ :default-upload="defaultUpload"
:list-type="uploadType === 'image' ? 'image-card' : 'text'"
:is-error-state="isErrorState"
@finish="handleFinish"
@@ -145,12 +154,12 @@ async function handleRemove(file: UploadFileInfo) {
点击或者拖动文件到该区域来上传
请上传
-
+
大小不超过
- {{ max }}MB
+ {{ fileSize }}MB
- 格式为
+ ,且格式为
{{ accept.replaceAll(',', '/') }}
的文件
@@ -159,12 +168,12 @@ async function handleRemove(file: UploadFileInfo) {
请上传
-
+
大小不超过
- {{ max }}MB
+ {{ fileSize }}MB
- 格式为
+ ,且格式为
{{ accept.replaceAll(',', '/') }}
的文件
diff --git a/src/service/api/system/user.ts b/src/service/api/system/user.ts
index 242e591c..d174aea1 100644
--- a/src/service/api/system/user.ts
+++ b/src/service/api/system/user.ts
@@ -58,6 +58,32 @@ export function fetchGetDeptTree() {
});
}
+/** 重置用户密码 */
+export function fetchResetUserPassword(userId: CommonType.IdType, password: string) {
+ return request({
+ url: '/system/user/resetPwd',
+ method: 'put',
+ data: { userId, password }
+ });
+}
+
+/** 根据用户编号获取授权角色 */
+export function fetchGetAuthRole(userId: CommonType.IdType) {
+ return request({
+ url: `/system/user/authRole/${userId}`,
+ method: 'get'
+ });
+}
+
+/** 用户授权角色 */
+export function fetchAuthUserRole(userId: CommonType.IdType, roleIds: CommonType.IdType[]) {
+ return request({
+ url: '/system/user/authRole',
+ method: 'put',
+ data: { userId, roleIds }
+ });
+}
+
/** 修改用户基本信息 */
export function fetchUpdateUserProfile(data: Api.System.UserProfileOperateParams) {
return request({
diff --git a/src/store/modules/auth/index.ts b/src/store/modules/auth/index.ts
index 6034c27f..f0976f97 100644
--- a/src/store/modules/auth/index.ts
+++ b/src/store/modules/auth/index.ts
@@ -6,7 +6,6 @@ import { fetchGetUserInfo, fetchLogin, fetchLogout } from '@/service/api';
import { useRouterPush } from '@/hooks/common/router';
import { localStg } from '@/utils/storage';
import { SetupStoreId } from '@/enum';
-import { $t } from '@/locales';
import { useRouteStore } from '../route';
import { useTabStore } from '../tab';
import { clearAuthStorage, getToken } from './shared';
@@ -81,11 +80,11 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
if (pass) {
await redirectFromLogin(redirect);
- window.$notification?.success({
- title: $t('page.login.common.loginSuccess'),
- content: $t('page.login.common.welcomeBack', { userName: userInfo.user?.nickName }),
- duration: 4500
- });
+ // window.$notification?.success({
+ // title: $t('page.login.common.loginSuccess'),
+ // content: $t('page.login.common.welcomeBack', { userName: userInfo.user?.nickName }),
+ // duration: 4500
+ // });
}
} else {
resetStore();
diff --git a/src/typings/api/system.api.d.ts b/src/typings/api/system.api.d.ts
index c4b4ad4c..a2f9a50f 100644
--- a/src/typings/api/system.api.d.ts
+++ b/src/typings/api/system.api.d.ts
@@ -23,7 +23,7 @@ declare namespace Api {
/** 备注 */
remark?: string;
/** 角色ID */
- roleId: number;
+ roleId: CommonType.IdType;
/** 角色权限字符串 */
roleKey: string;
/** 角色名称 */
@@ -144,6 +144,12 @@ declare namespace Api {
/** user list */
type UserList = Common.PaginatingQueryRecord;
+ /** auth role */
+ type AuthRole = {
+ user: User;
+ roles: Role[];
+ };
+
/** social */
type Social = Common.CommonRecord<{
/** 用户ID */
diff --git a/src/typings/components.d.ts b/src/typings/components.d.ts
index 3c041442..b3f7e404 100644
--- a/src/typings/components.d.ts
+++ b/src/typings/components.d.ts
@@ -47,7 +47,9 @@ declare module 'vue' {
IconIcRoundUpload: typeof import('~icons/ic/round-upload')['default']
IconLocalBanner: typeof import('~icons/local/banner')['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:upload2Rounded': typeof import('~icons/material-symbols/upload2-rounded')['default']
IconMaterialSymbolsHelpOutline: typeof import('~icons/material-symbols/help-outline')['default']
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
diff --git a/src/utils/service.ts b/src/utils/service.ts
index 76ba88d2..9007be77 100644
--- a/src/utils/service.ts
+++ b/src/utils/service.ts
@@ -50,7 +50,7 @@ export function createServiceConfig(env: Env.ImportMeta) {
* @param env - the current env
* @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 otherBaseURL = {} as Record;
diff --git a/src/views/_builtin/user-center/modules/social-card.vue b/src/views/_builtin/user-center/modules/social-card.vue
index 5a1ff6cd..c513cfc1 100644
--- a/src/views/_builtin/user-center/modules/social-card.vue
+++ b/src/views/_builtin/user-center/modules/social-card.vue
@@ -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 }"
/>
{{ source.name }}
-
- 绑定
-
+ 绑定
diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue
index a7cc7f04..3a4b67e9 100644
--- a/src/views/system/user/index.vue
+++ b/src/views/system/user/index.vue
@@ -1,7 +1,7 @@
@@ -247,7 +254,16 @@ function handleResetTreeData() {
@add="handleAdd"
@delete="handleBatchDelete"
@refresh="getData"
- />
+ >
+
+
+
+
+
+ 导入
+
+
+
+
+// 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 = {
+ Authorization: `Bearer ${getToken()}`,
+ clientid: import.meta.env.VITE_APP_CLIENT_ID!
+};
+
+const emit = defineEmits();
+
+const uploadRef = ref();
+const message = ref('');
+const success = ref(false);
+
+const visible = defineModel('visible', {
+ default: false
+});
+
+const data = ref>({
+ updateSupport: false
+});
+
+const fileList = ref([]);
+
+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 = '';
+ }
+});
+
+
+
+
+
+
+
+
+
+ 点击或者拖动文件到该区域来上传
+
+ 请上传大小不超过
+ 50MB
+ ,且格式为
+ xls/xlsx
+ 的文件
+
+
+
+
+ 是否更新已经存在的用户数据
+
+
+ {{ message }}
+
+
+
+ 下载模板
+ 导入用户
+
+
+
+
+
+
diff --git a/src/views/system/user/modules/user-operate-drawer.vue b/src/views/system/user/modules/user-operate-drawer.vue
index 1789572a..354cf8d9 100644
--- a/src/views/system/user/modules/user-operate-drawer.vue
+++ b/src/views/system/user/modules/user-operate-drawer.vue
@@ -70,7 +70,7 @@ type RuleKey = Extract = {
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, () => {
-
+