246 lines
6.7 KiB
Vue
246 lines
6.7 KiB
Vue
<template>
|
||
<div class="page-shell my-reports-page">
|
||
<!-- 顶部导航 -->
|
||
<view class="report-nav report-nav--tab-root nav-gradient" :style="navSafeStyle">
|
||
<view class="nav-placeholder"></view>
|
||
<text class="nav-title">洗护美容报告</text>
|
||
<view class="nav-placeholder"></view>
|
||
</view>
|
||
|
||
<view class="page-section reports-hero">
|
||
<view class="hero-title">服务成果回顾</view>
|
||
<view class="hero-sub">已生成 <text class="hero-count">{{ reportList.length }}</text> 份报告,点击卡片可查看详情并分享。</view>
|
||
</view>
|
||
|
||
<!-- 网格相册 -->
|
||
<div v-if="reportList.length > 0" class="page-section section-gap gallery-grid">
|
||
<div
|
||
v-for="r in reportList"
|
||
:key="r.id"
|
||
class="gallery-item"
|
||
@click="viewReport(r)"
|
||
>
|
||
<div class="gallery-cover">
|
||
<img
|
||
v-if="r.beforePhotos && r.beforePhotos.length > 0"
|
||
:src="imgUrl(r.beforePhotos[0])"
|
||
class="cover-img"
|
||
/>
|
||
<div v-else class="cover-placeholder">
|
||
<span class="placeholder-icon"><AppIcon name="camera" :size="18" color="#94a3b8" /></span>
|
||
</div>
|
||
<div class="gallery-overlay">
|
||
<div class="overlay-name">{{ r.petName }}</div>
|
||
<div class="overlay-service">{{ r.serviceType }}</div>
|
||
</div>
|
||
</div>
|
||
<div class="gallery-meta">
|
||
<span class="meta-chip">
|
||
<AppIcon name="report" :size="11" color="#64748b" />
|
||
<text>查看报告</text>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<view v-if="!loading && reportList.length === 0" class="empty"><text>暂无报告</text></view>
|
||
|
||
<!-- 仅商家/员工可新建报告;客户仅查看列表 -->
|
||
<view
|
||
v-if="userInfo.role !== 'customer'"
|
||
class="fab-new-report"
|
||
@click="openFillReport"
|
||
hover-class="fab-new-report--hover"
|
||
>
|
||
<text class="fab-new-report-icon">+</text>
|
||
</view>
|
||
|
||
<TabBar current-page="report" @change="goPage" />
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted } from 'vue'
|
||
import { onShow } from '@dcloudio/uni-app'
|
||
import AppIcon from '../../components/AppIcon.vue'
|
||
import TabBar from '../../components/TabBar.vue'
|
||
import { useNavigator } from '../../composables/useNavigator.js'
|
||
import { getReportList, imgUrl, API_ORIGIN } from '../../api/index.js'
|
||
import { getStoreSession, getUserSession } from '../../utils/session.js'
|
||
|
||
const { goPage } = useNavigator()
|
||
|
||
const emit = defineEmits(['change-page'])
|
||
const userInfo = getUserSession()
|
||
const storeInfo = getStoreSession()
|
||
const navSafeStyle = (() => {
|
||
const statusBarHeight = uni.getSystemInfoSync?.().statusBarHeight || 20
|
||
let navHeight = statusBarHeight + 44
|
||
const menuRect = uni.getMenuButtonBoundingClientRect?.()
|
||
if (menuRect && menuRect.top && menuRect.height) {
|
||
const verticalGap = Math.max(menuRect.top - statusBarHeight, 4)
|
||
navHeight = statusBarHeight + verticalGap * 2 + menuRect.height
|
||
}
|
||
return `padding-top:${statusBarHeight}px;height:${navHeight}px;`
|
||
})()
|
||
const loading = ref(false)
|
||
const reportList = ref([])
|
||
|
||
const loadReports = async () => {
|
||
loading.value = true
|
||
const params = userInfo.role === 'boss'
|
||
? { storeId: storeInfo.id, page: 1, pageSize: 50 }
|
||
: { userId: userInfo.id, page: 1, pageSize: 50 }
|
||
const res = await getReportList(params)
|
||
loading.value = false
|
||
if (res.code === 200) reportList.value = res.data
|
||
}
|
||
|
||
const openFillReport = () => {
|
||
uni.navigateTo({ url: '/pages/report/Report' })
|
||
}
|
||
|
||
const viewReport = (r) => {
|
||
let origin = API_ORIGIN
|
||
// #ifdef H5
|
||
origin = window.location.origin
|
||
// #endif
|
||
const url = `${origin}/report.html?token=${r.reportToken}`
|
||
// #ifdef H5
|
||
window.location.href = url
|
||
// #endif
|
||
// #ifndef H5
|
||
uni.navigateTo({ url: `/pages/report-view/reportView?token=${r.reportToken}` })
|
||
// #endif
|
||
}
|
||
|
||
onMounted(() => loadReports())
|
||
onShow(() => loadReports())
|
||
</script>
|
||
|
||
<style scoped>
|
||
.my-reports-page {
|
||
padding: 0 0 calc(76px + env(safe-area-inset-bottom));
|
||
box-sizing: border-box;
|
||
}
|
||
.nav-placeholder { width: 32px; }
|
||
.reports-hero {
|
||
margin-top: 12px;
|
||
padding: 14px 16px;
|
||
border: 1px solid #dcefe3;
|
||
border-radius: 14px;
|
||
background: linear-gradient(135deg, #f3fff7 0%, #ecfbf3 100%);
|
||
}
|
||
.hero-title { font-size: 16px; font-weight: 700; color: #166534; }
|
||
.hero-sub { margin-top: 4px; font-size: 12px; color: #4b5563; line-height: 1.45; }
|
||
.hero-count { font-weight: 700; color: #2db96d; }
|
||
.placeholder-icon {
|
||
width: 36px;
|
||
height: 36px;
|
||
border-radius: 10px;
|
||
background: #e2e8f0;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.report-nav {
|
||
padding: 14px 16px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 10;
|
||
}
|
||
.report-nav--tab-root .nav-title {
|
||
flex: 1;
|
||
text-align: center;
|
||
max-width: 72%;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
}
|
||
.gallery-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 12px;
|
||
padding-top: 14px;
|
||
}
|
||
.gallery-item {
|
||
border-radius: 14px;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
background: #fff;
|
||
border: 1px solid #e8edf4;
|
||
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.06);
|
||
}
|
||
.gallery-cover {
|
||
position: relative;
|
||
width: 100%;
|
||
aspect-ratio: 1;
|
||
overflow: hidden;
|
||
}
|
||
.cover-img { width: 100%; height: 100%; object-fit: cover; }
|
||
.cover-placeholder {
|
||
width: 100%;
|
||
height: 100%;
|
||
background: #f0ede8;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.gallery-overlay {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
padding: 8px 10px;
|
||
background: linear-gradient(transparent, rgba(0,0,0,0.65));
|
||
color: #fff;
|
||
}
|
||
.overlay-name { font-size: 13px; font-weight: 600; }
|
||
.overlay-service { font-size: 11px; opacity: 0.9; margin-top: 2px; }
|
||
.gallery-meta { padding: 8px 10px 10px; background: #fff; }
|
||
.meta-chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
height: 22px;
|
||
padding: 0 8px;
|
||
border-radius: 999px;
|
||
background: #f1f5f9;
|
||
color: #64748b;
|
||
font-size: 11px;
|
||
}
|
||
|
||
/* 新建报告(与首页新建预约 FAB 对齐,z-index 高于 TabBar) */
|
||
.fab-new-report {
|
||
position: fixed;
|
||
right: 20px;
|
||
bottom: calc(56px + env(safe-area-inset-bottom) + 20px);
|
||
width: 56px;
|
||
height: 56px;
|
||
border-radius: 28px;
|
||
background: #2db96d;
|
||
box-shadow: 0 6px 20px rgba(45, 185, 109, 0.42);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1002;
|
||
}
|
||
.fab-new-report--hover {
|
||
opacity: 0.92;
|
||
transform: scale(0.96);
|
||
}
|
||
.fab-new-report-icon {
|
||
color: #fff;
|
||
font-size: 32px;
|
||
font-weight: 300;
|
||
line-height: 1;
|
||
margin-top: -2px;
|
||
}
|
||
</style>
|