page.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. "use client";
  2. import {UserDetailInfo} from "@/app/_modules/definies";
  3. import {PageContainer, ProCard, ProDescriptions, ProForm, ProFormRadio, ProFormText,} from "@ant-design/pro-components";
  4. import type {GetProp, TabsProps, UploadProps} from "antd";
  5. import {App, Col, Divider, Flex, Row, Space, Tabs, Upload} from "antd";
  6. import {fetchApi} from "@/app/_modules/func";
  7. import {CalendarOutlined, MailOutlined, PhoneOutlined, UserOutlined,} from "@ant-design/icons";
  8. import {faSitemap, faUsers} from "@fortawesome/free-solid-svg-icons";
  9. import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
  10. import {useRouter} from "@/node_modules/next/navigation";
  11. import {useState} from "react";
  12. import globalMessage from "@/app/_modules/globalMessage";
  13. type FileType = Parameters<GetProp<UploadProps, "beforeUpload">>[0];
  14. const getBase64 = (img: FileType, callback: (url: string) => void) => {
  15. const reader = new FileReader();
  16. reader.addEventListener("load", () => callback(reader.result as string));
  17. reader.readAsDataURL(img);
  18. };
  19. //上传图片前校验
  20. const beforeUpload = (file: FileType) => {
  21. const isJpgOrPng = file.type === "image/jpeg" || file.type === "image/png";
  22. if (!isJpgOrPng) {
  23. globalMessage.error("只能上传 JPG/PNG 格式图片!");
  24. }
  25. const isLt2M = file.size / 1024 / 1024 < 2;
  26. if (!isLt2M) {
  27. globalMessage.error("图片大小不能超过 2MB!");
  28. }
  29. return isJpgOrPng && isLt2M;
  30. };
  31. export default function Profile() {
  32. const { message } = App.useApp();
  33. const [imageUrl, setImageUrl] = useState<string>();
  34. const { push } = useRouter();
  35. const [user, setUser] = useState({} as UserDetailInfo);
  36. //获取用户profile
  37. const getProfile = async () => {
  38. const body = await fetchApi("/api/system/user/profile", push);
  39. if (body !== undefined) {
  40. const data = body.data;
  41. const userData: UserDetailInfo = {
  42. userName: data.userName,
  43. phonenumber: data.phonenumber,
  44. email: data.email,
  45. deptName: data.dept.deptName,
  46. postGroup: body.postGroup,
  47. roleName: data.roles[0].roleName,
  48. nickName: data.nickName,
  49. sex: data.sex,
  50. createTime: data.createTime,
  51. };
  52. setUser(userData);
  53. setImageUrl(
  54. data.avatar === ""
  55. ? userData.sex === "1"
  56. ? "/avatar1.jpeg"
  57. : "/avatar0.jpeg"
  58. : "/api" + data.avatar
  59. );
  60. return userData;
  61. }
  62. };
  63. const handleChange: UploadProps["onChange"] = (info) => {
  64. if (info.file.status === "uploading") {
  65. return;
  66. }
  67. if (info.file.status === "done") {
  68. // Get this url from response in real world.
  69. getBase64(info.file.originFileObj as FileType, (url) => {
  70. setImageUrl(url);
  71. });
  72. }
  73. };
  74. //更新用户基本信息
  75. const updateProfile = async (values: any) => {
  76. const body = await fetchApi("/api/system/user/profile", push, {
  77. method: "PUT",
  78. headers: {
  79. "Content-Type": "application/json",
  80. },
  81. body: JSON.stringify(values),
  82. });
  83. return body;
  84. };
  85. //修改用户密码
  86. const updatePassword = async (values: any) => {
  87. const params = {
  88. oldPassword: values.oldPassword,
  89. newPassword: values.newPassword,
  90. };
  91. const body = await fetchApi(
  92. `/api/system/user/profile/updatePwd?${new URLSearchParams(params)}`,
  93. push,
  94. {
  95. method: "PUT",
  96. }
  97. );
  98. return body;
  99. };
  100. const uploadAvatar = async (options: any) => {
  101. const formData = new FormData();
  102. formData.append("avatarfile", options.file);
  103. const body = await fetchApi("/api/system/user/profile/avatar", push, {
  104. method: "POST",
  105. body: formData,
  106. });
  107. if (body.code == 200) {
  108. globalMessage.success("上传头像成功");
  109. setImageUrl("/api" + body.imgUrl);
  110. } else {
  111. globalMessage.error(body.msg);
  112. }
  113. };
  114. const executeUpdateProfile = async (values: any) => {
  115. const body = await updateProfile(values);
  116. if (body.code == 200) {
  117. globalMessage.success("修改成功");
  118. } else {
  119. globalMessage.error(body.msg);
  120. }
  121. };
  122. const executeUpdatePassword = async (values: any) => {
  123. const body = await updatePassword(values);
  124. if (body.code == 200) {
  125. globalMessage.success("修改成功");
  126. } else {
  127. globalMessage.error(body.msg);
  128. }
  129. };
  130. //定义的基本资料的tab页
  131. const items: TabsProps["items"] = [
  132. {
  133. key: "1",
  134. label: "基本资料",
  135. children: (
  136. <ProForm
  137. labelCol={{ span: 6 }}
  138. wrapperCol={{ span: 14 }}
  139. layout="horizontal"
  140. submitter={{
  141. render: (props, doms) => {
  142. return (
  143. <Row>
  144. <Col span={14} offset={4}>
  145. <Space>{doms}</Space>
  146. </Col>
  147. </Row>
  148. );
  149. },
  150. }}
  151. onFinish={executeUpdateProfile}
  152. request={getProfile}
  153. >
  154. <ProFormText
  155. width="md"
  156. name="nickName"
  157. label="用户昵称"
  158. placeholder="请输入用户昵称"
  159. rules={[{ required: true, message: "请输入用户昵称" }]}
  160. />
  161. <ProFormText
  162. width="md"
  163. name="phonenumber"
  164. label="手机号"
  165. placeholder="请输入手机号"
  166. rules={[{ required: true, message: "请输入手机号" }]}
  167. />
  168. <ProFormText
  169. name="email"
  170. width="md"
  171. label="邮箱"
  172. placeholder="请输入邮箱"
  173. rules={[{ required: true, message: "请输入邮箱" }]}
  174. />
  175. <ProFormRadio.Group
  176. name="sex"
  177. width="md"
  178. label="性别"
  179. options={[
  180. {
  181. label: "男",
  182. value: "0",
  183. },
  184. {
  185. label: "女",
  186. value: "1",
  187. },
  188. ]}
  189. rules={[{ required: true, message: "请选择性别" }]}
  190. />
  191. </ProForm>
  192. ),
  193. },
  194. {
  195. key: "2",
  196. label: "修改密码",
  197. children: (
  198. <ProForm<{
  199. oldPassword: string;
  200. newPassword: string;
  201. }>
  202. labelCol={{ span: 6 }}
  203. wrapperCol={{ span: 14 }}
  204. layout="horizontal"
  205. submitter={{
  206. render: (props, doms) => {
  207. return (
  208. <Row>
  209. <Col span={14} offset={4}>
  210. <Space>{doms}</Space>
  211. </Col>
  212. </Row>
  213. );
  214. },
  215. }}
  216. onFinish={executeUpdatePassword}
  217. params={{}}
  218. >
  219. <ProFormText.Password
  220. width="md"
  221. name="oldPassword"
  222. label="当前密码"
  223. placeholder="请输入当前密码"
  224. rules={[{ required: true, message: "请输入当前密码" }]}
  225. />
  226. <ProFormText.Password
  227. width="md"
  228. name="newPassword"
  229. label="新密码"
  230. placeholder="请输入新密码"
  231. rules={[{ required: true, message: "请输入新密码" }]}
  232. />
  233. <ProFormText.Password
  234. width="md"
  235. name="repeatPassword"
  236. label="确认新密码"
  237. placeholder="请再次输入新密码"
  238. rules={[
  239. { required: true, message: "请再次输入新密码" },
  240. ({ getFieldValue }) => ({
  241. validator(_, value) {
  242. if (!value || getFieldValue("newPassword") === value) {
  243. return Promise.resolve();
  244. }
  245. return Promise.reject(new Error("新密码两次输入不一致"));
  246. },
  247. }),
  248. ]}
  249. />
  250. </ProForm>
  251. ),
  252. },
  253. ];
  254. return (
  255. <PageContainer
  256. header={{
  257. title: "个人中心",
  258. breadcrumb: {},
  259. }}
  260. >
  261. <ProCard gutter={[16, 16]}>
  262. <ProCard
  263. colSpan="30%"
  264. title="个人信息"
  265. headerBordered
  266. bordered
  267. hoverable
  268. >
  269. <Flex justify="center" align="center">
  270. <div>
  271. <Upload
  272. name="avatarfile"
  273. accept=".jpg,.jpeg,.png"
  274. listType="picture-circle"
  275. className="avatar-uploader"
  276. showUploadList={false}
  277. customRequest={uploadAvatar}
  278. beforeUpload={beforeUpload}
  279. onChange={handleChange}
  280. >
  281. <div
  282. style={{
  283. width: "100px",
  284. height: "100px",
  285. borderRadius: "50%",
  286. overflow: "hidden",
  287. }}
  288. >
  289. {imageUrl && (
  290. <img
  291. style={{
  292. width: "100%",
  293. height: "100%",
  294. position: "relative",
  295. }}
  296. src={imageUrl}
  297. alt="avatar"
  298. />
  299. )}
  300. </div>
  301. </Upload>
  302. </div>
  303. </Flex>
  304. <Divider />
  305. <ProDescriptions column={1}>
  306. <ProDescriptions.Item
  307. label={
  308. <>
  309. <UserOutlined />
  310. 用户名
  311. </>
  312. }
  313. >
  314. {user.userName}
  315. </ProDescriptions.Item>
  316. <ProDescriptions.Item
  317. label={
  318. <>
  319. <PhoneOutlined />
  320. 手机号码
  321. </>
  322. }
  323. >
  324. {user.phonenumber}
  325. </ProDescriptions.Item>
  326. <ProDescriptions.Item
  327. label={
  328. <>
  329. <MailOutlined />
  330. 用户邮箱
  331. </>
  332. }
  333. >
  334. {user.email}
  335. </ProDescriptions.Item>
  336. <ProDescriptions.Item
  337. label={
  338. <>
  339. <FontAwesomeIcon icon={faSitemap} />
  340. 所属部门
  341. </>
  342. }
  343. >
  344. {user.deptName}/{user.postGroup}
  345. </ProDescriptions.Item>
  346. <ProDescriptions.Item
  347. label={
  348. <>
  349. <FontAwesomeIcon icon={faUsers} />
  350. 所属角色
  351. </>
  352. }
  353. >
  354. {user.roleName}
  355. </ProDescriptions.Item>
  356. <ProDescriptions.Item
  357. label={
  358. <>
  359. <CalendarOutlined />
  360. 创建时间
  361. </>
  362. }
  363. >
  364. {user.createTime}
  365. </ProDescriptions.Item>
  366. </ProDescriptions>
  367. </ProCard>
  368. <ProCard title="基本资料" headerBordered bordered hoverable>
  369. <Tabs defaultActiveKey="1" items={items} />
  370. </ProCard>
  371. </ProCard>
  372. </PageContainer>
  373. );
  374. }