diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 72afb3342..c5d3082ba 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -32,7 +32,12 @@ class CommentsController < ApplicationController # 列表 def index - discusses = @hack.discusses.where(root_id: nil) + discusses = + if current_user.admin_or_business? + @hack.discusses.where(root_id: nil) + else + @hack.discusses.where(root_id: nil, hidden: false) + end @discusses_count = discusses.count @discusses= paginate discusses end @@ -45,8 +50,9 @@ class CommentsController < ApplicationController # 隐藏、取消隐藏 def hidden - if @hack.manager?(current_user) - @discuss.update_attribute(:hidden, params[:hidden] == "1") + if current_user.admin_or_business? + @discuss = @hack.discusses.where(id: params[:id]).first + @discuss.update_attribute(:hidden, params[:hidden].to_i == 1) sucess_status else Educoder::TipException(403, "..") diff --git a/app/controllers/hacks_controller.rb b/app/controllers/hacks_controller.rb index b7f6a30a2..7a0fb7635 100644 --- a/app/controllers/hacks_controller.rb +++ b/app/controllers/hacks_controller.rb @@ -98,12 +98,20 @@ class HacksController < ApplicationController # 发布功能 def publish @hack.update_attribute(:status, 1) + base_attrs = { + trigger_user_id: current_user.id, viewed: 0, tiding_type: 'System', user_id: @hack.user_id, + } + @hack.tidings.create!(base_attrs) render_ok end # 取消发布 def cancel_publish @hack.update_attribute(:status, 0) + base_attrs = { + trigger_user_id: current_user.id, viewed: 0, tiding_type: 'System', user_id: @hack.user_id + } + @hack.tidings.create!(base_attrs) render_ok end diff --git a/app/controllers/shixuns_controller.rb b/app/controllers/shixuns_controller.rb index 190892730..940955e93 100644 --- a/app/controllers/shixuns_controller.rb +++ b/app/controllers/shixuns_controller.rb @@ -266,8 +266,20 @@ class ShixunsController < ApplicationController # 如果是jupyter,先创建一个目录,为了挂载(因为后续数据集,开启Pod后环境在没销毁前,你上传数据集是挂载不上目录的,因此要先创建目录,方便中间层挂载) if @new_shixun.is_jupyter? folder = EduSetting.get('shixun_folder') + raise "存储目录未定义" unless folder.present? path = "#{folder}/#{@new_shixun.identifier}" FileUtils.mkdir_p(path, :mode => 0777) unless File.directory?(path) + # 复制数据集 + save_path = File.join(folder, @shixun.identifier) + @shixun.data_sets.each do |set| + new_date_set = Attachment.new + new_date_set.attributes = set.attributes.dup.except("id", "container_id", "disk_directory") + new_date_set.container_id = @new_shixun.id + new_date_set.disk_directory = @new_shixun.identifier + new_date_set.save! + FileUtils.cp("#{save_path}/#{set.relative_path_filename}", path) + end + end # 同步复制关卡 if @shixun.challenges.present? diff --git a/app/controllers/subjects_controller.rb b/app/controllers/subjects_controller.rb index c8f2f5f57..0009ab2a6 100644 --- a/app/controllers/subjects_controller.rb +++ b/app/controllers/subjects_controller.rb @@ -216,10 +216,10 @@ class SubjectsController < ApplicationController @shixun.update_column(:repo_name, repo_path.split(".")[0]) mirror_id = if @shixun.is_jupyter? - MirrorRepository.where("type_name like '%Jupyter%'").first&.id folder = EduSetting.get('shixun_folder') path = "#{folder}/#{identifier}" FileUtils.mkdir_p(path, :mode => 0777) unless File.directory?(path) + MirrorRepository.where("type_name like '%Jupyter%'").first&.id else MirrorRepository.find_by(type_name: 'Python3.6')&.id end diff --git a/app/decorators/tiding_decorator.rb b/app/decorators/tiding_decorator.rb index 130e7f4b8..ff198468a 100644 --- a/app/decorators/tiding_decorator.rb +++ b/app/decorators/tiding_decorator.rb @@ -250,6 +250,9 @@ module TidingDecorator when 'shixunPublish' then name = Shixun.find_by(id: parent_container_id)&.name || '---' I18n.t(locale_format(parent_container_type)) % [name, container.score] + when 'Hack' then + name = Hack.find_by(id: container_id)&.name || '---' + I18n.t(locale_format(parent_container_type)) % [name, container.score] else I18n.t(locale_format(parent_container_type)) % container.score end diff --git a/app/models/discuss.rb b/app/models/discuss.rb index 652ffea37..148d6518b 100644 --- a/app/models/discuss.rb +++ b/app/models/discuss.rb @@ -52,16 +52,19 @@ class Discuss < ApplicationRecord private def send_tiding + if dis_type == 'Shixun' + user_id = has_parent? ? parent.user_id : Challenge.find(challenge_id).user_id + parent_container_type = 'Challenge' + challenge_id = challenge_id + elsif dis_type == 'Hack' + user_id = has_parent? ? parent.user_id : Hack.find(dis_id).user_id + parent_container_type = 'Hack' + challenge_id = nil + end base_attrs = { - trigger_user_id: user_id, parent_container_id: challenge_id, parent_container_type: 'Challenge', - belong_container_id: dis_id, belong_container_type: dis_type, viewed: 0, tiding_type: 'Comment' + trigger_user_id: user_id, parent_container_id: challenge_id, parent_container_type: parent_container_type, + belong_container_id: dis_id, belong_container_type: dis_type, viewed: 0, tiding_type: 'Comment' } - user_id = - if dis_type == 'Shixun' - has_parent? ? parent.user_id : Challenge.find(challenge_id).user_id - elsif dis_type == 'Hack' - has_parent? ? parent.user_id : Hack.find(dis_id).user_id - end tidings.create!(base_attrs.merge(user_id: user_id)) end end diff --git a/app/models/hack.rb b/app/models/hack.rb index 2e2ffb9e0..d1ec3932c 100644 --- a/app/models/hack.rb +++ b/app/models/hack.rb @@ -13,6 +13,8 @@ class Hack < ApplicationRecord has_many :discusses, as: :dis, dependent: :destroy # 点赞 has_many :praise_treads, as: :praise_tread_object, dependent: :destroy + # 消息 + has_many :tidings, as: :container, dependent: :destroy belongs_to :user @@ -21,6 +23,8 @@ class Hack < ApplicationRecord scope :opening, -> {where(open_or_not: 1)} scope :mine, -> (author_id){ where(user_id: author_id) } + after_destroy :send_delete_tiding + def language if hack_codes.count == 1 hack_codes.first.language @@ -49,4 +53,12 @@ class Hack < ApplicationRecord user_id == user.id || user.admin_or_business? end + private + def send_delete_tiding + base_attrs = { + user_id: user_id, viewed: 0, tiding_type: 'Delete', trigger_user_id: current_user.id, content: "你删除了题目:#{name}" + } + tidings.create!(base_attrs) + end + end diff --git a/app/models/praise_tread.rb b/app/models/praise_tread.rb index 58ec965b4..8e96b7d47 100644 --- a/app/models/praise_tread.rb +++ b/app/models/praise_tread.rb @@ -12,7 +12,7 @@ class PraiseTread < ApplicationRecord case self.praise_tread_object_type when "Memo","Message","Issue" self.tidings << Tiding.new(:trigger_user_id => self.user_id, :user_id => self.praise_tread_object.author_id, :parent_container_id => self.praise_tread_object_id, :parent_container_type => self.praise_tread_object_type, :viewed => 0, :tiding_type => "Praise") - when "Discuss","Challenge","HomeworkCommon","JournalsForMessage","Journal","GraduationTopic","GraduationTask" + when "Discuss","Challenge","HomeworkCommon","JournalsForMessage","Journal","GraduationTopic","GraduationTask", "Hack" self.tidings << Tiding.new(:trigger_user_id => self.user_id, :user_id => self.praise_tread_object.user_id, :parent_container_id => self.praise_tread_object_id, :parent_container_type => self.praise_tread_object_type, :viewed => 0, :tiding_type => "Praise") end end diff --git a/app/services/subjects/copy_subject_service.rb b/app/services/subjects/copy_subject_service.rb index f44191fda..4715a8bad 100644 --- a/app/services/subjects/copy_subject_service.rb +++ b/app/services/subjects/copy_subject_service.rb @@ -79,7 +79,7 @@ class Subjects::CopySubjectService < ApplicationService copy_shixun_service_configs_data!(shixun, to_shixun) copy_challenges_data!(shixun, to_shixun) copy_shixun_members_data!(to_shixun) - + copy_jupyter_data_sets(shixun, to_shixun) if shixun.is_jupyter? # 云上实验室 if laboratory laboratory.laboratory_shixuns.create(shixun: to_shixun) @@ -87,6 +87,25 @@ class Subjects::CopySubjectService < ApplicationService to_shixun end + # 复制jupyter的数据集 + def copy_jupyter_data_sets(shixun, to_shixun) + return unless shixun.is_jupyter? + folder = EduSetting.get('shixun_folder') + raise "存储目录未定义" unless folder.present? + path = "#{folder}/#{to_shixun.identifier}" + FileUtils.mkdir_p(path, :mode => 0777) unless File.directory?(path) + # 复制数据集 + save_path = File.join(folder, shixun.identifier) + shixun.data_sets.each do |set| + new_date_set = Attachment.new + new_date_set.attributes = set.attributes.dup.except("id", "container_id", "disk_directory") + new_date_set.container_id = to_shixun.id + new_date_set.disk_directory = to_shixun.identifier + new_date_set.save! + FileUtils.cp("#{save_path}/#{set.relative_path_filename}", path) + end + end + # 创建实训长字段内容 def copy_shixun_info_data!(shixun, to_shixun) to_shixun_info = ShixunInfo.new diff --git a/app/views/comments/_discuss.json.jbuilder b/app/views/comments/_discuss.json.jbuilder index b634b20a8..55b27acdf 100644 --- a/app/views/comments/_discuss.json.jbuilder +++ b/app/views/comments/_discuss.json.jbuilder @@ -5,10 +5,12 @@ json.id discuss.id json.content content_safe(discuss.content) json.time time_from_now(discuss.created_at) json.hack_id discuss.dis_id +json.hidden discuss.hidden # 主贴和回复有一些不同点 if discuss.parent_id json.can_delete discuss.can_deleted?(current_user) else json.praise_count discuss.praises_count json.user_praise discuss.praise_treads.select{|pt| pt.user_id == current_user.id}.length > 0 + json.can_delete discuss.can_deleted?(current_user) && child.count == 0 end \ No newline at end of file diff --git a/app/views/comments/index.json.jbuilder b/app/views/comments/index.json.jbuilder index b176d00b9..bfdbdacbb 100644 --- a/app/views/comments/index.json.jbuilder +++ b/app/views/comments/index.json.jbuilder @@ -1,6 +1,6 @@ json.disscuss_count @discusses_count json.comments @discusses do |discuss| - json.partial! 'comments/discuss', locals: { discuss: discuss} + json.partial! 'comments/discuss', locals: { discuss: discuss, child: discuss.child_discuss(current_user)} json.children discuss.child_discuss(current_user) do |c_d| json.partial! 'comments/discuss', locals: { discuss: c_d } end diff --git a/app/views/hack_user_lastest_codes/result.json.jbuilder b/app/views/hack_user_lastest_codes/result.json.jbuilder index 2561d4ecd..d8f010cc3 100644 --- a/app/views/hack_user_lastest_codes/result.json.jbuilder +++ b/app/views/hack_user_lastest_codes/result.json.jbuilder @@ -1,5 +1,5 @@ json.status 0 -json.message "评测成功" +json.message "评测完成" json.data do json.(@result, :id, :status, :error_line, :error_msg, :input, :output, :execute_time, :execute_memory) diff --git a/app/views/hack_user_lastest_codes/show.json.jbuilder b/app/views/hack_user_lastest_codes/show.json.jbuilder index be700f112..098c24546 100644 --- a/app/views/hack_user_lastest_codes/show.json.jbuilder +++ b/app/views/hack_user_lastest_codes/show.json.jbuilder @@ -1,5 +1,5 @@ json.hack do - json.(@hack, :name, :difficult, :time_limit, :description, :score, :identifier, :status, :praises_count) + json.(@hack, :id, :name, :difficult, :time_limit, :description, :score, :identifier, :status, :praises_count) json.language @hack.language json.username @hack.user.real_name json.code @my_hack.code @@ -7,6 +7,7 @@ json.hack do json.submit_count @hack.submit_num json.modify_code @modify json.comments_count @hack.discusses.count + json.user_praise @hack.praise_treads.select{|pt| pt.user_id == current_user.id}.length > 0 end json.test_case do @@ -16,5 +17,6 @@ end json.user do json.partial! 'users/user', user: current_user json.hack_manager @hack.manager?(current_user) + json.admin current_user.admin_or_business? end \ No newline at end of file diff --git a/app/views/shixuns/get_data_sets.json.jbuilder b/app/views/shixuns/get_data_sets.json.jbuilder index 20ced2acd..f800d1c32 100644 --- a/app/views/shixuns/get_data_sets.json.jbuilder +++ b/app/views/shixuns/get_data_sets.json.jbuilder @@ -5,7 +5,10 @@ json.data_sets do json.author set.author.real_name json.created_on set.created_on json.filesize number_to_human_size(set.filesize) - json.file_path "#{@absolute_folder}/#{set.relative_path_filename}" + # 这里需要去除shixunfiles目录后的标识;因为Jupyter代码里面会写死这样的路径,当实训fork后,这个版本库的路径无法修改,因此给用户展示的 + # 还是/data/shixunfiles/+文件名这种形式 + json.file_path "#{@absolute_folder}/#{set.relative_path_filename}".gsub("/#{@shixun.identifier}", "") end end -json.data_sets_count @data_count \ No newline at end of file +json.data_sets_count @data_count +json.folder_name @absolute_folder \ No newline at end of file diff --git a/config/locales/tidings/zh-CN.yml b/config/locales/tidings/zh-CN.yml index f484efdc1..dc56ad345 100644 --- a/config/locales/tidings/zh-CN.yml +++ b/config/locales/tidings/zh-CN.yml @@ -118,6 +118,7 @@ Answer: true_end: "查看实训%s第%s关的参考答案消耗金币:%s金币" false_end: "查看实训的参考答案消耗金币:%s金币" + Hack_end: "完成题目解答:%s,获得金币奖励:%s金币" Game_end: "通过实训%s的第%s关获得金币奖励:%s金币" Memo_end: "发布的评论或者帖子获得平台奖励:%s金币" Discusses_end: "发布的评论获得金币奖励:%s金币" diff --git a/config/routes.rb b/config/routes.rb index de884f3c1..ad52ecdb2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -79,6 +79,8 @@ Rails.application.routes.draw do resources :comments do collection do post :reply + end + member do post :hidden end end diff --git a/public/react/public/css/css_min_all.css b/public/react/public/css/css_min_all.css index cf453ac21..431a4da8e 100755 --- a/public/react/public/css/css_min_all.css +++ b/public/react/public/css/css_min_all.css @@ -1883,9 +1883,8 @@ a:hover.task_icons_close{background: url(../images/popup/sy_icons_close.png) -40 .newupload_nav li:last-child{ border-right: none;} .newupload_nav li a{font-size:12px; color:#444;} .newupload_nav_hover{ background: #3498db; } -.newupload_nav_nomal { } .newupload_nav_hover a{color: #fff !important; } - +.markdown-body { text-align: justify;word-break: break-all;} .bor-reds{ border:1px solid #FF0000!important; border-radius: 4px; @@ -1894,6 +1893,7 @@ a:hover.task_icons_close{background: url(../images/popup/sy_icons_close.png) -40 border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; } + @charset "UTF-8"; /*! diff --git a/public/react/public/css/edu-all.css b/public/react/public/css/edu-all.css index f628b1e5c..48386e675 100644 --- a/public/react/public/css/edu-all.css +++ b/public/react/public/css/edu-all.css @@ -2934,7 +2934,7 @@ a.singlepublishtwo{ padding: 40px !important; } .editormd-html-preview{ - width: 94% !important; + width: 100% !important; color: #323232 !important; } #homework_editorMd_description hr{ @@ -3478,3 +3478,9 @@ a.singlepublishtwo{ /*width: auto !important;*/ /*max-width: 600px !important;*/ /*}*/ + + +.markdown-body { + text-align: justify; + word-break: break-all; +} \ No newline at end of file diff --git a/public/react/src/AppConfig.js b/public/react/src/AppConfig.js index 0356f25f1..c75389419 100644 --- a/public/react/src/AppConfig.js +++ b/public/react/src/AppConfig.js @@ -52,7 +52,7 @@ export function initAxiosInterceptors(props) { //proxy="http://47.96.87.25:48080" proxy="https://pre-newweb.educoder.net" proxy="https://test-newweb.educoder.net" - proxy="https://test-jupyterweb.educoder.net" + // proxy="https://test-jupyterweb.educoder.net" //proxy="http://192.168.2.63:3001" // 在这里使用requestMap控制,避免用户通过双击等操作发出重复的请求; diff --git a/public/react/src/common/components/comment/CommentForm.js b/public/react/src/common/components/comment/CommentForm.js index 73e36cff9..7683e2dea 100644 --- a/public/react/src/common/components/comment/CommentForm.js +++ b/public/react/src/common/components/comment/CommentForm.js @@ -3,22 +3,24 @@ * @Author: tangjiang * @Github: * @Date: 2019-12-17 17:32:55 - * @LastEditors: tangjiang - * @LastEditTime: 2019-12-18 17:51:44 + * @LastEditors : tangjiang + * @LastEditTime : 2019-12-24 17:27:41 */ +import './index.scss'; import React, { useState } from 'react'; import { Form, Button, Input } from 'antd'; import QuillForEditor from '../../quillForEditor'; -import { QuillDeltaToHtmlConverter } from 'quill-delta-to-html' +// import { QuillDeltaToHtmlConverter } from 'quill-delta-to-html' +import {formatDelta} from './util'; const FormItem = Form.Item; function CommentForm (props) { const { - commentCtxChagne, onCancel, onSubmit, - form + form, + type } = props; const { getFieldDecorator } = form; @@ -34,22 +36,25 @@ function CommentForm (props) { // const { form: { getFieldDecorator } } = props; const [showQuill, setShowQuill] = useState(false); // 点击输入框 - const handleInputClick = () => { + const handleInputClick = (type) => { setShowQuill(true); } // 取消 const handleCancle = () => { setShowQuill(false); - onCancel && onCancel(); + setCtx(''); + props.form.resetFields(); + onCancel && onCancel(); } // 编辑器内容变化时 const handleContentChange = (content) => { + console.log('编辑器内容', content); setCtx(content); try { - const _html = new QuillDeltaToHtmlConverter(content.ops, {}).convert(); + // const _html = new QuillDeltaToHtmlConverter(content.ops, {}).convert(); // props.form.setFieldsValue({'comment': _html.replace(/<\/?[^>]*>/g, '')}); - props.form.setFieldsValue({'comment': _html}); + props.form.setFieldsValue({'comment': content}); } catch (error) { console.log(error); } @@ -63,13 +68,25 @@ function CommentForm (props) { const content = ctx; props.form.setFieldsValue({'comment': ''}); setCtx(''); - console.log(content); - onSubmit && onSubmit(content); + const _html = formatDelta(content.ops); + onSubmit && onSubmit(_html); } }); } + + const handleShowImage = (url) => { + alert(url); + } + + // const _clazz = type === 'bottom' ? 'comment_form_bottom_area' : 'comment_form_area'; + let _clazz; + if (type === 'bottom') { + _clazz = showQuill ? 'comment_form_bottom_area active' : 'comment_form_bottom_area'; + } else { + _clazz = 'comment_form_area'; + } return ( -
+ { getFieldDecorator('comment', { @@ -78,13 +95,13 @@ function CommentForm (props) { ], })( handleInputClick(type)} placeholder="说点儿什么~" + className={showQuill ? '' : 'show_input'} style={{ height: showQuill ? '0px' : '40px', overflow: showQuill ? 'hidden' : 'auto', opacity: showQuill ? 0 : 1, - transition: 'all .3s' }} /> ) @@ -98,14 +115,15 @@ function CommentForm (props) { overflow: showQuill ? 'none' : 'hidden', transition: 'all 0.3s' }} - style={{ height: '150px', overflowY: 'auto' }} + style={{ height: '150px' }} placeholder="说点儿什么~" options={options} value={ctx} + showUploadImage={handleShowImage} onContentChange={handleContentChange} /> - + diff --git a/public/react/src/common/components/comment/CommentIcon.js b/public/react/src/common/components/comment/CommentIcon.js index 5440e2579..97a57da5e 100644 --- a/public/react/src/common/components/comment/CommentIcon.js +++ b/public/react/src/common/components/comment/CommentIcon.js @@ -3,16 +3,19 @@ * @Author: tangjiang * @Github: * @Date: 2019-12-18 10:49:46 - * @LastEditors: tangjiang - * @LastEditTime: 2019-12-18 11:39:23 + * @LastEditors : tangjiang + * @LastEditTime : 2019-12-24 18:04:52 */ import './index.scss'; import React from 'react'; import { Icon } from 'antd'; +// import MyIcon from '../MyIcon'; function CommentIcon ({ type, // 图标类型 count, // 评论数 iconClick, + iconColor, + theme, ...props }) { @@ -21,10 +24,15 @@ function CommentIcon ({ iconClick && iconClick(); } + const _className = [undefined, null, ''].includes(count) ? 'comment_count_none' : 'comment_count'; return ( - - - { count } + + + { count } ) } diff --git a/public/react/src/common/components/comment/CommentItem.js b/public/react/src/common/components/comment/CommentItem.js index 19da645f5..e3a229c50 100644 --- a/public/react/src/common/components/comment/CommentItem.js +++ b/public/react/src/common/components/comment/CommentItem.js @@ -3,104 +3,134 @@ * @Author: tangjiang * @Github: * @Date: 2019-12-17 17:35:17 - * @LastEditors: tangjiang - * @LastEditTime: 2019-12-19 18:02:28 + * @LastEditors : tangjiang + * @LastEditTime : 2019-12-24 18:05:18 */ import './index.scss'; import React, { useState } from 'react'; import CommentIcon from './CommentIcon'; import { getImageUrl, CNotificationHOC } from 'educoder' import { Icon } from 'antd'; -import moment from 'moment'; -// import QuillForEditor from '../../quillForEditor'; import CommentForm from './CommentForm'; -// import {ModalConfirm} from '../ModalConfirm'; function CommentItem ({ + isAdmin, options, - confirm + confirm, + comment, + submitDeleteComment, + submitChildComment, + likeComment, + showOrHideComment }) { // 显示评论输入框 const [showQuill, setShowQuill] = useState(false); // 加载更多评论内容 - const [showMore, setShowMore] = useState(false); + // const [showMore, setShowMore] = useState(false); + // 显示子列数 + const [showItemCount, setShowItemCount] = useState(1); // 箭头方向 const [arrow, setArrow] = useState(false); - // 删除评论 - const deleteComment = () => { - console.log('删除评论...'); + + const { + author = {}, // 作者 + id, // 评论id + content, // 回复内容 + time, // 回复时间 + hidden, // 是否隐藏 + // hack_id, // OJ的ID + praise_count, // 点赞数 + user_praise, // 当前用户是否点赞 + can_delete, + children = [] // 子回复 + } = comment; + + // 删除评论 type: parent | child, id + const deleteComment = (id) => { confirm({ title: '提示', - content: (

确定要删除该条回复吗?

), + content: ('确定要删除该条回复吗?'), onOk () { - console.log('点击了删除'); + console.log('点击了删除', id); + submitDeleteComment && submitDeleteComment(id); } }); - // ModalConfirm('提示', (

确定要删除该条回复吗?

), () => { - // console.log('点击了删除'); - // }); } // 评论头像 - const commentAvatar = (url) => ( - + const commentAvatar = (author) => ( + ); // 评论信息 - const commentInfo = () => ( -

- 用户名 - {moment(new Date(), 'YYYYMMDD HHmmss').fromNow()} - -

- ); + const commentInfo = (id, author, time, can_delete) => { + const _classNames = can_delete ? 'item-close' : 'item-close hide'; + return ( +
+ {author.name || ''} + {time || ''} + + deleteComment(id)}/> + +
+ ); + }; // 评论内容 const commentCtx = (ctx) => ( -

- 这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容 -

+

); // 加载更多 - const handleOnLoadMore = () => { - if (!arrow) { - // 展开所有 - } else { - // 收起 - } + const handleOnLoadMore = (len) => { + setShowItemCount(!arrow ? len : 1); setArrow(!arrow); }; // 评论追加内容 - const commentAppend = () => { + const commentAppend = (children = []) => { + + const len = children.length; + const _moreClass = len > 1 ? 'comment_item_loadmore show' : 'comment_item_loadmore' + const lastTxt = len - showItemCount; + const renderChild = (children) => { + return children.map((child, i) => { + const { + id, // 评论id + author = {}, + time, + content, + can_delete + } = child; + const showOrHide = i < showItemCount ? 'comment_item_show' : 'comment_item_hide'; + return ( +
  • +
    + {commentAvatar(author)} +
    + {commentInfo(id, author, time, can_delete)} + {commentCtx(content)} +
    +
    +
  • + ); + }) + } + const _clazz = len > 0 ? 'comment_item_append_list active' : 'comment_item_append_list'; return ( -
      -
    • - {commentAvatar()} -
      - {commentInfo()} - {commentCtx()} -
      -
    • -
    • - {commentAvatar()} -
      - {commentInfo()} - {commentCtx()} -
      -
    • -
    • - {commentAvatar()} -
      - {commentInfo()} - {commentCtx()} -
      -
    • +
        + {renderChild(children)} -
      • -

        展开其余23条评论

        +
      • handleOnLoadMore(len)}> +

        展开其余{lastTxt}条评论

        @@ -109,7 +139,14 @@ function CommentItem ({ ); }; // 点击图标 - const handleIconClick = () => {} + const handleShowOrHide = (id, hidden) => { + showOrHideComment && showOrHideComment(id, hidden); + } + + // 点赞 + const handleClickLick = (id) => { + likeComment && likeComment(id); + } // 点击评论icon const handleClickMessage = () => { @@ -122,23 +159,29 @@ function CommentItem ({ } // 点击保存 - const handleClickSubmit = (content) => { - // 保存并关闭 - setShowQuill(false); - console.log('获取保存内容', content); + const handleClickSubmit = (id) => { + return (ctx) => { + setShowQuill(false); + submitChildComment && submitChildComment(id, ctx); + } } return (
      • - {commentAvatar()} + {commentAvatar(author)}
        - {commentInfo()} - {commentCtx()} + {commentInfo(id, author, time, can_delete)} + {commentCtx(content)} - {commentAppend()} + {commentAppend(children)}
        - + handleShowOrHide(id, !hidden ? 1 : 0)} + /> {/* 回复 */} {/* 点赞 */} - + handleClickLick(id)} + />
        diff --git a/public/react/src/common/components/comment/CommentList.js b/public/react/src/common/components/comment/CommentList.js index 9d8cde87b..d2bc1f6ee 100644 --- a/public/react/src/common/components/comment/CommentList.js +++ b/public/react/src/common/components/comment/CommentList.js @@ -3,16 +3,52 @@ * @Author: tangjiang * @Github: * @Date: 2019-12-17 17:34:00 - * @LastEditors: tangjiang - * @LastEditTime: 2019-12-18 11:48:09 + * @LastEditors : tangjiang + * @LastEditTime : 2019-12-24 18:08:07 */ import './index.scss'; import React from 'react'; import CommentItem from './CommentItem'; -function CommentList ({}) { +import { Empty } from 'antd'; +function CommentList (props) { + const { + isAdmin, + commentLists, // 评论列表 + submitChildComment, + submitDeleteComment, + likeComment, + showOrHideComment + } = props; + + const {comments = []} = commentLists; + + const renderLi = () => { + if (comments.length > 0) { + return comments.map((item, index) => { + return ( + + ); + }); + } else { + return ( +
        + +
        + ); + } + } + return (
          - + {renderLi()}
        ); } diff --git a/public/react/src/common/components/comment/index.js b/public/react/src/common/components/comment/index.js index f0ecf3309..5efa8c5ad 100644 --- a/public/react/src/common/components/comment/index.js +++ b/public/react/src/common/components/comment/index.js @@ -3,18 +3,42 @@ * @Author: tangjiang * @Github: * @Date: 2019-12-17 17:31:33 - * @LastEditors: tangjiang - * @LastEditTime: 2019-12-18 11:47:39 + * @LastEditors : tangjiang + * @LastEditTime : 2019-12-24 18:03:21 */ import React from 'react'; -import CommentForm from './CommentForm'; +// import CommentForm from './CommentForm'; import CommentList from './CommentList'; function Comment (props) { + const { + commentLists, + // addComment, + // cancelComment, + isAdmin, + addChildComment, + likeComment, + showOrHideComment, + submitDeleteComment + } = props; + + // const handleCancelComment = () => { + // cancelComment && cancelComment(); + // }; return ( - - + {/* */} + ); } diff --git a/public/react/src/common/components/comment/index.scss b/public/react/src/common/components/comment/index.scss index 816e6da6c..2e1dc4880 100644 --- a/public/react/src/common/components/comment/index.scss +++ b/public/react/src/common/components/comment/index.scss @@ -8,8 +8,20 @@ $ml: 20px; .comment_list_wrapper{ box-sizing: border-box; - border-top: 1px solid $bdColor; - + // border-top: 1px solid $bdColor; + .empty_comment{ + display: flex; + height: calc(100vh - 200px); + width: 100%; + justify-content: center; + align-items: center; + } + .comment_item_show{ + display: block; + } + .comment_item_hide{ + display: none; + } .comment_item_area{ display: flex; padding: 20px 0; @@ -34,8 +46,13 @@ $ml: 20px; margin-left: $ml; } .item-close{ - float: right; + display: inline-block; cursor: pointer; + float: right; + } + + .item-close.hide{ + display: none; } } .item-ctx{ @@ -50,13 +67,16 @@ $ml: 20px; margin-top: 10px; .comment-icon-margin{ - margin-left: 30px; + margin-left: 20px; + } + .comment-icon-margin-10{ + margin-left: 10px; } } - .comment_item_quill{ - margin-top: 20px; - } + // .comment_item_quill{ + // // margin-top: 10px; + // } } .comment_icon_count{ cursor: pointer; @@ -71,6 +91,9 @@ $ml: 20px; margin-left: 10px; transition: color .3s; } + .comment_count_none{ + margin-left: 0; + } &:hover{ .comment_icon, @@ -80,11 +103,15 @@ $ml: 20px; } } .comment_item_append_list{ + display: none; position: relative; background-color: $bgColor; border-radius: 5px; padding: 0 15px 10px; margin: 15px 0; + &.active{ + display: block; + } &::before { position: absolute; left: 15px; @@ -98,6 +125,7 @@ $ml: 20px; } .comment_item_loadmore{ + display: none; padding-top: 10px; cursor: pointer; .loadmore-txt, @@ -106,6 +134,41 @@ $ml: 20px; text-align: center; font-size: $fz12; } + + &.show{ + display: block; + } } } +} + +.comment_form_area, +.comment_form_bottom_area{ + width: 100%; +} +.comment_form_area{ + position: relative; + background: #fff; + // top: 10px; + .ant-form-explain{ + padding-left: 0px; + } + .show_input{ + margin-top: 10px; + } +} + +.comment_form_bottom_area{ + position: relative; + background: #fff; + top: 10px; + + &.active{ + position: absolute; + background: #fff; + left: 0px; + right: 0px; + top: -220px; + padding: 0 20px; + } } \ No newline at end of file diff --git a/public/react/src/common/components/comment/util.js b/public/react/src/common/components/comment/util.js new file mode 100644 index 000000000..5aac8c64e --- /dev/null +++ b/public/react/src/common/components/comment/util.js @@ -0,0 +1,78 @@ +/* + * @Description: quill delta -> html + * @Author: tangjiang + * @Github: + * @Date: 2019-12-24 08:51:25 + * @LastEditors : tangjiang + * @LastEditTime : 2019-12-24 10:45:44 + */ +export const formatDelta = (deltas) => { + + let formatted = []; + + deltas.forEach(element => { + let text = null; + // 没有图片时 + if (!element['insert']['image']) { + text = element['insert']; // 获取插入的内容 + // 元素有属性时 + if (element['attributes']) { + // 获取所有的key值 + const keys = Object.keys(element['attributes']); + keys.forEach(key => { + text = operate(text, key, element['attributes'][key]); + }); + } + } else { + const image = element['insert']['image']; + const {url, alt} = image; + if (url && (url.startsWith('http') || url.startsWith('https'))) { + text = ` + ${alt} + `; + // text = "+alt+"; + } + } + + formatted.push(text); + }); + + return formatted.join(''); +} + +/** + * @param {*} text 文本内容 + * @param {*} key 属性key + * @param {*} value 属性key对应的值 + */ +export const operate = (text, key, value) => { + let operatedText = null; + + switch (key) { + case 'bold': + operatedText = `${text}`; + break; + case 'italic': + operatedText = `${text}`; + break; + case 'strike': + operatedText = `${text}`; + break; + case 'underline': + operatedText = `${text}`; + break; + case 'link': + operatedText = `${text}`; + break; + default: + operatedText = text; + } + + return operatedText; +} diff --git a/public/react/src/common/quillForEditor/ImageBlot.js b/public/react/src/common/quillForEditor/ImageBlot.js index 091bd2c1f..1b474d5dc 100644 --- a/public/react/src/common/quillForEditor/ImageBlot.js +++ b/public/react/src/common/quillForEditor/ImageBlot.js @@ -3,8 +3,8 @@ * @Author: tangjiang * @Github: * @Date: 2019-12-16 15:50:45 - * @LastEditors: tangjiang - * @LastEditTime: 2019-12-17 16:44:48 + * @LastEditors : tangjiang + * @LastEditTime : 2019-12-24 09:44:03 */ import Quill from "quill"; @@ -43,8 +43,8 @@ export default class ImageBlot extends BlockEmbed { alt: node.getAttribute('alt'), url: node.getAttribute('src'), onclick: node.onclick, - // width: node.width, - // height: node.height, + width: node.width, + height: node.height, display: node.getAttribute('display') }; } diff --git a/public/react/src/modules/courses/exercise/Exercisesetting.js b/public/react/src/modules/courses/exercise/Exercisesetting.js index 970cbbe02..bf900eb2f 100644 --- a/public/react/src/modules/courses/exercise/Exercisesetting.js +++ b/public/react/src/modules/courses/exercise/Exercisesetting.js @@ -178,16 +178,9 @@ class Exercisesetting extends Component{ } if(result.data.exercise.unified_setting == true && moment(result.data.exercise.end_time) <= moment()){ - - // if(this.props.isSuperAdmin()===true){ - // this.setState({ - // end_timetype:false - // }) - // }else{ this.setState({ end_timetype:true }) - // } } let group=result.data.published_course_groups; @@ -236,10 +229,10 @@ class Exercisesetting extends Component{ //提交form表单 handleSubmit = (e) => { e.preventDefault(); - if(this.props&&this.props.Commonheadofthetestpaper.exercise_status){ - console.log("190"); - console.log(this.props.Commonheadofthetestpaper.exercise_status); - } + // if(this.props&&this.props.Commonheadofthetestpaper.exercise_status){ + // console.log("190"); + // console.log(this.props.Commonheadofthetestpaper.exercise_status); + // } this.props.form.validateFieldsAndScroll((err, values) => { if(!err){ @@ -327,7 +320,7 @@ class Exercisesetting extends Component{ } } - if(this.state.end_timetype === false){ + if(this.state.end_timetype === false||this.props.isAdmin()==true){ if(moment(end_time,dataformat) <= moment(publish_time,dataformat)){ this.setState({ unit_e_tip:"截止时间不能小于发布时间" @@ -525,7 +518,7 @@ class Exercisesetting extends Component{ end_time:null }) }else{ - if(moment(date,"YYYY-MM-DD HH:mm") <= moment()){ + if(dateString<=moment().format('YYYY-MM-DD HH:mm')){ this.setState({ unit_e_tip:"截止时间不能早于当前时间", e_flag:true diff --git a/public/react/src/modules/courses/graduation/tasks/index.js b/public/react/src/modules/courses/graduation/tasks/index.js index e58281ee4..c316035ee 100644 --- a/public/react/src/modules/courses/graduation/tasks/index.js +++ b/public/react/src/modules/courses/graduation/tasks/index.js @@ -666,7 +666,7 @@ class GraduationTasks extends Component{ {/*{this.props.isAdmin() ? this.addDir()}>题库选用:""}*/} {/*{this.props.isAdmin() ?导出成绩 :""}*/} - {this.props.isAdmin() ? + {this.props.isAdmin() ? 新建 diff --git a/public/react/src/modules/courses/poll/PollDetailTabForth.js b/public/react/src/modules/courses/poll/PollDetailTabForth.js index e5cbfdce7..98127be7e 100644 --- a/public/react/src/modules/courses/poll/PollDetailTabForth.js +++ b/public/react/src/modules/courses/poll/PollDetailTabForth.js @@ -33,6 +33,11 @@ function disabledDateTime() { // disabledSeconds: () => [55, 56], }; } + +function disabledDate(current) { + return current && current < moment().endOf('day').subtract(1, 'days'); +} + const dataformat="YYYY-MM-DD HH:mm"; class PollDetailTabForth extends Component{ @@ -584,7 +589,7 @@ class PollDetailTabForth extends Component{
        截止时间:
        - + diff --git a/public/react/src/modules/courses/poll/PollDetailTabForthRules.js b/public/react/src/modules/courses/poll/PollDetailTabForthRules.js index c531d20b7..1a53b1acd 100644 --- a/public/react/src/modules/courses/poll/PollDetailTabForthRules.js +++ b/public/react/src/modules/courses/poll/PollDetailTabForthRules.js @@ -447,7 +447,7 @@ class PollDetailTabForthRules extends Component{

        - + : ""} - { + {homework_status===undefined? +
        +
        +
        + +
        +
        +
        : homework_status && homework_status.length === 0 ?
        @@ -3609,11 +3616,7 @@ class Listofworksstudentone extends Component {
        :
        - -
        - - diff --git a/public/react/src/modules/tpm/TPMBanner.js b/public/react/src/modules/tpm/TPMBanner.js index 884efd4c2..fa8a4b222 100644 --- a/public/react/src/modules/tpm/TPMBanner.js +++ b/public/react/src/modules/tpm/TPMBanner.js @@ -59,7 +59,8 @@ class TPMBanner extends Component { openknow:false, openshowpublictype:false, Radiovalue:1, - TextAreaintshow:false + TextAreaintshow:false, + } } @@ -112,6 +113,15 @@ class TPMBanner extends Component { componentDidUpdate(prevProps, prevState) { if (prevProps != this.props) { + + if(prevProps.user != this.props.user){ + if(this.props.user&&this.props.user.admin===true||this.props.user&&this.props.user.business===true){ + this.setState({ + TextArea:"云上实验室使用" + }) + } + } + let shixunopenprocess=window.localStorage.shixunopenprocess; let openopenpublictype=window.localStorage.openopenpublictype; if(this.props.status===0&&this.props.openknows===false){ @@ -173,8 +183,12 @@ class TPMBanner extends Component { }) } + if (this.props.user && this.props.user.admin === true || this.props.user && this.props.user.business === true) { + this.setState({ + TextArea: "云上实验室使用" + }) + } } - /* * Fork * */ @@ -736,6 +750,11 @@ class TPMBanner extends Component { this.setState({ Radiovalue:e.target.value }) + if(e.target.value!=4){ + this.setState({ + TextAreaintshow:false + }) + } } render() { @@ -827,7 +846,7 @@ class TPMBanner extends Component { }; // // console.log(this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter) - // console.log(this.props) + // console.log(this.state) return ( @@ -838,7 +857,7 @@ class TPMBanner extends Component { ` .shixunDetail_top{ height: 180px !important; - padding-top:35px !important; + padding-top:50px !important; } .ant-popover{ z-index:1000 !important; @@ -881,9 +900,9 @@ class TPMBanner extends Component { }

        -
        +
        -
          +
            {shixunsDetails&&shixunsDetails.stu_num===0?"":
          • 学习人数 {shixunsDetails.stu_num} @@ -954,7 +973,7 @@ class TPMBanner extends Component {
        }> -
        +
        学员评分
        @@ -1318,7 +1337,9 @@ class TPMBanner extends Component { 其它原因 {this.state.Radiovalue === 4 ? -