login.vue 23 KB

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