refactor(personalization): 重新归类个人空间选项所属页签
- 数据管理页签删除,用量统计并入常规页 - 审核主动取证/续命轮数/token上限移至审核智能体页 - 使用自定义称呼移至个性化页,标题生成模型移至模型页 - 沙箱环境移至工作区与权限页 - 删除个性化页重复渲染的边线/高度开关 - 拆分撞车文案:默认思考模式(模型页)/默认工作模式(工作区页) - 教程用量步骤锚点改指常规页
This commit is contained in:
parent
deaa1560a5
commit
8da53635c4
@ -131,8 +131,6 @@
|
|||||||
|
|
||||||
<FilesTab v-else-if="activeTab === 'files'" key="files" />
|
<FilesTab v-else-if="activeTab === 'files'" key="files" />
|
||||||
|
|
||||||
<DataTab v-else-if="activeTab === 'data'" key="data" />
|
|
||||||
|
|
||||||
<VoiceTab v-else-if="activeTab === 'voice'" key="voice" />
|
<VoiceTab v-else-if="activeTab === 'voice'" key="voice" />
|
||||||
|
|
||||||
<SubAgentsTab v-else-if="activeTab === 'sub-agents'" key="sub-agents" />
|
<SubAgentsTab v-else-if="activeTab === 'sub-agents'" key="sub-agents" />
|
||||||
@ -174,7 +172,6 @@ import WorkspaceTab from './tabs/WorkspaceTab.vue';
|
|||||||
import ContextTab from './tabs/ContextTab.vue';
|
import ContextTab from './tabs/ContextTab.vue';
|
||||||
import ToolsTab from './tabs/ToolsTab.vue';
|
import ToolsTab from './tabs/ToolsTab.vue';
|
||||||
import FilesTab from './tabs/FilesTab.vue';
|
import FilesTab from './tabs/FilesTab.vue';
|
||||||
import DataTab from './tabs/DataTab.vue';
|
|
||||||
import VoiceTab from './tabs/VoiceTab.vue';
|
import VoiceTab from './tabs/VoiceTab.vue';
|
||||||
import SubAgentsTab from './tabs/SubAgentsTab.vue';
|
import SubAgentsTab from './tabs/SubAgentsTab.vue';
|
||||||
import ReviewAgentsTab from './tabs/ReviewAgentsTab.vue';
|
import ReviewAgentsTab from './tabs/ReviewAgentsTab.vue';
|
||||||
@ -251,7 +248,6 @@ type PersonalTab =
|
|||||||
| 'context'
|
| 'context'
|
||||||
| 'tools'
|
| 'tools'
|
||||||
| 'files'
|
| 'files'
|
||||||
| 'data'
|
|
||||||
| 'voice'
|
| 'voice'
|
||||||
| 'sub-agents'
|
| 'sub-agents'
|
||||||
| 'review-agents'
|
| 'review-agents'
|
||||||
@ -266,7 +262,6 @@ const baseTabs = [
|
|||||||
{ id: 'context', labelKey: 'personalization.tabContext', icon: 'chatBubble' },
|
{ id: 'context', labelKey: 'personalization.tabContext', icon: 'chatBubble' },
|
||||||
{ id: 'tools', labelKey: 'personalization.tabTools', icon: 'wrench' },
|
{ id: 'tools', labelKey: 'personalization.tabTools', icon: 'wrench' },
|
||||||
{ id: 'files', labelKey: 'personalization.tabFiles', icon: 'file' },
|
{ id: 'files', labelKey: 'personalization.tabFiles', icon: 'file' },
|
||||||
{ id: 'data', labelKey: 'personalization.tabData', icon: 'layers' },
|
|
||||||
{ id: 'voice', labelKey: 'personalization.tabVoice', icon: 'mic' },
|
{ id: 'voice', labelKey: 'personalization.tabVoice', icon: 'mic' },
|
||||||
{ id: 'sub-agents', labelKey: 'personalization.tabSubAgents', icon: 'bot' },
|
{ id: 'sub-agents', labelKey: 'personalization.tabSubAgents', icon: 'bot' },
|
||||||
{ id: 'review-agents', labelKey: 'personalization.tabReviewAgents', icon: 'checkbox' }
|
{ id: 'review-agents', labelKey: 'personalization.tabReviewAgents', icon: 'checkbox' }
|
||||||
@ -863,9 +858,6 @@ onMounted(async () => {
|
|||||||
watch(
|
watch(
|
||||||
() => [activeTab.value, visible.value],
|
() => [activeTab.value, visible.value],
|
||||||
([tab, isVisible]) => {
|
([tab, isVisible]) => {
|
||||||
if (isVisible && tab === 'data') {
|
|
||||||
fetchUsageSummary();
|
|
||||||
}
|
|
||||||
if (isVisible && tab === 'sub-agents') {
|
if (isVisible && tab === 'sub-agents') {
|
||||||
loadSubAgentRoles();
|
loadSubAgentRoles();
|
||||||
loadSubAgentSettings();
|
loadSubAgentSettings();
|
||||||
@ -874,9 +866,13 @@ watch(
|
|||||||
if (isVisible && tab === 'review-agents') {
|
if (isVisible && tab === 'review-agents') {
|
||||||
loadSubAgentModels();
|
loadSubAgentModels();
|
||||||
}
|
}
|
||||||
if (isVisible && tab === 'general') {
|
if (isVisible && tab === 'model') {
|
||||||
// 「标题生成模型」菜单复用子智能体模型库,需在常规页签也加载模型列表
|
// 「标题生成模型」菜单复用子智能体模型库,需在模型页签加载模型列表
|
||||||
loadSubAgentModels();
|
loadSubAgentModels();
|
||||||
|
}
|
||||||
|
if (isVisible && tab === 'general') {
|
||||||
|
// 用量统计已并入常规页签,切到时拉取一次
|
||||||
|
fetchUsageSummary();
|
||||||
if (
|
if (
|
||||||
isAppShell.value &&
|
isAppShell.value &&
|
||||||
!appUpdateInfo.value &&
|
!appUpdateInfo.value &&
|
||||||
|
|||||||
@ -145,21 +145,6 @@ const {
|
|||||||
value: $event.target.checked ? 'route' : 'blank'
|
value: $event.target.checked ? 'route' : 'blank'
|
||||||
})
|
})
|
||||||
" /><FancyCheck :checked="form.new_chat_button_behavior === 'route'" /></label>
|
" /><FancyCheck :checked="form.new_chat_button_behavior === 'route'" /></label>
|
||||||
<label class="settings-toggle-row"
|
|
||||||
><span class="settings-row-copy"
|
|
||||||
><span class="settings-row-title">{{ $t('personalization.useCustomNamesTitle') }}</span
|
|
||||||
><span class="settings-row-desc"
|
|
||||||
>{{ $t('personalization.useCustomNamesDesc') }}</span
|
|
||||||
></span
|
|
||||||
><input
|
|
||||||
type="checkbox"
|
|
||||||
:checked="form.use_custom_names"
|
|
||||||
@change="
|
|
||||||
personalization.updateField({
|
|
||||||
key: 'use_custom_names',
|
|
||||||
value: $event.target.checked
|
|
||||||
})
|
|
||||||
" /><FancyCheck :checked="form.use_custom_names" /></label>
|
|
||||||
<label class="settings-toggle-row"
|
<label class="settings-toggle-row"
|
||||||
><span class="settings-row-copy"
|
><span class="settings-row-copy"
|
||||||
><span class="settings-row-title">{{ $t('personalization.enhancedToolDisplayTitle') }}</span
|
><span class="settings-row-title">{{ $t('personalization.enhancedToolDisplayTitle') }}</span
|
||||||
|
|||||||
@ -1,182 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { inject } from 'vue';
|
|
||||||
|
|
||||||
defineOptions({ name: 'DataTab' });
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 共享上下文由 PersonalizationDrawer.vue 通过 provide 注入。
|
|
||||||
* 解构出的名称与主文件 script 顶层绑定一致,模板可直接引用。
|
|
||||||
*/
|
|
||||||
const ctx = inject<Record<string, any>>('personalizationDrawer')!;
|
|
||||||
const {
|
|
||||||
personalization,
|
|
||||||
fetchUsageSummary,
|
|
||||||
formatTokenCount,
|
|
||||||
usageError,
|
|
||||||
usageLoading,
|
|
||||||
usageSummary,
|
|
||||||
usageUpdatedText
|
|
||||||
} = ctx;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<section
|
|
||||||
class="settings-page usage-summary-page"
|
|
||||||
data-tutorial="personal-page-usage"
|
|
||||||
>
|
|
||||||
<div class="usage-summary-card settings-stats-card">
|
|
||||||
<div class="usage-summary-header">
|
|
||||||
<div>
|
|
||||||
<p class="usage-summary-eyebrow">{{ $t('personalization.usageEyebrow') }}</p>
|
|
||||||
<h3>{{ $t('personalization.usageTitle') }}</h3>
|
|
||||||
<p class="usage-summary-desc">{{ $t('personalization.usageDesc') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="usage-summary-grid usage-summary-grid--tokens">
|
|
||||||
<div class="usage-summary-item">
|
|
||||||
<div class="label">{{ $t('personalization.usageInputLabel') }}</div>
|
|
||||||
<div class="value value--success">
|
|
||||||
{{ formatTokenCount(usageSummary.total_input_tokens) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="usage-summary-item">
|
|
||||||
<div class="label">{{ $t('personalization.usageOutputLabel') }}</div>
|
|
||||||
<div class="value value--warning">
|
|
||||||
{{ formatTokenCount(usageSummary.total_output_tokens) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="usage-summary-grid usage-summary-grid--counts">
|
|
||||||
<div class="usage-summary-item">
|
|
||||||
<div class="label">{{ $t('personalization.usageConversationsLabel') }}</div>
|
|
||||||
<div class="value">
|
|
||||||
{{ formatTokenCount(usageSummary.total_conversations) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="usage-summary-item">
|
|
||||||
<div class="label">{{ $t('personalization.usageMessagesLabel') }}</div>
|
|
||||||
<div class="value">
|
|
||||||
{{ formatTokenCount(usageSummary.total_user_messages) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="usage-summary-item">
|
|
||||||
<div class="label">{{ $t('personalization.usageToolsLabel') }}</div>
|
|
||||||
<div class="value">
|
|
||||||
{{ formatTokenCount(usageSummary.total_tools) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="usage-summary-meta">
|
|
||||||
<span v-if="usageError" class="usage-summary-error">{{ usageError }}</span
|
|
||||||
><span v-else-if="usageLoading">{{ $t('personalization.usageSyncing') }}</span
|
|
||||||
><span v-else>{{ $t('personalization.usageUpdated', { time: usageUpdatedText }) }}</span
|
|
||||||
><button
|
|
||||||
type="button"
|
|
||||||
class="usage-summary-refresh"
|
|
||||||
@click="fetchUsageSummary"
|
|
||||||
:disabled="usageLoading"
|
|
||||||
>
|
|
||||||
{{ usageLoading ? $t('personalization.usageRefreshing') : $t('personalization.usageRefresh') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.usage-summary-page {
|
|
||||||
display: block;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-stats-card,
|
|
||||||
.usage-summary-card {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 720px;
|
|
||||||
padding: 0;
|
|
||||||
min-height: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage-summary-header h3 {
|
|
||||||
margin: 4px 0 6px;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage-summary-eyebrow {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 11px;
|
|
||||||
color: var(--accent-strong);
|
|
||||||
letter-spacing: 0.16em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage-summary-desc,
|
|
||||||
.usage-summary-note,
|
|
||||||
.usage-summary-meta {
|
|
||||||
margin: 0;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage-summary-grid {
|
|
||||||
display: grid;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage-summary-grid--tokens {
|
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage-summary-grid--counts {
|
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage-summary-item {
|
|
||||||
padding: 12px 14px;
|
|
||||||
border-radius: 14px;
|
|
||||||
border: 1px solid var(--theme-control-border);
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage-summary-item .label {
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage-summary-item .value {
|
|
||||||
margin-top: 4px;
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage-summary-item .value--success {
|
|
||||||
color: var(--state-success);
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage-summary-item .value--warning {
|
|
||||||
color: var(--state-warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage-summary-meta {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage-summary-refresh {
|
|
||||||
border: 1px solid var(--theme-control-border);
|
|
||||||
border-radius: 999px;
|
|
||||||
padding: 7px 14px;
|
|
||||||
background: transparent;
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-size: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,41 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, inject, onMounted } from 'vue';
|
import { inject } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import FancyCheck from '@/components/common/FancyCheck.vue';
|
import FancyCheck from '@/components/common/FancyCheck.vue';
|
||||||
import { useSandboxSetupStore } from '@/stores/sandboxSetup';
|
|
||||||
|
|
||||||
defineOptions({ name: 'GeneralTab' });
|
defineOptions({ name: 'GeneralTab' });
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const sandboxSetup = useSandboxSetupStore();
|
|
||||||
|
|
||||||
// 沙箱环境区块:进入通用页时补一次检测(复用 store 内部防抖)
|
|
||||||
onMounted(() => {
|
|
||||||
if (!sandboxSetup.status) void sandboxSetup.fetchStatus();
|
|
||||||
});
|
|
||||||
|
|
||||||
const sandboxStatusText = computed(() => {
|
|
||||||
const s = sandboxSetup.status;
|
|
||||||
if (!s || sandboxSetup.checking) return t('sandbox.sectionChecking');
|
|
||||||
if (!s.applicable) return t('sandbox.sectionUnavailable');
|
|
||||||
return s.state === 'ready' ? t('sandbox.sectionReady') : t('sandbox.sectionMissing');
|
|
||||||
});
|
|
||||||
|
|
||||||
const sandboxShowWizard = computed(() => {
|
|
||||||
const s = sandboxSetup.status;
|
|
||||||
return !!s && s.applicable && s.state !== 'ready';
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 共享上下文由 PersonalizationDrawer.vue 通过 provide 注入。
|
* 共享上下文由 PersonalizationDrawer.vue 通过 provide 注入。
|
||||||
* 解构出的名称与主文件 script 顶层绑定一致,模板可直接引用。
|
* 解构出的名称与主文件 script 顶层绑定一致,模板可直接引用。
|
||||||
*/
|
*/
|
||||||
const ctx = inject<Record<string, any>>('personalizationDrawer')!;
|
const ctx = inject<Record<string, any>>('personalizationDrawer')!;
|
||||||
const {
|
const {
|
||||||
activeDropdown,
|
|
||||||
activeTheme,
|
|
||||||
closeDropdown,
|
|
||||||
floatingMenuStyle,
|
|
||||||
personalization,
|
personalization,
|
||||||
form,
|
form,
|
||||||
startTutorial,
|
startTutorial,
|
||||||
@ -47,8 +21,12 @@ const {
|
|||||||
appUpdateChecking,
|
appUpdateChecking,
|
||||||
checkAppUpdate,
|
checkAppUpdate,
|
||||||
downloadLatestApp,
|
downloadLatestApp,
|
||||||
subAgentModels,
|
fetchUsageSummary,
|
||||||
toggleDropdown
|
formatTokenCount,
|
||||||
|
usageError,
|
||||||
|
usageLoading,
|
||||||
|
usageSummary,
|
||||||
|
usageUpdatedText
|
||||||
} = ctx;
|
} = ctx;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -72,51 +50,6 @@ const {
|
|||||||
<FancyCheck :checked="form.auto_generate_title" />
|
<FancyCheck :checked="form.auto_generate_title" />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<!-- 标题生成模型:复用子智能体模型库配置(个人空间为唯一配置来源) -->
|
|
||||||
<div class="settings-select-row">
|
|
||||||
<span class="settings-row-copy">
|
|
||||||
<span class="settings-row-title">{{ $t('personalization.titleModelTitle') }}</span>
|
|
||||||
<span class="settings-row-desc">{{ $t('personalization.titleModelDesc') }}</span>
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
class="settings-select-wrap"
|
|
||||||
:class="{ open: activeDropdown === 'title-model' }"
|
|
||||||
@click.stop
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="settings-select-button"
|
|
||||||
@click="toggleDropdown('title-model')"
|
|
||||||
>
|
|
||||||
{{ form.title_model || $t('personalization.defaultModelOption') }}
|
|
||||||
<span class="select-chevron" aria-hidden="true"></span>
|
|
||||||
</button>
|
|
||||||
<div
|
|
||||||
:class="['settings-floating-menu', { dark: activeTheme === 'dark' }]"
|
|
||||||
:style="activeDropdown === 'title-model' ? floatingMenuStyle : undefined"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="settings-menu-option"
|
|
||||||
:class="{ selected: !form.title_model }"
|
|
||||||
@click="personalization.updateField({ key: 'title_model', value: '' }); closeDropdown()"
|
|
||||||
>
|
|
||||||
<strong>{{ $t('personalization.defaultModelOption') }}</strong><span>{{ $t('personalization.titleDefaultModelDesc') }}</span><svg viewBox="0 0 24 24"><path d="M5 12.5 9.5 17 19 7" /></svg>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-for="m in subAgentModels"
|
|
||||||
:key="m.name"
|
|
||||||
type="button"
|
|
||||||
class="settings-menu-option"
|
|
||||||
:class="{ selected: form.title_model === m.name }"
|
|
||||||
@click="personalization.updateField({ key: 'title_model', value: m.name }); closeDropdown()"
|
|
||||||
>
|
|
||||||
<strong>{{ m.name }}</strong><span>{{ m.modes }} · {{ m.multimodal || $t('personalization.textOnly') }}</span><svg viewBox="0 0 24 24"><path d="M5 12.5 9.5 17 19 7" /></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings-action-row">
|
<div class="settings-action-row">
|
||||||
<span class="settings-row-copy">
|
<span class="settings-row-copy">
|
||||||
<span class="settings-row-title">{{ $t('personalization.tutorialTitle') }}</span>
|
<span class="settings-row-title">{{ $t('personalization.tutorialTitle') }}</span>
|
||||||
@ -158,44 +91,68 @@ const {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-action-row sandbox-env-row">
|
|
||||||
<span class="settings-row-copy">
|
<!-- 用量统计(原「数据管理」页签并入常规) -->
|
||||||
<span class="settings-row-title">{{ $t('sandbox.sectionTitle') }}</span>
|
<div class="usage-summary-block" data-tutorial="personal-page-usage">
|
||||||
<span class="settings-row-desc">{{ $t('sandbox.sectionDesc') }}</span>
|
<div class="usage-summary-card settings-stats-card">
|
||||||
</span>
|
<div class="usage-summary-header">
|
||||||
<div class="settings-inline-actions">
|
<div>
|
||||||
<span class="settings-mini-status" :class="{ warning: sandboxShowWizard }">{{
|
<p class="usage-summary-eyebrow">{{ $t('personalization.usageEyebrow') }}</p>
|
||||||
sandboxStatusText
|
<h3>{{ $t('personalization.usageTitle') }}</h3>
|
||||||
}}</span>
|
<p class="usage-summary-desc">{{ $t('personalization.usageDesc') }}
|
||||||
<span v-if="sandboxSetup.neverAsk" class="settings-mini-status">{{
|
</p>
|
||||||
$t('sandbox.neverAgainSet')
|
</div>
|
||||||
}}</span>
|
</div>
|
||||||
<button
|
<div class="usage-summary-grid usage-summary-grid--tokens">
|
||||||
v-if="sandboxSetup.neverAsk"
|
<div class="usage-summary-item">
|
||||||
|
<div class="label">{{ $t('personalization.usageInputLabel') }}</div>
|
||||||
|
<div class="value value--success">
|
||||||
|
{{ formatTokenCount(usageSummary.total_input_tokens) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="usage-summary-item">
|
||||||
|
<div class="label">{{ $t('personalization.usageOutputLabel') }}</div>
|
||||||
|
<div class="value value--warning">
|
||||||
|
{{ formatTokenCount(usageSummary.total_output_tokens) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="usage-summary-grid usage-summary-grid--counts">
|
||||||
|
<div class="usage-summary-item">
|
||||||
|
<div class="label">{{ $t('personalization.usageConversationsLabel') }}</div>
|
||||||
|
<div class="value">
|
||||||
|
{{ formatTokenCount(usageSummary.total_conversations) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="usage-summary-item">
|
||||||
|
<div class="label">{{ $t('personalization.usageMessagesLabel') }}</div>
|
||||||
|
<div class="value">
|
||||||
|
{{ formatTokenCount(usageSummary.total_user_messages) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="usage-summary-item">
|
||||||
|
<div class="label">{{ $t('personalization.usageToolsLabel') }}</div>
|
||||||
|
<div class="value">
|
||||||
|
{{ formatTokenCount(usageSummary.total_tools) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="usage-summary-meta">
|
||||||
|
<span v-if="usageError" class="usage-summary-error">{{ usageError }}</span
|
||||||
|
><span v-else-if="usageLoading">{{ $t('personalization.usageSyncing') }}</span
|
||||||
|
><span v-else>{{ $t('personalization.usageUpdated', { time: usageUpdatedText }) }}</span
|
||||||
|
><button
|
||||||
type="button"
|
type="button"
|
||||||
class="settings-secondary-button"
|
class="usage-summary-refresh"
|
||||||
@click="sandboxSetup.resetNeverAsk()"
|
@click="fetchUsageSummary"
|
||||||
|
:disabled="usageLoading"
|
||||||
>
|
>
|
||||||
{{ $t('sandbox.resetNeverAgain') }}
|
{{ usageLoading ? $t('personalization.usageRefreshing') : $t('personalization.usageRefresh') }}
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="settings-secondary-button"
|
|
||||||
:disabled="sandboxSetup.checking"
|
|
||||||
@click="sandboxSetup.recheck()"
|
|
||||||
>
|
|
||||||
{{ sandboxSetup.checking ? $t('common.refreshing') : $t('sandbox.recheck') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-if="sandboxShowWizard"
|
|
||||||
type="button"
|
|
||||||
class="settings-primary-button"
|
|
||||||
@click="sandboxSetup.openWizard()"
|
|
||||||
>
|
|
||||||
{{ $t('sandbox.openWizard') }}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="settings-action-row danger-zone">
|
<div class="settings-action-row danger-zone">
|
||||||
<span class="settings-row-copy">
|
<span class="settings-row-copy">
|
||||||
<span class="settings-row-title">{{ $t('personalization.logoutTitle') }}</span>
|
<span class="settings-row-title">{{ $t('personalization.logoutTitle') }}</span>
|
||||||
@ -213,14 +170,97 @@ const {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 沙箱环境区块:上下结构(标题描述在上,状态徽标与按钮组整行在下),
|
.usage-summary-block {
|
||||||
避免默认左右结构中右侧按钮组挤压左侧文案空间 */
|
display: block;
|
||||||
.sandbox-env-row {
|
}
|
||||||
grid-template-columns: minmax(0, 1fr);
|
|
||||||
|
.settings-stats-card,
|
||||||
|
.usage-summary-card {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 720px;
|
||||||
|
padding: 0;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-summary-header h3 {
|
||||||
|
margin: 4px 0 6px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-summary-eyebrow {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--accent-strong);
|
||||||
|
letter-spacing: 0.16em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-summary-desc,
|
||||||
|
.usage-summary-note,
|
||||||
|
.usage-summary-meta {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-summary-grid {
|
||||||
|
display: grid;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sandbox-env-row .settings-inline-actions {
|
.usage-summary-grid--tokens {
|
||||||
justify-content: flex-start;
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-summary-grid--counts {
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-summary-item {
|
||||||
|
padding: 12px 14px;
|
||||||
|
border-radius: 14px;
|
||||||
|
border: 1px solid var(--theme-control-border);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-summary-item .label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-summary-item .value {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-summary-item .value--success {
|
||||||
|
color: var(--state-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-summary-item .value--warning {
|
||||||
|
color: var(--state-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-summary-meta {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-summary-refresh {
|
||||||
|
border: 1px solid var(--theme-control-border);
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 7px 14px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -12,6 +12,7 @@ const {
|
|||||||
personalization,
|
personalization,
|
||||||
form,
|
form,
|
||||||
activeDropdown,
|
activeDropdown,
|
||||||
|
closeDropdown,
|
||||||
defaultModelLabel,
|
defaultModelLabel,
|
||||||
filteredModelOptions,
|
filteredModelOptions,
|
||||||
floatingMenuStyle,
|
floatingMenuStyle,
|
||||||
@ -22,6 +23,7 @@ const {
|
|||||||
selectDefaultModel,
|
selectDefaultModel,
|
||||||
selectDefaultReasoningEffort,
|
selectDefaultReasoningEffort,
|
||||||
selectDefaultRunMode,
|
selectDefaultRunMode,
|
||||||
|
subAgentModels,
|
||||||
toggleDropdown,
|
toggleDropdown,
|
||||||
runModeOptions,
|
runModeOptions,
|
||||||
reasoningEffortOptions,
|
reasoningEffortOptions,
|
||||||
@ -79,9 +81,9 @@ const {
|
|||||||
</div>
|
</div>
|
||||||
<div class="settings-select-row">
|
<div class="settings-select-row">
|
||||||
<span class="settings-row-copy"
|
<span class="settings-row-copy"
|
||||||
><span class="settings-row-title">{{ $t('personalization.defaultRunModeTitle') }}</span
|
><span class="settings-row-title">{{ $t('personalization.defaultThinkingModeTitle') }}</span
|
||||||
><span class="settings-row-desc"
|
><span class="settings-row-desc"
|
||||||
>{{ $t('personalization.defaultRunModeDesc') }}</span
|
>{{ $t('personalization.defaultThinkingModeDesc') }}</span
|
||||||
></span
|
></span
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -155,5 +157,49 @@ const {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 标题生成模型:复用子智能体模型库配置(个人空间为唯一配置来源) -->
|
||||||
|
<div class="settings-select-row">
|
||||||
|
<span class="settings-row-copy">
|
||||||
|
<span class="settings-row-title">{{ $t('personalization.titleModelTitle') }}</span>
|
||||||
|
<span class="settings-row-desc">{{ $t('personalization.titleModelDesc') }}</span>
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
class="settings-select-wrap"
|
||||||
|
:class="{ open: activeDropdown === 'title-model' }"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="settings-select-button"
|
||||||
|
@click="toggleDropdown('title-model')"
|
||||||
|
>
|
||||||
|
{{ form.title_model || $t('personalization.defaultModelOption') }}
|
||||||
|
<span class="select-chevron" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
:class="['settings-floating-menu', { dark: activeTheme === 'dark' }]"
|
||||||
|
:style="activeDropdown === 'title-model' ? floatingMenuStyle : undefined"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="settings-menu-option"
|
||||||
|
:class="{ selected: !form.title_model }"
|
||||||
|
@click="personalization.updateField({ key: 'title_model', value: '' }); closeDropdown()"
|
||||||
|
>
|
||||||
|
<strong>{{ $t('personalization.defaultModelOption') }}</strong><span>{{ $t('personalization.titleDefaultModelDesc') }}</span><svg viewBox="0 0 24 24"><path d="M5 12.5 9.5 17 19 7" /></svg>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-for="m in subAgentModels"
|
||||||
|
:key="m.name"
|
||||||
|
type="button"
|
||||||
|
class="settings-menu-option"
|
||||||
|
:class="{ selected: form.title_model === m.name }"
|
||||||
|
@click="personalization.updateField({ key: 'title_model', value: m.name }); closeDropdown()"
|
||||||
|
>
|
||||||
|
<strong>{{ m.name }}</strong><span>{{ m.modes }} · {{ m.multimodal || $t('personalization.textOnly') }}</span><svg viewBox="0 0 24 24"><path d="M5 12.5 9.5 17 19 7" /></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -20,11 +20,6 @@ const {
|
|||||||
activeTheme,
|
activeTheme,
|
||||||
communicationStyleLabel,
|
communicationStyleLabel,
|
||||||
conversationContinuityLabel,
|
conversationContinuityLabel,
|
||||||
currentBlockDisplayMode,
|
|
||||||
stackedHideBorders,
|
|
||||||
handleStackedHideBordersChange,
|
|
||||||
minimalExpandHeightLimited,
|
|
||||||
handleMinimalExpandHeightLimitedChange,
|
|
||||||
selectCommunicationStyle,
|
selectCommunicationStyle,
|
||||||
selectConversationContinuity
|
selectConversationContinuity
|
||||||
} = ctx;
|
} = ctx;
|
||||||
@ -96,6 +91,21 @@ const {
|
|||||||
@focus="personalization.clearFeedback()"
|
@focus="personalization.clearFeedback()"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
<label class="settings-toggle-row"
|
||||||
|
><span class="settings-row-copy"
|
||||||
|
><span class="settings-row-title">{{ $t('personalization.useCustomNamesTitle') }}</span
|
||||||
|
><span class="settings-row-desc"
|
||||||
|
>{{ $t('personalization.useCustomNamesDesc') }}</span
|
||||||
|
></span
|
||||||
|
><input
|
||||||
|
type="checkbox"
|
||||||
|
:checked="form.use_custom_names"
|
||||||
|
@change="
|
||||||
|
personalization.updateField({
|
||||||
|
key: 'use_custom_names',
|
||||||
|
value: $event.target.checked
|
||||||
|
})
|
||||||
|
" /><FancyCheck :checked="form.use_custom_names" /></label>
|
||||||
<div class="settings-input-row stackable">
|
<div class="settings-input-row stackable">
|
||||||
<span class="settings-row-title">{{ $t('personalization.toneTitle') }}</span>
|
<span class="settings-row-title">{{ $t('personalization.toneTitle') }}</span>
|
||||||
<div class="settings-input-stack">
|
<div class="settings-input-stack">
|
||||||
@ -138,36 +148,6 @@ const {
|
|||||||
@focus="personalization.clearFeedback()"
|
@focus="personalization.clearFeedback()"
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
<label
|
|
||||||
v-if="currentBlockDisplayMode === 'stacked'"
|
|
||||||
class="settings-toggle-row"
|
|
||||||
>
|
|
||||||
<span class="settings-row-copy">
|
|
||||||
<span class="settings-row-title">{{ $t('personalization.hideBlockBordersTitle') }}</span>
|
|
||||||
<span class="settings-row-desc">{{ $t('personalization.hideBlockBordersDesc') }}</span>
|
|
||||||
</span>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
:checked="stackedHideBorders"
|
|
||||||
@change="handleStackedHideBordersChange($event)"
|
|
||||||
/>
|
|
||||||
<FancyCheck :checked="stackedHideBorders" />
|
|
||||||
</label>
|
|
||||||
<label
|
|
||||||
v-if="currentBlockDisplayMode === 'minimal'"
|
|
||||||
class="settings-toggle-row"
|
|
||||||
>
|
|
||||||
<span class="settings-row-copy">
|
|
||||||
<span class="settings-row-title">{{ $t('personalization.minimalExpandHeightTitle') }}</span>
|
|
||||||
<span class="settings-row-desc">{{ $t('personalization.minimalExpandHeightDesc') }}</span>
|
|
||||||
</span>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
:checked="minimalExpandHeightLimited"
|
|
||||||
@change="handleMinimalExpandHeightLimitedChange($event)"
|
|
||||||
/>
|
|
||||||
<FancyCheck :checked="minimalExpandHeightLimited" />
|
|
||||||
</label>
|
|
||||||
<div class="settings-select-row">
|
<div class="settings-select-row">
|
||||||
<span class="settings-row-copy">
|
<span class="settings-row-copy">
|
||||||
<span class="settings-row-title">{{ $t('personalization.communicationStyleTitle') }}</span>
|
<span class="settings-row-title">{{ $t('personalization.communicationStyleTitle') }}</span>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { inject } from 'vue';
|
import { inject } from 'vue';
|
||||||
|
import FancyCheck from '@/components/common/FancyCheck.vue';
|
||||||
|
|
||||||
defineOptions({ name: 'ReviewAgentsTab' });
|
defineOptions({ name: 'ReviewAgentsTab' });
|
||||||
|
|
||||||
@ -11,12 +12,18 @@ const ctx = inject<Record<string, any>>('personalizationDrawer')!;
|
|||||||
const {
|
const {
|
||||||
activeDropdown,
|
activeDropdown,
|
||||||
activeTheme,
|
activeTheme,
|
||||||
|
clampGoalMaxTokens,
|
||||||
|
clampGoalMaxTurns,
|
||||||
closeDropdown,
|
closeDropdown,
|
||||||
floatingMenuStyle,
|
floatingMenuStyle,
|
||||||
|
form,
|
||||||
|
goalTokenLimitEnabled,
|
||||||
|
personalization,
|
||||||
reviewAgentDefs,
|
reviewAgentDefs,
|
||||||
reviewAgentOf,
|
reviewAgentOf,
|
||||||
subAgentModels,
|
subAgentModels,
|
||||||
toggleDropdown,
|
toggleDropdown,
|
||||||
|
toggleGoalTokenLimit,
|
||||||
updateReviewAgent,
|
updateReviewAgent,
|
||||||
updateReviewAgentInt
|
updateReviewAgentInt
|
||||||
} = ctx;
|
} = ctx;
|
||||||
@ -166,5 +173,79 @@ const {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- 目标模式(goal review 机制的运行参数,自「工作区与权限」迁入) -->
|
||||||
|
<div class="settings-section-divider">
|
||||||
|
<span class="settings-section-divider__label">{{ $t('personalization.goalModeDivider') }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="settings-toggle-row"
|
||||||
|
><span class="settings-row-copy"
|
||||||
|
><span class="settings-row-title">{{ $t('personalization.goalReviewActiveTitle') }}</span
|
||||||
|
><span class="settings-row-desc"
|
||||||
|
>{{ $t('personalization.goalReviewActiveDesc') }}</span
|
||||||
|
></span
|
||||||
|
><input
|
||||||
|
type="checkbox"
|
||||||
|
:checked="form.goal_review_mode === 'active'"
|
||||||
|
@change="
|
||||||
|
personalization.updateField({
|
||||||
|
key: 'goal_review_mode',
|
||||||
|
value: $event.target.checked ? 'active' : 'readonly'
|
||||||
|
})
|
||||||
|
" /><FancyCheck :checked="form.goal_review_mode === 'active'" /></label>
|
||||||
|
|
||||||
|
<div class="settings-select-row">
|
||||||
|
<span class="settings-row-copy"
|
||||||
|
><span class="settings-row-title">{{ $t('personalization.goalMaxTurnsTitle') }}</span
|
||||||
|
><span class="settings-row-desc"
|
||||||
|
>{{ $t('personalization.goalMaxTurnsDesc') }}</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
class="settings-number-input"
|
||||||
|
min="1"
|
||||||
|
max="100"
|
||||||
|
:value="form.goal_max_turns"
|
||||||
|
@change="
|
||||||
|
personalization.updateField({
|
||||||
|
key: 'goal_max_turns',
|
||||||
|
value: clampGoalMaxTurns($event.target.value)
|
||||||
|
})
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="settings-toggle-row"
|
||||||
|
><span class="settings-row-copy"
|
||||||
|
><span class="settings-row-title">{{ $t('personalization.goalTokenLimitTitle') }}</span
|
||||||
|
><span class="settings-row-desc"
|
||||||
|
>{{ $t('personalization.goalTokenLimitDesc') }}</span
|
||||||
|
></span
|
||||||
|
><input
|
||||||
|
type="checkbox"
|
||||||
|
:checked="goalTokenLimitEnabled"
|
||||||
|
@change="toggleGoalTokenLimit($event.target.checked)" /><FancyCheck :checked="goalTokenLimitEnabled" /></label>
|
||||||
|
|
||||||
|
<div v-if="goalTokenLimitEnabled" class="settings-select-row">
|
||||||
|
<span class="settings-row-copy"
|
||||||
|
><span class="settings-row-title">{{ $t('personalization.goalTokenLimitValueTitle') }}</span
|
||||||
|
><span class="settings-row-desc">{{ $t('personalization.goalTokenLimitValueDesc') }}</span></span
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
class="settings-number-input"
|
||||||
|
min="1000"
|
||||||
|
step="1000"
|
||||||
|
:value="form.goal_max_tokens || 100000"
|
||||||
|
@change="
|
||||||
|
personalization.updateField({
|
||||||
|
key: 'goal_max_tokens',
|
||||||
|
value: clampGoalMaxTokens($event.target.value)
|
||||||
|
})
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,9 +1,31 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { inject } from 'vue';
|
import { computed, inject, onMounted } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import FancyCheck from '@/components/common/FancyCheck.vue';
|
import FancyCheck from '@/components/common/FancyCheck.vue';
|
||||||
|
import { useSandboxSetupStore } from '@/stores/sandboxSetup';
|
||||||
|
|
||||||
defineOptions({ name: 'WorkspaceTab' });
|
defineOptions({ name: 'WorkspaceTab' });
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const sandboxSetup = useSandboxSetupStore();
|
||||||
|
|
||||||
|
// 沙箱环境区块:进入工作区与权限页时补一次检测(复用 store 内部防抖)
|
||||||
|
onMounted(() => {
|
||||||
|
if (!sandboxSetup.status) void sandboxSetup.fetchStatus();
|
||||||
|
});
|
||||||
|
|
||||||
|
const sandboxStatusText = computed(() => {
|
||||||
|
const s = sandboxSetup.status;
|
||||||
|
if (!s || sandboxSetup.checking) return t('sandbox.sectionChecking');
|
||||||
|
if (!s.applicable) return t('sandbox.sectionUnavailable');
|
||||||
|
return s.state === 'ready' ? t('sandbox.sectionReady') : t('sandbox.sectionMissing');
|
||||||
|
});
|
||||||
|
|
||||||
|
const sandboxShowWizard = computed(() => {
|
||||||
|
const s = sandboxSetup.status;
|
||||||
|
return !!s && s.applicable && s.state !== 'ready';
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 共享上下文由 PersonalizationDrawer.vue 通过 provide 注入。
|
* 共享上下文由 PersonalizationDrawer.vue 通过 provide 注入。
|
||||||
* 解构出的名称与主文件 script 顶层绑定一致,模板可直接引用。
|
* 解构出的名称与主文件 script 顶层绑定一致,模板可直接引用。
|
||||||
@ -13,23 +35,17 @@ const {
|
|||||||
activeDropdown,
|
activeDropdown,
|
||||||
floatingMenuStyle,
|
floatingMenuStyle,
|
||||||
form,
|
form,
|
||||||
goalTokenLimitEnabled,
|
|
||||||
permissionModeLabel,
|
permissionModeLabel,
|
||||||
personalization,
|
personalization,
|
||||||
selectDefaultPermissionMode,
|
selectDefaultPermissionMode,
|
||||||
selectDefaultWorkMode,
|
selectDefaultWorkMode,
|
||||||
selectVersioningBackupMode,
|
selectVersioningBackupMode,
|
||||||
toggleDropdown,
|
toggleDropdown,
|
||||||
toggleGoalTokenLimit,
|
|
||||||
versioningBackupModeLabel,
|
versioningBackupModeLabel,
|
||||||
workModeLabel,
|
workModeLabel,
|
||||||
permissionModeOptions,
|
permissionModeOptions,
|
||||||
workModeOptions,
|
workModeOptions,
|
||||||
// 清单外顶层绑定(模板引用):activeTheme 已在 drawerContext 中提供;
|
activeTheme
|
||||||
// clampGoalMaxTurns / clampGoalMaxTokens 为顶层 const 但当前不在 drawerContext 内(详见 report.md)
|
|
||||||
activeTheme,
|
|
||||||
clampGoalMaxTurns,
|
|
||||||
clampGoalMaxTokens
|
|
||||||
} = ctx;
|
} = ctx;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -74,7 +90,7 @@ const {
|
|||||||
</div>
|
</div>
|
||||||
<div class="settings-select-row">
|
<div class="settings-select-row">
|
||||||
<span class="settings-row-copy"
|
<span class="settings-row-copy"
|
||||||
><span class="settings-row-title">{{ $t('personalization.defaultRunModeTitle') }}</span
|
><span class="settings-row-title">{{ $t('personalization.defaultWorkModeTitle') }}</span
|
||||||
><span class="settings-row-desc"
|
><span class="settings-row-desc"
|
||||||
>{{ $t('personalization.workRunModeDesc') }}</span
|
>{{ $t('personalization.workRunModeDesc') }}</span
|
||||||
></span
|
></span
|
||||||
@ -111,6 +127,45 @@ const {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="settings-action-row sandbox-env-row">
|
||||||
|
<span class="settings-row-copy">
|
||||||
|
<span class="settings-row-title">{{ $t('sandbox.sectionTitle') }}</span>
|
||||||
|
<span class="settings-row-desc">{{ $t('sandbox.sectionDesc') }}</span>
|
||||||
|
</span>
|
||||||
|
<div class="settings-inline-actions">
|
||||||
|
<span class="settings-mini-status" :class="{ warning: sandboxShowWizard }">{{
|
||||||
|
sandboxStatusText
|
||||||
|
}}</span>
|
||||||
|
<span v-if="sandboxSetup.neverAsk" class="settings-mini-status">{{
|
||||||
|
$t('sandbox.neverAgainSet')
|
||||||
|
}}</span>
|
||||||
|
<button
|
||||||
|
v-if="sandboxSetup.neverAsk"
|
||||||
|
type="button"
|
||||||
|
class="settings-secondary-button"
|
||||||
|
@click="sandboxSetup.resetNeverAsk()"
|
||||||
|
>
|
||||||
|
{{ $t('sandbox.resetNeverAgain') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="settings-secondary-button"
|
||||||
|
:disabled="sandboxSetup.checking"
|
||||||
|
@click="sandboxSetup.recheck()"
|
||||||
|
>
|
||||||
|
{{ sandboxSetup.checking ? $t('common.refreshing') : $t('sandbox.recheck') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="sandboxShowWizard"
|
||||||
|
type="button"
|
||||||
|
class="settings-primary-button"
|
||||||
|
@click="sandboxSetup.openWizard()"
|
||||||
|
>
|
||||||
|
{{ $t('sandbox.openWizard') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<label class="settings-toggle-row"
|
<label class="settings-toggle-row"
|
||||||
><span class="settings-row-copy"
|
><span class="settings-row-copy"
|
||||||
><span class="settings-row-title">{{ $t('personalization.agentsMdInjectTitle') }}</span
|
><span class="settings-row-title">{{ $t('personalization.agentsMdInjectTitle') }}</span
|
||||||
@ -241,77 +296,18 @@ const {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="settings-section-divider">
|
|
||||||
<span class="settings-section-divider__label">{{ $t('personalization.goalModeDivider') }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label class="settings-toggle-row"
|
|
||||||
><span class="settings-row-copy"
|
|
||||||
><span class="settings-row-title">{{ $t('personalization.goalReviewActiveTitle') }}</span
|
|
||||||
><span class="settings-row-desc"
|
|
||||||
>{{ $t('personalization.goalReviewActiveDesc') }}</span
|
|
||||||
></span
|
|
||||||
><input
|
|
||||||
type="checkbox"
|
|
||||||
:checked="form.goal_review_mode === 'active'"
|
|
||||||
@change="
|
|
||||||
personalization.updateField({
|
|
||||||
key: 'goal_review_mode',
|
|
||||||
value: $event.target.checked ? 'active' : 'readonly'
|
|
||||||
})
|
|
||||||
" /><FancyCheck :checked="form.goal_review_mode === 'active'" /></label>
|
|
||||||
|
|
||||||
<div class="settings-select-row">
|
|
||||||
<span class="settings-row-copy"
|
|
||||||
><span class="settings-row-title">{{ $t('personalization.goalMaxTurnsTitle') }}</span
|
|
||||||
><span class="settings-row-desc"
|
|
||||||
>{{ $t('personalization.goalMaxTurnsDesc') }}</span
|
|
||||||
></span
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
class="settings-number-input"
|
|
||||||
min="1"
|
|
||||||
max="100"
|
|
||||||
:value="form.goal_max_turns"
|
|
||||||
@change="
|
|
||||||
personalization.updateField({
|
|
||||||
key: 'goal_max_turns',
|
|
||||||
value: clampGoalMaxTurns($event.target.value)
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label class="settings-toggle-row"
|
|
||||||
><span class="settings-row-copy"
|
|
||||||
><span class="settings-row-title">{{ $t('personalization.goalTokenLimitTitle') }}</span
|
|
||||||
><span class="settings-row-desc"
|
|
||||||
>{{ $t('personalization.goalTokenLimitDesc') }}</span
|
|
||||||
></span
|
|
||||||
><input
|
|
||||||
type="checkbox"
|
|
||||||
:checked="goalTokenLimitEnabled"
|
|
||||||
@change="toggleGoalTokenLimit($event.target.checked)" /><FancyCheck :checked="goalTokenLimitEnabled" /></label>
|
|
||||||
|
|
||||||
<div v-if="goalTokenLimitEnabled" class="settings-select-row">
|
|
||||||
<span class="settings-row-copy"
|
|
||||||
><span class="settings-row-title">{{ $t('personalization.goalTokenLimitValueTitle') }}</span
|
|
||||||
><span class="settings-row-desc">{{ $t('personalization.goalTokenLimitValueDesc') }}</span></span
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
class="settings-number-input"
|
|
||||||
min="1000"
|
|
||||||
step="1000"
|
|
||||||
:value="form.goal_max_tokens || 100000"
|
|
||||||
@change="
|
|
||||||
personalization.updateField({
|
|
||||||
key: 'goal_max_tokens',
|
|
||||||
value: clampGoalMaxTokens($event.target.value)
|
|
||||||
})
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 沙箱环境区块:上下结构(标题描述在上,状态徽标与按钮组整行在下),
|
||||||
|
避免默认左右结构中右侧按钮组挤压左侧文案空间 */
|
||||||
|
.sandbox-env-row {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sandbox-env-row .settings-inline-actions {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -15,7 +15,6 @@ export default {
|
|||||||
tabContext: 'Context',
|
tabContext: 'Context',
|
||||||
tabTools: 'Tools & Skills',
|
tabTools: 'Tools & Skills',
|
||||||
tabFiles: 'Files & Images',
|
tabFiles: 'Files & Images',
|
||||||
tabData: 'Data',
|
|
||||||
tabVoice: 'Voice Model',
|
tabVoice: 'Voice Model',
|
||||||
tabSubAgents: 'Sub-agents',
|
tabSubAgents: 'Sub-agents',
|
||||||
tabReviewAgents: 'Review Agents',
|
tabReviewAgents: 'Review Agents',
|
||||||
@ -82,8 +81,8 @@ export default {
|
|||||||
defaultModelTitle: 'Default model',
|
defaultModelTitle: 'Default model',
|
||||||
defaultModelDesc: 'Preferred model for new conversations and the first request after sign-in',
|
defaultModelDesc: 'Preferred model for new conversations and the first request after sign-in',
|
||||||
modelDisabled: 'Disabled by administrator',
|
modelDisabled: 'Disabled by administrator',
|
||||||
defaultRunModeTitle: 'Default run mode',
|
defaultThinkingModeTitle: 'Default thinking mode',
|
||||||
defaultRunModeDesc: 'Initial run mode on sign-in or when starting a new task',
|
defaultThinkingModeDesc: 'Initial thinking mode on sign-in or when starting a new task',
|
||||||
reasoningEffortTitle: 'Default reasoning effort',
|
reasoningEffortTitle: 'Default reasoning effort',
|
||||||
reasoningEffortDesc: 'Reasoning effort level attached to model requests in thinking mode',
|
reasoningEffortDesc: 'Reasoning effort level attached to model requests in thinking mode',
|
||||||
runModeFast: 'Fast mode',
|
runModeFast: 'Fast mode',
|
||||||
@ -166,7 +165,8 @@ export default {
|
|||||||
permissionAutoApprovalDesc: 'Writes pass through; high-risk operations are auto-approved by the background review agent',
|
permissionAutoApprovalDesc: 'Writes pass through; high-risk operations are auto-approved by the background review agent',
|
||||||
permissionUnrestricted: 'Unrestricted',
|
permissionUnrestricted: 'Unrestricted',
|
||||||
permissionUnrestrictedDesc: 'Tools execute directly through the normal flow',
|
permissionUnrestrictedDesc: 'Tools execute directly through the normal flow',
|
||||||
workRunModeDesc: 'Default run mode for new conversations; locked to read-only in plan mode',
|
defaultWorkModeTitle: 'Default work mode',
|
||||||
|
workRunModeDesc: 'Default work mode for new conversations; locked to read-only in plan mode',
|
||||||
workModePlan: 'Plan',
|
workModePlan: 'Plan',
|
||||||
workModePlanDesc: 'Only makes a plan and executes after approval',
|
workModePlanDesc: 'Only makes a plan and executes after approval',
|
||||||
workModeAsk: 'Ask',
|
workModeAsk: 'Ask',
|
||||||
|
|||||||
@ -16,7 +16,6 @@ export default {
|
|||||||
tabContext: '上下文',
|
tabContext: '上下文',
|
||||||
tabTools: '工具与 Skills',
|
tabTools: '工具与 Skills',
|
||||||
tabFiles: '文件与图片',
|
tabFiles: '文件与图片',
|
||||||
tabData: '数据管理',
|
|
||||||
tabVoice: '语音模型',
|
tabVoice: '语音模型',
|
||||||
tabSubAgents: '子智能体',
|
tabSubAgents: '子智能体',
|
||||||
tabReviewAgents: '审核智能体',
|
tabReviewAgents: '审核智能体',
|
||||||
@ -83,8 +82,8 @@ export default {
|
|||||||
defaultModelTitle: '默认模型',
|
defaultModelTitle: '默认模型',
|
||||||
defaultModelDesc: '为新对话/登录后首次请求选择首选模型',
|
defaultModelDesc: '为新对话/登录后首次请求选择首选模型',
|
||||||
modelDisabled: '已被管理员禁用',
|
modelDisabled: '已被管理员禁用',
|
||||||
defaultRunModeTitle: '默认运行模式',
|
defaultThinkingModeTitle: '默认思考模式',
|
||||||
defaultRunModeDesc: '登录或新建任务时的初始运行模式',
|
defaultThinkingModeDesc: '登录或新建任务时的初始思考模式',
|
||||||
reasoningEffortTitle: '默认推理强度',
|
reasoningEffortTitle: '默认推理强度',
|
||||||
reasoningEffortDesc: '思考模式下请求模型时附带的推理强度参数',
|
reasoningEffortDesc: '思考模式下请求模型时附带的推理强度参数',
|
||||||
runModeFast: '快速模式',
|
runModeFast: '快速模式',
|
||||||
@ -167,7 +166,8 @@ export default {
|
|||||||
permissionAutoApprovalDesc: '工作区内写入直通;高风险操作由后台审核智能体自动审批',
|
permissionAutoApprovalDesc: '工作区内写入直通;高风险操作由后台审核智能体自动审批',
|
||||||
permissionUnrestricted: '无限制',
|
permissionUnrestricted: '无限制',
|
||||||
permissionUnrestrictedDesc: '工具按常规流程直接执行',
|
permissionUnrestrictedDesc: '工具按常规流程直接执行',
|
||||||
workRunModeDesc: '新建对话时的默认运行模式;计划模式下权限锁定为只读',
|
defaultWorkModeTitle: '默认工作模式',
|
||||||
|
workRunModeDesc: '新建对话时的默认工作模式;计划模式下权限锁定为只读',
|
||||||
workModePlan: '计划',
|
workModePlan: '计划',
|
||||||
workModePlanDesc: '只制定计划,批准后执行',
|
workModePlanDesc: '只制定计划,批准后执行',
|
||||||
workModeAsk: '询问',
|
workModeAsk: '询问',
|
||||||
|
|||||||
@ -477,10 +477,11 @@ const TUTORIAL_STEP_DEFS: TutorialStepDef[] = [
|
|||||||
placement: 'right'
|
placement: 'right'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// 用量统计已并入「常规」页签,锚点指向 general
|
||||||
id: 'tab-usage',
|
id: 'tab-usage',
|
||||||
titleKey: 'tutorial.tabUsageTitle',
|
titleKey: 'tutorial.tabUsageTitle',
|
||||||
descriptionKey: 'tutorial.tabUsageDesc',
|
descriptionKey: 'tutorial.tabUsageDesc',
|
||||||
target: '[data-tutorial="personal-tab-usage"]',
|
target: '[data-tutorial="personal-tab-general"]',
|
||||||
mode: 'info',
|
mode: 'info',
|
||||||
autoClick: true,
|
autoClick: true,
|
||||||
placement: 'right'
|
placement: 'right'
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user