login.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996
  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/index/index'
  337. });
  338. },
  339. // 跳转到手机登录页面
  340. handlePhoneLogin() {
  341. uni.navigateTo({
  342. url: '/pagesA/public/phone-login'
  343. });
  344. },
  345. // 保存登录数据并跳转
  346. saveLoginDataAndRedirect(loginData) {
  347. uni.setStorageSync('access_token', loginData.access_token);
  348. uni.setStorageSync('refresh_token', loginData.refresh_token);
  349. uni.setStorageSync('user', loginData);
  350. this.$store.commit('isLogin', true);
  351. this.$store.commit('refresh_token', loginData.refresh_token);
  352. uni.showToast({
  353. title: '登录成功',
  354. icon: 'success',
  355. duration: 1000
  356. });
  357. // 登录成功后的跳转
  358. setTimeout(() => {
  359. uni.switchTab({
  360. url: '/pages/index/index'
  361. });
  362. }, 1000);
  363. },
  364. // 微信小程序头像选择回调
  365. async onChooseAvatar(e) {
  366. console.log('选择头像:', e.detail.avatarUrl);
  367. // 显示上传状态
  368. this.isUploadingAvatar = true;
  369. uni.showLoading({
  370. title: '上传头像中...',
  371. mask: true
  372. });
  373. try {
  374. // 上传头像到服务器
  375. const uploadResult = await this.uploadFilePromise(e.detail.avatarUrl);
  376. if (uploadResult && uploadResult.data && uploadResult.data.link) {
  377. this.tempAvatar = uploadResult.data.link;
  378. uni.showToast({
  379. title: '头像上传成功',
  380. icon: 'success'
  381. });
  382. } else {
  383. throw new Error('上传失败');
  384. }
  385. } catch (error) {
  386. console.error('头像上传失败:', error);
  387. uni.showToast({
  388. title: '头像上传失败,请重试',
  389. icon: 'none'
  390. });
  391. } finally {
  392. this.isUploadingAvatar = false;
  393. uni.hideLoading();
  394. }
  395. },
  396. // 上传文件到服务器
  397. uploadFilePromise(filePath) {
  398. return new Promise((resolve, reject) => {
  399. // 优先使用登录数据中的token,如果没有则使用本地存储的token
  400. const token = (this.loginData && this.loginData.access_token) || uni.getStorageSync(
  401. 'access_token');
  402. if (!token) {
  403. reject(new Error('未找到访问令牌'));
  404. return;
  405. }
  406. uni.uploadFile({
  407. url: UPLOAD_URL,
  408. filePath: filePath,
  409. header: {
  410. "Blade-Auth": token
  411. },
  412. name: 'file',
  413. formData: {
  414. user: 'avatar'
  415. },
  416. success: (res) => {
  417. try {
  418. const result = JSON.parse(res.data);
  419. if (result.success || result.code === 200) {
  420. resolve(result);
  421. } else {
  422. reject(new Error(result.msg || '上传失败'));
  423. }
  424. } catch (e) {
  425. reject(new Error('解析响应失败'));
  426. }
  427. },
  428. fail: (error) => {
  429. reject(error);
  430. }
  431. });
  432. });
  433. },
  434. // 确认个人信息设置
  435. async confirmProfile() {
  436. if (!this.tempAccount.trim()) {
  437. uni.showToast({
  438. title: '请输入账号',
  439. icon: 'none'
  440. });
  441. return;
  442. }
  443. uni.showLoading({
  444. title: '保存中...',
  445. mask: true
  446. });
  447. try {
  448. // 调用更新用户信息接口
  449. const updateRes = await updateProfile({
  450. account: this.tempAccount.trim(),
  451. avatar: this.tempAvatar
  452. });
  453. if (updateRes.code == 200) {
  454. // 更新登录数据中的用户信息
  455. this.loginData.account = this.tempAccount.trim();
  456. if (this.tempAvatar) {
  457. this.loginData.avatar = this.tempAvatar;
  458. this.loginData.avatarUrl = this.tempAvatar; // 同时更新 avatarUrl 字段
  459. }
  460. // 更新本地存储的用户信息
  461. uni.setStorageSync('user', this.loginData);
  462. // 保存登录数据并跳转
  463. this.saveLoginDataAndRedirect(this.loginData);
  464. this.showProfileModal = false;
  465. } else {
  466. uni.showToast({
  467. title: updateRes.msg || '保存失败,请重试',
  468. icon: 'none'
  469. });
  470. }
  471. } catch (error) {
  472. console.error('更新用户信息错误:', error);
  473. uni.showToast({
  474. title: '保存失败,请重试',
  475. icon: 'none'
  476. });
  477. } finally {
  478. uni.hideLoading();
  479. }
  480. }
  481. }
  482. }
  483. </script>
  484. <style lang="scss" scoped>
  485. .login-page {
  486. min-height: 100vh;
  487. background: #F5F5F5;
  488. position: relative;
  489. overflow: hidden;
  490. }
  491. .status-bar {
  492. height: var(--status-bar-height);
  493. width: 100%;
  494. }
  495. // 现代化背景
  496. .modern-bg {
  497. position: absolute;
  498. top: 0;
  499. left: 0;
  500. width: 100%;
  501. height: 100%;
  502. pointer-events: none;
  503. z-index: 1;
  504. .bg-shape {
  505. position: absolute;
  506. border-radius: 50%;
  507. &.shape-1 {
  508. width: 300rpx;
  509. height: 300rpx;
  510. top: 15%;
  511. right: -80rpx;
  512. background: linear-gradient(135deg, rgba(255, 91, 5, 0.1) 0%, transparent 70%);
  513. }
  514. &.shape-2 {
  515. width: 200rpx;
  516. height: 200rpx;
  517. top: 50%;
  518. left: -60rpx;
  519. background: linear-gradient(135deg, rgba(41, 44, 53, 0.08) 0%, transparent 70%);
  520. }
  521. &.shape-3 {
  522. width: 150rpx;
  523. height: 150rpx;
  524. bottom: 25%;
  525. right: 15%;
  526. background: linear-gradient(135deg, rgba(255, 91, 5, 0.06) 0%, transparent 70%);
  527. }
  528. }
  529. }
  530. .nav-back {
  531. position: fixed;
  532. left: 30rpx;
  533. top: calc(var(--status-bar-height) + 20rpx);
  534. z-index: 100;
  535. .back-btn {
  536. width: 80rpx;
  537. height: 80rpx;
  538. background: #FFFFFF;
  539. border: 1rpx solid rgba(255, 91, 5, 0.2);
  540. border-radius: 50%;
  541. display: flex;
  542. align-items: center;
  543. justify-content: center;
  544. transition: all 0.3s ease;
  545. box-shadow: 0 4rpx 16rpx rgba(41, 44, 53, 0.1);
  546. &:active {
  547. transform: scale(0.95);
  548. background: rgba(255, 91, 5, 0.1);
  549. }
  550. }
  551. .back-icon {
  552. font-family: "iconfont";
  553. font-size: 36rpx;
  554. color: #292C35;
  555. }
  556. }
  557. .header {
  558. display: flex;
  559. flex-direction: column;
  560. align-items: center;
  561. padding-top: 120rpx;
  562. position: relative;
  563. z-index: 2;
  564. .logo-container {
  565. position: relative;
  566. margin-bottom: 60rpx;
  567. .logo-circle {
  568. width: 200rpx;
  569. height: 200rpx;
  570. border-radius: 20rpx;
  571. background: #FFFFFF;
  572. display: flex;
  573. align-items: center;
  574. justify-content: center;
  575. box-shadow: 0 8rpx 24rpx rgba(41, 44, 53, 0.1);
  576. border: 2rpx solid rgba(255, 91, 5, 0.2);
  577. .logo {
  578. width: 160rpx;
  579. height: 160rpx;
  580. border-radius: 16rpx;
  581. }
  582. }
  583. .logo-shadow {
  584. position: absolute;
  585. bottom: -20rpx;
  586. left: 50%;
  587. transform: translateX(-50%);
  588. width: 160rpx;
  589. height: 20rpx;
  590. background: radial-gradient(ellipse, rgba(255, 91, 5, 0.1) 0%, transparent 70%);
  591. border-radius: 50%;
  592. }
  593. }
  594. .title-section {
  595. text-align: center;
  596. margin-bottom: 20rpx;
  597. position: relative;
  598. .title {
  599. font-size: 64rpx;
  600. font-weight: 600;
  601. color: #292C35;
  602. letter-spacing: 8rpx;
  603. font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
  604. text-shadow: 0 2rpx 8rpx rgba(41, 44, 53, 0.1);
  605. }
  606. .title-underline {
  607. position: absolute;
  608. bottom: -10rpx;
  609. left: 50%;
  610. transform: translateX(-50%);
  611. width: 80rpx;
  612. height: 4rpx;
  613. background: linear-gradient(90deg, transparent, #FF5B05, transparent);
  614. border-radius: 2rpx;
  615. }
  616. }
  617. .subtitle {
  618. font-size: 28rpx;
  619. color: rgba(41, 44, 53, 0.7);
  620. font-weight: 300;
  621. letter-spacing: 4rpx;
  622. font-family: inherit;
  623. }
  624. }
  625. .login-section {
  626. padding: 80rpx 60rpx;
  627. position: relative;
  628. z-index: 2;
  629. .btn-container {
  630. margin-bottom: 40rpx;
  631. }
  632. .wechat-btn {
  633. position: relative;
  634. display: flex;
  635. align-items: center;
  636. justify-content: center;
  637. width: 100%;
  638. height: 96rpx;
  639. background: linear-gradient(135deg, #FF5B05 0%, #FF8E3C 100%);
  640. border: none;
  641. border-radius: 48rpx;
  642. color: #fff;
  643. font-size: 32rpx;
  644. font-weight: 500;
  645. transition: all 0.3s ease;
  646. box-shadow: 0 4rpx 16rpx rgba(255, 91, 5, 0.3);
  647. &:active {
  648. transform: translateY(2rpx);
  649. box-shadow: 0 2rpx 8rpx rgba(255, 91, 5, 0.4);
  650. }
  651. &.btn-loading {
  652. background: linear-gradient(135deg, #FFB088 0%, #FFA573 100%);
  653. color: rgba(255, 255, 255, 0.8);
  654. }
  655. }
  656. .phone-btn {
  657. display: flex;
  658. align-items: center;
  659. justify-content: center;
  660. width: 100%;
  661. height: 96rpx;
  662. background: #FFFFFF;
  663. border: 2rpx solid #FF5B05;
  664. border-radius: 48rpx;
  665. color: #FF5B05;
  666. font-size: 32rpx;
  667. font-weight: 500;
  668. transition: all 0.3s ease;
  669. box-shadow: 0 4rpx 16rpx rgba(41, 44, 53, 0.1);
  670. &:active {
  671. transform: translateY(2rpx);
  672. background: rgba(255, 91, 5, 0.05);
  673. }
  674. }
  675. .divider {
  676. display: flex;
  677. align-items: center;
  678. justify-content: center;
  679. margin: 40rpx 0;
  680. .divider-line {
  681. flex: 1;
  682. height: 1rpx;
  683. background: linear-gradient(90deg, transparent, rgba(41, 44, 53, 0.1), transparent);
  684. }
  685. .divider-text {
  686. margin: 0 30rpx;
  687. font-size: 26rpx;
  688. color: rgba(41, 44, 53, 0.5);
  689. }
  690. }
  691. .browse-btn {
  692. display: flex;
  693. align-items: center;
  694. justify-content: center;
  695. width: 100%;
  696. height: 88rpx;
  697. background: transparent;
  698. border: 1rpx solid rgba(41, 44, 53, 0.2);
  699. border-radius: 44rpx;
  700. color: #292C35;
  701. font-size: 28rpx;
  702. transition: all 0.3s ease;
  703. &:active {
  704. background: rgba(41, 44, 53, 0.05);
  705. transform: translateY(1rpx);
  706. }
  707. }
  708. .agreement {
  709. display: flex;
  710. align-items: center;
  711. justify-content: center;
  712. margin-top: 60rpx;
  713. .checkbox-wrapper {
  714. margin-right: 16rpx;
  715. }
  716. .zen-checkbox {
  717. width: 36rpx;
  718. height: 36rpx;
  719. border: 2rpx solid rgba(255, 91, 5, 0.4);
  720. border-radius: 18rpx;
  721. display: flex;
  722. align-items: center;
  723. justify-content: center;
  724. transition: all 0.3s ease;
  725. background: rgba(255, 255, 255, 0.9);
  726. &.checked {
  727. background: #FF5B05;
  728. border-color: #FF5B05;
  729. }
  730. .zen-circle-inner {
  731. width: 16rpx;
  732. height: 16rpx;
  733. border-radius: 50%;
  734. background: #fff;
  735. }
  736. }
  737. .agreement-text {
  738. font-size: 24rpx;
  739. color: rgba(41, 44, 53, 0.6);
  740. line-height: 1.5;
  741. .link {
  742. color: #FF5B05;
  743. text-decoration: none;
  744. }
  745. }
  746. }
  747. }
  748. // 底部装饰
  749. .bottom-decoration {
  750. position: absolute;
  751. bottom: 40rpx;
  752. left: 50%;
  753. transform: translateX(-50%);
  754. z-index: 1;
  755. .company-info {
  756. text-align: center;
  757. .company-name {
  758. display: block;
  759. font-size: 24rpx;
  760. color: rgba(41, 44, 53, 0.6);
  761. margin-bottom: 8rpx;
  762. font-weight: 400;
  763. }
  764. .version {
  765. font-size: 20rpx;
  766. color: rgba(41, 44, 53, 0.4);
  767. font-weight: 300;
  768. }
  769. }
  770. }
  771. // 个人信息设置弹框样式
  772. .profile-modal {
  773. width: 750rpx;
  774. background: #fff;
  775. border-radius: 20rpx;
  776. overflow: hidden;
  777. .modal-header {
  778. text-align: center;
  779. padding: 60rpx 40rpx 40rpx;
  780. background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
  781. .modal-title {
  782. display: block;
  783. font-size: 36rpx;
  784. font-weight: 600;
  785. color: #2c3e50;
  786. margin-bottom: 12rpx;
  787. }
  788. .modal-subtitle {
  789. font-size: 26rpx;
  790. color: rgba(44, 62, 80, 0.7);
  791. }
  792. }
  793. .modal-content {
  794. padding: 40rpx;
  795. .avatar-section {
  796. margin-bottom: 40rpx;
  797. text-align: center;
  798. .section-label {
  799. display: block;
  800. font-size: 28rpx;
  801. color: #2c3e50;
  802. margin-bottom: 20rpx;
  803. font-weight: 500;
  804. }
  805. .avatar-button {
  806. background: none;
  807. border: none;
  808. padding: 0;
  809. margin: 0 auto;
  810. display: block;
  811. width: 160rpx;
  812. height: 160rpx;
  813. &::after {
  814. border: none;
  815. }
  816. &:disabled {
  817. opacity: 0.7;
  818. }
  819. }
  820. .avatar-container {
  821. position: relative;
  822. width: 160rpx;
  823. height: 160rpx;
  824. border-radius: 50%;
  825. overflow: hidden;
  826. border: 3rpx solid #4A90E2;
  827. .avatar-preview {
  828. width: 100%;
  829. height: 100%;
  830. }
  831. .avatar-overlay {
  832. position: absolute;
  833. bottom: 0;
  834. left: 0;
  835. right: 0;
  836. height: 50rpx;
  837. background: rgba(0, 0, 0, 0.5);
  838. display: flex;
  839. align-items: center;
  840. justify-content: center;
  841. }
  842. }
  843. }
  844. .account-section {
  845. .section-label {
  846. display: block;
  847. font-size: 28rpx;
  848. color: #2c3e50;
  849. margin-bottom: 20rpx;
  850. font-weight: 500;
  851. }
  852. }
  853. }
  854. .modal-actions {
  855. padding: 0 40rpx 40rpx;
  856. .confirm-btn {
  857. width: 100%;
  858. height: 80rpx;
  859. background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%);
  860. color: #fff;
  861. border-radius: 12rpx;
  862. font-size: 32rpx;
  863. font-weight: 500;
  864. border: none;
  865. transition: all 0.3s ease;
  866. &:disabled {
  867. background: #bdc3c7;
  868. color: rgba(255, 255, 255, 0.7);
  869. }
  870. &:not(:disabled):active {
  871. transform: translateY(2rpx);
  872. box-shadow: 0 2rpx 8rpx rgba(74, 144, 226, 0.3);
  873. }
  874. }
  875. }
  876. }
  877. </style>