pull/35/head
sunface 6 years ago
parent 636082faab
commit ca863975d0

@ -18,6 +18,7 @@ func apiHandler(e *echo.Echo) {
e.GET("/web/article/detail", post.GetArticleDetail) e.GET("/web/article/detail", post.GetArticleDetail)
e.GET("/web/article/beforeEdit", post.BeforeEditAr, user.CheckSignIn) e.GET("/web/article/beforeEdit", post.BeforeEditAr, user.CheckSignIn)
e.POST("/web/article/saveChanges", post.SaveArticleChanges, user.CheckSignIn) e.POST("/web/article/saveChanges", post.SaveArticleChanges, user.CheckSignIn)
e.POST("/article/like", post.ArticleLike, user.CheckSignIn)
// comment apis // comment apis
e.POST("/web/comment/create", post.Comment, user.CheckSignIn) e.POST("/web/comment/create", post.Comment, user.CheckSignIn)

@ -3,8 +3,10 @@ package post
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"strconv"
"time" "time"
"github.com/gocql/gocql"
"github.com/labstack/echo" "github.com/labstack/echo"
"github.com/thinkindev/im.dev/internal/ecode" "github.com/thinkindev/im.dev/internal/ecode"
"github.com/thinkindev/im.dev/internal/misc" "github.com/thinkindev/im.dev/internal/misc"
@ -26,8 +28,8 @@ type ArContent struct {
// NewArticle create a new article // NewArticle create a new article
func NewArticle(c echo.Context) error { func NewArticle(c echo.Context) error {
opType := c.FormValue("type") opType, _ := strconv.Atoi(c.FormValue("type"))
if opType != "1" && opType != "2" { if opType != PostDraft && opType != PostPublished {
return c.JSON(http.StatusBadRequest, misc.HTTPResp{ return c.JSON(http.StatusBadRequest, misc.HTTPResp{
ErrCode: ecode.ParamInvalid, ErrCode: ecode.ParamInvalid,
Message: ecode.ParamInvalidMsg, Message: ecode.ParamInvalidMsg,
@ -52,8 +54,16 @@ func NewArticle(c echo.Context) error {
// modify render // modify render
ar.Render = modify(ar.Render) ar.Render = modify(ar.Render)
err = misc.CQL.Query(`INSERT INTO article (id,uid,title,tags,md,render,status,edit_date,lang) words := countWords(ar.MD)
VALUES (?,?,?,?,?,?,?,?,?)`, ar.ID, sess.ID, ar.Title, ar.Tags, ar.MD, ar.Render, opType, time.Now().Unix(), ar.Lang).Exec() var q *gocql.Query
if opType == PostDraft {
q = misc.CQL.Query(`INSERT INTO article (id,uid,title,tags,md,render,words,status,edit_date,lang)
VALUES (?,?,?,?,?,?,?,?,?,?)`, ar.ID, sess.ID, ar.Title, ar.Tags, ar.MD, ar.Render, words, PostDraft, time.Now().Unix(), ar.Lang)
} else { // publish
q = misc.CQL.Query(`INSERT INTO article (id,uid,title,tags,md,render,words,status,publish_date,edit_date,lang)
VALUES (?,?,?,?,?,?,?,?,?,?,?)`, ar.ID, sess.ID, ar.Title, ar.Tags, ar.MD, ar.Render, words, PostPublished, time.Now().Unix(), 0, ar.Lang)
}
err = q.Exec()
if err != nil { if err != nil {
misc.Log.Warn("access database error", zap.Error(err)) misc.Log.Warn("access database error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{ return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
@ -79,6 +89,9 @@ type ArticleDetail struct {
PublishDate string `json:"publish_date"` PublishDate string `json:"publish_date"`
EditDate string `json:"edit_date"` EditDate string `json:"edit_date"`
Lang string `json:"lang"` Lang string `json:"lang"`
Words int `json:"words"`
Likes int `json:"likes"` // all likes of this article
Liked bool `json:"liked"` // current user liked this article
pubDate int64 pubDate int64
editDate int64 editDate int64
} }
@ -94,8 +107,8 @@ func GetArticleDetail(c echo.Context) error {
} }
detail := &ArticleDetail{ID: arID} detail := &ArticleDetail{ID: arID}
err := misc.CQL.Query(`SELECT uid,title,tags,render,status,publish_date,edit_date,lang FROM article WHERE id=?`, arID).Scan( err := misc.CQL.Query(`SELECT uid,title,tags,render,words,status,publish_date,edit_date,lang FROM article WHERE id=?`, arID).Scan(
&detail.UID, &detail.Title, &detail.Tags, &detail.Render, &detail.Status, &detail.pubDate, &detail.editDate, &detail.Lang, &detail.UID, &detail.Title, &detail.Tags, &detail.Render, &detail.Words, &detail.Status, &detail.pubDate, &detail.editDate, &detail.Lang,
) )
if err != nil { if err != nil {
if err.Error() == misc.CQLNotFound { if err.Error() == misc.CQLNotFound {
@ -112,11 +125,26 @@ func GetArticleDetail(c echo.Context) error {
} }
if detail.pubDate != 0 { if detail.pubDate != 0 {
detail.PublishDate = utils.Time2ReadableString(time.Unix(detail.pubDate, 0)) detail.PublishDate = utils.Time2EnglishString(time.Unix(detail.pubDate, 0))
} }
if detail.editDate != 0 { if detail.editDate != 0 {
detail.EditDate = utils.Time2ReadableString(time.Unix(detail.editDate, 0)) detail.EditDate = utils.Time2EnglishString(time.Unix(detail.editDate, 0))
}
// if user signin, get his liked about this article
sess := user.GetSession(c)
if sess != nil {
var date int64
q := misc.CQL.Query("SELECT input_date FROM post_like WHERE post_id=? and uid=?", arID, sess.ID)
q.Scan(&date)
if date != 0 {
detail.Liked = true
} }
}
// get how many user like this article
misc.CQL.Query("SELECT likes FROM post_counter WHERE id=?", arID).Scan(&detail.Likes)
return c.JSON(http.StatusOK, misc.HTTPResp{ return c.JSON(http.StatusOK, misc.HTTPResp{
Data: detail, Data: detail,
}) })
@ -215,8 +243,9 @@ func SaveArticleChanges(c echo.Context) error {
// modify render // modify render
ar.Render = modify(ar.Render) ar.Render = modify(ar.Render)
err = misc.CQL.Query(`UPDATE article SET title=?,tags=?,md=?,render=?,edit_date=?,lang=? WHERE id=?`, words := countWords(ar.MD)
ar.Title, ar.Tags, ar.MD, ar.Render, time.Now().Unix(), ar.Lang, ar.ID).Exec() err = misc.CQL.Query(`UPDATE article SET title=?,tags=?,md=?,render=?,words=?,edit_date=?,lang=? WHERE id=?`,
ar.Title, ar.Tags, ar.MD, ar.Render, words, time.Now().Unix(), ar.Lang, ar.ID).Exec()
if err != nil { if err != nil {
misc.Log.Warn("access database error", zap.Error(err)) misc.Log.Warn("access database error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{ return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
@ -239,3 +268,85 @@ func saveTags(arID string, tags []string) {
} }
} }
} }
// ArticleLike means a user like this article
func ArticleLike(c echo.Context) error {
id := c.FormValue("id")
tp := c.FormValue("type")
if id == "" || (tp != "1" && tp != "2") {
return c.JSON(http.StatusBadRequest, misc.HTTPResp{
ErrCode: ecode.ParamInvalid,
Message: ecode.ParamInvalidMsg,
})
}
sess := user.GetSession(c)
// check already liked
var pid string
q := misc.CQL.Query("SELECT post_id FROM post_like WHERE post_id=? and uid=?", id, sess.ID)
err := q.Scan(&pid)
if err != nil {
if err.Error() != misc.CQLNotFound {
misc.Log.Warn("access database error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.DatabaseError,
Message: ecode.CommonErrorMsg,
})
}
}
if tp == "1" { // like
if pid == id {
misc.Log.Info("someone is try to attack imdev server", zap.String("remote_ip", c.RealIP()))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.DatabaseError,
Message: ecode.CommonErrorMsg,
})
}
q = misc.CQL.Query("INSERT INTO post_like (post_id,uid,type,input_date) VALUES (?,?,?,?)",
id, sess.ID, OpPostLike, time.Now().Unix())
err = q.Exec()
if err != nil {
misc.Log.Warn("access database error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.DatabaseError,
Message: ecode.CommonErrorMsg,
})
}
q = misc.CQL.Query("UPDATE post_counter SET likes=likes + 1 WHERE id=?", id)
err = q.Exec()
if err != nil {
misc.Log.Warn("access database error", zap.Error(err))
}
} else { // cancel like
if pid != id {
misc.Log.Info("someone is try to attack imdev server", zap.String("remote_ip", c.RealIP()))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.DatabaseError,
Message: ecode.CommonErrorMsg,
})
}
q = misc.CQL.Query("DELETE FROM post_like WHERE post_id=? and uid=?",
id, sess.ID)
err = q.Exec()
if err != nil {
misc.Log.Warn("access database error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, misc.HTTPResp{
ErrCode: ecode.DatabaseError,
Message: ecode.CommonErrorMsg,
})
}
q = misc.CQL.Query("UPDATE post_counter SET likes=likes - 1 WHERE id=?", id)
err = q.Exec()
if err != nil {
misc.Log.Warn("access database error", zap.Error(err))
}
}
return c.JSON(http.StatusOK, misc.HTTPResp{})
}

@ -15,10 +15,15 @@ const (
) )
const ( const (
// OpCommentLike means a user like a post // OpCommentLike means a user like a comment
OpCommentLike = 1 OpCommentLike = 1
// OpCommentDislike means a user dislike a post // OpCommentDislike means a user dislike a comment
OpCommentDislike = 2 OpCommentDislike = 2
// OpPostLike means a user like a post
OpPostLike = 1
// OpPostDislike means a user dislike a post
OpPostDislike = 2
) )
const ( const (

@ -1 +1,74 @@
package post package post
import "strings"
// 空格先行分割
//1.数字和英文字母 遇到2类字符或者空格计数一
//2.其余字符计数一
// 这段代码因为考虑各种情况,略复杂,后续我会整理下
// @todo
func countWords(md string) int {
mds := strings.Split(md, " ")
count := 0
//上一个字符是否是特殊字符(非英文字母和数字)
special := false
lastCountSpecial := false
var last rune
var old rune
isfirst := false
for _, words := range mds {
last = rune(0)
special = false
lastCountSpecial = false
isfirst = true
for _, r := range words {
if special {
//如果上一个字符是特殊字符,那么当前字符无论是什么,都计数+1
count++
lastCountSpecial = true
} else {
//如果是首字符,+1
if isfirst {
count++
} else {
//如果上一个字符不是特殊字符,那么当前字符必须是特殊字符才能计数+1否则认为是连续的
if !isNumber(r) && !isAlpha(r) {
count++
}
}
if isNumber(r) || isAlpha(r) {
lastCountSpecial = false
}
}
//判断当前字符是否是特殊字符
special = !isNumber(r) && !isAlpha(r)
old = last
last = r
isfirst = false
}
//如果当前非特殊字符是最后一个字符,那么计数+1
if !special && lastCountSpecial && (isNumber(old) || isAlpha(old)) {
count++
}
}
return count
}
func isNumber(c rune) bool {
if c >= 48 && c <= 57 {
return true
}
return false
}
func isAlpha(c rune) bool {
if (c >= 65 && c <= 90) || (c >= 97 && c <= 122) {
return true
}
return false
}

@ -6,14 +6,6 @@ import (
"time" "time"
) )
// Time2ReadableString converts time to readable string
// 1分钟之内显示xx秒前
// 1小时之内显示XX分钟前
// 24小时之内显示xx小时前
// 昨天 x:x
// 前天 x:x
// 同一年显示x月xx
// 不同年显示xx.xx.xx
func Time2ReadableString(t time.Time) string { func Time2ReadableString(t time.Time) string {
now := time.Now().Local() now := time.Now().Local()
intv := now.Unix() - t.Unix() intv := now.Unix() - t.Unix()
@ -36,9 +28,35 @@ func Time2ReadableString(t time.Time) string {
return fmt.Sprintf("yestoday %02d:%02d", h, m) return fmt.Sprintf("yestoday %02d:%02d", h, m)
} }
if y1 == y2 { return Time2EnglishString(t)
return fmt.Sprintf("%02d.%02d", m2, d2) }
func Time2String(t time.Time) string {
return t.Format("2006-01-02 15:04:05.999")
}
var months = map[int]string{
1: "Jan",
2: "Feb",
3: "Mar",
4: "Apr",
5: "May",
6: "Jun",
7: "Jul",
8: "Aug",
9: "Sep",
10: "Oct",
11: "Nov",
12: "Dec",
}
func Time2EnglishString(t time.Time) string {
now := time.Now()
// 检查是否是同一年
y, m, d := t.Date()
if now.Year() == y {
return fmt.Sprintf("%s %d", months[int(m)], d)
} }
return t.Format("06.1.2") return fmt.Sprintf("%s %d,%d", months[int(m)], d, y)
} }

@ -40,6 +40,7 @@ CREATE TABLE IF NOT EXISTS article (
md text, -- markdown md text, -- markdown
render text, -- rendered html render text, -- rendered html
status tinyint, -- 1. draft 2. published 3. delete status tinyint, -- 1. draft 2. published 3. delete
words int, -- word count
publish_date bigint, publish_date bigint,
edit_date bigint, edit_date bigint,
@ -80,17 +81,6 @@ CREATE CUSTOM INDEX IF NOT EXISTS ON comment (post_id)
USING 'org.apache.cassandra.index.sasi.SASIIndex' ; USING 'org.apache.cassandra.index.sasi.SASIIndex' ;
CREATE TABLE IF NOT EXISTS post_counter (
id text, -- post id
comments counter,
likes counter,
recommands counter,
PRIMARY KEY (id)
) WITH gc_grace_seconds = 10800;
CREATE TABLE IF NOT EXISTS comment_counter ( CREATE TABLE IF NOT EXISTS comment_counter (
id text, -- comment id id text, -- comment id
likes counter, likes counter,
@ -107,3 +97,25 @@ CREATE TABLE IF NOT EXISTS comment_like (
) WITH gc_grace_seconds = 10800; ) WITH gc_grace_seconds = 10800;
CREATE CUSTOM INDEX IF NOT EXISTS ON comment_like (uid) CREATE CUSTOM INDEX IF NOT EXISTS ON comment_like (uid)
USING 'org.apache.cassandra.index.sasi.SASIIndex' ; USING 'org.apache.cassandra.index.sasi.SASIIndex' ;
CREATE TABLE IF NOT EXISTS post_counter (
id text, -- post id
comments counter,
likes counter,
recommands counter,
PRIMARY KEY (id)
) WITH gc_grace_seconds = 10800;
-- record whether a user likes a post
CREATE TABLE IF NOT EXISTS post_like (
post_id text, -- post id
uid text, -- user id
type tinyint, -- 1: like 2: dislike
input_date bigint,
PRIMARY KEY (post_id,uid)
) WITH gc_grace_seconds = 10800;
CREATE CUSTOM INDEX IF NOT EXISTS ON post_like (uid)
USING 'org.apache.cassandra.index.sasi.SASIIndex' ;

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" fill="rgb(135, 138, 140)"><path d="M757.76 637.44l-218.88 245.76c-14.72 16.64-40.32 16.64-54.4 0L265.6 637.44C244.48 613.76 261.12 576 293.12 576l437.76 0C762.24 576 779.52 613.76 757.76 637.44z" /></svg>

Before

Width:  |  Height:  |  Size: 462 B

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" fill="rgb(135, 138, 140)"><path d="M758.4 385.92 539.52 140.16c-14.72-16.64-40.32-16.64-54.4 0L266.24 385.92C244.48 409.6 261.76 448 293.12 448l437.76 0C762.88 448 779.52 409.6 758.4 385.92z" /></svg>

Before

Width:  |  Height:  |  Size: 458 B

@ -0,0 +1,12 @@
<svg
id="clap--icon"
xmlns="http://www.w3.org/2000/svg"
viewBox="-549 338 100.1 125"
>
<path
d="M-471.2 366.8c1.2 1.1 1.9 2.6 2.3 4.1.4-.3.8-.5 1.2-.7 1-1.9.7-4.3-1-5.9-2-1.9-5.2-1.9-7.2.1l-.2.2c1.8.1 3.6.9 4.9 2.2zm-28.8 14c.4.9.7 1.9.8 3.1l16.5-16.9c.6-.6 1.4-1.1 2.1-1.5 1-1.9.7-4.4-.9-6-2-1.9-5.2-1.9-7.2.1l-15.5 15.9c2.3 2.2 3.1 3 4.2 5.3zm-38.9 39.7c-.1-8.9 3.2-17.2 9.4-23.6l18.6-19c.7-2 .5-4.1-.1-5.3-.8-1.8-1.3-2.3-3.6-4.5l-20.9 21.4c-10.6 10.8-11.2 27.6-2.3 39.3-.6-2.6-1-5.4-1.1-8.3z"
/>
<path
d="M-527.2 399.1l20.9-21.4c2.2 2.2 2.7 2.6 3.5 4.5.8 1.8 1 5.4-1.6 8l-11.8 12.2c-.5.5-.4 1.2 0 1.7.5.5 1.2.5 1.7 0l34-35c1.9-2 5.2-2.1 7.2-.1 2 1.9 2 5.2.1 7.2l-24.7 25.3c-.5.5-.4 1.2 0 1.7.5.5 1.2.5 1.7 0l28.5-29.3c2-2 5.2-2 7.1-.1 2 1.9 2 5.1.1 7.1l-28.5 29.3c-.5.5-.4 1.2 0 1.7.5.5 1.2.4 1.7 0l24.7-25.3c1.9-2 5.1-2.1 7.1-.1 2 1.9 2 5.2.1 7.2l-24.7 25.3c-.5.5-.4 1.2 0 1.7.5.5 1.2.5 1.7 0l14.6-15c2-2 5.2-2 7.2-.1 2 2 2.1 5.2.1 7.2l-27.6 28.4c-11.6 11.9-30.6 12.2-42.5.6-12-11.7-12.2-30.8-.6-42.7m18.1-48.4l-.7 4.9-2.2-4.4m7.6.9l-3.7 3.4 1.2-4.8m5.5 4.7l-4.8 1.6 3.1-3.9"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -1,6 +1,8 @@
// overide global focus style // overide global focus style
h1 { h1 {
font-size: 36px; font-size: 36px;
margin-block-start: .4em;
margin-block-end: .4em;
} }
h3 { h3 {

@ -95,8 +95,8 @@
outline: 1px solid transparent; outline: 1px solid transparent;
border-radius: 50%; border-radius: 50%;
border: 1px solid #bdc3c7; border: 1px solid #bdc3c7;
width: 60px; width: 40px;
height: 60px; height: 40px;
background: none; background: none;
} }
.clap:after { .clap:after {
@ -106,8 +106,8 @@
left: 0; left: 0;
display: block; display: block;
border-radius: 50%; border-radius: 50%;
width: 59px; width: 39px;
height: 59px; height: 39px;
} }
.clap:hover { .clap:hover {
cursor: pointer; cursor: pointer;
@ -118,7 +118,7 @@
animation: shockwave 1s ease-in infinite; animation: shockwave 1s ease-in infinite;
} }
.clap svg { .clap svg {
width: 30px; width: 20px;
fill: none; fill: none;
stroke: #333; stroke: #333;
stroke-width: 1px; stroke-width: 1px;
@ -236,18 +236,7 @@
border-top: 1px solid #ddd border-top: 1px solid #ddd
} }
.content h1,
.content h2,
.content h3,
.content h4,
.content h5,
.content h6 {
margin: 0 0 12px;
font-weight: 700;
color: #2f2f2f;
line-height: 1.6;
text-rendering: optimizelegibility
}
.content h1 { .content h1 {
font-size: 26px font-size: 26px

@ -1,4 +1,4 @@
@grey-color: rgb(124,124,124); @grey-color: rgba(0,0,0,.54);
@light-grey-color: rgb(135, 138, 140); @light-grey-color: rgb(135, 138, 140);
@main-color: #303133; @main-color: #303133;

@ -7,14 +7,14 @@
.margin-right-5 {margin-right: 5px};.margin-right-10 {margin-right: 10px};.margin-right-15 {margin-right: 15px};.margin-right-20 {margin-right: 20px};.margin-right-30 {margin-right: 30px};.margin-right-40 {margin-right: 40px}; .margin-right-5 {margin-right: 5px};.margin-right-10 {margin-right: 10px};.margin-right-15 {margin-right: 15px};.margin-right-20 {margin-right: 20px};.margin-right-30 {margin-right: 30px};.margin-right-40 {margin-right: 40px};
.margin-top-2 {margin-top: 2px};.margin-top-5 {margin-top: 5px};.margin-top-8 {margin-top: 8px};.margin-top-10 {margin-top: 10px};.margin-top-15 {margin-top: 15px};.margin-top-20 {margin-top: 20px};.margin-top-25 {margin-top: 25px};.margin-top-30 {margin-top: 30px};.margin-top-40 {margin-top: 40px};.margin-top-60 {margin-top: 60px};.margin-top-80 {margin-top: 80px};.margin-top-100 {margin-top: 100px}; .margin-top-2 {margin-top: 2px};.margin-top-5 {margin-top: 5px};.margin-top-8 {margin-top: 8px};.margin-top-10 {margin-top: 10px};.margin-top-15 {margin-top: 15px};.margin-top-20 {margin-top: 20px};.margin-top-25 {margin-top: 25px};.margin-top-30 {margin-top: 30px};.margin-top-40 {margin-top: 40px};.margin-top-60 {margin-top: 60px};.margin-top-80 {margin-top: 80px};.margin-top-100 {margin-top: 100px};
.margin-top--35 {margin-top: -35px};.margin-top--40 {margin-top: -40px};.margin-top--50 {margin-top: -50px}; .margin-top--30 {margin-top: -30px};.margin-top--35 {margin-top: -35px};.margin-top--40 {margin-top: -40px};.margin-top--50 {margin-top: -50px};
.margin-bottom-5 {margin-bottom: 5px};.margin-bottom-10 {margin-bottom: 10px};.margin-bottom-15 {margin-bottom: 15px};.margin-bottom-20 { margin-bottom: 20px}.margin-bottom-30 {margin-bottom: 30px };.margin-bottom-40 {margin-bottom: 40px };.margin-bottom-50 {margin-bottom: 50px }; .margin-bottom-5 {margin-bottom: 5px};.margin-bottom-10 {margin-bottom: 10px};.margin-bottom-15 {margin-bottom: 15px};.margin-bottom-20 { margin-bottom: 20px}.margin-bottom-30 {margin-bottom: 30px };.margin-bottom-40 {margin-bottom: 40px };.margin-bottom-50 {margin-bottom: 50px };
/* ----------------------------padding------------------------ */ /* ----------------------------padding------------------------ */
.padding-5 {padding: 5px 5px};.padding-10 { padding: 10px 10px};.padding-20 {padding: 20px 20px}; .padding-5 {padding: 5px 5px};.padding-10 { padding: 10px 10px};.padding-20 {padding: 20px 20px};
.padding-left-5 {padding-left: 5px};.padding-left-10 {padding-left: 10px};.padding-left-15 {padding-left: 15px};.padding-left-20 {padding-left: 20px}; .padding-left-5 {padding-left: 5px};.padding-left-8 {padding-left: 8px};.padding-left-10 {padding-left: 10px};.padding-left-15 {padding-left: 15px};.padding-left-20 {padding-left: 20px};
.padding-right-5 {padding-right: 5px}.padding-right-10 {padding-right: 10px};.padding-right-15 {padding-right: 15px};.padding-right-20 {padding-right: 20px}; .padding-right-5 {padding-right: 5px}.padding-right-10 {padding-right: 10px};.padding-right-15 {padding-right: 15px};.padding-right-20 {padding-right: 20px};
@ -23,9 +23,9 @@
.padding-bottom-5 { padding-bottom: 5px};.padding-bottom-10 {padding-bottom: 10px};.padding-bottom-15 {padding-bottom: 15px};.padding-bottom-20 {padding-bottom: 20px};.padding-bottom-30 {padding-bottom: 30px};.padding-bottom-40 {padding-bottom: 40px}; .padding-bottom-5 { padding-bottom: 5px};.padding-bottom-10 {padding-bottom: 10px};.padding-bottom-15 {padding-bottom: 15px};.padding-bottom-20 {padding-bottom: 20px};.padding-bottom-30 {padding-bottom: 30px};.padding-bottom-40 {padding-bottom: 40px};
/* ----------------------------height/width------------------------ */ /* ----------------------------height/width------------------------ */
.height-45{height:45px};.height-50{height:50px};.height-100{height:100px};.height-150{height:150px};.height-200{height:200px}; .height-40{height:40px};.height-45{height:45px};.height-50{height:50px};.height-100{height:100px};.height-150{height:150px};.height-200{height:200px};
.width-50{width:50px};.width-100{width:100px};.width-150{width:150px};.width-200{width:200px};.width-300{width:300px}; .width-40{width:40px};.width-50{width:50px};.width-100{width:100px};.width-150{width:150px};.width-200{width:200px};.width-300{width:300px};
.width-100p {width: 100%}; .width-100p {width: 100%};

@ -9,7 +9,7 @@
> >
<div class="squares position-fixed z-index-100 margin-top-100"> <div class="squares position-fixed z-index-100 margin-top-100">
<div class="square font-hover-primary"> <div class="square font-hover-primary">
<button id="clap" :class="{clap:true, liked: arliked}" @click="arLike"> <button id="clap" :class="{clap:true, liked: arDetail.liked}" @click="arLike">
<span> <span>
<svg <svg
id="clap--icon" id="clap--icon"
@ -25,6 +25,7 @@
</svg> </svg>
</span> </span>
</button> </button>
<span class="position-absolute meta-word font-size-13 likes margin-left-30 margin-top--30 font-weight-bold">{{arDetail.likes}}</span>
</div> </div>
<div v-show="arDetail.uid==this.$store.state.user.id" class="square cursor-pointer margin-top-20"> <div v-show="arDetail.uid==this.$store.state.user.id" class="square cursor-pointer margin-top-20">
<a> <a>
@ -54,7 +55,11 @@
:lg="{ span: 13, offset: 5 }" :lg="{ span: 13, offset: 5 }"
> >
<h1 class=" margin-top-30">{{arDetail.title}}</h1> <h1 class=" margin-top-30">{{arDetail.title}}</h1>
<render :content="arDetail.render" style="min-height:400px"></render> <div class="margin-left-5">
<span class="vertical-align-middle display-inline-block"><img :src="authorInfo.avatar" alt="" class="height-40 width-40"></span>
<span class="meta-word font-size-14 font-weight-500">{{arDetail.publish_date}}<span> · {{arDetail.words}} words</span> <span v-if="arDetail.edit_date!=''">· Updated on {{arDetail.edit_date}}</span></span>
</div>
<render :content="arDetail.render" style="min-height:400px" class="min-height-400 margin-top-30 padding-left-8"></render>
</el-col> </el-col>
<el-col :span="1" > <el-col :span="1" >
<UserCard class="user-card z-index-100 position-fixed margin-top-40 max-width-300" :user="authorInfo"></UserCard> <UserCard class="user-card z-index-100 position-fixed margin-top-40 max-width-300" :user="authorInfo"></UserCard>
@ -88,7 +93,6 @@ export default {
arID: "", // unique article id arID: "", // unique article id
arDetail: {}, arDetail: {},
bookmarked: false, bookmarked: false,
arliked: false,
authorInfo : {}, authorInfo : {},
}; };
}, },
@ -100,7 +104,20 @@ export default {
computed: {}, computed: {},
methods: { methods: {
arLike() { arLike() {
this.arliked = !this.arliked var tp = 1 // like
if (this.arDetail.liked) {
tp = 2 //dislike
}
request({
url: "/article/like",
method: "POST",
params: {
id: this.arID,
type: tp
}
}).then(_ => {
this.arDetail.liked = !this.arDetail.liked
});
} }
}, },
beforeDestroy() { beforeDestroy() {
@ -124,6 +141,7 @@ export default {
} }
}).then(res => { }).then(res => {
this.arDetail = res0.data.data; this.arDetail = res0.data.data;
console.log(this.arDetail)
this.authorInfo = res.data.data this.authorInfo = res.data.data
}); });
}); });

@ -74,6 +74,11 @@ export default {
} }
}, },
watch: { watch: {
"$store.state.user.id"() {
if (this.$store.state.user.id != '') {
this.init()
}
},
}, },
computed: { computed: {
}, },
@ -186,17 +191,8 @@ export default {
} else { } else {
this.tags = []; this.tags = [];
} }
}
},
mounted() {
if (this.mode == 'new') {
var ar = localStorage.getItem(this.localStoreID)
if (ar != null) {
this.tempArticle = JSON.parse(ar)
}
}
}, },
created() { init() {
this.arID = this.$route.params.arID; this.arID = this.$route.params.arID;
if (this.arID != undefined) { if (this.arID != undefined) {
this.mode = 'edit' this.mode = 'edit'
@ -216,6 +212,18 @@ export default {
}); });
} }
} }
}
},
mounted() {
if (this.mode == 'new') {
var ar = localStorage.getItem(this.localStoreID)
if (ar != null) {
this.tempArticle = JSON.parse(ar)
}
}
},
created() {
this.init()
}, },
} }
</script> </script>

@ -12,7 +12,7 @@
</div> </div>
<div class="margin-top-15 margin-left-10" v-html="user.about" v-if="user.about!='' && user.about!=undefined"> <div class="margin-top-15 margin-left-10" v-html="user.about" v-if="user.about!='' && user.about!=undefined">
</div> </div>
<div class="text-align-center padding-top-10 padding-bottom-10 font-weight-bold border-radius-3 cursor-pointer margin-top-10" :style="{'background-color':user.bg_color,'color':user.text_color}">FOLLOW</div> <div class="text-align-center padding-top-10 padding-bottom-10 font-weight-bold border-radius-3 cursor-pointer margin-top-10" :style="{'background-color':user.bg_color,'color':followColor()}">FOLLOW</div>
<div class="margin-top-10 margin-left-10" v-if="user.website!='' && user.website!=undefined"> <div class="margin-top-10 margin-left-10" v-if="user.website!='' && user.website!=undefined">
<div class="color-regular font-weight-500">website</div> <div class="color-regular font-weight-500">website</div>
@ -53,6 +53,12 @@ export default {
watch: { watch: {
}, },
methods: { methods: {
followColor() {
if (this.user.bg_color == '#000' && this.user.text_color=='#000') {
return '#fff'
}
return this.user.text_color
},
userHome() { userHome() {
this.$router.push('/' + this.user.name) this.$router.push('/' + this.user.name)
} }

@ -26,12 +26,12 @@
</div> </div>
<div class="margin-top-30"> <div class="margin-top-30">
<h4>Background color</h4> <h4>Background color</h4>
<el-color-picker class="position-absolute" size="small" v-model="user.bg_color"></el-color-picker> <el-color-picker @change="changBgColor" color-format="hex" class="position-absolute" size="small" v-model="user.bg_color"></el-color-picker>
<span class="meta-word font-size-12 margin-left-40">The background color used in your home page</span> <span class="meta-word font-size-12 margin-left-40">The background color used in your home page</span>
</div> </div>
<div class="margin-top-30 margin-bottom-50"> <div class="margin-top-30 margin-bottom-50">
<h4>Text color </h4> <h4>Text color </h4>
<el-color-picker class="position-absolute" size="small" v-model="user.text_color"></el-color-picker> <el-color-picker @change="changTextColor" color-format="hex" class="position-absolute" size="small" v-model="user.text_color"></el-color-picker>
<span class="meta-word font-size-12 margin-left-40">The text color used in your home page</span> <span class="meta-word font-size-12 margin-left-40">The text color used in your home page</span>
</div> </div>
@ -106,6 +106,16 @@ export default {
}, },
}, },
methods: { methods: {
changBgColor(val) {
if (val==null) {
this.user.bg_color='#000'
}
},
changTextColor(val) {
if (val==null) {
this.user.text_color='#000'
}
},
setProfile() { setProfile() {
request({ request({
url: "/user/profile/set", url: "/user/profile/set",

Loading…
Cancel
Save