update
This commit is contained in:
@@ -74,7 +74,7 @@
|
||||
<div class="demo-container">
|
||||
<div class="login-section">
|
||||
<h2>登录组件</h2>
|
||||
<kv-login id="loginComponent">
|
||||
<kv-login>
|
||||
<div id="weixinLogin"></div>
|
||||
</kv-login>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kevisual/kv-login",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.6",
|
||||
"description": "",
|
||||
"main": "src/main.ts",
|
||||
"scripts": {
|
||||
@@ -8,24 +8,27 @@
|
||||
"build": "vite build --config vite-lib.config.ts",
|
||||
"build:test": "vite build",
|
||||
"prepub": "rm -rf ./dist && pnpm run build:test",
|
||||
"pub": "ev deploy ./dist -k kv-login-test -v 0.0.2 -u -y yes"
|
||||
"pub": "ev deploy ./dist -k kv-login-test -v 0.0.6 -u -y yes"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||
"license": "MIT",
|
||||
"packageManager": "pnpm@10.19.0",
|
||||
"packageManager": "pnpm@10.24.0",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@kevisual/query-login": "^0.0.6",
|
||||
"@kevisual/context": "^0.0.4",
|
||||
"@kevisual/query-login": "^0.0.7",
|
||||
"lit-html": "^3.3.1",
|
||||
"qrcode": "^1.5.4"
|
||||
},
|
||||
"exports": {
|
||||
".": "./dist/kv-login.es.js",
|
||||
"./kv-login.es.js": "./dist/kv-login.es.js",
|
||||
"./kv-login.umd.js": "./dist/kv-login.umd.js"
|
||||
}
|
||||
"./kv-login.umd.js": "./dist/kv-login.umd.js",
|
||||
"./types": "./types/index.d.ts"
|
||||
},
|
||||
"types": "./types/index.d.ts"
|
||||
}
|
||||
@@ -5,3 +5,8 @@
|
||||
黑白
|
||||
|
||||
|
||||
```html
|
||||
<kv-login>
|
||||
<div id="weixinLogin"></div>
|
||||
</kv-login>
|
||||
```
|
||||
@@ -1,2 +1,4 @@
|
||||
import './pages/kv-login'
|
||||
import './pages/kv-message'
|
||||
import './pages/kv-message'
|
||||
|
||||
export { loginEmitter } from './pages/kv-login'
|
||||
@@ -1,23 +1,25 @@
|
||||
import { query } from './query.ts';
|
||||
import { createMessage } from '../pages/kv-message.ts';
|
||||
import { WX_MP_APP_ID } from '../pages/kv-login.ts';
|
||||
import { emit } from './mitt.ts';
|
||||
export const message = createMessage();
|
||||
type LoginOpts = {
|
||||
loginMethod: 'password' | 'phone' | 'wechat' | 'wechat-mp' | 'wechat-mp-ticket',
|
||||
data: any,
|
||||
el: HTMLElement
|
||||
}
|
||||
/**
|
||||
* 登录成功后重定向到首页
|
||||
*/
|
||||
export const redirectHome = () => {
|
||||
console.log('重定向到首页')
|
||||
const href = window.location.href;
|
||||
const url = new URL(href);
|
||||
const redirect = url.searchParams.get('redirect');
|
||||
if (redirect) {
|
||||
const href = decodeURIComponent(redirect);
|
||||
window.open(href, '_self');
|
||||
} else {
|
||||
window.open('/root/home', '_self');
|
||||
}
|
||||
emit({ type: 'login-success', data: {} });
|
||||
}
|
||||
export const loginHandle = async (opts: LoginOpts) => {
|
||||
const { loginMethod, data, el } = opts
|
||||
|
||||
134
packages/kv-login/src/modules/mitt.ts
Normal file
134
packages/kv-login/src/modules/mitt.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
export interface EventData<T = any> {
|
||||
type: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export type EventHandler<T = any> = (event: EventData<T>) => void;
|
||||
|
||||
export class EventEmitter {
|
||||
private events: Map<string, Set<EventHandler>> = new Map();
|
||||
|
||||
/**
|
||||
* 监听事件
|
||||
* @param type 事件类型
|
||||
* @param handler 事件处理函数
|
||||
*/
|
||||
on<T = any>(type: string, handler: EventHandler<T>): void {
|
||||
if (!this.events.has(type)) {
|
||||
this.events.set(type, new Set());
|
||||
}
|
||||
this.events.get(type)!.add(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除事件监听器
|
||||
* @param type 事件类型
|
||||
* @param handler 事件处理函数 (可选,如果不提供则移除该类型的所有监听器)
|
||||
*/
|
||||
off<T = any>(type: string, handler?: EventHandler<T>): void {
|
||||
if (!this.events.has(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (handler) {
|
||||
this.events.get(type)!.delete(handler);
|
||||
// 如果该类型没有监听器了,删除该类型
|
||||
if (this.events.get(type)!.size === 0) {
|
||||
this.events.delete(type);
|
||||
}
|
||||
} else {
|
||||
// 移除该类型的所有监听器
|
||||
this.events.delete(type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件
|
||||
* @param event 事件对象,包含 type 和 data
|
||||
*/
|
||||
emit<T = any>(event: EventData<T>): void {
|
||||
const { type } = event;
|
||||
|
||||
if (!this.events.has(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handlers = this.events.get(type)!;
|
||||
handlers.forEach(handler => {
|
||||
try {
|
||||
handler(event);
|
||||
} catch (error) {
|
||||
console.error(`Error in event handler for type "${type}":`, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发事件(简化版本,直接传递type和data)
|
||||
* @param type 事件类型
|
||||
* @param data 事件数据
|
||||
*/
|
||||
emitSimple<T = any>(type: string, data: T): void {
|
||||
this.emit({ type, data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有事件监听器
|
||||
*/
|
||||
clear(): void {
|
||||
this.events.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定类型的监听器数量
|
||||
* @param type 事件类型
|
||||
* @returns 监听器数量
|
||||
*/
|
||||
listenerCount(type: string): number {
|
||||
return this.events.get(type)?.size || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有事件类型
|
||||
* @returns 事件类型数组
|
||||
*/
|
||||
eventNames(): string[] {
|
||||
return Array.from(this.events.keys());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有指定类型的监听器
|
||||
* @param type 事件类型
|
||||
* @returns 是否有监听器
|
||||
*/
|
||||
hasListeners(type: string): boolean {
|
||||
return this.events.has(type) && this.events.get(type)!.size > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 只监听一次事件
|
||||
* @param type 事件类型
|
||||
* @param handler 事件处理函数
|
||||
*/
|
||||
once<T = any>(type: string, handler: EventHandler<T>): void {
|
||||
const onceHandler: EventHandler<T> = (event) => {
|
||||
handler(event);
|
||||
this.off(type, onceHandler);
|
||||
};
|
||||
this.on(type, onceHandler);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建默认的事件发射器实例
|
||||
export const eventEmitter = new EventEmitter();
|
||||
|
||||
// 导出便捷方法
|
||||
export const on = <T = any>(type: string, handler: EventHandler<T>) => eventEmitter.on(type, handler);
|
||||
export const off = <T = any>(type: string, handler?: EventHandler<T>) => eventEmitter.off(type, handler);
|
||||
export const emit = <T = any>(event: EventData<T>) => eventEmitter.emit(event);
|
||||
export const emitSimple = <T = any>(type: string, data: T) => eventEmitter.emitSimple(type, data);
|
||||
export const clear = () => eventEmitter.clear();
|
||||
export const once = <T = any>(type: string, handler: EventHandler<T>) => eventEmitter.once(type, handler);
|
||||
|
||||
// 默认导出
|
||||
export default eventEmitter;
|
||||
@@ -3,6 +3,9 @@ import { unsafeHTML } from 'lit-html/directives/unsafe-html.js'
|
||||
import { loginHandle, checkWechat, getQrCode, checkMpQrCodeLogin } 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
|
||||
@@ -46,7 +49,6 @@ const getLoginMethodByDomain = (): LoginMethod[] => {
|
||||
}
|
||||
return DefaultLoginMethods.filter(method => methods.includes(method.id))
|
||||
}
|
||||
console.log('可用登录方式:', getLoginMethodByDomain().map(m => m.name).join(', '));
|
||||
class KvLogin extends HTMLElement {
|
||||
private selectedMethod: LoginMethods = 'password'
|
||||
|
||||
@@ -80,7 +82,6 @@ class KvLogin extends HTMLElement {
|
||||
}
|
||||
private bindEvents() {
|
||||
if (!this.shadowRoot) return
|
||||
|
||||
// 使用事件委托来处理登录方式切换
|
||||
this.shadowRoot.addEventListener('click', (e) => {
|
||||
const target = e.target as HTMLElement
|
||||
@@ -101,6 +102,9 @@ class KvLogin extends HTMLElement {
|
||||
this.handleLogin()
|
||||
}
|
||||
})
|
||||
loginEmitter.on('login-success', () => {
|
||||
console.log('收到登录成功事件,处理后续逻辑')
|
||||
});
|
||||
}
|
||||
|
||||
private handleLogin() {
|
||||
@@ -331,8 +335,8 @@ class KvLogin extends HTMLElement {
|
||||
|
||||
.login-methods {
|
||||
display: flex;
|
||||
background: #f8f9fa;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
background: #f5f5f5;
|
||||
border-bottom: 1px solid #000000;
|
||||
}
|
||||
|
||||
.login-method {
|
||||
@@ -350,7 +354,7 @@ class KvLogin extends HTMLElement {
|
||||
}
|
||||
|
||||
.login-method:hover {
|
||||
background: #e9ecef;
|
||||
background: #d0d0d0;
|
||||
}
|
||||
|
||||
.login-method.active {
|
||||
@@ -418,7 +422,7 @@ class KvLogin extends HTMLElement {
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #e9ecef;
|
||||
border: 2px solid #cccccc;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
transition: border-color 0.3s ease;
|
||||
@@ -481,7 +485,7 @@ class KvLogin extends HTMLElement {
|
||||
.qr-container {
|
||||
width: 340px;
|
||||
height: 340px;
|
||||
border: 2px dashed #cccccc;
|
||||
border: 2px solid #000000;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -490,7 +494,7 @@ class KvLogin extends HTMLElement {
|
||||
|
||||
.qr-placeholder {
|
||||
text-align: center;
|
||||
color: #6c757d;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.qr-icon {
|
||||
|
||||
127
packages/kv-login/types/index.d.ts
vendored
Normal file
127
packages/kv-login/types/index.d.ts
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
type LoginMethods = 'password' | 'phone' | 'wechat' | 'wechat-mp' | 'wechat-mp-ticket';
|
||||
|
||||
interface KvLoginEventMap {
|
||||
login: CustomEvent<{
|
||||
method: LoginMethods;
|
||||
data: LoginFormData[LoginMethods] | any;
|
||||
}>;
|
||||
/**
|
||||
* 登录方式切换事件
|
||||
*/
|
||||
methodChange: CustomEvent<{
|
||||
method: LoginMethods;
|
||||
previousMethod?: LoginMethods;
|
||||
}>;
|
||||
/**
|
||||
* 登录验证失败事件
|
||||
*/
|
||||
validationError: CustomEvent<{
|
||||
method: LoginMethods;
|
||||
errors: string[];
|
||||
formData: LoginFormData[LoginMethods] | any;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface KvLogin extends HTMLElement {
|
||||
/**
|
||||
* 设置登录方式
|
||||
*/
|
||||
setLoginMethods(methods: LoginMethod[]): void;
|
||||
/**
|
||||
* 添加自定义登录方式
|
||||
*/
|
||||
addLoginMethod(method: LoginMethod): void;
|
||||
/**
|
||||
* 移除登录方式
|
||||
*/
|
||||
removeLoginMethod(methodId: LoginMethods): void;
|
||||
/**
|
||||
* 获取当前选中的登录方式
|
||||
*/
|
||||
getSelectedMethod(): LoginMethods;
|
||||
/**
|
||||
* 设置默认登录方式
|
||||
*/
|
||||
setDefaultMethod(methodId: LoginMethods): void;
|
||||
|
||||
addEventListener<K extends keyof KvLoginEventMap>(
|
||||
type: K,
|
||||
listener: (this: KvLogin, ev: KvLoginEventMap[K]) => void,
|
||||
options?: boolean | AddEventListenerOptions
|
||||
): void;
|
||||
removeEventListener<K extends keyof KvLoginEventMap>(
|
||||
type: K,
|
||||
listener: (this: KvLogin, ev: KvLoginEventMap[K]) => void,
|
||||
options?: boolean | EventListenerOptions
|
||||
): void;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'kv-login': KvLogin;
|
||||
}
|
||||
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
'kv-login': KvLoginAttributes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface KvLoginAttributes extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> {
|
||||
/**
|
||||
* 自定义登录方式配置
|
||||
*/
|
||||
loginMethods?: LoginMethod[];
|
||||
/**
|
||||
* 自定义样式类名
|
||||
*/
|
||||
customClass?: string;
|
||||
/**
|
||||
* 是否显示登录方式选择器
|
||||
*/
|
||||
showMethodSelector?: boolean;
|
||||
/**
|
||||
* 默认选中的登录方式
|
||||
*/
|
||||
defaultMethod?: LoginMethods;
|
||||
}
|
||||
|
||||
interface LoginMethod {
|
||||
id: LoginMethods;
|
||||
name: string;
|
||||
icon: string | any; // 可以是emoji字符串、SVG字符串或其他图标类型
|
||||
appid?: string;
|
||||
disabled?: boolean;
|
||||
order?: number; // 用于排序
|
||||
}
|
||||
|
||||
interface LoginFormData {
|
||||
password?: {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
phone?: {
|
||||
phone: string;
|
||||
code: string;
|
||||
};
|
||||
wechat?: {
|
||||
wechatCode: string;
|
||||
};
|
||||
'wechat-mp'?: {
|
||||
wechatMpCode: string;
|
||||
};
|
||||
'wechat-mp-ticket'?: {
|
||||
wechatMpCode: string;
|
||||
ticket: string;
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
KvLogin,
|
||||
KvLoginEventMap,
|
||||
KvLoginAttributes,
|
||||
LoginMethods,
|
||||
LoginMethod,
|
||||
LoginFormData
|
||||
};
|
||||
Reference in New Issue
Block a user