JewelryMall/miniprogram/pages/mold/index.vue
2026-03-02 23:35:11 +08:00

243 lines
5.7 KiB
Vue

<template>
<view class="mold-page">
<!-- 自定义导航栏 -->
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="custom-navbar__content" :style="{ height: navBarHeight + 'px' }">
<text class="custom-navbar__title">版房专区</text>
</view>
</view>
<!-- 搜索框 -->
<view class="search-bar">
<input
class="search-bar__input"
type="text"
placeholder="搜索名称、款号、条码号、款式"
:value="keyword"
@input="onSearchInput"
@confirm="doSearch"
/>
</view>
<!-- 版房列表 -->
<view class="mold-list">
<view v-for="mold in filteredMolds" :key="mold.id" class="mold-card">
<text class="mold-card__name">{{ mold.name }}</text>
<scroll-view v-if="(mold.images || []).length > 0" class="mold-card__images" scroll-x>
<image
v-for="(img, idx) in (mold.images || [])"
:key="idx"
class="mold-card__img"
:src="fullUrl(img)"
mode="aspectFill"
@click="previewImage(mold.images || [], idx)"
/>
</scroll-view>
<view v-else class="mold-card__no-img">
<text>暂无图片</text>
</view>
<view class="mold-card__footer">
<text v-if="mold.styleNo" class="mold-card__tag">款号: {{ mold.styleNo }}</text>
<text v-if="mold.style" class="mold-card__tag">款式: {{ mold.style }}</text>
</view>
</view>
</view>
<!-- 空状态 -->
<view v-if="!loading && filteredMolds.length === 0" class="empty-tip">
<text>{{ keyword ? '未找到匹配结果' : '暂无版房信息' }}</text>
<view v-if="keyword" class="empty-tip__contact" @click="contactService">
联系客服咨询定制
</view>
</view>
<!-- 加载中 -->
<view v-if="loading" class="loading-tip">
<text>加载中...</text>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
// @ts-ignore - uni-app 页面生命周期
import { onShow } from '@dcloudio/uni-app'
import type { MoldInfo } from '../../types/mold'
import { get } from '../../utils/request'
import { BASE_URL } from '../../utils/request'
import { searchMolds } from '../../utils/moldSearch'
const molds = ref<MoldInfo[]>([])
const keyword = ref('')
const loading = ref(false)
const statusBarHeight = ref(20)
const navBarHeight = ref(44)
try {
const sysInfo = uni.getSystemInfoSync()
statusBarHeight.value = sysInfo.statusBarHeight || 20
// #ifdef MP-WEIXIN
const menuBtn = uni.getMenuButtonBoundingClientRect()
navBarHeight.value = (menuBtn.top - (sysInfo.statusBarHeight || 20)) * 2 + menuBtn.height
// #endif
} catch { /* fallback */ }
const filteredMolds = computed(() => searchMolds(molds.value, keyword.value))
function fullUrl(path: string): string {
if (!path) return ''
if (path.startsWith('http')) return path
return BASE_URL + path
}
function parseImages(images: any): string[] {
if (Array.isArray(images)) return images
if (typeof images === 'string') {
try { return JSON.parse(images) } catch { return [] }
}
return []
}
function onSearchInput(e: { detail: { value: string } }) {
keyword.value = e.detail.value
}
function doSearch() {
// 本地搜索,无需额外操作
}
function previewImage(images: string[], current: number) {
const urls = images.map(img => fullUrl(img))
uni.previewImage({ urls, current: urls[current] })
}
function contactService() {
uni.showModal({
title: '联系客服',
content: '如需定制服务,请添加客服微信咨询',
showCancel: false,
})
}
async function loadMolds() {
loading.value = true
try {
const res = await get<{ list: any[]; total: number }>('/api/molds', { pageSize: 100 } as any)
const list = res?.list || []
molds.value = list.map((m: any) => ({
...m,
images: parseImages(m.images),
}))
} catch {
// 错误已在 request 中统一处理
} finally {
loading.value = false
}
}
onMounted(() => {
loadMolds()
})
onShow(() => {
loadMolds()
})
</script>
<style scoped>
.mold-page {
min-height: 100vh;
background: #f5f5f5;
}
.custom-navbar {
background: linear-gradient(to right, #FFCFDE, #FFA6C4);
}
.custom-navbar__content {
display: flex;
align-items: center;
justify-content: center;
}
.custom-navbar__title {
font-size: 34rpx;
font-weight: bold;
color: #333;
}
.search-bar {
background: #fff;
padding: 16rpx 24rpx;
}
.search-bar__input {
background: #f5f5f5;
border-radius: 32rpx;
padding: 16rpx 24rpx;
font-size: 26rpx;
color: #333;
}
.mold-list {
padding: 16rpx;
}
.mold-card {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 16rpx;
}
.mold-card__name {
font-size: 30rpx;
font-weight: bold;
color: #333;
display: block;
margin-bottom: 16rpx;
}
.mold-card__images {
white-space: nowrap;
margin-bottom: 12rpx;
}
.mold-card__img {
width: 200rpx;
height: 200rpx;
border-radius: 8rpx;
margin-right: 12rpx;
display: inline-block;
}
.mold-card__no-img {
height: 200rpx;
display: flex;
align-items: center;
justify-content: center;
background: #f5f5f5;
border-radius: 8rpx;
color: #999;
font-size: 26rpx;
margin-bottom: 12rpx;
}
.mold-card__footer {
display: flex;
gap: 16rpx;
}
.mold-card__tag {
font-size: 22rpx;
color: #999;
background: #f5f5f5;
padding: 4rpx 12rpx;
border-radius: 4rpx;
}
.empty-tip {
text-align: center;
padding: 80rpx 0;
color: #999;
font-size: 28rpx;
}
.empty-tip__contact {
margin-top: 20rpx;
color: #e4393c;
font-size: 26rpx;
}
.loading-tip {
text-align: center;
padding: 60rpx 0;
color: #999;
font-size: 28rpx;
}
</style>