diff --git a/package.json b/package.json
index edc6e18e..927b96d5 100644
--- a/package.json
+++ b/package.json
@@ -87,6 +87,7 @@
"react-infinite-scroller": "^1.2.4",
"react-loadable": "^5.3.1",
"react-monaco-editor": "0.37",
+ "react-pdf": "^4.2.0",
"react-player": "^1.15.3",
"react-redux": "5.0.7",
"react-resizable": "^1.10.1",
diff --git a/src/App.js b/src/App.js
index 789c0af2..659ee589 100644
--- a/src/App.js
+++ b/src/App.js
@@ -75,6 +75,12 @@ const EducoderLogin = Loadable({
loading: Loading,
})
+// 成果库
+const Achieve=Loadable({
+ loader: () => import('./achieveRequirement/index'),
+ loading: Loading,
+})
+
class App extends Component {
constructor(props) {
super(props);
@@ -267,7 +273,20 @@ class App extends Component {
)
}
/>
+
+ {/*成果库*/}
+ {
+ return ()
+ }
+ }>
+
+
+
+
diff --git a/src/achieveRequirement/achieve/achieveDetail/index.jsx b/src/achieveRequirement/achieve/achieveDetail/index.jsx
new file mode 100644
index 00000000..e7a95f03
--- /dev/null
+++ b/src/achieveRequirement/achieve/achieveDetail/index.jsx
@@ -0,0 +1,142 @@
+import React, { useEffect, useState } from 'react';
+import { Descriptions, Modal } from 'antd';
+import { Document, Page, pdfjs } from 'react-pdf';
+import DelModal from '../../components/DelModal';
+import { httpUrl } from '../../../fetch';
+import { deleteAchieve, getAchieveDetail } from '../api';
+import './index.scss';
+
+pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
+
+export default (props) => {
+ const { match, history, current_user } = props;
+ const id = match.params.achieveId;
+ const [achiveForm, setAchiveForm] = useState({});
+ const [createUserId, setCreateUserId] = useState("");
+ const [visible, setVisible] = useState(false);
+ const [fileType, setFileType] = useState(4);
+ const [fileUrl, setFileUrl] = useState(4);
+ const [numPages, setNumPages] = useState(null);
+
+ useEffect(() => {
+ getAchieveDetail(id).then(data => {
+ setAchiveForm(data);
+ setCreateUserId(data.user.id);
+ })
+ }, [id]);
+
+ function editItem() {
+ props.history.push(`/achieve/achieveEdit/${id}`);
+ }
+
+ function deletItem() {
+ DelModal(() => {
+ deleteAchieve(id).then(res => {
+ if (res.code === '1') {
+ props.showNotification("删除成功");
+ history.go(-1);
+ } else {
+ props.showNotification("删除失败");
+ }
+ })
+ });
+ }
+
+ function downFile(item) {
+ let url = httpUrl + '/busiAttachments/download/' + item.id;
+ window.open(url);
+ }
+
+ function pdfShow(e) {
+ setFileType(e.fileType);
+ if (e.fileType === 1 || e.fileType === 2 || e.fileType === 3) {
+ // pdf||video||img
+ setVisible(true);
+ setFileUrl(httpUrl + '/busiAttachments/view/' + e.id);
+ }
+ }
+
+ function onDocumentLoadSuccess({ numPages }) {
+ setNumPages(numPages);
+ }
+
+ return (
+
+
+
+
{achiveForm.achievementName}
+ {
+ createUserId === current_user.user_id &&
+
编辑 删除
+ }
+
+
+
+ {achiveForm.achievementType && achiveForm.achievementType.dicItemName}
+
+ {
+ achiveForm.achievementLabel && achiveForm.achievementLabel.map(itemx => {
+ return {itemx}
+ })
+ }
+
+ {achiveForm.achievementBelong}
+ {achiveForm.updatedAt}
+ {achiveForm.achievementRemark}
+
+ {
+ achiveForm.achievementAttachments && achiveForm.achievementAttachments.map(item => {
+ return
+
{ pdfShow(item) }}>{item.fileName}
+
{ downFile(item) }}>下载
+
+ })
+ }
+
+
+
+
+
{ setVisible(false) }}
+ className="file-modal"
+ >
+ {
+ fileType === 3 &&
+

+
+ }
+ {
+ fileType === 2 &&
+
+
+ }
+ {
+ fileType === 1 &&
+ {
+
+ {new Array(numPages).fill("").map((item, index) => {
+ return (
+
+ );
+ })}
+
+ }
+
+ }
+
+
+ )
+}
diff --git a/src/achieveRequirement/achieve/achieveDetail/index.scss b/src/achieveRequirement/achieve/achieveDetail/index.scss
new file mode 100644
index 00000000..82956b03
--- /dev/null
+++ b/src/achieveRequirement/achieve/achieveDetail/index.scss
@@ -0,0 +1,52 @@
+// 内容详情
+.item-content {
+ padding: 10px 10px 0 30px;
+}
+.ant-descriptions-item-label {
+ width: 5em;
+ margin-right: 5px;
+ color: rgba(0, 0, 0, 0.65);
+ text-align: right;
+}
+.ant-descriptions-item-content {
+ color: rgba(0, 0, 0, 1);
+}
+.file-list-item > span {
+ vertical-align: top;
+}
+.file-list-box {
+ width: 600px;
+ height: 60px;
+ display: flex;
+ justify-content: space-between;
+ p:hover {
+ cursor: pointer;
+ color: #0089ff;
+ }
+ .downP {
+ color: #0089ff;
+ cursor: pointer;
+ text-decoration: none;
+ }
+}
+
+// 文件预览modal样式
+.file-modal {
+ width: 800px !important;
+ .ant-modal-body {
+ padding-top: 40px;
+ .pdf-box {
+ height: 70vh;
+ overflow-y: scroll;
+ }
+ }
+ .ant-modal-close {
+ top: 0 !important;
+ }
+ .show-img {
+ width: 100%;
+ }
+ .react-pdf__Page__canvas {
+ margin: 0 auto;
+ }
+}
diff --git a/src/achieveRequirement/achieve/achieveEdit/index.jsx b/src/achieveRequirement/achieve/achieveEdit/index.jsx
new file mode 100644
index 00000000..d1c39645
--- /dev/null
+++ b/src/achieveRequirement/achieve/achieveEdit/index.jsx
@@ -0,0 +1,219 @@
+import React, { forwardRef, useCallback, useEffect, useState } from 'react';
+import { Form, Input, Select, Button, } from 'antd';
+import Upload from '../../components/Upload';
+import { httpUrl } from '../../../fetch';
+import { getDictionary, getAchieveDetail, addAchieve, editAchieve } from '../api';
+import './index.scss';
+
+const Option = Select.Option;
+const { TextArea } = Input;
+
+export default Form.create()(
+ forwardRef(({ form, showNotification, match, history }, ref) => {
+
+ const [shareType, setShareType] = useState([]);
+ const [tagType, setTagType] = useState([]);
+ const [fileList, setFileList] = useState(null);
+ const [achievementFiles, setAchievementFiles] = useState(undefined);
+ const [achievementFileTypes, setAchievementFileTypes] = useState(undefined);
+
+ const id = match.params.achieveId;
+ const { getFieldDecorator, validateFields, setFieldsValue } = form;
+
+ useEffect(() => {
+ id && getAchieveDetail(id).then(data => {
+ const formValue = {
+ achievementName: data.achievementName,
+ achievementTypeId: data.achievementTypeId,
+ achievementBelong: data.achievementBelong,
+ achievementLabel: data.achievementLabel,
+ achievementRemark: data.achievementRemark
+ };
+ setFieldsValue(formValue);
+ if (data.achievementAttachments.length) {
+ for (const item of data.achievementAttachments) {
+ item.name = item.fileName;
+ item.size = item.fileSize;
+ item.uid = "rc-upload" + item.id
+ }
+ setFileList(data.achievementAttachments);
+ }
+ })
+ }, [id]);
+
+ // 加载选项数据
+ useEffect(() => {
+ getDictionary('share_type').then((res) => {
+ setShareType(res.data);
+ });
+ getDictionary("tag_type").then((res) => {
+ setTagType(res.data);
+ });
+ }, []);
+
+
+ // 上传附件后得到的文件id数组
+ function UploadFunc(ids, types) {
+ setAchievementFiles(ids);
+ setAchievementFileTypes(types);
+ }
+
+ const helper = useCallback(
+ (label, name, rules, widget) => (
+
+ {getFieldDecorator(name, { rules, validateFirst: true })(widget)}
+
+ ),
+ []
+ );
+
+ const formItemLayout = {
+ labelCol: {
+ xs: { span: 12 },
+ sm: { span: 4 },
+ },
+ wrapperCol: {
+ xs: { span: 12 },
+ sm: { span: 20 },
+ },
+ };
+
+ const tailFormItemLayout = {
+ wrapperCol: {
+ xs: {
+ span: 20,
+ offset: 4,
+ },
+ sm: {
+ span: 20,
+ offset: 4,
+ },
+ },
+ };
+
+ // 修改
+ function editItem() {
+ if (!achievementFiles.length) {
+ showNotification("请上传成果文件!");
+ return;
+ }
+ validateFields((error, values) => {
+ if (!error) {
+ let params = { ...values, achievementFiles, achievementFileTypes, uploadUserId: 1, };
+ params.achievementLabel = params.achievementLabel.join();
+
+ if (id) {
+ // 编辑
+ editAchieve(params).then(res => {
+ if (res.code === '1') {
+ showNotification("成果更新成功!");
+ history.go(-1);
+ } else {
+ showNotification(res.message);
+ }
+ });
+ } else {
+ // 新增
+ addAchieve(params).then(res => {
+ if (res.code === '1') {
+ showNotification("成果更新成功!");
+ history.go(-1);
+ } else {
+ showNotification(res.message);
+ }
+ });
+ }
+ }
+ })
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+ {helper(
+ "标签:",
+ "achievementLabel",
+ [{ required: true, message: "请选择成果标签" }],
+
+ )}
+
+ {helper(
+ "成果描述:",
+ "achievementRemark",
+ [{ required: true, message: "请输入成果描述" }],
+
+ )}
+
+
+
+
+
+
+
+
+
+ )
+ })
+)
diff --git a/src/achieveRequirement/achieve/achieveEdit/index.scss b/src/achieveRequirement/achieve/achieveEdit/index.scss
new file mode 100644
index 00000000..ad5c9622
--- /dev/null
+++ b/src/achieveRequirement/achieve/achieveEdit/index.scss
@@ -0,0 +1,4 @@
+ .achieve-form{
+ padding:24px 40px 0px 40px;
+ }
+
diff --git a/src/achieveRequirement/achieve/achieveList/index.jsx b/src/achieveRequirement/achieve/achieveList/index.jsx
new file mode 100644
index 00000000..661d6861
--- /dev/null
+++ b/src/achieveRequirement/achieve/achieveList/index.jsx
@@ -0,0 +1,147 @@
+import React, { useEffect, useState } from 'react';
+import classNames from 'classnames';
+import { Pagination, Button, Icon } from 'antd';
+import ChooseNav from '../../components/chooseNav';
+import ItemList from '../../components/itemList';
+import { getDictionary, getAchieveList } from '../api';
+
+export default (props) => {
+ const { current_user } = props;
+ // console.log(current_user);
+
+ const [achievementTypeId, setAchievementTypeId] = useState(''); //成果类别
+ const [achievementFileTypes, setAchievementFileTypes] = useState(''); //文件类型
+ const [orderBy, setOrderBy] = useState('-1');
+ const [curPage, setCurPage] = useState(1);
+ const [total, setTotal] = useState(0);
+ const [industryOption, setIndustryOption] = useState([]);
+ const [achiveList, setAchiveList] = useState([]);
+
+ useEffect(() => {
+ getDictionary('share_type').then(res => {
+ if (res && res.data) setIndustryOption(res.data);
+ });
+ }, []);
+
+ useEffect(() => {
+ const params = {
+ achievementTypeId,
+ achievementFileTypes,
+ achievementLabel: '',
+ isOwned: '',
+ orderBy,
+ curPage,
+ pageSize: 10
+ };
+ getAchieveList(params).then(data => {
+ setAchiveList(data.rows);
+ setTotal(data.total);
+ })
+ }, [achievementTypeId, achievementFileTypes, orderBy, curPage]);
+
+
+ function changeOptionId(id, type) {
+ if (type === 'share_type') {
+ setAchievementTypeId(id);
+ setCurPage(1);
+ } else if (type === 'file_type') {
+ setAchievementFileTypes(id);
+ setCurPage(1);
+ }
+ }
+
+ function changeSort(sortType) {
+ setOrderBy(sortType);
+ setCurPage(1);
+ }
+
+ function goAchieveMine() {
+ props.history.push('/achieve/achieveMine');
+ }
+ function goAdd() {
+ props.history.push("/achieve/achieveAdd");
+ }
+
+ function achiveClick(id) {
+ props.history.push(`/achieve/achieveDetail/${id}`);
+ }
+
+ const filetypeOption = [
+ {
+ dicItemName: '文件',
+ id: 1
+ },
+ {
+ dicItemName: '视频',
+ id: 2
+ },
+ {
+ dicItemName: '图片',
+ id: 3
+ },
+ {
+ dicItemName: '软件',
+ id: 4
+ },
+ {
+ dicItemName: '其它',
+ id: 5
+ },
+ ];
+
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
{ changeSort('-1') }}>默认
+
{ changeSort('created_at') }}>最新
+
{ changeSort('view_count') }}>最热
+
+ {
+ current_user.login &&
+
+
+
+
+ }
+
+
+
+
+
+
+
+
{ setCurPage(page) }}
+ current={curPage}
+ total={total}
+ />
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/achieveRequirement/achieve/achieveMine/index.jsx b/src/achieveRequirement/achieve/achieveMine/index.jsx
new file mode 100644
index 00000000..b7cf4734
--- /dev/null
+++ b/src/achieveRequirement/achieve/achieveMine/index.jsx
@@ -0,0 +1,81 @@
+import React, { useEffect, useState } from 'react';
+import { Pagination } from 'antd';
+import ItemList from '../../components/itemList';
+import DelModal from '../../components/DelModal';
+import { deleteAchieve, getAchieveList } from '../api';
+
+export default (props) => {
+ const [achiveList, setAchiveList] = useState([]);
+ const [curPage, setCurPage] = useState(1);
+ const [total, setTotal] = useState(0);
+
+ useEffect(() => {
+ getAcheiveListOwn();
+ }, [curPage]);
+
+ function getAcheiveListOwn() {
+ const params = {
+ achievementTypeId: '',
+ achievementFileTypes: '',
+ achievementLabel: '',
+ // isOwned: '', //方便游客身份时显示数据
+ isOwned: true,
+ orderBy: -1,
+ curPage,
+ pageSize: 10
+ };
+ getAchieveList(params).then(data => {
+ setAchiveList(data.rows);
+ setTotal(data.total);
+ })
+ }
+
+ function achiveClick(id) {
+ props.history.push(`/achieve/achieveDetail/${id}`);
+ }
+
+ function editItem(id) {
+ props.history.push(`/achieve/achieveEdit/${id}`);
+ }
+
+ function deletItem(id) {
+ DelModal(() => {
+ deleteAchieve(id).then(res => {
+ if (res.code === '1') {
+ props.showNotification("删除成功");
+ getAcheiveListOwn();
+ } else {
+ props.showNotification("删除失败");
+ }
+ })
+ });
+ }
+
+ return (
+
+
+
+
+
{ setCurPage(page) }}
+ current={curPage}
+ total={total}
+ />
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/achieveRequirement/achieve/api.js b/src/achieveRequirement/achieve/api.js
new file mode 100644
index 00000000..75676ca5
--- /dev/null
+++ b/src/achieveRequirement/achieve/api.js
@@ -0,0 +1,62 @@
+import fetch from '../../fetch';
+
+// 获取字典分类列表
+export function getDictionary(id) {
+ return fetch({
+ url: '/dicItem/getData?dicTypeCode=' + id,
+ method: 'get'
+ });
+}
+
+// 成果列表查询
+export async function getAchieveList(params) {
+ let res = await fetch({
+ url: '/api/achievement/',
+ method: 'get',
+ params,
+ })
+ if (res && res.data) {
+ for (const i of res.data.rows) {
+ // 处理分解标签
+ i.achievementLabel = i.achievementLabel.split(',');
+ }
+ }
+ return res.data;
+}
+
+//新增成果
+export function addAchieve(data) {
+ return fetch({
+ url: '/api/achievement/',
+ method: 'post',
+ data: data
+ });
+}
+
+//删除成果
+export function deleteAchieve(id) {
+ return fetch({
+ url: '/api/achievement/' + id,
+ method: 'DELETE',
+ });
+}
+
+//更新成果
+export function editAchieve(data) {
+ return fetch({
+ url: '/api/achievement/',
+ method: 'put',
+ data: data
+ });
+}
+
+// 成果详情查询
+export async function getAchieveDetail(id) {
+ let res = await fetch({
+ url: '/api/achievement/' + id,
+ method: 'get',
+ });
+ res.data.achievementLabel = res.data.achievementLabel.split(',');
+ return res.data;
+}
+
diff --git a/src/achieveRequirement/components/DelModal.jsx b/src/achieveRequirement/components/DelModal.jsx
new file mode 100644
index 00000000..b2ba4434
--- /dev/null
+++ b/src/achieveRequirement/components/DelModal.jsx
@@ -0,0 +1,20 @@
+import { Modal } from 'antd';
+
+export default (
+ handleOk,
+ title,
+ content,
+ handleCancel) => {
+ return Modal.confirm({
+ title: title || "警告",
+ content: content || "确认删除?",
+ okText: '确定',
+ cancelText: '取消',
+ onOk() {
+ handleOk && handleOk();
+ },
+ onCancel() {
+ handleCancel && handleCancel();
+ }
+ });
+}
\ No newline at end of file
diff --git a/src/achieveRequirement/components/Upload.jsx b/src/achieveRequirement/components/Upload.jsx
new file mode 100644
index 00000000..d9992ac9
--- /dev/null
+++ b/src/achieveRequirement/components/Upload.jsx
@@ -0,0 +1,93 @@
+import React, { useEffect, useState } from "react";
+import { Upload, Button } from 'antd';
+import axios from 'axios';
+import { appendFileSizeToUploadFileAll } from 'educoder';
+axios.defaults.withCredentials = true;
+
+function Uploads({ className, size, actionUrl, fileList, showNotification, load }) {
+ const [files, setFiles] = useState(undefined);
+
+ useEffect(() => {
+ if (fileList) {
+ init();
+ }
+ }, [fileList]);
+
+ function init() {
+ let f = appendFileSizeToUploadFileAll(fileList);
+ setFiles(f);
+ }
+ function onAttachmentRemove(file) {
+ if (!file.percent || file.percent === 100) {
+ deleteAttachment(file);
+ return false;
+ }
+ }
+ function deleteAttachment(file) {
+ let id = (file.response && file.response.data && file.response.data.id) || file.id;
+
+ // 暂时不直接删除上传的文件,只在最后保存的时候进行修改
+ let nf = files.filter(item => (item.response && item.response.data && item.response.data.id !== id) && (item.id !== id));
+ setFiles(nf);
+ fileIdList(nf);
+
+ // 发送服务到后台删除文件,同步更新数据
+ // const url = actionUrl + `/busiAttachments/${id}`;
+ // axios.delete(url).then((response) => {
+ // if (response.data) {
+ // if (response.data.code === "1") {
+ // let nf = files.filter(item => (item.response.data.id !== id) && (item.id !== id));
+ // setFiles(nf);
+ // fileIdList(nf);
+ // } else {
+ // showNotification(response.data.message);
+ // }
+ // }
+ // }).catch(function (error) {
+ // console.log(error);
+ // });
+ }
+
+
+ function handleChange(info) {
+ if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
+ let fileList = info.fileList;
+ setFiles(appendFileSizeToUploadFileAll(fileList));
+ if (info.file.status === 'done' || info.file.status === 'removed') fileIdList(fileList);
+ }
+ }
+
+ function fileIdList(fileList) {
+ let id = [];
+ let fileType = [];
+ for (const item of fileList) {
+ id.push(item.id || item.response.data.id);
+ fileType.push(item.fileType || item.response.data.fileType);
+ }
+ load && load(id.join(), fileType.join());
+ }
+
+ function beforeUpload(file) {
+ const isLt100M = file.size / 1024 / 1024 < size;
+ if (!isLt100M) {
+ showNotification(`文件大小必须小于${size}MB!`);
+ }
+ return isLt100M;
+ }
+
+ const upload = {
+ name: 'file',
+ fileList: files,
+ action: actionUrl + `/busiAttachments/upload`,
+ onChange: handleChange,
+ onRemove: onAttachmentRemove,
+ beforeUpload: beforeUpload,
+ };
+ return (
+
+
+ (你可以上传小于{size}MB的文件)
+
+ )
+}
+export default Uploads;
\ No newline at end of file
diff --git a/src/achieveRequirement/components/chooseNav/index.jsx b/src/achieveRequirement/components/chooseNav/index.jsx
new file mode 100644
index 00000000..1f887c90
--- /dev/null
+++ b/src/achieveRequirement/components/chooseNav/index.jsx
@@ -0,0 +1,29 @@
+import React, { useEffect, useState } from 'react';
+import classNames from 'classnames';
+import './index.scss';
+
+export default (props) => {
+ const {title, options,changeOptionId ,type} = props;
+
+ const [id, setId] = useState("");
+
+ useEffect(()=>{
+ changeOptionId(id,type);
+ },[id])
+
+ return (
+
+
+
{title}
+
+
{setId("")}}>全部
+ {
+ options.map((item) => {
+ return
{setId(item.id)}} >{item.dicItemName}
+ })
+ }
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/achieveRequirement/components/chooseNav/index.scss b/src/achieveRequirement/components/chooseNav/index.scss
new file mode 100644
index 00000000..069eba3f
--- /dev/null
+++ b/src/achieveRequirement/components/chooseNav/index.scss
@@ -0,0 +1,41 @@
+.nav-content {
+ margin-top:20px;
+ background: #fff;
+ border-bottom: 1px solid #eaeaea;
+ box-shadow: #ddd 0px 0px 5px;
+}
+.shop-box {
+ width: 1280px;
+ border-top: 1px solid #eaeaea;
+ display: flex;
+ justify-content: start;
+}
+.choose-title {
+ width: 110px;
+ background: #f3f3f3;
+ text-align: center;
+ padding: 16px 0;
+ font-size: 14px;
+ color: #666;
+ font-weight: 600;
+}
+.choose-list {
+ width: 1170px;
+ padding: 16px 0;
+ display: flex;
+ justify-content: start;
+}
+.choose-item {
+ font-size: 14px;
+ text-align: center;
+ padding: 0 15px ;
+ cursor: pointer;
+ &:hover{
+ color: #0072ff;
+ }
+}
+
+.choose-item-checked {
+ color: #0072ff;
+}
+
diff --git a/src/achieveRequirement/components/itemList/index.jsx b/src/achieveRequirement/components/itemList/index.jsx
new file mode 100644
index 00000000..c61a2ec6
--- /dev/null
+++ b/src/achieveRequirement/components/itemList/index.jsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import './index.scss';
+
+export default (props) => {
+ const { list, itemClick, editItem, deletItem } = props;
+
+ return (
+ list.map(item => {
+ return (
+
+
{ itemClick(item.id) }}>
+ {item.achievementName}
+
+
+ {
+ item.achievementLabel.map(i => {
+ return {i};
+ })
+ }
+
+
+ {item.user &&
发布者:{item.user.nickname ? item.user.nickname : item.user.login}
}
+
发布时间:{item.achievementPublishTime}
+
+ {editItem &&
+
+
+
}
+
+ )
+ })
+ )
+}
\ No newline at end of file
diff --git a/src/achieveRequirement/components/itemList/index.scss b/src/achieveRequirement/components/itemList/index.scss
new file mode 100644
index 00000000..9694c84e
--- /dev/null
+++ b/src/achieveRequirement/components/itemList/index.scss
@@ -0,0 +1,69 @@
+.list-box {
+ position: relative;
+ padding: 20px;
+ background: #fff;
+ border-bottom: 1px solid #dedede;
+}
+.list-title {
+ font-size: 17px;
+ color: #000;
+ cursor: pointer;
+}
+.list-title p {
+ display: inline-block;
+}
+.list-title p:hover {
+ color: #409eff;
+}
+.list-title span {
+ padding: 3px 5px;
+ background: #f8c753;
+ font-size: 13px;
+ color: #fff;
+ border-radius: 3px;
+}
+span.list-done {
+ background: #35d77e;
+}
+
+.list-tag {
+ display: inline-block;
+ margin: 5px 10px 5px 0;
+ background: #cfe9ff;
+ color: #0089ff;
+ font-size: 14px;
+ border-radius: 3px;
+ padding: 2px 5px;
+}
+.list-other > p {
+ display: inline-block;
+ margin: 0 10px 0 0;
+ font-size: 12px;
+ color: #666;
+}
+
+.list-box-action {
+ position: absolute;
+ display: flex;
+ align-items: center;
+ top: 0;
+ bottom: 0;
+ right: 100px;
+ visibility: hidden;
+}
+.list-box:hover .list-box-action {
+ visibility: visible;
+}
+.list-box-action button {
+ display: inline-block;
+ margin: 0 25px;
+ width: 50px;
+ height: 50px;
+ color: #0089ff;
+ line-height: 50px;
+ text-align: center;
+ border: 0;
+ border-radius: 25px;
+ box-shadow: 2px 2px 5px 2px #eee;
+ cursor: pointer;
+}
diff --git a/src/achieveRequirement/index.js b/src/achieveRequirement/index.js
new file mode 100644
index 00000000..62df9d32
--- /dev/null
+++ b/src/achieveRequirement/index.js
@@ -0,0 +1,90 @@
+import React, { Component } from "react";
+
+import { Route, Switch } from "react-router-dom";
+import { withRouter } from "react-router";
+import { SnackbarHOC } from "educoder";
+import { CNotificationHOC } from "../modules/courses/common/CNotificationHOC";
+import { TPMIndexHOC } from "../modules/tpm/TPMIndexHOC";
+import Loadable from "react-loadable";
+import Loading from "../Loading";
+import { ImageLayerOfCommentHOC } from "../modules/page/layers/ImageLayerOfCommentHOC";
+import './index.scss';
+
+const AchieveIndex = Loadable({
+ loader: () => import("./achieve/achieveList"),
+ loading: Loading,
+});
+const AchieveDetail = Loadable({
+ loader: () => import("./achieve/achieveDetail"),
+ loading: Loading,
+});
+const AchieveMine = Loadable({
+ loader: () => import("./achieve/achieveMine"),
+ loading: Loading,
+});
+const AchieveAdd = Loadable({
+ loader: () => import("./achieve/achieveEdit"),
+ loading: Loading,
+});
+const AchieveEdit = Loadable({
+ loader: () => import("./achieve/achieveEdit"),
+ loading: Loading,
+});
+
+class Index extends Component {
+ render() {
+ return (
+
+
+ (
+
+ )}
+ >
+ (
+
+ )}
+ >
+ (
+
+ )}
+ >
+ (
+
+ )}
+ >
+ (
+
+ )}
+ >
+
+ {/* (
+ this.props.current_user && this.props.current_user.login ?
+
+ :
+
+ )}
+ > */}
+
+
+ );
+ }
+}
+export default withRouter(
+ ImageLayerOfCommentHOC({
+ imgSelector: ".imageLayerParent img, .imageLayerParent .imageTarget",
+ parentSelector: ".newMain",
+ })(CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(Index))))
+);
diff --git a/src/achieveRequirement/index.scss b/src/achieveRequirement/index.scss
new file mode 100644
index 00000000..4525d37a
--- /dev/null
+++ b/src/achieveRequirement/index.scss
@@ -0,0 +1,73 @@
+// 本模块公共样式
+
+.centerbox {
+ width: 1280px;
+ margin:40px auto;
+}
+.center-content {
+ width: 1280px;
+ margin: 20px 0;
+ background: #fff;
+ border: 1px solid #dedede;
+ box-shadow: #eee 0px 1px 1px 3px;
+}
+.centerScreen {
+ display: flex;
+ justify-content: space-between;
+ height: 46px;
+ background-color: #fff;
+ border-bottom: 1px solid #dedede;
+}
+
+// 内容标题左侧样式
+.center-left-but {
+ display: flex;
+ justify-content: start;
+ align-items: center;
+ margin-left: 20px;
+ font-size: 16px;
+ font-weight: 600;
+}
+.center-left-butD {
+ height: 24px;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 24px;
+ color: #333;
+ padding: 0 10px;
+ background-color: #fff;
+ border: 1px solid #dedede;
+}
+.center-left-butD:hover {
+ background-color: #fff;
+ color: #409eff;
+ cursor: pointer;
+}
+.center-left-butDACT {
+ background-color: #409eff;
+ color: #fff;
+}
+
+// 内容标题右侧样式
+.center-right-but {
+ display: flex;
+ align-items: center;
+}
+.center-right-but>span{
+ margin: 0 10px;
+ font-size: 16px;
+ color: #0089ff;
+ cursor: pointer;
+}
+
+// 通用标签样式
+.list-tag{
+ display: inline-block;
+ margin-right: 10px;
+ background: #cfe9ff;
+ color: #0089ff;
+ font-size: 14px;
+ border-radius:3px ;
+ padding: 2px 5px;
+ line-height: 25px;
+}
diff --git a/src/fetch.js b/src/fetch.js
new file mode 100644
index 00000000..9193635d
--- /dev/null
+++ b/src/fetch.js
@@ -0,0 +1,86 @@
+import axios from 'axios';
+import cookie from 'react-cookies';
+
+export const httpUrl='http://117.50.100.12:8001';
+
+const TokenKey = 'autologin_forge_military';
+axios.defaults.withCredentials = true;
+
+// 创建axios实例
+const service = axios.create({
+ // baseURL: process.env.BASE_API, // api的base_url
+ baseURL:httpUrl,
+ timeout: 5000 // 请求超时时间
+ // headers:{'X-Custom-Header': 'foobar'} // 请求头
+});
+
+
+// request拦截器
+service.interceptors.request.use(config => {
+ if (cookie.load(TokenKey)) {
+ config.headers['Authorization'] = cookie.load(TokenKey); // 让每个请求携带自定义token 请根据实际情况自行修改
+ // config.headers['Authorization'] = 'autologin_forge_military=' + cookie.load(TokenKey); // 让每个请求携带自定义token 请根据实际情况自行修改
+ }
+ return config;
+}, error => {
+ // Do something with request error
+ console.log(error); // for debug
+ Promise.reject(error);
+});
+// respone拦截器
+service.interceptors.response.use(
+
+ response => {
+ /**
+ * 下面的注释为通过response自定义code来标示请求状态,当code返回如下情况为权限有问题,登出并返回到登录页
+ * 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中
+ */
+ const res = response;
+ if (res.status === 400) {
+ // Message({
+ // message: '请求错误',
+ // type: 'error',
+ // duration: 5 * 1000
+ // });
+ // eslint-disable-next-line prefer-promise-reject-errors
+ return Promise.reject('error');
+ }
+ if (res.status === 401) {
+ // Message({
+ // message: '未授权,请登录!',
+ // type: 'error',
+ // duration: 5 * 1000
+ // });
+ // eslint-disable-next-line prefer-promise-reject-errors
+ return Promise.reject('error');
+ }
+ if (res.status === 40001) {
+ // Message({
+ // message: '账户或密码错误!',
+ // type: 'warning'
+ // });
+ // eslint-disable-next-line prefer-promise-reject-errors
+ return Promise.reject('error');
+ }
+ if (response.status !== 200 && res.status !== 200) {
+ // Message({
+ // message: res.message,
+ // type: 'error',
+ // duration: 7 * 1000
+ // });
+ } else {
+ return response.data;
+ }
+ },
+ error => {
+ if (error.response&& error.response.status === 401 && !error.config.url.includes('/auth-server/oauth/token')) {
+ return cookie.remove(TokenKey);
+ // that.$router.push('/Login');
+ } else {
+ // that.$router.push('/Login');
+ }
+ return Promise.reject(error);
+ }
+);
+
+export default service;
diff --git a/src/forge/Source/Index.jsx b/src/forge/Source/Index.jsx
index 4e85a68b..51bcdece 100644
--- a/src/forge/Source/Index.jsx
+++ b/src/forge/Source/Index.jsx
@@ -44,6 +44,7 @@ function Index(props){
function getData(){
const url = https +`/api/project/achievement/`;
+ axios.defaults.withCredentials = true;
axios.get(url,{
params:{
projectId:repo_id,