feat(mini): 全局沉浸式导航统一改造

1. 大厅改为资讯页固定模式:flex+ScrollView 独立滚动,两级tab位于滚动区之外完全固定不被下拉;tab样式统一为药丸渐变
2. 资讯页tab上间距优化(4rpx→18rpx)
3. 所有页面标题区统一三段式:英文分类标签(no) + 中文标题(title) + 中文副标题(sub),移除所有英文注脚en
4. 所有二级页面(pages-extra)加 back 返回按钮
5. 补齐14个缺失页面的 navigationStyle:custom 和 PageHeader:
   cert-apply/content-detail/event-detail/my-cert/my-park/my-tasks/
   park-apply/park-transfer/pay/pay-qr/plan/scan-login/scan/tasks
6. 大厅PageHeader副标题改为中文描述
This commit is contained in:
Pine
2026-09-01 22:14:19 +08:00
parent b56476c69e
commit b4d4ac4258
56 changed files with 128 additions and 111 deletions
@@ -1 +1 @@
export default { navigationBarTitleText: 'OPC 认证' };
export default { navigationStyle: 'custom' };
@@ -6,6 +6,7 @@ import { submitOpCert, getOpCertMine } from '@/utils/profile';
import { uploadParkDoc } from '@/utils/parks';
import './index.scss';
import { PageHeader } from '@/components/TopInset';
const EMPTY = () => ({ real_name: '', gender: '', address: '', industry: '', ability: '', phone: '' });
const GENDER_OPTS = ['男', '女'];
@@ -90,7 +91,7 @@ export default function CertApply() {
if (done) {
return (
<View className="ca page-in">
<View className="ca page-in"><PageHeader back no="CERT" title="OPC 认证" sub="申请 OPC 人才认证" />
<View className="ok-icon" />
<Text className="ok-title">认证申请已提交</Text>
<Text className="ok-desc">平台将对您的资料进行审核请留意认证状态</Text>
@@ -20,7 +20,7 @@ export default function CommunityPostDetail() {
}, [id]);
useEffect(load, [load]);
if (!data) return <View className="hl-pad page-in"><PageHeader no="COMM" title="帖子" en="POST" back /><Skeleton /></View>;
if (!data) return <View className="hl-pad page-in"><PageHeader back no="COMM" title="帖子" sub="社区帖子详情"/><Skeleton /></View>;
const { post, comments } = data;
const like = async () => {
if (!isAuthed()) { Taro.showToast({ title: '请先登录', icon: 'none' }); return; }
@@ -42,7 +42,7 @@ export default function CommunityPostDetail() {
};
return (
<View className="hl-pad page-in">
<PageHeader no="COMM" title={post.topic || '帖子'} en="POST" back />
<PageHeader back no="COMM" title={post.topic || '帖子'} sub="社区帖子详情"/>
<View className="hl-post">
<View className="hl-post-head">
<View className="hl-avatar" style={{ width: 72, height: 72, fontSize: 30 }}><Text>{(post.authorName || '·').slice(0, 1)}</Text></View>
@@ -24,7 +24,7 @@ export default function CommunityPost() {
};
return (
<View className="hl-pad page-in">
<PageHeader no="COMM" title="发帖" en="POST" />
<PageHeader back no="COMM" title="发帖" sub="分享你的想法" />
<View className="hl-detail">
<View className="hl-chips" style={{ padding: 0 }}>
{TOPICS.map((t) => (
@@ -32,7 +32,7 @@ export default function Community() {
return (
<View className="hl-pad page-in">
<PageHeader no="COMM" title="社区" en="COMMUNITY" />
<PageHeader back no="COMM" title="社区" sub="交流 · 求助 · 资源 · 经验" />
<View className="hl-chips">
{TOPICS.map((t) => (
<Text key={t} className={`hl-chip${topic === t ? ' on' : ''}`} onClick={() => setTopic(t)}>{t}</Text>
@@ -136,7 +136,7 @@ export default function ComputeCenter() {
return (
<View className="page-in">
<PageHeader no="04" title="算力管理" en="COMPUTE" back />
<PageHeader back no="04" title="算力管理" sub="GPU 算力资源调度"/>
{!authed && <View className="card"><Text className="muted">请先登录我的 登录绑定</Text></View>}
@@ -1 +1 @@
export default { navigationBarTitleText: '资讯详情' };
export default { navigationStyle: 'custom' };
@@ -6,6 +6,7 @@ import { isAuthed } from '@/utils/auth';
import './index.scss';
import coverWide from '@/assets/huodong-wide.png';
import { PageHeader } from '@/components/TopInset';
const rw = (s) => (s || '').replace('T', ' ').replace('Z', '').slice(0, 16);
/* rich-text 内部节点不吃页面 CSS:渲染前把图片/视频宽度约束进正文(不超出页面) */
@@ -129,7 +130,7 @@ const pubBadge = badgeOf(item);
};
return (
<View className="cd page-in">
<View className="cd page-in"><PageHeader back no="NEWS" title="资讯详情" sub="政策 · 资讯 · 技能 · 动态" />
{/* 封面/视频(小图模式不显示封面) */}
{!isSmall && (item.video
? <Video className="cd-video" src={item.video} poster={item.video_cover || item.cover || ''} controls />
@@ -1 +1 @@
export default { navigationBarTitleText: '活动详情' };
export default { navigationStyle: 'custom' };
@@ -20,6 +20,7 @@ import coverWide from '@/assets/huodong-wide.png';
import Skeleton from '@/components/skeleton';
import './index.scss';
import { PageHeader } from '@/components/TopInset';
/* 活动报名状态(5 态) */
const STATUS_META = {
open: { text: '报名中', color: '#3fb68b' },
@@ -197,7 +198,7 @@ export default function EventDetail() {
};
if (loading) return (
<View className="ed page-in">
<View className="ed page-in"><PageHeader back no="EVENT" title="活动详情" sub="线下 · 线上 公益课与沙龙" />
<Skeleton.Box h={300} />
<View style={{ marginTop: 20 }}><Skeleton.Rows n={3} gap={14} /></View>
</View>
@@ -115,7 +115,7 @@ export default function EventEdit() {
return (
<View className="page-in">
<PageHeader no="EV" title={editId ? '编辑活动' : '发布活动'} en="EVENT" back />
<PageHeader back no="EV" title={editId ? '编辑活动' : '发布活动'} sub="编辑活动信息"/>
<View className="adm-wrap">
<View className="card adm-form">
<View className="adm-field">
@@ -22,8 +22,8 @@ export default function JobDetail() {
if (!id) { setErr('缺少参数'); return; }
hallJobDetail(id).then(setJob).catch((e) => setErr(e.message));
}, []);
if (err) return <View className="hl-pad page-in"><PageHeader no="JOBS" title="岗位详情" en="JOB" back /><Text className="hl-err">{err}</Text></View>;
if (!job) return <View className="hl-pad page-in"><PageHeader no="JOBS" title="岗位详情" en="JOB" back /><Skeleton /></View>;
if (err) return <View className="hl-pad page-in"><PageHeader back no="JOBS" title="岗位详情" sub="招聘岗位详情"/><Text className="hl-err">{err}</Text></View>;
if (!job) return <View className="hl-pad page-in"><PageHeader back no="JOBS" title="岗位详情" sub="招聘岗位详情"/><Skeleton /></View>;
const apply = async () => {
if (!isAuthed()) { Taro.showToast({ title: '请先登录', icon: 'none' }); return; }
@@ -37,7 +37,7 @@ export default function JobDetail() {
return (
<View className="hl-pad page-in">
<PageHeader no="JOBS" title={job.title} en="JOB" back />
<PageHeader back no="JOBS" title={job.title} sub="招聘岗位详情"/>
<View className="hl-detail">
<View className="hl-tags">
<Text className="hl-tag">{job.category || '综合'}</Text>
@@ -24,7 +24,7 @@ export default function HallJobPublish() {
};
return (
<View className="hl-pad page-in">
<PageHeader no="JOBS" title="发布岗位" en="POST" />
<PageHeader back no="JOBS" title="发布岗位" sub="发布招聘信息" />
<View className="hl-detail">
<Input className="hl-input" placeholder="岗位名称" value={f.title} onInput={set('title')} />
<Input className="hl-input" placeholder="企业名称" value={f.companyName} onInput={set('companyName')} />
@@ -19,7 +19,7 @@ export default function HallJobs() {
}, [wt]);
return (
<View className="hl-pad page-in">
<PageHeader no="JOBS" title="岗位大厅" en="JOBS" />
<PageHeader back no="JOBS" title="岗位大厅" sub="全职 · 兼职 · 实习 · 远程" />
<View className="hl-chips">
{TYPES.map((t) => (
<Text key={t.k} className={`hl-chip${wt === t.k ? ' on' : ''}`} onClick={() => setWt(t.k)}>{t.l}</Text>
@@ -21,8 +21,8 @@ export default function ServiceDetail() {
if (!id) { setErr('缺少参数'); return; }
hallServiceDetail(id).then(setS).catch((e) => setErr(e.message));
}, []);
if (err) return <View className="hl-pad page-in"><PageHeader no="SVC" title="服务详情" en="SERVICE" back /><Text className="hl-err">{err}</Text></View>;
if (!s) return <View className="hl-pad page-in"><PageHeader no="SVC" title="服务详情" en="SERVICE" back /><Skeleton /></View>;
if (err) return <View className="hl-pad page-in"><PageHeader back no="SVC" title="服务详情" sub="服务商品详情"/><Text className="hl-err">{err}</Text></View>;
if (!s) return <View className="hl-pad page-in"><PageHeader back no="SVC" title="服务详情" sub="服务商品详情"/><Skeleton /></View>;
const inquire = async () => {
if (!isAuthed()) { Taro.showToast({ title: '请先登录', icon: 'none' }); return; }
@@ -36,7 +36,7 @@ export default function ServiceDetail() {
return (
<View className="hl-pad page-in">
<PageHeader no="SVC" title={s.title} en="SERVICE" back />
<PageHeader back no="SVC" title={s.title} sub="服务商品详情"/>
<View className="hl-detail">
<View className="hl-tags">
<Text className="hl-tag">{s.category || '综合'}</Text>
@@ -22,7 +22,7 @@ export default function HallServicePublish() {
};
return (
<View className="hl-pad page-in">
<PageHeader no="SVC" title="发布服务" en="POST" />
<PageHeader back no="SVC" title="发布服务" sub="上架你的专业服务" />
<View className="hl-detail">
<Input className="hl-input" placeholder="服务名称" value={f.title} onInput={set('title')} />
<Input className="hl-input" placeholder="分类(限服务类,如:软件开发)" value={f.category} onInput={set('category')} />
@@ -14,7 +14,7 @@ export default function HallServices() {
}, []);
return (
<View className="hl-pad page-in">
<PageHeader no="SVC" title="服务大厅" en="SERVICES" />
<PageHeader back no="SVC" title="服务大厅" sub="专业服务交易市场" />
{err && <Text className="hl-err">{err}</Text>}
{!items && !err && <Skeleton />}
{items && items.length === 0 && <Text className="hl-empty">暂无服务</Text>}
@@ -15,11 +15,11 @@ export default function TalentDetail() {
if (!id) { setErr('缺少参数'); return; }
hallTalentDetail(id).then(setT).catch((e) => setErr(e.message));
}, []);
if (err) return <View className="hl-pad page-in"><PageHeader no="TALENT" title="人才详情" en="TALENT" back /><Text className="hl-err">{err}</Text></View>;
if (!t) return <View className="hl-pad page-in"><PageHeader no="TALENT" title="人才详情" en="TALENT" back /><Skeleton /></View>;
if (err) return <View className="hl-pad page-in"><PageHeader back no="TALENT" title="人才详情" sub="人才档案详情"/><Text className="hl-err">{err}</Text></View>;
if (!t) return <View className="hl-pad page-in"><PageHeader back no="TALENT" title="人才详情" sub="人才档案详情"/><Skeleton /></View>;
return (
<View className="hl-pad page-in">
<PageHeader no="TALENT" title={t.displayName} en={t.talentType === 'seeker' ? 'SEEKER' : 'OPC'} back />
<PageHeader back no="TALENT" title={t.displayName} sub="人才档案详情"/>
<View className="hl-detail">
<View className="hl-talent">
<View className="hl-avatar"><Text>{(t.displayName || '·').slice(0, 1)}</Text></View>
@@ -17,7 +17,7 @@ export default function HallTalents() {
}, [type]);
return (
<View className="hl-pad page-in">
<PageHeader no="TALENT" title="人才大厅" en="TALENTS" />
<PageHeader back no="TALENT" title="人才大厅" sub="OPC 认证人才 · 求职者" />
<View className="hl-chips">
{TYPES.map((t) => (
<Text key={t.k} className={`hl-chip${type === t.k ? ' on' : ''}`} onClick={() => setType(t.k)}>{t.l}</Text>
@@ -29,7 +29,7 @@ export default function HallTaskPublish() {
};
return (
<View className="hl-pad page-in">
<PageHeader no="HALL" title="发布任务" en="POST" />
<PageHeader back no="HALL" title="发布任务" sub="发布需求找对的人" />
<View className="hl-detail">
<Text className="hl-sec" style={{ marginTop: 0 }}>接单模式</Text>
<View className="hl-chips" style={{ padding: 0 }}>
@@ -171,7 +171,7 @@ export default function MyBookings() {
return (
<View className="page-in">
<PageHeader no="BK" title="我的报名" en="BOOKINGS" back />
<PageHeader back no="BK" title="我的报名" sub="活动报名记录"/>
{!isAuthed() && <View className="card"><Text className="muted">请先登录我的 登录绑定</Text></View>}
@@ -1 +1 @@
export default { navigationBarTitleText: '认证中心' };
export default { navigationStyle: 'custom' };
@@ -5,6 +5,7 @@ import { isAuthed } from '@/utils/auth';
import { getOpCertMine } from '@/utils/profile';
import './index.scss';
import { PageHeader } from '@/components/TopInset';
const CERT = {
uncertified: ['未认证', 'muted'],
pending: ['待提交', 'pending'],
@@ -35,7 +36,7 @@ export default function MyCert() {
const goApply = () => Taro.navigateTo({ url: '/pages-extra/cert-apply/index' });
return (
<View className="page-in">
<View className="page-in"><PageHeader back no="MY" title="认证中心" sub="我的认证证书" />
<View className="mk-head">
<Text className="mk-title">认证中心</Text>
<Text className="mk-sub">你的 OPC 创业伙伴认证进度</Text>
@@ -32,7 +32,7 @@ export default function MyEvents() {
return (
<View className="page-in">
<PageHeader no="MY" title="我的活动" en="MY EVENTS" back />
<PageHeader back no="MY" title="我的活动" sub="已报名的活动"/>
<View className="adm-wrap">
<Button className="btn-ghost adm-new" onClick={() => Taro.navigateTo({ url: '/pages-extra/event-edit/index' })}>+ 发布新活动</Button>
{err ? <View className="card"><Text className="adm-note">{err}</Text></View> : null}
@@ -1 +1 @@
export default { navigationBarTitleText: '我的园区' };
export default { navigationStyle: 'custom' };
@@ -5,6 +5,7 @@ import { isAuthed } from '@/utils/auth';
import { getMyAdmission } from '@/utils/parks';
import './index.scss';
import { PageHeader } from '@/components/TopInset';
function statusText(s) {
const k = String(s || '').toLowerCase();
if (['approved', 'verified', 'admitted'].includes(k) || s === '已入驻' || s === '通过') return ['已入驻', 'ok'];
@@ -31,7 +32,7 @@ export default function MyPark() {
const ad = items[0] || {};
return (
<View className="page-in">
<View className="page-in"><PageHeader back no="PARK" title="我的园区" sub="创业载体管理" />
<View className="mk-head">
<Text className="mk-title">我的园区</Text>
<Text className="mk-sub">查看你的园区入驻申请与所属园区</Text>
@@ -1 +1 @@
export default { navigationBarTitleText: '我的任务' };
export default { navigationStyle: 'custom' };
@@ -5,6 +5,7 @@ import { isAuthed } from '@/utils/auth';
import { getMyTasks } from '@/utils/tasks';
import './index.scss';
import { PageHeader } from '@/components/TopInset';
const MODE = { grab: '抢单', bid: '竞标', assign: '指派', recommend: '推荐' };
const money = (a, b) => (a || b) ? (a && b && a !== b ? `¥${a}${b}` : `¥${a || b}`) : '面议';
const itemsOf = (r) => (r && r.items) || (r && r.list) || (Array.isArray(r) ? r : []);
@@ -28,7 +29,7 @@ export default function MyTasks() {
const goSquare = () => Taro.navigateTo({ url: '/pages-extra/tasks/index' });
return (
<View className="page-in">
<View className="page-in"><PageHeader back no="TASK" title="我的任务" sub="我发布和承接的任务" />
<View className="mk-head">
<Text className="mk-title">我的任务</Text>
<Text className="mk-sub">你承接 / 参与的任务点击查看详情</Text>
@@ -1 +1 @@
export default { navigationBarTitleText: '园区入驻申请' };
export default { navigationStyle: 'custom' };
@@ -8,6 +8,7 @@ import {
} from '@/utils/parks';
import './index.scss';
import { PageHeader } from '@/components/TopInset';
const EMPTY = () => ({
name: '', birth: '', gender: '', grad_school_major: '', ethnicity: '',
grad_time: '', id_card: '', contact_phone: '',
@@ -167,7 +168,7 @@ export default function ParkApply() {
if (done) {
return (
<View className="pa page-in">
<View className="pa page-in"><PageHeader back no="PARK" title="园区入驻申请" sub="申请入驻创业载体" />
<View className="ok-icon" />
<Text className="ok-title">申请已提交</Text>
<Text className="ok-desc">园区将在收到材料后尽快审核</Text>
@@ -36,7 +36,7 @@ export default function ParkDetail() {
if (err) {
return (
<View className="pkdt page-in">
<PageHeader no="PARK" title="园区详情" en="PARK" />
<PageHeader back no="PARK" title="园区详情" sub="创业载体详情" />
<Text className="pkdt-err">{err}</Text>
</View>
);
@@ -44,7 +44,7 @@ export default function ParkDetail() {
if (!park) {
return (
<View className="pkdt page-in">
<PageHeader no="PARK" title="园区详情" en="PARK" />
<PageHeader back no="PARK" title="园区详情" sub="创业载体详情" />
<Skeleton />
</View>
);
@@ -74,7 +74,7 @@ export default function ParkDetail() {
return (
<View className="pkdt page-in">
<PageHeader no="PARK" title={park.name} en={park.city || 'PARK'} />
<PageHeader back no="PARK" title={park.name} sub="创业载体详情" />
<Image className="pkdt-cover" src={park.cover || coverWide} mode="aspectFill" />
@@ -1 +1 @@
export default { navigationBarTitleText: '转园申请' };
export default { navigationStyle: 'custom' };
@@ -6,6 +6,7 @@ import { submitParkTransfer, getParkTransferMine } from '@/utils/profile';
import { getParks } from '@/utils/parks';
import './index.scss';
import { PageHeader } from '@/components/TopInset';
/** 转园申请(C端):园区 OPC 申请转入目标园区 → 园区/平台审核。 */
export default function ParkTransfer() {
const [authed, setAuthed] = useState(isAuthed());
@@ -67,7 +68,7 @@ export default function ParkTransfer() {
if (done) {
return (
<View className="pt page-in">
<View className="pt page-in"><PageHeader back no="PARK" title="转园申请" sub="申请转移到其他载体" />
<View className="ok-icon" />
<Text className="ok-title">转园申请已提交</Text>
<Text className="ok-desc">园区与平台将对您的转园申请进行审核</Text>
+1 -1
View File
@@ -30,7 +30,7 @@ export default function Parks() {
return (
<View className="pks page-in">
<PageHeader no="PARK" title="创业载体" en="CARRIERS" />
<PageHeader back no="PARK" title="创业载体" sub="园区 · 孵化器 · 众创空间" />
<View className="pks-tabs">
{[{ k: 'all', label: '全部' }, ...regions.map((r) => ({ k: r, label: shortCity(r) }))].map((t) => (
+2 -1
View File
@@ -6,6 +6,7 @@ import { payBooking, bookingPayStatus, requestWxPay, priceText, fmtEvt } from '@
import { isAuthed, wxLogin, phoneLogin, sendCode } from '@/utils/auth';
import './index.scss';
import { PageHeader } from '@/components/TopInset';
/**
* Web 扫码支付页:微信扫「支付小程序码」直达本页(scene=报名单 id)。
* 展示报名摘要 → 微信支付(JSAPI)→ 成功后回「我的报名」。
@@ -72,7 +73,7 @@ export default function PayQr() {
if (paid) {
return (
<View className="page-in pq-page">
<View className="page-in pq-page"><PageHeader back no="PAY" title="扫码支付" sub="微信扫码支付" />
<View className="card pq-done">
<Text className="pq-done-title">支付成功</Text>
<Text className="pq-done-sub">报名已确认感谢参与</Text>
@@ -1 +1 @@
export default { navigationBarTitleText: '确认支付' };
export default { navigationStyle: 'custom' };
+2 -1
View File
@@ -5,6 +5,7 @@ import { request } from '@/utils/api';
import { isAuthed, wxLogin, phoneLogin, sendCode } from '@/utils/auth';
import './index.scss';
import { PageHeader } from '@/components/TopInset';
const MICRO = 1000000; // 1 元 = 1,000,000 微元
const MARKET_PREFIX = 'MK_'; // 市场订单前缀(技能/插件/应用购买)
@@ -148,7 +149,7 @@ export default function PayConfirm() {
const endedDesc = isMarket ? '该订单已完成或已过期,请在桌面端重新发起购买。' : '该订单已完成充值或已过期,请在桌面端重新发起充值。';
return (
<View className="pay">
<View className="pay"><PageHeader back no="PAY" title="确认支付" sub="订单支付确认" />
{/* 顶栏返回 */}
<View className="pay-top">
<View className="pay-back" onClick={cancel}><View className="ic-arrow-left" /></View>
@@ -1 +1 @@
export default { navigationBarTitleText: '创业启动流程' };
export default { navigationStyle: 'custom' };
+2 -1
View File
@@ -7,6 +7,7 @@ import { useTabIndex } from '@/utils/tab';
import Skeleton from '@/components/skeleton';
import './index.scss';
import { PageHeader } from '@/components/TopInset';
export default function Plan() {
useTabIndex(4);
const [region, setRegion] = useState('昆明');
@@ -43,7 +44,7 @@ export default function Plan() {
/* 配置未到:先渲染骨架,避免 SP_STATUS[-1] 崩溃/空白 */
if (!loaded) {
return (
<View className="plan page-in">
<View className="plan page-in"><PageHeader back no="PLAN" title="创业启动流程" sub="从零到一的创业路径" />
<Text className="muted">正在加载配置</Text>
<View style={{ marginTop: 20 }}><Skeleton.Rows n={4} gap={18} /></View>
</View>
+3 -3
View File
@@ -77,7 +77,7 @@ export default function Policy() {
if (!loaded) {
return (
<View className="policy page-in">
<PageHeader no="00" title="政策适配测评" en="Policy" back />
<PageHeader back no="00" title="政策适配测评" sub="测一测你适合哪些政策"/>
<Text className="muted">正在加载测评</Text>
<View style={{ marginTop: 20 }}><Skeleton.Rows n={4} gap={18} /></View>
</View>
@@ -87,7 +87,7 @@ export default function Policy() {
if (result) {
return (
<View className="policy page-in">
<PageHeader no="02" title="适配结果" en="Result" back />
<PageHeader back no="02" title="适配结果" sub="测一测你适合哪些政策"/>
<View className="summary">{result.summary}</View>
<Text className="section-head">适配政策</Text>
@@ -144,7 +144,7 @@ export default function Policy() {
return (
<View className="policy quiz-center">
<PageHeader no="01" title="政策适配测评" en="Policy" back />
<PageHeader back no="01" title="政策适配测评" sub="测一测你适合哪些政策"/>
<View className="ot-quiz-head">
<View className="ot-quiz-top">
<Text className="ot-progress-text">{current + 1} / {QUESTIONS.length} </Text>
@@ -1 +1 @@
export default { navigationBarTitleText: '扫码登录' };
export default { navigationStyle: 'custom' };
@@ -4,6 +4,7 @@ import { useState, useEffect } from 'react';
import { isAuthed, wxLogin, phoneLogin, sendCode, mpQrConfirm } from '@/utils/auth';
import './index.scss';
import { PageHeader } from '@/components/TopInset';
/**
* 小程序扫码登录确认页:由「小程序码(wxacode)」自动打开,或「我的→扫码登录」扫码进入。
* 规则:若小程序当前未登录 → 必须**先登录**(一键微信登录 / 手机号验证码)才能进行扫码确认;
@@ -80,7 +81,7 @@ export default function ScanLogin() {
const goBack = cancel;
return (
<View className="sl">
<View className="sl"><PageHeader back no="SCAN" title="扫码登录" sub="扫码登录桌面端" />
{/* 顶栏返回 */}
<View className="sl-top">
<View className="sl-back" onClick={goBack}><View className="ic-arrow-left" /></View>
@@ -1 +1 @@
export default { navigationBarTitleText: '扫码接单' };
export default { navigationStyle: 'custom' };
+2 -1
View File
@@ -6,6 +6,7 @@ import { claimByCode, getMyTasks, parseTaskCode } from '@/utils/tasks';
import Skeleton from '@/components/skeleton';
import './index.scss';
import { PageHeader } from '@/components/TopInset';
/* 扫码接单:扫描大屏任务卡二维码 → 解析 task_code → 领单(OPC 身份)→ 展示我的接单 */
export default function Scan() {
@@ -62,7 +63,7 @@ export default function Scan() {
};
return (
<View className="scan page-in">
<View className="scan page-in"><PageHeader back no="SCAN" title="扫码接单" sub="扫描二维码承接任务" />
<View className="scan-head">
<Text className="scan-title">扫码接单</Text>
<Text className="scan-sub">扫描园区大屏上的任务二维码即可领取任务</Text>
+3 -3
View File
@@ -114,7 +114,7 @@ export default function Survey() {
if (stage === 'welcome') {
return (
<View className="sv page-in">
<PageHeader no="00" title="OPC 创业伙伴调研" en="Survey" back />
<PageHeader no="00" title="OPC 创业伙伴调研" sub="帮助我们更好地服务你" back />
<Text className="sv-intro">
聊聊你在云南做的行业遇到的难题AI 赋能与政策诉求 {loaded ? SURVEY_QUESTIONS.length : '…'} 58 分钟匿名提交用于云南 OPC 创业生态建设与政策研究
</Text>
@@ -138,7 +138,7 @@ export default function Survey() {
if (stage === 'done') {
return (
<View className="sv page-in">
<PageHeader no="06" title="提交成功,感谢参与!" en="Thank you" back />
<PageHeader no="06" title="提交成功,感谢参与!" sub="帮助我们更好地服务你" back />
<View className="sv-thanks">
<Text>你的回答已匿名记录将用于云南 OPC 创业生态建设与政策研究我们正在组织 OPC 伙伴社群与调研回访欢迎继续参与</Text>
</View>
@@ -158,7 +158,7 @@ export default function Survey() {
return (
<View className="sv">
<PageHeader no="01" title="创业伙伴调研" en="Quiz" back={() => { setStage('welcome'); scrollTop(); }} />
<PageHeader no="01" title="创业伙伴调研" sub="帮助我们更好地服务你" back={() => { setStage('welcome'); scrollTop(); }} />
<View className="sv-quiz-head">
<View className="sv-quiz-top">
<Text className="sv-progress-text">{current + 1} / {SURVEY_QUESTIONS.length}</Text>
@@ -26,7 +26,7 @@ export default function TalentEdit() {
};
return (
<View className="hl-pad page-in">
<PageHeader no="TALENT" title="人才档案" en="PROFILE" />
<PageHeader back no="TALENT" title="人才档案" sub="完善你的人才档案" />
<View className="hl-detail">
<View className="hl-chips" style={{ padding: 0 }}>
{[{ k: 'opc', l: '我是 OPC 超级个体' }, { k: 'seeker', l: '我是求职者' }].map((t) => (
@@ -41,15 +41,15 @@ export default function TaskDetail() {
finally { setBusy(false); }
};
if (!loaded) return <View className="tkd page-in"><PageHeader no="TASK" title="任务详情" en="DETAIL" back /><Text className="tkd-empty">加载中</Text></View>;
if (!task) return <View className="tkd page-in"><PageHeader no="TASK" title="任务详情" en="DETAIL" back /><Text className="tkd-empty">任务不存在或已下线</Text></View>;
if (!loaded) return <View className="tkd page-in"><PageHeader back no="TASK" title="任务详情" sub="任务详情与抢单"/><Text className="tkd-empty">加载中</Text></View>;
if (!task) return <View className="tkd page-in"><PageHeader back no="TASK" title="任务详情" sub="任务详情与抢单"/><Text className="tkd-empty">任务不存在或已下线</Text></View>;
const can = task.can_accept;
const reasons = Array.isArray(task.accept_reasons) ? task.accept_reasons : [];
return (
<View className="tkd page-in">
<PageHeader no="TASK" title={task.title} en={MODE[task.mode] || 'TASK'} back />
<PageHeader back no="TASK" title={task.title} sub="任务详情与抢单"/>
<View className="tkd-hero">
<View className="tkd-row">
<Text className="tk-cat">{task.category || '综合'}</Text>
@@ -1,6 +1 @@
export default definePageConfig({
navigationBarTitleText: '任务广场',
navigationBarBackgroundColor: '#f7f9ff',
navigationBarTextStyle: 'black',
backgroundColor: '#f7f9ff'
});
export default definePageConfig({ navigationStyle: 'custom' });
+2 -1
View File
@@ -5,6 +5,7 @@ import { isAuthed } from '@/utils/auth';
import { listSquare, grabById } from '@/utils/tasks';
import './index.scss';
import { PageHeader } from '@/components/TopInset';
const MODE = { grab: '抢单', bid: '竞标', register: '报名', assign: '指派', recommend: '推荐' };
const money = (a, b) => (a || b) ? (a && b && a !== b ? `¥${a}${b}` : `¥${a || b}`) : '面议';
@@ -39,7 +40,7 @@ export default function Tasks() {
};
return (
<View className="tk page-in">
<View className="tk page-in"><PageHeader back no="TASK" title="任务广场" sub="全部任务列表" />
<View className="tk-head">
<Text className="tk-title">任务广场</Text>
<Text className="tk-sub">浏览平台发布的待接单任务点击查看详情登录后可抢单</Text>
+3 -3
View File
@@ -103,7 +103,7 @@ export default function Test() {
if (stage === 'welcome') {
return (
<View className="ot page-in">
<PageHeader no="00" title="开始测评" en="Start" />
<PageHeader no="00" title="开始测评" sub="测评入口" />
<Text className="ot-intro">
一个人也可以是一家公司510 分钟看清你的 OPC 赛道角色与 AI 协作方式
</Text>
@@ -143,7 +143,7 @@ export default function Test() {
return (
<View className="ot quiz-center page-in">
<PageHeader no="01" title="OPC 创业基因测评" sub={version === 'quick' ? '快速版' : '完整版'} en="Assessment" back={backToWelcome} />
<PageHeader no="01" title="OPC 创业基因测评" sub={version === 'quick' ? '快速版' : '完整版'} sub="测评入口" back={backToWelcome} />
<View className="ot-quiz-head">
<View className="ot-quiz-top">
<Text className="ot-progress-text">{current + 1} / {questions.length} </Text>
@@ -189,7 +189,7 @@ export default function Test() {
return (
<View className="ot page-in">
{/* 头部:类型码 + 人设 + 适配指数(复用统一标题区) */}
<PageHeader no="02" title="你的 OPC 画像" en="Report" back={backToWelcome} />
<PageHeader no="02" title="你的 OPC 画像" sub="测评入口" back={backToWelcome} />
<View className="ot-result-hero">
<View className="ot-hero-left">
<Text className="ot-code">{r.typeCode}</Text>
+1 -1
View File
@@ -103,7 +103,7 @@ export default function Booking() {
return (
<View className="events page-in">
<PageHeader no="EVENTS" title="活动" en="线下 · 线上 公益课与沙龙" />
<PageHeader no="EVENTS" title="活动" sub="线下 · 线上 公益课与沙龙" />
{/* 近期活动 · 首屏横幅(始终渲染,数据到位后刷新) */}
<View className="mk-section">
+1 -1
View File
@@ -110,7 +110,7 @@ export default function ContentIndex() {
<View className="ct page-in">
{/* 固定页头:标题在状态栏左侧,不随滚动 */}
{/* 固定标题区 */}
<PageHeader no="NEWS" title="资讯中心" en="INFORMATION" />
<PageHeader no="NEWS" title="资讯中心" sub="政策 · 资讯 · 技能 · 动态" />
{/* 类别 tab(含全部):紧贴标题区下方,位于滚动区之外 */}
<View className="ct-cats ct-cats-bar">
+1 -1
View File
@@ -28,7 +28,7 @@
/* 类别 tab 条:紧贴标题区下方,位于滚动区之外,固定不随内容滚动 */
.ct-cats-bar {
flex-wrap: nowrap; justify-content: flex-start; gap: 12rpx;
padding: 4rpx 0 12rpx;
padding: 18rpx 0 16rpx;
background: transparent;
}
.ct-cats-bar .ct-cat { flex: 0 0 auto; padding: 10rpx 30rpx; }
+15 -19
View File
@@ -1,8 +1,8 @@
import { View, Text, Button } from '@tarojs/components';
import { View, Text, Button, ScrollView } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { useState, useEffect, useCallback } from 'react';
import { hallOverview, hallTasks, hallTalents, hallJobs, hallServices } from '@/utils/hall';
import { PageHeader, getTopInset } from '@/components/TopInset';
import { PageHeader } from '@/components/TopInset';
import Skeleton from '@/components/skeleton';
import { isAuthed } from '@/utils/auth';
import '@/styles/hall.scss';
@@ -51,17 +51,9 @@ export default function Hall() {
const [ttype, setTtype] = useState(''); // 人才筛选
const [wt, setWt] = useState(''); // 岗位筛选
const [topic, setTopic] = useState(''); // 社区筛选
const [navBarH, setNavBarH] = useState(0); // PageHeader 实测高度(吸顶基准)
const { navH } = getTopInset();
useEffect(() => {
hallOverview().then(setStat).catch(() => {});
Taro.nextTick(() => {
Taro.createSelectorQuery()
.select('.page-navbar')
.boundingClientRect((r) => { if (r && r.height) setNavBarH(r.height); })
.exec();
});
}, []);
const goPublish = () => {
@@ -77,10 +69,10 @@ export default function Hall() {
const chipSet = tab === 'tasks' ? setMode : tab === 'talents' ? setTtype : tab === 'jobs' ? setWt : setTopic;
return (
<View className="hl-pad page-in">
<PageHeader no="HALL" title="大厅" en="HUB" />
{/* 两级 Tab 统一吸顶容器:一级 TABS + 二级筛选 chips,共享背景与宽度,无缝隙 */}
<View className="hl-tabbar" style={{ top: `${navBarH || navH}px` }}>
<View className="hl page-in">
<PageHeader no="HALL" title="大厅" sub="任务 · 人才 · 岗位 · 服务 · 社区" />
{/* 两级 Tab:位于滚动区之外,完全固定不被下拉 */}
<View className="hl-tabbar">
<View className="hl-tabs">
{TABS.map((t) => (
<Text key={t.k} className={`hl-tab${tab === t.k ? ' on' : ''}`} onClick={() => setTab(t.k)}>{t.l}</Text>
@@ -98,11 +90,15 @@ export default function Hall() {
</View>
)}
</View>
{tab === 'tasks' && <TasksPane mode={mode} onMode={setMode} />}
{tab === 'talents' && <TalentsPane ttype={ttype} onTtype={setTtype} />}
{tab === 'jobs' && <JobsPane wt={wt} onWt={setWt} />}
{tab === 'services' && <ServicesPane />}
{tab === 'community' && <CommunityPane topic={topic} onTopic={setTopic} />}
{/* 内容区独立滚动 */}
<ScrollView className="hl-scroll" scrollY enhanced showScrollbar={false}>
{tab === 'tasks' && <TasksPane mode={mode} onMode={setMode} />}
{tab === 'talents' && <TalentsPane ttype={ttype} onTtype={setTtype} />}
{tab === 'jobs' && <JobsPane wt={wt} onWt={setWt} />}
{tab === 'services' && <ServicesPane />}
{tab === 'community' && <CommunityPane topic={topic} onTopic={setTopic} />}
<View className="hl-scroll-pad" />
</ScrollView>
</View>
);
}
+2 -2
View File
@@ -240,7 +240,7 @@ export default function Mine() {
if (!user || !isAuthed()) {
return (
<View className="mine page-in">
<PageHeader no="MY" title="我的" en="Account" />
<PageHeader no="MY" title="我的" sub="个人中心" />
<View className="mine-card login-card">
<Button className="btn-main wx-btn" loading={busy} onClick={onWxLogin}>微信一键登录</Button>
<Text className="muted mine-tip">登录后可绑定手机查看报名场次与活动签到</Text>
@@ -276,7 +276,7 @@ export default function Mine() {
/* ============ 已登录:资料 + 我的报名 ============ */
return (
<View className="mine page-in">
<PageHeader no="MY" title="我的" en="Account" />
<PageHeader no="MY" title="我的" sub="个人中心" />
{/* 资料卡 */}
<View className="mine-card profile">
+27 -15
View File
@@ -1,5 +1,16 @@
/* 大厅与社区共享样式(Soft UI 柔彩卡片语言) */
/* 页面容器:flex 纵向,页头+tab 固定,内容独立滚动(参考资讯页模式) */
.hl { position: relative; z-index: 2; display: flex; flex-direction: column;
height: 100vh; overflow: hidden; padding: 0 24rpx 0; box-sizing: border-box;
background: linear-gradient(165deg, #edf2ff 0%, #f6eefc 34%, #ecf7f6 68%, #eaf4ff 100%); }
.hl::before { content: ''; position: fixed; inset: 0; z-index: -1;
background: radial-gradient(60% 46% at 12% 4%, rgba(229,167,224,.30), transparent 60%),
radial-gradient(50% 40% at 92% 8%, rgba(158,225,236,.34), transparent 60%),
radial-gradient(46% 40% at 80% 96%, rgba(151,225,212,.26), transparent 60%); }
/* 内容区独立滚动(页头+tab 固定) */
.hl-scroll { flex: 1 1 0; height: 0; min-height: 0; }
.hl-scroll-pad { height: 200rpx; }
.hl-pad { padding-bottom: 60rpx; }
.hl-head {
margin: 8rpx 24rpx 20rpx;
@@ -12,11 +23,14 @@
.hl-err { display: block; padding: 40rpx 24rpx; text-align: center; color: #e5484d; font-size: 26rpx; }
.hl-empty { display: block; padding: 100rpx 0; text-align: center; color: #9aa7bd; font-size: 26rpx; }
.hl-chips { display: flex; gap: 14rpx; overflow-x: auto; padding: 6rpx 0 2rpx; white-space: nowrap;
/* 二级筛选 chips:药丸样式(参考资讯页 ct-cat) */
.hl-chips { display: flex; gap: 12rpx; overflow-x: auto; padding: 4rpx 0 14rpx; white-space: nowrap;
&::-webkit-scrollbar { display: none; } }
.hl-chip { flex: none; padding: 10rpx 26rpx; border-radius: 999rpx; background: #fff; color: #6b7a99;
font-size: 24rpx; border: 1rpx solid #e6ebf5;
&.on { color: #3a5fe0; border-color: #b9c8f5; background: #eef2ff; font-weight: 600; } }
.hl-chip { flex: none; padding: 10rpx 28rpx; border-radius: 999px; white-space: nowrap; overflow: hidden;
background: rgba(255,255,255,.72); border: 1rpx solid rgba(99,132,255,.14); color: #8a94c0; font-size: 22rpx;
backdrop-filter: blur(8px);
&.on { background: linear-gradient(135deg, #6b8bff, #8f7bff); color: #ffffff; border-color: transparent; font-weight: 600;
box-shadow: 0 8rpx 22rpx rgba(98,132,255,.28); } }
/* Hub 四宫格 */
.hl-hub { margin: 0 24rpx; display: grid; grid-template-columns: 1fr 1fr; gap: 20rpx; }
@@ -76,15 +90,13 @@
.hl-pcontent { display: block; font-size: 25rpx; line-height: 1.7; color: #7a879e; margin-top: 8rpx; }
.hl-pfoot { display: flex; gap: 36rpx; margin-top: 16rpx; font-size: 22rpx; color: #9aa7bd; }
/* 大厅两级 Tab 统一吸顶容器(一级 TABS + 二级筛选 chips,共享背景与宽度,无缝隙) */
.hl-tabbar { position: sticky; z-index: 60;
padding: 10rpx 24rpx 14rpx;
background: rgba(247, 249, 255, 0.92);
backdrop-filter: blur(20px) saturate(160%); -webkit-backdrop-filter: blur(20px) saturate(160%);
border-bottom: 1rpx solid rgba(99, 132, 255, 0.08); }
.hl-tabs { display: flex; align-items: center; gap: 8rpx; margin: 0 0 8rpx; }
.hl-tab { padding: 10rpx 26rpx; font-size: 26rpx; color: #6b7a99;
&.on { color: #3a5fe0; font-weight: 700; border-bottom: 4rpx solid #3a5fe0; } }
/* 大厅两级 Tab 容器:位于滚动区之外,完全固定不被下拉 */
.hl-tabbar { flex: none; padding: 12rpx 0 6rpx; background: transparent; }
.hl-tabs { display: flex; align-items: center; gap: 6rpx; margin: 0 0 10rpx; }
.hl-tab { padding: 10rpx 24rpx; font-size: 26rpx; color: #8a94c0; border-radius: 999px; white-space: nowrap;
&.on { color: #ffffff; font-weight: 700; background: linear-gradient(135deg, #6b8bff, #8f7bff);
box-shadow: 0 8rpx 22rpx rgba(98,132,255,.28); } }
.hl-tab-spacer { flex: 1; }
.hl-pub { color: #3a5fe0; font-size: 25rpx; font-weight: 600; padding: 8rpx 18rpx;
background: #eef2ff; border-radius: 999rpx; }
.hl-pub { color: #3a5fe0; font-size: 25rpx; font-weight: 600; padding: 8rpx 20rpx;
background: rgba(255,255,255,.72); border: 1rpx solid rgba(99,132,255,.14); border-radius: 999px;
backdrop-filter: blur(8px); }