11.21
|
@ -572,7 +572,7 @@
|
|||
|
||||
.borContent {
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
height: 140px;
|
||||
overflow: scroll;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
@ -0,0 +1,225 @@
|
|||
<template>
|
||||
<view class="progress-bar">
|
||||
<view class="progress-bar-title" :style="titleStyle">{{ title }}</view>
|
||||
|
||||
<view v-if="!secondValue" class="bar">
|
||||
<view
|
||||
class="bar-single"
|
||||
:style="{
|
||||
backgroundColor: backColor,
|
||||
borderRadius: barRadius,
|
||||
height: barHeight,
|
||||
lineHeight: barHeight
|
||||
}"
|
||||
>
|
||||
<view
|
||||
class="bar-content"
|
||||
:style="{ width: firstWidth, minWidth: minWidth }"
|
||||
>
|
||||
<view
|
||||
:class="{ 'bar-content-inner': animationReal }"
|
||||
:style="{ background: contentColor }"
|
||||
>
|
||||
<text
|
||||
v-if="textPosition === 'inside'"
|
||||
class="bar-text"
|
||||
:style="{ color: textColor, fontSize: textSize }"
|
||||
>
|
||||
{{ singleText }}
|
||||
</text>
|
||||
<text v-else class="placeholder">1</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
v-if="textPosition === 'outside'"
|
||||
class="bar-text"
|
||||
:style="{ color: textColor, fontSize: textSize }"
|
||||
>
|
||||
{{ singleText }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="bar">
|
||||
<view
|
||||
class="bar-single bar-double"
|
||||
:style="{
|
||||
backgroundColor: backColor,
|
||||
borderRadius: barRadius,
|
||||
height: barHeight,
|
||||
lineHeight: barHeight
|
||||
}"
|
||||
>
|
||||
<view
|
||||
class="bar-content"
|
||||
:style="{ width: firstWidth, minWidth: minWidth }"
|
||||
>
|
||||
<view
|
||||
:class="{ 'bar-content-inner': animationReal }"
|
||||
:style="{ background: contentColor }"
|
||||
>
|
||||
<text
|
||||
v-if="textPosition !== 'none'"
|
||||
class="bar-text"
|
||||
:style="{ color: textColor, fontSize: textSize }"
|
||||
>
|
||||
{{ singleText }}
|
||||
</text>
|
||||
<text v-else class="placeholder">1</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bar-interval placeholder">1</view>
|
||||
<view
|
||||
class="bar-content"
|
||||
:style="{ width: secondWidth, minWidth: minWidth }"
|
||||
>
|
||||
<view
|
||||
:class="{ 'bar-content-inner': animationReal }"
|
||||
:style="{ background: contentColor2 }"
|
||||
>
|
||||
<text
|
||||
v-if="textPosition !== 'none'"
|
||||
class="bar-text"
|
||||
:style="{ color: textColor, fontSize: textSize }"
|
||||
>
|
||||
{{ doubleText }}
|
||||
</text>
|
||||
<text v-else class="placeholder">1</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import props from './props.js';
|
||||
|
||||
export default {
|
||||
mixins: [props],
|
||||
data() {
|
||||
return {
|
||||
singleText: '',
|
||||
doubleText: '',
|
||||
firstWidth: '',
|
||||
secondWidth: '',
|
||||
animationReal: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
firstValue: {
|
||||
handler(newVal) {
|
||||
// 进度条长度改变后,重新加载动画
|
||||
if (this.animationReal) {
|
||||
this.animationReal = false;
|
||||
this.firstWidth = this.manageWidth(0, 'singleText');
|
||||
setTimeout(() => {
|
||||
this.animationReal = true;
|
||||
this.firstWidth = this.manageWidth(
|
||||
newVal,
|
||||
'singleText'
|
||||
);
|
||||
}, 100);
|
||||
} else {
|
||||
this.firstWidth = this.manageWidth(newVal, 'singleText');
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
secondValue: {
|
||||
handler(newVal) {
|
||||
if (this.animationReal) {
|
||||
this.animationReal = false;
|
||||
this.secondWidth = this.manageWidth(0, 'doubleText');
|
||||
setTimeout(() => {
|
||||
this.animationReal = true;
|
||||
this.secondWidth = this.manageWidth(
|
||||
newVal,
|
||||
'doubleText'
|
||||
);
|
||||
}, 100);
|
||||
} else {
|
||||
this.secondWidth = this.manageWidth(newVal, 'doubleText');
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
animation: {
|
||||
handler(newVal) {
|
||||
this.animationReal = newVal;
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
manageWidth(target, flag) {
|
||||
let value =
|
||||
((target / this.total) * 100).toFixed(this.precision) + '%';
|
||||
this.percentum ? (this[flag] = value) : (this[flag] = target);
|
||||
// 减去间隔所占宽度
|
||||
this.secondValue ? (value = `calc(${value} - 1px)`) : '';
|
||||
return value;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.progress-bar {
|
||||
&-title {
|
||||
font-size: 4vw;
|
||||
font-weight: bold;
|
||||
margin-bottom: 1vw;
|
||||
}
|
||||
|
||||
.bar {
|
||||
/* #ifdef H5 */
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&-single {
|
||||
overflow: hidden;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
&-double {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
&-content {
|
||||
&-inner {
|
||||
animation: addWidth 1.5s 0s;
|
||||
}
|
||||
}
|
||||
|
||||
&-interval {
|
||||
background-color: #ffffff;
|
||||
width: 2px;
|
||||
}
|
||||
|
||||
&-text {
|
||||
margin: 0 5px;
|
||||
font-size: 4vw;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
color: rgba(0, 0, 0, 0);
|
||||
font-size: 4vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes addWidth {
|
||||
from {
|
||||
width: 0;
|
||||
}
|
||||
to {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,84 @@
|
|||
export default {
|
||||
props: {
|
||||
// 标题
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
// 标题样式
|
||||
titleStyle: {
|
||||
type: String,
|
||||
},
|
||||
// 总数,计算的分母
|
||||
total: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
// 第一个值
|
||||
firstValue: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
// 第二个值
|
||||
secondValue: {
|
||||
type: Number
|
||||
},
|
||||
// 进度条背景颜色
|
||||
backColor: {
|
||||
type: String,
|
||||
default: '#f2f2f2'
|
||||
},
|
||||
// 进度条高度
|
||||
barHeight: {
|
||||
type: String
|
||||
},
|
||||
// 进度条圆角
|
||||
barRadius: {
|
||||
type: String
|
||||
},
|
||||
// 进度条颜色
|
||||
contentColor: {
|
||||
type: String,
|
||||
default: '#4cd964'
|
||||
},
|
||||
// 进度条2颜色
|
||||
contentColor2: {
|
||||
type: String,
|
||||
default: '#f0ad4e'
|
||||
},
|
||||
// 进度条最小长度
|
||||
minWidth: {
|
||||
type: String
|
||||
},
|
||||
// 文字大小
|
||||
textSize: {
|
||||
type: String,
|
||||
default: '16px'
|
||||
},
|
||||
// 文字颜色
|
||||
textColor: {
|
||||
type: String,
|
||||
default: '#333333'
|
||||
},
|
||||
// 文字内显、外显、不显,可选值:inside/outside/none
|
||||
textPosition: {
|
||||
type: String,
|
||||
default: 'inside'
|
||||
},
|
||||
// 展示百分比或值
|
||||
percentum: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 百分比精确到小数点后几位
|
||||
precision: {
|
||||
type: [String, Number],
|
||||
default: 2
|
||||
},
|
||||
// 是否选择动画加载
|
||||
animation: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -151,6 +151,12 @@
|
|||
// 选择船输入框
|
||||
shipInput(e) {
|
||||
this.shipValue = e
|
||||
if (e == "") {
|
||||
this.vvyId = ""
|
||||
this.vvyShip = ""
|
||||
this.shipId = ""
|
||||
this.shipName = ""
|
||||
}
|
||||
this.getShip()
|
||||
},
|
||||
// 获取船舶
|
||||
|
@ -254,7 +260,7 @@
|
|||
line-height: 35px;
|
||||
padding-left: 10px;
|
||||
margin-right: 15px;
|
||||
margin-top: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
|
@ -265,7 +271,7 @@
|
|||
color: #fff;
|
||||
background-color: #0067CF;
|
||||
margin-right: 10px;
|
||||
margin-top: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -151,6 +151,12 @@
|
|||
// 选择船输入框
|
||||
shipInput(e) {
|
||||
this.shipValue = e
|
||||
if (e == "") {
|
||||
this.vvyId = ""
|
||||
this.vvyShip = ""
|
||||
this.shipId = ""
|
||||
this.shipName = ""
|
||||
}
|
||||
this.getShip()
|
||||
},
|
||||
// 获取船舶
|
||||
|
@ -206,10 +212,6 @@
|
|||
this.lotusLoadingData.isShow = false
|
||||
this.total = res.data.data.total
|
||||
this.itemList.push(...res.data.data.records)
|
||||
this.vvyId = ""
|
||||
this.vvyShip = ""
|
||||
this.shipId = ""
|
||||
this.shipName = ""
|
||||
}
|
||||
})
|
||||
},
|
||||
|
@ -265,7 +267,7 @@
|
|||
line-height: 35px;
|
||||
padding-left: 10px;
|
||||
margin-right: 15px;
|
||||
margin-top: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
|
@ -276,7 +278,7 @@
|
|||
color: #fff;
|
||||
background-color: #0067CF;
|
||||
margin-right: 10px;
|
||||
margin-top: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,8 +29,8 @@
|
|||
<text>板车照片</text>
|
||||
</view>
|
||||
<view class="picture">
|
||||
<template v-for="item in boardCarPhotos">
|
||||
<image :key="item" :src="item"></image>
|
||||
<template v-for="(item,index) in boardCarPhotos">
|
||||
<image :key="item" :src="item" @click="clickImg(boardCarPhotos,index)"></image>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
|
@ -40,8 +40,8 @@
|
|||
<text>板车车牌照</text>
|
||||
</view>
|
||||
<view class="picture">
|
||||
<template v-for="item in boardCarList">
|
||||
<image :key="item" :src="item"></image>
|
||||
<template v-for="(item,index) in boardCarList">
|
||||
<image :key="item" :src="item" @click="clickImg(boardCarList,index)"></image>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
|
@ -122,8 +122,8 @@
|
|||
<text>质损照片</text>
|
||||
</view>
|
||||
<view class="picture">
|
||||
<template v-for="item in qualityDamage">
|
||||
<image :key="item" :src="item"></image>
|
||||
<template v-for="(item,index) in qualityDamage">
|
||||
<image :key="item" :src="item" @click="clickImg(qualityDamage,index)"></image>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
|
@ -133,8 +133,8 @@
|
|||
<text>车架号图片</text>
|
||||
</view>
|
||||
<view class="picture">
|
||||
<template v-for="item in carFrameNumber">
|
||||
<image :key="item" :src="item"></image>
|
||||
<template v-for="(item,index) in carFrameNumber">
|
||||
<image :key="item" :src="item" @click="clickImg(carFrameNumber,index)"></image>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
|
@ -147,9 +147,6 @@
|
|||
<image :src="signImg"></image>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="btnList">
|
||||
<van-button type="default" @click="cancel">取消</van-button>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
@ -248,12 +245,13 @@
|
|||
}
|
||||
})
|
||||
},
|
||||
// 取消
|
||||
cancel() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/quality/index'
|
||||
// 点击图片放大
|
||||
clickImg(urlList, index) {
|
||||
uni.previewImage({
|
||||
current: index,
|
||||
urls: urlList,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -367,17 +365,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btnList {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
/deep/ .van-button {
|
||||
margin: 30px 0;
|
||||
width: 120px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -431,6 +431,7 @@
|
|||
success: (res) => {
|
||||
if (res.statusCode == 200) {
|
||||
this.infoData = res.data.data
|
||||
console.log(this.infoData)
|
||||
// 获取板车照片
|
||||
this.bcPhoto = this.infoData.boardCarPhotos
|
||||
this.infoData.boardCarPhotos.forEach(v => {
|
||||
|
@ -1307,6 +1308,7 @@
|
|||
this.carPhoto2.push(e.tempFiles[0])
|
||||
let data = JSON.parse(res.data).data
|
||||
this.carPhoto.push(data)
|
||||
console.log(this.carPhoto)
|
||||
},
|
||||
})
|
||||
},
|
||||
|
@ -1345,6 +1347,12 @@
|
|||
if (this.current2 != '其他') {
|
||||
this.other = ""
|
||||
}
|
||||
let editComplete = ""
|
||||
if (this.type == 'add') {
|
||||
editComplete = "1"
|
||||
} else if (this.type == "edit") {
|
||||
editComplete = "0"
|
||||
}
|
||||
let editReqDTO = {
|
||||
"boardCarLicensePlates": this.bcLicense, // 板车车牌照
|
||||
"boardCarPhotos": this.bcPhoto, // 板车照片
|
||||
|
@ -1354,7 +1362,7 @@
|
|||
"dsOther": this.other2, // 损伤情况其他备注
|
||||
"dispList": this.checkData3, // 处置情况
|
||||
"dpsOther": this.other3, // 处置情况其他备注
|
||||
"editComplete": "1", // pad提交传 1 编辑完毕 扫描提交的数据不全不给后台展示
|
||||
"editComplete": editComplete, // pad提交传 1 编辑完毕 扫描提交的数据不全不给后台展示
|
||||
"godId": this.godId, // 货物id
|
||||
"goodsType": this.carId, // 车型id
|
||||
"linkFeedback": this.feed, // 客户反馈
|
||||
|
|
|
@ -376,7 +376,7 @@
|
|||
background: #FAFAFA;
|
||||
border-top: 1px solid #EEEEEE;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
padding: 6px 10px;
|
||||
padding: 10px;
|
||||
position: fixed;
|
||||
top: 66px;
|
||||
z-index: 995;
|
||||
|
@ -458,7 +458,7 @@
|
|||
justify-content: space-between;
|
||||
font-family: PingFangSC-Regular;
|
||||
font-size: 14px;
|
||||
color: #23262E;
|
||||
color: #23262E;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.headTop {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<canvas class="mycanvas" canvas-id="mycanvas" @touchstart="touchstart" @touchmove="touchmove"
|
||||
@touchend="touchend" disable-scroll="true"></canvas>
|
||||
<view class="canvasBg">
|
||||
<image src="../../static/images/zs5.jpg" mode=""></image>
|
||||
<image src="../../static/images/zs2.png" mode=""></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="sigh-btns">
|
||||
|
@ -51,28 +51,31 @@
|
|||
methods: {
|
||||
init() {
|
||||
this.ctx = uni.createCanvasContext('mycanvas', this); //创建绘图对象
|
||||
this.ctx.setFillStyle('#ff0000');
|
||||
//设置画笔样式
|
||||
this.ctx.lineWidth = 4;
|
||||
this.ctx.lineCap = 'round';
|
||||
this.ctx.lineJoin = 'round';
|
||||
var that = this
|
||||
uni.getSystemInfo({
|
||||
success: function(res) {
|
||||
that.ctx.drawImage("../../static/images/zs5.jpg", 0, 0, 1000, 222)
|
||||
that.width = res.windowWidth;
|
||||
that.height = res.windowHeight;
|
||||
uni.getImageInfo({
|
||||
src: '../../static/images/zs2.png',
|
||||
success(res1) {
|
||||
//填充白色背景图片
|
||||
that.ctx.drawImage('../../static/images/zs2.png', 0, 0, 624, 224);
|
||||
let pattern = that.ctx.createPattern(img, 'no-repeat')
|
||||
that.ctx.fillStyle = pattern;
|
||||
},
|
||||
fail() {
|
||||
console.log("fail");
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
//触摸开始,获取到起点
|
||||
touchstart: function(e) {
|
||||
let startX = e.changedTouches[0].x;
|
||||
let startY = e.changedTouches[0].y;
|
||||
let startPoint = {
|
||||
X: startX,
|
||||
Y: startY
|
||||
X: startX / 1.75,
|
||||
Y: startY / 1.75
|
||||
};
|
||||
|
||||
/* **************************************************
|
||||
|
@ -89,8 +92,8 @@
|
|||
let moveX = e.changedTouches[0].x;
|
||||
let moveY = e.changedTouches[0].y;
|
||||
let movePoint = {
|
||||
X: moveX,
|
||||
Y: moveY
|
||||
X: moveX / 1.75,
|
||||
Y: moveY / 1.75
|
||||
};
|
||||
this.points.push(movePoint); //存点
|
||||
let len = this.points.length;
|
||||
|
@ -115,6 +118,8 @@
|
|||
let point1 = this.points[0];
|
||||
let point2 = this.points[1];
|
||||
this.points.shift();
|
||||
this.ctx.strokeStyle = 'red';
|
||||
this.ctx.lineWidth = 2;
|
||||
this.ctx.moveTo(point1.X, point1.Y);
|
||||
this.ctx.lineTo(point2.X, point2.Y);
|
||||
this.ctx.stroke();
|
||||
|
@ -130,7 +135,7 @@
|
|||
//清空画布
|
||||
handleReset: function() {
|
||||
var that = this
|
||||
that.ctx.clearRect(0, 0, 1000, 222);
|
||||
that.ctx.clearRect(0, 0, 624, 224);
|
||||
that.ctx.draw(true);
|
||||
tempPoint = [];
|
||||
that.init()
|
||||
|
@ -151,30 +156,6 @@
|
|||
uni.navigateBack({
|
||||
delta: 1
|
||||
});
|
||||
// let timestamp = new Date().getTime();
|
||||
// let sunumber = Math.floor(Math.random() * 999);
|
||||
// var file = that.base64ToFile(that.url, timestamp + sunumber)
|
||||
// uni.uploadFile({
|
||||
// url: `${that.$local}/api/file/upload`, //上传图片api
|
||||
// header: {
|
||||
// 'Authorization': `Bearer ${that.loginObj.access_token}`
|
||||
// },
|
||||
// file: file,
|
||||
// fileType: 'image',
|
||||
// name: 'file',
|
||||
// success: (res) => {
|
||||
// console.log(res)
|
||||
// console.log(JSON.parse(res.data))
|
||||
// that.signImg = JSON.parse(res.data).data.filePath
|
||||
// console.log(that.signImg)
|
||||
// // uni.navigateBack({
|
||||
// // delta: 1
|
||||
// // });
|
||||
// },
|
||||
// fail: (err) => {
|
||||
// console.log(err)
|
||||
// }
|
||||
// })
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error)
|
||||
|
@ -216,6 +197,7 @@
|
|||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
margin-top: 48px;
|
||||
}
|
||||
|
||||
.sign-view {
|
||||
|
@ -228,7 +210,7 @@
|
|||
}
|
||||
|
||||
.btn {
|
||||
margin: 10px 10px 0;
|
||||
margin: 100px 10px 0;
|
||||
}
|
||||
|
||||
/deep/.van-button {
|
||||
|
@ -240,17 +222,19 @@
|
|||
.mycanvas {
|
||||
margin: auto 0rpx;
|
||||
background-color: transparent;
|
||||
margin-top: 80px;
|
||||
width: 1000px;
|
||||
height: 222px;
|
||||
margin-top: 130px;
|
||||
width: 624px;
|
||||
height: 224px;
|
||||
z-index: 999;
|
||||
transform: scale(1.75)
|
||||
}
|
||||
|
||||
.canvasBg {
|
||||
width: 1000px;
|
||||
height: 222px;
|
||||
width: 624px;
|
||||
height: 224px;
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
top: 130px;
|
||||
transform: scale(1.75)
|
||||
}
|
||||
|
||||
.canvsborder {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</view>
|
||||
<view class="li">
|
||||
<p>贸易类型:</p>
|
||||
<text>{{shipInfo.spmTradeName}}</text>
|
||||
<text>{{spmTradeName}}</text>
|
||||
</view>
|
||||
<view class="li">
|
||||
<p><text class="required" v-if="obj.state != 'look'">*</text>作业日期:</p>
|
||||
|
@ -87,6 +87,7 @@
|
|||
|
||||
vvyInfo: [],
|
||||
importExportFlagName: "",
|
||||
spmTradeName: "",
|
||||
|
||||
// 下拉数据
|
||||
optionData: [],
|
||||
|
@ -165,6 +166,7 @@
|
|||
this.vvyInfo.forEach(v => {
|
||||
if (v.vvyId == e) {
|
||||
this.importExportFlagName = v.importExportFlagName
|
||||
this.spmTradeName = v.tradeTypeName
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
@ -101,7 +101,7 @@
|
|||
],
|
||||
otherListh: [{
|
||||
name: "安全巡检",
|
||||
url: "",
|
||||
url: "patrol",
|
||||
imgUrl: "../../static/images/shipWork/aqxj.png",
|
||||
tableName: "safetyInspectionRespList",
|
||||
},
|
||||
|
|
|
@ -85,10 +85,10 @@
|
|||
</template>
|
||||
<o-empty v-else height="70vh" bg="#f5f6fa" />
|
||||
</view>
|
||||
<view class="pageBox" v-if="itemList.length > 0">
|
||||
<!-- <view class="pageBox" v-if="itemList.length > 0">
|
||||
<uni-pagination :show-icon="true" :total="total" :pageSize="pageSize" :current="current"
|
||||
@change="changePage" />
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
<LotusLoading :lotusLoadingData="lotusLoadingData"></LotusLoading>
|
||||
|
@ -133,7 +133,7 @@
|
|||
vtpList: [],
|
||||
// 分页
|
||||
total: 0,
|
||||
pageSize: 6,
|
||||
pageSize: 12,
|
||||
current: 1,
|
||||
|
||||
// 下拉数据
|
||||
|
@ -197,6 +197,11 @@
|
|||
HeadInfo,
|
||||
LotusLoading
|
||||
},
|
||||
onReachBottom() {
|
||||
this.current++
|
||||
this.initData()
|
||||
this.lotusLoadingData.isShow = true
|
||||
},
|
||||
onBackPress(options) {
|
||||
// 触发返回就会调用此方法,这里实现的是禁用物理返回,顶部导航栏的自定义返回 uni.navigateBack 仍可使用
|
||||
if (options.from == 'backbutton') {
|
||||
|
@ -254,8 +259,9 @@
|
|||
method: 'GET', //请求方式,必须为大写
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
this.lotusLoadingData.isShow = false
|
||||
this.total = res.data.data.total
|
||||
this.itemList = res.data.data.records
|
||||
this.itemList.push(...res.data.data.records)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -335,7 +341,6 @@
|
|||
},
|
||||
// 船舶基本信息
|
||||
shipDataInfo(info, vtpId) {
|
||||
console.log(vtpId)
|
||||
console.log(info)
|
||||
let date = new Date().getTime()
|
||||
let webId = uuidv4()
|
||||
|
@ -398,6 +403,7 @@
|
|||
vvyIds.push(item.inVvyId)
|
||||
vvyIds.push(item.outVvyId)
|
||||
}
|
||||
console.log(vvyIds)
|
||||
uni.request({
|
||||
url: `${this.$local}/api/shipOperate/download?vvyIds=${vvyIds}`,
|
||||
header: {
|
||||
|
@ -994,11 +1000,18 @@
|
|||
}
|
||||
|
||||
.content {
|
||||
padding: 16px;
|
||||
min-height: calc(100vh - 68px);
|
||||
|
||||
.form {
|
||||
width: 100%;
|
||||
background: #f5f6fa;
|
||||
display: flex;
|
||||
height: ;
|
||||
position: fixed;
|
||||
top: 66px;
|
||||
right: 0;
|
||||
z-index: 995;
|
||||
padding: 16px;
|
||||
|
||||
.select {
|
||||
width: 200px;
|
||||
|
@ -1028,6 +1041,8 @@
|
|||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
padding: 16px;
|
||||
margin-top: 30px;
|
||||
|
||||
/deep/.o-empty {
|
||||
width: 100%;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</view>
|
||||
<view class="li">
|
||||
<p>贸易类型:</p>
|
||||
<text>{{shipInfo.spmTradeName}}</text>
|
||||
<text>{{spmTradeName}}</text>
|
||||
</view>
|
||||
<view class="li">
|
||||
<p><text class="required" v-if="obj.state != 'look'">*</text>作业日期:</p>
|
||||
|
@ -128,6 +128,7 @@
|
|||
|
||||
vvyInfo: [],
|
||||
importExportFlagName: "",
|
||||
spmTradeName: "",
|
||||
|
||||
// 下拉数据
|
||||
optionData: [],
|
||||
|
@ -211,6 +212,7 @@
|
|||
this.vvyInfo.forEach(v => {
|
||||
if (v.vvyId == e) {
|
||||
this.importExportFlagName = v.importExportFlagName
|
||||
this.spmTradeName = v.tradeTypeName
|
||||
}
|
||||
})
|
||||
},
|
||||
|
@ -352,7 +354,7 @@
|
|||
line-height: 35px;
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
|
||||
/deep/.is-input-border {
|
||||
margin-top: 8px;
|
||||
height: 40px;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</view>
|
||||
<view class="li">
|
||||
<p>贸易类型:</p>
|
||||
<text>{{shipInfo.spmTradeName}}</text>
|
||||
<text>{{spmTradeName}}</text>
|
||||
</view>
|
||||
<view class="li">
|
||||
<p><text class="required" v-if="obj.state != 'look'">*</text>作业日期:</p>
|
||||
|
@ -72,6 +72,7 @@
|
|||
|
||||
vvyInfo: [],
|
||||
importExportFlagName: "",
|
||||
spmTradeName: "",
|
||||
|
||||
// 下拉数据
|
||||
optionData: [],
|
||||
|
@ -148,6 +149,7 @@
|
|||
this.vvyInfo.forEach(v => {
|
||||
if (v.vvyId == e) {
|
||||
this.importExportFlagName = v.importExportFlagName
|
||||
this.spmTradeName = v.tradeTypeName
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</view>
|
||||
<view class="li">
|
||||
<p>贸易类型:</p>
|
||||
<text>{{shipInfo.spmTradeName}}</text>
|
||||
<text>{{spmTradeName}}</text>
|
||||
</view>
|
||||
<view class="li">
|
||||
<p><text class="required" v-if="obj.state != 'look'">*</text>泊位:</p>
|
||||
|
@ -106,6 +106,7 @@
|
|||
|
||||
vvyInfo: [],
|
||||
importExportFlagName: "",
|
||||
spmTradeName: "",
|
||||
|
||||
// 下拉数据
|
||||
optionData: [],
|
||||
|
@ -214,6 +215,7 @@
|
|||
this.vvyInfo.forEach(v => {
|
||||
if (v.vvyId == e) {
|
||||
this.importExportFlagName = v.importExportFlagName
|
||||
this.spmTradeName = v.tradeTypeName
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
<view class="liInfo">
|
||||
<p>类型:{{item.type}}</text></p>
|
||||
<p>贸易类型:{{item.tradeTypeName}}</p>
|
||||
<p>进出口:{{item.importExportFlagName}}</p>
|
||||
<p>进出口:{{item.importExportName}}</p>
|
||||
</view>
|
||||
<view class="status didNot">
|
||||
<!-- <view class="status didNot">
|
||||
<p>未上传</p>
|
||||
</view>
|
||||
</view> -->
|
||||
<!-- <view class="status success">
|
||||
<p>已上传</p>
|
||||
</view>
|
||||
|
@ -38,40 +38,68 @@
|
|||
data() {
|
||||
return {
|
||||
title: "船只 - 安全巡检",
|
||||
vtpId: "",
|
||||
shipInfo: {},
|
||||
infoList: []
|
||||
infoList: [],
|
||||
portObj: {},
|
||||
loginObj: {},
|
||||
vvyList: "",
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// this.shipInfo = uni.getStorageSync('shipInfo')
|
||||
// this.infoList = uni.getStorageSync('addPatrolArr')
|
||||
this.executeSql1('safetyInspectionRespList')
|
||||
this.executeSql1('shipInfoTable')
|
||||
let title = uni.getStorageSync('shipWorkTitle')
|
||||
this.title = `${title} / 安全巡检`
|
||||
this.portObj = uni.getStorageSync('portObj')
|
||||
this.loginObj = uni.getStorageSync('loginObj')
|
||||
this.vtpId = uni.getStorageSync('vtpId')
|
||||
this.executeSql1('voyageScheduleDataDetailRespDTOList')
|
||||
},
|
||||
methods: {
|
||||
// 查
|
||||
executeSql1(tableName) {
|
||||
let sql = `select * from ${tableName}`
|
||||
let sql = `select * from ${tableName} WHERE vtpId = '${this.vtpId}'`
|
||||
sqlite.executeSqlCeshi(sql).then((value) => {
|
||||
// 在resolve时执行的回调函数
|
||||
if (tableName == 'shipInfoTable') {
|
||||
this.shipInfo = value[0]
|
||||
this.title = `${this.shipInfo.vslCnname} - 安全巡检`
|
||||
} else {
|
||||
this.infoList = value
|
||||
}
|
||||
this.vvyList = value
|
||||
let vvyIds = []
|
||||
this.vvyList.forEach(v => {
|
||||
vvyIds.push(v.vvyId)
|
||||
})
|
||||
this.initData(vvyIds)
|
||||
}).catch((error) => {
|
||||
// 在reject时执行的回调函数
|
||||
console.error(error);
|
||||
});
|
||||
},
|
||||
initData(vvyIds) {
|
||||
// console.log(`pamId=${this.portObj.portId}&vvyIds=${vvyIds}`)
|
||||
uni.request({
|
||||
url: `${this.$local}/api/safetyInspection/page?pamId=${this.portObj.portId}&vvyIds=${vvyIds}`,
|
||||
header: {
|
||||
'Content-Type': 'application/json', //自定义请求头信息
|
||||
'Authorization': `Bearer ${this.loginObj.access_token}`
|
||||
},
|
||||
method: 'GET', //请求方式,必须为大写
|
||||
success: (res) => {
|
||||
this.infoList = res.data.data.records
|
||||
},
|
||||
fail: () => {
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
add(state, item, index) {
|
||||
if (state != 'add') {
|
||||
uni.setStorageSync('patrolRow', item);
|
||||
uni.setStorageSync('patrolRowIndex', index);
|
||||
}
|
||||
let id = ""
|
||||
if (state != 'add') {
|
||||
id = item.vsiId
|
||||
}
|
||||
const obj = {
|
||||
state: state,
|
||||
id: id,
|
||||
}
|
||||
const params = encodeURIComponent(JSON.stringify(obj));
|
||||
uni.navigateTo({
|
||||
|
|
|
@ -11,32 +11,32 @@
|
|||
<p><text class="required" v-if="obj.state != 'look'">*</text>航次:</p>
|
||||
<uni-data-select v-model="vvyId" :localdata="hcList" @change="hcChange"
|
||||
v-if="obj.state != 'look'"></uni-data-select>
|
||||
<text v-else>{{vvyName}}</text>
|
||||
<text v-else>{{patrolRow.vvyName}}</text>
|
||||
</view>
|
||||
<view class="li">
|
||||
<p>进出口:</p>
|
||||
<text>{{shipInfo.importExportFlagName}}</text>
|
||||
<text>{{importExportName}}</text>
|
||||
</view>
|
||||
<view class="li">
|
||||
<p>贸易类型:</p>
|
||||
<text>{{shipInfo.spmTradeName}}</text>
|
||||
<text>{{spmTradeName}}</text>
|
||||
</view>
|
||||
<view class="li">
|
||||
<p><text class="required" v-if="obj.state != 'look'">*</text>类型:</p>
|
||||
<uni-easyinput v-if="obj.state != 'look'" v-model="type" placeholder="请输入"></uni-easyinput>
|
||||
<text v-else>{{type}}</text>
|
||||
<text v-else>{{patrolRow.type}}</text>
|
||||
</view>
|
||||
<view class="li tpLi">
|
||||
<p><text class="required" v-if="obj.state != 'look'">*</text>图片上传:</p>
|
||||
<template v-if="obj.state != 'look'">
|
||||
<view class="picture">
|
||||
<uni-file-picker limit="9" v-model="urlList" fileMediatype="image" @select="select"
|
||||
<uni-file-picker limit="9" v-model="urlList2" fileMediatype="image" @select="select"
|
||||
@delete="delUrl" title="最多选择9张图片"></uni-file-picker>
|
||||
</view>
|
||||
</template>
|
||||
<view class="pictureLook" v-else>
|
||||
<view v-for="(item,index) in urlList" :key="index">
|
||||
<image :src="item.url" mode="widthFix">
|
||||
<view v-for="(item,index) in urlList2" :key="index">
|
||||
<image :src="item.url" mode="widthFix" @click="clickImg(urlList2,index)">
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
@ -44,7 +44,7 @@
|
|||
<p><text class="required" v-if="obj.state != 'look'">*</text>描述:</p>
|
||||
<uni-easyinput type="textarea" autoHeight v-model="remark" placeholder="请输入任务描述(200字以内)"
|
||||
maxlength="200" v-if="obj.state != 'look'"></uni-easyinput>
|
||||
<text v-else>{{remark}}</text>
|
||||
<text v-else>{{patrolRow.remark}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<uni-popup ref="popup" type="dialog">
|
||||
|
@ -79,10 +79,14 @@
|
|||
data() {
|
||||
return {
|
||||
title: "",
|
||||
vtpId: "",
|
||||
portObj: {},
|
||||
loginObj: {},
|
||||
shipInfo: {},
|
||||
obj: {},
|
||||
importExportName: "",
|
||||
spmTradeName: "",
|
||||
patrolRow: {},
|
||||
patrolRowIndex: {},
|
||||
// 航次下拉框
|
||||
vvyId: "",
|
||||
vvyName: "",
|
||||
|
@ -91,7 +95,7 @@
|
|||
type: "",
|
||||
// 图片
|
||||
urlList: [],
|
||||
ysUrl: [],
|
||||
urlList2: [],
|
||||
delUrlList: [],
|
||||
// 描述
|
||||
remark: "",
|
||||
|
@ -108,6 +112,12 @@
|
|||
},
|
||||
mounted() {
|
||||
that = this
|
||||
this.portObj = uni.getStorageSync('portObj')
|
||||
this.loginObj = uni.getStorageSync('loginObj')
|
||||
this.vtpId = uni.getStorageSync('vtpId')
|
||||
this.executeSql1('shipOption')
|
||||
this.executeSql1("shipInfoTable")
|
||||
this.executeSql1("voyageScheduleDataDetailRespDTOList")
|
||||
if (this.obj.state == 'edit' || this.obj.state == 'look') {
|
||||
this.getRow();
|
||||
}
|
||||
|
@ -118,14 +128,11 @@
|
|||
} else {
|
||||
this.title = "新增安全巡检"
|
||||
}
|
||||
this.executeSql1('shipOption')
|
||||
this.executeSql1("shipInfoTable")
|
||||
|
||||
},
|
||||
methods: {
|
||||
// 查
|
||||
executeSql1(tableName) {
|
||||
let sql = `select * from ${tableName}`
|
||||
let sql = `select * from ${tableName} WHERE vtpId = '${this.vtpId}'`
|
||||
sqlite.executeSqlCeshi(sql).then((value) => {
|
||||
// 在resolve时执行的回调函数
|
||||
if (tableName == 'shipOption') {
|
||||
|
@ -133,22 +140,21 @@
|
|||
this.getShip()
|
||||
} else if (tableName == 'shipInfoTable') {
|
||||
this.shipInfo = value[0]
|
||||
} else if (tableName == 'voyageScheduleDataDetailRespDTOList') {
|
||||
this.vvyInfo = value
|
||||
this.hcList = []
|
||||
value.forEach((v, index) => {
|
||||
this.hcList.push({
|
||||
text: v.vvyName,
|
||||
value: v.vvyId
|
||||
})
|
||||
})
|
||||
}
|
||||
}).catch((error) => {
|
||||
// 在reject时执行的回调函数
|
||||
console.error(error);
|
||||
});
|
||||
},
|
||||
executeSql2(tableName) {
|
||||
let sql = `select * from ${tableName} WHERE contactId = '${this.patrolRow.webId}'`
|
||||
sqlite.executeSqlCeshi(sql).then((value) => {
|
||||
// 在resolve时执行的回调函数
|
||||
this.urlList = value
|
||||
}).catch((error) => {
|
||||
// 在reject时执行的回调函数
|
||||
console.error(error);
|
||||
});
|
||||
},
|
||||
// 获取船只信息
|
||||
getShip() {
|
||||
// this.shipInfo = uni.getStorageSync('shipInfo')
|
||||
|
@ -168,13 +174,49 @@
|
|||
},
|
||||
// 获取当前行信息
|
||||
getRow() {
|
||||
this.patrolRow = uni.getStorageSync('patrolRow');
|
||||
this.patrolRowIndex = uni.getStorageSync('patrolRowIndex');
|
||||
this.executeSql2('safetyInspectionRespUrlList')
|
||||
this.vvyId = this.patrolRow.vvyId
|
||||
this.vvyName = this.patrolRow.vvyName
|
||||
this.type = this.patrolRow.type
|
||||
this.remark = this.patrolRow.remark
|
||||
uni.request({
|
||||
url: `${this.$local}/api/safetyInspection/details/${this.obj.id}`,
|
||||
header: {
|
||||
'Content-Type': 'application/json', //自定义请求头信息
|
||||
'Authorization': `Bearer ${this.loginObj.access_token}`
|
||||
},
|
||||
method: 'GET', //请求方式,必须为大写
|
||||
success: (res) => {
|
||||
this.patrolRow = res.data.data
|
||||
this.vvyId = this.patrolRow.vvyId
|
||||
this.vvyName = this.patrolRow.vvyName
|
||||
this.importExportName = this.patrolRow.importExportName
|
||||
this.spmTradeName = this.patrolRow.tradeTypeName
|
||||
this.type = this.patrolRow.type
|
||||
this.remark = this.patrolRow.remark
|
||||
if (this.patrolRow.url != null) {
|
||||
this.urlList = this.patrolRow.url
|
||||
} else {
|
||||
this.urlList = []
|
||||
}
|
||||
if (this.patrolRow.url.length > 0 || this.patrolRow.url != null) {
|
||||
this.patrolRow.url.forEach(v => {
|
||||
this.initImg(v)
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (error) => {}
|
||||
})
|
||||
},
|
||||
initImg(fileName) {
|
||||
uni.request({
|
||||
url: `${this.$local}/api/file/url/?fileName=${fileName}`,
|
||||
header: {
|
||||
'Content-Type': 'application/json', //自定义请求头信息
|
||||
'Authorization': `Bearer ${this.loginObj.access_token}`
|
||||
},
|
||||
method: 'GET', //请求方式,必须为大写
|
||||
success: (res) => {
|
||||
if (res.statusCode == 200) {
|
||||
this.urlList2.push(res.data)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
// 航次下拉
|
||||
hcChange(e) {
|
||||
|
@ -184,39 +226,53 @@
|
|||
this.vvyName = v.text
|
||||
}
|
||||
})
|
||||
this.vvyInfo.forEach(v => {
|
||||
if (v.vvyId == e) {
|
||||
this.importExportName = v.importExportFlagName
|
||||
this.spmTradeName = v.tradeTypeName
|
||||
}
|
||||
})
|
||||
},
|
||||
// 图片上传
|
||||
select(e) {
|
||||
pathToBase64(e.tempFiles[0].path)
|
||||
.then(base64 => {
|
||||
compressImgBySize(base64, 1200).then(baseImg => {
|
||||
// 输出图片base64
|
||||
that.ysUrl.push(baseImg)
|
||||
uni.uploadFile({
|
||||
url: `${this.$local}/api/file/upload`, //上传图片api
|
||||
header: {
|
||||
'Authorization': `Bearer ${this.loginObj.access_token}`
|
||||
},
|
||||
filePath: e.tempFilePaths[0],
|
||||
name: 'file',
|
||||
success: (res) => {
|
||||
this.urlList2.push(e.tempFiles[0])
|
||||
let data = JSON.parse(res.data).data
|
||||
this.urlList.push(data.filePath)
|
||||
},
|
||||
fail(e) {
|
||||
uni.showToast({
|
||||
title: `${e.errMsg}`,
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
// 移除图片
|
||||
delUrl(e) {
|
||||
if (this.obj.state == 'edit') {
|
||||
this.delUrlList.push(e.tempFile.webId)
|
||||
}
|
||||
pathToBase64(e.tempFile.path)
|
||||
.then(base64 => {
|
||||
compressImgBySize(base64, 1200).then(baseImg => {
|
||||
// 输出图片base64
|
||||
this.ysUrl.forEach((v, index) => {
|
||||
if (v == base64) {
|
||||
this.ysUrl.splice(index, 1)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error)
|
||||
})
|
||||
let delIndex = 0
|
||||
this.urlList2.forEach((v, index) => {
|
||||
if (v.uuid == e.tempFile.uuid) {
|
||||
delIndex = index
|
||||
}
|
||||
})
|
||||
this.urlList.splice(delIndex, 1)
|
||||
this.urlList2.splice(delIndex, 1)
|
||||
},
|
||||
// 点击图片放大
|
||||
clickImg(urlList, index) {
|
||||
uni.previewImage({
|
||||
current: index,
|
||||
urls: urlList,
|
||||
})
|
||||
},
|
||||
// 取消
|
||||
cancel() {
|
||||
|
@ -226,14 +282,22 @@
|
|||
},
|
||||
// 弹框删除
|
||||
delConfirm() {
|
||||
for (var i = 0; i < this.urlList.length; i++) {
|
||||
let delSql = `DELETE FROM safetyInspectionRespUrlList WHERE webId = '${this.urlList[i].webId}';`
|
||||
this.executeSql(delSql)
|
||||
}
|
||||
let sql = `DELETE FROM safetyInspectionRespList WHERE webId = '${this.patrolRow.webId}';`
|
||||
this.executeSql(sql)
|
||||
uni.navigateTo({
|
||||
url: '/pages/shipWork/patrol'
|
||||
uni.request({
|
||||
url: `${this.$local}/api/safetyInspection/${this.obj.id}`,
|
||||
header: {
|
||||
'Content-Type': 'application/json', //自定义请求头信息
|
||||
'Authorization': `Bearer ${this.loginObj.access_token}`
|
||||
},
|
||||
method: 'DELETE', //请求方式,必须为大写
|
||||
success: (res) => {
|
||||
console.log(res)
|
||||
if (res.statusCode == 200) {
|
||||
uni.navigateTo({
|
||||
url: '/pages/shipWork/patrol'
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: () => {}
|
||||
})
|
||||
},
|
||||
// 删除
|
||||
|
@ -242,43 +306,70 @@
|
|||
},
|
||||
// 保存
|
||||
save() {
|
||||
let date = new Date().getTime()
|
||||
let webId = uuidv4()
|
||||
let webStatus = 0
|
||||
let webDate = api.getDate(date)
|
||||
if (this.obj.state == "edit") {
|
||||
let sql =
|
||||
`UPDATE safetyInspectionRespList SET vvyId = '${this.vvyId}', vvyName = '${this.vvyName}',
|
||||
type = '${this.type}',remark = '${this.remark}', webStatus = '${webStatus}',
|
||||
webDate = '${webDate}' WHERE webId = '${this.patrolRow.webId}';`
|
||||
this.executeSql(sql)
|
||||
if (this.delUrlList.length > 0) {
|
||||
for (var i = 0; i < this.delUrlList.length; i++) {
|
||||
let delSql = `DELETE FROM safetyInspectionRespUrlList WHERE webId = '${this.delUrlList[i]}';`
|
||||
this.executeSql(delSql)
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < this.ysUrl.length; i++) {
|
||||
let webId2 = uuidv4()
|
||||
let sql2 = `insert into safetyInspectionRespUrlList values('${webId2}','${this.patrolRow.webId}','${this.ysUrl[i]}',
|
||||
'${webStatus}','${webDate}')`
|
||||
this.executeSql(sql2)
|
||||
}
|
||||
} else if (this.obj.state == "add") {
|
||||
let sql = `insert into safetyInspectionRespList values('${webId}','${this.vvyId}','${this.vvyName}',
|
||||
'${this.type}','${this.remark}','${this.shipInfo.tradeTypeName}','${this.shipInfo.importExportFlagName}',
|
||||
'${this.shipInfo.spmName}','${webStatus}','${webDate}')`
|
||||
this.executeSql(sql)
|
||||
for (var i = 0; i < this.ysUrl.length; i++) {
|
||||
let webId2 = uuidv4()
|
||||
let sql2 = `insert into safetyInspectionRespUrlList values('${webId2}','${webId}','${this.ysUrl[i]}',
|
||||
'${webStatus}','${webDate}')`
|
||||
this.executeSql(sql2)
|
||||
}
|
||||
let spmTradeName = ""
|
||||
if (this.spmTradeName == "内贸") {
|
||||
spmTradeName = "N"
|
||||
} else {
|
||||
spmTradeName = "W"
|
||||
}
|
||||
let importExportName = ""
|
||||
if (this.importExportName == "进口") {
|
||||
importExportName = "I"
|
||||
} else {
|
||||
importExportName = "E"
|
||||
}
|
||||
let dto = {
|
||||
"importExport": importExportName,
|
||||
"remark": this.remark,
|
||||
"spmId": this.vtpId,
|
||||
"tradeType": spmTradeName,
|
||||
"type": this.type,
|
||||
"uploadStatus": "",
|
||||
"url": this.urlList,
|
||||
"vsiId": this.obj.id,
|
||||
"vvyId": this.vvyId
|
||||
}
|
||||
if (this.obj.state == "add") {
|
||||
uni.request({
|
||||
url: `${this.$local}/api/safetyInspection`,
|
||||
data: dto,
|
||||
header: {
|
||||
'Content-Type': 'application/json', //自定义请求头信息
|
||||
'Authorization': `Bearer ${this.loginObj.access_token}`
|
||||
},
|
||||
method: 'POST', //请求方式,必须为大写
|
||||
success: (res) => {
|
||||
if (res.statusCode == 200) {
|
||||
uni.navigateTo({
|
||||
url: '/pages/shipWork/patrol'
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (e) => {
|
||||
console.log(e)
|
||||
}
|
||||
})
|
||||
} else if (this.obj.state == 'edit') {
|
||||
uni.request({
|
||||
url: `${this.$local}/api/safetyInspection/${this.obj.id}`,
|
||||
data: dto,
|
||||
header: {
|
||||
'Content-Type': 'application/json', //自定义请求头信息
|
||||
'Authorization': `Bearer ${this.loginObj.access_token}`
|
||||
},
|
||||
method: 'PUT', //请求方式,必须为大写
|
||||
success: (res) => {
|
||||
if (res.statusCode == 200) {
|
||||
uni.navigateTo({
|
||||
url: '/pages/shipWork/patrol'
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (e) => {
|
||||
console.log(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/pages/shipWork/patrol'
|
||||
})
|
||||
},
|
||||
// 编辑
|
||||
toGo(state) {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</view>
|
||||
<view class="li">
|
||||
<p>贸易类型:</p>
|
||||
<text>{{shipInfo.spmTradeName}}</text>
|
||||
<text>{{spmTradeName}}</text>
|
||||
</view>
|
||||
<view class="li">
|
||||
<p><text class="required" v-if="obj.state != 'look'">*</text>天气:</p>
|
||||
|
@ -131,6 +131,7 @@
|
|||
|
||||
vvyInfo: [],
|
||||
importExportFlagName: "",
|
||||
spmTradeName: "",
|
||||
|
||||
// 下拉数据
|
||||
optionData: [],
|
||||
|
@ -250,6 +251,7 @@
|
|||
this.vvyInfo.forEach(v => {
|
||||
if (v.vvyId == e) {
|
||||
this.importExportFlagName = v.importExportFlagName
|
||||
this.spmTradeName = v.tradeTypeName
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</view>
|
||||
<view class="li">
|
||||
<p>贸易类型:</p>
|
||||
<text>{{shipInfo.spmTradeName}}</text>
|
||||
<text>{{spmTradeName}}</text>
|
||||
</view>
|
||||
<view class="li">
|
||||
<p><text class="required">*</text>装卸类型:</p>
|
||||
|
@ -311,6 +311,7 @@
|
|||
|
||||
vvyInfo: [],
|
||||
importExportFlagName: "",
|
||||
spmTradeName: "",
|
||||
|
||||
contactId: "",
|
||||
aId: "",
|
||||
|
@ -625,6 +626,7 @@
|
|||
this.vvyInfo.forEach(v => {
|
||||
if (v.vvyId == e) {
|
||||
this.importExportFlagName = v.importExportFlagName
|
||||
this.spmTradeName = v.tradeTypeName
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</view>
|
||||
<view class="li">
|
||||
<p>贸易类型:</p>
|
||||
<text>{{shipInfo.spmTradeName}}</text>
|
||||
<text>{{spmTradeName}}</text>
|
||||
</view>
|
||||
<view class="li">
|
||||
<p><text class="required" v-if="obj.state != 'look'">*</text>泊位:</p>
|
||||
|
@ -103,6 +103,7 @@
|
|||
|
||||
vvyInfo: [],
|
||||
importExportFlagName: "",
|
||||
spmTradeName: "",
|
||||
|
||||
// 下拉数据
|
||||
optionData: [],
|
||||
|
@ -211,6 +212,7 @@
|
|||
this.vvyInfo.forEach(v => {
|
||||
if (v.vvyId == e) {
|
||||
this.importExportFlagName = v.importExportFlagName
|
||||
this.spmTradeName = v.tradeTypeName
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
</view>
|
||||
<view class="li">
|
||||
<p>贸易类型:</p>
|
||||
<text>{{shipInfo.spmTradeName}}</text>
|
||||
<text>{{spmTradeName}}</text>
|
||||
</view>
|
||||
<view class="li">
|
||||
<p><text class="required" v-if="obj.state != 'look'">*</text>泊位:</p>
|
||||
|
@ -120,6 +120,7 @@
|
|||
|
||||
vvyInfo: [],
|
||||
importExportFlagName: "",
|
||||
spmTradeName: "",
|
||||
|
||||
// 下拉数据
|
||||
optionData: [],
|
||||
|
@ -227,6 +228,7 @@
|
|||
this.vvyInfo.forEach(v => {
|
||||
if (v.vvyId == e) {
|
||||
this.importExportFlagName = v.importExportFlagName
|
||||
this.spmTradeName = v.tradeTypeName
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</view>
|
||||
<view class="li">
|
||||
<p>贸易类型:</p>
|
||||
<text>{{shipInfo.spmTradeName}}</text>
|
||||
<text>{{spmTradeName}}</text>
|
||||
</view>
|
||||
<view class="li">
|
||||
<p><text class="required" v-if="obj.state != 'look'">*</text>泊位:</p>
|
||||
|
@ -125,6 +125,7 @@
|
|||
|
||||
vvyInfo: [],
|
||||
importExportFlagName: "",
|
||||
spmTradeName: "",
|
||||
|
||||
// 下拉数据
|
||||
optionData: [],
|
||||
|
@ -251,6 +252,7 @@
|
|||
this.vvyInfo.forEach(v => {
|
||||
if (v.vvyId == e) {
|
||||
this.importExportFlagName = v.importExportFlagName
|
||||
this.spmTradeName = v.tradeTypeName
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 159 KiB |
|
@ -0,0 +1,17 @@
|
|||
## 1.0.7(2022-05-26)
|
||||
1. 优化局部改变数据更新问题,避免重新加载数据,只改变局部
|
||||
## 1.0.6(2022-04-18)
|
||||
1. 修改tab快速切换时会出现的BUG
|
||||
## 1.0.5(2022-04-18)
|
||||
1. 修复可能存在数据错误的BUG;
|
||||
2. 兼容,今后可以无需调用refresh()就可以更新数据;
|
||||
## 1.0.4(2022-04-18)
|
||||
1. 修复BUG;
|
||||
## 1.0.3(2022-04-15)
|
||||
1. 优化代码;
|
||||
2. 修改懒加载数据存在的BUG;
|
||||
## 1.0.1(2022-03-11)
|
||||
1. 增加隐藏图片字段的键名字段hideImageKey,默认hide
|
||||
2. 支持在列表中配置hide参数进行隐藏图片
|
||||
## 1.0.0(2022-03-09)
|
||||
使用最简单的思想实现瀑布流
|
|
@ -0,0 +1,323 @@
|
|||
<template>
|
||||
<view class="waterfalls-flow">
|
||||
<view v-for="(item,index) in data.column" :key="index" class="waterfalls-flow-column" :id="`waterfalls_flow_column_${index+1}`" :msg="msg" :style="{'width':w,'margin-left':index==0?0:m}">
|
||||
<view :class="['column-value',{'column-value-show':item2.o}]" v-for="(item2,index2) in columnValue(index)" :key="index2" :style="[s1]" @click.stop="wapperClick(item2)">
|
||||
<view class="inner" v-if="data.seat==1">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- #ifdef VUE2 -->
|
||||
<slot name="slot{{item2.index}}"></slot>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef VUE3 -->
|
||||
<slot :name="`slot${item2.index}`"></slot>
|
||||
<!-- #endif -->
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<slot v-bind="item2"></slot>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<image :class="['img',{'img-hide':item2[hideImageKey]==true||item2[hideImageKey]==1},{'img-error':!item2[data.imageKey]}]" :src="item2[data.imageKey]" mode="widthFix" @load="imgLoad(item2,index+1)" @error="imgError(item2,index+1)" @click.stop="imageClick(item2)"></image>
|
||||
<view class="inner" v-if="data.seat==2">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- #ifdef VUE2 -->
|
||||
<slot name="slot{{item2.index}}"></slot>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef VUE3 -->
|
||||
<slot :name="`slot${item2.index}`"></slot>
|
||||
<!-- #endif -->
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<slot v-bind="item2"></slot>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: Array,
|
||||
column: { // 列的数量
|
||||
type: [String, Number],
|
||||
default: 2
|
||||
},
|
||||
maxColumn: { // 最大列数
|
||||
type: [String, Number],
|
||||
default: 5
|
||||
},
|
||||
columnSpace: { // 列之间的间距 百分比
|
||||
type: [String, Number],
|
||||
default: 2
|
||||
},
|
||||
imageKey: { // 图片key
|
||||
type: [String],
|
||||
default: 'image'
|
||||
},
|
||||
hideImageKey: { // 隐藏图片key
|
||||
type: [String],
|
||||
default: 'hide'
|
||||
},
|
||||
seat: { // 文本的位置,1图片之上 2图片之下
|
||||
type: [String, Number],
|
||||
default: 2
|
||||
},
|
||||
listStyle: { // 单个展示项的样式:eg:{'background':'red'}
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: {
|
||||
list: this.value ? this.value : [],
|
||||
column: this.column < 2 ? 2 : this.column,
|
||||
columnSpace: this.columnSpace <= 5 ? this.columnSpace : 5,
|
||||
imageKey: this.imageKey,
|
||||
seat: this.seat
|
||||
},
|
||||
msg: 0,
|
||||
listInitStyle: {
|
||||
'border-radius': '12rpx',
|
||||
'margin-bottom': '20rpx',
|
||||
'background-color': '#fff'
|
||||
},
|
||||
adds: [], //预置数据
|
||||
isLoaded: true,
|
||||
curIndex: 0,
|
||||
isRefresh: true,
|
||||
flag: false,
|
||||
refreshDatas: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 计算列宽
|
||||
w() {
|
||||
const column_rate = `${100 / this.data.column - (+this.data.columnSpace)}%`;
|
||||
return column_rate;
|
||||
},
|
||||
// 计算margin
|
||||
m() {
|
||||
const column_margin = `${(100-(100 / this.data.column - (+this.data.columnSpace)).toFixed(5)*this.data.column)/(this.data.column-1)}%`;
|
||||
return column_margin;
|
||||
},
|
||||
// list样式
|
||||
s1() {
|
||||
return { ...this.listInitStyle, ...this.listStyle };
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 初始化
|
||||
this.refresh();
|
||||
},
|
||||
methods: {
|
||||
// 预加载图片
|
||||
loadImages(idx = 0) {
|
||||
let count = 0;
|
||||
const newList = this.data.list.filter((item, index) => index >= idx);
|
||||
for (let i = 0; i < newList.length; i++) {
|
||||
// #ifndef APP-PLUS
|
||||
uni.getImageInfo({
|
||||
src: `${newList[i][this.imageKey]}.jpg`,
|
||||
complete: res => {
|
||||
count++;
|
||||
if (count == newList.length) this.initValue(idx);
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
plus.io.getImageInfo({
|
||||
src: `${newList[i][this.imageKey]}.jpg`,
|
||||
complete: res => {
|
||||
count++;
|
||||
if (count == newList.length) this.initValue(idx);
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
// 刷新
|
||||
refresh() {
|
||||
if (!this.isLoaded) {
|
||||
this.refreshDatas = this.value;
|
||||
return false;
|
||||
};
|
||||
setTimeout(() => {
|
||||
this.refreshDatas = [];
|
||||
this.isRefresh = true;
|
||||
this.adds = [];
|
||||
this.data.list = this.value ? this.value : [];
|
||||
this.data.column = this.column < 2 ? 2 : this.column >= this.maxColumn ? this.maxColumn : this.column;
|
||||
this.data.columnSpace = this.columnSpace <= 5 ? this.columnSpace : 5;
|
||||
this.data.imageKey = this.imageKey;
|
||||
this.data.seat = this.seat;
|
||||
this.curIndex = 0;
|
||||
// 每列的数据初始化
|
||||
for (let i = 1; i <= this.data.column; i++) {
|
||||
this.data[`column_${i}_values`] = [];
|
||||
this.msg++;
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.initValue(this.curIndex, 'refresh==>');
|
||||
})
|
||||
}, 1)
|
||||
},
|
||||
columnValue(index) {
|
||||
return this.data[`column_${index+1}_values`];
|
||||
},
|
||||
change(newValue) {
|
||||
for (let i = 0; i < this.data.list.length; i++) {
|
||||
const cv = this.data[`column_${this.data.list[i].column}_values`];
|
||||
for (let j = 0; j < cv.length; j++) {
|
||||
if (newValue[i] && i === cv[j].index) {
|
||||
this.data[`column_${this.data.list[i].column}_values`][j] = Object.assign(cv[j], newValue[i]);
|
||||
this.msg++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
getMin(a, s) {
|
||||
let m = a[0][s];
|
||||
let mo = a[0];
|
||||
for (var i = a.length - 1; i >= 0; i--) {
|
||||
if (a[i][s] < m) {
|
||||
m = a[i][s];
|
||||
}
|
||||
}
|
||||
mo = a.filter(i => i[s] == m);
|
||||
return mo[0];
|
||||
},
|
||||
// 计算每列的高度
|
||||
getMinColumnHeight() {
|
||||
return new Promise(resolve => {
|
||||
const heightArr = [];
|
||||
for (let i = 1; i <= this.data.column; i++) {
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
query.select(`#waterfalls_flow_column_${i}`).boundingClientRect(data => {
|
||||
heightArr.push({ column: i, height: data.height });
|
||||
}).exec(() => {
|
||||
if (this.data.column <= heightArr.length) {
|
||||
resolve(this.getMin(heightArr, 'height'));
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
},
|
||||
async initValue(i, from) {
|
||||
this.isLoaded = false;
|
||||
if (i >= this.data.list.length || this.refreshDatas.length) {
|
||||
this.msg++;
|
||||
this.loaded();
|
||||
return false;
|
||||
}
|
||||
const minHeightRes = await this.getMinColumnHeight();
|
||||
const c = this.data[`column_${minHeightRes.column}_values`];
|
||||
this.data.list[i].column = minHeightRes.column;
|
||||
c.push({ ...this.data.list[i], cIndex: c.length, index: i, o: 0 });
|
||||
this.msg++;
|
||||
},
|
||||
// 图片加载完成
|
||||
imgLoad(item, c) {
|
||||
const i = item.index;
|
||||
item.o = 1;
|
||||
this.$set(this.data[`column_${c}_values`], item.cIndex, JSON.parse(JSON.stringify(item)));
|
||||
this.initValue(i + 1);
|
||||
},
|
||||
// 图片加载失败
|
||||
imgError(item, c) {
|
||||
const i = item.index;
|
||||
item.o = 1;
|
||||
item[this.data.imageKey] = null;
|
||||
this.$set(this.data[`column_${c}_values`], item.cIndex, JSON.parse(JSON.stringify(item)));
|
||||
this.initValue(i + 1);
|
||||
},
|
||||
// 渲染结束
|
||||
loaded() {
|
||||
if (this.refreshDatas.length) {
|
||||
this.isLoaded = true;
|
||||
this.refresh();
|
||||
return false;
|
||||
}
|
||||
this.curIndex = this.data.list.length;
|
||||
if (this.adds.length) {
|
||||
this.data.list = this.adds[0];
|
||||
this.adds.splice(0, 1);
|
||||
this.initValue(this.curIndex);
|
||||
} else {
|
||||
if (this.data.list.length) this.$emit('loaded');
|
||||
this.isLoaded = true;
|
||||
this.isRefresh = false;
|
||||
}
|
||||
},
|
||||
// 单项点击事件
|
||||
wapperClick(item) {
|
||||
this.$emit('wapperClick', item);
|
||||
},
|
||||
// 图片点击事件
|
||||
imageClick(item) {
|
||||
this.$emit('imageClick', item);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
deep: true,
|
||||
handler(newValue, oldValue) {
|
||||
setTimeout(() => {
|
||||
this.$nextTick(() => {
|
||||
if (this.isRefresh) return false;
|
||||
if (this.isLoaded) {
|
||||
// if (newValue.length <= this.curIndex) return this.refresh();
|
||||
if (newValue.length <= this.curIndex) return this.change(newValue);
|
||||
this.data.list = newValue;
|
||||
this.$nextTick(() => {
|
||||
this.initValue(this.curIndex, 'watch==>');
|
||||
})
|
||||
} else {
|
||||
this.adds.push(newValue);
|
||||
}
|
||||
})
|
||||
}, 10)
|
||||
}
|
||||
},
|
||||
column(newValue) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.waterfalls-flow {
|
||||
overflow: hidden;
|
||||
|
||||
&-column {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.column-value {
|
||||
width: 100%;
|
||||
font-size: 0;
|
||||
overflow: hidden;
|
||||
transition: opacity .4s;
|
||||
opacity: 0;
|
||||
|
||||
&-show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.inner {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.img {
|
||||
width: 100%;
|
||||
|
||||
&-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-error {
|
||||
background: #f2f2f2 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC4AAAAiAQMAAAAatXkPAAAABlBMVEUAAADMzMzIT8AyAAAAAXRSTlMAQObYZgAAAIZJREFUCNdlzjEKwkAUBNAfEGyCuYBkLyLuxRYW2SKlV1JSeA2tUiZg4YrLjv9PGsHqNTPMSAQuyAJgRDHSyvBPwtZoSJXakeJI9iuRLGDygdl6V0yKDtyMAeMPZySj8yfD+UapvRPj2JOwkyAooSV5IwdDjPdCPspe8LyTl9IKJvDETKKRv6vnlUasgg0fAAAAAElFTkSuQmCC) no-repeat center center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"id": "custom-waterfalls-flow",
|
||||
"displayName": "瀑布流 灵活配置 简单易用 兼容vue2vue3小程序、H5、app等多端",
|
||||
"version": "1.0.7",
|
||||
"description": "瀑布流,根据内容自动计算进行流式布局,简单参数配置,实现兼容多端及vue2和vue3的瀑布流布局;uv-ui发布https://ext.dcloud.net.cn/plugin?name=uv-ui",
|
||||
"keywords": [
|
||||
"瀑布流",
|
||||
"瀑布流式布局"
|
||||
],
|
||||
"repository": "https://gitee.com/my_dear_li_pan/my-uni-modules.git",
|
||||
"engines": {
|
||||
},
|
||||
"dcloudext": {
|
||||
"category": [
|
||||
"前端组件",
|
||||
"通用组件"
|
||||
],
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "插件不采集任何数据",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "n"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "y",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "u",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,445 @@
|
|||
- <a href="#c1" title="概要">概要</a>
|
||||
- <a href="#c2" title="支持的平台">支持的平台</a>
|
||||
- <a href="#c3" title="使用方式">使用方式</a>
|
||||
- <a href="#c4" title="属性说明">属性说明</a>
|
||||
- <a href="#c5" title="事件说明">事件说明</a>
|
||||
- <a href="#c6" title="组件方法">组件方法</a>
|
||||
- <a href="#c7" title="refresh的使用示例">refresh的使用示例</a>
|
||||
- <a href="#c8" title="隐藏单项图片示例">隐藏单项图片示例</a>
|
||||
- <a href="#c9" title="完整示例">完整示例</a>
|
||||
- <a href="#c10" title="温馨提示">温馨提示</a>
|
||||
- <a href="#c11" title="关注我,不迷路">关注我,不迷路</a>
|
||||
- <a href="#c12" title="个人作品展示">个人作品展示</a>
|
||||
|
||||
<div id="c1"></div>
|
||||
|
||||
#### 概要
|
||||
|
||||
custom-waterfalls-flow是一个瀑布流插件,灵活配置、简单易用、兼容多端、同时兼容vue2和vue3。
|
||||
|
||||
最近在做项目的时候需要用到瀑布流,于是在插件市场找了一些,下载量最高的是用了定位来做的,我认为瀑布流可以不用定位去实现,于是我就自己写了该插件。经过反复的测试优化,最终搞定!
|
||||
|
||||
**设置列数:** 瀑布流的列数可以通过参数直接控制,实时监听,随改随生效。列数最小为2,最大默认为5,可以通过maxColumn参数去控制最大列数,理论上可以设置无限大,具体值自己拿捏。
|
||||
|
||||
**更新数据:** 瀑布流的每项数据,可以直接通过修改value,随改随生效,这样可以实现加载更多数据。已经渲染过的数据不会再次渲染,每次只会渲染新增的数据,这样避免了数据越多渲染越慢的情况。可以调用组件的```refresh()```方法进行数据刷新,注意vue2和vue3中调用子组件的方法有区别,也会在下面进行说明。
|
||||
|
||||
**展示方式:** 瀑布流可以是纯图片,可以使用插槽自定义文字描述,微信小程序与app、h5使用会有些区别,也会在下面具体说明。内容高度及排序都不用担心,会根据每项的内容高度自动计算。
|
||||
|
||||
**实现思路:** 通过配置列数,先渲染出每列,再计算每列的高度,最小的那列就加入一条数据进行渲染,然后再重复计算每列,高度小的加入数据...其实思路是很简单的。
|
||||
|
||||
uniapp插件市场地址:[https://ext.dcloud.net.cn/plugin?id=7594](https://ext.dcloud.net.cn/plugin?id=7594)
|
||||
|
||||
<div id="c2"></div>
|
||||
|
||||
#### 支持的平台
|
||||
|
||||
H5、app、微信小程序(这三个平台经过反复测试优化,兼容vue2和vue3)。
|
||||
|
||||
百度小程序:由于插槽不能循环渲染的限制,只支持纯图片瀑布流。
|
||||
|
||||
其他小程序:暂未测试,需要的可以自己测试和修改,思路肯定是没错的,主要是兼容插槽的问题。
|
||||
|
||||
nvue:暂不支持,后期可能会支持,目前需要的可以自己修改源码。
|
||||
|
||||
<div id="c3"></div>
|
||||
|
||||
#### 使用方式
|
||||
|
||||
**1、导入插件**
|
||||
|
||||
该组件符合uni_modules规范,使用Hbuilderx导入插件,导入到项目根目录中的uni_modules文件夹中。
|
||||
|
||||
**2、template中使用**
|
||||
|
||||
uni_modules规范在项目页面中直接使用,不需要单独引入注册组件。
|
||||
|
||||
***纯图片瀑布流使用***
|
||||
|
||||
```
|
||||
<template>
|
||||
<custom-waterfalls-flow :value="data.list"></custom-waterfalls-flow>
|
||||
</template>
|
||||
```
|
||||
|
||||
***微信小程序自定义内容使用***
|
||||
|
||||
微信小程序没有动态模板,使用for循环的方式进行渲染。
|
||||
|
||||
```
|
||||
<template>
|
||||
<custom-waterfalls-flow :value="data.list">
|
||||
<view class="item" v-for="(item,index) in data.list" :key="index" slot="slot{{index}}">
|
||||
<view class="title">{{item.title}}</view>
|
||||
<view class="desc">{{item.desc}}</view>
|
||||
</view>
|
||||
</custom-waterfalls-flow>
|
||||
</template>
|
||||
```
|
||||
|
||||
***h5、app端自定义内容使用***
|
||||
|
||||
使用作用域插槽实现
|
||||
|
||||
```
|
||||
<template>
|
||||
<custom-waterfalls-flow :value="data.list">
|
||||
<template v-slot:default="item">
|
||||
<view class="item">
|
||||
<view class="title">{{item.title}}</view>
|
||||
<view class="desc">{{item.desc}}</view>
|
||||
</view>
|
||||
</template>
|
||||
</custom-waterfalls-flow>
|
||||
</template>
|
||||
```
|
||||
|
||||
***小程序、h5、app等多端自定义内容使用***
|
||||
|
||||
条件渲染-多端同时兼容
|
||||
|
||||
```
|
||||
<template>
|
||||
<custom-waterfalls-flow :value="data.list">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="item" v-for="(item,index) in data.list" :key="index" slot="slot{{index}}">
|
||||
<view class="title">{{item.title}}</view>
|
||||
<view class="desc">{{item.desc}}</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<template v-slot:default="item">
|
||||
<view class="item">
|
||||
<view class="title">{{item.title}}</view>
|
||||
<view class="desc">{{item.desc}}</view>
|
||||
</view>
|
||||
</template>
|
||||
<!-- #endif -->
|
||||
</custom-waterfalls-flow>
|
||||
</template>
|
||||
```
|
||||
|
||||
<div id="c4"></div>
|
||||
|
||||
#### 属性说明
|
||||
|
||||
参数|说明|类型|是否必填|可选值|默认值
|
||||
-|-|-|-|-|-|
|
||||
value|渲染的列表|Array|是|-|-
|
||||
column|列数|Number|否|2-maxColumn|2
|
||||
maxColumn|最大列数|Number|否|>2|5
|
||||
columnSpace|列之间的间距(单位是百分比)|Number|否|-|2
|
||||
imageKey|列表中的图片字段的键名|String|否|-|image
|
||||
hideImageKey|隐藏图片字段的键名|String|否|-|hide
|
||||
seat|自定义文字的位置,1-图片上方,2-图片下方|Number|否|1/2|2
|
||||
listStyle|单个展示项的样式|Object|否|示例:```{'background':'red'}```|-
|
||||
|
||||
<div id="c5"></div>
|
||||
|
||||
#### 事件说明
|
||||
|
||||
事件名称|说明|回调参数
|
||||
-|-|-|
|
||||
@loaded|图片加载完成事件|-
|
||||
@wapperClick|单项点击事件|单项对应参数
|
||||
@imageClick|图片点击事件|单项对应参数
|
||||
|
||||
<div id="c6"></div>
|
||||
|
||||
#### 组件方法
|
||||
|
||||
事件名称|说明|参数|使用场景
|
||||
-|-|-|-
|
||||
refresh|刷新数据,数据初始化,vue2中使用:```this.$refs.waterfallsFlowRef.refresh();```;vue3中使用:```const waterfallsFlowRef = ref(null);waterfallsFlowRef.value.refresh();```|-|下拉刷新等
|
||||
|
||||
<div id="c7"></div>
|
||||
|
||||
#### refresh的使用示例
|
||||
|
||||
***vue2中使用***
|
||||
|
||||
```
|
||||
<template>
|
||||
<view>
|
||||
<button class="btn" type="default" @click="reset()">刷新数据</button>
|
||||
<custom-waterfalls-flow ref="waterfallsFlowRef" :value="data.list"></custom-waterfalls-flow>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
data:{
|
||||
list: [
|
||||
{ image: 'https://via.placeholder.com/200x500.png/ff0000', title: '我是标题1', desc: '描述描述描述描述描述描述描述描述1' },
|
||||
{ image: 'https://via.placeholder.com/200x200.png/2878ff', title: '我是标题2', desc: '描述描述描述描述描述描述描述描述2' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
reset(){
|
||||
this.data.list = [{ image: 'https://via.placeholder.com/200x500.png/ff0000', title: '我是标题1', desc: '描述描述描述描述描述描述描述描述1' }]
|
||||
this.$refs.waterfallsFlowRef.refresh();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
***vue3中使用***
|
||||
|
||||
```
|
||||
<template>
|
||||
<view>
|
||||
<button class="btn" type="default" @click="reset()">刷新数据</button>
|
||||
<custom-waterfalls-flow ref="waterfallsFlowRef" :value="data.list"></custom-waterfalls-flow>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
const data = reactive({
|
||||
list: [
|
||||
{ image: 'https://via.placeholder.com/200x500.png/ff0000', title: '我是标题1', desc: '描述描述描述描述描述描述描述描述1' },
|
||||
{ image: 'https://via.placeholder.com/200x200.png/2878ff', title: '我是标题2', desc: '描述描述描述描述描述描述描述描述2' }
|
||||
]
|
||||
});
|
||||
const waterfallsFlowRef = ref(null);
|
||||
function reset(){
|
||||
data.list = [{ image: 'https://via.placeholder.com/200x500.png/ff0000', title: '我是标题1', desc: '描述描述描述描述描述描述描述描述1' }]
|
||||
waterfallsFlowRef.value.refresh();
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
<div id="c8"></div>
|
||||
|
||||
#### 隐藏单项图片示例
|
||||
|
||||
在数据列表中配置```hide:true```或者```hide:1```,就可以达到不显示图片的效果。支持使用参数hideImageKey自定义键名称,那就使用:```定义的键名称:true```或者```定义的键名称:1```。
|
||||
|
||||
```
|
||||
<template>
|
||||
<custom-waterfalls-flow :value="data.list">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="item" v-for="(item,index) in data.list" :key="index" slot="slot{{index}}">
|
||||
<view class="title">{{item.title}}</view>
|
||||
<view class="desc">{{item.desc}}</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<template v-slot:default="item">
|
||||
<view class="item">
|
||||
<view class="title">{{item.title}}</view>
|
||||
<view class="desc">{{item.desc}}</view>
|
||||
</view>
|
||||
</template>
|
||||
<!-- #endif -->
|
||||
</custom-waterfalls-flow>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
const data = reactive({
|
||||
list: [
|
||||
{ image: 'https://via.placeholder.com/200x500.png/ff0000',
|
||||
hide:1,title: '我是标题1', desc: '描述描述描述描述描述描述描述描述1' },
|
||||
{ image: 'https://via.placeholder.com/200x200.png/2878ff', title: '我是标题2', desc: '描述描述描述描述描述描述描述描述2' }
|
||||
]
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
<div id="c9"></div>
|
||||
|
||||
#### 完整示例
|
||||
|
||||
```
|
||||
<template>
|
||||
<view style="padding: 0 10rpx;">
|
||||
<view class="handle">
|
||||
<button class="btn" type="default" @click="add()">增加数据</button>
|
||||
<button class="btn" type="default" @click="changeColumn(1)">+列数({{column}})</button>
|
||||
<button class="btn" type="default" @click="changeColumn(0)">-列数({{column}})</button>
|
||||
<button class="btn" type="default" @click="reset()">刷新数据</button>
|
||||
</view>
|
||||
<custom-waterfalls-flow ref="waterfallsFlowRef" :value="data.list" :column="column" :columnSpace="1.5" :seat="2" @wapperClick="wapperClick" @imageClick="imageClick" @loaded="loaded">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="item" v-for="(item,index) in data.list" :key="index" slot="slot{{index}}">
|
||||
<view class="title">{{item.title}}</view>
|
||||
<view class="desc">{{item.desc}}</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<template v-slot:default="item">
|
||||
<view class="item">
|
||||
<view class="title">{{item.title}}</view>
|
||||
<view class="desc">{{item.desc}}</view>
|
||||
</view>
|
||||
</template>
|
||||
<!-- #endif -->
|
||||
</custom-waterfalls-flow>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
// #ifdef VUE3
|
||||
import { reactive, ref, onMounted } from 'vue';
|
||||
const data = reactive({
|
||||
list: [{ image: 'https://via.placeholder.com/200x500.png/ff0000', title: '我是标题1', desc: '描述描述描述描述描述描述描述描述1' },
|
||||
{ image: 'https://via.placeholder.com/200x200.png/2878ff', title: '我是标题2', desc: '描述描述描述描述描述描述描述描述2' },
|
||||
{ image: 'https://via.placeholder.com/200x100.png/FFB6C1', title: '我是标题3', desc: '描述描述描述描述描述描述描述描述3' },
|
||||
{ image: 'https://via.placeholder.com/200x300.png/9400D3', title: '我是标题4', desc: '描述描述描述描述描述描述描述描述4' },
|
||||
{ image: 'https://via.placeholder.com/100x240.png/B0E0E6', title: '我是标题5', desc: '描述描述描述描述描述描述描述描述5' },
|
||||
{ image: 'https://via.placeholder.com/140x280.png/7FFFAA', title: '我是标题6', desc: '描述描述描述描述描述描述描述描述6' },
|
||||
{ image: 'https://via.placeholder.com/40x60.png/EEE8AA', title: '我是标题7', desc: '描述描述描述描述描述描述描述描述7' }]
|
||||
});
|
||||
const column = ref(3);
|
||||
|
||||
function add() {
|
||||
const newArr = [{ image: 'https://via.placeholder.com/58x100.png/FF7F50', title: '我是标题8', desc: '描述描述描述描述描述描述描述描述8' },
|
||||
{ image: 'https://via.placeholder.com/59x100.png/C0C0C0', title: '我是标题9', desc: '描述描述描述描述描述描述描述描述9' },
|
||||
{ image: 'https://via.placeholder.com/60x100.png/FAEBD7', title: '我是标题10', desc: '描述描述描述描述描述描述描述描述10' }];
|
||||
data.list = data.list.concat(newArr);
|
||||
}
|
||||
|
||||
function changeColumn(h) {
|
||||
column.value = !h ? column.value - 1 : column.value + 1;
|
||||
}
|
||||
|
||||
function loaded() {
|
||||
console.log('加载完成')
|
||||
}
|
||||
|
||||
function wapperClick(item) {
|
||||
console.log('单项点击事件', item)
|
||||
}
|
||||
|
||||
function imageClick(item) {
|
||||
console.log('图片点击事件', item)
|
||||
}
|
||||
const waterfallsFlowRef = ref(null);
|
||||
|
||||
function reset() {
|
||||
data.list = [{ image: 'https://via.placeholder.com/200x500.png/ff0000', title: '我是标题1', desc: '描述描述描述描述描述描述描述描述1' }]
|
||||
waterfallsFlowRef.value.refresh();
|
||||
}
|
||||
// #endif
|
||||
</script>
|
||||
<script>
|
||||
// #ifdef VUE2
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
data: {
|
||||
list: [{ image: 'https://via.placeholder.com/200x500.png/ff0000', title: '我是标题1', desc: '描述描述描述描述描述描述描述描述1' },
|
||||
{ image: 'https://via.placeholder.com/200x200.png/2878ff', title: '我是标题2', desc: '描述描述描述描述描述描述描述描述2' },
|
||||
{ image: 'https://via.placeholder.com/200x100.png/FFB6C1', title: '我是标题3', desc: '描述描述描述描述描述描述描述描述3' },
|
||||
{ image: 'https://via.placeholder.com/200x300.png/9400D3', title: '我是标题4', desc: '描述描述描述描述描述描述描述描述4' },
|
||||
{ image: 'https://via.placeholder.com/100x240.png/B0E0E6', title: '我是标题5', desc: '描述描述描述描述描述描述描述描述5' },
|
||||
{ image: 'https://via.placeholder.com/140x280.png/7FFFAA', title: '我是标题6', desc: '描述描述描述描述描述描述描述描述6' },
|
||||
{ image: 'https://via.placeholder.com/40x60.png/EEE8AA', title: '我是标题7', desc: '描述描述描述描述描述描述描述描述7' }]
|
||||
},
|
||||
column: 3
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
add() {
|
||||
const newArr = [{ image: 'https://via.placeholder.com/58x100.png/FF7F50', title: '我是标题8', desc: '描述描述描述描述描述描述描述描述8' },
|
||||
{ image: 'https://via.placeholder.com/59x100.png/C0C0C0', title: '我是标题9', desc: '描述描述描述描述描述描述描述描述9' },
|
||||
{ image: 'https://via.placeholder.com/60x100.png/FAEBD7', title: '我是标题10', desc: '描述描述描述描述描述描述描述描述10' }]
|
||||
this.data.list = this.data.list.concat(newArr);
|
||||
},
|
||||
changeColumn(h) {
|
||||
this.column = !h ? this.column - 1 : this.column + 1;
|
||||
},
|
||||
loaded() {
|
||||
console.log('加载完成')
|
||||
},
|
||||
wapperClick(item) {
|
||||
console.log('单项点击事件', item)
|
||||
},
|
||||
imageClick(item) {
|
||||
console.log('图片点击事件', item)
|
||||
},
|
||||
reset() {
|
||||
this.data.list = [{ image: 'https://via.placeholder.com/200x500.png/ff0000', title: '我是标题1', desc: '描述描述描述描述描述描述描述描述1' }]
|
||||
this.$refs.waterfallsFlowRef.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
background-color: #f2f5f9;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.handle {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20rpx;
|
||||
padding: 10rpx;
|
||||
|
||||
.btn {
|
||||
margin: 20rpx 10rpx;
|
||||
padding: 0 20rpx;
|
||||
background: #2878FF;
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
|
||||
&::after {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 10rpx 10rpx 20rpx;
|
||||
|
||||
.title {
|
||||
line-height: 48rpx;
|
||||
font-size: 28rpx;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
<div id="c10"></div>
|
||||
|
||||
#### 温馨提示
|
||||
|
||||
1、该插件反复测试过微信小程序、h5、app-vue三个端,vue2和vue3都兼容,其他端可能需要测试改进。
|
||||
|
||||
2、该插件的使用hbuilderx版本最好升级到较新版本,我开发的版本是hbuilderx3.3.11.20220209。
|
||||
|
||||
3、对此插件或相关问题有好的建议,可以直接在评论区进行讨论。
|
||||
|
||||
4、希望遇到问题不要喷,也不要骂人,其实这种心情我能理解,写该插件也不是一时半会就完成了的,所以希望互相理解。只要有问题,我会第一时间回复解决。
|
||||
|
||||
5、对此插件有任何问题的可以在下方留言,我会第一时间回复和解决问题。还可以加QQ群进行前端技术交流 568984539,加群备注‘地区-名字-技术类型’。
|
||||
|
||||
#### 最后我想说:认为该插件对你有帮助的,记得收藏、好评,这样可以帮助到更多人哟!
|
||||
|
||||
---
|
||||
|
||||
<div id="c11"></div>
|
||||
|
||||
#### 关注我,不迷路
|
||||
|
||||
如果任何疑问的可以在评论区留言,还可以加QQ群交流:568984539,加群备注‘地区-名字-技术类型’。
|
||||
|
||||
更多前端等相关知识可关注我个人博客:https://blog.csdn.net/qq_42961150?spm=1011.2124.3001.5343
|
||||
|
||||
<div id="c12"></div>
|
||||
|
||||
#### 个人作品展示
|
||||
|
||||
uniapp+vue3.2+unicloud开发微信小程序:**皮皮虎去水印**。
|
||||
|
||||
关注下方公众号:【**全网免费网盘资源**】、【**美团外卖饿了么天天领红包**】、【**去水印**】
|
||||
|
||||
![image](https://vkceyugu.cdn.bspapp.com/VKCEYUGU-bb657efd-fece-483e-a715-5daea480fde8/6e029310-aec8-46e9-9883-1c88dc1925ad.jpg)
|
|
@ -0,0 +1,22 @@
|
|||
## 1.1.6(2022-10-24)
|
||||
修复 初始化渲染变量取值错误问题
|
||||
## 1.1.5(2022-10-24)
|
||||
初始化渲染增加状态条件判断
|
||||
## 1.1.4(2022-10-24)
|
||||
修改空数据提示
|
||||
## 1.1.3(2022-10-24)
|
||||
1、增加内容插槽
|
||||
|
||||
2、删除状态文本属性
|
||||
|
||||
3、组件创建时可触发渲染条件
|
||||
## 1.1.2(2022-09-26)
|
||||
修改了开启布局的判断条件
|
||||
## 1.1.1(2022-08-28)
|
||||
1、加强组件化封装
|
||||
|
||||
2、 完善注释和优化使用逻辑
|
||||
## 1.1.0(2022-08-22)
|
||||
重写渲染列表逻辑
|
||||
## 1.0.1(2021-06-08)
|
||||
修改插入方向计算方式
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<view class="waterfall-item-container">
|
||||
<view class="waterfall-item" @tap="onTap">
|
||||
<image :src="params.url" mode="widthFix" @load="emitHeight" @error="emitHeight"></image>
|
||||
<view class="content">
|
||||
<view>{{params.title}}</view>
|
||||
<view class="money">{{params.money}}元</view>
|
||||
<view style="margin: 0 0 8rpx 0;">
|
||||
<text class="label">{{params.label}}</text>
|
||||
</view>
|
||||
<view class="shop-name">{{params.shop}}</view>
|
||||
123
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name:"helangWaterfallItem",
|
||||
options:{
|
||||
virtualHost: true
|
||||
},
|
||||
props:{
|
||||
params:{
|
||||
type: Object,
|
||||
default(){
|
||||
return {}
|
||||
}
|
||||
},
|
||||
tag:{
|
||||
type:String | Number,
|
||||
default:''
|
||||
},
|
||||
index:{
|
||||
type:Number,
|
||||
default:-1
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
},
|
||||
methods:{
|
||||
// 发出组件高度信息,在此处可以区分正确和错误的加载,给予错误的提示图片
|
||||
emitHeight(e){
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
query.select('.waterfall-item-container').boundingClientRect(data => {
|
||||
let height = Math.floor(data.height);
|
||||
this.$emit("height",height,this.$props.tag);
|
||||
}).exec();
|
||||
},
|
||||
onTap(){
|
||||
this.$emit("click",this.$props.index,this.$props.tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.waterfall-item{
|
||||
padding: 16rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
|
||||
image{
|
||||
display: block;
|
||||
width: 100%;
|
||||
// 默认设置一个图片的大约值
|
||||
height: 350rpx;
|
||||
}
|
||||
|
||||
.content{
|
||||
margin-top: 16rpx;
|
||||
|
||||
.money{
|
||||
color: #fa3534;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.label{
|
||||
background-color: #fa3534;
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.shop-name{
|
||||
font-size: 20rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,241 @@
|
|||
<template>
|
||||
<view>
|
||||
<view class="waterfall-box h-flex-x h-flex-2">
|
||||
<view>
|
||||
<view v-for="(item,index) in leftList" :key="item._render_id"
|
||||
class="list-item"
|
||||
>
|
||||
<!-- :class="{'show': showPage > item._current_page }" -->
|
||||
456
|
||||
<helang-waterfall-item
|
||||
:params="item"
|
||||
tag="left"
|
||||
:index="index"
|
||||
@height="onHeight"
|
||||
@click="onClick"
|
||||
></helang-waterfall-item>
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<view v-for="(item,index) in rightList" :key="item._render_id"
|
||||
class="list-item"
|
||||
>
|
||||
456
|
||||
<helang-waterfall-item
|
||||
:params="item"
|
||||
@height="onHeight"
|
||||
@click="onClick"
|
||||
tag="right"
|
||||
:index="index"
|
||||
></helang-waterfall-item>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<slot name="default"></slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import helangWaterfallItem from "./waterfall-item.vue"
|
||||
|
||||
export default {
|
||||
name:"helangWaterfallList",
|
||||
options:{
|
||||
virtualHost: true
|
||||
},
|
||||
components: {
|
||||
"helang-waterfall-item": helangWaterfallItem
|
||||
},
|
||||
props:{
|
||||
// 组件状态
|
||||
status:{
|
||||
type: String,
|
||||
default:''
|
||||
},
|
||||
// 待渲染的数据
|
||||
list:{
|
||||
type: Array,
|
||||
default(){
|
||||
return [];
|
||||
}
|
||||
},
|
||||
// 重置列表,设置为 true 时,瀑布流会自动重新渲染列表
|
||||
reset:{
|
||||
type: Boolean,
|
||||
default:false
|
||||
},
|
||||
},
|
||||
watch:{
|
||||
"$props.status"(newValue,oldValue){
|
||||
// 状态变更为 加载成功 时,执行瀑布流数据渲染
|
||||
if(newValue == 'success'){
|
||||
this.startRender();
|
||||
}else if(!this.showList){
|
||||
this.resetData();
|
||||
}
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
showList(){
|
||||
return !["fail","empty"].includes(this.$props.status);
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 左侧列表高度
|
||||
leftHeight: 0,
|
||||
// 右侧列表高度
|
||||
rightHeight: 0,
|
||||
// 左侧列表数据
|
||||
leftList: [],
|
||||
// 右侧列表数据
|
||||
rightList: [],
|
||||
// 待渲染列表
|
||||
awaitRenderList:[],
|
||||
// 当前展示页码数据
|
||||
showPage:1
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if(this.$props.status == 'success'){
|
||||
this.startRender();
|
||||
}
|
||||
console.log('-----',this.list);
|
||||
},
|
||||
methods: {
|
||||
// 监听高度变化
|
||||
onHeight(height, tag) {
|
||||
/**
|
||||
* 这个为实际渲染后 CSS 中 margin-buttom 的值,本示例默认为20rpx
|
||||
* 用于解决实际渲染后因为数据条数关系,高度差计算偏差的问题
|
||||
* */
|
||||
let marginBottom = uni.upx2px(20);
|
||||
|
||||
// console.log(`左高:${this.leftHeight},右高:${this.rightHeight},当前高:${height},插入方向:${tag}`)
|
||||
|
||||
if (tag == 'left') {
|
||||
this.leftHeight += (height + marginBottom);
|
||||
} else {
|
||||
this.rightHeight += (height + marginBottom);
|
||||
}
|
||||
this.renderList();
|
||||
},
|
||||
// 组件点击事件
|
||||
onClick(index, tag){
|
||||
// 对应的数据
|
||||
if(tag == 'left'){
|
||||
this.$emit("click",this.leftList[index],index,tag);
|
||||
}else{
|
||||
this.$emit("click",this.rightList[index],index,tag);
|
||||
}
|
||||
},
|
||||
// 渲染列表,这里实现瀑布流的左右分栏
|
||||
renderList() {
|
||||
// 待渲染长度为 0 时表示已渲染完成
|
||||
if(this.awaitRenderList.length < 1){
|
||||
this.showPage++;
|
||||
this.$emit("done");
|
||||
|
||||
// 为防止 js 数值类型最大值溢出,当高度值大于 1亿时重置高度
|
||||
if(this.leftHeight > 100000000){
|
||||
if(this.leftHeight > this.rightHeight){
|
||||
this.leftHeight = 2;
|
||||
this.rightHeight = 1;
|
||||
}else{
|
||||
this.leftHeight = 1;
|
||||
this.rightHeight = 2;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
let item = {
|
||||
...this.awaitRenderList.splice(0,1)[0],
|
||||
// 当前数据添加当前页面标识
|
||||
_current_page:this.showPage,
|
||||
// 当前数据添加一个渲染id,解决 v-for 重复会出现不执行 load 的 BUG
|
||||
_render_id:new Date().getTime()
|
||||
};
|
||||
|
||||
if(this.leftHeight > this.rightHeight){
|
||||
this.rightList.push(item);
|
||||
}else{
|
||||
this.leftList.push(item);
|
||||
}
|
||||
console.log('r',this.rightList,this.leftList);
|
||||
},
|
||||
// 重置数据
|
||||
resetData(){
|
||||
this.leftHeight = 0;
|
||||
this.rightHeight = 0;
|
||||
this.leftList = [];
|
||||
this.rightList = [];
|
||||
this.awaitRenderList = [];
|
||||
// 当前展示页码数据
|
||||
this.showPage = 1;
|
||||
},
|
||||
// 启动渲染
|
||||
startRender(){
|
||||
if(!this.showList){
|
||||
this.resetData();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this.$props.list || this.$props.list.length < 1){
|
||||
console.log('河浪瀑布流插件提示:当前数据为空,不会触发列表渲染');
|
||||
return;
|
||||
}
|
||||
|
||||
// 若本次渲染为 重置 则先恢复组件的默认参数
|
||||
if(this.$props.reset){
|
||||
this.resetData();
|
||||
}
|
||||
|
||||
this.awaitRenderList = [...this.$props.list];
|
||||
this.renderList();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.waterfall-box {
|
||||
padding: 20rpx 10rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
>view {
|
||||
padding: 0 10rpx;
|
||||
}
|
||||
|
||||
.list-item{
|
||||
margin-bottom: 0;
|
||||
// 设置透明,默认是可视的
|
||||
opacity: 0;
|
||||
// 默认超出隐藏,不影响加载中的文字显示效果
|
||||
overflow: hidden;
|
||||
height: 0;
|
||||
|
||||
.show{
|
||||
margin-bottom: 20rpx;
|
||||
opacity: 1;
|
||||
overflow: auto;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.h-flex-x {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
align-content: flex-start;
|
||||
|
||||
&.h-flex-2 {
|
||||
>view {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,39 @@
|
|||
let list = ()=>{
|
||||
return new Promise((resolve,reject)=>{
|
||||
setTimeout(() => {
|
||||
// 生成随机数方法
|
||||
let random = (min = 0, max) => {
|
||||
return Math.floor(Math.random() * max) + min;
|
||||
}
|
||||
// 待选的图片数据
|
||||
let imgs = [];
|
||||
// 待选的标题数据
|
||||
let titles = [
|
||||
'桃花坞里桃花庵,桃花庵里桃花仙;',
|
||||
'桃花仙人种桃树,又摘桃花卖酒钱。',
|
||||
'酒醒只在花前坐,酒醉还来花下眠;半醒半醉日复日,花落花开年复年。',
|
||||
'但愿老死花酒间,不愿鞠躬车马前;',
|
||||
'车尘马足富者趣,酒盏花枝贫者缘。若将富贵比贫贱,',
|
||||
'一在平地一在天;若将贫贱比车马,他得驱驰我得闲。',
|
||||
'别人笑我太疯癫,我笑他人看不穿;不见五陵豪杰墓,无花无酒锄作田。'
|
||||
];
|
||||
|
||||
let res = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
res.push({
|
||||
id:i+1,
|
||||
url:`/uni_modules/helang-waterfall/static/waterfall/${random(0,3)}.jpg?t=${new Date().getTime()}`,
|
||||
title: titles[random(0, titles.length)],
|
||||
money: random(9, 9999),
|
||||
label:'官方自营',
|
||||
shop:'唐诗三百首旗舰店'
|
||||
})
|
||||
}
|
||||
resolve(res);
|
||||
}, 500);
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
getList:list
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
{
|
||||
"id": "helang-waterfall",
|
||||
"displayName": "瀑布流布局-waterfall",
|
||||
"version": "1.1.6",
|
||||
"description": "这是一款简单又好用的瀑布流布局模板,通过页面模板+组件的方式。充分利用组件的复用性和页面的生命周期。",
|
||||
"keywords": [
|
||||
"瀑布流",
|
||||
"布局",
|
||||
"列表",
|
||||
"waterfall"
|
||||
],
|
||||
"repository": "https://gitee.com/myDarling/uniapp-extend",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.2.11"
|
||||
},
|
||||
"dcloudext": {
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "",
|
||||
"type": "uniapp-template-page"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "y",
|
||||
"联盟": "y"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
<template>
|
||||
<view>
|
||||
<helang-waterfall-list
|
||||
:status="waterfall.status"
|
||||
:list="waterfall.list"
|
||||
:reset="waterfall.reset"
|
||||
@click="onClick"
|
||||
@done="onDone"
|
||||
>
|
||||
<template>
|
||||
<view v-if="waterfall.status == 'await'">
|
||||
<view class="load-txt">上拉加载更多</view>
|
||||
</view>
|
||||
<view v-else-if="waterfall.status == 'loading'">
|
||||
<view class="load-txt">加载中</view>
|
||||
</view>
|
||||
<view v-else-if="waterfall.status == 'success'">
|
||||
<view class="load-txt">加载中</view>
|
||||
</view>
|
||||
<view v-else-if="waterfall.status == 'finish'">
|
||||
<view class="load-txt">没有更多了</view>
|
||||
</view>
|
||||
<view v-else-if="waterfall.status == 'fail'">
|
||||
<image class="load-icon" src="../../static/waterfall/fail.png"></image>
|
||||
<view class="load-txt">出错了,请刷新重试</view>
|
||||
</view>
|
||||
<view v-else-if="waterfall.status == 'empty'">
|
||||
<image class="load-icon" src="../../static/waterfall/empty.png"></image>
|
||||
<view class="load-txt">暂无数据</view>
|
||||
</view>
|
||||
<view v-else><!-- 别问我为什么要写一个 v-else 的空 view,不写H5平台就会有CSS生效的离谱BUG --></view>
|
||||
</template>
|
||||
</helang-waterfall-list>
|
||||
|
||||
<view class="status-change" @tap="onStatusChange">
|
||||
<view>切换<br />状态</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import helangWaterfallList from "@/uni_modules/helang-waterfall/components/waterfall/waterfall-list"
|
||||
|
||||
// 列表接口模拟数据
|
||||
import mockData from '../../mock-data/waterfall-list.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
"helang-waterfall-list": helangWaterfallList
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 异步请求相关
|
||||
ajax: {
|
||||
// 是否可以加载
|
||||
load: true,
|
||||
// 每页的请求条件
|
||||
rows:10,
|
||||
// 页码
|
||||
page:1,
|
||||
// 数据列表
|
||||
dataList:[]
|
||||
},
|
||||
// 瀑布流组件相关
|
||||
waterfall:{
|
||||
status:"",
|
||||
reset:false,
|
||||
list:[]
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
onReady() {
|
||||
this.getList();
|
||||
},
|
||||
// 触底触发
|
||||
onReachBottom() {
|
||||
this.getList();
|
||||
},
|
||||
// 下拉刷新
|
||||
onPullDownRefresh(){
|
||||
// 正常情况下接口返回应该很会很快。故意延迟调用,让用户有在刷新的体验感
|
||||
setTimeout(()=>{
|
||||
this.ajax.page = 1;
|
||||
this.ajax.load = true;
|
||||
this.getList();
|
||||
},800);
|
||||
},
|
||||
methods: {
|
||||
// 瀑布流组件点击事件
|
||||
onClick(data,index, tag){
|
||||
console.log(data);
|
||||
let direction = {
|
||||
"left":'左',
|
||||
"right":'右'
|
||||
}
|
||||
uni.showToast({
|
||||
title:`${direction[tag]}侧列表第${index+1}个被点击`,
|
||||
icon:'none'
|
||||
})
|
||||
},
|
||||
// 瀑布流组件渲染完成
|
||||
onDone(){
|
||||
// 设置组件为 非重置,这行代码保留不删即可
|
||||
this.waterfall.reset = false;
|
||||
|
||||
// 恢复 getList 方法的调用
|
||||
this.ajax.load = true;
|
||||
this.ajax.page++;
|
||||
|
||||
// 设置组件状态为 等待加载
|
||||
this.waterfall.status = 'await';
|
||||
|
||||
/**
|
||||
* 如果你是一个追求完美的开发者,可以通过判断当前数据的长度和请求的数据长度来优化前端请求,减少不必要请求
|
||||
* 不需要则删除
|
||||
* */
|
||||
/**
|
||||
if(this.ajax.dataCount >= this.ajax.rows){
|
||||
this.ajax.load = true;
|
||||
this.ajax.page++;
|
||||
}
|
||||
*/
|
||||
},
|
||||
// 获取数据
|
||||
getList() {
|
||||
if (!this.ajax.load) {
|
||||
return;
|
||||
}
|
||||
this.ajax.load = false;
|
||||
|
||||
// 设置状态为加载中
|
||||
this.waterfall.status = 'loading';
|
||||
|
||||
// 请求数据, mockData.getList 示例一个 ajax 请求
|
||||
mockData.getList({
|
||||
pageNum:this.ajax.page,
|
||||
pageSize:this.ajax.rows
|
||||
}).then(res=>{
|
||||
// 获取到的数据,请注意数据结构
|
||||
console.log(res);
|
||||
|
||||
// 第一页数据执行以下代码
|
||||
if(this.ajax.page == 1){
|
||||
// 关闭下拉
|
||||
uni.stopPullDownRefresh();
|
||||
|
||||
// 设置组件状态为 重置,可供下拉刷新这类需要重置列表功能时使用
|
||||
this.waterfall.reset = true;
|
||||
}
|
||||
|
||||
// 数据无效时处理
|
||||
if(!res || res.length < 1){
|
||||
// 设置组件为 加载结束 状态
|
||||
this.waterfall.status = 'finish';
|
||||
return;
|
||||
}
|
||||
|
||||
// 将数据赋值给瀑布流 list 属性
|
||||
this.waterfall.list = res;
|
||||
// 设置组件为 加载成功 状态,此时瀑布流组件开始计算当前数据的布局
|
||||
this.waterfall.status = 'success';
|
||||
|
||||
/**
|
||||
* 下方的代码为扩展其他功能的示例代码 可做参考,不需要可删除
|
||||
* */
|
||||
|
||||
// 缓存当前数据给其他需要该数据的功能使用
|
||||
if(this.ajax.page == 1){
|
||||
this.ajax.dataList = res;
|
||||
}else{
|
||||
this.ajax.dataList = [...this.ajax.dataList,...res];
|
||||
}
|
||||
// 记录本次数据长度,意义请看 done 事件的回调
|
||||
this.ajax.dataCount = res.length || 0;
|
||||
|
||||
// 。。。下面不需要写代码了,等待组件渲染完成
|
||||
})
|
||||
},
|
||||
// 导航状态切换演示监听
|
||||
onStatusChange(){
|
||||
uni.showActionSheet({
|
||||
itemList: ['常规', '加载异常', '加载错误'],
|
||||
success: (res)=> {
|
||||
switch(res.tapIndex){
|
||||
case 0:
|
||||
this.ajax.page = 1;
|
||||
this.ajax.load = true;
|
||||
this.getList();
|
||||
break;
|
||||
case 1:
|
||||
// alert(111)
|
||||
this.waterfall.status = 'fail';
|
||||
break;
|
||||
case 2:
|
||||
this.waterfall.status = 'empty';
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.load-txt{
|
||||
padding: 0 0 20rpx 0;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.load-icon{
|
||||
width: 300rpx;
|
||||
height: 300rpx;
|
||||
margin: 0 auto 20rpx auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.status-change{
|
||||
position: fixed;
|
||||
right: 10rpx;
|
||||
top: 60%;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
z-index: 100;
|
||||
font-size: 24rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #0089ff;
|
||||
color: #fff;
|
||||
line-height: 1;
|
||||
opacity: .33;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1 @@
|
|||
# helang-waterfall
|
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 18 KiB |
|
@ -0,0 +1,24 @@
|
|||
## 1.1.1(2023-08-01)
|
||||
优化文档
|
||||
## 1.1.0(2023-08-01)
|
||||
优化文档
|
||||
## 1.0.9(2023-07-10)
|
||||
优化文档
|
||||
## 1.0.8(2023-06-25)
|
||||
优化文档
|
||||
## 1.0.7(2023-06-25)
|
||||
优化文档
|
||||
## 1.0.6(2023-05-26)
|
||||
优化文档
|
||||
## 1.0.5(2023-05-22)
|
||||
优化文档
|
||||
## 1.0.4(2023-04-30)
|
||||
新增图片放大功能,解决原生组件和tabbar导航栏等无法覆盖的问题
|
||||
## 1.0.3(2023-04-28)
|
||||
优化文档
|
||||
## 1.0.2(2023-04-28)
|
||||
优化文档
|
||||
## 1.0.1(2023-04-28)
|
||||
新增长按事件
|
||||
## 1.0.0(2023-04-28)
|
||||
插件上线
|
|
@ -0,0 +1,121 @@
|
|||
<template>
|
||||
<view class="previewImage" v-if="show" @tap="close">
|
||||
<view class="page" v-if="urls.length > 0">
|
||||
<text class="text">{{ current + 1 }} / {{ urls.length }}</text>
|
||||
</view>
|
||||
<swiper class="swiper" :current="current" @change="swiperChange" @touchstart="handleTouchStart" @touchend="handleTouchEnd">
|
||||
<swiper-item v-for="(item, index) in urls" :key="index">
|
||||
<movable-area class="movable-area" scale-area>
|
||||
<movable-view class="movable-view" direction="all" :inertia="true" damping="100" scale="true" scale-min="1" scale-max="4" :scale-value="scale">
|
||||
<scroll-view scroll-y="true" class="uni-scroll-view">
|
||||
<view class="scroll-view"><image :key="index" class="image" :src="item" mode="widthFix" @longpress="onLongpress(item)" /></view>
|
||||
</scroll-view>
|
||||
</movable-view>
|
||||
</movable-area>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
urls: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
current: 0, //当前页
|
||||
scale: 1,
|
||||
isZooming: false // 是否处于缩放状态
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
//打开
|
||||
open(current) {
|
||||
this.current = this.urls.findIndex(item => item === current);
|
||||
this.show = true;
|
||||
this.$emit('open');
|
||||
},
|
||||
//关闭
|
||||
close() {
|
||||
if (!this.isZooming) {
|
||||
this.show = false;
|
||||
this.current = 0;
|
||||
this.$emit('close');
|
||||
}
|
||||
},
|
||||
//图片改变
|
||||
swiperChange(e) {
|
||||
this.current = e.detail.current;
|
||||
},
|
||||
//监听长按
|
||||
onLongpress(e) {
|
||||
this.$emit('onLongpress', e);
|
||||
},
|
||||
handleTouchStart() {
|
||||
this.isZooming = true;
|
||||
},
|
||||
handleTouchEnd() {
|
||||
this.isZooming = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.previewImage {
|
||||
z-index: 9999;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000000;
|
||||
.swiper {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
swiper-item {
|
||||
.movable-area {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.movable-view {
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
.scroll-view {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
.image {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.page {
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
width: 100%;
|
||||
top: 60rpx;
|
||||
text-align: center;
|
||||
.text {
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
padding: 3rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,81 @@
|
|||
{
|
||||
"id": "q-previewImage",
|
||||
"displayName": "图片预览、多图左右滑动、图片放大、支持覆盖原生组件、原生导航栏、tabbar",
|
||||
"version": "1.1.1",
|
||||
"description": "最简洁的模拟图片预览,支持长按事件,多图左右滑动,大图上下滑动查看,支持图片放大,支持覆盖原生组件/原生导航栏/tabbar 支持vue2/vue3/app/小程序/h5",
|
||||
"keywords": [
|
||||
"图片预览"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.4.14"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "n"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "u",
|
||||
"微信浏览器(Android)": "u",
|
||||
"QQ浏览器(Android)": "u"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "u",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
# 最简洁的模拟图片预览,支持长按事件,多图左右滑动,大图上下滑动查看,支持图片放大,支持覆盖原生组件/原生导航栏/tabbar 支持vue2/vue3/app/小程序/h5
|
||||
|
||||
- 为了解决项目中因一些特殊原因无法使用uni.previewImage,例如App.onShow或者页面的oShow中写了方法。
|
||||
- 如果用uni.previewImage,每次预览图片都会进到onShow的方法里
|
||||
- 可以基本实现官方的预览图片功能,但是体验不如uni.previewImage()
|
||||
- 如没有特殊原因,还是推荐官方的uni.previewImage()
|
||||
|
||||
## 安装指引
|
||||
|
||||
##1. 在插件市场打开本插件页面,在右侧点击`使用 HBuilderX 导入插件`,选择要导入的项目点击确定
|
||||
|
||||
##2. 使用方法 vue2写法
|
||||
|
||||
```
|
||||
<template>
|
||||
<view>
|
||||
<video v-if="videoShow" id="myVideo" src="https://img.cdn.aliyun.dcloud.net.cn/guide/uniapp/%E7%AC%AC1%E8%AE%B2%EF%BC%88uni-app%E4%BA%A7%E5%93%81%E4%BB%8B%E7%BB%8D%EF%BC%89-%20DCloud%E5%AE%98%E6%96%B9%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B@20200317.mp4" controls></video>
|
||||
<image v-for="(item, index) in imgs" :key="index" :src="item" @click="preview(item)"></image>
|
||||
<q-previewImage ref="previewImage" :urls="imgs" @onLongpress="onLongpress" @open="open" @close="close"></q-previewImage>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
videoShow:true,//video组件是否显示
|
||||
imgs: [],
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
preview(url) {
|
||||
this.imgs = ['https://web-assets.dcloud.net.cn/unidoc/zh/multiport-20210812.png', 'https://web-assets.dcloud.net.cn/unidoc/zh/uni-function-diagram.png'] //设置图片数组
|
||||
// #ifdef MP-WEIXIN
|
||||
this.$nextTick(()=>{
|
||||
this.$refs.previewImage.open(url); // 传入当前选中的图片地址(小程序必须添加$nextTick,解决组件首次加载无图)
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
this.$refs.previewImage.open(url); // 传入当前选中的图片地址
|
||||
// #endif
|
||||
},
|
||||
onLongpress(e){ //长按事件
|
||||
console.log('当前长按的图片是' + e);
|
||||
uni.showActionSheet({
|
||||
itemList: ['转发给朋友', '保存到手机'],
|
||||
success: function (res) {
|
||||
console.log('选中了第' + (res.tapIndex + 1) + '个按钮');
|
||||
},
|
||||
fail: function (res) {
|
||||
console.log(res.errMsg);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/* open和close方法一般用不到,但是在一些特殊场景会用到,
|
||||
* 比如预览图片时你需要覆盖 NavigationBar和 TabBar,
|
||||
* 或者在app中需要预览图片时覆盖住原生组件,比如video或者map等,
|
||||
* 你可以根据open和close去做一些操作,例如隐藏导航栏或者隐藏一些原生组件等
|
||||
*/
|
||||
open(){ //监听组件显示 (隐藏TabBar和NavigationBar,隐藏video原生组件)
|
||||
// uni.hideTabBar()
|
||||
// uni.setNavigationBarColor({
|
||||
// frontColor: '#000000', // 设置前景色为黑色
|
||||
// backgroundColor: '#000000', // 设置背景色为黑色
|
||||
// })
|
||||
// this.videoShow = false
|
||||
},
|
||||
close(){ //监听组件隐藏 (显示TabBar和NavigationBar,显示video原生组件)
|
||||
// uni.showTabBar()
|
||||
// uni.setNavigationBarColor({
|
||||
// frontColor: '#ffffff', // 设置前景色为白色
|
||||
// backgroundColor: '#000000', // 设置背景色为黑色
|
||||
// })
|
||||
// this.videoShow = true
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
```
|
||||
|
||||
##3. vue3 setup写法
|
||||
|
||||
```
|
||||
|
||||
<template>
|
||||
<view>
|
||||
<video v-if="videoShow" id="myVideo" src="https://img.cdn.aliyun.dcloud.net.cn/guide/uniapp/%E7%AC%AC1%E8%AE%B2%EF%BC%88uni-app%E4%BA%A7%E5%93%81%E4%BB%8B%E7%BB%8D%EF%BC%89-%20DCloud%E5%AE%98%E6%96%B9%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B@20200317.mp4" controls></video>
|
||||
<image v-for="(item, index) in imgs" :key="index" :src="item" @click="preview(item)"></image>
|
||||
<q-previewImage ref="previewImage" :urls="imgs" @onLongpress="onLongpress" @open="open" @close="close"></q-previewImage>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, toRefs,nextTick } from 'vue';
|
||||
|
||||
const data = reactive({
|
||||
videoShow:true,//video组件是否显示
|
||||
imgs: [],
|
||||
});
|
||||
const previewImage = ref(null);
|
||||
|
||||
const { imgs,videoShow } = toRefs(data)// 解构
|
||||
|
||||
const preview = url => {
|
||||
data.imgs = ['https://web-assets.dcloud.net.cn/unidoc/zh/multiport-20210812.png', 'https://web-assets.dcloud.net.cn/unidoc/zh/uni-function-diagram.png'] //设置图片数组
|
||||
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
nextTick(()=>{
|
||||
previewImage.value.open(url); // 传入当前选中的图片地址(小程序必须添加nextTick,解决组件首次加载无图)
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
previewImage.value.open(url); // 传入当前选中的图片地址
|
||||
// #endif
|
||||
};
|
||||
|
||||
const onLongpress = e =>{
|
||||
console.log('当前长按的图片是' + e);
|
||||
uni.showActionSheet({
|
||||
itemList: ['转发给朋友', '保存到手机'],
|
||||
success: function (res) {
|
||||
console.log('选中了第' + (res.tapIndex + 1) + '个按钮');
|
||||
},
|
||||
fail: function (res) {
|
||||
console.log(res.errMsg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* open和close方法一般用不到,但是在一些特殊场景会用到,
|
||||
* 比如预览图片时你需要覆盖 NavigationBar和 TabBar,
|
||||
* 或者在app中需要预览图片时覆盖住原生组件,比如video或者map等,
|
||||
* 你可以根据open和close去做一些操作,例如隐藏导航栏或者隐藏一些原生组件等
|
||||
*/
|
||||
const open = () => { //监听组件显示 (隐藏TabBar和NavigationBar,隐藏video原生组件)
|
||||
// uni.hideTabBar()
|
||||
// uni.setNavigationBarColor({
|
||||
// frontColor: '#000000', // 设置前景色为黑色
|
||||
// backgroundColor: '#000000', // 设置背景色为黑色
|
||||
// })
|
||||
// data.videoShow = false
|
||||
}
|
||||
|
||||
const close = () => { //监听组件隐藏 (显示TabBar和NavigationBar,显示video原生组件)
|
||||
// uni.showTabBar()
|
||||
// uni.setNavigationBarColor({
|
||||
// frontColor: '#ffffff', // 设置前景色为白色
|
||||
// backgroundColor: '#000000', // 设置背景色为黑色
|
||||
// })
|
||||
// data.videoShow = true
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
```
|
||||
|
||||
##4. 项目示例 (一般返回的数据图片是以逗号或特殊字符分割的字符串,点击时就需要传两个参数,一个是图片数组,一个是当前图片的index)
|
||||
## 注意q-previewImage不要写在循环体中,imgs其实就是用来存放当前图片的数组,每次点击每次赋值就行
|
||||
|
||||
```
|
||||
<template>
|
||||
<view>
|
||||
<video v-if="videoShow" id="myVideo" src="https://img.cdn.aliyun.dcloud.net.cn/guide/uniapp/%E7%AC%AC1%E8%AE%B2%EF%BC%88uni-app%E4%BA%A7%E5%93%81%E4%BB%8B%E7%BB%8D%EF%BC%89-%20DCloud%E5%AE%98%E6%96%B9%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B@20200317.mp4" controls></video>
|
||||
<view v-for="(item, index) in list" :key="index" class="list">
|
||||
<image :src="i" mode="aspectFill" v-for="(i,imgindex) in item.urls.split(',')" @click.stop="preimg(item.urls.split(','),imgindex)"></image>
|
||||
<view>
|
||||
<q-previewImage ref="previewImage" :urls="imgs" @onLongpress="onLongpress" @open="open" @close="close"></q-previewImage>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
videoShow:true,//是否显示video组件
|
||||
imgs: [],//imgs其实就是用来存放当前图片的数组,每次点击每次赋值就行
|
||||
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
preimg(urls,index){
|
||||
this.imgs = urls //imgs其实就是用来存放当前图片的数组,每次点击每次赋值就行
|
||||
// #ifdef MP-WEIXIN
|
||||
this.$nextTick(()=>{
|
||||
this.$refs.previewImage.open(this.imgs[index]); // 传入当前选中的图片地址(小程序必须添加$nextTick,解决组件首次加载无图)
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
this.$refs.previewImage.open(this.imgs[index]); // 传入当前选中的图片地址
|
||||
// #endif
|
||||
},
|
||||
onLongpress(e){ //长按事件
|
||||
console.log('当前长按的图片是' + e);
|
||||
uni.showActionSheet({
|
||||
itemList: ['转发给朋友', '保存到手机'],
|
||||
success: function (res) {
|
||||
console.log('选中了第' + (res.tapIndex + 1) + '个按钮');
|
||||
},
|
||||
fail: function (res) {
|
||||
console.log(res.errMsg);
|
||||
}
|
||||
});
|
||||
},
|
||||
/* open和close方法一般用不到,但是在一些特殊场景会用到,
|
||||
* 比如预览图片时你需要覆盖 NavigationBar和 TabBar,
|
||||
* 或者在app中需要预览图片时覆盖住原生组件,比如video或者map等,
|
||||
* 你可以根据open和close去做一些操作,例如隐藏导航栏或者隐藏一些原生组件等
|
||||
*/
|
||||
open(){ //监听组件显示 (隐藏TabBar和NavigationBar,隐藏video原生组件)
|
||||
// uni.hideTabBar()
|
||||
// uni.setNavigationBarColor({
|
||||
// frontColor: '#000000', // 设置前景色为黑色
|
||||
// backgroundColor: '#000000', // 设置背景色为黑色
|
||||
// })
|
||||
// this.videoShow = false
|
||||
},
|
||||
close(){ //监听组件隐藏 (显示TabBar和NavigationBar,显示video原生组件)
|
||||
// uni.showTabBar()
|
||||
// uni.setNavigationBarColor({
|
||||
// frontColor: '#ffffff', // 设置前景色为白色
|
||||
// backgroundColor: '#000000', // 设置背景色为黑色
|
||||
// })
|
||||
// this.videoShow = true
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 如果插件对您有一点帮助,请给个五星好评,感谢支持
|
||||
|
||||
|
||||
## 如有问题,请加qq 965969604
|