【原创】uniapp+vue3多端chatgpt实例项目uniChatGpt

举报
Andy Yan 发表于 2023/06/28 11:08:24 2023/06/28
【摘要】 uniapp支持vue3语法有一段时间了,这次就基于uni-app+vite4搭建了一个跨端项目。uni-chatGpt 一款基于uni-app+vue3+pinia+markdown等技术搭建的跨多端模仿ChatGPT会话实例项目。支持兼容h5+小程序+App端。 技术栈编辑器:HbuilderX 3.8.4框架技术:Uniapp+Vite4+Vue3+PiniaUI组件库:uView-p...

uniapp支持vue3语法有一段时间了,这次就基于uni-app+vite4搭建了一个跨端项目。

uni-chatGpt 一款基于uni-app+vue3+pinia+markdown等技术搭建的跨多端模仿ChatGPT会话实例项目。支持兼容h5+小程序+App端。

技术栈

  • 编辑器:HbuilderX 3.8.4
  • 框架技术:Uniapp+Vite4+Vue3+Pinia
  • UI组件库:uView-plus^3.1.31
  • markdown渲染:markdown-it
  • 代码高亮:highlight.js
  • 本地缓存:pinia-plugin-unistorage
  • 弹框组件:ua-popup(基于uniapp封装跨端弹层组件)
  • 自定义组件:导航条ua-navbar+菜单栏ua-tabbar
  • 支持编译:小程序+H5+APP端

项目代码结构

使用HbuilderX创建项目,采用vue3语法编码开发。

mian.js配置

引入组件库、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
    }
}

App.vue模板

使用vue3 setup引入uniapp生命周期,获取状态条高度用于自定义顶部导航栏。

<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解析markdown语法

项目中使用了markdown-ithighlight.js插件进行markdown解析,已经全端支持了。

35add9f4a7a50e040b200e28e6d167ef_bVc8xEp.jpg

10a7a0a77e0b810f7012709bb6ef089a_bVc8xEr.jpg

由于篇幅原因,这里不作uniapp渲染markdown语法的详细介绍了。之前也有过一篇分享文章,大家可以去看看。

https://blog.csdn.net/yanxinyun1990/article/details/131349705

uniapp自定义编辑框

f881f4a0e3890bb553c5041a0985b4b8_bVc8xEM.png

项目中会话编辑框采用自定义组件实现功能,支持单行/多行、自定义插槽等功能。

c09846788b733bf2bed1deb4ecb2c2d1_bVc8xEN.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>

OK,以上就是uniapp+vue3开发多端chatgpt的一些分享,希望对大家有所帮助~~

文章来源: juejin.cn,作者:xiaoyan2015,版权归原作者所有,如需转载,请联系作者。

原文链接:https://juejin.cn/post/7249347651787243581

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。