Compare commits

..

57 Commits

Author SHA1 Message Date
e0858ea909 submit 2023-12-22 18:18:20 +08:00
6c2d306515 submit 2023-12-22 17:08:51 +08:00
94dc4c2a39 submit 2023-12-22 17:05:23 +08:00
b14826e6d1 submit 2023-12-22 15:17:22 +08:00
2c0698910e submit 2023-12-22 14:45:04 +08:00
731ad48407 submit 2023-12-21 14:30:16 +08:00
0d5038bffc submit 2023-12-21 11:04:17 +08:00
6dae410db8 submit 2023-12-21 09:46:15 +08:00
6553f4ac34 submit 2023-12-20 20:00:32 +08:00
95aa4241c0 submit 2023-12-20 15:35:08 +08:00
9b2bcbaeba submit 2023-12-20 15:07:23 +08:00
b3aeda2236 submit 2023-12-20 14:01:06 +08:00
1e0cdd5cde submit 2023-12-20 13:58:38 +08:00
780235f800 submit 2023-12-20 13:48:38 +08:00
e4a26b9013 submit 2023-12-20 13:41:25 +08:00
6a824c0b6c submit 2023-12-20 13:26:07 +08:00
ae4aa297c3 submit 2023-12-20 13:21:54 +08:00
de3915db25 submit 2023-12-20 13:19:54 +08:00
a30b63dd36 submit 2023-12-20 13:08:47 +08:00
206a594395 submit 2023-12-20 10:15:44 +08:00
f5986eeca5 submit 2023-12-20 10:05:14 +08:00
c015fe630d submit 2023-12-20 09:46:56 +08:00
c3ff3708c6 submit 2023-12-20 09:42:40 +08:00
fedcb33d15 submit 2023-12-20 09:37:55 +08:00
fa76fad14d submit 2023-12-19 20:02:07 +08:00
0dae8e26fb submit 2023-12-19 19:43:41 +08:00
5c1328bcea submit 2023-12-19 19:42:52 +08:00
e986e2917e submit 2023-12-19 19:08:55 +08:00
ee496f298d submit 2023-12-19 12:48:00 +08:00
fffbe3b63e submit 2023-12-19 10:18:08 +08:00
ad4d788585 submit 2023-12-18 20:00:31 +08:00
216aee9b4c submit 2023-12-18 17:37:46 +08:00
c544164e0e submit 2023-12-18 17:03:39 +08:00
db9bfe5fd4 submit 2023-12-18 15:50:44 +08:00
af55a106e3 submit 2023-12-16 11:34:16 +08:00
668a96a8ff submit 2023-12-16 10:15:22 +08:00
16d3488c80 submit 2023-12-16 09:37:49 +08:00
621f1d2f40 submit 2023-12-15 19:02:04 +08:00
3d280f21cf submit 2023-12-14 11:16:10 +08:00
77985bd2f3 submit 2023-12-14 11:12:48 +08:00
efad69a78b submit 2023-12-13 17:03:25 +08:00
70eb04db12 submit 2023-12-13 15:20:09 +08:00
95668bb1ad submit 2023-12-13 14:56:40 +08:00
acb4539165 submit 2023-12-12 16:39:02 +08:00
6276ee6465 submit 2023-12-12 14:39:39 +08:00
d134d3120e submit 2023-12-12 14:22:55 +08:00
a76d89c31b submit 2023-12-11 19:57:05 +08:00
b990b94490 submit 2023-12-11 18:46:53 +08:00
7a08be77a7 Merge remote-tracking branch 'origin/master' 2023-12-08 19:03:48 +08:00
acd816b2e3 submit 2023-12-08 19:03:40 +08:00
a45790073b fix 2023-12-08 18:22:05 +08:00
f62148c7f5 Merge branch 'master' of http://192.168.12.3:3000/xingyy/uni-ticket-system 2023-12-08 17:11:06 +08:00
9b2db7d33d fix 2023-12-08 17:10:52 +08:00
9148ba0877 submit 2023-12-08 16:59:43 +08:00
2822b072e9 fix 2023-12-08 16:48:11 +08:00
8ad6af6611 页面完成 2023-12-08 16:46:31 +08:00
23a2438a88 submit 2023-12-08 15:55:35 +08:00
55 changed files with 4694 additions and 2494 deletions

View File

@ -15,3 +15,4 @@
]
}

1666
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -49,8 +49,8 @@
"@dcloudio/uni-mp-xhs": "3.0.0-3090820231124001",
"@dcloudio/uni-quickapp-webview": "3.0.0-3090820231124001",
"@xingyy/uni-fetch": "^1.0.2",
"dayjs": "^1.11.10",
"echarts": "5.4.2",
"node-sass": "^9.0.0",
"pinia": " 2.0.33",
"sass": "^1.69.5",
"sass-loader": "^13.3.2",

View File

@ -1,17 +1,22 @@
<template>
</template>
<script lang="ts" setup></script>
<script setup>
import {onLaunch} from "@dcloudio/uni-app";
onLaunch(()=>{
console.log('onLaunch')
})
</script>
<style>
@import url('@/components/transition-min/transition.min.css');
/* #ifdef APP-PLUS-NVUE */
@import './tmui/scss/nvue.css';
/* #endif */
/* #ifndef APP-PLUS-NVUE */
@import './tmui/scss/noNvue.css';
/* #endif */
page{
display: flex;
}
</style>

View File

@ -0,0 +1,82 @@
<template>
<div class="content2" :style="{height: `${menuButtonTop}px`}">
<div class="wrap2" v-if="isBack" @click="goBack">
<image src="../../static/tbys@3x.png"></image>
</div>
<div class="wrap1" :style="{marginTop: `${titleTop}px`,height:`${titleHeight}px`}">{{title}}</div>
</div>
</template>
<script setup >
import { ref } from 'vue'
const menuButtonTop=ref(0)
const rpxToPx=(rpx)=>{
const systemInfo = uni.getSystemInfoSync();
const screenWidth = systemInfo.windowWidth;
return rpx * screenWidth / 750;
}
const titleTop=ref(0)
const titleHeight=ref(0)
const goBack=()=>{
uni.navigateBack({
delta: 1
});
}
const getMenuButtonBoundingClientRect=()=> {
// #ifdef MP-WEIXIN
const menuButtonInfo = uni.getMenuButtonBoundingClientRect();
if (menuButtonInfo) {
titleTop.value=menuButtonInfo.top
titleHeight.value=menuButtonInfo.height
menuButtonTop.value = menuButtonInfo.top + menuButtonInfo.height+rpxToPx(12);
}
}
getMenuButtonBoundingClientRect()
defineProps({
title: {
type: String,
default: ''
},
isBack: {
type: Boolean,
default: true
}
})
const back = () => {
uni.navigateBack({
delta: 2
});
}
</script>
<style scoped lang="scss">
.content2{
right: 0;
left: 0;
top: 0;
position: relative;
overflow: hidden;
width:100vw;
background-image: url("https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/6f9f30f6-9383-4ae6-872c-4e6795eaa25f.png");
.wrap2{
left: 42rpx;
bottom: 26rpx;
position: absolute;
width: 18rpx;
height: 34rpx;
image{
width: 100%;
height: 100%;
}
}
.wrap1{
font-size: 34rpx;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
}
</style>

View File

@ -1,19 +0,0 @@
<template>
<div class="content3">
</div>
</template>
<script setup>
</script>
<style scoped>
.content3{
height: 1rpx;
width: 100%;
background-image: url("../../static/zx303@3x (1).png");
background-size: 100%;
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<div class="content2">
<div class="content2" :style="styleColor">
<div class="wrap1" v-for="item in result">
<div class="wrap1_1">
<slot :name="Object.keys(item).find(x=>x.includes('l'))"></slot>
@ -11,8 +11,16 @@
</div>
</template>
<script setup >
import {useSlots,ref} from 'vue'
import {useSlots,ref,defineProps} from 'vue'
const slots = useSlots();
const prop=defineProps({
styleColor:{
type:Object,
default:()=>{
return {backgroundColor:'#fff'}
}
}
})
const groupObjectsByNumberKeys=(obj)=> {
const grouped = {};
for (const key in obj) {
@ -26,12 +34,8 @@ const groupObjectsByNumberKeys=(obj)=> {
}
const result = ref(groupObjectsByNumberKeys(slots))
</script>
<style scoped lang="scss">
.content2{
background-color: #fff;
border-radius: 20rpx;
padding-left: 18rpx;
padding-right: 32rpx;
@ -41,12 +45,20 @@ const result = ref(groupObjectsByNumberKeys(slots))
padding-bottom: 22rpx;
border-bottom: 1rpx solid #E4EAF1;;
display: flex;
&:last-child{
border-bottom: none;
}
.wrap1_2{
display: flex;
align-items: center;
flex-grow: 1;
padding-left: 36rpx;
font-size: 24rpx;
color: #939393;
}
.wrap1_1{
padding-right: 30rpx;
box-sizing: border-box;
display: flex;
align-items: center;
width: 192rpx;

View File

@ -1,31 +1,79 @@
<template>
<tm-tabbar :autoSelect="false" v-model:active="acc">
<tm-tabbar-item
@click="acc = 0"
activeColor="#EB783C"
open-type="reLaunch"
text="选票"
>
<div style="width: 52rpx;height: 52rpx">
<img v-if="acc===0" style="width: 100%;height: 100%" src="../../static/zu618.png" alt="">
<img v-else style="width: 100%;height: 100%" src="../../static/zu759@3x (1).png" alt="">
<div class="main">
<div class="content1" @click="goContent" >
<div class="wrap1">
<div class="wrap1_1">
<img v-show="acc===0" class="ld ld-fall-ttb-in" style="width: 52rpx;height:52rpx" src="../../static/zu618.png" alt="">
<img v-show="acc!==0" style="width: 52rpx;height:52rpx" src="../../static/zu759@3x.png" alt="">
</div>
<div :style="{color: acc===0?'#EB783C':'#000'}" class="wrap1_2">
选票
</div>
</div>
<div class="wrap2">
<div class="wrap2_1">
<img v-show="acc===0" style="width: 52rpx;height:52rpx" src="../../static/zu628.png" alt="">
<img v-show="acc!==0" class="ld ld-fall-ttb-in" style="width: 52rpx;height:52rpx" src="../../static/zu-44.png" alt="">
</div>
<div :style="{color: acc===1?'#EB783C':'#000'}" class="wrap2_2">我的</div>
</div>
</div>
</tm-tabbar-item>
<tm-tabbar-item @click="acc = 1" activeColor="#EB783C" text="我的">
<div style="width: 52rpx;height: 52rpx">
<img v-if="acc===0" style="width: 100%;height: 100%" src="../../static/zu628.png" alt="">
<img v-else style="width: 100%;height: 100%" src="../../static/zu-44.png" alt="">
</div>
</tm-tabbar-item>
</tm-tabbar>
</template>
<script setup>
import {ref, defineEmits, watch,} from 'vue'
import {ref, defineEmits, watch} from 'vue'
const emit=defineEmits(['update:modelValue'])
const acc = ref(1)
const goContent=(event)=>{
const windowWidth = uni.getSystemInfoSync().windowWidth;
if (event.changedTouches[0].clientX<windowWidth/2){
acc.value=0
}else {
acc.value=1
}
}
const acc = ref(0)
watch(acc,()=>{
emit('update:modelValue',acc.value)
})
</script>
<style scoped lang="scss">
.main{
box-shadow: 0px -15rpx 30rpx 0px rgba(0, 0, 0, 0.1);
position: fixed;
bottom: 0;
z-index: 9;
height: 170rpx;
width: 750rpx;
background-color: #fff;
.content1{
padding-right: 222rpx;
padding-left: 222rpx;
height: 100%;
display: flex;
align-items: center;
.wrap1{
display: flex;
flex-direction: column;
align-items: center;
margin-right: 206rpx;
.wrap1_2{
margin-top: 4rpx;
color: #000;
font-size: 20rpx;
}
}
.wrap2{
margin-right: 206rpx;
display: flex;
flex-direction: column;
align-items: center;
.wrap2_2{
margin-top: 4rpx;
color: #000;
font-size: 20rpx;
}
}
}
}
</style>

View File

@ -1,61 +0,0 @@
<template>
<div class="content1" :style="{ marginTop: `${statusBarHeight}px` }">
<div class="wrap1" v-if="isBack">
<tm-icon name="tmicon-angle-left" color="#FFFFFF" @click="back"></tm-icon>
</div>
<div class="wrap2">{{ title }}</div>
</div>
</template>
<script setup >
import { ref } from 'vue'
const statusBarHeight = ref(uni.getSystemInfoSync().statusBarHeight + 5)
defineProps({
title: {
type: String,
default: ''
},
isBack: {
type: Boolean,
default: true
}
})
const back = () => {
uni.navigateBack({
delta: 2
});
}
</script>
<style scoped lang="scss">
.content1 {
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
.wrap1 {
flex-basis: 0;
width: 34rpx;
height: 34rpx;
display: flex;
}
.wrap3 {
flex-grow: 1;
flex-basis: 0;
}
.wrap2 {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
flex-grow: 1;
flex-basis: 0;
color: #FFFFFF;
font-size: 32rpx;
}
}
</style>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,131 @@
// const defaultOption = {
// duration: 300,
// timingFunction: 'linear',
// delay: 0,
// transformOrigin: '50% 50% 0'
// }
// #ifdef APP-NVUE
const nvueAnimation = uni.requireNativePlugin('animation')
// #endif
class MPAnimation {
constructor(options, _this) {
this.options = options
// 在iOS10+QQ小程序平台下传给原生的对象一定是个普通对象而不是Proxy对象否则会报parameter should be Object instead of ProxyObject的错误
this.animation = uni.createAnimation({
...options
})
this.currentStepAnimates = {}
this.next = 0
this.$ = _this
}
_nvuePushAnimates(type, args) {
let aniObj = this.currentStepAnimates[this.next]
let styles = {}
if (!aniObj) {
styles = {
styles: {},
config: {}
}
} else {
styles = aniObj
}
if (animateTypes1.includes(type)) {
if (!styles.styles.transform) {
styles.styles.transform = ''
}
let unit = ''
if(type === 'rotate'){
unit = 'deg'
}
styles.styles.transform += `${type}(${args+unit}) `
} else {
styles.styles[type] = `${args}`
}
this.currentStepAnimates[this.next] = styles
}
_animateRun(styles = {}, config = {}) {
let ref = this.$.$refs['ani'].ref
if (!ref) return
return new Promise((resolve, reject) => {
nvueAnimation.transition(ref, {
styles,
...config
}, res => {
resolve()
})
})
}
_nvueNextAnimate(animates, step = 0, fn) {
let obj = animates[step]
if (obj) {
let {
styles,
config
} = obj
this._animateRun(styles, config).then(() => {
step += 1
this._nvueNextAnimate(animates, step, fn)
})
} else {
this.currentStepAnimates = {}
typeof fn === 'function' && fn()
this.isEnd = true
}
}
step(config = {}) {
// #ifndef APP-NVUE
this.animation.step(config)
// #endif
// #ifdef APP-NVUE
this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config)
this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin
this.next++
// #endif
return this
}
run(fn) {
// #ifndef APP-NVUE
this.$.animationData = this.animation.export()
this.$.timer = setTimeout(() => {
typeof fn === 'function' && fn()
}, this.$.durationTime)
// #endif
// #ifdef APP-NVUE
this.isEnd = false
let ref = this.$.$refs['ani'] && this.$.$refs['ani'].ref
if(!ref) return
this._nvueNextAnimate(this.currentStepAnimates, 0, fn)
this.next = 0
// #endif
}
}
const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d',
'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY',
'translateZ'
]
const animateTypes2 = ['opacity', 'backgroundColor']
const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom']
animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => {
MPAnimation.prototype[type] = function(...args) {
// #ifndef APP-NVUE
this.animation[type](...args)
// #endif
// #ifdef APP-NVUE
this._nvuePushAnimates(type, args)
// #endif
return this
}
})
export function createAnimation(option, _this) {
if(!_this) return
clearTimeout(_this.timer)
return new MPAnimation(option, _this)
}

View File

@ -0,0 +1,286 @@
<template>
<!-- #ifndef APP-NVUE -->
<view v-show="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
<!-- #endif -->
</template>
<script>
import { createAnimation } from './createAnimation'
/**
* Transition 过渡动画
* @description 简单过渡动画组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=985
* @property {Boolean} show = [false|true] 控制组件显示或隐藏
* @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
* @value fade 渐隐渐出过渡
* @value slide-top 由上至下过渡
* @value slide-right 由右至左过渡
* @value slide-bottom 由下至上过渡
* @value slide-left 由左至右过渡
* @value zoom-in 由小到大过渡
* @value zoom-out 由大到小过渡
* @property {Number} duration 过渡动画持续时间
* @property {Object} styles 组件样式 css 样式注意带-连接符的属性需要使用小驼峰写法如`backgroundColor:red`
*/
export default {
name: 'uniTransition',
emits:['click','change'],
props: {
show: {
type: Boolean,
default: false
},
modeClass: {
type: [Array, String],
default() {
return 'fade'
}
},
duration: {
type: Number,
default: 300
},
styles: {
type: Object,
default() {
return {}
}
},
customClass:{
type: String,
default: ''
},
onceRender:{
type:Boolean,
default:false
},
},
data() {
return {
isShow: false,
transform: '',
opacity: 1,
animationData: {},
durationTime: 300,
config: {}
}
},
watch: {
show: {
handler(newVal) {
if (newVal) {
this.open()
} else {
// close,
if (this.isShow) {
this.close()
}
}
},
immediate: true
}
},
computed: {
//
stylesObject() {
let styles = {
...this.styles,
'transition-duration': this.duration / 1000 + 's'
}
let transform = ''
for (let i in styles) {
let line = this.toLine(i)
transform += line + ':' + styles[i] + ';'
}
return transform
},
//
transformStyles() {
return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject
}
},
created() {
//
this.config = {
duration: this.duration,
timingFunction: 'ease',
transformOrigin: '50% 50%',
delay: 0
}
this.durationTime = this.duration
},
methods: {
/**
* ref 触发 初始化动画
*/
init(obj = {}) {
if (obj.duration) {
this.durationTime = obj.duration
}
this.animation = createAnimation(Object.assign(this.config, obj),this)
},
/**
* 点击组件触发回调
*/
onClick() {
this.$emit('click', {
detail: this.isShow
})
},
/**
* ref 触发 动画分组
* @param {Object} obj
*/
step(obj, config = {}) {
if (!this.animation) return
for (let i in obj) {
try {
if(typeof obj[i] === 'object'){
this.animation[i](...obj[i])
}else{
this.animation[i](obj[i])
}
} catch (e) {
console.error(`方法 ${i} 不存在`)
}
}
this.animation.step(config)
return this
},
/**
* ref 触发 执行动画
*/
run(fn) {
if (!this.animation) return
this.animation.run(fn)
},
//
open() {
clearTimeout(this.timer)
this.transform = ''
this.isShow = true
let { opacity, transform } = this.styleInit(false)
if (typeof opacity !== 'undefined') {
this.opacity = opacity
}
this.transform = transform
// nextTick wx
this.$nextTick(() => {
// TODO
this.timer = setTimeout(() => {
this.animation = createAnimation(this.config, this)
this.tranfromInit(false).step()
this.animation.run()
this.$emit('change', {
detail: this.isShow
})
}, 20)
})
},
//
close(type) {
if (!this.animation) return
this.tranfromInit(true)
.step()
.run(() => {
this.isShow = false
this.animationData = null
this.animation = null
let { opacity, transform } = this.styleInit(false)
this.opacity = opacity || 1
this.transform = transform
this.$emit('change', {
detail: this.isShow
})
})
},
//
styleInit(type) {
let styles = {
transform: ''
}
let buildStyle = (type, mode) => {
if (mode === 'fade') {
styles.opacity = this.animationType(type)[mode]
} else {
styles.transform += this.animationType(type)[mode] + ' '
}
}
if (typeof this.modeClass === 'string') {
buildStyle(type, this.modeClass)
} else {
this.modeClass.forEach(mode => {
buildStyle(type, mode)
})
}
return styles
},
//
tranfromInit(type) {
let buildTranfrom = (type, mode) => {
let aniNum = null
if (mode === 'fade') {
aniNum = type ? 0 : 1
} else {
aniNum = type ? '-100%' : '0'
if (mode === 'zoom-in') {
aniNum = type ? 0.8 : 1
}
if (mode === 'zoom-out') {
aniNum = type ? 1.2 : 1
}
if (mode === 'slide-right') {
aniNum = type ? '100%' : '0'
}
if (mode === 'slide-bottom') {
aniNum = type ? '100%' : '0'
}
}
this.animation[this.animationMode()[mode]](aniNum)
}
if (typeof this.modeClass === 'string') {
buildTranfrom(type, this.modeClass)
} else {
this.modeClass.forEach(mode => {
buildTranfrom(type, mode)
})
}
return this.animation
},
animationType(type) {
return {
fade: type ? 1 : 0,
'slide-top': `translateY(${type ? '0' : '-100%'})`,
'slide-right': `translateX(${type ? '0' : '100%'})`,
'slide-bottom': `translateY(${type ? '0' : '100%'})`,
'slide-left': `translateX(${type ? '0' : '-100%'})`,
'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`,
'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})`
}
},
//
animationMode() {
return {
fade: 'opacity',
'slide-top': 'translateY',
'slide-right': 'translateX',
'slide-bottom': 'translateY',
'slide-left': 'translateX',
'zoom-in': 'scale',
'zoom-out': 'scale'
}
},
// 线
toLine(name) {
return name.replace(/([A-Z])/g, '-$1').toLowerCase()
}
}
}
</script>
<style></style>

93
src/http/apis.js Normal file
View File

@ -0,0 +1,93 @@
import uniReq from '@/http/init'
export const login = (data) => {
return uniReq.post({
url: '/api/user/login/wx/telnum',
data
})
}
export const getInfo = () => {
return uniReq.post({
url: '/api/user/info'
})
}
export const ticketlist = (data) => {
return uniReq.post({
url: '/api/ticket/ticketList',
isLoading:false,
data
})
}
export const unusedTickets = (data) => {
return uniReq.post({
url: '/api/smart/appointment/get/all/unused/tickets',
isLoading:false,
data
})
}
export const historicalTickets = (data) => {
return uniReq.post({
url: '/api/smart/appointment/get/all/historical/tickets',
isLoading:false,
data
})
}
export const sendCode = (data) => {
return uniReq.post({
url: '/api/user/send/msg',
data
})
}
export const reTicket = (data) => {
return uniReq.post({
url: '/api/smart/appointment/book/ticket',
data
})
}
export const upload = (data) => {
return uniReq.upload({
name: data.name,
url: '/api/common/upload',
filePath: data.filePath,
interceptor: {
response(res) {
try {
return JSON.parse(res.data)
} catch (e) {
return e
}
}
}
})
}
export const updateInfo = (data) => {
return uniReq.post({
url: '/api/user/update',
data
})
}
export const extractingBlindBoxes= (data) => {
return uniReq.post({
url: '/api/smart/appointment/draw/ticket/from/blind/box',
data,
isShowMsg:false,
})
}
export const getQrCode= (data) => {
return uniReq.get({
url: '/api/smart/appointment/qr/get',
params:data,
responseType: 'arraybuffer'
})
}
export const getTicketPerInfo= (data) => {
return uniReq.post({
url: '/api/smart/appointment/get/ticket/info',
data
})
}
export const socketImg= (data) => {
return uniReq.post({
url: '/api/smart/appointment/pass/picUrl',
data
})
}

View File

@ -1,12 +0,0 @@
import http from "./interface";
// 地点
export const address = () => {
return http.request({
url: "/api/warehouse/address",
method: "POST",
});
};
export default {
address,
};

View File

@ -1,6 +0,0 @@
import deposit from "./deposit";
import login from "./login";
export default {
deposit,
login,
};

40
src/http/init.js Normal file
View File

@ -0,0 +1,40 @@
import {uniRequest} from "@/http/main";
const TEST_URL='http://172.16.100.93:9052'
const TY_URL='https://warehouse.szjixun.cn/ticket'
let configV
const uniReq=uniRequest.created({
baseUrl: TY_URL,
interceptor: {
request(config){
configV=config
config.header.Authorization=uni.getStorageSync('token')??''
if (config.isLoading){
uni.showLoading({
title: '加载中...',
mask: true
})
setTimeout(()=>{
uni.hideLoading()
},5000)
}
return config
},
response(response) {
if (configV.isShowMsg){
if (response.data.code!==200){
uni.showToast({
title: response.data.msg,
icon: 'none',
duration: 1300
})
}
}
if (configV.isLoading){
uni.hideLoading()
}
return response.data
}
}
})
export default uniReq

View File

@ -1,24 +0,0 @@
import {uniFetch} from "@/http/main";
const fetch = new uniFetch({
baseUrl: 'https://warehouse.szjixun.cn',
interceptors: {
//请求拦截器
request(config) {
return config
},
//响应拦截器
response(response) {
if (response.data?.status === 401) {
let curPage = getCurrentPages();
let route = curPage[curPage.length - 1].route; //获取当前页面的路由
if (route !== "pages/login/login") {
uni.navigateTo({
url: "/pages/login/login",
});
}
}
return response
}
}
})
export default fetch

View File

@ -1,57 +0,0 @@
import fetch from "@/http/init";
// openId
export const login = (data) => {
return fetch.request({
url: "/api/wxuser/openid",
method: "POST",
data,
});
};
// 获取手机号
export const getTel = (data) => {
return fetch.request({
url: "/api/wxuser/get/telnum",
method: "POST",
data,
});
};
// 注册
export const register = (data) => {
return fetch.request({
url: "/api/wxuser/register",
method: "POST",
data,
});
};
// 身份验证
export const chenckId = (data) => {
return fetch.request({
url: "/api/wxuser/ocr",
method: "POST",
data,
});
};
// 法大大
export const fddRealName = (data) => {
return fetch.request({
url: "/api/wxuser/bind/fdd",
method: "POST",
data,
});
};
//法大大是否验证
export const checkFdd = () => {
return fetch.request({
url: "/api/wxuser/fdd/check",
method: "POST",
});
};
export default {
login,
getTel,
register,
chenckId,
fddRealName,
checkFdd,
};

View File

@ -1,51 +1,86 @@
type HttpMethod =
| "GET"
| 'GET'
| 'get'
| "POST"
| 'POST'
| 'post'
| "PUT"
| 'PUT'
| 'put'
| "DELETE"
| 'DELETE'
| 'delete'
| "PATCH"
| 'patch'
| "OPTIONS"
| 'CONNECT'
| 'connect'
| 'OPTIONS'
| 'options'
| "HEAD"
| 'head'
| "TRACE"
| 'trace'
| "CONNECT"
| 'connect';
| 'TRACE'
| 'trace';
interface RequestOptions {
baseUrl?: string;
url: string;
url?: string;
isLoading?: boolean;
isShowMsg?: boolean;
data?: Record<string, any>;
method?: HttpMethod;
header?: Record<string, string>;
params?: Record<string, any>;
timeout?: number,
dataType?: string,
responseType?: string,
sslVerify?: boolean,
withCredentials?: boolean,
firstIpv4?: boolean,
enableHttp2?: boolean,
enableQuic?: boolean,
enableCache?: boolean,
enableHttpDNS?: boolean,
httpDNSServiceId?: string,
enableChunked?: boolean,
forceCellularNetwork?: boolean,
enableCookie?: boolean,
cloudCache?: object | boolean,
defer?: boolean,
interceptor?: {
request?: RequestInterceptor,
response?: ResponseInterceptor
}
}
interface UploadOptions {
url: string,
baseUrl?: string,
files?: File[],
fileType?: 'image' | 'video' | 'audio',
file?: File,
filePath?: string,
name?: string,
header?: object,
timeout?: number,
formData?: object,
interceptor?: {
request?: (config: UploadOptions) => UploadOptions,
response?: (response: any) => any;
}
}
type RequestInterceptor = (config: RequestOptions) => RequestOptions;
type ResponseInterceptor = (response: any) => any;
interface Interceptors {
request: (config: RequestOptions) => RequestOptions;
response: (response: any) => any;
}
class uniFetch {
baseUrl: string;
defaultHeader: Record<string, string>;
interceptors: { request: RequestInterceptor | null; response: ResponseInterceptor | null };
constructor({ baseUrl = '', defaultHeader = {}, interceptors = { request: null, response: null } }: {
class uniRequest {
baseUrl?: string;
defaultHeader?: Record<string, string>;
interceptors?: Interceptors;
}) {
this.baseUrl = baseUrl;
isLoading: boolean;
isShowMsg: boolean;
defaultHeader: Record<string, string>;
interceptors: { request?: RequestInterceptor; response?: ResponseInterceptor };
constructor(request: RequestOptions) {
this.isLoading= request.isLoading??true
this.isShowMsg= request.isShowMsg??true
this.baseUrl = request.baseUrl;
this.defaultHeader = {
"Content-Type": "application/json;charset=UTF-8",
...defaultHeader,
...request.header,
};
this.interceptors = interceptors;
this.interceptors = {request: request.interceptor?.request, response: request.interceptor?.response};
}
setBaseUrl(baseUrl: string): void {
@ -55,22 +90,30 @@ class uniFetch {
setDefaultHeader(header: Record<string, string>): void {
this.defaultHeader = header;
}
static created(options: RequestOptions) {
return new uniRequest(options);
}
request(options: RequestOptions): Promise<any> {
options = this.buildRequestOptions(options)
options = options || {};
options.isLoading ??= this.isLoading;
options.isShowMsg ??= this.isShowMsg;
options.baseUrl = options.baseUrl || this.baseUrl;
options.url = options.baseUrl + options.url;
options.url = `${options.baseUrl}${options.url}`;
options.data = options.data || {};
options.method = options.method || "GET";
options.method = (options.method?.toUpperCase() || "GET") as HttpMethod;
options.header = options.header || this.defaultHeader;
if (this.interceptors.request) {
if (typeof options.interceptor?.request === 'function') {
options = options.interceptor.request(options);
} else if (typeof this.interceptors?.request === 'function') {
options = this.interceptors.request(options);
}
return new Promise((resolve, reject) => {
// @ts-ignore
uni.request({
...options,
success: (res) => {
const response = this.handleResponse(res);
const response = this.handleResponse(res, options);
resolve(response);
},
fail: (err) => {
@ -81,19 +124,20 @@ class uniFetch {
});
}
private handleResponse(response: any): any {
if (this.interceptors.response) {
response = this.interceptors.response(response);
private handleResponse(response: any, options: RequestOptions): any {
if (options.interceptor?.response) {
response = options.interceptor?.response(response);
} else if (this.interceptors?.response) {
response = this.interceptors?.response(response);
}
const statusCode = response.statusCode;
if (statusCode === 200) {
return response.data;
} else {
throw response;
return response
}
private handleUploadResponse(res: any, options: UploadOptions){
if (options.interceptor?.response){
res = options.interceptor?.response(res);
}
return res
}
private handleError(error: any): any {
throw error;
}
@ -111,31 +155,52 @@ class uniFetch {
options.method = "GET";
return this.request(this.buildRequestOptions(options));
}
upload(options: UploadOptions) {
options.url = `${options.baseUrl ?? this.baseUrl}${options.url}`;
if (typeof options.interceptor?.request==='function'){
options = options.interceptor.request(options);
}
return new Promise((resolve, reject) => {
uni.uploadFile({
...options,
success: (res) => {
resolve(this.handleUploadResponse(res, options))
},
fail(res){
reject(res)
}
});
})
}
private buildRequestOptions(options: RequestOptions): RequestOptions {
if (options.params) {
if (options.params && Object.keys(options.params).length > 0) {
const queryString = Object.keys(options.params)
.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(options.params[key])}`)
.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(options.params![key])}`)
.join('&');
options.url += `?${queryString}`;
}
delete options.params;
return options;
}
post(options: RequestOptions): Promise<any> {
options = options || {};
options.method = "POST";
return this.request(this.buildRequestOptions(options));
}
put(options: RequestOptions): Promise<any> {
options = options || {};
options.method = "PUT";
return this.request(this.buildRequestOptions(options));
}
delete(options: RequestOptions): Promise<any> {
options = options || {};
options.method = "DELETE";
return this.request(this.buildRequestOptions(options));
}
}
export { uniFetch}
export {uniRequest}

View File

@ -1,17 +1,18 @@
import { createSSRApp } from "vue";
import * as Pinia from "pinia";
import tmui from "./tmui";
import App from "./App.vue";
// @ts-ignore
import api from "@/http";
import title from "./components/Title/index.vue";
import { createPinia } from 'pinia'
import customTitle from "./components/custom-title/index.vue";
import uniTransition from "@/components/uni-transition/uni-transition.vue";
export function createApp() {
const app = createSSRApp(App);
app.config.globalProperties.$api = api;
app.component("title", title);
const pinia = createPinia()
app.use(pinia)
app.component("customTitle", customTitle);
app.component("uniTransition", uniTransition);
app.use(tmui, { shareDisable: false } as Tmui.tmuiConfig);
return {
app,
Pinia,
pinia
};
}

View File

@ -97,7 +97,7 @@
},
"quickapp": {},
"mp-weixin": {
"appid": "",
"appid": "wx7da502ffb626aa8a",
"darkmode": false,
"setting": {
"urlCheck": false,

View File

@ -5,9 +5,58 @@
"^tm-(.*)": "@/tmui/components/tm-$1/tm-$1.vue"
}
},
"pages": [{
"pages": [
{
"path": "pages/index/index",
"style": {
"disableScroll": true,
"navigationStyle": "custom",
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"app-plus": {
"titleNView": false //
}
}
},
{
"path": "pages/blind-box/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"app-plus": {
"titleNView": false //
}
}
},
{
"path": "pages/facial/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"app-plus": {
"titleNView": false //
}
}
},
{
"path": "pages/face-auth/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"app-plus": {
"titleNView": false //
}
}
},
{
"path": "pages/home/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"app-plus": {
@ -26,9 +75,32 @@
}
}
},
{
"path": "pages/test/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"app-plus": {
"titleNView": false //
}
}
},
{
"path": "pages/login/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"app-plus": {
"titleNView": false //
}
}
},
{
"path": "pages/ticket-details/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"app-plus": {
@ -39,6 +111,7 @@
{
"path": "pages/setup/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"app-plus": {
@ -48,6 +121,18 @@
},
{
"path": "pages/view-venues/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"app-plus": {
"titleNView": false //
}
}
},
{
"path": "pages/mine/index",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
@ -65,10 +150,12 @@
},
"condition": {
"current": 0,
"list": [{
"list": [
{
"name": "",
"path": "",
"query": ""
}]
}
]
}
}

View File

@ -0,0 +1,293 @@
<template>
<div class="container">
<custom-title class="title-block" title="大运河博物馆纪念盲盒">
</custom-title>
<div class="main">
<div class="content1">
<div class="wrap1">
<image src="https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/78a61919-6e1e-4b5d-96cc-1ecd94c00236.png"></image>
</div>
<div class="wrap2">大运河博物馆纪念盲盒</div>
</div>
<div class="content2">
<image src="https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/13e8a0db-9dca-4bdf-89d5-5868ec7c96e0.png"></image>
</div>
<div class="content3" @click="goGet">领取兑换</div>
<div class="content4"></div>
<div class="content5">
<div class="wrap1" v-for="item in imgList" :key="item.url">
<image :src="item.url"></image>
</div>
</div>
<tm-drawer hideHeader color="#B1292E" :width="510" :height="324" ref="calendarView" placement="center" v-model:show="showWin2">
<div class="content6">
<div class="wrap1">领取盲盒失败</div>
<div class="wrap2">失败原因未实名</div>
<div class="wrap3" @click="goRealName">前往实名</div>
<div class="wrap4">*即将跳转实名页面</div>
</div>
</tm-drawer>
<tm-drawer hideHeader :width="510" :height="324" ref="calendarView" placement="center" v-model:show="showWin3">
<div class="content7">
<div class="wrap1">领取失败</div>
<div class="wrap2">失败原因{{errMsg}}</div>
<div class="wrap3" @click="goHome">确定</div>
<div class="wrap4">**即将返回首页</div>
</div>
</tm-drawer>
<tm-drawer hideHeader :width="510" :height="636" ref="calendarView" placement="center" v-model:show="showWin4">
<div class="content8">
<div class="wrap1">领取成功</div>
<div class="wrap2">1人1码请与工作人员核验领取</div>
<div class="wrap3">
<image :src="imageSrc"></image>
</div>
<div class="wrap4" @click="showWin4=false">确定</div>
<div class="wrap5">*可从我的票库查看</div>
</div>
</tm-drawer>
</div>
</div>
</template>
<script setup>
import {ref} from 'vue'
import {extractingBlindBoxes, getQrCode} from "@/http/apis";
import {onShow} from "@dcloudio/uni-app";
const imgList=ref([{url:'https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/8395f322-b677-4f24-a13d-b79474c09d35.png'},{url:'https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/8395f322-b677-4f24-a13d-b79474c09d35.png'}])
const showWin2=ref(false)
const showWin3=ref(false)
const showWin4=ref(false)
const userInfo =()=>{
return uni.getStorageSync('userInfo')
}
onShow(()=>{
showWin2.value=false
showWin3.value=false
showWin4.value=false
})
const goHome=()=>{
uni.navigateTo({
url: '/pages/index/index'
})
}
const goRealName=()=>{
uni.navigateTo({
url: '/pages/facial/index'
})
}
const errMsg=ref('')
const imageSrc=ref('')
const goGet=async ()=>{
if (!userInfo().idNum){
showWin2.value=true
return
}
const data={
"userName": userInfo().realName, //
"idCard": userInfo().idNum, //
"phone": userInfo().telNum, //
"blindBoxName": "大运河博物馆纪念盲盒", //
blindBoxPicUrl:'https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/13e8a0db-9dca-4bdf-89d5-5868ec7c96e0.png'
}
const res=await extractingBlindBoxes(data)
if (res.code===0){
showWin3.value=true
errMsg.value=res.msg
}else if (res.code===200){
const res1= await getQrCode({appointUid:res.data.appointmentUid})
const arrayBuffer = new Uint8Array(res1)
imageSrc.value = "data:image/png;base64," + uni.arrayBufferToBase64(arrayBuffer)
showWin4.value=true
}
}
</script>
<style scoped lang="scss">
.content8{
display: flex;
flex-direction: column;
align-items: center;
.wrap1{
font-size: 28rpx;
color: #000;
margin-top: 36rpx;
}
.wrap2{
color: #B29E92;
font-size: 24rpx;
}
.wrap3{
margin-top: 20rpx;
width: 360rpx;
height: 360rpx;
border-radius: 30rpx;
overflow: hidden;
image{
width: 100%;
height: 100%;
}
}
.wrap4{
margin-top: 20rpx;
width: 436rpx;
height: 60rpx;
background-color:#F7963B;
color: #fff;
font-size: 28rpx;
border-radius:30rpx ;
display: flex;
justify-content: center;
align-items: center;
}
.wrap5{
margin-top: 6rpx;
color: #B29E92;
font-size: 16rpx;
}
}
.content7{
display: flex;
flex-direction: column;
align-items: center;
.wrap4{
margin-top: 6rpx;
font-size: 16rpx;
color: #fff;
}
.wrap3{
margin-top: 32rpx;
display: flex;
justify-content: center;
align-items: center;
border-radius: 30rpx;
width: 436rpx;
height: 60rpx;
background-color: #E84030;
color: #fff;
font-size: 28rpx;
}
.wrap2{
margin-top: 30rpx;
font-size: 24rpx;
color: #644D3F;
}
.wrap1{
margin-top: 58rpx;
font-size: 32rpx;
color: #B1292E;
}
}
.content6{
display: flex;
flex-direction: column;
align-items: center;
.wrap4{
margin-top: 6rpx;
font-size: 16rpx;
color: #fff;
}
.wrap3{
margin-top: 32rpx;
display: flex;
justify-content: center;
align-items: center;
border-radius: 30rpx;
width: 436rpx;
height: 60rpx;
background-color: #fff;
color: #B1292E;
font-size: 28rpx;
}
.wrap2{
margin-top: 30rpx;
font-size: 24rpx;
color: #fff;
}
.wrap1{
margin-top: 58rpx;
font-size: 32rpx;
color: #fff;
}
}
.container{
display: flex;
flex-direction: column;
height: 100vh;
.main{
overflow-y: auto;
box-sizing: border-box;
padding: 32rpx 42rpx 0 42rpx;
width: 100vw;
flex: 1;
background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/16968647-fc99-46fe-b95c-620c55b7646f.png');
background-size: 100%;
.content5{
margin-bottom: 90rpx;
margin-top: 20rpx;
width: 100%;
.wrap1{
margin-bottom: 20rpx;
width: 100%;
height:454rpx;
image{
width: 100%;
height: 100%;
}
}
}
.content4 {
margin-top: 42rpx;
height: 1rpx;
width: 100%;
background-image: url("../../static/zx303@3x.png");
background-size: 100%;
}
.content3{
margin-top: 20rpx;
font-size: 24rpx;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
border-radius: 24rpx;
background-color: #B1292E;
width: 100%;
height: 82rpx;
}
.content2{
overflow: hidden;
margin-top: 12rpx;
width: 100%;
image{
width: 100%;
}
}
.content1{
box-sizing: border-box;
background-image: url("https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/793bc6e0-ba68-4085-b3e3-cbc8890c07af.png");
width: 100%;
height: 60rpx;
background-size: 100%;
display: flex;
align-items: center;
padding-left: 20rpx;
.wrap2{
font-size: 32rpx;
color: #fff;
}
.wrap1{
margin-right: 12rpx;
width: 39.75rpx;
height: 39.75rpx;
image{
width: 100%;
height: 100%;
}
}
}
}
}
</style>

View File

@ -0,0 +1,167 @@
<template>
<div class="container">
<custom-title class="title-block" title="人脸核验">
</custom-title>
<div class="main">
<tm-message ref="msg" :lines="2"></tm-message>
<div class="content1">
人像识别认证
</div>
<div class="content2">
<camera device-position="front">
</camera>
</div>
<div class="content3" @click="submit">
点击拍摄
</div>
<tm-drawer inContent :width="700" :height="800" hide-header ref="calendarView" placement="center" v-model:show="showWin2">
<div class="content4">
<div class="wrap1">
<image :src="tempImage"></image>
</div>
<div class="wrap2" @click="submitFace">提交</div>
</div>
</tm-drawer>
</div>
</div>
</template>
<script setup>
import displayBox from '../../components/display-box/index.vue'
import {ref} from "vue";
import {getInfo, socketImg, updateInfo, upload} from "@/http/apis";
const msg=ref(null)
const showWin2=ref(false)
const statusValue=ref(0)
const getUserInfo = async () => {
const res=await getInfo()
if (res.code===200){
uni.setStorageSync('userInfo',res.data);
}
}
const submitFace=async ()=>{
const res1= await upload({
name:'file',
filePath:tempImage.value
})
const res= await socketImg({
appointmentUid: uni.getStorageSync('ticket').appointmentUid,
imageUrl: res1.data.path
})
if (res.code===200){
uni.showToast({
title:'提交成功',
icon: 'none',
duration: 50000
})
}
}
const tempImage=ref('')
const submit=async ()=>{
if (statusValue.value===0){
const ctx = uni.createCameraContext();
ctx.takePhoto({
quality: 'high',
success: (res) => {
tempImage.value=res.tempImagePath
showWin2.value=true
},
fail: (err) => {
console.error(err);
}
});
}
}
const idInfo=ref({
name:'',
idCard:''
})
const validateIDCardNumber=(idNumber)=> {
const regExpMainland = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
return regExpMainland.test(idNumber);
}
</script>
<style scoped lang="scss">
.content4{
display: flex;
flex-direction: column;
align-items: center;
padding-right: 20rpx;
padding-left: 20rpx;
.wrap1{
margin-top: 50rpx;
image{
width: 600rpx;
height: 600rpx;
}
}
.wrap2{
margin-top: 40rpx;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 28rpx;
width: 436rpx;
height: 60rpx;
background-color: #000;
border-radius: 30rpx;
}
}
.container{
display: flex;
flex-direction: column;
height: 100vh;
.main{
overflow-y: auto;
box-sizing: border-box;
padding: 0rpx 42rpx 0 42rpx;
width: 100vw;
flex: 1;
background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/16968647-fc99-46fe-b95c-620c55b7646f.png');
background-size: 100%;
.content3{
left: 50%;
transform: translateX(-50%);
position: absolute;
bottom: 156rpx;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 28rpx;
width: 436rpx;
height: 60rpx;
background-color: #000;
border-radius: 30rpx;
}
.content2{
margin-top: 118rpx;
display: flex;
justify-content: center;
align-items: center;
camera{
width: 520rpx;
height: 520rpx;
border-radius: 360rpx
}
}
.content1{
font-size: 24rpx;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
margin-top: 68rpx;
border-radius: 24rpx;
height: 82rpx;
background-color: #B1292E;
}
}
}
</style>

126
src/pages/facial/index.vue Normal file
View File

@ -0,0 +1,126 @@
<template>
<div class="container">
<custom-title class="title-block" title="实名">
</custom-title>
<div class="main">
<tm-message ref="msg" :lines="2"></tm-message>
<div class="content1">
身份证认证
</div>
<div class="content2">
<display-box>
<template #l1>
<div class="box-left">
真实姓名
</div>
</template>
<template #r1>
<div class="box-right">
<input type="text" v-model="idInfo.name" placeholder="请填写您的真实姓名" placeholder-style="color:#DBDBDB;font-size:24rpx"/>
</div>
</template>
<template #l2>
<div class="box-left">
身份证号码
</div>
</template>
<template #r2>
<div class="box-right">
<input type="text" v-model="idInfo.idCard" placeholder="填写您的身份证号码" placeholder-style="color:#DBDBDB;font-size:24rpx"/>
</div>
</template>
</display-box>
</div>
<div class="content3" @click="submit">
提交
</div>
</div>
</div>
</template>
<script setup>
import displayBox from '../../components/display-box/index.vue'
import {ref} from "vue";
import {getInfo, updateInfo} from "@/http/apis";
const msg=ref(null)
const getUserInfo = async () => {
const res=await getInfo()
if (res.code===200){
uni.setStorageSync('userInfo',res.data);
}
}
const submit=async ()=>{
const res=await updateInfo({
idNum:idInfo.value.idCard,
realName:idInfo.value.name
})
if (res.code===200){
uni.showToast({
title: '实名成功',
duration: 1300
})
setTimeout(async ()=>{
getUserInfo()
uni.navigateBack()
},1300)
}
}
const idInfo=ref({
name:'',
idCard:''
})
const validateIDCardNumber=(idNumber)=> {
const regExpMainland = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
return regExpMainland.test(idNumber);
}
</script>
<style scoped lang="scss">
.container{
display: flex;
flex-direction: column;
height: 100vh;
.main{
overflow-y: auto;
box-sizing: border-box;
padding: 0rpx 42rpx 0 42rpx;
width: 100vw;
flex: 1;
background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/16968647-fc99-46fe-b95c-620c55b7646f.png');
background-size: 100%;
.content3{
left: 50%;
transform: translateX(-50%);
position: absolute;
bottom: 156rpx;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 28rpx;
width: 436rpx;
height: 60rpx;
background-color: #000;
border-radius: 30rpx;
}
.content2{
margin-top: 38rpx;
.box-left{
font-size: 24rpx;
color: #000;
}
}
.content1{
font-size: 24rpx;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
margin-top: 68rpx;
border-radius: 24rpx;
height: 82rpx;
background-color: #B1292E;
}
}
}
</style>

View File

@ -1,57 +1,258 @@
<template>
<div class="main">
<div class="header">
<div>门票名称</div>
<div>剩余数量</div>
</div>
<div class="container">
<div class="item">
<image src="@/static/logo.png" mode="scaleToFill" style="width: 174rpx;height: 108rpx;" />
<div class="detail">
<div style="width: 140rpx;">首都博物馆门票</div>
<div>1023/20000</div>
<tm-button color="#F7963B" :width="108" :height="56">预约</tm-button>
<div class="content3">
<div class="wrap1">
<image src="https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/1d693295-c16b-4845-a02b-c052d9cf847a.png" /></div>
<div class="wrap2" @click="viewBlindBoxDetail">
<div class="wrap2_1">博物馆开馆纪念盲盒</div>
<div class="wrap2_2">剩余 {{allRemainingQty}}/{{allIssueQty}}</div>
<div class="wrap2_3">查看详情</div>
</div>
</div>
<div class="content1">
<div class="wrap1">门票名称</div>
<div class="wrap2">剩余数量</div>
</div>
<div class="content2">
<div class="wrap1" v-for="(item,index) in [...tableData,...tableData]" :key="item.id">
<div @click="viewImg(item)" class="wrap1_1">
<image :src="item.ticketImage"></image>
</div>
<div class="wrap1_2">{{item.ticketName}}</div>
<div class="wrap1_3" :class="item.remainingQuantity===0?['sold_out']:[]">{{item.remainingQuantity===-1?'不限':item.remainingQuantity}}/{{item.issueQuantity===-1?'不限':item.issueQuantity}}</div>
<div class="wrap1_4" @click="goBooking(item)" :class="item.remainingQuantity===0?['sold_out']:[]">{{item.remainingQuantity===0?'无票':'预约'}}</div>
</div>
<div class="wrap2">
<image src="../../static/zu758@3x.png"></image>
</div>
</div>
<div class="attention" v-if="store.isShow">
<div>
港澳台游客请至现场办理门票业务
</div>
<tm-icon name="tmicon-times-circle" color="#fff" :fontSize="24" @click="handleTips"></tm-icon>
</div>
</div>
</template>
<script setup>
/* 导入区*/
import { ref } from "vue";
import {ticketlist} from "@/http/apis";
import {useMainStore} from "@/store";
/**/
const store=useMainStore()
let height = ref('')
let isShow = ref(true)
const tableData=ref([])
const page=ref(1)
const pageSize=ref(9999)
const viewImg=(item)=>{
uni.previewImage({
urls:[item.ticketImage],
indicator:'none'
})
}
const allIssueQty=ref(0)
const allRemainingQty=ref(0)
const getData=async ()=>{
const data={
"keyword": "",
"isMobile": 1,
"page": page.value,
"pageSize": pageSize.value
}
const res=await ticketlist(data)
if (res.code===200){
allIssueQty.value=res.data.allIssueQty
allRemainingQty.value=res.data.allRemainingQty
tableData.value=res.data.data
}
}
const goBooking=(item)=>{
if (item.remainingQuantity!==0){
uni.navigateTo({
url: '/pages/ticket/index'
})
}else {
uni.showToast({
title: '该门票已售罄',
icon: 'none',
duration: 1000
})
}
uni.setStorageSync('currentBooking',item)
store.currentBooking=item
}
const viewBlindBoxDetail=()=>{
uni.navigateTo({
url: '/pages/blind-box/index'
})
}
getData()
const handleTips = () => {
store.isShow=false
}
</script>
<style scoped lang="scss">
.main {
height: 100vh;
height: 80vh;
width: 100%;
background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/16968647-fc99-46fe-b95c-620c55b7646f.png');
background-size: 100%;
padding: 38rpx 32rpx;
padding: 0 32rpx 38rpx 32rpx;
display: flex;
flex-direction: column;
box-sizing: border-box;
.content3{
border-radius: 20rpx;
margin-bottom: 30rpx;
margin-top: 40rpx;
overflow: hidden;
display: flex;
.wrap2{
background-color: #fff;
width: 350rpx;
height: 214rpx;
display: flex;
flex-direction: column;
align-items: center;
.wrap2_1{
margin-top: 40rpx;
font-size: 28rpx;
color: #000;
}
.wrap2_2{
margin-top: 8rpx;
font-size: 24rpx;
color: #F7963B;
}
.wrap2_3{
margin-top: 20rpx;
font-size: 24rpx;
display: flex;
justify-content: center;
align-items: center;
border-radius: 20rpx;
width: 240rpx;
height: 56rpx;
color: #fff;
background-color: #F7963B;
}
}
.wrap1{
width: 350rpx;
height: 214rpx;
image{
width: 100%;
height: 100%;
}
}
}
.content2{
flex: 1;
overflow-y: scroll;
margin-top: 22rpx;
.wrap2{
margin-bottom: 68rpx;
margin-top: 68rpx;
display: flex;
justify-content: center;
image{
width: 587.8rpx;
height: 22rpx;
}
}
.wrap1{
margin-bottom: 18rpx;
border-radius: 20rpx;
background-color: #fff;
height: 108rpx;
width: 100%;
display: flex;
align-items: center;
overflow: hidden;
.wrap1_1{
box-sizing: border-box;
padding: 5rpx;
display: flex;
align-items: center;
justify-content: center;
image{
width: 165rpx;
height: 100rpx;
}
margin-right: 18rpx;
}
.wrap1_4{
width: 108rpx;
height: 56rpx;
background-color: #F7963B;
border-radius: 20rpx;
display: flex;
color: #fff;
justify-content: center;
align-items: center;
&.sold_out{
background-color: #AFAFAF;
}
}
.wrap1_3{
color: #F7963B;
font-size: 24rpx;
display: flex;
justify-content: center;
align-items: center;
width: 224rpx;
&.sold_out{
color: #AFAFAF;
}
}
.wrap1_2{
width: 140rpx;
height: 76rpx;
}
}
}
.content1 {
box-sizing: border-box;
.header {
width: 100%;
background: #AB2F23;
height: 70rpx;
border-radius: 20rpx;
display: flex;
justify-content: space-around;
align-items: center;
color: #FFFFFF;
font-size: 28rpx;
}
.wrap1 {
padding-right: 36rpx;
text-align: right;
width: 50%;
flex-shrink: 1;
font-size: 28rpx;
color: #FFFFFF;
}
.wrap2 {
padding-left: 36rpx;
width: 50%;
flex-shrink: 1;
font-size: 28rpx;
color: #FFFFFF;
}
}
.container {
box-sizing: border-box;
flex: 1;
overflow-y: auto;
height: 1120rpx;
box-sizing: border-box;
margin-top: 20rpx;
display: flex;
flex-direction: column;
padding-bottom: 2rpx;
margin-bottom: 20rpx;
.item {
overflow: hidden;
display: flex;
align-items: center;
background: #FFFFFF;
@ -67,5 +268,20 @@
}
}
}
.attention {
width: 664rpx;
display: flex;
justify-content: space-between;
align-items: center;
position: fixed;
background: #761C1F;
height: 68rpx;
color: #FFFFFF;
bottom: 50rpx;
padding: 16rpx 40rpx;
box-sizing: border-box;
border-radius: 24rpx;
font-size: 28rpx;
}
}
</style>

350
src/pages/home1/index.vue Normal file
View File

@ -0,0 +1,350 @@
<template>
<div class="main">
<!-- <div class="content3">
<div class="wrap1">
<image src="https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/1d693295-c16b-4845-a02b-c052d9cf847a.png" /></div>
<div class="wrap2" @click="viewBlindBoxDetail">
<div class="wrap2_1">博物馆开馆纪念盲盒</div>
<div class="wrap2_2">剩余 {{allRemainingQty}}/{{allIssueQty}}</div>
<div class="wrap2_3">查看详情</div>
</div>
</div>
<div class="content1">
<div class="wrap1">门票名称</div>
<div class="wrap2">剩余数量</div>
</div>
<div class="content2">
<div class="wrap1" v-for="(item,index) in [...tableData,...tableData]" :key="item.id">
<div @click="viewImg(item)" class="wrap1_1">
<image :src="item.ticketImage"></image>
</div>
<div class="wrap1_2">{{item.ticketName}}</div>
<div class="wrap1_3" :class="item.remainingQuantity===0?['sold_out']:[]">{{item.remainingQuantity===-1?'不限':item.remainingQuantity}}/{{item.issueQuantity===-1?'不限':item.issueQuantity}}</div>
<div class="wrap1_4" @click="goBooking(item)" :class="item.remainingQuantity===0?['sold_out']:[]">{{item.remainingQuantity===0?'无票':'预约'}}</div>
</div>
<div class="wrap2">
<image src="../../static/zu758@3x.png"></image>
</div>
</div>-->
<div class="content4">
<div class="wrap1">
<image src="https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/13e8a0db-9dca-4bdf-89d5-5868ec7c96e0.png"></image>
</div>
<div class="wrap2" @click="viewBlindBoxDetail">
<div class="wrap2_1">
<div class="wrap2_1_1">博物馆开馆纪念盲盒</div>
<div class="wrap2_1_2">剩余 {{allRemainingQty}}/{{allIssueQty}}</div>
</div>
<div class="wrap2_2">查看详情</div>
</div>
</div>
<div class="attention" v-if="store.isShow">
<div>
港澳台游客请至现场办理门票业务
</div>
<tm-icon name="tmicon-times-circle" color="#fff" :fontSize="24" @click="handleTips"></tm-icon>
</div>
</div>
</template>
<script setup>
/* 导入区*/
import { ref } from "vue";
import {ticketlist} from "@/http/apis";
import {useMainStore} from "@/store";
/**/
const store=useMainStore()
let height = ref('')
let isShow = ref(true)
const tableData=ref([])
const page=ref(1)
const pageSize=ref(9999)
const viewImg=(item)=>{
uni.previewImage({
urls:[item.ticketImage],
indicator:'none'
})
}
const allIssueQty=ref(0)
const allRemainingQty=ref(0)
const getData=async ()=>{
const data={
"keyword": "",
"isMobile": 1,
"page": page.value,
"pageSize": pageSize.value
}
const res=await ticketlist(data)
if (res.code===200){
allIssueQty.value=res.data.allIssueQty
allRemainingQty.value=res.data.allRemainingQty
tableData.value=res.data.data
}
}
const goBooking=(item)=>{
if (item.remainingQuantity!==0){
uni.navigateTo({
url: '/pages/ticket/index'
})
}else {
uni.showToast({
title: '该门票已售罄',
icon: 'none',
duration: 1000
})
}
uni.setStorageSync('currentBooking',item)
store.currentBooking=item
}
const viewBlindBoxDetail=()=>{
uni.navigateTo({
url: '/pages/blind-box/index'
})
}
getData()
const handleTips = () => {
store.isShow=false
}
</script>
<style scoped lang="scss">
.main {
height: 80vh;
width: 100%;
background-size: 100%;
padding: 0 32rpx 38rpx 32rpx;
display: flex;
flex-direction: column;
box-sizing: border-box;
.content4{
margin-top: 40rpx;
width: 700rpx;
height: 576rpx;
border-radius: 20rpx;
box-sizing: border-box;
overflow: hidden;
.wrap2{
box-sizing: border-box;
padding-right: 48rpx;
padding-left: 48rpx;
justify-content: space-between;
background-color: #fff;
display: flex;
align-items: center;
width: 700rpx;
height: 120rpx;
.wrap2_2{
font-size: 24rpx;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
width: 240rpx;
height: 56rpx;
background-color: #F7963B;
border-radius: 20rpx;
}
.wrap2_1{
.wrap2_1_2{
font-size: 20rpx;
color: #F7963B;
}
.wrap2_1_1{
font-size: 28rpx;
color: #000000;
}
}
}
.wrap1{
width: 700rpx;
height: 456rpx;
image{
width: 100%;
height: 100%;
}
}
}
.content3{
border-radius: 20rpx;
margin-bottom: 30rpx;
margin-top: 40rpx;
overflow: hidden;
display: flex;
.wrap2{
background-color: #fff;
width: 350rpx;
height: 214rpx;
display: flex;
flex-direction: column;
align-items: center;
.wrap2_1{
margin-top: 40rpx;
font-size: 28rpx;
color: #000;
}
.wrap2_2{
margin-top: 8rpx;
font-size: 24rpx;
color: #F7963B;
}
.wrap2_3{
margin-top: 20rpx;
font-size: 24rpx;
display: flex;
justify-content: center;
align-items: center;
border-radius: 20rpx;
width: 240rpx;
height: 56rpx;
color: #fff;
background-color: #F7963B;
}
}
.wrap1{
width: 350rpx;
height: 214rpx;
image{
width: 100%;
height: 100%;
}
}
}
.content2{
flex: 1;
overflow-y: scroll;
margin-top: 22rpx;
.wrap2{
margin-bottom: 68rpx;
margin-top: 68rpx;
display: flex;
justify-content: center;
image{
width: 587.8rpx;
height: 22rpx;
}
}
.wrap1{
margin-bottom: 18rpx;
border-radius: 20rpx;
background-color: #fff;
height: 108rpx;
width: 100%;
display: flex;
align-items: center;
overflow: hidden;
.wrap1_1{
box-sizing: border-box;
padding: 5rpx;
display: flex;
align-items: center;
justify-content: center;
image{
width: 165rpx;
height: 100rpx;
}
margin-right: 18rpx;
}
.wrap1_4{
width: 108rpx;
height: 56rpx;
background-color: #F7963B;
border-radius: 20rpx;
display: flex;
color: #fff;
justify-content: center;
align-items: center;
&.sold_out{
background-color: #AFAFAF;
}
}
.wrap1_3{
color: #F7963B;
font-size: 24rpx;
display: flex;
justify-content: center;
align-items: center;
width: 224rpx;
&.sold_out{
color: #AFAFAF;
}
}
.wrap1_2{
width: 140rpx;
height: 76rpx;
}
}
}
.content1 {
box-sizing: border-box;
width: 100%;
background: #AB2F23;
height: 70rpx;
border-radius: 20rpx;
display: flex;
align-items: center;
color: #FFFFFF;
font-size: 28rpx;
.wrap1 {
padding-right: 36rpx;
text-align: right;
width: 50%;
flex-shrink: 1;
font-size: 28rpx;
color: #FFFFFF;
}
.wrap2 {
padding-left: 36rpx;
width: 50%;
flex-shrink: 1;
font-size: 28rpx;
color: #FFFFFF;
}
}
.container {
overflow-y: auto;
height: 1120rpx;
box-sizing: border-box;
margin-top: 20rpx;
display: flex;
flex-direction: column;
padding-bottom: 2rpx;
margin-bottom: 20rpx;
.item {
overflow: hidden;
display: flex;
align-items: center;
background: #FFFFFF;
border-radius: 20rpx;
margin-bottom: 20rpx;
.detail {
flex: 1;
margin-left: 18rpx;
display: flex;
align-items: center;
justify-content: space-around;
}
}
}
.attention {
width: 664rpx;
display: flex;
justify-content: space-between;
align-items: center;
position: fixed;
background: #761C1F;
height: 68rpx;
color: #FFFFFF;
bottom: 50rpx;
padding: 16rpx 40rpx;
box-sizing: border-box;
border-radius: 24rpx;
font-size: 28rpx;
}
}
</style>

View File

@ -1,16 +1,29 @@
<template>
<div>
<!-- <div :style="{height:`${ztHehight}px`}"></div>-->
<home v-if="acc===0"/>
<mine v-if="acc===1"/>
<div class="tab-index">
<custom-title class="title-block" :title="accTitleList.find(x=>x.value===acc).title" :isBack="false">
</custom-title>
<div class="ld ld-float-ltr-in" v-show="acc===0"><home1 /></div>
<div v-show="acc===1" class="ld ld-float-rtl-in"><mine /></div>
<self-tabbar v-model="acc"></self-tabbar>
</div>
</template>
<script setup>
import home from '../home/index.vue'
import home1 from '../home1/index.vue'
import mine from '../mine/index.vue'
import selfTabbar from '../../components/self-tabbar/index.vue'
import {ref} from "vue";
const acc=ref(1)
const ztHehight=uni.getSystemInfoSync().statusBarHeight
import {onLoad} from "@dcloudio/uni-app";
onLoad((option)=>{
if (typeof option.acc==='string'){
acc.value=Number(option.acc)
}
})
const accTitleList=[{title:'大运河博物馆',value:0},{title:'智慧门票',value:1}]
const acc=ref(0)
</script>
<style lang="scss" scoped>
.tab-index{
background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/16968647-fc99-46fe-b95c-620c55b7646f.png');
background-size: 100%;
}
</style>

View File

@ -2,31 +2,41 @@
<div class="main">
<view>
<button open-type="getPhoneNumber" @getphonenumber="getPhoneNumber" class="btn">点击登录</button>
</view>
</div>
</template>
<script setup >
import { ref, getCurrentInstance } from "vue";
const currentInstance = getCurrentInstance();
const { $api } = currentInstance.appContext.config.globalProperties;
import { ref } from "vue";
import {login,getInfo} from "@/http/apis";
let isShow = ref(true);
const getPhoneNumber = () => {
console.log(123)
const getPhoneNumber =async (data) => {
const res=await login({
code:data.detail.code
})
if (res.code===200){
uni.setStorageSync('token',res.data.token);
getUserInfo()
}
}
const getUserInfo = async () => {
const res=await getInfo()
if (res.code===200){
uni.setStorageSync('userInfo',res.data);
uni.navigateTo({
url: '/pages/index/index?acc=0'
})
}
}
</script>
<style lang="scss" scoped>
.main {
background: url("https://cdns.fontree.cn/fonchain-main/prod/image/default/artwork/3f70cf7f-4017-42a6-9276-bf854f57f78d.png");
/* background: url("https://cdns.fontree.cn/fonchain-main/prod/image/default/artwork/3f70cf7f-4017-42a6-9276-bf854f57f78d.png");*/
background: url("https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/33e1a0c8-ce9e-4b05-bd49-7a75f7bd98d4.png");
background-size: cover;
height: 100vh;
display: flex;
align-items: center;
flex-direction: column;
.btn {
background: transparent;
width: 200rpx;

View File

@ -1,74 +1,202 @@
<template>
<div class="large-container">
<div class="content1">
<div class="wrap1">
<div class="wrap1_1">
<image src="../../static/06.png" alt=""/>
<image :src="userInfo()?.avatar" alt=""/>
</div>
<div class="wrap1_2">
<div class="wrap1_2_1">
<div class="wrap1_2_1_1">某某某</div>
<div class="wrap1_2_1_2">已实名</div>
<div class="wrap1_2_1_1">{{ userInfo()?.nickName }}</div>
<!-- <div class="wrap1_2_1_2">已实名</div>-->
</div>
<div class="wrap1_2_2">178273938123</div>
<div class="wrap1_2_2">{{ userInfo()?.telNum }}</div>
</div>
</div>
<div class="wrap2" @click="goSetUp">
<div class="wrap2_1">
<image src="../../static/zu609@3x (1).png" alt=""/>
<div class="wrap2">
<div class="wrap2_1" @click="goSetUp">
<div class="wrap2_1_1">
<image src="../../static/zu609@3x.png"></image>
</div>
<div class="wrap2_1_2">设置</div>
</div>
<div class="wrap2_2" @click="verifyIdentity">
<div class="wrap2_2_1">
<image src="../../static/zu1123@3x.png"></image>
</div>
<div class="wrap2_2_2">实名</div>
</div>
<div class="wrap2_2">设置</div>
</div>
</div>
<div class="content2">
<div class="wrap1 ">
<div class="wrap1_1">2</div>
<div class="wrap1" :class="currentTab===0?['active']:[]" @click="switchOptions(0)">
<div class="wrap1_1">{{tabList?.unusedNumber}}</div>
<div class="wrap1_2">未使用门票</div>
</div>
<div class="wrap2"></div>
<div class="wrap3 active">
<div class="wrap1_1">3</div>
<div class="wrap3" :class="currentTab===1?['active']:[]" @click="switchOptions(1)">
<div class="wrap1_1">{{tabList?.historyNumber}}</div>
<div class="wrap1_2">历史门票</div>
</div>
</div>
<div class="content3">
</div>
<div class="content4">·历史预约门票</div>
<div class="content3"></div>
<div class="content4" :style="{color:currentTab===0?'#EB783C':'#72665F'}">{{currentTab===0?'·我未使用的票务':'·历史预约门票'}}</div>
<div class="content5">
<div class="wrap1">
<div class="wrap1_1 verified">已核销</div>
<div class="wrap1" @click="goDetail(item)" :class="[`status${item.status}`]" v-for="(item,index) in tabList?.tickets" :key="item.appointmentUid">
<div class="wrap1_5">
<image :src="item.ticketCoverPic"></image>
</div>
<div class="wrap1_6" :style="{backgroundColor:item.isBlindBox===2?'#CC2727':''}">
</div>
<div class="wrap1_1" v-if="item.isBlindBox===1">{{statusList.find(x=>x.value===item.status).label}}</div>
<div class="wrap1_1" style="background-color: #fff" v-if="item.isBlindBox===2">盲盒</div>
<div class="wrap1_2">
<div class="wrap1_2_1">首都博物馆门票</div>
<div class="wrap1_2_2">预约场馆:首都博物馆</div>
<div class="wrap1_2_2">预约日期:2023.12.30</div>
<div class="wrap1_2_2">预约类型:1</div>
<div class="wrap1_2_1">{{item.ticketName}}</div>
<div class="wrap1_2_2">兑换场馆:{{item.venuesName}}</div>
<div class="wrap1_2_2">兑换日期:{{item.date?.replaceAll('/','.')}}</div>
<div class="wrap1_2_2">预约数量:1</div>
</div>
<div class="wrap1_3">
<image src="../../static/zu762@3x (1).png" alt=""/>
<div class="wrap1_3" :style="{backgroundColor:item.isBlindBox===2?'#000':''}">
<image @click="goViewVenues(item)" v-if="item.isBlindBox===1" src="../../static/zu762@3x.png" alt=""/>
<image @click.stop="exchange(item)" v-if="item.isBlindBox===2" style="width: 79rpx;height: 34rpx" src="../../static/zu1216@3x.png" alt=""/>
</div>
<tm-drawer hideHeader :width="510" :height="606" ref="calendarView" placement="center" v-model:show="showWin4">
<div class="content8">
<div class="wrap1">{{ticketInfo?.ticketName}}</div>
<div class="wrap2">1人1码请与工作人员核验领取</div>
<div class="wrap3">
<image :src="imageSrc"></image>
</div>
<div class="wrap1">
<div class="wrap1_1 verified">已核销</div>
<div class="wrap1_2">
<div class="wrap1_2_1">首都博物馆门票</div>
<div class="wrap1_2_2">预约场馆:首都博物馆</div>
<div class="wrap1_2_2">预约日期:2023.12.30</div>
<div class="wrap1_2_2">预约类型:1</div>
</div>
<div class="wrap1_3" @click="goViewVenues">
<image src="../../static/zu762@3x (1).png" alt=""/>
<div class="wrap4" :style="{backgroundColor:ticketInfo?.status===3?'#AFAFAF':''}" @click="showWin4=false">{{ticketInfo?.status===3?'已核验':'未核验'}}</div>
</div>
</tm-drawer>
</div>
</div>
</div>
</template>
<script setup>
const goViewVenues=()=>{
import {ref,watch} from 'vue'
import {onShow} from "@dcloudio/uni-app";
import {extractingBlindBoxes, getQrCode, historicalTickets, unusedTickets} from "@/http/apis";
const modeClass = ref('')
const currentTab = ref(0)
const showWin4=ref(false)
const show = ref(true)
const imageSrc=ref('')
const ticket=()=>{
return uni.getStorageSync('ticket')
}
const userInfo =()=>{
return uni.getStorageSync('userInfo')
}
onShow(() => {
show.value = true
})
const tabList=ref(null)
const statusList=ref([
{
label:'',
value:1
},
{
label:'已退票',
value:2
},
{
label:'已核销',
value:3
},
{
label:'已过期',
value:4
},
{
label:'',
value:5
},
{
label:'盲盒',
value:6
}
])
const goDetail=(item)=>{
uni.setStorageSync('ticket',item)
uni.navigateTo({
url: '/pages/ticket-details/index'
})
}
const getData=async ()=>{
const res =await unusedTickets()
if (res.code===200){
tabList.value=res.data
}
}
onShow(()=>{
getData()
})
const switchOptions=async (num)=>{
let res
if (num===0){
res=await unusedTickets()
}else if (num===1){
res=await historicalTickets()
}
if (res.code===200){
tabList.value=res.data
}
currentTab.value=num
}
const ticketInfo=ref(null)
watch(showWin4,()=>{
console.log('关闭Socket')
// uni.closeSocket()
})
const exchange=async (item)=>{
const res1= await getQrCode({appointUid:item.appointmentUid})
const arrayBuffer = new Uint8Array(res1)
imageSrc.value = "data:image/png;base64," + uni.arrayBufferToBase64(arrayBuffer)
ticketInfo.value=item
uni.setStorageSync('ticket',item)
showWin4.value=true
uni.connectSocket({
url: `ws://warehouse.szjixun.cn/ticket/api/smart/appointment/subscribe/msg?AppointmentUid=${item.appointmentUid}`,
success: function() {
console.log('WebSocket连接已创建成功');
}
});
uni.onSocketOpen( (res)=> {
console.log(res,'onSocketOpen')
});
uni.onSocketMessage((res)=>{
console.log('WebSocket接收到消息', res);
if (res.data==='start the camera'){
/* uni.closeSocket()*/
uni.navigateTo({
url: '/pages/face-auth/index'
})
}else if (res.data==='Check Finish'){
uni.closeSocket()
uni.navigateTo({
url: '/pages/index/index?acc=1'
})
}
});
}
const goViewVenues = (item) => {
uni.setStorageSync('ticket',item)
uni.navigateTo({
url: '/pages/view-venues/index'
})
}
const verifyIdentity=()=>{
uni.navigateTo({
url: '/pages/facial/index'
})
}
const goSetUp = () => {
uni.navigateTo({
url: '/pages/setup/index'
@ -76,14 +204,189 @@ const goSetUp=()=>{
}
</script>
<style scoped lang="scss">
.content8{
display: flex;
flex-direction: column;
align-items: center;
.wrap1{
font-size: 28rpx;
color: #000;
margin-top: 36rpx;
}
.wrap2{
color: #B29E92;
font-size: 24rpx;
}
.wrap3{
margin-top: 20rpx;
width: 360rpx;
height: 360rpx;
border-radius: 30rpx;
overflow: hidden;
image{
width: 100%;
height: 100%;
}
}
.wrap4{
margin-top: 20rpx;
width: 134rpx;
height: 60rpx;
background-color:#F7963B;
color: #fff;
font-size: 28rpx;
border-radius:30rpx ;
display: flex;
justify-content: center;
align-items: center;
}
.wrap5{
margin-top: 6rpx;
color: #B29E92;
font-size: 16rpx;
}
}
.large-container {
background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/16968647-fc99-46fe-b95c-620c55b7646f.png');
overflow: hidden;
display: flex;
flex-direction: column;
background-size: 100%;
height: 100vh;
padding: 38rpx 32rpx 0 32rpx;
height: 80vh;
padding: 0rpx 32rpx 0 32rpx;
.content5 {
margin-top: 14rpx;
flex-grow: 1;
overflow-y: auto;
.wrap1{
&:nth-child(n+2){
margin-top: 20rpx;
}
&:last-child{
margin-bottom: 100rpx;
}
display: flex;
position: relative;
width: 686rpx;
height: 210rpx;
box-sizing: border-box;
border: 2rpx solid #fff;
background-size: 100%;
border-radius: 40rpx;
overflow: hidden;
&.status2 {
.wrap1_1{
background-color: #fff;
color: #FF5C62;
}
.wrap1_6{
background-color: #B29E92;
}
}
&.status4 {
.wrap1_1{
background-color: #000000;
color: #fff;
}
.wrap1_6{
background-color: #B29E92;
}
}
&.status6{
.wrap1_6{
background-color: #fff;
}
}
&.status5{
.wrap1_6{
background-color: #EB783C;
}
}
&.status1{
.wrap1_6{
background-color: #EB783C;
}
}
&.status3 {
.wrap1_6{
background-color: #B29E92;
}
.wrap1_1{
background-color: #fff;
color: #72665F;
}
}
.wrap1_3 {
bottom: 18rpx;
right: 16rpx;
position: absolute;
width: 150rpx;
height: 56rpx;
background-color: rgba(0,0,0,0.5);
border-radius: 40rpx;
display: flex;
justify-content: center;
align-items: center;
image {
width: 119.5rpx;
height: 34rpx;
}
}
.wrap1_2 {
top: 16rpx;
left: 285rpx;
position: absolute;
.wrap1_2_1 {
font-size: 32rpx;
color: #fff;
margin-bottom: 6rpx;
}
.wrap1_2_2 {
margin-bottom: 2rpx;
font-size: 20rpx;
color: #fff;
}
}
.wrap1_1 {
top: 26rpx;
left: 26rpx;
position: absolute;
display: flex;
justify-content: center;
align-items: center;
width: 112rpx;
height: 40rpx;
border-radius: 22rpx;
font-size: 20rpx;
}
.wrap1_6{
box-sizing: border-box;
border-left: 7rpx solid #fff;
height: 208rpx;
flex-grow: 1;
background-color: red;
}
.wrap1_5{
width: 262rpx;
height: 208rpx;
image{
width: 100%;
height: 100%;
}
}
}
/*.wrap1 {
position: relative;
width: 686rpx;
height: 210rpx;
@ -100,11 +403,13 @@ background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/ava
display: flex;
justify-content: center;
align-items: center;
image {
width: 119.5rpx;
height: 34rpx;
}
}
.wrap1_2 {
top: 16rpx;
right: 182rpx;
@ -115,6 +420,7 @@ background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/ava
color: #fff;
margin-bottom: 6rpx;
}
.wrap1_2_2 {
margin-bottom: 2rpx;
font-size: 20rpx;
@ -122,6 +428,7 @@ background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/ava
}
}
.wrap1_1 {
top: 26rpx;
left: 26rpx;
@ -133,29 +440,39 @@ background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/ava
height: 40rpx;
border-radius: 22rpx;
font-size: 20rpx;
&.verified{
&.status2 {
background-color: #fff;
color: #FF5C62;
}
&.status4 {
background-color: #000000;
color: #fff;
}
&.status3 {
background-color: #fff;
color: #72665F;
}
}
}*/
}
}
.content4 {
margin-top: 22rpx;
font-size: 28rpx;
color:#72665F;
}
.content3 {
margin-top: 38rpx;
height: 1rpx;
width: 100%;
background-image: url("../../static/zx303@3x (1).png");
background-image: url("../../static/zx303@3x.png");
background-size: 100%;
}
.content2 {
flex-shrink: 0;
margin-top: 34rpx;
width: 686rpx;
height: 134rpx;
height: 134rpx!important;
display: flex;
background-color: #fff;
border-radius: 40rpx;
@ -169,24 +486,30 @@ background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/ava
flex-direction: column;
align-items: center;
justify-content: center;
&.active {
background-color: #F7963B;
.wrap1_1 {
color: #fff;
}
.wrap1_2 {
color: #fff;
}
}
.wrap1_1 {
font-size: 28rpx;
color: #F7963B;
}
.wrap1_2 {
font-size: 20rpx;
color: #000;
}
}
.wrap2 {
margin-left: 28rpx;
margin-right: 28rpx;
@ -203,45 +526,81 @@ background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/ava
flex-direction: column;
align-items: center;
justify-content: center;
&.active {
background-color: #F7963B;
.wrap1_1 {
color: #fff;
}
.wrap1_2 {
color: #fff;
}
}
.wrap1_1 {
font-size: 28rpx;
color: #979797;
}
.wrap1_2 {
font-size: 20rpx;
color: #000;
}
}
}
.content1 {
margin-top: 38rpx;
display: flex;
.wrap2{
margin-left: 24rpx;
width: 182rpx;
height: 150rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
.wrap2_2{
width: 184rpx;
height: 68rpx;
border-radius: 40rpx;
background-color: #B1292E;
display: flex;
align-items: center;
justify-content: center;
.wrap2_2_2{
margin-left: 8rpx;
font-size: 24rpx;
color: #fff;
}
.wrap2_2_1{
width: 32rpx;
height: 32rpx;
image{
width: 100%;
height: 100%;
}
}
}
.wrap2_1{
width: 184rpx;
height: 68rpx;
border-radius: 40rpx;
background-color: #E5580F;
display: flex;
align-items: center;
.wrap2_2{
justify-content: center;
.wrap2_1_2{
margin-left: 8rpx;
font-size: 24rpx;
color: #fff;
font-size: 20rpx;
}
.wrap2_1{
margin-left: 40rpx;
margin-right: 20rpx;
.wrap2_1_1{
width: 32rpx;
height: 32rpx;
image{
width: 40.88rpx;
height: 40.88rpx;
width: 100%;
height: 100%;
}
}
}
}
@ -252,20 +611,25 @@ background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/ava
background-color: #fff;
display: flex;
align-items: center;
.wrap1_2 {
display: flex;
flex-direction: column;
.wrap1_2_2 {
margin-top: 6rpx;
color: #626262;
}
.wrap1_2_1 {
display: flex;
align-items: center;
.wrap1_2_1_1 {
color: #000;
font-size: 28rpx;
}
.wrap1_2_1_2 {
margin-left: 6rpx;
display: flex;
@ -280,9 +644,11 @@ background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/ava
}
}
}
.wrap1_1 {
margin-left: 68rpx;
margin-right: 36rpx;
image {
width: 100rpx;
height: 100rpx;

View File

@ -1,44 +1,108 @@
<template>
<custom-title class="title-block" title="智慧门票">
</custom-title>
<div class="large-container">
<div class="content1">
<div class="wrap1">
<image src="../../static/zy68.png"></image>
<image :src="userInfo.avatar"></image>
</div>
<div class="wrap2">
<div class="wrap2_1">恢复默认头像</div>
<div class="wrap2_2">更换头像</div>
<div class="wrap2_1" @click="setDefault">恢复默认头像</div>
<div class="wrap2_2" @click="changeAvatar">更换头像</div>
</div>
</div>
<div class="content2">
<div class="wrap1">
<div class="wrap1_1">
<div class="wrap1_1_1">姓名</div>
<div class="wrap1_1_2">已实名</div>
<div class="wrap1_1_2" v-if="userInfo.idNum">已实名</div>
</div>
<div class="wrap1_2">xxx</div>
<div class="wrap1_2">{{userInfo.realName}}</div>
</div>
<div class="wrap1">
<div class="wrap1_1">
<div class="wrap1_1_1">身份证号</div>
</div>
<div class="wrap1_2">292199922283726657</div>
<div class="wrap1_2">{{userInfo.idNum}}</div>
</div>
<div class="wrap1">
<div class="wrap1_1">
<div class="wrap1_1_1">微信</div>
<div class="wrap1_1_1">手机</div>
</div>
<div class="wrap1_2">12318881999</div>
<div class="wrap1_2">{{ userInfo.telNum }}</div>
</div>
</div>
<div class="content3">*来自微信数据共享</div>
<div class="content4">
<div class="wrap1">注销账号</div>
<div class="wrap2">退出登录</div>
<div class="wrap2" @click="logOut">退出登录</div>
</div>
</div>
</template>
<script setup>
import {getInfo, updateInfo, upload} from "@/http/apis";
import {ref} from 'vue'
const userInfo=ref(uni.getStorageSync('userInfo'))
const getUserInfo = async () => {
const res=await getInfo()
if (res.code===200){
uni.setStorageSync('userInfo',res.data)
userInfo.value=res.data
}
}
const currentAvatar=ref('')
const changeAvatar=()=>{
uni.chooseImage({
count:1,
success:async (res)=>{
const res1= await upload({
name:'file',
filePath:res.tempFilePaths[0]
})
currentAvatar.value=res1.data.path
await reqAvatar()
}
})
}
const setDefault=()=>{
currentAvatar.value='https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/e1fd068c-180e-45d6-9196-680f778adc13.png'
reqAvatar()
}
const logOut=()=>{
uni.showModal({
title: '提示', //
content: '确认退出登录吗', //
showCancel: true, // true
cancelText: '取消', // "" 4
cancelColor: '#000000', // "#000000"
confirmText: '确定', // "" 4
confirmColor: '#576B95', // "#576B95"
success: function (res) {
if (res.confirm) {
uni.clearStorageSync()
uni.navigateTo({
url: '/pages/login/index'
})
} else if (res.cancel) {
}
},
fail: function (error) {
}
});
}
const reqAvatar=async ()=>{
const res= await updateInfo({
avatar:currentAvatar.value
})
if (res.code===200) {
await getUserInfo()
uni.showToast({
title: '更换头像成功',
icon: 'success',
duration: 200
})
}
}
</script>
<style scoped lang="scss">
.large-container{

284
src/pages/test/index.vue Normal file
View File

@ -0,0 +1,284 @@
<template>
<view>
<view class="itemlist list-animation">
<view class="item">
<view class="ld infinite ld-blur-in"></view>
<view>blur-in</view>
</view>
<view class="item">
<view class="ld infinite ld-blur-out"></view>
<view>blur-out</view>
</view>
<view class="item">
<view class="ld infinite ld-bounce-alt-in"></view>
<view>bounce-alt-in</view>
</view>
<view class="item">
<view class="ld infinite ld-bounce-alt-out"></view>
<view>bounce-alt-out</view>
</view>
<view class="item">
<view class="ld infinite ld-bounce-in"></view>
<view>bounce-in</view>
</view>
<view class="item">
<view class="ld infinite ld-bounce-out"></view>
<view>bounce-out</view>
</view>
<view class="item">
<view class="ld infinite ld-fade-in"></view>
<view>fade-in</view>
</view>
<view class="item">
<view class="ld infinite ld-fade-out"></view>
<view>fade-out</view>
</view>
<view class="item">
<view class="ld infinite ld-fall-btt-in"></view>
<view>fall-btt-in</view>
</view>
<view class="item">
<view class="ld infinite ld-fall-ltr-in"></view>
<view>fall-ltr-in</view>
</view>
<view class="item">
<view class="ld infinite ld-fall-rtl-in"></view>
<view>fall-rtl-in</view>
</view>
<view class="item">
<view class="ld infinite ld-fall-ttb-in"></view>
<view>fall-ttb-in</view>
</view>
<view class="item">
<view class="ld infinite ld-flip-h-in"></view>
<view>flip-h-in</view>
</view>
<view class="item">
<view class="ld infinite ld-flip-h-out"></view>
<view>flip-h-out</view>
</view>
<view class="item">
<view class="ld infinite ld-flip-v-in"></view>
<view>flip-v-in</view>
</view>
<view class="item">
<view class="ld infinite ld-flip-v-out"></view>
<view>flip-v-out</view>
</view>
<view class="item">
<view class="ld infinite ld-float-btt-in"></view>
<view>float-btt-in</view>
</view>
<view class="item">
<view class="ld infinite ld-float-btt-out"></view>
<view>float-btt-out</view>
</view>
<view class="item">
<view class="ld infinite ld-float-ltr-in"></view>
<view>float-ltr-in</view>
</view>
<view class="item">
<view class="ld infinite ld-float-ltr-out"></view>
<view>float-ltr-out</view>
</view>
<view class="item">
<view class="ld infinite ld-float-rtl-in"></view>
<view>float-rtl-in</view>
</view>
<view class="item">
<view class="ld infinite ld-float-rtl-out"></view>
<view>float-rtl-out</view>
</view>
<view class="item">
<view class="ld infinite ld-float-ttb-in"></view>
<view>float-ttb-in</view>
</view>
<view class="item">
<view class="ld infinite ld-float-ttb-out"></view>
<view>float-ttb-out</view>
</view>
<view class="item">
<view class="ld infinite ld-grow-btt-in"></view>
<view>grow-btt-in</view>
</view>
<view class="item">
<view class="ld infinite ld-grow-btt-out"></view>
<view>grow-btt-out</view>
</view>
<view class="item">
<view class="ld infinite ld-grow-ltr-in"></view>
<view>grow-ltr-in</view>
</view>
<view class="item">
<view class="ld infinite ld-grow-ltr-out"></view>
<view>grow-ltr-out</view>
</view>
<view class="item">
<view class="ld infinite ld-grow-rtl-in"></view>
<view>grow-rtl-in</view>
</view>
<view class="item">
<view class="ld infinite ld-grow-rtl-out"></view>
<view>grow-rtl-out</view>
</view>
<view class="item">
<view class="ld infinite ld-grow-ttb-in"></view>
<view>grow-ttb-in</view>
</view>
<view class="item">
<view class="ld infinite ld-grow-ttb-out"></view>
<view>grow-ttb-out</view>
</view>
<view class="item">
<view class="ld infinite ld-jump-alt-in"></view>
<view>jump-alt-in</view>
</view>
<view class="item">
<view class="ld infinite ld-jump-alt-out"></view>
<view>jump-alt-out</view>
</view>
<view class="item">
<view class="ld infinite ld-jump-in"></view>
<view>jump-in</view>
</view>
<view class="item">
<view class="ld infinite ld-jump-out"></view>
<view>jump-out</view>
</view>
<view class="item">
<view class="ld infinite ld-power-off"></view>
<view>power-off</view>
</view>
<view class="item">
<view class="ld infinite ld-power-on"></view>
<view>power-on</view>
</view>
<view class="item">
<view class="ld infinite ld-rush-btt-in"></view>
<view>rush-btt-in</view>
</view>
<view class="item">
<view class="ld infinite ld-rush-ltr-in"></view>
<view>rush-ltr-in</view>
</view>
<view class="item">
<view class="ld infinite ld-rush-rtl-in"></view>
<view>rush-rtl-in</view>
</view>
<view class="item">
<view class="ld infinite ld-rush-ttb-in"></view>
<view>rush-ttb-in</view>
</view>
<view class="item">
<view class="ld infinite ld-slide-btt-in"></view>
<view>slide-btt-in</view>
</view>
<view class="item">
<view class="ld infinite ld-slide-btt-out"></view>
<view>slide-btt-out</view>
</view>
<view class="item">
<view class="ld infinite ld-slide-ltr-in"></view>
<view>slide-ltr-in</view>
</view>
<view class="item">
<view class="ld infinite ld-slide-ltr-out"></view>
<view>slide-ltr-out</view>
</view>
<view class="item">
<view class="ld infinite ld-slide-rtl-in"></view>
<view>slide-rtl-in</view>
</view>
<view class="item">
<view class="ld infinite ld-slide-rtl-out"></view>
<view>slide-rtl-out</view>
</view>
<view class="item">
<view class="ld infinite ld-slide-ttb-in"></view>
<view>slide-ttb-in</view>
</view>
<view class="item">
<view class="ld infinite ld-slide-ttb-out"></view>
<view>slide-ttb-out</view>
</view>
<view class="item">
<view class="ld infinite ld-spring-btt-in"></view>
<view>spring-btt-in</view>
</view>
<view class="item">
<view class="ld infinite ld-spring-ltr-in"></view>
<view>spring-ltr-in</view>
</view>
<view class="item">
<view class="ld infinite ld-spring-rtl-in"></view>
<view>spring-rtl-in</view>
</view>
<view class="item">
<view class="ld infinite ld-spring-ttb-in"></view>
<view>spring-ttb-in</view>
</view>
<view class="item">
<view class="ld infinite ld-throw-btt-in"></view>
<view>throw-btt-in</view>
</view>
<view class="item">
<view class="ld infinite ld-throw-ltr-in"></view>
<view>throw-ltr-in</view>
</view>
<view class="item">
<view class="ld infinite ld-throw-rtl-in"></view>
<view>throw-rtl-in</view>
</view>
<view class="item">
<view class="ld infinite ld-throw-ttb-in"></view>
<view>throw-ttb-in</view>
</view>
<view class="item">
<view class="ld infinite ld-vortex-alt-in"></view>
<view>vortex-alt-in</view>
</view>
<view class="item">
<view class="ld infinite ld-vortex-alt-out"></view>
<view>vortex-alt-out</view>
</view>
<view class="item">
<view class="ld infinite ld-vortex-in"></view>
<view>vortex-in</view>
</view>
<view class="item">
<view class="ld infinite ld-vortex-out"></view>
<view>vortex-out</view>
</view>
<view class="item">
<view class="ld infinite ld-zoom-in"></view>
<view>zoom-in</view>
</view>
<view class="item">
<view class="ld infinite ld-zoom-out"></view>
<view>zoom-out</view>
</view>
</view>
</view>
</template>
<script setup>
</script>
<style>
@import url('../../components/transition-min/transition.min.css');
.item {
width: 250rpx;
height: 250rpx;
display: inline-block;
text-align: center;
font-size: 28rpx;
}
.list-animation .item view:first-child {
margin: 32px auto;
width: 32px;
height: 32px;
border-radius: 3px;
background: linear-gradient(45deg, #444 0%, #444 15%, transparent 15%, transparent 18%, #444 10%);
}
</style>

View File

@ -1,14 +1,17 @@
<template>
<div class="container">
<custom-title class="title-block" title="门票详情">
</custom-title>
<div class="large-container">
<div class="content1">
<div class="wrap1">
<image src="../../static/zu1053@3x.png"></image>
</div>
<div class="wrap2">门票系统的某个场</div>
<div class="wrap2">大运河博物</div>
</div>
<div class="content2">
<image
src="https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/2ec9fcae-55af-4ccd-8e92-6848282101bb.png"></image>
:src="ticket.ticketCoverPic"></image>
</div>
<div class="content3">
<display-box>
@ -19,17 +22,17 @@
</template>
<template #r1>
<div class="box-right">
首都博物馆门票
{{ticket.ticketName}}
</div>
</template>
<template #l2>
<div class="box-left">
预留手机号 <span style="font-size: 18rpx">(+86)</span>
预留手机号(+86)
</div>
</template>
<template #r2>
<div class="box-right">
192119280121
{{ticket.phone}}
</div>
</template>
<template #l3>
@ -39,7 +42,7 @@
</template>
<template #r3>
<div class="box-right">
2023年12月16日
{{ticket.date?dayjs(ticket.date).format('YYYY年MM月DD日'):'暂无'}}
</div>
</template>
<template #l4>
@ -49,16 +52,17 @@
</template>
<template #r4>
<div class="box-right">
2
{{ticket.number}}
</div>
</template>
</display-box>
</div>
<div class="content4">
<dashed-line></dashed-line>
</div>
<div class="content5">*参观人的身份证信息</div>
<div class="content6">
<div class="content5">
*参观人的身份证信息
</div>
<div class="content6" v-for="item in perList">
<display-box>
<template #l1>
<div class="box-left">
@ -66,8 +70,9 @@
</div>
</template>
<template #r1>
<div class="box-right">
陈x
<div class="box-right" style="display: flex;align-items: center">
<div style="margin-right: 32rpx">{{item.userName}}</div>
<div v-if="statusList.find(x=>x.value===item.status).label" style="width: 112rpx;height: 40rpx;background-color: #000;color: #fff;font-size: 20rpx;border-radius: 22rpx;display: flex;justify-content: center;align-items: center">{{statusList.find(x=>x.value===item.status).label}}</div>
</div>
</template>
<template #l2>
@ -82,60 +87,277 @@
</template>
</display-box>
</div>
<div class="content7">
<div class="content7" style="margin-bottom: 60rpx">
<display-box>
<template #l1>
<div class="box-left">
姓名
文物概览概览
</div>
</template>
<template #r1>
<div class="box-right">
陈x
</div>
<!-- 右侧内容或模板 -->
</template>
<template #l2>
<div class="box-left">
身份证号
文物藏品精华
</div>
</template>
<template #r2>
<div class="box-right">
192119280121
<!-- 右侧内容或模板 -->
</template>
<template #l3>
<div class="box-left">
文化深度
</div>
</template>
<template #r3>
<!-- 右侧内容或模板 -->
</template>
<template #l4>
<div class="box-left">
文物年份
</div>
</template>
<template #r4>
<!-- 右侧内容或模板 -->
</template>
<template #l5>
<div class="box-left">
文物起源
</div>
</template>
<template #r5>
<!-- 右侧内容或模板 -->
</template>
<template #l6>
<div class="box-left">
文物描述
</div>
</template>
<template #r6>
<!-- 右侧内容或模板 -->
</template>
<template #l7>
<div class="box-left">
文化价值
</div>
</template>
<template #r7>
<!-- 右侧内容或模板 -->
</template>
<template #l8>
<div class="box-left">
保存状态
</div>
</template>
<template #r8>
<!-- 右侧内容或模板 -->
</template>
<template #l9>
<div class="box-left">
展品故事
</div>
</template>
<template #r9>
<!-- 右侧内容或模板 -->
</template>
<template #l10>
<div class="box-left">
制作时代
</div>
</template>
<template #r10>
<!-- 右侧内容或模板 -->
</template>
<template #l11>
<div class="box-left">
艺术流派
</div>
</template>
<template #r11>
<!-- 右侧内容或模板 -->
</template>
<template #l12>
<div class="box-left">
获取途径
</div>
</template>
<template #r12>
<!-- 右侧内容或模板 -->
</template>
<template #l13>
<div class="box-left">
展览历程
</div>
</template>
<template #r13>
</template>
<template #l14>
<div class="box-left">
文物级别分类
</div>
</template>
<template #r14>
</template>
<template #l15>
<div class="box-left">
互动体验编码
</div>
</template>
<template #r15>
</template>
<template #l16>
<div class="box-left">
安全保护措施
</div>
</template>
<template #r16>
</template>
<template #l17>
<div class="box-left">
数字化档案编号
</div>
</template>
<template #r17>
</template>
<template #l18>
<div class="box-left">
权属证明区块链记录
</div>
</template>
<template #r18>
</template>
<template #l19>
<div class="box-left">
区块链高度
</div>
</template>
<template #r19>
</template>
<template #l20>
<div class="box-left">
Hash
</div>
</template>
<template #r20>
</template>
<template #l21>
<div class="box-left">
交易hash
</div>
</template>
<template #r21>
</template>
<template #l22>
<div class="box-left">
虚拟收藏认证
</div>
</template>
<template #r22>
</template>
<template #l23>
<div class="box-left">
区块链技术应用
</div>
</template>
<template #r23>
</template>
<template #l24>
<div class="box-left">
物品元数据哈希值
</div>
</template>
<template #r24>
</template>
<template #l25>
<div class="box-left">
登记交易哈希值
</div>
</template>
<template #r25>
</template>
</display-box>
</div>
<div class="content8" @click="returnTicket">
退票
</div>
<div class="content9" v-if="dialog">
<div class="wrap1">
<div class="wrap1_1">是否进行退票?</div>
<div class="wrap1_2">2某某某某某</div>
<div class="wrap1_3">预约日期2023年12月16日</div>
<div class="wrap1_4">
<div class="wrap1_4_1" @click="cancel">取消</div>
<div class="wrap1_4_2">确定</div>
</div>
<div class="wrap1_5">*确认后将不再出现在票库</div>
</div>
</div>
</div>
</template>
<script setup>
import {ref} from 'vue'
import displayBox from '../../components/display-box/index.vue'
import dashedLine from '../../components/dashed-line/index.vue'
const dialog=ref(false)
const cancel=()=>{
dialog.value=false
import {ref} from "vue";
import dayjs from "dayjs";
import {getTicketPerInfo} from "@/http/apis";
const list = [{}]
const statusList=ref([
{
label:'',
value:1
},
{
label:'已退票',
value:2
},
{
label:'已核销',
value:3
},
{
label:'已过期',
value:4
},
{
label:'',
value:5
},
{
label:'盲盒',
value:6
}
const returnTicket=()=>{
dialog.value=true
])
const ticket=ref(uni.getStorageSync('ticket'))
const perList=ref(null)
const getInfo=async ()=>{
const res= await getTicketPerInfo({
appointmentUid:ticket.value.appointmentUid
})
perList.value=res.data.userInfo
console.log(perList.value,'perList.value')
}
getInfo()
</script>
<style scoped lang="scss">
.container {
display: flex;
flex-direction: column;
height: 100vh;
}
.box-left{
font-size: 24rpx;
color: #000;
@ -145,116 +367,30 @@ const returnTicket=()=>{
color: #72665F;
}
.large-container {
overflow-y: auto;
flex-grow: 1;
background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/16968647-fc99-46fe-b95c-620c55b7646f.png');
background-size: 100%;
padding: 32rpx 42rpx 0 42rpx;
.content9{
display: flex;
justify-content: center;
padding-top: 400rpx;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
.wrap1{
width: 510rpx;
height: 352rpx;
background-color: #fff;
border-radius: 20rpx;
display: flex;
flex-direction: column;
align-items: center;
.wrap1_5{
margin-top: 12rpx;
box-sizing: border-box;
text-align: right;
padding-right: 48rpx;
width: 100%;
font-size: 16rpx;
color:#FF5C62;
}
.wrap1_4{
margin-top: 62rpx;
box-sizing: border-box;
display: flex;
width: 100%;
justify-content: space-between;
padding-left: 34rpx;
padding-right: 34rpx;
.wrap1_4_2{
border-radius: 30rpx;
display: flex;
justify-content: center;
align-items: center;
background-color: #F7963B;
color: #fff;
width: 210rpx;
height: 60rpx;
}
.wrap1_4_1{
border-radius: 30rpx;
display: flex;
justify-content: center;
align-items: center;
background-color: #000;
color: #fff;
width: 210rpx;
height: 60rpx;
}
}
.wrap1_3{
font-size: 24rpx;
color: #B29E92;
}
.wrap1_2{
margin-bottom: 16rpx;
font-size: 24rpx;
color: #B29E92;
}
.wrap1_1{
margin-top: 48rpx;
margin-bottom: 16rpx;
font-size: 28rpx;
color: #FF5C62;
}
}
}
.content8{
margin: 0 auto;
border-radius: 30rpx;
font-size: 28rpx;
color: #fff;
width: 436rpx;
height: 60rpx;
display: flex;
justify-content: center;
align-items: center;
background-color: #FF5C62;
}
.content7{
margin-bottom: 72rpx;
}
.content6{
margin-bottom: 20rpx;
margin-top: 12rpx;
margin-bottom: 24rpx;
}
.content5{
margin-bottom: 20rpx;
font-size: 24rpx;
margin-top: 24rpx;
color:#F7963B ;
text-align: left;
font-size: 24rpx;
}
.content3{
margin-top: 42rpx;
margin-bottom: 42rpx;
margin-bottom: 28rpx;
}
.content4 {
margin-bottom: 24rpx;
height: 1rpx;
width: 100%;
background-image: url("../../static/zx303@3x.png");
background-size: 100%;
}
.content2 {
margin-top: 12rpx;

View File

@ -1,172 +1,851 @@
<template>
<div class="container">
<custom-title class="title-block" :title="info.ticketName">
</custom-title>
<div class="main">
<title class="title-block" title="门票系统的某个场馆">
</title>
<div :style="height + 'px'" class="container">
<div class="title">
<tm-icon name="tmicon-position" color="#fff" :fontSize="40"></tm-icon>
<div style="margin-left: 10rpx;">门票系统的某个场馆</div>
<div class="content1">
<div class="wrap1">
<image src="https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/47742fc2-6608-48da-b2f8-b47b36eaffbb.png"></image>
</div>
<div class="wrap2">
{{info.ticketName}}
</div>
</div>
<div class="content2">
<image @click="viewImg" class="wrap2" :src="info.ticketImage"></image>
</div>
<div class="content3" >
<display-box :styleColor="{backgroundColor: '#B1292E'}">
<template #l1 >
<div class="box-left">
门票名称
</div>
</template>
<template #r1 >
<div class="box-right">
门票名称
</div>
</template>
<template #l2 >
<div class="box-left">
剩余数量
</div>
</template>
<template #r2 >
<div class="box-right">
1023/20000
</div>
</template>
</display-box>
</div>
<div class="content4"></div>
<div class="content5">
<display-box>
<template #l1 >
<div class="box-left">
预留手机号 <span style="font-size: 18rpx">(+86)</span>
</div>
</template>
<template #r1 >
<div>
<image src="../../static/logo.png" mode="scaleToFill"
style="width: 664rpx;height:354rpx;margin-top: 20rpx;display: ;" />
<input type="text" v-model="phone" placeholder="填写手机号" placeholder-style="color:#DBDBDB;fontSize:24rpx"/>
</div>
<div class="title-detail">
<div class="item">
<div class="name">门票名称</div>
<div style="margin-left: 20rpx;">首都博物馆门票</div>
</template>
<template #l2 >
<div class="box-left">
验证码
</div>
<tm-divider realColor></tm-divider>
<div class="item">
<div class="name">剩余数量</div>
<div style="margin-left: 20rpx;">1023/20000</div>
</div>
</div>
<div class="line"></div>
<div class="title-detail" style="background: #fff;">
<div class="item">
<div class="name" style="color: #000000;border-right:1rpx solid #BBC5E0;">预留手机号
<span style="font-size: 20rpx;"> (+86)</span>
</div>
<div style="margin-left: 20rpx; color: #000000;">
<input type="text" v-model="formData.phone" @input="changePhone" />
</div>
</div>
<tm-divider realColor></tm-divider>
<div class="item">
<div class="name" style="color: #000000;border-right:1rpx solid #BBC5E0;">验证码</div>
<div class="code">
<input type="text" v-model="formData.code" @input="changePhone" class="inputCode" />
<div style="color:#B1292E" @click="sendCode" v-if="isCode">{{ code }}</div>
<div v-else style="color:#B1292E">{{ formattedTime() }}</div>
</div>
</div>
</div>
<div class="title-detail" style="background: #fff; color: #000000">
<div class="item">
<div class="name" style="color: #000000;border-right:1rpx solid #BBC5E0;">选择时间</div>
<div class="time">
<span style="width: 300rpx;">2023年12月16日</span>
<div style="display: flex;align-items: center;" @click="showHandle">
<span>选择</span>
<tm-icon name="tmicon-angle-right" :font-size="24"></tm-icon>
</div>
</div>
</div>
<tm-divider realColor></tm-divider>
<div class="item">
<div class="name" style=" color: #000000;border-right:1rpx solid #BBC5E0;">选择人数</div>
<div style="margin-left: 20rpx;" class="stepper">
<div style="color: #B29E92;font-size: 24rpx">*单次最多可预约10人</div>
</template>
<template #r2 >
<div style="position: relative;width: 100%">
<div >
<tm-stepper v-model="formData.num" color="#ef952e" bgColor="primary" circular :defaultValue="0"
:max="10" :height="40" :width="160" @change="changeNum"></tm-stepper>
<input v-model="code" type="text" placeholder="填写验证码" placeholder-style="color:#DBDBDB;fontSize:24rpx"/>
</div>
<div @click="getCode" style="z-index:11;position: absolute;top: 50%;transform: translateY(-50%);right: 12rpx;color: #B1292E;font-size: 24rpx">{{buttonText}}</div>
</div>
</template>
</display-box>
</div>
<div class="content6">
<display-box>
<template #l1>
<div class="box-left">
选择时间
</div>
<div v-for="(item, index) in visitorsList" class="card" :key="index">
<div class="card-item">
<div class="name">姓名</div>
<div style="margin-left: 20rpx; color: #000000;">
<input type="text" v-model="item.name" />
</div>
</div>
<tm-divider realColor></tm-divider>
<div class="card-item">
<div class="name">身份证号</div>
<div style="margin-left: 20rpx; color: #000000;">
<input type="idcard" v-model="item.idCard" />
</div>
</div>
</div>
<tm-button @click="reservation" color="#000000" :width="436" :height="60" :round="25">预约</tm-button>
<select-day :show.sync="show"></select-day>
</template>
<template #r1 >
<div class="r1">
<div class="r1_1">{{dateStr?dayjs(dateStr).format('YYYY年MM月DD日'):'暂无'}}</div>
<div class="r1_2" @click="showWin=true">
<image src="https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/2c4872f9-aee5-4b04-9494-94b08c282ed9.png"></image>
</div>
</div>
</template>
<template #l2 >
<div class="box-left">
选择人数
</div>
</template>
<template #r2 >
<div class="r2">
<div class="r2_1" >
*单次最多可预约10人
</div>
<div class="r2_2">
<div class="r2_2_1" @click="peoNumChange(-1)">
<image src="../../static/zu1149@3x.png"></image>
</div>
<div class="r2_2_2">
<input disabled v-model="peoNum" :max="10" type="text">
</div>
<div class="r2_2_3" @click="peoNumChange(1)">
<image src="../../static/zu1150@3x.png"></image>
</div>
</div>
</div>
</template>
</display-box>
</div>
<div class="content7">
*请填写所有参观人的身份证信息
</div>
<div class="content8">
<div class="wrap1" v-for="(item,index) in userInfos" :key="index"> <display-box>
<template #l1>
<div class="box-left">
真实姓名
</div>
</template>
<template #r1>
<div class="box-right">
<input type="text" v-model="item.userName" placeholder="请填写您的真实姓名" placeholder-style="color:#DBDBDB;font-size:24rpx"/>
</div>
</template>
<template #l2>
<div class="box-left">
身份证号码
</div>
</template>
<template #r2>
<div class="box-right">
<input type="text" v-model="item.idCard" placeholder="填写您的身份证号码" placeholder-style="color:#DBDBDB;font-size:24rpx"/>
</div>
</template>
</display-box></div>
</div>
<div class="content9">
<div class="wrap1">*预约后信息不可更改请仔细核对预约信息</div>
<div class="wrap2" @click="goReservation">预约</div>
</div>
<tm-drawer :height="1054" :round="8" hideHeader inContent ref="calendarView" :placement="'bottom'" v-model:show="showWin">
<tm-calendar
format="YYYY-MM-DD"
:dateStyle="dateStyle"
:start="[new Date()]"
:multiple="multiple"
@confirm="confirmData"
color="#F7963B"
v-model:show="showdate"
v-model:model-str="dateStr"
:model="modeltype"
></tm-calendar>
<div class="content10" >
<div class="wrap1" @click="showWin=false">
<image src="../../static/Close@3x.png"></image>
</div>
<div class="wrap2">
请选择参观日期
</div>
<div class="wrap3"></div>
<div class="wrap4">
当前年月{{dayjs(currentData).year()}}{{dayjs(currentData).month()+1}}
</div>
<div class="wrap3"></div>
<div class="wrap5">
<div class="wrap5_2" @click="selectData(index)" :class="[dayjs(item.day).isBefore(dayjs(), 'day')?'wrap5_1':'',index===currentSelectedData?'wrap5_3':'']" v-for="(item,index) in showDay" :key="item.day">
<div class="wrap5_2_1">
{{dayjs(item.day).format('MM月DD日')}}·{{item.week}}
</div>
<div class="wrap5_2_2">
{{ dayjs(item.day).isBefore(dayjs(), 'day')?'*不可预约':'*可预约'}}
</div>
<div class="wrap5_2_3">
{{judgeDate(item.day)}}
</div>
</div>
<div class="wrap5_4" @click="goMore">
<div class="wrap5_4_1">更多</div>
<div class="wrap5_4_2">
<image src="../../static/lj781@3x.png"></image>
</div>
</div>
</div>
<div class="wrap3"></div>
<div class="wrap6" >
请选择参观时间
</div>
<div class="wrap7" >
<div class="wrap7_2" @click="selectTime(index)" :class="[isBeforeCurrentTime(`${showDay?.[currentSelectedData]?.day} ${item}`)?'wrap7_1':'',index===currentSelectedTime?'wrap7_3':'']" v-for="(item,index) in info?.admissionTime.split(',')" :key="item">
<div class="wrap7_2_1">{{item}}入场</div>
<div class="wrap7_2_2">
{{isBeforeCurrentTime(`${showDay?.[currentSelectedData]?.day} ${item}`)?'*不可选':'*可选'}}</div>
</div>
</div>
<div class="wrap3"></div>
<div class="wrap8">
*参观当日需要携带游客本人身份证件<br/>
*入馆时间<br/>
工作日08:30-18:00节假日/双休09:00-20:00具体时间以景区为准<br/>
</div>
<div class="wrap3" style="margin-bottom: 30rpx"></div>
<div class="wrap9" @click="showWin=false">确定</div>
</div>
</tm-drawer>
</div>
</div>
</template>
<script setup>
import { onMounted, ref, reactive, watch } from "vue";
import { useTimer } from '@/tmui/tool/useFun/useTimer'
import selectDay from './select-day/index'
const { start, stop, formattedTime, status, restart, timeObj, times, change } = useTimer({
totalTime: 10, unit: 'ss', format: 'ss秒'
});
let height = ref(0)
let code = ref('获取验证码')
let isCode = ref(true)
let show = ref(false)
const formData = reactive({
phone: '',
code: '',
num: 0
import dayjs from 'dayjs';
import localizedFormat from 'dayjs/plugin/localizedFormat';
import 'dayjs/locale/zh-cn';
import displayBox from '@/components/display-box/index.vue'
dayjs.extend(localizedFormat);
dayjs.locale('zh-cn');
import {useMainStore} from "@/store"
import {ref,onUnmounted} from 'vue'
import {reTicket, sendCode} from "@/http/apis";
const showWin=ref(false)
const peoNum=ref(1)
const store=useMainStore()
const modeltype = ref('day')
const multiple = ref(false)
const dateStr = ref('')
const code=ref('')
const showdate = ref(false)
const currentData=ref(dayjs().format('YYYY-MM-DD'))
const info=ref(uni.getStorageSync('currentBooking'))
const countdown = ref(5);
const isCounting = ref(false);
const buttonText = ref(`获取验证码`);
const userInfo=ref(uni.getStorageSync('userInfo'))
const viewImg=()=>{
uni.previewImage({
urls:[info.value.ticketImage],
indicator:'none'
})
let visitorsList = ref([])
const msg = ref(null)
}
const goReservation=async ()=>{
if (!phone.value){
uni.showToast({
title: '手机号码不能为空',
icon: 'none',
duration: 2000
})
return
}
if (!code.value){
uni.showToast({
title: '验证码不能为空',
icon: 'none',
duration: 2000
})
return
}
if (!dateStr.value){
uni.showToast({
title: '日期不能为空',
icon: 'none',
duration: 2000
})
return
}
/* if (!info.value?.admissionTime?.split(',')[currentSelectedTime.value]){
uni.showToast({
title: '时间不能为空',
icon: 'none',
duration: 2000
})
return
}*/
const data={
ticketUid:info.value.uuid,
userName:userInfo.value.realName,
idCard:userInfo.value.idNum,
phone:phone.value,
code:code.value,
date:dateStr.value?.replaceAll('-','/'),
time:info.value?.admissionTime?.split(',')[currentSelectedTime.value],
userInfo:userInfos.value
}
const res=await reTicket(data)
console.log(res,'goReservation')
}
const getCode=async ()=>{
console.log(buttonText.value,'buttonText.value')
if (isCounting.value) return;
isCounting.value = true;
countdown.value = 5;
buttonText.value = `剩余 ${countdown.value}`;
const res= await sendCode({telNum:phone.value})
if (res.code===200){
uni.showToast({
title: '发送成功',
icon: 'none',
duration: 2000
})
}
const intervalId = setInterval(() => {
if (countdown.value > 0) {
countdown.value--;
buttonText.value = `剩余 ${countdown.value}`;
} else {
clearInterval(intervalId);
isCounting.value = false;
buttonText.value = `重新获取验证码`;
}
}, 1000);
onMounted(() => {
uni.createSelectorQuery().select('.title-block').boundingClientRect(data => {
let res = uni.getSystemInfoSync();
height = res.windowHeight - data.bottom;
}).exec()
})
const sendCode = () => {
restart()
isCode.value = false;
if (status.value == -1 || status.value == 0) {
start();
}
}
watch(times, (newValue) => {
if (newValue === 0) {
code.value = '重新获取';
isCode.value = true;
stop()
}
})
const changeNum = (value) => {
formData.num = value
}
watch(() => formData.num, (newValue, oldValue) => {
console.log(newValue, oldValue)
if (newValue > oldValue) {
for (let i = 0; i < newValue - oldValue; i++) {
visitorsList.value.push({
name: '',
idCard: ''
})
}
}
if (newValue < oldValue) {
visitorsList.value.splice(-(oldValue - newValue))
}
})
onUnmounted(() => {
clearInterval(intervalId);
});
const reservation = () => {
console.log(visitorsList.value)
const result = visitorsList.value.filter((item) => item.name && item.idCard)
if (result.map((item) => item.idCard).some((n, index, arr) => arr.indexOf(n) !== index)) {
return uni.showToast({
title: '身份证不能重复',
icon: "error",
});
}
if (Object.values(formData).some((item) => !item)) {
return uni.showToast({
title: '信息填写完整',
icon: "error",
});
const userInfos=ref([{userName:'',idCard:''}])
const phone=ref('')
const currentSelectedData=ref(undefined)
const selectData=(index)=>{
if (dayjs(showDay.value[index].day).isBefore(dayjs(), 'day')){
return
}
dateStr.value=showDay.value[index].day
currentSelectedTime.value=undefined
currentSelectedData.value=index
}
const isBeforeCurrentTime=(dateTimeStr)=> {
const givenDateTime = dayjs(dateTimeStr);
const now = dayjs();
return givenDateTime.isBefore(now);
}
const currentSelectedTime=ref(undefined)
const selectTime=(index)=>{
if (isBeforeCurrentTime(`${showDay.value?.[currentSelectedData.value]?.day} ${info.value?.admissionTime.split(',')[index]}`)){
return
}
currentSelectedTime.value=index
}
const peoNumChange=(num)=>{
peoNum.value=peoNum.value+num
if (num>0){
userInfos.value.push({userName:'',idCard:''})
}else if (num<0){
userInfos.value.pop()
}
}
const showHandle = () => {
show.value = false
const confirmData=(data)=>{
currentSelectedData.value=1
showDay.value=weekDays(data?.[0]?.replaceAll('/', '-'))
}
const judgeDate=(relativeDate)=> {
const givenDate = dayjs(relativeDate);
const today = dayjs();
const tomorrow = dayjs().add(1, 'day');
if (givenDate.isSame(today, 'day')) {
return '今日';
} else if (givenDate.isSame(tomorrow, 'day')) {
return '明日';
} else {
return '';
}
}
const weekDays = (baseDate) => {
const base = baseDate ? dayjs(baseDate) : dayjs();
const formatDay = base.format('YYYY-MM-DD');
const formatWeek = base.format('dd');
const today = {
day: formatDay,
week: formatWeek
};
const yesterday = base.subtract(1, 'day');
const yesterdayFormatted = {
day: yesterday.format('YYYY-MM-DD'),
week: yesterday.format('dd')
};
const tomorrow = base.add(1, 'day');
const tomorrowFormatted = {
day: tomorrow.format('YYYY-MM-DD'),
week: tomorrow.format('dd')
};
return [yesterdayFormatted, today, tomorrowFormatted];
};
const showDay=ref(weekDays())
console.log(showDay.value,'showDay')
const dateStyle = ref([
{
date: '2022-12-8', //
text: false, //
color: '#F7963B', //.
extra: '测试' //
},
{
date: '2022-12-24', //
text: false, //
color: '#F7963B', //.
extra: '签到' //
}
])
const rpxToPx=(rpx)=>{
const systemInfo = uni.getSystemInfoSync();
const screenWidth = systemInfo.windowWidth;
return rpx * screenWidth / 750;
}
const goMore=()=>{
showdate.value=true
}
</script>
<style lang="scss" scoped>
.main {
.content10{
position: relative;
padding: 64rpx 24rpx 108rpx 24rpx;
.wrap9{
margin: 0 auto;
width: 436rpx;
height: 60rpx;
background-color: #000;
color: #fff;
font-size: 28rpx;
border-radius: 30rpx;
display: flex;
justify-content: center;
align-items: center;
}
.wrap8{
margin-bottom: 24rpx;
margin-top: 18rpx;
font-size: 20rpx;
color:#B29E92 ;
}
.wrap7{
margin-bottom: 70rpx;
margin-top: 20rpx;
display: flex;
justify-content: space-between;
overflow-x: auto;
.wrap7_3{
box-sizing: border-box;
border-radius: 32rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 152rpx;
height: 98rpx;
background-color: #F7963B!important;
.wrap7_2_2{
font-size: 16rpx;
color: #fff!important;
}
.wrap7_2_1{
margin-bottom: 2rpx;
color: #fff!important;
font-size: 24rpx;
}
}
.wrap7_2{
margin-right: 30rpx;
border: 2rpx solid #F7963B;
box-sizing: border-box;
border-radius: 32rpx;
flex-shrink: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 152rpx;
height: 98rpx;
background-color: #fff;
.wrap7_2_2{
font-size: 16rpx;
color: #B29E92;
}
.wrap7_2_1{
margin-bottom: 2rpx;
color: #000;
font-size: 24rpx;
}
}
.wrap7_1{
border-radius: 32rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 152rpx;
height: 98rpx;
background-color: #D8D8D8;
border: none;
.wrap7_2_2{
font-size: 16rpx;
color: #868686;
}
.wrap7_2_1{
margin-bottom: 2rpx;
color: #000;
font-size: 24rpx;
}
}
}
.wrap6{
margin-top: 38rpx;
font-size: 32rpx;
color: #000;
}
.wrap5{
margin-bottom: 72rpx;
margin-top: 54rpx;
display: flex;
justify-content: space-between;
.wrap5_4{
width: 60rpx;
height: 98rpx;
border-radius: 30rpx;
background-color: #F7963B;
display: flex;
align-items: center;
justify-content: center;
.wrap5_4_1{
margin-right: 8rpx;
font-size: 20rpx;
color: #fff;
writing-mode: vertical-rl;
}
.wrap5_4_2{
image{
width: 11.77rpx;
height: 25.53rpx;
}
}
}
.wrap5_3{
position: relative;
width: 196rpx;
height: 98rpx;
background-color: #F7963B!important;
border-radius: 32rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.wrap5_2_3{
left: 50%;
transform: translateX(-50%);
position: absolute;
bottom: -40rpx;
font-size: 24rpx;
color: #F7963B!important;
}
.wrap5_2_2{
color: #fff!important;
font-size: 16rpx;
}
.wrap5_2_1{
margin-bottom: 10rpx;
color: #fff!important;
font-size: 24rpx;
}
}
.wrap5_2{
border: 2rpx solid #F7963B;
box-sizing: border-box;
width: 196rpx;
height: 98rpx;
background-color: #fff;
border-radius: 32rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
.wrap5_2_3{
left: 50%;
transform: translateX(-50%);
position: absolute;
bottom: -40rpx;
font-size: 24rpx;
color: #F7963B;
}
.wrap5_2_2{
color: #B29E92;
font-size: 16rpx;
}
.wrap5_2_1{
margin-bottom: 10rpx;
color: #000;
font-size: 24rpx;
}
}
.wrap5_1{
width: 196rpx;
height: 98rpx;
background-color: #D8D8D8;
border-radius: 32rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: none;
.wrap5_2_2{
color: #868686;
font-size: 16rpx;
}
.wrap5_2_1{
margin-bottom: 10rpx;
color: #000;
font-size: 24rpx;
}
}
}
.wrap4{
margin-bottom: 24rpx;
margin-top: 16rpx;
font-size: 24rpx;
color:#F7963B;
}
.wrap3{
background-color: #868686;
height: 1rpx;
width: 100%;
}
.wrap2{
margin-bottom: 44rpx;
font-weight: bold;
color: #000;
font-size: 32rpx;
}
.wrap1{
top: 52rpx;
right: 46rpx;
position: absolute;
width: 48rpx;
height: 48rpx;
image{
width: 100%;
height: 100%;
}
}
}
.container{
display: flex;
flex-direction: column;
height: 100vh;
.main{
overflow-y: auto;
box-sizing: border-box;
padding: 32rpx 42rpx 0 42rpx;
width: 100vw;
flex: 1;
background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/16968647-fc99-46fe-b95c-620c55b7646f.png');
background-size: 100%;
.content9{
margin-bottom: 78rpx;
margin-top: 80rpx;
display: flex;
flex-direction: column;
align-items: center;
.wrap2{
margin-top: 14rpx;
font-size: 28rpx;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
background-color: #000;
border-radius: 30rpx;
width: 436rpx;
height: 60rpx;
}
.wrap1{
color: #B1292E;
font-size: 16rpx;
}
}
.content8{
margin-top: 12rpx;
.wrap1{
margin-bottom: 24rpx;
.box-left{
color: #000;
font-size: 24rpx;
}
}
}
.content7{
color: #F7963B;
margin-top: 20rpx;
font-size: 24rpx;
}
.content6{
margin-top:24rpx;
.r2{
position: relative;
.r2_2{
position: absolute;
top: 50%;
transform: translateY(-45%);
right: 0;
display: flex;
align-items: center;
.r2_2_3{
width: 31rpx;
height: 31rpx;
image{
height: 100%;
width: 100%;
}
}
.r2_2_1{
width: 31rpx;
height: 31rpx;
margin-right: 10rpx;
image{
height: 100%;
width: 100%;
}
}
.r2_2_2{
margin-right: 10rpx;
input{
text-align: center;
color: #000;
font-size: 24rpx;
width: 114rpx;
height: 54rpx;
background-color: #EFEFEF;
}
}
}
.r2_1{
color: #B29E92;
font-size: 16rpx;
}
}
.r1{
position: relative;
.r1_2{
top: 50%;
position: absolute;
transform: translateY(-50%);
right: 12rpx;
image{
width: 64rpx;
height: 32rpx;
}
}
}
.box-left{
display: flex;
align-items: center;
color: #000;
font-size: 24rpx;
}
}
.content5{
margin-top:40rpx ;
.r2{
position: relative;
.r2_2{
right: 42rpx;
top: 50%;
transform: translateY(-10%);
position: absolute;
color:#B1292E ;
font-size: 24rpx;
}
}
.box-left{
color: #000;
font-size: 24rpx;
}
}
.content4 {
margin-top: 30rpx;
height: 1rpx;
width: 100%;
background-image: url("../../static/zx303@3x.png");
background-size: 100%;
}
.content3{
margin-top: 20rpx;
.box-right{
color: #fff;
font-size: 24rpx;
}
.box-left{
color: #fff;
font-size: 24rpx;
}
}
.content2{
margin-top: 12rpx;
border-radius: 20rpx;
overflow: hidden;
image{
width: 664rpx;
height: 354rpx;
}
}
.content1{
box-sizing: border-box;
background-image: url("https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/20a57e10-8b08-4694-b083-d09e345a58a4.png");
width: 100%;
height: 60rpx;
background-size: 100%;
display: flex;
align-items: center;
padding-left: 20rpx;
.wrap2{
font-size: 32rpx;
color: #fff;
}
.wrap1{
margin-right: 10rpx;
image{
width: 21.18rpx;
height: 29rpx;
}
}
}
}
}
/*.top{
height: 100vh;
display: flex;
flex-direction: column;
}
.main {
overflow-y: auto;
flex-grow: 1;
width: 100%;
background-image: url('https://cdns.fontree.cn/fonchain-main/prod/image/1833/avatar/16968647-fc99-46fe-b95c-620c55b7646f.png');
background-size: 100%;
@ -287,5 +966,5 @@ const showHandle = () => {
width: 130rpx !important;
}
}
}*/
</style>

View File

@ -1,25 +1,225 @@
<template>
<div>
<tm-drawer placement="bottom" okText='确认' v-model:show="show" @close="close">
<tm-drawer placement="bottom" okText='确认' :show="show" :height="1054" :hideHeader=true>
<div class="container">
<tm-icon name="tmicon-times-circle" color="#ccc" class="icon-close" @click="close"></tm-icon>
<div class="title">请选择参观日期</div>
<tm-divider color="#868686"></tm-divider>
<div>当前年月日{{ currentDate }}</div>
<tm-divider color="#868686"></tm-divider>
<div class="date-box">
<div class="date-box-item" v-for="(item, index) in dateList" :key="index">
<div :class="[index === currentIndex ? 'active' : '', 'date-box-item-day']"
@click="chooseDatehandle($event, item.date, index)">
<div :style="{ fontSize: '30rpx', fontWeight: '600' }">{{ item.date }}·{{
dayChineseMap[item.week] }}
</div>
<div style="font-size: 22rpx;">*可预约</div>
</div>
<div style="font-size: 30rpx;color:#F7963B ;">{{ dayjs().format('MM-DD') + '日' == item.date
? '今日' : '明天' }}</div>
</div>
<div class="more-date" @click="changeMoreDate">
<div style="font-size: 30rpx;width: 100rpx;">选择更多日期</div>
<div>
<tm-icon name="tmicon-angle-right" :fontSize="26" color="#fff"></tm-icon>
</div>
</div>
</div>
<tm-divider color="#868686"></tm-divider>
<div class="title">请选择参观日期</div>
<div class="time-box">
<div class="time-box-item">
<div :style="{ fontWeight: '600' }">08:00入场</div>
<div :style="{ fontSize: '22rpx', }">*不可选</div>
</div>
<div class="time-box-item">
<div :style="{ fontWeight: '600' }">08:00入场</div>
<div :style="{ fontSize: '22rpx', }">*不可选</div>
</div>
<div class="time-box-item">
<div :style="{ fontWeight: '600' }">08:00入场</div>
<div :style="{ fontSize: '22rpx', }">*不可选</div>
</div>
<div class="time-box-item">
<div :style="{ fontWeight: '600' }">08:00入场</div>
<div :style="{ fontSize: '22rpx', }">*不可选</div>
</div>
<div class="time-box-item">
<div :style="{ fontWeight: '600' }">08:00入场</div>
<div :style="{ fontSize: '22rpx', }">*不可选</div>
</div>
<div class="time-box-item">
<div :style="{ fontWeight: '600' }">08:00入场</div>
<div :style="{ fontSize: '22rpx', }">*不可选</div>
</div>
</div>
<tm-divider color="#868686"></tm-divider>
<div class="tips">
<div>*参观当日需要携带游客本人身份证件</div>
<div> *入馆时间</div>
<div>
工作日08:30-18:00节假日/双休09:00-20:00具体时间以景区为准
</div>
</div>
<tm-divider color="#868686"></tm-divider>
<div style="margin: auto;">
<tm-button color="#000000" :width="436" :height="60" :round="25">确定</tm-button>
</div>
<tm-calendar format="YYYY-MM-DD" @confirm="comfirmCal" color="#F7963B" v-model:show="isMoreDate" update:show
:start="[dayjs()]" :end="[endDate]"></tm-calendar>
</div>
</tm-drawer>
</div>
</template>
<script setup>
import { defineProps, onMounted, defineEmits, watch } from 'vue'
import { defineProps, ref, defineEmits, watch, onMounted, computed } from 'vue'
import dayjs from 'dayjs'
const props = defineProps({
show: {
type: Boolean,
default: false
}
})
const emits = defineEmits(["update:show"]);
let currentIndex = ref(-1)
let currentDate = ref('')
let isMoreDate = ref(false)
const dayChineseMap = ['日', '一', '二', '三', '四', '五', '六'];
let dateList = ref([
{
date: dayjs().format('MM-DD'),
week: dayjs().day(),
},
{
date: dayjs().add(1, 'day').format('MM-DD'),
week: dayjs().add(1, 'day').day(),
}
])
const chooseDatehandle = (e, date, index) => {
currentDate.value = dayjs().year() + '-' + date
currentIndex.value = index
}
const close = () => {
emits("update:show", false);
}
const changeMoreDate = () => {
isMoreDate.value = true
}
const endDate = computed(() => {
const lastDayOfMonth = dayjs().endOf('month');
const year = lastDayOfMonth.year();
const month = lastDayOfMonth.month() + 1;
const date = lastDayOfMonth.date();
return dayjs(year + '-' + month + '-' + date).add(2, 'month');
});
const comfirmCal = (date) => {
currentDate.value = date[0].split('/').join('-')
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.container {
display: flex;
flex-direction: column;
padding: 30rpx 32rpx;
box-sizing: border-box;
.icon-close {
position: absolute;
right: 20rpx;
top: 20rpx;
}
.title {
font-size: 36rpx;
}
.date-box {
width: 100%;
display: flex;
margin: 20rpx 0;
.date-box-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.date-box-item-day {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-right: 20rpx;
height: 100rpx;
border-radius: 32rpx;
border: 1px solid #F7963B;
padding: 20rpx 18rpx;
box-sizing: border-box;
width: 240rpx;
}
.active {
background: #F7963B;
color: #fff;
}
}
.more-date {
display: flex;
padding: 20rpx 18rpx;
align-items: center;
justify-content: start;
height: 100rpx;
box-sizing: border-box;
border-radius: 32rpx;
color: #fff;
background-color: #F7963B;
}
}
.time-box::-webkit-scrollbar {
display: none;
/* 对于Webkit浏览器 */
}
.time-box {
width: 100%;
display: flex;
overflow-x: auto;
margin-top: 20rpx;
box-sizing: border-box;
-ms-overflow-style: none;
/* 对于IE和Edge */
scrollbar-width: none;
margin-bottom: 20rpx;
/* 对于Firefox */
.time-box-item {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
font-size: 30rpx;
padding: 20rpx;
margin-right: 20rpx;
border-radius: 32rpx;
border: 1rpx solid #F7963B;
width: 188rpx;
height: 114rpx;
box-sizing: border-box;
}
}
.tips {
display: flex;
flex-direction: column;
color: #B29E92;
font-size: 26rpx;
}
}
</style>

View File

@ -1,12 +1,15 @@
<template>
<div class="large-container">
<div class="content1">
<custom-title class="title-block" :title="ticket?.ticketName">
</custom-title>
<div class="content1" :style="{backgroundImage:`url(${ticket?.ticketCoverPic})`}">
<div class="wrap1" @click="dialog=true">
<image src="../../static/zu1041@3x (1).png"></image>
<image src="../../static/zu1041@3x.png"></image>
</div>
</div>
<div class="content2">
<div class="grid-cell red">
<div class="grid-cell red" @click="showWin=true">
<image src="../../static/zu1067@3x.png"></image>
</div>
<div class="grid-cell black"> <image src="../../static/zu1063@3x.png"></image></div>
@ -19,7 +22,7 @@
<image src="../../static/zu1065@3x.png"></image>
</div>
</div>
<div v-if="dialog" class="content4">
<div v-show="dialog" class="content4">
<div class="wrap1">
<div class="wrap1_1">
<div class="wrap1_1_1">
@ -43,7 +46,7 @@
<div class="wrap1_1_2_2">首都博物馆</div>
</div>
</div>
<div class="wrap1_2" @click="dialog=false">
<div class="wrap1_2" @click="closeDialog">
<div class="wrap1_2_1" >退出</div>
<image style="width:28rpx;height: 28rpx " src="../../static/zu1043@3x.png"></image>
</div>
@ -52,8 +55,14 @@
</div>
</template>
<script setup>
import {ref} from 'vue'
import {ref,nextTick} from 'vue'
const dialogRef=ref(null)
const dialog=ref(false)
const ticket= uni.getStorageSync('ticket')
const closeDialog=()=>{
dialog.value=false
}
</script>
<style scoped lang="scss">
.large-container{

BIN
src/static/112121@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
src/static/Close@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

BIN
src/static/lj781@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

BIN
src/static/tbys@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

BIN
src/static/zu1105@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 KiB

BIN
src/static/zu1123@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
src/static/zu1149@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
src/static/zu1150@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
src/static/zu1216@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
src/static/zu758@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 209 B

After

Width:  |  Height:  |  Size: 209 B

13
src/store/index.js Normal file
View File

@ -0,0 +1,13 @@
import {defineStore} from 'pinia'
import {ref} from 'vue'
import {getInfo} from "@/http/apis";
export const useMainStore = defineStore('main', () => {
const titleHeight = ref('')
const tabHeight = ref('')
const currentBooking = ref(null)
const isShow = ref(true)
return {titleHeight, tabHeight, isShow,currentBooking}
}
)

View File

@ -2,6 +2,7 @@ import { defineConfig } from "vite";
import uni from "@dcloudio/vite-plugin-uni";
import vueJsx from "@vitejs/plugin-vue-jsx";
import { resolve } from "path"
// import Components from 'unplugin-vue-components/vite'
// https://vitejs.dev/config/
export default defineConfig({