|
@@ -6,13 +6,45 @@
|
|
总提交:{{ statistics.totalSubmissions }} 人
|
|
总提交:{{ statistics.totalSubmissions }} 人
|
|
</div>
|
|
</div>
|
|
<div class="statistics-chart">
|
|
<div class="statistics-chart">
|
|
- <div v-for="option in statistics.options" :key="option" class="statistics-item">
|
|
|
|
|
|
+ <div class="tool45" v-if="statistics.tool=='45'">
|
|
|
|
+ <div v-for="(item,index) in statistics.titles" :key="index">
|
|
|
|
+ <div class="t45_title">
|
|
|
|
+ <span>{{ index+1 }}、</span>
|
|
|
|
+ <div>{{ item.title }}</div>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="t45_list">
|
|
|
|
+ <div class="t45_l_item" v-for="(i,index2) in item.checkList" :key="index+'_'+index2">
|
|
|
|
+ <div class="t45_l_i_option">
|
|
|
|
+ <span>{{ i.index }}:</span>
|
|
|
|
+ <div>
|
|
|
|
+ <div :style="{width:i.proportion}"></div>
|
|
|
|
+ </div>
|
|
|
|
+ <span>{{ i.proportion }}</span>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="t45_l_i_user">
|
|
|
|
+ <template v-for="(j,index3) in i.user" :key="index+'_'+index2+'_'+index3">
|
|
|
|
+ <div v-if="item.showAllUser || (!item.showAllUser && index3 < 3)">
|
|
|
|
+ <span>{{ j }}</span>
|
|
|
|
+ </div>
|
|
|
|
+ </template>
|
|
|
|
+ <span class="t45_o_btn" v-if="i.user.length > 3 && !item.showAllUser" @click="()=>{item.showAllUser = true,$forceUpdate()}">
|
|
|
|
+ <svg t="1756278651830" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7545" width="200" height="200"><path d="M293.546667 253.013333l264.533333 266.666667-264.106667 266.24 70.4 70.826667 334.506667-337.066667-334.933333-337.066667z" p-id="7546"></path></svg>
|
|
|
|
+ </span>
|
|
|
|
+ <span class="t45_o_btn" v-if="i.user.length > 3 && item.showAllUser" @click="()=>{item.showAllUser = false,$forceUpdate()}">
|
|
|
|
+ <svg style="transform: rotate(180deg);" t="1756278651830" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7545" width="200" height="200"><path d="M293.546667 253.013333l264.533333 266.666667-264.106667 266.24 70.4 70.826667 334.506667-337.066667-334.933333-337.066667z" p-id="7546"></path></svg>
|
|
|
|
+ </span>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ <!-- <div v-for="option in statistics.options" :key="option" class="statistics-item">
|
|
<div class="option-label">{{ option }}</div>
|
|
<div class="option-label">{{ option }}</div>
|
|
<div class="option-bar">
|
|
<div class="option-bar">
|
|
<div class="option-fill" :style="{ width: (statistics.stats[option] / statistics.totalSubmissions * 100) + '%' }"></div>
|
|
<div class="option-fill" :style="{ width: (statistics.stats[option] / statistics.totalSubmissions * 100) + '%' }"></div>
|
|
</div>
|
|
</div>
|
|
<div class="option-count">{{ statistics.stats[option] }}人</div>
|
|
<div class="option-count">{{ statistics.stats[option] }}人</div>
|
|
- </div>
|
|
|
|
|
|
+ </div> -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-else class="statistics-empty">
|
|
<div v-else class="statistics-empty">
|
|
@@ -39,32 +71,89 @@ const isChoiceQuestion = computed(() => {
|
|
return frame?.toolType === 45
|
|
return frame?.toolType === 45
|
|
})
|
|
})
|
|
|
|
|
|
|
|
+const optionList: Array<string> = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
// 选择题统计信息
|
|
// 选择题统计信息
|
|
const statistics = computed(() => {
|
|
const statistics = computed(() => {
|
|
if (!isChoiceQuestion.value || !props.workArray || props.workArray.length === 0) return null
|
|
if (!isChoiceQuestion.value || !props.workArray || props.workArray.length === 0) return null
|
|
-
|
|
|
|
- // 统计每个选项的选择人数
|
|
|
|
- const stats: { [key: string]: number } = {}
|
|
|
|
|
|
+
|
|
|
|
+ // 统计信息结构优化
|
|
|
|
+ let titles: Array<{
|
|
|
|
+ title: string,
|
|
|
|
+ showAllUser: boolean,
|
|
|
|
+ checkList: Array<{ options: string, num: number, proportion: string, index :string, user: Array<[]>}>
|
|
|
|
+ }> = []
|
|
let totalSubmissions = 0
|
|
let totalSubmissions = 0
|
|
-
|
|
|
|
- props.workArray.forEach(work => {
|
|
|
|
- if (work.content) {
|
|
|
|
- try {
|
|
|
|
- // const content = typeof work.content === 'string' ? JSON.parse(work.content) : work.content
|
|
|
|
- // const choice = content.choice || content.answer || content.selected || '未知'
|
|
|
|
- // stats[choice] = (stats[choice] || 0) + 1
|
|
|
|
|
|
+
|
|
|
|
+ let tool: string = ''
|
|
|
|
+
|
|
|
|
+ // 只初始化一次题目结构
|
|
|
|
+ let isTitleInitialized = false
|
|
|
|
+
|
|
|
|
+ props.workArray.forEach((work) => {
|
|
|
|
+ if (!work.content) return
|
|
|
|
+ try {
|
|
|
|
+ if (work.atool === '45') {
|
|
|
|
+ const content = JSON.parse(decodeURIComponent(work.content))
|
|
|
|
+ const testJson = content.testJson
|
|
|
|
+
|
|
|
|
+ // 初始化题目结构
|
|
|
|
+ if (!isTitleInitialized && Array.isArray(testJson)) {
|
|
|
|
+ tool = work.atool
|
|
|
|
+ titles = testJson.map((item: any) => ({
|
|
|
|
+ title: item.teststitle || '',
|
|
|
|
+ showAllUser: false,
|
|
|
|
+ checkList: Array.isArray(item.checkList)
|
|
|
|
+ ? item.checkList.map((i: any) => ({ options: i, num: 0, user: [], proportion: '', index: '' }))
|
|
|
|
+ : []
|
|
|
|
+ }))
|
|
|
|
+ isTitleInitialized = true
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 统计每道题的选项被选择次数
|
|
|
|
+ if (Array.isArray(titles) && Array.isArray(testJson)) {
|
|
|
|
+ testJson.forEach((item: any, index: number) => {
|
|
|
|
+ const answer = item.userAnswer
|
|
|
|
+ if (typeof answer === 'number') {
|
|
|
|
+ if (titles[index] && titles[index].checkList[answer]) {
|
|
|
|
+ titles[index].checkList[answer].num += 1
|
|
|
|
+ titles[index].checkList[answer].user.push(work.name)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if (Array.isArray(answer) && answer.length > 0) {
|
|
|
|
+ answer.forEach((i: number) => {
|
|
|
|
+ if (titles[index] && titles[index].checkList[i]) {
|
|
|
|
+ titles[index].checkList[i].num += 1
|
|
|
|
+ titles[index].checkList[i].user.push(work.name)
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
totalSubmissions++
|
|
totalSubmissions++
|
|
}
|
|
}
|
|
- catch (e) {
|
|
|
|
- console.log('解析选择题答案失败:', work.content)
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
+ catch (e) {
|
|
|
|
+ // eslint-disable-next-line no-console
|
|
|
|
+ console.log('解析选择题答案失败:', e, work.content)
|
|
}
|
|
}
|
|
})
|
|
})
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ titles.forEach((item: any) => {
|
|
|
|
+ const total = item.checkList.reduce((sum: number, cur: any) => sum + cur.num, 0)
|
|
|
|
+ item.checkList.forEach((option: any, index :number) => {
|
|
|
|
+ option.proportion = total > 0 ? ((option.num / total) * 100).toFixed(1) + '%' : '0.0%'
|
|
|
|
+ option.index = optionList[index]
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ // 返回结构优化,便于前端展示
|
|
return {
|
|
return {
|
|
totalSubmissions,
|
|
totalSubmissions,
|
|
- stats,
|
|
|
|
- options: Object.keys(stats)
|
|
|
|
|
|
+ titles,
|
|
|
|
+ tool
|
|
}
|
|
}
|
|
})
|
|
})
|
|
</script>
|
|
</script>
|
|
@@ -147,4 +236,103 @@ const statistics = computed(() => {
|
|
color: #999;
|
|
color: #999;
|
|
font-size: 14px;
|
|
font-size: 14px;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+.tool45{
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: auto;
|
|
|
|
+ &>div{
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: auto;
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
+ .t45_title{
|
|
|
|
+ width: 100%;
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ margin-bottom: 5px;
|
|
|
|
+ span{
|
|
|
|
+ font-size: 16px;
|
|
|
|
+ color: #1890ff;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ .t45_list{
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: auto;
|
|
|
|
+ .t45_l_item{
|
|
|
|
+
|
|
|
|
+ margin-top: 5px;
|
|
|
|
+ .t45_l_i_option{
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ height: auto;
|
|
|
|
+ width: 100%;
|
|
|
|
+ justify-content: space-between;
|
|
|
|
+ span{
|
|
|
|
+ display: flex;
|
|
|
|
+ width: 40px;
|
|
|
|
+ &:nth-of-type(1){
|
|
|
|
+ width: 20px;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ &>div{
|
|
|
|
+ margin: 0 5px;
|
|
|
|
+ width: calc(100% - 10px - 20px - 40px);
|
|
|
|
+ height: 15px;
|
|
|
|
+ background-color: #FFFFFF;
|
|
|
|
+ // box-sizing: border-box;
|
|
|
|
+ border: solid 1px #E9ECEF;
|
|
|
|
+ border-radius: 4px;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ &>div{
|
|
|
|
+ height: 100%;
|
|
|
|
+ background-color: #3681FC;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ .t45_l_i_user{
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: auto;
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
+ margin-top: 8px;
|
|
|
|
+ margin-bottom: 8px;
|
|
|
|
+ &>div{
|
|
|
|
+ align-items: center;
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
+ height: 35px;
|
|
|
|
+ span{
|
|
|
|
+ width: auto;
|
|
|
|
+ height: auto;
|
|
|
|
+ padding: 4px 10px;
|
|
|
|
+ background-color: #fff;
|
|
|
|
+ border: solid 1px #2f80ed;
|
|
|
|
+ border-radius: 6px;
|
|
|
|
+ margin-right:8px ;
|
|
|
|
+ margin-bottom: 5px;
|
|
|
|
+ color: #2f80ed;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ &>span{
|
|
|
|
+ width: auto;
|
|
|
|
+ height: auto;
|
|
|
|
+ padding: 4px 10px;
|
|
|
|
+ background-color: #fff;
|
|
|
|
+ border: solid 1px #E9ECEF;
|
|
|
|
+ border-radius: 6px;
|
|
|
|
+ margin-right:8px;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ transition: .2s;
|
|
|
|
+ &:hover{
|
|
|
|
+ background-color: #f1f1f1;
|
|
|
|
+ }
|
|
|
|
+ svg{
|
|
|
|
+ width: 15px;
|
|
|
|
+ height: 15px;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
</style>
|
|
</style>
|