You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

776 lines
16 KiB

1 month ago
  1. <template>
  2. <view class="login-registration-container">
  3. <!-- 自定义导航栏 -->
  4. <view
  5. class="custom-navbar"
  6. :style="{ paddingTop: safeAreaInsets?.top + 'px' }"
  7. >
  8. <!-- <view class="nav-left">
  9. <text class="back-btn" @click="goToIndex"></text>
  10. </view> -->
  11. <view class="nav-right">
  12. <image
  13. class="icons"
  14. src="../../../static/icons/Headset.png"
  15. alt="联系客服"
  16. />
  17. <image
  18. class="icons"
  19. @click="goToIndex"
  20. src="../../../static/icons/Frame.png"
  21. alt="返回首页"
  22. />
  23. </view>
  24. </view>
  25. <!-- Logo -->
  26. <!-- <image class="logo" src="/static/logo.png" mode="aspectFit"></image> -->
  27. <!-- 欢迎语 -->
  28. <text class="welcome-text">登录DeepChart</text>
  29. <!-- 邮箱/手机号切换 -->
  30. <view class="switch-container">
  31. <text
  32. class="switch-item"
  33. :class="{ active: switchType === 'User' }"
  34. @click="switchUser"
  35. >用户名</text
  36. >
  37. <text
  38. class="switch-item"
  39. :class="{ active: switchType === 'Phone' }"
  40. @click="switchPhone"
  41. >手机号</text
  42. ><text
  43. class="switch-item"
  44. :class="{ active: switchType === 'Email' }"
  45. @click="switchEmail"
  46. >邮箱</text
  47. >
  48. </view>
  49. <!-- 输入框 -->
  50. <view class="input-container">
  51. <view v-if="switchType === 'User'">
  52. <!-- 修改邮箱输入框容器将图标包含在内 -->
  53. <view class="input-with-icon">
  54. <image
  55. class="input-icon"
  56. src="../../../static/icons/People-safe.png"
  57. alt=""
  58. />
  59. <input
  60. class="input-field"
  61. type="text"
  62. placeholder="请输入DeepChart ID"
  63. v-model="email"
  64. />
  65. </view>
  66. <view class="input-with-icon">
  67. <image
  68. class="input-icon"
  69. src="../../../static/icons/Unlock.png"
  70. alt=""
  71. />
  72. <input
  73. class="input-field"
  74. type="text"
  75. placeholder="请输入密码"
  76. v-model="password"
  77. />
  78. </view>
  79. </view>
  80. <view v-if="switchType === 'Email'">
  81. <!-- 修改邮箱输入框容器将图标包含在内 -->
  82. <view class="input-with-icon">
  83. <image
  84. class="input-icon"
  85. src="../../../static/icons/Mail.png"
  86. alt=""
  87. />
  88. <input
  89. class="input-field"
  90. type="text"
  91. placeholder="请输入邮箱"
  92. v-model="email"
  93. />
  94. <view>
  95. <button
  96. class="send-code-btn-email"
  97. :disabled="isCodeBtnDisabled"
  98. :class="{ 'send-code-btn-disabled': isCodeBtnDisabled }"
  99. @click="sendCode"
  100. >
  101. <text
  102. class="send-code-text"
  103. :class="{ 'send-code-btn-disabled-text': isCodeBtnDisabled }"
  104. >{{ codeBtnText }}</text
  105. >
  106. </button>
  107. </view>
  108. </view>
  109. <view class="input-with-icon">
  110. <image
  111. class="input-icon"
  112. src="../../../static/icons/Text-recognition.png"
  113. alt=""
  114. />
  115. <input
  116. class="input-field"
  117. type="text"
  118. placeholder="请输入验证码"
  119. v-model="password"
  120. />
  121. </view>
  122. </view>
  123. <view v-if="switchType === 'Phone'" class="phone-input-container">
  124. <view class="country-code-selector" @click="showCountryPicker">
  125. <image
  126. class="country-flag-img"
  127. src="../../../static/icons/Iphone.png"
  128. mode="aspectFit"
  129. ></image>
  130. <text class="country-code">{{ selectedCountry.code }}</text>
  131. <!-- <text class="arrow-down"></text> -->
  132. </view>
  133. <input
  134. class="input-field phone-input"
  135. type="number"
  136. placeholder="输入手机号"
  137. v-model="phone"
  138. @input="onPhoneInput"
  139. />
  140. <view>
  141. <button
  142. class="send-code-btn"
  143. :disabled="isCodeBtnDisabled"
  144. :class="{ 'send-code-btn-disabled': isCodeBtnDisabled }"
  145. @click="sendCode"
  146. >
  147. <text
  148. class="send-code-text"
  149. :class="{ 'send-code-btn-disabled-text': isCodeBtnDisabled }"
  150. >{{ codeBtnText }}</text
  151. >
  152. </button>
  153. </view>
  154. </view>
  155. <view v-if="switchType === 'Phone'" class="input-with-icon">
  156. <image
  157. class="input-icon"
  158. src="../../../static/icons/Text-recognition.png"
  159. alt=""
  160. />
  161. <input
  162. class="input-field"
  163. type="text"
  164. placeholder="请输入验证码"
  165. v-model="password"
  166. />
  167. </view>
  168. </view>
  169. <!-- 用户协议 -->
  170. <view @click="changeCheckbox" class="agreement-container-one">
  171. <image class="checkbox" :src="checkboxUrl"></image>
  172. <text class="agreement-text-one"
  173. >接受 <text class="link" @click="openAgreement">用户协议</text>
  174. <text class="link" @click="openPrivacy">隐私政策</text></text
  175. >
  176. </view>
  177. <view v-if="switchType === 'User'" class="agreement-container">
  178. <text class="agreement-text"
  179. ><text @click="recoverPassword">忘记ID/密码</text>
  180. </text>
  181. </view>
  182. <view v-else class="agreement-container">
  183. <text class="agreement-text">
  184. <!-- 添加占位元素防止页面变形 -->
  185. <text style="visibility: hidden">占位符</text>
  186. </text>
  187. </view>
  188. <!-- 注册按钮 -->
  189. <button class="register-btn" @click="register">登录</button>
  190. <!-- 或者 -->
  191. <text class="or-text" @click="goToRegistration"
  192. >如果您还没有账号点击注册 >
  193. </text>
  194. <!-- 第三方登录 -->
  195. <view class="third-party-login">
  196. <view class="third-party-btn" @click="loginWithApple">
  197. <image
  198. class="apple-icon"
  199. src="../../../static/icons/appleIcons.png"
  200. mode="aspectFit"
  201. ></image>
  202. <text class="third-party-text">通过 Apple 登录 </text>
  203. </view>
  204. <view class="third-party-btn" @click="loginWithGoogle">
  205. <image
  206. class="google-icon"
  207. src="../../../static/icons/GoogleIcons.png"
  208. mode="aspectFit"
  209. ></image>
  210. <text class="third-party-text">通过 Google 登录</text>
  211. </view>
  212. </view>
  213. <footerBar class="static-footer" :type="type"></footerBar>
  214. </view>
  215. </template>
  216. <script setup>
  217. import { ref } from "vue";
  218. // 导入完整的国家列表
  219. import countryList from "./list.js";
  220. import footerBar from '../../../components/footerBar-cn'
  221. const type = ref('member')
  222. const email = ref("");
  223. const password = ref("");
  224. const phone = ref("");
  225. const agreed = ref(false);
  226. const switchType = ref("Email"); // 默认是邮箱注册
  227. const { safeAreaInsets } = uni.getSystemInfoSync();
  228. const codeBtnText = ref("获取验证码");
  229. const isCodeBtnDisabled = ref(false); // 添加验证码按钮禁用状态
  230. const checkboxUrl = ref("../../../static/icons/Check-one-false.png");
  231. // 使用从list.js导入的完整国家列表数据
  232. const countries = ref(
  233. countryList.list.map((item) => ({
  234. name: item.name,
  235. code: `+${item.tel}`,
  236. flag: item.flag,
  237. short: item.short,
  238. en: item.en,
  239. }))
  240. );
  241. // 默认选中的国家(中国)
  242. const selectedCountry = ref(
  243. countries.value.find((country) => country.short === "CN") ||
  244. countries.value[0]
  245. );
  246. // 显示国家选择器
  247. function showCountryPicker() {
  248. uni.showActionSheet({
  249. itemList: countries.value.map(
  250. (country) => `${country.name} ${country.code}`
  251. ),
  252. success: function (res) {
  253. selectedCountry.value = countries.value[res.tapIndex];
  254. },
  255. });
  256. }
  257. function goToIndex() {
  258. // 返回上一页
  259. uni.navigateTo({
  260. url: "/pages/start/index/index",
  261. });
  262. }
  263. function switchUser() {
  264. // 切换到手机注册
  265. switchType.value = "User";
  266. }
  267. function switchEmail() {
  268. // 切换到邮箱注册
  269. switchType.value = "Email";
  270. }
  271. function switchPhone() {
  272. // 切换到手机注册
  273. switchType.value = "Phone";
  274. }
  275. function register() {
  276. if (switchType.value === "Email") {
  277. // 登录逻辑
  278. if (!email.value) {
  279. uni.showToast({
  280. title: "请输入邮箱地址",
  281. icon: "none",
  282. });
  283. return;
  284. }
  285. // 发送登录请求
  286. console.log("登录:", email.value);
  287. }
  288. if (switchType.value === "Phone") {
  289. // 登录逻辑
  290. if (!phone.value) {
  291. uni.showToast({
  292. title: "请输入手机号码",
  293. icon: "none",
  294. });
  295. return;
  296. }
  297. // 发送登录请求
  298. console.log("登录:", phone.value);
  299. }
  300. }
  301. function loginWithApple() {
  302. // Apple登录逻辑
  303. console.log("通过Apple登录");
  304. uni.login({
  305. provider: "apple",
  306. success: function (loginRes) {
  307. // 登录成功
  308. uni.getUserInfo({
  309. provider: "apple",
  310. success: function (info) {
  311. console.log(info);
  312. },
  313. });
  314. },
  315. fail: function (err) {
  316. // 登录授权失败
  317. // err.code错误码参考`授权失败错误码(code)说明`
  318. console.log(err);
  319. },
  320. });
  321. }
  322. function loginWithGoogle() {
  323. // Google登录逻辑
  324. console.log("通过Google登录");
  325. uni.login({
  326. provider: "google",
  327. success: function (loginRes) {
  328. // 登录成功
  329. uni.getUserInfo({
  330. provider: "google",
  331. success: function (info) {
  332. console.log(info);
  333. },
  334. });
  335. },
  336. fail: function (err) {
  337. // 登录授权失败
  338. // err.code是错误码
  339. console.log(err);
  340. },
  341. });
  342. }
  343. function goToRegistration() {
  344. // 跳转到登录页
  345. uni.navigateTo({
  346. url: "/pages/start/Registration/Registration",
  347. });
  348. }
  349. function onPhoneInput(e) {
  350. // 确保只允许输入数字
  351. const value = e.detail.value;
  352. // 使用 isNaN 检查是否为有效数字
  353. if (isNaN(value)) {
  354. phone.value = "";
  355. } else {
  356. phone.value = value;
  357. }
  358. }
  359. function sendCode() {
  360. // 如果按钮已禁用,则不执行后续逻辑
  361. if (isCodeBtnDisabled.value) return;
  362. // 设置按钮为禁用状态
  363. isCodeBtnDisabled.value = true;
  364. codeBtnText.value = "重新发送";
  365. let time = 6;
  366. const timer = setInterval(() => {
  367. time--;
  368. codeBtnText.value = "重新发送 " + time + "s";
  369. if (time <= 0) {
  370. clearInterval(timer);
  371. codeBtnText.value = "重新发送";
  372. // 倒计时结束后启用按钮
  373. isCodeBtnDisabled.value = false;
  374. }
  375. }, 1000);
  376. return;
  377. }
  378. function openAgreement() {
  379. // 打开用户协议
  380. console.log("打开用户协议");
  381. uni.navigateTo({
  382. url: "/pages/start/agreement/agreement",
  383. });
  384. }
  385. function openPrivacy() {
  386. // 打开隐私政策
  387. console.log("打开隐私政策");
  388. uni.navigateTo({
  389. url: "/pages/start/privacy/privacy",
  390. });
  391. }
  392. function recoverPassword() {
  393. // 忘记密码
  394. console.log("忘记密码");
  395. }
  396. function changeCheckbox() {
  397. agreed.value = !agreed.value;
  398. checkboxUrl.value = agreed.value
  399. ? "../../../static/icons/Check-one-true.png"
  400. : "../../../static/icons/Check-one-false.png";
  401. }
  402. </script>
  403. <style scoped>
  404. .login-registration-container {
  405. display: flex;
  406. flex-direction: column;
  407. align-items: center;
  408. justify-content: center;
  409. padding: 0 70rpx;
  410. height: 100vh;
  411. background-color: #ffffff;
  412. }
  413. /* 自定义导航栏样式 */
  414. .custom-navbar {
  415. position: absolute;
  416. top: 0;
  417. left: 0;
  418. /* z-index: 999; */
  419. width: 90%;
  420. height: 80rpx;
  421. display: flex;
  422. justify-content: space-between;
  423. align-items: center;
  424. padding: 10rpx 40rpx;
  425. margin-bottom: 20rpx;
  426. }
  427. .nav-left,
  428. .nav-right {
  429. flex: 1;
  430. }
  431. .nav-right {
  432. display: flex;
  433. justify-content: flex-end;
  434. }
  435. .icons {
  436. margin: 20rpx;
  437. width: 40rpx;
  438. height: 40rpx;
  439. /* margin-right: 10rpx; */
  440. }
  441. .back-btn,
  442. .headphone-btn {
  443. font-size: 36rpx;
  444. font-weight: bold;
  445. color: #333333;
  446. padding: 10rpx;
  447. }
  448. .logo {
  449. width: 120rpx;
  450. height: 120rpx;
  451. margin-bottom: 60rpx;
  452. border-radius: 20%;
  453. }
  454. .welcome-text {
  455. font-size: 48rpx;
  456. font-weight: bold;
  457. color: #333333;
  458. margin-bottom: 60rpx;
  459. /* text-align: left; */
  460. /* align-self: flex-start; */
  461. }
  462. .switch-container {
  463. display: flex;
  464. margin-bottom: 40rpx;
  465. align-self: flex-start;
  466. }
  467. .switch-item {
  468. font-size: 28rpx;
  469. color: #999999;
  470. padding: 10rpx 20rpx;
  471. position: relative;
  472. }
  473. .switch-item::after {
  474. content: "";
  475. position: absolute;
  476. bottom: 0;
  477. left: 50%;
  478. transform: translateX(-50%);
  479. width: 60%;
  480. /* 控制边框宽度 */
  481. height: 2rpx;
  482. background-color: transparent;
  483. }
  484. .switch-item.active {
  485. color: #333333;
  486. font-weight: 700;
  487. }
  488. .switch-item.active::after {
  489. content: "";
  490. position: absolute;
  491. top: 60rpx;
  492. bottom: 0;
  493. left: 50%;
  494. transform: translateX(-50%);
  495. width: 30%;
  496. /* 控制边框宽度 */
  497. height: 7rpx;
  498. background-color: #333333;
  499. }
  500. .input-container {
  501. width: 100%;
  502. }
  503. /* 添加图标输入框样式 */
  504. .input-with-icon {
  505. display: flex;
  506. align-items: center;
  507. width: 100%;
  508. height: 80rpx;
  509. border-bottom: 2rpx solid #e5e5e5;
  510. margin-bottom: 20rpx;
  511. }
  512. .input-icon {
  513. width: 40rpx;
  514. height: 40rpx;
  515. margin: 0 20rpx;
  516. }
  517. .input-field {
  518. flex: 1;
  519. height: 80rpx;
  520. padding: 15rpx 0;
  521. font-size: 28rpx;
  522. color: #333333;
  523. border: none;
  524. background-color: transparent;
  525. }
  526. .phone-input-container {
  527. display: flex;
  528. align-items: center;
  529. width: 95.8%;
  530. height: 80rpx;
  531. /* border-radius: 20rpx; */
  532. /* border: 2rpx solid #e5e5e5; */
  533. /* background-color: #f5f5f5; */
  534. padding: 0 10rpx;
  535. border-bottom: 2rpx solid #e5e5e5;
  536. margin-bottom: 20rpx;
  537. }
  538. .country-code-selector {
  539. display: flex;
  540. align-items: center;
  541. padding: 0 10rpx;
  542. padding-bottom: 1rpx;
  543. height: 100%;
  544. /* border-right: 2rpx solid #e5e5e5; */
  545. /* background-color: #f5f5f5; */
  546. border-radius: 20rpx 0 0 20rpx;
  547. }
  548. .country-code {
  549. font-size: 28rpx;
  550. color: #333333;
  551. margin-right: 10rpx;
  552. }
  553. .country-flag-img {
  554. width: 40rpx;
  555. height: 40rpx;
  556. margin-right: 10rpx;
  557. }
  558. .arrow-down {
  559. font-size: 20rpx;
  560. color: #999999;
  561. }
  562. .phone-input {
  563. flex: 1;
  564. width: auto;
  565. height: 100%;
  566. border: none;
  567. background-color: transparent;
  568. padding: 0 0rpx;
  569. }
  570. .send-code-btn {
  571. width: 200rpx;
  572. height: 60rpx;
  573. display: inline-flex;
  574. padding: 0rpx 10rpx;
  575. justify-content: center;
  576. align-items: center;
  577. gap: 10px;
  578. border-radius: 4px;
  579. background: #000;
  580. }
  581. .send-code-btn-email {
  582. width: 200rpx;
  583. height: 60rpx;
  584. display: inline-flex;
  585. padding: 0rpx 10rpx;
  586. justify-content: center;
  587. align-items: center;
  588. gap: 10px;
  589. border-radius: 4px;
  590. background: #000;
  591. margin-right: 15rpx;
  592. }
  593. .send-code-btn-disabled {
  594. background: #e6e6e6;
  595. /* 禁用状态下的灰色背景 */
  596. }
  597. .send-code-btn-disabled-text {
  598. color: #999999 !important;
  599. }
  600. .send-code-text {
  601. color: #fff;
  602. font-size: 28rpx;
  603. }
  604. .agreement-container-one {
  605. display: flex;
  606. align-items: center;
  607. align-self: flex-start;
  608. margin-bottom: 80rpx;
  609. }
  610. .agreement-container {
  611. display: flex;
  612. align-items: center;
  613. margin-bottom: 30rpx;
  614. margin-top: -60rpx;
  615. align-self: flex-start;
  616. }
  617. .checkbox {
  618. width: 30rpx;
  619. height: 30rpx;
  620. margin-left: 20rpx;
  621. /* flex: content; */
  622. }
  623. .agreement-text-one {
  624. font-size: 22rpx;
  625. color: #666666;
  626. text-align: center;
  627. margin-left: 10rpx;
  628. }
  629. .agreement-text {
  630. margin-left: 20rpx;
  631. font-size: 24rpx;
  632. color: #666666;
  633. white-space: nowrap;
  634. }
  635. .link {
  636. color: #333333;
  637. font-weight: bold;
  638. text-decoration: underline;
  639. }
  640. .register-btn {
  641. width: 100%;
  642. height: 80rpx;
  643. background-color: #000000;
  644. color: white;
  645. font-size: 32rpx;
  646. font-weight: bold;
  647. border-radius: 40rpx;
  648. margin-bottom: 40rpx;
  649. }
  650. .or-text {
  651. font-size: 24rpx;
  652. color: #999999;
  653. margin-bottom: 40rpx;
  654. }
  655. .third-party-login {
  656. width: 100%;
  657. margin-bottom: 60rpx;
  658. }
  659. .third-party-text {
  660. color: #ffffff;
  661. font-weight: bold;
  662. white-space: pre;
  663. }
  664. .third-party-btn {
  665. width: 100%;
  666. height: 80rpx;
  667. background-color: rgb(0, 0, 0);
  668. border: 2rpx solid #e5e5e5;
  669. border-radius: 40rpx;
  670. display: flex;
  671. align-items: center;
  672. justify-content: center;
  673. margin-bottom: 20rpx;
  674. font-size: 28rpx;
  675. color: #333333;
  676. }
  677. .google-icon,
  678. .apple-icon {
  679. width: 60rpx;
  680. height: 60rpx;
  681. margin-right: 20rpx;
  682. }
  683. .existing-account {
  684. display: flex;
  685. align-items: center;
  686. }
  687. .account-text {
  688. font-size: 24rpx;
  689. color: #666666;
  690. }
  691. .login-link {
  692. font-size: 24rpx;
  693. font-weight: bold;
  694. color: #333333;
  695. margin-left: 10rpx;
  696. text-decoration: underline;
  697. }
  698. .static-footer {
  699. position: fixed;
  700. bottom: 0;
  701. }
  702. </style>