feat: 老师端top10
This commit is contained in:
parent
6fda3ac190
commit
6824b4fd28
4
.env
4
.env
@ -1,5 +1,5 @@
|
|||||||
#VITE_HOST = http://192.168.0.114:9053
|
#VITE_HOST = http://192.168.0.114:9053
|
||||||
#VITE_HOST = http://43.136.52.196:9053
|
VITE_HOST = http://43.136.52.196:9053
|
||||||
VITE_HOST= https://ai.xuexiaole.com
|
#VITE_HOST= https://ai.xuexiaole.com
|
||||||
VITE_OSS_HOST = https://xxl-1313840333.cos.ap-guangzhou.myqcloud.com
|
VITE_OSS_HOST = https://xxl-1313840333.cos.ap-guangzhou.myqcloud.com
|
||||||
VITE_WS_URL = wss://test.qiaoying.vip/wss/websocket
|
VITE_WS_URL = wss://test.qiaoying.vip/wss/websocket
|
||||||
@ -203,3 +203,53 @@ export const getClassHomeworkReport = (params: {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ---------- 薄弱知识点 Top10 school/report/weakKnowledge/top10 ----------
|
||||||
|
export interface WeakKnowledgeItem {
|
||||||
|
knowledgeId: number | string;
|
||||||
|
knowledgeName: string;
|
||||||
|
subjectId: number | string;
|
||||||
|
subjectName: string;
|
||||||
|
errorCount: number;
|
||||||
|
titleCount: number;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WeakKnowledgeTop10Data {
|
||||||
|
classId: number | string;
|
||||||
|
className: string;
|
||||||
|
topWeakKnowledgeList: WeakKnowledgeItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 薄弱知识点 Top10:GET,参数 form 风格 */
|
||||||
|
export const getWeakKnowledgeTop10 = (params: {
|
||||||
|
classId: string | number;
|
||||||
|
subjectIds?: number[];
|
||||||
|
startTime?: string;
|
||||||
|
endTime?: string;
|
||||||
|
schoolId?: string;
|
||||||
|
}) => {
|
||||||
|
const formEncode = (v: string) => encodeURIComponent(v).replace(/%20/g, '+');
|
||||||
|
const query: string[] = [];
|
||||||
|
query.push('classId=' + formEncode(String(params.classId)));
|
||||||
|
if (params.schoolId) {
|
||||||
|
query.push('schoolId=' + formEncode(String(params.schoolId)));
|
||||||
|
}
|
||||||
|
if (params.subjectIds && params.subjectIds.length > 0) {
|
||||||
|
params.subjectIds.forEach((id) => {
|
||||||
|
query.push('subjectIds=' + formEncode(String(id)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (params.startTime != null && params.startTime !== '' && String(params.startTime).trim() !== '') {
|
||||||
|
const st = String(params.startTime).trim();
|
||||||
|
query.push('startTime=' + formEncode(st.includes(' ') ? st : st + ' 00:00:00'));
|
||||||
|
}
|
||||||
|
if (params.endTime != null && params.endTime !== '' && String(params.endTime).trim() !== '') {
|
||||||
|
const et = String(params.endTime).trim();
|
||||||
|
query.push('endTime=' + formEncode(et.includes(' ') ? et : et + ' 23:59:59'));
|
||||||
|
}
|
||||||
|
return request({
|
||||||
|
url: '/school/report/weakKnowledge/top10?' + query.join('&'),
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
100
src/components/Loading/index.vue
Normal file
100
src/components/Loading/index.vue
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<template>
|
||||||
|
<view class="qy-loading">
|
||||||
|
<view class="qy-loading-inner">
|
||||||
|
<image
|
||||||
|
v-if="!loadError"
|
||||||
|
class="qy-loading-img"
|
||||||
|
:style="imgStyle"
|
||||||
|
:src="loadingSrc"
|
||||||
|
mode="aspectFit"
|
||||||
|
@error="onImageError"
|
||||||
|
/>
|
||||||
|
<view v-else class="qy-loading-fallback">
|
||||||
|
<view class="qy-loading-placeholder"></view>
|
||||||
|
</view>
|
||||||
|
<text v-if="text" class="qy-loading-text">{{ text }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
// 直接引用 static 下的 loading.webp,由构建输出正确路径
|
||||||
|
import defaultLoadingImg from '@/static/loading.webp';
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
/** 提示文字,不传则不显示 */
|
||||||
|
text?: string;
|
||||||
|
/** 尺寸,单位 rpx,默认 120 */
|
||||||
|
size?: number;
|
||||||
|
/** 自定义图片路径,不传则使用 static/loading.webp */
|
||||||
|
src?: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
text: '',
|
||||||
|
size: 120,
|
||||||
|
src: '',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const loadError = ref(false);
|
||||||
|
|
||||||
|
const loadingSrc = computed(() => {
|
||||||
|
if (props.src) return props.src;
|
||||||
|
return defaultLoadingImg;
|
||||||
|
});
|
||||||
|
|
||||||
|
const imgStyle = computed(() => ({
|
||||||
|
width: props.size + 'rpx',
|
||||||
|
height: props.size + 'rpx',
|
||||||
|
}));
|
||||||
|
|
||||||
|
function onImageError() {
|
||||||
|
loadError.value = true;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.qy-loading {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 120rpx;
|
||||||
|
padding: 24rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qy-loading-inner {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qy-loading-img {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qy-loading-fallback {
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qy-loading-placeholder {
|
||||||
|
width: 56rpx;
|
||||||
|
height: 56rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.qy-loading-text {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -140,6 +140,19 @@
|
|||||||
"pageOrientation": "landscape"
|
"pageOrientation": "landscape"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "weakKnowledge/index",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "薄弱知识点 Top10",
|
||||||
|
"disableScroll": true,
|
||||||
|
"pageOrientation": "landscape",
|
||||||
|
"allowsBounceVertical": "NO",
|
||||||
|
"mp-weixin": {
|
||||||
|
"pageOrientation": "landscape"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -77,10 +77,7 @@
|
|||||||
|
|
||||||
<!-- 加载中 -->
|
<!-- 加载中 -->
|
||||||
<view v-else class="loading-page">
|
<view v-else class="loading-page">
|
||||||
<qy-Empty
|
<qy-Loading :text="reportFlag ? '正在加载报告中...' : '正在精选题目中...'" />
|
||||||
type="loading"
|
|
||||||
:content="reportFlag ? '正在加载报告中...' : '正在精选题目中...'"
|
|
||||||
/>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -90,7 +90,7 @@
|
|||||||
<text>重置</text>
|
<text>重置</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="btn-confirm" :class="{ disabled: !selectedClassId }" @click="handleConfirm">
|
<view class="btn-confirm" :class="{ disabled: !selectedClassId }" @click="handleConfirm">
|
||||||
<text>查询</text>
|
<text>{{ confirmButtonText }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -98,13 +98,22 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch, onMounted } from 'vue';
|
import { ref, computed, watch, onMounted } from 'vue';
|
||||||
import { onShow } from '@dcloudio/uni-app';
|
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||||
import { getClassListByTeacher, getSubjectListByClassId } from '@/api/teacher';
|
import { getClassListByTeacher, getSubjectListByClassId } from '@/api/teacher';
|
||||||
import { teacher } from '@/store/teacher';
|
import { teacher } from '@/store/teacher';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
const teacherStore = teacher();
|
const teacherStore = teacher();
|
||||||
const { schoolId, teacherInfo } = storeToRefs(teacherStore);
|
const { schoolId, teacherInfo } = storeToRefs(teacherStore);
|
||||||
|
|
||||||
|
// 从首页「薄弱知识点」进入时带 target=weakKnowledge,确认后跳薄弱知识点页
|
||||||
|
const target = ref('');
|
||||||
|
onLoad((options: any) => {
|
||||||
|
if (options?.target) target.value = options.target;
|
||||||
|
});
|
||||||
|
const confirmButtonText = computed(() =>
|
||||||
|
target.value === 'weakKnowledge' ? '查看薄弱知识点' : '查询',
|
||||||
|
);
|
||||||
|
|
||||||
// 胶囊预留 + 安全区域
|
// 胶囊预留 + 安全区域
|
||||||
const topPaddingRightRpx = ref(0);
|
const topPaddingRightRpx = ref(0);
|
||||||
const safeAreaLeftRpx = ref(0);
|
const safeAreaLeftRpx = ref(0);
|
||||||
@ -233,24 +242,28 @@ function handleConfirm() {
|
|||||||
uni.showToast({ title: '请先选择班级', icon: 'none' });
|
uni.showToast({ title: '请先选择班级', icon: 'none' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 找到班级名称
|
const classIdEnc = encodeURIComponent(selectedClassId.value);
|
||||||
const cls = classList.value.find((c) => c.id === selectedClassId.value);
|
const params: string[] = ['classId=' + classIdEnc];
|
||||||
const className = cls?.name || '';
|
|
||||||
// 拼接参数跳转到作业记录页
|
|
||||||
const params: string[] = [];
|
|
||||||
params.push('classId=' + encodeURIComponent(selectedClassId.value));
|
|
||||||
params.push('className=' + encodeURIComponent(className));
|
|
||||||
if (selectedSubjectIds.value.length > 0) {
|
if (selectedSubjectIds.value.length > 0) {
|
||||||
params.push('subjectIds=' + encodeURIComponent(selectedSubjectIds.value.join(',')));
|
params.push('subjectIds=' + encodeURIComponent(selectedSubjectIds.value.join(',')));
|
||||||
}
|
}
|
||||||
if (startTime.value) {
|
if (startTime.value) params.push('startTime=' + encodeURIComponent(startTime.value));
|
||||||
params.push('startTime=' + encodeURIComponent(startTime.value));
|
if (endTime.value) params.push('endTime=' + encodeURIComponent(endTime.value));
|
||||||
|
const query = params.join('&');
|
||||||
|
|
||||||
|
if (target.value === 'weakKnowledge') {
|
||||||
|
uni.navigateTo({ url: '/pages/teacher/weakKnowledge/index?' + query });
|
||||||
|
} else {
|
||||||
|
const cls = classList.value.find((c) => c.id === selectedClassId.value);
|
||||||
|
const className = cls?.name || '';
|
||||||
|
const homeworkParams = ['classId=' + classIdEnc, 'className=' + encodeURIComponent(className)];
|
||||||
|
if (selectedSubjectIds.value.length > 0) {
|
||||||
|
homeworkParams.push('subjectIds=' + encodeURIComponent(selectedSubjectIds.value.join(',')));
|
||||||
}
|
}
|
||||||
if (endTime.value) {
|
if (startTime.value) homeworkParams.push('startTime=' + encodeURIComponent(startTime.value));
|
||||||
params.push('endTime=' + encodeURIComponent(endTime.value));
|
if (endTime.value) homeworkParams.push('endTime=' + encodeURIComponent(endTime.value));
|
||||||
|
uni.navigateTo({ url: '/pages/teacher/homework/index?' + homeworkParams.join('&') });
|
||||||
}
|
}
|
||||||
const url = '/pages/teacher/homework/index?' + params.join('&');
|
|
||||||
uni.navigateTo({ url });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchClassList = async () => {
|
const fetchClassList = async () => {
|
||||||
|
|||||||
@ -47,8 +47,7 @@
|
|||||||
<view class="main-body">
|
<view class="main-body">
|
||||||
<!-- 加载中 -->
|
<!-- 加载中 -->
|
||||||
<view v-if="loading" class="status-box">
|
<view v-if="loading" class="status-box">
|
||||||
<view class="status-icon">⏳</view>
|
<qy-Loading text="加载中..." />
|
||||||
<text class="status-text">加载中...</text>
|
|
||||||
</view>
|
</view>
|
||||||
<!-- 加载失败 -->
|
<!-- 加载失败 -->
|
||||||
<view v-else-if="error" class="status-box error">
|
<view v-else-if="error" class="status-box error">
|
||||||
|
|||||||
@ -25,6 +25,15 @@
|
|||||||
</view>
|
</view>
|
||||||
</picker>
|
</picker>
|
||||||
|
|
||||||
|
<!-- 薄弱知识点 Top10(点击后先到筛选页选条件,再进薄弱知识点页) -->
|
||||||
|
<view class="weak-knowledge-wrap">
|
||||||
|
<view class="weak-knowledge-btn" @click="goWeakKnowledge">
|
||||||
|
<text class="weak-knowledge-icon">📌</text>
|
||||||
|
<text class="weak-knowledge-label">薄弱知识点 Top10</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 学情报告等入口(横向排版) -->
|
||||||
<view class="left-actions">
|
<view class="left-actions">
|
||||||
<view class="action-item" @click="handleNavigate('/pages/teacher/homework/filter')">
|
<view class="action-item" @click="handleNavigate('/pages/teacher/homework/filter')">
|
||||||
<text class="action-icon">📊</text>
|
<text class="action-icon">📊</text>
|
||||||
@ -80,7 +89,7 @@
|
|||||||
<scroll-view class="dashboard-body" scroll-y enhanced :show-scrollbar="false">
|
<scroll-view class="dashboard-body" scroll-y enhanced :show-scrollbar="false">
|
||||||
<!-- 加载 / 空状态 -->
|
<!-- 加载 / 空状态 -->
|
||||||
<view v-if="loading" class="status-box">
|
<view v-if="loading" class="status-box">
|
||||||
<text class="status-text">加载中...</text>
|
<qy-Loading text="加载中..." />
|
||||||
</view>
|
</view>
|
||||||
<view v-else-if="!selectedClassId" class="status-box">
|
<view v-else-if="!selectedClassId" class="status-box">
|
||||||
<text class="status-text">请选择班级查看数据</text>
|
<text class="status-text">请选择班级查看数据</text>
|
||||||
@ -229,6 +238,18 @@ const handleNavigate = (url: string) => {
|
|||||||
}
|
}
|
||||||
uni.navigateTo({ url });
|
uni.navigateTo({ url });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 跳转筛选条件页面,选择完筛选项后进入薄弱知识点页面 */
|
||||||
|
function goWeakKnowledge() {
|
||||||
|
if (!token.value) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/login/index?redirect=${encodeURIComponent('/pages/teacher/homework/filter?target=weakKnowledge')}`,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uni.navigateTo({ url: '/pages/teacher/homework/filter?target=weakKnowledge' });
|
||||||
|
}
|
||||||
|
|
||||||
const goLogin = () => uni.navigateTo({ url: '/pages/login/index' });
|
const goLogin = () => uni.navigateTo({ url: '/pages/login/index' });
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
@ -516,23 +537,67 @@ $teacher-bg: 'https://xxl-1313840333.cos.ap-guangzhou.myqcloud.com/urm/main_bg.s
|
|||||||
&:active { opacity: 0.85; }
|
&:active { opacity: 0.85; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 薄弱知识点 Top10 按钮(班级筛选下方)
|
||||||
|
.weak-knowledge-wrap {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weak-knowledge-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4rpx;
|
||||||
|
padding: 8rpx 6rpx;
|
||||||
|
background: linear-gradient(135deg, #8f9df7 0%, #cab5ff 100%);
|
||||||
|
border-radius: 8rpx;
|
||||||
|
border: 1rpx solid rgba(143, 157, 247, 0.5);
|
||||||
|
|
||||||
|
.weak-knowledge-icon {
|
||||||
|
font-size: 14rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.weak-knowledge-label {
|
||||||
|
font-family: $font-special;
|
||||||
|
font-size: 11rpx;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
background: rgba(255, 255, 255, 0.5);
|
||||||
|
border-color: rgba(196, 181, 255, 0.3);
|
||||||
|
.weak-knowledge-label { color: #999; }
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active:not(.disabled) { opacity: 0.85; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 学情报告等入口(横向排版)
|
||||||
.left-actions {
|
.left-actions {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
gap: 6rpx;
|
gap: 6rpx;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-item {
|
.action-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4rpx;
|
||||||
padding: 8rpx 4rpx;
|
padding: 8rpx 4rpx;
|
||||||
background: rgba(255, 255, 255, 0.85);
|
background: rgba(255, 255, 255, 0.85);
|
||||||
border-radius: 8rpx;
|
border-radius: 8rpx;
|
||||||
border: 1rpx solid rgba(196, 181, 255, 0.2);
|
border: 1rpx solid rgba(196, 181, 255, 0.2);
|
||||||
|
margin-top: 8rpx;
|
||||||
|
width: 100%;
|
||||||
.action-icon {
|
.action-icon {
|
||||||
font-size: 18rpx;
|
font-size: 18rpx;
|
||||||
margin-bottom: 2rpx;
|
margin-bottom: 2rpx;
|
||||||
@ -546,6 +611,7 @@ $teacher-bg: 'https://xxl-1313840333.cos.ap-guangzhou.myqcloud.com/urm/main_bg.s
|
|||||||
&.logout {
|
&.logout {
|
||||||
background: rgba(255, 255, 255, 0.6);
|
background: rgba(255, 255, 255, 0.6);
|
||||||
.action-label { color: #999; }
|
.action-label { color: #999; }
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active { opacity: 0.8; }
|
&:active { opacity: 0.8; }
|
||||||
|
|||||||
298
src/pages/teacher/weakKnowledge/index.vue
Normal file
298
src/pages/teacher/weakKnowledge/index.vue
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
<template>
|
||||||
|
<view class="weak-page" :style="safeAreaStyle">
|
||||||
|
<!-- 顶部栏 -->
|
||||||
|
<view class="top-bar" :style="topRightPaddingStyle">
|
||||||
|
<qy-BackBar leftText="薄弱知识点 Top10" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 主体 -->
|
||||||
|
<view class="main-body">
|
||||||
|
<view v-if="loading" class="status-box">
|
||||||
|
<qy-Loading text="加载中..." />
|
||||||
|
</view>
|
||||||
|
<view v-else-if="errorMsg" class="status-box error">
|
||||||
|
<text class="status-text">{{ errorMsg }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-else-if="!classId" class="status-box">
|
||||||
|
<text class="status-text">缺少班级参数</text>
|
||||||
|
</view>
|
||||||
|
<scroll-view v-else class="list-scroll" scroll-y enhanced :show-scrollbar="false">
|
||||||
|
<view v-if="listData" class="list-content">
|
||||||
|
<view class="class-header">
|
||||||
|
<text class="class-name">{{ listData.className }}</text>
|
||||||
|
<text class="class-desc">薄弱知识点 Top10</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="!listData.topWeakKnowledgeList || listData.topWeakKnowledgeList.length === 0" class="empty-hint">
|
||||||
|
<text>暂无薄弱知识点数据</text>
|
||||||
|
</view>
|
||||||
|
<view v-else class="knowledge-list">
|
||||||
|
<view
|
||||||
|
v-for="(item, idx) in listData.topWeakKnowledgeList"
|
||||||
|
:key="item.knowledgeId + '-' + idx"
|
||||||
|
class="knowledge-item"
|
||||||
|
>
|
||||||
|
<view class="knowledge-rank" :class="'rank-' + (idx + 1)">{{ idx + 1 }}</view>
|
||||||
|
<view class="knowledge-main">
|
||||||
|
<text class="knowledge-name">{{ item.knowledgeName || '未命名知识点' }}</text>
|
||||||
|
<view class="knowledge-meta">
|
||||||
|
<text v-if="item.subjectName" class="knowledge-subject">{{ item.subjectName }}</text>
|
||||||
|
<text class="knowledge-count">错{{ item.errorCount ?? 0 }}次</text>
|
||||||
|
<text class="knowledge-titles">涉及{{ item.titleCount ?? 0 }}题</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, onMounted } from 'vue';
|
||||||
|
import { onLoad } from '@dcloudio/uni-app';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { teacher } from '@/store/teacher';
|
||||||
|
import { getWeakKnowledgeTop10 } from '@/api/teacher';
|
||||||
|
import type { WeakKnowledgeTop10Data } from '@/api/teacher';
|
||||||
|
|
||||||
|
const teacherStore = teacher();
|
||||||
|
const { schoolId } = storeToRefs(teacherStore);
|
||||||
|
|
||||||
|
const classId = ref('');
|
||||||
|
const startTime = ref('');
|
||||||
|
const endTime = ref('');
|
||||||
|
const subjectIds = ref<number[]>([]);
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const errorMsg = ref('');
|
||||||
|
const listData = ref<WeakKnowledgeTop10Data | null>(null);
|
||||||
|
|
||||||
|
// 安全区域与胶囊
|
||||||
|
const topPaddingRightRpx = ref(0);
|
||||||
|
const safeAreaLeftRpx = ref(0);
|
||||||
|
const topRightPaddingStyle = computed(() =>
|
||||||
|
topPaddingRightRpx.value > 0 ? { paddingRight: topPaddingRightRpx.value + 'rpx' } : {},
|
||||||
|
);
|
||||||
|
const safeAreaStyle = computed(() => {
|
||||||
|
const style: Record<string, string> = {};
|
||||||
|
if (safeAreaLeftRpx.value > 0) {
|
||||||
|
style.paddingLeft = safeAreaLeftRpx.value + 'rpx';
|
||||||
|
}
|
||||||
|
return style;
|
||||||
|
});
|
||||||
|
|
||||||
|
function getCapsulePadding() {
|
||||||
|
try {
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
const menuButton = uni.getMenuButtonBoundingClientRect();
|
||||||
|
const sys = uni.getSystemInfoSync();
|
||||||
|
if (menuButton && sys?.windowWidth) {
|
||||||
|
const r = 750 / sys.windowWidth;
|
||||||
|
topPaddingRightRpx.value = Math.ceil((sys.windowWidth - menuButton.left) * r) + 24;
|
||||||
|
} else {
|
||||||
|
topPaddingRightRpx.value = 140;
|
||||||
|
}
|
||||||
|
if (sys?.safeArea && sys.safeArea.left > 0) {
|
||||||
|
safeAreaLeftRpx.value = Math.ceil(sys.safeArea.left * (750 / (sys.windowWidth || sys.screenWidth))) + 4;
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
} catch {
|
||||||
|
topPaddingRightRpx.value = 140;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad((options: any) => {
|
||||||
|
if (options?.classId) classId.value = options.classId;
|
||||||
|
if (options?.startTime) startTime.value = options.startTime;
|
||||||
|
if (options?.endTime) endTime.value = options.endTime;
|
||||||
|
if (options?.subjectIds) {
|
||||||
|
const raw = options.subjectIds;
|
||||||
|
if (typeof raw === 'string') {
|
||||||
|
subjectIds.value = raw.split(',').map((s: string) => Number(s.trim())).filter((n: number) => !isNaN(n));
|
||||||
|
} else if (Array.isArray(raw)) {
|
||||||
|
subjectIds.value = raw.map((s: any) => Number(s)).filter((n: number) => !isNaN(n));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fetchList() {
|
||||||
|
if (!classId.value) return;
|
||||||
|
loading.value = true;
|
||||||
|
errorMsg.value = '';
|
||||||
|
listData.value = null;
|
||||||
|
try {
|
||||||
|
const params: any = { classId: classId.value };
|
||||||
|
if (schoolId.value) params.schoolId = schoolId.value;
|
||||||
|
if (subjectIds.value.length > 0) params.subjectIds = subjectIds.value;
|
||||||
|
if (startTime.value?.trim()) params.startTime = startTime.value.trim();
|
||||||
|
if (endTime.value?.trim()) params.endTime = endTime.value.trim();
|
||||||
|
const res = await getWeakKnowledgeTop10(params);
|
||||||
|
listData.value = (res?.data ?? res) as WeakKnowledgeTop10Data;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('薄弱知识点 Top10 请求失败', e);
|
||||||
|
errorMsg.value = '加载失败,请重试';
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getCapsulePadding();
|
||||||
|
if (classId.value) fetchList();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$teacher-bg: 'https://xxl-1313840333.cos.ap-guangzhou.myqcloud.com/urm/main_bg.svg';
|
||||||
|
|
||||||
|
.weak-page {
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-image: url($teacher-bg);
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0 16rpx 0 16rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-bar {
|
||||||
|
padding: 12rpx 0 8rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-body {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60rpx 20rpx;
|
||||||
|
.status-text {
|
||||||
|
font-size: 16rpx;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 1rpx 4rpx rgba(143, 157, 247, 0.5);
|
||||||
|
}
|
||||||
|
&.error .status-text {
|
||||||
|
color: #ffb3b3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-scroll {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-content {
|
||||||
|
padding-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.class-header {
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
padding: 12rpx 16rpx;
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
border-radius: 12rpx;
|
||||||
|
border: 1rpx solid rgba(196, 181, 255, 0.25);
|
||||||
|
.class-name {
|
||||||
|
font-family: $font-special;
|
||||||
|
font-size: 20rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $font-color;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.class-desc {
|
||||||
|
font-size: 13rpx;
|
||||||
|
color: #888;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-hint {
|
||||||
|
padding: 40rpx 0;
|
||||||
|
text {
|
||||||
|
font-size: 15rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.knowledge-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.knowledge-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12rpx;
|
||||||
|
padding: 12rpx 14rpx;
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
border-radius: 10rpx;
|
||||||
|
border: 1rpx solid rgba(196, 181, 255, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.knowledge-rank {
|
||||||
|
width: 32rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-family: $font-special;
|
||||||
|
font-size: 14rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #fff;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: #cab5ff;
|
||||||
|
&.rank-1 { background: linear-gradient(135deg, #ff6b6b, #ee5a24); }
|
||||||
|
&.rank-2 { background: linear-gradient(135deg, #f7a635, #f59f00); }
|
||||||
|
&.rank-3 { background: linear-gradient(135deg, #5b8def, #4a7bdf); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.knowledge-main {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.knowledge-name {
|
||||||
|
font-family: $font-special;
|
||||||
|
font-size: 15rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $font-color;
|
||||||
|
@include single-ellipsis;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.knowledge-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
margin-top: 6rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.knowledge-subject {
|
||||||
|
font-size: 12rpx;
|
||||||
|
color: #8f9df7;
|
||||||
|
padding: 2rpx 8rpx;
|
||||||
|
background: rgba(143, 157, 247, 0.12);
|
||||||
|
border-radius: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.knowledge-count {
|
||||||
|
font-size: 12rpx;
|
||||||
|
color: #e05040;
|
||||||
|
}
|
||||||
|
|
||||||
|
.knowledge-titles {
|
||||||
|
font-size: 12rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
BIN
src/static/loading.webp
Normal file
BIN
src/static/loading.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
Loading…
x
Reference in New Issue
Block a user