xiongzhu 4 سال پیش
والد
کامیت
e54b1966a6
8فایلهای تغییر یافته به همراه489 افزوده شده و 19 حذف شده
  1. BIN
      src/assets/img_official.png
  2. 9 0
      src/main.js
  3. 12 0
      src/router.js
  4. 19 6
      src/views/conversations.vue
  5. 3 3
      src/views/index/home.vue
  6. 10 10
      src/views/interact/forumDetail.vue
  7. 218 0
      src/views/message.vue
  8. 218 0
      src/views/sysMessage.vue

BIN
src/assets/img_official.png


+ 9 - 0
src/main.js

@@ -14,6 +14,11 @@ import NavBar from './components/NavBar';
 import vueg from 'vueg';
 import preview from './components/preview';
 import VueBus from 'vue-bus';
+import dayjs from 'dayjs';
+import relativeTime from 'dayjs/plugin/relativeTime';
+import zh from 'dayjs/locale/zh-cn';
+dayjs.locale('zh-cn');
+dayjs.extend(relativeTime);
 // const iNoBounce = require('./inobounce');
 // console.log(iNoBounce);
 // iNoBounce.enable();
@@ -24,6 +29,7 @@ Vue.config.productionTip = false;
 Vue.use(Vant);
 Vue.prototype.$toast.setDefaultOptions({ duration: 2000, forbidClick: true });
 Vue.prototype.$toast.setDefaultOptions('loading', { duration: 0, forbidClick: true });
+Vue.prototype.$dayjs = dayjs;
 Vue.use(http);
 Vue.use(VueMeta, {
     refreshOnceOnNavigation: false
@@ -71,6 +77,9 @@ Vue.mixin({
                 return datetimeStr.split(' ')[0];
             }
             return '';
+        },
+        toRelativeTime(time) {
+            return dayjs().to(dayjs(time, 'YYYY-MM-DD HH:mm:ss'));
         }
     }
 });

+ 12 - 0
src/router.js

@@ -252,6 +252,18 @@ const router = new Router({
             name: 'conversations',
             component: () => import(/* webpackChunkName: "conversations" */ '@/views/conversations.vue'),
             meta: { statusBar: 'dark' }
+        },
+        {
+            path: '/message',
+            name: 'message',
+            component: () => import(/* webpackChunkName: "message" */ '@/views/message.vue'),
+            meta: { statusBar: 'dark' }
+        },
+        {
+            path: '/sysMessage',
+            name: 'sysMessage',
+            component: () => import(/* webpackChunkName: "sysMessage" */ '@/views/sysMessage.vue'),
+            meta: { statusBar: 'dark' }
         }
     ]
 });

+ 19 - 6
src/views/conversations.vue

@@ -24,11 +24,6 @@
     </div>
 </template>
 <script>
-import dayjs from 'dayjs';
-import relativeTime from 'dayjs/plugin/relativeTime';
-import zh from 'dayjs/locale/zh-cn';
-dayjs.locale('zh-cn');
-dayjs.extend(relativeTime);
 export default {
     data() {
         return {
@@ -43,7 +38,7 @@ export default {
             this.$http.get('/conversation/my').then(res => {
                 res.forEach(i => {
                     if (i.lastUpdate) {
-                        i.relativeTime = dayjs().to(dayjs(i.lastUpdate, 'YYYY-MM-DD HH:mm:ss'));
+                        i.relativeTime = this.toRelativeTime(i.lastUpdate);
                     }
                 });
                 this.conversations = res;
@@ -54,6 +49,24 @@ export default {
                 sessionId: item.sessionId
             });
             this.$set(item, 'unread', 0);
+            if (item.type === 'TEXT') {
+                this.$router.push({
+                    name: 'message',
+                    query: {
+                        conversationId: item.id,
+                        sessionId: item.sessionId,
+                        title: item.title
+                    }
+                });
+            } else {
+                this.$router.push({
+                    name: 'sysMessage',
+                    query: {
+                        sessionId: item.sessionId,
+                        type: item.type
+                    }
+                });
+            }
         }
     }
 };

+ 3 - 3
src/views/index/home.vue

@@ -1,6 +1,6 @@
 <template>
     <div style="padding-bottom: calc(50px + var(--safe-bottom));">
-        <div class="notification-wrapper">
+        <div class="notification-wrapper" @click="$router.push({ name: 'conversations' })">
             <img src="../../assets/home_notification.png" />
             <div class="badge"></div>
         </div>
@@ -10,7 +10,7 @@
                     <img src="../../assets/icon_search.png" />
                     搜索新闻/政策/供需...
                 </div>
-                <div class="notification">
+                <div class="notification" @click="$router.push({ name: 'conversations' })">
                     <img src="../../assets/home_notification.png" />
                     <div class="badge"></div>
                 </div>
@@ -79,7 +79,7 @@ export default {
         this.$http.get('/article/all', { query: { typeId: 1005 }, sort: 'createdAt,desc' }).then(res => {
             this.news = res.content;
         });
-        this.$http.get('/product/all?size=5').then(res => {
+        this.$http.post('/resourceSupplyAndDemand/allDTO', { size: 5 }, { body: 'json' }).then(res => {
             this.products = res.content;
         });
     },

+ 10 - 10
src/views/interact/forumDetail.vue

@@ -38,25 +38,25 @@
             </div>
             <div class="opt">
                 <div class="time">{{ item.createdAt }}</div>
-                <div class="btn" style="margi-right:24px" @click="like(item, false)">
+                <div class="btn" style="margi-right:24px" @click="like(item, true)">
                     <img
                         :src="
-                            item.liked
-                                ? require('../../assets/interact_icon_like_pre.png')
-                                : require('../../assets/interact_icon_like.png')
+                            item.disliked
+                                ? require('../../assets/interact_icon_dislike_pre.png')
+                                : require('../../assets/interact_icon_dislike.png')
                         "
                     />
-                    {{ item.likeNum }}
+                    {{ item.dislikeNum }}
                 </div>
-                <div class="btn" @click="like(item, true)">
+                <div class="btn" @click="like(item, false)">
                     <img
                         :src="
-                            item.disliked
-                                ? require('../../assets/interact_icon_dislike_pre.png')
-                                : require('../../assets/interact_icon_dislike.png')
+                            item.liked
+                                ? require('../../assets/interact_icon_like_pre.png')
+                                : require('../../assets/interact_icon_like.png')
                         "
                     />
-                    {{ item.dislikeNum }}
+                    {{ item.likeNum }}
                 </div>
             </div>
         </div>

+ 218 - 0
src/views/message.vue

@@ -0,0 +1,218 @@
+<template>
+    <div style="background:#F5F7FA" @scroll="scroll">
+        <nav-bar @click-left="$router.go(-1)" :title="title">
+            <div slot="right">全部已读</div>
+        </nav-bar>
+        <div class="list" v-if="userInfo && userInfo.id">
+            <template v-for="item in reverseMessages">
+                <div
+                    class="message-item"
+                    :class="{ rtl: item.userId === userInfo.id }"
+                    v-if="item.type === 'TEXT' && item.userId === userInfo.id"
+                    :key="item.id"
+                >
+                    <img class="avatar" :src="(userInfo || {}).avatar" />
+                    <div class="msg-content">{{ item.content }}</div>
+                </div>
+                <div
+                    class="message-item"
+                    :class="{ rtl: item.userId === userInfo.id }"
+                    v-else-if="item.type === 'TEXT'"
+                    :key="item.id"
+                >
+                    <img class="avatar" :src="conversation.icon" />
+                    <div class="msg-content">{{ item.content }}</div>
+                </div>
+            </template>
+            <div class="bottom-holder"></div>
+            <div class="bottom-wrapper">
+                <div class="bottom">
+                    <input placeholder="请输入您要回复的内容" v-model="content" @keyup.enter="send" />
+                    <div class="btn-send" @click="send">发送</div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+import { mapState } from 'vuex';
+export default {
+    data() {
+        return {
+            title: '',
+            messages: [],
+            conversation: {},
+            page: 0,
+            last: false,
+            loading: false,
+            content: '',
+            lastTop: 0,
+            scrollBottom: 0
+        };
+    },
+    computed: {
+        ...mapState(['userInfo']),
+        reverseMessages() {
+            return [...this.messages].reverse();
+        }
+    },
+    created() {
+        this.title = this.$route.query.title;
+        this.getData();
+        this.$http.get(`/conversation/get/${this.$route.query.conversationId}`).then(res => {
+            this.conversation = res;
+            setTimeout(() => {
+                this.scrollToBottom();
+            }, 100);
+        });
+    },
+    mounted() {
+        this.$nextTick(() => {
+            this.scrollToBottom();
+        });
+    },
+    methods: {
+        getData() {
+            this.loading = true;
+            this.$http
+                .get('/message/all', {
+                    sessionId: this.$route.query.sessionId,
+                    page: this.page
+                })
+                .then(res => {
+                    // res.content[0].content = '嗯嗯,我对贵公司的产品简直不要太喜欢,算我穷尽我的词汇也无法真实表达我的感受';
+                    // res.content[2].userId = 1;
+                    if (res.first) {
+                        this.messages = [];
+                    }
+                    this.messages = this.messages.concat(res.content);
+                    this.last = res.last;
+                    this.loading = false;
+                    if (this.scrollBottom) {
+                        this.$nextTick(() => {
+                            this.$el.scrollTop =
+                                this.$el.scrollHeight - this.$el.getBoundingClientRect().height - this.scrollBottom;
+                        });
+                    }
+                })
+                .catch(e => {
+                    this.loading = false;
+                });
+        },
+        send() {
+            if (!this.content) return;
+            let data = {
+                sessionId: this.$route.query.sessionId,
+                toUserId: this.conversation.toUserId,
+                type: 'TEXT',
+                content: this.content
+            };
+            this.$http.post('/message/send', data).then(res => {
+                this.messages.unshift({ ...data, userId: this.userInfo.id });
+                this.content = '';
+                this.$nextTick(() => {
+                    this.scrollToBottom();
+                });
+            });
+        },
+        scrollToBottom() {
+            let el = document.querySelector('#root');
+            el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' });
+        },
+        scroll(e) {
+            let top = e.target.scrollTop;
+            if (top < this.lastTop && top < 30 && !this.last && !this.loading) {
+                console.log('scroll top');
+                this.page++;
+                this.getData();
+            }
+            this.lastTop = top;
+            this.scrollBottom = this.$el.scrollHeight - this.$el.scrollTop - this.$el.getBoundingClientRect().height;
+        }
+    }
+};
+</script>
+<style lang="less" scoped>
+/deep/ .nav-bar-right {
+    word-break: keep-all;
+    font-size: 14px;
+    color: @text3;
+    &:active {
+        color: fade(@text3, 60%);
+    }
+}
+.message-item {
+    .flex();
+    padding: 10px 16px 0 16px;
+    align-items: flex-start;
+    .avatar {
+        width: 36px;
+        height: 36px;
+        min-width: 36px;
+        border-radius: 18px;
+        object-fit: cover;
+    }
+    .msg-content {
+        font-size: 15px;
+        line-height: 24px;
+        padding: 12px;
+        color: black;
+        background: white;
+        border-radius: 0px 16px 16px 16px;
+        margin-left: 8px;
+        margin-right: 10px;
+    }
+    &.rtl {
+        direction: rtl;
+        .msg-content {
+            margin-left: 10px;
+            margin-right: 8px;
+            border-radius: 16px 0px 16px 16px;
+            background: @prim;
+            color: white;
+        }
+    }
+}
+.bottom-holder {
+    height: calc(76px + var(--safe-bottom));
+}
+.bottom-wrapper {
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    padding-bottom: var(--safe-bottom);
+    background: white;
+}
+.bottom {
+    height: 56px;
+    .flex();
+    padding: 0 16px;
+    input {
+        height: 40px;
+        border: 1px solid @bg;
+        border-radius: 4px;
+        flex-grow: 1;
+        font-size: 15px;
+        padding: 0 10px;
+        &::-webkit-input-placeholder {
+            color: @text4;
+        }
+    }
+    .btn-send {
+        width: 64px;
+        height: 40px;
+        border-radius: 4px;
+        background: @prim;
+        color: white;
+        font-size: 14px;
+        .flex();
+        justify-content: center;
+        margin-left: 8px;
+        user-select: none;
+        &:active {
+            background: shade(@prim, 20%);
+        }
+    }
+}
+</style>

+ 218 - 0
src/views/sysMessage.vue

@@ -0,0 +1,218 @@
+<template>
+    <div>
+        <nav-bar @click-left="$router.go(-1)" :title="title"></nav-bar>
+        <div class="list">
+            <div class="item" v-for="item in messages" :key="item.id">
+                <div class="info">
+                    <img class="avatar" :src="item.content.avatar" />
+                    <div class="name">
+                        {{ item.content.nickname }}
+                    </div>
+                    <div class="time">{{ item.relativeTime }}</div>
+                </div>
+                <div class="message">{{ item.content.message }}</div>
+                <div class="attach" v-if="item.comment" @click="detail(item.post)">
+                    <div class="col">
+                        <div class="title">{{ item.comment.content }}</div>
+                        <div class="desc">{{ getCommentType(item.post) }}</div>
+                    </div>
+                </div>
+                <div class="attach" v-else @click="detail(item.post)">
+                    <div class="col">
+                        <div class="title">{{ item.post.title }}</div>
+                        <div class="desc">{{ getPostType(item.post) }}</div>
+                    </div>
+                    <img class="img" :src="getPostImg(item.post)" />
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+import { mapState } from 'vuex';
+export default {
+    data() {
+        return {
+            type: 'comment',
+            messages: [],
+            last: false,
+            loading: false,
+            empty: false,
+            postTypeOptions: [
+                { label: '官方问答', value: 'OFFICIAL' },
+                { label: '热议话题', value: 'TOPIC' },
+                { label: '互动提问', value: 'QA' }
+            ]
+        };
+    },
+    created() {
+        this.type = this.$route.query.type;
+        this.getData();
+    },
+    computed: {
+        ...mapState(['userInfo']),
+        title() {
+            switch (this.type) {
+                case 'COMMENT':
+                    return '评论';
+                case 'LIKE':
+                    return '点赞';
+                default:
+                    return '';
+            }
+        }
+    },
+    methods: {
+        getData() {
+            if (this.loading || !this.userInfo) return;
+            this.loading = true;
+            this.$http
+                .get('/message/all', {
+                    sessionId: `${this.type.toLowerCase()}-${this.userInfo.id}`
+                })
+                .then(res => {
+                    if (res.first) {
+                        this.messages = [];
+                    }
+                    this.messages = this.messages.concat(
+                        res.content.map(i => {
+                            i.content = JSON.parse(i.content);
+                            i.relativeTime = this.toRelativeTime(i.createdAt);
+                            return i;
+                        })
+                    );
+                    this.last = res.last;
+                    this.empty = res.totalElements === 0;
+                    this.loading = false;
+                })
+                .catch(e => {
+                    this.loading = false;
+                });
+        },
+        getPostType(post) {
+            return (this.postTypeOptions.find(i => i.value === post.type) || {}).label;
+        },
+        getPostImg(post) {
+            switch (post.type) {
+                case 'OFFICIAL':
+                    return require('../assets/img_official.png');
+                case 'QA':
+                    return require('../assets/img_qa.png');
+                case 'TOPIC':
+                    return require('../assets/img_topic.png');
+            }
+        },
+        getCommentType(post) {
+            switch (post.type) {
+                case 'OFFICIAL':
+                    return '官方回复';
+                case 'QA':
+                    return '问题回复';
+                case 'TOPIC':
+                    return '话题回复';
+            }
+        },
+        detail(post) {
+            if (post.del) {
+                this.$toast(`该${this.getPostType(post)}已删除`);
+            } else {
+                switch (post.type) {
+                    case 'OFFICIAL':
+                        this.$router.push({
+                            name: 'officialDetail',
+                            query: { id: post.id }
+                        });
+                        break;
+                    case 'TOPIC':
+                        this.$router.push({
+                            name: 'forumDetail',
+                            query: { id: post.id, type: 'forum' }
+                        });
+                        break;
+                    case 'QA':
+                        this.$router.push({
+                            name: 'forumDetail',
+                            query: { id: post.id, type: 'qa' }
+                        });
+                        break;
+                }
+            }
+        }
+    },
+    watch: {
+        userInfo() {
+            this.getData();
+        }
+    }
+};
+</script>
+<style lang="less" scoped>
+.item {
+    .flex-col();
+    position: relative;
+    padding: 20px 16px;
+    &::after {
+        .setBottomLine();
+        left: 16px;
+        right: 16px;
+    }
+    .info {
+        .flex();
+        .avatar {
+            width: 24px;
+            height: 24px;
+            min-width: 24px;
+            border-radius: 12px;
+            object-fit: cover;
+        }
+        .name {
+            margin-left: 6px;
+            font-size: 13px;
+            color: @text1;
+            flex-basis: 0;
+            flex-grow: 1;
+            .ellipsisLn(1);
+        }
+        .time {
+            color: @text4;
+            font-size: 13px;
+            word-break: keep-all;
+        }
+    }
+    .message {
+        margin: 6px 0 0 30px;
+        font-size: 16px;
+        color: black;
+        font-weight: bold;
+        line-height: 24px;
+    }
+    .attach {
+        margin: 10px 0 0 30px;
+        background: @bg;
+        padding: 12px;
+        .flex();
+        align-items: flex-start;
+        border-radius: 0px 16px 16px 16px;
+        .col {
+            .flex-col();
+            .title {
+                font-size: 14px;
+                color: @text2;
+                .ellipsisLn(2);
+                line-height: 22px;
+            }
+            .desc {
+                font-size: 13px;
+                color: @text4;
+                margin-top: 2px;
+            }
+        }
+        .img {
+            width: 70px;
+            min-width: 70px;
+            height: 70px;
+            margin-left: 12px;
+        }
+    }
+}
+</style>