验收完成40%
This commit is contained in:
@@ -1,16 +1,10 @@
|
||||
<!-- 页面编号:G-01;用途:我的家谱列表与首次分流;本轮仍待用户重新审核。 -->
|
||||
<template>
|
||||
<view class="page-shell genealogy-index">
|
||||
<image
|
||||
class="page-paper-texture"
|
||||
src="/static/assets/foundation/opaque/page-paper.jpg"
|
||||
mode="scaleToFill"
|
||||
/>
|
||||
<image
|
||||
class="page-footer-landscape"
|
||||
src="/static/assets/foundation/transparent/footer-mountain-bamboo.png"
|
||||
mode="scaleToFill"
|
||||
/>
|
||||
<view
|
||||
class="page-shell genealogy-index"
|
||||
:class="{ 'genealogy-index--split': isListLayout }"
|
||||
>
|
||||
<GenealogyPageBackground />
|
||||
<PageHeader
|
||||
root
|
||||
title="我的家谱"
|
||||
@@ -31,14 +25,15 @@
|
||||
<text class="state-title">暂时无法读取家谱</text>
|
||||
<text class="state-copy">网络或服务暂不可用,请稍后重新查看。</text>
|
||||
<view class="state-retry" hover-class="action-hover" @click="retryLoad">
|
||||
<image class="state-retry__skin" src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" />
|
||||
<image class="state-retry__skin" src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png" mode="aspectFit" />
|
||||
<text class="state-retry__copy">重新加载</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<template v-else-if="hasGenealogies">
|
||||
<view class="current-slip" @click="openSwitcher">
|
||||
<view class="genealogy-fixed-zone">
|
||||
<view class="current-slip" @click="openSwitcher">
|
||||
<image
|
||||
class="current-frame"
|
||||
src="/static/assets/modules/genealogy/transparent/current-slip-frame.png"
|
||||
@@ -87,90 +82,98 @@
|
||||
<text class="current-meta-item-text">管理员</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="shortcut-grid">
|
||||
<view
|
||||
v-for="item in shortcuts"
|
||||
:key="item.key"
|
||||
class="shortcut-item"
|
||||
@click="openShortcut(item.key)"
|
||||
>
|
||||
<image class="shortcut-icon" :src="item.icon" mode="aspectFit" />
|
||||
<text class="shortcut-label">{{ item.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<image
|
||||
class="section-divider"
|
||||
src="/static/assets/modules/genealogy/transparent/section-divider.png"
|
||||
mode="scaleToFill"
|
||||
/>
|
||||
|
||||
<view class="genealogy-lower">
|
||||
<view v-if="createdGenealogies.length" class="list-section">
|
||||
<view class="section-heading"><text>我创建的</text></view>
|
||||
<GenealogyCard
|
||||
v-for="item in createdGenealogies"
|
||||
:key="item.id"
|
||||
:genealogy="item"
|
||||
role="管理员"
|
||||
:selected="item.id === currentGenealogy.id"
|
||||
@select="openGenealogy"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view v-if="joinedGenealogies.length" class="list-section">
|
||||
<view class="section-heading"><text>我加入的</text></view>
|
||||
<GenealogyCard
|
||||
v-for="item in joinedGenealogies"
|
||||
:key="item.id"
|
||||
:genealogy="item"
|
||||
role="成员"
|
||||
@select="openGenealogy"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="list-section application-section">
|
||||
<view class="section-heading"><text>加入申请</text></view>
|
||||
<view class="shortcut-grid">
|
||||
<view
|
||||
v-for="item in applicationRecords"
|
||||
:key="item.id"
|
||||
class="application-record"
|
||||
hover-class="action-hover"
|
||||
@click="openApplication(item)"
|
||||
v-for="item in shortcuts"
|
||||
:key="item.key"
|
||||
class="shortcut-item"
|
||||
@click="openShortcut(item.key)"
|
||||
>
|
||||
<image class="application-record__skin" src="/static/assets/modules/genealogy/transparent/list-slip-frame.png" mode="scaleToFill" />
|
||||
<view class="application-record__main">
|
||||
<view class="application-record__title-row">
|
||||
<text class="application-record__name">{{ item.name }}</text>
|
||||
<text class="application-record__status" :class="`application-record__status--${item.tone}`">{{ item.statusLabel }}</text>
|
||||
</view>
|
||||
<text class="application-record__copy">{{ item.description }}</text>
|
||||
</view>
|
||||
<image class="application-record__chevron" src="/static/assets/foundation/transparent/chevron-right.png" mode="aspectFit" />
|
||||
<image class="shortcut-icon" :src="item.icon" mode="aspectFit" />
|
||||
<text class="shortcut-label">{{ item.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="create-action" @click="openAddDialog">
|
||||
<image
|
||||
class="create-cloud"
|
||||
src="/static/assets/modules/genealogy/transparent/create-cloud.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<image
|
||||
class="create-icon"
|
||||
src="/static/assets/modules/genealogy/transparent/add.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text>添加家谱</text>
|
||||
<image
|
||||
class="create-cloud create-cloud--right"
|
||||
src="/static/assets/modules/genealogy/transparent/create-cloud.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<image
|
||||
class="section-divider"
|
||||
src="/static/assets/modules/genealogy/transparent/section-divider.png"
|
||||
mode="scaleToFill"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="genealogy-list-scroll"
|
||||
scroll-y
|
||||
:scroll-top="listScrollCommand"
|
||||
@scroll="handleListScroll"
|
||||
>
|
||||
<view class="genealogy-lower">
|
||||
<view v-if="createdGenealogies.length" class="list-section">
|
||||
<view class="section-heading"><text>我创建的</text></view>
|
||||
<GenealogyCard
|
||||
v-for="item in createdGenealogies"
|
||||
:key="item.id"
|
||||
:genealogy="item"
|
||||
role="管理员"
|
||||
:selected="item.id === currentGenealogy.id"
|
||||
@select="openGenealogy"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view v-if="joinedGenealogies.length" class="list-section">
|
||||
<view class="section-heading"><text>我加入的</text></view>
|
||||
<GenealogyCard
|
||||
v-for="item in joinedGenealogies"
|
||||
:key="item.id"
|
||||
:genealogy="item"
|
||||
role="成员"
|
||||
@select="openGenealogy"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="list-section application-section">
|
||||
<view class="section-heading"><text>加入申请</text></view>
|
||||
<view
|
||||
v-for="item in applicationRecords"
|
||||
:key="item.id"
|
||||
class="application-record"
|
||||
hover-class="action-hover"
|
||||
@click="openApplication(item)"
|
||||
>
|
||||
<image class="application-record__skin" src="/static/assets/modules/genealogy/transparent/list-slip-frame.png" mode="scaleToFill" />
|
||||
<view class="application-record__main">
|
||||
<view class="application-record__title-row">
|
||||
<text class="application-record__name">{{ item.name }}</text>
|
||||
<text class="application-record__status" :class="`application-record__status--${item.tone}`">{{ item.statusLabel }}</text>
|
||||
</view>
|
||||
<text class="application-record__copy">{{ item.description }}</text>
|
||||
</view>
|
||||
<image class="application-record__chevron" src="/static/assets/foundation/transparent/chevron-right.png" mode="aspectFit" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="create-action" @click="openAddDialog">
|
||||
<image
|
||||
class="create-cloud"
|
||||
src="/static/assets/modules/genealogy/transparent/create-cloud.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<image
|
||||
class="create-icon"
|
||||
src="/static/assets/modules/genealogy/transparent/add.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text>添加家谱</text>
|
||||
<image
|
||||
class="create-cloud create-cloud--right"
|
||||
src="/static/assets/modules/genealogy/transparent/create-cloud.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<view v-else class="genealogy-empty-state">
|
||||
@@ -203,8 +206,8 @@
|
||||
>
|
||||
<image
|
||||
class="empty-search-action__skin"
|
||||
src="/static/assets/foundation/opaque/a01-primary-button.png"
|
||||
mode="scaleToFill"
|
||||
src="/static/assets/foundation/transparent/a01-scroll-primary-v3.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="empty-search-action__copy empty-search-action__copy--primary">搜索家谱</text>
|
||||
</view>
|
||||
@@ -215,8 +218,8 @@
|
||||
>
|
||||
<image
|
||||
class="empty-invite-action__skin"
|
||||
src="/static/assets/foundation/opaque/a01-secondary-button.png"
|
||||
mode="scaleToFill"
|
||||
src="/static/assets/foundation/transparent/a01-scroll-secondary-v3.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="empty-invite-action__copy">邀请码加入</text>
|
||||
</view>
|
||||
@@ -231,19 +234,13 @@
|
||||
|
||||
<view v-if="addDialogVisible" class="add-dialog-layer" @click="closeAddDialog">
|
||||
<view class="add-dialog" @click.stop>
|
||||
<image class="add-dialog__skin" src="/static/assets/modules/genealogy/opaque/g01-empty-panel.png" mode="scaleToFill" />
|
||||
<image class="add-dialog__skin" src="/static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png" mode="aspectFit" />
|
||||
<view class="add-dialog__content">
|
||||
<text class="dialog-title">添加家谱</text>
|
||||
<text class="dialog-copy">建议先搜索已有家谱,避免重复创建</text>
|
||||
<view class="dialog-primary" @click="applyToJoin">
|
||||
<image class="dialog-action__skin" src="/static/assets/foundation/opaque/a01-primary-button.png" mode="scaleToFill" />
|
||||
<text class="dialog-primary__copy">搜索家谱</text>
|
||||
</view>
|
||||
<view class="dialog-secondary" @click="joinByInvite">
|
||||
<image class="dialog-action__skin" src="/static/assets/foundation/opaque/a01-secondary-button.png" mode="scaleToFill" />
|
||||
<text>邀请码加入</text>
|
||||
</view>
|
||||
<view class="dialog-create" @click="createGenealogy">确认没有现有家谱,继续创建</view>
|
||||
<AppButton block label="搜索家谱" @click="applyToJoin" />
|
||||
<AppButton block type="secondary" label="邀请码加入" @click="joinByInvite" />
|
||||
<AppButton block type="secondary" label="确认没有现有家谱,继续创建" @click="createGenealogy" />
|
||||
<view class="dialog-close" @click="closeAddDialog">取消</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -251,7 +248,7 @@
|
||||
|
||||
<view v-if="switcherVisible" class="genealogy-switcher-layer" @click="closeSwitcher">
|
||||
<view class="genealogy-switcher" @click.stop>
|
||||
<image class="genealogy-switcher__skin" src="/static/assets/modules/genealogy/opaque/g01-empty-panel.png" mode="scaleToFill" />
|
||||
<image class="genealogy-switcher__skin" src="/static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png" mode="aspectFit" />
|
||||
<view class="genealogy-switcher__content">
|
||||
<text class="dialog-title">切换当前家谱</text>
|
||||
<view
|
||||
@@ -277,10 +274,12 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from "vue";
|
||||
import { computed, nextTick, ref } from "vue";
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app";
|
||||
import AppTabbar from "@/components/AppTabbar.vue";
|
||||
import AppButton from "@/components/AppButton.vue";
|
||||
import GenealogyCard from "@/components/GenealogyCard.vue";
|
||||
import GenealogyPageBackground from "@/components/GenealogyPageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
import { genealogies, notifications } from "@/data/mock.js";
|
||||
|
||||
@@ -291,6 +290,8 @@ const forceEmptyState = ref(false);
|
||||
const addDialogVisible = ref(false);
|
||||
const switcherVisible = ref(false);
|
||||
const selectedGenealogyId = ref(genealogies[0]?.id || null);
|
||||
const listScrollCommand = ref(0);
|
||||
const currentListScrollTop = ref(0);
|
||||
|
||||
const readPresentationState = () => {
|
||||
if (typeof location !== "undefined") {
|
||||
@@ -322,6 +323,9 @@ const unreadCount = computed(
|
||||
() => notifications.filter((item) => item.unread).length,
|
||||
);
|
||||
const hasGenealogies = computed(() => !forceEmptyState.value && list.value.length > 0);
|
||||
const isListLayout = computed(
|
||||
() => !isLoading.value && !hasError.value && hasGenealogies.value,
|
||||
);
|
||||
const createdGenealogies = computed(() =>
|
||||
list.value.filter((item, index) => index === 0),
|
||||
);
|
||||
@@ -376,9 +380,19 @@ const openAddDialog = () => { addDialogVisible.value = true; };
|
||||
const closeAddDialog = () => { addDialogVisible.value = false; };
|
||||
const openSwitcher = () => { switcherVisible.value = true; };
|
||||
const closeSwitcher = () => { switcherVisible.value = false; };
|
||||
const selectGenealogy = (genealogy) => {
|
||||
const handleListScroll = (event) => {
|
||||
currentListScrollTop.value = Number(event?.detail?.scrollTop || 0);
|
||||
};
|
||||
const resetListScroll = async () => {
|
||||
listScrollCommand.value = currentListScrollTop.value;
|
||||
await nextTick();
|
||||
listScrollCommand.value = 0;
|
||||
currentListScrollTop.value = 0;
|
||||
};
|
||||
const selectGenealogy = async (genealogy) => {
|
||||
selectedGenealogyId.value = genealogy.id;
|
||||
closeSwitcher();
|
||||
await resetListScroll();
|
||||
};
|
||||
const openApplication = (record) => {
|
||||
const statusQuery = record.id === "pending" ? "pending" : record.id;
|
||||
@@ -408,37 +422,40 @@ const openShortcut = (key) => {
|
||||
background: #f9f6ef;
|
||||
}
|
||||
|
||||
.page-paper-texture {
|
||||
position: absolute;
|
||||
top: calc(124rpx + var(--status-bar-height, 0px));
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.72;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.page-footer-landscape {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 112rpx;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 480rpx;
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.genealogy-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 24rpx 32rpx 176rpx;
|
||||
}
|
||||
|
||||
.genealogy-index--split {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
min-height: 0;
|
||||
flex-direction: column;
|
||||
padding-bottom: calc(112rpx + env(safe-area-inset-bottom));
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.genealogy-index--split .genealogy-content {
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.genealogy-fixed-zone {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.genealogy-list-scroll {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.current-slip {
|
||||
position: relative;
|
||||
display: block;
|
||||
@@ -531,8 +548,9 @@ const openShortcut = (key) => {
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
margin-top: 0;
|
||||
color: $ink-muted;
|
||||
font-size: 25rpx;
|
||||
color: #62584c;
|
||||
font-size: 27rpx;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
@@ -635,10 +653,23 @@ const openShortcut = (key) => {
|
||||
.application-record__main { position: relative; z-index: 1; min-width: 0; flex: 1; }
|
||||
.application-record__title-row { display: flex; align-items: center; justify-content: space-between; }
|
||||
.application-record__name { overflow: hidden; color: $ink; font-family: "STKaiti", "KaiTi", serif; font-size: 32rpx; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.application-record__status { flex: 0 0 auto; margin-left: 16rpx; color: #9a641f; font-size: 23rpx; }
|
||||
.application-record__status {
|
||||
flex: 0 0 auto;
|
||||
margin-left: 16rpx;
|
||||
color: #7f4f16;
|
||||
font-size: 27rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
.application-record__status--rejected { color: #a7160c; }
|
||||
.application-record__status--muted { color: $ink-muted; }
|
||||
.application-record__copy { display: block; margin-top: 10rpx; color: $ink-muted; font-size: 23rpx; }
|
||||
.application-record__status--muted { color: #62584c; }
|
||||
.application-record__copy {
|
||||
display: block;
|
||||
margin-top: 10rpx;
|
||||
color: #62584c;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.application-record__chevron { position: relative; z-index: 1; width: 32rpx; height: 32rpx; margin-left: 14rpx; }
|
||||
|
||||
.create-action {
|
||||
@@ -862,14 +893,8 @@ const openShortcut = (key) => {
|
||||
.genealogy-switcher__content { position: relative; z-index: 1; display: flex; min-height: 720rpx; flex-direction: column; align-items: center; box-sizing: border-box; padding: 78rpx 58rpx 50rpx; }
|
||||
.dialog-title { color: $brand-red; font-family: "STKaiti", "KaiTi", serif; font-size: 42rpx; font-weight: 700; letter-spacing: 4rpx; }
|
||||
.dialog-copy { margin-top: 18rpx; color: $ink-muted; font-size: 23rpx; }
|
||||
.dialog-primary,
|
||||
.dialog-secondary { position: relative; display: flex; width: 100%; height: 98rpx; align-items: center; justify-content: center; overflow: hidden; font-family: "STKaiti", "KaiTi", serif; font-size: 29rpx; }
|
||||
.dialog-primary { margin-top: 42rpx; color: #fff9ec; }
|
||||
.dialog-secondary { margin-top: 16rpx; color: #7b4e24; }
|
||||
.dialog-action__skin { position: absolute; inset: 0; width: 100%; height: 100%; }
|
||||
.dialog-primary__copy,
|
||||
.dialog-secondary > text { position: relative; z-index: 1; }
|
||||
.dialog-create { display: flex; min-height: 76rpx; align-items: center; margin-top: 8rpx; color: $brand-red; font-size: 23rpx; }
|
||||
.add-dialog__content > .app-button:first-of-type { margin-top: 34rpx; }
|
||||
.add-dialog__content > .app-button + .app-button { margin-top: 10rpx; }
|
||||
.dialog-close { display: flex; min-height: 72rpx; align-items: center; justify-content: center; margin-top: auto; color: $ink-muted; font-size: 24rpx; }
|
||||
.switcher-item { display: flex; width: 100%; min-height: 108rpx; align-items: center; justify-content: space-between; box-sizing: border-box; padding: 18rpx 12rpx; border-bottom: 1rpx solid rgba(181, 138, 75, 0.42); }
|
||||
.switcher-item:first-of-type { margin-top: 28rpx; }
|
||||
|
||||
Reference in New Issue
Block a user