源文件

This commit is contained in:
gyq
2024-05-23 14:39:33 +08:00
commit a1128dd791
2997 changed files with 500069 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
<template>
<view></view>
<view class="loading"> </view>
</template>
<script setup>
import { ref, reactive } from "vue"
import { onLaunch } from "@dcloudio/uni-app"
import { checkCurrVersion,getExtStoreId } from "@/util/versionManage.js"
import storageManage from "@/util/storageManage.js"
onLaunch(() => {
getExtStoreId()
// 检查版本
checkCurrVersion()
const userInfo = storageManage.userInfo()
let isApp = false
// #ifdef APP-PLUS
isApp = true
// #endif
if (userInfo && userInfo.isEpUuser) {
uni.reLaunch({
url: "./pages/devWorkbench/devWorkbench",
success: () => {
if (isApp) {
plus.navigator.closeSplashscreen()
}
},
})
} else {
if (isApp) {
plus.navigator.closeSplashscreen()
}
// uni.reLaunch({ url: './pages/workbench/workbench'});
}
})
</script>
<style lang="scss">
/*每个页面公共css */
@import "./static/css/global.scss";
@import "./static/css/common.scss";
@import "./static/css/global.css";
</style>

View File

@@ -0,0 +1,27 @@
{
"version" : "1",
"prompt" : "template",
"title" : "服务协议和隐私政策",
"message" : "  请你务必审慎阅读、充分理解“服务协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。<br/>  你可阅读<a href=\"static/service.html\">《服务协议》</a>和<a href=\"static/privacy.html\">《隐私政策》</a>了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。",
"buttonAccept" : "同意并接受",
"buttonRefuse" : "暂不同意",
"second" : {
"title" : "确认提示",
"message" : "  进入应用前,你需先同意<a href=\"static/service.html\">《服务协议》</a>和<a href=\"static/privacy.html\">《隐私政策》</a>,否则将退出应用。",
"buttonAccept" : "同意并继续",
"buttonRefuse" : "退出应用"
},
"styles" : {
"backgroundColor" : "#FFFFFF",
"borderRadius" : "5px",
"title" : {
"color" : "#262626"
},
"buttonAccept" : {
"color" : "#3981FF"
},
"buttonRefuse" : {
"color" : "#A6A6A6"
}
}
}

View File

@@ -0,0 +1,137 @@
<template>
<uni-popup ref="refPopup" type="center" @change='change' :safe-area="false">
<view class="popup-wrapper">
<view class="title">用户服务协议与隐私政策</view>
<view class="content">
如需登录请先认真阅读并同意{{ $appName }}
<text class="info" @tap="toService">用户服务协议</text>
<text class="info" @tap="toPrivacy">隐私政策</text>
</view>
<view class="but-wrapper">
<view class="but-item" hover-class="touch-button" @tap="disagreeClose">拒绝</view>
<button class="but-item agree" id="agree-btn" open-type="agreePrivacyAuthorization"
@agreeprivacyauthorization="handAgree">同意</button>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { reactive, ref, onMounted } from 'vue'
const props = defineProps({
service: { type: String },
privacy: { type: String }
})
const refPopup = ref(null)
const emits = defineEmits(['agree'])
const open = () => {
getPrivacy()
refPopup.value.open()
}
const vdata = reactive({})
const close = () => refPopup.value.close()
const toPrivacy = () => {
// #ifdef APP-PLUS
if (props.privacy) return uni.navigateTo(props.privacy)
// #endif
// 打开小程序隐私政策
// #ifdef MP-WEIXIN
wx.openPrivacyContract(
{
fail: () => {
uni.showToast({
title: '打开失败请稍后重试', // 打开失败
icon: 'none'
})
},
}
)
// #endif
}
function disagreeClose () {
close()
}
// 获取微信你用户是否同意过隐私政策
const getPrivacy = () => {
wx.getPrivacySetting({
success: (r) => {
Object.assign(vdata, r)
if (vdata.needAuthorization) {
wx.onNeedPrivacyAuthorization(res => {
vdata.resolve = res
})
}
}
})
}
const handAgree = () => {
if (vdata.needAuthorization) {
vdata.resolve({ buttonId: 'agree-btn', event: 'agree' })
}
emits('agree')
close()
}
const toService = () => {
uni.navigateTo({ url: props.service })
}
defineExpose({ open, close })
</script>
<style lang="scss" scoped>
.popup-wrapper {
width: 600rpx;
border-radius: 20rpx;
background: #FFF;
overflow: hidden;
.title {
display: flex;
justify-content: center;
align-items: center;
padding: 30rpx 0 50rpx;
text-align: center;
color: rgba(0, 0, 0, 0.50);
font-size: 28rpx;
}
.content {
padding-bottom: 70rpx;
margin: 0 auto;
width: 500rpx;
color: #000;
font-size: 28rpx;
line-height: 1.5;
.info {
color: $primaryColor;
}
}
.but-wrapper {
display: flex;
.but-item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
height: 110rpx;
color: rgba(0, 0, 0, 0.70);
font-size: 32rpx;
font-weight: 400;
border-radius: 0;
}
.agree {
background: $primaryColor;
color: #fff;
padding: 0;
}
}
}
</style>

View File

@@ -0,0 +1,138 @@
<template>
<uni-popup ref="refPopup" type="center" @change='change' :safe-area="false">
<view class="popup-wrapper">
<view class="title">用户服务协议与隐私政策</view>
<view class="content">
在你使用 {{ $appName }} 小程序之前请仔细阅读
<text class="info" @tap="toService">用户服务协议</text>
<text class="info" @tap="toPrivacy">隐私政策</text>
<text>如你同意用户服务协议隐私政策请点击同意</text>
<text>开始使用 {{ $appName }}小程序</text>
</view>
<view class="but-wrapper">
<view class="but-item" hover-class="touch-button" @tap="disagreeClose">拒绝</view>
<button class="but-item agree" id="agree-btn" open-type="agreePrivacyAuthorization"
@agreeprivacyauthorization="handAgree">同意</button>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { reactive, ref, onMounted } from 'vue'
const props = defineProps({
service: { type: String },
privacy: { type: String }
})
const openFlag=ref(false)
const refPopup = ref(null)
const emits = defineEmits(['agree'])
const open = () => {
getPrivacy()
refPopup.value.open()
}
const vdata = reactive({})
const close = () => refPopup.value.close()
const toPrivacy = () => {
// #ifdef APP-PLUS
if (props.privacy) return uni.navigateTo(props.privacy)
// #endif
// 打开小程序隐私政策
// #ifdef MP-WEIXIN
wx.openPrivacyContract(
{
fail: () => {
uni.showToast({
title: '打开失败请稍后重试', // 打开失败
icon: 'none'
})
},
}
)
// #endif
}
function disagreeClose () {
close()
}
// 获取微信你用户是否同意过隐私政策
const getPrivacy = () => {
wx.getPrivacySetting({
success: (r) => {
Object.assign(vdata, r)
if (vdata.needAuthorization) {
wx.onNeedPrivacyAuthorization(res => {
vdata.resolve = res
})
}
}
})
}
const handAgree = () => {
if (vdata.needAuthorization) {
vdata.resolve({ buttonId: 'agree-btn', event: 'agree' })
}
emits('agree')
close()
}
const toService = () => {
uni.navigateTo({ url: props.service })
}
defineExpose({ open, close })
</script>
<style lang="scss" scoped>
.popup-wrapper {
width: 600rpx;
border-radius: 20rpx;
background: #FFF;
overflow: hidden;
.title {
display: flex;
justify-content: center;
align-items: center;
padding: 30rpx 0 50rpx;
text-align: center;
color: rgba(0, 0, 0, 0.50);
font-size: 28rpx;
}
.content {
padding-bottom: 70rpx;
margin: 0 auto;
width: 500rpx;
color: #000;
font-size: 28rpx;
line-height: 1.5;
.info {
color: $primaryColor;
}
}
.but-wrapper {
display: flex;
.but-item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
height: 110rpx;
color: rgba(0, 0, 0, 0.70);
font-size: 32rpx;
font-weight: 400;
border-radius: 0;
}
.agree {
background: $primaryColor;
color: #fff;
padding: 0;
}
}
}
</style>

View File

@@ -0,0 +1,107 @@
<template>
<uni-popup ref="popup" type="bottom" mask-background-color="rgba(0,0,0,.5)" @change="change" :safe-area="false">
<view class="tips-wrapper">
<view class="tips-text flex-center" v-if="title"> {{ title }} </view>
<block v-for="v in list" :key="v[value]">
<view class="single-text flex-center" hover-class="u-cell-hover" hover-stay-time="150" :style="{ color: v[value] == selected ? activeColor : v.color }" @tap="confirm(v)">{{
v[label]
}}</view>
</block>
<view class="line"></view>
<view class="tips-text tips-cancel flex-center" hover-class="u-cell-hover" hover-stay-time="150" @tap="popup.close()"> 取消 </view>
</view>
</uni-popup>
<JeepayPopupConfirm ref="jeepayPopupConfirm" />
</template>
<script setup>
import { reactive, ref, inject } from 'vue'
const emits = defineEmits(['confirm'])
const props = defineProps({
title: { type: String }, //标题 传入展示 不传则隐藏
activeColor: { type: String, default: '#2980FD' }, //选中后文字颜色
textColor: { type: String }, //列表文字颜色
textSize: { type: String }, //列表文字大小
titleColor: { type: String }, //标题文字颜色
titleSize: { type: String }, //标题文字大小
list: { type: Array }, //渲染单选列表集合
label: { type: String, default: 'label' }, //展示文字的键名 可传值复写
value: { type: String, default: 'value' }, //展示文字的key 可传值复写
})
// 引入依赖 解决滚动穿透问题 配合JeepayWrapper 使用
const wrapperHidden = inject('wrapperHidden')
const selected = ref(undefined)
const popup = ref(null)
const jeepayPopupConfirm = ref()
// 打开弹窗 val 选中数据的key
const open = (val) => {
selected.value = val
popup.value.open()
}
const confirm = (listItem) => {
popup.value.close()
// 包含回调函数
if (listItem.fun) {
// 包含确认弹层。
if (listItem.confirmText) {
jeepayPopupConfirm.value
.open(listItem.confirmText)
.then(() => {
listItem.fun()
})
.catch(() => {
popup.value.open()
})
} else {
listItem.fun()
}
} else {
1
emits('confirm', listItem)
}
}
const change = (e) => {
if (wrapperHidden) {
wrapperHidden(e.show)
}
}
const close = () => popup.value.close()
defineExpose({ open, close })
</script>
<style lang="scss" scoped>
.tips-wrapper {
// overflow: hidden;
border-radius: 32rpx 32rpx 0 0;
padding-top: 20rpx;
padding-bottom: 60rpx;
background-color: #fff;
.tips-text {
text-align: center;
height: 90rpx;
font-size: 30rpx;
border-bottom: 1rpx solid rgba(0, 0, 0, 0.07);
}
.single-text {
height: 120rpx;
color: #4d4d4d;
}
.line {
height: 20rpx;
background-color: rgba(0, 0, 0, 0.07);
}
.tips-cancel {
height: 110rpx;
color: #808080;
font-size: 33rpx;
border: none;
}
.u-cell-hover {
background-color: #f8f9fa;
}
}
</style>

View File

@@ -0,0 +1,123 @@
<template>
<scroll-view
class="table-list"
scroll-y
:style="{ '--height': height }"
@scrolltolower="addNext"
@refresherrefresh="openRefresh"
:refresher-enabled="refresh"
:refresher-triggered="vdata.isRefresh"
>
<template v-for="(record, i) in vdata.allData" :key="i">
<slot name="tableBody" :record="record" :index="i" />
</template>
<view class="list-null" v-if="!vdata.apiResData.hasNext && showListNull">暂无更多数据</view>
</scroll-view>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
// 定义传入属性
const props = defineProps({
reqTableDataFunc: { type: Function, default: () => {} },
searchData: { type: Object, default: () => {} }, // 搜索条件参数
pageSize: { type: Number, default: 10 }, // 默认每页条数
initData: { type: Boolean, default: true }, // 初始化列表数据, 默认true
showListNull: { type: Boolean, default: true }, //是否显示缺省 默认显示
height: { type: String, default: '88rpx' }, // 高度 这里的高度 为 外部使用高度 scroll-view 高度 为 100vh - 传入高度
refresh: { type: Boolean, default: false }, //是否开启下拉刷新 默认不开启
})
const vdata = reactive({
allData: [], // app与web不同 app是每次查询到数据会拼接到后面
// 接口返回的数据
apiResData: { total: 0, records: [] },
// 分页参数
iPage: { pageNumber: 1, pageSize: props.pageSize },
// 下拉刷新开启状态
isRefresh: false,
})
onMounted(() => {
//初始化表数据
props.initData ? refTable(true) : undefined
})
// 查询表格数据
function refTable(isToFirst = false) {
if (isToFirst) {
// 重新搜索, 第一页。
vdata.iPage.pageNumber = 1
vdata.allData = [] //清空数据
}
// 更新检索数据
return props.reqTableDataFunc(Object.assign({}, vdata.iPage, props.searchData)).then(({ bizData }) => {
Object.assign(vdata.apiResData, bizData) // 列表数据更新
if (bizData.records) {
bizData.records.forEach((r) => vdata.allData.push(r))
}
})
}
/** 追加下一页数据 **/
function addNext() {
// console.log('加载下一页')
// 包含下一页
if (vdata.apiResData.hasNext) {
vdata.iPage.pageNumber++
refTable(false)
}
}
// 下拉刷新
const openRefresh = () => {
vdata.isRefresh = true
refTable(true).then(() => {
setTimeout(() => {
vdata.isRefresh = false
}, 300)
})
}
// 将表格事件暴露出去 https://www.jianshu.com/p/39d14c25c987
defineExpose({ refTable, addNext })
</script>
<style lang="scss" scoped>
.table-list {
height: calc(75vh - var(--height));
}
.list-null {
position: relative;
line-height: 110rpx;
text-align: center;
font-size: 26rpx;
color: #a6a6a6;
&::after,
&::before {
content: '';
display: block;
position: absolute;
top: 50%;
width: 30%;
height: 2rpx;
background-color: #ededed;
transform: translateY(-50%);
}
&::after {
left: 40rpx;
}
&::before {
right: 40rpx;
}
}
</style>

View File

@@ -0,0 +1,55 @@
<template>
<!-- 单张广告 -->
<view class="ad-wrapper" :style="{margin:mg}" v-if="list.length == 1">
<image :src="list[0].imgUrl" mode="aspectFill" @tap='toH5(list[0].linkUrl)' />
</view>
<!-- 多张广告 -->
<view class="ads-wrapper" :style="{margin:mgs}" v-else>
<block v-for="(v, i) in list" :key="i">
<view>
<image :src="v.imgUrl" mode="aspectFill" @tap='toH5(v.linkUrl)' />
</view>
</block>
</view>
</template>
<script setup>
const props = defineProps({
list: { type: Array, default: [] },
mg:{type:String, default:'40rpx'},
mgs:{type:String, default:'30rpx'}
})
const toH5 = (url) => {
if (!url) return
uni.navigateTo({
url: '/pages/adH5/adH5?url=' + url
})
}
</script>
<style lang="scss" scoped>
.ad-wrapper {
margin: 40rpx;
max-height: 680rpx;
border-radius: 30rpx;
overflow: hidden;
}
.ads-wrapper {
display: flex;
justify-content: space-between;
margin: 30rpx;
view {
width: calc(50% - 20rpx);
max-height: 400rpx;
border-radius: 30rpx;
overflow: hidden;
}
}
image {
width: 100%;
}
</style>

View File

@@ -0,0 +1,237 @@
<template>
<view class="start" v-if="show">
<view class="skip" :style="skipPositionStyle" @click="onSkip">跳过 {{ time2 }}</view>
<swiper class="swiper" :interval="interval" :autoplay="list.length > 1" @change="onChangeSwiper">
<swiper-item v-for="(item, index) in list" :key="index">
<view class="swiper-item" :style="bgStyle">
<image class="image" :src="item.imgUrl" mode="aspectFill" @tap="toAdH5(item)"></image>
<view class="after" :style="afterStyle"></view>
</view>
</swiper-item>
<!-- autoplay -->
</swiper>
<view class="swiper-dot" v-if="list.length > 1">
<view class="view" :style="index === current ? currentStyle : ''" :class="{ active: index === current }"
v-for="(item, index) in list" :key="index" />
</view>
</view>
</template>
<script>
export default {
props: {
list: {
type: Array,
default() {
return []
},
required: true,
},
hasTabbar: {
type: Boolean,
default: false,
},
hasNavbar: {
type: Boolean,
default: false,
},
interval: {
type: Number,
default: 3000,
},
time: {
type: Number,
default: 3,
},
url: {
type: String,
default: '',
},
bgColor: {
type: String,
default: '#fff',
},
afterColor: {
type: String,
default: '',
},
currentColor: {
type: String,
default: '#BB1219',
},
},
data() {
return {
current: 0,
show: true,
timer: null,
time2: this.time,
}
},
watch: {
time2(val) {
if (val <= 0) {
this.onSkip()
}
},
},
computed: {
bgStyle() {
return this.obj2strStyle({
'background-color': this.bgColor,
})
},
afterStyle() {
return this.obj2strStyle({
'background-color': this.afterColor,
})
},
currentStyle() {
return this.obj2strStyle({
'background-color': this.currentColor,
})
},
skipPositionStyle() {
const { statusBarHeight } = uni.getSystemInfoSync()
if (!this.hasNavbar) {
return this.obj2strStyle({
top: `${statusBarHeight * 2 + 88 + 30}rpx`,
})
}
return this.obj2strStyle({
top: '30rpx',
})
},
},
created() {
this.$emit('openInt', this.onStart)
},
mounted() {
if (this.hasTabbar) {
uni.hideTabBar()
}
},
methods: {
onStart() {
this.timer = setInterval(() => {
this.time2--
}, 1000)
},
onPause() {
clearInterval(this.timer)
},
obj2strStyle(obj) {
let style = ''
for (let key in obj) {
style += `${key}:${obj[key]};`
}
return style
},
onSkip() {
const { url, hasTabbar, timer } = this
clearTimeout(timer)
this.show = false
if (hasTabbar) {
uni.showTabBar()
}
if (url) {
uni.reLaunch({
url: url,
})
}
},
onChangeSwiper(e) {
this.current = e.detail.current
},
toAdH5(ite) {
if (!ite.linkUrl) return
clearInterval(this.timer)
uni.navigateTo({
url: '/pages/adH5/adH5?url=' + ite.linkUrl,
})
},
},
}
</script>
<style scoped>
/* $nav-height: 44px; */
.start {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
}
.skip {
position: absolute;
z-index: 2;
background-color: rgba(0, 0, 0, 0.1);
color: #fff;
right: 30rpx;
font-size: 28rpx;
width: 133rpx;
height: 60rpx;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
}
.swiper {
height: 100vh;
width: 100vw;
}
.swiper-item {
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
background-color: transparent;
}
.swiper-item .after {
width: 100vw;
height: 500rpx;
position: absolute;
left: 0;
bottom: 0;
z-index: 1;
}
.swiper-item .image {
height: 100vh;
width: 100vw;
display: block;
position: relative;
z-index: 2;
}
.swiper-dot {
position: absolute;
width: 100vw;
left: 0;
bottom: 100rpx;
z-index: 3;
display: flex;
justify-content: center;
}
.swiper-dot .view {
width: 16rpx;
height: 16rpx;
border-radius: 100%;
background-color: rgba(0, 0, 0, 0.2);
margin: 0 12rpx;
}
.swiper-dot .view.active {
width: 30rpx;
border-radius: 24rpx;
}
</style>

View File

@@ -0,0 +1,57 @@
<template>
<swiper
class="swiper"
circular
:indicator-dots="true"
autoplay
:interval="interval * 1000"
:duration="500"
:style="{margin:mg}"
>
<block v-for="(v, i) in list" :key="i">
<swiper-item>
<image :src="v.imgUrl" mode="aspectFill" @tap="toH5(v.linkUrl)" />
</swiper-item>
</block>
</swiper>
</template>
<script setup>
const props = defineProps({
list: { type: Array, default: [] },
interval: { type: [String, Number] },
mg:{type: String, default:'40rpx'}
});
const toH5 = (url) => {
if (!url) return;
uni.navigateTo({
url: '/pages/adH5/adH5?url=' + url,
});
};
</script>
<style lang="scss" scoped>
.uni-margin-wrap {
width: 690rpx;
width: 100%;
}
.swiper {
margin: 40rpx;
height: 300rpx;
border-radius: 30rpx;
overflow: hidden;
}
.swiper-item {
display: block;
height: 300rpx;
line-height: 300rpx;
text-align: center;
}
image {
width: 100%;
}
</style>

View File

@@ -0,0 +1,131 @@
<!--
登录页专用文字上移样式行高宽度密码框与通用样式不一致
autoComplete="new-password"
-->
<template>
<div class="jee-input">
<div class="jee-text-up">
<a-input
ref="inputRef"
:type="inputPassword"
:value="value"
:required="isGoogle"
:disabled="props.disabled"
autoComplete="new-password"
@change="onChange($event)"
/>
<label @click="inputRef.focus()">{{ placeholder }}</label>
</div>
<div v-if="isPassword == 'password'" class="eyes" @click="changeEyes">
<!-- 密码框的小眼睛 -->
<eye-outlined v-if="!eyes" />
<eye-invisible-outlined v-else />
</div>
</div>
</template>
<script lang="ts" setup>
import { defineProps, ref } from 'vue'
// 定义input对象
const inputRef = ref()
// 定义父组件的传参和类型
const props = defineProps({
value: { type: String, default: null },
placeholder: { type: String, default: '' },
isPassword: { type: String, default: 'text' }, // 是否为密码框
isGoogle: { type: String, default: 'required' }, // 谷歌验证码是否必填
disabled: { type: Boolean, default: false } // 是否禁用
})
// nextTick(() => {
// // inputRef.value.focus()
// if(!props.value) {
// inputRef.value.blur()
// }
// // watch(() => props.value, () => {
// // })
// })
// 密码的显示与隐藏
let eyes = ref(true)
let inputPassword = ref(props.isPassword)
const changeEyes = () => {
eyes.value = !eyes.value
eyes.value
? (inputPassword.value = 'password')
: (inputPassword.value = 'text')
}
const emit = defineEmits(['update:value'])
const onChange = (e) => emit('update:value', e.target.value)
</script>
<style scoped lang="less">
.jee-input {
position: relative;
.eyes {
position: absolute;
right: 0;
top: 0;
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
z-index: 99;
}
}
// 文字上移 效果
.jee-text-up {
position: relative;
input {
outline: 0;
transition: all 0.2s ease;
height: 40px;
max-width: 300px;
}
input::-webkit-input-placeholder {
color: transparent;
font-size: 13px;
}
.placeShow::-webkit-input-placeholder {
color: #bfbfbf;
}
label {
margin-left: 6px;
position: absolute;
left: 0;
bottom: 13px;
padding: 0px 5px;
color: #bfbfbf;
font-size: 13px;
transition: all 0.2s ease;
border-radius: 3px;
line-height: 1;
display: flex;
justify-content: center;
align-items: center;
cursor: initial;
}
input:focus + label,
input:active + label,
input:valid + label {
font-weight: normal;
font-size: 12px;
letter-spacing: 0.05em;
text-align: left;
color: var(--ant-primary-color);
background: #fff; // 更换背景色
transform: translateY(-22px);
}
input:not(:focus) + label {
color: #b0afb3;
background: #fff;
}
}
</style>

View File

@@ -0,0 +1,103 @@
<!--
通用 确认弹层
@author terrfly
@site https://www.jeequan.com
@date 2022/11/22 07:30
-->
<template>
<uni-popup ref="popup" type="bottom" @maskClick="cancelFunc" mask-background-color="rgba(0,0,0,.5)" @change="change" :safe-area="false">
<view class="tips-wrapper">
<view class="tips-text"> {{ vdata.title }} </view>
<view class="tips-text tips-confirm" hover-class="u-cell-hover" hover-stay-time="150" :style="{color: vdata.confirmColor }" @tap="confirmFunc">
{{ vdata.confirmText || "确认" }}
</view>
<view class="line"></view>
<view class="tips-text tips-cancel" hover-class="u-cell-hover" hover-stay-time="150" @tap="cancelFunc">
{{ vdata.cancelText || "取消" }}
</view>
</view>
</uni-popup>
</template>
<script setup>
import { ref, reactive,inject } from "vue"
const wrapperHidden = inject('wrapperHidden')
const popup = ref(null) //弹窗实例
const tips = ref("") // 提示语
const vdata = reactive({
title: '',
confirmText: '',
cancelText: '',
confirmColor: '',
promiseObject: { },
})
function confirmFunc(){
popup.value.close()
vdata.promiseObject.resolve()
}
function cancelFunc(){
popup.value.close()
vdata.promiseObject.reject()
}
function open (title, confirmTextOrExtObject , cancelText ) {
vdata.title = title
vdata.confirmText = typeof confirmTextOrExtObject == "string" ? confirmTextOrExtObject : ''
vdata.cancelText = cancelText
if(typeof confirmTextOrExtObject == "object" ) {
Object.assign(vdata,confirmTextOrExtObject)
}
popup.value.open()
return new Promise( (resolve, reject) => {
vdata.promiseObject.resolve = resolve
vdata.promiseObject.reject = reject
})
}
const change = (e) =>{
if(wrapperHidden){
wrapperHidden(e.show)
}
}
defineExpose({ open })
</script>
<style lang="scss" scoped>
.tips-wrapper {
overflow: hidden;
border-radius: 32rpx 32rpx 0 0;
padding-bottom: 60rpx;
background-color: #fff;
.tips-text {
text-align: center;
height: 110rpx;
line-height: 110rpx;
font-size: 30rpx;
}
.line {
height: 20rpx;
background-color: rgba(0, 0, 0, 0.07);
}
.tips-confirm {
display: flex;
justify-content: center;
align-items: center;
border-top: 1rpx solid rgba(0, 0, 0, 0.07);
color: #2980fd;
font-size: 33rpx;
}
.tips-cancel {
justify-content: center;
align-items: center;
color: #808080;
font-size: 33rpx;
}
.u-cell-hover {
background-color: #f8f9fa;
}
}
</style>

View File

@@ -0,0 +1,358 @@
<!--
组件作用 弹层 列表支持 单选 多选
使用方法
<JeepayPopupListSelect ref="jeepayPopupListSelect" :reqTableDataFunc="reqTableDataFunc" :fields="{ key: 'articleId', left: 'articleId', right: 'title'}"/>
jeepayPopupListSelect.value.open().then((selected) => {
console.log(selected);
jeepayPopupListSelect.value.close() //自行关闭
})
@author terrfly
@site https://www.jeequan.com
@date 2022/11/26 16:24
-->
<template>
<uni-popup ref="popupRef" type="bottom" @maskClick="close" @change="change" mask-background-color="rgba(0,0,0,.5)" :safe-area="false">
<view class="card-wrapper">
<view class="selected-title" v-if="title">
{{ title }}
</view>
<view v-if="props.searchInputName" class="input-search">
<uni-easyinput
v-model="vdata.searchData[props.searchInputName]"
prefixIcon="search"
:inputBorder="false"
:clearable="false"
:styles="{
backgroundColor: 'transparent',
}"
type="text"
placeholder="搜索"
placeholderStyle=" color: rgba(0,0,0,0.85);font-size: 30rpx;"
/>
<view class="search-button" @tap="searchFunc">搜索</view>
</view>
<!-- 数据渲染 -->
<JTableList ref="jeepayTableListRef" :reqTableDataFunc="reqTableDataFuncWrapper" :searchData="vdata.searchData" :initData="false" height='406rpx'>
<template #tableBody="{ record }">
<view class="store" @tap="selectFunc(record)">
<template v-if="isCheckbox">
<view class="more-selected" :class="{ 'more-disabled': record.hasDirector, 'more-active': hasSelected(record) }">
<image :src="record.hasDirector ? '/static/iconImg/icon-disable.svg' : '/static/iconImg/icon-check.svg'" mode="scaleToFill" />
</view>
</template>
<view v-else :class="{ 'store-dot-def': true, 'active-dot': hasSelected(record) }"></view>
<slot name="content" :record="record">
<view class="store-inner-slot">
<view class="left">
<text>{{ record[props.fields.left] }}</text>
</view>
<view class="right">{{ record[props.fields.right] }}</view>
</view>
</slot>
</view>
</template>
</JTableList>
<!-- <button v-if="vdata.hasNext" @tap="jeepayTableListRef.addNext()">下一页</button> -->
<view class="footer-wrapper">
<view class="footer-main">
<view class="footer-button">
<view class="flex-center" hover-class="touch-button" @tap="close">取消</view>
<view @tap="confirmFunc" class="confirm flex-center" hover-class="touch-button">确认</view>
</view>
</view>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { inject, nextTick, reactive, ref, render, watch } from 'vue'
const jeepayTableListRef = ref()
// 定义组件参数
const props = defineProps({
configMode: { type: String, default: ''}, // 搜索时仅展示商户号
// 请求业务数据, 参数可自行控制。
reqTableDataFunc: { type: Function, default: () => {} },
// 搜索输入框的name 不传入则不显示搜索。
searchInputName: { type: String },
// 约定的字段 , 若不合适请通过插槽自行插入。
fields: { type: Object, default: { key: 'id', left: 'left', right: 'right' } },
// 是否多选框, 默认单选。
isCheckbox: { type: Boolean, default: false },
// 标题有则显示无则隐藏
title: [String, Number],
})
let changePageMetaOverflowFunc = inject('changePageMetaOverflowFunc')
const emits = defineEmits(['confirm'])
const popupRef = ref()
console.log(props,'propsprops')
const vdata = reactive({
searchData: {}, //当前页的搜索条件
hasNext: false, // 是否包含下一页数据
selectedList: [], // 选择的值
promiseObject: {},
})
// 点击搜索
function searchFunc() {
jeepayTableListRef.value.refTable(true)
}
/** reqTableDataFunc 处理函数 */
function reqTableDataFuncWrapper(params) {
return props.reqTableDataFunc(params).then(({ bizData }) => {
vdata.hasNext = bizData.hasNext // 是否包含下一页
return Promise.resolve({ bizData })
})
}
// 是否选中
function hasSelected(record) {
return vdata.selectedList.filter((r) => r[props.fields.key] == record[props.fields.key]).length > 0
}
/** 选择函数 **/
function selectFunc(record) {
// 判断是否已存在
if (hasSelected(record)) {
// 多选需删除
if (props.isCheckbox) {
vdata.selectedList.splice(
vdata.selectedList.findIndex((r) => r[props.fields.key] == record[props.fields.key]),
1
)
}
// 单选无需操作
return false
}
// 多选直接添加
if (props.isCheckbox) {
if (record.hasDirector) return //如果被其他店长绑定 禁止选中
return vdata.selectedList.push(record)
} else {
// 单选
return (vdata.selectedList = [record])
}
}
// 点击确定事件
function confirmFunc() {
// 多选
if (props.isCheckbox) {
emits('confirm', vdata.selectedList)
return vdata.promiseObject.resolve(vdata.selectedList)
}
// 单选 仅返回第一个即可。
emits('confirm', vdata.selectedList.length > 0 ? vdata.selectedList[0] : null)
vdata.promiseObject.resolve(vdata.selectedList.length > 0 ? vdata.selectedList[0] : null)
}
// 显示弹层, 并且返回一个promise
// promise.then(selected)
// 注意: 此Promise 只会回调一次, 如需要验证是否选中正确, 需要使用@confirm事件。
// 如果对拿到的值不做校验,获取到然后关闭。 那么可以直接使用.then() 然后立马关闭弹层。
function open(defaultVal) {
// 清空数据
vdata.selectedList = []
vdata.searchData[props.searchInputName] = ''
// 默认选中。
if (defaultVal) {
if (Array.isArray(defaultVal)) {
vdata.selectedList = defaultVal
} else {
vdata.selectedList = [defaultVal]
}
}
popupRef.value.open()
nextTick(() => {
jeepayTableListRef.value.refTable(true)
uni.hideTabBar() // 可能报错, 在nextTick中 不影响其他业务。
})
return new Promise((resolve, reject) => {
vdata.promiseObject.resolve = resolve
vdata.promiseObject.reject = reject
})
}
// 关闭弹层
function close() {
popupRef.value.close()
uni.showTabBar()
}
const change = (e) => {
if (changePageMetaOverflowFunc) {
changePageMetaOverflowFunc(!e.show)
}
}
// 将表格事件暴露出去 https://www.jianshu.com/p/39d14c25c987
defineExpose({ open, close })
</script>
<style lang="scss" scoped>
.card-wrapper {
border-radius: 32rpx 32rpx 0 0;
background-color: #fff;
overflow: hidden;
min-height: 70vh;
max-height: 80vh;
overflow-y: auto;
.selected-title {
height: 110rpx;
line-height: 110rpx;
text-align: center;
font-size: 30rpx;
font-weight: 500;
border-bottom: 1rpx solid #ededed;
}
.input-search {
display: flex;
align-items: center;
padding-left: 10rpx;
margin: 30rpx;
border-radius: 10rpx;
background-color: #f7f7f7;
.search-button {
padding: 24rpx 30rpx;
font-size: 32rpx;
font-weight: 500;
color: #2980fd;
}
}
.store {
position: relative;
display: flex;
justify-content: flex-start;
align-items: center;
padding: 0 40rpx;
height: 120rpx;
font-size: 30rpx;
.more-selected {
width: 36rpx;
height: 36rpx;
border-radius: 6rpx;
margin-right: 20rpx;
border: 2rpx solid rgba(217, 217, 217, 1);
image {
width: 100%;
height: 100%;
}
}
.more-disabled {
background-color: #f7f7f7;
}
.more-active {
background-color: #2980fd;
}
.store-dot-def {
position: relative;
margin-right: 20rpx;
width: 36rpx;
height: 36rpx;
background-color: #d7d8d9;
border-radius: 50%;
&::after {
content: '';
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 50%;
height: 50%;
border-radius: 50%;
background-color: #fff;
}
}
.active-dot {
background-color: #2980fd;
}
.store-inner-slot {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
.left {
display: flex;
align-items: center;
}
.right {
color: #999999;
}
}
}
.all-store::after {
content: '';
display: block;
position: absolute;
bottom: 0;
left: 40rpx;
right: 40rpx;
height: 1rpx;
background-color: #ededed;
}
.footer-wrapper {
height: 186rpx;
.footer-main {
position: fixed;
left: 0;
right: 0;
bottom: 0;
border-top: 1rpx solid #ededed;
.tips {
margin: 20rpx;
text-align: center;
font-size: 27rpx;
color: #a6a6a6;
}
.footer-button {
padding: 0 30rpx;
margin-top: 30rpx;
padding-bottom: 30rpx;
display: flex;
justify-content: space-between;
view {
width: 330rpx;
height: 110rpx;
font-size: 33rpx;
font-weight: 500;
color: rgba(0, 0, 0, 0.5);
border-radius: 20rpx;
background-color: #f7f7f7;
}
.confirm {
color: #fff;
background: $primaryColor;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,56 @@
<template>
<view class="tag-wrapper" :class="[calcType()]" :style="styles">
<slot />
</view>
</template>
<script setup>
import { reactive } from 'vue'
const props = defineProps({
type: { type: [String, Object], default: 'green' },
styles: { type: Object, default: () => ({}) },
})
const classList = ['purple', 'green', 'blue', 'green-rgba']
const calcType = () => {
try {
// 如果传入样式对象覆写样式
if (Object.keys(props.styles).length > 0) return ''
//如果预设样式类型中包含样式 使用预设样式类型
if (classList.includes(props.type)) return props.type
throw `预设样式类型中未包含此字段 请使使用style字段传入样式对象 自定义样式 注意样式名驼峰语法 目前预设样式字段有 ${classList.join(',')} `
} catch (err) {
console.error('error', err)
}
}
</script>
<style lang="scss" scoped>
.tag-wrapper {
display: inline-block;
margin-left: 15rpx;
padding: 0 15rpx;
height: 40rpx;
line-height: 40rpx;
border-radius: 6rpx;
text-align: center;
font-size: 23rpx;
color: #fff;
}
.purple {
background: linear-gradient(270deg, rgba(220, 61, 138, 1) 0%, rgba(187, 23, 92, 1) 100%);
}
.green {
background: linear-gradient(270deg, rgba(61, 220, 68, 1) 0%, rgba(23, 187, 118, 1) 100%);
}
.blue {
background: linear-gradient(270deg, rgba(35, 161, 252, 1) 0%, rgba(26, 102, 255, 1) 100%);
}
.green-rgba{
color: rgba(23, 188, 118, 1);
background: linear-gradient(270deg, rgba(61, 220, 68, 0.3) 0%, rgba(23, 187, 118, 0.3) 100%);
}
</style>

View File

@@ -0,0 +1,426 @@
<template>
<view>
<view class="default">
<!-- 删除按钮 -->
<view class="clear" v-if="vdata.hasImg && isShowClear && userIsShowClear">
<!-- -->
<image src="../../static/img/close.svg" @click.stop="logoutPopup.open()" mode="aspectFill"></image>
<!-- <image src="../../static/img/clear.svg" @click.stop="logoutPopup.open()"></image> -->
</view>
<view @click="previewImg" class="img-bg" v-if="vdata.hasImg">
<image :src="vdata.assemblyUrl" class="img-suc" mode="aspectFill"></image>
</view>
<image
v-if="!vdata.hasImg"
class="camera"
@click="chooseImg(['album', 'camera'])"
src="/static/iconImg/upload-picture.svg"
></image>
</view>
<!-- 图片预览手写非原生 -->
<enlarge
v-if="isPreview"
:changeIsShow="props.isShowClear"
:imgs="vdata.assemblyUrl"
@enlargeClose="enlargeClose"
@chooseImg="chooseImg"
/>
<!-- 删除图片二次确认 -->
<uni-popup ref="logoutPopup" type="dialog">
<uni-popup-dialog mode="base" :before-close="true" @close="logoutPopup.close()" @confirm="clear">
<template #title>
<view class="tip-content">确定要删除该图片吗</view>
</template>
</uni-popup-dialog>
</uni-popup>
</view>
</template>
<script setup>
import appConfig from "@/config/appConfig.js"
import { ref, reactive, watch, onMounted, nextTick } from "vue"
import { $ossFilesForm, $imgInfoDetail } from "@/http/apiManager.js"
import storageManage from "@/util/storageManage.js"
import enlarge from "./enlarge.vue" // 图片预览
import useBackPress from "@/hooks/useBackPress.js" // 返回阻断函数
import { sm4DecryptByResData } from "@/util/encryptUtil.js"
const logoutPopup = ref(null) // 二次确认弹框
const props = defineProps({
upLoadUrl: { type: String, default: "/api/ossFiles/singleFile" }, // 默认图片
imgUrl: { type: String, default: "" }, // 默认图片
recogObjectType: { type: String, default: "" }, // 识别对象类型idcard, idcard_back, bankcard, business_license
uploadCount: { type: Number, default: 1 }, // 上传图片张数
size: { type: Number, default: 2 }, // 图片限制大小,单位M
imgTypes: { type: Array, default: () => ["jpg", "jpeg", "png"] }, // 图片类型限制,如: ['jpg', 'jpeg']
isShowClear: { type: Boolean, default: true }, // 进件成功不展示删除按钮与更换按钮
ocrFlag: { type: String, default: "" }, // ocr识别标志图片类型 idCard-身份证bankCard-银行卡, license-营业执照
userIsShowClear: { type: Boolean, default: true }, // 在其余页面中是否展示删除按钮
})
onMounted(() => {
if (props.imgUrl) {
vdata.hasImg = true
vdata.assemblyUrl = props.imgUrl
}
})
// 关闭图片预览
const enlargeClose = () => {
isPreview.value = false
// #ifdef H5 || APP-PLUS
inactive()
// #endif
}
const vdata = reactive({
hasImg: false,
assemblyUrl: "",
})
watch(
() => props.imgUrl,
(newVal, oldVal) => {
nextTick(() => {
newVal ? (vdata.hasImg = true) : (vdata.hasImg = false)
vdata.assemblyUrl = newVal
})
}
)
const emit = defineEmits(["uploadSuccess", "clear"])
// OCR识别函数
function imgInfoDetail(res) {
$imgInfoDetail({
imgUrl: res.data,
type: props.ocrFlag,
}).then(({ bizData }) => {
if (Object.keys(bizData).length < 1) {
// uni.showToast({
// title: '图片识别失败',
// icon: 'none'
// })
} else {
uni.showToast({
title: "图片识别成功,已自动回填内容,请检查核对",
icon: "none",
})
}
res.ocrInfo = bizData
return emit("uploadSuccess", res)
})
}
function chooseImg(index) {
console.log(index)
if (props.isShowClear) {
uni.chooseImage({
count: props.uploadCount,
sizeType: ["compressed"],
sourceType: index,
crop: { quality: 20 },
success: (chooseImageRes) => {
isPreview.value = false //关闭图片预览
const msg = beforeUpload(chooseImageRes)
if (msg != "success") return uni.showToast({ title: msg, icon: "none" })
// 根据form信息判断图片上传地址
// 此处额外验证上传的图片是否含有name属性如果没有则用path代替
let sourceFileName
if (chooseImageRes.tempFiles[0].name) {
sourceFileName = chooseImageRes.tempFiles[0].name
} else {
sourceFileName = chooseImageRes.tempFiles[0].path
}
$ossFilesForm({
bizType: "applyment",
sourceFileName: sourceFileName,
sourceFileSize: chooseImageRes.tempFiles[0].size,
}).then(({ bizData }) => {
let url,
isOss,
ossImgUrl = ""
if (bizData.formActionUrl === "LOCAL_SINGLE_FILE_URL") {
url = appConfig.env.JEEPAY_BASE_URL + props.upLoadUrl
isOss = false
} else {
url = bizData.formActionUrl
ossImgUrl = { data: bizData.ossFileUrl }
isOss = true
}
// 调用图片上传方法
uploadImg(chooseImageRes.tempFilePaths, url, bizData.formParams, isOss, ossImgUrl)
console.log(url,'bizData.formParamsbizData.formParamsbizData.formParams');
})
},
})
}
}
// 上传图片
function uploadImg(tempFilePaths, url, formParams, isOss, ossImgUrl) {
const token = storageManage.token()
var successCount = 0 //多图时,上传成功数量
var qualification = [] //多图存储
uni.showLoading({ title: "上传中..." })
//循环上传图片
tempFilePaths.forEach((tempFilePath) => {
console.log(tempFilePath);
uni.uploadFile({
url: url,
filePath: tempFilePath,
name: "file",
header: {
itoken: token,
},
formData: formParams,
// 代表完成的函数 注:此处可以传入三个函数 success (成功)/ fail失败 / complete (完成)
complete(res) {
uni.hideLoading()
// oss接口特有情况
if (isOss) {
if (res.statusCode != 200) {
return uni.showToast({ title: "请求失败,请重试", icon: "none" })
}
// 如果存在OCR 识别信息那么要调用ocr识别函数
if (props.ocrFlag) return imgInfoDetail(ossImgUrl)
emit("uploadSuccess", ossImgUrl)
return (vdata.hasImg = true)
}
// res 即接口返回数据包
let {
statusCode, // 状态码
data, // 数据
msg, // 信息
errMsg, // 错误信息
} = res
// 接收请求,执行响应操作
if (statusCode != 200) {
uni.showToast({
title: "请求失败请重试statusCode" + statusCode,
icon: "none",
mask: true,
duration: 2000,
})
return
}
if (typeof data == "string") {
data = JSON.parse(data)
}
// 图片上传解密
if (data.encryptData) {
data.data = sm4DecryptByResData(data.encryptData)
}
//状态码为1001(未登录)
if (data.code == 1001) {
uni.showToast({
title: "请登录",
icon: "none",
mask: true,
duration: 1500,
})
setTimeout(function () {
uni.reLaunch({
url: "/pages/login/login",
})
}, 1500)
return
}
if (data.code != 0) {
uni.showToast({
title: data.msg || "请求失败,请重试",
duration: 2000,
mask: true,
icon: "none",
})
return
}
// 如果存在OCR 识别信息那么要调用ocr识别函数
if (props.ocrFlag) return imgInfoDetail(data)
// 没有遇到以上的情况
emit("uploadSuccess", data)
vdata.hasImg = true
},
})
})
}
// 上传图片前的校验
function beforeUpload(chooseImageRes) {
const tempFiles = chooseImageRes.tempFiles
let msg = "success"
if (tempFiles) {
for (let i = 0; i < tempFiles.length; i++) {
// 图片地址
let tempUrl = tempFiles[i].name || tempFiles[i].path
if (!tempUrl) {
msg = "请选择图片"
return msg
}
// 校验图片大小
if (tempFiles[i].size > props.size * 1024 * 1024) {
msg = "单张图片请不超过" + props.size + "MB"
return msg
}
// 校验后缀
if (!checkSuffix(tempUrl)) {
msg = "仅支持【" + props.imgTypes + "】格式的图片"
return msg
}
}
} else {
msg = "请选择图片"
return msg
}
if (chooseImageRes.tempFilePaths.length > props.uploadCount) {
msg = "最多上传" + props.uploadCount + "张图片"
return msg
}
return msg
}
// 校验图片后缀
function checkSuffix(fileName) {
const suffix = getFileSuffix(fileName).toLowerCase()
if (props.imgTypes.includes(suffix)) {
return true
}
return false
}
// 获取图片后缀
function getFileSuffix(fileName) {
if (!fileName || fileName.indexOf(".") < 0 || fileName.length <= 1) {
return ""
}
return fileName.substring(fileName.lastIndexOf(".") + 1)
}
let isPreview = ref(false) // 图片预览是否展示
// 预览图片
function previewImg() {
isPreview.value = true
// #ifdef H5 || APP-PLUS
active()
// #endif
// onceVar.imgView(true)
// let urls = [];
// urls.push(props.imgUrl)
// uni.previewImage({
// urls: urls
// });
}
// 叉号 清除图片
function clear() {
vdata.hasImg = false
emit("clear")
logoutPopup.value.close()
}
// #ifdef H5 || APP-PLUS
const { active, inactive } = useBackPress(enlargeClose) // onBackPress 阻断返回
// #endif
</script>
<style scoped lang="scss">
// 图片预览
.preview-img {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: #000;
z-index: 25;
}
.camera {
width: 50rpx;
height: 50rpx;
}
.default {
position: relative;
width: 120rpx;
height: 120rpx;
background-color: #fff;
border-radius: 5rpx;
display: flex;
align-items: center;
justify-content: center;
border: 1rpx dashed #cccdd9;
}
.img-bg {
position: relative;
width: 120rpx;
height: 120rpx;
background: #f1f1f1;
border-radius: 12rpx;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
/* margin-right: 30rpx; */
image {
height: 100%;
width: 100%;
}
}
.img-def {
width: 43rpx;
height: 37rpx;
}
.img-suc {
width: 100%;
/* height: 180rpx; */
border-radius: 12rpx;
}
.clear {
position: absolute;
top: -10rpx;
left: 90rpx;
width: 36rpx;
height: 36rpx;
border-radius: 50%;
z-index: 12;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
image {
width: 30rpx;
height: 30rpx;
}
}
.tip-content {
display: flex;
justify-content: center;
align-items: center;
height: 120rpx;
font-size: 32rpx;
font-weight: 700;
color: #3981ff;
}
</style>

View File

@@ -0,0 +1,171 @@
<template>
<view class="previewImage" :style="{ 'background-color': 'rgba(0,0,0,' + opacity + ')' }" @tap="close">
<movable-area class="marea" scale-area>
<movable-view
:id="'movable-view-' + i"
:key="'movable-view-' + i"
class="mview"
direction="all"
:out-of-bounds="false"
:inertia="true"
damping="90"
friction="2"
scale="true"
scale-min="1"
scale-max="4"
:scale-value="scale"
>
<image
:id="'image-' + i"
:key="'movable-view' + i"
class="image"
:src="imgs"
:data-index="i"
:data-src="img"
mode="aspectFill"
/>
</movable-view>
</movable-area>
<view v-if="changeIsShow" class="change-img">
<view class="change-item" style="border-bottom: 2rpx solid #1a1a1a" @click="chooseImg(['camera'])">
<image src="/static/iconImg/photograph.svg"></image>
<text>拍照</text>
</view>
<view class="change-item" @click="chooseImg(['album'])">
<image src="/static/iconImg/album.svg"></image>
<text>从相册选择</text>
</view>
</view>
</view>
</template>
<script>
export default {
name: "ksj-previewImage", //插件名称
props: {
imgs: {
//图片列表
type: String,
required: true,
default: "",
},
//透明度,0到1之间。
opacity: {
type: Number,
default: 1,
},
changeIsShow: { type: Boolean, default: true },
},
data() {
return {
swiper: false, //是否禁用
show: false, //显示状态
index: 0, //当前页
deg: 0, //旋转角度
time: 0, //定时器
interval: 1000, //长按事件
scale: 1, //缩放比例
}
},
methods: {
chooseImg(type) {
this.$emit("chooseImg", type)
},
//旋转
rotate(e) {
this.deg = this.deg == 270 ? 0 : this.deg + 90
},
close() {
this.$emit("enlargeClose")
},
},
}
</script>
<!--使用scss,只在本组件生效-->
<style lang="scss" scoped>
.previewImage {
z-index: 25;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #000000;
user-select: none;
.marea {
height: 100%;
width: 100%;
position: fixed;
overflow: hidden;
.mview {
display: flex;
align-items: center;
justify-content: center;
width: 692rpx;
height: 692rpx;
border-radius: 23rpx;
left: 29rpx;
top: 192rpx;
overflow: hidden;
.image {
width: 100%;
}
}
}
.rotate {
position: absolute;
right: 10rpx;
width: 120rpx;
height: 56rpx;
bottom: 10rpx;
text-align: center;
padding: 10rpx;
.text {
background-color: rgba(0, 0, 0, 0.5);
color: #fff;
font-size: 30rpx;
border-radius: 20rpx;
border: 1rpx solid #f1f1f1;
padding: 6rpx 22rpx;
user-select: none;
text-align: center;
}
.text:active {
background-color: rgba(100, 100, 100, 0.5);
}
}
}
.change-img {
position: fixed;
width: 385rpx;
height: 212rpx;
background: rgba(255, 255, 255, 0.15);
bottom: 5%;
left: 50%;
transform: translateX(-50%);
z-index: 30;
border-radius: 15rpx;
.change-item {
width: 100%;
height: 106rpx;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
image {
width: 31rpx;
height: 31rpx;
margin-right: 10rpx;
}
text {
font-weight: 500;
font-size: 33rpx;
color: #fff;
}
}
}
</style>

View File

@@ -0,0 +1,27 @@
<template>
<view class="main" :class="{ 'main-hidden': vdata.hidden }">
<slot />
</view>
</template>
<script setup>
import { reactive, provide } from 'vue'
const vdata = reactive({
hidden: false,
})
const wrapperHidden = (e) => {
return (vdata.hidden = e)
}
provide('wrapperHidden', wrapperHidden)
</script>
<style lang="scss" scoped>
.main {
min-height: 100vh;
}
.main-hidden {
height: 99vh;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,120 @@
<template>
<view class="item" :style="{backgroundColor: props.backColor}" :class="[props.border]">
<!-- 左侧内容 即标题 -->
<view class="title">
<text v-if="props.start" class="require">*</text>
<text style="color:#666f80"> {{ props.text }}</text>
</view>
<!-- 右侧内容 父组件替换此插槽可以转变为上传图片等左右结构的布局 -->
<slot>
<input type="text" :value="props.value" @input="onChange" :placeholder="placeholderText" :disabled="props.disabled" />
</slot>
</view>
<view v-if="props.tipText" class="tipText">
({{ props.tipText }})
</view>
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps({
value: {type: String, default: ''},
text: {type: String, default: ''},
tipText: {type: String, default: ''}, // 自定义的提示文字
start: {type: Boolean, default: true }, // 代表必填项的小星星
disabled: {type: Boolean, default: false}, // 是否禁止填写
backColor: {type: String, default: '#fafbfc'}, // 背景颜色
border: {type: String, default: 'top-border'}, // border位置 上或下
tipHolder: {type: String, default: ''} // 自定义placeholder
})
// 自定义placeholder
let placeholderText = props.tipHolder ? props.tipHolder : `请输入${props.text}`
const emit = defineEmits(['update:value'])
const onChange = e => emit('update:value', e.detail.value)
</script>
<style scoped lang="scss">
.item{
box-sizing: border-box;
//height: 110rpx;
width: 100%;
// background-color: #fafbfc;
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 35rpx 30rpx 35rpx 30rpx;
font-weight: 500;
font-size: 28rpx;
text-align: left;
color: #000;
.title{
text-align: left;
font-weight: 500;
font-size: 28rpx;
color: #666f80;
margin-right: 20rpx;
white-space: nowrap;
}
input, textarea {
flex-grow: 1;
text-align: right;
font-size: 28rpx;
}
.imgupload{
position: relative;
height: 120rpx;
width: 120rpx;
background-color: #E6E9ED;
border-radius: 10rpx;
display: flex;
align-items: center;
justify-content: center;
}
.delate{
position: absolute;
top: 70rpx;
left: -20rpx;
height: 40rpx;
width: 40rpx;
border-radius: 5rpx;
background-color: #ff0000;
}
}
// 星号
.require {
width: 20rpx;
color: red;
}
// 禁止标题换行
.title {
display: flex;
flex-direction: row;
text {
white-space: nowrap;
}
}
.top-border {
border-top: 1rpx solid #e8e8e8;
}
.bottom-border {
border-bottom: 1rpx solid #e8e8e8;
}
.tipText {
margin: 8rpx 20rpx 10rpx;
color: #808080;
}
</style>

View File

@@ -0,0 +1,16 @@
## 进件板块通用组件
## JeePayForm
- 通用的左右结构布局
- 单标签模式适用于普通输入框
- 通过传参,适用于文本域
- 通过插槽,适用于进件板块绝大部分左右结构布局
## termOfValidity
- 选择证件的有效期
- 默认是选结束时间
## dataPicker
- 由uni官方的 uni-data-picker二次封装而来
- 主要适用于进行选择行业mcc 省市区县等级联选择框
- 由于不同进件渠道所需要的值不同,需要根据不同的参数来返回不同的值 有的行业mcc码只要最后一个有的需要用_拼接 (省市区,有的只选择到市,绝大部分需要选择到县)

View File

@@ -0,0 +1,105 @@
<template>
<view>
<!-- 为了兼容微信小程序所以在此根据v-if写了两遍 v-model和value 也写了两边 -->
<uni-data-picker ref="dataPickerRef" v-if="props.code" :localdata="props.localdata" v-model="props.code" :value="props.code" @change="dataChange" v-slot:default="{data, error, options}" @popupopened="popupopened" @popupclosed="popupclosed" :map="props.mapText">
{{ data.length != 0 ? dataText(data) : '请选择' }}
</uni-data-picker>
<uni-data-picker ref="dataPickerRef" v-if="!props.code" :localdata="props.localdata" v-model="props.code" :value="props.code" @change="dataChange" v-slot:default="{data, error, options}" @popupopened="popupopened" @popupclosed="popupclosed" :map="props.mapText">
{{ data.length != 0 ? dataText(data) : '请选择' }}
</uni-data-picker>
</view>
</template>
<script setup>
import { ref } from 'vue'
import useBackPress from '@/hooks/useBackPress.js' // 返回阻断函数
const props = defineProps({
code: {type: String, default: ''}, // 文字回显用到的码
localdata: {type: Array, default: () => [] }, // 级联选择用到的数据
paramType: {type: String, default: 'arr' }, // 参数类型
mapText: {type: Object, default:{text:'text',value:'value'}} // 映射字段
})
const emit = defineEmits(['change'])
// 保持与其他数据参数格式一致,所以也改成 e.detail.value的方式
let data = {
detail: {
value: ''
}
}
const dataPickerRef = ref()
/*
* 参数类型集合 默认为arr
* splicing 拼接模式 适用于支付宝官方行业mccCode 例mccCode":"A0002_B0015"
* arr 数组模式 大部分省市区县选择,都需要数组 例 areaCode":["120000","120100","120112"]
* last 单值模式 适用于盛付通进件mccCode它只需要最后一个code 例mccCode":"4900"
* oneLevel 级联选择 只有一级 适用于云闪付mcc 拉卡拉pos类型等
*/
// 向父组件传递选择后的数据
const dataChange = e => {
let result = '' // 拿code值用
let resText = '' // 拿文字用
// 根据参数不同走不同分支
if (props.paramType === 'splicing') { // splicing 拼接
result = e.detail.value[0].value + '_' + e.detail.value[1].value
}
else if (props.paramType === 'arr') { // arr 数组
result = []
resText = []
e.detail.value.forEach(item => {
result.push(item.value)
resText.push(item.text)
})
}
else if (props.paramType === 'last') { // last 单值
result = e.detail.value[e.detail.value.length - 1].value
}
else if (props.paramType === 'oneLevel') { // oneLevel 只有一级
result = e.detail.value[0].value
}
// 父组件在拿到值之后,可以统一走 e.detail.value的方式拿值了
data.detail.value = result
data.detail.text = resText
emit('change', data)
}
// data-pciker 回显处理函数
const dataText = (data) => {
let result = ''
data.forEach(item => result += (item.text + '/'))
result = result.substr(0, result.length -1 ) // 截取掉最后一个斜杠
return result
}
/*
处理开启弹窗 与 关闭弹窗 的 阻断返回
*/
const popupopened = () => {
// #ifdef H5 || APP-PLUS
active()
// #endif
}
const popupclosed = () => {
// #ifdef H5 || APP-PLUS
inactive()
// #endif
}
const closeEd = () => dataPickerRef.value.hide()
// #ifdef H5 || APP-PLUS
const {active, inactive} = useBackPress(closeEd) // onBackPress 阻断返回
// #endif
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,44 @@
<template>
<view>
<picker mode="date" fields="day" :value="props.defaultDate" @change="bindDateChange" v-show="props.defaultDate != '长期'">
<view class="uni-input" style="text-align: right;">{{props.defaultDate ? props.defaultDate : '请选择'}}</view>
</picker>
<checkbox-group @change="end" v-if="props.isEnd">
<label><checkbox value="长期" :checked="props.defaultDate == '长期' " color="#FFCC33" />长期</label>
</checkbox-group>
</view>
</template>
<script setup>
import { ref } from 'vue'
import useBackPress from '@/hooks/useBackPress.js' // 返回阻断函数
const props = defineProps({
isEnd: { type: Boolean, default: true } , // 默认为选择结束时间
defaultDate: {type: String, default: ''}, // 传入的默认时间
})
const emit = defineEmits(['publicSelect'])
// 保持与其他数据参数格式一致,所以也改成 e.detail.value的方式
let data = {
detail: {
value: ''
}
}
// 普通日期选择
const bindDateChange = e => emit('publicSelect', e)
// 点击长期
const end = e => {
data.detail.value = e.detail.value[0] ? '长期' : ''
emit('publicSelect', data)
}
</script>
<style scoped lang="scss">
</style>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,124 @@
[{
"value": "0323",
"text": "上海华瑞银行"
}, {
"value": "A000",
"text": "境外银行"
}, {
"value": "0102",
"text": "中国工商银行"
}, {
"value": "0103",
"text": "中国农业银行"
}, {
"value": "0104",
"text": "中国银行"
}, {
"value": "0105",
"text": "中国建设银行"
}, {
"value": "0301",
"text": "交通银行"
}, {
"value": "0308",
"text": "招商银行"
}, {
"value": "0309",
"text": "兴业银行"
}, {
"value": "0305",
"text": "中国民生银行"
}, {
"value": "0306",
"text": "广东发展银行"
}, {
"value": "0307",
"text": "平安银行股份有限公司"
}, {
"value": "0310",
"text": "上海浦东发展银行"
}, {
"value": "0304",
"text": "华夏银行"
}, {
"value": "0313",
"text": "其他城市商业银行"
}, {
"value": "0402",
"text": "其他农村信用合作社"
}, {
"value": "0203",
"text": "中国农业发展银行"
}, {
"value": "0314",
"text": "其他农村商业银行"
}, {
"value": "0315",
"text": "恒丰银行"
}, {
"value": "0403",
"text": "中国邮政储蓄银行股份有限公司"
}, {
"value": "0303",
"text": "中国光大银行"
}, {
"value": "0302",
"text": "中信银行"
}, {
"value": "0316",
"text": "浙商银行股份有限公司"
}, {
"value": "0318",
"text": "渤海银行股份有限公司"
}, {
"value": "0319",
"text": "徽商银行"
}, {
"value": "0671",
"text": "香港地区渣打银行"
}, {
"value": "0502",
"text": "东亚银行(中国)有限公司"
}, {
"value": "0317",
"text": "其他农村合作银行"
}, {
"value": "0322",
"text": "上海农村商业银行"
}, {
"value": "0320",
"text": "村镇银行"
}, {
"value": "0501",
"text": "汇丰银行"
}, {
"value": "0503",
"text": "南洋商业银行"
}, {
"value": "0593",
"text": "友利银行"
}, {
"value": "0595",
"text": "新韩银行"
}, {
"value": "0596",
"text": "企业银行"
}, {
"value": "0597",
"text": "韩亚银行"
}, {
"value": "0621",
"text": "华侨银行"
}, {
"value": "0622",
"text": "大华银行"
}, {
"value": "0531",
"text": "花旗银行"
}, {
"value": "0781",
"text": "厦门国际银行"
}, {
"value": "0787",
"text": "华一银行"
}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,311 @@
{
"micro": [{
"settlementId": 747,
"settlementName": "提供网上交易场所或信息服务的业务、通讯业务、财经类业务及其他平台服务、餐饮、零售、交通出行等实体业务",
"settRule": "费率0.6%入账周期T+1",
"qualification": [{
"qualificationType": "",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}]
}, {
"settlementId": 777,
"settlementName": "物流快递服务",
"settRule": "费率0.3%入账周期T+1",
"qualification": [{
"qualificationType": "速送",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}]
}],
"individual": [{
"settlementId": 779,
"settlementName": "物流快递服务",
"settRule": "费率0.3%入账周期T+1",
"qualification": [{
"qualificationType": "速送",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}]
}, {
"settlementId": 802,
"settlementName": "提供网上交易场所或信息服务的业务、餐饮、零售、交通出行等实体业务",
"settRule": "费率0.6%入账周期T+1",
"qualification": [{
"qualificationType": "餐饮",
"isNeedQualification": false,
"qualificationDesc": "选填,若贵司具备以下资质,主体为餐饮业态,建议提供:《食品经营许可证》或《餐饮服务许可证》"
}, {
"qualificationType": "电商平台",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}, {
"qualificationType": "零售",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质,若涉及烟草售卖,需提供《烟草专卖零售许可证》"
}, {
"qualificationType": "食品生鲜",
"isNeedQualification": false,
"qualificationDesc": "选填,若贵司具备以下资质,主体为食品业态,建议提供:《食品经营许可证》或《食品生产许可证》或供销协议+合作方资质"
}, {
"qualificationType": "咨询/娱乐票务",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}, {
"qualificationType": "房产中介",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}, {
"qualificationType": "宠物医院",
"isNeedQualification": true,
"qualificationDesc": "《动物诊疗许可证》"
}, {
"qualificationType": "共享服务",
"isNeedQualification": true,
"qualificationDesc": "需提供资金监管协议。协议要求1、主体与商业银行签订2、内容针对交易资金使用和偿付进行监管3、协议须在有效期内4、结算账户须与资金监管账户一致。"
}, {
"qualificationType": "休闲娱乐/旅游服务",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}, {
"qualificationType": "游艺厅/KTV",
"isNeedQualification": true,
"qualificationDesc": "《娱乐场所经营许可证》或《文化经营许可证》"
}, {
"qualificationType": "网吧",
"isNeedQualification": true,
"qualificationDesc": "《网络文化经营许可证》"
}, {
"qualificationType": "院线影城",
"isNeedQualification": true,
"qualificationDesc": "《电影放映经营许可证》"
}, {
"qualificationType": "演出赛事",
"isNeedQualification": true,
"qualificationDesc": "《营业性演出许可证》"
}, {
"qualificationType": "居民生活服务",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}, {
"qualificationType": "景区/酒店",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}, {
"qualificationType": "铁路客运",
"isNeedQualification": true,
"qualificationDesc": "收费授权证明文件(如授权证明书或合同)"
}, {
"qualificationType": "机票/票务代理",
"isNeedQualification": true,
"qualificationDesc": "《航空公司营业执照》或《航空公司机票代理资格证》"
}, {
"qualificationType": "培训机构",
"isNeedQualification": true,
"qualificationDesc": "若贵司具备以下资质建议提供1、《办学许可证》或相关批文2、驾校培训提供有“驾驶员培训”项目的《道路运输经营许可证》"
}, {
"qualificationType": "保健器械/医疗器械/非处方药品",
"isNeedQualification": true,
"qualificationDesc": "互联网售药提供《互联网药品交易服务证》或《互联网药品信息服务资格证书》+《药品经营许可证》;线下门店卖药提供《药品经营许可证》;医疗器械提供《医疗器械经营企业许可证》"
}, {
"qualificationType": "私立/民营医院/诊所",
"isNeedQualification": true,
"qualificationDesc": "《医疗机构执业许可证》中医诊所提供《中医诊所备案证》"
}, {
"qualificationType": "其他缴费",
"isNeedQualification": true,
"qualificationDesc": "收费授权证明文件(如授权证明书或合同)"
}, {
"qualificationType": "停车缴费",
"isNeedQualification": false,
"qualificationDesc": "请提供停车收费资质"
}]
}],
"enterprise": [{
"settlementId": 780,
"settlementName": "保险业务",
"settRule": "费率0.6%入账周期T+1",
"qualification": [{
"qualificationType": "保险业务",
"isNeedQualification": true,
"qualificationDesc": "保险公司提供《经营保险业务许可证》《保险业务法人登记证书》,其他公司提供相关资质"
}]
}, {
"settlementId": 781,
"settlementName": "财经、互联网募捐信息平台服务",
"settRule": "费率0.6%入账周期T+1虚拟限额",
"qualification": [{
"qualificationType": "财经/股票类资讯",
"isNeedQualification": false,
"qualificationDesc": "选填,若有具体的荐股行为,需资质《证券投资咨询业务资格证书》"
}, {
"qualificationType": "互联网募捐信息平台",
"isNeedQualification": true,
"qualificationDesc": "必须符合并提供“慈善组织互联网募捐信息平台公告”截图,且必须提供资金监管协议。"
}]
}, {
"settlementId": 795,
"settlementName": "典当",
"settRule": "费率0.6%入账周期T+1",
"qualification": [{
"qualificationType": "典当",
"isNeedQualification": true,
"qualificationDesc": "典当:《典当经营许可证》"
}]
}, {
"settlementId": 800,
"settlementName": "提供网上交易场所或信息服务的业务、通讯业务、财经类业务及其他平台服务、餐饮、零售、交通出行等实体业务",
"settRule": "费率0.6%入账周期T+1",
"qualification": [{
"qualificationType": "餐饮",
"isNeedQualification": false,
"qualificationDesc": "选填,若贵司具备以下资质,主体为餐饮业态,建议提供:《食品经营许可证》或《餐饮服务许可证》"
}, {
"qualificationType": "电商平台",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}, {
"qualificationType": "零售",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质,若涉及烟草售卖,需提供《烟草专卖零售许可证》"
}, {
"qualificationType": "食品生鲜",
"isNeedQualification": false,
"qualificationDesc": "选填,若贵司具备以下资质,主体为食品业态,建议提供:《食品经营许可证》或《食品生产许可证》或供销协议+合作方资质"
}, {
"qualificationType": "咨询/娱乐票务",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}, {
"qualificationType": "房地产",
"isNeedQualification": true,
"qualificationDesc": "《建设用地规划许可证》《建设工程规划许可证》《建筑工程施工许可证》《国有土地使用证》《商品房预售许可证》缺一不可,证照主体名称要完全一致"
}, {
"qualificationType": "房产中介",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}, {
"qualificationType": "宠物医院",
"isNeedQualification": true,
"qualificationDesc": "《动物诊疗许可证》"
}, {
"qualificationType": "共享服务",
"isNeedQualification": true,
"qualificationDesc": "需提供资金监管协议。协议要求: 1、主体与商业银行签订 2、内容针对交易资金使用和偿付进行监管 3、协议须在有效期内 4、结算账户须与资金监管账户一致。"
}, {
"qualificationType": "休闲娱乐/旅游服务",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}, {
"qualificationType": "游艺厅/KTV",
"isNeedQualification": true,
"qualificationDesc": "《娱乐场所经营许可证》或《文化经营许可证》"
}, {
"qualificationType": "网吧",
"isNeedQualification": true,
"qualificationDesc": "《网络文化经营许可证》"
}, {
"qualificationType": "院线影城",
"isNeedQualification": true,
"qualificationDesc": "《电影放映经营许可证》"
}, {
"qualificationType": "演出赛事",
"isNeedQualification": true,
"qualificationDesc": "《营业性演出许可证》"
}, {
"qualificationType": "居民生活服务",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}, {
"qualificationType": "景区/酒店",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}, {
"qualificationType": "铁路客运",
"isNeedQualification": true,
"qualificationDesc": "收费授权证明文件(如授权证明书或合同)"
}, {
"qualificationType": "高速公路收费",
"isNeedQualification": true,
"qualificationDesc": "收费授权证明文件(如授权证明书或合同)"
}, {
"qualificationType": "城市公共交通",
"isNeedQualification": true,
"qualificationDesc": "收费授权证明文件(如授权证明书或合同)"
}, {
"qualificationType": "船舶/海运服务",
"isNeedQualification": true,
"qualificationDesc": "《港口经营许可证》"
}, {
"qualificationType": "旅行社",
"isNeedQualification": true,
"qualificationDesc": "《旅行社业务经营许可证》"
}, {
"qualificationType": "机票/票务代理",
"isNeedQualification": true,
"qualificationDesc": "《航空公司营业执照》或《航空公司机票代理资格证》"
}, {
"qualificationType": "培训机构",
"isNeedQualification": true,
"qualificationDesc": "若贵司具备以下资质建议提供1、《办学许可证》或相关批文2、驾校培训提供有“驾驶员培训”项目的《道路运输经营许可证》"
}, {
"qualificationType": "保健器械/医疗器械/非处方药品",
"isNeedQualification": true,
"qualificationDesc": "互联网售药提供《互联网药品交易服务证》或《互联网药品信息服务资格证书》+《药品经营许可证》;线下门店卖药提供《药品经营许可证》;医疗器械提供《医疗器械经营企业许可证》"
}, {
"qualificationType": "私立/民营医院/诊所",
"isNeedQualification": true,
"qualificationDesc": "《医疗机构执业许可证》;中医诊所提供《中医诊所备案证》"
}, {
"qualificationType": "有线电视缴费",
"isNeedQualification": true,
"qualificationDesc": "收费授权证明文件(如授权证明书或合同)"
}, {
"qualificationType": "其他缴费",
"isNeedQualification": true,
"qualificationDesc": "收费授权证明文件(如授权证明书或合同)"
}, {
"qualificationType": "文物经营/文物复制品销售",
"isNeedQualification": false,
"qualificationDesc": "选填,若销售文物,需提供《文物经营许可证》"
}, {
"qualificationType": "停车缴费",
"isNeedQualification": false,
"qualificationDesc": "请提供停车收费资质"
}]
}, {
"settlementId": 806,
"settlementName": "游戏、在线音视频等虚拟业务",
"settRule": "费率1%入账周期T+1虚拟限额",
"qualification": [{
"qualificationType": "在线图书/视频/音乐",
"isNeedQualification": true,
"qualificationDesc": "以下选其一:《互联网出版许可证》、《网络出版服务许可证》、《网络文化经营许可证》、《信息网络传播视听节目许可证》"
}, {
"qualificationType": "门户论坛/网络广告及推广/软件开发/其他互联网服务",
"isNeedQualification": false,
"qualificationDesc": "无需特殊资质"
}, {
"qualificationType": "游戏",
"isNeedQualification": true,
"qualificationDesc": "请提供有效期内的游戏版号(《网络游戏电子出版物审批》)"
}, {
"qualificationType": "网络直播",
"isNeedQualification": true,
"qualificationDesc": "需提供《网络文化经营许可证》且许可证的经营范围应当明确包括网络表演PC/wap网站需要有ICP备案"
}]
}, {
"settlementId": 801,
"settlementName": "物流快递服务",
"settRule": "费率0.3%,入账周期T+1",
"qualification": [{
"qualificationType": "快递",
"isNeedQualification": true,
"qualificationDesc": "需提供《快递业务经营许可证》"
}, {
"qualificationType": "物流",
"isNeedQualification": true,
"qualificationDesc": "需提供《道路运输许可证》,从事网络货运的,提供《增值电信业务许可证》、三级信息系统安全等级保护备案证明、《道路运输许可证》"
}]
}]
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,121 @@
<template>
<view class="dict">
<view>字典管理</view>
<view class="dict-main">
<input
class="dict-input"
v-model="dictText"
placeholder=""
@keydown.enter.native="searchDict"
placeholder-class="input-placeholder"
/>
<view class="dict-but">
<button type="primary" @click="searchDict" size="mini">
搜索
</button></view
>
</view>
<view
>{{ dictValue }} <text class="dict-copy" @click="copy">复制</text
><text class="dict-copy" @click="clearDictValue">清空</text></view
>
</view>
<view class="dict-card-list">
<block v-for="(v, i) in dictLists">
<view class="dict-card-main">
<view
>type{{ v.type }}
<text class="dict-add" @click="addDict(v.type)">添加</text></view
>
<view>value{{ v.value }}</view>
<view>text{{ v.text }}</view>
</view>
</block>
</view>
</template>
<script setup>
import { reactive, ref, watch } from "vue";
import { filterDicts } from "@/hooks/dict";
const dictText = ref("");
const dictLists = ref([]);
let dictValue = ref([]);
function searchDict() {
dictLists.value = filterDicts(dictText.value);
}
function addDict(v) {
dictValue.value.push(v);
}
function copy() {
uni.setClipboardData({
data: toStringDictType(),
});
}
function toStringDictType() {
let text = "";
dictValue.value.forEach((v, i) => {
if (i != dictValue.value.length - 1) {
text += `"${v}",`;
} else {
text += `"${v}"`;
}
});
return `[${text}]`;
}
function clearDictValue() {
dictValue.value = [];
}
</script>
<style lang="scss" scoped>
.dict {
width: 50vw;
margin: auto;
.dict-main {
display: flex;
align-items: center;
.dict-input {
width: 180px;
padding: 2px 0;
margin: 10px 0;
border: 1px solid #ccc;
border-radius: 5px;
text-indent: 1em;
}
.dict-but {
margin-left: 10px;
transform: translateY(1px);
}
}
.dict-copy {
font-size: 12px;
border: 0.5px solid #ccc;
background-color: skyblue;
border-radius: 2px;
padding: 0 5px;
margin: 0 5px;
cursor: pointer;
}
}
.dict-card-list {
display: flex;
flex-wrap: wrap;
.dict-card-main {
padding: 10px;
margin: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
view {
padding: 5px;
}
.dict-add {
font-size: 12px;
border: 0.5px solid #ccc;
background-color: skyblue;
border-radius: 2px;
padding: 0 5px;
cursor: pointer;
}
}
}
</style>

View File

@@ -0,0 +1,135 @@
<template>
<view v-show="isShow">
<view class="shade" @tap="hide"></view>
<view class="pop">
<view class="flex_col" style="margin-bottom: 20rpx;">
<view class="preview" :style="{'backgroundColor':pickerColor}"></view>
<view class="value">
<text v-if="pickerColor">颜色值{{pickerColor}}</text>
</view>
<view class="ok" @tap="setColor">确定</view>
</view>
<view class="list flex_col" v-for="(item,index) in colorArr" :key="index">
<view v-for="(v,i) in item" :key="i"
:style="{'backgroundColor':v}"
:data-color="v"
:data-index="index"
:data-i="i"
:class="{'active':(index==pickerArr[0] && i==pickerArr[1])}"
@tap="picker"></view>
</view>
<view :style="{'height':(bottom+'px')}"></view>
</view>
</view>
</template>
<script>
export default {
name:'picker-color',
props:{
isShow: {
type: Boolean,
default: false,
},
bottom:{
type: Number,
default: 0,
}
},
data() {
return {
colorArr:[
['#000000','#111111','#222222','#333333','#444444','#666666','#999999','#CCCCCC','#EEEEEE','#FFFFFF'],
['#ff0000','#ff0033','#ff3399','#ff33cc','#cc00ff','#9900ff','#cc00cc','#cc0099','#cc3399','#cc0066'],
['#cc3300','#cc6600','#ff9933','#ff9966','#ff9999','#ff99cc','#ff99ff','#cc66ff','#9966ff','#cc33ff'],
['#663300','#996600','#996633','#cc9900','#a58800','#cccc00','#ffff66','#ffff99','#ffffcc','#ffcccc'],
['#336600','#669900','#009900','#009933','#00cc00','#66ff66','#339933','#339966','#009999','#33cccc'],
['#003366','#336699','#3366cc','#0099ff','#000099','#0000cc','#660066','#993366','#993333','#800000']
],
pickerColor:'',
pickerArr:[-1,-1]
};
},
methods: {
picker(e) {
let data=e.currentTarget.dataset;
this.pickerColor=data.color;
this.pickerArr=[data.index,data.i];
},
hide(){
this.$emit("callback",'');
},
setColor(){
this.$emit("callback",this.pickerColor);
}
},
}
</script>
<style scoped>
.shade{
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0,0,0,0.5);
z-index: 99;
}
.pop{
position: fixed;
right: 0;
bottom: 0;
left: 0;
background-color: #fff;
z-index: 100;
padding: 20upx 20upx 10upx 20upx;
font-size: 32upx;
}
.flex_col{
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
align-content: center;
}
.list{
justify-content: space-between;
}
.list>view{
width: 60upx;
height: 60upx;
margin-bottom: 10upx;
box-sizing: border-box;
border-radius: 3px;
box-shadow: 0 0 2px #ccc;
}
.list .active{
box-shadow: 0 0 2px #09f;
transform:scale(1.05,1.05);
}
.preview{
width: 180upx;
height: 60upx;
}
.value{
margin: 0 40upx;
flex-grow: 1;
}
.ok{
width: 160upx;
height: 60upx;
line-height: 60upx;
text-align: center;
background-color: #ff9933;
color: #fff;
border-radius: 4px;
letter-spacing: 3px;
font-size: 32upx;
}
.ok:active{
background-color: rgb(255, 107, 34);
}
</style>

View File

@@ -0,0 +1,97 @@
<template>
<view>
<uni-popup ref="confirmRef" type="bottom" @maskClick="handleClose">
<JMainCard pd="0" wrapPd="30rpx">
<view class="content bgF2 bdR10">{{ confirmText }}</view>
<view class="confirm" hover-class="u-cell-hover" hover-stay-time="150" @tap="handleConfirm">确认</view>
</JMainCard>
<JButton pd="0 30rpx 30rpx 30rpx" pdTop="0" bgColor="#fff" color="#000" @HandleTouch="handleClose()"
>取消</JButton
>
</uni-popup>
</view>
</template>
<script setup>
import JMainCard from "@/components/newComponents/JMainCard/JMainCard"
import JButton from "@/components/newComponents/JButton/JButton"
/*
用于二次确认弹窗
参数1 确认按钮回调函数
参数2 取消按钮回调函数
参数3 弹框内部提示文字
*/
import { ref } from "vue"
import useBackPress from "@/hooks/useBackPress.js"
const confirmRef = ref()
let confirmText = ref("")
let confirmPop = () => {}
let cancelPop = () => {}
// 处理开启弹窗 与 关闭弹窗 的 阻断返回
const popupopened = () => {
// #ifdef H5 || APP-PLUS
active()
// #endif
}
const popupclosed = () => {
// #ifdef H5 || APP-PLUS
inactive()
// #endif
}
const closeEd = () => {
confirmRef.value.close()
}
// #ifdef H5 || APP-PLUS
const { active, inactive } = useBackPress(closeEd) // onBackPress 阻断返回
// #endif
// 确认按钮
const handleConfirm = () => {
popupclosed()
confirmPop()
confirmRef.value.close()
}
// 取消按钮
const handleClose = () => {
popupclosed()
cancelPop()
confirmRef.value.close()
}
// 开启弹窗,供父组件调用传递函数
const comfirmOpen = (confirm = () => {}, tipText = "您确认要改变状态吗?", cancel = () => {}) => {
popupopened()
confirmPop = confirm
cancelPop = cancel
confirmText.value = tipText
confirmRef.value.open()
}
defineExpose({ comfirmOpen })
</script>
<style scoped lang="scss">
.content {
margin: 30rpx 30rpx 10rpx;
padding: 20rpx;
font-size: 27rpx;
color: #666;
}
.confirm {
display: flex;
justify-content: center;
align-items: center;
height: 110rpx;
font-size: 33rpx;
color: #ff4343;
}
.u-cell-hover {
background-color: rgba($color: #999, $alpha: 0.1);
}
</style>

View File

@@ -0,0 +1,204 @@
<template>
<view>
<view class="picker-box animation-close" :style="{height:(vdata.isA?'232rpx':'120rpx')}">
<view class="date-filter">
<view class="date " :class="vdata.selectFlge ==item.index?'select':''" v-for="(item,index) in vdata.selectList" :key="index" @click="onSelsct(index)">
<text>{{item.text}}</text>
</view>
</view>
<view class="time-picker " >
<xp-picker @confirm="confirm" mode="ymdhi" :startTime="vdata.startDate">{{vdata.startDate}}</xp-picker>
<text></text>
<xp-picker @confirm="confirmEnd" mode="ymdhi" :startTime="vdata.endDate">{{vdata.endDate}}</xp-picker>
</view>
</view>
</view>
</template>
<script setup>
import { ref, reactive,toRaw } from 'vue'
import {getDay} from '@/util/timeInterval.js'
import { onLoad,onShow } from '@dcloudio/uni-app'
const emit = defineEmits(['confirmEnd','onSelsct','confirmStart'])
let params = {} //搜索参数
const vdata = reactive({
isA:false, //动画
selectFlge:1,
selectList:[
{index:0,value:'all',text:'全部'},
{index:1,value:'today',text:'今天'},
{index:2,value:'yesterday',text:'昨天'},
{index:3,value:'mouth',text:'本月'},
{index:4,value:'week',text:'上月'},
{index:5,value:'customer',text:'自定义'},
],
})
onLoad((option) => {
vdata.startDate =`${getDay(0,'-')} 00:00`
vdata.endDate = `${getDay(0,'-')} 23:59`
})
//时间选择器确定
const confirm =(e)=>{
vdata.startDate = e.value
// vdata.endDate = e.value
params.queryDateRange = `customDate_${vdata.startDate}:00_${vdata.endDate}:59`
console.log(params)
emit('confirmStart',params)
}
const confirmEnd=(e)=>{
vdata.endDate = e.value
params.queryDateRange = `customDate_${vdata.startDate}:00_${vdata.endDate}:59`
console.log(params)
emit('confirmEnd',params)
// getDevDetail(params)
}
const switchState = (e, item) =>{
item.state = Number(e.detail.value);
switchStatePopup.value.comfirmOpen(
() => {
$editAgent(item.agentNo, item);
},
undefined,
() => {
item.state = Number(!e.detail.value);
}
);
}
const onSelsct = (index) =>{
vdata.selectFlge = index
switch(index){
case 0:
vdata.isA = false
params.queryDateRange = ''
params.isQueryAll = true
// getDevDetail(params)
emit('onSelsct',params)
break;
case 1:
vdata.isA = false
params.queryDateRange = 'today'
delete params.isQueryAll
// getDevDetail(params)
emit('onSelsct',params)
break;
case 2:
vdata.isA = false
params.queryDateRange = 'yesterday'
// getDevDetail(params)
delete params.isQueryAll
emit('onSelsct',params)
break;
case 3:
vdata.isA = false
params.queryDateRange = 'near2now_30'
delete params.isQueryAll
// getDevDetail(params)
emit('onSelsct',params)
break;
case 4:
vdata.isA = false
params.queryDateRange = 'prevMonth'
delete params.isQueryAll
// getDevDetail(params)
emit('onSelsct',params)
break;
case 5:
vdata.isA = true
break;
}
}
</script>
<style lang="scss">
.animation-close{
transition: height 0.3s;
}
.picker-box{
width: 650rpx;
//height: 252rpx;
background-color: #FFFFFF;
margin: 0rpx auto;
border-radius: 20rpx;
overflow: hidden;
.select{
padding: 10rpx 10rpx;
border-radius: 10rpx;
background: rgba(57, 129, 255, 0.1);
font-weight: 500;
color: #3981ff;
}
.date-filter{
box-sizing: border-box;
height: 90rpx;
width: 650rpx;
margin: 0rpx auto;
margin-bottom: 0;
background-color: #FFFFFF;
// border-bottom: 2rpx solid #FAFBFC;
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 30rpx;
padding-right: 30rpx;
padding-top: 30rpx;
.date{
//width: 92rpx;
//height: 53rpx;
display: flex;
justify-content: center;
align-items: center;
padding: 10rpx 10rpx;
}
.close{
height: 40rpx;
width: 40rpx;
// background-color: #007AFF;
image{
height: 20rpx;
width: 20rpx;
transition: transform .5s;
}
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
}
}
.time-picker{
box-sizing: border-box;
width: 590rpx;
height: 72rpx;
border-radius: 10rpx;
background: #F5F6FC;
margin: 30rpx auto;
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 20rpx;
padding-right: 20rpx;
font-size: 25rpx;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,48 @@
<template>
<view class="list-null" v-if="isShow">
<view class="null-img">
<image src="/static/img/nomore.svg" mode=""></image>
</view>
<text>{{ text }}</text>
</view>
</template>
<script setup>
import { ref, watchEffect } from "vue"
const props = defineProps({
isShow: { type: Boolean, default: false },
// text: { type: String, default: '暂无更多数据' },
list: { type: Number, default: 0 },
})
let text = ref("")
watchEffect(() => {
props.list === 0 ? (text.value = "暂无数据") : (text.value = "暂无更多数据")
})
</script>
<style lang="scss">
.list-null {
height: 200rpx;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
font-weight: 500;
font-size: 23rpx;
letter-spacing: 0.05em;
text-align: left;
color: #9ea5b3;
.null-img {
margin-top: 30rpx;
height: 80rpx;
width: 124rpx;
//background-color: #2979FF;
image {
height: 100%;
width: 100%;
}
}
}
</style>

View File

@@ -0,0 +1,79 @@
<template>
<view class="content">
<view class="status-bar-box">
<view class="status-bar"></view>
</view>
<view class="navigation-box">
<view class="navigation" :class="props.postion ?'':'navigation-center'">
<text>{{props.title}}</text>
</view>
</view>
</view>
</template>
<script setup>
const props = defineProps({
title: String,
postion:{type:Boolean,default:true}
});
</script>
<style lang="scss" scoped>
.content {
position: relative;
z-index: 99;
.status-bar-box {
height: var(--status-bar-height);
width: 100%;
.status-bar {
position: fixed;
top: 0;
width: 100% !important;
height: var(--status-bar-height);
background: #F5F6FC !important;
z-index: 90;
}
}
.navigation-box {
height: 88rpx !important;
.navigation {
position: fixed;
left: var(--window-left);
right: var(--window-right);
height: 88rpx;
line-height: 88rpx;
display: flex;
justify-content: space-between;
// justify-content: center;
box-sizing: border-box;
padding: 0 36rpx;
background-color: #F5F6FC;
z-index: 90;
text {
font-size: 33rpx;
font-weight: 700;
}
}
.navigation-center {
position: fixed;
left: var(--window-left);
right: var(--window-right);
height: 88rpx;
line-height: 88rpx;
display: flex;
//justify-content: space-between;
justify-content: center;
box-sizing: border-box;
padding: 0 36rpx;
background-color: #F5F6FC;
z-index: 90;
text {
font-size: 33rpx;
font-weight: 700;
}
}
}
}
</style>

View File

@@ -0,0 +1,148 @@
<template>
<view class="search-box">
<view class="jeepay-search" @touchmove.stop.prevent="moveHandle">
<image src="/static/img/list-search.svg" alt="" /><!--放大镜-->
<input type="text" v-model="searchText" :placeholder="placeholder" placeholder-style="font-size:28rpx" @focus="focusHandle" @input="changeHandle($event)" :focus="props.isFocus">
<!-- 清空图片 -->
<image @click="clearText" src="../../static/img/clear-test.svg" class="clear-text" mode="" v-if="searchBtn && clearTextIsShow"></image>
<view class="btn">
<!-- <slot v-if="!searchBtn" name="option"></slot> -->
<view v-if="searchBtn" @click="searchHandle" class="search-text">
搜索
</view>
</view>
</view>
<slot v-if="!searchBtn" name="option" class="slot"></slot>
</view>
<view class="mark" @click="hiddenMask" v-show="searchBtn" @touchmove.stop.prevent="moveHandle">
</view>
</template>
<script setup>
import { ref, nextTick, watch } from 'vue'
import { onShow } from '@dcloudio/uni-app'
const props = defineProps({
value: {type: String, default: null},
placeholder: {type: String, default: '搜索内容'},
isFocus:{type: Boolean, default: false}
})
let searchText = ref('') // 搜索内容
let clearTextIsShow = ref(false) // 清空按钮是否展示
// 搜索按钮
let searchBtn = ref(false)
// 获得焦点
const focusHandle = () => {
searchBtn.value = true
emit('focusHandle')
}
function hiddenMask() {
searchBtn.value = false
}
onShow(() => {
hiddenMask();
})
//监听搜索框中有无内容
watch( () => searchText.value, () => {
if(!searchText.value) {
clearTextIsShow.value = false
} else {
clearTextIsShow.value = true
}
})
watch( () => props.value, () => {
if(props.value === ''){
searchText.value = ''
}
})
// 禁止页码滚动
const moveHandle = () => {}
const emit = defineEmits(['update:value', 'searchHandle','focusHandle','backPressHandel','orderBackPressHandel'])
const changeHandle = (e) => emit('update:value', e.detail.value)
const clearText = () => {
searchText.value = ''
emit('update:value', '')
}
const searchHandle = () => {
searchBtn.value = false
emit('searchHandle')
}
const backPressHandel = ()=>{
if(searchBtn.value){
searchBtn.value = false
return true
}
return false
}
const orderBackPressHandel = ()=>{
if(searchBtn.value){
searchBtn.value = false
}
}
defineExpose({ backPressHandel,orderBackPressHandel });
</script>
<style scoped lang="scss">
.mark {
position: absolute;
width: 100%;
height: calc(100vh - 180rpx);
top: 120rpx;
left: 0;
z-index:99;
background-color: rgba(0,0,0, 0.5);
}
.search-box{
display: flex;
justify-content: space-between;
align-items: center;
background-color: #f2f2f2;
border-bottom: 1rpx solid #e6e6e6;
.jeepay-search {
display: flex;
padding: 30rpx;
box-sizing: border-box;
align-items:center;
position: relative;
height: 70rpx;
width: 100%;
background-color: #FFF;
margin: 20rpx 30rpx 30rpx;
border-radius: 12rpx;
image {
width: 26rpx;
height: 26rpx;
margin-right: 25rpx;
}
input {
flex-grow: 1;
margin-right: 20rpx;
}
.btn{
display: flex;
align-items: center;
justify-content: space-between;
}
}
.search-text {
color: $uni-color-primary;
width: 90rpx;
}
.clear-text {
width: 20rpx;
height: 20rpx;
}
}
</style>

View File

@@ -0,0 +1,91 @@
<template>
<view class="b-wrapper" :class="[buttonSize()]" :style="{ paddingBottom: bottom, marginTop: pdTop, padding: pd }">
<view
@tap.stop="handleTap"
class="b-main"
:style="{
color: color,
backgroundColor: bgColor,
}"
hover-class="u-cell-hover"
hover-stay-time="150"
>
<slot>登录</slot>
</view>
</view>
<view :style="{ height }" v-if="height"></view>
</template>
<script setup>
import { reactive } from "vue"
const props = defineProps({
color: {
type: String,
default: "#fff",
},
bgColor: {
type: String,
default: "$primaryColor",
},
size: {
type: String,
default: "",
},
bottom: {
type: String,
default: "75rpx",
},
pdTop: {
type: String,
default: "30rpx",
},
height: {
type: String,
default: "",
},
pd: {
type: String,
},
})
const size = reactive({
medium: "medium",
max: "max",
})
const buttonSize = () => {
return size[props.size]
}
const emits = defineEmits(["HandleTouch"])
const handleTap = () => {
emits("HandleTouch")
}
</script>
<style lang="scss" scoped>
.b-wrapper {
padding: 50rpx;
.b-main {
position: relative;
font-size: 33rpx;
height: 110rpx;
background-color: $primaryColor;
border-radius: 20rpx;
outline: 0;
border: none;
display: flex;
justify-content: center;
align-items: center;
font-size: 33rpx;
}
.u-cell-hover {
opacity: 0.5;
}
}
.max {
padding: 50rpx;
}
.medium {
padding: 0 50rpx 50rpx 50rpx;
}
</style>

View File

@@ -0,0 +1,37 @@
<template>
<JMainCard pd="30rpx" wrapPd="0 30rpx">
<view class="card-title" @tap="confirm"> {{ title }} </view>
<view class="card-content"> <slot /> </view>
</JMainCard>
</template>
<script setup>
import JMainCard from "@/components/newComponents/JMainCard/JMainCard.vue"
const emits = defineEmits(["confirm"])
const props = defineProps({
title: {
type: String,
default: "确认退出",
},
})
const confirm = () => {
emits("confirm")
}
</script>
<style lang="scss" scoped>
.card-title {
text-align: center;
color: #ff4433;
}
.card-content {
padding: 20rpx;
margin-top: 30rpx;
background-color: #f2f2f2;
font-size: 27rpx;
line-height: 46rpx;
border-radius: 10rpx;
color: #666;
}
</style>

View File

@@ -0,0 +1,22 @@
<template>
<view class="content-wrapper" :style="{ backgroundColor: bgColor, padding: pd }"><slot /></view>
</template>
<script setup>
const props = defineProps({
bgColor: {
type: String,
default: '#fff'
},
pd: {
type: String,
default: '25rpx 50rpx 0 50rpx'
}
})
</script>
<style lang="scss" scoped>
.content-wrapper {
border-radius: 32rpx 32rpx 0 0;
}
</style>

View File

@@ -0,0 +1,74 @@
<template>
<view class="content-wrapper bdR16" :style="{ backgroundColor: bgColor, margin, color }" @tap="taps">
<view class="content-left">
<image :src="leftImg" mode="scaleToFill" />
<view class="content-title"><slot /></view>
</view>
<view class="content-right">
<image src="/static/iconImg/right-arrow.svg" mode="scaleToFill" />
</view>
</view>
<view :style="{ height }" v-if="height != ''"> </view>
</template>
<script setup>
const props = defineProps({
bgColor: {
type: String,
default: '#f7f7f7'
},
margin: {
type: String,
default: '30rpx 0 0 0'
},
leftImg: {
type: String,
default: '/static/iconImg/notice-img.svg'
},
height: {
type: String,
default: ''
},
rightImg: {
type: String,
default: '/static/iconImg/right-arrow.svg'
},
color: {
type: String,
default: '#000'
}
})
const emits = defineEmits(['HandleTouch'])
const taps = () => {
emits('HandleTouch')
}
</script>
<style lang="scss" scoped>
.content-wrapper {
padding: 40rpx 32rpx 40rpx 38rpx;
display: flex;
justify-content: space-between;
align-items: center;
.content-left {
display: flex;
align-items: center;
font-size: 33rpx;
color: #666;
image {
width: 36rpx;
height: 36rpx;
margin-right: 10rpx;
}
.content-title {
vertical-align: top;
}
}
.content-right {
image {
width: 40rpx;
height: 40rpx;
}
}
}
</style>

View File

@@ -0,0 +1,57 @@
<template>
<JPopup ref="popup" @onClose="emits('cancel', 'mask')" @change="change">
<JMainCard pd="0" wrapPd="30rpx">
<view class="content bgF2 bdR10">{{ text }}</view>
<view class="confirm" hover-class="u-cell-hover" hover-stay-time="150" @tap="confirm">确认</view>
</JMainCard>
<JButton pd="0 30rpx 30rpx 30rpx" pdTop="0" bgColor="#fff" color="#000" @HandleTouch="close()">取消</JButton>
</JPopup>
</template>
<script setup>
import { inject, ref } from "vue"
import JPopup from "@/components/newComponents/JPopup/JPopup"
import JMainCard from "@/components/newComponents/JMainCard/JMainCard"
import JButton from "@/components/newComponents/JButton/JButton"
const wrapperHidden = inject('wrapperHidden')
const emits = defineEmits(["confirm", "cancel"])
const text = ref("")
const popup = ref()
const open = (tips = "确认删除吗?一旦删除不可恢复!!!") => {
text.value = tips
popup.value.open()
}
const close = () => {
popup.value.close()
}
const confirm = () => {
emits("confirm")
popup.value.close()
}
const change = (e)=>{
if(wrapperHidden){
wrapperHidden(e.show)
}
}
defineExpose({ open })
</script>
<style lang="scss" scoped>
.content {
margin: 30rpx 30rpx 10rpx;
padding: 20rpx;
font-size: 27rpx;
color: #666;
}
.confirm {
display: flex;
justify-content: center;
align-items: center;
height: 110rpx;
font-size: 33rpx;
color: #ff4343;
}
.u-cell-hover {
background-color: rgba($color: #999, $alpha: 0.1);
}
</style>

View File

@@ -0,0 +1,99 @@
<template>
<view class="equipment-info">
<!-- 背景图片 -->
<image :src="props.bgImg" class="back-img" />
<view class="content">
<image :src="props.icon" class="code" />
<view class="name">{{ props.qrcAlias }}</view>
<view class="no">{{ props.qrcId }}</view>
<view class="edit" @tap="edit" v-if="editIsShow">
<image src="/static/equipmentImg/edit.svg" />
编辑信息
</view>
</view>
</view>
</template>
<script setup>
import { ref, reactive } from "vue"
const props = defineProps({
bgImg: { type: String, default: "" }, // 背景图片
icon: { type: String, default: "" }, // 居中的icon
qrcAlias: { type: String, default: "" }, // 标题
qrcId: { type: String, default: "" }, // iD
info: { type: Object, default: () => {} },
editIsShow: { type: Boolean, default: true }, // 是否展示编辑按钮
})
const emits = defineEmits(["editInfo"])
const edit = () => emits("editInfo")
</script>
<style scoped lang="scss">
.equipment-info {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.back-img {
position: absolute;
right: -30rpx;
top: -20rpx;
width: 100%;
height: 650rpx;
}
.content {
position: relative;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 10;
.code {
margin-top: 70rpx;
margin-bottom: 20rpx;
width: 93rpx;
height: 93rpx;
}
.name {
width: 500rpx;
margin: 0 auto;
text-align: center;
font-weight: 700;
line-height: 60rpx;
color: #ffffff;
font-size: 33rpx;
}
.no {
font-size: 25rpx;
color: rgba(255, 255, 255, 0.6);
margin-top: 12rpx;
margin-bottom: 30rpx;
}
.edit {
display: flex;
justify-content: center;
align-items: center;
background-color: #fff;
padding: 25rpx 42rpx;
border-radius: 10rpx;
margin-bottom: 50rpx;
color: #7737fe;
font-size: 28rpx;
image {
width: 26rpx;
height: 26rpx;
margin-right: 10rpx;
}
}
}
}
</style>

View File

@@ -0,0 +1,108 @@
<template>
<view class="equipment-info">
<!-- 背景图片 -->
<image src="/static/equipmentImg/big-code.svg" class="back-img" />
<view class="content">
<view class="code" @tap="down">
<image :src="info.qrcodeUrl" />
</view>
<view class="name">{{ info.qrcAlias ? info.qrcAlias : "未命名" }}</view>
<view class="no">{{ info.qrcId }}</view>
<view class="edit" @tap="edit">
<image src="/static/equipmentImg/edit.svg" />
编辑信息
</view>
</view>
</view>
</template>
<script setup>
import { ref, reactive } from "vue"
const vdata = defineProps({
info: { type: Object, default: () => {} },
})
const emits = defineEmits(["downUrl", "editInfo"])
const down = () => emits("downUrl")
const edit = () => emits("editInfo")
</script>
<style scoped lang="scss">
.equipment-info {
// position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
// overflow: hidden;
.back-img {
position: absolute;
right: -30rpx;
top: 0;
width: 100%;
height: 650rpx;
}
.content {
position: relative;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 10;
.code {
margin-top: 70rpx;
margin-bottom: 20rpx;
width: 360rpx;
height: 360rpx;
border-radius: 20rpx;
background: #ffffff;
display: flex;
justify-content: center;
align-items: center;
image {
width: 300rpx;
height: 300rpx;
}
}
.name {
width: 500rpx;
margin: 0 auto;
text-align: center;
line-height: 60rpx;
color: #ffffff;
font-size: 33rpx;
font-weight: 700;
}
.no {
font-size: 25rpx;
color: rgba(255, 255, 255, 0.6);
margin-top: 12rpx;
margin-bottom: 30rpx;
}
.edit {
display: flex;
justify-content: center;
align-items: center;
background-color: #fff;
padding: 25rpx 42rpx;
border-radius: 10rpx;
margin-bottom: 50rpx;
color: #7737fe;
font-size: 28rpx;
image {
width: 26rpx;
height: 26rpx;
margin-right: 10rpx;
}
}
}
}
</style>

View File

@@ -0,0 +1,126 @@
<template>
<view
class="wrapper"
:style="{ paddingTop: menu.top + 'px', backgroundColor: bgColor }"
:class="{ isPrimary: props.bgColor == '$primaryColor' }"
>
<!-- #ifdef APP-PLUS || H5 -->
<view class="status-bar"> </view>
<!-- #endif -->
<view
class="header-wrapper"
:style="{ backgroundColor: bgColor, color: color, height: menu.height + 'px', lineHeight: menu.height + 'px' }"
>
<view class="header-left" @tap="navBack" v-if="back">
<image :src="imgUrl" mode="scaleToFill" />
</view>
<view class="header-left logo-out" v-if="logOut" @tap="loginOut">
<image :src="imgUrl" mode="scaleToFill" />退出登录</view
>
<view class="header-text">{{ title }}</view>
</view>
</view>
<!-- <view class="wrapper-block" :style="{ backgroundColor: bgColor, paddingBottom: pdB }"></view> -->
</template>
<script setup>
import { ref, watch } from "vue"
import user from "@/hooks/user.js"
const props = defineProps({
bgColor: {
type: String,
default: "#fff",
},
title: {
type: String,
default: "首页",
},
color: {
type: String,
default: "#000",
},
pdB: {
type: String,
default: "100rpx",
},
imgUrl: {
type: String,
default: "/static/iconImg/left-arrow.svg",
},
back: {
type: Boolean,
default: true,
},
logOut: { type: Boolean, default: false },
})
const emits = defineEmits(["back"])
let menu = { top: 0, height: "" }
// #ifdef MP-WEIXIN
menu = uni.getMenuButtonBoundingClientRect()
// #endif
const navBack = () => {
emits("back")
uni.navigateBack({
fail: (err) => {},
})
}
const loginOut = () => {
user.logout()
uni.showToast({
icon: "none",
title: "退出成功",
})
}
</script>
<style lang="scss" scoped>
.wrapper {
position: sticky;
width: 100%;
left: 0;
right: 0;
top: 0;
padding-bottom: 10rpx;
z-index: 50;
.status-bar {
height: var(--status-bar-height);
width: 100%;
}
.header-wrapper {
position: relative;
height: 88rpx;
line-height: 88rpx;
text-align: center;
font-weight: 700;
.header-left {
position: absolute;
top: 50%;
left: 30rpx;
width: 50rpx;
height: 50rpx;
transform: translateY(-50%);
image {
width: 100%;
height: 100%;
}
}
.logo-out {
display: flex;
align-items: center;
width: 150rpx;
font-size: 25rpx;
image {
width: 40rpx;
height: 40rpx;
}
}
}
}
.wrapper-block {
height: var(--status-bar-height);
}
.isPrimary {
background: $primaryColor !important;
}
</style>

View File

@@ -0,0 +1,118 @@
<template>
<view
class="layout"
:class="{ first: props.isBorder }"
:style="{ padding: props.pd, backgroundColor: props.bgColor, alignItems: align }"
>
<!-- 最左侧插槽用于存放icon -->
<image :src="props.icon" v-if="props.icon" :style="{ width: props.iconSize, height: props.iconSize }" />
<view class="name" :style="{ color: props.textColor, fontSize: props.size }">{{ props.name }}</view>
<view class="input">
<slot>
<input
v-if="props.place != '' || props.value != ''"
:type="type"
:value="props.value"
placeholder-style="color:#A6A6A6;"
:placeholder="(rules != null ? '(必填)' : '') + place"
:style="{ fontSize: size }"
:maxlength="maxlength"
@input="onchange"
:focus="focus"
@focus="emits('focusShow')"
@blur="emits('focusNone')"
/>
<view v-else :style="{ fontSize: props.size, color: props.rightColor }">{{ props.right }}</view>
</slot>
</view>
<image src="/static/equipmentImg/arrow.svg" v-if="props.img" />
</view>
</template>
<script setup>
import { ref, reactive, watchEffect, watch, onDeactivated, onBeforeUnmount, onMounted } from "vue"
import { addRules, clearOneRule } from "@/hooks/rules"
const props = defineProps({
name: { type: String, default: "" }, // 左侧文字
icon: { type: String, default: "" }, // 左侧图标
iconSize: { type: String, default: "40rpx" }, // 左侧图标大小
size: { type: String, default: "33rpx" }, // 文字大小
right: { type: String, default: "" }, // 右侧文字
value: { type: String, default: "" }, // 输入框文字
place: { type: String, default: "" }, // 提示文字
bgColor: { type: String, default: "" }, // 背景颜色
isBorder: { type: Boolean, default: false }, // 是否展示上边框
borderBg: { type: String, default: "#f2f2f2" }, // 上边框颜色
img: { type: Boolean, default: false }, // 是否展示右箭头
pd: { type: String, default: "40rpx 32rpx" },
textColor: { type: String, default: "#000" }, // 左侧文字颜色
rightColor: { type: String, default: "#fff" }, // 右侧文字颜色
size: { type: String, default: "33rpx" }, // 文字大小,
type: { type: String, default: "text" }, //输入框类型
rules: { default: null },
maxlength: { type: Number },
align: { type: String }, //对齐方式
focus: { type: Boolean, default: false },
})
const borderColor = ref(props.borderBg)
const places = ref("")
const emits = defineEmits(["update:value", "focusShow", "focusNone"])
const onchange = (e) => emits("update:value", e.detail.value)
onMounted(() => {
if (props.rules !== null) {
props.rules.Msg = props.name
addRules(props.rules)
}
})
onBeforeUnmount(() => {
if (props.rules !== null) {
clearOneRule(props.rules)
}
})
</script>
<style scoped lang="scss">
page {
background-color: #f2f2f2;
}
.layout {
display: flex;
align-items: flex-start;
position: relative;
box-sizing: border-box;
.name {
white-space: nowrap;
font-size: 33rpx;
// line-height: 46rpx;
}
.input {
margin: 0 10rpx 0 20rpx;
flex-grow: 1;
text-align: right;
}
image {
flex-shrink: 0;
width: 40rpx;
height: 40rpx;
margin-right: 20rpx;
}
&::before {
content: "";
position: absolute;
top: 0;
left: 32rpx;
height: 2rpx;
width: calc(100% - 32rpx);
background-color: v-bind(borderColor);
}
}
.first {
&::before {
height: 0rpx;
}
}
</style>

View File

@@ -0,0 +1,84 @@
<template>
<view class="list-item" :class="{big: props.preBig}">
<view class="left">
<view class="back" :style="{background: props.bgColor}">
<image :src="props.icon" />
</view>
<view :style="{color: props.leftColor}">{{ props.ifName }}</view>
</view>
<view class="right">
<text class="state-color" :style="{background: props.stateColor}"></text>
<text class="state-text" :style="{color: props.rightColor}">{{ props.stateText }}</text>
<image src="/static/equipmentImg/arrow.svg" style="width:40rpx; height:40rpx;" v-if="isImg" />
</view>
</view>
</template>
<script setup>
const props = defineProps({
bgColor: { type: String, default: ''},
preBig: { type: Boolean, default: false}, // 预制颜色1
icon: { type: String, default: ''},
ifName: { type: String, default: ''},
stateColor: { type: String, default: ''},
stateText: { type: String, default: ''},
leftColor: { type: String, default: '#fff'},
rightColor: { type: String, default: 'rgba(255, 255, 255, 0.7)'},
isImg:{type:Boolean,default:true}
})
</script>
<style scoped lang="scss">
.list-item {
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
padding: 15rpx 0;
.left {
display: flex;
align-items: center;
color: #fff;
font-size: 33rpx;
}
.back {
width: 60rpx;
height: 60rpx;
border-radius: 5rpx;
margin-right: 20rpx;
display: flex;
justify-content: center;
align-items: center;
flex-shrink: 0;
image {
width: 40rpx;
height: 40rpx;
}
}
.right {
display: flex;
align-items: center;
.state-color {
width: 10rpx;
height: 10rpx;
border-radius: 50%;
margin-right: 16rpx;
}
.state-text {
font-size: 30rpx;
color: rgba(255, 255, 255, 0.7);
margin-right: 16rpx;
}
}
}
// 预制主题1
.big {
background: rgba(0, 0, 0, 0.03);
padding: 30rpx;
box-sizing: border-box;
border-radius: 20rpx;
}
</style>

View File

@@ -0,0 +1,66 @@
<template>
<view class="line" :class="{ 'border-none': props.isBorder }" :style="{ marginLeft: ml, padding: pd }">
<image :src="props.iconOn" class="icon" v-if="props.isSelect && iconOn != ''"></image>
<image :src="props.iconClose" class="icon" v-else-if="iconOn != ''"></image>
<view class="name single-text-beyond" :class="{ color: props.isSelect }">{{ props.name }}</view>
<view class="name right-text" :class="{ color: props.isSelect }">
{{ text }}
<image src="/static/equipmentImg/check.svg" class="select" v-if="props.isSelect"></image>
</view>
</view>
</template>
<script setup>
import { ref, reactive } from "vue"
const props = defineProps({
iconOn: { type: String, default: "" },
iconClose: { type: String, default: "" },
name: { type: String, default: "" },
isSelect: { type: Boolean, default: false },
isBorder: { type: Boolean, default: false },
text: { type: String },
ml: { type: String },
pd: { type: String },
})
</script>
<style scoped lang="scss">
.line {
border-top: 1rpx solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
box-sizing: border-box;
.icon {
width: 36rpx;
height: 36rpx;
flex-shrink: 0;
}
.name {
max-width: 300rpx;
color: #333333;
font-size: 33rpx;
line-height: 46rpx;
flex-grow: 1;
margin: 0 20rpx;
}
.right-text {
text-align: right;
}
.select {
width: 36rpx;
height: 36rpx;
flex-shrink: 0;
}
}
.color {
color: $primaryColor !important;
}
.border-none {
border: none;
}
</style>

View File

@@ -0,0 +1,29 @@
<template>
<view class="loading-wrapper" v-if="loadingStatus" @tap="hideLoading"></view>
</template>
<script setup>
import { loadingStatus,hideLoading } from '@/hooks/loading'
import { ref } from 'vue'
// let loadingStatus = ref(false)
// const loadingShow = () => {
// loadingStatus.value = true
// setTimeout(() => {
// console.log('执行')
// loadingStatus.value = false
// }, 1000)
// }
// defineExpose({ loadingShow })
</script>
<style lang="scss" scoped>
.loading-wrapper {
position: absolute;
top: 0;
left: 0;
z-index: 9999999999;
width: 100vw;
height: 100vh;
background-color: $primaryColor;
}
</style>

View File

@@ -0,0 +1,31 @@
<template>
<view class="card-wrapper" :style="{ padding: wrapPd }" @tap="emits('click')">
<view class="card-main bdR20" :style="{ backgroundColor: bgColor, padding: pd }">
<slot />
</view>
</view>
</template>
<script setup>
const emits = defineEmits(['click'])
const props = defineProps({
bgColor: {
type: String,
default: "#fff",
},
pd: {
type: String,
default: "50rpx",
},
wrapPd: {
type: String,
default: "50rpx",
},
})
</script>
<style scoped>
.card-main {
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,64 @@
<template>
<uni-popup ref="popup" :type="props.dir" :is-mask-click="false" mask-background-color="rgba(0,0,0,0.5)">
<view class="Mask" id="msk" :style="{ alignItems: position[type] }" @tap.stop="maskClose($event)">
<view class="wrapper" @tap.stop>
<slot></slot>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { ref, reactive } from "vue"
const emits = defineEmits(["onClose", "onOpen"])
const props = defineProps({
dir: {
type: String,
default: "bottom",
},
type: {
type: String,
default: "bottom",
},
mask: {
type: Boolean,
default: false,
},
})
const position = reactive({
bottom: "flex-end",
top: "flex-start",
center: "center",
})
const popup = ref()
const close = () => {
emits("onClose")
popup.value.close()
}
const open = () => {
emits("onOpen")
popup.value.open()
}
const maskClose = (e) => {
if (props.mask) return
if (e.target.id === "msk") return close()
}
defineExpose({ open, close })
</script>
<style lang="scss" scoped>
.Mask {
position: relative;
z-index: 999999999;
display: flex;
justify-content: center;
width: 100vw;
height: 100vh;
// background: rgba(0, 0, 0, 0.5);
// backdrop-filter: blur(30rpx);
.wrapper {
flex: 1;
}
}
</style>

View File

@@ -0,0 +1,262 @@
<template>
<view class="pre-wrapper" :class="{ 'preview-two': props.preStyle == 'one', 'preview-one': props.preStyle == 'two' }">
<view v-if="activeBox" class="active-box" :class="{ disabled: disabled, active: active }" @tap="emits('activeClick')">
<image :src="imageList[disabled ? 0 : 1]" mode="scaleToFill" />
</view>
<view
class="preview-list"
:class="{
'last-list': isLast,
'last-none': props.borderNone,
}"
@tap="emits('click')"
>
<view class="content-box">
<image :src="props.img" class="img" />
<view class="content-describe">
<text class="title single-text-beyond">{{ props.title }}
<slot name="titleTag"/>
</text>
<text>{{ props.qrcId }}</text>
</view>
<slot>
<view class="content-state">
<view class="top">
<view class="spot" :style="{ backgroundColor: props.spot }"></view>
<view class="status">{{ props.status }}</view>
</view>
<text></text>
</view>
</slot>
</view>
<!-- 组件下部具名插槽 默认填充内容图标描述号码 -->
<slot name="bottom">
<view class="info" v-if="mchName || mchNo">
<image src="@/static/equipmentImg/mch-little.svg" mode="aspectFit" class="mch-img" />
<view class="name">{{ props.mchName }}</view>
<view class="number">{{ props.mchNo }}</view>
</view>
</slot>
</view>
</view>
</template>
<script setup>
/*
列表预览组件
默认插槽放到最右边
具名插槽在下部
*/
import { ref, reactive, nextTick, watchEffect } from 'vue'
const emits = defineEmits(['activeClick', 'click'])
const vdata = reactive({
infoIsShow: false, // info板块是否展示
})
const props = defineProps({
img: { type: String, default: '/static/equipmentImg/code-open.svg' },
title: { type: String, default: '' },
qrcId: { type: String, default: '' },
spot: { type: String, default: '#fff' },
status: { type: String, default: '' },
mchName: { type: String, default: '' },
mchNo: { type: String, default: '' },
isLast: { type: Boolean, default: false }, // 最后一个列表,下划线通底
borderNone: { type: Boolean, default: false }, // 最后一个下划线隐藏
preStyle: { type: String, default: '' }, // 预制的主题颜色,在最下方
disabled: { type: Boolean, default: false }, //是否禁用默认否
active: { type: Boolean, default: false }, //是否选中默认未选中
activeBox: { type: Boolean, default: false }, // 是否显示 勾选框 默认不显示
})
const imageList = ['/static/iconImg/icon-disabled.svg', '/static/iconImg/icon-active.svg']
</script>
<style scoped lang="scss">
.pre-wrapper {
display: flex;
align-items: center;
background-color: #fff;
padding: 30rpx;
position: relative;
box-sizing: border-box;
.active-box {
flex-shrink: 0;
flex-grow: 0;
display: flex;
justify-content: center;
align-items: center;
margin-right: 30rpx;
width: 36rpx;
height: 36rpx;
border-radius: 6rpx;
border: 2rpx solid #d9d9d9;
image {
width: 100%;
height: 100%;
}
}
.active {
background-color: #2980fd;
}
.disabled {
background-color: #f2f2f2;
}
}
.preview-list {
flex: 1;
display: flex;
flex-wrap: wrap;
// 下边框线
&::after {
content: '';
position: absolute;
bottom: 0;
right: 0;
height: 1px;
width: calc(100% - 30rpx);
background-color: #d9d9d9;
transform: scaleY(0.5);
}
// 纯图片
.img {
flex-grow: 0;
flex-shrink: 0;
width: 93rpx;
height: 93rpx;
margin-right: 25rpx;
}
// 左侧内容
.content-box {
flex: 1;
display: flex;
justify-content: space-between;
.content-describe {
flex: 1;
color: #8c8c8c;
font-size: 25rpx;
// width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
line-height: 35rpx;
.title {
width: 380rpx;
font-size: 33rpx;
color: #000;
line-height: 46rpx;
}
}
}
// 右侧内容 默认为状态点以及文字
.content-state {
display: flex;
flex-direction: column;
justify-content: space-between;
.top {
display: flex;
align-items: center;
.spot {
width: 10rpx;
height: 10rpx;
border-radius: 50%;
margin-right: 16rpx;
background-color: #000;
}
.status {
font-size: 30rpx;
line-height: 46rpx;
white-space: nowrap;
color: #666;
}
}
}
// 底部样式
.info {
width: 100%;
display: flex;
justify-content: center;
padding: 20rpx;
box-sizing: border-box;
background-color: #f7f7f7;
border-radius: 10rpx;
margin-top: 20rpx;
.mch-img {
width: 40rpx;
height: 40rpx;
flex-shrink: 0;
}
.name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin: 0 10rpx;
color: #000;
font-size: 28rpx;
flex-grow: 1;
}
.number {
white-space: nowrap;
color: #8c8c8c;
font-size: 28rpx;
}
}
}
.last-list {
// 下边框线
&::after {
content: '';
position: absolute;
bottom: 0;
right: 0;
height: 1rpx;
width: 100%;
background-color: #e5e5e5;
}
}
.last-none {
// 下边框线
&::after {
height: 0;
}
}
// 预制样式1 开启状态 标题白色 id 白色0.6
.preview-one {
background: rgba(0, 0, 0, 0.1);
.content-box {
.content-describe {
color: rgba(255, 255, 255, 0.6);
.title {
color: #ffffff;
}
}
}
}
// 预制样式2 关闭状态 标题白色 id 白色0.6
.preview-two {
background: rgba(0, 0, 0, 0.1) !important;
.content-box {
.content-describe {
color: rgba(255, 255, 255, 0.5);
.title {
color: rgba(255, 255, 255, 0.5);
}
}
}
}
</style>

View File

@@ -0,0 +1,156 @@
<template>
<view v-if="vdata.dataList.length > 0">
<view class="bind-title">{{ props.title }}</view>
<view class="layout" >
<scroll-view scroll-y="true" :style="{maxHeight: props.maxH}"
@scrolltoupper="reset" @scrolltolower="refresh"
>
<view v-for="(item, index) in vdata.dataList" :key="index">
<slot :info="item" :index="index"></slot>
</view>
</scroll-view>
</view>
</view>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { onShow , onLoad } from '@dcloudio/uni-app'
const props = defineProps({
title: { type: String, default: '' },
maxH: { type: String, default: '424rpx' }, // 最高高度
pd: { type: String, default: '30rpx 50rpx' },
requestFun: { type: Function, default: () => {} },
params: { type: Object, default: () => ({}) },
flag: { type: Boolean, default: true },
isonshow: { type: Boolean, default: true },
pageSize: { type: Number, default: 10 },
})
const emits = defineEmits(['reset', 'refresh'])
const vdata = reactive({
dataList: [], // 数据列表
})
const listParams = {} // 页面传参对象 getList方法中传的参数
const queryObj = reactive({
pageNumber: 1, // 默认是1
pageSize: props.pageSize, // 默认10条可传参修改
isLoad: false, // 是否为上拉加载
titlePage: '' // 总页数
})
const getList = (listParamObj) => {
// 非下拉刷新才显示加载提示
if (queryObj.isLoad) {
uni.showLoading({ title: '加载中' })
}
// listParamObj 父组件在函数中携带的请求参数与页码页数固定参数合并到一起并且当该参数对象在调用时有具体值会将页码重置为1
let paramObj // 参数合并
if (listParamObj) {
queryObj.pageNumber = 1
Object.keys(listParamObj).forEach((item) => {
listParams[item] = listParamObj[item]
})
vdata.dataList = []
}
paramObj = Object.assign(
{
pageNumber: queryObj.pageNumber,
pageSize: queryObj.pageSize
},
props.params,
listParams
)
props.requestFun(paramObj)
.then(({ bizData }) => {
uni.hideLoading()
queryObj.titlePage = Math.ceil(bizData.total / queryObj.pageSize) // 页码总数
let data = bizData.records ? bizData.records : bizData
// 下拉刷新重置数据, 上拉加载新增数据
if (queryObj.isLoad) {
vdata.dataList.push(...data)
} else {
vdata.dataList = data
}
})
.catch((err) => uni.hideLoading())
}
// 只在onshow时调用一次 请求方法
const onShowGetList = () => {
if (!props.flag) return false
uni.showLoading({ title: '加载中' })
queryObj.pageNumber = 1 // 页码固定为1
let paramObj = Object.assign(
{
// 合并请求参数
pageNumber: 1,
pageSize: queryObj.pageSize
},
props.params
)
props.requestFun(paramObj)
.then(({ bizData }) => {
uni.hideLoading()
queryObj.titlePage = Math.ceil(bizData.total / queryObj.pageSize)
let data = bizData.records ? bizData.records : bizData
vdata.dataList = data // 由于只在onShow 中调用一次,所以数据重新赋值
})
}
onShow(() => {
if (props.isonshow) {
onShowGetList()
}
})
onLoad(() => {
if (!props.isonshow) {
onShowGetList()
}
})
// 下拉刷新
const reset = () => {
queryObj.pageNumber = 1
queryObj.isLoad = false
getList()
}
// 上拉加载
const refresh = () => {
queryObj.pageNumber++
if (queryObj.pageNumber > queryObj.titlePage) {
queryObj.pageNumber = queryObj.pageNumber
return (queryObj.isLoad = false)
}
queryObj.isLoad = true
getList()
}
</script>
<style scoped lang="scss">
.layout {
margin: 30rpx 50rpx;
}
.bind-title {
margin-top: 20rpx;
color: #fff;
text-align: center;
}
</style>

View File

@@ -0,0 +1,109 @@
<template>
<view class="search-wrapper" :style="{ padding: wrapPd }">
<view class="search-input bdR16">
<image src="/static/iconImg/icon-search.svg" mode="scaleToFill" />
<input type="text" :placeholder="place" placeholder-style="#8C8C8C" :focus="isFocus" v-model="searchText" :disabled="isDisabled" @input="change" />
<view class="search-text" v-if="searchText || flag" @tap="search">{{ text }}</view>
</view>
<slot>
<view class="search-screen" v-if="props.screen" @tap="tapScreen">
<image src="/static/iconImg/icon-filter.svg" mode="scaleToFill" />
筛选
</view>
</slot>
</view>
</template>
<script setup>
import { onMounted, ref, watch } from 'vue'
import { debounce } from '@/util/debounce'
const props = defineProps({
screen: {
type: Boolean,
default: false,
},
place: {
type: String,
default: '搜索代理商名、代理商号、联系人手机号',
},
bgColor: {
type: String,
default: '#fff',
},
wrapPd: {
type: String,
default: '30rpx',
},
isFocus: { type: Boolean, default: false },
isDisabled: { type: Boolean, default: false },
})
onMounted(() => {
focus.value = props.isFocus
})
const focus = ref(false)
const text = ref('搜索')
const searchText = ref('')
const flag = ref(false)
// 点击筛选触发回调
const emits = defineEmits(['screen', 'search', 'resetSearch'])
const tapScreen = () => {
emits('screen')
}
const change = () => {
text.value = '搜索'
}
// 点击触发事件
function search() {
flag.value = true
if (text.value === '搜索') {
if (!searchText.value.trim()) return uni.showToast({ title: '请输入搜索条件', icon: 'none' })
text.value = '取消'
emits('search', searchText.value)
} else {
searchText.value = ''
text.value = '搜索'
emits('resetSearch', 'reset')
}
}
defineExpose({ searchText })
</script>
<style lang="scss" scoped>
.search-wrapper {
display: flex;
align-items: center;
.search-input {
flex: 1;
display: flex;
align-items: center;
padding: 21rpx;
background-color: #fff;
input {
flex: 1;
font-size: 27rpx;
}
image {
margin: 0 10rpx 0 10rpx;
width: 30rpx;
height: 30rpx;
}
.search-text {
font-size: 30rxp;
padding: 0 10rpx;
color: $primaryColor;
}
}
.search-screen {
display: flex;
align-items: center;
font-size: 30rpx;
color: rgba(0, 0, 0, 0.6);
image {
width: 36rpx;
height: 36rpx;
margin: 0 15rpx;
}
}
}
</style>

View File

@@ -0,0 +1,105 @@
<template>
<view class="upload-wrapper" :style="{ paddingLeft: pdLeft }">
<view
class="upload-main"
:style="{ padding: pd, borderColor: borderBg }"
:class="[borderNone != undefined ? 'borderNone' : '']"
>
<view class="upload-title" :style="{ color: textColor, fontSize: fontSize }"><slot name="title">{{ name }}</slot> </view>
<view class="upload-img">
<JeepayUpLoad
v-if="imgUrl == ''"
@uploadSuccess="uploadSuccess"
:imgUrl="value"
@clear="clearImg"
></JeepayUpLoad>
<image v-else :src="imgUrl" @tap="previewImage([imgUrl])" mode="aspectFill" />
</view>
</view>
</view>
</template>
<script setup>
import { watch, onBeforeUnmount, ref, onMounted } from "vue"
import JeepayUpLoad from "@/components/JeepayUpLoad/JeepayUpLoad"
import { addRules, clearOneRule } from "@/hooks/rules"
import valid from "@/hooks/validate"
const props = defineProps({
value: {
type: String,
},
pd: {
type: String,
default: " 30rpx 30rpx 30rpx 0",
},
borderNone: {
type: String,
},
pdLeft: {
type: String,
default: "30rpx",
},
imgUrl: {
type: String,
default: "",
},
borderBg: {
type: String,
default: "#f2f2f2",
},
fontSize: { type: String },
textColor: {
type: String,
default: "#000",
},
name: { type: String },
rules: { default: null },
})
onMounted(() => {
if (props.rules !== null) {
props.rules.Msg = "请上传" + props.name
addRules(props.rules)
}
})
const emits = defineEmits(["update:value"])
const uploadSuccess = (res) => {
emits("update:value", res.data)
}
const previewImage = (v) => {
if (!valid.httpOrHttps(v)) return false
uni.previewImage({
indicator: "number",
loop: true,
urls: v,
})
}
const clearImg = () => {
emits("update:value", "")
}
onBeforeUnmount(() => {
if (props.rules !== null) {
clearOneRule(props.rules)
}
})
</script>
<style lang="scss" scoped>
.upload-wrapper {
padding-left: 30rpx;
font-size: 33rpx;
.upload-main {
display: flex;
justify-content: space-between;
border-top: 1rpx solid #f2f2f2;
image {
width: 150rpx;
height: 150rpx;
border-radius: 10rpx;
}
}
}
.borderNone {
border: none !important;
}
</style>

View File

@@ -0,0 +1,100 @@
<template>
<view class="login-wrapper" :style="{ padding: pd }">
<view class="login-main">
<view class="login-title" v-if="title">{{ title }}</view>
<view class="login-input" :style="{ backgroundColor: bgColor }">
<input
:password="password"
:placeholder="place"
:type="type"
placeholder-style="color: #A6A6A6;"
:value="value"
@input="change($event)"
/>
<view class="login-right"> <slot></slot> </view>
</view>
</view>
</view>
</template>
<script setup>
import { watchEffect, onBeforeUnmount, onMounted } from "vue"
import { addRules, clearOneRule } from "@/hooks/rules"
const props = defineProps({
title: {
type: String,
default: "账号",
},
place: {
type: String,
default: "请输入登录名/手机号",
},
type: {
type: String,
default: "text",
},
value: {
type: String,
default: "",
},
type: {
type: String,
default: "text",
},
pd: {
type: String,
default: "50rpx",
},
bgColor: {
type: String,
default: "#f3f2f5",
},
password: { type: Boolean, default: false },
rules: { default: null },
})
const emits = defineEmits(["update:value"])
const change = (e) => {
emits("update:value", e.detail.value)
}
onMounted(() => {
if (props.rules !== null) {
props.rules.Msg = props.title
addRules(props.rules)
}
})
onBeforeUnmount(() => {
if (props.rules !== null) {
clearOneRule(props.rules)
}
})
</script>
<style lang="scss" scoped>
.login-wrapper {
padding: 50rpx;
.login-title {
margin-left: 20rpx;
margin-bottom: 20rpx;
font-size: 30rpx;
}
.login-input {
display: flex;
justify-content: space-between;
align-items: center;
height: 110rpx;
.login-right {
align-self: center;
}
border-radius: 16rpx;
background: #f3f2f5;
input {
flex: 1;
font-size: 33rpx;
height: 110rpx;
margin-left: 32rpx;
}
}
}
</style>

View File

@@ -0,0 +1,169 @@
<template>
<view class="s-wrapper" :style="{ borderRadius: props.bdR, backgroundColor: bgColor }">
<view class="s-screening">
<block v-for="(v, i) in list" :key="v.value">
<view class="s-scr-item" :class="{ selected: i == index }" @tap="onSelect(v, i)"> {{ v.text }} </view>
</block>
</view>
<view class="theCustom" :class="{ customer: index === props.list.findIndex((v) => v.value == 'customer') }">
<view class="time-picker">
<xp-picker
ref="star"
@confirm="confirm"
@cancel="emits('close')"
@tap="isShow"
mode="ymdhi"
:startTime="time.startDate"
>{{ time.startDate }}</xp-picker
>
<text></text>
<xp-picker
ref="end"
@confirm="confirmEnd"
@cancel="emits('close')"
mode="ymdhi"
@tap="isShow"
:startTime="time.endDate"
>{{ time.endDate }}</xp-picker
>
</view>
</view>
</view>
</template>
<script setup>
import { onMounted, reactive, ref } from "vue"
import { getDay } from "@/util/timeInterval.js"
const time = ref({
startDate: `${getDay(0, "-")} 00:00`,
endDate: `${getDay(0, "-")} 23:59`,
})
const emits = defineEmits(["search", "open", "close"])
const props = defineProps({
bdR: {
type: String,
default: "20rpx",
},
bgColor: { type: String },
list: {
type: Array,
default: [
// { value: 'all', text: '全部' },
{ value: "today", text: "今天" },
{ value: "yesterday", text: "昨天" },
{ value: "currMonth", text: "本月" },
{ value: "prevMonth", text: "上月" },
{ value: "customer", text: "自定义" },
],
},
index: {
type: Number,
default: 0,
},
})
const star = ref(null)
const end = ref(null)
const scrInfo = ref(0)
const isOpen = ref(false)
const onSelect = (val, i) => {
if (val.value != "customer") {
emits("search", { val, i })
} else {
emits("search", {
val: {
text: "自定义",
value: "customer",
time: `customDate_${time.value.startDate}:00_${time.value.endDate}:00`,
},
i,
})
props.index = props.list.findIndex((v) => v.value == "customer")
isOpen.value = true
}
}
//时间选择器确定
const confirm = (e) => {
time.value.startDate = e.value
confirmEnd({ value: time.value.endDate })
emits("close")
}
const confirmEnd = (e) => {
time.value.endDate = e.value
let i = props.list.findIndex((v) => v.value == "customer")
emits("search", {
val: {
text: "自定义",
value: `customDate_${time.value.startDate}:00_${time.value.endDate}:00`,
},
i,
})
emits("close")
}
const isShow = (e) => {
if (star.value.pickerVisible || end.value.pickerVisible) {
emits("open")
} else {
emits("close")
}
}
// const endIsShow = () => {
// if (end.value.pickerVisible) {
// emits("open")
// } else {
// emits("close")
// }
// }
</script>
<style lang="scss" scoped>
.s-wrapper {
background: rgba(0, 0, 0, 0.1);
padding: 20rpx 20rpx 20rpx 20rpx;
.s-screening {
display: flex;
justify-content: space-between;
.s-scr-item {
padding: 20rpx;
text-align: center;
font-size: 28rpx;
white-space: nowrap;
border-radius: 7rpx;
color: rgba(255, 255, 255, 0.7);
}
.selected {
background-color: #fff;
color: #7737fe;
}
}
.theCustom {
height: 0;
overflow: hidden;
transition: height 0.3s ease-in;
.time-picker {
display: flex;
justify-content: space-between;
align-items: center;
height: 90rpx;
padding: 0 30rpx;
margin-top: 10rpx;
background: #fff;
font-size: 28rpx;
border-radius: 7rpx;
color: $primaryColor;
text {
width: 40rpx;
height: 2rpx;
background-color: $primaryColor;
}
}
}
.customer {
height: 100rpx;
transition: height 0.3s ease-in;
}
}
</style>

View File

@@ -0,0 +1,59 @@
<template>
<view class="phone-wrapper pd50">
<view class="phone-main bgF bdR20 t-center">
<view class="tips fs27 c333 lh46"> 为了您的账号安全请联系客服进行账号注销处理客服电话 </view>
<view class="phone-Number fb">{{ phoneNumber }} </view>
<view class="phone-button">
<view @tap="cancel">取消</view>
<view class="call-phone fb" @tap="callPhone">拨打电话</view>
</view>
</view>
</view>
</template>
<script setup>
const props = defineProps({
phoneNumber: {
type: String,
default: '186 8866 6688'
}
})
const emits = defineEmits(['cancel'])
const callPhone = () => {
uni.makePhoneCall({
phoneNumber: props.phoneNumber
})
}
const cancel = () => {
emits('cancel')
}
</script>
<style lang="scss" scoped>
.phone-main {
.tips {
padding: 50rpx 50rpx 0 50rpx;
}
.phone-Number {
margin-top: 30rpx;
font-size: 36rpx;
color: #7737fe;
}
.phone-button {
display: flex;
margin-top: 50rpx;
border-top: 1rpx solid rgb(232, 230, 230);
view {
width: 50%;
height: 110rpx;
line-height: 110rpx;
color: #8c8c8c;
font-size: 33rpx;
}
.call-phone {
border-left: 1rpx solid rgb(232, 230, 230);
color: $primaryColor;
}
}
}
</style>

View File

@@ -0,0 +1,209 @@
<template>
<view>
<uni-popup ref="publicPopup" @maskClick="closeEd()">
<view class="store-body">
<view >
<view style="height: 20rpx"></view>
<view class="search">
<input type="text" v-model="params.bankName" placeholder="请搜索银行名称" />
<view @click.stop="searchHandle()">搜索</view>
</view>
</view>
<scroll-view style="height: 800rpx;" scroll-y="true" @scrolltoupper="scrolltoupper" @scrolltolower="scrolltolower">
<view class="jee-list hide-border">
<view class="jee-list-item " v-for="(item, index) in data.dataList" @click.stop="onClick(item, index)" :key="index">
<view class="jee-list-left">{{ item.bank_alias }}</view>
<view class="jee-list-right">
<image v-if="index == data.index" src="@/static/img/select-radio.svg" mode=""></image>
<image v-else src="@/static/img/un-raido.svg" />
</view>
</view>
<jeepayListNull :list="data.dataList"></jeepayListNull>
</view>
<view class="tip-text" v-if="data.dataList.length >0">{{ data.tipText }}</view>
</scroll-view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import jeepayListNull from '@/components/jeepayListNull/jeepayListNull.vue'
import { onLoad, onShow, onBackPress } from '@dcloudio/uni-app';
import { ref, reactive } from 'vue';
import useBackPress from '@/hooks/useBackPress.js'
import { $bankCode } from '@/http/apiManager.js'
const emit = defineEmits(['bankInfo'])
const props = defineProps({
ifCode: ''
})
const data = reactive({
dataList: [],
index: -1,
tipText: '上拉加载更多',
isLoad: false // 是否为上拉加载
})
const publicPopup = ref(null);
// 请求参数
let params = {
settAccountType: '',
bankName: '',
pageSize: 50,
pageNumber: 1,
}
const closeEd = () => {
publicPopup.value.close()
// #ifdef H5 || APP-PLUS
inactive()
// #endif
}
// #ifdef H5 || APP-PLUS
const { active, inactive } = useBackPress(closeEd) // onBackPress 阻断返回
// #endif
const openHandle = type => {
// #ifdef H5 || APP-PLUS
active()
// #endif
params.settAccountType = type
publicPopup.value.open('bottom')
// 每次一打开请求参数重置params
params.pageNumber = 1
getList() // 请求列表数据
}
defineExpose({ openHandle })
const getList = () => {
data.tipText = '加载中';
$bankCode(props.ifCode ,params).then(({ bizData }) => {
data.titlePage = Math.ceil(bizData.total / params.pageSize); // 总页数
data.titlePage == params.pageNumber && data.titlePage != 1 ? (data.tipText = '暂无更多') : false;
if (bizData.records) data.tipText = '暂无更多';
if (data.isLoad) {
data.dataList.push(...bizData.records);
} else {
data.dataList = bizData.records;
}
})
}
// 搜索按钮
const searchHandle = () => {
params.pageNumber = 1
data.isLoad = false
getList()
}
// 滚动到顶部 下拉刷新
const scrolltoupper = () => {
data.isLoad = false
params.pageNumber = 1
getList()
}
// 滚动到底部 上拉加载
const scrolltolower = () => {
params.pageNumber++;
if (params.pageNumber > data.titlePage) {
params.pageNumber = params.pageNumber;
return data.isLoad = false
}
data.isLoad = true;
getList()
}
// 选择完毕后,将所有的信息抛出 由父组件接收
const onClick = (info, index) => {
data.index = index
emit('bankInfo', info)
closeEd()
}
</script>
<style scoped lang="scss">
.store-body {
background-color: #fff;
height: 900rpx;
border-top-left-radius: 20rpx;
border-top-right-radius: 20rpx;
}
.active {
background-color: #5476f1;
}
.search {
height: 80rpx;
background: #f5f6f7;
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: row;
border-radius: $uni-border-radius-base;
margin: 0 20rpx;
padding: 0 20rpx;
input {
width: 80%;
}
view {
width: 120rpx;
height: 60rpx;
line-height: 60rpx;
text-align: center;
border-radius: 5rpx;
color: #175be6;
background: #fff;
}
}
.jee-list {
image {
width: 40rpx;
height: 40rpx;
}
.jee-list-item {
&::after {
width: calc(100% - 70rpx);
}
}
}
.tip-text {
margin: 31rpx 54rpx;
color: #ccc;
text-align: center;
}
.btns {
padding: 0 30rpx 120rpx;
box-sizing: border-box;
display: flex;
flex-direction: row;
& > view {
padding: 24rpx 0;
text-align: center;
border-radius: $uni-border-radius-base;
font-size: 30rpx;
}
.cancel {
width: 270rpx;
color: #808080;
margin-right: 30rpx;
box-shadow: #c5c7cc 0px 0px 1px;
}
.confirm {
flex-grow: 1;
background: #3981FF;
color: #fff;
}
}
</style>

View File

@@ -0,0 +1,83 @@
<template>
<view>
<uni-popup ref="backPopupRef" type="bottom" >
<view class="bank-body">
<scroll-view class="bank-list" scroll-y="true">
<view class="item" v-for="(item, index) in props.bankList" @click="selectHandle(index)">{{ item[props.field] }}</view>
</scroll-view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import { ref, reactive, watch } from 'vue'
const props = defineProps({
bankList: {type: Array, default: () => []},
field: { type: String, default: 'bank_branch_name' }, // 默认渲染的字段
})
const backPopupRef = ref()
// 开启与关闭弹窗 供父组件调用
const open = () => backPopupRef.value.open()
const close = () => backPopupRef.value.close()
const emit = defineEmits(['bankBranchInfo'])
// 向父组件传递已选信息
const selectHandle = index => {
emit('bankBranckInfo', props.bankList[index])
close()
}
defineExpose({ open, close })
</script>
<style scoped lang="scss">
.bank-body {
width: 100%;
height: 70vh;
background-color: #fff;
border-top-right-radius: 20rpx;
border-top-left-radius: 20rpx;
padding: 20rpx 30rpx;
box-sizing: border-box;
position: relative;
}
.search {
width: calc(100% - 80rpx);
position: absolute;
top: 20rpx;
left: 30rpx;
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid #ddd;
border-radius: 10rpx;
padding: 5rpx 10rpx;
input {
flex-grow: 1;
margin-right: 20rpx;
}
.btn {
padding: 10rpx 30rpx;
text-align: center;
color:#fff;
background: #0041c4;
}
}
.bank-list {
padding-top: 80rpx;
overflow: scroll;
height: 90%;
.item {
padding: 10rpx 0;
}
}
</style>

View File

@@ -0,0 +1,224 @@
<template>
<view class="verification_code">
<view class="verification_code_continor">
<view
class="verification_code_item"
v-for="(item, index) in latticeNum"
:key="index"
:style="
blurShowLocal &&
(inputValues.length === index || (inputValues.length === latticeNum && index === latticeNum - 1))
? borderCheckStyle
: borderStyle
"
@tap="latticeFoc(index)"
>
<block v-if="inputValues[index]">
<view v-if="ciphertextSty == 1" class="point"></view>
<block v-else>{{ ciphertextSty == 2 ? "*" : inputValues[index] }}5</block>
</block>
</view>
</view>
<input
type="number"
:type="inputType"
focus
:maxlength="lattice"
class="input_info"
v-model="text"
@input="inputVal"
@blur="blur"
@focus="focus"
ref="input"
/>
</view>
</template>
<script>
export default {
props: {
// 输入框框框的个数
latticeNum: {
type: Number,
default: 4,
},
// 未选中样式
borderStyle: {
type: String,
default: "border:1rpx solid #DADEE6;border-radius: 12rpx;",
},
// 选中的样式
borderCheckStyle: {
type: String,
default: "border:2rpx solid #7737FE;border-radius: 12rpx;",
},
// input类型
inputType: {
type: String,
default: "number",
},
// 失去焦点后是否继续显示,当前所对焦的输入框(样式)
blurShow: {
type: Boolean,
default: false,
},
// 密文样式 1 圆点样式 2 星号 * 样式 默认为0 无密文
ciphertextSty: {
type: Number,
default: 2,
},
// 是否允许修改/填写某一个框框的值
updateOne: {
type: Boolean,
default: false,
},
},
data() {
return {
inputValues: [], //输入的值
blurShowLocal: true,
// cursor:null
lattice: 6,
keyCodeArr: [],
text: "",
}
},
mounted() {
this.blurShowLocal = this.blurShow
// if (this.updateOne) {
// let arr = [];
// for (let i = 0; i < this.latticeNum; i++) {
// arr.push(' ');
// }
// this.inputValues = arr;
// }
},
methods: {
/**
* 输入框的值
*/
inputVal(e) {
//#ifdef MP-WEIXIN
let keyCode = e.detail.keyCode
if (this.inputValues.length < 6) {
if (keyCode === 8 && this.inputValues.length > 0) {
this.inputValues.pop()
} else {
if (keyCode < 48 || keyCode > 57) {
uni.showToast({
icon: "none",
title: "请输入数字",
duration: 1000,
})
} else {
this.keyCodeArr.push(keyCode)
if (this.keyCodeArr.length < 6) {
this.lattice += 1
} else if (this.keyCodeArr.length >= 6) {
this.keyCodeArr = []
this.lattice = 6
}
let codeObj = {
48: "0",
49: "1",
50: "2",
51: "3",
52: "4",
53: "5",
54: "6",
55: "7",
56: "8",
57: "9",
}
this.inputValues.push(codeObj[keyCode])
this.$emit("getInputVerification", this.inputValues.join(""))
// console.log(this.inputValues)
}
}
} else if (keyCode === 8 && this.inputValues.length > 0) {
this.inputValues.pop()
return
}
//#endif
//#ifdef APP || H5
this.inputValues = this.text
this.$emit("getInputVerification", this.inputValues)
//#endif
},
// 设置验证码的值
/**
* verificationCodeValue 数组
*/
setVerificationCode(verificationCodeValue = []) {
this.inputValues = verificationCodeValue
},
/**
* 清空验证码的值
*/
cleanVal() {
this.inputValues = []
this.text = ""
},
latticeFoc(index) {},
blur() {
!this.blurShow ? (this.blurShowLocal = false) : ""
},
focus() {
!this.blurShow ? (this.blurShowLocal = true) : ""
},
},
}
</script>
<style lang="scss">
.verification_code {
// width: 260rpx;
// margin: 20rpx auto;
position: relative;
overflow: hidden;
.verification_code_continor {
display: flex;
text-align: center;
justify-content: center;
// background-color: beige;
.verification_code_item {
box-sizing: border-box;
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
display: flex;
background-color: #fff;
// border-radius: 12rpx;
margin-top: 6rpx;
}
.verification_code_item:not(:first-child) {
margin-left: 20rpx;
}
.point {
width: 20rpx;
height: 20rpx;
background-color: black;
border-radius: 200px;
}
}
.input_info {
width: 200%;
height: 100rpx;
border: 1px solid $primaryColor;
position: absolute;
opacity: 0;
-khtml-opacity: 0;
top: -10rpx;
left: -100%;
}
}
</style>

View File

@@ -0,0 +1,105 @@
const isLeapYear = y => y % 4 == 0 && y % 100 != 0 || y % 100 == 0 && y % 400 == 0
const variables = {
y: {
text: "年",
range: [null, null]
},
m: {
text: "月",
range: [1, 12]
},
d: {
text: "日",
range: [1, 31]
},
h: {
text: "时",
range: [0, 23]
},
i: {
text: "分",
range: [0, 59]
},
s: {
text: "秒",
range: [0, 59]
}
}
export function templateFactory({
mode,
value,
yearRange
}) {
const [start, end] = yearRange
let ret = {}
for (const key of mode) {
ret[key] = variables[key]
}
if (mode.indexOf("y") !== -1) ret['y'].range = [start || 2016, end || new Date().getFullYear()]
if (mode.indexOf("d") !== -1) {
const date = getDate(value || getLocalTime(mode))
ret['d'].range = [1, date]
}
return ret
}
export function getDate(dt) {
const s = dt.substring(0, dt.lastIndexOf("-"))
let year, month
const d = new Date()
switch (s.length) {
case 0:
year = d.getFullYear()
month = d.getMonth() + 1
break;
case 2:
year = d.getFullYear()
month = parseInt(s)
break;
default:
const [y, m] = s.split("-")
year = parseInt(y)
month = parseInt(m)
break;
}
const days = [31, isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
return days[month - 1]
}
export function getLocalTime(fmt) {
if (!fmt) return null
const da = new Date()
const y = fmtNumber(da.getFullYear()),
m = fmtNumber(da.getMonth() + 1),
d = fmtNumber(da.getDate()),
h = fmtNumber(da.getHours()),
i = fmtNumber(da.getMinutes()),
s = fmtNumber(da.getSeconds())
const types = {
'y': `${y}`,
'm': `${m}`,
'd': `${d}`,
'h': `${h}`,
'i': `${i}`,
's': `${s}`,
'ym': `${y}-${m}`,
'md': `${m}-${d}`,
'hi': `${h}:${i}`,
'is': `${i}:${s}`,
'ymd': `${y}-${m}-${d}`,
'his': `${h}:${i}:${s}`,
'mdh': `${m}-${d} ${h}`,
'ymdh': `${y}-${m}-${d} ${h}`,
'mdhi': `${m}-${d} ${h}:${i}`,
'mdhis': `${m}-${d} ${h}:${m}:${s}`,
'yd':`${y}-${d}`,
'ymdhi': `${y}-${m}-${d} ${h}:${i}`,
'ymdhis': `${y}-${m}-${d} ${h}:${i}:${s}`,
}
return types[fmt]
}
export function fmtNumber(n) {
// return n.toString().padStart(2,"0")
return n > 9 ? n + "" : "0" + n
}
export function time2Timestamp(timer) {
return new Date(timer).getTime()
}

View File

@@ -0,0 +1,345 @@
<template>
<view>
<view v-if="hasSlot" @tap="show">
<slot />
</view>
<view class="xp-picker" :style="{ visibility: pickerVisible ? 'visible' : 'hidden' }">
<view
class="xp-picker-mask"
:class="{ 'xp-picker-animation': animation }"
:style="{ opacity: pickerVisible ? 0.6 : 0 }"
@tap="_cancel"
></view>
<view
class="xp-picker-container"
:class="{ 'xp-picker-container--show': pickerVisible, 'xp-picker-animation': animation }"
>
<view v-if="actionPosition === 'top'" class="xp-picker-action">
<view class="xp-picker-action--cancel" @tap="_cancel">取消</view>
<view class="xp-picker-action--confirm" @tap="_confirm">确定</view>
</view>
<view v-if="isError" class="xp-picker-error" :style="{ height: height + 'vh' }">
<text>Errorplease check your configuration</text>
<text>请检查你的配置 查看控制台错误信息</text>
</view>
<picker-view
v-else
:style="{ height: height + 'vh' }"
indicator-style="height:40px;"
:value="selected"
@change="_change"
>
<picker-view-column v-for="(k, i) in modeArr" :key="i" class="xp-picker-column">
<view class="xp-picker-list-item" v-for="(item, index) in cols[i]" :key="index">
{{ item + units[i] }}
</view>
</picker-view-column>
</picker-view>
<view v-if="actionPosition === 'bottom'" class="xp-picker-btns">
<view class="xp-button xp-button--cancel" @tap="_cancel">取消</view>
<view class="xp-button xp-button--confirm" @tap="_confirm">确定</view>
</view>
</view>
</view>
</view>
</template>
<script>
import tool from "@/util/tool.js"
import { templateFactory, getLocalTime, fmtNumber, time2Timestamp, getDate } from "./util.js"
export default {
name: "XpPicker",
data() {
return {
isError: true,
isConfirm: false,
pickerVisible: false,
template: {},
cols: [],
selected: [],
}
},
props: {
startOrEnd: {
type: String,
default: "start",
},
startTime: {
type: String,
default: "",
},
endTime: {
type: String,
default: "",
},
mode: {
type: String,
default: "ymd",
},
animation: {
type: Boolean,
default: true,
},
height: {
type: [Number, String],
default: 35,
},
"action-position": {
type: String,
default: "bottom",
},
"year-range": {
type: Array,
default: () => [2010, 2035],
},
value: {
type: String,
default: null,
},
history: {
type: Boolean,
default: false,
},
},
watch: {
mode() {
this.render()
},
},
computed: {
hasSlot() {
return !!this.$slots["default"]
},
modeArr() {
return this.mode.split("")
},
units() {
const arr = []
for (const k in this.template) {
if (this.mode.indexOf(k) !== -1) arr.push(this.template[k].text)
}
return arr
},
},
created() {
this.render()
},
methods: {
render() {
this.assert() //检查用户配置
this.template = templateFactory(this) //生成所需列 默认模板
this.initCols() //根据模板 初始化列
this.initSelected() //设置默认值
},
assert() {
if ("ymdhis".indexOf(this.mode) === -1) {
throw new Error("render errorillegal 'mode'")
}
if (getLocalTime(this.mode) == undefined) {
throw new Error("render errorthe 'mode' is not found")
}
if (this.value != null) {
const arr = this.value.split(/-|:|\s/)
if (arr.length != this.modeArr.length) {
throw new Error("render errorbecause the 'value' cannot be formatted as 'mode'")
}
}
if (this.yearRange.length !== 2) {
throw new Error("render errorbecause the length of array 'year-rang' must be 2")
}
this.isError = false
},
initCols() {
for (const k of this.mode) {
const range = this.template[k].range
this.fillCol(k, ...range)
}
},
initSelected() {
const v = this.value || getLocalTime(this.mode)
if (this.startOrEnd === "start") {
this.setSelected(this.startTime)
} else {
this.setSelected(this.endTime)
}
},
fillCol(k, s, e) {
const index = this.mode.indexOf(k)
let arr = []
for (let i = s; i <= e; i++) arr.push(fmtNumber(i))
this.$set(this.cols, index, arr)
},
//dt 时间字符串 如 '2020-02-16'
setSelected(dt) {
const arr = dt.split(/-|:|\s/)
const a = this.cols
for (let i = 0; i < a.length; i++) this.$set(this.selected, i, a[i].indexOf(arr[i]))
},
resolveCurrentDt() {
let str = ""
for (let i = 0; i < this.selected.length; i++) str += this.cols[i][this.selected[i]] + this.units[i]
let dt = str
.replace("年", "-")
.replace("月", "-")
.replace("日", " ")
.replace("时", ":")
.replace("分", ":")
.replace("秒", "")
if (!this.mode.endsWith("s")) dt = dt.substring(0, dt.length - 1)
dt = dt.split("undefined").join("00")
return dt
},
show() {
if (this.history) {
if (!this.isConfirm) this.initSelected()
} else this.initSelected()
this.pickerVisible = true
},
_confirm() {
if (!this.isError) this.$emit("confirm", this._getResult())
if (!this.isConfirm) this.isConfirm = true
this.pickerVisible = false
},
_getResult() {
const detail = {
value: this.resolveCurrentDt(),
}
const tp = time2Timestamp(detail.value)
if (!isNaN(tp)) detail.timestamp = tp
return detail
},
_cancel() {
this.$emit("cancel")
this.pickerVisible = false
},
_change(e) {
let col
const newValue = e.detail.value
for (let i = 0; i < newValue.length; i++) {
if (newValue[i] !== this.selected[i]) {
col = this.modeArr[i]
break
}
}
this.selected = newValue
const index = this.mode.indexOf("d")
if (index !== -1 && (col === "y" || col === "m")) {
const currentDt = this.resolveCurrentDt()
this.fillCol("d", 1, getDate(currentDt))
}
},
},
}
</script>
<style scoped lang="scss">
.xp-picker {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 999;
font-size: 30rpx;
}
.xp-picker-container {
position: fixed;
bottom: 0;
transform: translateY(100%);
z-index: 999;
width: 100%;
background-color: #fff;
visibility: hidden;
}
.xp-picker-container--show {
transform: translateY(0);
visibility: visible;
}
.xp-picker-mask {
z-index: 998;
width: 100%;
height: 100%;
background-color: rgb(0, 0, 0);
}
.xp-picker-animation {
transition: all 0.25s;
}
.xp-picker-error {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #ff0000;
}
.xp-picker-action {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
height: 90rpx;
padding: 0 28rpx;
box-sizing: border-box;
position: relative;
font-size: 34rpx;
border-bottom: 0.5px solid #e5e5e5;
}
.xp-picker-btns {
width: 100%;
display: flex;
justify-content: space-around;
align-items: center;
height: 160rpx;
padding: 0 20rpx;
box-sizing: border-box;
position: relative;
}
.xp-button {
height: 45px;
}
.xp-button--cancel {
width: 115px;
border-radius: 5px;
background: #fff;
border: 0.5px solid #c5c7cc;
}
.xp-button--confirm {
width: 180px;
height: 45px;
border-radius: 5px;
background: #3981ff;
border: 1.5px solid #3981ff;
color: #fff;
}
.xp-button--confirm,
.xp-button--cancel {
display: flex;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
}
.xp-picker-column {
text-align: center;
border: none;
font-size: 34rpx;
}
.xp-picker-list-item {
display: flex;
justify-content: center;
align-items: center;
height: 40px;
}
</style>

View File

@@ -0,0 +1,33 @@
/**
* app 通用设置项目
*
* @author terrfly
* @site https://www.jeequan.com
* @date 2022/04/13 15:18
*/
const appConfig = {
// 项目名称
// appName: '收银呗展业宝',
appName: '服务商',
// token取值key
tokenKey: 'iToken',
tokenVal: '', //token取值vue线程内的缓存不必要每次读取应用数据影响性能
// 环境变量相关
env: {},
// 环境变量常量
ENV_ENUM: {
DEVELOPMENT: 'development', //开发地址
TEST: 'test', // 测试地址
DEMO: 'demo', // 演示环境
PRODUCTION: 'production', // 生产环境
PRODUCTION2: 'production2' // 生产环境2
},
encryptKey: '1234567890123456' //http数据加解密的key
}
export default appConfig;

View File

@@ -0,0 +1,10 @@
商福通展业宝app项目
本地启动:
1. 将 env.development_本地请复制一份 复制一份命名为env.development.js (此文件为本地请求地址)
2. 需要引入到 HBuilder X3.4.5 + 版本 进行启动;
打包的更改:
1. 修改appConfig.js 中的命名;
2. 修改manifest.json 中的相关软件名称和版本号, 以及发布小程序相关;
3. 如果需要打包app, 需要改调整static/service.html 和static/privacy.html文件内的内容因为app打包需要首次启动时获取协议内容 此时没有网络授权,需要在文件内写死一份)

49
jeepay-ui-uapp-agent/env/config.js vendored Normal file
View File

@@ -0,0 +1,49 @@
/**
* ENV 的封装
* 由于uniapp 无法设置.env等打包属性实现线上线下多版本配置属性的无缝切换。
* process.env.NODE_ENV 将在打包环境通过编译器进行替换, 线上打包后无法获取到 process对象。 所以将配置属性统一封装到appConfig中。
*
* 环境地址环境枚举详见: appConfig.js
*
* @author terrfly
* @site https://www.jeequan.com
* @date 2021/12/16 17:57
*/
import appConfig from '../config/appConfig.js';
// uni-app 暂时不支持动态import导包app报错, 需要将所有的env全部导入 = = 、
import development from './env.development.js';
import test from './env.test.js';
import demo from './env.demo.js';
import production from './env.production.js';
//import production2 from './env.production2.js'; //当生存环境上线后需要删除该配置。 仅仅为了上线审核(目前还没有生产地址)
const allEnvMap = {
development: development,
test: test,
demo: demo,
production: production,
}
// 获取当前环境变量
// 在 HBuilderX 中,点击“运行”编译出来的代码是开发环境,点击“发行”编译出来的代码是生产环境
// ( https://uniapp.dcloud.io/worktile/running-env.html#%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E5%92%8C%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83 )
const processEnv = process.env.NODE_ENV
// 改变env环境
function changeEnv(envMode){
appConfig.env = allEnvMap[envMode || processEnv]
// // 动态导包的方式设置全局env配置项目 : 当参数不存在, 那么获取node环境
// import(`./env.${envMode || processEnv}.js`).then(module => {
// appConfig.env = module.default
// }).catch(() => {
// appConfig.env = production //当出现错误, 比如本地没有复制dev文件时 默认使用生产环境
// })
}
export default { changeEnv: changeEnv }

5
jeepay-ui-uapp-agent/env/env.demo.js vendored Normal file
View File

@@ -0,0 +1,5 @@
export default {
// 'JEEPAY_BASE_URL': 'https://p.shouyinbei.com' //支付网关URL
'JEEPAY_BASE_URL': 'https://p.shouyinbei.com' //支付网关URL
// 'JEEPAY_BASE_URL': 'https://p.rscygroup.com' //支付网关URL
}

View File

@@ -0,0 +1,5 @@
export default {
// 'JEEPAY_BASE_URL': 'https://agent.jeepay.vip' //支付网关URL
'JEEPAY_BASE_URL': 'https://p.rscygroup.com' //支付网关URL
// 'JEEPAY_BASE_URL': 'https://p.rscygroup.com' //支付网关URL
}

4
jeepay-ui-uapp-agent/env/env.test.js vendored Normal file
View File

@@ -0,0 +1,4 @@
export default {
'JEEPAY_BASE_URL': 'https://p.shouyinbei.com' //支付网关URL
// 'JEEPAY_BASE_URL': 'https://p.rscygroup.com' //支付网关URL
}

View File

@@ -0,0 +1,10 @@
{
"extEnable": true,
"extAppid":"wx5ccd98b812c1ec9b",
"directCommit": false,
"ext": {
"name": "wechat"
}
}

View File

@@ -0,0 +1,218 @@
/***
* 公共字典参数
* @param {value} 数组 ['A','B']
* @function filterDcit 过滤 需要的 字典值
* */
export function filterDcit(value) {
if (!value) return [];
let list = dictLists.filter((v) => {
if (value.includes(v.type)) return true;
});
return list;
}
/**
* @param {val} String
* @return {list} Array
* 数组筛选字典数据 模糊查询 components文件下面dict 组件使用方法
* */
export function filterDicts(val) {
if (!val) return [];
if (val.trim() == "全部") return dictLists;
let list = dictLists.filter((v) => {
if (v.type.includes(val) || v.text.includes(val)) return true;
});
return list;
}
/***
* A 商户类型
* B 账户类型
* C 判断类型
* D 结算类型
* E 银行卡类型
* F 收款类型
* G 其他类型
* K 入账标识
* L 超管类型
* M 协议类型
* N 其他类型
* */
const dictLists = [
{
type: "A0",
value: 1,
text: "个人",
},
{
type: "A1",
value: 2,
text: "个体工商户",
},
{
type: "A2",
value: 3,
text: "企业",
},
{
type: "A3",
value: 1,
text: "小微商户",
},
{
type: "A4",
value: 6,
text: "个人卖家",
},
{
type: "A5",
value: 1,
text: "小微个人",
},
{
type: "B0",
value: "B",
text: "对公",
},
{
type: "B1",
value: "C",
text: "对私",
},
{
type: "B2",
value: "D",
text: "对私非法人",
},
{
type: "B3",
value: "1",
text: "对公",
},
{
type: "B4",
value: "2",
text: "对私",
},
{
type: "B5",
value: "3",
text: "授权人账户",
},
{
type: "C0",
value: "1",
text: "开通",
},
{
type: "C1",
value: "0",
text: "不开通",
},
{
type: "C2",
value: "1",
text: "是",
},
{
type: "C3",
value: "0",
text: "否",
},
{
type: "D0",
value: "1",
text: "自动结算T1",
},
{
type: "D1",
value: "2",
text: "手动结算",
},
{
type: "D2",
value: "3",
text: "自动结算D1",
},
{
type: "D3",
value: "4",
text: "定时结算",
},
{
type: "E0",
value: "RYX",
text: "瑞银信",
},
{
type: "E1",
value: "ZF",
text: "中付",
},
{
type: "E2",
value: "COMMONS",
text: "其他",
},
{
type: "F0",
value: "alipayAccount",
text: "支付宝账号",
},
{
type: "F1",
value: "bankCard",
text: "银行卡",
},
{
type: "G0",
value: "1",
text: "实体",
},
{
type: "G1",
value: "2",
text: "虚拟",
},
{
type: "K0",
value: "0",
text: "非法人入账",
},
{
type: "K1",
value: "1",
text: "法人入账",
},
{
type: "L0",
value: "SUPER",
text: "经办人",
},
{
type: "L1",
value: "LEGAL",
text: "经营者/法人",
},
{
type: "M0",
value: "0",
text: "电子协议",
},
{
type: "M1",
value: "1",
text: "纸质协议",
},
{
type: "N0",
value: "1",
text: "市",
},
{
type: "N1",
value: "2",
text: "区",
},
];

View File

@@ -0,0 +1,7 @@
export function formatData(val) {
for (const key in val) {
if (val[key] != 0 && !val[key] && !key.includes("img")) {
val[key] = "---"
}
}
}

View File

@@ -0,0 +1,6 @@
export function branch(val) {
return val * 100
}
export function horn(val) {
return (val / 100).toFixed(2)
}

View File

@@ -0,0 +1,14 @@
/*
* loading 加载页
*/
import { ref } from 'vue'
export let loadingStatus = ref(false)
export function showLoading() {
loadingStatus.value = true
}
export function hideLoading() {
console.log('执行');
loadingStatus.value = false
}

View File

@@ -0,0 +1,64 @@
import { $mapKey } from "@/http/apiManager.js"
import address from "@/components/applyJson/address.json" // 地址
/***
* 使用 Promise 风格对逆地理编码进行封装
* 入参 address 地理位置 type0地区码 1 经纬度
*
* */
export const getMapKey = (address, type) => {
return new Promise((res, rej) => {
$mapKey()
.then(({ bizData }) => {
return bizData.apiMapWebServiceKey
})
.then((key) => {
return new Promise((res, rej) => {
uni.request({
url: "https://restapi.amap.com/v3/geocode/geo", // 调用高德接口
data: {
key,
address,
},
success: ({ data }) => {
switch (type) {
case 0:
if (data.status == 1) return res({ code: data.geocodes[0].adcode, text: data.geocodes[0].district })
break
case 1:
if (data.status == 1)
return res({
lat: data.geocodes[0].location.split(",")[1],
lon: data.geocodes[0].location.split(",")[0],
flag: true,
})
break
}
console.log("res", data)
},
fail: (err) => {
console.log(err, "error")
},
})
})
})
.then((val) => {
if (val.flag) res(val)
res(inverse(val.code, val.text))
})
})
}
const inverse = (code, text) => {
let lastReg = new RegExp(code) // 将要用于正则匹配
let result = address.find((item) => {
// 获取省级信息
return lastReg.test(JSON.stringify(item))
})
let secondContent = result.children.find((item) => {
// 获取市级信息
return lastReg.test(JSON.stringify(item))
})
return { code: [result.value, secondContent.value, code], text: [result.text, secondContent.text, text].join("/") }
}

View File

@@ -0,0 +1,45 @@
import validate from "./validate"
let rulesArray = []
export function addRules(rules) {
let flag = true
rulesArray.forEach((v) => {
if (v.name === rules.name && v.Msg === rules.Msg) {
flag = false
}
})
if (flag) {
rulesArray.push(rules)
}
}
export function clearOneRule(val) {
rulesArray.forEach((v, i) => {
if (v.name === val.name || v.Msg === val.Msg) return rulesArray.splice(i, 1)
})
}
export function clearRulesArray() {
rulesArray = []
}
// 校验函数
export function validateArray(val) {
let flag = true
for (let i = 0; i < rulesArray.length; i++) {
if (!validate[rulesArray[i].rule](val[rulesArray[i].name])) {
uni.showToast({
title:
rulesArray[i].rule == "REG_NotNUll"
? rulesArray[i].Msg.includes("请上传")
? rulesArray[i].Msg
: "请输入" + rulesArray[i].Msg
: "请检查" + rulesArray[i].Msg + "格式",
icon: "none",
})
flag = false
break
} else {
flag = true
}
}
return flag
}

View File

@@ -0,0 +1 @@
import { onShow } from '@dcloudio/uni-app'

View File

@@ -0,0 +1,31 @@
import onceVar from '@/util/onceVar.js'
import { onBackPress } from '@dcloudio/uni-app'
import {nextTick} from 'vue'
/*
本函数用于阻断返回操作当页面开启图片预览与选择框时app上的返回操作会直接返回到上一级页面不符合正常的体验。 所以触发返回事件,优先关闭图片预览与其他弹窗
参数fun 为具体使用页面的关闭弹窗函数,需要在这里执行一次
onceVar 为全局唯一变量,保存在内存中。 可用vuex代替
*/
export default function (fun) {
onBackPress((option) => {
if (onceVar.imgView()) {
nextTick(() => {
fun()
inactive()
})
return true
}
return false
})
const active = () => onceVar.imgView(true)
const inactive = () => onceVar.imgView(false)
return { active, inactive }
}

View File

@@ -0,0 +1,133 @@
import {ref, reactive , toRefs, onMounted, watch} from 'vue'
import { onPullDownRefresh, onShow, onReachBottom,onLoad } from '@dcloudio/uni-app'
// requestFun: 请求函数 dataHandle: 数据处理函数 params 额外的固定的请求数据
export default function (requestFun, dataHandle = data => data, params = {} ,flag=true,onshow=true,pageSize = 10) {
// 该对象会返回给每个页面,用于取值
const useDataResult = reactive({
dataList: [], // 数据列表
listTipText: '加载更多' // 列表底部提示文字 1.加载更多 2.加载中 3.暂无更多
})
const listParams = {} // 页面传参对象 getList方法中传的参数
const queryObj = reactive({
pageNumber: 1, // 默认是1
pageSize: pageSize, // 默认10条可传参修改
isLoad: false, // 是否为上拉加载
titlePage: '', // 总页数
})
const getList = (listParamObj) => {
// 非下拉刷新才显示加载提示
if (queryObj.isLoad) {
uni.showLoading({ title:'加载中'})
}
useDataResult.listTipText = '加载中' // 列表底部提示文字 1.加载更多 2.加载中 3.暂无更多
// listParamObj 父组件在函数中携带的请求参数与页码页数固定参数合并到一起并且当该参数对象在调用时有具体值会将页码重置为1
let paramObj // 参数合并
if(listParamObj) {
queryObj.pageNumber = 1
Object.keys(listParamObj).forEach(item => {
listParams[item] = listParamObj[item]
})
useDataResult.dataList = []
}
paramObj = Object.assign({
pageNumber: queryObj.pageNumber,
pageSize: queryObj.pageSize
}, params, listParams)
requestFun(paramObj).then(({ bizData }) => {
uni.hideLoading()
queryObj.titlePage = Math.ceil(bizData.total / queryObj.pageSize) // 页码总数
if (queryObj.titlePage == queryObj.pageNumber || bizData.records.length == 0) {
useDataResult.listTipText = '暂无更多'
}
uni.stopPullDownRefresh() // 请求成功停止下拉刷新
// dataHandle 函数用于对数据的集中处理,在每个页面可自定义
let data = dataHandle(bizData.records ? bizData.records : bizData)
// 下拉刷新重置数据, 上拉加载新增数据
if (queryObj.isLoad) {
useDataResult.dataList.push(...data)
} else {
useDataResult.dataList = data
}
}).catch(err => uni.hideLoading())
}
// 只在onshow时调用一次 请求方法
const onShowGetList = () => {
if(!flag) return false
uni.showLoading({ title:'加载中'})
queryObj.pageNumber = 1 // 页码固定为1
let paramObj = Object.assign({ // 合并请求参数
pageNumber: 1,
pageSize: queryObj.pageSize
}, params)
requestFun(paramObj).then(({ bizData }) => {
uni.hideLoading()
queryObj.titlePage = Math.ceil(bizData.total / queryObj.pageSize)
if (queryObj.titlePage == queryObj.pageNumber || bizData.records.length == 0) {
useDataResult.listTipText = '暂无更多'
}
// dataHandle 函数用于对数据的集中处理,在每个页面可自定义
let data = dataHandle(bizData.records ? bizData.records : bizData)
useDataResult.dataList = data // 由于只在onShow 中调用一次,所以数据重新赋值
}).catch(err => {uni.hideLoading()})
}
onShow(() => {
if(onshow){
onShowGetList()
}
})
onLoad(()=>{
if(!onshow){
onShowGetList()
}
})
// 下拉刷新
onPullDownRefresh(() => {
queryObj.pageNumber = 1
queryObj.isLoad = false
getList()
})
// 上拉加载
onReachBottom( () => {
queryObj.pageNumber ++
if (queryObj.pageNumber > queryObj.titlePage) {
queryObj.pageNumber = queryObj.pageNumber
return queryObj.isLoad = false
}
queryObj.isLoad = true
getList()
})
return {
useDataResult, // 返回结果对象
getList // 请求函数,每个页面自定义请求参数
}
}

View File

@@ -0,0 +1,136 @@
import { ref, reactive, toRefs, onMounted, watch } from "vue"
import { onPullDownRefresh, onShow, onReachBottom, onLoad } from "@dcloudio/uni-app"
// requestFun: 请求函数 dataHandle: 数据处理函数 params 额外的固定的请求数据
export default function (config) {
const { requestFun, dataHandle = (data) => data, params = {}, flag = true, onshow = true, pageSize = 10 } = config
// 该对象会返回给每个页面,用于取值
const useDataResult = reactive({
dataList: [], // 数据列表
listTipText: "加载更多", // 列表底部提示文字 1.加载更多 2.加载中 3.暂无更多
})
const listParams = {} // 页面传参对象 getList方法中传的参数
const queryObj = reactive({
pageNumber: 1, // 默认是1
pageSize: pageSize, // 默认10条可传参修改
isLoad: false, // 是否为上拉加载
titlePage: "", // 总页数
})
const getList = (listParamObj) => {
// 非下拉刷新才显示加载提示
if (queryObj.isLoad) {
uni.showLoading({ title: "加载中" })
}
useDataResult.listTipText = "加载中" // 列表底部提示文字 1.加载更多 2.加载中 3.暂无更多
// listParamObj 父组件在函数中携带的请求参数与页码页数固定参数合并到一起并且当该参数对象在调用时有具体值会将页码重置为1
let paramObj // 参数合并
if (listParamObj) {
queryObj.pageNumber = 1
Object.keys(listParamObj).forEach((item) => {
listParams[item] = listParamObj[item]
})
useDataResult.dataList = []
}
paramObj = Object.assign(
{
pageNumber: queryObj.pageNumber,
pageSize: queryObj.pageSize,
},
params,
listParams
)
paramObj.pageNumber = queryObj.pageNumber
paramObj.pageSize = queryObj.pageSize
requestFun(paramObj)
.then(({ bizData }) => {
uni.hideLoading()
queryObj.titlePage = Math.ceil(bizData.total / queryObj.pageSize) // 页码总数
if (queryObj.titlePage == queryObj.pageNumber || bizData.records.length == 0) {
useDataResult.listTipText = "暂无更多"
}
uni.stopPullDownRefresh() // 请求成功停止下拉刷新
// dataHandle 函数用于对数据的集中处理,在每个页面可自定义
let data = dataHandle(bizData.records ? bizData.records : bizData)
// 下拉刷新重置数据, 上拉加载新增数据
if (queryObj.isLoad) {
useDataResult.dataList.push(...data)
} else {
useDataResult.dataList = data
}
})
.catch((err) => uni.hideLoading())
}
// 只在onshow时调用一次 请求方法
const onShowGetList = () => {
if (!flag) return false
uni.showLoading({ title: "加载中" })
queryObj.pageNumber = 1 // 页码固定为1
let paramObj = Object.assign(
{
// 合并请求参数
pageNumber: 1,
pageSize: queryObj.pageSize,
},
params
)
Object.assign(paramObj, listParams)
requestFun(paramObj).then(({ bizData }) => {
uni.hideLoading()
queryObj.titlePage = Math.ceil(bizData.total / queryObj.pageSize)
if (queryObj.titlePage == queryObj.pageNumber || bizData.records.length == 0) {
useDataResult.listTipText = "暂无更多"
}
// dataHandle 函数用于对数据的集中处理,在每个页面可自定义
let data = dataHandle(bizData.records ? bizData.records : bizData)
useDataResult.dataList = data // 由于只在onShow 中调用一次,所以数据重新赋值
})
}
onShow(() => {
if (onshow) {
onShowGetList()
}
})
onLoad(() => {
if (!onshow) {
onShowGetList()
}
})
// 下拉刷新
onPullDownRefresh(() => {
queryObj.pageNumber = 1
queryObj.isLoad = false
getList()
})
// 上拉加载
onReachBottom(() => {
queryObj.pageNumber++
if (queryObj.pageNumber > queryObj.titlePage) {
queryObj.pageNumber = queryObj.pageNumber
return (queryObj.isLoad = false)
}
queryObj.isLoad = true
getList()
})
return {
useDataResult, // 返回结果对象
getList, // 请求函数,每个页面自定义请求参数
}
}

View File

@@ -0,0 +1,29 @@
import { reactive } from "vue"
const store = reactive({
mchInfo: {}, // 商户信息
storeInfo: {}, // 门店信息
appInfo: {}, // 应用信息
bindMch: {}, // 商户绑定信息
team: {}, //团队信息
teamInfo: {}, //团队详情
createINfo: {}, //创建辅助终端设备需要信息
userState: {}, //注册状态
})
export default function () {
const getStore = (key) => {
return store[key]
}
const setStore = (key, value) => {
store[key] = value
}
const clearItem = (key) => {
store[key] = {}
}
return {
getStore,
setStore,
clearItem,
}
}

View File

@@ -0,0 +1,32 @@
import appConfig from '@/config/appConfig.js'
import storageManage from '@/util/storageManage.js';
import { $userInfo, $logout } from '@/http/apiManager.js'
let USER_INFO = null
const user = {
// 退出登录清空token
logout() {
// 退出登录接口
$logout()
// 清空用户信息
USER_INFO = null
// 清空token
storageManage.token('', true)
uni.reLaunch({
url: '/pages/login/login'
})
}
}
function reqUserInfo() {
return $userInfo()
}
export default user

View File

@@ -0,0 +1,37 @@
// 校验手机号
function REG_Phone(arg) {
const reg = /^1\d{10}$/
return reg.test(arg)
}
// 校验邮箱
function REG_Email(arg) {
const reg = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/
return reg.test(arg)
}
// 校验登录名
function REG_LoginName(arg) {
const reg = /^[a-zA-Z][a-zA-Z0-9]{5,19}$/
return !!arg && reg.test(arg)
}
// 非空校验
function REG_NotNUll(arg) {
return !!arg
}
// 校验货币金额 真能是正数包含小数点
function takeMoney(arg) {
const reg = /(?:^[1-9]([0-9]+)?(?:\.[0-9]{1,2})?$)|(?:^(?:0)$)|(?:^[0-9]\.[0-9](?:[0-9])?$)/
return reg.test(arg)
}
// 检验网址链接
function httpOrHttps(arg) {
const reg = /^(http|https)/
return reg.test(arg)
}
export default {
REG_Phone,
REG_Email,
REG_LoginName,
REG_NotNUll,
takeMoney,
httpOrHttps,
}

View File

@@ -0,0 +1,578 @@
/**
* api接口管理 全部以$开头
* @author terrfly
* @site https://www.jeequan.com
* @date 2021/12/17 09:57
*/
import http from "./http.js"
import appConfig from "../config/appConfig.js"
/* 密码登录 */
export function $login(data) {
return http.req("/api/anon/auth/validate", data, "POST")
}
/* 验证码登录 */
export function $phoneCodeLogin(data) {
return http.req("/api/anon/auth/phoneCode", data, "POST")
}
/* 退出登录 */
export function $logout() {
return http.req("/api/current/logout", null, "POST")
}
/* 注册发送短信验证码 */
export function $sendMessage(data) {
return http.req("/api/anon/sms/code", data, "POST")
}
/* 找回密码 */
export function $retrievePassword(data) {
return http.req("/api/anon/cipher/retrieve", data, "POST")
}
/* 修改密码 */
export function $modifyPwd(data) {
return http.req("/api/current/modifyPwd", data, "PUT")
}
// 更新支付密码
export function $updateAgentSipw(data) {
return http.req("/api/agentConfig/agentSipw", data, "PUT")
}
// 验证原支付密码是否正确
export function $isAgentSipw(data) {
return http.req("/api/agentConfig/agentSipwValidate", data, "POST")
}
// 验证是否有密码
export function $isSipw() {
return http.req("/api/agentConfig/hasSipwValidate", "GET")
}
/* 获取公司信息 */
export function $getCompanyInfo(data) {
return http.list("/api/anon/siteInfos", data)
}
/* 获取个人信息 */
export function $getUserInfo(data) {
return http.list("/api/current/user")
}
/* 修改个人信息 */
export function $modifyUser(data) {
return http.req("/api/current/user", data, "PUT")
}
/* 获取密码规则 */
export function $getPasswordRules() {
return http.req("/api/anon/cipher/pwdRulesRegexp", "GET")
}
//获取云打印机/云喇叭列表
export function $getDeviceList(data) {
return http.list("/api/store/device", data)
}
//获取云打印机/云喇叭详情
export function $getDeviceDetail(bizId) {
return http.req("/api/store/device/" + bizId, "GET")
}
//修改云打印机/云喇叭详情
export function $updateDeviceDetail(bizId, data) {
return http.updateById("/api/store/device", bizId, data)
}
//云打印机/云喇叭绑定
export function $bindDevice(bizId, data) {
return http.updateById("/api/store/device/bind", bizId, data)
}
//云打印机/云喇叭解绑
export function $unBind(bizId) {
return http.req("/api/store/device/unbind/" + bizId, undefined, "POST")
}
// 通道列表
export function $getmchApplymentList(data) {
return http.list("/api/mchApplyments", data)
}
// 隐私政策与服务协议
export function $getTreaty(data) {
return http.list("/api/anon/treaty", data)
}
// 新增门店
export function $addMchStore(data) {
return http.add("/api/mchStore", data)
}
// 修改门店
export function $updateMchStore(storeId, data) {
return http.updateById("/api/mchStore", storeId, data)
}
export function $rateConfig(infoId) {
return http.req("/api/payConfig/ifCodes", { infoId, configMode: "agentApplyment" }, "GET")
}
/* 版本检测 */
export function $versionDetection(data) {
return http.list("/api/anon/clientVersion/versionInfo", data)
}
/* 获取上传form信息 */
export function $ossFilesForm(data) {
return http.req("/api/ossFiles/form", data, "POST")
}
/** 费率配置: 获取全部接口 */
export function $getPayConfigIfcodes(infoId, configMode, ifName) {
return http.req("/api/payConfig/ifCodes", { infoId, configMode, ifName }, "GET")
}
/* 发起进件 */
export function $mchApplyments(data) {
return http.req("/api/mchApplyments", data, "POST")
}
/* 保存草稿 */
export function $updateApply(data, params) {
return http.updateById("/api/mchApplyments", data, params)
}
/* 获取个人信息 */
export function $userInfo(data) {
return http.req("/api/current/user", data, "GET")
}
/* 查询进件详情 */
export function $mchApplymentsInfo(applyId, originData) {
return http.req("/api/mchApplyments/" + applyId, originData ? { originData } : undefined)
}
/* 斗拱微信实名认证 */
export function $dgpayWxRealName(applyId, reqParams) {
return http.req("/api/mchApplyments/dgpayConfigOpen/" + applyId, reqParams, "POST")
}
/* 职能读取 */
export function $mergeApplyDetailInfos(mchNo) {
return http.req("/api/mchApplyments/mergeApplyDetailInfos/" + mchNo, "GET")
}
/* 银盛 上传图片 */
export function $yspayUpload(applyId, data) {
return http.req("/api/mchApplyments/yspayUpload/" + applyId, data, "POST")
}
/* 银盛 新增支付类型 */
export function $ysPayRateConfig(applyId, reqParams) {
return http.req("/api/mchApplyments/ysPayRateConfig/" + applyId, reqParams, "POST")
}
/* OCR图片识别 */
export function $imgInfoDetail(data) {
return http.req("/api/imgInfo/detail", data, "POST")
}
/* 统计数据页 */
export function $statistics(data) {
return http.req("/api/stat/index", data, "GET")
}
/* 获取商户列表*/
export function $getMerList(data) {
return http.list("/api/mchInfo", data)
}
/* 添加商户*/
export function $addMer(data) {
return http.req("/api/mchInfo", data, "POST")
}
/* 修改商户*/
export function $editMer(data, params) {
return http.updateById("/api/mchInfo", data, params)
}
/*获取地图key */
export function $mapKey() {
return http.req("/api/mchStore/mapConfig", "GET")
}
/* 查找商户*/
export function $findMer(data) {
return http.getById("/api/mchInfo", data)
}
/*请求静态码列表*/
export function $getQRcodeList(data) {
return http.list("/api/mchQrCodes", data)
}
/*请求静态码详情*/
export function $getQRcodeDetail(qrcId) {
return http.list("/api/mchQrCodes/" + qrcId)
}
/*更改静态码状态*/
export function $upDateQRcodeList(data, qrcId) {
return http.updateById("/api/mchQrCodes", data, qrcId)
}
/*码牌绑定门店*/
export function $qrcodeBind(data, qrcId) {
return http.updateById("/api/mchQrCodes/bind", data, qrcId)
}
/*请求代理商列表*/
export function $getAgentList(data) {
return http.list("/api/subAgents", data)
}
/*请求代理商列表*/
export function $editAgent(data, params) {
return http.updateById("/api/subAgents", data, params)
}
/* 查找代理商*/
export function $findAgent(data) {
return http.getById("/api/subAgents", data)
}
/* 添加代理商*/
export function $addAgent(data) {
return http.req("/api/subAgents", data, "POST")
}
/* 通过代理商获取统计数据*/
export function $getStatic(params) {
return http.list("/api/stat/index", params)
}
/* 订单列表 */
export function $getOrderList(data) {
return http.list("/api/payOrder", data)
}
/* 订单详情 */
export function $getPayOrder(data) {
return http.getById("/api/payOrder", data)
}
/* 退款列表 */
export function $getRefundOrder(data) {
return http.getById("/api/refundOrder", data)
}
/* 退款列表 */
export function $getRefundList(data) {
return http.list("/api/refundOrder", data)
}
/*请求码牌详情*/
export function $getQRcode(data) {
return http.getById("/api/mchQrcShells/viewByQrc", data)
}
/** 商户门店管理 **/
export function $getMchStoreList(data) {
return http.list("/api/mchStore", data)
}
/** 商户应用列表 **/
export function $getMchAppList(data) {
return http.list("/api/mchApps", data)
}
/** 团队列表 **/
export function $getTeams(data) {
return http.list("/api/userTeams", data)
}
/* 获取今天、昨天笔数统计 */
export function $getPayDayCount(data) {
return http.list("/api/mainChart/payDayCount", data)
}
/* 提现记录*/
export function $getCashout(data) {
return http.list("/api/cashout", data)
}
/* 提现记录详情 */
export function $getCashoutDetail(data) {
return http.getById("/api/cashout", data)
}
/** 进件列表 **/
export function $applyList(params) {
return http.list("/api/mchApplyments", params)
}
/* 创建团队*/
export function $addTeam(data) {
return http.req("/api/userTeams", data, "POST")
}
/** 进件渠道列表 **/
export function $payIfDefines(params) {
return http.list("/api/payIfDefines", params)
}
/** 进件前预先检查,避免重复进件 */
export function $applyPreCheck(mchNo, ifCode, isvNo) {
return http.req("/api/mchApplyments/applyPreCheck/" + mchNo + "/" + ifCode, {isvNo: isvNo}, "GET")
}
/* 查询合同信息 */
export function $getMchApplymentChannelSignInfo(applyId) {
return http.getById("/api/mchApplyments/channelSignInfo", applyId)
}
/* 开户意愿查询 */
export function $getMchApplymentWxOpenInfo(applyId) {
return http.getById("/api/mchApplyments/channelWxOpenInfo", applyId)
}
/* 添加拓展员*/
export function $addDev(data) {
return http.add("/api/sysUsers", data)
}
//拓展员详情
export function $devDetail(data) {
return http.getById("/api/sysUsers", data)
}
//修改拓展员
export function $editDev(data, params) {
return http.updateById("/api/sysUsers", data, params)
}
//拓展员列表
export function $getDev(data) {
return http.list("/api/sysUsers", data)
}
//发起提现
export function $putApplition(data) {
return http.add("/api/cashout/sett", data)
}
/* 订单详情 */
export function $getDevDetail(data) {
return http.list("/api/stat/userChart", data)
}
/** 费率配置: 获取 支付方式 */
export function $getRateConfigSavedMapData(infoId, configMode, ifCode) {
return http.req("/api/rateConfig/savedMapData", {
infoId,
configMode,
ifCode,
})
}
/** 费率配置: 获取 支付方式 */
export function $getRateConfigPayways(infoId, configMode, ifCode) {
return http.req("/api/rateConfig/payways", { infoId, configMode, ifCode })
}
//获取账户信息
export function $getAcountInfo() {
return http.req("/api/mainChart", "GET")
}
//获取提现费率
export function $getAmountFee() {
return http.req("/api/cashout/paywayFee", "GET")
}
/** 收付通进件 动态获取银行支行列表 */
export function $bankBranchList(mchNo, ifCode, params) {
return http.req("/api/mchApplyments/bankList/" + mchNo + "/" + ifCode, params)
}
/* 选择银行 */
export function $bankCode(ifCode, data) {
return http.req(`/api/mchApplyments/bankCode/${ifCode}`, data)
}
// 辅助终端设备 列表
export function $terminalList(data) {
return http.list("/api/mchTerminals", data)
}
// 辅助终端设备详情
export function $terminalInfo(id) {
return http.getById("/api/mchTerminals", id)
}
// 删除辅助终端设备
export function $delTerminal(trmId) {
return http.delById("/api/mchTerminals", trmId)
}
/** 辅助终端设备 报备信息集合 */
export function $getMchTermChannelBindInfos(trmId) {
return http.req("/api/mchTerminals/channelBindInfos/" + trmId)
}
/** 接口报备 */
export function $mchTermChannelSendup(trmId, ifCode, state) {
return http.req("/api/mchTerminals/channelSendup/" + trmId, { ifCode, state }, "POST")
}
/** 新增 辅助终端设备 */
export function $addTerminal(data) {
return http.add("/api/mchTerminals", data)
}
/** 更新 辅助终端设备 */
export function $updateTerminal(id, data) {
return http.updateById("/api/mchTerminals", id, data)
}
/** 辅助终端设备 更新默认设备 */
export function $updateMchTermDefault(trmId, defaultFlag) {
return http.req("/api/mchTerminals/trmDefaults", { trmId, defaultFlag }, "PUT")
}
/* 富友电子协议生成 */
export function $elecContractGenerate(applyId) {
return http.req("/api/mchApplyments/elecContractGenerate/" + applyId, {}, "POST")
}
/* 富友电子协议签署 */
export function $elecContractSign(applyId) {
return http.req("/api/mchApplyments/elecContractSign/" + applyId, {}, "POST")
}
/* 富友 上传图片 提交审核 */
export function $fuiouUpload(applyId) {
return http.req("/api/mchApplyments/fuiouUpload/" + applyId, {}, "POST")
}
/*公告列表*/
export function $getNoticeList(data) {
return http.list("/api/sysArticles", data)
}
//公告详情
export function $noticeDetail(data) {
return http.getById("/api/sysArticles", data)
}
/* 银联前置 新增支付类型 */
export function $payConfAdd(applyId, reqParams) {
return http.req("/api/mchApplyments/payConf/" + applyId, reqParams, "POST")
}
/*拉卡拉sass 支行列表*/
export function $lklGetBankList(areaCode, branchName) {
return http.req("/api/mchApplyments/getLklBankList/" + areaCode + '/' + branchName)
}
// 扫码登录
export function $scanCodeLogin(data) {
return http.req("/api/current/qrcode/login", data, "post")
}
// 注册
export function $register(data) {
return http.req("/api/anon/register/agentRegister", data, "post")
}
// 完善资料
export function $auditInfo(data) {
return http.req("/api/sysUsers/audit", data, "PUT")
}
// 获取用户状态
export function $getMainUserInfo() {
return http.req("/api/mainChart", "GET")
}
/* 创建团队*/
export function $editTeam(data) {
return http.req("/api/userTeams/" + data.teamId, data, "PUT")
}
// 查询门店详情
export function $storeDetails(storeId) {
return http.req("/api/mchStore/" + storeId, "GET")
}
// 删除 拓展员
export function $delSysUser(sysId) {
return http.delById("/api/sysUsers", sysId)
}
// 删除商户
export function $delMchInfo(mchId) {
return http.delById("/api/mchInfo", mchId)
}
// 删除代理商
export function $delAgent(agentId) {
return http.delById("/api/subAgents", agentId)
}
// 删除门店
export function $delStore(storeId) {
return http.delById("/api/mchStore", storeId)
}
/* 查询进件接口的最新状态 */
export function $getMchApplymentChannelState(applyId) {
return http.req("/api/mchApplyments/channelState/" + applyId, "GET")
}
/*应用列表*/
export function $getAppList(params) {
return http.list("/api/mchApps", params)
}
/*修改应用*/
export function $editApp(data) {
return http.updateById('/api/mchApps', data.appId, data)
}
/*应用详情*/
export function $getAppDetails(appId) {
return http.getById("/api/mchApps", appId)
}
/*获取应用支付配置列表*/
export function $getPayConfig(params) {
return http.list("/api/mch/payPassages", params)
}
/*获取可配置的支付渠道*/
export function $getPayChannelList(appId, wayCode) {
return http.req(`/api/mch/payPassages/availablePayInterface/${appId}/${wayCode}`, {}, "GET")
}
/*保存应用的支付渠道*/
export function $savePayConfig(data) {
return http.req('/api/mch/payPassages/mchPassage', data, "POST")
}
/*收回Or划拨接口*/
export function $allotORtakeBack(data) {
return http.req('/api/store/device/allotDevice', data, 'POST')
}
/*码牌划拨收回*/
export function $qrCodeAllotORTakeBack(data) {
return http.req('/api/mchQrCodes/allotQrc', data, 'POST')
}
/*刷脸设备*/
export function $faceList(params) {
return http.list('/api/store/device', params)
}
/*刷脸设备详情*/
export function $faceDetails(devId) {
return http.getById('/api/store/device', devId)
}
/*修改刷脸设备状态*/
export function $updateFace(data) {
return http.updateById('/api/store/device', data.deviceId, { state: data.state })
}
/* 获取广告接口*/
export function $adList(params){
return http.req('/api/advert/appAdvert', params,'GET')
}
// 获取上传图片 大小
export function $getUploadImgSize(){
return http.req('/api/defaultConfig',{},"GET")
}
// 新增商户应用
export function $addApp(data){
return http.req('/api/mchApps',data,"POST")
}
//获取渠道
export function $getIsvInfoList(data) {
data.infoType = "mch"
return http.req("/api/isvInfo/isvInfoList/",data, "GET")
}
// 请求验证码
export function $isCode () {
return http.req("/api/anon/auth/vercode", "GET")
}
export function $pmdfb (data) {
return http.req("/api/store/device/setMarqueeInfo",data, "POST")
}
export function $getImgWH (params) {
return http.req(`/api/store/device/getMarketPoint/${params}`,"GET")
}
export function $initializeImg (params) {
return http.req(`/api/store/device/getMarketPic/${params}`,"GET")
}
export function $uploadImg (data) {
return http.req(`/api/store/device/setMarketPic`,data,"POST")
}
/* 获取上传form信息 */
export function $NewOssFilesForm(data) {
return http.req("/api/ossFiles/form", data, "POST")
}

View File

@@ -0,0 +1,103 @@
/**
* HTTP的封装 基于uni.request
* 包括: 通用响应结果的处理 和 业务的增删改查函数
*
* @author terrfly
* @site https://www.jeequan.com
* @date 2021/12/16 18:35
*/
// 导入全局属性
import appConfig from "../config/appConfig.js"
import storageManage from "@/util/storageManage.js"
import { sm4DecryptByResData } from "@/util/encryptUtil.js"
function req(uri, data, method = "GET", extParams = {}) {
// 放置token
let headerObject = {}
headerObject[appConfig.tokenKey] = storageManage.token()
return uni
.request(
Object.assign(
{
url: appConfig.env.JEEPAY_BASE_URL + uri,
data: data,
method: method,
header: headerObject,
},
extParams
)
)
.then((httpData) => {
// 从http响应数据中解构响应数据 [ 响应码、 bodyData ]
let { statusCode, data } = httpData
// 避免混淆重新命名
let bodyData = data
if (statusCode == 401) {
uni.showToast({ title: "请登录1", icon: "none", mask: true, duration: 1500 })
setTimeout(function () {
uni.reLaunch({ url: "/pages/login/login" })
}, 1500)
}
// http响应码不正确
if (statusCode != 200) {
return Promise.reject(bodyData) // 跳转到catch函数
}
// 业务响应异常
if (bodyData.code != 0) {
uni.hideLoading()
uni.showToast({ icon: "none", title: bodyData.msg })
if (bodyData.code == 5005) {
// 密码已过期, 直接跳转到更改密码页面
uni.reLaunch({ url: "/pageWork/setUp/securitySetting/passwordSetting" })
}
return Promise.reject(bodyData.msg)
}
// 加密数据
if (!bodyData.data && bodyData.encryptData) {
return Promise.resolve({ bizData: sm4DecryptByResData(bodyData.encryptData), code: bodyData.code })
}
// 构造请求成功的响应数据
return Promise.resolve({ bizData: bodyData.data, code: bodyData.code })
})
.catch((res) => {
return Promise.reject(res)
})
}
// 通用list
function list(uri, params) {
return req(uri, params, "GET")
}
// 通用add
function add(uri, data) {
return req(uri, data, "POST")
}
// 通用 根据ID获取
function getById(uri, bizId) {
return req(`${uri}/${bizId}`, {}, "GET")
}
// 通用 更新
function updateById(uri, bizId, data) {
return req(`${uri}/${bizId}`, data, "PUT")
}
// 通用 删除
function delById(uri, bizId) {
return req(`${uri}/${bizId}`, {}, "DELETE")
}
export default {
req: req,
list: list,
add: add,
getById: getById,
updateById: updateById,
delById: delById,
}

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<style>
</style>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,43 @@
import App from './App'
import tool from './util/tool.js'
// 设置env配置文件
import envConfig from '@/env/config.js'
// 全局配置项
import appConfig from '@/config/appConfig.js'
import storageManage from '@/util/storageManage.js'
import JLoading from './components/newComponents/JLoading/JLoading.vue'
// import index from './pages/workbench/workbench.vue'
import { hideLoading, showLoading } from './hooks/loading'
// 设置node环境
envConfig.changeEnv(storageManage.env())
// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
console.log(app);
// app.config.globalProperties.$mpName = mpInfo.miniprogram.nickName || ''
app.config.globalProperties.$tool = tool
app.config.globalProperties.appConfig = appConfig
app.config.globalProperties.showLoading = showLoading
app.config.globalProperties.$appName = appConfig.appName
uni.$appName = appConfig.appName
app.component('JLoading', JLoading)
return {
app
}
}
// #endif

View File

@@ -0,0 +1,161 @@
{
"name" : "服务商",
"appid" : "__UNI__B6FCC53",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : 100,
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : false,
"waiting" : false,
"autoclose" : false,
"delay" : 0
},
/* */
"modules" : {
"Barcode" : {},
"Camera" : {},
"Geolocation" : {},
"Maps" : {}
},
/* */
"distribute" : {
/* android */
"android" : {
"schemes" : "jeepay",
"minSdkVersion" : 21,
"abiFilters" : [ "arm64-v8a" ],
"permissions" : [
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.INTERNET\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>"
]
},
/* ios */
"ios" : {
"privacyDescription" : {
"NSPhotoLibraryUsageDescription" : "上传进件信息需要访问相册权限,您是否同意?",
"NSPhotoLibraryAddUsageDescription" : "保存二维码信息需要访问相册权限,您是否同意?",
"NSCameraUsageDescription" : "扫描二维码或上传进件信息需要访问相机权限,您是否同意?",
"NSContactsUsageDescription" : "拨打客服电话需要访问您的通讯权限,您是否同意?",
"NSLocalNetworkUsageDescription" : "APP使用期间需要使用网络权限您是否同意"
},
"urltypes" : "jeepay",
"idfa" : false,
"dSYMs" : false
},
/* SDK */
"sdkConfigs" : {
"ad" : {},
"maps" : {
"amap" : {
"appkey_ios" : "745f90ba2a676441a8e721099cf5d9ab",
"appkey_android" : "610b4439d43686657ab3d74eea42a915"
}
},
"geolocation" : {
"amap" : {
"__platform__" : [ "ios", "android" ],
"appkey_ios" : "745f90ba2a676441a8e721099cf5d9ab",
"appkey_android" : "610b4439d43686657ab3d74eea42a915"
}
}
},
"icons" : {
"android" : {
"hdpi" : "unpackage/res/icons/72x72.png",
"xhdpi" : "unpackage/res/icons/96x96.png",
"xxhdpi" : "unpackage/res/icons/144x144.png",
"xxxhdpi" : "unpackage/res/icons/192x192.png"
},
"ios" : {
"appstore" : "unpackage/res/icons/1024x1024.png",
"ipad" : {
"app" : "unpackage/res/icons/76x76.png",
"app@2x" : "unpackage/res/icons/152x152.png",
"notification" : "unpackage/res/icons/20x20.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"proapp@2x" : "unpackage/res/icons/167x167.png",
"settings" : "unpackage/res/icons/29x29.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"spotlight" : "unpackage/res/icons/40x40.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png"
},
"iphone" : {
"app@2x" : "unpackage/res/icons/120x120.png",
"app@3x" : "unpackage/res/icons/180x180.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"notification@3x" : "unpackage/res/icons/60x60.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"settings@3x" : "unpackage/res/icons/87x87.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png",
"spotlight@3x" : "unpackage/res/icons/120x120.png"
}
}
},
"splashscreen" : {
"useOriginalMsgbox" : true,
"androidStyle" : "common",
"android" : {
"hdpi" : "E:/oem-infos/0.收银呗科技/前端打包文件/展业宝/启动图/480..png",
"xhdpi" : "E:/oem-infos/0.收银呗科技/前端打包文件/展业宝/启动图/720..png",
"xxhdpi" : "E:/oem-infos/0.收银呗科技/前端打包文件/展业宝/启动图/1080..png"
}
},
"permissionExternalStorage" : {
"request" : "none",
"prompt" : "应用保存运行状态等信息,需要获取读写手机存储(系统提示为访问设备上的照片、媒体内容和文件)权限,请允许。"
},
"permissionPhoneState" : {
"request" : "none",
"prompt" : "为保证您正常、安全地使用,需要获取设备识别码(部分手机提示为获取手机号码)使用权限,请允许。"
}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "wx5ccd98b812c1ec9b",
"setting" : {
"urlCheck" : false,
"minified" : true,
"postcss" : false
},
"optimization" : {
"subPackages" : true
},
"usingComponents" : true,
"permission" : {
"scope.userLocation" : {
"desc" : "您的位置信息将用于确定门店位置"
}
},
"requiredPrivateInfos" : [ "chooseLocation", "getLocation" ]
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "3"
}

913
jeepay-ui-uapp-agent/package-lock.json generated Normal file
View File

@@ -0,0 +1,913 @@
{
"name": "jeepay-ui-uapp-agent",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"dependencies": {
"dayjs": "^1.11.5",
"echarts": "^5.2.2",
"gm-crypto": "^0.1.8",
"js-base64": "^3.7.2",
"throttle-debounce": "^3.0.1",
"vue3-barcode": "^1.0.1",
"wxbarcode": "^1.0.2"
}
},
"node_modules/@babel/parser": {
"version": "7.17.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz",
"integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==",
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@vitejs/plugin-vue": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-1.10.2.tgz",
"integrity": "sha512-/QJ0Z9qfhAFtKRY+r57ziY4BSbGUTGsPRMpB/Ron3QPwBZM4OZAZHdTa4a8PafCwU5DTatXG8TMDoP8z+oDqJw==",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"vite": "^2.5.10"
}
},
"node_modules/@vue/compiler-core": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.30.tgz",
"integrity": "sha512-64fq1KfcR+k3Vlw+IsBM2VhV5B+2IP3YxvKU8LWCDLrkmlXtbf2eMK6+0IwX5KP41D0f1gzryIiXR7P8cB9O5Q==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.30",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.30.tgz",
"integrity": "sha512-t7arHz2SXLCXlF2fdGDFVbhENbGMez254Z5edUqb//6WXJU1lC7GvSkUE7i5x8WSjgfqt60i0V8zdmk16rvLdw==",
"dependencies": {
"@vue/compiler-core": "3.2.30",
"@vue/shared": "3.2.30"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.30.tgz",
"integrity": "sha512-P/5YpILtcQY92z72gxhkyOUPHVskEzhSrvYi91Xcr+csOxaDaYU5OqOxCzZKcf3Og70Tat404vO1OHrwprN90A==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.30",
"@vue/compiler-dom": "3.2.30",
"@vue/compiler-ssr": "3.2.30",
"@vue/reactivity-transform": "3.2.30",
"@vue/shared": "3.2.30",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7",
"postcss": "^8.1.10",
"source-map": "^0.6.1"
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.30.tgz",
"integrity": "sha512-OUh3MwAu/PsD7VN3UOdBbTkltkrUCNouSht47+CMRzpUR5+ta7+xyMAVHeq8wg4YZenWaJimbR5TL35Ka4Vk6g==",
"dependencies": {
"@vue/compiler-dom": "3.2.30",
"@vue/shared": "3.2.30"
}
},
"node_modules/@vue/reactivity": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.30.tgz",
"integrity": "sha512-qlNKbkRn2JiGxVUEdoXbLAy+vcuHUCcq+YH2uXWz0BNMvXY2plmz+oqsw+694llwmYLkke5lbdYF4DIupisIkg==",
"dependencies": {
"@vue/shared": "3.2.30"
}
},
"node_modules/@vue/reactivity-transform": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.30.tgz",
"integrity": "sha512-Le5XzCJyK3qTjoTnvQG/Ehu8fYjayauMNFyMaEnwFlm/avDofpuibpS9u+/6AgzsGnVWN+i0Jgf25bJd9DIwMw==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.30",
"@vue/shared": "3.2.30",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.30.tgz",
"integrity": "sha512-RTi7xH0Ht/6wfbo2WFBMJTEiyWFTqGhrksJm8lz6E+auO6lXZ6Eq3gPNfLt47GDWCm4xyrv+rs5R4UbarPEQ1Q==",
"dependencies": {
"@vue/reactivity": "3.2.30",
"@vue/shared": "3.2.30"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.30.tgz",
"integrity": "sha512-a3+jrncDvEFQmB+v9k0VyT4/Y3XO6OAueCroXXY4yLyr6PJeyxljweV5TzvW0rvVzH9sZO0QAvG76Lo+6C92Qw==",
"dependencies": {
"@vue/runtime-core": "3.2.30",
"@vue/shared": "3.2.30",
"csstype": "^2.6.8"
}
},
"node_modules/@vue/server-renderer": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.30.tgz",
"integrity": "sha512-pzb8J/w+JdZVOtuKFlirGqrs4GP60FXGDJySw3WV2pCetuFstaacDrnymEeSo3ohAD+Qjv7zAG+Y7OvkdxQxmQ==",
"dependencies": {
"@vue/compiler-ssr": "3.2.30",
"@vue/shared": "3.2.30"
},
"peerDependencies": {
"vue": "3.2.30"
}
},
"node_modules/@vue/shared": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.30.tgz",
"integrity": "sha512-B3HouBtUxcfu2w2d+VhdLcVBXKYYhXiFMAfQ+hoe8NUhKkPRkWDIqhpuehCZxVQ3S2dN1P1WfKGlxGC+pfmxGg=="
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/csstype": {
"version": "2.6.19",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.19.tgz",
"integrity": "sha512-ZVxXaNy28/k3kJg0Fou5MiYpp88j7H9hLZp8PDC3jV0WFjfH5E9xHb56L0W59cPbKbcHXeP4qyT8PrHp8t6LcQ=="
},
"node_modules/dayjs": {
"version": "1.11.5",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.5.tgz",
"integrity": "sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA=="
},
"node_modules/echarts": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.2.2.tgz",
"integrity": "sha512-yxuBfeIH5c+0FsoRP60w4De6omXhA06c7eUYBsC1ykB6Ys2yK5fSteIYWvkJ4xJVLQgCvAdO8C4mN6MLeJpBaw==",
"dependencies": {
"tslib": "2.3.0",
"zrender": "5.2.1"
}
},
"node_modules/esbuild": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz",
"integrity": "sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==",
"hasInstallScript": true,
"peer": true,
"bin": {
"esbuild": "bin/esbuild"
},
"optionalDependencies": {
"esbuild-android-arm64": "0.13.15",
"esbuild-darwin-64": "0.13.15",
"esbuild-darwin-arm64": "0.13.15",
"esbuild-freebsd-64": "0.13.15",
"esbuild-freebsd-arm64": "0.13.15",
"esbuild-linux-32": "0.13.15",
"esbuild-linux-64": "0.13.15",
"esbuild-linux-arm": "0.13.15",
"esbuild-linux-arm64": "0.13.15",
"esbuild-linux-mips64le": "0.13.15",
"esbuild-linux-ppc64le": "0.13.15",
"esbuild-netbsd-64": "0.13.15",
"esbuild-openbsd-64": "0.13.15",
"esbuild-sunos-64": "0.13.15",
"esbuild-windows-32": "0.13.15",
"esbuild-windows-64": "0.13.15",
"esbuild-windows-arm64": "0.13.15"
}
},
"node_modules/esbuild-windows-64": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz",
"integrity": "sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"peer": true
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"peer": true
},
"node_modules/gm-crypto": {
"version": "0.1.8",
"resolved": "https://registry.npmmirror.com/gm-crypto/-/gm-crypto-0.1.8.tgz",
"integrity": "sha512-gbTkobkbj3F70HJDQNhN9JNTvcR3O1YQJ0xGc8jTc4bZ1KuikmkjuFm5kZhyUbWxK/PwWDpPuTNyGwRYOopBLw==",
"dependencies": {
"buffer": "^5.7.0",
"jsbn": "^1.1.0",
"to-arraybuffer": "^1.0.1"
}
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"peer": true,
"dependencies": {
"function-bind": "^1.1.1"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"node_modules/is-core-module": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
"integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
"peer": true,
"dependencies": {
"has": "^1.0.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/js-base64": {
"version": "3.7.2",
"resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.2.tgz",
"integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ=="
},
"node_modules/jsbarcode": {
"version": "3.11.5",
"resolved": "https://registry.npmjs.org/jsbarcode/-/jsbarcode-3.11.5.tgz",
"integrity": "sha512-zv3KsH51zD00I/LrFzFSM6dst7rDn0vIMzaiZFL7qusTjPZiPtxg3zxetp0RR7obmjTw4f6NyGgbdkBCgZUIrA==",
"bin": {
"auto.js": "bin/barcodes/CODE128/auto.js",
"Barcode.js": "bin/barcodes/Barcode.js",
"barcodes": "bin/barcodes",
"canvas.js": "bin/renderers/canvas.js",
"checksums.js": "bin/barcodes/MSI/checksums.js",
"codabar": "bin/barcodes/codabar",
"CODE128": "bin/barcodes/CODE128",
"CODE128_AUTO.js": "bin/barcodes/CODE128/CODE128_AUTO.js",
"CODE128.js": "bin/barcodes/CODE128/CODE128.js",
"CODE128A.js": "bin/barcodes/CODE128/CODE128A.js",
"CODE128B.js": "bin/barcodes/CODE128/CODE128B.js",
"CODE128C.js": "bin/barcodes/CODE128/CODE128C.js",
"CODE39": "bin/barcodes/CODE39",
"constants.js": "bin/barcodes/ITF/constants.js",
"defaults.js": "bin/options/defaults.js",
"EAN_UPC": "bin/barcodes/EAN_UPC",
"EAN.js": "bin/barcodes/EAN_UPC/EAN.js",
"EAN13.js": "bin/barcodes/EAN_UPC/EAN13.js",
"EAN2.js": "bin/barcodes/EAN_UPC/EAN2.js",
"EAN5.js": "bin/barcodes/EAN_UPC/EAN5.js",
"EAN8.js": "bin/barcodes/EAN_UPC/EAN8.js",
"encoder.js": "bin/barcodes/EAN_UPC/encoder.js",
"ErrorHandler.js": "bin/exceptions/ErrorHandler.js",
"exceptions": "bin/exceptions",
"exceptions.js": "bin/exceptions/exceptions.js",
"fixOptions.js": "bin/help/fixOptions.js",
"GenericBarcode": "bin/barcodes/GenericBarcode",
"getOptionsFromElement.js": "bin/help/getOptionsFromElement.js",
"getRenderProperties.js": "bin/help/getRenderProperties.js",
"help": "bin/help",
"index.js": "bin/renderers/index.js",
"index.tmp.js": "bin/barcodes/index.tmp.js",
"ITF": "bin/barcodes/ITF",
"ITF.js": "bin/barcodes/ITF/ITF.js",
"ITF14.js": "bin/barcodes/ITF/ITF14.js",
"JsBarcode.js": "bin/JsBarcode.js",
"linearizeEncodings.js": "bin/help/linearizeEncodings.js",
"merge.js": "bin/help/merge.js",
"MSI": "bin/barcodes/MSI",
"MSI.js": "bin/barcodes/MSI/MSI.js",
"MSI10.js": "bin/barcodes/MSI/MSI10.js",
"MSI1010.js": "bin/barcodes/MSI/MSI1010.js",
"MSI11.js": "bin/barcodes/MSI/MSI11.js",
"MSI1110.js": "bin/barcodes/MSI/MSI1110.js",
"object.js": "bin/renderers/object.js",
"options": "bin/options",
"optionsFromStrings.js": "bin/help/optionsFromStrings.js",
"pharmacode": "bin/barcodes/pharmacode",
"renderers": "bin/renderers",
"shared.js": "bin/renderers/shared.js",
"svg.js": "bin/renderers/svg.js",
"UPC.js": "bin/barcodes/EAN_UPC/UPC.js",
"UPCE.js": "bin/barcodes/EAN_UPC/UPCE.js"
}
},
"node_modules/jsbn": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/jsbn/-/jsbn-1.1.0.tgz",
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
},
"node_modules/magic-string": {
"version": "0.25.7",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
"integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
"dependencies": {
"sourcemap-codec": "^1.4.4"
}
},
"node_modules/nanoid": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
"integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"peer": true
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
},
"node_modules/postcss": {
"version": "8.4.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz",
"integrity": "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==",
"dependencies": {
"nanoid": "^3.2.0",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
}
},
"node_modules/resolve": {
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
"integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
"peer": true,
"dependencies": {
"is-core-module": "^2.8.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/rollup": {
"version": "2.67.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.67.1.tgz",
"integrity": "sha512-1Sbcs4OuW+aD+hhqpIRl+RqooIpF6uQcfzU/QSI7vGkwADY6cM4iLsBGRM2CGLXDTDN5y/yShohFmnKegSPWzg==",
"peer": true,
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=10.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"peer": true,
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/throttle-debounce": {
"version": "3.0.1",
"resolved": "https://rg.cnpmjs.org/throttle-debounce/download/throttle-debounce-3.0.1.tgz",
"integrity": "sha1-MvlNhN+olPeGyaHykOemRbahmrs=",
"engines": {
"node": ">=10"
}
},
"node_modules/to-arraybuffer": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
"integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA=="
},
"node_modules/tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
},
"node_modules/vite": {
"version": "2.7.13",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.7.13.tgz",
"integrity": "sha512-Mq8et7f3aK0SgSxjDNfOAimZGW9XryfHRa/uV0jseQSilg+KhYDSoNb9h1rknOy6SuMkvNDLKCYAYYUMCE+IgQ==",
"peer": true,
"dependencies": {
"esbuild": "^0.13.12",
"postcss": "^8.4.5",
"resolve": "^1.20.0",
"rollup": "^2.59.0"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": ">=12.2.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
},
"peerDependencies": {
"less": "*",
"sass": "*",
"stylus": "*"
},
"peerDependenciesMeta": {
"less": {
"optional": true
},
"sass": {
"optional": true
},
"stylus": {
"optional": true
}
}
},
"node_modules/vue": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.30.tgz",
"integrity": "sha512-ZmTFWVJUX2XADkuOB8GcLTuxnBLogjJBTNVrM7WsTnjqRQ+VR8bLNrvNsbn8vj/LaP5+0WFAPrpngOYE2x+e+Q==",
"dependencies": {
"@vue/compiler-dom": "3.2.30",
"@vue/compiler-sfc": "3.2.30",
"@vue/runtime-dom": "3.2.30",
"@vue/server-renderer": "3.2.30",
"@vue/shared": "3.2.30"
}
},
"node_modules/vue3-barcode": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/vue3-barcode/-/vue3-barcode-1.0.1.tgz",
"integrity": "sha512-i9XU0iyLSLSQsWSbsmGoqiKr3kwflmyqbdmk1euITTna/f4aEbbibRRSv4rTLzriaSTGlXp3+iWxyEhimzNPBQ==",
"dependencies": {
"@vitejs/plugin-vue": "^1.6.0",
"jsbarcode": "^3.11.5",
"vue": "^3.0.4"
}
},
"node_modules/wxbarcode": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wxbarcode/-/wxbarcode-1.0.2.tgz",
"integrity": "sha1-zgLjSP8AYwHZIBd21sn1rZATFLU="
},
"node_modules/zrender": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.2.1.tgz",
"integrity": "sha512-M3bPGZuyLTNBC6LiNKXJwSCtglMp8XUEqEBG+2MdICDI3d1s500Y4P0CzldQGsqpRVB7fkvf3BKQQRxsEaTlsw==",
"dependencies": {
"tslib": "2.3.0"
}
}
},
"dependencies": {
"@babel/parser": {
"version": "7.17.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz",
"integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw=="
},
"@vitejs/plugin-vue": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-1.10.2.tgz",
"integrity": "sha512-/QJ0Z9qfhAFtKRY+r57ziY4BSbGUTGsPRMpB/Ron3QPwBZM4OZAZHdTa4a8PafCwU5DTatXG8TMDoP8z+oDqJw==",
"requires": {}
},
"@vue/compiler-core": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.30.tgz",
"integrity": "sha512-64fq1KfcR+k3Vlw+IsBM2VhV5B+2IP3YxvKU8LWCDLrkmlXtbf2eMK6+0IwX5KP41D0f1gzryIiXR7P8cB9O5Q==",
"requires": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.30",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
}
},
"@vue/compiler-dom": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.30.tgz",
"integrity": "sha512-t7arHz2SXLCXlF2fdGDFVbhENbGMez254Z5edUqb//6WXJU1lC7GvSkUE7i5x8WSjgfqt60i0V8zdmk16rvLdw==",
"requires": {
"@vue/compiler-core": "3.2.30",
"@vue/shared": "3.2.30"
}
},
"@vue/compiler-sfc": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.30.tgz",
"integrity": "sha512-P/5YpILtcQY92z72gxhkyOUPHVskEzhSrvYi91Xcr+csOxaDaYU5OqOxCzZKcf3Og70Tat404vO1OHrwprN90A==",
"requires": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.30",
"@vue/compiler-dom": "3.2.30",
"@vue/compiler-ssr": "3.2.30",
"@vue/reactivity-transform": "3.2.30",
"@vue/shared": "3.2.30",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7",
"postcss": "^8.1.10",
"source-map": "^0.6.1"
}
},
"@vue/compiler-ssr": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.30.tgz",
"integrity": "sha512-OUh3MwAu/PsD7VN3UOdBbTkltkrUCNouSht47+CMRzpUR5+ta7+xyMAVHeq8wg4YZenWaJimbR5TL35Ka4Vk6g==",
"requires": {
"@vue/compiler-dom": "3.2.30",
"@vue/shared": "3.2.30"
}
},
"@vue/reactivity": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.30.tgz",
"integrity": "sha512-qlNKbkRn2JiGxVUEdoXbLAy+vcuHUCcq+YH2uXWz0BNMvXY2plmz+oqsw+694llwmYLkke5lbdYF4DIupisIkg==",
"requires": {
"@vue/shared": "3.2.30"
}
},
"@vue/reactivity-transform": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.30.tgz",
"integrity": "sha512-Le5XzCJyK3qTjoTnvQG/Ehu8fYjayauMNFyMaEnwFlm/avDofpuibpS9u+/6AgzsGnVWN+i0Jgf25bJd9DIwMw==",
"requires": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.30",
"@vue/shared": "3.2.30",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7"
}
},
"@vue/runtime-core": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.30.tgz",
"integrity": "sha512-RTi7xH0Ht/6wfbo2WFBMJTEiyWFTqGhrksJm8lz6E+auO6lXZ6Eq3gPNfLt47GDWCm4xyrv+rs5R4UbarPEQ1Q==",
"requires": {
"@vue/reactivity": "3.2.30",
"@vue/shared": "3.2.30"
}
},
"@vue/runtime-dom": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.30.tgz",
"integrity": "sha512-a3+jrncDvEFQmB+v9k0VyT4/Y3XO6OAueCroXXY4yLyr6PJeyxljweV5TzvW0rvVzH9sZO0QAvG76Lo+6C92Qw==",
"requires": {
"@vue/runtime-core": "3.2.30",
"@vue/shared": "3.2.30",
"csstype": "^2.6.8"
}
},
"@vue/server-renderer": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.30.tgz",
"integrity": "sha512-pzb8J/w+JdZVOtuKFlirGqrs4GP60FXGDJySw3WV2pCetuFstaacDrnymEeSo3ohAD+Qjv7zAG+Y7OvkdxQxmQ==",
"requires": {
"@vue/compiler-ssr": "3.2.30",
"@vue/shared": "3.2.30"
}
},
"@vue/shared": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.30.tgz",
"integrity": "sha512-B3HouBtUxcfu2w2d+VhdLcVBXKYYhXiFMAfQ+hoe8NUhKkPRkWDIqhpuehCZxVQ3S2dN1P1WfKGlxGC+pfmxGg=="
},
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"csstype": {
"version": "2.6.19",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.19.tgz",
"integrity": "sha512-ZVxXaNy28/k3kJg0Fou5MiYpp88j7H9hLZp8PDC3jV0WFjfH5E9xHb56L0W59cPbKbcHXeP4qyT8PrHp8t6LcQ=="
},
"dayjs": {
"version": "1.11.5",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.5.tgz",
"integrity": "sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA=="
},
"echarts": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.2.2.tgz",
"integrity": "sha512-yxuBfeIH5c+0FsoRP60w4De6omXhA06c7eUYBsC1ykB6Ys2yK5fSteIYWvkJ4xJVLQgCvAdO8C4mN6MLeJpBaw==",
"requires": {
"tslib": "2.3.0",
"zrender": "5.2.1"
}
},
"esbuild": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz",
"integrity": "sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==",
"peer": true,
"requires": {
"esbuild-android-arm64": "0.13.15",
"esbuild-darwin-64": "0.13.15",
"esbuild-darwin-arm64": "0.13.15",
"esbuild-freebsd-64": "0.13.15",
"esbuild-freebsd-arm64": "0.13.15",
"esbuild-linux-32": "0.13.15",
"esbuild-linux-64": "0.13.15",
"esbuild-linux-arm": "0.13.15",
"esbuild-linux-arm64": "0.13.15",
"esbuild-linux-mips64le": "0.13.15",
"esbuild-linux-ppc64le": "0.13.15",
"esbuild-netbsd-64": "0.13.15",
"esbuild-openbsd-64": "0.13.15",
"esbuild-sunos-64": "0.13.15",
"esbuild-windows-32": "0.13.15",
"esbuild-windows-64": "0.13.15",
"esbuild-windows-arm64": "0.13.15"
}
},
"esbuild-windows-64": {
"version": "0.13.15",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz",
"integrity": "sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==",
"optional": true,
"peer": true
},
"estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"peer": true
},
"gm-crypto": {
"version": "0.1.8",
"resolved": "https://registry.npmmirror.com/gm-crypto/-/gm-crypto-0.1.8.tgz",
"integrity": "sha512-gbTkobkbj3F70HJDQNhN9JNTvcR3O1YQJ0xGc8jTc4bZ1KuikmkjuFm5kZhyUbWxK/PwWDpPuTNyGwRYOopBLw==",
"requires": {
"buffer": "^5.7.0",
"jsbn": "^1.1.0",
"to-arraybuffer": "^1.0.1"
}
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"peer": true,
"requires": {
"function-bind": "^1.1.1"
}
},
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"is-core-module": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
"integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
"peer": true,
"requires": {
"has": "^1.0.3"
}
},
"js-base64": {
"version": "3.7.2",
"resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.2.tgz",
"integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ=="
},
"jsbarcode": {
"version": "3.11.5",
"resolved": "https://registry.npmjs.org/jsbarcode/-/jsbarcode-3.11.5.tgz",
"integrity": "sha512-zv3KsH51zD00I/LrFzFSM6dst7rDn0vIMzaiZFL7qusTjPZiPtxg3zxetp0RR7obmjTw4f6NyGgbdkBCgZUIrA=="
},
"jsbn": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/jsbn/-/jsbn-1.1.0.tgz",
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
},
"magic-string": {
"version": "0.25.7",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
"integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
"requires": {
"sourcemap-codec": "^1.4.4"
}
},
"nanoid": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
"integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA=="
},
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"peer": true
},
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
},
"postcss": {
"version": "8.4.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz",
"integrity": "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==",
"requires": {
"nanoid": "^3.2.0",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
}
},
"resolve": {
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
"integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
"peer": true,
"requires": {
"is-core-module": "^2.8.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
}
},
"rollup": {
"version": "2.67.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.67.1.tgz",
"integrity": "sha512-1Sbcs4OuW+aD+hhqpIRl+RqooIpF6uQcfzU/QSI7vGkwADY6cM4iLsBGRM2CGLXDTDN5y/yShohFmnKegSPWzg==",
"peer": true,
"requires": {
"fsevents": "~2.3.2"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
},
"sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
},
"supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"peer": true
},
"throttle-debounce": {
"version": "3.0.1",
"resolved": "https://rg.cnpmjs.org/throttle-debounce/download/throttle-debounce-3.0.1.tgz",
"integrity": "sha1-MvlNhN+olPeGyaHykOemRbahmrs="
},
"to-arraybuffer": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
"integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA=="
},
"tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
},
"vite": {
"version": "2.7.13",
"resolved": "https://registry.npmjs.org/vite/-/vite-2.7.13.tgz",
"integrity": "sha512-Mq8et7f3aK0SgSxjDNfOAimZGW9XryfHRa/uV0jseQSilg+KhYDSoNb9h1rknOy6SuMkvNDLKCYAYYUMCE+IgQ==",
"peer": true,
"requires": {
"esbuild": "^0.13.12",
"fsevents": "~2.3.2",
"postcss": "^8.4.5",
"resolve": "^1.20.0",
"rollup": "^2.59.0"
}
},
"vue": {
"version": "3.2.30",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.30.tgz",
"integrity": "sha512-ZmTFWVJUX2XADkuOB8GcLTuxnBLogjJBTNVrM7WsTnjqRQ+VR8bLNrvNsbn8vj/LaP5+0WFAPrpngOYE2x+e+Q==",
"requires": {
"@vue/compiler-dom": "3.2.30",
"@vue/compiler-sfc": "3.2.30",
"@vue/runtime-dom": "3.2.30",
"@vue/server-renderer": "3.2.30",
"@vue/shared": "3.2.30"
}
},
"vue3-barcode": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/vue3-barcode/-/vue3-barcode-1.0.1.tgz",
"integrity": "sha512-i9XU0iyLSLSQsWSbsmGoqiKr3kwflmyqbdmk1euITTna/f4aEbbibRRSv4rTLzriaSTGlXp3+iWxyEhimzNPBQ==",
"requires": {
"@vitejs/plugin-vue": "^1.6.0",
"jsbarcode": "^3.11.5",
"vue": "^3.0.4"
}
},
"wxbarcode": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wxbarcode/-/wxbarcode-1.0.2.tgz",
"integrity": "sha1-zgLjSP8AYwHZIBd21sn1rZATFLU="
},
"zrender": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.2.1.tgz",
"integrity": "sha512-M3bPGZuyLTNBC6LiNKXJwSCtglMp8XUEqEBG+2MdICDI3d1s500Y4P0CzldQGsqpRVB7fkvf3BKQQRxsEaTlsw==",
"requires": {
"tslib": "2.3.0"
}
}
}
}

View File

@@ -0,0 +1,11 @@
{
"dependencies": {
"dayjs": "^1.11.5",
"echarts": "^5.2.2",
"gm-crypto": "^0.1.8",
"js-base64": "^3.7.2",
"throttle-debounce": "^3.0.1",
"vue3-barcode": "^1.0.1",
"wxbarcode": "^1.0.2"
}
}

View File

@@ -0,0 +1,222 @@
<template>
<JPopup ref="popup" dir="center" type="center">
<JMainCard v-if="take.amount">
<view class="take-header"
><image src="/static/iconImg/icon-close-X.svg" mode="scaleToFill" @tap="popup.close()" /> 请输入支付密码
</view>
<view class="take-num">
提现
<view>{{ take.amount }}</view>
</view>
<block v-for="(v, i) in takeInfo" :key="i">
<view class="take-info">
{{ v.text }}
<view class="take-value">{{ v.value }}</view>
</view>
</block>
<verificationCodeStyle
@getInputVerification="getInputVerification"
:latticeNum="6"
ciphertextSty="1"
ref="verCode"
></verificationCodeStyle>
</JMainCard>
</JPopup>
</template>
<script setup>
import { ref, reactive, onMounted, getCurrentInstance } from "vue"
import { Base64 } from "js-base64"
import { onLoad, onShow } from "@dcloudio/uni-app"
import { $getAmountFee, $getAcountInfo, $putApplition, $statistics, $isSipw } from "@/http/apiManager.js"
import { horn } from "@/hooks/handleMoney"
import JPopup from "@/components/newComponents/JPopup/JPopup"
import JMainCard from "@/components/newComponents/JMainCard/JMainCard"
import JButton from "@/components/newComponents/JButton/JButton"
import verificationCodeStyle from "@/components/verification-code-style2/verification-code-style2"
const take = ref({})
const popup = ref()
const { ctx } = getCurrentInstance()
const takeInfo = reactive([
{
text: "提现手续费",
value: "",
},
{
text: "预计到账",
value: "",
},
{
text: "提现账户类型",
value: "",
},
{
text: "提现账户",
value: "",
},
])
const settAccountType = reactive({
BANK_PRIVATE: "对私账户",
BANK_PUBLIC: "对公账户",
WX_CASH: "个人微信",
ALIPAY_CASH: "个人支付宝",
})
const getInputVerification = (val) => {
if (val.length >= 6) {
take.value.sipw = Base64.encode(val)
launchTake()
}
}
const open = (val) => {
take.value = val
takeInfo[2].value = settAccountType[val.settAccountType]
takeInfo[3].value = account(val.settAccountNo)
calculationFee()
popup.value.open()
}
const account = (val) => {
if (!val) return
return val.slice(0, 3) + "****" + val.slice(-4)
}
// 计算手续费
function calculationFee() {
$getAmountFee()
.then(({ bizData }) => {
if (bizData.freeLimit !== 0) {
//如果在额度限度里手续费为0
if (take.value.amount <= bizData.freeLimit / 100) {
takeInfo[0].value = "¥" + 0
} else {
console.log(3)
//如果超过限制额度
if (bizData.feeType === "FIX") {
//若是固定额度
takeInfo[0].value = "¥" + bizData.fixFee / 100
} else if (bizData.feeType === "SINGLE") {
//若是费率
takeInfo[0].value = "¥" + (bizData.feeRate * take.value.amount).toFixed(2)
} else if (bizData.feeType === "FIXANDRATE") {
//若是固定+费率
takeInfo[0].value = "¥" + ((bizData.fixFee / 100) + (bizData.feeRate * take.value.amount)).toFixed(2)
}
}
} else {
//如果额度为0则计算费率
if (bizData.feeType === "FIX") {
takeInfo[0].value = "¥" + bizData.fixFee / 100
} else if (bizData.feeType === "SINGLE") {
//若是费率
takeInfo[0].value = "¥" + (bizData.feeRate * take.value.amount).toFixed(2)
} else if (bizData.feeType === "FIXANDRATE") {
//若是固定+费率
takeInfo[0].value = "¥" + ((bizData.fixFee / 100) + (bizData.feeRate * take.value.amount)).toFixed(2)
}
}
takeInfo[1].value = (take.value.amount - takeInfo[0].value.split("¥")[1]).toFixed(2)
if (takeInfo[1].value <= 0) {
uni.showToast({
title: "提现金额不足",
icon: "none",
})
}
takeInfo[1].value = "¥" + takeInfo[1].value
})
.catch((err) => {
console.log(err)
})
}
// 发起提现
const launchTake = () => {
uni.showLoading({
title: "提交中",
mask: true,
})
const data = {
amount: take.value.amount,
applyRemark: take.value.applyRemark,
settAccountType: take.value.settAccountType,
settAccountNo: take.value.settAccountNo,
settAccountName: take.value.contactName,
settAccountBank: take.value.settAccountBank,
settAccountTelphone: take.value.contactTel,
sipw: take.value.sipw,
settCertImg: take.value.settCertImg,
applyRemark: take.value.applyRemark,
contactName: take.value.contactName,
contactTel: take.value.contactTel,
}
$putApplition(data)
.then((res) => {
if (!take.value.rid) {
uni.navigateBack({
success: () => {
uni.showToast({
title: "提交成功",
icon: "success",
})
},
})
} else {
uni.navigateBack({
delta: 2,
success: () => {
uni.showToast({
title: "提交成功",
icon: "success",
})
},
})
}
uni.hideLoading()
popup.value.close()
})
.catch(() => {
ctx.$refs.verCode.cleanVal()
take.value.sipw = ""
})
}
defineExpose({ open })
</script>
<style lang="scss" scoped>
.take-header {
position: relative;
text-align: center;
font-size: 30rpx;
font-weight: 700;
image {
position: absolute;
top: 8rpx;
left: 0;
width: 26rpx;
height: 26rpx;
}
}
.take-num {
text-align: center;
margin-top: 40rpx;
font-size: 33rpx;
color: #666;
view {
margin-top: 15rpx;
padding-bottom: 35rpx;
font-size: 57rpx;
font-weight: 700;
color: #000;
border-bottom: 1rpx solid #e6e6e6;
}
}
.take-info {
display: flex;
justify-content: space-between;
font-size: 28rpx;
color: #a6a6a6;
margin: 20rpx 0;
.take-value {
color: #000000;
}
}
</style>

View File

@@ -0,0 +1,56 @@
<template>
<JPopup ref="popup" type="center" dir="center">
<JMainCard>
<view class="s-tips bdR10"> 暂未设置支付密码请前往设置支付密码 </view>
<view class="s-button">
<view class="bdR10 cancel" @tap="popup.close()">取消</view>
<view class="bdR10 to-pwd" @tap="toPwd">前往设置</view>
</view>
</JMainCard>
</JPopup>
</template>
<script setup>
import { ref } from "vue"
import JPopup from "@/components/newComponents/JPopup/JPopup"
import JMainCard from "@/components/newComponents/JMainCard/JMainCard"
const popup = ref(null)
const toPwd = () => {
popup.value.close()
uni.navigateTo({
url: "/pageWork/setUp/takeMoneyPwd",
})
}
const open = () => {
popup.value.open()
}
defineExpose({ open })
</script>
<style lang="scss" scoped>
.s-tips {
padding: 35rpx;
background-color: #f2f2f2;
font-size: 30rpx;
color: #666;
}
.s-button {
display: flex;
justify-content: space-between;
text-align: center;
margin-top: 30rpx;
view {
flex: 1;
padding: 20rpx 0;
}
.to-pwd {
margin-left: 20rpx;
background-color: $primaryColor;
color: #fff;
}
.cancel {
color: #666;
background-color: #f2f2f2;
}
}
</style>

View File

@@ -0,0 +1,74 @@
<template>
<JPopup ref="popup">
<JMainCard pd="0" wrapPd="30rpx">
<JInput
name="联系人姓名"
:rules="{ name: 'name', rule: 'REG_NotNUll' }"
v-model:value="userInfo.name"
place="请输入联系人姓名"
:isBorder="true"
@focusShow="flag = true"
@focusNone="flag = false"
></JInput>
<JInput
name="联系人电话"
:rules="{ name: 'phone', rule: 'REG_NotNUll' }"
type="number"
v-model:value="userInfo.phone"
place="请输入联系人电话"
@focusShow="flagT = true"
@focusNone="flagT = false"
></JInput>
<view class="confirm bgF2" @tap="toUSer">确认</view>
</JMainCard>
<JButton pd="0 30rpx 30rpx 30rpx" bgColor="#f2f2f2" color="#000" bottom="0" pdTop="0" @HandleTouch="popup.close()"
>取消</JButton
>
<view class="height-block" v-if="flagT || flag"></view>
</JPopup>
</template>
<script setup>
import { reactive, ref } from "vue"
import JPopup from "@/components/newComponents/JPopup/JPopup"
import JMainCard from "@/components/newComponents/JMainCard/JMainCard"
import JInput from "@/components//newComponents/JInput/JInput"
import JButton from "@/components//newComponents/JButton/JButton"
import { validateArray } from "@/hooks/rules"
const emits = defineEmits(["userContact"])
const popup = ref()
const userInfo = reactive({})
const open = (val) => {
if (val) {
userInfo.name = val.contactName
userInfo.phone = val.contactTel
}
popup.value.open()
}
const flag = ref(false)
const flagT = ref(false)
const toUSer = () => {
if (validateArray(userInfo)) {
emits("userContact", userInfo)
uni.setStorageSync("contactInfo", userInfo)
popup.value.close()
}
}
defineExpose({ open })
</script>
<style lang="scss" scoped>
.confirm {
display: flex;
justify-content: center;
align-items: center;
height: 110rpx;
text-align: center;
color: $primaryColor;
font-size: 33rpx;
}
.height-block {
height: 25vh;
}
</style>

View File

@@ -0,0 +1,227 @@
<template>
<JHeaderTitle title="发起提现" bgColor="#f2f2f2"></JHeaderTitle>
<view class="user-contact bgF2 afterBdR32">
提现联系人
<view class="user-name">
<view
@tap="contact.open({ contactName: tackInfo.contactName, contactTel: tackInfo.contactTel })"
:class="{ 'user-weight': tackInfo.contactName }"
>{{ tackInfo.contactName ? tackInfo.contactName : "暂未设置联系人" }}
<image src="/static/iconImg/right-arrow.svg" mode="scaleToFill"
/></view>
<image v-if="!tackInfo.contactTel" src="/static/iconImg/user-contact.svg" mode="scaleToFill" />
<template v-else>{{ tackInfo.contactTel }}</template>
</view>
</view>
<view class="money-main">
<view class="money-title">提现金额</view>
<view class="money-input">
<input type="digit" v-model="tackInfo.amount" :maxlength="maxlength" @input="check" />
()
</view>
<view class="money-balance">可提现{{ tackInfo.allowTakeAmount }}<text @tap="allIn">全部提现</text></view>
<view class="money-upload">
<JUpLoad name="申请资料(请上传发票等凭证图片)" pd="0" pdLeft="0" fontSize="28rpx" v-model:value="settCertImg" borderNone>222</JUpLoad>
</view>
<view class="money-remarks">
<JInput
name="备注"
pd="40rpx 0"
place="请输入备注"
:maxlength="17"
size="28rpx"
v-model:value="tackInfo.applyRemark"
:isBorder="true"
></JInput>
</view>
</view>
<JButton pd="30rpx 60rpx 50rpx 60rpx " pdTop="40rpx" @HandleTouch="createTack">发起提现</JButton>
<USerContact ref="contact" @userContact="userContact"></USerContact>
<ConfirmTake ref="tack"></ConfirmTake>
<SipwTips ref="sipw" />
</template>
<script setup>
import { reactive, ref, watchEffect } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
import {
$getAmountFee,
$getAcountInfo,
$putApplition,
$statistics,
$getCashoutDetail,
$isSipw,
} from "@/http/apiManager.js"
import { horn } from "@/hooks/handleMoney"
import valid from "@/hooks/validate"
import JHeaderTitle from "@/components//newComponents/JHeaderTitle/JHeaderTitle"
import JInput from "@/components//newComponents/JInput/JInput"
import JUpLoad from "@/components//newComponents/JUpLoad/JUpLoad"
import JButton from "@/components//newComponents/JButton/JButton"
import USerContact from "./components/UserContact.vue"
import ConfirmTake from "./components/ConfirmTake"
import SipwTips from "./components/SipwTips.vue"
onLoad((options) => {
if (options.rid) {
rid = options.rid
getWithInfo(options.rid)
} else {
getTack()
}
})
const settCertImg = ref()
const contact = ref(null)
const tack = ref(null)
const sipw = ref(null)
const tackInfo = ref({
amount: "",
})
const maxlength = ref(10)
let rid = undefined
const getTack = () => {
$statistics().then(({ bizData }) => {
tackInfo.value.allowTakeAmount = horn(bizData.allowTakeAmount)
})
if (rid) return false
$getAcountInfo().then(({ bizData }) => {
tackInfo.value = bizData
const contactInfo = uni.getStorageSync("contactInfo")
if (contactInfo.name) {
tackInfo.value.contactName = contactInfo.name
tackInfo.value.contactTel = contactInfo.phone
}
})
}
// 展业宝提现记录页进入的位置↓↓↓
const getWithInfo = (rid) => {
$getCashoutDetail(rid).then(({ bizData }) => {
tackInfo.value = bizData
tackInfo.value.contactName = bizData.settAccountName
tackInfo.value.contactTel = bizData.settAccountTelphone
tackInfo.value.amount = (bizData.applyAmount / 100).toFixed(2)
tackInfo.value.allowTakeAmount = horn(bizData.allowTakeAmount)
tackInfo.value.rid = rid
})
}
const userContact = (val) => {
tackInfo.value.contactName = val.name
tackInfo.value.contactTel = val.phone
}
const allIn = () => {
tackInfo.value.amount = tackInfo.value.allowTakeAmount
}
const createTack = () => {
tackInfo.value.settCertImg = settCertImg.value
if (!valid.takeMoney(tackInfo.value.amount)) return errorMsg("请检查输入提现金额数据")
if (Number(tackInfo.value.amount) > Number(tackInfo.value.allowTakeAmount))
return errorMsg("提现金额不得大于可提现金额")
if (!tackInfo.value.contactName || !tackInfo.value.contactTel) return errorMsg("请输入联系人姓名和手机号")
if (tackInfo.value.contactTel.length != 11) return errorMsg("请检查联系人手机号格式")
if (tackInfo.value.amount <= 0 || !tackInfo.value.amount) return errorMsg("请输入提现金额并且提现金额不能为0")
isPwd()
}
// 判断是否设置支付密码
const isPwd = () => {
$isSipw().then(({ bizData }) => {
if (bizData) {
tack.value.open(tackInfo.value)
} else {
sipw.value.open()
}
})
}
const check = (e) => {
let value = e.detail.value
let dot = value.indexOf(".") //包含小数点
let reg = /^[0-9]+$/ //正整数
if (dot > -1) {
maxlength.value = dot + 3 //长度是小数点后两位
if (value.length > dot + 3) {
}
}
if (reg.test(value)) {
//如果是正整数不包含小数点
maxlength.value = 10
}
}
const errorMsg = (title) => {
uni.showToast({
title,
icon: "none",
mask: true,
})
}
</script>
<style lang="scss" scoped>
.user-contact {
position: relative;
display: flex;
align-items: flex-start;
font-size: 28rpx;
color: rgba(77, 77, 77, 1);
padding: 60rpx 60rpx 107rpx 60rpx;
.user-name {
flex: 1;
margin-left: 48rpx;
color: rgba(145, 145, 145, 1);
image {
width: 40rpx;
height: 40rpx;
}
view {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 11rpx;
}
.user-weight {
font-weight: 700;
color: #000;
}
}
}
.money-main {
padding: 0 60rpx;
.money-title {
font-size: 28rpx;
color: #4d4d4d;
}
.money-input {
display: flex;
align-items: center;
border-bottom: 1rpx solid rgba(0, 0, 0, 0.2);
input {
flex: 1;
height: 100rpx;
font-size: 57rpx;
font-weight: 700;
color: $primaryColor;
}
}
.money-balance {
margin-top: 20rpx;
font-size: 28rpx;
text {
margin-left: 15rpx;
color: $primaryColor;
}
}
.money-upload {
padding: 90rpx 0 30rpx 0;
border-bottom: 1rpx solid rgba(0, 0, 0, 0.2);
}
.money-remarks {
border-bottom: 1rpx solid rgba(0, 0, 0, 0.2);
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More