| @@ -99,3 +99,38 @@ $(document).on("turbolinks:before-cache", function () { | |||
| $(function () { | |||
| }); | |||
| $(document).on('turbolinks:load', function() { | |||
| $('.logo-item-left').on("change", 'input[type="file"]', function () { | |||
| var $fileInput = $(this); | |||
| var file = this.files[0]; | |||
| var imageType = /image.*/; | |||
| if (file && file.type.match(imageType)) { | |||
| var reader = new FileReader(); | |||
| reader.onload = function () { | |||
| var $box = $fileInput.parent(); | |||
| $box.find('img').attr('src', reader.result).css('display', 'block'); | |||
| $box.addClass('has-img'); | |||
| }; | |||
| reader.readAsDataURL(file); | |||
| } else { | |||
| } | |||
| }); | |||
| $('.attachment-item-left').on("change", 'input[type="file"]', function () { | |||
| var $fileInput = $(this); | |||
| var file = this.files[0]; | |||
| var imageType = /image.*/; | |||
| if (file && file.type.match(imageType)) { | |||
| var reader = new FileReader(); | |||
| reader.onload = function () { | |||
| var $box = $fileInput.parent(); | |||
| $box.find('img').attr('src', reader.result).css('display', 'block'); | |||
| $box.addClass('has-img'); | |||
| }; | |||
| reader.readAsDataURL(file); | |||
| } else { | |||
| } | |||
| }); | |||
| }) | |||
| @@ -0,0 +1,141 @@ | |||
| /* | |||
| * @Description: Do not edit | |||
| * @Date: 2021-08-31 11:16:45 | |||
| * @LastEditors: viletyy | |||
| * @Author: viletyy | |||
| * @LastEditTime: 2021-08-31 14:19:46 | |||
| * @FilePath: /forgeplus/app/assets/javascripts/admins/system_notifications/index.js | |||
| */ | |||
| $(document).on('turbolinks:load', function(){ | |||
| var showSuccessNotify = function() { | |||
| $.notify({ | |||
| message: '操作成功' | |||
| },{ | |||
| type: 'success' | |||
| }); | |||
| } | |||
| // close user | |||
| $('.project-list-container').on('click', '.recommend-action', function(){ | |||
| var $closeAction = $(this); | |||
| var $uncloseAction = $closeAction.siblings('.unrecommend-action'); | |||
| var $editAction = $closeAction.siblings('.edit-recommend-action'); | |||
| var keywordID = $closeAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认将该项目设置为推荐项目吗?', | |||
| ok: function(){ | |||
| $.ajax({ | |||
| url: '/admins/projects/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| project: { | |||
| recommend: true, | |||
| recommend_index: 1 | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.hide(); | |||
| $uncloseAction.show(); | |||
| $editAction.show(); | |||
| $(".project-item-"+keywordID).children('td').eq(5).text("√") | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| }); | |||
| // unclose user | |||
| $('.project-list-container').on('click', '.unrecommend-action', function(){ | |||
| var $uncloseAction = $(this); | |||
| var $closeAction = $uncloseAction.siblings('.recommend-action'); | |||
| var $editAction = $closeAction.siblings('.edit-recommend-action'); | |||
| var keywordID = $uncloseAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认取消该推荐项目吗?', | |||
| ok: function () { | |||
| $.ajax({ | |||
| url: '/admins/projects/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| project: { | |||
| recommend: false, | |||
| recommend_index: 0 | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.show(); | |||
| $uncloseAction.hide(); | |||
| $editAction.hide(); | |||
| $(".project-item-"+keywordID).children('td').eq(5).text("") | |||
| } | |||
| }); | |||
| } | |||
| }) | |||
| }); | |||
| // close user | |||
| $('.project-list-container').on('click', '.pinned-action', function(){ | |||
| var $closeAction = $(this); | |||
| var $uncloseAction = $closeAction.siblings('.unpinned-action'); | |||
| var keywordID = $closeAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认将该项目设置为精选项目吗?', | |||
| ok: function(){ | |||
| $.ajax({ | |||
| url: '/admins/projects/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| project: { | |||
| is_pinned: true, | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.hide(); | |||
| $uncloseAction.show(); | |||
| $(".project-item-"+keywordID).children('td').eq(4).text("√") | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| }); | |||
| // unclose user | |||
| $('.project-list-container').on('click', '.unpinned-action', function(){ | |||
| var $uncloseAction = $(this); | |||
| var $closeAction = $uncloseAction.siblings('.pinned-action'); | |||
| var keywordID = $uncloseAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认取消该精选项目吗?', | |||
| ok: function () { | |||
| $.ajax({ | |||
| url: '/admins/projects/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| project: { | |||
| is_pinned: false, | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.show(); | |||
| $uncloseAction.hide(); | |||
| $(".project-item-"+keywordID).children('td').eq(4).text("") | |||
| } | |||
| }); | |||
| } | |||
| }) | |||
| }); | |||
| }) | |||
| @@ -58,3 +58,149 @@ input.form-control { | |||
| position: absolute; | |||
| } | |||
| .logo-item { | |||
| display: flex; | |||
| &-img { | |||
| display: block; | |||
| width: 80px; | |||
| height: 80px; | |||
| background: #e9ecef; | |||
| } | |||
| &-upload { | |||
| cursor: pointer; | |||
| position: absolute; | |||
| top: 0; | |||
| width: 80px; | |||
| height: 80px; | |||
| background: #e9ecef; | |||
| border: 1px solid #ced4da; | |||
| &::before { | |||
| content: ''; | |||
| position: absolute; | |||
| top: 27px; | |||
| left: 39px; | |||
| width: 2px; | |||
| height: 26px; | |||
| background: #495057; | |||
| } | |||
| &::after { | |||
| content: ''; | |||
| position: absolute; | |||
| top: 39px; | |||
| left: 27px; | |||
| width: 26px; | |||
| height: 2px; | |||
| background: #495057; | |||
| } | |||
| } | |||
| &-left { | |||
| position: relative; | |||
| width: 80px; | |||
| height: 80px; | |||
| &.has-img { | |||
| .logo-item-upload { | |||
| display: none; | |||
| } | |||
| &:hover { | |||
| .logo-item-upload { | |||
| display: block; | |||
| background: rgba(145, 145, 145, 0.8); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| &-right { | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: space-between; | |||
| color: #777777; | |||
| font-size: 0.8rem; | |||
| } | |||
| &-title { | |||
| color: #23272B; | |||
| font-size: 1rem; | |||
| } | |||
| } | |||
| .attachment-item { | |||
| display: flex; | |||
| &-img { | |||
| display: block; | |||
| width: 160px; | |||
| height: 160px; | |||
| background: #e9ecef; | |||
| } | |||
| &-upload { | |||
| cursor: pointer; | |||
| position: absolute; | |||
| top: 0; | |||
| width: 160px; | |||
| height: 160px; | |||
| background: #e9ecef; | |||
| border: 1px solid #ced4da; | |||
| &::before { | |||
| content: ''; | |||
| position: absolute; | |||
| top: 54px; | |||
| left: 78px; | |||
| width: 2px; | |||
| height: 52px; | |||
| background: #495057; | |||
| } | |||
| &::after { | |||
| content: ''; | |||
| position: absolute; | |||
| top: 78px; | |||
| left: 54px; | |||
| width: 52px; | |||
| height: 2px; | |||
| background: #495057; | |||
| } | |||
| } | |||
| &-left { | |||
| position: relative; | |||
| width: 160px; | |||
| height: 160px; | |||
| &.has-img { | |||
| .attachment-item-upload { | |||
| display: none; | |||
| } | |||
| &:hover { | |||
| .attachment-item-upload { | |||
| display: block; | |||
| background: rgba(145, 145, 145, 0.8); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| &-right { | |||
| padding-top: 100px; | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: space-between; | |||
| color: #777777; | |||
| font-size: 0.8rem; | |||
| } | |||
| &-title { | |||
| color: #23272B; | |||
| font-size: 1rem; | |||
| } | |||
| } | |||
| @@ -5,7 +5,7 @@ class Admins::ProjectCategoriesController < Admins::BaseController | |||
| def index | |||
| sort_by = ProjectCategory.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' | |||
| sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' | |||
| q = ProjectCategory.ransack(name_cont: params[:name]) | |||
| q = ProjectCategory.includes(:projects).ransack(name_cont: params[:name]) | |||
| project_categories = q.result(distinct: true).order("#{sort_by} #{sort_direction}") | |||
| @project_categories = paginate(project_categories) | |||
| @@ -22,7 +22,7 @@ class Admins::ProjectCategoriesController < Admins::BaseController | |||
| max_position_items = ProjectCategory.select(:id, :position).pluck(:position).reject!(&:blank?) | |||
| max_position = max_position_items.present? ? max_position_items.max.to_i : 0 | |||
| @project_category = ProjectCategory.new(name: @name,position: max_position) | |||
| @project_category = ProjectCategory.new(name: @name,position: max_position, pinned_index: params[:project_category][:pinned_index].to_i) | |||
| if @project_category.save | |||
| redirect_to admins_project_categories_path | |||
| flash[:success] = '创建成功' | |||
| @@ -33,7 +33,7 @@ class Admins::ProjectCategoriesController < Admins::BaseController | |||
| end | |||
| def update | |||
| if @project_category.update_attribute(:name, @name) | |||
| if @project_category.update_attributes({name: @name, pinned_index: params[:project_category][:pinned_index].to_i}) && save_image_file(params[:logo], 'logo') | |||
| redirect_to admins_project_categories_path | |||
| flash[:success] = '更新成功' | |||
| else | |||
| @@ -43,7 +43,7 @@ class Admins::ProjectCategoriesController < Admins::BaseController | |||
| end | |||
| def destroy | |||
| if @project_language.destroy | |||
| if @project_category.destroy | |||
| redirect_to admins_project_categories_path | |||
| flash[:success] = "删除成功" | |||
| else | |||
| @@ -80,4 +80,12 @@ class Admins::ProjectCategoriesController < Admins::BaseController | |||
| flash[:danger] = '分类已存在' | |||
| end | |||
| end | |||
| def save_image_file(file, type) | |||
| return unless file.present? && file.is_a?(ActionDispatch::Http::UploadedFile) | |||
| file_path = Util::FileManage.source_disk_filename(@project_category, type) | |||
| File.delete(file_path) if File.exist?(file_path) # 删除之前的文件 | |||
| Util.write_file(file, file_path) | |||
| end | |||
| end | |||
| @@ -1,4 +1,5 @@ | |||
| class Admins::ProjectsController < Admins::BaseController | |||
| before_action :find_project, only: [:edit, :update] | |||
| def index | |||
| sort_by = Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_on' | |||
| @@ -8,6 +9,26 @@ class Admins::ProjectsController < Admins::BaseController | |||
| @projects = paginate projects.includes(:owner, :members, :issues, :versions, :attachments, :project_score) | |||
| end | |||
| def edit ;end | |||
| def update | |||
| respond_to do |format| | |||
| if @project.update_attributes(project_update_params) | |||
| format.html do | |||
| redirect_to admins_projects_path | |||
| flash[:sucess] = "更新成功" | |||
| end | |||
| format.js {render_ok} | |||
| else | |||
| format.html do | |||
| redirect_to admins_projects_path | |||
| flash[:danger] = "更新失败" | |||
| end | |||
| format.js {render_js_error} | |||
| end | |||
| end | |||
| end | |||
| def destroy | |||
| project = Project.find_by!(id: params[:id]) | |||
| ActiveRecord::Base.transaction do | |||
| @@ -21,4 +42,13 @@ class Admins::ProjectsController < Admins::BaseController | |||
| redirect_to admins_projects_path | |||
| flash[:danger] = "删除失败" | |||
| end | |||
| private | |||
| def find_project | |||
| @project = Project.find_by_id(params[:id]) | |||
| end | |||
| def project_update_params | |||
| params.require(:project).permit(:is_pinned, :recommend, :recommend_index) | |||
| end | |||
| end | |||
| @@ -69,8 +69,7 @@ class Organizations::OrganizationsController < Organizations::BaseController | |||
| def recommend | |||
| recommend = %W(xuos Huawei_Technology openatom_foundation pkecosystem TensorLayer) | |||
| @organizations = Organization.with_visibility(%w(common)) | |||
| .where(login: recommend).select(:id, :login, :firstname, :lastname, :nickname) | |||
| @organizations = Organization.includes(:organization_extension).where(organization_extensions: {recommend: true}).to_a.each_slice(group_size).to_a | |||
| end | |||
| private | |||
| @@ -81,6 +80,10 @@ class Organizations::OrganizationsController < Organizations::BaseController | |||
| :max_repo_creation, :nickname) | |||
| end | |||
| def group_size | |||
| params.fetch(:group_size, 4).to_i | |||
| end | |||
| def password | |||
| params.fetch(:password, "") | |||
| end | |||
| @@ -5,6 +5,10 @@ class ProjectCategoriesController < ApplicationController | |||
| @project_categories = q.result(distinct: true) | |||
| end | |||
| def pinned_index | |||
| @project_categories = ProjectCategory.where.not(pinned_index: 0).order(pinned_index: :desc) | |||
| end | |||
| def group_list | |||
| @project_categories = ProjectCategory.where('projects_count > 0').order(projects_count: :desc) | |||
| # projects = Project.no_anomory_projects.visible | |||
| @@ -4,9 +4,9 @@ class ProjectsController < ApplicationController | |||
| include ProjectsHelper | |||
| include Acceleratorable | |||
| before_action :require_login, except: %i[index branches branches_slice group_type_list simple show fork_users praise_users watch_users recommend about menu_list] | |||
| before_action :require_login, except: %i[index branches branches_slice group_type_list simple show fork_users praise_users watch_users recommend banner_recommend about menu_list] | |||
| before_action :require_profile_completed, only: [:create, :migrate] | |||
| before_action :load_repository, except: %i[index group_type_list migrate create recommend] | |||
| before_action :load_repository, except: %i[index group_type_list migrate create recommend banner_recommend] | |||
| before_action :authorizate_user_can_edit_project!, only: %i[update] | |||
| before_action :project_public?, only: %i[fork_users praise_users watch_users] | |||
| @@ -30,8 +30,8 @@ class ProjectsController < ApplicationController | |||
| def index | |||
| scope = current_user.logged? ? Projects::ListQuery.call(params, current_user.id) : Projects::ListQuery.call(params) | |||
| # @projects = kaminari_paginate(scope) | |||
| @projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units) | |||
| @projects = kaminari_paginate(scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units)) | |||
| # @projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units) | |||
| category_id = params[:category_id] | |||
| @total_count = | |||
| @@ -199,6 +199,10 @@ class ProjectsController < ApplicationController | |||
| @projects = Project.recommend.includes(:repository, :project_category, :owner).order(visits: :desc) | |||
| end | |||
| def banner_recommend | |||
| @projects = Project.recommend.where.not(recommend_index: 0).includes(:project_category, :owner, :project_language).order(recommend_index: :desc) | |||
| end | |||
| def about | |||
| @project_detail = @project.project_detail | |||
| @attachments = Array(@project_detail&.attachments) if request.get? | |||
| @@ -76,7 +76,7 @@ class Organization < Owner | |||
| validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, case_sensitive: false | |||
| validates :login, format: { with: NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" } | |||
| delegate :description, :website, :location, :repo_admin_change_team_access, | |||
| delegate :description, :website, :location, :repo_admin_change_team_access, :recommend, | |||
| :visibility, :max_repo_creation, :num_projects, :num_users, :num_teams, to: :organization_extension, allow_nil: true | |||
| scope :with_visibility, ->(visibility) { joins(:organization_extension).where(organization_extensions: {visibility: visibility}) if visibility.present? } | |||
| @@ -15,6 +15,7 @@ | |||
| # num_projects :integer default("0") | |||
| # num_users :integer default("0") | |||
| # num_teams :integer default("0") | |||
| # recommend :boolean default("0") | |||
| # | |||
| # Indexes | |||
| # | |||
| @@ -30,6 +31,8 @@ class OrganizationExtension < ApplicationRecord | |||
| enum visibility: {common: 0, limited: 1, privacy: 2} | |||
| before_save :set_recommend | |||
| def self.build(organization_id, description, website, location, repo_admin_change_team_access, visibility, max_repo_creation) | |||
| self.create!(organization_id: organization_id, | |||
| description: description, | |||
| @@ -39,4 +42,9 @@ class OrganizationExtension < ApplicationRecord | |||
| visibility: visibility, | |||
| max_repo_creation: max_repo_creation) | |||
| end | |||
| private | |||
| def set_recommend | |||
| self.recommend = false unless self.common? | |||
| end | |||
| end | |||
| @@ -55,8 +55,9 @@ | |||
| # platform :integer default("0") | |||
| # default_branch :string(255) default("master") | |||
| # website :string(255) | |||
| # order_index :integer default("0") | |||
| # lesson_url :string(255) | |||
| # is_pinned :boolean default("0") | |||
| # recommend_index :integer default("0") | |||
| # | |||
| # Indexes | |||
| # | |||
| @@ -79,6 +80,7 @@ | |||
| class Project < ApplicationRecord | |||
| include Matchable | |||
| include Publicable | |||
| @@ -128,12 +130,13 @@ class Project < ApplicationRecord | |||
| has_many :webhooks, class_name: "Gitea::Webhook", primary_key: :gpid, foreign_key: :repo_id | |||
| after_create :init_project_common, :incre_user_statistic, :incre_platform_statistic | |||
| after_save :check_project_members, :reset_cache_data | |||
| before_save :set_invite_code, :reset_unmember_followed | |||
| before_save :set_invite_code, :reset_unmember_followed, :set_recommend_and_is_pinned | |||
| before_destroy :decre_project_common | |||
| after_destroy :decre_user_statistic, :decre_platform_statistic | |||
| scope :project_statics_select, -> {select(:id,:name, :is_public, :identifier, :status, :project_type, :user_id, :forked_count, :visits, :project_category_id, :project_language_id, :license_id, :ignore_id, :watchers_count, :created_on)} | |||
| scope :project_statics_select, -> {select(:id,:name, :is_public, :identifier, :status, :project_type, :user_id, :forked_count, :description, :visits, :project_category_id, :project_language_id, :license_id, :ignore_id, :watchers_count, :created_on)} | |||
| scope :no_anomory_projects, -> {where("projects.user_id is not null and projects.user_id != ?", 2)} | |||
| scope :recommend, -> { visible.project_statics_select.where(recommend: true) } | |||
| scope :pinned, -> {where(is_pinned: true)} | |||
| delegate :content, to: :project_detail, allow_nil: true | |||
| delegate :name, to: :license, prefix: true, allow_nil: true | |||
| @@ -211,6 +214,16 @@ class Project < ApplicationRecord | |||
| end | |||
| end | |||
| def set_recommend_and_is_pinned | |||
| self.recommend = self.recommend_index.zero? ? false : true | |||
| # 私有项目不允许设置精选和推荐 | |||
| unless self.is_public | |||
| self.recommend = false | |||
| self.recommend_index = 0 | |||
| self.is_pinned = false | |||
| end | |||
| end | |||
| def self.search_project(search) | |||
| ransack(name_or_identifier_cont: search) | |||
| end | |||
| @@ -8,10 +8,27 @@ | |||
| # projects_count :integer default("0") | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # ancestry :string(255) | |||
| # pinned_index :integer default("0") | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_project_categories_on_ancestry (ancestry) | |||
| # | |||
| class ProjectCategory < ApplicationRecord | |||
| include Projectable | |||
| has_ancestry | |||
| def logo_url | |||
| image_url('logo') | |||
| end | |||
| private | |||
| def image_url(type) | |||
| return nil unless Util::FileManage.exists?(self, type) | |||
| Util::FileManage.source_disk_file_url(self, type) | |||
| end | |||
| end | |||
| @@ -11,7 +11,8 @@ class Projects::ListQuery < ApplicationQuery | |||
| end | |||
| def call | |||
| q = Project.visible.by_name_or_identifier(params[:search]) | |||
| q = params[:pinned].present? ? Project.pinned : Project | |||
| q = q.visible.by_name_or_identifier(params[:search]) | |||
| scope = q | |||
| .with_project_type(params[:project_type]) | |||
| @@ -7,9 +7,33 @@ | |||
| <span aria-hidden="true">×</span> | |||
| </button> | |||
| </div> | |||
| <%= form_for @project_category, url: {controller: "project_categories", action: "#{type}"} do |p| %> | |||
| <%= form_for @project_category, url: {controller: "project_categories", action: "#{type}"}, html: { enctype: 'multipart/form-data' } do |p| %> | |||
| <div class="modal-body"> | |||
| <%= p.text_field :name,class: "form-control input-lg",placeholder: "分类名称",required: true, maxlength: 64%> | |||
| <div class="form-group"> | |||
| <label> | |||
| 分类名称 <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :name,class: "form-control input-lg",placeholder: "分类名称",required: true, maxlength: 64%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 精选等级 | |||
| </label> | |||
| <%= p.number_field :pinned_index,class: "form-control input-lg",placeholder: "精选等级",required: true%> | |||
| </div> | |||
| <div class="logo-item"> | |||
| <% logo_img = @project_category.logo_url %> | |||
| <div class="logo-item-left mr-3 <%= logo_img ? 'has-img' : '' %>"> | |||
| <img class="logo-item-img nav-logo-img" src="<%= logo_img %>" style="<%= logo_img.present? ? '' : 'display: none' %>"/> | |||
| <%= file_field_tag(:logo, accept: 'image/png,image/jpg,image/jpeg',style: "display: none", value: params[:logo]) %> | |||
| <label for="logo" class="logo-item-upload" data-toggle="tooltip" data-title="选择图片"></label> | |||
| </div> | |||
| <div class="logo-item-right"> | |||
| <div class="logo-item-title flex-1">logo</div> | |||
| <div>格式:PNG、JPG</div> | |||
| <div>尺寸:高度38px以内,宽等比例缩放</div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="modal-footer"> | |||
| <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button> | |||
| @@ -3,7 +3,9 @@ | |||
| <tr> | |||
| <th width="5%">序号</th> | |||
| <th width="30%">名称</th> | |||
| <th width="20%"><%= sort_tag('精选', name: 'pinned_index', path: admins_project_categories_path) %></th> | |||
| <th width="20%"><%= sort_tag('项目数', name: 'projects_count', path: admins_project_categories_path) %></th> | |||
| <th width="20%">精选项目数</th> | |||
| <th width="20%"><%= sort_tag('创建时间', name: 'created_at', path: admins_project_categories_path) %></th> | |||
| <th width="25%">操作</th> | |||
| </tr> | |||
| @@ -16,7 +18,9 @@ | |||
| <td> | |||
| <%= link_to(project_category.name, "/projects?category_id=#{project_category.id}", target: '_blank') %> | |||
| </td> | |||
| <td><%= project_category.pinned_index == 0 ? "" : "√" %></td> | |||
| <td><%= project_category.projects_count %></td> | |||
| <td><%= project_category.projects.where(is_pinned: true).size %></td> | |||
| <td><%= project_category.created_at&.strftime('%Y-%m-%d %H:%M') %></td> | |||
| <td class="action-container"> | |||
| <%= link_to "编辑", edit_admins_project_category_path(project_category), remote: true, class: "action" %> | |||
| @@ -1,2 +1,19 @@ | |||
| $("#project-category-modals").html("<%= j render(partial: 'admins/project_categories/form_modal', locals: {type: 'update'}) %>") | |||
| $(".project-category-change-modal").modal('show'); | |||
| $(".project-category-change-modal").modal('show'); | |||
| $('.logo-item-left').on("change", 'input[type="file"]', function () { | |||
| var $fileInput = $(this); | |||
| var file = this.files[0]; | |||
| var imageType = /image.*/; | |||
| if (file && file.type.match(imageType)) { | |||
| var reader = new FileReader(); | |||
| reader.onload = function () { | |||
| var $box = $fileInput.parent(); | |||
| $box.find('img').attr('src', reader.result).css('display', 'block'); | |||
| $box.addClass('has-img'); | |||
| }; | |||
| reader.readAsDataURL(file); | |||
| } else { | |||
| } | |||
| }); | |||
| @@ -0,0 +1,2 @@ | |||
| $("#projects-modals").html("<%= j render(partial: 'admins/projects/shared/form_modal', locals: {type: 'update'}) %>") | |||
| $(".project-change-modal").modal('show'); | |||
| @@ -12,4 +12,7 @@ | |||
| <div class="box admin-list-container project-list-container"> | |||
| <%= render partial: 'admins/projects/shared/list', locals: { projects: @projects } %> | |||
| </div> | |||
| </div> | |||
| <div id="projects-modals"> | |||
| </div> | |||
| @@ -0,0 +1,26 @@ | |||
| <div class="modal fade project-change-modal" tabindex="-1" role="dialog" aria-hidden="true"> | |||
| <div class="modal-dialog modal-dialog-centered" role="document"> | |||
| <div class="modal-content"> | |||
| <div class="modal-header"> | |||
| <h5 class="modal-title"><%= type == "create" ? "新增" : "编辑" %></h5> | |||
| <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |||
| <span aria-hidden="true">×</span> | |||
| </button> | |||
| </div> | |||
| <%= form_for @project, url: {controller: "projects", action: "#{type}"} do |p| %> | |||
| <div class="modal-body"> | |||
| <div class="form-group"> | |||
| <label> | |||
| 推荐等级 | |||
| </label> | |||
| <%= p.number_field :recommend_index,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| </div> | |||
| <div class="modal-footer"> | |||
| <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button> | |||
| <%= p.submit "确认", class: "btn btn-primary submit-btn" %> | |||
| </div> | |||
| <% end %> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -1,10 +1,12 @@ | |||
| <table class="table table-hover text-center subject-list-table"> | |||
| <thead class="thead-light"> | |||
| <tr> | |||
| <th width="4%">序号</th> | |||
| <th width="4%">ID</th> | |||
| <th width="15%" class="text-left">项目名称</th> | |||
| <th width="6%">序号</th> | |||
| <th width="6%">ID</th> | |||
| <th width="20%" class="text-left">项目名称</th> | |||
| <th width="6%">公开</th> | |||
| <th width="6%">精选</th> | |||
| <th width="6%">推荐</th> | |||
| <th width="5%">issue</th> | |||
| <th width="5%">资源</th> | |||
| <th width="6%">版本库</th> | |||
| @@ -12,8 +14,8 @@ | |||
| <th width="6%">里程碑</th> | |||
| <th width="10%">成员</th> | |||
| <th width="10%">管理员</th> | |||
| <th width="11%"><%= sort_tag('创建时间', name: 'created_on', path: admins_projects_path) %></th> | |||
| <th width="10%">操作</th> | |||
| <th width="15%"><%= sort_tag('创建时间', name: 'created_on', path: admins_projects_path) %></th> | |||
| <th width="25%">操作</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| @@ -26,6 +28,8 @@ | |||
| <%= link_to(project.name, "/projects/#{project&.owner&.login}/#{project.identifier}", target: '_blank') %> | |||
| </td> | |||
| <td><%= project.is_public ? '√' : '' %></td> | |||
| <td><%= project.is_pinned ? '√' : '' %></td> | |||
| <td><%= project.recommend ? '√' : '' %></td> | |||
| <td><%= project.issues.size %></td> | |||
| <td><%= project.attachments.size %></td> | |||
| <td><%= project&.project_score.try(:changeset_num).to_i %></td> | |||
| @@ -37,8 +41,15 @@ | |||
| </td> | |||
| <td><%= project.created_on&.strftime('%Y-%m-%d %H:%M') %></td> | |||
| <td class="action-container"> | |||
| <% if project.is_public %> | |||
| <%= javascript_void_link '精选', class: 'action pinned-action', data: { id: project.id }, style: project.is_pinned ? 'display: none;' : '' %> | |||
| <%= javascript_void_link '取消精选', class: 'action unpinned-action', data: { id: project.id }, style: project.is_pinned ? '' : 'display: none;' %> | |||
| <%= javascript_void_link '推荐', class: 'action recommend-action', data: { id: project.id }, style: project.recommend ? 'display: none;' : '' %> | |||
| <%= javascript_void_link '取消推荐', class: 'action unrecommend-action', data: { id: project.id }, style: project.recommend ? '' : 'display: none;' %> | |||
| <%= link_to "设置推荐等级", edit_admins_project_path(project.id), remote: true, class: "action edit-recommend-action", style: project.recommend ? '' : 'display: none;' %> | |||
| <% end %> | |||
| <%= link_to "删除", admins_project_path(project.id), method: :delete, data:{confirm: "确认删除的吗?"}, class: "delete-project-action" %> | |||
| </td> | |||
| </td> | |||
| </tr> | |||
| <% end %> | |||
| <% else %> | |||
| @@ -1,6 +1,12 @@ | |||
| json.organizations @organizations do |organization| | |||
| json.id organization.id | |||
| json.name organization.login | |||
| json.nickname organization.nickname.blank? ? organization.name : organization.nickname | |||
| json.avatar_url url_to_avatar(organization) | |||
| json.organizations do | |||
| json.array! @organizations.each do |group| | |||
| json.array! group.each do |organization| | |||
| json.id organization.id | |||
| json.name organization.login | |||
| json.nickname organization.real_name | |||
| json.avatar_url url_to_avatar(organization) | |||
| json.website organization.website | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1 @@ | |||
| json.project_categories @project_categories, :id, :name, :logo_url | |||
| @@ -0,0 +1,36 @@ | |||
| json.partial! "commons/success" | |||
| json.projects do | |||
| json.array! @projects do |project| | |||
| owner = project.owner | |||
| json.id project.id | |||
| json.identifier project.identifier | |||
| json.name project.name | |||
| json.description project.description | |||
| json.visits project.visits | |||
| json.author do | |||
| json.name owner.try(:show_real_name) | |||
| json.type owner.type | |||
| json.login owner.login | |||
| json.image_url url_to_avatar(owner) | |||
| end | |||
| json.category do | |||
| if project.project_category.blank? | |||
| json.nil! | |||
| else | |||
| json.id project.project_category.id | |||
| json.name project.project_category.name | |||
| end | |||
| end | |||
| json.language do | |||
| if project.project_language.blank? | |||
| json.nil! | |||
| else | |||
| json.id project.project_language.id | |||
| json.name project.project_language.name | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -1,4 +1,4 @@ | |||
| json.total_count @total_count | |||
| json.total_count @projects.total_count | |||
| json.projects @projects do |project| | |||
| # json.partial! "/projects/project_detail", project: project | |||
| json.id project.id | |||
| @@ -159,6 +159,7 @@ Rails.application.routes.draw do | |||
| resources :project_categories, only: [:index, :show] do | |||
| get :group_list, on: :collection | |||
| get :pinned_index, on: :collection | |||
| end | |||
| resources :project_languages, only: [:index, :show] | |||
| resources :ignores, only: [:index, :show] | |||
| @@ -184,6 +185,7 @@ Rails.application.routes.draw do | |||
| post :migrate | |||
| get :group_type_list | |||
| get :recommend | |||
| get :banner_recommend | |||
| end | |||
| end | |||
| @@ -907,7 +909,7 @@ Rails.application.routes.draw do | |||
| resources :courses, only: [:index, :destroy, :update] | |||
| resources :projects, only: [:index, :destroy] | |||
| resources :projects, only: [:index, :edit, :update, :destroy] | |||
| resources :disciplines, only: [:index, :create, :edit, :update, :destroy] do | |||
| post :adjust_position, on: :member | |||
| @@ -0,0 +1,7 @@ | |||
| class AddSomeColumnsToProjectDetailFeature < ActiveRecord::Migration[5.2] | |||
| def change | |||
| add_column :project_categories, :pinned_index, :integer, default: 0 | |||
| add_column :projects, :is_pinned, :boolean, default: false | |||
| add_column :projects, :recommend_index, :integer, default: 0 | |||
| end | |||
| end | |||
| @@ -0,0 +1,5 @@ | |||
| class AddRecommendToOrganizationExtensions < ActiveRecord::Migration[5.2] | |||
| def change | |||
| add_column :organization_extensions, :recommend, :boolean, default: false | |||
| end | |||
| end | |||