Commit 27d81322 by guomingpang
parents 7becb496 81d33c35
...@@ -20,7 +20,8 @@ interface FetchParams { ...@@ -20,7 +20,8 @@ interface FetchParams {
} }
interface FetchOptions { interface FetchOptions {
requestType: string // 请求类型 form为表单类型 json为json类型,默认json类型 requestType: string, // 请求类型 form为表单类型 json为json类型,默认json类型
reject: boolean,
} }
interface HeadersType{ interface HeadersType{
storeId?:any, storeId?:any,
...@@ -33,7 +34,7 @@ class Axios { ...@@ -33,7 +34,7 @@ class Axios {
method: string, method: string,
url: string, url: string,
params: any, params: any,
options: FetchOptions = { requestType: 'json' } options: FetchOptions = { requestType: 'json', reject: false }
): Promise<any> { ): Promise<any> {
const _url = `${url}?storeId=${User.getStoreId()}&token=${User.getToken()}&storeUserId=${User.getStoreUserId()}&userId=${User.getUserId()}`; const _url = `${url}?storeId=${User.getStoreId()}&token=${User.getToken()}&storeUserId=${User.getStoreUserId()}&userId=${User.getUserId()}`;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
...@@ -86,8 +87,9 @@ class Axios { ...@@ -86,8 +87,9 @@ class Axios {
const { message: ResMessage, success, resultMsg, resultCode } = response.data; const { message: ResMessage, success, resultMsg, resultCode } = response.data;
if (success || resultCode === 0) { if (success || resultCode === 0) {
return response; return response;
} else if (!options.reject) {
message.error(ResMessage || resultMsg);
} }
message.error(ResMessage || resultMsg);
return Promise.reject(response.data); return Promise.reject(response.data);
}, (error): AxiosPromise => { }, (error): AxiosPromise => {
const requestStatus = error.request.status const requestStatus = error.request.status
......
import React, { useEffect, useState } from 'react';
import { Empty, ConfigProvider, Table } from 'antd';
function XMTable(props) {
const [empty, setEmpty] = useState(props.renderEmpty || {});
const [data, setData] = useState({});
useEffect(() => {
setEmpty(props.renderEmpty || {})
setData(props);
}, [props]);
// 自定义表格空状态
function customizeRenderEmpty() {
return (
<Empty
image={empty.image || Empty.PRESENTED_IMAGE_SIMPLE}
imageStyle={{
height: 150,
}}
description={empty.description}
></Empty>
);
};
return (
<ConfigProvider className="xm-table-component" renderEmpty={customizeRenderEmpty}>
<Table
{...data}
/>
</ConfigProvider>
)
}
export default XMTable;
\ No newline at end of file
.xm-table-component {
}
\ No newline at end of file
/* /*
* @Author: wufan * @Author: wufan
* @Date: 2020-11-26 14:48:57 * @Date: 2020-11-26 14:48:57
* @Last Modified by: mikey.zhaopeng * @Last Modified by: chenshu
* @Last Modified time: 2020-11-26 14:52:02 * @Last Modified time: 2021-06-03 15:21:09
*/ */
import SearchBar from './SearchBar.tsx'; import SearchBar from './SearchBar.tsx';
...@@ -10,6 +10,7 @@ import PageControl from './PageControl.tsx'; ...@@ -10,6 +10,7 @@ import PageControl from './PageControl.tsx';
import CheckBox from './CheckBox.tsx'; import CheckBox from './CheckBox.tsx';
import CropperModal from './CropperModal.tsx'; import CropperModal from './CropperModal.tsx';
import ImgCutModalNew from './ImgCutModalNew'; import ImgCutModalNew from './ImgCutModalNew';
import XMTable from './XMTable';
export { export {
...@@ -17,5 +18,6 @@ export { ...@@ -17,5 +18,6 @@ export {
PageControl, PageControl,
CheckBox, CheckBox,
CropperModal, CropperModal,
ImgCutModalNew ImgCutModalNew,
XMTable,
} }
\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: Michael * @Author: Michael
* @Date: 2017-09-08 17:38:18 * @Date: 2017-09-08 17:38:18
* @Last Modified by: chenshu * @Last Modified by: chenshu
* @Last Modified time: 2020-08-31 14:55:30 * @Last Modified time: 2021-06-04 18:19:23
*/ */
@import './variables.less'; @import './variables.less';
...@@ -602,7 +602,7 @@ td.ant-table-column-sort { ...@@ -602,7 +602,7 @@ td.ant-table-column-sort {
background: none; background: none;
} }
.ant-modal-content .ant-table-thead > tr > th { .ant-modal-content .ant-table-thead > tr > th {
padding: 9px 24px; padding: 9px 24px !important;
} }
.ant-modal-content tr > td { .ant-modal-content tr > td {
...@@ -766,11 +766,12 @@ td.ant-table-column-sort { ...@@ -766,11 +766,12 @@ td.ant-table-column-sort {
} }
} }
.ant-table-column-title {
flex: initial !important;
}
// 排序小三角 // 排序小三角
.ant-table-column-sorter { .ant-table-column-sorter {
margin-left: 2px !important; margin-left: 8px !important;
}
.ant-table-column-sorter-full {
margin-top: -0.32rem !important;
} }
/* /*
* @Author: zhujian * @Author: zhujian
* @Date: 2018-10-10 20:49:11 * @Date: 2018-10-10 20:49:11
* @Last Modified by: zhujiapeng * @Last Modified by: chenshu
* @Last Modified time: 2020-11-16 17:02:11 * @Last Modified time: 2021-06-03 16:42:22
*/ */
// import './s.less' // import './s.less'
...@@ -42,6 +42,7 @@ class DefaultIcon extends React.Component { ...@@ -42,6 +42,7 @@ class DefaultIcon extends React.Component {
preserveAspectRatio: 'xMidYMid slice' preserveAspectRatio: 'xMidYMid slice'
} }
} }
console.log(defaultOptions, this.props, 777777)
return ( return (
<div style={this.props.style} className="DefaultIcon" key="icon"> <div style={this.props.style} className="DefaultIcon" key="icon">
<Lottie <Lottie
......
...@@ -12,6 +12,7 @@ import { Input, Button, Tree } from "antd"; ...@@ -12,6 +12,7 @@ import { Input, Button, Tree } from "antd";
import "./Classification.less"; import "./Classification.less";
import User from "@/common/js/user"; import User from "@/common/js/user";
import KnowledgeAPI from "@/data-source/knowledge/request-api"; import KnowledgeAPI from "@/data-source/knowledge/request-api";
import Bus from "@/core/bus";
const { Search } = Input; const { Search } = Input;
const { DirectoryTree } = Tree; const { DirectoryTree } = Tree;
...@@ -31,6 +32,11 @@ class Classification extends Component { ...@@ -31,6 +32,11 @@ class Classification extends Component {
componentDidMount() { componentDidMount() {
this.queryCategoryTree(); this.queryCategoryTree();
Bus.bind('knowledgeCategoryTree', this.queryCategoryTree)
}
componentWillUnmount() {
Bus.unbind('knowledgeCategoryTree', this.queryCategoryTree)
} }
shouldComponentUpdate = (nextProps, nextState) => { shouldComponentUpdate = (nextProps, nextState) => {
......
...@@ -7,17 +7,19 @@ ...@@ -7,17 +7,19 @@
* @Copyright: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyright: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React from "react" import React from "react";
import { Table, Modal, message, Tooltip, Switch, Dropdown, Button } from "antd" import { Modal, message, Tooltip, Switch, Dropdown, Button } from "antd";
import { Route, withRouter } from "react-router-dom" import { Route, withRouter } from "react-router-dom";
import { PageControl } from "@/components" import Lottie from 'react-lottie';
import { LIVE_SHARE_MAP } from "@/common/constants/academic/cloudClass" import { PageControl, XMTable } from "@/components";
import { appId, shareUrl, LIVE_SHARE } from "@/domains/course-domain/constants" import { LIVE_SHARE_MAP } from "@/common/constants/academic/cloudClass";
import ScanFileModal from "../../resource-disk/modal/ScanFileModal" import { appId, shareUrl, LIVE_SHARE } from "@/domains/course-domain/constants";
import WatchData from "./WatchData" import ScanFileModal from "../../resource-disk/modal/ScanFileModal";
import KnowledgeAPI from "@/data-source/knowledge/request-api" import WatchData from "./WatchData";
import ENUM from "../ENUM.js" import KnowledgeAPI from "@/data-source/knowledge/request-api";
import "./KnowledgeBaseList.less" import ENUM from "../ENUM.js";
import * as nodata from '../../lottie/nodata/data.json';
import "./KnowledgeBaseList.less";
const DEFAULT_SIZE_UNIT = 1000 * 1000 // 将B转换成M const DEFAULT_SIZE_UNIT = 1000 * 1000 // 将B转换成M
const { confirm } = Modal const { confirm } = Modal
...@@ -127,8 +129,7 @@ class KnowledgeBaseList extends React.Component { ...@@ -127,8 +129,7 @@ class KnowledgeBaseList extends React.Component {
// 预览文件 // 预览文件
handleScanFile = (folder) => { handleScanFile = (folder) => {
console.log(folder) const { folderFormat, folderSize, ossUrl } = folder;
const { folderFormat, folderSize, ossUrl } = folder
switch (folderFormat) { switch (folderFormat) {
case "PDF": case "PDF":
window.open(ossUrl, "_blank") window.open(ossUrl, "_blank")
...@@ -403,7 +404,6 @@ class KnowledgeBaseList extends React.Component { ...@@ -403,7 +404,6 @@ class KnowledgeBaseList extends React.Component {
width: 160, width: 160,
fixed: "right", fixed: "right",
render: (val, record, index) => { render: (val, record, index) => {
console.log(this.props.categoryId)
return this.props.categoryId === "0" ? ( return this.props.categoryId === "0" ? (
<div className='operate'> <div className='operate'>
<div className='operate__item' onClick={() => this.handleDelete(record)}> <div className='operate__item' onClick={() => this.handleDelete(record)}>
...@@ -451,9 +451,8 @@ class KnowledgeBaseList extends React.Component { ...@@ -451,9 +451,8 @@ class KnowledgeBaseList extends React.Component {
// 前往上课数据页面 // 前往上课数据页面
handleLinkToClassData = (item) => { handleLinkToClassData = (item) => {
const { match } = this.props const { match } = this.props;
console.log(item) localStorage.setItem("WatchData_CourseName", item.name);
localStorage.setItem("WatchData_CourseName", item.name)
window.RCHistory.push({ window.RCHistory.push({
// pathname: `${match.url}/course-data?type=${item.courseType}&id=${item.liveCourseId}`, // pathname: `${match.url}/course-data?type=${item.courseType}&id=${item.liveCourseId}`,
pathname: `${match.url}/course-data?type=${item.type}&id=${item.id}` pathname: `${match.url}/course-data?type=${item.type}&id=${item.id}`
...@@ -467,9 +466,13 @@ class KnowledgeBaseList extends React.Component { ...@@ -467,9 +466,13 @@ class KnowledgeBaseList extends React.Component {
} }
KnowledgeAPI.delKnowledge(params).then((res) => { KnowledgeAPI.delKnowledge(params).then((res) => {
if (res.success) { if (res.success) {
message.success("移出成功") const { onChange, updateCategoryTree, selectedRowKeys, onSelectChange } = this.props;
this.props.onChange() message.success("移出成功");
this.props.updateCategoryTree() onChange();
updateCategoryTree();
if (selectedRowKeys.includes(item.id)) {
onSelectChange(_.reject(selectedRowKeys, value => value === item.id));
}
} }
}) })
} }
...@@ -484,19 +487,45 @@ class KnowledgeBaseList extends React.Component { ...@@ -484,19 +487,45 @@ class KnowledgeBaseList extends React.Component {
} }
render() { render() {
const { dataSource = [], totalCount, query, match } = this.props const { dataSource = [], totalCount, query, match, selectedRowKeys, onSelectChange } = this.props;
const { current, size } = query const { current, size } = query;
const rowSelection = {
selectedRowKeys,
preserveSelectedRowKeys: true,
onChange: onSelectChange,
}
const defaultOptions = {
loop: true,
autoplay: true,
animationData: nodata,
rendererSettings: {
preserveAspectRatio: 'xMidYMid slice'
}
}
return ( return (
<div className='knowledge-base-list'> <div className="knowledge-base-list">
<Table <XMTable
rowKey={(record) => record.id} rowKey={(record) => record.id}
size='middle' rowSelection={rowSelection}
size="middle"
dataSource={dataSource} dataSource={dataSource}
columns={this.parseColumns()} columns={this.parseColumns()}
pagination={false} pagination={false}
scroll={{ x: 900 }} scroll={{ x: 900 }}
bordered bordered
className='knowledge-list-table' className="knowledge-list-table"
renderEmpty={{
image: <div style={{ marginTop: 24 }}>
<Lottie
options={defaultOptions}
height={150}
width={150}
isStopped={false}
isPaused={false}
/>
</div>,
description: <span style={{ display: 'block', paddingBottom: 24 }}>暂无数据</span>
}}
/> />
<div className='box-footer'> <div className='box-footer'>
......
...@@ -9,19 +9,27 @@ ...@@ -9,19 +9,27 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { Button, Menu, Dropdown,message } from "antd"; import { Button, Menu, Dropdown,message, Modal } from "antd";
import SelectPrepareFileModal from "@/modules/prepare-lesson/modal/SelectPrepareFileModal"; import SelectPrepareFileModal from "@/modules/prepare-lesson/modal/SelectPrepareFileModal";
import { DownOutlined } from "@ant-design/icons"; import { DownOutlined } from "@ant-design/icons";
import AddCourse from "../modal/AddCourse"; import AddCourse from "../modal/AddCourse";
import User from "@/common/js/user"; import User from "@/common/js/user";
import Service from "@/common/js/service";
import KnowledgeAPI from "@/data-source/knowledge/request-api"; import KnowledgeAPI from "@/data-source/knowledge/request-api";
import MoveModal from '../../teach-tool/modal/MoveModal';
import Bus from "@/core/bus";
import './KnowledgeBaseOpt.less';
export default function KnowledgeBaseOpt({ export default function KnowledgeBaseOpt({
categoryId, categoryId,
selectedRowKeys,
updateCategoryTree, updateCategoryTree,
onChange, onChange,
onChangeKeys,
}) { }) {
const [modal, setModal] = useState(null); const [modal, setModal] = useState(null);
const [openMoveModal, setOpenMoveModal] = useState(false);
const [data, setData] = useState([]);
const menu = ( const menu = (
<Menu> <Menu>
...@@ -34,6 +42,40 @@ export default function KnowledgeBaseOpt({ ...@@ -34,6 +42,40 @@ export default function KnowledgeBaseOpt({
</Menu> </Menu>
); );
useEffect(() => {
queryCategoryTree();
}, []);
useEffect(() => {
clearSelect();
}, [categoryId])
// 查询分类树
function queryCategoryTree() {
let query = {
storeId: User.getStoreId(),
withCount: true,
};
KnowledgeAPI.getCategoryTree(query).then((res) => {
const { categoryList = [] } = res.result;
let list = renderTreeNodes(categoryList);
setData(list);
});
}
function renderTreeNodes(data) {
let newTreeData = data.map((item) => {
item.title = item.categoryName;
item.value = item.id;
item.key = item.id;
if (item.sonCategoryList) {
item.children = renderTreeNodes(item.sonCategoryList);
}
return item;
});
return newTreeData;
};
function handAddCourse() { function handAddCourse() {
let modal = ( let modal = (
<AddCourse <AddCourse
...@@ -86,16 +128,127 @@ export default function KnowledgeBaseOpt({ ...@@ -86,16 +128,127 @@ export default function KnowledgeBaseOpt({
setModal(modal); setModal(modal);
} }
return ( function batchMove() {
<div className="video-course-opt"> if (_.isEmpty(selectedRowKeys)) {
<Dropdown overlay={menu}> message.warning('请先选择要移动的知识');
<Button type="primary" className="mr12"> return null;
添加知识 }
<DownOutlined /> setOpenMoveModal(true);
</Button> }
</Dropdown>
function batchMoveRemote(categoryId) {
const data = {
categoryId,
ids: selectedRowKeys,
storeId: User.getStoreId(),
};
Service.Hades('public/hades/batchMoveKnowledgeCategory', data, { reject: true }).then((res) => {
if (res.success) {
message.success('移动成功');
onChange();
Bus.trigger('knowledgeCategoryTree');
clearSelect();
} else {
message.error('移动失败');
}
}).catch(() => {
message.error('移动失败');
})
}
function batchDelete() {
if (_.isEmpty(selectedRowKeys)) {
message.warning('请先选择要删除的知识');
return null;
}
Modal.confirm({
title: "确定要删除知识吗?",
content: "删除后,不可恢复。",
icon: (
<span className="icon iconfont default-confirm-icon">&#xe839; </span>
),
okText: "删除",
cancelText: "取消",
onOk: () => {
const data = {
ids: selectedRowKeys,
storeId: User.getStoreId(),
};
Service.Hades('public/hades/batchDelKnowledge', data, { reject: true }).then((res) => {
if (res.success) {
message.success('删除成功');
onChange();
Bus.trigger('knowledgeCategoryTree');
clearSelect();
} else {
message.error('删除失败');
}
}).catch(() => {
message.error('删除失败');
})
},
})
}
function onSelectPaper(keys) {
if (keys.length > 50) {
message.warning('最多只能选择50个知识');
return null;
}
onChangeKeys(keys);
};
function clearSelect() {
onChangeKeys([]);
}
return (
<div className="knowledge-course-opt">
{_.isEmpty(selectedRowKeys) ?
(categoryId !== '0' && <Dropdown overlay={menu}>
<Button type="primary" className="mr8">
添加知识
<DownOutlined />
</Button>
</Dropdown>)
: <div className="select-container">
<span className="con">
<div>
<span className="icon iconfont tip">&#xe6f2;</span>
<span className="text">已选择{selectedRowKeys.length}</span>
</div>
<div>
<span className="clear" onClick={clearSelect}>清空</span>
</div>
</span>
</div>
}
<Button
className="mr8"
onClick={() => {
batchMove();
}}
>批量移动</Button>
<Button
onClick={() => {
batchDelete();
}}
>批量删除</Button>
{modal} {modal}
{openMoveModal &&
<MoveModal
visible={openMoveModal}
title="知识"
data={data}
categoryId={categoryId}
length={selectedRowKeys.length}
onCancel={() => setOpenMoveModal(false)}
onOk={(categoryId) => {
batchMoveRemote(categoryId);
setOpenMoveModal(false);
}}
/>
}
</div> </div>
); );
} }
.knowledge-course-opt {
display: flex;
.select-container{
margin-right: 24px;
.con{
background: #E9EFFF;
border-radius: 4px;
padding: 3px 16px;
display: inline-flex;
align-items: center;
justify-content: space-between;
.tip{
font-size:14px;
color:#2966FF;
margin-right:8px;
}
.text{
font-size:14px;
color:#666;
margin-right:30px;
}
.clear{
color:#5289FA;
font-size:14px;
}
}
}
}
\ No newline at end of file
...@@ -6,12 +6,12 @@ ...@@ -6,12 +6,12 @@
* @LastEditTime: 2021-05-30 20:36:42 * @LastEditTime: 2021-05-30 20:36:42
* @Copyright: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyright: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import React from "react" import React from "react";
import { message } from 'antd'
import KnowledgeBaseFilter from "./components/KnowledgeBaseFilter" import KnowledgeBaseFilter from "./components/KnowledgeBaseFilter";
import KnowledgeBaseOpt from "./components/KnowledgeBaseOpt" import KnowledgeBaseOpt from "./components/KnowledgeBaseOpt";
import KnowledgeBaseList from "./components/KnowledgeBaseList" import KnowledgeBaseList from "./components/KnowledgeBaseList";
import Classification from "./components/Classification" import Classification from "./components/Classification";
import KnowledgeAPI from "@/data-source/knowledge/request-api" import KnowledgeAPI from "@/data-source/knowledge/request-api"
import User from "@/common/js/user" import User from "@/common/js/user"
...@@ -28,9 +28,10 @@ export default class KnowledgeBase extends React.Component { ...@@ -28,9 +28,10 @@ export default class KnowledgeBase extends React.Component {
}, },
dataSource: [], // 知识库列表 dataSource: [], // 知识库列表
totalCount: 0, // 知识库数据总条数 totalCount: 0, // 知识库数据总条数
categoryId: "0", categoryId: '0',
updateCategoryFlag: false updateCategoryFlag: false,
} selectedRowKeys: [],
};
} }
componentWillMount() { componentWillMount() {
...@@ -73,8 +74,23 @@ export default class KnowledgeBase extends React.Component { ...@@ -73,8 +74,23 @@ export default class KnowledgeBase extends React.Component {
}) })
} }
onSelectChange = (selectedRowKeys) => {
if (selectedRowKeys.length > 50) {
message.warning('最多只能选择50个题目');
}
const list = _.filter(selectedRowKeys, (item, index) => index < 50);
this.setState({ selectedRowKeys: list });
};
render() { render() {
const { dataSource, totalCount, query, categoryId, updateCategoryFlag } = this.state const {
dataSource,
totalCount,
query,
categoryId,
updateCategoryFlag,
selectedRowKeys,
} = this.state;
return ( return (
<div className='page'> <div className='page'>
<div className='content-header'>知识库</div> <div className='content-header'>知识库</div>
...@@ -92,13 +108,19 @@ export default class KnowledgeBase extends React.Component { ...@@ -92,13 +108,19 @@ export default class KnowledgeBase extends React.Component {
<KnowledgeBaseFilter onChange={this.handleFetchScheduleList} /> <KnowledgeBaseFilter onChange={this.handleFetchScheduleList} />
{/* 操作模块 */} {/* 操作模块 */}
{categoryId !== "0" && ( <KnowledgeBaseOpt
<KnowledgeBaseOpt onChange={this.handleFetchScheduleList} updateCategoryTree={this.updateCategoryTree} categoryId={categoryId} /> onChange={this.handleFetchScheduleList}
)} updateCategoryTree={this.updateCategoryTree}
categoryId={categoryId}
selectedRowKeys={selectedRowKeys}
onChangeKeys={(keys) => this.setState({ selectedRowKeys: keys })}
/>
{/* 知识库列表模块 */} {/* 知识库列表模块 */}
<KnowledgeBaseList <KnowledgeBaseList
query={query} query={query}
selectedRowKeys={selectedRowKeys}
onSelectChange={this.onSelectChange}
categoryId={categoryId} categoryId={categoryId}
dataSource={dataSource} dataSource={dataSource}
totalCount={totalCount} totalCount={totalCount}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
/* /*
* @Author: yuananting * @Author: yuananting
* @Date: 2021-02-23 18:28:50 * @Date: 2021-02-23 18:28:50
* @LastEditors: fusanqiasng * @LastEditors: yuananting
* @LastEditTime: 2021-05-21 17:57:59 * @LastEditTime: 2021-06-02 14:25:06
* @Description: 助学工具-课程分类 * @Description: 助学工具-课程分类
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -153,9 +153,7 @@ class CourseCategoryManage extends Component { ...@@ -153,9 +153,7 @@ class CourseCategoryManage extends Component {
<Space className='title-opts' size={16}> <Space className='title-opts' size={16}>
<span <span
onClick={() => { onClick={() => {
let nodesCount = 0 const { originTreeData } = this.state;
const { originTreeData } = this.state
console.log('orororo', originTreeData)
if ( if (
(item.categoryLevel === 0 && originTreeData.length >= 29) || (item.categoryLevel === 0 && originTreeData.length >= 29) ||
(item.categoryLevel > 0 && this.getRelatedNodes(item.parentId).length >= 30) (item.categoryLevel > 0 && this.getRelatedNodes(item.parentId).length >= 30)
...@@ -343,8 +341,10 @@ class CourseCategoryManage extends Component { ...@@ -343,8 +341,10 @@ class CourseCategoryManage extends Component {
let dragNodes = [] let dragNodes = []
dragNodes.push(dragNode.id) dragNodes.push(dragNode.id)
if (dragNode.parentId != 0) { if (dragNode.parentId !== "0") {
dragNodes = dragNodes.concat(this.getParentDragNodesLevel(this.state.treeMap[dragNode.parentId])) dragNodes = dragNodes.concat(
this.getParentDragNodesLevel(this.state.treeMap[dragNode.parentId])
);
} }
return dragNodes return dragNodes
} }
...@@ -365,14 +365,24 @@ class CourseCategoryManage extends Component { ...@@ -365,14 +365,24 @@ class CourseCategoryManage extends Component {
// 拖拽 // 拖拽
onDrop = (info) => { onDrop = (info) => {
if (this.state.categoryName) { // 带搜索时的分类树不允许拖拽
return // 不允许其他节点拖拽到未分类中
// 不允许其他节点拖拽到未分类之前
if (
this.state.categoryName ||
(info.node.categoryName === "未分类" && info.dropPosition === 0) ||
(info.node.categoryName === "未分类" &&
info.dropToGap &&
info.dropPosition === -1)
) {
return;
} }
// 未分类不可以拖拽 // 未分类不可以拖拽
if (info.dragNode.categoryName === '未分类' && info.dragNode.categoryLevel === 0) return message.info('未分类”为默认分类暂不支持移动') if (
info.dragNode.categoryName === "未分类" &&
// 不允许其他节点拖拽到未分类之前 info.dragNode.categoryLevel === 0
if (info.node.categoryName === '未分类' && info.dropToGap && info.dropPosition === -1) return )
return message.info("“未分类”为默认分类暂不支持移动");
let targetParentId = info.dropToGap ? info.node.parentId : info.node.id let targetParentId = info.dropToGap ? info.node.parentId : info.node.id
let relatedNodes = this.getRelatedNodes(targetParentId) let relatedNodes = this.getRelatedNodes(targetParentId)
...@@ -383,8 +393,7 @@ class CourseCategoryManage extends Component { ...@@ -383,8 +393,7 @@ class CourseCategoryManage extends Component {
let nodesArr = this.getDragNodesLevel(this.state.treeMap[info.dragNode.id]) let nodesArr = this.getDragNodesLevel(this.state.treeMap[info.dragNode.id])
let parentArr = this.getParentDragNodesLevel(this.state.treeMap[targetParentId]) let parentArr = this.getParentDragNodesLevel(this.state.treeMap[targetParentId])
if (nodesArr.length + parentArr.length > 4) { if (nodesArr.length + parentArr.length > 4) {
console.log(nodesArr.length, parentArr.length) return message.info("最多支持5级分类");
return message.info('最多支持5级分类')
} }
} }
if (relatedNodes && relatedNodes.length >= 30) { if (relatedNodes && relatedNodes.length >= 30) {
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
white-space: nowrap; white-space: nowrap;
} }
.ant-tree-node-content-wrapper.ant-tree-node-selected { .ant-tree-node-content-wrapper.ant-tree-node-selected {
color: #666666; color: #2966FF;
} }
} }
.ant-tree-treenode-selected:hover::before, .ant-tree-treenode-selected:hover::before,
......
.examPage{ .examPage{
padding-bottom: 50px; padding-bottom: 50px;
.box { .box {
padding-bottom: 40px!important; padding-bottom: 66px!important;
} }
.ant-form-item{ .ant-form-item{
margin-bottom: 24px !important;
&:last-child{ &:last-child{
margin-bottom: 0px !important; margin-bottom: 0px !important;
} }
} }
.form{ .form{
margin-top: 12px; margin-top: 24px;
margin-bottom: 32px;
width: 1000px; width: 1000px;
.title{ .title{
position: relative;
padding-left: 28px;
font-size: 16px; font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC; font-family: PingFangSC-Medium, PingFang SC;
font-weight: bold; font-weight: bold;
color: #333333; color: #333333;
line-height: 22px; line-height: 22px;
margin-bottom: 8px; margin-bottom: 24px;
&.first {
&::before{
width:4px;
height:12px;
content:'';
background-image: linear-gradient(#2966FF 83.5%, #0ACCA4 16.5%);
display:inline-block;
position: absolute;
left:16px;
top:6px;
}
}
} }
} }
......
import React, { useState, useRef, useEffect, useContext } from 'react' import React, { useState, useRef, useEffect, useContext } from 'react'
import Breadcrumbs from "@/components/Breadcrumbs"; import Breadcrumbs from "@/components/Breadcrumbs";
import { Form, Alert, Input, Button, InputNumber, DatePicker, Switch, Radio, message, Modal } from 'antd'; import { Form, Alert, Input, Button, InputNumber, DatePicker, Switch, Radio, message, Modal, Tooltip } from 'antd';
import { Route, withRouter } from 'react-router-dom'; import { Route, withRouter } from 'react-router-dom';
import User from "@/common/js/user"; import User from "@/common/js/user";
import moment from 'moment' import moment from 'moment'
...@@ -36,20 +36,27 @@ function AddExam(props: any) { ...@@ -36,20 +36,27 @@ function AddExam(props: any) {
const [getData, setGetData] = useState(false); const [getData, setGetData] = useState(false);
const [preview, setPreview] = useState(false); const [preview, setPreview] = useState(false);
const [examTotal, setExamTotal] = useState(0); const [examTotal, setExamTotal] = useState(0);
const timer = useRef({});
const request = useRef(false); const request = useRef(false);
const { match } = props; const { match } = props;
const [examDuration, setExamDuration] = useState(undefined); const [examDuration, setExamDuration] = useState(undefined);
useEffect(() => { useEffect(() => {
if (props.type === 'edit') { switch (props.type) {
queryExamDetail() case "copy": // 考试列表-复制考试进入
case "edit": // 考试列表-编辑考试进入
queryExamDetail();
break;
case "organizeExam": // 试卷列表-组织考试进入
case "newPaperToAddExam": // 组卷页面-新建保存试卷并组织考试
case "editPaperToAddExam": // 组卷页面-编辑保存试卷并组织考试
setGetData(true);
setPaperInfo(props.paperInfo);
break;
} }
}, []) }, [])
useEffect(() => { useEffect(() => {
console.log(paperInfo)
setPaperId(paperInfo.paperId) setPaperId(paperInfo.paperId)
setPassRate(paperInfo.passRate) setPassRate(paperInfo.passRate)
...@@ -75,9 +82,9 @@ function AddExam(props: any) { ...@@ -75,9 +82,9 @@ function AddExam(props: any) {
const { result } = res const { result } = res
setPaperInfo(result.examPaper) setPaperInfo(result.examPaper)
setPaperId(result.examPaper.paperId) setPaperId(result.examPaper.paperId)
setStartTime(result.examStartTime) setStartTime(props.type === 'edit' ? result.examStartTime : '')
setExamEndTime(result.examEndTime) setExamEndTime(props.type === 'edit' ? result.examEndTime : '')
setExamName(result.examName) setExamName(props.type === 'edit' ? result.examName : `${result.examName}(复制)`)
setPassRate(result.passRate * 100) setPassRate(result.passRate * 100)
setNeedPhone(result.needPhone) setNeedPhone(result.needPhone)
setExamDesc(result.examDesc) setExamDesc(result.examDesc)
...@@ -125,6 +132,11 @@ function AddExam(props: any) { ...@@ -125,6 +132,11 @@ function AddExam(props: any) {
return return
} }
if (param.examName && param.examName.length > 40) {
message.warning('考试名称最多40字');
return
}
if (!paperId) { if (!paperId) {
message.warning('请选择试卷'); message.warning('请选择试卷');
return return
...@@ -174,8 +186,19 @@ function AddExam(props: any) { ...@@ -174,8 +186,19 @@ function AddExam(props: any) {
Service.Hades(props.type === 'edit' ? 'public/hades/editExam' : "public/hades/createExam", param).then((res) => { Service.Hades(props.type === 'edit' ? 'public/hades/editExam' : "public/hades/createExam", param).then((res) => {
message.success(props.type === 'edit' ? '编辑成功' : '创建成功'); message.success(props.type === 'edit' ? '编辑成功' : '创建成功');
props.freshList() switch (props.type) {
props.history.goBack(); case "organizeExam": // 试卷列表-组织考试进入
case "newPaperToAddExam": // 组卷保存组织考试
case "editPaperToAddExam":
window.RCHistory.push("/examination-manage-index")
break;
case "add":
case "edit": // 考试列表-新建或编辑
case "copy": // 考试列表-新建或编辑
props.freshList()
props.history.goBack();
break;
}
}) })
} }
...@@ -222,14 +245,30 @@ function AddExam(props: any) { ...@@ -222,14 +245,30 @@ function AddExam(props: any) {
cancelText: '留在本页', cancelText: '留在本页',
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>, icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>,
onOk: () => { onOk: () => {
props.history.goBack(); window.RCHistory.push("/examination-manage-index")
} }
}) })
} }
let title = '';
switch (props.type) {
case 'add':
case "organizeExam":
case "newPaperToAddExam":
case "editPaperToAddExam":
title = '新建考试';
break;
case 'edit':
title = '编辑考试';
break;
case 'copy':
title = '复制考试';
break;
default:
break;
}
return <div className="page examPage"> return <div className="page examPage">
<Breadcrumbs navList={props.type === 'edit' ? "编辑考试" : "新建考试"} goBack={handleGoBack} /> <Breadcrumbs navList={title} goBack={handleGoBack} />
<div className="box"> <div className="box">
<div className="show-tips"> <div className="show-tips">
...@@ -242,8 +281,8 @@ function AddExam(props: any) { ...@@ -242,8 +281,8 @@ function AddExam(props: any) {
layout="horizontal" layout="horizontal"
> >
<Form.Item label="考试名称" <Form.Item label="考试名称"
validateStatus={(check && !examName) ? 'error' : ''} validateStatus={(check && (!examName ? '请输入考试名称' : (examName.length > 40) && '考试名称最多40字')) ? 'error' : ''}
help={check && !examName && '请选择课程'} help={check && (!examName ? '请输入考试名称' : (examName.length > 40) && '考试名称最多40字')}
required> required>
<Input placeholder='请输入考试名称(40字以内)' maxLength={40} value={examName} onChange={(e) => { <Input placeholder='请输入考试名称(40字以内)' maxLength={40} value={examName} onChange={(e) => {
...@@ -296,11 +335,18 @@ function AddExam(props: any) { ...@@ -296,11 +335,18 @@ function AddExam(props: any) {
<Form.Item label="及格线" <Form.Item
label={<div>
<span>及格线</span>
<Tooltip title="默认为选中试卷所设置的及格线,可修改">
<span className="icon iconfont" style={{ color: '#BFBFBF', marginLeft: 4 }}>&#xe61d;</span>
</Tooltip>
</div>}
style={{ marginTop: 24 }} style={{ marginTop: 24 }}
validateStatus={(check && !passRate) ? 'error' : ''} validateStatus={(check && !passRate) ? 'error' : ''}
help={check && !passRate && '请输入及格线'} help={check && !passRate && '请输入及格线'}
required> required
>
<InputNumber value={passRate} min={0} max={100} onChange={(value: any) => { setPassRate(parseInt(value)) }} style={{ width: 100 }} /> <InputNumber value={passRate} min={0} max={100} onChange={(value: any) => { setPassRate(parseInt(value)) }} style={{ width: 100 }} />
<span style={{ marginLeft: 4 }}>% <span style={{ marginLeft: 4 }}>%
</span> </span>
...@@ -314,10 +360,11 @@ function AddExam(props: any) { ...@@ -314,10 +360,11 @@ function AddExam(props: any) {
required> required>
<RangePicker <RangePicker
style={{ width: 320 }} style={{ width: 320 }}
showTime={{ defaultValue: [moment().add(5, 'minutes'), moment().add(5, 'minutes')] }}
ranges={{ ranges={{
'近七天': [moment(), moment().add(6, 'day').endOf('day')], '近七天': [moment().add(5, 'minute'), moment().add(6, 'day').endOf('day')],
'近1个月': [moment(), moment().add(1, 'month').endOf('day')], '近1个月': [moment().add(5, 'minute'), moment().add(1, 'month').endOf('day')],
'近3个月': [moment(), moment().add(3, 'month').endOf('day')], '近3个月': [moment().add(5, 'minute'), moment().add(3, 'month').endOf('day')],
}} }}
disabledDate={disabledDate} disabledDate={disabledDate}
value={[ value={[
...@@ -325,7 +372,6 @@ function AddExam(props: any) { ...@@ -325,7 +372,6 @@ function AddExam(props: any) {
examEndTime ? moment(Number(examEndTime)) : null examEndTime ? moment(Number(examEndTime)) : null
]} ]}
disabledTime={disabledRangeTime} disabledTime={disabledRangeTime}
showTime
format="YYYY/MM/DD HH:mm" format="YYYY/MM/DD HH:mm"
onChange={(date: any) => { onChange={(date: any) => {
setStartTime(date && date[0]?.valueOf()); setStartTime(date && date[0]?.valueOf());
...@@ -350,7 +396,7 @@ function AddExam(props: any) { ...@@ -350,7 +396,7 @@ function AddExam(props: any) {
help={check && (desclen > 1000) && '最多只能输入1000个字'} help={check && (desclen > 1000) && '最多只能输入1000个字'}
> >
{ {
(getData || (props.type !== 'edit')) && <GraphicsEditor (getData || (props.type === 'add')) && <GraphicsEditor
maxLimit={1000} maxLimit={1000}
isIntro={true} isIntro={true}
detail={{ detail={{
......
...@@ -39,11 +39,10 @@ function DataAnalysic(props: any) { ...@@ -39,11 +39,10 @@ function DataAnalysic(props: any) {
<Breadcrumbs navList={"考试数据"} goBack={props.history.goBack} /> <Breadcrumbs navList={"考试数据"} goBack={props.history.goBack} />
<div className="box"> <div className="box">
<div className="titleBox "> <div className="titleBox ">
<span className='tips'></span>
考试名称:{examDetail.examName} 考试名称:{examDetail.examName}
</div> </div>
</div> </div>
<div className="box"> <div className="box" style={{ paddingTop: 0 }}>
<Tabs activeKey={selectKey} onChange={(key: any) => { <Tabs activeKey={selectKey} onChange={(key: any) => {
setSelectKey(key) setSelectKey(key)
}}> }}>
......
...@@ -207,7 +207,6 @@ function ExamData(props: any) { ...@@ -207,7 +207,6 @@ function ExamData(props: any) {
return <div className="rr"> return <div className="rr">
<a <a
target="_blank"
download download
id="load-play-back-excel" id="load-play-back-excel"
style={{ position: "absolute", left: "-10000px" }} style={{ position: "absolute", left: "-10000px" }}
......
import React, { useState, useRef, useEffect, useContext } from 'react' import React, { useState, useRef, useEffect, useContext } from 'react'
import { Input, Select, DatePicker, Tooltip, Button, Table, Dropdown, Menu, Modal } from 'antd'; import { Input, Select, DatePicker, Tooltip, Button, Table, Dropdown, Menu, Modal } from 'antd';
import Lottie from 'react-lottie';
import TeacherSelect from '@/modules/common/TeacherSelect'; import TeacherSelect from '@/modules/common/TeacherSelect';
import { Route, withRouter } from 'react-router-dom'; import { Route, withRouter } from 'react-router-dom';
import Service from "@/common/js/service"; import Service from "@/common/js/service";
import moment from 'moment'; import moment from 'moment';
import { PageControl } from "@/components"; import { PageControl, XMTable } from "@/components";
import AddExam from './AddExam'; import AddExam from './AddExam';
import User from "@/common/js/user"; import User from "@/common/js/user";
import { XMContext } from "@/store/context"; import { XMContext } from "@/store/context";
import ExamShareModal from './ExamShareModal' import ExamShareModal from './ExamShareModal'
import DataAnalysic from './DataAnalysic' import DataAnalysic from './DataAnalysic'
import PreviewModal from './PreviewModal'
import * as nodata from '../../lottie/nodata/data.json';
import './index.less' import './index.less'
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
const { Search } = Input; const { Search } = Input;
...@@ -53,6 +56,8 @@ function ExaminationManager(props: any) { ...@@ -53,6 +56,8 @@ function ExaminationManager(props: any) {
const [order, setOrder] = useState(sortStatus.type); const [order, setOrder] = useState(sortStatus.type);
const [modal, setModal] = useState(null); const [modal, setModal] = useState(null);
const [questionCntSort, setQuestionCntSort] = useState(sortState) const [questionCntSort, setQuestionCntSort] = useState(sortState)
const [openPreviewModal, setOpenPreviewModal] = useState(false);
const [info, setInfo] = useState({ examDuration: 0 });
const queryRef = useRef({}); const queryRef = useRef({});
const orderEnum = { const orderEnum = {
...@@ -65,8 +70,8 @@ function ExaminationManager(props: any) { ...@@ -65,8 +70,8 @@ function ExaminationManager(props: any) {
descend: 'PASS_CNT_DESC' descend: 'PASS_CNT_DESC'
}, },
examCreateTime: { examCreateTime: {
ascend: 'EXAM_START_TIME_ASC', ascend: 'CREATED_ASC',
descend: 'EXAM_START_TIME_DESC' descend: 'CREATED_DESC'
} }
} }
...@@ -139,6 +144,7 @@ function ExaminationManager(props: any) { ...@@ -139,6 +144,7 @@ function ExaminationManager(props: any) {
title: "操作", title: "操作",
fixed:fixStr.right, fixed:fixStr.right,
dataIndex: "operate", dataIndex: "operate",
width: 150,
render: (text: any, record: any) => <div className="table_operate"> render: (text: any, record: any) => <div className="table_operate">
{ {
ctx.xmState?.userPermission?.SeeExamData() && [<div ctx.xmState?.userPermission?.SeeExamData() && [<div
...@@ -162,13 +168,11 @@ function ExaminationManager(props: any) { ...@@ -162,13 +168,11 @@ function ExaminationManager(props: any) {
onClick={() => { shareModal(record) }} onClick={() => { shareModal(record) }}
> >
分享 分享
</div> </div>
<span className="operate__item split" > | </span>
{ <Dropdown overlay={getOpe(record)}>
((ctx.xmState?.userPermission?.AddExam() && (moment().valueOf() < record.examStartTime)) || (ctx.xmState?.userPermission?.DelExam() && (moment().valueOf() + 30 * 60 * 1000 < record.examStartTime))) && [<span className="operate__item split" > | </span>, <Dropdown overlay={getOpe(record)}> <span className='more'>更多</span>
<span className='more'>更多</span> </Dropdown>
</Dropdown>]
}
...@@ -177,6 +181,18 @@ function ExaminationManager(props: any) { ...@@ -177,6 +181,18 @@ function ExaminationManager(props: any) {
]; ];
function queryExamDetail(examId: string) {
Service.Hades("public/hades/queryExamDetail", {
examId,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0
}).then((res) => {
setInfo(res.result);
setOpenPreviewModal(true);
});
}
function shareModal(record: any) { function shareModal(record: any) {
const modal = <ExamShareModal const modal = <ExamShareModal
...@@ -190,8 +206,14 @@ function ExaminationManager(props: any) { ...@@ -190,8 +206,14 @@ function ExaminationManager(props: any) {
function getOpe(item: any) { function getOpe(item: any) {
return <Menu> return <Menu>
<Menu.Item
key="1"
onClick={() => {
queryExamDetail(item.examId);
}}
>预览</Menu.Item>
{ {
ctx.xmState?.userPermission?.AddExam() && (moment().valueOf() < item.examStartTime) && <Menu.Item key="0"> ctx.xmState?.userPermission?.AddExam() && (moment().valueOf() < item.examStartTime) && <Menu.Item key="2">
<span <span
onClick={() => { onClick={() => {
if (moment().valueOf() + 5 * 60 * 1000 > item.examStartTime) { if (moment().valueOf() + 5 * 60 * 1000 > item.examStartTime) {
...@@ -213,8 +235,11 @@ function ExaminationManager(props: any) { ...@@ -213,8 +235,11 @@ function ExaminationManager(props: any) {
</Menu.Item> </Menu.Item>
} }
{ctx.xmState?.userPermission?.AddExam() &&
<Menu.Item key="3" onClick={() => props.history.push(`${match.url}/copy/${item.examId}`)}>复制</Menu.Item>
}
{ {
ctx.xmState?.userPermission?.DelExam() && (moment().valueOf() + 30 * 60 * 1000 < item.examStartTime) && <Menu.Item key="1"> ctx.xmState?.userPermission?.DelExam() && ((moment().valueOf() + 30 * 60 * 1000 < item.examStartTime) || (moment().valueOf() > item.examEndTime)) && <Menu.Item key="4">
<span <span
onClick={() => { onClick={() => {
deleteExam(item) deleteExam(item)
...@@ -281,12 +306,19 @@ function ExaminationManager(props: any) { ...@@ -281,12 +306,19 @@ function ExaminationManager(props: any) {
function onChange(pagination: any, filters: any, sorter: any, extra: any) { function onChange(pagination: any, filters: any, sorter: any, extra: any) {
setfield(sorter.field); setfield(sorter.field);
setOrder(sorter.order) setOrder(sorter.order)
console.log(sorter.field, sorter.order, (orderEnum as any)[sorter.field])
let _query: any = { ...queryRef.current }; let _query: any = { ...queryRef.current };
_query.order = (orderEnum as any)[sorter.field][sorter.order] || 'EXAM_START_TIME_DESC' _query.order = (orderEnum as any)[sorter.field][sorter.order] || 'EXAM_START_TIME_DESC'
setQuery(_query) setQuery(_query)
} }
const defaultOptions = {
loop: true,
autoplay: true,
animationData: nodata,
rendererSettings: {
preserveAspectRatio: 'xMidYMid slice'
}
}
return <div className="page examination-manager"> return <div className="page examination-manager">
<div className="content-header">考试</div> <div className="content-header">考试</div>
<div className="box content-body"> <div className="box content-body">
...@@ -381,7 +413,7 @@ function ExaminationManager(props: any) { ...@@ -381,7 +413,7 @@ function ExaminationManager(props: any) {
<div className="content"> <div className="content">
<Table <XMTable
bordered bordered
size="small" size="small"
columns={columns} columns={columns}
...@@ -390,8 +422,20 @@ function ExaminationManager(props: any) { ...@@ -390,8 +422,20 @@ function ExaminationManager(props: any) {
onChange={onChange} onChange={onChange}
pagination={false} pagination={false}
style={{ margin: '0px 0 16px' }} style={{ margin: '0px 0 16px' }}
renderEmpty={{
image: <div style={{ marginTop: 24 }}>
<Lottie
options={defaultOptions}
height={150}
width={150}
isStopped={false}
isPaused={false}
/>
</div>,
description: <span style={{ display: 'block', paddingBottom: 24 }}>暂无数据</span>
}}
> >
</Table> </XMTable>
{total > 0 && {total > 0 &&
<PageControl <PageControl
size="small" size="small"
...@@ -400,7 +444,6 @@ function ExaminationManager(props: any) { ...@@ -400,7 +444,6 @@ function ExaminationManager(props: any) {
total={total} total={total}
onShowSizeChange={onShowSizeChange} onShowSizeChange={onShowSizeChange}
toPage={(page: any) => { toPage={(page: any) => {
console.log(page)
let _query: any = { ...queryRef.current }; let _query: any = { ...queryRef.current };
_query.current = page + 1; _query.current = page + 1;
setQuery(_query) setQuery(_query)
...@@ -409,8 +452,14 @@ function ExaminationManager(props: any) { ...@@ -409,8 +452,14 @@ function ExaminationManager(props: any) {
} }
</div> </div>
</div> </div>
{openPreviewModal &&
<PreviewModal
info={{ ...info, examDuration: (info.examDuration || 0) / 60000 }}
onClose={() => { setOpenPreviewModal(false) }}
/>
}
<Route path={`${match.url}/add`} render={() => { <Route path={`${match.url}/add`} render={() => {
return <AddExam freshList={() => { return <AddExam type="add" freshList={() => {
let _query: any = { ...queryRef.current }; let _query: any = { ...queryRef.current };
if (_query.current != 1) { if (_query.current != 1) {
_query.current = 1; _query.current = 1;
...@@ -418,8 +467,6 @@ function ExaminationManager(props: any) { ...@@ -418,8 +467,6 @@ function ExaminationManager(props: any) {
} else { } else {
getList() getList()
} }
}} />; }} />;
}} /> }} />
<Route path={`${match.url}/edit/:id`} render={() => { <Route path={`${match.url}/edit/:id`} render={() => {
...@@ -427,6 +474,11 @@ function ExaminationManager(props: any) { ...@@ -427,6 +474,11 @@ function ExaminationManager(props: any) {
getList() getList()
}} />; }} />;
}} /> }} />
<Route path={`${match.url}/copy/:id`} render={() => {
return <AddExam type='copy' freshList={() => {
getList()
}} />;
}} />
<Route path={`${match.url}/analysic/:id`} render={() => { <Route path={`${match.url}/analysic/:id`} render={() => {
return <DataAnalysic />; return <DataAnalysic />;
......
...@@ -123,6 +123,7 @@ ...@@ -123,6 +123,7 @@
border-radius: 4px; border-radius: 4px;
padding: 14px; padding: 14px;
padding-bottom: 8px; padding-bottom: 8px;
padding-right: 0;
.item{ .item{
margin-bottom: 8px; margin-bottom: 8px;
.name{ .name{
......
...@@ -33,18 +33,18 @@ function PreviewModal(props: any) { ...@@ -33,18 +33,18 @@ function PreviewModal(props: any) {
<div className="phone"> <div className="phone">
<div className="content"> <div className="content">
<div className="topContent"> <div className="topContent">
<div className="title" style={{ fontSize: props.info.examName.length > 24 ? 13 : 22 ,marginTop:props.info.examName.length > 24?44:20}}>{props.info.examName || ' '}</div> <div className="title" style={{ fontSize: props.info.examName.length > 24 ? 13 : 22 ,marginTop: 20 }}>{(props.info.examName.length > 40 ? props.info.examName.substring(0, 40) : props.info.examName) || ' '}</div>
{ {
props.info.examStartTime && <div className="time">{moment(props.info.examStartTime).format("YYYY-MM-DD HH:mm")}~{moment(props.info.examEndTime).format("YYYY-MM-DD HH:mm")}</div> props.info.examStartTime && <div className="time">{moment(props.info.examStartTime).format("YYYY-MM-DD HH:mm")}~{moment(props.info.examEndTime).format("YYYY-MM-DD HH:mm")}</div>
} }
<div className="rule"> <div className="rule">
<div className="item"> <div className="item">
<div className="num">{props.info.totalScore || 0}</div> <div className="num">{props.info.totalScore || (props.info.examPaper || {}).totalScore || 0}</div>
<div className="text">总分 <span className="dw" style={{color:'#999'}}>(分)</span></div> <div className="text">总分 <span className="dw" style={{color:'#999'}}>(分)</span></div>
</div> </div>
<div className="item"> <div className="item">
<div className="num">{props.info.examTotal || 0}</div> <div className="num">{props.info.examTotal || (props.info.examPaper || {}).questionCnt || 0}</div>
<div className="text">总题数<span className="dw" style={{color:'#999'}} >(道)</span></div> <div className="text">总题数<span className="dw" style={{color:'#999'}} >(道)</span></div>
</div> </div>
<div className="item"> <div className="item">
......
...@@ -182,7 +182,6 @@ function DataAnalysic(props: any) { ...@@ -182,7 +182,6 @@ function DataAnalysic(props: any) {
return <div className="rr"> return <div className="rr">
<a <a
target="_blank"
download download
id="load-play-back-excel" id="load-play-back-excel"
style={{ position: "absolute", left: "-10000px" }} style={{ position: "absolute", left: "-10000px" }}
...@@ -217,7 +216,7 @@ function DataAnalysic(props: any) { ...@@ -217,7 +216,7 @@ function DataAnalysic(props: any) {
</div> </div>
</div> </div>
<div className="xm-search-filter" style={{ marginTop: 12 }}> <div className="xm-search-filter" style={{ marginTop: 16 }}>
<div style={{ display: 'flex' }}> <div style={{ display: 'flex' }}>
<div className="search-condition"> <div className="search-condition">
<div className="search-condition__item"> <div className="search-condition__item">
......
.dataAnalysic{ .dataAnalysic{
.titleBox{ .titleBox{
position: relative;
padding-left: 28px;
font-size: 19px; font-size: 19px;
font-family: PingFangSC-Medium, PingFang SC; font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500; font-weight: 500;
color: #333333; color: #333333;
line-height: 26px; line-height: 26px;
background: #FFFFFF; background: #FFFFFF;
// padding: 20px 24px; &::before{
// margin-bottom: 8px; width:4px;
.tips{ height:12px;
width: 4px; content:'';
height: 16px; background-image: linear-gradient(#2966FF 83.5%, #0ACCA4 16.5%);
background: #336DFF; display:inline-block;
display: inline-block; position: absolute;
margin-right: 4px; left:16px;
top:7px;
} }
} }
.ant-tabs-content-holder {
margin-top: 8px;
}
} }
\ No newline at end of file
...@@ -13,4 +13,12 @@ ...@@ -13,4 +13,12 @@
.ant-table-column-sorter { .ant-table-column-sorter {
margin-top: 0px !important; margin-top: 0px !important;
} }
.ant-table tbody tr {
&:nth-child(even) {
background: #fff;
}
&:nth-child(odd) {
background: #fafafa;
}
}
} }
\ No newline at end of file
import React from 'react';
import { Modal, TreeSelect } from 'antd';
import './MoveModal.less';
class MoveModal extends React.Component {
constructor(props) {
super(props);
this.state = {
categoryId: undefined,
};
}
handleChangeCatalogList = (value, label) => {
this.setState({ categoryId: value, categoryName: label[0] });
};
filterData = (data, id) => {
let newTreeData = data.map((item) => {
item.disabled = id === item.id;
if (item.children) {
item.children = this.filterData(item.children, id);
}
return item;
});
return newTreeData;
}
render() {
const { visible, title, onCancel, onOk, data, length, categoryId: id } = this.props;
const { categoryId } = this.state;
const moveData = this.filterData(data, id);
return (
<Modal
title={`移动${title}`}
visible={visible}
onCancel={onCancel}
maskClosable={false}
className="common-move-modal"
onOk={() => onOk(categoryId)}
>
<div className="tip">
<span className="icon iconfont">&#xe6f2;</span>
<span className="text">已选择<span style={{ color: '#2966FF' }}>{length}</span>{title},移动后,原有分类将移除此{title}</span>
</div>
<div className="move-item" id="move-item">
<span className="label">选择分类:</span>
<TreeSelect
showSearch
treeNodeFilterProp="title"
getPopupContainer={() => document.querySelector('#move-item')}
style={{ width: 240 }}
treeData={moveData}
placeholder="请选择分类"
allowClear
value={categoryId}
onChange={(value, label) => {
this.handleChangeCatalogList(value, label);
}}
/>
</div>
</Modal>
)
}
}
export default MoveModal;
\ No newline at end of file
.common-move-modal {
.tip {
display: flex;
align-items: center;
margin-bottom: 16px;
.iconfont {
font-size: 14px;
color: #BFBFBF;
margin-right: 8px;
}
.text {
color: #999;
font-size: 14px;
}
}
.move-item {
.label {
color: #333;
}
}
.ant-select-tree .ant-select-tree-node-content-wrapper.ant-select-tree-node-selected {
background: rgba(41, 102, 255, 0.06);
}
}
\ No newline at end of file
.operate-paper-page { .operate-paper-page {
.box { .box {
margin-bottom: 52px !important; margin-bottom: 52px !important;
.ant-form-item {
margin-bottom: 24px !important;
}
.table-style { .table-style {
border: 1px solid #f0f0f0 !important; border: 1px solid #f0f0f0 !important;
} }
...@@ -12,7 +15,6 @@ ...@@ -12,7 +15,6 @@
margin-left: 12px; margin-left: 12px;
} }
.choose-btn { .choose-btn {
margin-top: 8px;
margin-bottom: 12px; margin-bottom: 12px;
} }
.paper-info-tip { .paper-info-tip {
...@@ -60,10 +62,14 @@ ...@@ -60,10 +62,14 @@
} }
} }
.ant-table tbody tr { .ant-table tbody tr {
&:last-child { &:nth-child(even) {
td { background: #fff;
border-bottom: none!important; }
} &:nth-child(odd) {
background: #fafafa;
}
td {
border-bottom: none!important;
} }
} }
......
.paper-list { .paper-list {
.select-box {
display: flex;
align-items: center;
.select-container{
margin-right: 24px;
.con{
background: #E9EFFF;
border-radius: 4px;
padding: 3px 16px;
display: inline-flex;
align-items: center;
justify-content: space-between;
.tip{
font-size:14px;
color:#2966FF;
margin-right:8px;
}
.text{
font-size:14px;
color:#666;
margin-right:30px;
}
.clear{
color:#5289FA;
font-size:14px;
}
}
}
}
.ant-radio-wrapper{ .ant-radio-wrapper{
left: -10px; left: -10px;
} }
...@@ -45,7 +74,7 @@ ...@@ -45,7 +74,7 @@
} }
.paper-list-content { .paper-list-content {
position: relative; position: relative;
margin-top: 16px; margin-top: 12px;
.empty-list-tip { .empty-list-tip {
color: #2966FF; color: #2966FF;
cursor: pointer; cursor: pointer;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-02-25 11:23:47 * @Date: 2021-02-25 11:23:47
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-05-20 11:43:21 * @LastEditTime: 2021-05-30 18:17:05
* @Description: 助学工具-新建试卷-选择题目列表 * @Description: 助学工具-新建试卷-选择题目列表
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -16,6 +16,7 @@ import { ...@@ -16,6 +16,7 @@ import {
Select, Select,
Tooltip, Tooltip,
message, message,
DatePicker,
} from "antd"; } from "antd";
import { PageControl } from "@/components"; import { PageControl } from "@/components";
import "./SelectQuestionList.less"; import "./SelectQuestionList.less";
...@@ -23,8 +24,10 @@ import User from "@/common/js/user"; ...@@ -23,8 +24,10 @@ import User from "@/common/js/user";
import AidToolService from "@/domains/aid-tool-domain/AidToolService"; import AidToolService from "@/domains/aid-tool-domain/AidToolService";
import _ from "underscore"; import _ from "underscore";
import Bus from "@/core/bus"; import Bus from "@/core/bus";
import moment from 'moment';
const { Search } = Input; const { Search } = Input;
const { RangePicker } = DatePicker;
const questionTypeEnum = { const questionTypeEnum = {
SINGLE_CHOICE: "单选题", SINGLE_CHOICE: "单选题",
...@@ -68,12 +71,15 @@ class SelectQuestionList extends Component { ...@@ -68,12 +71,15 @@ class SelectQuestionList extends Component {
categoryId: null, // 当前题库分类Id categoryId: null, // 当前题库分类Id
questionName: null, // 题目名称 questionName: null, // 题目名称
questionType: null, // 题目类型 questionType: null, // 题目类型
updateDateStart: null,
updateDateEnd: null,
source: 0, source: 0,
tenantId: User.getStoreId(), tenantId: User.getStoreId(),
userId: User.getStoreUserId(), userId: User.getStoreUserId(),
}, },
dataSource: [], dataSource: [],
selectQuestionKeys: [], selectQuestionKeys: [],
expandFilter: false,
}; };
} }
...@@ -98,6 +104,8 @@ class SelectQuestionList extends Component { ...@@ -98,6 +104,8 @@ class SelectQuestionList extends Component {
order: "UPDATED_DESC", // 排序规则 order: "UPDATED_DESC", // 排序规则
questionName: null, // 题目名称 questionName: null, // 题目名称
questionType: null, // 题目类型 questionType: null, // 题目类型
updateDateStart: null,
updateDateEnd: null,
}; };
this.setState({ query: _query }, () => { this.setState({ query: _query }, () => {
this.queryQuestionPageListWithContent(); this.queryQuestionPageListWithContent();
...@@ -134,6 +142,8 @@ class SelectQuestionList extends Component { ...@@ -134,6 +142,8 @@ class SelectQuestionList extends Component {
order: "UPDATED_DESC", // 排序规则 order: "UPDATED_DESC", // 排序规则
questionName: null, // 题目名称 questionName: null, // 题目名称
questionType: null, // 题目类型 questionType: null, // 题目类型
updateDateStart: null,
updateDateEnd: null,
}; };
this.setState({ query: _query }, () => { this.setState({ query: _query }, () => {
this.queryQuestionPageListWithContent(); this.queryQuestionPageListWithContent();
...@@ -144,6 +154,14 @@ class SelectQuestionList extends Component { ...@@ -144,6 +154,14 @@ class SelectQuestionList extends Component {
parseColumns = () => { parseColumns = () => {
const columns = [ const columns = [
{ {
title: "题型",
key: "questionTypeEnum",
dataIndex: "questionTypeEnum",
render: (val) => {
return questionTypeEnum[val];
},
},
{
title: "题目", title: "题目",
key: "questionStem", key: "questionStem",
dataIndex: "questionStem", dataIndex: "questionStem",
...@@ -170,14 +188,6 @@ class SelectQuestionList extends Component { ...@@ -170,14 +188,6 @@ class SelectQuestionList extends Component {
}, },
}, },
{ {
title: "题型",
key: "questionTypeEnum",
dataIndex: "questionTypeEnum",
render: (val) => {
return questionTypeEnum[val];
},
},
{
title: "正确率", title: "正确率",
key: "accuracy", key: "accuracy",
dataIndex: "accuracy", dataIndex: "accuracy",
...@@ -215,11 +225,23 @@ class SelectQuestionList extends Component { ...@@ -215,11 +225,23 @@ class SelectQuestionList extends Component {
// 改变搜索条件 // 改变搜索条件
handleChangeQuery = (searchType, value) => { handleChangeQuery = (searchType, value) => {
const _query = this.state.query;
switch (searchType) {
case "questionName":
_query.questionName = value;
break;
case "updatedTime":
_query.updateDateStart = value && value[0]?.startOf('day').valueOf();
_query.updateDateEnd = value && value[1]?.endOf('day').valueOf();
break;
case "questionType":
_query.questionType = value;
break
}
this.setState( this.setState(
{ {
query: { query: {
...this.state.query, ..._query,
[searchType]: value || null,
current: 1, current: 1,
}, },
}, },
...@@ -281,6 +303,7 @@ class SelectQuestionList extends Component { ...@@ -281,6 +303,7 @@ class SelectQuestionList extends Component {
total, total,
query, query,
selectQuestionKeys = [], selectQuestionKeys = [],
expandFilter,
} = this.state; } = this.state;
const { current, size, questionName, questionType } = query; const { current, size, questionName, questionType } = query;
const rowSelection = { const rowSelection = {
...@@ -364,6 +387,17 @@ class SelectQuestionList extends Component { ...@@ -364,6 +387,17 @@ class SelectQuestionList extends Component {
})} })}
</Select> </Select>
</div> </div>
<div className="search-condition__item">
<span className="search-label">更新时间:</span>
<RangePicker
value={[
query.updateDateStart ? moment(Number(query.updateDateStart)) : null,
query.updateDateEnd ? moment(Number(query.updateDateEnd)) : null
]}
onChange={(value) => {
this.handleChangeQuery("updatedTime", value)
}} />
</div>
</div> </div>
<div className="reset-fold-area"> <div className="reset-fold-area">
......
...@@ -2,19 +2,22 @@ ...@@ -2,19 +2,22 @@
.select-question-filter { .select-question-filter {
position: relative; position: relative;
.search-condition { .search-condition {
width: calc(100% - 80px); width: calc(100% - 20px);
display: flex; display: flex;
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
&__item { &__item {
margin-right: 3%; margin-right: 3%;
width: 30%;
margin-bottom: 16px; margin-bottom: 16px;
display: flex;
.search-label { .search-label {
vertical-align: middle; vertical-align: middle;
display: inline-block; display: inline-block;
height: 32px; height: 32px;
line-height: 32px; line-height: 32px;
flex-shrink: 0;
} }
} }
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-03-27 11:15:03 * @Date: 2021-03-27 11:15:03
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-04-15 13:22:10 * @LastEditTime: 2021-06-01 17:28:21
* @Description: 助学工具-试卷-预览试卷 * @Description: 助学工具-试卷-预览试卷
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -284,7 +284,7 @@ class PreviewPaperModal extends Component { ...@@ -284,7 +284,7 @@ class PreviewPaperModal extends Component {
footer={null} footer={null}
onCancel={this.props.close} onCancel={this.props.close}
> >
{paperName && <div className="paper-title">{paperName}</div>} {paperName && <div className="paper-title">{paperName.length > 40 ? paperName.substring(0, 40) : paperName}</div>}
{questionList && questionList.length > 0 ? ( {questionList && questionList.length > 0 ? (
<div className="question-list-box"> <div className="question-list-box">
{_.map(questionList, (questionItem, questionIndex) => { {_.map(questionList, (questionItem, questionIndex) => {
......
...@@ -8,13 +8,16 @@ ...@@ -8,13 +8,16 @@
flex-wrap: wrap; flex-wrap: wrap;
&__item { &__item {
display: flex;
margin-right: 3%; margin-right: 3%;
margin-bottom: 16px; margin-bottom: 16px;
width: 30%;
.search-label { .search-label {
vertical-align: middle; vertical-align: middle;
display: inline-block; display: inline-block;
height: 32px; height: 32px;
line-height: 32px; line-height: 32px;
flex-shrink: 0;
} }
} }
} }
...@@ -37,12 +40,37 @@ ...@@ -37,12 +40,37 @@
} }
} }
} }
.select-container{
.con {
background: #E9EFFF;
border-radius: 4px;
padding: 3px 16px;
display: inline-flex;
align-items: center;
justify-content: space-between;
.tip {
font-size: 14px;
color: #2966FF;
margin-right: 8px;
}
.text {
font-size: 14px;
color: #666;
margin-right: 30px;
}
.clear {
color: #5289FA;
font-size: 14px;
}
}
}
.data-icon { .data-icon {
cursor: pointer; cursor: pointer;
} }
.question-list-content { .question-list-content {
position: relative; position: relative;
margin-top: 16px; margin-top: 12px;
.empty-list-tip { .empty-list-tip {
color: #2966FF; color: #2966FF;
cursor: pointer; cursor: pointer;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment