u
This commit is contained in:
1 parent
ae3ca3cfa1
commit
7bd5f1dde9
45 files changed
+3414
-229
No files matched your search
@@ -0,0 +1,54 @@
|
||||
# 统一行尾:索引内一律 LF,检出时按下面的规则落地。
|
||||
# CRLF 只留给 Windows 批处理 —— cmd.exe 处理 LF 结尾的 .cmd/.bat 不稳(标签/goto 会出错)。
|
||||
#
|
||||
# 工作区里已经存在的文件不会被自动改写;要让索引与工作区彻底一致,执行一次:
|
||||
# git add --renormalize .
|
||||
# git checkout -- .
|
||||
# 之后新加的文件会自动按本文件规范化,不再需要人工干预。
|
||||
|
||||
* text=auto
|
||||
|
||||
# 源码与文本:一律 LF
|
||||
*.java text eol=lf
|
||||
*.kt text eol=lf
|
||||
*.ts text eol=lf
|
||||
*.tsx text eol=lf
|
||||
*.js text eol=lf
|
||||
*.mjs text eol=lf
|
||||
*.cjs text eol=lf
|
||||
*.vue text eol=lf
|
||||
*.scss text eol=lf
|
||||
*.css text eol=lf
|
||||
*.html text eol=lf
|
||||
*.json text eol=lf
|
||||
*.yaml text eol=lf
|
||||
*.yml text eol=lf
|
||||
*.xml text eol=lf
|
||||
*.md text eol=lf
|
||||
*.properties text eol=lf
|
||||
*.sql text eol=lf
|
||||
.gitignore text eol=lf
|
||||
.gitattributes text eol=lf
|
||||
|
||||
# 脚本:Unix 侧必须 LF(mvnw 是 shell 脚本,CRLF 会让它跑不起来)
|
||||
*.sh text eol=lf
|
||||
mvnw text eol=lf
|
||||
|
||||
# Windows 批处理:必须 CRLF
|
||||
*.cmd text eol=crlf
|
||||
*.bat text eol=crlf
|
||||
|
||||
# 二进制:不做行尾转换、不参与 diff
|
||||
*.xdb binary
|
||||
*.jar binary
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
*.gif binary
|
||||
*.ico binary
|
||||
*.woff binary
|
||||
*.woff2 binary
|
||||
*.ttf binary
|
||||
|
||||
# 锁文件:内容由 pnpm 生成,不展示 diff
|
||||
pnpm-lock.yaml -diff
|
||||
@@ -12,6 +12,11 @@ target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
.mvn/timing.properties
|
||||
|
||||
# ---- 后端本地敏感配置与本地库(server/)----
|
||||
server/config/dbconfigs/*
|
||||
!server/config/dbconfigs/README.md
|
||||
server/src/main/resources/ip2region.xdb
|
||||
|
||||
# ---- 日志与运行时产物 ----
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
G3Soft ERP 前后端同仓。业务域:海运 / 空运 / 箱管 / 财务 / CRM。
|
||||
|
||||
> **当前状态:前端骨架已搭好并可运行;后端尚未开始。**
|
||||
> 元数据驱动引擎(通用查询 / 保存 / 权限)**未开始设计**,属后续独立立项。
|
||||
> **当前状态:前端骨架已搭好并可运行;后端已从原型 `fms-api` 迁入元数据驱动引擎实现**(见 `server/README.md`)。
|
||||
> 元数据驱动引擎(通用查询 / 保存 / 权限)**尚未按业务域重构**,属后续独立立项。
|
||||
|
||||
## 目录结构
|
||||
|
||||
@@ -18,7 +18,7 @@ g3soft-erp/
|
||||
│ ├── .oxlintrc.json
|
||||
│ ├── .oxfmtrc.json
|
||||
│ └── src/
|
||||
├── server/ # 后端占位(Maven + Spring Boot,未开始)
|
||||
├── server/ # 后端(Maven + Spring Boot,已迁入原型实现)
|
||||
├── docs/ # 设计文档
|
||||
└── README.md
|
||||
```
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
| 项 | 状态 |
|
||||
|---|---|
|
||||
| 元数据驱动引擎(通用查询 / 保存 / 权限) | 未设计 |
|
||||
| 后端设计与 Java 分包 | 未开始 |
|
||||
| 后端设计与 Java 分包 | 已开始(见 `server/README.md`) |
|
||||
| `stk-table-vue`(虚拟滚动表格) | 待定 |
|
||||
| LogicFlow(审批流设计器) | 待定 |
|
||||
| 登录 / 权限 / 用户模块 | 未开始(骨架已留位) |
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
- 关联、聚合或其他配置表达起来不合适的查询,可在模块配置中使用 `b_query_sql`;它是模块配置数据,不要求在后端代码中为每家公司登记 SQL 编码。
|
||||
- 对于按机构隔离的数据库,机构数据源路由继续使用现有 `OrgContext` / `OrgDataSourceManager`。各机构可在自己的配置库维护不同模块 SQL,不新增租户配置层。
|
||||
- 运行查询 SQL 必须经过现有只读校验路径;只允许查询语句,不允许用查询 SQL 执行新增、修改、删除或 DDL。写入走现有保存/删除接口或明确的业务服务。
|
||||
- `b_order_sql` 是模块默认排序;排序标识符按现有 `DbUtils` 校验规则处理。查询参数和值不得通过未校验的字符串拼接进入 SQL。
|
||||
- `b_order_sql` 是模块默认排序;排序标识符按现有 `SqlText` 校验规则处理。查询参数和值不得通过未校验的字符串拼接进入 SQL。
|
||||
|
||||
## 4. 沿用的通用数据 API
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
| `POST /data/describe` | 探测表或视图列结构 |
|
||||
| `POST /data/nextid`、`/data/nextcode` | 取雪花 ID 或模块业务编码 |
|
||||
|
||||
`loaddata` / `page` 使用表或视图名、结构化筛选和排序参数;`loaddatabysql` 用于复杂只读查询。已有 `DbUtils` 负责标识符验证、SQL 只读检查和 SQL 执行。通用保存服务依据真实表结构生成参数化写入,不通过 `b_query_sql` 写业务数据。
|
||||
`loaddata` / `page` 使用表或视图名、结构化筛选和排序参数;`loaddatabysql` 用于复杂只读查询。已有 `SqlText` 负责标识符验证与 SQL 只读检查,`MetadataReader` / `RowReader` / `RowWriter` 分别负责表结构探测、只读查询与写入执行。通用保存服务依据真实表结构生成参数化写入,不通过 `b_query_sql` 写业务数据。
|
||||
|
||||
新接入系统应沿用这些请求/响应格式和既有身份、机构、权限上下文;模块管理页面负责读取模块配置并组织请求,不需要另造一套模块专属 CRUD API。
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
|
||||
- 客户端不能以任意表名或列名绕过后端标识符验证。
|
||||
- 普通查询的筛选值以参数传递;字段、排序方向等 SQL 标识由校验后的配置控制。
|
||||
- `b_query_sql` 和 `/data/loaddatabysql` 仅用于只读查询,并使用现有 `DbUtils.validateReadOnlySql()` 校验。
|
||||
- `b_query_sql` 和 `/data/loaddatabysql` 仅用于只读查询,并使用现有 `SqlText.validateReadOnlySql()` 校验。
|
||||
- SQL 配置及执行需经过现有机构、用户和数据范围权限上下文;不能因为使用 SQL 扩展绕过权限校验。
|
||||
- 写操作使用 `/data/saveobjt`、`/data/delete` 或已有业务服务,并纳入现有事务和审计规则。
|
||||
- 版本恢复同样执行配置权限校验、引用校验和事务处理。
|
||||
|
||||
@@ -221,10 +221,21 @@ watch(
|
||||
--sidebar-muted-color: var(--g3-color-text-tertiary, #86909c);
|
||||
--sidebar-title-color: var(--g3-color-foreground, #1f2329);
|
||||
|
||||
/* 全局深色(html.dark):整片侧栏切深色。
|
||||
与 LayoutSidebar 的处理保持一致;「深色侧栏」开关由各组件自己的
|
||||
.is-dark 覆盖(见 LayoutMixedRail / LayoutMixedColumn)。 */
|
||||
.dark & {
|
||||
/* ---- 深色侧栏:两个入口都要接上 ----
|
||||
* html.dark 全局深色模式
|
||||
* html.semi-dark-sidebar 「深色侧栏」开关(浅色模式下由偏好开启,
|
||||
* 见 preferences/update-css-variables.ts:103)
|
||||
* 二者的并集恰好等于「侧栏该是深色」(semiDarkSidebar || isDark),
|
||||
* 也就是双列两列各自 is-dark 的判断条件(LayoutMixedRail:32 / LayoutMixedColumn:25)。
|
||||
*
|
||||
* ⚠️ 少接一根就是这个 bug:半深色侧栏**不会**给 html 加 .dark,所以只写 `.dark &`
|
||||
* 时,双列两列的 `.is-dark` 只把 background 刷成 #1d2129,而 --sidebar-item-color
|
||||
* 仍停在上面那行的深灰 #1f2329 —— 深字压深底,菜单文字看不见(实测对比度 1.89,
|
||||
* WCAG 正文下限是 4.5)。
|
||||
* 单列布局没这个问题:LayoutSidebar 自带同名变量组,会覆盖这里的值。
|
||||
* 深色档取值与 LayoutSidebar 的 `.is-dark` 保持一致,两种布局同色。 */
|
||||
.dark &,
|
||||
.semi-dark-sidebar & {
|
||||
--sidebar-bg: #1d2129;
|
||||
--sidebar-border: #2e3238;
|
||||
--sidebar-item-color: rgb(255 255 255 / 80%);
|
||||
|
||||
@@ -110,12 +110,16 @@ const showLogoText = computed(
|
||||
background: var(--sidebar-bg, #fff);
|
||||
border-right: 1px solid var(--sidebar-border, #e5e6eb);
|
||||
|
||||
/* 深色侧栏(semiDarkSidebar):与 LayoutSidebar 的 .is-dark 同一组值。
|
||||
全局深色(html.dark)由 DefaultLayout 根上的 .dark 规则统一处理。 */
|
||||
&.is-dark {
|
||||
background: #1d2129;
|
||||
border-right-color: #2e3238;
|
||||
}
|
||||
/* 深色侧栏:这里**不写任何颜色**。
|
||||
底色与前景(文字 / hover / 选中 / 箭头 / 标题)全部由 DefaultLayout 的
|
||||
.layout 调色板按 .dark / .semi-dark-sidebar 整体切换 —— 本列是 .layout 的
|
||||
子节点,直接继承那组 --sidebar-*。
|
||||
早期这里只刷了 background / border,没管前景,于是「深色侧栏 + 双列菜单」下
|
||||
菜单文字仍是浅色档的深灰 #1f2329,压在 #1d2129 上(实测对比度 1.89,看不见)。
|
||||
|
||||
⚠️ is-dark 这个类本身**要保留**(模板上仍在):styles/skin-shadcn.scss:123
|
||||
用 `html[data-g3-style='shadcn'] .layout-mixed-column.is-dark` 提权覆盖色板,
|
||||
删掉类会让 shadcn 皮肤的深色侧栏静默失效。 */
|
||||
|
||||
/* logo 文字行(对照 vben:VbenLogo 放进 #extra-title)。
|
||||
高度由模板行内 style 给(顶栏高 - 1,与 vben 的 extraTitleStyle 一致)。
|
||||
|
||||
@@ -176,11 +176,15 @@ defineExpose({ railItems, activeRootKey, isLeafOnly })
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 深色侧栏(semiDarkSidebar):与 LayoutSidebar 的 .is-dark 同一组值。
|
||||
全局深色(html.dark)由 DefaultLayout 根上的 .dark 规则统一处理。 */
|
||||
&.is-dark {
|
||||
background: #1d2129;
|
||||
border-right-color: #2e3238;
|
||||
}
|
||||
/* 深色侧栏:这里**不写任何颜色**。
|
||||
底色与前景(文字 / hover / 选中 / 标题)全部由 DefaultLayout 的 .layout
|
||||
调色板按 .dark / .semi-dark-sidebar 整体切换 —— 本列是 .layout 的子节点,
|
||||
直接继承那组 --sidebar-*。
|
||||
早期这里只刷了 background / border,没管前景,于是「深色侧栏 + 双列菜单」下
|
||||
文字仍是浅色档的深灰 #1f2329,压在 #1d2129 上(实测对比度 1.89,看不见)。
|
||||
|
||||
⚠️ is-dark 这个类本身**要保留**(模板上仍在):styles/skin-shadcn.scss:122
|
||||
用 `html[data-g3-style='shadcn'] .layout-mixed-rail.is-dark` 提权覆盖色板,
|
||||
删掉类会让 shadcn 皮肤的深色侧栏静默失效。 */
|
||||
}
|
||||
</style>
|
||||
@@ -1,10 +1,318 @@
|
||||
<script setup lang="ts">
|
||||
import DomainPlaceholder from '@/components/DomainPlaceholder.vue'
|
||||
import { ref, watch } from 'vue'
|
||||
import { G3Message, G3Table } from '@g3soft/ui'
|
||||
import type { G3TableColumn, TableRow } from '@g3soft/ui'
|
||||
|
||||
/**
|
||||
* 订舱单(海运)—— G3Table 使用范例。
|
||||
*
|
||||
* 三个要点:
|
||||
* 1. 假数据:本地生成,不请求接口
|
||||
* 2. 条数可调:默认 1000 行,用 rowCount 决定数据量
|
||||
* 3. 行编辑:列上配 editor,单击进入;Enter 全选内容(便于覆写)、
|
||||
* Esc 取消并恢复原值、失焦提交;值变化经 change 事件回写到本地数据
|
||||
*
|
||||
* 不分页:1000 行直接交给表格,由 G3Table 内部的**虚拟滚动门控**兜住性能
|
||||
* (> 100 行才开启,DOM 里只渲染视口内若干行)。
|
||||
*
|
||||
* 需要留意 G3Table 的两条行为(文档「实现说明」里有写):
|
||||
* - rowKey 默认取 'id',数据里必须存在该字段且唯一,否则编辑态会串行
|
||||
* - 列宽/列序拖拽要求 v-model:columns 绑定;本例不需要,故不绑(组件会自动关掉)
|
||||
*/
|
||||
|
||||
defineOptions({ name: 'OceanBooking' })
|
||||
|
||||
interface BookingRow extends TableRow {
|
||||
id: number
|
||||
bookingNo: string
|
||||
customer: string
|
||||
pol: string
|
||||
pod: string
|
||||
vessel: string
|
||||
etd: string
|
||||
containerCount: number
|
||||
status: number
|
||||
}
|
||||
|
||||
/* ---------------- 假数据 ---------------- */
|
||||
|
||||
const PORT_OPTIONS = [
|
||||
{ label: '上海', value: 'CNSHA' },
|
||||
{ label: '宁波', value: 'CNNGB' },
|
||||
{ label: '深圳', value: 'CNSZX' },
|
||||
{ label: '青岛', value: 'CNTAO' },
|
||||
{ label: '新加坡', value: 'SGSIN' },
|
||||
{ label: '釜山', value: 'KRPUS' },
|
||||
]
|
||||
|
||||
const STATUS_OPTIONS = [
|
||||
{ label: '待确认', value: 0 },
|
||||
{ label: '已确认', value: 1 },
|
||||
{ label: '已放舱', value: 2 },
|
||||
{ label: '已取消', value: 3 },
|
||||
]
|
||||
|
||||
const CUSTOMERS = ['中远海运', '东方海外', '马士基', '长荣海运', '赫伯罗特', '达飞轮船']
|
||||
|
||||
const VESSELS = ['EVER GIVEN', 'MSC OSCAR', 'COSCO SHIPPING', 'ONE APOLLO', 'HMM ALGECIRAS']
|
||||
|
||||
/** 简易可复现伪随机:同一个 index 永远得到同一行,翻页时数据不跳变。 */
|
||||
function seeded(n: number): number {
|
||||
const x = Math.sin(n * 12.9898) * 43758.5453
|
||||
return x - Math.floor(x)
|
||||
}
|
||||
|
||||
function pick<T>(list: T[], n: number): T {
|
||||
return list[Math.floor(seeded(n) * list.length) % list.length]
|
||||
}
|
||||
|
||||
/** 生成第 index 条假订舱单 */
|
||||
function makeRow(index: number): BookingRow {
|
||||
const m = String((index % 12) + 1).padStart(2, '0')
|
||||
const d = String(((index * 3) % 27) + 1).padStart(2, '0')
|
||||
return {
|
||||
id: index + 1,
|
||||
bookingNo: `BK${String(20260000 + index + 1)}`,
|
||||
customer: pick(CUSTOMERS, index * 1.7),
|
||||
pol: pick(PORT_OPTIONS, index * 2.3).value,
|
||||
pod: pick(PORT_OPTIONS, index * 3.1).value,
|
||||
vessel: pick(VESSELS, index * 4.7),
|
||||
etd: `2026-${m}-${d}`,
|
||||
containerCount: Math.floor(seeded(index * 5.9) * 90) + 10,
|
||||
status: Math.floor(seeded(index * 7.3) * 4),
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------- 数据量 ---------------- */
|
||||
|
||||
/** 数据条数:改这个值就等于「指定数据有多少」 */
|
||||
const rowCount = ref(1000)
|
||||
|
||||
/**
|
||||
* 假数据。
|
||||
*
|
||||
* 不做分页:1000 行**一次性**交给表格。G3Table 内部按行数门控虚拟滚动
|
||||
* (> 100 行才开启),所以这里会走虚拟滚动路径,DOM 里只渲染视口内的行,
|
||||
* 1000 行与 10 行的滚动开销几乎无差别。
|
||||
*
|
||||
* ⚠ 必须用 `ref`(深层响应式),**不能用 `computed` 返回普通对象数组**。
|
||||
*
|
||||
* 行内编辑是靠「直接改 row 对象上的字段」回写的(见 utils/editable.ts 的
|
||||
* onUpdate:modelValue → row[dataIndex] = value)。若行对象不是响应式代理,
|
||||
* 这次写入**不会触发任何重渲染** —— 表现极具迷惑性:
|
||||
* · change 事件照常抛出(数据确实改了)
|
||||
* · 但界面上仍旧显示旧值
|
||||
* · 点一下别处(触发无关重渲染)才"突然"更新
|
||||
* 实测:computed 数组里 row.pol 从 CNSHA 改成 CNNGB 后,
|
||||
* G3Select 收到的 modelValue 仍是 CNSHA,界面纹丝不动。
|
||||
*
|
||||
* ref 会递归代理内部对象,行字段的读写都可被追踪,编辑器选完即刷新。
|
||||
*/
|
||||
const rows = ref<BookingRow[]>([])
|
||||
|
||||
watch(
|
||||
rowCount,
|
||||
(n) => {
|
||||
rows.value = Array.from({ length: n }, (_, i) => makeRow(i))
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
/* ---------------- 列定义 ---------------- */
|
||||
|
||||
/**
|
||||
* 列定义用 `G3TableColumn<TableRow>`(而非 `G3TableColumn<BookingRow>`)。
|
||||
*
|
||||
* 原因:G3Table 的 props 是按 `TableRow` 声明的(`columns?: G3TableColumn<TableRow>[]`),
|
||||
* 没有暴露泛型参数。函数类型参数是**逆变**的,所以带 `sorter` / 带
|
||||
* `editor.options((row: BookingRow) => ...)` 的列无法赋给它 —— 直接写
|
||||
* `G3TableColumn<BookingRow>[]` 会报 TS2322。
|
||||
*
|
||||
* 行数据的类型安全改由函数体内断言承担:columns / 事件入参都必须是
|
||||
* `TableRow`(组件的 props 与 emit 都按它声明),需要具体字段时在回调里
|
||||
* `row as BookingRow`(见下方 onChange)。
|
||||
*/
|
||||
const columns = ref<G3TableColumn<TableRow>[]>([
|
||||
{ dataIndex: 'bookingNo', title: '订舱号', width: 130 },
|
||||
{
|
||||
dataIndex: 'customer',
|
||||
title: '客户',
|
||||
width: 140,
|
||||
// 文本编辑器:单击进入,失焦提交,Esc 取消并恢复原值
|
||||
editor: { type: 'text' },
|
||||
},
|
||||
{
|
||||
dataIndex: 'pol',
|
||||
title: '起运港',
|
||||
width: 120,
|
||||
// select 编辑器:options 传数组;非编辑态会把 value 解析成 label 展示
|
||||
editor: { type: 'select', options: PORT_OPTIONS },
|
||||
},
|
||||
{
|
||||
dataIndex: 'pod',
|
||||
title: '目的港',
|
||||
width: 120,
|
||||
editor: { type: 'select', options: PORT_OPTIONS },
|
||||
},
|
||||
{ dataIndex: 'vessel', title: '船名', width: 170, editor: { type: 'text' } },
|
||||
{ dataIndex: 'etd', title: '开航日', width: 120, editor: { type: 'text' } },
|
||||
{
|
||||
dataIndex: 'containerCount',
|
||||
title: '箱量',
|
||||
width: 100,
|
||||
align: 'right',
|
||||
// 数字编辑器
|
||||
editor: { type: 'number' },
|
||||
},
|
||||
{
|
||||
dataIndex: 'status',
|
||||
title: '状态',
|
||||
width: 120,
|
||||
editor: { type: 'select', options: STATUS_OPTIONS },
|
||||
},
|
||||
])
|
||||
|
||||
/* ---------------- 编辑回写 ---------------- */
|
||||
|
||||
const lastChange = ref('')
|
||||
|
||||
/**
|
||||
* 行编辑后把值写回本地数据。
|
||||
*
|
||||
* G3Table 在编辑时会**直接改行对象**(编辑器组件内部已写入),这里主要是:
|
||||
* 1. 拿到「哪一行、哪一列、改成了什么」,对接后端时在这里发接口;
|
||||
* 2. 只有值**真的变化**才会触发 —— 组件按编辑开始时的快照做了去重,
|
||||
* 「点进去又点出来」不会产生无意义的 change。
|
||||
*
|
||||
* ⚠ 入参类型必须是 `TableRow`,不能写 `BookingRow`:
|
||||
* 组件的 emit 签名是 `(e: 'change', row: TableRow, col: G3TableColumn<TableRow>, ...)`,
|
||||
* 事件处理器的参数类型直接来自它,写窄了会报 TS2322(与上面 columns 同因)。
|
||||
* 需要具体字段时在函数体内断言,见下。
|
||||
*/
|
||||
function onChange(row: TableRow, col: G3TableColumn<TableRow>, value: unknown) {
|
||||
const booking = row as BookingRow
|
||||
lastChange.value = `${booking.bookingNo} 的「${col.title}」改为 ${value}`
|
||||
}
|
||||
|
||||
function onRefresh() {
|
||||
G3Message.success(`已刷新(共 ${rowCount.value} 条)`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DomainPlaceholder
|
||||
title="订舱单"
|
||||
:features="['订舱单录入与修改', '舱位确认', '订舱状态流转', '与提单关联']"
|
||||
/>
|
||||
<div class="booking">
|
||||
<div class="booking__toolbar">
|
||||
<h2 class="booking__title">订舱单</h2>
|
||||
|
||||
<label class="booking__field">
|
||||
数据量
|
||||
<select v-model.number="rowCount" class="booking__select">
|
||||
<option v-for="n in [10, 100, 1000, 5000, 20000]" :key="n" :value="n">
|
||||
{{ n }} 条
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<button class="booking__btn" type="button" @click="onRefresh">刷新</button>
|
||||
</div>
|
||||
|
||||
<p class="booking__tip">
|
||||
单击单元格进入编辑 · 失焦(点别处)提交 · Enter 全选内容 · Esc 取消并恢复原值 ——
|
||||
<template v-if="lastChange">
|
||||
最近一次修改:<strong>{{ lastChange }}</strong>
|
||||
</template>
|
||||
<template v-else>(还没改过)</template>
|
||||
</p>
|
||||
|
||||
<!-- 表格区域:G3Table 需要父级有确定高度,这里用 flex:1 撑开 -->
|
||||
<div class="booking__table">
|
||||
<G3Table
|
||||
:columns="columns"
|
||||
:data-source="rows"
|
||||
row-key="id"
|
||||
:row-height="32"
|
||||
@change="onChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.booking {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
padding: 16px;
|
||||
background: var(--g3-color-bg-container, #fff);
|
||||
border: 1px solid var(--g3-color-border, #e5e6eb);
|
||||
border-radius: 8px;
|
||||
|
||||
&__toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: var(--g3-color-text, #1f2329);
|
||||
}
|
||||
|
||||
&__field {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 13px;
|
||||
color: var(--g3-color-text-secondary, #4e5969);
|
||||
}
|
||||
|
||||
&__select {
|
||||
height: 30px;
|
||||
padding: 0 8px;
|
||||
font-size: 13px;
|
||||
color: var(--g3-color-text, #1f2329);
|
||||
background: var(--g3-color-bg-container, #fff);
|
||||
border: 1px solid var(--g3-color-border, #e5e6eb);
|
||||
border-radius: 6px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--g3-color-primary, #165dff);
|
||||
}
|
||||
}
|
||||
|
||||
&__btn {
|
||||
height: 30px;
|
||||
padding: 0 14px;
|
||||
font-size: 13px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
background: var(--g3-color-primary, #165dff);
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.88;
|
||||
}
|
||||
}
|
||||
|
||||
&__tip {
|
||||
margin: 12px 0;
|
||||
font-size: 13px;
|
||||
color: var(--g3-color-text-secondary, #4e5969);
|
||||
flex: none;
|
||||
}
|
||||
|
||||
/* 表格容器必须有确定高度:G3Table 内部按容器高度算虚拟滚动与滚动区 */
|
||||
&__table {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user