| | |
| | | <template> |
| | | <PageWrapper title="欢迎" contentBackground> |
| | | <div class="welcome-container"> |
| | | <div class="welcome-content"> |
| | | <h1 class="welcome-title">您好,欢迎访问</h1> |
| | | <h2 class="welcome-system-name">艾亿欧AIEO智能业务管理系统!</h2> |
| | | <PageWrapper title="客户详情页" contentBackground> |
| | | <a-card title="基本信息"> |
| | | <a-descriptions title="" :column="3"> |
| | | <a-descriptions-item label="企业名称"> |
| | | <span>{{ formData.customer?.enterpriseName || '-' }}</span> |
| | | </a-descriptions-item> |
| | | <a-descriptions-item label="行业"> |
| | | <span>{{ formData.customer?.industry || '-' }}</span> |
| | | </a-descriptions-item> |
| | | <a-descriptions-item label="客户地址"> |
| | | <span>{{ formData.customer?.customerAddress || '-' }}</span> |
| | | </a-descriptions-item> |
| | | <a-descriptions-item label="对接人"> |
| | | <span>{{ formData.customer?.contactPerson || '-' }}</span> |
| | | </a-descriptions-item> |
| | | <a-descriptions-item label="客户电话"> |
| | | <span>{{ formData.customer?.customerPhone || '-' }}</span> |
| | | </a-descriptions-item> |
| | | <a-descriptions-item label="客户邮箱"> |
| | | <span>{{ formData.customer?.customerEmail || '-' }}</span> |
| | | </a-descriptions-item> |
| | | </a-descriptions> |
| | | </a-card> |
| | | <a-card title="合同信息"> |
| | | <a-descriptions title="" :column="1"> |
| | | <a-descriptions-item label="紧急状态"> |
| | | <span>{{ formData.emergencyStatus === 1 ? '紧急' : formData.emergencyStatus === 3 ? '正常' : '-' }}</span> |
| | | </a-descriptions-item> |
| | | <a-descriptions-item label="合作周期"> |
| | | <span>{{ formData.month ? `${formData.month}月` : '-' }}</span> |
| | | </a-descriptions-item> |
| | | <!-- <a-descriptions-item label="合作开始时间"> |
| | | <span>{{ formData.startDate || '-' }}</span> |
| | | </a-descriptions-item> |
| | | <a-descriptions-item label="合作结束时间"> |
| | | <span>{{ formData.endDate || '-' }}</span> |
| | | </a-descriptions-item> --> |
| | | <a-descriptions-item label="合同"> |
| | | <div v-if="contractFiles.length > 0"> |
| | | <div v-for="file in contractFiles" :key="file.uid" class="file-item"> |
| | | <div class="file-header"> |
| | | <a @click="handleDownload(file)" style="display: inline-block">{{ getFileName(file) }}</a> |
| | | <a @click="toggleFilePreview(file)" class="preview-toggle"> |
| | | {{ isFilePreviewOpen(file) ? '收起' : '[预览]' }} |
| | | </a> |
| | | </div> |
| | | <div v-if="isFilePreviewOpen(file)" class="docx-preview"> |
| | | <div v-if="isReviewFile(file) && getFileUrl(file)"> |
| | | <vue-office-docx :src="getFileUrl(file)" style="width: 100%; height: 600px;" v-if="isDocxFile(file)"/> |
| | | <vue-office-excel :src="getFileUrl(file)" style="width: 100%; height: 600px;" v-if="isXlsxFile(file)"/> |
| | | <vue-office-pdf :src="getFileUrl(file)" style="width: 100%; height: 600px;" v-if="isPdfFile(file)"/> |
| | | <template v-if="isTxtFile(file)"> |
| | | <a-spin :spinning="isTxtLoading(file)"> |
| | | <pre class="code-area">{{ getTxtContent(file) }}</pre> |
| | | </a-spin> |
| | | </template> |
| | | <template v-if="isImageFile(file)"> |
| | | <img :src="getFileUrl(file)" style="width: 100%; height: 600px;" /> |
| | | </template> |
| | | </div> |
| | | <div v-else class="preview-not-supported"> |
| | | <a-empty description="当前文件类型的预览功能不支持!" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div v-else> |
| | | <span>-</span> |
| | | </div> |
| | | </a-descriptions-item> |
| | | <a-descriptions-item label="附件"> |
| | | <div v-if="attachmentFiles.length > 0"> |
| | | <div v-for="file in attachmentFiles" :key="file.uid" class="file-item"> |
| | | <div class="file-header"> |
| | | <a @click="handleDownload(file)" style="display: inline-block">{{ getFileName(file) }}</a> |
| | | <a @click="toggleFilePreview(file)" class="preview-toggle"> |
| | | {{ isFilePreviewOpen(file) ? '收起' : '[预览]' }} |
| | | </a> |
| | | </div> |
| | | <div v-if="isFilePreviewOpen(file)" class="docx-preview"> |
| | | <div v-if="isReviewFile(file) && getFileUrl(file)"> |
| | | <vue-office-docx :src="getFileUrl(file)" style="width: 100%; height: 600px;" v-if="isDocxFile(file)"/> |
| | | <vue-office-excel :src="getFileUrl(file)" style="width: 100%; height: 600px;" v-if="isXlsxFile(file)"/> |
| | | <vue-office-pdf :src="getFileUrl(file)" style="width: 100%; height: 600px;" v-if="isPdfFile(file)"/> |
| | | <template v-if="isTxtFile(file)"> |
| | | <a-spin :spinning="isTxtLoading(file)"> |
| | | <pre class="code-area">{{ getTxtContent(file) }}</pre> |
| | | </a-spin> |
| | | </template> |
| | | <template v-if="isImageFile(file)"> |
| | | <img :src="getFileUrl(file)" style="width: 100%; height: 600px;" /> |
| | | </template> |
| | | </div> |
| | | <div v-else class="preview-not-supported"> |
| | | <a-empty description="当前文件类型的预览功能不支持!" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div v-else> |
| | | <span>-</span> |
| | | </div> |
| | | </a-descriptions-item> |
| | | </a-descriptions> |
| | | </a-card> |
| | | <a-card title="语义词信息"> |
| | | <div v-if="semanticWords.length > 0"> |
| | | <a-descriptions title="" :column="1"> |
| | | <a-descriptions-item v-for="word in semanticWords" :key="word.id" :label="`语义词 ${semanticWords.indexOf(word) + 1}`"> |
| | | <div style="display: flex; flex-direction: column; gap: 12px; width: 100%"> |
| | | <div style="display: flex; gap: 16px; width: 100%"> |
| | | <span style="flex: 1;"><strong>类型:</strong>{{ word.category === '1' ? '品牌' : word.category === '2' ? '地区词' : '全国词' }}</span> |
| | | <span style="flex: 1;"><strong>语义词:</strong>{{ word.word || '-' }}</span> |
| | | <span style="flex: 1;"><strong>露出词:</strong>{{ word.outWord || '-' }}</span> |
| | | <span style="flex: 1;"><strong>价格:</strong>{{ word.price ? `${formatPrice(word.price)}元` : '-' }}</span> |
| | | <span style="flex: 1;"><strong>合作周期:</strong>{{ word.month ? `${word.month}月` : '-' }}</span> |
| | | <span style="flex: 1;"><strong>签约排名:</strong>{{ formatRanking(word.ranking) }}</span> |
| | | <span style="flex: 1;"><strong>验收指标(>=):</strong>{{ word.acceptindicator ? `${word.acceptindicator}%` : '-' }}</span> |
| | | </div> |
| | | </div> |
| | | </a-descriptions-item> |
| | | </a-descriptions> |
| | | </div> |
| | | <div v-else> |
| | | <a-empty description="暂无语义词数据" /> |
| | | </div> |
| | | </a-card> |
| | | <!-- 返回按钮 --> |
| | | <div class="action-buttons"> |
| | | <a-button type="default" @click="handleBack" class="back-btn">返回</a-button> |
| | | </div> |
| | | </PageWrapper> |
| | | </template> |
| | | <script lang="ts"> |
| | | import { defineComponent, reactive, ref, onMounted } from 'vue'; |
| | | import { PageWrapper } from '/@/components/Page'; |
| | | import { message } from 'ant-design-vue'; |
| | | import { useRoute, useRouter } from 'vue-router'; |
| | | import { defHttp } from '/@/utils/http/axios'; |
| | | import { getFileAccessHttpUrl } from '/@/utils/common/compUtils'; |
| | | import VueOfficeDocx from '@vue-office/docx'; |
| | | import VueOfficeExcel from '@vue-office/excel'; |
| | | import VueOfficePdf from '@vue-office/pdf'; |
| | | import '@vue-office/docx/lib/v3/index.css'; |
| | | import '@vue-office/excel/lib/v3/index.css'; |
| | | |
| | | <script lang="ts" setup> |
| | | // 欢迎页面组件 |
| | | export default defineComponent({ |
| | | name: 'DescDetailKehu', |
| | | components: { PageWrapper, VueOfficeDocx, VueOfficeExcel, VueOfficePdf }, |
| | | setup() { |
| | | const route = useRoute(); |
| | | const router = useRouter(); |
| | | |
| | | // 初始数据 |
| | | const initialData = { |
| | | id: '', |
| | | createTime: '', |
| | | updateBy: null, |
| | | updateTime: null, |
| | | agentsId: null, |
| | | agentsName: null, |
| | | contractFileList: null, |
| | | contractName: '', |
| | | createBy: '', |
| | | customer: { |
| | | id: '', |
| | | createBy: '', |
| | | createTime: '', |
| | | updateBy: null, |
| | | updateTime: null, |
| | | agentsName: null, |
| | | contactPerson: '', |
| | | contract: null, |
| | | customerAddress: '', |
| | | customerEmail: '', |
| | | customerPhone: '', |
| | | enterpriseName: '', |
| | | industry: '', |
| | | semanticWordList: null, |
| | | }, |
| | | customerName: '', |
| | | endDate: '', |
| | | rejectionReasons: null, |
| | | reviewStatus: '', |
| | | semanticWordList: null, |
| | | startDate: '', |
| | | sysOrgCode: '', |
| | | emergencyStatus: 3, // 默认正常状态 |
| | | month: null, // 合作周期(月) |
| | | }; |
| | | |
| | | // 响应式表单数据 |
| | | const formData = reactive({ ...initialData }); |
| | | |
| | | // 语义词列表 |
| | | const semanticWords = ref<any[]>([]); |
| | | |
| | | // 合同文件列表 |
| | | const contractFiles = ref<any[]>([]); |
| | | const attachmentFiles = ref<any[]>([]); |
| | | |
| | | // 文件预览展开状态(使用文件 uid 作为 key) |
| | | const filePreviewState = reactive<Record<string, boolean>>({}); |
| | | const txtContentMap = reactive<Record<string, string>>({}); |
| | | const txtLoadingMap = reactive<Record<string, boolean>>({}); |
| | | |
| | | // 加载客户数据 |
| | | const loadCustomerData = async () => { |
| | | const id = route.query.id as string; |
| | | if (id) { |
| | | try { |
| | | // 调用API根据id获取客户数据 |
| | | const result = await defHttp.get({ |
| | | url: '/contract/contract/queryById', |
| | | params: { id }, |
| | | }); |
| | | console.log('客户详情数据:', result); |
| | | if (result) { |
| | | const customerData = result; |
| | | // 填充表单数据 |
| | | Object.assign(formData, customerData); |
| | | // 确保 emergencyStatus 正确设置(1=紧急,3=正常) |
| | | const emergencyStatus = Number(customerData.emergencyStatus); |
| | | if (emergencyStatus === 1 || emergencyStatus === 3) { |
| | | formData.emergencyStatus = emergencyStatus; |
| | | } else { |
| | | formData.emergencyStatus = 3; // 默认正常状态 |
| | | } |
| | | |
| | | // 加载合同文件数据 |
| | | await loadContractFiles(id); |
| | | |
| | | // 加载语义词数据 |
| | | await loadSemanticWords(id); |
| | | } else { |
| | | message.error('加载客户详情数据失败'); |
| | | } |
| | | } catch (error) { |
| | | console.error('加载客户详情数据失败:', error); |
| | | message.error('加载客户详情数据失败'); |
| | | } |
| | | } else { |
| | | message.error('缺少客户ID参数'); |
| | | } |
| | | }; |
| | | |
| | | // 加载合同文件数据 |
| | | const loadContractFiles = async (id: string) => { |
| | | try { |
| | | const result = await defHttp.get({ |
| | | url: '/contract/contract/queryContractFileByMainId', |
| | | params: { id }, |
| | | }); |
| | | console.log('合同文件数据:', result); |
| | | // 这里可以根据返回的数据结构处理合同文件 |
| | | if (result && Array.isArray(result)) { |
| | | // 根据 fileType 区分合同文件和附件 |
| | | const contractFileList: any[] = []; |
| | | const attachmentFileList: any[] = []; |
| | | |
| | | result.forEach((file: any) => { |
| | | const fileItem = { |
| | | uid: file.id, |
| | | name: file.appendixFile || '文件', |
| | | status: 'done', |
| | | url: file.appendixFile, |
| | | }; |
| | | |
| | | if (file.fileType === '合同文件') { |
| | | contractFileList.push(fileItem); |
| | | } else if (file.fileType === '合同附件') { |
| | | attachmentFileList.push(fileItem); |
| | | } |
| | | }); |
| | | |
| | | contractFiles.value = contractFileList; |
| | | attachmentFiles.value = attachmentFileList; |
| | | } |
| | | } catch (error) { |
| | | console.error('加载合同文件失败:', error); |
| | | } |
| | | }; |
| | | // 加载语义词数据 |
| | | const loadSemanticWords = async (id: string) => { |
| | | try { |
| | | const result = await defHttp.get({ |
| | | url: '/contract/contract/querySemanticWordByMainId', |
| | | params: { id }, |
| | | }); |
| | | console.log('语义词数据:', result); |
| | | // 这里可以根据返回的数据结构处理语义词 |
| | | if (result && Array.isArray(result)) { |
| | | // 将语义词列表保存到响应式变量中 |
| | | semanticWords.value = result; |
| | | } |
| | | } catch (error) { |
| | | console.error('加载语义词失败:', error); |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | loadCustomerData(); |
| | | }); |
| | | |
| | | // 工具方法:获取文件名(去掉 temp/) |
| | | const getFileName = (file: any) => { |
| | | const name = file.name || ''; |
| | | if (name.startsWith('temp/')) { |
| | | return name.substring(5); |
| | | } |
| | | return name; |
| | | }; |
| | | |
| | | // 工具方法:获取文件的完整访问 URL |
| | | const getFileUrl = (file: any) => { |
| | | if (!file.url) { |
| | | return ''; |
| | | } |
| | | return getFileAccessHttpUrl(file.url); |
| | | }; |
| | | |
| | | // 统一获取文件 key,避免重复逻辑 |
| | | const getFileKey = (file: any) => file?.uid || file?.id || file?.url || file?.name || ''; |
| | | |
| | | // 读取 TXT 文本内容用于预览 |
| | | const fetchTxtContent = async (file: any) => { |
| | | const fileKey = getFileKey(file); |
| | | if (!fileKey || txtLoadingMap[fileKey]) return; |
| | | |
| | | txtLoadingMap[fileKey] = true; |
| | | try { |
| | | const url = getFileUrl(file); |
| | | if (!url) throw new Error('TXT 文件地址不存在'); |
| | | const resp = await fetch(url); |
| | | if (!resp.ok) throw new Error(`请求失败:${resp.status}`); |
| | | const text = await resp.text(); |
| | | txtContentMap[fileKey] = text || '(文件为空)'; |
| | | } catch (error) { |
| | | console.error('TXT 文件加载失败', error); |
| | | txtContentMap[fileKey] = 'TXT 文件预览失败,请稍后重试'; |
| | | message.error('TXT 文件预览失败'); |
| | | } finally { |
| | | txtLoadingMap[fileKey] = false; |
| | | } |
| | | }; |
| | | |
| | | const isReviewFile = (file: any) => { |
| | | if (!file || !file.url) { |
| | | return false; |
| | | } |
| | | const fileName = file.name || file.url || ''; |
| | | const lowerName = fileName.toLowerCase(); |
| | | return lowerName.endsWith('.docx') || lowerName.endsWith('.xlsx') || lowerName.endsWith('.pdf') || lowerName.endsWith('.txt') ||lowerName.endsWith('.jpg') || lowerName.endsWith('.jpeg') || lowerName.endsWith('.png') || lowerName.endsWith('.gif') || lowerName.endsWith('.bmp') || lowerName.endsWith('.webp'); |
| | | }; |
| | | |
| | | // 判断是否为 DOCX 文件 |
| | | const isDocxFile = (file: any) => { |
| | | if (!file) { |
| | | return false; |
| | | } |
| | | const fileName = file.name || file.url || ''; |
| | | const lowerName = fileName.toLowerCase(); |
| | | return lowerName.endsWith('.docx'); |
| | | }; |
| | | |
| | | // 判断是否为 XLSX 文件 |
| | | const isXlsxFile = (file: any) => { |
| | | if (!file) { |
| | | return false; |
| | | } |
| | | const fileName = file.name || file.url || ''; |
| | | const lowerName = fileName.toLowerCase(); |
| | | return lowerName.endsWith('.xlsx'); |
| | | }; |
| | | |
| | | // 判断是否为 PDF 文件 |
| | | const isPdfFile = (file: any) => { |
| | | if (!file) { |
| | | return false; |
| | | } |
| | | const fileName = file.name || file.url || ''; |
| | | const lowerName = fileName.toLowerCase(); |
| | | return lowerName.endsWith('.pdf'); |
| | | }; |
| | | |
| | | // 判断是否为 TXT 文件 |
| | | const isTxtFile = (file: any) => { |
| | | if (!file) { |
| | | return false; |
| | | } |
| | | const fileName = file.name || file.url || ''; |
| | | const lowerName = fileName.toLowerCase(); |
| | | return lowerName.endsWith('.txt'); |
| | | }; |
| | | |
| | | // 判断是否为图片文件 |
| | | const isImageFile = (file: any) => { |
| | | if (!file) { |
| | | return false; |
| | | } |
| | | const fileName = file.name || file.url || ''; |
| | | const lowerName = fileName.toLowerCase(); |
| | | return lowerName.endsWith('.jpg') || lowerName.endsWith('.jpeg') || lowerName.endsWith('.png') || lowerName.endsWith('.gif') || lowerName.endsWith('.bmp') || lowerName.endsWith('.webp'); |
| | | }; |
| | | |
| | | const getTxtContent = (file: any) => { |
| | | const key = getFileKey(file); |
| | | return txtContentMap[key] || '加载中...'; |
| | | }; |
| | | |
| | | const isTxtLoading = (file: any) => { |
| | | const key = getFileKey(file); |
| | | return !!txtLoadingMap[key]; |
| | | }; |
| | | |
| | | // 切换文件预览状态 |
| | | const toggleFilePreview = (file: any) => { |
| | | const fileId = getFileKey(file); |
| | | if (!fileId) return; |
| | | |
| | | const nextState = !filePreviewState[fileId]; |
| | | filePreviewState[fileId] = nextState; |
| | | |
| | | // 打开 TXT 时加载内容 |
| | | if (nextState && isTxtFile(file) && !txtContentMap[fileId]) { |
| | | fetchTxtContent(file); |
| | | } |
| | | }; |
| | | |
| | | // 检查文件是否展开预览 |
| | | const isFilePreviewOpen = (file: any) => { |
| | | const fileId = getFileKey(file); |
| | | return filePreviewState[fileId] || false; |
| | | }; |
| | | |
| | | // 文件下载功能 |
| | | const handleDownload = (file: any) => { |
| | | if (!file.url) { |
| | | message.error('文件链接不存在'); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | // 创建一个隐藏的a标签进行下载 |
| | | const link = document.createElement('a'); |
| | | link.href = getFileAccessHttpUrl(file.url); |
| | | link.download = getFileName(file); |
| | | link.target = '_blank'; |
| | | link.style.display = 'none'; |
| | | |
| | | // 添加到DOM中 |
| | | document.body.appendChild(link); |
| | | |
| | | // 触发点击事件 |
| | | link.click(); |
| | | |
| | | // 清理DOM |
| | | document.body.removeChild(link); |
| | | } catch (error) { |
| | | console.error('文件下载失败:', error); |
| | | message.error('文件下载失败'); |
| | | } |
| | | }; |
| | | |
| | | // 返回功能 |
| | | const handleBack = () => { |
| | | router.back(); |
| | | }; |
| | | |
| | | // 格式化价格 |
| | | const formatPrice = (price: number) => { |
| | | if (!price) return '-'; |
| | | return price.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); |
| | | }; |
| | | |
| | | // 格式化签约排名 |
| | | const formatRanking = (ranking: any) => { |
| | | if (ranking === 1 || ranking === 2 || ranking === 3) { |
| | | return ranking.toString(); |
| | | } else if (ranking === '保展现不保排名') { |
| | | return '保展现不保排名'; |
| | | } |
| | | return '-'; |
| | | }; |
| | | |
| | | return { |
| | | formData, |
| | | contractFiles, |
| | | attachmentFiles, |
| | | semanticWords, |
| | | handleDownload, |
| | | handleBack, |
| | | getFileName, |
| | | getFileUrl, |
| | | isReviewFile, |
| | | isDocxFile, |
| | | isXlsxFile, |
| | | isPdfFile, |
| | | isTxtFile, |
| | | isImageFile, |
| | | getTxtContent, |
| | | isTxtLoading, |
| | | toggleFilePreview, |
| | | isFilePreviewOpen, |
| | | formatPrice, |
| | | formatRanking, |
| | | }; |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .welcome-container { |
| | | min-height: calc(100vh - 200px); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background-color: #f0f2f5; |
| | | } |
| | | .desc-wrap { |
| | | padding: 16px; |
| | | background-color: @component-background; |
| | | } |
| | | |
| | | .welcome-content { |
| | | text-align: center; |
| | | padding: 40px; |
| | | } |
| | | .action-buttons { |
| | | position: fixed; |
| | | right: 24px; |
| | | bottom: 24px; |
| | | display: flex; |
| | | gap: 12px; |
| | | z-index: 1000; |
| | | |
| | | .welcome-title { |
| | | font-size: 32px; |
| | | color: #333; |
| | | margin-bottom: 20px; |
| | | } |
| | | .back-btn { |
| | | min-width: 80px; |
| | | height: 40px; |
| | | border-radius: 6px; |
| | | font-weight: 500; |
| | | background-color: #f5f5f5; |
| | | border-color: #d9d9d9; |
| | | color: #666; |
| | | |
| | | .welcome-system-name { |
| | | font-size: 40px; |
| | | color: #1890ff; |
| | | font-weight: bold; |
| | | } |
| | | &:hover { |
| | | background-color: #e6f7ff; |
| | | border-color: #40a9ff; |
| | | color: #40a9ff; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 文件下载链接样式 |
| | | a { |
| | | color: #1890ff; |
| | | text-decoration: none; |
| | | cursor: pointer; |
| | | transition: color 0.3s; |
| | | |
| | | &:hover { |
| | | color: #40a9ff; |
| | | text-decoration: underline; |
| | | } |
| | | |
| | | &:active { |
| | | color: #096dd9; |
| | | } |
| | | } |
| | | |
| | | .file-item { |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | } |
| | | |
| | | .file-header { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .preview-toggle { |
| | | margin-left: 8px; |
| | | color: #1890ff; |
| | | cursor: pointer; |
| | | font-size: 14px; |
| | | user-select: none; |
| | | |
| | | &:hover { |
| | | color: #40a9ff; |
| | | text-decoration: underline; |
| | | } |
| | | } |
| | | |
| | | .docx-preview { |
| | | margin-top: 12px; |
| | | border: 1px solid #e8e8e8; |
| | | border-radius: 4px; |
| | | overflow: hidden; |
| | | background: #fff; |
| | | } |
| | | |
| | | .preview-not-supported { |
| | | padding: 40px 20px; |
| | | text-align: center; |
| | | min-height: 200px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .code-area { |
| | | margin: 0; |
| | | padding: 12px; |
| | | background: #e2e8f0; |
| | | color: #0f172a; |
| | | border-radius: 4px; |
| | | font-size: 13px; |
| | | line-height: 1.6; |
| | | white-space: pre-wrap; |
| | | word-break: break-word; |
| | | min-height: 120px; |
| | | max-height: 600px; |
| | | overflow: auto; |
| | | } |
| | | </style> |