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