diff --git a/package-lock.json b/package-lock.json index c6d7d35..c1400e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "@tarojs/webpack5-runner": "^4.0.0", "@types/react": "^18.2.66", "babel-preset-taro": "^4.0.0", + "miniprogram-api-typings": "^5.2.0", "typescript": "^5.4.5" } }, @@ -10249,6 +10250,13 @@ "miniprogram-exparser": "latest" } }, + "node_modules/j-component/node_modules/miniprogram-api-typings": { + "version": "3.12.3", + "resolved": "https://registry.npmmirror.com/miniprogram-api-typings/-/miniprogram-api-typings-3.12.3.tgz", + "integrity": "sha512-o7bOfrU28MEMCBWo83nXv0ROQSBFxJcfCl4f2wTYqah64ipC5RGqLJfvWJTWhlQt2ECVwspSzM8LgvnfMo7TEQ==", + "dev": true, + "license": "MIT" + }, "node_modules/jackspeak": { "version": "2.3.6", "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-2.3.6.tgz", @@ -11395,9 +11403,9 @@ } }, "node_modules/miniprogram-api-typings": { - "version": "3.12.3", - "resolved": "https://registry.npmmirror.com/miniprogram-api-typings/-/miniprogram-api-typings-3.12.3.tgz", - "integrity": "sha512-o7bOfrU28MEMCBWo83nXv0ROQSBFxJcfCl4f2wTYqah64ipC5RGqLJfvWJTWhlQt2ECVwspSzM8LgvnfMo7TEQ==", + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/miniprogram-api-typings/-/miniprogram-api-typings-5.2.0.tgz", + "integrity": "sha512-dkel1zG/eAfApabCtZnr9Y69+5z89GtWVPb6aCTvTJ0gu9mk+A0wCwdxlKWReFfXhcvhuonFrfYDwfSnSEkxsA==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index 1565931..aba516b 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@tarojs/webpack5-runner": "^4.0.0", "@types/react": "^18.2.66", "babel-preset-taro": "^4.0.0", + "miniprogram-api-typings": "^5.2.0", "typescript": "^5.4.5" } } diff --git a/src/app.tsx b/src/app.tsx index 9209af4..209a279 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,6 +1,7 @@ import "./app.scss"; +import type { ReactNode } from "react"; -function App(props) { +function App(props: { children?: ReactNode }) { const { children } = props; return children ?? null; } diff --git a/src/pages/profile/index.config.ts b/src/pages/profile/index.config.ts index 186f3e0..0ef25ba 100644 --- a/src/pages/profile/index.config.ts +++ b/src/pages/profile/index.config.ts @@ -1,3 +1,4 @@ export default definePageConfig({ + navigationStyle: "custom", navigationBarTitleText: "个人信息" }); diff --git a/src/pages/profile/index.scss b/src/pages/profile/index.scss index 719270f..d22046a 100644 --- a/src/pages/profile/index.scss +++ b/src/pages/profile/index.scss @@ -1,3 +1,214 @@ .profile-page { - display: block; + min-height: 100vh; + box-sizing: border-box; + padding: calc(var(--profile-top-safe-height, 0px) + 22rpx) 0 72rpx; + background: + linear-gradient(180deg, rgba(39, 44, 60, 0.98) 0 150rpx, transparent 150rpx), + linear-gradient(180deg, #181d2a 0%, #171c29 100%); + color: var(--color-text-primary); +} + +.profile-page__nav { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 26rpx; +} + +.profile-page__back { + display: flex; + align-items: center; + justify-content: flex-start; + width: 112rpx; + height: 72rpx; +} + +.profile-page__back-icon { + width: 24rpx; + height: 24rpx; + border-left: 4rpx solid var(--color-text-white); + border-bottom: 4rpx solid var(--color-text-white); + transform: rotate(45deg); +} + +.profile-page__title { + color: var(--color-text-white); + font-size: 52rpx; + font-weight: 600; + letter-spacing: 2rpx; +} + +.profile-page__save { + display: flex; + align-items: center; + justify-content: center; + min-width: 126rpx; + height: 60rpx; + padding: 0 24rpx; + border-radius: 24rpx; + background: linear-gradient(135deg, var(--color-brand-start), var(--color-brand-end)); + box-shadow: 0 12rpx 24rpx var(--color-brand-shadow-soft); +} + +.profile-page__save-text { + color: var(--color-text-white); + font-size: 28rpx; +} + +.profile-page__content { + padding: 70rpx 30rpx 0; +} + +.profile-page__avatar-block { + display: flex; + flex-direction: column; + align-items: center; +} + +.profile-page__avatar { + display: flex; + align-items: center; + justify-content: center; + width: 154rpx; + height: 154rpx; + border-radius: 50%; + background: + radial-gradient(circle at 50% 34%, #f8fafc 0 20rpx, transparent 20rpx), + linear-gradient(180deg, #b8c1cf 0%, #7f8ba0 100%); + box-shadow: 0 16rpx 40rpx rgba(7, 10, 20, 0.26); + overflow: hidden; +} + +.profile-page__avatar-image { + width: 100%; + height: 100%; + border-radius: 50%; + background: linear-gradient(180deg, #dbe2ed 0%, #aeb8c7 100%); +} + +.profile-page__avatar-placeholder { + position: relative; + width: 100%; + height: 100%; + border-radius: 50%; + background: + radial-gradient(circle at 50% 34%, #f6f8fb 0 24rpx, transparent 25rpx), + linear-gradient(180deg, #d9dfea 0 58%, #b7c1d1 58% 100%); +} + +.profile-page__avatar-head { + position: absolute; + top: 36rpx; + left: 50%; + width: 42rpx; + height: 42rpx; + margin-left: -21rpx; + border-radius: 50%; + background: #2d3650; +} + +.profile-page__avatar-body { + position: absolute; + left: 50%; + bottom: 24rpx; + width: 90rpx; + height: 54rpx; + margin-left: -45rpx; + border-radius: 50rpx 50rpx 28rpx 28rpx; + background: #2d3650; +} + +.profile-page__avatar-tip { + margin-top: 42rpx; + color: #08e0da; + font-size: 30rpx; +} + +.profile-page__nickname-block { + margin: 110rpx 30rpx 0; + padding: 0 12rpx 18rpx; + border-top: 2rpx solid rgba(255, 255, 255, 0.28); + border-bottom: 2rpx solid rgba(255, 255, 255, 0.28); +} + +.profile-page__nickname-input { + width: 100%; + height: 108rpx; + color: rgba(255, 255, 255, 0.64); + font-size: 34rpx; + line-height: 108rpx; + text-align: center; +} + +.profile-page__nickname-placeholder { + color: rgba(255, 255, 255, 0.28); +} + +.profile-page__info-card { + margin-top: 92rpx; + padding: 18rpx 36rpx; + border-radius: 30rpx; + background: rgba(41, 46, 61, 0.96); + box-shadow: + inset 0 0 0 2rpx rgba(255, 255, 255, 0.03), + 0 16rpx 34rpx rgba(5, 9, 18, 0.18); +} + +.profile-page__info-row { + display: flex; + align-items: center; + justify-content: space-between; + min-height: 154rpx; + gap: 20rpx; + border-bottom: 2rpx solid rgba(255, 255, 255, 0.04); +} + +.profile-page__info-row--last { + border-bottom: 0; +} + +.profile-page__info-main { + display: flex; + align-items: baseline; + gap: 16rpx; + min-width: 0; +} + +.profile-page__info-label { + color: var(--color-text-white); + font-size: 34rpx; +} + +.profile-page__info-value { + color: rgba(255, 255, 255, 0.42); + font-size: 28rpx; +} + +.profile-page__info-value--muted { + color: rgba(255, 255, 255, 0.26); +} + +.profile-page__info-action { + display: flex; + align-items: center; + gap: 18rpx; + flex-shrink: 0; +} + +.profile-page__info-action-text { + color: #08e0da; + font-size: 28rpx; +} + +.profile-page__info-action-text--muted { + color: rgba(255, 255, 255, 0.42); +} + +.profile-page__info-action-text--bound { + color: #08e0da; +} + +.profile-page__info-arrow { + color: rgba(255, 255, 255, 0.72); + font-size: 28rpx; } diff --git a/src/pages/profile/index.tsx b/src/pages/profile/index.tsx index cdc847e..bc54c0e 100644 --- a/src/pages/profile/index.tsx +++ b/src/pages/profile/index.tsx @@ -1,26 +1,181 @@ -import SecondaryPage, { type SecondaryPageSection } from "../../components/secondary-page"; +import { Input, Text, View } from "@tarojs/components"; +import Taro from "@tarojs/taro"; +import type { CSSProperties } from "react"; +import { useEffect, useState } from "react"; import "./index.scss"; -const sections: SecondaryPageSection[] = [ - { - title: "资料编辑", - items: [ - { label: "头像", value: "支持后续替换" }, - { label: "昵称", value: "张天爱" }, - { label: "手机号", value: "135****2598" }, - { label: "修改密码", value: "待接入" } - ] +type ProfileState = { + avatar: string; + nickname: string; + phone: string; + email: string; + wechatBound: boolean; +}; + +type InfoAction = "replacePhone" | "replaceEmail" | "bindWechat"; + +const defaultProfile: ProfileState = { + avatar: "", + nickname: "玛利亚", + phone: "139****0753", + email: "", + wechatBound: false +}; + +function getActionToast(action: InfoAction) { + switch (action) { + case "replacePhone": + return "手机号更换功能待接入"; + case "replaceEmail": + return "邮箱更换功能待接入"; + case "bindWechat": + return "微信绑定功能待接入"; + default: + return "功能待接入"; } -]; +} export default function ProfilePage() { + const [profile, setProfile] = useState(defaultProfile); + const [draftNickname, setDraftNickname] = useState(defaultProfile.nickname); + const [topSafeHeight, setTopSafeHeight] = useState(0); + + useEffect(() => { + const windowInfo = typeof Taro.getWindowInfo === "function" ? Taro.getWindowInfo() : Taro.getSystemInfoSync(); + const menuButtonRect = + typeof Taro.getMenuButtonBoundingClientRect === "function" ? Taro.getMenuButtonBoundingClientRect() : null; + const safeTop = menuButtonRect?.bottom || windowInfo.statusBarHeight || 0; + + setTopSafeHeight(safeTop); + }, []); + + const pageStyle = { + "--profile-top-safe-height": `${topSafeHeight}px` + } as CSSProperties; + + const showToast = (title: string, icon: "none" | "success" = "none") => { + Taro.showToast({ + title, + icon + }); + }; + + const handleBack = () => { + const pages = Taro.getCurrentPages(); + + if (pages.length > 1) { + Taro.navigateBack({ delta: 1 }); + return; + } + + Taro.redirectTo({ url: "/pages/mine/index" }); + }; + + const handleSave = () => { + const nickname = draftNickname.trim() || defaultProfile.nickname; + + setProfile((current) => ({ + ...current, + nickname + })); + setDraftNickname(nickname); + showToast("保存成功", "success"); + }; + + const handleAvatarClick = () => { + showToast("头像更换功能待接入"); + }; + + const handleInfoAction = (action: InfoAction) => { + showToast(getActionToast(action)); + }; + return ( - + + + + + + + 个人信息 + + + 保存 + + + + + + + {profile.avatar ? ( + + ) : ( + + + + + )} + + + 点击更换头像 + + + + setDraftNickname(event.detail.value)} + /> + + + + handleInfoAction("replacePhone")}> + + 手机号 + ({profile.phone}) + + + + 更换 + > + + + + handleInfoAction("replaceEmail")}> + + 邮箱 + + {profile.email || "暂未填写"} + + + + + 更换 + > + + + + handleInfoAction("bindWechat")}> + + 微信 + + + + + {profile.wechatBound ? "已绑定" : "去绑定"} + + > + + + + + ); } diff --git a/tsconfig.json b/tsconfig.json index effe83c..7e77ef4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,7 +18,7 @@ }, "types": [ "@tarojs/taro", - "wechat-miniprogram" + "miniprogram-api-typings" ] }, "include": [