个人信息修改和切花部门

This commit is contained in:
禺狨 2023-03-27 12:15:31 +08:00
parent e1ce7d8b47
commit 987c6e21ec
6 changed files with 141 additions and 35 deletions

View File

@ -18,3 +18,10 @@ export function courses(depId: number) {
dep_id: depId, dep_id: depId,
}); });
} }
// 修改头像
export function avatar(file: any) {
return client.put("/api/v1/user/avatar", {
file: file,
});
}

View File

@ -1,9 +1,13 @@
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import { Modal, Button, Dropdown, MenuProps } from "antd"; import { Modal, Button, Dropdown } from "antd";
import type { MenuProps } from "antd";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { Link, useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import { logoutAction } from "../../store/user/loginUserSlice"; import {
logoutAction,
saveCurrentDepId,
} from "../../store/user/loginUserSlice";
import { ChangePasswordModel } from "../change-password"; import { ChangePasswordModel } from "../change-password";
import { UserInfoModel } from "../user-info"; import { UserInfoModel } from "../user-info";
import { ExclamationCircleFilled } from "@ant-design/icons"; import { ExclamationCircleFilled } from "@ant-design/icons";
@ -20,6 +24,27 @@ export const Header: React.FC = () => {
const [changePasswordVisiale, setChangePasswordVisiale] = const [changePasswordVisiale, setChangePasswordVisiale] =
useState<boolean>(false); useState<boolean>(false);
const [userInfoVisiale, setUserInfoVisiale] = useState<boolean>(false); const [userInfoVisiale, setUserInfoVisiale] = useState<boolean>(false);
const [departmentsMenu, setDepartmentsMenu] = useState<any>([]);
const [currentDepartment, setCurrentDepartment] = useState<string>("");
useEffect(() => {
setCurrentDepartment(departments[0].name);
const arr: any = [
{
key: "1",
type: "group",
label: "部门",
children: [],
},
];
departments.map((item: any) => {
arr[0].children.push({
key: item.id,
label: item.name,
});
});
setDepartmentsMenu(arr);
}, [departments]);
const onClick: MenuProps["onClick"] = ({ key }) => { const onClick: MenuProps["onClick"] = ({ key }) => {
if (key === "login_out") { if (key === "login_out") {
@ -75,6 +100,32 @@ export const Header: React.FC = () => {
}, },
]; ];
const depItems: MenuProps["items"] = departmentsMenu;
const onDepClick: MenuProps["onClick"] = ({ key }) => {
let name: string = "";
departments.map((item: any) => {
if (Number(key) === item.id) {
name = item.name;
}
});
confirm({
title: "操作确认",
icon: <ExclamationCircleFilled />,
content: "确认切换部门?",
centered: true,
okText: "确认",
cancelText: "取消",
onOk() {
setCurrentDepartment(name);
dispatch(saveCurrentDepId(Number(key)));
},
onCancel() {
console.log("Cancel");
},
});
};
return ( return (
<div className={styles["app-header"]}> <div className={styles["app-header"]}>
<div className={styles["main-header"]}> <div className={styles["main-header"]}>
@ -84,10 +135,15 @@ export const Header: React.FC = () => {
</Link> </Link>
</div> </div>
<div className="d-flex"> <div className="d-flex">
{departments.length > 0 && ( {departments.length === 1 && (
<div className={styles["department-name"]}> <div className={styles["department-name"]}>{currentDepartment}</div>
{departments[0].name} )}
</div> {departments.length > 1 && (
<Dropdown menu={{ items: depItems, onClick: onDepClick }}>
<div className={styles["department-name"]}>
{currentDepartment}
</div>
</Dropdown>
)} )}
<Button.Group className={styles["button-group"]}> <Button.Group className={styles["button-group"]}>
<Dropdown menu={{ items, onClick }} placement="bottomRight"> <Dropdown menu={{ items, onClick }} placement="bottomRight">

View File

@ -1,7 +1,12 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { Modal, Image, Form, Input, message } from "antd"; import { Modal, Image, Form, message, Upload, Button } from "antd";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { user } from "../../api/index"; import { user } from "../../api/index";
import { useDispatch } from "react-redux";
import { loginAction } from "../../store/user/loginUserSlice";
import type { UploadProps } from "antd";
import config from "../../js/config";
import { getToken } from "../../utils/index";
interface PropInterface { interface PropInterface {
open: boolean; open: boolean;
@ -9,9 +14,12 @@ interface PropInterface {
} }
export const UserInfoModel: React.FC<PropInterface> = ({ open, onCancel }) => { export const UserInfoModel: React.FC<PropInterface> = ({ open, onCancel }) => {
const dispatch = useDispatch();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [loading, setLoading] = useState<boolean>(true); const [loading, setLoading] = useState<boolean>(true);
const [avatar, setAvatar] = useState<string>(""); const [avatar, setAvatar] = useState<string>("");
const [name, setName] = useState<string>("");
const [idCard, setIdCard] = useState<string>("");
useEffect(() => { useEffect(() => {
getUser(); getUser();
@ -20,21 +28,41 @@ export const UserInfoModel: React.FC<PropInterface> = ({ open, onCancel }) => {
const getUser = () => { const getUser = () => {
user.detail().then((res: any) => { user.detail().then((res: any) => {
setAvatar(res.data.user.avatar); setAvatar(res.data.user.avatar);
form.setFieldsValue({ setName(res.data.user.name);
name: res.data.user.name, setIdCard(res.data.user.id_card);
}); dispatch(loginAction(res.data));
}); });
}; };
const onFinish = (values: any) => { const props: UploadProps = {
user.password(avatar, values.name).then((res: any) => { name: "file",
message.success("保存成功!"); multiple: false,
onCancel(); method: "PUT",
}); action: config.app_url + "/api/v1/user/avatar",
}; headers: {
Accept: "application/json",
const onFinishFailed = (errorInfo: any) => { authorization: "Bearer " + getToken(),
console.log("Failed:", errorInfo); },
beforeUpload: (file) => {
const isPNG = file.type === ("image/png" || "image/jpg");
if (!isPNG) {
message.error(`${file.name}不是图片文件`);
}
return isPNG || Upload.LIST_IGNORE;
},
onChange(info: any) {
const { status, response } = info.file;
if (status === "done") {
if (response.code === 0) {
message.success(`${info.file.name} 上传成功`);
getUser();
} else {
message.error(response.msg);
}
} else if (status === "error") {
message.error(`${info.file.name} 上传失败`);
}
},
}; };
return ( return (
@ -45,25 +73,22 @@ export const UserInfoModel: React.FC<PropInterface> = ({ open, onCancel }) => {
forceRender forceRender
open={open} open={open}
width={416} width={416}
onOk={() => form.submit()}
onCancel={() => onCancel()} onCancel={() => onCancel()}
maskClosable={false} maskClosable={false}
footer={null}
> >
<div className="float-left mt-24"> <div className="mt-24">
<Form <Form
form={form} form={form}
name="user-info" name="user-info"
labelCol={{ span: 8 }} labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }} wrapperCol={{ span: 16 }}
initialValues={{ remember: true }} initialValues={{ remember: true }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off" autoComplete="off"
> >
<Form.Item <Form.Item
label="学员头像" label="学员头像"
labelCol={{ style: { marginTop: 15, marginLeft: 52 } }} labelCol={{ style: { marginTop: 15, marginLeft: 52 } }}
name="avatar"
> >
<div className="d-flex"> <div className="d-flex">
{avatar && ( {avatar && (
@ -75,15 +100,18 @@ export const UserInfoModel: React.FC<PropInterface> = ({ open, onCancel }) => {
preview={false} preview={false}
/> />
)} )}
<div className="d-flex ml-16"></div> <div className="d-flex ml-16">
<Upload {...props} showUploadList={false}>
<Button></Button>
</Upload>
</div>
</div> </div>
</Form.Item> </Form.Item>
<Form.Item label="修改姓名" name="name"> <Form.Item label="学员姓名">
<Input <div>{name}</div>
style={{ width: 200 }} </Form.Item>
autoComplete="off" <Form.Item label="身份证号" style={{ marginBottom: 16 }}>
placeholder="请输入姓名" <div>{idCard}</div>
/>
</Form.Item> </Form.Item>
</Form> </Form>
</div> </div>

3
src/js/config.ts Normal file
View File

@ -0,0 +1,3 @@
export default {
app_url: import.meta.env.VITE_APP_URL || "",
};

View File

@ -19,14 +19,17 @@ const IndexPage = () => {
const departments = useSelector( const departments = useSelector(
(state: any) => state.loginUser.value.departments (state: any) => state.loginUser.value.departments
); );
const currentDepId = useSelector(
(state: any) => state.loginUser.value.currentDepId
);
useEffect(() => { useEffect(() => {
getData(); getData();
}, [tabKey]); }, [tabKey, currentDepId]);
const getData = () => { const getData = () => {
setLoading(true); setLoading(true);
user.courses(departments[0].id).then((res: any) => { user.courses(currentDepId).then((res: any) => {
const records = res.data.learn_course_records; const records = res.data.learn_course_records;
setStats(res.data.stats); setStats(res.data.stats);
setLearnCourseRecords(records); setLearnCourseRecords(records);

View File

@ -3,12 +3,14 @@ import { createSlice } from "@reduxjs/toolkit";
type UserStoreInterface = { type UserStoreInterface = {
user: null; user: null;
departments: string[]; departments: string[];
currentDepId: number;
isLogin: boolean; isLogin: boolean;
}; };
let defaultValue: UserStoreInterface = { let defaultValue: UserStoreInterface = {
user: null, user: null,
departments: [], departments: [],
currentDepId: 0,
isLogin: false, isLogin: false,
}; };
@ -22,16 +24,23 @@ const loginUserSlice = createSlice({
stage.value.user = e.payload.user; stage.value.user = e.payload.user;
stage.value.departments = e.payload.departments; stage.value.departments = e.payload.departments;
stage.value.isLogin = true; stage.value.isLogin = true;
if (e.payload.departments.length > 0 && stage.value.currentDepId === 0) {
stage.value.currentDepId = e.payload.departments[0].id;
}
}, },
logoutAction(stage) { logoutAction(stage) {
stage.value.user = null; stage.value.user = null;
stage.value.departments = []; stage.value.departments = [];
stage.value.isLogin = false; stage.value.isLogin = false;
}, },
saveCurrentDepId(stage, e) {
stage.value.currentDepId = e.payload;
},
}, },
}); });
export default loginUserSlice.reducer; export default loginUserSlice.reducer;
export const { loginAction, logoutAction } = loginUserSlice.actions; export const { loginAction, logoutAction, saveCurrentDepId } =
loginUserSlice.actions;
export type { UserStoreInterface }; export type { UserStoreInterface };