login.uvue 10 KB

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