import { render, html } from 'lit-html' import { unsafeHTML } from 'lit-html/directives/unsafe-html.js' import { loginHandle, checkWechat, getQrCode, checkMpQrCodeLogin, redirectHome } from '../modules/login-handle.ts' import { setWxerwma } from '../modules/wx/ws-login.ts'; import { useCreateLoginQRCode } from '../modules/wx-mp/qr.ts'; import { eventEmitter } from '../modules/mitt.ts'; import { useContextKey } from '@kevisual/context' export const loginEmitter = useContextKey('login-emitter', eventEmitter); export const WX_MP_APP_ID = "wxff97d569b1db16b6"; interface LoginMethod { id: LoginMethods name: string icon: any appid?: string } const wxmpSvg = `` const wxOpenSvg = `` const phone = `` const pwd = `` const web = `` const icons: any = { pwd, web, phone, wxmpSvg, wxOpenSvg } const DefaultLoginMethods: LoginMethod[] = [ { id: 'password', name: '密码登录', icon: 'pwd' }, { id: 'web', name: '网页登录', icon: 'web' }, { id: 'wechat', name: '微信登录', icon: 'wxmpSvg', appid: "wx9378885c8390e09b" }, { id: 'wechat-mp', name: '微信公众号', icon: 'wxOpenSvg', appid: WX_MP_APP_ID }, { id: 'wechat-mp-ticket', name: '微信公众号', icon: 'wxOpenSvg' }, { id: 'phone', name: '手机号登录', icon: 'phone' } ] const LoginMethods = ['password', 'web', 'phone', 'wechat', 'wechat-mp', 'wechat-mp-ticket'] as const; type LoginMethods = 'password' | 'web' | 'phone' | 'wechat' | 'wechat-mp' | 'wechat-mp-ticket'; const getLoginMethodByDomain = (): LoginMethod[] => { let domain = window.location.host let methods: LoginMethods[] = [] const has51 = domain.includes('localhost') && (domain.endsWith('51515') || domain.endsWith('51015')); if (has51) { domain = 'localhost' } switch (domain) { case 'kevisual.xiongxiao.me': methods = ['password', 'wechat-mp'] break; case 'kevisual.cn': methods = ['password', 'wechat-mp-ticket', 'wechat',] break; case 'localhost': methods = ['password', 'web'] break default: methods = ['password', 'web', 'phone', 'wechat', 'wechat-mp', 'wechat-mp-ticket'] break; } return DefaultLoginMethods.filter(method => methods.includes(method.id)) } const getLoginMethod = (methods: LoginMethods[]): LoginMethod[] => { return DefaultLoginMethods.filter(method => methods.includes(method.id)) } class KvLogin extends HTMLElement { private selectedMethod: LoginMethods = 'password' private loginMethods: LoginMethod[] = getLoginMethodByDomain(); setLoginMethods(methods: LoginMethod[]) { this.loginMethods = methods this.render() } constructor() { super() } connectedCallback() { this.attachShadow({ mode: 'open' }) this.render() this.bindEvents() checkWechat() const method = this.getAttribute('method'); if (method) { const methods = method ? method.split(',') as LoginMethods[] : []; if (methods.length > 0) { const loginMethods = methods.filter(m => LoginMethods.includes(m)); if (loginMethods.length > 0) { this.loginMethods = getLoginMethod(loginMethods) this.selectedMethod = loginMethods[0] return; } } this.loginMethods = getLoginMethodByDomain(); this.selectedMethod = this.loginMethods[0].id; } } #clearTimer: any = null; private selectLoginMethod(methodId: LoginMethods) { this.selectedMethod = methodId this.render() if (this.#clearTimer) { this.#clearTimer(); this.#clearTimer = null; } } private getMethodData(methodId: LoginMethods): LoginMethod | undefined { return this.loginMethods.find(method => method.id === methodId); } private bindEvents() { if (!this.shadowRoot) return // 使用事件委托来处理登录方式切换 this.shadowRoot.addEventListener('click', (e) => { const target = e.target as HTMLElement const methodButton = target.closest('.login-method') if (methodButton) { const methodId = methodButton.getAttribute('data-method') as LoginMethods if (methodId) { this.selectLoginMethod(methodId) } } }) // 使用事件委托来处理表单提交 this.shadowRoot.addEventListener('submit', (e) => { const target = e.target as HTMLElement if (target && target.id === 'loginForm') { e.preventDefault() this.handleLogin() } }) loginEmitter.on('login-success', () => { console.log('收到登录成功事件,处理后续逻辑') }); } private handleLogin() { const formData = this.getFormData() // console.log('登录方式:', this.selectedMethod) // console.log('登录数据:', formData) loginHandle({ loginMethod: this.selectedMethod, data: formData, el: this }) // 这里可以触发自定义事件,通知父组件 this.dispatchEvent(new CustomEvent('login', { detail: { method: this.selectedMethod, data: formData }, bubbles: true })) } private getFormData(): any { if (!this.shadowRoot) return {} switch (this.selectedMethod) { case 'password': const username = this.shadowRoot.querySelector('#username') as HTMLInputElement const password = this.shadowRoot.querySelector('#password') as HTMLInputElement return { username: username?.value || '', password: password?.value || '' } case 'web': return {} case 'phone': const phone = this.shadowRoot.querySelector('#phone') as HTMLInputElement const code = this.shadowRoot.querySelector('#code') as HTMLInputElement return { phone: phone?.value || '', code: code?.value || '' } case 'wechat': return { wechatCode: 'mock_wechat_code' } case 'wechat-mp': return { wechatMpCode: 'mock_wechat_mp_code' } default: return {} } } private renderPasswordForm() { return html`
` } private renderWebForm() { return html`
` } private renderPhoneForm() { return html`
` } private renderWechatForm() { return html`
` } private renderWechatMpForm() { const that = this setTimeout(() => { const qrcode = that.shadowRoot!.querySelector('#qrcode'); const { clear } = useCreateLoginQRCode(qrcode as HTMLCanvasElement); that.#clearTimer = clear; }, 0) return html`

请使用微信扫描二维码登录

` } private renderWechatMpTicketForm() { const that = this; setTimeout(async () => { const data = await getQrCode(); if (!data) return; const imgEl = that.shadowRoot!.querySelector('.qrcode') as HTMLImageElement; if (data.url) { imgEl.src = data.url; // TODO: 轮询检测登录状态 const clear = checkMpQrCodeLogin(data.ticket) // 当切换登录方式时,停止轮询 that.#clearTimer = clear } }, 0) return html`

请使用微信扫描二维码登录

` } private sendVerificationCode() { console.log('发送验证码') // 这里可以实现发送验证码的逻辑 } private refreshQR() { console.log('刷新二维码') // 这里可以实现刷新二维码的逻辑 } private renderLoginForm() { const data = this.getMethodData(this.selectedMethod); switch (this.selectedMethod) { case 'password': return this.renderPasswordForm() case 'web': return this.renderWebForm() case 'phone': return this.renderPhoneForm() case 'wechat': setWxerwma({ appid: data?.appid! || "" }); return this.renderWechatForm() case 'wechat-mp': return this.renderWechatMpForm() case 'wechat-mp-ticket': return this.renderWechatMpTicketForm() default: return this.renderPasswordForm() } } render() { if (!this.shadowRoot) return const renderIcon = (icon: any) => { // 如果是emoji字符,直接返回 if (typeof icon === 'string' && !icons[icon]) { return html`${icon}` } // 如果是SVG引用,从icons对象获取 if (typeof icon === 'string' && icons[icon]) { return html`${unsafeHTML(icons[icon])}` } // 如果直接是SVG内容 if (typeof icon === 'string' && (icon.includes('${unsafeHTML(icon)}` } // 默认情况 return html`${icon}` } const template = html`
` render(template, this.shadowRoot) } } customElements.define('kv-login', KvLogin)