You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

669 lines
22 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/** 资质考试 */
//import { AlignLeftOutlined, PlusOutlined } from '@ant-design/icons';
import { Switch, Button, Card, Col, List, Menu, Progress, Row, Typography, Space, Divider, Radio, Checkbox, Tag, Dropdown, Upload, Modal, Form, Input, message, Popconfirm } from 'antd';
import { PageContainer } from '@ant-design/pro-layout';
//import { useRequest } from 'umi';
//import { queryFakeList } from './service';
//import type { CardListItemDataType } from './data';
import styles from './style.less';
//import SubMenu from 'antd/lib/menu/SubMenu';
//import ProCard from '@ant-design/pro-card';
import ProList from '@ant-design/pro-list';
import { ReactText, useEffect, useRef, useState } from 'react';
import { PlusOutlined, DeleteOutlined, DownloadOutlined, UploadOutlined, EditOutlined, EyeOutlined, EyeInvisibleOutlined, DownOutlined, UserOutlined } from '@ant-design/icons';
import { exportQuestionTemplate, queryQuestionById, queryQuestionList, queryQuestionType, removeQuestion, saveQuestion } from './service';
import { useParams, useRequest, history, useRouteMatch } from 'umi';
import { queryCourseListByTag, queryCourseView, queryTagList } from '@/pages/course/option/service';
import ProForm, { BetaSchemaForm, ProFormCheckbox, ProFormList, ProFormRadio, ProFormSelect, ProFormText } from '@ant-design/pro-form';
import { DataItem } from '../dashboard/analysis/data';
import { ActionType, ProColumns } from '@ant-design/pro-table';
import ProFormRichEdit from '../course/subject/components/ProFormRichEdit';
import { TableListItem } from '../course/option/data';
import AnswersSelector from './components/AnswersEditor';
//const { Paragraph } = Typography;
const { Text, Link } = Typography;
const labels = ['A','B','C','D','E','F','G','H','I','J','K'];
const parsingMap = new Map()
console.log('first');
/**
* 添加试题
*
* @param fields
*/
const handleAdd = async (fields: TableListItem) => {
const hide = message.loading('正在添加');
try {
await saveQuestion({ ...fields});
hide();
message.success('添加成功');
return true;
} catch (error) {
hide();
message.error('添加失败请重试!');
return false;
}
};
/**
* 删除试题
*
* @param selectedRows
*/
const handleRemove = async (selectedRows: TableListItem[]) => {
const hide = message.loading('正在删除');
if (!selectedRows) return true;
try {
const {code, msg} = await removeQuestion({
key: selectedRows.map((row) => row.key),
});
hide();
if(code === 2000 ){
message.success('删除成功,即将刷新');
}else{
message.warning(msg);
}
return true;
} catch (error) {
hide();
message.error('删除失败,请重试');
return false;
}
};
const QuestionBank = () => {
const match = useRouteMatch();
console.log('match', match);
const type = history.location.pathname === '/questionbank/attestation' ? 1 : 0 ; // 题库类型
const actionRef = useRef<ActionType>();
const [questionType, setQuestionType] = useState([]);
const [parsing, setParsing] = useState();
const [createModalVisible, handleCreateModalVisible] = useState<boolean>(false);
const [updateModalVisible, handleUpdateModalVisible] = useState<boolean>(false);
const [selectedRowsState, setSelectedRows] = useState<API.RuleListItem[]>([]);
const [currentRow, setCurrentRow] = useState<TableListItem>();
const [expandedDescRowKeys, setExpandedDescRowKeys] = useState<readonly ReactText[]>([]); // 展开解析设置
const [addType, setAddType] = useState({name: '', value: 0});
/** 表单项定义 */
const columns: ProColumns<TableListItem>[] = [
{
title: '标签',
valueType: 'select',
dataIndex: 'tags',
sorter: false,
hideInTable: true,
hideInForm: false,
hideInSearch: true,
fieldProps: {
mode: "multiple"
},
renderText: (val: string) => `${val}`,
request: async () => {
const { data: Items } = await queryTagList({});
console.log('queryTagList...')
const tags = []
for (let i = 0; i < Items.length; i++) {
tags.push({ label: Items[i].tag_name, value: Items[i].tag_id })
}
console.log(tags, 'tags:::');
return tags;
},
},
{
title: '课程',
valueType: 'select',
dataIndex: 'course_id',
sorter: false,
hideInTable: false,
hideInForm: false,
hideInSearch: true,
fieldProps: {
//mode: "multiple"
},
formItemProps: {
rules: [
{
required: true,
message: '请填选择课程',
},
]
},
renderText: (val: string) => `${val}`,
dependencies: ['tags'],
request: async (params) => {
const {tags} = params;
const { data: Items } = await queryCourseListByTag({tag_ids: tags?.toString()});
console.log('queryCourseListByTag...')
const courses = []
for (let i = 0; i < Items?.length; i++) {
courses.push({ label: Items[i]?.course_name, value: Items[i]?.course_id })
}
console.log(courses, 'courses:::');
return courses;
},
},
{
title: '题干',
dataIndex: 'question_stem',
valueType: 'text',
hideInTable: false,
hideInDescriptions: false,
hideInForm: false,
hideInSearch: true,
formItemProps: {
rules: [
{
required: true,
message: '请填题干内容',
},
]
},
},
{
title: '选项',
dataIndex: 'answers',
valueType: 'text',
sorter: false,
hideInTable: false,
hideInForm: false,
hideInSearch: true,
renderFormItem: (item, { defaultRender, ...rest }, form) => (
<AnswersSelector />
)
/*
formItemProps: {
rules: [
{
required: true,
message: '请填写试题解析',
},
]
},*/
},
{
title: '答案',
dataIndex: 'answertrue',
valueType: 'text',
sorter: false,
hideInTable: false,
hideInForm: false,
hideInSearch: true,
renderFormItem: (item, { defaultRender, ...rest }, form) => {
// 需要处理 关联长度变化及编辑回显
console.log('edit answertrue11', item)
console.log('form::::::',form.getFieldValue('answertrue'))
// 0 单选 1 多选 2 判断
return (addType?.value !== 1 ) ?
<Radio.Group
name="answertrue"
defaultValue={'A'}
options={['A', 'B', 'C', 'D']}
onChange={(e)=>{
form.setFieldsValue({answertrue: 'B'})
}}
/>
:
<ProFormCheckbox.Group
name="answertrue"
label=""
initialValue={[]}
options={['A', 'B', 'C', 'D']}
/>
},
/*
formItemProps: {
rules: [
{
required: true,
message: '请填答案',
},
]
},*/
},
{
title: '解析',
dataIndex: 'parsing',
valueType: 'textarea',
sorter: false,
hideInTable: false,
hideInForm: false,
hideInSearch: true,
formItemProps: {
rules: [
{
required: true,
message: '请填写试题解析',
},
]
},
renderText: (val: string) => (<div dangerouslySetInnerHTML={{__html: val}} />),
renderFormItem: (item, { defaultRender, ...rest }, form) => (
<ProFormRichEdit
name="parsing"
label=""
width="xl"
// tooltip="最长为 6 位汉字,需要与考生身份证一致"
placeholder="请填写试题解析"
// rules={[{ required: true }]}
value=""
// disabled
/>
),
},
]
/** 获取题型 */
const { data } = useRequest(() => {
return queryQuestionType();
},{
formatResult: (result) => {
return result.list;
}
});
/*
const { data: template } = useRequest(() => {
return exportQuestionTemplate();
});
*/
useEffect(() => {
setQuestionType(data || []);
return ()=>{
/** 退出当前页面清空Map */
parsingMap.clear();
}
}, [data]);
//saveQuestion
return (
<PageContainer content={false} extraContent={false} className={styles.questionbank}>
<ProList<any>
itemLayout="vertical"
actionRef={actionRef}
rowClassName='questionbank-list-item'
pagination={{
defaultPageSize: 10,
showSizeChanger: false,
}}
toolBarRender={() => {
const menuItems = [];
console.log('toolBarRender', questionType);
if(questionType?.length > 0){
console.log('push');
questionType.forEach((item: {code: number, name: string})=>{
menuItems.push(<Menu.Item key={item?.code}>{item?.name}</Menu.Item>)
})
}
const menu = (
<Menu onClick={(value)=>{
console.log('menu11', value);
console.log('menu11', value.key);
console.log('menuquestionType', );
setAddType({ name: questionType[Number(value?.key)]?.name, value: Number(value?.key)});
handleCreateModalVisible(true);
}}>
{menuItems}
</Menu>
);
return [
<Dropdown overlay={menu}>
<Button >
<PlusOutlined /> <DownOutlined />
</Button>
</Dropdown>,
/*
<Button key="remove" type="default" danger>
<DeleteOutlined /> 批量删除
</Button>*/,
<Button key="download" onClick={()=>{window.location.href='/dsideal_yy/zygh/training/exportQuestionTemplate'}} >
<DownloadOutlined />
</Button>,
<Upload
accept='.xlsx'
showUploadList={false}
action="/dsideal_yy/zygh/training/importQuestionData"
data={
{
type: type,
}
}
>
<Button key="upload" >
<UploadOutlined />
</Button>
</Upload>,
];
}}
onRow={(record: any) => {
/*
return {
onMouseEnter: () => {
console.log(record);
},
onClick: () => {
console.log(record);
},
};*/
}}
rowKey="id"
headerTitle={false}
tooltip={false}
request={async (value) => {
console.log('value', value)
/*
const { create_time } = value;
if (create_time) {
value.begin_time = create_time[0]
value.end_time = create_time[1]
}*/
const questions = await queryQuestionList({
...value,
type: type,
page_number: value?.current || 1,
page_size: value?.pageSize,
});
// 课程名称及课程标签
console.log('data',questions);
const data = []
for(let i=0; i<questions?.table_List.length; i++){
const { data: course } = await queryCourseView({
course_id: questions?.table_List[i]?.course_id
});
data[i] = {...questions?.table_List[i], course_name:course?.course_name, tag_name:course?.tag_name, };
}
return {
current: questions?.pageNumber,
data: data,
pageSize: questions?.pageSize,
success: true,
total: questions?.totalRow || 0,
};
}}
//dataSource={dataSource}
rowSelection={{
onChange: (_, selectedRows) => {
setSelectedRows(selectedRows);
},
}}
// grid={{ gutter: 16, column: 1 }}
showActions="always"
showExtra="always"
metas={{
title: {
dataIndex: 'question_stem',
render: (text: React.ReactNode, record: T, index: number) => (<><Text code style={{width:80, fontSize:10, color:'bfbfbf'}}>{record?.id}</Text> {text}</>),
},
avatar: {
dataIndex: 'question_type',
valueType: 'text',
render: (text: React.ReactNode, record: T, index: number) => {
const type = questionType?.filter((item, idx, self)=>{
console.log('FFFF', item, idx, self);
return item?.code === record.question_type
});
console.log('type', type?.name);
console.log('type', type?.name);
console.log('questionType::::',questionType);
return `[${type[0]?.name}]`
},
},
description: {
dataIndex: 'answers',
valueType: 'checkbox',
render: (text: React.ReactNode, record: T, index: number) => {
console.log('description answers', record)
return (
<List
header={false}
footer={false}
bordered={false}
dataSource={record.answers || []}
rowKey='id'
renderItem={ (item, key) => {
console.log('item', item);
return (
<List.Item>
<Typography.Text mark={false}>{`${(record?.question_type === 2) ? ['A','B'][key] : labels[key]}. ${item?.answer}`}</Typography.Text>
</List.Item>)
}}
/>
);
},
},
subTitle: { },
content: {
render: (text: React.ReactNode, record: T, index: number) => {
let answer = '';
const answertrue = record?.answertrue?.split(',');
console.log('answertrue', answertrue)
/** 题型 */
switch(record?.question_type){
case 0: // 单选
case 1: // 多选
answer = labels?.filter((x, idx, self)=>`${answertrue[idx]}` === `1`).toString()
break;
case 2: // 判断
answer = ['A', 'B']?.filter((x, idx, self)=>`${answertrue[idx]}` === `1`).toString()
break;
}
console.log('expandedDescRowKeys', expandedDescRowKeys)
if(expandedDescRowKeys?.indexOf(record.id) > -1){
return (
<Space direction="vertical" style={{borderTop:'solid 1px #f0f0f0', padding:10, margin: '5px -18px 0 -24px', background: '#fdfdfd'}}>
<Typography>
{answer}
</Typography>
<Typography><div dangerouslySetInnerHTML={{__html: parsing.get(record.id)}} /></Typography>
</Space>
);
}else{
return (
<Space direction="vertical" style={{borderTop:'solid 1px #f0f0f0', padding:10, margin: '5px -18px 0 -24px', background: '#fdfdfd'}}>
<Typography>
{answer}
</Typography>
</Space>
)
}
},
},
actions: {
cardActionProps: 'extra',
render: (text: React.ReactNode, record: T, _index: number) => {
let eye
if(expandedDescRowKeys?.indexOf(record.id) > -1){
eye = <><EyeInvisibleOutlined /> </>
}else{
eye = <><EyeOutlined /> </>
}
return(
<Row style={{padding:'10px 24px'}}>
<Col flex={1} style={{textAlign:'left'}}>
<Space direction="horizontal" size="large">
<Typography>{record?.create_time}</Typography>
<Typography>{record?.tag_name}</Typography>
<Typography>{record?.course_name}</Typography>
</Space>
</Col>
<Col flex={1} style={{textAlign:'right'}}>
<Space direction="horizontal" size="middle">
<a onClick={()=>{
console.log('record',record)
setCurrentRow(record);
handleUpdateModalVisible(true)
return false;
}} target="_blank" rel="noopener noreferrer" key="link">
<EditOutlined />
</a>
<Popconfirm key="popconfirm" title={`确认删除当前项吗?`} okText="是" cancelText="否"
onConfirm={async () => {
const success = await handleRemove([{ key: record?.id }]); // 调用批量删除函数如果接口不支持批量需要在service中处理
if (success) {
// handleModalVisible(false);
if (actionRef.current) {
setSelectedRows([]);
actionRef.current?.reload();
}
}
}}
>
<a key="remove" ><DeleteOutlined /> </a>
</Popconfirm>
<a
key="view"
onClick={async ()=>{
if(expandedDescRowKeys?.indexOf(record.id) > -1){
const descRowKeys = expandedDescRowKeys?.filter((item, idx, self)=>{
console.log('FFFF', item, idx, self);
return item !== record.id
});
setExpandedDescRowKeys([...descRowKeys]);
}else{
const { bean } = await queryQuestionById({id: record.id})
parsingMap.set(bean.id, bean.parsing)
setParsing(parsingMap);
console.log('parsing', parsing);
setExpandedDescRowKeys([...expandedDescRowKeys, record.id]);
}
console.log('record id:', record.id);
console.log('expandedDescRowKeys......', expandedDescRowKeys)
}}
>
{eye}
</a>
</Space>
</Col>
</Row>
)
}
},
}}
/>
<Modal
title={`新建${addType?.name }`}
//
width="60%"
visible={createModalVisible}
destroyOnClose
onCancel={() => {
handleCreateModalVisible(false);
}}
footer={null}
>
<BetaSchemaForm<DataItem>
layout="horizontal"
layoutType="Form"
labelCol={{ span: 8 }}
wrapperCol={{ span: 12 }}
onFinish={async (values: any) => {
// 表单处理
console.log('columns:', columns);
console.log('values:', values);
const opts = [];
values?.answers?.forEach((item, key)=>{
const isTrue = labels[key] === values?.answertrue ? 1 : 0; // 判断是否为正确答案
opts.push({answer:item, is_true:isTrue})
})
const success = await handleAdd({
...values,
type: type, // 必填0-常规题,1-资质考试题
answers: JSON.stringify(opts),
question_type: addType.value,
// subject_id: params?.id || 0,
});
console.log('123')
if(success){
handleCreateModalVisible(false);
actionRef.current?.reload();
}
}}
submitter={{
render: (props, doms) => (
<Row>
<Col span={12} offset={8}>
<Space>{doms}</Space>
</Col>
</Row>
),
}}
// action = ''
title="新建"
columns={columns}
/>
</Modal>
<Modal
title={`编辑${addType?.name }`}
//
width="60%"
visible={updateModalVisible}
destroyOnClose
onCancel={() => {
handleUpdateModalVisible(false);
}}
footer={null}
>
<BetaSchemaForm<DataItem>
layout="horizontal"
layoutType="Form"
labelCol={{ span: 8 }}
wrapperCol={{ span: 12 }}
request={()=>{
//
console.log('currentRow',currentRow)
//answers:currentRow?.answers.map((item)=>(item.answer))
return {...currentRow, answers: currentRow?.answers.map((item)=>(item.answer)), answer_true: 1 }
}}
onFinish={async (values: any) => {
// 表单处理
console.log('columns:', columns);
console.log('values:', values);
const opts = [];
values?.answers?.forEach((item)=>{
opts.push({answer:item, is_true:0})
})
const success = await handleAdd({
...values,
type: type, // 必填0-常规题,1-资质考试题
answers: JSON.stringify(opts),
question_type: addType.value,
// subject_id: params?.id || 0,
});
console.log('123')
if(success){
handleCreateModalVisible(false);
actionRef.current?.reloadAndRest?.();
}
}}
submitter={{
render: (props, doms) => (
<Row>
<Col span={12} offset={8}>
<Space>{doms}</Space>
</Col>
</Row>
),
}}
// action = ''
title="编辑"
columns={columns}
/>
</Modal>
</PageContainer>
);
};
export default QuestionBank;