Browse Source

[新增] [公共方法] 时间戳转化为时间日期格式

[新增] [公共样式] 新的类名,改变盒子模型
[新增组件] image组件
[新增组件] 预览图片组件
[新增组件] 步骤条组件
master
LAPTOP-D7TKRI82\邓 5 years ago
parent
commit
d7f4282e1d
  1. 24
      common/shared.js
  2. 8
      common/styles/common.css
  3. 190
      components/lf-image/lf-image.vue
  4. 315
      components/lf-previewImage/lf-previewImage.vue
  5. 118
      components/lf-stepbar/lf-stepbar.vue

24
common/shared.js

@ -25,3 +25,27 @@ export function isValueType(value) {
let str = Object.prototype.toString.call(value);
return str.match(/\[object (.*?)\]/)[1].toLowerCase();
}
// 解析时间戳,参数非必传,不传参时默认显示当前最新日期+时间;
// 第一个参数为当前日期时间戳,第二个日期分隔符,第三个不传参或传all显示日期+时间,传date显示日期,time显示时间
export function recordTime(time = new Date(), separator = "-", swf = 'all'){
let year = time.getFullYear();
let month = time.getMonth() + 1;
let day = time.getDate();
let hour = time.getHours();
let min = time.getMinutes();
let ppn = time.getSeconds();
if(swf === "time"){
return [hour, min, ppn].map(cover).join(":");
}else if(swf === "date"){
return [year, month, day].map(cover).join(String(separator));
}else{
return [year, month, day].map(cover).join(String(separator)) +" "+ [hour, min, ppn].map(cover).join(":");
}
}
// 位数不足2,前面补0
var cover = function(par) {
par = par.toString()[1] ? par : "0" + par;
return par;
}

8
common/styles/common.css

@ -11,6 +11,10 @@
height: 100%;
}
.lf-h-maxcontent{
height: max-content;
}
.lf-flex {
display: flex;
align-items: center;
@ -266,6 +270,10 @@
border-left: 1px solid #EEEEEE;
}
.lf-border-box{
box-sizing: border-box;
}
.lf-m-1 {
margin: 1rpx !important;
}

190
components/lf-image/lf-image.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>

315
components/lf-previewImage/lf-previewImage.vue

@ -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, // dotsquarenumber
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>

118
components/lf-stepbar/lf-stepbar.vue

@ -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>
Loading…
Cancel
Save