地址路径修改
This commit is contained in:
257
miniprogram/components/avatar/avatar.js
Normal file
257
miniprogram/components/avatar/avatar.js
Normal file
@@ -0,0 +1,257 @@
|
||||
// 头像组件逻辑
|
||||
Component({
|
||||
properties: {
|
||||
// 头像URL
|
||||
avatarUrl: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
// 头像尺寸(rpx)
|
||||
size: {
|
||||
type: Number,
|
||||
value: 120
|
||||
},
|
||||
// 占位符文本
|
||||
placeholderText: {
|
||||
type: String,
|
||||
value: '👤'
|
||||
},
|
||||
// 占位符文本大小
|
||||
textSize: {
|
||||
type: Number,
|
||||
value: 40
|
||||
},
|
||||
// 是否可编辑
|
||||
editable: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
// 头像形状:circle(圆形)或 square(方形)
|
||||
shape: {
|
||||
type: String,
|
||||
value: 'circle'
|
||||
},
|
||||
// 是否显示边框
|
||||
bordered: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
// 是否显示阴影
|
||||
shadow: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
}
|
||||
},
|
||||
|
||||
data: {
|
||||
showAvatarModal: false // 是否显示头像选择弹窗
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 头像点击事件
|
||||
onAvatarTap() {
|
||||
if (this.properties.editable) {
|
||||
this.showAvatarModal();
|
||||
}
|
||||
this.triggerEvent('tap', {
|
||||
avatarUrl: this.properties.avatarUrl
|
||||
});
|
||||
},
|
||||
|
||||
// 显示头像选择弹窗
|
||||
showAvatarModal() {
|
||||
this.setData({
|
||||
showAvatarModal: true
|
||||
});
|
||||
},
|
||||
|
||||
// 隐藏头像选择弹窗
|
||||
hideAvatarModal() {
|
||||
this.setData({
|
||||
showAvatarModal: false
|
||||
});
|
||||
},
|
||||
|
||||
// 拍照
|
||||
takePhoto() {
|
||||
this.hideAvatarModal();
|
||||
|
||||
wx.chooseMedia({
|
||||
count: 1,
|
||||
mediaType: ['image'],
|
||||
sourceType: ['camera'],
|
||||
camera: 'front',
|
||||
success: (res) => {
|
||||
if (res.tempFiles && res.tempFiles.length > 0) {
|
||||
this.processImage(res.tempFiles[0].tempFilePath);
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('拍照失败:', err);
|
||||
wx.showToast({
|
||||
title: '拍照失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 从相册选择
|
||||
chooseImage() {
|
||||
this.hideAvatarModal();
|
||||
|
||||
wx.chooseMedia({
|
||||
count: 1,
|
||||
mediaType: ['image'],
|
||||
sourceType: ['album'],
|
||||
success: (res) => {
|
||||
if (res.tempFiles && res.tempFiles.length > 0) {
|
||||
this.processImage(res.tempFiles[0].tempFilePath);
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('选择图片失败:', err);
|
||||
wx.showToast({
|
||||
title: '选择图片失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 使用默认头像
|
||||
useDefaultAvatar() {
|
||||
this.hideAvatarModal();
|
||||
|
||||
// 触发默认头像选择事件
|
||||
this.triggerEvent('change', {
|
||||
avatarUrl: '',
|
||||
isDefault: true
|
||||
});
|
||||
},
|
||||
|
||||
// 处理图片
|
||||
processImage(tempFilePath) {
|
||||
wx.showLoading({
|
||||
title: '处理中...'
|
||||
});
|
||||
|
||||
// 压缩图片
|
||||
wx.compressImage({
|
||||
src: tempFilePath,
|
||||
quality: 80,
|
||||
success: (res) => {
|
||||
// 裁剪图片为正方形
|
||||
this.cropImage(res.tempFilePath);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('图片压缩失败:', err);
|
||||
wx.hideLoading();
|
||||
wx.showToast({
|
||||
title: '图片处理失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 裁剪图片
|
||||
cropImage(tempFilePath) {
|
||||
// 获取图片信息
|
||||
wx.getImageInfo({
|
||||
src: tempFilePath,
|
||||
success: (infoRes) => {
|
||||
const { width, height } = infoRes;
|
||||
const size = Math.min(width, height);
|
||||
const x = (width - size) / 2;
|
||||
const y = (height - size) / 2;
|
||||
|
||||
// 创建画布进行裁剪
|
||||
const ctx = wx.createCanvasContext('avatar-canvas');
|
||||
|
||||
// 绘制圆形裁剪区域
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI);
|
||||
ctx.clip();
|
||||
|
||||
// 绘制图片
|
||||
ctx.drawImage(tempFilePath, x, y, size, size, 0, 0, size, size);
|
||||
ctx.restore();
|
||||
|
||||
// 将画布内容导出为图片
|
||||
ctx.draw(false, () => {
|
||||
wx.canvasToTempFilePath({
|
||||
canvasId: 'avatar-canvas',
|
||||
success: (canvasRes) => {
|
||||
wx.hideLoading();
|
||||
|
||||
// 触发头像改变事件
|
||||
this.triggerEvent('change', {
|
||||
avatarUrl: canvasRes.tempFilePath,
|
||||
isDefault: false
|
||||
});
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('画布导出失败:', err);
|
||||
wx.hideLoading();
|
||||
wx.showToast({
|
||||
title: '头像处理失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('获取图片信息失败:', err);
|
||||
wx.hideLoading();
|
||||
wx.showToast({
|
||||
title: '图片处理失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 生成头像缩略图
|
||||
generateThumbnail(avatarUrl, size = 60) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!avatarUrl) {
|
||||
resolve('');
|
||||
return;
|
||||
}
|
||||
|
||||
wx.getImageInfo({
|
||||
src: avatarUrl,
|
||||
success: (infoRes) => {
|
||||
const ctx = wx.createCanvasContext('thumbnail-canvas');
|
||||
|
||||
// 绘制圆形缩略图
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI);
|
||||
ctx.clip();
|
||||
ctx.drawImage(avatarUrl, 0, 0, size, size);
|
||||
ctx.restore();
|
||||
|
||||
ctx.draw(false, () => {
|
||||
wx.canvasToTempFilePath({
|
||||
canvasId: 'thumbnail-canvas',
|
||||
destWidth: size,
|
||||
destHeight: size,
|
||||
success: (res) => {
|
||||
resolve(res.tempFilePath);
|
||||
},
|
||||
fail: (err) => {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
4
miniprogram/components/avatar/avatar.json
Normal file
4
miniprogram/components/avatar/avatar.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
52
miniprogram/components/avatar/avatar.wxml
Normal file
52
miniprogram/components/avatar/avatar.wxml
Normal file
@@ -0,0 +1,52 @@
|
||||
<!-- 头像组件 -->
|
||||
<view class="avatar-container">
|
||||
<!-- 头像显示区域 -->
|
||||
<view class="avatar-display" bindtap="onAvatarTap">
|
||||
<view wx:if="{{avatarUrl}}" class="avatar-image-wrapper">
|
||||
<image
|
||||
class="avatar-image"
|
||||
src="{{avatarUrl}}"
|
||||
mode="aspectFill"
|
||||
style="width: {{size}}rpx; height: {{size}}rpx;"
|
||||
></image>
|
||||
</view>
|
||||
<view wx:else class="avatar-placeholder">
|
||||
<text class="avatar-text" style="font-size: {{textSize}}rpx;">{{placeholderText}}</text>
|
||||
</view>
|
||||
<!-- 编辑图标 -->
|
||||
<view wx:if="{{editable}}" class="avatar-edit-icon">
|
||||
<text class="edit-icon">✏️</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 头像选择弹窗 -->
|
||||
<view wx:if="{{showAvatarModal}}" class="avatar-modal">
|
||||
<view class="modal-mask" bindtap="hideAvatarModal"></view>
|
||||
<view class="modal-content">
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">选择头像</text>
|
||||
<text class="modal-close" bindtap="hideAvatarModal">×</text>
|
||||
</view>
|
||||
|
||||
<view class="avatar-options">
|
||||
<!-- 拍照 -->
|
||||
<view class="avatar-option" bindtap="takePhoto">
|
||||
<text class="option-icon">📷</text>
|
||||
<text class="option-text">拍照</text>
|
||||
</view>
|
||||
|
||||
<!-- 从相册选择 -->
|
||||
<view class="avatar-option" bindtap="chooseImage">
|
||||
<text class="option-icon">🖼️</text>
|
||||
<text class="option-text">从相册选择</text>
|
||||
</view>
|
||||
|
||||
<!-- 默认头像 -->
|
||||
<view class="avatar-option" bindtap="useDefaultAvatar">
|
||||
<text class="option-icon">👤</text>
|
||||
<text class="option-text">使用默认头像</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
204
miniprogram/components/avatar/avatar.wxss
Normal file
204
miniprogram/components/avatar/avatar.wxss
Normal file
@@ -0,0 +1,204 @@
|
||||
/* 头像组件样式 */
|
||||
.avatar-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* 头像显示区域 */
|
||||
.avatar-display {
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
background-color: #f0f0f0;
|
||||
border: 2px solid #e0e0e0;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.avatar-display:active {
|
||||
transform: scale(0.95);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 头像图片容器 */
|
||||
.avatar-image-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 头像图片 */
|
||||
.avatar-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* 头像占位符 */
|
||||
.avatar-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.avatar-text {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* 编辑图标 */
|
||||
.avatar-edit-icon {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border-radius: 50%;
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transform: translate(25%, 25%);
|
||||
}
|
||||
|
||||
.edit-icon {
|
||||
font-size: 12rpx;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* 头像选择弹窗 */
|
||||
.avatar-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.modal-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background: white;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
padding: 40rpx;
|
||||
box-sizing: border-box;
|
||||
animation: slideUp 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
font-size: 40rpx;
|
||||
color: #999;
|
||||
padding: 10rpx;
|
||||
}
|
||||
|
||||
/* 头像选项 */
|
||||
.avatar-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30rpx;
|
||||
}
|
||||
|
||||
.avatar-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
background: #f8f9fa;
|
||||
border-radius: 15rpx;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.avatar-option:active {
|
||||
background: #e9ecef;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.option-icon {
|
||||
font-size: 40rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.option-text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 不同尺寸的头像样式 */
|
||||
.avatar-small {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.avatar-medium {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
}
|
||||
|
||||
.avatar-large {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
}
|
||||
|
||||
.avatar-xlarge {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
}
|
||||
|
||||
/* 圆形头像样式 */
|
||||
.avatar-circle {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* 方形头像样式 */
|
||||
.avatar-square {
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
|
||||
/* 带边框的头像 */
|
||||
.avatar-bordered {
|
||||
border: 3rpx solid #667eea;
|
||||
}
|
||||
|
||||
/* 阴影效果 */
|
||||
.avatar-shadow {
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
Reference in New Issue
Block a user