wxapp使用submodule

This commit is contained in:
lik
2026-05-27 15:10:30 +08:00
parent 43436f9a37
commit afc0e4d2e2
957 changed files with 14404 additions and 1 deletions

View File

@@ -0,0 +1,195 @@
// pages/mine/comp_address/comp_address.js
Component({
properties: {
visible: {
type: Boolean,
value: false
}
},
data: {
addresses: [],
editData: {
_index: -1,
label: '',
province: '',
city: '',
district: '',
address: '',
postcode: '',
isDefault: false
},
isEditing: false
},
observers: {
visible(newVal) {
if (newVal) {
this.initAddressData()
}
}
},
methods: {
initAddressData() {
const app = getApp()
const user = app.globalData.user || {}
const addresses = user.addresses || []
this.setData({
addresses: addresses.map(item => ({ ...item })),
isEditing: false,
editData: {
_index: -1,
label: '',
province: '',
city: '',
district: '',
address: '',
postcode: '',
isDefault: false
}
})
},
onClose() {
this.triggerEvent('close')
},
onSheetTap() {},
onAddAddress() {
this.setData({
isEditing: true,
editData: {
_index: -1,
label: '',
province: '',
city: '',
district: '',
address: '',
postcode: '',
isDefault: this.data.addresses.length === 0
}
})
},
onEditAddress(e) {
const index = e.currentTarget.dataset.index
const item = this.data.addresses[index]
this.setData({
isEditing: true,
editData: {
_index: index,
label: item.label || '',
province: item.province || '',
city: item.city || '',
district: item.district || '',
address: item.address || '',
postcode: item.postcode || '',
isDefault: item.isDefault || false
}
})
},
onDeleteAddress(e) {
const index = e.currentTarget.dataset.index
wx.showModal({
title: '提示',
content: '确定删除该地址吗?',
success: (res) => {
if (res.confirm) {
const addresses = this.data.addresses.slice()
addresses.splice(index, 1)
if (addresses.length > 0 && !addresses.some(a => a.isDefault)) {
addresses[0].isDefault = true
}
this.setData({ addresses })
this.triggerEvent('save', { addresses })
}
}
})
},
onSetDefault(e) {
const index = e.currentTarget.dataset.index
const addresses = this.data.addresses.map((item, i) => ({
...item,
isDefault: i === index
}))
this.setData({ addresses })
this.triggerEvent('save', { addresses })
},
onBackToList() {
this.setData({ isEditing: false })
},
onRegionChange(e) {
const region = e.detail.value
this.setData({
'editData.province': region[0] || '',
'editData.city': region[1] || '',
'editData.district': region[2] || ''
})
},
onFieldInput(e) {
const field = e.currentTarget.dataset.field
this.setData({
[`editData.${field}`]: e.detail.value
})
},
onDefaultChange(e) {
this.setData({
'editData.isDefault': e.detail.value
})
},
onSaveEdit() {
const editData = this.data.editData
if (!editData.label.trim()) {
wx.showToast({ title: '请输入地址标签', icon: 'none' })
return
}
if (!editData.province || !editData.city || !editData.district) {
wx.showToast({ title: '请选择所在地区', icon: 'none' })
return
}
if (!editData.address.trim()) {
wx.showToast({ title: '请输入详细地址', icon: 'none' })
return
}
const addresses = this.data.addresses.slice()
const newItem = {
label: editData.label.trim(),
province: editData.province,
city: editData.city,
district: editData.district,
address: editData.address.trim(),
postcode: editData.postcode.trim(),
isDefault: editData.isDefault
}
if (editData.isDefault) {
addresses.forEach(item => { item.isDefault = false })
}
if (editData._index >= 0) {
addresses[editData._index] = newItem
} else {
if (addresses.length === 0) {
newItem.isDefault = true
}
addresses.push(newItem)
}
this.setData({
addresses,
isEditing: false
})
this.triggerEvent('save', { addresses })
}
}
})

View File

@@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"t-icon": "tdesign-miniprogram/icon/icon"
}
}

View File

@@ -0,0 +1,79 @@
<view class="address-overlay" wx:if="{{visible}}" bindtap="onClose">
<view class="address-sheet" catchtap="onSheetTap">
<view class="address-header">
<view class="address-back" wx:if="{{isEditing}}" bindtap="onBackToList">
<t-icon name="chevron-left" size="36rpx" color="#999" />
</view>
<text class="address-title">{{isEditing ? (editData._index >= 0 ? '编辑地址' : '新增地址') : '邮寄地址'}}</text>
<view class="address-close" bindtap="onClose">
<t-icon name="close" size="36rpx" color="#999" />
</view>
</view>
<view class="address-body" wx:if="{{!isEditing}}">
<view class="address-empty" wx:if="{{addresses.length === 0}}">
<t-icon name="location" size="80rpx" color="#CCCCCC" />
<text class="address-empty-text">暂无地址,点击添加</text>
</view>
<view class="address-list" wx:if="{{addresses.length > 0}}">
<view class="address-card" wx:for="{{addresses}}" wx:key="index">
<view class="address-card-main" bindtap="onEditAddress" data-index="{{index}}">
<view class="address-card-top">
<text class="address-label">{{item.label}}</text>
<text class="address-default-tag" wx:if="{{item.isDefault}}">默认</text>
</view>
<view class="address-card-content">
<text class="address-region">{{item.province}} {{item.city}} {{item.district}}</text>
<text class="address-detail">{{item.address}}</text>
<text class="address-postcode" wx:if="{{item.postcode}}">邮编:{{item.postcode}}</text>
</view>
</view>
<view class="address-card-actions">
<view class="address-action-default" bindtap="onSetDefault" data-index="{{index}}">
<t-icon name="{{item.isDefault ? 'check-circle-filled' : 'circle'}}" size="32rpx" color="{{item.isDefault ? '#FF8500' : '#999'}}" />
<text class="action-text {{item.isDefault ? 'active' : ''}}">{{item.isDefault ? '默认地址' : '设为默认'}}</text>
</view>
<view class="address-action-delete" bindtap="onDeleteAddress" data-index="{{index}}">
<t-icon name="delete" size="32rpx" color="#999" />
<text class="action-text">删除</text>
</view>
</view>
</view>
</view>
</view>
<view class="address-body" wx:if="{{isEditing}}">
<view class="address-form">
<view class="form-item">
<text class="form-label">地址标签</text>
<input class="form-input" value="{{editData.label}}" placeholder="如:家庭、公司" maxlength="20" bindinput="onFieldInput" data-field="label" />
</view>
<view class="form-item">
<text class="form-label">所在地区</text>
<picker mode="region" value="{{[editData.province, editData.city, editData.district]}}" bindchange="onRegionChange">
<view class="form-picker">
{{editData.province || '请选择地区'}}{{editData.city ? ' ' + editData.city : ''}}{{editData.district ? ' ' + editData.district : ''}}
</view>
</picker>
</view>
<view class="form-item">
<text class="form-label">详细地址</text>
<input class="form-input" value="{{editData.address}}" placeholder="请输入详细地址" maxlength="100" bindinput="onFieldInput" data-field="address" />
</view>
<view class="form-item form-item-switch">
<text class="form-label">设为默认地址</text>
<switch checked="{{editData.isDefault}}" bindchange="onDefaultChange" color="#FF8500" />
</view>
</view>
</view>
<view class="address-footer">
<button class="address-add-btn" wx:if="{{!isEditing}}" bindtap="onAddAddress">添加新地址</button>
<button class="address-save-btn" wx:if="{{isEditing}}" bindtap="onSaveEdit">保存</button>
</view>
</view>
</view>

View File

@@ -0,0 +1,237 @@
.address-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.address-sheet {
background: #FFFFFF;
border-radius: 32rpx 32rpx 0 0;
padding-bottom: env(safe-area-inset-bottom);
animation: slideUp 0.3s ease;
max-height: 85vh;
overflow-y: auto;
display: flex;
flex-direction: column;
}
@keyframes slideUp {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
.address-header {
display: flex;
align-items: center;
justify-content: center;
padding: 32rpx;
position: relative;
border-bottom: 1rpx solid #F5F5F5;
flex-shrink: 0;
}
.address-title {
font-size: 32rpx;
font-weight: 600;
color: #1A1A1A;
}
.address-back {
position: absolute;
left: 32rpx;
top: 50%;
transform: translateY(-50%);
padding: 8rpx;
}
.address-close {
position: absolute;
right: 32rpx;
top: 50%;
transform: translateY(-50%);
padding: 8rpx;
}
.address-body {
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
}
.address-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 32rpx;
}
.address-empty-text {
margin-top: 24rpx;
font-size: 28rpx;
color: #999999;
}
.address-list {
padding: 24rpx 32rpx;
}
.address-card {
background: #FAFAFA;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 24rpx;
}
.address-card-main {
margin-bottom: 16rpx;
}
.address-card-top {
display: flex;
align-items: center;
gap: 16rpx;
margin-bottom: 12rpx;
}
.address-label {
font-size: 30rpx;
font-weight: 600;
color: #1A1A1A;
}
.address-default-tag {
font-size: 22rpx;
color: #FF8500;
background: rgba(255, 133, 0, 0.1);
padding: 4rpx 12rpx;
border-radius: 8rpx;
}
.address-card-content {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.address-region {
font-size: 28rpx;
color: #666666;
}
.address-detail {
font-size: 28rpx;
color: #1A1A1A;
line-height: 1.5;
}
.address-postcode {
font-size: 24rpx;
color: #999999;
}
.address-card-actions {
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1rpx solid #EEEEEE;
padding-top: 16rpx;
}
.address-action-default,
.address-action-delete {
display: flex;
align-items: center;
gap: 8rpx;
padding: 8rpx;
}
.action-text {
font-size: 26rpx;
color: #666666;
}
.action-text.active {
color: #FF8500;
}
.address-footer {
padding: 4rpx 32rpx 48rpx;
flex-shrink: 0;
}
.address-add-btn {
background: linear-gradient(135deg, #FF9B33 0%, #FF8500 100%);
color: #FFFFFF;
font-size: 30rpx;
font-weight: 600;
padding: 28rpx;
border-radius: 20rpx;
border: none;
line-height: 1.5;
}
.address-add-btn::after {
border: none;
}
.address-form {
padding: 0 32rpx;
}
.form-item {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 100rpx;
border-bottom: 1rpx solid #F5F5F5;
}
.form-item-switch {
justify-content: space-between;
}
.form-label {
font-size: 30rpx;
color: #1A1A1A;
font-weight: 500;
flex-shrink: 0;
margin-right: 24rpx;
}
.form-input {
flex: 1;
text-align: right;
font-size: 30rpx;
color: #1A1A1A;
}
.form-picker {
flex: 1;
text-align: right;
font-size: 30rpx;
color: #666666;
}
.address-save-btn {
background: linear-gradient(135deg, #FF9B33 0%, #FF8500 100%);
color: #FFFFFF;
font-size: 30rpx;
font-weight: 600;
padding: 28rpx;
border-radius: 20rpx;
border: none;
line-height: 1.5;
}
.address-save-btn::after {
border: none;
}

View File

@@ -0,0 +1,135 @@
// pages/mine/comp_profile/comp_profile.js
Component({
properties: {
visible: {
type: Boolean,
value: false
}
},
data: {
editData: {
name: '',
sex: 'male',
birth: '1970-1-1',
province: '',
city: '',
district: '',
phone: ''
},
today: ''
},
lifetimes: {
attached() {
const today = new Date()
this.setData({
today: `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`
})
},
ready() {
this.initEditData()
}
},
observers: {
visible(newVal) {
if (newVal) {
this.initEditData()
}
}
},
methods: {
initEditData() {
const app = getApp()
const user = app.globalData.user || {}
const profile = user.profile || {}
const location = user.location || {}
let birth = ''
if (profile.birth) {
const date = new Date(profile.birth)
birth = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
}
this.setData({
editData: {
avatar: profile.avatar || '/images/home-active2.png',
name: profile.name || '',
phone: profile.mobile || '',
sex: profile.sex || '',
birth: birth,
province: location.province || '',
city: location.city || '',
district: location.district || ''
}
})
},
onClose() {
this.triggerEvent('close')
},
onSheetTap() {},
onChooseAvatar() {
wx.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFilePath = res.tempFiles[0].tempFilePath
this.setData({
'editData.avatar': tempFilePath
})
}
})
},
onSelectSex(e) {
const sex = e.currentTarget.dataset.sex
this.setData({
'editData.sex': sex
})
},
onBirthChange(e) {
this.setData({
'editData.birth': e.detail.value
})
},
onRegionChange(e) {
const region = e.detail.value
this.setData({
'editData.province': region[0] || '',
'editData.city': region[1] || '',
'editData.district': region[2] || ''
})
},
onFieldInput(e) {
const field = e.currentTarget.dataset.field
this.setData({
[`editData.${field}`]: e.detail.value
})
},
onSave() {
const editData = this.data.editData
if (editData.idnumber && editData.idnumber.length !== 18) {
wx.showToast({
title: '身份证号应为18位',
icon: 'none'
})
return
}
this.triggerEvent('save', {
editData: editData
})
}
}
})

View File

@@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"t-icon": "tdesign-miniprogram/icon/icon"
}
}

View File

@@ -0,0 +1,64 @@
<view class="profile-overlay" wx:if="{{visible}}" bindtap="onClose">
<view class="profile-sheet" catchtap="onSheetTap">
<view class="profile-header">
<text class="profile-title">个人信息</text>
<view class="profile-close" bindtap="onClose">
<t-icon name="close" size="36rpx" color="#999" />
</view>
</view>
<view class="profile-body">
<view class="profile-item profile-item-avatar" bindtap="onChooseAvatar">
<text class="profile-label">头像</text>
<view class="profile-avatar-wrap">
<image class="profile-avatar" src="{{editData.avatar}}" mode="aspectFill" />
<t-icon name="chevron-right" size="32rpx" color="#ccc" />
</view>
</view>
<view class="profile-item">
<text class="profile-label">姓名</text>
<input class="profile-input" value="{{editData.name}}" placeholder="请输入姓名" maxlength="20" bindinput="onFieldInput" data-field="name" />
</view>
<view class="profile-item">
<text class="profile-label">性别</text>
<view class="profile-sex-wrap">
<view class="profile-sex-tag {{editData.sex === 'male' ? 'profile-sex-tag-active' : ''}}" bindtap="onSelectSex" data-sex="male">
<text class="profile-sex-text">男</text>
</view>
<view class="profile-sex-tag {{editData.sex === 'female' ? 'profile-sex-tag-active' : ''}}" bindtap="onSelectSex" data-sex="female">
<text class="profile-sex-text">女</text>
</view>
</view>
</view>
<view class="profile-item">
<text class="profile-label">出生日期</text>
<picker mode="date" value="{{editData.birth}}" start="1930-01-01" end="{{today}}" bindchange="onBirthChange">
<view class="picker">
{{editData.birth || '请选择出生日期'}}
</view>
</picker>
</view>
<view class="profile-item">
<text class="profile-label">所在地区</text>
<picker mode="region" value="{{[editData.province, editData.city, editData.district]}}" bindchange="onRegionChange">
<view class="picker">
{{editData.province || '请选择地区'}}{{editData.city ? '' + editData.city : ''}}{{editData.district ? '' + editData.district : ''}}
</view>
</picker>
</view>
<view class="profile-item">
<text class="profile-label">手机号</text>
<text class="profile-value">{{editData.phone || '未绑定'}}</text>
</view>
</view>
<view class="profile-footer">
<button class="profile-save-btn" bindtap="onSave">保存</button>
</view>
</view>
</view>

View File

@@ -0,0 +1,158 @@
.profile-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.profile-sheet {
background: #FFFFFF;
border-radius: 32rpx 32rpx 0 0;
padding-bottom: env(safe-area-inset-bottom);
animation: slideUp 0.3s ease;
max-height: 85vh;
overflow-y: auto;
display: flex;
flex-direction: column;
}
@keyframes slideUp {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
.profile-header {
display: flex;
align-items: center;
justify-content: center;
padding: 32rpx;
position: relative;
border-bottom: 1rpx solid #F5F5F5;
flex-shrink: 0;
}
.profile-title {
font-size: 32rpx;
font-weight: 600;
color: #1A1A1A;
}
.profile-close {
position: absolute;
right: 32rpx;
top: 50%;
transform: translateY(-50%);
padding: 8rpx;
}
.profile-body {
padding: 0 32rpx;
flex: 1;
overflow-y: auto;
}
.profile-item {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 100rpx;
border-bottom: 1rpx solid #F5F5F5;
}
.profile-item-avatar {
justify-content: space-between;
}
.profile-item-last {
border-bottom: none;
}
.profile-label {
font-size: 30rpx;
color: #1A1A1A;
font-weight: 500;
flex-shrink: 0;
margin-right: 24rpx;
}
.profile-input {
flex: 1;
text-align: right;
font-size: 30rpx;
color: #1A1A1A;
}
.picker {
flex: 1;
text-align: right;
font-size: 30rpx;
color: #666666;
}
.profile-value {
font-size: 30rpx;
color: #666666;
}
.profile-avatar-wrap {
display: flex;
align-items: center;
gap: 16rpx;
}
.profile-avatar {
width: 96rpx;
height: 96rpx;
border-radius: 50%;
background: #F5F5F5;
}
.profile-sex-wrap {
display: flex;
gap: 20rpx;
}
.profile-sex-tag {
padding: 10rpx 48rpx;
border-radius: 8rpx;
background: #F5F5F5;
}
.profile-sex-tag-active {
background: linear-gradient(135deg, #FF9B33 0%, #FF8500 100%);
}
.profile-sex-text {
font-size: 28rpx;
color: #666666;
}
.profile-sex-tag-active .profile-sex-text {
color: #FFFFFF;
}
.profile-footer {
padding: 4rpx 32rpx 48rpx;
flex-shrink: 0;
}
.profile-save-btn {
background: linear-gradient(135deg, #FF9B33 0%, #FF8500 100%);
color: #FFFFFF;
font-size: 30rpx;
font-weight: 600;
padding: 28rpx;
border-radius: 20rpx;
border: none;
line-height: 1.5;
}
.profile-save-btn::after {
border: none;
}

228
pages/mine/mine.js Normal file
View File

@@ -0,0 +1,228 @@
const API = require('../../utils/api.js')
Page({
data: {
userInfo: {
avatar: '/images/home-active2.png',
name: '用户',
phone: '',
sex: '',
birth: '',
email: '',
idnumber: '',
ssn: '',
isLoggedIn: false
},
profileVisible: false,
menuGroups: [{
groupName: '个人服务',
items: [{
name: '个人信息',
icon: 'verify',
bindtap: 'onTapProfile'
}, {
name: '健康档案',
icon: 'user-vip',
bindtap: 'onTapHealth'
}, {
name: '邮寄地址',
icon: 'location',
bindtap: 'onTapAddress'
}]
}, {
groupName: '陪诊',
items: [{
name: '陪诊记录',
icon: 'assignment',
bindtap: 'onTapEscortRecord'
}]
}, {
groupName: '其他',
items: [{
name: '关于我们',
icon: 'info-circle',
bindtap: 'onTapAboutUs'
}]
}]
},
onLoad() {
this.loadUserInfo()
},
onShow() {
this.loadUserInfo()
},
loadUserInfo() {
const app = getApp()
if (app.globalData.user && app.globalData.user.profile) {
const profile = app.globalData.user.profile
this.setData({
'userInfo.isLoggedIn': true,
'userInfo.name': profile.name || '用户',
'userInfo.phone': profile.mobile || '',
'userInfo.sex': profile.sex || '',
'userInfo.birth': profile.birth ? this.formatDate(profile.birth) : '',
'userInfo.email': profile.email || '',
'userInfo.idnumber': profile.idnumber || '',
'userInfo.ssn': profile.ssn || ''
})
}
},
formatDate(date) {
if (!date) return ''
const d = new Date(date)
const year = d.getFullYear()
const month = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
},
onTapProfile() {
this.setData({ profileVisible: true })
},
onTapAddress() {
this.setData({ addressVisible: true })
},
onAddressClose() {
this.setData({ addressVisible: false })
},
onAddressSave(e) {
const app = getApp()
const user = app.globalData.user
if (!user || !user._id) {
wx.showToast({ title: '请先登录', icon: 'none' })
return
}
wx.showLoading({ title: '保存中...' })
const updateData = {
_id: user._id,
addresses: e.detail.addresses
}
API.user.update(updateData)
.then((data) => {
if (data.code === 0) {
app.globalData.user = data.data.user
wx.showToast({ title: '保存成功', icon: 'success' })
} else {
wx.showToast({ title: data.msg || '保存失败', icon: 'none' })
}
})
.catch(() => {
wx.showToast({ title: '网络请求失败', icon: 'none' })
})
.finally(() => {
wx.hideLoading()
})
},
onTapHealth() {
this.setData({ healthVisible: true })
},
onHealthClose() {
this.setData({ healthVisible: false })
},
onTapEscortRecord() {
wx.navigateTo({
url: '/pages/escort_record_list/escort_record_list'
})
},
onTapSetting() {
this.setData({ settingVisible: true })
},
onSettingClose() {
this.setData({ settingVisible: false })
},
onProfileClose() {
this.setData({ profileVisible: false })
},
onProfileSave(e) {
const newUserInfo = e.detail.editData
const app = getApp()
const user = app.globalData.user
if (!user || !user._id) {
wx.showToast({ title: '请先登录', icon: 'none' })
return
}
wx.showLoading({ title: '保存中...' })
const updateData = {
_id: user._id,
profile: {
name: newUserInfo.name,
sex: newUserInfo.sex,
birth: newUserInfo.birth ? new Date(newUserInfo.birth).toISOString() : null,
mobile: newUserInfo.phone
},
location: {
province: newUserInfo.province,
city: newUserInfo.city,
district: newUserInfo.district,
},
}
API.user.update(updateData)
.then((data) => {
if (data.code === 0) {
app.globalData.user = data.data.user
this.loadUserInfo()
wx.showToast({ title: '保存成功', icon: 'success' })
this.setData({ profileVisible: false })
} else {
wx.showToast({ title: data.msg || '保存失败', icon: 'none' })
}
})
.catch(() => {
wx.showToast({ title: '网络请求失败', icon: 'none' })
})
.finally(() => {
wx.hideLoading()
})
},
handleLogin(e) {
const phoneCode = e.detail.code ? e.detail.code : ''
API.user.wxGetPhoneNumber({ code: phoneCode })
.then((data) => {
const phoneNumber = data.data.phoneNumber
wx.setStorageSync('user_phonenumber', phoneNumber)
wx.login({
success: (res) => {
if (res.code) {
API.user.wxSignin({ phoneNumber, code: res.code })
.then((data) => {
if (data.code == 0) {
const app = getApp()
app.globalData.user = data.data.user
this.loadUserInfo()
}
})
} else {
console.log('登录失败!' + res.errMsg)
}
}
})
})
.catch((err) => {
wx.showToast({ title: '网络请求失败', icon: 'none' })
console.log('请求失败', err)
})
}
})

11
pages/mine/mine.json Normal file
View File

@@ -0,0 +1,11 @@
{
"navigationStyle": "custom",
"navigationBarTitleText": "Home",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"usingComponents": {
"t-icon": "tdesign-miniprogram/icon/icon",
"comp-profile": "./comp_profile/comp_profile",
"comp-address": "./comp_address/comp_address"
}
}

41
pages/mine/mine.wxml Normal file
View File

@@ -0,0 +1,41 @@
<view class="container">
<view class="header-card" bindtap="onTapProfile">
<image class="avatar" src="{{userInfo.avatar}}" mode="aspectFill"></image>
<text class="user-name">{{userInfo.name}}</text>
</view>
<view class="menu-section" wx:for="{{menuGroups}}" wx:key="groupName" wx:if="{{userInfo.isLoggedIn || item.groupName === '其他'}}">
<view class="menu-list">
<view class="menu-item" wx:for="{{item.items}}" wx:for-item="menuItem" wx:key="name" hover-class="menu-item-hover" bindtap="{{menuItem.bindtap}}">
<t-icon name="{{menuItem.icon}}" class="menu-icon" size="40rpx" />
<text class="menu-text">{{menuItem.name}}</text>
<t-icon name="chevron-right" class="menu-arrow" size="36rpx" />
</view>
</view>
</view>
<view class="login-section" wx:if="{{!userInfo.isLoggedIn}}">
<button class="login-btn" open-type="getPhoneNumber" bindgetphonenumber="handleLogin">
登录
</button>
</view>
<view class="footer-section">
<text class="version">版本号V 0.1.0</text>
<text class="copyright">© 2026 北京奕华盛科技 版权所有</text>
</view>
<comp-profile
id="profileComp"
visible="{{profileVisible}}"
bind:close="onProfileClose"
bind:save="onProfileSave"
/>
<comp-address
id="addressComp"
visible="{{addressVisible}}"
bind:close="onAddressClose"
bind:save="onAddressSave"
/>
</view>

153
pages/mine/mine.wxss Normal file
View File

@@ -0,0 +1,153 @@
page {
background-color: #F5F5F5;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
.container {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.header-card {
background: #FFFFFF;
padding: 150rpx 32rpx 48rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.avatar {
width: 120rpx;
height: 120rpx;
}
.user-name {
font-size: 36rpx;
font-weight: 600;
color: #1A1A1A;
margin-top: 24rpx;
}
.user-phone {
font-size: 26rpx;
color: #999999;
margin-top: 12rpx;
}
.login-section {
padding: 32rpx;
display: flex;
flex-direction: column;
gap: 20rpx;
}
.login-btn {
background: linear-gradient(135deg, #FF9B33 0%, #FF8500 100%);
color: #FFFFFF;
font-size: 30rpx;
font-weight: 600;
padding: 28rpx;
border-radius: 20rpx;
border: none;
line-height: 1.5;
}
.login-btn::after {
border: none;
}
.avatar-btn {
background: #FFFFFF;
color: #FF9B33;
font-size: 28rpx;
font-weight: 500;
padding: 24rpx;
border-radius: 20rpx;
border: 1rpx solid #FF9B33;
line-height: 1.5;
}
.avatar-btn::after {
border: none;
}
.nickname-input-wrap {
background: #FFFFFF;
border-radius: 20rpx;
padding: 24rpx 32rpx;
}
.nickname-input {
font-size: 28rpx;
color: #1F2937;
}
.menu-section {
padding: 24rpx 22rpx 0rpx 22rpx;
}
.menu-group-header {
font-size: 26rpx;
color: #999999;
padding: 16rpx 0;
margin-bottom: 16rpx;
}
.menu-list {
background: #FFFFFF;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
}
.menu-item {
display: flex;
align-items: center;
padding: 32rpx;
border-bottom: 1rpx solid #F5F5F5;
}
.menu-item:last-child {
border-bottom: none;
}
.menu-item-hover {
background: #FAFAFA;
}
.menu-icon {
color: #FF9B33;
margin-right: 24rpx;
}
.menu-text {
flex: 1;
font-size: 30rpx;
color: #1A1A1A;
font-weight: 500;
}
.menu-arrow {
color: #CCCCCC;
}
.footer-section {
display: flex;
flex-direction: column;
align-items: center;
padding: 60rpx 32rpx;
margin-top: auto;
}
.version {
font-size: 24rpx;
color: #B0B0B0;
margin-bottom: 12rpx;
}
.copyright {
font-size: 22rpx;
color: #B0B0B0;
}