WXL
17 小时以前 05c363fdd7ab04e3bd9a753e2c5d5bfff04d681c
pages/login/Login.vue
@@ -1,16 +1,20 @@
<template>
  <view class="login-container">
    <view v-if="isAutoLogining" class="sso-mask">
      <uni-load-more
        status="loading"
        :content-text="{ contentdown: '自动登录中...' }"
      ></uni-load-more>
    </view>
    <view class="header">
      <image src="/static/avatar/logo.png" class="logo" />
      <text class="hospital-name">青附院OPO管理平台</text>
      <text class="hospital-name">青大附院OPO管理平台</text>
    </view>
    <view class="form-container">
      <view class="input-group">
        <uni-icons type="contact" size="24" color="#409EFF" />
        <input v-model="username" placeholder="请输入账号" class="input" />
      </view>
      <view class="input-group">
        <uni-icons type="locked" size="24" color="#409EFF" />
        <input
@@ -26,7 +30,6 @@
          @click="showPassword = !showPassword"
        />
      </view>
      <button
        class="login-btn"
        :class="{ active: username && password }"
@@ -35,145 +38,262 @@
      >
        登录
      </button>
      <!-- <view class="footer-links">
        <view @click="gotoRegister">注册账号</view>
        <view @click="gotoForgetPassword">忘记密码</view>
      </view> -->
    </view>
  </view>
</template>
<script setup>
import { ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import { onLoad, onShow } from "@dcloudio/uni-app";
import { useUserStore } from "@/stores/user";
import { getToken, setToken } from "@/utils/auth";
import { encrypt } from "@/utils/crypto";
let isAutoLogining = false;
let hasAutoLogin = false;
const username = ref("");
const password = ref("");
const showPassword = ref(false);
const isHarmonyOS = ref(false);
const redirect = ref("/pages/index/index"); // 默认跳转首页
const redirect = ref("");
onLoad((options) => {
  // 格式化当前日期为 YYYYMMDD
  const getCurrentDate = () => {
    const now = new Date();
    const year = now.getFullYear();
    const month = String(now.getMonth() + 1).padStart(2, "0");
    const day = String(now.getDate()).padStart(2, "0");
    return `${year}${month}${day}`;
  };
// tabBar 页面列表(与 pages.json 保持一致)
const tabBarPages = [
  "/pages/index/index",
  "/pages/appointment/index",
  "/pages/consultation/index",
  "/pages/my/index",
];
  // 自动生成密码函数
  const generatePassword = () => {
    const currentDate = getCurrentDate();  // 直接调用函数
    return `Hrs#${currentDate}*`;
  };
onLoad(async (options) => {
  console.log("登录页onLoad,原始参数:", options);
  const launchQuery = uni.getLaunchOptionsSync().query || {};
  const allParams = { ...launchQuery, ...options };
  console.log("合并后参数:", allParams);
  if (options.redirect) {
    redirect.value = decodeURIComponent(options.redirect);
  // 提取 redirect
  if (allParams.redirect) {
    redirect.value = decodeURIComponent(allParams.redirect);
  }
  password.value = generatePassword();  // 直接调用函数
  username.value = "admin";
  // 检测是否鸿蒙系统
  // #ifdef HARMONY
  isHarmonyOS.value = true;
  // #endif
  // ✅ 优先处理 code 免登
  const code = allParams.code;
  if (code) {
    console.log("检测到免登码,开始解析:", code);
    // 显示 loading
    uni.showLoading({ title: "解析免登信息...", mask: true });
    try {
      // 调用解析接口
      const res = await uni.$uapi.get("/GiLink/getCode", { code });
      uni.hideLoading();
      console.log(res.data);
      res.data.passWord = res.data.userPhone;
      if (!res.data || !res.data.userName || !res.data.passWord) {
        throw new Error("解析免登码失败");
      }
      let obj = JSON.parse(res.data.extContent);
      // const { userName, passWord, ...otherParams } = res;
      console.log(res.data.userName);
      const userName = res.data.userName;
      const passWord = res.data.passWord;
      const otherParams = {
        id: obj.infoId,
        type: obj.type,
        status: obj.status,
        fcid: obj.id,
      };
      console.log("解析结果:", { userName, otherParams });
      // 清除可能存在的旧 token
      if (getToken()) uni.removeStorageSync("token");
      // 执行 SSO 登录,并将其他业务参数传递下去
      await handleSSOLogin(userName, passWord, otherParams);
    } catch (err) {
      uni.hideLoading();
      console.error("免登码解析失败:", err);
      uni.showToast({ title: "免登录失败,请手动登录", icon: "none" });
    }
    return;
  }
  // 原有逻辑:无 code 且已有 token 跳转目标页
  if (getToken()) {
    setTimeout(() => navigateToTarget(), 100);
    return;
  }
  // 普通登录,清空表单
  username.value = "";
  password.value = "";
});
onShow(() => {
  if (hasAutoLogin) hasAutoLogin = false;
});
/**
 * SSO 免登
 */
const handleSSOLogin = async (userName, passWord, pageParams = {}) => {
  if (isAutoLogining) return;
  isAutoLogining = true;
  console.log("开始SSO免登:", { userName, pageParams });
  uni.showLoading({ title: "自动登录中...", mask: true });
  try {
    const tokenRes = await uni.$uapi.post("/getToken", { userName, passWord });
    uni.hideLoading();
    if (!tokenRes.data?.token) throw new Error("获取token失败");
    setToken(tokenRes.data.token);
    const userStore = useUserStore();
    const userInfo = await uni.$uapi.get("/getInfo");
    if (userInfo && userInfo.user) {
      userStore.setUserInfo(userInfo.user);
      if (userInfo.roles) userStore.setroleKey(userInfo.roles);
    }
    // 确定目标页面
    let targetPage = redirect.value;
    if (!targetPage) {
      const launchOptions = uni.getLaunchOptionsSync();
      const originalPath = launchOptions.path;
      if (originalPath && !originalPath.includes("login/Login")) {
        targetPage = "/" + originalPath;
      }
    }
    if (!targetPage) targetPage = "/pages/index/index";
    hasAutoLogin = true;
    // 跳转时携带 pageParams(业务参数)
    await navigateToTargetPage(targetPage, pageParams);
  } catch (err) {
    uni.hideLoading();
    console.error("SSO免登失败:", err);
    uni.showToast({
      title: err.message || "自动登录失败,请手动登录",
      icon: "none",
      duration: 3000,
    });
    uni.removeStorageSync("token");
  } finally {
    isAutoLogining = false;
  }
};
/**
 * 普通登录
 */
const handleLogin = async () => {
  try {
    const userStore = useUserStore();
    if (!username.value || !password.value) {
      uni.showToast({ title: "请输入账号密码", icon: "none" });
      return;
    }
    uni.showLoading({ title: "登录中...", mask: true });
    // 1. 登录获取token
    const userStore = useUserStore();
    const encryptedPassword = encrypt(password.value);
    const encryptedUsername = encrypt(username.value);
    const loginRes = await uni.$uapi.post("/login", {
      username: username.value,
      password: password.value,
      username: encryptedUsername,
      password: encryptedPassword,
    });
    // 2. 存储token
    userStore.setToken(loginRes.token);
    if (!loginRes || !loginRes.token) throw new Error("登录失败");
    setToken(loginRes.token);
    // 3. 获取用户信息
    let userInfo;
    try {
      userInfo = await uni.$uapi.get("/getInfo");
    } catch (err) {
      throw new Error("获取用户信息失败:" + (err.message || "未知错误"));
    const userInfo = await uni.$uapi.get("/getInfo");
    if (userInfo && userInfo.user) {
      userStore.setUserInfo(userInfo.user);
      if (userInfo.roles) userStore.setroleKey(userInfo.roles);
    }
    // 4. 获取权限信息
    // let roles;
    // try {
    //   roles = await uni.$uapi.get("/current/user/current_roles");
    // } catch (err) {
    //   throw new Error("获取权限信息失败:" + (err.message || "未知错误"));
    // }
    // // 5. 验证权限信息并设置
    // if (Array.isArray(roles) && roles.length > 0 && roles[0]?.roleKey) {
    //   userStore.setroleKey(roles[0].roleKey);
    // } else {
    //   // 设置默认角色或提示用户
    //   userStore.setroleKey("user"); // 假设"user"是默认角色
    // }
    // 6. 存储用户信息
    userStore.setUserInfo(userInfo);
    // 7. 跳转到目标页面
    const redirects = redirect.value || "/pages/index/index";
    // 判断是否为tabBar页面
    const tabBarPages = [
      "/pages/index/index",
      "/pages/appointment/index",
      "/pages/consultation/index",
      "/pages/my/index",
    ];
    if (tabBarPages.includes(redirects)) {
      console.log(redirects, "预路由1");
      uni.switchTab({ url: redirects });
    } else {
      console.log(redirects, "预路由2");
      uni.redirectTo({ url: redirects });
    }
    uni.hideLoading();
    await navigateToTarget();
  } catch (err) {
    uni.hideLoading();
    console.error("登录失败:", err);
    uni.showToast({
      title: err.message || "登录失败",
      icon: "none",
      duration: 3000,
    });
  }
};
const gotoRegister = () => {
  uni.navigateTo({
    url: "/pages/login/Register",
  });
/**
 * 根据 redirect 跳转
 */
const navigateToTarget = () => {
  if (redirect.value) {
    return navigateToTargetPage(redirect.value, {});
  } else {
    console.log("没有目标页面,跳转首页");
    uni.switchTab({ url: "/pages/index/index" });
    return Promise.resolve(true);
  }
};
const gotoForgetPassword = () => {
  uni.navigateTo({
    url: "/pages/login/ForgetPwd",
  });
};
/**
 * 通用跳转函数
 * @param {string} targetPage 目标页面路径
 * @param {object} pageParams 附加参数
 */
const navigateToTargetPage = (targetPage, pageParams = {}) => {
  return new Promise((resolve) => {
    if (!targetPage) {
      console.log("目标页为空,跳转首页");
      uni.switchTab({ url: "/pages/index/index" });
      resolve(true);
      return;
    }
const appleLogin = () => {
  uni.showToast({
    title: "暂未开通 Apple 登录",
    icon: "none",
  });
};
    let finalUrl = targetPage;
    const paramKeys = Object.keys(pageParams).filter(
      (key) => pageParams[key] !== undefined && pageParams[key] !== null,
    );
    if (paramKeys.length > 0) {
      const queryStr = paramKeys
        .map((key) => `${key}=${encodeURIComponent(pageParams[key])}`)
        .join("&");
      finalUrl += (finalUrl.includes("?") ? "&" : "?") + queryStr + "&sso=1";
    }
const harmonyLogin = () => {
  // 调用鸿蒙登录插件
  // #ifdef HARMONY
  const harmonyAuth = uni.requireNativePlugin("Harmony-Auth");
  harmonyAuth.login((result) => {
    console.log("鸿蒙登录结果:", result);
    console.log("最终跳转目标URL:", finalUrl);
    const baseUrl = finalUrl.split("?")[0];
    const isTabBar = tabBarPages.includes(baseUrl);
    setTimeout(() => {
      if (isTabBar) {
        uni.switchTab({
          url: baseUrl,
          success: () => resolve(true),
          fail: (err) => {
            console.error("switchTab失败:", err);
            uni.switchTab({ url: "/pages/index/index" });
            resolve(false);
          },
        });
      } else {
        uni.redirectTo({
          url: finalUrl,
          success: () => resolve(true),
          fail: (err) => {
            console.error("redirectTo失败:", err);
            uni.switchTab({ url: "/pages/index/index" });
            resolve(false);
          },
        });
      }
    }, 300);
  });
  // #endif
};
</script>
@@ -183,6 +303,20 @@
  background: linear-gradient(to bottom, #e6f7ff, #ffffff);
  height: 100vh;
  box-sizing: border-box;
  position: relative;
}
.sso-mask {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(255, 255, 255, 0.9);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
}
.header {
@@ -251,16 +385,5 @@
.button-hover {
  opacity: 0.8;
}
.footer-links {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  text-align: center;
  margin-top: 30rpx;
  color: #1890ff;
  font-size: 28rpx;
  width: 100%;
}
</style>