add
This commit is contained in:
parent
8500f0752a
commit
0e0ae91473
|
|
@ -1,3 +1,4 @@
|
|||
import './styles/scrollbar.css'
|
||||
import { initPreferences } from '@vben/preferences';
|
||||
import { unmountGlobalLoading } from '@vben/utils';
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
name: 'Settings',
|
||||
path: '/settings',
|
||||
component: () => import('#/views/settings/index.vue'),
|
||||
meta: {
|
||||
icon: 'carbon:settings',
|
||||
title: '设置',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/* 亮色 */
|
||||
::-webkit-scrollbar {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 10px;
|
||||
border: 4px solid #f5f5f5;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #a0a0a0;
|
||||
border: 4px solid #f5f5f5;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
/* 暗色 */
|
||||
html.dark ::-webkit-scrollbar-track {
|
||||
background: #1e1e1e;
|
||||
}
|
||||
|
||||
html.dark ::-webkit-scrollbar-thumb {
|
||||
background: #4a4a4a;
|
||||
border: 4px solid #1e1e1e;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
html.dark ::-webkit-scrollbar-thumb:hover {
|
||||
background: #666666;
|
||||
border: 4px solid #1e1e1e;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import SourceStep from './steps/SourceStep.vue';
|
|||
import SegmentConfig from './steps/SegmentConfig.vue';
|
||||
import SegmentPreview from './steps/SegmentPreview.vue';
|
||||
import type { SegmentConfig as SegmentConfigType } from './steps/SegmentConfig.vue';
|
||||
import Page from '../../../../../../packages/effects/common-ui/src/components/page/page.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
|
@ -50,37 +51,48 @@ function goBack() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<NFlex vertical style="height: 100%; overflow: hidden">
|
||||
<!-- 顶部导航栏 -->
|
||||
<NFlex
|
||||
align="center"
|
||||
style="
|
||||
padding: 0 24px;
|
||||
height: 56px;
|
||||
border-bottom: 1px solid var(--n-border-color);
|
||||
flex-shrink: 0;
|
||||
"
|
||||
>
|
||||
<NButton text @click="goBack">← {{ datasetName }}</NButton>
|
||||
<NSteps :current="currentStep" size="small" style="flex: 1; margin: 0 auto; max-width: 560px">
|
||||
<NStep title="选择数据源" />
|
||||
<NStep title="文本分段与清洗" />
|
||||
<NStep title="处理并完成" />
|
||||
</NSteps>
|
||||
</NFlex>
|
||||
<Page auto-content-height>
|
||||
<NFlex vertical style="width: 100%; height: 100%">
|
||||
<!-- 顶部导航栏 -->
|
||||
<NFlex
|
||||
align="center"
|
||||
style="
|
||||
padding: 0 24px;
|
||||
height: 56px;
|
||||
border-bottom: 1px solid var(--n-border-color);
|
||||
flex-shrink: 0;
|
||||
"
|
||||
>
|
||||
<NButton text @click="goBack">← {{ datasetName }}</NButton>
|
||||
<NSteps
|
||||
:current="currentStep"
|
||||
size="small"
|
||||
style="flex: 1; margin: 0 auto; max-width: 560px"
|
||||
>
|
||||
<NStep title="选择数据源" />
|
||||
<NStep title="文本分段与清洗" />
|
||||
<NStep title="处理并完成" />
|
||||
</NSteps>
|
||||
</NFlex>
|
||||
|
||||
<!-- Step 1: 选择数据源 -->
|
||||
<SourceStep
|
||||
v-if="currentStep === 1"
|
||||
v-model:source="selectedSource"
|
||||
v-model:file-list="fileList"
|
||||
@next="handleSourceNext"
|
||||
/>
|
||||
<!-- Step 1: 选择数据源 -->
|
||||
<SourceStep
|
||||
v-if="currentStep === 1"
|
||||
v-model:source="selectedSource"
|
||||
v-model:file-list="fileList"
|
||||
@next="handleSourceNext"
|
||||
style="flex: 1; overflow-y: auto"
|
||||
/>
|
||||
|
||||
<!-- Step 2: 文本分段与清洗 -->
|
||||
<NFlex v-else-if="currentStep === 2">
|
||||
<SegmentConfig @prev="handlePrev" @save="handleSave" style="width: 50%; overflow: hidden" />
|
||||
<SegmentPreview style="flex: 1; overflow: hidden" />
|
||||
<!-- Step 2: 文本分段与清洗 -->
|
||||
<NFlex v-else-if="currentStep === 2" style="flex: 1; min-height: 0; overflow: hidden">
|
||||
<SegmentConfig
|
||||
@prev="handlePrev"
|
||||
@save="handleSave"
|
||||
style="width: 50%; height: 100%; overflow-y: auto"
|
||||
/>
|
||||
<SegmentPreview style="width: 50%; height: 100%; overflow-y: auto" />
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -69,18 +69,16 @@ function handleSave() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height style="width: 100%">
|
||||
<NFlex
|
||||
vertical
|
||||
:gap="0"
|
||||
style="
|
||||
height: 100%;
|
||||
flex-shrink: 0;
|
||||
border-right: 1px solid var(--n-border-color);
|
||||
overflow-y: auto;
|
||||
padding: 20px 0;
|
||||
"
|
||||
>
|
||||
<NFlex
|
||||
vertical
|
||||
style="
|
||||
height: 100%;
|
||||
flex-shrink: 0;
|
||||
border-right: 1px solid var(--n-border-color);
|
||||
overflow-y: auto;
|
||||
"
|
||||
>
|
||||
<NFlex vertical style="padding: 0 10px">
|
||||
<!-- 分段设置 -->
|
||||
<NText strong style="margin-bottom: 12px">分段设置</NText>
|
||||
|
||||
|
|
@ -223,10 +221,10 @@ function handleSave() {
|
|||
</NFlex>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<NFlex justify="space-between">
|
||||
<NFlex justify="space-between" style="padding-bottom: 24px">
|
||||
<NButton secondary @click="emit('prev')">← 上一步</NButton>
|
||||
<NButton type="primary" @click="handleSave">保存并处理</NButton>
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
</Page>
|
||||
</NFlex>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -45,35 +45,85 @@ const props = withDefaults(defineProps<{ file?: PreviewFile }>(), {
|
|||
content:
|
||||
'二、过去(Past)这类时态都用来描述"过去"发生的动作或状态,有明确时间段,也有在另一个过去之事发生的动作。',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
chars: 89,
|
||||
content:
|
||||
'好的,这种分类方式更直观,面事朋友从"时可"这个维度来理解和记忆时态。我们可以把16种时态归纳为四大类:**现在**、**过去**、**将来**和**过去的将来**。',
|
||||
},
|
||||
{ id: 2, chars: 3, content: '---' },
|
||||
{
|
||||
id: 3,
|
||||
chars: 62,
|
||||
content:
|
||||
'一、现在(Present)这类时态着重与**现在**这个时间点相关,包括可能性动作、正在发生的动作、以及对现在有影响的动作。',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
chars: 638,
|
||||
content:
|
||||
'**一般现在时(Simple Present Tense)** **用法:** 描述习惯、事实、真理。 **例句:** I **go** to the gym every morning.(我每天早上都去健身房。)**现在进行时(Present Continuous Tense)** **用法:** 描述正在发生、或最近一段时间正在进行的动作。 **例句:** He **is studying** for his exam right now.(他现在正在准备考试。)**现在完成时(Present Perfect Tense)** **用法:** 描述过去发生但对现在有影响的动作。 **例句:** I **have lost** my phone.(我把手机弄丢了,所以现在没有手机。)**现在完成进行时(Present Perfect Continuous Tense)** **用法:** 描述从过去开始持续到现在,且可能还在继续的动作。 **例句:** It **has been raining** for two hours.(雨已经下了两个小时了,且目还在下。)',
|
||||
},
|
||||
{ id: 5, chars: 3, content: '---' },
|
||||
{
|
||||
id: 6,
|
||||
chars: 60,
|
||||
content:
|
||||
'二、过去(Past)这类时态都用来描述"过去"发生的动作或状态,有明确时间段,也有在另一个过去之事发生的动作。',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
chars: 89,
|
||||
content:
|
||||
'好的,这种分类方式更直观,面事朋友从"时可"这个维度来理解和记忆时态。我们可以把16种时态归纳为四大类:**现在**、**过去**、**将来**和**过去的将来**。',
|
||||
},
|
||||
{ id: 2, chars: 3, content: '---' },
|
||||
{
|
||||
id: 3,
|
||||
chars: 62,
|
||||
content:
|
||||
'一、现在(Present)这类时态着重与**现在**这个时间点相关,包括可能性动作、正在发生的动作、以及对现在有影响的动作。',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
chars: 638,
|
||||
content:
|
||||
'**一般现在时(Simple Present Tense)** **用法:** 描述习惯、事实、真理。 **例句:** I **go** to the gym every morning.(我每天早上都去健身房。)**现在进行时(Present Continuous Tense)** **用法:** 描述正在发生、或最近一段时间正在进行的动作。 **例句:** He **is studying** for his exam right now.(他现在正在准备考试。)**现在完成时(Present Perfect Tense)** **用法:** 描述过去发生但对现在有影响的动作。 **例句:** I **have lost** my phone.(我把手机弄丢了,所以现在没有手机。)**现在完成进行时(Present Perfect Continuous Tense)** **用法:** 描述从过去开始持续到现在,且可能还在继续的动作。 **例句:** It **has been raining** for two hours.(雨已经下了两个小时了,且目还在下。)',
|
||||
},
|
||||
{ id: 5, chars: 3, content: '---' },
|
||||
{
|
||||
id: 6,
|
||||
chars: 60,
|
||||
content:
|
||||
'二、过去(Past)这类时态都用来描述"过去"发生的动作或状态,有明确时间段,也有在另一个过去之事发生的动作。',
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<NFlex vertical style="flex: 1; overflow-y: auto; padding: 20px 24px" :gap="0">
|
||||
<!-- 文件信息 -->
|
||||
<NFlex align="center" :gap="8" style="margin-bottom: 16px">
|
||||
<NTag size="small" type="info">预览</NTag>
|
||||
<NText style="font-size: 13px">{{ props.file.name }}</NText>
|
||||
<NText depth="3" style="font-size: 12px">{{ props.file.timeAgo }}</NText>
|
||||
</NFlex>
|
||||
|
||||
<!-- 分块列表 -->
|
||||
<NList :show-divider="false">
|
||||
<NListItem v-for="chunk in props.file.chunks" :key="chunk.id" style="padding: 0 0 16px">
|
||||
<NFlex vertical :gap="4">
|
||||
<NFlex align="center" :gap="8">
|
||||
<NTag size="tiny" type="default">Chunk-{{ chunk.id }}</NTag>
|
||||
<NText depth="3" style="font-size: 11px">{{ chunk.chars }} characters</NText>
|
||||
</NFlex>
|
||||
<NText style="font-size: 13px; line-height: 1.7; white-space: pre-wrap">
|
||||
{{ chunk.content }}
|
||||
</NText>
|
||||
</NFlex>
|
||||
</NListItem>
|
||||
</NList>
|
||||
<NFlex vertical style="flex: 1; height: 100%">
|
||||
<!-- 文件信息 -->
|
||||
<NFlex align="center" :gap="8" style="margin-bottom: 16px">
|
||||
<NTag size="small" type="info">预览</NTag>
|
||||
<NText style="font-size: 13px">{{ props.file.name }}</NText>
|
||||
<NText depth="3" style="font-size: 12px">{{ props.file.timeAgo }}</NText>
|
||||
</NFlex>
|
||||
</Page>
|
||||
|
||||
<!-- 分块列表 -->
|
||||
<NList :show-divider="false" style="flex: 1; overflow-y: auto">
|
||||
<NListItem v-for="chunk in props.file.chunks" :key="chunk.id">
|
||||
<NFlex vertical>
|
||||
<NFlex align="center">
|
||||
<NTag size="tiny" type="default">Chunk-{{ chunk.id }}</NTag>
|
||||
<NText depth="3" style="font-size: 11px">{{ chunk.chars }} characters</NText>
|
||||
</NFlex>
|
||||
<NText style="font-size: 13px; line-height: 1.7; white-space: pre-wrap">
|
||||
{{ chunk.content }}
|
||||
</NText>
|
||||
</NFlex>
|
||||
</NListItem>
|
||||
</NList>
|
||||
</NFlex>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ function handleNext() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<NFlex justify="center" align="center">
|
||||
<NFlex justify="center" align="flex-start">
|
||||
<NFlex vertical>
|
||||
<NText strong style="font-size: 15px">选择数据源</NText>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, shallowRef, defineAsyncComponent } from 'vue';
|
||||
import { NFlex, NLayout, NLayoutContent, NLayoutSider, NText } from 'naive-ui';
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
const ApiExtension = defineAsyncComponent(() => import('./panels/ApiExtension.vue'));
|
||||
const ModelProviders = defineAsyncComponent(() => import('./panels/ModelProviders.vue'));
|
||||
const Members = defineAsyncComponent(() => import('./panels/Members.vue'));
|
||||
const DataSources = defineAsyncComponent(() => import('./panels/DataSources.vue'));
|
||||
const Language = defineAsyncComponent(() => import('./panels/Language.vue'));
|
||||
|
||||
type PanelKey = 'model-providers' | 'members' | 'data-sources' | 'api-extension' | 'language';
|
||||
|
||||
const activePanel = ref<PanelKey>('model-providers');
|
||||
|
||||
const panelMap: Record<PanelKey, ReturnType<typeof defineAsyncComponent>> = {
|
||||
'model-providers': ModelProviders,
|
||||
members: Members,
|
||||
'data-sources': DataSources,
|
||||
'api-extension': ApiExtension,
|
||||
language: Language,
|
||||
};
|
||||
|
||||
const workspaceItems: { key: PanelKey; label: string; icon: string }[] = [
|
||||
{ key: 'model-providers', label: '模型供应商', icon: '🤖' },
|
||||
{ key: 'members', label: '成员', icon: '👥' },
|
||||
{ key: 'data-sources', label: '数据来源', icon: '🗃️' },
|
||||
{ key: 'api-extension', label: 'API 扩展', icon: '🔌' },
|
||||
];
|
||||
|
||||
const generalItems: { key: PanelKey; label: string; icon: string }[] = [
|
||||
{ key: 'language', label: '语言', icon: '🌐' },
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<NLayout has-sider style="height: 100%">
|
||||
<NLayoutSider
|
||||
:width="220"
|
||||
:native-scrollbar="false"
|
||||
content-style="padding: 24px 12px;"
|
||||
bordered
|
||||
>
|
||||
<NText strong tag="div" style="font-size: 18px; padding: 0 12px; margin-bottom: 20px">
|
||||
设置
|
||||
</NText>
|
||||
|
||||
<!-- 工作空间 -->
|
||||
<NText
|
||||
depth="3"
|
||||
tag="div"
|
||||
style="
|
||||
font-size: 11px;
|
||||
padding: 0 12px;
|
||||
margin-bottom: 6px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
"
|
||||
>
|
||||
工作空间
|
||||
</NText>
|
||||
<NFlex vertical :gap="2" style="margin-bottom: 16px">
|
||||
<NFlex
|
||||
v-for="item in workspaceItems"
|
||||
:key="item.key"
|
||||
align="center"
|
||||
:gap="10"
|
||||
style="
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
"
|
||||
:style="activePanel === item.key ? 'background: var(--n-color-embedded);' : ''"
|
||||
@click="activePanel = item.key"
|
||||
>
|
||||
<span style="font-size: 15px; line-height: 1">{{ item.icon }}</span>
|
||||
<NText :style="activePanel === item.key ? 'font-weight: 500;' : ''">
|
||||
{{ item.label }}
|
||||
</NText>
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
|
||||
<!-- 通用 -->
|
||||
<NText
|
||||
depth="3"
|
||||
tag="div"
|
||||
style="
|
||||
font-size: 11px;
|
||||
padding: 0 12px;
|
||||
margin-bottom: 6px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
"
|
||||
>
|
||||
通用
|
||||
</NText>
|
||||
<NFlex vertical :gap="2">
|
||||
<NFlex
|
||||
v-for="item in generalItems"
|
||||
:key="item.key"
|
||||
align="center"
|
||||
:gap="10"
|
||||
style="
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
"
|
||||
:style="activePanel === item.key ? 'background: var(--n-color-embedded);' : ''"
|
||||
@click="activePanel = item.key"
|
||||
>
|
||||
<span style="font-size: 15px; line-height: 1">{{ item.icon }}</span>
|
||||
<NText :style="activePanel === item.key ? 'font-weight: 500;' : ''">
|
||||
{{ item.label }}
|
||||
</NText>
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
</NLayoutSider>
|
||||
|
||||
<NLayoutContent :native-scrollbar="false" content-style="padding: 32px 40px;">
|
||||
<component :is="panelMap[activePanel]" />
|
||||
</NLayoutContent>
|
||||
</NLayout>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<script setup lang="ts">
|
||||
import { NButton, NCard, NFlex, NTag, NText, NA } from 'naive-ui';
|
||||
import { useMessage } from 'naive-ui';
|
||||
|
||||
const message = useMessage();
|
||||
|
||||
function handleAdd() {
|
||||
message.info('新增 API 扩展');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NFlex vertical :gap="20">
|
||||
<NText tag="h2" style="font-size: 20px; font-weight: 600; margin: 0">API 扩展</NText>
|
||||
|
||||
<NCard :bordered="true" style="max-width: 760px">
|
||||
<NFlex :gap="16" align="flex-start">
|
||||
<NFlex
|
||||
align="center"
|
||||
justify="center"
|
||||
style="
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border-radius: 12px;
|
||||
background: var(--n-color-embedded);
|
||||
flex-shrink: 0;
|
||||
"
|
||||
>
|
||||
<span style="font-size: 24px">📤</span>
|
||||
</NFlex>
|
||||
<NFlex vertical :gap="6">
|
||||
<NText style="font-size: 14px; line-height: 1.6">
|
||||
API 扩展提供了一个集中式的 API 管理,在此统一添加 API 配置后,方便在 Dify
|
||||
上的各类应用中直接使用。
|
||||
</NText>
|
||||
<NA href="#" style="font-size: 13px"> 了解如何开发您自己的 API 扩展。↗ </NA>
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
</NCard>
|
||||
|
||||
<NButton
|
||||
secondary
|
||||
style="max-width: 760px; width: 100%; justify-content: center"
|
||||
@click="handleAdd"
|
||||
>
|
||||
+ 新增 API 扩展
|
||||
</NButton>
|
||||
</NFlex>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { NFlex, NText, NCard, NButton, NIcon, NSpin } from 'naive-ui';
|
||||
import { ChevronDownOutline, ChevronForwardOutline } from '@vicons/ionicons5';
|
||||
|
||||
const installExpanded = ref(true);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NFlex vertical :gap="24">
|
||||
<NText tag="h2" style="font-size: 20px; font-weight: 600; margin: 0">数据来源</NText>
|
||||
|
||||
<!-- Google Drive Card -->
|
||||
<NCard :bordered="true" size="small" style="border-radius: 10px">
|
||||
<!-- 顶部:logo + 名称 + 副标题 + 配置按钮 -->
|
||||
<NFlex align="center" justify="space-between">
|
||||
<NFlex align="center" :gap="12">
|
||||
<!-- Google Drive logo -->
|
||||
<div
|
||||
style="
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 87.3 78"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style="width: 36px; height: 32px"
|
||||
>
|
||||
<path
|
||||
d="m6.6 66.85 3.85 6.65c.8 1.4 1.95 2.5 3.3 3.3l13.75-23.8h-27.5c0 1.55.4 3.1 1.2 4.5z"
|
||||
fill="#0066da"
|
||||
/>
|
||||
<path
|
||||
d="m43.65 25-13.75-23.8c-1.35.8-2.5 1.9-3.3 3.3l-25.4 44a9.06 9.06 0 0 0 -1.2 4.5h27.5z"
|
||||
fill="#00ac47"
|
||||
/>
|
||||
<path
|
||||
d="m73.55 76.8c1.35-.8 2.5-1.9 3.3-3.3l1.6-2.75 7.65-13.25c.8-1.4 1.2-2.95 1.2-4.5h-27.502l5.852 11.5z"
|
||||
fill="#ea4335"
|
||||
/>
|
||||
<path
|
||||
d="m43.65 25 13.75-23.8c-1.35-.8-2.9-1.2-4.5-1.2h-18.5c-1.6 0-3.15.45-4.5 1.2z"
|
||||
fill="#00832d"
|
||||
/>
|
||||
<path
|
||||
d="m59.8 53h-32.3l-13.75 23.8c1.35.8 2.9 1.2 4.5 1.2h50.8c1.6 0 3.15-.45 4.5-1.2z"
|
||||
fill="#2684fc"
|
||||
/>
|
||||
<path
|
||||
d="m73.4 26.5-12.7-22c-.8-1.4-1.95-2.5-3.3-3.3l-13.75 23.8 16.15 28h27.45c0-1.55-.4-3.1-1.2-4.5z"
|
||||
fill="#ffba00"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<NFlex vertical :gap="3">
|
||||
<NText style="font-size: 14px; font-weight: 600; line-height: 1.4">Google Drive</NText>
|
||||
<NText depth="3" style="font-size: 12px">langgenius/google_drive</NText>
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
<NButton size="small" secondary>+ 配置</NButton>
|
||||
</NFlex>
|
||||
|
||||
<!-- 已连接的工作区 sub-section -->
|
||||
<div style="margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--n-border-color)">
|
||||
<NText depth="3" style="font-size: 12px; display: block; margin-bottom: 10px"
|
||||
>已连接的工作区</NText
|
||||
>
|
||||
<NText depth="3" style="font-size: 13px">请配置凭据</NText>
|
||||
</div>
|
||||
</NCard>
|
||||
|
||||
<!-- 安装数据源供应商 -->
|
||||
<div>
|
||||
<NFlex
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style="cursor: pointer; user-select: none; padding: 4px 0"
|
||||
@click="installExpanded = !installExpanded"
|
||||
>
|
||||
<NFlex align="center" :gap="6">
|
||||
<NIcon
|
||||
:component="installExpanded ? ChevronDownOutline : ChevronForwardOutline"
|
||||
size="16"
|
||||
style="color: #6b7280"
|
||||
/>
|
||||
<NText style="font-size: 14px; font-weight: 500">安装数据源供应商</NText>
|
||||
</NFlex>
|
||||
<a
|
||||
href="javascript:void(0)"
|
||||
style="font-size: 13px; color: #3b82f6; text-decoration: none"
|
||||
@click.stop
|
||||
>
|
||||
发现更多就在 Dify 市场 ↗
|
||||
</a>
|
||||
</NFlex>
|
||||
<div v-if="installExpanded" style="padding: 20px 0; text-align: center">
|
||||
<NSpin size="small" />
|
||||
</div>
|
||||
</div>
|
||||
</NFlex>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { NFlex, NText, NSelect } from 'naive-ui';
|
||||
|
||||
const language = ref('zh-CN');
|
||||
const langOptions = [
|
||||
{ label: '简体中文', value: 'zh-CN' },
|
||||
{ label: 'English', value: 'en-US' },
|
||||
{ label: '日本語', value: 'ja-JP' },
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NFlex vertical :gap="20">
|
||||
<NText tag="h2" style="font-size: 20px; font-weight: 600; margin: 0">语言</NText>
|
||||
<NFlex align="center" :gap="16">
|
||||
<NText style="font-size: 14px; white-space: nowrap">界面语言</NText>
|
||||
<NSelect v-model:value="language" :options="langOptions" style="width: 200px" />
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<script setup lang="ts">
|
||||
import { NFlex, NText, NButton, NDataTable, NAvatar } from 'naive-ui';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import type { DataTableColumns } from 'naive-ui';
|
||||
import { h, ref } from 'vue';
|
||||
|
||||
const message = useMessage();
|
||||
|
||||
interface Member {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
const data = ref<Member[]>([
|
||||
{ id: 1, name: 'godboy', email: 'godboy@example.com', role: '管理员' },
|
||||
]);
|
||||
|
||||
const columns: DataTableColumns<Member> = [
|
||||
{ title: '姓名', key: 'name' },
|
||||
{ title: '邮箱', key: 'email' },
|
||||
{ title: '角色', key: 'role' },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
render: (row) =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
text: true,
|
||||
type: 'error',
|
||||
onClick: () => message.info(`移除 ${row.name}`),
|
||||
},
|
||||
{ default: () => '移除' },
|
||||
),
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NFlex vertical :gap="20">
|
||||
<NFlex align="center" justify="space-between">
|
||||
<NText tag="h2" style="font-size: 20px; font-weight: 600; margin: 0">成员</NText>
|
||||
<NButton type="primary" size="small" @click="message.info('邀请成员')">+ 邀请成员</NButton>
|
||||
</NFlex>
|
||||
<NDataTable :columns="columns" :data="data" :bordered="false" />
|
||||
</NFlex>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import {
|
||||
NFlex,
|
||||
NText,
|
||||
NCard,
|
||||
NButton,
|
||||
NInput,
|
||||
NIcon,
|
||||
NAvatar,
|
||||
NCollapse,
|
||||
NCollapseItem,
|
||||
NDivider,
|
||||
} from 'naive-ui';
|
||||
import { SearchOutline, SettingsOutline } from '@vicons/ionicons5';
|
||||
import { useVbenForm, useVbenModal } from '@vben/common-ui';
|
||||
import { ModelCertificateFrom } from './schema';
|
||||
|
||||
const searchText = ref('');
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
title: '编辑模型凭据',
|
||||
});
|
||||
const manageModels = (id: string) => {
|
||||
modalApi.open();
|
||||
};
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
showDefaultActions: false,
|
||||
...ModelCertificateFrom,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NFlex vertical :gap="24">
|
||||
<!-- 页面标题 + 搜索 -->
|
||||
<NFlex align="center" justify="space-between">
|
||||
<NText tag="h2" style="font-size: 20px; font-weight: 600; margin: 0">模型供应商</NText>
|
||||
<NInput
|
||||
v-model:value="searchText"
|
||||
placeholder="搜索"
|
||||
size="small"
|
||||
style="width: 200px"
|
||||
clearable
|
||||
round
|
||||
>
|
||||
<template #prefix>
|
||||
<NIcon :component="SearchOutline" />
|
||||
</template>
|
||||
</NInput>
|
||||
</NFlex>
|
||||
|
||||
<!-- 模型列表 -->
|
||||
<div>
|
||||
<NFlex align="center" justify="space-between" style="margin-bottom: 12px">
|
||||
<NText style="font-size: 14px; font-weight: 500">模型列表</NText>
|
||||
<NButton text size="small" type="primary">
|
||||
<template #icon>
|
||||
<NIcon :component="SettingsOutline" />
|
||||
</template>
|
||||
默认模型设置
|
||||
</NButton>
|
||||
</NFlex>
|
||||
|
||||
<!-- Ollama Card (已配置) -->
|
||||
<NFlex>
|
||||
<NCard :bordered="true" size="small" style="border-radius: 10px">
|
||||
<NFlex justify="space-between" align="center">
|
||||
<NFlex align="center" :gap="12">
|
||||
<!-- Ollama Logo -->
|
||||
<NAvatar
|
||||
style="width: 40px; height: 40px"
|
||||
size="small"
|
||||
src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/avatar-v1.webp"
|
||||
/>
|
||||
<NText style="font-size: 14px; font-weight: 600; line-height: 1">Ollama</NText>
|
||||
</NFlex>
|
||||
<NFlex :gap="8">
|
||||
<NButton size="small" secondary @click="manageModels('ollama')">管理凭据</NButton>
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
<NDivider />
|
||||
<NCollapse>
|
||||
<NCollapseItem>
|
||||
<template #header>
|
||||
<n-text type="primary"> 已连接的模型 </n-text>
|
||||
</template>
|
||||
</NCollapseItem>
|
||||
</NCollapse>
|
||||
</NCard>
|
||||
</NFlex>
|
||||
</div>
|
||||
</NFlex>
|
||||
<Modal>
|
||||
<Form />
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.n-divider:not(.n-divider--vertical) {
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
import type { VbenFormProps } from "@vben/common-ui";
|
||||
|
||||
export const ModelCertificateFrom: VbenFormProps = {
|
||||
schema: [
|
||||
{
|
||||
fieldName: 'CertificateType',
|
||||
label: '凭证类型',
|
||||
component: "Select",
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: 'API密钥', value: 'apiKey' },
|
||||
{ label: '其他', value: 'other' },
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: "modelName",
|
||||
label: "模型名称",
|
||||
component: "Input",
|
||||
rules: "required",
|
||||
dependencies: {
|
||||
triggerFields: ["CertificateType"],
|
||||
show: (values) => values.CertificateType === 'other',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: "modelType",
|
||||
label: "模型类型",
|
||||
component: "Select",
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: 'LLM', value: 'llm' },
|
||||
{ label: 'Text Embedding', value: 'textEmbedding' },
|
||||
{ label: 'Rerank', value: 'rerank' },
|
||||
]
|
||||
},
|
||||
dependencies: {
|
||||
triggerFields: ["CertificateType"],
|
||||
show: (values) => values.CertificateType === 'other',
|
||||
},
|
||||
}
|
||||
]
|
||||
};
|
||||
Loading…
Reference in New Issue