Compare commits

...

2 Commits

Author SHA1 Message Date
1378012178@qq.com a481d31fb2 Merge branch 'master' of http://47.115.223.229:8888/yangjun/dbsd_kczx 2025-01-15 10:42:06 +08:00
1378012178@qq.com 1169908e50 1、防作弊
2、课程作业-分组
2025-01-15 10:41:58 +08:00
21 changed files with 2299 additions and 510 deletions

1
.gitignore vendored
View File

@ -32,3 +32,4 @@ pnpm-debug.log*
/.vscode/
/.history/
/svn clear.bat
/dist.zip

View File

@ -33,10 +33,10 @@
<BasicTable @register="registerTable" style="margin-top:-20px;">
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
<TableAction :actions="getTableAction(record)" />
</template>
<!--字段回显插槽-->
<template #htmlSlot="{text}">
<template #htmlSlot="{ text }">
<div v-html="text"></div>
</template>
<!--省市区字段回显插槽-->
@ -49,157 +49,162 @@
</template>
<script lang="ts" name="zyInfoStudent-zyInfoStudent" setup>
import { ref, reactive,defineExpose,unref } from 'vue';
import { useListPage } from '/@/hooks/system/useListPage';
import { columnsKccyStu } from '/@/views/zy/zyInfoStudent/ZyInfoStudent.data';
import { stuKccyList } from '/@/views/zy/zyInfoStudent/ZyInfoStudent.api';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import { ref, reactive, defineExpose, unref } from 'vue';
import { useListPage } from '/@/hooks/system/useListPage';
import { columnsKccyStu } from '/@/views/zy/zyInfoStudent/ZyInfoStudent.data';
import { stuKccyList } from '/@/views/zy/zyInfoStudent/ZyInfoStudent.api';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import WjxWjxxTmlbDjModal from '/@/views/kc/wjxWjxx/components/WjxWjxxTmlbDjModal.vue'
import WjxWjxxTmlbDjjgModal from '/@/views/kc/wjxWjxx/components/WjxWjxxTmlbDjjgModal.vue'
import { useGlobSetting } from '/@/hooks/setting';
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
import { JInput } from '/@/components/Form';
import { useRouter } from 'vue-router';
import WjxWjxxTmlbDjModal from '/@/views/kc/wjxWjxx/components/WjxWjxxTmlbDjModal.vue'
import WjxWjxxTmlbDjjgModal from '/@/views/kc/wjxWjxx/components/WjxWjxxTmlbDjjgModal.vue'
import { useGlobSetting } from '/@/hooks/setting';
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
import { JInput } from '/@/components/Form';
import { useRouter } from 'vue-router';
const { currentRoute } = useRouter();
const { query } = unref(currentRoute);
const { rwbh,xqxn,type,teano } = query;//
const { currentRoute } = useRouter();
const { query } = unref(currentRoute);
const { rwbh, xqxn, type, teano } = query;//
const { createMessage } = useMessage();
const { createMessage } = useMessage();
const queryParam = ref<any>({rwbh,xqxn,teano,atype:'6'});
const queryParam = ref<any>({ rwbh, xqxn, teano, atype: '6' });
const WjxWjxxTmlbDjModalPage = ref();
const WjxWjxxTmlbDjjgModalPage = ref();
const WjxWjxxTmlbDjModalPage = ref();
const WjxWjxxTmlbDjjgModalPage = ref();
//table
const { prefixCls, tableContext} = useListPage({
tableProps: {
api: stuKccyList,
columns:columnsKccyStu,
canResize:false,
useSearchForm: false,
actionColumn: {
width: '150px',
fixed: 'right',
},
showTableSetting: true,
defSort: {
field: 'name',
order: 'ascend',
},
beforeFetch: (params) => {
params.column = 'createTime',params.order = 'desc';//
return Object.assign(params, queryParam.value);
},
//table
const { prefixCls, tableContext } = useListPage({
tableProps: {
api: stuKccyList,
columns: columnsKccyStu,
canResize: false,
useSearchForm: false,
actionColumn: {
width: '150px',
fixed: 'right',
},
});
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
const labelCol = reactive({
xs: { span: 24 },
sm: { span: 7 },
});
const wrapperCol = reactive({
xs: { span: 24 },
sm: { span: 16 },
});
showTableSetting: true,
defSort: {
field: 'name',
order: 'ascend',
},
beforeFetch: (params) => {
params.column = 'createTime', params.order = 'desc';//
return Object.assign(params, queryParam.value);
},
},
});
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
const labelCol = reactive({
xs: { span: 24 },
sm: { span: 7 },
});
const wrapperCol = reactive({
xs: { span: 24 },
sm: { span: 16 },
});
/**
* 答卷详情
*/
function handleDj(record: Recordable) {
let timestamp = new Date().getTime();
let startTimestamp = Date.parse(record.startTime);
if(timestamp<startTimestamp){
createMessage.error('还未到答卷时间!');
return;
}
let endTimestamp = Date.parse(record.endTime+" 23:59:59");
if(timestamp>endTimestamp){
createMessage.error('答卷时间已过!');
return;
}
WjxWjxxTmlbDjModalPage.value.disableSubmit = false;
WjxWjxxTmlbDjModalPage.value.edit(record,false,"6",'xs');
/**
* 答卷详情
*/
function handleDj(record: Recordable) {
let timestamp = new Date().getTime();
let startTimestamp = Date.parse(record.startTime);
if (timestamp < startTimestamp) {
createMessage.error('还未到答卷时间!');
return;
}
let endTimestamp = Date.parse(record.endTime + " 23:59:59");
if (timestamp > endTimestamp) {
createMessage.error('答卷时间已过!');
return;
}
// WjxWjxxTmlbDjModalPage.value.disableSubmit = false;
// WjxWjxxTmlbDjModalPage.value.edit(record, false, "6", 'xs');
var url = "/site/studentDjcy?cytitle=" + record.title + "&cyid=" + record.id + "&cyatype=6&sfzd=0&sfbs=xs";
window.open(url, '_blank')
}
/**
* 答卷详情
*/
function handleDjjg(record: Recordable) {
WjxWjxxTmlbDjjgModalPage.value.disableSubmit = true;
WjxWjxxTmlbDjjgModalPage.value.edit(record.djId, false);
}
/**
* 成功回调
*/
function handleSuccess() {
selectedRowKeys.value = [];
reload();
}
/**
* 操作栏
*/
function getTableAction(record) {
// <a-button type="primary" @click="handleDj(item)" style="margin-left:5px;padding: 0px 8px;background:rgb(28, 132, 198);" v-if="item.flag=='-1'"></a-button>
// <a-button type="primary" style="margin-left:5px;padding: 0px 8px;background:rgb(28, 132, 198);color:#ffffff" v-if="item.flag=='0'" disabled></a-button>
// <a-button type="primary" @click="handleDjjg(item)" style="margin-left:5px;padding: 0px 8px;background:rgb(28, 132, 198);" v-if="item.flag=='1'"></a-button>
if (record.flag == '-1') {
var list = [
{
label: '开始测验',
onClick: handleDj.bind(null, record),
},
];
return list;
} else if (record.flag == '0') {
return null;
} else {
var list = [
{
label: '查看结果',
onClick: handleDjjg.bind(null, record),
},
];
return list;
}
/**
* 答卷详情
*/
function handleDjjg(record: Recordable) {
WjxWjxxTmlbDjjgModalPage.value.disableSubmit = true;
WjxWjxxTmlbDjjgModalPage.value.edit(record.djId,false);
}
}
/**
* 成功回调
*/
function handleSuccess() {
selectedRowKeys.value = [];
reload();
}
/**
* 操作栏
*/
function getTableAction(record) {
// <a-button type="primary" @click="handleDj(item)" style="margin-left:5px;padding: 0px 8px;background:rgb(28, 132, 198);" v-if="item.flag=='-1'"></a-button>
// <a-button type="primary" style="margin-left:5px;padding: 0px 8px;background:rgb(28, 132, 198);color:#ffffff" v-if="item.flag=='0'" disabled></a-button>
// <a-button type="primary" @click="handleDjjg(item)" style="margin-left:5px;padding: 0px 8px;background:rgb(28, 132, 198);" v-if="item.flag=='1'"></a-button>
if(record.flag == '-1'){
var list = [
{
label: '开始测验',
onClick: handleDj.bind(null, record),
},
];
return list;
}else if(record.flag == '0'){
return null;
}else{
var list = [
{
label: '查看结果',
onClick: handleDjjg.bind(null, record),
},
];
return list;
}
}
/**
* 查询
*/
function searchQuery() {
selectedRowKeys.value = [];
reload();
}
/**
* 查询
*/
function searchQuery() {
selectedRowKeys.value = [];
reload();
}
</script>
<style lang="less" scoped>
.jeecg-basic-table-form-container {
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
.query-group-cust{
width: calc(50% - 15px);
min-width: 100px !important;
}
.query-group-split-cust{
width: 30px;
display: inline-block;
text-align: center
}
.jeecg-basic-table-form-container {
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
.query-group-cust {
width: calc(50% - 15px);
min-width: 100px !important;
}
.query-group-split-cust {
width: 30px;
display: inline-block;
text-align: center
}
}
</style>

View File

@ -30,39 +30,44 @@
</a-form>
</div>
<a-row>
<a-col :span="24" v-for="(item, index) in tableData" :key="index" style="padding: 0px 0px 5px 5px;overflow:hidden;">
<a-col :span="24" v-for="(item, index) in tableData" :key="index"
style="padding: 0px 0px 5px 5px;overflow:hidden;">
<div style="width: 100%; "></div>
<a-card style="height: 130px;border: 1px solid #eef1f2; border-radius:5px; margin:3px 5px">
<div class="rotate" :style="classFun(item.flag)">{{getStatus(item.flag)}}</div>
<div class="rotate" :style="classFun(item.flag)">{{ getStatus(item.flag) }}</div>
<a-row style="top: -48px;position: relative;">
<a-col :lg="{span:0}" :span="4" :push="20">
<a-col :lg="{ span: 0 }" :span="4" :push="20">
<div class="wczt-sm" :style="classFun_sm(item.flag)">{{ getStatus(item.flag) }}</div>
</a-col>
<a-col :span="24" style="margin-bottom: 10px;height:53px;overflow:hidden;">
<div style="font-size: 18px;font-weight: bold;">{{item.title}}</div>
<div style="font-size: 18px;font-weight: bold;">{{ item.title }}</div>
</a-col>
<a-col :span="24" class="zyCon">
<a-row>
<a-col :span="18" class="zyCon" style=" color: #666;">
时间{{dayjs(item.startTime).format('MM.DD HH-mm')}} - {{dayjs(item.endTime).format('MM.DD HH-mm')}}
时间{{ dayjs(item.startTime).format('MM.DD HH-mm') }} - {{ dayjs(item.endTime).format('MM.DD HH-mm') }}
</a-col>
<a-col :span="6" style="text-align:center;">
<a-button type="primary" @click="handleDj(item)" style="margin-left:5px;padding: 0px 8px;background:#1ab394;" v-if="item.flag=='-1'">开始测验</a-button>
<a-button type="primary" style="margin-left:5px;padding: 0px 8px;background:#1ab394;color:#ffffff" v-if="item.flag=='0'" disabled>正在批卷</a-button>
<a-button type="primary" @click="handleDjjg(item)" style="margin-left:5px;padding: 0px 8px;background:#1ab394;" v-if="item.flag=='1'">查看结果</a-button>
<a-button type="primary" @click="handleDj(item, true)"
style="margin-left:5px;padding: 0px 8px;background:#1ab394;" v-if="item.flag == '-1'">开始测验</a-button>
<a-button type="primary" style="margin-left:5px;padding: 0px 8px;background:#1ab394;color:#ffffff"
v-if="item.flag == '0'" disabled>正在批卷</a-button>
<a-button type="primary" @click="handleDjjg(item)"
style="margin-left:5px;padding: 0px 8px;background:#1ab394;" v-if="item.flag == '1'">查看结果</a-button>
</a-col>
</a-row>
</a-col>
</a-row>
</a-card>
</a-col>
<a-col :span="24">
<div v-show="tableData.length>0">
<a-pagination v-model="current" :total="total" @change="handlePageChange" :pageSize="pageSize" style="text-align: right;"/>
<div v-show="tableData.length > 0">
<a-pagination v-model="current" :total="total" @change="handlePageChange" :pageSize="pageSize"
style="text-align: right;" />
</div>
<div v-show="tableData.length==0">
<a-empty/>
<div v-show="tableData.length == 0">
<a-empty />
</div>
</a-col>
</a-row>
@ -72,198 +77,211 @@
</template>
<script lang="ts" name="wjxWjxx-wjxWjxx" setup>
import {ref, reactive, defineExpose, unref, onMounted} from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { Input, Popover, Pagination, Empty } from 'ant-design-vue';
import WjxWjxxTmlbDjModal from '/@/views/kc/wjxWjxx/components/WjxWjxxTmlbDjModal.vue'
import WjxWjxxTmlbDjjgModal from '/@/views/kc/wjxWjxx/components/WjxWjxxTmlbDjjgModal.vue'
import {useRouter} from "vue-router";
import {useMessage} from "/@/hooks/web/useMessage";
import dayjs from 'dayjs';
import { ref, reactive, defineExpose, unref, onMounted } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { Input, Popover, Pagination, Empty } from 'ant-design-vue';
import WjxWjxxTmlbDjModal from '/@/views/kc/wjxWjxx/components/WjxWjxxTmlbDjModal.vue'
import WjxWjxxTmlbDjjgModal from '/@/views/kc/wjxWjxx/components/WjxWjxxTmlbDjjgModal.vue'
import { useRouter } from "vue-router";
import { useMessage } from "/@/hooks/web/useMessage";
import dayjs from 'dayjs';
//
const { currentRoute } = useRouter();
const { query } = unref(currentRoute);
const { rwbh,xqxn,type,teano } = query;//
const { createMessage } = useMessage();
const APagination = Pagination;
const queryParam = ref<any>({});
const current = ref<number>(0);
const total = ref<number>(0);
const pageNo = ref<number>(0);
const pageSize = ref<number>(6);
const tableData = ref<any>([]);
//
const { currentRoute } = useRouter();
const { query } = unref(currentRoute);
const { rwbh, xqxn, type, teano } = query;//
const { createMessage } = useMessage();
const APagination = Pagination;
const queryParam = ref<any>({});
const current = ref<number>(0);
const total = ref<number>(0);
const pageNo = ref<number>(0);
const pageSize = ref<number>(6);
const tableData = ref<any>([]);
const WjxWjxxTmlbDjModalPage = ref();
const WjxWjxxTmlbDjjgModalPage = ref();
const WjxWjxxTmlbDjModalPage = ref();
const WjxWjxxTmlbDjjgModalPage = ref();
const labelCol = reactive({
xs: { span: 24 },
sm: { span: 7 },
});
const wrapperCol = reactive({
xs: { span: 24 },
sm: { span: 16 },
const labelCol = reactive({
xs: { span: 24 },
sm: { span: 7 },
});
const wrapperCol = reactive({
xs: { span: 24 },
sm: { span: 16 },
});
/**
* 答卷详情
*/
function handleDj(record: Recordable, isTesting = false) {
let timestamp = new Date().getTime();
let startTimestamp = Date.parse(record.startTime);
if (timestamp < startTimestamp) {
createMessage.error('还未到答卷时间!');
return;
}
let endTimestamp = Date.parse(record.endTime);
if (timestamp > endTimestamp) {
createMessage.error('答卷时间已过!');
return;
}
// WjxWjxxTmlbDjModalPage.value.disableSubmit = false;
// WjxWjxxTmlbDjModalPage.value.edit(record,false,"6",'xs');
// cytitle, cyid, cyatype, sfzd, sfbs
var url = "/site/studentDjcy?cytitle=" + record.title + "&cyid=" + record.id + "&cyatype=6&sfzd=0&sfbs=xs";
window.open(url, '_blank')
}
/**
* 答卷详情
*/
function handleDjjg(record: Recordable) {
WjxWjxxTmlbDjjgModalPage.value.disableSubmit = true;
WjxWjxxTmlbDjjgModalPage.value.edit(record.djId, false);
}
function reload() {
queryParam.value.pageNo = current.value;
queryParam.value.pageSize = pageSize.value;
queryParam.value.rwbh = rwbh;
queryParam.value.xqxn = xqxn;
queryParam.value.teano = teano;
queryParam.value.atype = "6";
defHttp.get({ url: '/wjxWjxx/wjxWjxx/stuList', params: queryParam.value }).then(res => {
// console.log(`🚀 ~ defHttp.get ~ res:`, res)
total.value = res.total;
pageNo.value = res.pages;
current.value = res.current;
tableData.value = res.records;
});
}
/**
* 答卷详情
*/
function handleDj(record: Recordable) {
let timestamp = new Date().getTime();
let startTimestamp = Date.parse(record.startTime);
if(timestamp<startTimestamp){
createMessage.error('还未到答卷时间!');
return;
}
let endTimestamp = Date.parse(record.endTime);
if(timestamp>endTimestamp){
createMessage.error('答卷时间已过!');
return;
}
// WjxWjxxTmlbDjModalPage.value.disableSubmit = false;
// WjxWjxxTmlbDjModalPage.value.edit(record,false,"6",'xs');
// cytitle, cyid, cyatype, sfzd, sfbs
var url ="/site/studentDjcy?cytitle="+record.title+"&cyid="+record.id+"&cyatype=6&sfzd=0&sfbs=xs";
window.open(url,'_blank')
}
/**
* 答卷详情
*/
function handleDjjg(record: Recordable) {
WjxWjxxTmlbDjjgModalPage.value.disableSubmit = true;
WjxWjxxTmlbDjjgModalPage.value.edit(record.djId,false);
}
function reload(){
queryParam.value.pageNo = current.value;
queryParam.value.pageSize = pageSize.value;
queryParam.value.rwbh = rwbh;
queryParam.value.xqxn = xqxn;
queryParam.value.teano = teano;
queryParam.value.atype = "6";
defHttp.get({ url: '/wjxWjxx/wjxWjxx/stuList', params: queryParam.value }).then(res => {
// console.log(`🚀 ~ defHttp.get ~ res:`, res)
total.value = res.total;
pageNo.value = res.pages;
current.value = res.current;
tableData.value = res.records;
});
}
/**
* 成功回调
*/
function handleSuccess() {
reload();
}
/**
* 查询
*/
function searchQuery() {
total.value = 1;
handlePageChange(1);
}
/**
* 重置
*/
function searchReset() {
queryParam.value = {};
total.value = 1;
handlePageChange(1);
}
function handlePageChange(record){
current.value = record;
reload();
}
onMounted(() => {
searchQuery();
});
function classFun(type){
if(type == '-1'){
return 'background: linear-gradient(180deg, #63c4e4, #24a7d5);';
}else if(type == '0'){
return 'background: linear-gradient(180deg, #fea317, #ff7d28);';
}else if(type == '1'){
return 'background: linear-gradient(180deg, #fb8c7f, #f56670)';
}
/**
* 成功回调
*/
function handleSuccess() {
reload();
}
/**
* 查询
*/
function searchQuery() {
total.value = 1;
handlePageChange(1);
}
/**
* 重置
*/
function searchReset() {
queryParam.value = {};
total.value = 1;
handlePageChange(1);
}
function handlePageChange(record) {
current.value = record;
reload();
}
onMounted(() => {
searchQuery();
});
function classFun(type) {
if (type == '-1') {
return 'background: linear-gradient(180deg, #63c4e4, #24a7d5);';
} else if (type == '0') {
return 'background: linear-gradient(180deg, #fea317, #ff7d28);';
} else if (type == '1') {
return 'background: linear-gradient(180deg, #fb8c7f, #f56670)';
}
}
function classFun_sm(type) {
if (type == '-1') {
return 'color: rgb(215, 72, 82);'; //
} else if (type == '0') {
return 'color: rgb(225, 95, 10);'; //
} else if (type == '1') {
return 'color: rgb(5, 136, 182);'; //
}
return 'color: rgb(215, 72, 82);'; //
} else if (type == '0') {
return 'color: rgb(225, 95, 10);'; //
} else if (type == '1') {
return 'color: rgb(5, 136, 182);'; //
}
}
function getStatus(flag){
if(flag == '-1'){
return "待提交";
}else if(flag == '0'){
return "待评分";
}else if(flag == '1'){
return "已完成";
}
function getStatus(flag) {
if (flag == '-1') {
return "待提交";
} else if (flag == '0') {
return "待评分";
} else if (flag == '1') {
return "已完成";
}
}
</script>
<style lang="less" scoped>
.wczt-sm {
border-radius: 3px;
color: rgb(0, 0, 0); /* 设置文字颜色为白色 */
font-weight: bold;
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
height: 100%; /* 确保容器有高度,否则垂直居中可能不会按预期工作 */
text-align: center; /* 如果有多行文本,保证它们也居中 */
.wczt-sm {
border-radius: 3px;
color: rgb(0, 0, 0);
/* 设置文字颜色为白色 */
font-weight: bold;
display: flex;
justify-content: center;
/* 水平居中 */
align-items: center;
/* 垂直居中 */
height: 100%;
/* 确保容器有高度,否则垂直居中可能不会按预期工作 */
text-align: center;
/* 如果有多行文本,保证它们也居中 */
}
.jeecg-basic-table-form-container {
padding: 0;
width: 99%;
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
.jeecg-basic-table-form-container {
padding: 0;
width: 99%;
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
.query-group-cust{
width: calc(50% - 15px);
min-width: 100px !important;
}
.query-group-split-cust{
width: 30px;
display: inline-block;
text-align: center
}
.query-group-cust {
width: calc(50% - 15px);
min-width: 100px !important;
}
.jeecg-basic-table-form-container .ant-form {
padding: 12px 10px 0px 10px;
margin-bottom: 0px;
background-color: #fff;
border-radius: 2px;
.query-group-split-cust {
width: 30px;
display: inline-block;
text-align: center
}
}
.ellipsis {
overflow: hidden; /* 确保超出容器的内容被裁剪 */
white-space: nowrap; /* 确保文本在一行内显示 */
text-overflow: ellipsis; /* 超出部分显示省略号 */
}
.zyCon{
line-height: 30px;
}
.rotate {
.jeecg-basic-table-form-container .ant-form {
padding: 12px 10px 0px 10px;
margin-bottom: 0px;
background-color: #fff;
border-radius: 2px;
}
.ellipsis {
overflow: hidden;
/* 确保超出容器的内容被裁剪 */
white-space: nowrap;
/* 确保文本在一行内显示 */
text-overflow: ellipsis;
/* 超出部分显示省略号 */
}
.zyCon {
line-height: 30px;
}
.rotate {
transform: rotate(45deg);
background: linear-gradient(90deg, #93d861, #8ddc53, #35c495);
color: #fff;

View File

@ -144,14 +144,25 @@
valueFormat="YYYY-MM-DD HH:mm"
format="YYYY-MM-DD HH:mm"
style="width: 100%"
:disabled="editDisabled"
/>
</a-form-item>
</a-col>
<a-col :span="24" class="jbxx-cytm">
<a-form-item label="防作弊">
<a-radio-group v-model:value="zyInfo.fzbms" :options="fzbOptions" :disabled="editDisabled"/><span style="color:#AAAAAA;">防作弊功能仅限PC端不支持文件题测验</span>
</a-form-item>
</a-col>
<a-col :span="24" class="jbxx-cytm">
<a-form-item label="作答时限">
<a-input-number id="inputNumber" v-model:value="zyInfo.zdsx" :min="0" :max="120" :controls="false" :precision="0" :disabled="editDisabled"/>
</a-form-item>
</a-col>
<a-col :span="24" class="jbxx-ms">
<a-form-item label="描述" v-bind="validateInfos.content">
<j-editor v-model:value="zyInfo.content" v-if="zyyqShow" @blur="handleZyyqShow(0)" />
<div style="color: #777777" v-html="zyInfo.content" v-if="!zyyqShow"></div>
<div @click="handleZyyqShow(1)" class="tishi" style="width: 100%" :disabled="!editDisabled">温馨提示点击可编辑描述</div>
<div v-show="!editDisabled" @click="handleZyyqShow(1)" class="tishi" style="width: 100%" :disabled="!editDisabled">温馨提示点击可编辑描述</div>
</a-form-item>
</a-col>
</div>
@ -611,7 +622,7 @@
</a-col>
<a-col :span="24" style="text-align: center; margin-top: 10px; margin-bottom: 40px">
<a-button type="primary" @click="submitForm" v-if="!editDisabled">保存</a-button>
<a-button type="primary" @click="submitForm" v-if="!editDisabled">保存2</a-button>
<a-button type="primary" @click="reloadZy" style="margin-left: 15px">返回</a-button>
</a-col>
</a-row>
@ -1331,7 +1342,7 @@ const total = ref<number>(0);
const pageNo = ref<number>(0);
const pageSize = ref<number>(100);
const editDisabled = ref<boolean>(false);
const zyInfo = ref<any>({});
const zyInfo = ref<any>({fzbms:'N'});
const dataKhnr = ref<any>([]);
const confirmLoading = ref<boolean>(false);
const zyyqShow = ref<boolean>(false);
@ -1393,7 +1404,10 @@ const labelCol4 = reactive({
const wrapperCol4 = reactive({
sm: { span: 12 },
});
const fzbOptions = ref([
{ label: '关闭', value: 'N' },
{ label: '开启', value: 'Y' },
]);
//
function handleFenxiang(record){
console.log(record);
@ -2323,6 +2337,18 @@ async function submitForm() {
confirmLoading.value = false;
return;
}
//
if (zyInfo.value.fzbms == 'Y') {
let falg = true
tiganData.value.forEach(item => {
if (item.wjType == 8) falg = false
});
if (!falg) {
createMessage.error('包含文件题时,无法启用防作弊模式,请主动关闭!');
confirmLoading.value = false;
return;
}
}
model.list = values;
console.log('😨', model);
@ -2365,6 +2391,7 @@ function handleEdit(record, type) {
}
editDisabled.value = type;
zyInfo.value = record;
if(!zyInfo.value.fzbms)zyInfo.value.fzbms = 'N'
dataKhnr.value = [];
mainId.value = record.id;

View File

@ -148,6 +148,16 @@
/>
</a-form-item>
</a-col>
<a-col :span="24" class="jbxx-cytm">
<a-form-item label="防作弊">
<a-radio-group v-model:value="zyInfo.fzbms" :options="fzbOptions" :disabled="editDisabled"/>
</a-form-item>
</a-col>
<a-col :span="24" class="jbxx-cytm">
<a-form-item label="作答时限">
<a-input-number id="inputNumber" v-model:value="zyInfo.zdsx" :min="0" :max="120" :controls="false" :precision="0" :disabled="editDisabled"/>
</a-form-item>
</a-col>
<a-col :span="24" class="jbxx-ms">
<a-form-item label="描述" v-bind="validateInfos.content">
<p v-html="zyInfo.content"></p>
@ -1334,6 +1344,10 @@ const kczyzb = ref<number>(0);
const qmkszb = ref<number>(0);
const ktcyzb = ref<number>(0);
const importOpen = ref<boolean>(false);
const fzbOptions = ref([
{ label: '关闭', value: 'N' },
{ label: '开启', value: 'Y' },
]);
//
const disabledDate = (current: Dayjs) => {

View File

@ -0,0 +1,92 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from "/@/hooks/web/useMessage";
const { createConfirm } = useMessage();
enum Api {
list = '/wjxDjxxFzbtj/wjxDjxxFzbtj/list',
save='/wjxDjxxFzbtj/wjxDjxxFzbtj/add',
edit='/wjxDjxxFzbtj/wjxDjxxFzbtj/edit',
deleteOne = '/wjxDjxxFzbtj/wjxDjxxFzbtj/delete',
deleteBatch = '/wjxDjxxFzbtj/wjxDjxxFzbtj/deleteBatch',
importExcel = '/wjxDjxxFzbtj/wjxDjxxFzbtj/importExcel',
exportXls = '/wjxDjxxFzbtj/wjxDjxxFzbtj/exportXls',
getExamTime = '/wjxDjxxFzbtj/wjxDjxxFzbtj/getExamTime'
}
/**
* api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* api
*/
export const getImportUrl = Api.importExcel;
/**
*
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
*
* @param params
* @param handleSuccess
*/
export const deleteOne = (params,handleSuccess) => {
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
/**
*
* @param params
* @param handleSuccess
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
});
}
/**
*
* @param params
* @param isUpdate
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params }, { isTransformResponse: false });
}
/**
*
* @param params
* @param isUpdate
*/
export const fzbSaveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params }, { isTransformResponse: false });
}
/**
* redis中获取答题结束时间
* @param params
* @param isUpdate
*/
export const getExamTime = (params) => {
return defHttp.post({ url: Api.getExamTime, data:params }, { isTransformResponse: false });
}

View File

@ -0,0 +1,84 @@
import {BasicColumn} from '/@/components/Table';
import {FormSchema} from '/@/components/Table';
import { rules} from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
//列表数据
export const columns: BasicColumn[] = [
{
title: 'wjx_djxx表id',
align: "center",
dataIndex: 'djxxId'
},
{
title: '学生姓名',
align: "center",
dataIndex: 'studentName'
},
{
title: '问卷编号(提交问卷、查询问卷会用到)',
align: "center",
dataIndex: 'vid'
},
{
title: '答卷编号',
align: "center",
dataIndex: 'jid'
},
// 作弊类型 1、退出全屏2、切屏3、考试超时4、开始答题
{
title: '作弊类型',
align: "center",
dataIndex: 'type'
},
{
title: '作弊内容XX时间XX操作',
align: "center",
dataIndex: 'content'
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: 'wjx_djxx表id',
field: 'djxxId',
component: 'Input',
},
{
label: '学生姓名',
field: 'studentName',
component: 'Input',
},
{
label: '问卷编号(提交问卷、查询问卷会用到)',
field: 'vid',
component: 'Input',
},
{
label: '答卷编号',
field: 'jid',
component: 'Input',
},
// // 作弊类型 1、退出全屏2、切屏3、考试超时4、开始答题
{
label: '作弊类型',
field: 'type',
component: 'InputNumber',
},
{
label: '作弊内容XX时间XX操作',
field: 'content',
component: 'InputTextArea',
},
// TODO 主键隐藏字段目前写死为ID
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];

View File

@ -0,0 +1,215 @@
<template>
<div>
<!--查询区域-->
<div class="jeecg-basic-table-form-container">
<a-form @keyup.enter.native="searchQuery" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-row :gutter="24">
</a-row>
</a-form>
</div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
</template>
<!--字段回显插槽-->
<template #htmlSlot="{text}">
<div v-html="text"></div>
</template>
<!--省市区字段回显插槽-->
<!--<template #pcaSlot="{text}">
{{ getAreaTextByCode(text) }}
</template>-->
<template #fileSlot="{text}">
<span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
</template>
</BasicTable>
<!-- 表单区域 -->
<WjxDjxxFzbtjModal ref="registerModal" @success="handleSuccess"></WjxDjxxFzbtjModal>
</div>
</template>
<script lang="ts" name="wjxDjxxFzbtj-wjxDjxxFzbtj" setup>
import { ref, reactive } from 'vue';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { columns } from './WjxDjxxFzbtj.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './WjxDjxxFzbtj.api';
import { downloadFile } from '/@/utils/common/renderUtils';
import WjxDjxxFzbtjModal from './components/WjxDjxxFzbtjModal.vue'
const queryParam = ref<any>({});
const toggleSearchStatus = ref<boolean>(false);
const registerModal = ref();
//table
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
tableProps: {
title: 'wjx_djxx_fzbtj',
api: list,
columns,
canResize:false,
useSearchForm: false,
actionColumn: {
width: 120,
fixed: 'right',
},
beforeFetch: (params) => {
params.column = '',params.order = '';//
return Object.assign(params, queryParam.value);
},
},
exportConfig: {
name: "wjx_djxx_fzbtj",
url: getExportUrl,
},
importConfig: {
url: getImportUrl,
success: handleSuccess
},
});
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
const labelCol = reactive({
xs: { span: 24 },
sm: { span: 7 },
});
const wrapperCol = reactive({
xs: { span: 24 },
sm: { span: 16 },
});
/**
* 新增事件
*/
function handleAdd() {
registerModal.value.disableSubmit = false;
registerModal.value.add();
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
registerModal.value.disableSubmit = false;
registerModal.value.edit(record);
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
registerModal.value.disableSubmit = true;
registerModal.value.edit(record);
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({ id: record.id }, handleSuccess);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
];
}
/**
* 下拉操作栏
*/
function getDropDownAction(record) {
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
}, {
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
}
}
]
}
/**
* 查询
*/
function searchQuery() {
reload();
}
/**
* 重置
*/
function searchReset() {
queryParam.value = {};
selectedRowKeys.value = [];
//
reload();
}
</script>
<style lang="less" scoped>
.jeecg-basic-table-form-container {
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
.query-group-cust{
width: calc(50% - 15px);
min-width: 100px !important;
}
.query-group-split-cust{
width: 30px;
display: inline-block;
text-align: center
}
}
</style>

View File

@ -0,0 +1,157 @@
<template>
<a-spin :spinning="confirmLoading">
<a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-row>
<a-col :span="24">
<a-form-item label="wjx_djxx表id" v-bind="validateInfos.djxxId">
<a-input v-model:value="formData.djxxId" placeholder="请输入wjx_djxx表id" :disabled="disabled"></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="学生姓名" v-bind="validateInfos.studentName">
<a-input v-model:value="formData.studentName" placeholder="请输入学生姓名" :disabled="disabled"></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="问卷编号(提交问卷、查询问卷会用到)" v-bind="validateInfos.vid">
<a-input v-model:value="formData.vid" placeholder="请输入问卷编号(提交问卷、查询问卷会用到)" :disabled="disabled"></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="答卷编号" v-bind="validateInfos.jid">
<a-input v-model:value="formData.jid" placeholder="请输入答卷编号" :disabled="disabled"></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="作弊类型" v-bind="validateInfos.type">
<a-input-number v-model:value="formData.type" placeholder="请输入作弊类型" style="width: 100%" :disabled="disabled"/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="作弊内容XX时间XX操作" v-bind="validateInfos.content">
<a-textarea v-model:value="formData.content" rows="4" placeholder="请输入作弊内容XX时间XX操作" :disabled="disabled"/>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-spin>
</template>
<script lang="ts" setup>
import { ref, reactive, defineExpose, nextTick, defineProps, computed, onMounted } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
import { getValueType } from '/@/utils';
import { saveOrUpdate } from '../WjxDjxxFzbtj.api';
import { Form } from 'ant-design-vue';
const props = defineProps({
formDisabled: { type: Boolean, default: false },
formData: { type: Object, default: ()=>{} },
formBpm: { type: Boolean, default: true }
});
const formRef = ref();
const useForm = Form.useForm;
const emit = defineEmits(['register', 'ok']);
const formData = reactive<Record<string, any>>({
id: '',
djxxId: '',
studentName: '',
vid: '',
jid: '',
type: undefined,
content: '',
});
const { createMessage } = useMessage();
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
const confirmLoading = ref<boolean>(false);
//
const validatorRules = {
};
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: true });
//
const disabled = computed(()=>{
if(props.formBpm === true){
if(props.formData.disabled === false){
return false;
}else{
return true;
}
}
return props.formDisabled;
});
/**
* 新增
*/
function add() {
edit({});
}
/**
* 编辑
*/
function edit(record) {
nextTick(() => {
resetFields();
//
Object.assign(formData, record);
});
}
/**
* 提交数据
*/
async function submitForm() {
//
await validate();
confirmLoading.value = true;
const isUpdate = ref<boolean>(false);
//
let model = formData;
if (model.id) {
isUpdate.value = true;
}
//
for (let data in model) {
//
if (model[data] instanceof Array) {
let valueType = getValueType(formRef.value.getProps, data);
//
if (valueType === 'string') {
model[data] = model[data].join(',');
}
}
}
await saveOrUpdate(model, isUpdate.value)
.then((res) => {
if (res.success) {
createMessage.success(res.message);
emit('ok');
} else {
createMessage.warning(res.message);
}
})
.finally(() => {
confirmLoading.value = false;
});
}
defineExpose({
add,
edit,
submitForm,
});
</script>
<style lang="less" scoped>
.antd-modal-form {
min-height: 500px !important;
overflow-y: auto;
padding: 24px 24px 24px 24px;
}
</style>

View File

@ -0,0 +1,75 @@
<template>
<a-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
<WjxDjxxFzbtjForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></WjxDjxxFzbtjForm>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, nextTick, defineExpose } from 'vue';
import WjxDjxxFzbtjForm from './WjxDjxxFzbtjForm.vue'
const title = ref<string>('');
const width = ref<number>(800);
const visible = ref<boolean>(false);
const disableSubmit = ref<boolean>(false);
const registerForm = ref();
const emit = defineEmits(['register', 'success']);
/**
* 新增
*/
function add() {
title.value = '新增';
visible.value = true;
nextTick(() => {
registerForm.value.add();
});
}
/**
* 编辑
* @param record
*/
function edit(record) {
title.value = disableSubmit.value ? '详情' : '编辑';
visible.value = true;
nextTick(() => {
registerForm.value.edit(record);
});
}
/**
* 确定按钮点击事件
*/
function handleOk() {
registerForm.value.submitForm();
}
/**
* form保存回调事件
*/
function submitCallback() {
handleCancel();
emit('success');
}
/**
* 取消按钮回调事件
*/
function handleCancel() {
visible.value = false;
}
defineExpose({
add,
edit,
disableSubmit,
});
</script>
<style>
/**隐藏样式-modal确定按钮 */
.jee-hidden {
display: none !important;
}
</style>

View File

@ -5,34 +5,66 @@
<!-- 页头 -->
<headerPage />
<!-- 主体部分 -->
<a-layout-content>
<a-layout-content v-show="loadTestInfo" class="ts_container">
<div>
<strong class="ts_title" style="font-size: 17px;"><a-spin />正在加载考试信息请稍等</strong>
</div>
</a-layout-content>
<a-layout-content v-show="!loadTestInfo && !showContent" class="ts_container">
<div>
<strong class="ts_title" style="font-size: 17px;color:#8B0000;">考试须知</strong>
<ol class="ts_rules-list">
<li><strong>1保持全屏模式</strong>在考试过程中请始终保持全屏模式避免使用ESC键或点击出现在屏幕顶部的退出全屏按钮以防意外退出考试界面</li>
<li><strong>2禁止切换屏幕</strong>考试期间请勿进行任何切屏操作以确保考试的连续性和公正性</li>
<li v-show="zdsx && !finishTime"><strong>
3</strong><strong style="color: red;">作答时限
</strong>点击开始测验后开始计时本次作答时限为<span style="color: red;">{{ zdsx }}分钟</span>
</li>
<li v-show="zdsx && !!finishTime"><strong>
3</strong><strong style="color: red;">作答时限
</strong>本次测验于<span style="color: red;">{{ finishTime }}</span><span>结束</span>
</li>
</ol>
</div>
<a-button type="primary" @click="fsFUNC()" class="ts_center-button">{{ testingTips }}</a-button>
</a-layout-content>
<a-layout-content v-show="!loadTestInfo && showContent">
<a-spin :spinning="confirmLoading" v-if="showType == 1">
<a-row style="min-height: calc(80vh); background: #fcfcfc">
<a-col :span="24" style="overflow-y: scroll;width: 100%; word-wrap: break-word; white-space: normal;">
<div style="text-align: center; width: 100%; font-weight: bold; font-size: 20px;margin-top: 50px;margin-bottom: 40px;"> {{ title }}</div>
<div style="text-align: center; width: 100%; font-weight: bold; font-size: 20px;color:red;" v-if="sfsxs"> 您不是此测验的学生不能进行提交</div>
<div
style="text-align: center; width: 100%; font-weight: bold; font-size: 20px;margin-top: 50px;margin-bottom: 40px;">
{{ title }}</div>
<div style="text-align: center; width: 100%; font-weight: bold; font-size: 20px;color:red;"
v-if="sfsxs"> 您不是此测验的学生不能进行提交</div>
<div class="con-class" v-html="content"></div>
<!-- 题干信息 -->
<div style="width: 100%; padding: 0 10px; margin: 0 auto" v-for="(item, index) in tiganData" :key="index">
<div style="width: 100%; padding: 0 10px; margin: 0 auto" v-for="(item, index) in tiganData"
:key="index">
<!-- 单选题 -->
<div style="width: 100%" v-if="(item.wjType == 3 || item.wjType == '3') && item.wjSubtype==null">
<div style="text-align: left; width: 100%; font-weight: bold; line-height: 80px; font-size: 18px">单选题</div>
<div style="width: 100%" v-if="(item.wjType == 3 || item.wjType == '3') && item.wjSubtype == null">
<div style="text-align: left; width: 100%; font-weight: bold; line-height: 80px; font-size: 18px">
单选题</div>
<a-card>
<template #title>
<div style="white-space: pre-wrap; word-wrap: break-word"><span>{{ index + 1 }}</span><span v-html="item.wjTitle" style="white-space: pre-wrap; word-wrap: break-word"></span></div>
<div style="white-space: pre-wrap; word-wrap: break-word"><span>{{ index + 1 }}</span><span
v-html="item.wjTitle" style="white-space: pre-wrap; word-wrap: break-word"></span></div>
<div v-if="item.picPath">
<j-upload v-model:value="item.picPath" fileType="image" disabled :maxCount="item.picPath.split(',').length" :buttonVisible="true" ></j-upload>
<j-upload v-model:value="item.picPath" fileType="image" disabled
:maxCount="item.picPath.split(',').length" :buttonVisible="true"></j-upload>
</div>
<div style="text-align: right;">题目分值 <span class="answer-word">{{ item.wjScore }}</span>
</div>
<div style="text-align: right;">题目分值 <span class="answer-word">{{ item.wjScore }}</span> </div>
</template>
<!-- <template #extra>
<span style="margin-left: 40px" v-if="isShow"
>题目分值 <span class="answer-word">{{ item.wjScore }}</span> </span
>
</template> -->
<a-radio-group v-model:value="item.itemSelected" style="width: 100%" size="default" :disabled="disabled">
<a-radio-group v-model:value="item.itemSelected" style="width: 100%" size="default"
:disabled="disabled">
<div style="width: 100%" v-for="(tmxx, index) in item.wjxWjxxTmxxList" :key="index">
<a-radio :value="tmxx.itemIndex + ``" style="width: 100%; margin-bottom: 5px">
<span v-html="tmxx.itemTitle" style="width: 80%; font-size: 16px"></span>
@ -43,17 +75,22 @@
</div>
<!-- 判断题 -->
<div style="width: 100%" v-else-if="item.wjType == 3 && item.wjSubtype == 305">
<div style="text-align: left; width: 100%; font-weight: bold; line-height: 80px; font-size: 18px">判断题</div>
<div style="text-align: left; width: 100%; font-weight: bold; line-height: 80px; font-size: 18px">
判断题</div>
<a-card>
<template #title>
<div style="white-space: pre-wrap; word-wrap: break-word"><span>{{ index + 1 }}</span><span v-html="item.wjTitle" style="white-space: pre-wrap; word-wrap: break-word"></span></div>
<div style="white-space: pre-wrap; word-wrap: break-word"><span>{{ index + 1 }}</span><span
v-html="item.wjTitle" style="white-space: pre-wrap; word-wrap: break-word"></span></div>
<div v-if="item.picPath">
<j-upload v-model:value="item.picPath" fileType="image" disabled :maxCount="item.picPath.split(',').length" :buttonVisible="true" ></j-upload>
<j-upload v-model:value="item.picPath" fileType="image" disabled
:maxCount="item.picPath.split(',').length" :buttonVisible="true"></j-upload>
</div>
<div style="text-align: right;">题目分值 <span class="answer-word">{{ item.wjScore }}</span>
</div>
<div style="text-align: right;">题目分值 <span class="answer-word">{{ item.wjScore }}</span> </div>
</template>
<a-radio-group v-model:value="item.itemSelected" style="width: 100%" size="default" :disabled="disabled">
<a-radio-group v-model:value="item.itemSelected" style="width: 100%" size="default"
:disabled="disabled">
<div style="width: 100%" v-for="(tmxx, index) in item.wjxWjxxTmxxList" :key="index">
<a-radio :value="tmxx.itemIndex + ``" style="width: 100%; margin-bottom: 5px">
<span v-html="tmxx.itemTitle" style="width: 80%; font-size: 16px"></span>
@ -64,17 +101,22 @@
</div>
<!-- 多选题 -->
<div style="width: 100%" v-else-if="item.wjType == 4">
<div style="text-align: left; width: 100%; font-weight: bold; line-height: 80px; font-size: 18px">多选题</div>
<div style="text-align: left; width: 100%; font-weight: bold; line-height: 80px; font-size: 18px">
多选题</div>
<a-card>
<template #title>
<div style="white-space: pre-wrap; word-wrap: break-word"><span>{{ index + 1 }}</span><span v-html="item.wjTitle" style="white-space: pre-wrap; word-wrap: break-word"></span></div>
<div style="white-space: pre-wrap; word-wrap: break-word"><span>{{ index + 1 }}</span><span
v-html="item.wjTitle" style="white-space: pre-wrap; word-wrap: break-word"></span></div>
<div v-if="item.picPath">
<j-upload v-model:value="item.picPath" fileType="image" disabled :maxCount="item.picPath.split(',').length" :buttonVisible="true" ></j-upload>
<j-upload v-model:value="item.picPath" fileType="image" disabled
:maxCount="item.picPath.split(',').length" :buttonVisible="true"></j-upload>
</div>
<div style="text-align: right;">题目分值 <span class="answer-word">{{ item.wjScore }}</span>
</div>
<div style="text-align: right;">题目分值 <span class="answer-word">{{ item.wjScore }}</span> </div>
</template>
<a-checkbox-group v-model:value="item.itemSelected" style="width: 100%" size="default" :disabled="disabled">
<a-checkbox-group v-model:value="item.itemSelected" style="width: 100%" size="default"
:disabled="disabled">
<a-row>
<a-col :span="24" v-for="(tmxx, index) in item.wjxWjxxTmxxList" :key="index">
<a-checkbox :value="tmxx.itemIndex" style="width: 100%; margin-bottom: 5px">
@ -86,115 +128,102 @@
</a-card>
</div>
<!-- 填空题 -->
<div style="width: 100%" v-else-if="item.wjType == 5 && item.wjSubtype==null">
<div style="text-align: left; width: 100%; font-weight: bold; line-height: 80px; font-size: 18px">填空题</div>
<div style="width: 100%" v-else-if="item.wjType == 5 && item.wjSubtype == null">
<div style="text-align: left; width: 100%; font-weight: bold; line-height: 80px; font-size: 18px">
填空题</div>
<a-card>
<template #title>
<div style="white-space: pre-wrap; word-wrap: break-word"><span>{{ index + 1 }}</span><span v-html="item.wjTitle" style="white-space: pre-wrap; word-wrap: break-word"></span></div>
<div style="white-space: pre-wrap; word-wrap: break-word"><span>{{ index + 1 }}</span><span
v-html="item.wjTitle" style="white-space: pre-wrap; word-wrap: break-word"></span></div>
<div v-if="item.picPath">
<j-upload v-model:value="item.picPath" fileType="image" disabled :maxCount="item.picPath.split(',').length" :buttonVisible="true" ></j-upload>
<j-upload v-model:value="item.picPath" fileType="image" disabled
:maxCount="item.picPath.split(',').length" :buttonVisible="true"></j-upload>
</div>
<div style="text-align: right;">题目分值 <span class="answer-word">{{ item.wjScore }}</span>
</div>
<div style="text-align: right;">题目分值 <span class="answer-word">{{ item.wjScore }}</span> </div>
</template>
<a-row>
<a-col :span="24">
<a-textarea
placeholder="请填写答案"
v-model:value="item.wjAnswer"
style="width: 100%"
:auto-size="{ minRows: 2, maxRows: 5 }"
:disabled="disabled"
/>
<a-textarea placeholder="请填写答案" v-model:value="item.wjAnswer" style="width: 100%"
:auto-size="{ minRows: 2, maxRows: 5 }" :disabled="disabled" />
</a-col>
</a-row>
</a-card>
</div>
<!-- 简答题 -->
<div style="width: 100%" v-else-if="item.wjType == 5 && item.wjSubtype == 5">
<div style="text-align: left; width: 100%; font-weight: bold; line-height: 80px; font-size: 18px">简答题</div>
<div style="text-align: left; width: 100%; font-weight: bold; line-height: 80px; font-size: 18px">
简答题</div>
<a-card>
<template #title>
<div style="white-space: pre-wrap; word-wrap: break-word"><span>{{ index + 1 }}</span><span v-html="item.wjTitle" style="white-space: pre-wrap; word-wrap: break-word"></span></div>
<div style="white-space: pre-wrap; word-wrap: break-word"><span>{{ index + 1 }}</span><span
v-html="item.wjTitle" style="white-space: pre-wrap; word-wrap: break-word"></span></div>
<div v-if="item.picPath">
<j-upload v-model:value="item.picPath" fileType="image" disabled :maxCount="item.picPath.split(',').length" :buttonVisible="true" ></j-upload>
<j-upload v-model:value="item.picPath" fileType="image" disabled
:maxCount="item.picPath.split(',').length" :buttonVisible="true"></j-upload>
</div>
<div style="text-align: right;">题目分值 <span class="answer-word">{{ item.wjScore }}</span>
</div>
<div style="text-align: right;">题目分值 <span class="answer-word">{{ item.wjScore }}</span> </div>
</template>
<a-row>
<a-col :span="24">
<a-textarea
placeholder="请填写答案"
v-model:value="item.wjAnswer"
style="width: 100%"
:auto-size="{ minRows: 2, maxRows: 5 }"
:disabled="disabled"
/>
<a-textarea placeholder="请填写答案" v-model:value="item.wjAnswer" style="width: 100%"
:auto-size="{ minRows: 2, maxRows: 5 }" :disabled="disabled" />
</a-col>
</a-row>
</a-card>
</div>
<!-- 文件题 -->
<div style="width: 100%" v-else-if="item.wjType == 8">
<div style="text-align: left; width: 100%; font-weight: bold; line-height: 80px; font-size: 18px">文件题</div>
<div style="text-align: left; width: 100%; font-weight: bold; line-height: 80px; font-size: 18px">
文件题</div>
<a-card>
<template #title>
<div style="white-space: pre-wrap; word-wrap: break-word"><span>{{ index + 1 }}</span><span v-html="item.wjTitle" style="white-space: pre-wrap; word-wrap: break-word"></span></div>
<div style="white-space: pre-wrap; word-wrap: break-word"><span>{{ index + 1 }}</span><span
v-html="item.wjTitle" style="white-space: pre-wrap; word-wrap: break-word"></span></div>
<div v-if="item.picPath">
<j-upload v-model:value="item.picPath" fileType="image" disabled :maxCount="item.picPath.split(',').length" :buttonVisible="true" ></j-upload>
<j-upload v-model:value="item.picPath" fileType="image" disabled
:maxCount="item.picPath.split(',').length" :buttonVisible="true"></j-upload>
</div>
<div style="text-align: right;">题目分值 <span class="answer-word">{{ item.wjScore }}</span>
</div>
<div style="text-align: right;">题目分值 <span class="answer-word">{{ item.wjScore }}</span> </div>
</template>
<a-row>
<a-col :span="24">
<!-- -{{item.picPath.split(',')}}- -->
<!-- -{{item.picPath.split(',')}}- -->
<!-- <JImageUpload v-model:value="item.picPath" disabled></JImageUpload> -->
<!-- <j-image-upload v-model:value="item.picPath" :disabled="disabled"></j-image-upload> -->
</a-col>
<a-col :span="24">
<j-upload v-model:value="item.wjAnswer" :max-count="1" :disabled="disabled" accept=".doc,.docx,.pdf,.jpg,.jpeg,.png,.mp3,.mp4,.zip,.ppt,.pptx,.rar,.excel" ></j-upload>
<j-upload v-model:value="item.wjAnswer" :max-count="1" :disabled="disabled"
accept=".doc,.docx,.pdf,.jpg,.jpeg,.png,.mp3,.mp4,.zip,.ppt,.pptx,.rar,.excel"></j-upload>
</a-col>
</a-row>
</a-card>
</div>
<div v-else> 无对应类型-{{item.wjType}}-{{item.wjSubtype}}=</div>
<div v-else> 无对应类型-{{ item.wjType }}-{{ item.wjSubtype }}=</div>
</div>
</a-col>
<a-col :span="24" style="text-align: center" v-if="!sfsxs">
<a-button type="primary" @click="submitForm">提交</a-button>
</a-col>
</a-row>
</a-spin>
<div v-if="showType == 2" style="width: 100%; background: #fff; text-align: center; min-height: 500px">
<div style="padding-top: 200px">
<img class="work-img-img" src="../../../../assets/images/homework.png" /> <span style="color: #666">您已提交了测验请勿重复提交</span
><br /><a-button
type="primary"
@click="
() => {
router.push({ path: '/site/index' });
}
"
>返回</a-button
>
<img class="work-img-img" src="../../../../assets/images/homework.png" /> <span
style="color: #666">您已提交了测验请勿重复提交</span><br /><a-button type="primary" @click="goBack()">返回</a-button>
</div>
</div>
<div v-if="showType == 3" style="width: 100%; text-align: center; min-height: 500px">
<div style="padding-top: 200px">
<img class="work-img-img" src="../../../../assets/images/homework.png" />
<span style="color: #666">提交成功请返回首页</span><br /><a-button
type="primary"
@click="
() => {
router.push({ path: '/site/index' });
}
"
>返回</a-button
>
<span style="color: #666">提交成功请返回首页</span><br /><a-button type="primary" @click="goBack()">返回</a-button>
</div>
</div>
<!-- <div v-if="showType == 3" style="width: 100%; text-align: center">
@ -219,18 +248,21 @@
</template>
<script lang="ts" name="wjxWjxx-add" setup>
import { ref, nextTick, unref, defineExpose, onMounted } from 'vue';
import { ref, nextTick, unref, defineExpose, onMounted, onUnmounted } from 'vue';
import { Icon } from '/@/components/Icon';
import { useMessage } from '/@/hooks/web/useMessage';
import { defHttp } from '/@/utils/http/axios';
import { queryWjxWjxxTmxxListByMainId, queryDataById, saveOrUpdate, djtj } from '/@/views/kc/wjxWjxxTmlb/WjxWjxxTmlb.api';
import { fzbSaveOrUpdate, getExamTime } from '/@/views/kc/wjxDjxxFzbtj/WjxDjxxFzbtj.api';
import headerPage from '/@/views/site/common/header.vue';
import footerPage from '/@/views/site/common/footer.vue';
import JUpload from '/@/components/Form/src/jeecg/components/JUpload/JUpload.vue';
import JImageUpload from '/@/components/Form/src/jeecg/components/JImageUpload.vue';
const title = ref<string>('');
const content = ref<string>('');
const content = ref<string>('');
const infoDataRef = ref<string>('');
const mainId = ref<string>('');
const isShow = ref<boolean>(false);
const disabled = ref<boolean>(false);
@ -249,8 +281,11 @@ const { currentRoute } = useRouter();
const router = useRouter();
const { query } = unref(currentRoute);
const { rwbh, xqxn, type, teano, cytitle, cyid, cyatype, sfzd, sfbs } = query; //
const it = ref('') //fs
const zdsx = ref(null) //
const loadTestInfo = ref(true) //loading
const sfsxs = ref<boolean>(false);
const showContent = ref(false)
//
function edit(record, isDisabled, type, flag) {
console.log('👨‍👨‍👦', record, isDisabled, type, flag);
@ -266,7 +301,6 @@ function edit(record, isDisabled, type, flag) {
mainId.value = record.id;
if (flag == 'ls') {
defHttp.get({ url: '/wjxWjxxTmlb/wjxWjxxTmlb/queryByMainId', params: { id: record.id } }).then((res) => {
// console.log(`🚀 ~ defHttp.get ~ res:`, res)
let list = res;
for (let i = 0; i < list.length; i++) {
let par = list[i];
@ -279,12 +313,10 @@ function edit(record, isDisabled, type, flag) {
}
}
tiganData.value = res;
console.log("🚀 ~ defHttp.get1111 ~ tiganData.value:", tiganData.value)
});
} else {
defHttp.get({ url: '/wjxWjxxTmlb/wjxWjxxTmlb/queryDjByMainId', params: { id: record.id } }).then((res) => {
tiganData.value = res;
console.log("🚀 ~ defHttp.get2222 ~ tiganData.value:", tiganData.value)
});
}
}
@ -293,26 +325,25 @@ function edit(record, isDisabled, type, flag) {
async function submitForm() {
const data = tiganData.value;
const values = Object.assign([], data);
console.log("🚀 ~ submitForm ~ values:", values)
for (let i = 0; i < values.length; i++) {
let param = values[i];
if (param.wjType == 3 || param.wjType == 4) {
if (param.itemSelected == null) {
emit('closeLoading');
createMessage.warning('第'+(i+1)+'题没有作答,请检查试卷,完成所有作答!');
createMessage.warning('第' + (i + 1) + '题没有作答,请检查试卷,完成所有作答!');
return;
}
values[i].itemSelected = param.itemSelected + '';
} else if (param.wjType == 5) {
if (param.wjAnswer == null || param.wjAnswer == '') {
emit('closeLoading');
createMessage.warning('第'+(i+1)+'题没有作答,请检查试卷,完成所有作答!');
createMessage.warning('第' + (i + 1) + '题没有作答,请检查试卷,完成所有作答!');
return;
}
} else if (param.wjType == 8) {
if (param.wjAnswer == null || param.wjAnswer == '') {
emit('closeLoading');
createMessage.warning('第'+(i+1)+'题没有作答,请检查试卷,完成所有作答!');
createMessage.warning('第' + (i + 1) + '题没有作答,请检查试卷,完成所有作答!');
return;
}
}
@ -321,8 +352,28 @@ async function submitForm() {
const isUpdate = false;
console.log('👨‍🚒', values);
showType.value = 3;
//
if (!!zdsx.value) {
let finishDate = new Date(finishTime.value.replace(' ', 'T'));
let now = new Date();
if (now > finishDate) {
//
fzbSaveOrUpdate(Object.assign({}, { djxxId: infoDataRef.value.id, vid: infoDataRef.value.vid, type: 3 }), false)
}
}
//
await djtj(values, isUpdate);
if (it.value == 'fs') {
//djxx
defHttp.post({ url: '/wjxDjxx/upZbcs', data: { id: infoDataRef.value.id, vid: infoDataRef.value.vid, createBy: infoDataRef.value.createBy } })
}
//
showContent.value = true
it.value == ''
}
function getCurrentTimeFormat() {
@ -340,14 +391,116 @@ function getCurrentTimeFormat() {
function padZero(num) {
return num < 10 ? `0${num}` : `${num}`;
}
// >>>
const zbtj = ref({
tcqp: 0,
qp: 0
})
const testingTips = ref('开始测验')
const finishTime = ref('')
const element = document.documentElement;
//
function inFullScreen() {
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.mozRequestFullScreen) { // Firefox
element.mozRequestFullScreen();
} else if (element.webkitRequestFullscreen) { // Chrome, Safari and Opera
element.webkitRequestFullscreen();
} else if (element.msRequestFullscreen) { // IE/Edge
element.msRequestFullscreen();
}
}
//退
function outFullScreen() {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) { // Firefox
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) { // Chrome, Safari and Opera
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) { // IE/Edge
document.msExitFullscreen();
}
}
const isFullscreen = ref(false);
//
const handleFullscreenChange = () => {
isFullscreen.value = !!document.fullscreenElement;
if (it.value == 'fs' && !isFullscreen.value) {
showContent.value = false
testingTips.value = '继续测验'
zbtj.value.tcqp += 1
fzbSaveOrUpdate(Object.assign({}, { djxxId: infoDataRef.value.id, vid: infoDataRef.value.vid, type: 1 }), false)
// alert('')
}
};
function recordTime() {
//,wjx_djxx_fzbtj
if (!!zdsx.value) {
fzbSaveOrUpdate(Object.assign({}, { djxxId: infoDataRef.value.id, vid: infoDataRef.value.vid, type: 4, zdsx: zdsx.value, endTime: infoDataRef.value.endTime }), false).then(res => {
finishTime.value = res.result.finishTime
})
}
}
function fsFUNC() {
if (it.value && it.value == 'fs') {
inFullScreen()
showContent.value = true
}
// else{
//
showContent.value = true
// }
recordTime()
}
const isPageVisible = ref(true);
//
const handleVisibilityChange = () => {
isPageVisible.value = !document.hidden;
if (it.value == 'fs' && showContent.value && !isPageVisible.value) {
zbtj.value.qp += 1
fzbSaveOrUpdate(Object.assign({}, { djxxId: infoDataRef.value.id, vid: infoDataRef.value.vid, type: 2 }), false)
// alert('')
}
};
//
function removeListening() {
//
document.removeEventListener('fullscreenchange', handleFullscreenChange);
document.removeEventListener('webkitfullscreenchange', handleFullscreenChange);
document.removeEventListener('mozfullscreenchange', handleFullscreenChange);
document.removeEventListener('MSFullscreenChange', handleFullscreenChange);
//
document.removeEventListener('visibilitychange', handleVisibilityChange);
}
// <<<
function goBack() {
removeListening()
outFullScreen()
router.push({ path: '/site/index' });
}
async function refreshFT() {
const finishTimeData = await getExamTime('ktcy:' + infoDataRef.value.vid)
finishTime.value = finishTimeData.result
}
//
onMounted(() => {
onMounted(async () => {
//
defHttp.get({ url: '/wjxWjxx/wjxWjxx/querySfdtById', params: { id: cyid } }).then((res) => {
console.log('🎅', res);
var num = res.num;
console.log('🧛', num);
console.log('🧛', res);
if (num && parseInt(num) > 0) {
showType.value = 2;
} else {
@ -356,25 +509,58 @@ onMounted(() => {
}
});
defHttp.get({ url: '/wjxWjxx/wjxWjxx/queryById', params: { id: cyid } }).then((res) => {
console.log('🎅11111111', res);
content.value = res.content
});
//
{
const infoData = await defHttp.get({ url: '/wjxWjxx/wjxWjxx/queryById', params: { id: cyid } })
content.value = infoData.contentf
infoDataRef.value = infoData
await refreshFT()
//zdsxrecordTime()
if (!!infoData.zdsx) {
zdsx.value = infoData.zdsx
}
if (infoData.fzbms == 'Y') {
it.value = 'fs'
} else {
//
recordTime()
}
//it=='fs'
if (it.value && it.value == 'fs') {
//
document.addEventListener('fullscreenchange', handleFullscreenChange);
document.addEventListener('webkitfullscreenchange', handleFullscreenChange); // Safari
document.addEventListener('mozfullscreenchange', handleFullscreenChange); // Firefox
document.addEventListener('MSFullscreenChange', handleFullscreenChange); // IE/Edge
//
document.addEventListener('visibilitychange', handleVisibilityChange);
showContent.value = false
loadTestInfo.value = false
} else {
showContent.value = true
loadTestInfo.value = false
}
}
defHttp.get({ url: '/wjxWjxx/wjxWjxx/getSfxs', params: { id: cyid } }).then((res) => {
console.log('🙂', res);
var num = res.num;
if(num=='1'){
if (num == '1') {
sfsxs.value = false;
}else{
} else {
sfsxs.value = true;
}
});
});
//
onUnmounted(() => {
removeListening()
});
defineExpose({
edit,
submitForm,
@ -398,7 +584,7 @@ defineExpose({
/deep/span.ant-radio + * {
/deep/span.ant-radio+* {
width: 100%;
}
@ -416,7 +602,8 @@ defineExpose({
cursor: pointer;
width: 80%;
}
/deep/.ant-checkbox + span {
/deep/.ant-checkbox+span {
padding-right: 8px;
padding-left: 8px;
width: 100%;
@ -426,11 +613,13 @@ defineExpose({
// font-size: ;
// height: 100%;
background: #f3f3f4;
#maxSite {
//
max-width: 1170px;
//
margin: 0 auto;
.rowGutter {
margin-top: 1rem;
margin-bottom: 1rem;
@ -440,30 +629,68 @@ defineExpose({
color: #fff;
background: #1ab394;
}
.ant-layout-footer {
background: #fff;
}
.ant-layout-sider {
color: #fff;
background: #3ba0e9;
}
.ant-layout-content {
min-height: 120px;
color: #000;
background: #f3f3f4;
}
.dictBox :deep(.ant-select) {
width: 100%;
}
}
}
.answer-word {
color: #ff8710;
font-weight: bold;
}
.work-img-img {
margin: 0 auto;
width: 180px;
height: 139px;
}
.ts_container {
padding: 35px;
/* 根据需要调整padding值 */
}
.ts_title {
display: block;
text-align: center;
margin-bottom: 10px;
/* 可选:根据需要调整间距 */
}
.ts_rules-list {
text-align: left;
margin-left: auto;
margin-right: auto;
width: 80%;
/* 让列表占据容器的80%,居中显示 */
}
.ts_rules-list li {
text-align: left;
margin-left: 20px;
/* 根据需要调整缩进 */
}
.ts_center-button {
display: block;
margin-left: auto;
margin-right: auto;
}
</style>

View File

@ -1,16 +1,17 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from "/@/hooks/web/useMessage";
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/grab/xxhbbks/getXsxkbList',
save='/grab/xxhbbks/add',
edit='/grab/xxhbbks/edit',
save = '/grab/xxhbbks/add',
edit = '/grab/xxhbbks/edit',
deleteOne = '/grab/xxhbbks/delete',
deleteBatch = '/grab/xxhbbks/deleteBatch',
importExcel = '/grab/xxhbbks/importExcel',
exportXls = '/grab/xxhbbks/exportXls',
getXKRY = '/grab/xxhbbks/getXKRY',
}
/**
@ -35,11 +36,11 @@ export const list = (params) => defHttp.get({ url: Api.list, params });
* @param params
* @param handleSuccess
*/
export const deleteOne = (params,handleSuccess) => {
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
export const deleteOne = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
}
};
/**
*
@ -54,12 +55,12 @@ export const batchDelete = (params, handleSuccess) => {
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
}
},
});
}
};
/**
*
@ -69,4 +70,14 @@ export const batchDelete = (params, handleSuccess) => {
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params }, { isTransformResponse: false });
}
};
/**
*
* @param params
* @returns
*/
export const getXKRY = (params) => {
return defHttp.post({ url: Api.getXKRY, data: params }, { isTransformResponse: false });
};

View File

@ -328,7 +328,18 @@ function handleDetail(record: Recordable) {
}
//
function handleShangchuan(record) {
async function handleShangchuan(record) {
//
let stuZyIds = []
if(record.xzxstjzy == 'Y'){
stuZyIds = await defHttp.post({ url: '/zyInfoStudent/zyInfoStudent/getZyStuId', data: { zyinfoId: record.id, xh: record.stuId } })
const own = stuZyIds.filter(stu => stu.id == record.stuId)
if(own[0].sfzz == 'N'){
createMessage.error('您不是组长,无法提交作业!')
return
}
}
let timestamp = new Date().getTime();
let startTimestamp = Date.parse(record.startTime);
if (timestamp < startTimestamp) {
@ -342,7 +353,7 @@ function handleShangchuan(record) {
}
ZyInfoStudentModalPage.value.disableSubmit = false;
var param = { id: record.id,stuId:record.stuId, zyfj: record.filePath };
var param = { id: record.id, stuId: record.stuId, zyfj: record.filePath, xzxstjzy: record.xzxstjzy,stuZyIds };
console.log('------->',param);
ZyInfoStudentModalPage.value.edit(param);
}

View File

@ -14,6 +14,8 @@ enum Api {
deleteBatch = '/zyInfo/zyInfo/deleteBatch',
importExcel = '/zyInfo/zyInfo/importExcel',
exportXls = '/zyInfo/zyInfo/exportXls',
getxzxx = '/zyInfo/zyInfo/getxzxx',
saveData = '/zyInfo/zyInfo/saveData',
}
/**
@ -76,3 +78,19 @@ export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params }, { isTransformResponse: false });
}
/**
*
* @param params
*/
export const getxzxx = (params) => {
return defHttp.post({ url: Api.getxzxx, params }, { isTransformResponse: false });
}
/**
*
* @param params
*/
export const saveData = (params) => {
return defHttp.post({ url: Api.saveData, params }, { isTransformResponse: false });
}

View File

@ -899,6 +899,7 @@ function handleBohui(record: Recordable) {
cancelText: '取消',
onOk: () => {
defHttp.post({ url: '/zyInfoStudent/zyInfoStudent/editBohui', params: { id: record.id } }).then((res) => {
createMessage.success('作业驳回成功!')
handleSuccess();
});
},

View File

@ -159,6 +159,17 @@
</a-form-item>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="小组形式提交作业" >
<a-radio-group v-model:value="zyInfo.xzxstjzy" @change="xztjzyxsFunc(zyInfo.xzxstjzy)" :options="xzxstjzyOptions" :disabled="editDisabled"/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="小组信息" >
<a-button type="primary" @click="bjxzxxFunc(true)" v-show="!editDisabled" :disabled="zyInfo.xzxstjzy=='N'" >设置小组信息</a-button>
<a-button type="primary" @click="bjxzxxFunc(false)" v-show="editDisabled" >查看小组信息</a-button>
</a-form-item>
</a-col>
</div>
</a-col>
<a-col :span="24">
@ -479,6 +490,10 @@
<a @click="handleFenxiang(item)" class="home-status">
<Icon icon="ant-design:share-alt-outlined" />分享
</a>
<a-divider type="vertical"/>
<a @click="ckxzxxFunc(item)" v-if="item.dpynum == 0 && item.ypynum == 0" class="home-status">
<Icon icon="fluent:people-team-16-regular" />查看分组
</a>
<a-divider type="vertical" />
<a @click="handleChehui(item)" v-if="item.dpynum == 0 && item.ypynum == 0" class="home-status">
<Icon icon="ant-design:import-outlined" />撤回作业
@ -546,6 +561,7 @@
<XxhbbksListModal ref="XxhbbksListModalPage"></XxhbbksListModal>
<YyzyListModal ref="YyzyListModalPage" @success="handleCallYinyong"></YyzyListModal>
<ZyCyFenxiangListModal ref="ZyCyFenxiangListModalpage"></ZyCyFenxiangListModal>
<Xzxx ref="XzxxRef" :studentsQP="queryParam" @saveSuccess="xzxxSaveSuccess"></Xzxx>
<a-modal v-model:visible="imgvisible" title="图片预览" width="800px" :cancelText="`关闭`"
:okButtonProps="{ class: { 'jee-hidden': true } }">
@ -565,7 +581,7 @@ import { ref, reactive, onMounted, unref , onUnmounted, watchEffect} from 'vue';
import { list, deleteOne } from './ZyInfo.api';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
import { Input, Popover, Pagination, Empty } from 'ant-design-vue';
import { Input, Popover, Pagination, Empty, message } from 'ant-design-vue';
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
import { useRouter } from 'vue-router';
@ -597,6 +613,7 @@ import XxhbbksListModal from '/@/views/kc/xxhbbks/XxhbbksListModal.vue';
import ZyInfoEditModal from './components/ZyInfoEditModal.vue';
import YyzyListModal from '/@/views/zy/zyInfo/YyzyListModal.vue';
import ZyCyFenxiangListModal from '/@/views/zy/zyCyFenxiang/ZyCyFenxiangListModal.vue';
import Xzxx from './components/Xzxx.vue';
import { encryptByBase64 } from '/@/utils/cipher';
const globSetting = useGlobSetting();
@ -623,7 +640,7 @@ const qmkszb = ref<number>(0);
const ktcyzb = ref<number>(0);
const zycsDisabled = ref<boolean>(false);
const zyInfo = ref<any>({});
const zyInfo = ref<any>({xzxstjzy:'N'});
const formRef = ref();
const confirmLoading = ref<boolean>(false);
const zyyqShow = ref<boolean>(false);
@ -652,6 +669,7 @@ const registerDetialModal = ref();
const registerPiyueModal = ref();
const YyzyListModalPage = ref();
const ZyCyFenxiangListModalpage = ref();
const XzxxRef = ref()
const pdfUrl = ref('/downPath/ylhpsf.pdf');
//table
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
@ -693,7 +711,10 @@ const disabledDate = (current: Dayjs) => {
return current && current < dayjs().subtract(1, 'days').endOf('day');
};
const useForm = Form.useForm;
const xzxstjzyOptions = ref([
{ label: '关闭', value: 'N' },
{ label: '开启', value: 'Y' },
])
//
function handleYyzy() {
var params = { xqxn, rwbh };
@ -710,7 +731,7 @@ function handleCallYinyong(record) {
record.id = zyid;
record.sort = zysort;
record.zyLeixing = '0'; //
zyInfo.value = record;
setZyInfo(record)
zyInfo.value.createBy = createBy
zyInfo.value.rwbh = rwbh
zyInfo.value.zyStatus = "0"
@ -870,6 +891,7 @@ function searchQueryZyxq() {
// queryParam.value.mainId = zyInfo.value.id;
queryParam.value.queryType = queryType.value;
console.log('queryParam.value',queryParam.value)
reload();
}
@ -937,17 +959,20 @@ function yulanFile(record) {
}
}
//
function handleBohui(record: Recordable) {
async function handleBohui(record: Recordable) {
createConfirm({
iconType: 'warning',
title: '驳回',
content: '您确定驳回此作业吗,驳回后需要学生重新提交?',
okText: '确认',
cancelText: '取消',
onOk: () => {
defHttp.post({ url: '/zyInfoStudent/zyInfoStudent/editBohui', params: { id: record.id } }).then((res) => {
handleSuccess();
});
onOk: async () => {
let stuZyIds = await defHttp.post({ url: '/zyInfoStudent/zyInfoStudent/getZyStuIdById', data: { zyinfoId: record.mainId, xh: record.createBy } })
stuZyIds.forEach(async stu => {
await defHttp.post({ url: '/zyInfoStudent/zyInfoStudent/editBohui', params: { id: stu.id } })
})
createMessage.success('作业驳回成功!')
handleSuccess();
},
});
}
@ -1171,7 +1196,7 @@ function handlePfbzShow(type) {
}
//
function handleSzzynr(record) {
zyInfo.value = record;
setZyInfo(record)
dataKhnr.value = [];
handleShowType(2);
}
@ -1367,6 +1392,12 @@ async function submitForm() {
var sfcc = model.sfcc;
console.log('🚶‍♂️', model);
if (sfcc == '1') {
var xzxstjzy = model.xzxstjzy
if(xzxstjzy == 'Y'){
createMessage.warning('由于设置了小组形式提交作业,因此无法开启查重功能!');
confirmLoading.value = false;
return;
}
//
if (wwtgl) {
if ((wwcc == false && xncc == false && nwcc == false && aigccc == false) || (!wwcc && !xncc && !nwcc && !aigccc)) {
@ -1620,7 +1651,7 @@ function handleEdit(record, type) {
record.xssfck = '1';
}
editDisabled.value = type;
zyInfo.value = record;
setZyInfo(record)
console.log('👨‍👦', zyInfo.value);
if (zyInfo.value.xncc == 'false') {
zyInfo.value.xncc = null;
@ -1644,6 +1675,11 @@ function handleEdit(record, type) {
handleShowType(2);
}
function setZyInfo(record){
zyInfo.value = record;
if(!zyInfo.value.xzxstjzy)zyInfo.value.xzxstjzy = 'N'
}
function handleBjzy(record) {
registerEditModal.value.disableSubmit = false;
registerEditModal.value.edit(record);
@ -1686,7 +1722,7 @@ function handleZyxx(record, type) {
}
}
zyInfo.value = record;
setZyInfo(record)
handleShowType(3);
queryType.value = type;
rowSelection.value = [];
@ -1821,6 +1857,27 @@ function handlePageChange(record) {
reloadZy();
}
//
function bjxzxxFunc(isEdit){
XzxxRef.value.showModal(zyInfo.value,tableData.value,isEdit)
}
function ckxzxxFunc(item){
XzxxRef.value.showModal(item,tableData.value,false)
}
//
function xztjzyxsFunc(val){
if(val == 'Y') createMessage.warning('开启小组形式提交作业后,将无法使用查重功能!');
}
const xzxxSaveSuccess = (id,data) => {
tableData.value.forEach(item => {
if(item.id == id) item.xzrs = data.rs
});
// createMessage.success('')
}
onMounted(() => {
searchQuery();
});

View File

@ -182,6 +182,16 @@
</a-form-item>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="小组形式提交作业" >
<a-radio-group v-model:value="zyInfo.xzxstjzy" @change="xztjzyxsFunc(zyInfo.xzxstjzy)" :options="xzxstjzyOptions" :disabled="true"/>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="小组信息" >
<a-button type="primary" @click="bjxzxxFunc(false)" >查看小组信息</a-button>
</a-form-item>
</a-col>
</div>
</a-col>
<a-col :span="24">
@ -530,6 +540,10 @@
<a @click="handleFenxiang(item)" class="home-status"><Icon icon="ant-design:share-alt-outlined" />分享</a> -->
<!-- <a-divider type="vertical" />
<a @click="handleChehui(item)" v-if="item.dpynum == 0 && item.ypynum == 0" class="home-status"><Icon icon="ant-design:import-outlined" />撤回作业</a> -->
<a-divider type="vertical"/>
<a @click="ckxzxxFunc(item)" v-if="item.ytjnum <= 0" class="home-status">
<Icon icon="fluent:people-team-16-regular" />查看分组
</a>
<a-divider type="vertical" />
<a @click="handleZyxx(item, '')" class="home-status"><Icon icon="ant-design:file-done-outlined" />查看作业</a>
<!-- <a-divider type="vertical" v-if="item.dpynum == 0 && item.ypynum == 0"/>
@ -585,6 +599,7 @@
<XxhbbksListModal ref="XxhbbksListModalPage"></XxhbbksListModal>
<YyzyListModal ref="YyzyListModalPage" @success="handleCallYinyong"></YyzyListModal>
<ZyCyFenxiangListModal ref="ZyCyFenxiangListModalpage"></ZyCyFenxiangListModal>
<Xzxx ref="XzxxRef" :studentsQP="queryParam" ></Xzxx>
<a-modal v-model:visible="imgvisible" title="图片预览" width="800px" :cancelText="`关闭`" :okButtonProps="{ class: { 'jee-hidden': true } }">
<div style="padding: 10px 20px">
@ -636,6 +651,7 @@ import ZyInfoEditModal from './components/ZyInfoEditModal.vue';
import YyzyListModal from '/@/views/zy/zyInfo/YyzyListModal.vue';
import ZyCyFenxiangListModal from '/@/views/zy/zyCyFenxiang/ZyCyFenxiangListModal.vue';
import { encryptByBase64 } from '/@/utils/cipher';
import Xzxx from './components/Xzxx.vue';
const globSetting = useGlobSetting();
const baseApiUrl = globSetting.domainUrl;
@ -691,6 +707,11 @@ const registerPiyueModal = ref();
const YyzyListModalPage = ref();
const ZyCyFenxiangListModalpage = ref();
const pdfUrl = ref('/downPath/ylhpsf.pdf');
const XzxxRef = ref()
const xzxstjzyOptions = ref([
{ label: '关闭', value: 'N' },
{ label: '开启', value: 'Y' },
])
//table
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
tableProps: {
@ -1807,6 +1828,10 @@ function handleZySuccess(zyid) {
defHttp.get({ url: '/zyInfo/zyInfo/sendZyWechat', params: { id: zyid } }).then((res) => {});
}
function ckxzxxFunc(item){
XzxxRef.value.showModal(item,tableData.value,false)
}
/**
* 查询
*/
@ -1854,6 +1879,11 @@ function reloadZy() {
});
}
//
function bjxzxxFunc(isEdit){
XzxxRef.value.showModal(zyInfo.value,tableData.value,false)
}
function handlePageChange(record) {
current.value = record;
reloadZy();

View File

@ -0,0 +1,715 @@
<template>
<div>
<!-- 学生分配模态框 -->
<a-modal v-model:visible="open" title="配置小组信息" :style="{ height: '85vh' }" width="80%" :okText="isEdit ? '保存' : '确定'"
:cancelText="`关闭`" @ok="handleOk" :maskClosable="false">
<a-row style="height: 20%; overflow-y: auto;" v-show="isEdit">
<a-col :span="6" style="padding-right: 16px;height: 100%;">
<!-- 筛选条件 -->
<a-input-search v-model:value="searchQuery" placeholder="请输入人名或学号进行筛选"
style="margin-bottom: 16px; position: sticky; top: 0; background-color: white; z-index: 10;" />
</a-col>
<a-col :span="18" style="border-left: 1px solid rgb(240,240,240); padding-left: 16px;height: 100%;">
<span style="font-weight: 700;margin-top: 20px;">
<span style="color: red;">说明</span>1.先设置小组人数 2.点击自动分组可根据小组人数进行自动划分 3.若需要手动分组直接在左侧人员展示处进行手动划分
<span style="color: red;">4.分组后需要点击确定按钮否则会分组失败需要重新分组</span>
</span>
</a-col>
</a-row>
<a-row style="height: 80%; overflow-y: auto;">
<!-- 左侧未分配的学生列表 -->
<a-col :span="6" style="padding: 5px 20px;height: 70vh;">
<a-list item-layout="horizontal"
style="max-height: 100%; overflow-y: auto;padding-left: 15px;padding-right: 15px;">
<a-list-item v-for="student in filteredUnallocatedStudents" :key="student.xh">
<a-list-item-meta :title="`${student.xm}${student.xh}`" />
<template #actions>
<a-button v-show="isEdit" type="primary" size="small" @click="assignStudent(student)">分配</a-button>
</template>
</a-list-item>
</a-list>
</a-col>
<!-- 右侧已分配的小组卡片 -->
<a-col :span="18" style="border-left: 1px solid rgb(240,240,240); padding: 10px;height: 65vh;">
<a-row
style="margin-bottom: 16px; position: sticky; top: 0; background-color: white; z-index: 10;padding:10px;">
<a-col :span="4" style="display: flex; align-items: center;">
<div>
<span style="font-size: 17px;">小组人数</span>
<span style="font-size: 17px;" v-show="!!allocatedData.rs">{{ allocatedData.rs }}</span>
<span style="font-size: 17px;color:red;" v-show="!allocatedData.rs">未设置!</span>
</div>
</a-col>
<a-col :span="20">
<a-button v-show="isEdit" :type="!allocatedData.rs ? 'danger' : 'primary'" @click="setGroupSize()"
style="margin-left: 5px;">设置人数</a-button>
<a-button v-show="isEdit" type="primary" @click="autoAssignGroups()"
style="margin-left: 5px;">自动分组</a-button>
<a-button v-show="isEdit" type="primary" @click="resetGroups()" style="margin-left: 5px;">重置分组</a-button>
<a-button v-show="isEdit" type="primary" @click="quotationManager()"
style="margin-left: 5px;">引用分组</a-button>
</a-col>
</a-row>
<p v-if="!hasAllocatedData">未分配数据</p>
<a-row :gutter="[16, 16]" style="max-height: 100%; overflow-y: auto;">
<a-col :span="8" v-for="group in sortedAllocatedGroups" :key="group.sort">
<a-card>
<template #title>
<span style="flex-grow: 1;">
<!-- 现有的小组信息 -->
<span v-show="allocatedData.rs - group.students.length > 0">
{{ group.xzmc }} (<span style="color: red;">还差{{ allocatedData.rs - group.students.length
}}</span>) - 排序{{ group.sort }}
</span>
<span v-show="allocatedData.rs - group.students.length == 0">{{ group.xzmc }}
(<span style="color: green;">已满员</span>) - 排序{{ group.sort }}
</span>
<span v-show="allocatedData.rs - group.students.length < 0">
{{ group.xzmc }} (<span style="color: red;">超员{{ Math.abs(allocatedData.rs -
group.students.length
) }}</span>) -
排序{{ group.sort }}
</span>
</span>
</template>
<a-row>
<a-col :span="12">
<span>组长</span>
<span v-if="hasLeader(group)" style="color:green">{{ getLeaderName(group) }}</span>
<a-button v-show="isEdit" v-if="hasLeader(group)" type="default" size="small"
style="margin-left: 5px;" @click="removeLeader(group)">移除</a-button>
<span v-if="!hasLeader(group)" style="color: red;">未设置组长</span>
</a-col>
</a-row>
<a-row style="margin-top: 3px;">
<a-col :span="12">组员</a-col>
</a-row>
<a-row v-for="member in group.students.filter(member => member.sfzz !== 'Y')" :key="member.xh"
style="margin-top: 3px;">
<a-col :span="24">{{ member.xm }}
<a-button v-show="isEdit" type="default" size="small" @click="removeFromGroup(member)">移除</a-button>
<a-button v-show="isEdit" type="default" size="small" @click="promotionTeamLeader(member)"
style="margin-left: 5px;">改为组长</a-button>
</a-col>
</a-row>
<a-row style="margin-top: 10px;border-top:1px solid rgb(240, 240, 240);padding-top: 10px;">
<a-col :span="24">
<a-button v-show="isEdit" type="default" size="small" @click="editGroup(group)">编辑</a-button>
<a-button v-show="isEdit" type="default" size="small" @click="dissolveGroup(group)"
style="margin-left: 5px;">解散</a-button>
</a-col>
</a-row>
</a-card>
</a-col>
</a-row>
</a-col>
</a-row>
</a-modal>
<!-- 分配学生对话框 -->
<a-modal v-model:visible="assignVisible" title="选择小组" @ok="handleAssign" @cancel="assignVisible = false">
<a-row style="margin-top: 10px;">
<a-col :push="1" :span="22">
<a-steps :current="currentStep" size="small">
<a-step title="选择分配方式" />
<a-step title="填写详细信息" />
</a-steps>
</a-col>
</a-row>
<a-row style="padding: 20px;">
<a-col :span="24">
<span style="color: green;">当前学生{{ selectedStudent.xm }}{{ selectedStudent.xh }}</span>
</a-col>
</a-row>
<!-- 第一步选择分配方式 -->
<div v-show="currentStep === 0">
<a-row style="padding: 10px 25px;">
<a-col :span="24">
<a-radio-group v-model:value="form.assignType" @change="nextStep">
<a-radio value="new">创建新小组</a-radio>
<a-radio value="existing">分配至已有小组</a-radio>
</a-radio-group>
</a-col>
</a-row>
</div>
<!-- 第二步填写详细信息 -->
<div v-show="currentStep !== 0">
<a-form :model="form" layout="vertical">
<template v-if="form.assignType === 'new'">
<a-row :gutter="[16, 16]" style="padding-top: 10px;padding-left: 25px;padding-right: 25px;">
<a-col :span="12">
<a-form-item label="小组名称">
<a-input v-model:value="form.newGroupName" placeholder="请输入小组名称" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="小组排序">
<a-input-number v-model:value="form.newGroupSort" placeholder="请输入排序值" :min="1" :precision="0"
:step="1" />
</a-form-item>
</a-col>
</a-row>
<a-row style="padding: 0px 25px;">
<a-col :span="24">
<a-form-item label="是否设置为组长">
<a-switch v-model:checked="form.isLeader" />
</a-form-item>
</a-col>
</a-row>
</template>
<template v-else>
<a-row :gutter="[16, 16]" style="padding-top: 10px;padding-left: 25px;padding-right: 25px;">
<a-col :span="24">
<a-form-item label="选择小组">
<a-select v-model:value="form.existingGroupId" placeholder="请选择小组">
<!-- :disabled="isGroupFull(group)" -->
<a-select-option v-for="(group, index) in allocatedData.allocated" :key="group.id"
:value="group.id">
{{ group.xzmc }} (<span v-show="isGroupFull(group)" style="color: red;">已满员</span><span
v-show="!isGroupFull(group)">有空位</span> )
</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row style="padding: 0px 25px;">
<a-col :span="24">
<a-form-item label="是否设置为组长">
<a-switch v-model:checked="form.isLeader" />
</a-form-item>
</a-col>
</a-row>
</template>
</a-form>
</div>
</a-modal>
<!-- 编辑小组信息对话框 -->
<a-modal v-model:visible="editModalVisible" title="编辑小组信息" @ok="saveEdit" @cancel="editModalVisible = false">
<a-form :model="editingGroup" layout="vertical">
<a-row :gutter="[16, 16]" style="padding:20px;">
<a-col :span="24">
<a-form-item label="小组名称">
<a-input v-model:value="editingGroup.xzmc" placeholder="请输入小组名称" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="小组排序">
<a-input-number v-model:value="editingGroup.sort" placeholder="请输入排序值" :min="1" :precision="0"
:step="1" />
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-modal>
<!-- 设置小组人数对话框 -->
<a-modal v-model:visible="setGroupSizeVisible" title="设置小组人数" @ok="confirmSetGroupSize"
@cancel="setGroupSizeVisible = false">
<a-form :model="form" layout="vertical">
<a-row style="padding: 20px;">
<a-col :span="24">
<a-form-item label="每组人数">
<a-input-number v-model:value="form.groupSize" :min="1" :precision="0" :step="1" />
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-modal>
<!-- 引用小组对话框 -->
<a-modal v-model:visible="yyfzModalVisible" title="引用分组配置" @ok="yyfzOk" :okText="'引用'"
@cancel="yyfzModalVisible = false">
<a-form layout="vertical">
<a-row :gutter="[16, 16]" style="padding:20px;">
<a-col :span="24">
<a-form-item label="课程作业">
<a-select v-model:value="yyfzValue" placeholder="请选择课程作业">
<a-select-option v-for="item in kczyOptions" :key="item.id" :value="item.id">
<span>{{ item.title }}</span>
</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-modal>
</div>
</template>
<script lang="ts" name="xzxx" setup>
import { ref, computed, reactive, onMounted, toRaw } from 'vue';
import { Modal, message } from 'ant-design-vue';
import { getXKRY } from '/@/views/kc/xxhbbks/Xxhbbks.api';
import { getxzxx, saveData } from '/@/views/zy/zyInfo/ZyInfo.api';
import { useMessage } from '/@/hooks/web/useMessage';
const yyfzModalVisible = ref(false)
const { createMessage } = useMessage();
const editingGroup = ref(null);
const editModalVisible = ref(false);
const open = ref<boolean>(false);
const assignVisible = ref<boolean>(false);
const currentStep = ref<number>(0); //
const students = ref([]);
const allocatedData = ref({
allocated: [] // sort
});
const selectedStudent = ref(null);
const setGroupSizeVisible = ref(false);
const zyinfoId = ref('')
const hmz = ref(null)
const kczyOptions = ref([])
const yyfzValue = ref('')
const form = reactive({
id: '',
assignType: '', // 'new' | 'existing'
newGroupName: '', //
newGroupSort: 1, //
existingGroupId: null, // ID
isLeader: false, //
groupSize: 3
});
const emit = defineEmits(['saveSuccess']);
const props = defineProps({
studentsQP: { type: Object, default: () => ({}) }
});
const isEdit = ref(false)
// sortedAllocatedGroups
const sortedAllocatedGroups = computed(() => {
return allocatedData.value.allocated?.slice().sort((a, b) => a.sort - b.sort) || [];
});
//
const unallocatedStudents = computed(() => {
const allocatedXhs = allocatedData.value.allocated?.flatMap(group => {
return group.students.map(student => student.xh)
}) || [];
return students.value.filter(student => !allocatedXhs.includes(student.xh));
});
//
const hasAllocatedData = computed(() => {
return allocatedData.value.allocated && allocatedData.value.allocated.length > 0;
});
//
const availableGroups = computed(() => {
return allocatedData.value.allocated?.filter(group => !isGroupFull(group)) || [];
});
//
const isGroupFull = (group) => {
return group.students.length >= allocatedData.value.rs;
};
//
const hasLeader = (group) => group.students.some(member => member.sfzz === 'Y');
//
const getLeaderName = (group) => {
const leader = group.students.find(member => member.sfzz === 'Y');
return leader ? leader.xm : '';
};
//
const nextStep = () => {
if (form.assignType) {
currentStep.value = 1;
}
};
async function iniAllocatedData(zyInfo, selectStudents = true) {
allocatedData.value.allocated = []
allocatedData.value.unabsorbed = []
//
if (selectStudents) await loadStudents();
//
const data = await getxzxx({ id: zyInfo.id });
allocatedData.value.allocated = data.result
}
//
const showModal = async (zyInfo, kczyList, isEdit_) => {
zyinfoId.value = zyInfo.id
allocatedData.value.rs = zyInfo.xzrs
open.value = true;
//Options
kczyOptions.value = []
kczyList.forEach(kczy => {
if (kczy.id != zyInfo.id && kczy.sfpzgxzxx == 'Y') {
kczyOptions.value.push({
title: kczy.title,
id: kczy.id,
rs: kczy.xzrs
})
}
});
//
iniAllocatedData(zyInfo)
isEdit.value = isEdit_
};
//
const loadStudents = async () => {
const res = await getXKRY(props.studentsQP.rwbh);
students.value = res.result;
};
//
const handleAssign = async () => {
if (!form.assignType) {
message.error('请先选择分配方式');
return;
}
//
if (currentStep.value === 1 && validateForm()) {
try {
//
await performAssignment();
message.success('分配成功');
assignVisible.value = false;
} catch (error) {
message.error('分配失败,请稍后再试');
}
}
};
//
const validateForm = () => {
if (form.assignType === 'new') {
if (!form.newGroupName || !form.newGroupSort) {
message.error('请填写完整的新小组信息');
return false;
}
} else if (form.assignType === 'existing') {
if (!form.existingGroupId) {
message.error('请选择要分配的小组');
return false;
}
}
return true;
};
//
const performAssignment = () => {
if (form.assignType === 'new') {
if (!hmz.value) hmz.value = new Date().getTime()
else hmz.value = hmz.value + 1
const newGroup = {
id: hmz.value,
xzmc: form.newGroupName,
sort: form.newGroupSort,
students: [{ ...selectedStudent.value, sfzz: form.isLeader ? 'Y' : 'N' }]
};
allocatedData.value.allocated.push(newGroup);
} else if (form.assignType === 'existing') {
const targetGroup = allocatedData.value.allocated.find(group => group.id === form.existingGroupId);
if (targetGroup) {
if (form.isLeader) {
targetGroup.students.forEach(s_ => {
s_.sfzz = 'N'
});
}
targetGroup.students.push({ ...selectedStudent.value, sfzz: form.isLeader ? 'Y' : 'N' });
}
}
};
function resetFormData() {
form.id = ''
form.assignType = ''
form.newGroupName = ''
form.newGroupSort = 1
form.existingGroupId = null
form.isLeader = false
}
//
const assignStudent = (student) => {
if (!allocatedData.value.rs) {
message.warning('未设置小组人数');
return;
}
selectedStudent.value = student;
assignVisible.value = true;
currentStep.value = 0;
resetFormData()
};
//
const setLeader = (group) => {
//
Modal.confirm({
title: '设置组长',
content: '请选择一位成员作为组长',
okText: '确认',
cancelText: '取消',
onOk: () => {
// TODO
}
});
};
//
const removeFromGroup = (member) => {
Modal.confirm({
title: '移除成员',
content: `确定要将${member.xm}从小组中移除吗?`,
okText: '确认',
cancelText: '取消',
onOk: () => {
//
allocatedData.value.allocated.forEach(group => {
const index = group.students.findIndex(student => student.xh === member.xh);
if (index !== -1) {
group.students.splice(index, 1);
}
});
//
if (!students.value.some(student => student.xh === member.xh)) {
students.value.push(member);
}
}
});
};
//
function promotionTeamLeader(member) {
//
allocatedData.value.allocated.forEach(group => {
const index = group.students.findIndex(student => student.xh === member.xh);
if (index !== -1) {
group.students.forEach(item => {
if (item.xh == member.xh) item.sfzz = 'Y'
else item.sfzz = 'N'
})
}
});
}
//
const dissolveGroup = (group) => {
Modal.confirm({
title: '解散小组',
content: `确定要解散${group.xzmc}吗?`,
okText: '确认',
cancelText: '取消',
onOk: () => {
//
const newMembers = group.students.filter(student =>
!students.value.some(existingStudent => existingStudent.xh === student.xh)
);
//
students.value.push(...newMembers);
//
allocatedData.value.allocated = allocatedData.value.allocated.filter(g => g.sort !== group.sort);
}
});
};
//
const editGroup = (group) => {
// editingGroup
editingGroup.value = { ...group };
editModalVisible.value = true;
};
//
const saveEdit = () => {
if (!editingGroup.value.xzmc) {
message.error('请填写小组名称');
return;
}
if (!editingGroup.value.sort) {
message.error('请填写小组排序');
return;
}
if (!editingGroup.value) return;
const targetGroupIndex = allocatedData.value.allocated.findIndex(g => g.id === editingGroup.value.id);
if (targetGroupIndex !== -1) {
allocatedData.value.allocated.splice(targetGroupIndex, 1, editingGroup.value);
}
message.success('编辑成功');
editModalVisible.value = false;
};
//
const searchQuery = ref('');
const filteredUnallocatedStudents = computed(() => {
if (!searchQuery.value) return unallocatedStudents.value;
const query = searchQuery.value.toLowerCase();
return unallocatedStudents.value.filter(student =>
student.xm.toLowerCase().includes(query) ||
student.xh.toString().includes(query)
);
});
const setGroupSize = () => {
setGroupSizeVisible.value = true;
};
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
// 0i
const j = Math.floor(Math.random() * (i + 1));
// array[i]array[j]
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
// 使
const myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const performAutoAssignment = async () => {
let unassigned = [...unallocatedStudents.value];
let totalGroupsNeeded = Math.ceil(students.value.length / allocatedData.value.rs);
//
if (!allocatedData.value.allocated || allocatedData.value.allocated.length === 0) {
for (let i = 0; i < totalGroupsNeeded; i++) {
const newGroupName = `小组${i + 1}`;
const newGroupSort = i + 1;
allocatedData.value.allocated.push({ id: Date.now() + i, xzmc: newGroupName, sort: newGroupSort, students: [] });
}
}
//
if (allocatedData.value.allocated.length < totalGroupsNeeded) {
for (let i = allocatedData.value.allocated.length; i < totalGroupsNeeded; i++) {
const newGroupName = `小组${i + 1}`;
const newGroupSort = allocatedData.value.allocated.reduce((max, g) => Math.max(max, g.sort), 0) + 1;
allocatedData.value.allocated.push({ id: Date.now() + i, xzmc: newGroupName, sort: newGroupSort, students: [] });
}
}
unassigned = shuffleArray(unassigned)
//
allocatedData.value.allocated.forEach(group => {
while (group.students.length < allocatedData.value.rs && unassigned.length > 0) {
const student = unassigned.shift();
if (student) {
group.students.push({ ...student, sfzz: group.students.length === 0 ? 'Y' : 'N' });
}
}
});
message.success('自动分配成功');
};
const autoAssignGroups = async () => {
if (!allocatedData.value.rs) {
message.warning('未设置小组人数');
return;
}
await performAutoAssignment();
};
const resetGroups = () => {
Modal.confirm({
title: '重置分组',
content: '是否重置分组信息?',
okText: '重置',
cancelText: '取消',
onOk: () => {
allocatedData.value.allocated = []
allocatedData.value.unabsorbed = []
}
});
}
const confirmSetGroupSize = () => {
if (form.groupSize > 0) {
allocatedData.value.rs = form.groupSize;
setGroupSizeVisible.value = false;
} else {
message.error('请设置有效的人数');
}
};
const removeLeader = (group) => {
removeFromGroup(toRaw(group.students.find(member => member.sfzz === 'Y')))
}
//
const handleOk = () => {
if(!isEdit.value){
open.value = false
return
}
allocatedData.value.allocated.forEach(allo => {
allo.zyinfoId = zyinfoId.value
allo.students?.forEach(stu => {
stu.zyinfoId = zyinfoId.value
});
})
saveData({ zyinfoId: zyinfoId.value, rs: allocatedData.value.rs, list: allocatedData.value.allocated }).then(res => {
emit('saveSuccess', zyinfoId.value, allocatedData.value)
createMessage.success('保存成功!')
open.value = false
}).catch(err => {
createMessage.error('服务器异常,请稍后重试!');
})
};
//
const quotationManager = () => {
yyfzModalVisible.value = true
}
//
const yyfzOk = async () => {
if (!!yyfzValue.value) {
kczyOptions.value.forEach(kczy => {
if (yyfzValue.value == kczy.id) {
allocatedData.value.rs = kczy.rs
}
})
await iniAllocatedData({ id: yyfzValue.value }, false)
yyfzModalVisible.value = false
} else {
createMessage.warning('未选择课程作业!')
}
}
//
onMounted(async () => {
});
defineExpose({
showModal
});
</script>
<style lang="less" scoped>
/* 确保模态框内的表单项有适当的间距 */
.a-modal {
.ant-form-item {
margin-bottom: 16px;
/* 增加表单项之间的垂直间距 */
}
}
/* 可选:增加一些额外的样式以改善视觉效果 */
.ant-form-vertical .ant-form-item-label {
padding-bottom: 8px;
}
.ant-card-head-title {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>

View File

@ -228,6 +228,7 @@
}
function handleBohui(record: Recordable){
defHttp.post({url:'/zyInfoStudent/zyInfoStudent/editBohui',params:{id:record.id}}).then(res =>{
createMessage.success('作业驳回成功!')
handleSuccess()
})
}

View File

@ -158,6 +158,8 @@
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
const confirmLoading = ref<boolean>(false);
const stuZyIds = ref([])
const xzxstjzy = ref(null)
let zyInfo = ref<any>({});
//
const validatorRules = {
@ -251,6 +253,8 @@ function handleKcnr(kcnr) {
* 编辑
*/
function edit(record) {
stuZyIds.value = record.stuZyIds
xzxstjzy.value = record.xzxstjzy
console.log(`🚀 ~ edit ~ record:`, record)
stuId.value = record.stuId;
nextTick(() => {
@ -272,70 +276,96 @@ function handleKcnr(kcnr) {
});
}
function submitZy(model, ids, isUpdate) {
let models = [model];
if (Array.isArray(ids) && ids.length > 0) {
ids.forEach(id => {
let newModel = { ...model, stuId: id.id };
models.push(newModel);
});
}
let msgr = true
let msg = '操作成功'
models.forEach(async m_ => {
await zyscStu(m_, isUpdate)
.then((res) => {
console.log(`🚀 ~ .then ~ res:`, res)
if (res.success) {
//
stuWpKsjc(model, isUpdate).then((res) => { })
} else {
msg = res.message
createMessage.warning(res.message);
}
}).catch(err => {
msgr = false
msg = '提交失败'
})
})
if (msgr) {
createMessage.success("操作成功");
emit('ok');
confirmLoading.value = false;
emit('closeLoading');
} else {
createMessage.warning(msg);
}
}
/**
* 提交数据
*/
async function submitForm() {
//
// var t = await validate();
confirmLoading.value = true;
const isUpdate = ref<boolean>(false);
//
let model = formData;
if (model.id) {
isUpdate.value = true;
}
//
for (let data in model) {
//
if (model[data] instanceof Array) {
let valueType = getValueType(formRef.value.getProps, data);
//
if (valueType === 'string') {
model[data] = model[data].join(',');
}
async function submitForm() {
//
// var t = await validate();
confirmLoading.value = true;
const isUpdate = ref<boolean>(false);
//
let model = formData;
if (model.id) {
isUpdate.value = true;
}
//
for (let data in model) {
//
if (model[data] instanceof Array) {
let valueType = getValueType(formRef.value.getProps, data);
//
if (valueType === 'string') {
model[data] = model[data].join(',');
}
}
if(!model.filePath){
emit('closeLoading');
confirmLoading.value = false;
createMessage.warning("请上传作业附件");
return;
}
createConfirm({
iconType: 'warning',
title: '确认提交',
content: '是否确认提交数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
model.stuId = stuId.value;
model.mainId = zyInfo.value.id;
zyscStu(model, isUpdate.value)
.then((res) => {
console.log(`🚀 ~ .then ~ res:`, res)
if (res.success) {
createMessage.success("操作成功");
emit('ok');
//
stuWpKsjc(model, isUpdate.value).then((res) => {})
} else {
createMessage.warning(res.message);
}
emit('closeLoading');
})
.finally(() => {
confirmLoading.value = false;
emit('closeLoading');
});
},
onCancel: () => {
confirmLoading.value = false;
emit('closeLoading');
},
});
}
if (!model.filePath) {
emit('closeLoading');
confirmLoading.value = false;
createMessage.warning("请上传作业附件");
return;
}
createConfirm({
iconType: 'warning',
title: '确认提交',
content: '是否确认提交数据',
okText: '确认',
cancelText: '取消',
onOk: async () => {
model.stuId = stuId.value;
model.mainId = zyInfo.value.id;
if(xzxstjzy.value == 'Y'){
const zys = stuZyIds.value.filter(stu => stu.id != model.stuId)
submitZy(model,zys,isUpdate.value)
}else{
submitZy(model,[],isUpdate.value)
}
},
onCancel: () => {
confirmLoading.value = false;
emit('closeLoading');
},
});
}
function openPdf(record){
if(record.stuPdfPath){

View File

@ -299,7 +299,7 @@ function handleVideo(three){
}
}
defHttp.post({url:'/zyInfoStudent/zyInfoStudent/editPiyue',params:model}).then(res =>{
defHttp.post({url:'/zyInfoStudent/zyInfoStudent/editFabu',params:model}).then(res =>{
emit('ok');
})
.finally(() => {
@ -344,7 +344,7 @@ function handleVideo(three){
}
}
await defHttp.post({url:'/zyInfoStudent/zyInfoStudent/editPiyue',params:model}).then(res =>{
await defHttp.post({url:'/zyInfoStudent/zyInfoStudent/editFabu',params:model}).then(res =>{
emit('ok2');
})