u
This commit is contained in:
1 parent
63b168affc
commit
069b4506e4
29 files changed
+1077
-1116
No files matched your search
@@ -1,232 +1,337 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>共享空间</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto;
|
||||
background: #f6f6f7;
|
||||
color: #111;
|
||||
}
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover" />
|
||||
<title>共享空间 — 邀请 / 加入</title>
|
||||
<style>
|
||||
:root{
|
||||
--bg: #f6f7fb;
|
||||
--card: #ffffff;
|
||||
--muted: #9aa4b2;
|
||||
--accent: #5b8cff;
|
||||
--danger: #ff6b6b;
|
||||
--radius: 12px;
|
||||
--gap: 12px;
|
||||
--shadow: 0 6px 18px rgba(22,27,40,0.06);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial;
|
||||
}
|
||||
|
||||
.page {
|
||||
max-width: 420px;
|
||||
margin: 0 auto;
|
||||
padding: 16px;
|
||||
background: #fff;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* Reset / basic */
|
||||
*{box-sizing:border-box}
|
||||
html,body{height:100%}
|
||||
body{
|
||||
margin:0;
|
||||
background:linear-gradient(180deg, var(--bg), #eef2ff 60%);
|
||||
color:#0f1724;
|
||||
-webkit-font-smoothing:antialiased;
|
||||
-moz-osx-font-smoothing:grayscale;
|
||||
padding:16px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
/* Container */
|
||||
.screen{
|
||||
max-width:480px;
|
||||
margin:0 auto;
|
||||
}
|
||||
|
||||
/* 按钮 */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 40px;
|
||||
padding: 0 14px;
|
||||
border-radius: 8px;
|
||||
background: #111;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin-top: 12px;
|
||||
border: 1px solid #111;
|
||||
}
|
||||
header.appbar{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:12px;
|
||||
padding:8px 4px;
|
||||
margin-bottom:12px;
|
||||
}
|
||||
.title{
|
||||
font-size:18px;
|
||||
font-weight:600;
|
||||
}
|
||||
.subtitle{
|
||||
display:block;
|
||||
font-size:12px;
|
||||
color:var(--muted);
|
||||
margin-top:2px;
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background: transparent;
|
||||
color: #111;
|
||||
}
|
||||
/* Cards */
|
||||
.card{
|
||||
background:var(--card);
|
||||
border-radius:var(--radius);
|
||||
box-shadow:var(--shadow);
|
||||
padding:14px;
|
||||
margin-bottom:var(--gap);
|
||||
}
|
||||
|
||||
.input-box {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #ddd;
|
||||
margin-top: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
/* Invite area */
|
||||
.invite-top{
|
||||
display:flex;
|
||||
gap:12px;
|
||||
align-items:center;
|
||||
}
|
||||
.space-avatar{
|
||||
width:56px;height:56px;border-radius:12px;background:linear-gradient(135deg,#7fb6ff,#5b8cff);
|
||||
display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;font-size:20px;
|
||||
}
|
||||
.space-info{
|
||||
flex:1;
|
||||
}
|
||||
.space-name{font-weight:600;font-size:16px}
|
||||
.space-meta{font-size:12px;color:var(--muted);margin-top:4px}
|
||||
|
||||
.list-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 14px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.invite-row{
|
||||
margin-top:12px;display:flex;gap:8px;
|
||||
}
|
||||
.btn{
|
||||
flex:1;
|
||||
border:0;padding:10px 12px;border-radius:10px;font-weight:600;
|
||||
background:var(--accent);color:#fff;cursor:pointer;
|
||||
box-shadow:0 6px 12px rgba(91,140,255,0.18);
|
||||
}
|
||||
.btn.ghost{background:transparent;color:var(--accent);border:1px solid rgba(91,140,255,0.12);box-shadow:none}
|
||||
.btn.danger{background:var(--danger);box-shadow:0 6px 12px rgba(255,107,107,0.16)}
|
||||
|
||||
.switcher {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
/* Invite code box */
|
||||
.code-box{display:flex;align-items:center;justify-content:space-between;padding:10px;border-radius:10px;background:#f4f7ff;border:1px dashed rgba(91,140,255,0.18)}
|
||||
.code-text{font-weight:700;letter-spacing:2px;font-size:16px}
|
||||
.code-actions{display:flex;gap:8px}
|
||||
|
||||
.switcher button {
|
||||
flex: 1;
|
||||
height: 36px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
border: 1px solid #ccc;
|
||||
background: #fafafa;
|
||||
}
|
||||
/* Join section */
|
||||
.join-input{
|
||||
display:flex;gap:8px;margin-top:8px;
|
||||
}
|
||||
.input{
|
||||
flex:1;padding:10px;border-radius:10px;border:1px solid #e6e9ef;background:#fff;
|
||||
font-size:14px;
|
||||
}
|
||||
.help-line{font-size:12px;color:var(--muted);margin-top:8px}
|
||||
|
||||
.switcher button.active {
|
||||
background: #111;
|
||||
color: #fff;
|
||||
border-color: #111;
|
||||
}
|
||||
/* Recent invites / list */
|
||||
.list-row{display:flex;align-items:center;justify-content:space-between;padding:10px 0;border-top:1px solid #f2f4f8}
|
||||
.list-left{display:flex;align-items:center;gap:10px}
|
||||
.small-avatar{width:36px;height:36px;border-radius:8px;background:#eef4ff;display:flex;align-items:center;justify-content:center;color:#3759c8;font-weight:700}
|
||||
.list-title{font-weight:600}
|
||||
.list-sub{font-size:12px;color:var(--muted)}
|
||||
|
||||
/* 弹窗 */
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
/* Modal (simple) */
|
||||
.overlay{position:fixed;left:0;right:0;top:0;bottom:0;background:rgba(8,10,15,0.45);display:flex;align-items:center;justify-content:center;padding:20px;z-index:80}
|
||||
.modal{width:100%;max-width:420px;border-radius:14px;background:var(--card);padding:16px;box-shadow:var(--shadow)}
|
||||
.modal .h1{font-weight:700;margin-bottom:8px}
|
||||
.modal .muted{color:var(--muted);font-size:13px;margin-bottom:12px}
|
||||
.share-grid{display:flex;gap:8px;flex-wrap:wrap}
|
||||
|
||||
.modal {
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
text-align: center;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 0;
|
||||
}
|
||||
.share-chip{flex:0 0 30%;min-width:84px;padding:10px;border-radius:10px;background:#f7f9ff;text-align:center;font-size:13px;color:#123;background:linear-gradient(180deg,#fff,#f7f9ff);cursor:pointer}
|
||||
.copy-toast{position:fixed;left:50%;transform:translateX(-50%);bottom:22px;padding:8px 14px;background:rgba(0,0,0,0.7);color:#fff;border-radius:20px;font-size:13px;z-index:90}
|
||||
|
||||
.qr-box {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
background: #eee;
|
||||
margin: 16px auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 10px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
/* Footer hint */
|
||||
.hint{font-size:12px;color:var(--muted);text-align:center;margin-top:12px}
|
||||
|
||||
/* small screens */
|
||||
@media (max-width:360px){
|
||||
.btn{padding:10px}
|
||||
.space-avatar{width:48px;height:48px;font-size:18px}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="screen">
|
||||
<header class="appbar">
|
||||
<div style="width:44px;"></div>
|
||||
<div>
|
||||
<div class="title">共享空间</div>
|
||||
<div class="subtitle">邀请别人加入你的空间,或输入邀请码加入别人空间</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="page">
|
||||
<h1>共享空间(预览)</h1>
|
||||
<!-- Invite card -->
|
||||
<section class="card">
|
||||
<div class="invite-top">
|
||||
<div class="space-avatar" id="spaceAvatar">A</div>
|
||||
<div class="space-info">
|
||||
<div class="space-name" id="spaceName">工作协作空间</div>
|
||||
<div class="space-meta">成员 12 • 权限 管理者</div>
|
||||
</div>
|
||||
<div style="width:68px;text-align:right"><button class="btn ghost" id="manageBtn">管理</button></div>
|
||||
</div>
|
||||
|
||||
<div class="switcher">
|
||||
<button id="btnNotJoined" class="active" onclick="showPage('not')">未加入</button>
|
||||
<button id="btnJoined" onclick="showPage('yes')">已加入</button>
|
||||
<div style="margin-top:14px">
|
||||
<div class="code-box" id="codeBox">
|
||||
<div class="code-text" id="inviteCode">AB12-CD34</div>
|
||||
<div class="code-actions">
|
||||
<button class="btn ghost" id="copyBtn">复制</button>
|
||||
<button class="btn" id="shareBtn">分享</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="invite-row">
|
||||
<button class="btn ghost" id="qrBtn">二维码</button>
|
||||
<button class="btn ghost" id="linkBtn">链接</button>
|
||||
<button class="btn danger" id="revokeBtn">撤销并重置</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hint">邀请码有效期:24小时 · 点击分享可选择渠道</div>
|
||||
</section>
|
||||
|
||||
<!-- Join other -->
|
||||
<section class="card" aria-label="加入他人空间">
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;">
|
||||
<div style="font-weight:700">加入别人的空间</div>
|
||||
<div class="list-sub">通过邀请码或扫码加入</div>
|
||||
</div>
|
||||
|
||||
<div class="join-input">
|
||||
<input class="input" id="joinInput" placeholder="输入邀请码或链接(例如:AB12-CD34)" />
|
||||
<button class="btn" id="joinBtn">加入</button>
|
||||
</div>
|
||||
<div class="help-line">若对方使用链接邀请,可直接粘贴链接;扫码请使用 App 内相机。</div>
|
||||
|
||||
<div style="margin-top:12px">
|
||||
<div style="font-weight:600;margin-bottom:8px">最近加入的空间</div>
|
||||
<div class="list-row">
|
||||
<div class="list-left">
|
||||
<div class="small-avatar">P</div>
|
||||
<div>
|
||||
<div class="list-title">产品设计空间</div>
|
||||
<div class="list-sub">邀请人:小李 · 2 天前</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align:right;">
|
||||
<div class="list-sub">成员 8</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-row">
|
||||
<div class="list-left">
|
||||
<div class="small-avatar">M</div>
|
||||
<div>
|
||||
<div class="list-title">市场推广</div>
|
||||
<div class="list-sub">邀请人:小王 · 1 周前</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align:right;">
|
||||
<div class="list-sub">成员 20</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- footer actions -->
|
||||
<div style="display:flex;gap:8px;margin-top:8px">
|
||||
<button class="btn ghost" style="flex:1" id="createSpace">创建新空间</button>
|
||||
<button class="btn" style="flex:1" id="explore">发现空间</button>
|
||||
</div>
|
||||
|
||||
<!-- 未加入 -->
|
||||
<div id="notJoined">
|
||||
<p>你尚未加入共享空间,你可以创建自己的空间或加入他人的。</p>
|
||||
<div style="height:40px"></div>
|
||||
</div>
|
||||
|
||||
<div class="btn" onclick="openInviteModal()">生成邀请码</div>
|
||||
<div class="btn-outline btn" onclick="openScanModal()">扫码加入</div>
|
||||
<!-- Modal markup (hidden initially) -->
|
||||
<div id="modalOverlay" class="overlay" style="display:none;">
|
||||
<div class="modal" role="dialog" aria-modal="true">
|
||||
<div class="h1">分享邀请</div>
|
||||
<div class="muted">选择一种方式邀请你的成员</div>
|
||||
|
||||
<div class="btn btn-outline" onclick="toggleJoinInput()">输入邀请码加入</div>
|
||||
<div class="share-grid" style="margin-bottom:12px;">
|
||||
<div class="share-chip" data-action="wechat">微信</div>
|
||||
<div class="share-chip" data-action="qq">QQ</div>
|
||||
<div class="share-chip" data-action="link">复制链接</div>
|
||||
<div class="share-chip" data-action="sms">短信</div>
|
||||
<div class="share-chip" data-action="email">邮件</div>
|
||||
<div class="share-chip" data-action="more">更多</div>
|
||||
</div>
|
||||
|
||||
<input id="inviteInput" type="text" class="input-box" placeholder="邀请码" style="display:none;" />
|
||||
<div id="joinBtn" class="btn" style="display:none;">加入</div>
|
||||
</div>
|
||||
|
||||
<!-- 已加入 -->
|
||||
<div id="joined" style="display:none;">
|
||||
<p>你已加入共享空间,可查看成员、权限和邀请他人加入。</p>
|
||||
|
||||
<h3 style="margin-top:20px; font-size:16px; font-weight:600;">成员列表</h3>
|
||||
|
||||
<div class="list-item"><span>张三</span><span>管理员</span></div>
|
||||
<div class="list-item"><span>李四</span><span>成员</span></div>
|
||||
<div class="list-item"><span>王五</span><span>成员</span></div>
|
||||
|
||||
<div class="btn" onclick="openInviteModal()">邀请新成员</div>
|
||||
<div style="display:flex;gap:8px;justify-content:flex-end">
|
||||
<button class="btn ghost" onclick="closeModal()">取消</button>
|
||||
<button class="btn" id="modalShare">分享</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 邀请二维码弹窗 -->
|
||||
<div id="inviteModal" class="modal-mask">
|
||||
<div class="modal">
|
||||
<h3>邀请码 / 二维码</h3>
|
||||
<div id="qrBox" class="qr-box">二维码生成中…</div>
|
||||
<div class="btn" onclick="closeInviteModal()">关闭</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 扫码加入弹窗(假装相机界面) -->
|
||||
<div id="scanModal" class="modal-mask">
|
||||
<div class="modal">
|
||||
<h3>扫码加入</h3>
|
||||
<div class="qr-box">[ 相机画面模拟区域 ]</div>
|
||||
<p style="font-size:12px;color:#666;">打开摄像头并扫描共享空间二维码</p>
|
||||
<div class="btn" onclick="closeScanModal()">关闭</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- toast -->
|
||||
<div id="copyToast" class="copy-toast" style="display:none;">已复制到剪贴板</div>
|
||||
|
||||
<script>
|
||||
function showPage(type) {
|
||||
const not = document.getElementById("notJoined");
|
||||
const yes = document.getElementById("joined");
|
||||
const btnNot = document.getElementById("btnNotJoined");
|
||||
const btnYes = document.getElementById("btnJoined");
|
||||
// demo interactions
|
||||
const inviteCodeEl = document.getElementById('inviteCode');
|
||||
const copyBtn = document.getElementById('copyBtn');
|
||||
const shareBtn = document.getElementById('shareBtn');
|
||||
const modal = document.getElementById('modalOverlay');
|
||||
const copyToast = document.getElementById('copyToast');
|
||||
const joinInput = document.getElementById('joinInput');
|
||||
const joinBtn = document.getElementById('joinBtn');
|
||||
|
||||
if (type === 'not') {
|
||||
not.style.display = 'block';
|
||||
yes.style.display = 'none';
|
||||
btnNot.classList.add('active');
|
||||
btnYes.classList.remove('active');
|
||||
} else {
|
||||
not.style.display = 'none';
|
||||
yes.style.display = 'block';
|
||||
btnYes.classList.add('active');
|
||||
btnNot.classList.remove('active');
|
||||
// copy code
|
||||
copyBtn.addEventListener('click', async () => {
|
||||
const code = inviteCodeEl.textContent.trim();
|
||||
try {
|
||||
await navigator.clipboard.writeText(code);
|
||||
showToast('已复制邀请码');
|
||||
} catch (e) {
|
||||
showToast('复制失败,请手动复制');
|
||||
}
|
||||
});
|
||||
|
||||
// open share modal
|
||||
shareBtn.addEventListener('click', () => openModal());
|
||||
|
||||
document.getElementById('qrBtn').addEventListener('click', ()=> {
|
||||
alert('打开二维码生成器(示例占位)');
|
||||
});
|
||||
document.getElementById('linkBtn').addEventListener('click', ()=> {
|
||||
alert('打开链接分享(示例占位)');
|
||||
});
|
||||
document.getElementById('revokeBtn').addEventListener('click', ()=> {
|
||||
const newCode = randomCode();
|
||||
inviteCodeEl.textContent = newCode;
|
||||
showToast('邀请码已重置');
|
||||
});
|
||||
|
||||
// join
|
||||
joinBtn.addEventListener('click', ()=> {
|
||||
const v = (joinInput.value || '').trim();
|
||||
if (!v) return showToast('请输入邀请码或链接');
|
||||
// 简单验证格式示例
|
||||
const matched = /^[A-Z0-9]{2,}-[A-Z0-9]{2,}$/.test(v) || v.includes('http');
|
||||
if (!matched) return showToast('邀请码格式不正确');
|
||||
showToast('加入请求已发送');
|
||||
// TODO: 调用后端 API 加入空间
|
||||
});
|
||||
|
||||
// modal actions
|
||||
function openModal(){ modal.style.display='flex'; }
|
||||
function closeModal(){ modal.style.display='none'; }
|
||||
|
||||
document.getElementById('modalShare').addEventListener('click', ()=>{
|
||||
// 示例:复制链接并关闭
|
||||
const fakeLink = location.href + '?invite=' + inviteCodeEl.textContent.trim();
|
||||
navigator.clipboard.writeText(fakeLink).catch(()=>{});
|
||||
showToast('邀请链接已复制');
|
||||
closeModal();
|
||||
});
|
||||
|
||||
// copy chips
|
||||
document.querySelectorAll('.share-chip').forEach(el=>{
|
||||
el.addEventListener('click', ()=> {
|
||||
const action = el.getAttribute('data-action');
|
||||
if (action === 'link') {
|
||||
const fakeLink = location.href + '?invite=' + inviteCodeEl.textContent.trim();
|
||||
navigator.clipboard.writeText(fakeLink).then(()=> showToast('链接已复制'));
|
||||
} else {
|
||||
showToast('已选择:' + action);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// small utilities
|
||||
function showToast(text){
|
||||
copyToast.textContent = text;
|
||||
copyToast.style.display = 'block';
|
||||
setTimeout(()=> copyToast.style.display='none', 1500);
|
||||
}
|
||||
|
||||
// ----------------- 邀请码 / 二维码弹窗 -----------------
|
||||
function openInviteModal() {
|
||||
const modal = document.getElementById('inviteModal');
|
||||
const qr = document.getElementById('qrBox');
|
||||
const code = Math.random().toString(36).substring(2, 8).toUpperCase();
|
||||
qr.innerHTML = `二维码:${code}`;
|
||||
modal.style.display = 'flex';
|
||||
}
|
||||
function closeInviteModal() {
|
||||
document.getElementById('inviteModal').style.display = 'none';
|
||||
}
|
||||
|
||||
// ----------------- 扫码加入弹窗 -----------------
|
||||
function openScanModal() {
|
||||
document.getElementById('scanModal').style.display = 'flex';
|
||||
}
|
||||
function closeScanModal() {
|
||||
document.getElementById('scanModal').style.display = 'none';
|
||||
}
|
||||
|
||||
function toggleJoinInput() {
|
||||
const input = document.getElementById("inviteInput");
|
||||
const btn = document.getElementById("joinBtn");
|
||||
const show = input.style.display === 'none';
|
||||
input.style.display = show ? 'block' : 'none';
|
||||
btn.style.display = show ? 'block' : 'none';
|
||||
function randomCode(){
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
const pick = (n)=> Array.from({length:n}).map(()=>chars[Math.floor(Math.random()*chars.length)]).join('');
|
||||
return pick(2)+'-'+pick(4);
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user