Phoenix 7 months ago
parent b7f46d1c22
commit bf5f9f7752

@ -2,8 +2,6 @@
VITE_GLOB_API_URL=/api
VITE_APP_API_BASE_URL=http://114.218.158.24:9020
https://erpapi.fontree.cn#正式
http://114.218.158.24:9020#测试
# Whether long replies are supported, which may result in higher API fees
VITE_GLOB_OPEN_LONG_REPLY=true

@ -0,0 +1,9 @@
# Glob API URL
VITE_GLOB_API_URL=/api
VITE_APP_API_BASE_URL=https://erpapi.fontree.cn
# Whether long replies are supported, which may result in higher API fees
VITE_GLOB_OPEN_LONG_REPLY=true
# When you want to use PWA
VITE_GLOB_APP_PWA=false

2885
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -12,9 +12,9 @@
],
"scripts": {
"dev": "vite",
"build": "run-p type-check build-only",
"build": "run-p build-only",
"preview": "vite preview",
"build-only": "vite build",
"build-only": "vite build --mode prod",
"type-check": "vue-tsc --noEmit",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
@ -65,9 +65,12 @@
"less": "^4.1.3",
"lint-staged": "^13.1.2",
"markdown-it-link-attributes": "^4.0.1",
"node-sass": "^9.0.0",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.21",
"rimraf": "^4.3.0",
"sass": "^1.70.0",
"sass-loader": "^14.1.0",
"tailwindcss": "^3.2.7",
"typescript": "~4.9.5",
"unplugin-auto-import": "^0.17.5",

@ -8,13 +8,13 @@ import zhCN from 'ant-design-vue/es/locale/zh_CN';
const { theme } = useTheme()
const { language } = useLanguage()
import {Local} from './utils/storage/storage.js'
const props = window.$wujie?.props;
if (props){
/* const props = window.$wujie?.props;
/!* if (props){
Local.set('token',props.token)
Local.set('mode',props.mode)
Local.set('userInfo',props.userInfo)
Local.set('isGPT4',props.isGPT4)
}
} *!/ */
const themeOverrides = {
common: {
primaryColorHover:'#764CF6',

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 12 12"><g fill="none"><path d="M5 4a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H5zm6 2A5 5 0 1 1 1 6a5 5 0 0 1 10 0zm-1 0a4 4 0 1 0-8 0a4 4 0 0 0 8 0z" fill="currentColor"></path></g></svg>

After

Width:  |  Height:  |  Size: 303 B

@ -1,8 +1,8 @@
import {createSession, getSessionList, postRequest, sessionDetailReq} from "@/api/api";
import {useScroll} from "@/views/chat/hooks/useScroll";
import dayjs from "dayjs";
import { ref } from 'vue';
import { defineStore } from 'pinia';
export const sessionDetailForSetup = defineStore('session', () => {
const sessionDetail = ref([]);
const currentListUuid=ref('')
@ -10,6 +10,7 @@ const currentListUuid=ref('')
const dataList = ref([]);
const isStop = ref(false);
const isGPT4 = ref(false);
const loading=ref(false)
const getDataList = async () => {
const data = {
page: 1,
@ -63,5 +64,5 @@ const currentListUuid=ref('')
getDataList()
}
}
return { sessionDetail,currentListUuid ,gptMode,getDataList,dataList,getSessionDetail,createSessionStore,deleteSession,isStop,isGPT4};
return { sessionDetail,currentListUuid ,gptMode,getDataList,dataList,getSessionDetail,createSessionStore,deleteSession,isStop,isGPT4,loading};
});

@ -69,6 +69,7 @@ function addCopyEvents() {
const copyBtn = textRef.value.querySelectorAll('.code-block-header__copy')
copyBtn.forEach((btn) => {
btn.addEventListener('click', () => {
console.log(btn.parentElement?.nextElementSibling?.textContent,'btn.parentElement?.nextElementSibling?.textContent')
const code = btn.parentElement?.nextElementSibling?.textContent
if (code) {
copyToClip(code).then(() => {

@ -12,10 +12,15 @@ interface ScrollReturn {
export function useScroll(): ScrollReturn {
const scrollRef = ref<ScrollElement>(null)
const scrollToBottom = async () => {
const scrollToBottom = async (value:ScrollBehavior) => {
await nextTick()
if (scrollRef.value)
scrollRef.value.scrollTop = scrollRef.value.scrollHeight
if (scrollRef.value){
scrollRef.value.scrollTo({
top: scrollRef.value.scrollHeight, // 滚动到底部
behavior: value// 滚动行为平滑过渡
});
}
/* scrollRef.value.scrollTop = scrollRef.value.scrollHeight */
}
const scrollToTop = async () => {

@ -20,7 +20,7 @@ import {t} from '@/locales'
import {UploadOutlined} from '@ant-design/icons-vue';
import {storeToRefs} from 'pinia'
import {sessionDetailForSetup} from '@/store'
import StopSvg from '@/assets/RecordStop12Regular.svg'
const sessionDetailData = sessionDetailForSetup()
let controller = new AbortController()
const {
@ -28,7 +28,8 @@ const {
currentListUuid,
gptMode,
isStop,
isGPT4
isGPT4,
loading
} = storeToRefs(sessionDetailData)
const dialog = useDialog()
@ -51,7 +52,6 @@ const {
toggleUsingContext
} = useUsingContext()
const prompt = ref('')
const loading = ref(false)
const inputRef = ref(null)
// PromptStore
@ -68,19 +68,32 @@ dataSources.value.forEach((item, index) => {
})
function handleSubmit() {
if (loading.value){
handleStop()
}else {
dataSources.value.push({
dateTime: dayjs().format('YYYY/MM/DD HH:mm:ss'),
text: prompt.value,
text: prompt.value?.trim(),
inversion: true,
error: false,
fileList: fileList.value.map(x => x.url),
loading: false
})
sendDataStream()
scrollToBottom('smooth')
}
const API_URL = `${import.meta.env.VITE_APP_API_BASE_URL}/chat/completion`;
}
//
/* const isTopBottom=()=>{
if ( scrollRef.value.scrollTop ===0){
}
} */
const API_URL = `${import.meta.env.VITE_APP_API_BASE_URL}/chat/completion`;
const createParams = () => {
const messages = dataSources.value.map((x) => {
return {
@ -160,7 +173,6 @@ const sendDataStream = async () => {
});
visible.value = false
try {
loading.value = true
const response = await fetch(API_URL, {
method: "POST",
timeout: 10000,
@ -271,7 +283,7 @@ function handleEnter(event) {
}
}
function handleStop(item) {
function handleStop() {
if (loading.value) {
loading.value = false
isStop.value = true
@ -312,7 +324,7 @@ const placeholder = computed(() => {
})
const buttonDisabled = computed(() => {
return loading.value || !prompt.value || prompt.value.trim() === ''
return prompt.value.trim() === '' &&!loading.value
})
const footerClass = computed(() => {
@ -322,14 +334,35 @@ const footerClass = computed(() => {
}
return classes
})
const isShowBottom=ref(false)
onMounted(() => {
scrollToBottom()
scrollRef.value.addEventListener('scroll', function() {
if (scrollRef.value.scrollTop + scrollRef.value.clientHeight +100>= scrollRef.value.scrollHeight) {
isShowBottom.value=false
}else {
isShowBottom.value=true
}
});
if (inputRef.value && !isMobile.value) {
inputRef.value?.focus()
}
})
const currentColor = ref('#ff0000'); //
const colors = ['#ff0000', '#ffffff']; //
let intervalId = null;
//
const changeColor = () => {
currentColor.value = currentColor.value === colors[0] ? colors[1] : colors[0];
};
watch(loading,()=>{
if (loading.value){
intervalId = setInterval(changeColor, 1000);
}else {
clearInterval(intervalId);
}
})
const fileList = ref([]);
onUnmounted(() => {
if (loading.value) {
@ -345,7 +378,6 @@ function getBase64(file) {
reader.onerror = error => reject(error);
});
}
const previewVisible = ref(false);
const previewImage = ref('');
const previewTitle = ref('');
@ -394,7 +426,10 @@ const upItemImage1 = async (file) => {
sendDataStream()
}
}
watch(dataSources,()=>{
loading.value=false
scrollToBottom('auto')
})
const customRequest = async (file) => {
const res = await uploadImg({
file: file.file,
@ -417,9 +452,10 @@ const customRequest = async (file) => {
@handle-clear="handleClear"
/>
<main class="flex-1 overflow-hidden">
<div class="shortcut-arrow">
<transition name="fade">
<div class="shortcut-arrow" v-if="isShowBottom">
<div class="top">
<n-button @click="scrollToBottom" type="primary" dashed circle>
<n-button @click="scrollToBottom('smooth')" type="primary" dashed circle>
<template #icon>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="48"
@ -433,7 +469,7 @@ const customRequest = async (file) => {
</n-button>
</div>
</div>
</transition>
<div id="scrollRef" ref="scrollRef" class="h-full overflow-hidden overflow-y-auto">
<div
@ -462,12 +498,12 @@ const customRequest = async (file) => {
@delete="handleDelete(index)"
/>
<div class="sticky bottom-0 left-0 flex justify-center">
<NButton v-if="loading" type="warning" @click="handleStop(item)">
<!-- <NButton v-if="loading" type="warning" @click="handleStop(item)">
<template #icon>
<SvgIcon icon="ri:stop-circle-line"/>
</template>
停止响应
</NButton>
</NButton>-->
</div>
</div>
</template>
@ -561,8 +597,13 @@ const customRequest = async (file) => {
</NAutoComplete>
<NButton color="#8a2be2" type="primary" :disabled="buttonDisabled" @click="handleSubmit">
<template #icon>
<span class="dark:text-black">
<span class="dark:text-black" v-if="!loading">
<SvgIcon icon="ri:send-plane-fill"/>
</span>
<span class="dark:text-black" v-if="loading">
<svg style="width:100%;height:100%;" xmlns="http://www.w3.org/2000/svg" :style="{color:currentColor}" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 12 12"><g fill="none"><path d="M5 4a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H5zm6 2A5 5 0 1 1 1 6a5 5 0 0 1 10 0zm-1 0a4 4 0 1 0-8 0a4 4 0 0 0 8 0z" fill="currentColor"></path></g></svg>
</span>
</template>
</NButton>
@ -572,13 +613,19 @@ const customRequest = async (file) => {
</div>
</template>
<style lang="scss" scoped>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.4s ease-in-out;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
.shortcut-arrow {
width: min-content;
height: min-content;
position: absolute;
z-index: 10;
left: 50%;
bottom: 90px;
bottom: 100px;
.top {
transform: rotate(270deg) translateX(-50%);
width: min-content;

@ -1,5 +1,6 @@
<script setup>
import {createSession} from "@/api/api";
import {Local} from "@/utils/storage/storage";
import {storeToRefs} from "pinia";
import {computed, ref, watch} from 'vue'
import {NButton, NLayoutSider, useDialog, NSelect} from 'naive-ui'
@ -10,8 +11,15 @@ import { useBasicLayout } from '@/hooks/useBasicLayout'
import {PromptStore, SvgIcon} from '@/components/common'
import {t} from '@/locales'
import {sessionDetailForSetup} from "@/store";
const sessionDetailData = sessionDetailForSetup()
const {sessionDetail, dataList,gptMode,currentListUuid,isGPT4 } = storeToRefs(sessionDetailData)
const {
sessionDetail,
dataList,
gptMode,
currentListUuid,
isGPT4
} = storeToRefs(sessionDetailData)
const appStore = useAppStore()
const chatStore = useChatStore()
@ -21,6 +29,7 @@ const { isMobile } = useBasicLayout()
const show = ref(false)
const collapsed = computed(() => appStore.siderCollapsed)
async function handleAdd() {
await sessionDetailForSetup().createSessionStore()
sessionDetail.value = []
@ -30,6 +39,7 @@ async function handleAdd() {
function handleUpdateCollapsed() {
appStore.setSiderCollapsed(!collapsed.value)
}
function handleClearAll() {
dialog.warning({
title: t('chat.deleteMessage'),
@ -38,8 +48,9 @@ function handleClearAll() {
negativeText: t('common.no'),
onPositiveClick: () => {
chatStore.clearHistory()
if (isMobile.value)
if (isMobile.value) {
appStore.setSiderCollapsed(true)
}
},
})
}
@ -62,14 +73,32 @@ const mobileSafeArea = computed(() => {
}
return {}
})
const options=ref([{ label: 'GPT-3.5', value: 'gpt-3.5-turbo' },
{ label: 'GPT-4.0', value: 'gpt-4-1106-preview' ,permission:'isGPT4'},
{ label: 'GPT-V', value: 'gpt-4-vision-preview',permission:'isGPT4' }])
setTimeout(()=>{
if (localStorage.getItem('isGPT4')!=='true'){
options.value=options.value.filter(item=>item.permission!=='isGPT4')
const options = () => {
return Local.get('ruleBtn').find(x => x === 'gpt-4-btn') ? [{
label: 'GPT-3.5',
value: 'gpt-3.5-turbo',
permission: 'gpt-3.5-btn'
},
{
label: 'GPT-4.0',
value: 'gpt-4-1106-preview',
permission: 'gpt-4-btn'
},
{
label: 'GPT-V',
value: 'gpt-4-vision-preview',
permission: 'gpt-4-btn'
}] : [{
label: 'GPT-4.0',
value: 'gpt-4-1106-preview',
permission: 'gpt-4-btn'
},
{
label: 'GPT-V',
value: 'gpt-4-vision-preview',
permission: 'gpt-4-btn'
}]
}
},1000)
watch(
isMobile,
(val) => {
@ -97,11 +126,14 @@ watch(
<div class="flex flex-col h-full" :style="mobileSafeArea">
<main class="flex flex-col flex-1 min-h-0">
<div class="p-4" style="display: flex">
<NButton color="#764CF6" style="flex:0.9;display: flex;justify-content: center;align-items: center;margin-right: auto" dashed block @click="handleAdd">
<NButton color="#764CF6"
style="flex:0.9;display: flex;justify-content: center;align-items: center;margin-right: auto" dashed
block @click="handleAdd"
>
新建会话
</NButton>
<div style="width: 110px">
<n-select v-model:value="gptMode" :options="options" />
<n-select v-model:value="gptMode" :options="options()"/>
</div>
</div>
<div class="flex-1 min-h-0 pb-4 overflow-hidden">

Loading…
Cancel
Save