diff --git a/public/css/gitlink.min.css b/public/css/gitlink.min.css index 640041452..e7342cb7e 100644 --- a/public/css/gitlink.min.css +++ b/public/css/gitlink.min.css @@ -1818,6 +1818,10 @@ a.decoration { margin-left: 85px } +.ml100 { + margin-left: 100px +} + .ml180 { margin-left: 180px } diff --git a/src/AppConfig.js b/src/AppConfig.js index 2a225d262..59ce12314 100644 --- a/src/AppConfig.js +++ b/src/AppConfig.js @@ -3,7 +3,7 @@ import { requestProxy } from "./indexEduplus2RequestProxy"; import { broadcastChannelOnmessage, isDev, queryString } from 'educoder'; import { notification } from 'antd'; -import './index.css'; +import './index.scss'; let message501 = false; broadcastChannelOnmessage('refreshPage', () => { diff --git a/src/forge/Component/feedBack.jsx b/src/forge/Component/feedBack.jsx index 0bd2aecdf..57a8a097e 100644 --- a/src/forge/Component/feedBack.jsx +++ b/src/forge/Component/feedBack.jsx @@ -86,14 +86,7 @@ function SiderBar(props) { // 意见反馈按钮点击事件 function feedBackClick(){ - if(login){ - if(forumsDoMain){ - return window.location.href=`${forumsDoMain}/forums/new`; - } - setFeedBackModal(true); - }else{ - history.push(forumsDoMain ? `/login?go_page=${forumsDoMain}/forums/new` : `/login`); - } + history.push(login ? `/forums/add?sec=4,8` : `/login?go_page=/forums/add?sec=4,8`); } function applyFeedBack(){ diff --git a/src/forge/Team/Component/UploadImage.jsx b/src/forge/Team/Component/UploadImage.jsx index 8db17f100..d4ecd2803 100644 --- a/src/forge/Team/Component/UploadImage.jsx +++ b/src/forge/Team/Component/UploadImage.jsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { Upload , Icon , message } from "antd"; +import { Upload , Icon , message, Modal } from "antd"; import { getUploadActionUrl } from 'educoder'; import cookie from 'react-cookies'; @@ -8,8 +8,12 @@ const TokenKey = 'autologin_trustie'; function UploadImage({ getImage , url, getImageId, maxSize = 2, action = getUploadActionUrl(), getImageUrl }){ const [ imageUrl , setImageUrl ] = useState(undefined); const [loading, setLoading] = useState(false); + const [previewVisible, setPreviewVisible] = useState(false); + const [fileList, setFileList] = useState([]); + useEffect(()=>{ setImageUrl(url); + url && setFileList([{ uid: '-1', url: url }]) },[url]) function getBase64(img, callback) { @@ -35,6 +39,7 @@ function UploadImage({ getImage , url, getImageId, maxSize = 2, action = getUplo } // 上传完成后 function handleChange(info){ + setFileList([info.file]); if(info && info.file && info.file.status === "done"){ setLoading(false); getImageId && getImageId(info.file.response.id); @@ -42,30 +47,54 @@ function UploadImage({ getImage , url, getImageId, maxSize = 2, action = getUplo getBase64(info.file.originFileObj, imageUrl => setImageUrl(imageUrl) ); + + }else if(info && info.file && info.file.status === "removed"){ + setLoading(false); + getImageId && getImageId(undefined); + getImageUrl && getImageUrl(undefined); + setImageUrl(undefined); + setFileList([]); } } + + const handlePreview = async (file) => { + setPreviewVisible(true); + }; + + const handleRemove = (file) => { + setImageUrl(undefined); + setFileList([]); + }; return( - - { - imageUrl ? - avatar - : -
- -
点击上传
-
- } -
+
+ + { + fileList.length >= 1 ? + null + : +
+ +
点击上传
+
+ } +
+ setPreviewVisible(false)}> + example + +
) } export default UploadImage; \ No newline at end of file diff --git a/src/forums/api.js b/src/forums/api.js index b9c76d818..b49e7ce5a 100644 --- a/src/forums/api.js +++ b/src/forums/api.js @@ -46,6 +46,7 @@ export function postReply(id,data) { }); } +// 也可以用于删除帖子 export function deleteReply(id) { return fetch({ url: `/api/memos/${id}.json`, @@ -68,4 +69,58 @@ export function setWatch(id,isWatch) { is_watch:isWatch, }, }); +} + +export async function getForumSectionList(params) { + return fetch({ + url: `/api/forum_sections.json`, + method: 'get', + params + }); +} + +export function createMemo(data) { + return fetch({ + url: `/api/memos.json`, + method: 'post', + data + }); +} +export function updateMemo(data) { + return fetch({ + url: `/api/memos/${data.id}.json`, + method: 'put', + data + }); +} +export async function getMemoByEditor(id) { + return fetch({ + url: `/api/memos/${id}/edit.json`, + method: 'get', + }); +} + +export async function setMemoTopStatus(id, sticky) { + return fetch({ + url: `/api/memos/${id}/set_top_or_down.json?sticky=${sticky}`, + method: 'get', + }); +} + +export async function setMemoFineStatus(id, is_fine) { + return fetch({ + url: `/api/memos/${id}/is_fine.json`, + method: 'POST', + data: {is_fine} + }); +} + +export function deleteUserForums(id,is_apply) { + return fetch({ + url: `/api/memos/${id}/confirm_delete.json`, + method: 'post', + data:{ + is_apply + } + }); } \ No newline at end of file diff --git a/src/forums/comments/list.jsx b/src/forums/comments/list.jsx index fb8448b18..6c94263bc 100644 --- a/src/forums/comments/list.jsx +++ b/src/forums/comments/list.jsx @@ -12,8 +12,6 @@ import RenderHtml from '../../components/render-html'; import { deleteReply, getReplyList } from '../api'; function IssueCommentList(props){ - console.log('props', props); - const{docId, history: {location}, reload, reloadComment, showNotification, current_user:{login, admin, image_url, username}, isManager, showLoginDialog, author} = props; const {owner, projectsId, index} = props.match.params; const [category, setCategory] = useState('comment'); @@ -57,8 +55,6 @@ function IssueCommentList(props){ page: page } getReplyList(docId, params).then(res=>{ - console.log('res', res); - if(res){ const {memo_replies, memos_count, total_count} = res; // if(category === 'all'){ @@ -90,7 +86,7 @@ function IssueCommentList(props){ setSpin(false) } }) - }, [reload, category, page]) + }, [reload, category, page, docId]) function commentCtx(v){ return ; @@ -112,7 +108,7 @@ function IssueCommentList(props){ } return( - (isPhone() && journalsCount === 0) ? '' :
+ (isPhone() && journalsCount === 0) ? '' :
{/* 评论 */}
0 && 'pb15'}`}>
diff --git a/src/forums/detail/index.jsx b/src/forums/detail/index.jsx index 743e51c85..20d90eaab 100644 --- a/src/forums/detail/index.jsx +++ b/src/forums/detail/index.jsx @@ -1,7 +1,7 @@ -import React, { useEffect, useState } from "react"; +import React, { Fragment, useEffect, useState } from "react"; import './index.scss'; -import { getForumMemoById, postPlus, setWatch } from "../api"; -import { Divider, message, Tag } from "antd"; +import { deleteReply, deleteUserForums, getForumMemoById, postPlus, setMemoFineStatus, setMemoTopStatus, setWatch } from "../api"; +import { Button, Divider, Dropdown, message, Modal, Tag } from "antd"; import icon12 from '../image/icon12.png'; import icon6 from '../image/icon6.png'; import icon10 from '../image/icon10.png'; @@ -12,11 +12,13 @@ import CommentList from '../comments/list' import { httpUrl } from "../fetch"; function detail(props) { - const { match: { params: { id } }, current_user } = props; + const { match: { params: { id } }, current_user, history } = props; const [reload, setReload] = useState(undefined); const [detail, setDetail] = useState(undefined); const [commentReload, setCommentReload] = useState(undefined); - const { memo = {}, author_info = {}, recent_memos=[], memo_image_info } = detail || {}; + const [visible, setVisible] = useState(false); + const { memo = {}, author_info = {}, recent_memos=[], memo_image_info, banned_permission=false } = detail || {}; + const {apply_destroy=false} = memo || {}; useEffect(() => { id && getForumMemoById(id).then(res => { @@ -56,7 +58,7 @@ function detail(props) { ] function actionClick(item){ - const {memoKey} = item; + const {memoKey, id} = item; if(memoKey === "user_praise"){ postPlus({ container_type: "Memo", @@ -75,9 +77,21 @@ function detail(props) { setReload(Math.random()); } }) + }else if(id === "reply"){ + // 点击按钮,页面滚动到评论区,并且打开新增评论编辑框 + const commentsSection = document.getElementById('commentListBox_forums'); + if (commentsSection) { + commentsSection.scrollIntoView({ behavior: 'smooth' }); + } } } + const tipByModal = { + 1: ["确定删除该帖子吗?", "删除后将无法恢复,请谨慎操作!", "删除帖子"], + 2: ["确定申请删除该帖子吗?", "申请后请耐心等待管理员审核,删除后将无法恢复,请谨慎操作!", "申请删除帖子"], + 3: ["确定撤销删除申请吗?", "撤销后该帖子将恢复正常!", "撤销删除申请"] + } + return (
@@ -88,7 +102,7 @@ function detail(props) { {/* 左侧操作区 */}
{actions.map((item, index) => ( - {actionClick(item)}}> + {actionClick(item)}}> {item.icon ? : } @@ -102,11 +116,55 @@ function detail(props) {
{memo.is_original && 原创} -
- 【{memo.tag_name}】 - {memo.subject} - {memo.sticky && 置顶} - {memo.is_fine && 精华} +
+
+ 【{memo.tag_name}】 + {memo.subject} + {memo.sticky && 置顶} + {memo.is_fine && 精华} +
+ {(memo.is_current_user||banned_permission)&& trigger.parentNode} + overlay={ + + } + > + + }
@@ -168,6 +226,37 @@ function detail(props) {
+ {!!visible && {setVisible(false)}} + onOk={()=>{ + if(visible === 1){ + deleteReply(id).then(res=>{ + if(res && !res.status){ + setVisible(false); + message.success('删除成功'); + history.push(`/forums`) + } + }) + }else{ + deleteUserForums(id, apply_destroy ? 0 : 1).then(res=>{ + if(res && res.status === 1){ + setVisible(false); + message.success(res.message); + setReload(Math.random()); + } + }) + } + }} + className="purpleGradient" + > +
{tipByModal[visible][0]}
+

{tipByModal[visible][1]}

+
}
) } diff --git a/src/forums/detail/index.scss b/src/forums/detail/index.scss index 5168e90e0..0f53b94d1 100644 --- a/src/forums/detail/index.scss +++ b/src/forums/detail/index.scss @@ -94,6 +94,12 @@ img:not(.forums-detail-content-headImg){ max-width: 100%; } + .forumsDetail_edit_dropdown{ + border-radius: 15px; + padding: 0 14px; + height: 30px; + line-height: 29px; + } } .forums-detail-right{ width: 21%; @@ -131,6 +137,20 @@ background-size: 100% 100%; padding: 14px 25px; } +.forumsDetail_edit_dropdownList{ + background: #fff; + border-radius: 6px; + box-shadow: 0 4px 8px rgba(65, 84, 241, .1); + padding: 10px 5px; + width: 100px; + a{ + text-align: center; + display: block; + padding: 6px 0px; + font-size: 15px; + opacity: 0.8; + } +} @keyframes gifEmoji { from { diff --git a/src/forums/edit/index.jsx b/src/forums/edit/index.jsx new file mode 100644 index 000000000..aea7ebd1c --- /dev/null +++ b/src/forums/edit/index.jsx @@ -0,0 +1,228 @@ +import React, { useState, useEffect } from "react"; +import { createMemo, getForumSectionList, getMemoByEditor, updateMemo } from "../api"; +import './index.scss'; +import { Divider, Form, Input, Cascader, Select, Breadcrumb, Upload, Button, Icon, Radio, message } from "antd"; +import RichEditor from '../../forge/Component/RichEditor'; +import UploadImage from '../../forge/Team/Component/UploadImage'; +import { httpUrl } from "../fetch"; +const { Option } = Select; + +function EditForum(props) { + const { match, form, history, location:{ search } } = props; + const { id } = match.params; + const { getFieldDecorator, validateFields, setFieldsValue, getFieldsValue } = form; + const searchParams = new URLSearchParams(search.substring(1)); + const sec = searchParams.get("sec"); + const [sectionList, setSectionList] = useState([]); + const [fileList, setFileList] = useState(undefined); + const [detail, setDetail] = useState(undefined); + const { is_original } = getFieldsValue(); + + useEffect(() => { + getForumSectionList().then(res => { + const processedSections = res.forum_sections.map(item => { + const section = { + value: item.id, + label: item.name, + }; + if (item.children_tags && !!item.children_tags.length) { + section.children = item.children_tags.map(ele => ({ + value: ele.id, + label: ele.title, + })); + } + return section; + }); + setSectionList(processedSections); + if(!!sec && !id){ + setFieldsValue({ + // 转成int数组 + forumList: sec.split(",").map(item => parseInt(item)) + }) + } + }) + }, []) + + useEffect(() => { + if (id) { + getMemoByEditor(id).then(res => { + setDetail(res) + const {subject, forum_section: {forum_id}, children_forum_section: {children_forum_id}, attachments_url=[]} = res; + document.title = `编辑-${subject}`; + const files = attachments_url.map(item=>{ + return { + name: item.title, + uid: item.id, + id: item.id + } + }) + setFieldsValue({ + ...res, + forumList:[forum_id,children_forum_id], + is_original: res.is_original? 1 : 0, + attachments: {fileList: files} + }) + setFileList(files) + }) + } else { + document.title = `新增-论坛交流`; + setFieldsValue({ + is_original: 1 + }) + } + }, []) + + function beforeUpload(file) { + const isLt100M = file.size / 1024 / 1024 < 100; + if (!isLt100M) { + message.error(`文件大小必须小于${100}MB!`); + } + return isLt100M; + } + + function changeFileList(info) { + if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { + const fileList = info.fileList || []; + setFileList(fileList); + } + } + + const submitForm = () => { + validateFields((error, values) => { + if (!error) { + const {attachments} = values; + const attachmentIdArray = attachments && attachments.fileList.map(item => item.response && item.response.id ? item.response.id : item.id ? item.id : item) + const params = { + attachment_id: values.attachment_id, + attachments: attachmentIdArray, + children_forum_id: values.forumList[1] || '0', + forum_id: values.forumList[0], + id, + memo: { + content: values.content, + is_original: values.is_original, + reprint_link: values.reprint_link, + subject: values.subject, + tag_id: values.tag_id, + }, + } + if (id) { + updateMemo(params).then(ret => { + message.success(ret.message); + if(ret.status === 1){ + history.push("/forums/" + id); + } + }) + }else { + createMemo(params).then(ret => { + message.success(ret.message); + if(ret.status === 1){ + history.push("/forums/" + ret.memo_id); + } + }) + } + + } + }) + + } + + const layout = { + labelCol: { span: 2 }, + wrapperCol: { span: 14 }, + }; + + return
+
+ + 论坛交流 + {id && detail && {detail.subject}} + {id ? '编辑' : '新建'}帖子 + +
+
+

{id ? `编辑` : `新增`}

+ +
+ + {getFieldDecorator("subject", { + rules: [{ required: true, message: "请输入标题" }] + })( + + )} + + + {getFieldDecorator("forumList", { + rules: [{ required: true, message: "请选择主题板块" }] + })( + + )} + + + {getFieldDecorator("tag_id", { + rules: [{ required: true, message: "请选择帖子标签" }] + })( + + )} + + + {getFieldDecorator("is_original", { + rules: [{ required: true, message: "请选择是否原创" }] + })( + + + + + )} + + {!is_original && + {getFieldDecorator("reprint_link")( + + )} + } + + {getFieldDecorator("content", { + rules: [{ required: true, message: "请输入内容" }], + })( + { setFieldsValue({ "content": value }) }} /> + )} + + + {getFieldDecorator("attachment_id")( + setFieldsValue({ "attachment_id": id })} action={`${httpUrl}/api/attachments.json`} /> + )} + + + {getFieldDecorator('attachments')( + +

+ +

+

点击或拖拽文件到此区域上传,文件大小不超过 100MB

+
)} +
+
+ + + + +
+
+
+
; +} + +export default Form.create()(EditForum); \ No newline at end of file diff --git a/src/forums/edit/index.scss b/src/forums/edit/index.scss new file mode 100644 index 000000000..858c9d4f9 --- /dev/null +++ b/src/forums/edit/index.scss @@ -0,0 +1,24 @@ +.forums_edit_box{ + background-color: #f1f6fd; + background-image: url('../image/bk.png'); + background-size: 100% auto; + background-repeat: no-repeat; +} +.forums_edit_content{ + box-shadow: rgba(35, 44, 121, 0.07) 0px 1px 3px 1px; + background: rgb(255, 255, 255); + border-radius: 4px; + padding: 20px 30px 40px; +} +.forum_edit_form{ + .ant-col-2{ + width: 6%; + } + .submit_button{ + button{ + padding: 6px 30px; + height: auto; + line-height: normal; + } + } +} \ No newline at end of file diff --git a/src/forums/image/banner.png b/src/forums/image/banner.png index e47bbedfb..ef9bbae6a 100644 Binary files a/src/forums/image/banner.png and b/src/forums/image/banner.png differ diff --git a/src/forums/image/forums1.png b/src/forums/image/forums1.png new file mode 100644 index 000000000..71ba05703 Binary files /dev/null and b/src/forums/image/forums1.png differ diff --git a/src/forums/image/forums2.png b/src/forums/image/forums2.png new file mode 100644 index 000000000..4e31e2938 Binary files /dev/null and b/src/forums/image/forums2.png differ diff --git a/src/forums/image/forums3.png b/src/forums/image/forums3.png new file mode 100644 index 000000000..7ec241a0e Binary files /dev/null and b/src/forums/image/forums3.png differ diff --git a/src/forums/image/forums4.png b/src/forums/image/forums4.png new file mode 100644 index 000000000..4eddf1749 Binary files /dev/null and b/src/forums/image/forums4.png differ diff --git a/src/forums/index.jsx b/src/forums/index.jsx index c40c112c6..16e62dac0 100644 --- a/src/forums/index.jsx +++ b/src/forums/index.jsx @@ -15,10 +15,26 @@ const List = Loadable({ loader : () => import("./list"), loading: Loading, }) +const Editor = Loadable({ + loader : () => import("./edit"), + loading: Loading, +}) function SoftBot(props){ return
+ ( + + )} + > + ( + + )} + > ( diff --git a/src/forums/list/index.jsx b/src/forums/list/index.jsx index 5235667e1..55aec8272 100644 --- a/src/forums/list/index.jsx +++ b/src/forums/list/index.jsx @@ -17,7 +17,7 @@ import { rollingButton } from "../../components/button"; import { HoverUnderline } from "../../components/title"; function List(props) { - const {mygetHelmetapi, location} = props; + const {mygetHelmetapi, location, current_user} = props; const {common} = mygetHelmetapi || {}; const {forums: forumsDoMain} = common || {}; const tagNameByState = location && location.state && location.state.name; @@ -151,7 +151,7 @@ function List(props) {
论坛交流·开始思想的联接与互动
需求反馈 技术交流 创意分享
- {forumsDoMain && rollingButton({href: `${forumsDoMain}/forums/new`, name: '发布帖子', hoverName: '发布帖子', classname: "add-forums"})} + {forumsDoMain && rollingButton({href: current_user && current_user.login ? `/forums/add` : `/login?go_page=/forums/add`, name: '发布帖子', hoverName: '发布帖子', classname: "add-forums"})}
{forumsImgList && !!forumsImgList.length && {setActiveId(nextId)}}> diff --git a/src/forums/list/index.scss b/src/forums/list/index.scss index 31534dd53..394fa9e55 100644 --- a/src/forums/list/index.scss +++ b/src/forums/list/index.scss @@ -17,7 +17,7 @@ .forums-banner-box { background-image: url('../image/banner.png'); background-size: 100% 100%; - padding: 70px 0 45px; + padding: 70px 0 145px; .forums-banner-title1 { font-family: 'DouyinSansBold'; @@ -170,7 +170,8 @@ background-image: linear-gradient(179.88deg, #ffffff 0%, rgba(255, 255, 255, 0.62) 16.1%, rgba(255, 255, 255, 0) 100%); border-radius: 40px; position: relative; - --offsetTop: 66px; + --offsetTop: 116px; + --top: -60px; animation: itemUp .4s ease-in-out 1.56s forwards, bgOpacity .4s linear 1.56s reverse backwards; .user-img{ width:24px; @@ -211,20 +212,24 @@ } } &:nth-of-type(3) .forums-section-item{ - background-image:linear-gradient(90deg,#D7DEFF 0% 49.37%,#C7CFFF 100%); + background-image: url('../image/forums3.png'); + background-size: 100% 100%; } &:nth-of-type(2) .forums-section-item{ - background-image:linear-gradient(90deg,#C7E3FF 0% 49.37%,#B2D6FF 100%); + background-image: url('../image/forums2.png'); + background-size: 100% 100%; } &:nth-of-type(4) .forums-section-item{ - background-image:linear-gradient(90deg,#DCF6D6 0% 49.37%,#C8FFDF 100%); + background-image: url('../image/forums4.png'); + background-size: 100% 100%; } } .forums-section-item{ position: relative; cursor: pointer; border-radius:15px; - background-image:linear-gradient(90deg,#C0E9FE 0% 49.37%,#A6DDFC 100%); + background-image: url('../image/forums1.png'); + background-size: 100% 100%; .memo_image_info{ width: 100%; diff --git a/src/index.js b/src/index.js index 24ef80190..0fd8586cf 100644 --- a/src/index.js +++ b/src/index.js @@ -3,7 +3,7 @@ import { AppContainer } from 'react-hot-loader'; import React from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter, Route } from 'react-router-dom' -import './index.css'; +import './index.scss'; import App from './App'; import { configureUrlQuery } from 'react-url-query'; import history from './history'; diff --git a/src/index.css b/src/index.scss similarity index 80% rename from src/index.css rename to src/index.scss index 29b89ae3f..09231008e 100644 --- a/src/index.css +++ b/src/index.scss @@ -256,4 +256,49 @@ i.color-orange05:hover { margin-top: -48px; vertical-align:bottom; height: 100vh; +} + +/* 紫色渐变弹窗 */ +.purpleGradient.ant-modal{ + &.headerBgImage .ant-modal-content{ + // background-image: url("./images/modalbk.png"); + background-size: 100% auto; + background-color:#fbfcff; + background-repeat: no-repeat; + border-radius:34px; + } + .ant-modal-title{ + color: #191919; + text-align: center; + } + .ant-modal-content{ + border-radius: 20px; + background:linear-gradient(179.76deg, #D8DDFF 0%, #fbfcff 27%) + } + .ant-modal-header, .agreement-content{ + background: none; + } + .ant-modal-header{ + margin: 0 20px; + border-bottom:1px dashed rgba(174, 175, 185, 0.39); + } + .ant-modal-body{ + padding-bottom: 0px; + .ant-input:hover{ + border-color: #4154F1; + } + } + .ant-modal-footer{ + padding-bottom: 55px; + text-align: center; + border-top: none; + button{ + width:101px; + height:36px; + border-radius:6px; + &:not(:last-of-type){ + margin-right: 35px; + } + } + } } \ No newline at end of file