style: 更新多个组件以使用分段控件替代按钮组

This commit is contained in:
Wenjie Zhang 2025-10-14 01:46:43 +08:00
parent edc4caaf9c
commit be40799af9
5 changed files with 83 additions and 110 deletions

View File

@ -350,9 +350,7 @@ def check_and_migrate(db_path: str):
logger.warning(f" - {issue}")
if os.path.exists(db_path):
logger.info(
"建议运行迁移脚本: docker exec api-dev python /app/scripts/migrate_user_soft_delete.py"
)
logger.info("建议运行迁移脚本: docker exec api-dev python /app/scripts/migrate_user_soft_delete.py")
migrator = DatabaseMigrator(db_path)

View File

@ -234,7 +234,7 @@
<a-modal
v-model:open="toolsModalOpen"
title="选择工具"
:width="600"
:width="800"
:footer="null"
:maskClosable="false"
class="tools-modal"
@ -569,7 +569,7 @@ watch(() => props.isOpen, (newVal) => {
<style lang="less" scoped>
@padding-bottom: 40px;
@padding-bottom: 0px;
.agent-config-sidebar {
position: relative;
width: 0;
@ -642,7 +642,7 @@ watch(() => props.isOpen, (newVal) => {
.sidebar-footer {
position: sticky;
bottom: 16px;
bottom: 0px;
padding: 12px 0;
border-top: 1px solid var(--gray-100);
background: #fff;
@ -1159,4 +1159,4 @@ watch(() => props.isOpen, (newVal) => {
width: 100vw;
}
}
</style>
</style>

View File

@ -9,36 +9,10 @@
<div class="task-center">
<div class="task-toolbar">
<div class="task-filter-group">
<a-button-group>
<a-button
:type="isActiveFilter('all') ? 'primary' : 'default'"
@click="setFilter('all')"
>
全部
<span class="filter-count">{{ totalCount }}</span>
</a-button>
<a-button
:type="isActiveFilter('active') ? 'primary' : 'default'"
@click="setFilter('active')"
>
进行中
<span class="filter-count">{{ inProgressCount }}</span>
</a-button>
<a-button
:type="isActiveFilter('success') ? 'primary' : 'default'"
@click="setFilter('success')"
>
已完成
<span class="filter-count">{{ completedCount }}</span>
</a-button>
<a-button
:type="isActiveFilter('failed') ? 'primary' : 'default'"
@click="setFilter('failed')"
>
失败
<span class="filter-count">{{ failedCount }}</span>
</a-button>
</a-button-group>
<a-segmented
v-model:value="statusFilter"
:options="taskFilterOptions"
/>
</div>
<div class="task-toolbar-actions">
<a-button
@ -135,7 +109,6 @@ import { computed, h, onBeforeUnmount, watch, ref } from 'vue'
import { Modal } from 'ant-design-vue'
import { useTaskerStore } from '@/stores/tasker'
import { storeToRefs } from 'pinia'
import { ReloadOutlined } from '@ant-design/icons-vue'
import { formatFullDateTime, formatRelative, parseToShanghai } from '@/utils/time'
const taskerStore = useTaskerStore()
@ -146,6 +119,48 @@ const tasks = computed(() => sortedTasks.value)
const loadingState = computed(() => Boolean(loading.value))
const lastErrorState = computed(() => lastError.value)
const statusFilter = ref('all')
const inProgressCount = computed(
() => tasks.value.filter((task) => ACTIVE_CLASS_STATUSES.has(task.status)).length
)
const completedCount = computed(() => tasks.value.filter((task) => task.status === 'success').length)
const failedCount = computed(
() => tasks.value.filter((task) => FAILED_STATUSES.has(task.status)).length
)
const totalCount = computed(() => tasks.value.length)
const taskFilterOptions = computed(() => [
{
label: () =>
h('span', { class: 'task-filter-option' }, [
'全部',
h('span', { class: 'filter-count' }, totalCount.value)
]),
value: 'all'
},
{
label: () =>
h('span', { class: 'task-filter-option' }, [
'进行中',
h('span', { class: 'filter-count' }, inProgressCount.value)
]),
value: 'active'
},
{
label: () =>
h('span', { class: 'task-filter-option' }, [
'已完成',
h('span', { class: 'filter-count' }, completedCount.value)
]),
value: 'success'
},
{
label: () =>
h('span', { class: 'task-filter-option' }, [
'失败',
h('span', { class: 'filter-count' }, failedCount.value)
]),
value: 'failed'
}
])
const filteredTasks = computed(() => {
const list = tasks.value
@ -321,22 +336,6 @@ function canCancel(task) {
return ['pending', 'running', 'queued'].includes(task.status) && !task.cancel_requested
}
const inProgressCount = computed(
() => tasks.value.filter((task) => ACTIVE_CLASS_STATUSES.has(task.status)).length
)
const completedCount = computed(() => tasks.value.filter((task) => task.status === 'success').length)
const failedCount = computed(
() => tasks.value.filter((task) => FAILED_STATUSES.has(task.status)).length
)
const totalCount = computed(() => tasks.value.length)
function setFilter(value) {
statusFilter.value = value
}
function isActiveFilter(value) {
return statusFilter.value === value
}
</script>
<style scoped lang="less">
.task-center {
@ -365,13 +364,7 @@ function isActiveFilter(value) {
gap: 4px;
}
.task-filter-group :deep(.ant-btn) {
display: inline-flex;
align-items: center;
gap: 4px;
}
.filter-count {
:deep(.filter-count) {
margin-left: 2px;
font-size: 12px;
color: #94a3b8;

View File

@ -7,11 +7,11 @@
:footer="null"
>
<a-space style="margin-bottom: 16px">
<a-radio-group v-model:value="feedbackFilter" @change="loadFeedbacks">
<a-radio-button value="all">全部</a-radio-button>
<a-radio-button value="like">点赞</a-radio-button>
<a-radio-button value="dislike">点踩</a-radio-button>
</a-radio-group>
<a-segmented
v-model:value="feedbackFilter"
:options="feedbackOptions"
@change="loadFeedbacks"
/>
</a-space>
<!-- 卡片列表 -->
@ -92,7 +92,7 @@
</template>
<script setup>
import { ref, reactive, watch } from 'vue'
import { ref, watch } from 'vue'
import { message } from 'ant-design-vue'
import { LikeOutlined, DislikeOutlined, ClockCircleOutlined } from '@ant-design/icons-vue'
import { dashboardApi } from '@/apis/dashboard_api'
@ -113,6 +113,11 @@ const modalVisible = ref(false)
const feedbacks = ref([])
const loadingFeedbacks = ref(false)
const feedbackFilter = ref('all')
const feedbackOptions = [
{ label: '全部', value: 'all' },
{ label: '点赞', value: 'like' },
{ label: '点踩', value: 'dislike' }
]
//
const show = () => {

View File

@ -18,19 +18,19 @@
<h3>检索配置</h3>
<div class="section">
<div class="card card-select">
<span class="label">对话模型</span>
<span class="label">{{ items?.default_model?.des || '默认对话模型' }}</span>
<ModelSelectorComponent
@select-model="handleChatModelSelect"
:model_name="configStore.config?.model_name"
:model_provider="configStore.config?.model_provider"
:model_spec="configStore.config?.default_model"
placeholder="请选择默认模型"
/>
</div>
<div class="card card-select">
<span class="label">{{ items?.fast_model.des }}</span>
<ModelSelectorComponent
@select-model="handleFastModelSelect"
:model_name="fastModelName"
:model_provider="fastModelProvider"
:model_spec="configStore.config?.fast_model"
placeholder="请选择模型"
/>
</div>
<div class="card card-select">
@ -109,8 +109,8 @@
<span class="label">{{ items?.content_guard_llm_model.des }}</span>
<ModelSelectorComponent
@select-model="handleContentGuardModelSelect"
:model_name="contentGuardModelName"
:model_provider="contentGuardModelProvider"
:model_spec="configStore.config?.content_guard_llm_model"
placeholder="请选择模型"
/>
</div>
</div>
@ -251,45 +251,22 @@ const updateWindowWidth = () => {
state.windowWidth = window?.innerWidth || 0
}
const handleChatModelSelect = ({ provider, name }) => {
configStore.setConfigValues({
model_provider: provider,
model_name: name,
})
const handleChatModelSelect = (spec) => {
if (typeof spec === 'string' && spec) {
configStore.setConfigValue('default_model', spec)
}
}
const fastModelProvider = computed(() => {
const fastModel = configStore.config?.fast_model
if (!fastModel) return ''
return fastModel.split('/')[0]
})
const fastModelName = computed(() => {
const fastModel = configStore.config?.fast_model
if (!fastModel) return ''
const parts = fastModel.split('/')
return parts.slice(1).join('/')
})
const handleFastModelSelect = ({ provider, name }) => {
configStore.setConfigValue('fast_model', `${provider}/${name}`)
const handleFastModelSelect = (spec) => {
if (typeof spec === 'string' && spec) {
configStore.setConfigValue('fast_model', spec)
}
}
const contentGuardModelProvider = computed(() => {
const contentGuardModel = configStore.config?.content_guard_llm_model
if (!contentGuardModel) return ''
return contentGuardModel.split('/')[0]
})
const contentGuardModelName = computed(() => {
const contentGuardModel = configStore.config?.content_guard_llm_model
if (!contentGuardModel) return ''
const parts = contentGuardModel.split('/')
return parts.slice(1).join('/')
})
const handleContentGuardModelSelect = ({ provider, name }) => {
configStore.setConfigValue('content_guard_llm_model', `${provider}/${name}`)
const handleContentGuardModelSelect = (spec) => {
if (typeof spec === 'string' && spec) {
configStore.setConfigValue('content_guard_llm_model', spec)
}
}
onMounted(() => {
@ -381,7 +358,7 @@ const openLink = (url) => {
.setting-container {
--setting-header-height: 55px;
max-width: 870px;
max-width: 1054px;
}
.setting-header {