index.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  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="username">
  60. <n-input
  61. v-model:value="formInline.username"
  62. :placeholder="$t('global.form_account')"
  63. >
  64. <template #prefix>
  65. <n-icon size="18">
  66. <PersonOutlineIcon></PersonOutlineIcon>
  67. </n-icon>
  68. </template>
  69. </n-input>
  70. </n-form-item>
  71. <n-form-item path="password">
  72. <n-input
  73. v-model:value="formInline.password"
  74. type="password"
  75. show-password-on="click"
  76. :placeholder="$t('global.form_password')"
  77. >
  78. <template #prefix>
  79. <n-icon size="18">
  80. <LockClosedOutlineIcon></LockClosedOutlineIcon>
  81. </n-icon>
  82. </template>
  83. </n-input>
  84. </n-form-item>
  85. <n-form-item>
  86. <div class="flex justify-between">
  87. <div class="flex-initial">
  88. <n-checkbox v-model:checked="autoLogin">{{
  89. $t('login.form_auto')
  90. }}</n-checkbox>
  91. </div>
  92. </div>
  93. </n-form-item>
  94. <n-form-item>
  95. <n-button
  96. type="primary"
  97. @click="getCode"
  98. size="large"
  99. :loading="loading"
  100. block
  101. >{{ $t('login.form_button') }}</n-button
  102. >
  103. </n-form-item>
  104. <Verify
  105. ref="verify"
  106. mode="pop"
  107. :captchaType="captchaType"
  108. :imgSize="{ width: '400px', height: '200px' }"
  109. @success="handleSubmit"
  110. />
  111. </n-form>
  112. </n-card>
  113. </n-collapse-transition>
  114. </div>
  115. </div>
  116. </div>
  117. <div class="go-login-box-footer">
  118. <layout-footer></layout-footer>
  119. </div>
  120. </div>
  121. </template>
  122. <script lang="ts" setup>
  123. import { reactive, ref, onMounted } from 'vue'
  124. import shuffle from 'lodash/shuffle'
  125. import { carouselInterval } from '@/settings/designSetting'
  126. import { useSystemStore } from '@/store/modules/systemStore/systemStore'
  127. import { SystemStoreUserInfoEnum, SystemStoreEnum } from '@/store/modules/systemStore/systemStore.d'
  128. import { GoThemeSelect } from '@/components/GoThemeSelect'
  129. import { GoLangSelect } from '@/components/GoLangSelect'
  130. import { LayoutHeader } from '@/layout/components/LayoutHeader'
  131. import { LayoutFooter } from '@/layout/components/LayoutFooter'
  132. import { PageEnum } from '@/enums/pageEnum'
  133. import { StorageEnum } from '@/enums/storageEnum'
  134. import { icon } from '@/plugins'
  135. import { routerTurnByName } from '@/utils'
  136. import { loginApi } from '@/api/path'
  137. import { Verify } from '@/components/Verifition'
  138. interface FormState {
  139. username: string
  140. password: string
  141. }
  142. const { GO_SYSTEM_STORE } = StorageEnum
  143. const { PersonOutlineIcon, LockClosedOutlineIcon } = icon.ionicons5
  144. const formRef = ref()
  145. const loading = ref(false)
  146. const autoLogin = ref(true)
  147. const show = ref(false)
  148. const showBg = ref(false)
  149. const systemStore = useSystemStore()
  150. const t = window['$t']
  151. const formInline = reactive({
  152. username: 'admin',
  153. password: 'admin123',
  154. })
  155. const rules = {
  156. username: {
  157. required: true,
  158. message: t('global.form_account'),
  159. trigger: 'blur',
  160. },
  161. password: {
  162. required: true,
  163. message: t('global.form_password'),
  164. trigger: 'blur',
  165. },
  166. }
  167. // 定时器
  168. const shuffleTimiing = ref()
  169. // 轮播图
  170. const carouselImgList = ['one', 'two', 'three']
  171. // 背景图
  172. const bgList = ref([
  173. 'bar_y',
  174. 'bar_x',
  175. 'line_gradient',
  176. 'line',
  177. 'funnel',
  178. 'heatmap',
  179. 'map',
  180. 'pie',
  181. 'radar',
  182. ])
  183. // 处理url获取
  184. const getImageUrl = (name: string, folder: string) => {
  185. return new URL(`../../assets/images/${folder}/${name}.png`, import.meta.url).href
  186. }
  187. // 打乱图片顺序
  188. const shuffleHandle = () => {
  189. shuffleTimiing.value = setInterval(() => {
  190. bgList.value = shuffle(bgList.value)
  191. }, carouselInterval)
  192. }
  193. // 验证码
  194. const verify = ref()
  195. const captchaType = ref('blockPuzzle') // blockPuzzle 滑块 clickWord 点击文字
  196. // 获取验证码
  197. const captchaEnable = import.meta.env.VITE_APP_CAPTCHA_ENABLE
  198. const getCode = async () => {
  199. // 情况一,未开启:则直接登录
  200. if (captchaEnable === 'false') {
  201. await handleSubmit({})
  202. } else {
  203. // 情况二,已开启:则展示验证码;只有完成验证码的情况,才进行登录
  204. // 弹出验证码
  205. verify.value.show()
  206. }
  207. }
  208. // 登录
  209. const handleSubmit = async (params: any) => {
  210. formRef.value.validate(async (errors: any) => {
  211. if (!errors) {
  212. const { username, password } = formInline
  213. loading.value = true
  214. // 提交请求【登录】
  215. const loginRes = await loginApi({
  216. username,
  217. password,
  218. captchaVerification: params.captchaVerification
  219. })
  220. if(loginRes && loginRes.data) {
  221. // Token 信息
  222. const tokenValue = loginRes.data.accessToken
  223. const tokenName = 'Authorization'
  224. // 个人信息
  225. const id = loginRes.data.userId
  226. const username = '芋道源码'
  227. const nickname = '芋道源码'
  228. // 存储到 pinia
  229. systemStore.setItem(SystemStoreEnum.USER_INFO, {
  230. [SystemStoreUserInfoEnum.USER_TOKEN]: tokenValue,
  231. [SystemStoreUserInfoEnum.TOKEN_NAME]: tokenName,
  232. [SystemStoreUserInfoEnum.USER_ID]: id,
  233. [SystemStoreUserInfoEnum.USER_NAME]: username,
  234. [SystemStoreUserInfoEnum.NICK_NAME]: nickname,
  235. t
  236. })
  237. window['$message'].success(t('login.login_success'))
  238. routerTurnByName(PageEnum.BASE_HOME_NAME, true)
  239. }
  240. loading.value = false
  241. } else {
  242. window['$message'].error(t('login.login_message'))
  243. }
  244. })
  245. }
  246. onMounted(() => {
  247. setTimeout(() => {
  248. show.value = true
  249. }, 300)
  250. setTimeout(() => {
  251. showBg.value = true
  252. }, 100)
  253. shuffleHandle()
  254. })
  255. </script>
  256. <style lang="scss" scoped>
  257. $width: 450px;
  258. $go-login-height: 100vh;
  259. $account-img-height: 210px;
  260. $footer-height: 50px;
  261. $carousel-width: 30%;
  262. $carousel-image-height: 60vh;
  263. * {
  264. box-sizing: border-box;
  265. }
  266. @include go(login-box) {
  267. height: $go-login-height;
  268. overflow: hidden;
  269. @include background-image('background-image');
  270. &-header {
  271. display: flex;
  272. justify-content: space-between;
  273. align-items: center;
  274. padding: 0 40px;
  275. height: $--header-height;
  276. }
  277. &-divider {
  278. margin: 0;
  279. padding-top: 0;
  280. }
  281. @include go(login) {
  282. z-index: 2;
  283. display: flex;
  284. justify-content: space-around;
  285. align-items: center;
  286. margin-top: -$--header-height;
  287. height: $go-login-height;
  288. width: 100vw;
  289. &-carousel {
  290. width: $carousel-width;
  291. margin-top: 100px;
  292. min-width: 500px;
  293. &-img {
  294. display: block;
  295. margin: 0 auto;
  296. height: $carousel-image-height;
  297. }
  298. }
  299. .login-account {
  300. display: flex;
  301. flex-direction: column;
  302. margin: 0 160px;
  303. &-container {
  304. width: $width;
  305. }
  306. &-card {
  307. @extend .go-background-filter;
  308. @include fetch-bg-color('filter-color');
  309. box-shadow: 0 0 20px 5px rgba(40, 40, 40, 0.3);
  310. }
  311. &-top {
  312. padding-top: 10px;
  313. text-align: center;
  314. height: $account-img-height;
  315. margin-bottom: 20px;
  316. }
  317. }
  318. }
  319. &-footer {
  320. z-index: 2;
  321. position: fixed;
  322. width: 100%;
  323. bottom: 0;
  324. }
  325. &-bg {
  326. z-index: 0;
  327. position: fixed;
  328. display: flex;
  329. justify-content: space-around;
  330. align-items: center;
  331. width: 100vw;
  332. height: 100vh;
  333. background: url('@/assets/images/login/login-bg.png') no-repeat 0 -120px;
  334. .bg-slot {
  335. width: $carousel-width;
  336. }
  337. .bg-img-box {
  338. position: relative;
  339. display: flex;
  340. flex-wrap: wrap;
  341. width: 770px;
  342. margin-right: -20px;
  343. &-li {
  344. img {
  345. margin-right: 20px;
  346. margin-top: 20px;
  347. width: 230px;
  348. border-radius: 2 * $--border-radius-base;
  349. opacity: 0.9;
  350. }
  351. }
  352. }
  353. }
  354. }
  355. @media only screen and (max-width: 1200px) {
  356. .bg-img-box,
  357. .bg-slot,
  358. .go-login-carousel {
  359. display: none !important;
  360. }
  361. .go-login-box-footer {
  362. position: relative;
  363. }
  364. }
  365. </style>