566 lines
15 KiB
Vue
566 lines
15 KiB
Vue
|
|
<script setup lang="ts">
|
|||
|
|
import { computed, ref, watch } from 'vue'
|
|||
|
|
import type { UploadFile } from 'element-plus'
|
|||
|
|
import type { ExternalDataSource, ProcessType } from './types'
|
|||
|
|
|
|||
|
|
interface UploadedSourceFile {
|
|||
|
|
uid: string | number
|
|||
|
|
name: string
|
|||
|
|
size: number
|
|||
|
|
count: number
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const props = defineProps<{
|
|||
|
|
processType: ProcessType
|
|||
|
|
uploadedFiles: UploadedSourceFile[]
|
|||
|
|
externalSource: ExternalDataSource
|
|||
|
|
externalPulling: boolean
|
|||
|
|
externalConnected: boolean
|
|||
|
|
}>()
|
|||
|
|
|
|||
|
|
const emit = defineEmits<{
|
|||
|
|
'update:externalSource': [value: ExternalDataSource]
|
|||
|
|
'file-change': [file: UploadFile]
|
|||
|
|
'remove-file': [uid: string | number]
|
|||
|
|
'use-sample': []
|
|||
|
|
'test-connection': []
|
|||
|
|
'pull-data': []
|
|||
|
|
}>()
|
|||
|
|
|
|||
|
|
const DATA_SOURCE_TYPES = [
|
|||
|
|
{ value: 'mysql', label: 'MySQL' },
|
|||
|
|
{ value: 'postgresql', label: 'PostgreSQL' },
|
|||
|
|
{ value: 'mongodb', label: 'MongoDB' },
|
|||
|
|
{ value: 'api', label: 'REST API' },
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
const AUTH_MODES = [
|
|||
|
|
{ value: 'none', label: '免鉴权' },
|
|||
|
|
{ value: 'basic', label: '账号密码' },
|
|||
|
|
{ value: 'token', label: 'Token' },
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
const FILE_PAGE_SIZE = 10
|
|||
|
|
const currentFilePage = ref(1)
|
|||
|
|
|
|||
|
|
const isExternal = computed(() => props.processType === 'external')
|
|||
|
|
|
|||
|
|
const uploadAccept = computed(() => props.processType === 'unstructured'
|
|||
|
|
? '.txt,.md,.pdf,.docx,.doc,.json,.jsonl'
|
|||
|
|
: '.json,.jsonl,.csv,.xlsx,.xls')
|
|||
|
|
|
|||
|
|
const pagedUploadedFiles = computed(() => {
|
|||
|
|
const start = (currentFilePage.value - 1) * FILE_PAGE_SIZE
|
|||
|
|
return props.uploadedFiles.slice(start, start + FILE_PAGE_SIZE)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
watch(() => props.uploadedFiles.length, (newLength, oldLength) => {
|
|||
|
|
const totalPages = Math.max(1, Math.ceil(newLength / FILE_PAGE_SIZE))
|
|||
|
|
if (newLength > oldLength) {
|
|||
|
|
currentFilePage.value = totalPages
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
currentFilePage.value = Math.min(currentFilePage.value, totalPages)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
watch(() => props.processType, () => {
|
|||
|
|
currentFilePage.value = 1
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
function updateExternalField<K extends keyof ExternalDataSource>(field: K, value: ExternalDataSource[K]) {
|
|||
|
|
emit('update:externalSource', { ...props.externalSource, [field]: value })
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function formatSize(size: number) {
|
|||
|
|
if (!size) return '0 KB'
|
|||
|
|
if (size >= 1024 * 1024) return `${(size / 1024 / 1024).toFixed(1)} MB`
|
|||
|
|
return `${(size / 1024).toFixed(1)} KB`
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<section class="source-upload-step" aria-labelledby="source-upload-title">
|
|||
|
|
<div v-if="isExternal" class="form-section external-section">
|
|||
|
|
<div class="section-title-row">
|
|||
|
|
<div>
|
|||
|
|
<h3 id="source-upload-title">数据源配置</h3>
|
|||
|
|
<p>配置并验证外部数据源,拉取成功后可在下一步预览数据内容</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="external-form">
|
|||
|
|
<el-form label-position="top" class="external-grid">
|
|||
|
|
<el-form-item label="数据源类型">
|
|||
|
|
<el-select
|
|||
|
|
:model-value="externalSource.type"
|
|||
|
|
placeholder="请选择数据源类型"
|
|||
|
|
aria-label="数据源类型"
|
|||
|
|
@update:model-value="updateExternalField('type', $event)"
|
|||
|
|
>
|
|||
|
|
<el-option
|
|||
|
|
v-for="item in DATA_SOURCE_TYPES"
|
|||
|
|
:key="item.value"
|
|||
|
|
:label="item.label"
|
|||
|
|
:value="item.value"
|
|||
|
|
/>
|
|||
|
|
</el-select>
|
|||
|
|
</el-form-item>
|
|||
|
|
<el-form-item label="地址 / URL">
|
|||
|
|
<el-input
|
|||
|
|
:model-value="externalSource.url"
|
|||
|
|
placeholder="例如:mysql://host:3306/db 或 https://api.example.com/data"
|
|||
|
|
aria-label="数据源地址或 URL"
|
|||
|
|
@update:model-value="updateExternalField('url', $event)"
|
|||
|
|
/>
|
|||
|
|
</el-form-item>
|
|||
|
|
<el-form-item label="鉴权方式">
|
|||
|
|
<el-select
|
|||
|
|
:model-value="externalSource.authMode"
|
|||
|
|
aria-label="鉴权方式"
|
|||
|
|
@update:model-value="updateExternalField('authMode', $event)"
|
|||
|
|
>
|
|||
|
|
<el-option
|
|||
|
|
v-for="item in AUTH_MODES"
|
|||
|
|
:key="item.value"
|
|||
|
|
:label="item.label"
|
|||
|
|
:value="item.value"
|
|||
|
|
/>
|
|||
|
|
</el-select>
|
|||
|
|
</el-form-item>
|
|||
|
|
<el-form-item v-if="externalSource.authMode === 'basic'" label="账号">
|
|||
|
|
<el-input
|
|||
|
|
:model-value="externalSource.username"
|
|||
|
|
autocomplete="username"
|
|||
|
|
placeholder="请输入账号"
|
|||
|
|
aria-label="数据源账号"
|
|||
|
|
@update:model-value="updateExternalField('username', $event)"
|
|||
|
|
/>
|
|||
|
|
</el-form-item>
|
|||
|
|
<el-form-item v-if="externalSource.authMode === 'basic'" label="密码">
|
|||
|
|
<el-input
|
|||
|
|
:model-value="externalSource.password"
|
|||
|
|
type="password"
|
|||
|
|
show-password
|
|||
|
|
autocomplete="current-password"
|
|||
|
|
placeholder="请输入密码"
|
|||
|
|
aria-label="数据源密码"
|
|||
|
|
@update:model-value="updateExternalField('password', $event)"
|
|||
|
|
/>
|
|||
|
|
</el-form-item>
|
|||
|
|
<el-form-item v-if="externalSource.authMode === 'token'" label="Token">
|
|||
|
|
<el-input
|
|||
|
|
:model-value="externalSource.token"
|
|||
|
|
type="password"
|
|||
|
|
show-password
|
|||
|
|
autocomplete="off"
|
|||
|
|
placeholder="请输入访问 Token"
|
|||
|
|
aria-label="数据源访问 Token"
|
|||
|
|
@update:model-value="updateExternalField('token', $event)"
|
|||
|
|
/>
|
|||
|
|
</el-form-item>
|
|||
|
|
<el-form-item label="拉取条数">
|
|||
|
|
<el-input-number
|
|||
|
|
:model-value="externalSource.limit"
|
|||
|
|
:min="1"
|
|||
|
|
:max="100000"
|
|||
|
|
:step="100"
|
|||
|
|
controls-position="right"
|
|||
|
|
aria-label="数据拉取条数"
|
|||
|
|
@update:model-value="updateExternalField('limit', Number($event) || 0)"
|
|||
|
|
/>
|
|||
|
|
</el-form-item>
|
|||
|
|
</el-form>
|
|||
|
|
|
|||
|
|
<div class="external-actions">
|
|||
|
|
<el-button
|
|||
|
|
:loading="externalPulling && !externalConnected"
|
|||
|
|
:disabled="externalPulling"
|
|||
|
|
plain
|
|||
|
|
@click="emit('test-connection')"
|
|||
|
|
>
|
|||
|
|
测试连接
|
|||
|
|
</el-button>
|
|||
|
|
<el-button
|
|||
|
|
type="primary"
|
|||
|
|
:loading="externalPulling"
|
|||
|
|
:disabled="externalPulling"
|
|||
|
|
@click="emit('pull-data')"
|
|||
|
|
>
|
|||
|
|
拉取数据
|
|||
|
|
</el-button>
|
|||
|
|
<span v-if="externalConnected" class="external-status is-connected" role="status">
|
|||
|
|
<i class="fa fa-check-circle" aria-hidden="true" /> 连接正常
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<section v-if="uploadedFiles.length" class="uploaded-file-list" aria-label="已拉取数据列表">
|
|||
|
|
<div class="uploaded-file-list-header">
|
|||
|
|
<span>已拉取 {{ uploadedFiles.length }} 个数据集</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="uploaded-file-items">
|
|||
|
|
<div v-for="file in pagedUploadedFiles" :key="file.uid" class="uploaded-file">
|
|||
|
|
<span class="file-icon"><i class="fa fa-cloud-download" aria-hidden="true" /></span>
|
|||
|
|
<div class="file-main">
|
|||
|
|
<strong :title="file.name">{{ file.name }}</strong>
|
|||
|
|
<span>
|
|||
|
|
{{ formatSize(file.size) }}
|
|||
|
|
<template v-if="file.count"> · {{ file.count.toLocaleString() }} 条</template>
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
<span class="file-status"><i class="fa fa-check-circle" aria-hidden="true" /> 拉取成功</span>
|
|||
|
|
<el-button
|
|||
|
|
link
|
|||
|
|
type="danger"
|
|||
|
|
:aria-label="`删除数据集 ${file.name}`"
|
|||
|
|
@click="emit('remove-file', file.uid)"
|
|||
|
|
>
|
|||
|
|
删除
|
|||
|
|
</el-button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<el-pagination
|
|||
|
|
v-if="uploadedFiles.length > FILE_PAGE_SIZE"
|
|||
|
|
v-model:current-page="currentFilePage"
|
|||
|
|
:page-size="FILE_PAGE_SIZE"
|
|||
|
|
:total="uploadedFiles.length"
|
|||
|
|
:pager-count="5"
|
|||
|
|
small
|
|||
|
|
background
|
|||
|
|
layout="prev, pager, next"
|
|||
|
|
class="uploaded-file-pagination"
|
|||
|
|
aria-label="已拉取数据分页"
|
|||
|
|
/>
|
|||
|
|
</section>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div v-else class="form-section upload-section">
|
|||
|
|
<div class="section-title-row">
|
|||
|
|
<div>
|
|||
|
|
<h3 id="source-upload-title">源数据上传</h3>
|
|||
|
|
<p>上传后可在下一步检查内容和切分效果,支持同时添加多个文件</p>
|
|||
|
|
</div>
|
|||
|
|
<el-button v-if="uploadedFiles.length === 0" link type="primary" @click="emit('use-sample')">
|
|||
|
|
使用示例数据
|
|||
|
|
</el-button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<el-upload
|
|||
|
|
v-if="uploadedFiles.length === 0"
|
|||
|
|
drag
|
|||
|
|
multiple
|
|||
|
|
:accept="uploadAccept"
|
|||
|
|
:auto-upload="false"
|
|||
|
|
:show-file-list="false"
|
|||
|
|
:on-change="(file: UploadFile) => emit('file-change', file)"
|
|||
|
|
aria-label="选择或拖拽源数据文件"
|
|||
|
|
>
|
|||
|
|
<i class="fa fa-cloud-upload upload-icon" aria-hidden="true" />
|
|||
|
|
<div class="el-upload__text">拖拽文件到此处,或<em>点击选择文件</em></div>
|
|||
|
|
<template #tip>
|
|||
|
|
<div class="el-upload__tip">
|
|||
|
|
{{ processType === 'unstructured'
|
|||
|
|
? '支持 TXT、Markdown、PDF、Word、JSON、JSONL,单文件不超过 200MB'
|
|||
|
|
: '支持 JSON、JSONL、CSV、Excel,单文件不超过 200MB' }}
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
</el-upload>
|
|||
|
|
|
|||
|
|
<section v-else class="uploaded-file-list" aria-label="已上传文件列表">
|
|||
|
|
<div class="uploaded-file-list-header">
|
|||
|
|
<span>已添加 {{ uploadedFiles.length }} 个文件</span>
|
|||
|
|
<div class="continue-upload">
|
|||
|
|
<el-upload
|
|||
|
|
multiple
|
|||
|
|
:accept="uploadAccept"
|
|||
|
|
:auto-upload="false"
|
|||
|
|
:show-file-list="false"
|
|||
|
|
:on-change="(file: UploadFile) => emit('file-change', file)"
|
|||
|
|
aria-label="继续添加源数据文件"
|
|||
|
|
>
|
|||
|
|
<el-button size="small" type="primary">继续上传</el-button>
|
|||
|
|
</el-upload>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="uploaded-file-items">
|
|||
|
|
<div v-for="file in pagedUploadedFiles" :key="file.uid" class="uploaded-file">
|
|||
|
|
<span class="file-icon"><i class="fa fa-file-text-o" aria-hidden="true" /></span>
|
|||
|
|
<div class="file-main">
|
|||
|
|
<strong :title="file.name">{{ file.name }}</strong>
|
|||
|
|
<span>
|
|||
|
|
{{ formatSize(file.size) }}
|
|||
|
|
<template v-if="file.count"> · {{ file.count.toLocaleString() }} 条</template>
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
<span class="file-status"><i class="fa fa-check-circle" aria-hidden="true" /> 校验通过</span>
|
|||
|
|
<el-button
|
|||
|
|
link
|
|||
|
|
type="danger"
|
|||
|
|
:aria-label="`删除文件 ${file.name}`"
|
|||
|
|
@click="emit('remove-file', file.uid)"
|
|||
|
|
>
|
|||
|
|
删除
|
|||
|
|
</el-button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<el-pagination
|
|||
|
|
v-if="uploadedFiles.length > FILE_PAGE_SIZE"
|
|||
|
|
v-model:current-page="currentFilePage"
|
|||
|
|
:page-size="FILE_PAGE_SIZE"
|
|||
|
|
:total="uploadedFiles.length"
|
|||
|
|
:pager-count="5"
|
|||
|
|
small
|
|||
|
|
background
|
|||
|
|
layout="prev, pager, next"
|
|||
|
|
class="uploaded-file-pagination"
|
|||
|
|
aria-label="已上传文件分页"
|
|||
|
|
/>
|
|||
|
|
</section>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<style scoped lang="scss">
|
|||
|
|
.source-upload-step {
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-section {
|
|||
|
|
padding: 0;
|
|||
|
|
|
|||
|
|
h3 {
|
|||
|
|
margin: 0 0 5px;
|
|||
|
|
color: #2f3747;
|
|||
|
|
font-size: 15px;
|
|||
|
|
font-weight: 650;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.section-title-row {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: flex-start;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
gap: 16px;
|
|||
|
|
|
|||
|
|
p {
|
|||
|
|
margin: 0;
|
|||
|
|
color: #8a93a3;
|
|||
|
|
font-size: 12px;
|
|||
|
|
line-height: 1.6;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.external-section {
|
|||
|
|
.external-grid {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|||
|
|
gap: 18px 20px;
|
|||
|
|
margin-top: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(.el-form-item) {
|
|||
|
|
margin-bottom: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(.el-select),
|
|||
|
|
:deep(.el-input),
|
|||
|
|
:deep(.el-input-number) {
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.external-actions {
|
|||
|
|
display: flex;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 12px;
|
|||
|
|
margin-top: 22px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.external-status {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 6px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
|
|||
|
|
&.is-connected {
|
|||
|
|
color: #2ca66a;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-section :deep(.el-upload) {
|
|||
|
|
width: 100%;
|
|||
|
|
margin-top: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-section :deep(.el-upload-dragger) {
|
|||
|
|
width: 100%;
|
|||
|
|
min-height: 154px;
|
|||
|
|
padding: 32px 20px;
|
|||
|
|
background: #fbfcfe;
|
|||
|
|
border-color: #dfe3ea;
|
|||
|
|
transition: border-color 0.18s ease, background-color 0.18s ease;
|
|||
|
|
|
|||
|
|
&:hover,
|
|||
|
|
&:focus-visible {
|
|||
|
|
background: #fafaff;
|
|||
|
|
border-color: #8b82f4;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&:focus-visible {
|
|||
|
|
outline: 2px solid #5b50f2;
|
|||
|
|
outline-offset: 2px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-icon {
|
|||
|
|
margin-bottom: 12px;
|
|||
|
|
color: #5b50f2;
|
|||
|
|
font-size: 30px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.uploaded-file-list {
|
|||
|
|
margin-top: 20px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
background: #fff;
|
|||
|
|
border: 1px solid #dfe3ea;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.uploaded-file-list-header {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
gap: 12px;
|
|||
|
|
padding: 10px 14px;
|
|||
|
|
color: #5f6878;
|
|||
|
|
font-size: 12px;
|
|||
|
|
background: #fbfcfe;
|
|||
|
|
border-bottom: 1px solid #edf0f5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.continue-upload {
|
|||
|
|
flex: 0 0 auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.continue-upload :deep(.el-upload) {
|
|||
|
|
width: auto;
|
|||
|
|
margin-top: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.uploaded-file-pagination {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: flex-end;
|
|||
|
|
padding: 10px 14px;
|
|||
|
|
border-top: 1px solid #edf0f5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.uploaded-file {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 10px;
|
|||
|
|
min-height: 48px;
|
|||
|
|
padding: 8px 14px;
|
|||
|
|
border-bottom: 1px solid #edf0f5;
|
|||
|
|
|
|||
|
|
&:last-child {
|
|||
|
|
border-bottom: 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.file-icon {
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
flex: 0 0 auto;
|
|||
|
|
width: 28px;
|
|||
|
|
height: 28px;
|
|||
|
|
color: #5b50f2;
|
|||
|
|
font-size: 14px;
|
|||
|
|
background: #f0efff;
|
|||
|
|
border-radius: 7px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.file-main {
|
|||
|
|
display: flex;
|
|||
|
|
flex: 1;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 5px;
|
|||
|
|
min-width: 0;
|
|||
|
|
|
|||
|
|
strong {
|
|||
|
|
overflow: hidden;
|
|||
|
|
color: #273142;
|
|||
|
|
font-size: 14px;
|
|||
|
|
text-overflow: ellipsis;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
span {
|
|||
|
|
color: #8a93a3;
|
|||
|
|
font-size: 12px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.file-status {
|
|||
|
|
color: #2ca66a;
|
|||
|
|
font-size: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.uploaded-file :deep(.el-button) {
|
|||
|
|
flex: 0 0 auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@media (max-width: 900px) {
|
|||
|
|
.external-section .external-grid {
|
|||
|
|
grid-template-columns: minmax(0, 1fr);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.uploaded-file {
|
|||
|
|
gap: 8px;
|
|||
|
|
padding: 8px 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.file-status {
|
|||
|
|
flex: 0 1 auto;
|
|||
|
|
line-height: 1.4;
|
|||
|
|
white-space: normal;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.uploaded-file-pagination {
|
|||
|
|
justify-content: center;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@media (max-width: 560px) {
|
|||
|
|
.section-title-row {
|
|||
|
|
align-items: flex-start;
|
|||
|
|
flex-direction: column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.uploaded-file-list-header {
|
|||
|
|
align-items: flex-start;
|
|||
|
|
flex-direction: column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.uploaded-file {
|
|||
|
|
align-items: flex-start;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.file-main {
|
|||
|
|
min-width: calc(100% - 40px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.file-status {
|
|||
|
|
margin-left: 38px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@media (prefers-reduced-motion: reduce) {
|
|||
|
|
.upload-section :deep(.el-upload-dragger) {
|
|||
|
|
transition: none;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|