Files
lucky_shop/pages_tool/ai-chat/components/ai-chat-avatar-selector.vue
ZF sun 556ce9d132 refactor(ai-chat): 重构AI聊天国际化文件结构
- 将 ai-chat 语言文件从 ai/ 迁移至 ai-chat/ 目录
- 重构消息组件及多个子组件
2026-05-18 15:36:13 +08:00

227 lines
5.7 KiB
Vue

<template>
<view class="avatar-selector-popup" v-if="visible">
<view class="avatar-selector-mask" @click="close"></view>
<view class="avatar-selector-panel">
<view class="avatar-selector-header">
<text>{{ $lang('avatarSelectorTitle') }}</text>
<button class="close-btn" @click="close">
<text class="iconfont icon-close"></text>
</button>
</view>
<view class="avatar-selector-content">
<view class="current-avatar-preview">
<image :src="avatar" mode="aspectFill" show-menu-by-longpress />
</view>
<view class="avatar-selector-actions">
<button class="avatar-select-action" @click="chooseAvatarFromAlbum">
<text class="iconfont icon-album"></text>
<text>{{ $lang('chooseFromAlbum') }}</text>
</button>
<button class="avatar-select-action cancel" @click="close">
<text>{{ $lang('cancel') }}</text>
</button>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* ai-chat-avatar-selector - 头像选择弹窗组件
*
* Props:
* visible: Boolean - 控制显示/隐藏
* avatar: String - 当前头像 URL
*
* Events:
* update:visible - 同步关闭状态
* avatar-change - 头像变更,参数:新头像 URL
*/
export default {
name: 'AiChatAvatarSelector',
props: {
visible: {
type: Boolean,
default: false
},
avatar: {
type: String,
default: ''
}
},
methods: {
close() {
this.$emit('update:visible', false)
},
chooseAvatarFromAlbum() {
this.selectAvatar()
},
selectAvatar() {
// #ifdef H5
const input = document.createElement('input')
input.type = 'file'
input.accept = 'image/*'
input.style.display = 'none'
input.onchange = (e) => {
const file = e.target.files[0]
if (file) {
const tempFilePath = URL.createObjectURL(file)
const reader = new FileReader()
reader.onload = (e) => {
const base64 = e.target.result
uni.setStorageSync('user_avatar', base64)
}
reader.readAsDataURL(file)
this.$emit('avatar-change', tempFilePath)
this.close()
uni.showToast({ title: this.$lang('avatarChangeSuccess'), icon: 'success', duration: 1500 })
} else {
this.close()
}
}
input.click()
// #endif
// #ifdef MP-WEIXIN
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album'],
success: (res) => {
const tempFilePath = res.tempFilePaths[0]
uni.setStorageSync('user_avatar', tempFilePath)
this.$emit('avatar-change', tempFilePath)
this.close()
uni.showToast({ title: this.$lang('avatarChangeSuccess'), icon: 'success', duration: 1500 })
},
fail: (error) => {
if (error.errMsg && error.errMsg.includes('deny')) {
uni.showToast({ title: this.$lang('enableAlbumPermission'), icon: 'none', duration: 2000 })
}
this.close()
}
})
// #endif
}
}
}
</script>
<style lang="scss" scoped>
/* 引入图标字体 */
@import url('/common/css/iconfont.css');
/* 引入全局变量 */
@import '../styles/ai-chat-global.scss';
.avatar-selector-popup {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
.avatar-selector-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.4);
}
.avatar-selector-panel {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: $bg-card;
border-radius: $radius-lg;
padding: 32rpx;
width: calc(100% - 80rpx);
max-width: 520rpx;
box-shadow: 0 8rpx 36rpx rgba(0, 0, 0, 0.15);
box-sizing: border-box;
.avatar-selector-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
border-bottom: 2rpx solid #f0f4f8;
padding-bottom: 16rpx;
text {
font-size: 34rpx;
font-weight: 600;
color: $color-text;
}
.close-btn {
width: 56rpx;
height: 56rpx;
border-radius: 50%;
background-color: $bg-main;
display: flex;
align-items: center;
justify-content: center;
border: none;
.iconfont {
font-size: 28rpx;
color: $color-text-light;
}
}
}
.avatar-selector-content {
.current-avatar-preview {
width: 180rpx;
height: 180rpx;
border-radius: 50%;
overflow: hidden;
margin: 0 auto 32rpx;
box-shadow: $shadow-md;
border: 4rpx solid #f0f4f8;
image {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.avatar-selector-actions {
display: flex;
flex-direction: column;
gap: 16rpx;
.avatar-select-action {
padding: 24rpx;
border-radius: $radius-lg;
font-size: 28rpx;
display: flex;
align-items: center;
gap: 16rpx;
border: 2rpx solid #f0f4f8;
background-color: $bg-main;
color: $color-text;
&.cancel {
background-color: #f8fafc;
border-color: #e9edf2;
}
}
}
}
}
}
</style>