Browse Source
[新增] [公共方法] 时间戳转化为时间日期格式
[新增] [公共方法] 时间戳转化为时间日期格式
[新增] [公共样式] 新的类名,改变盒子模型 [新增组件] image组件 [新增组件] 预览图片组件 [新增组件] 步骤条组件master
5 changed files with 656 additions and 1 deletions
-
26common/shared.js
-
8common/styles/common.css
-
190components/lf-image/lf-image.vue
-
315components/lf-previewImage/lf-previewImage.vue
-
118components/lf-stepbar/lf-stepbar.vue
@ -0,0 +1,190 @@ |
|||
<template> |
|||
<image |
|||
:class="{'loading': loading}" |
|||
:data-loading-text="loadingText" |
|||
:src="img_src" |
|||
:mode="mode" |
|||
:lazy-load="true" |
|||
@click="click" |
|||
@load="load" |
|||
@error="error"> |
|||
</image> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
props: { |
|||
src: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
mode: { |
|||
type: String, |
|||
default: 'scaleToFill' |
|||
}, |
|||
errSrc: { |
|||
type: String, |
|||
default: '../../static/images/empty.png' |
|||
}, |
|||
loadingText: { |
|||
type: String, |
|||
default: '正在加载中' |
|||
} |
|||
}, |
|||
data(){ |
|||
return { |
|||
img_src: this.src, |
|||
loading: true |
|||
} |
|||
}, |
|||
methods: { |
|||
load(event){ |
|||
this.loading = false; |
|||
}, |
|||
error(event){ |
|||
this.loading = false; |
|||
this.img_src = this.$props.errSrc; |
|||
}, |
|||
click(event){ |
|||
this.$emit('click', event); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped="scoped"> |
|||
image{ |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
.loading{ |
|||
position: relative; |
|||
// background-color: #e5e5e5; |
|||
background: linear-gradient(-45deg, #e5e5e5, #d9d9d9, #f1f3f5, #f0f0f0); |
|||
animation: gradientBG 15s ease infinite; |
|||
background-size: 400% 400%; |
|||
transition: all 1s; |
|||
&::after{ |
|||
content: attr(data-loading-text); |
|||
position: absolute; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
left: 0; |
|||
color: #969696; |
|||
font-size: 20rpx; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
flex-wrap: wrap; |
|||
animation: flicker-in-1 2s linear both infinite; |
|||
transition: all 2s; |
|||
} |
|||
} |
|||
@keyframes gradientBG { |
|||
0% { |
|||
background-position: 0% 50%; |
|||
} |
|||
50% { |
|||
background-position: 100% 50%; |
|||
} |
|||
100% { |
|||
background-position: 0% 50%; |
|||
} |
|||
} |
|||
@keyframes flicker-in-1 { |
|||
0% { |
|||
opacity: 0.5; |
|||
} |
|||
10% { |
|||
opacity: 0.5; |
|||
} |
|||
10.1% { |
|||
opacity: 1; |
|||
} |
|||
10.2% { |
|||
opacity: 0.5; |
|||
} |
|||
20% { |
|||
opacity: 0.5; |
|||
} |
|||
20.1% { |
|||
opacity: 1; |
|||
} |
|||
20.6% { |
|||
opacity: 0.5; |
|||
} |
|||
30% { |
|||
opacity: 0.5; |
|||
} |
|||
30.1% { |
|||
opacity: 1; |
|||
} |
|||
30.5% { |
|||
opacity: 1; |
|||
} |
|||
30.6% { |
|||
opacity: 0.5; |
|||
} |
|||
45% { |
|||
opacity: 0.5; |
|||
} |
|||
45.1% { |
|||
opacity: 1; |
|||
} |
|||
50% { |
|||
opacity: 1; |
|||
} |
|||
55% { |
|||
opacity: 1; |
|||
} |
|||
55.1% { |
|||
opacity: 0.5; |
|||
} |
|||
57% { |
|||
opacity: 0.5; |
|||
} |
|||
57.1% { |
|||
opacity: 1; |
|||
} |
|||
60% { |
|||
opacity: 1; |
|||
} |
|||
60.1% { |
|||
opacity: 0.5; |
|||
} |
|||
65% { |
|||
opacity: 0.5; |
|||
} |
|||
65.1% { |
|||
opacity: 1; |
|||
} |
|||
75% { |
|||
opacity: 1; |
|||
} |
|||
75.1% { |
|||
opacity: 0.5; |
|||
} |
|||
77% { |
|||
opacity: 0.5; |
|||
} |
|||
77.1% { |
|||
opacity: 1; |
|||
} |
|||
85% { |
|||
opacity: 1; |
|||
} |
|||
85.1% { |
|||
opacity: 0.5; |
|||
} |
|||
86% { |
|||
opacity: 0.5; |
|||
} |
|||
86.1% { |
|||
opacity: 1; |
|||
} |
|||
100% { |
|||
opacity: 1; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,315 @@ |
|||
<template> |
|||
<view> |
|||
<swiper class="swiper" :animation="animationData" |
|||
:style="{ 'opacity': animation ? '0' : '1', |
|||
'background-color': perspective ? 'rgba(0,0,0,0.5)' : '#000000'}" |
|||
:current="swiper_current" :circular="circular" |
|||
v-if="show_assembly" @click="hideAssembly" |
|||
@transition="transition" |
|||
@touchmove.stop.prevent="touchmove" |
|||
@change="e => switchItem(e.detail.current)"> |
|||
<swiper-item v-for="(item, index) in images" :key="index" |
|||
class="swiper-item"> |
|||
<!-- 可移动缩放操作的容器 --> |
|||
<movable-area class="movable-area" :scale-area="true"> |
|||
<movable-view class="movable-view" direction="all" :scale="true" :inertia="true" @scale="scale" :scale-value="scale_values[swiper_current]"> |
|||
<image :src="item" class="swiper-image" mode="aspectFit" @mousewheel.stop="mousewheel" @longpress="longpress"></image> |
|||
</movable-view> |
|||
</movable-area> |
|||
<!-- 控制器 --> |
|||
<view class="controls" v-if="controls"> |
|||
<view> |
|||
<view @click.stop="switchItem(swiper_current - 1)" v-if="swiper_current != 0"> |
|||
<uni-icons type="arrowleft" size="36" color="#fff"></uni-icons> |
|||
</view> |
|||
<view v-else></view> |
|||
</view> |
|||
<view> |
|||
<view @click.stop="switchItem(swiper_current + 1)" v-if="swiper_current != images.length - 1"> |
|||
<uni-icons type="arrowright" size="36" color="#fff"></uni-icons> |
|||
</view> |
|||
<view v-else></view> |
|||
</view> |
|||
</view> |
|||
<!-- 指示器 --> |
|||
<view class="indicators" v-if="indicators && images.length > 1"> |
|||
<view class="number" v-if="indicatorType == 'number'">{{ swiper_current + 1 }} / {{ images.length }}</view> |
|||
<view class="square" v-else-if="indicatorType == 'square'"> |
|||
<view v-for="(item, index) in images" :key="index" class="indicators-icon" :style="swiper_current == index ? 'background-color:'+ themeColor : ''"></view> |
|||
</view> |
|||
<view class="dot" v-else-if="indicatorType == 'dot'"> |
|||
<view v-for="(item, index) in images" :key="index" class="indicators-icon" :style="swiper_current == index ? 'background-color:'+ themeColor : ''"></view> |
|||
</view> |
|||
</view> |
|||
<!-- 底部菜单弹出层 --> |
|||
<view class="menu-popup" v-if="menu && show_menu" @click.stop> |
|||
<button @click="download">保存图片</button> |
|||
</view> |
|||
</swiper-item> |
|||
</swiper> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
props: { |
|||
controls: { |
|||
type: Boolean, // 是否显示左右箭头控制 |
|||
default: true |
|||
}, |
|||
indicators: { |
|||
type: Boolean, |
|||
default: true // 是否显示指示器 |
|||
}, |
|||
indicatorType: { |
|||
type: String, // 指示器类型,dot圆点,square方形,number数字 |
|||
default: 'number' |
|||
}, |
|||
perspective: { |
|||
type: Boolean, // 是否开启背景透视,遮罩透明可以看见底下其他元素 |
|||
default: true |
|||
}, |
|||
circular: { |
|||
type: Boolean, // 是否可以衔接滑动 |
|||
default: false |
|||
}, |
|||
themeColor: { |
|||
type: String, // 主题色 |
|||
default: '#1833F2' |
|||
}, |
|||
animation: { |
|||
type: Boolean, // 是否开启动画 |
|||
default: false |
|||
}, |
|||
menu: { |
|||
type: Boolean, // 长按图片时是否显示菜单按钮,H5不支持,传参无效果 |
|||
default: true |
|||
} |
|||
}, |
|||
data(){ |
|||
return { |
|||
swiper_current: 0, // 当前显示的图片下标 |
|||
images: [], |
|||
show_assembly: false, // 显示预览图片 |
|||
duration: 500, // 动画持续时间 |
|||
animationObj: {}, // 动画配置对象 |
|||
animationData: {}, // 动画导出执行对象 |
|||
scale_values: [], // 每张图的缩放比例 |
|||
clientY: 0, |
|||
show_menu: false |
|||
} |
|||
}, |
|||
created(){ |
|||
// TODO PC端屏蔽遮罩滚动穿透 |
|||
// TODO PC端滚轮放大缩小 |
|||
// TODO 双击时会被关掉 |
|||
}, |
|||
methods: { |
|||
// 初始化加载动画 |
|||
initAnimation(){ |
|||
let animationObj = uni.createAnimation({ |
|||
duration: this.duration |
|||
}); |
|||
this.animationObj = animationObj; |
|||
animationObj.opacity(0).step(); |
|||
animationObj.opacity(1).step(); |
|||
this.animationData = animationObj.export(); |
|||
}, |
|||
// swiper touchmove事件 |
|||
touchmove(){ |
|||
return false; |
|||
}, |
|||
// swiper 位置发生变化 |
|||
transition(){ |
|||
if(this.show_menu){ |
|||
this.show_menu = false; |
|||
} |
|||
}, |
|||
// swiper被改变 |
|||
switchItem(current){ |
|||
this.swiper_current = current; |
|||
// this.clientY = 0; // 切换时需要复位 |
|||
}, |
|||
// movable 缩放事件 |
|||
scale(event){ |
|||
let scale = parseInt(event.detail.scale * 100) +'%'; |
|||
uni.showToast({ |
|||
title: scale, |
|||
icon: 'none', |
|||
position: 'bottom' |
|||
}) |
|||
}, |
|||
// 鼠标滚动缩放事件,目前会触发浏览器页面滚动事件,暂时先不要这个功能 |
|||
mousewheel(event){ |
|||
return; |
|||
let scale = this.scale_values[this.swiper_current]; |
|||
if(this.clientY > event.clientY){ |
|||
if(scale < 10){ |
|||
scale += 0.5; |
|||
} |
|||
}else{ |
|||
if(scale > 0.5){ |
|||
scale -= 0.5; |
|||
} |
|||
} |
|||
this.clientY = event.clientY; |
|||
this.scale_values.splice(this.swiper_current, 1, scale); |
|||
}, |
|||
// 显示图片 |
|||
show(options){ |
|||
this.images = options.images || []; |
|||
this.swiper_current = options.current || 0; |
|||
this.scale_values = this.images.map(item => 1); |
|||
this.show_assembly = true; |
|||
if(this.$props.animation){ |
|||
this.initAnimation(); |
|||
} |
|||
}, |
|||
// 关闭隐藏图片 |
|||
hideAssembly(){ |
|||
if(this.show_menu){ |
|||
this.show_menu = false; |
|||
return; |
|||
} |
|||
if(this.$props.animation){ |
|||
this.animationObj.opacity(0).step(); |
|||
this.animationData = this.animationObj.export(); |
|||
setTimeout(() => { |
|||
this.show_assembly = false; |
|||
}, this.duration); |
|||
}else{ |
|||
this.show_assembly = false; |
|||
} |
|||
}, |
|||
// 长按事件 |
|||
longpress(event){ |
|||
// #ifndef H5 |
|||
if(this.$props.menu){ |
|||
this.show_menu = true; |
|||
} |
|||
// #endif |
|||
}, |
|||
// 保存图片 |
|||
download(){ |
|||
uni.showLoading({ |
|||
title: '正在保存' |
|||
}) |
|||
this.show_menu = false; |
|||
uni.downloadFile({ |
|||
url: this.images[this.swiper_current], |
|||
success: res => { |
|||
let tempFilePath = res.tempFilePath; |
|||
uni.saveFile({ |
|||
tempFilePath, |
|||
success: result => { |
|||
uni.showToast({ |
|||
title: '保存成功', |
|||
icon: 'success' |
|||
}) |
|||
}, |
|||
fail: err => { |
|||
uni.showToast({ |
|||
title: '保存失败', |
|||
icon: 'none' |
|||
}) |
|||
}, |
|||
complete: () => uni.hideLoading() |
|||
}) |
|||
}, |
|||
complete: () => uni.hideLoading() |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped="scoped"> |
|||
.swiper{ |
|||
width: 100vw; |
|||
height: 100vh; |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
z-index: 1000; |
|||
.swiper-item{ |
|||
position: relative; |
|||
.movable-area, .movable-view, .swiper-image{ |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
.controls{ |
|||
position: absolute; |
|||
padding: 0 16rpx; |
|||
box-sizing: border-box; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
top: 47vh; |
|||
left: 0; |
|||
font-size: 40rpx; |
|||
width: 100%; |
|||
z-index: 1002; |
|||
} |
|||
.indicators{ |
|||
width: 100%; |
|||
position: absolute; |
|||
left: 0; |
|||
bottom: 10vh; |
|||
z-index: 1002; |
|||
display: flex; |
|||
justify-content: center; |
|||
.number, .square, .dot{ |
|||
width: max-content; |
|||
height: max-content; |
|||
background-color: #dfe4ea; |
|||
} |
|||
.number{ |
|||
padding: 4rpx 26rpx; |
|||
border-radius: 40rpx; |
|||
color: #555555; |
|||
} |
|||
.square{ |
|||
border-radius: 40rpx; |
|||
display: flex; |
|||
padding: 2rpx 4rpx; |
|||
view{ |
|||
border-radius: 50%; |
|||
} |
|||
} |
|||
.dot{ |
|||
display: flex; |
|||
padding: 4rpx 4rpx; |
|||
} |
|||
.indicators-icon{ |
|||
width: 20rpx; |
|||
height: 20rpx; |
|||
background-color: #bdc5bd; |
|||
margin: 2rpx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.menu-popup{ |
|||
position: fixed; |
|||
bottom: 0; |
|||
left: 0; |
|||
height: max-content; |
|||
width: 100%; |
|||
background-color: #FFFFFF; |
|||
z-index: 1004; |
|||
padding-bottom: constant(safe-area-inset-bottom); |
|||
padding-bottom: env(safe-area-inset-bottom); |
|||
button{ |
|||
height: 100rpx; |
|||
line-height: 100rpx; |
|||
background-color: transparent; |
|||
border-bottom: 1rpx solid #e5e5e5; |
|||
&:last-child{ |
|||
border-bottom: none; |
|||
} |
|||
} |
|||
} |
|||
// 轻提示框样式 |
|||
/deep/.uni-sample-toast{ |
|||
z-index: 1002; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,118 @@ |
|||
<template> |
|||
<view class="content"> |
|||
<view v-for="(item, index) in list" :key="index" |
|||
class="list" |
|||
:style="index == list.length - 1 ? 'padding-bottom: 60rpx' : ''"> |
|||
<view class="left"> |
|||
<view class="up-line" :class="{'remove-line': index == 0}" |
|||
:style="{'border-color': themeColor, 'background-color': themeColor}"></view> |
|||
<view class="icon" :style="{ |
|||
'background-color': index == list.length - 1 && !item.isFinished ? themeColor : '#fff', |
|||
'border-color': themeColor, |
|||
'color': themeColor}"> |
|||
<u-icon name="arrow-down" color="#fff" v-if="index == list.length - 1 && !item.isFinished"></u-icon> |
|||
<u-icon name="checkmark" v-else></u-icon> |
|||
</view> |
|||
<view class="down-line" :class="{ |
|||
'dotted-line': index == list.length - 1 && !item.isFinished, |
|||
'remove-line': index == list.length - 1 && item.isFinished |
|||
}" :style="{'border-color': themeColor, 'background-color': themeColor}"> |
|||
</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view class="desc"> |
|||
<text class="lf-line-2">{{ item.action }}</text> |
|||
<text class="date">{{ item.created_at }}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
// TODO 该组件只能显示两行的文字,如果需要更多文字,只能重构(左,定,直;圆,居;右,字,居;其,定) |
|||
export default { |
|||
name: 'lf-step-bar', |
|||
props: { |
|||
themeColor: { |
|||
type: String, |
|||
default: '#1833F2' |
|||
}, |
|||
list: { |
|||
type: Array, |
|||
default(){ |
|||
return [] |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped="scoped"> |
|||
.content{ |
|||
width: 100%; |
|||
height: max-content; |
|||
padding: 0 16px; |
|||
} |
|||
.list{ |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
min-height: 46px; |
|||
.left{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
align-content: center; |
|||
align-items: center; |
|||
margin-right: 6px; |
|||
.up-line,.down-line{ |
|||
height: 20px; |
|||
width: 1px; |
|||
border: 1px solid #1833F2; |
|||
background-color: #1833F2; |
|||
position: relative; |
|||
} |
|||
.remove-line{ |
|||
border: none !important; |
|||
background-color: transparent !important; |
|||
} |
|||
.dotted-line::after{ |
|||
content: ''; |
|||
position: absolute; |
|||
left: -1px; |
|||
bottom: -20px; |
|||
height: 20px; |
|||
width: 0px; |
|||
border: 1px dashed #999999; |
|||
} |
|||
.icon{ |
|||
width: 30px; |
|||
height: 30px; |
|||
box-sizing: border-box; |
|||
border: 2px solid #1833F2; |
|||
color: #1833F2; |
|||
border-radius: 50%; |
|||
background-color: #fff; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-content: center; |
|||
} |
|||
} |
|||
.right{ |
|||
flex: auto; |
|||
.desc{ |
|||
position: relative; |
|||
font-size: 28rpx; |
|||
color: #222222; |
|||
.date{ |
|||
position: absolute; |
|||
bottom: -18px; |
|||
left: 0; |
|||
font-size: 24rpx; |
|||
color: #999999; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue