初始化项目:添加后端代码、ThinkPHP框架、前端资源
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\model\app\exam;
|
||||
|
||||
use think\Model;
|
||||
|
||||
|
||||
class Exercises extends Model
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 表名
|
||||
protected $name = 'app_exam_exercises';
|
||||
|
||||
// 自动写入时间戳字段
|
||||
protected $autoWriteTimestamp = 'integer';
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = 'createtime';
|
||||
protected $updateTime = 'updatetime';
|
||||
protected $deleteTime = false;
|
||||
|
||||
// 追加属性
|
||||
protected $append = [
|
||||
'status_text'
|
||||
];
|
||||
|
||||
|
||||
|
||||
public function getStatusList()
|
||||
{
|
||||
return ['1' => __('Status 1'), '0' => __('Status 0')];
|
||||
}
|
||||
|
||||
public function getHideList()
|
||||
{
|
||||
return ['1' => __('Hide 1'), '0' => __('Hide 0')];
|
||||
}
|
||||
|
||||
public function getSalesTypeList()
|
||||
{
|
||||
return ['1' => __('Sales_type 1'), '3' => __('Sales_type 3'), '2' => __('Sales_type 2')];
|
||||
}
|
||||
|
||||
public function getQuestionModeList()
|
||||
{
|
||||
return ['sort' => __('Question_mode sort'), 'random' => __('Question_mode random')];
|
||||
}
|
||||
|
||||
|
||||
public function getStatusTextAttr($value, $data)
|
||||
{
|
||||
$value = $value ? $value : (isset($data['status']) ? $data['status'] : '');
|
||||
$list = $this->getStatusList();
|
||||
return isset($list[$value]) ? $list[$value] : '';
|
||||
}
|
||||
|
||||
|
||||
public function course()
|
||||
{
|
||||
return $this->belongsTo('app\admin\model\course\Course', 'bind_course', 'id', [], 'LEFT')->setEagerlyType(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取绑定的题库分组
|
||||
* @param $exercisesId
|
||||
* @return mixed
|
||||
*/
|
||||
public function getBindWarehouseGroup($exercisesId){
|
||||
$data = \app\admin\model\app\exam\ExercisesBindWarehouseGroup::with(['group'])->where([
|
||||
'exercises_id'=>$exercisesId,
|
||||
])->order('id','asc')->select();
|
||||
|
||||
// 转换为数组处理
|
||||
$result = collection($data)->toArray();
|
||||
|
||||
// 处理未分组的情况(warehouse_group_id = 0 或 group.id 为 null)
|
||||
$allGroupIds = null;
|
||||
foreach ($result as &$item) {
|
||||
// 检查 group.id 是否为 null(表示关联的分组不存在)
|
||||
if (empty($item['group']['id'])) {
|
||||
// 计算未分组的题目数量(只查询一次)
|
||||
if ($allGroupIds === null) {
|
||||
$allGroupIds = \app\admin\model\app\exam\WarehouseGroup::field('id')->column('id');
|
||||
}
|
||||
$questionCount = \app\admin\model\app\exam\WarehouseQuestion::where(['group_id' => ['NOT IN', $allGroupIds]])->count();
|
||||
|
||||
// 设置未分组信息
|
||||
$item['group'] = [
|
||||
'id' => 0,
|
||||
'name' => '未分组',
|
||||
'question_count' => $questionCount
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\model\app\exam;
|
||||
|
||||
use think\Model;
|
||||
|
||||
|
||||
class ExercisesBindWarehouseGroup extends Model
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 表名
|
||||
protected $name = 'app_exam_exercises_bind_warehouse_group';
|
||||
|
||||
// 自动写入时间戳字段
|
||||
protected $autoWriteTimestamp = 'integer';
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = 'createtime';
|
||||
protected $updateTime = false;
|
||||
protected $deleteTime = false;
|
||||
|
||||
// 追加属性
|
||||
protected $append = [
|
||||
|
||||
];
|
||||
|
||||
|
||||
public function group()
|
||||
{
|
||||
return $this->belongsTo('app\admin\model\app\exam\WarehouseGroup', 'warehouse_group_id', 'id', [], 'LEFT')->setEagerlyType(0);
|
||||
}
|
||||
|
||||
|
||||
public function exercises()
|
||||
{
|
||||
return $this->belongsTo('Exercises', 'exercises_id', 'id', [], 'LEFT')->setEagerlyType(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过练习ID绑定题库分组
|
||||
* @param $exercisesId
|
||||
* @param $groupIds
|
||||
* @return bool
|
||||
*/
|
||||
public static function exercisesBindWarehouseGroup($exercisesId,$groupIds){
|
||||
self::where([
|
||||
'exercises_id'=>$exercisesId,
|
||||
'uniacid'=>UNIACID
|
||||
])->delete();
|
||||
|
||||
if($groupIds){
|
||||
foreach ($groupIds as $itemId){
|
||||
self::insert([
|
||||
'exercises_id'=>$exercisesId,
|
||||
'uniacid'=>UNIACID,
|
||||
'warehouse_group_id'=>$itemId,
|
||||
'createtime'=>time()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\model\app\exam;
|
||||
|
||||
use think\Model;
|
||||
use think\Db;
|
||||
|
||||
class ExercisesGroup extends Model
|
||||
{
|
||||
|
||||
|
||||
// 表名
|
||||
protected $name = 'app_exam_exercises_group';
|
||||
|
||||
// 自动写入时间戳字段
|
||||
protected $autoWriteTimestamp = 'integer';
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = 'createtime';
|
||||
protected $updateTime = false;
|
||||
protected $deleteTime = false;
|
||||
|
||||
// 追加属性
|
||||
protected $append = [
|
||||
'status_text'
|
||||
];
|
||||
|
||||
|
||||
|
||||
public function getStatusList()
|
||||
{
|
||||
return ['11' => __('Status 11')];
|
||||
}
|
||||
|
||||
|
||||
public function getStatusTextAttr($value, $data)
|
||||
{
|
||||
$value = $value ? $value : (isset($data['status']) ? $data['status'] : '');
|
||||
$list = $this->getStatusList();
|
||||
return isset($list[$value]) ? $list[$value] : '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取排序
|
||||
* @return void
|
||||
*/
|
||||
public function defineSort(){
|
||||
$maxItem = self::field("sort")->order("sort desc")->find();
|
||||
|
||||
if(!$maxItem){
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $maxItem['sort'] + 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\model\app\exam;
|
||||
|
||||
use think\Model;
|
||||
|
||||
|
||||
class ExercisesLog extends Model
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 表名
|
||||
protected $name = 'app_exam_exercises_log';
|
||||
|
||||
// 自动写入时间戳字段
|
||||
protected $autoWriteTimestamp = 'integer';
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = 'createtime';
|
||||
protected $updateTime = 'updatetime';
|
||||
protected $deleteTime = false;
|
||||
|
||||
// 追加属性
|
||||
protected $append = [
|
||||
'status_text'
|
||||
];
|
||||
|
||||
|
||||
|
||||
public function getStatusList()
|
||||
{
|
||||
return ['1' => __('Status 1'),'0' => __('Status 0')];
|
||||
}
|
||||
|
||||
|
||||
public function getStatusTextAttr($value, $data)
|
||||
{
|
||||
$value = $value ? $value : (isset($data['status']) ? $data['status'] : '');
|
||||
$list = $this->getStatusList();
|
||||
return isset($list[$value]) ? $list[$value] : '';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function exercises()
|
||||
{
|
||||
return $this->belongsTo('Exercises', 'exercises_id', 'id', [], 'RIGHT')->setEagerlyType(0);
|
||||
}
|
||||
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('app\admin\model\User', 'user_id', 'id', [], 'RIGHT')->setEagerlyType(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\model\app\exam;
|
||||
|
||||
use think\Model;
|
||||
|
||||
|
||||
class ExercisesSubscribe extends Model
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 表名
|
||||
protected $name = 'app_exam_exercises_subscribe';
|
||||
|
||||
// 自动写入时间戳字段
|
||||
protected $autoWriteTimestamp = 'integer';
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = 'createtime';
|
||||
protected $updateTime = false;
|
||||
protected $deleteTime = false;
|
||||
|
||||
// 追加属性
|
||||
protected $append = [
|
||||
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public function exercises()
|
||||
{
|
||||
return $this->belongsTo('Exercises', 'exercises_id', 'id', [], 'RIGHT')->setEagerlyType(0);
|
||||
}
|
||||
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('app\admin\model\User', 'user_id', 'id', [], 'RIGHT')->setEagerlyType(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\model\app\exam;
|
||||
|
||||
use think\Model;
|
||||
|
||||
|
||||
class WarehouseGroup extends Model
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 表名
|
||||
protected $name = 'app_exam_warehouse_group';
|
||||
|
||||
// 自动写入时间戳字段
|
||||
protected $autoWriteTimestamp = 'integer';
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = 'createtime';
|
||||
protected $updateTime = false;
|
||||
protected $deleteTime = false;
|
||||
|
||||
// 追加属性
|
||||
protected $append = [
|
||||
'status_text',
|
||||
'question_count'
|
||||
];
|
||||
|
||||
public $defaultGroudId = 990000;
|
||||
|
||||
|
||||
public function getStatusList()
|
||||
{
|
||||
return ['11' => __('Status 11')];
|
||||
}
|
||||
|
||||
|
||||
public function getStatusTextAttr($value, $data)
|
||||
{
|
||||
$value = $value ? $value : (isset($data['status']) ? $data['status'] : '');
|
||||
$list = $this->getStatusList();
|
||||
return isset($list[$value]) ? $list[$value] : '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取分组绑定的题目数量
|
||||
* @param $value
|
||||
* @param $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function getQuestionCountAttr($value, $data)
|
||||
{
|
||||
|
||||
if(!$data['id']){
|
||||
$ids = self::field('id')->column('id');
|
||||
|
||||
$count = \app\admin\model\app\exam\WarehouseQuestion::where(['group_id'=>['NOT IN',$ids]])->count();
|
||||
return $count;
|
||||
}
|
||||
|
||||
$count = \app\admin\model\app\exam\WarehouseQuestion::where([
|
||||
'group_id'=>$data['id']
|
||||
])->count();
|
||||
return $count;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\model\app\exam;
|
||||
|
||||
use think\Model;
|
||||
|
||||
|
||||
class WarehouseImport extends Model
|
||||
{
|
||||
|
||||
protected $excelSheetNoTypes = ['single','multiple','judge','indefinite','fillblank','essay'];
|
||||
|
||||
|
||||
/**
|
||||
* 获取每一行的数据
|
||||
* @param $data 题目数据
|
||||
* @param $type 题目类型下标
|
||||
* @param $groupId 分组ID
|
||||
* @return false|mixed|null
|
||||
*/
|
||||
public function getRowData($data,$typeIndex,$groupId=0){
|
||||
$rowData = false;
|
||||
|
||||
|
||||
$type = $this->excelSheetNoTypes[$typeIndex];
|
||||
|
||||
|
||||
if(empty($data[0])){
|
||||
return $rowData;
|
||||
}
|
||||
|
||||
switch ($type){
|
||||
case 'judge':
|
||||
if(count($data) != 3){
|
||||
return false;
|
||||
}
|
||||
if(!in_array($data[1],["错误","正确","错","对"])){
|
||||
return false;
|
||||
}
|
||||
|
||||
$rowData = $this->parseJudge($data);
|
||||
break;
|
||||
case 'single':
|
||||
if(count($data) < 13){
|
||||
return false;
|
||||
}
|
||||
//判断是不是字母
|
||||
if(!$data[11] || strlen($data[11]) != 1 || !ctype_alpha($data[11])){
|
||||
return false;
|
||||
}
|
||||
$rowData = $this->parseSingle($data);
|
||||
break;
|
||||
case 'multiple':
|
||||
if(count($data) < 13){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!$data[11] || strlen($data[11]) > 10 || !ctype_alpha($data[11])){
|
||||
return false;
|
||||
}
|
||||
$rowData = $this->parseMultiple($data);
|
||||
|
||||
break;
|
||||
case 'indefinite':
|
||||
if(count($data) < 13){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!$data[11] || strlen($data[11]) > 10 || !ctype_alpha($data[11])){
|
||||
return false;
|
||||
}
|
||||
$rowData = $this->parseMultiple($data, 'indefinite');
|
||||
|
||||
break;
|
||||
case 'fillblank':
|
||||
if(count($data) != 3){
|
||||
return false;
|
||||
}
|
||||
$rowData = $this->parseFillblank($data);
|
||||
|
||||
break;
|
||||
case 'essay':
|
||||
if(count($data) != 3){
|
||||
return false;
|
||||
}
|
||||
$rowData = $this->parseEssay($data);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return $rowData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 格式化单选题
|
||||
* @param $data
|
||||
* @return array|false
|
||||
*/
|
||||
public function parseSingle($data){
|
||||
|
||||
|
||||
$rowData = [
|
||||
'type'=>'single',
|
||||
'question'=>$data[0],
|
||||
'option'=>[],
|
||||
'answer'=>0,
|
||||
'analysis'=>$data[12]
|
||||
];
|
||||
|
||||
foreach($data as $index => $option){
|
||||
if($index > 0 && $index < 11){
|
||||
if(!$option){
|
||||
break;
|
||||
}
|
||||
|
||||
if($option instanceof \PhpOffice\PhpSpreadsheet\RichText\RichText){
|
||||
$option = $option->getPlainText();
|
||||
}
|
||||
|
||||
$rowData['option'][] = $option;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$answer = ord($data[11]) - 65;
|
||||
|
||||
if($answer < 0 || ($answer > count($rowData['option']) - 1)){
|
||||
return false;
|
||||
}
|
||||
|
||||
$rowData['option'] = json_encode($rowData['option'],true);
|
||||
$rowData['answer'] = $answer;
|
||||
|
||||
return $rowData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析判断题
|
||||
* @param $data
|
||||
* @return array
|
||||
*/
|
||||
public function parseJudge($data){
|
||||
$rowData = [
|
||||
'type'=>'judge',
|
||||
'question'=>$data[0],
|
||||
'option'=>json_encode([]),
|
||||
'answer'=>'',
|
||||
'analysis'=>$data[2]
|
||||
];
|
||||
|
||||
$rowData['answer'] = 1;
|
||||
|
||||
if(in_array($data[1],["对","正确"])){
|
||||
$rowData['answer'] = 0;
|
||||
}
|
||||
|
||||
return $rowData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析多选题
|
||||
* @param $data
|
||||
* @return array|false
|
||||
*/
|
||||
public function parseMultiple($data, $type = 'multiple'){
|
||||
$rowData = [
|
||||
'type'=>$type,
|
||||
'question'=>$data[0],
|
||||
'option'=>[],
|
||||
'answer'=>'',
|
||||
'analysis'=>$data[12]
|
||||
];
|
||||
|
||||
foreach($data as $index => $option){
|
||||
if($index > 0 && $index < 11){
|
||||
if(!$option){
|
||||
break;
|
||||
}
|
||||
if($option instanceof \PhpOffice\PhpSpreadsheet\RichText\RichText){
|
||||
$option = $option->getPlainText();
|
||||
}
|
||||
$rowData['option'][] = $option;
|
||||
}
|
||||
}
|
||||
|
||||
$answer = [];
|
||||
|
||||
foreach (str_split($data[11]) as $item){
|
||||
$item = ord($item) - 65;
|
||||
if($item < 0 || $item > (count($rowData['option']) -1 )){
|
||||
return false;
|
||||
}
|
||||
$answer[] = $item;
|
||||
}
|
||||
|
||||
$rowData['option'] = json_encode($rowData['option'],true);
|
||||
|
||||
if(count($answer) < 2 || count($answer) > 10){
|
||||
return false;
|
||||
}
|
||||
|
||||
$rowData['answer'] = implode(",",$answer);
|
||||
|
||||
return $rowData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析不定项选择题
|
||||
* @param $data
|
||||
* @return array|false
|
||||
*/
|
||||
public function parseIndefinite($data){
|
||||
return $this->parseMultiple($data, 'indefinite');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析填空题
|
||||
* 格式:题目、各空格答案(空格用"|"分隔,备选答案用","分隔)、解析
|
||||
* @param $data
|
||||
* @return array
|
||||
*/
|
||||
public function parseFillblank($data){
|
||||
$answers = [];
|
||||
$blanks = explode('|', $data[1]);
|
||||
foreach($blanks as $blank){
|
||||
$answers[] = explode(',', $blank);
|
||||
}
|
||||
|
||||
return [
|
||||
'type'=>'fillblank',
|
||||
'question'=>$data[0],
|
||||
'option'=>json_encode([]),
|
||||
'answer'=>json_encode($answers),
|
||||
'analysis'=>$data[2]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析问答题
|
||||
* 格式:题目、参考答案、解析
|
||||
* @param $data
|
||||
* @return array
|
||||
*/
|
||||
public function parseEssay($data){
|
||||
return [
|
||||
'type'=>'essay',
|
||||
'question'=>$data[0],
|
||||
'option'=>json_encode([]),
|
||||
'answer'=>$data[1],
|
||||
'analysis'=>$data[2]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\model\app\exam;
|
||||
|
||||
use think\Model;
|
||||
|
||||
|
||||
class WarehouseQuestion extends Model
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 表名
|
||||
protected $name = 'app_exam_warehouse_question';
|
||||
|
||||
// 自动写入时间戳字段
|
||||
protected $autoWriteTimestamp = 'integer';
|
||||
|
||||
// 定义时间戳字段名
|
||||
protected $createTime = 'createtime';
|
||||
protected $updateTime = false;
|
||||
protected $deleteTime = false;
|
||||
|
||||
// 追加属性
|
||||
protected $append = [
|
||||
'option',
|
||||
'type_text',
|
||||
];
|
||||
|
||||
|
||||
public function getOptionAttr($value, $data)
|
||||
{
|
||||
if(!isset($data['option']) || !$data['option']){
|
||||
return [];
|
||||
}
|
||||
|
||||
return json_decode($data['option'],true);
|
||||
}
|
||||
|
||||
public function getTypeTextAttr($value, $data)
|
||||
{
|
||||
// $lang = require_once APP_PATH."admin/lang/zh-cn/app/exam/warehouse_question.php";
|
||||
|
||||
if(!isset($data['type']) || !$data['type']){
|
||||
return '';
|
||||
}
|
||||
|
||||
return __('Question Type '.$data['type']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function getTypeList()
|
||||
{
|
||||
|
||||
return ['single' => __('Status single'),'multiple' => __('Status multiple'),'judge' => __('Status judge'),'indefinite' => __('Status indefinite'),'fillblank' => __('Status fillblank'),'essay' => __('Status essay')];
|
||||
}
|
||||
|
||||
//难度
|
||||
public function getDegreeList()
|
||||
{
|
||||
return ['1' => __('Degree 1'),'2' => __('Degree 2'),'3' => __('Degree 3')];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public function group()
|
||||
{
|
||||
return $this->belongsTo('app\admin\model\app\exam\WarehouseGroup', 'group_id', 'id', [], 'LEFT')->setEagerlyType(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\model\app\exam\warehouse;
|
||||
|
||||
use think\Model;
|
||||
|
||||
class Group extends Model
|
||||
{
|
||||
// 表名
|
||||
protected $name = 'app_exam_warehouse_group';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace app\admin\model\app\exam\warehouse;
|
||||
|
||||
use think\Model;
|
||||
|
||||
class Question extends Model
|
||||
{
|
||||
// 表名
|
||||
protected $name = 'app_exam_warehouse_question';
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user