feat: 我的、设置、打印指南页面开发完成

This commit is contained in:
R524809
2026-05-07 16:32:49 +08:00
parent 0737c6165e
commit c732111e12
43 changed files with 1702 additions and 837 deletions
+3 -1
View File
@@ -6,6 +6,8 @@
"backgroundColor": "#F8F0E0",
"usingComponents": {
"van-icon": "@vant/weapp/icon/index",
"nav-bar": "../../components3.0/nav-bar/nav-bar"
"van-popup": "@vant/weapp/popup/index",
"nav-bar": "../../components3.0/nav-bar/nav-bar",
"toy-icon": "../../toy/icon/icon"
}
}
+144 -4
View File
@@ -57,8 +57,8 @@ page {
}
.profile-hero__avatar {
width: 256rpx;
height: 256rpx;
width: 224rpx;
height: 224rpx;
border-radius: 50%;
border: 8rpx solid @bg-white;
background: @bg-gray;
@@ -98,7 +98,7 @@ page {
.profile-hero__name {
margin-top: 32rpx;
font-size: 40rpx;
font-size: 36rpx;
font-weight: 700;
color: @text-title;
text-align: center;
@@ -116,7 +116,7 @@ page {
.profile-hero__stat {
flex: 1;
background: @bg-header;
border-radius: @radius;
border-radius: 48rpx;
border: 1rpx solid rgba(124, 118, 106, 0.12);
padding: 32rpx 24rpx;
text-align: center;
@@ -321,4 +321,144 @@ page {
color: @text-secondary;
letter-spacing: 0.2em;
text-transform: uppercase;
}
.profile-editor-popup {
border-radius: 40rpx 40rpx 0 0;
overflow: hidden;
}
.profile-editor {
overflow-y: auto;
padding: 32rpx 40rpx;
padding-bottom: env(safe-area-inset-bottom);
background: @bg-white;
box-sizing: border-box;
}
.profile-editor__header {
margin-bottom: 24rpx;
}
.profile-editor__title {
display: block;
font-size: 36rpx;
font-weight: 700;
color: @text-title;
}
.profile-editor__desc {
display: block;
margin-top: 12rpx;
font-size: 24rpx;
line-height: 1.5;
color: @text-secondary;
}
.profile-editor__avatar-section {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
margin-bottom: 28rpx;
}
.profile-editor__avatar-button {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
margin: 0;
background: transparent;
border: 0;
line-height: 1;
}
.profile-editor__avatar-button::after {
border: 0;
}
.profile-editor__avatar {
width: 180rpx;
height: 180rpx;
border-radius: 50%;
background: @bg-gray;
border: 2rpx dashed fade(@text-secondary, 30%);
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
}
.profile-editor__avatar-placeholder {
width: 120rpx;
font-size: 24rpx;
line-height: 1.4;
color: @text-secondary;
text-align: center;
}
.profile-editor__avatar-img {
width: 100%;
height: 100%;
}
.profile-editor__field {
margin-bottom: 28rpx;
}
.profile-editor__label {
display: block;
margin-bottom: 16rpx;
font-size: 26rpx;
font-weight: 600;
color: @text-title;
}
.profile-editor__input {
width: 100%;
height: 96rpx;
padding: 0 28rpx;
border-radius: 24rpx;
background: @bg-header;
border: 1rpx solid rgba(124, 118, 106, 0.12);
box-sizing: border-box;
font-size: 30rpx;
color: @text-title;
}
.profile-editor__actions {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 24rpx;
padding-top: 20rpx;
}
.profile-editor__action {
height: 92rpx;
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
font-size: 30rpx;
font-weight: 700;
}
.profile-editor__action--ghost {
background: @bg-header;
color: @text-title;
border: 1rpx solid rgba(124, 118, 106, 0.12);
}
.profile-editor__action--primary {
background: @brand;
color: @text-selected-btn;
box-shadow: @shadow;
}
.profile-editor__action--disabled {
opacity: 0.6;
}
+137 -14
View File
@@ -1,31 +1,154 @@
import { getTodayDownloadCount } from '../../utils/downloadPrint';
import { getDownloadLogCount } from '../../utils/downloadLogs';
import { getFavoriteCount } from '../../utils/favorites';
import {
getCachedUser,
getUser,
type UserInfo,
updateUserProfile,
} from '../../utils/auth';
Page({
DEFAULT_AVATAR_URL: '/assets/imgs/doodle-head.png',
data: {
userName: '微信用户',
avatarUrl: '',
printCount: 0,
favoriteCount: 0,
version: '',
isDevEnv: false,
version: '3.0.0',
showProfileEditor: false,
draftNickName: '',
draftAvatarUrl: '/assets/imgs/doodle-head.png',
isSavingProfile: false,
},
onLoad() {
const info = wx.getAccountInfoSync();
const version = info.miniProgram.version || '开发版';
const isDevEnv = info.miniProgram.envVersion === 'develop';
this.setData({ version, isDevEnv });
this.syncUserInfoFromApp();
},
onShow() {
this.refreshStats();
this.syncUserInfoFromApp();
void this.refreshStats();
},
refreshStats() {
const printCount = getTodayDownloadCount();
// 收藏数待与收藏页数据源打通后接入
const favoriteCount = 0;
this.setData({ printCount, favoriteCount });
async syncUserInfoFromApp() {
const app = getApp<IAppOption>();
const cachedUser = app.globalData.user || getCachedUser();
if (cachedUser) {
this.applyUserInfo(cachedUser);
}
try {
const latestUser = await getUser();
this.applyUserInfo(latestUser);
} catch (error) {
console.error('获取用户信息失败', error);
}
},
applyUserInfo(user: UserInfo) {
const userName = user.nickName?.trim() || '微信用户';
const avatarUrl =
user.avatarUrl || (this as any).DEFAULT_AVATAR_URL || '';
this.setData({
userName,
avatarUrl,
draftNickName: userName === '微信用户' ? '' : userName,
draftAvatarUrl: avatarUrl,
});
},
async refreshStats() {
try {
const [printCount, favoriteCount] = await Promise.all([
getDownloadLogCount(),
getFavoriteCount(),
]);
this.setData({ printCount, favoriteCount });
} catch (e) {
console.error('refreshStats failed', e);
}
},
onOpenProfileEditor() {
this.setData({
showProfileEditor: true,
draftNickName:
this.data.userName === '微信用户' ? '' : this.data.userName,
draftAvatarUrl: this.data.avatarUrl,
});
},
onCloseProfileEditor() {
if (this.data.isSavingProfile) return;
this.setData({ showProfileEditor: false });
},
onChooseAvatar(e: WechatMiniprogram.CustomEvent<{ avatarUrl?: string }>) {
const avatarUrl = e.detail?.avatarUrl || '';
if (!avatarUrl) return;
this.setData({ draftAvatarUrl: avatarUrl });
},
onNicknameInput(e: WechatMiniprogram.CustomEvent<{ value: string }>) {
const nickName = (e.detail?.value || '').trim();
this.setData({ draftNickName: nickName });
},
async onSubmitProfile() {
if (this.data.isSavingProfile) return;
const nickName = this.data.draftNickName.trim();
if (!nickName) {
wx.showToast({ title: '请输入昵称', icon: 'none' });
return;
}
if (!this.data.draftAvatarUrl) {
wx.showToast({ title: '请选择头像', icon: 'none' });
return;
}
this.setData({ isSavingProfile: true });
try {
const avatarUrl = await this.persistAvatarIfNeeded(
this.data.draftAvatarUrl,
);
const user = await updateUserProfile({
nickName,
avatarUrl,
});
this.applyUserInfo(user);
this.setData({ showProfileEditor: false });
wx.showToast({ title: '资料已更新', icon: 'success' });
} catch (error) {
console.error('更新用户资料失败', error);
wx.showToast({ title: '更新失败,请稍后重试', icon: 'none' });
} finally {
this.setData({ isSavingProfile: false });
}
},
async persistAvatarIfNeeded(avatarUrl: string): Promise<string> {
if (!avatarUrl) return avatarUrl;
if (
avatarUrl.startsWith('cloud://') ||
avatarUrl.startsWith('http://') ||
avatarUrl.startsWith('https://')
) {
return avatarUrl;
}
const user = await getUser();
const cloudPath = `user-avatars/${user._id}-${Date.now()}.png`;
const uploadRes = await wx.cloud.uploadFile({
cloudPath,
filePath: avatarUrl,
});
return uploadRes.fileID;
},
onGoToDownloads() {
@@ -47,7 +170,7 @@ Page({
},
onGoToSettings() {
wx.showToast({ title: '功能开发中', icon: 'none' });
wx.navigateTo({ url: '/supportPages/settings/settings' });
},
onGoToDebug() {
+95 -20
View File
@@ -8,19 +8,14 @@
<view class="profile-hero__tint"></view>
<view class="profile-hero__avatar-wrap">
<view class="profile-hero__avatar">
<text
class="profile-hero__avatar-emoji"
wx:if="{{!avatarUrl}}"
>🦆</text
>
<image
wx:else
class="profile-hero__avatar-img"
src="{{avatarUrl}}"
mode="aspectFill" />
</view>
<view
class="profile-hero__edit-badge"
bindtap="onOpenProfileEditor"
aria-role="button"
aria-label="编辑头像">
<van-icon name="edit" size="28rpx" color="#6c5a00" />
@@ -73,9 +68,13 @@
</view>
<text class="profile-row__label">打印指南</text>
</view>
<text class="profile-row__chevron"></text>
<toy-icon
class="profile-row__chevron"
name="arrow-right"
size="40rpx"
color="#9a958b" />
</view>
<view class="profile-row" bindtap="onGoToFeedback">
<!-- <view class="profile-row" bindtap="onGoToFeedback">
<view class="profile-row__left">
<view class="profile-row__icon-bg">
<van-icon
@@ -85,8 +84,12 @@
</view>
<text class="profile-row__label">提交反馈</text>
</view>
<text class="profile-row__chevron"></text>
</view>
<toy-icon
class="profile-row__chevron"
name="arrow-right"
size="40rpx"
color="#9a958b" />
</view> -->
<view class="profile-row" bindtap="onGoToSettings">
<view class="profile-row__left">
<view class="profile-row__icon-bg">
@@ -97,7 +100,11 @@
</view>
<text class="profile-row__label">设置</text>
</view>
<text class="profile-row__chevron"></text>
<toy-icon
class="profile-row__chevron"
name="arrow-right"
size="40rpx"
color="#9a958b" />
</view>
<view
wx:if="{{isDevEnv}}"
@@ -109,9 +116,13 @@
</view>
<text class="profile-row__label">Debug 工具</text>
</view>
<text class="profile-row__chevron"></text>
<toy-icon
class="profile-row__chevron"
name="arrow-right"
size="40rpx"
color="#9a958b" />
</view>
<view
<!-- <view
class="profile-row profile-row--last"
bindtap="onGoToAbout">
<view class="profile-row__left">
@@ -123,12 +134,16 @@
</view>
<text class="profile-row__label">关于涂鸦丫</text>
</view>
<text class="profile-row__chevron"></text>
</view>
<toy-icon
class="profile-row__chevron"
name="arrow-right"
size="40rpx"
color="#9a958b" />
</view> -->
</view>
<!-- 专业版推广(Stitch 深色卡) -->
<view class="profile-upsell" bindtap="onGoToPremium">
<!-- <view class="profile-upsell" bindtap="onGoToPremium">
<van-icon
name="gem-o"
class="profile-upsell__deco"
@@ -141,13 +156,73 @@
>
<view class="profile-upsell__btn">立即升级</view>
</view>
</view>
</view> -->
<view class="profile-version">
<text class="profile-version__text"
>Doodle Mini v{{version}}</text
>
<text class="profile-version__text">涂鸦丫 v{{version}}</text>
</view>
</view>
</view>
<van-popup
show="{{showProfileEditor}}"
position="bottom"
round
safe-area-inset-bottom="{{false}}"
custom-class="profile-editor-popup"
bind:close="onCloseProfileEditor">
<view class="profile-editor">
<view class="profile-editor__header">
<text class="profile-editor__title">完善资料</text>
<text class="profile-editor__desc"
>选择头像并填写昵称后保存</text
>
</view>
<view class="profile-editor__avatar-section">
<button
class="profile-editor__avatar-button"
open-type="chooseAvatar"
bindchooseavatar="onChooseAvatar">
<view class="profile-editor__avatar">
<text
class="profile-editor__avatar-placeholder"
wx:if="{{!draftAvatarUrl}}"
>选择头像</text
>
<image
wx:else
class="profile-editor__avatar-img"
src="{{draftAvatarUrl}}"
mode="aspectFill" />
</view>
</button>
</view>
<view class="profile-editor__field">
<text class="profile-editor__label">昵称</text>
<input
class="profile-editor__input"
type="nickname"
placeholder="请输入昵称"
maxlength="20"
value="{{draftNickName}}"
bindblur="onNicknameInput"
bindinput="onNicknameInput" />
</view>
<view class="profile-editor__actions">
<view
class="profile-editor__action profile-editor__action--ghost"
bindtap="onCloseProfileEditor">
取消
</view>
<view
class="profile-editor__action profile-editor__action--primary {{isSavingProfile ? 'profile-editor__action--disabled' : ''}}"
bindtap="onSubmitProfile">
{{isSavingProfile ? '保存中...' : '保存'}}
</view>
</view>
</view>
</van-popup>
</view>