Browse Source

feat: page-category;

pull/1/head
Fox-33 2 weeks ago
parent
commit
5810babd23
23 changed files with 1071 additions and 83 deletions
  1. +1
    -1
      common.scss
  2. +11
    -3
      components/base/tabbar.vue
  3. +50
    -0
      components/home/bgSwiperView.vue
  4. +45
    -3
      components/home/pictureLiveView.vue
  5. +1
    -1
      components/home/productView.vue
  6. +84
    -1
      components/product/productCard.vue
  7. +107
    -0
      components/product/sortBar.vue
  8. +6
    -0
      pages.json
  9. +752
    -0
      pages/index/category.vue
  10. +7
    -3
      pages/index/index.vue
  11. +7
    -71
      pages_order/product/search.vue
  12. BIN
      static/image/icon-arrow-down.png
  13. BIN
      static/image/icon-arrow-up-light.png
  14. BIN
      static/image/icon-arrow-up.png
  15. BIN
      static/image/icon-degree.png
  16. BIN
      static/image/icon-nav.png
  17. BIN
      static/image/icon-pdf.png
  18. BIN
      static/image/icon-word.png
  19. BIN
      static/image/logo.png
  20. BIN
      static/image/temp-01.png
  21. BIN
      static/image/temp-02.png
  22. BIN
      static/image/temp-03.png
  23. BIN
      static/image/temp-04.png

+ 1
- 1
common.scss View File

@ -54,7 +54,7 @@
width: 100vw;
min-height: 100vh;
color: #181818;
background-color: $uni-bg-color-grey;
background: linear-gradient(#DAF3FF, #F4F4F4 200rpx, #F4F4F4);
position: relative;
font-family: PingFang SC;
font-weight: 400;


+ 11
- 3
components/base/tabbar.vue View File

@ -28,13 +28,21 @@
},
data() {
return {
list: [{
list: [
{
"selectedIconPath": "/static/image/tabbar/home-active.png",
"iconPath": "/static/image/tabbar/home.png",
"pagePath": "/pages/index/index",
"title": "首页",
key: 'home',
},
{
"selectedIconPath": "/static/image/tabbar/category-active.png",
"iconPath": "/static/image/tabbar/category.png",
"pagePath": "/pages/index/category",
"title": "分类",
key: 'category',
},
]
};
},
@ -69,7 +77,7 @@
z-index: 999999;
bottom: 0;
left: 0;
color: #BCBCBC;
color: #999999;
.tabbar-item {
flex: 1;
@ -99,7 +107,7 @@
}
.tabbar-active {
color: $uni-color !important;
color: #181818 !important;
}
}
}

+ 50
- 0
components/home/bgSwiperView.vue View File

@ -0,0 +1,50 @@
<template>
<view class="swiper">
<uv-swiper
:list="bannerList" keyName="image"
indicator
indicatorMode="dot"
indicatorInactiveColor="rgba(255, 255, 255, 0.7)"
height="382rpx"
></uv-swiper>
</view>
</template>
<script>
export default {
data() {
return {
bannerList: [],
}
},
created() {
this.getData()
},
methods: {
getData() {
// todo: fetch
this.bannerList = [
{
image: '/static/image/bg.png',
},
{
image: '/static/image/bg.png',
},
{
image: '/static/image/bg.png',
},
]
},
},
}
</script>
<style scoped lang="scss">
.swiper {
border-radius: 40rpx;
overflow: hidden;
}
</style>

+ 45
- 3
components/home/pictureLiveView.vue View File

@ -2,13 +2,13 @@
<view class="live">
<view class="flex live-header">
<view>图片直播</view>
<button class="flex btn">
<button class="flex btn" @click="showAll">
<view>查看全部</view>
<image class="img" src="@/static/image/icon-arrow-right.png" mode="widthFix"></image>
</button>
</view>
<!-- todo: auto scroll -->
<view class="live-content">
<!-- <view class="live-content">
<view class="live-item" v-for="item in liveList" :key="item.id">
<image class="live-item-bg" :src="item.image" mode="aspectFill"></image>
<view class="live-item-info">
@ -16,7 +16,25 @@
<view class="live-item-info-time">{{ item.time }}</view>
</view>
</view>
</view>
</view> -->
<swiper
class="swiper"
:current="current"
:autoplay="true"
:display-multiple-items="3.2"
>
<swiper-item v-for="item in liveList" :key="item.id" style="display: inline-block;">
<view class="swiper-item">
<view class="swiper-item-content" @click="jumpToLive(item.id)">
<image class="live-item-bg" :src="item.image" mode="aspectFill"></image>
<view class="live-item-info">
<view class="text-ellipsis live-item-info-title">{{ item.title }}</view>
<view class="live-item-info-time">{{ item.time }}</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
</template>
@ -75,6 +93,12 @@
]
},
jumpToLive(id) {
// todo
},
showAll() {
// todo
}
},
}
</script>
@ -154,4 +178,22 @@
}
}
}
.swiper {
width: 100%;
height: 240rpx;
&-item {
width: 180rpx;
height: 240rpx;
&-content {
position: relative;
width: 100%;
height: 100%;
border-radius: 12rpx;
overflow: hidden;
}
}
}
</style>

+ 1
- 1
components/home/productView.vue View File

@ -43,7 +43,7 @@
</template>
<script>
import productCard from '@/components/home/productCard.vue'
import productCard from '@/components/product/productCard.vue'
export default {
components: {


components/home/productCard.vue → components/product/productCard.vue View File

@ -1,5 +1,5 @@
<template>
<view class="product">
<view class="product" @touchstart="onTouchstart" @touchmove="onTouchmove" @touchend="onTouchend">
<image class="product-img" :src="data.image" mode="aspectFill"></image>
<view class="flex flex-column product-info">
<view class="product-info-top">
@ -25,6 +25,14 @@
<button class="btn">报名</button>
</view>
</view>
<button class="flex btn-collect"
:style="collectBtnStyle"
@click.stop="onCollect"
@touchstart.stop="() => {}"
>
<view>收藏</view>
</button>
</view>
</template>
@ -38,6 +46,13 @@
}
}
},
data() {
return {
isMove: false,
startClientX: null,
displayX: 0,
}
},
computed: {
priceInt() {
return parseInt(this.data.currentPrice)
@ -45,12 +60,66 @@
priceFrac() {
return (this.data.currentPrice % this.priceInt).toFixed(2).slice(1)
},
collectBtnStyle() {
let display = Math.ceil(this.displayX / 56 * 100)
display > 100 && (display = 100)
const translateX = 100 - display
return `transform: translateX(${translateX}%);`
}
},
methods: {
onTouchstart(e) {
const clientX = e.changedTouches[0].clientX
this.isMove = false
this.startClientX = clientX
this.displayX = 0
},
onTouchmove(e) {
const clientX = e.changedTouches[0].clientX
if (clientX < this.startClientX) {
this.displayX = this.startClientX - clientX
} else {
this.displayX = 0
}
this.isMove = true
},
onTouchend() {
if (this.displayX < 100) {
this.displayX = 0
}
this.isMove = false
},
showCollectBtn() {
this.displayX = 100
},
hiddenCollectBtn() {
this.displayX = 0
},
onCollect() {
console.log('onCollect')
// todo: fetch collect
uni.showToast({
icon: 'success',
title: '已收藏',
});
this.hiddenCollectBtn()
}
},
}
</script>
<style scoped lang="scss">
.product {
position: relative;
height: 464rpx;
background: #FFFFFF;
border: 2rpx solid #FFFFFF;
@ -134,4 +203,18 @@
}
}
.btn-collect {
position: absolute;
top: 0;
right: 0;
row-gap: 8rpx;
width: 112rpx;
height: 100%;
font-size: 24rpx;
line-height: 1;
color: #FFFFFF;
background: #FF9035;
}
</style>

+ 107
- 0
components/product/sortBar.vue View File

@ -0,0 +1,107 @@
<template>
<view class="flex sort" :style="style">
<view :class="['flex', 'sort-item', sort == 'comprehensive' ? 'is-active' : '']" @click="onClickSort('comprehensive')">综合</view>
<view :class="['flex', 'sort-item', ['sale-asc', 'sale-desc'].includes(sort) ? 'is-active' : '']" @click="onClickSort('sale')">
<view>销量</view>
<view class="sort-item-icon">
<uv-icon v-if="sort == 'sale-asc'" name="arrow-up-fill" color="#00A9FF" size="16rpx" :bold="true"></uv-icon>
<uv-icon v-else-if="sort == 'sale-desc'" name="arrow-down-fill" color="#00A9FF" size="16rpx" :bold="true"></uv-icon>
<image v-else style="width: 8rpx; height: auto;" src="/static/image/icon-sort.png" mode="widthFix"></image>
</view>
</view>
<view :class="['flex', 'sort-item', ['price-asc', 'price-desc'].includes(sort) ? 'is-active' : '']" @click="onClickSort('price')">
<view>价格</view>
<view class="sort-item-icon">
<uv-icon v-if="sort == 'price-asc'" name="arrow-up-fill" color="#00A9FF" size="16rpx" :bold="true"></uv-icon>
<uv-icon v-else-if="sort == 'price-desc'" name="arrow-down-fill" color="#00A9FF" size="16rpx" :bold="true"></uv-icon>
<image v-else style="width: 8rpx; height: auto;" src="/static/image/icon-sort.png" mode="widthFix"></image>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
value: {
type: String,
default: 'comprehensive'
},
style: {
type: String,
default: ''
}
},
computed: {
sort: {
set(val) {
this.$emit('input', val)
},
get() {
return this.value
}
}
},
methods: {
onClickSort(key) {
let sort = 'comprehensive'
switch(key) {
case 'comprehensive':
sort = 'comprehensive'
break;
case 'sale':
if (this.sort == 'sale-desc') {
sort = 'sale-asc'
} else {
sort = 'sale-desc'
}
break;
case 'price':
if (this.sort == 'price-desc') {
sort = 'price-asc'
} else {
sort = 'price-desc'
}
break;
default:
break;
}
this.sort = sort
this.$emit('change', sort)
},
},
}
</script>
<style scoped lang="scss">
.sort {
width: 100%;
justify-content: space-between;
&-item {
padding: 12rpx 32rpx;
font-size: 28rpx;
line-height: 1.5;
color: #191919;
column-gap: 4rpx;
&.is-active {
font-weight: 600;
color: #00A9FF;
}
&-icon {
width: 32rpx;
display: inline-flex;
align-items: center;
justify-content: center;
}
}
}
</style>

+ 6
- 0
pages.json View File

@ -5,6 +5,12 @@
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/index/category",
"style": {
"navigationBarTitleText": ""
}
}
],
"preloadRule": {


+ 752
- 0
pages/index/category.vue View File

@ -0,0 +1,752 @@
<template>
<view class="page__view">
<view class="header">
<view class="filter">
<view class="filter-header">
<view class="bar">
<view>
<button v-if="isFold" class="btn is-fold" @click="isFold = false">
<text>筛选</text>
<image class="btn-icon" src="@/static/image/icon-arrow-down.png" mode="widthFix"></image>
</button>
<button v-else class="btn" @click="isFold = true">
<text>筛选</text>
<image class="btn-icon" src="@/static/image/icon-arrow-up-light.png" mode="widthFix"></image>
</button>
</view>
<view class="title">分类</view>
</view>
</view>
<view v-if="!isFold" class="filter-content">
<view class="filter-item" v-for="filter in filters" :key="filter.id">
<view class="filter-item-label">{{ `${filter.label}` }}</view>
<view class="filter-item-content">
<template v-if="filter.key === 'price'">
<view class="flex range price">
<view class="range-item">
<uv-input
v-model="startPrice"
type="number"
inputAlign="center"
placeholder="开始价格"
placeholderStyle="color: #181818; font-size: 28rpx; font-weight: 400;"
:customStyle="{
backgroundColor: 'transparent',
padding: '0',
boxSizing: 'border-box',
fontSize: '28rpx',
border: 'none',
}"
fontSize="28rpx"
:clearable="true"
></uv-input>
</view>
<view class="split"></view>
<view class="range-item">
<uv-input
v-model="endPrice"
type="number"
inputAlign="center"
placeholder="结束价格"
placeholderStyle="color: #181818; font-size: 28rpx; font-weight: 400;"
:customStyle="{
backgroundColor: 'transparent',
padding: '0',
boxSizing: 'border-box',
fontSize: '28rpx',
border: 'none',
}"
fontSize="28rpx"
:clearable="true"
></uv-input>
</view>
</view>
</template>
<template v-else-if="filter.key === 'time'">
<view class="flex range time">
<view class="range-item" @click="openStartDatePicker">
{{ startDate ? $dayjs(startDate).format('YYYY-MM-DD') : '开始日期' }}
<button v-if="startDate" class="btn btn-clear" @click.stop="onClearStartDate">
<uv-icon name="close-circle" color="#B5B5B5" size="28rpx"></uv-icon>
</button>
</view>
<view class="split"></view>
<view class="range-item" @click="openEndDatePicker">
{{ endDate ? $dayjs(endDate).format('YYYY-MM-DD') : '结束日期' }}
<button v-if="endDate" class="btn btn-clear" @click.stop="onClearEndDate">
<uv-icon name="close-circle" color="#B5B5B5" size="28rpx"></uv-icon>
</button>
</view>
</view>
<uv-datetime-picker
ref="startDatePicker"
v-model="startDate"
mode="date"
title="开始日期"
confirmColor="#00A9FF"
round="32rpx"
:minDate="minTime"
@confirm="onStartDateChange"
></uv-datetime-picker>
<uv-datetime-picker
ref="endDatePicker"
v-model="endDate"
mode="date"
title="结束日期"
confirmColor="#00A9FF"
round="32rpx"
:minDate="startDate || minTime"
@confirm="onEndDateChange"
></uv-datetime-picker>
</template>
<template v-else>
<view class="option">
<view
v-for="option in filter.options"
:key="option.id"
:class="['option-item', option.id == queryParams[filter.key] ? 'is-active' : '']"
@click="onClickFilter(filter.key, option.id)"
>
{{ option.label }}
</view>
</view>
</template>
</view>
</view>
<button class="flex btn btn-fold" @click="isFold = false">
<image class="btn-icon" src="@/static/image/icon-arrow-up.png" mode="widthFix"></image>
</button>
</view>
</view>
<view class="sort">
<sortBar></sortBar>
</view>
</view>
<!-- 分类商品列表 -->
<view class="main" >
<uv-vtabs
:list="categoryList"
keyName="name"
:current="current"
:chain="true"
@change="change"
barWidth="177rpx"
barBgColor="#F5F5F5"
:barItemStyle="{
color: '#1D2129',
fontSize: '28rpx',
fontWeight: 400,
}"
:barItemActiveStyle="{
color: '#00A9FF',
fontWeight: 600,
backgroundColor: '#FFFFFF',
}"
:barItemActiveLineStyle="{
background: '#00A9FF',
margin: '48rpx 4rpx',
borderRadius: '4rpx',
}"
>
<uv-vtabs-item v-for="(item, index) in categoryList" :index="index" :key="item.id">
<template v-if="item.children.length">
<view class="card" v-for="product in item.children" :key="product.id" >
<productCard :data="product" ></productCard>
</view>
</template>
<template v-else>
<uv-empty text="还没有呢"/>
</template>
</uv-vtabs-item>
</uv-vtabs>
</view>
<!-- tabbar -->
<tabber select="category" />
</view>
</template>
<script>
// import mixinsList from '@/mixins/list.js'
import { mapState } from 'vuex'
import tabber from '@/components/base/tabbar.vue'
import sortBar from '@/components/product/sortBar.vue'
import productCard from '@/components/product/productCard.vue'
export default {
// mixins: [mixinsList],
components: {
sortBar,
productCard,
tabber,
},
data() {
return {
current : 0,
startPrice: null,
endPrice: null,
startDate: null,
endDate: null,
minTime: new Date().getTime(),
queryParams: {
pageNo: 1,
pageSize: 1000,
},
categoryList: [],
filters: [],
isFold: true,
}
},
onShow() {
},
async onLoad({ categoryId }) {
await Promise.allSettled([this.fetchCategoryList(), this.fetchFilters()])
console.log('categoryList', this.categoryList)
console.log('filters', this.filters)
await this.initList()
if(this.categoryList.length > 0 && categoryId){
this.current = this.categoryList.findIndex(item => item.id === categoryId)
}
},
methods: {
async fetchCategoryList() {
this.categoryList = [
{
id: '001',
name: '国际游',
children: [],
},
{
id: '002',
name: '夏令营',
children: [],
},
{
id: '003',
name: '周末营',
children: [],
},
{
id: '004',
name: '周边游',
children: [],
},
{
id: '005',
name: '定制游',
children: [],
},
{
id: '006',
name: '周末活动',
children: [],
},
{
id: '007',
name: '亲子活动',
children: [],
},
{
id: '008',
name: '社会实践',
children: [],
},
{
id: '009',
name: '夏令营',
children: [],
},
{
id: '010',
name: '周末活动',
children: [],
},
]
return
try {
this.categoryList = (await this.$fetch('getCategoryList', { pageSize: 1000 }))?.records?.map(item => ({ id: item.id, name: item.name, children: [] }))
} catch(err) {
this.categoryList = []
}
},
async fetchFilters() {
this.filters = [
{
id: '001',
key: 'frontier',
label: '国境',
options: [
{
id: '00101',
label: '国内',
},
{
id: '00102',
label: '国外',
},
],
},
{
id: '002',
key: 'target',
label: '目的地',
options: [
{
label: '全部',
},
{
id: '00201',
label: '上海',
},
{
id: '00202',
label: '北京',
},
{
id: '00203',
label: '浙江省',
},
{
id: '00204',
label: '广东省',
},
{
id: '00205',
label: '广西省',
},
{
id: '00206',
label: '云南省',
},
],
},
{
id: '003',
key: 'age',
label: '适合年龄',
options: [
{
label: '全部',
},
{
id: '00301',
label: '6-10岁',
},
{
id: '00302',
label: '11-14岁',
},
{
id: '00303',
label: '15-16岁',
},
{
id: '00304',
label: '17-18岁',
},
],
},
{
id: '004',
key: 'long',
label: '活动时长',
options: [
{
label: '全部',
},
{
id: '00401',
label: '1日',
},
{
id: '00402',
label: '多日',
},
{
id: '00403',
label: '寒假',
},
{
id: '00404',
label: '暑假',
},
],
},
{
id: '005',
key: 'price',
label: '价格区间',
},
{
id: '006',
key: 'time',
label: '出发日期',
},
]
this.filters.forEach(item => {
const { key, options } = item
if (!options?.length || !options[0]?.id) {
return
}
this.queryParams[key] = options[0].id
})
// todo: fetch
},
async queryProductList(categoryId) {
return [
{
id: '001',
image: '/static/image/temp-20.png',
name: '新疆天山行7/9日丨醉美伊犁&吐鲁番双套餐',
desc: '国内游·7-9天·12岁+',
currentPrice: 688.99,
originalPrice: 1200,
registered: 4168,
},
{
id: '002',
image: '/static/image/temp-24.png',
name: '坝上双草原6日|乌兰布统+锡林郭勒+长城',
desc: '国内游·7-9天·12岁+',
currentPrice: 688.99,
originalPrice: 1200,
registered: 4168,
},
{
id: '003',
image: '/static/image/temp-25.png',
name: '牛湖线探秘 | 清远牛湖线徒步,探秘天坑与大草原',
desc: '国内游·7-9天·12岁+',
currentPrice: 688.99,
originalPrice: 1200,
registered: 4168,
},
{
id: '004',
image: '/static/image/temp-26.png',
name: '低海拔藏区草原,汉藏文化大穿越',
desc: '国内游·7-9天·12岁+',
currentPrice: 688.99,
originalPrice: 1200,
registered: 4168,
},
{
id: '005',
image: '/static/image/temp-27.png',
name: '新丝路到敦煌7日 | 甘青轻松穿越,沙漠+草原',
desc: '国内游·7-9天·12岁+',
currentPrice: 688.99,
originalPrice: 1200,
registered: 4168,
},
{
id: '006',
image: '/static/image/temp-28.png',
name: '呼伦贝尔6/8日|经典or环线双套餐可选',
desc: '国内游·7-9天·12岁+',
currentPrice: 688.99,
originalPrice: 1200,
registered: 4168,
},
]
return
try {
return (await this.$fetch('queryProductList', { ...this.queryParams, categoryId }))?.records || []
} catch (err) {
return []
}
},
async initList() {
console.log('queryParams', this.queryParams)
const results = await Promise.allSettled(this.categoryList.map(category => { return this.queryProductList(category.id) }))
results.forEach((result, index) => {
this.categoryList[index].children = result.value || []
})
console.log('categoryList', this.categoryList)
},
change(e) {
this.current = e
},
search(){
// todo: set filter
this.initList()
},
onClickFilter(key, val) {
if (val) {
this.queryParams[key] = val
} else {
delete this.queryParams[key]
}
this.initList()
},
openStartDatePicker() {
this.$refs.startDatePicker?.[0]?.open?.();
},
onStartDateChange(e) {
const date = e.value
this.queryParams.startDate = date
const { endDate } = this.queryParams
if (endDate && this.$dayjs(date).isAfter(endDate, 'day')) {
this.endDate = null
delete this.queryParams.endDate
}
this.initList()
},
onClearStartDate() {
this.startDate = null
delete this.queryParams.startDate
this.initList()
},
openEndDatePicker() {
this.$refs.endDatePicker?.[0]?.open?.();
},
onEndDateChange(e) {
const date = e.value
this.queryParams.endDate = date
const { startDate } = this.queryParams
if (startDate && this.$dayjs(date).isBefore(startDate, 'day')) {
this.startDate = null
delete this.queryParams.startDate
}
this.initList()
},
onClearEndDate() {
this.endDate = null
delete this.queryParams.endDate
this.initList()
},
}
}
</script>
<style scoped lang="scss">
.page__view {
height: 100vh;
background: linear-gradient(#DAF3FF, #FBFEFF 200rpx, #FBFEFF);
/deep/ .uv-popup {
z-index: 1000000 !important;
}
}
.header {
width: 100%;
padding: 0 32rpx;
box-sizing: border-box;
}
.filter {
&-header {
display: flex;
flex-direction: column;
justify-content: flex-end;
height: 176rpx;
padding-bottom: 12rpx;
box-sizing: border-box;
.bar {
display: grid;
grid-template-columns: repeat(3, 1fr);
.btn {
display: inline-flex;
align-items: center;
column-gap: 8rpx;
padding: 8rpx 30rpx;
font-size: 28rpx;
font-weight: 500;
color: #FFFFFF;
background: #00A9FF;
border: 2rpx solid #00A9FF;
border-radius: 64rpx;
&-icon {
width: 32rpx;
height: auto;
}
&.is-fold {
font-weight: 400;
color: #191919;
background: #D8F2FF;
border-color: #00A9FF66;
}
}
.title {
text-align: center;
font-size: 32rpx;
font-weight: 600;
color: #191919;
}
}
}
&-content {
margin-top: 24rpx;
.btn {
&-fold {
margin-top: 32rpx;
width: 100%;
}
&-icon {
width: 40rpx;
height: auto;
}
}
}
&-item {
display: flex;
& + & {
margin-top: 32rpx;
}
&-label {
width: 156rpx;
min-height: 64rpx;
line-height: 64rpx;
flex: none;
}
&-content {
flex: 1;
.option {
margin-top: 6rpx;
display: flex;
flex-wrap: wrap;
gap: 24rpx;
&-item {
padding: 8rpx 16rpx;
font-size: 28rpx;
color: #181818;
border-radius: 4rpx;
&.is-active {
color: #FFFFFF;
background: #00A9FF;
}
}
}
.range {
margin: 4rpx 0;
column-gap: 8rpx;
border-bottom: 2rpx solid #EEEEEE;
&-item {
width: 220rpx;
box-sizing: border-box;
}
.split {
padding: 0 24rpx;
font-size: 32rpx;
line-height: 1;
color: #8B8B8B;
}
&.price {
.range-item {
padding: 4rpx 0;
}
}
&.time {
.range-item {
// width: 220rpx;
padding: 8rpx 0;
text-align: center;
font-size: 28rpx;
color: #181818;
.btn-clear {
margin: 6rpx 0;
float: right;
}
}
}
}
}
}
}
.sort {
width: 100%;
height: 116rpx;
padding: 24rpx 0;
box-sizing: border-box;
}
.main {
/deep/ .uv-vtabs,
/deep/ .uv-vtabs__bar,
/deep/ .uv-vtabs__content {
height: calc(100vh - 292rpx - #{$tabbar-height} - env(safe-area-inset-bottom)) !important;
}
/deep/ .uv-vtabs__bar {
background: #F6F6F6 !important;
}
/deep/ .uv-vtabs__bar-item {
padding: 48rpx 32rpx;
}
/deep/ .uv-vtabs__content {
padding: 24rpx 24rpx 0 24rpx;
box-sizing: border-box;
background: linear-gradient(#DAF3FF, #F4F4F4 250rpx, #F4F4F4);
}
}
.card {
& + & {
margin-top: 32rpx;
}
&:last-child {
padding-bottom: 24rpx;
}
}
</style>

+ 7
- 3
pages/index/index.vue View File

@ -1,9 +1,9 @@
<template>
<view class="page__view">
<!-- todo: 轮播图 -->
<view class="bg">
<!-- <view class="bg">
<image class="img" src="@/static/image/bg.png" mode="widthFix"></image>
</view>
</view> -->
<bgSwiperView></bgSwiperView>
<view class="main">
@ -55,6 +55,8 @@
</view>
<!-- todo: 联系客服 -->
<tabber select="home" />
</view>
@ -64,6 +66,7 @@
import mixinsList from '@/mixins/list.js'
import tabber from '@/components/base/tabbar.vue'
import bgSwiperView from '@/components/home/bgSwiperView.vue'
import categoryView from '@/components/home/categoryView.vue'
import recommendView from '@/components/home/recommendView.vue'
import swiperView from '@/components/home/swiperView.vue'
@ -74,6 +77,7 @@
mixins: [mixinsList],
components: {
tabber,
bgSwiperView,
categoryView,
recommendView,
swiperView,


+ 7
- 71
pages_order/product/search.vue View File

@ -24,25 +24,7 @@
</view>
<view class="main">
<view class="flex sort">
<view :class="['flex', 'sort-item', queryParams.sort == 'comprehensive' ? 'is-active' : '']" @click="onClickSort('comprehensive')">综合</view>
<view :class="['flex', 'sort-item', ['sale-asc', 'sale-desc'].includes(queryParams.sort) ? 'is-active' : '']" @click="onClickSort('sale')">
<view>销量</view>
<view class="sort-item-icon">
<uv-icon v-if="queryParams.sort == 'sale-asc'" name="arrow-up-fill" color="#00A9FF" size="16rpx" :bold="true"></uv-icon>
<uv-icon v-else-if="queryParams.sort == 'sale-desc'" name="arrow-down-fill" color="#00A9FF" size="16rpx" :bold="true"></uv-icon>
<image v-else style="width: 8rpx; height: auto;" src="/static/image/icon-sort.png" mode="widthFix"></image>
</view>
</view>
<view :class="['flex', 'sort-item', ['price-asc', 'price-desc'].includes(queryParams.sort) ? 'is-active' : '']" @click="onClickSort('price')">
<view>价格</view>
<view class="sort-item-icon">
<uv-icon v-if="queryParams.sort == 'price-asc'" name="arrow-up-fill" color="#00A9FF" size="16rpx" :bold="true"></uv-icon>
<uv-icon v-else-if="queryParams.sort == 'price-desc'" name="arrow-down-fill" color="#00A9FF" size="16rpx" :bold="true"></uv-icon>
<image v-else style="width: 8rpx; height: auto;" src="/static/image/icon-sort.png" mode="widthFix"></image>
</view>
</view>
</view>
<sortBar v-model="queryParams.sort" @change="onSortChange"></sortBar>
<view v-if="list.length" class="content">
<view v-for="item in list" :key="item.id">
@ -62,11 +44,13 @@
<script>
import mixinsList from '@/mixins/list.js'
import productCard from '@/components/home/productCard.vue'
import sortBar from '@/components/product/sortBar.vue'
import productCard from '@/components/product/productCard.vue'
export default {
mixins: [mixinsList],
components: {
sortBar,
productCard,
},
data() {
@ -157,28 +141,8 @@
this.queryParams.title = this.keyword
this.getData()
},
onClickSort(key) {
switch(key) {
case 'comprehensive':
this.queryParams.sort = 'comprehensive'
break;
case 'sale':
if (this.queryParams.sort == 'sale-desc') {
this.queryParams.sort = 'sale-asc'
} else {
this.queryParams.sort = 'sale-desc'
}
break;
case 'price':
if (this.queryParams.sort == 'price-desc') {
this.queryParams.sort = 'price-asc'
} else {
this.queryParams.sort = 'price-desc'
}
break;
default:
break;
}
onSortChange(sort) {
console.log('onSortChange', sort)
},
},
}
@ -186,10 +150,6 @@
<style scoped lang="scss">
.page__view {
background: linear-gradient(#DAF3FF, #F4F4F4 200rpx, #F4F4F4);
}
.search {
$h: 64rpx;
$radius: 32rpx;
@ -229,31 +189,7 @@
margin-top: 24rpx;
padding: 0 32rpx 100rpx 32rpx;
}
.sort {
justify-content: space-between;
&-item {
padding: 12rpx 32rpx;
font-size: 28rpx;
line-height: 1.5;
color: #191919;
column-gap: 4rpx;
&.is-active {
font-weight: 600;
color: #00A9FF;
}
&-icon {
width: 32rpx;
display: inline-flex;
align-items: center;
justify-content: center;
}
}
}
.content {
margin-top: 24rpx;
display: grid;


BIN
static/image/icon-arrow-down.png View File

Before After
Width: 48  |  Height: 48  |  Size: 415 B

BIN
static/image/icon-arrow-up-light.png View File

Before After
Width: 48  |  Height: 48  |  Size: 287 B

BIN
static/image/icon-arrow-up.png View File

Before After
Width: 61  |  Height: 60  |  Size: 396 B

BIN
static/image/icon-degree.png View File

Before After
Width: 57  |  Height: 41  |  Size: 1.7 KiB

BIN
static/image/icon-nav.png View File

Before After
Width: 401  |  Height: 44  |  Size: 4.3 KiB

BIN
static/image/icon-pdf.png View File

Before After
Width: 97  |  Height: 129  |  Size: 2.7 KiB

BIN
static/image/icon-word.png View File

Before After
Width: 97  |  Height: 129  |  Size: 3.9 KiB

BIN
static/image/logo.png View File

Before After
Width: 222  |  Height: 222  |  Size: 6.9 KiB

BIN
static/image/temp-01.png View File

Before After
Width: 253  |  Height: 345  |  Size: 113 KiB

BIN
static/image/temp-02.png View File

Before After
Width: 253  |  Height: 345  |  Size: 113 KiB

BIN
static/image/temp-03.png View File

Before After
Width: 213  |  Height: 285  |  Size: 82 KiB

BIN
static/image/temp-04.png View File

Before After
Width: 218  |  Height: 291  |  Size: 101 KiB

Loading…
Cancel
Save