理论考试实验报告联调完成

dev_review
yujialong 3 months ago
parent 3668825847
commit 89a1a63f71
  1. 1
      src/assets/img/right.svg
  2. 1
      src/assets/img/shrink.svg
  3. 1
      src/assets/img/tag-active.svg
  4. 1
      src/assets/img/tag.svg
  5. 1
      src/assets/img/wrong.svg
  6. 50
      src/const/ques.js
  7. 37
      src/libs/util.js
  8. 42
      src/router/index.js
  9. 1
      src/utils/api.js
  10. 378
      src/views/match/manage/matchArchList.vue
  11. 914
      src/views/match/manage/theoryReport.vue
  12. 0
      src/views/match/manage/trialReport.vue

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1723792589398" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5839" width="20" height="20" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M874 150C674.09-50 349.91-50 150 150s-200 524.09 0 724 524.09 200 724 0 200-524.09 0-724zM760.57 440.57l-256 256a80 80 0 0 1-113.14 0l-128-128a80 80 0 0 1 113.14-113.14L448 526.86l199.43-199.43a80 80 0 0 1 113.14 113.14z" fill="#2abd8c" p-id="5840"></path></svg>

After

Width:  |  Height:  |  Size: 593 B

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1723535697263" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5903" width="18" height="18" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M877.5 565.9l-367.6-340-367.6 340c-22.5 14.5-52.5 14.5-67.5 0-15-21.7-15-50.7 0-65.1L495 117.5c0-7.2 7.5-7.2 15-7.2s7.5 0 15 7.2l420.2 383.3c22.5 21.7 15 50.6 0 65.1-22.7 14.5-52.7 14.5-67.7 0zM494.8 450.1c0-7.2 7.5-7.2 15-7.2s15 0 15 7.2L945 833.4c22.5 21.7 15 50.6 0 65.1-22.5 14.4-52.5 14.4-67.5 0L509.8 558.6 142.2 905.8c-22.5 14.5-52.5 14.5-67.5 0-15-21.7-15-50.7 0-65.1l420.1-390.6z m0 0" fill="#666666" p-id="5904"></path></svg>

After

Width:  |  Height:  |  Size: 766 B

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1723619200843" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7128" width="18" height="18" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M695.7 118.6H326.9c-58.2 0-105.4 47.2-105.4 105.4v579.6c0 58.2 47.2 105.4 105.4 105.4l184.4-184.4L695.7 909c58.2 0 105.4-47.2 105.4-105.4V224c0-58.2-47.1-105.4-105.4-105.4z" fill="#d81e06" p-id="7129"></path></svg>

After

Width:  |  Height:  |  Size: 545 B

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1723619200843" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7128" width="18" height="18" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M695.7 118.6H326.9c-58.2 0-105.4 47.2-105.4 105.4v579.6c0 58.2 47.2 105.4 105.4 105.4l184.4-184.4L695.7 909c58.2 0 105.4-47.2 105.4-105.4V224c0-58.2-47.1-105.4-105.4-105.4z" fill="#bfbfbf" p-id="7129"></path></svg>

After

Width:  |  Height:  |  Size: 545 B

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1723792667165" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7415" width="20" height="20" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M480 64C217.6 64 0 281.6 0 544s217.6 480 480 480 480-217.6 480-480S742.4 64 480 64z m204.8 614.4c19.2 19.2 19.2 44.8 0 64-19.2 19.2-44.8 19.2-64 0L486.4 608 345.6 748.8c-19.2 19.2-51.2 19.2-70.4 0-19.2-19.2-19.2-51.2 0-70.4L416 537.6 281.6 403.2c-19.2-19.2-19.2-44.8 0-64 19.2-19.2 44.8-19.2 64 0L480 473.6l140.8-140.8c19.2-19.2 51.2-19.2 70.4 0 19.2 19.2 19.2 51.2 0 70.4L550.4 544l134.4 134.4z" fill="#e75050" p-id="7416"></path></svg>

After

Width:  |  Height:  |  Size: 768 B

@ -0,0 +1,50 @@
export default {
difficults: [
{
id: 'basic',
name: '基础',
coefficient: 0.2, // 难度系数,试卷里计算试卷难度专用
theme: 'success',
},
{
id: 'easy',
name: '普通',
coefficient: 0.4,
theme: '',
},
{
id: 'medium',
name: '较难',
coefficient: 0.6,
theme: 'warning',
},
{
id: 'hard',
name: '难',
coefficient: 0.8,
theme: 'danger',
},
],
questionTypes: [
{
id: 'single_choice',
name: '单选题'
},
{
id: 'multiple_choice',
name: '多选题'
},
{
id: 'judgement',
name: '判断题'
},
{
id: 'fill_blank',
name: '填空题'
},
{
id: 'essay',
name: '问答题'
},
],
}

@ -172,6 +172,43 @@ const util = {
resolve(new Date(data.currentTime))
})
},
// 阿拉伯数字转化为中文数字
arabicToChinese (num) {
const arr1 = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
const arr2 = ['', '十', '百', '千', '万', '亿', '点', ''];
const a = `${num}`.replace(/(^0*)/g, '').split('.');
let k = 0;
let re = '';
for (let i = a[0].length - 1; i >= 0; i--) {
switch (k) {
case 0:
re = arr2[7] + re;
break;
case 4:
if (!new RegExp(`0{4}//d{${a[0].length - i - 1}}$`).test(a[0])) re = arr2[4] + re;
break;
case 8:
re = arr2[5] + re;
arr2[7] = arr2[5];
k = 0;
break;
default:
}
if (k % 4 == 2 && a[0].charAt(i + 2) != 0 && a[0].charAt(i + 1) == 0) re = arr1[0] + re;
if (a[0].charAt(i) != 0) re = arr1[a[0].charAt(i)] + arr2[k % 4] + re;
k++;
}
return num > 9 && num < 20 ? re.slice(1) : re;
},
// 阿拉伯数字转化为英文字母
numToLetter (num) {
let result = ''
if (num > 26) {
result += numberToLetter((num / 26) >> 0 - 1)
}
result += String.fromCharCode(65 + (num % 26))
return result
},
}
export default util

@ -19,143 +19,119 @@ let router = new Router({
{
path: '/customer',
component: () => import('../views/customer/customer.vue'),
// meta: { title: '客户管理' }
},
{
path: '/addcustomer',
component: () => import('../views/customer/AddCustomer.vue'),
// meta: { title: '新增客户' }
},
{
path: '/bmOrder',
component: () => import('../views/customer/Order.vue'),
// meta: { title: '新增客户' }
},
{
path: '/user',
component: () => import('../views/user/User.vue'),
// meta: { title: '用户管理' }
},
{
path: '/adduser',
component: () => import('../views/user/AddUser.vue'),
// meta: { title: '新增用户' }
},
{
path: '/info',
component: () => import('../views/user/Info.vue'),
// meta: { title: '新增用户' }
},
{
path: '/order',
component: () => import('../views/order/Order.vue'),
// meta: { title: '订单管理' }
},
{
path: '/addorder',
component: () => import('../views/order/AddOrder.vue'),
// meta: { title: '新增订单' }
},
{
path: '/selectClient',
component: () => import('../views/order/selectClient.vue'),
// meta: { title: '选择订单客户' }
},
{
path: '/configure',
component: () => import('../views/serve/Configure.vue'),
// meta: { title: '服务配置' }
},
{
path: '/backstage',
component: () => import('../views/serve/backstage'),
// meta: { title: '服务配置' }
},
{
path: '/addModel',
component: () => import('../views/serve/addModel'),
// meta: { title: '服务配置' }
},
{
path: '/addconfigure',
component: () => import('../views/serve/AddConfigure.vue'),
// meta: { title: '新增配置' }
},
{
path: '/projectList',
component: () => import('../views/serve/projectList.vue'),
// meta: { title: '项目管理' }
},
{
path: '/projectAdd',
component: () => import('../views/serve/projectAdd.vue'),
// meta: { title: '项目配置' }
},
{
path: '/curriculum',
component: () => import('../views/course/Curriculum.vue'),
// meta: { title: '课程管理' }
},
{
path: '/addcurriculum',
component: () => import('../views/course/AddCurriculum.vue'),
// meta: { title: '新建课程' }
},
{
path: '/contentSettings',
component: () => import('../views/course/contentSettings.vue'),
// meta: { title: '内容设置' }
},
{
path: '/addlink',
component: () => import('../views/course/AddLink.vue'),
// meta: { title: '添加环节' }
},
{
path: '/data',
component: () => import('../views/data/Data.vue'),
// meta: { title: '数据管理' }
},
{
path: '/match',
component: () => import('../views/match/list'),
// meta: { title: '数据管理' }
},
{
path: '/addMatch',
component: () => import('../views/match/add'),
// meta: { title: '数据管理' }
},
{
path: '/matchManage',
component: () => import('../views/match/manage'),
// meta: { title: '数据管理' }
},
{
path: '/noticeDetail',
component: () => import('../views/match/manage/noticeDetail'),
// meta: { title: '数据管理' }
},
{
path: '/matchArchList',
component: () => import('../views/match/manage/matchArchList'),
// meta: { title: '数据管理' }
},
{
path: '/matchRank',
component: () => import('../views/match/manage/matchRank'),
// meta: { title: '数据管理' }
},
{
path: '/matchReport',
component: () => import('../views/match/manage/matchReport'),
// meta: { title: '数据管理' }
path: '/trialReport',
component: () => import('../views/match/manage/trialReport'),
},
{
path: '/theoryReport',
component: () => import('../views/match/manage/theoryReport'),
},
{
path: '/matchInfo',
component: () => import('../views/match/manage/matchInfo'),
// meta: { title: '数据管理' }
},
{
path: `/theoreticalCourse`,
@ -190,32 +166,26 @@ let router = new Router({
{
path: '/system',
component: () => import('../views/system'),
// meta: { title: '数据管理' }
},
{
path: '/manageLog',
component: () => import('../views/system/manageLog'),
// meta: { title: '数据管理' }
},
{
path: '/addLog',
component: () => import('../views/system/addLog'),
// meta: { title: '数据管理' }
},
{
path: '/permission',
component: () => import('../views/customer/Permission.vue'),
// meta: { title: '应用权限', permission: true }
},
{
path: '/person',
component: () => import('../views/setting'),
// meta: { title: '个人中心' }
},
{
path: '/parner',
component: () => import('../views/parner'),
// meta: { title: '个人中心' }
},
{
path: '/parnerOperation',

@ -344,6 +344,7 @@ export default {
libraryList: `exam/exam/paperLibrary/libraryList`,
examPaperList: `exam/exam/paper/examPaperList`,
copyExamPaper: `exam/exam/paper/copyExamPaper`,
getDetailedExamScores: `exam/exam/paper/getDetailedExamScores`,
// 赛事内容
addCompetitionContent: `competition/competition/content/addCompetitionContent`,

@ -1,289 +1,166 @@
<template>
<div>
<el-card shadow="hover"
class="m-b-20 head-card">
<el-card shadow="hover" class="m-b-20 head-card">
<div class="flex-between m-b-20">
<el-page-header @back="back"
content="成绩管理"></el-page-header>
<el-page-header @back="back" content="成绩管理"></el-page-header>
</div>
</el-card>
<div v-loading="loading">
<el-card v-if="method != 2"
shadow="hover"
class="m-b-20">
<el-card v-if="method != 2" shadow="hover" class="m-b-20">
<div class="stat">
<div class="nums">
<div class="item">
<p class="name">已参加/应参加人数</p>
<p class="val">{{ isNaN(statData.totalNumber) ? '' : statData.attendance + '/' + statData.totalNumber }}</p>
<p class="val">{{ isNaN(statData.totalNumber) ? '' : statData.attendance + '/' + statData.totalNumber }}
</p>
</div>
<div class="item">
<p class="name">实验平均分</p>
<p class="val">{{ avgScore }}</p>
</div>
</div>
<div class="chart"
id="chart"></div>
<div class="chart" id="chart"></div>
</div>
</el-card>
<el-card shadow="hover"
class="m-b-20">
<div v-if="showFile"
class="tabs m-b-20">
<a class="item"
v-for="(item, i) in tabs"
:key="i"
:class="{active: i === active}"
@click="tabChange(i)">{{ item }}</a>
<el-card shadow="hover" class="m-b-20">
<div v-if="showFile" class="tabs m-b-20">
<a class="item" v-for="(item, i) in tabs" :key="i" :class="{ active: i === active }" @click="tabChange(i)">{{
item }}</a>
</div>
<div class="flex-between m-b-20">
<div>
<el-input size="small"
placeholder="请输入学校/学生姓名"
prefix-icon="el-icon-search"
v-model="keyword"
clearable
style="width: 300px"></el-input>
<el-input size="small" placeholder="请输入学校/学生姓名" prefix-icon="el-icon-search" v-model="keyword" clearable
style="width: 300px"></el-input>
</div>
<div v-if="!active">
<el-button v-if="method == 2"
type="primary"
@click="batchImport">上传成绩</el-button>
<el-button type="primary"
:disabled="!!multipleSelection.find(e => method != 2 && !e.reportId)"
@click="delAllData">批量删除</el-button>
<el-button type="primary"
:loading="exporting"
@click="exportData">{{ exporting ? '正在导出' : '批量导出' }}</el-button>
<el-button v-if="method == 2" type="primary" @click="batchImport">上传成绩</el-button>
<el-button type="primary" :disabled="!!multipleSelection.find(e => method != 2 && !e.reportId)"
@click="delAllData">批量删除</el-button>
<el-button type="primary" :loading="exporting" @click="exportData">{{ exporting ? '正在导出' : '批量导出'
}}</el-button>
</div>
<div v-else>
<el-button type="primary"
:loading="exporting1"
@click="exportData1">{{ exporting1 ? '正在导出' : '批量导出' }}</el-button>
<el-button type="primary" :loading="exporting1" @click="exportData1">{{ exporting1 ? '正在导出' : '批量导出'
}}</el-button>
</div>
</div>
<template v-if="!active">
<el-table :data="list"
class="table"
:key="1"
ref="table"
stripe
header-align="center"
@selection-change="handleSelectionChange"
row-key="id">
<el-table-column type="selection"
width="55"
align="center"
:reserve-selection="true"></el-table-column>
<el-table-column type="index"
width="60"
label="序号"
align="center">
<el-table :data="list" class="table" :key="1" ref="table" stripe header-align="center"
@selection-change="handleSelectionChange" row-key="id">
<el-table-column type="selection" width="55" align="center" :reserve-selection="true"></el-table-column>
<el-table-column type="index" width="60" label="序号" align="center">
<template slot-scope="scope">
{{ scope.$index + (page - 1) * pageSize + 1 }}
</template>
</el-table-column>
<el-table-column prop="schoolName"
label="学生账号归属"
min-width="100"
align="center"></el-table-column>
<el-table-column prop="realSchool"
label="学生所在院校"
min-width="100"
align="center"></el-table-column>
<el-table-column v-if="competitionType"
prop="teamName"
label="团队名称"
min-width="100"
align="center"></el-table-column>
<el-table-column prop="userName"
label="学生姓名"
min-width="100"
align="center"></el-table-column>
<el-table-column prop="workNumber"
label="学号"
min-width="100"
align="center"></el-table-column>
<el-table-column prop="score"
label="分数"
width="90"
align="center">
<el-table-column prop="schoolName" label="学生账号归属" min-width="100" align="center"></el-table-column>
<el-table-column prop="realSchool" label="学生所在院校" min-width="100" align="center"></el-table-column>
<el-table-column v-if="competitionType" prop="teamName" label="团队名称" min-width="100"
align="center"></el-table-column>
<el-table-column prop="userName" label="学生姓名" min-width="100" align="center"></el-table-column>
<el-table-column prop="workNumber" label="学号" min-width="100" align="center"></el-table-column>
<el-table-column prop="score" label="分数" width="90" align="center">
<template slot-scope="scope">
{{ scope.row.submitTime ? scope.row.score : '--' }}
</template>
</el-table-column>
<el-table-column prop="timeSum"
label="耗时"
width="90"
align="center">
<el-table-column prop="timeSum" label="耗时" width="90" align="center">
<template slot-scope="scope">
{{ scope.row.timeSum ? scope.row.timeSum + 'min' : '--' }}
</template>
</el-table-column>
<el-table-column prop="submitTime"
label="提交时间"
min-width="150"
align="center">
<el-table-column prop="submitTime" label="提交时间" min-width="150" align="center">
<template slot-scope="scope">
{{ scope.row.submitTime || '--' }}
</template>
</el-table-column>
<el-table-column label="状态"
width="100"
align="center">
<el-table-column label="状态" width="100" align="center">
<template slot-scope="scope">
{{ scope.row.reportId || method == 2 ? '已参加' : '未参加' }}
</template>
</el-table-column>
<el-table-column label="操作"
align="center"
width="160">
<el-table-column label="操作" align="center" width="160">
<template slot-scope="scope">
<el-button v-if="method != 2 && scope.row.reportId"
type="text"
@click="show(scope.row)">查看成绩报告</el-button>
<el-button v-if="scope.row.reportId"
type="text"
@click="handleDelete(scope.row)">删除</el-button>
<el-button v-if="method != 2 && scope.row.reportId" type="text"
@click="show(scope.row)">查看成绩报告</el-button>
<el-button v-if="scope.row.reportId" type="text" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination background
layout="total, prev, pager, next"
:total="total"
@current-change="handleCurrentChange"
:current-page="page">
<el-pagination background layout="total, prev, pager, next" :total="total"
@current-change="handleCurrentChange" :current-page="page">
</el-pagination>
</div>
</template>
<template v-else>
<el-table :data="list1"
class="table"
:key="2"
stripe
header-align="center"
@selection-change="handleSelectionChange1"
row-key="id">
<el-table-column type="selection"
width="55"
align="center"
:reserve-selection="true"></el-table-column>
<el-table-column type="index"
width="60"
label="序号"
align="center">
<el-table :data="list1" class="table" :key="2" stripe header-align="center"
@selection-change="handleSelectionChange1" row-key="id">
<el-table-column type="selection" width="55" align="center" :reserve-selection="true"></el-table-column>
<el-table-column type="index" width="60" label="序号" align="center">
<template slot-scope="scope">
{{ scope.$index + (page1 - 1) * pageSize + 1 }}
</template>
</el-table-column>
<el-table-column prop="schoolName"
label="学生账号归属"
align="center"></el-table-column>
<el-table-column prop="realSchool"
label="学生所在院校"
align="center"></el-table-column>
<el-table-column v-if="competitionType"
prop="teamName"
label="团队名称"
align="center"></el-table-column>
<el-table-column prop="userName"
label="学生姓名"
align="center"></el-table-column>
<el-table-column prop="workNumber"
label="学号"
align="center"></el-table-column>
<el-table-column prop="fileName"
label="文件名"
align="center"></el-table-column>
<el-table-column prop="fileSize"
label="文件大小"
align="center"></el-table-column>
<el-table-column prop="fileType"
label="文件类型"
align="center"></el-table-column>
<el-table-column prop="fileFormat"
label="文件格式"
align="center"></el-table-column>
<el-table-column prop="createTime"
label="提交时间"
width="150"
align="center">
<el-table-column prop="schoolName" label="学生账号归属" align="center"></el-table-column>
<el-table-column prop="realSchool" label="学生所在院校" align="center"></el-table-column>
<el-table-column v-if="competitionType" prop="teamName" label="团队名称" align="center"></el-table-column>
<el-table-column prop="userName" label="学生姓名" align="center"></el-table-column>
<el-table-column prop="workNumber" label="学号" align="center"></el-table-column>
<el-table-column prop="fileName" label="文件名" align="center"></el-table-column>
<el-table-column prop="fileSize" label="文件大小" align="center"></el-table-column>
<el-table-column prop="fileType" label="文件类型" align="center"></el-table-column>
<el-table-column prop="fileFormat" label="文件格式" align="center"></el-table-column>
<el-table-column prop="createTime" label="提交时间" width="150" align="center">
</el-table-column>
<el-table-column label="操作"
width="200">
<el-table-column label="操作" width="200">
<template slot-scope="scope">
<el-button v-if="!isCompress(scope.row.fileFormat)"
type="text"
@click="preview(scope.row)">预览文件</el-button>
<el-button type="primary"
size="mini"
:loading="scope.row.loading"
@click="exportFile(scope.row)">导出文件</el-button>
<el-button v-if="!isCompress(scope.row.fileFormat)" type="text"
@click="preview(scope.row)">预览文件</el-button>
<el-button type="primary" size="mini" :loading="scope.row.loading"
@click="exportFile(scope.row)">导出文件</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination background
layout="total, prev, pager, next"
:total="total1"
@current-change="handleCurrentChange1"
:current-page="page1">
<el-pagination background layout="total, prev, pager, next" :total="total1"
@current-change="handleCurrentChange1" :current-page="page1">
</el-pagination>
</div>
</template>
</el-card>
</div>
<el-dialog title="批量导入"
:visible.sync="importVisible"
width="24%"
:close-on-click-modal="false"
@close="cancelUpload">
<el-dialog title="批量导入" :visible.sync="importVisible" width="24%" :close-on-click-modal="false"
@close="cancelUpload">
<div style="text-align: center">
<template v-if="!uploadFaild">
<div style="margin-bottom: 10px;">
<el-button type="primary"
@click="download">模板下载<i class="el-icon-download el-icon--right"></i></el-button>
<el-button type="primary" @click="download">模板下载<i class="el-icon-download el-icon--right"></i></el-button>
</div>
<el-upload ref="upload"
name="file"
accept=".xls,.xlsx"
class="import-file"
:before-upload="beforeUpload"
:on-remove="handleRemove"
:on-error="uploadError"
:on-success="uploadSuccess"
:before-remove="beforeRemove"
:limit="1"
:on-exceed="handleExceed"
:action="this.api.batchImportGrades"
:file-list="uploadList"
:headers="headers"
:disabled="uploading"
:data="{
competitionId: this.id,
stageId: this.stageId,
systemId: 0
}">
<el-button type="primary"
:loading="uploading"
class="ml20">上传文件<i class="el-icon-upload2 el-icon--right"></i></el-button>
<el-upload ref="upload" name="file" accept=".xls,.xlsx" class="import-file" :before-upload="beforeUpload"
:on-remove="handleRemove" :on-error="uploadError" :on-success="uploadSuccess" :before-remove="beforeRemove"
:limit="1" :on-exceed="handleExceed" :action="this.api.batchImportGrades" :file-list="uploadList"
:headers="headers" :disabled="uploading" :data="{
competitionId: this.id,
stageId: this.stageId,
systemId: 0
}">
<el-button type="primary" :loading="uploading" class="ml20">上传文件<i
class="el-icon-upload2 el-icon--right"></i></el-button>
</el-upload>
</template>
<template v-else>
<p style="margin: -10px 0 13px;font-size: 14px;color: #e90000;">{{ faildData.tip }}</p>
<p type="primary"
style="margin-bottom: 10px;font-size: 14px;color: #9076FF;text-decoration: underline;cursor: pointer;"
@click="showFaild">部分数据导入失败查看失败原因</p>
style="margin-bottom: 10px;font-size: 14px;color: #9076FF;text-decoration: underline;cursor: pointer;"
@click="showFaild">部分数据导入失败查看失败原因</p>
</template>
</div>
<span v-if="uploading"
slot="footer"
class="dialog-footer">
<span v-if="uploading" slot="footer" class="dialog-footer">
<el-button @click="cancelUpload">停止导入</el-button>
</span>
</el-dialog>
@ -407,7 +284,7 @@ export default {
},
//
show (row) {
this.$router.push(`/matchReport?reportId=${row.reportId}`)
this.$router.push(`/${row.paperId ? 'theoryReport' : 'trialReport'}?reportId=${row.reportId}`)
},
// ()
async exportData () {
@ -646,72 +523,73 @@ export default {
<style lang="scss" scoped>
/deep/ .head-card {
.el-card__body {
padding-bottom: 0px;
.el-card__body {
padding-bottom: 0px;
.el-tabs__header {
margin-bottom: 1px;
.el-tabs__header {
margin-bottom: 1px;
.el-tabs__nav-wrap::after {
display: none;
}
.el-tabs__nav-wrap::after {
display: none;
}
.el-tabs__item {
font-size: 18px;
}
}
.el-tabs__item {
font-size: 18px;
}
}
}
}
.stat {
display: flex;
display: flex;
.nums {
display: flex;
align-items: center;
margin-right: 20px;
.nums {
display: flex;
align-items: center;
margin-right: 20px;
.item:nth-child(1) {
background-image: url('../../../assets/img/total.png');
}
.item:nth-child(1) {
background-image: url('../../../assets/img/total.png');
}
.item:nth-child(2) {
background-image: url('../../../assets/img/avg.png');
}
.item:nth-child(2) {
background-image: url('../../../assets/img/avg.png');
}
.item {
width: 300px;
min-height: 145px;
padding: 30px 30px;
margin: 0 10px;
box-sizing: border-box;
border-radius: 8px;
background-size: 100% 100%;
background-repeat: no-repeat;
.item {
width: 300px;
min-height: 145px;
padding: 30px 30px;
margin: 0 10px;
box-sizing: border-box;
border-radius: 8px;
background-size: 100% 100%;
background-repeat: no-repeat;
p {
font-size: 18px;
color: #ffffff;
}
p {
font-size: 18px;
color: #ffffff;
}
.val {
margin-top: 10px;
color: #ffffff;
font-size: 36px;
}
}
.val {
margin-top: 10px;
color: #ffffff;
font-size: 36px;
}
}
}
.chart {
flex: 1;
height: 300px;
}
.chart {
flex: 1;
height: 300px;
}
}
/deep/.import-file {
.el-progress__text,
.el-progress,
.el-upload-list__item-status-label {
display: none !important;
}
.el-progress__text,
.el-progress,
.el-upload-list__item-status-label {
display: none !important;
}
}
</style>

@ -0,0 +1,914 @@
<template>
<div class="wrap">
<el-card shadow="hover" class="m-b-20">
<el-page-header @back="$router.back()" content="查看报告"></el-page-header>
</el-card>
<div class="report" v-loading="loading">
<div class="left">
<h6 class="title">答题卡</h6>
<div v-if="paper" class="type-wrap">
<template v-for="(item, i) in paper">
<div
v-if="item.userAnswerList.length && (!sheetStatus || item.userAnswerList.some(e => e.isCorrect === sheetStatus))"
:key="i" class="type">
<h6 class="stem">{{ arabicToChinese(i + 1) }}{{ item.outlineName }}本题共{{ item.questionNum }}小题{{
item.targetScore }}</h6>
<ul class="serials">
<template v-for="(ques, j) in item.userAnswerList">
<li v-if="!sheetStatus || sheetStatus === ques.isCorrect" :key="j" :class="'status' + ques.isCorrect">
<p :class="['serial', { answered: ques.answered, partAnswer: ques.partAnswer }]">{{ j + 1 }}</p>
<p class="score">{{ ques.userScore }}</p>
</li>
</template>
</ul>
</div>
</template>
</div>
<ul class="status-filter">
<li v-for="(item, i) in statusList" :key="i" :class="{ active: sheetStatus === item.id }"
@click="filterStatus(item.id)">{{ item.name }}</li>
</ul>
</div>
<div class="right">
<div class="text-right">
<el-button type="primary" @click="exportPage">导出报告</el-button>
</div>
<h6 class="r-title">标准实验报告</h6>
<div class="info">
<h6 class="l-title">
<img src="@/assets/img/info1.png" alt="">
基本信息
</h6>
<ul :class="['info-list', { edit: editing }]">
<li>
<label>学生姓名</label>
<el-input v-if="editing" v-model="info.userName" disabled></el-input>
<span v-else>{{ info.userName }}</span>
</li>
<li>
<label>学生学号</label>
<el-input v-if="editing" v-model="info.workNumber" disabled></el-input>
<span v-else>{{ info.workNumber }}</span>
</li>
<li>
<label>学生班级</label>
<el-input v-if="editing" v-model="info.className"></el-input>
<span v-else>{{ info.className }}</span>
</li>
<li>
<label>成绩</label>
<el-input v-if="editing" v-model="info.score" disabled></el-input>
<div v-else class="score-wrap">
<em>{{ info.score }}</em>
<img src="@/assets/img/point.png" alt="">
<p v-if="essayExist" class="exist">部分试题待判分成绩待定</p>
</div>
</li>
<li>
<label>提交时间</label>
<el-input v-if="editing" v-model="info.submitTime" disabled></el-input>
<span v-else>{{ info.submitTime }}</span>
</li>
<li>
<label>用时</label>
<el-input v-if="editing" v-model="info.timeSum"></el-input>
<span v-else>{{ info.timeSum }}</span>
</li>
<li>
<label>指导老师</label>
<el-input v-if="editing" v-model="info.instructor"></el-input>
<span v-else>{{ info.instructor }}</span>
</li>
</ul>
<div class="m-b-20">
<h6 class="l-title">
<img src="@/assets/img/report4.png" alt="">
得分情况
</h6>
<el-table :data="paper" class="table" border stripe header-align="center">
<el-table-column prop="outlineName" label="大题名称" align="center"></el-table-column>
<el-table-column prop="questionNum" label="小题总数" align="center"></el-table-column>
<el-table-column prop="targetScore" label="总分" align="center"></el-table-column>
<el-table-column prop="userTotalScore" label="得分" align="center">
<template slot-scope="scope">
<p v-if="scope.row.questionType === 'essay'" class="text-red">待评分</p>
<p v-else>{{ scope.row.userTotalScore }}</p>
</template>
</el-table-column>
<el-table-column prop="scoreRatePercentage" label="得分率" align="center"></el-table-column>
</el-table>
</div>
<ul v-if="paper" class="ques-wrap">
<li v-for="(item, i) in paper" :key="i">
<div class="outline">
{{ arabicToChinese(i + 1) }}{{ item.questionTypeName }}本题共{{ item.questionNum }}小题{{
item.targetScore }}
<img :class="['shrink', { active: item.shrink }]" src="@/assets/img/shrink.svg" alt=""
@click="item.shrink = !item.shrink">
</div>
<div :class="['ques', { hide: item.shrink }]">
<div v-for="(ques, j) in item.userAnswerList" :key="j" class="item">
<div class="stem-wrap">
<div class="labels">
<span class="label">{{ j + 1 }} / {{ item.questionNum }}</span>
<span class="label">{{ item.questionTypeName }}</span>
</div>
<el-tag class="m-r-5" :type="ques.difficultTheme">{{ ques.difficult }}</el-tag>
<div class="stem" :id="'stem' + ques.id" v-html="getQuesStem(ques)"></div>
<p>{{ ques.questionScore }}</p>
</div>
<div
v-if="item.questionType !== 'fill_blank' && item.questionType !== 'essay' && ques.questionAnswerVersionsList"
class="m-b-10">
<div v-for="(opt, j) in ques.questionAnswerVersionsList" :key="j" class="opt">
<el-checkbox v-if="item.questionType === 'multiple_choice'" v-model="opt.answerIsCorrect"
:true-label="1"></el-checkbox>
<el-radio v-else v-model="opt.answerIsCorrect" :true-label="1" :label="1" disabled></el-radio>
<span>{{ numToLetter(j) }}.&nbsp;</span>
<div class="text" v-html="opt.optionText"></div>
</div>
</div>
<template v-if="item.questionType === 'essay'">
<div v-if="ques.stemAttachment" class="m-b-10">
<el-link class="m-r-10" type="primary">{{ ques.stemAttachment }}</el-link>
<el-button type="primary" size="mini" round @click="download(ques.stemAttachment)">下载</el-button>
</div>
<div v-if="ques.uploadInstructions" class="line m-b-10">
<span>考生上传附件说明</span>
<div v-html="ques.uploadInstructions"></div>
</div>
</template>
<div class="m-b-10">
<span>知识点</span>
<el-tag v-for="(kp, k) in ques.kpList" :key="k" class="m-r-5">{{ kp }}</el-tag>
</div>
<div class="flex m-b-10">
<span>解析</span>
<div
v-if="ques.questionAnswerVersionsList.length && ques.questionAnswerVersionsList[0].answerAnalysis"
v-html="ques.questionAnswerVersionsList[0].answerAnalysis"></div>
<div v-else>暂无解析</div>
</div>
<div :class="['ques-info', { essay: item.questionType === 'essay' }]">
<template v-if="item.questionType === 'essay'">
<div class="line">
<span>参考答案</span>
<div v-html="ques.questionAnswerVersionsList[0].referenceAnswer"></div>
</div>
<div class="line">
<span>考生答案</span>
<div v-html="ques.answerContent"></div>
</div>
<div v-if="ques.attachmentUrl" class="line">
<span>考生上传附件</span>
<el-link class="m-r-10" type="primary">{{ ques.attachmentName }}</el-link>
<el-button type="primary" size="mini" round @click="download(ques)">下载</el-button>
</div>
</template>
<template v-else>
<div class="line">正确答案{{ ques.quesAnswer }}</div>
<div class="line">
<span>考生答案</span>
<p v-if="item.questionType !== 'fill_blank'">{{ ques.userAnswerStr }}</p>
<div v-else-if="ques.userAnswerFill" class="fill-answers">
<p v-for="(ans, j) in ques.userAnswerFill" :key="j" class="fill-answer">
填空{{ j + 1 }}{{ ans.studentAnswer }}
<img v-if="ans.correct" src="@/assets/img/right.svg" alt="" class="icon">
<img v-else src="@/assets/img/wrong.svg" alt="" class="icon">
</p>
</div>
</div>
</template>
<div class="line">题目分值{{ ques.questionScore }}</div>
<div class="line">考生得分<el-input class="score-input" size="small" :value="ques.userScore"
disabled />
</div>
</div>
</div>
</div>
</li>
</ul>
<div class="m-b-20">
<h6 class="l-title">
<img src="@/assets/img/report5.png" alt="">
考试总结与体会
</h6>
<quill v-if="editing" :border="true" v-model="form.summarize" :minHeight="150" :height="150" />
<div v-else class="pre-wrap" v-html="form.summarize"></div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import QuesConst from '@/const/ques'
import TestPaperConst from '@/const/testPaper'
import Util from '@/libs/util'
export default {
data () {
return {
numToLetter: Util.numToLetter,
arabicToChinese: Util.arabicToChinese,
reportId: this.$route.query.reportId,
title: "实验报告",
form: {
className: "",
instructor: "",
period: "",
projectName: "",
summarize: "",
},
info: {},
outline: [],
editing: false,
loadIns: null,
loading: false,
sheetStatus: '',
statusList: [
{
id: 1,
name: '正确'
},
{
id: 2,
name: '错误'
},
{
id: 3,
name: '部分正确'
},
{
id: 4,
name: '待判分'
},
],
paper: [],
essayExist: 0,
};
},
mounted () {
this.getData()
},
methods: {
async getData () {
this.loading = true
try {
const { userAnswers: paper, report } = await this.$get(`${this.api.getDetailedExamScores}?reportId=${this.reportId}`)
this.form = report
//
const { questionTypes: types, difficults } = QuesConst
const { numToLetter } = Util
let essayExist = 0
paper.map(e => {
if (e.questionType === 'essay') essayExist = 1
e.shrink = false
const type = e.questionType
e.questionTypeName = types.find(n => n.id === type).name
e.userAnswerList && e.userAnswerList.map(n => {
if (n.difficulty) {
const curDiff = difficults.find(m => m.id === n.difficulty)
if (curDiff) {
n.difficult = curDiff.name
n.difficultTheme = curDiff.theme
}
}
n.kpList = n.knowledgePointName.split(',')
const opts = n.questionAnswerVersionsList
if (type !== 'fill_blank' && type !== 'essay') { //
n.isCorrect = n.userScore && n.userScore === n.questionScore ? 1 : 2
const quesAnswer = []
opts && opts.map((m, j) => {
m.answerIsCorrect && quesAnswer.push(numToLetter(j))
})
n.quesAnswer = quesAnswer.join('')
let { userAnswer } = n
if (userAnswer) {
userAnswer = JSON.parse(userAnswer)
n.userAnswerStr = userAnswer.map(m => numToLetter(+m - 1)).join('')
}
} else if (type === 'fill_blank') { //
//
let { jsonText } = n
if (jsonText) {
n.userAnswerFill = JSON.parse(jsonText)
}
//
if (opts && opts.length) {
let { answerData } = opts[0]
if (answerData) {
answerData = JSON.parse(answerData)
let quesAnswer = ''
answerData.map((m, j) => {
if (m.fills) {
quesAnswer += `填空${j + 1}${m.fills.map(n => n.val).join(' ||| ')}`
}
})
n.quesAnswer = quesAnswer
}
}
//
let rightLen = 0
if (n.userAnswerFill) rightLen = n.userAnswerFill.filter(m => m.correct).length //
n.isCorrect = n.userScore && n.questionScore === n.userScore ? 1 : (rightLen ? 3 : 2)
} else if (type === 'essay') { //
n.isCorrect = 4 //
}
})
})
this.essayExist = essayExist //
this.paper = paper
let { form } = this
this.info = {
workNumber: form.workNumber,
timeSum: form.timeSum,
instructor: form.instructor,
submitTime: form.submitTime,
score: form.score,
className: form.className,
userName: form.userName
}
this.loading = false
} catch (e) {
this.loading = false
}
},
//
filterStatus (e) {
this.sheetStatus = this.sheetStatus === e ? '' : e
},
//
getQuesStem (ques) {
let { stem } = ques
if (ques.questionType === 'fill_blank') { //
return stem
let { fills } = ques
const regex = /<span class="gapfilling-span" data-id="(.*?)">______<\/span>/g
let match
let index = 0 //
let result = stem
while ((match = regex.exec(stem)) !== null) {
const newInput = `______`
result = result.replace(match[0], newInput)
index++
}
return result
} else {
return stem
}
},
//
download (ques) {
Util.downloadFile(ques.attachmentName, ques.attachmentUrl)
},
//
exportPage () {
const form = Object.assign(this.form, this.info)
const list = JSON.parse(JSON.stringify(this.expData))
list.map((e, i) => {
const item = this.userScores.find(n => n.judgmentId == e.judgmentId)
if (item && item.runThePicture) e.runThePicture = item.runThePicture
if (item && item.runThePictureList) e.runThePictureList = item.runThePictureList
e.id = i + 1
// if (e.referenceAnswer && typeof e.referenceAnswer === 'string') e.referenceAnswer = e.referenceAnswer.replace(/<[^>]+>/g, '').replace(/(&nbsp;|&amp;|%s)/g, '').replace(/>/g, '&gt;').replace(/</g, '&lt;')
if (e.answer && typeof e.answer === 'string') e.answer = e.answer.replace(/<[^>]+>/g, '').replace(/(&nbsp;|&amp;|%s)/g, '').replace(/>/g, '&gt;').replace(/</g, '&lt;')
})
for (const i in form) {
if (form[i] && typeof form[i] === 'string') form[i] = form[i].replace(/<[^>]+>/g, '')
}
form.purpose = form.purpose.replace(/<[^>]+>/g, '')
this.$post(this.project ? this.api.exportBankExperimentReport : this.api.exportLabReport, {
...form,
experimentalData: list
}).then(res => {
console.log(res)
Util.downloadFileDirect(`实验报告.docx`, new Blob([res]))
}).catch(res => { })
},
}
};
</script>
<style lang="scss" scoped>
.wrap {
padding: 10px;
}
.text-right {
text-align: right;
}
.text-red {
color: #f00;
}
code,
kbd,
samp {
font-family: 'PingFang SC', 'Helvetica Neue', Helvetica, 'microsoft yahei', arial, STHeiTi, sans-serif;
word-wrap: break-word;
white-space: pre-wrap;
}
/deep/ pre {
white-space: pre-wrap;
/* css-3 */
white-space: -moz-pre-wrap;
/* Mozilla, since 1999 */
white-space: pre-wrap;
/* Opera 4-6 */
white-space: -o-pre-wrap;
/* Opera 7 */
word-wrap: break-word;
/* Internet Explorer 5.5+ */
word-break: break-all;
overflow: hidden;
font-size: 12px;
font-weight: 400;
font-family: 'PingFang SC', 'Helvetica Neue', Helvetica, 'microsoft yahei', arial, STHeiTi, sans-serif;
}
.report {
display: flex;
.r-title {
margin-bottom: 40px;
font-size: 24px;
text-align: center;
color: #333;
}
.info {
padding: 20px 16px;
border: 1px solid #e1e6f2;
}
.l-title {
display: flex;
align-items: center;
padding: 5px 8px;
margin-bottom: 12px;
font-size: 14px;
color: #333;
background-color: #f7f5ff;
}
.info-list {
display: flex;
flex-wrap: wrap;
padding: 10px 0 0 20px;
li {
display: inline-flex;
width: 23%;
padding: 0 10px;
margin-bottom: 34px;
}
&.edit {
li {
align-items: center;
}
}
label {
font-size: 14px;
color: #333;
white-space: nowrap;
}
span {
min-width: 150px;
padding: 0 10px 3px;
border-bottom: 1px solid #e1e6f2;
}
/deep/.el-input {
width: 174px;
}
}
.score-wrap {
position: relative;
min-width: 150px;
border-bottom: 1px solid #e1e6f2;
em {
position: absolute;
top: -12px;
left: 30px;
font-family: din;
font-size: 30px;
font-weight: 600;
color: #0b1d30;
}
img {
position: absolute;
bottom: -15px;
left: 0;
}
.exist {
position: absolute;
left: 70px;
white-space: nowrap;
color: #f00;
}
}
/deep/.el-textarea .el-textarea__inner,
.pre-wrap {
min-height: 72px;
padding: 10px 16px;
font-size: 14px;
color: #333;
&.edit {
color: #abb3c6;
border: 1px solid #cacfdb;
border-radius: 4px;
background-color: #f6f7f9;
}
}
/deep/ .table th {
background-color: #e5dfff !important;
.cell {
line-height: 35px;
color: #fff;
}
}
.left {
width: 290px;
max-height: calc(100vh - 267px);
margin-right: 15px;
background-color: #fff;
overflow: auto;
.title {
padding: 10px 0;
font-size: 16px;
text-align: center;
color: #333;
}
.progress {
padding: 10px;
color: #5a5a5a;
background-color: #d4e9ff;
}
.type-wrap {
padding: 10px;
.type {
margin-bottom: 20px;
}
.stem {
font-size: 15px;
color: #333;
}
}
.serials {
display: flex;
flex-wrap: wrap;
margin-top: 5px;
li {
position: relative;
width: 30px;
margin: 7px 9px;
font-size: 13px;
text-align: center;
}
.serial {
color: #505050;
line-height: 30px;
border: 1px solid #d3d3d3;
border-bottom: 1px solid transparent;
}
.status1 .serial {
color: #fff;
background-color: #2abd8c;
border-color: #2abd8c;
}
.status2 .serial {
color: #fff;
background-color: #e75050;
border-color: #e75050;
}
.status3 .serial {
color: #2abd8c;
border-color: #2abd8c;
}
.status4 .serial {
color: #fff;
background-color: #fe9f0a;
border-color: #fe9f0a;
}
.score {
height: 22px;
border: 1px solid #d3d3d3;
border-top: 0;
line-height: 22px;
}
}
.status-filter {
display: flex;
justify-content: space-between;
padding: 10px;
border-top: 1px solid #e5e5e5;
li {
display: inline-flex;
align-items: center;
padding: 0 3px;
font-size: 12px;
color: #333;
cursor: pointer;
border: 1px solid transparent;
&:before {
content: '';
width: 13px;
height: 13px;
margin-right: 5px;
border: 1px solid transparent;
}
&:first-child {
&.active {
color: #2abd8c;
border-color: #2abd8c;
}
&:before {
background-color: #2abd8c;
}
}
&:nth-child(2) {
&.active {
color: #e75050;
border-color: #e75050;
}
&:before {
background-color: #e75050;
}
}
&:nth-child(3) {
&.active {
color: #2abd8c;
border-color: #2abd8c;
}
&:before {
border-color: #2abd8c;
}
}
&:last-child {
&.active {
color: #fe9f0a;
border-color: #fe9f0a;
}
&:before {
background-color: #fe9f0a;
}
}
&.active {
font-weight: 600;
}
}
}
}
.right {
width: calc(100% - 359px);
height: calc(100vh - 287px);
padding: 10px;
background-color: #fff;
overflow: auto;
&>li {
margin-bottom: 15px;
}
.outline {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
font-size: 15px;
font-weight: 600;
color: #333;
background-color: #e8f0ff;
}
.shrink {
cursor: pointer;
transition: .5s;
&.active {
transform: rotate(180deg);
}
}
.ques {
max-height: none;
padding: 15px;
margin-bottom: 15px;
transition: all 1.5s;
overflow: hidden;
&.hide {
max-height: 10px;
}
}
.item:not(:last-child) {
padding-bottom: 15px;
margin-bottom: 15px;
border-bottom: 1px dashed #e1e1e1;
}
.stem-wrap {
display: flex;
align-items: baseline;
margin-bottom: 10px;
}
.tag {
margin-left: 10px;
cursor: pointer;
&:hover {
opacity: .9;
}
}
.labels {
display: inline-flex;
align-items: center;
}
.label {
padding: 3px 5px;
margin-right: 10px;
font-size: 12px;
line-height: 1;
color: #9076FF;
white-space: nowrap;
border: 1px solid;
border-radius: 2px;
}
.stem {
max-width: calc(100% - 207px);
}
.ques-info {
position: relative;
padding: 10px;
background-color: #f3f3f3;
overflow: hidden;
&.essay {
&:after {
content: '待 判 分';
position: absolute;
top: 17px;
right: -33px;
padding: 5px 36px;
font-size: 14px;
color: #fff;
background-color: #fe9f0a;
transform: rotate(45deg);
}
}
}
.line {
display: flex;
align-items: center;
margin-bottom: 8px;
font-size: 13px;
color: #333;
&:last-child {
margin-bottom: 0;
}
}
.fill-answers {
display: inline-flex;
align-items: center;
}
.fill-answer {
display: inline-flex;
align-items: center;
margin-right: 10px;
.icon {
margin-left: 7px;
}
}
.score-input {
width: 100px;
margin-right: 5px;
}
.fill-input {
width: 100px;
height: 28px;
padding: 0 15px;
margin: 0 10px;
font-size: 13px;
line-height: 28px;
color: #606266;
border: 1px solid #DCDEE0;
border-radius: 2px;
&:focus {
outline: none;
}
}
.opt {
display: flex;
flex-wrap: wrap;
align-items: baseline;
padding-left: 10px;
margin-bottom: 5px;
font-size: 14px;
color: #707070;
line-height: 1.6;
.el-radio,
.el-checkbox {
margin-right: 15px;
}
.el-radio__label {
display: none;
}
.text {
max-width: calc(100% - 48px);
}
}
}
}
.result-pic {
margin: 10px 0;
}
@media (max-width: 1650px) {
.wrap {
padding: 12px 200px 20px;
}
}
@media (max-width: 1430px) {
.wrap {
padding: 12px 100px 20px;
}
}
</style>
Loading…
Cancel
Save