Init project

This commit is contained in:
lifizer
2023-09-20 21:49:33 +08:00
parent 85a5989c96
commit c9ceac9f37
710 changed files with 125705 additions and 0 deletions
@@ -0,0 +1,759 @@
<template>
<view class="vue-cropper" ref="cropper" :style="{ top : `${containerTop}px` }" v-show="show">
<view class="cropper-box">
<view class="cropper-box-canvas" @touchstart.stop.prevent="imgTouchStart" @touchmove.stop.prevent="imgMoveing" @touchend.stop.prevent="imgMoveEnd" :style="{
'width': imageWidth + 'px',
'height': imageHeight + 'px',
'transform': 'scale(' + scale + ',' + scale + ') ' + 'translate3d('+ x / scale + 'px,' + y / scale + 'px,' + '0)'
+ 'rotateZ('+ rotate * 90 +'deg)'
}">
<image :src="src" alt="cropper-img" ref="cropperImg" mode="scaleToFill" class="uni-image"></image>
</view>
</view>
<view class="cropper-drag-box cropper-modal cropper-move pointer-events"></view>
<view class="cropper-crop-box" :class="{'pointer-events': cropFixed}" :style="{'width': cropW + 'px','height': cropH + 'px','transform': 'translate3d('+ cropOffsertX + 'px,' + cropOffsertY + 'px,' + '0)'}">
<view class="cropper-view-box">
<image :style="{'width': imageWidth + 'px','height': imageHeight + 'px','transform': 'scale(' + scale + ',' + scale + ') ' + 'translate3d('+ (x - cropOffsertX) / scale + 'px,' + (y - cropOffsertY) / scale + 'px,' + '0)' + 'rotateZ('+ rotate * 90 +'deg)'}" mode="scaleToFill" :src="src" alt="cropper-img"></image>
</view>
<view v-if="!cropFixed" class="cropper-face cropper-move" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="cropMoveing"></view>
<view class="crop-line line-w"></view>
<view class="crop-line line-a"></view>
<view class="crop-line line-s"></view>
<view class="crop-line line-d"></view>
<block v-if="!cropFixed">
<view class="crop-point point-lt" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'left-top')"></view>
<view class="crop-point point-mt" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'middle-top')"></view>
<view class="crop-point point-rt" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'right-top')"></view>
<view class="crop-point point-ml" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'middle-left')"></view>
<view class="crop-point point-mr" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'middle-right')"></view>
<view class="crop-point point-lb" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'left-bottom')"></view>
<view class="crop-point point-mb" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'middle-bottom')"></view>
<view class="crop-point point-rb" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="dragMove($event, 'right-bottom')"></view>
</block>
</view>
<canvas canvas-id="myCanvas" class="cropper-canvas" :style="{ 'width': cropW + 'px','height': cropH + 'px' }"></canvas>
<view class="btn-group">
<view class="btn-item reset-btn" v-show="showResetBtn" @tap="init"></view>
<view class="btn-item rotate-btn" v-show="showRotateBtn" @tap="rotateHandler"></view>
</view>
<view class="uni-info__ft">
<view class="uni-modal__btn uni-modal__btn_default" style="color: rgb(0, 0, 0);" @tap="cancel">取消</view>
<view class="uni-modal__btn uni-modal__btn_primary" style="color: rgb(0, 122, 255);" @tap="confirm">确定</view>
</view>
</view>
</template>
<script>
export default {
name: 'image-cropper',
props: {
cropWidth: {
type: Number,
default: 200,
},
cropHeight: {
type: Number,
default: 200
},
cropFixed: {
type: Boolean,
default: false,
},
src: {
type: String,
},
showResetBtn: {
type: Boolean,
default: true,
},
showRotateBtn: {
type: Boolean,
default: true,
}
},
data() {
const sysInfo = uni.getSystemInfoSync();
const pixelRatio = sysInfo.pixelRatio
return {
show: false,
scale: 1,
rotate: 0,
cropW: 0,
cropH: 0,
cropOldW: 0,
cropOldH: 0,
sysInfo: sysInfo,
pixelRatio: pixelRatio,
imageRealWidth: 0,
imageRealHeight: 0,
cropOffsertX: 0,
cropOffsertY: 0,
startX: 0,
startY: 0,
// 裁剪框与边界间距
border: 5,
x: 0,
y: 0,
startL: 0,
oldScale: 1,
}
},
watch: {
src(val) {
if(val.length > 0) {
this.init()
}
},
show(val) {
if(!val) {
this.src = ''
}
}
},
computed: {
containerTop() {
let top = 0
// #ifdef H5
top = 44
// #endif
return top;
},
// 容器高度
containerHeight() {
return this.windowHeight - 48;
},
// 屏幕宽度
windowWidth() {
return this.sysInfo.windowWidth;
},
windowHeight() {
return this.sysInfo.windowHeight;
},
// 图片宽高比
imageRatio() {
if (this.imageRealHeight > 0) {
return this.imageRealWidth / this.imageRealHeight
}
return 0
},
// 等比缩放后的宽度
imageWidth() {
if (this.imageRatio >= 1) {
return this.windowWidth
}
return this.windowWidth * this.imageRatio
},
// 等比缩放后的高度
imageHeight() {
if (this.imageRatio >= 1) {
return this.windowWidth / this.imageRatio
}
return this.windowWidth
},
},
methods: {
rotateHandler() {
if(this.rotate == 3) {
this.rotate = 0;
} else {
++this.rotate
}
},
init() {
this.rotate = 0;
this.scale = 1;
this.cropW = this.cropWidth
this.cropH = this.cropHeight
uni.showLoading({
title: '图片加载中...',
})
this.loadImage(this.src).then((e) => {
uni.hideLoading()
}).catch((e) => {
uni.hideLoading()
uni.showModal({
title: '标题',
content: '图片加载失败'
})
})
},
loadImage(src) {
const _this = this
return new Promise((resolve, reject) => {
uni.getImageInfo({
src: src,
success: (res) => {
_this.imageRealWidth = res.width
_this.imageRealHeight = res.height
_this.cropOffsertX = _this.windowWidth / 2 - _this.cropW / 2
_this.cropOffsertY = _this.windowHeight / 2 - _this.cropH / 2
_this.show = true
_this.$nextTick(() => {
_this.x = _this.windowWidth / 2 - _this.imageWidth / 2
_this.y = _this.containerHeight / 2 - _this.imageHeight / 2
});
resolve(res)
},
fail: (e) => {
_this.show = false
reject(e)
}
})
});
},
cancel() {
this.show = false
this.$emit('cancel')
},
confirm(event) {
uni.showLoading({
title: '裁剪中...',
})
const _this = this
const ctx = uni.createCanvasContext('myCanvas', _this);
const pixelRatio = _this.pixelRatio
const imgage = _this.src
const imgW = _this.imageWidth * _this.scale;
const imgH = _this.imageHeight * _this.scale
const rotate = _this.rotate
let dx = _this.cropOffsertX - _this.x - (_this.imageWidth - imgW) / 2;
let dy = _this.cropOffsertY - _this.y - (_this.imageHeight - imgH) / 2;
ctx.setFillStyle('white')
ctx.fillRect(0, 0, imgW, imgH)
ctx.save()
ctx.rotate((rotate * 90 * Math.PI) / 180);
switch (rotate) {
case 1:
dx += (imgH-imgW) / 2
dy -= (imgH-imgW) / 2
ctx.drawImage(imgage, -dy, dx, imgW, -imgH);
break;
case 2:
ctx.drawImage(imgage, dx, dy, -imgW, -imgH);
break;
case 3:
dx += (imgH-imgW) / 2
dy -= (imgH-imgW) / 2
ctx.drawImage(imgage, dy, -dx, -imgW, imgH);
break;
default:
ctx.drawImage(imgage, -dx, -dy, imgW, imgH);
break;
}
ctx.restore()
ctx.draw(false, () => {
uni.canvasToTempFilePath({
canvasId: 'myCanvas',
destWidth: _this.cropW * pixelRatio,
destHeight: _this.cropH * pixelRatio,
success: (res) => {
uni.hideLoading()
event.detail.tempFilePath = res.tempFilePath
_this.show = false
_this.$emit('confirm', event)
},
fail: (e) => {
uni.hideLoading()
uni.showModal({
title: '提示',
content: '裁剪失败'
})
}
}, _this);
})
},
imgTouchStart(e) {
if(e.touches.length == 2) {
this.oldScale = this.scale
this.scaling = true
const x = e.touches[0].pageX - e.touches[1].pageX
const y = e.touches[0].pageY - e.touches[1].pageY
const hypotenuse = Math.sqrt(
Math.pow(x, 2) +
Math.pow(y, 2)
)
this.startL = Math.max(x, y, hypotenuse)
uni.showModal({
content: this.startL
})
} else {
this.startX = e.touches[0].pageX - this.x
this.startY = e.touches[0].pageY - this.y
}
},
imgMoveing(e) {
if(this.scaling) {
let scale = this.oldScale
const x = e.touches[0].pageX - e.touches[1].pageX
const y = e.touches[0].pageY - e.touches[1].pageY
const hypotenuse = Math.sqrt(
Math.pow(x, 2) +
Math.pow(y, 2)
)
const newL = Math.max(x, y, hypotenuse)
const cha = newL - this.startL;
// 根据图片本身大小 决定每次改变大小的系数, 图片越大系数越小
// 1px - 0.2
let coe = 1;
coe =
coe / this.imageWidth > coe / this.imageHeight
? coe / this.imageHeight
: coe / this.imageWidth;
coe = coe > 0.1 ? 0.1 : coe;
const num = coe * cha;
if (cha > 0) {
scale += Math.abs(num);
} else if (cha < 0) {
scale > Math.abs(num) ? (scale -= Math.abs(num)) : scale;
}
this.scale = scale;
} else {
const moveX = e.touches[0].pageX - this.startX
const moveY = e.touches[0].pageY - this.startY
this.x = moveX
this.y = moveY
}
},
imgMoveEnd() {
setTimeout(() => {
this.scaling = false
}, 100)
},
touchStart(e) {
this.startX = e.touches[0].pageX - this.cropOffsertX;
this.startY = e.touches[0].pageY - this.cropOffsertY;
this.cropOldW = this.cropW
this.cropOldH = this.cropH
},
cropMoveing(e) {
const moveX = this._cropX(e.touches[0].pageX - this.startX)
const moveY = this._cropY(e.touches[0].pageY - this.startY)
this.cropOffsertX = moveX
this.cropOffsertY = moveY
},
dragMove(e, type) {
if(this.cropFixed) {
return false
}
const moveX = e.touches[0].pageX - this.startX
const moveY = e.touches[0].pageY - this.startY
switch (type) {
case 'left-top':
this._cropMoveLeft(moveX)
this._cropMoveTop(moveY)
break;
case 'middle-top':
this._cropMoveTop(moveY)
break;
case 'right-top':
this._cropMoveTop(moveY)
this._cropMoveRight(moveX)
break;
case 'middle-right':
this._cropMoveRight(moveX)
break;
case 'right-bottom':
this._cropMoveRight(moveX)
this._cropMoveBottom(moveY)
break;
case 'middle-bottom':
this._cropMoveBottom(moveY)
break;
case 'left-bottom':
this._cropMoveBottom(moveY)
this._cropMoveLeft(moveX)
break;
case 'middle-left':
this._cropMoveLeft(moveX)
break;
default:
break;
}
},
_cropMoveTop(y) {
const topY = this._cropY(y)
this.cropH += this.cropOffsertY - topY
this.cropOffsertY = topY
},
_cropMoveRight(x) {
if(this.cropOldW + x >= this.windowWidth - this.border) {
return false;
}
this.cropW = this.cropOldW + (x - this.cropOffsertX)
},
_cropMoveBottom(y) {
if(this.cropOldH + y >= this.windowHeight - this.containerTop - this.border) {
return false;
}
this.cropH = this.cropOldH + (y - this.cropOffsertY)
},
_cropMoveLeft(x) {
const leftX = this._cropY(x)
this.cropW += this.cropOffsertX - leftX
this.cropOffsertX = leftX
},
_cropX(x) {
if(x <= this.border) {
return this.border
}
if(x + this.cropW >= this.windowWidth - this.border) {
return this.windowWidth - this.cropW - this.border
}
return x
},
_cropY(y) {
if(y <= this.border) {
return this.border
}
if(y + this.cropH >= this.windowHeight - this.containerTop - this.border) {
return this.windowHeight - this.cropH - this.containerTop - this.border
}
return y
}
}
}
</script>
<style scoped lang="css">
@font-face {
font-family: "iconfont";
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAR4AAsAAAAACKgAAAQsAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDBgqEfIRGATYCJAMMCwgABCAFhG0HShugB8gOJUHBwAAAAAFEBNmwzd4dtatSmmpFoVAEhUThEAYkCozFKDCqCVO6RfH/89v869awDnTR1qrSANFt4GG4SNxreBn91fmV9f3+53J613ieHba+N1zmGM8PA7oXTaCAxpjei8IoLWFsGLu4jPME6vWJJdovqmgAO4U2LRBnep0K7GJmpYQWanXVOWuLuAFrtenK4haAa/f38QnKsCOpyrRFh6eFWsh5KXnfYcn958BGQNKfE8wmMmaAQpzkuo9Z+ukZluoltVV5abUipL5i/ysArlhWVut/eCRBVNPUjYg6oUo7JTHFoaYDSvdacnKTq9GAB4AY5y2dtL3qpFh1DENdnJC6Hq+xYb7pyRMDMzc/fYoJjY8flwO3m98rMucF+IZHj6Cagw5UeKpxyFbt2rHGY/8jpa7CYMvLfcIesLjY3bdqhaf+nqgQs2qT/+rjCH/VfA0VFGuAC3iE8NEr/Vau8vZsXiUy7+V3c3tQQXMAuNjDCC89KDIHH0OFhnUi81GEPwyc7wZUaN7DnUf4g+ZLQsMKYV/94NjK7R7TEM4niTY1oJ5zEU62aNVaasUub08YLUEam5EnT6a61/I17dNk+vTu9jpJjXhsTFwjqTtpCBxBIIgS6iQnc/Zod1YGKp0rAwsD8kkyP6AwcK0hcAwkiQmBhWvxPZWKDu86aUH2nLEdi9rGX1eXq5P6A1SrnAucMVMdZH/GKi/jyfCqJyucfK3mXpVujXOPfFf5LC4Dvx0X/943JyOq4HuCTZ8KiIPPAb6ro8akpT6ufiq39BQrNlk5mp8pO0JlJLk8f5QalRjoP60IMx0N8n7wGhSD3n6/F1zlcTVz/cR+Ev0lkLSTd7UiPbD/wCxGRMA2Krwro2O0bTQtImbwhjAJc0S3N4ROx15/PH60IzaIOjCbEelqkDOfETNxb/FMixnWNzeJp2KPQw9A5d76jGUOQOUvH7RE/o2RfkNatd3OGf9q0QKbnq8WB7qy+hVqJRjJn1BQgP/iErks0yy5iGJTrOayW7C/z0IoZH0qNH+7N+31XXc7G2p1hZDU6IWs1ghaqDNQpcEKVKu1BfWmFW9u0IFhKUodpswCEFodgqTZHWStbqOF+hqqdPsG1VrDEuodhfueDcZCj+QzuIrFtZh6BNNraIowbCzi1dbhOlOfionKXHoTzgzoY5hCKk/minEKZ/pYMDCoU7IsgREM3Y8Vgcvwvj4aMzK0AdewUpJljWkyGZH3IKmG7gfEHgZOhYXTwqiNwOhp0CiE3ZiFpL5fB6dj0keFKcGV+JvgGAP0vWMUpOQ10GI1VQt3LoMHDNJRYrEIPInAoPXDFEEnrk9P0zDG/FEGOA2WFNkiaZRGhuoRddXS8bX917cL6mn9c6TIUXSekybKHKQfJXFq2KSiRklLYU8dNKWDIX0cAA==') format('woff2');
}
.vue-cropper {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 998;
box-sizing: border-box;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
direction: ltr;
touch-action: none;
text-align: left;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC");
}
.cropper-canvas {
position: absolute;
top: -9999px;
left:-9999px;
z-index: -998;
}
.vue-cropper .uni-info__ft {
position: absolute;
line-height: 48px;
font-size: 18px;
display: -webkit-box;
display: -webkit-flex;
display: flex;
bottom: 0;
left: 0;
right: 0;
z-index: 998;
}
.btn-group {
position: absolute;
right: 30px;
bottom: 78px;
z-index: 998;
}
.btn-item {
position: relative;
/* width: 40px; */
/* height: 40px; */
background: #fff;
border-radius: 20px;
padding: 10px;
display: inline-block;
margin-left: 10px;
}
.btn-item:active {
background: #ccc;
}
.rotate-btn {
font-family: "iconfont" !important;
font-size: 24px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
line-height: 20px;
}
.rotate-btn:before {
content: "\e65c";
margin-left: -2px;
}
.reset-btn {
font-family: "iconfont" !important;
font-size: 24px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
line-height: 20px;
}
.reset-btn:before {
content: "\e648";
margin-left: -2px;
}
.vue-cropper .uni-info__ft:after {
content: " ";
position: absolute;
left: 0;
top: 0;
right: 0;
height: 1px;
border-top: 1px solid #d5d5d6;
color: #d5d5d6;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
-webkit-transform: scaleY(.5);
transform: scaleY(.5);
z-index: 998;
}
.vue-cropper .uni-modal__btn {
display: block;
-webkit-box-flex: 1;
-webkit-flex: 1;
flex: 1;
color: #3cc51f;
text-decoration: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
position: relative;
text-align: center;
background-color: #fff;
z-index: 998;
}
.vue-cropper .uni-modal__btn:first-child:after { display: none }
.vue-cropper .uni-modal__btn:after {
content: " ";
position: absolute;
left: 0;
top: 0;
width: 1px;
bottom: 0;
border-left: 1px solid #d5d5d6;
color: #d5d5d6;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
-webkit-transform: scaleX(.5);
transform: scaleX(.5);
z-index: 998;
}
.vue-cropper .uni-modal__btn:active {
background-color: #eee;
}
.cropper-box,
.cropper-box-canvas,
.cropper-drag-box,
.cropper-crop-box,
.cropper-face {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
user-select: none;
z-index: 998;
}
.uni-image {
width: 100%;
height: 100%;
}
.cropper-box-canvas image {
position: relative;
text-align: left;
user-select: none;
transform: none;
max-width: none;
max-height: none;
z-index: 998;
}
.cropper-box {
overflow: hidden;
}
.cropper-move {
cursor: move;
}
.cropper-crop {
cursor: crosshair;
}
.cropper-modal {
background: rgba(0, 0, 0, 0.5);
}
.pointer-events {
pointer-events:none;
}
.cropper-crop-box {
/*border: 2px solid #39f;*/
}
.cropper-view-box {
display: block;
overflow: hidden;
width: 100%;
height: 100%;
outline: 1px solid #39f;
outline-color: rgba(51, 153, 255, 0.75);
user-select: none;
}
.cropper-view-box image {
user-select: none;
text-align: left;
max-width: none;
max-height: none;
}
.cropper-face {
top: 0;
left: 0;
background-color: #fff;
opacity: 0.1;
}
.crop-line {
position: absolute;
display: block;
width: 100%;
height: 100%;
opacity: 0.1;
z-index: 998;
}
.line-w {
top: -3px;
left: 0;
height: 5px;
cursor: n-resize;
}
.line-a {
top: 0;
left: -3px;
width: 5px;
cursor: w-resize;
}
.line-s {
bottom: -3px;
left: 0;
height: 5px;
cursor: s-resize;
}
.line-d {
top: 0;
right: -3px;
width: 5px;
cursor: e-resize;
}
.crop-point {
position: absolute;
width: 8px;
height: 8px;
opacity: 0.75;
background-color: #39f;
border-radius: 100%;
z-index: 998;
}
.point-lt {
top: -4px;
left: -4px;
cursor: nw-resize;
}
.point-mt {
top: -5px;
left: 50%;
margin-left: -3px;
cursor: n-resize;
}
.point-rt {
top: -4px;
right: -4px;
cursor: ne-resize;
}
.point-ml {
top: 50%;
left: -4px;
margin-top: -3px;
cursor: w-resize;
}
.point-mr {
top: 50%;
right: -4px;
margin-top: -3px;
cursor: e-resize;
}
.point-lb {
bottom: -5px;
left: -4px;
cursor: sw-resize;
}
.point-mb {
bottom: -5px;
left: 50%;
margin-left: -3px;
cursor: s-resize;
}
.point-rb {
bottom: -5px;
right: -4px;
cursor: se-resize;
}
</style>
+209
View File
@@ -0,0 +1,209 @@
<template>
<view class="user-addBankCard">
<view class="acea-row row-column">
<view class="input-box acea-row row-column">
<view class="input-item acea-row row-middle">
<view class="input-title">持卡人</view>
<view class="input">
<input
placeholder="请输入持卡人姓名"
placeholder-style="color:#DADCE0"
v-model="name"
/>
</view>
</view>
<view class="input-item acea-row row-middle">
<view class="input-title">卡号</view>
<view class="input">
<input
placeholder="请输入卡号"
placeholder-style="color:#DADCE0"
type="number"
v-model="cardNo"
@input='inputCardChange'
/>
</view>
</view>
<view class="input-item acea-row row-middle">
<view class="input-title">银行</view>
<view class="input acea-row row-middle" style="position: relative;">
<picker style="width: 100%;z-index: 1;" @change="bindPickerChange" :value="index" :range="banks">
<view class="" style="font-size: 28rpx;">{{ banks[index] }}</view>
</picker>
<uni-icons class="bankArrow" type="arrowright" size="13"></uni-icons>
</view>
</view>
</view>
<view class="acea-row row-center row-middle">
<button class="confirm-btn" @click="confirmAdd">确认添加</button>
</view>
</view>
</view>
</template>
<script>
import {bankList, addBankCard} from "@/api/user";
export default {
components: {},
data: function () {
return {
name: "",
cardNo: "",
bankName: "",
bankSub: "",
bankList: [],
banks: [],
index: 0,
showPicker: false
};
},
watch: {},
mounted: function () {
this.getBankList();
},
methods: {
inputCardChange: function (event) {
var value = event.target.value;
console.log('value:' + value);
this.cardNo = value.replace(/[^\d]/g, ""); // 不允许输入非数字字符
},
bindPickerChange: function (e) {
console.log('picker发送选择改变,携带值为', e.target.value);
this.index = e.target.value;
this.bankName = this.bankList[this.index].name;
},
bankSelectClick: function () {
this.showPicker = true;
},
onConfirmBank: function (value, index) {
this.bankName = value;
this.showPicker = false;
},
onBackClick: function () {
this.$router.back();
},
getBankList: function () {
let that = this;
bankList({
page: 1,
limit: 999
})
.then(res => {
that.bankList = res.data;
that.banks = [];
that.bankList.forEach(function (bank, idx) {
that.banks.push(bank.name);
if (idx == 0) {
that.bankName = bank.name;
}
});
})
.catch(err => {
that.$dialog.error(err.msg || "获取银行列表失败");
});
},
//提交
confirmAdd: function () {
let that = this;
//参数检查
if (that.name.length == 0) {
that.$dialog.error("请输入持卡人姓名");
return;
}
if (that.cardNo.length == 0) {
that.$dialog.error("请输入银行卡号");
return;
}
if (that.bankName.length == 0) {
that.$dialog.error("请选择银行");
return;
}
that.$dialog.loading.open();
addBankCard({
bankName: that.bankName,
// bankSub: that.bankSub,
cardNo: that.cardNo,
name: that.name
})
.then(res => {
that.$dialog.loading.close();
that.$dialog.toast({mes: "添加成功"});
setTimeout(function () {
that.$yrouter.back();
}, 1000);
})
.catch(err => {
that.$dialog.loading.close();
that.$dialog.error(err.msg || "添加失败");
});
}
}
};
</script>
<style scoped lang="less">
.user-addBankCard {
min-height: 100vh;
padding: 60rpx 32rpx;
background: #F9F9F9;
color: #333333;
font-size: 24rpx;
line-height: 34rpx;
.input-box {
.input-item {
margin-bottom: 24rpx;
.input-title {
width: 96rpx;
margin-right: 10rpx;
font-weight: bold;
flex-shrink: 0;
}
.input {
position: relative;
height: 80rpx;
box-sizing: border-box;
padding: 8rpx 24rpx;
border-radius: 12rpx;
background: #FFFFFF;
flex-grow: 1;
input {
width: 100%;
height: 100%;
}
}
.bankArrow {
position: absolute;
right: 24rpx;
top: 0;
line-height: 80rpx;
}
}
}
.confirm-btn {
width: 686rpx;
height: 80rpx;
margin-top: 56rpx;
border-radius: 8rpx;
background: #FD574B;
color: #FFFFFF;
font-size: 28rpx;
}
}
</style>
+274
View File
@@ -0,0 +1,274 @@
<template>
<view class="container">
<mescroll-body ref="mescrollRef" top="0" bottom="0" class='cotent-scroll' :up="upOption" :down="downOption"
@up="upCallback" @down="downCallback" @init="mescrollInit" @emptyclick="emptyClick">
<view id="dataList" class="data-list acea-row row-column">
<view class="item acea-row row-column" :style="{'background':card.bgColor}" :key="index"
v-for="(card, index) in list" @click="cardClick(card)">
<view class="acea-row row-column">
<view class="acea-row row-between">
<view class="txt-bankname">{{ card.bankName }}</view>
<view class="unbind-btn" @click="unbindCard(index)">解除绑定</view>
</view>
<view class="acea-row row-middle" style="margin-top: 58rpx;flex-wrap: nowrap;">
<view class="acea-row row-left" style="flex: 1;margin-right: 18rpx;">
<view class="dot"></view>
<view class="dot"></view>
<view class="dot"></view>
<view class="dot"></view>
</view>
<view class="acea-row row-left" style="flex: 1;margin-right: 18rpx;">
<view class="dot"></view>
<view class="dot"></view>
<view class="dot"></view>
<view class="dot"></view>
</view>
<view class="acea-row row-left" style="flex: 1;margin-right: 18rpx;">
<view class="dot"></view>
<view class="dot"></view>
<view class="dot"></view>
<view class="dot"></view>
</view>
<view class="" style="flex-shrink: 0;">
<text class="txt-cardno">{{ last4CardNo(card.cardNo) }}</text>
</view>
</view>
<view class="txt-name" style="margin-top: 38rpx;">{{ card.name }}</view>
</view>
</view>
</view>
<view class="bottom-buttons-wrap acea-row row-middle row-between" @click="addBtnClick">
<view class="" style="color: #999999;font-size: 28rpx;">添加银行卡</view>
<view class="acea-row row-middle">
<image style="width: 82rpx;height: 82rpx;" :src="webUrl+'/20220922225634717078.png'" mode=""></image>
</view>
</view>
<view id="loading-more"></view>
</mescroll-body>
</view>
</template>
<script>
import MescrollMixins from "@/mixins/mescroll-mixins.js";
import MescrollBody from "@/components/mescroll-uni/mescroll-body.vue";
import {getBank, postCashInfo, bankCardList, deleteBankCard} from "@/api/user";
export default {
data() {
return {
list: [],
upOption: {
noMoreSize: 10, //如果列表已无数据,可设置列表的总数量要大于半页才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看; 默认5
auto: true,
empty: {
tip: '暂无银行卡' // 提示
// btnText:'点击刷新'
}
},
downOption: {
auto: false
},
isSelectMode: false,
webUrl: this.$VUE_APP_RESOURCES_URL,
bgColor: ["#FD574B", "#0DC5C5", "#3F57DA", "#FF4F48"]
}
},
components: {
MescrollBody
},
mixins: [
MescrollMixins({
getPageData(mescroll) {
var _this = this;
// 此时mescroll会携带page的参数:
let pageNum = mescroll.num; // 页码, 默认从1开始
let pageSize = mescroll.size; // 页长, 默认每页10条
var params = {
limit: pageNum,
page: pageSize
}
bankCardList(params)
.then(res => {
if (mescroll.num == 1) {
_this.list = []; //如果是第一页需手动制空列表
}
_this.list = _this.list.concat(res.data);//追加新数据
_this.handleColor();
//mescroll.endByPage(res.data, res.data.total);
mescroll.endSuccess(res.data.length);
})
.catch(err => {
mescroll.endErr();
uni.showToast({
title: err + '',
icon: 'none',
mask: false,
duration: 1500
});
})
}
})
],
onLoad: function (e) {
//从query里取模式
this.isSelectMode = this.$yroute.query.isSelected;
},
onShow: function () {
//静默刷新
this.mescroll && this.mescroll.resetUpScroll(false);
},
methods: {
last4CardNo: function (cardNo) {
var str = cardNo;
var reg = /^\d+(\d{4})$/;
str = str.replace(reg, "$1");
return str;
},
formartCard: function (cardNo) {
var str = cardNo;
var reg = /^(\d{4})\d+(\d{4})$/;
str = str.replace(reg, "$1 **** **** $2");
return str;
//return cardNo.replace(/(\d{4})(?=\d)/g, '$1 ');
},
cardClick: function (card) {
//判断是否选择模式
if (this.isSelectMode) {
//把数据放入本地存储里,返回上一页时再取出来
//localStorage.setItem("selectedBankCard", JSON.stringify(card));
// uni.setStorage({
// "selectedBankCard":JSON.stringify(card)
// });
uni.setStorageSync("selectedBankCard", JSON.stringify(card));
this.$yrouter.back();
} else {
}
},
addBtnClick: function () {
uni.navigateTo({
url:"addBankCard"
})
},
unbindCard: function (index) {
let that = this;
let card = that.list[index];
let id = card.id;
uni.showModal({
title: '是否解绑此银行卡?',
content: '',
showCancel: true,
cancelText: '否',
confirmText: '是',
success: res1 => {
if (res1.confirm) {
deleteBankCard({
id: id
}).then(res => {
uni.showToast({
title: res.msg,
icon: "success",
duration: 2000
});
//刷新界面数据
that.mescroll && that.mescroll.resetUpScroll(false);
});
} else {
}
},
fail: () => {
},
complete: () => {
}
});
},
handleColor() {
if (this.list.length > 0) {
this.list.forEach((item, index) => {
item.bgColor = this.bgColor[index % 4];
})
}
}
}
}
</script>
<style>
.data-list {
padding: 40rpx 32rpx 0;
}
.item {
flex-wrap: nowrap;
margin-bottom: 40rpx;
padding: 32rpx 40rpx;
/*background: linear-gradient(*/
/* 221deg,*/
/* rgba(255, 187, 140, 1) 0%,*/
/* rgba(250, 134, 135, 1) 100%*/
/*);*/
opacity: 1;
border-radius: 20rpx;
}
.txt-bankname {
font-size: 30rpx;
color: #fff;
font-weight: bold;
}
.txt-cardno {
font-size: 40rpx;
color: #fff;
font-weight: bold;
}
.txt-name {
font-size: 26rpx;
color: #fff;
font-weight: bold;
}
.unbind-btn {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.6);
}
.bottom-buttons-wrap {
height: 106rpx;
margin: 0 32rpx;
padding: 0 26rpx 0 48rpx;
box-shadow: 0px 6px 20px rgba(0, 0, 0, 0.16);
background-color: #fff;
}
.add-btn {
font-size: 36rpx;
background: linear-gradient(90deg, rgba(255, 149, 141, 1) 0%, rgba(255, 94, 138, 1) 100%);
border-radius: 44rpx;
color: rgba(255, 255, 255, 1);
padding: 20rpx 70rpx;
flex-wrap: nowrap;
}
.dot {
width: 18rpx;
height: 18rpx;
background: #fff;
border-radius: 50%;
opacity: 0.3;
margin-right: 18rpx;
}
</style>
+222
View File
@@ -0,0 +1,222 @@
<template>
<view class="user-bindPhone">
<view class="input-box flex ai-center" style="margin-bottom: 32rpx">
<image class="icon-phone flex-0" :src="webUrl+'/20220925102913233030.png'"/>
<input v-model="phone" type="number" placeholder="请填写手机号码" placeholder-style="color:#E2E2E2"/>
</view>
<view class="flex jc-between ai-center">
<view class="input-box flex ai-center">
<input v-model="captcha" type="text" placeholder="请填写验证码" placeholder-style="color:#E2E2E2"/>
</view>
<button class="btn-code" :class="disabled === true ? 'on' : ''"
:disabled="disabled" @click="code">{{ text }}
</button>
</view>
<button class="btn-save" @click="confirm">确认绑定</button>
</view>
</template>
<script>
import {mapGetters} from "vuex";
import sendVerifyCode from "@/mixins/SendVerifyCode";
import {required, alpha_num, chs_phone} from "@/utils/validate";
import {validatorDefaultCatch} from "@/utils/dialog";
import {registerVerify, bindingPhoneByInput} from "@/api/user";
export default {
name: "BindingPhone",
components: {},
props: {},
data: function () {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
captcha: "",
phone: "" //手机号
};
},
mixins: [sendVerifyCode],
computed: mapGetters(["userInfo"]),
mounted: function () {
},
methods: {
async confirm() {
let that = this;
const {phone, captcha} = that;
try {
await that
.$validator({
phone: [
chs_phone(chs_phone.message("手机号码")),
alpha_num(alpha_num.message())
],
captcha: [
required(required.message("验证码")),
alpha_num(alpha_num.message("验证码"))
]
})
.validate({phone, captcha});
} catch (e) {
return validatorDefaultCatch(e);
}
bindingPhoneByInput({
phone: this.phone,
captcha: this.captcha
})
.then(res => {
if (res.data !== undefined && res.data.is_bind) {
uni.showModal({
title: "提示",
content: "确认绑定?",
success: function (res) {
if (res.confirm) {
bindingPhoneByInput({
phone: this.phone,
captcha: this.captcha,
step: 1
})
.then(res => {
uni.showToast({
title: res.msg,
icon: "none",
duration: 2000
});
that.$yrouter.replace({
path: "/pkg_user/views/personalData"
});
})
.catch(res => {
uni.showToast({
title: res.msg,
icon: "none",
duration: 2000
});
that.$yrouter.replace({
path: "/pkg_user/views/personalData"
});
});
} else if (res.cancel) {
uni.showToast({
title: "已取消绑定",
icon: "none",
duration: 2000
});
that.$yrouter.replace({
path: "/pkg_user/views/personalData"
});
}
}
});
} else {
uni.showToast({
title: res.msg,
icon: "none",
duration: 2000
});
that.$yrouter.replace({
path: "/pkg_user/views/personalData"
});
}
})
.catch(res => {
uni.showToast({
title: res.msg,
icon: "none",
duration: 2000
});
});
},
async code() {
let that = this;
const {phone} = that;
try {
await that
.$validator({
phone: [
required(required.message("手机号码")),
chs_phone(chs_phone.message())
]
})
.validate({phone});
} catch (e) {
return validatorDefaultCatch(e);
}
registerVerify({phone: phone})
.then(res => {
uni.showToast({
title: res.msg,
icon: "none",
duration: 2000
});
that.sendCode();
})
.catch(res => {
uni.showToast({
title: res.msg,
icon: "none",
duration: 2000
});
});
}
}
};
</script>
<style scoped lang="less">
.user-bindPhone {
min-height: 100vh;
padding: 60rpx 32rpx;
background: #F9F9F9;
font-size: 28rpx;
line-height: 40rpx;
button {
border-radius: 8rpx;
color: #FFFFFF;
font-size: 28rpx;
font-weight: bold;
}
button:after {
display: none;
}
.input-box {
height: 100rpx;
padding: 0 36rpx;
border-radius: 8rpx;
background: #FFFFFF;
color: #999999;
.icon-phone {
width: 48rpx;
height: 48rpx;
vertical-align: middle;
margin-right: 20rpx;
}
input {
font-size: 28rpx;
line-height: 40rpx;
}
}
.btn-code {
width: 200rpx;
height: 72rpx;
margin: 0 0 0 32rpx;
background: #28AAF4;
line-height: 72rpx;
}
.btn-save {
height: 80rpx;
margin-top: 80rpx;
background: #FD574B;
line-height: 80rpx;
}
}
</style>
+285
View File
@@ -0,0 +1,285 @@
<template>
<view class="user-payPassword">
<!-- <view class="content acea-row row-column">-->
<!-- <input class="phone-input" type="text" :value="phone" disabled/>-->
<!-- <view class="acea-row row-middle" style="flex-wrap: nowrap;">-->
<!-- <input type="text" placeholder="填写验证码" placeholder-style="color:#DADCE0;font-size:28rpx;" class="codeIput" v-model="captcha" />-->
<!-- <button-->
<!-- class="code acea-row row-middle"-->
<!-- :disabled="disabled"-->
<!-- :class="disabled === true ? 'on' : ''"-->
<!-- @click="code"-->
<!-- >{{ text }}</button>-->
<!-- </view>-->
<!-- <input type="number" maxlength="6" password placeholder="请输新6位数字支付密码" placeholder-style="color:#DADCE0;font-size:28rpx" class="payPwdInput" v-model="payPwd" @input='inputPaypwdChange' />-->
<!-- </view>-->
<!-- <view class="confirmBnt bg-color-red" @click="confirm()">保存</view>-->
<view class="input-box flex ai-center" style="margin-bottom: 32rpx">
<image class="icon-phone flex-0" :src="webUrl+'/20220925102913233030.png'"/>
<input :value="phone" disabled/>
</view>
<view class="flex jc-between ai-center">
<view class="input-box flex ai-center">
<input v-model="captcha" type="text" placeholder="请填写验证码" placeholder-style="color:#E2E2E2"/>
</view>
<button class="btn-code" :class="disabled === true ? 'on' : ''"
:disabled="disabled" @click="code">{{ text }}
</button>
</view>
<view class="input-box flex ai-center" style="margin-top: 32rpx">
<input style="width: 100%" v-model="payPwd" type="number" maxlength="6" placeholder="请重新输入6位数字支付密码" placeholder-style="color:#E2E2E2" @input="inputPaypwdChange"/>
</view>
<button class="btn-save" @click="confirm">保存</button>
</view>
</template>
<script>
import { mapGetters } from "vuex";
import sendVerifyCode from "@/mixins/SendVerifyCode";
import { required, alpha_num, chs_phone } from "@/utils/validate";
import { validatorDefaultCatch } from "@/utils/dialog";
import { getUserInfo, registerVerify, bindingPhone,modifyUserPayPwd } from "@/api/user";
export default {
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
captcha: "",
phone:'',
payPwd:''
}
},
mixins: [sendVerifyCode],
watch:{
userInfo(){
this.phone = this.userInfo.phone;
}
},
computed:{
...mapGetters(["userInfo"]),
},
onLoad:function(e){
if (e.isSetMode) {
uni.setNavigationBarTitle({
title: '设置支付密码'
});
} else{
uni.setNavigationBarTitle({
title: '修改支付密码'
});
}
//获取用户信息
if (this.$store.getters.token) {
this.$store.dispatch("getUser", true);
}
},
methods: {
inputPaypwdChange:function(event){
var value = event.target.value;
var filterValue = value.replace(/\D/g,"");
//console.log('value:'+filterValue);
this.payPwd = filterValue; // 不允许输入非数字字符
},
async code() {
let that = this;
const { phone } = that;
try {
await that
.$validator({
phone: [
required(required.message("手机号码")),
chs_phone(chs_phone.message())
]
})
.validate({ phone });
} catch (e) {
return validatorDefaultCatch(e);
}
registerVerify({ phone: phone })
.then(res => {
uni.showToast({
title: res.data,
icon: "none",
duration: 2000
});
that.sendCode();
})
.catch(res => {
uni.showToast({
title: res.msg,
icon: "none",
duration: 2000
});
});
},
async confirm() {
let that = this;
const { phone, captcha, payPwd } = that;
try {
await that
.$validator({
phone: [
chs_phone(chs_phone.message("手机号码")),
alpha_num(alpha_num.message())
],
captcha: [
required(required.message("验证码")),
alpha_num(alpha_num.message("验证码"))
],
payPwd: [
required(required.message("支付密码")),
alpha_num(alpha_num.message("支付密码"))
]
})
.validate({ phone, captcha,payPwd });
} catch (e) {
return validatorDefaultCatch(e);
}
//网络请求
uni.showLoading();
modifyUserPayPwd({
passwordPay:that.payPwd,
code:that.captcha,
phone:that.phone
}).then((res)=>{
//提示成功并返回
uni.hideLoading();
that.$dialog.toast({ mes: "保存成功" });
setTimeout(function(){
that.$yrouter.back();
},1000);
}).catch((err)=>{
uni.hideLoading();
that.$dialog.error(err.msg || "修改失败");
})
}
}
}
</script>
<style scoped lang="less">
.user-payPassword{
min-height: 100vh;
padding: 60rpx 32rpx;
background: #F9F9F9;
font-size: 28rpx;
line-height: 40rpx;
button {
border-radius: 8rpx;
color: #FFFFFF;
font-size: 28rpx;
font-weight: bold;
}
button:after {
display: none;
}
.input-box {
height: 100rpx;
padding: 0 36rpx;
border-radius: 8rpx;
background: #FFFFFF;
color: #999999;
.icon-phone {
width: 48rpx;
height: 48rpx;
vertical-align: middle;
margin-right: 20rpx;
}
input {
font-size: 28rpx;
line-height: 40rpx;
}
}
.btn-code {
width: 200rpx;
height: 72rpx;
margin: 0 0 0 32rpx;
background: #28AAF4;
line-height: 72rpx;
}
.btn-save {
height: 80rpx;
margin-top: 80rpx;
background: #FD574B;
line-height: 80rpx;
}
}
/*page{*/
/* background: #fff;*/
/*}*/
/*.container{*/
/* background: #fff;*/
/*}*/
/*.confirmBnt {*/
/* font-size: 32rpx;*/
/* width: 580rpx;*/
/* height: 90rpx;*/
/* border-radius: 45rpx;*/
/* color: #fff;*/
/* margin: 92rpx auto 0 auto;*/
/* text-align: center;*/
/* line-height: 90rpx;*/
/*}*/
/*.content{*/
/* margin: 114rpx;*/
/*}*/
/*.phone-input{*/
/* color: #8B8F99;*/
/* font-size: 36rpx;*/
/* height:88rpx;*/
/* background:rgba(247,247,249,1);*/
/* opacity:1;*/
/* border-radius:12rpx;*/
/* margin-bottom: 32rpx;*/
/*}*/
/*.codeIput{*/
/* font-size: 28rpx;*/
/* height:88rpx;*/
/* border:1px solid rgba(218,220,224,1);*/
/* opacity:1;*/
/* border-radius:12rpx;*/
/* flex-grow: 1;*/
/*}*/
/*.code{*/
/* !* width:224rpx; *!*/
/* height:88rpx;*/
/* background:rgba(121,129,253,1);*/
/* opacity:1;*/
/* color: #fff;*/
/* font-size: 28rpx;*/
/* border-radius:12rpxpx;*/
/* flex-shrink: 0;*/
/* margin-left: 16rpx;*/
/* text-align: center;*/
/* padding-left: 24rpx;*/
/* padding-right: 24rpx;*/
/*}*/
/*.payPwdInput{*/
/* font-size: 28rpx;*/
/* height:88rpx;*/
/* border:1px solid rgba(218,220,224,1);*/
/* opacity:1;*/
/* border-radius:12rpx;*/
/* margin-top: 32rpx;*/
/*}*/
</style>
+366
View File
@@ -0,0 +1,366 @@
<template>
<view class="user-personalData">
<view class="top-box" :style="{'backgroundImage':`url(${webUrl}/20220925102928757633.png)`}">
<view class="flex ai-center">
<image-cropper :src="tempFilePath" @confirm="confirm" @cancel="cancel"></image-cropper>
<view style="position: relative" @tap="chooseImage">
<image class="cover" :src="avatar"/>
<image class="btn-change" :src="webUrl+'/20220925111121285647.png'"/>
</view>
<view>
<view class="name">{{ userInfo.nickname }}</view>
<view class="phone">绑定手机号{{ userInfo.phone || '未绑定' }}</view>
</view>
</view>
</view>
<view class="cell-box">
<view class="cell flex jc-between ai-center">
<view class="flex ai-center">
<view class="title">昵称</view>
<view class="value">
<input type="text" v-model="userInfo.nickname"/>
</view>
</view>
<view class="icon">
<image :src="webUrl+'/20220903145836941399.png'"/>
</view>
</view>
<view class="cell flex jc-between ai-center">
<view class="flex ai-center">
<view class="title">ID号</view>
<view class="value">{{ userInfo.uid }}</view>
</view>
<view class="icon">
<image :src="webUrl+'/20220925102920713727.png'" style="width: 40rpx;height: 40rpx"/>
</view>
</view>
<view class="cell flex jc-between ai-center" @click="bindPhone">
<view class="flex ai-center">
<view class="title">手机号</view>
<view class="value">{{ userInfo.phone || '未绑定' }}</view>
</view>
<view class="icon">
<image :src="webUrl+'/20220903145836941399.png'"/>
</view>
</view>
<view class="cell flex jc-between ai-center" @click="payPwdClick">
<view class="flex ai-center">
<view class="title">支付密码</view>
<view class="value"></view>
</view>
<view class="icon">
<image :src="webUrl+'/20220903145836941399.png'"/>
</view>
</view>
</view>
<button class="btn-save" @click="submit">保存修改</button>
<!-- <view class="list">-->
<!-- <view class="item acea-row row-between-wrapper">-->
<!-- <view>昵称</view>-->
<!-- <view class="input">-->
<!-- <input type="text" v-model="userInfo.nickname"/>-->
<!-- </view>-->
<!-- </view>-->
<!-- <view class="item acea-row row-between-wrapper">-->
<!-- <view>ID号</view>-->
<!-- <view class="input acea-row row-between-wrapper">-->
<!-- <input type="text" :value="userInfo.uid" disabled class="id"/>-->
<!-- <text class="iconfont icon-suozi"></text>-->
<!-- </view>-->
<!-- </view>-->
<!-- <view class="item acea-row row-between-wrapper" @click="bindPhone">-->
<!-- <view>手机号</view>-->
<!-- <view class="input acea-row row-between-wrapper">-->
<!-- <input type="text" disabled v-if="userInfo.phone" v-model="userInfo.phone" class="id"/>-->
<!-- <input type="text" v-else value="未绑定" disabled class="id"/>-->
<!-- <text class="iconfont icon-jiantou"></text>-->
<!-- </view>-->
<!-- </view>-->
<!-- <view class="item acea-row row-between-wrapper" @click="payPwdClick">-->
<!-- <view>支付密码</view>-->
<!-- <view class="input acea-row row-right">-->
<!-- <text class="iconfont icon-jiantou"></text>-->
<!-- </view>-->
<!-- </view>-->
<!-- </view>-->
<!-- <view class="modifyBnt bg-color-lightred" @click="submit">保存修改</view>-->
<view
class="logOut cart-color acea-row row-center-wrapper"
@click="logout"
v-if="$deviceType=='app'"
>退出登录
</view>
</view>
</template>
<script>
import {mapGetters} from "vuex";
import {trim, isWeixin, chooseImage, uploadImage} from "@/utils";
import {
postUserEdit,
getLogout,
switchH5Login
} from "@/api/user";
import cookie from "@/utils/store/cookie";
import store from "@//store";
import dayjs from "dayjs";
import ImageCropper from "@/pkg_user/components/invinbg-image-cropper/invinbg-image-cropper.vue";
export default {
name: "PersonalData",
components: {
ImageCropper
},
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
avatar: "",
isWeixin: false,
currentAccounts: 0,
switchUserInfo: [],
userIndex: 0,
tempFilePath: '',
cropFilePath: '',
};
},
computed: mapGetters(["userInfo"]),
mounted: function () {
this.avatar = this.userInfo.avatar;
this.isWeixin = isWeixin();
},
onShow: function () {
//获取用户信息
this.$store.dispatch("getUser", true);
},
methods: {
confirm(e) {
this.tempFilePath = '';
this.cropFilePath = e.detail.tempFilePath;
uploadImage(this.cropFilePath, img => {
console.log(img)
this.avatar = img;
this.cropFilePath = '';
})
},
cancel() {
console.log('canceled')
},
bindPhone: function () {
this.$yrouter.push("/pkg_user/views/bindPhone");
},
payPwdClick: function () {
//先判断用户是否绑定过手机
if (this.userInfo.phone) {
//再判断用户是否设置过支付密码
if (this.userInfo.hasPwdPay) {
//修改支付密码
this.$yrouter.push("/pkg_user/views/payPassword");
} else {
//设置支付密码
this.$yrouter.push("/pkg_user/views/payPassword?isSetMode=true");
}
} else {
//跳转到绑定手机
this.$yrouter.push("/pkg_user/views/bindPhone");
}
},
switchAccounts: function (index) {
let that = this;
this.userIndex = index;
let userInfo = this.switchUserInfo[this.userIndex];
if (this.switchUserInfo.length <= 1) return true;
if (userInfo === undefined) {
uni.showToast({
title: "切换的账号不存在",
icon: "none",
duration: 2000
});
return;
}
if (userInfo.user_type === "h5") {
switchH5Login()
.then(({data}) => {
uni.hideLoading();
const expires_time = dayjs(data.expires_time);
store.commit("login", data.token, expires_time);
that.$emit("changeswitch", false);
location.reload();
})
.catch(err => {
uni.hideLoading();
uni.showToast({
title:
err.msg || err.response.data.msg || err.response.data.message,
icon: "none",
duration: 2000
});
});
} else {
cookie.set("loginType", "wechat", 60);
uni.hideLoading();
this.$store.commit("logout");
this.$emit("changeswitch", false);
}
},
chooseImage() {
uni.chooseImage({
count: 1, //默认9
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album'], //从相册选择
success: (res) => {
this.tempFilePath = res.tempFilePaths.shift()
}
});
},
submit: function () {
let userInfo = this.userInfo;
let that = this;
postUserEdit({
nickname: trim(this.userInfo.nickname),
avatar: this.avatar
}).then(
res => {
that.$store.dispatch("userInfo", true);
uni.showToast({
title: res.msg,
icon: "none",
duration: 2000
});
setTimeout(function () {
that.$yrouter.back();
}, 2000)
},
err => {
uni.showToast({
title:
err.msg || err.response.data.msg || err.response.data.message,
icon: "none",
duration: 2000
});
}
);
},
logout: function () {
uni.showModal({
title: "提示",
content: "确认退出登录?",
success: function (res) {
if (res.confirm) {
getLogout()
.then(res => {
this.$store.commit("logout");
this.$yrouter.replace({
path: "/pages/user/Login/index",
query: {}
});
})
.catch(err => {
});
} else if (res.cancel) {
// console.log("用户点击取消");
}
}
});
}
}
};
</script>
<style scoped lang="less">
.user-personalData {
min-height: 100vh;
background: #F9F9F9;
view {
box-sizing: border-box;
}
.top-box {
width: 100vw;
height: 332.5rpx;
padding: 94rpx 0 0 104rpx;
background-size: 750rpx 332.5rpx;
color: #FFFFFF;
.cover {
width: 136rpx;
height: 136rpx;
border-radius: 50%;
margin-right: 42rpx;
}
.btn-change {
position: absolute;
top: 100rpx;
left: 13rpx;
width: 110rpx;
height: 44rpx;
}
.name {
font-size: 32rpx;
line-height: 48rpx;
font-weight: bold;
}
.phone {
margin-top: 22rpx;
font-size: 28rpx;
line-height: 40rpx;
}
}
.cell-box {
margin: 54rpx 32rpx;
padding: 0 25rpx;
border-radius: 12rpx;
background: #FFFFFF;
.cell:last-child {
border-bottom: none;
}
.cell {
padding: 32rpx 0 20rpx;
border-bottom: 1rpx solid #E8E9E9;
font-size: 28rpx;
line-height: 40rpx;
.title {
width: 112rpx;
margin-right: 40rpx;
color: #333333;
font-weight: bold;
}
.value {
color: #999999;
}
.icon {
image {
width: 26rpx;
height: 26rpx;
}
}
}
}
.btn-save {
margin: 0 32rpx;
background: #FE5261;
color: #FFFFFF;
font-size: 32rpx;
}
}
</style>
+467
View File
@@ -0,0 +1,467 @@
<template>
<view class="user-withdrawal">
<view class="bold">提现到</view>
<view class="bank-box acea-row row-between row-middle" @click="bankSelect">
<view class="acea-row row-middle" v-if="selectedBank">
<image class="icon-card" :src="webUrl+'/20220924105211695252.png'"/>
<view class="acea-row row-column">
<span class="bold">{{ selectedBank.bankName }}</span>
<span class="bold">{{ selectedBank.cardNo }}/{{ selectedBank.name }}</span>
</view>
</view>
<view class="flex ai-center bold" v-else>
<image class="icon-card" :src="webUrl+'/20220924105211695252.png'"/>
请选择银行卡
</view>
</view>
<view class="amount-box">
<view class="title bold">提现金额</view>
<view class="item acea-row row-middle">
<view class="dollar bold"></view>
<view class="input">
<input
:placeholder="'最低提现金额' + minPrice"
placeholder-style="font-size:28rpx"
v-model="amout"
:maxlength='maxlength'
@blur="balanceInputBlur"
@input="balanceInputChange"
type="digit"
autofocus
/>
</view>
<button class="withdraw-all-btn" @click="withdrawalAll">全部提现</button>
</view>
<view class="vline"></view>
<view class="acea-row row-between row-middle" style="margin-top: 22rpx;">
<view class="bold">可提现金额{{ commissionCount }}</view>
<view class="bold">手续费
<text class="red">{{ totalFee }}</text>
</view>
</view>
</view>
<view class="acea-row row-column row-middle">
<button class="confirm-btn" @click="confrimBtnClick">提现</button>
<view class="record-btn" style="" @click="goCashRecord">提现记录</view>
</view>
<uni-popup ref="popup" type="bottom">
<blPaymentPasswordInput hideTopBorder ref="secrity" @input="onInput" @confirm="onConfirm">
<block slot='header' style='width: 100%;'>
<view style="font-size: 36rpx;color: #8B8F99;margin-top: 10rpx;">请输入支付密码</view>
</block>
<block slot='middle'>
<view class="" style="background-color: #fff;"></view>
</block>
</blPaymentPasswordInput>
</uni-popup>
</view>
</template>
<script>
import {mapGetters} from "vuex";
import {getUserInfo, getMenuUser, bindingPhone, getBank, bankCardList, postCashInfo} from "@/api/user";
import NP from "number-precision";
import uniPopup from '@/components/uni-popup/uni-popup.vue';
import blPaymentPasswordInput from '@/components/blPaymentPasswordInput.vue';
export default {
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
payPwd: "",
selectedBank: null,
amout: "",
minPrice: 0,
banks: [],
maxlength: -1,
commissionCount: 0, //可提现金额
fee: 0, //手续费
gdAmount: 0, //固定手续费
gdLimit: 0, //最低固定费率额度
feeRate: 0, //手续费费率
remark: "" //提现说明
}
},
components: {
uniPopup,
blPaymentPasswordInput
},
computed: {
...mapGetters(["userInfo"]),
//计算手续费
totalFee() {
/*
if( 提现金额 < gdLimit ) sxf = gdAmount
if( 提现金额 >= gdLimit ) sxf = 提现金额*fl
*/
return this.calculateSxf(this.amout);
}
},
onLoad: function (e) {
this.getBank();
this.getDefaultBankCards();
},
onShow() {
if (this.$store.getters.token) {
this.$store.dispatch("getUser", true);
}
//获取选择的银行卡
let card = uni.getStorageSync('selectedBankCard');
if (card) {
console.log("card:" + JSON.parse(card));
this.selectedBank = JSON.parse(card);
//清空存储
uni.removeStorageSync('selectedBankCard');
}
},
methods: {
calculateSxf: function (amout) {
var sxf = 0;
if (amout == 0) {
return sxf;
}
if (parseFloat(this.gdLimit) > 0 && amout < parseFloat(this.gdLimit)) {
sxf = parseFloat(this.gdAmount);
console.log("fixed");
} else {
if (parseFloat(this.feeRate) > 0) {
sxf = NP.times(amout, NP.divide(parseFloat(this.feeRate), 100));
}
console.log("variable");
}
console.log("sxf:" + sxf);
return this.$force2Decimal(sxf);
},
goCashRecord() {
this.$yrouter.push("/pages/user/promotion/CashRecord/index");
},
clearNoNum: function (value) {
var str = value;
var len1 = str.substr(0, 1);
var len2 = str.substr(1, 1);
//如果第一位是0,第二位不是点,就用数字把点替换掉
if (str.length > 1 && len1 == 0 && len2 != ".") {
str = str.substr(1, 1);
}
//第一位不能是.
if (len1 == ".") {
str = "";
}
//限制只能输入一个小数点
if (str.indexOf(".") != -1) {
var str_ = str.substr(str.indexOf(".") + 1);
if (str_.indexOf(".") != -1) {
str = str.substr(0, str.indexOf(".") + str_.indexOf(".") + 1);
}
}
value = str
var index = String(value).indexOf('.');
value = value.replace(/[^\d.]/g, ""); //清除“数字”和“.”以外的字符
// value = value.replace(/\.{2,}/g,"."); //只保留第一个. 清除多余的
// value = value.replace(".","$#$").replace(/\./g,"").replace("$#$",".");
value = value.replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3');//只能输入两个小数
//修复可输入3位小数的bug
if (index != -1) {
var arrStr = [];
arrStr = String(value).split('.');
//当小数位数达两位的时候,设备最大输入长度为当前长度
if (arrStr[1] && arrStr[1].length == 2) {
this.maxlength = String(value).length
} else {
this.maxlength = -1//恢复
}
} else {
this.maxlength = -1//恢复
}
return value
},
balanceInputChange: function (e) {
var value = e.target.value;
this.amout = value
var v = this.clearNoNum(value)
setTimeout(() => {
this.amout = v
}, 0)
},
balanceInputBlur: function (e) {
var value = e.target.value;
if (String(value).length == 0) {
this.amout = 0
}
},
openPwdInput() {
this.$refs.popup.open();
},
//支付密码输入监听
onInput(e) {
},
// 支付密码输入确认
onConfirm(e) {
let password = e.value;
this.payPwd = password;
if (this.payPwd.length == 0 || this.payPwd.length < 6) {
uni.showToast({
title: '请输入正确的支付密码'
});
return;
}
//网络请求
//网络请求
/*
extractType 填 'bank'
realName 持卡人姓名
bankAddress 支行地址
bankCode 卡号
money 提现金额
*/
var that = this;
this.$dialog.loading.open();
postCashInfo({
extractType: 'bank',
realName: this.selectedBank.name,
bankCode: this.selectedBank.cardNo,
bankAddress: this.selectedBank.bankSub,
money: this.amout,
passwordPay: this.payPwd
}).then(
res => {
that.$dialog.loading.close();
that.$dialog.message(res.msg);
that.$refs.popup.close();
that.$refs.secrity.clear();
setTimeout(function () {
that.$yrouter.replace("/pages/user/promotion/CashAudit/index");
}, 1000);
},
error => {
that.$dialog.loading.close();
that.$dialog.message(error.msg);
that.$refs.popup.close();
that.$refs.secrity.clear();
}
);
},
getDefaultBankCards() {
let that = this;
bankCardList({
limit: 1,
page: 1
})
.then(response => {
// 请求的列表数据
let arr = response.data.records;
//取第一张银行卡做为默认
if (arr != undefined && arr.length > 0) {
that.selectedBank = arr[0];
}
})
.catch(err => {
});
},
confrimBtnClick: function () {
let that = this;
if (!this.selectedBank) {
this.$dialog.error("请选择提现银行卡");
return;
}
if (this.amout.length == 0) {
this.$dialog.error("请输入提现金额");
return;
} else if (
parseFloat(this.amout) > parseFloat(this.commissionCount) ||
parseFloat(this.commissionCount) == 0
) {
this.$dialog.error("可提现金额不足");
return;
} else if (parseFloat(this.amout) < parseFloat(this.minPrice)) {
this.$dialog.error("金额不能小于最低提现金额" + this.minPrice);
return;
}
//先判断用户是否绑定过手机
if (this.userInfo.phone) {
//再判断用户是否设置过支付密码
if (this.userInfo.hasPwdPay) {
//显示支付密码输入框
this.openPwdInput();
} else {
//设置支付密码
this.$yrouter.push({path: "/pkg_user/views/payPassword?isSetMode=true"});
}
} else {
//跳转到绑定手机
this.$yrouter.push({path: "/pkg_user/views/bindPhone"});
}
},
onBackClick: function () {
console.log("back");
this.$yrouter.back();
},
//选择银行卡
bankSelect: function () {
console.log("bankSelect");
this.$yrouter.push({
path: "/pkg_user/views/bankCardList",
query: {
isSelected: true
}
});
},
withdrawalAll: function () {
if (this.commissionCount) {
this.amout = this.commissionCount + "";
}
},
getBank: function () {
let that = this;
getBank().then(
res => {
that.banks = res.data.extractBank;
that.minPrice = res.data.minPrice;
that.commissionCount = this.$force2Decimal(res.data.commissionCount);
that.gdAmount = parseFloat(res.data.gdAmount);
that.gdLimit = parseFloat(res.data.gdLimit);
that.feeRate = parseFloat(res.data.fl);
//可提现金额去除手续费
if (that.commissionCount > 0) {
var sxf = that.calculateSxf(that.commissionCount);
console.log('手续费:' + sxf);
that.commissionCount = this.$force2Decimal(Math.max(0, (that.commissionCount - sxf)));
;
}
},
function (err) {
}
);
}
}
}
</script>
<style lang="less">
.user-withdrawal {
min-height: 100vh;
box-sizing: border-box;
padding: 42rpx 32rpx;
background: #F9F9F9;
color: #333333;
font-size: 24rpx;
line-height: 34rpx;
.icon-card {
width: 36rpx;
height: 36rpx;
margin-right: 8rpx;
}
}
.bank-box {
margin: 10rpx 0 40rpx;
padding: 24rpx;
border-radius: 12rpx;
background: #FFFFFF;
box-sizing: border-box;
}
.amount-box {
padding: 32rpx 24rpx 54rpx;
border-radius: 12rpx;
background: rgba(255, 255, 255, 1);
box-sizing: border-box;
.title {
margin-bottom: 30rpx;
font-size: 28rpx;
line-height: 40rpx;
}
}
.amount-box .item {
margin-bottom: 12rpx;
}
.red {
color: #E91717;
}
.dollar {
font-size: 32rpx;
line-height: 48rpx;
flex-shrink: 0;
}
.withdraw-all-btn {
width: 148rpx;
height: 56rpx;
padding: 0;
border-radius: 4rpx;
background: #28AAF4;
color: #FFFFFF;
font-size: 24rpx;
font-weight: bold;
flex-shrink: 0;
}
.vline {
height: 0;
border: 1rpx solid #E8E9E9;
}
.input {
padding: 0 20rpx;
font-size: 28rpx;
line-height: 40rpx;
flex-grow: 1;
}
.confirm-btn {
width: 686rpx;
height: 80rpx;
margin: 54rpx 0 40rpx;
border-radius: 8rpx;
background: #FD574B;
color: #FFFFFF;
font-size: 28rpx;
font-weight: bold;
line-height: 80rpx;
}
.record-btn {
color: #776A89;
font-size: 28rpx;
line-height: 40rpx;
text-decoration: underline;
}
</style>