|
|
@@ -1,846 +0,0 @@
|
|
|
-<script>
|
|
|
-import Prompt from '!!raw-loader!./system/prompt.md'
|
|
|
-const systemPlugin = require.context('./system/plugin', true, /\.vue$/)
|
|
|
-const otherPlugin = require.context('./plugin', true, /\.vue$/)
|
|
|
-const components = {}
|
|
|
-otherPlugin.keys().forEach(key => {
|
|
|
- initComponent(otherPlugin(key))
|
|
|
-})
|
|
|
-systemPlugin.keys().forEach(key => {
|
|
|
- initComponent(systemPlugin(key))
|
|
|
-})
|
|
|
-function initComponent(plugin) {
|
|
|
- const data = plugin.default.options.data()
|
|
|
- if (data.type === 'comp') {
|
|
|
- components[plugin.default.options.name] = plugin.default
|
|
|
- }
|
|
|
-}
|
|
|
-import Vue from 'vue'
|
|
|
-import { GoogleGenAI, Type } from '@google/genai'
|
|
|
-export default Vue.extend({
|
|
|
- name: 'Index',
|
|
|
- components: components,
|
|
|
- data() {
|
|
|
- return {
|
|
|
- AiKey: '',
|
|
|
- AiModel: '',
|
|
|
- aiConfig: {},
|
|
|
- showText: false,
|
|
|
- showMessage: false,
|
|
|
- showMenu: false,
|
|
|
- text: '',
|
|
|
- message: '',
|
|
|
- timer: null,
|
|
|
- target: null,
|
|
|
- inProcess: false,
|
|
|
- aiState: '0',
|
|
|
- workLength: 0,
|
|
|
- retryCount: 0,
|
|
|
- aiContext: [],
|
|
|
- tool_name: '',
|
|
|
- tool_input: '',
|
|
|
- tool_data: {},
|
|
|
- toolList: [],
|
|
|
- taskTollList: [],
|
|
|
- pluginList: [],
|
|
|
- componentList: [],
|
|
|
- pageList: [],
|
|
|
- taskList: [],
|
|
|
- prompt: Prompt,
|
|
|
- aboutList: [
|
|
|
- '优化一下首页的SEO数据',
|
|
|
- '翻译一下关于我们页面',
|
|
|
- '站内有多少种产品类型'
|
|
|
- ],
|
|
|
- aboutText: '',
|
|
|
- guideData: {
|
|
|
- step: 0,
|
|
|
- apiKey: '',
|
|
|
- model: ''
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- mounted() {
|
|
|
- this.importPlugin()
|
|
|
- this.getConfig()
|
|
|
- this.workStart()
|
|
|
- this.scrollText()
|
|
|
- this.$bus.$on('sentAi', (tool_data, tool_role) => {
|
|
|
- this.sentAi(tool_data, tool_role)
|
|
|
- })
|
|
|
- this.$bus.$on('runWork', (workData) => {
|
|
|
- this.runWork(workData)
|
|
|
- })
|
|
|
- },
|
|
|
- methods: {
|
|
|
- scrollText() {
|
|
|
- let index = 0
|
|
|
- this.aboutText = this.aboutList[index]
|
|
|
- setInterval(() => {
|
|
|
- if (index === this.aboutList.length) {
|
|
|
- index = 0
|
|
|
- }
|
|
|
- this.aboutText = this.aboutList[index]
|
|
|
- index += 1
|
|
|
- }, 4000)
|
|
|
- },
|
|
|
- importPlugin() {
|
|
|
- systemPlugin.keys().forEach(key => {
|
|
|
- const plugin = systemPlugin(key)
|
|
|
- const data = plugin.default.options.data()
|
|
|
- if (data.type === 'page') {
|
|
|
- this.importAsPage(plugin)
|
|
|
- } else if (data.type === 'tool') {
|
|
|
- this.importAsTool(plugin)
|
|
|
- } else if (data.type === 'task') {
|
|
|
- this.importAsTask(plugin)
|
|
|
- } else if (data.type === 'comp') {
|
|
|
- this.importAsComp(plugin)
|
|
|
- } else {
|
|
|
- console.log(plugin.default.options.name + '类型未知,无法加载')
|
|
|
- }
|
|
|
- })
|
|
|
- otherPlugin.keys().forEach(key => {
|
|
|
- const plugin = otherPlugin(key)
|
|
|
- const data = plugin.default.options.data()
|
|
|
- if (data.type === 'page') {
|
|
|
- this.importAsPage(plugin)
|
|
|
- } else if (data.type === 'tool') {
|
|
|
- this.importAsTool(plugin)
|
|
|
- } else if (data.type === 'task') {
|
|
|
- this.importAsTask(plugin)
|
|
|
- } else if (data.type === 'comp') {
|
|
|
- this.importAsComp(plugin)
|
|
|
- } else {
|
|
|
- console.log(plugin.default.options.name + '类型未知,无法加载')
|
|
|
- }
|
|
|
- })
|
|
|
- },
|
|
|
- importAsComp(plugin) {
|
|
|
- const data = plugin.default.options.data()
|
|
|
- this.componentList.push(plugin.default.options.name)
|
|
|
- this.prompt += data.prompt
|
|
|
- console.log('已从' + plugin.default.options.name + '中加载组件')
|
|
|
- },
|
|
|
- importAsPage(plugin) {
|
|
|
- const data = plugin.default.options.data()
|
|
|
- const routeConfig = data.router
|
|
|
- this.pageList.push({
|
|
|
- name: routeConfig.name,
|
|
|
- title: routeConfig.meta.title
|
|
|
- })
|
|
|
- if (!routeConfig.component) {
|
|
|
- routeConfig.component = plugin.default
|
|
|
- }
|
|
|
- if (data.parent) {
|
|
|
- this.$router.addRoute(data.parent, routeConfig)
|
|
|
- } else {
|
|
|
- this.$router.addRoute(routeConfig)
|
|
|
- }
|
|
|
- this.prompt += data.prompt
|
|
|
- console.log('已加载' + data.router.meta.title + '页面')
|
|
|
- },
|
|
|
- importAsTool(plugin) {
|
|
|
- const tools = plugin.default.options.methods
|
|
|
- for (const key in tools) {
|
|
|
- this.toolList.push({
|
|
|
- name: key,
|
|
|
- tool: tools[key]
|
|
|
- })
|
|
|
- }
|
|
|
- const data = plugin.default.options.data()
|
|
|
- this.prompt += data.prompt
|
|
|
- console.log('已从' + plugin.default.options.name + '中加载' + Object.keys(tools).length + '项能力')
|
|
|
- },
|
|
|
- importAsTask(plugin) {
|
|
|
- const tools = plugin.default.options.methods
|
|
|
- for (const key in tools) {
|
|
|
- this.taskTollList.push({
|
|
|
- name: key,
|
|
|
- tool: tools[key]
|
|
|
- })
|
|
|
- }
|
|
|
- const data = plugin.default.options.data()
|
|
|
- this.prompt += data.prompt
|
|
|
- console.log('已从' + plugin.default.options.name + '中加载' + Object.keys(tools).length + '项技能')
|
|
|
- },
|
|
|
- workStart() {
|
|
|
- setInterval(() => {
|
|
|
- const that = this
|
|
|
- this.workLength = this.taskList.length
|
|
|
- if (this.taskList.length && this.taskList[0].states === 'wait') {
|
|
|
- if (this.retryCount > this.aiConfig.maxRetry) {
|
|
|
- this.postMessage('已达失败重试上限,请检查AI配置以及额度')
|
|
|
- this.taskList.splice(0, 0)
|
|
|
- return
|
|
|
- }
|
|
|
- this.taskList[0].states = 'running'
|
|
|
- this.taskTollList.forEach(tool => {
|
|
|
- if (tool.name === this.taskList[0].type) {
|
|
|
- tool.tool(this.taskList[0].data, that)
|
|
|
- .then(result => {
|
|
|
- this.retryCount = 0
|
|
|
- this.postMessage(this.taskList[0].data.title + '任务结束')
|
|
|
- this.taskList.shift()
|
|
|
- }).catch(err => {
|
|
|
- this.retryCount += 1
|
|
|
- this.postMessage(this.taskList[0].data.title + '任务失败')
|
|
|
- const task = this.taskList.shift()
|
|
|
- if (task.count) {
|
|
|
- if (task.count > this.aiConfig.retryOne) {
|
|
|
- this.postMessage(this.taskList[0].data.title + '已达失败重试上限,任务移除')
|
|
|
- } else {
|
|
|
- task.count += 1
|
|
|
- task.states = 'wait'
|
|
|
- this.taskList.push(task)
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
- }, 100)
|
|
|
- },
|
|
|
- textInput(e) {
|
|
|
- this.text = this.target.innerText
|
|
|
- },
|
|
|
- sent(e) {
|
|
|
- if (this.inProcess && this.aiState === '2') {
|
|
|
- this.resetContext()
|
|
|
- } else if (!this.inProcess) {
|
|
|
- this.inProcess = true
|
|
|
- this.sentAi()
|
|
|
- } else {
|
|
|
-
|
|
|
- }
|
|
|
- },
|
|
|
- guide(tool_data) {
|
|
|
- console.log(tool_data)
|
|
|
- if (tool_data) {
|
|
|
- if (
|
|
|
- tool_data.text === '用户反馈先前的操作或者数据存在错误,请询问是何处错误' ||
|
|
|
- tool_data.text === '用户反馈需要结束对话,若有未完成的工作确认是否放弃工作,然后结束对话') {
|
|
|
- this.inProcess = true
|
|
|
- this.parseAiData({
|
|
|
- context: '在开始使用前,需要先进行相关配置',
|
|
|
- tool_name: 'initSelect',
|
|
|
- finish: false,
|
|
|
- data: JSON.stringify([
|
|
|
- { label: '继续', value: 'go' }
|
|
|
- ])
|
|
|
- })
|
|
|
- this.guideData.step = 0
|
|
|
- return
|
|
|
- }
|
|
|
- if (this.guideData.step === 0) {
|
|
|
- this.guideData.step = 1
|
|
|
- this.parseAiData({
|
|
|
- context: '请输入Gemini apiKey。可以前往https://aistudio.google.com/apikey获取',
|
|
|
- tool_name: 'initInput',
|
|
|
- finish: false,
|
|
|
- data: ''
|
|
|
- })
|
|
|
- return
|
|
|
- }
|
|
|
- if (this.guideData.step === 1) {
|
|
|
- if (tool_data.text.length < 10) {
|
|
|
- this.parseAiData({
|
|
|
- context: 'apiKey似乎不对。可以前往https://aistudio.google.com/apikey获取',
|
|
|
- tool_name: 'initInput',
|
|
|
- finish: false,
|
|
|
- data: ''
|
|
|
- })
|
|
|
- return
|
|
|
- }
|
|
|
- this.guideData.apiKey = tool_data.text
|
|
|
- this.guideData.step = 2
|
|
|
- this.parseAiData({
|
|
|
- context: '你想使用哪个Ai模型?',
|
|
|
- tool_name: 'initSelect',
|
|
|
- data: JSON.stringify([
|
|
|
- {
|
|
|
- label: 'Gemini 2.5 Pro',
|
|
|
- value: 'gemini-2.5-pro'
|
|
|
- },
|
|
|
- {
|
|
|
- label: 'Gemini 2.5 Flash',
|
|
|
- value: 'gemini-2.5-flash'
|
|
|
- },
|
|
|
- {
|
|
|
- label: 'Gemini 2.5 Flash Lite',
|
|
|
- value: 'gemini-2.5-flash-lite'
|
|
|
- }
|
|
|
- ])
|
|
|
- })
|
|
|
- return
|
|
|
- }
|
|
|
- if (this.guideData.step === 2) {
|
|
|
- this.guideData.step = 3
|
|
|
- this.guideData.model = tool_data.value
|
|
|
- const AiConfig = {}
|
|
|
- AiConfig.setting = {
|
|
|
- maxRetry: 20,
|
|
|
- retryOne: 5,
|
|
|
- language: '',
|
|
|
- debug: false
|
|
|
- }
|
|
|
- AiConfig.Google_Ai = {
|
|
|
- apiKey: this.guideData.apiKey,
|
|
|
- model: this.guideData.model
|
|
|
- }
|
|
|
- AiConfig.active = 'Google_Ai'
|
|
|
- localStorage.setItem('aiConfig', JSON.stringify(AiConfig))
|
|
|
- this.parseAiData({
|
|
|
- context: '一切就绪,手动刷新页面后就可以使用自由聊天功能了。',
|
|
|
- tool_name: 'initSelect',
|
|
|
- finish: false,
|
|
|
- data: JSON.stringify([
|
|
|
- { label: '刷新页面', value: 'go' }
|
|
|
- ])
|
|
|
- })
|
|
|
- return
|
|
|
- }
|
|
|
- if (this.guideData.step === 3) {
|
|
|
- location.reload()
|
|
|
- }
|
|
|
- } else {
|
|
|
- this.inProcess = true
|
|
|
- this.parseAiData({
|
|
|
- context: '在开始使用前,需要先进行相关配置',
|
|
|
- tool_name: 'initSelect',
|
|
|
- finish: false,
|
|
|
- data: JSON.stringify([
|
|
|
- { label: '继续', value: 'go' }
|
|
|
- ])
|
|
|
- })
|
|
|
- }
|
|
|
- },
|
|
|
- sentAi(tool_data, tool_role) {
|
|
|
- if (this.AiKey === '') {
|
|
|
- this.guide(tool_data)
|
|
|
- return
|
|
|
- }
|
|
|
- const ai = new GoogleGenAI({
|
|
|
- apiKey: this.AiKey
|
|
|
- })
|
|
|
- let role = 'user'
|
|
|
- if (tool_role) { role = tool_role }
|
|
|
- const model = this.AiModel
|
|
|
- const config = {
|
|
|
- thinkingConfig: {
|
|
|
- thinkingBudget: -1
|
|
|
- },
|
|
|
- responseMimeType: 'application/json',
|
|
|
- responseSchema: {
|
|
|
- type: Type.OBJECT,
|
|
|
- required: ['context', 'data', 'finish'],
|
|
|
- properties: {
|
|
|
- context: {
|
|
|
- type: Type.STRING
|
|
|
- },
|
|
|
- tool_name: {
|
|
|
- type: Type.STRING
|
|
|
- },
|
|
|
- data: {
|
|
|
- type: Type.STRING
|
|
|
- },
|
|
|
- finish: {
|
|
|
- type: Type.BOOLEAN
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- if (this.aiContext.length === 0) {
|
|
|
- this.aiContext.push({
|
|
|
- role: 'model',
|
|
|
- parts: [{
|
|
|
- text: this.prompt + Prompt
|
|
|
- }]
|
|
|
- })
|
|
|
- this.aiContext.push({
|
|
|
- role: role,
|
|
|
- parts: [{
|
|
|
- text: this.text
|
|
|
- }]
|
|
|
- })
|
|
|
- } else {
|
|
|
- this.aiContext.push({
|
|
|
- role: role,
|
|
|
- parts: [{
|
|
|
- text: '工具调用成功,数据为' + JSON.stringify(tool_data)
|
|
|
- }]
|
|
|
- })
|
|
|
- }
|
|
|
- this.aiState = '0'
|
|
|
- this.tool_name = ''
|
|
|
- this.tool_input = ''
|
|
|
- this.tool_data = ''
|
|
|
- ai.models.generateContent({
|
|
|
- model: model,
|
|
|
- config: config,
|
|
|
- contents: this.aiContext
|
|
|
- }).then(response => {
|
|
|
- this.parseAiData(JSON.parse(response.text))
|
|
|
- }).catch(error => {
|
|
|
- const errMessage = JSON.parse(error.message.split(' . ')[1])
|
|
|
- this.aiState = '2'
|
|
|
- this.target.innerText = '呼哟!出错了,把这些内容给到开发说不定有用:' + errMessage.error.message
|
|
|
- console.log(errMessage)
|
|
|
- })
|
|
|
- },
|
|
|
- parseAiData(data) {
|
|
|
- this.target.innerText = data.context
|
|
|
- this.aiContext.push({
|
|
|
- role: 'model',
|
|
|
- parts: [{
|
|
|
- text: JSON.stringify(data)
|
|
|
- }]
|
|
|
- })
|
|
|
- if (!data.tool_name && !data.finish) {
|
|
|
- this.sentAi('若会话未完成,必须调用一个工具', 'user')
|
|
|
- return
|
|
|
- }
|
|
|
- this.aiState = '1'
|
|
|
- this.useTool(data.tool_name ? data.tool_name : '', data.data ? JSON.parse(data.data) : {})
|
|
|
- if (data.finish) {
|
|
|
- this.aiState = '2'
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 任务
|
|
|
- runWork(workData) {
|
|
|
- for (let i = 0; i < this.taskList.length; i++) {
|
|
|
- if (this.taskList[i].type === workData.type && this.taskList[i].data.id === workData.data.id) {
|
|
|
- this.postMessage('已有相同的任务,请勿重复添加')
|
|
|
- return
|
|
|
- }
|
|
|
- }
|
|
|
- this.taskList.push({
|
|
|
- ...workData,
|
|
|
- states: 'wait'
|
|
|
- })
|
|
|
- console.log(workData)
|
|
|
- this.postMessage(workData.data.title + '加入队列')
|
|
|
- },
|
|
|
- postMessage(message) {
|
|
|
- if (this.timer) { clearTimeout(this.timer) }
|
|
|
- this.message = message
|
|
|
- this.showMessage = true
|
|
|
- this.timer = setTimeout(() => {
|
|
|
- this.showMessage = false
|
|
|
- }, 2000)
|
|
|
- },
|
|
|
- useTool(toolName, data) {
|
|
|
- this.tool_name = toolName
|
|
|
- this.aiState = '1'
|
|
|
- this.toolList.forEach((item) => {
|
|
|
- if (toolName === item.name) {
|
|
|
- item.tool(data)
|
|
|
- .then(res => {
|
|
|
- this.sentAi(res.data)
|
|
|
- console.log(res)
|
|
|
- }).catch(err => {
|
|
|
- console.log(err)
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
- this.componentList.forEach((item) => {
|
|
|
- if (toolName === item) {
|
|
|
- this.tool_name = item
|
|
|
- this.tool_data = data
|
|
|
- }
|
|
|
- })
|
|
|
- },
|
|
|
- gotoUrl(pageName) {
|
|
|
- this.$router.push({
|
|
|
- name: pageName
|
|
|
- })
|
|
|
- },
|
|
|
- resetContext() {
|
|
|
- this.aiContext = this.aiContext.slice(0, 0)
|
|
|
- this.aiState = '1'
|
|
|
- this.tool_name = ''
|
|
|
- this.tool_input = ''
|
|
|
- this.tool_data = {}
|
|
|
- this.inProcess = false
|
|
|
- this.target.innerText = ''
|
|
|
- this.text = ''
|
|
|
- },
|
|
|
- getConfig() {
|
|
|
- this.target = document.getElementById('ai-input')
|
|
|
- const config = JSON.parse(localStorage.getItem('aiConfig')) || {}
|
|
|
- this.aiConfig = config.setting
|
|
|
- this.AiKey = config.active ? config[config.active].apiKey : ''
|
|
|
- this.AiModel = config.active ? config[config.active].model : ''
|
|
|
- if (!config.active) {
|
|
|
- this.guide()
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-})
|
|
|
-</script>
|
|
|
-<template>
|
|
|
- <div :class="['ai-ball',showText?'':'hide',showMessage?'':'message-hide',workLength?'working':'',inProcess?'process':'']">
|
|
|
- <div class="ai-inner" @click="showText=!showText">
|
|
|
- <div class="eye" />
|
|
|
- <div class="eye" />
|
|
|
- </div>
|
|
|
- <div :class="['page-menu',showMenu?'show':'']">
|
|
|
- <div class="page-list">
|
|
|
- <div v-for="page in pageList" class="list-item" @click="gotoUrl(page.name);showMenu=false">{{ page.title }}</div>
|
|
|
- </div>
|
|
|
- <div /><div />
|
|
|
- <div class="button" @click="showMenu=!showMenu">
|
|
|
- <div class="line" />
|
|
|
- <div class="line" />
|
|
|
- <div class="line" />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="text-box">
|
|
|
- <div id="ai-input" class="input" :contenteditable="!inProcess" @input="textInput" @keydown.ctrl.enter="sent" />
|
|
|
- <div :class="['send',inProcess?aiState==='2'?'el-icon-check':'el-icon-loading':'el-icon-top']" @click="sent" />
|
|
|
- <div v-for="comp in componentList" :class="['tool',tool_name===comp?'':'hide']">
|
|
|
- <component
|
|
|
- :is="comp"
|
|
|
- :tool_name.sync="tool_name"
|
|
|
- :tool_input.sync="tool_input"
|
|
|
- :tool_data.sync="tool_data"
|
|
|
- @runWork="runWork"
|
|
|
- @sentAi="sentAi"
|
|
|
- />
|
|
|
- </div>
|
|
|
- <div class="state">
|
|
|
- <span v-if="aiState === '0'">AI助理思考中...</span>
|
|
|
- <span v-if="aiState === '1'">工具调用中...</span>
|
|
|
- <span v-if="aiState === '2'">对话已结束,请重新开始</span>
|
|
|
- <span v-if="inProcess&&aiState==='1'" style="margin-left: auto;cursor: pointer" @click="sentAi({text:'用户反馈先前的操作或者数据存在错误,请询问是何处错误'},'model')">有误</span>
|
|
|
- <span v-if="inProcess&&aiState==='1'" style="cursor: pointer" @click="sentAi({text:'用户反馈需要结束对话,若有未完成的工作确认是否放弃工作,然后结束对话'},'model')">结束</span>
|
|
|
- </div>
|
|
|
- <div v-show="!inProcess && text===''" class="about-text">
|
|
|
- <span>{{ aboutText }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="message">
|
|
|
- <div class="inner">{{ message }}</div>
|
|
|
- </div>
|
|
|
- <div class="counter">
|
|
|
- {{ workLength }}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<style scoped lang="scss">
|
|
|
- .ai-ball{
|
|
|
- filter: drop-shadow( 0 0 8px #4F46E522);
|
|
|
- width: 70px;
|
|
|
- height: 70px;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- border-radius: 50%;
|
|
|
- background: #DBEAFE;
|
|
|
- position: fixed;
|
|
|
- z-index: 100;
|
|
|
- right: 40px;
|
|
|
- bottom: 120px;
|
|
|
- .page-menu{
|
|
|
- transition-duration: 300ms;
|
|
|
- background: #DBEAFE;
|
|
|
- width: 44px;
|
|
|
- border-radius: 22px;
|
|
|
- height: 44px;
|
|
|
- position: absolute;
|
|
|
- bottom: 60px;
|
|
|
- right: 8px;
|
|
|
- pointer-events: none;
|
|
|
- opacity: 0;
|
|
|
- display: grid;
|
|
|
- grid-template-rows: 0 44px;
|
|
|
- grid-template-columns: 0 44px;
|
|
|
- .page-list{
|
|
|
- padding: 0;
|
|
|
- overflow: hidden;
|
|
|
- box-sizing: border-box;
|
|
|
- .list-item{
|
|
|
- box-sizing: border-box;
|
|
|
- border-radius: 36px;
|
|
|
- font-size: 16px;
|
|
|
- padding: 6px 16px;
|
|
|
- width: 100%;
|
|
|
- cursor: pointer;
|
|
|
- transition-duration: 300ms;
|
|
|
- color: #2b2677;
|
|
|
- &:hover{
|
|
|
- background: #b4cbec;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- .button{
|
|
|
- border-radius: 50%;
|
|
|
- cursor: pointer;
|
|
|
- padding: 12px;
|
|
|
- width: 44px;
|
|
|
- height: 44px;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- grid-gap: 4px;
|
|
|
- align-items: flex-end;
|
|
|
- justify-content: center;
|
|
|
- transition-duration: 300ms;
|
|
|
- &:hover{
|
|
|
- background: #b4cbec;
|
|
|
- }
|
|
|
- .line{
|
|
|
- transition-duration: 300ms;
|
|
|
- transform-origin: 100% 50%;
|
|
|
- width: 20px;
|
|
|
- height: 2px;
|
|
|
- background: #4F46E5;
|
|
|
- }
|
|
|
- }
|
|
|
- &.show{
|
|
|
- width: 244px;
|
|
|
- height: 344px;
|
|
|
- grid-template-rows: 300px 44px;
|
|
|
- grid-template-columns: 200px 44px;
|
|
|
- .page-list{
|
|
|
- padding: 16px;
|
|
|
- }
|
|
|
- .button{
|
|
|
- grid-gap: 0;
|
|
|
- .line{
|
|
|
- &:first-child{
|
|
|
- width: 14px;
|
|
|
- transform: translateY(2px) translateX(2px) rotate(40deg);
|
|
|
- }
|
|
|
- &:last-child{
|
|
|
- width: 14px;
|
|
|
- transform: translateY(-2px) translateX(2px) rotate(-40deg);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- .counter{
|
|
|
- pointer-events: none;
|
|
|
- position: absolute;
|
|
|
- padding: 0 7px;
|
|
|
- font-size: 16px;
|
|
|
- height: 24px;
|
|
|
- line-height: 24px;
|
|
|
- text-align: center;
|
|
|
- color: white;
|
|
|
- border-radius: 12px;
|
|
|
- right: -12px;
|
|
|
- bottom: -2px;
|
|
|
- transition-duration: 300ms;
|
|
|
- background: #4F46E5;
|
|
|
- scale: 0;
|
|
|
- }
|
|
|
- .message{
|
|
|
- pointer-events: none;
|
|
|
- width: 1000px;
|
|
|
- height: 36px;
|
|
|
- position: absolute;
|
|
|
- bottom: -42px;
|
|
|
- right: 60px;
|
|
|
- transition-duration: 300ms;
|
|
|
- .inner{
|
|
|
- float: right;
|
|
|
- font-size: 16px;
|
|
|
- border-radius: 16px;
|
|
|
- text-align: end;
|
|
|
- line-height: 36px;
|
|
|
- padding: 0 20px;
|
|
|
- color: #2b2677;
|
|
|
- background: #DBEAFE;
|
|
|
- width: fit-content;
|
|
|
- height: 100%;
|
|
|
- overflow: hidden;
|
|
|
- }
|
|
|
- }
|
|
|
- .text-box{
|
|
|
- padding: 8px;
|
|
|
- position: absolute;
|
|
|
- border-radius: 26px;
|
|
|
- width: 500px;
|
|
|
- min-height: 50px;
|
|
|
- background: #DBEAFE;
|
|
|
- right: 86px;
|
|
|
- bottom: 8px;
|
|
|
- transition-duration: 300ms;
|
|
|
- display: flex;
|
|
|
- flex-wrap: wrap;
|
|
|
- align-items: flex-end;
|
|
|
- grid-gap: 0 10px;
|
|
|
- .about-text{
|
|
|
- pointer-events: none;
|
|
|
- overflow: hidden;
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- position: absolute;
|
|
|
- display: flex;
|
|
|
- padding: 0 24px;
|
|
|
- font-size: 16px;
|
|
|
- color: gray;
|
|
|
- align-items: center;
|
|
|
- left: 0;
|
|
|
- top: 0;
|
|
|
- }
|
|
|
- .tool{
|
|
|
- width: 100%;
|
|
|
- display: grid;
|
|
|
- transition-duration: 300ms;
|
|
|
- grid-template-rows: 1fr;
|
|
|
- overflow: hidden;
|
|
|
- padding: 10px 10px;
|
|
|
- &.hide{
|
|
|
- padding: 0 10px;
|
|
|
- grid-template-rows: 0fr;
|
|
|
- }
|
|
|
- }
|
|
|
- .state{
|
|
|
- display: flex;
|
|
|
- grid-gap: 6px;
|
|
|
- transition-duration: 300ms;
|
|
|
- overflow: hidden;
|
|
|
- height: 0;
|
|
|
- font-size: 13px;
|
|
|
- margin: 0 12px;
|
|
|
- width: 100%;
|
|
|
- color: #2b2677
|
|
|
- }
|
|
|
- .send{
|
|
|
- cursor: pointer;
|
|
|
- background: #4F46E5;
|
|
|
- width: 36px;
|
|
|
- height: 36px;
|
|
|
- font-size: 24px;
|
|
|
- color: white;
|
|
|
- line-height: 36px;
|
|
|
- text-align: center;
|
|
|
- transition-duration: 300ms;
|
|
|
- border-radius: 50%;
|
|
|
- &:hover{
|
|
|
- scale: 1.08;
|
|
|
- }
|
|
|
- }
|
|
|
- .input{
|
|
|
- color: #2b2677;
|
|
|
- margin: 6px 12px;
|
|
|
- flex: 1;
|
|
|
- height: fit-content;
|
|
|
- }
|
|
|
- }
|
|
|
- .ai-inner{
|
|
|
- cursor: pointer;
|
|
|
- width: 44px;
|
|
|
- height: 32px;
|
|
|
- transition-duration: 300ms;
|
|
|
- border-radius: 12px;
|
|
|
- border-bottom-right-radius: 2px;
|
|
|
- background: #4F46E5;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-evenly;
|
|
|
- @keyframes eyes {
|
|
|
- 0%{
|
|
|
- height: 12px;
|
|
|
- }
|
|
|
- 4%{
|
|
|
- height: 0;
|
|
|
- }
|
|
|
- 8%{
|
|
|
- height: 12px;
|
|
|
- }
|
|
|
- 12%{
|
|
|
- height: 12px;
|
|
|
- }
|
|
|
- 16%{
|
|
|
- height: 0;
|
|
|
- }
|
|
|
- 20%{
|
|
|
- height: 12px;
|
|
|
- }
|
|
|
- }
|
|
|
- &:hover{
|
|
|
- transform: rotate(6deg) scale(1.04);
|
|
|
- }
|
|
|
- }
|
|
|
- .eye{
|
|
|
- position: relative;
|
|
|
- height: 12px;
|
|
|
- width: 6px;
|
|
|
- background: white;
|
|
|
- border-radius: 2px;
|
|
|
- animation: 5s eyes infinite;
|
|
|
- transition-duration: 300ms;
|
|
|
- transform: translateX(-60%);
|
|
|
- }
|
|
|
- &.hide{
|
|
|
- .page-menu{
|
|
|
- bottom: 80px;
|
|
|
- right: 4px;
|
|
|
- opacity: 1;
|
|
|
- pointer-events: auto;
|
|
|
- }
|
|
|
- .message{
|
|
|
- bottom: 12px;
|
|
|
- right: 86px;
|
|
|
- }
|
|
|
- .text-box{
|
|
|
- right: 70px;
|
|
|
- opacity: 0;
|
|
|
- pointer-events: none;
|
|
|
- }
|
|
|
- .ai-inner{
|
|
|
- .eye{
|
|
|
- transform: translateX(0);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- &.message-hide{
|
|
|
- .message{
|
|
|
- opacity: 0;
|
|
|
- }
|
|
|
- }
|
|
|
- &.working{
|
|
|
- .counter{
|
|
|
- scale: 1;
|
|
|
- animation: working 1s infinite;
|
|
|
- }
|
|
|
- @keyframes working {
|
|
|
- from{
|
|
|
- box-shadow: 0 0 0 0 #4F46E5ff;
|
|
|
- }
|
|
|
- to{
|
|
|
- box-shadow: 0 0 0 6px #4F46E500;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- &.process{
|
|
|
- .text-box{
|
|
|
- .input{
|
|
|
- max-height: 600px;
|
|
|
- overflow: hidden;
|
|
|
- overflow-y: scroll;
|
|
|
- }
|
|
|
- .tool{
|
|
|
- .init-select{
|
|
|
- overflow-y: scroll;
|
|
|
- }
|
|
|
- }
|
|
|
- .send{
|
|
|
- background: #DBEAFE;
|
|
|
- color: #4F46E5;
|
|
|
- &:hover{
|
|
|
- scale: 1;
|
|
|
- }
|
|
|
- }
|
|
|
- .state{
|
|
|
- height: 16px;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-</style>
|