login.uvue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. <template>
  2. <cl-page>
  3. <Back />
  4. <!-- <cl-image src="https://oss.xiaoxiongcode.com/static/home/1.jpg" mode="heightFix" class="w-full h-full object-cover"
  5. height="100%" width="100%" /> -->
  6. <view class="content">
  7. <view class="group">
  8. <view class="title">
  9. <cl-image src="https://oss.xiaoxiongcode.com/static/home/logo.png" width="120px" height="120px"
  10. mode="heightFix" />
  11. <cl-text :size="20" color="#666">
  12. 少儿物理启蒙
  13. </cl-text>
  14. </view>
  15. <view class="w-[80vw]">
  16. <!-- <cl-tabs v-model="val" :list="list" fill :form="formData" @change="tabsChange"></cl-tabs> -->
  17. <cl-form v-model="formData" ref="formRef" :pt="{ className: 'mt-1' }">
  18. <view v-if="val === 'quickly_login'">
  19. <phone :form="formData" />
  20. </view>
  21. <view v-else-if="val === 'password'">
  22. <password :form="formData" />
  23. </view>
  24. <view v-else-if="val === 'register'">
  25. <register :form="formData" />
  26. </view>
  27. <view class="flex flex-row items-center justify-center mb-5">
  28. <cl-button v-if="val === 'weixin'"
  29. :pt="{ className: '!h-[45px] !rounded-full w-[200px] mx-auto' }" type="success"
  30. @tap="toWechatLogin">
  31. <view class="flex flex-row items-center justify-center">
  32. <cl-image src="https://oss.xiaoxiongcode.com/static/个人中心/微信.png" mode="heightFix"
  33. height="20px" width="auto"></cl-image>
  34. <view class="ml-2">
  35. 微信快捷登录
  36. </view>
  37. </view>
  38. </cl-button>
  39. <cl-button v-else-if="val === 'register'" :disabled="!agree || !agree2"
  40. :pt="{ className: '!h-[45px] !rounded-full w-[200px] mx-auto' }" :loading="loading"
  41. @tap="toRegister">
  42. 注册并登录
  43. </cl-button>
  44. <cl-button v-else
  45. :disabled="!agree || !agree2 || (formData.grant_type == 'quickly_login' && formData.code == '')"
  46. :pt="{ className: '!h-[45px] !rounded-full w-[200px] mx-auto' }" :loading="loading"
  47. @tap="toLogin">
  48. 登录
  49. </cl-button>
  50. </view>
  51. <cl-checkbox v-model="agree" v-if="val !== 'weixin'">
  52. <view class="flex flex-row items-center w-full text-[12px]">
  53. 同意并阅读
  54. <cl-text :size="12" color="primary" @tap.stop="toAgreement">
  55. 《用户服务协议》
  56. </cl-text>
  57. </view>
  58. </cl-checkbox>
  59. <cl-checkbox v-model="agree2" v-if="val !== 'weixin'">
  60. <view class="flex flex-row items-center w-full text-[12px]">
  61. 同意并阅读
  62. <cl-text :size="12" color="primary" @tap.stop="toPrivacy">
  63. 《隐私政策》
  64. </cl-text>
  65. </view>
  66. </cl-checkbox>
  67. </cl-form>
  68. </view>
  69. <view>
  70. <view class=" w-full flex flex-row gap-4 items-center justify-center mb-4">
  71. <view v-if="val !== 'weixin'">
  72. <cl-button type="success"
  73. :pt="{ className: '!h-[40px] !rounded-full w-[40px] mx-auto !p-1' }" :loading="loading"
  74. @tap="tabsChange('weixin')">
  75. <cl-image src="https://oss.xiaoxiongcode.com/static/个人中心/微信.png" mode="heightFix"
  76. height="20px" width="auto"></cl-image>
  77. </cl-button>
  78. <view class="mt-1">
  79. <cl-text :size="14" color="#666">
  80. 微信登录
  81. </cl-text>
  82. </view>
  83. </view>
  84. <view v-if="val !== 'quickly_login'">
  85. <cl-button type="info" :pt="{ className: '!h-[40px] !rounded-full w-[40px] mx-auto !p-1' }"
  86. :loading="loading" @tap="tabsChange('quickly_login')">
  87. <cl-image src="https://oss.xiaoxiongcode.com/static/home/phone.svg" mode="heightFix"
  88. height="20px" width="auto"></cl-image>
  89. </cl-button>
  90. <view class="mt-1">
  91. <cl-text :size="14" color="#666">
  92. 验证码登录
  93. </cl-text>
  94. </view>
  95. </view>
  96. <view v-if="val !== 'password'">
  97. <cl-button type="info" :pt="{ className: '!h-[40px] !rounded-full w-[40px] mx-auto !p-1' }"
  98. :loading="loading" @tap="tabsChange('password')">
  99. <cl-image src="https://oss.xiaoxiongcode.com/static/home/password.svg" mode="heightFix"
  100. height="20px" width="auto"></cl-image>
  101. </cl-button>
  102. <view class="mt-1">
  103. <cl-text :size="14" color="#666">
  104. 密码登录
  105. </cl-text>
  106. </view>
  107. </view>
  108. </view>
  109. <view v-if="val !== 'register'" class=" w-full flex flex-row gap-4 items-center justify-center">
  110. <cl-text :size="14" color="primary" @tap="tabsChange('register')">
  111. 注册账号
  112. </cl-text>
  113. </view>
  114. </view>
  115. </view>
  116. </view>
  117. <cl-action-sheet ref="actionSheetRef">
  118. <template #prepend>
  119. <view class=" mb-3 font-bold text-center">
  120. <cl-text :size="20" color="#666">
  121. 选择登录账号
  122. </cl-text>
  123. </view>
  124. </template>
  125. </cl-action-sheet>
  126. <cl-popup v-model="visible" showClose :show-header="false" direction="center" :pt="{
  127. inner: {
  128. className: '!bg-transparent'
  129. }
  130. }">
  131. <view class="w-[330px] relative">
  132. <image src="https://oss.xiaoxiongcode.com/static/home/6172.png" mode="widthFix"
  133. class="w-[330px] relative ">
  134. </image>
  135. <view class=" wz-1 ">
  136. 当前微信账号,<br />尚未绑定,<br />无法快捷登录
  137. </view>
  138. <view class="wz-2 " @tap="handleClosePopup">
  139. 立即登录
  140. </view>
  141. </view>
  142. </cl-popup>
  143. </cl-page>
  144. </template>
  145. <script lang="ts" setup>
  146. import Back from '@/components/back.uvue'
  147. import type { LoginForm } from "./components/types";
  148. import phone from './components/phone.uvue';
  149. import password from './components/password.uvue';
  150. import register from './components/register.uvue';
  151. import { ref } from 'vue'
  152. import type { ClTabsItem } from "@/uni_modules/cool-ui";
  153. import { encryptPassword, user, router } from '@/.cool';
  154. import { loginApi, wechatLogin, quicklyRegister, selfRegister } from "@/services/user";
  155. import type { ClActionSheetOptions } from "@/uni_modules/cool-ui";
  156. import { type ClFormRule, useForm } from "@/uni_modules/cool-ui";
  157. const actionSheetRef = ref<ClActionSheetComponentPublicInstance | null>(null);
  158. const visible = ref(false)
  159. const { formRef, validate, clearValidate } = useForm();
  160. const val = ref('weixin')
  161. const agree = ref(false)
  162. const agree2 = ref(false)
  163. const list = [
  164. {
  165. label: '验证码登录',
  166. value: 'quickly_login'
  167. },
  168. {
  169. label: '密码登录',
  170. value: 'password'
  171. },
  172. {
  173. label: '微信登录',
  174. value: 'weixin'
  175. },
  176. {
  177. label: '注册账号',
  178. value: 'register'
  179. }
  180. ]
  181. const formData = ref<LoginForm>({
  182. username: '',
  183. password: '',
  184. randomStr: 0,
  185. grant_type: 'quickly_login',
  186. scope: 'server',
  187. loginType: 99,
  188. code: ''
  189. })
  190. const loading = ref(false)
  191. function tabsChange(type: string) {
  192. val.value = type
  193. if (val.value === 'quickly_login') {
  194. formData.value.code = ''
  195. formData.value.loginType = 99
  196. formData.value.grant_type = 'quickly_login'
  197. formData.value.password = ''
  198. } else {
  199. formData.value.grant_type = 'password'
  200. formData.value.password = ''
  201. formData.value.loginType = 1
  202. formData.value.code = ''
  203. }
  204. }
  205. async function toRegister() {
  206. validate(async (valid, errors) => {
  207. if (valid) {
  208. loading.value = true
  209. try {
  210. await selfRegister({
  211. ...formData.value,
  212. password: formData.value.password && encryptPassword(formData.value.password),
  213. })
  214. uni.showToast({
  215. title: '注册成功',
  216. icon: 'success'
  217. })
  218. handleSelect(formData.value.username)
  219. loading.value = false
  220. } catch (error) {
  221. loading.value = false
  222. }
  223. } else {
  224. // 验证不通过,可以提示错误信息 errors
  225. console.log(formData.value, 2);
  226. }
  227. });
  228. }
  229. function toLogin() {
  230. loading.value = true
  231. validate((valid, errors) => {
  232. if (valid) {
  233. console.log(formData.value);
  234. loginApi({
  235. ...formData.value,
  236. password: formData.value.password && encryptPassword(formData.value.password),
  237. }).then(res => {
  238. user.setToken(res)
  239. loading.value = false
  240. uni.showToast({
  241. title: '登录成功',
  242. icon: 'success'
  243. })
  244. router.home();
  245. }).catch(err => {
  246. loading.value = false
  247. if (err.message == '手机号不存在' && val.value === 'quickly_login') {
  248. quicklyRegister({
  249. username: formData.value.username,
  250. password: 'ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413',
  251. }).then(res => {
  252. handleSelect(formData.value.username)
  253. })
  254. } else {
  255. uni.showToast({
  256. title: err.message,
  257. icon: 'none'
  258. })
  259. }
  260. })
  261. } else {
  262. // 验证不通过,可以提示错误信息 errors
  263. loading.value = false
  264. console.log(formData.value, 2);
  265. }
  266. });
  267. }
  268. function handleClosePopup() {
  269. visible.value = false
  270. tabsChange('quickly_login')
  271. }
  272. function toWechatLogin() {
  273. uni.login({
  274. provider: 'weixin',
  275. success(res) {
  276. wechatLogin({
  277. code: res.code,
  278. }).then(res => {
  279. console.log(res);
  280. if (res.length === 0) {
  281. visible.value = true
  282. return
  283. } else if (res.length === 1) {
  284. handleSelect(res[0])
  285. return
  286. }
  287. const list = res.map(item => ({
  288. label: item,
  289. icon: "user-line",
  290. callback() {
  291. handleSelect(item)
  292. }
  293. }))
  294. actionSheetRef.value!.open({
  295. list: list
  296. } as ClActionSheetOptions);
  297. })
  298. }
  299. })
  300. }
  301. function handleSelect(item: string) {
  302. formData.value.username = item
  303. formData.value.password = 'quickly_login'
  304. formData.value.grant_type = 'quickly_login'
  305. formData.value.loginType = 88
  306. loginApi({
  307. ...formData.value,
  308. }).then(res => {
  309. user.setToken(res)
  310. uni.showToast({
  311. title: '登录成功',
  312. icon: 'success'
  313. })
  314. router.home();
  315. })
  316. }
  317. function toAgreement() {
  318. router.push({
  319. path: '/pages/user/agreement'
  320. })
  321. }
  322. function toPrivacy() {
  323. router.push({
  324. path: '/pages/user/privacy'
  325. })
  326. }
  327. </script>
  328. <style lang="scss" scoped>
  329. .content {
  330. @apply w-full h-full;
  331. // 渐变背景
  332. background: linear-gradient(180deg, #169deb5e 0%, #fff 20%);
  333. }
  334. .wz-1 {
  335. @apply absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-black text-3xl font-bold w-[300px] text-center;
  336. line-height: 40px;
  337. }
  338. .wz-2 {
  339. @apply absolute bottom-[12px] text-black text-2xl font-bold text-center;
  340. line-height: 40px;
  341. left: 50%;
  342. transform: translateX(-50%);
  343. }
  344. .title {
  345. //设置字间距
  346. letter-spacing: 0.1em;
  347. margin-bottom: 20px;
  348. @apply flex flex-col w-full items-center justify-center;
  349. }
  350. .group {
  351. @apply absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 px-4 py-2 pb-10 rounded-xl;
  352. display: flex;
  353. height: 100%;
  354. align-items: center;
  355. flex-direction: column;
  356. justify-content: space-around;
  357. }
  358. </style>