refactor admin post blog feature without angularjs

This commit is contained in:
yafeilee 2016-04-21 18:07:55 +08:00
parent 368467dfad
commit 3615504b76
35 changed files with 245 additions and 211 deletions

1
.gitignore vendored
View File

@ -14,3 +14,4 @@
/public/assets/*
*.old
*.bak
.byebug_history

View File

@ -12,6 +12,8 @@ gem 'foundation-rails', '~> 6.2.1'
gem 'foundation-icons-sass-rails'
gem 'font-awesome-sass'
gem 'angularjs-rails'
gem 'carrierwave'
gem 'kaminari', git: 'git@github.com:amatsuda/kaminari.git'
gem 'jbuilder'
gem 'pg'
@ -48,6 +50,8 @@ group :development do
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
gem 'byebug'
gem 'rack-cors', :require => 'rack/cors'
end

View File

@ -1,3 +1,11 @@
GIT
remote: git@github.com:amatsuda/kaminari.git
revision: 0e21d52feca1f79a9aca83613cf053eb5273827e
specs:
kaminari (1.0.0.alpha)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
GEM
remote: https://rubygems.org/
specs:
@ -46,6 +54,7 @@ GEM
babel-source (>= 4.0, < 6)
execjs (~> 2.0)
builder (3.2.2)
byebug (8.2.4)
capybara (2.7.0)
addressable
mime-types (>= 1.16)
@ -53,6 +62,11 @@ GEM
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
carrierwave (0.11.0)
activemodel (>= 3.2.0)
activesupport (>= 3.2.0)
json (>= 1.7)
mime-types (>= 1.16)
chunky_png (1.3.5)
codeclimate-test-reporter (0.5.0)
simplecov (>= 0.7.1, < 1.0.0)
@ -306,7 +320,9 @@ PLATFORMS
DEPENDENCIES
angularjs-rails
byebug
capybara
carrierwave
chunky_png
codeclimate-test-reporter
coffee-rails (~> 4.1.0)
@ -322,6 +338,7 @@ DEPENDENCIES
html_truncator
jbuilder
jquery-rails
kaminari!
listen (~> 3.0.5)
mina
mina-multistage

View File

@ -8,11 +8,31 @@ $(document).ready ->
$('input[type=file]').show().focus().click().hide()
false
$('#tabs').on 'change.zf.tabs', ()->
if $('#preview:visible').length > 0
$('#preview').text('Loading...')
$.ajax
url: '/admin/posts/preview'
type: 'POST'
data:
content: $('#content-input').val()
success: (data)->
$('#preview').html(data)
$('a.tag').click (event)->
event.preventDefault()
new_labels = $(this).text()
if $('#labels').val() == ''
labels = new_labels
else
labels = $('#labels').val() + ", #{new_labels}"
$('#labels').val(labels)
opt =
type: 'POST'
url: "/photos"
success: (data,status,xhr)->
txtBox = $("#post_content")
txtBox = $("#content-input")
caret_pos = txtBox.caret('pos')
src_merged = "\n" + data + "\n"
source = txtBox.val()
@ -22,5 +42,4 @@ $(document).ready ->
txtBox.scope().content = txtBox.val()
txtBox.focus()
$('input[type=file]').fileUpload opt

View File

@ -1,35 +0,0 @@
@app.controller 'AdminPostsController', [ '$scope', '$http', '$location', '$timeout', '$cookies', '$sce', ($scope, $http, $location, $timeout, $cookies, $sce)->
$scope.body_active = true
$scope.init = (id)->
url = '/admin/posts/' + id + '.json'
$http
url: url
method: 'GET'
.success (res)->
$scope.title = res.title
$scope.type = res.type
$scope.labels = res.labels
$scope.content = res.content
$scope.changeToBody = ->
$scope.body_active = true
$scope.changeToPreview = ->
$scope.body_active = false
$scope.previewHTML = 'Loading...'
$http.post '/admin/posts/preview', { content: $scope.content }
.success (res)->
$scope.previewHTML = res
$scope.trustAsPreviewHTML = ()->
$sce.trustAsHtml($scope.previewHTML)
$scope.addTag = (e)->
new_labels= $(e.target).text()
if $scope.labels
$scope.labels += ", #{new_labels}"
else
$scope.labels = new_labels
]

View File

@ -0,0 +1,4 @@
$(document).ready ()->
$('#qrcode-link').click (event)->
event.preventDefault()
$('.social-share').toggle()

View File

@ -233,7 +233,7 @@ $breadcrumbs-item-slash: true;
// 11. Button
// ----------
$button-padding: 0.85em 1em;
$button-padding: 1rem 2rem 1.0625rem 2rem;
$button-margin: 0 0 $global-margin 0;
$button-fill: solid;
$button-background: $primary-color;
@ -512,12 +512,12 @@ $show-header-for-stacked: false;
$tab-margin: 0;
$tab-background: $white;
$tab-background-active: $light-gray;
$tab-item-font-size: rem-calc(12);
$tab-item-font-size: rem-calc(16);
$tab-item-background-hover: $white;
$tab-item-padding: 1.25rem 1.5rem;
$tab-expand-max: 6;
$tab-content-background: $white;
$tab-content-border: $light-gray;
$tab-content-border: none;
$tab-content-color: foreground($tab-background, $primary-color);
$tab-content-padding: 1rem;

View File

@ -7,10 +7,14 @@
margin-bottom: 2rem;
}
#post_content {
#content-input {
min-height: 20rem;
}
.content-field {
padding: 0;
}
.preview {
min-height: 20rem;
border: 1px solid #DDDDDD;
@ -25,7 +29,7 @@
#upload_photo {
float: right;
margin-top: 2rem;
margin-top: 1.875rem;
}
tr {

View File

@ -1,6 +1,7 @@
@import 'font-awesome-sprockets';
@import 'font-awesome';
@import 'foundation_and_overrides';
@import 'foundation-icons';
@import 'aboutme_welcome';
@import 'archives';

View File

@ -69,6 +69,10 @@
}
}
.social-share {
display: none;
}
.qrcode-wrapper {
float: right;
margin-top: -2rem;

View File

@ -10,16 +10,6 @@ class Admin::PostsController < ApplicationController
@post = Post.find( params[:id] )
end
def show
post = Post.find( params[:id] )
render :json=> {
title: post.title,
type: post.type,
labels: post.labels_content(true),
content: post.content
}
end
def destroy
@post = Post.find( params[:id] )
if @post.destroy
@ -32,7 +22,7 @@ class Admin::PostsController < ApplicationController
end
def index
@posts = Post.desc(:created_at)
@posts = Post.order(created_at: :desc).page(params[:page]).per(1)
end
def create
@ -71,7 +61,7 @@ class Admin::PostsController < ApplicationController
private
def initialize_or_create_labels(labels)
@post.labels = []
labels.split(",").each do |name|
labels.split(",").map { |i| i.strip }.uniq.each do |name|
label = Label.find_or_initialize_by(name: name.strip)
label.save!
@post.labels << label

View File

@ -5,17 +5,19 @@ class Admin::SessionsController < ApplicationController
end
def create
#TODO
if ENV['ADMIN_USER'].blank?
render :json=> { success: false, message: t('admin.session.no_configuration') }
flash.now[:alert] = t('admin.session.no_configuration')
render :new
elsif ENV['ADMIN_USER'] != params[:username]
render :json=> { success: false, message: t('admin.session.username_error') }
flash.now[:alert] = t('admin.session.username_error')
render :new
elsif ENV['ADMIN_PASSWORD'] != params[:password]
render :json=> { success: false, message: t('admin.session.password_error') }
flash.now[:alert] = t('admin.session.password_error')
render :new
else
flash[:notice] = t('admin.session.login_success')
session[:login] = true
render :json=> { success: true }
redirect_to admin_root_path
end
end

View File

@ -1,60 +1,5 @@
class ArchivesController < ApplicationController
def index
type = map[params[:type]]
limit = 10
start_with = params[:start_with]
@posts = Post.desc(:created_at)
if type
@posts = @posts.where(type: type)
@type = type
end
# all 与 start_with 参数同在, 说明是要获取所有start_with之前的数据
if params[:all] and params[:start_with]
@posts = @posts.where(:created_at.gte => Time.at(start_with.to_i))
else
@posts = @posts.limit(limit)
end
if !params[:all] and start_with
@posts = @posts.where(:created_at.lt => Time.at(start_with.to_i))
end
#update start_with
unless @posts.empty?
start_with = @posts[-1].created_at.to_i.to_s
end
respond_to do |format|
format.json do
render :json => {
posts: @posts.collect { |post| build_summary(post) },
start_with: start_with
}
end
format.html
end
end
private
def map
{
"life"=> "生活",
"tech"=> "技术",
"creator"=> "创业"
}
end
def build_summary(post)
{
title: post.title,
type: post.type_en,
created_at: format_date(post.created_at),
id: post.id.to_s,
liked_count: post.liked_count,
visited_count: post.visited_count,
labels: post.labels_content
}
@posts = Post.order(created_at: :desc)
end
end

View File

@ -19,9 +19,10 @@ class BlogsController < ApplicationController
def show
@post = Post.find(params[:id])
@post.visited
@prev = Post.where(:created_at.lt => @post.created_at).desc(:created_at).where(:id.ne => @post.id).first
@next = Post.where(:created_at.gt => @post.created_at).asc(:created_at).where(:id.ne => @post.id).first
@prev = Post.where('created_at < ?', @post.created_at).order(created_at: :desc).first
@prev = Post.where('created_at > ?', @post.created_at).order(created_at: :asc).first
@comments = @post.comments
@likes_count = @post.likes.count
respond_to do |format|
format.html
format.json

View File

@ -1,2 +1,5 @@
module ApplicationHelper
def format_date(time)
time.strftime('%Y-%m-%d')
end
end

View File

@ -2,14 +2,11 @@
= simple_form_for(@post, url: url, html: {novalidate: '' }) do |f|
.row
.large-6.columns
= f.input :title, label: t('admin.posts_attributes.title'), "ng-model"=>"title", input_html: { name: 'title' }
.row
.small-6.large-3.columns
= f.input :type, :as=>:select, :collection=> [ Post::TECH, Post::LIFE, Post::CREATOR ], label: t('admin.posts_attributes.type'), "ng-model"=>"type", input_html: { name: 'type' }
= f.input :title, label: t('admin.posts_attributes.title'), input_html: { name: 'title' }
.row
.small-12.large-6.columns
= label_tag :labels, t('admin.posts_attributes.labels')
= text_field_tag :labels, @post.labels_content(true), "ng-model"=>"labels", "ng-initial" => ''
= text_field_tag :labels, @post.labels_content(true)
.row
.small-12.columns
@ -17,22 +14,23 @@
| #{t('admin.posts_attributes.already_labels')}
span
- Label.all.each do |label|
a.tag href="#" ng-click="addTag($event)" #{label.name}
a.tag href="#" #{label.name}
/ tabs and upload file field
dl.tabs
dd ng-class="{ active: body_active }"
a href="" ng-click="changeToBody()" #{t('admin.posts_attributes.content')}
dd ng-class="{ active: !body_active }"
a href="#" ng-click="changeToPreview()" #{t('admin.posts_attributes.preview')}
ul.tabs#tabs data-tabs=''
li.tabs-title.is-active
a href="#content" #{t('admin.posts_attributes.content')}
li.tabs-title
a href="#preview" #{t('admin.posts_attributes.preview')}
= link_to t('admin.posts_attributes.upload_photo'), "#", :id=>'upload_photo'
input[type="file" style="display: none;"]
.content-field ng-show="body_active" ng-model= 'content'
= f.input :content, :as=> :text, :label => false, input_html: { name: 'content', "ng-model"=>'content', 'ng-initial'=>'' }
.tabs-content data-tabs-content='tabs'
.tabs-panel.content-field.is-active#content
= f.input :content, :as=> :text, :label => false, input_html: { name: 'content', id: 'content-input' }
.preview.markdown ng-hide="body_active" ng-bind-html=" trustAsPreviewHTML() "
.tabs-panel.preview.markdown#preview
.row
.small-12.large-6.columns.posts-button
button #{t('admin.posts_attributes.submit')}
button.button type='submit' #{t('admin.posts_attributes.submit')}

View File

@ -15,8 +15,6 @@
td.admin-post-summary-field
i.fi-calendar
span #{format_time(post.created_at)}
i.fi-list
span #{ post.type_en }
i.fi-pricetag-multiple
span #{ post.labels_content }
i.fi-torsos
@ -27,3 +25,4 @@
= link_to t('comment'), admin_post_comments_path(post.id), class: 'edit-post-link'
= link_to t('edit'), edit_admin_post_path(post), class: 'edit-post-link'
= link_to t('destroy'), admin_post_path(post), method: 'DELETE', 'data-confirm' => '确认删除?'
== paginate @posts

View File

@ -1,16 +1,12 @@
.row ng-controller="AdminSessionsController"
.small-12.large-8.columns
h3.blog-title #{t('admin.session.title')}
form ng-submit="login()"
= form_tag admin_sessions_path
.row
.small-12.large-8.columns
= label_tag 'username', t('admin.session.username')
= text_field_tag 'username', nil, placeholder: t('admin.session.username_placeholder'), "ng-model"=>"username"
= text_field_tag 'username', params[:username], placeholder: t('admin.session.username_placeholder')
= label_tag 'username', t('admin.session.password')
= password_field_tag 'password', nil, placeholder: t('admin.session.password_placeholder'), "ng-model"=>"password"
= password_field_tag 'password', params[:password], placeholder: t('admin.session.password_placeholder')
p
.alert-box.warning ng-show=" error_msg "
|{{ error_msg }}
button #{t('admin.session.login_button')}
button.button type='submit' #{t('admin.session.login_button')}

View File

@ -1,30 +1,21 @@
- content_for(:title) do
| #{@type ? @type : t('title.timeline')}
.row ng-controller="ArchivesController" ng-cloak=""
| #{t('title.timeline')}
.row
.small-12.large-9.large-centered.columns
ul.archives-field ng-model="type" ng-init=" type= '#{@type}' "
li ng-repeat=" post in posts "
a.blog-title ng-href="{{ visit(post.id) }}"
|{{ post.title }}
ul.archives-field
- @posts.each do |post|
li
= link_to post.title, blog_path(post), class: 'blog-title'
p.tags-field
i.fi-calendar
span
|{{ post.created_at }}
i.fi-list-thumbnails
span
|{{ post.type }}
= format_date(post.created_at)
i.fi-pricetag-multiple
span
|{{ post.labels }}
= post.labels_content
i.fi-torsos
span
|{{ post.visited_count }}
= post.visited_count
i.fi-heart
span
|{{ post.liked_count }}
.no-more-field
p ng-show="no_more_flag" #{t('nocontent')}
.load-more
button.small ng-click="load()" ng-show="!loading_flag" Load More
button.small ng-show="loading_flag" Loading
= post.liked_count

View File

@ -1,21 +1,22 @@
.row ng-controller="CommentsController" ng-cloak=""
.row
.small-12.large-9.large-centered.columns
form novalidate='' name='form'
= form_for Comment.new, url: blog_comments_path(@post) do |f|
.row
.small-12.large-12.columns
= text_area_tag(:content, nil, placeholder: t('comment_placeholder.content'), 'ng-model'=> 'content', 'ng-required'=> true)
= f.text_area :content, placeholder: t('comment_placeholder.content')
.row
.small-12.large-6.columns
= text_field_tag(:name, nil, placeholder: t('comment_placeholder.name'), 'ng-model'=> 'name', 'ng-required'=> 'true')
= text_field_tag(:email, nil, placeholder: t('comment_placeholder.email'), 'ng-model'=> 'email', 'ng-pattern'=>"/^.+@.+$/", 'ng-required'=>"true")
button.comment-submit ng-click="submit()" ng-disabled="form.$invalid || submitting" {{ submitting && "#{t('comment_placeholder.submitting')}" || "#{t('comment_placeholder.submit')}" }}
p.comment-success ng-show="publish_success" #{t('comment_placeholder.publish_success')}
p.comment-fail ng-show="publish_success == false" #{t('comment_placeholder.publish_fail')}: {{ publish_fail_msg }}
= f.text_field :name, placeholder: t('comment_placeholder.name')
= f.text_field :email, placeholder: t('comment_placeholder.email')
button.button.comment-submit type='submit' #{t('comment_placeholder.submit')}
.comment-diag
.comment-wrapper ng-repeat=" comment in comments "
.comment-wrapper
- comments.each do |comment|
p.name
|{{ comment.name + " • " }}
| #{comment.name}
| " • "
span.created-at
|{{ comment.created_at }}
| #{comment.created_at }
/ ignore "white-space: pre" 's effect
<p class=comment-content>{{ comment.content }}</p>
<p class=comment-content>#{comment.content}</p>

View File

@ -8,21 +8,15 @@ p.ptag.published-at
span #{format_date(post.created_at)}
= render 'common/copyright'
hr.blog-over
p ng-controller="LikesController" ng-cloak=""
button.like-button ng-show="! is_liked " ng-click="submit()"
|{{ count }}
span Like
button.like-button.liked ng-show=" is_liked " ng-click="cancel()"
|{{ count }}
span Like
div ng-controller = "QRCodesController"
p
button.button.like-button type='button'
| #{@likes_count} Like
.qrcode
a href="#" ng-model="qrcode" ng-init="qrcode=false" ng-click="show()"
a#qrcode-link href="#"
i.fi-link
| #{t('qr_code')}
.social-share ng-show='qrcode'
.social-share
.qrcode-wrapper
= render partial: "qrcode", locals: { str: blog_url(post) }

View File

@ -1,9 +1,6 @@
/ require: locals: { post : post }
h2.blog-title #{post.title}
p.ptag
span
i.fi-list-thumbnails
span #{post.type_en}
span
i.fi-pricetag-multiple
span #{post.labels_content}

View File

@ -0,0 +1,7 @@
/ Non-link tag that stands for skipped pages...
- available local variables
current_page : a page object for the currently displayed page
total_pages : total number of pages
per_page : number of items to fetch per page
remote : data-remote
li.ellipsis

View File

@ -0,0 +1,10 @@
/ Link to the "Last" page
- available local variables
url : url to the last page
current_page : a page object for the currently displayed page
total_pages : total number of pages
per_page : number of items to fetch per page
remote : data-remote
span.last
== link_to_unless current_page.last?, t('views.pagination.last').html_safe, url, :remote => remote
'

View File

@ -0,0 +1,10 @@
/ Link to the "Next" page
- available local variables
url : url to the next page
current_page : a page object for the currently displayed page
total_pages : total number of pages
per_page : number of items to fetch per page
remote : data-remote
li.pagination-next class="#{'disabled' if current_page.last?}"
== link_to_unless current_page.last?, t('views.pagination.next').html_safe, url, :rel => 'next', :remote => remote
'

View File

@ -0,0 +1,11 @@
/ Link showing page number
- available local variables
page : a page object for "this" page
url : url to this page
current_page : a page object for the currently displayed page
total_pages : total number of pages
per_page : number of items to fetch per page
remote : data-remote
li class="page#{' current' if page.current?}"
== link_to_unless page.current?, page, url, {:remote => remote, :rel => page.rel}
'

View File

@ -0,0 +1,17 @@
/ The container tag
- available local variables
current_page : a page object for the currently displayed page
total_pages : total number of pages
per_page : number of items to fetch per page
remote : data-remote
paginator : the paginator that renders the pagination tags inside
== paginator.render do
ul.pagination
== prev_page_tag
- each_page do |page|
- if page.left_outer? || page.right_outer? || page.inside_window?
== page_tag page
- elsif !page.was_truncated?
== gap_tag
== next_page_tag

View File

@ -0,0 +1,10 @@
/ Link to the "Previous" page
- available local variables
url : url to the previous page
current_page : a page object for the currently displayed page
total_pages : total number of pages
per_page : number of items to fetch per page
remote : data-remote
li.pagination-previous class="#{'disabled' if current_page.first?}"
== link_to_unless current_page.first?, t('views.pagination.previous').html_safe, url, :rel => 'prev', :remote => remote
'

View File

@ -10,7 +10,7 @@ html
= favicon_link_tag 'favicon.png', type: 'image/png'
= javascript_include_tag "application"
= csrf_meta_tags
body
body data-whatinput="mouse"
- if content_for?(:main)
= yield(:main)
- else

View File

@ -0,0 +1,10 @@
Kaminari.configure do |config|
config.default_per_page = 10
# config.max_per_page = nil
# config.window = 4
# config.outer_window = 0
# config.left = 0
# config.right = 0
# config.page_method_name = :page
# config.param_name = :page
end

View File

@ -25,7 +25,7 @@ WBlog::Application.routes.draw do
get '/qrcodes' => 'qrcodes#show'
namespace :admin do
resources :posts do
resources :posts, except: [:show] do
collection do
post :preview
end

View File

@ -0,0 +1,8 @@
class CreateLabelsPosts < ActiveRecord::Migration[5.0]
def change
create_table :labels_posts, id: false do |t|
t.integer :label_id
t.integer :post_id
end
end
end

View File

@ -0,0 +1,5 @@
class ChangeColumnDefaultVisitedCountToPosts < ActiveRecord::Migration[5.0]
def change
change_column_default :posts, :visited_count, 0
end
end

View File

@ -0,0 +1,5 @@
class RenameColumnContentToComments < ActiveRecord::Migration[5.0]
def change
rename_column :comments, :conent, :content
end
end

View File

@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160420082909) do
ActiveRecord::Schema.define(version: 20160421062614) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -19,7 +19,7 @@ ActiveRecord::Schema.define(version: 20160420082909) do
create_table "comments", force: :cascade do |t|
t.string "name"
t.string "email"
t.text "conent"
t.text "content"
t.integer "post_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
@ -31,6 +31,11 @@ ActiveRecord::Schema.define(version: 20160420082909) do
t.datetime "updated_at", null: false
end
create_table "labels_posts", id: false, force: :cascade do |t|
t.integer "label_id"
t.integer "post_id"
end
create_table "likes", force: :cascade do |t|
t.integer "post_id"
t.datetime "created_at", null: false
@ -46,7 +51,7 @@ ActiveRecord::Schema.define(version: 20160420082909) do
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "content"
t.integer "visited_count"
t.integer "visited_count", default: 0
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end