源文件

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,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>