login.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. <template>
  2. <view class="login-page">
  3. <!-- 顶部关闭按钮 -->
  4. <view class="close-btn" @click="handleClose">✕</view>
  5. <!-- 登录标题 -->
  6. <view class="login-title">
  7. <view class="title-line">您好,欢迎登录</view>
  8. <view class="title-line">二轮延包管理系统</view>
  9. </view>
  10. <!-- 表单区域 -->
  11. <view class="form-container">
  12. <!-- 手机号输入框 -->
  13. <view class="input-item">
  14. <view class="input-icon">👤</view>
  15. <input class="input-field" v-model="loginForm.phone" placeholder="请输入手机号"
  16. maxlength="11" />
  17. <view class="clear-btn" v-if="loginForm.phone" @click="clearPhone">✕</view>
  18. </view>
  19. <!-- 密码输入框 -->
  20. <view class="input-item">
  21. <view class="input-icon">🔒</view>
  22. <input class="input-field" v-model="loginForm.password" :type="showPassword ? 'text' : 'password'"
  23. placeholder="请输入密码" maxlength="20" />
  24. <view class="password-actions">
  25. <view class="eye-btn" @click="togglePassword">
  26. {{ showPassword ? '👁' : '👁‍🗨' }}
  27. </view>
  28. <view class="clear-btn" v-if="loginForm.password" @click="clearPassword">✕</view>
  29. </view>
  30. </view>
  31. <!-- 功能链接区 -->
  32. <!-- <view class="link-group">
  33. <view class="link-item" @click="goVerifyLogin">验证码登录</view>
  34. <view class="link-item" @click="goForgetPwd">忘记密码</view>
  35. <view class="link-item" @click="goRegister">快速注册</view>
  36. </view> -->
  37. <!-- 登录按钮 -->
  38. <view class="login-btn" :class="{ 'btn-disabled': !isAgree }" @click="handleLogin">
  39. 登录
  40. </view>
  41. <!-- 协议勾选 -->
  42. <view class="agreement-box">
  43. <checkbox :checked="isAgree" @click="toggleAgree" color="#409eff" />
  44. <view class="agreement-text">
  45. 已阅读并同意
  46. <text class="agreement-link" @click="goUserAgreement">《用户协议》</text>
  47. <text class="agreement-link" @click="goPrivacyPolicy">《隐私政策》</text>
  48. </view>
  49. </view>
  50. </view>
  51. </view>
  52. </template>
  53. <script setup lang="ts">
  54. import { ref, computed } from 'vue'
  55. import { login,getUserInfo } from '@/api/user';
  56. // 登录表单数据
  57. const loginForm = ref({
  58. phone: '', // 示例手机号,实际为空
  59. password: '' // 示例密码,实际为空
  60. })
  61. // 密码显示状态
  62. const showPassword = ref(false)
  63. // 协议同意状态
  64. const isAgree = ref(false)
  65. // 表单校验:手机号+密码+协议 都满足才可点击登录
  66. const isFormValid = computed(() => {
  67. return (
  68. /^1[3-9]\d{9}$/.test(loginForm.value.phone) && // 手机号格式校验
  69. loginForm.value.password.length >= 6 && // 密码长度≥6
  70. isAgree.value // 同意协议
  71. )
  72. })
  73. // 清空手机号
  74. const clearPhone = () => {
  75. loginForm.value.phone = ''
  76. }
  77. // 清空密码
  78. const clearPassword = () => {
  79. loginForm.value.password = ''
  80. }
  81. // 切换密码显示/隐藏
  82. const togglePassword = () => {
  83. showPassword.value = !showPassword.value
  84. }
  85. // 切换协议勾选
  86. const toggleAgree = () => {
  87. isAgree.value = !isAgree.value
  88. }
  89. // 示例:关闭登录页
  90. const handleClose = () => {
  91. uni.navigateBack({
  92. delta: 1
  93. })
  94. }
  95. // 登录按钮点击
  96. const handleLogin = () => {
  97. if (!isAgree.value) {
  98. uni.showToast({ title: '请先同意用户协议和隐私政策', icon: 'none' })
  99. return
  100. }
  101. getLogin(loginForm)
  102. }
  103. async function getLogin(loginForm:any) {
  104. try {
  105. const res = await login({
  106. username: loginForm.value.phone,
  107. password: loginForm.value.password
  108. });
  109. // 增加空值判断,防止 res 或 res.token 不存在
  110. if (res && res.token) {
  111. // 登录成功,存储 Token
  112. uni.setStorageSync('token', res.token);
  113. uni.showToast({ title: '登录成功' });
  114. await getUserInfoList()
  115. uni.navigateTo({
  116. url: '/pages/index/index'
  117. })
  118. } else {
  119. uni.showToast({ title: '登录成功但未获取到Token', icon: 'none' });
  120. console.warn('Token 缺失:', res);
  121. }
  122. } catch (err) {
  123. console.error('登录失败:', err);
  124. uni.showToast({ title: '登录失败:' + (err.msg || '网络错误'), icon: 'none' });
  125. }
  126. }
  127. async function getUserInfoList() {
  128. try {
  129. const res = await getUserInfo();
  130. uni.setStorageSync('investigator_FBFBM', res.user.fbfbm);
  131. console.log('获取用户信息:', res); // 打印完整返回值
  132. } catch (err) {
  133. console.error('获取失败:', err);
  134. }
  135. }
  136. // 功能链接跳转
  137. const goVerifyLogin = () => {
  138. uni.navigateTo({
  139. url: '/pages/index/index'
  140. })
  141. }
  142. const goForgetPwd = () => {
  143. uni.navigateTo({
  144. url: '/pages/index/index'
  145. })
  146. }
  147. const goRegister = () => {
  148. uni.navigateTo({
  149. url: '/pages/index/index'
  150. })
  151. }
  152. const goUserAgreement = () => {
  153. uni.navigateTo({
  154. url: '/pages/index/index'
  155. })
  156. }
  157. const goPrivacyPolicy = () => {
  158. uni.navigateTo({
  159. url: '/pages/index/index'
  160. })
  161. }
  162. </script>
  163. <style lang="scss" scoped>
  164. page {
  165. background-color: #f8fafc;
  166. height: 100vh;
  167. }
  168. .login-page {
  169. padding: 40rpx 30rpx;
  170. box-sizing: border-box;
  171. height: 100vh;
  172. display: flex;
  173. flex-direction: column;
  174. }
  175. // 顶部关闭按钮
  176. .close-btn {
  177. font-size: 60rpx;
  178. color: #333;
  179. font-weight: 300;
  180. line-height: 1;
  181. margin-bottom: 80rpx;
  182. }
  183. // 登录标题
  184. .login-title {
  185. margin-bottom: 80rpx;
  186. }
  187. .title-line {
  188. font-size: 48rpx;
  189. font-weight: bold;
  190. color: #333;
  191. line-height: 1.5;
  192. margin-bottom: 20rpx;
  193. }
  194. // 表单容器
  195. .form-container {
  196. flex: 1;
  197. }
  198. // 输入框项
  199. .input-item {
  200. display: flex;
  201. align-items: center;
  202. border-bottom: 1rpx solid #e5e7eb;
  203. padding: 30rpx 0;
  204. margin-bottom: 20rpx;
  205. }
  206. .input-icon {
  207. font-size: 36rpx;
  208. color: #999;
  209. margin-right: 20rpx;
  210. flex-shrink: 0;
  211. }
  212. .input-field {
  213. flex: 1;
  214. font-size: 34rpx;
  215. color: #333;
  216. height: 40rpx;
  217. line-height: 40rpx;
  218. &::placeholder {
  219. color: #c9cdd4;
  220. }
  221. }
  222. .clear-btn {
  223. font-size: 36rpx;
  224. color: #999;
  225. width: 40rpx;
  226. height: 40rpx;
  227. display: flex;
  228. align-items: center;
  229. justify-content: center;
  230. flex-shrink: 0;
  231. }
  232. .password-actions {
  233. display: flex;
  234. align-items: center;
  235. gap: 20rpx;
  236. }
  237. .eye-btn {
  238. font-size: 36rpx;
  239. color: #999;
  240. width: 40rpx;
  241. height: 40rpx;
  242. display: flex;
  243. align-items: center;
  244. justify-content: center;
  245. flex-shrink: 0;
  246. }
  247. // 功能链接区
  248. .link-group {
  249. display: flex;
  250. justify-content: space-between;
  251. margin: 40rpx 0 60rpx;
  252. }
  253. .link-item {
  254. font-size: 32rpx;
  255. color: #666;
  256. }
  257. // 登录按钮
  258. .login-btn {
  259. width: 100%;
  260. height: 90rpx;
  261. background-color: #409eff;
  262. color: #fff;
  263. font-size: 36rpx;
  264. font-weight: 500;
  265. border-radius: 45rpx;
  266. display: flex;
  267. align-items: center;
  268. justify-content: center;
  269. margin-bottom: 40rpx;
  270. box-shadow: 0 4rpx 12rpx rgba(64, 158, 255, 0.3);
  271. }
  272. .btn-disabled {
  273. background-color: #c0c4cc !important;
  274. box-shadow: none;
  275. }
  276. // 协议勾选区
  277. .agreement-box {
  278. display: flex;
  279. align-items: center;
  280. justify-content: center;
  281. gap: 16rpx;
  282. }
  283. .agreement-text {
  284. font-size: 30rpx;
  285. color: #666;
  286. line-height: 1.5;
  287. }
  288. .agreement-link {
  289. color: #409eff;
  290. }
  291. </style>