feat: 新增锁屏、作业模式
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
This commit is contained in:
parent
0fd4d0d39c
commit
adbfc847ec
@ -1,6 +1,12 @@
|
||||
import { post } from '#/api/http'
|
||||
|
||||
import { BasicService } from './basic'
|
||||
|
||||
class ClassService extends BasicService {
|
||||
async lockScreen(data: { classId: any; status: 0 | 1 }) {
|
||||
await post('/school/classScreenLock/lock', data)
|
||||
}
|
||||
|
||||
protected findPageUrl(): string {
|
||||
return '/org/class/list'
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import MjTable, {
|
||||
mj_form_text,
|
||||
} from '#/components/mj/mj-table'
|
||||
import { pattern_phone } from '#/components/mj/mj-table/fmt'
|
||||
import hud from '#/utils/hud'
|
||||
import obj from '#/utils/obj'
|
||||
import { grades } from '#/store/basic-data'
|
||||
|
||||
@ -73,6 +74,7 @@ const addFields = [
|
||||
'contact',
|
||||
'phone',
|
||||
'currencyFlag',
|
||||
'workMode',
|
||||
'gradeId',
|
||||
'masterTeacherId',
|
||||
]
|
||||
@ -148,12 +150,27 @@ const editForm = {
|
||||
name: 'VxeSwitch',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'workMode',
|
||||
title: '作业模式',
|
||||
span: 12,
|
||||
itemRender: {
|
||||
name: 'VxeSwitch',
|
||||
props: {
|
||||
openValue: 1,
|
||||
closeValue: 0,
|
||||
openLabel: '已开启',
|
||||
closeLabel: '未开启',
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
data: {},
|
||||
},
|
||||
addFields,
|
||||
updateFields: ['id', ...addFields],
|
||||
beforeSaveOrUpdate(formData: any) {
|
||||
formData.workMode ??= 0
|
||||
if (userInfo.value.isOnlyStaff) {
|
||||
formData.schoolId = userInfo.value.schoolId
|
||||
}
|
||||
@ -176,6 +193,7 @@ const editForm = {
|
||||
if (userInfo.value.gradeId) {
|
||||
data.gradeId = userInfo.value.gradeId
|
||||
}
|
||||
data.workMode ??= 0
|
||||
if (edit) {
|
||||
gridRef.value.hideEditFormItems('schoolName')
|
||||
return data
|
||||
@ -200,6 +218,13 @@ const searchForm = {
|
||||
// 表格
|
||||
const grid = {
|
||||
columns: obj.joinObjs<any>(
|
||||
{
|
||||
title: '操作',
|
||||
width: 160,
|
||||
slots: {
|
||||
default: 'operation',
|
||||
},
|
||||
},
|
||||
{ type: 'seq' },
|
||||
{
|
||||
field: 'name',
|
||||
@ -216,6 +241,12 @@ const grid = {
|
||||
default: 'currencyFlag',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '作业模式',
|
||||
slots: {
|
||||
default: 'workMode',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'contact',
|
||||
title: '联系人',
|
||||
@ -249,6 +280,35 @@ function currencyFlagChange(row: any) {
|
||||
row.currencyFlag = oldFlag
|
||||
})
|
||||
}
|
||||
|
||||
// 作业模式
|
||||
function workModeChange(row: any) {
|
||||
const oldMode = row.workMode === 1 ? 0 : 1
|
||||
classService
|
||||
.saveOrUpdate({
|
||||
id: row.id,
|
||||
workMode: row.workMode,
|
||||
})
|
||||
.catch(() => {
|
||||
row.workMode = oldMode
|
||||
})
|
||||
}
|
||||
|
||||
function updateScreenLock(row: any, status: 0 | 1) {
|
||||
const option = status === 1 ? '锁屏' : '解锁'
|
||||
hud.confirmLoad(`确定${option}【${row.name}】吗?`, {
|
||||
task: async () => {
|
||||
await classService.lockScreen({
|
||||
classId: row.id,
|
||||
status,
|
||||
})
|
||||
gridRef.value?.reload()
|
||||
},
|
||||
hideError: true,
|
||||
option,
|
||||
hideSuccess: false,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -263,12 +323,30 @@ function currencyFlagChange(row: any) {
|
||||
:service="classService"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<template #operation="{ row }">
|
||||
<el-button type="danger" @click="updateScreenLock(row, 1)">
|
||||
锁屏
|
||||
</el-button>
|
||||
<el-button type="success" @click="updateScreenLock(row, 0)">
|
||||
解锁
|
||||
</el-button>
|
||||
</template>
|
||||
<template #currencyFlag="{ row }">
|
||||
<el-switch
|
||||
v-model="row.currencyFlag"
|
||||
@click="currencyFlagChange(row)"
|
||||
/>
|
||||
</template>
|
||||
<template #workMode="{ row }">
|
||||
<vxe-switch
|
||||
v-model="row.workMode"
|
||||
:close-value="0"
|
||||
close-label="未开启"
|
||||
:open-value="1"
|
||||
open-label="已开启"
|
||||
@change="workModeChange(row)"
|
||||
/>
|
||||
</template>
|
||||
</MjTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -212,20 +212,41 @@ function resetZoom() {
|
||||
|
||||
// ======================== 讲解模式 ========================
|
||||
const currentIndex = ref(0)
|
||||
const answerExpanded = ref(false)
|
||||
const explanationExpanded = ref(false)
|
||||
|
||||
const currentQuestion = computed(() => {
|
||||
return paperDetail.value[currentIndex.value] || null
|
||||
})
|
||||
|
||||
const hasAnswer = computed(() => Boolean(currentQuestion.value?.xkwTitle?.answer))
|
||||
const hasExplanation = computed(() =>
|
||||
Boolean(currentQuestion.value?.xkwTitle?.explanation),
|
||||
)
|
||||
|
||||
function getTitleKey(item: any) {
|
||||
return (
|
||||
item?.xkwTitle?.titleId ?? item?.titleId ?? item?.xkwTitle?.id ?? item?.id
|
||||
)
|
||||
}
|
||||
|
||||
function collapseAnswerAnalysis() {
|
||||
answerExpanded.value = false
|
||||
explanationExpanded.value = false
|
||||
}
|
||||
|
||||
function toggleAnswer() {
|
||||
answerExpanded.value = !answerExpanded.value
|
||||
}
|
||||
|
||||
function toggleExplanation() {
|
||||
explanationExpanded.value = !explanationExpanded.value
|
||||
}
|
||||
|
||||
async function enterExplaining() {
|
||||
initLeftWidth()
|
||||
resetZoom()
|
||||
collapseAnswerAnalysis()
|
||||
phase.value = 'explaining'
|
||||
currentIndex.value = 0
|
||||
await callExplainApi(1)
|
||||
@ -250,6 +271,7 @@ async function callExplainApi(status: number, overrideQuestionId?: string) {
|
||||
function prevQuestion() {
|
||||
if (currentIndex.value > 0) {
|
||||
currentIndex.value--
|
||||
collapseAnswerAnalysis()
|
||||
callExplainApi(1)
|
||||
}
|
||||
}
|
||||
@ -257,12 +279,14 @@ function prevQuestion() {
|
||||
function nextQuestion() {
|
||||
if (currentIndex.value < paperDetail.value.length - 1) {
|
||||
currentIndex.value++
|
||||
collapseAnswerAnalysis()
|
||||
callExplainApi(1)
|
||||
}
|
||||
}
|
||||
|
||||
function goToQuestion(idx: number) {
|
||||
currentIndex.value = idx
|
||||
collapseAnswerAnalysis()
|
||||
callExplainApi(1)
|
||||
}
|
||||
|
||||
@ -402,8 +426,8 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
|
||||
<div class="explain-panel-header">
|
||||
<span class="panel-title">第 {{ currentIndex + 1 }} 题</span>
|
||||
</div>
|
||||
<div class="explain-panel-body">
|
||||
<div class="explain-zoom-layer">
|
||||
<div class="explain-panel-body question-panel-body">
|
||||
<div class="question-zoom-layer">
|
||||
<template
|
||||
v-if="
|
||||
Array.isArray(currentQuestion.xkwTitle?.questionList) &&
|
||||
@ -441,24 +465,34 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
|
||||
<span class="panel-title">答案与解析</span>
|
||||
</div>
|
||||
<div class="explain-panel-body">
|
||||
<div class="explain-zoom-layer">
|
||||
<div class="answer-zoom-layer">
|
||||
<!-- 答案 -->
|
||||
<div class="info-section" v-if="currentQuestion.xkwTitle?.answer">
|
||||
<div class="info-label">【答案】</div>
|
||||
<div class="info-content">
|
||||
<div class="answer-analysis-section">
|
||||
<div class="answer-analysis-header">
|
||||
<span class="answer-analysis-title">【答案】</span>
|
||||
</div>
|
||||
<div v-if="answerExpanded && hasAnswer" class="info-content">
|
||||
<latex-text :text="currentQuestion.xkwTitle.answer" />
|
||||
</div>
|
||||
<div v-else class="answer-analysis-collapsed">
|
||||
{{ hasAnswer ? '答案已收起,点击底部“展开答案”查看' : '暂无答案' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 解析 -->
|
||||
<div
|
||||
class="info-section"
|
||||
v-if="currentQuestion.xkwTitle?.explanation"
|
||||
>
|
||||
<div class="info-label">【解析】</div>
|
||||
<div class="info-content">
|
||||
<div class="answer-analysis-section">
|
||||
<div class="answer-analysis-header">
|
||||
<span class="answer-analysis-title">【解析】</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="explanationExpanded && hasExplanation"
|
||||
class="info-content"
|
||||
>
|
||||
<latex-text :text="currentQuestion.xkwTitle.explanation" />
|
||||
</div>
|
||||
<div v-else class="answer-analysis-collapsed">
|
||||
{{ hasExplanation ? '解析已收起,点击底部“展开解析”查看' : '暂无解析' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计数据 -->
|
||||
@ -531,6 +565,12 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-right">
|
||||
<el-button :disabled="!hasAnswer" @click="toggleAnswer">
|
||||
{{ answerExpanded ? '收起答案' : '展开答案' }}
|
||||
</el-button>
|
||||
<el-button :disabled="!hasExplanation" @click="toggleExplanation">
|
||||
{{ explanationExpanded ? '收起解析' : '展开解析' }}
|
||||
</el-button>
|
||||
<el-button @click="resetZoom">重置缩放 {{ zoomPercent }}%</el-button>
|
||||
<el-button type="danger" @click="endExplaining">结束讲解</el-button>
|
||||
</div>
|
||||
@ -690,6 +730,7 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
|
||||
.explain-panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 14px 20px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
@ -706,12 +747,22 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
|
||||
overflow: hidden auto;
|
||||
}
|
||||
|
||||
.explain-zoom-layer {
|
||||
.question-panel-body {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.question-zoom-layer {
|
||||
width: 100%;
|
||||
transform: scale(var(--explain-zoom-scale));
|
||||
transform-origin: left top;
|
||||
}
|
||||
|
||||
.answer-zoom-layer {
|
||||
zoom: var(--explain-zoom-scale);
|
||||
}
|
||||
|
||||
@supports not (zoom: 1) {
|
||||
.explain-zoom-layer {
|
||||
.answer-zoom-layer {
|
||||
width: calc(100% / var(--explain-zoom-scale));
|
||||
transform: scale(var(--explain-zoom-scale));
|
||||
transform-origin: left top;
|
||||
@ -727,6 +778,33 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
|
||||
border-bottom: 1px dashed #ebeef5;
|
||||
}
|
||||
|
||||
.answer-analysis-section {
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px dashed #ebeef5;
|
||||
}
|
||||
|
||||
.answer-analysis-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.answer-analysis-title {
|
||||
font-weight: 600;
|
||||
color: #246cff;
|
||||
}
|
||||
|
||||
.answer-analysis-collapsed {
|
||||
padding: 18px 16px;
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
text-align: center;
|
||||
background: #f7f8fa;
|
||||
border: 1px dashed #dcdfe6;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
min-width: 60px;
|
||||
font-weight: 600;
|
||||
@ -852,19 +930,24 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
|
||||
.footer-center {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
margin: 0 16px;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.question-nav {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-wrap: nowrap;
|
||||
gap: 6px;
|
||||
justify-content: center;
|
||||
width: max-content;
|
||||
min-width: max-content;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.nav-dot {
|
||||
display: inline-flex;
|
||||
flex: 0 0 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 32px;
|
||||
|
||||
@ -33,8 +33,8 @@ export default defineConfig(async () => {
|
||||
open: true,
|
||||
proxy: {
|
||||
'/api': {
|
||||
// target: 'http://43.136.52.196:9053',
|
||||
target: 'https://admin.xuexiaole.com',
|
||||
target: 'http://43.136.52.196:9053',
|
||||
// target: 'https://admin.xuexiaole.com',
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user