index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. <template>
  2. <view class="login-page" :isHeaderFixed="true" :isLoginPage="true">
  3. <view class="fixed" />
  4. <view class="main-content">
  5. <!-- <cwg-match-media :min-width="991">-->
  6. <!-- <view class="global-header-bar pc-header">-->
  7. <!-- <view class="header-inner">-->
  8. <!-- <view class="logo-placeholder"></view> &lt;!&ndash; 左侧可预留放logo或留空 &ndash;&gt;-->
  9. <!-- <LoginHeaderGroup text-color="#fff" icon-color="#fff" />-->
  10. <!-- </view>-->
  11. <!-- </view>-->
  12. <!-- </cwg-match-media>-->
  13. <!-- 移动端顶部栏:悬浮在上方,深色文字 -->
  14. <!-- <cwg-match-media :max-width="991">-->
  15. <!-- <view class="mobile-header-bar">-->
  16. <!-- &lt;!&ndash; <view class="fixed"/>&ndash;&gt;-->
  17. <!-- <LoginHeaderGroup text-color="#141d22" icon-color="#141d22" />-->
  18. <!-- </view>-->
  19. <!-- </cwg-match-media>-->
  20. <uni-row class="demo-uni-row">
  21. <cwg-match-media :min-width="991" style="height: 100%;">
  22. <uni-col :xs="24" :sm="24" :md="12" :lg="14" :xl="16" style="height: 100%">
  23. <view class="left-bg">
  24. <view class="left-box">
  25. <view class="inner">
  26. <view class="title w-700">
  27. {{ `${t('newLoop.item12')} ` }}
  28. <text class="color-white" v-t="'newLoop.item13'"></text>
  29. </view>
  30. <view class="text-white" v-t="'newLoop.item14'"></view>
  31. </view>
  32. <image src="/static/images/trust-pilot.png" class="img-fluid mt--10" mode="widthFix"></image>
  33. <view class="left-content">
  34. <view class="des text-white">
  35. <text v-html="t('newSignin.item12')"></text>
  36. <br />
  37. <text v-html="t('newSignin.item12_1')"></text>
  38. <br />
  39. <text v-html="t('newSignin.item10')"></text>
  40. <br />
  41. <text v-html="t('newSignin.item11')"></text>
  42. <br />
  43. <text v-t="'newSignin.item13'"></text>
  44. <cwg-link type="pdf" :title="'newSignin.item13_1'"
  45. :url="`pdf/Risk-Disclosures-and-Acknowledgements-2020-08.pdf`" target="_blank"
  46. v-t="'newSignin.item13_1'" class="doc-link" />
  47. <text v-t="'newSignin.item13_2'"></text>
  48. <!-- <view v-t="'newSignin.item13_3'"></view>
  49. <text v-t="'newSignin.item13_4'"></text> -->
  50. </view>
  51. </view>
  52. </view>
  53. </view>
  54. </uni-col>
  55. </cwg-match-media>
  56. <uni-col :xs="24" :sm="24" :md="12" :lg="10" :xl="8" class="right-f">
  57. <view class="account">
  58. <cwg-match-media :max-width="991">
  59. <view class="company u-flex-y u-flex-y-center">
  60. <image src="/static/images/logo-full.svg" class="company-icon" mode="widthFix"></image>
  61. </view>
  62. </cwg-match-media>
  63. <view class="title">
  64. <view class="tit1">{{ t('newSignin.item1') }}</view>
  65. <view class="tit2">{{ t('newSignin.item2') }}</view>
  66. </view>
  67. <view>
  68. <up-form :model="form" ref="uFormRef">
  69. <up-form-item label="" prop="loginName">
  70. <up-input :customStyle="customStyle" v-model="form.loginName" border="none"
  71. :placeholder="t('signin.form.email')">
  72. <template #prefix>
  73. <cwg-icon name="email-outline" :size="20" color="#000" />
  74. </template>
  75. </up-input>
  76. </up-form-item>
  77. <up-form-item label="" prop="password">
  78. <up-input :customStyle="customStyle" v-model="form.password" :type="inputType" border="none"
  79. :placeholder="t('signin.form.password')">
  80. <template #prefix>
  81. <cwg-icon name="lock-outline" :size="20" color="#000" />
  82. </template>
  83. </up-input>
  84. </up-form-item>
  85. </up-form>
  86. </view>
  87. <view class="u-flex u-flex-between u-flex-y-center mb1">
  88. <view class="check-box">
  89. <up-checkbox-group v-model="remenber" @change="checkboxChange">
  90. <up-checkbox size="14" labelSize="14" labelColor="#666666" activeColor="#ea002a"
  91. :label="t('newSignin.item5')" name="记住我" class="wcg-checkbox"></up-checkbox>
  92. </up-checkbox-group>
  93. </view>
  94. <navigator url="/pages/login/reset" class="account-tip">
  95. <text>{{ t('signin.forget') }}</text>
  96. </navigator>
  97. </view>
  98. <view class="cwg-button">
  99. <u-button type="primary" class="" @click="submit">
  100. {{ t('signin.login') }}
  101. </u-button>
  102. </view>
  103. <navigator url="/pages/login/regist" class="account-tip">
  104. {{ t('signin.words') }}
  105. <text>{{ t('signin.signup') }}</text>
  106. </navigator>
  107. <cwg-match-media :min-width="791">
  108. <!-- <view class="qr-container">
  109. <view class="qr-title">
  110. <view class="line"></view>
  111. <view class="qr-tit2">{{ t('newSignin.item2') }}</view>
  112. <view class="line"></view>
  113. </view>
  114. <QrCode width="200" height="200" text="cardGuide" :logo="logoImage"></QrCode>
  115. </view> -->
  116. </cwg-match-media>
  117. </view>
  118. </uni-col>
  119. </uni-row>
  120. </view>
  121. <view class="chat-icon" :class="{ 'chat-icon-expanded': isChatIconExpanded, 'chat-icon-hidden': type == 'chat' }"
  122. @click="handleChatIconClick">
  123. <cwg-icon name="chat" color="#fff" />
  124. </view>
  125. </view>
  126. </template>
  127. <script setup>
  128. import { ref, onMounted, computed } from 'vue'
  129. import QrCode from '@/components/QrCode.vue'
  130. import { post } from '@/utils/request'
  131. import { userToken } from '@/composables/config'
  132. import { userApi } from '@/api/user'
  133. import { ucardApi } from '@/api/ucard'
  134. import { customApi } from '@/service/custom'
  135. import useGlobalStore from '@/stores/use-global-store'
  136. import useUserStore from '@/stores/use-user-store'
  137. import useRouter from '@/hooks/useRouter'
  138. import { useI18n } from 'vue-i18n'
  139. import companyLogo from '@/static/images/logo4.png'
  140. import LoginHeaderGroup from './components/LoginHeaderGroup.vue'
  141. import LiveChatService from '@/utils/liveChat.js'
  142. import { useWindowWidth } from '@/composables/useWindowWidth'
  143. const windowWidth = useWindowWidth(300)
  144. const isMobile = computed(() => windowWidth.value <= 991)
  145. const router = useRouter()
  146. const { t } = useI18n()
  147. const userStore = useUserStore()
  148. const globalStore = useGlobalStore()
  149. const modeStore = computed(() => globalStore.mode)
  150. // 响应式表单数据
  151. const form = ref({
  152. loginName: '',
  153. password: '',
  154. })
  155. function submit() {
  156. if (!form.value.loginName) {
  157. uni.$u.toast(t('signin.form.email'))
  158. return
  159. }
  160. if (!form.value.password) {
  161. uni.$u.toast(t('signin.form.password'))
  162. return
  163. }
  164. handleLogin()
  165. }
  166. const customStyle = {
  167. height: '44px',
  168. 'border-radius': '8px',
  169. background: '#f7f8fa',
  170. padding: '0 20px !important',
  171. position: 'relative',
  172. }
  173. const remenber = ref([])
  174. const checkboxChange = (e) => {
  175. remenber.value = e
  176. }
  177. const fetchUserList = (params) => post('/Login/AcctLogin', params)
  178. async function handleLogin() {
  179. try {
  180. const res = await userApi.login({
  181. loginName: form.value.loginName,
  182. password: form.value.password,
  183. })
  184. if (res.code === 200) {
  185. userToken.value = res.data
  186. uni.$u.toast(t('login.msg0_1'))
  187. getCustomLoginInfo()
  188. // getCardUserInfo();
  189. reasonsRefusalList()
  190. if (remenber.value.length) {
  191. userStore.saveAccountInfo({
  192. loginName: form.value.loginName,
  193. password: form.value.password,
  194. rememberPassword: true,
  195. })
  196. } else {
  197. userStore.saveAccountInfo({
  198. loginName: '',
  199. password: '',
  200. rememberPassword: false,
  201. })
  202. }
  203. // console.log(1111);
  204. } else {
  205. uni.showToast({ title: res.msg })
  206. // console.log(12112);
  207. }
  208. } catch (error) {
  209. // console.log(error)
  210. uni.showToast({ title: error.msg, icon: 'none' })
  211. // console.log(error, 19089);
  212. }
  213. }
  214. async function getCustomLoginInfo() {
  215. try {
  216. const res = await userApi.getUserInfo()
  217. userStore.saveUserInfo(res.data)
  218. if (res.code === 200) {
  219. switch (modeStore.value) {
  220. case 'customer':
  221. router.reLaunch('/pages/customer/index')
  222. break;
  223. case 'ib':
  224. router.reLaunch('/pages/ib/index')
  225. break;
  226. case 'follow':
  227. router.reLaunch('/pages/follow/index')
  228. break;
  229. default:
  230. break;
  231. }
  232. } else {
  233. uni.$u.toast(res.msg || t('login.msg0'))
  234. }
  235. } catch (error) {
  236. // console.log(error, 111);
  237. }
  238. }
  239. async function getCardUserInfo() {
  240. try {
  241. const res = await ucardApi.getSingle()
  242. userStore.saveUserInfo(res.data)
  243. if (res.code === 200) {
  244. if (!res.data || res.data.approveStatus != 2) {
  245. router.push('/pages/mine/improve')
  246. } else {
  247. router.push('/pages/card/index')
  248. }
  249. } else {
  250. uni.$u.toast(res.msg || t('login.msg0'))
  251. }
  252. } catch (error) {
  253. // console.log(error, 111);
  254. }
  255. }
  256. async function reasonsRefusalList() {
  257. try {
  258. const res = await customApi.reasonsRefusalList()
  259. if (res.code === 200) {
  260. pickFields(res.data)
  261. } else {
  262. uni.$u.toast(res.msg || t('login.msg0'))
  263. }
  264. } catch (error) {
  265. // console.log(error, 111);
  266. }
  267. }
  268. function pickFields(source, fields = ['content', 'enContent']) {
  269. const result = {}
  270. Object.entries(source).forEach(([key, value]) => {
  271. result[key] = fields.reduce((acc, f) => {
  272. acc[f] = value[f] ?? null
  273. return acc
  274. }, {})
  275. })
  276. userStore.saveReasonsOptions(result)
  277. }
  278. onMounted(() => {
  279. const accountInfo = userStore.accountInfo
  280. if (accountInfo?.rememberPassword) {
  281. form.value.loginName = accountInfo?.loginName || ''
  282. form.value.password = accountInfo?.password || ''
  283. remenber.value = ['记住我']
  284. } else {
  285. form.value.loginName = ''
  286. form.value.password = ''
  287. remenber.value = []
  288. }
  289. })
  290. const inputType = ref('password')
  291. const isChatIconExpanded = ref(false)
  292. // 处理聊天图标点击
  293. const handleChatIconClick = () => {
  294. // 如果还没显示 → 先滑出来
  295. if (!isChatIconExpanded.value) {
  296. isChatIconExpanded.value = true
  297. return
  298. }
  299. if (isMobile.value) {
  300. router.push('/pages/common/chat')
  301. } else {
  302. if (LiveChatService) {
  303. LiveChatService.showChat();
  304. }
  305. }
  306. setTimeout(() => {
  307. isChatIconExpanded.value = false
  308. }, 300)
  309. }
  310. </script>
  311. <style lang="scss" scoped>
  312. @import "@/uni.scss";
  313. :deep(uni-content) {
  314. padding-left: 0 !important;
  315. }
  316. .login-page {
  317. height: 100vh;
  318. border: none;
  319. padding: 0;
  320. position: relative;
  321. display: flex;
  322. flex-direction: column;
  323. }
  324. .chat-icon {
  325. width: px2rpx(50);
  326. height: px2rpx(50);
  327. border-radius: 50%;
  328. background-color: #cf1322;
  329. display: flex;
  330. align-items: center;
  331. justify-content: center;
  332. position: fixed;
  333. bottom: px2rpx(25);
  334. right: px2rpx(-25);
  335. z-index: 999;
  336. cursor: pointer;
  337. transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  338. box-shadow: 0 px2rpx(8) px2rpx(20) rgba(0, 0, 0, 0.15);
  339. will-change: transform;
  340. }
  341. .chat-icon:hover {
  342. transform: scale(1.1);
  343. }
  344. .chat-icon-expanded {
  345. bottom: px2rpx(20);
  346. right: px2rpx(20);
  347. transform: scale(1.1);
  348. box-shadow: 0 px2rpx(12) px2rpx(30) rgba(0, 0, 0, 0.2);
  349. }
  350. .chat-icon-hidden {
  351. display: none;
  352. }
  353. .fixed {
  354. width: 100%;
  355. height: var(--status-bar-height);
  356. background-color: var(--color-white);
  357. z-index: 9;
  358. }
  359. .global-header-bar {
  360. width: 100%;
  361. height: px2rpx(60);
  362. display: flex;
  363. align-items: center;
  364. justify-content: center;
  365. flex-shrink: 0;
  366. z-index: 100;
  367. &.pc-header {
  368. background-color: transparent;
  369. }
  370. .header-inner {
  371. width: 100%;
  372. padding: 0 5%;
  373. display: flex;
  374. justify-content: space-between;
  375. /* 两端对齐,可放logo和组件 */
  376. align-items: center;
  377. }
  378. }
  379. .mobile-header-bar {
  380. //position: absolute;
  381. //top: px2rpx(20);
  382. //right: px2rpx(20);
  383. z-index: 10;
  384. width: 100%;
  385. display: flex;
  386. background-color: var(--color-white);
  387. justify-content: flex-end;
  388. }
  389. .main-content {
  390. flex: 1;
  391. overflow: hidden;
  392. }
  393. .demo-uni-row {
  394. height: 100%;
  395. margin: 0 !important;
  396. .left-bg {
  397. height: 100%;
  398. min-height: calc(100vh - 120px);
  399. background-image: url(/static/images/login-bg.gif);
  400. background-size: cover;
  401. background-position: center;
  402. display: flex;
  403. flex-direction: column;
  404. align-items: center;
  405. justify-content: center;
  406. .left-box {
  407. display: flex;
  408. flex-direction: column;
  409. justify-content: center;
  410. align-items: flex-start;
  411. width: 60%;
  412. //margin-top: px2rpx(20);
  413. .inner {
  414. width: 100%;
  415. text-align: start;
  416. margin-bottom: px2rpx(20);
  417. .section-title {
  418. margin-top: px2rpx(10);
  419. margin-bottom: px2rpx(10);
  420. }
  421. .title {
  422. font-size: px2rpx(50);
  423. line-height: 1;
  424. color: #fff;
  425. font-weight: 700;
  426. text-align: left;
  427. }
  428. .w-700 {
  429. font-weight: 700;
  430. }
  431. .subtitle {
  432. width: 45%;
  433. font-size: px2rpx(18);
  434. letter-spacing: px2rpx(1);
  435. display: block;
  436. margin-bottom: px2rpx(24);
  437. color: #ffffff;
  438. line-height: px2rpx(15);
  439. font-weight: 500;
  440. padding: px2rpx(10) px2rpx(20);
  441. border-radius: px2rpx(100);
  442. text-transform: uppercase;
  443. background-color: #e61f1e;
  444. text-align: center;
  445. }
  446. .w-40 {
  447. max-width: 40%;
  448. }
  449. .text-white {
  450. margin-top: px2rpx(10);
  451. font-size: px2rpx(14);
  452. line-height: 1.6;
  453. color: #fff;
  454. }
  455. }
  456. .img-fluid {
  457. width: 100%;
  458. max-width: px2rpx(240);
  459. }
  460. .mt--10 {
  461. margin-top: px2rpx(10);
  462. }
  463. .h1 {
  464. // text-align: center;
  465. color: #fff;
  466. font-size: 30px;
  467. margin-top: 30px;
  468. line-height: 1.5;
  469. }
  470. .h6 {
  471. text-align: start;
  472. line-height: 20px;
  473. color: #fff;
  474. font-size: 14px;
  475. margin-top: 10px;
  476. }
  477. .company {
  478. padding: px2rpx(10) 0 px2rpx(10) 0;
  479. position: relative;
  480. align-items: flex-start !important;
  481. width: 100%;
  482. }
  483. }
  484. .left-content {
  485. width: 100%;
  486. .des {
  487. text-align: start;
  488. line-height: 24px;
  489. color: #fff;
  490. font-size: 14px;
  491. margin-top: px2rpx(20);
  492. :nth-child(n) {
  493. display: inline;
  494. word-break: break-all;
  495. word-wrap: break-word;
  496. }
  497. .doc-link {
  498. color: var(--color-error);
  499. text-decoration: underline;
  500. margin: 0 px2rpx(4);
  501. }
  502. }
  503. }
  504. }
  505. .right-f {
  506. background-color: var(--color-white);
  507. padding: 0 px2rpx(24);
  508. height: 100%;
  509. box-sizing: border-box;
  510. .account {
  511. background-color: var(--color-white);
  512. position: relative;
  513. height: calc(100vh - 120px);
  514. display: flex;
  515. flex-direction: column;
  516. justify-content: center;
  517. padding: 0 10%;
  518. .company {
  519. padding: px2rpx(50) 0 px2rpx(20) 0;
  520. position: relative;
  521. align-items: center !important;
  522. }
  523. .company-icon {
  524. width: px2rpx(234);
  525. }
  526. }
  527. }
  528. }
  529. .bottom-box {
  530. width: 100%;
  531. height: 60px;
  532. background-color: var(--color-white);
  533. display: flex;
  534. justify-content: center;
  535. align-items: center;
  536. color: #000;
  537. .bottom-title {
  538. text-align: center;
  539. font-size: px2rpx(14);
  540. font-weight: 500;
  541. line-height: 1.5;
  542. color: #666666;
  543. }
  544. .ellipsis {
  545. width: px2rpx(200);
  546. white-space: nowrap;
  547. overflow: hidden;
  548. text-overflow: ellipsis;
  549. }
  550. .cwg-button {
  551. width: 120px !important;
  552. padding: px2rpx(4) 0 !important;
  553. }
  554. }
  555. button {
  556. background-color: #ea002a;
  557. font-size: px2rpx(14);
  558. font-weight: normal;
  559. height: px2rpx(44);
  560. line-height: px2rpx(44);
  561. }
  562. .right-f .account .company {
  563. padding: px2rpx(50) 0 px2rpx(200) 0;
  564. position: relative;
  565. align-items: flex-start !important;
  566. }
  567. .logo {
  568. margin-left: px2rpx(48);
  569. }
  570. .left-bg .company-icon {
  571. width: px2rpx(234);
  572. }
  573. .left-bg .left-content {
  574. position: relative;
  575. z-index: 1;
  576. }
  577. .title {
  578. margin: px2rpx(32) 0;
  579. font-size: px2rpx(24);
  580. font-weight: bolder;
  581. color: #e4e4e4;
  582. text-align: center;
  583. i {
  584. margin-right: px2rpx(10);
  585. }
  586. .tit1 {
  587. font-size: px2rpx(34);
  588. line-height: 1.5;
  589. font-weight: bold;
  590. color: #000000;
  591. }
  592. .tit2 {
  593. font-size: px2rpx(16);
  594. line-height: 1.5;
  595. color: #cecece;
  596. font-weight: 500;
  597. }
  598. }
  599. .qr-title {
  600. font-size: px2rpx(16);
  601. line-height: 1.5;
  602. color: #cecece;
  603. font-weight: 500;
  604. text-align: center;
  605. margin: px2rpx(40) 0;
  606. display: flex;
  607. align-items: center;
  608. justify-content: center;
  609. .line {
  610. flex: 1;
  611. height: 1px;
  612. background-color: #e4e4e4;
  613. }
  614. .qr-tit2 {
  615. margin: 0 px2rpx(12);
  616. }
  617. }
  618. .input {
  619. height: px2rpx(44);
  620. border-radius: px2rpx(8);
  621. background: #f7f8fa;
  622. padding: 0 px2rpx(20) !important;
  623. position: relative;
  624. }
  625. .account-icon {
  626. width: px2rpx(12);
  627. height: px2rpx(14) !important;
  628. margin-right: px2rpx(5);
  629. }
  630. :deep(.u-input__content__prefix-icon) {
  631. height: px2rpx(20);
  632. }
  633. .regiset-btn {
  634. margin: px2rpx(20) 0;
  635. }
  636. .account-tip {
  637. color: #666666;
  638. font-size: px2rpx(14);
  639. text-align: center;
  640. text {
  641. color: #ea002a;
  642. }
  643. }
  644. :deep(.u-form-item__body) {
  645. padding: 0 !important;
  646. padding-bottom: px2rpx(24) !important;
  647. }
  648. :deep(.wcg-checkbox) {
  649. padding: 0 !important;
  650. }
  651. .cwg-button {
  652. padding: px2rpx(34) 0 !important;
  653. }
  654. </style>