flat: 暂存

This commit is contained in:
Apcallover
2025-12-30 15:40:46 +08:00
parent 634610ca7e
commit 6641e07b8a
3 changed files with 582 additions and 723 deletions

View File

@@ -18,6 +18,7 @@
"postinstall": "electron-builder install-app-deps", "postinstall": "electron-builder install-app-deps",
"build:unpack": "npm run build && electron-builder --dir", "build:unpack": "npm run build && electron-builder --dir",
"build:win": "npm run build && electron-builder --win", "build:win": "npm run build && electron-builder --win",
"build:win:x64": "npm run build && electron-builder --win --x64",
"build:mac": "npm run build && electron-builder --mac", "build:mac": "npm run build && electron-builder --mac",
"build:linux": "npm run build && electron-builder --linux" "build:linux": "npm run build && electron-builder --linux"
}, },

View File

@@ -34,7 +34,7 @@ function createWindow(): void {
mainWindow.show(); mainWindow.show();
}); });
mainWindow.webContents.openDevTools(); // mainWindow.webContents.openDevTools();
mainWindow.webContents.setWindowOpenHandler((details) => { mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url); shell.openExternal(details.url);

View File

@@ -1,13 +1,15 @@
<template><a-layout class="app-container"> <template>
<a-layout class="app-container">
<a-layout-header class="header"> <a-layout-header class="header">
<a-flex justify="space-between" align="center" style="height: 100%"> <a-flex justify="space-between" align="center" style="height: 100%">
<div class="logo-title"> <div class="logo-title">
<!-- <WifiOutlined /> <!-- <WifiOutlined v-if="isLiveOn" style="color: #52c41a" /> -->
直播中台控制系统 --> <!-- 直播中台控制系统 -->
</div> </div>
<a-space :size="20"> <a-space :size="20">
<a-badge :dot="isConnected">
<SettingOutlined class="action-icon" /> <SettingOutlined class="action-icon" />
</a-badge>
<a-avatar size="default" style="background-color: #1677ff"> <a-avatar size="default" style="background-color: #1677ff">
<template #icon> <template #icon>
<UserOutlined /> <UserOutlined />
@@ -17,334 +19,183 @@
</a-flex> </a-flex>
</a-layout-header> </a-layout-header>
<a-layout> <a-layout class="sub-layout">
<a-layout-sider resizable :width="300" :min-width="250" :max-width="500" class="sider">
<a-layout-sider resizable width="300" :min-width="250" :max-width="500" class="sider"> <div class="sider-container">
<div class="sider-top" style="overflow-y: auto;flex: 2;border-bottom: 1px solid #e8e8e8;max-height: calc(100% * 2 / 5);min-height: calc(100% * 2 / 5);"> <div class="sider-section top-panel">
<div class="section-header">
<a-flex justify="space-between" align="center" style="margin-bottom: 16px"> <a-typography-title :level="5" style="margin: 0">岗位列表</a-typography-title>
<a-space align="left" > <a-button type="primary" size="small" @click="handleShow" :loading="modelLoading">
<a-typography-title :level="5"> <template #icon>
岗位列表 <PlusOutlined />
</a-typography-title> </template>添加
</a-space>
<a-space align="right" >
<a-button type="primary" @click="handleShow" :loading="modelLoading" >
添加岗位
</a-button> </a-button>
</a-space>
</a-flex>
<a-list :data-source="positions" item-layout="horizontal">
<template #renderItem="{ item, index }" >
<a-list-item class="position-item" >
<DeleteOutlined title="删除此项" @click.stop="deletedGw(index)"
style="color: #1677ff; margin-right: 5px;margin-bottom: 3px;" />
<a-list-item-meta :title="item.title" @click="setGwDetail(index)" >
</a-list-item-meta>
<RightOutlined @click="sendMessageGwAll(index)" />
</a-list-item>
</template>
</a-list>
<a-divider />
</div> </div>
<div ref="listScrollRef" class="sider-bottom" style="overflow-y: auto;flex: 3;max-height: calc(100% * 3 / 5);"> <div class="section-content">
<a-list :data-source="positions" item-layout="horizontal" size="small">
<a-typography-title :level="5">岗位详情</a-typography-title>
<a-list :data-source="dutyList" item-layout="horizontal">
<template #renderItem="{ item, index }"> <template #renderItem="{ item, index }">
<a-list-item @click="resetSayin" :ref="($el) => insertItemList($el)" class="position-item" :class="{ 'highlight-item': index === scrollIndex }"> <a-list-item class="position-item" @click="setGwDetail(index)">
<h6 class="details-title" v-if="item.length === 5 " > <div class="item-main">
<StarFilled style="color: #1677ff; margin-left:-20px;margin-right:5px;" />{{ item }} <span class="item-title">{{ item.title }}</span>
</h6> </div>
<a-list-item-meta v-else :title="item" > <template #actions>
</a-list-item-meta> <a-space>
<DeleteOutlined class="delete-btn" @click.stop="deletedGw(index)" />
<RightOutlined class="play-btn" @click.stop="sendMessageGwAll(index)" />
</a-space>
</template>
</a-list-item> </a-list-item>
</template> </template>
</a-list> </a-list>
</div> </div>
</div>
<div class="sider-section bottom-panel">
<div class="section-header sticky-header">
<a-typography-title :level="5" style="margin: 0">岗位详情</a-typography-title>
</div>
<div ref="listScrollRef" class="section-content">
<a-list :data-source="dutyList" item-layout="horizontal" size="small">
<template #renderItem="{ item, index }">
<a-list-item @click="resetSayin" :ref="($el) => insertItemList($el)"
class="detail-item" :class="{ 'highlight-item': index === scrollIndex }">
<div v-if="item.length === 5" class="special-duty">
<StarFilled class="star-icon" />
<span class="bold-text">{{ item }}</span>
</div>
<span v-else class="normal-duty">{{ item }}</span>
</a-list-item>
</template>
</a-list>
<a-empty v-if="dutyList.length === 0" :image="aEmpty.PRESENTED_IMAGE_SIMPLE"
description="暂无详情" />
</div>
</div>
</div>
</a-layout-sider> </a-layout-sider>
<a-layout class="main-content-layout"> <a-layout class="main-content-layout">
<a-layout-content class="content-scroll-area"> <a-layout-content class="content-scroll-area">
<a-space direction="vertical" size="middle" style="display: flex">
<a-card title="直播控制" size="small"> <a-card title="直播控制" size="small" :bordered="false" class="custom-card">
<a-row :gutter="[16, 16]"> <a-row :gutter="[12, 12]">
<a-col :xs="24" :sm="12" :md="8"> <a-col :span="8">
<a-button type="primary" block @click="startLive" :loading="loading || isLiveOn"> <a-button type="primary" block @click="startLive" :loading="loading || isLiveOn">
<template #icon> <template #icon>
<PlayCircleOutlined /> <PlayCircleOutlined />
</template> </template>开始直播
开始直播
</a-button> </a-button>
</a-col> </a-col>
<a-col :xs="24" :sm="12" :md="8"> <a-col :span="8">
<a-button type="danger" block @click="stopLive" :loading="loading" <a-button danger block @click="stopLive" :loading="loading" :disabled="!isLiveOn">
:disabled="!isLiveOn">
<template #icon> <template #icon>
<StopOutlined /> <StopOutlined />
</template> </template>结束直播
结束直播
</a-button> </a-button>
</a-col> </a-col>
<a-col :xs="24" :sm="12" :md="8"> <a-col :span="8">
<a-button block @click="fullScreenInsert" :disabled="!isLiveOn"> <a-button danger ghost block @click="forceClose">
<template #icon>
<DesktopOutlined />
</template>
全屏插播
</a-button>
</a-col>
<a-col :xs="24" :sm="12" :md="8">
<a-button block @click="windowInsert" :disabled="!isLiveOn">
<template #icon>
<FullscreenOutlined />
</template>
窗口插播
</a-button>
</a-col>
<a-col :xs="24" :sm="12" :md="8">
<a-button block @click="audioInsert" :disabled="!isLiveOn">
<template #icon>
<HighlightOutlined />
</template>
音频插入
</a-button>
</a-col>
<a-col :xs="24" :sm="12" :md="8">
<a-button block @click="forceClose">
<template #icon> <template #icon>
<VideoCameraOutlined /> <VideoCameraOutlined />
</template> </template>强制关闭
强制关闭
</a-button> </a-button>
</a-col> </a-col>
<a-col :span="8"><a-button block @click="fullScreenInsert"
:disabled="!isLiveOn">全屏插播</a-button></a-col>
<a-col :span="8"><a-button block @click="windowInsert"
:disabled="!isLiveOn">窗口插播</a-button></a-col>
<a-col :span="8"><a-button block @click="audioInsert"
:disabled="!isLiveOn">音频插入</a-button></a-col>
</a-row> </a-row>
</a-card> </a-card>
<a-card title="讲解输出" size="small"> <a-card title="AI模型管理" size="small" :bordered="false" class="custom-card">
<div class="placeholder-box"> <template #extra>
</div> <a-button type="link" size="small" @click="refreshModelStatus">
</a-card> <ReloadOutlined />刷新
</a-button>
<a-card title="AI模型管理" size="small"> </template>
<a-flex justify="space-between" align="center" style="margin-bottom: 16px"> <a-flex justify="space-between" align="center" style="margin-bottom: 16px">
<a-space> <a-space>
<a-typography-text>当前模型:</a-typography-text> <a-tag :color="modelStatus.exists ? 'green' : 'orange'">{{ modelStatus.modelName
<a-tag :color="modelStatus.exists ? 'green' : 'orange'"> }}</a-tag>
{{ modelStatus.modelName }} <a-badge :status="modelStatus.ollamaRunning ? 'processing' : 'default'"
<template v-if="modelStatus.exists"> :text="modelStatus.ollamaRunning ? 'Ollama运行中' : '服务未就绪'" />
<CheckCircleOutlined />
</template>
<template v-else>
<ClockCircleOutlined />
</template>
</a-tag>
<a-tag :color="modelStatus.ollamaRunning ? 'green' : 'red'">
{{ modelStatus.ollamaRunning ? 'Ollama运行中' : 'Ollama未运行' }}
</a-tag>
</a-space>
<a-space>
<a-button type="primary" @click="loadModel" :loading="modelLoading"
:disabled="modelLoading">
<template #icon>
<DownloadOutlined />
</template>
{{ modelLoading ? '加载中...' : '加载模型' }}
</a-button>
<a-button @click="refreshModelStatus" :disabled="modelLoading">
<template #icon>
<ReloadOutlined />
</template>
刷新状态
</a-button>
</a-space> </a-space>
<a-button type="primary" size="small" @click="loadModel"
:loading="modelLoading">加载模型</a-button>
</a-flex> </a-flex>
<!-- 文件拖拽区域 --> <div class="file-drop-zone" @drop="handleFileDrop" @dragover.prevent
<div class="file-drop-zone" @drop="handleFileDrop" @dragover.prevent @dragenter.prevent
:class="{ 'is-dragging': fileUploadLoading }"> :class="{ 'is-dragging': fileUploadLoading }">
<p class="file-drop-icon"> <InboxOutlined class="drop-icon" />
<InboxOutlined /> <p class="drop-text">拖拽图片或视频至此处自动展示</p>
</p>
<p class="file-drop-text">拖拽图片拖拽视频文件自动展示</p>
<p class="file-drop-hint">支持图片视频格式</p>
</div> </div>
<!-- 已上传文件列表 --> <div v-if="uploadedFiles.length > 0" class="file-list-container">
<div v-if="uploadedFiles.length > 0" class="uploaded-files-list"> <div v-for="(file, index) in uploadedFiles" :key="index" class="file-item-row">
<div v-for="(file, index) in uploadedFiles" :key="index" class="file-item">
<div class="file-info"> <div class="file-info">
<FileOutlined class="file-icon" /> <FileOutlined /> <span class="file-name">{{ file.name }}</span>
<span class="file-name">{{ file.name }}</span> <a-tag size="small" :color="getFileTagColor(file.type)">{{
<a-tag :color="getFileTagColor(file.type)" size="small">{{
getFileTypeText(file.type) getFileTypeText(file.type)
}}</a-tag> }}</a-tag>
</div> </div>
<a-button size="small" danger @click="removeFile(index)"> <a-button type="text" danger size="small" @click="removeFile(index)">
<template #icon>
<DeleteOutlined /> <DeleteOutlined />
</template>
删除
</a-button> </a-button>
</div> </div>
</div> </div>
<a-space style="margin-top: 12px;"> <a-space style="margin-top: 12px">
<a-button @click="clearAllFiles" :disabled="uploadedFiles.length === 0"> <a-button size="small" @click="clearAllFiles"
<template #icon> :disabled="uploadedFiles.length === 0">清空</a-button>
<DeleteOutlined /> <a-button size="small" type="primary" ghost @click="showFilesInLive"
</template> :disabled="uploadedFiles.length === 0 || !isLiveOn">在直播中展示</a-button>
清空文件
</a-button>
<a-button @click="showFilesInLive" :disabled="uploadedFiles.length === 0 || !isLiveOn">
<template #icon>
<EyeOutlined />
</template>
在直播中展示
</a-button>
</a-space> </a-space>
</a-card> </a-card>
</a-space>
</a-layout-content> </a-layout-content>
<a-layout-footer class="input-footer"> <a-layout-footer class="input-footer">
<a-space> <div class="footer-inner">
<a-button danger size="small" block @click="polishText('inputValue')" :loading="polishLoading" <a-flex gap="small" vertical>
:disabled="!inputValue.trim()"> <div class="quick-commands">
润色 <span class="label">快捷指令:</span>
</a-button> <a-button v-for="cmd in ['欢迎新观众', '感谢陪伴', '互动提问', '行业分享']" :key="cmd" size="small"
</a-space> class="cmd-btn" @click="setQuickCommand(cmd)">{{ cmd }}</a-button>
<a-space-compact block style="display: flex; margin-bottom: 8px; margin-top: 8px;"> </div>
<a-textarea v-model:value="inputValue" placeholder="输入指令..." <a-space-compact block>
<a-textarea v-model:value="inputValue" placeholder="输入指令或内容..."
:auto-size="{ minRows: 1, maxRows: 3 }" @press-enter="sendMessage" /> :auto-size="{ minRows: 1, maxRows: 3 }" @press-enter="sendMessage" />
<a-button type="primary" @click="sendMessage" :disabled="!isLiveOn">发送</a-button> <a-button type="primary" @click="sendMessage" :disabled="!isLiveOn"
style="height: auto">发送</a-button>
</a-space-compact> </a-space-compact>
<a-space> <a-button size="small" class="polish-btn" @click="polishText('inputValue')"
<a-typography-text type="secondary">快捷指令:</a-typography-text> :loading="polishLoading">
<a-button type="dashed" size="small" @click="setQuickCommand('欢迎新观众')">欢迎新观众</a-button> <RotateRightOutlined /> 智能润色
<a-button type="dashed" size="small" @click="setQuickCommand('感谢大家的陪伴')">感谢陪伴</a-button> </a-button>
<a-button type="dashed" size="small" @click="setQuickCommand('有什么问题可以随时提问')">互动提问</a-button> </a-flex>
<a-button type="dashed" size="small" </div>
@click="setQuickCommand('今天给大家分享一些有趣的行业资讯')">行业分享</a-button>
</a-space>
</a-layout-footer> </a-layout-footer>
</a-layout> </a-layout>
</a-layout>
</a-layout> </a-layout>
<!-- 可输入内容的模态框 --> <a-modal v-model:open="showModal" title="新增岗位信息" width="600px" @ok="handleOk">
<a-modal <a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
v-model:open="showModal" <a-form-item label="详细信息" name="infoin">
width="800px" <a-textarea v-model:value="formData.infoin" :rows="10" placeholder="请输入岗位描述,系统将自动切分段落..." />
ok-text="提交"
cancel-text="取消"
@ok="handleOk"
@cancel="handleCancel"
:maskClosable="false"
:destroyOnClose="true"
:confirmLoading="confirmLoading"
>
<template #title>
<div class="modal-title-wrapper">
<!-- 标题栏右侧按钮组 -->
<div class="title-button-group">
<a-row>
<a-col :span="10">
<!-- 原标题文字 -->
<a-label style="font-weight: bolder;" >新增岗位信息</a-label>
<!-- 暂时没有其他格式得默认就是text
<a-button
:type="btn1Type"
size="small"
@click="typeChange('btn1')"
style="margin-left:10px;"
>
文本
</a-button>
<a-button
:type="btn2Type"
size="small"
@click="typeChange('btn2')"
style="margin-left:5px;"
>
JSON
</a-button>
-->
</a-col>
<a-col :span="14">
<!-- 在提交得时候自动进行转换 仅保留文字润色功能
<a-button
type="default"
size="small"
@click="transText"
style="margin-left:13px;"
>
转换
</a-button>
-->
<a-button
type="default"
size="small"
@click="polishText('gangwei')"
style="margin-left:5px;"
>
润色
</a-button>
</a-col>
</a-row>
</div>
</div>
</template>
<!-- 表单区域 -->
<a-form
ref="formRef"
:model="formData"
:rules="formRules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 50 }"
layout="horizontal"
>
<a-row :gutter="16">
<a-col :span="24">
<!-- 单行输入框 -->
<a-form-item label="" name="infoin">
<a-textarea
v-model:value="formData.infoin"
placeholder="请输岗位信息"
:rows="15"
maxlength="2000"
/>
</a-form-item> </a-form-item>
</a-col> <a-button size="small" @click="polishText('gangwei')" :loading="polishLoading">
<RotateRightOutlined />润色内容
<a-col :span="0"> </a-button>
<div style="width: 375px;height: 340px;overflow-y: scroll;">
<!-- 多行文本框 -->
<div v-for="(item, index) in formData.infoDetail"
:key="index"
>
<textarea style="overflow: hidden;border: none;line-height: 1.1;height: auto;min-height: 10px;outline: none;width: 100%;flex: none;resize: none;margin: 0px;padding: 0px 5px;word-wrap: break-word;word-break: break-all;"
v-model="formData.infoDetail[index]"
readonly="true" class="auto-wrap-textarea" wrap="soft"
/>
</div>
</div>
</a-col>
</a-row>
</a-form> </a-form>
</a-modal> </a-modal>
</a-layout>
</template> </template>
<script setup> <script setup>
import { ref, onMounted, onUnmounted, computed, reactive, nextTick, watch } from 'vue'; import { ref, onMounted, onUnmounted, computed, reactive, nextTick, watch } from 'vue';
import { message, Modal, } from 'ant-design-vue'; import { message, Modal, Empty as aEmpty } from 'ant-design-vue';
import SocketClient from '@renderer/utils/socket'; import SocketClient from '@renderer/utils/socket';
import { import {
WifiOutlined, WifiOutlined,
@@ -367,6 +218,8 @@ import {
ClockCircleOutlined, ClockCircleOutlined,
FileOutlined, FileOutlined,
EyeOutlined, EyeOutlined,
RotateRightOutlined,
PlusOutlined
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import { useUserStore } from '@renderer/stores/useUserStore'; import { useUserStore } from '@renderer/stores/useUserStore';
@@ -1012,6 +865,7 @@ const polishTextBackStr = async (_inputStr) => {
// const originalText = _inputStr+123; // const originalText = _inputStr+123;
// console.log("originalText",originalText) // console.log("originalText",originalText)
// return originalText; // return originalText;
const originalText = _inputStr;
try { try {
console.log('开始润色文本:', originalText); console.log('开始润色文本:', originalText);
@@ -1374,239 +1228,243 @@ onMounted(() => {
}); });
</script> </script>
<style scoped> <style scoped lang="scss">
/* 根容器: /* 布局基座 */
使用 100vh 充满窗口,并禁止窗口级别滚动。
内部的 <a-layout> 会自动继承这个高度。
*/
.app-container { .app-container {
height: 100vh; height: 100vh;
overflow: hidden; overflow: hidden;
/* 关键:禁止最外层滚动 */
} }
/* 顶部导航栏 */
.header { .header {
background: #fff; background: #fff;
border-bottom: 1px solid #f0f0f0; border-bottom: 1px solid #f0f0f0;
padding: 0 20px; padding: 0 20px;
height: 60px; height: 56px;
/* 缩小顶部栏高度 */ line-height: 56px;
line-height: 60px; z-index: 100;
flex-shrink: 0; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
/* 防止顶部栏被压缩 */
-webkit-app-region: drag; -webkit-app-region: drag;
} }
.logo-title { .sub-layout {
font-size: 18px; height: calc(100vh - 56px);
/* 缩小字体 */
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
} }
.action-icon { /* 侧边栏结构优化 */
font-size: 16px;
cursor: pointer;
}
/* 左侧边栏:
- ant-layout-sider 默认有 100% 高度
- 我们需要让 *内部* 的内容可滚动
*/
.sider { .sider {
background: #fff; background: #fff !important;
border-right: 1px solid #f0f0f0; border-right: 1px solid #f0f0f0;
/* 关键Sider 本身高度 100%
但内部内容可能溢出,所以用一个 wrapper 来滚动
*/
height: 100%;
}
.sider-scroll-content { :deep(.ant-layout-sider-children) {
height: 100%;
overflow-y: auto;
padding: 16px;
/* 缩小内边距 */
}
.position-item {
padding: 10px 8px;
/* 缩小列表项间距 */
border-radius: 6px;
cursor: pointer;
transition: background-color 0.3s;
}
.position-item:hover {
background-color: #f7f7f7;
}
/* 岗位详情 */
.details-title {
font-size: 14px;
font-weight: 600;
margin-top: 16px;
margin-bottom: 8px;
display: flex; display: flex;
align-items: center; flex-direction: column;
}
} }
.details-list { .sider-container {
padding-left: 24px;
/* 缩小缩进 */
margin: 0;
color: #555;
line-height: 1.7;
}
/* 右侧主布局:
- 默认就是 flex: 1自动撑满
- 背景色放在这里,而不是 content-scroll-area
*/
.main-content-layout {
background: #f0f2f5;
/* 关键:确保内部的 content 和 footer 垂直排列 */
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
/* 继承父级高度 */
} }
/* 右侧可滚动内容区: .sider-section {
- flex: 1: 自动撑满所有可用空间 display: flex;
- overflow-y: auto: 独立滚动 flex-direction: column;
*/ overflow: hidden;
.content-scroll-area {
overflow-y: auto; &.top-panel {
padding: 16px; flex: 4; // 岗位列表占 40%
/* 缩小内边距 */ border-bottom: 1px solid #f0f0f0;
flex: 1;
/* 关键:撑满除 footer 外的空间 */
} }
/* 讲解输出占位符 */ &.bottom-panel {
.placeholder-box { flex: 6; // 岗位详情占 60%
min-height: 120px; background-color: #fafafa;
/* 缩小高度 */ }
background: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 8px;
} }
/* 底部输入框: .section-header {
- flex-shrink: 0: 确保不会被压缩
- height: auto: 高度自适应
*/
.input-footer {
background: #fff;
padding: 12px 16px; padding: 12px 16px;
/* 缩小内边距 */ display: flex;
border-top: 1px solid #f0f0f0; justify-content: space-between;
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05); align-items: center;
height: auto; background: #fff;
/* 关键:高度自适应 */
flex-shrink: 0;
/* 关键:防止被压缩 */
} }
/* 文件拖拽区域样式 */ .section-content {
flex: 1;
overflow-y: auto;
padding: 0 12px 12px;
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-thumb {
background: #e8e8e8;
border-radius: 2px;
}
}
/* 列表项交互样式 */
.position-item {
padding: 8px 12px !important;
border-radius: 6px;
margin-bottom: 4px;
cursor: pointer;
transition: all 0.2s;
border: 1px solid transparent !important;
&:hover {
background-color: #f0f7ff;
border-color: #e6f4ff !important;
}
.play-btn {
color: #1677ff;
&:hover {
opacity: 0.8;
}
}
.delete-btn {
color: #bfbfbf;
&:hover {
color: #ff4d4f;
}
}
}
.detail-item {
padding: 10px 12px !important;
border-radius: 4px;
margin-bottom: 2px;
background: #fff;
border-left: 3px solid transparent !important;
&.highlight-item {
background-color: #e6f4ff !important;
border-left-color: #1677ff !important;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
}
.special-duty {
display: flex;
align-items: center;
color: #1677ff;
.star-icon {
margin-right: 8px;
}
.bold-text {
font-weight: 600;
}
}
/* 主内容与卡片 */
.main-content-layout {
background: #f5f7fa;
}
.content-scroll-area {
padding: 16px;
overflow-y: auto;
}
.custom-card {
border-radius: 8px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
:deep(.ant-card-head) {
border-bottom: 1px solid #f0f0f0;
min-height: 40px;
}
}
/* 文件上传区 */
.file-drop-zone { .file-drop-zone {
border: 2px dashed #d9d9d9; border: 1px dashed #d9d9d9;
border-radius: 8px; border-radius: 8px;
padding: 24px; padding: 24px;
text-align: center; text-align: center;
background-color: #fafafa; background: #fff;
transition: all 0.3s ease; transition: all 0.3s;
cursor: pointer;
margin-bottom: 16px;
}
.file-drop-zone:hover, &.is-dragging,
.file-drop-zone.is-dragging { &:hover {
border-color: #1677ff; border-color: #1677ff;
background-color: #f0f8ff; background: #f0f7ff;
} }
.file-drop-icon { .drop-icon {
font-size: 48px; font-size: 32px;
color: #1677ff; color: #1677ff;
margin-bottom: 8px; margin-bottom: 8px;
} }
.file-drop-text { .drop-text {
font-size: 16px;
color: #262626;
margin-bottom: 4px;
font-weight: 500;
}
.file-drop-hint {
font-size: 14px;
color: #8c8c8c; color: #8c8c8c;
margin: 0; font-size: 13px;
}
} }
/* 文件列表样式 */ .file-list-container {
.uploaded-files-list { margin-top: 12px;
max-height: 200px;
overflow-y: auto;
border: 1px solid #f0f0f0; border: 1px solid #f0f0f0;
border-radius: 6px; border-radius: 6px;
margin-bottom: 12px; max-height: 150px;
overflow-y: auto;
} }
.file-item { .file-item-row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; padding: 6px 12px;
padding: 8px 12px; background: #fff;
border-bottom: 1px solid #f0f0f0; border-bottom: 1px solid #f9f9f9;
}
.file-item:last-child { &:last-child {
border-bottom: none; border-bottom: none;
} }
.file-item:hover {
background-color: #f5f5f5;
} }
.file-info { /* 底部区域 */
.input-footer {
background: #fff;
padding: 12px 20px;
border-top: 1px solid #f0f0f0;
height: auto;
}
.quick-commands {
display: flex; display: flex;
align-items: center; align-items: center;
flex: 1; gap: 8px;
min-width: 0;
.label {
color: #8c8c8c;
font-size: 12px;
} }
.file-icon { .cmd-btn {
margin-right: 8px; font-size: 12px;
color: #1677ff; border-radius: 10px;
flex-shrink: 0; background: #f5f5f5;
border: none;
}
} }
.file-name { .polish-btn {
font-size: 14px; align-self: flex-start;
color: #262626; color: #722ed1;
margin-right: 8px; border-color: #d3adf7;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
}
/* 确保卡片之间有间距 */ &:hover {
.ant-card { background: #f9f0ff;
margin-bottom: 16px;
} }
/* 新增index=5 时的高亮样式(可根据需求自定义) */
.highlight-item {
background-color: #e6f7ff; /* 浅蓝色背景 */
border-left: 3px solid #1677ff; /* 蓝色左侧边框强调 */
padding-left: 15px !important; /* 调整内边距 */
color: #1677ff; /* 文字变蓝 */
} }
</style> </style>