betterscroll滚动
<template>
<div ref="wrapper">
<slot></slot>
</div>
</template>
<script>
import BScroll from "better-scroll";
export default {
name: "Scroll",
props: {
probeType: {
type: Number,
default: 1,
},
data: {
type: Array,
default: () => {
return [];
},
},
pullUpLoad: {
type: Boolean,
default: false,
},
},
data() {
return {
scroll: {},
};
},
mounted() {
setTimeout(this.__initScroll, 20);
},
methods: {
__initScroll() {
// 1.初始化BScroll对象
if (!this.$refs.wrapper) return;
this.scroll = new BScroll(this.$refs.wrapper, {
probeType: this.probeType,
click: true,
pullUpLoad: this.pullUpLoad,
});
// 2.将监听事件回调
this.scroll.on("scroll", (pos) => {
this.$emit("scroll", pos);
});
// 3.监听上拉到底部
this.scroll.on("pullingUp", () => {
console.log("上拉加载");
this.$emit("pullingUp");
});
},
refresh() {
this.scroll && this.scroll.refresh && this.scroll.refresh();
},
finishPullUp() {
this.scroll &&
this.scroll.finishPullUp &&
this.scroll.finishPullUp();
},
scrollTo(x, y, time) {
this.scroll &&
this.scroll.scrollTo &&
this.scroll.scrollTo(x, y, time);
},
},
watch: {
data() {
setTimeout(this.refresh, 20);
},
},
};
</script>
<style scoped>
</style>
使用
<!--codewhy的代码演示部分-->
<template>
<div id="home">
<nav-bar class="nav-bar"><div slot="center">购物街</div></nav-bar>
<tab-control
v-show="isTabFixed"
class="fixed"
@itemClick="tabClick"
:titles="['流行', '新款', '精选']"
></tab-control>
<scroll
class="content"
ref="scroll"
@scroll="contentScroll"
@pullingUp="loadMore"
:data="showGoodsList"
:pull-up-load="true"
//这里加:代表传递的是数字 不加:传递的是字符串
:probe-type="3"
>
<div>
<home-swiper :banners="banners" ref="hSwiper"></home-swiper>
<feature-view :features="recommends"></feature-view>
<recommend-view></recommend-view>
<tab-control
@itemClick="tabClick"
:titles="['流行', '新款', '精选']"
ref="tabControl"
></tab-control>
<goods-list :goods-list="showGoodsList"></goods-list>
</div>
</scroll>
<back-top @backTop="backTop" class="back-top" v-show="showBackTop">
<img src="~assets/img/common/top.png" alt="" />
</back-top>
</div>
</template>
<script>
import NavBar from "common/navbar/NavBar";
import Scroll from "common/scroll/Scroll";
import TabControl from "content/tabControl/TabControl";
import BackTop from "content/backTop/BackTop";
import HomeSwiper from "./childComps/HomeSwiper";
import FeatureView from "./childComps/FeatureView";
import RecommendView from "./childComps/RecommendView";
import GoodsList from "./childComps/GoodsList";
import { getHomeMultidata, getHomeData, RECOMMEND, BANNER } from "network/home";
import { NEW, POP, SELL, BACKTOP_DISTANCE } from "@/common/const";
export default {
name: "Home",
components: {
NavBar,
Scroll,
TabControl,
BackTop,
HomeSwiper,
FeatureView,
RecommendView,
GoodsList,
},
data() {
return {
banners: [],
recommends: [],
goodsList: {
pop: { page: 1, list: [] },
new: { page: 1, list: [] },
sell: { page: 1, list: [] },
},
currentType: POP,
isTabFixed: false,
tabOffsetTop: 0,
showBackTop: false,
};
},
computed: {
showGoodsList() {
return this.goodsList[this.currentType].list;
},
},
created() {
console.log("创建Home");
// 1.请求多个数据
this.getMultiData();
// 2.请求商品数据
this.getHomeProducts(POP);
this.getHomeProducts(NEW);
this.getHomeProducts(SELL);
},
activated: function () {
this.$refs.hSwiper.startTimer();
},
deactivated: function () {
this.$refs.hSwiper.stopTimer();
},
updated() {
// this.tabOffsetTop = this.$refs.tabControl.$el.offsetTop
// console.log(this.tabOffsetTop);
},
methods: {
tabClick(index) {
switch (index) {
case 0:
this.currentType = POP;
break;
case 1:
this.currentType = NEW;
break;
case 2:
this.currentType = SELL;
break;
}
},
contentScroll(position) {
// 1.决定tabFixed是否显示
this.isTabFixed = position.y < -this.tabOffsetTop;
// 2.决定backTop是否显示
this.showBackTop = position.y < -BACKTOP_DISTANCE;
},
loadMore() {
this.getHomeProducts(this.currentType);
},
backTop() {
this.$refs.scroll.scrollTo(0, 0, 300);
},
/**
* 网络请求相关方法
*/
getMultiData() {
getHomeMultidata().then((res) => {
this.banners = res.data[BANNER].list;
this.recommends = res.data[RECOMMEND].list;
// 下次更新DOM时,获取新的tabOffsetTop值(不保险,可以在updated钩子中获取)
this.$nextTick(() => {
this.tabOffsetTop = this.$refs.tabControl.$el.offsetTop;
});
});
},
getHomeProducts(type) {
getHomeData(type, this.goodsList[type].page).then((res) => {
const goodsList = res.data.list;
this.goodsList[type].list.push(...goodsList);
this.goodsList[type].page += 1;
this.$refs.scroll.finishPullUp();
});
},
},
};
</script>
<style scoped>
#home {
/*position: relative;*/
height: 100vh;
}
.nav-bar {
background-color: var(--color-tint);
font-weight: 700;
color: #fff;
}
.content {
position: absolute;
top: 44px;
bottom: 49px;
left: 0;
right: 0;
}
.fixed {
position: fixed;
top: 44px;
left: 0;
right: 0;
}
.back-top {
position: fixed;
right: 10px;
bottom: 60px;
}
</style>
vue-scroller封装
// 使用vue的混入方式
import {initData} from '@/api/baseData'
export default {
data() {
return {
loading: false,
data: [],
page: 0,
limit: 10,
total: 0,
url: '',
params: {},
pageInfo: {},
time: 170,
noDate: false,
hasAlive:false,
}
},
mounted() {
this.top = 10
this.bottom = 20
},
methods: {
async init(done) {
this.hasAlive = true; //keepalive组件内是否已经调用过init标识
if (!await this.beforeInit()) {
return
}
if(this.loading){
this.$refs.scroller.finishPullToRefresh();
return
}
return new Promise((resolve, reject) => {
this.loading = true;
initData(this.url, this.params).then(res => {
this.loading = false;
this.$refs.scroller.finishPullToRefresh();
done()
this.total = Number(res.data.total)
if ((this.page - 1) * this.limit >= this.total) {
this.noDate = true;
} else {
this.noDate = false
}
if (this.noDate) {
this.$refs.scroller.finishInfinite(true);
}
// 判断是下拉刷新还是上拉加载(这一步也是比较巧妙的,当然也很好理解)
if (this.page <= 1) {
this.data = res.data.records;
} else {
this.data = this.data.concat(res.data.records)
console.log(this.data)
}
}).catch(err => {
this.noDate = true; //防止死循环
this.loading = false;
done();
reject(err)
})
})
},
beforeInit() {
return true
},
loadNewData(done) {
this.page = 1;//重置页数刷新每次页数都是第一页
this.noDate = false;//重置数据判断
setTimeout(function () {
this.init(done)
}.bind(this), 200)
},
loadMore(done) {
setTimeout(() => {
if (this.noDate) {
this.$refs.scroller.finishInfinite(true);
} else {
this.page++;//下拉一次页数+1
this.init(done);
}
}, 200);
},
// 预防删除第二页最后一条数据时,或者多选删除第二页的数据时,页码错误导致请求无数据
dleChangePage(size) {
if (size === undefined) {
size = 1
}
if (this.data.length === size && this.page !== 1) {
this.page = this.page - 1
}
},
//查询
handleQuery() {
this.$refs.scroller.triggerPullToRefresh()
}
}
}
使用方法:
<template>
<div class="flex1 posi-rel pb-250 yet-adv" v-if="flag">
<scroller ref="scroller" :on-refresh="loadNewData" :on-infinite="loadMore" >
<div class="advert-item click w702 bg-fff" v-for="(item,idx) in data" :key="idx" @click="gotoAdvDetail(item.siteId)">
<div class="adv-left">
<div class="fs-30 color-000 flex-center">
<span class="label fs-24"
:class="{'label' : item.gradeInfo.gradeName=='一级屏','label2' : item.gradeInfo.gradeName=='二级屏','label3' : item.gradeInfo.gradeName=='三级屏','label4' : item.gradeInfo.gradeName=='四级屏'}"
>{{item.gradeInfo.gradeName}}
</span>
<span class="adv-title font-bold">{{item.siteName}}</span>
</div>
<div class="flex-center mt-15">
<div class="tag fs-24">{{item.businessName}}</div>
<div class="line"></div>
<div v-for="(item2,idx2) in item.deviceProp" :key="idx2" class="flex-center border-none">
<div class="tag fs-24">{{item2.value}}</div>
<div class="line-right"></div>
</div>
</div>
<div class="fs-24 color-333 mt-10">{{item.detailedAddress}}</div>
<div class="mt-24"><span class="coin fs-28">{{item.price}}币/秒</span><span class="qty fs-20 ml-24">日均曝光量{{item.dailyExposure}}</span></div>
</div>
<div class="adv-right">
<div class="adv-img">
<template v-if="item.picViewList[0]">
<img :src="item.picViewList[0].fileUrlView" alt="">
</template>
</div>
<div class="dist fs-20">{{item.distance}}</div>
</div>
</div>
</scroller>
</div>
</template>
<script>
import backTitleBar from "@/components/titleBar/backTitleBar";
import initData from "@/mixins/initData";
import { advAlreadyEnter, advWaitEnter } from "@/api/advMine";
export default {
name:'yetAdv',
mixins:[initData],
data() {
return {
yetadvList:[],
flag:false
}
},
methods: {
beforeInit() {
this.params = { page : this.page, limit: this.limit};
this.url = 'backstage/udianAdvertSiteInfo/xxx';
this.query = {
flag:1
}
for (var key in this.query){
if(this.query[key]){
this.params[key] = this.query[key]
}
}
return true
},
async getYetAdvData(){
let params = {
flag:1
}
let res = await advAlreadyEnter(params)
this.yetadvList = res.data.records
this.flag = true
console.log(res)
this.$emit("yetAdvData", this.yetadvList)
},
gotoAdvDetail(value){
console.log(value)
// this.$goRouter('advertDetail',{siteId : value})
}
},
created() {
this.getYetAdvData()
},
computed: {
yetadv(){
return this.yetadvList.length == 0
},
},
};
</script>
<style scoped lang="scss">
.yet-adv{
height:80vh;
// padding-bottom: 1.3rem;
}
.page-state{
position: relative;
// top: 0.88rem;
}
.advert-list{
background: #f5f5f5;
// padding-top: 2rem;
padding-bottom: 2rem;
// overflow: auto;
}
// 这部分是已入驻列表
.advert-item{
height: 2.43rem;
padding: 0.26rem 0.24rem 0;
margin-left: 0.24rem;
margin-bottom: 0.18rem;
display: flex;
justify-content: space-between;
border-radius: 0.18rem;
&:nth-child(2){
margin-top: 0.18rem;
}
.label{
width: 0.8rem;
height: .34rem;
line-height: 0.34rem;
text-align: center;
display: inline-block;
background: linear-gradient(90deg, #FECB00 0%, #FEBA00 100%);
border-radius: .08rem;
font-size: .2rem;
color: #fff;
}
.label2{
background: #C3D8E6;
color: #fff;
}
.label3{
background: #E0C19D;
color: #fff;
}
.label4{
background:#F5F5F5;
color: #333333;
}
.adv-title{
margin-left: 0.18rem;
width: 3.5rem;
display: inline-block;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
}
.tag{
color: #8C8C8C;
}
.line{
height: 0.19rem;
background: #8C8C8C;
margin: 0 0.23rem;
position: relative;
&::after{
content: '';
display: block;
position: absolute;
top: 0;
bottom: 0;
right: 0;
-webkit-transform: scale(0.5, 1);
transform: scale(0.5, 1);
border-right: 1px solid #8C8C8C;
}
&:last-child::after{
border: 0;
}
}
.coin{
color: $main-color;
}
.adv-img{
width: 1.6rem;
height: 1.6rem;
border-radius: 0.18rem;
img{
width: 100%;
height: 100%;
}
}
.dist{
color:#8C8C8C;
margin-top: 0.03rem;
text-align: right;
}
}
</style>
发送短信验证码
<template>
<div class="btn-code click" :class="{'disable': isGet}" @click="getCode">{{codeName}}</div>
</template>
<script>
// import { getCode } from "@/api/user";
import { getCode } from "@/api/advMine";
export default {
name: "msgCode",
props:{
mobileno:{
type:String,
default:''
}
},
data(){
return {
codeName: '获取验证码',
isGet: false,
timer: null,
num: "-1",
}
},
methods:{
getCode() {
if (this.isGet) {
return;
}
if (!this.mobileno) {
this.$toast("请输入手机号码");
return;
} else {
if (!this.mobileno.match(/^1\d{10}$/)) {
this.$toast("请输入正确的手机号码");
return;
}
}
this.setTimer();
getCode(this.mobileno, "inAgent").then(res => {
this.$toast("短信发送成功,请注意查收");
this.setTimer();
});
},
setTimer() {
let _this = this;
if (this.num < 0) {
this.isGet = true;
this.num = 60;
this.codeName = "重新获取(" + this.num + "s)";
}
if (this.num > 0) {
this.timer = setInterval(function() {
_this.num -= 1;
if (_this.num < 0) {
clearInterval(_this.timer);
_this.isGet = false;
_this.num = -1;
_this.codeName = "重新获取";
} else {
_this.codeName = "重新获取(" + _this.num + "s)";
}
}, 1000);
}
},
}
}
</script>
<style scoped lang="scss">
.btn-code{
width: 2rem;
height: .64rem;
background: linear-gradient(90deg, #FECB00 0%, #FEBA00 100%);
text-align: center;
line-height: .64rem;
color: #fff;
font-size: .24rem;
border-radius: .32rem;
}
.disable{
background: #ccc;
}
</style>
// 没封装时的代码
// async getCodeInfo(){
// if(this.getCode){
// this.numberCode = 60
// }
// this.getCode = false
// this.timer()
// // 发送请求获取验证码
// let params = {
// mobile: this.contactInformation.userPhone,
// tmplType: 'inAgent',
// }
// let res = await applyAdvMsg(params)
// // console.log(res)
// },
//发送手机验证码倒计时
// timer() {
// if (this.numberCode > 0) {
// this.numberCode--;
// if(this.numberCode < 10 && this.numberCode > 0 ){
// this.numberCode = "0" +this.numberCode
// }
// setTimeout(this.timer, 1000);
// } else{
// this.numberCode = 0;
// this.codetxt = "发送验证码";
// this.getCode = true
// }
// },
地址弹窗的封装
<template>
<div class="pick-area">
<div class="address-title color-333 fs-28 mt-20">联系地址</div>
<!-- 进度条 -->
<div class="pd-030 mt-40" v-show="isSelectCity">
<div class="fs-28 color-000" @click="selpro()"><span class="mr-24 circle"></span>{{prov}}</div>
<div class="shu-line"></div>
<div class="fs-28 color-000" @click="selcit()"><span class="mr-24" :class="[showcountry ? 'circle' : 'circle-hollow']"></span>{{city}}</div>
<div class="shu-line" v-show="showcountry"></div>
<div class="fs-28 color-000" @click="selcou()" v-show="showcountry"><span class="mr-24 circle-hollow" :class="[showcountry ? 'circle' : 'circle-hollow']"></span>{{country}}</div>
<div class="heng-line"></div>
</div>
<!-- 定位 -->
<div class="current-address pd-030 mt-40" v-show="showlocator" @click="getAddress()">
<div class="fs-28 color-000">当前定位</div>
<div class="address-detail pd-040 text-el mt-24 flex-center">
<span class="iconfont alifont iconzu275 fs-24 mr-20" style="color:#fecb00;"></span>
<!-- <div v-if="true">{{location}}</div> -->
<div class="fs-28 color-000" v-if="locationProvince == ''&& !errFlag">{{location}}</div>
<div v-if="locationProvince !== ''&& !errFlag">
<span class="fs-28 color-000">{{locationProvince}}</span>
<span class="fs-28 color-000 mg-010">{{locationCity}}</span>
<span class="fs-28 color-000">{{locationCountry}}</span>
</div>
<div class="fs-28 color-000" v-show="errFlag">{{errorLocation}}</div>
</div>
</div>
<!-- 热门城市 -->
<div class="hot-city pd-030 mt-40" v-show="showhotcity">
<div class="fs-28 color-8C8 mb-28">热门省份</div>
<div class="hot-city-box">
<div
class="hot-city-item fs-28 color-000"
v-for="(item,idx) in hotPro" :key="idx"
@click="selecthotcity(item.codename)"
>{{item.codename}}</div>
<!-- <div class="hot-city-item fs-28 color-000">北京北京北京</div>
<div class="hot-city-item fs-28 color-000">北京北</div> -->
</div>
</div>
<div class="wrapper">
<div class="select-city-area mt-10 pd-030" v-show="showprovinceList">
<div class="fs-28 color-8C8">选择省份/地区</div>
<div class="mt-25">
<div class="mb-48" v-for="(item,idx) in provinceList" :key="idx" @click="selectprovince(item.areaname)">
<!-- <span class="color-8C8 fs-28 mr-27">A</span> -->
<span class="color-000 fs-28">{{item.areaname}}</span>
</div>
</div>
</div>
<div class="select-city-area mt-10 pd-030" v-show="showcityList">
<div class="fs-28 color-8C8">选择城市</div>
<div class="mt-25">
<div class="mb-48" v-for="(item,idx) in cityList" :key="idx" @click="selectcity(item.areaname)">
<!-- <span class="color-8C8 fs-28 mr-27">A</span> -->
<span class="color-000 fs-28">{{item.areaname}}</span>
</div>
</div>
</div>
<div class="select-city-area mt-10 pd-030" v-show="showcountryList" >
<div class="fs-28 color-8C8">选择区/县</div>
<div class="mt-25">
<div class="mb-48" v-for="(item,idx) in countryList" :key="idx" @click="selectcountry(item.areaname)">
<!-- <span class="color-8C8 fs-28 mr-27">A</span> -->
<span class="color-000 fs-28">{{item.areaname}}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import {getArea, getHotProvince} from "@/api/advMine"
export default {
name: "pickAreaSelect",
data() {
return {
popupShow: false,
// 显示进度条
showlocator:true,
// 显示进度条中区县
showcountry:false,
// 显示热门城市
showhotcity:true,
hotPro:[],
flag: 1,
prov:'',
city:'请选择城市',
country:' 请选择区/县',
provID:'',
cityID:'',
countryID:'',
provinceList: [],
cityList: [],
countryList: [],
// 显示省市区列表
isSelectCity:false,
showprovinceList:true,
showcityList:false,
showcountryList:false,
// 定位的
location: '定位中...',
locationProvince:'',
locationCity:'',
locationCountry:'',
errFlag: false,
errorLocation:'',
};
},
created() {
// 获取用户定位
this.getLocation();
// 调用热门城市
this.getHotPro("hotProvince")
// 调用省列表
this.getAreaList("-1", "provinceList");
},
methods: {
// 进度条
selpro(){
console.log(1)
this.showhotcity = false
this.showprovinceList = true
this.showcityList = false
this.showcountryList = false
this.city = '请选择城市'
this.showcountry = false
},
selcit(){
console.log(2)
this.showprovinceList = false
this.showcityList = true
this.showcountryList = false
this.showcountry = true
this.country = ' 请选择区/县'
},
selcou(){
this.showprovinceList = false
this.showcityList = false
this.showcountryList = true
},
// 列表选择
selectprovince(value){
this.isSelectCity = true
this.showhotcity = false
this.showlocator = false
this.showprovinceList = false
this.showcityList = true
this.showcountryList = false
console.log(value)
let _this = this
this.provinceList.forEach((item, index)=>{
if(item.areaname == value){
this.prov = item.areaname
this.provID = item.areacode
this.getAreaList(item.areacode, "cityList");
}
})
},
selectcity(value){
this.isSelectCity = true
this.showhotcity = false
this.showlocator = false
this.showprovinceList = false
this.showcityList = false
this.showcountryList = true
this.showcountry = true
let _this = this
this.cityList.forEach((item, index)=>{
if(item.areaname == value){
this.city = item.areaname
this.cityID= item.areacode
this.getAreaList(item.areacode, "countryList");
}
})
},
selectcountry(value){
this.isSelectCity = true
this.showhotcity = false
this.showlocator = false
this.showprovinceList = false
this.showcityList = false
this.showcountryList = true
let _this = this
this.countryList.forEach((item, index)=>{
if(item.areaname == value){
this.country = item.areaname
this.countryID= item.areacode
}
})
// 取消弹框
let data= {
flag:false,
prov:this.prov,
city:this.city,
country:this.country,
provID:this.provID,
cityID: this.cityID,
countryID:this.countryID
}
this.$emit("cancelPopup", data);
},
// 选择热门城市
selecthotcity(value){
console.log(value)
this.isSelectCity = true
this.prov = value
this.hotPro.forEach(item=>{
if(item.codename == value){
this.getAreaList(item.code, "cityList");
this.showprovinceList = false
this.showcityList = true
}
})
if(this.flag == 0){
this.showlocator = true
this.showhotcity = true
this.flag = 1
}else{
this.showlocator = false
this.showhotcity = false
this.flag = 0
}
},
getHotPro(hotProvince){
let params = {
codetype: "hotProvince",
}
getHotProvince(params).then(res=>{
this.hotPro = res.data.records
console.log(this.hotPro)
})
},
getAreaList(pcode, name) {
let params = {
pcode: pcode || "-1",
};
getArea(params).then(res=>{
if(name == 'provinceList'){
this[name] = res.data;
// this.getAreaList(res.data[0].areacode,'cityList');
}else if(name == 'cityList'){
this[name] = res.data;
// this.getAreaList(res.data[0].areacode,'countryList');
}else if(name == 'countryList'){
this[name] = res.data;
// this.getAreaList(res.data[0].areacode,'countryList');
}
})
},
// 获取当前位置
getLocation() {
const _this = this;
_this.$toast("正在定位中...")
AMap.plugin('AMap.Geolocation', function () {
var geolocation = new AMap.Geolocation({
// 是否使用高精度定位,默认:true
enableHighAccuracy: true,
// 设置定位超时时间,默认:无穷大
timeout: 10000
});
geolocation.getCurrentPosition();
AMap.event.addListener(geolocation, 'complete', onComplete);
AMap.event.addListener(geolocation, 'error', onError);
function onComplete (value) {
// value是具体的定位信息
console.log('定位成功信息:', value);
console.log('定位成功信息:', value.addressComponent.province);
console.log('定位成功信息:', value.addressComponent.city);
console.log('定位成功信息:', value.addressComponent.district);
_this.address = value.addressComponent.city;
_this.locationProvince = value.addressComponent.province
_this.locationCity = value.addressComponent.city
_this.locationCountry = value.addressComponent.district
// 取消弹框
let data = {
flag:false,
prov:_this.locationProvince,
city: _this.locationCity,
country:_this.locationCountry,
provID:'',
cityID: '',
countryID:''
}
_this.$emit("cancelPopup", data);
}
function onError (value) {
// 定位出错
console.log('定位失败错误:', value);
// 调用IP定位
// _this.getLngLatLocation();
_this.errFlag = true
_this.errorLocation = "定位失败,请手动选择地址"
}
});
},
// 通过IP获取当前位置
getLngLatLocation () {
AMap.plugin('AMap.CitySearch', function () {
var citySearch = new AMap.CitySearch();
citySearch.getLocalCity(function (status, result) {
if (status === 'complete' && result.info === 'OK') {
// 查询成功,result即为当前所在城市信息
console.log('通过ip获取当前城市:', result);
// 逆向地理编码
AMap.plugin('AMap.Geocoder', function () {
var geocoder = new AMap.Geocoder({
// city 指定进行编码查询的城市,支持传入城市名、adcode 和 citycode
city: result.adcode
});
var lnglat = result.rectangle.split(';')[0].split(',');
geocoder.getAddress(lnglat, function (status, data) {
if (status === 'complete' && data.info === 'OK') {
// result为对应的地理位置详细信息
console.log(data);
}
});
});
}
});
});
},
// 点击获取定位
getAddress(){
this.errFlag = false
this.getLocation();
}
},
};
</script>
<style scoped lang="scss">
.pick-area {
height: 10.15rem;
overflow: hidden;
}
.address-title{
width: 100%;
text-align: center;
}
.address-detail{
width: 4.6rem;
height: 0.78rem;
border-radius: 0.42rem;
text-align: left;
line-height: 0.78rem;
background: #F5F5F5;
// display: flex;
}
.hot-city-box{
flex-wrap: wrap;
display: flex;
align-items: center;
justify-content: flex-start;
}
.hot-city-item{
padding: 0 0.35rem;
height: 0.58rem;
text-align: center;
line-height: 0.58rem;
background: #F5F5F5;
margin-bottom: 0.3rem;
border-radius: 0.3rem;
margin-right: 0.16rem;
}
.wrapper{
flex: 1;
height: 6rem;
overflow: auto;
}
.circle{
display: inline-block;
margin-bottom: 0.05rem;
width: 0.1rem;
height: 0.1rem;
border-radius: 50%;
background: $main-color;
}
.circle-hollow{
display: inline-block;
margin-bottom: 0.05rem;
width: 0.1rem;
height: 0.1rem;
border-radius: 50%;
border: 0.02rem solid $main-color;
}
.shu-line{
width: 1px;
height: 0.5rem;
background: $main-color;
margin-left: 0.05rem;
}
.heng-line{
width: 100%;
height: 1px;
background: #DDDDDD;
margin: 0.4rem 0;
}
</style>
<style lang="scss">
.pick-area .van-popup__close-icon--top-right{
top: 8px;
}
.pick-area .van-icon-cross::before{
color: #B3B3B3;
}
</style>
import request from "@/service/index"
import postJson from "@/service/postJson"
//获取地区信息列表
export function getArea(params) {
return request({
url: 'base/area',
method: 'get',
params
})
}
//获取地区信息列表
export function getHotProvince(params) {
return request({
url: 'base/code',
method: 'get',
params,
})
}
可编辑框组件
<template>
<div>
<div v-if="isEdit">
<input type="text" v-model='newVal'>
<button @click="save">保存</button>
<button @click="cancel">取消</button>
</div>
<div v-else @dblclick="edit">{{newVal}}</div>
</div>
</template>
<script>
export default {
name: 'EditTableField',
props:['val'],
data() {
return {
newVal:this.val,
bakup:null,
isEdit:false,
nameCom: 'EditTableField',
}
},
created(){
},
computed:{
},
methods:{
cancel(){
// 原来的值赋值于变量bakup
this.newVal = this.bakup
this.isEdit = false
},
save(){
this.isEdit = false
},
edit(){
// 修改的值赋值于变量newVal
this.bakup = this.newVal
// 拿到所有的父级下的子级元素
// console.log(this.$parent.$children)
console.log(this.$parent.$children.filter(item=>item.nameCom === 'EditTableField'))
// 让其他的编辑框都隐藏
this.$parent.$children.filter(item=>item.nameCom === 'EditTableField').forEach(item=>{
item.isEdit = false
})
this.isEdit = true
}
},
}
</script>
<style lang="scss" scoped>
</style>
使用
<EditTableField :val='obj.nickname'></EditTableField>
<EditTableField :val='obj.age'></EditTableField>
<EditTableField :val='obj.gender'></EditTableField>
单图片预览
<template>
<div>
<img class="image-small"
:src="imgSrc"
alt=""
@click="isPreview=true"
/>
<div class="preview-img" v-if="isPreview" @click="isPreview=false">
<img class="image-big"
:src="imgSrc"
alt=""
@click.stop="handleClick"
/>
</div>
</div>
</template>
<script>
export default {
name: "SingleImagePreview",
props:{
imgSrc:{
type:String,
defalut:''
}
},
data() {
return {
isPreview:false,
showImg:true
};
},
created() {},
computed: {},
methods: {
handleClick(e){
console.log(e)
// 阻止事件冒泡 或者用@click.stop
e.stopPropagation();
// this.isPreview=false;
}
},
};
</script>
<style lang="scss" scoped>
.image-small{
width: 200px;
height: 100px;
}
.image-big{
width: 500px;
height: 300px;
}
.preview-img {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(255,255,255, 0.2);
z-index: 99;
display: flex;
align-items: center;
justify-content: center;
}
</style>
使用
<ImgPreview :imgSrc='imgSrc'></ImgPreview>
多图片预览
<template>
<div>
<img
class="image-small"
:src="img"
alt=""
@click="visibleImg(img)"
v-for="(img,index) in imgSrcList"
:key='index'
/>
<div
class="preview-img"
v-if="isPreview"
@click="isPreview = false"
>
<img class="image-big" :src="currentImg" alt="" @click.stop="handleClick" />
</div>
</div>
</template>
<script>
export default {
name: "MoreImagePreview",
props:{
imgSrcList:{
type:Array,
default:()=>{
return []
}
}
},
data() {
return {
currentImg:'',
isPreview:false
};
},
created() {},
computed: {},
methods: {
visibleImg(img){
this.isPreview = true
this.currentImg = img
},
handleClick(e){
console.log(e)
// 阻止事件冒泡 或者用@click.stop
e.stopPropagation();
// this.isPreview=false;
}
},
};
</script>
<style lang="scss" scoped>
.image-small{
width: 200px;
height: 100px;
}
.image-big{
width: 500px;
height: 300px;
}
.preview-img {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(255,255,255, 0.2);
z-index: 99;
display: flex;
align-items: center;
justify-content: center;
}
</style>
抽屉组件
<template>
<div class="drawer" v-if="value" @click="innerVisible=false">
<div class="container" :style="{width,height}" v-if="value" :class="position" @click.stop>
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: 'Drawer',
props:{
value:{
type:Boolean,
default:false
},
width:{
type:String,
// default:'300px'
},
height:{
type:String,
// default:'300px'
},
position:{
type:String,
default:'left'
}
},
data() {
return {
innerVisible:this.value
}
},
watch:{
value(){
this.innerVisible = this.value
},
innerVisible(val){
this.$emit('input',val)
}
},
created(){
},
computed:{
},
methods:{
},
}
</script>
<style lang="scss" scoped>
.container{
position: fixed;
background: #fff;
z-index: 999;
}
.drawer{
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 9;
}
.container.left{
position: absolute;
height: 100%;
left:0;
top: 0;
}
.container.right{
position: absolute;
height: 100%;
right:0;
top: 0;
}
.container.top{
position: absolute;
width: 100%;
left:0;
top: 0;
}
.container.bottom{
position: absolute;
width: 100%;
left:0;
bottom: 0;
}
</style>
使用
<template>
<div>
<Drawer v-model="visible" :position='pos' height="300px" width="500px">
<div>内容</div>
</Drawer>
<button @click="showDrawer()">按钮</button>
</div>
</template>
<script>
import Drawer from '../../components/Drawer'
export default {
name: "blogs",
components: {
Drawer
},
data() {
return {
// pos:'left',
// pos:'right',
pos:'top',
// pos:'bottom',
};
},
created() {},
computed: {},
methods: {
showDrawer(){
this.visible = !this.visible
}
},
};
</script>
<style lang="scss" scoped>
</style>
日历组件
分页组件
<template>
<div>
<ul class="page flex">
<li v-show="current>1" @click="current=1">first</li>
<li :class="{disaled:current==1}" @click="current--" >prev</li>
<li class="item"
:class="{active:item===current}"
v-for='(item,index) in pageCount' :key="index"
@click="current=item"
>{{item}}</li>
<li :class="{disaled:current==pageCount}" @click="current++">nest</li>
<li v-show="current<pageCount" @click="current=pageCount">last</li>
</ul>
</div>
</template>
<script>
export default {
name: 'Pagination',
props:{
total:{
type:Number,
required:true,
default:10
},
pageSize:{
type:Number,
default:10
},
value:{
type:Number,
defalult:1
}
},
data() {
return {
current:this.value,
}
},
created(){
},
computed:{
pageCount(){
return Math.ceil(this.total/this.pageSize)
}
},
methods:{
},
watch: {
current(val){
if(val < 1) this.current = 1
else if(val > this.pageCount) this.current = this.pageCount
// 把带有当前页数传递出去
this.$emit('input',this.current)
}
},
}
</script>
<style lang="scss" scoped>
.page{
display: flex;
}
.page li{
border: 1px solid red;
width: 30px;
height: 30px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
margin: 0 2px;
-moz-user-select: none;
cursor: pointer;
}
.page li:not(.item){
width: 50px;
}
.page li.active{
background: red;
color: #fff;
}
.page li.disaled{
background: #ccc;
color: #eee;
border: 1px solid #eee;
cursor: not-allowed;
}
</style>
使用
<template>
<div>
<ul>
<li v-for="(item,index) in list" :key='index'>{{item.areaName}}</li>
</ul>
<Pagination :total='total' :page-size='pageSize' v-model="pageIndex"></Pagination>
<!-- 这里的change是为了拿到里面的页数 用v-model也可以 -->
{{pageIndex}}
<!-- <Pagination :total='15' :page-size='5' @change='pageIndex=$event'></Pagination> -->
<!-- <Pagination :total='15' :page-size='5'></Pagination>
<Pagination :total='20'></Pagination> -->
</div>
</template>
<script>
import {getPageData,getCityArea} from '../../api/test'
import Pagination from '../../components/Pagination';
export default {
name: "blogs",
components: {
Pagination
},
data() {
return {
pageIndex :1,
pageSize:50,
total:'',
list:[]
};
},
computed: {},
methods: {
async getData(){
let params = {
// 页数
page : this.pageIndex,
limit: this.pageSize,
city: '',
district:'',
r: '',
lon: '',
lat:'',
tmt: '',
secondTmt: '',
dayExposure: '',
ratio: '',
}
let {data} = await getPageData(params)
// let data = await getCityArea()
console.log(data)
this.list = data.advertInfoList
this.total = data.total
}
},
created() {
this.getData()
},
watch: {
pageIndex(){
this.getData()
}
},
};
</script>
<style lang="scss" scoped>
</style>