xuqiang 4 jaren geleden
bovenliggende
commit
3156a4c9ae
8 gewijzigde bestanden met toevoegingen van 754 en 8 verwijderingen
  1. 159 0
      src/components/ChatInfo.vue
  2. 151 0
      src/components/NewsInfo.vue
  3. 1 0
      src/main.js
  4. 3 0
      src/mixins/common.js
  5. 3 4
      src/pages/Home.vue
  6. 348 0
      src/pages/chat.vue
  7. 1 1
      src/pages/mine.vue
  8. 88 3
      src/pages/news.vue

+ 159 - 0
src/components/ChatInfo.vue

@@ -0,0 +1,159 @@
+<template>
+    <div class="chat-box">
+        <div class="time" v-if="info.time">{{ info.time }}</div>
+
+        <div class="chat" :class="{ alignRight: isMine }">
+            <van-image :src="sendUserInfo.avatar" round width="36" height="36" fit="cover" />
+            <div class="chat-content">
+                <div class="chat-text" v-if="contentType === 'TEXT'">
+                    {{ info.info }}
+                </div>
+                <van-image
+                    @click="preview(info.info)"
+                    class="chat-img"
+                    width="190"
+                    height="190"
+                    fit="heightFix"
+                    :src="info.info"
+                    v-else-if="contentType === 'IMAGE'"
+                />
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    name: 'ChatInfo',
+    props: {
+        info: {
+            type: Object,
+            default: () => {
+                return {};
+            }
+        },
+        contentType: {
+            type: String,
+            default: 'TEXT'
+        },
+        isMine: {
+            type: Boolean,
+            default: false
+        }
+    },
+    computed: {
+        sendUser() {
+            return this.info.sendUser || null;
+        },
+        sendUserInfo() {
+            if (this.sendUser) {
+                if (this.sendUser.store && !this.isMine) {
+                    return {
+                        name: this.sendUser.store.storeName,
+                        avatar: this.getLogo(this.sendUser.store.logo)
+                    };
+                } else {
+                    return {
+                        name: this.sendUser.nickname,
+                        avatar: this.sendUser.avatar
+                    };
+                }
+            } else {
+                return {};
+            }
+        },
+        productInfo() {
+            if (this.contentType === 'LINK') {
+                return JSON.parse(this.info.info);
+            } else {
+                return {};
+            }
+        }
+    }
+};
+</script>
+
+<style lang="less" scoped>
+.chat-box {
+    padding-top: 30px;
+}
+.chat {
+    // margin-top: 30px;
+    .flex();
+    align-items: flex-start;
+    &.alignRight {
+        flex-direction: row-reverse;
+        .chat-content {
+            margin-left: 0px;
+            margin-right: 8px;
+            flex-direction: row-reverse;
+        }
+        .chat-text {
+            background: #ff6c00;
+            border-radius: 12px 2px 12px 12px;
+            color: #fff;
+        }
+    }
+
+    ._van-image {
+        flex-shrink: 0;
+    }
+
+    .chat-content {
+        flex-grow: 1;
+        overflow: hidden;
+        margin-left: 8px;
+        .flex();
+    }
+
+    .chat-text {
+        background: #ffffff;
+        border-radius: 2px 12px 12px 12px;
+        font-size: 16px;
+        color: #000000;
+        line-height: 26px;
+        padding: 10px;
+    }
+}
+
+.product {
+    background-color: #fff;
+    padding: 15px;
+    border-radius: 12px;
+    // margin-top: 30px;
+}
+
+/deep/.chat {
+    .chat-img {
+        .van-image {
+            .van-image__img {
+                border-radius: 12px 0px 12px 12px;
+            }
+            overflow: hidden;
+        }
+    }
+    &.alignRight {
+        .chat-img {
+            .van-image {
+                .van-image__img {
+                    border-radius: 12px 0px 12px 12px;
+                }
+                display: flex;
+                justify-content: flex-end;
+            }
+        }
+    }
+}
+
+.time {
+    font-size: 12px;
+    color: #878d99;
+    line-height: 17px;
+    text-align: center;
+    padding-bottom: 10px;
+}
+
+/deep/ .van-image {
+    --image-placeholder-background-color: #fff;
+}
+</style>

+ 151 - 0
src/components/NewsInfo.vue

@@ -0,0 +1,151 @@
+<template>
+    <div
+        class="news-info"
+        @click="navigateTo(`/pages/chat?toUserId=${otherUserInfo.userId}&toName=${otherUserInfo.name}`)"
+    >
+        <div class="icon">
+            <van-image :src="otherUserInfo.img" round width="50" height="50" fit="cover" />
+            <div class="dot" v-if="dot">{{ dot }}</div>
+        </div>
+        <div class="content">
+            <div class="text1">
+                {{ otherUserInfo.name }}
+            </div>
+            <span class="time">{{ time }}</span>
+            <div class="text2">{{ text }}</div>
+        </div>
+    </div>
+</template>
+
+<script>
+import { mapState } from 'vuex';
+import dayjs from 'dayjs';
+import * as relativeTime from 'dayjs/plugin/relativeTime';
+import 'dayjs/locale/zh-cn';
+dayjs.locale('zh-cn');
+dayjs.extend(relativeTime);
+
+export default {
+    name: 'NewsInfo',
+    props: {
+        info: {
+            type: Object,
+            default: () => {
+                return {};
+            }
+        },
+        dot: {
+            type: Number,
+            default: 0
+        }
+    },
+    computed: {
+        ...mapState(['userInfo']),
+        otherUserInfo() {
+            if (!this.userInfo) {
+                return {};
+            }
+            if (this.info.storeUserId !== this.userInfo.id) {
+                return {
+                    userId: this.info.storeUserId,
+                    img: this.info.storeUser.store
+                        ? this.getLogo(this.info.storeUser.store.logo)
+                        : this.info.storeUser.store.avatar,
+                    name: this.info.storeUser.store ? this.info.storeUser.store.storeName : this.info.storeUser.nickname
+                };
+            }
+            if (this.info.userId !== this.userInfo.id) {
+                return {
+                    userId: this.info.userId,
+                    img: this.info.user.store ? this.getLogo(this.info.user.store.logo) : this.info.user.avatar,
+                    name: this.info.user.store ? this.info.user.store.storeName : this.info.user.nickname
+                };
+            }
+
+            return {};
+        },
+        lastMsg() {
+            return this.info.lastMsg || { createdAt: '' };
+        },
+        text() {
+            if (this.lastMsg.messageType === 'TEXT') {
+                return this.lastMsg.info;
+            } else if (this.lastMsg.messageType === 'IMAGE') {
+                return '图片...';
+            } else if (this.lastMsg.messageType === 'LINK') {
+                let product = JSON.parse(this.lastMsg.info);
+                return product.caseName;
+            }
+            return '';
+        },
+        time() {
+            if (this.lastMsg.createdAt) {
+                return dayjs(this.lastMsg.createdAt).fromNow();
+            } else {
+                return '';
+            }
+        }
+    }
+};
+</script>
+
+<style lang="less" scoped>
+.news-info {
+    .flex();
+    padding: 20px;
+    overflow: hidden;
+
+    .icon {
+        flex-shrink: 0;
+        margin-right: 8px;
+        position: relative;
+
+        .dot {
+            min-width: 6px;
+            height: 18px;
+            font-size: 13px;
+            color: #ffffff;
+            line-height: 18px;
+            padding: 0 6px;
+            text-align: center;
+            position: absolute;
+            left: 36px;
+            top: -6px;
+            background: @prim;
+            border-radius: 9px;
+        }
+    }
+
+    .content {
+        flex-grow: 1;
+        overflow: hidden;
+        position: relative;
+        .text1 {
+            font-size: 16px;
+            font-weight: bold;
+            color: #000000;
+            line-height: 26px;
+        }
+
+        .text2 {
+            font-size: 14px;
+            color: #c8c9cc;
+            line-height: 24px;
+            .ellipsis();
+        }
+
+        .time {
+            font-size: 13px;
+            color: #c8c9cc;
+            line-height: 24px;
+            position: absolute;
+            right: 0px;
+            top: 0;
+        }
+    }
+
+    &:active {
+        background-color: rgba(0, 0, 0, 0.1);
+    }
+}
+</style>

+ 1 - 0
src/main.js

@@ -30,6 +30,7 @@ export default {
         pages: [
             'pages/Home',
             'pages/news',
+            'pages/chat',
             'pages/mine',
             'pages/setting',
             'pages/changeText',

+ 3 - 0
src/mixins/common.js

@@ -115,6 +115,9 @@ export default {
                 title: '加载中...'
             });
         },
+        getLogo(img) {
+            return img ? img + '?x-oss-process=image/circle,r_300/format,png' : '/native/imgs/defaultLogo.png';
+        },
         hideLoading() {
             wx.hideLoading();
         },

+ 3 - 4
src/pages/Home.vue

@@ -164,12 +164,11 @@ export default {
             font-weight: 400;
             color: #000000;
             line-height: 22px;
-            width: 100%;
-            margin-top: 26px;
-            text-align: center;
-            justify-content: space-between;
+            margin: 26px 30px 0 0;
             padding-bottom: 11px;
             &.active {
+                font-size: 18px;
+                color: #159eff;
                 border-bottom: 1px solid #159eff;
             }
         }

+ 348 - 0
src/pages/chat.vue

@@ -0,0 +1,348 @@
+<config>
+{
+    "navigationBarTitleText":'',
+    "backgroundColor":"#F5F7FA"
+}
+</config>
+<template>
+    <div class="page">
+        <div
+            id="chat-list"
+            class="chat-list"
+            @click="showAttach = ''"
+            :style="{ paddingBottom: `calc(${100 + attachHeight}px + env(safe-area-inset-bottom))` }"
+        >
+            <chat-info
+                v-for="(item, index) in list"
+                :key="index"
+                :info="item"
+                :isMine="userInfo && item.sendId === userInfo.id"
+                :contentType="item.messageType"
+            ></chat-info>
+
+            <div class="loading" v-if="loading">
+                <van-loading size="24px">加载中...</van-loading>
+            </div>
+            <!-- <chat-info isMine></chat-info>
+            <chat-info isMine contentType="image"></chat-info> -->
+        </div>
+
+        <div class="bottom-box">
+            <div class="input-box">
+                <van-field
+                    :boder="false"
+                    placeholder="请输入需要咨询的问题"
+                    :value="message"
+                    @blur="message = $event.detail.value"
+                    @input="message = $event.detail"
+                    @confirm="confirm"
+                    clearable
+                />
+
+                <img @click="showAttach = showAttach !== 'emoji' ? 'emoji' : ''" alt="" class="icon" />
+                <van-button type="primary" size="small" @click="confirm('')" v-if="message"> 发送</van-button>
+                <img @click="showAttach = showAttach != 'img' ? 'img' : ''" alt="" class="icon" v-else />
+            </div>
+            <div class="attach-box" :style="{ height: `${attachHeight}px` }">
+                <div class="up-btn" @click="sendImg" v-if="showAttach === 'img'">
+                    <!-- <img src="/native/svgs/icon_zhaopian.svg" alt="" /> -->
+                </div>
+
+                <div class="emojiList" v-else>
+                    <div class="emoji-item" @click="addEmoji(item)" v-for="(item, index) in connectemoji" :key="index">
+                        {{ item }}
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import ChatInfo from '@/components/ChatInfo';
+import { mapState } from 'vuex';
+import dayjs from 'dayjs';
+import * as calendar from 'dayjs/plugin/calendar';
+import 'dayjs/locale/zh-cn';
+dayjs.locale('zh-cn');
+dayjs.extend(calendar);
+export default {
+    name: 'Chat',
+    components: {
+        ChatInfo
+    },
+    data() {
+        return {
+            message: '',
+            showAttach: '',
+            topicInfo: null,
+            list: [],
+            toUserId: 0,
+            loading: true,
+            timeFormat: {
+                sameDay: 'A hh:mm', // The same day ( Today at 2:30 AM )
+                nextDay: '[明天]', // The next day ( Tomorrow at 2:30 AM )
+                nextWeek: 'dddd', // The next week ( Sunday at 2:30 AM )
+                lastDay: 'MM/DD HH:mm', // The day before ( Yesterday at 2:30 AM )
+                lastWeek: 'MM/DD HH:mm', // Last week ( Last Monday at 2:30 AM )
+                sameElse: 'YYYY/MM/DD' // Everything else ( 7/10/2011 )
+            },
+            connectemoji: [
+                '😊',
+                '😅',
+                '😲',
+                '😭',
+                '😂',
+                '😄',
+                '😩',
+                '😞',
+                '😵',
+                '😒',
+                '😍',
+                '😤',
+                '😜',
+                '😝',
+                '😋',
+                '😘',
+                '😚',
+                '😷',
+                '😳',
+                '😃',
+                '😆',
+                '😁',
+                '😢',
+                '😨',
+                '😠',
+                '😣',
+                '😌',
+                '😖',
+                '😔',
+                '😰',
+                '😱',
+                '😪',
+                '😏',
+                '😓'
+            ]
+        };
+    },
+    computed: {
+        ...mapState(['userInfo', 'systemInfo']),
+        attachHeight() {
+            return this.showAttach ? 200 : 0;
+        }
+    },
+    onLoad(options) {
+        // if (options.toName) {
+        //     wx.setNavigationBarTitle({
+        //         title: options.toName
+        //     });
+        // }
+        // if (options.toUserId) {
+        //     this.toUserId = options.toUserId;
+        // }
+    },
+    methods: {
+        // loginMethods() {
+        //     let eventChannel = this.$mp.page.getOpenerEventChannel();
+        //     this.$http
+        //         .post('/topic/newTopic', {
+        //             targetId: this.$mp.options.toUserId
+        //         })
+        //         .then(res => {
+        //             this.topicInfo = res;
+        //             return this.getList();
+        //         })
+        //         .then(() => {
+        //             if (eventChannel && eventChannel.on) {
+        //                 eventChannel.on('productInfo', data => {
+        //                     this.newMessage(JSON.stringify(data), true, 'LINK');
+        //                     setTimeout(() => {
+        //                         this.newMessage('欢迎光临本店,小店客服会尽快为您提供服务。', false, 'TEXT', false);
+        //                     }, 1000);
+        //                 });
+        //             }
+        //         });
+        // },
+        // sendImg() {
+        //     this.choosePhoto().then(res => {
+        //         console.log(res);
+        //         this.newMessage(res, true, 'IMAGE');
+        //     });
+        // },
+        // getList() {
+        //     this.loading = true;
+        //     return this.$http
+        //         .get('/message/findTopicMessages', {
+        //             topicId: this.topicInfo.id
+        //         })
+        //         .then(res => {
+        //             console.log(res);
+        //             this.loading = false;
+        //             this.list = res.map((item, index) => {
+        //                 let time = '';
+        //                 if (index == 0) {
+        //                     time = dayjs(item.createdAt).calendar(null, this.timeFormat);
+        //                 } else {
+        //                     const date1 = dayjs(item.createdAt);
+        //                     if (date1.diff(res[index - 1].createdAt, 'minute') > 5) {
+        //                         time = dayjs(item.createdAt).calendar(null, this.timeFormat);
+        //                     }
+        //                 }
+        //                 return {
+        //                     ...item,
+        //                     time: time
+        //                 };
+        //             });
+        //             this.$nextTick(() => {
+        //                 setTimeout(() => {
+        //                     wx.createSelectorQuery()
+        //                         .select('#chat-list')
+        //                         .boundingClientRect(rect => {
+        //                             wx.pageScrollTo({
+        //                                 scrollTop: rect.height + 100
+        //                             });
+        //                         })
+        //                         .exec();
+        //                 }, 100);
+        //             });
+        //             this.$http.post('/message/allRead', {
+        //                 topicId: this.topicInfo.id
+        //             });
+        //             return Promise.resolve();
+        //         });
+        // },
+        // confirm(e) {
+        //     this.$nextTick(() => {
+        //         this.newMessage(e ? e.detail : this.message);
+        //         this.message = '';
+        //     });
+        // },
+        // newMessage(info, isSend = true, messageType = 'TEXT', beenRead = false) {
+        //     let data = {
+        //         topicId: this.topicInfo.id,
+        //         info: info,
+        //         messageType: messageType,
+        //         beenRead: beenRead
+        //     };
+        //     if (isSend) {
+        //         data.sendId = this.$store.state.userInfo.id;
+        //         data.resId = this.toUserId;
+        //     } else {
+        //         data.sendId = this.toUserId;
+        //         data.resId = this.$store.state.userInfo.id;
+        //     }
+        //     this.$http.postJson('/message/save', data).then(res => {
+        //         this.getList();
+        //     });
+        // },
+        // addEmoji(emoji) {
+        //     this.message = this.message + emoji + ' ';
+        // }
+    }
+};
+</script>
+<style lang="less" scoped>
+/deep/ page {
+    background-color: @bg;
+}
+.chat-list {
+    padding: 10px 20px 100px;
+    z-index: 1;
+}
+
+.bottom-box {
+    .bottom();
+    background-color: #fff;
+    position: fixed;
+    z-index: 200;
+    bottom: 0;
+    left: 0;
+    right: 0;
+}
+.input-box {
+    .flex();
+
+    .icon {
+        width: 28px;
+        height: 28px;
+        margin-left: 12px;
+    }
+
+    ._van-field {
+        flex-grow: 1;
+    }
+
+    padding: 8px 20px;
+}
+
+/deep/ .input-box {
+    .van-cell {
+        --cell-background-color: @bg;
+
+        border-radius: 8px;
+        &::after {
+            content: none;
+        }
+    }
+
+    input {
+        font-weight: normal;
+    }
+
+    .van-button {
+        --button-small-min-width: 50px;
+        margin-left: 10px;
+    }
+}
+
+.attach-box {
+    transition: height ease-in-out 0.3s;
+    padding: 0 20px;
+    overflow: hidden;
+    z-index: 2;
+    .up-btn {
+        width: 60px;
+        height: 60px;
+        border-radius: 8px;
+        border: 1px solid #f5f7fa;
+        .flex();
+        justify-content: center;
+        img {
+            width: 30px;
+            height: 20px;
+            display: block;
+        }
+
+        &:active {
+            background-color: #00000010;
+        }
+    }
+}
+
+.loading {
+    padding: 20px;
+    .flex();
+    justify-content: center;
+}
+.emojiList {
+    padding: 5px 0;
+    overflow: auto;
+    box-sizing: border-box;
+    height: 100%;
+    .flex();
+    flex-wrap: wrap;
+}
+.emoji-item {
+    width: 20%;
+    flex-shrink: 0;
+    text-align: center;
+    padding: 10px 0;
+    margin-top: 10px;
+    border-radius: 2px;
+    &:active {
+        background-color: #00000010;
+    }
+    font-family: 'color-emoji';
+    font-size: 25px;
+}
+</style>

+ 1 - 1
src/pages/mine.vue

@@ -36,7 +36,7 @@
                     <van-grid-item text="地址" @click="navigateTo('/pages/chooseAddress')">
                         <img class="gird-icon" src="../native/tabbar/info_icon_dizhi@3x.png" slot="icon" alt="" />
                     </van-grid-item>
-                    <van-grid-item text="消息">
+                    <van-grid-item text="消息" @click="navigateTo('/pages/news')">
                         <img class="gird-icon" src="../native/tabbar/info_icon_xiaoxi@3x.png" slot="icon" alt="" />
                     </van-grid-item>
                 </van-grid>

+ 88 - 3
src/pages/news.vue

@@ -1,7 +1,92 @@
+<config>
+{
+    "navigationBarTitleText": "消息中心",
+    "navigationStyle": "default",
+    "usingComponents": {
+        "van-swipe-cell": "/vant/swipe-cell/index"
+    }
+}
+</config>
 <template>
-    <div></div>
+    <div>
+        <block v-for="item in list" :key="item.id">
+            <van-swipe-cell :right-width="65">
+                <news-info :info="item" :dot="item.unread"></news-info>
+                <div slot="right" class="del" @click="closeNews(item)">删除</div>
+            </van-swipe-cell>
+        </block>
+
+        <van-empty
+            v-if="empty"
+            image="https://ticket-exchange.oss-cn-hangzhou.aliyuncs.com/wechat/kong_png_xiaoxiliebiao.png"
+            description="你还没有任何消息哦~"
+        >
+        </van-empty>
+    </div>
 </template>
+
 <script>
-export default {};
+import NewsInfo from '../components/NewsInfo.vue';
+export default {
+    name: 'News',
+    components: {
+        NewsInfo
+    },
+    data() {
+        return {
+            list: [],
+            empty: true
+        };
+    },
+    onShow() {
+        this.loginMethods();
+    },
+    methods: {
+        loginMethods() {
+            if (this.isLogin) {
+                this.empty = false;
+                this.showLoading();
+                this.$http.get('/topic/getMyTopicList').then(res => {
+                    this.list = res;
+                    this.hideLoading();
+                    if (res.length === 0) {
+                        this.empty = true;
+                    }
+                });
+            }
+        },
+        closeNews(info) {
+            wx.showModal({
+                content: '确定要删除该聊天记录吗?',
+                confirmColor: this.$colors.prim,
+                success: res => {
+                    if (res.confirm) {
+                        this.$http.post('/topic/del/' + info.id).then(res => {
+                            this.toast('删除成功', 'success');
+                            setTimeout(() => {
+                                this.loginMethods();
+                            }, 1000);
+                        });
+                    }
+                }
+            });
+        }
+    }
+};
 </script>
-<style lang="less"></style>
+<style lang="less" scoped>
+/deep/ .van-empty {
+    padding-top: 80px !important;
+}
+
+.del {
+    background-color: red;
+    height: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: #fff;
+    font-size: 12px;
+    width: 65px;
+}
+</style>