修复: workspace_root 编译期常量消除+data_dir 运行期+首次授权引导
- 删除 production 中全部 env!("CARGO_MANIFEST_DIR")引用
- workspace_root() 替换为运行期 data_dir(Tauri app_data_dir)
- 相对路径锚定改为 AllowedDirs.first_persistent_dir(),无授权时引导绑定项目
- .trash 迁到 data_dir/.trash,含自动迁移
- authz_debug → temp_dir,workspace_drive → current_dir
- run_command 默认 working_dir 改为空串
- G1 目标刷新改为每次消息覆盖(含 ai_chat_edit 路径)
- #6 评分关键词拆到独立文件 scoring_keywords.rs
- #7 promote 补偿删除 purge_with_descendants → soft_delete
- #8 关联双向同步: 后端事务 sync_related_ids + IPC + 前端全链路
- 设置面板新增数据目录显示
This commit is contained in:
@@ -40,6 +40,11 @@ export const ideaApi = {
|
||||
return invoke('promote_idea', { id })
|
||||
},
|
||||
|
||||
/** 双向同步关联关系:原子地设置主体灵感的目标关联列表,自动添加/移除反向关联。 */
|
||||
relateIdeas(subjectId: string, targetIds: string[]): Promise<void> {
|
||||
return invoke('relate_ideas', { subjectId, targetIds })
|
||||
},
|
||||
|
||||
/** 列出灵感评估历史(version DESC)。Tauri 默认把 ideaId 映射到 idea_id。 */
|
||||
listEvaluations(ideaId: string): Promise<IdeaEvaluationRecord[]> {
|
||||
return invoke('list_idea_evaluations', { ideaId })
|
||||
|
||||
@@ -16,6 +16,11 @@ export const settingsApi = {
|
||||
return invoke<boolean>('settings_set', { key, value })
|
||||
},
|
||||
|
||||
/** 获取 DevFlow 数据目录路径(运行期确定,跨平台) */
|
||||
getDataDir(): Promise<string> {
|
||||
return invoke<string>('get_data_dir')
|
||||
},
|
||||
|
||||
/** 取全部 key/value(启动时一次性拉回恢复偏好) */
|
||||
getAll(): Promise<Record<string, string>> {
|
||||
return invoke<Record<string, string>>('settings_get_all')
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
<h2>{{ $t('settings.sectionAdvanced') }}</h2>
|
||||
</div>
|
||||
<div class="general-settings">
|
||||
<!-- ═══ 应用数据目录(运行期确定,跨平台) ═══ -->
|
||||
<SettingRow :label="$t('settings.labelDataDir')" :desc="$t('settings.descDataDir')">
|
||||
<span class="data-dir-path" :title="dataDir">{{ dataDir || $t('settings.loading') }}</span>
|
||||
</SettingRow>
|
||||
|
||||
<!-- F-#97:三档 low/medium/all。low=仅低风险自动(等价旧 autoExecute=false);
|
||||
medium=含中风险自动;all=全自动无审批(完全 AI 接管,选时二次确认) -->
|
||||
<SettingRow ref="rowAutoExecute" :label="$t('settings.labelAutoExecute')" :desc="$t('settings.descAutoExecute')">
|
||||
@@ -40,10 +45,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, watch, useTemplateRef } from 'vue'
|
||||
import { ref, reactive, watch, useTemplateRef, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useConfirm } from '@/composables/useConfirm'
|
||||
import { useAppSettingsStore } from '@/stores/appSettings'
|
||||
import { settingsApi } from '@/api'
|
||||
import SettingRow from './SettingRow.vue'
|
||||
|
||||
// ============================================================
|
||||
@@ -56,6 +62,14 @@ import SettingRow from './SettingRow.vue'
|
||||
// ============================================================
|
||||
const { t } = useI18n()
|
||||
const appSettings = useAppSettingsStore()
|
||||
|
||||
// 应用数据目录(运行期确定,跨平台)
|
||||
const dataDir = ref('')
|
||||
onMounted(async () => {
|
||||
try {
|
||||
dataDir.value = await settingsApi.getDataDir()
|
||||
} catch { /* 静默 */ }
|
||||
})
|
||||
// useConfirm 真实 API:返回 { confirmState, confirmDialog(msg, dangerLabel), answerConfirm(ok) }
|
||||
// confirmDialog 返回 Promise<boolean>(true=确认,false=取消),无 title/content/confirmText/cancelText
|
||||
// 字段。此处用 msg 拼标题+内容,dangerLabel 作确认按钮文案,自渲染独立弹层(上方模板)。
|
||||
@@ -125,6 +139,11 @@ function markSaved() {
|
||||
|
||||
F-#97 all 二次确认弹层:复用 Settings.vue 壳层 confirm 弹层同款样式(mask/box/msg/
|
||||
actions/transition),此处独立持有 useConfirm 状态(不与壳层共用,壳层按钮写死「删除」)。 */
|
||||
.data-dir-path {
|
||||
font-size: 12px;
|
||||
color: var(--df-text-dim);
|
||||
word-break: break-all;
|
||||
}
|
||||
.confirm-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
|
||||
@@ -22,6 +22,14 @@ export default {
|
||||
deleting: 'Deleting…',
|
||||
promoting: 'Promoting…',
|
||||
|
||||
// Errors
|
||||
err: {
|
||||
loadFailed: 'Failed to load ideas',
|
||||
createFailed: 'Failed to create idea',
|
||||
deleteFailed: 'Failed to delete idea',
|
||||
relateFailed: 'Failed to update relations',
|
||||
},
|
||||
|
||||
// Adversarial evaluation
|
||||
adversarialTitle: '⚖️ Adversarial Evaluation',
|
||||
evalModeHeuristic: 'Heuristic',
|
||||
|
||||
@@ -111,6 +111,9 @@ export default {
|
||||
aiLanguageZh: '🇨🇳 简体中文',
|
||||
aiLanguageEn: '🇺🇸 English',
|
||||
|
||||
labelDataDir: 'App Data Directory',
|
||||
descDataDir: 'Where DevFlow stores .trash (recycle bin), database, logs and other internal data',
|
||||
|
||||
labelAutoExecute: 'AI auto-execute scope',
|
||||
descAutoExecute: 'Control risk level AI auto-executes (low/medium/all-takeover)',
|
||||
autoModeLow: 'Low risk (default)',
|
||||
|
||||
@@ -22,6 +22,14 @@ export default {
|
||||
deleting: '删除中…',
|
||||
promoting: '立项中…',
|
||||
|
||||
// 错误
|
||||
err: {
|
||||
loadFailed: '加载灵感失败',
|
||||
createFailed: '创建灵感失败',
|
||||
deleteFailed: '删除灵感失败',
|
||||
relateFailed: '更新关联关系失败',
|
||||
},
|
||||
|
||||
// 对抗式评估
|
||||
adversarialTitle: '⚖️ 对抗式评估',
|
||||
evalModeHeuristic: '启发式',
|
||||
|
||||
@@ -111,6 +111,9 @@ export default {
|
||||
aiLanguageZh: '🇨🇳 简体中文',
|
||||
aiLanguageEn: '🇺🇸 English',
|
||||
|
||||
labelDataDir: '应用数据目录',
|
||||
descDataDir: 'DevFlow 存储 .trash(回收站)、数据库、日志等内部数据的目录路径',
|
||||
|
||||
labelAutoExecute: 'AI 自动执行范围',
|
||||
descAutoExecute: '控制 AI 自动执行操作的风险等级(低/中/全部接管)',
|
||||
autoModeLow: '低风险(默认)',
|
||||
|
||||
@@ -61,6 +61,15 @@ export function createIdeasStore() {
|
||||
return res
|
||||
}
|
||||
|
||||
async function relateIdeas(subjectId: string, targetIds: string[]) {
|
||||
try {
|
||||
await ideaApi.relateIdeas(subjectId, targetIds)
|
||||
await loadIdeas() // 后端已原子更新全部受影响灵感,刷新列表保持 store 一致
|
||||
} catch (e: any) {
|
||||
state.error = e?.toString() ?? t('ideas.err.relateFailed')
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
loadIdeas,
|
||||
createIdea,
|
||||
@@ -68,5 +77,6 @@ export function createIdeasStore() {
|
||||
deleteIdea,
|
||||
evaluateIdea,
|
||||
promoteIdea,
|
||||
relateIdeas,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,10 +373,10 @@ async function onUpdateDesc(desc: string) {
|
||||
await store.updateIdea(currentIdea.value.id, 'description', desc)
|
||||
}
|
||||
|
||||
// 接收 IdeaDetail 子组件 emit 的 'update-related'(关联灵感编辑保存)
|
||||
// 接收 IdeaDetail 子组件 emit 的 'update-related'(关联灵感编辑保存,双向同步)
|
||||
async function onUpdateRelated(ids: string[]) {
|
||||
if (!currentIdea.value) return
|
||||
await store.updateIdea(currentIdea.value.id, 'related_ids', JSON.stringify(ids))
|
||||
await store.relateIdeas(currentIdea.value.id, ids)
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
Reference in New Issue
Block a user