| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- <template>
- <div class="min-h-screen relative flex items-center justify-center font-sans">
- <img
- :src="bgImage"
- alt="background"
- class="absolute inset-0 w-full h-full object-cover"
- />
- <div class="absolute inset-0 bg-black/10"></div>
- <div class="absolute top-6 left-6 z-10 flex items-center gap-3">
- <a href="/" class="flex justify-center gap-2">
- <img :src="logo" alt="logo" class="h-9" />
- <span class="text-white text-lg font-semibold mt-1"
- >DeepOil智慧经营平台</span
- >
- </a>
- </div>
- <div class="relative z-10 w-full max-w-[420px] px-6">
- <div class="bg-white/95 backdrop-blur-sm rounded-lg p-8 shadow-2xl">
- <h1 class="text-2xl font-bold text-center">登录</h1>
- <!-- 用户名密码登陆 -->
- <div>
- <el-form
- :model="form"
- :rules="rules"
- ref="formRef"
- label-position="top"
- >
- <el-form-item prop="username">
- <el-input v-model="form.username" placeholder="请输入用户名" />
- </el-form-item>
- <el-form-item prop="password">
- <el-input
- v-model="form.password"
- type="password"
- placeholder="请输入密码"
- show-password
- />
- </el-form-item>
- <div class="flex items-center justify-between mb-3">
- <el-checkbox v-model="form.remember">记住我</el-checkbox>
- <a class="text-[#409EFF] text-sm cursor-pointer">忘记密码?</a>
- </div>
- </el-form>
- <div class="flex flex-col justify-center items-center">
- <el-button
- type="primary"
- class="w-full"
- :loading="loading"
- @click="onSubmit"
- >登录</el-button
- >
- <div class="w-full mt-3">
- <el-button class="w-full" @click="qrLogin"
- >钉钉扫码登录</el-button
- >
- </div>
- </div>
- </div>
- <!-- 钉钉登陆 -->
- <div class="text-center">
- <div id="login_container" class="pb-2"></div>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script lang="ts" setup>
- import { nextTick, onMounted, reactive, ref } from "vue";
- import { ElMessage } from "element-plus";
- import logo from "@/assets/images/logo.png";
- import bgImage from "@/assets/images/bg.png";
- import { login } from "@/api/user";
- import * as authUtil from "@/utils/auth";
- import { manualLogoutKey, reloginCancelKey } from "@/config/axios/service";
- type LoginForm = {
- username: string;
- password: string;
- remember: boolean;
- };
- const form = reactive<LoginForm>({
- username: "",
- password: "",
- remember: false,
- });
- const formRef = ref();
- const loading = ref(false);
- const rules = {
- username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
- password: [{ required: true, message: "请输入密码", trigger: "blur" }],
- };
- const onSubmit = async () => {
- await formRef.value?.validate(async (valid: boolean) => {
- if (!valid) return;
- loading.value = true;
- try {
- const res = await login({
- username: form.username,
- password: form.password,
- captchaVerification: "",
- });
- authUtil.setToken(res);
- sessionStorage.removeItem(manualLogoutKey);
- sessionStorage.removeItem(reloginCancelKey);
- if (form.remember) {
- authUtil.setLoginForm(form);
- } else {
- authUtil.removeLoginForm();
- }
- window.location.href = "/";
- ElMessage.success("登录成功");
- } finally {
- loading.value = false;
- }
- });
- };
- // QR 登录相关状态与方法
- const showQrDialog = ref(false);
- const showQrOnly = ref(false);
- const _getRandomString = (len: number) => {
- len = len || 10;
- let $chars = "ABCDEFGHIJKMNOPQRSTUVWXYZ";
- let maxPos = $chars.length;
- let pwd = "";
- for (let i = 0; i < len; i++) {
- pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
- }
- return pwd;
- };
- const handleMsg = (state?: string) => {
- return async function (event: MessageEvent) {
- if (event.origin === "https://login.dingtalk.com") {
- let loginTmpCode = event.data;
- console.log("收到钉钉扫码登录消息:", loginTmpCode);
- window.location.href =
- "https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=" +
- import.meta.env.VITE_DINGTALK_APP_ID +
- "&response_type=code&scope=snsapi_login&state=" +
- state +
- "&redirect_uri=" +
- import.meta.env.VITE_DINGTALK_REDIRECT_URI +
- "&loginTmpCode=" +
- loginTmpCode;
- // const res = await socialLogin("20", loginTmpCode as string, "22");
- // console.log("登录结果:", res);
- // authUtil.setToken(res);
- }
- };
- };
- const initDingLogin = () => {
- let state = _getRandomString(10);
- const gotoUrl = encodeURIComponent(
- "https://oapi.dingtalk.com/connect/oauth2/sns_authorize?" +
- "appid=" +
- import.meta.env.VITE_DINGTALK_APP_ID +
- "&response_type=code" +
- "&scope=snsapi_login" +
- "&state=" +
- state +
- "&redirect_uri=" +
- import.meta.env.VITE_DINGTALK_REDIRECT_URI,
- );
- nextTick(() => {
- window.DDLogin({
- id: "login_container",
- goto: gotoUrl,
- style: "border:none;background-color:transparent;",
- width: "100%",
- height: "290",
- });
- // 重置扫码登录框的样式,让登录框居中
- const box = document.getElementById("login_container");
- if (box) {
- const iframe = box.querySelector("iframe");
- if (iframe) {
- iframe.style.top = "0";
- iframe.style.bottom = "0";
- iframe.style.left = "0";
- iframe.style.right = "0";
- iframe.style.margin = "auto";
- }
- }
- });
- window.addEventListener("message", handleMsg(state), false);
- };
- const qrLogin = () => {
- showQrOnly.value = true;
- initDingLogin();
- };
- const backToPasswordLogin = () => {
- showQrOnly.value = false;
- // 卸载钉钉扫码登录组件
- let box = document.getElementById("login_container");
- if (box) {
- box.innerHTML = "";
- }
- // 移除监听事件
- window.removeEventListener("message", handleMsg());
- };
- onMounted(() => {
- qrLogin();
- });
- </script>
- <style scoped>
- .qr-dialog {
- max-height: none;
- }
- .qr-dialog .el-dialog__body {
- overflow: visible;
- padding: 20px 32px;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- }
- .qr-dialog .el-dialog__footer {
- display: flex;
- justify-content: center;
- gap: 12px;
- padding: 12px 24px 20px;
- }
- .qr-dialog img {
- max-width: 100%;
- height: auto;
- display: block;
- }
- /* 在非常小的视窗内减小二维码尺寸并防止滚动 */
- @media (max-width: 420px) {
- .qr-dialog .el-dialog {
- width: calc(100% - 32px) !important;
- }
- .qr-dialog img {
- width: 160px;
- height: 160px;
- }
- }
- /* 确保扫码登录容器居中 */
- #login_container {
- display: flex;
- justify-content: center;
- align-items: center;
- }
- /* 确保 iframe 正确居中 */
- #login_container iframe {
- position: static !important;
- margin: 0 auto !important;
- }
- /* 增强毛玻璃效果 */
- .bg-white\/95 {
- background: rgba(255, 255, 255, 0.85) !important;
- backdrop-filter: blur(10px) !important;
- -webkit-backdrop-filter: blur(10px) !important;
- border: 1px solid rgba(255, 255, 255, 0.18);
- box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15);
- }
- /* 兼容性处理 */
- @supports not (backdrop-filter: blur(10px)) {
- .bg-white\/95 {
- background: rgba(255, 255, 255, 0.95) !important;
- }
- }
- /* 确保扫码登录容器居中 */
- #login_container {
- display: flex;
- justify-content: center;
- align-items: center;
- }
- /* 确保 iframe 正确居中 */
- #login_container iframe {
- position: static !important;
- margin: 0 auto !important;
- }
- </style>
|