20260827173205

This commit is contained in:
oneao committed 2026-08-27 17:32:05 +08:00
1 parent 60cacfa8fb
commit 3824af0f12
38 files changed
+2398 -3552

No files matched your search

+215 -121
View File
@@ -1,20 +1,20 @@
<script setup>
import { computed, ref } from 'vue'
import { LayoutDashboard } from '@lucide/vue'
import { useRouter } from 'vue-router'
import Button from '@/components/ui/button/button.vue'
import Input from '@/components/ui/input/input.vue'
import { ref } from 'vue'
import {
ChevronDown,
Download,
Plus,
RefreshCw,
Search,
SlidersHorizontal,
Trash2,
Upload,
} from '@lucide/vue'
import { Button, Dropdown, Input, Pagination, RangePicker, Select } from '@/components/ui'
import FmsTable from '@/components/fms-table/FmsTable.vue'
defineOptions({ name: 'Dashboard' })
const router = useRouter()
// 跳转到详情页(不同 id = 不同路由缓存),用于验证「多参数独立缓存」
function goDetail(id) {
router.push(`/detail/${id}`)
}
/* ---------- fms-table 大数据量 + 单行编辑 demo ---------- */
const TYPE_OPTIONS = [
{ value: 'task', label: '任务' },
@@ -57,9 +57,16 @@ function makeColumns() {
editor: { type: 'input' },
})
}
// 纯展示列(无 editor、无 customCell),走库原生文本渲染:
// money 千分位 / datetime 日期时间 / date 日期 / percent 百分比
columns.push(
{ title: '金额', dataIndex: 'amount', width: 120 },
{ title: '创建时间', dataIndex: 'createdAt', width: 170 },
{ title: '到期日', dataIndex: 'expireDate', width: 110 },
{ title: '完成率', dataIndex: 'progress', width: 90 },
)
return columns
}
function makeRows(count) {
const rows = []
for (let i = 0; i < count; i++) {
@@ -70,6 +77,10 @@ function makeRows(count) {
status: STATUS_OPTIONS[i % STATUS_OPTIONS.length].value,
enabled: i % 2,
score: Math.round(Math.random() * 1000) / 10,
amount: Math.round(Math.random() * 10000000) / 100,
createdAt: new Date(Date.now() - (count - i) * 3600_000).toISOString(),
expireDate: new Date(Date.now() + ((i % 365) + 1) * 86400_000).toISOString(),
progress: Math.round(Math.random() * 100) / 100,
}
for (let j = 1; j <= EXTRA_FIELD_COUNT; j++) row[`f${j}`] = `${i + 1}-${j}`
rows.push(row)
@@ -77,68 +88,117 @@ function makeRows(count) {
return rows
}
const DEFAULT_ROW_COUNT = 5000
// 行级特殊样式:根据业务字段返回行 class(经 $attrs 透传给 stk-table 的 rowClassName)。
// 返回的 class 在下方 <style scoped> 中通过 :deep() 定义字体/背景颜色。
function rowClassName(row) {
if (row.type === 'alert') return 'fms-row-alert'
if (row.status === 'pending') return 'fms-row-pending'
if (row.status === 'disabled') return 'fms-row-disabled'
return ''
}
/* ---------- 表格数据:默认 1w 行(虚拟滚动) ---------- */
const DEFAULT_ROW_COUNT = 10000
const columns = ref(makeColumns())
const rowCount = ref(DEFAULT_ROW_COUNT)
const dataSource = ref(makeRows(DEFAULT_ROW_COUNT))
const editCount = ref(0)
// 修改行数后重新生成全新数据(行对象全部重建,fms-table 编辑状态随之自然重置)
function regenerate() {
const count = Math.max(
1,
Math.min(100000, Math.floor(Number(rowCount.value)) || DEFAULT_ROW_COUNT),
)
rowCount.value = count
dataSource.value = makeRows(count)
editCount.value = 0
/* ---------- 工具栏 UI(仅界面骨架,搜索/查询逻辑后续接入) ---------- */
const keyword = ref('')
const advancedOpen = ref(false)
const advancedForm = ref({
name: '',
type: undefined,
status: undefined,
range: null,
})
const moreItems = [
{ key: 'export', label: '导出数据', icon: Download },
{ key: 'import', label: '导入数据', icon: Upload },
{ key: 'refresh', label: '刷新', icon: RefreshCw },
{ key: 'divider-1', divider: true },
{ key: 'batch-delete', label: '批量删除', icon: Trash2, danger: true },
]
// TODO: 接入真实搜索 / 高级搜索 / 更多操作 / 新增逻辑
function onMoreAction() {}
function onAdvancedSearch() {}
function onAdvancedReset() {
advancedForm.value = { name: '', type: undefined, status: undefined, range: null }
}
// fms-table 编辑提交时回传 (row, col, newValue),这里统计编辑次数
function onCellChange() {
editCount.value++
}
const totalCells = computed(() => columns.value.length * dataSource.value.length)
</script>
<template>
<div class="dashboard-page">
<header class="dashboard-header">
<div class="dashboard-heading">
<div class="dashboard-icon" aria-hidden="true">
<LayoutDashboard />
</div>
<div>
<h1 class="dashboard-title">工作台 · fms-table Demo</h1>
<p class="dashboard-desc">
20 列单行编辑示例:单击单元格进入编辑,Enter 提交,Esc 取消,方向键在单元格间移动。默认
5000 行(虚拟滚动),可修改行数后「重新生成」。
</p>
</div>
</div>
<div class="dashboard-actions">
<Button type="primary" @click="goDetail(1)">打开详情 #1</Button>
<Button type="primary" @click="goDetail(2)">打开详情 #2</Button>
</div>
</header>
<section class="demo-card">
<!-- 工具栏:搜索 + 高级搜索 | 更多操作 + 新增 -->
<div class="demo-toolbar">
<label class="toolbar-field">
<span class="toolbar-label">行数</span>
<Input v-model="rowCount" type="number" class="toolbar-input" />
</label>
<Button type="primary" @click="regenerate">重新生成</Button>
<span class="toolbar-meta">
{{ dataSource.length.toLocaleString() }} 行 × {{ columns.length }} 列(内置序号列不计入),
共 {{ totalCells.toLocaleString() }} 个单元格 · 已编辑 {{ editCount }} 次
</span>
<div class="toolbar-left">
<Input
v-model="keyword"
class="toolbar-search"
placeholder="搜索名称 / ID"
allow-clear
>
<template #prefix><Search :size="14" aria-hidden="true" /></template>
</Input>
<Button
type="outline"
:class="{ 'toolbar-toggle-active': advancedOpen }"
@click="advancedOpen = !advancedOpen"
>
<SlidersHorizontal :size="14" aria-hidden="true" />
高级搜索
</Button>
</div>
<div class="toolbar-right">
<Dropdown :items="moreItems" @click="onMoreAction">
<template #trigger>
<Button type="outline">
更多操作
<ChevronDown :size="14" aria-hidden="true" />
</Button>
</template>
</Dropdown>
<Button type="primary">
<Plus :size="14" aria-hidden="true" />
新增
</Button>
</div>
</div>
<!-- 高级搜索面板:label 在上、控件等宽网格(适配多语言标签长度),仅 UI 骨架 -->
<div v-show="advancedOpen" class="demo-advanced">
<div class="advanced-field">
<label class="advanced-label">名称 / ID</label>
<Input v-model="advancedForm.name" placeholder="模糊搜索" allow-clear />
</div>
<div class="advanced-field">
<label class="advanced-label">类型</label>
<Select v-model="advancedForm.type" :options="TYPE_OPTIONS" clearable />
</div>
<div class="advanced-field">
<label class="advanced-label">状态</label>
<Select v-model="advancedForm.status" :options="STATUS_OPTIONS" clearable />
</div>
<div class="advanced-field">
<label class="advanced-label">创建时间</label>
<RangePicker v-model="advancedForm.range" />
</div>
<div class="advanced-actions">
<Button type="primary" @click="onAdvancedSearch">查询</Button>
<Button type="secondary" @click="onAdvancedReset">重置</Button>
</div>
</div>
<div class="demo-table">
<FmsTable :columns="columns" :data-source="dataSource" row-key="id" @change="onCellChange" />
<FmsTable :columns="columns" :data-source="dataSource" row-key="id" />
</div>
<!-- 底部分页:仅 UI 骨架,分页逻辑后续接入 -->
<div class="demo-pagination">
<Pagination :total="dataSource.length" show-total show-size-changer />
</div>
</section>
</div>
@@ -155,56 +215,7 @@ const totalCells = computed(() => columns.value.length * dataSource.value.length
color: var(--fms-text);
}
.dashboard-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
}
.dashboard-heading {
display: flex;
align-items: center;
gap: 12px;
}
.dashboard-icon {
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
flex: none;
border-radius: 10px;
background: color-mix(in srgb, var(--fms-primary) 10%, var(--fms-card));
color: var(--fms-primary);
:deep(svg) {
width: 22px;
height: 22px;
}
}
.dashboard-title {
margin: 0;
font-size: 18px;
font-weight: 600;
}
.dashboard-desc {
margin: 2px 0 0;
font-size: 13px;
color: var(--fms-text-secondary);
}
.dashboard-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
/* ---- demo 卡片:工具栏 + 表格(灰底画布上的白卡,靠阴影分层,无边框线) ---- */
/* ---- demo 卡片:工具栏 + 高级搜索 + 表格 + 分页(灰底画布上的白卡,靠阴影分层,无边框线) ---- */
.demo-card {
display: flex;
flex-direction: column;
@@ -216,38 +227,121 @@ const totalCells = computed(() => columns.value.length * dataSource.value.length
box-shadow: var(--fms-shadow);
}
/* ---- 工具栏:左侧搜索 + 高级搜索,右侧更多操作 + 新增 ---- */
.demo-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
padding: 10px 12px;
border-bottom: 1px solid var(--fms-border);
}
.toolbar-field {
display: inline-flex;
.toolbar-left,
.toolbar-right {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.toolbar-label {
font-size: 13px;
color: var(--fms-text-secondary);
.toolbar-search {
width: 240px;
}
.toolbar-input {
width: 120px;
/* 高级搜索按钮:展开面板时高亮 */
.toolbar-toggle-active {
color: var(--fms-primary);
background: color-mix(in srgb, var(--fms-primary) 10%, var(--fms-card));
}
.toolbar-meta {
margin-left: auto;
/* ---- 高级搜索面板:label 在上、控件等宽网格。
* 多语言下标签长度不一,label 置顶 + 控件铺满网格单元即可保证各列对齐;
* 查询/重置独立一行、靠右。 ---- */
.demo-advanced {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 12px 16px;
padding: 12px;
border-bottom: 1px solid var(--fms-border);
background: color-mix(in srgb, var(--fms-background) 30%, var(--fms-card));
}
.advanced-field {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 0;
}
.advanced-label {
font-size: 12px;
color: var(--fms-text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* 控件统一铺满所在网格单元(Input / Select / RangePicker 根节点) */
.advanced-field :deep(.input-wrapper),
.advanced-field :deep(.select),
.advanced-field :deep(.fms-range-picker) {
width: 100%;
min-width: 0;
}
.advanced-actions {
grid-column: 1 / -1;
display: flex;
justify-content: flex-end;
gap: 8px;
}
.demo-table {
flex: 1;
min-height: 0;
/* 去掉表格外框:卡片本身靠阴影分层,表格直接铺满白卡(仅此处覆盖,
不影响其他页面使用 FmsTable 时保留外框) */
:deep(.fms-table) {
border: none;
}
/* 行级特殊样式:rowClassName 返回的 class 落在 stk-table 的 tr 上,
这里通过 :deep() 跨过组件边界设置字体颜色与背景色 */
:deep(.fms-row-alert) {
color: var(--fms-danger);
td {
background: color-mix(in srgb, var(--fms-danger) 8%, var(--fms-card));
}
}
:deep(.fms-row-pending) {
color: var(--fms-warning);
td {
background: color-mix(in srgb, var(--fms-warning) 8%, var(--fms-card));
}
}
:deep(.fms-row-disabled) {
color: var(--fms-disabled-text);
td {
background: var(--fms-background);
}
}
}
/* ---- 底部分页条:总数靠左,分页靠右 ---- */
.demo-pagination {
display: flex;
align-items: center;
justify-content: flex-end;
flex-wrap: wrap;
gap: 8px;
padding: 10px 12px;
}
</style>