|
@@ -42,6 +42,10 @@
|
|
|
<div class="search_name">分组:</div>
|
|
<div class="search_name">分组:</div>
|
|
|
<el-input v-model="queryParams.groupName" placeholder="请输入" class="select_width" ></el-input>
|
|
<el-input v-model="queryParams.groupName" placeholder="请输入" class="select_width" ></el-input>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ <div class="item_search" v-if="queryParams.questionType == 2" >
|
|
|
|
|
+ (给分规则:少选得<el-input v-model="shaoxuan" @input="(val: any) => HandleScoreInput(null,'shaoxuan', val)" style="width: 50px;" maxlength="4" @change="ScoreYanzheng()"></el-input>分
|
|
|
|
|
+ )
|
|
|
|
|
+ </div>
|
|
|
<div class="item_search">
|
|
<div class="item_search">
|
|
|
<el-button type="primary" style="width: 68px;" @click="EnterSetQuestion()">确定</el-button>
|
|
<el-button type="primary" style="width: 68px;" @click="EnterSetQuestion()">确定</el-button>
|
|
|
</div>
|
|
</div>
|
|
@@ -203,6 +207,9 @@ const queryParams= ref({
|
|
|
groupName:'',//分组名称
|
|
groupName:'',//分组名称
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+//多选题「少选得分」(questionType==2 时使用),与模板 v-model="shaoxuan" 绑定
|
|
|
|
|
+const shaoxuan = ref('')//批量设置多选题给分分布的功能
|
|
|
|
|
+
|
|
|
const templateName=ref('机读卡');
|
|
const templateName=ref('机读卡');
|
|
|
const scanText=ref('开始扫描');
|
|
const scanText=ref('开始扫描');
|
|
|
const templateQuestionCount=ref(0);//模板题目总数量
|
|
const templateQuestionCount=ref(0);//模板题目总数量
|
|
@@ -288,6 +295,56 @@ const onScoreInput = (target: any, key: string, val: any) => {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+// 组件顶层分数输入 ref 映射:当 obj 传 null 时,按 prop 名写入对应的 ref
|
|
|
|
|
+const scoreRefMap: Record<string, any> = { shaoxuan }
|
|
|
|
|
+
|
|
|
|
|
+// 每小题分数输入验证(0.5 分制)
|
|
|
|
|
+// obj:目标对象,为 null 时表示组件顶层 ref(如 shaoxuan);prop:属性名;value:输入值
|
|
|
|
|
+const HandleScoreInput = (obj: any, prop: string, value: any) => {
|
|
|
|
|
+ // 1. 移除所有非数字和非小数点的字符
|
|
|
|
|
+ let newValue = String(value ?? '').replace(/[^\d.]/g, '')
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 确保只有一个小数点
|
|
|
|
|
+ const parts = newValue.split('.')
|
|
|
|
|
+ if (parts.length > 2) {
|
|
|
|
|
+ newValue = parts[0] + '.' + parts.slice(1).join('')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 不允许以多个 0 开头(0.xx 的情况除外)
|
|
|
|
|
+ if (newValue.length > 1 && newValue.startsWith('0') && !newValue.startsWith('0.')) {
|
|
|
|
|
+ newValue = newValue.substring(1)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 校验是否为 0.5 的倍数:数值 ×2 应为整数,否则四舍五入到最近的 0.5 倍数
|
|
|
|
|
+ if (newValue && newValue !== '.' && !isNaN(Number(newValue))) {
|
|
|
|
|
+ const numValue = parseFloat(newValue)
|
|
|
|
|
+ if (numValue > 0) {
|
|
|
|
|
+ const doubled = numValue * 2
|
|
|
|
|
+ if (Math.abs(doubled - Math.round(doubled)) > 0.0001) {
|
|
|
|
|
+ newValue = (Math.round(doubled) / 2).toString()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 动态赋值:obj 为 null 时写入顶层 ref,否则写入 obj[prop]
|
|
|
|
|
+ if (obj == null) {
|
|
|
|
|
+ const targetRef = scoreRefMap[prop]
|
|
|
|
|
+ if (targetRef) targetRef.value = newValue
|
|
|
|
|
+ } else {
|
|
|
|
|
+ obj[prop] = newValue
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 少选得分校验(@change 触发):不能超过每题满分
|
|
|
|
|
+const ScoreYanzheng = () => {
|
|
|
|
|
+ const fullMark = parseFloat(String(queryParams.value.fullMark ?? ''))
|
|
|
|
|
+ const score = parseFloat(String(shaoxuan.value ?? ''))
|
|
|
|
|
+ if (!isNaN(score) && !isNaN(fullMark) && score >= fullMark) {
|
|
|
|
|
+ ElMessage.error('少选得分必须小于每题满分')
|
|
|
|
|
+ shaoxuan.value = ''
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
//打开选择模板弹窗
|
|
//打开选择模板弹窗
|
|
|
const OpenChooseTemplate=()=>{
|
|
const OpenChooseTemplate=()=>{
|
|
|
showChooseTemplate.value=true;
|
|
showChooseTemplate.value=true;
|
|
@@ -862,7 +919,7 @@ onUnmounted(() => {
|
|
|
{
|
|
{
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- gap: 10px;
|
|
|
|
|
|
|
+
|
|
|
margin-right: 10px;
|
|
margin-right: 10px;
|
|
|
.search_name
|
|
.search_name
|
|
|
{
|
|
{
|
|
@@ -874,7 +931,9 @@ onUnmounted(() => {
|
|
|
|
|
|
|
|
.select_width
|
|
.select_width
|
|
|
{
|
|
{
|
|
|
- width: 100px;
|
|
|
|
|
|
|
+ margin-left: 5px;
|
|
|
|
|
+ margin-right: 5px;
|
|
|
|
|
+ width: 90px;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
.item_title
|
|
.item_title
|
|
@@ -886,7 +945,7 @@ onUnmounted(() => {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
justify-content: flex-start;
|
|
justify-content: flex-start;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- gap: 10px;
|
|
|
|
|
|
|
+ gap: 4px;
|
|
|
position: relative;
|
|
position: relative;
|
|
|
|
|
|
|
|
.preview_btn
|
|
.preview_btn
|