用户协议默认不勾选

This commit is contained in:
2026-07-15 19:50:09 +08:00
parent 5a2e8dfcf8
commit 68aa7d3ce2
34 changed files with 821 additions and 18 deletions
+3
View File
@@ -1,6 +1,7 @@
import { Routes, Route } from 'react-router-dom';
import AuthGate from './components/AuthGate';
import LoginPage from './pages/LoginPage';
import LegalPage from './pages/LegalPage';
import PartnerAppRoutes from './PartnerAppRoutes';
export default function App() {
@@ -8,6 +9,8 @@ export default function App() {
<AuthGate>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/legal/user-agreement" element={<LegalPage docId="user-agreement" />} />
<Route path="/legal/privacy-policy" element={<LegalPage docId="privacy-policy" />} />
<Route path="/*" element={<PartnerAppRoutes />} />
</Routes>
</AuthGate>
+1 -1
View File
@@ -3,7 +3,7 @@ import { getPartnerProfile, hasPartnerWxSession } from '../lib/api';
import { usePartnerSession } from '../contexts/PartnerSessionContext';
import { partnerHomePath } from '../lib/partnerAccess';
const PUBLIC_PATHS = new Set(['/login']);
const PUBLIC_PATHS = new Set(['/login', '/legal/user-agreement', '/legal/privacy-policy']);
export default function AuthGate({ children }: { children: React.ReactNode }) {
const { ready, authenticated, account } = usePartnerSession();
+1
View File
@@ -6,6 +6,7 @@ import App from './App';
import { PartnerSessionProvider } from './contexts/PartnerSessionContext';
import { PartnerToastProvider } from './contexts/PartnerToastContext';
import './styles.css';
import './styles/legal.css';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
+36
View File
@@ -0,0 +1,36 @@
import { Link } from 'react-router-dom';
import { getLegalDocument, type LegalDocument } from '@dukang/shared-types';
type LegalPageProps = {
docId: LegalDocument['id'];
/** 返回登录页的路径,如 /login */
backTo?: string;
};
/** H5 各端共用的协议/隐私正文页 */
export default function LegalPage({ docId, backTo = '/login' }: LegalPageProps) {
const doc = getLegalDocument(docId);
return (
<div className="legal-h5-page">
<header className="legal-h5-header">
<Link to={backTo} className="legal-h5-back" aria-label="返回">
</Link>
<h1 className="legal-h5-title">{doc.title}</h1>
</header>
<main className="legal-h5-body">
<p className="legal-h5-updated">{doc.updatedAt}</p>
<p className="legal-h5-intro">{doc.intro}</p>
{doc.sections.map((section) => (
<section key={section.heading} className="legal-h5-section">
<h2>{section.heading}</h2>
{section.paragraphs.map((p, i) => (
<p key={`${section.heading}-${i}`}>{p}</p>
))}
</section>
))}
</main>
</div>
);
}
+9 -2
View File
@@ -68,7 +68,7 @@ export default function LoginPage() {
const [phone, setPhone] = useState(remembered.phone || getLastPhone());
const [code, setCode] = useState('');
const [rememberAccount, setRememberAccount] = useState(remembered.remember);
const [agreed, setAgreed] = useState(true);
const [agreed, setAgreed] = useState(false);
const [loading, setLoading] = useState(false);
const [wxLoading, setWxLoading] = useState(false);
const [msg, setMsg] = useState('');
@@ -343,7 +343,14 @@ export default function LoginPage() {
<label className="partner-checkbox-row">
<input type="checkbox" checked={agreed} onChange={(e) => setAgreed(e.target.checked)} />
<span>
<span className="text-primary" style={{ fontWeight: 600 }}></span> <span className="text-primary" style={{ fontWeight: 600 }}></span>
{' '}
<Link to="/legal/user-agreement" className="text-primary" style={{ fontWeight: 600 }} onClick={(e) => e.stopPropagation()}>
</Link>{' '}
{' '}
<Link to="/legal/privacy-policy" className="text-primary" style={{ fontWeight: 600 }} onClick={(e) => e.stopPropagation()}>
</Link>
</span>
</label>
</div>
+75
View File
@@ -0,0 +1,75 @@
.legal-h5-page {
min-height: 100vh;
background: #faf9f7;
color: #1f1a17;
}
.legal-h5-header {
position: sticky;
top: 0;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
height: 52px;
padding: 0 16px;
background: #faf9f7;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
.legal-h5-back {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
line-height: 1;
color: #1f1a17;
text-decoration: none;
}
.legal-h5-title {
margin: 0;
font-size: 17px;
font-weight: 600;
}
.legal-h5-body {
padding: 16px 20px 40px;
max-width: 720px;
margin: 0 auto;
}
.legal-h5-updated {
margin: 0 0 12px;
font-size: 12px;
color: #8d706e;
}
.legal-h5-intro {
margin: 0 0 20px;
font-size: 14px;
line-height: 1.7;
}
.legal-h5-section {
margin-bottom: 18px;
}
.legal-h5-section h2 {
margin: 0 0 8px;
font-size: 15px;
font-weight: 600;
}
.legal-h5-section p {
margin: 0 0 8px;
font-size: 13px;
line-height: 1.7;
color: #5c504c;
}