feat(mini): 大厅沉浸式——去除原生导航栏,两级 Tab(一级TABS+二级筛选)sticky 吸顶悬浮不随内容滚动
- hall 页新增 index.config.js:navigationStyle=custom(与其他 tabBar 页一致,去掉原生状态栏/双标题) - 二级筛选 chips 从各 pane 提升到 Hall 顶层,与一级 TABS 连续吸顶 - hl-tabs/hl-chips 加 position:sticky + 毛玻璃背景,top 由 getTopInset() navH + 实测 tab 高度动态赋值 - 服务 tab 无筛选不渲染 chips
This commit is contained in:
@@ -0,0 +1 @@
|
||||
export default { navigationBarTitleText: '大厅', navigationStyle: 'custom' };
|
||||
@@ -2,12 +2,14 @@ import { View, Text, Button } 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 } from '@/components/TopInset';
|
||||
import { PageHeader, getTopInset } from '@/components/TopInset';
|
||||
import Skeleton from '@/components/skeleton';
|
||||
import { isAuthed } from '@/utils/auth';
|
||||
import '@/styles/hall.scss';
|
||||
|
||||
/* 大厅(Tab 容器):任务 / 人才 / 岗位 / 服务 / 社区 页内切换;详情跳子页 */
|
||||
/* 大厅(Tab 容器):任务 / 人才 / 岗位 / 服务 / 社区 页内切换;详情跳子页
|
||||
* 原生导航栏关闭(navigationStyle=custom),两级 Tab(一级 TABS + 二级筛选 chips)
|
||||
* 吸顶悬浮在顶部,内容滚动时不被带走。 */
|
||||
const TABS = [
|
||||
{ k: 'tasks', l: '任务' },
|
||||
{ k: 'talents', l: '人才' },
|
||||
@@ -15,6 +17,23 @@ const TABS = [
|
||||
{ k: 'services', l: '服务' },
|
||||
{ k: 'community', l: '社区' },
|
||||
];
|
||||
const CHIPS = {
|
||||
tasks: [
|
||||
{ k: '', l: '全部' }, { k: 'grab', l: '抢单' }, { k: 'bid', l: '投标' },
|
||||
{ k: 'register', l: '报名' }, { k: 'assign', l: '指派' },
|
||||
],
|
||||
talents: [
|
||||
{ k: '', l: '全部' }, { k: 'opc', l: 'OPC' }, { k: 'seeker', l: '求职者' },
|
||||
],
|
||||
jobs: [
|
||||
{ k: '', l: '全部' }, { k: 'fulltime', l: '全职' }, { k: 'parttime', l: '兼职' },
|
||||
{ k: 'intern', l: '实习' }, { k: 'remote', l: '远程' },
|
||||
],
|
||||
community: [
|
||||
{ k: '', l: '全部' }, { k: '交流', l: '交流' }, { k: '求助', l: '求助' },
|
||||
{ k: '资源', l: '资源' }, { k: '经验', l: '经验' }, { k: '公告', l: '公告' },
|
||||
],
|
||||
};
|
||||
const MODE_LABEL = { grab: '抢单', bid: '投标', register: '报名', assign: '指派', recommend: '推荐' };
|
||||
const WT = { fulltime: '全职', parttime: '兼职', intern: '实习', remote: '远程' };
|
||||
const money = (a, b) => (!a && !b ? '面议' : a && b && a !== b ? `¥${a}–${b}` : `¥${a || b}`);
|
||||
@@ -29,33 +48,63 @@ const PUBLISH_URL = {
|
||||
export default function Hall() {
|
||||
const [tab, setTab] = useState('tasks');
|
||||
const [stat, setStat] = useState(null);
|
||||
useEffect(() => { hallOverview().then(setStat).catch(() => {}); }, []);
|
||||
const [mode, setMode] = useState(''); // 任务筛选
|
||||
const [ttype, setTtype] = useState(''); // 人才筛选
|
||||
const [wt, setWt] = useState(''); // 岗位筛选
|
||||
const [topic, setTopic] = useState(''); // 社区筛选
|
||||
const [tabBarH, setTabBarH] = useState(0); // 一级 tab 实测高度(吸顶基准)
|
||||
const { navH } = getTopInset();
|
||||
|
||||
useEffect(() => {
|
||||
hallOverview().then(setStat).catch(() => {});
|
||||
Taro.nextTick(() => {
|
||||
Taro.createSelectorQuery()
|
||||
.select('.hl-tabs')
|
||||
.boundingClientRect((r) => { if (r && r.height) setTabBarH(r.height); })
|
||||
.exec();
|
||||
});
|
||||
}, []);
|
||||
|
||||
const goPublish = () => {
|
||||
if (!isAuthed()) { Taro.showToast({ title: '请先登录', icon: 'none' }); return; }
|
||||
Taro.navigateTo({ url: PUBLISH_URL[tab] || PUBLISH_URL.tasks });
|
||||
};
|
||||
|
||||
// 当前二级筛选
|
||||
const cur = CHIPS[tab] || null;
|
||||
const chipVal = tab === 'tasks' ? mode : tab === 'talents' ? ttype : tab === 'jobs' ? wt : topic;
|
||||
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" />
|
||||
<View className="hl-tabs">
|
||||
{/* 一级 Tab:吸顶在自定义标题下沿 */}
|
||||
<View className="hl-tabs" style={{ top: `${navH}px`, zIndex: 60 }}>
|
||||
{TABS.map((t) => (
|
||||
<Text key={t.k} className={`hl-tab${tab === t.k ? ' on' : ''}`} onClick={() => setTab(t.k)}>{t.l}</Text>
|
||||
))}
|
||||
<View className="hl-tab-spacer" />
|
||||
<Text className="hl-pub" onClick={goPublish}>+ 发布</Text>
|
||||
</View>
|
||||
{tab === 'tasks' && <TasksPane />}
|
||||
{tab === 'talents' && <TalentsPane />}
|
||||
{tab === 'jobs' && <JobsPane />}
|
||||
{/* 二级筛选:吸顶在一级 Tab 下沿(无筛选的 tab 不渲染) */}
|
||||
{cur && (
|
||||
<View className="hl-chips" style={{ top: `${navH + tabBarH}px`, zIndex: 59 }}>
|
||||
{cur.map((c) => (
|
||||
<Text key={c.k} className={`hl-chip${chipVal === c.k ? ' on' : ''}`} onClick={() => chipSet(c.k)}>{c.l}</Text>
|
||||
))}
|
||||
</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 />}
|
||||
{tab === 'community' && <CommunityPane topic={topic} onTopic={setTopic} />}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/* ---------- 任务 ---------- */
|
||||
function TasksPane() {
|
||||
const [mode, setMode] = useState('');
|
||||
function TasksPane({ mode, onMode }) {
|
||||
const [items, setItems] = useState(null);
|
||||
const [err, setErr] = useState('');
|
||||
const load = useCallback(() => {
|
||||
@@ -64,11 +113,6 @@ function TasksPane() {
|
||||
useEffect(load, [load]);
|
||||
return (
|
||||
<>
|
||||
<View className="hl-chips">
|
||||
{[{ k: '', l: '全部' }, { k: 'grab', l: '抢单' }, { k: 'bid', l: '投标' }, { k: 'register', l: '报名' }, { k: 'assign', l: '指派' }].map((m) => (
|
||||
<Text key={m.k} className={`hl-chip${mode === m.k ? ' on' : ''}`} onClick={() => setMode(m.k)}>{m.l}</Text>
|
||||
))}
|
||||
</View>
|
||||
{err && <Text className="hl-err">{err}</Text>}
|
||||
{!items && !err && <Skeleton />}
|
||||
{items && items.length === 0 && <Text className="hl-empty">暂无任务</Text>}
|
||||
@@ -95,20 +139,14 @@ function TasksPane() {
|
||||
}
|
||||
|
||||
/* ---------- 人才 ---------- */
|
||||
function TalentsPane() {
|
||||
const [type, setType] = useState('');
|
||||
function TalentsPane({ ttype, onTtype }) {
|
||||
const [items, setItems] = useState(null);
|
||||
const [err, setErr] = useState('');
|
||||
useEffect(() => {
|
||||
hallTalents({ type }).then((d) => setItems(d.items || [])).catch((e) => setErr(e.message));
|
||||
}, [type]);
|
||||
hallTalents({ type: ttype }).then((d) => setItems(d.items || [])).catch((e) => setErr(e.message));
|
||||
}, [ttype]);
|
||||
return (
|
||||
<>
|
||||
<View className="hl-chips">
|
||||
{[{ k: '', l: '全部' }, { k: 'opc', l: 'OPC' }, { k: 'seeker', l: '求职者' }].map((t) => (
|
||||
<Text key={t.k} className={`hl-chip${type === t.k ? ' on' : ''}`} onClick={() => setType(t.k)}>{t.l}</Text>
|
||||
))}
|
||||
</View>
|
||||
{err && <Text className="hl-err">{err}</Text>}
|
||||
{!items && !err && <Skeleton />}
|
||||
{items && items.length === 0 && <Text className="hl-empty">暂无人才档案</Text>}
|
||||
@@ -134,8 +172,7 @@ function TalentsPane() {
|
||||
}
|
||||
|
||||
/* ---------- 岗位 ---------- */
|
||||
function JobsPane() {
|
||||
const [wt, setWt] = useState('');
|
||||
function JobsPane({ wt, onWt }) {
|
||||
const [items, setItems] = useState(null);
|
||||
const [err, setErr] = useState('');
|
||||
useEffect(() => {
|
||||
@@ -143,11 +180,6 @@ function JobsPane() {
|
||||
}, [wt]);
|
||||
return (
|
||||
<>
|
||||
<View className="hl-chips">
|
||||
{[{ k: '', l: '全部' }, { k: 'fulltime', l: '全职' }, { k: 'parttime', l: '兼职' }, { k: 'intern', l: '实习' }, { k: 'remote', l: '远程' }].map((t) => (
|
||||
<Text key={t.k} className={`hl-chip${wt === t.k ? ' on' : ''}`} onClick={() => setWt(t.k)}>{t.l}</Text>
|
||||
))}
|
||||
</View>
|
||||
{err && <Text className="hl-err">{err}</Text>}
|
||||
{!items && !err && <Skeleton />}
|
||||
{items && items.length === 0 && <Text className="hl-empty">暂无招聘岗位</Text>}
|
||||
@@ -200,21 +232,15 @@ function ServicesPane() {
|
||||
}
|
||||
|
||||
/* ---------- 社区(列表页内切换;帖子详情跳子页) ---------- */
|
||||
function CommunityPane() {
|
||||
const [topic, setTopic] = useState('全部');
|
||||
function CommunityPane({ topic, onTopic }) {
|
||||
const [posts, setPosts] = useState(null);
|
||||
const load = useCallback(() => {
|
||||
import('@/utils/hall').then(({ communityPosts }) =>
|
||||
communityPosts({ topic: topic === '全部' ? '' : topic }).then((d) => setPosts(d.items || [])).catch(() => {}));
|
||||
communityPosts({ topic: topic === '' ? '' : topic }).then((d) => setPosts(d.items || [])).catch(() => {}));
|
||||
}, [topic]);
|
||||
useEffect(load, [load]);
|
||||
return (
|
||||
<>
|
||||
<View className="hl-chips">
|
||||
{['全部', '交流', '求助', '资源', '经验', '公告'].map((t) => (
|
||||
<Text key={t} className={`hl-chip${topic === t ? ' on' : ''}`} onClick={() => setTopic(t)}>{t}</Text>
|
||||
))}
|
||||
</View>
|
||||
{!posts && <Skeleton />}
|
||||
{posts && posts.length === 0 && <Text className="hl-empty">还没有帖子,来发第一帖</Text>}
|
||||
{(posts || []).map((p) => (
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
.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: 8rpx 24rpx 20rpx; white-space: nowrap;
|
||||
.hl-chips { position: sticky; display: flex; gap: 14rpx; overflow-x: auto; padding: 8rpx 24rpx 20rpx; white-space: nowrap;
|
||||
background: rgba(247, 249, 255, 0.92); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
|
||||
&::-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;
|
||||
@@ -76,8 +77,10 @@
|
||||
.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(页内切换) */
|
||||
.hl-tabs { display: flex; align-items: center; gap: 8rpx; margin: 0 24rpx 20rpx; }
|
||||
/* 大厅 Tab(页内切换,吸顶悬浮不随内容滚动;top 由页面动态赋值) */
|
||||
.hl-tabs { position: sticky; display: flex; align-items: center; gap: 8rpx; margin: 0 24rpx 12rpx;
|
||||
padding: 14rpx 0 10rpx; background: rgba(247, 249, 255, 0.92);
|
||||
backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); }
|
||||
.hl-tab { padding: 10rpx 26rpx; font-size: 26rpx; color: #6b7a99;
|
||||
&.on { color: #3a5fe0; font-weight: 700; border-bottom: 4rpx solid #3a5fe0; } }
|
||||
.hl-tab-spacer { flex: 1; }
|
||||
|
||||
Reference in New Issue
Block a user