login.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931
  1. <template>
  2. <!-- 手机登录界面+微信快捷登录 -->
  3. <view class="login-page">
  4. <!-- 状态栏占位 -->
  5. <view class="status-bar"></view>
  6. <!-- 现代化背景 -->
  7. <view class="modern-bg">
  8. <view class="bg-shape shape-1"></view>
  9. <view class="bg-shape shape-2"></view>
  10. <view class="bg-shape shape-3"></view>
  11. </view>
  12. <!-- 返回按钮 -->
  13. <view class="nav-back pad-t-20" @click="goBack">
  14. <view class="back-btn">
  15. <u-icon size="30" name="arrow-left"></u-icon>
  16. </view>
  17. </view>
  18. <!-- Logo和标题 -->
  19. <view class="header">
  20. <view class="logo-circle">
  21. <image class="logo" src="/static/images/logo.png" mode="aspectFill"></image>
  22. </view>
  23. <view class="logo-shadow"></view>
  24. </view>
  25. <view class="title-section">
  26. <text class="title">知己</text>
  27. <view class="title-underline"></view>
  28. </view>
  29. <text class="subtitle">访客记录软件</text>
  30. </view>
  31. <!-- 登录按钮 -->
  32. <view class="login-section">
  33. <view class="btn-container">
  34. <button class="wechat-btn" @tap="handleWxLogin" :class="{ 'btn-loading': isLoading }">
  35. <u-icon name="weixin-fill" size="20" color="#fff" style="margin-right: 12rpx;"></u-icon>
  36. <text>授权快捷登录</text>
  37. </button>
  38. </view>
  39. <view class="divider">
  40. <view class="divider-line"></view>
  41. <text class="divider-text">或</text>
  42. <view class="divider-line"></view>
  43. </view>
  44. <view class="btn-container">
  45. <button class="phone-btn" @tap="handlePhoneLogin">
  46. <u-icon name="phone" size="20" color="#e28669" style="margin-right: 12rpx;"></u-icon>
  47. <text>手机号登录</text>
  48. </button>
  49. </view>
  50. <view class="btn-container">
  51. <button class="browse-btn" @tap="handleBrowse">
  52. <text>暂不登录,先去看看</text>
  53. </button>
  54. </view>
  55. <view class="agreement">
  56. <view class="checkbox-wrapper" @tap="toggleAgreement">
  57. <view class="zen-checkbox" :class="{ 'checked': isAgree }">
  58. <view class="zen-circle-inner" v-if="isAgree"></view>
  59. </view>
  60. </view>
  61. <text class="agreement-text">
  62. 登录即代表您已同意
  63. <text class="link" @tap="goToPrivacy">《隐私政策》</text>
  64. <text class="link" @tap="goToService">《服务协议》</text>
  65. </text>
  66. </view>
  67. </view>
  68. <!-- 底部装饰 -->
  69. <view class="bottom-decoration">
  70. <view class="company-info">
  71. <text class="company-name">知己访客记录系统</text>
  72. <text class="version">v1.0.0</text>
  73. </view>
  74. </view>
  75. <!-- 首次登录设置弹框 -->
  76. <u-popup :show="showProfileModal">
  77. <view class="profile-modal">
  78. <view class="modal-header">
  79. <text class="modal-title">完善个人信息</text>
  80. <text class="modal-subtitle">请设置您的账号和头像</text>
  81. </view>
  82. <view class="modal-content">
  83. <!-- 头像选择 -->
  84. <view class="avatar-section">
  85. <text class="section-label">头像</text>
  86. <button class="avatar-button" open-type="chooseAvatar" @chooseavatar="onChooseAvatar"
  87. :disabled="isUploadingAvatar">
  88. <view class="avatar-container">
  89. <image class="avatar-preview" :src="tempAvatar || '/static/images/avatar.png'"
  90. mode="aspectFill"></image>
  91. <view class="avatar-overlay">
  92. <u-icon v-if="!isUploadingAvatar" name="camera" size="24" color="#fff"></u-icon>
  93. <u-loading-icon v-else mode="spinner" size="24" color="#fff"></u-loading-icon>
  94. </view>
  95. </view>
  96. </button>
  97. </view>
  98. <!-- 账号输入 -->
  99. <view class="account-section">
  100. <text class="section-label">用户名</text>
  101. <u-input v-model="tempAccount" placeholder="请输入用户名" :maxlength="20" :clearable="true"
  102. border="surround"></u-input>
  103. </view>
  104. </view>
  105. <view class="modal-actions">
  106. <button class="confirm-btn" @tap="confirmProfile" :disabled="!tempAccount.trim() || isUploadingAvatar">
  107. <text v-if="!isUploadingAvatar">确认</text>
  108. <text v-else>头像上传中...</text>
  109. </button>
  110. </view>
  111. </view>
  112. </u-popup>
  113. </template>
  114. <script>
  115. import {
  116. wxLogin,
  117. updateProfile
  118. } from '@/config/api.js';
  119. import {
  120. UPLOAD_URL
  121. } from '@/common/config.js';
  122. export default {
  123. data() {
  124. return {
  125. isAgree: false,
  126. isLoading: false,
  127. redirectUrl: '', // 登录成功后的重定向地址
  128. showProfileModal: false, // 显示个人信息设置弹框
  129. tempAccount: '', // 临时账号
  130. tempAvatar: '', // 临时头像
  131. loginData: null, // 临时保存登录数据
  132. isUploadingAvatar: false // 头像上传状态
  133. }
  134. },
  135. onLoad(options) {
  136. // 获取重定向地址
  137. if (options.redirect) {
  138. this.redirectUrl = decodeURIComponent(options.redirect);
  139. }
  140. },
  141. methods: {
  142. // 处理微信登录
  143. async handleWxLogin() {
  144. if (!this.isAgree) {
  145. let that = this
  146. // 弹出确认弹框询问用户是否同意协议
  147. uni.showModal({
  148. title: '提示',
  149. content: '登录需要您同意《隐私政策》和《服务协议》,是否同意?',
  150. success: function (res) {
  151. if (res.confirm) {
  152. // 用户点击同意,自动勾选协议并执行登录
  153. that.isAgree = true;
  154. that.executeLogin();
  155. } else if (res.cancel) {
  156. console.log('用户点击取消');
  157. }
  158. }
  159. });
  160. return;
  161. }
  162. this.executeLogin();
  163. },
  164. // 执行登录逻辑
  165. async executeLogin() {
  166. uni.showLoading({
  167. mask: true
  168. })
  169. this.isLoading = true;
  170. uni.getUserProfile({
  171. desc: '用于完善会员资料',
  172. success: (profileRes) => {
  173. uni.login({
  174. provider: 'weixin',
  175. success: async (loginRes) => {
  176. console.log('loginRes:', loginRes.code);
  177. try {
  178. const loginData = await wxLogin({
  179. code: loginRes.code,
  180. account: profileRes.userInfo.nickName,
  181. avatarUrl: profileRes.userInfo.avatarUrl,
  182. grant_type: 'wechat'
  183. });
  184. if (loginData.code === 200) {
  185. // 检查是否为首次登录
  186. if (loginData.isFirstLogin) {
  187. // 首次登录,显示个人信息设置弹框
  188. this.loginData = loginData;
  189. this.tempAccount = loginData.account || '';
  190. this.tempAvatar = loginData.avatar || '';
  191. this.showProfileModal = true;
  192. this.isLoading = false;
  193. uni.setStorageSync('access_token', loginData
  194. .access_token);
  195. uni.setStorageSync('refresh_token', loginData
  196. .refresh_token);
  197. uni.setStorageSync('user', loginData);
  198. this.$store.commit('isLogin', true);
  199. this.$store.commit('refresh_token', loginData
  200. .refresh_token);
  201. uni.hideLoading();
  202. } else {
  203. // 非首次登录,直接保存并跳转
  204. this.saveLoginDataAndRedirect(loginData);
  205. }
  206. } else {
  207. uni.showToast({
  208. title: loginData.msg || '登录失败',
  209. icon: 'none'
  210. });
  211. }
  212. } catch (e) {
  213. console.error('登录错误:', e);
  214. uni.showToast({
  215. title: '登录失败,请重试',
  216. icon: 'none'
  217. });
  218. } finally {
  219. this.isLoading = false;
  220. }
  221. },
  222. fail: () => {
  223. this.isLoading = false;
  224. }
  225. });
  226. },
  227. fail: (err) => {
  228. this.isLoading = false;
  229. if (err.errMsg.includes('cancel')) {
  230. return; // 用户取消,不显示提示
  231. }
  232. uni.showToast({
  233. title: '获取用户信息失败',
  234. icon: 'none'
  235. });
  236. }
  237. });
  238. },
  239. // 切换协议同意状态
  240. toggleAgreement() {
  241. this.isAgree = !this.isAgree;
  242. },
  243. // 返回上一页
  244. goBack() {
  245. uni.navigateBack();
  246. },
  247. // 跳转到隐私政策
  248. goToPrivacy() {
  249. uni.navigateTo({
  250. url: '/pagesA/public/richtext'
  251. });
  252. },
  253. // 跳转到服务协议
  254. goToService() {
  255. uni.navigateTo({
  256. url: '/pagesA/public/richtext'
  257. });
  258. },
  259. handleBrowse() {
  260. // 直接返回首页
  261. uni.switchTab({
  262. url: '/pages/index/index'
  263. });
  264. },
  265. // 跳转到手机登录页面
  266. handlePhoneLogin() {
  267. uni.navigateTo({
  268. url: '/pagesA/public/phone-login'
  269. });
  270. },
  271. // 保存登录数据并跳转
  272. saveLoginDataAndRedirect(loginData) {
  273. uni.setStorageSync('access_token', loginData.access_token);
  274. uni.setStorageSync('refresh_token', loginData.refresh_token);
  275. uni.setStorageSync('user', loginData);
  276. this.$store.commit('isLogin', true);
  277. this.$store.commit('refresh_token', loginData.refresh_token);
  278. uni.showToast({
  279. title: '登录成功',
  280. icon: 'success',
  281. duration: 1000
  282. });
  283. // 登录成功后的跳转
  284. setTimeout(() => {
  285. if (this.redirectUrl) {
  286. uni.redirectTo({
  287. url: this.redirectUrl
  288. });
  289. } else {
  290. // 检查是否有待处理的邀请,如果有则跳转到首页触发邀请弹框
  291. const pendingInvitation = uni.getStorageSync('pendingInvitation');
  292. if (pendingInvitation && pendingInvitation.inviterId) {
  293. console.log('登录成功,发现待处理邀请,跳转到首页');
  294. // 使用 reLaunch 确保首页能重新加载并检查邀请
  295. uni.reLaunch({
  296. url: '/pages/index/index'
  297. });
  298. } else {
  299. uni.switchTab({
  300. url: '/pages/index/index'
  301. });
  302. }
  303. }
  304. }, 1000);
  305. },
  306. // 微信小程序头像选择回调
  307. async onChooseAvatar(e) {
  308. console.log('选择头像:', e.detail.avatarUrl);
  309. // 显示上传状态
  310. this.isUploadingAvatar = true;
  311. uni.showLoading({
  312. title: '上传头像中...',
  313. mask: true
  314. });
  315. try {
  316. // 上传头像到服务器
  317. const uploadResult = await this.uploadFilePromise(e.detail.avatarUrl);
  318. if (uploadResult && uploadResult.data && uploadResult.data.link) {
  319. this.tempAvatar = uploadResult.data.link;
  320. uni.showToast({
  321. title: '头像上传成功',
  322. icon: 'success'
  323. });
  324. } else {
  325. throw new Error('上传失败');
  326. }
  327. } catch (error) {
  328. console.error('头像上传失败:', error);
  329. uni.showToast({
  330. title: '头像上传失败,请重试',
  331. icon: 'none'
  332. });
  333. } finally {
  334. this.isUploadingAvatar = false;
  335. uni.hideLoading();
  336. }
  337. },
  338. // 上传文件到服务器
  339. uploadFilePromise(filePath) {
  340. return new Promise((resolve, reject) => {
  341. // 优先使用登录数据中的token,如果没有则使用本地存储的token
  342. const token = (this.loginData && this.loginData.access_token) || uni.getStorageSync(
  343. 'access_token');
  344. if (!token) {
  345. reject(new Error('未找到访问令牌'));
  346. return;
  347. }
  348. uni.uploadFile({
  349. url: UPLOAD_URL,
  350. filePath: filePath,
  351. header: {
  352. "Blade-Auth": token
  353. },
  354. name: 'file',
  355. formData: {
  356. user: 'avatar'
  357. },
  358. success: (res) => {
  359. try {
  360. const result = JSON.parse(res.data);
  361. if (result.success || result.code === 200) {
  362. resolve(result);
  363. } else {
  364. reject(new Error(result.msg || '上传失败'));
  365. }
  366. } catch (e) {
  367. reject(new Error('解析响应失败'));
  368. }
  369. },
  370. fail: (error) => {
  371. reject(error);
  372. }
  373. });
  374. });
  375. },
  376. // 确认个人信息设置
  377. async confirmProfile() {
  378. if (!this.tempAccount.trim()) {
  379. uni.showToast({
  380. title: '请输入账号',
  381. icon: 'none'
  382. });
  383. return;
  384. }
  385. uni.showLoading({
  386. title: '保存中...',
  387. mask: true
  388. });
  389. try {
  390. // 调用更新用户信息接口
  391. const updateRes = await updateProfile({
  392. account: this.tempAccount.trim(),
  393. avatar: this.tempAvatar
  394. });
  395. if (updateRes.code == 200) {
  396. // 更新登录数据中的用户信息
  397. this.loginData.account = this.tempAccount.trim();
  398. if (this.tempAvatar) {
  399. this.loginData.avatar = this.tempAvatar;
  400. this.loginData.avatarUrl = this.tempAvatar; // 同时更新 avatarUrl 字段
  401. }
  402. // 更新本地存储的用户信息
  403. uni.setStorageSync('user', this.loginData);
  404. // 保存登录数据并跳转
  405. this.saveLoginDataAndRedirect(this.loginData);
  406. this.showProfileModal = false;
  407. } else {
  408. uni.showToast({
  409. title: updateRes.msg || '保存失败,请重试',
  410. icon: 'none'
  411. });
  412. }
  413. } catch (error) {
  414. console.error('更新用户信息错误:', error);
  415. uni.showToast({
  416. title: '保存失败,请重试',
  417. icon: 'none'
  418. });
  419. } finally {
  420. uni.hideLoading();
  421. }
  422. }
  423. }
  424. }
  425. </script>
  426. <style lang="scss" scoped>
  427. .login-page {
  428. min-height: 100vh;
  429. background: #F5F5F5;
  430. position: relative;
  431. overflow: hidden;
  432. }
  433. .status-bar {
  434. height: var(--status-bar-height);
  435. width: 100%;
  436. }
  437. // 现代化背景
  438. .modern-bg {
  439. position: absolute;
  440. top: 0;
  441. left: 0;
  442. width: 100%;
  443. height: 100%;
  444. pointer-events: none;
  445. z-index: 1;
  446. .bg-shape {
  447. position: absolute;
  448. border-radius: 50%;
  449. &.shape-1 {
  450. width: 300rpx;
  451. height: 300rpx;
  452. top: 15%;
  453. right: -80rpx;
  454. background: linear-gradient(135deg, rgba(255, 91, 5, 0.1) 0%, transparent 70%);
  455. }
  456. &.shape-2 {
  457. width: 200rpx;
  458. height: 200rpx;
  459. top: 50%;
  460. left: -60rpx;
  461. background: linear-gradient(135deg, rgba(41, 44, 53, 0.08) 0%, transparent 70%);
  462. }
  463. &.shape-3 {
  464. width: 150rpx;
  465. height: 150rpx;
  466. bottom: 25%;
  467. right: 15%;
  468. background: linear-gradient(135deg, rgba(255, 91, 5, 0.06) 0%, transparent 70%);
  469. }
  470. }
  471. }
  472. .nav-back {
  473. position: fixed;
  474. left: 30rpx;
  475. top: calc(var(--status-bar-height) + 20rpx);
  476. z-index: 100;
  477. .back-btn {
  478. width: 80rpx;
  479. height: 80rpx;
  480. background: #FFFFFF;
  481. border: 1rpx solid rgba(255, 91, 5, 0.2);
  482. border-radius: 50%;
  483. display: flex;
  484. align-items: center;
  485. justify-content: center;
  486. transition: all 0.3s ease;
  487. box-shadow: 0 4rpx 16rpx rgba(41, 44, 53, 0.1);
  488. &:active {
  489. transform: scale(0.95);
  490. background: rgba(255, 91, 5, 0.1);
  491. }
  492. }
  493. .back-icon {
  494. font-family: "iconfont";
  495. font-size: 36rpx;
  496. color: #292C35;
  497. }
  498. }
  499. .header {
  500. display: flex;
  501. flex-direction: column;
  502. align-items: center;
  503. padding-top: 120rpx;
  504. position: relative;
  505. z-index: 2;
  506. .logo-container {
  507. position: relative; // 设置为相对定位,作为子元素的定位参考
  508. margin-bottom: 60rpx; // 底部外边距60rpx,与标题保持间距
  509. .logo-circle {
  510. width: 100rpx; // Logo容器宽度100rpx
  511. height: 100rpx; // Logo容器高度100rpx
  512. border-radius: 20rpx; // 圆角20rpx,让容器呈圆角矩形
  513. background: #FFFFFF; // 背景色为白色
  514. display: flex; // 使用弹性布局
  515. align-items: center; // 垂直居中对齐
  516. justify-content: center; // 水平居中对齐
  517. box-shadow: 0 8rpx 24rpx rgba(41, 44, 53, 0.1); // 添加阴影效果,让容器有浮起感
  518. border: 2rpx solid rgba(255, 91, 5, 0.2); // 边框2rpx,橙色半透明
  519. .logo {
  520. width: 40rpx; // Logo图片宽度40rpx
  521. height: 40rpx; // Logo图片高度40rpx
  522. border-radius: 16rpx; // Logo图片圆角16rpx
  523. }
  524. }
  525. .logo-shadow {
  526. position: absolute; // 绝对定位,相对于logo-container定位
  527. bottom: -20rpx; // 距离底部-20rpx,创建阴影向下延伸效果
  528. left: 50%; // 距离左边50%
  529. transform: translateX(-50%); // 水平居中,通过左移自身宽度的一半来实现
  530. width: 160rpx; // 阴影宽度160rpx
  531. height: 20rpx; // 阴影高度20rpx
  532. background: radial-gradient(ellipse, rgba(255, 91, 5, 0.1) 0%, transparent 70%); // 径向渐变,从中心向外逐渐透明
  533. border-radius: 50%; // 完全圆形
  534. }
  535. }
  536. .title-section {
  537. text-align: center;
  538. margin-bottom: 20rpx;
  539. position: relative;
  540. .title {
  541. font-size: 64rpx;
  542. font-weight: 600;
  543. color: #292C35;
  544. letter-spacing: 8rpx;
  545. font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
  546. text-shadow: 0 2rpx 8rpx rgba(41, 44, 53, 0.1);
  547. }
  548. .title-underline {
  549. position: absolute;
  550. bottom: -10rpx;
  551. left: 50%;
  552. transform: translateX(-50%);
  553. width: 80rpx;
  554. height: 4rpx;
  555. background: linear-gradient(90deg, transparent, #FF5B05, transparent);
  556. border-radius: 2rpx;
  557. }
  558. }
  559. .subtitle {
  560. font-size: 28rpx;
  561. color: rgba(41, 44, 53, 0.7);
  562. font-weight: 300;
  563. letter-spacing: 4rpx;
  564. font-family: inherit;
  565. }
  566. }
  567. .login-section {
  568. padding: 80rpx 60rpx;
  569. position: relative;
  570. z-index: 2;
  571. .btn-container {
  572. margin-bottom: 40rpx;
  573. }
  574. .wechat-btn {
  575. position: relative;
  576. display: flex;
  577. align-items: center;
  578. justify-content: center;
  579. width: 100%;
  580. height: 96rpx;
  581. background: linear-gradient(135deg, #FF5B05 0%, #FF8E3C 100%);
  582. border: none;
  583. border-radius: 48rpx;
  584. color: #fff;
  585. font-size: 32rpx;
  586. font-weight: 500;
  587. transition: all 0.3s ease;
  588. box-shadow: 0 4rpx 16rpx rgba(255, 91, 5, 0.3);
  589. &:active {
  590. transform: translateY(2rpx);
  591. box-shadow: 0 2rpx 8rpx rgba(255, 91, 5, 0.4);
  592. }
  593. &.btn-loading {
  594. background: linear-gradient(135deg, #FFB088 0%, #FFA573 100%);
  595. color: rgba(255, 255, 255, 0.8);
  596. }
  597. }
  598. .phone-btn {
  599. display: flex;
  600. align-items: center;
  601. justify-content: center;
  602. width: 100%;
  603. height: 96rpx;
  604. background: #FFFFFF;
  605. border: 2rpx solid #FF5B05;
  606. border-radius: 48rpx;
  607. color: #FF5B05;
  608. font-size: 32rpx;
  609. font-weight: 500;
  610. transition: all 0.3s ease;
  611. box-shadow: 0 4rpx 16rpx rgba(41, 44, 53, 0.1);
  612. &:active {
  613. transform: translateY(2rpx);
  614. background: rgba(255, 91, 5, 0.05);
  615. }
  616. }
  617. .divider {
  618. display: flex;
  619. align-items: center;
  620. justify-content: center;
  621. margin: 40rpx 0;
  622. .divider-line {
  623. flex: 1;
  624. height: 1rpx;
  625. background: linear-gradient(90deg, transparent, rgba(41, 44, 53, 0.1), transparent);
  626. }
  627. .divider-text {
  628. margin: 0 30rpx;
  629. font-size: 26rpx;
  630. color: rgba(41, 44, 53, 0.5);
  631. }
  632. }
  633. .browse-btn {
  634. display: flex;
  635. align-items: center;
  636. justify-content: center;
  637. width: 100%;
  638. height: 88rpx;
  639. background: transparent;
  640. border: 1rpx solid rgba(41, 44, 53, 0.2);
  641. border-radius: 44rpx;
  642. color: #292C35;
  643. font-size: 28rpx;
  644. transition: all 0.3s ease;
  645. &:active {
  646. background: rgba(41, 44, 53, 0.05);
  647. transform: translateY(1rpx);
  648. }
  649. }
  650. .agreement {
  651. display: flex;
  652. align-items: center;
  653. justify-content: center;
  654. margin-top: 60rpx;
  655. .checkbox-wrapper {
  656. margin-right: 16rpx;
  657. }
  658. .zen-checkbox {
  659. width: 36rpx;
  660. height: 36rpx;
  661. border: 2rpx solid rgba(255, 91, 5, 0.4);
  662. border-radius: 18rpx;
  663. display: flex;
  664. align-items: center;
  665. justify-content: center;
  666. transition: all 0.3s ease;
  667. background: rgba(255, 255, 255, 0.9);
  668. &.checked {
  669. background: #FF5B05;
  670. border-color: #FF5B05;
  671. }
  672. .zen-circle-inner {
  673. width: 16rpx;
  674. height: 16rpx;
  675. border-radius: 50%;
  676. background: #fff;
  677. }
  678. }
  679. .agreement-text {
  680. font-size: 24rpx;
  681. color: rgba(41, 44, 53, 0.6);
  682. line-height: 1.5;
  683. .link {
  684. color: #FF5B05;
  685. text-decoration: none;
  686. }
  687. }
  688. }
  689. }
  690. // 底部装饰
  691. .bottom-decoration {
  692. position: absolute;
  693. bottom: 40rpx;
  694. left: 50%;
  695. transform: translateX(-50%);
  696. z-index: 1;
  697. .company-info {
  698. text-align: center;
  699. .company-name {
  700. display: block;
  701. font-size: 24rpx;
  702. color: rgba(41, 44, 53, 0.6);
  703. margin-bottom: 8rpx;
  704. font-weight: 400;
  705. }
  706. .version {
  707. font-size: 20rpx;
  708. color: rgba(41, 44, 53, 0.4);
  709. font-weight: 300;
  710. }
  711. }
  712. }
  713. // 个人信息设置弹框样式
  714. .profile-modal {
  715. width: 750rpx;
  716. background: #fff;
  717. border-radius: 20rpx;
  718. overflow: hidden;
  719. .modal-header {
  720. text-align: center;
  721. padding: 60rpx 40rpx 40rpx;
  722. background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
  723. .modal-title {
  724. display: block;
  725. font-size: 36rpx;
  726. font-weight: 600;
  727. color: #2c3e50;
  728. margin-bottom: 12rpx;
  729. }
  730. .modal-subtitle {
  731. font-size: 26rpx;
  732. color: rgba(44, 62, 80, 0.7);
  733. }
  734. }
  735. .modal-content {
  736. padding: 40rpx;
  737. .avatar-section {
  738. margin-bottom: 40rpx;
  739. text-align: center;
  740. .section-label {
  741. display: block;
  742. font-size: 28rpx;
  743. color: #2c3e50;
  744. margin-bottom: 20rpx;
  745. font-weight: 500;
  746. }
  747. .avatar-button {
  748. background: none;
  749. border: none;
  750. padding: 0;
  751. margin: 0 auto;
  752. display: block;
  753. width: 160rpx;
  754. height: 160rpx;
  755. &::after {
  756. border: none;
  757. }
  758. &:disabled {
  759. opacity: 0.7;
  760. }
  761. }
  762. .avatar-container {
  763. position: relative;
  764. width: 160rpx;
  765. height: 160rpx;
  766. border-radius: 50%;
  767. overflow: hidden;
  768. border: 3rpx solid #4A90E2;
  769. .avatar-preview {
  770. width: 100%;
  771. height: 100%;
  772. }
  773. .avatar-overlay {
  774. position: absolute;
  775. bottom: 0;
  776. left: 0;
  777. right: 0;
  778. height: 50rpx;
  779. background: rgba(0, 0, 0, 0.5);
  780. display: flex;
  781. align-items: center;
  782. justify-content: center;
  783. }
  784. }
  785. }
  786. .account-section {
  787. .section-label {
  788. display: block;
  789. font-size: 28rpx;
  790. color: #2c3e50;
  791. margin-bottom: 20rpx;
  792. font-weight: 500;
  793. }
  794. }
  795. }
  796. .modal-actions {
  797. padding: 0 40rpx 40rpx;
  798. .confirm-btn {
  799. width: 100%;
  800. height: 80rpx;
  801. background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%);
  802. color: #fff;
  803. border-radius: 12rpx;
  804. font-size: 32rpx;
  805. font-weight: 500;
  806. border: none;
  807. transition: all 0.3s ease;
  808. &:disabled {
  809. background: #bdc3c7;
  810. color: rgba(255, 255, 255, 0.7);
  811. }
  812. &:not(:disabled):active {
  813. transform: translateY(2rpx);
  814. box-shadow: 0 2rpx 8rpx rgba(74, 144, 226, 0.3);
  815. }
  816. }
  817. }
  818. }
  819. </style>