page.tsx 11 KB

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