This commit is contained in:
zpc 2025-06-23 03:38:22 +08:00
parent f4c6b693e0
commit 3d44b8af3f
7 changed files with 383 additions and 232 deletions

View File

@ -28,4 +28,18 @@ export const getProductList = async (pageNo, pageSize, searchKeyword) => {
if (res.status == 1) {
return res.data;
}
}
}
/**
* 获取商品详情
* @param {number} id 商品id
* @returns {Promise} 商品详情
*/
export const getProductDetail = async (id) => {
const res = await HttpRequest.get('/get_product_detail', {
id: id
});
if (res.status == 1) {
return res.data;
}
return null;
}

1
components.d.ts vendored
View File

@ -19,6 +19,7 @@ declare module 'vue' {
PagePopup: typeof import('./components/youdas-container/page-popup.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
TextSearch: typeof import('./components/youdas-container/text-search.vue')['default']
UniNavBar: typeof import('./components/uni-nav-bar/uni-nav-bar.vue')['default']
UniStatusBar: typeof import('./components/uni-nav-bar/uni-status-bar.vue')['default']
}

View File

@ -0,0 +1,99 @@
<template>
<view class="search-container">
<view class="search-box">
<image src="/static/ic_search.png" class="search-icon" mode="aspectFit"></image>
<input class="search-input" type="text" v-model="keyword" :placeholder="placeholder" confirm-type="search" @confirm="handleSearch" />
<view class="search-btn" v-if="keyword.length > 0" @click="clearSearch">
<text class="search-btn-text">×</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, watch } from 'vue';
const props = defineProps({
placeholder: {
type: String,
default: '搜索'
},
modelValue: {
type: String,
default: ''
}
});
const emit = defineEmits(['update:modelValue', 'search', 'clear']);
const keyword = ref(props.modelValue);
// v-model
watch(() => props.modelValue, (newVal) => {
keyword.value = newVal;
});
//
watch(() => keyword.value, (newVal) => {
emit('update:modelValue', newVal);
});
//
const handleSearch = () => {
emit('search', keyword.value);
};
//
const clearSearch = () => {
keyword.value = '';
emit('clear');
};
</script>
<style lang="scss">
.search-container {
width: 100%;
padding: 20rpx 30rpx;
box-sizing: border-box;
background-color: #FFFFFF;
}
.search-box {
width: 100%;
height: 70rpx;
background-color: #F7F7F7;
border-radius: 35rpx;
display: flex;
align-items: center;
padding: 0 25rpx;
box-sizing: border-box;
position: relative;
}
.search-icon {
width: 30rpx;
height: 30rpx;
margin-right: 15rpx;
}
.search-input {
flex: 1;
height: 70rpx;
font-size: 26rpx;
}
.search-btn {
width: 40rpx;
height: 40rpx;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
background-color: #d1d1d1;
}
.search-btn-text {
color: #fff;
font-size: 28rpx;
}
</style>

View File

@ -10,8 +10,10 @@
</template>
<z-paging ref="paging" show-refresher-update-time loading-more-no-more-text="我也是有底线的" v-model="dataList"
@query="queryList" :fixed="false" :show-loading-more-when-reload="true">
<!-- 搜索 -->
<text-search v-model="searchKeyword" placeholder="搜索商品" @search="handleSearch" @clear="clearSearch" />
<!-- 顶部三个商品 -->
<swiper :display-multiple-items="3" class="top" :duration="500">
<swiper v-if="!isSearch" :display-multiple-items="3" class="top" :duration="500">
<swiper-item v-for="(item, i) in topDataList" :key="i" @click="goToDetail(item)">
<view class="item">
<view class="img">
@ -83,12 +85,14 @@
import { ref } from 'vue';
const paging = ref(null);
import { getProductList, getBannerList } from '@/common/server/product';
import TextSearch from '@/components/youdas-container/text-search.vue';
//
const topDataList = ref([
]);
let isSearch = ref(false);
const dataList = ref([]);
const searchKeyword = ref(''); //
//
const goToDetail = (item) => {
@ -117,12 +121,32 @@ const buyProduct = (item, event) => {
icon: 'success'
});
};
//
const handleSearch = () => {
if (searchKeyword.value == '') {
isSearch.value = false;
} else {
isSearch.value = true;
}
paging.value.reload();
};
//
const clearSearch = () => {
searchKeyword.value = '';
isSearch.value = false;
paging.value.reload();
};
const queryList = (pageNo, pageSize) => {
getProductList(pageNo, pageSize, "").then(res => {
getProductList(pageNo, pageSize, searchKeyword.value).then(res => {
dataList.value.concat(res || []);
paging.value.complete(res || false);
});
};
onLoad(async () => {
const bannerList = await getBannerList();
topDataList.value = bannerList;

View File

@ -1,229 +1,263 @@
<template>
<view class="" style="width: 100%; height: 715.65rpx; background-color: #D8D8D8;">
<z-paging ref="paging" refresher-only @onRefresh="onRefresh">
</view>
<!-- 返回 -->
<image src="/static/ic_back.png" @click="toBack()"
style="width: 41.98rpx; height: 41.98rpx; position: fixed; left: 30rpx; top: 137rpx;z-index: 1000;" mode="">
</image>
<swiper v-if="productDetail" :duration="500" circular :autoplay="true" :interval="3000"
style="width: 100%; height: 715.65rpx; background-color: #D8D8D8;">
<swiper-item v-for="(item, i) in productDetail.detail_image" :key="i">
<image :src="item" style="width: 100%; height: 100%;"></image>
</swiper-item>
</swiper>
<view v-if="productDetail" class=""
style="width: 100%; height: 244.27rpx; background-color: white; position: relative;">
<!-- 返回 -->
<image src="/static/ic_back.png" @click="toBack()"
style="width: 41.98rpx; height: 41.98rpx; position: fixed; left: 30rpx; top: 137rpx;" mode=""></image>
<view class="item-price">
<text class="price-symbol-small"></text>
<text class="price-value-small">{{ productDetail.price }}</text>
</view>
<view class="" style="position: absolute; left: 30.53rpx; top: 95.42rpx; font-size: 26.72rpx;">
{{ productDetail.title }}
</view>
<view class="" style="width: 100%; height: 244.27rpx; background-color: white; position: relative;">
<view class="item-price">
<text class="price-symbol-small"></text>
<text class="price-value-small">69</text>
</view>
<view class="" style="position: absolute; left: 30.53rpx; top: 95.42rpx; font-size: 26.72rpx;">
英雄联盟K/DA系列
</view>
<view class="" style="position: absolute; left: 30.53rpx; bottom: 21rpx; font-size: 22.9rpx;">
商品详情
</view>
<view class="product-count">
<text class="count-text">item.num/1</text>
<image src="/static/ic_box.png" class="box-icon" mode="aspectFit"></image>
</view>
</view>
<!-- 详情列表 -->
<view class="" v-for="(item, index) in detailList"
style="width: 100%; height: 666.03rpx; background-color: #D8D8D8;">
{{ index }}
</view>
<view class="" style="width: 100%; height: 188.94rpx; position: fixed; bottom: 0; background-color: white;">
<view class="" @click="bayOpen()"
style="width: 654.58rpx; height: 80.15rpx; margin: 33rpx auto; background-color: #333333; display: flex; align-items: center; justify-content: center; border-radius: 15.27rpx;">
<text style="color: #CDEF27; font-size: 22.9rpx;">立即购买</text>
</view>
</view>
<uni-popup ref="bayPop" type="bottom">
<view
style="width: 100%; height: 965.65rpx; background-color: #F7F7F7; border-radius: 15.27rpx 15.27rpx 0rpx 0rpx;">
<view class="" style="width: 688.93rpx; height: 100%; margin: 0 auto;">
<view class=""
style="width: 100%; height: 30rpx; display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding-top: 44rpx;">
<view class="" style="width: 24.81rpx; height: 24.81rpx;"></view>
<text style="font-size: 26.72rpx;">确认订单</text>
<image src="/static/ic_close.png" style="width: 24.81rpx; height: 24.81rpx;" @click="closePop"
mode=""></image>
</view>
<view class=""
style="width: 100%; height: 227.1rpx; background-color: white; position: relative; margin-top: 42rpx; border-radius: 15.27rpx;">
<image src=""
style="width: 180.12rpx; height: 180.12rpx; background-color: #D8D8D8; border-radius: 15.27rpx; position: absolute; left: 24.81rpx; top: 24.81rpx;"
mode=""></image>
<text
style="font-size: 22.9rpx; position: absolute; left: 238.55rpx; top: 41.98rpx;">新人福利第6弹-小米音箱</text>
<text
style="font-size: 19.08rpx; position: absolute; left: 238.55rpx; top: 91.6rpx; color: #999999;">类型明信片</text>
<view class=""
style="position: absolute; display: flex; flex-direction: row; left: 238.55rpx; bottom: 44rpx; align-items: center;">
<text class=" " style="font-size: 19.08rpx; margin-top: 15rpx; color: #FF6A6A;"></text>
<text class=" " style="font-size: 41.98rpx; font-weight: bold; color: #FF6A6A;">69</text>
</view>
<text
style="position: absolute; right: 24.81rpx; bottom: 41.98rpx; font-size: 19.08rpx; color: #999999;">x1</text>
</view>
<view class="" @click="toAddress()"
style="width: 100%; height: 82.06rpx; background-color: white; border-radius: 15.27rpx; margin-top: 28.63rpx; display: flex; align-items: center; position: relative;">
<text style="left: 24.81rpx; font-size: 22.9rpx; position: absolute;">地址管理</text>
<image src="/static/ic_arrow.png" class="arrow-icon"></image>
</view>
<view class=""
style="width: 100%; height: 82.06rpx; background-color: white; border-radius: 15.27rpx; margin-top: 28.63rpx; display: flex; align-items: center; position: relative;">
<text style="left: 24.81rpx; font-size: 22.9rpx; position: absolute;">优惠券</text>
<text style="right: 45.8rpx; font-size: 19.08rpx; position: absolute; color: #999999;">未选择</text>
<image src="/static/ic_arrow.png" class="arrow-icon"></image>
</view>
<view class=""
style="width: 100%; height: 82.06rpx; background-color: #F9F8E1; border-radius: 15.27rpx; margin-top: 28.63rpx; display: flex; align-items: center; position: relative;">
<text style="left: 24.81rpx; font-size: 22.9rpx; position: absolute;">售前·售后须知</text>
<image src="/static/ic_arrow.png" class="arrow-icon"></image>
</view>
<view class=""
style="width: 100%; display: flex; flex-direction: row; align-items: center; justify-content: center; margin-top: 45.8rpx;">
<image :src="isCheck?'/static/ic_check_s.png':'/static/ic_check.png'" @click="setCheck()"
style="width: 24.02rpx; height: 24.02rpx;" mode=""></image>
<text
style="font-size: 19.08rpx; color: #999999; margin-left: 11.45rpx;">我已满18岁阅读并同意用户协议隐私政策</text>
</view>
<view class=""
style="width: 100%; height: 83.97rpx; background-color: #333333; border-radius: 15.27rpx; display: flex; align-items: center; justify-content: center; margin-top: 30.53rpx;">
<text style="font-size: 22.9rpx; color: #CDEF27;">确认支付</text>
</view>
<view class="" style="position: absolute; left: 30.53rpx; bottom: 21rpx; font-size: 22.9rpx;">
商品详情
</view>
<view class="product-count">
<text class="count-text">{{ productDetail.sales }}/{{ productDetail.stock }}</text>
<image src="/static/ic_box.png" class="box-icon" mode="aspectFit"></image>
</view>
</view>
</uni-popup>
<!-- 详情列表 -->
<view v-if="productDetail" class="" style="width: 100%;">
<rich-text :nodes="productDetail.detail_html"></rich-text>
</view>
<view class="" style="width: 100%; height: 188.94rpx; position: fixed; bottom: 0; background-color: white;">
<view class="" @click="bayOpen"
style="width: 654.58rpx; height: 80.15rpx; margin: 33rpx auto; background-color: #333333; display: flex; align-items: center; justify-content: center; border-radius: 15.27rpx;">
<text style="color: #CDEF27; font-size: 22.9rpx;">立即购买</text>
</view>
</view>
<uni-popup ref="bayPop" type="bottom">
<view
style="width: 100%; height: 965.65rpx; background-color: #F7F7F7; border-radius: 15.27rpx 15.27rpx 0rpx 0rpx;">
<view class="" style="width: 688.93rpx; height: 100%; margin: 0 auto;">
<view class=""
style="width: 100%; height: 30rpx; display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding-top: 44rpx;">
<view class="" style="width: 24.81rpx; height: 24.81rpx;"></view>
<text style="font-size: 26.72rpx;">确认订单</text>
<image src="/static/ic_close.png" style="width: 24.81rpx; height: 24.81rpx;" @click="closePop"
mode="">
</image>
</view>
<view class=""
style="width: 100%; height: 227.1rpx; background-color: white; position: relative; margin-top: 42rpx; border-radius: 15.27rpx;">
<image :src="productDetail.image"
style="width: 180.12rpx; height: 180.12rpx; background-color: #D8D8D8; border-radius: 15.27rpx; position: absolute; left: 24.81rpx; top: 24.81rpx;"
mode=""></image>
<text
style="font-size: 22.9rpx; position: absolute; left: 238.55rpx; top: 41.98rpx;">{{ productDetail.title }}</text>
<text
style="font-size: 19.08rpx; position: absolute; left: 238.55rpx; top: 91.6rpx; color: #999999;">类型明信片</text>
<view class=""
style="position: absolute; display: flex; flex-direction: row; left: 238.55rpx; bottom: 44rpx; align-items: center;">
<text class=" " style="font-size: 19.08rpx; margin-top: 15rpx; color: #FF6A6A;"></text>
<text class=" "
style="font-size: 41.98rpx; font-weight: bold; color: #FF6A6A;">{{ productDetail.price }}</text>
</view>
<text
style="position: absolute; right: 24.81rpx; bottom: 41.98rpx; font-size: 19.08rpx; color: #999999;">x1</text>
</view>
<view class="" @click="toAddress"
style="width: 100%; height: 82.06rpx; background-color: white; border-radius: 15.27rpx; margin-top: 28.63rpx; display: flex; align-items: center; position: relative;">
<text style="left: 24.81rpx; font-size: 22.9rpx; position: absolute;">地址管理</text>
<image src="/static/ic_arrow.png" class="arrow-icon"></image>
</view>
<view class=""
style="width: 100%; height: 82.06rpx; background-color: white; border-radius: 15.27rpx; margin-top: 28.63rpx; display: flex; align-items: center; position: relative;">
<text style="left: 24.81rpx; font-size: 22.9rpx; position: absolute;">优惠券</text>
<text
style="right: 45.8rpx; font-size: 19.08rpx; position: absolute; color: #999999;">未选择</text>
<image src="/static/ic_arrow.png" class="arrow-icon"></image>
</view>
<view class=""
style="width: 100%; height: 82.06rpx; background-color: #F9F8E1; border-radius: 15.27rpx; margin-top: 28.63rpx; display: flex; align-items: center; position: relative;">
<text style="left: 24.81rpx; font-size: 22.9rpx; position: absolute;">售前·售后须知</text>
<image src="/static/ic_arrow.png" class="arrow-icon"></image>
</view>
<view class=""
style="width: 100%; display: flex; flex-direction: row; align-items: center; justify-content: left; margin-top: 45.8rpx;">
<image :src="isCheck ? '/static/ic_check_s.png' : '/static/ic_check.png'" @click="setCheck"
style="width: 24.02rpx; height: 24.02rpx;" mode=""></image>
<text
style="font-size: 19.08rpx; color: #999999; margin-left: 11.45rpx;">我已满18岁阅读并同意
<text style="color: #333333;">用户协议</text>
<text style="color: #333333;">隐私政策</text>
</text>
</view>
<view class=""
style="width: 100%; height: 83.97rpx; background-color: #333333; border-radius: 15.27rpx; display: flex; align-items: center; justify-content: center; margin-top: 30.53rpx;">
<text style="font-size: 22.9rpx; color: #CDEF27;">确认支付</text>
</view>
</view>
</view>
</uni-popup>
</z-paging>
</template>
<script>
export default {
data() {
return {
detailList: [1, 2, 3, 4, 5],
isCheck: true,
}
},
methods: {
toBack() {
console.log("777777");
uni.navigateBack();
},
bayOpen() {
this.$refs.bayPop.open();
},
closePop() {
this.$refs.bayPop.close();
},
setCheck() {
this.isCheck = !this.isCheck;
},
toAddress(){
uni.navigateTo({
url: '/pages/mall/receiving-address'
});
}
}
<script setup>
import { ref } from 'vue';
import { getProductDetail } from '@/common/server/product';
let productDetail = ref(null);
let paging = ref(null);
let productId = null;
onLoad(async (options) => {
const id = options.id;
productId = id;
queryProductDetail();
});
const onRefresh = () => {
console.log("onRefresh");
queryProductDetail().then(() => {
paging.value.complete();
});
};
const queryProductDetail = async () => {
const res = await getProductDetail(productId);
if (res) {
productDetail.value = res;
}
};
//
const detailList = ref([1, 2, 3, 4, 5]);
const isCheck = ref(true);
const bayPop = ref(null);
//
const toBack = () => {
console.log("777777");
uni.navigateBack();
};
const bayOpen = () => {
bayPop.value.open();
};
const closePop = () => {
bayPop.value.close();
};
const setCheck = () => {
isCheck.value = !isCheck.value;
};
const toAddress = () => {
uni.navigateTo({
url: '/pages/mall/receiving-address'
});
};
</script>
<style lang="scss">
.item-price {
display: flex;
flex-direction: row;
align-items: center;
position: absolute;
left: 30.53rpx;
top: 26.72rpx;
.item-price {
display: flex;
flex-direction: row;
align-items: center;
position: absolute;
left: 30.53rpx;
top: 26.72rpx;
}
.price-symbol-small {
font-size: 19.08rpx;
margin-top: 10rpx;
color: #FF6A6A;
}
.price-value-small {
font-size: 41.98rpx;
font-weight: bold;
color: #FF6A6A;
}
.product-count {
position: absolute;
display: flex;
flex-direction: row;
right: 30.53rpx;
top: 40rpx;
align-items: center;
.count-text {
font-size: 15.27rpx;
color: #6C6C6C;
}
.price-symbol-small {
font-size: 19.08rpx;
margin-top: 10rpx;
color: #FF6A6A;
.box-icon {
width: 17.39rpx;
height: 17.39rpx;
margin-left: 7rpx;
}
}
.price-value-small {
font-size: 41.98rpx;
font-weight: bold;
color: #FF6A6A;
}
.arrow-icon {
width: 10.67rpx;
height: 19.66rpx;
right: 25rpx;
position: absolute;
}
.product-count {
position: absolute;
display: flex;
flex-direction: row;
right: 30.53rpx;
top: 40rpx;
align-items: center;
.count-text {
font-size: 15.27rpx;
color: #6C6C6C;
}
.box-icon {
width: 17.39rpx;
height: 17.39rpx;
margin-left: 7rpx;
}
}
.arrow-icon {
width: 10.67rpx;
height: 19.66rpx;
right: 25rpx;
position: absolute;
}
//
::v-deep rich-text img {
max-width: 95% !important;
border-radius: 10rpx;
height: auto !important;
display: block !important;
margin: 0 auto;
}
</style>

View File

@ -17,13 +17,8 @@
:currentIndex="current">
<template #search>
<view class="search-container">
<view class="search">
<text class="iconfont icon-search" style="margin-right: 10rpx; color: #999;"></text>
<input type="text" v-model="searchKeywords[index]" placeholder="搜索"
confirm-type="search" @confirm="() => onSearch(index)" />
<text class="iconfont icon-close" v-if="searchKeywords[index]"
@click="() => clearSearch(index)" style="margin-left: 10rpx; color: #999;"></text>
</view>
<text-search v-model="searchKeywords[index]" placeholder="搜索"
@search="() => onSearch(index)" @clear="() => clearSearch(index)" />
</view>
</template>
</news-list-item>
@ -34,6 +29,7 @@
<script setup>
import newsListItem from '@/components/youdas-container/news-list-item.vue'
import TextSearch from '@/components/youdas-container/text-search.vue'
import { getFeaturedNewsList, getHotNewsList, getFollowNewsList } from '@/common/server/news'
import { navigateTo } from '@/common/system/router'
//
@ -98,23 +94,6 @@ const clearSearch = (index) => {
box-sizing: border-box;
}
.search {
height: 70rpx;
background-color: #F7F7F7;
border-radius: 35rpx;
padding: 0 20rpx;
display: flex;
align-items: center;
width: 100%;
box-sizing: border-box;
input {
flex: 1;
height: 100%;
font-size: 28rpx;
}
}
.tab {
width: 100%;
display: flex;

BIN
static/ic_search.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B