wxapp使用submodule
This commit is contained in:
235
pages/ai/aichat/aichat.js
Normal file
235
pages/ai/aichat/aichat.js
Normal file
@@ -0,0 +1,235 @@
|
||||
const API = require('../../../utils/api.js')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
messages: [],
|
||||
inputValue: '',
|
||||
inputFocus: false,
|
||||
isTyping: false,
|
||||
scrollToMessage: '',
|
||||
quickQuestions: [
|
||||
'怎么加入暖橙团队?',
|
||||
'陪诊服务的流程是什么?',
|
||||
'如何预约陪诊服务?',
|
||||
'陪诊服务收费标准?'
|
||||
]
|
||||
},
|
||||
|
||||
messageIdCounter: 0,
|
||||
|
||||
onLoad() {
|
||||
this.loadChatHistory()
|
||||
this.initSocketListeners()
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
this.removeSocketListeners()
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.setData({ inputFocus: true })
|
||||
},
|
||||
|
||||
initSocketListeners() {
|
||||
const app = getApp()
|
||||
const socket = app.globalData.chatSocket
|
||||
if (!socket) return
|
||||
|
||||
this._onSocketMessage = (data) => {
|
||||
if (data && data.type === 'ai') {
|
||||
this.handleAIReply(data)
|
||||
}
|
||||
}
|
||||
|
||||
this._onSocketError = (err) => {
|
||||
console.error('[AIChat] WebSocket error', err)
|
||||
this.handleAIError('网络连接失败,请检查网络设置')
|
||||
}
|
||||
|
||||
socket.onMessage(this._onSocketMessage)
|
||||
socket.onError(this._onSocketError)
|
||||
},
|
||||
|
||||
removeSocketListeners() {
|
||||
const app = getApp()
|
||||
const socket = app.globalData.chatSocket
|
||||
if (!socket) return
|
||||
|
||||
if (this._onSocketMessage) {
|
||||
socket.onMessage(null)
|
||||
}
|
||||
if (this._onSocketError) {
|
||||
socket.onError(null)
|
||||
}
|
||||
},
|
||||
|
||||
handleAIReply(data) {
|
||||
let messages = null
|
||||
if (this.data.messages.length > 0) {
|
||||
let lastMessage = this.data.messages[this.data.messages.length - 1]
|
||||
if (lastMessage.type === 'ai' && lastMessage._serverId === data.id) {
|
||||
lastMessage.content += data.content || ''
|
||||
messages = this.data.messages
|
||||
}
|
||||
}
|
||||
|
||||
if (!messages) {
|
||||
const aiResponse = {
|
||||
id: `msg_${++this.messageIdCounter}`,
|
||||
_serverId: data.id,
|
||||
type: 'ai',
|
||||
contentType: 'text',
|
||||
content: data.content || '',
|
||||
time: this.formatTime(new Date())
|
||||
}
|
||||
|
||||
if (data.sessionId) {
|
||||
wx.setStorageSync('ai_session_id', data.sessionId)
|
||||
}
|
||||
|
||||
messages = [...this.data.messages, aiResponse]
|
||||
}
|
||||
|
||||
this.setData({
|
||||
messages: messages,
|
||||
isTyping: false
|
||||
})
|
||||
this.saveChatHistory(messages)
|
||||
this.scrollToBottom()
|
||||
},
|
||||
|
||||
loadChatHistory() {
|
||||
try {
|
||||
const history = wx.getStorageSync('ai_chat_history')
|
||||
if (history && history.length > 0) {
|
||||
this.setData({ messages: history })
|
||||
this.scrollToBottom()
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('加载聊天记录失败', e)
|
||||
}
|
||||
},
|
||||
|
||||
saveChatHistory(messages) {
|
||||
try {
|
||||
wx.setStorageSync('ai_chat_history', messages)
|
||||
} catch (e) {
|
||||
console.log('保存聊天记录失败', e)
|
||||
}
|
||||
},
|
||||
|
||||
onInputChange(e) {
|
||||
this.setData({ inputValue: e.detail.value })
|
||||
},
|
||||
|
||||
onQuickQuestionTap(e) {
|
||||
const question = e.currentTarget.dataset.question
|
||||
this.setData({ inputValue: question })
|
||||
this.sendMessage()
|
||||
},
|
||||
|
||||
sendMessage() {
|
||||
const content = this.data.inputValue.trim()
|
||||
if (!content) return
|
||||
|
||||
const userMessage = {
|
||||
id: `msg_${++this.messageIdCounter}`,
|
||||
type: 'user',
|
||||
contentType: 'text',
|
||||
content: content,
|
||||
time: this.formatTime(new Date())
|
||||
}
|
||||
|
||||
const messages = [...this.data.messages, userMessage]
|
||||
this.setData({
|
||||
messages: messages,
|
||||
inputValue: '',
|
||||
isTyping: true
|
||||
})
|
||||
|
||||
this.saveChatHistory(messages)
|
||||
this.scrollToBottom()
|
||||
|
||||
this.sendToAI(content)
|
||||
},
|
||||
|
||||
sendToAI(content, type = 'chat') {
|
||||
const app = getApp()
|
||||
const user = app.globalData.user
|
||||
const socket = app.globalData.chatSocket
|
||||
|
||||
if (!socket.isConnected) {
|
||||
socket.connect()
|
||||
}
|
||||
|
||||
if (socket) {
|
||||
socket.send({
|
||||
type: type,
|
||||
content: content,
|
||||
userId: user ? user._id : '',
|
||||
appId: app.globalData.appId
|
||||
})
|
||||
} else {
|
||||
// 处理连接失败的情况
|
||||
this.handleAIError('网络连接失败,请检查网络设置')
|
||||
}
|
||||
},
|
||||
|
||||
handleAIError(errorMessage) {
|
||||
const errorResponse = {
|
||||
id: `msg_${++this.messageIdCounter}`,
|
||||
type: 'ai',
|
||||
contentType: 'text',
|
||||
content: errorMessage,
|
||||
time: this.formatTime(new Date())
|
||||
}
|
||||
|
||||
const messages = [...this.data.messages, errorResponse]
|
||||
this.setData({
|
||||
messages: messages,
|
||||
isTyping: false
|
||||
})
|
||||
this.saveChatHistory(messages)
|
||||
this.scrollToBottom()
|
||||
},
|
||||
|
||||
scrollToBottom() {
|
||||
setTimeout(() => {
|
||||
const lastMessage = this.data.messages[this.data.messages.length - 1]
|
||||
if (lastMessage) {
|
||||
this.setData({
|
||||
scrollToMessage: `msg-${lastMessage.id}`
|
||||
})
|
||||
}
|
||||
}, 100)
|
||||
},
|
||||
|
||||
previewImage(e) {
|
||||
const src = e.currentTarget.dataset.src
|
||||
wx.previewImage({
|
||||
urls: [src],
|
||||
current: src
|
||||
})
|
||||
},
|
||||
|
||||
formatTime(date) {
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
return `${hours}:${minutes}`
|
||||
},
|
||||
|
||||
clearChat() {
|
||||
wx.showModal({
|
||||
title: '确认清空',
|
||||
content: '确定要清空所有聊天记录吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.sendToAI('clear', 'clear')
|
||||
this.setData({ messages: [] })
|
||||
wx.removeStorageSync('ai_chat_history')
|
||||
wx.removeStorageSync('ai_session_id')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
6
pages/ai/aichat/aichat.json
Normal file
6
pages/ai/aichat/aichat.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"navigationBarTitleText": "AI陪诊助手",
|
||||
"usingComponents": {
|
||||
"t-icon": "tdesign-miniprogram/icon/icon"
|
||||
}
|
||||
}
|
||||
123
pages/ai/aichat/aichat.wxml
Normal file
123
pages/ai/aichat/aichat.wxml
Normal file
@@ -0,0 +1,123 @@
|
||||
<view class="chat-container">
|
||||
<!-- 消息列表区域 -->
|
||||
<scroll-view
|
||||
class="message-list {{messages.length > 0 ? 'has-messages' : ''}}"
|
||||
scroll-y
|
||||
scroll-into-view="{{scrollToMessage}}"
|
||||
enhanced
|
||||
show-scrollbar
|
||||
>
|
||||
<!-- 欢迎消息 -->
|
||||
<view class="welcome-area" wx:if="{{messages.length === 0}}">
|
||||
<view class="welcome-icon">
|
||||
<t-icon name="chat" size="80rpx" color="#FF9B33" />
|
||||
</view>
|
||||
<text class="welcome-title">AI陪诊助手</text>
|
||||
<text class="welcome-desc">我是您的智能陪诊助手,可以为您解答陪诊相关问题</text>
|
||||
<view class="quick-questions">
|
||||
<view
|
||||
class="quick-item"
|
||||
wx:for="{{quickQuestions}}"
|
||||
wx:key="index"
|
||||
bindtap="onQuickQuestionTap"
|
||||
data-question="{{item}}"
|
||||
>
|
||||
<text>{{item}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 消息气泡 -->
|
||||
<view
|
||||
class="message-item {{item.type}}"
|
||||
wx:for="{{messages}}"
|
||||
wx:key="id"
|
||||
id="msg-{{item.id}}"
|
||||
>
|
||||
<!-- AI消息:头像在左,内容在右 -->
|
||||
<block wx:if="{{item.type === 'ai'}}">
|
||||
<image class="avatar ai-avatar" src="/images/home-w.png" mode="aspectFill" />
|
||||
<view class="message-content">
|
||||
<view class="message-bubble">
|
||||
<text class="message-text" wx:if="{{item.contentType === 'text'}}">{{item.content}}</text>
|
||||
<image
|
||||
wx:if="{{item.contentType === 'image'}}"
|
||||
class="message-image"
|
||||
src="{{item.content}}"
|
||||
mode="widthFix"
|
||||
bindtap="previewImage"
|
||||
data-src="{{item.content}}"
|
||||
/>
|
||||
</view>
|
||||
<text class="message-time">{{item.time}}</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- 用户消息:内容在左,头像在右 -->
|
||||
<block wx:if="{{item.type === 'user'}}">
|
||||
<view class="message-content">
|
||||
<view class="message-bubble">
|
||||
<text class="message-text" wx:if="{{item.contentType === 'text'}}">{{item.content}}</text>
|
||||
<image
|
||||
wx:if="{{item.contentType === 'image'}}"
|
||||
class="message-image"
|
||||
src="{{item.content}}"
|
||||
mode="widthFix"
|
||||
bindtap="previewImage"
|
||||
data-src="{{item.content}}"
|
||||
/>
|
||||
</view>
|
||||
<text class="message-time">{{item.time}}</text>
|
||||
</view>
|
||||
<view class="avatar user-avatar">
|
||||
<t-icon name="user-1" size="40rpx" color="#FFFFFF" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- AI正在输入提示 -->
|
||||
<view class="message-item ai" wx:if="{{isTyping}}">
|
||||
<image class="avatar ai-avatar" src="/images/home-active2.png" mode="aspectFill" />
|
||||
<view class="message-content">
|
||||
<view class="message-bubble typing-bubble">
|
||||
<view class="typing-indicator">
|
||||
<view class="dot"></view>
|
||||
<view class="dot"></view>
|
||||
<view class="dot"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部留白,确保最新消息可见 -->
|
||||
<view style="height: 40rpx;"></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 清空按钮 -->
|
||||
<view class="clear-btn" wx:if="{{messages.length > 0}}" bindtap="clearChat">
|
||||
<t-icon name="delete" size="28rpx" color="#999999" />
|
||||
<text class="clear-text">清空聊天</text>
|
||||
</view>
|
||||
|
||||
<!-- 输入区域 -->
|
||||
<view class="input-area">
|
||||
<view class="input-wrapper">
|
||||
<input
|
||||
class="message-input"
|
||||
type="text"
|
||||
value="{{inputValue}}"
|
||||
placeholder="请输入您的问题..."
|
||||
placeholder-class="input-placeholder"
|
||||
confirm-type="send"
|
||||
bindinput="onInputChange"
|
||||
bindconfirm="sendMessage"
|
||||
focus="{{inputFocus}}"
|
||||
/>
|
||||
<view class="input-actions">
|
||||
<view class="action-btn send-btn {{inputValue ? 'active' : ''}}" bindtap="sendMessage">
|
||||
<t-icon name="send" size="40rpx" color="{{inputValue ? '#FFFFFF' : '#CCCCCC'}}" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
278
pages/ai/aichat/aichat.wxss
Normal file
278
pages/ai/aichat/aichat.wxss
Normal file
@@ -0,0 +1,278 @@
|
||||
.chat-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: #F5F5F5;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 清空按钮 */
|
||||
.clear-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 6rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
background-color: #FFFFFF;
|
||||
border-top: 1rpx solid #EEEEEE;
|
||||
}
|
||||
|
||||
.clear-btn:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.clear-text {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
/* 消息列表区域 */
|
||||
.message-list {
|
||||
flex: 1;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.message-list.has-messages {
|
||||
padding-top: 20rpx;
|
||||
}
|
||||
|
||||
/* 欢迎区域 */
|
||||
.welcome-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 80rpx 40rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.welcome-icon {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #FF9B33 0%, #FF7A33 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 40rpx;
|
||||
box-shadow: 0 8rpx 32rpx rgba(255, 155, 51, 0.3);
|
||||
}
|
||||
|
||||
.welcome-title {
|
||||
font-size: 40rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.welcome-desc {
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
margin-bottom: 60rpx;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.quick-questions {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.quick-item {
|
||||
background-color: #FFFFFF;
|
||||
padding: 28rpx 32rpx;
|
||||
border-radius: 16rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
text-align: left;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.quick-item:active {
|
||||
transform: scale(0.98);
|
||||
background-color: #FFF8F0;
|
||||
}
|
||||
|
||||
/* 消息项 */
|
||||
.message-item {
|
||||
display: flex;
|
||||
padding: 16rpx 24rpx;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.message-item.ai {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.message-item.user {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* 头像 */
|
||||
.avatar {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 40%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ai-avatar {
|
||||
background: linear-gradient(135deg, #FF9B33 0%, #FF7A33 100%);
|
||||
margin-right: 16rpx;
|
||||
padding: 16rpx;
|
||||
width: 46rpx;
|
||||
height: 46rpx;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
background: linear-gradient(135deg, #4CAF50 0%, #45A049 100%);
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
/* 消息内容 */
|
||||
.message-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 70%;
|
||||
}
|
||||
|
||||
.message-item.user .message-content {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.message-item.ai .message-content {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
/* 消息气泡 */
|
||||
.message-bubble {
|
||||
padding: 20rpx 24rpx;
|
||||
border-radius: 16rpx;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.message-item.ai .message-bubble {
|
||||
background-color: #FFFFFF;
|
||||
border-top-left-radius: 4rpx;
|
||||
}
|
||||
|
||||
.message-item.user .message-bubble {
|
||||
background: linear-gradient(135deg, #FF9B33 0%, #FF7A33 100%);
|
||||
border-top-right-radius: 4rpx;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
font-size: 28rpx;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.message-item.ai .message-text {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.message-item.user .message-text {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.message-image {
|
||||
max-width: 400rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
font-size: 20rpx;
|
||||
color: #999999;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
/* 正在输入指示器 */
|
||||
.typing-bubble {
|
||||
padding: 24rpx 32rpx;
|
||||
}
|
||||
|
||||
.typing-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #CCCCCC;
|
||||
animation: typing 1.4s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.dot:nth-child(1) {
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
.dot:nth-child(2) {
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
|
||||
@keyframes typing {
|
||||
0%, 80%, 100% {
|
||||
transform: scale(0.6);
|
||||
opacity: 0.5;
|
||||
}
|
||||
40% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 输入区域 */
|
||||
.input-area {
|
||||
background-color: #FFFFFF;
|
||||
padding: 20rpx 24rpx;
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
border-top: 1rpx solid #EEEEEE;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.message-input {
|
||||
flex: 1;
|
||||
height: 72rpx;
|
||||
background-color: #F5F5F5;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 28rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.input-placeholder {
|
||||
color: #AAAAAA;
|
||||
}
|
||||
|
||||
.input-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #F5F5F5;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.send-btn.active {
|
||||
background: linear-gradient(135deg, #FF9B33 0%, #FF7A33 100%);
|
||||
}
|
||||
151
pages/escort_record_list/escort_record_list.js
Normal file
151
pages/escort_record_list/escort_record_list.js
Normal file
@@ -0,0 +1,151 @@
|
||||
const API = require('../../utils/api.js')
|
||||
|
||||
const STATUS_MAP = {
|
||||
pending: { text: '待确认', color: '#F59E0B', bg: '#FEF3C7' },
|
||||
confirmed: { text: '已确认', color: '#3B82F6', bg: '#DBEAFE' },
|
||||
in_progress: { text: '进行中', color: '#8B5CF6', bg: '#EDE9FE' },
|
||||
completed: { text: '已完成', color: '#10B981', bg: '#D1FAE5' },
|
||||
cancelled: { text: '已取消', color: '#6B7280', bg: '#F3F4F6' }
|
||||
}
|
||||
|
||||
const TYPE_MAP = {
|
||||
outpatient: '门诊陪诊',
|
||||
inpatient: '住院陪诊',
|
||||
examination: '检查陪诊',
|
||||
surgery: '手术陪诊',
|
||||
medication: '取药陪诊',
|
||||
other: '其他'
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
records: [],
|
||||
loading: false,
|
||||
hasMore: true,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
statusFilter: '',
|
||||
statusTabs: [
|
||||
{ value: '', label: '全部' },
|
||||
{ value: 'pending', label: '待确认' },
|
||||
{ value: 'confirmed', label: '已确认' },
|
||||
{ value: 'in_progress', label: '进行中' },
|
||||
{ value: 'completed', label: '已完成' }
|
||||
]
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.loadRecords(true)
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.loadRecords(true)
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
this.loadRecords(true).finally(() => {
|
||||
wx.stopPullDownRefresh()
|
||||
})
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
if (this.data.hasMore && !this.data.loading) {
|
||||
this.loadRecords(false)
|
||||
}
|
||||
},
|
||||
|
||||
loadRecords(reset = false) {
|
||||
const app = getApp()
|
||||
const user = app.globalData.user
|
||||
|
||||
if (!user || !user._id) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
const page = reset ? 1 : this.data.page + 1
|
||||
this.setData({ loading: true })
|
||||
|
||||
const params = {
|
||||
userId: user._id,
|
||||
page,
|
||||
pageSize: this.data.pageSize
|
||||
}
|
||||
if (this.data.statusFilter) {
|
||||
params.status = this.data.statusFilter
|
||||
}
|
||||
|
||||
return API.escort.getMyRecords(params)
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
const list = res.data.records || []
|
||||
const formatted = list.map(item => this.formatRecord(item))
|
||||
this.setData({
|
||||
records: reset ? formatted : this.data.records.concat(formatted),
|
||||
page,
|
||||
hasMore: list.length >= this.data.pageSize
|
||||
})
|
||||
} else {
|
||||
wx.showToast({ title: res.msg || '加载失败', icon: 'none' })
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
wx.showToast({ title: '网络请求失败', icon: 'none' })
|
||||
})
|
||||
.finally(() => {
|
||||
this.setData({ loading: false })
|
||||
})
|
||||
},
|
||||
|
||||
formatRecord(item) {
|
||||
const statusInfo = STATUS_MAP[item.escort?.status] || STATUS_MAP.pending
|
||||
const typeText = TYPE_MAP[item.escort?.type] || '陪诊服务'
|
||||
const fee = item.payment?.totalFee || 0
|
||||
const appointmentTime = item.schedule?.date
|
||||
? this.formatDateTime(item.schedule.date) + item.schedule.startTime
|
||||
: '--'
|
||||
|
||||
return {
|
||||
_id: item._id,
|
||||
patientName: item.patient?.name || '--',
|
||||
patientMobile: item.patient?.mobile || '--',
|
||||
hospital: item.hospital?.name || '--',
|
||||
department: item.hospital?.department || '--',
|
||||
typeText,
|
||||
status: item.escort?.status,
|
||||
statusText: statusInfo.text,
|
||||
statusColor: statusInfo.color,
|
||||
statusBg: statusInfo.bg,
|
||||
appointmentTime,
|
||||
fee: fee.toFixed(2),
|
||||
escortNote: item.notes?.escortNote || ''
|
||||
}
|
||||
},
|
||||
|
||||
formatDateTime(dateStr) {
|
||||
const d = new Date(dateStr)
|
||||
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} `
|
||||
},
|
||||
|
||||
onTabChange(e) {
|
||||
const status = e.currentTarget.dataset.value
|
||||
this.setData({ statusFilter: status })
|
||||
this.loadRecords(true)
|
||||
},
|
||||
|
||||
onTapRecord(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
wx.navigateTo({
|
||||
url: `/pages/mine/escort_record_detail/escort_record_detail?id=${id}`
|
||||
})
|
||||
},
|
||||
|
||||
makePhoneCall() {
|
||||
wx.makePhoneCall({
|
||||
phoneNumber: '18618162956'
|
||||
})
|
||||
}
|
||||
})
|
||||
6
pages/escort_record_list/escort_record_list.json
Normal file
6
pages/escort_record_list/escort_record_list.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"navigationBarTitleText": "陪诊记录",
|
||||
"usingComponents": {},
|
||||
"enablePullDownRefresh": true,
|
||||
"backgroundTextStyle": "dark"
|
||||
}
|
||||
80
pages/escort_record_list/escort_record_list.wxml
Normal file
80
pages/escort_record_list/escort_record_list.wxml
Normal file
@@ -0,0 +1,80 @@
|
||||
<view class="container">
|
||||
<!-- 状态筛选标签 -->
|
||||
<view class="status-tabs">
|
||||
<view
|
||||
class="tab-item {{statusFilter === item.value ? 'active' : ''}}"
|
||||
wx:for="{{statusTabs}}"
|
||||
wx:key="value"
|
||||
data-value="{{item.value}}"
|
||||
bindtap="onTabChange"
|
||||
>
|
||||
{{item.label}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 记录列表 -->
|
||||
<view class="record-list" wx:if="{{records.length > 0}}">
|
||||
<view
|
||||
class="record-card"
|
||||
wx:for="{{records}}"
|
||||
wx:key="_id"
|
||||
data-id="{{item._id}}"
|
||||
bindtap="onTapRecord"
|
||||
>
|
||||
<!-- 卡片头部 -->
|
||||
<view class="card-header">
|
||||
<view class="header-left">
|
||||
<text class="record-type">{{item.typeText}}</text>
|
||||
<text class="record-time">{{item.appointmentTime}}</text>
|
||||
</view>
|
||||
<view class="status-badge" style="color: {{item.statusColor}}; background: {{item.statusBg}};">
|
||||
{{item.statusText}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 卡片内容 -->
|
||||
<view class="card-body">
|
||||
<view class="info-row">
|
||||
<text class="info-label">就诊医院</text>
|
||||
<text class="info-value">{{item.hospital}}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">就诊科室</text>
|
||||
<text class="info-value">{{item.department}}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">就诊人</text>
|
||||
<text class="info-value">{{item.patientName}} {{item.patientMobile}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 卡片底部 -->
|
||||
<view class="card-footer">
|
||||
<view class="fee-wrap">
|
||||
<text class="fee-label">费用:</text>
|
||||
<text class="fee-value">¥{{item.fee}}</text>
|
||||
</view>
|
||||
<view class="arrow">
|
||||
<t-icon name="chevron-right" size="32rpx" color="#9CA3AF" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty-state" wx:if="{{!loading && records.length === 0}}">
|
||||
<t-icon name="file-unknown" size="120rpx" color="#D1D5DB" />
|
||||
<text class="empty-text">暂无陪诊记录</text>
|
||||
<text class="empty-subtext">您还没有预约陪诊服务</text>
|
||||
</view>
|
||||
|
||||
<!-- 加载中 -->
|
||||
<view class="loading-wrap" wx:if="{{loading}}">
|
||||
<t-loading theme="circular" size="40rpx" text="加载中..." />
|
||||
</view>
|
||||
|
||||
<!-- 到底提示 -->
|
||||
<view class="no-more" wx:if="{{!loading && !hasMore && records.length > 0}}">
|
||||
<text>—— 已经到底了 ——</text>
|
||||
</view>
|
||||
</view>
|
||||
183
pages/escort_record_list/escort_record_list.wxss
Normal file
183
pages/escort_record_list/escort_record_list.wxss
Normal file
@@ -0,0 +1,183 @@
|
||||
page {
|
||||
background-color: #FAF6F1;
|
||||
}
|
||||
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
/* 状态筛选标签 */
|
||||
.status-tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
padding: 24rpx 32rpx;
|
||||
background: #FFFFFF;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 12rpx 28rpx;
|
||||
font-size: 26rpx;
|
||||
color: #6B7280;
|
||||
background: #F3F4F6;
|
||||
border-radius: 32rpx;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.tab-item.active {
|
||||
color: #FFFFFF;
|
||||
background: linear-gradient(135deg, #FF9B33 0%, #FF8500 100%);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 记录列表 */
|
||||
.record-list {
|
||||
padding: 20rpx 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.record-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 20rpx;
|
||||
padding: 28rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
/* 卡片头部 */
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20rpx;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 1rpx solid #F3F4F6;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.record-type {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #1F2937;
|
||||
}
|
||||
|
||||
.record-time {
|
||||
font-size: 24rpx;
|
||||
color: #9CA3AF;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
/* 卡片内容 */
|
||||
.card-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 26rpx;
|
||||
color: #9CA3AF;
|
||||
width: 140rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 26rpx;
|
||||
color: #374151;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 卡片底部 */
|
||||
.card-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-top: 20rpx;
|
||||
border-top: 1rpx solid #F3F4F6;
|
||||
}
|
||||
|
||||
.fee-wrap {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.fee-label {
|
||||
font-size: 26rpx;
|
||||
color: #6B7280;
|
||||
}
|
||||
|
||||
.fee-value {
|
||||
font-size: 32rpx;
|
||||
color: #FF9B33;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-top: 200rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 30rpx;
|
||||
color: #9CA3AF;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.empty-subtext {
|
||||
font-size: 26rpx;
|
||||
color: #D1D5DB;
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
|
||||
/* 加载中 */
|
||||
.loading-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40rpx;
|
||||
}
|
||||
|
||||
/* 到底提示 */
|
||||
.no-more {
|
||||
text-align: center;
|
||||
padding: 32rpx;
|
||||
font-size: 24rpx;
|
||||
color: #D1D5DB;
|
||||
}
|
||||
66
pages/healthrecord/main/healthrecord.js
Normal file
66
pages/healthrecord/main/healthrecord.js
Normal file
@@ -0,0 +1,66 @@
|
||||
// pages/healthrecord/healthrecord.js
|
||||
Page({
|
||||
|
||||
/**
|
||||
* 页面的初始数据
|
||||
*/
|
||||
data: {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad(options) {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面初次渲染完成
|
||||
*/
|
||||
onReady() {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow() {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面隐藏
|
||||
*/
|
||||
onHide() {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面卸载
|
||||
*/
|
||||
onUnload() {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面相关事件处理函数--监听用户下拉动作
|
||||
*/
|
||||
onPullDownRefresh() {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面上拉触底事件的处理函数
|
||||
*/
|
||||
onReachBottom() {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户点击右上角分享
|
||||
*/
|
||||
onShareAppMessage() {
|
||||
|
||||
}
|
||||
})
|
||||
3
pages/healthrecord/main/healthrecord.json
Normal file
3
pages/healthrecord/main/healthrecord.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"usingComponents": {}
|
||||
}
|
||||
2
pages/healthrecord/main/healthrecord.wxml
Normal file
2
pages/healthrecord/main/healthrecord.wxml
Normal file
@@ -0,0 +1,2 @@
|
||||
<!--pages/healthrecord/healthrecord.wxml-->
|
||||
<text>pages/healthrecord/healthrecord.wxml</text>
|
||||
1
pages/healthrecord/main/healthrecord.wxss
Normal file
1
pages/healthrecord/main/healthrecord.wxss
Normal file
@@ -0,0 +1 @@
|
||||
/* pages/healthrecord/healthrecord.wxss */
|
||||
43
pages/index/index.js
Normal file
43
pages/index/index.js
Normal file
@@ -0,0 +1,43 @@
|
||||
Page({
|
||||
data: {
|
||||
contact: {
|
||||
icon: 'logo-wechat-stroke',
|
||||
phone: '18618162956'
|
||||
},
|
||||
services: {},
|
||||
},
|
||||
|
||||
async onLoad() {
|
||||
const app = getApp()
|
||||
const services = await app.globalData.servicesReady
|
||||
this.setData({
|
||||
services: services.sort((a, b) => parseFloat(a.price) - parseFloat(b.price))
|
||||
});
|
||||
},
|
||||
|
||||
makePhoneCall() {
|
||||
const phoneNumber = this.data.contact.phone;
|
||||
wx.makePhoneCall({
|
||||
phoneNumber: phoneNumber,
|
||||
success: () => {
|
||||
console.log('拨打电话成功');
|
||||
},
|
||||
fail: () => {
|
||||
console.log('拨打电话失败');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
goToDetail(e) {
|
||||
const id = e.currentTarget.dataset.id;
|
||||
wx.navigateTo({
|
||||
url: `/pages/itemdetail/itemdetail?id=${id}`
|
||||
});
|
||||
},
|
||||
|
||||
goToAIChat() {
|
||||
wx.switchTab({
|
||||
url: '/pages/ai/aichat/aichat'
|
||||
});
|
||||
}
|
||||
});
|
||||
6
pages/index/index.json
Normal file
6
pages/index/index.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"navigationBarTitleText": "暖橙陪诊",
|
||||
"usingComponents": {
|
||||
"t-icon": "tdesign-miniprogram/icon/icon"
|
||||
}
|
||||
}
|
||||
73
pages/index/index.wxml
Normal file
73
pages/index/index.wxml
Normal file
@@ -0,0 +1,73 @@
|
||||
<view class="container">
|
||||
<view class="header-section">
|
||||
<image class="header-bg" src="/images/background.jpg" mode="aspectFill"></image>
|
||||
<view class="user-info">
|
||||
<image class="avatar" src="/images/home-active2.png" mode="aspectFill"></image>
|
||||
<view class="welcome-text">
|
||||
<text class="user-name">专业陪诊</text>
|
||||
<text class="welcome-subtitle">让就医更简单,让客户更安心</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="services-section">
|
||||
<view class="service-item">
|
||||
<view class="service-icon-wrap blue">
|
||||
<image class="service-icon" src="/images/home-w.png" mode="aspectFill"></image>
|
||||
</view>
|
||||
<text class="service-name">陪诊</text>
|
||||
</view>
|
||||
<view class="service-item" bindtap="goToAIChat">
|
||||
<view class="service-icon-wrap blue">
|
||||
<image class="service-icon" src="/images/zixun-w.png" mode="aspectFill"></image>
|
||||
</view>
|
||||
<text class="service-name">AI客服</text>
|
||||
</view>
|
||||
<view class="service-item" bindtap="goToAIChat">
|
||||
<view class="service-icon-wrap blue">
|
||||
<image class="service-icon" src="/images/team-w.png" mode="aspectFill"></image>
|
||||
</view>
|
||||
<text class="service-name">商务合作</text>
|
||||
</view>
|
||||
|
||||
<view class="service-item" bindtap="makePhoneCall">
|
||||
<view class="service-icon-wrap blue">
|
||||
<image class="service-icon" src="/images/call-w.png" mode="aspectFill"></image>
|
||||
</view>
|
||||
<text class="service-name">电话联系</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="articles-section">
|
||||
<view class="section-header">
|
||||
<view class="section-title-wrap">
|
||||
<t-icon name="view-list" class="section-icon" />
|
||||
<text class="section-title">陪诊项目</text>
|
||||
</view>
|
||||
<text class="section-more"></text>
|
||||
</view>
|
||||
|
||||
<view class="article-list">
|
||||
<view class="article-item" wx:for="{{ services }}" wx:key="id" bindtap="goToDetail" data-id="{{ item.id }}">
|
||||
<image class="article-image" src="{{ item.image }}" mode="aspectFill"></image>
|
||||
<view class="article-content">
|
||||
<text class="article-title">{{ item.title }}</text>
|
||||
<view class="article-meta">
|
||||
<text class="article-date">{{ item.subtitle }}</text>
|
||||
<text class="article-time">{{ item.readTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="article-price">¥{{ item.price }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="contact-section">
|
||||
<view class="contact-box">
|
||||
<t-icon name="{{ contact.icon }}" class="contact-icon" />
|
||||
<text class="contact-phone">18618162956</text>
|
||||
<button class="contact-btn" bindtap="makePhoneCall">打电话 </button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
268
pages/index/index.wxss
Normal file
268
pages/index/index.wxss
Normal file
@@ -0,0 +1,268 @@
|
||||
page {
|
||||
background-color: #FAF6F1;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
}
|
||||
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.header-section {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 360rpx;
|
||||
overflow: hidden;
|
||||
padding-top: 10rpx;
|
||||
}
|
||||
|
||||
.header-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.header-blur-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.doctor-image {
|
||||
position: absolute;
|
||||
right: -60rpx;
|
||||
bottom: 0;
|
||||
width: 380rpx;
|
||||
height: 420rpx;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
padding: 100rpx 40rpx 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.welcome-text {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
|
||||
.welcome-title {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #FFFFFF;
|
||||
opacity: 0.9;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
display: block;
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
color: #FFFFFF;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.welcome-subtitle {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
color: #FFFFFF;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.contact-section {
|
||||
padding: 0 32rpx;
|
||||
margin-top: 40rpx;
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.contact-box {
|
||||
background: #FFFFFF;
|
||||
border-radius: 10rpx;
|
||||
padding: 34rpx 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.contact-icon {
|
||||
font-size: 38rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.contact-text {
|
||||
font-size: 30rpx;
|
||||
color: #9CA3AF;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.contact-phone {
|
||||
font-size: 28rpx;
|
||||
color: #1F2937;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.contact-btn {
|
||||
background: linear-gradient(135deg, #FF9B33 0%, #FF8500 100%);
|
||||
color: #FFFFFF;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
padding: 26rpx 6rpx;
|
||||
border-radius: 20rpx;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.services-section {
|
||||
padding: 36rpx 32rpx;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
background: #FFFFFF;
|
||||
margin: 30rpx 0rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.service-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.service-icon-wrap {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.service-icon-wrap.blue {
|
||||
background-color: #FF9B33;
|
||||
}
|
||||
|
||||
.service-icon {
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
color: #FF9B33;
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: 28rpx;
|
||||
color: #374151;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.articles-section {
|
||||
padding: 10rpx 0rpx;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
|
||||
.section-icon {
|
||||
font-size: 36rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.section-title-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.section-more {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1F2937;
|
||||
}
|
||||
|
||||
.section-more {
|
||||
font-size: 26rpx;
|
||||
color: #FF9B33;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.article-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.article-item {
|
||||
background: #FFFFFF;
|
||||
border-radius: 0rpx;
|
||||
padding: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.article-image {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 10rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.article-content {
|
||||
flex: 1;
|
||||
margin-left: 20rpx;
|
||||
padding-right: 16rpx;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.article-title {
|
||||
display: block;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #1F2937;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 22rpx;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.article-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.article-date {
|
||||
font-size: 26rpx;
|
||||
color: #6c6c6c;
|
||||
}
|
||||
|
||||
.article-time {
|
||||
font-size: 22rpx;
|
||||
color: #9CA3AF;
|
||||
}
|
||||
|
||||
.article-price {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #FF9B33;
|
||||
align-self: flex-start;
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
1123
pages/itemdetail/itemdetail.js
Normal file
1123
pages/itemdetail/itemdetail.js
Normal file
File diff suppressed because it is too large
Load Diff
6
pages/itemdetail/itemdetail.json
Normal file
6
pages/itemdetail/itemdetail.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"t-icon": "tdesign-miniprogram/icon/icon"
|
||||
},
|
||||
"navigationBarTitleText": "服务详情"
|
||||
}
|
||||
179
pages/itemdetail/itemdetail.wxml
Normal file
179
pages/itemdetail/itemdetail.wxml
Normal file
@@ -0,0 +1,179 @@
|
||||
<view class="container">
|
||||
<!-- 服务图片 -->
|
||||
<view class="service-image-wrap">
|
||||
<image class="service-image" src="{{service.image}}" mode="aspectFill"></image>
|
||||
</view>
|
||||
|
||||
<!-- 服务基本信息 -->
|
||||
<view class="info-card">
|
||||
<view class="service-header">
|
||||
<view class="service-title-wrap">
|
||||
<text class="service-title">{{service.title}}</text>
|
||||
<view class="service-tag" wx:if="{{service.tag}}">{{service.tag}}</view>
|
||||
</view>
|
||||
<view class="service-price-wrap">
|
||||
<text class="price-symbol">¥</text>
|
||||
<text class="service-price">{{service.price}}</text>
|
||||
<text class="price-unit">/次</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="service-subtitle">{{service.subtitle}}</view>
|
||||
</view>
|
||||
|
||||
<!-- 服务详情 -->
|
||||
<view class="detail-card">
|
||||
<view class="card-title">
|
||||
<t-icon name="info-circle" class="card-icon" size="36rpx" />
|
||||
<text>服务详情</text>
|
||||
</view>
|
||||
<view class="detail-content">
|
||||
<text class="detail-text">{{service.description}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 服务流程 -->
|
||||
<view class="detail-card">
|
||||
<view class="card-title">
|
||||
<t-icon name="list" class="card-icon" size="36rpx" />
|
||||
<text>服务流程</text>
|
||||
</view>
|
||||
<view class="flow-list">
|
||||
<view class="flow-item" wx:for="{{service.flow}}" wx:key="index">
|
||||
<view class="flow-number">{{index + 1}}</view>
|
||||
<view class="flow-content">
|
||||
<text class="flow-title">{{item.title}}</text>
|
||||
<text class="flow-desc">{{item.desc}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 注意事项 -->
|
||||
<view class="detail-card">
|
||||
<view class="card-title">
|
||||
<t-icon name="error-circle" class="card-icon" size="36rpx" />
|
||||
<text>注意事项</text>
|
||||
</view>
|
||||
<view class="notice-list">
|
||||
<view class="notice-item" wx:for="{{service.notices}}" wx:key="index">
|
||||
<view class="notice-dot"></view>
|
||||
<text class="notice-text">{{item}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部占位,避免被下单栏遮挡 -->
|
||||
<view class="bottom-placeholder"></view>
|
||||
|
||||
<!-- 底部下单栏 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="price-info">
|
||||
<text class="total-label">合计:</text>
|
||||
<text class="total-price">¥{{totalPrice}}</text>
|
||||
</view>
|
||||
<button class="order-btn" bindtap="openOrderPopup">立即下单</button>
|
||||
</view>
|
||||
|
||||
<!-- 下单弹窗遮罩 -->
|
||||
<view class="popup-mask {{showOrderPopup ? 'show' : ''}}" bindtap="closeOrderPopup"></view>
|
||||
|
||||
<!-- 下单弹窗 -->
|
||||
<view class="popup-content {{showOrderPopup ? 'show' : ''}}">
|
||||
<view class="popup-header">
|
||||
<text class="popup-title">确认订单</text>
|
||||
<view class="popup-close" bindtap="closeOrderPopup">
|
||||
<t-icon name="close" size="40rpx" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view class="popup-body" scroll-y>
|
||||
<!-- 服务信息 -->
|
||||
<view class="order-service-info">
|
||||
<image class="order-service-image" src="{{service.image}}" mode="aspectFill"></image>
|
||||
<view class="order-service-text">
|
||||
<text class="order-service-title">{{service.title}}</text>
|
||||
<text class="order-service-price">¥{{service.price}}/次</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 就诊人姓名 -->
|
||||
<view class="order-row">
|
||||
<text class="order-label">就诊人姓名</text>
|
||||
<input class="order-input" value="{{patientName}}" placeholder="请输入就诊人姓名" placeholder-class="input-placeholder" bindinput="onPatientNameChange" />
|
||||
</view>
|
||||
|
||||
<!-- 就诊人电话 -->
|
||||
<view class="order-row">
|
||||
<text class="order-label">联系电话</text>
|
||||
<input class="order-input" value="{{patientPhone}}" placeholder="请输入联系电话" placeholder-class="input-placeholder" type="number" bindinput="onPatientPhoneChange" />
|
||||
</view>
|
||||
|
||||
<!-- 就诊省份 -->
|
||||
<view class="order-row">
|
||||
<text class="order-label">就诊省份</text>
|
||||
<picker mode="selector" range="{{provinces}}" value="{{provinceIndex}}" bindchange="onProvinceChange">
|
||||
<view class="picker-value {{province ? '' : 'placeholder'}}">
|
||||
{{province || '请选择省份'}}
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<!-- 就诊医院 -->
|
||||
<view class="order-row">
|
||||
<text class="order-label">就诊医院</text>
|
||||
<input class="order-input" value="{{hospital}}" placeholder="请输入就诊医院" placeholder-class="input-placeholder" bindinput="onHospitalChange" />
|
||||
</view>
|
||||
|
||||
<!-- 就诊科室 -->
|
||||
<view class="order-row">
|
||||
<text class="order-label">就诊科室</text>
|
||||
<input class="order-input" value="{{department}}" placeholder="请输入就诊科室" placeholder-class="input-placeholder" bindinput="onDepartmentChange" />
|
||||
</view>
|
||||
|
||||
<!-- 陪诊师性别要求 -->
|
||||
<view class="order-row">
|
||||
<text class="order-label">陪诊师性别</text>
|
||||
<picker mode="selector" range="{{genderOptions}}" value="{{genderIndex}}" bindchange="onGenderChange">
|
||||
<view class="picker-value {{gender ? '' : 'placeholder'}}">
|
||||
{{gender || '请选择性别要求'}}
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<!-- 预约日期 -->
|
||||
<view class="order-row">
|
||||
<text class="order-label">预约日期</text>
|
||||
<picker mode="date" value="{{appointmentDate}}" start="{{today}}" bindchange="onDateChange">
|
||||
<view class="picker-value {{appointmentDate ? '' : 'placeholder'}}">
|
||||
{{appointmentDate || '请选择预约日期'}}
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<!-- 预约时间 -->
|
||||
<view class="order-row">
|
||||
<text class="order-label">预约时间</text>
|
||||
<picker mode="time" value="{{appointmentTime}}" start="08:00" end="18:00" bindchange="onTimeChange">
|
||||
<view class="picker-value {{appointmentTime ? '' : 'placeholder'}}">
|
||||
{{appointmentTime || '请选择预约时间'}}
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<!-- 备注 -->
|
||||
<view class="order-row column">
|
||||
<text class="order-label">备注说明</text>
|
||||
<textarea class="order-textarea" value="{{remark}}" placeholder="请输入其他需求或备注信息" maxlength="200" placeholder-class="input-placeholder" bindinput="onRemarkChange" />
|
||||
<text class="textarea-count">{{remark.length}}/200</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="popup-footer">
|
||||
<view class="popup-price">
|
||||
<text class="popup-price-label">合计:</text>
|
||||
<text class="popup-price-value">¥{{totalPrice}}</text>
|
||||
</view>
|
||||
<button class="popup-submit-btn" bindtap="submitOrder">提交订单</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
495
pages/itemdetail/itemdetail.wxss
Normal file
495
pages/itemdetail/itemdetail.wxss
Normal file
@@ -0,0 +1,495 @@
|
||||
page {
|
||||
background-color: #FAF6F1;
|
||||
}
|
||||
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
/* 服务图片 */
|
||||
.service-image-wrap {
|
||||
width: 100%;
|
||||
height: 420rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.service-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 信息卡片 */
|
||||
.info-card {
|
||||
background: #FFFFFF;
|
||||
margin: -40rpx 24rpx 24rpx;
|
||||
border-radius: 20rpx;
|
||||
padding: 32rpx;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.service-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.service-title-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.service-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #1F2937;
|
||||
}
|
||||
|
||||
.service-tag {
|
||||
background: linear-gradient(135deg, #FF9B33 0%, #FF8500 100%);
|
||||
color: #FFFFFF;
|
||||
font-size: 22rpx;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.service-price-wrap {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.price-symbol {
|
||||
font-size: 28rpx;
|
||||
color: #FF9B33;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.service-price {
|
||||
font-size: 44rpx;
|
||||
color: #FF9B33;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.price-unit {
|
||||
font-size: 24rpx;
|
||||
color: #9CA3AF;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
|
||||
.service-subtitle {
|
||||
font-size: 26rpx;
|
||||
color: #6B7280;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* 详情卡片 */
|
||||
.detail-card {
|
||||
background: #FFFFFF;
|
||||
margin: 0 24rpx 24rpx;
|
||||
border-radius: 20rpx;
|
||||
padding: 32rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1F2937;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
color: #FF9B33;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.detail-text {
|
||||
font-size: 28rpx;
|
||||
color: #4B5563;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
/* 服务流程 */
|
||||
.flow-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.flow-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.flow-number {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
background: linear-gradient(135deg, #FF9B33 0%, #FF8500 100%);
|
||||
color: #FFFFFF;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.flow-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.flow-title {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #1F2937;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.flow-desc {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
color: #6B7280;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* 注意事项 */
|
||||
.notice-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.notice-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.notice-dot {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
background: #FF9B33;
|
||||
border-radius: 50%;
|
||||
margin-top: 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.notice-text {
|
||||
font-size: 28rpx;
|
||||
color: #4B5563;
|
||||
line-height: 1.6;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 底部占位 */
|
||||
.bottom-placeholder {
|
||||
height: 140rpx;
|
||||
}
|
||||
|
||||
/* 底部下单栏 */
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #FFFFFF;
|
||||
padding: 24rpx 32rpx;
|
||||
padding-bottom: calc(24rpx + constant(safe-area-inset-bottom));
|
||||
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.06);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.price-info {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.total-label {
|
||||
font-size: 28rpx;
|
||||
color: #6B7280;
|
||||
}
|
||||
|
||||
.total-price {
|
||||
font-size: 40rpx;
|
||||
color: #FF9B33;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.order-btn {
|
||||
background: linear-gradient(135deg, #FF9B33 0%, #FF8500 100%);
|
||||
color: #FFFFFF;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
padding: 24rpx 56rpx;
|
||||
border-radius: 40rpx;
|
||||
border: none;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.order-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* 弹窗遮罩 */
|
||||
.popup-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 200;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s, visibility 0.3s;
|
||||
}
|
||||
|
||||
.popup-mask.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* 弹窗内容 */
|
||||
.popup-content {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #FFFFFF;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
z-index: 201;
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.3s ease-out;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 85vh;
|
||||
}
|
||||
|
||||
.popup-content.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.popup-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 32rpx;
|
||||
position: relative;
|
||||
border-bottom: 1rpx solid #F3F4F6;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.popup-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1F2937;
|
||||
}
|
||||
|
||||
.popup-close {
|
||||
position: absolute;
|
||||
right: 32rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #9CA3AF;
|
||||
}
|
||||
|
||||
.popup-body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 32rpx;
|
||||
max-height: 60vh;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* 弹窗内服务信息 */
|
||||
.order-service-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
padding-bottom: 24rpx;
|
||||
border-bottom: 1rpx solid #F3F4F6;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.order-service-image {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.order-service-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.order-service-title {
|
||||
display: block;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #1F2937;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.order-service-price {
|
||||
display: block;
|
||||
font-size: 32rpx;
|
||||
color: #FF9B33;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 表单行 */
|
||||
.order-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #F3F4F6;
|
||||
}
|
||||
|
||||
.order-row.column {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.order-label {
|
||||
font-size: 28rpx;
|
||||
color: #374151;
|
||||
font-weight: 500;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.order-input {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
font-size: 28rpx;
|
||||
color: #1F2937;
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
|
||||
.input-placeholder {
|
||||
color: #9CA3AF;
|
||||
}
|
||||
|
||||
.picker-value {
|
||||
font-size: 28rpx;
|
||||
color: #1F2937;
|
||||
}
|
||||
|
||||
.picker-value.placeholder {
|
||||
color: #9CA3AF;
|
||||
}
|
||||
|
||||
/* 数量控制 */
|
||||
.quantity-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.quantity-btn {
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
background: #F3F4F6;
|
||||
border-radius: 8rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32rpx;
|
||||
color: #1F2937;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.quantity-btn.disabled {
|
||||
color: #CCCCCC;
|
||||
background: #F9FAFB;
|
||||
}
|
||||
|
||||
.quantity-value {
|
||||
font-size: 30rpx;
|
||||
color: #1F2937;
|
||||
font-weight: 600;
|
||||
min-width: 40rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 文本域 */
|
||||
.order-textarea {
|
||||
width: 100%;
|
||||
height: 160rpx;
|
||||
background: #F9FAFB;
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #1F2937;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.textarea-count {
|
||||
font-size: 24rpx;
|
||||
color: #9CA3AF;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
/* 弹窗底部 */
|
||||
.popup-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 24rpx 32rpx;
|
||||
padding-bottom: calc(24rpx + constant(safe-area-inset-bottom));
|
||||
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||||
border-top: 1rpx solid #F3F4F6;
|
||||
background: #FFFFFF;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.popup-price {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.popup-price-label {
|
||||
font-size: 28rpx;
|
||||
color: #6B7280;
|
||||
}
|
||||
|
||||
.popup-price-value {
|
||||
font-size: 40rpx;
|
||||
color: #FF9B33;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.popup-submit-btn {
|
||||
background: linear-gradient(135deg, #FF9B33 0%, #FF8500 100%);
|
||||
color: #FFFFFF;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
padding: 24rpx 56rpx;
|
||||
border-radius: 40rpx;
|
||||
border: none;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.popup-submit-btn::after {
|
||||
border: none;
|
||||
}
|
||||
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