login.vue 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. <template>
  2. <div class="login">
  3. <img class="head" src="@/assets/images/head.png" alt="" />
  4. <div class="login-info flex-row-between">
  5. <div></div>
  6. <div class="login-bos">
  7. <el-form ref="loginRef" :model="loginForm" :rules="loginRules">
  8. <div class="login-type flex-row-between">
  9. <div :class="type == 1 ? 'hig' : ''" @click="onType(1)">账户登录</div>
  10. <div class="border"></div>
  11. <div :class="type == 2 ? 'hig' : ''" @click="onType(2)">验证码登录</div>
  12. </div>
  13. <template v-if="type == 1">
  14. <el-form-item prop="username">
  15. <el-input class="login-input" v-model="loginForm.username" placeholder="员工编号/手机号码">
  16. <template #prefix>
  17. <el-icon><User /></el-icon>
  18. </template>
  19. </el-input>
  20. </el-form-item>
  21. <el-form-item prop="password">
  22. <el-input class="login-input" type="password" v-model="loginForm.password" placeholder="输入登录密码" @keyup.enter="handleLogin">
  23. <template #prefix>
  24. <el-icon><Lock /></el-icon>
  25. </template>
  26. </el-input>
  27. </el-form-item>
  28. </template>
  29. <template v-else>
  30. <el-form-item prop="mobile">
  31. <el-input :maxlength="11" class="login-input" v-model="loginForm.mobile" placeholder="手机号">
  32. <template #prefix>
  33. <el-icon><Iphone /></el-icon>
  34. </template>
  35. </el-input>
  36. </el-form-item>
  37. <el-form-item prop="smsCode">
  38. <el-input :maxlength="6" class="login-input" v-model="loginForm.smsCode" placeholder="验证码">
  39. <template #prefix>
  40. <el-icon><Message /></el-icon>
  41. </template>
  42. <template #suffix>
  43. <span class="code" @click="sendSmsCode">{{ smsCodeText }}</span>
  44. </template>
  45. </el-input>
  46. </el-form-item>
  47. </template>
  48. <el-form-item>
  49. <el-button class="login-btn" type="primary" :loading="loading" @click.prevent="handleLogin">
  50. <span v-if="!loading">登录</span>
  51. <span v-else>登录中...</span>
  52. </el-button>
  53. </el-form-item>
  54. <div class="login-text flex-row-between">
  55. <div @click="handleForgetPassword">忘记密码?</div>
  56. <div class="border"></div>
  57. <div @click="onPath('/breg')">新用户注册</div>
  58. <!-- <router-link to="/register" class="register-link">新用户注册</router-link> -->
  59. </div>
  60. </el-form>
  61. </div>
  62. </div>
  63. <div class="login-foot flex-column-between">
  64. <div class="font-bos flex-row-center">
  65. <div>客户管理</div>
  66. <div style="margin: 0 10px">|</div>
  67. <div>供应商合作</div>
  68. <div style="margin: 0 10px">|</div>
  69. <div>关于我们</div>
  70. <div style="margin: 0 10px">|</div>
  71. <div>帮助中心</div>
  72. <div style="margin: 0 10px">|</div>
  73. <div>联系我们</div>
  74. </div>
  75. <div class="font-box">CopyRight @ 优易365 2026</div>
  76. </div>
  77. </div>
  78. </template>
  79. <script setup lang="ts">
  80. import { useUserStore } from '@/store/modules/user';
  81. import { LoginData } from '@/api/types';
  82. import { to } from 'await-to-js';
  83. import { User, Lock, Iphone, Message } from '@element-plus/icons-vue';
  84. import { onPath } from '@/utils/siteConfig';
  85. const userStore = useUserStore();
  86. const router = useRouter();
  87. const type = ref<number>(1);
  88. const loading = ref(false);
  89. const smsCodeText = ref('发送验证码');
  90. const smsCountdown = ref(0);
  91. const loginRef = ref<ElFormInstance>();
  92. const redirect = ref('/');
  93. const loginForm = ref<LoginData>({
  94. username: '13322331122',
  95. password: '123456',
  96. mobile: '',
  97. smsCode: '',
  98. tenantId: '000000',
  99. rememberMe: false,
  100. code: '',
  101. uuid: '',
  102. clientId: '',
  103. grantType: 'password'
  104. } as LoginData);
  105. const loginRules: ElFormRules = {
  106. username: [{ required: true, trigger: 'blur', message: '请输入员工编号/手机号码' }],
  107. password: [{ required: true, trigger: 'blur', message: '请输入登录密码' }],
  108. mobile: [{ required: true, trigger: 'blur', message: '请输入手机号' }],
  109. smsCode: [{ required: true, trigger: 'blur', message: '请输入验证码' }]
  110. };
  111. const onType = (val: number) => {
  112. type.value = val;
  113. // 切换登录类型时更新 grantType
  114. loginForm.value.grantType = val === 1 ? 'password' : 'sms';
  115. };
  116. /**
  117. * 监听路由变化,获取重定向地址
  118. */
  119. watch(
  120. () => router.currentRoute.value,
  121. (newRoute: any) => {
  122. redirect.value = newRoute.query && newRoute.query.redirect && decodeURIComponent(newRoute.query.redirect);
  123. },
  124. { immediate: true }
  125. );
  126. /**
  127. * 处理登录
  128. */
  129. const handleLogin = () => {
  130. loginRef.value?.validate(async (valid: boolean) => {
  131. if (valid) {
  132. loading.value = true;
  133. // 勾选记住密码时保存到 localStorage
  134. if (loginForm.value.rememberMe) {
  135. localStorage.setItem('username', String(loginForm.value.username));
  136. localStorage.setItem('password', String(loginForm.value.password));
  137. localStorage.setItem('rememberMe', String(loginForm.value.rememberMe));
  138. } else {
  139. localStorage.removeItem('username');
  140. localStorage.removeItem('password');
  141. localStorage.removeItem('rememberMe');
  142. }
  143. // 调用登录
  144. const [err] = await to(userStore.login(loginForm.value));
  145. if (!err) {
  146. const redirectUrl = redirect.value || '/';
  147. onPath(redirectUrl);
  148. loading.value = false;
  149. } else {
  150. loading.value = false;
  151. ElMessage.error('登录失败,请检查用户名和密码');
  152. }
  153. }
  154. });
  155. };
  156. /**
  157. * 发送短信验证码
  158. */
  159. const sendSmsCode = () => {
  160. if (smsCountdown.value > 0) return;
  161. if (!loginForm.value.mobile) {
  162. ElMessage.warning('请输入手机号');
  163. return;
  164. }
  165. // TODO: 调用发送短信验证码接口
  166. ElMessage.success('验证码已发送');
  167. smsCountdown.value = 60;
  168. const timer = setInterval(() => {
  169. smsCountdown.value--;
  170. if (smsCountdown.value > 0) {
  171. smsCodeText.value = `${smsCountdown.value}s后重发`;
  172. } else {
  173. smsCodeText.value = '发送验证码';
  174. clearInterval(timer);
  175. }
  176. }, 1000);
  177. };
  178. /**
  179. * 忘记密码
  180. */
  181. const handleForgetPassword = () => {
  182. ElMessage.info('请联系管理员重置密码');
  183. };
  184. /**
  185. * 获取保存的登录信息
  186. */
  187. const getLoginData = () => {
  188. const username = localStorage.getItem('username');
  189. const password = localStorage.getItem('password');
  190. const rememberMe = localStorage.getItem('rememberMe');
  191. if (username && password && rememberMe) {
  192. loginForm.value.username = username;
  193. loginForm.value.password = password;
  194. loginForm.value.rememberMe = Boolean(rememberMe);
  195. }
  196. };
  197. onMounted(() => {
  198. getLoginData();
  199. });
  200. </script>
  201. <style lang="scss" scoped>
  202. .login {
  203. height: 100%;
  204. width: 100%;
  205. background-image: url('@/assets/images/login/login1.png');
  206. overflow: auto;
  207. background-position: center center;
  208. background-repeat: no-repeat;
  209. background-size: cover;
  210. .head {
  211. width: 185px;
  212. height: 90px;
  213. margin-top: 53px;
  214. margin-left: 84px;
  215. }
  216. .login-info {
  217. width: 100%;
  218. height: 660px;
  219. background-image: url('@/assets/images/login/login2.png');
  220. overflow: hidden;
  221. background-position: center center;
  222. background-repeat: no-repeat;
  223. background-size: cover;
  224. margin-top: 68px;
  225. min-width: 1200px;
  226. padding: 0 5%;
  227. .login-bos {
  228. width: 520px;
  229. height: 510px;
  230. background: #ffffff;
  231. border-radius: 30px 30px 30px 30px;
  232. padding: 90px 85px 0 85px;
  233. :deep(.el-form-item) {
  234. margin-bottom: 18px;
  235. }
  236. :deep(.el-form-item__error) {
  237. padding-top: 4px;
  238. }
  239. .login-type {
  240. font-weight: 600;
  241. font-size: 22px;
  242. color: #101828;
  243. padding: 0 67px;
  244. margin-bottom: 40px;
  245. div {
  246. cursor: pointer;
  247. }
  248. .hig {
  249. color: #e7000b;
  250. }
  251. .border {
  252. width: 1px;
  253. height: 16px;
  254. background: #e6e8ec;
  255. }
  256. }
  257. .login-input {
  258. width: 350px;
  259. height: 50px;
  260. font-size: 16px;
  261. .code {
  262. font-size: 14px;
  263. color: #e7000b;
  264. cursor: pointer;
  265. }
  266. }
  267. :deep(.el-input__wrapper) {
  268. border: none;
  269. box-shadow: none;
  270. outline: none;
  271. background: #f4f6f8;
  272. }
  273. :deep(.el-input__prefix) {
  274. font-size: 18px;
  275. color: #9ca3af;
  276. }
  277. .login-btn {
  278. width: 350px;
  279. height: 50px;
  280. margin-top: 20px;
  281. font-size: 16px;
  282. background-color: #c8102e;
  283. border-color: #c8102e;
  284. &:hover {
  285. background-color: #a50d26;
  286. border-color: #a50d26;
  287. }
  288. }
  289. .login-text {
  290. font-size: 14px;
  291. color: #6a7282;
  292. padding: 0 107px;
  293. margin-top: 14px;
  294. div {
  295. cursor: pointer;
  296. &:hover {
  297. color: #c8102e;
  298. }
  299. }
  300. .border {
  301. width: 1px;
  302. height: 12px;
  303. background: #e6e8ec;
  304. }
  305. .register-link {
  306. color: #c8102e;
  307. text-decoration: none;
  308. &:hover {
  309. text-decoration: underline;
  310. }
  311. }
  312. }
  313. }
  314. }
  315. .font-bos {
  316. width: 100%;
  317. font-size: 13px;
  318. color: #999999;
  319. margin-top: 30px;
  320. }
  321. .font-box {
  322. width: 100%;
  323. font-size: 13px;
  324. color: #999999;
  325. margin-top: 20px;
  326. text-align: center;
  327. }
  328. }
  329. </style>