single-window-login.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. <template>
  2. <div class="single-window-login">
  3. <div class="swl-head">
  4. <div class="swl-head-center">
  5. <a href="https://www.singlewindow.cn"><img src="https://update.singlewindow.cn/images/swLogo.png"></a>
  6. <div class="swl-head-lang">
  7. <div class="__hover" :class="{ active: config.lang === 'zh-cn' }" @click="configLang('zh-cn')">简体中文</div>
  8. <div>/</div>
  9. <div class="__hover" :class="{ active: config.lang === 'en' }" @click="configLang('en')">English</div>
  10. </div>
  11. </div>
  12. </div>
  13. <div class="swl-content">
  14. <div class="swl-content-login">
  15. <div class="swl-content-login-left"/>
  16. <div class="swl-content-login-right">
  17. <div class="swlclr-login">
  18. <div class="swlclr-login-div">
  19. <div class="swlclr-login-div-title">账号登录</div>
  20. <a-form class="login-form" ref="loginForm" :model="ruleForm" :rules="rules">
  21. <a-form-item name="account">
  22. <a-input
  23. v-model:value="ruleForm.account"
  24. :placeholder="$t('login.accountPlaceholder')"
  25. size="large"
  26. @keyup.enter="login"
  27. >
  28. <template #prefix>
  29. <UserOutlined class="login-icon-gray" />
  30. </template>
  31. </a-input>
  32. </a-form-item>
  33. <a-form-item name="password">
  34. <a-input-password
  35. v-model:value="ruleForm.password"
  36. :placeholder="$t('login.PWPlaceholder')"
  37. size="large"
  38. autocomplete="off"
  39. @keyup.enter="login"
  40. >
  41. <template #prefix>
  42. <LockOutlined class="login-icon-gray" />
  43. </template>
  44. </a-input-password>
  45. </a-form-item>
  46. <a-form-item name="validCode" v-if="captchaOpen === 'true'">
  47. <a-row :gutter="8">
  48. <a-col :span="17">
  49. <a-input
  50. v-model:value="ruleForm.validCode"
  51. :placeholder="$t('login.validLaceholder')"
  52. size="large"
  53. @keyup.enter="login"
  54. >
  55. <template #prefix>
  56. <verified-outlined class="login-icon-gray" />
  57. </template>
  58. </a-input>
  59. </a-col>
  60. <a-col :span="7">
  61. <img :src="validCodeBase64" class="login-validCode-img" @click="loginCaptcha" />
  62. </a-col>
  63. </a-row>
  64. </a-form-item>
  65. <div class="agree">
  66. <a-checkbox v-model:checked="loginAgree"/>
  67. <span style="color:#222222">已阅读并同意</span>
  68. <span style="color: #096ac6;text-decoration:underline dotted; cursor:pointer;" @click="agreeTipsYhfwxy = true">《用户服务协议》</span>
  69. <span>和</span>
  70. <span style="color: #096ac6;text-decoration:underline dotted; cursor:pointer;" @click="agreeTipsYhyszc = true">《用户隐私政策》</span>
  71. </div>
  72. <a-button type="primary" class="w-full mt-4" :loading="loading" round size="large" @click="login">
  73. {{ $t('login.signIn') }}
  74. </a-button>
  75. <div class="zhuce">还没有账号?<span>立即注册</span></div>
  76. </a-form>
  77. </div>
  78. </div>
  79. </div>
  80. </div>
  81. </div>
  82. <div class="swl-foot" v-if="config.lang === 'zh-cn'">
  83. </div>
  84. <a-modal
  85. v-model:visible="agreeTipsYhfwxy"
  86. title="用户服务协议"
  87. centered
  88. width="730px"
  89. @ok="agreeTipsYhfwxy = false, loginAgree = true"
  90. >
  91. <iframe src="https://app.singlewindow.cn/userserver/user/document/nraServiceAgreement" style="width: 100%; height: 530px;"/>
  92. </a-modal>
  93. <a-modal
  94. v-model:visible="agreeTipsYhyszc"
  95. title="用户隐私政策"
  96. centered
  97. width="730px"
  98. @ok="agreeTipsYhyszc = false, loginAgree = true"
  99. >
  100. <iframe src="https://app.singlewindow.cn/userserver/user/document/nraPrivacyPolicy" style="width: 100%; height: 530px;"/>
  101. </a-modal>
  102. </div>
  103. </template>
  104. <script setup>
  105. import loginApi from '@/api/auth/loginApi'
  106. import smCrypto from '@/utils/smCrypto'
  107. import { required } from '@/utils/formRules'
  108. import { afterLogin } from './util'
  109. import configData from '@/config'
  110. import configApi from '@/api/dev/configApi'
  111. import tool from '@/utils/tool'
  112. import { globalStore, iframeStore, keepAliveStore, viewTagsStore } from '@/store'
  113. import {message, Modal} from "ant-design-vue";
  114. import {createVNode} from "vue";
  115. import {ExclamationCircleOutlined} from "@ant-design/icons-vue";
  116. import router from "@/router";
  117. const { proxy } = getCurrentInstance()
  118. const activeKey = ref('userAccount')
  119. const captchaOpen = ref(configData.SYS_BASE_CONFIG.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN)
  120. const validCodeBase64 = ref('')
  121. const loading = ref(false)
  122. const loginAgree = ref(false)
  123. const agreeTipsYhfwxy = ref(false)
  124. const agreeTipsYhyszc = ref(false)
  125. const ruleForm = reactive({
  126. account: 'superAdmin',
  127. password: '123456',
  128. validCode: '',
  129. validCodeReqNo: '',
  130. autologin: false
  131. })
  132. const rules = reactive({
  133. account: [required(proxy.$t('login.accountError'), 'blur')],
  134. password: [required(proxy.$t('login.PWError'), 'blur')]
  135. })
  136. const lang = ref([
  137. {
  138. name: '简体中文',
  139. value: 'zh-cn'
  140. },
  141. {
  142. name: 'English',
  143. value: 'en'
  144. }
  145. ])
  146. const config = ref({
  147. lang: tool.data.get('APP_LANG') || proxy.$CONFIG.LANG,
  148. theme: tool.data.get('APP_THEME') || 'default'
  149. })
  150. const store = globalStore()
  151. const kStore = keepAliveStore()
  152. const iStore = iframeStore()
  153. const vStore = viewTagsStore()
  154. const setSysBaseConfig = store.setSysBaseConfig
  155. const clearKeepLive = kStore.clearKeepLive
  156. const clearIframeList = iStore.clearIframeList
  157. const clearViewTags = vStore.clearViewTags
  158. const sysBaseConfig = computed(() => {
  159. return store.sysBaseConfig
  160. })
  161. onMounted(() => {
  162. let formData = ref(configData.SYS_BASE_CONFIG)
  163. configApi.configSysBaseList().then((data) => {
  164. if (data) {
  165. data.forEach((item) => {
  166. formData.value[item.configKey] = item.configValue
  167. })
  168. captchaOpen.value = formData.value.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN
  169. tool.data.set('SNOWY_SYS_BASE_CONFIG', formData.value)
  170. setSysBaseConfig(formData.value)
  171. refreshSwitch()
  172. }
  173. })
  174. })
  175. onBeforeMount(() => {
  176. clearViewTags()
  177. clearKeepLive()
  178. clearIframeList()
  179. })
  180. // 监听语言
  181. watch(
  182. () => config.value.lang,
  183. (newValue) => {
  184. proxy.$i18n.locale = newValue
  185. tool.data.set('APP_LANG', newValue)
  186. }
  187. )
  188. // 主题
  189. watch(
  190. () => config.value.theme,
  191. (newValue) => {
  192. document.body.setAttribute('data-theme', newValue)
  193. }
  194. )
  195. // 通过开关加载内容
  196. const refreshSwitch = () => {
  197. // 判断是否开启验证码
  198. if (captchaOpen.value === 'true') {
  199. // 加载验证码
  200. loginCaptcha()
  201. // 加入校验
  202. rules.validCode = [required(proxy.$t('login.validError'), 'blur')]
  203. }
  204. }
  205. // 获取验证码
  206. const loginCaptcha = () => {
  207. loginApi.getPicCaptcha().then((data) => {
  208. validCodeBase64.value = data.validCodeBase64
  209. ruleForm.validCodeReqNo = data.validCodeReqNo
  210. })
  211. }
  212. //登陆
  213. const loginForm = ref()
  214. const login = async () => {
  215. if (loginAgree.value) {
  216. loginForm.value
  217. .validate()
  218. .then(async () => {
  219. loading.value = true
  220. const loginData = {
  221. account: ruleForm.account,
  222. // 密码进行SM2加密,传输过程中看到的只有密文,后端存储使用hash
  223. password: smCrypto.doSm2Encrypt(ruleForm.password),
  224. validCode: ruleForm.validCode,
  225. validCodeReqNo: ruleForm.validCodeReqNo
  226. }
  227. // 获取token
  228. // loginApi.login(loginData).then((loginToken) => {
  229. // afterLogin(loginToken)
  230. // }).catch(() => {
  231. // loading.value = false
  232. // loginCaptcha()
  233. // })
  234. // 获取token
  235. try {
  236. const loginToken = await loginApi.login(loginData)
  237. const loginAfter = afterLogin(loginToken)
  238. } catch (err) {
  239. loading.value = false
  240. loginCaptcha()
  241. }
  242. })
  243. .catch(() => {})
  244. } else {
  245. Modal.confirm({
  246. title: '提示',
  247. content: '请阅读并勾选《用户服务协议》和《用户隐私政策》!',
  248. centered: true,
  249. width: 420,
  250. icon: createVNode(ExclamationCircleOutlined),
  251. maskClosable: false,
  252. onOk() {},
  253. onCancel() {}
  254. })
  255. }
  256. }
  257. const configLang = (key) => {
  258. config.value.lang = key
  259. }
  260. </script>
  261. <style lang="less" scoped>
  262. .single-window-login {
  263. display: flex;
  264. flex-direction: column;
  265. width: 100%;
  266. height: 100%;
  267. .swl-head {
  268. background-color: #ffffff;
  269. min-width: 1200px;
  270. height: 90px;
  271. line-height: 90px;
  272. display: flex;
  273. .swl-head-center {
  274. width: 1200px;
  275. display: flex;
  276. align-items: center;
  277. margin: 0 auto;
  278. .swl-head-lang {
  279. margin-left: auto;
  280. display: flex;
  281. align-items: center;
  282. gap: 10px;
  283. color: #00599C;
  284. font-size: 15px;
  285. &:nth-child(2) {
  286. font-size: 12px;
  287. color: #00599C;
  288. }
  289. .active {
  290. color: #00599C;
  291. font-weight: bold;
  292. font-size: 16px;
  293. }
  294. }
  295. }
  296. }
  297. .swl-content {
  298. flex: 1;
  299. background-image: url('https://app.singlewindow.cn/cas/images/newLogin/icon_bgNew.png');
  300. background-repeat: no-repeat;
  301. background-size: 100% 100%;
  302. .swl-content-login {
  303. height: 659px;
  304. max-width: 1200px;
  305. display: flex;
  306. flex-direction: row;
  307. align-items: center;
  308. justify-content: center;
  309. margin: 0 auto;
  310. .swl-content-login-left {
  311. width: 553px;
  312. height: 478px;
  313. background-image: url('https://app.singlewindow.cn/cas/images/leftimg_04.png');
  314. background-repeat: no-repeat;
  315. background-position: center;
  316. }
  317. .swl-content-login-right {
  318. width: 600px;
  319. height: 478px;
  320. .swlclr-login {
  321. width: 412px;
  322. height: auto;
  323. background-color: #FFFFFF;
  324. border: solid #ccc 0px;
  325. border-radius: 8px;
  326. margin: 0px auto;
  327. color: #666666;
  328. padding-bottom: 12px;
  329. .swlclr-login-div {
  330. width: 410px;
  331. height: 420px;
  332. background: #ffffff;
  333. border-radius: 10px;
  334. margin: 20px auto;
  335. padding: 26px 41px 0 39px;
  336. box-sizing: border-box;
  337. position: relative;
  338. .swlclr-login-div-title {
  339. font-weight: 700;
  340. color: #00599c;
  341. font-size: 18px;
  342. margin-top: 18px;
  343. margin-bottom: 28px;
  344. text-align: center;
  345. }
  346. :deep(.login-form) {
  347. .ant-form-item-control-input-content {
  348. border-bottom: 1px solid #e5e5e5;
  349. .ant-input-affix-wrapper {
  350. border: none;
  351. box-shadow: none;
  352. }
  353. }
  354. }
  355. .agree {
  356. font-size: 12px;
  357. }
  358. .zhuce {
  359. margin-top: 40px;
  360. width: 100%;
  361. text-align: right;
  362. font-size: 13px;
  363. color: #999999;
  364. >span {
  365. color: #096AC6;
  366. font-weight: 700;
  367. }
  368. }
  369. }
  370. }
  371. }
  372. }
  373. }
  374. .swl-foot {
  375. margin: 0 auto;
  376. width: 1200px;
  377. height: 160px;
  378. background-color: rgba(230, 230, 230, 1);
  379. }
  380. }
  381. </style>