123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- <template>
- <div class="login-container">
- <!-- 顶部 -->
- <!-- <div class="absolute-lt flex-x-end p-3 w-full">
- <el-switch
- v-model="isDark"
- inline-prompt
- :active-icon="Moon"
- :inactive-icon="Sunny"
- @change="toggleTheme"
- />
- <lang-select class="ml-2 cursor-pointer" />
- </div> -->
- <!-- 登录表单 -->
- <el-card class="!border-none !bg-transparent !rounded-4% w-100 <sm:w-85">
- <div class="text-center relative">
- <h2>{{ defaultSettings.title }}</h2>
- <el-tag class="ml-2 absolute-rt">{{ defaultSettings.version }}</el-tag>
- </div>
- <el-form
- ref="loginFormRef"
- :model="loginData"
- :rules="loginRules"
- class="login-form"
- >
- <!-- 用户名 -->
- <el-form-item prop="userName">
- <div class="flex-y-center w-full">
- <svg-icon icon-class="user" class="mx-2" />
- <el-input
- ref="username"
- v-model="loginData.userName"
- :placeholder="$t('login.username')"
- name="userName"
- size="large"
- class="h-[48px]"
- />
- </div>
- </el-form-item>
- <!-- 密码 -->
- <el-tooltip
- :visible="isCapslock"
- :content="$t('login.capsLock')"
- placement="right"
- >
- <el-form-item prop="password">
- <div class="flex-y-center w-full">
- <svg-icon icon-class="lock" class="mx-2" />
- <el-input
- v-model="loginData.password"
- :placeholder="$t('login.password')"
- type="password"
- name="password"
- @keyup="checkCapslock"
- @keyup.enter="handleLogin"
- size="large"
- class="h-[48px] pr-2"
- show-password
- />
- </div>
- </el-form-item>
- </el-tooltip>
- <!-- 组织 -->
- <el-form-item prop="orgId">
- <div class="flex-y-center w-full">
- <svg-icon icon-class="captcha" class="mx-2" />
- <el-select
- v-model="loginData.orgId"
- placeholder="请选择组织"
- size="large"
- class="no-border"
- @keyup.enter="handleLogin"
- >
- <el-option
- v-for="item in orgList"
- :key="item.id"
- :label="item.deptName"
- :value="item.id"
- />
- </el-select>
- </div>
- </el-form-item>
- <!-- 登录按钮 -->
- <el-button
- :loading="loading"
- type="primary"
- size="large"
- class="w-full"
- @click.prevent="handleLogin"
- >{{ $t("login.login") }}
- </el-button>
- <!-- 账号密码提示 -->
- <!-- <div class="mt-10 text-sm">
- <span>{{ $t("login.username") }}: admin</span>
- <span class="ml-4"> {{ $t("login.password") }}: 123456</span>
- </div> -->
- </el-form>
- </el-card>
- <!-- ICP备案 -->
- <div class="absolute bottom-1 text-[10px] text-center" v-show="icpVisible">
- <p>Copyright © 2022-2025 jgai.com All Rights Reserved.</p>
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { useSettingsStore, useUserStore, useDictionaryStore } from "@/store";
- import { getCaptchaApi, getOrgListApi, getUserDicts } from "@/api/auth";
- import { LoginData } from "@/api/auth/types";
- import { Sunny, Moon } from "@element-plus/icons-vue";
- import { LocationQuery, LocationQueryValue, useRoute } from "vue-router";
- import defaultSettings from "@/settings";
- import { ThemeEnum } from "@/enums/ThemeEnum";
- import { usePermissionStore } from "@/store/modules/permission";
- // Stores
- const userStore = useUserStore();
- const settingsStore = useSettingsStore();
- // 数据字典相关
- const dictStore = useDictionaryStore();
- // Internationalization
- const { t } = useI18n();
- // Reactive states
- const isDark = ref(settingsStore.theme === ThemeEnum.DARK);
- const icpVisible = ref(true);
- const orgList = ref<any>([]);
- const loading = ref(false); // 按钮loading
- const isCapslock = ref(false); // 是否大写锁定
- const captchaBase64 = ref(); // 验证码图片Base64字符串
- const loginFormRef = ref(ElForm); // 登录表单ref
- const { height } = useWindowSize();
- const loginData = ref<LoginData>({
- userName: "",
- password: "",
- });
- const loginRules = computed?.(() => {
- return {
- userName: [
- {
- required: true,
- trigger: "blur",
- message: t("login.message.username.required"),
- },
- ],
- password: [
- {
- required: true,
- trigger: "blur",
- message: t("login.message.password.required"),
- },
- {
- min: 6,
- message: t("login.message.password.min"),
- trigger: "blur",
- },
- ],
- orgId: [
- {
- required: true,
- trigger: "blur",
- message: t("login.message.orgId.required"),
- },
- ],
- };
- });
- /**
- * 获取验证码
- */
- function getCaptcha() {
- getCaptchaApi().then(({ data }) => {
- loginData.value.captchaKey = data.captchaKey;
- captchaBase64.value = data.captchaBase64;
- });
- }
- function getOrgList() {
- getOrgListApi().then((data: any) => {
- orgList.value = data.data;
- if (orgList.value) {
- loginData.value.orgId = orgList.value[0].id;
- }
- });
- }
- /**
- * 登录
- */
- const route = useRoute();
- const router = useRouter();
- function handleLogin() {
- loginFormRef.value.validate((valid: boolean) => {
- if (valid) {
- toLogin();
- }
- });
- }
- const toLogin = () => {
- //保存用户名和密码
- localStorage.setItem("local_name", loginData.value.userName);
- localStorage.setItem("local_pwd", loginData.value.password);
- loading.value = true;
- userStore
- .login(loginData.value)
- .then(async () => {
- const query: LocationQuery = route.query;
- const redirect = (query.redirect as LocationQueryValue) ?? "/";
- const otherQueryParams = Object.keys(query).reduce(
- (acc: any, cur: string) => {
- if (cur !== "redirect") {
- acc[cur] = query[cur];
- }
- return acc;
- },
- {}
- );
- // 获取字典
- getUserDicts(dictStore.types).then((res) => {
- if (res.data) {
- dictStore.dicts = res?.data ?? [];
- }
- });
- router.push({
- path: redirect,
- query: { ...otherQueryParams, date: new Date().getTime() },
- });
- // router.push("/welcome");
- })
- .finally(() => {
- loading.value = false;
- });
- };
- /**
- * 主题切换
- */
- const toggleTheme = () => {
- //const newTheme = settingsStore.theme === ThemeEnum.DARK ? ThemeEnum.LIGHT : ThemeEnum.DARK;
- //settingsStore.changeTheme(newTheme);
- };
- /**
- * 根据屏幕宽度切换设备模式
- */
- watchEffect?.(() => {
- if (height.value < 600) {
- icpVisible.value = false;
- } else {
- icpVisible.value = true;
- }
- });
- /**
- * 检查输入大小写
- */
- function checkCapslock(e: any) {
- isCapslock.value = e.getModifierState("CapsLock");
- }
- onMounted?.(() => {
- // 处理SSO
- const query: LocationQuery = route.query;
- if (query.token) {
- loginData.value.token = query.token + "";
- toLogin();
- // localStorage.setItem("token", query.token + "");
- // const redirect = (query.redirect as LocationQueryValue) ?? "/";
- // // 获取字典
- // getUserDicts(dictStore.types).then((res) => {
- // if (res.data) {
- // dictStore.dicts = res?.data ?? [];
- // }
- // });
- //
- // router.push({ path: redirect });
- }
- getOrgList();
- toggleTheme();
- if (
- localStorage.getItem("local_name") &&
- localStorage.getItem("local_name") !== "null"
- ) {
- loginData.value.userName = localStorage.getItem("local_name");
- loginData.value.password = localStorage.getItem("local_pwd");
- }
- });
- </script>
- <style lang="scss" scoped>
- html.dark .login-container {
- background: url("@/assets/images/login-bg-dark.jpg") no-repeat center right;
- }
- .login-container {
- overflow-y: auto;
- background: url("@/assets/images/login-bg.jpg") no-repeat center right;
- @apply wh-full flex-center;
- .login-form {
- padding: 30px 10px;
- }
- }
- .el-form-item {
- background: var(--el-input-bg-color);
- border: 1px solid var(--el-border-color);
- border-radius: 5px;
- }
- :deep(.el-input) {
- .el-input__wrapper {
- padding: 0;
- background-color: transparent;
- box-shadow: none;
- &.is-focus,
- &:hover {
- box-shadow: none !important;
- }
- input:-webkit-autofill {
- /* 通过延时渲染背景色变相去除背景颜色 */
- transition: background-color 1000s ease-in-out 0s;
- }
- }
- }
- :deep(.el-select) {
- .el-select__wrapper {
- padding: 0;
- background-color: transparent;
- box-shadow: none;
- &.is-focus,
- &:hover {
- box-shadow: none !important;
- }
- input:-webkit-autofill {
- /* 通过延时渲染背景色变相去除背景颜色 */
- transition: background-color 1000s ease-in-out 0s;
- }
- }
- }
- </style>
|