Claw 项目完整结构提交
This commit is contained in:
163
Claw/client/wechat_app/components/message/message.js
Normal file
163
Claw/client/wechat_app/components/message/message.js
Normal file
@@ -0,0 +1,163 @@
|
||||
// 消息组件逻辑
|
||||
Component({
|
||||
properties: {
|
||||
// 消息内容
|
||||
content: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 消息类型
|
||||
type: {
|
||||
type: String,
|
||||
value: 'text'
|
||||
},
|
||||
// 发送者头像
|
||||
avatar: {
|
||||
type: String,
|
||||
value: '/assets/images/default-avatar.png'
|
||||
},
|
||||
// 发送者昵称
|
||||
nickname: {
|
||||
type: String,
|
||||
value: '未知用户'
|
||||
},
|
||||
// 发送时间
|
||||
time: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 是否是自己发送的消息
|
||||
isMe: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
// 文件名(文件消息)
|
||||
fileName: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 文件大小(文件消息)
|
||||
fileSize: {
|
||||
type: String,
|
||||
value: ''
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 预览图片
|
||||
previewImage() {
|
||||
if (this.properties.type === 'image') {
|
||||
wx.previewImage({
|
||||
urls: [this.properties.content],
|
||||
current: this.properties.content
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 下载文件
|
||||
downloadFile() {
|
||||
if (this.properties.type === 'file') {
|
||||
wx.showLoading({
|
||||
title: '下载中...'
|
||||
})
|
||||
|
||||
wx.downloadFile({
|
||||
url: this.properties.content,
|
||||
success: (res) => {
|
||||
wx.hideLoading()
|
||||
|
||||
if (res.statusCode === 200) {
|
||||
// 保存文件到本地
|
||||
wx.saveFile({
|
||||
tempFilePath: res.tempFilePath,
|
||||
success: (saveRes) => {
|
||||
wx.showToast({
|
||||
title: '文件已保存',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
// 打开文件
|
||||
wx.openDocument({
|
||||
filePath: saveRes.savedFilePath,
|
||||
showMenu: true
|
||||
})
|
||||
},
|
||||
fail: () => {
|
||||
wx.showToast({
|
||||
title: '保存失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
wx.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
wx.hideLoading()
|
||||
wx.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 长按消息
|
||||
onLongPress() {
|
||||
wx.showActionSheet({
|
||||
itemList: ['复制', '转发', '删除'],
|
||||
success: (res) => {
|
||||
switch (res.tapIndex) {
|
||||
case 0: // 复制
|
||||
this.copyMessage()
|
||||
break
|
||||
case 1: // 转发
|
||||
this.forwardMessage()
|
||||
break
|
||||
case 2: // 删除
|
||||
this.deleteMessage()
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 复制消息
|
||||
copyMessage() {
|
||||
if (this.properties.type === 'text') {
|
||||
wx.setClipboardData({
|
||||
data: this.properties.content,
|
||||
success: () => {
|
||||
wx.showToast({
|
||||
title: '已复制',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 转发消息
|
||||
forwardMessage() {
|
||||
this.triggerEvent('forward', {
|
||||
content: this.properties.content,
|
||||
type: this.properties.type,
|
||||
fileName: this.properties.fileName,
|
||||
fileSize: this.properties.fileSize
|
||||
})
|
||||
},
|
||||
|
||||
// 删除消息
|
||||
deleteMessage() {
|
||||
this.triggerEvent('delete', {
|
||||
content: this.properties.content,
|
||||
type: this.properties.type
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
3
Claw/client/wechat_app/components/message/message.json
Normal file
3
Claw/client/wechat_app/components/message/message.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"component": true
|
||||
}
|
||||
20
Claw/client/wechat_app/components/message/message.wxml
Normal file
20
Claw/client/wechat_app/components/message/message.wxml
Normal file
@@ -0,0 +1,20 @@
|
||||
<!-- 消息组件 -->
|
||||
<view class="message-item {{isMe ? 'message-right' : 'message-left'}}">
|
||||
<view class="message-avatar">
|
||||
<image src="{{avatar}}" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="message-content">
|
||||
<view class="message-header">
|
||||
<text class="message-nickname">{{nickname}}</text>
|
||||
<text class="message-time">{{time}}</text>
|
||||
</view>
|
||||
<view class="message-body">
|
||||
<text class="message-text" wx:if="{{type === 'text'}}">{{content}}</text>
|
||||
<image class="message-image" wx:if="{{type === 'image'}}" src="{{content}}" mode="widthFix" bindtap="previewImage"></image>
|
||||
<view class="message-file" wx:if="{{type === 'file'}}" bindtap="downloadFile">
|
||||
<text class="file-name">{{fileName}}</text>
|
||||
<text class="file-size">{{fileSize}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
98
Claw/client/wechat_app/components/message/message.wxss
Normal file
98
Claw/client/wechat_app/components/message/message.wxss
Normal file
@@ -0,0 +1,98 @@
|
||||
/* 消息组件样式 */
|
||||
.message-item {
|
||||
display: flex;
|
||||
margin: 20rpx 0;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.message-left {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.message-right {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.message-avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
margin: 0 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.message-avatar image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
max-width: 70%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.message-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.message-nickname {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
font-size: 20rpx;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.message-body {
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 10rpx;
|
||||
padding: 20rpx;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.message-right .message-body {
|
||||
background-color: #95ec69;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.message-right .message-text {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.message-image {
|
||||
max-width: 300rpx;
|
||||
max-height: 300rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.message-file {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #fff;
|
||||
border: 1rpx solid #e0e0e0;
|
||||
border-radius: 10rpx;
|
||||
padding: 20rpx;
|
||||
min-width: 200rpx;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.file-size {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
143
Claw/client/wechat_app/components/task-card/task-card.js
Normal file
143
Claw/client/wechat_app/components/task-card/task-card.js
Normal file
@@ -0,0 +1,143 @@
|
||||
// 任务卡片组件逻辑
|
||||
const { TASK_STATUS } = require('../../utils/constant.js')
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
// 任务ID
|
||||
taskId: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 任务标题
|
||||
title: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 任务描述
|
||||
description: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 任务类型
|
||||
type: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 任务状态
|
||||
status: {
|
||||
type: String,
|
||||
value: TASK_STATUS.PENDING
|
||||
},
|
||||
// 优先级
|
||||
priority: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 创建时间
|
||||
createdAt: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 更新时间
|
||||
updatedAt: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 处理结果
|
||||
result: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 进度(0-100)
|
||||
progress: {
|
||||
type: Number,
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
|
||||
data: {
|
||||
statusText: ''
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() {
|
||||
this.updateStatusText()
|
||||
}
|
||||
},
|
||||
|
||||
observers: {
|
||||
'status': function(status) {
|
||||
this.updateStatusText()
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 更新状态文本
|
||||
updateStatusText() {
|
||||
const statusMap = {
|
||||
[TASK_STATUS.PENDING]: '待处理',
|
||||
[TASK_STATUS.PROCESSING]: '处理中',
|
||||
[TASK_STATUS.COMPLETED]: '已完成',
|
||||
[TASK_STATUS.FAILED]: '处理失败',
|
||||
[TASK_STATUS.CANCELLED]: '已取消'
|
||||
}
|
||||
|
||||
this.setData({
|
||||
statusText: statusMap[this.properties.status] || '未知状态'
|
||||
})
|
||||
},
|
||||
|
||||
// 开始处理任务
|
||||
startTask() {
|
||||
this.triggerEvent('start', {
|
||||
taskId: this.properties.taskId,
|
||||
title: this.properties.title
|
||||
})
|
||||
},
|
||||
|
||||
// 完成任务
|
||||
completeTask() {
|
||||
this.triggerEvent('complete', {
|
||||
taskId: this.properties.taskId,
|
||||
title: this.properties.title
|
||||
})
|
||||
},
|
||||
|
||||
// 重试任务
|
||||
retryTask() {
|
||||
this.triggerEvent('retry', {
|
||||
taskId: this.properties.taskId,
|
||||
title: this.properties.title
|
||||
})
|
||||
},
|
||||
|
||||
// 查看任务详情
|
||||
viewDetails() {
|
||||
this.triggerEvent('detail', {
|
||||
taskId: this.properties.taskId,
|
||||
title: this.properties.title,
|
||||
description: this.properties.description,
|
||||
status: this.properties.status,
|
||||
result: this.properties.result,
|
||||
createdAt: this.properties.createdAt,
|
||||
updatedAt: this.properties.updatedAt
|
||||
})
|
||||
},
|
||||
|
||||
// 取消任务
|
||||
cancelTask() {
|
||||
wx.showModal({
|
||||
title: '确认取消',
|
||||
content: `确定要取消任务"${this.properties.title}"吗?`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.triggerEvent('cancel', {
|
||||
taskId: this.properties.taskId,
|
||||
title: this.properties.title
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"component": true
|
||||
}
|
||||
39
Claw/client/wechat_app/components/task-card/task-card.wxml
Normal file
39
Claw/client/wechat_app/components/task-card/task-card.wxml
Normal file
@@ -0,0 +1,39 @@
|
||||
<!-- 任务卡片组件 -->
|
||||
<view class="task-card {{status}}">
|
||||
<view class="task-header">
|
||||
<text class="task-title">{{title}}</text>
|
||||
<text class="task-status {{status}}">{{statusText}}</text>
|
||||
</view>
|
||||
|
||||
<view class="task-content">
|
||||
<text class="task-description">{{description}}</text>
|
||||
|
||||
<view class="task-meta" wx:if="{{type}}">
|
||||
<text class="task-type">类型:{{type}}</text>
|
||||
<text class="task-priority" wx:if="{{priority}}">优先级:{{priority}}</text>
|
||||
</view>
|
||||
|
||||
<view class="task-timeline" wx:if="{{createdAt}}">
|
||||
<text class="task-time">创建时间:{{createdAt}}</text>
|
||||
<text class="task-time" wx:if="{{updatedAt}}">更新时间:{{updatedAt}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="task-actions">
|
||||
<button class="action-btn primary" wx:if="{{status === 'pending'}}" bindtap="startTask">开始处理</button>
|
||||
<button class="action-btn success" wx:if="{{status === 'processing'}}" bindtap="completeTask">标记完成</button>
|
||||
<button class="action-btn warning" wx:if="{{status === 'failed'}}" bindtap="retryTask">重试</button>
|
||||
<button class="action-btn info" bindtap="viewDetails">查看详情</button>
|
||||
<button class="action-btn danger" wx:if="{{status !== 'completed'}}" bindtap="cancelTask">取消</button>
|
||||
</view>
|
||||
|
||||
<view class="task-progress" wx:if="{{status === 'processing' && progress}}">
|
||||
<progress percent="{{progress}}" stroke-width="6" activeColor="#07c160" backgroundColor="#f0f0f0"/>
|
||||
<text class="progress-text">{{progress}}%</text>
|
||||
</view>
|
||||
|
||||
<view class="task-result" wx:if="{{result}}">
|
||||
<text class="result-label">处理结果:</text>
|
||||
<text class="result-content">{{result}}</text>
|
||||
</view>
|
||||
</view>
|
||||
195
Claw/client/wechat_app/components/task-card/task-card.wxss
Normal file
195
Claw/client/wechat_app/components/task-card/task-card.wxss
Normal file
@@ -0,0 +1,195 @@
|
||||
/* 任务卡片组件样式 */
|
||||
.task-card {
|
||||
background: white;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
margin: 20rpx 0;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
|
||||
border-left: 8rpx solid #e0e0e0;
|
||||
}
|
||||
|
||||
.task-card.pending {
|
||||
border-left-color: #f0ad4e;
|
||||
}
|
||||
|
||||
.task-card.processing {
|
||||
border-left-color: #10aeff;
|
||||
}
|
||||
|
||||
.task-card.completed {
|
||||
border-left-color: #07c160;
|
||||
}
|
||||
|
||||
.task-card.failed {
|
||||
border-left-color: #dd524d;
|
||||
}
|
||||
|
||||
.task-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.task-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.task-status {
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.task-status.pending {
|
||||
background-color: #fff3cd;
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
.task-status.processing {
|
||||
background-color: #cce5ff;
|
||||
color: #004085;
|
||||
}
|
||||
|
||||
.task-status.completed {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
.task-status.failed {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
.task-content {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.task-description {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.task-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.task-type,
|
||||
.task-priority {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
background: #f8f8f8;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.task-timeline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.task-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.task-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15rpx;
|
||||
margin-top: 20rpx;
|
||||
padding-top: 20rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 16rpx 32rpx;
|
||||
border: none;
|
||||
border-radius: 25rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
min-width: 120rpx;
|
||||
}
|
||||
|
||||
.action-btn.primary {
|
||||
background: linear-gradient(135deg, #07c160 0%, #06a050 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.action-btn.success {
|
||||
background: linear-gradient(135deg, #28a745 0%, #218838 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.action-btn.warning {
|
||||
background: linear-gradient(135deg, #ffc107 0%, #e0a800 100%);
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.action-btn.info {
|
||||
background: linear-gradient(135deg, #17a2b8 0%, #138496 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.action-btn.danger {
|
||||
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.action-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.task-progress {
|
||||
margin: 20rpx 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
min-width: 60rpx;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.task-result {
|
||||
background: #f8f9fa;
|
||||
border-radius: 10rpx;
|
||||
padding: 20rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.result-label {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
background: white;
|
||||
padding: 15rpx;
|
||||
border-radius: 8rpx;
|
||||
border: 1rpx solid #e9ecef;
|
||||
}
|
||||
117
Claw/client/wechat_app/components/user-avatar/user-avatar.js
Normal file
117
Claw/client/wechat_app/components/user-avatar/user-avatar.js
Normal file
@@ -0,0 +1,117 @@
|
||||
// 用户头像组件逻辑
|
||||
const { DEFAULT_AVATAR } = require('../../utils/constant.js')
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
// 头像图片地址
|
||||
src: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 默认头像
|
||||
defaultAvatar: {
|
||||
type: String,
|
||||
value: DEFAULT_AVATAR
|
||||
},
|
||||
// 尺寸:small, medium, large, xlarge
|
||||
size: {
|
||||
type: String,
|
||||
value: 'medium'
|
||||
},
|
||||
// 形状:circle, rounded, square
|
||||
shape: {
|
||||
type: String,
|
||||
value: 'circle'
|
||||
},
|
||||
// 图片裁剪模式
|
||||
mode: {
|
||||
type: String,
|
||||
value: 'aspectFill'
|
||||
},
|
||||
// 是否懒加载
|
||||
lazyLoad: {
|
||||
type: Boolean,
|
||||
value: true
|
||||
},
|
||||
// 是否显示在线状态
|
||||
showStatus: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
// 在线状态:online, offline, busy, away
|
||||
status: {
|
||||
type: String,
|
||||
value: 'offline'
|
||||
},
|
||||
// 徽章数量
|
||||
badge: {
|
||||
type: Number,
|
||||
value: 0
|
||||
},
|
||||
// 是否显示加载状态
|
||||
loading: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
// 自定义样式
|
||||
customStyle: {
|
||||
type: String,
|
||||
value: ''
|
||||
}
|
||||
},
|
||||
|
||||
data: {
|
||||
imageLoaded: false,
|
||||
imageError: false
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 图片加载成功
|
||||
onImageLoad() {
|
||||
this.setData({
|
||||
imageLoaded: true,
|
||||
imageError: false
|
||||
})
|
||||
this.triggerEvent('load')
|
||||
},
|
||||
|
||||
// 图片加载失败
|
||||
onImageError(e) {
|
||||
console.error('头像加载失败:', e)
|
||||
this.setData({
|
||||
imageLoaded: false,
|
||||
imageError: true
|
||||
})
|
||||
this.triggerEvent('error', e)
|
||||
},
|
||||
|
||||
// 点击头像
|
||||
onAvatarTap() {
|
||||
this.triggerEvent('tap', {
|
||||
src: this.properties.src,
|
||||
status: this.properties.status,
|
||||
badge: this.properties.badge
|
||||
})
|
||||
},
|
||||
|
||||
// 长按头像
|
||||
onAvatarLongPress() {
|
||||
this.triggerEvent('longpress', {
|
||||
src: this.properties.src,
|
||||
status: this.properties.status,
|
||||
badge: this.properties.badge
|
||||
})
|
||||
},
|
||||
|
||||
// 获取头像状态文本
|
||||
getStatusText() {
|
||||
const statusMap = {
|
||||
online: '在线',
|
||||
offline: '离线',
|
||||
busy: '忙碌',
|
||||
away: '离开'
|
||||
}
|
||||
return statusMap[this.properties.status] || '未知'
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"component": true
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<!-- 用户头像组件 -->
|
||||
<view class="user-avatar {{size}} {{shape}}" style="{{customStyle}}">
|
||||
<image
|
||||
class="avatar-image"
|
||||
src="{{src || defaultAvatar}}"
|
||||
mode="{{mode}}"
|
||||
lazy-load="{{lazyLoad}}"
|
||||
bindload="onImageLoad"
|
||||
binderror="onImageError"
|
||||
></image>
|
||||
|
||||
<!-- 在线状态指示器 -->
|
||||
<view class="status-indicator {{status}}" wx:if="{{showStatus}}"></view>
|
||||
|
||||
<!-- 徽章/未读消息数 -->
|
||||
<view class="badge" wx:if="{{badge > 0}}">
|
||||
<text class="badge-text">{{badge > 99 ? '99+' : badge}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view class="loading-overlay" wx:if="{{loading}}">
|
||||
<view class="loading-spinner"></view>
|
||||
</view>
|
||||
</view>
|
||||
143
Claw/client/wechat_app/components/user-avatar/user-avatar.wxss
Normal file
143
Claw/client/wechat_app/components/user-avatar/user-avatar.wxss
Normal file
@@ -0,0 +1,143 @@
|
||||
/* 用户头像组件样式 */
|
||||
.user-avatar {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 尺寸样式 */
|
||||
.user-avatar.small {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
}
|
||||
|
||||
.user-avatar.medium {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.user-avatar.large {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
}
|
||||
|
||||
.user-avatar.xlarge {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
/* 形状样式 */
|
||||
.user-avatar.circle {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.user-avatar.rounded {
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.user-avatar.square {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* 头像图片 */
|
||||
.avatar-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 在线状态指示器 */
|
||||
.status-indicator {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
border-radius: 50%;
|
||||
border: 4rpx solid white;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.status-indicator.online {
|
||||
background-color: #07c160;
|
||||
}
|
||||
|
||||
.status-indicator.offline {
|
||||
background-color: #999;
|
||||
}
|
||||
|
||||
.status-indicator.busy {
|
||||
background-color: #f0ad4e;
|
||||
}
|
||||
|
||||
.status-indicator.away {
|
||||
background-color: #10aeff;
|
||||
}
|
||||
|
||||
/* 徽章 */
|
||||
.badge {
|
||||
position: absolute;
|
||||
top: -10rpx;
|
||||
right: -10rpx;
|
||||
background-color: #dd524d;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
min-width: 32rpx;
|
||||
height: 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20rpx;
|
||||
font-weight: bold;
|
||||
padding: 0 8rpx;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.badge-text {
|
||||
font-size: 20rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* 加载遮罩 */
|
||||
.loading-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border: 4rpx solid #f3f3f3;
|
||||
border-top: 4rpx solid #07c160;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 默认头像样式 */
|
||||
.user-avatar::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(135deg, #07c160 0%, #06a050 100%);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.user-avatar.error::before {
|
||||
background: linear-gradient(135deg, #dd524d 0%, #c82333 100%);
|
||||
}
|
||||
Reference in New Issue
Block a user