我的项目简介

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