Vue + Vite 构建 自己的ChartGPT 项目

news2025/1/11 2:41:16

9a69fede8b2044a79dd834e3e48f20b4.png前期回顾f8e3cc1a0f694ac2b665ca2ad14c49d7.png   

两分钟学会 制作自己的浏览器 —— 并将 ChatGPT 接入_彩色之外的博客-CSDN博客自定义浏览器,并集合ChatGPT,源码已公开https://blog.csdn.net/m0_57904695/article/details/130467253?spm=1001.2014.3001.5501

目录

效果图

代码步骤:(配key)

 主页面:

✅   谢谢观看 :


已经部署上线:ChatGPT-彩色之外

效果图

  

代码步骤:(配key)

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  server: {
    host: true
  },
  plugins: [vue()],
  define: {
    'process.env': {
      INSCODE_API_KEY: process.env.INSCODE_API_KEY
    }
  }
})

 主页面:

<template>
  <div class="container ivu-p">
    <!-- 聊天窗口 -->
    <div class="dialog">
      <!-- 使用提示 -->
      <div class="dialog-item" style="display: block; text-align: center;">
        <div class="dialog-item-main" style="max-width: 100%; margin-bottom: 4px; color: #61647b;">试试发送一些问题给我,比如
          <br />"{{ topic }}"
        </div>
        <div><span
            style="padding: 5px 12px; border: 1px solid #444f8a; background-color: #3a4684; color: #fff; border-radius: 8px; cursor: pointer;"
            @click="rewardSupportCard = true">打赏支持 🎉</span></div>
      </div>

      <!-- 回答 -->
      <template v-for="(item, index) in dialogs" :key="index">
        <div class="dialog-item" :class="{ 'dialog-item-me': item.role === 'me', 'dialog-item-ai': item.role === 'ai' }">
          <div class="dialog-item-main" v-html="highlightCode(item.text)">
          </div>
        </div>
      </template>


    </div>
    <!-- 发送信息 -->
    <div class="question ivu-mt">
      <Input v-model.trim="question" type="textarea" :autosize="{ minRows: 4, maxRows: 6 }" placeholder="输入你的问题"
        @keyup.enter="handleSend" v-focus autofocus />
      <Row class="ivu-mt" style="display: flex; justify-content: right;">
        <Col>
        <Button type="primary" size="large" icon="md-send" style="background-color: #3a4684; border-color: #3a4684;"
          :loading="loading" @click="handleSend">发送</Button>
        </Col>
        <Col style="display: none;">
        <Button size="large" class="ivu-ml" icon="md-add" :disabled="loading" @click="handleNewChat">新对话</Button>
        </Col>
      </Row>
    </div>
    <!-- 打赏支持 -->
    <div class="reward-support" v-show="rewardSupportCard">
      <div class="reward-support__card">
        <div class="reward-support__head">
          <button type="button" :class="{ 'reward-support__btn--active': QRCodeModel == 1 }" class="reward-support__btn"
            @click="QRCodeModel = 1">支付宝</button>
          <button type="button" :class="{ 'reward-support__btn--active': QRCodeModel == 2 }"
            :style="{ 'backgroundColor': QRCodeModel == 2 ? '#21aa38' : '' }" class="reward-support__btn"
            @click="QRCodeModel = 2">微信</button>
        </div>
        <div class="reward-support__content">
          <div class="reward-support__QRCode" style="margin-bottom: 10px;">
            <img v-if="QRCodeModel == 1" src="./assets/zhifubao.jpg" alt="">
            <img v-else src="./assets/weixin.jpg" alt="">
          </div>
          <div class="reward-support__text" style="margin-bottom: 10px; text-align: center; color: #baccbe;">
            <span v-if="QRCodeModel == 1">支付宝扫一扫</span>
            <span v-else>微信扫一扫</span>
          </div>
          <div class="reward-support__quit"><span @click="rewardSupportCard = false">下次一定</span></div>
        </div>
      </div>
      <div class="reward-support__shadow" @click.stop="rewardSupportCard = false"></div>
    </div>
    <!-- 设置 -->

  </div>
</template>

<script>
import { fetchEventSource } from '@microsoft/fetch-event-source';
import { apiKey, apiUrl } from './api';

import 'prismjs'
import 'prismjs/themes/prism.css'
import 'prismjs/components/prism-javascript'

export default {
  mounted() {
    Prism.highlightAll();
  },

  data() {
    return {
      question: '',
      loading: false,
      dialogs: [],
      dialog: [],
      rewardSupportCard: false,
      QRCodeModel: 1,
      gptTopic: ["彩色之外怎么这么帅、泰裤辣~", "推荐一些有趣的电影、音乐", "编写一个适合5岁小孩的睡前小故事", "怎么像喜欢的人表白"],
      topic: "",
    }
  },

  created() {
    this.topic = this.gptTopic[parseInt(Math.random() * this.gptTopic.length)]
  },

  methods: {

    highlightCode(text) {
      const code = Prism.highlight(text, Prism.languages.javascript, 'javascript');
      // 如果有 ``` 就换行
      const codeWithNewLines = code.replace(/```[\s\S]*?```/g, (match) => {
        const codeWithoutTicks = match.slice(3, -3);
        return '\n' + codeWithoutTicks + '\n';
      });
      console.log(codeWithNewLines);
      return `<code>${codeWithNewLines}</code>`;
      
    }
    ,
    handleSend() {
      if (this.loading || this.question === '') return;
      this.loading = true;

      const question = this.question;
      this.question = '';

      // Add user dialog to the list
      this.dialogs.push({
        id: this.dialogs.length + 1,
        role: 'me',
        text: question
      });

      const aiDialogID = this.dialogs.length + 1;

      // Add AI dialog to the list
      this.dialogs.push({
        id: aiDialogID,
        role: 'ai',
        text: '让大哥想一下 ...'
      });

      const dialog = this.dialogs.find(item => item.id === aiDialogID);

      // Send request to GPT backend service
      const body = {
        messages: [
          {
            role: 'user',
            content: "你现在是《彩色之外》的AI机器人。" + question
          }
        ],
        apikey: apiKey
      }

      const source = fetchEventSource(apiUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(body),
        onopen: (response) => {
          dialog.text = '';
        },
        onmessage: (msg) => {
          if (msg.data === '[DONE]') {
            this.loading = false;
            return;
          };

          const data = JSON.parse(msg.data);
          const finish_reason = data.choices[0].finish_reason;
          const finish = finish_reason === 'stop' || finish_reason === 'length';
          const content = data.choices[0].delta.content;

          if (finish) {
            this.loading = false;
          } else if (content) {
            const text = content;
            dialog.text += text;
          }
        },
        onerror: (err) => {
          console.log("error", err);
        }
      });
    },

    handleNewChat() {
      this.dialogs = [];
    },
  }
}

</script>

<style>
.copy-btn:hover {
  border: 1px solid #ccc;
  border-radius: 3px;
}

.question {
  display: flex;
}

.question .ivu-mt {
  margin: 0 !important;
}

.ivu-input-wrapper {
  margin-right: 10px;
}

.ivu-input-wrapper textarea {
  height: 40px !important;
  min-height: 40px !important;
}

.ivu-input:hover {
  border-color: #3a4684 !important;
}

.ivu-input:focus {
  box-shadow: 0 0 0 2px rgba(58, 70, 132, .2) !important;
}

.ivu-input:focus {
  border-color: #3a4684 !important;
}

.ivu-btn-primary:focus {
  box-shadow: 0 0 0 2px rgba(58, 70, 132, .2) !important;
}

.ivu-btn:active,
.ivu-btn:hover {
  /* color: #3a4684 !important; */
  border-color: #3a4684 !important;
}

.ivu-btn:focus {
  box-shadow: 0 0 0 2px rgba(58, 70, 132, .2) !important;
}

.reward-support {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.reward-support .reward-support__card {
  position: absolute;
  top: 10%;
  left: 50%;
  transform: translateX(-50%);
  width: 300px;
  padding: 24px;
  border-radius: 12px;
  background-color: #f8f8f8;
  z-index: 1;
}

.reward-support .reward-support__head {
  display: flex;
  justify-content: center;
  margin-bottom: 14px;
}

.reward-support .reward-support__btn {
  width: 65px;
  height: 25px;
  border: 0;
  margin-right: 10px;
  border-radius: 6px;
  font-size: 14px;
  background-color: #ebebeb;
  cursor: pointer;
  color: #8e8e94;
}

.reward-support .reward-support__QRCode {
  width: 163px;
  height: 163px;
  margin: 0 auto;
  background-color: #fff;
}

.reward-support .reward-support__QRCode img {
  width: 100%;
}

.reward-support .reward-support__quit {
  text-align: center;
}

.reward-support .reward-support__quit span {
  width: 120px;
  height: 36px;
  line-height: 36px;
  display: inline-block;
  border-radius: 6px;
  background-color: #ffffff;
  cursor: pointer;
  font-size: 14px;
}

.reward-support .reward-support__quit span:hover {
  background-color: #ebebeb;
}

.reward-support .reward-support__btn--active {
  background-color: #1777ff;
  color: #fff;
}

.reward-support .reward-support__shadow {
  width: 100%;
  height: 100%;
  background-color: #7f7f7f;
  opacity: .7;
}

.container {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.dialog {
  flex: 1;
  overflow: auto;
}

.dialog-item {
  display: flex;
}

.dialog-item-main {
  max-width: 80%;
  padding: 8px;
  word-wrap: break-word;
  word-break: break-all;
  margin-top: 16px;
  border-radius: 4px;
}

.dialog-item-me {
  justify-content: flex-end;
}

.dialog-item-me .dialog-item-main {
  background-color: #95ec69;
}

.dialog-item-ai .dialog-item-main {
  background-color: #eee;
}
</style>

✅   谢谢观看 :

65a8a112b4934898b05e0b83b7947427.gif

7730e2bd39d64179909767e1967da702.jpeg

 _______________________________  期待再见  _______________________________ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/580643.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】软件包管理器/编辑器/yum是应用商店?/vim编辑器什么?

本文思维导图&#xff1a; 文章目录 Linux软件安装关于Linux的软件生态 1.Linux软件包管理器&#xff1a;yum到底是什么关于yum指令&#xff1a;关于yum源 2. rzsz指令1. Linux编辑器——vim编辑器vim编辑器的三种主要模式vim编辑器命令模式常用快捷键&#xff1a;vim操作总结…

spring(事务管理)

事物可以看做是由对数据库若干操作组成的一个单元 事务的作用就是为了保证用户的每一个操作都是可靠的&#xff0c;事务中的每一步操作都 必须成功执行&#xff0c;只要有发生异常就回退到事务开始未进行操作的状态,这些操作 要么都完成&#xff0c;要么都取消&#xff0c;从而…

Linux:/dev/tty、/dev/tty0 和 /dev/console 之间的区别

在Linux操作系统中&#xff0c;/dev/tty、/dev/tty0和/dev/console是三个特殊的设备文件&#xff0c;它们在终端控制和输入/输出过程中扮演着重要的角色。尽管它们看起来很相似&#xff0c;但实际上它们之间存在一些重要的区别。本文将详细介绍这三个设备文件之间的区别以及它们…

浅谈如何fltk项目编译和实现显示中文

目录 一、编译 二、中文显示如何处理&#xff1a; 2.1在发文2天前突然发现&#xff0c;我这个界面显示英文出现问题了&#xff0c;开始我的搜索之旅&#xff0c;一些参考页面有碰到问题也可以看看&#xff1a; 2.2、 那就开始翻翻官方自带的例程吧&#xff0c;看看他如何显…

Join的连接原理

1. 连接简介 1.1 连接的本质 连接就是把各个表中的记录都取出来进行一次匹配&#xff0c;并把匹配后的组合发送给客户端。如果连接查询中的结果集中包含一个表中的每一条记录与另一个表中的每一条记录相互匹配的组合&#xff0c;那么这样的结果集就可以称为笛卡尔积。 1.2 连…

计算机网络基础知识(七)—— 什么是HTTPS协议?你听我“瞎掰”

文章目录 01 | 工作原理02 | SSL/TLS协议2.1 | 握手协议2.2 | 更换密码协议&#xff08;Change Cipher Spec Protocol&#xff09;2.3 | 警告协议&#xff08;Alert Protocol&#xff09;2.4 | 应用数据协议&#xff08;Application Data Protocol&#xff09; 03 | 加密算法3.…

CSRF及SSRF漏洞案例讲解(29)

讲解一下这个图片&#xff0c;用户在浏览器登陆银行界面发送一个请求&#xff0c;通过转账&#xff0c;转载的数据包假如是下面那串字符&#xff0c;黑客呢就自己一个网站或控制一个网站&#xff0c;去写入一个代码&#xff0c;这个代码就是请求这个数据包&#xff0c;刚好这个…

人工智能学习07--pytorch19--目标检测:常见指标(mAP计算+coco评价标准)

怎样才算正确检测到一个目标&#xff1f; 什么是IOU&#xff1a; https://blog.csdn.net/qq_51831335/article/details/125719420 mAP计算方法&#xff1a; 假设针对某一类别的AP情况 TP&#xff1a;预测正确的边界框个数。预测边界框与GT-box的IOU>0.5 FP&#xff1a;假…

原工程运行正常,重新复制一份后再 npm install 后再运行就报错的解决办法

原工程&#xff0c;运行正常 将刚刚的工程复制一份呢&#xff0c;重新 npm install 再 npm run serve 就报错 出现这个问题十之八九都是依赖的问题。有可能是因为这个工程里面之前安装过一些东西&#xff0c;后来莫名其妙的就把 package.json 里面相关的依赖给删掉了。但由于原…

lwIP 开发指南

目录 lwIP 初探TCP/IP 协议栈是什么TCP/IP 协议栈架构TCP/IP 协议栈的封包和拆包 lwIP 简介lwIP 源码下载lwIP 文件说明 MAC 内核简介PHY 芯片介绍YT8512C 简介LAN8720A 简介 以太网接入MCU 方案 lwIP 无操作系统移植lwIP 带操作系统移植ARP 协议ARP 协议的简介ARP 协议的工作流…

uni-app项目运行和项目结构目录讲解

UNI-APP学习系列 uni-app项目运行和项目结构目录讲解 文章目录 UNI-APP学习系列前言总结 前言 UNI-APP学习系列之uni-app项目运行和项目结构目录讲解 运行项目 使用 pnpm 包管理工具 # 查看是否安装pnpmpnpm -v# 无则安装npm install -g pnpm下载依赖 pnpm i运行pnpm dev:h…

Window的创建

Window的创建 上一篇说到了Window和WindowManager的关系并且讲述了WindowManager如何添加Window与Window内部的三个方法的实现 这篇主要讲几个常见的Window的创建比如Activity,Dialog和Toast 其中Activity属于应用Window Dialog属于子Window Toast属于系统Window z-order…

python基础知识(二):变量和常用数据类型

目录 1. 变量1.1 变量的定义1.2 变量的命名规则 2. 常用数据类型2.1 字符串2.1.1 字符串的常用方法2.1.1.1 title()方法&#xff1a;将字符串中的单词首字母大写2.1.1.2 upper()方法&#xff1a;将字符串中的单词字母全大写2.1.1.3 lower()方法&#xff1a;将字符串中的单词字母…

什么是分段路由?如何在网络中实施分段路由?

在计算机网络中&#xff0c;分段路由&#xff08;Subnetting&#xff09;是一种将一个大的网络划分为多个较小子网的技术。它允许网络管理员更有效地分配 IP 地址和管理网络流量。本文将详细介绍分段路由的概念、原理以及如何在网络中实施分段路由。 1. 分段路由的概念 分段路…

【深入浅出Spring Security(一)】Spring Security的整体架构

Spring Security的整体架构 一、整体架构认证&#xff08;Authentication&#xff09;AuthenticationManagerAuthentication登录后的数据保存&#xff08;SecurityContextHolder&#xff09; 授权&#xff08;Authorization&#xff09;ConfigAttribute 二、总结 这篇博客所述主…

CISCN 2023 初赛 pwn——Shellwego 题解

这是一个用go语言写的elf程序&#xff0c;没有PIE。这也是本蒟蒻第一次解go pwn题&#xff0c;故在此记录以便参考。 而且&#xff0c;这还是一个全部符号表被抠的go elf&#xff0c;直接面对一堆不知名的函数实在有些应付不来&#xff0c;因此在比赛时委托逆向的队友把符号表…

2023/5/28总结

static static:静态&#xff0c;可以修饰成员方法&#xff0c;成员变量。&#xff08;是所有成员共享的&#xff09; static修饰的特点&#xff1a; 被类的所有对象共享&#xff08;判断是否使用静态关键字的条件&#xff09;可以通过类名和对象名调用在定义对象时&#xff0c;…

图【数据结构】

目录 一、图的定义和基本术语 二、图的类型定义 三、图的存储结构 1、数组&#xff08;邻接矩阵&#xff09;表示法 二、邻接表&#xff08;链式&#xff09;表示法 三、图的邻接表的存储表示 四、十字链表与邻接多重链表 &#xff08;1&#xff09;十字链表 &#xff…

113.删除有序数组中的重复项 removeDuplicatesFromSortedArray

文章目录 题目描述解题思路代码详解运行截图 题目描述 题目链接 给你一个 升序排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元…

Java中ReentrantLock的概念深入理解

ReentrantLock和Synchronized的区别 核心区别 ReentrantLock是一个类&#xff0c;Synchronized是Java中的一个关键字。 两者都是JVM层面实现互斥锁的方式 效率区别 线程竞争激烈推荐使用ReentrantLock去实现&#xff0c;不存在锁竞争观念&#xff1b; Synchronized是存在锁升…