修复了大部分样式引起的小问题

This commit is contained in:
tsui110 2026-02-07 00:58:10 +08:00
parent e7256ae88e
commit 636041d6fa
7 changed files with 334 additions and 42 deletions

View File

@ -104,7 +104,7 @@
<view class="game-topbar"> <view class="game-topbar">
<text class="game-topbar-title">对对碰游戏</text> <text class="game-topbar-title">对对碰游戏</text>
<view class="game-close-btn" @tap="closeGame"> <view class="game-close-btn" @tap="closeGame">
<text>×</text> <text class="game-close-btn-text">×</text>
</view> </view>
</view> </view>
@ -2366,7 +2366,7 @@ onLoad((opts) => {
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
border-radius: 50%; border-radius: 50%;
text { .game-close-btn-text {
font-size: 48rpx; font-size: 48rpx;
color: rgba(255, 255, 255, 0.8); color: rgba(255, 255, 255, 0.8);
line-height: 1; line-height: 1;

View File

@ -69,9 +69,9 @@
> >
<text class="choice-number">{{ item.number || item.position || index + 1 }}</text> <text class="choice-number">{{ item.number || item.position || index + 1 }}</text>
<view class="choice-status"> <view class="choice-status">
<text v-if="item.status === 'sold' || item.is_sold">已售</text> <text class="choice-status-text" v-if="item.status === 'sold' || item.is_sold">已售</text>
<text v-else-if="isChoiceSelected(item)">已选</text> <text class="choice-status-text" v-else-if="isChoiceSelected(item)">已选</text>
<text v-else>可选</text> <text class="choice-status-text" v-else>可选</text>
</view> </view>
</view> </view>
</view> </view>
@ -960,7 +960,7 @@ watch([activityId, currentIssueId], ([newActId, newIssueId]) => {
font-size: 20rpx; font-size: 20rpx;
color: $text-tertiary; color: $text-tertiary;
text { .choice-status-text {
padding: 4rpx 12rpx; padding: 4rpx 12rpx;
border-radius: 20rpx; border-radius: 20rpx;
background: rgba(255, 255, 255, 0.8); background: rgba(255, 255, 255, 0.8);

View File

@ -4,7 +4,14 @@
<view class="loading" v-if="loading">加载中...</view> <view class="loading" v-if="loading">加载中...</view>
<view v-else-if="isOutOfStock" class="empty">商品库存不足由于市场价格存在波动请联系客服核实价格和补充库存</view> <view v-else-if="isOutOfStock" class="empty">商品库存不足由于市场价格存在波动请联系客服核实价格和补充库存</view>
<view v-else-if="detail.id" class="detail-wrap"> <view v-else-if="detail.id" class="detail-wrap">
<image v-if="detail.main_image" class="main-image" :src="detail.main_image" mode="widthFix" /> <!-- 商品图片轮播 -->
<swiper class="main-image-swiper" v-if="imageList.length > 0" circular autoplay interval="3000" duration="500">
<swiper-item v-for="(img, index) in imageList" :key="index">
<image class="main-image" :src="img" mode="aspectFill" @tap="previewImage(index)" />
</swiper-item>
</swiper>
<!-- 单张图片显示 -->
<image v-else-if="mainImage" class="main-image" :src="mainImage" mode="aspectFill" @tap="previewImage(0)" />
<view class="info-card"> <view class="info-card">
<view class="title">{{ detail.title || detail.name || '-' }}</view> <view class="title">{{ detail.title || detail.name || '-' }}</view>
<view class="price-row"> <view class="price-row">
@ -37,7 +44,7 @@
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app' import { onLoad } from '@dcloudio/uni-app'
import { getProductDetail } from '../../api/appUser' import { getProductDetail } from '../../api/appUser'
import { redeemProductByPoints } from '../../utils/request.js' import { redeemProductByPoints } from '../../utils/request.js'
@ -46,11 +53,53 @@ const detail = ref({})
const loading = ref(false) const loading = ref(false)
const isOutOfStock = ref(false) const isOutOfStock = ref(false)
//
const imageList = computed(() => {
if (!detail.value) return []
// 使 album
if (detail.value.album) {
// album
if (Array.isArray(detail.value.album)) {
return detail.value.album.filter(img => img) //
}
// album
if (typeof detail.value.album === 'string') {
return [detail.value.album]
}
}
// 使 main_image
if (detail.value.main_image) {
return [detail.value.main_image]
}
return []
})
//
const mainImage = computed(() => {
if (imageList.value.length > 0) {
return imageList.value[0]
}
return detail.value.main_image || ''
})
function formatPrice(p) { function formatPrice(p) {
if (p === undefined || p === null) return '0.00' if (p === undefined || p === null) return '0.00'
return (Number(p) / 100).toFixed(2) return (Number(p) / 100).toFixed(2)
} }
//
function previewImage(index) {
if (imageList.value.length > 0) {
uni.previewImage({
urls: imageList.value,
current: index
})
}
}
// - // -
function formatPoints(value) { function formatPoints(value) {
if (value === undefined || value === null) return '0.00' if (value === undefined || value === null) return '0.00'
@ -71,7 +120,15 @@ async function fetchDetail(id) {
try { try {
const res = await getProductDetail(id) const res = await getProductDetail(id)
detail.value = res || {} detail.value = res || {}
console.log(detail.value); console.log('[商品详情] 原始数据:', detail.value)
// - in_stock stock
if (detail.value.in_stock !== undefined) {
detail.value.stock = detail.value.in_stock ? 99 : 0
}
console.log('[商品详情] 处理后数据:', detail.value)
if (detail.value.code === 20002 || detail.value.message === '商品缺货') { if (detail.value.code === 20002 || detail.value.message === '商品缺货') {
// ,"" // ,""
// request.js // request.js
@ -233,6 +290,13 @@ onLoad((opts) => {
z-index: 1; z-index: 1;
} }
/* 商品图片轮播 */
.main-image-swiper {
width: 100%;
height: 750rpx;
background: $bg-secondary;
}
.main-image { .main-image {
width: 100%; width: 100%;
height: 750rpx; height: 750rpx;

View File

@ -130,7 +130,7 @@
:class="{ disabled: !canSendCode || sendingCode || countdown > 0 }" :class="{ disabled: !canSendCode || sendingCode || countdown > 0 }"
@tap="handleSendCode" @tap="handleSendCode"
> >
<text>{{ sendingCode ? '发送中' : (countdown > 0 ? `${countdown}s` : '获取验证码') }}</text> <text class="send-code-btn-text">{{ sendingCode ? '发送中' : (countdown > 0 ? `${countdown}s` : '获取验证码') }}</text>
</view> </view>
</view> </view>
</view> </view>
@ -990,7 +990,7 @@ function fetchExtraData(userId) {
justify-content: center; justify-content: center;
margin-left: 16rpx; margin-left: 16rpx;
text { .send-code-btn-text {
font-size: 24rpx; font-size: 24rpx;
color: #fff; color: #fff;
font-weight: 600; font-weight: 600;

View File

@ -2,6 +2,14 @@
<view class="page"> <view class="page">
<view class="bg-decoration"></view> <view class="bg-decoration"></view>
<!-- 自定义 tabBar -->
<!-- #ifdef MP-TOUTIAO -->
<customTabBarToutiao />
<!-- #endif -->
<!-- #ifndef MP-TOUTIAO -->
<customTabBar />
<!-- #endif -->
<!-- [NEW] 全新左右布局布局容器 --> <!-- [NEW] 全新左右布局布局容器 -->
<view class="shop-layout"> <view class="shop-layout">
@ -39,6 +47,9 @@
<view class="search-bar"> <view class="search-bar">
<text class="search-icon">🔍</text> <text class="search-icon">🔍</text>
<input class="search-input" v-model="keyword" placeholder="搜好物" @confirm="onSearchConfirm" /> <input class="search-input" v-model="keyword" placeholder="搜好物" @confirm="onSearchConfirm" />
<view class="filter-btn" @tap="togglePriceFilter">
<text class="filter-icon">🔽</text>
</view>
</view> </view>
<!-- 频道切换 (商品/优惠券) --> <!-- 频道切换 (商品/优惠券) -->
<view class="tab-pill"> <view class="tab-pill">
@ -54,6 +65,33 @@
</view> </view>
</view> </view>
<!-- 价格区间筛选面板 (独立浮层) -->
<view class="price-filter-panel" :class="{ expanded: showPriceFilter }">
<view class="filter-row">
<view class="price-input-group">
<input class="price-input" v-model="priceMin" type="digit" placeholder="最低价" />
<text class="price-separator">-</text>
<input class="price-input" v-model="priceMax" type="digit" placeholder="最高价" />
</view>
<view class="filter-actions">
<view class="filter-btn-reset" @tap="resetPriceFilter">重置</view>
<view class="filter-btn-confirm" @tap="applyPriceFilter">确定</view>
</view>
</view>
<!-- 快捷价格区间 -->
<view class="quick-price-ranges">
<view
v-for="range in priceRanges"
:key="range.key"
class="price-range-tag"
:class="{ active: isRangeActive(range.key) }"
@tap="selectQuickPrice(range)"
>
{{ range.label }}
</view>
</view>
</view>
<!-- 内容滚动容器 (带缩放动画) --> <!-- 内容滚动容器 (带缩放动画) -->
<scroll-view <scroll-view
scroll-y scroll-y
@ -81,7 +119,7 @@
<text class="points-val">{{ p.points }}</text> <text class="points-val">{{ p.points }}</text>
<text class="points-unit">积分</text> <text class="points-unit">积分</text>
</view> </view>
<view class="redeem-btn-sm" @tap.stop="onRedeemTap(p)">+</view> <view class="redeem-btn-sm" v-if="p.stock > 0" @tap.stop="onRedeemTap(p)">+</view>
</view> </view>
</view> </view>
</view> </view>
@ -111,7 +149,7 @@
</view> </view>
<view v-if="!items.length && !loading" class="empty-mini"> <view v-if="!items.length && !loading" class="empty-mini">
<image src="/static/empty.png" mode="widthFix" /> <image class="empty-mini-img" src="/static/empty.png" mode="widthFix" />
<text>空空如也</text> <text>空空如也</text>
</view> </view>
@ -153,6 +191,9 @@ const hasMore = ref(true)
const categories = ref([]) const categories = ref([])
const selectedCategoryId = ref(0) const selectedCategoryId = ref(0)
//
let hasInitialized = false
// //
const loadingTexts = [ const loadingTexts = [
'请稍等,正在努力加载中。。。', '请稍等,正在努力加载中。。。',
@ -201,6 +242,7 @@ watch(loading, (newVal) => {
// //
const priceMin = ref('') const priceMin = ref('')
const priceMax = ref('') const priceMax = ref('')
const showPriceFilter = ref(false)
const priceRanges = [ const priceRanges = [
{ key: 'all', label: '全部', min: null, max: null }, { key: 'all', label: '全部', min: null, max: null },
{ key: '0-100', label: '0-100', min: 0, max: 100 }, { key: '0-100', label: '0-100', min: 0, max: 100 },
@ -351,6 +393,8 @@ function applyPriceFilter() {
hasMore.value = true hasMore.value = true
allItems.value = [] allItems.value = []
loadItems() loadItems()
//
showPriceFilter.value = false
} }
function resetPriceFilter() { function resetPriceFilter() {
@ -362,9 +406,12 @@ function resetPriceFilter() {
hasMore.value = true hasMore.value = true
allItems.value = [] allItems.value = []
loadItems() loadItems()
//
showPriceFilter.value = false
} }
function selectQuickPrice(range) { function selectQuickPrice(range) {
vibrateShort()
activePriceRange.value = range.key activePriceRange.value = range.key
if (range.min !== null) { if (range.min !== null) {
priceMin.value = range.min.toString() priceMin.value = range.min.toString()
@ -381,12 +428,20 @@ function selectQuickPrice(range) {
hasMore.value = true hasMore.value = true
allItems.value = [] allItems.value = []
loadItems() loadItems()
//
showPriceFilter.value = false
} }
function isRangeActive(range) { function isRangeActive(range) {
return activePriceRange.value === range.key return activePriceRange.value === range.key
} }
// /
function togglePriceFilter() {
vibrateShort()
showPriceFilter.value = !showPriceFilter.value
}
function onSearchConfirm() { function onSearchConfirm() {
// //
page.value = 1 page.value = 1
@ -494,11 +549,15 @@ onShow(() => {
const token = uni.getStorageSync('token') const token = uni.getStorageSync('token')
if (token) { if (token) {
//
if (!hasInitialized) {
page.value = 1 page.value = 1
hasMore.value = true hasMore.value = true
allItems.value = [] allItems.value = []
loadItems() loadItems()
fetchCategories() fetchCategories()
hasInitialized = true
}
} }
}) })
@ -590,11 +649,18 @@ onUnmounted(() => {
flex-direction: column; flex-direction: column;
background: #fff; background: #fff;
position: relative; position: relative;
overflow: visible;
} }
.top-nav { .top-nav {
padding: 20rpx 24rpx; padding: 20rpx 24rpx;
border-bottom: 1rpx solid rgba(0, 0, 0, 0.03); border-bottom: 1rpx solid rgba(0, 0, 0, 0.03);
position: relative;
z-index: 50;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(30rpx);
-webkit-backdrop-filter: blur(30rpx);
flex-shrink: 0;
} }
.search-wrap { .search-wrap {
display: flex; display: flex;
@ -614,6 +680,124 @@ onUnmounted(() => {
.search-icon { font-size: 24rpx; opacity: 0.4; } .search-icon { font-size: 24rpx; opacity: 0.4; }
.search-input { flex: 1; margin-left: 12rpx; font-size: 24rpx; } .search-input { flex: 1; margin-left: 12rpx; font-size: 24rpx; }
/* 筛选按钮 */
.filter-btn {
margin-left: 12rpx;
padding: 8rpx;
display: flex;
align-items: center;
justify-content: center;
}
.filter-icon {
font-size: 20rpx;
opacity: 0.5;
transition: transform 0.3s;
}
/* 价格筛选面板 */
.price-filter-panel {
position: absolute;
top: 120rpx;
left: 24rpx;
right: 24rpx;
padding: 20rpx;
background: rgba(255, 255, 255, 0.98);
border-radius: 20rpx;
overflow: hidden;
max-height: 0;
opacity: 0;
transition: all 0.3s ease-out;
z-index: 100;
box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.12);
pointer-events: none;
&.expanded {
max-height: 500rpx;
opacity: 1;
pointer-events: auto;
}
}
.filter-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16rpx;
margin-bottom: 16rpx;
}
.price-input-group {
flex: 1;
display: flex;
align-items: center;
background: rgba(0, 0, 0, 0.03);
border-radius: 12rpx;
padding: 0 16rpx;
height: 60rpx;
}
.price-input {
flex: 1;
height: 100%;
font-size: 24rpx;
text-align: center;
}
.price-separator {
margin: 0 12rpx;
color: #999;
font-size: 24rpx;
}
.filter-actions {
display: flex;
gap: 12rpx;
}
.filter-btn-reset,
.filter-btn-confirm {
padding: 12rpx 24rpx;
border-radius: 12rpx;
font-size: 24rpx;
white-space: nowrap;
}
.filter-btn-reset {
background: rgba(0, 0, 0, 0.05);
color: #666;
}
.filter-btn-confirm {
background: $gradient-brand;
color: #fff;
}
/* 快捷价格区间 */
.quick-price-ranges {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
}
.price-range-tag {
padding: 10rpx 20rpx;
background: rgba(0, 0, 0, 0.04);
border-radius: 20rpx;
font-size: 22rpx;
color: #666;
transition: all 0.2s;
&.active {
background: $gradient-brand;
color: #fff;
transform: scale(1.05);
}
&:active {
transform: scale(0.95);
}
}
.tab-pill { .tab-pill {
display: flex; display: flex;
background: rgba(0, 0, 0, 0.03); background: rgba(0, 0, 0, 0.03);
@ -638,6 +822,8 @@ onUnmounted(() => {
height: 0; /* 关键:在 flex 布局中需要设置为 0 才能正确计算高度 */ height: 0; /* 关键:在 flex 布局中需要设置为 0 才能正确计算高度 */
background: #fcfcfc; background: #fcfcfc;
overflow: hidden; overflow: hidden;
position: relative;
z-index: 1;
} }
/* 缩放动画的核心部分 */ /* 缩放动画的核心部分 */
@ -691,10 +877,30 @@ onUnmounted(() => {
position: relative; position: relative;
background: #fafafa; background: #fafafa;
overflow: hidden; overflow: hidden;
image { }
.thumb-wrap .product-thumb {
transition: opacity 0.3s; transition: opacity 0.3s;
}
/* 库存标签 */
.stock-tag {
position: absolute;
top: 8rpx;
right: 8rpx;
padding: 4rpx 12rpx;
border-radius: 20rpx;
font-size: 20rpx;
color: #fff;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(4rpx);
-webkit-backdrop-filter: blur(4rpx);
z-index: 2;
&.out {
background: rgba(255, 59, 48, 0.9);
} }
} }
.product-thumb { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .product-thumb { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
.product-info { padding: 16rpx; } .product-info { padding: 16rpx; }
.product-title { .product-title {
@ -722,7 +928,17 @@ onUnmounted(() => {
.c-price { font-size: 20rpx; color: #999; } .c-price { font-size: 20rpx; color: #999; }
.c-btn { background: #333; color: #fff; font-size: 22rpx; padding: 6rpx 20rpx; border-radius: 10rpx; } .c-btn { background: #333; color: #fff; font-size: 22rpx; padding: 6rpx 20rpx; border-radius: 10rpx; }
.empty-mini { padding-top: 100rpx; text-align: center; color: #ccc; font-size: 24rpx; image { width: 160rpx; display: block; margin: 0 auto 10rpx; } } .empty-mini {
padding-top: 100rpx;
text-align: center;
color: #ccc;
font-size: 24rpx;
}
.empty-mini-img {
width: 160rpx;
display: block;
margin: 0 auto 10rpx;
}
.mini-loading { padding-top: 60rpx; display: flex; justify-content: center; } .mini-loading { padding-top: 60rpx; display: flex; justify-content: center; }
.pulse { width: 40rpx; height: 40rpx; background: $brand-primary; border-radius: 50%; opacity: 0.3; animation: pulse 1s infinite; } .pulse { width: 40rpx; height: 40rpx; background: $brand-primary; border-radius: 50%; opacity: 0.3; animation: pulse 1s infinite; }
@keyframes pulse { from { transform: scale(1); opacity: 0.3; } to { transform: scale(2); opacity: 0; } } @keyframes pulse { from { transform: scale(1); opacity: 0.3; } to { transform: scale(2); opacity: 0; } }

View File

@ -1,5 +1,8 @@
import { getUserProfile } from '../api/appUser' import { getUserProfile } from '../api/appUser'
// 标记本次应用运行期间是否已经检查过手机号绑定状态
let hasCheckedPhoneBound = false
/** /**
* 检查用户是否已绑定手机号同步仅检查本地 * 检查用户是否已绑定手机号同步仅检查本地
* @returns {boolean} 是否已绑定手机号 * @returns {boolean} 是否已绑定手机号
@ -23,9 +26,16 @@ export function hasPhoneBound() {
*/ */
export async function checkPhoneBound() { export async function checkPhoneBound() {
try { try {
// 如果本次应用运行期间已经检查过且已绑定,则跳过检查
if (hasCheckedPhoneBound) {
console.log('[checkPhoneBound] 本次应用运行期间已检查过绑定状态,跳过重复检查')
return true
}
// 优先使用同步检查 // 优先使用同步检查
if (hasPhoneBound()) { if (hasPhoneBound()) {
console.log('[checkPhoneBound] 用户已通过手机号登录,跳过绑定检查') console.log('[checkPhoneBound] 用户已通过手机号登录,跳过绑定检查')
hasCheckedPhoneBound = true
return true return true
} }
@ -41,6 +51,7 @@ export async function checkPhoneBound() {
console.log('[checkPhoneBound] 已检测到手机号,允许通过:', mobile) console.log('[checkPhoneBound] 已检测到手机号,允许通过:', mobile)
// 缓存手机号 // 缓存手机号
uni.setStorageSync('phone_number', mobile) uni.setStorageSync('phone_number', mobile)
hasCheckedPhoneBound = true
return true return true
} }
@ -65,6 +76,7 @@ export async function checkPhoneBound() {
console.log('[checkPhoneBound] 降级检查本地缓存:', phoneNumber ? phoneNumber : '未找到') console.log('[checkPhoneBound] 降级检查本地缓存:', phoneNumber ? phoneNumber : '未找到')
if (phoneNumber) { if (phoneNumber) {
hasCheckedPhoneBound = true
return true return true
} }

View File

@ -1,5 +1,5 @@
const BASE_URL = 'http://127.0.0.1:9991' //const BASE_URL = 'http://127.0.0.1:9991'
// const BASE_URL = 'https://kdy.1024tool.vip' const BASE_URL = 'https://kdy.1024tool.vip'
let authModalShown = false let authModalShown = false
function handleAuthExpired() { function handleAuthExpired() {