- 三层评测:规则层沿用原五维规则分,语义层用本地 BGE 向量算问答/来源 相关性,评审层复用生成模型按 rubric 打分(忠实度/正确性/清晰度等, 区分 standard/reasoning/dpo 输出类型),任一层失败自动降级 - 组合分 = 规则 35% + 语义 20% + 评审 45%,缺层自动重归一 - 新增 results/evaluate-batch 批量评测接口,镜像批量重生成的并发、 乐观锁与部分成功语义;生成阶段不再展示质量分 - 详情页与结果编辑页新增"数据评测"按钮和批量进度;质量分列悬停弹出 雷达图浮窗(评审 5 维 + 语义 2 维、三层分项、评审理由) - 手动编辑/恢复后重算规则与语义层并丢弃过期评审分,雷达图不再展示 失效数据
669 lines
19 KiB
Vue
669 lines
19 KiB
Vue
<script setup lang="ts">
|
||
import { computed, ref } from 'vue'
|
||
import QualityRadarPopover from './QualityRadarPopover.vue'
|
||
import type { BulkResultRegenerationState, PreviewItem, ResultEvaluationState, ResultItem } from './types'
|
||
import type { DataProcessOutputType } from '@/types/dataProcess'
|
||
|
||
const props = defineProps<{
|
||
items: ResultItem[]
|
||
previewItems: PreviewItem[]
|
||
selectedId: string | null
|
||
regeneratingResultId: string | null
|
||
bulkRegeneration: BulkResultRegenerationState
|
||
evaluation: ResultEvaluationState
|
||
outputType: DataProcessOutputType
|
||
}>()
|
||
|
||
const emit = defineEmits<{
|
||
'update:selectedId': [value: string]
|
||
'update:field': [id: string, field: 'instruction' | 'input' | 'output' | 'chosen' | 'rejected', value: string]
|
||
'regenerate:item': [id: string]
|
||
'regenerate:all': []
|
||
'evaluate:all': []
|
||
}>()
|
||
|
||
const search = ref('')
|
||
const invalidOnly = ref(false)
|
||
const selectedItem = computed(() => props.items.find((item) => item.id === props.selectedId) ?? props.items[0])
|
||
const selectedIndex = computed(() => props.items.findIndex((item) => item.id === selectedItem.value?.id))
|
||
const invalidCount = computed(() => (
|
||
props.items.filter((item) => item.savedStatus === 'invalid').length
|
||
))
|
||
const bulkRegenerationActive = computed(() => props.bulkRegeneration.status === 'running')
|
||
const bulkRegenerationVisible = computed(() => (
|
||
props.bulkRegeneration.status !== 'idle' && props.bulkRegeneration.total > 0
|
||
))
|
||
const evaluationActive = computed(() => props.evaluation.status === 'running')
|
||
const evaluationVisible = computed(() => (
|
||
props.evaluation.status !== 'idle' && props.evaluation.total > 0
|
||
))
|
||
const evaluationPercentage = computed(() => {
|
||
if (!props.evaluation.total) return 0
|
||
return Math.round((props.evaluation.completed / props.evaluation.total) * 100)
|
||
})
|
||
const evaluatedCount = computed(() => props.items.filter((item) => item.qualityDetails?.evaluated).length)
|
||
const bulkRegenerationPercentage = computed(() => (
|
||
props.bulkRegeneration.total > 0
|
||
? Math.round((props.bulkRegeneration.completed / props.bulkRegeneration.total) * 100)
|
||
: 0
|
||
))
|
||
const itemRegenerating = (id: string) => (
|
||
props.regeneratingResultId === id
|
||
|| props.bulkRegeneration.targetIds.includes(id)
|
||
)
|
||
const selectedItemRegenerating = computed(() => (
|
||
selectedItem.value ? itemRegenerating(selectedItem.value.id) : false
|
||
))
|
||
const selectedSource = computed(() => {
|
||
const previewItemId = selectedItem.value?.previewItemId
|
||
if (!previewItemId) return null
|
||
return props.previewItems.find((item) => item.id === previewItemId) ?? null
|
||
})
|
||
const selectedSourceContent = computed(() => (
|
||
selectedSource.value?.editedContent.trim()
|
||
|| selectedSource.value?.originalContent.trim()
|
||
|| ''
|
||
))
|
||
const selectedSourceWasPreprocessed = computed(() => Boolean(
|
||
selectedSource.value
|
||
&& selectedSource.value.editedContent.trim()
|
||
&& selectedSource.value.editedContent !== selectedSource.value.originalContent
|
||
))
|
||
const selectedSourceMeta = computed(() => {
|
||
const source = selectedSource.value
|
||
if (!source) return ''
|
||
const parts: string[] = []
|
||
if (source.sourcePages?.length) {
|
||
const first = source.sourcePages[0]
|
||
const last = source.sourcePages[source.sourcePages.length - 1]
|
||
parts.push(first === last ? `第 ${first} 页` : `第 ${first}–${last} 页`)
|
||
}
|
||
if (source.sourceStartLine != null) {
|
||
parts.push(
|
||
source.sourceEndLine != null && source.sourceEndLine !== source.sourceStartLine
|
||
? `第 ${source.sourceStartLine}–${source.sourceEndLine} 行`
|
||
: `第 ${source.sourceStartLine} 行`,
|
||
)
|
||
}
|
||
if (source.tokenCount > 0) parts.push(`${source.tokenCount} Token`)
|
||
return parts.join(' · ')
|
||
})
|
||
|
||
const filteredItems = computed(() => props.items.filter((item, index) => {
|
||
const keyword = search.value.trim().toLowerCase()
|
||
const matchesSearch = !keyword
|
||
|| item.instruction.toLowerCase().includes(keyword)
|
||
|| item.output.toLowerCase().includes(keyword)
|
||
|| item.chosen.toLowerCase().includes(keyword)
|
||
|| item.rejected.toLowerCase().includes(keyword)
|
||
|| String(index + 1).includes(keyword)
|
||
return matchesSearch && (!invalidOnly.value || item.status === 'invalid')
|
||
}))
|
||
|
||
function selectRelative(offset: number) {
|
||
if (!props.items.length) return
|
||
const nextIndex = Math.min(Math.max(selectedIndex.value + offset, 0), props.items.length - 1)
|
||
emit('update:selectedId', props.items[nextIndex].id)
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<section class="result-step">
|
||
<div class="result-workspace">
|
||
<aside class="result-list-pane" :class="{ 'has-bulk-progress': bulkRegenerationVisible || evaluationVisible }">
|
||
<div class="pane-header result-list-header">
|
||
<div class="result-list-title">
|
||
<strong>生成结果</strong><span>共 {{ items.length }} 条<template v-if="evaluatedCount"> · 已评测 {{ evaluatedCount }}</template></span>
|
||
</div>
|
||
<div class="result-list-actions">
|
||
<el-button
|
||
size="small"
|
||
plain
|
||
:loading="evaluationActive"
|
||
:disabled="!items.length || bulkRegenerationActive || Boolean(regeneratingResultId)"
|
||
@click="emit('evaluate:all')"
|
||
>
|
||
<i v-if="!evaluationActive" class="fa fa-check-square-o" style="margin-right: 4px;" />
|
||
数据评测
|
||
</el-button>
|
||
<el-button
|
||
v-if="invalidCount > 0"
|
||
size="small"
|
||
plain
|
||
type="primary"
|
||
:loading="bulkRegenerationActive"
|
||
:disabled="Boolean(regeneratingResultId) || bulkRegenerationActive || evaluationActive"
|
||
@click="emit('regenerate:all')"
|
||
>
|
||
<i v-if="!bulkRegenerationActive" class="fa fa-refresh" style="margin-right: 4px;" />
|
||
{{ bulkRegenerationActive ? '重新生成中' : `全部重新生成(${invalidCount})` }}
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<div v-if="evaluationVisible" class="bulk-regeneration-progress">
|
||
<div>
|
||
<span>数据评测 {{ evaluation.completed }} / {{ evaluation.total }}</span>
|
||
<span>成功 {{ evaluation.succeeded }} · 失败 {{ evaluation.failed }}</span>
|
||
</div>
|
||
<el-progress
|
||
:percentage="evaluationPercentage"
|
||
:show-text="false"
|
||
:stroke-width="5"
|
||
:color="evaluation.failed > 0 ? '#d97706' : '#5b50f2'"
|
||
/>
|
||
</div>
|
||
<div v-if="bulkRegenerationVisible" class="bulk-regeneration-progress">
|
||
<div>
|
||
<span>已处理 {{ bulkRegeneration.completed }} / {{ bulkRegeneration.total }}</span>
|
||
<span>成功 {{ bulkRegeneration.succeeded }} · 失败 {{ bulkRegeneration.failed }}</span>
|
||
</div>
|
||
<el-progress
|
||
:percentage="bulkRegenerationPercentage"
|
||
:show-text="false"
|
||
:stroke-width="5"
|
||
:color="bulkRegeneration.failed > 0 ? '#d97706' : '#5b50f2'"
|
||
/>
|
||
</div>
|
||
<div class="result-toolbar">
|
||
<el-input v-model="search" clearable size="small" placeholder="搜索结果">
|
||
<template #prefix><i class="fa fa-search" /></template>
|
||
</el-input>
|
||
<el-checkbox v-model="invalidOnly">仅看错误</el-checkbox>
|
||
</div>
|
||
<div class="result-list">
|
||
<button
|
||
v-for="item in filteredItems"
|
||
:key="item.id"
|
||
type="button"
|
||
class="result-item"
|
||
:class="{ 'is-active': item.id === selectedItem?.id }"
|
||
@click="emit('update:selectedId', item.id)"
|
||
>
|
||
<span class="result-index">#{{ String(items.findIndex((entry) => entry.id === item.id) + 1).padStart(3, '0') }}</span>
|
||
<span class="result-copy">
|
||
<strong>{{ item.instruction || '未填写指令' }}</strong>
|
||
<small>{{ outputType === 'dpo' ? (item.chosen || '未填写 Chosen') : (item.output || '未填写输出') }}</small>
|
||
</span>
|
||
<el-popover
|
||
v-if="item.qualityScore != null && item.qualityDetails"
|
||
placement="right"
|
||
:width="296"
|
||
trigger="hover"
|
||
:show-after="150"
|
||
popper-class="quality-radar-popper"
|
||
>
|
||
<template #reference>
|
||
<span
|
||
class="result-score"
|
||
:class="item.qualityScore >= 80 ? 'is-success' : item.qualityScore >= 60 ? 'is-warning' : 'is-danger'"
|
||
@click.stop
|
||
>{{ item.qualityScore.toFixed(0) }}</span>
|
||
</template>
|
||
<QualityRadarPopover :quality="item.qualityDetails" :score="item.qualityScore" />
|
||
</el-popover>
|
||
<i v-if="itemRegenerating(item.id)" class="css-spinner" />
|
||
<i
|
||
v-else
|
||
class="fa"
|
||
:class="item.status === 'invalid' ? 'fa-exclamation-circle is-error' : 'fa-check-circle is-valid'"
|
||
/>
|
||
</button>
|
||
</div>
|
||
</aside>
|
||
|
||
<div v-if="selectedItem" class="result-editor-pane">
|
||
<div class="pane-header">
|
||
<div>
|
||
<strong>结果 #{{ String(selectedIndex + 1).padStart(3, '0') }}</strong>
|
||
<span v-if="selectedItem.status === 'modified'" class="modified-label">已修改</span>
|
||
<el-tag v-if="selectedItem.split" size="small" effect="plain">{{ selectedItem.split }}</el-tag>
|
||
<el-tag
|
||
v-if="selectedItem.qualityScore != null"
|
||
size="small"
|
||
:type="selectedItem.qualityScore >= 80 ? 'success' : selectedItem.qualityScore >= 60 ? 'warning' : 'danger'"
|
||
>质量 {{ selectedItem.qualityScore.toFixed(1) }}</el-tag>
|
||
</div>
|
||
<div class="result-header-actions">
|
||
<el-button
|
||
v-if="selectedItem.savedStatus === 'invalid'"
|
||
size="small"
|
||
plain
|
||
type="primary"
|
||
:loading="selectedItemRegenerating"
|
||
:disabled="bulkRegenerationActive || (Boolean(regeneratingResultId) && !selectedItemRegenerating)"
|
||
@click="emit('regenerate:item', selectedItem.id)"
|
||
>
|
||
<i v-if="!selectedItemRegenerating" class="fa fa-refresh" style="margin-right: 4px;" /> 重新生成
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<article class="source-reference" aria-labelledby="source-reference-title">
|
||
<div class="source-reference-heading">
|
||
<div>
|
||
<strong id="source-reference-title">原文参照</strong>
|
||
<span v-if="selectedSourceMeta">{{ selectedSourceMeta }}</span>
|
||
</div>
|
||
<el-tag v-if="selectedSourceWasPreprocessed" size="small" effect="plain">已智能预处理</el-tag>
|
||
</div>
|
||
<pre v-if="selectedSourceContent">{{ selectedSourceContent }}</pre>
|
||
<p v-else>当前结果没有关联到可用的原文切片。</p>
|
||
<small v-if="selectedSourceWasPreprocessed">这里展示的是实际送入模型的预处理后原文,便于核对问题和答案是否有依据。</small>
|
||
</article>
|
||
|
||
<div class="field-editor">
|
||
<label>Instruction <em>必填</em></label>
|
||
<el-input
|
||
:model-value="selectedItem.instruction"
|
||
:disabled="selectedItemRegenerating"
|
||
type="textarea"
|
||
:rows="3"
|
||
@update:model-value="emit('update:field', selectedItem.id, 'instruction', $event)"
|
||
/>
|
||
</div>
|
||
<div class="field-editor">
|
||
<label>Input <span>选填</span></label>
|
||
<el-input
|
||
:model-value="selectedItem.input"
|
||
:disabled="selectedItemRegenerating"
|
||
type="textarea"
|
||
:rows="2"
|
||
@update:model-value="emit('update:field', selectedItem.id, 'input', $event)"
|
||
/>
|
||
</div>
|
||
<div v-if="outputType !== 'dpo'" class="field-editor">
|
||
<label>Output <em>必填</em></label>
|
||
<el-input
|
||
:model-value="selectedItem.output"
|
||
:disabled="selectedItemRegenerating"
|
||
type="textarea"
|
||
:rows="7"
|
||
@update:model-value="emit('update:field', selectedItem.id, 'output', $event)"
|
||
/>
|
||
</div>
|
||
<template v-else>
|
||
<div class="field-editor dpo-field is-chosen">
|
||
<label>Chosen <em>优选回答,必填</em></label>
|
||
<el-input
|
||
:model-value="selectedItem.chosen"
|
||
:disabled="selectedItemRegenerating"
|
||
type="textarea"
|
||
:rows="6"
|
||
@update:model-value="emit('update:field', selectedItem.id, 'chosen', $event)"
|
||
/>
|
||
</div>
|
||
<div class="field-editor dpo-field is-rejected">
|
||
<label>Rejected <em>拒选回答,必填</em></label>
|
||
<el-input
|
||
:model-value="selectedItem.rejected"
|
||
:disabled="selectedItemRegenerating"
|
||
type="textarea"
|
||
:rows="6"
|
||
@update:model-value="emit('update:field', selectedItem.id, 'rejected', $event)"
|
||
/>
|
||
</div>
|
||
</template>
|
||
<div v-if="selectedItem.error" class="validation-error">
|
||
<i class="fa fa-exclamation-circle" /> {{ selectedItem.error }}
|
||
</div>
|
||
<div v-else class="validation-success">
|
||
<i class="fa fa-check-circle" /> 字段校验通过
|
||
</div>
|
||
<div class="editor-pagination">
|
||
<el-button :disabled="selectedIndex <= 0" @click="selectRelative(-1)">上一条</el-button>
|
||
<span>{{ selectedIndex + 1 }} / {{ items.length }}</span>
|
||
<el-button :disabled="selectedIndex >= items.length - 1" @click="selectRelative(1)">下一条</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
|
||
<style scoped lang="scss">
|
||
.result-step {
|
||
min-width: 0;
|
||
}
|
||
|
||
|
||
.result-workspace {
|
||
display: grid;
|
||
grid-template-columns: minmax(280px, 34fr) minmax(480px, 66fr);
|
||
min-height: clamp(420px, calc(100vh - 500px), 590px);
|
||
overflow: hidden;
|
||
border: 1px solid #e2e5ec;
|
||
border-radius: 9px;
|
||
}
|
||
|
||
.result-list-pane {
|
||
min-width: 0;
|
||
border-right: 1px solid #e5e8ee;
|
||
}
|
||
|
||
.result-list-title {
|
||
min-width: 0;
|
||
|
||
span {
|
||
margin-left: 8px;
|
||
color: #8a93a3;
|
||
font-size: 11px;
|
||
font-weight: 400;
|
||
}
|
||
}
|
||
|
||
.result-list-header {
|
||
gap: 10px;
|
||
|
||
:deep(.el-button) {
|
||
flex: none;
|
||
}
|
||
}
|
||
|
||
.result-list-actions {
|
||
display: flex;
|
||
flex: none;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.result-score {
|
||
flex: none;
|
||
min-width: 34px;
|
||
padding: 2px 8px;
|
||
border-radius: 10px;
|
||
color: #475467;
|
||
background: #f2f4f7;
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
font-variant-numeric: tabular-nums;
|
||
text-align: center;
|
||
cursor: default;
|
||
|
||
&.is-success {
|
||
color: #067647;
|
||
background: #e6f4ee;
|
||
}
|
||
|
||
&.is-warning {
|
||
color: #b54708;
|
||
background: #fef0c7;
|
||
}
|
||
|
||
&.is-danger {
|
||
color: #b42318;
|
||
background: #fee4e2;
|
||
}
|
||
}
|
||
|
||
.pane-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
min-height: 52px;
|
||
padding: 0 15px;
|
||
color: #344054;
|
||
background: #fff;
|
||
border-bottom: 1px solid #e8ebf0;
|
||
font-size: 13px;
|
||
|
||
> div {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
> span,
|
||
.modified-label {
|
||
color: #8a93a3;
|
||
font-size: 11px;
|
||
}
|
||
}
|
||
|
||
.result-toolbar {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) auto;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 10px;
|
||
border-bottom: 1px solid #edf0f5;
|
||
}
|
||
|
||
.bulk-regeneration-progress {
|
||
padding: 9px 12px 10px;
|
||
background: #fafaff;
|
||
border-bottom: 1px solid #e8e7ff;
|
||
|
||
> div {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 10px;
|
||
margin-bottom: 7px;
|
||
color: #667085;
|
||
font-size: 10px;
|
||
}
|
||
}
|
||
|
||
.result-list {
|
||
height: 476px;
|
||
padding: 7px;
|
||
overflow: auto;
|
||
}
|
||
|
||
.has-bulk-progress .result-list {
|
||
height: 420px;
|
||
}
|
||
|
||
.result-header-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.result-item {
|
||
display: grid;
|
||
grid-template-columns: 46px minmax(0, 1fr) 18px;
|
||
align-items: center;
|
||
gap: 8px;
|
||
width: 100%;
|
||
min-height: 62px;
|
||
padding: 9px;
|
||
text-align: left;
|
||
background: #fff;
|
||
border: 1px solid transparent;
|
||
border-bottom-color: #edf0f5;
|
||
cursor: pointer;
|
||
|
||
&:hover,
|
||
&.is-active {
|
||
background: #f7f6ff;
|
||
}
|
||
|
||
&.is-active {
|
||
border-color: #5b50f2;
|
||
border-radius: 6px;
|
||
}
|
||
}
|
||
|
||
.result-index {
|
||
color: #667085;
|
||
font-size: 11px;
|
||
}
|
||
|
||
.result-copy {
|
||
min-width: 0;
|
||
|
||
strong,
|
||
small {
|
||
display: block;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
strong {
|
||
color: #344054;
|
||
font-size: 12px;
|
||
}
|
||
|
||
small {
|
||
margin-top: 5px;
|
||
color: #98a2b3;
|
||
font-size: 10px;
|
||
}
|
||
}
|
||
|
||
.is-valid {
|
||
color: #2ca66a;
|
||
}
|
||
|
||
.is-error {
|
||
color: #d97706;
|
||
}
|
||
|
||
.css-spinner {
|
||
width: 14px;
|
||
height: 14px;
|
||
border: 2px solid rgba(91, 80, 242, 0.2);
|
||
border-top-color: #5b50f2;
|
||
border-radius: 50%;
|
||
animation: css-spin 0.8s linear infinite;
|
||
display: inline-block;
|
||
}
|
||
|
||
@keyframes css-spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
.result-editor-pane {
|
||
min-width: 0;
|
||
}
|
||
|
||
.source-reference {
|
||
margin: 14px 18px 2px;
|
||
padding: 12px 14px;
|
||
border: 1px solid #dfe4ec;
|
||
border-radius: 7px;
|
||
background: #f8fafc;
|
||
|
||
pre {
|
||
max-height: 168px;
|
||
margin: 10px 0 0;
|
||
overflow: auto;
|
||
color: #344054;
|
||
font-family: inherit;
|
||
font-size: 12px;
|
||
line-height: 1.7;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
}
|
||
|
||
p {
|
||
margin: 10px 0 0;
|
||
color: #98a2b3;
|
||
font-size: 12px;
|
||
}
|
||
|
||
> small {
|
||
display: block;
|
||
margin-top: 8px;
|
||
color: #8a93a3;
|
||
font-size: 10px;
|
||
}
|
||
}
|
||
|
||
.source-reference-heading {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
|
||
> div {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 8px;
|
||
}
|
||
|
||
strong {
|
||
color: #344054;
|
||
font-size: 12px;
|
||
}
|
||
|
||
span {
|
||
color: #8a93a3;
|
||
font-size: 10px;
|
||
}
|
||
}
|
||
|
||
.field-editor {
|
||
padding: 13px 18px 0;
|
||
|
||
label {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
margin-bottom: 7px;
|
||
color: #344054;
|
||
font-size: 12px;
|
||
font-weight: 650;
|
||
}
|
||
|
||
em {
|
||
color: #e05252;
|
||
font-size: 10px;
|
||
font-style: normal;
|
||
font-weight: 400;
|
||
}
|
||
|
||
span {
|
||
color: #98a2b3;
|
||
font-size: 10px;
|
||
font-weight: 400;
|
||
}
|
||
}
|
||
|
||
.validation-error,
|
||
.validation-success {
|
||
margin: 12px 18px 0;
|
||
padding: 9px 11px;
|
||
font-size: 11px;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.validation-error {
|
||
color: #b45309;
|
||
background: #fff7e8;
|
||
}
|
||
|
||
.validation-success {
|
||
color: #25895c;
|
||
background: #edf9f3;
|
||
}
|
||
|
||
.editor-pagination {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
gap: 12px;
|
||
padding: 12px 18px;
|
||
|
||
span {
|
||
color: #8a93a3;
|
||
font-size: 11px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 900px) {
|
||
.result-workspace {
|
||
grid-template-columns: minmax(0, 1fr);
|
||
}
|
||
|
||
.result-list-pane {
|
||
border-right: 0;
|
||
border-bottom: 1px solid #e5e8ee;
|
||
}
|
||
|
||
.result-list {
|
||
height: 260px;
|
||
}
|
||
}
|
||
</style>
|