微信小程序实战,用vue3实现每日浪漫情话推荐~

微信小程序实战,用vue3实现每日浪漫情话推荐~ 之前做了个恋爱话术微信小程序,实现高情商的恋爱聊天。 但最近突然发现,每天早上给女朋友发一段优美情话可以让她开心一整天,但无奈自己的语言水平确实有限,不能随手拈来,着实让人有点不爽。 不过办法总比困难多,作为高情商的程序猿,来源于日常生活的需求往往是咱们最大的动力,必须盘他,所以想着在之前的恋爱话术小程序上在加一个每日情话推荐的功能。

1、前言

之前做了个恋爱话术微信小程序,满足了日常聊天的需要,实现高情商的恋爱聊天。最近也完成了话术库的优化更新,新增30万条话术数据,同时使用了分词技术,匹配更完善。

但最近突然发现,每天早上给女朋友发一段优美情话可以让她开心一整天,但无奈自己的语言水平确实有限,不能随手拈来,着实让人有点不爽。

不过办法总比困难多,作为高情商的程序猿,来源于日常生活的需求往往是咱们最大的动力,必须盘他,所以想着在之前的恋爱话术小程序上在加一个每日情话推荐的功能。

希望这个程序对其他单身或者恋爱中的兄弟们有所帮助,也希望兄弟们关注加三连支持一波哈~~~

2、推荐接口简介

辣鸡服务器,兄弟们轻点调哈

浪漫推荐情话开放接口

接口地址:https://yin-feng.top/open/getRecommendLove/open/getRecommendLove

请求方式:POST

请求数据类型:application/json

响应数据类型:*/*

请求参数:

{}

响应参数:

参数名称参数说明类型schema
content内容string
ididinteger(int64)integer(int64)
score分数integer(int32)integer(int32)
title标题string

响应示例:

[
        {
                "content": "",
                "id": 0,
                "score": 0,
                "title": ""
        }
]

3、前端页面开发

老样子,咱们还是使用uniapp框架来开发,uniapp的介绍就不多说了哈。

3.1 配置pages.json文件

我们这次打算配两个菜单,包括浪漫情话推荐和话术搜索。主要在pages.json里面就行配置。

pages.json 文件用来对 uni-app 进行全局配置,决定页面文件的路径、窗口样式、原生的导航栏、底部的原生tabbar 等。它类似微信小程序中app.json的页面管理部分。

pages.json 中提供 tabBar 配置,不仅仅是为了方便快速开发导航,更重要的是在App和小程序端提升性能。在这两个平台,底层原生引擎在启动时无需等待js引擎初始化,即可直接读取 pages.json 中配置的 tabBar 信息,渲染原生tab。

官方文档参考tabBar

特别注意

  1. 当设置 position 为 top 时,将不会显示 icon
  2. tabBar 中的 list 是一个数组,只能配置最少2个、最多5个 tab,tab 按数组的顺序排序。
  3. tabbar 切换第一次加载时可能渲染不及时,可以在每个tabbar页面的onLoad生命周期里先弹出一个等待雪花(hello uni-app使用了此方式)
  4. tabbar 的页面展现过一次后就保留在内存中,再次切换 tabbar 页面,只会触发每个页面的onShow,不会再触发onLoad。
  5. 顶部的 tabbar 目前仅微信小程序上支持。需要用到顶部选项卡的话,建议不使用 tabbar 的顶部设置,而是自己做顶部选项卡。

新增浪漫情话页面配置

{
                        "path": "pages/recommend/recommend",
                        "style": {
                                "navigationBarTitleText": "浪漫情话",
                                "backgroundColor": "#eeeeee",
                                "enablePullDownRefresh": false
                        }

                }

添加两个tab标签

"tabBar": {
                "color": "#7A7E83",
                "selectedColor": "#0055ff",
                "borderStyle": "black",
                "backgroundColor": "#ffffe1",
                "height":"60px",
                "fontSize":"18px",
                "list": [
                        {
                                "pagePath": "pages/recommend/recommend",
                                "iconPath": "static/imgs/love1.png",
                                "selectedIconPath": "static/imgs/love2.png",
                                "text": "浪漫情话"
                        },
                        {
                                "pagePath": "pages/index/index",
                                "iconPath": "static/imgs/爱心1.png",
                                "selectedIconPath": "static/imgs/爱心2.png",
                                "text": "话术搜索"
                        }
                        
                ]
        }

完整的page.json文件

{
        "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
                {
                        "path": "pages/recommend/recommend",
                        "style": {
                                "navigationBarTitleText": "浪漫情话",
                                "backgroundColor": "#eeeeee",
                                "enablePullDownRefresh": false
                        }

                },
                {

                        "path": "pages/index/index",
                        "style": {
                                "navigationBarTitleText": "恋爱话术",
                                "backgroundColor": "#eeeeee",
                                "enablePullDownRefresh": false
                        }
                }, {
                        "path": "component/WebView/WebView",
                        "style": {
                                "navigationBarTitleText": "",
                                "enablePullDownRefresh": false
                        }

                }

        ],
        "tabBar": {
                "color": "#7A7E83",
                "selectedColor": "#0055ff",
                "borderStyle": "black",
                "backgroundColor": "#ffffe1",
                "height":"60px",
                "fontSize":"18px",
                "list": [
                        {
                                "pagePath": "pages/recommend/recommend",
                                "iconPath": "static/imgs/love1.png",
                                "selectedIconPath": "static/imgs/love2.png",
                                "text": "浪漫情话"
                        },
                        {
                                "pagePath": "pages/index/index",
                                "iconPath": "static/imgs/爱心1.png",
                                "selectedIconPath": "static/imgs/爱心2.png",
                                "text": "话术搜索"
                        }
                        
                ]
        },
        "globalStyle": {
                "navigationBarTextStyle": "black",
                "navigationBarTitleText": "恋爱话术",
                "navigationBarBackgroundColor": "#ffffe1",
                "backgroundColor": "#f5ffff"
        }
}

微信小程序实战,用vue3实现每日浪漫情话推荐~

3.2 封装api接口

主要在http.api.js里面配置,这个文件之前也包含了咱们的话术搜索接口

import service from './http.interceptor.js'

const api = {
        // 话术搜索
        getLoveChat: params => service.post('/open/getLoveChat', params),
        getBanner: () => service.post('/open/getBanner'),
        // 浪漫情话推荐
        getRecommendLove: () => service.post('/open/getRecommendLove'),
        loveScore: params => service.post('/open/loveScore', params),
}

export default api

3.3 编写浪漫情话页面

咱们还是使用vue3加uniapp的语法进行页面开发,同时为了使页面更优美,封装一个瀑布流组件进行渲染数据。下图就是咱们要实现的大致效果

微信小程序实战,用vue3实现每日浪漫情话推荐~

3.3.1 封装瀑布流组件

<template>
        <view class="u-waterfall">
                <view  class="u-column">
                        <slot name="left" :leftList="leftList"></slot>
                </view>
                <view  class="u-column">
                        <slot name="right" :rightList="rightList"></slot>
                </view>
        </view>
</template>

<script>
        export default {
                name: "waterfall",
                props: {
                        value: {
                                // 瀑布流数据
                                type: Array,
                                required: true,
                                default: function() {
                                        return [];
                                }
                        }
                },
                data() {
                        return {
                                leftList: [],
                                rightList: [],
                                tempList: []
                        }
                },
                watch: {
                        copyFlowList(nVal, oVal) {
                                this.tempList = this.cloneData(this.copyFlowList);
                                this.leftList = []
                                this.rightList = []
                                this.splitData();
                        }
                },
                mounted() {
                        this.tempList = this.cloneData(this.copyFlowList);
                        this.splitData();
                },
                computed: {
                        // 破坏flowList变量的引用,否则watch的结果新旧值是一样的
                        copyFlowList() {
                                return this.cloneData(this.value);
                        }
                },
                methods: {
                        async splitData() {
                                if (!this.tempList.length) return;
                                let leftRect = await this.$uGetRect('#u-left-column');
                                let rightRect = await this.$uGetRect('#u-right-column');
                                // 如果左边小于或等于右边,就添加到左边,否则添加到右边
                                let item = this.tempList[0];
                                // 解决多次快速上拉后,可能数据会乱的问题,因为经过上面的两个await节点查询阻塞一定时间,加上后面的定时器干扰
                                // 数组可能变成[],导致此item值可能为undefined
                                if (!item) return;
                                if (leftRect.height < rightRect.height) {
                                        this.leftList.push(item);
                                } else if (leftRect.height > rightRect.height) {
                                        this.rightList.push(item);
                                } else {
                                        // 这里是为了保证第一和第二张添加时,左右都能有内容
                                        // 因为添加第一张,实际队列的高度可能还是0,这时需要根据队列元素长度判断下一个该放哪边
                                        if (this.leftList.length <= this.rightList.length) {
                                                this.leftList.push(item);
                                        } else {
                                                this.rightList.push(item);
                                        }
                                }
                                // 移除临时列表的第一项
                                this.tempList.splice(0, 1);
                                // 如果临时数组还有数据,继续循环
                                if (this.tempList.length) {
                                        this.splitData();
                                } else {
                                        // 在这里模拟触发 我们定义的全局事件 实现数据通信的目的
                                        let height = (leftRect.height > rightRect.height ? leftRect.height : rightRect.height) + 120
                                        uni.$emit('swiperHeightChange', height + 'px')
                                }
                                
                        },
                        // 复制而不是引用对象和数组
                        cloneData(data) {
                                return JSON.parse(JSON.stringify(data));
                        },
                }
        }
</script>

<style  scoped>
        @mixin vue-flex($direction: row) {
                /* #ifndef APP-NVUE */
                display: flex;
                flex-direction: $direction;
                /* #endif */
        }

        .u-waterfall {
                @include vue-flex;
                flex-direction: row;
                align-items: flex-start;
        }

        .u-column {
                @include vue-flex;
                flex: 1;
                flex-direction: column;
                height: auto;
        }

        .u-image {
                width: 100%;
        }
</style>

3.3.2 template代码编写

这里在引入瀑布流组件之后可直接在html里面使用

<template>
        <view>
                <view class="change" @click="changeContent">
                        没找到想要的?赶紧点击这里
                        <text class="change-btn">换一批</text>
                        <view class="card">
                                点击下方卡片可直接复制
                        </view>
                </view>
                <waterfall :value="dataList">
                        <template v-slot:left="left">
                                <view v-for="item in left.leftList" :key="item.id" class="left-content" @click="copy(item)">
                                        <view class="item">
                                                {{item.content}}
                                        </view>
                                </view>
                        </template>
                        <template v-slot:right="right">
                                <view v-for="item in right.rightList" :key="item.id" class="right-content" @click="copy(item)">
                                        <view class="item">
                                                {{item.content}}
                                        </view>
                                </view>
                        </template>
                </waterfall>
        </view>
</template>

3.3.3 js核心方法编写

关键代码也不多,主要是一些请求数据的方法和响应式变量的定义。

<script>
        import {
                toRefs,
                reactive,
                onMounted
        } from 'vue'
        import waterfall from '../../component/waterfall/index.vue'
        import api from '../../axios/http.api.js'
        export default {
                name: 'content',
                components: {
                        waterfall
                },
                setup() {
                        const state = reactive({
                                dataList: [],
                                loading: false,
                        })
                        const methods = reactive({
                                getRecommendLove: async () => {
                                        state.loading = true
                                        let res = await api.getRecommendLove()
                                        state.loading = false
                                        if (res.code) {
                                                uni.showToast({
                                                        title: res.msg,
                                                        icon: 'none',
                                                        position: 'top'
                                                })
                                        }
                                        state.dataList = res.data
                                },
                                copy(item) {
                                        // 复制话术到剪切板
                                        uni.setClipboardData({
                                                data: item.content
                                        })
                                        methods.score(item.id)
                                },
                                // 换一批
                                changeContent() {
                                        methods.getRecommendLove()
                                },
                                // 打分
                                async score(id) {
                                        let res = await api.loveScore({
                                                id
                                        })
                                        if (res.code) {
                                                uni.showToast({
                                                        title: res.msg,
                                                        icon: 'none',
                                                        position: 'top'
                                                })
                                        }
                                }

                        })
                        onMounted(() => {
                                methods.getRecommendLove()
                                // 分享菜单
                                wx.showShareMenu({
                                        withShareTicket: true,
                                        menus: ['shareAppMessage', 'shareTimeline']
                                })

                        })
                        return {
                                ...toRefs(state),
                                ...toRefs(methods)
                        }
                }
        }
</script>

3.3.4 css样式代码编写

咱们毕竟不是专业的前端,所以样式就随便应付一下就行,兄弟们不要介意哈

<style  scoped>
        .change {
                width: 100%;
                height: 120px;
                border-bottom: 2px #aaaaff solid;
                border-radius: 0 0 50px 50px;
                background-size: 100% 100%;
                opacity: 0.8;
                padding-top: 45px;
                text-align: center;
                font-size: 20px;
                color: #000000;
                background-image: url(../../static/imgs/img4.png);

                .change-btn {
                        display: inline-block;
                        padding: 2px;
                        color: red;
                }

                .card {
                        padding-top: 12px;
                        font-size: 13px;
                }
        }

        .left-content {
                margin: 15px 5px 15px 10px;
                padding: 8px;
                border-radius: 6px;
                background: #aaffff linear-gradient(44deg, #aaffff 0%, #F4F8FF 100%);
                font-size: 16px;
                letter-spacing: 5px;

                .item {
                        margin-bottom: 10px;
                }
        }

        .right-content {
                margin: 15px 10px 15px 5px;
                padding: 8px;
                border-radius: 6px;
                background: #aaffff linear-gradient(44deg, #aaffff 0%, #F4F8FF 100%);
                font-size: 16px;
                letter-spacing: 5px;

                .item {
                        margin-bottom: 10px;
                }
        }
</style>

微信小程序实战,用vue3实现每日浪漫情话推荐~

4、发布微信小程序

发布流程参考这篇博客恋爱话术微信小程序

微信小程序实战,用vue3实现每日浪漫情话推荐~

兄弟们可以扫描下面的太阳码进行体验哈

微信小程序实战,用vue3实现每日浪漫情话推荐~

5、总结

源码地址在这哈,会保持一直开源,希望兄弟们多多支持,下了源码的老铁麻烦点个star哈

// 下了源码的老铁麻烦点个star哈
https://gitee.com/yinfeng-code/love-chat-wx.git

肝文不易,希望对其他单身或者恋爱中的兄弟们有所帮助,也希望兄弟们关注加三连支持一波哈