This commit is contained in:
xiangqingqing 2024-08-09 11:22:51 +08:00
commit 4b71ec6da4
70 changed files with 5499 additions and 0 deletions

26
.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
.DS_Store
node_modules
package-lock.json
uni_modules
unpackage
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

16
.hbuilderx/launch.json Normal file
View File

@ -0,0 +1,16 @@
{ // launch.json configurations app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
// launchtypelocalremote, localremote
"version": "0.0",
"configurations": [{
"default" :
{
"launchtype" : "local"
},
"mp-weixin" :
{
"launchtype" : "local"
},
"type" : "uniCloud"
}
]
}

20
App.vue Normal file
View File

@ -0,0 +1,20 @@
<script setup>
import {
onLoad,
onShow,
onHide,
onLaunch
} from "@dcloudio/uni-app";
onLaunch(() => {
});
</script>
<style lang="scss">
@import '@/static/style/public.scss';
page{
padding: 0;
margin: 0;
}
</style>

16
README.md Normal file
View File

@ -0,0 +1,16 @@
奉节教育直播录播项目
第一步 命令运行
npm i
第二步
安装 sass
npm install node-sass
npm install sass --save-dev
// UI uni-ui
https://ext.dcloud.net.cn/plugin?id=55
[uni-ui地址](https://uniapp.dcloud.io/component/uniui/uni-drawer.html)

56
api/exam-data.ts Normal file
View File

@ -0,0 +1,56 @@
import { Request } from '@/utils/request'
// 切换学生
export function getChangeStudent (param:any) {
return Request({
url:'/api/user/Account/switchstudent',
method:'GET',
data:param
})
}
// 获取绑定的学生列表
export function getBindStudentList () {
return Request({
url:'/api/user/Student/bindstudentlist',
method:'GET'
})
}
// 报告列表
export function getUserReportList (param:any) {
return Request({
url:'/api/user/Home/examrecord',
method:'GET',
data:param
})
}
// 报告详情页
export function getUserReportDetails (param:any) {
return Request({
url:'/api/user/Home/userexamrecord',
method:'GET',
data:param
})
}
// 测评报告
export function getTestReportData (param:any) {
return Request({
url:'/api/user/Home/report',
method:'GET',
data:param
})
}
// 获取评测客服信息
export function getTestInfo () {
return Request({
url:'/api/user/Student/getstudyreportinfo',
method:'GET'
})
}
// 查看答卷
export function getTestImgs (param:any) {
return Request({
url:'/api/user/Home/pagerImgs',
method:'GET',
data:param
})
}

120
api/user.ts Normal file
View File

@ -0,0 +1,120 @@
import { Request } from '@/utils/request'
import { number } from 'echarts/lib/export'
// 用户登录
export const getUserLogin =async (param:any) => {
return await Request({
url: `/api/user/Account/login?code=${param}`,
method: "POST"
})
}
//注册
// 用户登录信息
export const getUserInfo = () => {
return Request({
url: '/api/user/Account',
method: "GET"
})
}
// 切换学生
export const getChangeUserList = (param:any) => {
return Request({
url: `/api/user/Account/switchstudent?studentId=${param}`,
method: "GET"
})
}
//查询未绑定学生
export const getUnbindstudentinfo = (param:string) => {
return Request({
url: `/api/user/Student/unbindstudentinfo?telephone=${param}`,
method: "GET"
})
}
//获取绑定学生刘表
export const getbindstudentList = () => {
return Request({
url: `/api/user/Student/bindstudentlist`,
method: "GET"
})
}
//绑定学生 【511】跳电话尾号验证 【512】跳电话验证码验证
export const getSendbindstudent = (params) => {
return Request({
url: `/api/user/Student/bindstudent?StudentTelephone=${params.StudentTelephone}&VerifyName=${params.VerifyName}&VerifyLastTelephone=${params.VerifyLastTelephone}&VerifySMSId=${params.VerifySMSId}&VerifyCode=${params.VerifyCode}`,
method: "GET"
})
}
//发送电话验证码
export const getSendTelphoneCode = (telephone,type) => {
return Request({
url: `/api/user/Account/sms?mobile=${telephone}&smsType=${type}`,
method: "GET"
})
}
//动态令牌换取用户手机号
export const getUserPhone = (param:string) => {
return Request({
url: `/api/user/Account/getXcxMobileFlag?code=${param}`,
method: "POST"
})
}
//获取配置信息
export const getConfig = () => {
return Request({
url: `/api/user/Account/config`,
method: "GET"
})
}
//登录失败,注册绑定
export const postRegister = (data) => {
return Request({
url: `/api/user/Account/register`,
method: "POST",
data
})
}
//接触绑定账号
export const putUnBind = (data) => {
return Request({
url: `/api/user/Student/ubind`,
method: "PUT",
data
})
}
//获取测评客服信息
export const getSTudyReportInfo = () => {
return Request({
url: `/api/user/Student/getstudyreportinfo`,
method: "GET"
})
}
//获取查看学生信息
export const LookStudentInfo = (data)=>{
return Request({
url:`/api/user/Account/student?studentId=${data}`,
method:'GET'
})
}
//绑定家长手机号码
export const bindMobile = (data:string)=>{
return Request({
url:`/api/user/Account/bindMobile?code=${data}`,
method:'POST'
})
}

View File

@ -0,0 +1,214 @@
<!-- 当前学生组件 -->
<template>
<view class="ech-card" :class="{'purple':props.bgIndex % 3 === 0,'green':(props.bgIndex-1) % 3 === 0,'blue':(props.bgIndex-2) % 3 === 0}">
<view class="change-stu">
<view class="person-tag" :style="{color:(props.bgIndex % 3 === 0?'#6B86FF':(props.bgIndex-1) % 3 === 0?'#45CB9F':(props.bgIndex-2) % 3 === 0?'#22C1F2':'')}">
执器于身共建未来
</view>
<view class="change" v-if="props.isChange&&listLength!==1" @click="changeStudent">
<view class="change-icon"></view>
<view class="change-text">切换学生</view>
</view>
<view class="person-contain">
<view class="stu-img">
<image src="https://minio.23544.com:9010/data-center/gzh/Avatar.png" mode="学生头像"></image>
</view>
<view class="person-info">
<view class="school">
<view class="house-icon"></view>
<view class="school-name">{{props.datas.SchoolName}}</view>
</view>
<view class="person-ma">
<text>{{props.datas.RealName}}</text> | {{props.datas.Grade+props.datas.ClassName}}
</view>
</view>
<view class="person-btn" v-if="!props.isChange">
<view class="look" @click="openStuInfo()">查看</view>
<view class="un-bind" @click="unBind()">解绑</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
isChange: {
type: Boolean,
required: true
},
datas: {
type: Object,
required: true
},
bgIndex: {
type: Number,
required: true
},
listLength: {
type: Number,
// required: true
}
})
const emit = defineEmits(['changeStu','onOpenStu','onUnBind'])
//
const changeStudent=()=>{
emit('changeStu')
}
//
const openStuInfo=()=>{
emit('onOpenStu')
}
//
const unBind=()=>{
emit('onUnBind')
}
</script>
<style lang="scss" scoped>
.ech-card {
width: 343px;
height: 121px;
position: relative;
left: 50%;
transform: translate(-50%);
margin-bottom: 10px;
overflow: hidden;
}
.purple{
background: url('@/static/education/purple-bg.png') no-repeat !important;
background-size: 100% 100% !important;
.look{
color: #6B86FF !important;
}
}
.green{
background: url('@/static/education/green-bg.png') no-repeat !important;
background-size: 100% 100% !important;
.look{
color: #45CB9F !important;
}
}
.blue{
background: url('@/static/education/blue-bg.png') no-repeat !important;
background-size: 100% 100% !important;
.look{
color: #22C1F2 !important;
}
}
.person-tag{
position: absolute;
left: 38rpx;
top: 10rpx;
font-size: 20rpx;
letter-spacing: 22rpx;
}
.change-stu{
.change {
position: absolute;
right: 20px;
top: 5px;
display: flex;
flex-direction: column;
align-items: center;
color: #999;
font-size: 11px;
font-style: normal;
font-weight: 500;
line-height: normal;
.change-icon {
width: 16px;
height: 16px;
background: url('@/static/education/change.png');
background-size: 100% 100%;
}
}
.person-contain {
position: absolute;
left: 5px;
bottom: 5px;
display: flex;
align-items: center;
width: 95%;
.stu-img {
width: 90px;
height: 90px;
display: flex;
justify-content: center;
align-items: center;
image{
width: 68px;
height: 68px;
border-radius: 50%;
}
}
.person-info {
width: 52%;
.school {
display: flex;
align-items: center;
}
.house-icon {
width: 16px;
height: 16px;
background: url('@/static/education/house.png');
background-size: 100% 100%;
margin-right: 5px;
}
.school-name {
height: 20px;
color: #FFF;
font-size: 15px;
font-style: normal;
font-weight: 600;
line-height: 20px;
letter-spacing: 0.84px;
}
.person-ma {
margin-top: 8px;
height: 20px;
color: #FFF;
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: 20px;
letter-spacing: 0.72px;
text{
font-weight: 500;
}
}
}
.person-btn{
margin:10px 0 0 20px;
.look {
width: 55px;
height: 22px;
border-radius: 21px;
background: #FFF;
box-shadow: 0px 1px 5px 0px rgba(182, 187, 214, 0.66);
font-size: 12px;
font-style: normal;
line-height: 22px;
letter-spacing: 0.6px;
text-align: center;
}
.un-bind{
margin-top: 8px;
width: 55px;
height: 22px;
border-radius: 21px;
background: rgba(0,0,0,0);
border: 1px solid #fff;
box-shadow: 0px 1px 5px 0px rgba(182, 187, 214, 0.66);
font-size: 12px;
font-style: normal;
line-height: 22px;
letter-spacing: 0.6px;
text-align: center;
color: #fff;
}
}
}
}
</style>

View File

@ -0,0 +1,305 @@
<!-- 学生老师共用雷达图 -->
<template>
<view class="content">
<uniChart :option="person.option" />
<view class="cont_text">超过<text>{{props.rate}}%</text>该年龄段的学生</view>
</view>
</template>
<script setup>
import { reactive, shallowRef, onMounted } from 'vue'
import * as echarts from "@/components/uniapp-echarts/static/echarts.min.js";
import uniChart from "@/components/uniapp-echarts/components/uni-chart/uni-chart.vue";
const props = defineProps({
percentage:{
type: Number,
required: true
},
Title:{
type: String,
required: true
},
rate:{
type: Number,
required: true
}
})
let person=reactive({
option:{}
})
onMounted(()=>{
GetEchar()
})
const GetEchar = () => {
person.option = {
series: [
{
type: 'gauge',
min: 0,
max: 100,
splitNumber: 10,
radius: '99%',
endAngle: -270,
axisLine: {
lineStyle: {
width: '100%',
color: [[1, {
type: 'radial',
x: .5,
y: .5,
x2: 1,
y2: 1,
colorStops: [{
offset: .5, color: 'rgba(105, 132, 255, 1)' // 0%
}, {
offset: 1, color: 'rgba(107, 134, 255, 0.25)' // 100%
}],
globalCoord: false
}]]
}
},
splitLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
show: false
},
pointer: {
show: false
},
title: {
show: false
},
anchor: {
show: false
}
},
{
type: 'gauge',
radius: '60%',
endAngle: -270,
axisLine: {
lineStyle: {
width: '100%',
color: [[1, '#fff']]
}
},
splitLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
show: false
},
pointer: {
show: false
},
title: {
show: false
},
anchor: {
show: false
}
},
{
type: 'gauge',
min: 0,
max: 100,
splitNumber: 10,
radius: '85%',
endAngle: -270,
axisLine: {
lineStyle: {
width: 110,
color: [
[0.75, '#6984FF'],
[1, '#7A9CFF']
]
}
},
splitLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
show: false
},
pointer: {
show: false
},
title: {
show: false
},
anchor: {
show: false
}
},
{
type: 'gauge',
min: 0,
max: 100,
splitNumber: 10,
radius: '80%',
progress: {
show: true,
width: 30
},
itemStyle: {
color: '#57DCFF'
},
axisLine: {
distance: 50,
length: 18,
lineStyle: {
color: [[1, '#fff']],
width: 30
}
},
splitLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
distance: -100,
color: '#ffffff',
fontSize: 40
},
anchor: {
show: false
},
pointer: {
show: false
},
detail: {
offsetCenter: [0, '-22px'],
fontSize: 150,
color: '#6B86FF'
},
title: {
//
offsetCenter: [0, '30%'],
textStyle: {
fontSize: 40,
color: '#3E4E96'
}
},
data: [
{
value: props.percentage,
name: props.Title.split('测评')[0]+'\n测评指数'
}
]
},
{
type: 'gauge',
min: 0,
max: 100,
radius: '85%',
splitNumber: 10,
axisLine: {
lineStyle: {
color: [[1, '#57DCFF']],
width: 3
}
},
splitLine: {
distance: -12,
length: 12,
lineStyle: {
color: '#57DCFF'
}
},
axisTick: {
show: false
},
axisLabel: {
show: false
},
pointer: {
show: false
},
title: {
show: false
},
anchor: {
show: false
}
},
{
type: 'gauge',
min: 0,
max: 100,
radius: '60%',
splitNumber: 21,
startAngle: 200,
endAngle: -270,
axisLine: {
lineStyle: {
color: [[1, '#194280']],
width: 3
}
},
splitLine: {
distance: 0,
length: 10,
lineStyle: {
color: '#18EFE2'
}
},
axisTick: {
show: false
},
axisLabel: {
show: false
},
pointer: {
show: false
},
title: {
show: false
},
anchor: {
show: false
}
}
]
};
}
</script>
<style lang="scss" scoped>
.content {
width: 220px;
height: 220px;
position: relative;
left: 50%;
transform: translate(-50%);
border-radius: 50%;
margin-bottom: 50rpx;
.cont_text{
position: absolute;
bottom: -60rpx;
left: 0;
width: 100%;
text-align: center;
color: #414F8E;
font-size: 28rpx;
text{
color: #FFA755;
}
}
}
</style>

View File

@ -0,0 +1,183 @@
<template>
<view class="content">
<uniChart :option="person.option" />
</view>
</template>
<script setup>
import { reactive, shallowRef, onMounted, nextTick } from 'vue'
import * as echarts from "@/components/uniapp-echarts/static/echarts.min.js";
import uniChart from "@/components/uniapp-echarts/components/uni-chart/uni-chart.vue";
const props = defineProps({
id: {
type: String,
required: true
},
datas:{
type: Array,
required: true
},
studentName:{
type: String,
required: true
}
})
let person=reactive({
userScore:[],
userAvgScore:[],
xTime:[], // x
max: 0, //
option: {}
})
onMounted(()=>{
load()
GetEchar()
})
const load=()=>{
//
person.userScore = props.datas.map(f => { return f.Score })
person.userAvgScore = props.datas.map(f => { return f.AvgScore })
person.xTime = props.datas.map(f => { return f.Name })
person.max = Math.max.apply(null,props.datas.map(f => { return f.TotalScore }));
}
const GetEchar = () => {
person.option = {
tooltip: {
show: false,
trigger: 'axis',
formatter: function (params) {
return params[0].name+'\n'
+person.option.legend.data[0].name+''+person.userScore[params[0].dataIndex]+'分\n'
+person.option.legend.data[1].name+''+person.userAvgScore[params[0].dataIndex]+'分'
}
},
legend: {
data: [
{ name: '学生/'+props.studentName },
{ name: '年级平均' }
],
icon: 'circle',
y: 'bottom',
itemWidth: 20, //
itemHeight: 20, //
itemGap: 30, //
textStyle: {
color: '#333',
fontSize: 40
}
},
grid: {
top: '3%',
left: '3%',
right: '5%',
bottom: '10%',
containLabel: true
},
xAxis: [
{
type: 'category',
axisTick: {
show:false
},
axisLine: {
onZero: false,
lineStyle: {
color: '#2A2A2A',
width: 4
}
},
axisLabel: {
//
textStyle: {
color: '#6F6F70',
fontSize: 40
},
formatter: function (params) {
return params;
}
},
data: person.xTime
}
],
yAxis: [
{
type: 'value',
axisTick: {
show:false
},
axisLine: {
show:false
},
max: person.max,
min: 0,
// y
axisLabel: {
textStyle: {
color: '#6F6F70',
fontSize: 40
}
},
splitLine: {
show: true,
lineStyle: {
color: ['#5E5E5E'],
width: 2,
type: 'dashed'
}
}
}
],
series: [
{
name: '学生/'+props.studentName,
type: 'line',
symbol: 'circle', //
symbolSize: 8, //
// 线
itemStyle: {
normal: {
//
color: '#6B86FF',
borderColor: '#6B86FF', //
borderWidth: 4
},
emphasis: {
//
color: '#fff'
}
},
data: person.userScore
},
{
name: '年级平均',
type: 'line',
symbol: 'circle', //
symbolSize: 8, //
// 线
itemStyle: {
normal: {
//
color: '#FFA755',
borderColor: '#FFA755', //
borderWidth: 4
},
emphasis: {
//
color: '#fff'
}
},
data: person.userAvgScore
}
]
};
}
</script>
<style>
.content {
width: 98%;
height: 260px;
}
</style>

View File

@ -0,0 +1,69 @@
<!-- 弹窗组件 -->
<template>
<view class="add_popup" v-if="person.isShowPopup">
<view class="popup_cont" :style="'background:'+props.bgColor+'!important;'">
<view class="popup_close"></view>
<slot></slot>
</view>
</view>
</template>
<script setup>
import { reactive, shallowRef, nextTick, onMounted, watch } from 'vue'
const props = defineProps({
isShow: {
type: Boolean,
required: true
},
bgColor: {
type: String,
required: true
}
})
let person=reactive({
isShowPopup:false
})
watch(()=>props.isShow,(newVal)=>{
person.isShowPopup=newVal
})
onMounted(()=>{
setTimeout(()=>{
person.isShowPopup=false
},3000)
})
</script>
<style lang="scss" scoped>
.add_popup{
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
// justify-content: start;
justify-content: flex-end;
z-index: 9999;
.popup_cont{
background: rgba(0,0,0,.4);
text-align: center;
margin: 60rpx auto 100rpx auto;
border-radius: 20rpx;
color: #ffffff;
font-size: 30rpx;
padding: 40rpx;
display: flex;
flex-direction: column;
justify-content: center;
.popup_close{
margin-bottom: 10rpx;
}
}
}
</style>

View File

@ -0,0 +1,156 @@
<template>
<view class="title">
<view class="title-title" :style="{
'color' : props.color,
'padding-top' : top+'rpx',
'background-color' : props.bgc,
'height' : tops+'rpx',
'line-height' : tops+'rpx'
}">
<view v-if="props.isReturn===0" class="title-return" @click="returns()">
<image src="@/static/report/back_icon.png" mode=""></image>
</view>
<view v-else-if="props.isReturn===1" class="title-return title-icon" @click="returns()">
<image :src="props.returnIcon" mode=""></image>
</view>
<view v-else-if="props.isReturn===4" class="title-return" @click="goHome()">
<image src="@/static/report/back_icon.png" mode=""></image>
</view>
<view v-else class="title-return"></view>
<view class="title-name"
:style="{'text-align':props.isCenter?'center;':'left;',marginLeft:props.isReturn===2?'-130rpx':''}">
{{props.title_name}}</view>
</view>
<view :style="{height: top+tops+'rpx'}"></view>
</view>
</template>
<script setup>
import {
defineProps,
ref
} from 'vue'
import {
onLoad,
onShow
} from "@dcloudio/uni-app";
const props = defineProps({
title_name: {
type: String,
required: true
},
isReturn: { // 0 1 2 3
type: Number,
required: true
},
returnIcon: {
type: String,
// required: true
},
color: {
type: String,
required: true
},
bgc: {
type: String,
required: true
},
isCenter: {
type: Boolean,
required: true
}
})
const isLast = ref(false)
const top = ref(0)
const tops = ref(100)
const emit = defineEmits(['iconClick'])
onShow(() => {
getLast()
getTopWeiXin()
})
const goHome = () => {
uni.switchTab({
url: '/pages/index/index' //
})
}
//
const returns = () => {
if (props.isReturn === 0) {
if (isLast.value) {
uni.switchTab({
url: '/pages/index/index' //
})
} else {
uni.navigateBack({
delta: 1 //
});
}
} else if (props.isReturn === 1) {
//
emit('iconClick')
}
}
//
const getTopWeiXin = () => {
top.value = parseInt(uni.getSystemInfoSync().statusBarHeight * 750 / uni.getSystemInfoSync().screenWidth)
uni.setStorageSync('top', top.value);
}
//
const getLast = () => {
let pages = getCurrentPages(); //
if (pages.length == 1) {
isLast.value = true
} else {
isLast.value = false
}
}
</script>
<style lang="scss" scoped>
.title {
.title-title {
position: fixed;
z-index: 999;
width: 750rpx;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
.title-return {
width: 60rpx;
position: absolute;
left: 0;
z-index: 999;
margin-left: 20rpx;
margin-top: 8rpx;
image {
width: 36rpx;
height: 36rpx;
}
}
.title-icon {
margin-top: 20rpx;
image {
width: 46rpx !important;
height: 46rpx !important;
}
}
.title-name {
width: 560rpx;
font-weight: bold;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
word-break: break-all;
}
}
}
</style>

View File

@ -0,0 +1,93 @@
<!-- 弹窗组件 -->
<template>
<view class="add_popup" v-if="person.isShowPopup">
<view class="popup_cont" :style="{width:props.width&&props.width>0?props.width+'px;':'auto;',
height:props.height&&props.height>0?props.height+'px;':'auto;',
background:props.bgColor+'!important;'}">
<view class="popup_close" @click="cancel" v-if="props.showCancel"></view>
<slot></slot>
</view>
</view>
</template>
<script setup lang="ts">
import { reactive, shallowRef, nextTick, onMounted, watch } from 'vue'
const props = defineProps({
isShow: {
type: Boolean,
required: true
},
width: {
type: Number,
required: false
},
height: {
type: Number,
required: false
},
bgColor: {
type: String,
required: false
},
showCancel:{
type:Boolean,
default:true
}
})
interface Iemit{
(e:'cancelBtn'):void
}
const emit=defineEmits<Iemit>()
let person=reactive({
isShowPopup:false
})
watch(()=>props.isShow,(newVal)=>{
person.isShowPopup=newVal
})
onMounted(()=>{
})
const cancel=()=>{
person.isShowPopup=!person.isShowPopup
emit('cancelBtn')
}
</script>
<style lang="scss" scoped>
.add_popup{
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
background: rgba(0,0,0,.4);
z-index: 99;
.popup_cont{
text-align: center;
margin: -60rpx auto 0 auto;
border-radius: 20rpx;
color: #3D425B;
font-size: 30rpx;
padding: 40rpx;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
.popup_close{
position: absolute;
bottom: -100rpx;
left: 50%;
transform: translate(-50%);
width: 80rpx;
height: 80rpx;
background: url('@/static/education/cancel.png') no-repeat;
background-size: 100% 100%;
}
}
}
</style>

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 xbmlz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,106 @@
export class UniCanvas {
constructor(ctx, canvasNode) {
this.ctx = ctx
this.chart = {}
this.canvasNode = canvasNode
if (!canvasNode) {
this._initStyle(ctx)
}
this._initEvent()
}
getContext(contextType) {
if (contextType === '2d') {
return this.ctx
}
}
setChart(chart) {
this.chart = chart
}
attachEvent() {
// noop
}
detachEvent() {
// noop
}
addEventListener() {
// noop
}
removeEventListener() {
// noop
}
_initCanvas(zrender, ctx) {
zrender.util.getContext = () => {
return ctx
}
zrender.util.$override('measureText', (text, font) => {
ctx.font = font || '12px sans-serif'
return ctx.measureText(text)
})
}
_initStyle(ctx) {
ctx.createCircularGradient = (x, y, r) => {
return ctx.createCircularGradient(x, y, r)
}
}
_initEvent() {
this.event = {}
const eventNames = [{
wxName: 'touchStart',
ecName: 'mousedown',
},
{
wxName: 'touchMove',
ecName: 'mousemove',
},
{
wxName: 'touchEnd',
ecName: 'mouseup',
},
{
wxName: 'touchEnd',
ecName: 'click',
},
]
eventNames.forEach((name) => {
this.event[name.wxName] = (e) => {
const touch = e.touches[0]
this.chart.getZr().handler.dispatch(name.ecName, {
zrX: name.wxName === 'tap' ? touch.clientX : touch.x,
zrY: name.wxName === 'tap' ? touch.clientY : touch.y,
preventDefault: () => {},
stopImmediatePropagation: () => {},
stopPropagation: () => {},
})
}
})
}
get width() {
if (this.canvasNode) return this.canvasNode.width
return 0
}
set width(w) {
if (this.canvasNode) this.canvasNode.width = w
}
get height() {
if (this.canvasNode) return this.canvasNode.height
return 0
}
set height(h) {
if (this.canvasNode) this.canvasNode.height = h
}
}

View File

@ -0,0 +1,363 @@
<template>
<!-- #ifdef APP-VUE || H5 -->
<view v-if="systemInfo.deviceType === 'pc'" :id="canvasId" class="uni-canvas" @click="emits('click', $event)" />
<view
v-else
:id="canvasId"
class="uni-canvas"
@click="click"
@touchstart="touchStart"
@touchmove.stop="touchMove"
@touchend="touchEnd"
/>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN || MP-QQ -->
<scroll-view :scroll-top="scrollTop" scroll-y="true" class="scroll-Y"
@scrolltoupper="upper" @scrolltolower="lower" @scroll="scroll">
<canvas
v-if="useNewCanvas"
type="2d"
:id="canvasId"
class="uni-canvas"
:canvas-id="canvasId"
@click="click"
@touchstart="touchStart"
@touchmove.stop="touchMove"
@touchend="touchEnd"
force-use-old-canvas="true"
/>
<canvas
v-else
:id="canvasId"
class="uni-canvas"
:canvas-id="canvasId"
@click="click"
@touchstart="touchStart"
@touchmove.stop="touchMove"
@touchend="touchEnd"
force-use-old-canvas="true"
/>
</scroll-view>
<!-- #endif -->
</template>
<script>
// #ifdef VUE3
import '../../static/echarts.min.js'
// #endif
// #ifdef VUE2 || MP-WEIXIN
const echarts = require('../../static/echarts.min.js')
// #endif
import { UniCanvas } from './uni-canvas.js'
let chart = null
export default {
name: 'uni-chart',
emits: ['inited', 'click', 'touchStart', 'touchMove', 'touchEnd'],
props: {
// 使 canvas
forceUseOldCanvas: {
type: Boolean,
default: false
},
//
theme: {
type: [String, Object],
},
//
option: {
type: Object,
require: true,
default() {
return {}
}
},
//
map: {
type: Object,
default() {
return {}
}
}
},
data() {
return {
canvasId: `uni-canvas-${Date.now()}`,
systemInfo: uni.getSystemInfoSync(),
useNewCanvas: true
}
},
mounted() {
this.registerPreprocessor()
this.registerMap()
this.$nextTick(() => {
// #ifdef APP-VUE || H5
this.initH5()
// #endif
// #ifdef MP-WEIXIN || MP-QQ
this.initMiniProgram()
// #endif
})
},
beforeDestroy() {
this.dispose()
},
methods: {
// register echarts preprocessor
registerPreprocessor() {
echarts.registerPreprocessor(option => {
if (option && option.series) {
if (option.series.length > 0) {
option.series.forEach(series => {
series.progressive = 0
})
} else if (typeof option.series === 'object') {
option.series.progressive = 0
}
}
})
},
// register geo json
registerMap() {
if (JSON.stringify(this.$props.map) === '{}') return
echarts.registerMap(this.$props.map.name, this.$props.map.opt)
},
// register theme color
registerTheme(name, opt) {
console.log('registerTheme', name, opt)
echarts.registerTheme(name, opt)
},
// init H5 app-vue
initH5() {
const canvasNode = document.getElementById(this.canvasId)
chart = echarts.init(canvasNode, this.$props.theme)
this.$emit('inited', chart)
chart.setOption(this.$props.option)
},
// init mini program
initMiniProgram() {
const version = this.systemInfo.SDKVersion
console.log(`当前基础库版本为: ${version}`)
const oldVersion = '1.9.91'
const baseVersion = '2.9.0'
let canUseNewCanvas = this.compareVersion(version, baseVersion) >= 0
if (this.$props.forceUseOldCanvas) {
if (canUseNewCanvas) console.warn('开发者强制使用旧canvas,建议关闭')
canUseNewCanvas = false
}
this.useNewCanvas = canUseNewCanvas && !this.forceUseOldCanvas
if (this.useNewCanvas) {
// 2.9.0 使 <canvas type="2d"></canvas>
this.initNewCanvas()
} else {
const isValid = this.compareVersion(version, oldVersion) >= 0
if (!isValid) {
console.error(`基础库版本过低,需大于等于 ${oldVersion}`)
return
} else {
console.warn(`建议将基础库调整大于等于${baseVersion}版本。升级后绘图将有更好性能`)
this.initOldCanvas()
}
}
},
// initNewCanvas
initNewCanvas() {
const query = uni.createSelectorQuery().in(this)
query
.select(`#${this.canvasId}`)
.node(res => {
const canvasNode = res.node
const ctx = canvasNode?.getContext('2d')
canvasNode.width = canvasNode.width * this.systemInfo.pixelRatio
canvasNode.height = canvasNode.height * this.systemInfo.pixelRatio
ctx.scale(this.pixelRatio, this.pixelRatio)
const canvas = new UniCanvas(ctx, canvasNode)
this.initECharts(canvas, canvasNode.width, canvasNode.width, this.pixelRatio)
})
.exec()
},
// initOldCanvas
initOldCanvas() {
// 1.9.91 <= sdkVersion < 2.9.0
const ctx = uni.createCanvasContext(`#${this.canvasId}`, this)
const canvas = new UniCanvas(ctx)
const query = uni.createSelectorQuery().in(this)
query
.select(`#${this.canvasId}`)
.boundingClientRect(res => {
// canvasdpr
this.initECharts(res.width, res.height, 1)
})
.exec()
},
// init
initECharts(canvas, width, height, dpr) {
echarts.setPlatformAPI({ createCanvas: () => canvas })
const theme = this.$props.theme
let themeName = ''
console.log('typeof theme', typeof theme)
if (typeof theme === 'object') {
this.registerTheme(theme.name, theme.opt)
themeName = theme.name
}
if (typeof theme === 'string') {
themeName = theme
}
chart = echarts.init(canvas, themeName, {
width: width,
height: height,
devicePixelRatio: dpr
})
this.$emit('inited', chart)
this.setOption(this.$props.option)
},
canvasToTempFilePath(opt) {
if (this.useNewCanvas) {
const query = uni.createSelectorQuery().in(this)
query
.select(`#${this.canvasId}`)
.node(res => {
const canvasNode = res.node
opt.canvas = canvasNode
uni.canvasToTempFilePath(opt)
})
.exec()
} else {
if (!opt.canvasId) {
opt.canvasId = this.canvasId
}
// TODO
chart.ctx.draw(true, () => {
uni.canvasToTempFilePath(opt, this)
})
}
},
setOption(opt) {
chart.setOption(opt)
},
dispose() {
chart && chart.dispose()
},
// event
wrapTouch(event) {
for (let i = 0; i < event.touches.length; ++i) {
const touch = event.touches[i]
touch.offsetX = touch.x
touch.offsetY = touch.y
}
return event
},
click(e) {
this.$emit('click', e)
},
touchStart(e) {
if (chart && e.touches.length > 0) {
const touch = e.touches[0]
const handler = chart.getZr().handler
handler.dispatch('mousedown', {
zrX: touch.x,
zrY: touch.y,
preventDefault: () => {},
stopPropagation: () => {}
})
handler.dispatch('mousemove', {
zrX: touch.x,
zrY: touch.y,
preventDefault: () => {},
stopPropagation: () => {}
})
handler.processGesture(this.wrapTouch(e), 'start')
this.$emit('touchStart', e)
}
},
touchMove(e) {
if (chart && e.touches.length > 0) {
let touch = e.touches[0]
const {
target: { offsetLeft, offsetTop }
} = e
touch.x = touch.pageX - offsetLeft
touch.y = touch.pageY - offsetTop
const handler = chart.getZr().handler
if (handler) {
handler.dispatch('mousemove', {
zrX: touch.x,
zrY: touch.y
})
handler.processGesture(this.wrapTouch(e), 'change')
}
this.$emit('touchMove', e)
}
},
touchEnd(e) {
if (chart) {
const touch = e.changedTouches ? e.changedTouches[0] : {}
const {
target: { offsetLeft, offsetTop }
} = e
touch.x = touch.pageX - offsetLeft
touch.y = touch.pageY - offsetTop
var handler = chart.getZr().handler
if (handler) {
handler.dispatch('mouseup', {
zrX: touch.x,
zrY: touch.y
})
handler.dispatch('click', {
zrX: touch.x,
zrY: touch.y
})
handler.processGesture(this.wrapTouch(e), 'end')
}
this.$emit('touchEnd', e)
}
},
// utils
compareVersion(v1, v2) {
v1 = v1.split('.')
v2 = v2.split('.')
const len = Math.max(v1.length, v2.length)
while (v1.length < len) {
v1.push('0')
}
while (v2.length < len) {
v2.push('0')
}
for (let i = 0; i < len; i++) {
const num1 = parseInt(v1[i])
const num2 = parseInt(v2[i])
if (num1 > num2) {
return 1
} else if (num1 < num2) {
return -1
}
}
return 0
}
}
}
</script>
<style>
.scroll-Y{
height: 100%;
position: relative;
left: 50%;
transform: translate(-50%);
}
.uni-canvas {
width: 100%;
height: 100%;
display: block;
z-index: -1 !important;
}
</style>

View File

@ -0,0 +1,89 @@
{
"id": "echarts-for-uniapp",
"name": "uniapp-echarts",
"displayName": "echarts-for-uniapp",
"version": "1.0.2",
"description": "uni-app 版本 echarts, 支持 Vue2/3, 开发者可以通过熟悉的 ECharts 配置方式,快速开发图表,满足各种可视化需求.",
"keywords": [
"echarts-uniapp",
"echarts",
"echarts",
"图表",
"可视化"
],
"scripts": {
"release": "bumpp && npm publish"
},
"repository": "https://github.com/xbmlz/echarts-for-uniapp",
"engines": {
"HBuilderX": "^3.2.13"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": "997909544"
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/echarts-for-uniapp"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "u",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

File diff suppressed because one or more lines are too long

8
config/config.ts Normal file
View File

@ -0,0 +1,8 @@
export const config= {
// host:"https://apifox.com/apidoc/shared-b43049a6-fed3-4025-8af9-3673b3d1c216/api-102610320",
// host:"https://mp-mk.qwit.top"
// host:"http://192.168.2.33:5011"
host:"https://mp-mk-api.23544.com"
// host:"https://mp-mk-api.23544.com"
// host:"http://192.168.2.7:8011"
}

8
env.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}

20
index.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
// var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
// CSS.supports('top: constant(a)'))
// document.write(
// '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
// (coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.ts"></script>
</body>
</html>

14
main.ts Normal file
View File

@ -0,0 +1,14 @@
import App from "./App.vue";
import { createSSRApp } from "vue";
if (process.env.NODE_ENV === 'production') {
//正式环境去除
console.log = () => {}
}
export function createApp() {
const app = createSSRApp(App);
return {
app,
};
}

81
manifest.json Normal file
View File

@ -0,0 +1,81 @@
{
"name" : "Yuanduan",
"appid" : "wx3979b942304174d5",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "wxe12af2df5f60bd72",
"setting" : {
"urlCheck" : false,
"es6" : false,
"minified" : false
},
"usingComponents" : true,
"permission" : {},
"lazyCodeLoading":"requiredComponents"
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "3",
"h5" : {
"devServer" : {
"port" : 6500
}
}
}

68
package.json Normal file
View File

@ -0,0 +1,68 @@
{
"name": "uni-preset-vue",
"version": "0.0.0",
"scripts": {
"dev:app": "uni -p app",
"dev:custom": "uni -p",
"dev:h5": "uni",
"dev:h5:ssr": "uni --ssr",
"dev:mp-alipay": "uni -p mp-alipay",
"dev:mp-baidu": "uni -p mp-baidu",
"dev:mp-kuaishou": "uni -p mp-kuaishou",
"dev:mp-lark": "uni -p mp-lark",
"dev:mp-qq": "uni -p mp-qq",
"dev:mp-toutiao": "uni -p mp-toutiao",
"dev:mp-weixin": "uni -p mp-weixin",
"dev:quickapp-webview": "uni -p quickapp-webview",
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
"build:app": "uni build -p app",
"build:custom": "uni build -p",
"build:h5": "uni build",
"build:h5:ssr": "uni build --ssr",
"build:mp-alipay": "uni build -p mp-alipay",
"build:mp-baidu": "uni build -p mp-baidu",
"build:mp-kuaishou": "uni build -p mp-kuaishou",
"build:mp-lark": "uni build -p mp-lark",
"build:mp-qq": "uni build -p mp-qq",
"build:mp-toutiao": "uni build -p mp-toutiao",
"build:mp-weixin": "uni build -p mp-weixin",
"build:quickapp-webview": "uni build -p quickapp-webview",
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
"build:quickapp-webview-union": "uni build -p quickapp-webview-union"
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-alpha-3080720230627002",
"@dcloudio/uni-app-plus": "3.0.0-alpha-3080720230627002",
"@dcloudio/uni-components": "3.0.0-alpha-3080720230627002",
"@dcloudio/uni-h5": "3.0.0-alpha-3080720230627002",
"@dcloudio/uni-mp-alipay": "3.0.0-alpha-3080720230627002",
"@dcloudio/uni-mp-baidu": "3.0.0-alpha-3080720230627002",
"@dcloudio/uni-mp-jd": "3.0.0-alpha-3080720230627002",
"@dcloudio/uni-mp-kuaishou": "3.0.0-alpha-3080720230627002",
"@dcloudio/uni-mp-lark": "3.0.0-alpha-3080720230627002",
"@dcloudio/uni-mp-qq": "3.0.0-alpha-3080720230627002",
"@dcloudio/uni-mp-toutiao": "3.0.0-alpha-3080720230627002",
"@dcloudio/uni-mp-weixin": "3.0.0-alpha-3080720230627002",
"@dcloudio/uni-mp-xhs": "3.0.0-alpha-3080720230627002",
"@dcloudio/uni-quickapp-webview": "3.0.0-alpha-3080720230627002",
"@fingerprintjs/fingerprintjs": "^3.4.1",
"vue": "3.3.4",
"vuex": "^4.0.2",
"wxml2canvas": "^1.0.1"
},
"devDependencies": {
"@dcloudio/types": "3.3.3",
"@dcloudio/uni-automator": "3.0.0-alpha-3080720230627002",
"@dcloudio/uni-cli-shared": "3.0.0-alpha-3080720230627002",
"@dcloudio/uni-stacktracey": "3.0.0-alpha-3080720230627002",
"@dcloudio/vite-plugin-uni": "3.0.0-alpha-3080720230627002",
"js-md5": "^0.7.3",
"postcss-loader": "^7.0.2",
"postcss-pxtorpx-pro": "^2.0.0",
"sass": "^1.52.3",
"typescript": "^4.7.3",
"vconsole": "^3.15.0",
"vite": "4.0.3"
}
}

120
pages.json Normal file
View File

@ -0,0 +1,120 @@
{
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/examData/index",
"style": {
"navigationBarTitleText": "教育中心",
"navigationStyle": "custom",
"app-plus": {
"titleNView": false
}
}
},
{
"path": "pages/user/index",
"style": {
"navigationBarTitleText": "个人中心",
"navigationStyle": "custom",
"app-plus": {
"titleNView": false
}
}
},
{
"path": "pages/webview/webview",
"style": {
"navigationBarTitleText": "注册中心",
"navigationStyle": "custom",
"app-plus": {
"titleNView": false
}
}
},
{
"path": "pages/examReport/index",
"style": {
"navigationBarTitleText": "考试报告",
"navigationStyle": "custom",
"app-plus": {
"titleNView": false
}
}
},
{
"path": "pages/examTest/index",
"style": {
"navigationBarTitleText": "查看原卷",
"navigationStyle": "custom",
"app-plus": {
"titleNView": false
}
}
},
{
"path": "pages/userRelated/index",
"style": {
"navigationBarTitleText": "关于",
"navigationStyle": "custom",
"app-plus": {
"titleNView": false
}
}
},
{
"path": "pages/userAddStudent/index",
"style": {
"navigationBarTitleText": "添加学生",
"navigationStyle": "custom",
"app-plus": {
"titleNView": false
}
}
}
,{
"path" : "pages/login/login",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "远端小程序",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"tabBar": {
"color": "#A1A9B2",
"selectedColor": "#2080F7",
"borderStyle": "black",
"height": "50px",
// "spacing": "6px",
"iconWidth": "24px",
"backgroundColor": "white",
"list": [{
"pagePath": "pages/examData/index",
"iconPath": "static/tabBar/material.png",
"selectedIconPath": "static/tabBar/material-active.png",
"text": "教育中心"
},
{
"pagePath": "pages/user/index",
"iconPath": "static/tabBar/monitor.png",
"selectedIconPath": "static/tabBar/monitor-active.png",
"text": "个人中心"
}
]
},
"condition": { //
"current": 0, //(list )
"list": [{
"name": "", //
"path": "", //
"query": "" //onLoad
}]
}
}

431
pages/examData/index.vue Normal file
View File

@ -0,0 +1,431 @@
<template>
<view class="page-bg">
<Navigation :title_name="'教育中心'" :isReturn="3" :color="'#000000'" :bgc="'#FFFFFF'" :isCenter="true" />
<view class="ech" v-if="person.studentList[person.studentActive]">
<CurrItem v-if="person.studentList[person.studentActive]" :datas="person.studentList[person.studentActive]"
@changeStu="getChangeStu" :isChange="true" :listLength="person.studentList.length"
:bgIndex="person.studentActive" />
<view class="ech-list">
<view class="time-logo">
<view class="time-reserve" @tap="reverseSort">排序:按时间 <view class="time-icon"
:class="showTimeIcon?'dowm-time-icon':'up-time-icon'"></view>
</view>
</view>
<scroll-view @scrolltolower="lowerBottom" @refresherrefresh="getFresh" scroll-y="true"
class="scrollHeight">
<template v-if="person.data.list&&person.data.list.length>0">
<view class="ech-item" v-for="(rep,repIndex) in person.data.list" :key="repIndex">
<view class="ech-item-type"
:class="[rep.ExamType==='周考'?'ech-type-blue':rep.ExamType==='月考'?'ech-type-green':rep.ExamType==='期中'?'ech-type-purple':'ech-type-purple']">
{{rep.ExamType}}
</view>
<view class="left">
<view class="item-tit">{{rep.Name}}</view>
<view class="time">时间{{rep.Time}}</view>
</view>
<view class="right" @click="lookReport(rep,repIndex)">查看<br />报告</view>
</view>
<view class="more_text" v-if="person.showMoreData">没有数据了...</view>
</template>
<template v-else>
<view class="empty_box">
<image src="@/static/null_icon.png" mode=""></image>
<text>暂无数据~</text>
</view>
</template>
</scroll-view>
</view>
</view>
<view v-else class="empty_box">
<image src="@/static/null_icon.png" mode=""></image>
<text>暂无数据~</text>
</view>
<Popups :isShow="showChangeStudent" :bgColor="'#fff'">
<view class="dio-card">
<view class="stu-item" :class="[person.studentActive===stuIndex?'active-bg':'']"
v-for="(stu,stuIndex) in person.studentList" :key="stuIndex"
@click.stop="studentChange(stu,stuIndex)">
<image src="https://minio.23544.com:9010/data-center/gzh/Avatar.png" mode=""></image>
<view class="stu-name">{{stu.RealName}}</view>
</view>
</view>
</Popups>
</view>
</template>
<script setup>
import {
ref,
reactive
} from "vue";
import {
onLoad,
onShow
} from "@dcloudio/uni-app";
import Popups from "@/components/popups/index.vue";
import CurrItem from "@/components/currItem/index.vue";
import Navigation from '@/components/navigation/index.vue'
import {
getChangeStudent,
getBindStudentList,
getUserReportList
} from "@/api/exam-data";
import {
getUserInfo,
postRegister
} from "@/api/user";
let person = reactive({
showMoreData: false,
//
studentActive: 0,
studentList: [],
//
data: {
list: [],
pageIndex: 1,
pageSize: 10,
total: 0
}
})
//
onShow(() => {
person.data.pageIndex=1
person.data.list=[]
uni.pageScrollTo({
scrollTop: 0
});
GetBindStudentList()
if (person.data.pageIndex * person.data.pageSize >= person.data.total) return person.showMoreData = true
})
//
const showTimeIcon = ref(false)
const reverseSort = () => {
showTimeIcon.value = !showTimeIcon.value
if (person.data.list && person.data.list.length > 0) {
if (showTimeIcon.value) {
//
person.data.list = person.data.list.sort(dateData("Time", true))
} else {
//
person.data.list = person.data.list.sort(dateData("Time", false))
}
}
}
//
const dateData = (property, bol) => {
return function(a, b) {
let value1 = a[property];
let value2 = b[property];
if (bol) {
//
return Date.parse(value1) - Date.parse(value2);
} else {
//
return Date.parse(value2) - Date.parse(value1)
}
}
}
//
const showChangeStudent = ref(false)
const getChangeStu = () => {
showChangeStudent.value = !showChangeStudent.value
}
//
const studentChange = (item, index) => {
person.studentActive = index
showChangeStudent.value = false
if (showChangeStudent.value === false) {
person.data.pageIndex = 1
person.data.list = []
person.data.total = 0
GetChangeStudent(item.Id)
}
}
//
const lookReport = (item, index) => {
uni.navigateTo({
url: '/pages/examReport/index?examId=' + item.ExamId
})
}
//
const lowerBottom = () => {
//
if (person.data.pageIndex * person.data.pageSize >= person.data.total) return person.showMoreData = true
// +1
person.data.pageIndex += 1
//
//
GetUserReportList()
}
//
const getFresh = () => {}
//
const GetChangeStudent = (Id) => {
getChangeStudent({
studentId: Id
}).then(res => {
let {
Code,
Data
} = res
if (Code === 200 && Data) {
GetUserReportList()
}
})
}
//
const GetBindStudentList = () => {
getBindStudentList().then(res => {
let {
Code,
Data,
Message
} = res
if (Code === 200 && Data) {
person.studentList = Data
if (person.studentList && person.studentList.length > 0) {
GetChangeStudent(person.studentList[person.studentActive].Id)
}
} else {
uni.showToast({
title: Message,
icon: 'none'
})
}
})
}
//
const GetUserReportList = () => {
let obj = {
PageIndex: person.data.pageIndex,
PageSize: person.data.pageSize
}
getUserReportList(obj).then(res => {
let {
Code,
Data,
Message
} = res
if (Code === 200 && Data) {
setTimeout(() => {}, 200)
person.data.list = [...person.data.list, ...Data.Data]
person.data.total = Data.Total
} else {
uni.showToast({
title: Message,
icon: 'none'
})
}
})
}
</script>
<style lang="scss" scoped>
.page-bg {
height: calc(100vh - 4rpx);
overflow: hidden;
}
//
.scrollHeight {
height: calc(100vh - 550rpx);
.more_text {
margin: 20rpx 0;
color: #999;
font-size: 24rpx;
text-align: center;
}
}
.ech {
width: 100vw;
padding: 10px 20px 0 20px;
box-sizing: border-box;
position: relative;
}
.ech-card {
position: absolute;
z-index: 10;
}
.ech-list {
border-radius: 0 0 10px 10px;
background: #FFF;
box-shadow: 0px 3px 7px 0px rgba(0, 0, 0, 0.15);
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: center;
padding-bottom: 20px;
margin-top: -15px;
.ech-item {
margin: 0 auto 10px auto;
width: 303px;
border-radius: 6px;
background: linear-gradient(180deg, rgba(228, 233, 255, 0.74) 0%, rgba(255, 255, 255, 0.49) 29.69%, rgba(128, 151, 255, 0.67) 100%);
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
padding: 20px 10px 20px 10px;
position: relative;
.ech-item-type {
position: absolute;
top: 0;
left: 0;
padding: 2px 20px;
border-radius: 6px 0 10px 0;
font-size: 12px;
font-weight: 600;
}
.ech-type-blue {
color: #4F6FFF;
background: #E3E8FF;
}
.ech-type-green {
color: #00B6DE;
background: #C0F4FF;
}
.ech-type-purple {
color: #9361FF;
background: #E8DEFE;
}
.left {
width: 78%;
.item-tit {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
color: #5B5B5B;
font-size: 15px;
font-weight: 600;
letter-spacing: 0.78px;
}
.time {
margin-top: 7px;
height: 20px;
color: #5E5E5E !important;
font-size: 12px;
letter-spacing: 0.6px;
}
}
}
}
.time-logo {
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
margin: 10px 0;
.time-reserve {
margin-right: 20px;
height: 15px;
line-height: 15px;
color: #777;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: normal;
letter-spacing: 0.6px;
display: flex;
align-items: center;
.time-icon {
margin-left: 5px;
width: 14px;
height: 14px;
background-size: 100% 100%;
}
}
}
.dowm-time-icon {
background: url('@/static/education/reverse.png');
}
.up-time-icon {
background: url('@/static/education/reverse_up.png');
}
.right {
width: 50px;
height: 54px;
background: #fff;
border-radius: 5px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #6B86FF;
font-family: PingFang SC;
font-size: 12px;
font-style: normal;
font-weight: 500;
line-height: normal;
letter-spacing: 0.72px;
}
.dio-card {
width: 202px;
height: 100%;
overflow: auto;
max-height: 250px;
&::-webkit-scrollbar {
display: none;
}
.stu-item {
width: 100%;
height: 91px;
border-radius: 8px;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: center;
background: #EFEFEF;
color: #0D0A0A;
image {
width: 68px;
height: 68px;
border-radius: 50%;
}
.stu-name {
margin-left: 20px;
font-size: 16px;
letter-spacing: 0.9px;
width: 30%;
text-align: left;
}
}
.active-bg {
color: #FFF;
background: #6B86FF;
}
}
</style>

700
pages/examReport/index.vue Normal file
View File

@ -0,0 +1,700 @@
<template>
<Navigation :title_name="person.reportInfo.ExamName"
:isReturn="person.studentId?4:0"
:color="'#000000'"
:bgc="'#FFFFFF'"
:isCenter="false"/>
<view class="page-bg">
<!-- 用户信息 -->
<view class="report_user" v-if="person.reportInfo.BaseInfoTitle">
<view class="report_user_info" v-if="person.reportInfo.BaseInfoTitle.ReachSubjctCount==0&&person.reportInfo.BaseInfoTitle.TargetLevelName=='民办本科'">
<text>{{person.reportInfo.BaseInfoTitle.UserName}}</text>同学你本次考试超过专科线还未达到民办本科线还需继续努力哟
</view>
<view class="report_user_info" v-else>
<text>{{person.reportInfo.BaseInfoTitle.UserName}}</text>同学
你本次考试有<text>{{person.reportInfo.BaseInfoTitle.ReachSubjctCount}}</text>学科超过<text>
{{person.reportInfo.BaseInfoTitle.TargetLevelName}}</text>线
<template v-if="person.reportInfo.BaseInfoTitle.LowerSubjctCount>0">还有<text>{{person.reportInfo.BaseInfoTitle.LowerSubjctCount}}</text>未达到{{person.reportInfo.BaseInfoTitle.TargetLevelName}}线</template>还需继续努力哦
</view>
<view class="report_user_tag flex">
<view class="report_tag_item flex" v-for="(educa,edIndex) in person.educaList" :key="edIndex">
<view v-if="person.reportInfo.BaseInfoTitle.TargetLevelName===educa.name" class="tag_item_active">
<image src="../../static/report/book_icon.png" mode=""></image>
</view>
<view class="tag_item_active_txt" v-if="person.reportInfo.BaseInfoTitle.TargetLevelName===educa.name||person.educaActive-1===edIndex">
{{educa.name}}
</view>
<view v-else>{{educa.name}}</view>
</view>
</view>
<view class="report_user_txt">(: 本结论只针对于本次考试分数分析<text>仅供参考</text></view>
</view>
<!-- echarts -->
<view class="report_echarts">
<view class="report_rate_title bold">
{{person.reportInfo.ExamType}} {{dateName}}
</view>
<view class="report_echart_cont" style="margin-top: 20rpx;">
<view class="cont_table" v-if="person.reportInfo.IsJuniorSchool">
<view class="table_title">
<view class="table_title_txt" style="'width:25%;'">
<view class="txt_item">类别</view>
<view class="txt_item">进退步/</view>
<view class="txt_item">等级</view>
<view class="txt_item">原卷</view>
</view>
</view>
<view class="table_name">
<view class="table_name_txt" v-for="(tab, taIndex) in person.subjectScore" :key="taIndex">
<view class="txt_item">{{ tab.SubjectName }}</view>
<view class="txt_item flex">
<view class="flex">
{{ tab.ClassRankingChange }}
<image v-if="tab.ClassRankingChange<0" src="@/static/report/down_icon.png" mode=""></image>
<image v-else src="@/static/report/top_icon.png" mode=""></image>
</view>
<view class="flex">
{{' / '+tab.GradeRankingChange }}
<image v-if="tab.GradeRankingChange<0" src="@/static/report/down_icon.png" mode=""></image>
<image v-else src="@/static/report/top_icon.png" mode=""></image>
</view>
</view>
<view class="txt_item">{{ tab.Score }}</view>
<view class="txt_item">
<view class="txt_item_press" v-if="taIndex===0">
<slider disabled :value="((person.reportInfo.Score/person.reportInfo.TotalSocre)*100).toFixed(2)" activeColor="rgba(0,0,0,0)" block-color="rgba(0,0,0,0)" block-size="1"/>
</view>
<view class="txt_item_btn" v-else-if="tab.ExamJoin" @click.stop="seeTest(tab,person.reportInfo)">查看答卷</view>
<view class="txt_item_red" v-else>缺考</view>
</view>
</view>
</view>
</view>
<view class="cont_table" v-else>
<view class="table_title">
<view class="table_title_txt" style="'width:25%;'">
<view class="txt_item">类别</view>
<view class="txt_item">分数<br/>个人/年级</view>
<view class="txt_item">进退步<br/>/</view>
<view class="txt_item">排名<br/>/</view>
<view class="txt_item">原卷</view>
</view>
</view>
<view class="table_name">
<view class="table_name_txt" v-for="(tab, taIndex) in person.subjectScore" :key="taIndex">
<view class="txt_item">{{ tab.SubjectName }}</view>
<view class="txt_item">{{ tab.Score+'/'+tab.GradeScore }}</view>
<view class="txt_item flex">
<view class="flex">
{{ tab.ClassRankingChange }}
<image v-if="tab.ClassRankingChange<0" src="@/static/report/down_icon.png" mode=""></image>
<image v-else src="@/static/report/top_icon.png" mode=""></image>
</view>
<view class="flex">
{{' / '+tab.GradeRankingChange }}
<image v-if="tab.GradeRankingChange<0" src="@/static/report/down_icon.png" mode=""></image>
<image v-else src="@/static/report/top_icon.png" mode=""></image>
</view>
</view>
<view class="txt_item">{{ tab.ClassRanking+'/'+tab.GradeRanking }}</view>
<view class="txt_item">
<view class="txt_item_press" v-if="taIndex===0">
<slider disabled :value="((person.reportInfo.Score/person.reportInfo.TotalSocre)*100).toFixed(2)" activeColor="rgba(0,0,0,0)" block-color="rgba(0,0,0,0)" block-size="1"/>
</view>
<view class="txt_item_btn" v-else-if="tab.ExamJoin" @click.stop="seeTest(tab,person.reportInfo)">查看答卷</view>
<view class="txt_item_red" v-else>缺考</view>
</view>
</view>
</view>
</view>
</view>
<view class="report_echart_cont" v-if="person.studentScore && person.studentScore.length > 0&&person.reportInfo.BaseInfoTitle.UserName">
<PublicLine v-if="person.studentScore && person.studentScore.length > 0&&person.reportInfo.BaseInfoTitle.UserName" :id="'studentGrade'"
:datas="person.studentScore" :studentName="person.reportInfo.BaseInfoTitle.UserName" />
</view>
</view>
<view v-if="person.testInfo">
<view class="report_echarts">
<view class="report_echart_cont">
<view class="report_rate_title bold">
{{person.testInfo.IntroTitle}}
</view>
<view class="report_ccc">{{person.testInfo.IntroDesc}}</view>
<view class="report_blue">{{person.testInfo.IntroFooter}}</view>
</view>
<view class="report_echart_cont">
<view class="report_rate_title bold">
学习力测评
</view>
<view class="report_rate_score">{{person.testInfo.ReportName}}测评报告</view>
<view class="report_rate_info">
姓名<text class="report_blue" v-if="person.reportInfo.BaseInfoTitle">{{person.reportInfo.BaseInfoTitle.UserName}}</text>
学校<text class="report_blue">{{person.testInfo.schoolName}}</text><br/>
测评时间<text class="report_blue">{{person.testInfo.ReportTime}}</text>
</view>
<PublicGauge v-if="person.testInfo.Score" :Title="person.testInfo.ReportName" :percentage="person.testInfo.Score" :rate="person.testInfo.exceedRate" />
</view>
</view>
<!-- 领取报告 -->
<view class="report_receive">
<view class="report_receive_des">
<view class="report_blue" v-if="person.reportInfo.BaseInfoTitle">
{{person.reportInfo.BaseInfoTitle.UserName}}同学本月参加了学习能力测评{{person.testInfo.ReportName}}测评
</view>
<view>
{{person.testInfo.ReportName}}测评得分<text>{{person.testInfo.Score}}</text>超过<text>{{person.testInfo.exceedRate}}%</text>的同龄孩子
</view>
<view>
考试成绩
<template v-if="person.reportInfo.IsJuniorSchool">
等级{{person.subjectScore[0].Score}}
</template>
<template v-else>
总分<text>{{person.reportInfo.TotalSocre}}</text>排名<text>{{person.reportInfo.TotalRanking}}</text>
</template>
</view>
<view class="mar-top" v-if="person.testInfo.levelDescription">
<view class="receive_des_title">根据学习能力测评和考试成绩综合分析</view>
<view class="receive_des">
{{person.testInfo.levelDescription}}
</view>
</view>
<view v-if="person.testInfo.methodDescription">
<view class="receive_des_title">建议您对孩子采用以下训练方法</view>
<view class="receive_des">
{{person.testInfo.methodDescription}}
</view>
</view>
</view>
<view class="report_receive_btn flex">
<text @click="receiveReport">领取完整报告及训练工具</text>
</view>
<Popups :isShow="person.IsReceiveReport" :width="280" :height="360" :bgColor="'#6B86FF'">
<view class="p_cont">
<view class="popup_cont_title">
领取报告
</view>
<view class="popup_cont_pic" >
<image :show-menu-by-longpress="true" :src="person.cusSerInfo.EQImgUrl" mode=""></image>
</view>
<view class="popup_cont_des">
长按屏幕识别上方二维码
</view>
<view class="popup_list">
<view class="popup_item flex">
<text>客服电话</text>
<text>{{person.cusSerInfo.ServiceNumber}}</text>
</view>
<view class="popup_item flex">
<text>教师电话</text>
<text>{{person.cusSerInfo.TeacherNumber}}</text>
</view>
</view>
</view>
</Popups>
</view>
<!-- 测评进度 -->
<view class="report_rate">
<view class="report_rate_title bold">
测评进度<text>:蓝色表示已完成</text>
</view>
<view class="report_rate_list">
<view class="report_rate_item" :class="[rate.Score>0?'report_rate_blue':'']" v-for="(rate,raIndex) in person.testInfo.reports" :key="raIndex">
{{rate.ReportName}}
</view>
</view>
</view>
</view>
<view v-else class="lock_box">
<image src="@/static/report/lock_icon.png" mode=""></image>
您的孩子未完成测评<br/>
完成后解锁报告
</view>
</view>
</template>
<script setup>
import { reactive, ref, watch } from 'vue'
import { onLoad, onShow } from "@dcloudio/uni-app";
import PublicLine from "@/components/echarts/publicLine.vue"
import PublicGauge from "@/components/echarts/publicGauge.vue"
import Popups from "@/components/popups/index.vue";
import Navigation from '@/components/navigation/index.vue'
import { getChangeStudent, getUserReportDetails, getTestReportData, getTestInfo } from "@/api/exam-data";
import {postRegister,getUserInfo} from "@/api/user";
let person=reactive({
//
educaActive:0,
educaList:[
{name:'专科'},
{name:'民办本科'},
{name:'公办本科'},
{name:'211'},
{name:'985'},
{name:'C9'}
],
subjectScore:[], //
studentScore:[], //
IsReceiveReport:false, //
reportInfo:{}, //
testInfo:null, //
cusSerInfo:{}, //
examId:0, // Id
studentId:0 // Id
})
onLoad((e)=>{
// e
person.examId=e.examId
person.studentId=e.studentId
})
//
onShow(async ()=>{
if(person.studentId){
GetChangeStudent(person.studentId)
}else{
await GetUserReportDetails()
await GetTestReportData()
}
if(person.reportInfo.ExamName){
uni.setNavigationBarTitle({
title: person.reportInfo.ExamName
})
}
})
//
const seeTest=(item,info)=>{
if(item.ExamJoin){
if(item.ExamSubjectId>0){
let infos={
examName:info.ExamName,
userName:info.BaseInfoTitle.UserName,
totalScore:info.IsJuniorSchool?'等级'+item.Score:item.Score+'分',
subject:item.SubjectName,
baseScore:item.BaseScore,
isJuniorSchool:info.IsJuniorSchool
}
uni.navigateTo({
url:'/pages/examTest/index?Id='+item.ExamSubjectId+'&info='+JSON.stringify(infos)
})
}
}
}
//
const receiveReport=()=>{
person.IsReceiveReport=!person.IsReceiveReport
if(!person.cusSerInfo.EQImgUrl){
GetTestInfo()
}
}
//
const GetChangeStudent = (Id) => {
getChangeStudent({studentId: Id}).then(res => {
let { Code, Data } = res
if (Code === 200 && Data) {
GetUserReportDetails()
GetTestReportData()
}
})
}
const dateName=ref('')
//
const GetUserReportDetails = () => {
if(person.examId){
uni.showLoading({title: '加载中'});
getUserReportDetails({examId: person.examId}).then(res => {
let { Code, Data, Message } = res
if(Code===200&&Data){
setTimeout(()=>{
uni.hideLoading();
},100)
let { BaseInfo, ExamRecord, Study, Subjects }=res.Data
person.reportInfo = BaseInfo
dateName.value=person.reportInfo.StartTime.replace('-','年').replace('-','月')+'日'
person.studentScore = ExamRecord
person.subjectScore = Subjects
person.educaActive = person.educaList.findIndex(f=>{return f.name===BaseInfo.BaseInfoTitle.TargetLevelName})
}else{
uni.hideLoading();
uni.showToast({
title: Message,
icon: 'none'
})
}
})
}
}
//
const GetTestReportData = () => {
getTestReportData({examId: person.examId}).then(res => {
let { Code, Data, Message } = res
if(Code===200&&Data){
person.testInfo=Data
}
})
}
//
const GetTestInfo = () => {
getTestInfo().then(res => {
let { Code, Data, Message } = res
if(Code===200&&Data){
person.cusSerInfo=Data
}
})
}
</script>
<style lang="scss" scoped>
.page-bg{
padding-bottom: 40rpx;
}
.lock_box{
height: 500px;
background-image: radial-gradient(#6b84f4 0, #d3d5de 100%);
color: #fff;
border-radius: 20rpx;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
margin: 32rpx;
font-size: 30rpx;
image{
width: 150rpx;
height: 150rpx;
margin: 0 auto 20rpx auto;
}
}
.cont_table{
.table_title{
font-size: 26rpx;
.table_title_txt{
display: flex;
text-align: center;
color: #6471AC;
font-weight: 600;
border-right: 2rpx solid #fff;
border-bottom: 2rpx dotted #8093d7;
padding: 20rpx 0rpx;
white-space: nowrap;
.txt_item{
width: 25%;
line-height: 35rpx;
display: flex;
flex-direction: column;
justify-content: center;
}
}
}
.table_name{
font-size: 26rpx;
text-align: center;
.table_name_txt{
display: flex;
align-items: center;
justify-content: space-between;
line-height: 70rpx;
.txt_item{
width: 25%;
height: 70rpx;
overflow: hidden;
color: #6F6F70;
justify-content: center;
white-space: nowrap;
image{
width: 20rpx;
height: 20rpx;
}
:deep(.txt_item_press){
uni-slider{
.uni-slider-handle-wrapper{
width: 120rpx;
height: 20rpx;
background-image: linear-gradient(to right, #DBE1FF, #6B86FF);
}
.uni-slider-thumb{
background: rgba(0,0,0,0) !important;
border-radius: 0;
box-shadow: inherit !important;
top: 28rpx;
transform: rotate(180deg);
&::after{
content: "";
width: 0 !important;
height: 0 !important;
border-style: solid;
border-width: 0rpx 12rpx 12rpx 12rpx;
border-color: transparent transparent #6B86FF transparent;
border-radius: 0;
}
}
.uni-slider-track{
background: rgba(0,0,0,0) !important;
}
.uni-slider-wrapper {
justify-content: center;
}
.uni-slider-tap-area{
flex: inherit;
}
}
}
.txt_item_btn{
color: #6B86FF;
}
.txt_item_red{
color: red;
}
}
}
}
}
:deep(.txt_item_press){
wx-slider{
border-radius: 40rpx;
width: 85% !important;
height: 20rpx !important;
background-image: linear-gradient(to right, #DBE1FF, #6B86FF) !important;
margin: 30rpx auto 0 auto;
border: 0;
.wx-slider-handle{
width: 1px;
height: 1px;
margin-top: -20rpx;
position: relative;
transform: rotate(180deg);
border: 0;
&::before{
position: absolute;
left: -45rpx;
content: "";
width: 0 !important;
height: 0 !important;
border-style: solid;
border-width: 0rpx 12rpx 12rpx 12rpx;
border-color: transparent transparent #6B86FF transparent;
border-radius: 0;
}
}
.wx-slider-handle-wrapper{
height: 0;
}
.wx-slider-thumb{
box-shadow: inherit !important;
}
}
}
.report_user{
border-top: 2rpx solid #eee;
background: #ffffff;
padding: 32rpx;
margin: 0 0 40rpx 0;
.report_user_info{
color: #4A4A4A;
font-size: 30rpx;
text-indent: 2em;
text{
color: #FFA066;
}
}
.report_user_tag{
background-image: linear-gradient(to right, #DBE1FF, #6B86FF);
padding: 32rpx;
border-radius: 20rpx;
margin: 20rpx 0;
justify-content: space-between;
.report_tag_item{
color: #ffffff;
font-size: 22rpx;
white-space: nowrap;
text-align: center;
justify-content: center;
&:last-child{
margin-right: 0;
}
.tag_item_active_txt{
font-size: 28rpx;
font-weight: 600;
}
.tag_item_active{
position: relative;
// &::before{
// position: absolute;
// top: 18rpx;
// left: -32rpx;
// content: "";
// width: 0 !important;
// height: 0 !important;
// border-style: solid;
// border-width: 0rpx 10rpx 18rpx 10rpx;
// border-color: transparent transparent #fff transparent;
// border-radius: 0;
// }
image{
position: absolute;
top: -14rpx;
left: -42rpx;
width: 28rpx;
height: 26rpx;
}
}
}
}
.report_user_txt{
color: #FF0000;
font-size: 24rpx;
text-align: right;
text{
font-size: 26rpx;
font-weight: 600;
}
}
}
.report_echarts{
margin: 0 32rpx;
.report_echart_cont{
border-radius: 20rpx;
background: #ffffff;
margin-top: 40rpx;
padding: 30rpx 30rpx;
line-height: 60rpx;
font-size: 28rpx;
}
:deep(.cont_table){
.uni_table{
.uni-tr{
width: 100%;
border: 1px solid red;
}
}
}
}
.report_receive{
margin: 30rpx 30rpx;
.report_receive_des{
font-size: 30rpx;
color: #736F6D;
line-height: 45rpx;
.mar-top{
margin-top: 40rpx;
}
.receive_des_title{
font-weight: 600;
}
.receive_des{
text-indent: 2em;
}
text{
color: #FFA755;
}
}
.report_receive_btn{
justify-content: center;
margin-top: 60rpx;
text{
background: #6B86FF;
color: #ffffff;
border-radius: 40rpx;
font-size: 30rpx;
padding: 20rpx 100rpx;
text-align: center;
letter-spacing: 4rpx;
}
}
.p_cont{
color: #fff;
font-size: 28rpx;
.popup_cont_title{
font-size: 38rpx;
font-weight: 600;
}
.popup_cont_pic{
margin: 40rpx auto 20rpx auto;
background: #fff;
width: 380rpx;
height: 380rpx;
border-radius: 30rpx;
overflow: hidden;
image{
width: 100%;
height: 100%;
}
}
.popup_cont_des{
color: #fff;
}
.popup_list{
padding: 0rpx 20rpx;
margin-top: 70rpx;
.popup_item{
justify-content: space-between;
padding: 12rpx 0;
border-bottom: 2rpx solid rgba(255, 255, 255, 0.21);
&:last-child{
border-bottom: 0;
}
}
}
}
}
.report_rate_score{
font-size: 30rpx;
white-space: nowrap;
line-height: 38rpx;
margin: 30rpx 0;
.score_blue{
color: #152FA0;
}
text{
font-size: 28rpx;
}
}
.report_rate_info{
font-size: 28rpx;
line-height: 38rpx;
margin-bottom: 28rpx;
text{
margin-right: 40rpx;
}
}
.report_rate_title{
font-size: 32rpx;
text{
font-size: 24rpx;
font-weight: 400;
}
}
.report_ccc{
color: #787878;
}
.report_blue{
color: #6B86FF;
}
.report_orange{
color: #FFA755;
}
.report_rate{
margin: 0 32rpx;
padding: 30rpx;
background: #ffffff;
border-radius: 20rpx;
.report_rate_list{
display: flex;
flex-wrap: wrap;
font-size: 28rpx;
.report_rate_item{
margin-top: 20rpx;
margin-right: 30rpx;
white-space: nowrap;
}
.report_rate_blue{
color: #6B86FF !important;
}
}
}
</style>

261
pages/examTest/index.vue Normal file
View File

@ -0,0 +1,261 @@
<template>
<Navigation :title_name="person.exam.examName"
:isReturn="0"
:color="'#000000'"
:bgc="'#FFFFFF'"
:isCenter="false" />
<view class="page-bg">
<view class="test_top">
<text>{{person.exam.userName}}</text>
<text>{{person.exam.subject}}</text>
<text>{{person.exam.totalScore}}</text>
<text class="top-score" v-if="person.exam.isJuniorSchool">{{person.exam.baseScore?person.exam.baseScore+'':'0分'}}</text>
<text>{{person.testData.length>0?person.btnActive+1:person.btnActive}}/{{person.testData.length}}</text>
</view>
<view v-if="person.testData&&person.testData.length>0">
<movable-area class="movable_box" v-if="person.itemExam.PaperType===1">
<movable-view :style="{height:person.itemExam.Width*person.scaleHeight+'px;'}" direction="all" @scale="onScale" scale scale-min="0.5" scale-max="4" >
<view class="test_center" :style="{transform:'rotate(90deg);',width:person.itemExam.Width*person.scaleHeight+'px;',height:person.itemExam.Height*person.scaleHeight+'px;'}">
<image :style="{width:person.itemExam.Width*person.scaleHeight+'px;',height:person.itemExam.Height*person.scaleHeight+'px;'}" :src="person.itemExam.ImageUrl" mode=""></image>
<view class="page_item flex" v-for="(quest,quIndex) in person.itemExam.Questions" :key="quIndex"
:style="{
top:quest.Y*person.scaleHeight +'px;',
left:quest.X*person.scaleHeight+'px;',
width:quest.Width*person.scaleHeight+'px;',
height:quest.Height*person.scaleHeight+'px;'}">
<view class="text_flex flex" :style="{fontSize:quest.isObj?'24rpx':''}">
<text class="color_red">{{quest.score}}</text>/
<text class="color_blue">{{quest.totalScore}}</text>
</view>
</view>
</view>
</movable-view>
</movable-area>
<movable-area class="movable_box" v-else-if="person.itemExam.PaperType===2">
<movable-view :style="{height:person.itemExam.Height*person.scaleWidth+'px;'}" direction="all" @scale="onScale" scale scale-min="0.5" scale-max="4" >
<view class="test_center" :style="{width:person.itemExam.Width*person.scaleWidth+'px;',height:person.itemExam.Height*person.scaleWidth+'px;'}">
<image data-type="image" :src="person.itemExam.ImageUrl" mode=""></image>
<view class="page_item flex" v-for="(quest,quIndex) in person.itemExam.Questions" :key="quIndex"
:style="{
top:quest.Y*person.scaleWidth +'px;',
left:quest.X*person.scaleWidth+'px;',
width:quest.Width*person.scaleWidth+'px;',
height:quest.Height*person.scaleWidth+'px;'}">
<view class="text_flex flex" :style="{fontSize:quest.isObj?'24rpx':''}">
<text class="color_red">{{quest.score}}</text>/
<text class="color_blue">{{quest.totalScore}}</text>
</view>
</view>
</view>
</movable-view>
</movable-area>
</view>
<view v-else>
<view class="empty_box">
<image src="../../static/null_icon.png" mode=""></image>
<text>暂无数据~</text>
</view>
</view>
<view class="test_bottom flex">
<view class="bottom_list">
<view class="btn_list flex" v-if="person.itemLength>1">
<view class="btn_item" :class="[person.btnActive===index?'btn_item_active':'']" v-for="(item,index) in person.itemLength" :key="index" @click="changeBtn(index)">
{{item}}
</view>
</view>
</view>
<view class="bottom_list flex">
<view class="bottom_item flex">
<text class="bottom_item_red"></text>
我的得分
</view>
<view class="bottom_item flex">
<text class="bottom_item_blue"></text>
年级平均分
</view>
</view>
</view>
</view>
</template>
<script setup>
import { nextTick, reactive, ref, watch } from 'vue'
import { onLoad, onShow } from "@dcloudio/uni-app";
import Navigation from '@/components/navigation/index.vue'
import { getTestImgs } from "@/api/exam-data";
let person=reactive({
testData:[],
exam:{},
btnActive:0,
itemExam:{},
itemLength:0,
scaleWidth:0, //
scaleHeight:0
})
onLoad((e)=>{
// e
person.exam=JSON.parse(e.info)
GetTestImgs(e.Id)
load()
})
//
const load=()=>{
if(person.testData&&person.testData.length>0){
person.itemExam=person.testData[person.btnActive]
person.itemLength=person.testData.length
//
uni.getSystemInfo({
success:(res)=> {
person.scaleWidth = res.windowWidth/person.itemExam.Width
person.scaleHeight = res.windowWidth/person.itemExam.Height
}
})
}
}
//
const GetTestImgs=(Id)=> {
getTestImgs({examSubjectId: Id}).then(res => {
let { Code, Data, Message } = res
if(Code===200&&Data){
person.testData=Data
load()
}else{
uni.showToast({
title: Message,
icon: 'none'
})
}
})
}
const changeBtn=(index)=>{
person.btnActive=index
if(person.testData&&person.testData.length>0){
person.itemExam=person.testData[index]
}
}
</script>
<style lang="scss" scoped>
.test_top{
padding: 0 32rpx;
height: 80rpx;
line-height: 80rpx;
font-size: 28rpx;
color: #888888;
text{
margin-right:30rpx;
}
.top-score{
color: red;
font-weight: bold;
}
}
.page-bg{
height: calc(100vh - 230rpx);
}
.movable_box{
width: 375px;
height: calc(100vh - 340rpx);
overflow: hidden;
movable-view{
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
}
.test_center{
position: relative;
top: 0;
left: 0;
image{
width: 100%;
height: 100%;
}
.page_item{
position: absolute;
border: 2rpx solid red;
.text_flex{
position: absolute;
right: 10rpx;
top: 10rpx;
font-size: 36rpx;
line-height: 30rpx;
overflow: hidden;
text{
font-weight: 600;
}
}
}
}
.test_bottom{
background: #fff;
height: 80rpx;
overflow: hidden;
width: 100%;
position: fixed;
bottom: 0;
text-align: center;
border-top: 2rpx solid #eee;
justify-content: space-between;
z-index: 999;
.bottom_list{
padding: 25rpx 32rpx;
.btn_list{
padding: 10rpx 0;
max-width: 350rpx;
display: -webkit-box;
white-space: nowrap;
overflow-x: scroll;
-webkit-overflow-scrolling:touch; //
.btn_item{
border: 2rpx solid #999;
color: #999;
margin-right: 25rpx;
border-radius: 50%;
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
&:last-child{
margin-right: 0;
}
}
.btn_item_active{
background: #6B86FF !important;
color: #fff !important;
border: 2rpx solid #6B86FF !important;
}
}
.bottom_item{
font-size: 24rpx;
color: #888888;
margin-left: 20rpx;
white-space: nowrap;
text{
width: 24rpx;
height: 24rpx;
border-radius: 50%;
margin-right: 8rpx;
}
.bottom_item_red{
background: #FF2929;
}
.bottom_item_blue{
background: #6B86FF;
}
}
}
}
.color_red{
color: #FF2929;
}
.color_blue{
color: #6B86FF;
}
</style>

90
pages/login/login.vue Normal file
View File

@ -0,0 +1,90 @@
<template>
<view class="warpper-login">
<view class="img_loading"></view>
<view class="text">登录中···</view>
</view>
</template>
<script setup lang="ts">
import {
getUserLogin,
getConfig
} from "@/api/user";
import {onShow } from "@dcloudio/uni-app";
onShow(()=>{
uni.login({
provider: 'weixin', //使
success: (loginRes) => {
getUserLogin(loginRes.code).then((res :any) => {
console.log('APP.vue请求登录',res);
if (res.Code == 200) {
uni.setStorageSync('TokenType', res.Data.TokenType)
uni.setStorageSync('HAS_STUDENT', res.Data.user.HasStudent)
uni.setStorageSync('HAS_BINDSTUDENT', res.Data.user.BindMobile)
if (res.Data.AccessToken) {
uni.setStorageSync('token', res.Data.AccessToken)
uni.navigateBack()
} else {
uni.setStorageSync('XCXID', res.Data.xcxId)
getConfig().then((resCig:any) => {
console.log('公众号AppId', resCig);
let url = 'https://mk-wxauth.23544.com'
uni.navigateTo({
url: `/pages/webview/webview?appId=${resCig.Data.gzh.AppId}&url=${url}`
})
})
}
} else {
console.log('denglushibai', res.Code);
}
})
}
});
})
</script>
<style lang="scss">
page{
font-family: 'PingFang SC';
border: 0 !important;
background: #ffffff;
}
.warpper-login{
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
background: rgba(0 , 0, 0, 0.5);
justify-content: center;
align-items: center;
}
.img_loading{
width: 50px;
height: 50px;
background: url('@/static/login_loading.png');
background-size: 100% 100%;
animation: runLoginMove 2s infinite linear;
margin-bottom: 20px;
}
.text{
color: #fff;
font-family: PingFang SC;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: normal;
letter-spacing: 0.96px;
margin-bottom: 200px;
}
@keyframes runLoginMove {
0%{
transform: rotate(0turn);
}
50%{
transform: rotate(0.5turn);
}
100%{
transform: rotate(1turn);
}
}
</style>

672
pages/user/index.vue Normal file
View File

@ -0,0 +1,672 @@
<template>
<Navigation :title_name="'个人中心'" :isReturn="person.userList&&person.userList.length>0?1:3"
:returnIcon="'../../static/education/add_user.png'" :color="'#000000'" :bgc="'#FFFFFF'"
@iconClick="jumpAddStudent" :isCenter="true" />
<view v-if="person.userList&&person.userList.length>0">
<view class="ech">
<view class="card-wrapper">
<view class="card-scroll">
<view v-for="(item,index) in person.userList" :key="index">
<CurrItem :datas="item" @onOpenStu="getOpenStu(item,index)" @onUnBind="getUnBind(item)"
:isChange="false" :bgIndex="index" />
</view>
</view>
</view>
<view class="option-list">
<view class="option-item" v-for="(op,opIndex) in person.optionList" :key="opIndex"
@click.stop="toRelated(op,opIndex)">
<image :src="op.icon" mode=""></image>
<view class="option-item-name">{{op.name}}</view>
</view>
</view>
</view>
<!-- 用户信息弹窗 -->
<view class="dio" v-if="showChangeStudent">
<view class="dio-contain">
<view class="dio-padding">
<view class="center" :style="{background:stuInfoBackground[backIndex]}">
<image src="https://minio.23544.com:9010/data-center/gzh/Avatar.png" mode=""></image>
<view class="stu-name">{{showLookStudentInfo.name}}</view>
</view>
<view class="id-card">
<view class="number">{{showLookStudentInfo.number}}</view>
<view class="look-btn" :style="{color:stuInfoBackground[backIndex]}" @click="seePhone">查看</view>
</view>
</view>
<view class="cancel" @tap="showChangeStudent=false"></view>
</view>
</view>
<!-- 联系客服弹窗 -->
<Popups :isShow="person.isShowService" :bgColor="'#fff'">
<view style="width: 250px;display: flex;flex-direction: column;align-items: center;">
<view class="popup_cont_des" style="font-size: 20px;margin-top: 25px;">
{{person.phoneNum}}
</view>
<view class="popup_add_btn">
<button class="flex" @tap="connectServe"
style="font-size: 16px;height: 35px;line-height: 35px;width: 100%;">
<image src="@/static/education/phone_icon.png" mode=""></image>
联系客服
</button>
</view>
</view>
</Popups>
</view>
<view v-else>
<view class="empty_box">
<image src="@/static/null_icon.png" mode=""></image>
<text>暂无学生个人信息~</text>
</view>
<view class="add_btn">
<button open-type="getPhoneNumber" v-if="!showReactivePhone" @getphonenumber="getPhoneNumber">添加学生</button>
<button @tap="jumpAddStudent" v-else>添加学生</button>
</view>
</view>
<!-- 短信验证弹窗 -->
<Popups :isShow="showDioUnbindMseeage" :width="260" height="auto" :bgColor="'#fff'"
@cancelBtn="showDioUnbindMseeage=false">
<view class="p_cont">
<view class="popup_cont_title">
短信验证
</view>
<view class="popup_cont_list">
<view class="cont_list_item1">
<view style="font-size: 18px;">{{unBindPhone}}发送验证信息<br />请输入验证码</view>
<view class="flex">
<input v-for="(item,index) in sendYZMCode" @input="inputLastNumber(index,$event)" :key="index"
:value="item.name" :focus="item.isFocus" @blur="preventFocus(index,$event)" @click="messageFocus(index,$event)"
placeholder-style="color:#BDBDBD;" type="number" maxlength="1"
:class="{'border-indey-message':item.isFocus,'border-error':errroYZM}"
:selection-start="(item.isFocus)?0:-1" :selection-end="(item.isFocus)?item.name.length:-1"/>
</view>
</view>
</view>
<view class="popup_cont_tag flex">
<button v-if="timeLast>0" class="send-btn">{{timeLast}}秒后重发</button>
<button v-else @click="retrieAgain" class="send-btn"
style="font-size: 16px;">{{showMessageText?'再次获取':'获取验证码'}}</button>
</view>
<view class="popup_add_btn">
<button @click="okClickMessage" class="massage-btn" style="font-size: 18px;">确认</button>
</view>
</view>
</Popups>
</template>
<script setup>
import {
ref,
reactive,
onMounted,
watch,
nextTick
} from "vue";
import Popups from "@/components/popups/index.vue";
import CurrItem from "@/components/currItem/index.vue";
import {
getUserInfo,
getUserLogin,
getUserPhone,
getConfig,
postRegister,
getbindstudentList,
getSTudyReportInfo,
putUnBind,
LookStudentInfo,
bindMobile,
getSendTelphoneCode
} from "@/api/user";
import {
onShow,
onHide
} from "@dcloudio/uni-app";
import Navigation from '@/components/navigation/index.vue'
const showDioUnbindMseeage = ref(false) //
const showMessageText = ref(false) //
const unBindPhone = ref('')
const timeLast = ref(0)
const unBindParams = { //
"StudentId": 0,
"Mobile": "0",
"SmsId": "0",
"SmsCode": "0"
}
const errroYZM=ref(false) //
const sendYZMCode = ref([ //6
{
name: '',
isFocus: false
},
{
name: '',
isFocus: false
},
{
name: '',
isFocus: false
},
{
name: '',
isFocus: false
},
{
name: '',
isFocus: false
},
{
name: '',
isFocus: false
},
])
let person = reactive({
phoneNum: '18812545252',
phoneNumAll: '',
userList: [], //
optionList: [{
name: '联系客服',
icon: '../../static/education/phone.png'
},
{
name: '关于',
icon: '../../static/education/about.png'
}
],
isShowService: false, //
currStudent: {} //
})
const showLookStudentInfo = reactive < {
number: string,
name: string
} > ({})
const webSrc = ref('') //webview code UR
const jumpHasConnentStudent = ref(false) //
const appId = ref('') //appId
const showStudentList = ref(false)
const showReactivePhone = ref(false) //
const preventFocus = (index) => {
sendYZMCode.value[index].isFocus = false
}
const focstState=ref(false)
//
const inputLastNumber = (index, event) => {
sendYZMCode.value[index].name = event.detail.value
if(!focstState.value){
if (sendYZMCode.value[index].name) {
if (index < 5) {
sendYZMCode.value[index + 1].isFocus = true
}
}else{
if (index > 0) {
sendYZMCode.value[index - 1].isFocus = true
}
}
}else{
focstState.value=false //
}
}
const messageFocus=(index,event)=>{ //
let name = sendYZMCode.value[index].name
if(!name){
focstState.value=false //
}else{
focstState.value=true //
}
errroYZM.value=false //
sendYZMCode.value[index].isFocus=true
}
//
const showTimer = ref(true) //60
const timer = ref(0)
const retrieAgain = () => {
if (showTimer.value) {
showTimer.value = false
// let telephone = uni.getStorageSync('USER_PHONE')
showMessageText.value = true
timeLast.value = 60
timer.value = setInterval(() => {
timeLast.value--; //
if (timeLast.value <= 0) { //
clearInterval(timer.value); //
showTimer.value = true
}
}, 1000); //
console.log(unBindPhone.value);
getSendTelphoneCode(unBindPhone.value, 2).then(res => {
console.log(res);
if (res.Code == 200) {
unBindParams.SmsId = res.Data
}
})
}
}
const okClickMessage = () => {
let messageCode = ''
sendYZMCode.value.forEach(item => {
messageCode += item.name
})
unBindParams.SmsCode = messageCode
putUnBind(unBindParams).then(res => {
console.log(res);
if (res.Code == 200) {
showDioUnbindMseeage.value = false
getStudentList() //
} else {
errroYZM.value=true
uni.showToast({
title:res.Message,
position:'bottom'
});
sendYZMCode.value.forEach(item => {
item.name=''
})
}
})
}
//
const getUnBind = (item) => {
showDioUnbindMseeage.value = true
errroYZM.value=false
LookStudentInfo(item.Id).then(res => {
unBindParams.StudentId = item.Id
unBindParams.Mobile = res.Data.Telephone
unBindPhone.value = res.Data.Telephone
})
sendYZMCode.value.forEach(item => {
item.name = ''
})
}
onShow(() => {
getUserInfo().then(res => {
console.log('用户信息', res);
showReactivePhone.value = res.Data.BindMobile
})
getStudentList()
})
//
const showChangeStudent = ref(false)
const stuInfoBackground = ['#6B86FF', '#45CB9F', '#22C1F2']
const backIndex = ref(0)
const getOpenStu = (item, index) => {
showChangeStudent.value = true
person.currStudent = item
backIndex.value = index % 3
LookStudentInfo(item.Id).then(res => {
console.log(res);
showLookStudentInfo.name = res.Data.RealName
person.phoneNumAll = res.Data.Telephone
showLookStudentInfo.number = res.Data.Telephone.replace(res.Data.Telephone.substring(3, 7), '****')
})
}
//
const seePhone = () => {
showLookStudentInfo.number = person.phoneNumAll
}
//
const toRelated = (item, index) => {
if (item.name === '关于') {
uni.navigateTo({
url: `/pages/userRelated/index`
})
} else if (item.name === '联系客服') {
person.isShowService = !person.isShowService
6000().then(res => {
console.log(res);
if (res.Code == 200) {
person.phoneNum = res.Data.ServiceNumber
} else {
uni.showToast({
title: '暂无客服'
})
}
})
}
}
//
const toAddStudent = (item, index) => {
console.log(99999);
}
//
const getPhoneNumber = (e) => {
console.log(111, e.detail.code);
if (e.detail.errMsg == "getPhoneNumber:ok") {
bindMobile(e.detail.code).then(res => {
if (res.Code == 200) {
uni.navigateTo({
url: '/pages/userAddStudent/index'
})
} else {
console.log('未授权手机号')
}
})
}
}
const jumpAddStudent = (e) => {
uni.navigateTo({
url: '/pages/userAddStudent/index'
})
}
//
const getStudentList = () => {
getbindstudentList().then(res => {
console.log('学生列表', res);
if (res.Code == 200) {
person.userList = res.Data
}
})
}
//token
watch(() => showStudentList.value, () => {
console.log(showStudentList.value);
if (showStudentList.value) {
getStudentList()
}
})
//
const connectServe = () => {
uni.makePhoneCall({
phoneNumber: person.phoneNum
})
}
</script>
<style lang="scss" scoped>
.empty_box {
z-index: 0;
}
page {
overflow: hidden;
position: inherit !important;
}
.popup_cont {
width: 211px;
}
.add_btn {
position: absolute;
bottom: 30%;
}
.ech {
width: 100vw;
position: relative;
top: 0px;
margin-bottom: 50px;
.card-wrapper {
background: #F5F5F5;
padding: 10px 20px;
display: flex;
justify-content: center;
}
}
.option-list {
width: 100%;
padding: 10px 20px;
box-sizing: border-box;
background: #fff;
.option-item {
display: flex;
align-items: center;
color: #3D425B;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: normal;
letter-spacing: 0.72px;
padding: 10px 0;
border-bottom: 1px solid #ECECEC;
&:last-child {
border-bottom: 0 !important;
}
image {
width: 20px;
height: 20px;
margin-right: 8px;
}
.option-item-name {
width: 92%;
}
}
}
.dio {
width: 100vw;
height: 100%;
position: fixed;
left: 0;
top: 0;
background: rgba(0, 0, 0, 0.42);
z-index: 99;
.dio-contain {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.dio-padding {
border-radius: 10px;
background: #FFF;
padding: 40rpx;
display: flex;
flex-direction: column;
justify-content: center;
}
.cancel {
margin-top: 20px;
width: 40px;
height: 40px;
background: url('@/static/education/cancel.png');
background-size: 100% 100%;
}
}
}
.center {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 250px;
height: 200px;
border-radius: 10px;
background: #6B86FF;
image {
width: 90px;
height: 90px;
border-radius: 50%;
}
.stu-name {
color: #FFF;
font-size: 18px;
font-weight: 400;
letter-spacing: 0.72px;
margin-top: 5px;
}
}
.id-card {
width: 100%;
box-sizing: border-box;
padding: 20px 0 0 0;
color: #3D425B;
font-family: PingFang SC;
font-size: 18px;
font-style: normal;
font-weight: 400;
line-height: normal;
letter-spacing: 0.72px;
display: flex;
justify-content: space-between;
align-items: center;
.look-btn {
color: #6B86FF;
}
}
.btn-add {
width: 20px;
height: 20px;
position: absolute;
left: 10px;
opacity: 0;
}
.p_cont {
// padding: 20px;
background: #fff;
border-radius: 30rpx;
.popup_cont_pic {
margin: 40rpx auto 20rpx auto;
background: #fff;
width: 140rpx;
height: 140rpx;
border-radius: 30rpx;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
}
.popup_add_btn {
button {
width: 45%;
&:nth-child(1) {
width: 55%;
}
}
}
.popup_cont_title {
font-size: 36rpx;
color: #575757;
font-weight: 600;
}
.popup_cont_tag {
justify-content: flex-end;
width: 100%;
text-align: right;
margin-top: 20rpx;
button {
border: 2rpx solid #A5B5FA;
color: #A5B5FA;
font-size: 24rpx;
margin: 0;
padding: 0 20rpx;
background: #fff;
height: 45rpx;
line-height: 45rpx;
&:nth-child(2) {
margin-left: 20rpx;
}
}
}
.popup_cont_list {
text-align: left;
.cont_list_item {
font-size: 28rpx;
background: #F8F9FF;
border-radius: 8rpx;
padding: 32rpx;
margin-top: 30rpx;
&:first-child {
input {
width: 99%;
border: 0;
padding: 10rpx 0;
text-align: left;
}
}
input {
border: 2rpx solid #9C9C9C;
border-radius: 12rpx;
width: 16.6%;
padding: 15rpx 0;
color: #2C2C2C;
font-weight: 500;
margin-top: 20rpx;
margin-right: 10rpx;
text-align: center;
&:last-child {
margin-right: 0;
}
}
}
.cont_list_item1 {
font-size: 28rpx;
border-radius: 8rpx;
margin-top: 30rpx;
input {
border: 2rpx solid #9C9C9C;
border-radius: 12rpx;
width: 16.6%;
padding: 15rpx 0;
color: #2C2C2C;
font-weight: 500;
margin-top: 20rpx;
margin-right: 10rpx;
text-align: center;
&:last-child {
margin-right: 0;
}
}
}
}
}
.massage-btn {
width: 100% !important;
height: 40px !important;
line-height: 40px !important;
}
.send-btn {
width: 50% !important;
height: 30px !important;
line-height: 30px !important;
}
.border-indey-message{
border: 3px solid skyblue !important;
}
.border-error{
border: 1px solid #FF0000 !important;
}
</style>

View File

@ -0,0 +1,759 @@
<template>
<Navigation :title_name="'添加学生'" :isReturn="0" :color="'#000000'" :bgc="'#FFFFFF'" :isCenter="true" />
<view class="add_phone">
<view class="input_des text_right" v-if="!person.studentInfo.name">请输入办理智慧校园套餐的手机号</view>
<view class="add_phone_input flex">
<view class="input_name">
<text style="font-size:20px;">学生账号</text>
<br />
<br />
<br />
<text v-if="!person.studentInfo.name" class="input_des">(学生绑定手机号)</text>
</view>
<view class="showAccountNumber">
{{person.keyword.length}}/11
</view>
<input placeholder-style="color:#BDBDBD;" type="number" maxlength="11" v-model="person.keyword" @focus="focusBtn" focus
placeholder="请输入手机号" />
<svg @click="clearBtn" v-if="person.clearbtn" t="1669974117863" class="icon" viewBox="0 0 1024 1024"
version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3497" width="20" height="20">
<path
d="M687.603949 656.994302 541.10027 510.457878 687.603949 363.943966c8.829086-8.840342 8.829086-23.122627 0-31.961946-8.850575-8.840342-23.13286-8.840342-31.962969 0L509.138324 478.495932 362.623389 331.980997c-8.840342-8.818853-23.122627-8.818853-31.962969 0-8.840342 8.840342-8.840342 23.144116 0 31.984459l146.493445 146.514935L330.638931 656.994302c-8.819876 8.830109-8.819876 23.133883 0 31.962969 8.840342 8.829086 23.144116 8.829086 31.984459 0l146.514935-146.514935 146.502655 146.514935c8.830109 8.829086 23.112394 8.829086 31.962969 0C696.433034 680.129208 696.45657 665.824411 687.603949 656.994302z"
p-id="3498" fill="#bfbfbf"></path>
<path
d="M938.362063 510.457878c0-237.061161-192.174857-429.234995-429.247274-429.234995-237.062184 0-429.246251 192.173834-429.246251 429.234995 0 237.083673 192.185091 429.257507 429.246251 429.257507 97.345072 0 186.435133-33.110095 258.440074-87.677898 2.958378-3.354398 4.900613-7.636934 4.900613-12.449543 0-10.506285-8.521071-19.026332-19.027355-19.026332-5.431709 0-10.287297 2.162246-13.752212 5.826705l-0.2415 0c-64.456011 47.414893-143.745868 75.800383-229.876528 75.800383-214.679407 0-388.730489-174.073594-388.730489-388.719232 0-214.688617 174.051081-388.718209 388.730489-388.718209 214.688617 0 388.697743 174.029592 388.697743 388.718209 0 65.548902-15.386432 127.277802-44.081984 181.490517l0 0.309038c-0.508583 1.811252-1.104147 3.576455-1.104147 5.519714 0 10.507308 8.520047 19.028379 19.028379 19.028379 8.18952 0 15.054881-5.254677 17.703197-12.494569l0 0.132006C920.349827 648.38625 938.362063 581.536726 938.362063 510.457878z"
p-id="3499" fill="#bfbfbf"></path>
</svg>
</view>
<view class="phone_list" v-if="person.studentInfo.StudentName">
<view class="phone_item">姓名{{person.studentInfo.StudentName}}</view>
<view class="phone_item">学校{{person.studentInfo.SchoolName}}</view>
<view class="phone_item">年级{{person.studentInfo.Grade}}·{{person.studentInfo.ClassName}}</view>
</view>
</view>
<view class="add_btn">
<!-- 查询 -->
<button @click.stop="addQuery" style="font-size: 17px;height: 44px;line-height: 44px;"
v-if="!person.studentInfo.StudentName">查询</button>
<!-- 本机绑定成功 -->
<button @click.stop="addBind" style="font-size: 17px;height: 44px;line-height: 44px;"
v-if="person.studentInfo.StudentName && (!person.isShowVerifyPopup) && (!person.isShowInformPopup)">绑定</button>
<!-- 名字绑定成功 -->
<button @click.stop="isShowPopupStudent=true" style="font-size: 17px;height: 44px;line-height: 44px;"
v-if="person.isShowVerifyPopup">绑定</button>
<!-- 短信绑定成功 -->
<button @click.stop="isShowPopupMessage=true" style="font-size: 17px;height: 44px;line-height: 44px;"
v-if="person.isShowInformPopup">绑定</button>
<!-- 返回个人中心 -->
<button @click.stop="backUser" style="font-size: 17px;height: 44px;line-height: 44px;">返回个人中心</button>
</view>
<!-- 绑定成功弹窗 -->
<Popups :isShow="person.isShowBindPopup" :showCancel="false" :width="300" :height="200" :bgColor="'#fff'"
@cancelBtn="person.isShowBindPopup=false">
<view class="p_cont">
<view class="popup_cont_pic">
<image src="@/static/education/ok_icon.png" mode=""></image>
</view>
<view class="popup_cont_des" style="font-size: 20px;">
绑定成功
</view>
<view class="popup_add_btn flex">
<button @click="covAddQuery" class="send-btn">继续绑定学生</button>
<button @click="backUser" class="send-btn">返回个人中心</button>
</view>
</view>
</Popups>
<!-- 学生验证弹窗 -->
<!-- <Popups :isShow="true" :width="300" :bgColor="'#fff'"> -->
<Popups :isShow="isShowPopupStudent" :width="300" :bgColor="'#fff'" @cancelBtn="isShowPopupStudent=false">
<view class="p_cont">
<view class="popup_cont_title">
验证信息
</view>
<view class="popup_cont_list">
<view class="cont_list_item"
style="display: flex;flex-direction: column;align-items: center;justify-content: center;">
<view class="item_subName" style="font-size: 20px;margin-bottom: 10px;">请补充完学生姓名</view>
<view style="display: flex;align-items: center;justify-content: center;">
<view style="margin-right:4px;color: #000;">{{nameFull.getName}}</view>
<input placeholder-style="color:#fff;" style="width:28px;background-color: #fff;padding-left: 6px;"
v-model="nameFull.inputName" focus maxlength="1" type="text"/>
</view>
</view>
<view class="cont_list_item">
<view style="font-size: 19px;margin-bottom: 10px;">请输入家长手机号后六位数字</view>
<view class="flex">
<input v-for="(item,index) in teleLastNumber"
:key="index" placeholder-style="color:#BDBDBD;"
:class="{'border-indey-message':item.isFocus}"
@blur="preventFocusInfo(index,$event)"
@click="infoFocus(index,$event)"
:value="item.name" type="number" maxlength="1"
:focus="item.isFocus" @input="lastInput(index,$event)"
:selection-start="(item.isFocus)?0:-1" :selection-end="(item.isFocus)?item.name.length:-1"/>
</view>
</view>
</view>
<view class="popup_add_btn">
<button @click="addVerifyBind" class="massage-btn" style="font-size: 20px;">绑定</button>
</view>
</view>
</Popups>
<!-- 短信验证弹窗 -->
<!-- <Popups :isShow="true" :width="300" :bgColor="'#fff'"> -->
<Popups :isShow="isShowPopupMessage" :width="260" :bgColor="'#fff'" @cancelBtn="isShowPopupMessage =false">
<view class="p_cont">
<view class="popup_cont_title">
短信验证
</view>
<view class="popup_cont_list">
<view class="cont_list_item1">
<view style="font-size: 16px;">{{uesrTellphone}}发送验证信息<br />请输入验证码</view>
<view class="flex">
<!-- @blur="preventFocus(index,$event)" :value="item.name" :focus="item.isFocus" @focus="messageFocus(index,$event)" -->
<input v-for="(item,index) in sendYZMCode" @input="inputLastNumber(index,$event)"
:key="index" @click="messageFocus(index,$event)" @blur="messageBlur(index)"
:value="item.name" :focus="item.isFocus"
:class="{'border-indey-message':item.isFocus,'border-error':errroYZM}"
placeholder-style="color:#BDBDBD;" type="number" maxlength="1"
:selection-start="(item.isFocus)?0:-1" :selection-end="(item.isFocus)?item.name.length:-1" />
</view>
</view>
</view>
<view class="popup_cont_tag flex">
<button v-if="person.oneTime>0" class="send-btn">{{person.oneTime}}秒后重发</button>
<button v-else @click="retrieAgain" class="send-btn">{{getSendMsgText?'再次获取':'获取验证码'}}</button>
</view>
<view class="popup_add_btn">
<button @click="okClick" class="massage-btn" style="font-size: 20px;">确认</button>
</view>
</view>
</Popups>
<MessageToast :isShow="person.isShowMessage" :bgColor="'rgba(0,0,0,.4)'">
{{searchMsg}}
</MessageToast>
<!-- 信息错误提示框 -->
<MessageToast :isShow="person.isShowErrorMessage" :bgColor="'rgba(0,0,0,.7)'">
信息错误
<view class="message_red" style="font-size: 17px;margin-top: 5px;">{{InfoMessage}}</view>
</MessageToast>
</template>
<script setup>
import {
reactive,
nextTick,
ref
} from "vue";
import Popups from "@/components/popups/index.vue";
import MessageToast from "@/components/messageToast/index.vue";
import Navigation from '@/components/navigation/index.vue'
//api
import {
getUnbindstudentinfo,
getSendbindstudent,
getSendTelphoneCode
} from '@/api/user.ts'
const searchMsg = ref('')
const InfoMessage = ref('')
let person = reactive({
keyword: '', //
clearbtn: false, //
studentInfo: {},
btnName: '查询',
isShowBindPopup: false, //
isShowVerifyPopup: false,
isShowInformPopup: false, //
nameKeyword: '',
phoneKeyword: '',
timer: null, //
oneTime: 0,
isShowMessage: false,
isShowErrorMessage: false, //
})
const isShowPopupStudent = ref(false) //
const isShowPopupMessage = ref(false) //
const BindCode = ref(0) //code
let uesrTellphone = ''
const sendYZMCode = ref([ //6
{
name: '',
isFocus: false,
start:-1,
end:-1
},
{
name: '',
isFocus: false,
start:-1,
end:-1
},
{
name: '',
isFocus: false,
start:-1,
end:-1
},
{
name: '',
isFocus: false,
start:-1,
end:-1
},
{
name: '',
isFocus: false,
start:-1,
end:-1
},
{
name: '',
isFocus: false,
start:-1,
end:-1
},
])
const teleLastNumber = ref([ //6
{
name: '',
isFocus: false
},
{
name: '',
isFocus: false
},
{
name: '',
isFocus: false
},
{
name: '',
isFocus: false
},
{
name: '',
isFocus: false
},
{
name: '',
isFocus: false
},
])
const getSendMsgText = ref(false)
let sendCodeData = ''
const nameFull = reactive({ //
getName: '',
inputName: ''
})
//
const clearBtn = () => {
person.keyword = ''
person.clearbtn = false
}
//
const focusBtn = () => {
person.clearbtn = true
}
const messageBlur=(index)=>{
sendYZMCode.value[index].isFocus = false
}
//
const inputLastNumber = (index, event) => {
sendYZMCode.value[index].name = event.detail.value
console.log(focstState.value);
if(!focstState.value){
if (sendYZMCode.value[index].name) {
if (index < 5) {
sendYZMCode.value[index + 1].isFocus = true
}
}else{
if (index > 0) {
sendYZMCode.value[index - 1].isFocus = true
}
}
}else{
focstState.value=false //
}
}
const focstState=ref(false)
const messageFocus=(index,event)=>{ //
let name = sendYZMCode.value[index].name
if(!name){
focstState.value=false //
}else{
focstState.value=true //
}
errroYZM.value=false //
sendYZMCode.value[index].isFocus=true
}
const focstState1=ref(false)
const infoFocus=(index,event)=>{ //
teleLastNumber.value[index].isFocus=true
let name = teleLastNumber.value[index].name
if(!name){
focstState1.value=false //
}else{
focstState1.value=true //
}
}
const preventFocusInfo = (index) => {
teleLastNumber.value[index].isFocus = false
}
//
const lastInput = (index, event) => {
teleLastNumber.value[index].name = event.detail.value
if(!focstState1.value){
if (teleLastNumber.value[index].name) {
if (index < 5) {
teleLastNumber.value[index + 1].isFocus = true
}
}else{
if (index > 0) {
teleLastNumber.value[index - 1].isFocus = true
}
}
}else{
focstState1.value=false //
}
}
//
const addQuery = () => {
//
if (person.keyword) {
person.isShowMessage = false
console.log(person.keyword);
getUnbindstudentinfo(person.keyword).then(res => {
console.log(res);
if (res.Code !== 200) {
person.isShowMessage = true
searchMsg.value = res.Message
setTimeout(()=>{
person.isShowMessage = false
},3000)
person.studentInfo = {}
} else {
person.studentInfo = res.Data
person.nameKeyword = person.studentInfo.StudentName
nameFull.getName = person.studentInfo.StudentName.replace('*', '')
let sentNumber = person.keyword
uesrTellphone = sentNumber.substring(0, 3) + '****' + sentNumber.substring(7, 11)
console.log(uesrTellphone);
}
})
}
}
const cont1 = ref(true)
const cont2 = ref(true)
const errroYZM =ref(false)
//
const addBind = () => {
//
//
let params = {
StudentTelephone: person.keyword,
VerifyName: '',
VerifyLastTelephone: '',
VerifySMSId: ''
}
switch (BindCode.value) {
case 0:
params = {
StudentTelephone: person.keyword,
VerifyName: '',
VerifyLastTelephone: '',
VerifySMSId: '',
VerifyCode: ''
}
break;
case 511:
let lastNumber = ''
let VerifyName = nameFull.getName + nameFull.inputName
console.log(VerifyName)
teleLastNumber.value.forEach(item => {
lastNumber += item.name
})
params = {
StudentTelephone: person.keyword,
VerifyName: VerifyName,
VerifyLastTelephone: lastNumber,
VerifySMSId: '',
VerifyCode: ''
}
if (!nameFull.inputName) {
person.isShowMessage = true
searchMsg.value = '请补全名字'
setTimeout(() => {
person.isShowMessage = false
}, 3000)
return
}
if (lastNumber.length !== 6) {
console.log('尾号的长度是不是等于');
person.isShowMessage = true
searchMsg.value = '请补全手机尾号'
setTimeout(() => {
person.isShowMessage = false
}, 3000)
return
}
break;
case 1:
console.log('验证码1')
case 512:
let messageCode = ''
sendYZMCode.value.forEach(item => {
messageCode += item.name
})
params = {
StudentTelephone: person.keyword,
VerifyName: '',
VerifyLastTelephone: '',
VerifySMSId: sendCodeData,
VerifyCode: messageCode
}
if (messageCode.length !== 6) {
person.isShowMessage = true
searchMsg.value = '请补全验证码'
setTimeout(() => {
person.isShowMessage = false
}, 3000)
return
}
default:
break;
}
console.log(params);
if (person.studentInfo.StudentName) {
person.isShowMessage = false
getSendbindstudent(params).then(res => {
console.log(res);
BindCode.value = res.Code
if (res.Code == 200) {
person.isShowBindPopup = true
person.isShowVerifyPopup = false
person.isShowInformPopup = false
isShowPopupStudent.value = false
isShowPopupMessage.value = false
BindCode.value=0
teleLastNumber.value.forEach(item => {
item.name=''
})
nameFull.inputName=''
sendYZMCode.value.forEach(item => {
item.name=''
})
// uni.navigateBack() //,
} else if (res.Code == 511) {
isShowPopupStudent.value = true
person.isShowVerifyPopup = true
person.isShowBindPopup = false
person.isShowInformPopup = false
//
if (cont2.value) {
cont2.value = false
} else {
person.isShowErrorMessage = true
InfoMessage.value = `您还有${res.Message}机会`
setTimeout(() => {
person.isShowErrorMessage = false
}, 3000)
}
} else if ((res.Code == 512)||(res.Code == 1)) {
isShowPopupStudent.value = false
isShowPopupMessage.value = true
person.isShowInformPopup = true
person.isShowBindPopup = false
person.isShowVerifyPopup = false
person.oneTime = 0
if (cont1.value) {
cont1.value = false
person.isShowMessage = true
searchMsg.value = '信息验证有误,跳转电话验证'
setTimeout(() => {
person.isShowMessage = false
}, 3000)
} else {
errroYZM.value=true //
person.isShowMessage = true
searchMsg.value = '短信验证错误,请重试'
sendYZMCode.value.forEach(item=>item.name='')
setTimeout(() => {
person.isShowMessage = false
}, 3000)
}
//
} else {
person.isShowMessage = true
searchMsg.value = res.Message
setTimeout(() => {
person.isShowMessage = false
}, 3000)
}
})
}
}
//
const covAddQuery = () => {
person.keyword = ''
person.isShowBindPopup = false
person.isShowVerifyPopup = false
person.isShowInformPopup = false
person.studentInfo = {}
person.btnName = '查询'
}
//
const addVerifyBind = () => {
addBind()
}
//
//
const okClick = () => {
addBind()
}
const showTimer = ref(true) //60
//
const retrieAgain = () => {
if (showTimer.value) {
showTimer.value = false
// let telephone = uni.getStorageSync('USER_PHONE')
getSendMsgText.value = true
person.oneTime = 60
person.timer = setInterval(() => {
person.oneTime--; //
if (person.oneTime <= 0) { //
clearInterval(person.timer); //
showTimer.value = true
}
}, 1000); //
getSendTelphoneCode(person.keyword, 1).then(res => {
console.log(res);
if (res.Code == 200) {
sendCodeData = res.Data
}
})
}
}
//
const backUser = () => {
uni.reLaunch({
url: '/pages/user/index'
})
}
</script>
<style lang="scss" scoped>
page {
background: #f8f8f8 !important;
}
.message_red {
color: #BC0101;
font-size: 24rpx;
margin-top: 20rpx;
}
.add_phone {
background: #fff;
padding: 32rpx 32rpx 60rpx 32rpx;
font-size: 28rpx;
color: #545454;
.input_des {
color: #727272;
font-size: 24rpx;
font-weight: 400;
letter-spacing: 0rpx;
}
.text_right {
margin: 0 0 15rpx 200rpx;
}
.add_phone_input {
position: relative;
white-space: nowrap;
.showAccountNumber{
position: absolute;
right: 10px;
}
.input_name {
width: 200rpx;
font-size: 30rpx;
font-weight: 600;
white-space: nowrap;
line-height: 35rpx;
letter-spacing: 4rpx;
text {
white-space: nowrap;
}
}
input {
border: 2rpx solid #BABABA;
border-radius: 12rpx;
width: 60%;
padding: 15rpx 60rpx 15rpx 30rpx;
color: #2C2C2C;
font-weight: 500;
}
.icon {
position: absolute;
right: 10rpx;
}
}
.phone_list {
margin-top: 40rpx;
margin-bottom: 50rpx;
.phone_item {
line-height: 50rpx;
font-size: 16px;
color: #545454;
}
}
}
.p_cont {
// padding: 20px;
background: #fff;
border-radius: 30rpx;
.popup_cont_pic {
margin: 40rpx auto 20rpx auto;
background: #fff;
width: 150rpx;
height: 150rpx;
border-radius: 30rpx;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
}
.popup_add_btn {
button {
width: 45%;
&:nth-child(1) {
width: 55%;
}
}
}
.popup_cont_title {
font-size: 42rpx;
color: #575757;
font-weight: 600;
}
.popup_cont_tag {
justify-content: flex-end;
width: 100%;
text-align: right;
margin-top: 20rpx;
button {
border: 2rpx solid #A5B5FA;
color: #A5B5FA;
font-size: 24rpx;
margin: 0;
padding: 0 20rpx;
background: #fff;
height: 45rpx;
line-height: 45rpx;
&:nth-child(2) {
margin-left: 20rpx;
}
}
}
.popup_cont_list {
text-align: left;
.cont_list_item {
font-size: 28rpx;
background: #F8F9FF;
border-radius: 8rpx;
padding: 28rpx;
margin-top: 30rpx;
color: rgba(137, 137, 137, 1);
&:first-child {
input {
width: 99%;
border: 0;
padding: 10rpx 0;
text-align: left;
}
}
input {
border: 2rpx solid #9C9C9C;
border-radius: 12rpx;
width: 16.6%;
padding: 15rpx 0;
color: #2C2C2C;
font-weight: 500;
// margin-top: 20rpx;
margin-right: 10rpx;
text-align: center;
&:last-child {
margin-right: 0;
}
}
}
.cont_list_item1 {
font-size: 28rpx;
border-radius: 8rpx;
margin-top: 30rpx;
input {
border: 2rpx solid #9C9C9C;
border-radius: 12rpx;
width: 16.6%;
padding: 15rpx 0;
color: #2C2C2C;
font-weight: 500;
margin-top: 20rpx;
margin-right: 10rpx;
text-align: center;
&:last-child {
margin-right: 0;
}
}
}
}
}
.massage-btn {
width: 100% !important;
height: 40px !important;
line-height: 40px !important;
}
.send-btn {
width: 50% !important;
height: 35px !important;
line-height: 35px !important;
font-size: 18px;
}
.border-indey-message{
border: 3px solid skyblue !important;
}
.border-error{
border: 1px solid #FF0000 !important;
}
</style>

View File

@ -0,0 +1,53 @@
<template>
<Navigation :title_name="'关于'"
:isReturn="0"
:color="'#000000'"
:bgc="'#FFFFFF'"
:isCenter="true" />
<view class="related-list">
<view class="related-item" v-for="(item,index) in person.relatedList" :key="index">
<view class="related-item-name">{{item.name}}</view>
<view class="related-item-time">{{item.des}}</view>
</view>
</view>
</template>
<script setup>
import { reactive } from "vue";
import Navigation from '@/components/navigation/index.vue'
let person=reactive({
relatedList:[
{name:'智喵慧查V1.01',des:'2023年08月12日'},
{name:'更新内容:',des:'版本上线'}
]
})
</script>
<style lang="scss" scoped>
page{
background: #fff;
}
.related-list {
width: 100%;
padding: 10px 20px;
box-sizing: border-box;
.related-item {
color: #454545;
letter-spacing: 0.72px;
padding: 10px 0;
border-bottom: 1px solid #ECECEC;
line-height: 20px;
&:last-child{
border-bottom: 0 !important;
}
.related-item-name{
font-size: 16px;
font-weight: 500;
}
.related-item-time{
font-size: 14px;
color: #999;
}
}
}
</style>

33
pages/webview/webview.vue Normal file
View File

@ -0,0 +1,33 @@
<template>
<web-view :src="webSrc" @load="bindLoad" @message="receptMessage"></web-view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref } from 'vue';
import {postRegister} from "@/api/user";
const webSrc=ref('')
onLoad((option)=>{
webSrc.value =`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${option.appId}&redirect_uri=${encodeURI(option.url)}&response_type=code&scope=snsapi_base&state=123#wechat_redirect`
})
const bindLoad=(e)=>{
let code =e.detail.src.split('=')[1].split('&')[0]
let XcxId = uni.getStorageSync('XCXID')
postRegister({
XcxId,
Code: code
}).then((res :any)=> {
console.log('注册', res);
if (res.Code == 200) {
uni.setStorageSync('TokenType', res.Data.TokenType)
uni.setStorageSync('HAS_STUDENT', res.Data.user.HasStudent)
uni.setStorageSync('token', res.Data.AccessToken)
}
})
}
const receptMessage=(e :any)=>{
console.log('接受消息',e);
}
</script>
<style lang="scss" scoped>
</style>

BIN
static/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
static/education/about.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
static/education/cancel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
static/education/change.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
static/education/house.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
static/education/phone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

BIN
static/login_loading.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 B

BIN
static/null_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
static/report/back_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

BIN
static/report/book_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 B

BIN
static/report/down_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

BIN
static/report/er_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
static/report/lock_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
static/report/top_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

109
static/style/public.scss Normal file
View File

@ -0,0 +1,109 @@
page {
font-family: 'PingFang SC';
// border: 1rpx solid rgba(0,0,0,0);
background: #ffffff;
}
.page-bg {
background: #f5f5f5;
}
.flex {
display: flex;
align-items: center;
}
.bold {
font-weight: bold;
}
// 空提示
.empty_box {
height: 60%;
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
text-align: center;
position: absolute;
top: 100rpx;
left: 0;
z-index: 1;
color: #666;
image {
margin: 0 auto;
width: 140px;
height: 120px;
}
text {
margin-top: 20rpx;
font-size: 28rpx;
color: #666;
}
}
// 查询按钮
.add_btn {
padding: 64rpx 0;
width: 100%;
button {
width: 80%;
height: 70rpx;
line-height: 70rpx;
border-radius: 40rpx;
font-size: 28rpx;
background: #6b86ff;
margin-bottom: 20rpx;
&:nth-child(1) {
background: #6b86ff;
color: #fff;
&:active {
background: skyblue;
transform: scale(0.95);
}
}
&:nth-child(2) {
background: rgba(0, 0, 0, 0);
color: #6b86ff;
border: 2rpx solid #6b86ff;
}
}
}
// 继续绑定按钮
.popup_add_btn {
padding: 64rpx 0 0 0;
width: 100%;
button {
width: 80%;
height: 48rpx;
line-height: 48rpx;
border-radius: 40rpx;
font-size: 24rpx;
background: #6b86ff;
margin-bottom: 20rpx;
justify-content: center;
padding: 0;
&:nth-child(1) {
background: #6b86ff;
color: #fff;
&:active {
background: skyblue;
transform: scale(0.95);
}
}
&:nth-child(2) {
background: rgba(0, 0, 0, 0);
color: #6b86ff;
border: 2rpx solid #6b86ff;
margin-left: 20rpx;
&:active {
transform: scale(0.95);
}
}
image {
width: 32rpx;
height: 32rpx;
margin-right: 10rpx;
}
}
}
.popup_cont_des {
color: #3d425b;
font-size: 30rpx;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 B

BIN
static/tabBar/live.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 872 B

BIN
static/tabBar/material.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
static/tabBar/monitor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 910 B

BIN
static/tabBar/recording.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

13
store/index.ts Normal file
View File

@ -0,0 +1,13 @@
import { createStore } from "vuex";
export default createStore({
state: {
IsLogin:false // 是否登录
},
mutations:{
setIsLogin(state: any, data: any) {
state.IsLogin = data
}
},
actions:{},
getters:{}
});

16
tsconfig.json Normal file
View File

@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "esnext",
"useDefineForClassFields": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"types": ["@dcloudio/types"]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}

76
uni.scss Normal file
View File

@ -0,0 +1,76 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场https://ext.dcloud.net.cn上很多三方插件均使用了这些样式变量
* 如果你是插件开发者建议你使用scss预处理并在插件代码中直接使用这些变量无需 import 这个文件方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者插件使用者你可以通过修改这些变量来定制自己的插件主题实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理你也可以直接在你的 scss 代码中使用如下变量同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color:#333;//基本色
$uni-text-color-inverse:#fff;//反色
$uni-text-color-grey:#999;//辅助灰色如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable:#c0c0c0;
/* 背景颜色 */
$uni-bg-color:#ffffff;
$uni-bg-color-grey:#f8f8f8;
$uni-bg-color-hover:#f1f1f1;//点击状态颜色
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
/* 边框颜色 */
$uni-border-color:#c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm:24rpx;
$uni-font-size-base:28rpx;
$uni-font-size-lg:32rpx;
/* 图片尺寸 */
$uni-img-size-sm:40rpx;
$uni-img-size-base:52rpx;
$uni-img-size-lg:80rpx;
/* Border Radius */
$uni-border-radius-sm: 4rpx;
$uni-border-radius-base: 6rpx;
$uni-border-radius-lg: 12rpx;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 10px;
$uni-spacing-row-base: 20rpx;
$uni-spacing-row-lg: 30rpx;
/* 垂直间距 */
$uni-spacing-col-sm: 8rpx;
$uni-spacing-col-base: 16rpx;
$uni-spacing-col-lg: 24rpx;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2C405A; // 文章标题颜色
$uni-font-size-title:40rpx;
$uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle:36rpx;
$uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:30rpx;

35
utils/request.ts Normal file
View File

@ -0,0 +1,35 @@
import { config } from '@/config/config'; //引入公用文件
export const Request = options => {
return new Promise((resolve, reject) => {
const token = uni.getStorageSync('token');
const TokenType = uni.getStorageSync('TokenType');
uni.request({
url: config.host + options.url, //接口地址:前缀+方法中传入的地址
method: options.method || 'GET', //请求方法传入的方法或者默认是“GET”
data: options.data || {}, //传递参数:传入的参数或者默认传递空集合
header: {
Authorization: `${TokenType || 'Bearer'} ${token || ''}`,
'Content-Type': 'application/json;charset=utf-8'
},
success: function(res) {
if (res.statusCode == 401) {
console.log('跳转');
uni.navigateTo({url:'/pages/login/login'})
} else {
resolve(res.data);
}
},
fail: err => {
uni.showToast({
title: '请求失败,请刷新',
duration: 2000,
icon: 'none'
});
reject(err);
}
});
});
};

34
vite.config.ts Normal file
View File

@ -0,0 +1,34 @@
import { defineConfig } from "vite";
import uni from "@dcloudio/vite-plugin-uni";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [uni()],
css: {
postcss: {
plugins: [
require('postcss-pxtorpx-pro')({
// 转化的单位
unit: 'rpx',
// 单位精度
unitPrecision: 5,
// 不需要处理的css选择器
selectorBlackList: [],
// 不需要转化的css属性
propBlackList: [],
// 直接修改px还是新加一条css规则
replace: true,
// 是否匹配媒介查询的px
mediaQuery: false,
// 需要转化的最小的pixel值低于该值的px单位不做转化
minPixelValue: 2,
// 不处理的文件
exclude: /node_modules/gi,
// 转化函数
// 视口375px
transform: (x: any) => 2 * x
})
]
}
}
});