feat:首页和案例库功能

This commit is contained in:
李伊帆
2026-09-21 15:41:52 +08:00
parent 24d001e0f5
commit b0a711f70a
35 changed files with 1201 additions and 354 deletions

View File

@@ -0,0 +1,188 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import { getProjectSubmissionDetail } from '@/api/project-submission'
import type { ProjectSubmissionItem, ProjectServiceType } from '@/types/project-submission'
import { ensureLoggedIn } from '@/utils/auth-guard'
import { navigateBackWithFallback } from '@/utils/navigate-back'
import { resolveProjectSubmissionMedia } from '@/utils/resolve-project-submission-media'
import heroBg from '@/static/icons/background.png'
const detail = ref<ProjectSubmissionItem | null>(null)
const loading = ref(true)
const heroLogo = ref('')
const serviceIcons = ref<Record<string, string>>({})
const sortedServiceTypes = computed<ProjectServiceType[]>(() => {
if (!detail.value?.serviceTypes?.length)
return []
return [...detail.value.serviceTypes].sort(
(a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0),
)
})
async function loadDetail(id: string) {
loading.value = true
uni.showLoading({ title: '加载中...', mask: true })
try {
const data = await getProjectSubmissionDetail(id)
detail.value = data
const media = await resolveProjectSubmissionMedia(data)
heroLogo.value = media.logoUrl || ''
serviceIcons.value = media.serviceIcons
}
catch {
detail.value = null
uni.showToast({ title: '加载失败', icon: 'none' })
}
finally {
loading.value = false
uni.hideLoading()
}
}
onLoad((options) => {
const id = options?.id as string
if (!id) {
loading.value = false
uni.showToast({ title: '参数错误', icon: 'none' })
return
}
loadDetail(id)
})
onShow(() => {
ensureLoggedIn()
})
function handleBack() {
navigateBackWithFallback('/pages/home-index/index')
}
function handleSubmit() {
if (!detail.value)
return
const businessType = detail.value.businessType || detail.value.title
uni.navigateTo({
url: `/pages/home-index/requirement-submit/index?submissionId=${detail.value.id}&businessType=${encodeURIComponent(businessType)}`,
})
}
</script>
<template>
<view
v-if="detail"
class="business-detail"
>
<view class="business-detail__navbar">
<view
class="business-detail__back"
@tap="handleBack"
>
<image
class="business-detail__back-icon"
src="/static/icons/icon-back.svg"
mode="aspectFit"
/>
</view>
<text class="business-detail__navbar-title">
业务详情
</text>
<view class="business-detail__navbar-placeholder" />
</view>
<scroll-view
class="business-detail__scroll"
scroll-y
:show-scrollbar="false"
>
<view class="business-detail__hero">
<image
class="business-detail__hero-bg"
:src="heroBg"
mode="scaleToFill"
/>
<view class="business-detail__hero-inner">
<image
v-if="heroLogo"
class="business-detail__hero-icon"
:src="heroLogo"
mode="aspectFit"
/>
<text class="business-detail__hero-title">
{{ detail.title }}
</text>
<text
v-if="detail.subtitle"
class="business-detail__hero-subtitle"
>
{{ detail.subtitle }}
</text>
</view>
</view>
<view
v-if="sortedServiceTypes.length"
class="business-detail__section"
>
<text class="business-detail__section-title">
服务类型
</text>
<view class="business-detail__type-row">
<view
v-for="type in sortedServiceTypes"
:key="type.id"
class="business-detail__type-item"
>
<image
v-if="serviceIcons[String(type.id)]"
class="business-detail__type-icon"
:src="serviceIcons[String(type.id)]"
mode="aspectFit"
/>
<text class="business-detail__type-label">
{{ type.serviceName }}
</text>
</view>
</view>
</view>
</scroll-view>
<view class="business-detail__footer">
<view
class="business-detail__submit-btn"
@tap="handleSubmit"
>
立即提报
</view>
</view>
</view>
<view
v-else-if="!loading"
class="business-detail business-detail--empty"
>
<text class="business-detail__empty-text">
暂无详情
</text>
</view>
</template>
<style scoped lang="scss">
@import '../business-detail/index.scss';
.business-detail--empty {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
}
.business-detail__empty-text {
font-size: 28rpx;
color: #999999;
}
</style>