项目简介

uniapp-ttlive 一款基于uni-app+vue3+uview-plus+pinia等技术搭建的支持h5+小程序+App端仿制chatgpt会话模板项目。支持渲染markdown语法及代码高亮。

预览效果

如下图:编译到h5+小程序+App端效果。

使用技术

  • 编码工具:HbuilderX 3.8.4
  • 技术框架:uniapp+vite4+vue3+pinia
  • UI组件库:uni-ui + uview-plus^3.1.31
  • markdown渲染:markdown-it
  • 代码高亮:highlight.js
  • 数据缓存:pinia-plugin-unistorage
  • 支持编译:小程序+H5+APP端

特性

  1. 全屏沉浸式顶部导航条+底部tabbar
  2. 支持解析h5+小程序+App端markdown语法及代码高亮
  3. 使用pinia全局状态管理
  4. 基于uview-plus跨端vue3组件库
  5. 支持会话本地缓存

代码目录结构

整个项目采用uni-app vue3 setup语法编码开发。

uniapp自定义navbar+tabbar组件

如下图:为了整体UI一致,顶部导航栏和底部菜单栏均是自定义组件实现功能。

image.png

组件放在components目录下,支持easycom引入。

e46d23bee115f312c17dc336d8cc50d8_1289798-20230627000746528-933532786.png

<ua-navbar back="false" custom :title="title" size="40rpx" center fixed :bgcolor="bgcolor">
    <template #left>
        <view @click="showSidebar=true"><text class="iconfont ve-icon-menuon"></text></view>
    </template>
    <template #right>
        <text class="iconfont ve-icon-plus fs-36" @click="handleNewChat"></text>
    </template>
</ua-navbar>

调用非常简单,支持自定义插槽功能。目前这两个插件vue2版本已经发布到了插件市场,大家可以去下载使用。

https://ext.dcloud.net.cn/plugin?id=5592
https://ext.dcloud.net.cn/plugin?id=5593

App.vue初始模板配置

在app.vue中处理应用生命周期,获取系统状态条等功能。

<script setup>
    import { provide } from 'vue'
    import { onLaunch, onShow, onHide, onPageNotFound } from '@dcloudio/uni-app'
    
    onLaunch(() => {
        console.log('App Launch')
        
        // 隐藏tabBar
        uni.hideTabBar()
        // 初始化
        initSysInfo()
    })
    
    onShow(() => {
        console.log('App Show')
    })
    
    onHide(() => {
        console.log('App Hide')
    })
    
    onPageNotFound((e) => {
        console.warn('Router Error>>', ` No match path "${e.path}" `);
        uni.redirectTo({
            url: '/pages/404/index'
        })
    })
    
    const initSysInfo = () => {
        uni.getSystemInfo({
            success: (e) => {
                // 获取手机状态栏高度
                let statusBar = e.statusBarHeight
                let customBar
                
                // #ifndef MP
                customBar = statusBar + (e.platform == 'android' ? 50 : 45)
                // #endif
                
                // #ifdef MP-WEIXIN
                // 获取胶囊按钮的布局位置信息
                let menu = wx.getMenuButtonBoundingClientRect()
                // 导航栏高度 = 胶囊下距离 + 胶囊上距离 - 状态栏高度
                customBar = menu.bottom + menu.top - statusBar
                // #endif
                
                // #ifdef MP-ALIPAY
                customBar = statusBar + e.titleBarHeight
                // #endif
                
                // 目前globalData在vue3 setup支持性不好,改为provide/inject方式
                provide('globalData', {
                    statusBarH: statusBar,
                    customBarH: customBar,
                    platform: e.platform
                })
            }
        })
    }
</script>

需要注意:uniapp vue3 setup中自定义全局globalData有兼容问题,大家可以考虑provide/inject替代处理。

main.js配置

在main.js中使用vue3语法、引入uview组件库、Pinia状态管理。

/**
 * 主入口配置
  */

import App from './App'
import { createSSRApp } from 'vue'

// 引入pinia状态管理
import pinia from '@/store'

// 引入uview-plus组件库
import uviewplus from '@/uview-plus'

export function createApp() {
    const app = createSSRApp(App)
    app.use(pinia)
    app.use(uviewplus)
    return {
        app,
        pinia
    }
}

uni-app解析markdown语法/代码高亮

be72578ccee50c58b4eac07b1007e908_1289798-20230627004606258-312937200.jpg

使用了markdown-ithighlight.js插件渲染markdown语法和代码块高亮。

301d041072baf7e16fa041c3294143f6_1289798-20230627004627111-725339449.jpg

至于在uniapp中如何解析markdown语法,这里不作过多介绍,之前有分享过文章,大家可以去看看。

uni-app+vue3渲染markdown语法结构/代码高亮展示

uniapp自定义多行文本输入框

image.png

ua-input组件支持h5+小程序+App端,可单行+多行自适应高度,自定义前缀/后缀插槽等功能。

uaInput组件已经发布到了uniapp插件市场,免费下载使用。

https://ext.dcloud.net.cn/plugin?id=13275

8d89b85642a085bb08c05e27ad9e6567_1289798-20230627011224518-1516348240.png

<template>
    <div
        class="ve__input"
        :class="[
            preClass,
            isClass,
            sizeClass,
            {'is-disabled': isDisabled},
            {'is-resizable': type == 'textarea' && !autosize},
            {'ve__input--group': $slots.prepend || $slots.append},
            {'ve__input--group__prepend': $slots.prepend},
            {'ve__input--group__append': $slots.append}
        ]"
    >
            <!-- 前置插槽(prepend slot) -->
            <div v-if="$slots.prepend" class="ve__input--prepend"><slot name="prepend" /></div>

            <div class="ve__input-wrapper">
                <!-- 输入框前缀 -->
                <div v-if="$slots.prefix || prefixIcon" class="ve__input--prefix">
                    <span class="ve__input--prefix__inner">
                        <slot name="prefix" />
                        <i v-if="prefixIcon" class="iconfont" :class="prefixIcon"></i>
                    </span>
                </div>

                <template v-if="type != 'textarea'">
                    <input
                        class="ve__input-inner"
                        ref="inputRef"
                        :type="showPassword ? (passwordVisible ? 'text' : 'password') : type"
                        :value="modelValue"
                        :name="name"
                        :maxlength="maxlength"
                        :readonly="readonly"
                        :disabled="isDisabled"
                        :placeholder="placeholder"
                        :cursor-spacing="15"
                        :focus="autofocus"
                        @focus="handleFocus"
                        @blur="handleBlur"
                        @input="handleInput"
                        @change="handleChange"
                        @keydown="handleKeydown"
                    />
                </template>
                <template v-else>
                    <textarea
                        class="ve__input-inner ve__textarea-inner"
                        ref="textareaRef"
                        :value="modelValue"
                        :maxlength="maxlength"
                        :readonly="readonly"
                        :disabled="isDisabled"
                        :placeholder="placeholder"
                        :show-confirm-bar="false"
                        :adjust-position="false"
                        :cursor-spacing="15"
                        :focus="autofocus"
                        :auto-height="isTrue(autosize) || isObject(autosize)"
                        :style="textareaStyle"
                        @focus="handleFocus"
                        @blur="handleBlur"
                        @input="handleInput"
                        @change="handleChange"
                        @keydown="handleKeydown"
                    />
                </template>

                <!-- 输入框后缀 -->
                <div v-if="showSuffixVisible" class="ve__input--suffix" @click="handleSearch" @mousedown.prevent>
                    <span class="ve__input--suffix__inner">
                        <!-- 后缀 -->
                        <template v-if="!showClear || !showPwdVisible">
                            <slot name="suffix" />
                            <i v-if="suffixIcon" class="iconfont" :class="suffixIcon"></i>
                        </template>
                        <!-- 清除 -->
                        <i v-if="showClear" class="iconfont ve-icon-close-circle ve__input-clear" @click="handleClear" @mousedown.prevent></i>
                        <!-- 密码可见 -->
                        <i v-if="showPwdVisible" class="iconfont ve-icon-hide ve__input-password" :class="{'ve-icon-eye1': passwordVisible}" @click="handlePwdVisible" @mousedown.prevent @mouseup.prevent></i>
                        <!-- 限制字数 -->
                        <em v-if="showLimitWordVisible" class="ve__input-limitword">{{inputLength}} / {{maxlength}}</em>
                    </span>
                </div>
            </div>

            <!-- 后置插槽(append slot) -->
            <div v-if="$slots.append" class="ve__input--append" @click="handleSearch" @mousedown.prevent><slot name="append" /></div>
    </div>
</template>

目前基于uniapp+vite4+pinia构建跨端chatgpt实例就暂时分享到这里。

最后附上两个最新实战项目
https://segmentfault.com/a/1190000043667464
https://segmentfault.com/a/1190000043886272


xiaoyan2017
765 声望314 粉丝

web前端开发爱好者,专注于前端h5、jquery、vue、react、angular等技术研发实战项目案例。