This commit is contained in:
2025-04-02 10:35:17 +08:00
commit 89db955ec1
701 changed files with 91082 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
<!--
组件功能 对button的封装
@author terrfly
@site https://www.jeequan.com
@date 2022/12/05 14:39
-->
<template>
<button class="jeepay-btn" hover-class="jeepay-hover-button">
<slot />
</button>
</template>
<script setup>
const props = defineProps({
})
</script>
<style lang="scss" scoped>
.jeepay-btn {
display: flex;
justify-content: center;
align-items: center;
height: 110rpx;
font-size: 33rpx;
font-weight: 500;
color: $J-color-tff;
border-radius: 20rpx;
background: $jeepay-bg-primary;
box-shadow: 0 20rpx 60rpx -20rpx rgba(0,84,210,0.5);
&.jeepay-hover-button {
opacity: 0.5;
}
}
</style>

View File

@@ -0,0 +1,208 @@
<template>
<my-model ref="model" :title="title" iconColor="#000" @close="resetForm">
<template #desc>
<view class="u-text-left u-p-30 color-666">
<view class="u-m-t-32 u-flex ">
<view>应付金额</view>
<view class="u-m-l-32">
{{form.price}}
</view>
</view>
<view class="u-m-t-40 u-flex ">
<view>实收金额</view>
<view class="u-m-l-32 border u-p-l-10 u-p-r-10 u-flex-1">
<uni-easyinput type="number" @input="currentPriceInput" @change="currentPriceChange" paddingNone :inputBorder="false"
v-model="form.currentPrice"
placeholder="输入实际金额"></uni-easyinput>
</view>
</view>
<view class="u-m-t-54 u-flex ">
<view>优惠折扣</view>
<view class="u-m-l-32 u-flex-1 u-flex border u-p-l-10 u-p-r-10">
<view class="u-flex-1">
<uni-easyinput type="number" @input="discountInput" @change="discountChange" paddingNone :inputBorder="false"
v-model="form.discount"
placeholder="输入折扣"></uni-easyinput>
</view>
<view class="u-font-32 color-333">%</view>
</view>
</view>
</view>
</template>
<template #btn>
<view class="u-p-30">
<view class="u-m-t-10">
<my-button @tap="confirm" shape="circle" fontWeight="700" >修改</my-button>
<view class="">
<my-button @tap="close" type="cancel" bgColor="#fff" >取消</my-button>
</view>
</view>
</view>
</template>
</my-model>
</template>
<script setup>
import {
reactive,
nextTick,
ref,watch
} from 'vue';
import myModel from '@/components/my-components/my-model.vue'
import myButton from '@/components/my-components/my-button.vue'
import myTabs from '@/components/my-components/my-tabs.vue'
import infoBox from '@/commons/utils/infoBox.js'
const props = defineProps({
title: {
type: String,
default: ''
},
data: {
type: Array,
default: []
},
discount:{
type: [Number,String],
default:100
},
price: {
type: [Number,String],
default: 0
}
})
function currentPriceInput(newval){
form.discount=(newval*100/form.price).toFixed()
}
function discountInput(newval){
form.currentPrice=(form.price*newval/100).toFixed(2)
}
function currentPriceChange(newval){
if(newval<0){
form.currentPrice=0
form.discount=100
return infoBox.showToast('实收金额不能小于0')
}
if(newval>props.price){
form.currentPrice=props.price
form.discount=0
return infoBox.showToast('实收金额不能大于应付金额')
}
}
function discountChange(newval){
if(newval<0){
form.currentPrice=props.price
form.discount=0
return infoBox.showToast('优惠折扣不能小于0')
}
if(newval>100){
form.discount=100
form.currentPrice=0
return infoBox.showToast('优惠折扣不能大于100')
}
}
const $form = {
price:props.price,
currentPrice: props.price,
discount: 100
}
const form = reactive({
...$form
})
watch(()=>props.price,(newval)=>{
console.log(newval);
form.price=newval
form.currentPrice=newval
})
function resetForm() {
Object.assign(form, {
...$form
})
}
const model = ref(null)
function open() {
model.value.open()
form.price=props.price
form.currentPrice=props.price
form.discount=props.discount
}
function close() {
model.value.close()
}
const emits = defineEmits(['confirm'])
function confirm() {
console.log(form);
emits('confirm',{...form,currentPrice:Number(form.currentPrice).toFixed(2)})
close()
}
defineExpose({
open,
close
})
</script>
<style lang="scss" scoped>
.border{
border-radius: 8rpx;
overflow: hidden;
border-color: #999;
}
.lh34 {
line-height: 34rpx;
}
.tag {
background-color: #fff;
border: 1px solid #E5E5E5;
line-height: inherit;
font-size: 24rpx;
color: #666666;
padding: 6rpx 20rpx;
border-radius: 8rpx;
&.active {
border-color: #E6F0FF;
color: $my-main-color;
}
}
.hover-class {
background-color: #E5E5E5;
}
.discount {
.u-absolute {
top: 0;
bottom: 0;
right: 0;
}
}
.bg1 {
background: #F7F7FA;
}
.tab {
padding: 0 80rpx;
}
.border {
border: 1px solid #E5E5E5;
border-radius: 4rpx;
}
.input-box {
padding: 22rpx 32rpx;
font-size: 28rpx;
color: #666;
}
.placeholder-class {
font-size: 28rpx;
}
</style>

View File

@@ -0,0 +1,107 @@
<template>
<view class="action-sheet" @tap="close" v-if="show">
<view class="box">
<slot name="title">
</slot>
<view class="item" @tap.stop="itemClick(index)" v-for="(item,index) in props.list" :key="index">
<button class="bg-fff btn" hover-class="btn-hover-class" :class="{'color-main':active==index}">{{item}}</button>
</view>
<view class="bock-gary"></view>
<view class="cancel-btn" @tap="close">
<button>取消</button>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
const props=defineProps({
//那个按钮文字高亮
active:{
type:Number,
default:-1
},
autoClose:{
type:Boolean,
default:true
},
title:{
type:String,
default:''
},
list:{
type:Array,
default:['编辑','删除']
},
show:{
type:Boolean,
default:false
}
})
const emits=defineEmits(['itemClick','close'])
let show=ref(false)
function open(){
show.value=true
}
function close(){
show.value=false
emits('close')
}
function itemClick(index){
if(props.autoClose){
close()
}
emits('itemClick',index)
}
defineExpose({
open,close
})
</script>
<style lang="scss">
$bg:rgb(240, 240, 240);
.action-sheet{
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
background-color: rgba(51,51,51,.5);
z-index: 999;
.box{
position: absolute;
left: 0;
right: 0;
bottom: 0;
border-radius: 20rpx 20rpx 0rpx 0rpx;
overflow: hidden;
background-color: #fff;
/* #ifndef H5 */
padding-bottom: calc(24rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
/* #endif */
.item{
text-align: center;
font-size: 32rpx;
border-bottom: 1px solid $bg;
.btn{
border-radius: 0;
}
}
}
}
.bock-gary{
background-color: $bg;
height: 20rpx;
}
.cancel-btn{
text-align: center;
font-size: 32rpx;
}
.btn-hover-class{
background-color: rgb(222, 222, 222);
}
</style>

View File

@@ -0,0 +1,158 @@
<template>
<view class="">
<button class="btn" hover-class="btn-hover-class"
@tap="tap"
@click="click"
:disabled="disabled"
:style="computeStyle()"
:class="[returnShape,returnType,returnShadow,returnDisabled]"
>
<view class="u-flex u-row-center u-col-center auto-center">
<slot></slot>
</view>
</button>
</view>
</template>
<script setup>
import { computed } from 'vue';
const props=defineProps({
disabled:{
type:Boolean,
default:false
},
plain:{
type:Boolean,
default:false
},
borderRadius:{
type:String,
default:''
},
bgColor:{
type:String,
default:''
},
color:{
type:String
},
shape:{
//circle square
type:String,
default:'square'
},
type:{
type:String,
default:'primary'
},
width:{
type:[String,Number],
default:''
},
height:{
type:[String,Number],
default:'80'
},
fontSize:{
type:[String,Number],
default:'28'
},
fontWeight:{
type:[String,Number],
default:'500'
},
showShadow:{
type:Boolean,
default:false
}
})
const emits=defineEmits(['click','tap'])
function tap(){
emits('tap')
}
function click(){
emits('click')
}
const shapeClassList={circle:'circle',square:'square'}
const typeClassList={primary:'primary',default:'default',cancel:'cancel'}
const returnShape= computed(()=>{
return shapeClassList[props.shape]
})
const returnShadow=computed(()=>{
return props.showShadow?'shadow':''
})
const returnDisabled=computed(()=>{
return props.disabled?'disabled':''
})
const returnType=computed(()=>{
if(props.plain){
return 'plain-'+typeClassList[props.type]+' '+' plain'
}
return typeClassList[props.type]
})
function computeStyle(){
return `
line-height:${props.height}rpx;
${props.width>=0?('width:'+props.width+'rpx;'):''}
${props.plain?('background-color:transparent;'):''}
font-size:${props.fontSize}rpx;
font-weight:${props.fontWeight};
${props.color?('color:'+props.color+';'):''}
${props.bgColor?('background-color:'+props.bgColor+';'):''}
${props.bgColor?('border-color:'+props.bgColor+';'):''}
${props.borderRadius?('border-radius:'+props.borderRadius+';'):''}
`
}
</script>
<style lang="scss" scoped>
.auto-center{
margin: auto;
}
.shadow{
// box-shadow: 0 0 10px #aaa;
box-shadow: 0 20rpx 60rpx -20rpx rgba(0,84,210,0.5);
}
.btn {
font-size: 28rpx;
border:1px solid transparent ;
&.disabled{
color: #bbb!important;
border-color: #eee!important;
}
}
.plain{
background-color: transparent;
}
.plain-primary{
color: $my-main-color;
border-color: $my-main-color;
}
.plain-default{
color: #999;
border-color: #999;
}
.plain-cancel{
color: #999;
border-color: #999;
}
.primary{
background-color: $my-main-color;
color: #fff;
border-color: $my-main-color;
}
.default{
background-color: transparent;
color: #999;
border-color: #999;
}
.circle{
border-radius: 200rpx;
}
.square{
border-radius: 12rpx;
}
.btn-hover-class {
opacity: .6;
}
</style>

View File

@@ -0,0 +1,601 @@
<template>
<view class="mask" v-if="show" @tap="close">
<view class="box" @tap.stop="nullFunction">
<view class="u-flex u-relative u-row-center u-p-30 top" v-if="props.isArea">
<view class="font-bold u-font-32">{{props.title}}</view>
<view class="close" @tap="close">
<uni-icons type="closeempty" size="24"></uni-icons>
</view>
</view>
<view class="u-p-30 u-flex u-flex-wrap gap-20 fastTime" v-if="props.isArea">
<view class="item" v-for="(item,index) in fastTime" :key="index" @tap="changeTime(item.key)">
{{item.title}}
</view>
</view>
<picker-view :immediate-change="true" @pickend="pickend" :value="value" @change="bindChange"
class="picker-view">
<template v-if="props.mode==='all'||props.mode==='date'">
<picker-view-column>
<view class="item" v-for="(item,index) in years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item,index) in months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item,index) in days" :key="index">{{item}}</view>
</picker-view-column>
</template>
<template v-if="props.mode==='all'||props.mode==='time'">
<picker-view-column>
<view class="item" v-for="(item,index) in hours" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item,index) in minutes" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item,index) in seconds" :key="index">{{item}}</view>
</picker-view-column>
</template>
</picker-view>
<template v-if="props.isArea">
<view class="u-text-center color-999"></view>
<picker-view :immediate-change="true" :value="value1" @pickend="pickend1" @change="bindChange1"
class="picker-view">
<template v-if="props.mode==='all'||props.mode==='date'">
<picker-view-column>
<view class="item" v-for="(item,index) in years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item,index) in months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item,index) in days1" :key="index">{{item}}</view>
</picker-view-column>
</template>
<template v-if="props.mode==='all'||props.mode==='time'">
<picker-view-column>
<view class="item" v-for="(item,index) in hours" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item,index) in minutes" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item,index) in seconds" :key="index">{{item}}</view>
</picker-view-column>
</template>
</picker-view>
</template>
<!-- 占位 -->
<view style="height: 80px;"></view>
<template v-if="props.isArea">
<view class="fixed_b" >
<my-button shape="circle" @tap="confirm">确定</my-button>
</view>
</template>
<template v-else>
<view class="fixed_b u-flex u-row-center">
<view class="u-m-r-16">
<my-button type="cancel" @tap="close" width="240">
<view class="color-999">取消</view>
</my-button>
</view>
<view class="u-m-l-16">
<my-button @tap="confirm" width="240">确定</my-button>
</view>
</view>
</template>
</view>
</view>
</template>
<script setup>
import myButton from "@/components/my-components/my-button.vue"
import {
reactive,
nextTick,
ref
} from 'vue';
const props = defineProps({
selTime:{
type: [String,Number],
},
defaultIndex: {
type: Array,
default: () => {
return []
}
},
defaultTime: {
type: Array,
default: () => {
return []
}
},
title: {
type: String,
default: '筛选日期时间'
},
isArea: {
//是否选中范围时间
type: Boolean,
default: true
},
mode: {
//all date time
type: String,
default: 'time'
},
yearsLen:{
type: Number,
default:30
}
})
const $nowDate = new Date()
const nowDate = {
year: $nowDate.getFullYear(),
month: $nowDate.getMonth() + 1,
day: $nowDate.getDate(),
hours: $nowDate.getHours(),
minutes: $nowDate.getMinutes(),
seconds: $nowDate.getSeconds()
}
const yearsLen = props.yearsLen
function returnYears() {
if(props.isArea){
return new Array(yearsLen).fill(1).map((v, index) => {
return nowDate.year - index
}).reverse()
}else{
return new Array(yearsLen).fill(1).map((v, index) => {
return nowDate.year-Math.floor(yearsLen/2) + index
})
}
}
const years = returnYears()
const months = new Array(12).fill(1).map((v, index) => {
return index + 1
})
const days = ref(new Array(getMonthArea($nowDate, 'end').getDate()).fill(1).map((v, index) => {
return index + 1
}))
const days1 = ref(new Array(getMonthArea($nowDate, 'end').getDate()).fill(1).map((v, index) => {
return index + 1
}))
const hours = new Array(24).fill(1).map((v, index) => {
return index
})
const minutes = new Array(60).fill(1).map((v, index) => {
return index
})
const seconds = new Array(60).fill(1).map((v, index) => {
return index
})
const fastTime = reactive([{
title: '今日',
key: 'now'
},
{
title: '昨日',
key: 'prve'
},
{
title: '本月',
key: 'nowMonth'
},
{
title: '上月',
key: 'prveMonth'
}
])
function setPrveDay() {
}
function setNowMoneth() {
}
function setprveMoneth() {
}
function setDay(start, end) {
value.value = [
start.year,
start.month,
start.day,
0,
0,
0,
]
value1.value = [
end.year,
end.month,
end.day,
23,
59,
59,
]
}
function changeTime(key) {
const yearIndex = years.findIndex(v => v == nowDate.year)
const prveyearIndex = years.findIndex(v => v == nowDate.year) - 1
const nowMonthIndex = nowDate.month - 1
const nowDayIndex = nowDate.day - 1
const dataMap = {
now: function() {
return {
start: {
year: yearIndex,
month: nowMonthIndex,
day: nowDayIndex
},
end: {
year: yearIndex,
month: nowMonthIndex,
day: nowDayIndex
}
}
},
prve: function() {
const oneDay = 1000 * 60 * 60 * 24
const date = new Date(new Date(nowDate.year, nowDate.month, nowDate.day, 0, 0, 0).getTime() -
oneDay)
return {
start: {
year: years.findIndex(v => v == date.getFullYear()),
month: date.getMonth() - 1 < 0 ? 11 : date.getMonth() - 1,
day: date.getDate() - 1
},
end: {
year: years.findIndex(v => v == date.getFullYear()),
month: date.getMonth() - 1 < 0 ? 11 : date.getMonth() - 1,
day: date.getDate() - 1
}
}
},
nowMonth: function() {
return {
start: {
year: yearIndex,
month: nowMonthIndex,
day: 0
},
end: {
year: yearIndex,
month: nowMonthIndex,
day: new Date(nowDate.year, nowDate.month, 0).getDate() - 1
}
}
},
prveMonth: function() {
const oneDay = 1000 * 60 * 60 * 24
const date = new Date(new Date(nowDate.year, nowDate.month - 1, 0, 0, 0).getTime() - oneDay)
console.log(date.getMonth());
return {
start: {
year: years.findIndex(v => v == date.getFullYear()),
month: date.getMonth(),
day: 0
},
end: {
year: years.findIndex(v => v == date.getFullYear()),
month: date.getMonth(),
day: date.getDate()
}
}
}
}
const data = dataMap[key]()
setDay(data.start, data.end)
changeDays(false, value.value)
changeDays(true, value1.value)
console.log(value1.value);
const start = returnDateString(value.value)
const end = returnDateString(value1.value)
emits('confirm', {
text: `${start}——${end}`,
start,
end
})
close()
}
let value = ref([])
let value1 = ref([])
initValue()
function returnFindIndex(arr) {
const yearIndex = years.findIndex(v => v == arr[0])
const monthIndex = arr[1]
const dayIndex = arr[2] - 1
const hIndex = arr[3]
const mIndex = arr[4]
const sIndex = arr[5]
return [
yearIndex, monthIndex, dayIndex, hIndex, mIndex, sIndex
]
}
function initValue() {
if (props.defaultTime.length && !props.isArea) {
value.value = returnFindIndex(props.defaultTime)
} else {
const yearIndex = years.findIndex(v => v == nowDate.year)
value.value = [
yearIndex,
nowDate.month - 1,
nowDate.day - 1,
0,
0,
0,
]
value1.value = [
yearIndex,
nowDate.month - 1,
nowDate.day - 1,
23,
59,
59,
]
}
}
let show = ref(false)
const emits = defineEmits('close', 'open', 'confirm')
function toggle() {
show.value = !show.value
if (show.value) {
open()
} else {
close()
}
}
function close() {
show.value = false
// emits('close', false)
}
function open() {
if(typeof props.selTime==='number'){
const d=new Date(props.selTime)
value.value[0]=years.findIndex(v=>v===d.getFullYear())
value.value[1]=months.findIndex(v=>v===d.getMonth())+1
value.value[2]=days.value.findIndex(v=>v===d.getDate())
}
show.value = true
// emits('open', true)
}
function returnDateString(arr, isObj) {
const year = years[arr[0]]
const month = arr[1] + 1
const day = arr[2] + 1
const hour = ('0' + arr[3]).slice(-2)
const min = ('0' + arr[4]).slice(-2)
const sen = ('0' + arr[5]).slice(-2)
if (isObj) {
return new Date(year, month, day, hour, min, sen)
}
return `${year}-${month}-${day} ${hour}:${min}:${sen}`
}
function confirm(e) {
const start = returnDateString(value.value)
console.log(start);
const end = returnDateString(value1.value)
if (!props.isArea) {
emits('confirm', start)
} else {
emits('confirm', {
text: `${start}——${end}`,
start,
end
})
}
close()
}
function returnMonthStart(arr) {
return new Date(years[arr[0]], months[arr[1]] - 1, 1).getDate();
}
function returnMonthEnd(arr) {
return new Date(years[arr[0]], months[arr[1]], 0).getDate();
}
//防抖
function debounce(fn, wait) {
let timeout = null;
return function() {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
let callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait);
if (callNow) fn.apply(context, args);
};
}
/**
* @param {Object} isDays1 //是否是结束时间选择
* @param {Object} arr
*/
function changeDays(isDays1, arr) {
const end = returnMonthEnd(arr)
if (end) {
if (isDays1) {
days1.value = new Array(end).fill(1).map((v,
index) => {
return index + 1
})
} else {
days.value = new Array(end).fill(1).map((v,
index) => {
return index + 1
})
}
}
}
function bindChange(e) {
const startTotal = returnDateString(e.detail.value, true).getTime()
const endTotal = returnDateString(value1.value, true).getTime()
value.value = e.detail.value
setTimeout(()=>{
if (props.isArea) {
value.value = startTotal > endTotal ? value1.value : e.detail.value
}
debounce(changeDays(false, value.value), 100)
},10)
// nextTick(() => {
// if (props.isArea) {
// value.value = startTotal > endTotal ? value1.value : e.detail.value
// }
// console.log(value.value);
// debounce(changeDays(false, value.value), 100)
// })
}
function bindChange1(e) {
const startTotal = returnDateString(value.value, true).getTime()
const endTotal = returnDateString(e.detail.value, true).getTime()
value1.value = e.detail.value
nextTick(() => {
value1.value = endTotal < startTotal ? value.value : e.detail.value
debounce(changeDays(true, value1.value), 100)
})
}
function getDayDate(date = new Date(), type) {
const now = date
if (type === 'start') {
const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());
return startOfDay
}
if (type === 'end') {
const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
return endOfDay;
}
}
function getMonthArea(date = new Date(), type) {
let now = date
let currentMonthStart = new Date(now.getFullYear(), now.getMonth(), 1);
let currentMonthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
if (type === 'start') {
return currentMonthStart
}
if (type === 'end') {
return currentMonthEnd;
}
return {
start: currentMonthStart,
end: currentMonthEnd
};
}
function nullFunction() {
}
function pickend(e) {
console.log(e);
}
function pickend1(e) {
console.log(e);
}
defineExpose({
close,
open,
confirm,
toggle
})
</script>
<style lang="scss" scoped>
.fastTime {
.item {
background-color: rgb(247, 247, 247);
padding: 6rpx 40rpx;
border-radius: 6rpx;
font-size: 32rpx;
}
}
.top {
border-bottom: 1px solid #eee;
}
.close {
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 30rpx;
}
.mask {
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
z-index: 999;
background-color: rgba(51, 51, 51, .5);
.item {
line-height: 34px;
text-align: center;
}
.box {
position: absolute;
background-color: #fff;
bottom: 0;
left: 0;
right: 0;
border-radius: 16rpx 16rpx 0 0;
}
}
.fixed_b {
position: absolute;
left: 0;
right: 0;
bottom: 0;
padding: 30rpx;
z-index: 100;
background-color: #fff;
}
.picker-view {
width: 750rpx;
height: 300rpx;
}
</style>

View File

@@ -0,0 +1,38 @@
<template>
<view class="u-flex u-row-center">
<view class="empty color-999 u-font-28 u-m-t-60 u-text-center">{{text}}</view>
</view>
</template>
<script setup>
const props= defineProps({
text:{
type:String,
default:'暂无记录'
}
})
</script>
<style lang="scss">
.empty{
position: relative;
padding: 0 16rpx;
&:before,&::after{
position: absolute;
content: '';
display: block;
top: 50%;
height: 1px;
background-color: #E5E5E5;
border-radius: 1px;
width: 94rpx;
}
&:before{
right: 100%;
}
&::after{
left: 100%;
}
}
</style>

View File

@@ -0,0 +1,71 @@
<template>
<view class="icon"
:class="[computedClass]"
></view>
</template>
<script setup>
import { computed } from 'vue';
const props=defineProps({
type:{
type:String
}
})
const classMap={
add:'icon-add',
reduce:'icon-reduce'
}
const computedClass=computed(()=>{
return classMap[props.type]
})
</script>
<style lang="scss">
$icon-size: 34rpx;
$icon-line-width: 20rpx;
$icon-line-height: 4rpx;
.icon {
width: $icon-size;
height: $icon-size;
position: relative;
border-radius: 50%;
&:before,
&::after {
position: absolute;
display: block;
content: '';
background-color: #fff;
}
}
.icon-add {
background-color: $my-main-color;
&::before {
width: $icon-line-height;
height: $icon-line-width;
top: calc(($icon-size /2) - ($icon-line-width / 2));
left: calc(($icon-size /2) - ($icon-line-height / 2));
}
&::after {
width: $icon-line-width;
height: 4rpx;
top: calc(($icon-size /2) - ($icon-line-height / 2));
left: calc(($icon-size /2) - ($icon-line-width / 2));
}
}
.icon-reduce {
background-color: $my-red-color;
&::after {
width: $icon-line-width;
height: $icon-line-height;
top: calc(($icon-size /2) - ($icon-line-height / 2));
left: calc(($icon-size /2) - ($icon-line-width / 2));
}
}
</style>

View File

@@ -0,0 +1,32 @@
<template>
<view class="u-flex u-flex-col u-row-center u-col-center w-full">
<slot>
<image :src="img" mode="" class="img"></image>
<view class="tips">{{tips}}</view>
</slot>
</view>
</template>
<script setup>
const props= defineProps({
tips:{
type:String,
default:'暂无数据'
},
img:{
type:String,
default:'/static/icon-empty.svg'
}
})
</script>
<style lang="scss">
.img{
width: 326rpx;
height: 336rpx;
}
.tips{
margin-top: 74rpx;
}
</style>

View File

@@ -0,0 +1,326 @@
<template>
<up-popup :show="show" @close="close" closeOnClickOverlay @open="open" mode="center" :round="16">
<view class="login-wrapper">
<view class="login-bottom jeepay-form">
<view class="u-flex u-flex-row u-row-right u-m-t-10">
<up-icon name="close-circle-fill" size="20" color="#999" @click="close"></up-icon>
</view>
<up-form errorType="toast" :model="vdata.formData" :rules="rules" ref="loginFormRef">
<view class="">
<up-tabs :list="accountType.list" keyName="label" @change="accountTypeChange"></up-tabs>
</view>
<template v-if="accountType.sel==1">
<up-form-item prop="merchantName">
<up--input v-model="vdata.formData.merchantName" placeholder="请输入商户号">
</up--input>
</up-form-item>
</template>
<up-form-item prop="username">
<up--input v-model="vdata.formData.username" placeholder="请输入登录名/手机号"></up--input>
</up-form-item>
<up-form-item prop="pwd">
<up--input v-model="vdata.formData.pwd" type="password" placeholder="请输入登录密码"></up--input>
</up-form-item>
<up-form-item prop="code">
<up--input v-model="vdata.formData.code" placeholder="请输入验证码">
</up--input>
<image :src="vdata.formData.img" class=" " style="width: 100px; height: 40px;margin-left: 5px;"
@click="getCode" mode="">
</image>
</up-form-item>
<view class="u-m-t-10">
<up-button shape="circle" @click="loginFunc" type="primary" text="登录"></up-button>
</view>
</up-form>
<!-- <uni-forms ref="loginFormRef" label-width="0" :model="vdata.formData" :rules="rules">
<view class="u-p-b-30">
<my-tabs size="large" @change="accountTypeChange" v-model="accountType.sel" :list="accountType.list"
textKey="label"></my-tabs>
</view>
<view>
<template v-if="accountType.sel==1">
<uni-forms-item name="merchantName">
<uni-easyinput class='jeepay-easyinput' placeholder="请输入商户号"
v-model="vdata.formData.merchantName" :clearable="false">
<template #prefixIcon>
<image src="@/static/login/icon-user.svg" class="input-icon" />
</template>
</uni-easyinput>
</uni-forms-item>
</template>
<uni-forms-item name="username">
<uni-easyinput class='jeepay-easyinput' placeholder="请输入登录名/手机号"
v-model="vdata.formData.username" :clearable="false">
<template #prefixIcon>
<image src="@/static/login/icon-user.svg" class="input-icon" />
</template>
</uni-easyinput>
</uni-forms-item>
<uni-forms-item name="pwd">
<uni-easyinput class='jeepay-easyinput' :type="vdata.isShowPwd ? 'text' : 'password'"
v-model="vdata.formData.pwd" :clearable="false" placeholder="请输入登录密码">
<template #prefixIcon>
<image src="@/static/login/icon-pw.svg" class="input-icon" />
</template>
<template #suffixIcon>
<view class='show-tips' @tap="vdata.isShowPwd = !vdata.isShowPwd ">
{{ vdata.isShowPwd ? '隐藏' : '显示' }}
</view>
</template>
</uni-easyinput>
</uni-forms-item>
<uni-forms-item name="code">
<view style="display: flex;" class="u-flex u-flex-y-center u-flex-row">
<uni-easyinput class='jeepay-easyinput' :maxlength="6" placeholder="请输入验证码"
v-model="vdata.formData.code" :clearable="false">
<template #prefixIcon>
<image src="@/static/login/icon-sms-code.svg" class="input-icon" />
</template>
</uni-easyinput>
<image :src="vdata.formData.img" class=" u-m-b-50"
style="width: 200rpx; height: 80rpx;margin-left: 10rpx;" @click="getCode" mode="">
</image>
</view>
</uni-forms-item>
</view>
<up-button @click="loginFunc" type="primary" text="登录"></up-button>
</uni-forms> -->
</view>
</view>
</up-popup>
</template>
<script setup>
import {
encrypt
} from '@/commons/utils/rsaEncrypt.js'
import {
ref,
reactive,
onMounted,
watch
} from 'vue';
import storageManage from '@/commons/utils/storageManage.js'
import infoBox from '@/commons/utils/infoBox.js';
import go from '@/commons/utils/go.js'
import timer from '@/commons/utils/timer.js'
import formUtil from '@/commons/utils/formUtil.js'
import {
login,
getCodeImg
} from '@/http/yskApi/login.js';
let show = ref(false)
const emits = defineEmits(['update:modelValue', 'loginSuccess'])
const props = defineProps({
modelValue: {
type: Boolean,
default: false
}
})
watch(() => props.modelValue, (newval) => {
show.value = newval
})
watch(() => show.value, (newval) => {
if(newval){
getCode()
}
emits('update:modelValue', newval)
})
function close() {
show.value = false
}
function open() {
show.value = true
}
const accountType = reactive({
list: [{
label: '商户',
value: 'merchant'
},
{
label: '员工',
value: 'staff'
},
],
sel: 0
})
function accountTypeChange(e) {
console.log(e);
accountType.sel = e.index
}
const loginFormRef = ref()
const envChangeTipsRef = ref()
const refAgr = ref()
const rules = {
merchantName: [{
required: true,
message: '请输入商户号',
trigger: ['blur', 'change'],
}],
username: [{
required: true,
message: '请输入员工账号,登录名/手机号',
trigger: ['blur', 'change'],
}],
pwd: [{
required: true,
message: '请输入密码',
trigger: ['blur', 'change'],
}],
code: [{
required: true,
message: '请输入验证码',
trigger: ['blur', 'change'],
}]
}
const vdata = reactive({
isShowPwd: false, // 是否显示密码
loginType: 'pwd', // 类型切换: pwd or sms
isShowSafetyCode: false, // 是否显示安全码输入框
allowSendMsgFlag: true, // 是否可发送短信验证码
sendMsgText: '发送验证码',
isSelectedAgreement: false, // 勾选隐私协议
siteInfos: storageManage.siteInfos() || {},
formData: {
username: '', // 账密登录: 用户名
pwd: '', // 账密登录: 密码
rememberMe: false,
code: '',
uuid: '',
merchantName: '',
loginType: 'merchant'
},
})
const getCode = () => {
getCodeImg().then(res => {
vdata.formData.img = res.img
vdata.formData.uuid = res.uuid
})
}
getCode()
onMounted(() => {})
function loginFunc() {
// 表单验证
formUtil.validate(loginFormRef.value).then(() => {
let loginPromise = null;
loginPromise = login({
username: vdata.formData.username,
password: encrypt(vdata.formData.pwd),
rememberMe: false,
code: vdata.formData.code,
uuid: vdata.formData.uuid,
merchantName: vdata.formData.merchantName,
loginType: accountType.list[accountType.sel].value
})
if (loginPromise == null) {
return false;
}
// 请求后的操作
loginPromise.then(res => {
console.log(res);
// 登录成功
loginFinishFunc(res)
}).catch(e => {
getCode()
})
})
}
watch(() => accountType.sel, (newval) => {
if (newval == 1) {
vdata.formData.merchantName = uni.getStorageSync('merchantName') || ''
} else {
vdata.formData.username = ''
}
})
// 封装登录成功后的操作
async function loginFinishFunc(loginBizData) {
// 保存 token
storageManage.setLogin(loginBizData)
storageManage.token(loginBizData.token)
storageManage.shopId(loginBizData.shopId)
storageManage.shopUserId(loginBizData.user.user.id)
storageManage.userInfo(loginBizData)
emits('loginSuccess')
}
</script>
<style lang="scss">
.input-icon {
width: 18px;
height: 18px;
}
::v-deep .input-placeholder {
font-size: 14px !important;
}
.login-wrapper {
background-color: #fff;
border-radius: 8px;
overflow: hidden;
.login-bottom {
min-height: 368px;
padding: 0 20px 20px 20px;
min-width: 400px;
box-sizing: border-box;
.show-tips {
color: rgba(88, 132, 204, 1);
font-weight: 400;
font-size: 16px;
}
.register-box {
display: flex;
justify-content: space-between;
padding: 0 30rpx;
margin-top: 50rpx;
font-size: 26rpx;
font-weight: 400;
.register {
text:last-child {
color: #1D79FD;
}
}
.forget {
color: #1D79FD;
}
}
.swidth-login {
display: flex;
justify-content: center;
align-items: center;
margin-top: 212rpx;
font-size: 30rpx;
font-weight: 400;
color: rgba(77, 77, 77, 1);
image {
width: 40rpx;
height: 40rpx;
}
}
}
}
</style>

View File

@@ -0,0 +1,79 @@
<template>
<view class="mask u-fixed tranistion position-all u-flex u-flex-col u-row-center u-col-center" v-if="show"
:style="computedStyle" @tap="maskClick">
<view class="w-full" @tap.stop="nullFunction">
<slot></slot>
</view>
</view>
</template>
<script setup>
import {
computed,
ref,
watch
} from 'vue';
const props = defineProps({
show:{
type: Boolean,
default: false
},
zIndex: {
type: [Number, String]
},
tapClose: {
type: Boolean,
default: true
}
})
const emits = defineEmits(['close', 'toggle', 'open'])
watch(()=>props.show,(newval)=>{
show.value=newval
})
let show = ref(props.show)
function close() {
show.value = false
emits('close')
}
function open() {
show.value = true
emits('open')
}
function nullFunction() {
}
function toggle() {
if (show.value) {
close()
} else {
open()
}
}
function maskClick() {
if (props.tapClose) {
close()
}
}
const computedStyle = computed(() => {
return `
z-index:${props.zIndex};
`
})
defineExpose({
close,
toggle,
open
})
</script>
<style lang="scss">
.mask {
background-color: rgba(51, 51, 51, .5);
}
</style>

View File

@@ -0,0 +1,232 @@
<template>
<view class="model u-text-center" v-if="show" @tap="modelTap">
<div @tap.stop="" class="box u-font-32 tranistion" :class="[returnBoxClass]" :style="[computeBoxStyle,computeStyle()]" >
<view class="u-relative">
<view class=" color-333 font-bold">{{props.title||'提示'}}</view>
<template v-if="showIcon">
<slot name="icon">
<view class="close" @tap="close">
<uni-icons :size="iconSize" :color="iconColor" type="clear"></uni-icons>
</view>
</slot>
</template>
</view>
<slot name="desc">
<view class=" color-666 u-p-30">{{props.desc}}</view>
</slot>
<slot name="btn" v-if="showBtn">
<view class="btns u-flex">
<view class="cancel btn" @tap="cancel">{{props.cancelText}}</view>
<view class="confirm btn" @tap="confirm">{{props.confirmText}}</view>
</view>
</slot>
</div>
</view>
</template>
<script setup>
import {
computed,
ref
} from 'vue';
const emits = defineEmits(['cancel', 'confirm', 'close', 'open'])
function isNumber(value) {
return Number(value)==value && !isNaN(value);
}
const props = defineProps({
confirmClickClose:{
type:Boolean,
default:true
},
isClickMaskHide:{
type:Boolean,
default:true
},
iconSize:{
type: Number,
default: 26
},
iconColor:{
type: String,
default: '#999'
},
mode:{
//center bottom
type: String,
default: 'center'
},
//单位rpx
borderRadius:{
type:[String,Number],
default:36
},
showIcon:{
type:Boolean,
default:true
},
showBtn:{
type:Boolean,
default:true
},
cancelText: {
type: String,
default: '取消'
},
confirmText: {
type: String,
default: '确定'
},
title: {
type: String,
default: '提示'
},
desc: {
type: String,
default: ''
},
width:{
type:[Number,String],
default:''
}
})
function computeStyle(){
const width=props.width?props.width:''
const bottomBorderRadius=`${props.borderRadius}rpx ${props.borderRadius}rpx 0 0`
const centerBorderRadius=`${props.borderRadius}rpx`
const borderRadius=isNumber(props.borderRadius)?(props.mode==='bottom'?bottomBorderRadius:centerBorderRadius):props.borderRadius
return `
width:${width?width:''};
borderRadius:${borderRadius};
`
}
const computedClass=computed(()=>{
return show.value?'show':'none'
})
const returnBoxClass=computed(()=>{
if(props.mode==='center'){
return ''
}
return 'bottom-box'
})
const computeBoxStyle=computed(()=>{
if(props.mode==='center'){
return ''
}
})
function getComputeClass() {
}
let show = ref(false)
function open() {
show.value = true
emits('open')
}
function close() {
show.value = false
emits('close')
}
function modelTap(){
if(props.isClickMaskHide){
close()
}
}
function cancel() {
show.value = false
emits('cancel')
emits('close')
}
function confirm() {
emits('confirm')
if(props.confirmClickClose){
close()
}
}
defineExpose({
open,
close,
cancel,
confirm
})
</script>
<style lang="scss" scoped>
.show{
transform: scale(1);
}
.none{
transform: scale(0);
}
.close {
position: absolute;
right: 30rpx;
top: 50%;
transform: translateY(-50%);
// background: #999999;
// border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
}
.model {
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
background: rgba(51, 51, 51, 0.5);
z-index: 980;
display: flex;
justify-content: center;
align-items: center;
padding: 0 46rpx;
.box {
width: 100%;
background-color: #fff;
border-radius: 36rpx 36rpx 36rpx 36rpx;
padding-top: 32rpx;
overflow: hidden;
max-width: 50vw;
.btns {
border-top: 2rpx solid #E5E5E5;
.cancel {
color: #666;
}
.confirm {
color: $my-main-color;
}
&>.btn {
padding: 30rpx;
flex: 1;
}
}
}
.bottom-box{
position: absolute;left: 0;
right: 0;
bottom: 0;
border-radius: 0;
padding-bottom: 100rpx;
border-radius: 36rpx 36rpx 0 0;
}
}
</style>

View File

@@ -0,0 +1,150 @@
<template>
<view v-if="pagesData.length">
<view class="u-flex pagination u-row-center u-font-28">
<view class="prve btn" :class="{disabled:currentPage===1}" @tap="btnClick('')">上一页</view>
<view>
<picker @change="pageChange" :value="defaultPaageIndex" :range="pagesData">
<view class="u-flex current-page u-col-center btn">
<text class="page color-main font-bold">{{currentPage}}</text>
<!-- <text class="page color-main font-bold">{{currentPage}}/{{maxPage}}</text> -->
<view class="arrow-down"></view>
</view>
</picker>
</view>
<!-- <view class="u-flex current-page u-col-center btn">
<text class="page color-main font-bold">{{page}}</text>
<view class="arrow-down"></view>
</view> -->
<view class="next btn" :class="{disabled:currentPage===maxPage}" @tap="btnClick('add')">下一页</view>
</view>
</view>
</template>
<script setup>
import {
computed,
reactive,
ref,
watch
} from 'vue';
import infoBox from '@/commons/utils/infoBox.js'
const props = defineProps({
size: {
type: Number,
default: 10
},
totalElements: {
type: Number,
default: 0
},
page: {
type: Number,
default: 1
},
pages: {
type: [Array, Number],
default: [1]
},
defaultPaageIndex: {
type: Number,
default: 0
}
})
const emits = defineEmits(['change','update:page'])
let currentPage = ref(props.page === 0 ? 1 : props.page)
function returnMaxPage() {
const result=Math.ceil(props.totalElements / props.size)
return result
}
let maxPage=ref(returnMaxPage())
const pagesData = computed(() => {
maxPage.value=returnMaxPage()
return new Array(returnMaxPage()).fill(1).map((v, index) => index + 1)
})
// const pagesData = ref( Array.isArray(props.pages) ? props.pages : new Array(props.pages).fill(1).map((v,index)=>index+1))
// const maxPage=ref( Array.isArray(props.pages)? (props.pages[props.pages.length - 1] +1): (props.pages+1))
let defaultPaageIndex = ref(props.defaultPaageIndex > 0 ? props.defaultPaageIndex : props.page - 1)
//设置页码值
function setCurrentPage(page) {
currentPage.value = page
}
function pageChange(e) {
setCurrentPage(e.detail.value * 1 + 1)
}
//看是否到达临界值来决定是否改变页码
function btnClick(isAdd) {
let newPage = currentPage.value * 1 + (isAdd == 'add' ? 1 : -1)
if (newPage <= 0) {
return infoBox.showToast('已经是第一页了')
}
if (newPage > maxPage.value) {
return infoBox.showToast('没有更多页了')
}
setCurrentPage(newPage)
}
watch(()=>props.page,(newval)=>{
currentPage.value =newval
})
watch(() => currentPage.value, (newval) => {
emits('change', newval)
emits('update:page', newval)
defaultPaageIndex.value = newval - 1
})
</script>
<style lang="scss" scoped>
.btn {
box-sizing: border-box;
border: 2px solid $my-main-color;
padding: 8rpx 46rpx;
}
.pagination {
gap: 30rpx;
.current-page {
position: relative;
padding-left: 18rpx;
.page {
margin-right: 74rpx;
}
}
.prve,
.next {
background-color: $my-main-color;
color: #fff;
}
.prve,.next {
&.disabled {
// background-color: #ccc;
// border-color: #ccc;
}
}
}
.arrow-down {
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 12rpx;
margin-top: 10rpx;
width: 0;
height: 0;
border-top: 20rpx solid $my-main-color;
border-right: 20rpx solid transparent;
border-left: 20rpx solid transparent;
border-bottom: 20rpx solid transparent;
}
</style>

View File

@@ -0,0 +1,344 @@
<template>
<view class="mask" v-if="show" @tap="close">
<view class="box" @tap.stop="nullFunction">
<view class="u-flex u-relative u-row-center u-p-30 top" v-if="props.showTitle">
<view class="font-bold u-font-32">{{props.title}}</view>
<view class="close" @tap="close">
<uni-icons type="closeempty" size="24"></uni-icons>
</view>
</view>
<!-- <view class="u-p-30 u-flex u-flex-wrap gap-20 fastTime">
<view class="item" v-for="(item,index) in fastTime" :key="index" @tap="changeTime(item.key)">
{{item.title}}
</view>
</view> -->
<picker-view indicator-class="activeClass" :immediate-change="true" @pickend="pickend" :value="value"
@change="bindChange" class="picker-view">
<template v-if="list.length">
<picker-view-column v-for="(arr,index) in list" :key="index">
<view class="item" v-for="(item,itemIndex) in arr" :key="itemIndex">
{{props.rangeKey? item[props.rangeKey]:item }}
</view>
</picker-view-column>
</template>
<!-- <template>
<picker-view-column v-if="props.list.length">
<view class="item" v-for="(item,index) in props.list" :key="index">
{{props.rangeKey? item[props.rangeKey]:item }}</view>
</picker-view-column>
</template> -->
</picker-view>
<!-- 站位 -->
<view style="height: 80px;"></view>
<view class="fixed_b u-flex u-row-between">
<my-button type="cancel" @tap="close" width="240">
<view class="color-999">取消</view>
</my-button>
<my-button @tap="confirm" width="240">确定</my-button>
</view>
</view>
</view>
</template>
<script setup>
import myButton from "@/components/my-components/my-button.vue"
import {
reactive,
nextTick,
ref
} from 'vue';
const props = defineProps({
autoClear: {
//是否自动清除选中
type: Boolean,
default: false
},
defaultIndex: {
type: Object,
default: () => {
return [0]
}
},
showTitle: {
type: Boolean,
default: true
},
list: {
//[name:'',value:'',children:[]]
type: Object,
default: () => {
return []
}
},
rangeKey: {
type: String
},
title: {
type: String,
default: '筛选日期时间'
},
isLink: {
type: Boolean,
default: true
},
mode: {
//all date time
type: String,
default: 'all'
},
showSeconds:{
type: Boolean,
default: false
}
})
function nullFunction() {
}
let list = ref([])
function isTowLvArr(arr) {
return arr.some(item => Array.isArray(item));
}
function returnNowArr(arr, indexArr) {
try{
if(!arr.length){
return []
}
let lv = 0
let result = [arr[0]]
function returnItem(item, selIndex) {
if (item.hasOwnProperty('children') && Array.isArray(item['children'])) {
lv++
result.push(item['children'])
returnItem(item['children'][selIndex], indexArr[lv] || 0)
}
}
returnItem(arr[0][indexArr[lv]], indexArr[lv])
return result
}catch(e){
console.error(`请绑定指定样式数据list:[{name:'',value:'',children:[
{name:'',value:''}
]}]`)
//TODO handle the exception
}
}
function setList() {
if (props.mode === 'time') {
const length=props.showSeconds?3:2
list.value = new Array(length).fill(1).map((v, index) => {
//时
if (index === 0) {
return new Array(24).fill(1).map((h, hIndex) => {
return ('0' + hIndex).slice(-2)
})
}
//分
if (index === 1) {
return new Array(60).fill(1).map((m, hIndex) => {
return ('0' + hIndex).slice(-2)
})
}
//秒
if (index === 2) {
return new Array(60).fill(1).map((s, hIndex) => {
return ('0' + hIndex).slice(-2)
})
}
})
return
}
const propArr = isTowLvArr(props.list) ? props.list : [props.list]
const indexArr = props.defaultIndex
const computedArr = props.isLink ? returnNowArr(propArr, indexArr) : propArr
list.value = computedArr||[]
}
setList()
let value = ref(props.defaultIndex)
function resetValue() {
value.value = value.value.map(v => 0)
}
function setValue() {
if(props.mode==='time'){
return value.value=props.defaultIndex
}
const arr = new Array(list.value.length).fill(0).map((v, index) => {
return props.defaultIndex[index] !== undefined ? props.defaultIndex[index] : v
})
value.value = props.defaultIndex.length === list.value.length ? props.defaultIndex : arr
}
setValue()
let show = ref(false)
const emits = defineEmits('close', 'open', 'confirm')
function toggle() {
show.value = !show.value
if (show.value) {
open()
} else {
close()
}
}
function close() {
show.value = false
if (props.autoClear) {
resetValue()
}
// emits('close', false)
}
function open() {
show.value = true
nextTick(()=>{
setValue()
})
// emits('open', true)
}
function getItem() {
let result = []
for (let i in value.value) {
result.push(list.value[i][value.value[i]])
}
return result
}
function confirm(e) {
const result = getItem()
emits('confirm', [...result])
close()
}
//防抖
function debounce(fn, wait) {
let timeout = null;
return function() {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
let callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait);
if (callNow) fn.apply(context, args);
};
}
function changeList() {
if (!props.isLink) {
return
}
const computedArr = returnNowArr(list.value, value.value)
list.value = computedArr
}
function bindChange(e) {
value.value = e.detail.value
debounce(changeList(), 100)
}
function pickend(e) {
console.log(e);
}
defineExpose({
close,
open,
confirm,
toggle,
resetValue
})
</script>
<style lang="scss" scoped>
.fastTime {
.item {
background-color: rgb(247, 247, 247);
padding: 6rpx 40rpx;
border-radius: 6rpx;
font-size: 32rpx;
}
}
.top {
border-bottom: 1px solid #eee;
}
.close {
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 30rpx;
}
.mask {
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
z-index: 999;
background-color: rgba(51, 51, 51, .5);
.activeClass {
font-size: 32rpx;
}
.item {
line-height: 34px;
text-align: center;
}
.box {
position: absolute;
background-color: #fff;
bottom: 0;
left: 0;
right: 0;
border-radius: 16rpx 16rpx 0 0;
}
}
.fixed_b {
position: absolute;
left: 0;
right: 0;
bottom: 0;
padding: 48rpx 120rpx;
padding-bottom: calc(env(safe-area-inset-bottom) + 48rpx);
z-index: 100;
background-color: #fff;
}
.picker-view {
width: 750rpx;
height: 300rpx;
}
</style>

View File

@@ -0,0 +1,96 @@
<template>
<view class="my-radio u-font-28 u-flex color-333" @tap.stop="changeVal">
<view class="circle u-flex u-row-center" :style="computedStyle()"
:class="{active:modelValue,square:shape==='square'}">
<uni-icons type="checkmarkempty" v-if="modelValue" :size="size-4" color="#fff"></uni-icons>
</view>
<view class="u-m-l-12">
<slot>{{text}}</slot>
</view>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
import color from '@/commons/color.js'
const props = defineProps({
disabled: {
type: [Boolean],
default: false
},
borderColor: {
type: String,
default: '#bbb',
},
size: {
//单位px
type: Number,
default: 16,
},
// v-modal
modelValue: {
type: [Number, Boolean],
default: false,
},
shape: {
//circle square
type: String,
default: 'circle',
},
text: {
type: String,
default: ''
}
})
function computedStyle() {
return `
width:${props.size}px;
height:${props.size}px;
border-color:${props.borderColor};
border-color:${props.modelValue?color.ColorMain:props.borderColor};
`
}
const emits = defineEmits(['update:modelValue', 'change'])
function changeVal() {
if (props.disabled) {
return
}
emits('click')
let currentVal = props.modelValue
let type = typeof currentVal
if (type === 'number') {
currentVal = currentVal === 0 ? 1 : 0
}
if (type === 'boolean') {
currentVal = !currentVal
}
emits('update:modelValue', currentVal)
emits('change', currentVal)
}
</script>
<style lang="scss" scoped>
.my-radio {
.circle {
background: #FFFFFF;
&.active {
background-color: $my-main-color;
border-color: $my-main-color;
}
border: 1px solid #707070;
border-radius: 50%;
overflow: hidden;
&.square {
border-radius: 8rpx;
}
}
}
</style>

View File

@@ -0,0 +1,345 @@
<template>
<up-popup customStyle="overflow: hidden;" :show="show" round="20" mode="bottom" @close="close" @open="open">
<view class="reportDamage">
<view class="reportDamage_head">
<view class="reportDamage_title">{{title}}</view>
<up-icon name="close-circle-fill" color="#333" size="25" @tap="close"></up-icon>
</view>
<view class="reportDamage_content">
<view class="reportDamage_cell">
<view class="cell_lable">
<up-image v-if="type=='product'" class="thumbnail" radius="10" :show-loading="true" :src="item.coverImg"></up-image>
<view>{{item.conName}}</view>
</view>
<view class="cell_value">
<up-icon name="minus-circle" color="#999" size="25" @tap="minus"></up-icon>
<view class="text">{{vdata.stockNumber}}</view>
<up-icon name="plus-circle-fill" color="#318AFE" size="25" @tap="plus"></up-icon>
</view>
</view>
<view class="reportDamage_cell">
<view class="cell_lable">商品单位</view>
<view class="cell_value"><view>{{item.conUnit}}</view> <up-icon name="arrow-right" color="#999999" size="15"></up-icon></view>
</view>
<view class="reportDamage_cell">
<view class="cell_lable">报损图片</view>
<view class="cell_value file">
<view class="file_img" v-for="(item,index) in vdata.imgUrlList">
<up-image class="file_img_item" :show-loading="true" :src="item"></up-image>
<view class="del" @tap="del(index)">
<up-icon name="trash" color="#fff" size="25" @tap="plus"></up-icon>
</view>
</view>
<view class="file" @tap="chooseAndUploadAvatar()">
<up-icon name="camera-fill" color="#E5E5E5" size="35"></up-icon>
</view>
</view>
</view>
</view>
<view class="reportDamage_footer">
<view class="reportDamage_btn" @tap="affirm">确认</view>
</view>
</view>
</up-popup>
</template>
<script setup>
import {
computed,
ref,
reactive,
onMounted,
watch
} from 'vue';
import { $uploadFile } from '@/http/yskApi/file.js'
import { consumableBreakage , productBreakage} from '@/http/yskApi/breakage.js'
const props = defineProps({
show:{
type: Boolean,
default: false
},
type:{
type: String,
default: ""
},
title:{
type: String,
default: ""
},
item:{
type: Object,
},
})
const emits = defineEmits(['close','open',"affirm"])
const vdata = reactive({
stockNumber: 1,
imgUrlList: [],
})
let show = ref(props.show)
let type = ref(props.type)
let itemData = ref(props.item)
watch(()=>props.show,(newval)=>{
show.value=newval
})
onMounted(() => {
})
function close() {
show.value = false;
vdata.imgUrlList = [];
vdata.stockNumber = 1;
emits('close')
}
/**
* 打开报损弹窗
*/
function open() {
show.value = true;
emits('open')
}
/**
* 报损数量减少
*/
function minus() {
if ( vdata.stockNumber <= 1) {
return;
}
vdata.stockNumber--;
}
/**
* 报损数量增加
*/
function plus() {
vdata.stockNumber++;
}
/**
* 删除报损图片
*/
function del ( index ) {
vdata.imgUrlList.splice(index,1)
}
/**
* 上传报损图片
*/
function chooseAndUploadAvatar () {
if ( vdata.imgUrlList.length >= 6 ) {
uni.showToast({
title:'最多只可以上传六张',
icon:'none'
})
return;
}
// 选择图片
uni.chooseImage({
count: 1, // 默认为1只选择一张图片
sizeType: ['original', 'compressed'], // 图片质量,原图或压缩
sourceType: ['album', 'camera'], // 图片来源,相册或相机
success: (res) => {
let file = res.tempFiles[0];
console.log(res)
$uploadFile(file).then(res => {
console.log(res);
vdata.imgUrlList.push(res.data[0])
}).catch(res=>{
console.log(res);
if(res.errMsg){
uni.showToast({
title:'图片大小超出限制',
icon:'error'
})
}
})
},
fail: chooseImageError => {
// 选择图片失败处理逻辑
console.log('choose image fail:', chooseImageError);
}
});
}
/**
* 确认
*/
function affirm () {
// emits('affirm')
if (vdata.imgUrlList.length <= 0) {
uni.showToast({
title:'请上传报损图片',
icon:'none'
})
return;
}
let params = {
coverImg: vdata.imgUrlList,
}
//商品报损
if ( type.value == 'consumable') {
params.consId = itemData.value.conIn;
params.amount = vdata.stockNumber;
}
consumableBreakage(params).then((res) => {
show.value = false;
vdata.imgUrlList = [];
vdata.stockNumber = 1;
})
}
defineExpose({
close,
open,
affirm
})
</script>
<style lang="scss">
.mask {
background-color: rgba(51, 51, 51, .5);
}
::v-deep .u-popup__content{
// background-color: transparent;
}
.reportDamage{
width: 100%;
display: flex;
flex-direction: column;
.reportDamage_head{
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx;
box-sizing: border-box;
background: #F4F4F4;
.reportDamage_title{
font-weight: bold;
font-size: 32rpx;
color: #333333;
}
}
.reportDamage_content{
display: flex;
flex-direction: column;
background-color: #fff;
.reportDamage_cell{
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx;
box-sizing: border-box;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
font-size: 28rpx;
color: #333333;
border-bottom: 2rpx solid #E5E5E5;
.cell_lable{
display: flex;
align-items: center;
flex-shrink: 0;
.thumbnail{
width: 112rpx;
height: 112rpx;
margin-right: 24rpx;
}
::v-deep .u-image,.u-image__loading,.u-image__image{
width: 100%!important;
height: 100%!important;
}
::v-deep uni-image{
width: 112rpx!important;
height: 112rpx!important;
}
}
.cell_value{
display: flex;
align-items: center;
justify-content: flex-end;
flex-wrap: wrap;
.text{
margin-left: 20rpx;
margin-right: 20rpx;
}
.file{
padding: 30rpx;
border-radius: 8rpx 8rpx 8rpx 8rpx;
border: 2rpx solid #E5E5E5;
box-sizing: border-box;
margin-left: 20rpx;
}
.file_img{
width: 120rpx;
height: 120rpx;
margin-left: 20rpx;
margin-bottom: 20rpx;
position: relative;
border-radius: 10rpx;
overflow: hidden;
.del{
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.2);
display: flex;
align-items: center;
justify-content: center;
}
.file_img_item{
width: 120rpx;
height: 120rpx;
}
::v-deep .u-image,.u-image__loading,.u-image__image{
width: 100%!important;
height: 100%!important;
}
::v-deep uni-image{
width: 120rpx!important;
height: 120rpx!important;
}
}
}
.cell_value.file{
// flex-direction: row-reverse;
// justify-content: flex-start;
}
}
.reportDamage_cell:last-child{
border-bottom: none;
}
}
.reportDamage_footer{
background-color: #fff;
padding: 32rpx;
padding-bottom: 68rpx;
box-sizing: border-box;
}
.reportDamage_btn{
width: 100%;
height: 80rpx;
line-height: 80rpx;
text-align: center;
background: #318AFE;
border-radius: 40rpx 40rpx 40rpx 40rpx;
font-weight: 500;
font-size: 32rpx;
color: #FFFFFF;
}
}
</style>

View File

@@ -0,0 +1,59 @@
<template>
<view class="search-box" :class="{'shape-circle':shape==='circle'}">
<view class="search-btn u-flex u-flex-1">
<image src="@/static/iconImg/icon-search.svg" class="icon-search" />
<view class="u-flex-1 u-p-l-24"><input v-model="search.keyword" @confirm="confirm" type="text"
placeholder-style="font-size:28rpx;" :placeholder="placeholder" /></view>
</view>
<view @tap.stop="clear" v-if="search.keyword">
<uni-icons type="clear" size="16" color="#999"></uni-icons>
</view>
</view>
</template>
<script setup>
import {
reactive
} from 'vue';
const props = defineProps({
placeholder: {
type:String,
default:'搜索'
},
shape:{
type:String,
default:''
}
})
const search = reactive({
keyword: "",
})
function clear() {
search.keyword = ''
}
const emits = defineEmits(['confirm'])
function confirm(e) {
emits('confirm', search.keyword)
}
</script>
<style lang="scss">
.search-box {
background: #F9F9F9;
padding: 10rpx 30rpx 10rpx 30rpx;
border-radius: 12rpx;
display: flex;
box-sizing: border-box;
&.shape-circle{
border-radius: 100px;
}
.icon-search {
width: 26rpx;
height: 26rpx;
}
}
</style>

View File

@@ -0,0 +1,95 @@
<template>
<view>
<view class="u-flex u-p-b-28 item" :class="{'active':active==index}" v-for="(item,index) in list" :key="index">
<view class="left ">
<view class="circle"></view>
<view class="left-line" :class="{hide:index==list.length-1}"></view>
</view>
<view class="u-p-l-12 ">
<view class="u-font-20">{{formatTitle(item[titleKey]||'') }}</view>
<view class="u-font-24 u-m-t-2">{{item[contentKey]||''}}</view>
</view>
</view>
</view>
</template>
<script setup>
import dayjs from 'dayjs';
const props = defineProps({
active: {
type: [String, Number],
default: 0
},
list: {
type: Array,
default: () => []
},
titleKey: {
type: String,
default: 'title'
},
contentKey: {
type: String,
default: 'content'
}
})
function formatTitle(time){
return dayjs(time).format('YYYY-M-D HH:MM:ss')
}
</script>
<style lang="scss" scoped>
$leftWidth: 12rpx;
$circleSize: 12rpx;
$lineWidth: 2rpx;
$circleLeft:0;
$lineLeft: calc($circleSize / 2 - $lineWidth / 2 );
.item {
color: #999;
position: relative;
.left {
height: 100%;
width: $leftWidth;
}
&.active {
color: $my-main-color;
.circle {
background: $my-main-color;
}
.left-line {
background-color: $my-main-color;
}
}
}
.circle {
position: absolute;
top: 6rpx;
left: $circleLeft;
width: $circleSize;
height: $circleSize;
border-radius: 50%;
background: #999;
margin: auto;
}
.left-line {
left: $lineLeft;
position: absolute;
bottom: 4rpx;
top: calc($circleSize + 10rpx);
width: $lineWidth;
background-color: #999;
border-radius: $lineWidth;
}
.hide {
opacity: 0;
}
</style>

View File

@@ -0,0 +1,163 @@
<template>
<view>
<view class="q-switch-box" :style="{
backgroundColor: modelValue ? activeBgColor : inActiveBgColor,
width: width,
height: height,
borderRadius: `calc(${height} / 2)`,
}" @click.stop="changeSwitch"
:class="{disabled:disabled&&openDisabledClass}"
>
<view v-show="!modelValue" class="before" :style="{
left: `calc(${margin} * 2)`,
}">
<slot name="left">{{ inShowText }}</slot>
</view>
<!-- 圆圈 -->
<view class="circle-inner" :style="{
margin: margin,
width: circleRadius,
height: circleRadius,
backgroundColor: '#ffffff',
...switchAnimation,
}"></view>
<view v-show="modelValue" class="after" :style="{
right: `calc(${margin} * 2)`,
}">
<slot name="right">{{ showText }}</slot>
</view>
</view>
</view>
</template>
<!-- 自定义Switch:自定义左右内容 -->
<script setup>
import {
computed,
ref
} from 'vue'
const props = defineProps({
disabled:{
type:Boolean,
default:false
},
openDisabledClass:{
type:Boolean,
default:true
},
height: {
type: String,
default: '40rpx',
},
width: {
type: String,
default: '78rpx',
},
margin: {
type: String,
default: '4rpx',
},
fontSize: {
type: String,
default: '32rpx',
},
// 激活背景色
activeBgColor: {
type: String,
default: '#318AFE',
},
// 未激活背景色
inActiveBgColor: {
type: String,
default: '#ECECEC',
},
// 激活文本
showText: {
type: String,
default: '',
},
// 未激活文本
inShowText: {
type: String,
default: '',
},
// v-modal
modelValue: {
type: [Boolean,Number],
default: false,
},
})
const emits = defineEmits(['update:modelValue', 'change','click'])
// 修改switch值
function changeSwitch() {
if(props.disabled){
emits('click')
return
}
let currentVal=props.modelValue
let type=typeof currentVal
if(type==='number'){
currentVal=currentVal===0?1:0
}
if(type==='boolean'){
currentVal=!currentVal
}
emits('update:modelValue', currentVal)
emits('change', currentVal)
}
// 圆圈半径
let circleRadius = computed(() => {
return `calc(((${props.height} / 2) - ${props.margin})* 2)`
})
// 动画效果
let switchAnimation = computed(() => {
let obj = {
transition: `transform 0.3s`,
transform: 'translateX(0)',
}
//激活
if (props.modelValue) {
let innerRadius = `((${props.height} / 2) - ${props.margin})` //圆圈半径
let moveValue =
`calc(${props.width} - ${props.margin} * 2 - (${innerRadius} * 2))` //偏移距离 总宽度 - 圆圈左右边距 - 圆圈宽度
// console.log('move-value', moveValue)
obj.transform = `translateX(${moveValue})`
} else {
// 未激活
obj.transform = 'translateX(0)'
}
return obj
})
</script>
<style lang="scss" scoped>
.q-switch-box {
display: flex;
background-color: #eaeaea;
position: relative;
height: 100%;
&.disabled{
opacity: .5;
}
.circle-inner {
border-radius: 50%;
}
.before {
position: absolute;
color: #bbbbbb;
font-size: 28rpx;
display: flex;
height: 100%;
align-items: center;
}
.after {
position: absolute;
font-size: 28rpx;
color: #ffffff;
display: flex;
height: 100%;
align-items: center;
}
}
</style>

View File

@@ -0,0 +1,144 @@
<template>
<view class="u-relative bg border-r-16">
<view class="u-flex tabs zhanwei u-relative">
<view class="active-block" :style="computedBlockStyle">
</view>
<view class="u-flex-1 u-text-center item" :class="[index===current?'':'active',size]" @tap="changeCurrent(index)"
v-for="(item,index) in props.list" :key="index">
{{textKey?item[textKey]:item}}
</view>
</view>
<view class="u-flex tabs u-absolute position-all">
<view class="u-flex-1 u-text-center item" :class="{active:index===current}" @tap="changeCurrent(index)"
v-for="(item,index) in props.list" :key="index">
{{textKey?item[textKey]:item}}
</view>
</view>
</view>
</template>
<script setup>
import {
computed,
ref,
watch
} from 'vue';
const props = defineProps({
size:{
type: String,
default:''
},
padding: {
type: String
},
list: {
type: Array
},
textKey: {
type: String,
default: ''
},
defaultIndex: {
type: Number,
default: 0
},
modelValue: {
type: Number,
default: 0
}
})
const emit = defineEmits(['change', 'update:modelValue'])
let current = ref(props.modelValue || props.defaultIndex || 0)
function changeCurrent(index) {
current.value = index
}
const computedBlockStyle = computed(() => {
const oneWidth = 100 / props.list.length
const left = current.value * oneWidth
return {
width: oneWidth + '%',
left: left + '%'
}
})
watch(() => props.modelValue, (newval) => {
current.value = newval
})
watch(() => current.value, (newval) => {
emit('update:modelValue', newval)
emit('change', newval)
})
</script>
<style lang="scss" scoped>
.zhanwei {
.item {
opacity: 0;
}
}
.bg {
background: #E6F0FF;
padding: 7rpx 10rpx;
}
.border-r-16 {
border-radius: 16rpx;
}
.active-block {
background-color: $my-main-color;
color: #fff;
top: 4rpx;
bottom: 4rpx;
left: 10rpx;
position: absolute;
border-radius: 8rpx;
transition: all .2s ease-in-out;
overflow: hidden;
z-index: 1;
}
.tabs {
border-radius: 16rpx;
font-size: 28rpx;
color: #318AFE;
z-index: 2;
.active-block {
background-color: $my-main-color;
color: #fff;
top: 4rpx;
bottom: 4rpx;
left: 0;
position: absolute;
border-radius: 8rpx;
transition: all .2s ease-in-out;
overflow: hidden;
z-index: 1;
}
.item {
padding: 9rpx 0;
transition: all .2s ease-in-out;
position: relative;
background-color: transparent;
z-index: 2;
&.large{
padding: 16rpx 0;
}
}
.item.active {
border-radius: 8rpx;
// background-color: $my-main-color;
color: #fff;
font-weight: bold;
}
}
</style>

View File

@@ -0,0 +1,9 @@
<template>
<view></view>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,49 @@
<template>
<myPickerview :list="list" :isLink="false" @confirm="confirm" ref="picker"></myPickerview>
</template>
<script setup>
import {
reactive,
ref
} from "vue";
import myPickerview from './my-pickerview'
const picker = ref(null)
function generateString(length, char) {
return char.repeat(length);
}
function createNumberArr(len) {
return new Array(len).fill(1).map((v, index) => {
return `${generateString(`${len}`.length-1,'0')}${index}`.slice(-2)
})
}
const $HOURS =createNumberArr(24)
const $MINUTES = createNumberArr(60)
const list = reactive(
[
$HOURS,
[':'],
$MINUTES,
['至'],
$HOURS,
[':'],
$MINUTES
]
)
const emits=defineEmits(['confirm'])
function open() {
picker.value.open()
}
function confirm(e){
const val=e.join('').replace('至','-')
emits('confirm',val)
}
defineExpose({
open
})
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,118 @@
<template>
<up-upload :fileList="images" @afterRead="afterRead" @delete="deletePic" :multiple="multiple" :width="width"
:height="height" :maxCount="maxCount"></up-upload>
</template>
<script setup>
import {
$uploadFile
} from '@/http/yskApi/file.js'
import {
ref,
watch
} from 'vue';
const props = defineProps({
modelValue: {
type: Array,
default: () => []
},
width: {
type: [String, Number],
default: 60
},
height: {
type: [String, Number],
default: 60
},
maxCount: {
type: [Number],
default: 10
},
multiple: {
type: Boolean,
default: true
}
})
const emits = defineEmits(['update:modelValue'])
const images = ref(props.modelValue)
function uploadfile(par){
let file=null;
// #ifdef H5
file= par.file
// #endif
// #ifndef H5
file= par
// #endif
return $uploadFile(file)
}
function afterRead(e) {
console.log(e);
if (Array.isArray(e.file)) {
for (let i in e.file) {
const file = e.file[i]
console.log(file);
uploadfile(file).then(res => {
console.log(res);
images.value.push({
url: e.file[i].url,
serveUrl: res.data[0]
})
}).catch(res => {
console.log(res);
if (res.errMsg) {
images.value.splice(i, 1)
uni.showToast({
title: '图片大小超出限制',
icon: 'error'
})
}
})
}
}else{
const i=0
uploadfile(e.file).then(res => {
console.log(res);
images.value.push({
url: e.file.url,
serveUrl: res.data[0]
})
}).catch(res => {
console.log(res);
if (res.errMsg) {
images.value.splice(i, 1)
uni.showToast({
title: '图片大小超出限制',
icon: 'error'
})
}
})
}
}
function deletePic(e) {
const {
index
} = e
images.value.splice(index, 1)
}
watch(() => images.value, (newval) => {
emits('update:modelValue', newval)
})
watch(() => props.modelValue, (newval) => {
images.value = newval
})
</script>
<style>
</style>

View File

@@ -0,0 +1,116 @@
<template>
<uni-file-picker v-model="imgList" file-mediatype="image" mode="grid" :limit="limit" @progress="FileUploadprogress"
:image-styles="imageStyles"
@delete="fileDelete" @success="FileUploadsuccess" @fail="FileUploadail" @select="FileUploadselect" />
</template>
<script setup>
import {
$uploadFile
} from '@/http/yskApi/file.js'
import {
reactive,
ref,
watch
} from 'vue';
const props = defineProps({
images: {
type: [Array,String],
default: () => {
return []
}
},
limit: {
type: Number,
default: 10
},
imageStyles:{
type:Object,
default:()=>{
return {
border:{
radius:'12rpx'
}
}
}
}
})
const emits = defineEmits(['change'])
const arr=props.images===''?[]:typeof props.images==='string'? [{path:props.images,url:props.images}]:props.images
let imgList = ref(arr)
let flag=false
const stopWatcher = watch(() => props.images, (newval) => {
if(!flag){
imgList.value = (typeof newval==='string'&&newval!=='')?[{path:newval,url:newval}]:newval
flag=true
}
})
watch(() => imgList.value.length, () => {
change()
})
//图片上传
function FileUploadprogress() {
}
function FileUploadsuccess() {
}
function FileUploadail() {
}
function FileUploadselect(e) {
for (let i in e.tempFiles) {
const file = e.tempFiles[i]
$uploadFile(file).then(res => {
console.log(res);
imgList.value.push({
url: res.data[0],
path: file.path
})
}).catch(res=>{
console.log(res);
if(res.errMsg){
imgList.value.splice(i,1)
uni.showToast({
title:'图片大小超出限制',
icon:'error'
})
}
})
}
}
function fileDelete(e) {
const index = imgList.value.findIndex(v => e.tempFilePath === v.path)
if (index != -1) {
imgList.value.splice(index, 1)
}
console.log(imgList.value);
}
function getFileList() {
if (props.limit === 1) {
const item=imgList.value[0]
return item===undefined?'':typeof item === 'string' ? item : item.url
}
return imgList.value.map(v => {
const url = v.url
return typeof v === 'string' ? v : url
})
}
function change() {
console.log(getFileList());
emits('change', getFileList())
}
defineExpose({
getFileList
})
</script>
<style>
</style>