feat(project): init

This commit is contained in:
2026-02-03 18:05:47 +08:00
commit f37a119eff
188 changed files with 29246 additions and 0 deletions

View File

@@ -0,0 +1,102 @@
import { useCallback, useEffect } from "react"
import { DigitalIcon } from "../resource/icons/digital"
import { useLoginStore } from "@/components/login/store"
import { useRouter } from "next/navigation"
import { APP, PLATFORM, TENANT } from "@/components/login/libs/common"
import { APP_ID, APP_SECRECT } from "./util"
import { fetchFeishuLogin } from "../api"
const HeaderIcon = DigitalIcon
const FeishuAutoLogin = ({ useUserStore }: { useUserStore: any }) => {
const fingerprint = useLoginStore.getState().fingerprint
const list = window.location.href.split("/")
const http_header =
window.location.protocol.toLowerCase() === "https:" ? "https" : "http"
const redirect_url =
http_header + "://" + list[2] + `${process.env.NEXT_PUBLIC_BASE_PATH}/login`
const { setUserInfo, refreshToken } = useUserStore()
const router = useRouter()
const autoLogin = useCallback(
(
fingerprint: any,
redirect_url: any,
router: any,
setUserInfo: (arg0: any, arg1: any, arg2: any) => void
) => {
if (!window.h5sdk || !window.tt) {
console.error("Feishu H5 SDK is not available")
setTimeout(() => {
autoLogin(fingerprint, redirect_url, router, setUserInfo)
}, 500)
return
}
// 确保每次进到这个页面是未登录,然后触发自动登录逻辑
window.h5sdk.ready(async () => {
console.log("Feishu H5 SDK is ready, requesting auth code")
window.tt.requestAuthCode({
appId: APP_ID,
success: async (res: any) => {
try {
const params = {
code: res.code,
redirect_uri: redirect_url,
tenant: TENANT,
platform: PLATFORM,
app: APP,
fingerprint,
app_id: APP_ID,
app_secret: APP_SECRECT,
}
const response = await fetchFeishuLogin(params)
const info = response.message
setUserInfo(info.tenant, info.user_info, info.rbac)
refreshToken({
access_token: info.access_token || "",
refresh_token: info.refresh_token || "",
})
window.location.href = "/"
} catch (err) {
console.log(err)
}
},
fail: (err: any) => {
console.error("Failed to get auth code from Feishu:", err)
alert(JSON.stringify(err))
},
})
})
},
[refreshToken]
)
const isFeishu = useCallback(() => !!(window.h5sdk && window.tt), [])
useEffect(() => {
console.log("Feishu auto login effect triggered")
autoLogin(fingerprint, redirect_url, router, setUserInfo)
}, [fingerprint, redirect_url, router, setUserInfo, autoLogin, isFeishu])
return (
<div
style={{
width: "100%",
height: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
gap: 20,
}}>
<HeaderIcon />
<span>...</span>
</div>
)
}
export default FeishuAutoLogin

View File

@@ -0,0 +1,93 @@
import { useCallback, useEffect, useState } from "react"
import { useSearchParams } from "next/navigation"
import { Flex } from "@mantine/core"
import { useLoginStore } from "@/components/login/store"
import { APP_ID, APP_SECRECT, qr_load, qr_login } from "./util"
import {
APP,
PLATFORM,
TENANT,
getQueryVariable,
} from "@/components/login/libs/common"
import { fetchFeishuLogin } from "../api"
const QrLogin = ({ useUserStore }: { useUserStore: any }) => {
const fingerprint = useLoginStore.getState().fingerprint
const list = window.location.href.split("/")
const http_header =
window.location.protocol.toLowerCase() === "https:" ? "https" : "http"
const redirect_url =
http_header + "://" + list[2] + `${process.env.NEXT_PUBLIC_BASE_PATH}/login`
const searchParams = useSearchParams()
const renderCode = useCallback(async () => {
await qr_load()
qr_login(
APP_ID,
redirect_url,
getQueryVariable("app_id") || searchParams.get("app_id") || ""
)
}, [redirect_url, searchParams])
const [times, setTimes] = useState(0)
const callbackUrl = useCallback(() => {
times !== 2 && setTimes(times + 1)
}, [times])
const { setUserInfo, refreshToken } = useUserStore()
const feishuLogin = useCallback(async () => {
try {
const params = {
code: getQueryVariable("code") || "",
redirect_uri: redirect_url,
tenant: TENANT,
platform: PLATFORM,
app: APP,
fingerprint,
app_id: APP_ID,
app_secret: APP_SECRECT,
}
const res = await fetchFeishuLogin(params)
const info = res.message
setUserInfo(info.tenant, info.user_info, info.rbac)
refreshToken({
access_token: info.access_token || "",
refresh_token: info.refresh_token || "",
})
window.location.href = "/"
} catch (err) {
console.log(err)
}
}, [fingerprint, redirect_url, refreshToken, setUserInfo])
useEffect(() => {
if (times === 1) {
renderCode()
if (getQueryVariable("code")) {
fingerprint && feishuLogin()
}
}
}, [feishuLogin, fingerprint, renderCode, times])
useEffect(() => {
callbackUrl()
}, [callbackUrl])
return (
<Flex align="center" justify="center" w={"100%"} h={"100%"}>
<div
id={"login_container"}
style={{
width: "200px",
height: "210px",
display: "flex",
justifyContent: "center",
scale: 0.7,
}}></div>
</Flex>
)
}
export default QrLogin

8
components/login/feishu/type.d.ts vendored Normal file
View File

@@ -0,0 +1,8 @@
interface Window {
tt: any
h5sdk: any
}
interface window {
[key: string]: any
}

View File

@@ -0,0 +1,53 @@
// 飞书脚本
export function qr_load() {
return new Promise<void>((resolve, reject) => {
const script = document.createElement("script")
script.type = "text/javascript"
script.src =
"https://sf3-cn.feishucdn.com/obj/feishu-static/lark/passport/qrcode/LarkSSOSDKWebQRCode-1.0.2.js"
script.onerror = reject
script.onload = () => {
resolve()
}
document.head.appendChild(script)
})
}
export async function qr_login(
appId: string,
redirect_url: string,
state: string
) {
const gotoUrl = `https://passport.feishu.cn/suite/passport/oauth/authorize?client_id=${appId}&redirect_uri=${encodeURIComponent(
redirect_url
)}&response_type=code&state=${encodeURIComponent(state)}`
const QRLogin = (<any>window).QRLogin
const QRLoginObj = QRLogin({
// eslint-disable-next-line prettier/prettier
id: "login_container",
goto: gotoUrl,
style: `width: 270px;height: 270px;border: 0;border-radius:12px;background-color: #E4F2FF;
background-image:
radial-gradient(at 47% 33%, hsl(212.37, 72%, 59%) 0, transparent 59%),
radial-gradient(at 82% 65%, hsl(198.00, 100%, 50%) 0, transparent 55%);`,
})
const handleMessage = function (event: any) {
const origin = event.origin
if (
QRLoginObj.matchOrigin(origin) &&
window.location.href.indexOf("login") > -1
) {
const loginTmpCode = event.data
window.location.href = `${gotoUrl}&tmp_code=${loginTmpCode}`
}
}
if (typeof window.addEventListener !== "undefined") {
window.addEventListener("message", handleMessage, false)
} else if (typeof (<any>window).attachEvent !== "undefined") {
// eslint-disable-next-line prettier/prettier
;(<any>window).attachEvent("onmessage", handleMessage)
}
}
export const APP_ID = "cli_a9a902d6c3b95ceb"
export const APP_SECRECT = "zVbsYlJa3nzK5DwabshHFgcWQdnp4HLu"