576 lines
15 KiB
Vue
576 lines
15 KiB
Vue
|
|
<template>
|
|||
|
|
<div class="prompt-selector">
|
|||
|
|
<!-- 下拉菜单式选择器(推荐方案) -->
|
|||
|
|
<div v-if="displayMode === 'select'" class="prompt-select-container">
|
|||
|
|
<a-select
|
|||
|
|
v-model:value="selectedPromptId"
|
|||
|
|
placeholder="选择提示词风格"
|
|||
|
|
style="width: 100%"
|
|||
|
|
@change="handleSelectChange"
|
|||
|
|
:loading="loading"
|
|||
|
|
>
|
|||
|
|
<a-select-option
|
|||
|
|
v-for="prompt in allPrompts"
|
|||
|
|
:key="prompt.id"
|
|||
|
|
:value="prompt.id"
|
|||
|
|
>
|
|||
|
|
<div class="prompt-option">
|
|||
|
|
<span class="prompt-option-name">{{ prompt.name }}</span>
|
|||
|
|
</div>
|
|||
|
|
</a-select-option>
|
|||
|
|
</a-select>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 标签式选择器 -->
|
|||
|
|
<div v-else-if="displayMode === 'tags'" class="prompt-tags-container">
|
|||
|
|
<div class="prompt-tags-grid">
|
|||
|
|
<div
|
|||
|
|
v-for="prompt in displayPrompts"
|
|||
|
|
:key="prompt.id"
|
|||
|
|
class="prompt-tag"
|
|||
|
|
:class="{ 'prompt-tag-selected': selectedPromptId === prompt.id }"
|
|||
|
|
@click="selectPrompt(prompt)"
|
|||
|
|
>
|
|||
|
|
<span class="prompt-tag-name">{{ prompt.name }}</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div v-if="hasMore && showMoreButton" class="prompt-more-button">
|
|||
|
|
<a-button size="small" type="link" @click="showAllPromptsModal = true" :disabled="allPrompts.length === 0">
|
|||
|
|
更多 ({{ allPrompts.length - displayCount }})
|
|||
|
|
</a-button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 加载状态 -->
|
|||
|
|
<div v-if="loading && allPrompts.length === 0" class="prompt-loading">
|
|||
|
|
<a-spin size="small" />
|
|||
|
|
<span class="loading-text">加载中...</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 空状态 -->
|
|||
|
|
<div v-else-if="!loading && allPrompts.length === 0" class="prompt-empty-state">
|
|||
|
|
<div class="prompt-empty-icon">
|
|||
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
|||
|
|
<circle cx="8.5" cy="8.5" r="1.5"></circle>
|
|||
|
|
<polyline points="21 15 16 10 5 21"></polyline>
|
|||
|
|
</svg>
|
|||
|
|
</div>
|
|||
|
|
<p class="prompt-empty-text">没有找到提示词</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 更多提示词弹窗 -->
|
|||
|
|
<div v-if="showAllPromptsModal" class="prompt-modal-mask" @click.self="showAllPromptsModal = false">
|
|||
|
|
<div class="prompt-modal">
|
|||
|
|
<div class="prompt-modal-header">
|
|||
|
|
<h3 class="prompt-modal-title">选择提示词</h3>
|
|||
|
|
<button class="prompt-modal-close" @click="showAllPromptsModal = false">
|
|||
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|||
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|||
|
|
</svg>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 搜索框 -->
|
|||
|
|
<div class="prompt-modal-search">
|
|||
|
|
<svg class="prompt-search-icon" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
|
<circle cx="11" cy="11" r="8"></circle>
|
|||
|
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|||
|
|
</svg>
|
|||
|
|
<input
|
|||
|
|
v-model="promptSearchKeyword"
|
|||
|
|
type="text"
|
|||
|
|
placeholder="搜索提示词..."
|
|||
|
|
class="prompt-search-input"
|
|||
|
|
/>
|
|||
|
|
<button class="refresh-button" @click="refreshUserPrompts" :disabled="loading">
|
|||
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
|
<polyline points="23 4 23 10 17 10"></polyline>
|
|||
|
|
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path>
|
|||
|
|
</svg>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 提示词列表 -->
|
|||
|
|
<div class="prompt-modal-content">
|
|||
|
|
<div v-if="filteredPrompts.length > 0" class="all-prompts-grid">
|
|||
|
|
<div
|
|||
|
|
v-for="prompt in filteredPrompts"
|
|||
|
|
:key="prompt.id"
|
|||
|
|
class="all-prompt-tag"
|
|||
|
|
:class="{ 'all-prompt-tag-selected': selectedPromptId === prompt.id }"
|
|||
|
|
@click="selectPrompt(prompt)"
|
|||
|
|
>
|
|||
|
|
<span class="all-prompt-tag-name">{{ prompt.name }}</span>
|
|||
|
|
<span v-if="prompt.status === 1" class="all-prompt-tag-status">启用</span>
|
|||
|
|
<span v-else class="all-prompt-tag-status all-prompt-tag-status-disabled">禁用</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div v-else class="prompt-empty-state">
|
|||
|
|
<div class="prompt-empty-icon">
|
|||
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
|||
|
|
<circle cx="8.5" cy="8.5" r="1.5"></circle>
|
|||
|
|
<polyline points="21 15 16 10 5 21"></polyline>
|
|||
|
|
</svg>
|
|||
|
|
</div>
|
|||
|
|
<p class="prompt-empty-text">没有找到匹配的提示词</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup>
|
|||
|
|
import { ref, computed, watch, onMounted } from 'vue'
|
|||
|
|
import { useUserStore } from '@/stores/user'
|
|||
|
|
import { usePromptStore } from '@/stores/prompt'
|
|||
|
|
import { message } from 'ant-design-vue'
|
|||
|
|
import { setJSON, getJSON } from '@/utils/storage'
|
|||
|
|
|
|||
|
|
// Props
|
|||
|
|
const props = defineProps({
|
|||
|
|
// 当前选中的提示词ID
|
|||
|
|
modelValue: {
|
|||
|
|
type: Number,
|
|||
|
|
default: null
|
|||
|
|
},
|
|||
|
|
// 展示模式:tags(标签)或 select(下拉选择)
|
|||
|
|
displayMode: {
|
|||
|
|
type: String,
|
|||
|
|
default: 'select' // 默认为select模式,因为用户反馈标签模式不好看
|
|||
|
|
},
|
|||
|
|
// 展示数量(仅标签模式有效)
|
|||
|
|
displayCount: {
|
|||
|
|
type: Number,
|
|||
|
|
default: 6
|
|||
|
|
},
|
|||
|
|
// 是否显示"更多"按钮
|
|||
|
|
showMoreButton: {
|
|||
|
|
type: Boolean,
|
|||
|
|
default: true
|
|||
|
|
},
|
|||
|
|
// 本地存储键名前缀,用于保存和恢复选择
|
|||
|
|
storageKey: {
|
|||
|
|
type: String,
|
|||
|
|
default: 'prompt_selector'
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// Emits
|
|||
|
|
const emit = defineEmits(['update:modelValue', 'change'])
|
|||
|
|
|
|||
|
|
// Stores
|
|||
|
|
const userStore = useUserStore()
|
|||
|
|
const promptStore = usePromptStore()
|
|||
|
|
|
|||
|
|
// Refs
|
|||
|
|
const showAllPromptsModal = ref(false)
|
|||
|
|
const promptSearchKeyword = ref('')
|
|||
|
|
const selectedPromptId = ref(props.modelValue)
|
|||
|
|
|
|||
|
|
// 使用 store 中的数据
|
|||
|
|
const allPrompts = computed(() => promptStore.promptList)
|
|||
|
|
const loading = computed(() => promptStore.promptListLoading)
|
|||
|
|
|
|||
|
|
// 计算属性:展示的部分提示词
|
|||
|
|
const displayPrompts = computed(() => {
|
|||
|
|
if (props.displayMode !== 'tags') return []
|
|||
|
|
return allPrompts.value.slice(0, props.displayCount)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 计算属性:是否有更多提示词
|
|||
|
|
const hasMore = computed(() => {
|
|||
|
|
return allPrompts.value.length > props.displayCount
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 计算属性:过滤后的全部提示词(用于"更多"弹窗)
|
|||
|
|
const filteredPrompts = computed(() => {
|
|||
|
|
if (!promptSearchKeyword.value.trim()) {
|
|||
|
|
return allPrompts.value
|
|||
|
|
}
|
|||
|
|
const keyword = promptSearchKeyword.value.trim().toLowerCase()
|
|||
|
|
return allPrompts.value.filter(p =>
|
|||
|
|
p.name.toLowerCase().includes(keyword) ||
|
|||
|
|
(p.content && p.content.toLowerCase().includes(keyword))
|
|||
|
|
)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 监听 selectedPromptId 变化,同步到父组件
|
|||
|
|
watch(selectedPromptId, (newValue) => {
|
|||
|
|
emit('update:modelValue', newValue)
|
|||
|
|
saveSelectedPromptId(newValue)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 监听 props.modelValue 变化,更新内部状态
|
|||
|
|
watch(() => props.modelValue, (newValue) => {
|
|||
|
|
selectedPromptId.value = newValue
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 加载用户提示词
|
|||
|
|
async function loadUserPrompts() {
|
|||
|
|
// 检查用户是否登录
|
|||
|
|
if (!userStore.userId) {
|
|||
|
|
console.warn('用户未登录,无法加载提示词')
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 使用 store 加载数据
|
|||
|
|
const prompts = await promptStore.loadPromptList(userStore.userId)
|
|||
|
|
|
|||
|
|
// 如果有选中ID,但当前选中的提示词不在列表中,清空选择
|
|||
|
|
if (selectedPromptId.value && !prompts.find(p => p.id === selectedPromptId.value)) {
|
|||
|
|
selectedPromptId.value = null
|
|||
|
|
}
|
|||
|
|
// 如果没有选中ID且有提示词,默认选中第一个
|
|||
|
|
else if (!selectedPromptId.value && prompts.length > 0) {
|
|||
|
|
selectedPromptId.value = prompts[0].id
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 尝试从本地存储恢复选中状态
|
|||
|
|
await restoreSelectedPromptId()
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('加载提示词失败:', error)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 保存选中的提示词ID到本地存储
|
|||
|
|
async function saveSelectedPromptId(promptId) {
|
|||
|
|
if (!promptId) return
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
await setJSON(`${props.storageKey}_selected`, promptId)
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('保存提示词选择失败:', error)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 从本地存储恢复选中的提示词ID
|
|||
|
|
async function restoreSelectedPromptId() {
|
|||
|
|
try {
|
|||
|
|
const savedId = await getJSON(`${props.storageKey}_selected`, null)
|
|||
|
|
if (savedId) {
|
|||
|
|
// 检查保存的ID是否在当前列表中
|
|||
|
|
const prompt = allPrompts.value.find(p => p.id === savedId)
|
|||
|
|
if (prompt) {
|
|||
|
|
selectedPromptId.value = savedId
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('恢复提示词选择失败:', error)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 选择提示词
|
|||
|
|
function selectPrompt(prompt) {
|
|||
|
|
if (!prompt || !prompt.content) {
|
|||
|
|
console.warn('提示词内容为空')
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
selectedPromptId.value = prompt.id
|
|||
|
|
emit('change', prompt)
|
|||
|
|
showAllPromptsModal.value = false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理下拉选择变化
|
|||
|
|
function handleSelectChange(value) {
|
|||
|
|
const prompt = allPrompts.value.find(p => p.id === value)
|
|||
|
|
if (prompt) {
|
|||
|
|
emit('change', prompt)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 刷新用户提示词
|
|||
|
|
async function refreshUserPrompts() {
|
|||
|
|
try {
|
|||
|
|
await promptStore.refreshPromptList(userStore.userId)
|
|||
|
|
await restoreSelectedPromptId()
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('刷新提示词失败:', error)
|
|||
|
|
message.error('刷新提示词失败')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 组件挂载时加载提示词
|
|||
|
|
onMounted(() => {
|
|||
|
|
loadUserPrompts()
|
|||
|
|
})
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
/* 基础样式 */
|
|||
|
|
.prompt-selector {
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 下拉选择器样式 */
|
|||
|
|
.prompt-select-container {
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-option {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-option-name {
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 标签模式 */
|
|||
|
|
.prompt-tags-container {
|
|||
|
|
margin-bottom: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-tags-grid {
|
|||
|
|
display: flex;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
gap: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-tag {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 6px 14px;
|
|||
|
|
background: var(--color-surface);
|
|||
|
|
border: 1px solid var(--color-border);
|
|||
|
|
border-radius: 16px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: var(--color-text);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-tag:hover {
|
|||
|
|
border-color: var(--color-primary);
|
|||
|
|
background: rgba(24, 144, 255, 0.08);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-tag-selected {
|
|||
|
|
border-color: var(--color-primary);
|
|||
|
|
background: var(--color-primary);
|
|||
|
|
color: #fff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-tag-name {
|
|||
|
|
white-space: nowrap;
|
|||
|
|
user-select: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-more-button {
|
|||
|
|
margin-top: 8px;
|
|||
|
|
text-align: right;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 加载状态 */
|
|||
|
|
.prompt-loading {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
gap: 8px;
|
|||
|
|
padding: 20px;
|
|||
|
|
color: var(--color-text-secondary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.loading-text {
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 空状态 */
|
|||
|
|
.prompt-empty-state {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
padding: 40px 20px;
|
|||
|
|
color: var(--color-text-secondary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-empty-icon {
|
|||
|
|
color: var(--color-text-secondary);
|
|||
|
|
opacity: 0.5;
|
|||
|
|
margin-bottom: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-empty-text {
|
|||
|
|
font-size: 14px;
|
|||
|
|
margin: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 弹窗 */
|
|||
|
|
.prompt-modal-mask {
|
|||
|
|
position: fixed;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
right: 0;
|
|||
|
|
bottom: 0;
|
|||
|
|
background: rgba(0, 0, 0, 0.5);
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: center;
|
|||
|
|
align-items: center;
|
|||
|
|
z-index: 1000;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-modal {
|
|||
|
|
background: var(--color-surface);
|
|||
|
|
border-radius: var(--radius-card);
|
|||
|
|
width: 600px;
|
|||
|
|
max-height: 80vh;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
overflow: hidden;
|
|||
|
|
box-shadow: var(--shadow-lg);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-modal-header {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 16px 20px;
|
|||
|
|
border-bottom: 1px solid var(--color-border);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-modal-title {
|
|||
|
|
margin: 0;
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: var(--color-text);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-modal-close {
|
|||
|
|
background: transparent;
|
|||
|
|
border: none;
|
|||
|
|
cursor: pointer;
|
|||
|
|
color: var(--color-text-secondary);
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
width: 32px;
|
|||
|
|
height: 32px;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-modal-close:hover {
|
|||
|
|
background: var(--color-bg);
|
|||
|
|
color: var(--color-text);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-modal-search {
|
|||
|
|
position: relative;
|
|||
|
|
padding: 16px 20px;
|
|||
|
|
border-bottom: 1px solid var(--color-border);
|
|||
|
|
display: flex;
|
|||
|
|
gap: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-search-icon {
|
|||
|
|
position: absolute;
|
|||
|
|
left: 36px;
|
|||
|
|
top: 50%;
|
|||
|
|
transform: translateY(-50%);
|
|||
|
|
color: var(--color-text-secondary);
|
|||
|
|
pointer-events: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-search-input {
|
|||
|
|
flex: 1;
|
|||
|
|
padding: 10px 12px 10px 40px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: var(--color-text);
|
|||
|
|
background: var(--color-bg);
|
|||
|
|
border: 1px solid var(--color-border);
|
|||
|
|
border-radius: 6px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-search-input:focus {
|
|||
|
|
outline: none;
|
|||
|
|
border-color: var(--color-primary);
|
|||
|
|
box-shadow: 0 0 0 3px rgba(24, 144, 255, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.refresh-button {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
width: 36px;
|
|||
|
|
height: 36px;
|
|||
|
|
background: var(--color-bg);
|
|||
|
|
border: 1px solid var(--color-border);
|
|||
|
|
border-radius: 6px;
|
|||
|
|
color: var(--color-text);
|
|||
|
|
cursor: pointer;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.refresh-button:hover:not(:disabled) {
|
|||
|
|
background: var(--color-primary);
|
|||
|
|
color: #fff;
|
|||
|
|
border-color: var(--color-primary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.refresh-button:disabled {
|
|||
|
|
opacity: 0.5;
|
|||
|
|
cursor: not-allowed;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.prompt-modal-content {
|
|||
|
|
overflow-y: auto;
|
|||
|
|
max-height: calc(80vh - 180px);
|
|||
|
|
padding: 16px 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 提示词列表 */
|
|||
|
|
.all-prompts-grid {
|
|||
|
|
display: flex;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
gap: 8px;
|
|||
|
|
padding: 0 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.all-prompt-tag {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 8px;
|
|||
|
|
padding: 8px 14px;
|
|||
|
|
background: var(--color-surface);
|
|||
|
|
border: 1px solid var(--color-border);
|
|||
|
|
border-radius: 16px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: var(--color-text);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.all-prompt-tag:hover {
|
|||
|
|
border-color: var(--color-primary);
|
|||
|
|
background: rgba(24, 144, 255, 0.08);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.all-prompt-tag-selected {
|
|||
|
|
border-color: var(--color-primary);
|
|||
|
|
background: var(--color-primary);
|
|||
|
|
color: #fff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.all-prompt-tag-name {
|
|||
|
|
white-space: nowrap;
|
|||
|
|
user-select: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.all-prompt-tag-status {
|
|||
|
|
font-size: 12px;
|
|||
|
|
padding: 2px 6px;
|
|||
|
|
border-radius: 10px;
|
|||
|
|
background: rgba(16, 185, 129, 0.1);
|
|||
|
|
color: #10b981;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.all-prompt-tag-status-disabled {
|
|||
|
|
background: rgba(239, 68, 68, 0.1);
|
|||
|
|
color: #ef4444;
|
|||
|
|
}
|
|||
|
|
</style>
|