zhengpengju 3 years ago
commit 4a322d4594

@ -85,7 +85,7 @@ export default defineConfig({
name: '主题设置',
icon: 'smile',
path: '/course/subject',
component: './course/subject',
component: './course/subject/index_new',
},
{
name: '新建主题',
@ -102,7 +102,7 @@ export default defineConfig({
hideInMenu: true,
},
{
name: '编辑主题',
name: '新建主题',
icon: 'smile',
path: '/course/subject/step/create',
component: './course/subject/step',

@ -414,68 +414,68 @@ const TableList: React.FC = () => {
<ProFormTextArea width="md" name="desc" />
</ModalForm>
<Modal
title={'主题信息'}
width="50%"
visible={detailModalVisible}
onCancel={() => {
setCurrentRow(undefined); // 设置当前行
handleDetailModalVisible(false);
}}
footer={null}
centered
>
{currentRow?.subject_id && (
<>
<ProDescriptions
layout='horizontal'
column={1}
//actionRef={actionRef}
title={false}
/*
request={async () => {
console.log('step2 主题信息')
return Promise.resolve({
success: true,
data: { id: '这是一段文本', object: '', date: '2020-07-30 08:00', duration: '', grade: 100, through: '>60', learn: '>20 min', times: 2 },
});
}}*/
extra={false}
>
<ProDescriptions.Item dataIndex="id" hideInDescriptions />
<ProDescriptions.Item dataIndex="subject_name" label="主题名称" valueType="text">{currentRow?.subject_name}</ProDescriptions.Item>
<ProDescriptions.Item dataIndex="subject_describe" label="主题介绍" valueType="text">
<div dangerouslySetInnerHTML={{ __html: currentRow?.subject_describe }} />
</ProDescriptions.Item>
</ProDescriptions>
title={'主题信息'}
width="50%"
visible={detailModalVisible}
onCancel={() => {
setCurrentRow(undefined); // 设置当前行
handleDetailModalVisible(false);
}}
footer={null}
centered
>
{currentRow?.subject_id && (
<>
<ProDescriptions
layout='horizontal'
column={1}
//actionRef={actionRef}
title={false}
/*
request={async () => {
console.log('step2 主题信息')
return Promise.resolve({
success: true,
data: { id: '这是一段文本', object: '', date: '2020-07-30 08:00', duration: '', grade: 100, through: '>60', learn: '>20 min', times: 2 },
});
}}*/
extra={false}
>
<ProDescriptions.Item dataIndex="id" hideInDescriptions />
<ProDescriptions.Item dataIndex="subject_name" label="主题名称" valueType="text">{currentRow?.subject_name}</ProDescriptions.Item>
<ProDescriptions.Item dataIndex="subject_describe" label="主题介绍" valueType="text">
<div dangerouslySetInnerHTML={{ __html: currentRow?.subject_describe }} />
</ProDescriptions.Item>
</ProDescriptions>
<ProTable<TableListItem, TableListPagination>
headerTitle={false}
actionRef={actionChapterRef}
rowKey="chapter_id"
options={false}
search={false}
toolBarRender={false}
request={async (value) => {
const { data } = await queryListChapterBySubject({
subject_id: currentRow?.subject_id,
page_number: value?.current || 1,
page_size: value?.pageSize,
});
return {
current: data?.page_number,
data: data?.list,
pageSize: data?.page_size,
success: true,
total: data?.total_row || 0,
};
}}
// dataSource={list}
columns={chapterColumns}
rowSelection={false}
/>
</>
<ProTable<TableListItem, TableListPagination>
headerTitle={false}
actionRef={actionChapterRef}
rowKey="chapter_id"
options={false}
search={false}
toolBarRender={false}
request={async (value) => {
const { data } = await queryListChapterBySubject({
subject_id: currentRow?.subject_id,
page_number: value?.current || 1,
page_size: value?.pageSize,
});
return {
current: data?.page_number,
data: data?.list,
pageSize: data?.page_size,
success: true,
total: data?.total_row || 0,
};
}}
// dataSource={list}
columns={chapterColumns}
rowSelection={false}
/>
</>
)}
</Modal>
</Modal>
<UpdateForm
onSubmit={async (value) => {
const success = await handleUpdate(value, currentRow);

@ -0,0 +1,379 @@
import { PlusOutlined } from '@ant-design/icons';
import { Button, message, Card, List,Image, Modal,DatePicker,Input,Typography} from 'antd';
import React, { useState, useRef,useEffect } from 'react';
import { history,useRequest } from 'umi';
import { PageContainer, FooterToolbar } from '@ant-design/pro-layout';
import type { ProColumns, ActionType } from '@ant-design/pro-table';
import ProTable from '@ant-design/pro-table';
import { ModalForm, ProFormText, ProFormTextArea } from '@ant-design/pro-form';
import type { ProDescriptionsItemProps } from '@ant-design/pro-descriptions';
import ProDescriptions from '@ant-design/pro-descriptions';
import type { FormValueType } from './components/UpdateForm';
import UpdateForm from './components/UpdateForm';
import { copySubject, addSubject, updateSubject, removeSubject, querySubjectList, queryListChapterBySubject } from './service';
import type { TableListItem, TableListPagination } from './data';
import { queryCourseListByTag } from '../option/service';
const { confirm } = Modal;
const { RangePicker } = DatePicker;
const { Search } = Input;
const { Paragraph } = Typography;
import pic from './image/tip.svg';
import styles from './style.less';
// import useEffect = require("react");
import moment from 'moment';
const dateFormat='YYYY-MM-DD';
const TableList: React.FC = () => {
/** 新建窗口的弹窗 */
const [createModalVisible, handleModalVisible] = useState<boolean>(false);
const [updateModalVisible, handleUpdateModalVisible] = useState<boolean>(false);
const [detailModalVisible, handleDetailModalVisible] = useState<boolean>(false);
const [courseList,setCourseList]=useState([]);
const [date,setDate]=useState('');
const [beginTime,setBeginTime]=useState('');
const [endTime,setEndTime]=useState('');
const [subjectName,setSubjectName]=useState('');
const [pageNumber,setPageNumber]=useState([]);
const [total,setTotal]=useState(0);
const actionRef = useRef<ActionType>();
const actionChapterRef = useRef<ActionType>();
const [currentRow, setCurrentRow] = useState<TableListItem>();
const [selectedRowsState, setSelectedRows] = useState<TableListItem[]>([]);
const chapterColumns: ProColumns<TableListItem>[] = [
{
title: '序号',
key: 'index',
valueType: 'indexBorder',
width: 48,
},
{
title: '章节名称',
dataIndex: 'chapter_name',
valueType: 'text',
hideInTable: false,
hideInDescriptions: false,
hideInForm: false,
hideInSearch: true,
},
{
title: '简单介绍',
dataIndex: 'chapter_describe',
valueType: 'textarea',
sorter: false,
hideInTable: false,
hideInForm: false,
hideInSearch: true,
renderText: (val: string) => (<div dangerouslySetInnerHTML={{ __html: val }} />),
},
{
title: '包含课程',
valueType: 'select',
dataIndex: 'course_names',
sorter: false,
hideInTable: false,
hideInForm: false,
hideInSearch: true,
fieldProps: {
mode: "multiple"
},
renderText: (val: string) => `${val}`,
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: 'course_minutes',
valueType: 'text',
sorter: false,
hideInTable: false,
hideInForm: true,
hideInSearch: true,
renderText: (val: string) => `${val}学时`,
},
];
const { data, run, loading } = useRequest(() => {
return querySubjectList({
begin_time:beginTime,
end_time:endTime,
page_number:pageNumber,
page_size:9999,
subject_name:subjectName,
});
},{
onSuccess:(result)=>{
console.log('result9999',result);
setCourseList(result.list);
setTotal(result.total_row)
}
});
const showConfirm=async (record)=>{
confirm({
title: '确认删除主题吗?',
centered:true,
onOk() {
handleRemove([{ key: record?.subject_id }]); // 调用批量删除函数如果接口不支持批量需要在service中处理
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
},
onCancel() {
},
});
};
const handleRemove = async (selectedRows: TableListItem[]) => {
const hide = message.loading('正在删除');
if (!selectedRows) return true;
try {
const { code, msg } = await removeSubject({
key: selectedRows.map((row) => row.key),
});
hide();
if (code === 2000) {
message.success('删除成功,即将刷新');
run()
} else {
message.warning(msg);
}
return true;
} catch (error) {
hide();
message.error('删除失败,请重试');
return false;
}
};
useEffect(()=>{
run()
},[beginTime,endTime,subjectName])
return (
<PageContainer>
<div style={{height:window.innerHeight-300,background:'#fff',overflow:'auto',padding:18}}>
<div>
<span></span>
<Input placeholder="请输入主题关键字"
style={{ width: 200 }}
value={subjectName}
onChange={(e)=>{
setSubjectName(e.target.value);
}}
/>
<span style={{marginLeft:'2rem'}}></span>
<RangePicker value={date}
onChange={(dates, dateStrings)=>{
setDate([moment(dateStrings[0],'YYYY-MM-DD'),moment(dateStrings[1],'YYYY-MM-DD')])
setBeginTime(dateStrings[0])
setEndTime(dateStrings[1]);
}}/>
<Button style={{float:'right'}}
type="primary"
onClick={() => {
history.push('/course/subject/step/create')
}}
></Button>
</div>
<div style={{height:'2.5rem',backgroundColor: 'rgba(230, 247, 255, 1)',marginTop:'1rem',paddingTop:'.5rem'}}>
<img src={pic} style={{width:'20px',fontSize:'20px',lineHeight:'18px',vertextAlign:'bottom'}}/>
{total}
<span style={{color:'#096dd9',marginLeft:'2rem',cursor:'pointer'}}
onClick={()=>{
setBeginTime('');
setEndTime('');
setSubjectName('');
setDate('')
}}
></span>
</div>
<div style={{margin:'1rem '}} className={styles.cardList}>
<List
rowKey="id"
loading={loading}
grid={{
gutter: 16,
xs: 1,
sm: 1,
md: 1,
lg: 1,
xl: 1,
xxl: 1,
}}
dataSource={courseList}
renderItem={(item) => {
if (item && item.subject_id) {
return (
<List.Item key={item?.subject_id}>
<Card
title={<a style={{color:'#000'}}>{item?.subject_name}</a>}
hoverable
className={styles.card}
actions={[]}
>
<Card.Meta
avatar={<Image preview={false} width={200} height={150} src={`/dsideal_yy/html/${item.attachment_json.url}`} fallback="../fallback.svg" />}
title={false}
description={
<>
<Card.Grid hoverable={false} style={{width:'50%',padding:0}}>
<Paragraph className={styles.item} ellipsis={{ rows: 4 }}>
{/*{item.subject_describe}*/}
<div dangerouslySetInnerHTML={{__html:item.subject_describe}}/>
</Paragraph>
</Card.Grid>
<Card.Grid hoverable={false} style={{marginTop:'2rem',width:'30%',vertextAlign:'center',textAlign:'center',borderLeft:'1px solid #ccc'}}>
<div>
{
item.b_use===0?
<div></div>:
<div>
{item.total_chapter_number} <br />
{item.total_course_number} <br />
{item.total_course_minutes}
</div>
}
</div>
</Card.Grid>
<Card.Grid hoverable={false} style={{width:'20%',textAlign:'center'}}>
<Button type="primary"
style={{marginBottom:'.5rem'}}
onClick={() => {
setCurrentRow(item);
if (actionChapterRef.current) {
actionChapterRef.current.reload();
}
handleDetailModalVisible(true);
}}
></Button><br />
{
item.isExistRelation?'':
[<Button type="primary"
style={{marginBottom:'.5rem'}}
onClick={() => {
history.push(`/course/subject/step/update/${item.subject_id}`)
}}
></Button>,<br />]
}
<Button type="primary"
style={{marginBottom:'.5rem'}}
onClick={async () => {
const hide = message.loading('正在复制');
// console.log(e, record, 'copy')
const success = await copySubject({ subject_id: item.subject_id });
if (success) {
hide();
run();
// handleModalVisible(false);
if (actionRef.current) {
actionRef.current.reload();
}
}
}}
></Button><br />
{
item.isExistRelation?'':
[ <Button type="primary"
style={{marginBottom:'.5rem'}}
onClick={() => {
showConfirm(item)
}}
></Button>,<br />]
}
</Card.Grid>
</>
}
/>
</Card>
</List.Item>
);
}
}}
/>
</div>
</div>
<Modal
title={'主题信息'}
width="50%"
visible={detailModalVisible}
onCancel={() => {
setCurrentRow(undefined); // 设置当前行
handleDetailModalVisible(false);
}}
footer={null}
centered
>
{currentRow?.subject_id && (
<>
<ProDescriptions
layout='horizontal'
column={1}
//actionRef={actionRef}
title={false}
/*
request={async () => {
console.log('step2 主题信息')
return Promise.resolve({
success: true,
data: { id: '这是一段文本', object: '', date: '2020-07-30 08:00', duration: '', grade: 100, through: '>60', learn: '>20 min', times: 2 },
});
}}*/
extra={false}
>
<ProDescriptions.Item dataIndex="id" hideInDescriptions />
<ProDescriptions.Item dataIndex="subject_name" label="主题名称" valueType="text">{currentRow?.subject_name}</ProDescriptions.Item>
<ProDescriptions.Item dataIndex="subject_describe" label="主题介绍" valueType="text">
<div dangerouslySetInnerHTML={{ __html: currentRow?.subject_describe }} />
</ProDescriptions.Item>
</ProDescriptions>
<ProTable<TableListItem, TableListPagination>
headerTitle={false}
actionRef={actionChapterRef}
rowKey="chapter_id"
options={false}
search={false}
toolBarRender={false}
request={async (value) => {
const { data } = await queryListChapterBySubject({
subject_id: currentRow?.subject_id,
page_number: value?.current || 1,
page_size: value?.pageSize,
});
return {
current: data?.page_number,
data: data?.list,
pageSize: data?.page_size,
success: true,
total: data?.total_row || 0,
};
}}
// dataSource={list}
columns={chapterColumns}
rowSelection={false}
/>
</>
)}
</Modal>
</PageContainer>
);
};
export default TableList;

@ -614,7 +614,7 @@ export default () => {
// console.log('values:新建', values);
const success = await handleAddChapter({
...values,
subject_id: params?.id || 0,
subject_id:subjectId,
});
if (success) {
handleCreateModalVisible(false);
@ -657,7 +657,7 @@ export default () => {
// console.log('currentRow', currentRow)
const success = await handleUpdateChapter({
...values,
subject_id: params?.id || 0,
subject_id: subjectId,
chapter_id: currentRow.chapter_id
});
if (success) {

@ -0,0 +1,118 @@
@import '~antd/es/style/themes/default.less';
@import './utils/utils.less';
.cardList {
.card {
:global {
.ant-card-meta-title {
margin-bottom: 12px;
& > a {
display: inline-block;
max-width: 100%;
color: @heading-color;
}
}
.ant-card-body:hover {
.ant-card-meta-title > a {
color: @primary-color;
}
}
}
}
.item {
height: 64px;
}
:global {
.ant-list .ant-list-item-content-single {
max-width: 100%;
}
.ant-card-meta-description{
height:100%;
}
.ant-card-grid{
box-shadow:none;
height:100%;
}
}
}
:global {
.ant-card-meta-detail{
height: 200px;
}
}
.extraImg {
width: 155px;
margin-top: -20px;
text-align: center;
img {
width: 100%;
}
}
.newButton {
width: 100%;
height: 201px;
color: @text-color-secondary;
background-color: @component-background;
border-color: @border-color-base;
}
.cardAvatar {
width: 270px;
height: 150px;
border-radius: 10px;
}
.cardDescription {
.textOverflowMulti();
}
.pageHeaderContent {
position: relative;
}
.contentLink {
margin-top: 16px;
a {
margin-right: 32px;
img {
width: 24px;
}
}
img {
margin-right: 8px;
vertical-align: middle;
}
}
@media screen and (max-width: @screen-lg) {
.contentLink {
a {
margin-right: 16px;
}
}
}
@media screen and (max-width: @screen-md) {
.extraImg {
display: none;
}
}
@media screen and (max-width: @screen-sm) {
.pageHeaderContent {
padding-bottom: 30px;
}
.contentLink {
position: absolute;
bottom: -4px;
left: 0;
width: 1000px;
a {
margin-right: 16px;
}
img {
margin-right: 4px;
}
}
}

@ -0,0 +1,50 @@
.textOverflow() {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
word-break: break-all;
}
.textOverflowMulti(@line: 3, @bg: #fff) {
position: relative;
max-height: @line * 1.5em;
margin-right: -1em;
padding-right: 1em;
overflow: hidden;
line-height: 1.5em;
text-align: justify;
&::before {
position: absolute;
right: 14px;
bottom: 0;
padding: 0 1px;
background: @bg;
content: '...';
}
&::after {
position: absolute;
right: 14px;
width: 1em;
height: 1em;
margin-top: 0.2em;
background: white;
content: '';
}
}
// mixins for clearfix
// ------------------------
.clearfix() {
zoom: 1;
&::before,
&::after {
display: table;
content: ' ';
}
&::after {
clear: both;
height: 0;
font-size: 0;
visibility: hidden;
}
}

@ -180,7 +180,7 @@ const CardList = () => {
<div style={{ marginBottom: '2rem', }}>
<div style={{ paddingRight: '1rem', width: '10%', display: 'inline-block' }}>{item?.sort_no}</div>
<div title={item?.course_name} style={{ paddingRight: '1rem', width: '30%', display: 'inline-block', verticalAlign: 'middle', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{item?.course_name}</div>
<div style={{ paddingRight: '1rem', width: '10%', display: 'inline-block' }}>{item?.course_hours*60}</div>
<div style={{ paddingRight: '1rem', width: '10%', display: 'inline-block' }}>{item?.course_minutes}</div>
<div style={{ paddingRight: '1rem', width: '30%', display: 'inline-block' }}>
<Progress percent={item?parseInt(item.learning_progress):0}
format={(percent) => percent === 100 ? '100%' : `${percent}%`}

Loading…
Cancel
Save