wxapp使用submodule
This commit is contained in:
195
pages/mine/comp_address/comp_address.js
Normal file
195
pages/mine/comp_address/comp_address.js
Normal 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 })
|
||||
}
|
||||
}
|
||||
})
|
||||
6
pages/mine/comp_address/comp_address.json
Normal file
6
pages/mine/comp_address/comp_address.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"t-icon": "tdesign-miniprogram/icon/icon"
|
||||
}
|
||||
}
|
||||
79
pages/mine/comp_address/comp_address.wxml
Normal file
79
pages/mine/comp_address/comp_address.wxml
Normal 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>
|
||||
237
pages/mine/comp_address/comp_address.wxss
Normal file
237
pages/mine/comp_address/comp_address.wxss
Normal 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;
|
||||
}
|
||||
135
pages/mine/comp_profile/comp_profile.js
Normal file
135
pages/mine/comp_profile/comp_profile.js
Normal 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
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
6
pages/mine/comp_profile/comp_profile.json
Normal file
6
pages/mine/comp_profile/comp_profile.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"t-icon": "tdesign-miniprogram/icon/icon"
|
||||
}
|
||||
}
|
||||
64
pages/mine/comp_profile/comp_profile.wxml
Normal file
64
pages/mine/comp_profile/comp_profile.wxml
Normal 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>
|
||||
158
pages/mine/comp_profile/comp_profile.wxss
Normal file
158
pages/mine/comp_profile/comp_profile.wxss
Normal 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
228
pages/mine/mine.js
Normal 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
11
pages/mine/mine.json
Normal 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
41
pages/mine/mine.wxml
Normal 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
153
pages/mine/mine.wxss
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user