2026032301
This commit is contained in:
parent
dc801a0ba1
commit
de22605535
@ -262,6 +262,7 @@
|
|||||||
"useSwipe": true,
|
"useSwipe": true,
|
||||||
"useSystemConfig": true,
|
"useSystemConfig": true,
|
||||||
"useTabBarStore": true,
|
"useTabBarStore": true,
|
||||||
|
"useTableContainerHeight": true,
|
||||||
"useTemplateRef": true,
|
"useTemplateRef": true,
|
||||||
"useTemplateRefsList": true,
|
"useTemplateRefsList": true,
|
||||||
"useTextDirection": true,
|
"useTextDirection": true,
|
||||||
|
|||||||
@ -43,74 +43,6 @@ OMS(Order Management System,订单管理系统)作为独立服务运行和
|
|||||||
4. **出库扫描**:订单发货时完成**出库扫描**,进入待发货/运输中。
|
4. **出库扫描**:订单发货时完成**出库扫描**,进入待发货/运输中。
|
||||||
5. **闭环完成**:出库扫描完成后,订单进入运输、派送环节,正常订单流程闭环结束。
|
5. **闭环完成**:出库扫描完成后,订单进入运输、派送环节,正常订单流程闭环结束。
|
||||||
|
|
||||||
----------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
## 一、通用设计规范
|
|
||||||
|
|
||||||
### 1.1 接口设计规范
|
|
||||||
|
|
||||||
#### HTTP 方法约束(仅 POST / GET)
|
|
||||||
|
|
||||||
| 方法 | 用途 | 说明 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| **GET** | 查询、详情、列表、导出 | 幂等、无副作用,参数放 Query String |
|
|
||||||
| **POST** | 新增、修改、删除、状态变更、提交 | 所有变更类操作统一用 POST,参数放 Request Body |
|
|
||||||
|
|
||||||
> 因网关/代理/防火墙限制,仅支持 GET/POST 时,更新、删除等操作均通过 POST + 业务动作区分。
|
|
||||||
|
|
||||||
#### URL 设计规范
|
|
||||||
|
|
||||||
| 规范 | 说明 | 示例 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 路径小写 | 使用小写字母,短横线分隔 | `/api/order/list`、`/api/sales-product/detail` |
|
|
||||||
| 资源名词 | URL 表示资源,不包含动词 | `/api/customer` 而非 `/api/getCustomer` |
|
|
||||||
| 层级清晰 | 最多 3 级,避免过深 | `/api/channel/sales-product/price/list` |
|
|
||||||
| 动作后缀 | POST 动作可用动词后缀 | `/api/order/delete`、`/api/recharge/approve` |
|
|
||||||
|
|
||||||
#### 统一响应格式
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0, // 0-成功,非0-业务错误码
|
|
||||||
"msg": "success", // 提示信息
|
|
||||||
"data": { } // 业务数据,可为 null
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 分页规范
|
|
||||||
|
|
||||||
**请求**:`page`(页码,从 1 开始)、`pageSize`(每页条数,默认 20,最大 100)
|
|
||||||
|
|
||||||
**响应**:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0, // 0-成功,非0-业务错误码
|
|
||||||
"msg": "success", // 提示信息
|
|
||||||
"data": {
|
|
||||||
"items": [], // 数据列表
|
|
||||||
"total": 100 // 总条数
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 错误码规范
|
|
||||||
|
|
||||||
| 错误码 | 说明 |
|
|
||||||
| --- | --- |
|
|
||||||
| 0 | 成功 |
|
|
||||||
| 401 | 未登录 |
|
|
||||||
| 403 | 无权限 |
|
|
||||||
| 400 | 参数错误 |
|
|
||||||
| 500 | 服务器内部错误 |
|
|
||||||
| 1xxxx | 业务错误码(按模块划分) |
|
|
||||||
|
|
||||||
#### 其他规范
|
|
||||||
|
|
||||||
- **Content-Type**:`application/json`;文件上传用 `multipart/form-data`
|
|
||||||
- **时间格式**:`yyyy-MM-dd HH:mm:ss` 或 ISO 8601
|
|
||||||
- **认证**:`Authorization: Bearer {token}` 或 Cookie
|
|
||||||
- **幂等**:涉及金额、库存等变更的接口需支持幂等(如传 `requestId`)
|
|
||||||
|
|
||||||
----------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -81,6 +81,57 @@ a-layout (垂直)
|
|||||||
@screen-xxl: 1600px; // 超大屏
|
@screen-xxl: 1600px; // 超大屏
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 2.5 页面壳、白卡片与列表区尺寸(列表 / Handsontable 类页面)
|
||||||
|
|
||||||
|
以下约定与 `default-layout` + `page-layout`、`*-container` / `*-page` 等实现一致,避免「灰底大块留白、列表高度塌缩、内外边距叠成 32px/40px」等问题。
|
||||||
|
|
||||||
|
#### 2.5.1 页面层(灰底壳)
|
||||||
|
|
||||||
|
| 项目 | 约定 |
|
||||||
|
|------|------|
|
||||||
|
| 背景色 | `#f0f3f4`(与布局主内容区一致) |
|
||||||
|
| 页面根容器 padding | **`16px 16px 24px`**:上/左/右 **16px**,**底 24px** —— 与视口底边留白,列表区随 flex 撑满后,整块内容与屏幕底边保持 **24px** 间距 |
|
||||||
|
| 参与主内容区高度 | `flex: 1; min-height: 0; display: flex; flex-direction: column;`,与 `PageLayout` 子节点 flex 链衔接 |
|
||||||
|
| 纵向滚动 | 页面根容器宜 `overflow-y: hidden`(或仅最外层允许滚动),**表格/Handsontable 在内部 `.list-wrap` / `.wtHolder` 滚动**,避免整页与表格双滚动条 |
|
||||||
|
|
||||||
|
#### 2.5.2 白卡片(白色内容块)
|
||||||
|
|
||||||
|
| 项目 | 约定 |
|
||||||
|
|------|------|
|
||||||
|
| 视觉 | 背景 `#fff`,`border-radius: 8px`,`box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08)`(可按页面微调透明度) |
|
||||||
|
| 撑满剩余高度 | `flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden` |
|
||||||
|
| 内边距(与列表区配合) | **二选一,避免与 `.list-wrap` 重复叠加**:<br>• **推荐**:白卡片 **`padding: 0`**,由 **页头/搜索行、`.table-toolbar`、`.list-wrap`** 分区控制留白(见下);<br>• **简化页**:白卡片统一 `padding: 16px`,则内部列表区不再重复加左右底 padding |
|
||||||
|
|
||||||
|
#### 2.5.3 表格工具栏(`.table-toolbar`)
|
||||||
|
|
||||||
|
| 项目 | 约定 |
|
||||||
|
|------|------|
|
||||||
|
| 与列表横向对齐 | `padding: 16px 16px 0`(上 16、左右 16,底由与列表的间距承担) |
|
||||||
|
| 与分页/标题间距 | `margin-bottom: 16px`(分页组件(`PaginationPro`)与列表区 `.list-wrap` 的固定间距) |
|
||||||
|
| 收缩 | `flex-shrink: 0` |
|
||||||
|
|
||||||
|
分页组件放置约束:`PaginationPro` 放在 `.toolbar-right` 内,并与 `toolbar-left` 保持在同一行(同一个 `flex` 行中)展示;工具栏外层使用 `padding: 16px 16px 0` 以保证分页组件距上边/右边均为 16px,且不需要在 `PaginationPro` 外再额外套一层 padding。
|
||||||
|
|
||||||
|
有**独立搜索行**(如轨迹配置页)时:搜索框可用 `margin: 16px 16px 16px`,与工具栏、列表区 **左右 16px** 对齐。
|
||||||
|
|
||||||
|
#### 2.5.4 列表区(`.list-wrap`)
|
||||||
|
|
||||||
|
用于包裹 **HandsontablePro** 或需 **固定测量高度** 的表格容器(配合 `useTableContainerHeight` 的 `tableContainerRef` **挂在本层**)。
|
||||||
|
|
||||||
|
| 项目 | 约定 |
|
||||||
|
|------|------|
|
||||||
|
| 与内容区四边距离 | **默认**:`padding: 16px`(上、右、下、左均为 **16px**,相对**白卡片内容边缘**或分区后的可用区域,勿与白卡片 padding 重复叠加)。<br>**与 `.list-toolbar` 联用**:当 `.list-toolbar` 使用 `margin-bottom: 16px` 作为“分页组件到列表”的固定间距时,为避免重复留白,建议 `.list-wrap` 使用 `padding: 0 16px 16px`(上 0、左右/下 16)。 |
|
||||||
|
| 布局 | `flex: 1; min-height: 0; width: 100%; box-sizing: border-box; overflow: hidden; display: flex; flex-direction: column` |
|
||||||
|
| 触摸/滚动冒泡 | 可选:`overscroll-behavior: contain; touch-action: pan-x pan-y` |
|
||||||
|
| 子级 Handsontable | 建议 `:deep(.handsontable-pro-container) { flex: 1; min-height: 0; display: flex; flex-direction: column; }`,保证纵向撑满 |
|
||||||
|
|
||||||
|
分页组件同一行约束(列表工具栏场景):当页面存在 `.list-toolbar` / `table-toolbar` 时,`PaginationPro` 必须放在右侧同一行的工具栏区域(如 `.toolbar-right`),不要将分页组件单独再做成一行(避免“工具栏一行 + 分页下一行”的不一致布局)。
|
||||||
|
|
||||||
|
#### 2.5.5 与布局组件的关系
|
||||||
|
|
||||||
|
- `PageLayout` 下直接子节点(路由页根)需能被主内容区赋予高度:项目中通过 `flex: 1; min-height: 0` 等与 `a-layout-content` 链式配合。
|
||||||
|
- `default-layout` 中对 `[class$='-container']` 的样式仅作用于布局模板内节点;**业务页根类名**(如 `xxx-page`、`track-order-container`)需在**页面自身样式**中按上表补齐 flex 与 padding。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 三、色彩规范
|
## 三、色彩规范
|
||||||
@ -426,8 +477,11 @@ const { t } = useI18n();
|
|||||||
|
|
||||||
### 13.2 容器类
|
### 13.2 容器类
|
||||||
|
|
||||||
- 页面根容器:`xxx-container`(如 `role-page`、业务相关命名),保证 `padding: 16px`
|
- 页面根容器:`xxx-container` / `xxx-page`(如 `role-page`、`track-order-container`),**列表类页面**推荐 `padding: 16px 16px 24px`,详见 **§2.5 页面壳、白卡片与列表区尺寸**
|
||||||
- 表格工具栏:`table-toolbar`、`toolbar-left`、`toolbar-right`
|
- 白卡片:`*-page-card`、`*-section` 等,与 `.list-wrap` 的 padding **勿重复叠加**
|
||||||
|
- 列表区:`.list-wrap`,**四边 16px**(相对分区后的内容边缘)
|
||||||
|
- 表格工具栏:`table-toolbar`、`toolbar-left`、`toolbar-right`,工具栏 **`padding: 16px 16px 0`** 与列表对齐
|
||||||
|
- Handsontable/列表分页工具栏:`.list-toolbar`(当页面没有标准 `table-toolbar` 时),与 `table-toolbar` 保持一致,使用 **`padding: 16px 16px 0`** 且 `margin-bottom: 16px`;`PaginationPro` 必须与 `toolbar-left` 同一行展示(放在 `.toolbar-right` 内,禁止分页另起行),同时 `.list-wrap` 在联用场景建议 `padding-top: 0`(例如 `padding: 0 16px 16px`),实现分页距上/右 16px、距列表 16px
|
||||||
|
|
||||||
### 13.3 按钮间距
|
### 13.3 按钮间距
|
||||||
|
|
||||||
@ -438,8 +492,10 @@ const { t } = useI18n();
|
|||||||
|
|
||||||
## 十四、检查清单(新页面开发)
|
## 十四、检查清单(新页面开发)
|
||||||
|
|
||||||
- [ ] 使用 `xxx-container` 或等效布局结构
|
- [ ] 使用 `xxx-container` / `xxx-page` 或等效布局结构;列表页见 **§2.5**:灰底 **`padding: 16px 16px 24px`**、`flex: 1; min-height: 0`
|
||||||
- [ ] 工具栏使用 `table-toolbar`、`toolbar-left`、`toolbar-right`
|
- [ ] 白卡片与 **`.list-wrap`** 内边距勿重复叠加(推荐白卡片 `padding: 0` + 分区 padding)
|
||||||
|
- [ ] **`.list-wrap`**:`padding: 16px`、`flex: 1; min-height: 0`;需测高时 `ref` 挂在本层
|
||||||
|
- [ ] 工具栏使用 `table-toolbar`、`toolbar-left`、`toolbar-right`,工具栏 **`padding: 16px 16px 0`**
|
||||||
- [ ] 按钮、输入框符合全局样式(高度、圆角)
|
- [ ] 按钮、输入框符合全局样式(高度、圆角)
|
||||||
- [ ] 使用主题色变量,不硬编码 `#2D8C8C`
|
- [ ] 使用主题色变量,不硬编码 `#2D8C8C`
|
||||||
- [ ] 弹窗 / 抽屉使用 ModalPro / DrawerPro + useModal
|
- [ ] 弹窗 / 抽屉使用 ModalPro / DrawerPro + useModal
|
||||||
@ -517,4 +573,4 @@ const { t } = useI18n();
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*文档版本:1.0 | 更新日期:2025-03-21*
|
*文档版本:1.1 | 更新日期:2026-03-23*
|
||||||
|
|||||||
2
src/auto-imports.d.ts
vendored
2
src/auto-imports.d.ts
vendored
@ -251,6 +251,7 @@ declare global {
|
|||||||
const useSwipe: typeof import('@vueuse/core')['useSwipe']
|
const useSwipe: typeof import('@vueuse/core')['useSwipe']
|
||||||
const useSystemConfig: typeof import('./composables/useSystemConfig')['useSystemConfig']
|
const useSystemConfig: typeof import('./composables/useSystemConfig')['useSystemConfig']
|
||||||
const useTabBarStore: typeof import('./store/index')['useTabBarStore']
|
const useTabBarStore: typeof import('./store/index')['useTabBarStore']
|
||||||
|
const useTableContainerHeight: typeof import('./composables/useTableContainerHeight')['useTableContainerHeight']
|
||||||
const useTemplateRef: typeof import('vue')['useTemplateRef']
|
const useTemplateRef: typeof import('vue')['useTemplateRef']
|
||||||
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
|
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
|
||||||
const useTextDirection: typeof import('@vueuse/core')['useTextDirection']
|
const useTextDirection: typeof import('@vueuse/core')['useTextDirection']
|
||||||
@ -561,6 +562,7 @@ declare module 'vue' {
|
|||||||
readonly useSwipe: UnwrapRef<typeof import('@vueuse/core')['useSwipe']>
|
readonly useSwipe: UnwrapRef<typeof import('@vueuse/core')['useSwipe']>
|
||||||
readonly useSystemConfig: UnwrapRef<typeof import('./composables/useSystemConfig')['useSystemConfig']>
|
readonly useSystemConfig: UnwrapRef<typeof import('./composables/useSystemConfig')['useSystemConfig']>
|
||||||
readonly useTabBarStore: UnwrapRef<typeof import('./store/index')['useTabBarStore']>
|
readonly useTabBarStore: UnwrapRef<typeof import('./store/index')['useTabBarStore']>
|
||||||
|
readonly useTableContainerHeight: UnwrapRef<typeof import('./composables/useTableContainerHeight')['useTableContainerHeight']>
|
||||||
readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']>
|
readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']>
|
||||||
readonly useTemplateRefsList: UnwrapRef<typeof import('@vueuse/core')['useTemplateRefsList']>
|
readonly useTemplateRefsList: UnwrapRef<typeof import('@vueuse/core')['useTemplateRefsList']>
|
||||||
readonly useTextDirection: UnwrapRef<typeof import('@vueuse/core')['useTextDirection']>
|
readonly useTextDirection: UnwrapRef<typeof import('@vueuse/core')['useTextDirection']>
|
||||||
|
|||||||
@ -58,6 +58,10 @@
|
|||||||
showCheckboxColumn?: boolean;
|
showCheckboxColumn?: boolean;
|
||||||
// 复选框列表头文案(空则表头为空)
|
// 复选框列表头文案(空则表头为空)
|
||||||
checkboxColumnHeader?: string;
|
checkboxColumnHeader?: string;
|
||||||
|
// 序号列表头文案
|
||||||
|
serialNoHeader?: string;
|
||||||
|
// 操作列表头文案
|
||||||
|
actionColumnHeader?: string;
|
||||||
// 复选框变化回调
|
// 复选框变化回调
|
||||||
onCheckboxChange?: (row: any, checked: boolean, rowIndex: number) => void;
|
onCheckboxChange?: (row: any, checked: boolean, rowIndex: number) => void;
|
||||||
// 操作列渲染器
|
// 操作列渲染器
|
||||||
@ -105,6 +109,8 @@
|
|||||||
actionColumnWidth: 60,
|
actionColumnWidth: 60,
|
||||||
showCheckboxColumn: false,
|
showCheckboxColumn: false,
|
||||||
checkboxColumnHeader: '',
|
checkboxColumnHeader: '',
|
||||||
|
serialNoHeader: '序号',
|
||||||
|
actionColumnHeader: '操作',
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
multiValueFields: () => [],
|
multiValueFields: () => [],
|
||||||
height: undefined,
|
height: undefined,
|
||||||
@ -176,16 +182,23 @@
|
|||||||
|
|
||||||
// 计算表格高度
|
// 计算表格高度
|
||||||
const calculatedHeight = computed(() => {
|
const calculatedHeight = computed(() => {
|
||||||
if (props.height) {
|
if (props.height != null && props.height > 0) {
|
||||||
return props.height;
|
return props.height;
|
||||||
}
|
}
|
||||||
const headerHeight = 40;
|
const headerHeight = 40;
|
||||||
const rowHeight = 50;
|
const rowHeight = 50;
|
||||||
const maxRows = 20;
|
const maxRows = 20;
|
||||||
const minHeight = 1000;
|
const minHeight = 400;
|
||||||
return Math.max(headerHeight + maxRows * rowHeight, minHeight);
|
return Math.max(headerHeight + maxRows * rowHeight, minHeight);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 外部传入 height 为 0 时不能用 ?? 回退(0 非 nullish),需显式判断 */
|
||||||
|
const resolveTableHeight = (containerH: number): number => {
|
||||||
|
if (props.height != null && props.height > 0) return props.height;
|
||||||
|
if (containerH > 0) return containerH;
|
||||||
|
return calculatedHeight.value;
|
||||||
|
};
|
||||||
|
|
||||||
// 创建省略号渲染器
|
// 创建省略号渲染器
|
||||||
const createEllipsisRenderer = () => {
|
const createEllipsisRenderer = () => {
|
||||||
return function(instance: any, td: any, row: number, col: number, prop: any, value: any) {
|
return function(instance: any, td: any, row: number, col: number, prop: any, value: any) {
|
||||||
@ -241,7 +254,7 @@
|
|||||||
// 操作列(在前面)
|
// 操作列(在前面)
|
||||||
if (props.showActionColumn && props.actionColumnPosition === 'start') {
|
if (props.showActionColumn && props.actionColumnPosition === 'start') {
|
||||||
columns.push({ data: currentDataIndex, type: 'text', readOnly: true, width: props.actionColumnWidth, className: 'htCenter', headerClassName: 'htLeft' });
|
columns.push({ data: currentDataIndex, type: 'text', readOnly: true, width: props.actionColumnWidth, className: 'htCenter', headerClassName: 'htLeft' });
|
||||||
headerTexts.push('操作');
|
headerTexts.push(props.actionColumnHeader);
|
||||||
currentDataIndex++;
|
currentDataIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,7 +268,7 @@
|
|||||||
headerClassName: 'htLeft',
|
headerClassName: 'htLeft',
|
||||||
renderer: ellipsisRenderer
|
renderer: ellipsisRenderer
|
||||||
});
|
});
|
||||||
headerTexts.push('序号');
|
headerTexts.push(props.serialNoHeader);
|
||||||
currentDataIndex++;
|
currentDataIndex++;
|
||||||
|
|
||||||
// 数据列(beforeLast 时最后一列在操作列之后)
|
// 数据列(beforeLast 时最后一列在操作列之后)
|
||||||
@ -264,7 +277,7 @@
|
|||||||
// beforeLast:在倒数第二列后插入操作列
|
// beforeLast:在倒数第二列后插入操作列
|
||||||
if (props.showActionColumn && props.actionColumnPosition === 'beforeLast' && index === dataColCount - 1) {
|
if (props.showActionColumn && props.actionColumnPosition === 'beforeLast' && index === dataColCount - 1) {
|
||||||
columns.push({ data: currentDataIndex, type: 'text', readOnly: true, width: props.actionColumnWidth, className: 'htCenter', headerClassName: 'htLeft' });
|
columns.push({ data: currentDataIndex, type: 'text', readOnly: true, width: props.actionColumnWidth, className: 'htCenter', headerClassName: 'htLeft' });
|
||||||
headerTexts.push('操作');
|
headerTexts.push(props.actionColumnHeader);
|
||||||
currentDataIndex++;
|
currentDataIndex++;
|
||||||
}
|
}
|
||||||
columns.push({
|
columns.push({
|
||||||
@ -282,7 +295,7 @@
|
|||||||
// 操作列(在后面,非 beforeLast 时)
|
// 操作列(在后面,非 beforeLast 时)
|
||||||
if (props.showActionColumn && props.actionColumnPosition === 'end') {
|
if (props.showActionColumn && props.actionColumnPosition === 'end') {
|
||||||
columns.push({ data: currentDataIndex, type: 'text', readOnly: true, width: props.actionColumnWidth, className: 'htCenter', headerClassName: 'htLeft' });
|
columns.push({ data: currentDataIndex, type: 'text', readOnly: true, width: props.actionColumnWidth, className: 'htCenter', headerClassName: 'htLeft' });
|
||||||
headerTexts.push('操作');
|
headerTexts.push(props.actionColumnHeader);
|
||||||
currentDataIndex++;
|
currentDataIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,7 +356,7 @@
|
|||||||
try {
|
try {
|
||||||
// 未传 height 时使用容器实际高度,使列表高度与容器一致
|
// 未传 height 时使用容器实际高度,使列表高度与容器一致
|
||||||
const containerH = containerRef.value?.clientHeight ?? 0;
|
const containerH = containerRef.value?.clientHeight ?? 0;
|
||||||
const tableHeight = props.height ?? (containerH > 0 ? containerH : calculatedHeight.value);
|
const tableHeight = resolveTableHeight(containerH);
|
||||||
|
|
||||||
const hotConfig: any = {
|
const hotConfig: any = {
|
||||||
data: data,
|
data: data,
|
||||||
@ -511,7 +524,7 @@
|
|||||||
|
|
||||||
// 操作列、序号列:用极简表头替换,去除 div.relative/span.colHeader 等多余容器,确保 60px 下完整显示
|
// 操作列、序号列:用极简表头替换,去除 div.relative/span.colHeader 等多余容器,确保 60px 下完整显示
|
||||||
if (col === actionColStartIndex || col === serialColIndex || col === actionColEndIndex) {
|
if (col === actionColStartIndex || col === serialColIndex || col === actionColEndIndex) {
|
||||||
const label = col === serialColIndex ? '序号' : '操作';
|
const label = col === serialColIndex ? props.serialNoHeader : props.actionColumnHeader;
|
||||||
TH.innerHTML = '';
|
TH.innerHTML = '';
|
||||||
TH.classList.add('ht-header-simple');
|
TH.classList.add('ht-header-simple');
|
||||||
const span = document.createElement('span');
|
const span = document.createElement('span');
|
||||||
@ -1069,17 +1082,12 @@
|
|||||||
}, 50);
|
}, 50);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 初始化完成后再次刷新尺寸
|
// 初始化完成后刷新尺寸(避免与 ResizeObserver 重复狂刷导致 stretchH 列宽错乱)
|
||||||
const finalizeInit = () => {
|
const finalizeInit = () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (hotInstance) {
|
if (hotInstance) {
|
||||||
hotInstance.refreshDimensions();
|
hotInstance.refreshDimensions();
|
||||||
|
setTimeout(() => hotInstance?.refreshDimensions(), 100);
|
||||||
setTimeout(() => {
|
|
||||||
if (hotInstance) {
|
|
||||||
hotInstance.refreshDimensions();
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
};
|
};
|
||||||
@ -1193,13 +1201,15 @@
|
|||||||
if (!containerRef.value) return;
|
if (!containerRef.value) return;
|
||||||
resizeObserver = new ResizeObserver((entries) => {
|
resizeObserver = new ResizeObserver((entries) => {
|
||||||
const entry = entries[0];
|
const entry = entries[0];
|
||||||
if (entry && hotInstance && entry.contentRect.height > 0) {
|
if (!entry || !hotInstance) return;
|
||||||
const h = Math.floor(entry.contentRect.height);
|
const h = entry.contentRect.height;
|
||||||
hotInstance.updateSettings({ height: h });
|
// 高度以本组件根容器实测为准;勿用外层 props.height 覆盖,否则与 flex 内真实高度不一致,stretchH 会把列压满导致横向条消失
|
||||||
|
if (h <= 0) return;
|
||||||
|
const fh = Math.floor(h);
|
||||||
|
hotInstance.updateSettings({ height: fh });
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
hotInstance?.refreshDimensions();
|
hotInstance?.refreshDimensions();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
resizeObserver.observe(containerRef.value);
|
resizeObserver.observe(containerRef.value);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -80,7 +80,7 @@
|
|||||||
:model-value="props.pageSize"
|
:model-value="props.pageSize"
|
||||||
size="small"
|
size="small"
|
||||||
class="page-size-select"
|
class="page-size-select"
|
||||||
:style="{ width: '110px' }"
|
:style="{ width: '85px' }"
|
||||||
@update:model-value="handlePageSizeChange"
|
@update:model-value="handlePageSizeChange"
|
||||||
>
|
>
|
||||||
<a-option
|
<a-option
|
||||||
@ -97,7 +97,7 @@
|
|||||||
v-model="jumpPage"
|
v-model="jumpPage"
|
||||||
size="small"
|
size="small"
|
||||||
class="jump-input"
|
class="jump-input"
|
||||||
:style="{ width: '60px' }"
|
:style="{ width: '50px' }"
|
||||||
@keyup.enter="handleJumpPage"
|
@keyup.enter="handleJumpPage"
|
||||||
/>
|
/>
|
||||||
<span class="page-text">{{ t('common.pagination.page') }}</span>
|
<span class="page-text">{{ t('common.pagination.page') }}</span>
|
||||||
|
|||||||
@ -52,6 +52,9 @@
|
|||||||
padding: 16px;
|
padding: 16px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-height: 0;
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layout {
|
.layout {
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-layout-root > * {
|
.page-layout-root :deep(> *) {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -109,6 +109,16 @@ export default {
|
|||||||
'menu.api.apiDebugger': 'API Debugger',
|
'menu.api.apiDebugger': 'API Debugger',
|
||||||
'menu.api.apiLike': 'API LIKE',
|
'menu.api.apiLike': 'API LIKE',
|
||||||
'menu.api.apiTest': 'API Test',
|
'menu.api.apiTest': 'API Test',
|
||||||
|
|
||||||
|
// API - Call Statistics
|
||||||
|
'api.callStatistics.name.app': 'Application Name',
|
||||||
|
'api.callStatistics.name.provider': 'Service Provider Name',
|
||||||
|
'api.callStatistics.table.date': 'Date',
|
||||||
|
'api.callStatistics.table.interfaceName': 'Interface Name',
|
||||||
|
'api.callStatistics.table.total': 'Total Calls',
|
||||||
|
'api.callStatistics.table.succeedCount': 'Successful Calls',
|
||||||
|
'api.callStatistics.table.failCount': 'Failed Calls',
|
||||||
|
'api.callStatistics.table.successRate': 'Success Rate',
|
||||||
'menu.system': 'System',
|
'menu.system': 'System',
|
||||||
'menu.system.org': 'Organization',
|
'menu.system.org': 'Organization',
|
||||||
'menu.system.organization': 'Organization',
|
'menu.system.organization': 'Organization',
|
||||||
|
|||||||
@ -109,6 +109,16 @@ export default {
|
|||||||
'menu.api.apiDebugger': 'API调试',
|
'menu.api.apiDebugger': 'API调试',
|
||||||
'menu.api.apiLike': 'API LIKE',
|
'menu.api.apiLike': 'API LIKE',
|
||||||
'menu.api.apiTest': 'API测试',
|
'menu.api.apiTest': 'API测试',
|
||||||
|
|
||||||
|
// API - 调用统计
|
||||||
|
'api.callStatistics.name.app': '应用名称',
|
||||||
|
'api.callStatistics.name.provider': '服务商名称',
|
||||||
|
'api.callStatistics.table.date': '日期',
|
||||||
|
'api.callStatistics.table.interfaceName': '接口名称',
|
||||||
|
'api.callStatistics.table.total': '总调用次数',
|
||||||
|
'api.callStatistics.table.succeedCount': '成功调用次数',
|
||||||
|
'api.callStatistics.table.failCount': '失败调用次数',
|
||||||
|
'api.callStatistics.table.successRate': '成功率',
|
||||||
'menu.system': '系统',
|
'menu.system': '系统',
|
||||||
'menu.system.org': '组织管理',
|
'menu.system.org': '组织管理',
|
||||||
'menu.system.organization': '组织管理',
|
'menu.system.organization': '组织管理',
|
||||||
|
|||||||
@ -6,14 +6,16 @@
|
|||||||
import HandsontablePro from '@/components/HandsontablePro/index.vue';
|
import HandsontablePro from '@/components/HandsontablePro/index.vue';
|
||||||
import PaginationPro from '@/components/PaginationPro/index.vue';
|
import PaginationPro from '@/components/PaginationPro/index.vue';
|
||||||
import { useTableContainerHeight } from '@/composables/useTableContainerHeight';
|
import { useTableContainerHeight } from '@/composables/useTableContainerHeight';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
defineOptions({ name: 'CallStatistics' });
|
defineOptions({ name: 'CallStatistics' });
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const activeName = ref('1'); // 1:应用,2:服务商
|
const activeName = ref('1'); // 1:应用,2:服务商
|
||||||
const { tableContainerRef, tableHeight } = useTableContainerHeight();
|
const { tableContainerRef, tableHeight } = useTableContainerHeight();
|
||||||
const tabList = [
|
const tabList = [
|
||||||
{ key: '1', label: '应用' },
|
{ key: '1', label: t('system.log.options.app') },
|
||||||
{ key: '2', label: '服务商' },
|
{ key: '2', label: t('system.log.options.provider') },
|
||||||
];
|
];
|
||||||
|
|
||||||
const tableData = ref<any[]>([]);
|
const tableData = ref<any[]>([]);
|
||||||
@ -30,16 +32,16 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const typeParam = computed(() => (activeName.value === '2' ? 2 : 1));
|
const typeParam = computed(() => (activeName.value === '2' ? 2 : 1));
|
||||||
const nameLabel = computed(() => (activeName.value === '2' ? '服务商名称' : '应用名称'));
|
const nameLabel = computed(() => (activeName.value === '2' ? t('api.callStatistics.name.provider') : t('api.callStatistics.name.app')));
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{ key: 'date', label: '日期', width: 120, searchable: false },
|
{ key: 'date', label: t('api.callStatistics.table.date'), width: 120, searchable: false },
|
||||||
{ key: 'name', label: nameLabel.value, width: 180, searchable: true, searchField: 'name' },
|
{ key: 'name', label: nameLabel.value, width: 180, searchable: true, searchField: 'name' },
|
||||||
{ key: 'interfaceName', label: '接口名称', width: 180, searchable: true, searchField: 'interfaceName' },
|
{ key: 'interfaceName', label: t('api.callStatistics.table.interfaceName'), width: 180, searchable: true, searchField: 'interfaceName' },
|
||||||
{ key: 'total', label: '总调用次数', width: 120, searchable: false },
|
{ key: 'total', label: t('api.callStatistics.table.total'), width: 120, searchable: false },
|
||||||
{ key: 'succeedCount', label: '成功调用次数', width: 140, searchable: false },
|
{ key: 'succeedCount', label: t('api.callStatistics.table.succeedCount'), width: 140, searchable: false },
|
||||||
{ key: 'failCount', label: '失败调用次数', width: 140, searchable: false },
|
{ key: 'failCount', label: t('api.callStatistics.table.failCount'), width: 140, searchable: false },
|
||||||
{ key: 'successRate', label: '成功率', width: 100, searchable: false },
|
{ key: 'successRate', label: t('api.callStatistics.table.successRate'), width: 100, searchable: false },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const processedTableData = computed(() =>
|
const processedTableData = computed(() =>
|
||||||
@ -121,7 +123,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="call-statistics-container">
|
<div class="callStatistics-container">
|
||||||
|
<div class="callStatistics-page-card">
|
||||||
<!-- 标签页导航 -->
|
<!-- 标签页导航 -->
|
||||||
<div class="tabs-section">
|
<div class="tabs-section">
|
||||||
<div class="tabs-container">
|
<div class="tabs-container">
|
||||||
@ -161,7 +164,7 @@
|
|||||||
/>
|
/>
|
||||||
<a-input
|
<a-input
|
||||||
v-model="searchParams.interfaceName"
|
v-model="searchParams.interfaceName"
|
||||||
placeholder="接口名称"
|
:placeholder="t('system.log.placeholder.interfaceName')"
|
||||||
class="filter-input"
|
class="filter-input"
|
||||||
style="width: 160px"
|
style="width: 160px"
|
||||||
allow-clear
|
allow-clear
|
||||||
@ -184,7 +187,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Handsontable 表格 -->
|
<!-- Handsontable 表格 -->
|
||||||
<div ref="tableContainerRef" class="table-wrapper">
|
<div ref="tableContainerRef" class="list-wrap">
|
||||||
<HandsontablePro
|
<HandsontablePro
|
||||||
:key="`table-${activeName}-${currentPage}`"
|
:key="`table-${activeName}-${currentPage}`"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
@ -202,24 +205,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.call-statistics-container {
|
.callStatistics-container {
|
||||||
padding: 16px;
|
padding: 16px 16px 24px;
|
||||||
background-color: #f0f3f4;
|
background-color: #f0f3f4;
|
||||||
height: 100vh;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.callStatistics-page-card {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-section {
|
.tabs-section {
|
||||||
background: #fff;
|
padding: 16px 16px 0;
|
||||||
border-radius: 8px;
|
margin-bottom: 8px;
|
||||||
padding: 0 24px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-container {
|
.tabs-container {
|
||||||
@ -255,18 +274,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table-section {
|
.table-section {
|
||||||
background: #fff;
|
background: transparent;
|
||||||
border-radius: 8px;
|
border-radius: 0;
|
||||||
padding: 12px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: none;
|
||||||
height: calc(100vh - 80px);
|
flex: 1;
|
||||||
overflow: visible;
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-toolbar {
|
.table-toolbar {
|
||||||
|
padding: 16px 16px 0;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -305,8 +326,26 @@
|
|||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: auto;
|
overflow: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-wrap {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
:deep(.handsontable-pro-container) {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -681,10 +681,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table-toolbar {
|
.table-toolbar {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
.toolbar-left {
|
.toolbar-left {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive } from 'vue'
|
import { computed, nextTick, reactive, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { $baseMessage } from '@/utils'
|
import { $baseMessage } from '@/utils'
|
||||||
|
import HandsontablePro from '@/components/HandsontablePro/index.vue'
|
||||||
|
import { useTableContainerHeight } from '@/composables/useTableContainerHeight'
|
||||||
|
import ModalPro from '@/components/ModalPro/index.vue'
|
||||||
|
import useModal from '@/components/ModalPro/useModal'
|
||||||
|
import FormPro from '@/components/FormPro/index.vue'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'AutoTrigger'
|
name: 'AutoTrigger'
|
||||||
@ -9,8 +15,20 @@ defineOptions({
|
|||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
// 表格数据
|
type AutoTriggerRow = {
|
||||||
const tableData = ref([
|
id: number
|
||||||
|
status: boolean
|
||||||
|
operation: string
|
||||||
|
triggerTiming: string
|
||||||
|
triggerCondition: string
|
||||||
|
promptMessage: string
|
||||||
|
notifier: string
|
||||||
|
createTime: string
|
||||||
|
creator: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格数据(模拟)
|
||||||
|
const tableData = ref<AutoTriggerRow[]>([
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
status: true,
|
status: true,
|
||||||
@ -49,27 +67,221 @@ const tableData = ref([
|
|||||||
// 分页相关
|
// 分页相关
|
||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
const pageSize = ref(10)
|
const pageSize = ref(10)
|
||||||
const totalCount = ref(tableData.value.length)
|
const totalCount = computed(() => tableData.value.length)
|
||||||
|
|
||||||
|
// 当前页数据(HandsontablePro 内部不做分页,只负责渲染当前 data)
|
||||||
|
const processedTableData = computed(() => {
|
||||||
|
const start = (currentPage.value - 1) * pageSize.value
|
||||||
|
return tableData.value.slice(start, start + pageSize.value)
|
||||||
|
})
|
||||||
|
|
||||||
// 切换状态
|
// 切换状态
|
||||||
const handleToggleStatus = (row: any) => {
|
function handleToggleStatus(row: AutoTriggerRow) {
|
||||||
row.status = !row.status
|
row.status = !row.status
|
||||||
$baseMessage(row.status ? t('channel.autoTrigger.message.statusEnabled') : t('channel.autoTrigger.message.statusDisabled'), 'success')
|
$baseMessage(
|
||||||
|
row.status ? t('channel.autoTrigger.message.statusEnabled') : t('channel.autoTrigger.message.statusDisabled'),
|
||||||
|
'success'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除
|
// 删除
|
||||||
const handleDelete = (row: any) => {
|
function handleDelete(row: AutoTriggerRow) {
|
||||||
$baseMessage(t('channel.autoTrigger.message.deletePending'), 'info')
|
$baseMessage(t('channel.autoTrigger.message.deletePending'), 'info')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 编辑
|
type ModalMode = 'add' | 'edit'
|
||||||
const handleEdit = (row: any) => {
|
const modalMode = ref<ModalMode>('add')
|
||||||
$baseMessage(t('channel.autoTrigger.message.editPending'), 'info')
|
const modalTitle = computed(() =>
|
||||||
|
modalMode.value === 'add' ? t('channel.autoTrigger.actions.add') : t('channel.autoTrigger.actions.edit')
|
||||||
|
)
|
||||||
|
|
||||||
|
const [registerAutoTriggerModal, modalMethods] = useModal() as any
|
||||||
|
|
||||||
|
const formRef = ref<InstanceType<typeof FormPro> | null>(null)
|
||||||
|
const editId = ref<number | null>(null)
|
||||||
|
|
||||||
|
const formState = reactive({
|
||||||
|
status: true,
|
||||||
|
operation: '拦截',
|
||||||
|
triggerTiming: '',
|
||||||
|
triggerCondition: '',
|
||||||
|
promptMessage: '',
|
||||||
|
notifier: '',
|
||||||
|
createTime: '',
|
||||||
|
creator: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const formItemList = computed(() => {
|
||||||
|
const requiredMsg = t('system.serviceProviders.modal.rules.requiredInput')
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: t('channel.autoTrigger.table.status'),
|
||||||
|
field: 'status',
|
||||||
|
comp: 'ASelect',
|
||||||
|
span: 24,
|
||||||
|
rules: [{ required: true, message: requiredMsg }],
|
||||||
|
compProps: {
|
||||||
|
placeholder: t('common.selectPlaceholder'),
|
||||||
|
options: [
|
||||||
|
{ label: t('channel.autoTrigger.message.statusEnabled'), value: true },
|
||||||
|
{ label: t('channel.autoTrigger.message.statusDisabled'), value: false },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('channel.autoTrigger.table.operation'),
|
||||||
|
field: 'operation',
|
||||||
|
comp: 'ASelect',
|
||||||
|
span: 24,
|
||||||
|
rules: [{ required: true, message: requiredMsg }],
|
||||||
|
compProps: {
|
||||||
|
placeholder: t('common.selectPlaceholder'),
|
||||||
|
options: [
|
||||||
|
{ label: '拦截', value: '拦截' },
|
||||||
|
{ label: '警告', value: '警告' },
|
||||||
|
{ label: '问题件', value: '问题件' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('channel.autoTrigger.table.triggerTiming'),
|
||||||
|
field: 'triggerTiming',
|
||||||
|
comp: 'AInput',
|
||||||
|
span: 24,
|
||||||
|
rules: [{ required: true, message: requiredMsg }],
|
||||||
|
compProps: { placeholder: t('channel.area.form.placeholder.input') },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('channel.autoTrigger.table.triggerCondition'),
|
||||||
|
field: 'triggerCondition',
|
||||||
|
comp: 'ATextarea',
|
||||||
|
span: 24,
|
||||||
|
rules: [{ required: true, message: requiredMsg }],
|
||||||
|
compProps: {
|
||||||
|
placeholder: t('channel.area.form.placeholder.input'),
|
||||||
|
autoSize: { minRows: 3, maxRows: 6 },
|
||||||
|
showWordLimit: true,
|
||||||
|
maxlength: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('channel.autoTrigger.table.promptMessage'),
|
||||||
|
field: 'promptMessage',
|
||||||
|
comp: 'AInput',
|
||||||
|
span: 24,
|
||||||
|
rules: [{ required: true, message: requiredMsg }],
|
||||||
|
compProps: { placeholder: t('channel.area.form.placeholder.input') },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('channel.autoTrigger.table.notifier'),
|
||||||
|
field: 'notifier',
|
||||||
|
comp: 'AInput',
|
||||||
|
span: 24,
|
||||||
|
compProps: { placeholder: t('channel.area.form.placeholder.input'), allowClear: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('channel.autoTrigger.table.createTime'),
|
||||||
|
field: 'createTime',
|
||||||
|
comp: 'AInput',
|
||||||
|
span: 24,
|
||||||
|
compProps: { disabled: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('channel.autoTrigger.table.creator'),
|
||||||
|
field: 'creator',
|
||||||
|
comp: 'AInput',
|
||||||
|
span: 24,
|
||||||
|
compProps: { disabled: true },
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
async function openAddModal() {
|
||||||
|
modalMode.value = 'add'
|
||||||
|
editId.value = null
|
||||||
|
Object.assign(formState, {
|
||||||
|
status: true,
|
||||||
|
operation: '拦截',
|
||||||
|
triggerTiming: '',
|
||||||
|
triggerCondition: '',
|
||||||
|
promptMessage: '',
|
||||||
|
notifier: '',
|
||||||
|
createTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
creator: '当前用户',
|
||||||
|
})
|
||||||
|
modalMethods.setModalProps({ title: modalTitle.value, width: 720 })
|
||||||
|
modalMethods.openModal(true)
|
||||||
|
await nextTick()
|
||||||
|
formRef.value?.setValues?.(formState)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增
|
async function openEditModal(row: AutoTriggerRow) {
|
||||||
const handleAdd = () => {
|
modalMode.value = 'edit'
|
||||||
$baseMessage(t('channel.autoTrigger.message.addPending'), 'info')
|
editId.value = row.id
|
||||||
|
Object.assign(formState, {
|
||||||
|
status: row.status,
|
||||||
|
operation: row.operation,
|
||||||
|
triggerTiming: row.triggerTiming,
|
||||||
|
triggerCondition: row.triggerCondition,
|
||||||
|
promptMessage: row.promptMessage,
|
||||||
|
notifier: row.notifier,
|
||||||
|
createTime: row.createTime,
|
||||||
|
creator: row.creator,
|
||||||
|
})
|
||||||
|
modalMethods.setModalProps({ title: modalTitle.value, width: 720 })
|
||||||
|
modalMethods.openModal(true)
|
||||||
|
await nextTick()
|
||||||
|
formRef.value?.setValues?.(formState)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑入口(Handsontable 操作列)
|
||||||
|
function handleEdit(row: AutoTriggerRow) {
|
||||||
|
openEditModal(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增入口(页面新增按钮)
|
||||||
|
function handleAdd() {
|
||||||
|
openAddModal()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleAutoTriggerModalOk() {
|
||||||
|
try {
|
||||||
|
await formRef.value?.validate()
|
||||||
|
const values = formRef.value?.getValues?.() || {}
|
||||||
|
|
||||||
|
if (modalMode.value === 'add') {
|
||||||
|
const newRow: AutoTriggerRow = {
|
||||||
|
id: Date.now(),
|
||||||
|
...values,
|
||||||
|
status: !!values.status,
|
||||||
|
notifier: values.notifier || '',
|
||||||
|
}
|
||||||
|
tableData.value.unshift(newRow)
|
||||||
|
currentPage.value = 1
|
||||||
|
$baseMessage(t('common.addSuccess'), 'success')
|
||||||
|
} else {
|
||||||
|
const id = editId.value
|
||||||
|
if (id == null) return
|
||||||
|
const idx = tableData.value.findIndex((d) => d.id === id)
|
||||||
|
if (idx >= 0) {
|
||||||
|
tableData.value[idx] = {
|
||||||
|
...tableData.value[idx],
|
||||||
|
...values,
|
||||||
|
status: !!values.status,
|
||||||
|
notifier: values.notifier || '',
|
||||||
|
}
|
||||||
|
$baseMessage(t('common.editSuccess'), 'success')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modalMethods.closeModal()
|
||||||
|
} catch (_) {
|
||||||
|
// 校验失败由表单组件自行提示
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAutoTriggerModalCancel() {
|
||||||
|
modalMethods.closeModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分页改变
|
// 分页改变
|
||||||
@ -82,33 +294,142 @@ const handlePageSizeChange = (size: number) => {
|
|||||||
pageSize.value = size
|
pageSize.value = size
|
||||||
currentPage.value = 1
|
currentPage.value = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hotColumns = computed(() => [
|
||||||
|
{ key: 'status', label: t('channel.autoTrigger.table.status'), width: 80 },
|
||||||
|
{ key: 'operation', label: t('channel.autoTrigger.table.operation'), width: 110 },
|
||||||
|
{ key: 'triggerTiming', label: t('channel.autoTrigger.table.triggerTiming'), width: 110 },
|
||||||
|
{ key: 'triggerCondition', label: t('channel.autoTrigger.table.triggerCondition'), width: 320 },
|
||||||
|
{ key: 'promptMessage', label: t('channel.autoTrigger.table.promptMessage'), width: 240 },
|
||||||
|
{ key: 'notifier', label: t('channel.autoTrigger.table.notifier'), width: 120 },
|
||||||
|
{ key: 'createTime', label: t('channel.autoTrigger.table.createTime'), width: 170 },
|
||||||
|
{ key: 'creator', label: t('channel.autoTrigger.table.creator'), width: 110 }
|
||||||
|
])
|
||||||
|
|
||||||
|
// 与其他 Handsontable 页面一致:捕获阶段处理内联开关,避免 Handsontable 抢事件
|
||||||
|
const { tableContainerRef, tableHeight } = useTableContainerHeight()
|
||||||
|
const handsontableHeight = computed(() => Math.max(tableHeight.value || 0, 400))
|
||||||
|
|
||||||
|
function handleStatusSwitchMouseDown(e: MouseEvent) {
|
||||||
|
const switchEl = (e.target as HTMLElement | null)?.closest?.('.customer-status-switch') as HTMLElement | null
|
||||||
|
if (!switchEl) return
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
const rowIndex = parseInt(switchEl.getAttribute('data-row-index') || '0', 10)
|
||||||
|
const idx = (currentPage.value - 1) * pageSize.value + rowIndex
|
||||||
|
const record = tableData.value[idx]
|
||||||
|
if (record) {
|
||||||
|
handleToggleStatus(record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function customCellRenderer(row: number, col: number, value: any, rowData: AutoTriggerRow): string | null {
|
||||||
|
// HandsontablePro 列结构(本页不显示 checkbox,action 列在末尾):
|
||||||
|
// col=0: 序号;col=1..n: 数据列
|
||||||
|
const dataColIndex = col - 1
|
||||||
|
if (dataColIndex < 0 || dataColIndex >= hotColumns.value.length) return null
|
||||||
|
const colKey = hotColumns.value[dataColIndex].key
|
||||||
|
|
||||||
|
if (colKey === 'status') {
|
||||||
|
const enabled = !!rowData.status
|
||||||
|
return `
|
||||||
|
<label class="customer-status-switch" data-row-index="${row}" style="display:inline-flex;align-items:center;cursor:pointer;">
|
||||||
|
<input type="checkbox" ${enabled ? 'checked' : ''} style="display:none;" />
|
||||||
|
<span class="switch-track" style="position:relative;width:36px;height:20px;background:${enabled ? '#00b42a' : '#e5e6eb'};border-radius:10px;transition:background .2s;">
|
||||||
|
<span class="switch-thumb" style="position:absolute;top:2px;left:${enabled ? '18px' : '2px'};width:16px;height:16px;background:#fff;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,.2);transition:left .2s;"></span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colKey === 'operation') {
|
||||||
|
const op = rowData.operation
|
||||||
|
const cls = op === '拦截' ? 'intercept' : op === '警告' ? 'warning' : op === '问题件' ? 'problem' : ''
|
||||||
|
return `<span class="operation-tag ${cls}">${op}</span>`
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function actionRenderer(row: AutoTriggerRow, rowIndex: number): string {
|
||||||
|
return `
|
||||||
|
<div class="action-btns" style="display:flex;gap:12px;align-items:center;justify-content:center;">
|
||||||
|
<span
|
||||||
|
data-action="delete"
|
||||||
|
data-row-index="${rowIndex}"
|
||||||
|
title="${t('channel.autoTrigger.actions.delete')}"
|
||||||
|
style="cursor:pointer;color:#f53f3f;display:inline-flex;align-items:center;justify-content:center;"
|
||||||
|
>
|
||||||
|
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2" style="width:16px;height:16px;">
|
||||||
|
<path d="M5 11h5.5m0 0v29a1 1 0 0 0 1 1h25a1 1 0 0 0 1-1V11m-27 0H16m21.5 0H43m-5.5 0H32m-16 0V7h16v4m-16 0h16M20 18v15m8-15v15"/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
data-action="edit"
|
||||||
|
data-row-index="${rowIndex}"
|
||||||
|
title="${t('channel.autoTrigger.actions.edit')}"
|
||||||
|
style="cursor:pointer;color:var(--app-primary-color);display:inline-flex;align-items:center;justify-content:center;"
|
||||||
|
>
|
||||||
|
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2" style="width:16px;height:16px;">
|
||||||
|
<path d="m30.48 19.038 5.733-5.734a1 1 0 0 0 0-1.414l-5.586-5.586a1 1 0 0 0-1.414 0l-5.734 5.734m7 7L15.763 33.754a1 1 0 0 1-.59.286l-6.048.708a1 1 0 0 1-1.113-1.069l.477-6.31a1 1 0 0 1 .29-.631l14.7-14.7m7 7-7-7M6 42h36" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleActionClick(row: AutoTriggerRow, rowIndex: number, action?: string) {
|
||||||
|
const idx = (currentPage.value - 1) * pageSize.value + rowIndex
|
||||||
|
const record = tableData.value[idx] || row
|
||||||
|
switch (action) {
|
||||||
|
case 'delete':
|
||||||
|
handleDelete(record)
|
||||||
|
break
|
||||||
|
case 'edit':
|
||||||
|
default:
|
||||||
|
handleEdit(record)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handsontableRef = ref<InstanceType<typeof HandsontablePro> | null>(null)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.auto-trigger-layout {
|
.autoTrigger-container {
|
||||||
display: flex;
|
width: 100%;
|
||||||
height: 100%;
|
box-sizing: border-box;
|
||||||
padding: 16px;
|
padding: 16px 16px 24px;
|
||||||
gap: 16px;
|
|
||||||
background-color: var(--color-fill-2);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-content.content-panel {
|
|
||||||
flex: 1;
|
|
||||||
background-color: var(--color-bg-1);
|
|
||||||
border-radius: 24px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
background-color: #f0f3f4;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoTrigger-page-card {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-toolbar {
|
.table-toolbar {
|
||||||
|
padding: 16px 16px 0;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
.toolbar-left {
|
.toolbar-left {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@ -229,6 +550,28 @@ const handlePageSizeChange = (size: number) => {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-wrap {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
touch-action: pan-x pan-y;
|
||||||
|
|
||||||
|
:deep(.handsontable-pro-container) {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.custom-table-header {
|
.custom-table-header {
|
||||||
background: #f7f8fa;
|
background: #f7f8fa;
|
||||||
border-bottom: 1px solid #e5e6eb;
|
border-bottom: 1px solid #e5e6eb;
|
||||||
@ -431,8 +774,12 @@ const handlePageSizeChange = (size: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.empty-state {
|
.empty-state {
|
||||||
padding: 40px 0;
|
flex: 1;
|
||||||
text-align: center;
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 状态开关样式
|
// 状态开关样式
|
||||||
@ -444,32 +791,33 @@ const handlePageSizeChange = (size: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 操作类型样式
|
// 操作类型样式
|
||||||
.operation-tag {
|
:deep(.operation-tag) {
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
&.intercept {
|
// HandsontablePro 单元格使用 innerHTML 注入,需用 :deep 让样式穿透 scoped
|
||||||
|
:deep(.operation-tag.intercept) {
|
||||||
background: #ffe7ba;
|
background: #ffe7ba;
|
||||||
color: #d46b08;
|
color: #d46b08;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.warning {
|
:deep(.operation-tag.warning) {
|
||||||
background: #fff2e8;
|
background: #fff2e8;
|
||||||
color: #d4380d;
|
color: #d4380d;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.problem {
|
:deep(.operation-tag.problem) {
|
||||||
background: #fff1f0;
|
background: #fff1f0;
|
||||||
color: #cf1322;
|
color: #cf1322;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="auto-trigger-layout">
|
<div class="autoTrigger-container">
|
||||||
<div class="right-content content-panel">
|
<div class="autoTrigger-page-card">
|
||||||
<!-- 表格操作按钮和分页控制 -->
|
<!-- 表格操作按钮和分页控制 -->
|
||||||
<div class="table-toolbar">
|
<div class="table-toolbar">
|
||||||
<div class="toolbar-left">
|
<div class="toolbar-left">
|
||||||
@ -518,115 +866,53 @@ const handlePageSizeChange = (size: number) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 自定义表格 -->
|
<!-- HandsontablePro 列表区 -->
|
||||||
<div class="custom-table-container">
|
|
||||||
<!-- 自定义表头 -->
|
|
||||||
<div class="custom-table-header">
|
|
||||||
<div class="header-row">
|
|
||||||
<div class="header-cell index-cell">
|
|
||||||
{{ t('channel.autoTrigger.table.serialNo') }}
|
|
||||||
</div>
|
|
||||||
<div class="header-cell status-cell">
|
|
||||||
{{ t('channel.autoTrigger.table.status') }}
|
|
||||||
</div>
|
|
||||||
<div class="header-cell operation-cell">
|
|
||||||
{{ t('channel.autoTrigger.table.operation') }}
|
|
||||||
</div>
|
|
||||||
<div class="header-cell timing-cell">
|
|
||||||
{{ t('channel.autoTrigger.table.triggerTiming') }}
|
|
||||||
</div>
|
|
||||||
<div class="header-cell condition-cell">
|
|
||||||
{{ t('channel.autoTrigger.table.triggerCondition') }}
|
|
||||||
</div>
|
|
||||||
<div class="header-cell message-cell">
|
|
||||||
{{ t('channel.autoTrigger.table.promptMessage') }}
|
|
||||||
</div>
|
|
||||||
<div class="header-cell notifier-cell">
|
|
||||||
{{ t('channel.autoTrigger.table.notifier') }}
|
|
||||||
</div>
|
|
||||||
<div class="header-cell time-cell">
|
|
||||||
{{ t('channel.autoTrigger.table.createTime') }}
|
|
||||||
</div>
|
|
||||||
<div class="header-cell creator-cell">
|
|
||||||
{{ t('channel.autoTrigger.table.creator') }}
|
|
||||||
</div>
|
|
||||||
<div class="header-cell action-cell">
|
|
||||||
{{ t('channel.autoTrigger.table.actions') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 表格内容 -->
|
|
||||||
<div class="custom-table-body">
|
|
||||||
<div
|
|
||||||
v-for="(row, index) in tableData"
|
|
||||||
:key="row.id"
|
|
||||||
class="table-row"
|
|
||||||
>
|
|
||||||
<div class="table-cell index-cell">
|
|
||||||
{{ index + 1 }}
|
|
||||||
</div>
|
|
||||||
<div class="table-cell status-cell">
|
|
||||||
<a-switch
|
|
||||||
v-model="row.status"
|
|
||||||
class="status-switch"
|
|
||||||
@change="handleToggleStatus(row)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="table-cell operation-cell">
|
|
||||||
<span
|
|
||||||
class="operation-tag"
|
|
||||||
:class="{
|
|
||||||
intercept: row.operation === '拦截',
|
|
||||||
warning: row.operation === '警告',
|
|
||||||
problem: row.operation === '问题件'
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ row.operation }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="table-cell timing-cell">
|
|
||||||
{{ row.triggerTiming }}
|
|
||||||
</div>
|
|
||||||
<div class="table-cell condition-cell">
|
|
||||||
{{ row.triggerCondition }}
|
|
||||||
</div>
|
|
||||||
<div class="table-cell message-cell">
|
|
||||||
{{ row.promptMessage }}
|
|
||||||
</div>
|
|
||||||
<div class="table-cell notifier-cell">
|
|
||||||
{{ row.notifier || '--' }}
|
|
||||||
</div>
|
|
||||||
<div class="table-cell time-cell">
|
|
||||||
{{ row.createTime }}
|
|
||||||
</div>
|
|
||||||
<div class="table-cell creator-cell">
|
|
||||||
{{ row.creator }}
|
|
||||||
</div>
|
|
||||||
<div class="table-cell action-cell">
|
|
||||||
<div class="action-buttons">
|
|
||||||
<a
|
|
||||||
class="action-link"
|
|
||||||
@click="handleDelete(row)"
|
|
||||||
>
|
|
||||||
{{ t('channel.autoTrigger.actions.delete') }}
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
class="action-link"
|
|
||||||
@click="handleEdit(row)"
|
|
||||||
>
|
|
||||||
{{ t('channel.autoTrigger.actions.edit') }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 空状态 -->
|
|
||||||
<div v-if="!tableData.length" class="empty-state">
|
<div v-if="!tableData.length" class="empty-state">
|
||||||
<a-empty :description="t('channel.autoTrigger.empty')" />
|
<a-empty :description="t('channel.autoTrigger.empty')" />
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
ref="tableContainerRef"
|
||||||
|
class="list-wrap"
|
||||||
|
@mousedown.capture="handleStatusSwitchMouseDown"
|
||||||
|
>
|
||||||
|
<HandsontablePro
|
||||||
|
ref="handsontableRef"
|
||||||
|
:columns="hotColumns"
|
||||||
|
:data="processedTableData"
|
||||||
|
:total="totalCount"
|
||||||
|
:current-page="currentPage"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:show-action-column="true"
|
||||||
|
:action-column-position="'end'"
|
||||||
|
:action-column-width="100"
|
||||||
|
:serial-no-header="t('channel.autoTrigger.table.serialNo')"
|
||||||
|
:action-column-header="t('channel.autoTrigger.table.actions')"
|
||||||
|
:action-renderer="actionRenderer"
|
||||||
|
:custom-cell-renderer="customCellRenderer"
|
||||||
|
:stretch-columns="true"
|
||||||
|
:height="handsontableHeight"
|
||||||
|
@action-click="handleActionClick"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<ModalPro
|
||||||
|
:title="modalTitle"
|
||||||
|
:width="720"
|
||||||
|
@register="registerAutoTriggerModal"
|
||||||
|
@ok="handleAutoTriggerModalOk"
|
||||||
|
@cancel="handleAutoTriggerModalCancel"
|
||||||
|
>
|
||||||
|
<FormPro
|
||||||
|
ref="formRef"
|
||||||
|
:item-list="formItemList"
|
||||||
|
:label-col-props="{ span: 6 }"
|
||||||
|
:wrapper-col-props="{ span: 16 }"
|
||||||
|
:row-gutter="20"
|
||||||
|
:default-span="6"
|
||||||
|
form-layout="horizontal"
|
||||||
|
/>
|
||||||
|
</ModalPro>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="label-design-page">
|
<div class="labelDesign-container">
|
||||||
|
<div class="labelDesign-page-card">
|
||||||
<FullDesigner
|
<FullDesigner
|
||||||
ref="designerRef"
|
ref="designerRef"
|
||||||
default-lang="cn"
|
default-lang="cn"
|
||||||
@ -12,6 +13,7 @@
|
|||||||
<template #headerRight></template>
|
<template #headerRight></template>
|
||||||
</FullDesigner>
|
</FullDesigner>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@ -43,35 +45,57 @@ function onSave(payload: { template: unknown; data: unknown }) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.label-design-page {
|
.labelDesign-container {
|
||||||
padding: 16px;
|
padding: 16px 16px 24px;
|
||||||
background-color: var(--color-bg-1);
|
background-color: #f0f3f4;
|
||||||
min-height: calc(100vh - 86px);
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.labelDesign-page-card {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 顶部栏背景透明(第一行 .primary-toolbar + 第二行 .secondary-toolbar) */
|
/* 顶部栏背景透明(第一行 .primary-toolbar + 第二行 .secondary-toolbar) */
|
||||||
.label-design-page :deep(.primary-toolbar),
|
.labelDesign-page-card :deep(.primary-toolbar),
|
||||||
.label-design-page :deep(.primary-toolbar .toolbar-content),
|
.labelDesign-page-card :deep(.primary-toolbar .toolbar-content),
|
||||||
.label-design-page :deep(.secondary-toolbar),
|
.labelDesign-page-card :deep(.secondary-toolbar),
|
||||||
.label-design-page :deep(.secondary-toolbar .toolbar-content) {
|
.labelDesign-page-card :deep(.secondary-toolbar .toolbar-content) {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 顶部中间/右侧区域也透明 */
|
/* 顶部中间/右侧区域也透明 */
|
||||||
.label-design-page :deep(.header-center),
|
.labelDesign-page-card :deep(.header-center),
|
||||||
.label-design-page :deep(.header-right.toolbar-right) {
|
.labelDesign-page-card :deep(.header-right.toolbar-right) {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 隐藏 vg-print 顶部左侧 logo + 文本(打包代码中为 .header-left / .toolbar-logo / .toolbar-title) */
|
/* 隐藏 vg-print 顶部左侧 logo + 文本(打包代码中为 .header-left / .toolbar-logo / .toolbar-title) */
|
||||||
.label-design-page :deep(.header-left) {
|
.labelDesign-page-card :deep(.header-left) {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 隐藏 vg-print 内置的反馈/帮助按钮(class: btn-feedback) */
|
/* 隐藏 vg-print 内置的反馈/帮助按钮(class: btn-feedback) */
|
||||||
.label-design-page :deep(.btn-feedback) {
|
.labelDesign-page-card :deep(.btn-feedback) {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 让 FullDesigner 在白卡片内纵向撑满 */
|
||||||
|
.labelDesign-page-card :deep(.vg-print),
|
||||||
|
.labelDesign-page-card :deep(.full-designer),
|
||||||
|
.labelDesign-page-card :deep(.fullDesigner),
|
||||||
|
.labelDesign-page-card :deep(.designer-root) {
|
||||||
|
height: 100%;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,16 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="waybill-container">
|
<div class="waybill-container">
|
||||||
<!-- 页面标题栏 -->
|
|
||||||
<div class="page-header">
|
|
||||||
<div class="header-left">
|
|
||||||
<h2 class="page-title">{{ $t('operation.inbound.pageTitle') }}</h2>
|
|
||||||
</div>
|
|
||||||
<div class="header-right">
|
|
||||||
<icon-notification class="sound-icon" />
|
|
||||||
<span class="broadcast-text">{{ $t('operation.inbound.broadcastSettings') }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 表单区域 -->
|
<!-- 表单区域 -->
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<a-form :model="formData" layout="vertical" class="waybill-form">
|
<a-form :model="formData" layout="vertical" class="waybill-form">
|
||||||
@ -110,7 +99,7 @@
|
|||||||
<div class="recent-orders-section">
|
<div class="recent-orders-section">
|
||||||
<div class="list-toolbar">
|
<div class="list-toolbar">
|
||||||
<div class="toolbar-left">
|
<div class="toolbar-left">
|
||||||
<span class="section-subtitle">{{ $t('operation.inbound.recentOrders.subtitle') }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar-right">
|
<div class="toolbar-right">
|
||||||
<div class="pagination-controls">
|
<div class="pagination-controls">
|
||||||
@ -144,7 +133,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { ref, reactive, computed, onMounted } from 'vue';
|
import { ref, reactive, computed, onMounted } from 'vue';
|
||||||
import { IconNotification } from '@arco-design/web-vue/es/icon';
|
|
||||||
import { $baseMessage } from '@/utils';
|
import { $baseMessage } from '@/utils';
|
||||||
import HandsontablePro from '@/components/HandsontablePro/index.vue';
|
import HandsontablePro from '@/components/HandsontablePro/index.vue';
|
||||||
import PaginationPro from '@/components/PaginationPro/index.vue';
|
import PaginationPro from '@/components/PaginationPro/index.vue';
|
||||||
@ -303,8 +291,8 @@ onMounted(() => {
|
|||||||
.waybill-container {
|
.waybill-container {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
min-height: 100vh;
|
flex: 1;
|
||||||
height: auto;
|
min-height: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -449,9 +437,10 @@ onMounted(() => {
|
|||||||
.recent-orders-section {
|
.recent-orders-section {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 24px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
@ -459,8 +448,10 @@ onMounted(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 12px;
|
padding: 16px 16px 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
.toolbar-left .section-subtitle {
|
.toolbar-left .section-subtitle {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@ -490,14 +481,17 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handsontable 列表区固定高度,父级 flex 链供 HandsontablePro 撑满
|
|
||||||
.list-wrap {
|
.list-wrap {
|
||||||
flex: 0 0 auto;
|
width: 100%;
|
||||||
height: 850px;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
touch-action: pan-x pan-y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,16 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="waybill-container">
|
<div class="waybill-container">
|
||||||
<!-- 页面标题栏 -->
|
|
||||||
<div class="page-header">
|
|
||||||
<div class="header-left">
|
|
||||||
<h2 class="page-title">{{ $t('operation.outbound.pageTitle') }}</h2>
|
|
||||||
</div>
|
|
||||||
<div class="header-right">
|
|
||||||
<icon-notification class="sound-icon" />
|
|
||||||
<span class="broadcast-text">{{ $t('operation.outbound.broadcastSettings') }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 表单区域 -->
|
<!-- 表单区域 -->
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<a-form :model="formData" layout="vertical" class="waybill-form">
|
<a-form :model="formData" layout="vertical" class="waybill-form">
|
||||||
@ -39,7 +28,7 @@
|
|||||||
<div class="recent-orders-section">
|
<div class="recent-orders-section">
|
||||||
<div class="list-toolbar">
|
<div class="list-toolbar">
|
||||||
<div class="toolbar-left">
|
<div class="toolbar-left">
|
||||||
<span class="section-subtitle">{{ $t('operation.outbound.recentOrders.subtitle') }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar-right">
|
<div class="toolbar-right">
|
||||||
<div class="pagination-controls">
|
<div class="pagination-controls">
|
||||||
@ -73,7 +62,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { ref, reactive, computed, onMounted } from 'vue';
|
import { ref, reactive, computed, onMounted } from 'vue';
|
||||||
import { IconNotification } from '@arco-design/web-vue/es/icon';
|
|
||||||
import { $baseMessage } from '@/utils';
|
import { $baseMessage } from '@/utils';
|
||||||
import HandsontablePro from '@/components/HandsontablePro/index.vue';
|
import HandsontablePro from '@/components/HandsontablePro/index.vue';
|
||||||
import PaginationPro from '@/components/PaginationPro/index.vue';
|
import PaginationPro from '@/components/PaginationPro/index.vue';
|
||||||
@ -189,8 +177,8 @@ onMounted(() => {
|
|||||||
.waybill-container {
|
.waybill-container {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
min-height: 100vh;
|
flex: 1;
|
||||||
height: auto;
|
min-height: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -273,9 +261,10 @@ onMounted(() => {
|
|||||||
.recent-orders-section {
|
.recent-orders-section {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 24px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
@ -283,8 +272,10 @@ onMounted(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 12px;
|
padding: 16px 16px 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
.toolbar-left .section-subtitle {
|
.toolbar-left .section-subtitle {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@ -315,12 +306,16 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.list-wrap {
|
.list-wrap {
|
||||||
flex: 0 0 auto;
|
width: 100%;
|
||||||
height: 850px;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
touch-action: pan-x pan-y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,16 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="waybill-container">
|
<div class="waybill-container">
|
||||||
<!-- 页面标题栏 -->
|
|
||||||
<div class="page-header">
|
|
||||||
<div class="header-left">
|
|
||||||
<h2 class="page-title">{{ $t('operation.printLabel.pageTitle') }}</h2>
|
|
||||||
</div>
|
|
||||||
<div class="header-right">
|
|
||||||
<icon-notification class="sound-icon" />
|
|
||||||
<span class="broadcast-text">{{ $t('operation.printLabel.broadcastSettings') }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 表单区域 -->
|
<!-- 表单区域 -->
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<a-form :model="formData" layout="vertical" class="waybill-form">
|
<a-form :model="formData" layout="vertical" class="waybill-form">
|
||||||
@ -86,7 +75,7 @@
|
|||||||
<div class="recent-orders-section">
|
<div class="recent-orders-section">
|
||||||
<div class="list-toolbar">
|
<div class="list-toolbar">
|
||||||
<div class="toolbar-left">
|
<div class="toolbar-left">
|
||||||
<span class="section-subtitle">{{ $t('operation.printLabel.recentOrders.subtitle') }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar-right">
|
<div class="toolbar-right">
|
||||||
<div class="pagination-controls">
|
<div class="pagination-controls">
|
||||||
@ -120,7 +109,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { ref, reactive, computed, onMounted } from 'vue';
|
import { ref, reactive, computed, onMounted } from 'vue';
|
||||||
import { IconNotification } from '@arco-design/web-vue/es/icon';
|
|
||||||
import { $baseMessage } from '@/utils';
|
import { $baseMessage } from '@/utils';
|
||||||
import HandsontablePro from '@/components/HandsontablePro/index.vue';
|
import HandsontablePro from '@/components/HandsontablePro/index.vue';
|
||||||
import PaginationPro from '@/components/PaginationPro/index.vue';
|
import PaginationPro from '@/components/PaginationPro/index.vue';
|
||||||
@ -302,8 +290,8 @@ function handleReset() {
|
|||||||
.waybill-container {
|
.waybill-container {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
min-height: 100vh;
|
flex: 1;
|
||||||
height: auto;
|
min-height: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -400,9 +388,10 @@ function handleReset() {
|
|||||||
.recent-orders-section {
|
.recent-orders-section {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 24px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
@ -410,8 +399,10 @@ function handleReset() {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 12px;
|
padding: 16px 16px 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
.toolbar-left .section-subtitle {
|
.toolbar-left .section-subtitle {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@ -442,12 +433,16 @@ function handleReset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.list-wrap {
|
.list-wrap {
|
||||||
flex: 0 0 auto;
|
width: 100%;
|
||||||
height: 850px;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
touch-action: pan-x pan-y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -521,12 +521,14 @@
|
|||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.my-orders-container {
|
.my-orders-container {
|
||||||
padding: 16px;
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 16px 16px 24px;
|
||||||
background-color: #f0f3f4;
|
background-color: #f0f3f4;
|
||||||
min-height: 100vh;
|
flex: 1;
|
||||||
height: auto;
|
min-height: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
@ -781,18 +783,22 @@
|
|||||||
.table-section {
|
.table-section {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 16px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-toolbar {
|
.table-toolbar {
|
||||||
margin-bottom: 8px;
|
padding: 16px 16px 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
.toolbar-left {
|
.toolbar-left {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@ -820,11 +826,22 @@
|
|||||||
.list-wrap {
|
.list-wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
height: 1150px;
|
min-height: 0;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
touch-action: pan-x pan-y;
|
||||||
|
|
||||||
|
:deep(.handsontable-pro-container) {
|
||||||
|
width: 100%;
|
||||||
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.handsontable-wrapper {
|
.handsontable-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@ -319,10 +319,15 @@
|
|||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.place-order-container {
|
.place-order-container {
|
||||||
padding: 24px;
|
padding: 16px 16px 24px;
|
||||||
background-color: #f5f5f5;
|
background-color: #f0f3f4;
|
||||||
height: 100vh;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
@ -339,7 +344,9 @@
|
|||||||
.main-content {
|
.main-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
margin-bottom: 24px;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.left-column {
|
.left-column {
|
||||||
@ -394,7 +401,7 @@
|
|||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: #2D8C8C;
|
border-color: var(--app-primary-color);
|
||||||
background: #f0f9f9;
|
background: #f0f9f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,7 +532,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #2D8C8C !important;
|
color: var(--app-primary-color) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -556,7 +563,7 @@
|
|||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: #2D8C8C;
|
border-color: var(--app-primary-color);
|
||||||
background: #f0f9f9;
|
background: #f0f9f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -332,12 +332,13 @@
|
|||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.price-check-container {
|
.price-check-container {
|
||||||
padding: 16px;
|
width: 100%;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
padding: 16px 16px 24px;
|
||||||
background-color: #f0f3f4;
|
background-color: #f0f3f4;
|
||||||
min-height: 100vh;
|
|
||||||
height: auto;
|
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -428,9 +429,9 @@
|
|||||||
.table-section {
|
.table-section {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 12px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -438,6 +439,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table-toolbar {
|
.table-toolbar {
|
||||||
|
padding: 16px 16px 0;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -447,9 +449,10 @@
|
|||||||
|
|
||||||
.list-wrap {
|
.list-wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
height: 900px;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
padding: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -457,6 +460,14 @@
|
|||||||
touch-action: pan-x pan-y;
|
touch-action: pan-x pan-y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-wrap :deep(.handsontable-pro-container) {
|
||||||
|
width: 100%;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.order-action-link) {
|
:deep(.order-action-link) {
|
||||||
color: var(--app-primary-color);
|
color: var(--app-primary-color);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="organization-table-wrap">
|
||||||
<a-table
|
<a-table
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:data="dataSource"
|
:data="dataSource"
|
||||||
@ -81,6 +82,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑组织弹窗 -->
|
<!-- 新增/编辑组织弹窗 -->
|
||||||
<a-modal
|
<a-modal
|
||||||
@ -350,31 +352,47 @@ async function handleDelete(id: string | undefined) {
|
|||||||
.organization-page {
|
.organization-page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 16px;
|
padding: 16px 16px 24px;
|
||||||
background-color: #f0f3f4;
|
background-color: #f0f3f4;
|
||||||
min-height: 100vh;
|
flex: 1;
|
||||||
height: auto;
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.organization-page-card {
|
.organization-page-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 16px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 16px;
|
padding: 16px 16px 0;
|
||||||
|
margin-bottom: 8px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.organization-table-wrap {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
.action-buttons {
|
.action-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@ -522,20 +522,24 @@ async function loadOrganizations() {
|
|||||||
.user-page {
|
.user-page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 16px;
|
padding: 16px 16px 24px;
|
||||||
background-color: #f0f3f4;
|
background-color: #f0f3f4;
|
||||||
min-height: 100vh;
|
flex: 1;
|
||||||
height: auto;
|
min-height: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-page-card {
|
.user-page-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 16px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -544,16 +548,19 @@ async function loadOrganizations() {
|
|||||||
.page-header {
|
.page-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
padding: 16px 16px 0;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
flex-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-wrap {
|
.list-wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
height: 1200px;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
overscroll-behavior: contain;
|
overscroll-behavior: contain;
|
||||||
touch-action: pan-x pan-y;
|
touch-action: pan-x pan-y;
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="menu-table-wrap">
|
||||||
<a-table
|
<a-table
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:data="dataSource"
|
:data="dataSource"
|
||||||
@ -86,6 +87,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑菜单弹窗 -->
|
<!-- 新增/编辑菜单弹窗 -->
|
||||||
<a-modal
|
<a-modal
|
||||||
@ -394,31 +396,47 @@ function getIcon(iconName: string) {
|
|||||||
.menu-page {
|
.menu-page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 16px;
|
padding: 16px 16px 24px;
|
||||||
background-color: #f0f3f4;
|
background-color: #f0f3f4;
|
||||||
min-height: 100vh;
|
flex: 1;
|
||||||
height: auto;
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-page-card {
|
.menu-page-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 16px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: 16px 16px 0;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-table-wrap {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
.action-buttons {
|
.action-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@ -470,20 +470,24 @@ onMounted(() => {
|
|||||||
.role-page {
|
.role-page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 16px;
|
padding: 16px 16px 24px;
|
||||||
background-color: #f0f3f4;
|
background-color: #f0f3f4;
|
||||||
min-height: 100vh;
|
flex: 1;
|
||||||
height: auto;
|
min-height: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-page-card {
|
.role-page-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 16px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -492,16 +496,19 @@ onMounted(() => {
|
|||||||
.page-header {
|
.page-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
padding: 16px 16px 0;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
flex-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-wrap {
|
.list-wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
height: 1200px;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
overscroll-behavior: contain;
|
overscroll-behavior: contain;
|
||||||
touch-action: pan-x pan-y;
|
touch-action: pan-x pan-y;
|
||||||
|
|||||||
@ -296,24 +296,32 @@ onMounted(() => {
|
|||||||
.warehouse-address-page {
|
.warehouse-address-page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 16px;
|
padding: 16px 16px 24px;
|
||||||
background-color: #f0f3f4;
|
background-color: #f0f3f4;
|
||||||
min-height: 100vh;
|
flex: 1;
|
||||||
height: auto;
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.warehouse-address-page-card {
|
.warehouse-address-page-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 16px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
margin-bottom: 16px;
|
flex-shrink: 0;
|
||||||
|
padding: 16px 16px 0;
|
||||||
|
|
||||||
.page-title {
|
.page-title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -324,6 +332,11 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card-grid {
|
.card-grid {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
|||||||
@ -528,20 +528,24 @@
|
|||||||
.track-content-page {
|
.track-content-page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 16px;
|
padding: 16px 16px 24px;
|
||||||
background-color: #f0f3f4;
|
background-color: #f0f3f4;
|
||||||
min-height: 100vh;
|
flex: 1;
|
||||||
height: auto;
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-content-page-card {
|
.track-content-page-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 16px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -550,21 +554,24 @@
|
|||||||
.search-input {
|
.search-input {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
max-width: 320px;
|
max-width: 320px;
|
||||||
margin-bottom: 16px;
|
margin: 16px 16px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-toolbar {
|
.table-toolbar {
|
||||||
margin-bottom: 8px;
|
padding: 0 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-wrap {
|
.list-wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
height: 1200px;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@ -375,20 +375,24 @@
|
|||||||
.track-flag-page {
|
.track-flag-page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 16px;
|
padding: 16px 16px 24px;
|
||||||
background-color: #f0f3f4;
|
background-color: #f0f3f4;
|
||||||
min-height: 100vh;
|
flex: 1;
|
||||||
height: auto;
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-flag-page-card {
|
.track-flag-page-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 16px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -397,21 +401,24 @@
|
|||||||
.search-input {
|
.search-input {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
max-width: 320px;
|
max-width: 320px;
|
||||||
margin-bottom: 16px;
|
margin: 16px 16px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-toolbar {
|
.table-toolbar {
|
||||||
margin-bottom: 8px;
|
padding: 0 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-wrap {
|
.list-wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
height: 1200px;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@ -2191,12 +2191,12 @@
|
|||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.track-order-container {
|
.track-order-container {
|
||||||
padding: 16px; // 符合设计规范
|
padding: 16px 16px 24px; // 底边 24px 与主内容区对齐
|
||||||
background-color: #f0f3f4; // 与主背景统一
|
background-color: #f0f3f4; // 与主背景统一
|
||||||
min-height: 100vh;
|
flex: 1;
|
||||||
height: auto;
|
min-height: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -2251,18 +2251,17 @@
|
|||||||
/*
|
/*
|
||||||
* 列表滚动相关(Handsontable 实际滚动发生在 .ht_master > .wtHolder):
|
* 列表滚动相关(Handsontable 实际滚动发生在 .ht_master > .wtHolder):
|
||||||
* - 高度:useTableContainerHeight 读取 .list-wrap 的 clientHeight → :height="tableHeight",决定 wtHolder 可视区高度。
|
* - 高度:useTableContainerHeight 读取 .list-wrap 的 clientHeight → :height="tableHeight",决定 wtHolder 可视区高度。
|
||||||
* - 布局:.table-section 固定 1230px + 纵向 flex;.list-wrap flex:1 + min-height:0;HandsontablePro 根节点 flex 填满。
|
* - 布局:.table-section flex:1 + min-height:0;.list-wrap flex:1 + min-height:0 + 四边 16px 内边距;HandsontablePro 根节点 flex 填满。
|
||||||
* - 外层 .track-order-container 为 overflow-y:auto 时,滚轮易冒泡到整页,体感像「拖不动」;对表区域加 overscroll-behavior:contain 优先把滚轮留在表内。
|
* - 外层 .track-order-container 为 overflow-y:auto 时,滚轮易冒泡到整页,体感像「拖不动」;对表区域加 overscroll-behavior:contain 优先把滚轮留在表内。
|
||||||
* - HandsontablePro 内 .ht_master 为 overflow:visible(克隆表头需要),横向/纵向滚动条样式写在 .wtHolder 上。
|
* - HandsontablePro 内 .ht_master 为 overflow:visible(克隆表头需要),横向/纵向滚动条样式写在 .wtHolder 上。
|
||||||
*/
|
*/
|
||||||
.table-section {
|
.table-section {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 12px; // 减少padding,增加内容空间
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: 1230px;
|
flex: 1;
|
||||||
flex: 0 0 auto;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -2273,8 +2272,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.list-wrap {
|
.list-wrap {
|
||||||
|
width: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden; // 仅裁剪外层,纵向/横向滚动交给 Handsontable .wtHolder
|
overflow: hidden; // 仅裁剪外层,纵向/横向滚动交给 Handsontable .wtHolder
|
||||||
@ -3382,10 +3384,12 @@
|
|||||||
|
|
||||||
|
|
||||||
.table-toolbar {
|
.table-toolbar {
|
||||||
margin-bottom: 8px;
|
padding: 16px 16px 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-wrap: nowrap;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
.toolbar-left {
|
.toolbar-left {
|
||||||
|
|||||||
@ -410,23 +410,28 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
/* 参与 page-layout 主内容区 flex,撑满高度;底部仅 24px 留白(列表容器随区域增高,不再出现大块灰底) */
|
||||||
.track-state-page {
|
.track-state-page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 16px;
|
padding: 16px 16px 24px;
|
||||||
background-color: #f0f3f4;
|
background-color: #f0f3f4;
|
||||||
min-height: 100vh;
|
flex: 1;
|
||||||
height: auto;
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-state-page-card {
|
.track-state-page-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 16px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -435,21 +440,24 @@
|
|||||||
.search-input {
|
.search-input {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
max-width: 320px;
|
max-width: 320px;
|
||||||
margin-bottom: 16px;
|
margin: 16px 16px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-toolbar {
|
.table-toolbar {
|
||||||
margin-bottom: 8px;
|
padding: 0 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-wrap {
|
.list-wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
height: 1200px;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@ -513,23 +513,24 @@ const handsontableRef = ref<InstanceType<typeof HandsontablePro>>()
|
|||||||
.customer-page {
|
.customer-page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 16px;
|
padding: 16px 16px 24px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: #f0f3f4;
|
background: #f0f3f4;
|
||||||
min-height: 100vh;
|
flex: 1;
|
||||||
height: auto;
|
min-height: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customer-page-card {
|
.customer-page-card {
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 16px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -539,8 +540,10 @@ const handsontableRef = ref<InstanceType<typeof HandsontablePro>>()
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 12px;
|
padding: 16px 16px 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
.toolbar-left {
|
.toolbar-left {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -559,9 +562,10 @@ const handsontableRef = ref<InstanceType<typeof HandsontablePro>>()
|
|||||||
.list-wrap {
|
.list-wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
height: 1200px;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@ -234,23 +234,24 @@ function handleViewPayImageClick(event: MouseEvent) {
|
|||||||
.recharge-record-page {
|
.recharge-record-page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 16px;
|
padding: 16px 16px 24px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: #f0f3f4;
|
background: #f0f3f4;
|
||||||
min-height: 100vh;
|
flex: 1;
|
||||||
height: auto;
|
min-height: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.recharge-record-page-card {
|
.recharge-record-page-card {
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 16px;
|
padding: 0;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -260,8 +261,10 @@ function handleViewPayImageClick(event: MouseEvent) {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 12px;
|
padding: 16px 16px 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
.toolbar-right {
|
.toolbar-right {
|
||||||
.pagination-controls {
|
.pagination-controls {
|
||||||
@ -274,9 +277,10 @@ function handleViewPayImageClick(event: MouseEvent) {
|
|||||||
.list-wrap {
|
.list-wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
flex: 0 0 auto;
|
flex: 1;
|
||||||
height: 1200px;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user