index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. <template>
  2. <!-- 登录 -->
  3. <div class="go-login-box">
  4. <div class="go-login-box-bg">
  5. <aside class="bg-slot"></aside>
  6. <aside class="bg-img-box">
  7. <transition-group name="list-complete">
  8. <template v-for="item in bgList" :key="item">
  9. <div class="bg-img-box-li list-complete-item">
  10. <n-collapse-transition :appear="true" :show="showBg">
  11. <img :src="getImageUrl(item, 'chart/charts')" alt="chart" />
  12. </n-collapse-transition>
  13. </div>
  14. </template>
  15. </transition-group>
  16. </aside>
  17. </div>
  18. <layout-header>
  19. <template #left></template>
  20. <template #right>
  21. <go-lang-select></go-lang-select>
  22. <go-theme-select></go-theme-select>
  23. </template>
  24. </layout-header>
  25. <div class="go-login">
  26. <div class="go-login-carousel">
  27. <n-carousel
  28. autoplay
  29. dot-type="line"
  30. :interval="Number(carouselInterval)"
  31. >
  32. <img
  33. v-for="(item, i) in carouselImgList"
  34. :key="i"
  35. class="go-login-carousel-img"
  36. :src="getImageUrl(item, 'login')"
  37. alt="image"
  38. />
  39. </n-carousel>
  40. </div>
  41. <div class="login-account">
  42. <div class="login-account-container">
  43. <n-collapse-transition :appear="true" :show="show">
  44. <n-card class="login-account-card" :title="$t('login.desc')">
  45. <div class="login-account-top">
  46. <img
  47. class="login-account-top-logo"
  48. src="~@/assets/images/login/input.png"
  49. alt="展示图片"
  50. />
  51. </div>
  52. <n-form
  53. ref="formRef"
  54. label-placement="left"
  55. size="large"
  56. :model="formInline"
  57. :rules="rules"
  58. >
  59. <n-form-item path="tenantId" v-if="tenantEnable ==='true'">
  60. <n-input
  61. v-model:value="formInline.tenantName"
  62. :placeholder="$t('global.form_tenant')"
  63. >
  64. <template #prefix>
  65. <n-icon size="18">
  66. <TvOutlineIcon></TvOutlineIcon>
  67. </n-icon>
  68. </template>
  69. </n-input>
  70. </n-form-item>
  71. <n-form-item path="username">
  72. <n-input
  73. v-model:value="formInline.username"
  74. :placeholder="$t('global.form_account')"
  75. >
  76. <template #prefix>
  77. <n-icon size="18">
  78. <PersonOutlineIcon></PersonOutlineIcon>
  79. </n-icon>
  80. </template>
  81. </n-input>
  82. </n-form-item>
  83. <n-form-item path="password">
  84. <n-input
  85. v-model:value="formInline.password"
  86. type="password"
  87. show-password-on="click"
  88. :placeholder="$t('global.form_password')"
  89. >
  90. <template #prefix>
  91. <n-icon size="18">
  92. <LockClosedOutlineIcon></LockClosedOutlineIcon>
  93. </n-icon>
  94. </template>
  95. </n-input>
  96. </n-form-item>
  97. <n-form-item>
  98. <div class="flex justify-between">
  99. <div class="flex-initial">
  100. <n-checkbox v-model:checked="autoLogin">{{
  101. $t('login.form_auto')
  102. }}</n-checkbox>
  103. </div>
  104. </div>
  105. </n-form-item>
  106. <n-form-item>
  107. <n-button
  108. type="primary"
  109. @click="getCode"
  110. size="large"
  111. :loading="loading"
  112. block
  113. >{{ $t('login.form_button') }}</n-button
  114. >
  115. </n-form-item>
  116. <Verify
  117. ref="verify"
  118. mode="pop"
  119. :captchaType="captchaType"
  120. :imgSize="{ width: '400px', height: '200px' }"
  121. @success="handleSubmit"
  122. />
  123. </n-form>
  124. </n-card>
  125. </n-collapse-transition>
  126. </div>
  127. </div>
  128. </div>
  129. <div class="go-login-box-footer">
  130. <layout-footer></layout-footer>
  131. </div>
  132. </div>
  133. </template>
  134. <script lang="ts" setup>
  135. import { reactive, ref, onMounted } from 'vue'
  136. import shuffle from 'lodash/shuffle'
  137. import { carouselInterval } from '@/settings/designSetting'
  138. import { useSystemStore } from '@/store/modules/systemStore/systemStore'
  139. import { SystemStoreUserInfoEnum, SystemStoreEnum } from '@/store/modules/systemStore/systemStore.d'
  140. import { GoThemeSelect } from '@/components/GoThemeSelect'
  141. import { GoLangSelect } from '@/components/GoLangSelect'
  142. import { LayoutHeader } from '@/layout/components/LayoutHeader'
  143. import { LayoutFooter } from '@/layout/components/LayoutFooter'
  144. import {PageEnum, PreviewEnum} from '@/enums/pageEnum'
  145. import { StorageEnum } from '@/enums/storageEnum'
  146. import { icon } from '@/plugins'
  147. import {
  148. fetchPathByName,
  149. getSessionStorage,
  150. previewPath,
  151. routerTurnByName,
  152. routerTurnByPath,
  153. setSessionStorage
  154. } from '@/utils'
  155. import {getTenantIdByNameApi, getUserProfileApi, loginApi} from '@/api/path'
  156. import { Verify } from '@/components/Verifition'
  157. interface FormState {
  158. username: string
  159. password: string
  160. }
  161. const { GO_SYSTEM_STORE } = StorageEnum
  162. const { PersonOutlineIcon, LockClosedOutlineIcon, TvOutlineIcon } = icon.ionicons5
  163. const formRef = ref()
  164. const loading = ref(false)
  165. const autoLogin = ref(true)
  166. const show = ref(false)
  167. const showBg = ref(false)
  168. const systemStore = useSystemStore()
  169. const viteRouter = import.meta.env.VITE_ROUTER_DEFAULT
  170. const t = window['$t']
  171. const formInline = reactive({
  172. tenantName: '芋道源码',
  173. username: 'admin',
  174. password: 'admin123',
  175. })
  176. const rules = {
  177. username: {
  178. required: true,
  179. message: t('global.form_account'),
  180. trigger: 'blur',
  181. },
  182. password: {
  183. required: true,
  184. message: t('global.form_password'),
  185. trigger: 'blur',
  186. },
  187. tenantName: {
  188. required: true,
  189. message: t('global.form_password'),
  190. trigger: 'blur',
  191. },
  192. }
  193. // 定时器
  194. const shuffleTimiing = ref()
  195. // 轮播图
  196. const carouselImgList = ['one', 'two', 'three']
  197. // 背景图
  198. const bgList = ref([
  199. 'bar_y',
  200. 'bar_x',
  201. 'line_gradient',
  202. 'line',
  203. 'funnel',
  204. 'heatmap',
  205. 'map',
  206. 'pie',
  207. 'radar',
  208. ])
  209. // 处理url获取
  210. const getImageUrl = (name: string, folder: string) => {
  211. return new URL(`../../assets/images/${folder}/${name}.png`, import.meta.url).href
  212. }
  213. // 打乱图片顺序
  214. const shuffleHandle = () => {
  215. shuffleTimiing.value = setInterval(() => {
  216. bgList.value = shuffle(bgList.value)
  217. }, carouselInterval)
  218. }
  219. // 验证码
  220. const verify = ref()
  221. const captchaType = ref('blockPuzzle') // blockPuzzle 滑块 clickWord 点击文字
  222. const captchaEnable = import.meta.env.VITE_APP_CAPTCHA_ENABLE
  223. // 获取验证码
  224. const getCode = async () => {
  225. // 情况一,未开启:则直接登录
  226. if (captchaEnable === 'false') {
  227. await handleSubmit({})
  228. } else {
  229. // 情况二,已开启:则展示验证码;只有完成验证码的情况,才进行登录
  230. // 弹出验证码
  231. verify.value.show()
  232. }
  233. }
  234. // 多租户
  235. const tenantEnable = import.meta.env.VITE_APP_TENANT_ENABLE
  236. // 获取租户 ID
  237. const getTenantId = async () => {
  238. if (tenantEnable === 'true') {
  239. const res = await getTenantIdByNameApi(formInline.tenantName)
  240. if (res && res.data) {
  241. // 存储到 pinia
  242. systemStore.setItem(SystemStoreEnum.TENANT_INFO, {
  243. tenantId: res.data
  244. })
  245. }
  246. }
  247. }
  248. // 登录
  249. const handleSubmit = async (params: any) => {
  250. formRef.value.validate(async (errors: any) => {
  251. if (!errors) {
  252. // 获取租户 ID
  253. await getTenantId()
  254. const { username, password } = formInline
  255. loading.value = true
  256. // 提交请求【登录】
  257. const loginRes = await loginApi({
  258. username,
  259. password,
  260. captchaVerification: params.captchaVerification
  261. })
  262. if(loginRes && loginRes.data) {
  263. // ① Token 信息(先存储下,保证可以加载个人信息)
  264. const tokenValue = loginRes.data.accessToken
  265. const tokenName = 'Authorization'
  266. const refreshToken = loginRes.data.refreshToken
  267. systemStore.setItem(SystemStoreEnum.USER_INFO, {
  268. [SystemStoreUserInfoEnum.USER_TOKEN]: tokenValue,
  269. [SystemStoreUserInfoEnum.TOKEN_NAME]: tokenName
  270. })
  271. // 个人信息
  272. const profileRes = await getUserProfileApi()
  273. const id = loginRes.data.userId
  274. const username = profileRes?.data?.nickname
  275. const nickname = profileRes?.data?.nickname
  276. // 存储到 pinia
  277. systemStore.setItem(SystemStoreEnum.USER_INFO, {
  278. [SystemStoreUserInfoEnum.USER_TOKEN]: tokenValue,
  279. [SystemStoreUserInfoEnum.USER_REFRESH_TOKEN]: refreshToken,
  280. [SystemStoreUserInfoEnum.TOKEN_NAME]: tokenName,
  281. [SystemStoreUserInfoEnum.USER_ID]: id,
  282. [SystemStoreUserInfoEnum.USER_NAME]: username,
  283. [SystemStoreUserInfoEnum.NICK_NAME]: nickname,
  284. })
  285. window['$message'].success(t('login.login_success'))
  286. const redirectPath:string = getSessionStorage('setRedirectPath') as string
  287. const redirectPathId = getSessionStorage('setRedirectPathId')
  288. console.log(redirectPath)
  289. console.log(redirectPathId)
  290. if(redirectPath && viteRouter==='false'){
  291. console.log("重定向缓存页面",redirectPath,viteRouter,!viteRouter,!!viteRouter)
  292. routerTurnByPath(redirectPath,[redirectPathId],true,false)
  293. }else{
  294. console.log("重定向到首页》》》")
  295. routerTurnByName(PageEnum.BASE_HOME_NAME, true);
  296. }
  297. // routerTurnByName(PageEnum.BASE_HOME_NAME, true)
  298. }
  299. loading.value = false
  300. } else {
  301. window['$message'].error(t('login.login_message'))
  302. }
  303. })
  304. }
  305. onMounted(() => {
  306. setTimeout(() => {
  307. show.value = true
  308. }, 300)
  309. setTimeout(() => {
  310. showBg.value = true
  311. }, 100)
  312. shuffleHandle()
  313. })
  314. </script>
  315. <style lang="scss" scoped>
  316. $width: 450px;
  317. $go-login-height: 100vh;
  318. $account-img-height: 210px;
  319. $footer-height: 50px;
  320. $carousel-width: 30%;
  321. $carousel-image-height: 60vh;
  322. * {
  323. box-sizing: border-box;
  324. }
  325. @include go(login-box) {
  326. height: $go-login-height;
  327. overflow: hidden;
  328. @include background-image('background-image');
  329. &-header {
  330. display: flex;
  331. justify-content: space-between;
  332. align-items: center;
  333. padding: 0 40px;
  334. height: $--header-height;
  335. }
  336. &-divider {
  337. margin: 0;
  338. padding-top: 0;
  339. }
  340. @include go(login) {
  341. z-index: 2;
  342. display: flex;
  343. justify-content: space-around;
  344. align-items: center;
  345. margin-top: -$--header-height;
  346. height: $go-login-height;
  347. width: 100vw;
  348. &-carousel {
  349. width: $carousel-width;
  350. margin-top: 100px;
  351. min-width: 500px;
  352. &-img {
  353. display: block;
  354. margin: 0 auto;
  355. height: $carousel-image-height;
  356. }
  357. }
  358. .login-account {
  359. display: flex;
  360. flex-direction: column;
  361. margin: 0 160px;
  362. &-container {
  363. width: $width;
  364. }
  365. &-card {
  366. @extend .go-background-filter;
  367. @include fetch-bg-color('filter-color');
  368. box-shadow: 0 0 20px 5px rgba(40, 40, 40, 0.3);
  369. }
  370. &-top {
  371. padding-top: 10px;
  372. text-align: center;
  373. height: $account-img-height;
  374. margin-bottom: 20px;
  375. }
  376. }
  377. }
  378. &-footer {
  379. z-index: 2;
  380. position: fixed;
  381. width: 100%;
  382. bottom: 0;
  383. }
  384. &-bg {
  385. z-index: 0;
  386. position: fixed;
  387. display: flex;
  388. justify-content: space-around;
  389. align-items: center;
  390. width: 100vw;
  391. height: 100vh;
  392. background: url('@/assets/images/login/login-bg.png') no-repeat 0 -120px;
  393. .bg-slot {
  394. width: $carousel-width;
  395. }
  396. .bg-img-box {
  397. position: relative;
  398. display: flex;
  399. flex-wrap: wrap;
  400. width: 770px;
  401. margin-right: -20px;
  402. &-li {
  403. img {
  404. margin-right: 20px;
  405. margin-top: 20px;
  406. width: 230px;
  407. border-radius: 2 * $--border-radius-base;
  408. opacity: 0.9;
  409. }
  410. }
  411. }
  412. }
  413. }
  414. @media only screen and (max-width: 1200px) {
  415. .bg-img-box,
  416. .bg-slot,
  417. .go-login-carousel {
  418. display: none !important;
  419. }
  420. .go-login-box-footer {
  421. position: relative;
  422. }
  423. }
  424. </style>