|
@@ -1,7 +1,8 @@
|
|
|
<script setup>
|
|
|
import { ref, unref, onMounted, onUnmounted, computed, watch } from 'vue';
|
|
|
-import { useMessage, NInput, NSwitch, NPopover, NScrollbar } from 'naive-ui';
|
|
|
-import dayjs from 'dayjs';
|
|
|
+import { useMessage, NInput, NSwitch, NPopover, NScrollbar, NUpload, NTooltip, NProgress } from 'naive-ui';
|
|
|
+import { useUserStore } from '@/stores/modules/userStore';
|
|
|
+import { baseURL } from '@/utils/request';
|
|
|
import { getFormatYesterDay } from '@/utils/format';
|
|
|
import { helperApi } from '@/api/helper';
|
|
|
import SvgIcon from '@/components/SvgIcon';
|
|
@@ -19,6 +20,8 @@ const emit = defineEmits(['onClick', 'onEnter']);
|
|
|
|
|
|
const MAX_NUM = 5;
|
|
|
|
|
|
+const useStore = useUserStore();
|
|
|
+
|
|
|
const modelLoading = defineModel('loading');
|
|
|
const switchStatus = defineModel('switch');
|
|
|
|
|
@@ -37,7 +40,11 @@ const popoverTriggerRef = ref(null);
|
|
|
const popoverInnerRef = ref(null);
|
|
|
const scrollRef = ref(null);
|
|
|
|
|
|
+const uploadFileList = ref([]);
|
|
|
+const uploadLoading = ref(false);
|
|
|
+
|
|
|
const agentOptions = computed(() => helperList.value.filter(({ tools }) => tools));
|
|
|
+const lastFileListIndex = computed(() => uploadFileList.value.length == 0 ? 0 : uploadFileList.value.length - 1);
|
|
|
|
|
|
const focusInput = _ => isFocusState.value = true;
|
|
|
|
|
@@ -80,9 +87,14 @@ const commonEmitEvent = (eventName) => {
|
|
|
return message.warning('当前对话进行中');
|
|
|
}
|
|
|
|
|
|
- emit(eventName, { question: val, selectedOption: selectedOption.value || {} });
|
|
|
+ if ( uploadLoading.value ) {
|
|
|
+ return message.warning('文件上传中,请稍后');
|
|
|
+ }
|
|
|
+
|
|
|
+ emit(eventName, { question: val, selectedOption: selectedOption.value || {}, uploadFileList: uploadFileList.value });
|
|
|
|
|
|
inpVal.value = '';
|
|
|
+ uploadFileList.value = [];
|
|
|
}
|
|
|
|
|
|
// 回车事件
|
|
@@ -160,6 +172,17 @@ const handleKeyDown = (event) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+const formatFileItem = (file) => {
|
|
|
+ const { name } = file;
|
|
|
+ return {
|
|
|
+ name: name.substring(0, name.lastIndexOf('.')),
|
|
|
+ url: "",
|
|
|
+ size: (file.file.size / 1024).toFixed(2) + "KB",
|
|
|
+ suffix: name.substring( name.lastIndexOf('.') + 1 ).toUpperCase(),
|
|
|
+ originSuffix:name.substring( name.lastIndexOf('.') )
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// 处理点击空白处关闭
|
|
|
const closePopoverOutside =(event) => {
|
|
|
if (!isOpen.value) return;
|
|
@@ -176,11 +199,88 @@ const selectOption = (index) => {
|
|
|
inpVal.value = selectedOption.value.content;
|
|
|
}
|
|
|
|
|
|
+const beforeUpload = ({ file }) => {
|
|
|
+ if (( file.file?.size / ( 1024 * 2 ) ) > 5) {
|
|
|
+ message.warning("只能上传.doc和.txt格式的文件, 请重新上传");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ const index = unref(lastFileListIndex);
|
|
|
+
|
|
|
+ uploadFileList.value[index] = {
|
|
|
+ ...formatFileItem(file),
|
|
|
+ percentage: 0
|
|
|
+ }
|
|
|
+
|
|
|
+ uploadLoading.value = true
|
|
|
+
|
|
|
+ console.log( "开始上传", uploadFileList.value );
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+// 上传文件
|
|
|
+const handleUploadChange = ({ file }) => {
|
|
|
+
|
|
|
+ if (file.status === 'uploading') {
|
|
|
+ uploadFileList.value[lastFileListIndex.value].percentage = file.percentage;
|
|
|
+ console.log( "上传中", uploadFileList.value );
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// 文件上传完成
|
|
|
+const handleFinish = ({ file, event }) => {
|
|
|
+
|
|
|
+ uploadLoading.value = false
|
|
|
+
|
|
|
+ try {
|
|
|
+
|
|
|
+ const res = JSON.parse( (event?.target).response );
|
|
|
+
|
|
|
+ if ( res.code == 200 ) {
|
|
|
+
|
|
|
+ uploadFileList.value[lastFileListIndex.value] = {
|
|
|
+ ...uploadFileList.value[lastFileListIndex.value],
|
|
|
+ url: res.data,
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log( "上传完成", uploadFileList.value );
|
|
|
+
|
|
|
+ // uploadFileList.value.push({
|
|
|
+ // name: name.substring(0, name.lastIndexOf('.')),
|
|
|
+ // url: res.data,
|
|
|
+ // size: (file.file.size / 1024).toFixed(2) + "KB",
|
|
|
+ // suffix: name.substring( name.lastIndexOf('.') + 1 ).toUpperCase()
|
|
|
+ // })
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.log("上传完成, 但是存在错误", error);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// 上传失败
|
|
|
+const handleUploadError = (error) => {
|
|
|
+ uploadLoading.value = false;
|
|
|
+}
|
|
|
+
|
|
|
+// 删除文件
|
|
|
+const onRemoveFile = (i) => {
|
|
|
+ uploadFileList.value.splice(i, 1);
|
|
|
+}
|
|
|
+
|
|
|
onMounted(async () => {
|
|
|
+ const url = import.meta.env.VITE_BASE_URL;
|
|
|
+const prefix = import.meta.env.VITE_BASE_PREFIX;
|
|
|
+const baseURL = url + prefix;
|
|
|
+
|
|
|
+console.log( "baseURL", baseURL );
|
|
|
const { data } = await helperApi.getHelperList();
|
|
|
|
|
|
const result = getFormatYesterDay(data)
|
|
|
- console.log("result", result);
|
|
|
+
|
|
|
helperList.value = result;
|
|
|
document.addEventListener('keydown', handleKeyDown);
|
|
|
document.addEventListener('click', closePopoverOutside);
|
|
@@ -223,8 +323,54 @@ defineExpose({
|
|
|
<SvgIcon name="chat-icon-close-btn"></SvgIcon>
|
|
|
</li>
|
|
|
</ul>
|
|
|
+
|
|
|
+ <ul class="file-list-wrapper" v-show="uploadFileList.length">
|
|
|
+ <li class="file-item space-x-[14px]" v-for="(item, index) in uploadFileList" :key="index">
|
|
|
+ <div class="file-icon"></div>
|
|
|
+ <div class="file-info">
|
|
|
+ <p class="title">{{ item.name }}</p>
|
|
|
+ <p class="info space-x-[8px]">
|
|
|
+ <span class="suffix">{{ item.suffix }}</span>
|
|
|
+ <span class="size">{{ item.size }}</span>
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ <span class="close" @click="onRemoveFile(i)" v-show="item.percentage == 100">x</span>
|
|
|
+ <div class="file-progress" v-if="item.percentage != 100">
|
|
|
+ <NProgress
|
|
|
+ type="line"
|
|
|
+ color="#3153f5"
|
|
|
+ :percentage="item.percentage"
|
|
|
+ :show-indicator="false"
|
|
|
+ :height="3"
|
|
|
+ ></NProgress>
|
|
|
+ </div>
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+
|
|
|
<div class="chat-inp-inner">
|
|
|
<div class="inp-wrapper flex-1" @click="handleInpFocus">
|
|
|
+ <div class="upload-inner">
|
|
|
+ <NUpload
|
|
|
+ accept=".doc,.txt"
|
|
|
+ :disabled="uploadLoading"
|
|
|
+ :show-file-list="false"
|
|
|
+ :action="baseURL + '/qiniuyun/upLoadImage'"
|
|
|
+ :headers="{
|
|
|
+ 'Authorization': 'Bearer' + useStore.token,
|
|
|
+ }"
|
|
|
+ @on-error="handleUploadError"
|
|
|
+ @change="handleUploadChange"
|
|
|
+ @finish="handleFinish"
|
|
|
+ @before-upload="beforeUpload"
|
|
|
+ >
|
|
|
+ <NTooltip trigger="hover">
|
|
|
+ <template #trigger>
|
|
|
+ <div class="upload-file-button"></div>
|
|
|
+ </template>
|
|
|
+ <span class="text-[12px]">支持上传文件(每次一个, 大小5MB以内)接受.doc、.txt等格式的文件</span>
|
|
|
+ </NTooltip>
|
|
|
+ </NUpload>
|
|
|
+ </div>
|
|
|
<NInput
|
|
|
class="flex-1"
|
|
|
ref="inpRef"
|
|
@@ -312,7 +458,25 @@ defineExpose({
|
|
|
background: #fff;
|
|
|
|
|
|
.inp-wrapper {
|
|
|
- padding: 17px 0px 17px 34px;
|
|
|
+ @include flex(x, start, center);
|
|
|
+ padding: 17px 0px 17px 17px;
|
|
|
+
|
|
|
+ .upload-inner {
|
|
|
+ width: 30px;
|
|
|
+ height: 30px;
|
|
|
+ padding-top: 2px;
|
|
|
+
|
|
|
+ .upload-file-button {
|
|
|
+ width: 30px;
|
|
|
+ height: 30px;
|
|
|
+ background: url("@/assets/svgs/chat/icon-file-default.svg") center center no-repeat;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: url("@/assets/svgs/chat/icon-file-active.svg") center center no-repeat;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.submit-btn {
|
|
@@ -328,6 +492,77 @@ defineExpose({
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ .file-list-wrapper {
|
|
|
+ padding: 10px;
|
|
|
+ padding-bottom: 0px;
|
|
|
+ background: #fff;
|
|
|
+
|
|
|
+ .file-item {
|
|
|
+ position: relative;
|
|
|
+ @include flex(x, center, start);
|
|
|
+ width: 30%;
|
|
|
+ // height: 52px;
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 4px;
|
|
|
+ background: #f5f5f5;
|
|
|
+
|
|
|
+ .file-icon {
|
|
|
+ flex-shrink: 0;
|
|
|
+ width: 36px;
|
|
|
+ height: 36px;
|
|
|
+ background: url("@/assets/svgs/chat/icon-file.svg") center center no-repeat;
|
|
|
+ }
|
|
|
+
|
|
|
+ .file-info {
|
|
|
+ flex: 1;
|
|
|
+ @include flex(y, start, start);
|
|
|
+ font-size: 12px;
|
|
|
+ .title {
|
|
|
+ width: 170px;
|
|
|
+ color: #1a2029;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ overflow: hidden;
|
|
|
+ word-break: break-all;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+ .info {
|
|
|
+ flex: 1;
|
|
|
+ @include flex(x, center, between);
|
|
|
+ color: #838a95;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .file-progress {
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ bottom: 0;
|
|
|
+ width: 100%;
|
|
|
+ margin: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .close {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ right: 0;
|
|
|
+ width: 16px;
|
|
|
+ height: 16px;
|
|
|
+ border: 1px solid #fff;
|
|
|
+ border-radius: 100%;
|
|
|
+ transform: translate(40%, -40%);
|
|
|
+ background: #c8c8c8;
|
|
|
+ font-size: 10px;
|
|
|
+ text-align: center;
|
|
|
+ line-height: 12px;
|
|
|
+ cursor: pointer;
|
|
|
+ color: #fff;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: #b7b7b7;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.popover-inner {
|