refactor: 范式分段写作页面下拉框支持图标渲染

- 用自定义下拉组件替代原生 select (原生不支持渲染组件)
- 添加下拉框控制逻辑和点击外部关闭功能
- 添加自定义下拉框 CSS 样式

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
empty
2026-01-12 03:36:41 +08:00
parent 200560da4c
commit f97d821b09

View File

@@ -14,20 +14,27 @@
<!-- 1. 选择范式 -->
<section class="writer-section">
<label class="writer-label">1. 选择写作范式</label>
<select
v-model="selectedParadigmId"
@change="handleParadigmChange"
class="writer-select"
>
<option value="" disabled>请选择范式...</option>
<option
v-for="p in paradigmList"
:key="p.id"
:value="p.id"
>
{{ p.icon }} {{ p.name }}
</option>
</select>
<div class="custom-select" @click="toggleDropdown">
<div class="custom-select-trigger">
<template v-if="activeParadigm">
<IconLibrary :name="activeParadigm.icon" :size="16" />
<span>{{ activeParadigm.name }}</span>
</template>
<span v-else class="text-muted">请选择范式...</span>
<IconLibrary name="expand" :size="14" class="ml-auto" />
</div>
<div v-show="isDropdownOpen" class="custom-select-dropdown">
<div
v-for="p in paradigmList"
:key="p.id"
@click.stop="selectParadigm(p)"
:class="['custom-select-option', { 'selected': selectedParadigmId === p.id }]"
>
<IconLibrary :name="p.icon" :size="16" />
<span>{{ p.name }}</span>
</div>
</div>
</div>
<p v-if="activeParadigm" class="text-xs text-muted mt-2">
{{ activeParadigm.description }}
</p>
@@ -174,7 +181,7 @@
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { storeToRefs } from 'pinia'
import { useAppStore } from '../stores/app'
import IconLibrary from './icons/IconLibrary.vue'
@@ -189,6 +196,33 @@ const { paradigmWriterState, activeParadigm, generatedContent } = storeToRefs(ap
const selectedParadigmId = ref('')
const currentSectionIndex = ref(0)
const paradigmList = ref([])
const isDropdownOpen = ref(false)
// 下拉框控制
const toggleDropdown = () => {
isDropdownOpen.value = !isDropdownOpen.value
}
const selectParadigm = (p) => {
selectedParadigmId.value = p.id
isDropdownOpen.value = false
handleParadigmChange()
}
// 点击外部关闭下拉框
const closeDropdown = (e) => {
if (!e.target.closest('.custom-select')) {
isDropdownOpen.value = false
}
}
onMounted(() => {
document.addEventListener('click', closeDropdown)
})
onUnmounted(() => {
document.removeEventListener('click', closeDropdown)
})
// 加载完整的范式列表(包含自定义范式)
const loadAllParadigms = () => {
@@ -649,4 +683,59 @@ onMounted(() => {
opacity: 0.5;
cursor: not-allowed;
}
/* 自定义下拉框 */
.custom-select {
position: relative;
width: 100%;
}
.custom-select-trigger {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-3);
background: var(--bg-primary);
border: 1px solid var(--border-default);
border-radius: var(--radius-lg);
cursor: pointer;
transition: all var(--transition-fast);
}
.custom-select-trigger:hover {
border-color: var(--accent-primary);
}
.custom-select-dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
margin-top: var(--space-1);
background: var(--bg-elevated);
border: 1px solid var(--border-default);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg);
z-index: 100;
max-height: 240px;
overflow-y: auto;
}
.custom-select-option {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-3);
cursor: pointer;
transition: all var(--transition-fast);
}
.custom-select-option:hover {
background: var(--bg-sunken);
}
.custom-select-option.selected {
background: var(--info-bg);
color: var(--accent-primary);
}
</style>