9 changed files with 912 additions and 86 deletions
@ -0,0 +1,161 @@ |
|||
<template> |
|||
<view class="diy-banner" style="height: 13rem;"> |
|||
<!-- 图片轮播 --> |
|||
<swiper class="swiper-box" :autoplay="autoplay" duration="800" circular="true" interval="3000" @change="_bindChange"> |
|||
<swiper-item v-for="(dataItem, index) in dataList" :key="index"> |
|||
<image mode="widthFix" class="slide-image" :src="dataItem.imgUrl" @click="onLink(dataItem.link)" @load="_imagesHeight" /> |
|||
</swiper-item> |
|||
</swiper> |
|||
<!-- 指示点 --> |
|||
<view class="indicator-dots rectangle"> |
|||
<view class="dots-item" :class="{ active: imgCurrent == index }" style="{ backgroundColor:#e2534d }" |
|||
v-for="(dataItem, index) in dataList" :key="index"></view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import mixin from '@/components/page/mixin.js'; |
|||
|
|||
export default { |
|||
name: 'Banner', |
|||
|
|||
/** |
|||
* 组件的属性列表 |
|||
* 用于组件自定义设置 |
|||
*/ |
|||
props: { |
|||
// itemIndex: String, |
|||
// itemStyle: Object, |
|||
// params: Object, |
|||
dataList: Array |
|||
}, |
|||
|
|||
watch:{ |
|||
dataList:function(e){ |
|||
console.log("datalist",e) |
|||
} |
|||
}, |
|||
|
|||
mixins: [mixin], |
|||
|
|||
/** |
|||
* 私有数据,组件的初始数据 |
|||
* 可用于模版渲染 |
|||
*/ |
|||
data() { |
|||
return { |
|||
windowWidth: 750, |
|||
indicatorDots: false, // 是否显示面板指示点 |
|||
autoplay: true, // 是否自动切换 |
|||
duration: 800, // 滑动动画时长 |
|||
imgHeights: [], // 图片的高度 |
|||
imgCurrent: 0 // 当前banne所在滑块指针 |
|||
}; |
|||
}, |
|||
|
|||
created() { |
|||
const app = this; |
|||
uni.getSystemInfo({ |
|||
success({ windowWidth }) { |
|||
app.windowWidth = windowWidth > 750 ? 750 : windowWidth; |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
/** |
|||
* 组件的方法列表 |
|||
* 更新属性和数据的方法与更新页面数据的方法类似 |
|||
*/ |
|||
methods: { |
|||
/** |
|||
* 计算图片高度 |
|||
*/ |
|||
_imagesHeight({ detail }) { |
|||
console.log(detail) |
|||
const app = this; |
|||
// 获取图片真实宽度 |
|||
const { width, height } = detail; |
|||
// 宽高比 |
|||
const ratio = width / height; |
|||
// 计算的高度值 |
|||
const viewHeight = app.windowWidth / ratio; |
|||
// 把每一张图片的高度记录到数组里 |
|||
app.imgHeights.push(viewHeight); |
|||
}, |
|||
|
|||
/** |
|||
* 记录当前指针 |
|||
*/ |
|||
_bindChange(e) { |
|||
this.imgCurrent = e.detail.current; |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.diy-banner { |
|||
position: relative; |
|||
|
|||
// swiper组件 |
|||
.swiper-box { |
|||
height: 100%; |
|||
|
|||
.slide-image { |
|||
width: 100%; |
|||
height: 100%; |
|||
margin: 0 auto; |
|||
display: block; |
|||
} |
|||
} |
|||
|
|||
/* 指示点 */ |
|||
.indicator-dots { |
|||
width: 100%; |
|||
height: 28rpx; |
|||
padding: 0 20rpx; |
|||
position: absolute; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 20rpx; |
|||
opacity: 0.8; |
|||
display: flex; |
|||
justify-content: center; |
|||
|
|||
.dots-item { |
|||
width: 16rpx; |
|||
height: 16rpx; |
|||
margin-right: 8rpx; |
|||
background-color: #fff; |
|||
|
|||
&:last-child { |
|||
margin-right: 0; |
|||
} |
|||
|
|||
&.active { |
|||
background-color: #313131 !important; |
|||
} |
|||
} |
|||
|
|||
// 圆形 |
|||
&.round .dots-item { |
|||
width: 16rpx; |
|||
height: 16rpx; |
|||
border-radius: 20rpx; |
|||
} |
|||
|
|||
// 正方形 |
|||
&.square .dots-item { |
|||
width: 16rpx; |
|||
height: 16rpx; |
|||
} |
|||
|
|||
// 长方形 |
|||
&.rectangle .dots-item { |
|||
width: 22rpx; |
|||
height: 14rpx; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,256 @@ |
|||
<template> |
|||
<!-- 商品组 --> |
|||
<view class="diy-goods" :style="{ background: itemStyle.background }"> |
|||
<view class="goods-list" :class="[`display__${itemStyle.display}`, `column__${itemStyle.column}`]"> |
|||
<scroll-view :scroll-x="itemStyle.display === 'slide'"> |
|||
<view class="goods-item" v-for="(dataItem, index) in dataList" :key="index" @click="onTargetGoods(dataItem.goods_id)"> |
|||
|
|||
<!-- 单列商品 --> |
|||
<block v-if="itemStyle.column === 1"> |
|||
<view class="dis-flex"> |
|||
<!-- 商品图片 --> |
|||
<view class="goods-item_left"> |
|||
<image class="image" :src="dataItem.goods_image"></image> |
|||
</view> |
|||
<view class="goods-item_right"> |
|||
<!-- 商品名称 --> |
|||
<view v-if="itemStyle.show.includes('goodsName')" class="goods-name"> |
|||
<text class="twoline-hide">{{ dataItem.goods_name }}</text> |
|||
</view> |
|||
<view class="goods-item_desc"> |
|||
<!-- 商品卖点 --> |
|||
<view v-if="itemStyle.show.includes('sellingPoint')" class="desc-selling_point dis-flex"> |
|||
<text class="oneline-hide">{{ dataItem.selling_point }}</text> |
|||
</view> |
|||
<!-- 商品销量 --> |
|||
<view v-if="itemStyle.show.includes('goodsSales')" class="desc-goods_sales dis-flex"> |
|||
<text>已售{{ dataItem.goods_sales }}件</text> |
|||
</view> |
|||
<!-- 商品价格 --> |
|||
<view class="desc_footer"> |
|||
<text v-if="itemStyle.show.includes('goodsPrice')" class="price_x">¥{{ dataItem.goods_price_min }}</text> |
|||
<text class="price_y col-9" |
|||
v-if="itemStyle.show.includes('linePrice') && dataItem.line_price_min > 0">¥{{ dataItem.line_price_min }}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
<!-- 多列商品 --> |
|||
<block v-else> |
|||
<!-- 商品图片 --> |
|||
<view class="goods-image"> |
|||
<image class="image" mode="aspectFill" :src="dataItem.goods_image"></image> |
|||
</view> |
|||
<view class="detail"> |
|||
<!-- 商品标题 --> |
|||
<view v-if="itemStyle.show.includes('goodsName')" class="goods-name"> |
|||
<text class="twoline-hide">{{ dataItem.goods_name }}</text> |
|||
</view> |
|||
<!-- 商品价格 --> |
|||
<view class="detail-price oneline-hide"> |
|||
<text v-if="itemStyle.show.includes('goodsPrice')" class="goods-price f-30 col-m">¥{{ dataItem.goods_price_min }}</text> |
|||
<text v-if="itemStyle.show.includes('linePrice') && dataItem.line_price_min > 0" |
|||
class="line-price col-9 f-24">¥{{ dataItem.line_price_min }}</text> |
|||
</view> |
|||
</view> |
|||
</block> |
|||
|
|||
</view> |
|||
</scroll-view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: "Goods", |
|||
/** |
|||
* 组件的属性列表 |
|||
* 用于组件自定义设置 |
|||
*/ |
|||
props: { |
|||
itemIndex: String, |
|||
itemStyle: Object, |
|||
params: Object, |
|||
dataList: Array |
|||
}, |
|||
|
|||
/** |
|||
* 组件的方法列表 |
|||
* 更新属性和数据的方法与更新页面数据的方法类似 |
|||
*/ |
|||
methods: { |
|||
|
|||
/** |
|||
* 跳转商品详情页 |
|||
*/ |
|||
onTargetGoods(goodsId) { |
|||
this.$navTo(`pages/goods/detail`, { goodsId }) |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.diy-goods { |
|||
.goods-list { |
|||
padding: 4rpx; |
|||
box-sizing: border-box; |
|||
|
|||
.goods-item { |
|||
box-sizing: border-box; |
|||
padding: 6rpx; |
|||
|
|||
.goods-image { |
|||
position: relative; |
|||
width: 100%; |
|||
height: 0; |
|||
padding-bottom: 100%; |
|||
overflow: hidden; |
|||
background: #fff; |
|||
|
|||
&:after { |
|||
content: ''; |
|||
display: block; |
|||
margin-top: 100%; |
|||
} |
|||
|
|||
.image { |
|||
position: absolute; |
|||
width: 100%; |
|||
height: 100%; |
|||
top: 0; |
|||
left: 0; |
|||
-o-object-fit: cover; |
|||
object-fit: cover; |
|||
} |
|||
} |
|||
|
|||
.detail { |
|||
padding: 8rpx; |
|||
background: #fff; |
|||
|
|||
.goods-name { |
|||
min-height: 68rpx; |
|||
line-height: 1.3; |
|||
white-space: normal; |
|||
color: #484848; |
|||
font-size: 26rpx; |
|||
} |
|||
|
|||
.detail-price { |
|||
.goods-price { |
|||
margin-right: 8rpx; |
|||
} |
|||
|
|||
.line-price { |
|||
text-decoration: line-through; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
&.display__slide { |
|||
white-space: nowrap; |
|||
font-size: 0; |
|||
|
|||
.goods-item { |
|||
display: inline-block; |
|||
} |
|||
} |
|||
|
|||
&.display__list { |
|||
.goods-item { |
|||
float: left; |
|||
} |
|||
} |
|||
|
|||
&.column__2 { |
|||
.goods-item { |
|||
width: 50%; |
|||
} |
|||
} |
|||
|
|||
&.column__3 { |
|||
.goods-item { |
|||
width: 33.33333%; |
|||
} |
|||
} |
|||
|
|||
&.column__1 { |
|||
.goods-item { |
|||
width: 100%; |
|||
height: 280rpx; |
|||
margin-bottom: 12rpx; |
|||
padding: 20rpx; |
|||
box-sizing: border-box; |
|||
background: #fff; |
|||
line-height: 1.6; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
} |
|||
|
|||
.goods-item_left { |
|||
display: flex; |
|||
width: 40%; |
|||
background: #fff; |
|||
align-items: center; |
|||
|
|||
.image { |
|||
display: block; |
|||
width: 240rpx; |
|||
height: 240rpx; |
|||
} |
|||
} |
|||
|
|||
.goods-item_right { |
|||
position: relative; |
|||
width: 60%; |
|||
|
|||
.goods-name { |
|||
margin-top: 20rpx; |
|||
min-height: 68rpx; |
|||
line-height: 1.3; |
|||
white-space: normal; |
|||
color: #484848; |
|||
font-size: 26rpx; |
|||
} |
|||
} |
|||
|
|||
.goods-item_desc { |
|||
margin-top: 8rpx; |
|||
} |
|||
|
|||
.desc-selling_point { |
|||
width: 400rpx; |
|||
font-size: 24rpx; |
|||
color: #e49a3d; |
|||
} |
|||
|
|||
.desc-goods_sales { |
|||
color: #999; |
|||
font-size: 24rpx; |
|||
} |
|||
|
|||
.desc_footer { |
|||
font-size: 24rpx; |
|||
|
|||
.price_x { |
|||
margin-right: 16rpx; |
|||
color: #f03c3c; |
|||
font-size: 30rpx; |
|||
} |
|||
|
|||
.price_y { |
|||
text-decoration: line-through; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,237 @@ |
|||
<template> |
|||
<view class="sovue"> |
|||
<!-- <view class="container"> --> |
|||
|
|||
<view class="search-wrapper"> |
|||
<view class="search-input"> |
|||
<view class="search-input-wrapper"> |
|||
<view class="left"> |
|||
<text class="search-icon iconfont icon-search"></text> |
|||
</view> |
|||
<view class="right"> |
|||
<input v-model="searchValue" class="input" focus="true" placeholder="请输入搜索关键词" type="text"></input> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<view class="search-button"> |
|||
<view class="button" @click="onSearch">搜索</view> |
|||
</view> |
|||
</view> |
|||
<view class="history" v-if="historySearch.length"> |
|||
<view class="his-head"> |
|||
<text class="title">最近搜索</text> |
|||
<text class="icon iconfont icon-delete" @click="clearSearch"></text> |
|||
</view> |
|||
<view class="his-list"> |
|||
<view class="his-item" v-for="(val, index) in historySearch" :key="index"> |
|||
<view class="history-button" @click="handleQuick(val)">{{ val }}</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<!-- </view> --> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
const HISTORY_SEARCH = 'historySearch' |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
historySearch: [], |
|||
searchValue: '' |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 生命周期函数--监听页面加载 |
|||
*/ |
|||
onLoad(options) { |
|||
// 获取历史搜索 |
|||
this.historySearch = this.getHistorySearch() |
|||
}, |
|||
|
|||
methods: { |
|||
|
|||
/** |
|||
* 获取历史搜索 |
|||
*/ |
|||
getHistorySearch() { |
|||
return uni.getStorageSync(HISTORY_SEARCH) || [] |
|||
}, |
|||
|
|||
/** |
|||
* 搜索提交 |
|||
*/ |
|||
onSearch() { |
|||
const { searchValue } = this |
|||
if (searchValue) { |
|||
// 记录历史搜索 |
|||
this.setHistory(searchValue) |
|||
// 跳转到商品列表页 |
|||
this.$navTo('pages/goods/list', { search: searchValue }) |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 记录历史搜索 |
|||
*/ |
|||
setHistory(searchValue) { |
|||
const data = this.getHistorySearch() |
|||
const index = data.indexOf(searchValue) |
|||
index > -1 && data.splice(index, 1) |
|||
data.unshift(searchValue) |
|||
this.historySearch = data |
|||
this.onUpdateStorage() |
|||
}, |
|||
|
|||
/** |
|||
* 清空最近搜索记录 |
|||
*/ |
|||
clearSearch() { |
|||
this.historySearch = [] |
|||
this.onUpdateStorage() |
|||
}, |
|||
|
|||
/** |
|||
* 更新历史搜索缓存 |
|||
* @param {Object} data |
|||
*/ |
|||
onUpdateStorage(data) { |
|||
uni.setStorageSync(HISTORY_SEARCH, this.historySearch) |
|||
}, |
|||
|
|||
/** |
|||
* 跳转到最近搜索 |
|||
*/ |
|||
handleQuick(search) { |
|||
this.$navTo('pages/goods/list', { search }) |
|||
} |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.container { |
|||
padding: 20rpx; |
|||
min-height: 100vh; |
|||
background: #f7f7f7; |
|||
} |
|||
|
|||
.sovue{ |
|||
padding: 20rpx; |
|||
position: absolute; |
|||
top: 20rpx; |
|||
left: 4rpx; |
|||
width: 100%; |
|||
} |
|||
|
|||
.search-wrapper { |
|||
display: flex; |
|||
height: 84rpx; |
|||
} |
|||
|
|||
// 搜索输入框 |
|||
.search-input { |
|||
width: 80%; |
|||
background: #fff; |
|||
border-radius: 10rpx 0 0 10rpx; |
|||
box-sizing: border-box; |
|||
overflow: hidden; |
|||
|
|||
.search-input-wrapper { |
|||
display: flex; |
|||
|
|||
.left { |
|||
display: flex; |
|||
width: 60rpx; |
|||
justify-content: center; |
|||
align-items: center; |
|||
|
|||
.search-icon { |
|||
display: block; |
|||
color: #b4b4b4; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
} |
|||
|
|||
.right { |
|||
flex: 1; |
|||
|
|||
input { |
|||
font-size: 28rpx; |
|||
height: 84rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
.input-placeholder { |
|||
color: #aba9a9; |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
// 搜索按钮 |
|||
.search-button { |
|||
width: 20%; |
|||
box-sizing: border-box; |
|||
|
|||
.button { |
|||
height: 84rpx; |
|||
font-size: 28rpx; |
|||
border-radius: 0 10rpx 10rpx 0; |
|||
background: #fa2209; |
|||
color: #fff; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
} |
|||
|
|||
|
|||
// 最近搜索 |
|||
.history { |
|||
|
|||
.his-head { |
|||
font-size: 28rpx; |
|||
padding: 50rpx 0 0 0; |
|||
color: #777; |
|||
|
|||
.icon { |
|||
float: right; |
|||
} |
|||
|
|||
} |
|||
|
|||
.his-list { |
|||
padding: 20rpx 0; |
|||
overflow: hidden; |
|||
|
|||
.his-item { |
|||
width: 33.3%; |
|||
float: left; |
|||
padding: 10rpx; |
|||
box-sizing: border-box; |
|||
|
|||
.history-button { |
|||
text-align: center; |
|||
padding: 14rpx; |
|||
line-height: 30rpx; |
|||
border-radius: 100rpx; |
|||
background: #fff; |
|||
font-size: 26rpx; |
|||
border: 1rpx solid #efefef; |
|||
overflow: hidden; |
|||
white-space: nowrap; |
|||
text-overflow: ellipsis; |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,132 @@ |
|||
<template> |
|||
<view class="container"> |
|||
<!-- 店铺页面组件 --> |
|||
<Page :items="items" /> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { setCartTabBadge } from '@/core/app' |
|||
import * as Api from '@/api/page' |
|||
import Page from '@/components/page' |
|||
|
|||
const App = getApp() |
|||
|
|||
export default { |
|||
components: { |
|||
Page |
|||
}, |
|||
data() { |
|||
return { |
|||
// 页面参数 |
|||
options: {}, |
|||
// 页面属性 |
|||
page: {}, |
|||
// 页面元素 |
|||
items: [] |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 生命周期函数--监听页面加载 |
|||
*/ |
|||
onLoad(options) { |
|||
// 当前页面参数 |
|||
this.options = options |
|||
// 加载页面数据 |
|||
this.getPageData(); |
|||
}, |
|||
|
|||
/** |
|||
* 生命周期函数--监听页面显示 |
|||
*/ |
|||
onShow() { |
|||
// 更新购物车角标 |
|||
setCartTabBadge() |
|||
}, |
|||
|
|||
methods: { |
|||
|
|||
/** |
|||
* 加载页面数据 |
|||
* @param {Object} callback |
|||
*/ |
|||
getPageData(callback) { |
|||
const app = this |
|||
const pageId = app.options.pageId || 0 |
|||
Api.detail(pageId) |
|||
.then(result => { |
|||
// 设置页面数据 |
|||
const { data: { pageData } } = result |
|||
app.page = pageData.page |
|||
app.items = pageData.items |
|||
// 设置顶部导航栏栏 |
|||
app.setPageBar(); |
|||
}) |
|||
.finally(() => callback && callback()) |
|||
}, |
|||
|
|||
/** |
|||
* 设置顶部导航栏 |
|||
*/ |
|||
setPageBar() { |
|||
const { |
|||
page |
|||
} = this |
|||
// 设置页面标题 |
|||
uni.setNavigationBarTitle({ |
|||
title: page.params.title |
|||
}); |
|||
// 设置navbar标题、颜色 |
|||
uni.setNavigationBarColor({ |
|||
frontColor: page.style.titleTextColor === 'white' ? '#ffffff' : '#000000', |
|||
backgroundColor: page.style.titleBackgroundColor |
|||
}) |
|||
}, |
|||
|
|||
}, |
|||
|
|||
/** |
|||
* 下拉刷新 |
|||
*/ |
|||
onPullDownRefresh() { |
|||
// 获取首页数据 |
|||
this.getPageData(() => { |
|||
uni.stopPullDownRefresh() |
|||
}) |
|||
}, |
|||
|
|||
/** |
|||
* 分享当前页面 |
|||
*/ |
|||
onShareAppMessage() { |
|||
const app = this |
|||
const { page } = app |
|||
return { |
|||
title: page.params.share_title, |
|||
path: "/pages/index/index?" + app.$getShareUrlParams() |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 分享到朋友圈 |
|||
* 本接口为 Beta 版本,暂只在 Android 平台支持,详见分享到朋友圈 (Beta) |
|||
* https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html |
|||
*/ |
|||
onShareTimeline() { |
|||
const app = this |
|||
const { page } = app |
|||
return { |
|||
title: page.params.share_title, |
|||
path: "/pages/index/index?" + app.$getShareUrlParams() |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.container { |
|||
background: #fff; |
|||
} |
|||
</style> |
|||
|
Before Width: | Height: | Size: 4.0 KiB |
Loading…
Reference in new issue