feat: i18n

This commit is contained in:
王丹 2025-01-16 18:04:58 +08:00
parent 7eb17bab2f
commit 8fcf935201
15 changed files with 136 additions and 62 deletions

View File

@ -0,0 +1,13 @@
export default {
noHistory: '暂无历史记录',
createChat: '新建对话',
history: '历史记录',
only20history: '仅显示最近 20 条对话',
question_count: '条提问',
exportReords: '导出聊天记录',
passwordValidator: {
title: '请输入密码打开链接',
errorMessage1: '密码不能为空',
errorMessage2: '密码错误'
}
}

View File

@ -1,4 +1,6 @@
import dynamicsForm from './dynamics-form'
import chat from './ai-chat'
export default {
dynamicsForm
dynamicsForm,
chat
}

View File

@ -1,3 +1,35 @@
export default {
node: '节点',
baseNodes: '基础组件',
searchBar: {
placeholder: '按名称搜索'
},
info: {
previewVersion: '预览版本:',
saveTime: '保存时间:'
},
setting: {
restoreVersion: '恢复版本',
restoreCurrentVersion: '恢复此版本',
addComponent: '添加组件',
public: '发布',
releaseHistory: '发布历史',
autoSave: '自动保存',
latestRelease: '最近发布'
},
tip: {
publicSuccess: '发布成功',
noData: '没有找到相关结果',
nameMessage: '名字不能为空!',
onlyRight: '只允许从右边的锚点连出',
notRecyclable: '不可循环连线',
onlylest: '只允许连接左边的锚点'
},
variable: {
global: '全局变量',
Referencing: '引用变量',
ReferencingRequired: '引用变量必填',
ReferencingError: '引用变量错误',
NoReferencing:'不存在的引用变量',
}
}

View File

@ -1,3 +1,6 @@
import Password from '@/views/chat/auth/component/password.vue'
import { create } from 'lodash'
export default {
title: '应用',
createApplication: '创建应用',
@ -195,5 +198,6 @@ export default {
text: '针对用户提问调试段落匹配情况,保障回答效果。',
emptyMessage1: '命中段落显示在这里',
emptyMessage2: '没有命中的分段'
}
},
}

View File

@ -11,6 +11,7 @@ import document from './document';
import paragraph from './paragraph';
import problem from './problem';
import log from './log';
import applicationWorkflow from './application-workflow';
export default {
notFound,
application,
@ -24,5 +25,6 @@ export default {
document,
paragraph,
problem,
log
log,
applicationWorkflow
};

View File

@ -2,14 +2,18 @@
<div v-show="show" class="workflow-dropdown-menu border border-r-4">
<el-tabs v-model="activeName" class="workflow-dropdown-tabs">
<div style="display: flex; width: 100%; justify-content: center" class="mb-4">
<el-input v-model="search_text" style="width: 240px" placeholder="按名称搜索">
<el-input
v-model="search_text"
style="width: 240px"
:placeholder="$t('views.applicationWorkflow.searchBar.placeholder')"
>
<template #suffix>
<el-icon class="el-input__icon"><search /></el-icon>
</template>
</el-input>
</div>
<el-tab-pane label="基础组件" name="base">
<el-tab-pane :label="$t('views.applicationWorkflow.baseNodes')" name="base">
<el-scrollbar height="400">
<div v-if="filter_menu_nodes.length > 0">
<template v-for="(item, index) in filter_menu_nodes" :key="index">
@ -27,11 +31,11 @@
</template>
</div>
<div v-else class="ml-16 mt-8">
<el-text type="info">没有找到相关结果</el-text>
<el-text type="info">{{ $t('views.applicationWorkflow.tip.noData') }}</el-text>
</div>
</el-scrollbar>
</el-tab-pane>
<el-tab-pane label="函数库" name="function">
<el-tab-pane :label="$t('views.functionLib.title')" name="function">
<el-scrollbar height="400">
<div
class="workflow-dropdown-item cursor flex p-8-12"
@ -69,7 +73,7 @@
</template>
</el-scrollbar>
</el-tab-pane>
<el-tab-pane label="应用" name="application">
<el-tab-pane :label="$t('views.application.title')" name="application">
<el-scrollbar height="400">
<div v-if="filter_application_list.length > 0">
<template v-for="(item, index) in filter_application_list" :key="index">
@ -100,16 +104,18 @@
</p>
</div>
<div class="status-tag" style="margin-left: auto">
<el-tag type="warning" v-if="isWorkFlow(item.type)" style="height: 22px"
>高级编排</el-tag
<el-tag type="warning" v-if="isWorkFlow(item.type)" style="height: 22px">
{{ $t('views.application.workflow') }}</el-tag
>
<el-tag class="blue-tag" v-else style="height: 22px">简单配置</el-tag>
<el-tag class="blue-tag" v-else style="height: 22px">{{
$t('views.application.simple')
}}</el-tag>
</div>
</div>
</template>
</div>
<div v-else class="ml-16 mt-8">
<el-text type="info">没有找到相关结果</el-text>
<el-text type="info">{{ $t('views.applicationWorkflow.tip.noData') }}</el-text>
</div>
</el-scrollbar>
</el-tab-pane>

View File

@ -1,6 +1,6 @@
<template>
<div class="workflow-publish-history border-l">
<h4 class="border-b p-16-24">发布历史</h4>
<h4 class="border-b p-16-24">{{ $t('views.applicationWorkflow.setting.releaseHistory') }}</h4>
<div class="list-height pt-0">
<el-scrollbar>
<div class="p-8 pt-0">
@ -23,7 +23,9 @@
:write="row.writeStatus"
@close="closeWrite(row)"
/>
<el-tag v-if="index === 0" class="default-tag ml-4">最近发布</el-tag>
<el-tag v-if="index === 0" class="default-tag ml-4">{{
$t('views.applicationWorkflow.setting.latestRelease')
}}</el-tag>
</h5>
<el-text type="info" class="color-secondary flex mt-8">
<AppAvatar :size="20" class="avatar-grey mr-4">
@ -46,7 +48,7 @@
</el-dropdown-item>
<el-dropdown-item @click="refreshVersion(row)">
<el-icon><RefreshLeft /></el-icon>
恢复此版本
{{ $t('views.applicationWorkflow.setting.restoreCurrentVersion') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
@ -57,7 +59,7 @@
<template #empty>
<div class="text-center">
<el-text type="info">暂无历史记录</el-text>
<el-text type="info"> {{ $t('components.chat.noHistory') }}</el-text>
</div>
</template>
</common-list>
@ -72,6 +74,7 @@ import { useRoute } from 'vue-router'
import applicationApi from '@/api/application'
import { datetimeFormat } from '@/utils/time'
import { MsgSuccess, MsgError } from '@/utils/message'
import { t } from '@/locales'
const route = useRoute()
const {
params: { id }
@ -114,7 +117,7 @@ function editName(val: string, item: any) {
getList()
})
} else {
MsgError('名字不能为空!')
MsgError(t('views.applicationWorkflow.tip.nameMessage'))
}
}

View File

@ -8,23 +8,26 @@
<h4>{{ detail?.name }}</h4>
<div v-if="showHistory && disablePublic">
<el-text type="info" class="ml-16 color-secondary"
>预览版本
>{{ $t('views.applicationWorkflow.info.previewVersion') }}
{{ currentVersion.name || datetimeFormat(currentVersion.update_time) }}</el-text
>
</div>
<el-text type="info" class="ml-16 color-secondary" v-else-if="saveTime"
>保存时间{{ datetimeFormat(saveTime) }}</el-text
>{{ $t('views.applicationWorkflow.info.saveTime')
}}{{ datetimeFormat(saveTime) }}</el-text
>
</div>
<div v-if="showHistory && disablePublic">
<el-button type="primary" class="mr-8" @click="refreshVersion()"> 恢复版本 </el-button>
<el-button type="primary" class="mr-8" @click="refreshVersion()">
{{ $t('views.applicationWorkflow.setting.restoreVersion') }}
</el-button>
<el-divider direction="vertical" />
<el-button text @click="closeHistory">
<el-icon><Close /></el-icon>
</el-button>
</div>
<div v-else>
<el-button icon="Plus" @click="showPopover = !showPopover"> 添加组件 </el-button>
<el-button icon="Plus" @click="showPopover = !showPopover"> {{ $t('views.applicationWorkflow.setting.addComponent') }} </el-button>
<el-button @click="clickShowDebug" :disabled="showDebug">
<AppIcon iconName="app-play-outlined" class="mr-4"></AppIcon>
{{ $t('common.debug') }}</el-button
@ -33,7 +36,7 @@
<AppIcon iconName="app-save-outlined" class="mr-4"></AppIcon>
{{ $t('common.save') }}
</el-button>
<el-button type="primary" @click="publicHandle"> 发布 </el-button>
<el-button type="primary" @click="publicHandle"> {{ $t('views.applicationWorkflow.setting.public') }} </el-button>
<el-dropdown trigger="click">
<el-button text @click.stop class="ml-8 mt-4">
@ -43,11 +46,11 @@
<el-dropdown-menu>
<el-dropdown-item @click="openHistory">
<AppIcon iconName="app-history-outlined"></AppIcon>
发布历史
{{ $t('views.applicationWorkflow.setting.releaseHistory') }}
</el-dropdown-item>
<el-dropdown-item>
<AppIcon iconName="app-save-outlined"></AppIcon>
自动保存
{{ $t('views.applicationWorkflow.setting.autoSave') }}
<div class="ml-4">
<el-switch size="small" v-model="isSave" @change="changeSave" />
</div>
@ -146,7 +149,7 @@ import { datetimeFormat } from '@/utils/time'
import useStore from '@/stores'
import { WorkFlowInstance } from '@/workflow/common/validate'
import { hasPermission } from '@/utils/permission'
import { t } from '@/locales'
const { user, application } = useStore()
const router = useRouter()
const route = useRoute()
@ -258,17 +261,17 @@ async function publicHandle() {
return
}
applicationApi.putPublishApplication(id as String, obj, loading).then(() => {
MsgSuccess('发布成功')
MsgSuccess(t('views.applicationWorkflow.tip.publicSuccess'))
})
})
.catch((res: any) => {
const node = res.node
const err_message = res.errMessage
if (typeof err_message == 'string') {
MsgError(res.node.properties?.stepName + '节点 ' + err_message)
MsgError(res.node.properties?.stepName + `${t('views.applicationWorkflow.node')} ` + err_message)
} else {
const keys = Object.keys(err_message)
MsgError(node.properties?.stepName + '节点 ' + err_message[keys[0]]?.[0]?.message)
MsgError(node.properties?.stepName + `${t('views.applicationWorkflow.node')} ` + err_message[keys[0]]?.[0]?.message)
}
})
}
@ -297,10 +300,10 @@ const clickShowDebug = () => {
const node = res.node
const err_message = res.errMessage
if (typeof err_message == 'string') {
MsgError(res.node.properties?.stepName + '节点 ' + err_message)
MsgError(res.node.properties?.stepName + `${t('views.applicationWorkflow.node')} ` + err_message)
} else {
const keys = Object.keys(err_message)
MsgError(node.properties?.stepName + '节点 ' + err_message[keys[0]]?.[0]?.message)
MsgError(node.properties?.stepName + `${t('views.applicationWorkflow.node')} ` + err_message[keys[0]]?.[0]?.message)
}
})
}

View File

@ -3,7 +3,7 @@
:modelValue="show"
modal-class="positioned-mask"
width="300"
title="请输入密码打开链接"
:title="$t('components.chat.passwordValidator.title')"
custom-class="no-close-button"
:close-on-click-modal="false"
:close-on-press-escape="false"
@ -26,6 +26,7 @@
import { ref, computed } from 'vue'
import { useRoute } from 'vue-router'
import useStore from '@/stores'
import { t } from '@/locales'
const route = useRoute()
const FormRef = ref()
const {
@ -51,10 +52,10 @@ const auth = () => {
}
const validator_auth = (rule: any, value: string, callback: any) => {
if (value === '') {
callback(new Error('密码不能为空'))
callback(new Error(t('components.chat.passwordValidator.errorMessage1')))
} else {
auth().catch(() => {
callback(new Error('密码错误'))
callback(new Error(t('components.chat.passwordValidator.errorMessage2')))
})
}
}

View File

@ -47,7 +47,8 @@
<template #operateBefore>
<div class="chat-width">
<el-button type="primary" link class="new-chat-button mb-8" @click="newChat">
<el-icon><Plus /></el-icon><span class="ml-4"></span>
<el-icon><Plus /></el-icon
><span class="ml-4">{{ $t('components.chat.createChat') }}</span>
</el-button>
</div>
</template>
@ -71,7 +72,7 @@
<el-collapse-transition>
<div v-show="show" class="chat-popover w-full" v-click-outside="clickoutside">
<div class="border-b p-16-24">
<span>历史记录</span>
<span>{{ $t('components.chat.history') }}</span>
</div>
<el-scrollbar max-height="300">
@ -99,13 +100,13 @@
</template>
<template #empty>
<div class="text-center mt-24">
<el-text type="info">暂无历史记录</el-text>
<el-text type="info">{{ $t('components.chat.noHistory') }}</el-text>
</div>
</template>
</common-list>
</div>
<div v-if="chatLogData.length" class="gradient-divider lighter mt-8">
<span>仅显示最近 20 条对话</span>
<span>{{ $t('components.chat.only20history') }}</span>
</div>
</el-scrollbar>
</div>
@ -119,7 +120,6 @@ import { ref, onMounted, reactive, nextTick, computed } from 'vue'
import { isAppIcon } from '@/utils/application'
import { hexToRgba } from '@/utils/theme'
import useStore from '@/stores'
const { user, log } = useStore()
const AiChatRef = ref()

View File

@ -38,9 +38,9 @@
<el-icon>
<Plus />
</el-icon>
<span class="ml-4">新建对话</span>
<span class="ml-4">{{ $t('components.chat.createChat') }}</span>
</el-button>
<p class="mt-20 mb-8">历史记录</p>
<p class="mt-20 mb-8">{{ $t('components.chat.history') }}</p>
</div>
<div class="left-height pt-0">
<el-scrollbar>
@ -76,13 +76,13 @@
<template #empty>
<div class="text-center">
<el-text type="info">暂无历史记录</el-text>
<el-text type="info">{{ $t('components.chat.noHistory') }}</el-text>
</div>
</template>
</common-list>
</div>
<div v-if="chatLogData?.length" class="gradient-divider lighter mt-8">
<span>仅显示最近 20 条对话</span>
<span>{{ $t('components.chat.only20history') }}</span>
</div>
</el-scrollbar>
</div>
@ -101,14 +101,18 @@
style="font-size: 16px"
></AppIcon>
<span v-if="paginationConfig.total" class="lighter">
{{ paginationConfig.total }} 条提问
{{ paginationConfig.total }} {{ $t('components.chat.question_count') }}
</span>
<el-dropdown class="ml-8">
<AppIcon iconName="app-export" class="cursor" title="导出聊天记录"></AppIcon>
<AppIcon
iconName="app-export"
class="cursor"
:title="$t('components.chat.exportReords')"
></AppIcon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="exportMarkdown">导出 Markdown</el-dropdown-item>
<el-dropdown-item @click="exportHTML">导出 HTML</el-dropdown-item>
<el-dropdown-item @click="exportMarkdown">{{ $t('common.exprt') }} Markdown</el-dropdown-item>
<el-dropdown-item @click="exportHTML">{{ $t('common.exprt') }} HTML</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@ -147,7 +151,7 @@ import { isAppIcon } from '@/utils/application'
import useStore from '@/stores'
import useResize from '@/layout/hooks/useResize'
import { hexToRgba } from '@/utils/theme'
import { t } from '@/locales'
useResize()
const { user, log, common } = useStore()
@ -175,7 +179,7 @@ const classObj = computed(() => {
const newObj = {
id: 'new',
abstract: '新建对话'
abstract: t('components.chat.createChat')
}
const props = defineProps<{
application_profile: any
@ -202,7 +206,7 @@ const paginationConfig = ref({
const currentRecordList = ref<any>([])
const currentChatId = ref('new') // Id 'new'
const currentChatName = ref('新建对话')
const currentChatName = ref(t('components.chat.createChat'))
const mouseId = ref('')
function mouseenter(row: any) {
@ -212,7 +216,7 @@ function deleteLog(row: any) {
log.asyncDelChatClientLog(applicationDetail.value.id, row.id, left_loading).then(() => {
if (currentChatId.value === row.id) {
currentChatId.value = 'new'
currentChatName.value = '新建对话'
currentChatName.value = t('components.chat.createChat')
paginationConfig.value.current_page = 1
paginationConfig.value.total = 0
currentRecordList.value = []
@ -247,7 +251,7 @@ function newChat() {
currentRecordList.value = []
}
currentChatId.value = 'new'
currentChatName.value = '新建对话'
currentChatName.value = t('components.chat.createChat')
if (common.isMobile()) {
isCollapse.value = false
}

View File

@ -25,9 +25,10 @@
<script setup lang="ts">
import { ref } from 'vue'
import DynamicsFormConstructor from '@/components/dynamics-form/constructor/index.vue'
import { t } from '@/locales'
const props = withDefaults(
defineProps<{ title?: string; addFormField: (form_data: any) => void }>(),
{ title: '添加参数' }
{ title: t('views.template.templateForm.title.addParam') }
)
const dialogVisible = ref<boolean>(false)
const dynamicsFormConstructorRef = ref<InstanceType<typeof DynamicsFormConstructor>>()

View File

@ -27,9 +27,10 @@
<script setup lang="ts">
import { ref } from 'vue'
import DynamicsFormConstructor from '@/components/dynamics-form/constructor/index.vue'
import { t } from '@/locales'
const props = withDefaults(
defineProps<{ title?: string; editFormField: (form_data: any, index: number) => void }>(),
{ title: '修改参数' }
{ title: t('views.template.templateForm.title.editParam') }
)
const dialogVisible = ref<boolean>(false)
const dynamicsFormConstructorRef = ref<InstanceType<typeof DynamicsFormConstructor>>()

View File

@ -21,6 +21,7 @@
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { iconComponent } from '../icons/utils'
import { t } from '@/locales'
const props = defineProps<{
nodeModel: any
modelValue: Array<any>
@ -69,7 +70,7 @@ function _getIncomingNode(id: String, startId: String, value: Array<any>) {
if (item.properties?.globalFields && item.type === 'start-node') {
firstElement = {
value: 'global',
label: '全局变量',
label: t('views.applicationWorkflow.variable.global'),
type: 'global',
children: item.properties?.config?.globalFields || []
}
@ -94,21 +95,21 @@ const validate = () => {
const incomingNodeValue = getIncomingNode(props.nodeModel.id)
options.value = incomingNodeValue
if (!data.value || data.value.length === 0) {
return Promise.reject('引用变量必填')
return Promise.reject(t('views.applicationWorkflow.variable.ReferencingRequired'))
}
if (data.value.length < 2) {
return Promise.reject('引用变量错误')
return Promise.reject(t('views.applicationWorkflow.variable.ReferencingError'))
}
const node_id = data.value[0]
const node_field = data.value[1]
const nodeParent = incomingNodeValue.find((item: any) => item.value === node_id)
if (!nodeParent) {
data.value = []
return Promise.reject('不存在的引用变量')
return Promise.reject(t('views.applicationWorkflow.variable.NoReferencing'))
}
if (!nodeParent.children.some((item: any) => item.value === node_field)) {
data.value = []
return Promise.reject('不存在的引用变量')
return Promise.reject(t('views.applicationWorkflow.variable.NoReferencing'))
}
return Promise.resolve('')
}

View File

@ -276,6 +276,7 @@ class AppNodeModel extends HtmlResize.model {
}
setAttributes() {
const { t } = i18n.global;
this.width = this.get_width()
const isLoop = (node_id: string, target_node_id: string) => {
const up_node_list = this.graphModel.getNodeIncomingNode(node_id)
@ -293,13 +294,13 @@ class AppNodeModel extends HtmlResize.model {
return false
}
const circleOnlyAsTarget = {
message: '只允许从右边的锚点连出',
message: t('views.applicationWorkflow.tip.onlyRight'),
validate: (sourceNode: any, targetNode: any, sourceAnchor: any) => {
return sourceAnchor.type === 'right'
}
}
this.sourceRules.push({
message: '不可循环连线',
message: t('views.applicationWorkflow.tip.notRecyclable'),
validate: (sourceNode: any, targetNode: any, sourceAnchor: any, targetAnchor: any) => {
return !isLoop(sourceNode.id, targetNode.id)
}
@ -307,7 +308,7 @@ class AppNodeModel extends HtmlResize.model {
this.sourceRules.push(circleOnlyAsTarget)
this.targetRules.push({
message: '只允许连接左边的锚点',
message: t('views.applicationWorkflow.tip.onlylest'),
validate: (sourceNode: any, targetNode: any, sourceAnchor: any, targetAnchor: any) => {
return targetAnchor.type === 'left'
}