fsl-js-sdk
Version:
sdk for web
278 lines (246 loc) • 6.86 kB
text/typescript
// FSLContainer.ts
function copyText(text: string): Promise<string> {
if (navigator.clipboard) {
return navigator.clipboard
.writeText(text)
.then(() => {
return text;
})
.catch((error) => {
return error;
});
}
if (Reflect.has(document, 'execCommand')) {
return new Promise<string>((resolve, reject) => {
try {
const textArea = document.createElement('textarea');
textArea.value = text;
// 在手机 Safari 浏览器中,点击复制按钮,整个页面会跳动一下
textArea.style.width = '0';
textArea.style.position = 'fixed';
textArea.style.left = '-999px';
textArea.style.top = '10px';
textArea.setAttribute('readonly', 'readonly');
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
resolve(text);
} catch (error: any) {
reject(error);
}
});
}
return Promise.reject(
`"navigator.clipboard" 或 "document.execCommand" 中存在API错误, 拷贝失败!`,
);
}
const formatEmail = (email: string | undefined) => {
if (!email) {
return '';
}
const [name, company] = email.split('@');
let suffix = company;
if (company && company.length > 11) {
suffix = `***${company.slice(-11)}`;
}
if (name.length > 3) {
return `${name.slice(0, 2)}***${name.slice(-1)}@${suffix}`;
}
return email;
};
export class FSLContainer extends HTMLElement {
static get observedAttributes() {
return ['avatar', 'name'];
}
avatar: string = '/assets/avatar.png';
name: string = 'fsl@gmail.com';
isPC: boolean = true;
noCompress: boolean = true;
copy: ((text: string) => void) | null = null;
close: ((id: string) => void) | null = null;
shadow: ShadowRoot;
constructor() {
super();
document.body.style.margin = '0';
this.shadow = this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.updateState();
this.render();
window.addEventListener('resize', this.updateState.bind(this));
}
disconnectedCallback() {
window.removeEventListener('resize', this.updateState.bind(this));
}
attributeChangedCallback(
name: string,
oldVal: string | ((text?: string) => void) | null,
newVal: string | ((text?: string) => void) | null,
) {
console.log('newVal', newVal);
if (oldVal !== newVal) {
if (typeof newVal === 'string') {
this[name as 'avatar' | 'name'] = newVal;
} else {
this[name as 'copy' | 'close'] = newVal;
}
this.render();
}
}
updateState() {
const clientWidth = document.documentElement.clientWidth;
const clientHeight = document.documentElement.clientHeight;
this.isPC = clientWidth > 1024;
this.noCompress = clientHeight > 844;
this.render();
}
render() {
this.shadow.innerHTML = `
<style>
:host {
display: block;
height: 100vh;
min-width: 390px;
position: relative;
}
.appContainer {
background: url(/assets/background.png) center center/cover no-repeat;
height: 100vh;
}
.appContainer::after {
position: absolute;
content: '';
width: 100%;
height: 100%;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1;
}
.main {
position: relative;
z-index: 10;
height: 100%;
overflow: auto;
}
.centerContainer {
display: flex;
flex-direction: column;
justify-content: center;
min-height: 842px;
position: relative;
${this.noCompress ? 'top: 50%; transform: translateY(-50%);' : ''}
}
.phoneFrame {
width: 390px;
height: 844px;
${this.isPC ? 'border: 1px solid #ddd;' : ''}
border-radius: 30px;
overflow: hidden;
margin: 0 auto;
background: white;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32px 20px 0;
color: #0E0F0C;
background: #fff;
}
.header .left {
display: flex;
align-items: center;
}
.header .left img.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
}
.header .left span {
margin-left: 10px;
font-weight: 600;
}
.header .left img.copy {
margin-left: 10px;
width: 20px;
cursor: pointer;
}
.header .right {
display: flex;
align-items: center;
}
.header .right img.close {
width: 38px;
cursor: pointer;
}
.page {
flex: 1;
padding: 20px 16px;
overflow: auto;
background: #fff;
}
.scroll-container {
overflow: auto;
}
.container {
position: relative;
height: 100%;
display: flex;
flex-direction: column;
}
</style>
<div class="appContainer">
<div class="main">
${
this.isPC
? `
<div class="centerContainer">
<div class="phoneFrame">
${this.renderContent()}
</div>
</div>`
: `
<div class="phoneFrame" style="width: 100%; height: 100%; border-radius: 0;">
${this.renderContent()}
</div>`
}
</div>
</div>
`;
this.shadow.querySelector('.copy')?.addEventListener('click', () => {
copyText(this.name);
this.copy?.(this.name);
});
this.shadow.querySelector('.close')?.addEventListener('click', () => {
const uid = new URLSearchParams(location.search).get('uid');
if (this.close) {
this.close(uid!);
return;
}
location.href = `https://id.fsl.com/application?uid=${uid}`;
});
}
renderContent() {
const maskedName = formatEmail(this.name);
return `
<div class="container">
<div class="header">
<div class="left">
<img class="avatar" src="${this.avatar}" alt="avatar" />
<span>${maskedName}</span>
<img class="copy" src="/assets/copy.svg" alt="copy" />
</div>
<div class="right">
<slot name="expand"></slot>
<img class="close" src="/assets/close.svg" alt="close" />
</div>
</div>
<div class="page scroll-container">
<slot></slot>
</div>
</div>
`;
}
}