page.tsx 11 KB

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