Files
jurong_circle_frontdesk/src/views/EditDetailsPage.vue
2025-09-10 16:42:48 +08:00

636 lines
18 KiB
Vue

<template>
<div class="profile-edit-page">
<!-- 导航栏 -->
<nav class="navbar">
<div class="nav-left">
<router-link to="/myprofile" class="back-btn">
<el-icon><ArrowLeft /></el-icon>
返回
</router-link>
</div>
<div class="nav-center">
<h1 class="nav-title">编辑个人资料</h1>
</div>
<div class="nav-right"></div>
</nav>
<!-- 个人信息 -->
<div class="profile-content">
<!-- 个人资料表单 -->
<div class="profile-form">
<el-form
ref="profileFormRef"
:model="form"
:rules="rules"
label-width="80px"
>
<el-form-item label="用户名" prop="username">
<el-input v-model="form.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item label="昵称" prop="nickname">
<el-input v-model="form.nickname" placeholder="请输入昵称" />
</el-form-item>
<el-form-item label="真实姓名" prop="realName">
<el-input v-model="form.realName" placeholder="请输入真实姓名" />
</el-form-item>
<el-form-item label="身份证号" prop="idCard">
<el-input v-model="form.idCard" placeholder="请输入身份证号" maxlength="18" />
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model="form.phone" placeholder="请输入手机号" maxlength="11" />
</el-form-item>
<el-form-item label="微信收款码" prop="wechatQr">
<div class="qr-upload-container">
<el-upload
class="qr-uploader"
:action="uploadUrl"
:headers="uploadHeaders"
:before-upload="beforeQrUpload"
:on-success="(response) => handleQrUploadSuccess(response, 'wechat')"
:on-error="handleQrUploadError"
:show-file-list="false"
accept="image/*"
>
<img v-if="form.wechatQr" :src="formatImageUrl(form.wechatQr)" class="qr-preview" />
<div v-else class="qr-upload-placeholder">
<el-icon class="qr-upload-icon"><Plus /></el-icon>
<div class="qr-upload-text">上传微信收款码</div>
</div>
</el-upload>
<el-button v-if="form.wechatQr" type="danger" size="small" @click="removeQrCode('wechat')" class="remove-btn">
删除
</el-button>
</div>
</el-form-item>
<el-form-item label="支付宝收款码" prop="alipayQr">
<div class="qr-upload-container">
<el-upload
class="qr-uploader"
:action="uploadUrl"
:headers="uploadHeaders"
:before-upload="beforeQrUpload"
:on-success="(response) => handleQrUploadSuccess(response, 'alipay')"
:on-error="handleQrUploadError"
:show-file-list="false"
accept="image/*"
>
<img v-if="form.alipayQr" :src="formatImageUrl(form.alipayQr)" class="qr-preview" />
<div v-else class="qr-upload-placeholder">
<el-icon class="qr-upload-icon"><Plus /></el-icon>
<div class="qr-upload-text">上传支付宝收款码</div>
</div>
</el-upload>
<el-button v-if="form.alipayQr" type="danger" size="small" @click="removeQrCode('alipay')" class="remove-btn">
删除
</el-button>
</div>
</el-form-item>
<el-form-item label="银行卡号" prop="bankCard">
<el-input v-model="form.bankCard" placeholder="请输入银行卡号" />
</el-form-item>
<el-form-item label="云闪付收款码" prop="unionpayQr">
<div class="qr-upload-container">
<el-upload
class="qr-uploader"
:action="uploadUrl"
:headers="uploadHeaders"
:before-upload="beforeQrUpload"
:on-success="(response) => handleQrUploadSuccess(response, 'unionpay')"
:on-error="handleQrUploadError"
:show-file-list="false"
accept="image/*"
>
<img v-if="form.unionpayQr" :src="formatImageUrl(form.unionpayQr)" class="qr-preview" />
<div v-else class="qr-upload-placeholder">
<el-icon class="qr-upload-icon"><Plus /></el-icon>
<div class="qr-upload-text">上传云闪付收款码</div>
</div>
</el-upload>
<el-button v-if="form.unionpayQr" type="danger" size="small" @click="removeQrCode('unionpay')" class="remove-btn">
删除
</el-button>
</div>
</el-form-item>
<el-form-item label="营业执照" prop="businessLicense">
<div class="qr-upload-container">
<el-upload
class="qr-uploader"
:action="uploadUrl"
:headers="uploadHeaders"
:before-upload="beforeDocumentUpload"
:on-success="(response) => handleDocumentUploadSuccess(response, 'businessLicense')"
:on-error="handleQrUploadError"
:show-file-list="false"
accept="image/*"
>
<img v-if="form.businessLicense" :src="formatImageUrl(form.businessLicense)" class="qr-preview" />
<div v-else class="qr-upload-placeholder">
<el-icon class="qr-upload-icon"><Plus /></el-icon>
<div class="qr-upload-text">上传营业执照</div>
</div>
</el-upload>
<el-button v-if="form.businessLicense" type="danger" size="small" @click="removeDocument('businessLicense')" class="remove-btn">
删除
</el-button>
</div>
</el-form-item>
<el-form-item label="身份证正面" prop="idCardFront">
<div class="qr-upload-container">
<el-upload
class="qr-uploader"
:action="uploadUrl"
:headers="uploadHeaders"
:before-upload="beforeDocumentUpload"
:on-success="(response) => handleDocumentUploadSuccess(response, 'idCardFront')"
:on-error="handleQrUploadError"
:show-file-list="false"
accept="image/*"
>
<img v-if="form.idCardFront" :src="formatImageUrl(form.idCardFront)" class="qr-preview" />
<div v-else class="qr-upload-placeholder">
<el-icon class="qr-upload-icon"><Plus /></el-icon>
<div class="qr-upload-text">上传身份证正面</div>
</div>
</el-upload>
<el-button v-if="form.idCardFront" type="danger" size="small" @click="removeDocument('idCardFront')" class="remove-btn">
删除
</el-button>
</div>
</el-form-item>
<el-form-item label="身份证反面" prop="idCardBack">
<div class="qr-upload-container">
<el-upload
class="qr-uploader"
:action="uploadUrl"
:headers="uploadHeaders"
:before-upload="beforeDocumentUpload"
:on-success="(response) => handleDocumentUploadSuccess(response, 'idCardBack')"
:on-error="handleQrUploadError"
:show-file-list="false"
accept="image/*"
>
<img v-if="form.idCardBack" :src="formatImageUrl(form.idCardBack)" class="qr-preview" />
<div v-else class="qr-upload-placeholder">
<el-icon class="qr-upload-icon"><Plus /></el-icon>
<div class="qr-upload-text">上传身份证反面</div>
</div>
</el-upload>
<el-button v-if="form.idCardBack" type="danger" size="small" @click="removeDocument('idCardBack')" class="remove-btn">
删除
</el-button>
</div>
</el-form-item>
</el-form>
<div class="form-actions">
<el-button @click="resetForm">重置</el-button>
<el-button type="primary" @click="saveProfile" :loading="saving">
保存资料
</el-button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useUserStore } from '@/stores/user'
import { ElMessage } from 'element-plus'
import { ArrowLeft, Plus } from '@element-plus/icons-vue'
import api from '@/utils/api'
import { uploadURL, getImageUrl, getUploadConfig } from '@/config'
const router = useRouter()
const route = useRoute()
const userStore = useUserStore()
// 响应式数据
const form = reactive({
username: '',
nickname: '',
phone: '',
realName: '',
idCard: '',
wechatQr: '',
alipayQr: '',
bankCard: '',
unionpayQr: '',
businessLicense: '',
idCardFront: '',
idCardBack: ''
})
const saving = ref(false)
const profileFormRef = ref()
// 上传配置
const uploadUrl = ref(uploadURL)
const uploadHeaders = computed(() => getUploadConfig().headers)
// 表单验证规则
const rules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '用户名长度在 3 到 20 个字符', trigger: 'blur' }
],
realName: [
{ required: true, message: '请输入真实姓名', trigger: 'blur' },
{ min: 2, max: 10, message: '真实姓名长度在 2 到 10 个字符', trigger: 'blur' }
],
idCard: [
{ required: true, message: '请输入身份证号', trigger: 'blur' },
{ pattern: /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/, message: '请输入正确的身份证号', trigger: 'blur' }
],
phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
]
}
// 方法
/**
* 获取用户信息
*/
const getUserInfo = async () => {
try {
const response = await api.get('/users/profile')
console.log(response.data, 'response.data.data')
// 同步到表单
Object.keys(form).forEach(key => {
form[key] = response.data.user[key] || ''
})
} catch (error) {
console.error('获取用户信息失败:', error)
ElMessage.error('获取用户信息失败')
}
}
/**
* 保存个人资料
*/
const saveProfile = async () => {
try {
await profileFormRef.value.validate()
saving.value = true
form.wechatQr = form.wechatQr.replace('https://minio.zrbjr.com', '')
form.alipayQr = form.alipayQr.replace('https://minio.zrbjr.com', '')
form.unionpayQr = form.unionpayQr.replace('https://minio.zrbjr.com', '')
form.businessLicense = form.businessLicense.replace('https://minio.zrbjr.com', '')
form.idCardFront = form.idCardFront.replace('https://minio.zrbjr.com', '')
form.idCardBack = form.idCardBack.replace('https://minio.zrbjr.com', '')
const response = await api.put('/users/profile', form)
// 更新本地数据
if (response.data.success) {
ElMessage.success(response.data.message || '保存成功')
router.back() // 保存成功后返回上一页
} else {
ElMessage.error(response.data.message || '保存失败')
}
} catch (error) {
console.error('保存个人资料失败:', error)
if (error.response) {
ElMessage.error(error.response.data.message || '保存失败')
} else {
ElMessage.error('保存失败')
}
} finally {
saving.value = false
}
}
const resetForm = () => {
getUserInfo() // 重置为原始数据
}
/**
* 二维码上传前的验证
* @param {File} file - 上传的文件
* @returns {boolean} 是否通过验证
*/
const beforeQrUpload = (file) => {
const isImage = file.type.startsWith('image/')
const isLt5M = file.size / 1024 / 1024 < 5
if (!isImage) {
ElMessage.error('只能上传图片文件!')
return false
}
if (!isLt5M) {
ElMessage.error('图片大小不能超过 5MB!')
return false
}
return true
}
/**
* 处理二维码上传成功回调
* @param {Object} response - 上传接口返回的响应数据
* @param {string} type - 二维码类型 (wechat/alipay/unionpay)
*/
const handleQrUploadSuccess = (response, type) => {
if (response.success) {
// 更新对应的二维码字段
const fieldMap = {
wechat: 'wechatQr',
alipay: 'alipayQr',
unionpay: 'unionpayQr'
}
form[fieldMap[type]] = response.data.url
ElMessage.success('收款码上传成功')
} else {
ElMessage.error(response.message || '上传失败')
}
}
/**
* 处理二维码上传错误
* @param {Object} error - 错误信息
*/
const handleQrUploadError = (error) => {
console.error('上传错误:', error)
ElMessage.error('上传失败,请重试')
}
/**
* 删除二维码
* @param {string} type - 二维码类型 (wechat/alipay/unionpay)
*/
const removeQrCode = (type) => {
const fieldMap = {
wechat: 'wechatQr',
alipay: 'alipayQr',
unionpay: 'unionpayQr'
}
form[fieldMap[type]] = ''
ElMessage.success('收款码已删除')
}
/**
* 证件上传前的验证
* @param {File} file - 上传的文件
* @returns {boolean} 是否通过验证
*/
const beforeDocumentUpload = (file) => {
const isImage = file.type.startsWith('image/')
const isLt10M = file.size / 1024 / 1024 < 10
if (!isImage) {
ElMessage.error('只能上传图片文件!')
return false
}
if (!isLt10M) {
ElMessage.error('图片大小不能超过 10MB!')
return false
}
return true
}
/**
* 处理证件上传成功回调
* @param {Object} response - 上传接口返回的响应数据
* @param {string} type - 证件类型 (businessLicense/idCardFront/idCardBack)
*/
const handleDocumentUploadSuccess = (response, type) => {
console.log(response, 'response');
if (response.success) {
form[type] = response.data.url
const typeNames = {
businessLicense: '营业执照',
idCardFront: '身份证正面',
idCardBack: '身份证反面'
}
ElMessage.success(`${typeNames[type]}上传成功`)
} else {
ElMessage.error(response.message || '上传失败')
}
}
/**
* 删除证件
* @param {string} type - 证件类型 (businessLicense/idCardFront/idCardBack)
*/
const removeDocument = (type) => {
form[type] = ''
const typeNames = {
businessLicense: '营业执照',
idCardFront: '身份证正面',
idCardBack: '身份证反面'
}
ElMessage.success(`${typeNames[type]}已删除`)
}
/**
* 获取图片URL - 使用配置文件处理
* @param {string} url - 相对路径或完整URL
* @returns {string} 处理后的图片URL
*/
const formatImageUrl = (url) => {
return getImageUrl(url)
}
// 生命周期
onMounted(() => {
getUserInfo()
})
</script>
<style lang="scss" scoped>
.profile-edit-page {
min-height: 100vh;
background-color: #f5f5f5;
}
.navbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
height: 56px;
background: white;
border-bottom: 1px solid #eee;
position: sticky;
top: 0;
z-index: 100;
}
.nav-left,
.nav-right {
flex: 1;
}
.nav-right {
text-align: right;
}
.back-btn {
color: #409eff;
font-size: 14px;
display: flex;
align-items: center;
gap: 5px;
}
.nav-title {
margin: 0;
font-size: 18px;
font-weight: 500;
color: #333;
}
.profile-content {
padding: 20px 16px;
}
.profile-form {
background: white;
border-radius: 12px;
padding: 25px;
margin-bottom: 20px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
border: 1px solid rgba(0, 0, 0, 0.05);
}
.form-actions {
text-align: center;
margin-top: 30px;
display: flex;
flex-direction: column; // 修改为垂直排列
gap: 15px;
align-items: center; // 水平居中对齐
}
.form-actions .el-button {
width: 100%; // 按钮宽度占满容器
max-width: 220px; // 最大宽度限制
border-radius: 8px;
font-weight: 500;
}
/* 二维码上传样式 */
.qr-upload-container {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.qr-uploader {
width: 120px;
}
.qr-uploader :deep(.el-upload) {
border: 2px dashed #d9d9d9;
border-radius: 8px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: 0.3s;
width: 120px;
height: 120px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #fafafa;
}
.qr-uploader :deep(.el-upload:hover) {
border-color: #409eff;
background-color: #f0f9ff;
}
.qr-upload-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
text-align: center;
}
.qr-upload-icon {
font-size: 24px;
color: #8c939d;
margin-bottom: 8px;
}
.qr-upload-text {
font-size: 12px;
color: #8c939d;
line-height: 1.2;
}
.qr-preview {
width: 120px;
height: 120px;
object-fit: cover;
border-radius: 6px;
}
.remove-btn {
align-self: flex-start;
}
/* 响应式设计 */
@media (max-width: 768px) {
.profile-content {
padding: 10px;
}
.profile-form {
padding: 20px;
border-radius: 8px;
}
.qr-uploader {
width: 100px;
}
.qr-uploader :deep(.el-upload) {
width: 100px;
height: 100px;
}
.qr-preview {
width: 100px;
height: 100px;
}
.form-actions {
flex-direction: column;
align-items: center;
}
.form-actions .el-button {
width: 100%;
max-width: 200px;
}
.el-form {
:deep(.el-form-item__label) {
width: 80px !important;
font-size: 14px;
}
}
}
</style>