347 lines
8.8 KiB
Vue
347 lines
8.8 KiB
Vue
<script setup>
|
||
import { ref } from 'vue'
|
||
import { RotateCcw, FileUp, ExternalLink, Inbox } from '@lucide/vue'
|
||
import Button from '@/components/ui/button/button.vue'
|
||
import Input from '@/components/ui/input/input.vue'
|
||
import Checkbox from '@/components/ui/checkbox/checkbox.vue'
|
||
import FmsFile from '@/components/fms-file/FmsFile.vue'
|
||
import FmsFileModal from '@/components/fms-file/FmsFileModal.vue'
|
||
import { useFileModal } from '@/components/fms-file/useFileModal'
|
||
|
||
defineOptions({ name: 'FileDemo' })
|
||
|
||
/* ---------- 参数配置 ---------- */
|
||
const moduleCode = ref('b_port')
|
||
const father = ref('1001')
|
||
const showTree = ref(true)
|
||
|
||
/* 内嵌示例:key 变化强制重挂载(参数修改后点「刷新」生效) */
|
||
const embeddedKey = ref(0)
|
||
function reloadEmbedded() {
|
||
embeddedKey.value++
|
||
}
|
||
|
||
/* ---------- 弹窗示例 ---------- */
|
||
// 声明式
|
||
const modalOpen = ref(false)
|
||
// 命令式
|
||
const fileModal = useFileModal()
|
||
function openImperative() {
|
||
fileModal.open({
|
||
moduleCode: moduleCode.value,
|
||
father: father.value,
|
||
showTree: showTree.value,
|
||
onUploaded: () => log('uploaded', '命令式弹窗:文件已上传'),
|
||
onDeleted: () => log('deleted', '命令式弹窗:文件已删除'),
|
||
onSelected: (f) => log('selected', `命令式弹窗选中:${f.mx_filename}`),
|
||
})
|
||
}
|
||
|
||
/* ---------- 事件日志 ---------- */
|
||
const events = ref([])
|
||
function log(type, payload) {
|
||
events.value.unshift({ type, payload, time: new Date().toLocaleTimeString() })
|
||
if (events.value.length > 20) events.value.pop()
|
||
}
|
||
function clearEvents() {
|
||
events.value = []
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="file-demo">
|
||
<header class="file-demo__head">
|
||
<h1 class="file-demo__title">FmsFile 附件管理组件演示</h1>
|
||
<p class="file-demo__sub">
|
||
支持上传(本地 / OSS 自动匹配)、拖拽 / 粘贴、分类筛选、网格 / 列表视图、预览与删除。下方参数可切换后点「刷新」重新加载。
|
||
</p>
|
||
</header>
|
||
|
||
<div class="file-demo__grid">
|
||
<!-- 左列:参数 + 弹窗 + 事件 -->
|
||
<div class="file-demo__side">
|
||
<!-- 参数配置 -->
|
||
<section class="file-demo__card">
|
||
<h2 class="file-demo__card-title">参数配置</h2>
|
||
<div class="file-demo__field">
|
||
<label class="file-demo__label">模块编码 moduleCode</label>
|
||
<Input v-model="moduleCode" placeholder="如 b_port" />
|
||
</div>
|
||
<div class="file-demo__field">
|
||
<label class="file-demo__label">业务 ID father</label>
|
||
<Input v-model="father" placeholder="如 1001" />
|
||
</div>
|
||
<div class="file-demo__row">
|
||
<Checkbox v-model="showTree">显示分类树</Checkbox>
|
||
<Button type="outline" @click="reloadEmbedded">
|
||
<RotateCcw />
|
||
刷新内嵌
|
||
</Button>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 弹窗用法 -->
|
||
<section class="file-demo__card">
|
||
<h2 class="file-demo__card-title">弹窗用法</h2>
|
||
<div class="file-demo__btns">
|
||
<Button type="primary" @click="modalOpen = true">
|
||
<FileUp />
|
||
声明式弹窗
|
||
</Button>
|
||
<Button type="outline" @click="openImperative">
|
||
<ExternalLink />
|
||
命令式弹窗
|
||
</Button>
|
||
</div>
|
||
<p class="file-demo__tip">
|
||
命令式:<code>useFileModal().open({ moduleCode, father })</code>,支持
|
||
onUploaded / onDeleted / onSelected 回调。
|
||
</p>
|
||
</section>
|
||
|
||
<!-- 事件日志 -->
|
||
<section class="file-demo__card file-demo__card--grow">
|
||
<div class="file-demo__card-head">
|
||
<h2 class="file-demo__card-title">事件日志</h2>
|
||
<Button type="ghost" @click="clearEvents">清空</Button>
|
||
</div>
|
||
<ul v-if="events.length" class="file-demo__events">
|
||
<li v-for="(ev, i) in events" :key="i" class="file-demo__event">
|
||
<span class="file-demo__event-time">{{ ev.time }}</span>
|
||
<span class="file-demo__event-tag" :class="`is-${ev.type}`">{{ ev.type }}</span>
|
||
<span class="file-demo__event-text">{{ ev.payload }}</span>
|
||
</li>
|
||
</ul>
|
||
<div v-else class="file-demo__empty">
|
||
<Inbox :size="28" :stroke-width="1" />
|
||
<span>暂无事件,去上传 / 删除 / 选中文件试试</span>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
|
||
<!-- 右列:内嵌用法 -->
|
||
<section class="file-demo__card file-demo__main">
|
||
<div class="file-demo__card-head">
|
||
<h2 class="file-demo__card-title">内嵌用法</h2>
|
||
<span class="file-demo__badge">moduleCode={{ moduleCode }} · father={{ father }}</span>
|
||
</div>
|
||
<div class="file-demo__embedded">
|
||
<FmsFile
|
||
:key="embeddedKey"
|
||
:module-code="moduleCode"
|
||
:father="father"
|
||
:show-tree="showTree"
|
||
@uploaded="log('uploaded', '内嵌:文件已上传')"
|
||
@deleted="log('deleted', '内嵌:文件已删除')"
|
||
@selected="(f) => log('selected', `内嵌选中:${f.mx_filename}`)"
|
||
/>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
|
||
<!-- 弹窗(声明式) -->
|
||
<FmsFileModal
|
||
v-model:open="modalOpen"
|
||
:module-code="moduleCode"
|
||
:father="father"
|
||
:show-tree="showTree"
|
||
@uploaded="log('uploaded', '声明式弹窗:文件已上传')"
|
||
@deleted="log('deleted', '声明式弹窗:文件已删除')"
|
||
@selected="(f) => log('selected', `声明式弹窗选中:${f.mx_filename}`)"
|
||
/>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped lang="scss">
|
||
.file-demo {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
color: var(--fms-text);
|
||
}
|
||
|
||
.file-demo__head {
|
||
.file-demo__title {
|
||
margin: 0 0 4px;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.file-demo__sub {
|
||
margin: 0;
|
||
font-size: 13px;
|
||
line-height: 20px;
|
||
color: var(--fms-text-secondary);
|
||
}
|
||
}
|
||
|
||
.file-demo__grid {
|
||
display: grid;
|
||
grid-template-columns: 320px minmax(0, 1fr);
|
||
gap: 12px;
|
||
align-items: start;
|
||
}
|
||
|
||
.file-demo__side {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.file-demo__card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
padding: 12px;
|
||
border-radius: var(--fms-control-radius);
|
||
background: var(--fms-card);
|
||
box-shadow: var(--fms-shadow);
|
||
|
||
&--grow {
|
||
flex: 1;
|
||
min-height: 180px;
|
||
}
|
||
}
|
||
|
||
.file-demo__card-head {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 8px;
|
||
}
|
||
|
||
.file-demo__card-title {
|
||
margin: 0;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.file-demo__field {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.file-demo__label {
|
||
font-size: 12px;
|
||
color: var(--fms-text-secondary);
|
||
}
|
||
|
||
.file-demo__row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 8px;
|
||
}
|
||
|
||
.file-demo__btns {
|
||
display: flex;
|
||
gap: 8px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.file-demo__tip {
|
||
margin: 0;
|
||
font-size: 12px;
|
||
line-height: 18px;
|
||
color: var(--fms-text-secondary);
|
||
|
||
code {
|
||
padding: 1px 5px;
|
||
border-radius: 4px;
|
||
background: var(--fms-secondary-hover);
|
||
font-size: 11px;
|
||
}
|
||
}
|
||
|
||
.file-demo__events {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
margin: 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
overflow: auto;
|
||
max-height: 320px;
|
||
}
|
||
|
||
.file-demo__event {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 6px;
|
||
padding: 4px 6px;
|
||
border-radius: 4px;
|
||
background: var(--fms-secondary-hover);
|
||
font-size: 12px;
|
||
line-height: 16px;
|
||
|
||
&-time {
|
||
flex: none;
|
||
font-variant-numeric: tabular-nums;
|
||
color: var(--fms-text-secondary);
|
||
font-size: 11px;
|
||
}
|
||
|
||
&-tag {
|
||
flex: none;
|
||
padding: 0 4px;
|
||
border-radius: 3px;
|
||
font-size: 10px;
|
||
font-weight: 600;
|
||
color: var(--fms-primary);
|
||
background: color-mix(in srgb, var(--fms-primary) 12%, transparent);
|
||
|
||
&.is-uploaded {
|
||
color: var(--fms-success);
|
||
background: color-mix(in srgb, var(--fms-success) 12%, transparent);
|
||
}
|
||
|
||
&.is-deleted {
|
||
color: var(--fms-danger);
|
||
background: color-mix(in srgb, var(--fms-danger) 12%, transparent);
|
||
}
|
||
}
|
||
|
||
&-text {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
}
|
||
|
||
.file-demo__empty {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
flex: 1;
|
||
min-height: 120px;
|
||
font-size: 12px;
|
||
color: var(--fms-text-secondary);
|
||
}
|
||
|
||
/* 内嵌主区 */
|
||
.file-demo__main {
|
||
min-width: 0;
|
||
}
|
||
|
||
.file-demo__badge {
|
||
padding: 2px 8px;
|
||
border-radius: 999px;
|
||
background: var(--fms-secondary-hover);
|
||
color: var(--fms-text-secondary);
|
||
font-size: 11px;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.file-demo__embedded {
|
||
height: 640px;
|
||
min-height: 0;
|
||
border: 1px solid var(--fms-border);
|
||
border-radius: var(--fms-control-radius);
|
||
overflow: hidden;
|
||
}
|
||
</style>
|