Merge remote-tracking branch 'origin/ai-hub' into ai-hub
This commit is contained in:
@@ -19,3 +19,8 @@ export function getChatList(params: GetChatListParams) {
|
|||||||
// return get<ChatMessageVo[]>('/system/message/list', params);
|
// return get<ChatMessageVo[]>('/system/message/list', params);
|
||||||
return get<ChatMessageVo[]>('/message', params).json();
|
return get<ChatMessageVo[]>('/message', params).json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增对应会话聊天记录
|
||||||
|
export function aiChatTool() {
|
||||||
|
return post('/ai-chat/tool').json();
|
||||||
|
}
|
||||||
|
|||||||
416
Yi.Ai.Vue3/src/components/ToolList/index.vue
Normal file
416
Yi.Ai.Vue3/src/components/ToolList/index.vue
Normal file
@@ -0,0 +1,416 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ChromeFilled, ElementPlus, Loading, MagicStick, Search } from '@element-plus/icons-vue';
|
||||||
|
import { computed, onMounted, ref } from 'vue';
|
||||||
|
import { aiChatTool } from '@/api';
|
||||||
|
|
||||||
|
// API返回的工具接口
|
||||||
|
interface ApiToolItem {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
inputSchema: {
|
||||||
|
type: string;
|
||||||
|
properties: Record<string, any>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 前端工具按钮的数据
|
||||||
|
interface ToolItem {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
apiName: string;
|
||||||
|
icon: any;
|
||||||
|
tip: string;
|
||||||
|
enabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件事件
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'tools-update': [payload: {
|
||||||
|
selectedToolIds: number[];
|
||||||
|
selectedApiTools: string[];
|
||||||
|
}];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// 图标映射配置 - 根据API名称映射图标
|
||||||
|
const iconMap: Record<string, any> = {
|
||||||
|
online_search: ChromeFilled, // 在线搜索
|
||||||
|
deep_think: ElementPlus, // 深度思考
|
||||||
|
search: Search, // 搜索
|
||||||
|
web_search: ChromeFilled, // 网页搜索
|
||||||
|
thinking: MagicStick, // 思考
|
||||||
|
default: ElementPlus, // 默认图标
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提示信息映射
|
||||||
|
const tipMap: Record<string, string> = {
|
||||||
|
online_search: '实时搜索最新信息,获取网络资料',
|
||||||
|
deep_think: '深度推理分析,解决复杂问题',
|
||||||
|
web_search: '联网搜索网页内容',
|
||||||
|
thinking: '深入思考和分析问题',
|
||||||
|
default: '点击启用此功能',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 响应式工具列表 - 初始为空,等待接口加载
|
||||||
|
const tools = ref<ToolItem[]>([]);
|
||||||
|
|
||||||
|
// 当前选中的工具ID数组(支持多选)
|
||||||
|
const activeToolIds = ref<number[]>([]);
|
||||||
|
const loading = ref(false);
|
||||||
|
const error = ref<string | null>(null);
|
||||||
|
|
||||||
|
// 从API数据转换为前端格式
|
||||||
|
function transformApiTools(apiTools: ApiToolItem[]): ToolItem[] {
|
||||||
|
return apiTools.map((tool, index) => {
|
||||||
|
const apiName = tool.name;
|
||||||
|
return {
|
||||||
|
id: index + 1,
|
||||||
|
name: tool.description, // 使用中文描述
|
||||||
|
apiName,
|
||||||
|
icon: iconMap[apiName] || iconMap.default,
|
||||||
|
tip: tipMap[apiName] || tipMap.default,
|
||||||
|
enabled: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有选中的工具对象
|
||||||
|
const selectedTools = computed(() => {
|
||||||
|
return tools.value.filter(tool => activeToolIds.value.includes(tool.id));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取所有选中的API工具名称
|
||||||
|
const selectedApiTools = computed(() => {
|
||||||
|
return selectedTools.value.map(tool => tool.apiName);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 点击事件处理
|
||||||
|
function handleToolClick(tool: ToolItem) {
|
||||||
|
if (!tool.enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const index = activeToolIds.value.indexOf(tool.id);
|
||||||
|
|
||||||
|
if (index > -1) {
|
||||||
|
activeToolIds.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
activeToolIds.value.push(tool.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('当前选中的工具:', selectedTools.value.map(t => t.name));
|
||||||
|
console.log('对应的API名称:', selectedApiTools.value);
|
||||||
|
|
||||||
|
emitToolsUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数:检查某个工具是否被选中
|
||||||
|
function isActive(toolId: number) {
|
||||||
|
return activeToolIds.value.includes(toolId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空所有选中
|
||||||
|
function clearSelection() {
|
||||||
|
activeToolIds.value = [];
|
||||||
|
emitToolsUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全选已启用的工具
|
||||||
|
function selectAll() {
|
||||||
|
activeToolIds.value = tools.value
|
||||||
|
.filter(tool => tool.enabled)
|
||||||
|
.map(tool => tool.id);
|
||||||
|
emitToolsUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启用/禁用工具
|
||||||
|
function setToolEnabled(toolId: number, enabled: boolean) {
|
||||||
|
const tool = tools.value.find(t => t.id === toolId);
|
||||||
|
if (tool) {
|
||||||
|
tool.enabled = enabled;
|
||||||
|
if (!enabled && isActive(toolId)) {
|
||||||
|
const index = activeToolIds.value.indexOf(toolId);
|
||||||
|
if (index > -1) {
|
||||||
|
activeToolIds.value.splice(index, 1);
|
||||||
|
emitToolsUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据API名称启用/禁用工具
|
||||||
|
function setToolEnabledByApiName(apiName: string, enabled: boolean) {
|
||||||
|
const tool = tools.value.find(t => t.apiName === apiName);
|
||||||
|
if (tool) {
|
||||||
|
setToolEnabled(tool.id, enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知父组件工具状态变化
|
||||||
|
function emitToolsUpdate() {
|
||||||
|
console.log('工具状态已更新:', {
|
||||||
|
selectedToolIds: activeToolIds.value,
|
||||||
|
selectedTools: selectedTools.value,
|
||||||
|
selectedApiTools: selectedApiTools.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 触发自定义事件
|
||||||
|
emit('tools-update', {
|
||||||
|
selectedToolIds: activeToolIds.value,
|
||||||
|
selectedApiTools: selectedApiTools.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从API获取工具列表
|
||||||
|
async function getAiChatToolList() {
|
||||||
|
loading.value = true;
|
||||||
|
error.value = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await aiChatTool();
|
||||||
|
|
||||||
|
if (res.data && Array.isArray(res.data)) {
|
||||||
|
console.log('API返回的工具列表:', res.data);
|
||||||
|
|
||||||
|
// 转换API数据
|
||||||
|
const apiTools = transformApiTools(res.data);
|
||||||
|
|
||||||
|
// 更新工具列表
|
||||||
|
tools.value = apiTools;
|
||||||
|
|
||||||
|
// 默认选中第一个可用的工具
|
||||||
|
const firstEnabledTool = apiTools.find(tool => tool.enabled);
|
||||||
|
if (firstEnabledTool && activeToolIds.value.length === 0) {
|
||||||
|
activeToolIds.value = [firstEnabledTool.id];
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('加载的工具列表:', tools.value);
|
||||||
|
emitToolsUpdate();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
error.value = '接口返回数据格式不正确';
|
||||||
|
console.error('接口返回数据格式错误:', res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
error.value = '获取工具列表失败,请检查网络连接';
|
||||||
|
console.error('获取工具列表失败:', err);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新工具列表
|
||||||
|
async function refreshTools() {
|
||||||
|
await getAiChatToolList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件挂载时获取工具列表
|
||||||
|
onMounted(() => {
|
||||||
|
getAiChatToolList();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 暴露方法给父组件
|
||||||
|
defineExpose({
|
||||||
|
selectedTools,
|
||||||
|
selectedApiTools,
|
||||||
|
clearSelection,
|
||||||
|
selectAll,
|
||||||
|
setToolEnabled,
|
||||||
|
setToolEnabledByApiName,
|
||||||
|
refreshTools,
|
||||||
|
getAiChatToolList,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="tools-container">
|
||||||
|
<!-- 加载状态 -->
|
||||||
|
<div v-if="loading" class="loading-state">
|
||||||
|
<el-icon class="loading-icon">
|
||||||
|
<Loading />
|
||||||
|
</el-icon>
|
||||||
|
<span>加载工具中...</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 错误状态 -->
|
||||||
|
<div v-else-if="error" class="error-state">
|
||||||
|
<el-icon class="error-icon">
|
||||||
|
<ElementPlus />
|
||||||
|
</el-icon>
|
||||||
|
<span>{{ error }}</span>
|
||||||
|
<el-button type="text" class="retry-btn" @click="refreshTools">
|
||||||
|
重试
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<div v-else-if="tools.length === 0" class="empty-state">
|
||||||
|
<span>暂无可用工具</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 工具按钮 -->
|
||||||
|
<div
|
||||||
|
v-for="tool in tools"
|
||||||
|
v-else
|
||||||
|
:key="tool.id"
|
||||||
|
class="tool-item"
|
||||||
|
:class="{
|
||||||
|
active: isActive(tool.id),
|
||||||
|
disabled: !tool.enabled,
|
||||||
|
}"
|
||||||
|
:title="tool.tip"
|
||||||
|
@click="handleToolClick(tool)"
|
||||||
|
>
|
||||||
|
<el-icon class="tool-icon">
|
||||||
|
<component :is="tool.icon" />
|
||||||
|
</el-icon>
|
||||||
|
<span class="tool-text">{{ tool.name }}</span>
|
||||||
|
|
||||||
|
<!-- 选中指示器 -->
|
||||||
|
<!-- <span v-if="isActive(tool.id)" class="check-indicator">✓</span> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 调试信息 -->
|
||||||
|
<div v-if="false" class="debug-info">
|
||||||
|
<div>工具数量: {{ tools.length }}</div>
|
||||||
|
<div>当前选中ID: {{ activeToolIds }}</div>
|
||||||
|
<div>选中的API工具: {{ selectedApiTools }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.tools-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 4px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-state,
|
||||||
|
.error-state,
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-state {
|
||||||
|
.loading-icon {
|
||||||
|
animation: rotate 1s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-state {
|
||||||
|
color: #f56c6c;
|
||||||
|
|
||||||
|
.error-icon {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.retry-btn {
|
||||||
|
margin-left: 8px;
|
||||||
|
padding: 0;
|
||||||
|
height: auto;
|
||||||
|
color: #409EFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
color: #c0c4cc;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 10px 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
background: white;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 10px;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.04);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: rgba(64, 158, 255, 0.1);
|
||||||
|
border-color: #409EFF;
|
||||||
|
color: #409EFF;
|
||||||
|
|
||||||
|
.tool-icon {
|
||||||
|
color: #409EFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(64, 158, 255, 0.15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: white;
|
||||||
|
transform: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active:not(.disabled) {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #606266;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-text {
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-indicator {
|
||||||
|
margin-left: 4px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #409EFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-info {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
div {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -13,13 +13,13 @@ 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 { 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';
|
||||||
import { useModelStore } from '@/stores/modules/model';
|
import { useModelStore } from '@/stores/modules/model';
|
||||||
import { useUserStore } from '@/stores/modules/user';
|
import { useUserStore } from '@/stores/modules/user';
|
||||||
import { getUserProfilePicture, systemProfilePicture } from '@/utils/user.ts';
|
import { getUserProfilePicture, systemProfilePicture } from '@/utils/user.ts';
|
||||||
|
import YMarkdown from '@/vue-element-plus-y/components/XMarkdown/index.vue';
|
||||||
import '@/styles/github-markdown.css';
|
import '@/styles/github-markdown.css';
|
||||||
import '@/styles/yixin-markdown.scss';
|
import '@/styles/yixin-markdown.scss';
|
||||||
|
|
||||||
@@ -284,10 +284,12 @@ async function startSSE(chatContent: string) {
|
|||||||
// 如果有图片或文件,使用数组格式
|
// 如果有图片或文件,使用数组格式
|
||||||
if (contentArray.length > 1 || imageFiles.length > 0 || textFiles.length > 0) {
|
if (contentArray.length > 1 || imageFiles.length > 0 || textFiles.length > 0) {
|
||||||
baseMessage.content = contentArray;
|
baseMessage.content = contentArray;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
baseMessage.content = item.content;
|
baseMessage.content = item.content;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// 其他消息保持原样
|
// 其他消息保持原样
|
||||||
baseMessage.content = (item.role === 'ai' || item.role === 'assistant') && item.content.length > 10000
|
baseMessage.content = (item.role === 'ai' || item.role === 'assistant') && item.content.length > 10000
|
||||||
? `${item.content.substring(0, 10000)}...(内容过长,已省略)`
|
? `${item.content.substring(0, 10000)}...(内容过长,已省略)`
|
||||||
@@ -391,7 +393,7 @@ function addMessage(message: string, isUser: boolean, images?: Array<{ url: stri
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理思考链展开/收起状态变化
|
* 处理思考链展开/收起状态变化
|
||||||
* @param {Object} payload - 状态变化的载荷
|
* @param {object} payload - 状态变化的载荷
|
||||||
* @param {boolean} payload.value - 展开/收起状态
|
* @param {boolean} payload.value - 展开/收起状态
|
||||||
* @param {ThinkingStatus} payload.status - 思考状态
|
* @param {ThinkingStatus} payload.status - 思考状态
|
||||||
*/
|
*/
|
||||||
@@ -477,9 +479,9 @@ function handleImagePreview(url: string) {
|
|||||||
:key="index"
|
:key="index"
|
||||||
class="user-file-item"
|
class="user-file-item"
|
||||||
>
|
>
|
||||||
<el-icon class="file-icon">
|
<ElIcon class="file-icon">
|
||||||
<Document />
|
<Document />
|
||||||
</el-icon>
|
</ElIcon>
|
||||||
<span class="file-name">{{ file.name }}</span>
|
<span class="file-name">{{ file.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -543,6 +545,7 @@ function handleImagePreview(url: string) {
|
|||||||
<template #prefix>
|
<template #prefix>
|
||||||
<div class="flex-1 flex items-center gap-8px flex-none w-fit overflow-hidden">
|
<div class="flex-1 flex items-center gap-8px flex-none w-fit overflow-hidden">
|
||||||
<FilesSelect />
|
<FilesSelect />
|
||||||
|
<!-- < ToolList/> -->
|
||||||
<ModelSelect />
|
<ModelSelect />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
1
Yi.Ai.Vue3/types/components.d.ts
vendored
1
Yi.Ai.Vue3/types/components.d.ts
vendored
@@ -79,6 +79,7 @@ declare module 'vue' {
|
|||||||
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']
|
||||||
TokenFormDialog: typeof import('./../src/components/userPersonalCenter/components/TokenFormDialog.vue')['default']
|
TokenFormDialog: typeof import('./../src/components/userPersonalCenter/components/TokenFormDialog.vue')['default']
|
||||||
|
ToolList: typeof import('./../src/components/ToolList/index.vue')['default']
|
||||||
UsageStatistics: typeof import('./../src/components/userPersonalCenter/components/UsageStatistics.vue')['default']
|
UsageStatistics: typeof import('./../src/components/userPersonalCenter/components/UsageStatistics.vue')['default']
|
||||||
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']
|
||||||
|
|||||||
1
Yi.Ai.Vue3/types/import_meta.d.ts
vendored
1
Yi.Ai.Vue3/types/import_meta.d.ts
vendored
@@ -7,7 +7,6 @@ interface ImportMetaEnv {
|
|||||||
readonly VITE_WEB_BASE_API: string;
|
readonly VITE_WEB_BASE_API: string;
|
||||||
readonly VITE_API_URL: string;
|
readonly VITE_API_URL: string;
|
||||||
readonly VITE_FILE_UPLOAD_API: string;
|
readonly VITE_FILE_UPLOAD_API: string;
|
||||||
readonly VITE_BUILD_COMPRESS: string;
|
|
||||||
readonly VITE_SSO_SEVER_URL: string;
|
readonly VITE_SSO_SEVER_URL: string;
|
||||||
readonly VITE_APP_VERSION: string;
|
readonly VITE_APP_VERSION: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user