关于chatgpt:unichatGPT基于uniappvue3pinia跨平台chatgpt渲染对话实例

2次阅读

共计 4998 个字符,预计需要花费 13 分钟才能阅读完成。

我的项目简介

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 统一,顶部导航栏和底部菜单栏均是自定义组件实现性能。

组件放在 components 目录下,反对 easycom 引入。

<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 语法 / 代码高亮

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

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

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

uniapp 自定义多行文本输入框

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

<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

正文完
 0