init: llpreheat 学法送演唱会门票 H5项目
This commit is contained in:
@@ -0,0 +1,370 @@
|
||||
<template>
|
||||
<view class="content">
|
||||
<view class="page-body">
|
||||
<view class="loading-box" v-if="questions.length === 0 && !loadError">
|
||||
<u-loading mode="circle" size="40"></u-loading>
|
||||
<text class="loading-text">加载题目中...</text>
|
||||
</view>
|
||||
<view class="loading-box" v-if="loadError">
|
||||
<text class="error-text">{{ loadError }}</text>
|
||||
<button class="retry-btn" @tap="fetchQuestions">重新加载</button>
|
||||
</view>
|
||||
<template v-if="questions.length > 0">
|
||||
<image src="https://aliyunoss.hn12301.net/learnlaw/learn_dtbj.jpg" mode="widthFix" style="width:750rpx;"></image>
|
||||
<view style="position:absolute;top:200rpx;left:0;z-index:0;padding: 30rpx;" >
|
||||
<view class="quiz-header">
|
||||
<view class="timer-wrap">
|
||||
<text class="timer-label">答题时间</text>
|
||||
<text class="timer">{{ formatTime(usedSeconds) }}</text>
|
||||
</view>
|
||||
<text class="answered-count">已答 {{ answeredCount }} / {{ questions.length }}</text>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="question-item" v-for="(q, qi) in questions" :key="q.question_id">
|
||||
<view class="q-header">
|
||||
<text class="q-index">{{ qi + 1 }}</text>
|
||||
<text class="q-content">{{ q.question_content }}</text>
|
||||
</view>
|
||||
<view class="options">
|
||||
<view
|
||||
class="option"
|
||||
v-for="(opt, oi) in q.answerList"
|
||||
:key="opt.id"
|
||||
:class="{ selected: answers[q.question_id] === oi }"
|
||||
@tap="select(q.question_id, oi)"
|
||||
>
|
||||
<view class="option-radio" :class="{ checked: answers[q.question_id] === oi }">
|
||||
<view v-if="answers[q.question_id] === oi" class="radio-dot"></view>
|
||||
</view>
|
||||
<text class="option-text">{{ opt.answer_content }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="tips" v-if="answers[q.question_id] !== undefined">
|
||||
<text class="tips-label">解析</text>
|
||||
<text class="tips-text">{{ q.tips }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<button class="submit-btn" @tap="submit">提交</button>
|
||||
</view>
|
||||
|
||||
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
questions: [],
|
||||
answers: {},
|
||||
loadError: '',
|
||||
startTime: 0,
|
||||
usedSeconds: 0,
|
||||
timer: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
answeredCount() {
|
||||
return Object.keys(this.answers).length
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.fetchQuestions()
|
||||
},
|
||||
onUnload() {
|
||||
this.stopTimer()
|
||||
},
|
||||
methods: {
|
||||
fetchQuestions() {
|
||||
this.loadError = ''
|
||||
const userId = uni.getStorageSync('userId')
|
||||
const userPhone = uni.getStorageSync('userPhone')
|
||||
this.$u.api.queryWenjuanQA({
|
||||
wenjuan_id: 1001,
|
||||
user_id: userId,
|
||||
user_phone: userPhone
|
||||
}).then(res => {
|
||||
if (res.code === 0 && res.Data?.wenjuanTmList) {
|
||||
this.questions = res.Data.wenjuanTmList
|
||||
this.startTimer()
|
||||
} else {
|
||||
this.loadError = res.msg || '数据异常'
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('获取题目失败', err)
|
||||
this.loadError = '网络请求失败'
|
||||
})
|
||||
},
|
||||
startTimer() {
|
||||
this.startTime = Date.now()
|
||||
this.timer = setInterval(() => {
|
||||
this.usedSeconds = Math.floor((Date.now() - this.startTime) / 1000)
|
||||
}, 1000)
|
||||
},
|
||||
stopTimer() {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer)
|
||||
this.timer = null
|
||||
}
|
||||
},
|
||||
formatTime(sec) {
|
||||
const m = Math.floor(sec / 60)
|
||||
const s = sec % 60
|
||||
return (m < 10 ? '0' : '') + m + ':' + (s < 10 ? '0' : '') + s
|
||||
},
|
||||
select(qId, oi) {
|
||||
this.$set(this.answers, qId, oi)
|
||||
},
|
||||
submit() {
|
||||
if (this.answeredCount < this.questions.length) {
|
||||
uni.showToast({ title: '还有题目未作答', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this.stopTimer()
|
||||
const userId = uni.getStorageSync('userId')
|
||||
const userPhone = uni.getStorageSync('userPhone')
|
||||
const details = this.questions.map(q => ({
|
||||
question_id: q.question_id,
|
||||
user_answer: q.answerList[this.answers[q.question_id]]?.answer_content || ''
|
||||
}))
|
||||
this.$u.api.submitQA({
|
||||
user_id: userId,
|
||||
user_phone: userPhone,
|
||||
wenjuan_id: 1001,
|
||||
use_time: this.usedSeconds,
|
||||
total_question_num: this.questions.length,
|
||||
channel: 1,
|
||||
user_answer_details: details
|
||||
}).then(res => {
|
||||
if (res.code === 0) {
|
||||
uni.setStorageSync('resultData', {
|
||||
...res.Data,
|
||||
use_time: this.usedSeconds,
|
||||
total_question_num: this.questions.length
|
||||
})
|
||||
uni.navigateTo({ url: '/pages/result/result' })
|
||||
} else {
|
||||
uni.showToast({ title: res.msg || '提交失败', icon: 'none' })
|
||||
}
|
||||
}).catch(() => {
|
||||
uni.showToast({ title: '网络异常', icon: 'none' })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #B3E9F3;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
.page-body {
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.quiz-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
padding: 16rpx 24rpx;
|
||||
background: #71BCFB66;
|
||||
border-radius: 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.quiz-scroll {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.timer-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.timer-label {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.timer {
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.answered-count {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
|
||||
.question-item {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 20rpx;
|
||||
padding: 32rpx 28rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.q-header {
|
||||
display: flex;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.q-index {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
line-height: 44rpx;
|
||||
text-align: center;
|
||||
background: #FD5401;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
margin-right: 16rpx;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.q-content {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14rpx;
|
||||
}
|
||||
|
||||
.option {
|
||||
padding: 22rpx 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 2rpx solid #eee;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.option.selected {
|
||||
border-color: #FD5401;
|
||||
background: rgba(253, 84, 1, 0.05);
|
||||
}
|
||||
|
||||
.option-radio {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 50%;
|
||||
border: 3rpx solid #CCC;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 16rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.option-radio.checked {
|
||||
border-color: #FD5401;
|
||||
}
|
||||
|
||||
.radio-dot {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
border-radius: 50%;
|
||||
background: #FD5401;
|
||||
}
|
||||
|
||||
.option-text {
|
||||
font-size: 27rpx;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.tips {
|
||||
background: rgba(255, 247, 237, 0.95);
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx;
|
||||
margin-top: 16rpx;
|
||||
border-left: 6rpx solid #FD5401;
|
||||
}
|
||||
|
||||
.tips-label {
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
color: #FD5401;
|
||||
display: block;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.tips-text {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
background: linear-gradient(135deg, #FD5401, #FF7A3D);
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: 44rpx;
|
||||
margin: 10rpx 0 40rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(253, 84, 1, 0.3);
|
||||
}
|
||||
|
||||
.loading-box {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
margin-top: 30rpx;
|
||||
width: 240rpx;
|
||||
height: 72rpx;
|
||||
line-height: 72rpx;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #FD5401;
|
||||
border: none;
|
||||
border-radius: 36rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<view>
|
||||
<image src="https://aliyunoss.hn12301.net/learnlaw/xfsp_p_bj.jpg" style="width: 750rpx;" mode="widthFix"></image>
|
||||
<view style="position: absolute;top: 1210rpx;width: 300rpx;left: 225rpx;height: 80rpx;" @click="goRegister"></view>
|
||||
|
||||
<view class="side-tab" style="top: 350rpx;" hover-class="side-tab-hover" @click="goRecords">答题记录</view>
|
||||
<view class="side-tab" style="top: 430rpx;" hover-class="side-tab-hover" @click="goRules">活动规则</view>
|
||||
<view class="blocked-mask" v-if="blocked">
|
||||
<text class="blocked-tip">{{ blockTip }}</text>
|
||||
<text class="blocked-sub">本活动仅支持手机微信访问</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const APP_ID = 'wx71c218bd9ff0e2da'
|
||||
const SCOPE = 'snsapi_base'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
blocked: false,
|
||||
blockTip: ''
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
// if (new Date().getTime() < new Date('2026/09/02 10:00:00')) {
|
||||
// uni.redirectTo({
|
||||
// url:'/pages/jqqd/jqqd'
|
||||
// })
|
||||
// }
|
||||
this.initLogin()
|
||||
},
|
||||
onShow() {
|
||||
if (this.blocked) return
|
||||
const openId = uni.getStorageSync('openId')
|
||||
const isRegistered = uni.getStorageSync('isRegistered')
|
||||
const userId = uni.getStorageSync('userId')
|
||||
if (openId && (!isRegistered || !userId)) {
|
||||
this.loginWithOpenId(openId)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initLogin() {
|
||||
if (!this.isWechatBrowser()) {
|
||||
this.block('请在微信中打开')
|
||||
return
|
||||
}
|
||||
if (this.isPcWechat()) {
|
||||
this.block('请使用手机微信打开')
|
||||
return
|
||||
}
|
||||
let openId = uni.getStorageSync('openId')
|
||||
if (openId) {
|
||||
this.loginWithOpenId(openId)
|
||||
return
|
||||
}
|
||||
const code = this.getCodeFromUrl()
|
||||
if (code) {
|
||||
this.exchangeOpenId(code)
|
||||
} else {
|
||||
this.wechatAuth()
|
||||
}
|
||||
},
|
||||
block(tip) {
|
||||
this.blocked = true
|
||||
this.blockTip = tip
|
||||
},
|
||||
isWechatBrowser() {
|
||||
return /MicroMessenger/i.test(navigator.userAgent)
|
||||
},
|
||||
isPcWechat() {
|
||||
return /MicroMessenger/i.test(navigator.userAgent) && /WindowsWechat|MacWechat/i.test(navigator.userAgent)
|
||||
},
|
||||
getCodeFromUrl() {
|
||||
const match = window.location.href.match(/[?&]code=([^&#]+)/)
|
||||
return match ? match[1] : null
|
||||
},
|
||||
cleanUrlCode() {
|
||||
const cleanUrl = window.location.href.replace(/[?&]code=[^&#]*(&|#|$)/, (m, p1) => p1 === '&' ? '?' : (
|
||||
p1 === '#' ? '#' : ''))
|
||||
window.history.replaceState({}, '', cleanUrl)
|
||||
},
|
||||
wechatAuth() {
|
||||
const redirectUri = encodeURIComponent(window.location.href.split('?')[0].split('#')[0])
|
||||
const url =
|
||||
`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${APP_ID}&redirect_uri=${redirectUri}&response_type=code&scope=${SCOPE}&state=STATE#wechat_redirect`
|
||||
window.location.href = url
|
||||
},
|
||||
loginWithOpenId(openId) {
|
||||
this.$u.api.wxOpenidLogin({ wenjuan_id: 1001, open_id: openId }).then(loginRes => {
|
||||
if (!loginRes.Data) {
|
||||
uni.setStorageSync('isRegistered', false)
|
||||
return
|
||||
}
|
||||
if (loginRes.code === 0 && loginRes.Data?.user_name && loginRes.Data?.user_phone) {
|
||||
uni.setStorageSync('isRegistered', true)
|
||||
uni.setStorageSync('userId', loginRes.Data.user_id)
|
||||
uni.setStorageSync('userName', loginRes.Data.user_name)
|
||||
uni.setStorageSync('userPhone', loginRes.Data.user_phone)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('登录请求失败', err)
|
||||
})
|
||||
},
|
||||
goRegister() {
|
||||
const isRegistered = uni.getStorageSync('isRegistered')
|
||||
if (isRegistered) {
|
||||
uni.navigateTo({
|
||||
url: '/pages/answer/answer'
|
||||
})
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/pages/register/register'
|
||||
})
|
||||
},
|
||||
goRecords() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/records/records'
|
||||
})
|
||||
},
|
||||
goRules() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/rules/rules'
|
||||
})
|
||||
},
|
||||
exchangeOpenId(code) {
|
||||
this.$u.api.getWxOpenId(code).then(res => {
|
||||
if (res.code !== 0 || !res.Data || !res.Data.openid) return
|
||||
const openId = res.Data.openid
|
||||
uni.setStorageSync('openId', openId)
|
||||
this.cleanUrlCode()
|
||||
return this.$u.api.wxOpenidLogin({ wenjuan_id: 1001, open_id: openId })
|
||||
}).then(loginRes => {
|
||||
if (loginRes && loginRes.code === 0 && loginRes.Data && loginRes.Data.user_name && loginRes.Data.user_phone) {
|
||||
uni.setStorageSync('isRegistered', true)
|
||||
uni.setStorageSync('userId', loginRes.Data.user_id || loginRes.Data.uesr_id)
|
||||
uni.setStorageSync('userName', loginRes.Data.user_name)
|
||||
uni.setStorageSync('userPhone', loginRes.Data.user_phone)
|
||||
} else if (loginRes && !loginRes.Data) {
|
||||
uni.setStorageSync('isRegistered', false)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('请求失败', err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page{
|
||||
background-color: #7CC6EB;
|
||||
}
|
||||
|
||||
.blocked-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 999;
|
||||
background-color: #7CC6EB;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.blocked-tip {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.blocked-sub {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.side-tab {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 160rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 30rpx 0 0 30rpx;
|
||||
background: linear-gradient(135deg, #FD5401, #FF8A3D);
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2rpx;
|
||||
border-left: 6rpx solid rgba(255, 255, 255, 0.65);
|
||||
box-shadow: 0 6rpx 16rpx rgba(253, 84, 1, 0.35);
|
||||
}
|
||||
|
||||
.side-tab-hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<view>
|
||||
<image src="https://aliyunoss.hn12301.net/learnlaw/xfspyr_jjqd.jpg" style="width: 750rpx;" mode="widthFix"></image>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<view class="content">
|
||||
<view class="page-header">
|
||||
<text class="header-back" @tap="goBack">‹</text>
|
||||
<text class="header-title">用户隐私协议</text>
|
||||
</view>
|
||||
<scroll-view scroll-y class="doc-scroll">
|
||||
<view class="doc-card">
|
||||
<view class="doc-title">用户隐私协议</view>
|
||||
<text class="doc-meta">主办方:湖南一二三零一文化旅游服务有限公司</text>
|
||||
<text class="doc-meta">生效日期:自发布之日起</text>
|
||||
|
||||
<view class="sec">
|
||||
<text class="sec-title">一、适用范围</text>
|
||||
<text class="sec-text">本协议适用于"学法送演唱会门票"普法答题抽奖活动(以下简称"本活动")过程中我们对您个人信息的收集、使用与保护。您勾选同意本协议即表示您已阅读、理解并同意本协议全部内容。</text>
|
||||
</view>
|
||||
<view class="sec">
|
||||
<text class="sec-title">二、我们收集的信息</text>
|
||||
<text class="sec-text">1. 注册信息:姓名、手机号码,用于活动参与资格确认与实名核验,短信验证码(通过阿里云短信服务发送)用于验证手机归属;</text>
|
||||
<text class="sec-text">2. 微信授权信息:本活动通过微信网页授权(snsapi_base静默授权)获取您的openid,用于关联您的身份与本活动答题、中奖记录,授权范围不包含您的微信昵称、头像等信息;</text>
|
||||
<text class="sec-text">3. 答题与积分数据:答题记录、对错结果、用时及积分,用于成绩结算、排名与抽奖;</text>
|
||||
<text class="sec-text">4. 中奖及兑奖信息:中奖记录;演唱会门票实行实名制管理,兑奖时需填写身份证号码,仅用于核验兑奖资格及门票发放。</text>
|
||||
</view>
|
||||
<view class="sec">
|
||||
<text class="sec-title">三、信息的使用</text>
|
||||
<text class="sec-text">1. 提供答题、积分计算、排名及开奖抽奖服务;</text>
|
||||
<text class="sec-text">2. 验证用户身份及实名信息真实性,保障活动公平,防范刷量、冒领;</text>
|
||||
<text class="sec-text">3. 完成兑奖核验,并在中奖时与您联系;</text>
|
||||
<text class="sec-text">4. 满足法律法规及主管部门的监管要求。</text>
|
||||
</view>
|
||||
<view class="sec">
|
||||
<text class="sec-title">四、信息的存储与保护</text>
|
||||
<text class="sec-text">1. 您的个人信息存储于活动服务器,接口数据采用加密方式传输,并采取访问控制等安全措施;</text>
|
||||
<text class="sec-text">2. 我们不会出售您的个人信息,亦不会向与本活动无关的第三方提供;除法律法规明确要求或司法、行政机关依法要求外,任何共享均经审慎评估;</text>
|
||||
<text class="sec-text">3. 活动结束后,相关数据将依法律规定予以保留或删除。</text>
|
||||
</view>
|
||||
<view class="sec">
|
||||
<text class="sec-title">五、信息的公示</text>
|
||||
<text class="sec-text">为保障抽奖活动公开透明,中奖名单、积分排行等信息将依活动监管要求进行公示,公示内容将对姓名、手机号码等个人信息作脱敏处理。</text>
|
||||
</view>
|
||||
<view class="sec">
|
||||
<text class="sec-title">六、您的权利</text>
|
||||
<text class="sec-text">您可通过本协议所列联系方式向我们申请查询、更正、删除您的个人信息,或撤回您的同意。撤回同意不影响撤回前基于同意已进行的信息处理,但可能导致您无法继续参与本活动。</text>
|
||||
</view>
|
||||
<view class="sec">
|
||||
<text class="sec-title">七、未成年人保护</text>
|
||||
<text class="sec-text">本活动面向具备完全民事行为能力的成年人。未满十八周岁的未成年人请勿在本活动中提交个人信息,如需参与应取得监护人同意并在监护人陪同下进行。</text>
|
||||
</view>
|
||||
<view class="sec">
|
||||
<text class="sec-title">八、协议更新</text>
|
||||
<text class="sec-text">我们可能根据活动安排及法律法规变化适时更新本协议,更新后的协议将在活动页面公示后生效。若您继续参与活动,即表示您接受更新后的协议。</text>
|
||||
</view>
|
||||
<view class="sec">
|
||||
<text class="sec-title">九、联系方式</text>
|
||||
<text class="sec-text">如您对本协议或本活动个人信息保护事宜有任何疑问、意见或投诉,请通过以下方式联系我们:</text>
|
||||
<text class="sec-text">主办方:湖南一二三零一文化旅游服务有限公司</text>
|
||||
<text class="sec-text">联系电话:0731-88912301</text>
|
||||
</view>
|
||||
<view style="height: 40rpx;"></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="page-footer">
|
||||
<button class="agree-btn" @tap="agreeAndBack">已阅读并同意</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
goBack() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
agreeAndBack() {
|
||||
const pages = getCurrentPages()
|
||||
const prev = pages[pages.length - 2]
|
||||
if (prev && prev.$vm) prev.$vm.agreed = true
|
||||
uni.navigateBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 30rpx;
|
||||
background: linear-gradient(135deg, #FD5401, #FF7A3D);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.header-back {
|
||||
font-size: 48rpx;
|
||||
color: #fff;
|
||||
line-height: 1;
|
||||
margin-right: 16rpx;
|
||||
padding: 0 10rpx;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.doc-scroll {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.doc-card {
|
||||
background: #fff;
|
||||
margin: 24rpx;
|
||||
border-radius: 20rpx;
|
||||
padding: 36rpx 30rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.doc-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.doc-meta {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.sec {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
|
||||
.sec-title {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #FD5401;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.sec-text {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
color: #555;
|
||||
line-height: 1.7;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.page-footer {
|
||||
flex-shrink: 0;
|
||||
padding: 20rpx 30rpx 40rpx;
|
||||
background: #fff;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.agree-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
background: linear-gradient(135deg, #FD5401, #FF7A3D);
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: 44rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(253, 84, 1, 0.3);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,224 @@
|
||||
<template>
|
||||
<view class="content">
|
||||
<view class="page-body">
|
||||
<view class="loading-box" v-if="detailList.length === 0 && !loadError">
|
||||
<u-loading mode="circle" size="40"></u-loading>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
<view class="loading-box" v-if="loadError">
|
||||
<text class="error-text">{{ loadError }}</text>
|
||||
<button class="retry-btn" @tap="fetchDetail">重新加载</button>
|
||||
</view>
|
||||
<template v-if="detailList.length > 0">
|
||||
<view class="detail-item" v-for="(item, i) in detailList" :key="item.id">
|
||||
<view class="detail-index" :class="item.is_correct ? 'correct' : 'wrong'">
|
||||
<text class="detail-index-text">{{ i + 1 }}</text>
|
||||
</view>
|
||||
<view class="detail-body">
|
||||
<text class="detail-q">{{ item.tiku_question_content }}</text>
|
||||
<view class="detail-answer-row">
|
||||
<text class="detail-label">你的答案:</text>
|
||||
<text class="detail-value" :class="item.is_correct ? 'text-correct' : 'text-wrong'">{{ item.user_answer }}</text>
|
||||
</view>
|
||||
<view class="detail-answer-row" v-if="!item.is_correct">
|
||||
<text class="detail-label">正确答案:</text>
|
||||
<text class="detail-value text-correct-answer">{{ item.tiku_correct_answer }}</text>
|
||||
</view>
|
||||
<view class="tips" v-if="item.tips">
|
||||
<text class="tips-label">解析</text>
|
||||
<text class="tips-text">{{ item.tips }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
resultId: 0,
|
||||
detailList: [],
|
||||
loadError: ''
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
this.resultId = options.resultId
|
||||
this.fetchDetail()
|
||||
},
|
||||
methods: {
|
||||
fetchDetail() {
|
||||
this.loadError = ''
|
||||
this.$u.api.getUserHisResultDetail({
|
||||
resultId: this.resultId
|
||||
}).then(res => {
|
||||
if (res.code === 0 && Array.isArray(res.Data) && res.Data.length) {
|
||||
this.detailList = res.Data
|
||||
} else {
|
||||
this.loadError = res.msg || '数据异常'
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('获取答题详情失败', err)
|
||||
this.loadError = '网络请求失败'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #7CC6EB;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
.page-body {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
padding: 40rpx 30rpx;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
background: rgba(255,255,255,0.95);
|
||||
border-radius: 20rpx;
|
||||
padding: 28rpx;
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
.detail-index {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
margin-right: 16rpx;
|
||||
margin-top: 2rpx;
|
||||
}
|
||||
|
||||
.detail-index.correct {
|
||||
background: linear-gradient(135deg, #52c41a, #73d13d);
|
||||
box-shadow: 0 2rpx 8rpx rgba(82,196,26,0.3);
|
||||
}
|
||||
|
||||
.detail-index.wrong {
|
||||
background: linear-gradient(135deg, #f5222d, #ff4d4f);
|
||||
box-shadow: 0 2rpx 8rpx rgba(245,34,45,0.3);
|
||||
}
|
||||
|
||||
.detail-index-text {
|
||||
font-size: 20rpx;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.detail-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.detail-q {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
display: block;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.detail-answer-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.text-correct {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.text-wrong {
|
||||
color: #f5222d;
|
||||
}
|
||||
|
||||
.text-correct-answer {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.tips {
|
||||
background: rgba(255, 247, 237, 0.95);
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx;
|
||||
margin-top: 16rpx;
|
||||
border-left: 6rpx solid #FD5401;
|
||||
}
|
||||
|
||||
.tips-label {
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
color: #FD5401;
|
||||
display: block;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.tips-text {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.loading-box {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
margin-top: 30rpx;
|
||||
width: 240rpx;
|
||||
height: 72rpx;
|
||||
line-height: 72rpx;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #FD5401;
|
||||
border: none;
|
||||
border-radius: 36rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<view class="content">
|
||||
<view class="page-body">
|
||||
<view class="loading-box" v-if="records.length === 0 && !loadError">
|
||||
<u-loading mode="circle" size="40"></u-loading>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
<view class="loading-box" v-if="loadError">
|
||||
<text class="error-text">{{ loadError }}</text>
|
||||
<button class="retry-btn" @tap="fetchRecords">重新加载</button>
|
||||
</view>
|
||||
<template v-if="records.length > 0">
|
||||
<view class="record-item" v-for="(item, i) in records" :key="item.id" @click="goDetail(item)">
|
||||
<view class="record-index">
|
||||
<text class="record-index-text">{{ i + 1 }}</text>
|
||||
</view>
|
||||
<view class="record-body">
|
||||
<view class="record-row">
|
||||
<text class="record-label">答题时间:</text>
|
||||
<text class="record-value">{{ item.create_time }}</text>
|
||||
</view>
|
||||
<view class="record-row">
|
||||
<text class="record-label">答对题数:</text>
|
||||
<text class="record-value">{{ item.correct_question_num }} / {{ item.total_question_num }} 题</text>
|
||||
</view>
|
||||
<view class="record-row">
|
||||
<text class="record-label">答题用时:</text>
|
||||
<text class="record-value">{{ formatTime(item.use_time) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
records: [],
|
||||
loadError: ''
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.fetchRecords()
|
||||
},
|
||||
methods: {
|
||||
fetchRecords() {
|
||||
this.loadError = ''
|
||||
const userId = uni.getStorageSync('userId')
|
||||
if (!userId) {
|
||||
this.loadError = '暂未登录'
|
||||
return
|
||||
}
|
||||
this.$u.api.getUserHisResultList({
|
||||
wenjuanId: 1001,
|
||||
userId: userId
|
||||
}).then(res => {
|
||||
if (res.code === 0 && res.Data?.result_list) {
|
||||
this.records = res.Data.result_list
|
||||
} else {
|
||||
this.loadError = res.msg || '数据异常'
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('获取答题记录失败', err)
|
||||
this.loadError = '网络请求失败'
|
||||
})
|
||||
},
|
||||
formatTime(sec) {
|
||||
const m = Math.floor(sec / 60)
|
||||
const s = sec % 60
|
||||
return (m < 10 ? '0' : '') + m + ':' + (s < 10 ? '0' : '') + s
|
||||
},
|
||||
goDetail(item) {
|
||||
uni.navigateTo({
|
||||
url: '/pages/record-detail/record-detail?resultId=' + item.id
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #7CC6EB;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
.page-body {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
padding: 40rpx 30rpx;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.record-item {
|
||||
background: rgba(255,255,255,0.95);
|
||||
border-radius: 20rpx;
|
||||
padding: 28rpx;
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
.record-index {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #FD5401, #FF7A3D);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
margin-right: 16rpx;
|
||||
margin-top: 2rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(253,84,1,0.3);
|
||||
}
|
||||
|
||||
.record-index-text {
|
||||
font-size: 20rpx;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.record-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.record-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.record-row:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.record-label {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.record-value {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.loading-box {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.retry-btn {
|
||||
margin-top: 30rpx;
|
||||
width: 240rpx;
|
||||
height: 72rpx;
|
||||
line-height: 72rpx;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #FD5401;
|
||||
border: none;
|
||||
border-radius: 36rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,340 @@
|
||||
<template>
|
||||
<view class="register-page">
|
||||
<view class="header">
|
||||
<text class="header-title">注册信息</text>
|
||||
<text class="header-desc">填写资料即可参与答题抽奖</text>
|
||||
</view>
|
||||
<view class="form">
|
||||
<view class="field">
|
||||
<text class="label">姓名</text>
|
||||
<view class="input-wrap">
|
||||
<text class="input-icon">👤</text>
|
||||
<input class="input" v-model="userName" placeholder="请输入您的姓名" />
|
||||
</view>
|
||||
<text class="name-tip">本次演唱会门票实行实名制管理,参与抽奖活动须如实填报本人姓名与手机号码;中奖后须在24小时内完成兑奖操作,兑奖时需补充填报本人身份证号码。</text>
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">手机号</text>
|
||||
<view class="input-wrap">
|
||||
<text class="input-icon">📱</text>
|
||||
<input class="input" v-model="phone" placeholder="请输入手机号" type="number" maxlength="11" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="field">
|
||||
<text class="label">验证码</text>
|
||||
<view class="field-row">
|
||||
<view class="input-wrap code-input-wrap">
|
||||
<text class="input-icon">✉️</text>
|
||||
<input class="input" v-model="smsCode" placeholder="请输入验证码" type="number" maxlength="6" />
|
||||
</view>
|
||||
<button class="code-btn" @tap="sendCode" :disabled="sending" :class="{ disabled: sending }">
|
||||
{{ sending ? countdown + 's' : '获取验证码' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="agree-row" @tap="agreed = !agreed">
|
||||
<view class="agree-checkbox" :class="{ checked: agreed }">
|
||||
<text v-if="agreed" class="agree-check-mark">✓</text>
|
||||
</view>
|
||||
<view class="agree-text">
|
||||
<text>我已阅读并同意</text>
|
||||
<text class="agree-link" @tap.stop="goPrivacy">《用户隐私协议》</text>
|
||||
</view>
|
||||
</view>
|
||||
<button class="submit-btn" @tap="register" :disabled="submitting" :class="{ disabled: submitting }">
|
||||
{{ submitting ? '提交中...' : '立即注册' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
userName: '',
|
||||
phone: '',
|
||||
smsCode: '',
|
||||
sending: false,
|
||||
countdown: 60,
|
||||
timer: null,
|
||||
submitting: false,
|
||||
agreed: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goPrivacy() {
|
||||
uni.navigateTo({ url: '/pages/privacy/privacy' })
|
||||
},
|
||||
sendCode() {
|
||||
if (!/^1\d{10}$/.test(this.phone)) {
|
||||
uni.showToast({ title: '请输入正确手机号', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this.sending = true
|
||||
this.$u.api.sendSms(this.phone).then(res => {
|
||||
if (res.code === 0) {
|
||||
uni.showToast({ title: '验证码已发送', icon: 'none' })
|
||||
this.startCountdown()
|
||||
} else {
|
||||
uni.showToast({ title: res.msg || '发送失败', icon: 'none' })
|
||||
this.sending = false
|
||||
}
|
||||
}).catch(() => {
|
||||
uni.showToast({ title: '网络错误', icon: 'none' })
|
||||
this.sending = false
|
||||
})
|
||||
},
|
||||
startCountdown() {
|
||||
this.countdown = 60
|
||||
this.timer = setInterval(() => {
|
||||
this.countdown--
|
||||
if (this.countdown <= 0) {
|
||||
clearInterval(this.timer)
|
||||
this.timer = null
|
||||
this.sending = false
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
register() {
|
||||
if (!this.userName || !this.phone || !this.smsCode) {
|
||||
uni.showToast({ title: '请填写完整信息', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!this.agreed) {
|
||||
uni.showToast({ title: '请阅读并同意《用户隐私协议》', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this.submitting = true
|
||||
const openId = uni.getStorageSync('openId')
|
||||
this.$u.api.userRegist({
|
||||
wenjuan_id: 1001,
|
||||
open_id: openId,
|
||||
user_name: this.userName,
|
||||
user_phone: this.phone,
|
||||
sms_code: this.smsCode,
|
||||
channel: 1
|
||||
}).then(res => {
|
||||
if (res.code === 0) {
|
||||
uni.setStorageSync('isRegistered', true)
|
||||
const data = res.Data || {}
|
||||
const userId = data.user_id || data.uesr_id
|
||||
if (userId) uni.setStorageSync('userId', userId)
|
||||
if (data.user_name) uni.setStorageSync('userName', data.user_name)
|
||||
if (data.user_phone) uni.setStorageSync('userPhone', data.user_phone)
|
||||
this.syncUserInfo(openId)
|
||||
uni.showToast({ title: '注册成功' })
|
||||
setTimeout(() => uni.navigateBack(), 1500)
|
||||
} else {
|
||||
uni.showToast({ title: res.msg || '注册失败', icon: 'none' })
|
||||
}
|
||||
}).catch(() => {
|
||||
uni.showToast({ title: '网络错误', icon: 'none' })
|
||||
}).finally(() => {
|
||||
this.submitting = false
|
||||
})
|
||||
},
|
||||
syncUserInfo(openId) {
|
||||
this.$u.api.wxOpenidLogin({ wenjuan_id: 1001, open_id: openId }).then(loginRes => {
|
||||
if (loginRes.code === 0 && loginRes.Data) {
|
||||
const data = loginRes.Data
|
||||
if (data.user_id || data.uesr_id) uni.setStorageSync('userId', data.user_id || data.uesr_id)
|
||||
if (data.user_name) uni.setStorageSync('userName', data.user_name)
|
||||
if (data.user_phone) uni.setStorageSync('userPhone', data.user_phone)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('注册后同步用户信息失败', err)
|
||||
})
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer)
|
||||
this.timer = null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background: linear-gradient(135deg, #FFF8ED 0%, #FFE9C4 100%);
|
||||
}
|
||||
|
||||
.register-page {
|
||||
padding: 60rpx 40rpx;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 50rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
color: #5D533D;
|
||||
display: block;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.header-desc {
|
||||
font-size: 26rpx;
|
||||
color: #8B7355;
|
||||
}
|
||||
|
||||
.form {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 24rpx;
|
||||
padding: 50rpx 40rpx;
|
||||
box-shadow: 0 8rpx 40rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.field {
|
||||
margin-bottom: 36rpx;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #5D533D;
|
||||
margin-bottom: 16rpx;
|
||||
display: block;
|
||||
padding-left: 8rpx;
|
||||
}
|
||||
|
||||
.input-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 88rpx;
|
||||
border: 2rpx solid #F0E4D0;
|
||||
border-radius: 12rpx;
|
||||
background: #FEFCF8;
|
||||
padding: 0 24rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.input-wrap:focus-within {
|
||||
border-color: #FD5401;
|
||||
box-shadow: 0 0 0 4rpx rgba(253, 84, 1, 0.08);
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
font-size: 32rpx;
|
||||
margin-right: 16rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.input {
|
||||
height: 100%;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
border: none;
|
||||
background: transparent;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.name-tip {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: #E4393C;
|
||||
line-height: 1.5;
|
||||
margin-top: 12rpx;
|
||||
padding: 12rpx 16rpx;
|
||||
background: #FFEFE9;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.field-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.code-input-wrap {
|
||||
flex: 1;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.code-btn {
|
||||
flex-shrink: 0;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
padding: 0 28rpx;
|
||||
font-size: 26rpx;
|
||||
color: #FD5401;
|
||||
background: #FFF0E6;
|
||||
border: none;
|
||||
border-radius: 12rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.code-btn.disabled {
|
||||
color: #B8A898;
|
||||
background: #F0E8E0;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 96rpx;
|
||||
line-height: 96rpx;
|
||||
background: linear-gradient(135deg, #FD5401, #FF7A3D);
|
||||
color: #fff;
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: 16rpx;
|
||||
margin-top: 20rpx;
|
||||
box-shadow: 0 6rpx 20rpx rgba(253, 84, 1, 0.3);
|
||||
}
|
||||
|
||||
.submit-btn.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.agree-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: -16rpx 8rpx 28rpx;
|
||||
}
|
||||
|
||||
.agree-checkbox {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
border: 2rpx solid #CCC;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
margin-right: 12rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.agree-checkbox.checked {
|
||||
border-color: #FD5401;
|
||||
background: #FD5401;
|
||||
}
|
||||
|
||||
.agree-check-mark {
|
||||
font-size: 22rpx;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.agree-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 24rpx;
|
||||
color: #8B7355;
|
||||
}
|
||||
|
||||
.agree-link {
|
||||
color: #FD5401;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,237 @@
|
||||
<template>
|
||||
<view class="content">
|
||||
<image src="https://aliyunoss.hn12301.net/learnlaw/learn_dtbj.jpg" mode="widthFix" style="width:750rpx;position:absolute;top:0;left:0;z-index:0;"></image>
|
||||
<view class="page-body">
|
||||
<view class="correct-card">
|
||||
<text class="correct-label">答对题数</text>
|
||||
<text class="correct-num">{{ correctCount }} <text class="correct-total">/ {{ detailList.length }}</text></text>
|
||||
</view>
|
||||
|
||||
<view class="detail-section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">答题详情</text>
|
||||
<text class="section-subtitle">共 {{ detailList.length }} 题,{{ correctCount }} 对 {{ wrongCount }} 错</text>
|
||||
</view>
|
||||
<view class="detail-item" v-for="(item, i) in detailList" :key="i">
|
||||
<view class="detail-index" :class="item.is_correct || item.status === 1 ? 'correct' : 'wrong'">
|
||||
<text class="detail-index-text">{{ i + 1 }}</text>
|
||||
</view>
|
||||
<view class="detail-body">
|
||||
<text class="detail-q">{{ item.tiku_question_content || '题目' + (i+1) }}</text>
|
||||
<view class="detail-answer-row">
|
||||
<text class="detail-label">你的答案:</text>
|
||||
<text class="detail-value" :class="item.is_correct || item.status === 1 ? 'text-correct' : 'text-wrong'">{{ item.user_answer || '未作答' }}</text>
|
||||
</view>
|
||||
<view class="detail-answer-row" v-if="!(item.is_correct || item.status === 1)">
|
||||
<text class="detail-label">正确答案:</text>
|
||||
<text class="detail-value text-correct-answer">{{ item.tiku_correct_answer || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button class="back-btn" @tap="goBack">返回首页</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
correctCount: 0,
|
||||
wrongCount: 0,
|
||||
detailList: []
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
try {
|
||||
const data = uni.getStorageSync('resultData') || {}
|
||||
if (data.resultDetailList && data.resultDetailList.length) {
|
||||
this.detailList = data.resultDetailList
|
||||
this.correctCount = data.resultDetailList.filter(r => r.is_correct === true || r.is_correct === 1 || r.status === 1).length
|
||||
this.wrongCount = data.resultDetailList.length - this.correctCount
|
||||
}
|
||||
} catch(e) {
|
||||
console.error('解析结果数据失败', e)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goBack() {
|
||||
uni.reLaunch({ url: '/pages/index/index' })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #B3E9F3;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
.page-body {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
padding: 40rpx 30rpx;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.correct-card {
|
||||
background: linear-gradient(135deg, #FD5401, #FF7A3D);
|
||||
border-radius: 20rpx;
|
||||
padding: 28rpx 24rpx;
|
||||
text-align: center;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(253, 84, 1, 0.2);
|
||||
}
|
||||
|
||||
.correct-label {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255,255,255,0.85);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.correct-num {
|
||||
font-size: 72rpx;
|
||||
font-weight: 800;
|
||||
color: #FFF;
|
||||
display: block;
|
||||
line-height: 1.2;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.correct-total {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
|
||||
.detail-section {
|
||||
background: rgba(255,255,255,0.95);
|
||||
border-radius: 20rpx;
|
||||
padding: 28rpx;
|
||||
margin-bottom: 30rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-bottom: 20rpx;
|
||||
padding-bottom: 16rpx;
|
||||
border-bottom: 2rpx solid #f5f5f5;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.section-subtitle {
|
||||
font-size: 24rpx;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.detail-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.detail-index {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
margin-right: 16rpx;
|
||||
margin-top: 2rpx;
|
||||
}
|
||||
|
||||
.detail-index.correct {
|
||||
background: linear-gradient(135deg, #52c41a, #73d13d);
|
||||
box-shadow: 0 2rpx 8rpx rgba(82,196,26,0.3);
|
||||
}
|
||||
|
||||
.detail-index.wrong {
|
||||
background: linear-gradient(135deg, #f5222d, #ff4d4f);
|
||||
box-shadow: 0 2rpx 8rpx rgba(245,34,45,0.3);
|
||||
}
|
||||
|
||||
.detail-index-text {
|
||||
font-size: 20rpx;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.detail-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.detail-q {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
display: block;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.detail-answer-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.text-correct {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.text-wrong {
|
||||
color: #f5222d;
|
||||
}
|
||||
|
||||
.text-correct-answer {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
width: 690rpx;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
background: linear-gradient(135deg, #FD5401, #FF7A3D);
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: 44rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(253,84,1,0.3);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,220 @@
|
||||
<template>
|
||||
<view class="content">
|
||||
<view class="page-header">
|
||||
<text class="header-back" @tap="goBack">‹</text>
|
||||
<text class="header-title">活动规则</text>
|
||||
</view>
|
||||
<view class="preview-wrap">
|
||||
<view class="loading-box" v-if="loading">
|
||||
<u-loading mode="circle" size="40"></u-loading>
|
||||
<text class="loading-text">内容加载中...</text>
|
||||
</view>
|
||||
<scroll-view class="doc-scroll" scroll-y v-if="!loading && !error">
|
||||
<rich-text class="doc-content" :nodes="htmlContent"></rich-text>
|
||||
<view style="height:30rpx;"></view>
|
||||
</scroll-view>
|
||||
<view class="error-box" v-if="error">
|
||||
<text class="error-text">{{ error }}</text>
|
||||
<button class="preview-btn" @tap="loadDoc">重新加载</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
docUrl: 'https://goldnar.hntgov.net/img/llpreheat_rules.docx',
|
||||
loading: false,
|
||||
error: '',
|
||||
htmlContent: ''
|
||||
}
|
||||
},
|
||||
onReady() {
|
||||
this.loadDoc()
|
||||
},
|
||||
methods: {
|
||||
goBack() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
loadDoc() {
|
||||
this.loading = true
|
||||
this.error = ''
|
||||
this.htmlContent = ''
|
||||
this.ensureMammoth().then(() => {
|
||||
return this.fetchDocx(this.docUrl)
|
||||
}).then(arrayBuffer => {
|
||||
return window.mammoth.convertToHtml({
|
||||
arrayBuffer
|
||||
})
|
||||
}).then(result => {
|
||||
this.htmlContent = result.value || ''
|
||||
this.loading = false
|
||||
}).catch(err => {
|
||||
console.error('加载文档失败', err)
|
||||
this.loading = false
|
||||
this.error = '文档加载失败,请重试'
|
||||
})
|
||||
},
|
||||
ensureMammoth() {
|
||||
if (window.mammoth) return Promise.resolve()
|
||||
return new Promise((resolve, reject) => {
|
||||
const urls = [
|
||||
'/static/mammoth.browser.min.js',
|
||||
'https://unpkg.com/mammoth@1.6.0/mammoth.browser.min.js'
|
||||
]
|
||||
let i = 0
|
||||
const loadNext = () => {
|
||||
if (i >= urls.length) {
|
||||
reject(new Error('解析组件加载失败'))
|
||||
return
|
||||
}
|
||||
const s = document.createElement('script')
|
||||
s.src = urls[i++]
|
||||
s.onload = () => window.mammoth ? resolve() : loadNext()
|
||||
s.onerror = loadNext
|
||||
document.head.appendChild(s)
|
||||
}
|
||||
loadNext()
|
||||
})
|
||||
},
|
||||
fetchDocx(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (typeof fetch !== 'undefined') {
|
||||
fetch(url).then(res => {
|
||||
if (!res.ok) throw new Error('HTTP ' + res.status)
|
||||
return res.arrayBuffer()
|
||||
}).then(resolve).catch(() => this.downloadDocx(url).then(resolve).catch(reject))
|
||||
return
|
||||
}
|
||||
this.downloadDocx(url).then(resolve).catch(reject)
|
||||
})
|
||||
},
|
||||
downloadDocx(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.downloadFile({
|
||||
url: url,
|
||||
success: (res) => {
|
||||
if (res.statusCode !== 200) {
|
||||
reject(new Error('下载失败'))
|
||||
return
|
||||
}
|
||||
uni.getFileSystemManager().readFile({
|
||||
filePath: res.tempFilePath,
|
||||
success: (r) => resolve(r.data),
|
||||
fail: () => {
|
||||
if (res.tempFilePath && typeof fetch !== 'undefined') {
|
||||
fetch(res.tempFilePath).then(r => r.arrayBuffer())
|
||||
.then(resolve).catch(reject)
|
||||
} else {
|
||||
reject(new Error('读取文件失败'))
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
fail: reject
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #F8F8F8;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 30rpx;
|
||||
background: linear-gradient(135deg, #FD5401, #FF7A3D);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.header-back {
|
||||
font-size: 48rpx;
|
||||
color: #fff;
|
||||
line-height: 1;
|
||||
margin-right: 16rpx;
|
||||
padding: 0 10rpx;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.preview-wrap {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.loading-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.error-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.preview-btn {
|
||||
width: 300rpx;
|
||||
height: 76rpx;
|
||||
line-height: 76rpx;
|
||||
background: linear-gradient(135deg, #FD5401, #FF7A3D);
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: 38rpx;
|
||||
}
|
||||
|
||||
.doc-scroll {
|
||||
height: 100%;
|
||||
padding: 20rpx 30rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.doc-content {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
line-height: 1.8;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user