login.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. <template>
  2. <div class="login-wrapper">
  3. <div class="login_left">
  4. <div class="version">
  5. <a class="xn-color-a0a0a0" :href="sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL" target="_blank">{{
  6. sysBaseConfig.SNOWY_SYS_COPYRIGHT
  7. }}</a>
  8. <div class="xn-color-a0a0a0">鄂ICP备20000826号-1</div>
  9. </div>
  10. </div>
  11. <div class="login_right">
  12. <div class="login_head">
  13. <img class="login-logo" :src="sysBaseConfig.SNOWY_SYS_LOGO" />
  14. <span class="login-title">{{ sysBaseConfig.SNOWY_SYS_NAME }}</span>
  15. </div>
  16. <div class="login_form">
  17. <div class="form_head">登录</div>
  18. <a-form ref="loginForm" :model="ruleForm" :rules="rules" layout="vertical">
  19. <!-- 用户名 -->
  20. <a-form-item label="用户名" name="account">
  21. <a-input v-model:value="ruleForm.account" placeholder="用户名" size="large" @keyup.enter="login"> </a-input>
  22. </a-form-item>
  23. <!-- 密码 -->
  24. <a-form-item label="密码" name="password">
  25. <a-input-password
  26. v-model:value="ruleForm.password"
  27. placeholder="密码"
  28. size="large"
  29. autocomplete="off"
  30. @keyup.enter="login"
  31. >
  32. </a-input-password>
  33. </a-form-item>
  34. <!-- 验证码 -->
  35. <a-form-item label="验证码" name="validCode" v-if="captchaOpen === 'true'">
  36. <a-row :gutter="8">
  37. <a-col :span="17">
  38. <a-input v-model:value="ruleForm.validCode" placeholder="验证码" size="large" @keyup.enter="login">
  39. </a-input>
  40. </a-col>
  41. <a-col :span="7">
  42. <img :src="validCodeBase64" class="login-validCode-img" @click="loginCaptcha" />
  43. </a-col>
  44. </a-row>
  45. </a-form-item>
  46. <!-- 记住登录状态 -->
  47. <a-form-item>
  48. <a-checkbox v-model:checked="ruleForm.autologin">记住用户名密码</a-checkbox>
  49. </a-form-item>
  50. <!-- 登录按钮 -->
  51. <a-form-item>
  52. <a-button type="primary" class="w-full" :loading="loading" round size="large" @click="login"
  53. >{{ $t('login.signIn') }}
  54. </a-button>
  55. </a-form-item>
  56. </a-form>
  57. <!-- <div class="form_foot">没有账号?点击<router-link to="/findpwd" class="nav-link">注册</router-link></div> -->
  58. </div>
  59. <div class="login_official">
  60. <img src="/src/assets/images/login/official.jpg" class="code" />
  61. </div>
  62. <div class="tip">扫码关注公众号,实时获取预警信息</div>
  63. </div>
  64. </div>
  65. </template>
  66. <script setup>
  67. import loginApi from '@/api/auth/loginApi'
  68. const PhoneLoginForm = defineAsyncComponent(() => import('./phoneLoginForm.vue'))
  69. import ThreeLogin from './threeLogin.vue'
  70. import smCrypto from '@/utils/smCrypto'
  71. import { required } from '@/utils/formRules'
  72. import { afterLogin } from './util'
  73. import configData from '@/config'
  74. import configApi from '@/api/dev/configApi'
  75. import tool from '@/utils/tool'
  76. import { globalStore, iframeStore, keepAliveStore, viewTagsStore } from '@/store'
  77. const { proxy } = getCurrentInstance()
  78. const activeKey = ref('userAccount')
  79. const captchaOpen = ref(configData.SYS_BASE_CONFIG.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN)
  80. const validCodeBase64 = ref('')
  81. const loading = ref(false)
  82. const ruleForm = reactive({
  83. account: 'superAdmin',
  84. password: '123456',
  85. validCode: '',
  86. validCodeReqNo: '',
  87. autologin: false
  88. })
  89. const rules = reactive({
  90. account: [required('请输入用户名', 'blur')],
  91. password: [required(proxy.$t('login.PWError'), 'blur')]
  92. })
  93. const lang = ref([
  94. {
  95. name: '简体中文',
  96. value: 'zh-cn'
  97. },
  98. {
  99. name: 'English',
  100. value: 'en'
  101. }
  102. ])
  103. const config = ref({
  104. lang: tool.data.get('APP_LANG') || proxy.$CONFIG.LANG,
  105. theme: tool.data.get('APP_THEME') || 'default'
  106. })
  107. const store = globalStore()
  108. const kStore = keepAliveStore()
  109. const iStore = iframeStore()
  110. const vStore = viewTagsStore()
  111. const setSysBaseConfig = store.setSysBaseConfig
  112. const clearKeepLive = kStore.clearKeepLive
  113. const clearIframeList = iStore.clearIframeList
  114. const clearViewTags = vStore.clearViewTags
  115. const sysBaseConfig = computed(() => {
  116. return store.sysBaseConfig
  117. })
  118. onMounted(() => {
  119. let formData = ref(configData.SYS_BASE_CONFIG)
  120. getAutologin() //记住登录状态
  121. configApi
  122. .configSysBaseList()
  123. .then((data) => {
  124. if (data) {
  125. data.forEach((item) => {
  126. formData.value[item.configKey] = item.configValue
  127. })
  128. captchaOpen.value = formData.value.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN
  129. tool.data.set('SNOWY_SYS_BASE_CONFIG', formData.value)
  130. setSysBaseConfig(formData.value)
  131. refreshSwitch()
  132. }
  133. })
  134. .catch(() => {})
  135. })
  136. onBeforeMount(() => {
  137. clearViewTags()
  138. clearKeepLive()
  139. clearIframeList()
  140. })
  141. // 监听语言
  142. watch(
  143. () => config.value.lang,
  144. (newValue) => {
  145. proxy.$i18n.locale = newValue
  146. tool.data.set('APP_LANG', newValue)
  147. }
  148. )
  149. // 主题
  150. watch(
  151. () => config.value.theme,
  152. (newValue) => {
  153. document.body.setAttribute('data-theme', newValue)
  154. }
  155. )
  156. //记住状态
  157. const getAutologin = () => {
  158. let account = tool.data.get('account')
  159. let password = tool.data.get('password')
  160. let autologin = tool.data.get('autologin')
  161. ruleForm.account = account === undefined ? ruleForm.account : account
  162. ruleForm.password = password === undefined ? ruleForm.password : password
  163. ruleForm.autologin = autologin === undefined ? ruleForm.account : autologin
  164. }
  165. // 通过开关加载内容
  166. const refreshSwitch = () => {
  167. // 判断是否开启验证码
  168. if (captchaOpen.value === 'true') {
  169. // 加载验证码
  170. loginCaptcha()
  171. // 加入校验
  172. rules.validCode = [required(proxy.$t('login.validError'), 'blur')]
  173. }
  174. }
  175. // 获取验证码
  176. const loginCaptcha = () => {
  177. loginApi.getPicCaptcha().then((data) => {
  178. validCodeBase64.value = data.validCodeBase64
  179. ruleForm.validCodeReqNo = data.validCodeReqNo
  180. })
  181. }
  182. //登陆
  183. const loginForm = ref()
  184. const login = async () => {
  185. loginForm.value
  186. .validate()
  187. .then(async () => {
  188. loading.value = true
  189. const loginData = {
  190. account: ruleForm.account,
  191. // 密码进行SM2加密,传输过程中看到的只有密文,后端存储使用hash
  192. password: smCrypto.doSm2Encrypt(ruleForm.password),
  193. validCode: ruleForm.validCode,
  194. validCodeReqNo: ruleForm.validCodeReqNo
  195. }
  196. //是否记住登录状态
  197. if (ruleForm.autologin) {
  198. tool.data.set('loginForm', ruleForm.account)
  199. tool.data.set('password', ruleForm.password)
  200. tool.data.set('autologin', ruleForm.autologin)
  201. } else {
  202. tool.data.remove('account')
  203. tool.data.remove('password')
  204. tool.data.remove('autologin')
  205. }
  206. // 获取token
  207. try {
  208. const loginToken = await loginApi.login(loginData)
  209. await afterLogin(loginToken)
  210. } catch (err) {
  211. loading.value = false
  212. if (captchaOpen.value === 'true') {
  213. loginCaptcha()
  214. }
  215. }
  216. })
  217. .catch(() => {})
  218. }
  219. const configLang = (key) => {
  220. config.value.lang = key
  221. }
  222. // logo链接
  223. const handleLink = (e) => {
  224. if (!sysBaseConfig.value.SNOWY_SYS_COPYRIGHT_URL) {
  225. e?.stopPropagation()
  226. e?.preventDefault()
  227. }
  228. }
  229. </script>
  230. <style lang="less" scoped>
  231. .login-wrapper {
  232. width: 100%;
  233. min-height: 900px;
  234. height: 100vh;
  235. display: flex;
  236. position: relative;
  237. background-image: url('/src/assets/images/login/bg.png'); /* 背景图 */
  238. background-size: cover;
  239. background-position: center;
  240. .login_left {
  241. flex: 7.5;
  242. position: relative;
  243. .version {
  244. font-size: 13px;
  245. position: absolute;
  246. bottom: 40px;
  247. left: 60px;
  248. a {
  249. text-decoration: none;
  250. }
  251. div {
  252. margin-top: 8px;
  253. }
  254. }
  255. }
  256. .login_right {
  257. flex: 2.5;
  258. padding: 0 50px 50px;
  259. padding-top: 5%;
  260. background-color: rgba(0, 0, 0, 0.6);
  261. // 标题logo区域
  262. .login_head {
  263. display: flex;
  264. align-items: center;
  265. .login-logo {
  266. width: 50px;
  267. height: 45px;
  268. }
  269. .login-title {
  270. padding: 0 15px;
  271. font-size: 28px;
  272. color: #ffffff;
  273. }
  274. }
  275. // 登录表单
  276. .login_form {
  277. width: 100%;
  278. padding: 0 5px;
  279. display: flex;
  280. flex-direction: column;
  281. // 登录名称
  282. .form_head {
  283. font-size: 24px;
  284. color: #fff;
  285. margin: 60px 0 40px;
  286. }
  287. // 登录按钮
  288. .w-full {
  289. background-color: #016de8;
  290. margin-top: 10px;
  291. }
  292. // 注册
  293. .form_foot {
  294. font-size: 14px;
  295. .nav-link {
  296. text-decoration: none;
  297. color: #0052cc;
  298. }
  299. .nav-link:hover {
  300. text-decoration: underline;
  301. }
  302. }
  303. }
  304. :deep(.ant-form-item) {
  305. margin-bottom: 35px;
  306. label {
  307. font-size: 15px;
  308. color: #676767;
  309. }
  310. .ant-input,
  311. .ant-input-affix-wrapper,
  312. .ant-input-password-icon {
  313. background-color: #1a1a1a;
  314. color: #a0a0a0;
  315. border-color: #1a1a1a;
  316. }
  317. .ant-input::placeholder {
  318. font-size: 13px;
  319. color: #a0a0a0;
  320. }
  321. .ant-input:hover,
  322. .ant-input-affix-wrapper:hover {
  323. background-color: #1a1a1a;
  324. border-color: #1a1a1a;
  325. }
  326. }
  327. // 公众号
  328. .login_official {
  329. width: max-content;
  330. text-align: center;
  331. padding: 4px;
  332. box-sizing: border-box;
  333. margin: 80px auto 20px;
  334. border: 1px solid #a0a0a0;
  335. .code {
  336. width: 100px;
  337. height: 100px;
  338. }
  339. }
  340. // 提示扫码
  341. .tip {
  342. font-size: 13px;
  343. color: #a0a0a0;
  344. text-align: center;
  345. }
  346. }
  347. }
  348. </style>