Compare commits
4 Commits
63490484e9
...
markdown
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f90b8fd91 | ||
|
|
f175c3e5d6 | ||
|
|
89b19cf541 | ||
|
|
21ef1d51a6 |
@@ -35,37 +35,32 @@
|
|||||||
"@floating-ui/dom": "^1.7.2",
|
"@floating-ui/dom": "^1.7.2",
|
||||||
"@floating-ui/vue": "^1.1.7",
|
"@floating-ui/vue": "^1.1.7",
|
||||||
"@jsonlee_12138/enum": "^1.0.4",
|
"@jsonlee_12138/enum": "^1.0.4",
|
||||||
|
"@shikijs/transformers": "^3.7.0",
|
||||||
"@vueuse/core": "^13.5.0",
|
"@vueuse/core": "^13.5.0",
|
||||||
"@vueuse/integrations": "^13.5.0",
|
"@vueuse/integrations": "^13.5.0",
|
||||||
|
"chatarea": "^6.0.3",
|
||||||
"date-fns": "^2.30.0",
|
"date-fns": "^2.30.0",
|
||||||
|
"deepmerge": "^4.3.1",
|
||||||
|
"dompurify": "^3.2.6",
|
||||||
"driver.js": "^1.3.6",
|
"driver.js": "^1.3.6",
|
||||||
"echarts": "^6.0.0",
|
"echarts": "^6.0.0",
|
||||||
"element-plus": "^2.10.4",
|
"element-plus": "^2.10.4",
|
||||||
"fingerprintjs": "^0.5.3",
|
"fingerprintjs": "^0.5.3",
|
||||||
|
"github-markdown-css": "^5.8.1",
|
||||||
|
"highlight.js": "^11.11.1",
|
||||||
"hook-fetch": "^2.0.4-beta.1",
|
"hook-fetch": "^2.0.4-beta.1",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mammoth": "^1.11.0",
|
"mammoth": "^1.11.0",
|
||||||
|
"mermaid": "11.12.0",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pdfjs-dist": "^5.4.449",
|
"pdfjs-dist": "^5.4.449",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"pinia-plugin-persistedstate": "^4.4.1",
|
"pinia-plugin-persistedstate": "^4.4.1",
|
||||||
"qrcode": "^1.5.4",
|
|
||||||
"radash": "^12.1.1",
|
|
||||||
"reset-css": "^5.0.2",
|
|
||||||
"vue": "^3.5.17",
|
|
||||||
"vue-element-plus-x": "1.3.7",
|
|
||||||
"vue-router": "4",
|
|
||||||
"xlsx": "^0.18.5",
|
|
||||||
"@shikijs/transformers": "^3.7.0",
|
|
||||||
"chatarea": "^6.0.3",
|
|
||||||
"deepmerge": "^4.3.1",
|
|
||||||
"dompurify": "^3.2.6",
|
|
||||||
"github-markdown-css": "^5.8.1",
|
|
||||||
"highlight.js": "^11.11.1",
|
|
||||||
"lodash": "^4.17.21",
|
|
||||||
"mermaid": "11.12.0",
|
|
||||||
"prismjs": "^1.30.0",
|
"prismjs": "^1.30.0",
|
||||||
"property-information": "^7.1.0",
|
"property-information": "^7.1.0",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
|
"radash": "^12.1.1",
|
||||||
"rehype-katex": "^7.0.1",
|
"rehype-katex": "^7.0.1",
|
||||||
"rehype-raw": "^7.0.0",
|
"rehype-raw": "^7.0.0",
|
||||||
"rehype-sanitize": "^6.0.0",
|
"rehype-sanitize": "^6.0.0",
|
||||||
@@ -74,46 +69,21 @@
|
|||||||
"remark-math": "^6.0.0",
|
"remark-math": "^6.0.0",
|
||||||
"remark-parse": "^11.0.0",
|
"remark-parse": "^11.0.0",
|
||||||
"remark-rehype": "^11.1.2",
|
"remark-rehype": "^11.1.2",
|
||||||
|
"reset-css": "^5.0.2",
|
||||||
"shiki": "^3.7.0",
|
"shiki": "^3.7.0",
|
||||||
"ts-md5": "^2.0.1",
|
"ts-md5": "^2.0.1",
|
||||||
"unified": "^11.0.5",
|
"unified": "^11.0.5",
|
||||||
"unist-util-visit": "^5.0.0"
|
"unist-util-visit": "^5.0.0",
|
||||||
|
"vue": "^3.5.17",
|
||||||
|
"vue-element-plus-x": "1.3.7",
|
||||||
|
"vue-router": "4",
|
||||||
|
"xlsx": "^0.18.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^4.16.2",
|
"@antfu/eslint-config": "^4.16.2",
|
||||||
"@changesets/cli": "^2.29.5",
|
"@changesets/cli": "^2.29.5",
|
||||||
"@commitlint/config-conventional": "^19.8.1",
|
|
||||||
"@types/fingerprintjs__fingerprintjs": "^3.0.2",
|
|
||||||
"@vitejs/plugin-vue": "^6.0.0",
|
|
||||||
"@vue/tsconfig": "^0.7.0",
|
|
||||||
"commitlint": "^19.8.1",
|
|
||||||
"cz-git": "^1.12.0",
|
|
||||||
"eslint": "^9.31.0",
|
|
||||||
"husky": "^9.1.7",
|
|
||||||
"lint-staged": "^16.1.2",
|
|
||||||
"postcss": "8.4.31",
|
|
||||||
"postcss-html": "1.5.0",
|
|
||||||
"prettier": "^3.6.2",
|
|
||||||
"rollup-plugin-visualizer": "^6.0.3",
|
|
||||||
"sass-embedded": "^1.89.2",
|
|
||||||
"stylelint": "^16.21.1",
|
|
||||||
"stylelint-config-html": "^1.1.0",
|
|
||||||
"stylelint-config-recess-order": "^7.1.0",
|
|
||||||
"stylelint-config-recommended-scss": "^15.0.1",
|
|
||||||
"stylelint-config-recommended-vue": "^1.6.1",
|
|
||||||
"stylelint-config-standard": "^38.0.0",
|
|
||||||
"stylelint-config-standard-scss": "^15.0.1",
|
|
||||||
"typescript": "~5.8.3",
|
|
||||||
"typescript-api-pro": "^0.0.7",
|
|
||||||
"unocss": "66.3.3",
|
|
||||||
"unplugin-auto-import": "^19.3.0",
|
|
||||||
"unplugin-vue-components": "^28.8.0",
|
|
||||||
"vite": "^6.3.5",
|
|
||||||
"vite-plugin-compression": "^0.5.1",
|
|
||||||
"vite-plugin-env-typed": "^0.0.2",
|
|
||||||
"vite-plugin-svg-icons": "^2.0.1",
|
|
||||||
"vue-tsc": "^3.0.1",
|
|
||||||
"@chromatic-com/storybook": "^3.2.7",
|
"@chromatic-com/storybook": "^3.2.7",
|
||||||
|
"@commitlint/config-conventional": "^19.8.1",
|
||||||
"@jsonlee_12138/markdown-it-mermaid": "0.0.6",
|
"@jsonlee_12138/markdown-it-mermaid": "0.0.6",
|
||||||
"@storybook/addon-essentials": "^8.6.14",
|
"@storybook/addon-essentials": "^8.6.14",
|
||||||
"@storybook/addon-onboarding": "^8.6.14",
|
"@storybook/addon-onboarding": "^8.6.14",
|
||||||
@@ -127,22 +97,52 @@
|
|||||||
"@storybook/vue3": "^8.6.14",
|
"@storybook/vue3": "^8.6.14",
|
||||||
"@storybook/vue3-vite": "^8.6.14",
|
"@storybook/vue3-vite": "^8.6.14",
|
||||||
"@types/dom-speech-recognition": "^0.0.4",
|
"@types/dom-speech-recognition": "^0.0.4",
|
||||||
|
"@types/fingerprintjs__fingerprintjs": "^3.0.2",
|
||||||
"@types/fs-extra": "^11.0.4",
|
"@types/fs-extra": "^11.0.4",
|
||||||
"@types/markdown-it": "^14.1.2",
|
"@types/markdown-it": "^14.1.2",
|
||||||
"@types/prismjs": "^1.26.5",
|
"@types/prismjs": "^1.26.5",
|
||||||
|
"@vitejs/plugin-vue": "^6.0.0",
|
||||||
"@vitest/browser": "^3.2.4",
|
"@vitest/browser": "^3.2.4",
|
||||||
"@vitest/coverage-v8": "^3.2.4",
|
"@vitest/coverage-v8": "^3.2.4",
|
||||||
|
"@vue/tsconfig": "^0.7.0",
|
||||||
|
"commitlint": "^19.8.1",
|
||||||
|
"cz-git": "^1.12.0",
|
||||||
|
"eslint": "^9.31.0",
|
||||||
"esno": "^4.8.0",
|
"esno": "^4.8.0",
|
||||||
"fast-glob": "^3.3.3",
|
"fast-glob": "^3.3.3",
|
||||||
|
"husky": "^9.1.7",
|
||||||
|
"lint-staged": "^16.1.2",
|
||||||
"playwright": "^1.53.2",
|
"playwright": "^1.53.2",
|
||||||
|
"postcss": "8.4.31",
|
||||||
|
"postcss-html": "1.5.0",
|
||||||
|
"prettier": "^3.6.2",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
|
"rollup-plugin-visualizer": "^6.0.3",
|
||||||
"sass": "^1.89.2",
|
"sass": "^1.89.2",
|
||||||
|
"sass-embedded": "^1.89.2",
|
||||||
"storybook": "^8.6.14",
|
"storybook": "^8.6.14",
|
||||||
"storybook-dark-mode": "^4.0.2",
|
"storybook-dark-mode": "^4.0.2",
|
||||||
|
"stylelint": "^16.21.1",
|
||||||
|
"stylelint-config-html": "^1.1.0",
|
||||||
|
"stylelint-config-recess-order": "^7.1.0",
|
||||||
|
"stylelint-config-recommended-scss": "^15.0.1",
|
||||||
|
"stylelint-config-recommended-vue": "^1.6.1",
|
||||||
|
"stylelint-config-standard": "^38.0.0",
|
||||||
|
"stylelint-config-standard-scss": "^15.0.1",
|
||||||
"terser": "^5.43.1",
|
"terser": "^5.43.1",
|
||||||
|
"typescript": "~5.8.3",
|
||||||
|
"typescript-api-pro": "^0.0.7",
|
||||||
|
"unocss": "66.3.3",
|
||||||
|
"unplugin-auto-import": "^19.3.0",
|
||||||
|
"unplugin-vue-components": "^28.8.0",
|
||||||
|
"vite": "^6.3.5",
|
||||||
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-dts": "^4.5.4",
|
"vite-plugin-dts": "^4.5.4",
|
||||||
|
"vite-plugin-env-typed": "^0.0.2",
|
||||||
"vite-plugin-lib-inject-css": "^2.2.2",
|
"vite-plugin-lib-inject-css": "^2.2.2",
|
||||||
"vitest": "^3.2.4"
|
"vite-plugin-svg-icons": "^2.0.1",
|
||||||
|
"vitest": "^3.2.4",
|
||||||
|
"vue-tsc": "^3.0.1"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"commitizen": {
|
"commitizen": {
|
||||||
|
|||||||
14992
Yi.Ai.Vue3/pnpm-lock.yaml
generated
14992
Yi.Ai.Vue3/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,340 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="chat-message-list" :style="{ maxHeight }">
|
|
||||||
<div ref="scrollContainer" class="chat-message-list__container">
|
|
||||||
<div class="chat-message-list__content">
|
|
||||||
<div
|
|
||||||
v-for="(item, index) in list"
|
|
||||||
:key="item.key || index"
|
|
||||||
class="chat-message-item"
|
|
||||||
:class="{
|
|
||||||
'chat-message-item--user': item.placement === 'end',
|
|
||||||
'chat-message-item--assistant': item.placement === 'start'
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<!-- 消息头部 -->
|
|
||||||
<div v-if="$slots.header" class="chat-message-item__header">
|
|
||||||
<slot name="header" :item="item" :index="index" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 消息主体 -->
|
|
||||||
<div class="chat-message-item__body">
|
|
||||||
<!-- 头像 -->
|
|
||||||
<div v-if="item.avatar" class="chat-message-item__avatar">
|
|
||||||
<img
|
|
||||||
:src="item.avatar"
|
|
||||||
:style="{
|
|
||||||
width: item.avatarSize || '40px',
|
|
||||||
height: item.avatarSize || '40px'
|
|
||||||
}"
|
|
||||||
alt="avatar"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 消息内容 -->
|
|
||||||
<div
|
|
||||||
class="chat-message-item__content"
|
|
||||||
:class="{
|
|
||||||
'chat-message-item__content--no-style': item.noStyle
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<slot name="content" :item="item" :index="index">
|
|
||||||
{{ item.content }}
|
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 消息底部 -->
|
|
||||||
<div v-if="$slots.footer" class="chat-message-item__footer">
|
|
||||||
<slot name="footer" :item="item" :index="index" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, nextTick, onMounted, onUnmounted, watch } from 'vue';
|
|
||||||
|
|
||||||
interface MessageItem {
|
|
||||||
key?: number;
|
|
||||||
avatar?: string;
|
|
||||||
avatarSize?: string;
|
|
||||||
placement?: 'start' | 'end';
|
|
||||||
content?: string;
|
|
||||||
noStyle?: boolean;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
list: MessageItem[];
|
|
||||||
maxHeight?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
|
||||||
list: () => [],
|
|
||||||
maxHeight: '100%'
|
|
||||||
});
|
|
||||||
|
|
||||||
const scrollContainer = ref<HTMLDivElement | null>(null);
|
|
||||||
const autoScroll = ref(true); // 是否自动滚动到底部
|
|
||||||
const isUserScrolling = ref(false); // 用户是否正在手动滚动
|
|
||||||
let scrollTimeout: any = null;
|
|
||||||
let mutationObserver: MutationObserver | null = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查是否滚动到底部
|
|
||||||
* 允许一定的误差范围(5px)
|
|
||||||
*/
|
|
||||||
function isScrolledToBottom(): boolean {
|
|
||||||
if (!scrollContainer.value) return false;
|
|
||||||
const { scrollTop, scrollHeight, clientHeight } = scrollContainer.value;
|
|
||||||
return scrollHeight - scrollTop - clientHeight < 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 平滑滚动到底部(不使用 smooth 行为,避免跳动)
|
|
||||||
*/
|
|
||||||
function scrollToBottomSmooth() {
|
|
||||||
if (!scrollContainer.value || !autoScroll.value) return;
|
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
if (scrollContainer.value) {
|
|
||||||
scrollContainer.value.scrollTop = scrollContainer.value.scrollHeight;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 滚动到底部(立即)
|
|
||||||
*/
|
|
||||||
function scrollToBottom() {
|
|
||||||
nextTick(() => {
|
|
||||||
if (scrollContainer.value) {
|
|
||||||
autoScroll.value = true; // 重新启用自动滚动
|
|
||||||
scrollContainer.value.scrollTop = scrollContainer.value.scrollHeight;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理滚动事件
|
|
||||||
* 检测用户是否手动滚动离开底部
|
|
||||||
*/
|
|
||||||
function handleScroll() {
|
|
||||||
if (!scrollContainer.value) return;
|
|
||||||
|
|
||||||
// 清除之前的定时器
|
|
||||||
if (scrollTimeout) {
|
|
||||||
clearTimeout(scrollTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 标记用户正在滚动
|
|
||||||
isUserScrolling.value = true;
|
|
||||||
|
|
||||||
// 检查是否在底部
|
|
||||||
const atBottom = isScrolledToBottom();
|
|
||||||
|
|
||||||
if (atBottom) {
|
|
||||||
// 如果滚动到底部,启用自动滚动
|
|
||||||
autoScroll.value = true;
|
|
||||||
} else {
|
|
||||||
// 如果不在底部,禁用自动滚动
|
|
||||||
autoScroll.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 300ms 后标记用户停止滚动
|
|
||||||
scrollTimeout = setTimeout(() => {
|
|
||||||
isUserScrolling.value = false;
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 监听内容变化,自动滚动到底部
|
|
||||||
*/
|
|
||||||
function observeContentChanges() {
|
|
||||||
if (!scrollContainer.value) return;
|
|
||||||
|
|
||||||
const contentElement = scrollContainer.value.querySelector('.chat-message-list__content');
|
|
||||||
if (!contentElement) return;
|
|
||||||
|
|
||||||
// 创建 MutationObserver 监听内容变化
|
|
||||||
mutationObserver = new MutationObserver(() => {
|
|
||||||
// 如果启用了自动滚动且用户没有在滚动,则滚动到底部
|
|
||||||
if (autoScroll.value && !isUserScrolling.value) {
|
|
||||||
scrollToBottomSmooth();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听子元素的变化和文本内容的变化
|
|
||||||
mutationObserver.observe(contentElement, {
|
|
||||||
childList: true, // 监听子元素的添加/删除
|
|
||||||
subtree: true, // 监听所有后代元素
|
|
||||||
characterData: true, // 监听文本内容变化
|
|
||||||
attributes: false // 不监听属性变化
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列表变化
|
|
||||||
watch(
|
|
||||||
() => props.list,
|
|
||||||
() => {
|
|
||||||
// 列表变化时,如果启用了自动滚动,滚动到底部
|
|
||||||
if (autoScroll.value) {
|
|
||||||
nextTick(() => {
|
|
||||||
scrollToBottomSmooth();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
// 组件挂载时初始化
|
|
||||||
onMounted(() => {
|
|
||||||
if (scrollContainer.value) {
|
|
||||||
scrollContainer.value.addEventListener('scroll', handleScroll, { passive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化时滚动到底部
|
|
||||||
nextTick(() => {
|
|
||||||
scrollToBottom();
|
|
||||||
// 开始监听内容变化
|
|
||||||
observeContentChanges();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 组件卸载时清理
|
|
||||||
onUnmounted(() => {
|
|
||||||
if (scrollContainer.value) {
|
|
||||||
scrollContainer.value.removeEventListener('scroll', handleScroll);
|
|
||||||
}
|
|
||||||
if (scrollTimeout) {
|
|
||||||
clearTimeout(scrollTimeout);
|
|
||||||
}
|
|
||||||
if (mutationObserver) {
|
|
||||||
mutationObserver.disconnect();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 暴露方法给父组件调用
|
|
||||||
defineExpose({
|
|
||||||
scrollToBottom
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.chat-message-list {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&__container {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
|
|
||||||
/* 美化滚动条 */
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
background: rgba(0, 0, 0, 0.2);
|
|
||||||
border-radius: 3px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__content {
|
|
||||||
width: 100%;
|
|
||||||
min-height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-message-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
|
|
||||||
&__header {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__body {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__avatar {
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
img {
|
|
||||||
border-radius: 50%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__content {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
padding: 12px 16px;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
border-radius: 12px;
|
|
||||||
word-wrap: break-word;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
|
|
||||||
&--no-style {
|
|
||||||
background-color: transparent;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__footer {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 8px;
|
|
||||||
padding-left: 52px; /* 头像宽度 + gap */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 用户消息样式 */
|
|
||||||
&--user {
|
|
||||||
.chat-message-item__body {
|
|
||||||
flex-direction: row-reverse;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-message-item__content {
|
|
||||||
background-color: #409eff;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-message-item__footer {
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 52px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 助手消息样式 */
|
|
||||||
&--assistant {
|
|
||||||
.chat-message-item__body {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-message-item__content {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -6,6 +6,8 @@ import { ElMessage } from 'element-plus';
|
|||||||
import mammoth from 'mammoth';
|
import mammoth from 'mammoth';
|
||||||
import * as pdfjsLib from 'pdfjs-dist';
|
import * as pdfjsLib from 'pdfjs-dist';
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
|
import Popover from '@/components/Popover/index.vue';
|
||||||
|
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||||
import { useFilesStore } from '@/stores/modules/files';
|
import { useFilesStore } from '@/stores/modules/files';
|
||||||
|
|
||||||
// 配置 PDF.js worker - 使用稳定的 CDN
|
// 配置 PDF.js worker - 使用稳定的 CDN
|
||||||
@@ -411,74 +413,50 @@ onChange(async (files) => {
|
|||||||
// 处理图片文件
|
// 处理图片文件
|
||||||
if (isImage) {
|
if (isImage) {
|
||||||
try {
|
try {
|
||||||
// 控制参数:是否开启图片压缩
|
// 多级压缩策略:逐步降低质量和分辨率
|
||||||
const enableImageCompression = true; // 这里可以设置为变量或从配置读取
|
const compressionLevels = [
|
||||||
|
{ maxWidth: 800, maxHeight: 800, quality: 0.6 },
|
||||||
|
{ maxWidth: 600, maxHeight: 600, quality: 0.5 },
|
||||||
|
{ maxWidth: 400, maxHeight: 400, quality: 0.4 },
|
||||||
|
];
|
||||||
|
|
||||||
let finalBlob: Blob = file;
|
let compressedBlob: Blob | null = null;
|
||||||
let base64 = '';
|
let base64 = '';
|
||||||
let compressionLevel = 0;
|
let compressionLevel = 0;
|
||||||
const originalSize = (file.size / 1024).toFixed(2);
|
|
||||||
let finalSize = originalSize;
|
|
||||||
|
|
||||||
if (enableImageCompression) {
|
// 尝试不同级别的压缩
|
||||||
// 多级压缩策略:逐步降低质量和分辨率
|
for (const level of compressionLevels) {
|
||||||
const compressionLevels = [
|
compressionLevel++;
|
||||||
{ maxWidth: 800, maxHeight: 800, quality: 0.6 },
|
compressedBlob = await compressImage(file, level.maxWidth, level.maxHeight, level.quality);
|
||||||
{ maxWidth: 600, maxHeight: 600, quality: 0.5 },
|
base64 = await blobToBase64(compressedBlob);
|
||||||
{ maxWidth: 400, maxHeight: 400, quality: 0.4 },
|
|
||||||
];
|
|
||||||
|
|
||||||
let compressedBlob: Blob | null = null;
|
// 检查是否满足总长度限制
|
||||||
|
|
||||||
// 尝试不同级别的压缩
|
|
||||||
for (const level of compressionLevels) {
|
|
||||||
compressionLevel++;
|
|
||||||
compressedBlob = await compressImage(file, level.maxWidth, level.maxHeight, level.quality);
|
|
||||||
base64 = await blobToBase64(compressedBlob);
|
|
||||||
|
|
||||||
// 检查是否满足总长度限制
|
|
||||||
const estimatedLength = Math.floor(base64.length * 0.5);
|
|
||||||
if (totalContentLength + estimatedLength <= MAX_TOTAL_CONTENT_LENGTH) {
|
|
||||||
// 满足限制,使用当前压缩级别
|
|
||||||
totalContentLength += estimatedLength;
|
|
||||||
finalBlob = compressedBlob;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果是最后一级压缩仍然超限,则跳过
|
|
||||||
if (compressionLevel === compressionLevels.length) {
|
|
||||||
const fileSizeMB = (file.size / 1024 / 1024).toFixed(2);
|
|
||||||
ElMessage.error(`${file.name} 图片内容过大,请压缩后上传`);
|
|
||||||
compressedBlob = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果压缩失败,跳过此文件
|
|
||||||
if (!compressedBlob) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算压缩比例
|
|
||||||
finalSize = (finalBlob.size / 1024).toFixed(2);
|
|
||||||
console.log(`图片压缩: ${file.name} - 原始: ${originalSize}KB, 压缩后: ${finalSize}KB (级别${compressionLevel})`);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// 不开启压缩时,直接转换原始文件
|
|
||||||
base64 = await blobToBase64(file);
|
|
||||||
|
|
||||||
// 检查总长度限制
|
|
||||||
const estimatedLength = Math.floor(base64.length * 0.5);
|
const estimatedLength = Math.floor(base64.length * 0.5);
|
||||||
if (totalContentLength + estimatedLength > MAX_TOTAL_CONTENT_LENGTH) {
|
if (totalContentLength + estimatedLength <= MAX_TOTAL_CONTENT_LENGTH) {
|
||||||
const fileSizeMB = (file.size / 1024 / 1024).toFixed(2);
|
// 满足限制,使用当前压缩级别
|
||||||
ElMessage.error(`${file.name} (${fileSizeMB}MB) 超过总长度限制,已跳过`);
|
totalContentLength += estimatedLength;
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
totalContentLength += estimatedLength;
|
// 如果是最后一级压缩仍然超限,则跳过
|
||||||
console.log(`图片未压缩: ${file.name} - 大小: ${originalSize}KB`);
|
if (compressionLevel === compressionLevels.length) {
|
||||||
|
const fileSizeMB = (file.size / 1024 / 1024).toFixed(2);
|
||||||
|
ElMessage.error(`${file.name} (${fileSizeMB}MB) 即使压缩后仍超过总内容限制,已跳过`);
|
||||||
|
compressedBlob = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果压缩失败,跳过此文件
|
||||||
|
if (!compressedBlob) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算压缩比例
|
||||||
|
const originalSize = (file.size / 1024).toFixed(2);
|
||||||
|
const compressedSize = (compressedBlob.size / 1024).toFixed(2);
|
||||||
|
console.log(`图片压缩: ${file.name} - 原始: ${originalSize}KB, 压缩后: ${compressedSize}KB (级别${compressionLevel})`);
|
||||||
|
|
||||||
arr.push({
|
arr.push({
|
||||||
uid: crypto.randomUUID(),
|
uid: crypto.randomUUID(),
|
||||||
name: file.name,
|
name: file.name,
|
||||||
@@ -495,8 +473,8 @@ onChange(async (files) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('处理图片失败:', error);
|
console.error('压缩图片失败:', error);
|
||||||
ElMessage.error(`${file.name} 处理失败`);
|
ElMessage.error(`${file.name} 压缩失败`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -515,10 +493,9 @@ onChange(async (files) => {
|
|||||||
// 至少保留1000字符才有意义
|
// 至少保留1000字符才有意义
|
||||||
finalContent = result.content.substring(0, remainingSpace);
|
finalContent = result.content.substring(0, remainingSpace);
|
||||||
wasTruncated = true;
|
wasTruncated = true;
|
||||||
}
|
} else if (remainingSpace <= 1000) {
|
||||||
else if (remainingSpace <= 1000) {
|
|
||||||
const fileSizeKB = (file.size / 1024).toFixed(2);
|
const fileSizeKB = (file.size / 1024).toFixed(2);
|
||||||
ElMessage.error(`${file.name} (${fileSizeKB}KB) 会超过总长度限制,已跳过`);
|
ElMessage.error(`${file.name} (${fileSizeKB}KB) 会超过总内容限制,已跳过`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,10 +542,9 @@ onChange(async (files) => {
|
|||||||
if (result.content.length > remainingSpace && remainingSpace > 1000) {
|
if (result.content.length > remainingSpace && remainingSpace > 1000) {
|
||||||
finalContent = result.content.substring(0, remainingSpace);
|
finalContent = result.content.substring(0, remainingSpace);
|
||||||
wasTruncated = true;
|
wasTruncated = true;
|
||||||
}
|
} else if (remainingSpace <= 1000) {
|
||||||
else if (remainingSpace <= 1000) {
|
|
||||||
const fileSizeKB = (file.size / 1024).toFixed(2);
|
const fileSizeKB = (file.size / 1024).toFixed(2);
|
||||||
ElMessage.error(`${file.name} (${fileSizeKB}KB) 会超过总长度限制,已跳过`);
|
ElMessage.error(`${file.name} (${fileSizeKB}KB) 会超过总内容限制,已跳过`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,10 +591,9 @@ onChange(async (files) => {
|
|||||||
if (result.content.length > remainingSpace && remainingSpace > 1000) {
|
if (result.content.length > remainingSpace && remainingSpace > 1000) {
|
||||||
finalContent = result.content.substring(0, remainingSpace);
|
finalContent = result.content.substring(0, remainingSpace);
|
||||||
wasTruncated = true;
|
wasTruncated = true;
|
||||||
}
|
} else if (remainingSpace <= 1000) {
|
||||||
else if (remainingSpace <= 1000) {
|
|
||||||
const fileSizeKB = (file.size / 1024).toFixed(2);
|
const fileSizeKB = (file.size / 1024).toFixed(2);
|
||||||
ElMessage.error(`${file.name} (${fileSizeKB}KB) 会超过总长度限制,已跳过`);
|
ElMessage.error(`${file.name} (${fileSizeKB}KB) 会超过总内容限制,已跳过`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,10 +645,9 @@ onChange(async (files) => {
|
|||||||
if (finalContent.length > remainingSpace && remainingSpace > 1000) {
|
if (finalContent.length > remainingSpace && remainingSpace > 1000) {
|
||||||
finalContent = finalContent.substring(0, remainingSpace);
|
finalContent = finalContent.substring(0, remainingSpace);
|
||||||
truncated = true;
|
truncated = true;
|
||||||
}
|
} else if (remainingSpace <= 1000) {
|
||||||
else if (remainingSpace <= 1000) {
|
|
||||||
const fileSizeKB = (file.size / 1024).toFixed(2);
|
const fileSizeKB = (file.size / 1024).toFixed(2);
|
||||||
ElMessage.error(`${file.name} (${fileSizeKB}KB) 会超过总长度限制,已跳过`);
|
ElMessage.error(`${file.name} (${fileSizeKB}KB) 会超过总内容限制,已跳过`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,12 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div v-show="layout === 'blankPage'">
|
||||||
<component :is="LayoutComponent[layout]" />
|
<LayoutBlankPage />
|
||||||
|
<!-- <component :is="LayoutComponent[layout]" /> -->
|
||||||
|
</div>
|
||||||
|
<div v-show="layout !== 'blankPage'">
|
||||||
|
<LayoutVertical />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { AnyObject } from 'typescript-api-pro';
|
import type { AnyObject } from 'typescript-api-pro';
|
||||||
import type { BubbleProps } from 'vue-element-plus-x/types/Bubble';
|
import type { BubbleProps } from 'vue-element-plus-x/types/Bubble';
|
||||||
|
import type { BubbleListInstance } from 'vue-element-plus-x/types/BubbleList';
|
||||||
import type { FilesCardProps } from 'vue-element-plus-x/types/FilesCard';
|
import type { FilesCardProps } from 'vue-element-plus-x/types/FilesCard';
|
||||||
import type { ThinkingStatus } from 'vue-element-plus-x/types/Thinking';
|
import type { ThinkingStatus } from 'vue-element-plus-x/types/Thinking';
|
||||||
import { ArrowLeftBold, ArrowRightBold, Document, Loading } from '@element-plus/icons-vue';
|
import { ArrowLeftBold, ArrowRightBold, Document, Loading } from '@element-plus/icons-vue';
|
||||||
@@ -12,8 +13,6 @@ import { Sender } from 'vue-element-plus-x';
|
|||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { send } from '@/api';
|
import { send } from '@/api';
|
||||||
import ModelSelect from '@/components/ModelSelect/index.vue';
|
import ModelSelect from '@/components/ModelSelect/index.vue';
|
||||||
import YMarkdown from '@/vue-element-plus-y/components/XMarkdown/index.vue';
|
|
||||||
import ChatMessageList from '@/components/ChatMessageList/index.vue';
|
|
||||||
import { useGuideTourStore } from '@/stores';
|
import { useGuideTourStore } from '@/stores';
|
||||||
import { useChatStore } from '@/stores/modules/chat';
|
import { useChatStore } from '@/stores/modules/chat';
|
||||||
import { useFilesStore } from '@/stores/modules/files';
|
import { useFilesStore } from '@/stores/modules/files';
|
||||||
@@ -22,6 +21,7 @@ import { useUserStore } from '@/stores/modules/user';
|
|||||||
import { getUserProfilePicture, systemProfilePicture } from '@/utils/user.ts';
|
import { getUserProfilePicture, systemProfilePicture } from '@/utils/user.ts';
|
||||||
import '@/styles/github-markdown.css';
|
import '@/styles/github-markdown.css';
|
||||||
import '@/styles/yixin-markdown.scss';
|
import '@/styles/yixin-markdown.scss';
|
||||||
|
import YMarkdown from '@/vue-element-plus-y/components/XMarkdown/index.vue';
|
||||||
|
|
||||||
type MessageItem = BubbleProps & {
|
type MessageItem = BubbleProps & {
|
||||||
key: number;
|
key: number;
|
||||||
@@ -50,7 +50,7 @@ const avatar = computed(() => {
|
|||||||
const inputValue = ref('');
|
const inputValue = ref('');
|
||||||
const senderRef = ref<InstanceType<typeof Sender> | null>(null);
|
const senderRef = ref<InstanceType<typeof Sender> | null>(null);
|
||||||
const bubbleItems = ref<MessageItem[]>([]);
|
const bubbleItems = ref<MessageItem[]>([]);
|
||||||
const bubbleListRef = ref<InstanceType<typeof ChatMessageList> | null>(null);
|
const bubbleListRef = ref<BubbleListInstance | null>(null);
|
||||||
const isSending = ref(false);
|
const isSending = ref(false);
|
||||||
|
|
||||||
const { stream, loading: isLoading, cancel } = useHookFetch({
|
const { stream, loading: isLoading, cancel } = useHookFetch({
|
||||||
@@ -446,7 +446,7 @@ function handleImagePreview(url: string) {
|
|||||||
<template>
|
<template>
|
||||||
<div class="chat-with-id-container">
|
<div class="chat-with-id-container">
|
||||||
<div class="chat-warp">
|
<div class="chat-warp">
|
||||||
<ChatMessageList ref="bubbleListRef" :list="bubbleItems" max-height="calc(100vh - 240px)">
|
<BubbleList ref="bubbleListRef" :list="bubbleItems" max-height="calc(100vh - 240px)">
|
||||||
<template #header="{ item }">
|
<template #header="{ item }">
|
||||||
<Thinking
|
<Thinking
|
||||||
v-if="item.reasoning_content" v-model="item.thinlCollapse" :content="item.reasoning_content"
|
v-if="item.reasoning_content" v-model="item.thinlCollapse" :content="item.reasoning_content"
|
||||||
@@ -503,7 +503,7 @@ function handleImagePreview(url: string) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ChatMessageList>
|
</BubbleList>
|
||||||
|
|
||||||
<Sender
|
<Sender
|
||||||
ref="senderRef" v-model="inputValue" class="chat-defaul-sender" data-tour="chat-sender" :auto-size="{
|
ref="senderRef" v-model="inputValue" class="chat-defaul-sender" data-tour="chat-sender" :auto-size="{
|
||||||
@@ -577,10 +577,10 @@ function handleImagePreview(url: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
:deep() {
|
:deep() {
|
||||||
.chat-message-list {
|
.el-bubble-list {
|
||||||
padding-top: 24px;
|
padding-top: 24px;
|
||||||
}
|
}
|
||||||
.chat-message-item {
|
.el-bubble {
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
padding-bottom: 24px;
|
padding-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import type { PluggableList } from 'unified';
|
|||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
|
|
||||||
import type { CustomAttrs, SanitizeOptions, TVueMarkdown } from './types';
|
import type { CustomAttrs, SanitizeOptions, TVueMarkdown } from './types';
|
||||||
import { defineComponent, shallowRef, toRefs, watch } from 'vue';
|
import { computed, defineComponent, ref, shallowRef, toRefs, watch } from 'vue';
|
||||||
|
import { watchDebounced } from '@vueuse/core';
|
||||||
// import { useMarkdownContext } from '../components/MarkdownProvider';
|
// import { useMarkdownContext } from '../components/MarkdownProvider';
|
||||||
import { render } from './hast-to-vnode';
|
import { render } from './hast-to-vnode';
|
||||||
import { useMarkdownProcessor } from './useProcessor';
|
import { useMarkdownProcessor } from './useProcessor';
|
||||||
@@ -63,10 +64,24 @@ const vueMarkdownImpl = defineComponent({
|
|||||||
sanitizeOptions
|
sanitizeOptions
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 防抖优化:控制markdown更新频率,避免流式渲染时频繁触发
|
||||||
|
const debouncedMarkdown = ref(markdown.value);
|
||||||
|
watchDebounced(
|
||||||
|
markdown,
|
||||||
|
(val) => {
|
||||||
|
debouncedMarkdown.value = val;
|
||||||
|
},
|
||||||
|
{ debounce: 50, maxWait: 200 } // 50ms防抖,最多200ms必须更新一次
|
||||||
|
);
|
||||||
|
|
||||||
|
// 缓存优化:使用computed缓存解析结果,避免重复计算
|
||||||
|
const hast = computed(() => {
|
||||||
|
const mdast = processor.value.parse(debouncedMarkdown.value);
|
||||||
|
return processor.value.runSync(mdast) as Root;
|
||||||
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
const mdast = processor.value.parse(markdown.value);
|
return render(hast.value, attrs, slots, customAttrs.value);
|
||||||
const hast = processor.value.runSync(mdast) as Root;
|
|
||||||
return render(hast, attrs, slots, customAttrs.value);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -93,12 +108,25 @@ const vueMarkdownAsyncImpl = defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const hast = shallowRef<Root | null>(null);
|
const hast = shallowRef<Root | null>(null);
|
||||||
|
|
||||||
|
// 防抖优化:控制markdown更新频率
|
||||||
|
const debouncedMarkdown = ref(markdown.value);
|
||||||
|
|
||||||
const process = async (): Promise<void> => {
|
const process = async (): Promise<void> => {
|
||||||
const mdast = processor.value.parse(markdown.value);
|
const mdast = processor.value.parse(debouncedMarkdown.value);
|
||||||
hast.value = (await processor.value.run(mdast)) as Root;
|
hast.value = (await processor.value.run(mdast)) as Root;
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => [markdown.value, processor.value], process, { flush: 'sync' });
|
// 使用防抖watch,避免频繁触发异步处理
|
||||||
|
watchDebounced(
|
||||||
|
markdown,
|
||||||
|
(val) => {
|
||||||
|
debouncedMarkdown.value = val;
|
||||||
|
},
|
||||||
|
{ debounce: 50, maxWait: 200 }
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(() => [debouncedMarkdown.value, processor.value], process, { flush: 'post' });
|
||||||
|
|
||||||
await process();
|
await process();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { TVueMarkdownProps } from '../';
|
import type { TVueMarkdownProps } from '@components/XMarkdownCore';
|
||||||
import type { CodeBlockHeaderExpose } from '../components/CodeBlock/shiki-header';
|
import type { CodeBlockHeaderExpose } from '@components/XMarkdownCore/components/CodeBlock/shiki-header';
|
||||||
import type { ElxRunCodeOptions } from '../components/RunCode/type';
|
import type { ElxRunCodeOptions } from '../components/RunCode/type';
|
||||||
import type { InitShikiOptions } from './shikiHighlighter';
|
import type { InitShikiOptions } from './shikiHighlighter';
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"]
|
"@/*": ["src/*"],
|
||||||
|
"@components/*": ["src/vue-element-plus-y/components/*"]
|
||||||
},
|
},
|
||||||
"typeRoots": ["./node_modules/@types", "./types"],
|
"typeRoots": ["./node_modules/@types", "./types"],
|
||||||
/* Linting */
|
/* Linting */
|
||||||
|
|||||||
16
Yi.Ai.Vue3/types/components.d.ts
vendored
16
Yi.Ai.Vue3/types/components.d.ts
vendored
@@ -11,7 +11,11 @@ declare module 'vue' {
|
|||||||
AccountPassword: typeof import('./../src/components/LoginDialog/components/FormLogin/AccountPassword.vue')['default']
|
AccountPassword: typeof import('./../src/components/LoginDialog/components/FormLogin/AccountPassword.vue')['default']
|
||||||
APIKeyManagement: typeof import('./../src/components/userPersonalCenter/components/APIKeyManagement.vue')['default']
|
APIKeyManagement: typeof import('./../src/components/userPersonalCenter/components/APIKeyManagement.vue')['default']
|
||||||
CardFlipActivity: typeof import('./../src/components/userPersonalCenter/components/CardFlipActivity.vue')['default']
|
CardFlipActivity: typeof import('./../src/components/userPersonalCenter/components/CardFlipActivity.vue')['default']
|
||||||
ChatMessageList: typeof import('./../src/components/ChatMessageList/index.vue')['default']
|
CodeBlock: typeof import('./../src/components/YMarkdownCore/components/CodeBlock/index.vue')['default']
|
||||||
|
CodeLine: typeof import('./../src/components/YMarkdownCore/components/CodeLine/index.vue')['default']
|
||||||
|
CodeX: typeof import('./../src/components/YMarkdownCore/components/CodeX/index.vue')['default']
|
||||||
|
CopyCodeButton: typeof import('./../src/components/YMarkdownCore/components/CodeBlock/copy-code-button.vue')['default']
|
||||||
|
CustomLoading: typeof import('./../src/components/YMarkdownCore/components/RunCode/components/custom-loading.vue')['default']
|
||||||
DailyTask: typeof import('./../src/components/userPersonalCenter/components/DailyTask.vue')['default']
|
DailyTask: typeof import('./../src/components/userPersonalCenter/components/DailyTask.vue')['default']
|
||||||
DeepThinking: typeof import('./../src/components/DeepThinking/index.vue')['default']
|
DeepThinking: typeof import('./../src/components/DeepThinking/index.vue')['default']
|
||||||
ElAlert: typeof import('element-plus/es')['ElAlert']
|
ElAlert: typeof import('element-plus/es')['ElAlert']
|
||||||
@@ -25,8 +29,6 @@ declare module 'vue' {
|
|||||||
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
||||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||||
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
|
|
||||||
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
|
|
||||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||||
ElDivider: typeof import('element-plus/es')['ElDivider']
|
ElDivider: typeof import('element-plus/es')['ElDivider']
|
||||||
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||||
@@ -59,9 +61,12 @@ declare module 'vue' {
|
|||||||
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
|
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
|
||||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||||
FilesSelect: typeof import('./../src/components/FilesSelect/index.vue')['default']
|
FilesSelect: typeof import('./../src/components/FilesSelect/index.vue')['default']
|
||||||
|
HighLightCode: typeof import('./../src/components/YMarkdownCore/components/HighLightCode/index.vue')['default']
|
||||||
IconSelect: typeof import('./../src/components/IconSelect/index.vue')['default']
|
IconSelect: typeof import('./../src/components/IconSelect/index.vue')['default']
|
||||||
Indexl: typeof import('./../src/components/SupportModelProducts/indexl.vue')['default']
|
Indexl: typeof import('./../src/components/SupportModelProducts/indexl.vue')['default']
|
||||||
LoginDialog: typeof import('./../src/components/LoginDialog/index.vue')['default']
|
LoginDialog: typeof import('./../src/components/LoginDialog/index.vue')['default']
|
||||||
|
Mermaid: typeof import('./../src/components/YMarkdownCore/components/Mermaid/index.vue')['default']
|
||||||
|
MermaidToolbar: typeof import('./../src/components/YMarkdownCore/components/Mermaid/MermaidToolbar.vue')['default']
|
||||||
ModeList: typeof import('./../src/components/modeList/index.vue')['default']
|
ModeList: typeof import('./../src/components/modeList/index.vue')['default']
|
||||||
ModelSelect: typeof import('./../src/components/ModelSelect/index.vue')['default']
|
ModelSelect: typeof import('./../src/components/ModelSelect/index.vue')['default']
|
||||||
NavDialog: typeof import('./../src/components/userPersonalCenter/NavDialog.vue')['default']
|
NavDialog: typeof import('./../src/components/userPersonalCenter/NavDialog.vue')['default']
|
||||||
@@ -75,6 +80,10 @@ declare module 'vue' {
|
|||||||
RegistrationForm: typeof import('./../src/components/LoginDialog/components/FormLogin/RegistrationForm.vue')['default']
|
RegistrationForm: typeof import('./../src/components/LoginDialog/components/FormLogin/RegistrationForm.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
RunCode: typeof import('./../src/components/YMarkdownCore/components/RunCode/index.vue')['default']
|
||||||
|
RunCodeButton: typeof import('./../src/components/YMarkdownCore/components/CodeBlock/run-code-button.vue')['default']
|
||||||
|
RunCodeContent: typeof import('./../src/components/YMarkdownCore/components/RunCode/components/run-code-content.vue')['default']
|
||||||
|
RunCodeHeader: typeof import('./../src/components/YMarkdownCore/components/RunCode/components/run-code-header.vue')['default']
|
||||||
SupportModelList: typeof import('./../src/components/userPersonalCenter/components/SupportModelList.vue')['default']
|
SupportModelList: typeof import('./../src/components/userPersonalCenter/components/SupportModelList.vue')['default']
|
||||||
SvgIcon: typeof import('./../src/components/SvgIcon/index.vue')['default']
|
SvgIcon: typeof import('./../src/components/SvgIcon/index.vue')['default']
|
||||||
SystemAnnouncementDialog: typeof import('./../src/components/SystemAnnouncementDialog/index.vue')['default']
|
SystemAnnouncementDialog: typeof import('./../src/components/SystemAnnouncementDialog/index.vue')['default']
|
||||||
@@ -83,6 +92,7 @@ declare module 'vue' {
|
|||||||
UserManagement: typeof import('./../src/components/userPersonalCenter/components/UserManagement.vue')['default']
|
UserManagement: typeof import('./../src/components/userPersonalCenter/components/UserManagement.vue')['default']
|
||||||
VerificationCode: typeof import('./../src/components/LoginDialog/components/FormLogin/VerificationCode.vue')['default']
|
VerificationCode: typeof import('./../src/components/LoginDialog/components/FormLogin/VerificationCode.vue')['default']
|
||||||
WelecomeText: typeof import('./../src/components/WelecomeText/index.vue')['default']
|
WelecomeText: typeof import('./../src/components/WelecomeText/index.vue')['default']
|
||||||
|
YMarkdownAsync: typeof import('./../src/components/YMarkdownAsync/index.vue')['default']
|
||||||
}
|
}
|
||||||
export interface GlobalDirectives {
|
export interface GlobalDirectives {
|
||||||
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
||||||
|
|||||||
Reference in New Issue
Block a user