20260820223837
This commit is contained in:
1 parent
68ac5db038
commit
45854b0570
48 files changed
+7545
-650
No files matched your search
@@ -0,0 +1,346 @@
|
||||
<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: 1px solid var(--fms-border);
|
||||
border-radius: var(--fms-control-radius);
|
||||
background: var(--fms-card);
|
||||
|
||||
&--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>
|
||||
Reference in new issue
Block a user