HostToolsPanel.vue 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445
  1. <script setup lang="ts">
  2. import { onMounted, ref } from 'vue'
  3. import { apiRequest } from '@/api/client'
  4. const emit = defineEmits<{ 'dirty-change': [value: boolean] }>()
  5. const deny = ref(true)
  6. const loading = ref(false), saving = ref(false), error = ref(''), saved = ref(false)
  7. async function load() {
  8. loading.value = true; error.value = ''
  9. try {
  10. const value = await apiRequest<{ denyServerTools: boolean }>('/api/app/settings/host-tools')
  11. deny.value = value.denyServerTools
  12. emit('dirty-change', false)
  13. } catch (cause) { error.value = cause instanceof Error ? cause.message : '无法读取服务端工具设置' }
  14. finally { loading.value = false }
  15. }
  16. function changed() { saved.value = false; emit('dirty-change', true) }
  17. async function save() {
  18. saving.value = true; error.value = ''; saved.value = false
  19. try {
  20. const value = await apiRequest<{ denyServerTools: boolean }>('/api/app/settings/host-tools', { method: 'PUT', body: { denyServerTools: deny.value } })
  21. deny.value = value.denyServerTools
  22. saved.value = true; emit('dirty-change', false)
  23. } catch (cause) { error.value = cause instanceof Error ? cause.message : '保存失败,请重试' }
  24. finally { saving.value = false }
  25. }
  26. onMounted(load)
  27. </script>
  28. <template>
  29. <section class="host-tools" aria-label="服务端工具">
  30. <p>控制 Bot 在服务端本机上能不能用终端和文件。默认禁用。已勾选的脚本机、脚本机浏览器、虚拟环境和云虚拟机不受这个开关影响。</p>
  31. <form @submit.prevent="save" @change="changed">
  32. <label class="toggle"><input v-model="deny" type="checkbox" :disabled="loading || saving" /><span><strong>禁用服务端本机的终端和文件</strong><small>开启后,Bot 对话不会拿到服务端的 terminal 和 file 工具。关掉之后,新开的会话才会放开,已经在跑的会话要重开一次。</small></span></label>
  33. <p v-if="error" class="error" role="alert">{{ error }}</p>
  34. <p v-if="saved" role="status">已保存。下一次开始或恢复的 Bot 会话会按这个设置执行。</p>
  35. <div class="actions"><button type="button" :disabled="loading || saving" @click="load">重新读取</button><button type="submit" class="save" :disabled="loading || saving">{{ saving ? '保存中…' : loading ? '读取中…' : '保存设置' }}</button></div>
  36. </form>
  37. </section>
  38. </template>
  39. <style scoped>
  40. .host-tools{display:grid;gap:18px;max-width:680px;color:var(--text-primary);font-size:13px;line-height:1.7}.host-tools p{margin:0}.host-tools small{display:block;color:var(--text-secondary);font-size:12px}.host-tools form{display:grid;gap:14px}.toggle{display:flex;gap:12px;align-items:flex-start;cursor:pointer}.toggle input{width:20px;height:20px;margin-top:2px;accent-color:var(--accent)}.actions{display:flex;gap:10px;justify-content:flex-end}.actions button{min-height:44px;padding:8px 16px;border:1px solid var(--line);border-radius:9px;background:var(--surface);color:var(--text-primary);font:inherit;cursor:pointer}.actions .save{background:var(--accent);color:var(--text-on-solid)}button:disabled{opacity:.55;cursor:default}.error{color:var(--danger)}input:focus-visible,button:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
  41. </style>