考试功能问题修改、考试码实现、其他问题修复

main
liuyiliang 3 months ago
parent 552a82a178
commit 73809d33ad
  1. 62
      package.json
  2. 1
      packages/examination/.env.development
  3. 1
      packages/examination/.env.production
  4. 13
      packages/examination/package.json
  5. 7
      packages/examination/src/api/axios.js
  6. 16
      packages/examination/src/api/exam-online/index.tsx
  7. 61
      packages/examination/src/style/common.css
  8. 72
      packages/examination/src/views/exam-online/compoents/ExamEditPage.tsx
  9. 119
      packages/examination/src/views/exam-online/compoents/ExamListPage.tsx
  10. 26
      packages/examination/src/views/exam-online/compoents/ExamPaperAnalysisPage.tsx
  11. 89
      packages/examination/src/views/exam-online/compoents/ExamStatisticsPage.tsx
  12. 22
      packages/examination/src/views/exam-online/exam-edit.tsx
  13. 26
      packages/examination/src/views/statistical/customerRetention.tsx
  14. 3
      packages/examination/src/views/statistical/enterpriseFile.tsx
  15. 24
      packages/examination/src/views/statistical/serviceStatistics.tsx

@ -1,62 +0,0 @@
{
"name": "ccic-web",
"private": true,
"version": "1.0.0",
"description": "大地项目",
"repository": {
"type": "git",
"url": "git@120.78.213.5:/home/git/ccic-code/ccic-web.git"
},
"scripts": {
"start:insurance": "lerna run start --scope=insure",
"start:expert": "lerna run start --scope=expert",
"start:enterprise": "lerna run start --scope=enterprise",
"build": "lerna run build --scope=*",
"build:pro": "cross-env --REACT_APP_ENVIRONMENT=official lerna run build --scope=*"
},
"dependencies": {
"@ahooksjs/use-url-state": "^3.5.0",
"@amap/amap-jsapi-loader": "^1.0.1",
"@ant-design/pro-components": "2.3.20",
"ahooks": "3.7.1",
"antd": "^4.24.2",
"axios": "^0.27.2",
"downloadjs": "^1.4.7",
"echarts": "^5.3.3",
"js-base64": "^3.7.5",
"js-cookie": "^3.0.1",
"lodash": "^4.17.21",
"lru-cache": "^6.0.0",
"mime-match": "^1.0.2",
"moment": "^2.29.4",
"nprogress": "^0.2.0",
"query-string": "^7.1.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router-dom": "6.3.0",
"react-scripts": "5.0.1",
"store2": "^2.14.2",
"whatwg-fetch": "^3.6.2",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@craco/craco": "^7.1.0",
"@types/lodash": "^4.14.182",
"@types/node": "^14.14.10",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@types/react-router-dom": "^5.3.3",
"babel-plugin-transform-remove-console": "^6.9.4",
"craco-less": "^2.1.0-alpha.0",
"cross-env": "^7.0.3",
"http-proxy-middleware": "^2.0.6",
"lerna": "^5.6.2",
"typescript": "^4.7.4",
"webpack-cli": "^5.0.1"
},
"author": "guowei",
"license": "ISC",
"workspaces": [
"packages/*"
]
}

@ -0,0 +1 @@
REACT_APP_SERVER_URL=http://localhost:8187/

@ -0,0 +1 @@
REACT_APP_SERVER_URL=http://172.16.10.66:8187/

@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@ant-design/icons": "^5.6.1",
"@craco/craco": "5.9.0",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
@ -12,18 +13,24 @@
"antd": "^4.7.0",
"axios": "^0.21.0",
"craco-less": "^1.17.1",
"echarts": "4.9.0",
"echarts-for-react": "^2.0.16",
"echarts": "5.2.0",
"echarts-for-react": "^3.0.2",
"file-saver": "^2.0.5",
"html2canvas": "^1.4.1",
"js-cookie": "^2.2.1",
"jspdf": "^3.0.0",
"process": "^0.11.10",
"quill-emoji": "^0.2.0",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react-qrcode-logo": "^3.0.0",
"react-quill": "^1.3.5",
"react-redux": "^7.2.2",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.3",
"redux": "^4.0.5",
"typescript": "^4.1.3"
"typescript": "^4.1.3",
"xlsx": "^0.18.5"
},
"scripts": {
"start": "craco start",

@ -22,16 +22,9 @@ const instance = axios.create({
withCredentials: true,
timeout: 100000
})
export const serverUrl = 'http://localhost:8187/';
localStorage.setItem('si', JSON.stringify({ eName: 'admin' }));
// const token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJjY2ljLXVzZXIiLCJpYXQiOjE3NDI1NDI4MTIsImV4cCI6MTc0MjYyOTIxMiwiaWQiOiI0MjY1NjA4NTc2MDI4MjYyNCIsInVzZXJOYW1lIjoi5rWL6K-VNjI2ODg1Iiwib3JnYW5JZCI6MzEwMTAwMDAsIm9yZ2FuTmFtZSI6IuS4iua1t-WIhuWFrOWPuCIsIm9yZ2FuQ29kZSI6IjMxMDEwMDAwIiwidXNlck5vIjoiODAwMDYyNjg4NSIsIm9yZ2FuVHlwZSI6IjMiLCJncmlkQ29kZSI6IjMxMDAwMCJ9.C8a04P9zSCaN388EMmCqlhcTen2H6GV4TmNQaA1qDac'
// if (token) {
// setToken(token);
// localStorage.setItem('si', JSON.stringify({ eName: 'admin' }));
// }
instance.interceptors.request.use(
config => {
let token = getToken();

@ -7,9 +7,18 @@ export function getList(obj: any) {
params: obj
})
}
//新增考试
export function add(obj: any) {
return axios.post( '/ex/exam-schedule/insert',
// 新增或编辑考试
export function save(obj: any) {
return axios.post( '/ex/exam-schedule/save',
obj,{
headers: {
'Content-Type': 'application/json'
}
})
}
// 保存考试码
export function saveQrCode(obj: any) {
return axios.post( '/ex/exam-schedule/saveQrCode',
obj,{
headers: {
'Content-Type': 'application/json'
@ -26,7 +35,6 @@ export function getExamDataById(id:any){
}
// 发布与批量发布
export function doPublish(ids: string[]) {
debugger
return axios.post('/ex/exam-schedule/publish', ids, {
headers: {
'Content-Type': 'application/json'

@ -321,3 +321,64 @@ table.ikd-input-table {
.ant-breadcrumb li:last-child a {
color: #6789E2;
}
.qrCodeRef {
position: relative;
display: inline-block;
}
.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgb(200 205 219 / 85%);
display: none;
justify-content: center;
align-items: center;
border-radius: 4px;
}
.refresh-link {
color: black;
text-decoration: none;
font-size: revert;
font-weight: bold;
}
.refresh-link:hover,
.refresh-link:active {
color: #48609f;
}
.qrCodeRef:hover .overlay {
display: flex;
}
.exam-card-left {
height: 100%;
}
.exam-card-left .ant-card-body {
height: 90%;
}
.exam-card .ant-card-head {
background: #D8E6F5;
}
.exam-card .ant-card-head-title {
font-size: x-large;
}
.exam-card .ant-select-selector {
background-color: rgb(0 0 0 / 0%) !important;
border-color: #bfced6 !important;
}
.exam-statistic-box {
text-align: center;
background: rgb(220, 228, 248);
height: 110px;
border-radius: 4px;
}
.exam-statistic {
justify-content: center;
align-items: center;
display: flex;
height: 100%;
font-size: x-large;
}

@ -4,10 +4,9 @@ import { Form, Input, Select, DatePicker, Space, ColProps, Button, Cascader, Def
import type { FormProps, FormInstance } from 'antd';
import ESBreadcrumbComponent from './ESBreadcrumbComponent'; // 引入面包屑组件
import { withRouter, RouteComponentProps } from 'react-router-dom'; // 引入 withRouter 和 RouteComponentProps
import { add, getIndustryList, getPaperListWithDetails } from "api/exam-online/index"; // 修改接口为获取带详情的试卷列表
import { save, getIndustryList, getPaperListWithDetails } from "api/exam-online/index"; // 修改接口为获取带详情的试卷列表
import { provice } from './city.js'; // 引入省市县数据
import moment, { Moment } from 'moment';
import TextArea from "antd/es/input/TextArea"; // 引入 moment
const { Option } = Select;
@ -15,6 +14,7 @@ const { Option } = Select;
export interface ExamBasicInfo {
examId: string;
examName: string;
industryId: string | number;
regulatedIndustry: string | number;
paperId: string;
examScore: string;
@ -78,10 +78,16 @@ const convertArrToRegion = (regionArr: string[]) => {
return regionArr.join('/');
};
interface industryObj {
value: any;
label: any;
}
const ExamBasicInfoForm: React.FC<PropsWithRouter> = (props) => {
const { isEdit, initialFormData = {} as ExamBasicInfo, history } = props;
const formRef = useRef<FormInstance<ExamBasicInfo>>(null);
const [industryOptions, setIndustryOptions] = useState<{ value: string | number; label: string }[]>([]);
// const [industryOptions, setIndustryOptions] = useState<{ value: string | number; label: string }[]>([]);
const [industryOptions, setIndustryOptions] = useState<industryObj[]>([]);
const [allPaperOptions, setAllPaperOptions] = useState<{ value: string; label: string; examScore: string; examDuration: string; regulatedIndustry: string | number }[]>([]);
const [paperOptions, setPaperOptions] = useState<{ value: string; label: string; examScore: string; examDuration: string }[]>([]);
const [isDataLoaded, setIsDataLoaded] = useState(false);
@ -95,13 +101,14 @@ const ExamBasicInfoForm: React.FC<PropsWithRouter> = (props) => {
const formattedValidTo = formatDate(validTo);
const formattedValues = {
...otherValues,
id:values.examId,
examRegion: formattedRegion,
validFrom: formattedValidFrom,
validTo: formattedValidTo
};
try {
// 调用 add 方法将数据发送到后台
const response = await add(formattedValues);
// 调用 save 方法将数据发送到后台
const response = await save(formattedValues);
console.log('数据登录成功:', response);
// 登录成功后迁移到 /exam-schedule 页面
history.push("/exam-schedule");
@ -139,38 +146,30 @@ const ExamBasicInfoForm: React.FC<PropsWithRouter> = (props) => {
const fetchData = async () => {
try {
// 获取监管行业列表
const industryResponse = await getIndustryList();
setIndustryOptions(industryResponse.map((item: any) => ({ value: item.industry_id, label: item.industry_name })));
let industryResponse = await getIndustryList();
let industryOptionsArray = industryResponse.map((item: any) => ({ value: item.industry_id, label: item.industry_name }));
setIndustryOptions(industryOptionsArray);
// 获取带详情的试卷列表
const paperResponse = await getPaperListWithDetails();
setAllPaperOptions(paperResponse.map((item: any) => ({
let allPaperOptionsArray = paperResponse.map((item: any) => ({
value: item.paper_id,
label: item.paper_name,
examScore: item.total_score,
examDuration: item.exam_duration,
regulatedIndustry: item.regulatory_industry // 假设接口返回的试卷数据包含监管行业信息
})));
regulatedIndustry: item.regulatory_industry
}))
setAllPaperOptions(allPaperOptionsArray);
setIsDataLoaded(true);
} catch (error) {
console.error('数据加载失败:', error);
}
};
fetchData();
}, []);
useEffect(() => {
if (isDataLoaded) {
const initialIndustry = initialFormData.regulatedIndustry;
const initialIndustry = initialFormData.industryId;
if (String(initialIndustry) === '') {
setPaperOptions(allPaperOptions);
setPaperOptions(allPaperOptionsArray);
} else if (initialIndustry) {
const filteredPapers = allPaperOptions.filter(paper => String(paper.regulatedIndustry) === String(initialIndustry));
const filteredPapers = allPaperOptionsArray.filter((paper: { regulatedIndustry: any; }) => String(paper.regulatedIndustry) === String(initialIndustry));
setPaperOptions(filteredPapers);
} else {
setPaperOptions(allPaperOptions);
setPaperOptions(allPaperOptionsArray);
}
if (formRef.current) {
@ -182,9 +181,8 @@ const ExamBasicInfoForm: React.FC<PropsWithRouter> = (props) => {
validTo: initialFormData.validTo || null,
remark: initialFormData.remark || '',
});
// 设置监管行业的值
const selectedIndustry = industryOptions.find(option => String(option.value) === String(initialFormData.regulatedIndustry));
const selectedIndustry = industryOptionsArray.find((option: { value: any; }) => String(option.value) === String(initialFormData.industryId));
if (selectedIndustry) {
formRef.current.setFieldsValue({ regulatedIndustry: selectedIndustry.value });
} else {
@ -195,21 +193,35 @@ const ExamBasicInfoForm: React.FC<PropsWithRouter> = (props) => {
formRef.current.setFieldsValue({ paperId: initialFormData.paperId || '' });
// 触发 handlePaperChange 函数更新考试分值和考试时长
handlePaperChange(initialFormData.paperId);
const selectedPaper = allPaperOptionsArray.find((paper: { value: any; }) => String(paper.value) === String(initialFormData.paperId));
if (selectedPaper && formRef.current) {
// 更新表单中的考试分值和考试时长
formRef.current.setFieldsValue({
examScore: selectedPaper.examScore,
examDuration: selectedPaper.examDuration
});
}
}
}, [isDataLoaded, allPaperOptions, initialFormData, industryOptions]);
} catch (error) {
console.error('数据加载失败:', error);
}
};
fetchData()
}, []);
const handleIndustryChange = (value: string | number) => {
if (String(value) === '') {
setPaperOptions(allPaperOptions);
} else {
const filteredPapers = allPaperOptions.filter(paper => String(paper.regulatedIndustry) === String(value));
if (filteredPapers.length > 0) {
setPaperOptions(filteredPapers);
}
if (formRef.current) {
formRef.current.setFieldsValue({ paperId: '' }); // 清空试卷选择
}
// @ts-ignore
formRef.current.setFieldsValue({ paperId: '' }); // 清空试卷选择
};
const handlePaperChange = (value: string) => {

@ -1,8 +1,12 @@
import React, { useEffect, useState } from 'react';
import { Table, Input, Button, Select, DatePicker, Modal, Checkbox, message, Form } from 'antd';
import React, { useEffect, useState, useRef } from 'react';
import {Table, Input, Button, Select, DatePicker, Modal, Checkbox, message, Form, Space } from 'antd';
import {
UndoOutlined
} from '@ant-design/icons';
import { Link, withRouter } from 'react-router-dom';
import moment, { Moment } from 'moment'; // 引入 moment
import { getList, doDelete, doCancel, doPublish, getIndustryList } from "api/exam-online/index";
import { saveQrCode, getList, doDelete, doCancel, doPublish, getIndustryList } from "api/exam-online/index";
import { QRCode } from 'react-qrcode-logo';
const { Option } = Select;
@ -17,9 +21,11 @@ type Exam = {
validTo: string;
examDuration: string;
publishStatus: 0 | 1 | 2; // 0: 待发布, 1: 已发布, 2: 已撤回
captcha: string;
};
const ExamListPage = ({ history }: { history: any }) => {
const qrCodeRef = useRef(null);
const [searchForm, setSearchForm] = useState({
examName: '',
paperName: '',
@ -35,6 +41,7 @@ const ExamListPage = ({ history }: { history: any }) => {
pageSize: 10,
total: 0,
});
const [loading, setLoading] = useState<boolean>(false);
useEffect(() => {
const fetchData = async () => {
@ -64,6 +71,7 @@ const ExamListPage = ({ history }: { history: any }) => {
// 查询
const handleSearch = async (page = 1) => {
setLoading(true)
try {
// 构建请求参数
const params = {
@ -82,6 +90,7 @@ const ExamListPage = ({ history }: { history: any }) => {
const endIndex = startIndex + pagination.pageSize;
setCurrentPageExamList(data.data.slice(startIndex, endIndex));
setPagination({ ...pagination, current: page, total: data.total });
setLoading(false)
} catch (error) {
console.error('查询出错:', error);
}
@ -190,10 +199,10 @@ const ExamListPage = ({ history }: { history: any }) => {
// 编辑
const handleEdit = (examId: number) => {
sessionStorage.setItem('examId', String(examId));
history.push('/exam-edit');
// 实现编辑跳转逻辑
console.log('跳转到编辑页面,examId:', examId);
history.push({
pathname: '/exam-edit',
state: { examId },
});
};
const handleDetail = (examId: number) => {
@ -203,11 +212,22 @@ const ExamListPage = ({ history }: { history: any }) => {
}
const [qrCodeVisible, setQrCodeVisible] = useState(false);
const [examId, setExamId] = useState('');
const [qrCodeData, setQrCodeData] = useState('');
const [captcha, setCaptcha] = useState('');
const [examName, setExamName] = useState('');
const handleGenerateQrCode = (examId: number) => {
const handleGenerateQrCode = (exam: any) => {
// 实现生成考试码逻辑
setQrCodeData(`examId: ${examId}`);
setExamId(exam.examId);
setQrCodeData(exam.examId);
setExamName(exam.examName);
if (exam.captcha) {
setCaptcha(exam.captcha);
} else {
setCaptcha(generateCaptcha());
}
setQrCodeVisible(true);
};
@ -338,12 +358,12 @@ const ExamListPage = ({ history }: { history: any }) => {
key: 'examRegion',
},
{
title: '考试有效时间',
title: '考试有效日期',
dataIndex: 'validFrom',
key: 'validFrom',
},
{
title: '考试无效时间',
title: '考试无效日期',
dataIndex: 'validTo',
key: 'validTo',
},
@ -373,23 +393,27 @@ const ExamListPage = ({ history }: { history: any }) => {
title: '操作',
key: 'action',
render: (cellValue: number, record: Exam) => (
<div>
<Space size="middle">
<a onClick={() => handleEdit(record.examId)}></a>
<span style={{ margin: '0 8px' }}>|</span>
{record.publishStatus === 1 ? (<a
onClick={(e) => {
{record.publishStatus === 1 ? (
<a onClick={(e) => {
if (record.publishStatus !== 1) {
e.preventDefault();
return;
}
handleGenerateQrCode(record.examId);
handleGenerateQrCode(record);
}}
>
</a>) : (
<span style={{ color: 'gray' }} ></span>
)}
<span style={{ margin: '0 8px' }}>|</span>
{/*<a onClick={(e) => {*/}
{/* handleGenerateQrCode(record);*/}
{/*}}*/}
{/*>*/}
{/* 生成考试码*/}
{/*</a>*/}
{record.publishStatus === 0 ? (
<a onClick={() => handlePublishSingle(record.examId)}></a>
) : record.publishStatus === 1 ? (
@ -397,9 +421,8 @@ const ExamListPage = ({ history }: { history: any }) => {
) : (
<span style={{ color: 'gray' }}></span>
)}
<span style={{ margin: '0 8px' }}>|</span>
<a onClick={() => handleDeleteSingle(record.examId)}></a>
</div>
</Space>
),
},
];
@ -411,6 +434,43 @@ const ExamListPage = ({ history }: { history: any }) => {
handleSearch(pagination.current);
};
const handleDownloadQRCode = () => {
let formData = {
id: examId,
captcha: captcha,
path: qrCodeData
}
saveQrCode(formData).then(res => {
// @ts-ignore
const canvas = qrCodeRef.current.querySelector('canvas');
if (canvas) {
const dataUrl = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = dataUrl;
link.download = examName + '-考试码.png';
link.click();
setQrCodeVisible(false)
handleSearch(pagination.current);
}
})
};
const handleRefreshQRCode = (examId: any) => {
const str = '/' + Math.floor(Math.random() * 900000);
setQrCodeData(examId + str);
setCaptcha(generateCaptcha());
}
const generateCaptcha = () => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let captcha = '';
for (let i = 0; i < 6; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
captcha += characters[randomIndex];
}
return captcha;
}
return (
<div className="list-filter">
<Form
@ -462,7 +522,7 @@ const ExamListPage = ({ history }: { history: any }) => {
</Select>
</Form.Item>
<Form.Item
label="考试有效时间:"
label="考试有效日期:"
>
<DatePicker
value={searchForm.validTime} // 使用 Moment 类型
@ -497,6 +557,7 @@ const ExamListPage = ({ history }: { history: any }) => {
</div>
<Table
columns={columns}
loading={loading}
dataSource={currentPageExamList}
rowKey="examId"
pagination={pagination}
@ -505,12 +566,24 @@ const ExamListPage = ({ history }: { history: any }) => {
onChange={handleTableChange}
/>
<Modal
title="考试码二维码"
title={<div style={{ textAlign: 'center' }}></div>}
visible={qrCodeVisible}
onCancel={() => setQrCodeVisible(false)}
footer={null}
onOk={handleDownloadQRCode}
centered
okText="确认"
>
{/*<QRCode value={qrCodeData} size={200} />*/}
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: '20px' }}>
<div ref={qrCodeRef} className="qrCodeRef">
<QRCode value={'https://4s27589r64.zicp.fun/wx/'+qrCodeData} size={200} fgColor="#000000" ecLevel="H"/>
<div className="overlay">
<a onClick={() => handleRefreshQRCode(examId)} className="refresh-link"><UndoOutlined /></a>
</div>
</div>
</div>
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: '20px',fontWeight: 'bolder', fontSize: 'larger' }}>
<span>{captcha}</span>
</div>
</Modal>
</div>
);

@ -98,10 +98,12 @@ const ExamPaperAnalysisPage: React.FC<RouteComponentProps> = ({history}) => {
setPagination(newPagination);
};
const columns = [
const columns: any = [
{
title: '序号',
dataIndex: 'key',
width: 70,
align: 'center',
render: (text: any, record: PaperAnalysisDataItem, index: number) => (pagination.current - 1) * pagination.pageSize + index + 1,
},
{
@ -175,32 +177,38 @@ const ExamPaperAnalysisPage: React.FC<RouteComponentProps> = ({history}) => {
</Col>
</Row>
{/* 试卷数量行 */}
<Row gutter={16} style={{ marginTop: 20 }}>
<Col span={6}>
<div style={{ backgroundColor: '#DCE4F8', padding: 10, textAlign: 'center' }}>
<Row gutter={24} style={{ marginTop: 40 }}>
<Col span={8}>
<div className="exam-statistic-box">
<div className="exam-statistic">
<span style={{ color: 'black' }}></span>
<span style={{ color: '#86A3E7' }}>{totalCount}</span>
<span style={{ color: 'black' }}></span>
</div>
</div>
</Col>
<Col span={6}>
<div style={{ backgroundColor: '#DCE4F8', padding: 10, textAlign: 'center' }}>
<Col span={8}>
<div className="exam-statistic-box">
<div className="exam-statistic">
<span style={{ color: 'black' }}>使</span>
<span style={{ color: '#86A3E7' }}>{usedCount}</span>
<span style={{ color: 'black' }}></span>
</div>
</div>
</Col>
<Col span={6}>
<div style={{ backgroundColor: '#DCE4F8', padding: 10, textAlign: 'center' }}>
<Col span={8}>
<div className="exam-statistic-box">
<div className="exam-statistic">
<span style={{ color: 'black' }}></span>
<span style={{ color: '#86A3E7' }}>{disabledCount}</span>
<span style={{ color: 'black' }}></span>
</div>
</div>
</Col>
</Row>
{/* 表格 */}
<div style={{marginTop: 20}}>
<h3>使</h3>
<div style={{fontSize: 'large', fontWeight: 600}}>使</div>
<Table
columns={columns}
dataSource={tableData}

@ -112,6 +112,7 @@ const Dashboard: React.FC = () => {
const handleSelectChange = (value: string) => {
console.log('Selected value:', value);
value = !value ? '' : value;
if (value === '') {
// 选择“全部”
setShowIndustryDimension(true);
@ -137,10 +138,13 @@ const Dashboard: React.FC = () => {
};
// 环状图配置项生成函数
const getRingChartOption = (data: { value: number; name: string }[]) => ({
const getRingChartOption = (data: { value: number; name: string }[], marginVal: any) => (
{
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)',
formatter: function (params: { name: string; value: string; percent: string; }) {
return params.name + '<br/> ' + params.value + ' (' + params.percent + '%)';
}
},
legend: {
orient: 'vertical',
@ -152,12 +156,32 @@ const Dashboard: React.FC = () => {
{
name: '占比',
type: 'pie',
radius: ['30%', '50%'], // 设置为环状图
center: ['40%', '50%'],
data,
radius: ['50%', '75%'],
center: [ `${marginVal}%`, '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 3,
borderColor: '#fff',
},
],
});
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: false,
fontSize: 10,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: data
}
]
}
);
const getPieChartOption = (data: { value: number; name: string }[]) => ({
tooltip: {
trigger: 'item',
@ -173,8 +197,12 @@ const Dashboard: React.FC = () => {
{
name: '占比',
type: 'pie',
radius: '50%',
radius: '75%',
center: ['40%', '50%'],
label: {
show: false,
position: 'center'
},
data,
},
],
@ -185,18 +213,18 @@ const Dashboard: React.FC = () => {
{/* 第一行:试题数、试卷数、考试次数 */}
<Row gutter={16}>
<Col span={8}>
<div style={{ textAlign: 'center', background: '#DCE4F8', height: 60 }}>
<p><span style={{color:"#86A3E7"}}>{questionCount}</span></p>
<div className="exam-statistic-box">
<div className="exam-statistic"><span style={{color:"#86A3E7"}}>{questionCount}</span></div>
</div>
</Col>
<Col span={8}>
<div style={{ textAlign: 'center', background: '#DCE4F8', height: 60 }}>
<p><span style={{color:"#86A3E7"}}>{paperCount}</span></p>
<div className="exam-statistic-box">
<div className="exam-statistic"><span style={{color:"#86A3E7"}}>{paperCount}</span></div>
</div>
</Col>
<Col span={8}>
<div style={{ textAlign: 'center', background: '#DCE4F8', height: 60 }}>
<p><span style={{color:"#86A3E7"}}>{examCount}</span></p>
<div className="exam-statistic-box">
<div className="exam-statistic"><span style={{color:"#86A3E7"}}>{examCount}</span></div>
</div>
</Col>
</Row>
@ -205,14 +233,13 @@ const Dashboard: React.FC = () => {
<Row gutter={16} style={{ marginTop: 20 }}>
{/* 左半区:题库情况 */}
<Col span={12}>
<Card
<Card className="exam-card exam-card-left"
title={(
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span></span>
<span>
<span style={{fontSize: 'medium', color: '#9A9A9A'}}>
<Select placeholder="请选择监管行业" onChange={handleSelectChange} style={{ width: 180 }} defaultValue="">
<Option key="" value=""></Option>
<Select placeholder="全部" onChange={handleSelectChange} style={{ width: 180 }} allowClear>
{industryOptions.map(option => (
<Option key={option.value} value={option.value}>
{option.label}
@ -225,29 +252,29 @@ const Dashboard: React.FC = () => {
>
{/* 监管行业维度统计环状图 */}
{showIndustryDimension && (
<div style={{ marginBottom: 20 }}>
<h3></h3>
<ReactECharts option={getRingChartOption(industryDimensionData)} />
<div style={{ marginBottom: 20, height: '50%' }}>
<div style={{ fontSize: 'large', fontWeight: 600 }}></div>
<ReactECharts option={getRingChartOption(industryDimensionData, '35')} />
</div>
)}
{!showIndustryDimension && (
<div style={{ marginBottom: 20 }}>
<h3>AQ </h3>
<ReactECharts option={getRingChartOption(aqServiceDimensionData)} />
<div style={{ marginBottom: 20, height: '50%' }}>
<div style={{ fontSize: 'large', fontWeight: 600 }}>AQ </div>
<ReactECharts option={getRingChartOption(aqServiceDimensionData, '35')} />
</div>
)}
<hr style={{ border: '1px solid #e8e8e8', marginBottom: 20 }} />
{/* AQ 服务类型维度统计环状图 */}
{showAQServiceDimension && (
<div>
<h3>AQ </h3>
<ReactECharts option={getRingChartOption(aqServiceDimensionData)} />
<div style={{ marginBottom: 20, height: '50%' }}>
<div style={{ fontSize: 'large', fontWeight: 600 }}>AQ </div>
<ReactECharts option={getRingChartOption(aqServiceDimensionData, '25')} />
</div>
)}
{!showAQServiceDimension && (
<div>
<h3></h3>
<ReactECharts option={getRingChartOption(questionTypeDistributionData)} />
<ReactECharts option={getRingChartOption(questionTypeDistributionData, '35')} />
</div>
)}
</Card>
@ -255,7 +282,7 @@ const Dashboard: React.FC = () => {
{/* 右半区:考试情况和试卷情况 */}
<Col span={12}>
{/* 考试情况 */}
<Card
<Card className="exam-card"
title={(
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span></span>
@ -268,7 +295,7 @@ const Dashboard: React.FC = () => {
<ReactECharts option={getPieChartOption(scoreDistributionData)} />
</Card>
{/* 试卷情况环状图 */}
<Card
<Card className="exam-card"
title={(
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span></span>
@ -277,7 +304,7 @@ const Dashboard: React.FC = () => {
)}
style={{ marginTop: 20 }}
>
<ReactECharts option={getRingChartOption(paperDistributionData)} />
<ReactECharts option={getRingChartOption(paperDistributionData, '40')} />
</Card>
</Col>
</Row>

@ -4,6 +4,7 @@ import { withRouter, RouteComponentProps } from 'react-router-dom';
import { getExamDataById } from "api/exam-online/index";
import { ExamBasicInfo } from './compoents/ExamEditPage';
import moment, { Moment } from 'moment';
import { useLocation } from 'react-router-dom';
// 定义 Params 类型
interface Params {
@ -12,6 +13,7 @@ interface Params {
// 定义 location.state 的类型
interface LocationState {
examId: any;
data?: number;
}
@ -30,14 +32,14 @@ interface CustomRouteComponentProps extends RouteComponentProps<Params> {
hash: string;
};
}
class ExamEdit extends Component<CustomRouteComponentProps, States> {
constructor(props: CustomRouteComponentProps) {
super(props);
this.state = {
initialFormData: {
examId: '',
examId: props.location.state.examId,
examName: '',
industryId: '',
regulatedIndustry: '',
paperId: '',
examScore: '',
@ -52,20 +54,14 @@ class ExamEdit extends Component<CustomRouteComponentProps, States> {
}
async componentDidMount() {
// 尝试从 sessionStorage 中获取 examId
const storedExamId = sessionStorage.getItem('examId');
const examId = storedExamId;
if(storedExamId){
sessionStorage.removeItem('examId');
}
const examId = this.state.initialFormData.examId;
try {
const response = await getExamDataById(examId);
const newInitFormData: ExamBasicInfo = {
examId: response.data.examId,
examId: response.data.id,
examName: response.data.examName,
regulatedIndustry: response.data.regulatedIndustry,
industryId: response.data.industryId,
regulatedIndustry: response.data.industryId,
paperId: response.data.paperId,
examScore: response.data.examScore,
examDuration: response.data.examDuration,
@ -75,8 +71,6 @@ class ExamEdit extends Component<CustomRouteComponentProps, States> {
remark: response.data.remark
};
console.log('Received examId:', examId);
this.setState({
initialFormData: newInitFormData,
isLoading: false

@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
import {Table, Button, Spin, Space, Modal, Form, Upload, Image, notification} from 'antd';
import TextArea from "antd/es/input/TextArea";
import {getCustomerRetPage, saveCustomerRet} from "../../api/statistical";
import {serverUrl} from "../../api/axios";
import {
getToken,
} from '../../api/token'
@ -34,22 +33,6 @@ interface FileRes {
const CustomerRetention: React.FC<CustomerRetentionProps> = ({ customer }) => {
// if (!customer) {
// // @ts-ignore
// const { history } = this.props;
// history.push({
// pathname: '/customer'
// });
// return (<div/>);
// }
if (!customer) {
customer = {
customerId: 0,
customerName: 'asda'
}
}
const [data, setData] = useState<Enterprise[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [query, setQuery] = useState<FormData | null>(null);
@ -141,14 +124,17 @@ const CustomerRetention: React.FC<CustomerRetentionProps> = ({ customer }) => {
const showModal2 = (fileUid: string) => {
console.log(fileUid)
setFileUrl('./ex/file/download?fileName=' + fileUid + '&token=' + getToken());
setFileUrl('./api/ex/file/download?fileName=' + fileUid + '&token=' + getToken());
setIsModalOpen2(true);
};
const handleOk2 = () => {
const url = serverUrl + fileUrl;
// @ts-ignore
const url = fileUrl;
const link = document.createElement('a');
if (typeof url === "string") {
link.href = url;
}
link.click();
setFileUrl(null);
setIsModalOpen2(false);
@ -226,7 +212,7 @@ const CustomerRetention: React.FC<CustomerRetentionProps> = ({ customer }) => {
formData.append('file', file);
formData.append('fileType', 'CUSTOMER_RETENTION');
try {
const response = await fetch('/ex/file/upload', {
const response = await fetch('/api/ex/file/upload', {
method: 'POST',
body: formData,
headers: {

@ -106,7 +106,6 @@ const EnterpriseFile: React.FC<CustomerRetentionProps> = ({ customer }) => {
children: res.data.orgRegistrationAddress
}];
setItems(baseData);
debugger
let policyArr = [];
for (var index = 0; index < res.data.policyList.length; index ++) {
let policyCoverageList = [];
@ -318,7 +317,7 @@ const EnterpriseFile: React.FC<CustomerRetentionProps> = ({ customer }) => {
hazardInvestigationList.push(res.data.serviceStatisticsList[index3].hazardInvestigationList[sIndex])
}
policyServiceArr.push({
policyNumber: res.data.hiveClaimStatisticsList[index3].policyNumber,
policyNumber: res.data.serviceStatisticsList[index3].policyNumber,
accidentPreventionList: accidentPreventionList,
riskList: riskList,
hazardInvestigationList: hazardInvestigationList

@ -3,7 +3,6 @@ import {Table, Button, Spin, Radio, DatePicker, Select, Form, Modal} from 'antd'
import {getServiceStatPage} from "../../api/statistical";
import type { RadioChangeEvent } from 'antd';
import {dictionary} from "../../api/dict";
import {serverUrl} from "../../api/axios";
interface Enterprise {
key: string;
@ -50,6 +49,13 @@ const App: React.FC = () => {
align: 'center',
render: (text: string, record: any, index: number) => index + 1
},
{
title: '数据日期',
dataIndex: 'createdAt',
align: 'center',
width: 150,
key: 'createdAt',
},
{
title: '保单号',
dataIndex: 'policyNumber',
@ -68,6 +74,20 @@ const App: React.FC = () => {
align: 'center',
key: 'serviceStatusName',
},
{
title: '启保时间',
dataIndex: 'startDate',
align: 'center',
width: 150,
key: 'startDate',
},
{
title: '终保时间',
dataIndex: 'doneDate',
align: 'center',
width: 150,
key: 'doneDate',
},
{
title: '备注',
dataIndex: 'remarks',
@ -169,7 +189,7 @@ const App: React.FC = () => {
params['serviceStatus'] = serviceStatus;
}
const urlParams = new URLSearchParams(params).toString();
window.open(serverUrl + `./ex/statistics/exportServiceStatList?` + urlParams);
window.open(process.env.REACT_APP_SERVER_URL + `./ex/statistics/exportServiceStatList?` + urlParams);
setIsModalOpen(false);
};

Loading…
Cancel
Save