| | |
| | | <template> |
| | | <view class="profile-container"> |
| | | <!-- 头像区域 --> |
| | | <view class="avatar-section"> |
| | | <view class="avatar-wrapper" @tap="changeAvatar"> |
| | | <image :src="userInfo.avatar" mode="aspectFill" class="avatar" /> |
| | | <view class="avatar-section" @tap="changeAvatar"> |
| | | <view class="avatar-wrapper"> |
| | | <image :src="avatarUrl" mode="aspectFill" class="avatar" /> |
| | | <view class="edit-icon"> |
| | | <text class="iconfont icon-camera"></text> |
| | | <text class="plus">+</text> |
| | | </view> |
| | | </view> |
| | | <text class="tip">点击更换头像</text> |
| | | </view> |
| | | |
| | | <!-- 基本信息 --> |
| | | |
| | | <!-- 个人信息 --> |
| | | <view class="info-card"> |
| | | <view class="info-item" @tap="editNickname"> |
| | | <text class="label">昵称</text> |
| | | <view class="value"> |
| | | <text>{{ userInfo.nickname }}</text> |
| | | <text class="iconfont icon-arrow-right"></text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="info-item" @tap="editGender"> |
| | | <text class="label">性别</text> |
| | | <view class="value"> |
| | | <text>{{ userInfo.gender === 1 ? '男' : '女' }}</text> |
| | | <text class="iconfont icon-arrow-right"></text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="info-item" @tap="editBirthday"> |
| | | <text class="label">生日</text> |
| | | <view class="value"> |
| | | <text>{{ userInfo.birthday || '请选择' }}</text> |
| | | <text class="iconfont icon-arrow-right"></text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="info-item" @tap="editPhone"> |
| | | <text class="label">手机号</text> |
| | | <view class="value"> |
| | | <text>{{ formatPhone(userInfo.phone) }}</text> |
| | | <text class="iconfont icon-arrow-right"></text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="info-item" @tap="editEmail"> |
| | | <text class="label">邮箱</text> |
| | | <view class="value"> |
| | | <text>{{ userInfo.email || '未绑定' }}</text> |
| | | <text class="iconfont icon-arrow-right"></text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 实名认证 --> |
| | | <view class="verify-card" @tap="goVerify"> |
| | | <view class="verify-info"> |
| | | <text class="title">实名认证</text> |
| | | <text class="status">{{ userInfo.verified ? '已认证' : '未认证' }}</text> |
| | | </view> |
| | | <text class="iconfont icon-arrow-right"></text> |
| | | </view> |
| | | |
| | | <!-- 隐私设置 --> |
| | | <view class="privacy-card"> |
| | | <view class="card-header"> |
| | | <text class="title">隐私设置</text> |
| | | <text class="title">个人信息</text> |
| | | </view> |
| | | <view class="setting-list"> |
| | | <view class="setting-item"> |
| | | <text class="label">公开个人主页</text> |
| | | <switch |
| | | :checked="privacy.publicProfile" |
| | | @change="e => togglePrivacy('publicProfile', e.detail.value)" |
| | | color="#0f95b0" |
| | | <view class="info-list"> |
| | | <view class="info-item"> |
| | | <text class="label">用户名称</text> |
| | | <text class="value">{{ user.userName }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="label">手机号码</text> |
| | | <text class="value">{{ user.phonenumber }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="label">用户邮箱</text> |
| | | <text class="value">{{ user.email }}</text> |
| | | </view> |
| | | <view class="info-item" v-if="user.dept"> |
| | | <text class="label">所属部门</text> |
| | | <text class="value">{{ user.dept.deptName }} / {{ postGroup }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="label">所属角色</text> |
| | | <text class="value">{{ roleGroup }}</text> |
| | | </view> |
| | | <view class="info-item"> |
| | | <text class="label">创建日期</text> |
| | | <text class="value">{{ user.createTime }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 基本资料 --> |
| | | <view class="info-card"> |
| | | <view class="card-header"> |
| | | <text class="title">基本资料</text> |
| | | </view> |
| | | <view class="form-list"> |
| | | <view class="form-item"> |
| | | <text class="label required">用户昵称</text> |
| | | <input |
| | | v-model="form.nickName" |
| | | placeholder="请输入用户昵称" |
| | | maxlength="30" |
| | | /> |
| | | </view> |
| | | <view class="setting-item"> |
| | | <text class="label">允许陌生人私信</text> |
| | | <switch |
| | | :checked="privacy.allowMessage" |
| | | @change="e => togglePrivacy('allowMessage', e.detail.value)" |
| | | color="#0f95b0" |
| | | <view class="form-item"> |
| | | <text class="label required">手机号码</text> |
| | | <input |
| | | v-model="form.phonenumber" |
| | | type="number" |
| | | placeholder="请输入手机号码" |
| | | maxlength="11" |
| | | /> |
| | | </view> |
| | | <view class="form-item"> |
| | | <text class="label required">邮箱</text> |
| | | <input |
| | | v-model="form.email" |
| | | placeholder="请输入邮箱" |
| | | maxlength="50" |
| | | /> |
| | | </view> |
| | | <view class="form-item"> |
| | | <text class="label">性别</text> |
| | | <view class="gender-types"> |
| | | <view |
| | | class="gender-item" |
| | | :class="{ active: form.sex === '0' }" |
| | | @tap="form.sex = '0'" |
| | | > |
| | | <text class="radio"></text> |
| | | <text>男</text> |
| | | </view> |
| | | <view |
| | | class="gender-item" |
| | | :class="{ active: form.sex === '1' }" |
| | | @tap="form.sex = '1'" |
| | | > |
| | | <text class="radio"></text> |
| | | <text>女</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <button class="save-btn" @tap="saveProfile">保存</button> |
| | | </view> |
| | | |
| | | <!-- 修改密码 --> |
| | | <view class="info-card"> |
| | | <view class="card-header"> |
| | | <text class="title">修改密码</text> |
| | | </view> |
| | | <view class="form-list"> |
| | | <view class="form-item"> |
| | | <text class="label required">旧密码</text> |
| | | <input |
| | | v-model="pwdForm.oldPassword" |
| | | :password="true" |
| | | placeholder="请输入旧密码" |
| | | /> |
| | | </view> |
| | | <view class="form-item"> |
| | | <text class="label required">新密码</text> |
| | | <input |
| | | v-model="pwdForm.newPassword" |
| | | :password="true" |
| | | placeholder="请输入新密码(8-20位)" |
| | | /> |
| | | </view> |
| | | <view class="form-item"> |
| | | <text class="label required">确认密码</text> |
| | | <input |
| | | v-model="pwdForm.confirmPassword" |
| | | :password="true" |
| | | placeholder="请确认新密码" |
| | | /> |
| | | </view> |
| | | </view> |
| | | <button class="save-btn" @tap="savePassword">保存</button> |
| | | </view> |
| | | |
| | | <!-- 账号安全 --> |
| | | <view class="security-card"> |
| | | <view class="card-header"> |
| | | <text class="title">账号安全</text> |
| | | </view> |
| | | <view class="security-list"> |
| | | <view class="security-item" @tap="changePassword"> |
| | | <view class="info"> |
| | | <text class="label">修改密码</text> |
| | | <text class="desc">建议定期更换密码,确保账号安全</text> |
| | | </view> |
| | | <text class="iconfont icon-arrow-right"></text> |
| | | </view> |
| | | <view class="security-item" @tap="bindPhone"> |
| | | <view class="info"> |
| | | <text class="label">手机绑定</text> |
| | | <text class="desc">已绑定手机号:{{ formatPhone(userInfo.phone) }}</text> |
| | | </view> |
| | | <text class="iconfont icon-arrow-right"></text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | |
| | | <!-- 退出登录 --> |
| | | <button class="logout-btn" @tap="logout">退出登录</button> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from 'vue' |
| | | import { ref, computed } from "vue"; |
| | | import { onLoad } from "@dcloudio/uni-app"; |
| | | import { useUserStore } from "@/stores/user"; |
| | | |
| | | // 用户信息 |
| | | const userInfo = ref({ |
| | | avatar: '/static/avatar/default.jpg', |
| | | nickname: '张三', |
| | | gender: 1, |
| | | birthday: '1990-01-01', |
| | | phone: '13800138000', |
| | | email: 'example@email.com', |
| | | verified: true |
| | | }) |
| | | const userStore = useUserStore(); |
| | | |
| | | // 隐私设置 |
| | | const privacy = ref({ |
| | | publicProfile: true, |
| | | allowMessage: false |
| | | }) |
| | | // 用户信息(来自 /system/user/profile) |
| | | const user = ref({}); |
| | | const roleGroup = ref(""); |
| | | const postGroup = ref(""); |
| | | |
| | | // 格式化手机号 |
| | | const formatPhone = (phone) => { |
| | | if (!phone) return '未绑定' |
| | | return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') |
| | | } |
| | | // 基本资料表单 |
| | | const form = ref({ |
| | | nickName: "", |
| | | phonenumber: "", |
| | | email: "", |
| | | sex: "0", |
| | | }); |
| | | |
| | | // 修改密码表单 |
| | | const pwdForm = ref({ |
| | | oldPassword: "", |
| | | newPassword: "", |
| | | confirmPassword: "", |
| | | }); |
| | | |
| | | // 头像地址(相对路径拼接服务器地址) |
| | | const avatarUrl = computed(() => { |
| | | const avatar = user.value.avatar; |
| | | if (!avatar) return "/static/avatar/default.png"; |
| | | if (/^https?:\/\//.test(avatar)) return avatar; |
| | | // #ifdef H5 |
| | | return `/api${avatar}`; |
| | | // #endif |
| | | // #ifndef H5 |
| | | return `${userStore.baseUrlHt}${avatar}`; |
| | | // #endif |
| | | }); |
| | | |
| | | onLoad(() => { |
| | | loadProfile(); |
| | | }); |
| | | |
| | | // 加载个人信息 |
| | | const loadProfile = async () => { |
| | | // 先用 store 中的用户信息兜底显示 |
| | | if (userStore.userInfo) { |
| | | user.value = { ...userStore.userInfo }; |
| | | fillForm(); |
| | | } |
| | | try { |
| | | const res = await uni.$uapi.get("/system/user/profile"); |
| | | user.value = res.data || {}; |
| | | roleGroup.value = res.roleGroup || ""; |
| | | postGroup.value = res.postGroup || ""; |
| | | fillForm(); |
| | | } catch (e) { |
| | | // 拦截器已统一提示 |
| | | } |
| | | }; |
| | | |
| | | // 用用户信息回填基本资料表单 |
| | | const fillForm = () => { |
| | | form.value.nickName = user.value.nickName || ""; |
| | | form.value.phonenumber = user.value.phonenumber || ""; |
| | | form.value.email = user.value.email || ""; |
| | | form.value.sex = String(user.value.sex ?? "0"); |
| | | }; |
| | | |
| | | // 更换头像 |
| | | const changeAvatar = () => { |
| | | uni.chooseImage({ |
| | | count: 1, |
| | | sizeType: ["compressed"], |
| | | sourceType: ["album", "camera"], |
| | | success: (res) => { |
| | | // 这里处理头像上传逻辑 |
| | | userInfo.value.avatar = res.tempFilePaths[0] |
| | | } |
| | | }) |
| | | } |
| | | uploadAvatar(res.tempFilePaths[0]); |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // 编辑昵称 |
| | | const editNickname = () => { |
| | | uni.navigateTo({ |
| | | url: '/pages/my/edit-nickname' |
| | | }) |
| | | } |
| | | // 上传头像 |
| | | const uploadAvatar = (filePath) => { |
| | | uni.showLoading({ title: "上传中...", mask: true }); |
| | | const token = uni.getStorageSync("token"); |
| | | // #ifdef H5 |
| | | const uploadUrl = "/api/system/user/profile/avatar"; |
| | | // #endif |
| | | // #ifndef H5 |
| | | const uploadUrl = `${userStore.baseUrlHt}/system/user/profile/avatar`; |
| | | // #endif |
| | | |
| | | // 编辑性别 |
| | | const editGender = () => { |
| | | uni.showActionSheet({ |
| | | itemList: ['男', '女'], |
| | | uni.uploadFile({ |
| | | url: uploadUrl, |
| | | filePath, |
| | | name: "avatarfile", |
| | | header: { Authorization: `Bearer ${token}` }, |
| | | success: (res) => { |
| | | userInfo.value.gender = res.tapIndex + 1 |
| | | } |
| | | }) |
| | | } |
| | | uni.hideLoading(); |
| | | if (res.statusCode === 200) { |
| | | try { |
| | | const data = JSON.parse(res.data); |
| | | if (data.code === 200) { |
| | | user.value.avatar = data.imgUrl; |
| | | userStore.refreshUserInfo(); |
| | | uni.showToast({ title: "头像修改成功", icon: "success" }); |
| | | } else { |
| | | uni.showToast({ title: data.msg || "上传失败", icon: "none" }); |
| | | } |
| | | } catch (e) { |
| | | uni.showToast({ title: "响应解析失败", icon: "none" }); |
| | | } |
| | | } else { |
| | | uni.showToast({ |
| | | title: `上传失败,状态码: ${res.statusCode}`, |
| | | icon: "none", |
| | | }); |
| | | } |
| | | }, |
| | | fail: () => { |
| | | uni.hideLoading(); |
| | | uni.showToast({ title: "上传失败", icon: "none" }); |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | // 编辑生日 |
| | | const editBirthday = () => { |
| | | uni.showDatePicker({ |
| | | current: userInfo.value.birthday, |
| | | success: (res) => { |
| | | userInfo.value.birthday = res.date |
| | | } |
| | | }) |
| | | } |
| | | // 保存基本资料 |
| | | const saveProfile = async () => { |
| | | if (!form.value.nickName) { |
| | | uni.showToast({ title: "用户昵称不能为空", icon: "none" }); |
| | | return; |
| | | } |
| | | if (!form.value.phonenumber) { |
| | | uni.showToast({ title: "手机号码不能为空", icon: "none" }); |
| | | return; |
| | | } |
| | | if (!/^1[3|4|5|6|7|8|9][0-9]\d{8}$/.test(form.value.phonenumber)) { |
| | | uni.showToast({ title: "请输入正确的手机号码", icon: "none" }); |
| | | return; |
| | | } |
| | | if (!form.value.email) { |
| | | uni.showToast({ title: "邮箱地址不能为空", icon: "none" }); |
| | | return; |
| | | } |
| | | if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.value.email)) { |
| | | uni.showToast({ title: "请输入正确的邮箱地址", icon: "none" }); |
| | | return; |
| | | } |
| | | |
| | | // 编辑手机号 |
| | | const editPhone = () => { |
| | | uni.navigateTo({ |
| | | url: '/pages/my/edit-phone' |
| | | }) |
| | | } |
| | | uni.showLoading({ title: "保存中...", mask: true }); |
| | | try { |
| | | await uni.$uapi.post("/system/user/profile/updateProfile", { |
| | | nickName: form.value.nickName, |
| | | phonenumber: form.value.phonenumber, |
| | | email: form.value.email, |
| | | sex: form.value.sex, |
| | | }); |
| | | uni.hideLoading(); |
| | | uni.showToast({ title: "修改成功", icon: "success" }); |
| | | |
| | | // 编辑邮箱 |
| | | const editEmail = () => { |
| | | uni.navigateTo({ |
| | | url: '/pages/my/edit-email' |
| | | }) |
| | | } |
| | | // 同步本地显示与 store |
| | | user.value.nickName = form.value.nickName; |
| | | user.value.phonenumber = form.value.phonenumber; |
| | | user.value.email = form.value.email; |
| | | user.value.sex = form.value.sex; |
| | | userStore.refreshUserInfo(); |
| | | } catch (e) { |
| | | uni.hideLoading(); |
| | | } |
| | | }; |
| | | |
| | | // 前往实名认证 |
| | | const goVerify = () => { |
| | | uni.navigateTo({ |
| | | url: '/pages/my/verify' |
| | | }) |
| | | } |
| | | // 密码复杂度校验:需同时包含大写、小写、数字和特殊字符其中三项 |
| | | const checkPwdLegal = (pwd) => { |
| | | if (!pwd || pwd.length < 8 || pwd.length > 20) return false; |
| | | let count = 0; |
| | | if (/[a-z]/.test(pwd)) count++; |
| | | if (/[A-Z]/.test(pwd)) count++; |
| | | if (/[0-9]/.test(pwd)) count++; |
| | | if (/[\W_]/.test(pwd)) count++; |
| | | return count >= 3; |
| | | }; |
| | | |
| | | // 切换隐私设置 |
| | | const togglePrivacy = (key, value) => { |
| | | privacy.value[key] = value |
| | | } |
| | | // 保存密码 |
| | | const savePassword = async () => { |
| | | const { oldPassword, newPassword, confirmPassword } = pwdForm.value; |
| | | if (!oldPassword) { |
| | | uni.showToast({ title: "旧密码不能为空", icon: "none" }); |
| | | return; |
| | | } |
| | | if (!newPassword) { |
| | | uni.showToast({ title: "新密码不能为空", icon: "none" }); |
| | | return; |
| | | } |
| | | if (newPassword.length < 8 || newPassword.length > 20) { |
| | | uni.showToast({ title: "密码长度在 8 到 20 个字符", icon: "none" }); |
| | | return; |
| | | } |
| | | if (!checkPwdLegal(newPassword)) { |
| | | uni.showToast({ |
| | | title: "密码必须同时包含大写、小写、数字和特殊字符其中三项", |
| | | icon: "none", |
| | | }); |
| | | return; |
| | | } |
| | | if (!confirmPassword) { |
| | | uni.showToast({ title: "确认密码不能为空", icon: "none" }); |
| | | return; |
| | | } |
| | | if (confirmPassword !== newPassword) { |
| | | uni.showToast({ title: "两次输入的密码不一致", icon: "none" }); |
| | | return; |
| | | } |
| | | |
| | | // 修改密码 |
| | | const changePassword = () => { |
| | | uni.navigateTo({ |
| | | url: '/pages/my/change-password' |
| | | }) |
| | | } |
| | | |
| | | // 绑定手机 |
| | | const bindPhone = () => { |
| | | uni.navigateTo({ |
| | | url: '/pages/my/bind-phone' |
| | | }) |
| | | } |
| | | uni.showLoading({ title: "保存中...", mask: true }); |
| | | try { |
| | | await uni.$uapi.post("/system/user/profile/updatePwd", { |
| | | oldPassword, |
| | | newPassword, |
| | | }); |
| | | uni.hideLoading(); |
| | | uni.showToast({ title: "密码修改成功", icon: "success" }); |
| | | pwdForm.value = { oldPassword: "", newPassword: "", confirmPassword: "" }; |
| | | } catch (e) { |
| | | uni.hideLoading(); |
| | | } |
| | | }; |
| | | |
| | | // 退出登录 |
| | | const logout = () => { |
| | | uni.showModal({ |
| | | title: '提示', |
| | | content: '确定要退出登录吗?', |
| | | title: "提示", |
| | | content: "确定要退出登录吗?", |
| | | success: (res) => { |
| | | if (res.confirm) { |
| | | // 这里处理退出登录逻辑 |
| | | uni.clearStorageSync(); |
| | | uni.reLaunch({ |
| | | url: '/pages/login/Login' |
| | | }) |
| | | url: "/pages/login/Login", |
| | | }); |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | }, |
| | | }); |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | |
| | | min-height: 100vh; |
| | | background: $bg-color; |
| | | padding-bottom: 40rpx; |
| | | |
| | | |
| | | .avatar-section { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | padding: 60rpx 0; |
| | | background: #fff; |
| | | |
| | | padding: 60rpx 0 40rpx; |
| | | background: $primary-gradient; |
| | | |
| | | .avatar-wrapper { |
| | | position: relative; |
| | | margin-bottom: 20rpx; |
| | | |
| | | margin-bottom: 16rpx; |
| | | |
| | | .avatar { |
| | | width: 160rpx; |
| | | height: 160rpx; |
| | | border-radius: 50%; |
| | | border: 4rpx solid rgba(255, 255, 255, 0.4); |
| | | } |
| | | |
| | | |
| | | .edit-icon { |
| | | position: absolute; |
| | | right: 0; |
| | | bottom: 0; |
| | | width: 48rpx; |
| | | height: 48rpx; |
| | | background: $primary-color; |
| | | background: #fff; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | |
| | | .iconfont { |
| | | font-size: 28rpx; |
| | | color: #fff; |
| | | |
| | | .plus { |
| | | font-size: 40rpx; |
| | | font-weight: bold; |
| | | color: $primary-color; |
| | | line-height: 1; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | .tip { |
| | | font-size: 26rpx; |
| | | color: $text-secondary; |
| | | font-size: 24rpx; |
| | | color: rgba(255, 255, 255, 0.9); |
| | | } |
| | | } |
| | | |
| | | |
| | | .info-card { |
| | | background: #fff; |
| | | margin: 20rpx 0; |
| | | |
| | | .info-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 30rpx; |
| | | border-bottom: 1rpx solid $border-color; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .label { |
| | | font-size: 30rpx; |
| | | color: $text-primary; |
| | | } |
| | | |
| | | .value { |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | text { |
| | | font-size: 30rpx; |
| | | color: $text-regular; |
| | | |
| | | &.iconfont { |
| | | font-size: 32rpx; |
| | | color: $text-secondary; |
| | | margin-left: 12rpx; |
| | | } |
| | | } |
| | | } |
| | | |
| | | &:active { |
| | | background: $bg-color; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .verify-card { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | background: #fff; |
| | | padding: 30rpx; |
| | | margin: 20rpx 0; |
| | | |
| | | .verify-info { |
| | | .title { |
| | | font-size: 30rpx; |
| | | color: $text-primary; |
| | | margin-bottom: 8rpx; |
| | | display: block; |
| | | } |
| | | |
| | | .status { |
| | | font-size: 26rpx; |
| | | color: $success; |
| | | } |
| | | } |
| | | |
| | | .iconfont { |
| | | font-size: 32rpx; |
| | | color: $text-secondary; |
| | | } |
| | | |
| | | &:active { |
| | | background: $bg-color; |
| | | } |
| | | } |
| | | |
| | | .privacy-card, |
| | | .security-card { |
| | | background: #fff; |
| | | margin: 20rpx 0; |
| | | |
| | | margin: 20rpx 20rpx 0; |
| | | border-radius: $radius-lg; |
| | | overflow: hidden; |
| | | box-shadow: $shadow-sm; |
| | | |
| | | .card-header { |
| | | padding: 30rpx; |
| | | border-bottom: 1rpx solid $border-color; |
| | | |
| | | border-bottom: 1rpx solid #eee; |
| | | |
| | | .title { |
| | | font-size: 32rpx; |
| | | color: $text-primary; |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | |
| | | .setting-list { |
| | | .setting-item { |
| | | |
| | | .info-list { |
| | | .info-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 30rpx; |
| | | border-bottom: 1rpx solid $border-color; |
| | | |
| | | padding: 28rpx 30rpx; |
| | | border-bottom: 1rpx solid #f5f5f5; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | |
| | | .label { |
| | | font-size: 30rpx; |
| | | font-size: 28rpx; |
| | | color: $text-regular; |
| | | } |
| | | |
| | | .value { |
| | | font-size: 28rpx; |
| | | color: $text-primary; |
| | | text-align: right; |
| | | flex: 1; |
| | | margin-left: 20rpx; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .security-list { |
| | | .security-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 30rpx; |
| | | border-bottom: 1rpx solid $border-color; |
| | | |
| | | |
| | | .form-list { |
| | | padding: 0 30rpx; |
| | | |
| | | .form-item { |
| | | margin-top: 30rpx; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | margin-bottom: 30rpx; |
| | | } |
| | | |
| | | .info { |
| | | .label { |
| | | font-size: 30rpx; |
| | | color: $text-primary; |
| | | margin-bottom: 8rpx; |
| | | display: block; |
| | | } |
| | | |
| | | .desc { |
| | | font-size: 26rpx; |
| | | color: $text-secondary; |
| | | |
| | | .label { |
| | | font-size: 28rpx; |
| | | color: $text-primary; |
| | | margin-bottom: 16rpx; |
| | | display: block; |
| | | |
| | | &.required::before { |
| | | content: "*"; |
| | | color: $danger; |
| | | margin-right: 4rpx; |
| | | } |
| | | } |
| | | |
| | | .iconfont { |
| | | font-size: 32rpx; |
| | | color: $text-secondary; |
| | | } |
| | | |
| | | &:active { |
| | | |
| | | input { |
| | | width: 100%; |
| | | height: 88rpx; |
| | | background: $bg-color; |
| | | border-radius: $radius-lg; |
| | | padding: 0 30rpx; |
| | | font-size: 28rpx; |
| | | color: $text-primary; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .gender-types { |
| | | display: flex; |
| | | gap: 30rpx; |
| | | |
| | | .gender-item { |
| | | flex: 1; |
| | | height: 88rpx; |
| | | background: $bg-color; |
| | | border-radius: $radius-lg; |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 0 30rpx; |
| | | |
| | | .radio { |
| | | width: 32rpx; |
| | | height: 32rpx; |
| | | border: 2rpx solid $text-secondary; |
| | | border-radius: 50%; |
| | | margin-right: 16rpx; |
| | | position: relative; |
| | | |
| | | &::after { |
| | | content: ""; |
| | | position: absolute; |
| | | left: 50%; |
| | | top: 50%; |
| | | transform: translate(-50%, -50%); |
| | | width: 16rpx; |
| | | height: 16rpx; |
| | | background: $primary-color; |
| | | border-radius: 50%; |
| | | opacity: 0; |
| | | transition: all 0.3s; |
| | | } |
| | | } |
| | | |
| | | text { |
| | | font-size: 28rpx; |
| | | color: $text-regular; |
| | | } |
| | | |
| | | &.active { |
| | | background: $primary-light; |
| | | |
| | | .radio { |
| | | border-color: $primary-color; |
| | | |
| | | &::after { |
| | | opacity: 1; |
| | | } |
| | | } |
| | | |
| | | text { |
| | | color: $primary-color; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .save-btn { |
| | | width: calc(100% - 60rpx); |
| | | height: 80rpx; |
| | | line-height: 80rpx; |
| | | margin: 0 30rpx 30rpx; |
| | | font-size: 30rpx; |
| | | color: #fff; |
| | | background: $primary-gradient; |
| | | border-radius: $radius-xl; |
| | | box-shadow: 0 4rpx 12rpx rgba($primary-color, 0.3); |
| | | |
| | | &:active { |
| | | opacity: 0.85; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | .logout-btn { |
| | | width: calc(100% - 60rpx); |
| | | height: 88rpx; |
| | | line-height: 88rpx; |
| | | margin: 40rpx auto; |
| | | margin: 40rpx auto 0; |
| | | font-size: 32rpx; |
| | | color: $warning; |
| | | color: $danger; |
| | | background: #fff; |
| | | border-radius: $radius-lg; |
| | | |
| | | |
| | | &:active { |
| | | opacity: 0.8; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | | </style> |