|
@@ -4,17 +4,25 @@ import {apiRequest} from '@/api/client'
|
|
|
import AppIcon from '@/components/common/AppIcon.vue'
|
|
import AppIcon from '@/components/common/AppIcon.vue'
|
|
|
import type {DesktopEnvironmentState,BotBrowserState} from '@shared/desktopEnvironment'
|
|
import type {DesktopEnvironmentState,BotBrowserState} from '@shared/desktopEnvironment'
|
|
|
import GrokAuthPanel from './GrokAuthPanel.vue'
|
|
import GrokAuthPanel from './GrokAuthPanel.vue'
|
|
|
-import {supportsHostEnvironment,type WorkspaceAgent} from '@shared/workspace'
|
|
|
|
|
|
|
+import {supportsHostEnvironment,agentEnvs,type WorkspaceAgent} from '@shared/workspace'
|
|
|
import type {ComputerFrame} from '@shared/computerControl'
|
|
import type {ComputerFrame} from '@shared/computerControl'
|
|
|
import {vmIdleStopLabel,type LocalVmInstance,type LocalVmAction} from '@shared/localVm'
|
|
import {vmIdleStopLabel,type LocalVmInstance,type LocalVmAction} from '@shared/localVm'
|
|
|
const props=defineProps<{agents:WorkspaceAgent[];isAdmin:boolean;active?:boolean;embedded?:boolean}>()
|
|
const props=defineProps<{agents:WorkspaceAgent[];isAdmin:boolean;active?:boolean;embedded?:boolean}>()
|
|
|
-const emit=defineEmits<{close:[];changed:[];settings:[];desktop:[agent:WorkspaceAgent];workspace:[agent:WorkspaceAgent]}>()
|
|
|
|
|
|
|
+const emit=defineEmits<{close:[];changed:[];settings:[];desktop:[agent:WorkspaceAgent,backend?:'desktop'|'cloud'|'vm'];workspace:[agent:WorkspaceAgent]}>()
|
|
|
const selected=ref(''),state=ref<LocalVmInstance&{enabled:boolean;controlMode?:string}>(),frame=ref<ComputerFrame>(),error=ref(''),busy=ref(false),loading=ref(true)
|
|
const selected=ref(''),state=ref<LocalVmInstance&{enabled:boolean;controlMode?:string}>(),frame=ref<ComputerFrame>(),error=ref(''),busy=ref(false),loading=ref(true)
|
|
|
const agent=computed(()=>props.agents.find(a=>a.id===selected.value))
|
|
const agent=computed(()=>props.agents.find(a=>a.id===selected.value))
|
|
|
const native=ref<DesktopEnvironmentState>()
|
|
const native=ref<DesktopEnvironmentState>()
|
|
|
const nativeSelected=computed(()=>native.value?.selected)
|
|
const nativeSelected=computed(()=>native.value?.selected)
|
|
|
|
|
+const modern=computed(()=>!!agent.value?.envs)
|
|
|
|
|
+const envs=computed(()=>agent.value?agentEnvs(agent.value):{vm:false,cloud:false,desktop:false,browser:false})
|
|
|
|
|
+function toggleEnv(key:'vm'|'cloud'|'desktop'|'browser',enabled:boolean){
|
|
|
|
|
+ if(computerChoiceDisabled.value)return
|
|
|
|
|
+ void run(()=>apiRequest(`/api/app/agents/${selected.value}/computer-selection`,{method:'PUT',body:{envs:{...envs.value,[key]:enabled}}}))
|
|
|
|
|
+}
|
|
|
const computerTabs=ref<HTMLElement>(),cloud=ref<{configured:boolean;running:boolean;connected:boolean;mode?:string}>(),connectionSettings=ref(false)
|
|
const computerTabs=ref<HTMLElement>(),cloud=ref<{configured:boolean;running:boolean;connected:boolean;mode?:string}>(),connectionSettings=ref(false)
|
|
|
-const cloudSelected=computed(()=>agent.value?.computer==='cloud'||(agent.value?.computer==='auto'&&!nativeSelected.value&&agent.value.execution!=='computer'&&cloud.value?.configured))
|
|
|
|
|
|
|
+const cloudSelected=computed(()=>modern.value?envs.value.cloud:agent.value?.computer==='cloud'||(agent.value?.computer==='auto'&&!nativeSelected.value&&agent.value.execution!=='computer'&&!!cloud.value?.configured))
|
|
|
|
|
+const showCloud=computed(()=>cloudSelected.value&&(!nativeSelected.value||modern.value))
|
|
|
|
|
+const showVm=computed(()=>modern.value?(envs.value.vm&&!!state.value?.enabled):(!!state.value?.enabled&&!nativeSelected.value&&!cloudSelected.value))
|
|
|
const computerChoice=computed(()=>state.value?.fixedCapacity&&agent.value?.execution==='computer'?(state.value.desktopId??'off'):agent.value?.computer??(state.value?.enabled?'vm':'off'))
|
|
const computerChoice=computed(()=>state.value?.fixedCapacity&&agent.value?.execution==='computer'?(state.value.desktopId??'off'):agent.value?.computer??(state.value?.enabled?'vm':'off'))
|
|
|
const canAllowHost=computed(()=>!!agent.value&&supportsHostEnvironment(agent.value))
|
|
const canAllowHost=computed(()=>!!agent.value&&supportsHostEnvironment(agent.value))
|
|
|
const localVmSelected=computed(()=>canAllowHost.value&&!cloudSelected.value)
|
|
const localVmSelected=computed(()=>canAllowHost.value&&!cloudSelected.value)
|
|
@@ -59,17 +67,20 @@ async function refresh(){
|
|
|
if(nativeSelected.value==='browser'){const b=await apiRequest<BotBrowserState>(`/api/app/agents/${selected.value}/browser`);ready=b.open}
|
|
if(nativeSelected.value==='browser'){const b=await apiRequest<BotBrowserState>(`/api/app/agents/${selected.value}/browser`);ready=b.open}
|
|
|
state.value={enabled:true,container:ready?'running':'stopped',ready,image:true,mode:'shared',maxInstances:1,inUse:false}
|
|
state.value={enabled:true,container:ready?'running':'stopped',ready,image:true,mode:'shared',maxInstances:1,inUse:false}
|
|
|
if(ready){const f=await apiRequest<ComputerFrame>(`/api/app/agents/${selected.value}/computer/frame`);if(version===revision&&!closed)frame.value=f}else frame.value=undefined
|
|
if(ready){const f=await apiRequest<ComputerFrame>(`/api/app/agents/${selected.value}/computer/frame`);if(version===revision&&!closed)frame.value=f}else frame.value=undefined
|
|
|
- error.value='';return
|
|
|
|
|
|
|
+ error.value=''
|
|
|
}
|
|
}
|
|
|
const cloudState=await apiRequest<typeof cloud.value>(`/api/app/agents/${selected.value}/cloud-computer`);if(version!==revision||closed)return;cloud.value=cloudState
|
|
const cloudState=await apiRequest<typeof cloud.value>(`/api/app/agents/${selected.value}/cloud-computer`);if(version!==revision||closed)return;cloud.value=cloudState
|
|
|
- if(cloudSelected.value){
|
|
|
|
|
|
|
+ if(showCloud.value){
|
|
|
state.value={enabled:true,container:cloudState?.running?'running':'stopped',ready:!!cloudState?.connected&&!!cloudState?.running,image:!!cloudState?.configured,mode:'shared',maxInstances:1,inUse:cloudState?.mode==='agent'||cloudState?.mode==='human',controlMode:cloudState?.mode}
|
|
state.value={enabled:true,container:cloudState?.running?'running':'stopped',ready:!!cloudState?.connected&&!!cloudState?.running,image:!!cloudState?.configured,mode:'shared',maxInstances:1,inUse:cloudState?.mode==='agent'||cloudState?.mode==='human',controlMode:cloudState?.mode}
|
|
|
- if(state.value.ready){const image=await apiRequest<ComputerFrame>(`/api/app/agents/${selected.value}/computer/frame`);if(version===revision&&!closed)frame.value=image}else frame.value=undefined
|
|
|
|
|
- error.value='';return
|
|
|
|
|
|
|
+ if(state.value.ready&&!nativeSelected.value){const image=await apiRequest<ComputerFrame>(`/api/app/agents/${selected.value}/computer/frame`);if(version===revision&&!closed)frame.value=image}else if(!nativeSelected.value)frame.value=undefined
|
|
|
|
|
+ error.value=''
|
|
|
|
|
+ }
|
|
|
|
|
+ const wantVm=modern.value?envs.value.vm:(!nativeSelected.value&&!cloudSelected.value)
|
|
|
|
|
+ if(wantVm){
|
|
|
|
|
+ const result=await apiRequest<typeof state.value>(base());if(version!==revision||closed)return;state.value=result
|
|
|
|
|
+ if(result?.container==='running'&&result.ready&&!nativeSelected.value&&!showCloud.value){const image=await apiRequest<ComputerFrame>(`/api/app/agents/${selected.value}/computer/frame`);if(version===revision&&!closed)frame.value=image}
|
|
|
|
|
+ else if(!nativeSelected.value&&!showCloud.value)frame.value=undefined
|
|
|
}
|
|
}
|
|
|
- const result=await apiRequest<typeof state.value>(base());if(version!==revision||closed)return;state.value=result
|
|
|
|
|
- if(result?.container==='running'&&result.ready){const image=await apiRequest<ComputerFrame>(`/api/app/agents/${selected.value}/computer/frame`);if(version===revision&&!closed)frame.value=image}
|
|
|
|
|
- else frame.value=undefined
|
|
|
|
|
error.value=''
|
|
error.value=''
|
|
|
}catch(e){if(version===revision)error.value=e instanceof Error?e.message:'无法读取电脑画面'}finally{if(version===revision)loading.value=false}
|
|
}catch(e){if(version===revision)error.value=e instanceof Error?e.message:'无法读取电脑画面'}finally{if(version===revision)loading.value=false}
|
|
|
}
|
|
}
|
|
@@ -89,7 +100,7 @@ function chooseVmExecution(value:string){
|
|
|
if(computerChoiceDisabled.value||!localVmSelected.value)return
|
|
if(computerChoiceDisabled.value||!localVmSelected.value)return
|
|
|
void run(()=>apiRequest(`/api/app/agents/${selected.value}/computer-selection`,{method:'PUT',body:{computer:'vm',vmExecution:value,allowHostEnvironment:value==='profile'}}))
|
|
void run(()=>apiRequest(`/api/app/agents/${selected.value}/computer-selection`,{method:'PUT',body:{computer:'vm',vmExecution:value,allowHostEnvironment:value==='profile'}}))
|
|
|
}
|
|
}
|
|
|
-async function openDesktop(){if(!agent.value)return;if(cloudSelected.value){await run(()=>apiRequest(`/api/app/agents/${selected.value}/cloud-computer/open`,{method:'POST',body:{}}));if(error.value)return}emit('desktop',agent.value)}
|
|
|
|
|
|
|
+async function openDesktop(backend?:'desktop'|'cloud'|'vm'){if(!agent.value)return;if(backend==='cloud'||(!backend&&cloudSelected.value&&!nativeSelected.value)){await run(()=>apiRequest(`/api/app/agents/${selected.value}/cloud-computer/open`,{method:'POST',body:{}}));if(error.value)return}emit('desktop',agent.value,backend)}
|
|
|
function action(value:LocalVmAction){
|
|
function action(value:LocalVmAction){
|
|
|
if(value==='recreate'&&!confirm('重建这台虚拟机?当前桌面程序将关闭,工作文件和浏览器资料会保留。'))return
|
|
if(value==='recreate'&&!confirm('重建这台虚拟机?当前桌面程序将关闭,工作文件和浏览器资料会保留。'))return
|
|
|
if(value==='remove'&&!confirm('移除这台虚拟机实例以切换镜像?工作文件和浏览器资料会保留。'))return
|
|
if(value==='remove'&&!confirm('移除这台虚拟机实例以切换镜像?工作文件和浏览器资料会保留。'))return
|
|
@@ -109,7 +120,7 @@ onBeforeUnmount(()=>{closed=true;revision++;clearTimeout(timer)})
|
|
|
<p v-if="!agent">当前聊天没有可用的机器人。</p>
|
|
<p v-if="!agent">当前聊天没有可用的机器人。</p>
|
|
|
<template v-else>
|
|
<template v-else>
|
|
|
<div class="screen-caption"><span>{{agent.name}}的电脑</span><small v-if="state?.enabled">{{nativeSelected?(native?.host?.name??'桌面端离线'):cloudSelected?'Grok Bot 云端':'本地虚拟机'}}</small></div>
|
|
<div class="screen-caption"><span>{{agent.name}}的电脑</span><small v-if="state?.enabled">{{nativeSelected?(native?.host?.name??'桌面端离线'):cloudSelected?'Grok Bot 云端':'本地虚拟机'}}</small></div>
|
|
|
- <button class="preview" :disabled="!frame||busy" :aria-label="`打开${agent.name}的桌面`" @click="openDesktop">
|
|
|
|
|
|
|
+ <button class="preview" :disabled="!frame||busy" :aria-label="`打开${agent.name}的桌面`" @click="openDesktop()">
|
|
|
<img v-if="frame" :src="`data:image/png;base64,${frame.data}`" :alt="`${agent.name}的电脑画面`">
|
|
<img v-if="frame" :src="`data:image/png;base64,${frame.data}`" :alt="`${agent.name}的电脑画面`">
|
|
|
<span v-else class="empty"><AppIcon name="monitor" :size="26"/><span>{{loading?'正在检查电脑…':nativeSelected?(nativeSelected==='browser'?'点击打开机器人的独立浏览器':native?.local.ready?'正在读取本机画面…':'请完成本机控制授权'):!state?.enabled?(computerChoice==='auto'?'暂无可用电脑':'未使用电脑'):!state.image?(cloudSelected?'尚未连接 Grok Bot 账号':'本地虚拟机尚未准备'):state.container==='missing'?'尚未创建虚拟机':state.container==='stopped'?'虚拟机已停止':cloudSelected?'打开云端电脑后可查看桌面':'正在连接桌面…'}}</span></span>
|
|
<span v-else class="empty"><AppIcon name="monitor" :size="26"/><span>{{loading?'正在检查电脑…':nativeSelected?(nativeSelected==='browser'?'点击打开机器人的独立浏览器':native?.local.ready?'正在读取本机画面…':'请完成本机控制授权'):!state?.enabled?(computerChoice==='auto'?'暂无可用电脑':'未使用电脑'):!state.image?(cloudSelected?'尚未连接 Grok Bot 账号':'本地虚拟机尚未准备'):state.container==='missing'?'尚未创建虚拟机':state.container==='stopped'?'虚拟机已停止':cloudSelected?'打开云端电脑后可查看桌面':'正在连接桌面…'}}</span></span>
|
|
|
<span v-if="frame" class="open-badge"><AppIcon name="external" :size="12"/>打开桌面</span>
|
|
<span v-if="frame" class="open-badge"><AppIcon name="external" :size="12"/>打开桌面</span>
|
|
@@ -117,7 +128,14 @@ onBeforeUnmount(()=>{closed=true;revision++;clearTimeout(timer)})
|
|
|
<p v-if="error||state?.problem" class="problem" role="alert">{{error||state?.problem}}</p>
|
|
<p v-if="error||state?.problem" class="problem" role="alert">{{error||state?.problem}}</p>
|
|
|
<div class="computer-select">
|
|
<div class="computer-select">
|
|
|
<span>此机器人使用的电脑</span>
|
|
<span>此机器人使用的电脑</span>
|
|
|
- <div ref="computerTabs" class="computer-tabs" role="tablist" aria-label="此机器人使用的电脑" @keydown="moveComputerTab">
|
|
|
|
|
|
|
+ <div v-if="modern" class="env-checks" role="group" aria-label="此机器人使用的电脑(可多选)">
|
|
|
|
|
+ <label class="env-check"><input type="checkbox" :checked="envs.vm" :disabled="computerChoiceDisabled" @change="toggleEnv('vm',($event.target as HTMLInputElement).checked)"><span>本地虚拟机<small>在隔离的本地桌面中工作;执行节点离线时自动改用本机会话</small></span></label>
|
|
|
|
|
+ <label class="env-check"><input type="checkbox" :checked="envs.cloud" :disabled="computerChoiceDisabled" @change="toggleEnv('cloud',($event.target as HTMLInputElement).checked)"><span>云端 · Grok Bot<small>共享云端电脑,持续保存工作</small></span></label>
|
|
|
|
|
+ <label class="env-check"><input type="checkbox" :checked="envs.desktop" :disabled="computerChoiceDisabled" @change="toggleEnv('desktop',($event.target as HTMLInputElement).checked)"><span>本机控制<small>{{native?.hosts?.length?`已连接 ${native.hosts.filter((h:any)=>h.online).length} 台 Mac,工具可按名称选择`:'暂无已连接的 Mac'}}</small></span></label>
|
|
|
|
|
+ <label class="env-check"><input type="checkbox" :checked="envs.browser" :disabled="computerChoiceDisabled" @change="toggleEnv('browser',($event.target as HTMLInputElement).checked)"><span>仅浏览器<small>在连接的 Mac 上使用机器人独立浏览器</small></span></label>
|
|
|
|
|
+ <p class="hint">勾选的环境并列可用;全部取消即不使用任何电脑环境。</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-else ref="computerTabs" class="computer-tabs" role="tablist" aria-label="此机器人使用的电脑" @keydown="moveComputerTab">
|
|
|
<button v-for="option in computerChoices" :id="`computer-tab-${selected}-${option.value}`" :key="option.value" type="button" role="tab" :aria-selected="computerChoice===option.value" :aria-controls="`computer-options-${selected}`" :tabindex="computerChoice===option.value?0:-1" :aria-label="option.label" :title="option.description" :disabled="computerChoiceDisabled||option.disabled" @click="selectComputer(option.value)"><span><AppIcon :name="computerChoice===option.value?'check':option.icon" :size="15"/>{{option.label}}</span><small>{{option.description}}</small></button>
|
|
<button v-for="option in computerChoices" :id="`computer-tab-${selected}-${option.value}`" :key="option.value" type="button" role="tab" :aria-selected="computerChoice===option.value" :aria-controls="`computer-options-${selected}`" :tabindex="computerChoice===option.value?0:-1" :aria-label="option.label" :title="option.description" :disabled="computerChoiceDisabled||option.disabled" @click="selectComputer(option.value)"><span><AppIcon :name="computerChoice===option.value?'check':option.icon" :size="15"/>{{option.label}}</span><small>{{option.description}}</small></button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -152,15 +170,15 @@ onBeforeUnmount(()=>{closed=true;revision++;clearTimeout(timer)})
|
|
|
<p v-if="native?.local.ready&&!native?.local.fullAuthorized" class="hint">授权后,机器人可以在这台 Mac 的用户主目录内读写文件并执行命令;每台电脑单独授权,可随时撤销。</p>
|
|
<p v-if="native?.local.ready&&!native?.local.fullAuthorized" class="hint">授权后,机器人可以在这台 Mac 的用户主目录内读写文件并执行命令;每台电脑单独授权,可随时撤销。</p>
|
|
|
</template>
|
|
</template>
|
|
|
<label v-else>浏览器资料<select :value="native?.browser.profile" :disabled="busy" @change="run(()=>apiRequest(`/api/app/agents/${selected}/browser-profile`,{method:'PUT',body:{profile:($event.target as HTMLSelectElement).value}}))"><option value="persistent">此机器人的浏览器 · 保存登录</option><option value="temporary">临时浏览器 · 退出桌面端后清除</option></select></label>
|
|
<label v-else>浏览器资料<select :value="native?.browser.profile" :disabled="busy" @change="run(()=>apiRequest(`/api/app/agents/${selected}/browser-profile`,{method:'PUT',body:{profile:($event.target as HTMLSelectElement).value}}))"><option value="persistent">此机器人的浏览器 · 保存登录</option><option value="temporary">临时浏览器 · 退出桌面端后清除</option></select></label>
|
|
|
- <button class="primary" :disabled="busy||!native?.online||(nativeSelected==='local'&&!native?.local.ready)" @click="openDesktop">{{nativeSelected==='browser'?'打开浏览器':'接管本机'}}</button>
|
|
|
|
|
|
|
+ <button class="primary" :disabled="busy||!native?.online||(nativeSelected==='local'&&!native?.local.ready)" @click="openDesktop()">{{nativeSelected==='browser'?'打开浏览器':'接管本机'}}</button>
|
|
|
</template>
|
|
</template>
|
|
|
- <template v-else-if="cloudSelected">
|
|
|
|
|
|
|
+ <template v-if="showCloud">
|
|
|
<p>多个机器人共用同一台 Grok Bot 云端虚拟机、工作文件和浏览器登录。基础机器人和聊天记录保持独立。</p>
|
|
<p>多个机器人共用同一台 Grok Bot 云端虚拟机、工作文件和浏览器登录。基础机器人和聊天记录保持独立。</p>
|
|
|
- <button v-if="cloud?.configured" class="primary" :disabled="busy" @click="openDesktop">{{state?.controlMode==='human'?'打开控制中的桌面':'接管云端电脑'}}</button>
|
|
|
|
|
|
|
+ <button v-if="cloud?.configured" class="primary" :disabled="busy" @click="openDesktop('cloud')">{{state?.controlMode==='human'?'打开控制中的桌面':'接管云端电脑'}}</button>
|
|
|
<button @click="connectionSettings=!connectionSettings">{{cloud?.configured?'云端连接设置':'连接 Grok Bot'}}</button>
|
|
<button @click="connectionSettings=!connectionSettings">{{cloud?.configured?'云端连接设置':'连接 Grok Bot'}}</button>
|
|
|
<p v-if="!isAdmin&&!cloud?.configured" class="hint">请由管理员为当前账号配置 Grok Bot 连接。</p>
|
|
<p v-if="!isAdmin&&!cloud?.configured" class="hint">请由管理员为当前账号配置 Grok Bot 连接。</p>
|
|
|
</template>
|
|
</template>
|
|
|
- <template v-else-if="state?.enabled">
|
|
|
|
|
|
|
+ <template v-if="showVm&&state">
|
|
|
<p>{{agent.temporaryGoalId?'临时助手的电脑由当前任务管理':state.mode==='shared'?'共享虚拟机 · 与其他成员共用桌面和工作文件':'此机器人的独立虚拟机'}}</p>
|
|
<p>{{agent.temporaryGoalId?'临时助手的电脑由当前任务管理':state.mode==='shared'?'共享虚拟机 · 与其他成员共用桌面和工作文件':'此机器人的独立虚拟机'}}</p>
|
|
|
<template v-if="!state.fixedCapacity&&state.images?.length&&!agent.temporaryGoalId">
|
|
<template v-if="!state.fixedCapacity&&state.images?.length&&!agent.temporaryGoalId">
|
|
|
<label>虚拟机镜像<select :value="state.imageKey??''" :disabled="busy||state.inUse||state.container!=='missing'" @change="run(()=>apiRequest(base()+'/image',{method:'PUT',body:{imageKey:($event.target as HTMLSelectElement).value}}))"><option v-if="!state.imageKey" value="" disabled>{{state.image?'当前保留的镜像':'请选择已准备的镜像'}}</option><option v-for="image in state.images" :key="image.key" :value="image.key" :disabled="!image.ready">{{image.name}}{{image.ready?'':' · 未准备'}}</option></select></label>
|
|
<label>虚拟机镜像<select :value="state.imageKey??''" :disabled="busy||state.inUse||state.container!=='missing'" @change="run(()=>apiRequest(base()+'/image',{method:'PUT',body:{imageKey:($event.target as HTMLSelectElement).value}}))"><option v-if="!state.imageKey" value="" disabled>{{state.image?'当前保留的镜像':'请选择已准备的镜像'}}</option><option v-for="image in state.images" :key="image.key" :value="image.key" :disabled="!image.ready">{{image.name}}{{image.ready?'':' · 未准备'}}</option></select></label>
|
|
@@ -170,7 +188,7 @@ onBeforeUnmount(()=>{closed=true;revision++;clearTimeout(timer)})
|
|
|
<button v-else-if="!state.fixedCapacity&&state.container==='missing'&&!agent.temporaryGoalId" class="primary" :disabled="busy" @click="action('create')">创建 {{agent.name}} 的虚拟机</button>
|
|
<button v-else-if="!state.fixedCapacity&&state.container==='missing'&&!agent.temporaryGoalId" class="primary" :disabled="busy" @click="action('create')">创建 {{agent.name}} 的虚拟机</button>
|
|
|
<button v-else-if="!state.fixedCapacity&&state.container==='stopped'&&!agent.temporaryGoalId" class="primary" :disabled="busy" @click="action('start')">启动虚拟机</button>
|
|
<button v-else-if="!state.fixedCapacity&&state.container==='stopped'&&!agent.temporaryGoalId" class="primary" :disabled="busy" @click="action('start')">启动虚拟机</button>
|
|
|
<template v-else-if="state.container==='running'">
|
|
<template v-else-if="state.container==='running'">
|
|
|
- <button class="primary" :disabled="!state.ready||busy" @click="openDesktop">打开桌面</button>
|
|
|
|
|
|
|
+ <button class="primary" :disabled="!state.ready||busy" @click="openDesktop(modern?'vm':undefined)">打开桌面</button>
|
|
|
<button v-if="state.mode==='per-bot'" :disabled="busy" @click="emit('workspace',agent)"><AppIcon name="panel"/>打开双桌面</button>
|
|
<button v-if="state.mode==='per-bot'" :disabled="busy" @click="emit('workspace',agent)"><AppIcon name="panel"/>打开双桌面</button>
|
|
|
<p class="control-state">{{state.controlMode==='human'?'你或其他操作者正在控制电脑':state.inUse?'机器人正在操作':'仅查看 · 虚拟机空闲'}}</p>
|
|
<p class="control-state">{{state.controlMode==='human'?'你或其他操作者正在控制电脑':state.inUse?'机器人正在操作':'仅查看 · 虚拟机空闲'}}</p>
|
|
|
</template>
|
|
</template>
|