Sfoglia il codice sorgente

新增用户个人信息页面,整合用户信息获取和更新功能,支持表单验证和密码修改,提升用户体验和代码可维护性。同时更新相关类型定义和API路由,确保数据一致性。

zyh 8 mesi fa
parent
commit
1977a6757d

+ 1 - 0
client/mobile/deno.json

@@ -4,6 +4,7 @@
     "react-dom": "https://esm.d8d.fun/react-dom@19.0.0",
     "react-dom": "https://esm.d8d.fun/react-dom@19.0.0",
     "react-dom/client": "https://esm.d8d.fun/react-dom@19.0.0/client",
     "react-dom/client": "https://esm.d8d.fun/react-dom@19.0.0/client",
     "react-router": "https://esm.d8d.fun/react-router@7.3.0?deps=react@19.0.0,react-dom@19.0.0",
     "react-router": "https://esm.d8d.fun/react-router@7.3.0?deps=react@19.0.0,react-dom@19.0.0",
+    "react-hook-form": "https://esm.d8d.fun/react-hook-form@7.55.0?deps=react@19.0.0,react-dom@19.0.0",
     "@heroicons/react/24/outline": "https://esm.d8d.fun/@heroicons/react@2.1.1/24/outline?deps=react@19.0.0,react-dom@19.0.0",
     "@heroicons/react/24/outline": "https://esm.d8d.fun/@heroicons/react@2.1.1/24/outline?deps=react@19.0.0,react-dom@19.0.0",
     "@heroicons/react/24/solid": "https://esm.d8d.fun/@heroicons/react@2.1.1/24/solid?deps=react@19.0.0,react-dom@19.0.0",
     "@heroicons/react/24/solid": "https://esm.d8d.fun/@heroicons/react@2.1.1/24/solid?deps=react@19.0.0,react-dom@19.0.0",
     "axios": "https://esm.d8d.fun/axios@1.6.7",
     "axios": "https://esm.d8d.fun/axios@1.6.7",

+ 13 - 0
client/mobile/deno.lock

@@ -26,7 +26,9 @@
     "https://esm.d8d.fun/@types/proxy-from-env@~1.0.4/index.d.ts": "https://esm.d8d.fun/@types/proxy-from-env@1.0.4/index.d.ts",
     "https://esm.d8d.fun/@types/proxy-from-env@~1.0.4/index.d.ts": "https://esm.d8d.fun/@types/proxy-from-env@1.0.4/index.d.ts",
     "https://esm.d8d.fun/@types/react-dom@~19.0.4/X-ZHJlYWN0QDE5LjAuMA/index.d.ts": "https://esm.d8d.fun/@types/react-dom@19.0.6/X-ZHJlYWN0QDE5LjAuMA/index.d.ts",
     "https://esm.d8d.fun/@types/react-dom@~19.0.4/X-ZHJlYWN0QDE5LjAuMA/index.d.ts": "https://esm.d8d.fun/@types/react-dom@19.0.6/X-ZHJlYWN0QDE5LjAuMA/index.d.ts",
     "https://esm.d8d.fun/@types/react-dom@~19.0.4/client.d.ts": "https://esm.d8d.fun/@types/react-dom@19.0.6/client.d.ts",
     "https://esm.d8d.fun/@types/react-dom@~19.0.4/client.d.ts": "https://esm.d8d.fun/@types/react-dom@19.0.6/client.d.ts",
+    "https://esm.d8d.fun/@types/react-dom@~19.0.4/index.d.ts": "https://esm.d8d.fun/@types/react-dom@19.0.6/index.d.ts",
     "https://esm.d8d.fun/@types/react-dom@~19.0.6/X-ZHJlYWN0QDE5LjAuMA/client.d.ts": "https://esm.d8d.fun/@types/react-dom@19.0.6/X-ZHJlYWN0QDE5LjAuMA/client.d.ts",
     "https://esm.d8d.fun/@types/react-dom@~19.0.6/X-ZHJlYWN0QDE5LjAuMA/client.d.ts": "https://esm.d8d.fun/@types/react-dom@19.0.6/X-ZHJlYWN0QDE5LjAuMA/client.d.ts",
+    "https://esm.d8d.fun/@types/react@~19.0.11/index.d.ts": "https://esm.d8d.fun/@types/react@19.0.14/index.d.ts",
     "https://esm.d8d.fun/@types/react@~19.0.12/index.d.ts": "https://esm.d8d.fun/@types/react@19.0.14/index.d.ts",
     "https://esm.d8d.fun/@types/react@~19.0.12/index.d.ts": "https://esm.d8d.fun/@types/react@19.0.14/index.d.ts",
     "https://esm.d8d.fun/@types/react@~19.0.12/jsx-runtime.d.ts": "https://esm.d8d.fun/@types/react@19.0.14/jsx-runtime.d.ts",
     "https://esm.d8d.fun/@types/react@~19.0.12/jsx-runtime.d.ts": "https://esm.d8d.fun/@types/react@19.0.14/jsx-runtime.d.ts",
     "https://esm.d8d.fun/@types/react@~19.0.14/X-ZHJlYWN0LWRvbUAxOS4wLjA/index.d.ts": "https://esm.d8d.fun/@types/react@19.0.14/X-ZHJlYWN0LWRvbUAxOS4wLjA/index.d.ts",
     "https://esm.d8d.fun/@types/react@~19.0.14/X-ZHJlYWN0LWRvbUAxOS4wLjA/index.d.ts": "https://esm.d8d.fun/@types/react@19.0.14/X-ZHJlYWN0LWRvbUAxOS4wLjA/index.d.ts",
@@ -52,6 +54,7 @@
     "https://esm.d8d.fun/nanoid@^5.1.2?target=denonext": "https://esm.d8d.fun/nanoid@5.1.5?target=denonext",
     "https://esm.d8d.fun/nanoid@^5.1.2?target=denonext": "https://esm.d8d.fun/nanoid@5.1.5?target=denonext",
     "https://esm.d8d.fun/node-gyp-build@^4.3.0?target=denonext": "https://esm.d8d.fun/node-gyp-build@4.8.4?target=denonext",
     "https://esm.d8d.fun/node-gyp-build@^4.3.0?target=denonext": "https://esm.d8d.fun/node-gyp-build@4.8.4?target=denonext",
     "https://esm.d8d.fun/proxy-from-env@^1.1.0?target=denonext": "https://esm.d8d.fun/proxy-from-env@1.1.0?target=denonext",
     "https://esm.d8d.fun/proxy-from-env@^1.1.0?target=denonext": "https://esm.d8d.fun/proxy-from-env@1.1.0?target=denonext",
+    "https://esm.d8d.fun/react@%3E=18?target=denonext": "https://esm.d8d.fun/react@19.1.0?target=denonext",
     "https://esm.d8d.fun/scheduler@^0.25.0?target=denonext": "https://esm.d8d.fun/scheduler@0.25.0?target=denonext",
     "https://esm.d8d.fun/scheduler@^0.25.0?target=denonext": "https://esm.d8d.fun/scheduler@0.25.0?target=denonext",
     "https://esm.d8d.fun/set-cookie-parser@^2.6.0?target=denonext": "https://esm.d8d.fun/set-cookie-parser@2.7.1?target=denonext",
     "https://esm.d8d.fun/set-cookie-parser@^2.6.0?target=denonext": "https://esm.d8d.fun/set-cookie-parser@2.7.1?target=denonext",
     "https://esm.d8d.fun/socket.io-client@^4.7.2?target=denonext": "https://esm.d8d.fun/socket.io-client@4.8.1?target=denonext",
     "https://esm.d8d.fun/socket.io-client@^4.7.2?target=denonext": "https://esm.d8d.fun/socket.io-client@4.8.1?target=denonext",
@@ -78,7 +81,9 @@
     "https://esm.d8d.fun/@heroicons/react@2.1.1/24/outline?deps=react@19.0.0": "d2c14cfd7f3090062c9f968f25d0ddbb277ca76055af1ac3fd22045276571a75",
     "https://esm.d8d.fun/@heroicons/react@2.1.1/24/outline?deps=react@19.0.0": "d2c14cfd7f3090062c9f968f25d0ddbb277ca76055af1ac3fd22045276571a75",
     "https://esm.d8d.fun/@heroicons/react@2.1.1/24/outline?deps=react@19.0.0,react-dom@19.0.0": "5e99f4d40ce60c55b5cf421c3cf3f13df1707cf53152e447b2332570412cd77a",
     "https://esm.d8d.fun/@heroicons/react@2.1.1/24/outline?deps=react@19.0.0,react-dom@19.0.0": "5e99f4d40ce60c55b5cf421c3cf3f13df1707cf53152e447b2332570412cd77a",
     "https://esm.d8d.fun/@heroicons/react@2.1.1/24/solid?deps=react@19.0.0": "546051c9fdfdca5c7d51cd4cf588fe709da509274c5fcf203d616a5e87bdd595",
     "https://esm.d8d.fun/@heroicons/react@2.1.1/24/solid?deps=react@19.0.0": "546051c9fdfdca5c7d51cd4cf588fe709da509274c5fcf203d616a5e87bdd595",
+    "https://esm.d8d.fun/@heroicons/react@2.1.1/24/solid?deps=react@19.0.0,react-dom@19.0.0": "e3940182b574da537337b1e90a1b7f380e17050457423e13d5ac8c7bc88a3cc0",
     "https://esm.d8d.fun/@heroicons/react@2.1.1/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/24/outline.mjs": "640f934a0c987f682032049e5d4a455567db676de47bca0d44e76b72023661f7",
     "https://esm.d8d.fun/@heroicons/react@2.1.1/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/24/outline.mjs": "640f934a0c987f682032049e5d4a455567db676de47bca0d44e76b72023661f7",
+    "https://esm.d8d.fun/@heroicons/react@2.1.1/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/24/solid.mjs": "dcbd0c377d92857b6eb23c7dbb2ee6e650b12aa6ae1ef7fcc10dc1964df8ba47",
     "https://esm.d8d.fun/@heroicons/react@2.1.1/X-ZHJlYWN0QDE5LjAuMA/denonext/24/outline.mjs": "640f934a0c987f682032049e5d4a455567db676de47bca0d44e76b72023661f7",
     "https://esm.d8d.fun/@heroicons/react@2.1.1/X-ZHJlYWN0QDE5LjAuMA/denonext/24/outline.mjs": "640f934a0c987f682032049e5d4a455567db676de47bca0d44e76b72023661f7",
     "https://esm.d8d.fun/@heroicons/react@2.1.1/X-ZHJlYWN0QDE5LjAuMA/denonext/24/solid.mjs": "dcbd0c377d92857b6eb23c7dbb2ee6e650b12aa6ae1ef7fcc10dc1964df8ba47",
     "https://esm.d8d.fun/@heroicons/react@2.1.1/X-ZHJlYWN0QDE5LjAuMA/denonext/24/solid.mjs": "dcbd0c377d92857b6eb23c7dbb2ee6e650b12aa6ae1ef7fcc10dc1964df8ba47",
     "https://esm.d8d.fun/@socket.io/component-emitter@3.1.2/denonext/component-emitter.mjs": "3c6c5f2d64d4933b577a7117df1d8855c51ff01ab3dea8f42af1adcb1a5989e7",
     "https://esm.d8d.fun/@socket.io/component-emitter@3.1.2/denonext/component-emitter.mjs": "3c6c5f2d64d4933b577a7117df1d8855c51ff01ab3dea8f42af1adcb1a5989e7",
@@ -173,6 +178,7 @@
     "https://esm.d8d.fun/node-gyp-build@4.8.4?target=denonext": "261a6cedf1fdbf159798141ba1e2311ac1510682c5c8b55dacc8cf5fdee4aa06",
     "https://esm.d8d.fun/node-gyp-build@4.8.4?target=denonext": "261a6cedf1fdbf159798141ba1e2311ac1510682c5c8b55dacc8cf5fdee4aa06",
     "https://esm.d8d.fun/proxy-from-env@1.1.0/denonext/proxy-from-env.mjs": "f60f9c79fc3baa07c13c800798d645ae70d1b2059b8d593dcd4f8c5710b50333",
     "https://esm.d8d.fun/proxy-from-env@1.1.0/denonext/proxy-from-env.mjs": "f60f9c79fc3baa07c13c800798d645ae70d1b2059b8d593dcd4f8c5710b50333",
     "https://esm.d8d.fun/proxy-from-env@1.1.0?target=denonext": "bf02a050a1a6aa56ddba25dbea2c355da294630e5c5520fddea4b2f30a9292bc",
     "https://esm.d8d.fun/proxy-from-env@1.1.0?target=denonext": "bf02a050a1a6aa56ddba25dbea2c355da294630e5c5520fddea4b2f30a9292bc",
+    "https://esm.d8d.fun/react-dom@19.0.0": "d057f65e74eca8add1702ba9a5ecbcc8e60a73dd358b7852094bde0361725137",
     "https://esm.d8d.fun/react-dom@19.0.0/X-ZHJlYWN0QDE5LjAuMA/denonext/client.mjs": "af662fd134eea98f37fdcea6142accd0f8a7d2e13c1c3c9e98dc37a8c7aad46b",
     "https://esm.d8d.fun/react-dom@19.0.0/X-ZHJlYWN0QDE5LjAuMA/denonext/client.mjs": "af662fd134eea98f37fdcea6142accd0f8a7d2e13c1c3c9e98dc37a8c7aad46b",
     "https://esm.d8d.fun/react-dom@19.0.0/X-ZHJlYWN0QDE5LjAuMA/denonext/react-dom.mjs": "a2f7bc344e1d5b7ca47e68665291e206ae4db17ee84f234f3d3e2533b9119f63",
     "https://esm.d8d.fun/react-dom@19.0.0/X-ZHJlYWN0QDE5LjAuMA/denonext/react-dom.mjs": "a2f7bc344e1d5b7ca47e68665291e206ae4db17ee84f234f3d3e2533b9119f63",
     "https://esm.d8d.fun/react-dom@19.0.0/client": "c972c16184c695fc5828dfa61d7f341edbc463d20d8108765c93a98027c24227",
     "https://esm.d8d.fun/react-dom@19.0.0/client": "c972c16184c695fc5828dfa61d7f341edbc463d20d8108765c93a98027c24227",
@@ -180,14 +186,21 @@
     "https://esm.d8d.fun/react-dom@19.0.0/denonext/client.mjs": "af662fd134eea98f37fdcea6142accd0f8a7d2e13c1c3c9e98dc37a8c7aad46b",
     "https://esm.d8d.fun/react-dom@19.0.0/denonext/client.mjs": "af662fd134eea98f37fdcea6142accd0f8a7d2e13c1c3c9e98dc37a8c7aad46b",
     "https://esm.d8d.fun/react-dom@19.0.0/denonext/react-dom.mjs": "a2f7bc344e1d5b7ca47e68665291e206ae4db17ee84f234f3d3e2533b9119f63",
     "https://esm.d8d.fun/react-dom@19.0.0/denonext/react-dom.mjs": "a2f7bc344e1d5b7ca47e68665291e206ae4db17ee84f234f3d3e2533b9119f63",
     "https://esm.d8d.fun/react-dom@19.0.0?deps=react@19.0.0,react-dom@19.0.0": "0e49978c3f0fb4a94db9c9318aebd7e1b35651678050871a91ebb080cc3e1f83",
     "https://esm.d8d.fun/react-dom@19.0.0?deps=react@19.0.0,react-dom@19.0.0": "0e49978c3f0fb4a94db9c9318aebd7e1b35651678050871a91ebb080cc3e1f83",
+    "https://esm.d8d.fun/react-hook-form@7.55.0/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/react-hook-form.mjs": "788ec1a54e10051f539ba435aa513802c823bad03e11e2534b1b17df99189a87",
+    "https://esm.d8d.fun/react-hook-form@7.55.0?deps=react@19.0.0,react-dom@19.0.0": "8ed376b3af6e11be43538b15e654692d5995232523a6dc16ce7f81263b1a3614",
+    "https://esm.d8d.fun/react-router@7.3.0": "ed310627e3a6bd90acbaefa1474263abd85e127041ccc5f665cf3d3a574c85c8",
     "https://esm.d8d.fun/react-router@7.3.0/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/dist/development/chunk-K6CSEXPM.mjs": "441898046ad7c4fd9a6b53e13a398c9c74c4412c519e942f82b8a77f7af9f9d6",
     "https://esm.d8d.fun/react-router@7.3.0/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/dist/development/chunk-K6CSEXPM.mjs": "441898046ad7c4fd9a6b53e13a398c9c74c4412c519e942f82b8a77f7af9f9d6",
     "https://esm.d8d.fun/react-router@7.3.0/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/react-router.mjs": "b0b05fcfc3a03c5f679cd0bc69ca19aa10abaa977395df00e86b3fb114e5e346",
     "https://esm.d8d.fun/react-router@7.3.0/X-ZHJlYWN0LWRvbUAxOS4wLjAscmVhY3RAMTkuMC4w/denonext/react-router.mjs": "b0b05fcfc3a03c5f679cd0bc69ca19aa10abaa977395df00e86b3fb114e5e346",
+    "https://esm.d8d.fun/react-router@7.3.0/denonext/dist/development/chunk-K6CSEXPM.mjs": "095a3225d9bbe00e5749781a8335be24f770a9e2634f88f75bb691de46a50a18",
+    "https://esm.d8d.fun/react-router@7.3.0/denonext/react-router.mjs": "b0b05fcfc3a03c5f679cd0bc69ca19aa10abaa977395df00e86b3fb114e5e346",
     "https://esm.d8d.fun/react-router@7.3.0?deps=react@19.0.0,react-dom@19.0.0": "ad747718e32a45020d67eb4ff98f9734cb06a10ceb393baac0a965043e96cdf0",
     "https://esm.d8d.fun/react-router@7.3.0?deps=react@19.0.0,react-dom@19.0.0": "ad747718e32a45020d67eb4ff98f9734cb06a10ceb393baac0a965043e96cdf0",
     "https://esm.d8d.fun/react@19.0.0": "ab1f4aa20ac56c237bbb204632bdb55f03a0ab005d21944eeb447e5e37879637",
     "https://esm.d8d.fun/react@19.0.0": "ab1f4aa20ac56c237bbb204632bdb55f03a0ab005d21944eeb447e5e37879637",
     "https://esm.d8d.fun/react@19.0.0/X-ZHJlYWN0LWRvbUAxOS4wLjA/denonext/react.mjs": "87fdb28d39ca8983bdba3e7ec329305f95463cfc70c015b2620b4900fa15efdd",
     "https://esm.d8d.fun/react@19.0.0/X-ZHJlYWN0LWRvbUAxOS4wLjA/denonext/react.mjs": "87fdb28d39ca8983bdba3e7ec329305f95463cfc70c015b2620b4900fa15efdd",
     "https://esm.d8d.fun/react@19.0.0/denonext/jsx-runtime.mjs": "643b749fa9666fbf73619a99fd708722edb4acaa34c8cea7be783a3432367780",
     "https://esm.d8d.fun/react@19.0.0/denonext/jsx-runtime.mjs": "643b749fa9666fbf73619a99fd708722edb4acaa34c8cea7be783a3432367780",
     "https://esm.d8d.fun/react@19.0.0/denonext/react.mjs": "87fdb28d39ca8983bdba3e7ec329305f95463cfc70c015b2620b4900fa15efdd",
     "https://esm.d8d.fun/react@19.0.0/denonext/react.mjs": "87fdb28d39ca8983bdba3e7ec329305f95463cfc70c015b2620b4900fa15efdd",
     "https://esm.d8d.fun/react@19.0.0?deps=react@19.0.0,react-dom@19.0.0": "05a4c12599a7d4b62ff7fc37228964902d26e2f7ba03a61d6335793b998972b7",
     "https://esm.d8d.fun/react@19.0.0?deps=react@19.0.0,react-dom@19.0.0": "05a4c12599a7d4b62ff7fc37228964902d26e2f7ba03a61d6335793b998972b7",
+    "https://esm.d8d.fun/react@19.1.0/denonext/react.mjs": "b43f435068776ab7a40daea8854ab1f8eca6252e86a9ac8b716bb9110ffeb76e",
+    "https://esm.d8d.fun/react@19.1.0?target=denonext": "8cb1e2ba1aeb012dc6807c8b3cf6ae90579b448317c9debc9d888dcabc246e66",
     "https://esm.d8d.fun/scheduler@0.25.0/denonext/scheduler.mjs": "50687edf9e0034b6db97303b1b16893b59c5833c21ea8cf913dc380b537f6aaf",
     "https://esm.d8d.fun/scheduler@0.25.0/denonext/scheduler.mjs": "50687edf9e0034b6db97303b1b16893b59c5833c21ea8cf913dc380b537f6aaf",
     "https://esm.d8d.fun/scheduler@0.25.0?target=denonext": "c12810f51123057a8a8e309cc8befaac0b5cd371cb4d61bf0372ab8046acc8e0",
     "https://esm.d8d.fun/scheduler@0.25.0?target=denonext": "c12810f51123057a8a8e309cc8befaac0b5cd371cb4d61bf0372ab8046acc8e0",
     "https://esm.d8d.fun/set-cookie-parser@2.7.1/denonext/set-cookie-parser.mjs": "81f09c909c63221a2460bc7602746543af6fd05b54fd866a04e81bb754bc7f26",
     "https://esm.d8d.fun/set-cookie-parser@2.7.1/denonext/set-cookie-parser.mjs": "81f09c909c63221a2460bc7602746543af6fd05b54fd866a04e81bb754bc7f26",

+ 132 - 0
client/mobile/pages_profile.tsx

@@ -0,0 +1,132 @@
+import React from 'react'
+import { useNavigate } from 'react-router'
+import { useForm } from 'react-hook-form'
+import { UserAPI } from './api.ts'
+import type { User } from '../share/types.ts'
+
+export default function ProfilePage() {
+  const navigate = useNavigate()
+  const { register, handleSubmit, formState: { errors }, setValue } = useForm<Omit<User, 'id' | 'role' | 'avatar'> & { password?: string }>()
+  const [loading, setLoading] = React.useState(false)
+  const [user, setUser] = React.useState<User | null>(null)
+
+  // 获取当前用户信息
+  React.useEffect(() => {
+    const fetchUser = async () => {
+      try {
+        const res = await UserAPI.getUsers({ limit: 1 })
+        if (res.data?.length > 0) {
+          const userData = res.data[0]
+          setUser(userData)
+          setValue('username', userData.username)
+          setValue('nickname', userData.nickname)
+          setValue('email', userData.email)
+          setValue('phone', userData.phone)
+        }
+      } catch (error) {
+        console.error('获取用户信息失败:', error)
+      }
+    }
+    fetchUser()
+  }, [setValue])
+
+  // 提交表单更新用户信息
+  const onSubmit = async (data: User) => {
+    try {
+      setLoading(true)
+      if (!user?.id) return
+      
+      const updatedUser = await UserAPI.updateUser(user.id, {
+        nickname: data.nickname,
+        email: data.email,
+        phone: data.phone,
+        ...(data.password ? { password: data.password } : {})
+      })
+      
+      setUser(updatedUser.data)
+      alert('更新成功')
+    } catch (error) {
+      console.error('更新失败:', error)
+      alert('更新失败')
+    } finally {
+      setLoading(false)
+    }
+  }
+
+  return (
+    <div className="p-4 max-w-md mx-auto">
+      <h1 className="text-2xl font-bold mb-6 text-gray-800">个人信息</h1>
+      
+      <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
+        <div>
+          <label className="block text-sm font-medium text-gray-700 mb-1">用户名</label>
+          <input
+            {...register('username')}
+            disabled
+            className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 bg-gray-100"
+          />
+        </div>
+
+        <div>
+          <label className="block text-sm font-medium text-gray-700 mb-1">昵称</label>
+          <input
+            {...register('nickname', { required: '请输入昵称' })}
+            className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
+          />
+          {errors.nickname && <p className="mt-1 text-sm text-red-600">{errors.nickname.message}</p>}
+        </div>
+
+        <div>
+          <label className="block text-sm font-medium text-gray-700 mb-1">邮箱</label>
+          <input
+            {...register('email', { 
+              required: '请输入邮箱',
+              pattern: {
+                value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
+                message: '请输入有效的邮箱地址'
+              }
+            })}
+            type="email"
+            className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
+          />
+          {errors.email && <p className="mt-1 text-sm text-red-600">{errors.email.message}</p>}
+        </div>
+
+        <div>
+          <label className="block text-sm font-medium text-gray-700 mb-1">手机号</label>
+          <input
+            {...register('phone')}
+            className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
+          />
+        </div>
+
+        <div>
+          <label className="block text-sm font-medium text-gray-700 mb-1">新密码</label>
+          <input
+            {...register('password')}
+            type="password"
+            placeholder="留空则不修改密码"
+            className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
+          />
+        </div>
+
+        <div className="flex space-x-3 pt-4">
+          <button
+            type="submit"
+            disabled={loading}
+            className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50"
+          >
+            {loading ? '保存中...' : '保存'}
+          </button>
+          <button
+            type="button"
+            onClick={() => navigate(-1)}
+            className="px-4 py-2 bg-gray-200 text-gray-800 rounded-md hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2"
+          >
+            返回
+          </button>
+        </div>
+      </form>
+    </div>
+  )
+}

+ 1 - 0
client/share/types.ts

@@ -32,6 +32,7 @@ export interface User {
   phone?: string;
   phone?: string;
   role: string;
   role: string;
   avatar?: string;
   avatar?: string;
+  password?: string;
 }
 }
 
 
 export interface MenuItem {
 export interface MenuItem {

+ 71 - 1
server/routes_users.ts

@@ -237,5 +237,75 @@ export function createUserRoutes(withAuth: WithAuth) {
     }
     }
   })
   })
 
 
+  // 获取当前用户信息
+  usersRoutes.get('/me', withAuth, async (c) => {
+    try {
+      const user = c.get('user')!
+      
+      const apiClient = c.get('apiClient')
+      const userData = await apiClient.database.table('users')
+        .where('id', user.id)
+        .select('id', 'username', 'nickname', 'email', 'phone', 'role', 'created_at')
+        .first()
+      
+      if (!user) {
+        return c.json({ error: '用户不存在' }, 404)
+      }
+      
+      return c.json({
+        data: user,
+        message: '获取用户详情成功'
+      })
+    } catch (error) {
+      console.error('获取当前用户信息失败:', error)
+      return c.json({ error: '获取当前用户信息失败' }, 500)
+    }
+  })
+
+  // 更新当前用户信息
+  usersRoutes.put('/me', withAuth, async (c) => {
+    try {
+      const user = c.get('user')!
+      const apiClient = c.get('apiClient')
+      const body = await c.req.json()
+      
+      // 验证必填字段
+      const { nickname, email, phone } = body
+      if (!nickname || !email) {
+        return c.json({ error: '缺少必要的用户信息' }, 400)
+      }
+
+      // 更新用户信息
+      const updateData: any = {
+        nickname,
+        email,
+        phone: phone || null,
+        updated_at: new Date()
+      }
+
+      // 如果提供了新密码,则更新密码
+      if (body.password) {
+        updateData.password = body.password
+      }
+
+      await apiClient.database.table('users')
+        .where('id', user.id)
+        .update(updateData)
+
+      const updatedUser = await apiClient.database.table('users')
+        .where('id', user.id)
+        .select('id', 'username', 'nickname', 'email', 'phone', 'role', 'created_at')
+        .first()
+
+      return c.json({
+        data: updatedUser,
+        message: '更新用户信息成功'
+      })
+    } catch (error) {
+      console.error('更新当前用户信息失败:', error)
+      return c.json({ error: '更新当前用户信息失败' }, 500)
+    }
+  })
+
   return usersRoutes
   return usersRoutes
-} 
+}