style(demo): 优化文件上传演示界面布局与交互

- 卡片化展示单图、多图、文件三类上传组件,结构更清晰
- 右侧实时预览各组件 v-model 绑定值,便于理解用法
- 窄屏自适应,演示说明文案精简
This commit is contained in:
Ray.Hao
2026-08-26 08:17:13 +08:00
parent 384696affc
commit 17ca4c1b7b

View File

@@ -1,34 +1,123 @@
<!-- 文件上传组件示例 -->
<template>
<div class="page-container">
<el-link
href="https://gitee.com/youlaiorg/vue3-element-admin/blob/master/src/views/demo/upload.vue"
type="primary"
target="_blank"
class="mb-10"
>
示例源码 请点击>>>
</el-link>
<div class="page-container upload-demo">
<!-- 页头 -->
<header class="page-header">
<div class="page-header__info">
<h2 class="page-header__title">文件上传</h2>
<p class="page-header__desc">
SingleImageUpload、MultiImageUpload、FileUpload 通用上传组件演示,右侧实时预览 v-model
绑定值
</p>
</div>
<el-button
class="page-header__link"
tag="a"
href="https://gitee.com/youlaiorg/vue3-element-admin/blob/master/src/views/demo/upload.vue"
target="_blank"
type="primary"
plain
round
>
<template #icon>
<el-icon><Link /></el-icon>
</template>
示例源码
</el-button>
</header>
<el-form>
<el-form-item label="单图上传">
<SingleImageUpload v-model="picUrl" />
</el-form-item>
<!-- 单图上传 -->
<section class="demo-card">
<header class="demo-card__header">
<span class="demo-card__icon demo-card__icon--primary">
<el-icon :size="18"><Picture /></el-icon>
</span>
<div class="demo-card__meta">
<h3 class="demo-card__title">单图上传</h3>
<p class="demo-card__desc">
头像、封面等仅需一张图片的场景,自带格式与大小校验,支持预览、删除
</p>
</div>
<el-tag effect="plain" size="small">SingleImageUpload</el-tag>
</header>
<el-form-item label="多图上传">
<MultiImageUpload v-model="picUrls" :limit="2" />
</el-form-item>
<div class="demo-card__body">
<div class="demo-card__stage">
<SingleImageUpload v-model="picUrl" />
</div>
<aside class="value-panel">
<div class="value-panel__caption">
<span class="value-panel__label">v-model</span>
<code class="value-panel__type">string</code>
</div>
<pre class="value-panel__code" :class="{ 'is-empty': !picUrl }">{{ picUrlJson }}</pre>
</aside>
</div>
</section>
<el-form-item label="文件上传">
<FileUpload v-model="fileUrls" />
</el-form-item>
</el-form>
<!-- 多图上传 -->
<section class="demo-card">
<header class="demo-card__header">
<span class="demo-card__icon demo-card__icon--success">
<el-icon :size="18"><Pictures /></el-icon>
</span>
<div class="demo-card__meta">
<h3 class="demo-card__title">多图上传</h3>
<p class="demo-card__desc">
相册、轮播等需要多张图片的场景,数量上限 limit 可配(示例 limit=2),支持预览、删除
</p>
</div>
<el-tag effect="plain" size="small" type="success">MultiImageUpload</el-tag>
</header>
<div class="demo-card__body">
<div class="demo-card__stage">
<MultiImageUpload v-model="picUrls" :limit="2" />
</div>
<aside class="value-panel">
<div class="value-panel__caption">
<span class="value-panel__label">v-model</span>
<code class="value-panel__type">string[]</code>
</div>
<pre class="value-panel__code" :class="{ 'is-empty': picUrls.length === 0 }">{{
picUrlsJson
}}</pre>
</aside>
</div>
</section>
<!-- 文件上传 -->
<section class="demo-card">
<header class="demo-card__header">
<span class="demo-card__icon demo-card__icon--warning">
<el-icon :size="18"><Files /></el-icon>
</span>
<div class="demo-card__meta">
<h3 class="demo-card__title">文件上传</h3>
<p class="demo-card__desc">任意类型文件多选上传,实时展示上传进度,支持下载、删除</p>
</div>
<el-tag effect="plain" size="small" type="warning">FileUpload</el-tag>
</header>
<div class="demo-card__body">
<div class="demo-card__stage">
<FileUpload v-model="fileUrls" />
</div>
<aside class="value-panel">
<div class="value-panel__caption">
<span class="value-panel__label">v-model</span>
<code class="value-panel__type">FileInfo[]</code>
</div>
<pre class="value-panel__code" :class="{ 'is-empty': fileUrls.length === 0 }">{{
fileUrlsJson
}}</pre>
</aside>
</div>
</section>
</div>
</template>
<script setup lang="ts">
import MultiImageUpload from "@/components/Upload/MultiImageUpload.vue";
// 单图
const picUrl = ref("https://s2.loli.net/2023/05/24/yNsxFC8rLHMZQcK.jpg");
const picUrls = ref(["https://s2.loli.net/2023/05/24/yNsxFC8rLHMZQcK.jpg"]);
@@ -37,4 +126,199 @@ const fileUrls = ref([
{ name: "照片1.jpg", url: "https://s2.loli.net/2023/05/24/yNsxFC8rLHMZQcK.jpg" },
{ name: "照片2.jpg", url: "https://s2.loli.net/2023/05/24/RuHFMwW4rG5lIqs.jpg" },
]);
// 绑定值 JSON 预览
const picUrlJson = computed(() => (picUrl.value ? JSON.stringify(picUrl.value) : "// 暂无数据"));
const picUrlsJson = computed(() =>
picUrls.value.length ? JSON.stringify(picUrls.value, null, 2) : "// 暂无数据"
);
const fileUrlsJson = computed(() =>
fileUrls.value.length ? JSON.stringify(fileUrls.value, null, 2) : "// 暂无数据"
);
</script>
<style scoped lang="scss">
$code-font: ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, monospace;
.upload-demo {
overflow: auto;
}
.page-header {
display: flex;
flex-shrink: 0;
gap: 16px;
align-items: center;
justify-content: space-between;
padding: 4px 4px 0;
&__info {
min-width: 0;
}
&__title {
margin: 0;
font-size: 20px;
font-weight: 600;
line-height: 28px;
color: var(--el-text-color-primary);
}
&__desc {
margin: 4px 0 0;
font-size: 13px;
line-height: 20px;
color: var(--el-text-color-secondary);
}
&__link {
flex-shrink: 0;
}
}
.demo-card {
flex-shrink: 0;
padding: 20px 24px;
background: var(--content-bg);
border: 1px solid var(--card-border);
border-radius: var(--card-radius);
box-shadow: var(--card-shadow);
transition:
border-color 0.2s,
box-shadow 0.2s;
&:hover {
border-color: var(--card-border-hover);
box-shadow: var(--card-shadow-hover);
}
&__header {
display: flex;
gap: 14px;
align-items: center;
margin-bottom: 18px;
}
&__icon {
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 10px;
&--primary {
color: var(--el-color-primary);
background: var(--el-color-primary-light-9);
}
&--success {
color: var(--el-color-success);
background: var(--el-color-success-light-9);
}
&--warning {
color: var(--el-color-warning);
background: var(--el-color-warning-light-9);
}
}
&__meta {
flex: 1;
min-width: 0;
}
&__title {
margin: 0;
font-size: 15px;
font-weight: 600;
line-height: 22px;
color: var(--el-text-color-primary);
}
&__desc {
margin: 2px 0 0;
font-size: 12px;
line-height: 18px;
color: var(--el-text-color-secondary);
}
&__body {
display: flex;
gap: 24px;
align-items: stretch;
}
&__stage {
flex-shrink: 0;
min-width: 0;
}
}
.value-panel {
display: flex;
flex: 1 1 0;
flex-direction: column;
gap: 8px;
min-width: 0;
padding-left: 24px;
border-left: 1px dashed var(--card-border);
&__caption {
display: flex;
gap: 8px;
align-items: center;
}
&__label {
font-size: 12px;
font-weight: 600;
color: var(--el-text-color-regular);
}
&__type {
padding: 1px 8px;
font-family: $code-font;
font-size: 11px;
color: var(--el-color-primary);
background: var(--el-color-primary-light-9);
border-radius: 4px;
}
&__code {
flex: 1;
max-height: 180px;
padding: 12px 14px;
margin: 0;
overflow: auto;
font-family: $code-font;
font-size: 12px;
line-height: 1.7;
color: var(--el-text-color-regular);
word-break: break-all;
white-space: pre-wrap;
background: var(--el-fill-color-light);
border-radius: 6px;
&.is-empty {
font-style: italic;
color: var(--el-text-color-placeholder);
}
}
}
// 窄屏时上下排列
@media (max-width: 992px) {
.demo-card__body {
flex-direction: column;
}
.value-panel {
padding-top: 16px;
padding-left: 0;
border-top: 1px dashed var(--card-border);
border-left: none;
}
}
</style>