增量提交
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// 聊天页面逻辑
|
||||
// 聊天页面编辑
|
||||
const { API, WebSocketManager, util, constants } = require('../../utils/api.js')
|
||||
const { formatRelativeTime } = util
|
||||
const { MESSAGE_TYPE, WS_MESSAGE_TYPE } = constants
|
||||
@@ -13,18 +13,157 @@ Page({
|
||||
websocketManager: null,
|
||||
lastMessageId: '',
|
||||
isConnected: false,
|
||||
currentPage: 1
|
||||
currentPage: 1,
|
||||
userInfo: null,
|
||||
showUserInfoCard: false,
|
||||
locationInfo: null,
|
||||
deviceInfo: null,
|
||||
networkType: '',
|
||||
showAuthPrompt: false,
|
||||
authType: 'userInfo'
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.initWebSocket()
|
||||
this._initWebSocket()
|
||||
this.loadChatHistory()
|
||||
this.addWelcomeMessage()
|
||||
this.checkUserAuth()
|
||||
// 通知 tab-bar 进入聊天模式
|
||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
||||
this.getTabBar().setData({
|
||||
currentTab: 1,
|
||||
showChatPanel: true
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 检查用户授权状态
|
||||
checkUserAuth() {
|
||||
const app = getApp()
|
||||
const userInfo = app.globalData.userInfo
|
||||
|
||||
if (userInfo) {
|
||||
this.setData({ userInfo })
|
||||
this.showUserInfoCard()
|
||||
this.getUserAdditionalInfo()
|
||||
} else {
|
||||
this.requestUserAuth()
|
||||
}
|
||||
},
|
||||
|
||||
// 请求用户授权
|
||||
requestUserAuth() {
|
||||
const app = getApp()
|
||||
app.getUserInfo((userInfo) => {
|
||||
if (userInfo) {
|
||||
this.setData({ userInfo })
|
||||
this.showUserInfoCard()
|
||||
this.getUserAdditionalInfo()
|
||||
} else {
|
||||
// 未授权,显示授权提示组件
|
||||
this.setData({
|
||||
showAuthPrompt: true,
|
||||
authType: 'userInfo'
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 获取用户附加信息
|
||||
getUserAdditionalInfo() {
|
||||
const app = getApp()
|
||||
|
||||
// 获取地理位置信息
|
||||
app.getLocation((locationInfo) => {
|
||||
if (locationInfo) {
|
||||
this.setData({ locationInfo })
|
||||
}
|
||||
})
|
||||
|
||||
// 获取设备信息
|
||||
const deviceInfo = app.getDeviceInfo()
|
||||
if (deviceInfo) {
|
||||
this.setData({ deviceInfo })
|
||||
}
|
||||
|
||||
// 获取网络状态
|
||||
app.getNetworkType((networkType) => {
|
||||
if (networkType) {
|
||||
this.setData({ networkType })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 显示用户信息卡片
|
||||
showUserInfoCard() {
|
||||
const { userInfo, locationInfo, deviceInfo, networkType } = this.data
|
||||
|
||||
// 获取UnionID
|
||||
let userId = this.getUnionId()
|
||||
|
||||
// 获取手机号
|
||||
const phoneNumber = wx.getStorageSync('phoneNumber')
|
||||
|
||||
let userInfoContent = `你好,${userInfo.nickName}!\n`
|
||||
userInfoContent += `UnionID:${userId}\n`
|
||||
|
||||
if (phoneNumber) {
|
||||
userInfoContent += `手机号:${phoneNumber}\n`
|
||||
}
|
||||
|
||||
if (userInfo.gender === 1) {
|
||||
userInfoContent += '性别:男\n'
|
||||
} else if (userInfo.gender === 2) {
|
||||
userInfoContent += '性别:女\n'
|
||||
}
|
||||
|
||||
if (userInfo.city) {
|
||||
userInfoContent += `地区:${userInfo.country} ${userInfo.province} ${userInfo.city}\n`
|
||||
}
|
||||
|
||||
if (locationInfo) {
|
||||
userInfoContent += `位置:${locationInfo.latitude.toFixed(2)}, ${locationInfo.longitude.toFixed(2)}\n`
|
||||
}
|
||||
|
||||
if (deviceInfo) {
|
||||
userInfoContent += `设备:${deviceInfo.model}\n`
|
||||
userInfoContent += `系统:${deviceInfo.system}\n`
|
||||
}
|
||||
|
||||
if (networkType) {
|
||||
userInfoContent += `网络:${networkType}`
|
||||
}
|
||||
|
||||
const userInfoMessage = {
|
||||
id: util.generateUniqueId(),
|
||||
content: userInfoContent,
|
||||
type: MESSAGE_TYPE.TEXT,
|
||||
isMe: false,
|
||||
nickname: '系统',
|
||||
avatar: '/assets/images/system-avatar.png',
|
||||
time: formatRelativeTime(new Date())
|
||||
}
|
||||
|
||||
this.addMessage(userInfoMessage)
|
||||
this.setData({ showUserInfoCard: true })
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 更新自定义 TabBar 选中态 & 强制聊天模式
|
||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
||||
this.getTabBar().setData({
|
||||
currentTab: 1,
|
||||
showChatPanel: true
|
||||
})
|
||||
}
|
||||
// 每次显示页面时检查授权状态
|
||||
this.checkUserAuth()
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
if (this.data.websocketManager) {
|
||||
this.data.websocketManager.disconnect()
|
||||
if (this.wsManager) {
|
||||
this.wsManager.disconnect()
|
||||
this.wsManager = null
|
||||
}
|
||||
},
|
||||
|
||||
@@ -32,7 +171,7 @@ Page({
|
||||
addWelcomeMessage() {
|
||||
const welcomeMessage = {
|
||||
id: util.generateUniqueId(),
|
||||
content: '你好!我是智控未来的AI助手,有什么可以帮你的吗?',
|
||||
content: '你好,我是智控未来的AI助手,有什么可以帮助你的吗?',
|
||||
type: MESSAGE_TYPE.TEXT,
|
||||
isMe: false,
|
||||
nickname: '智控未来',
|
||||
@@ -42,25 +181,14 @@ Page({
|
||||
this.addMessage(welcomeMessage)
|
||||
},
|
||||
|
||||
// 初始化WebSocket连接
|
||||
initWebSocket() {
|
||||
const manager = new WebSocketManager()
|
||||
// 初始化 WebSocket 连接(小程序 WebSocket)
|
||||
_initWebSocket() {
|
||||
if (this.wsManager && this.wsManager.isConnected) return
|
||||
const app = getApp()
|
||||
|
||||
manager.onMessage(WS_MESSAGE_TYPE.MESSAGE, (data) => {
|
||||
this.handleNewMessage(data)
|
||||
})
|
||||
|
||||
manager.onMessage(WS_MESSAGE_TYPE.SYSTEM, (data) => {
|
||||
this.handleSystemMessage(data)
|
||||
})
|
||||
|
||||
manager.connect(app.globalData.websocketUrl || 'wss://pactgo.cn/api/v1/ws/control')
|
||||
|
||||
this.setData({
|
||||
websocketManager: manager,
|
||||
isConnected: true
|
||||
})
|
||||
const wsUrl = app.globalData.websocketUrl || 'wss://pactgo.cn/api/v1/ws/miniprogram'
|
||||
this.wsManager = new WebSocketManager()
|
||||
this.wsManager.connect(wsUrl)
|
||||
this.setData({ isConnected: true })
|
||||
},
|
||||
|
||||
// 加载聊天记录
|
||||
@@ -82,7 +210,7 @@ Page({
|
||||
|
||||
// 加载更多历史消息
|
||||
loadMoreHistory() {
|
||||
if (this.data.currentPage < 3) { // 模拟加载3页历史
|
||||
if (this.data.currentPage < 3) {
|
||||
this.setData({ currentPage: this.data.currentPage + 1 })
|
||||
const moreHistory = this.getMockHistoryMessages(this.data.currentPage)
|
||||
if (moreHistory.length > 0) {
|
||||
@@ -98,7 +226,7 @@ Page({
|
||||
if (page === 1) {
|
||||
messages.push({
|
||||
id: 'history-1',
|
||||
content: '你好,我是智控未来的AI助手',
|
||||
content: '你好,我是智控未来的AI助手,有什么可以帮助你的吗?',
|
||||
type: MESSAGE_TYPE.TEXT,
|
||||
isMe: false,
|
||||
nickname: '智控未来',
|
||||
@@ -116,7 +244,7 @@ Page({
|
||||
})
|
||||
messages.push({
|
||||
id: 'history-3',
|
||||
content: '我可以帮你解答问题、提供信息、生成内容等。请问有什么具体需求吗?',
|
||||
content: '我可以帮你解答问题、提供信息、生成内容等。请问你有什么具体需求吗?',
|
||||
type: MESSAGE_TYPE.TEXT,
|
||||
isMe: false,
|
||||
nickname: '智控未来',
|
||||
@@ -135,7 +263,7 @@ Page({
|
||||
})
|
||||
messages.push({
|
||||
id: 'history-5',
|
||||
content: '<p>学习编程的步骤:</p><ol><li>选择一门编程语言,如Python、JavaScript等</li><li>学习基础语法和概念</li><li>实践项目,积累经验</li><li>参与社区,学习他人的代码</li></ol>',
|
||||
content: '<p>学习编程的步骤:</p><ol><li>选择一门编程语言,如Python、JavaScript等</li><li>学习基础语法和概念</li><li>动手项目,积累经验</li><li>参与社区,学习他人的代码</li></ol>',
|
||||
type: MESSAGE_TYPE.richText,
|
||||
isMe: false,
|
||||
nickname: '智控未来',
|
||||
@@ -149,7 +277,7 @@ Page({
|
||||
// 处理新消息
|
||||
handleNewMessage(data) {
|
||||
this.setData({ isTyping: false })
|
||||
|
||||
|
||||
// 模拟不同类型的消息
|
||||
if (data.content.includes('代码')) {
|
||||
const codeMessage = {
|
||||
@@ -181,7 +309,7 @@ Page({
|
||||
} else if (data.content.includes('富文本')) {
|
||||
const richTextMessage = {
|
||||
id: data.id || util.generateUniqueId(),
|
||||
content: '<h3>标题</h3><p>这是一段<b>加粗</b>的文本,包含<ul><li>无序列表项1</li><li>无序列表项2</li></ul></p>',
|
||||
content: '<h3>标题</h3><p>这是一段<b>加粗</b>的文字,包含<ul><li>无序列表项</li><li>无序列表项</li></ul></p>',
|
||||
type: 'richText',
|
||||
isMe: false,
|
||||
nickname: '智控未来',
|
||||
@@ -239,26 +367,20 @@ Page({
|
||||
}, 100)
|
||||
},
|
||||
|
||||
// 输入变化
|
||||
// 输入框变化
|
||||
onInputChange(e) {
|
||||
this.setData({
|
||||
inputValue: e.detail.value
|
||||
})
|
||||
},
|
||||
|
||||
// 发送消息
|
||||
async sendMessage() {
|
||||
const content = this.data.inputValue.trim()
|
||||
if (!content || this.data.sending) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setData({
|
||||
sending: true,
|
||||
isTyping: true
|
||||
})
|
||||
|
||||
// 添加用户消息到界面
|
||||
// 发送消息(走 SSE → Gateway → SmartClaw → LMStudio)
|
||||
async sendMessage(text) {
|
||||
const content = text ? text.trim() : this.data.inputValue.trim()
|
||||
if (!content || this.data.sending) return
|
||||
|
||||
this.setData({ sending: true, isTyping: true })
|
||||
|
||||
const userMessage = {
|
||||
id: util.generateUniqueId(),
|
||||
content: content,
|
||||
@@ -268,58 +390,144 @@ Page({
|
||||
avatar: '/assets/images/user-avatar.png',
|
||||
time: formatRelativeTime(new Date())
|
||||
}
|
||||
|
||||
this.addMessage(userMessage)
|
||||
|
||||
try {
|
||||
// 模拟AI回复
|
||||
setTimeout(() => {
|
||||
const aiResponse = {
|
||||
id: util.generateUniqueId(),
|
||||
content: this.getMockAIResponse(content),
|
||||
type: MESSAGE_TYPE.TEXT,
|
||||
isMe: false,
|
||||
nickname: '智控未来',
|
||||
avatar: '/assets/images/ai-avatar.png',
|
||||
time: formatRelativeTime(new Date())
|
||||
}
|
||||
this.handleNewMessage(aiResponse)
|
||||
}, 1500)
|
||||
|
||||
this.setData({
|
||||
inputValue: '',
|
||||
sending: false
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('发送消息失败:', error)
|
||||
util.showError('发送失败,请重试')
|
||||
this.setData({
|
||||
sending: false,
|
||||
isTyping: false
|
||||
})
|
||||
this.setData({ inputValue: '' })
|
||||
|
||||
// 创建 AI 占位消息(打字机效果)
|
||||
const aiMsgId = util.generateUniqueId()
|
||||
const aiMsg = {
|
||||
id: aiMsgId, content: '', type: MESSAGE_TYPE.TEXT,
|
||||
isMe: false, nickname: '智控未来',
|
||||
avatar: '/assets/images/ai-avatar.png',
|
||||
time: formatRelativeTime(new Date()), isStreaming: true
|
||||
}
|
||||
this.addMessage(aiMsg)
|
||||
|
||||
try {
|
||||
const app = getApp()
|
||||
const gatewayUrl = app.globalData.gatewayUrl || 'http://localhost:8000'
|
||||
|
||||
// 构建 SSE 请求数据
|
||||
const requestData = {
|
||||
model: "qwen2.5-vl-7b-instruct",
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: content
|
||||
}
|
||||
],
|
||||
stream: true,
|
||||
temperature: 0.7,
|
||||
max_tokens: 500
|
||||
}
|
||||
|
||||
// 发送 SSE 请求
|
||||
const self = this
|
||||
let fullResponse = ''
|
||||
|
||||
// 使用 wx.request 发送请求并处理 SSE 流
|
||||
wx.request({
|
||||
url: `${gatewayUrl}/v1/chat/completions`,
|
||||
method: 'POST',
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: requestData,
|
||||
responseType: 'text',
|
||||
success: function(res) {
|
||||
// 处理 SSE 响应
|
||||
const lines = res.data.split('\n')
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data: ')) {
|
||||
const data = line.substring(6)
|
||||
if (data === '[DONE]') {
|
||||
// SSE 流结束
|
||||
self._typewriterAppend(aiMsgId, fullResponse, true)
|
||||
break
|
||||
}
|
||||
try {
|
||||
const json = JSON.parse(data)
|
||||
if (json.choices && json.choices[0] && json.choices[0].delta && json.choices[0].delta.content) {
|
||||
const chunk = json.choices[0].delta.content
|
||||
fullResponse += chunk
|
||||
self._typewriterAppend(aiMsgId, fullResponse)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('解析 SSE 数据失败:', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
fail: function(err) {
|
||||
console.error('SSE 请求失败:', err)
|
||||
self._typewriterAppend(aiMsgId, self.getMockAIResponse(content), true)
|
||||
}
|
||||
})
|
||||
|
||||
// 8 秒超时:降级为模拟回复
|
||||
setTimeout(() => {
|
||||
const msgs = self.data.messages
|
||||
const aiM = msgs.find(m => m.id === aiMsgId)
|
||||
if (aiM && aiM.isStreaming) {
|
||||
aiM.isStreaming = false
|
||||
self._typewriterAppend(aiMsgId, self.getMockAIResponse(content), true)
|
||||
}
|
||||
}, 8000)
|
||||
|
||||
} catch (err) {
|
||||
console.error('发送失败:', err)
|
||||
const aiResponse = {
|
||||
id: aiMsgId,
|
||||
content: this.getMockAIResponse(content),
|
||||
type: MESSAGE_TYPE.TEXT,
|
||||
isMe: false,
|
||||
nickname: '智控未来',
|
||||
avatar: '/assets/images/ai-avatar.png',
|
||||
time: formatRelativeTime(new Date())
|
||||
}
|
||||
this.handleNewMessage(aiResponse)
|
||||
}
|
||||
|
||||
this.setData({ sending: false })
|
||||
},
|
||||
|
||||
// 打字机效果
|
||||
_typewriterAppend(msgId, fullContent, isFinal = false) {
|
||||
const msgs = this.data.messages
|
||||
const idx = msgs.findIndex(m => m.id === msgId)
|
||||
if (idx === -1) return
|
||||
const cur = msgs[idx].content || ''
|
||||
if (isFinal || cur.length >= fullContent.length) {
|
||||
msgs[idx] = { ...msgs[idx], content: fullContent, isStreaming: false }
|
||||
this.setData({ messages: [...msgs], isTyping: false })
|
||||
return
|
||||
}
|
||||
const next = fullContent[cur.length]
|
||||
msgs[idx] = { ...msgs[idx], content: cur + next, isStreaming: true }
|
||||
this.setData({ messages: [...msgs] })
|
||||
setTimeout(() => { this._typewriterAppend(msgId, fullContent) }, 30)
|
||||
},
|
||||
|
||||
// 模拟AI回复
|
||||
getMockAIResponse(content) {
|
||||
const responses = {
|
||||
'你好': '你好!很高兴为你服务。',
|
||||
'你好': '你好,很高兴为你服务。',
|
||||
'今天天气怎么样': '今天天气晴朗,适合户外活动。',
|
||||
'如何学习编程': '学习编程需要持之以恒,建议从基础语法开始,多动手实践。',
|
||||
'代码': 'def hello():\n print("Hello, World!")',
|
||||
'卡片': '这是一个卡片消息',
|
||||
'卡片': '这是一个卡片消息。',
|
||||
'富文本': '这是一段富文本消息,包含<b>加粗</b>和<u>下划线</u>。',
|
||||
'今天是几月几日': '今天是' + new Date().toLocaleDateString('zh-CN'),
|
||||
'今天是几月几号': '今天是 ' + new Date().toLocaleDateString('zh-CN'),
|
||||
'你是谁': '我是智控未来的AI助手,由LMStudio提供支持。'
|
||||
}
|
||||
return responses[content] || '感谢你的提问,我会为你提供准确的回答。'
|
||||
return responses[content] || '感谢你的提问,我会为你提供准确的答案。'
|
||||
},
|
||||
|
||||
// 长按消息
|
||||
onMessageLongPress(e) {
|
||||
const messageId = e.currentTarget.dataset.messageId
|
||||
const message = this.data.messages.find(msg => msg.id === messageId)
|
||||
|
||||
|
||||
if (message && message.type === MESSAGE_TYPE.TEXT) {
|
||||
wx.showActionSheet({
|
||||
itemList: ['复制消息', '删除消息'],
|
||||
@@ -386,7 +594,7 @@ Page({
|
||||
onCardButtonTap(e) {
|
||||
const action = e.currentTarget.dataset.action
|
||||
wx.showToast({
|
||||
title: `你点击了${action}`,
|
||||
title: '你点击了' + action,
|
||||
icon: 'none'
|
||||
})
|
||||
},
|
||||
@@ -404,10 +612,7 @@ Page({
|
||||
messages: [],
|
||||
lastMessageId: ''
|
||||
})
|
||||
|
||||
// 清空本地存储
|
||||
wx.removeStorageSync('chatHistory')
|
||||
|
||||
wx.showToast({
|
||||
title: '已清空',
|
||||
icon: 'success'
|
||||
@@ -415,5 +620,89 @@ Page({
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 授权成功回调
|
||||
onAuthSuccess(e) {
|
||||
const { authType, data } = e.detail
|
||||
console.log('授权成功:', authType, data)
|
||||
|
||||
const app = getApp()
|
||||
|
||||
switch (authType) {
|
||||
case 'userInfo':
|
||||
app.globalData.userInfo = data
|
||||
wx.setStorageSync('userInfo', data)
|
||||
this.setData({ userInfo: data })
|
||||
this.showUserInfoCard()
|
||||
this.getUserAdditionalInfo()
|
||||
break
|
||||
case 'location':
|
||||
const locationInfo = {
|
||||
latitude: data.latitude,
|
||||
longitude: data.longitude,
|
||||
speed: data.speed,
|
||||
accuracy: data.accuracy
|
||||
}
|
||||
wx.setStorageSync('locationInfo', locationInfo)
|
||||
this.setData({ locationInfo })
|
||||
break
|
||||
}
|
||||
},
|
||||
|
||||
// 授权失败回调
|
||||
onAuthFail(e) {
|
||||
const { authType, error } = e.detail
|
||||
console.error('授权失败:', authType, error)
|
||||
|
||||
wx.showToast({
|
||||
title: '授权失败,请重试',
|
||||
icon: 'none'
|
||||
})
|
||||
},
|
||||
|
||||
// 取消授权回调
|
||||
onAuthCancel(e) {
|
||||
const { authType } = e.detail
|
||||
console.log('取消授权:', authType)
|
||||
|
||||
// 如果是用户信息授权,使用默认信息
|
||||
if (authType === 'userInfo') {
|
||||
this.setData({
|
||||
userInfo: {
|
||||
nickName: '游客',
|
||||
avatarUrl: '/assets/images/user-avatar.png',
|
||||
gender: 0,
|
||||
city: '',
|
||||
province: '',
|
||||
country: ''
|
||||
}
|
||||
})
|
||||
this.showUserInfoCard()
|
||||
}
|
||||
},
|
||||
|
||||
// 打开设置回调
|
||||
onSettingsOpen(e) {
|
||||
const { authType, settings } = e.detail
|
||||
console.log('打开设置:', authType, settings)
|
||||
},
|
||||
|
||||
// 获取UnionID
|
||||
getUnionId() {
|
||||
// 从本地存储获取
|
||||
let unionId = wx.getStorageSync('globalUserId')
|
||||
|
||||
if (!unionId) {
|
||||
const app = getApp()
|
||||
// 如果本地没有,调用app方法获取
|
||||
app.getGlobalUserId((id) => {
|
||||
unionId = id
|
||||
})
|
||||
// 同时返回一个临时ID,确保页面正常显示
|
||||
unionId = '获取中...'
|
||||
}
|
||||
|
||||
return unionId
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1 @@
|
||||
{
|
||||
"navigationBarTitleText": "智控未来",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black",
|
||||
"enablePullDownRefresh": false,
|
||||
"backgroundTextStyle": "light"
|
||||
}
|
||||
{"navigationBarTitleText": "设备", "navigationBarBackgroundColor": "#1a1a1a", "navigationBarTextStyle": "white", "enablePullDownRefresh": false, "backgroundTextStyle": "light", "usingComponents": {"message": "../../components/message/message", "auth-prompt": "../../components/auth-prompt/auth-prompt"}}
|
||||
@@ -100,8 +100,8 @@
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 输入区域 -->
|
||||
<view class="input-container">
|
||||
<!-- 输入区域(tab-bar 聊天模式时由 tab-bar 接管,此处隐藏) -->
|
||||
<view class="input-container" wx:if="{{false}}">
|
||||
<view class="input-wrapper">
|
||||
<input
|
||||
class="message-input"
|
||||
@@ -123,4 +123,14 @@
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 授权提示组件 -->
|
||||
<auth-prompt
|
||||
show="{{showAuthPrompt}}"
|
||||
auth-type="{{authType}}"
|
||||
bind:authSuccess="onAuthSuccess"
|
||||
bind:authFail="onAuthFail"
|
||||
bind:authCancel="onAuthCancel"
|
||||
bind:settingsOpen="onSettingsOpen"
|
||||
/>
|
||||
</view>
|
||||
@@ -1,11 +1,11 @@
|
||||
/* 聊天页面样式 */
|
||||
/* 聊天页面样式 - 深色主题 */
|
||||
@import '../../utils/constant.wxss';
|
||||
|
||||
.chat-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: #f7f7f7;
|
||||
background-color: #0d0d0d;
|
||||
}
|
||||
|
||||
.message-list {
|
||||
@@ -20,12 +20,12 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 400rpx;
|
||||
color: #999;
|
||||
color: #555555;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
color: #555555;
|
||||
}
|
||||
|
||||
.history-tip {
|
||||
@@ -37,8 +37,8 @@
|
||||
|
||||
.history-tip text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
background-color: #f0f0f0;
|
||||
color: #666666;
|
||||
background-color: #1a1a1a;
|
||||
padding: 5rpx 20rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
@@ -85,7 +85,7 @@
|
||||
|
||||
.message-nickname {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
color: #666666;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -96,15 +96,15 @@
|
||||
}
|
||||
|
||||
.message-left .message-body {
|
||||
background-color: white;
|
||||
background-color: #1e1e1e;
|
||||
border-radius: 16rpx 16rpx 16rpx 4rpx;
|
||||
box-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.05);
|
||||
border: 2rpx solid rgba(255,255,255,0.06);
|
||||
}
|
||||
|
||||
.message-right .message-body {
|
||||
background-color: #1677FF;
|
||||
border-radius: 16rpx 16rpx 4rpx 16rpx;
|
||||
box-shadow: 0 1rpx 2rpx rgba(22, 119, 255, 0.1);
|
||||
box-shadow: 0 4rpx 16rpx rgba(22,119,255,0.25);
|
||||
}
|
||||
|
||||
.message-text-container {
|
||||
@@ -117,7 +117,7 @@
|
||||
}
|
||||
|
||||
.message-left .message-text {
|
||||
color: #333;
|
||||
color: #e8e8e8;
|
||||
}
|
||||
|
||||
.message-right .message-text {
|
||||
@@ -132,7 +132,7 @@
|
||||
|
||||
.message-time {
|
||||
font-size: 20rpx;
|
||||
color: #ccc;
|
||||
color: #444444;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
@@ -143,12 +143,13 @@
|
||||
margin: 4rpx 0;
|
||||
}
|
||||
|
||||
/* 代码块样式 */
|
||||
/* 代码块样式 - 深色 */
|
||||
.message-code {
|
||||
background-color: #f5f5f5;
|
||||
background-color: #141414;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
margin: 8rpx 0;
|
||||
border: 2rpx solid #252525;
|
||||
}
|
||||
|
||||
.code-header {
|
||||
@@ -156,8 +157,8 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12rpx 16rpx;
|
||||
background-color: #e8e8e8;
|
||||
border-bottom: 1rpx solid #ddd;
|
||||
background-color: #1a1a1a;
|
||||
border-bottom: 2rpx solid #252525;
|
||||
}
|
||||
|
||||
.code-language {
|
||||
@@ -188,30 +189,30 @@
|
||||
padding: 16rpx;
|
||||
max-height: 400rpx;
|
||||
overflow-y: auto;
|
||||
background-color: #f5f5f5;
|
||||
background-color: #0d0d0d;
|
||||
}
|
||||
|
||||
.code-text {
|
||||
font-size: 24rpx;
|
||||
line-height: 1.4;
|
||||
color: #333;
|
||||
color: #a8dadc;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/* 按钮卡片样式 */
|
||||
/* 按钮卡片样式 - 深色 */
|
||||
.message-button-card {
|
||||
background-color: white;
|
||||
background-color: #1e1e1e;
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx;
|
||||
box-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.05);
|
||||
margin: 8rpx 0;
|
||||
border: 2rpx solid rgba(255,255,255,0.06);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
color: #ffffff;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
@@ -222,8 +223,8 @@
|
||||
}
|
||||
|
||||
.card-button {
|
||||
background-color: #f8f8f8;
|
||||
border: 1rpx solid #e0e0e0;
|
||||
background-color: #141414;
|
||||
border: 2rpx solid #252525;
|
||||
border-radius: 8rpx;
|
||||
padding: 16rpx;
|
||||
font-size: 26rpx;
|
||||
@@ -266,7 +267,7 @@
|
||||
|
||||
.typing-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
@keyframes typing-bounce {
|
||||
@@ -278,17 +279,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* 输入区域 */
|
||||
/* 输入区域 - 深色 */
|
||||
.input-container {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: white;
|
||||
border-top: 1rpx solid #e0e0e0;
|
||||
background: linear-gradient(180deg, #1a1a1a 0%, #141414 100%);
|
||||
border-top: 2rpx solid rgba(255,255,255,0.06);
|
||||
padding: 20rpx;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
@@ -299,11 +299,12 @@
|
||||
|
||||
.message-input {
|
||||
flex: 1;
|
||||
border: 1rpx solid #e0e0e0;
|
||||
border: 2rpx solid #252525;
|
||||
border-radius: 28rpx;
|
||||
padding: 20rpx 28rpx;
|
||||
font-size: 28rpx;
|
||||
background-color: #f8f8f8;
|
||||
background-color: #0d0d0d;
|
||||
color: #ffffff;
|
||||
min-height: 80rpx;
|
||||
max-height: 200rpx;
|
||||
overflow-y: auto;
|
||||
@@ -343,7 +344,7 @@
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* 富文本样式 */
|
||||
/* 富文本样式 - 深色主题 */
|
||||
rich-text {
|
||||
line-height: 1.5;
|
||||
}
|
||||
@@ -361,17 +362,17 @@ rich-text :deep(h3) {
|
||||
|
||||
rich-text :deep(h1) {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
rich-text :deep(h2) {
|
||||
font-size: 28rpx;
|
||||
color: #444;
|
||||
color: #e5e5e5;
|
||||
}
|
||||
|
||||
rich-text :deep(h3) {
|
||||
font-size: 26rpx;
|
||||
color: #555;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
rich-text :deep(ul),
|
||||
@@ -383,6 +384,7 @@ rich-text :deep(ol) {
|
||||
rich-text :deep(li) {
|
||||
margin: 8rpx 0;
|
||||
line-height: 1.5;
|
||||
color: #d0d0d0;
|
||||
}
|
||||
|
||||
rich-text :deep(strong) {
|
||||
|
||||
@@ -1,224 +1,322 @@
|
||||
// 首页逻辑
|
||||
// 首页逻辑 - 深色主题信息流
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
userInfo: null,
|
||||
// 用户信息
|
||||
hasUserInfo: false,
|
||||
canIUse: wx.canIUse('button.open-type.getUserInfo'),
|
||||
openId: '',
|
||||
phoneNumber: '',
|
||||
unionId: '',
|
||||
locationInfo: null,
|
||||
deviceInfo: null,
|
||||
systemInfo: null,
|
||||
networkType: '',
|
||||
wxVersion: '',
|
||||
accountInfo: null,
|
||||
clipboardData: null,
|
||||
|
||||
// 时间显示
|
||||
currentTime: '',
|
||||
yesterdayTime: '',
|
||||
|
||||
// 轮播Banner数据 - 蓝色主题
|
||||
banners: [
|
||||
{ id: 1, tag: '健康科普', title: '在多才和多艺之间\n我选择了多肉~', desc: '医生,我喝水都要胖,咋个整?', image: '' },
|
||||
{ id: 2, tag: '专家讲座', title: '春季养生指南:\n中医教你调理身体', desc: '', image: '' }
|
||||
],
|
||||
|
||||
// 轮播Banner数据 - 红色喜报主题
|
||||
redBanners: [
|
||||
{ id: 1, title: '四川省人民医院高质量发展跃上新台阶,成功迈入全国公立医院第一方阵!', image: '' }
|
||||
],
|
||||
|
||||
websocketConnected: false,
|
||||
version: app.globalData.version
|
||||
version: app.globalData.version || '1.0.0',
|
||||
socketTask: null,
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
if (app.globalData.userInfo) {
|
||||
this.setData({
|
||||
userInfo: app.globalData.userInfo,
|
||||
hasUserInfo: true
|
||||
})
|
||||
} else if (this.data.canIUse) {
|
||||
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
|
||||
// 所以此处加入 callback 以防止这种情况
|
||||
app.userInfoReadyCallback = res => {
|
||||
this.setData({
|
||||
userInfo: res.userInfo,
|
||||
hasUserInfo: true
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// 在没有 open-type=getUserInfo 版本的兼容处理
|
||||
wx.getUserInfo({
|
||||
success: res => {
|
||||
app.globalData.userInfo = res.userInfo
|
||||
this.setData({
|
||||
userInfo: res.userInfo,
|
||||
hasUserInfo: true
|
||||
})
|
||||
}
|
||||
})
|
||||
this.updateTimeDisplay()
|
||||
this.loadStoredInfo()
|
||||
this.initWebSocketSafe()
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.updateTimeDisplay()
|
||||
this.setTabBarActive()
|
||||
if (this.data.hasUserInfo) this.loadStoredInfo()
|
||||
},
|
||||
|
||||
// ==============================================
|
||||
// 加载本地存储的信息
|
||||
// ==============================================
|
||||
loadStoredInfo() {
|
||||
const openId = wx.getStorageSync('openId') || ''
|
||||
const phoneNumber = wx.getStorageSync('phoneNumber') || ''
|
||||
const deviceInfo = app.getDeviceInfo?.() || null
|
||||
|
||||
this.setData({ openId, phoneNumber, deviceInfo })
|
||||
|
||||
if (openId) {
|
||||
this.setData({ hasUserInfo: true })
|
||||
}
|
||||
|
||||
// 获取网络状态
|
||||
app.getNetworkType?.((networkType) => {
|
||||
if (networkType) this.setData({ networkType })
|
||||
})
|
||||
|
||||
// 获取位置(需要授权)
|
||||
app.getLocation?.((locationInfo) => {
|
||||
if (locationInfo) this.setData({ locationInfo })
|
||||
})
|
||||
|
||||
// 获取小程序信息
|
||||
try {
|
||||
const accountInfo = wx.getAccountInfoSync()
|
||||
this.setData({ accountInfo })
|
||||
} catch (error) {
|
||||
console.error('获取小程序信息失败:', error)
|
||||
this.setData({ accountInfo: null })
|
||||
}
|
||||
|
||||
// 获取剪贴板信息(需要授权)
|
||||
wx.getClipboardData({
|
||||
success: (res) => {
|
||||
this.setData({ clipboardData: res.data })
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('获取剪贴板信息失败:', error)
|
||||
this.setData({ clipboardData: null })
|
||||
}
|
||||
})
|
||||
|
||||
// 获取微信版本信息
|
||||
try {
|
||||
const systemInfo = wx.getSystemInfoSync()
|
||||
this.setData({ wxVersion: systemInfo.version, systemInfo })
|
||||
} catch (error) {
|
||||
console.error('获取微信版本信息失败:', error)
|
||||
this.setData({ wxVersion: null, systemInfo: null })
|
||||
}
|
||||
|
||||
// 获取UnionID(个人小程序无法获取)
|
||||
this.setData({ unionId: '无法获取(需企业资质)' })
|
||||
|
||||
// 获取手机号(个人小程序无法获取)
|
||||
if (!phoneNumber) {
|
||||
this.setData({ phoneNumber: '无法获取(需企业资质)' })
|
||||
}
|
||||
|
||||
// 初始化WebSocket连接
|
||||
this.initWebSocket()
|
||||
},
|
||||
|
||||
getUserProfile(e) {
|
||||
// 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
|
||||
wx.getUserProfile({
|
||||
desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
|
||||
// ==============================================
|
||||
// 登录:通过 WebSocket 发 wechat_login,收到 openid 后存本地
|
||||
// ==============================================
|
||||
onLogin() {
|
||||
// 确保 WebSocket 已连接
|
||||
if (!this.data.socketTask || !this.data.websocketConnected) {
|
||||
// 先重连
|
||||
this.initWebSocketSafe()
|
||||
setTimeout(() => this._doLogin(), 1500)
|
||||
return
|
||||
}
|
||||
this._doLogin()
|
||||
},
|
||||
|
||||
_doLogin() {
|
||||
if (!this.data.websocketConnected) {
|
||||
wx.showToast({ title: '连接中...', icon: 'none' })
|
||||
return
|
||||
}
|
||||
wx.login({
|
||||
success: (res) => {
|
||||
app.globalData.userInfo = res.userInfo
|
||||
this.setData({
|
||||
userInfo: res.userInfo,
|
||||
hasUserInfo: true
|
||||
if (!res.code) {
|
||||
wx.showToast({ title: '微信登录失败', icon: 'none' })
|
||||
return
|
||||
}
|
||||
// 记录待处理登录(收到 wechat_login_ret 时判断)
|
||||
this._pendingLogin = true
|
||||
this.data.socketTask.send({
|
||||
data: JSON.stringify({
|
||||
type: 'wechat_login',
|
||||
data: { code: res.code }
|
||||
}),
|
||||
fail: () => {
|
||||
this._pendingLogin = false
|
||||
wx.showToast({ title: '发送失败', icon: 'none' })
|
||||
}
|
||||
})
|
||||
|
||||
// 发送用户信息到服务器
|
||||
this.sendUserInfoToServer(res.userInfo)
|
||||
},
|
||||
fail:
|
||||
|
||||
// ==============================================
|
||||
// 获取手机号(button open-type="getPhoneNumber")
|
||||
// ==============================================
|
||||
onGetPhoneNumber(e) {
|
||||
// 检查是否有加密数据(旧版方式,个人小程序可用)
|
||||
const { encryptedData, iv, code } = e.detail
|
||||
if (!encryptedData || !iv) {
|
||||
wx.showToast({ title: '请允许授权', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// 必须先登录有 openid
|
||||
const openid = this.data.openId || wx.getStorageSync('openId')
|
||||
if (!openid) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// 确保 WebSocket 连接
|
||||
if (!this.data.websocketConnected) {
|
||||
wx.showToast({ title: '连接中...', icon: 'none' })
|
||||
this.initWebSocketSafe()
|
||||
setTimeout(() => {
|
||||
this._sendPhoneDecrypt(openid, encryptedData, iv)
|
||||
}, 1500)
|
||||
return
|
||||
}
|
||||
|
||||
this._sendPhoneDecrypt(openid, encryptedData, iv)
|
||||
},
|
||||
|
||||
_sendPhoneDecrypt(openid, encryptedData, iv) {
|
||||
if (!this.data.websocketConnected) {
|
||||
wx.showToast({ title: 'WebSocket 未连接', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this._pendingPhone = true
|
||||
this.data.socketTask.send({
|
||||
data: JSON.stringify({
|
||||
type: 'wechat_decrypt_phone',
|
||||
data: { openid, encryptedData, iv }
|
||||
}),
|
||||
fail: () => {
|
||||
this._pendingPhone = false
|
||||
wx.showToast({ title: '发送失败', icon: 'none' })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getUserInfo(e) {
|
||||
// 不推荐使用getUserInfo获取用户信息,预计自2021年4月13日起,getUserInfo将不再弹出弹窗,并直接返回匿名的用户个人信息
|
||||
app.globalData.userInfo = e.detail.userInfo
|
||||
// ==============================================
|
||||
// 时间显示
|
||||
// ==============================================
|
||||
updateTimeDisplay() {
|
||||
const now = new Date()
|
||||
const m = now.getMonth() + 1
|
||||
const d = now.getDate()
|
||||
const h = now.getHours()
|
||||
const mm = String(now.getMinutes()).padStart(2, '0')
|
||||
const period = h >= 12 ? '下午' : '上午'
|
||||
const displayH = h > 12 ? h - 12 : h
|
||||
|
||||
this.setData({
|
||||
userInfo: e.detail.userInfo,
|
||||
hasUserInfo: true
|
||||
})
|
||||
|
||||
// 发送用户信息到服务器
|
||||
this.sendUserInfoToServer(e.detail.userInfo)
|
||||
},
|
||||
|
||||
// 发送用户信息到服务器
|
||||
sendUserInfoToServer(userInfo) {
|
||||
wx.request({
|
||||
url: `${app.globalData.apiBase}/user/info`,
|
||||
method: 'POST',
|
||||
data: {
|
||||
userInfo: userInfo,
|
||||
deviceId: app.globalData.systemInfo.model
|
||||
},
|
||||
header: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
success: (res) => {
|
||||
console.log('用户信息上传成功', res.data)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('用户信息上传失败', err)
|
||||
}
|
||||
currentTime: `${m}月${d}日 ${period}${displayH}:${mm}`,
|
||||
yesterdayTime: `昨天 晚上8:36`
|
||||
})
|
||||
},
|
||||
|
||||
// 初始化WebSocket连接
|
||||
initWebSocket() {
|
||||
const socket = wx.connectSocket({
|
||||
url: app.globalData.websocketUrl,
|
||||
header: {
|
||||
'content-type': 'application/json'
|
||||
}
|
||||
})
|
||||
// ==============================================
|
||||
// TabBar 选中
|
||||
// ==============================================
|
||||
setTabBarActive() {
|
||||
if (this.getTabBar) this.getTabBar()?.setData({ currentTab: 0 })
|
||||
},
|
||||
|
||||
// ==============================================
|
||||
// 新闻点击
|
||||
// ==============================================
|
||||
onNewsTap(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
console.log('点击新闻:', id)
|
||||
wx.showToast({ title: '加载中...', icon: 'none' })
|
||||
},
|
||||
|
||||
// ==============================================
|
||||
// WebSocket 安全版(防重复连接、防崩溃)
|
||||
// ==============================================
|
||||
initWebSocketSafe() {
|
||||
if (this.data.socketTask) return
|
||||
|
||||
const socket = wx.connectSocket({ url: app.globalData.websocketUrl })
|
||||
this.setData({ socketTask: socket })
|
||||
|
||||
socket.onOpen(() => {
|
||||
console.log('WebSocket连接已打开')
|
||||
this.setData({
|
||||
websocketConnected: true
|
||||
})
|
||||
|
||||
// 发送认证信息
|
||||
this.setData({ websocketConnected: true })
|
||||
socket.send({
|
||||
data: JSON.stringify({
|
||||
type: 'auth',
|
||||
userId: app.globalData.userInfo ? app.globalData.userInfo.nickName : 'anonymous',
|
||||
deviceId: app.globalData.systemInfo.model,
|
||||
userId: app.globalData.userInfo?.nickName || 'anonymous',
|
||||
deviceId: app.globalData.systemInfo?.model || '',
|
||||
timestamp: Date.now()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
socket.onMessage((res) => {
|
||||
console.log('收到WebSocket消息', res.data)
|
||||
socket.onMessage(msg => {
|
||||
try {
|
||||
const data = JSON.parse(res.data)
|
||||
const data = JSON.parse(msg.data)
|
||||
this.handleWebSocketMessage(data)
|
||||
} catch (e) {
|
||||
console.error('解析WebSocket消息失败', e)
|
||||
}
|
||||
} catch (e) {}
|
||||
})
|
||||
|
||||
socket.onClose(() => {
|
||||
console.log('WebSocket连接已关闭')
|
||||
this.setData({
|
||||
websocketConnected: false
|
||||
})
|
||||
|
||||
// 3秒后尝试重连
|
||||
setTimeout(() => {
|
||||
this.initWebSocket()
|
||||
}, 3000)
|
||||
this.setData({ websocketConnected: false, socketTask: null })
|
||||
setTimeout(() => this.initWebSocketSafe(), 3000)
|
||||
})
|
||||
|
||||
socket.onError((err) => {
|
||||
console.error('WebSocket连接错误', err)
|
||||
this.setData({
|
||||
websocketConnected: false
|
||||
})
|
||||
socket.onError(() => {
|
||||
this.setData({ websocketConnected: false, socketTask: null })
|
||||
})
|
||||
},
|
||||
|
||||
// 处理WebSocket消息
|
||||
handleWebSocketMessage(data) {
|
||||
switch (data.type) {
|
||||
case 'task_status':
|
||||
// 处理任务状态更新
|
||||
this.handleTaskStatusUpdate(data)
|
||||
case 'wechat_login_ret':
|
||||
// 收到登录响应(可能比 send 回调更早或更晚到达)
|
||||
if (data.data?.openid) {
|
||||
wx.setStorageSync('openId', data.data.openid)
|
||||
this.setData({ openId: data.data.openid, hasUserInfo: true })
|
||||
wx.showToast({ title: '登录成功', icon: 'success' })
|
||||
} else {
|
||||
wx.showToast({ title: '登录失败:' + (data.data?.error || '未知'), icon: 'none' })
|
||||
}
|
||||
this._pendingLogin = false
|
||||
break
|
||||
case 'message':
|
||||
// 处理聊天消息
|
||||
this.handleChatMessage(data)
|
||||
case 'ai_message':
|
||||
// AI 回复(聊天页使用)
|
||||
if (this.handleAiMessage) this.handleAiMessage(data)
|
||||
break
|
||||
case 'wechat_decrypt_phone_ret':
|
||||
// 手机号解密响应
|
||||
if (data.data?.phone) {
|
||||
wx.setStorageSync('phoneNumber', data.data.phone)
|
||||
this.setData({ phoneNumber: data.data.phone })
|
||||
wx.showToast({ title: '绑定成功', icon: 'success' })
|
||||
} else {
|
||||
wx.showToast({ title: '获取失败:' + (data.data?.error || '未知'), icon: 'none' })
|
||||
}
|
||||
this._pendingPhone = false
|
||||
break
|
||||
case 'pong':
|
||||
break
|
||||
default:
|
||||
console.log('未知消息类型', data.type)
|
||||
break
|
||||
}
|
||||
},
|
||||
|
||||
// 处理任务状态更新
|
||||
handleTaskStatusUpdate(data) {
|
||||
// 可以在这里更新任务列表或显示通知
|
||||
if (data.status === 'completed') {
|
||||
wx.showToast({
|
||||
title: '任务完成',
|
||||
icon: 'success'
|
||||
})
|
||||
} else if (data.status === 'failed') {
|
||||
wx.showToast({
|
||||
title: '任务失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 处理聊天消息
|
||||
handleChatMessage(data) {
|
||||
// 可以在这里显示新消息通知
|
||||
if (data.message) {
|
||||
wx.showToast({
|
||||
title: '新消息',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 跳转到聊天页面
|
||||
// ==============================================
|
||||
// 跳转聊天页
|
||||
// ==============================================
|
||||
goToChat() {
|
||||
wx.navigateTo({
|
||||
url: '/pages/chat/chat'
|
||||
})
|
||||
wx.navigateTo({ url: '/pages/chat/chat' })
|
||||
},
|
||||
|
||||
// 跳转到任务页面
|
||||
// ==============================================
|
||||
// 跳转任务页
|
||||
// ==============================================
|
||||
goToTask() {
|
||||
wx.navigateTo({
|
||||
url: '/pages/task/task'
|
||||
})
|
||||
},
|
||||
|
||||
// 显示设备信息
|
||||
showDeviceInfo() {
|
||||
const systemInfo = app.globalData.systemInfo
|
||||
wx.showModal({
|
||||
title: '设备信息',
|
||||
content: `设备型号:${systemInfo.model}\n系统版本:${systemInfo.system}\n微信版本:${systemInfo.version}\n屏幕尺寸:${systemInfo.screenWidth}x${systemInfo.screenHeight}`,
|
||||
showCancel: false
|
||||
})
|
||||
},
|
||||
|
||||
onShareAppMessage() {
|
||||
return {
|
||||
title: '智控未来 - 企业微信智能控制系统',
|
||||
path: '/pages/index/index'
|
||||
}
|
||||
wx.navigateTo({ url: '/pages/task/task' })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
{
|
||||
"navigationBarTitleText": "智控未来"
|
||||
}
|
||||
{"navigationBarTitleText": "智控未来"}
|
||||
@@ -1,50 +1,283 @@
|
||||
<!-- 首页 - 用户登录和主界面 -->
|
||||
<view class="container">
|
||||
<!-- 用户信息区域 -->
|
||||
<view class="user-info" wx:if="{{userInfo}}">
|
||||
<image class="avatar" src="{{userInfo.avatarUrl}}" mode="aspectFill"></image>
|
||||
<text class="nickname">{{userInfo.nickName}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 登录按钮 -->
|
||||
<view class="login-section" wx:else>
|
||||
<button class="login-btn" bindtap="getUserProfile">授权登录</button>
|
||||
<text class="login-tip">请先授权登录以使用完整功能</text>
|
||||
</view>
|
||||
|
||||
<!-- 功能菜单 -->
|
||||
<view class="feature-menu" wx:if="{{userInfo}}">
|
||||
<view class="menu-item" bindtap="goToChat">
|
||||
<image class="menu-icon" src="/assets/icons/chat.png"></image>
|
||||
<text class="menu-title">智能聊天</text>
|
||||
<text class="menu-desc">与AI助手对话</text>
|
||||
</view>
|
||||
<!-- 首页 - 深色主题信息流布局 -->
|
||||
<view class="home-page">
|
||||
|
||||
<!-- 内容滚动区域 -->
|
||||
<scroll-view class="content-scroll" scroll-y="{{true}}">
|
||||
|
||||
<view class="menu-item" bindtap="goToTask">
|
||||
<image class="menu-icon" src="/assets/icons/task.png"></image>
|
||||
<text class="menu-title">任务管理</text>
|
||||
<text class="menu-desc">创建和管理任务</text>
|
||||
<!-- 时间分隔线 -->
|
||||
<view class="time-divider">
|
||||
<text class="time-text">{{currentTime}}</text>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" bindtap="showDeviceInfo">
|
||||
<image class="menu-icon" src="/assets/icons/device.png"></image>
|
||||
<text class="menu-title">设备信息</text>
|
||||
<text class="menu-desc">查看设备状态</text>
|
||||
|
||||
<!-- 用户信息卡片 -->
|
||||
<view class="user-card">
|
||||
<!-- 卡片头部 -->
|
||||
<view class="user-card-header">
|
||||
<text class="user-card-title">账户信息</text>
|
||||
<text class="user-card-subtitle" wx:if="{{!hasUserInfo}}">点击按钮获取信息</text>
|
||||
<text class="user-card-subtitle status-ok" wx:if="{{hasUserInfo}}">已获取</text>
|
||||
</view>
|
||||
|
||||
<!-- 已登录:展示所有可获取信息 -->
|
||||
<block wx:if="{{hasUserInfo}}">
|
||||
<!-- 头像 + 昵称区域 -->
|
||||
<view class="user-profile-row">
|
||||
<view class="avatar-wrap">
|
||||
<open-data type="userAvatarUrl" class="open-avatar"></open-data>
|
||||
</view>
|
||||
<view class="user-basic-info">
|
||||
<view class="info-row">
|
||||
<text class="info-label">昵称</text>
|
||||
<text class="info-value受限">暂时受限</text>
|
||||
<text class="info-status error">❌</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">头像</text>
|
||||
<text class="info-value受限">暂时受限</text>
|
||||
<text class="info-status error">❌</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">OpenID</text>
|
||||
<text class="info-value" selectable="true">{{openId || '加载中...'}}</text>
|
||||
<text class="info-status ok">✅</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 信息列表 -->
|
||||
<view class="info-list">
|
||||
<view class="info-row">
|
||||
<text class="info-label">UnionID</text>
|
||||
<text class="info-value受限">{{unionId}}</text>
|
||||
<text class="info-status error">❌</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">手机号</text>
|
||||
<text class="info-value" wx:if="{{phoneNumber}}" selectable="true">{{phoneNumber}}</text>
|
||||
<text class="info-value hint" wx:if="{{!phoneNumber}}">点击下方按钮获取</text>
|
||||
<text class="info-status ok">✅</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">性别</text>
|
||||
<text class="info-value受限">暂时受限</text>
|
||||
<text class="info-status error">❌</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">地区</text>
|
||||
<text class="info-value受限">暂时受限</text>
|
||||
<text class="info-status error">❌</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">设备</text>
|
||||
<text class="info-value" wx:if="{{systemInfo}}">{{systemInfo.brand}} {{systemInfo.model}}</text>
|
||||
<text class="info-value hint" wx:if="{{!systemInfo}}">加载中...</text>
|
||||
<text class="info-status ok">✅</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">系统</text>
|
||||
<text class="info-value" wx:if="{{systemInfo}}">{{systemInfo.system}} {{systemInfo.version}}</text>
|
||||
<text class="info-value hint" wx:if="{{!systemInfo}}">加载中...</text>
|
||||
<text class="info-status ok">✅</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">微信版本</text>
|
||||
<text class="info-value" wx:if="{{wxVersion}}">{{wxVersion}}</text>
|
||||
<text class="info-value hint" wx:if="{{!wxVersion}}">加载中...</text>
|
||||
<text class="info-status ok">✅</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">小程序版本</text>
|
||||
<text class="info-value" wx:if="{{accountInfo}}">{{accountInfo.miniProgram.version}}</text>
|
||||
<text class="info-value hint" wx:if="{{!accountInfo}}">加载中...</text>
|
||||
<text class="info-status ok">✅</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">网络</text>
|
||||
<text class="info-value" wx:if="{{networkType}}">{{networkType}}</text>
|
||||
<text class="info-value hint" wx:if="{{!networkType}}">加载中...</text>
|
||||
<text class="info-status ok">✅</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">位置</text>
|
||||
<text class="info-value受限" wx:if="{{!locationInfo}}">授权后获取</text>
|
||||
<text class="info-value" wx:if="{{locationInfo}}">{{locationInfo.latitude?.toFixed(4)}}, {{locationInfo.longitude?.toFixed(4)}}</text>
|
||||
<text class="info-status error">⚠️</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">剪贴板</text>
|
||||
<text class="info-value" wx:if="{{clipboardData}}">{{clipboardData}}</text>
|
||||
<text class="info-value hint" wx:if="{{!clipboardData}}">未授权</text>
|
||||
<text class="info-status ok">✅</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 手机号获取按钮 -->
|
||||
<view class="phone-btn-wrap" wx:if="{{!phoneNumber}}">
|
||||
<button
|
||||
class="phone-btn"
|
||||
open-type="getPhoneNumber"
|
||||
bindgetphonenumber="onGetPhoneNumber"
|
||||
>
|
||||
📱 获取手机号
|
||||
</button>
|
||||
</view>
|
||||
<view class="phone-btn-wrap" wx:if="{{phoneNumber}}">
|
||||
<view class="phone-btn-done">✅ 手机号已绑定:{{phoneNumber}}</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- 未登录 -->
|
||||
<block wx:if="{{!hasUserInfo}}">
|
||||
<view class="not-logged-in">
|
||||
<view class="open-data-info">
|
||||
<open-data type="userAvatarUrl" class="open-avatar-large"></open-data>
|
||||
</view>
|
||||
<text class="not-logged-hint">微信昵称暂时受限,请先登录</text>
|
||||
<view class="info-list">
|
||||
<view class="info-row">
|
||||
<text class="info-label">OpenID</text>
|
||||
<text class="info-value受限">登录后获取</text>
|
||||
<text class="info-status error">❌</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">手机号</text>
|
||||
<text class="info-value受限">登录后获取</text>
|
||||
<text class="info-status error">❌</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">设备</text>
|
||||
<text class="info-value受限">登录后获取</text>
|
||||
<text class="info-status error">❌</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">系统</text>
|
||||
<text class="info-value受限">登录后获取</text>
|
||||
<text class="info-status error">❌</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">网络</text>
|
||||
<text class="info-value受限">登录后获取</text>
|
||||
<text class="info-status error">❌</text>
|
||||
</view>
|
||||
</view>
|
||||
<button class="login-btn" bindtap="onLogin">微信登录,获取 OpenID</button>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 系统状态 -->
|
||||
<view class="system-status">
|
||||
<view class="status-item">
|
||||
<text class="status-label">连接状态:</text>
|
||||
<text class="status-value {{websocketConnected ? 'status-success' : 'status-error'}}">
|
||||
{{websocketConnected ? '已连接' : '未连接'}}
|
||||
</text>
|
||||
|
||||
<!-- 轮播Banner -->
|
||||
<swiper
|
||||
class="banner-swiper"
|
||||
indicator-dots="{{false}}"
|
||||
autoplay="{{true}}"
|
||||
interval="{{5000}}"
|
||||
duration="{{500}}"
|
||||
circular="{{true}}"
|
||||
>
|
||||
<swiper-item wx:for="{{banners}}" wx:key="id">
|
||||
<view class="banner-item" style="background-image: url('{{item.image}}');">
|
||||
<view class="banner-content">
|
||||
<view class="banner-tag-row">
|
||||
<text class="banner-tag">{{item.tag}}</text>
|
||||
</view>
|
||||
<text class="banner-title">{{item.title}}</text>
|
||||
<text class="banner-desc" wx:if="{{item.desc}}">{{item.desc}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<!-- 信息流卡片列表 -->
|
||||
<view class="news-list">
|
||||
|
||||
<!-- 卡片项1 -->
|
||||
<view class="news-card" bindtap="onNewsTap" data-id="1">
|
||||
<view class="card-main">
|
||||
<view class="card-category">【省医药事】</view>
|
||||
<text class="card-title">硝酸甘油vs速效救心丸:心脏急救药,用对才救命!</text>
|
||||
</view>
|
||||
<view class="card-tag tag-blue">
|
||||
<text class="tag-text">省医药事</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 卡片项2 -->
|
||||
<view class="news-card" bindtap="onNewsTap" data-id="2">
|
||||
<view class="card-main">
|
||||
<view class="card-category">【科普小站】</view>
|
||||
<text class="card-title">守护"星星的孩子"笑容:孤独症患儿口腔保健全攻略</text>
|
||||
</view>
|
||||
<view class="card-tag tag-cyan">
|
||||
<text class="tag-text">科普小站</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="status-item">
|
||||
<text class="status-label">系统版本:</text>
|
||||
<text class="status-value">{{version}}</text>
|
||||
|
||||
<!-- 第二个时间分隔线 -->
|
||||
<view class="time-divider">
|
||||
<text class="time-text">{{yesterdayTime}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 第二个轮播Banner(喜报类型) -->
|
||||
<swiper
|
||||
class="banner-swiper banner-red"
|
||||
indicator-dots="{{false}}"
|
||||
autoplay="{{true}}"
|
||||
interval="{{6000}}"
|
||||
duration="{{500}}"
|
||||
circular="{{true}}"
|
||||
>
|
||||
<swiper-item wx:for="{{redBanners}}" wx:key="id">
|
||||
<view class="banner-item banner-red-item" style="background-image: url('{{item.image}}');">
|
||||
<view class="banner-content">
|
||||
<view class="banner-decoration">喜报</view>
|
||||
<text class="banner-title banner-white-title">{{item.title}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<!-- 更多信息流卡片 -->
|
||||
<view class="news-list">
|
||||
|
||||
<!-- 卡片项3 -->
|
||||
<view class="news-card" bindtap="onNewsTap" data-id="3">
|
||||
<view class="card-main">
|
||||
<view class="card-category">【线上义诊】</view>
|
||||
<text class="card-title">连续三天!全国肿瘤防治宣传周在线义诊来啦~!</text>
|
||||
</view>
|
||||
<view class="card-tag tag-red">
|
||||
<text class="tag-text">义诊信息</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 卡片项4 -->
|
||||
<view class="news-card" bindtap="goToChat">
|
||||
<view class="card-main">
|
||||
<view class="card-category">【智能服务】</view>
|
||||
<text class="card-title">AI智能助手在线,随时随地解答健康疑问</text>
|
||||
</view>
|
||||
<view class="card-tag tag-purple">
|
||||
<text class="tag-text">AI助手</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 卡片项5 -->
|
||||
<view class="news-card" bindtap="goToTask">
|
||||
<view class="card-main">
|
||||
<view class="card-category">【任务中心】</view>
|
||||
<text class="card-title">查看您的待办事项和预约提醒</text>
|
||||
</view>
|
||||
<view class="card-tag tag-green">
|
||||
<text class="tag-text">任务中心</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<!-- 底部占位 -->
|
||||
<view class="bottom-placeholder"></view>
|
||||
</scroll-view>
|
||||
|
||||
</view>
|
||||
|
||||
@@ -1,125 +1,579 @@
|
||||
/* 首页样式 */
|
||||
.container {
|
||||
padding: 20rpx;
|
||||
background-color: #f5f5f5;
|
||||
/* 首页 - 深色主题信息流样式 */
|
||||
|
||||
.home-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
background-color: #0d0d0d;
|
||||
}
|
||||
|
||||
/* 用户信息区域 */
|
||||
.user-info {
|
||||
/* ====== 顶部标题栏 ====== */
|
||||
|
||||
|
||||
/* ====== 滚动内容区 ====== */
|
||||
.content-scroll {
|
||||
flex: 1;
|
||||
height: calc(100vh - 180rpx);
|
||||
}
|
||||
|
||||
/* ====== 时间分隔线 ====== */
|
||||
.time-divider {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 28rpx 0 20rpx;
|
||||
}
|
||||
|
||||
.time-text {
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
/* ====== 轮播Banner ====== */
|
||||
.banner-swiper {
|
||||
margin: 0 24rpx 24rpx;
|
||||
height: 320rpx;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.banner-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 20rpx;
|
||||
position: relative;
|
||||
/* 默认蓝色渐变背景(当图片未加载时) */
|
||||
background-image: linear-gradient(135deg, #1890ff 0%, #0958d9 50%, #0050b3 100%);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
padding: 40rpx 36rpx;
|
||||
}
|
||||
|
||||
.banner-red-item {
|
||||
/* 红色渐变背景 */
|
||||
background-image: linear-gradient(135deg, #cf1322 0%, #a8071a 50%, #820014 100%);
|
||||
}
|
||||
|
||||
.banner-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.banner-tag-row {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.banner-tag {
|
||||
display: inline-block;
|
||||
background: rgba(255,255,255,0.25);
|
||||
color: white;
|
||||
font-size: 22rpx;
|
||||
padding: 6rpx 18rpx;
|
||||
border-radius: 8rpx;
|
||||
backdrop-filter: blur(10rpx);
|
||||
}
|
||||
|
||||
.banner-title {
|
||||
display: block;
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
line-height: 1.4;
|
||||
letter-spacing: 1rpx;
|
||||
text-shadow: 0 2rpx 8rpx rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.banner-white-title {
|
||||
font-size: 38rpx;
|
||||
}
|
||||
|
||||
.banner-desc {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
color: rgba(255,255,255,0.9);
|
||||
margin-top: 8rpx;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.banner-decoration {
|
||||
font-size: 72rpx;
|
||||
font-weight: 900;
|
||||
color: rgba(255,215,0,0.85);
|
||||
letter-spacing: 8rpx;
|
||||
margin-bottom: 8rpx;
|
||||
text-shadow:
|
||||
0 0 20rpx rgba(255,215,0,0.5),
|
||||
2rpx 2rpx 0 rgba(139,0,0,0.5);
|
||||
}
|
||||
|
||||
/* ====== 信息流卡片列表 ====== */
|
||||
.news-list {
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.news-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 40rpx;
|
||||
background: linear-gradient(135deg, #07c160 0%, #06a050 100%);
|
||||
border-radius: 20rpx;
|
||||
margin-bottom: 30rpx;
|
||||
color: white;
|
||||
justify-content: space-between;
|
||||
background-color: #1a1a1a;
|
||||
border-radius: 16rpx;
|
||||
padding: 28rpx 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 120rpx;
|
||||
.news-card:active {
|
||||
transform: scale(0.98);
|
||||
background-color: #222222;
|
||||
}
|
||||
|
||||
.card-main {
|
||||
flex: 1;
|
||||
margin-right: 20rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-category {
|
||||
font-size: 22rpx;
|
||||
color: #888888;
|
||||
margin-bottom: 10rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 29rpx;
|
||||
color: #e8e8e8;
|
||||
line-height: 1.5;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* ====== 标签徽章 ====== */
|
||||
.card-tag {
|
||||
flex-shrink: 0;
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
writing-mode: vertical-lr;
|
||||
letter-spacing: 6rpx;
|
||||
}
|
||||
|
||||
.tag-blue {
|
||||
background: linear-gradient(145deg, #1677ff, #0958d9);
|
||||
}
|
||||
|
||||
.tag-cyan {
|
||||
background: linear-gradient(145deg, #13c2c2, #08979c);
|
||||
}
|
||||
|
||||
.tag-red {
|
||||
background: linear-gradient(145deg, #f5222d, #cf1322);
|
||||
}
|
||||
|
||||
.tag-purple {
|
||||
background: linear-gradient(145deg, #722ed1, #531dab);
|
||||
}
|
||||
|
||||
.tag-green {
|
||||
background: linear-gradient(145deg, #52c41a, #389e0d);
|
||||
}
|
||||
|
||||
.tag-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* ====== 底部占位(给自定义TabBar留空间)====== */
|
||||
.bottom-placeholder {
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 30rpx;
|
||||
border: 4rpx solid white;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
/* ====== 登录弹窗 ====== */
|
||||
.login-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0,0,0,0.75);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: relative;
|
||||
width: 600rpx;
|
||||
background: linear-gradient(180deg, #222222 0%, #1a1a1a 100%);
|
||||
border-radius: 32rpx;
|
||||
padding: 60rpx 48rpx;
|
||||
z-index: 2;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
margin-bottom: 48rpx;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
display: block;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
/* 登录区域 */
|
||||
.login-section {
|
||||
padding: 60rpx 40rpx;
|
||||
background: white;
|
||||
.modal-subtitle {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.modal-login-btn {
|
||||
width: 100%;
|
||||
height: 96rpx;
|
||||
background: linear-gradient(135deg, #07c160 0%, #06ae56 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 48rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 8rpx 24rpx rgba(7,193,96,0.35);
|
||||
transition: all 0.3s ease;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.modal-login-btn:active {
|
||||
transform: scale(0.97);
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.modal-cancel {
|
||||
font-size: 28rpx;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
/* ====== 用户信息显示区域 ====== */
|
||||
.user-info-section {
|
||||
margin: 0 24rpx 24rpx;
|
||||
background-color: #1a1a1a;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.user-info-header {
|
||||
background: linear-gradient(135deg, #1677ff 0%, #0958d9 100%);
|
||||
padding: 20rpx 24rpx;
|
||||
}
|
||||
|
||||
.user-info-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.user-info-content {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.user-info-text {
|
||||
font-size: 26rpx;
|
||||
color: #e8e8e8;
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.get-phone-btn {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(135deg, #07c160 0%, #06ae56 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
margin-top: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.get-phone-btn:active {
|
||||
opacity: 0.9;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.open-data-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.open-data-info open-data {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.open-data-info open-data[type="userAvatarUrl"] {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.open-data-info open-data[type="userNickName"] {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(135deg, #1677FF 0%, #0C66E4 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
margin-top: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4rpx 12rpx rgba(22, 119, 255, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.login-btn:active {
|
||||
opacity: 0.9;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
/* ====== 用户信息卡片 ====== */
|
||||
.user-card {
|
||||
margin: 0 24rpx 24rpx;
|
||||
background-color: #1a1a1a;
|
||||
border-radius: 20rpx;
|
||||
margin-bottom: 30rpx;
|
||||
padding: 28rpx 28rpx 24rpx;
|
||||
}
|
||||
|
||||
.user-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.user-card-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #e8e8e8;
|
||||
}
|
||||
|
||||
.user-card-subtitle {
|
||||
font-size: 22rpx;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.user-card-subtitle.status-ok {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
/* 头像+昵称行 */
|
||||
.user-profile-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 24rpx;
|
||||
padding-bottom: 24rpx;
|
||||
border-bottom: 1rpx solid #2a2a2a;
|
||||
}
|
||||
|
||||
.avatar-wrap {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
background-color: #2a2a2a;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.open-avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.open-avatar-large {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
}
|
||||
|
||||
.user-basic-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 信息列表 */
|
||||
.info-list {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 14rpx 0;
|
||||
border-bottom: 1rpx solid #222222;
|
||||
}
|
||||
|
||||
.info-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
width: 140rpx;
|
||||
font-size: 26rpx;
|
||||
color: #888888;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: #e8e8e8;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.info-value.hint {
|
||||
color: #666666;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.info-value受限 {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: #555555;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.info-status {
|
||||
font-size: 24rpx;
|
||||
margin-left: 16rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info-status.ok {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.info-status.error {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
/* 手机号按钮 */
|
||||
.phone-btn-wrap {
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.phone-btn {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(135deg, #1677ff, #0958d9);
|
||||
color: #ffffff;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
border-radius: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.phone-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.phone-btn-done {
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
color: #52c41a;
|
||||
padding: 16rpx 0;
|
||||
}
|
||||
|
||||
/* 未登录状态 */
|
||||
.not-logged-in {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.open-data-info {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
background-color: #2a2a2a;
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.not-logged-hint {
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
margin-bottom: 24rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
background: linear-gradient(135deg, #07c160 0%, #06a050 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50rpx;
|
||||
padding: 30rpx 80rpx;
|
||||
font-size: 32rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.login-tip {
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
/* 功能菜单 */
|
||||
.feature-menu {
|
||||
background: white;
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.menu-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.menu-item:active {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
margin-bottom: 10rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.menu-desc {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 系统状态 */
|
||||
.system-status {
|
||||
background: white;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.status-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.status-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.status-label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.status-value {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(135deg, #1677ff, #0958d9);
|
||||
color: #ffffff;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
border-radius: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.login-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.login-btn:active {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,10 @@ Page({
|
||||
|
||||
onShow() {
|
||||
this.loadTasks()
|
||||
// 更新自定义TabBar选中态
|
||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
||||
this.getTabBar().setData({ currentTab: 2 })
|
||||
}
|
||||
},
|
||||
|
||||
// 监听输入变化
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
{
|
||||
"navigationBarTitleText": "任务管理",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
{"navigationBarTitleText": "任务", "enablePullDownRefresh": true, "usingComponents": {"task-card": "/components/task-card/task-card"}}
|
||||
@@ -1,63 +1,70 @@
|
||||
/* 任务页面样式 */
|
||||
/* 任务页面 - 深色主题样式 */
|
||||
.task-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
background-color: #0d0d0d;
|
||||
}
|
||||
|
||||
/* 任务创建区域 */
|
||||
.task-create {
|
||||
background: white;
|
||||
background: linear-gradient(180deg, #1a1a1a 0%, #161616 100%);
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
border-radius: 20rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
|
||||
border-radius: 24rpx;
|
||||
border: 2rpx solid rgba(255,255,255,0.06);
|
||||
}
|
||||
|
||||
.input-group {
|
||||
margin-bottom: 30rpx;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 15rpx;
|
||||
font-size: 27rpx;
|
||||
color: #aaaaaa;
|
||||
margin-bottom: 14rpx;
|
||||
font-weight: 500;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
.task-input {
|
||||
width: 100%;
|
||||
border: 1rpx solid #e0e0e0;
|
||||
border-radius: 10rpx;
|
||||
padding: 20rpx;
|
||||
border: 2rpx solid #252525;
|
||||
border-radius: 14rpx;
|
||||
padding: 22rpx 24rpx;
|
||||
font-size: 28rpx;
|
||||
background-color: #f8f8f8;
|
||||
transition: border-color 0.3s;
|
||||
background-color: #141414;
|
||||
color: #ffffff;
|
||||
transition: all 0.3s ease;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.task-input:focus {
|
||||
border-color: #07c160;
|
||||
background-color: white;
|
||||
border-color: #1677FF;
|
||||
background-color: #1a1a1a;
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3rpx rgba(22,119,255,0.12);
|
||||
}
|
||||
|
||||
.task-textarea {
|
||||
width: 100%;
|
||||
min-height: 120rpx;
|
||||
border: 1rpx solid #e0e0e0;
|
||||
border-radius: 10rpx;
|
||||
padding: 20rpx;
|
||||
border: 2rpx solid #252525;
|
||||
border-radius: 14rpx;
|
||||
padding: 22rpx 24rpx;
|
||||
font-size: 28rpx;
|
||||
background-color: #f8f8f8;
|
||||
transition: border-color 0.3s;
|
||||
background-color: #141414;
|
||||
color: #ffffff;
|
||||
transition: all 0.3s ease;
|
||||
resize: vertical;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.task-textarea:focus {
|
||||
border-color: #07c160;
|
||||
background-color: white;
|
||||
outline: none;
|
||||
border-color: #1677FF;
|
||||
background-color: #1a1a1a;
|
||||
box-shadow: 0 0 0 3rpx rgba(22,119,255,0.12);
|
||||
}
|
||||
|
||||
.picker {
|
||||
@@ -65,89 +72,97 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
border: 1rpx solid #e0e0e0;
|
||||
border-radius: 10rpx;
|
||||
padding: 20rpx;
|
||||
border: 2rpx solid #252525;
|
||||
border-radius: 14rpx;
|
||||
padding: 22rpx 24rpx;
|
||||
font-size: 28rpx;
|
||||
background-color: #f8f8f8;
|
||||
transition: border-color 0.3s;
|
||||
background-color: #141414;
|
||||
color: #e5e5e5;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.picker:focus {
|
||||
border-color: #07c160;
|
||||
background-color: white;
|
||||
border-color: #1677FF;
|
||||
background-color: #1a1a1a;
|
||||
}
|
||||
|
||||
.picker-arrow {
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
color: #555555;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
background: linear-gradient(135deg, #07c160 0%, #06a050 100%);
|
||||
background: linear-gradient(135deg, #1677FF 0%, #0958d9 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50rpx;
|
||||
padding: 30rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
padding: 28rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 20rpx;
|
||||
box-shadow: 0 6rpx 24rpx rgba(22,119,255,0.3);
|
||||
}
|
||||
|
||||
.submit-btn:active {
|
||||
transform: scale(0.98);
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.submit-btn:disabled {
|
||||
background: #ccc;
|
||||
color: #999;
|
||||
background: #222222;
|
||||
color: #666666;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* 任务列表区域 */
|
||||
.task-list {
|
||||
flex: 1;
|
||||
background: white;
|
||||
border-radius: 20rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
|
||||
background: linear-gradient(180deg, #1a1a1a 0%, #151515 100%);
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
border: 2rpx solid rgba(255,255,255,0.06);
|
||||
}
|
||||
|
||||
.list-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
padding: 28rpx 32rpx;
|
||||
border-bottom: 2rpx solid rgba(255,255,255,0.06);
|
||||
}
|
||||
|
||||
.list-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.filter-buttons {
|
||||
display: flex;
|
||||
gap: 10rpx;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
padding: 10rpx 20rpx;
|
||||
border: 1rpx solid #e0e0e0;
|
||||
border: 2rpx solid #252525;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
background: white;
|
||||
color: #666;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 23rpx;
|
||||
background: transparent;
|
||||
color: #777777;
|
||||
transition: all 0.25s ease;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.filter-btn.active {
|
||||
background: #07c160;
|
||||
background: #1677FF;
|
||||
color: white;
|
||||
border-color: #07c160;
|
||||
border-color: #1677FF;
|
||||
box-shadow: 0 4rpx 12rpx rgba(22,119,255,0.25);
|
||||
}
|
||||
|
||||
.filter-btn:active {
|
||||
@@ -156,7 +171,7 @@
|
||||
|
||||
.task-scroll {
|
||||
flex: 1;
|
||||
padding: 20rpx;
|
||||
padding: 16rpx 20rpx;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
@@ -172,10 +187,11 @@
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
margin-bottom: 30rpx;
|
||||
opacity: 0.5;
|
||||
opacity: 0.25;
|
||||
filter: brightness(0) invert(1) opacity(0.3);
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
color: #555555;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ Page({
|
||||
onShow() {
|
||||
this.loadUserData()
|
||||
this.loadTaskStats()
|
||||
// "我的"页面不在TabBar中,无需设置选中态
|
||||
},
|
||||
|
||||
// 加载用户数据
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
{
|
||||
"navigationBarTitleText": "个人中心"
|
||||
}
|
||||
{"navigationBarTitleText": "我的", "usingComponents": {"user-avatar": "/components/user-avatar/user-avatar"}}
|
||||
@@ -1,19 +1,19 @@
|
||||
/* 用户中心页面样式 */
|
||||
/* 用户中心页面 - 深色主题样式 */
|
||||
.user-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
background-color: #0d0d0d;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
/* 用户信息卡片 */
|
||||
.user-card {
|
||||
background: white;
|
||||
border-radius: 20rpx;
|
||||
background: linear-gradient(145deg, #1e1e1e 0%, #1a1a1a 100%);
|
||||
border-radius: 24rpx;
|
||||
padding: 40rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 24rpx;
|
||||
border: 2rpx solid rgba(255,255,255,0.06);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -33,21 +33,24 @@
|
||||
display: block;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
color: #ffffff;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.user-id {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
color: #666666;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #07c160;
|
||||
display: inline-block;
|
||||
font-size: 22rpx;
|
||||
background-color: rgba(82,196,26,0.15);
|
||||
color: #52c41a;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 12rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -56,7 +59,7 @@
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding-top: 30rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
border-top: 2rpx solid rgba(255,255,255,0.06);
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
@@ -66,32 +69,34 @@
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 40rpx;
|
||||
font-size: 44rpx;
|
||||
font-weight: bold;
|
||||
color: #07c160;
|
||||
margin-bottom: 10rpx;
|
||||
color: #1677FF;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
/* 菜单区域 */
|
||||
.menu-section {
|
||||
background: white;
|
||||
border-radius: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
|
||||
background: linear-gradient(180deg, #1a1a1a 0%, #161616 100%);
|
||||
border-radius: 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
overflow: hidden;
|
||||
border: 2rpx solid rgba(255,255,255,0.06);
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
padding: 30rpx 30rpx 20rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #999999;
|
||||
padding: 28rpx 32rpx 18rpx;
|
||||
letter-spacing: 2rpx;
|
||||
text-transform: uppercase;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.menu-list {
|
||||
@@ -101,9 +106,9 @@
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
transition: background-color 0.3s;
|
||||
padding: 28rpx 32rpx;
|
||||
border-bottom: 2rpx solid rgba(255,255,255,0.04);
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
|
||||
.menu-item:last-child {
|
||||
@@ -111,45 +116,47 @@
|
||||
}
|
||||
|
||||
.menu-item:active {
|
||||
background-color: #f8f8f8;
|
||||
background-color: rgba(22,119,255,0.08);
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 20rpx;
|
||||
opacity: 0.7;
|
||||
margin-right: 24rpx;
|
||||
opacity: 0.65;
|
||||
filter: brightness(0) invert(1) opacity(0.7);
|
||||
}
|
||||
|
||||
.menu-text {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-size: 29rpx;
|
||||
color: #e5e5e5;
|
||||
}
|
||||
|
||||
.menu-badge {
|
||||
background: #dd524d;
|
||||
background: linear-gradient(135deg, #ff4d4f, #cf1322);
|
||||
color: white;
|
||||
font-size: 20rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 20rpx;
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 16rpx;
|
||||
margin-right: 20rpx;
|
||||
min-width: 32rpx;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.menu-arrow {
|
||||
font-size: 32rpx;
|
||||
color: #999;
|
||||
font-size: 34rpx;
|
||||
color: #555555;
|
||||
}
|
||||
|
||||
/* 系统信息 */
|
||||
.system-info {
|
||||
background: white;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
|
||||
background: linear-gradient(180deg, #1a1a1a 0%, #161616 100%);
|
||||
border-radius: 24rpx;
|
||||
padding: 30rpx 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
border: 2rpx solid rgba(255,255,255,0.06);
|
||||
}
|
||||
|
||||
.info-item {
|
||||
@@ -157,7 +164,7 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
border-bottom: 2rpx solid rgba(255,255,255,0.04);
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
@@ -165,13 +172,13 @@
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
font-size: 27rpx;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-size: 27rpx;
|
||||
color: #cccccc;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -180,7 +187,7 @@
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-top: auto;
|
||||
padding-top: 40rpx;
|
||||
padding-top: 30rpx;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
@@ -188,36 +195,41 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 30rpx 20rpx;
|
||||
justify-content: center;
|
||||
padding: 28rpx 16rpx;
|
||||
border: none;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
transition: all 0.3s ease;
|
||||
min-height: 120rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.action-btn.primary {
|
||||
background: linear-gradient(135deg, #07c160 0%, #06a050 100%);
|
||||
background: linear-gradient(135deg, #1677FF 0%, #0958d9 100%);
|
||||
color: white;
|
||||
box-shadow: 0 6rpx 20rpx rgba(22,119,255,0.3);
|
||||
}
|
||||
|
||||
.action-btn.secondary {
|
||||
background: linear-gradient(135deg, #17a2b8 0%, #138496 100%);
|
||||
color: white;
|
||||
background: linear-gradient(135deg, #2f2f2f 0%, #262626 100%);
|
||||
color: #aaaaaa;
|
||||
border: 2rpx solid rgba(255,255,255,0.08);
|
||||
}
|
||||
|
||||
.action-btn.danger {
|
||||
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
|
||||
background: linear-gradient(135deg, #ff4d4f 0%, #cf1322 100%);
|
||||
color: white;
|
||||
box-shadow: 0 6rpx 20rpx rgba(255,77,79,0.3);
|
||||
}
|
||||
|
||||
.action-btn:active {
|
||||
transform: scale(0.98);
|
||||
transform: scale(0.97);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.action-btn image {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
width: 38rpx;
|
||||
height: 38rpx;
|
||||
margin-bottom: 10rpx;
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user