LingTropy/lingtropy-client/src/renderer/components/InputSection.vue
2025-03-18 18:52:50 +08:00

342 lines
7.3 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="input-section">
<div class="header-section">
<div class="logo-container">
<div class="logo-icon">AI</div>
</div>
<div class="header-content">
<h1 class="title">文案生成工具</h1>
<p class="subtitle">智能改写提升文案质量</p>
</div>
<button @click="$emit('toggle-settings')" class="settings-button">
<span class="settings-icon"></span>
</button>
</div>
<ModelSelector
:selectedModel="selectedModel"
:availableModels="availableModels"
@update:selectedModel="$emit('update:selectedModel', $event)"
/>
<RewriteMode
:mode="mode"
:generationCount="generationCount"
@update:mode="$emit('update:mode', $event)"
@update:generationCount="$emit('update:generationCount', $event)"
/>
<div class="original-text-section">
<h2 class="section-title">
<span class="section-icon">📝</span>
原始文案
<span v-if="originalText" class="char-count">{{ originalText.length }} 字</span>
</h2>
<textarea
:value="originalText"
@input="$emit('update:originalText', $event.target.value)"
class="text-input"
placeholder="请在此输入需要改写的文本..."
></textarea>
</div>
<button
:disabled="isGenerating"
@click="$emit('rewrite')"
class="rewrite-button"
>
<span v-if="isGenerating" class="loading-spinner"></span>
<span v-else>{{ mode === 'single' ? '生成改写' : '批量生成' }}</span>
</button>
</div>
</template>
<script setup>
import ModelSelector from './ModelSelector.vue'
import RewriteMode from './RewriteMode.vue'
defineProps({
mode: String,
originalText: String,
generationCount: Number,
selectedModel: String,
availableModels: Array,
isGenerating: Boolean
})
defineEmits([
'update:mode',
'update:originalText',
'update:generationCount',
'update:selectedModel',
'rewrite',
'toggle-settings'
])
</script>
<style scoped>
.input-section {
flex: 0 0 40%;
max-width: 600px;
min-width: 320px;
background-color: white;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
padding: 28px;
display: flex;
flex-direction: column;
overflow-y: auto;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
background-color: rgba(255, 255, 255, 0.9);
}
:deep(.dark-mode) .input-section {
background-color: rgba(30, 30, 46, 0.9);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
/* Logo 样式 */
.header-section {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 28px;
position: relative;
}
.header-content {
flex: 1;
}
.logo-container {
display: flex;
align-items: center;
justify-content: center;
}
.logo-icon {
width: 48px;
height: 48px;
background: linear-gradient(135deg, #2ecc71, #27ae60);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 20px;
box-shadow: 0 4px 12px rgba(46, 204, 113, 0.3);
}
/* 设置按钮 */
.settings-button {
width: 40px;
height: 40px;
border-radius: 10px;
background-color: #f5f5f5;
border: none;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
}
.settings-button:hover {
background-color: #e0e0e0;
transform: rotate(30deg);
}
.settings-icon {
font-size: 20px;
}
:deep(.dark-mode) .settings-button {
background-color: #2d2d3a;
color: #f0f0f0;
}
:deep(.dark-mode) .settings-button:hover {
background-color: #3d3d4a;
}
/* 标题样式 */
.title {
font-size: 24px;
font-weight: 700;
margin: 0 0 4px 0;
background: linear-gradient(90deg, #2ecc71, #27ae60);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
}
.subtitle {
color: #666;
margin: 0;
font-size: 14px;
}
:deep(.dark-mode) .subtitle {
color: #aaa;
}
.section-title {
font-size: 18px;
font-weight: 600;
margin: 0 0 16px 0;
color: #333;
display: flex;
align-items: center;
gap: 8px;
}
:deep(.dark-mode) .section-title {
color: #f0f0f0;
}
.section-icon {
font-size: 20px;
}
/* 字数统计 */
.char-count {
font-size: 14px;
color: #666;
font-weight: normal;
margin-left: auto;
background-color: #f0f0f0;
padding: 2px 8px;
border-radius: 12px;
}
:deep(.dark-mode) .char-count {
background-color: #2d2d3a;
color: #aaa;
}
/* 输入区域样式 */
.original-text-section {
margin-bottom: 24px;
flex-grow: 1;
display: flex;
flex-direction: column;
}
.text-input {
border: 1px solid #e0e0e0;
border-radius: 10px;
font-family: inherit;
font-size: 16px;
padding: 16px;
resize: none;
width: 100%;
flex-grow: 1;
min-height: 150px;
white-space: pre-wrap;
word-wrap: break-word;
overflow-wrap: break-word;
transition: border-color 0.3s, box-shadow 0.3s;
background-color: #f9f9f9;
}
.text-input:focus {
outline: none;
border-color: #2ecc71;
box-shadow: 0 0 0 3px rgba(46, 204, 113, 0.2);
background-color: white;
}
:deep(.dark-mode) .text-input {
background-color: #2d2d3a;
border-color: #3d3d4a;
color: #f0f0f0;
}
:deep(.dark-mode) .text-input:focus {
border-color: #2ecc71;
background-color: #252533;
}
/* 按钮样式 */
.rewrite-button {
background: linear-gradient(135deg, #2ecc71, #27ae60);
border: none;
border-radius: 10px;
color: white;
cursor: pointer;
font-size: 16px;
font-weight: 600;
margin-top: 16px;
padding: 16px;
transition: all 0.3s ease;
width: 100%;
position: relative;
overflow: hidden;
box-shadow: 0 4px 12px rgba(46, 204, 113, 0.3);
}
.rewrite-button:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(46, 204, 113, 0.4);
}
.rewrite-button:active:not(:disabled) {
transform: translateY(1px);
box-shadow: 0 2px 8px rgba(46, 204, 113, 0.3);
}
.rewrite-button:disabled {
opacity: 0.7;
cursor: not-allowed;
}
/* 加载动画 */
.loading-spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: white;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@media (max-width: 1200px) {
.input-section {
flex: none;
width: 100%;
max-width: 100%;
}
}
@media (max-width: 768px) {
.input-section {
padding: 20px;
border-radius: 12px;
}
.header-section {
flex-direction: column;
text-align: center;
}
.settings-button {
position: absolute;
top: 0;
right: 0;
}
}
@media (max-width: 480px) {
.input-section {
padding: 16px;
}
}
</style>