Phase 007: migrate every remaining view from naive-ui to Arco Design Vue
The project now has exactly one UI library. Every naive-ui component in
packages/client/src is replaced by its Arco equivalent across 24 views and
roughly 80 components, and naive-ui is dropped from package.json along with
its transitive vueuc / @css-render / vdirs / seemly dependencies.
The migration is rendering-layer only. No store logic, no API contract, and
no /chat-run payload was touched.
Three classes of leftover were invisible to `grep naive-ui` and are now
covered by tests/client/no-naive-ui.test.ts, which asserts all three:
- dead .n-* class names in style blocks
- dead --n-* custom properties
- naive-ui in package.json
Notable behavioural traps handled along the way, recorded in
docs/arco-migration-rules.md:
- a-input-password defaults to masked; passing default-visibility="false"
would have exposed API keys in clear text
- a-upload fires a real XHR unless auto-upload is false
- nested Trigger components fight over the first element child, so the
group-chat settings dropdown was flattened
- Arco renders tooltip default-slot text inline, which was silently
collapsing the chat and group-chat toolbars
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { NCollapse, NCollapseItem } from 'naive-ui'
|
||||
import KanbanTaskCard from './KanbanTaskCard.vue'
|
||||
import type { KanbanTask, KanbanTaskStatus } from '@/api/hermes/kanban'
|
||||
|
||||
@@ -35,8 +34,8 @@ const headerTitle = computed(() => `${statusIcon.value} ${statusLabel.value} (
|
||||
|
||||
<template>
|
||||
<div class="kanban-column">
|
||||
<NCollapse :default-expanded-names="[status]" display-directive="show">
|
||||
<NCollapseItem :title="headerTitle" :name="status">
|
||||
<a-collapse :default-active-key="[status]" :bordered="false">
|
||||
<a-collapse-item :key="status" :header="headerTitle">
|
||||
<KanbanTaskCard
|
||||
v-for="task in tasks"
|
||||
:key="task.id"
|
||||
@@ -46,8 +45,8 @@ const headerTitle = computed(() => `${statusIcon.value} ${statusLabel.value} (
|
||||
<div v-if="tasks.length === 0" class="column-empty">
|
||||
{{ t('kanban.noTasks') }}
|
||||
</div>
|
||||
</NCollapseItem>
|
||||
</NCollapse>
|
||||
</a-collapse-item>
|
||||
</a-collapse>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -61,20 +60,17 @@ const headerTitle = computed(() => `${statusIcon.value} ${statusLabel.value} (
|
||||
border-radius: $radius-md;
|
||||
border: 1px solid $border-light;
|
||||
|
||||
:deep(.n-collapse) {
|
||||
--n-title-font-size: 13px;
|
||||
--n-title-font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(.n-collapse-item__header-main) {
|
||||
:deep(.arco-collapse-item-header-title) {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: $text-primary;
|
||||
}
|
||||
|
||||
:deep(.n-collapse-item__content-wrapper) {
|
||||
:deep(.arco-collapse-item-content-box) {
|
||||
padding: 0 10px 10px;
|
||||
}
|
||||
|
||||
:deep(.n-collapse-item) {
|
||||
:deep(.arco-collapse-item) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { NModal, NForm, NFormItem, NInput, NInputNumber, NSelect, NButton, NCheckbox } from 'naive-ui'
|
||||
import { useMessage } from '@/composables/useMessage'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useKanbanStore } from '@/stores/hermes/kanban'
|
||||
@@ -35,6 +34,23 @@ const saving = ref(false)
|
||||
const skillsLoading = ref(false)
|
||||
const skillOptions = ref<Array<{ label: string; value: string }>>([])
|
||||
|
||||
// a-form 的 model 是必填 prop;这里的字段本来就各自是独立的 ref,
|
||||
// 收集成一个对象只是为了满足 Arco 的类型要求,不改变任何提交逻辑。
|
||||
const formModel = computed(() => ({
|
||||
title: title.value,
|
||||
body: body.value,
|
||||
assignee: assignee.value,
|
||||
priority: priority.value,
|
||||
tenant: tenant.value,
|
||||
workspaceKind: workspaceKind.value,
|
||||
workspacePath: workspacePath.value,
|
||||
branch: branch.value,
|
||||
skills: skills.value,
|
||||
maxRuntime: maxRuntime.value,
|
||||
maxRetries: maxRetries.value,
|
||||
goalMaxTurns: goalMaxTurns.value,
|
||||
}))
|
||||
|
||||
const priorityOptions = computed(() => [
|
||||
{ label: t('kanban.card.priority.low'), value: 1 },
|
||||
{ label: t('kanban.card.priority.medium'), value: 2 },
|
||||
@@ -128,78 +144,101 @@ async function handleSubmit() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NModal :show="true" preset="dialog" :title="t('kanban.createTask')" style="width: 480px;" @close="emit('close')">
|
||||
<NForm label-placement="top">
|
||||
<NFormItem :label="t('kanban.form.title')">
|
||||
<NInput v-model:value="title" :placeholder="t('kanban.form.titlePlaceholder')" />
|
||||
</NFormItem>
|
||||
<NFormItem :label="t('kanban.form.body')">
|
||||
<NInput v-model:value="body" type="textarea" :rows="3" :placeholder="t('kanban.form.bodyPlaceholder')" />
|
||||
</NFormItem>
|
||||
<NFormItem :label="t('kanban.form.assignee')">
|
||||
<NSelect v-model:value="assignee" :options="assigneeOptions" :placeholder="t('kanban.form.selectAssignee')" clearable />
|
||||
</NFormItem>
|
||||
<NFormItem :label="t('kanban.form.priority')">
|
||||
<NSelect v-model:value="priority" :options="priorityOptions" :placeholder="t('kanban.form.selectPriority')" clearable />
|
||||
</NFormItem>
|
||||
<NFormItem :label="t('kanban.form.tenant')">
|
||||
<NInput v-model:value="tenant" :placeholder="t('kanban.form.tenantPlaceholder')" />
|
||||
</NFormItem>
|
||||
<NFormItem :label="t('kanban.form.workspace')">
|
||||
<NSelect v-model:value="workspaceKind" :options="workspaceOptions" />
|
||||
</NFormItem>
|
||||
<NFormItem v-if="workspaceKind !== 'scratch'" :label="t('kanban.form.workspacePath')">
|
||||
<NInput v-model:value="workspacePath" :placeholder="t('kanban.form.workspacePathPlaceholder')" />
|
||||
</NFormItem>
|
||||
<NFormItem v-if="workspaceKind === 'worktree'" :label="t('kanban.form.branch')">
|
||||
<NInput v-model:value="branch" :placeholder="t('kanban.form.branchPlaceholder')" />
|
||||
</NFormItem>
|
||||
<NFormItem :label="t('kanban.form.skills')">
|
||||
<NSelect
|
||||
v-model:value="skills"
|
||||
<a-modal
|
||||
:visible="true"
|
||||
:title="t('kanban.createTask')"
|
||||
:style="{ width: '480px' }"
|
||||
:mask-closable="false"
|
||||
:esc-to-close="false"
|
||||
@cancel="emit('close')"
|
||||
>
|
||||
<a-form :model="formModel" layout="vertical">
|
||||
<a-form-item :label="t('kanban.form.title')">
|
||||
<a-input v-model="title" :placeholder="t('kanban.form.titlePlaceholder')" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('kanban.form.body')">
|
||||
<a-textarea v-model="body" :textarea-attrs="{ rows: 3 }" :placeholder="t('kanban.form.bodyPlaceholder')" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('kanban.form.assignee')">
|
||||
<a-select
|
||||
:model-value="assignee ?? undefined"
|
||||
:options="assigneeOptions"
|
||||
:placeholder="t('kanban.form.selectAssignee')"
|
||||
allow-clear
|
||||
@change="value => assignee = (value as string) ?? null"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('kanban.form.priority')">
|
||||
<a-select
|
||||
:model-value="priority ?? undefined"
|
||||
:options="priorityOptions"
|
||||
:placeholder="t('kanban.form.selectPriority')"
|
||||
allow-clear
|
||||
@change="value => priority = (value as number) ?? null"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('kanban.form.tenant')">
|
||||
<a-input v-model="tenant" :placeholder="t('kanban.form.tenantPlaceholder')" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('kanban.form.workspace')">
|
||||
<a-select v-model="workspaceKind" :options="workspaceOptions" />
|
||||
</a-form-item>
|
||||
<a-form-item v-if="workspaceKind !== 'scratch'" :label="t('kanban.form.workspacePath')">
|
||||
<a-input v-model="workspacePath" :placeholder="t('kanban.form.workspacePathPlaceholder')" />
|
||||
</a-form-item>
|
||||
<a-form-item v-if="workspaceKind === 'worktree'" :label="t('kanban.form.branch')">
|
||||
<a-input v-model="branch" :placeholder="t('kanban.form.branchPlaceholder')" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('kanban.form.skills')">
|
||||
<a-select
|
||||
v-model="skills"
|
||||
multiple
|
||||
filterable
|
||||
clearable
|
||||
allow-search
|
||||
allow-clear
|
||||
:loading="skillsLoading"
|
||||
:options="skillOptions"
|
||||
:placeholder="t('kanban.form.skillsPlaceholder')"
|
||||
/>
|
||||
</NFormItem>
|
||||
</a-form-item>
|
||||
<div class="advanced-row">
|
||||
<NCheckbox v-model:checked="triage">{{ t('kanban.form.triage') }}</NCheckbox>
|
||||
<NCheckbox v-model:checked="goalMode">{{ t('kanban.form.goalMode') }}</NCheckbox>
|
||||
<a-checkbox v-model="triage">{{ t('kanban.form.triage') }}</a-checkbox>
|
||||
<a-checkbox v-model="goalMode">{{ t('kanban.form.goalMode') }}</a-checkbox>
|
||||
</div>
|
||||
<div class="advanced-grid">
|
||||
<NFormItem :label="t('kanban.form.maxRuntime')">
|
||||
<NInput v-model:value="maxRuntime" :placeholder="t('kanban.form.maxRuntimePlaceholder')" />
|
||||
</NFormItem>
|
||||
<NFormItem :label="t('kanban.form.maxRetries')">
|
||||
<NInputNumber
|
||||
v-model:value="maxRetries"
|
||||
<a-form-item :label="t('kanban.form.maxRuntime')">
|
||||
<a-input v-model="maxRuntime" :placeholder="t('kanban.form.maxRuntimePlaceholder')" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('kanban.form.maxRetries')">
|
||||
<a-input-number
|
||||
:model-value="maxRetries ?? undefined"
|
||||
:min="1"
|
||||
:max="100"
|
||||
:precision="0"
|
||||
:placeholder="t('kanban.form.maxRetriesPlaceholder')"
|
||||
clearable
|
||||
allow-clear
|
||||
style="width: 100%"
|
||||
@change="value => maxRetries = value ?? null"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem v-if="goalMode" :label="t('kanban.form.goalMaxTurns')">
|
||||
<NInputNumber
|
||||
v-model:value="goalMaxTurns"
|
||||
</a-form-item>
|
||||
<a-form-item v-if="goalMode" :label="t('kanban.form.goalMaxTurns')">
|
||||
<a-input-number
|
||||
:model-value="goalMaxTurns ?? undefined"
|
||||
:min="1"
|
||||
:max="100"
|
||||
:precision="0"
|
||||
:placeholder="t('kanban.form.goalMaxTurnsPlaceholder')"
|
||||
clearable
|
||||
allow-clear
|
||||
style="width: 100%"
|
||||
@change="value => goalMaxTurns = value ?? null"
|
||||
/>
|
||||
</NFormItem>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</NForm>
|
||||
<template #action>
|
||||
<NButton @click="emit('close')">{{ t('common.cancel') }}</NButton>
|
||||
<NButton type="primary" :loading="saving" @click="handleSubmit">{{ t('common.create') }}</NButton>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<a-button @click="emit('close')">{{ t('common.cancel') }}</a-button>
|
||||
<a-button type="primary" :loading="saving" @click="handleSubmit">{{ t('common.create') }}</a-button>
|
||||
</template>
|
||||
</NModal>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { NTooltip } from 'naive-ui'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import ProfileAvatar from '@/components/hermes/profiles/ProfileAvatar.vue'
|
||||
import type { KanbanTask } from '@/api/hermes/kanban'
|
||||
@@ -40,21 +39,18 @@ const priorityText = computed(() => {
|
||||
<div class="kanban-task-card" :class="`status-${task.status}`" @click="emit('click', task.id)">
|
||||
<div class="card-title">{{ task.title }}</div>
|
||||
<div class="card-meta">
|
||||
<NTooltip v-if="task.assignee" trigger="hover">
|
||||
<template #trigger>
|
||||
<span class="meta-tag assignee-tag">
|
||||
<ProfileAvatar
|
||||
class="assignee-profile-avatar"
|
||||
:name="task.assignee"
|
||||
:avatar="assigneeAvatar"
|
||||
:size="18"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span>{{ task.assignee }}</span>
|
||||
</span>
|
||||
</template>
|
||||
{{ t('kanban.card.assigneeTooltip') }}
|
||||
</NTooltip>
|
||||
<a-tooltip v-if="task.assignee" :content="t('kanban.card.assigneeTooltip')">
|
||||
<span class="meta-tag assignee-tag">
|
||||
<ProfileAvatar
|
||||
class="assignee-profile-avatar"
|
||||
:name="task.assignee"
|
||||
:avatar="assigneeAvatar"
|
||||
:size="18"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span>{{ task.assignee }}</span>
|
||||
</span>
|
||||
</a-tooltip>
|
||||
<span v-if="task.priority >= 2" class="meta-tag priority-tag" :class="priorityLabel">{{ priorityText }}</span>
|
||||
<span class="meta-time">{{ timeAgo }}</span>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { NDrawer, NDrawerContent, NButton, NSelect, NInput, NSpin, NModal } from 'naive-ui'
|
||||
import { useMessage } from '@/composables/useMessage'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
@@ -367,9 +366,14 @@ function handleNavigateTask(taskId: string) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NDrawer :show="!!taskId" :width="420" placement="right" @update:show="(v: boolean) => { if (!v) emit('close') }">
|
||||
<NDrawerContent :title="detail?.task.title || ''" closable>
|
||||
<NSpin :show="loading">
|
||||
<a-drawer
|
||||
:visible="!!taskId"
|
||||
:width="420"
|
||||
placement="right"
|
||||
:title="detail?.task.title || ''"
|
||||
@cancel="emit('close')"
|
||||
>
|
||||
<a-spin :loading="loading">
|
||||
<template v-if="detail">
|
||||
<!-- Metadata -->
|
||||
<div class="detail-section">
|
||||
@@ -436,35 +440,42 @@ function handleNavigateTask(taskId: string) {
|
||||
<div class="section-title">{{ t('kanban.action.title') }}</div>
|
||||
<div class="action-group">
|
||||
<template v-if="!showCompleteInput">
|
||||
<NButton size="small" @click="showCompleteInput = true">
|
||||
<a-button size="small" @click="showCompleteInput = true">
|
||||
{{ t('kanban.action.complete') }}
|
||||
</NButton>
|
||||
</a-button>
|
||||
</template>
|
||||
<div v-else class="complete-input">
|
||||
<NInput v-model:value="completeSummary" size="small" :placeholder="t('kanban.action.completeSummary')" />
|
||||
<NButton size="small" type="primary" @click="handleComplete">{{ t('common.ok') }}</NButton>
|
||||
<NButton size="small" @click="showCompleteInput = false; completeSummary = ''">{{ t('common.cancel') }}</NButton>
|
||||
<a-input v-model="completeSummary" size="small" :placeholder="t('kanban.action.completeSummary')" />
|
||||
<a-button size="small" type="primary" @click="handleComplete">{{ t('common.ok') }}</a-button>
|
||||
<a-button size="small" @click="showCompleteInput = false; completeSummary = ''">{{ t('common.cancel') }}</a-button>
|
||||
</div>
|
||||
<template v-if="detail.task.status === 'blocked'">
|
||||
<NButton size="small" @click="handleUnblock">{{ t('kanban.action.unblock') }}</NButton>
|
||||
<a-button size="small" @click="handleUnblock">{{ t('kanban.action.unblock') }}</a-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<NButton v-if="!showBlockInput" size="small" @click="showBlockInput = true">{{ t('kanban.action.block') }}</NButton>
|
||||
<a-button v-if="!showBlockInput" size="small" @click="showBlockInput = true">{{ t('kanban.action.block') }}</a-button>
|
||||
<div v-else class="block-input">
|
||||
<NInput v-model:value="blockReason" size="small" :placeholder="t('kanban.action.blockReason')" />
|
||||
<NButton size="small" type="primary" @click="handleBlock">{{ t('common.ok') }}</NButton>
|
||||
<a-input v-model="blockReason" size="small" :placeholder="t('kanban.action.blockReason')" />
|
||||
<a-button size="small" type="primary" @click="handleBlock">{{ t('common.ok') }}</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="detail.task.status !== 'running'" class="assign-group">
|
||||
<NSelect v-model:value="assignProfile" :options="assigneeOptions" size="small" :placeholder="t('kanban.action.assignTo')" style="flex: 1;" />
|
||||
<NButton size="small" :disabled="!assignProfile" @click="handleAssign">{{ t('kanban.action.assign') }}</NButton>
|
||||
<a-select
|
||||
:model-value="assignProfile ?? undefined"
|
||||
:options="assigneeOptions"
|
||||
size="small"
|
||||
:placeholder="t('kanban.action.assignTo')"
|
||||
style="flex: 1;"
|
||||
@change="value => assignProfile = (value as string) ?? null"
|
||||
/>
|
||||
<a-button size="small" :disabled="!assignProfile" @click="handleAssign">{{ t('kanban.action.assign') }}</a-button>
|
||||
</div>
|
||||
<div class="recovery-group">
|
||||
<NInput v-model:value="recoveryReason" size="small" :placeholder="t('kanban.action.recoveryReason')" />
|
||||
<NButton size="small" secondary @click="handleReclaim">{{ t('kanban.action.reclaim') }}</NButton>
|
||||
<NButton size="small" secondary :disabled="!assignProfile" @click="handleReassign">{{ t('kanban.action.reassign') }}</NButton>
|
||||
<NButton v-if="detail.task.status === 'triage'" size="small" secondary @click="handleSpecify">{{ t('kanban.action.specify') }}</NButton>
|
||||
<a-input v-model="recoveryReason" size="small" :placeholder="t('kanban.action.recoveryReason')" />
|
||||
<a-button size="small" type="secondary" @click="handleReclaim">{{ t('kanban.action.reclaim') }}</a-button>
|
||||
<a-button size="small" type="secondary" :disabled="!assignProfile" @click="handleReassign">{{ t('kanban.action.reassign') }}</a-button>
|
||||
<a-button v-if="detail.task.status === 'triage'" size="small" type="secondary" @click="handleSpecify">{{ t('kanban.action.specify') }}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -472,7 +483,7 @@ function handleNavigateTask(taskId: string) {
|
||||
<div v-if="detail.runs.length > 0" class="detail-section">
|
||||
<div class="section-title" style="cursor: pointer;" @click="searchTaskSessions">
|
||||
{{ t('kanban.detail.sessions') }}
|
||||
<NSpin v-if="sessionLoading" :size="12" style="margin-left: 6px;" />
|
||||
<a-spin v-if="sessionLoading" :size="12" style="margin-left: 6px;" />
|
||||
</div>
|
||||
<div v-if="showSessions && sessionResults.length > 0" class="session-list">
|
||||
<div v-for="session in sessionResults" :key="session.id" class="session-item" @click="router.push({ name: 'hermes.chat', query: { session: session.id } })">
|
||||
@@ -493,8 +504,8 @@ function handleNavigateTask(taskId: string) {
|
||||
<span>{{ t('kanban.detail.runs') }}</span>
|
||||
<span class="run-history-range">{{ runHistoryRange }}</span>
|
||||
<div v-if="runHistoryPageCount > 1" class="run-history-controls">
|
||||
<NButton size="tiny" secondary :disabled="runHistoryPage <= 1" @click="runHistoryPage -= 1">‹</NButton>
|
||||
<NButton size="tiny" secondary :disabled="runHistoryPage >= runHistoryPageCount" @click="runHistoryPage += 1">›</NButton>
|
||||
<a-button size="mini" type="secondary" :disabled="runHistoryPage <= 1" @click="runHistoryPage -= 1">‹</a-button>
|
||||
<a-button size="mini" type="secondary" :disabled="runHistoryPage >= runHistoryPageCount" @click="runHistoryPage += 1">›</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="run in visibleRuns" :key="run.id" class="run-item">
|
||||
@@ -522,16 +533,16 @@ function handleNavigateTask(taskId: string) {
|
||||
<div v-if="canMutateTask" class="detail-section">
|
||||
<div class="section-title">{{ t('kanban.detail.addComment') }}</div>
|
||||
<div class="comment-input">
|
||||
<NInput v-model:value="commentBody" type="textarea" :rows="3" :placeholder="t('kanban.detail.commentPlaceholder')" />
|
||||
<NButton size="small" type="primary" :disabled="!commentBody.trim()" @click="handleAddComment">{{ t('common.add') }}</NButton>
|
||||
<a-textarea v-model="commentBody" :textarea-attrs="{ rows: 3 }" :placeholder="t('kanban.detail.commentPlaceholder')" />
|
||||
<a-button size="small" type="primary" :disabled="!commentBody.trim()" @click="handleAddComment">{{ t('common.add') }}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="section-title">{{ t('kanban.detail.operations') }}</div>
|
||||
<div class="action-group">
|
||||
<NButton size="small" secondary :loading="taskLogLoading" @click="handleLoadLog">{{ t('kanban.action.loadLog') }}</NButton>
|
||||
<NButton size="small" secondary :loading="diagnosticsLoading" @click="handleLoadDiagnostics">{{ t('kanban.action.loadDiagnostics') }}</NButton>
|
||||
<a-button size="small" type="secondary" :loading="taskLogLoading" @click="handleLoadLog">{{ t('kanban.action.loadLog') }}</a-button>
|
||||
<a-button size="small" type="secondary" :loading="diagnosticsLoading" @click="handleLoadDiagnostics">{{ t('kanban.action.loadDiagnostics') }}</a-button>
|
||||
</div>
|
||||
<pre v-if="taskLog !== null" class="log-output">{{ taskLog }}</pre>
|
||||
<pre v-if="diagnostics !== null" class="log-output">{{ JSON.stringify(diagnostics, null, 2) }}</pre>
|
||||
@@ -546,16 +557,22 @@ function handleNavigateTask(taskId: string) {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</NSpin>
|
||||
</NDrawerContent>
|
||||
</NDrawer>
|
||||
</a-spin>
|
||||
</a-drawer>
|
||||
|
||||
<!-- Session messages modal (click result summary) -->
|
||||
<NModal v-if="historySession" :show="showMessagesModal" preset="card" :title="detail?.task.title || ''" :style="{ width: '900px', maxWidth: 'calc(100vw - 48px)' }" @close="showMessagesModal = false">
|
||||
<a-modal
|
||||
v-if="historySession"
|
||||
v-model:visible="showMessagesModal"
|
||||
:title="detail?.task.title || ''"
|
||||
:style="{ width: '900px', maxWidth: 'calc(100vw - 48px)' }"
|
||||
:footer="false"
|
||||
@close="showMessagesModal = false"
|
||||
>
|
||||
<div class="messages-modal-body">
|
||||
<HistoryMessageList :session="historySession" />
|
||||
</div>
|
||||
</NModal>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -943,7 +960,7 @@ function handleNavigateTask(taskId: string) {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.comment-input :deep(.n-input) {
|
||||
.comment-input :deep(.arco-textarea-wrapper) {
|
||||
flex: 1;
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user