Files
alab-amis-fix-exam/src/pages/schema/centre/exam/question.schema.ts

1643 lines
73 KiB
TypeScript
Raw Normal View History

2024-11-13 09:05:45 +08:00
const schema = {
type: 'page',
body: [
{
type: 'form',
name: 'cg',
body: [
{
type: 'service',
name: 'service',
api: {
method: 'get',
url: 'rest/exam_question_categories/${tree}',
sendOn: 'this.tree',
},
messages: {},
body: [
{
type: 'formula',
name: 'filter',
formula: "'(category.in.(' + data.tree + '),category.is.null)'",
condition: 'data.tree && data.no_cate',
id: 'u:20719f5b3c61',
},
{
type: 'formula',
name: 'filter',
condition: 'data.tree && !data.no_cate',
formula: "'(category.in.(' + data.tree + '))'",
id: 'u:da3404346ffb',
},
{
type: 'formula',
name: 'filter',
condition: '!data.tree && data.no_cate',
formula: "'(category.is.null)'",
id: 'u:c7c6c0aa04b6',
},
{
type: 'formula',
name: 'filter',
formula: "'(id.neq.0)'",
condition: '!data.tree && !data.no_cate',
id: 'u:d2517bd7c8ee',
},
],
id: 'u:87d7f2fe2416',
dsType: 'api',
},
{
type: 'tree-select',
label: '分类',
name: 'tree',
mode: 'horizontal',
source: {
method: 'get',
url: 'rest/exam_question_categories?path=cd.root&order=name',
adaptor:
'const recursive = id => {\r\n let nodes = payload.data.items.filter(x => {\r\n return x.parent === id\r\n })\r\n\r\n return nodes.map(item => {\r\n let temp = recursive(item.id)\r\n\r\n if (temp.length > 0) {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: `${item.id}`,\r\n children: temp\r\n }\r\n } else {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: `${item.id}`\r\n }\r\n }\r\n })\r\n}\r\n\r\nlet tops = payload.data.items.filter(x => x.parent === null)\r\n\r\nreturn {\r\n ...payload,\r\n data: tops.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}\r\n',
},
initiallyOpen: false,
searchable: true,
multiple: true,
joinValues: true,
onlyChildren: false,
withChildren: true,
cascade: true,
heightAuto: true,
virtualThreshold: false,
id: 'u:cde3f7416895',
},
{
type: 'checkbox',
label: '',
name: 'no_cate',
mode: 'inline',
option: '显示无分类题目',
id: 'u:50f0cbebc61b',
},
{
type: 'switch',
name: 'is_show',
mode: 'inline',
value: true,
option: '缩略显示',
optionAtLeft: false,
trueValue: true,
falseValue: false,
id: 'u:bfaf3772c9d4',
},
],
title: '表单',
wrapWithPanel: false,
reload:
'stu?parent=${tree}&tree=${tree}&org_name=${name}&filter=${filter}&is_show=${is_show}',
submitOnInit: true,
submitOnChange: true,
target: '',
initApi: '',
data: {
parentId: 1,
},
md: 3,
resetAfterSubmit: false,
id: 'u:48ceddfb7e02',
feat: 'View',
},
{
type: 'divider',
lineStyle: 'solid',
id: 'u:7a199eac8602',
},
{
type: 'crud',
messages: {},
api: {
method: 'get',
url: 'rest/exam_questions?select=id,type,content,analysis,tag,score,organization_id,answer,difficulty,is_objective,category,exam_question_categories(*),dicts(dictkey,dictvalue)&organization_id=eq.$centre_id&order=category,id',
data: {
page: '${page}',
perPage: '${perPage}',
or: '${filter}',
},
adaptor:
"return {\r\n ...payload,\r\n data: {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n let pattern = /<p>|<\\/p>|<strong>|<\\/strong>|<br \\/>/g\r\n let image_pattern = /<img .*?\\/>/g\r\n let raw_options = ''\r\n item.content.options && item.content.options.forEach(option => {\r\n raw_options = raw_options.concat(option.label.replace(/^<p>/, '<p>'.concat(option.key, '. ')).replace(/^<img/, ''.concat(option.key, '. <img')))\r\n })\r\n return {\r\n ...item,\r\n raw_title: item.content.title.replace(pattern, '').replace(image_pattern, '[图片]').replaceAll('&hellip;', '…').replaceAll('&mdash;', '—').replaceAll('&ldquo;', '“').replaceAll('&rdquo;', '”').replaceAll('&lsquo;', '').replaceAll('&rsquo;', ''),\r\n raw_options: raw_options\r\n }\r\n })\r\n }\r\n}",
requestAdaptor: '',
sendOn: 'this.filter',
},
syncLocation: false,
name: 'stu',
columns: [
{
name: '',
type: 'button',
placeholder: '-',
visibleOn: 'this.is_show',
label: '$raw_title',
actionType: 'dialog',
dialog: {
type: 'dialog',
title: '题目详情',
body: [
{
type: 'tpl',
tpl: "<div><span class=\"label label-default text-md\"><%= this.dicts && this.dicts.dictvalue %></span></div>\n<div><%= this.content && this.content.title %></div>\n<% this.content && this.content.options && this.content.options.forEach(item => { %>\n <div><%= item.label.replace(/^<p>/, '<p>'.concat(item.key, '. ')).replace(/^<img/, ''.concat(item.key, '. <img')) %></div>\n<% }) %>\n<div><%= this.answer && '<span class=\"label label-default text-md\">答案</span>'.concat(this.answer) %></div>\n<div><%= this.analysis && this.analysis.replace(/^<p>/, '<p>'.concat('<span class=\"label label-default text-md\">答案解析</span>')).replace(/^<img/, '<span class=\"label label-default text-md\">答案解析</span><img') %></div>",
inline: false,
},
],
size: 'lg',
actions: [],
closeOnEsc: true,
},
width: 500,
innerClassName: '',
className: 'question',
block: false,
level: 'link',
remark: '',
tooltip: '$raw_title',
tooltipPlacement: 'bottom',
id: 'u:84ea04af2db4',
},
{
name: 'content.title',
type: 'tpl',
placeholder: '-',
tpl: '<% if(data.content) { %><%= data.content.title %><% } %>',
label: '题干',
quickEdit: {
mode: 'popOver',
type: 'image-upload',
saveImmediately: {
api: {
method: 'patch',
url: 'rest/exam_questions/$id',
data: {
content: '$content',
},
requestAdaptor:
'let pattern = new RegExp("(\\\\.\\\\./){1,6}")\r\n\r\nif (api.data.content.options.length) {\r\n api.data.content = {\r\n title: api.data.content.title.replace(pattern, "/"),\r\n options: api.data.content.options.map(item => {\r\n return {\r\n ...item,\r\n label: item.label.replace(pattern, "/")\r\n }\r\n })\r\n }\r\n} else {\r\n api.data.content = {\r\n title: api.data.content.title.replace(pattern, "/")\r\n }\r\n}\r\n\r\nreturn api',
},
},
name: 'content.title',
},
inline: false,
popOver: false,
copyable: false,
visibleOn: '!this.is_show',
id: 'u:16e82384ee3c',
},
{
type: 'tpl',
name: 'raw_options',
placeholder: '-',
visibleOn: '!this.is_show',
label: '选项',
tpl: '<%= this.raw_options %>',
inline: false,
quickEdit: {
type: 'custom-option',
name: 'content.options',
label: '选项配置',
saveImmediately: {
api: {
method: 'patch',
url: 'rest/exam_questions/$id',
data: {
content: '$content',
},
requestAdaptor:
'let options = JSON.parse(JSON.stringify(api.data.content.options))\r\nlet index = options\r\n .findIndex(item => !Object.keys(item).length)\r\nwhile (index != -1) {\r\n options.splice(index, 1)\r\n index = options\r\n .findIndex(item => !Object.keys(item).length)\r\n}\r\n\r\napi.data.content = {\r\n ...api.data.content,\r\n options: options\r\n}\r\n\r\nlet pattern = new RegExp("(\\\\.\\\\./){1,6}")\r\n\r\nif (api.data.content.options.length) {\r\n api.data.content = {\r\n title: api.data.content.title.replace(pattern, "/"),\r\n options: api.data.content.options.map(item => {\r\n return {\r\n ...item,\r\n label: item.label.replace(pattern, "/")\r\n }\r\n })\r\n }\r\n} else {\r\n api.data.content = {\r\n title: api.data.content.title.replace(pattern, "/")\r\n }\r\n}\r\n\r\nreturn api',
},
},
mode: 'popOver',
form: {
title: '修改选项',
controls: [
{
type: 'text',
label: '选项',
name: 'key',
disabledOn: 'true',
},
{
name: 'label',
label: '内容',
type: 'input-rich-text',
vendor: 'tinymce',
options: {
menubar: 'true',
height: 400,
plugins:
'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking table emoticons template paste help',
toolbar:
'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media fullpage | forecolor backcolor emoticons | help',
},
receiver: {
method: 'post',
url: 'storage/v1/object/exam',
adaptor: '',
data: null,
requestAdaptor: '',
},
mode: 'normal',
},
{
type: 'switch',
name: 'is_fixed',
label: '',
option: '是否固定',
optionAtLeft: false,
trueValue: true,
falseValue: false,
},
],
size: 'lg',
},
multiple: true,
btnLabel: '设置选项',
},
popOver: false,
copyable: false,
quickEditEnabledOn:
"this.type === 'question_single_choice' || this.type === 'question_multi_choice'",
id: 'u:aee56529edd2',
},
{
type: 'tpl',
placeholder: '-',
name: 'answer',
label: '答案',
quickEdit: {
type: 'input-text',
saveImmediately: {
api: {
method: 'patch',
url: 'rest/exam_questions/$id',
data: {
answer: '$answer',
},
},
saveImmediately: true,
},
},
tpl: '<%= data.answer %>',
inline: false,
visibleOn: '!this.is_show',
id: 'u:01aba281f46f',
},
{
type: 'tpl',
name: 'analysis',
label: '答案解析',
placeholder: '-',
tpl: '<%= data.analysis %>',
inline: false,
quickEdit: {
type: 'input-rich-text',
saveImmediately: {
api: {
method: 'patch',
url: 'rest/exam_questions/$id',
data: {
analysis: '$analysis',
},
requestAdaptor:
'let pattern = new RegExp("(\\\\.\\\\./){1,6}")\r\n\r\napi.data.analysis = api.data.analysis.replace(pattern, "/")\r\n\r\nreturn api',
},
},
mode: 'popOver',
name: 'analysis',
vendor: 'tinymce',
options: {
menubar: 'true',
relative_urls: false,
remove_script_host: true,
height: 400,
plugins:
'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking table emoticons template paste help',
toolbar:
'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media fullpage | forecolor backcolor emoticons | help',
},
receiver: {
method: 'post',
url: 'storage/v1/object/exam',
adaptor: '',
data: null,
requestAdaptor: '',
},
},
visibleOn: '!this.is_show',
id: 'u:c7737bb550ad',
},
{
type: 'text',
label: '题型',
name: 'dicts.dictvalue',
placeholder: '-',
quickEdit: {
type: 'select',
options: [
{
label: '单选题',
value: 'question_single_choice',
},
{
label: '多选题',
value: 'question_multi_choice',
},
{
label: '判断题',
value: 'question_true_or_false',
},
],
saveImmediately: {
api: {
method: 'patch',
url: 'rest/exam_questions/$id',
data: {
type: '${dicts.dictkey}',
},
requestAdaptor:
"if(api.data.type === 'question_single_choice' || api.data.type === 'question_multi_choice' || api.data.type === 'question_true_or_false') {\r\n api.data.is_objective = true;\r\n} else {\r\n api.data.is_objective = false;\r\n}\r\nreturn api;",
},
},
name: 'dicts.dictkey',
mode: 'popOver',
},
id: 'u:cb1a31cb64c5',
},
{
type: 'text',
label: '难易程度',
placeholder: '-',
name: 'difficulty',
quickEdit: {
type: 'select',
options: [
{
label: '简单',
value: '简单',
},
{
label: '中等',
value: '中等',
},
{
label: '困难',
value: '困难',
},
],
saveImmediately: {
api: {
method: 'patch',
url: 'rest/exam_questions/$id',
data: {
difficulty: '$difficulty',
},
},
},
},
id: 'u:f802eab00c6d',
},
{
type: 'text',
label: '分数',
name: 'score',
placeholder: '-',
quickEdit: {
type: 'input-number',
saveImmediately: {
api: {
method: 'patch',
url: 'rest/exam_questions/$id',
data: {
score: '$score',
},
},
},
},
id: 'u:b3daeb51d76d',
},
{
type: 'tpl',
label: '类别',
name: 'exam_question_categories.name',
placeholder: '-',
quickEdit: {
type: 'tree-select',
source: {
method: 'get',
url: 'rest/exam_question_categories?path=cd.root&order=name',
adaptor:
'const recursive = id => {\r\n let nodes = payload.data.items.filter(x => {\r\n return x.parent === id\r\n })\r\n return nodes.map(item => {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: `${item.id}`,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}\r\nlet tops = payload.data.items.filter(x => x.parent === null)\r\nreturn {\r\n ...payload,\r\n data: tops.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}',
},
saveImmediately: {
api: {
method: 'patch',
url: 'rest/exam_questions/$id',
data: {
category: '${exam_question_categories.id}',
},
},
},
name: 'exam_question_categories.id',
initiallyOpen: false,
heightAuto: true,
virtualThreshold: false,
mode: 'popOver',
},
id: 'u:9c16b39eb764',
},
{
type: 'operation',
buttons: [
{
type: 'button-group',
buttons: [
{
type: 'button',
actionType: 'dialog',
dialog: {
type: 'dialog',
title: '修改',
body: [
{
type: 'form',
title: '表单',
api: {
method: 'patch',
url: 'rest/exam_questions/$id',
data: {
content: '$content',
category: '$category',
answer: '$answer',
analysis: '$analysis',
difficulty: '$difficulty',
is_objective: '$is_objective',
type: '$type',
},
dataType: 'json',
requestAdaptor:
'if (api && api.data && api.data.content && api.data.content.options) {\r\n let options = JSON.parse(JSON.stringify(api.data.content.options))\r\n let index = options.findIndex(item => !Object.keys(item).length)\r\n while (index != -1) {\r\n options.splice(index, 1)\r\n index = options.findIndex(item => !Object.keys(item).length)\r\n }\r\n\r\n api.data.content = {\r\n ...api.data.content,\r\n options: options\r\n }\r\n} else if (api && api.data && api.data.type === \'question_true_or_false\') {\r\n api.data.content = {\r\n ...api.data.content,\r\n options: [\r\n { "key": "正确", "label": "正确", "is_fixed": false },\r\n { "key": "错误", "label": "错误", "is_fixed": false }\r\n ]\r\n }\r\n}\r\n\r\nlet pattern = new RegExp("(\\\\.\\\\./){1,6}")\r\napi.data.content.title = api.data.content.title.replace(pattern, "/")\r\n\r\nif (api.data.content.options.length) {\r\n api.data.content.options = api.data.content.options.map(item => {\r\n return {\r\n ...item,\r\n label: item.label.replace(pattern, "/")\r\n }\r\n })\r\n}\r\n\r\napi.data.analysis = api.data.analysis.replace(pattern, "/")\r\n\r\nreturn api',
},
body: [
{
label: '',
type: 'combo',
name: 'content',
multiple: false,
multiLine: true,
joinValues: true,
messages: {},
typeSwitchable: false,
strictMode: true,
mode: 'normal',
clearValueOnHidden: true,
visibleOn:
"this.type === 'question_single_choice' || this.type === 'question_multi_choice'",
items: [
{
type: 'input-rich-text',
label: '题干',
name: 'title',
mode: 'normal',
reciever: {
method: 'post',
url: 'storage/v1/object/exam',
adaptor: '',
data: null,
requestAdaptor: '',
},
vendor: 'tinymce',
options: {
menubar: 'true',
height: 400,
plugins:
'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking table emoticons template paste help',
toolbar:
'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media fullpage | forecolor backcolor emoticons | help',
},
},
{
label: '选项配置',
name: 'options',
type: 'custom-option',
form: {
title: '修改选项',
controls: [
{
type: 'text',
label: '选项',
name: 'key',
},
{
name: 'label',
label: '内容',
type: 'input-rich-text',
vendor: 'tinymce',
options: {
menubar: 'true',
height: 400,
plugins:
'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking table emoticons template paste help',
toolbar:
'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media fullpage | forecolor backcolor emoticons | help',
},
receiver: {
method: 'post',
url: 'storage/v1/object/exam',
adaptor: '',
data: null,
requestAdaptor: '',
},
mode: 'normal',
},
{
type: 'switch',
name: 'is_fixed',
label: '',
option: '是否固定',
optionAtLeft: false,
trueValue: true,
falseValue: false,
},
],
size: 'lg',
},
multiple: true,
btnLabel: '设置选项',
},
],
},
{
type: 'combo',
label: '',
name: 'content',
mode: 'normal',
value: '',
multiple: false,
multiLine: true,
joinValues: true,
messages: {},
typeSwitchable: false,
strictMode: true,
visibleOn:
"this.type === 'question_true_or_false' || this.type === 'question_filling' || this.type === 'question_short_answer'",
clearValueOnHidden: true,
items: [
{
type: 'input-rich-text',
label: '题干',
name: 'title',
vendor: 'tinymce',
options: {
menubar: 'true',
height: 400,
plugins:
'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking table emoticons template paste help',
toolbar:
'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media fullpage | forecolor backcolor emoticons | help',
},
receiver: {
method: 'post',
url: 'storage/v1/object/exam',
adaptor: '',
data: null,
requestAdaptor: '',
},
mode: 'normal',
},
],
},
{
type: 'input-text',
label: '答案',
name: 'answer',
mode: 'normal',
},
{
type: 'input-rich-text',
label: '答案解析',
name: 'analysis',
mode: 'normal',
receiver: {
method: 'post',
url: 'storage/v1/object/exam',
adaptor: '',
data: null,
requestAdaptor: '',
},
vendor: 'tinymce',
options: {
menubar: 'true',
relative_urls: false,
remove_script_host: true,
height: 400,
plugins:
'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking table emoticons template paste help',
toolbar:
'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media fullpage | forecolor backcolor emoticons | help',
},
},
{
type: 'input-text',
label: '难易程度',
name: 'difficulty',
mode: 'normal',
},
{
type: 'select',
name: 'type',
label: '题型',
mode: 'normal',
options: [],
checkAll: false,
source: {
method: 'get',
url: 'rest/dicts?typecode=eq.019',
adaptor:
'return {\r\n ...payload.data,\r\n items: payload.data.items.map(item => {\r\n return {\r\n label: item.dictvalue,\r\n value: item.dictkey\r\n }\r\n })\r\n}',
},
},
{
type: 'formula',
name: 'is_objective',
formula:
"data.type === 'question_single_choice' || data.type === 'question_multi_choice' || data.type === 'question_true_or_false'",
},
{
type: 'hidden',
name: 'is_objective',
},
{
type: 'tree-select',
name: 'category',
label: '类别',
options: [],
source: {
method: 'get',
url: 'rest/exam_question_categories?path=cd.root&order=name',
adaptor:
'const recursive = id => {\r\n let nodes = payload.data.items.filter(x => {\r\n return x.parent === id\r\n })\r\n return nodes.map(item => {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: `${item.id}`,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}\r\nlet tops = payload.data.items.filter(x => x.parent === null)\r\nreturn {\r\n ...payload,\r\n data: tops.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}',
},
mode: 'normal',
heightAuto: true,
virtualThreshold: false,
},
],
},
],
closeOnEsc: true,
showCloseButton: true,
size: 'lg',
},
level: 'link',
size: 'md',
icon: 'fa fa-pencil text-info',
tooltip: '修改',
tooltipPlacement: 'top',
className: 'p-r-none p-l-none',
iconClassName: 'pull-left',
id: 'u:467671f595fd',
},
{
type: 'button',
actionType: 'ajax',
confirmText: '确认删除?',
api: {
method: 'delete',
url: 'rest/exam_questions/$id',
},
level: 'link',
icon: 'fa fa-times text-danger',
size: 'md',
tooltip: '删除',
tooltipPlacement: 'top',
iconClassName: 'pull-left',
className: 'p-r-none p-l-none',
id: 'u:9b3cceb15b58',
},
],
label: '操作',
id: 'u:1c467b0d0cdb',
},
],
label: '操作',
placeholder: '-',
width: 100,
id: 'u:2850d5909876',
},
],
visibleOn: '',
perPageAvailable: ['10', '20', 30, 40, 50],
className: '',
headerToolbar: [
{
type: 'bulk-actions',
},
{
type: 'wrapper',
title: '',
body: [
{
type: 'tpl',
tpl: '${org_name}',
inline: true,
name: 'titletpl',
visibleOn: "this.tree && this.tree.split(',').length === 1",
id: 'u:3f915fa4c5c9',
},
{
type: 'button',
label: '',
actionType: 'dialog',
dialog: {
type: 'dialog',
title: '题目信息',
body: [
{
type: 'form',
title: '表单',
api: {
method: 'patch',
url: 'rest/exam_question_categories/$tree',
data: {
name: '$name',
parent: '$parent',
},
},
initApi: 'rest/exam_question_categories/${tree}',
body: [
{
label: '名称',
type: 'input-text',
name: 'name',
mode: 'normal',
id: 'u:d00a7b4108d3',
},
{
type: 'tree-select',
name: 'parent',
label: '上级目录',
mode: 'normal',
onlyChildren: true,
heightAuto: true,
virtualThreshold: false,
source: {
url: 'rest/exam_question_categories?path=cd.root&order=name',
adaptor:
'const recursive = id => {\r\n let nodes = payload.data.items.filter(x => {\r\n return x.parent === id\r\n })\r\n return nodes.map(item => {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: `${item.id}`,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}\r\nlet tops = payload.data.items.filter(x => x.parent === null)\r\nreturn {\r\n ...payload,\r\n data: tops.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}',
method: 'get',
},
id: 'u:28516ae265d1',
},
],
id: 'u:f314267b4b38',
actions: [
{
type: 'submit',
label: 'Submit',
primary: true,
},
],
feat: 'Edit',
dsType: 'api',
},
],
closeOnEsc: true,
showCloseButton: true,
id: 'u:8a446118216b',
actions: [
{
type: 'button',
actionType: 'cancel',
label: 'Cancel',
id: 'u:63e0b9a383ab',
},
{
type: 'button',
actionType: 'confirm',
label: 'confirm',
primary: true,
id: 'u:3df731dbb334',
},
],
closeOnOutside: false,
showErrorMsg: true,
showLoading: true,
draggable: false,
},
level: 'link',
icon: 'fa fa-edit',
iconClassName: '',
visibleOn: "this.tree && this.tree.split(',').length === 1",
reload: 'cg.tree',
id: 'u:0b1f6849e465',
},
{
type: 'button',
label: '',
actionType: 'ajax',
icon: 'fa fa-trash',
level: 'link',
iconClassName: 'text-danger',
confirmText: '确认删除?',
api: {
method: 'delete',
url: 'rest/exam_question_categories/$tree',
},
reload: 'cg.tree',
visibleOn: "this.tree && this.tree.split(',').length === 1",
id: 'u:e972a2b364b2',
},
],
name: 'header',
id: 'u:373ca0cc2ca7',
},
{
type: 'button',
label: '添加分类',
actionType: 'dialog',
align: 'right',
icon: '',
tpl: '内容',
level: 'primary',
dialog: {
type: 'dialog',
title: '添加分类',
body: [
{
type: 'form',
title: '表单',
api: {
method: 'post',
url: 'rest/exam_question_categories',
requestAdaptor:
'return {\r\n ...api,\r\n data: {\r\n ...api.data,\r\n parent: api.data.parent ? api.data.parent : null\r\n }\r\n}',
headers: {
Prefer: 'params=single-object',
},
adaptor: '',
},
body: [
{
label: '名称',
type: 'input-text',
name: 'name',
required: true,
mode: 'normal',
},
{
type: 'tree-select',
label: '上级目录',
name: 'parent',
mode: 'normal',
source: {
method: 'get',
url: 'rest/exam_question_categories?path=cd.root&order=name',
adaptor:
'const recursive = id => {\r\n let nodes = payload.data.items.filter(x => {\r\n return x.parent === id\r\n })\r\n return nodes.map(item => {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: `${item.id}`,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}\r\nlet tops = payload.data.items.filter(x => x.parent === null)\r\nreturn {\r\n ...payload,\r\n data: tops.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}',
},
onlyChildren: true,
heightAuto: true,
virtualThreshold: false,
disabledOn: 'true',
},
],
},
],
closeOnEsc: true,
showCloseButton: true,
},
reload: 'cg.tree',
id: 'u:88fc9975bcf8',
},
{
type: 'button',
label: '题库导入',
level: 'primary',
actionType: 'dialog',
dialog: {
type: 'dialog',
title: '题库导入',
body: [
{
type: 'form',
title: '表单',
controls: [
{
type: 'tree-select',
label: '分类',
name: 'tree',
mode: 'inline',
options: [],
source: {
method: 'get',
url: 'rest/exam_question_categories?path=cd.root&order=name',
adaptor:
'const recursive = id => {\r\n let nodes = payload.data.items.filter(x => {\r\n return x.parent === id\r\n })\r\n return nodes.map(item => {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: `${item.id}`,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}\r\nlet tops = payload.data.items.filter(x => x.parent === null)\r\nreturn {\r\n ...payload,\r\n data: tops.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}',
},
size: 'sm',
initiallyOpen: false,
heightAuto: true,
virtualThreshold: false,
},
{
type: 'examimport',
name: 'import',
mode: 'inline',
label: '请选择要导入的Word',
visibleOn: 'this.tree',
},
{
type: 'table',
name: 'import',
mode: 'normal',
label: '',
columns: [
{
label: '题干',
name: 'title',
quickEdit: {
type: 'input-rich-text',
name: 'title',
saveImmediately: {
api: {
method: 'patch',
url: 'rest/exam_questions/$id',
data: {
content: '$content',
},
},
},
receiver: {
method: 'post',
url: 'storage/v1/object/exam',
adaptor: '',
data: null,
requestAdaptor: '',
},
vendor: 'tinymce',
options: {
menubar: 'true',
height: 400,
plugins:
'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking table emoticons template paste help',
toolbar:
'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media fullpage | forecolor backcolor emoticons | help',
},
mode: 'popOver',
},
type: 'tpl',
width: 400,
placeholder: '-',
},
{
label: '选项',
name: 'options',
type: 'each',
items: {
type: 'tpl',
tpl: '<%= data.key %>. <%= data.label %>',
inline: false,
},
quickEdit: {
type: 'custom-option',
form: {
title: '修改选项',
controls: [
{
type: 'text',
label: '选项',
name: 'key',
disabledOn: 'true',
},
{
name: 'label',
label: '内容',
type: 'input-rich-text',
saveImmediately: {
api: {
method: 'patch',
url: 'rest/exam_questions/$id',
data: {
content: '$content',
},
},
},
receiver: {
method: 'post',
url: 'storage/v1/object/exam',
adaptor: '',
data: null,
requestAdaptor: '',
},
vendor: 'tinymce',
options: {
menubar: 'true',
height: 400,
plugins:
'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking table emoticons template paste help',
toolbar:
'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media fullpage | forecolor backcolor emoticons | help',
},
},
{
type: 'switch',
name: 'is_fixed',
label: '',
option: '是否固定',
optionAtLeft: false,
trueValue: true,
falseValue: false,
},
],
size: 'lg',
},
multiple: true,
btnLabel: '设置选项',
name: 'options',
mode: 'popOver',
},
quickEditEnabledOn:
"this.type === '单选题' || this.type === '多选题'",
placeholder: '-',
},
{
label: '答案',
name: 'answer',
type: 'tpl',
quickEdit: {
type: 'text',
},
},
{
label: '答案解析',
name: 'analysis',
quickEdit: {
type: 'input-rich-text',
name: 'analysis',
saveImmediately: {
api: {
method: 'patch',
url: 'rest/exam_questions/$id',
data: {
content: '$content',
},
},
},
receiver: {
method: 'post',
url: 'storage/v1/object/exam',
adaptor: '',
data: null,
requestAdaptor: '',
},
vendor: 'tinymce',
options: {
menubar: 'true',
relative_urls: false,
remove_script_host: true,
height: 400,
plugins:
'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking table emoticons template paste help',
toolbar:
'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media fullpage | forecolor backcolor emoticons | help',
},
mode: 'popOver',
},
type: 'tpl',
placeholder: '-',
},
{
label: '难度',
name: 'difficulty',
quickEdit: {
type: 'select',
options: [
{
label: '简单',
value: '简单',
},
{
label: '中等',
value: '中等',
},
{
label: '困难',
value: '困难',
},
],
},
type: 'tpl',
width: 10,
},
{
label: '题型',
name: 'type',
quickEdit: {
type: 'select',
options: [
{
label: '单选题',
value: '单选题',
},
{
label: '多选题',
value: '多选题',
},
{
label: '判断题',
value: '判断题',
},
],
},
type: 'tpl',
width: 50,
},
],
},
],
api: {
method: 'post',
url: 'rest/exam_questions',
data: {
'&': '$import',
},
requestAdaptor:
"return {\r\n ...api,\r\n data: api.data.map(item => {\r\n let content = {}\r\n if (item.options) {\r\n let options = JSON.parse(JSON.stringify(item.options))\r\n let index = options\r\n .findIndex(item => !Object.keys(item).length)\r\n\r\n while (index != -1) {\r\n options.splice(index, 1)\r\n index = options\r\n .findIndex(item => !Object.keys(item).length)\r\n }\r\n\r\n content = {\r\n title: item.title ? item.title : '',\r\n options: options\r\n }\r\n } else {\r\n content = {\r\n title: item.title ? item.title : ''\r\n }\r\n }\r\n if (!item.type) {\r\n return {\r\n content: content,\r\n answer: item.answer ? item.answer : '',\r\n analysis: item.analysis ? item.analysis : '',\r\n difficulty: item.difficulty ? item.difficulty : '',\r\n category: item.category,\r\n type: 'question_unknow',\r\n organization_id: item.organization_id,\r\n is_objective: true\r\n }\r\n } else if(item.type === '单选题') {\r\n return {\r\n content: content,\r\n answer: item.answer ? item.answer : '',\r\n analysis: item.analysis ? item.analysis : '',\r\n difficulty: item.difficulty ? item.difficulty : '',\r\n category: item.category,\r\n type: 'question_single_choice',\r\n organization_id: item.organization_id,\r\n is_objective: true\r\n }\r\n } else if(item.type === '多选题') {\r\n return {\r\n content: content,\r\n answer: item.answer ? item.answer : '',\r\n analysis: item.analysis ? item.analysis : '',\r\n difficulty: item.difficulty ? item.difficulty : '',\r\n category: item.category,\r\n type: 'question_multi_choice',\r\n organization_id: item.organization_id,\r\n is_objective: true\r\n }\r\n } else if(item.type === '判断题') {\r\n return {\r\n content: content,\r\n answer: item.answer ? item.answer : '',\r\n analysis: item.analysis ? item.analysis : '',\r\n difficulty: item.difficulty ? item.difficulty : '',\r\n category: item.category,\r\n type: 'question_true_or_false',\r\n organization_id: item.organization_id,\r\n is_objective: true\r\n }\r\n } else if(item.type === '填空题') {\r\n return {\r\n content: content,\r\n answer: item.answer ? item.answer : '',\r\n analysis: item.analysis ? item.analysis : '',\r\n difficulty: item.difficulty ? item.difficulty : '',\r\n category: item.category,\r\n type: 'question_filling',\r\n organization_id: item.organization_id,\r\n is_objective: false\r\n }\r\n } else if(item.type === '简答题') {\r\n return {\r\n content: content,\r\n answer: item.answer ? item.answer : '',\r\n analysis: item.analysis ? item.analysis : '',\r\n difficulty: item.difficulty ? item.difficulty : '',\r\n category: item.category,\r\n type: 'question_short_answer',\r\n organization_id: item.organization_id,\r\n is_objective: false\r\n }\r\n } else {\r\n return {\r\n content: content,\r\n answer: item.answer ? item.answer : '',\r\n analysis: item.analysis ? item.analysis : '',\r\n difficulty: item.difficulty ? item.difficulty : '',\r\n category: item.category,\r\n type: 'question_unknow',\r\n organization_id: item.organization_id,\r\n is_objective: false\r\n }\r\n }\r\n })\r\n}",
},
target: '',
name: 'form',
actions: [
{
actionType: 'reload',
target: 'recognize',
},
],
},
],
closeOnEsc: true,
showCloseButton: true,
size: 'xl',
},
className: '',
align: 'right',
visibleOn: 'this.tree',
id: 'u:9208bc84a232',
},
{
type: 'button',
label: '添加试题',
level: 'primary',
actionType: 'dialog',
dialog: {
type: 'dialog',
title: '添加试题',
body: [
{
type: 'form',
title: '表单',
api: {
method: 'post',
url: 'rest/exam_questions',
data: {
organization_id: '${centre_id}',
type: '$type',
content: '$content',
answer: '$answer',
analysis: '$analysis',
score: '$score',
difficulty: '$difficulty',
category: '$tree',
is_objective: '$is_objective',
},
requestAdaptor:
'if (api && api.data && api.data.content && api.data.content.options) {\r\n let options = JSON.parse(JSON.stringify(api.data.content.options))\r\n let index = options.findIndex(item => !Object.keys(item).length)\r\n while (index != -1) {\r\n options.splice(index, 1)\r\n index = options.findIndex(item => !Object.keys(item).length)\r\n }\r\n\r\n api.data.content = {\r\n ...api.data.content,\r\n options: options\r\n }\r\n} else if (api && api.data && api.data.type === \'question_true_or_false\') {\r\n api.data.content = {\r\n ...api.data.content,\r\n options: [\r\n { "key": "正确", "label": "正确", "is_fixed": false },\r\n { "key": "错误", "label": "错误", "is_fixed": false }\r\n ]\r\n }\r\n}\r\n\r\nlet pattern = new RegExp("(\\\\.\\\\./){1,6}")\r\napi.data.content.title = api.data.content.title.replace(pattern, "/")\r\n\r\nif (api.data.content.options.length) {\r\n api.data.content.options = api.data.content.options.map(item => {\r\n return {\r\n ...item,\r\n label: item.label.replace(pattern, "/")\r\n }\r\n })\r\n}\r\n\r\napi.data.analysis = api.data.analysis.replace(pattern, "/")\r\n\r\nreturn api',
},
body: [
{
label: '题型',
type: 'select',
name: 'type',
mode: 'normal',
options: [],
checkAll: false,
source: {
method: 'get',
url: 'rest/dicts?select=value:dictkey,label:dictvalue&typecode=eq.019&dictkey=neq.question_unknow',
},
},
{
type: 'formula',
name: 'is_objective',
formula:
"data.type === 'question_single_choice' || data.type === 'question_multi_choice' || data.type === 'question_true_or_false'",
},
{
type: 'hidden',
name: 'is_objective',
},
{
type: 'combo',
label: '',
name: 'content',
mode: 'normal',
multiple: false,
multiLine: true,
joinValues: true,
messages: {},
typeSwitchable: false,
strictMode: true,
value: {
options: [
{
key: 'A',
label: '',
is_fixed: false,
},
{
key: 'B',
label: '',
is_fixed: false,
},
{
key: 'C',
label: '',
is_fixed: false,
},
{
key: 'D',
label: '',
is_fixed: false,
},
],
},
visibleOn:
"this.type === 'question_single_choice' || this.type === 'question_multi_choice'",
clearValueOnHidden: true,
items: [
{
type: 'input-rich-text',
label: '题干',
name: 'title',
vendor: 'tinymce',
options: {
menubar: 'true',
height: 400,
plugins:
'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking table emoticons template paste help',
toolbar:
'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media fullpage | forecolor backcolor emoticons | help',
},
receiver: {
method: 'post',
url: 'storage/v1/object/exam',
adaptor: '',
data: null,
requestAdaptor: '',
},
mode: 'normal',
},
{
label: '选项配置',
name: 'options',
type: 'custom-option',
form: {
title: '修改选项',
controls: [
{
type: 'text',
label: '选项',
name: 'key',
disabledOn: 'true',
},
{
name: 'label',
label: '内容',
type: 'input-rich-text',
vendor: 'tinymce',
options: {
menubar: 'true',
height: 400,
plugins:
'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking table emoticons template paste help',
toolbar:
'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media fullpage | forecolor backcolor emoticons | help',
},
receiver: {
method: 'post',
url: 'storage/v1/object/exam',
adaptor: '',
data: null,
requestAdaptor: '',
},
mode: 'normal',
},
{
type: 'switch',
name: 'is_fixed',
label: '',
option: '是否固定',
optionAtLeft: false,
trueValue: true,
falseValue: false,
},
],
size: 'lg',
},
multiple: true,
btnLabel: '设置选项',
},
],
},
{
type: 'combo',
name: 'content',
label: '',
mode: 'normal',
value: '',
multiple: false,
multiLine: true,
joinValues: true,
messages: {},
typeSwitchable: false,
strictMode: true,
visibleOn:
"this.type === 'question_true_or_false' || this.type === 'question_filling' || this.type === 'question_short_answer'",
clearValueOnHidden: true,
items: [
{
type: 'input-rich-text',
label: '题干',
name: 'title',
vendor: 'tinymce',
options: {
menubar: 'true',
height: 400,
plugins:
'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking table emoticons template paste help',
toolbar:
'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media fullpage | forecolor backcolor emoticons | help',
},
receiver: {
method: 'post',
url: 'storage/v1/object/exam',
adaptor: '',
data: null,
requestAdaptor: '',
},
mode: 'normal',
},
],
},
{
type: 'input-text',
label: '答案',
name: 'answer',
mode: 'normal',
},
{
type: 'input-rich-text',
label: '答案解析',
name: 'analysis',
mode: 'normal',
receiver: {
method: 'post',
url: 'storage/v1/object/exam',
adaptor: '',
data: null,
requestAdaptor: '',
},
vendor: 'tinymce',
options: {
menubar: 'true',
relative_urls: false,
remove_script_host: true,
height: 400,
plugins:
'advlist autolink link image lists charmap print preview hr anchor pagebreak spellchecker searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking table emoticons template paste help',
toolbar:
'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media fullpage | forecolor backcolor emoticons | help',
},
},
{
type: 'input-number',
label: '分数',
name: 'score',
mode: 'normal',
value: 1,
},
{
type: 'select',
label: '难易程度',
name: 'difficulty',
mode: 'normal',
options: [
{
label: '简单',
value: '简单',
},
{
label: '中等',
value: '中等',
},
{
label: '困难',
value: '困难',
},
],
checkAll: false,
},
{
type: 'tree-select',
label: '类别',
name: 'tree',
options: [],
mode: 'normal',
source: {
method: 'get',
url: 'rest/exam_question_categories?path=cd.root&order=name',
adaptor:
'const recursive = id => {\r\n let nodes = payload.data.items.filter(x => {\r\n return x.parent === id\r\n })\r\n return nodes.map(item => {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: `${item.id}`,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}\r\nlet tops = payload.data.items.filter(x => x.parent === null)\r\nreturn {\r\n ...payload,\r\n data: tops.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}',
},
initiallyOpen: false,
heightAuto: true,
virtualThreshold: false,
},
],
},
],
closeOnEsc: true,
showCloseButton: true,
size: 'lg',
},
visibleOn: 'this.tree',
align: 'right',
id: 'u:79a6cf04b4d0',
},
{
type: 'button',
label: '题库模板示例',
onEvent: {
click: {
actions: [
{
args: {
url: '/download/题库模板参考.docx',
},
actionType: 'url',
},
],
weight: 0,
},
},
level: 'success',
icon: 'fa fa-download',
align: 'right',
id: 'u:06075e8d295a',
},
{
type: 'button',
label: '题库模板说明',
onEvent: {
click: {
actions: [
{
args: {
url: '/download/题库模板说明.docx',
},
actionType: 'url',
},
],
weight: 0,
},
},
level: 'success',
icon: 'fa fa-download',
align: 'right',
id: 'u:2f4c3be56c4f',
},
],
footerToolbar: [
{
type: 'pagination',
},
{
type: 'switch-per-page',
},
{
type: 'statistics',
},
],
bulkActions: [
{
label: '批量移动',
type: 'button',
actionType: 'dialog',
dialog: {
type: 'dialog',
title: '移动到分类',
body: [
{
type: 'form',
title: '表单',
api: {
method: 'patch',
url: 'rest/exam_questions?id=in.(${ids})',
data: {
category: '$category',
},
requestAdaptor:
'return {\r\n ...api,\r\n data: {\r\n category: api.data.category ? api.data.category : null\r\n }\r\n}',
},
body: [
{
type: 'tree-select',
label: '分类',
name: 'category',
options: [],
source: {
method: 'get',
url: 'rest/exam_question_categories?path=cd.root&order=name',
adaptor:
'const recursive = id => {\r\n let nodes = payload.data.items.filter(x => {\r\n return x.parent === id\r\n })\r\n return nodes.map(item => {\r\n return {\r\n ...item,\r\n label: item.name,\r\n value: `${item.id}`,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}\r\nlet tops = payload.data.items.filter(x => x.parent === null)\r\nreturn {\r\n ...payload,\r\n data: tops.map(item => {\r\n return {\r\n label: item.name,\r\n value: item.id,\r\n children: recursive(item.id)\r\n }\r\n })\r\n}',
},
mode: 'normal',
heightAuto: true,
virtualThreshold: false,
},
],
},
],
closeOnEsc: true,
showCloseButton: true,
},
id: 'u:1900a8775c6a',
},
{
label: '批量删除',
actionType: 'ajax',
api: {
method: 'delete',
url: 'rest/exam_questions?id=in.(${ids})',
data: null,
},
confirmText: '确认删除?',
type: 'button',
id: 'u:b24dc3837adb',
},
],
md: 9,
columnsTogglable: false,
filter: null,
quickSaveItemApi: '',
hideQuickSaveBtn: true,
id: 'u:d5ccedbd35f2',
},
],
title: '',
messages: {},
bodyClassName: '',
definitions: {
options: {
type: 'combo',
multiple: true,
multiLine: true,
controls: [
{
type: 'group',
controls: [
{
label: '选项',
name: 'key',
type: 'text',
},
{
label: '内容',
name: 'label',
type: 'text',
},
{
type: 'switch',
option: '开关',
name: 'is_fixed',
label: '是否固定',
},
],
label: false,
},
],
},
},
id: 'u:757690c834b6',
asideResizor: false,
pullRefresh: {
disabled: true,
},
};
export { schema };