列表頁

2.解決問題

超過三張圖片:橫排三張圖(選最后三張圖)
圖片少于3張:圖文左右排列(選最后一張圖)
無圖片:只顯示文章內(nèi)容(字?jǐn)?shù)控制)
圖文混排
文章表:id哎迄、u_id障本、title搪泳、content象对、create_time
圖片表:id蚕礼、a_id严卖、img_url
評論表:id席舍、u_id、a_id哮笆、content来颤、comment_time

3.關(guān)于數(shù)據(jù)庫文章內(nèi)容的樣式
<div>
<img src="http://hcoder.oss-cn-beijing.aliyuncs.com/public/images/xcp2.png" width="100%" />grace.hcoder.net<br />富文本可以展示html標(biāo)簽 _
</div>

4.后端
4.1 mapper

ArticleMapper接口,需要聲明如下方法

List<ArticleVO> selectAll();
ArticleVO getArticleById(int aId);

ImgMapper接口

List<Img> selectImgsByAId(int aId);

CommentMapper接口

List<CommentVO> selectCommentsByAId(int aId);

4.2 Service接口及實現(xiàn)稠肘、單元測試
4.3 Controller

ArticleController

package com.soft1721.jianyue.api.controller;

import com.aliyun.oss.OSSClient;
import com.soft1721.jianyue.api.entity.Article;
import com.soft1721.jianyue.api.entity.Img;
import com.soft1721.jianyue.api.entity.User;
import com.soft1721.jianyue.api.entity.vo.ArticleVO;
import com.soft1721.jianyue.api.entity.vo.CommentVO;
import com.soft1721.jianyue.api.service.ArticleService;
import com.soft1721.jianyue.api.service.CommentService;
import com.soft1721.jianyue.api.service.ImgService;
import com.soft1721.jianyue.api.util.ResponseResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.*;

@RestController
@RequestMapping(value = "/api/article")
public class ArticleController {
@Resource
private ArticleService articleService;
@Resource
private CommentService commentService;
@Resource
private ImgService imgService;

@GetMapping(value = "/list")
public ResponseResult getAll() {
    List<ArticleVO> articleList = articleService.selectAll();
    return ResponseResult.success(articleList);
}

@GetMapping(value = "/{aId}")
public ResponseResult getArticleById(@PathVariable("aId") int aId) {
    ArticleVO article = articleService.getArticleById(aId);
    List<CommentVO> comments = commentService.selectCommentsByAId(aId);
    Map<String,Object> map = new HashMap<>();
    map.put("article",article);
    map.put("comments",comments);
    return ResponseResult.success(map);
}

4.5 swagger測試
5.前端

index.vue

    <view class="article-box">
        <view class="article" v-for="(article,index) in articles" :key="index">
            <!-- 標(biāo)題 -->
            <text class="article-title" @tap="gotoDetail(article.id)">{{article.title}}</text>
            <!-- 大于等于三張圖片的顯示方式 -->
            <view class="" v-if="article.imgs.length >= 3">
                <view class="thumbnail-box">
                    <view class="thumbnail-item" v-for="(item,index1) in article.imgs" :key="index1" v-if="index1<3">
                        <image :src="item.imgUrl"></image>
                    </view>
                </view>
            </view>
            <!-- 小于三張圖片的顯示方式 -->
            <view class="" v-else-if="article.imgs.length >= 1">
                <view class="text-img-box">
                    <view class="left">
                        <text>{{<!-- handleContent -->article.title}}...</text>
                    </view>
                    <view class="right">
                        <image :src="article.imgs[article.imgs.length - 1].imgUrl"></image>
                    </view>
                </view>
            </view>
            <!-- 沒有圖片的顯示方式 -->
            <view class="text-box" v-else>
                <text>{{<!-- handleContent -->article.title}}...</text>
            </view>
            <!-- 文章作者等信息 -->
            <view class="article-info">
                <image :src="article.avatar" class="avatar small"></image>
                <text class="info-text">{{article.nickname}}</text>
                <text class="info-text1">{{article.createTime}}</text>
            </view>
        </view>
    </view>
    <view>
        <navigator url="../write/write" hover-class="navigator-hover" v-if="login" @tap="islogin()">
            <button class="btn">+</button>
        </navigator>
        <navigator url="../signin/signin" hover-class="navigator-hover" @tap="islogin()" v-else>
            <button class="btn">+</button>
        </navigator>
    </view>


</view>

</template>

<script>
export default {
data() {
return {
articles: [],
login:false
};
},
onLoad: function() {
this.getArticles();
},
onShow: function() {
const loginKey = uni.getStorageSync('login_key');
console.log(loginKey);
if (loginKey) {
this.login = true;
} else {
this.login = false;
}
},

    onPullDownRefresh: function() {
        this.getArticles();
    },
    methods: {
        
        changeTab: function() {
            uni.navigateTo({
                url: '../write/write'
            })
        },
        getArticles: function() {
            var _this = this;
            uni.request({
                url: this.apiServer + '/article/list',
                method: 'GET',
                header: {
                    'content-type': 'application/x-www-form-urlencoded'
                },
                success: res => {
                    _this.articles = res.data.data;
                },
                complete: function() {
                    uni.stopPullDownRefresh();
                }
            });
        },
        gotoDetail: function(aId) {
            uni.navigateTo({
                url: '../article_detail/article_detail?aId=' + aId
            });
        },
        handleTime: function(createTime) {
            var date = new Date(createTime);
            var year = date.getFullYear();
            var month =
                date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
            var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
            var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
            var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
            var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
            // 拼接
            return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds;
        },
        handleContent: function(content) {
            content = content.replace(/(\n)/g, '');
            content = content.replace(/(\t)/g, '');
            content = content.replace(/(\r)/g, '');
            content = content.replace(/<\/?[^>]*>/g, '');
            content = content.replace(/\s*/g, '');
            return content.substring(0, 50);
        },
        islogin: function() {
            if (this.login) {
                console.log('已登錄');
            } else {
                console.log('未登錄');
            }
        }

    }
};

</script>

<style scoped>

/* 圓形按鈕 */
.btn {
    /* 陰影效果福铅,四個參數(shù)分別是:水平陰影位置、垂直陰影位置项阴、陰影尺寸位置大小滑黔、陰影顏色 */
    box-shadow: 2px 5px 10px #AAA;
    width: 65px;
    height: 65px;
    margin: 10px;
    border-radius: 50%;
    padding: 0;
    cursor: pointer;
    border: none;
    outline: none;
    bottom: 35px;
    right: 20px;
    background: linear-gradient(40deg, #ffd86f, #fc6262);
    color: #FFF;
    display: flex;
    justify-content: center;
    align-items: center;
    position: fixed;
    font-size: 30px;
}

.icon-text {
    color: #FFFFFF;
    font-size: 35px;
}

.article-box {
    height: auto;
    width: 100%;
}

.article-title {
    font-weight: bold;
}

.article {
    display: flex;
    flex-direction: column;
    margin-top: 10px;
    border-bottom: 1px solid #EEEEEE;
    height: 210px;
}

.thumbnail-box {
    margin-top: 10px;
    display: flex;
    height: 120px;
    width: 100%;
}

.thumbnail-item {
    flex: 1 1 33%;
    height: 110px;
    margin-right: 5px;
    margin-left: 5px;
}

.thumbnail-item image {
    width: 100%;
    height: 100%;
}

.avatar {
    width: 50px;
    height: 50px;
}

.info-text {
    margin-left: 10px;
    font-size: 18px;
}

.info-text1 {
    margin-left: 5px;
    color: #CCCCCC;
    font-size: 16px;
}

.text-img-box {
    display: flex;
    margin-top: 15px;
}

.text-box {
    margin-top: 15px;
}

.left {
    flex: 1 1 70%;
}

.right {
    flex: 1 1 40%;
    height: 110px;
}

.right image {
    width: 100%;
    height: 100%;
}

.article-info {
    display: flex;
    align-items: center;
}
</style>

article_detail.vue

<template>
<view class="container">
<text class="article-title">{{ article.title }}</text>
<view class="article-info">
<image :src="article.avatar" class="avatar small"></image>
<text style="margin-left: 10px;">{{ article.nickname }}</text>
<text class="info-text">{{ handleTime(article.createTime)}}</text>

<button v-if="userId != article.uId && !followed" class="btn follow-btn" @tap="follow">+ 關(guān)注</button>
<button v-if="userId != article.uId && followed" class="btn follow-btn cancel" @tap="cancelFollow">取消</button>
</view>

    <view class="grace-text" style="margin-top: 10px;">
        <rich-text :nodes="article.content" bindtap="tap"></rich-text>
    </view>
    <button v-if="!liked" class="like-btn" @tap="like">收藏 </button>
    <button  v-if="liked" class="cancel-like" @tap="cancelLike">取消</button>
    <text class="info-text">評論 {{ comments.length }}</text>
    <view class="comment-item" v-for="(comment, index) in comments" :key="index">
        <view class="left">
            <image :src="comment.avatar" class="avatar small"></image>
        </view>
        <view class="right">
            <view class="right-content">
                <text>{{ comment.nickname }}</text>
                <text>{{ comment.content }}</text>
            </view>
            <view class="right-time">
                <text style="margin-right: 10px;">{{ comments.length - index }}樓·{{comment.commentTime}}</text>
                <!-- <text>{{  handleTime(comment.commentTime)}}</text> -->
            </view>
        </view>
    </view>
    
    <input class="uni-input comment-box" type="text" placeholder="寫下你的評論" v-model="content" required="required" />
    <button class="green-btn" @tap="send">提交</button>
</view>

</template>

<script>
export default {
data() {
return {
article: {
aId: 0,
uId: 0,
title: '',
content: '',
avatar: '',
nickname: '',
createTime: ''
},
comments: [],
content: '',
userId: uni.getStorageSync('login_key').userId,
followed: false,
liked:false
};
},
onLoad: function(option) {
//option為object類型,會序列化上個頁面?zhèn)鬟f的參數(shù)
this.article.aId = option.aId;
},
onShow: function() {
this.getArticle();
},
onPullDownRefresh: function() {
this.getArticle();
},
methods: {
getArticle: function() {
var _this = this;
uni.request({
url: this.apiServer + '/article/' + this.article.aId,
method: 'GET',
header: {
'content-type': 'application/x-www-form-urlencoded'
},
data: {
userId: this.userId
},
success: res => {
// console.log(res.data.data.article);
_this.article.aId = res.data.data.article.id;
_this.article.uId = res.data.data.article.uid;
_this.article.title = res.data.data.article.title;
_this.article.content = res.data.data.article.content;
_this.article.nickname = res.data.data.article.nickname;
_this.article.avatar = res.data.data.article.avatar;
_this.article.createTime = res.data.data.article.createTime;
_this.comments = res.data.data.comments;
if (res.data.data.followed === '已關(guān)注') {
_this.followed = true;
}
},
complete: function() {
uni.stopPullDownRefresh();
}
});
},
handleTime: function(date) {
var d = new Date(date);
var year = d.getFullYear();
var month = d.getMonth() + 1;
var day = d.getDate() < 10 ? '0' + d.getDate() : '' + d.getDate();
var hour = d.getHours() < 10 ? '0' + d.getHours() : '' + d.getHours();
var minutes = d.getMinutes() < 10 ? '0' + d.getMinutes() : '' + d.getMinutes();
var seconds = d.getSeconds() < 10 ? '0' + d.getSeconds() : '' + d.getSeconds();
return year + '-' + month + '-' + day + ' ' + hour + ':' + minutes + ':' + seconds;
},
send: function() {
console.log('評論人編號:' + this.userId + ',文章編號:' + this.article.aId + '环揽,評論內(nèi)容:' + this.content);
uni.request({
url: this.apiServer + '/comment/add',
method: 'POST',
header: {
'content-type': 'application/x-www-form-urlencoded'
},
data: {
aId: this.article.aId,
uId: this.userId,
content: this.content
},
success: res => {
if (res.data.code === 0) {
uni.showToast({
title: '評論成功'
});
this.getArticle();
this.content = '';
}
}
});
},
follow: function() {
uni.request({
url: this.apiServer + '/follow/add',
method: 'POST',
header: {
'content-type': 'application/x-www-form-urlencoded'
},
data: {
fromUId: this.userId,
toUId: this.article.uId
},
success: res => {
if (res.data.code === 0) {
uni.showToast({
title: '關(guān)注成功'
});
this.followed = true;
}
}
});
},
like: function() {
uni.request({
url: this.apiServer + '/like/add',
method: 'POST',
header: {
'content-type': 'application/x-www-form-urlencoded'
},
data: {
uId: this.userId,
aId: this.article.aId
},
success: res => {
if (res.data.code === 0) {
uni.showToast({
title: '收藏成功'
});
this.liked = true;
}
}
});
},
cancelFollow: function() {
uni.request({
url: this.apiServer + '/follow/cancel',
method: 'POST',
header: {
'content-type': 'application/x-www-form-urlencoded'
},
data: {
fromUId: this.userId,
toUId: this.article.uId
},
success: res => {
if (res.data.code === 0) {
uni.showToast({
title: '已取消關(guān)注'
});
this.followed = false;
}
}
});
},
cancelLike: function() {
uni.request({
url: this.apiServer + '/like/cancel',
method: 'POST',
header: {
'content-type': 'application/x-www-form-urlencoded'
},
data: {
uId: this.userId,
aId: this.article.aId
},
success: res => {
if (res.data.code === 0) {
uni.showToast({
title: '已取消收藏'
});
this.liked = false;
}
}
});
}
}
};
</script>

<style>

.link {
    cursor: pointer;
}

.article-title {
    font-weight: bold;
    padding: 10px;
    font-size: 22px;
}

.article-info {
    display: flex;
    margin-top: 20px;
    align-items: center;
}

.grace-text {
    margin-top: 10px;
}

.avatar {
    width: 60px;
    height: 60px;
    margin-left: 5px;
}

.info-text {
    margin-left: 10px;
    font-size: 18px;
    margin-top: 10px;
    display: flex;
    flex-direction: column;
}

.btn {
    margin-right: 10px;
    width: 90px;
    height: 40px;
    background: #00C777;
    display: flex;
    justify-content: center;
    align-items: center;
    color: #EEEEEE;
}

.content {
    width: 90%;
    margin: auto;
}

.comment-item {
    display: flex;
    margin-top: 5px;
}

.right {
    display: flex;
    flex-direction: column;
    margin-left: 10px;
}

.right-content {
    display: flex;
    flex-direction: column;
}

.right-time {
    margin-top: 5px;
    color: #C1C1C1;
    font-size: 15px;
}

.uni-input {
    margin-top: 10px;
    font-size: 18px;
}

.green-btn {
    margin-top: 10px;
    width: 60%;
    cursor: pointer;
    border-radius: 10px;
    background: #00EE76;
    color: white;
}

.cancel {
    background-color:#AAAAAA;
}
.like-btn{
    width: 90px;
    height: 40px;
    background: white;
    display: flex;
    justify-content: center;
    align-items: center;
    color:#FF7900;
    border: 1px solid #FF7900;
    border-radius: 10px;
    margin-top: 10px;
}
.cancel-like{
    width: 90px;
    height: 40px;
    background-color: #aaa;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 10px;
    margin-top: 10px;
}

</style>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末略荡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子歉胶,更是在濱河造成了極大的恐慌汛兜,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件通今,死亡現(xiàn)場離奇詭異粥谬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)辫塌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門漏策,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人臼氨,你說我怎么就攤上這事掺喻⊙饩。” “怎么了收毫?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵蘑秽,是天一觀的道長夸楣。 經(jīng)常有香客問我,道長抑月,這世上最難降的妖魔是什么树叽? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮谦絮,結(jié)果婚禮上题诵,老公的妹妹穿的比我還像新娘。我一直安慰自己层皱,他們只是感情好性锭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著叫胖,像睡著了一般草冈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瓮增,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天怎棱,我揣著相機(jī)與錄音,去河邊找鬼绷跑。 笑死拳恋,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的砸捏。 我是一名探鬼主播谬运,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼垦藏!你這毒婦竟也來了梆暖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤掂骏,失蹤者是張志新(化名)和其女友劉穎式廷,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芭挽,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年蝗肪,在試婚紗的時候發(fā)現(xiàn)自己被綠了袜爪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡薛闪,死狀恐怖辛馆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤昙篙,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布腊状,位于F島的核電站,受9級特大地震影響苔可,放射性物質(zhì)發(fā)生泄漏缴挖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一焚辅、第九天 我趴在偏房一處隱蔽的房頂上張望映屋。 院中可真熱鬧,春花似錦同蜻、人聲如沸棚点。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘫析。三九已至,卻和暖如春默责,著一層夾襖步出監(jiān)牢的瞬間贬循,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工傻丝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留甘有,地道東北人。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓葡缰,卻偏偏與公主長得像亏掀,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子泛释,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評論 2 354