fix bug
This commit is contained in:
108
AGENTS.md
108
AGENTS.md
@@ -1,9 +1,11 @@
|
||||
# AGENTS.md
|
||||
|
||||
本文件用于指导 Codex (Codex.ai/code) 在 Saber3 代码仓库中工作时的行为规范。
|
||||
|
||||
> 本规范适用于 Saber3 前端工程的所有开发任务,为强制性条款。除非用户显式豁免,任何条目都不得忽视或删减。
|
||||
>
|
||||
> 作为 AI 助手参与本项目开发时,你必须:
|
||||
>
|
||||
> - 每次输出前深度理解 BladeX 微服务架构体系、Saber3 前端工程结构和 Vue 3 技术栈特征
|
||||
> - 当回答依赖外部知识时,先查询 Vue 3、Element Plus、Avue、Vite 等官方文档
|
||||
> - 若需求含糊,先复述已知信息并列出关键澄清问题
|
||||
@@ -20,18 +22,18 @@
|
||||
|
||||
### 1.1 技术栈
|
||||
|
||||
| 技术 | 版本 | 用途 |
|
||||
| ------------------ | -------- | ------------------ |
|
||||
| Vue | ^3.5.13 | 核心框架 |
|
||||
| Element Plus | ^2.10.1 | UI 组件库 |
|
||||
| @smallwei/avue | ^3.7.2 | 增强型 CRUD 组件库 |
|
||||
| Vue Router | ^4.3.2 | 路由管理 |
|
||||
| Vuex | ^4.1.0 | 状态管理 |
|
||||
| Axios | ^1.8.3 | HTTP 客户端 |
|
||||
| Vite | ^5.4.19 | 构建工具 |
|
||||
| vue-i18n | ^11.1.3 | 国际化 |
|
||||
| Sass | ^1.85.1 | CSS 预处理器 |
|
||||
| crypto-js / sm-crypto | - | 加密(AES / SM2) |
|
||||
| 技术 | 版本 | 用途 |
|
||||
| --------------------- | ------- | ------------------ |
|
||||
| Vue | ^3.5.13 | 核心框架 |
|
||||
| Element Plus | ^2.10.1 | UI 组件库 |
|
||||
| @smallwei/avue | ^3.7.2 | 增强型 CRUD 组件库 |
|
||||
| Vue Router | ^4.3.2 | 路由管理 |
|
||||
| Vuex | ^4.1.0 | 状态管理 |
|
||||
| Axios | ^1.8.3 | HTTP 客户端 |
|
||||
| Vite | ^5.4.19 | 构建工具 |
|
||||
| vue-i18n | ^11.1.3 | 国际化 |
|
||||
| Sass | ^1.85.1 | CSS 预处理器 |
|
||||
| crypto-js / sm-crypto | - | 加密(AES / SM2) |
|
||||
|
||||
---
|
||||
|
||||
@@ -91,27 +93,36 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
||||
- 命名约定:列表 `getList(current, size, params)`、详情 `getDetail(id)`、新增 `add(row)`、更新 `update(row)`、删除 `remove(ids)`、树形 `getXxxTree()`
|
||||
- 后端微服务前缀:`/blade-system/`、`/blade-resource/`、`/blade-flow/`、`/blade-desk/`、`/blade-log/`、`/blade-develop/`
|
||||
|
||||
### 3.4 认证机制
|
||||
### 3.4 批量导入失败文件下载规范
|
||||
|
||||
- 所有批量导入功能如果存在部分数据无法导入、校验失败或后端处理异常,前端必须支持自动下载后端返回的导入失败 Excel。
|
||||
- 失败 Excel 的字段顺序必须与原导入模板保持一致,并在最后一列追加“导入失败原因”。
|
||||
- 前端上传接口需支持 `blob` 响应;Avue 默认上传无法可靠处理文件流时,应使用 `httpRequest` 或独立 API 方法自定义上传。
|
||||
- 前端需根据响应 `content-type` 或 Blob 类型区分 JSON 成功响应与 Excel 文件流:成功时提示并刷新列表,失败文件流时下载失败明细并提示“部分数据导入失败,已下载失败明细”。
|
||||
- 批量导入失败明细文件命名统一包含业务名称、`导入失败明细` 和时间戳,例如 `货物类型导入失败明细YYYY-MM-DD HH:mm:ss.xlsx`。
|
||||
- 批量导入的前端预校验与后端导入校验必须与新增、编辑表单校验保持一致,包括必填、长度、格式、枚举范围、父子级联关系和金额/日期等业务规则;新增、编辑校验调整时,必须同步更新导入校验。
|
||||
|
||||
### 3.5 认证机制
|
||||
|
||||
- OAuth2:`Basic` 头传递 `clientId:clientSecret`(Base64 编码)
|
||||
- Token:请求头 `Blade-Auth: bearer {token}`,支持 AES 加密模式
|
||||
- 存储:`saber3-access-token` / `saber3-refresh-token`(通过 `utils/auth.js` 管理)
|
||||
- 401 自动刷新 Token,并发请求排队等待;登录密码使用 SM2 国密加密
|
||||
|
||||
### 3.5 路由系统
|
||||
### 3.6 路由系统
|
||||
|
||||
- 静态路由:`router/page/` + `router/views/`
|
||||
- 动态路由:`avue-router.js` 将后端菜单数据转换为 Vue Router 路由
|
||||
- 多级路由自动扁平化为二级,支持 keep-alive 跨层级缓存
|
||||
- 外部链接自动转换为 iframe 路由,支持 Token 透传
|
||||
|
||||
### 3.6 权限控制
|
||||
### 3.7 权限控制
|
||||
|
||||
- 路由守卫:`permission.js` 控制登录态、锁屏、标签页
|
||||
- 按钮权限:`store.getters.permission`,格式 `{module}_{action}`(如 `dict_add`)
|
||||
- 管理员判断:`userInfo.authority.includes('admin')`
|
||||
|
||||
### 3.7 多租户
|
||||
### 3.8 多租户
|
||||
|
||||
通过 `website.tenantMode` 控制开关,管理组租户编号 `000000`,后端自动通过请求头传递租户信息。
|
||||
|
||||
@@ -124,6 +135,7 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
||||
项目同时支持 **Options API**(主流,绝大多数现有页面)和 **Composition API**(新组件可选)。
|
||||
|
||||
**选择原则**:
|
||||
|
||||
- 修改现有页面:保持该页面原有风格,不混用
|
||||
- 新建 CRUD 页面:调用 `avue-design` Skill 生成,或参考现有页面手动编写
|
||||
- 新建复杂交互页面:可使用 Composition API + `<script setup>`
|
||||
@@ -131,14 +143,14 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
||||
|
||||
### 4.2 命名规范
|
||||
|
||||
| 类型 | 命名方式 | 示例 |
|
||||
| -------------- | -------------------------- | ----------------------------- |
|
||||
| 页面文件 | kebab-case | `notice.vue` |
|
||||
| 组件文件 | kebab-case 目录 + main.vue | `basic-container/main.vue` |
|
||||
| API / Option / 工具文件 | camelCase | `dict.js`、`dictbiz.js` |
|
||||
| 变量 / 函数 | camelCase | `dictValue`、`handleDelete` |
|
||||
| Vuex mutations | UPPER_SNAKE | `SET_IS_MENU`、`ADD_TAG` |
|
||||
| Vuex actions | PascalCase | `FedLogOut`、`RefreshToken` |
|
||||
| 类型 | 命名方式 | 示例 |
|
||||
| ----------------------- | -------------------------- | --------------------------- |
|
||||
| 页面文件 | kebab-case | `notice.vue` |
|
||||
| 组件文件 | kebab-case 目录 + main.vue | `basic-container/main.vue` |
|
||||
| API / Option / 工具文件 | camelCase | `dict.js`、`dictbiz.js` |
|
||||
| 变量 / 函数 | camelCase | `dictValue`、`handleDelete` |
|
||||
| Vuex mutations | UPPER_SNAKE | `SET_IS_MENU`、`ADD_TAG` |
|
||||
| Vuex actions | PascalCase | `FedLogOut`、`RefreshToken` |
|
||||
|
||||
### 4.3 代码格式(Prettier)
|
||||
|
||||
@@ -148,6 +160,21 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
||||
|
||||
- 全局 SCSS 变量通过 `styles/variables.scss` 定义,Vite 自动注入所有组件
|
||||
- 编写样式优先使用已有变量和 mixin(`styles/mixin.scss`),而非硬编码值
|
||||
- 系统主色调统一使用 `#409eff`,涉及主题变量、Element Plus 主题覆盖、按钮/链接/选中态等品牌色场景均应保持一致
|
||||
- 各页面搜索栏多行展示时,行与行之间的垂直间距统一为 `8px`
|
||||
- 搜索组件 label 宽度统一不小于 `160px`,label 文本不得换行。
|
||||
- 搜索组件内部 padding 统一为上、左右 `12px`,底部 `4px`。
|
||||
- 搜索组件需展示轻量阴影,统一使用 `0 2px 8px rgba(0, 0, 0, 0.06)`,不得被局部卡片去样式规则覆盖。
|
||||
- 表格线条颜色需统一使用 `#EFF1F7`,包括表格外边框、单元格分割线和固定列边线。
|
||||
- 表格偶数行需统一使用 `#FAFAFA` 背景色,固定列单元格必须与对应行背景保持一致。
|
||||
- 表格操作列同时展示“查看、编辑、删除”等三个按钮时,操作列宽度统一不小于 `220px`,禁止出现按钮裁切或显示不全。
|
||||
- `.avue-crud__header` 顶部间距统一为 `12px`。
|
||||
- 分页组件整体靠右展示,必须展示接口返回的数据总条数,`X条/页` 的页容量选择器必须放在总条数右侧。
|
||||
- `.basic-container__card` 的直接子级 `.el-card__body` 内边距统一去除,保持 `padding: 0`
|
||||
- 所有 CRUD 页面搜索栏需与下方表格拆分为独立区域,两者垂直间距统一为 `8px`。
|
||||
- 所有 CRUD 页面搜索栏操作按钮(搜索、清空等)需在搜索表单下方独占一行并放置在最右侧;搜索条件超过两行时必须显示“展开/折叠”,默认折叠且仅展示两行搜索条件。
|
||||
- 所有 CRUD 页面中新增、批量导入等顶部按钮组行不需要背景色,应保持透明背景。
|
||||
- 所有 `.basic-container__card` 内的 Avue 内层 `el-card` 需去卡片化,仅保留外层容器卡片样式,禁止出现二级卡片边框、阴影或额外内边距。
|
||||
|
||||
### 4.5 全局注册组件
|
||||
|
||||
@@ -165,8 +192,15 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
||||
|
||||
### 4.8 表单布局规范
|
||||
|
||||
- 新增/编辑弹窗中的表单项,若 `label` 文案超过 4 个字,`label` 必须单独占一行展示,表单控件另起一行,避免长标签挤压输入控件或换行错位。
|
||||
- 使用 Avue 配置、Element Plus 表单或自定义弹窗表单时均需遵守该规则;可通过 `label-position="top"`、自定义 `formslot`、局部样式或单项布局调整实现。
|
||||
- 新增、编辑弹窗中的表单 `label` 默认必须位于控件左侧,Avue 配置统一使用 `labelPosition: 'right'` 与 `labelWidth: 'auto'`,Element Plus 表单统一使用 `label-position="right"` 与 `label-width="auto"`。
|
||||
- 新增、编辑弹窗中的表单 `label` 宽度必须以当前视觉列内最长 `label` 为准;多列表单需按列分别计算 label 宽度,禁止用整张表单的最长 label 统一挤压所有列。
|
||||
- 所有新增、编辑弹窗中的表单 `label` 文本必须右对齐,确保同一列内控件起始位置一致。
|
||||
- 新增、编辑弹窗标题左侧必须展示 `4px` 宽的主色竖条,竖条与标题文本间距统一为 `8px`。
|
||||
- 复杂布局弹窗中的自定义分区标题、明细表格标题、步骤区标题等承担弹窗内容标题作用时,也必须沿用 `4px` 主色竖条与 `8px` 标题间距;可使用全局 `.dialog-section-title` 或等效局部样式实现。
|
||||
- 新增、编辑弹窗中的表单项上下间距统一为 `16px`,弹窗内表单布局不得叠加 `.el-col` 与 `.el-form-item` 的额外下边距。
|
||||
- 所有新增、编辑弹窗中的备注字段(如果有)必须单独占一整行展示,禁止与其他字段并列;Avue 配置可使用 `span: 24` 或等效布局实现。
|
||||
- 新增、编辑弹窗底部按钮顺序统一为“取消、提交”,取消按钮在左,提交按钮在右。
|
||||
- 使用 Avue 配置、Element Plus 表单或自定义弹窗表单时均需遵守该规则;若局部组件因特殊结构无法自动对齐,应通过 `src/utils/dialog-form-label.js` 的全局增强、局部样式或 `formslot` 保证 `label` 左侧展示、按列统一宽度、文本右对齐。
|
||||
|
||||
### 4.9 审计字段展示规范
|
||||
|
||||
@@ -175,6 +209,10 @@ Avue 表格/表单配置独立存放于 `src/option/` 目录,与 `views` 和 `
|
||||
- “更新人”列统一使用 `prop: 'updateUserName'`,并通过 `src/utils/audit.js` 中的 `formatUpdateUserName(row)` 做兼容兜底。
|
||||
- 表单提交仍保留后端审计字段机制,前端不得手动提交或覆盖 `createUser`、`updateUser`。
|
||||
|
||||
### 4.10 表格排序规范
|
||||
|
||||
- 表格的排序默认按照创建时间降序;分页列表应由后端默认按 `create_time DESC` 返回,前端不得在无明确业务要求时覆盖为其它默认排序。
|
||||
|
||||
---
|
||||
|
||||
## 5. 新功能开发流程
|
||||
@@ -225,17 +263,17 @@ pnpm run serve # 预览构建产物
|
||||
|
||||
项目采用 **Gitmoji** 风格,中文描述:
|
||||
|
||||
| Emoji | 代码 | 场景 |
|
||||
| ---------- | ------------ | ---------------- |
|
||||
| Emoji | 代码 | 场景 |
|
||||
| ---------- | ------------ | ------------------ |
|
||||
| :sparkles: | `:sparkles:` | 新增功能、优化增强 |
|
||||
| :bug: | `:bug:` | 修复 Bug |
|
||||
| :bug: | `:bug:` | 修复 Bug |
|
||||
| :zap: | `:zap:` | 性能优化、问题修复 |
|
||||
| :tada: | `:tada:` | 重大版本发布 |
|
||||
| :lipstick: | `:lipstick:` | 样式调整 |
|
||||
| :recycle: | `:recycle:` | 代码重构 |
|
||||
| :wrench: | `:wrench:` | 配置修改 |
|
||||
| :memo: | `:memo:` | 文档更新 |
|
||||
| :fire: | `:fire:` | 删除代码/文件 |
|
||||
| :tada: | `:tada:` | 重大版本发布 |
|
||||
| :lipstick: | `:lipstick:` | 样式调整 |
|
||||
| :recycle: | `:recycle:` | 代码重构 |
|
||||
| :wrench: | `:wrench:` | 配置修改 |
|
||||
| :memo: | `:memo:` | 文档更新 |
|
||||
| :fire: | `:fire:` | 删除代码/文件 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -13,14 +13,13 @@
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
<link rel="stylesheet" href="/iconfont/index.css" />
|
||||
<link rel="stylesheet" href="/iconfont/avue/iconfont.css" />
|
||||
<link rel="stylesheet" href="/iconfont/saber/iconfont.css" />
|
||||
<link rel="stylesheet" href="/iconfont/common/iconfont.css" />
|
||||
<link rel="stylesheet" href="/css/loading.css" />
|
||||
<link rel="stylesheet" href="/css/saber.css" />
|
||||
<title>TMS物流运输管理系统</title>
|
||||
<title>运输板块数字化管理平台</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
"dependencies": {
|
||||
"element-plus": "^2.14.2",
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@open-file-viewer/core": "^0.1.27",
|
||||
"@open-file-viewer/vue": "^0.1.27",
|
||||
"@smallwei/avue": "~3.9.2",
|
||||
"avue-plugin-ueditor": "^1.0.4",
|
||||
"@saber/nf-design-base-elp": "^1.3.0",
|
||||
@@ -27,6 +29,7 @@
|
||||
"js-cookie": "^3.0.0",
|
||||
"js-md5": "^0.7.3",
|
||||
"nprogress": "^0.2.0",
|
||||
"pdfjs-dist": "^6.1.200",
|
||||
"sm-crypto": "^0.4.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-i18n": "^11.4.2",
|
||||
|
||||
701
pnpm-lock.yaml
generated
701
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 88 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 113 KiB |
73
src/api/base/cargo-type.js
Normal file
73
src/api/base/cargo-type.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import request from '@/axios';
|
||||
|
||||
export const getList = (current, size, params) => {
|
||||
return request({
|
||||
url: '/blade-system/cargo-type/list',
|
||||
method: 'get',
|
||||
params: {
|
||||
...params,
|
||||
current,
|
||||
size,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const getDetail = id => {
|
||||
return request({
|
||||
url: '/blade-system/cargo-type/detail',
|
||||
method: 'get',
|
||||
params: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const remove = ids => {
|
||||
return request({
|
||||
url: '/blade-system/cargo-type/remove',
|
||||
method: 'post',
|
||||
params: {
|
||||
ids,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const submit = row => {
|
||||
return request({
|
||||
url: '/blade-system/cargo-type/submit',
|
||||
method: 'post',
|
||||
data: row,
|
||||
});
|
||||
};
|
||||
|
||||
export const importCargoType = file => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
return request({
|
||||
url: '/blade-system/cargo-type/import-cargo-type',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
responseType: 'blob',
|
||||
timeout: 60000,
|
||||
});
|
||||
};
|
||||
|
||||
export const getParentOptions = keyword => {
|
||||
return request({
|
||||
url: '/blade-system/cargo-type/parent-options',
|
||||
method: 'get',
|
||||
params: {
|
||||
keyword,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const getNextCode = parentCargoCode => {
|
||||
return request({
|
||||
url: '/blade-system/cargo-type/next-code',
|
||||
method: 'get',
|
||||
params: {
|
||||
parentCargoCode,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -13,3 +13,15 @@ export const exportBlob = (url, params) => {
|
||||
responseType: 'blob',
|
||||
});
|
||||
};
|
||||
|
||||
export const importBlob = (url, file) => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data: formData,
|
||||
responseType: 'blob',
|
||||
timeout: 60000,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -48,6 +48,11 @@ export default {
|
||||
|
||||
&__card {
|
||||
width: 100%;
|
||||
background: none;
|
||||
|
||||
> .el-card__body {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div class="cron-config-area">
|
||||
<!-- 每天模式 -->
|
||||
<div v-if="cronMode === 'daily'" class="mode-content">
|
||||
<el-form :model="dailyConfig" label-width="100px" size="default">
|
||||
<el-form :model="dailyConfig" label-width="auto" size="default">
|
||||
<el-form-item label="执行时间">
|
||||
<el-time-picker
|
||||
v-model="dailyConfig.time"
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<!-- 每周模式 -->
|
||||
<div v-if="cronMode === 'weekly'" class="mode-content">
|
||||
<el-form :model="weeklyConfig" label-width="100px" size="default">
|
||||
<el-form :model="weeklyConfig" label-width="auto" size="default">
|
||||
<el-form-item label="星期选择">
|
||||
<el-checkbox-group v-model="weeklyConfig.weekDays" @change="generateCron" :disabled="disabled">
|
||||
<el-checkbox label="1">周一</el-checkbox>
|
||||
@@ -58,7 +58,7 @@
|
||||
|
||||
<!-- 每月模式 -->
|
||||
<div v-if="cronMode === 'monthly'" class="mode-content">
|
||||
<el-form :model="monthlyConfig" label-width="100px" size="default">
|
||||
<el-form :model="monthlyConfig" label-width="auto" size="default">
|
||||
<el-form-item label="日期选择">
|
||||
<el-select
|
||||
v-model="monthlyConfig.days"
|
||||
@@ -93,7 +93,7 @@
|
||||
|
||||
<!-- 自定义模式 -->
|
||||
<div v-if="cronMode === 'custom'" class="mode-content">
|
||||
<el-form :model="customConfig" label-width="100px" size="default">
|
||||
<el-form :model="customConfig" label-width="auto" size="default">
|
||||
<el-form-item label="秒">
|
||||
<el-input
|
||||
v-model="customConfig.second"
|
||||
@@ -161,7 +161,7 @@
|
||||
</div>
|
||||
|
||||
<div class="cron-result">
|
||||
<el-form label-width="120px">
|
||||
<el-form label-width="auto">
|
||||
<el-form-item label="Cron 表达式">
|
||||
<el-input
|
||||
v-model="cronExpression"
|
||||
@@ -567,7 +567,7 @@ export default {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
:deep(.el-checkbox) {
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<el-pagination
|
||||
background
|
||||
:current-page="page.currentPage"
|
||||
:page-size="page.pageSize"
|
||||
:page-size="pageSize"
|
||||
:page-sizes="pageSizes"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="0"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
@@ -27,9 +27,15 @@ export default {
|
||||
isEmptyTotal() {
|
||||
return Number(this.page.total || 0) === 0;
|
||||
},
|
||||
pageSize() {
|
||||
return this.page.pageSize || 10;
|
||||
},
|
||||
pageSizes() {
|
||||
return this.page.pageSizes || [10, 20, 50, 100];
|
||||
},
|
||||
total() {
|
||||
return Number(this.page.total || 0);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleSizeChange(pageSize) {
|
||||
@@ -51,7 +57,6 @@ export default {
|
||||
.empty-pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 20px 0 0;
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
:show-close="false"
|
||||
width="20%"
|
||||
>
|
||||
<el-form :model="form" ref="form" label-width="80px">
|
||||
<el-form :model="form" ref="form" label-width="auto">
|
||||
<el-form-item v-if="tenantMode" label="租户编号">
|
||||
<el-input v-model="form.tenantId" placeholder="请输入租户编号"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
364
src/components/vehicle-attachment-upload/main.vue
Normal file
364
src/components/vehicle-attachment-upload/main.vue
Normal file
@@ -0,0 +1,364 @@
|
||||
<template>
|
||||
<div class="vehicle-attachment-upload">
|
||||
<el-upload
|
||||
:action="uploadAction"
|
||||
name="file"
|
||||
:headers="uploadHeaders"
|
||||
:file-list="fileList"
|
||||
:accept="acceptText"
|
||||
:multiple="multiple"
|
||||
:limit="limit"
|
||||
:disabled="readonly"
|
||||
:show-file-list="true"
|
||||
:on-success="handleSuccess"
|
||||
:on-error="handleError"
|
||||
:on-remove="handleRemove"
|
||||
:on-preview="handlePreview"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<el-button v-if="!readonly" type="primary" plain :icon="buttonIcon">
|
||||
{{ buttonText }}
|
||||
</el-button>
|
||||
<template #tip>
|
||||
<div v-if="showTip && tipText && !readonly" class="el-upload__tip">
|
||||
{{ tipText }}
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<span v-if="readonly && fileList.length === 0" class="vehicle-attachment-upload__empty">
|
||||
-
|
||||
</span>
|
||||
|
||||
<el-dialog
|
||||
v-model="previewVisible"
|
||||
:title="previewFile.name || '附件预览'"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
width="90%"
|
||||
top="4vh"
|
||||
class="vehicle-attachment-upload__viewer-dialog"
|
||||
>
|
||||
<open-file-viewer
|
||||
v-if="previewVisible && previewSource"
|
||||
:file="previewSource"
|
||||
:file-name="previewFile.name"
|
||||
:mime-type="previewFile.mimeType"
|
||||
width="100%"
|
||||
height="72vh"
|
||||
fit="contain"
|
||||
theme="auto"
|
||||
locale="zh-CN"
|
||||
:toolbar="viewerToolbar"
|
||||
:plugins="viewerPlugins"
|
||||
@unsupported="handleUnsupported"
|
||||
@error="handleViewerError"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { OpenFileViewer } from '@open-file-viewer/vue';
|
||||
import {
|
||||
fallbackPlugin,
|
||||
imagePlugin,
|
||||
officePlugin,
|
||||
pdfPlugin,
|
||||
textPlugin,
|
||||
} from '@open-file-viewer/core';
|
||||
import '@open-file-viewer/core/style.css';
|
||||
import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
|
||||
import { baseUrl } from '@/config/env';
|
||||
import website from '@/config/website';
|
||||
import { getToken } from '@/utils/auth';
|
||||
|
||||
const MIME_MAP = {
|
||||
jpg: 'image/jpeg',
|
||||
jpeg: 'image/jpeg',
|
||||
png: 'image/png',
|
||||
gif: 'image/gif',
|
||||
bmp: 'image/bmp',
|
||||
webp: 'image/webp',
|
||||
pdf: 'application/pdf',
|
||||
doc: 'application/msword',
|
||||
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
xls: 'application/vnd.ms-excel',
|
||||
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
ppt: 'application/vnd.ms-powerpoint',
|
||||
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
txt: 'text/plain',
|
||||
};
|
||||
|
||||
const viewerPlugins = [
|
||||
imagePlugin(),
|
||||
pdfPlugin({
|
||||
workerSrc: pdfWorkerSrc,
|
||||
useFetchData: true,
|
||||
}),
|
||||
officePlugin({
|
||||
pdf: {
|
||||
workerSrc: pdfWorkerSrc,
|
||||
useFetchData: true,
|
||||
},
|
||||
}),
|
||||
textPlugin(),
|
||||
fallbackPlugin(),
|
||||
];
|
||||
|
||||
export default {
|
||||
name: 'VehicleAttachmentUpload',
|
||||
components: {
|
||||
OpenFileViewer,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [Array, String, Object],
|
||||
default: () => [],
|
||||
},
|
||||
action: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
accept: {
|
||||
type: [String, Array],
|
||||
default: '',
|
||||
},
|
||||
fileTypes: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 20,
|
||||
},
|
||||
maxSize: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
buttonText: {
|
||||
type: String,
|
||||
default: '上传附件',
|
||||
},
|
||||
buttonIcon: {
|
||||
type: String,
|
||||
default: 'el-icon-upload',
|
||||
},
|
||||
tip: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
showTip: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue', 'change', 'success'],
|
||||
data() {
|
||||
return {
|
||||
fileList: [],
|
||||
previewVisible: false,
|
||||
previewFile: {},
|
||||
previewSource: '',
|
||||
viewerToolbar: {
|
||||
download: true,
|
||||
fullscreen: true,
|
||||
print: true,
|
||||
rotate: true,
|
||||
zoom: true,
|
||||
},
|
||||
viewerPlugins,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
uploadAction() {
|
||||
return this.action || `${baseUrl}/blade-resource/oss/endpoint/put-file`;
|
||||
},
|
||||
uploadHeaders() {
|
||||
return {
|
||||
[website.tokenHeader]: getToken(),
|
||||
};
|
||||
},
|
||||
acceptedList() {
|
||||
const types = this.fileTypes.length ? this.fileTypes : this.accept;
|
||||
if (Array.isArray(types)) {
|
||||
return types.map(item => String(item).trim()).filter(Boolean);
|
||||
}
|
||||
return String(types || '')
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean);
|
||||
},
|
||||
acceptText() {
|
||||
return this.acceptedList.join(',');
|
||||
},
|
||||
tipText() {
|
||||
if (this.tip) return this.tip;
|
||||
const typeText = this.acceptText || '所有类型';
|
||||
const sizeText = this.maxSize > 0 ? `,单文件不超过 ${this.maxSize}MB` : '';
|
||||
return `支持上传 ${typeText}${sizeText}`;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
modelValue: {
|
||||
handler(value) {
|
||||
this.fileList = this.toUploadList(value);
|
||||
},
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toUploadList(value) {
|
||||
const list = this.parseValue(value);
|
||||
return list.map((item, index) => {
|
||||
const url = this.getFileUrl(item);
|
||||
return {
|
||||
...item,
|
||||
uid: item.uid || `${url || item.name || 'file'}-${index}`,
|
||||
name: item.name || this.getFileName(url) || '附件',
|
||||
url,
|
||||
status: 'success',
|
||||
};
|
||||
});
|
||||
},
|
||||
parseValue(value) {
|
||||
if (!value) return [];
|
||||
if (Array.isArray(value)) return value.filter(Boolean);
|
||||
if (typeof value === 'object') return [value];
|
||||
try {
|
||||
const list = JSON.parse(value);
|
||||
return Array.isArray(list) ? list : [];
|
||||
} catch (error) {
|
||||
return String(value)
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean)
|
||||
.map(item => ({
|
||||
name: this.getFileName(item),
|
||||
url: item,
|
||||
}));
|
||||
}
|
||||
},
|
||||
getFileUrl(file) {
|
||||
return file.url || file.link || file.src || file.domain || '';
|
||||
},
|
||||
getFileName(url) {
|
||||
if (!url) return '';
|
||||
const path = String(url).split('?')[0];
|
||||
return decodeURIComponent(path.substring(path.lastIndexOf('/') + 1));
|
||||
},
|
||||
getExtension(file) {
|
||||
const name = String(file.name || this.getFileUrl(file) || '').split('?')[0];
|
||||
const index = name.lastIndexOf('.');
|
||||
return index >= 0 ? name.substring(index + 1).toLowerCase() : '';
|
||||
},
|
||||
beforeUpload(file) {
|
||||
if (!this.validFileType(file)) {
|
||||
this.$message.warning('文件类型不符合上传要求');
|
||||
return false;
|
||||
}
|
||||
if (this.maxSize > 0 && file.size / 1024 / 1024 > this.maxSize) {
|
||||
this.$message.warning(`单文件大小不能超过 ${this.maxSize}MB`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
validFileType(file) {
|
||||
if (!this.acceptedList.length) return true;
|
||||
const fileName = String(file.name || '').toLowerCase();
|
||||
const mimeType = String(file.type || '').toLowerCase();
|
||||
return this.acceptedList.some(type => {
|
||||
const value = String(type).toLowerCase();
|
||||
if (value === '*/*') return true;
|
||||
if (value.startsWith('.')) return fileName.endsWith(value);
|
||||
if (value.endsWith('/*')) return mimeType.startsWith(value.slice(0, -1));
|
||||
if (value.includes('/')) return mimeType === value;
|
||||
return fileName.endsWith(`.${value}`);
|
||||
});
|
||||
},
|
||||
handleSuccess(response, file, files) {
|
||||
if (!response || response.success === false || (response.code && response.code !== 200)) {
|
||||
this.$message.error((response && response.msg) || '上传失败');
|
||||
return;
|
||||
}
|
||||
this.emitUploadFiles(this.multiple ? files : [file]);
|
||||
this.$emit('success', this.normalizeUploadFile(file));
|
||||
this.$message.success('上传成功');
|
||||
},
|
||||
handleError() {
|
||||
this.$message.error('上传失败');
|
||||
},
|
||||
handleRemove(file, files) {
|
||||
if (this.readonly) return false;
|
||||
this.emitUploadFiles(files);
|
||||
return true;
|
||||
},
|
||||
emitUploadFiles(files) {
|
||||
const list = files
|
||||
.filter(file => file.status === 'success' || this.getFileUrl(file))
|
||||
.map(this.normalizeUploadFile)
|
||||
.filter(item => item.url);
|
||||
this.$emit('update:modelValue', list);
|
||||
this.$emit('change', list);
|
||||
},
|
||||
normalizeUploadFile(file) {
|
||||
const data = (file.response && file.response.data) || file.data || file;
|
||||
const url = data.link || data.url || data.domain || file.url || '';
|
||||
const name = data.name || data.originalName || file.name || this.getFileName(url) || '附件';
|
||||
const extension = this.getExtension({ name, url });
|
||||
return {
|
||||
...data,
|
||||
uid: file.uid || data.uid,
|
||||
name,
|
||||
url,
|
||||
link: data.link || url,
|
||||
size: data.size || data.attachSize || file.size || '',
|
||||
extension,
|
||||
mimeType: data.mimeType || data.contentType || file.raw?.type || MIME_MAP[extension] || '',
|
||||
};
|
||||
},
|
||||
handlePreview(file) {
|
||||
const previewFile = this.normalizeUploadFile(file);
|
||||
if (!previewFile.url) {
|
||||
this.$message.warning('附件地址为空,无法预览');
|
||||
return;
|
||||
}
|
||||
this.previewFile = previewFile;
|
||||
this.previewSource = previewFile.url;
|
||||
this.previewVisible = true;
|
||||
},
|
||||
handleUnsupported() {
|
||||
this.$message.warning('当前文件暂不支持在线预览');
|
||||
},
|
||||
handleViewerError() {
|
||||
this.$message.error('附件预览失败');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.vehicle-attachment-upload {
|
||||
width: 100%;
|
||||
|
||||
:deep(.el-upload-list__item-name) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__empty {
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.vehicle-attachment-upload__viewer-dialog .el-dialog__body) {
|
||||
padding: 12px 16px 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -5,7 +5,7 @@ export default {
|
||||
title: '运输板块数字化管理平台',
|
||||
logo: 'X',
|
||||
key: 'saber', //配置主键,目前用于存储
|
||||
indexTitle: 'TMS物流运输管理系统',
|
||||
indexTitle: '运输板块数字化管理平台',
|
||||
clientId: 'saber3', // 客户端id
|
||||
clientSecret: 'saber3_secret', // 客户端密钥
|
||||
tenantMode: true, // 是否开启租户模式
|
||||
@@ -53,7 +53,7 @@ export default {
|
||||
//水印配置
|
||||
watermark: {
|
||||
mode: false,
|
||||
text: 'TMS物流运输管理系统',
|
||||
text: '运输板块数字化管理平台',
|
||||
},
|
||||
//oauth2配置
|
||||
oauth2: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export default {
|
||||
title: 'TMS物流运输管理系统',
|
||||
title: '运输板块数字化管理平台',
|
||||
confirmTip: '提示',
|
||||
logoutTip: '退出系统, 是否继续?',
|
||||
submitText: '确定',
|
||||
@@ -79,7 +79,7 @@ export default {
|
||||
},
|
||||
login: {
|
||||
title: '登录 ',
|
||||
info: 'TMS物流运输管理系统',
|
||||
info: '运输板块数字化管理平台',
|
||||
tenantId: '请输入租户ID',
|
||||
name: '请输入姓名',
|
||||
username: '请输入账号',
|
||||
|
||||
16
src/main.js
16
src/main.js
@@ -13,6 +13,8 @@ import '@smallwei/avue/lib/index.css';
|
||||
import avueUeditor from 'avue-plugin-ueditor';
|
||||
import crudCommon from '@/mixins/crud.js';
|
||||
import { getScreen, findColumn } from './utils/util';
|
||||
import { setupPaginationEnhancer } from './utils/pagination';
|
||||
import { setupDialogFormLabelAlign } from './utils/dialog-form-label';
|
||||
import './permission';
|
||||
import error from './error';
|
||||
import App from './App.vue';
|
||||
@@ -28,6 +30,7 @@ import cronEditor from './components/cron-editor/main.vue';
|
||||
import basicBlock from './components/basic-block/main.vue';
|
||||
import basicContainer from './components/basic-container/main.vue';
|
||||
import emptyPagination from './components/empty-pagination/main.vue';
|
||||
import vehicleAttachmentUpload from './components/vehicle-attachment-upload/main.vue';
|
||||
import thirdRegister from './components/third-register/main.vue';
|
||||
import flowDesign from './components/flow-design/main.vue';
|
||||
import flowDesignStep from './components/flow-design-step/main.vue';
|
||||
@@ -47,6 +50,7 @@ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
app.component('basicContainer', basicContainer);
|
||||
app.component('basicBlock', basicBlock);
|
||||
app.component('emptyPagination', emptyPagination);
|
||||
app.component('vehicleAttachmentUpload', vehicleAttachmentUpload);
|
||||
app.component('highlight', highlight);
|
||||
app.component('codeEditor', codeEditor);
|
||||
app.component('cronEditor', cronEditor);
|
||||
@@ -72,6 +76,16 @@ app.use(ElementPlus, {
|
||||
app.use(Avue, {
|
||||
axios,
|
||||
calcHeight: 10,
|
||||
crudOption: {
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchLabelWidth: 160,
|
||||
searchMenuSpan: 24,
|
||||
searchMenuPosition: 'right',
|
||||
menuWidth: 220,
|
||||
},
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
locale: language,
|
||||
i18n: (key, options) => i18n.global.t(key, options),
|
||||
});
|
||||
@@ -81,3 +95,5 @@ app.use(VueClipboard, {
|
||||
appendToBody: true, // 这可以帮助解决一些更复杂的使用场景下的问题
|
||||
});
|
||||
app.mount('#app');
|
||||
setupPaginationEnhancer();
|
||||
setupDialogFormLabelAlign();
|
||||
|
||||
@@ -11,7 +11,10 @@ export const createOption = ({ validateLongitude, validateLatitude }) => ({
|
||||
dialogWidth: 980,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
indexLabel: '序号',
|
||||
@@ -83,16 +86,14 @@ export const createOption = ({ validateLongitude, validateLatitude }) => ({
|
||||
{
|
||||
label: '经度',
|
||||
prop: 'longitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
formslot: true,
|
||||
minWidth: 120,
|
||||
rules: [{ validator: validateLongitude, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '纬度',
|
||||
prop: 'latitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
formslot: true,
|
||||
minWidth: 120,
|
||||
rules: [{ validator: validateLatitude, trigger: 'blur' }],
|
||||
},
|
||||
|
||||
@@ -120,13 +120,16 @@ export default {
|
||||
calcHeight: 30,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
grid: true,
|
||||
labelWidth: 180,
|
||||
labelWidth: 'auto',
|
||||
menuWidth: 350,
|
||||
dialogWidth: 1200,
|
||||
dialogClickModal: false,
|
||||
|
||||
@@ -3,13 +3,16 @@ export default {
|
||||
calcHeight: 30,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
grid: true,
|
||||
labelWidth: 100,
|
||||
labelWidth: 'auto',
|
||||
menuWidth: 350,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
|
||||
@@ -12,7 +12,10 @@ export const LOCK_STATUS_DIC = [
|
||||
export const authLockOption = {
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
addBtn: false,
|
||||
|
||||
@@ -10,7 +10,10 @@ export const GRANT_TYPE_DIC = [
|
||||
export const authLogOption = {
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
addBtn: false,
|
||||
|
||||
@@ -3,7 +3,10 @@ export const optionParent = {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 10,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
@@ -92,7 +95,10 @@ export const optionChild = {
|
||||
calcHeight: 95,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 10,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
@@ -105,8 +111,8 @@ export const optionChild = {
|
||||
{
|
||||
label: '字典编号',
|
||||
prop: 'code',
|
||||
addDisabled: true,
|
||||
editDisabled: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [
|
||||
|
||||
@@ -3,7 +3,10 @@ export const optionParent = {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 10,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
@@ -93,7 +96,10 @@ export const optionChild = {
|
||||
calcHeight: 95,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 10,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
@@ -106,8 +112,8 @@ export const optionChild = {
|
||||
{
|
||||
label: '字典编号',
|
||||
prop: 'code',
|
||||
addDisabled: true,
|
||||
editDisabled: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [
|
||||
|
||||
@@ -23,7 +23,10 @@ export const userOption = safe => {
|
||||
calcHeight: 150,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
@@ -353,13 +356,16 @@ export const userOption = safe => {
|
||||
export const platformOption = {
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
viewBtn: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 120,
|
||||
menuWidth: 220,
|
||||
editBtnText: '配置',
|
||||
column: [
|
||||
{
|
||||
@@ -475,7 +481,7 @@ export const excelOption = {
|
||||
export const platformFormOption = {
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
labelWidth: 100,
|
||||
labelWidth: 'auto',
|
||||
column: [
|
||||
{
|
||||
label: '用户账号',
|
||||
|
||||
@@ -6,11 +6,14 @@ export const codeOption = {
|
||||
dialogWidth: 900,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
menuWidth: 350,
|
||||
viewBtn: true,
|
||||
dialogClickModal: false,
|
||||
@@ -376,7 +379,7 @@ export const codeOption = {
|
||||
};
|
||||
|
||||
export const genOption = {
|
||||
labelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
column: [
|
||||
{
|
||||
label: '上级菜单',
|
||||
|
||||
@@ -3,14 +3,17 @@ export default {
|
||||
calcHeight: 50,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
grid: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
searchLabelWidth: 100,
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
menuWidth: 320,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
|
||||
@@ -3,14 +3,17 @@ export default {
|
||||
calcHeight: 50,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
grid: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
searchLabelWidth: 100,
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
menuWidth: 320,
|
||||
dialogWidth: 500,
|
||||
dialogClickModal: false,
|
||||
|
||||
@@ -3,7 +3,10 @@ export const option = {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
@@ -71,7 +74,17 @@ export const option = {
|
||||
{
|
||||
label: '所属组织',
|
||||
prop: 'organizationName',
|
||||
type: 'cascader',
|
||||
search: true,
|
||||
emitPath: false,
|
||||
checkStrictly: true,
|
||||
filterable: true,
|
||||
dicData: [],
|
||||
props: {
|
||||
label: 'label',
|
||||
value: 'value',
|
||||
children: 'children',
|
||||
},
|
||||
width: 140,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3,7 +3,10 @@ export const option = {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
|
||||
@@ -3,7 +3,10 @@ export const option = {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
|
||||
@@ -33,21 +33,26 @@ export const accidentResponsibilityDic = [
|
||||
export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 1050,
|
||||
labelPosition: 'top',
|
||||
dialogWidth: 960,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
column: [
|
||||
{
|
||||
label: '车船类型',
|
||||
@@ -57,6 +62,7 @@ export const option = {
|
||||
dicData: vehicleTypeDic,
|
||||
value: '车辆',
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [{ required: true, message: '请选择车船类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -66,6 +72,8 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '输入车牌号/船号查询选择',
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号/船号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
@@ -78,6 +86,8 @@ export const option = {
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '请选择,日期选择器',
|
||||
rules: [{ required: true, message: '请选择事故发生日期', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
@@ -85,6 +95,8 @@ export const option = {
|
||||
prop: 'accidentLocation',
|
||||
minWidth: 200,
|
||||
overHidden: true,
|
||||
span: 12,
|
||||
placeholder: '请输入,100字内',
|
||||
rules: [{ max: 100, message: '最多 100 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -94,6 +106,8 @@ export const option = {
|
||||
dicData: accidentNatureDic,
|
||||
search: true,
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择事故性质', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -103,6 +117,8 @@ export const option = {
|
||||
dicData: accidentResponsibilityDic,
|
||||
search: true,
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择事故责任', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -112,6 +128,8 @@ export const option = {
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -121,17 +139,20 @@ export const option = {
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '事故原因',
|
||||
label: '事故原因及损坏情况',
|
||||
prop: 'accidentReasonDamage',
|
||||
type: 'textarea',
|
||||
minRows: 3,
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
search: true,
|
||||
minWidth: 220,
|
||||
overHidden: true,
|
||||
placeholder: '请输入,500字以内',
|
||||
rules: [{ max: 500, message: '最多 500 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -142,24 +163,16 @@ export const option = {
|
||||
span: 24,
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
placeholder: '请输入,200字以内',
|
||||
rules: [{ max: 200, message: '最多 200 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '事故评定开始日期',
|
||||
@@ -203,7 +216,6 @@ export const option = {
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 00:00:00',
|
||||
valueFormat: 'YYYY-MM-DD 00:00:00',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
@@ -215,7 +227,6 @@ export const option = {
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 23:59:59',
|
||||
valueFormat: 'YYYY-MM-DD 23:59:59',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
@@ -261,6 +272,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -32,21 +32,26 @@ export const shipInspectionTypeDic = [
|
||||
export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 1050,
|
||||
labelPosition: 'top',
|
||||
dialogWidth: 960,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
column: [
|
||||
{
|
||||
label: '车船类型',
|
||||
@@ -56,6 +61,7 @@ export const option = {
|
||||
dicData: vehicleTypeDic,
|
||||
value: '车辆',
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [{ required: true, message: '请选择车船类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -65,6 +71,8 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '输入车牌号/船号查询选择',
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号/船号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
@@ -77,6 +85,8 @@ export const option = {
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '请选择,日期选择器',
|
||||
rules: [{ required: true, message: '请选择检测评定日期', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
@@ -86,6 +96,8 @@ export const option = {
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ required: true, message: '请选择有效期截止日', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
@@ -94,6 +106,8 @@ export const option = {
|
||||
type: 'select',
|
||||
dicData: vehicleTechnicalLevelDic,
|
||||
minWidth: 130,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择车辆技术等级', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -103,18 +117,24 @@ export const option = {
|
||||
dicData: shipInspectionTypeDic,
|
||||
display: false,
|
||||
minWidth: 130,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择船舶检验类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '客车类型及等级',
|
||||
prop: 'passengerTypeLevel',
|
||||
minWidth: 150,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '检测评定单位',
|
||||
prop: 'inspectionUnit',
|
||||
minWidth: 160,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -124,6 +144,8 @@ export const option = {
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 100,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入费用', trigger: 'blur' },
|
||||
{ validator: validateNonNegative, trigger: 'blur' },
|
||||
@@ -133,6 +155,8 @@ export const option = {
|
||||
label: '评定(复核)单位',
|
||||
prop: 'assessmentUnit',
|
||||
minWidth: 160,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -143,24 +167,16 @@ export const option = {
|
||||
span: 24,
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
placeholder: '请输入,200字以内',
|
||||
rules: [{ max: 200, message: '最多 200 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '检测评定开始日期',
|
||||
@@ -204,7 +220,6 @@ export const option = {
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 00:00:00',
|
||||
valueFormat: 'YYYY-MM-DD 00:00:00',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
@@ -216,7 +231,6 @@ export const option = {
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 23:59:59',
|
||||
valueFormat: 'YYYY-MM-DD 23:59:59',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
@@ -262,6 +276,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -22,17 +22,26 @@ const maskEtcCardNo = value => {
|
||||
export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'top',
|
||||
dialogWidth: 960,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
menuFixed: 'right',
|
||||
column: [
|
||||
{
|
||||
@@ -42,22 +51,13 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '输入车牌号模糊查询选择',
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'ETC卡号',
|
||||
prop: 'etcCardNo',
|
||||
formatter: row => maskEtcCardNo(row.etcCardNo),
|
||||
search: true,
|
||||
minWidth: 220,
|
||||
rules: [
|
||||
{ required: true, message: '请输入ETC卡号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '入口时间',
|
||||
prop: 'entryTime',
|
||||
@@ -65,6 +65,21 @@ export const option = {
|
||||
format: 'YYYY-MM-DD HH:mm',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
minWidth: 170,
|
||||
span: 12,
|
||||
placeholder: '请选择,日期选择器',
|
||||
},
|
||||
{
|
||||
label: 'ETC卡号',
|
||||
prop: 'etcCardNo',
|
||||
formatter: row => maskEtcCardNo(row.etcCardNo),
|
||||
search: true,
|
||||
minWidth: 220,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入ETC卡号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '出口时间',
|
||||
@@ -73,18 +88,38 @@ export const option = {
|
||||
format: 'YYYY-MM-DD HH:mm',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
minWidth: 170,
|
||||
span: 12,
|
||||
placeholder: '请选择,日期选择器',
|
||||
rules: [{ required: true, message: '请选择出口时间', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
label: '入口站',
|
||||
prop: 'entryStation',
|
||||
minWidth: 150,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '交易金额',
|
||||
prop: 'transactionAmount',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 130,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入交易金额', trigger: 'blur' },
|
||||
{ validator: validateNonNegative, trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '出口站',
|
||||
prop: 'exitStation',
|
||||
minWidth: 150,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -94,18 +129,7 @@ export const option = {
|
||||
minWidth: 120,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '交易金额',
|
||||
prop: 'transactionAmount',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 130,
|
||||
rules: [
|
||||
{ required: true, message: '请输入交易金额', trigger: 'blur' },
|
||||
{ validator: validateNonNegative, trigger: 'blur' },
|
||||
],
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
@@ -115,24 +139,16 @@ export const option = {
|
||||
span: 24,
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
placeholder: '请输入,200字以内',
|
||||
rules: [{ max: 200, message: '最多 200 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '组织',
|
||||
@@ -185,6 +201,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -30,24 +30,26 @@ export const ocrTemplateDic = [
|
||||
];
|
||||
|
||||
export const option = {
|
||||
title: '保险记录(OCR)',
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 1100,
|
||||
labelPosition: 'top',
|
||||
labelPosition: 'right',
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
column: [
|
||||
{
|
||||
label: '车船类型',
|
||||
@@ -243,6 +245,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -12,71 +12,109 @@ const validateNonNegative = (rule, value, callback) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const vehicleTypeDic = [
|
||||
{ label: '车辆', value: '车辆' },
|
||||
{ label: '船舶', value: '船舶' },
|
||||
];
|
||||
|
||||
export const option = {
|
||||
title: '里程记录(无船)',
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'top',
|
||||
labelPosition: 'right',
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
menuFixed: 'right',
|
||||
column: [
|
||||
{
|
||||
label: '车牌号',
|
||||
label: '车船类型',
|
||||
prop: 'vehicleType',
|
||||
type: 'radio',
|
||||
dicData: vehicleTypeDic,
|
||||
value: '车辆',
|
||||
search: true,
|
||||
minWidth: 110,
|
||||
rules: [{ required: true, message: '请选择车船类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '车牌号/船号',
|
||||
prop: 'vehicleNo',
|
||||
slot: true,
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 130,
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号', trigger: 'blur' },
|
||||
{ required: true, message: '请输入车牌号/船号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '上月统计日里程表示值(km)',
|
||||
label: '上月统计里程数',
|
||||
prop: 'previousMonthMileage',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '公里',
|
||||
minWidth: 210,
|
||||
slot: true,
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '本月统计日里程表示值(km)',
|
||||
label: '本月统计里程数',
|
||||
prop: 'currentMonthMileage',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '公里',
|
||||
minWidth: 210,
|
||||
slot: true,
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '本月行驶里程(km)',
|
||||
label: '本月行驶里程数',
|
||||
prop: 'monthlyMileage',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '公里',
|
||||
minWidth: 170,
|
||||
slot: true,
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '累计行驶里程(km)',
|
||||
label: '累计行驶里程数',
|
||||
prop: 'totalMileage',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '公里',
|
||||
minWidth: 170,
|
||||
slot: true,
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '里程单位',
|
||||
prop: 'mileageUnit',
|
||||
type: 'select',
|
||||
dicData: [
|
||||
{ label: '公里', value: '公里' },
|
||||
{ label: '海里', value: '海里' },
|
||||
],
|
||||
value: '公里',
|
||||
hide: true,
|
||||
display: false,
|
||||
search: false,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
@@ -90,37 +128,28 @@ export const option = {
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '累计行驶里程开始值',
|
||||
label: '累计行驶里程',
|
||||
prop: 'totalMileageStart',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
search: true,
|
||||
searchSpan: 8,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '累计行驶里程结束值',
|
||||
label: '累计行驶里程数结束值',
|
||||
prop: 'totalMileageEnd',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
@@ -202,6 +231,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -26,16 +26,19 @@ export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'top',
|
||||
labelPosition: 'right',
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
menuFixed: 'right',
|
||||
column: [
|
||||
{
|
||||
@@ -85,6 +88,7 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
placeholder: '输入车牌号/船号查询选择',
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号/船号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
@@ -159,19 +163,10 @@ export const option = {
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '交易开始时间',
|
||||
@@ -237,7 +232,6 @@ export const option = {
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 00:00:00',
|
||||
valueFormat: 'YYYY-MM-DD 00:00:00',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
@@ -249,7 +243,6 @@ export const option = {
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD 23:59:59',
|
||||
valueFormat: 'YYYY-MM-DD 23:59:59',
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
@@ -295,6 +288,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -30,37 +30,28 @@ export const expenseTypeDic = [
|
||||
export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'top',
|
||||
dialogWidth: 960,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
menuFixed: 'right',
|
||||
column: [
|
||||
{
|
||||
label: '费用日期',
|
||||
prop: 'expenseDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 140,
|
||||
rules: [{ required: true, message: '请选择费用日期', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
label: '其他费用类型',
|
||||
prop: 'expenseType',
|
||||
type: 'select',
|
||||
dicData: expenseTypeDic,
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
rules: [{ required: true, message: '请选择其他费用类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '车船类型',
|
||||
prop: 'vehicleType',
|
||||
@@ -69,6 +60,7 @@ export const option = {
|
||||
value: '车辆',
|
||||
search: true,
|
||||
minWidth: 110,
|
||||
span: 24,
|
||||
rules: [{ required: true, message: '请选择车船类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -78,11 +70,49 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
span: 12,
|
||||
placeholder: '输入车牌号/船号查询选择',
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号/船号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '费用日期',
|
||||
prop: 'expenseDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '请选择,日期选择器',
|
||||
rules: [{ required: true, message: '请选择费用日期', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
label: '费用类型',
|
||||
prop: 'expenseType',
|
||||
type: 'select',
|
||||
dicData: expenseTypeDic,
|
||||
search: true,
|
||||
minWidth: 150,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择费用类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '金额',
|
||||
prop: 'amount',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入金额', trigger: 'blur' },
|
||||
{ validator: validateNonNegative, trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '数据来源',
|
||||
prop: 'dataSource',
|
||||
@@ -90,18 +120,7 @@ export const option = {
|
||||
minWidth: 120,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '金额',
|
||||
prop: 'amount',
|
||||
type: 'number',
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 120,
|
||||
rules: [
|
||||
{ required: true, message: '请输入金额', trigger: 'blur' },
|
||||
{ validator: validateNonNegative, trigger: 'blur' },
|
||||
],
|
||||
viewDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
@@ -111,24 +130,16 @@ export const option = {
|
||||
span: 24,
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
placeholder: '请输入,200字以内',
|
||||
rules: [{ max: 200, message: '最多 200 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '费用日期',
|
||||
@@ -194,6 +205,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -21,24 +21,28 @@ const validatePositiveInteger = (rule, value, callback) => {
|
||||
};
|
||||
|
||||
export const option = {
|
||||
title: '换胎记录(无船)',
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'top',
|
||||
dialogWidth: 960,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
column: [
|
||||
{
|
||||
label: '车牌号',
|
||||
@@ -47,6 +51,8 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 130,
|
||||
span: 12,
|
||||
placeholder: '输入车牌号查询选择',
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
@@ -56,6 +62,8 @@ export const option = {
|
||||
label: '处理人',
|
||||
prop: 'handler',
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 20, message: '最多 20 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -65,12 +73,16 @@ export const option = {
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
minWidth: 130,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择换胎时间', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
label: '轮胎品牌',
|
||||
prop: 'tireBrand',
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -79,6 +91,8 @@ export const option = {
|
||||
type: 'number',
|
||||
precision: 0,
|
||||
minWidth: 110,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ validator: validatePositiveInteger, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -88,6 +102,8 @@ export const option = {
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入换胎费用', trigger: 'blur' },
|
||||
{ validator: validateNonNegative, trigger: 'blur' },
|
||||
@@ -98,6 +114,8 @@ export const option = {
|
||||
prop: 'replacementDescription',
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
span: 24,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 200, message: '最多 200 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -108,24 +126,16 @@ export const option = {
|
||||
span: 24,
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 500, message: '最多 500 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
@@ -203,6 +213,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -17,16 +17,19 @@ export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'top',
|
||||
labelPosition: 'right',
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
menuFixed: 'right',
|
||||
column: [
|
||||
{
|
||||
@@ -46,6 +49,7 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 140,
|
||||
placeholder: '输入车牌号/船号模糊查询选择',
|
||||
rules: [{ max: 30, message: '最多 30 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -83,19 +87,10 @@ export const option = {
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
@@ -173,6 +168,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -41,21 +41,26 @@ export const processStatusDic = [
|
||||
export const option = {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 1100,
|
||||
labelPosition: 'top',
|
||||
dialogWidth: 960,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 180,
|
||||
menuWidth: 220,
|
||||
column: [
|
||||
{
|
||||
label: '车船类型',
|
||||
@@ -65,6 +70,7 @@ export const option = {
|
||||
dicData: vehicleTypeDic,
|
||||
value: '车辆',
|
||||
search: true,
|
||||
span: 24,
|
||||
rules: [{ required: true, message: '请选择车船类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -74,6 +80,8 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '输入车牌号/船号查询选择',
|
||||
rules: [
|
||||
{ required: true, message: '请输入车牌号/船号', trigger: 'blur' },
|
||||
{ max: 30, message: '最多 30 个字符', trigger: 'blur' },
|
||||
@@ -85,6 +93,8 @@ export const option = {
|
||||
formslot: true,
|
||||
search: true,
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入驾驶人/船长', trigger: 'blur' },
|
||||
{ max: 20, message: '最多 20 个字符', trigger: 'blur' },
|
||||
@@ -96,6 +106,8 @@ export const option = {
|
||||
type: 'select',
|
||||
dicData: violationTypeDic,
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -104,6 +116,8 @@ export const option = {
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
display: false,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入事项', trigger: 'blur' },
|
||||
{ max: 100, message: '最多 100 个字符', trigger: 'blur' },
|
||||
@@ -116,6 +130,8 @@ export const option = {
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
minWidth: 170,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: true, message: '请选择时间', trigger: 'click' }],
|
||||
},
|
||||
{
|
||||
@@ -123,6 +139,8 @@ export const option = {
|
||||
prop: 'location',
|
||||
minWidth: 180,
|
||||
overHidden: true,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入地点', trigger: 'blur' },
|
||||
{ max: 100, message: '最多 100 个字符', trigger: 'blur' },
|
||||
@@ -135,6 +153,8 @@ export const option = {
|
||||
precision: 2,
|
||||
append: '元',
|
||||
minWidth: 100,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ validator: validateNonNegative, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -143,12 +163,16 @@ export const option = {
|
||||
type: 'number',
|
||||
precision: 0,
|
||||
minWidth: 110,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ validator: validateDeductPoints, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '被罚单位',
|
||||
prop: 'penaltyUnit',
|
||||
minWidth: 150,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 50, message: '最多 50 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
@@ -160,6 +184,7 @@ export const option = {
|
||||
search: true,
|
||||
clearable: true,
|
||||
minWidth: 120,
|
||||
span: 12,
|
||||
rules: [{ required: true, message: '请选择状态', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
@@ -170,6 +195,7 @@ export const option = {
|
||||
span: 24,
|
||||
minWidth: 220,
|
||||
overHidden: true,
|
||||
placeholder: '请输入',
|
||||
rules: [
|
||||
{ required: true, message: '请输入过程描述', trigger: 'blur' },
|
||||
{ max: 500, message: '最多 500 个字符', trigger: 'blur' },
|
||||
@@ -183,24 +209,16 @@ export const option = {
|
||||
span: 24,
|
||||
minWidth: 220,
|
||||
overHidden: true,
|
||||
placeholder: '请输入',
|
||||
rules: [{ max: 500, message: '最多 500 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '附件',
|
||||
prop: 'attachments',
|
||||
type: 'upload',
|
||||
listType: 'text',
|
||||
multiple: true,
|
||||
accept: '.jpg,.jpeg,.png,.pdf',
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
url: 'link',
|
||||
name: 'name',
|
||||
},
|
||||
action: '/blade-resource/oss/endpoint/put-file',
|
||||
span: 24,
|
||||
minWidth: 100,
|
||||
slot: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
@@ -278,6 +296,7 @@ export const option = {
|
||||
};
|
||||
|
||||
export const excelOption = {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
|
||||
@@ -30,12 +30,10 @@ export default {
|
||||
</script>
|
||||
<style scoped>
|
||||
.logo-title {
|
||||
font-size: 16px;
|
||||
background-image: linear-gradient(120deg, #54b6d0 16%, #3f8bdb, #2c77f1);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
padding-left: 30px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
.logo-img {
|
||||
width: 40px;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
:key="index"
|
||||
@click="handleSelect(item)"
|
||||
>
|
||||
<i :class="[item[iconKey], 'avue-searchs__item-icon']"></i>
|
||||
<i v-if="item[iconKey]" :class="[item[iconKey], 'avue-searchs__item-icon']"></i>
|
||||
<span class="avue-searchs__item-title">{{ item[labelKey] }}</span>
|
||||
<div class="avue-searchs__item-path">
|
||||
{{ item[pathKey] }}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
@click="open(item)"
|
||||
:key="item[labelKey]"
|
||||
>
|
||||
<i :class="item[iconKey]"></i>
|
||||
<i v-if="item[iconKey]" :class="item[iconKey]"></i>
|
||||
<template #title>
|
||||
<span :alt="item[pathKey]">{{ getTitle(item) }}</span>
|
||||
</template>
|
||||
@@ -17,7 +17,7 @@
|
||||
:key="item[labelKey]"
|
||||
>
|
||||
<template #title>
|
||||
<i :class="item[iconKey]"></i>
|
||||
<i v-if="item[iconKey]" :class="item[iconKey]"></i>
|
||||
<span>{{ getTitle(item) }}</span>
|
||||
</template>
|
||||
<template v-for="(child, cindex) in item[childrenKey]" :key="child[labelKey]">
|
||||
@@ -26,7 +26,7 @@
|
||||
@click="open(child)"
|
||||
v-if="validatenull(child[childrenKey])"
|
||||
>
|
||||
<i :class="child[iconKey]"></i>
|
||||
<i v-if="child[iconKey]" :class="child[iconKey]"></i>
|
||||
<template #title>
|
||||
<span>{{ getTitle(child) }}</span>
|
||||
</template>
|
||||
|
||||
@@ -161,7 +161,7 @@ export default {
|
||||
roleId: '',
|
||||
},
|
||||
userOption: {
|
||||
labelWidth: 70,
|
||||
labelWidth: 'auto',
|
||||
submitBtn: true,
|
||||
emptyBtn: false,
|
||||
submitText: '切 换',
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'; // default color
|
||||
const DEFAULT_PRIMARY_COLOR = '#409eff';
|
||||
|
||||
export default {
|
||||
name: 'topColor',
|
||||
data() {
|
||||
@@ -17,7 +19,7 @@ export default {
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.themeVal = this.colorName || '#2C77F1';
|
||||
this.themeVal = this.colorName || DEFAULT_PRIMARY_COLOR;
|
||||
},
|
||||
watch: {
|
||||
themeVal(val, oldVal) {
|
||||
@@ -69,6 +71,7 @@ export default {
|
||||
`${this.getLightColor(e, i / 10)}`
|
||||
);
|
||||
}
|
||||
document.documentElement.style.setProperty(`${pre}-dark-2`, `${this.getDarkColor(e, 0.2)}`);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<span v-if="text" @click="handleLock">{{ text }}</span>
|
||||
<i v-else class="icon-suoping" @click="handleLock"></i>
|
||||
<el-dialog title="设置锁屏密码" v-model="box" width="30%" append-to-body>
|
||||
<el-form :model="form" ref="form" label-width="80px">
|
||||
<el-form :model="form" ref="form" label-width="auto">
|
||||
<el-form-item
|
||||
label="锁屏密码"
|
||||
prop="passwd"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<el-menu class="top-menu" :default-active="activeIndex" mode="horizontal" text-color="#333">
|
||||
<el-menu-item index="0" @click="openHome">
|
||||
<template #title>
|
||||
<i :class="itemHome.source" style="padding-right: 5px"></i>
|
||||
<i v-if="itemHome.source" :class="itemHome.source" style="padding-right: 5px"></i>
|
||||
<span>{{ itemHome.name }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
@@ -10,7 +10,7 @@
|
||||
<template v-for="(item, index) in items" :key="index">
|
||||
<el-menu-item :index="item.id + ''" @click="openMenu(item)">
|
||||
<template #title>
|
||||
<i :class="item.source" style="padding-right: 5px"></i>
|
||||
<i v-if="item.source" :class="item.source" style="padding-right: 5px"></i>
|
||||
<span>{{ item.name }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
@select="handleSelect"
|
||||
>
|
||||
<template #="{ item }">
|
||||
<i :class="[item[iconKey], 'icon']"></i>
|
||||
<i v-if="item[iconKey]" :class="[item[iconKey], 'icon']"></i>
|
||||
<div class="name">{{ item[labelKey] }}</div>
|
||||
<div class="addr">{{ item[pathKey] }}</div>
|
||||
</template>
|
||||
|
||||
@@ -171,7 +171,7 @@ RouterPlugin.install = function (option = {}) {
|
||||
export const formatPath = (ele, first) => {
|
||||
const propsDefault = website.menu;
|
||||
const icon = ele[propsDefault.icon];
|
||||
ele[propsDefault.icon] = !icon ? propsDefault.iconDefault : icon;
|
||||
ele[propsDefault.icon] = icon || '';
|
||||
ele.meta = {
|
||||
keepAlive: ele.isOpen === 2,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { setStore, getStore, removeStore } from 'utils/store';
|
||||
import website from '@/config/website';
|
||||
|
||||
const DEFAULT_PRIMARY_COLOR = '#409eff';
|
||||
const LEGACY_PRIMARY_COLORS = ['#2C77F1', '#5C9DF8'];
|
||||
const storedColorName = getStore({ name: 'colorName' });
|
||||
|
||||
const common = {
|
||||
state: {
|
||||
language: getStore({ name: 'language' }) || 'zh-cn',
|
||||
@@ -10,7 +14,10 @@ const common = {
|
||||
isSearch: false,
|
||||
isRefresh: true,
|
||||
isLock: getStore({ name: 'isLock' }),
|
||||
colorName: getStore({ name: 'colorName' }) || '#2C77F1',
|
||||
colorName:
|
||||
!storedColorName || LEGACY_PRIMARY_COLORS.includes(String(storedColorName).toUpperCase())
|
||||
? DEFAULT_PRIMARY_COLOR
|
||||
: storedColorName,
|
||||
themeName: getStore({ name: 'themeName' }) || 'theme-go',
|
||||
lockPasswd: getStore({ name: 'lockPasswd' }) || '',
|
||||
website: website,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
//图标配置
|
||||
@use './iconfont.scss';
|
||||
//登录样式
|
||||
@use "./login.scss";
|
||||
@use './login.scss';
|
||||
//适配
|
||||
@use './media.scss';
|
||||
//滚动条样式
|
||||
@@ -30,6 +30,13 @@ a {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
.el-menu-item span,
|
||||
.el-sub-menu__title span {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
outline: none;
|
||||
}
|
||||
@@ -40,7 +47,7 @@ a {
|
||||
.avue-layout,
|
||||
.login-logo,
|
||||
.avue-main {
|
||||
transition: all .3s;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.avue-layout {
|
||||
@@ -50,7 +57,6 @@ a {
|
||||
|
||||
&:not(.avue-layout--horizontal) {
|
||||
.avue-sidebar {
|
||||
background-image: url('/img/左侧背景放底部.png') !important;
|
||||
background-repeat: no-repeat !important;
|
||||
background-position: left bottom !important;
|
||||
background-size: 100% auto !important;
|
||||
@@ -65,7 +71,9 @@ a {
|
||||
height: variables.$top_height;
|
||||
display: flex;
|
||||
|
||||
.avue-menu, .el-menu-item, .el-sub-menu__title {
|
||||
.avue-menu,
|
||||
.el-menu-item,
|
||||
.el-sub-menu__title {
|
||||
height: variables.$top_height;
|
||||
line-height: variables.$top_height;
|
||||
}
|
||||
@@ -76,7 +84,7 @@ a {
|
||||
}
|
||||
|
||||
.avue-logo {
|
||||
width: variables.$sidebar_width
|
||||
width: variables.$sidebar_width;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +112,7 @@ a {
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
background: #f0f2f5;
|
||||
background: #f4f6fa;
|
||||
}
|
||||
|
||||
#avue-view {
|
||||
@@ -132,7 +140,7 @@ a {
|
||||
}
|
||||
|
||||
.mac_bg {
|
||||
background-image: url("/img/bg.jpg");
|
||||
background-image: url('/img/bg.jpg');
|
||||
background-color: #000;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
:root {
|
||||
--el-color-primary: #409eff;
|
||||
--el-color-primary-light-1: #53a8ff;
|
||||
--el-color-primary-light-2: #66b1ff;
|
||||
--el-color-primary-light-3: #79bbff;
|
||||
--el-color-primary-light-4: #8cc5ff;
|
||||
--el-color-primary-light-5: #a0cfff;
|
||||
--el-color-primary-light-6: #b3d8ff;
|
||||
--el-color-primary-light-7: #c6e2ff;
|
||||
--el-color-primary-light-8: #d9ecff;
|
||||
--el-color-primary-light-9: #ecf5ff;
|
||||
--el-color-primary-dark-2: #337ecc;
|
||||
}
|
||||
|
||||
.el-card.is-always-shadow {
|
||||
box-shadow: none;
|
||||
border: none !important;
|
||||
@@ -22,7 +36,6 @@
|
||||
z-index: 2048;
|
||||
}
|
||||
|
||||
|
||||
.el-col {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
@@ -31,12 +44,19 @@
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item--divided:before, .el-menu, .el-menu--horizontal > .el-menu-item:not(.is-disabled):focus, .el-menu--horizontal > .el-menu-item:not(.is-disabled):hover, .el-menu--horizontal > .el-sub-menu .el-sub-menu__title:hover {
|
||||
.el-dropdown-menu__item--divided:before,
|
||||
.el-menu,
|
||||
.el-menu--horizontal > .el-menu-item:not(.is-disabled):focus,
|
||||
.el-menu--horizontal > .el-menu-item:not(.is-disabled):hover,
|
||||
.el-menu--horizontal > .el-sub-menu .el-sub-menu__title:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
|
||||
.el-dropdown-menu__item--divided:before, .el-menu, .el-menu--horizontal > .el-menu-item:not(.is-disabled):focus, .el-menu--horizontal > .el-menu-item:not(.is-disabled):hover, .el-menu--horizontal > .el-sub-menu .el-sub-menu__title:hover {
|
||||
.el-dropdown-menu__item--divided:before,
|
||||
.el-menu,
|
||||
.el-menu--horizontal > .el-menu-item:not(.is-disabled):focus,
|
||||
.el-menu--horizontal > .el-menu-item:not(.is-disabled):hover,
|
||||
.el-menu--horizontal > .el-sub-menu .el-sub-menu__title:hover {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
@@ -49,18 +69,20 @@
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.el-button.is-text:not(.is-disabled):focus, .el-button.is-text:not(.is-disabled):hover {
|
||||
.el-button.is-text:not(.is-disabled):focus,
|
||||
.el-button.is-text:not(.is-disabled):hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
.el-dropdown{
|
||||
.el-dropdown {
|
||||
display: inline-block;
|
||||
line-height: inherit;
|
||||
.el-button.is-text:not(.is-disabled):focus-visible{
|
||||
.el-button.is-text:not(.is-disabled):focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.avue-icon i, .avue-icon svg {
|
||||
.avue-icon i,
|
||||
.avue-icon svg {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
@@ -68,6 +90,144 @@
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.basic-container__card .avue-crud > .el-card,
|
||||
.basic-container__card .avue-crud__pagination.el-card {
|
||||
border: none;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.basic-container__card .avue-crud__search.el-card {
|
||||
border: none;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.basic-container__card .avue-crud > .el-card > .el-card__body,
|
||||
.basic-container__card .avue-crud__pagination.el-card > .el-card__body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.basic-container__card .avue-crud__search.el-card > .el-card__body {
|
||||
padding: 12px 12px 4px;
|
||||
}
|
||||
|
||||
.basic-container__card .avue-crud__header {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.avue-crud__header,
|
||||
.avue-crud .el-table th .cell,
|
||||
.avue-crud .el-table td .cell {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.avue-crud__search .el-form-item,
|
||||
.avue-crud__search .el-form-item--default.el-form-item,
|
||||
.avue-crud__search .el-form-item--small.el-form-item,
|
||||
.avue-crud__search .el-form-item--large.el-form-item,
|
||||
.avue-crud__search .el-form-item--medium.el-form-item {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.avue-crud__search .el-form-item__label {
|
||||
min-width: 160px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.avue-crud__search.el-card {
|
||||
border: none;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.avue-crud__search.el-card > .el-card__body {
|
||||
padding: 12px 12px 4px;
|
||||
}
|
||||
|
||||
.avue-crud__search {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.avue-crud__search .avue-form__menu--right {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.el-dialog .el-form-item__label {
|
||||
justify-content: flex-end;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.el-dialog .el-col {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.el-dialog .el-form-item,
|
||||
.el-dialog .el-form-item--default.el-form-item,
|
||||
.el-dialog .el-form-item--small.el-form-item,
|
||||
.el-dialog .el-form-item--large.el-form-item,
|
||||
.el-dialog .el-form-item--medium.el-form-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.el-dialog__title,
|
||||
.dialog-section-title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.el-dialog__title:not(:empty)::before,
|
||||
.dialog-section-title::before {
|
||||
content: '';
|
||||
flex: none;
|
||||
width: 4px;
|
||||
height: 16px;
|
||||
border-radius: 2px;
|
||||
background-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.avue-crud__dialog .avue-dialog__footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.avue-crud__dialog .avue-dialog__footer--left {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.avue-crud__dialog .avue-dialog__footer--center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.avue-crud__dialog .avue-dialog__footer > .el-button {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.avue-crud__dialog .avue-dialog__footer > .el-button:last-of-type {
|
||||
order: 1;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.avue-crud__dialog .avue-dialog__footer > .el-button.el-button--primary {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
// 统一表格线条颜色
|
||||
.el-table {
|
||||
--el-table-border-color: #eff1f7;
|
||||
--el-table-border: 1px solid var(--el-table-border-color);
|
||||
}
|
||||
|
||||
.el-table,
|
||||
.el-table th.el-table__cell,
|
||||
.el-table td.el-table__cell {
|
||||
border-color: #eff1f7;
|
||||
}
|
||||
|
||||
// 统一 avue-crud 表头背景色
|
||||
.avue-crud .el-table th.el-table__cell,
|
||||
.avue-crud .el-table__header-wrapper tr th.el-table-fixed-column--left,
|
||||
@@ -75,8 +235,212 @@
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
// 统一 avue-crud 偶数行背景色
|
||||
.avue-crud .el-table__body-wrapper tr.el-table__row:nth-child(even) td.el-table__cell,
|
||||
.avue-crud .el-table__body-wrapper tr.el-table__row:nth-child(even) td.el-table-fixed-column--left,
|
||||
.avue-crud
|
||||
.el-table__body-wrapper
|
||||
tr.el-table__row:nth-child(even)
|
||||
td.el-table-fixed-column--right {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
// 统一 avue-crud 行 hover 背景色
|
||||
.avue-crud .el-table__body-wrapper tr.hover-row td.el-table__cell,
|
||||
.avue-crud .el-table__body-wrapper tr.hover-row td.el-table-fixed-column--left,
|
||||
.avue-crud .el-table__body-wrapper tr.hover-row td.el-table-fixed-column--right {
|
||||
background-color: var(--el-table-row-hover-bg-color);
|
||||
}
|
||||
|
||||
.avue-crud__header {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
// 统一分页为按钮样式并靠右展示
|
||||
.avue-crud__pagination,
|
||||
.empty-pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.avue-crud__pagination .el-pagination,
|
||||
.empty-pagination .el-pagination,
|
||||
.el-pagination {
|
||||
--el-pagination-button-width: 36px;
|
||||
--el-pagination-button-height: 32px;
|
||||
--el-pagination-border-radius: 4px;
|
||||
--el-pagination-button-bg-color: #fff;
|
||||
--el-pagination-bg-color: #fff;
|
||||
--el-pagination-hover-color: var(--el-color-primary);
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
gap: 6px;
|
||||
padding: 18px 0 0;
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.el-pagination__total {
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.el-pagination__sizes {
|
||||
display: inline-flex !important;
|
||||
margin: 0 6px 0 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-pagination__sizes .el-select {
|
||||
width: 112px;
|
||||
}
|
||||
|
||||
.el-pagination__sizes .el-select__wrapper {
|
||||
min-height: 32px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 0 1px #dcdfe6 inset;
|
||||
}
|
||||
|
||||
.el-pagination.is-background .btn-prev,
|
||||
.el-pagination.is-background .btn-next,
|
||||
.el-pagination.is-background .el-pager li,
|
||||
.el-pagination .btn-prev,
|
||||
.el-pagination .btn-next,
|
||||
.el-pagination .el-pager li,
|
||||
.erp-pagination-button {
|
||||
min-width: 36px;
|
||||
height: 32px;
|
||||
margin: 0 !important;
|
||||
padding: 0 12px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
color: #303133;
|
||||
line-height: 30px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.el-pagination .btn-prev,
|
||||
.el-pagination .btn-next,
|
||||
.erp-pagination-first,
|
||||
.erp-pagination-last {
|
||||
min-width: 54px;
|
||||
}
|
||||
|
||||
.el-pagination .el-pager {
|
||||
gap: 6px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.el-pagination .btn-prev .el-icon,
|
||||
.el-pagination .btn-next .el-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.el-pagination .btn-prev::before {
|
||||
content: '上一页';
|
||||
}
|
||||
|
||||
.el-pagination .btn-next::before {
|
||||
content: '下一页';
|
||||
}
|
||||
|
||||
.el-pagination.is-background .el-pager li.is-active,
|
||||
.el-pagination .el-pager li.is-active {
|
||||
border-color: var(--el-color-primary);
|
||||
background-color: var(--el-color-primary);
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.el-pagination.is-background .btn-prev:hover,
|
||||
.el-pagination.is-background .btn-next:hover,
|
||||
.el-pagination.is-background .el-pager li:hover,
|
||||
.el-pagination .btn-prev:hover,
|
||||
.el-pagination .btn-next:hover,
|
||||
.el-pagination .el-pager li:hover,
|
||||
.erp-pagination-button:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-pagination.is-background .el-pager li.is-active:hover,
|
||||
.el-pagination .el-pager li.is-active:hover,
|
||||
.erp-pagination-jump:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.el-pagination .btn-prev:disabled,
|
||||
.el-pagination .btn-prev.is-disabled,
|
||||
.el-pagination .btn-next:disabled,
|
||||
.el-pagination .btn-next.is-disabled,
|
||||
.erp-pagination-button:disabled {
|
||||
border-color: #ebeef5;
|
||||
background: #fff;
|
||||
color: #c0c4cc;
|
||||
box-shadow: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.el-pagination__jump {
|
||||
margin-left: 6px;
|
||||
color: #606266;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.el-pagination__goto {
|
||||
margin-right: 0;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.el-pagination__goto::before {
|
||||
content: '第';
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.el-pagination__editor.el-input {
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
.el-pagination__editor .el-input__wrapper {
|
||||
height: 32px;
|
||||
padding: 0 6px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 0 1px #dcdfe6 inset;
|
||||
}
|
||||
|
||||
.el-pagination__editor .el-input__inner {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.el-pagination__classifier {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.erp-pagination-button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.erp-pagination-jump {
|
||||
min-width: 60px;
|
||||
border-color: var(--el-color-primary);
|
||||
background: var(--el-color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.avue-crud__pagination .el-pagination,
|
||||
.empty-pagination .el-pagination,
|
||||
.el-pagination {
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,16 +46,16 @@
|
||||
|
||||
.avue-logo {
|
||||
width:100%;
|
||||
height:50px;
|
||||
line-height: 50px;
|
||||
height:60px;
|
||||
line-height: 60px;
|
||||
background:#fff;
|
||||
color:#333;
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) !important;
|
||||
}
|
||||
|
||||
.avue-top{
|
||||
height:50px;
|
||||
line-height: 50px;
|
||||
height:60px;
|
||||
line-height: 60px;
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) !important;
|
||||
.top-bar__left i, .top-bar__right i{
|
||||
line-height: 50px;
|
||||
@@ -74,7 +74,7 @@
|
||||
}
|
||||
}
|
||||
.avue-main{
|
||||
background-color: rgb(249,250,251);
|
||||
background-color: #f4f6fa;
|
||||
}
|
||||
.avue-tags {
|
||||
margin: 5px 6px 8px 6px;
|
||||
|
||||
@@ -33,3 +33,50 @@
|
||||
|
||||
//beautiful
|
||||
@use './beautiful.scss';
|
||||
|
||||
.theme-white,
|
||||
.theme-star,
|
||||
.theme-vip,
|
||||
.theme-d2,
|
||||
.theme-bule,
|
||||
.theme-iview,
|
||||
.theme-go,
|
||||
.theme-hey,
|
||||
.theme-dark,
|
||||
.theme-lte,
|
||||
.theme-cool,
|
||||
.theme-beautiful {
|
||||
.avue-logo {
|
||||
background: var(--el-color-primary) !important;
|
||||
background-color: var(--el-color-primary) !important;
|
||||
color: #fff;
|
||||
|
||||
.avue-logo_title,
|
||||
.avue-logo_subtitle,
|
||||
.logo-title {
|
||||
color: #fff;
|
||||
background-image: none;
|
||||
-webkit-text-fill-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avue-layout:not(.avue-layout--horizontal) {
|
||||
.avue-sidebar {
|
||||
.el-menu-item,
|
||||
.el-sub-menu__title {
|
||||
&.is-active {
|
||||
background-color: #E6EAED !important;
|
||||
|
||||
&::before {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
i,
|
||||
span {
|
||||
color: #333 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
|
||||
.avue-breadcrumb {
|
||||
height: 100%;
|
||||
color: #fff;
|
||||
|
||||
i {
|
||||
color: #fff;
|
||||
font-size: 30px !important;
|
||||
}
|
||||
|
||||
@@ -130,7 +132,7 @@
|
||||
height: 100%;
|
||||
padding-left: 50px;
|
||||
box-sizing: border-box;
|
||||
background: url('/img/系统首页顶部背景图.png') center center / cover no-repeat;
|
||||
background: var(--el-color-primary);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@@ -141,7 +143,7 @@
|
||||
.avue-logo {
|
||||
height: variables.$top_height;
|
||||
line-height: variables.$top_height;
|
||||
background-color: #031527;
|
||||
background-color: var(--el-color-primary);
|
||||
font-size: 20px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
@@ -153,6 +155,7 @@
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&_subtitle {
|
||||
|
||||
54
src/utils/coordinate.js
Normal file
54
src/utils/coordinate.js
Normal file
@@ -0,0 +1,54 @@
|
||||
export const coordinateInputPattern = /^-?\d+(\.\d{0,6})?$/;
|
||||
|
||||
const coordinateRanges = {
|
||||
longitude: {
|
||||
label: '经度',
|
||||
min: -180,
|
||||
max: 180,
|
||||
},
|
||||
latitude: {
|
||||
label: '纬度',
|
||||
min: -90,
|
||||
max: 90,
|
||||
},
|
||||
};
|
||||
|
||||
export const normalizeCoordinateInput = value => {
|
||||
const rawValue = String(value ?? '').replace(/[^\d.-]/g, '');
|
||||
const isNegative = rawValue.startsWith('-');
|
||||
const unsignedValue = rawValue.replace(/-/g, '');
|
||||
if (!unsignedValue) {
|
||||
return isNegative ? '-' : '';
|
||||
}
|
||||
const [integerPart, ...decimalParts] = unsignedValue.split('.');
|
||||
const sign = isNegative ? '-' : '';
|
||||
if (!decimalParts.length) {
|
||||
return `${sign}${integerPart}`;
|
||||
}
|
||||
return `${sign}${integerPart || '0'}.${decimalParts.join('').slice(0, 6)}`;
|
||||
};
|
||||
|
||||
export const isCoordinateInput = value => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
return true;
|
||||
}
|
||||
return coordinateInputPattern.test(String(value));
|
||||
};
|
||||
|
||||
export const getCoordinateValidationMessage = (value, type, required = false) => {
|
||||
const range = coordinateRanges[type];
|
||||
if (!range) {
|
||||
return '';
|
||||
}
|
||||
if (value === undefined || value === null || value === '') {
|
||||
return required ? `${range.label}不能为空` : '';
|
||||
}
|
||||
if (!isCoordinateInput(value)) {
|
||||
return `${range.label}最多保留6位小数`;
|
||||
}
|
||||
const numberValue = Number(value);
|
||||
if (numberValue < range.min || numberValue > range.max) {
|
||||
return `${range.label}范围为 ${range.min} 到 ${range.max}`;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
136
src/utils/dialog-form-label.js
Normal file
136
src/utils/dialog-form-label.js
Normal file
@@ -0,0 +1,136 @@
|
||||
const FORM_SELECTOR = '.el-dialog .el-form';
|
||||
const ITEM_SELECTOR = '.el-form-item';
|
||||
const COLUMN_TOLERANCE = 8;
|
||||
const REQUIRED_MARK_WIDTH = 12;
|
||||
|
||||
let textMeasureEl;
|
||||
|
||||
const isVisible = element => {
|
||||
if (!element) return false;
|
||||
const rect = element.getBoundingClientRect();
|
||||
return rect.width > 0 && rect.height > 0;
|
||||
};
|
||||
|
||||
const getDirectLabel = item => {
|
||||
return Array.from(item.children).find(child => child.classList.contains('el-form-item__label'));
|
||||
};
|
||||
|
||||
const getColumnLeft = item => {
|
||||
const column = item.closest('.el-col');
|
||||
const target = column || item;
|
||||
return Math.round(target.getBoundingClientRect().left);
|
||||
};
|
||||
|
||||
const findColumnGroup = (groups, left) => {
|
||||
return groups.find(group => Math.abs(group.left - left) <= COLUMN_TOLERANCE);
|
||||
};
|
||||
|
||||
const getTextMeasureEl = () => {
|
||||
if (textMeasureEl) return textMeasureEl;
|
||||
textMeasureEl = document.createElement('span');
|
||||
textMeasureEl.style.position = 'fixed';
|
||||
textMeasureEl.style.left = '-9999px';
|
||||
textMeasureEl.style.top = '-9999px';
|
||||
textMeasureEl.style.visibility = 'hidden';
|
||||
textMeasureEl.style.whiteSpace = 'nowrap';
|
||||
textMeasureEl.style.pointerEvents = 'none';
|
||||
document.body.appendChild(textMeasureEl);
|
||||
return textMeasureEl;
|
||||
};
|
||||
|
||||
const measureLabelWidth = (label, item) => {
|
||||
const text = String(label.textContent || '').trim();
|
||||
if (!text) return 0;
|
||||
|
||||
const style = window.getComputedStyle(label);
|
||||
const measurer = getTextMeasureEl();
|
||||
measurer.style.font = style.font;
|
||||
measurer.style.letterSpacing = style.letterSpacing;
|
||||
measurer.textContent = text;
|
||||
|
||||
const paddingLeft = Number.parseFloat(style.paddingLeft) || 0;
|
||||
const paddingRight = Number.parseFloat(style.paddingRight) || 0;
|
||||
const requiredWidth = item.classList.contains('is-required') ? REQUIRED_MARK_WIDTH : 0;
|
||||
return Math.ceil(measurer.getBoundingClientRect().width + paddingLeft + paddingRight + requiredWidth);
|
||||
};
|
||||
|
||||
const collectLabelGroups = form => {
|
||||
const groups = [];
|
||||
const items = Array.from(form.querySelectorAll(ITEM_SELECTOR));
|
||||
|
||||
items.forEach(item => {
|
||||
if (!isVisible(item) || item.closest('.avue-form__menu')) return;
|
||||
|
||||
const label = getDirectLabel(item);
|
||||
if (!label || !isVisible(label)) return;
|
||||
|
||||
const width = measureLabelWidth(label, item);
|
||||
if (!width) return;
|
||||
|
||||
const left = getColumnLeft(item);
|
||||
let group = findColumnGroup(groups, left);
|
||||
if (!group) {
|
||||
group = {
|
||||
left,
|
||||
labels: [],
|
||||
width: 0,
|
||||
};
|
||||
groups.push(group);
|
||||
}
|
||||
|
||||
group.labels.push(label);
|
||||
group.width = Math.max(group.width, width);
|
||||
});
|
||||
|
||||
return groups;
|
||||
};
|
||||
|
||||
const alignFormLabelsByColumn = form => {
|
||||
const groups = collectLabelGroups(form);
|
||||
|
||||
groups.forEach(group => {
|
||||
group.labels.forEach(label => {
|
||||
const width = `${group.width}px`;
|
||||
if (label.style.width !== width) {
|
||||
label.style.width = width;
|
||||
}
|
||||
if (label.style.flexBasis !== width) {
|
||||
label.style.flexBasis = width;
|
||||
}
|
||||
if (label.style.justifyContent !== 'flex-end') {
|
||||
label.style.justifyContent = 'flex-end';
|
||||
}
|
||||
if (label.style.textAlign !== 'right') {
|
||||
label.style.textAlign = 'right';
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const alignAllDialogFormLabels = () => {
|
||||
document.querySelectorAll(FORM_SELECTOR).forEach(alignFormLabelsByColumn);
|
||||
};
|
||||
|
||||
export const setupDialogFormLabelAlign = () => {
|
||||
if (typeof window === 'undefined' || typeof document === 'undefined') return;
|
||||
|
||||
let frameId = 0;
|
||||
const scheduleAlign = () => {
|
||||
if (frameId) return;
|
||||
frameId = window.requestAnimationFrame(() => {
|
||||
frameId = 0;
|
||||
alignAllDialogFormLabels();
|
||||
});
|
||||
};
|
||||
|
||||
scheduleAlign();
|
||||
window.addEventListener('resize', scheduleAlign);
|
||||
|
||||
const observer = new MutationObserver(scheduleAlign);
|
||||
observer.observe(document.body, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class', 'style'],
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
};
|
||||
76
src/utils/import-excel.js
Normal file
76
src/utils/import-excel.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import { importBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
const excelPattern = /\.(xls|xlsx)$/;
|
||||
|
||||
const isExcelResponse = res => {
|
||||
const contentType = res.headers['content-type'] || res.data.type || '';
|
||||
return contentType.includes('application/vnd.ms-excel');
|
||||
};
|
||||
|
||||
const parseBlobJson = async blob => {
|
||||
const text = await blob.text();
|
||||
return text ? JSON.parse(text) : {};
|
||||
};
|
||||
|
||||
export const openImportDialog = (vm, businessName, onSuccess) => {
|
||||
const column = vm.findColumn(vm.excelOption.column, 'excelFile');
|
||||
column.httpRequest = (option, uploadColumn) =>
|
||||
handleImportExcel(vm, option, uploadColumn, businessName, onSuccess);
|
||||
vm.excelBox = true;
|
||||
};
|
||||
|
||||
export const handleImportExcel = async (vm, option, column, businessName, onSuccess) => {
|
||||
const file = option.file;
|
||||
const fileName = file && file.name ? file.name.toLowerCase() : '';
|
||||
if (!excelPattern.test(fileName)) {
|
||||
vm.$message.error('请上传 .xls,.xlsx 标准格式文件');
|
||||
if (typeof option.onError === 'function') {
|
||||
option.onError(new Error('请上传 .xls,.xlsx 标准格式文件'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
NProgress.start();
|
||||
try {
|
||||
const res = await importBlob(column.action, file);
|
||||
if (isExcelResponse(res)) {
|
||||
downloadXls(
|
||||
res.data,
|
||||
`${businessName}导入失败明细${vm.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`
|
||||
);
|
||||
vm.$message.warning('部分数据导入失败,已下载失败明细');
|
||||
} else {
|
||||
const result = await parseBlobJson(res.data);
|
||||
if (result.code !== 200) {
|
||||
vm.$message.error(result.msg || '导入失败');
|
||||
if (typeof option.onError === 'function') {
|
||||
option.onError(new Error(result.msg || '导入失败'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
vm.$message.success(result.msg || '导入数据成功');
|
||||
}
|
||||
vm.excelBox = false;
|
||||
vm.excelForm = {};
|
||||
if (typeof onSuccess === 'function') {
|
||||
onSuccess();
|
||||
} else if (typeof vm.onLoad === 'function') {
|
||||
vm.onLoad(vm.page, vm.query);
|
||||
} else if (typeof vm.initTree === 'function') {
|
||||
vm.initTree();
|
||||
}
|
||||
if (typeof option.onSuccess === 'function') {
|
||||
option.onSuccess(res);
|
||||
}
|
||||
} catch (error) {
|
||||
vm.$message.error(error.message || '导入失败');
|
||||
if (typeof option.onError === 'function') {
|
||||
option.onError(error);
|
||||
}
|
||||
} finally {
|
||||
NProgress.done();
|
||||
}
|
||||
};
|
||||
167
src/utils/pagination.js
Normal file
167
src/utils/pagination.js
Normal file
@@ -0,0 +1,167 @@
|
||||
const FIRST_BUTTON_CLASS = 'erp-pagination-first';
|
||||
const LAST_BUTTON_CLASS = 'erp-pagination-last';
|
||||
const JUMP_BUTTON_CLASS = 'erp-pagination-jump';
|
||||
const DEFAULT_PAGE_SIZE = 10;
|
||||
const DEFAULT_PAGE_SIZES = [10, 20, 50, 100];
|
||||
const PAGE_KEY_PATTERN = /(^page$|Page$|^page[A-Z])/;
|
||||
|
||||
const toNumber = value => {
|
||||
const number = Number(value);
|
||||
return Number.isFinite(number) ? number : 0;
|
||||
};
|
||||
|
||||
const getNativeInputValueSetter = input => {
|
||||
const prototype = Object.getPrototypeOf(input);
|
||||
const descriptor = Object.getOwnPropertyDescriptor(prototype, 'value');
|
||||
return descriptor && descriptor.set;
|
||||
};
|
||||
|
||||
const setInputValue = (input, value) => {
|
||||
const setter = getNativeInputValueSetter(input);
|
||||
if (setter) {
|
||||
setter.call(input, value);
|
||||
} else {
|
||||
input.value = value;
|
||||
}
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
};
|
||||
|
||||
const getPaginationState = pagination => {
|
||||
const active = pagination.querySelector('.el-pager li.is-active');
|
||||
const jumpInput = pagination.querySelector('.el-pagination__editor input');
|
||||
const currentPage = toNumber(
|
||||
active && active.textContent ? active.textContent.trim() : jumpInput?.value
|
||||
);
|
||||
const pageCount = toNumber(jumpInput?.getAttribute('max'));
|
||||
return {
|
||||
currentPage: currentPage || 1,
|
||||
pageCount: pageCount || 1,
|
||||
jumpInput,
|
||||
};
|
||||
};
|
||||
|
||||
const changePage = (pagination, page) => {
|
||||
const { currentPage, pageCount, jumpInput } = getPaginationState(pagination);
|
||||
const targetPage = Math.min(Math.max(toNumber(page), 1), pageCount);
|
||||
if (!targetPage || targetPage === currentPage) return;
|
||||
|
||||
const targetPager = Array.from(pagination.querySelectorAll('.el-pager li.number')).find(
|
||||
item => toNumber(item.textContent && item.textContent.trim()) === targetPage
|
||||
);
|
||||
if (targetPager) {
|
||||
targetPager.click();
|
||||
return;
|
||||
}
|
||||
|
||||
if (jumpInput) {
|
||||
setInputValue(jumpInput, targetPage);
|
||||
jumpInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
jumpInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
|
||||
}
|
||||
};
|
||||
|
||||
const createPaginationButton = (className, text, onClick) => {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = `erp-pagination-button ${className}`;
|
||||
button.textContent = text;
|
||||
button.addEventListener('click', onClick);
|
||||
return button;
|
||||
};
|
||||
|
||||
const setButtonDisabled = (button, disabled) => {
|
||||
if (!button || button.disabled === disabled) return;
|
||||
button.disabled = disabled;
|
||||
};
|
||||
|
||||
const isPageConfig = value =>
|
||||
value &&
|
||||
typeof value === 'object' &&
|
||||
('pageSize' in value || 'currentPage' in value || 'total' in value);
|
||||
|
||||
const normalizePageConfig = page => {
|
||||
if (!isPageConfig(page)) return;
|
||||
page.pageSize = DEFAULT_PAGE_SIZES.includes(toNumber(page.pageSize))
|
||||
? toNumber(page.pageSize)
|
||||
: DEFAULT_PAGE_SIZE;
|
||||
page.pageSizes = DEFAULT_PAGE_SIZES;
|
||||
};
|
||||
|
||||
const normalizeComponentPageConfigs = pagination => {
|
||||
let component = pagination.__vueParentComponent;
|
||||
while (component) {
|
||||
[component.data, component.setupState].forEach(state => {
|
||||
if (!state) return;
|
||||
Object.keys(state)
|
||||
.filter(key => PAGE_KEY_PATTERN.test(key))
|
||||
.forEach(key => normalizePageConfig(state[key]));
|
||||
});
|
||||
component = component.parent;
|
||||
}
|
||||
};
|
||||
|
||||
const updatePaginationButtons = pagination => {
|
||||
const { currentPage, pageCount, jumpInput } = getPaginationState(pagination);
|
||||
const firstButton = pagination.querySelector(`.${FIRST_BUTTON_CLASS}`);
|
||||
const lastButton = pagination.querySelector(`.${LAST_BUTTON_CLASS}`);
|
||||
const jumpButton = pagination.querySelector(`.${JUMP_BUTTON_CLASS}`);
|
||||
|
||||
setButtonDisabled(firstButton, currentPage <= 1);
|
||||
setButtonDisabled(lastButton, currentPage >= pageCount);
|
||||
setButtonDisabled(jumpButton, !jumpInput || jumpInput.disabled);
|
||||
};
|
||||
|
||||
const enhancePagination = pagination => {
|
||||
const prevButton = pagination.querySelector('.btn-prev');
|
||||
const nextButton = pagination.querySelector('.btn-next');
|
||||
const jumpWrapper = pagination.querySelector('.el-pagination__jump');
|
||||
|
||||
normalizeComponentPageConfigs(pagination);
|
||||
|
||||
if (prevButton && !pagination.querySelector(`.${FIRST_BUTTON_CLASS}`)) {
|
||||
const firstButton = createPaginationButton(FIRST_BUTTON_CLASS, '首页', () => {
|
||||
changePage(pagination, 1);
|
||||
});
|
||||
prevButton.before(firstButton);
|
||||
}
|
||||
|
||||
if (nextButton && !pagination.querySelector(`.${LAST_BUTTON_CLASS}`)) {
|
||||
const lastButton = createPaginationButton(LAST_BUTTON_CLASS, '尾页', () => {
|
||||
const { pageCount } = getPaginationState(pagination);
|
||||
changePage(pagination, pageCount);
|
||||
});
|
||||
nextButton.after(lastButton);
|
||||
}
|
||||
|
||||
if (jumpWrapper && !pagination.querySelector(`.${JUMP_BUTTON_CLASS}`)) {
|
||||
const jumpButton = createPaginationButton(JUMP_BUTTON_CLASS, '跳转', () => {
|
||||
const { jumpInput } = getPaginationState(pagination);
|
||||
if (!jumpInput) return;
|
||||
changePage(pagination, jumpInput.value);
|
||||
});
|
||||
jumpWrapper.after(jumpButton);
|
||||
}
|
||||
|
||||
updatePaginationButtons(pagination);
|
||||
};
|
||||
|
||||
const enhanceAllPaginations = () => {
|
||||
document.querySelectorAll('.el-pagination').forEach(enhancePagination);
|
||||
};
|
||||
|
||||
export const setupPaginationEnhancer = () => {
|
||||
if (typeof window === 'undefined' || typeof document === 'undefined') return;
|
||||
|
||||
const scheduleEnhance = () => {
|
||||
window.requestAnimationFrame(enhanceAllPaginations);
|
||||
};
|
||||
|
||||
scheduleEnhance();
|
||||
const observer = new MutationObserver(scheduleEnhance);
|
||||
observer.observe(document.body, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class', 'disabled'],
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
};
|
||||
@@ -72,7 +72,7 @@
|
||||
v-for="(item, index) in apiPathList"
|
||||
:key="index"
|
||||
:gutter="8"
|
||||
style="margin-bottom: 8px"
|
||||
style="margin-bottom: 16px"
|
||||
>
|
||||
<el-col :span="22">
|
||||
<el-input
|
||||
@@ -106,7 +106,7 @@
|
||||
v-for="(item, index) in extParamsList"
|
||||
:key="index"
|
||||
:gutter="8"
|
||||
style="margin-bottom: 8px"
|
||||
style="margin-bottom: 16px"
|
||||
>
|
||||
<el-col :span="8">
|
||||
<el-input v-model="item.key" placeholder="参数名" :disabled="type === 'view'" />
|
||||
@@ -330,7 +330,10 @@ export default {
|
||||
menuWidth: 350,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
@@ -493,7 +496,10 @@ export default {
|
||||
tip: false,
|
||||
header: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
|
||||
@@ -157,13 +157,16 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
searchLabelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
menuWidth: 250,
|
||||
dialogWidth: 900,
|
||||
dialogClickModal: false,
|
||||
@@ -263,7 +266,10 @@ export default {
|
||||
tip: false,
|
||||
simplePage: true,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
@@ -324,12 +330,15 @@ export default {
|
||||
optionScope: {
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
menuWidth: 250,
|
||||
dialogWidth: 900,
|
||||
dialogClickModal: false,
|
||||
|
||||
@@ -158,13 +158,16 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
searchLabelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
menuWidth: 250,
|
||||
dialogWidth: 900,
|
||||
dialogClickModal: false,
|
||||
@@ -300,7 +303,10 @@ export default {
|
||||
tip: false,
|
||||
simplePage: true,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
@@ -363,12 +369,15 @@ export default {
|
||||
optionScope: {
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
labelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
menuWidth: 250,
|
||||
dialogWidth: 900,
|
||||
dialogClickModal: false,
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
v-for="(item, index) in limitPatternList"
|
||||
:key="index"
|
||||
:gutter="8"
|
||||
style="margin-bottom: 8px"
|
||||
style="margin-bottom: 16px"
|
||||
>
|
||||
<el-col :span="22">
|
||||
<el-input
|
||||
@@ -208,9 +208,12 @@ export default {
|
||||
dialogWidth: 700,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
labelWidth: 110,
|
||||
searchLabelWidth: 90,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
@@ -360,7 +363,10 @@ export default {
|
||||
tip: false,
|
||||
header: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
|
||||
@@ -242,14 +242,17 @@ export default {
|
||||
tip: false,
|
||||
simplePage: true,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
viewBtn: true,
|
||||
labelWidth: 120,
|
||||
searchLabelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
menuWidth: 350,
|
||||
dialogWidth: 600,
|
||||
dialogClickModal: false,
|
||||
|
||||
@@ -79,15 +79,45 @@
|
||||
{{ row.status === 1 ? '停用' : '启用' }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #districtCode-form>
|
||||
<el-select
|
||||
v-model="form.districtCode"
|
||||
clearable
|
||||
filterable
|
||||
:loading="districtLoading"
|
||||
:disabled="!form.cityCode"
|
||||
:placeholder="form.cityCode ? '请选择区县' : '请先选择城市'"
|
||||
style="width: 100%"
|
||||
@change="handleDistrictChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in districtOptions"
|
||||
:key="item.id"
|
||||
:label="item.title"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
<template #longitude-form>
|
||||
<el-input
|
||||
v-model="form.longitude"
|
||||
clearable
|
||||
placeholder="请输入经度"
|
||||
@input="handleCoordinateInput('longitude', $event)"
|
||||
/>
|
||||
</template>
|
||||
<template #latitude-form>
|
||||
<el-input
|
||||
v-model="form.latitude"
|
||||
clearable
|
||||
placeholder="请输入纬度"
|
||||
@input="handleCoordinateInput('latitude', $event)"
|
||||
/>
|
||||
</template>
|
||||
</avue-crud>
|
||||
|
||||
<el-dialog title="空港机场主数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form
|
||||
:option="excelOption"
|
||||
v-model="excelForm"
|
||||
:upload-before="uploadBefore"
|
||||
:upload-after="uploadAfter"
|
||||
>
|
||||
<avue-form :option="excelOption" v-model="excelForm">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
@@ -104,8 +134,10 @@ import { getLazyTree } from '@/api/base/region';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { openImportDialog } from '@/utils/import-excel';
|
||||
import { formatUpdateUserName } from '@/utils/audit';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { getCoordinateValidationMessage, normalizeCoordinateInput } from '@/utils/coordinate';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
@@ -128,19 +160,17 @@ export default {
|
||||
}
|
||||
};
|
||||
const validateLongitude = (rule, value, callback) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback();
|
||||
} else if (Number(value) < -180 || Number(value) > 180) {
|
||||
callback(new Error('经度范围为 -180 到 180'));
|
||||
const message = getCoordinateValidationMessage(value, 'longitude');
|
||||
if (message) {
|
||||
callback(new Error(message));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const validateLatitude = (rule, value, callback) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback();
|
||||
} else if (Number(value) < -90 || Number(value) > 90) {
|
||||
callback(new Error('纬度范围为 -90 到 90'));
|
||||
const message = getCoordinateValidationMessage(value, 'latitude');
|
||||
if (message) {
|
||||
callback(new Error(message));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
@@ -154,6 +184,9 @@ export default {
|
||||
excelForm: {},
|
||||
provinceOptions: [],
|
||||
cityOptions: [],
|
||||
districtOptions: [],
|
||||
districtLoading: false,
|
||||
regionInitializing: false,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
@@ -165,11 +198,15 @@ export default {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'right',
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
labelWidth: 120,
|
||||
searchLabelWidth: 120,
|
||||
searchMenuSpan: 6,
|
||||
labelWidth: 'auto',
|
||||
searchLabelWidth: 160,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
indexLabel: '序号',
|
||||
@@ -185,8 +222,8 @@ export default {
|
||||
label: '编码',
|
||||
prop: 'code',
|
||||
search: true,
|
||||
addDisabled: true,
|
||||
editDisabled: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
placeholder: '系统自动生成',
|
||||
searchPlaceholder: '请输入编码',
|
||||
},
|
||||
@@ -271,14 +308,35 @@ export default {
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '行政区划',
|
||||
prop: 'regionName',
|
||||
search: true,
|
||||
label: '所属区县',
|
||||
prop: 'districtCode',
|
||||
type: 'select',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
props: {
|
||||
label: 'title',
|
||||
value: 'id',
|
||||
},
|
||||
dicData: [],
|
||||
filterable: true,
|
||||
rules: [{ required: true, message: '请选择区县', trigger: 'change' }],
|
||||
change: ({ value }) => this.handleDistrictChange(value),
|
||||
},
|
||||
{
|
||||
label: '所属区县',
|
||||
prop: 'districtName',
|
||||
minWidth: 140,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '行政区划编号',
|
||||
prop: 'regionCode',
|
||||
minWidth: 160,
|
||||
overHidden: false,
|
||||
maxlength: 128,
|
||||
showWordLimit: true,
|
||||
rules: [{ max: 128, message: '行政区划不能超过128字', trigger: 'blur' }],
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
rules: [{ max: 32, message: '行政区划编码不能超过32字', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '详细地址',
|
||||
@@ -295,20 +353,24 @@ export default {
|
||||
{
|
||||
label: '经度',
|
||||
prop: 'longitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
formslot: true,
|
||||
minWidth: 130,
|
||||
overHidden: false,
|
||||
rules: [{ validator: validateLongitude, trigger: 'blur' }],
|
||||
rules: [
|
||||
{ required: true, message: '请输入经度', trigger: 'blur' },
|
||||
{ validator: validateLongitude, trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '纬度',
|
||||
prop: 'latitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
formslot: true,
|
||||
minWidth: 130,
|
||||
overHidden: false,
|
||||
rules: [{ validator: validateLatitude, trigger: 'blur' }],
|
||||
rules: [
|
||||
{ required: true, message: '请输入纬度', trigger: 'blur' },
|
||||
{ validator: validateLatitude, trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '数据来源',
|
||||
@@ -317,6 +379,8 @@ export default {
|
||||
search: true,
|
||||
minWidth: 120,
|
||||
overHidden: false,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
dicData: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '初始导入', value: '初始导入' },
|
||||
@@ -333,6 +397,8 @@ export default {
|
||||
dataType: 'number',
|
||||
minWidth: 110,
|
||||
overHidden: false,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
dicData: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '启用', value: 1 },
|
||||
@@ -383,6 +449,7 @@ export default {
|
||||
],
|
||||
},
|
||||
excelOption: {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
@@ -419,7 +486,9 @@ export default {
|
||||
},
|
||||
isAdmin() {
|
||||
const authority = this.userInfo && this.userInfo.authority;
|
||||
return Array.isArray(authority) ? authority.includes('admin') : String(authority || '').includes('admin');
|
||||
return Array.isArray(authority)
|
||||
? authority.includes('admin')
|
||||
: String(authority || '').includes('admin');
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
@@ -441,7 +510,7 @@ export default {
|
||||
},
|
||||
loadProvinceOptions() {
|
||||
getLazyTree(DEFAULT_COUNTRY_CODE).then(res => {
|
||||
this.provinceOptions = res.data.data;
|
||||
this.provinceOptions = (res.data.data || []).map(this.normalizeRegionOption);
|
||||
this.findColumn('provinceCode').dicData = this.provinceOptions;
|
||||
});
|
||||
},
|
||||
@@ -452,10 +521,60 @@ export default {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return getLazyTree(provinceCode).then(res => {
|
||||
this.cityOptions = res.data.data;
|
||||
this.cityOptions = (res.data.data || []).map(this.normalizeRegionOption);
|
||||
this.findColumn('cityCode').dicData = this.cityOptions;
|
||||
});
|
||||
},
|
||||
loadDistrictOptions(cityCode) {
|
||||
if (!cityCode) {
|
||||
this.districtOptions = [];
|
||||
this.findColumn('districtCode').dicData = [];
|
||||
return Promise.resolve();
|
||||
}
|
||||
this.districtLoading = true;
|
||||
return getLazyTree(cityCode)
|
||||
.then(res => {
|
||||
this.districtOptions = (res.data.data || []).map(this.normalizeRegionOption);
|
||||
this.findColumn('districtCode').dicData = this.districtOptions;
|
||||
})
|
||||
.finally(() => {
|
||||
this.districtLoading = false;
|
||||
});
|
||||
},
|
||||
normalizeRegionOption(region) {
|
||||
return {
|
||||
...region,
|
||||
id: region.id === undefined || region.id === null ? region.id : String(region.id),
|
||||
};
|
||||
},
|
||||
normalizeRegionForm(row) {
|
||||
return {
|
||||
...row,
|
||||
provinceCode:
|
||||
row.provinceCode === undefined || row.provinceCode === null
|
||||
? row.provinceCode
|
||||
: String(row.provinceCode),
|
||||
cityCode:
|
||||
row.cityCode === undefined || row.cityCode === null ? row.cityCode : String(row.cityCode),
|
||||
districtCode:
|
||||
row.districtCode === undefined || row.districtCode === null
|
||||
? row.districtCode
|
||||
: String(row.districtCode),
|
||||
};
|
||||
},
|
||||
ensureDistrictOption(row) {
|
||||
if (!row.districtCode || !row.districtName) return;
|
||||
const exists = this.districtOptions.some(item => item.id === row.districtCode);
|
||||
if (exists) return;
|
||||
this.districtOptions = [
|
||||
...this.districtOptions,
|
||||
{
|
||||
id: row.districtCode,
|
||||
title: row.districtName,
|
||||
},
|
||||
];
|
||||
this.findColumn('districtCode').dicData = this.districtOptions;
|
||||
},
|
||||
handleIataChange(value) {
|
||||
this.form.iataCode = String(value || '')
|
||||
.toUpperCase()
|
||||
@@ -472,22 +591,67 @@ export default {
|
||||
handleProvinceChange(value) {
|
||||
const province = this.provinceOptions.find(item => item.id === value);
|
||||
this.form.provinceName = province ? province.title : '';
|
||||
if (this.regionInitializing) {
|
||||
this.loadCityOptions(value);
|
||||
return;
|
||||
}
|
||||
this.form.cityCode = undefined;
|
||||
this.form.cityName = '';
|
||||
this.form.districtCode = undefined;
|
||||
this.form.districtName = '';
|
||||
this.form.regionCode = '';
|
||||
this.form.regionName = '';
|
||||
this.loadCityOptions(value);
|
||||
this.loadDistrictOptions();
|
||||
},
|
||||
handleCityChange(value) {
|
||||
const city = this.cityOptions.find(item => item.id === value);
|
||||
this.form.cityName = city ? city.title : '';
|
||||
if (this.regionInitializing) {
|
||||
this.loadDistrictOptions(value);
|
||||
return;
|
||||
}
|
||||
this.form.districtCode = undefined;
|
||||
this.form.districtName = '';
|
||||
this.form.regionCode = '';
|
||||
this.form.regionName = '';
|
||||
this.loadDistrictOptions(value);
|
||||
},
|
||||
handleDistrictChange(value) {
|
||||
const district = this.districtOptions.find(item => item.id === value);
|
||||
this.form.districtName = district ? district.title : '';
|
||||
this.form.regionCode = value || '';
|
||||
this.form.regionName = district ? district.title : '';
|
||||
},
|
||||
handleCoordinateInput(prop, value) {
|
||||
this.form[prop] = normalizeCoordinateInput(value);
|
||||
},
|
||||
normalizeRow(row) {
|
||||
row.code = row.iataCode ? `JC-${row.iataCode}` : row.code;
|
||||
row.regionCode = String(row.regionCode || '').trim();
|
||||
row.regionName = String(row.regionName || '').trim();
|
||||
row.districtCode = String(row.districtCode || '').trim();
|
||||
row.districtName = String(row.districtName || '').trim();
|
||||
row.detailAddress = String(row.detailAddress || '').trim();
|
||||
row.longitude = normalizeCoordinateInput(row.longitude);
|
||||
row.latitude = normalizeCoordinateInput(row.latitude);
|
||||
if (!row.dataSource) row.dataSource = '手动录入';
|
||||
if (!row.status) row.status = 1;
|
||||
return row;
|
||||
},
|
||||
validateCoordinateFields(row) {
|
||||
const longitudeMessage = getCoordinateValidationMessage(row.longitude, 'longitude', true);
|
||||
if (longitudeMessage) {
|
||||
this.$message.warning(longitudeMessage);
|
||||
return false;
|
||||
}
|
||||
const latitudeMessage = getCoordinateValidationMessage(row.latitude, 'latitude', true);
|
||||
if (latitudeMessage) {
|
||||
this.$message.warning(latitudeMessage);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
validateUnique(row) {
|
||||
const rowId = String(row.id || '');
|
||||
const hasDuplicate = (params, prop) => {
|
||||
@@ -519,6 +683,10 @@ export default {
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const submitRow = this.normalizeRow(row);
|
||||
if (!this.validateCoordinateFields(submitRow)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
this.validateUnique(submitRow)
|
||||
.then(() => submit(submitRow))
|
||||
.then(() => {
|
||||
@@ -530,6 +698,10 @@ export default {
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const submitRow = this.normalizeRow(row);
|
||||
if (!this.validateCoordinateFields(submitRow)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
this.validateUnique(submitRow)
|
||||
.then(() => submit(submitRow))
|
||||
.then(() => {
|
||||
@@ -584,16 +756,30 @@ export default {
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
this.regionInitializing = true;
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
this.loadCityOptions(this.form.provinceCode).then(() => done());
|
||||
const detail = this.normalizeRegionForm(res.data.data || {});
|
||||
this.loadCityOptions(detail.provinceCode)
|
||||
.then(() => this.loadDistrictOptions(detail.cityCode))
|
||||
.then(() => {
|
||||
this.ensureDistrictOption(detail);
|
||||
this.form = detail;
|
||||
done();
|
||||
this.$nextTick(() => {
|
||||
window.setTimeout(() => {
|
||||
this.regionInitializing = false;
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.form.code = '';
|
||||
this.form.dataSource = '手动录入';
|
||||
this.form.status = 1;
|
||||
this.loadCityOptions(this.form.provinceCode).then(() => done());
|
||||
this.loadCityOptions(this.form.provinceCode)
|
||||
.then(() => this.loadDistrictOptions(this.form.cityCode))
|
||||
.then(() => done());
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
@@ -635,23 +821,7 @@ export default {
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadBefore(file, done) {
|
||||
const fileName = file && file.name ? file.name.toLowerCase() : '';
|
||||
if (!/\.(xls|xlsx)$/.test(fileName)) {
|
||||
this.$message.error('请上传 .xls,.xlsx 标准格式文件');
|
||||
return false;
|
||||
}
|
||||
if (typeof done === 'function') {
|
||||
done();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
openImportDialog(this, '空港机场主数据');
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出空港机场主数据?', '提示', {
|
||||
|
||||
705
src/views/base/cargo-type.vue
Normal file
705
src/views/base/cargo-type.vue
Normal file
@@ -0,0 +1,705 @@
|
||||
<template>
|
||||
<basic-container class="cargo-type-page">
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
v-model="form"
|
||||
ref="crud"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
@row-save="rowSave"
|
||||
@row-update="rowUpdate"
|
||||
@row-del="rowDel"
|
||||
@search-change="searchChange"
|
||||
@search-reset="searchReset"
|
||||
@selection-change="selectionChange"
|
||||
@current-change="currentChange"
|
||||
@size-change="sizeChange"
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
plain
|
||||
v-if="permission.cargo_type_import"
|
||||
@click="handleImport"
|
||||
>批量导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="permission.cargo_type_template"
|
||||
@click="handleTemplate"
|
||||
>下载模板
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="permission.cargo_type_export"
|
||||
@click="handleExport"
|
||||
>批量导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="permission.cargo_type_delete"
|
||||
@click="handleDelete"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<template #parentCargoName="{ row }">
|
||||
{{ row.typeLevel === 1 ? '/' : row.parentCargoName || '/' }}
|
||||
</template>
|
||||
<template #parentCargoCode="{ row }">
|
||||
{{ row.typeLevel === 1 ? '/' : row.parentCargoCode || '/' }}
|
||||
</template>
|
||||
<template #createUserName="{ row }">
|
||||
{{ row.createUserName || row.createUser || '' }}
|
||||
</template>
|
||||
|
||||
<template #parentId-form>
|
||||
<el-select
|
||||
v-model="form.parentId"
|
||||
filterable
|
||||
remote
|
||||
clearable
|
||||
:remote-method="loadParentOptions"
|
||||
:loading="parentLoading"
|
||||
:disabled="form.typeLevel !== 2"
|
||||
placeholder="请输入名称或编码搜索"
|
||||
style="width: 100%"
|
||||
@change="handleParentChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in parentOptions"
|
||||
:key="item.id"
|
||||
:label="`${item.cargoName}(${item.cargoCode})`"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<template #menu="{ row, index }">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-view"
|
||||
v-if="permission.cargo_type_view"
|
||||
@click="$refs.crud.rowView(row, index)"
|
||||
>
|
||||
查看
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-edit"
|
||||
v-if="permission.cargo_type_edit"
|
||||
@click="$refs.crud.rowEdit(row, index)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.cargo_type_delete"
|
||||
@click="rowDel(row)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-crud>
|
||||
|
||||
<empty-pagination
|
||||
:page="page"
|
||||
@size-change="sizeChange"
|
||||
@current-change="currentChange"
|
||||
@load="onLoad(page, query)"
|
||||
/>
|
||||
|
||||
<el-dialog title="货物类型导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-form>
|
||||
</el-dialog>
|
||||
</basic-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getDetail,
|
||||
getList,
|
||||
getNextCode,
|
||||
getParentOptions,
|
||||
remove,
|
||||
submit,
|
||||
} from '@/api/base/cargo-type';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { openImportDialog } from '@/utils/import-excel';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
const typeLevelDic = [
|
||||
{ label: '一级货物类型', value: 1 },
|
||||
{ label: '二级货物类型', value: 2 },
|
||||
];
|
||||
|
||||
export default {
|
||||
data() {
|
||||
const validateCargoName = (rule, value, callback) => {
|
||||
const cargoName = String(value || '').trim();
|
||||
if (!cargoName) {
|
||||
callback(new Error('请输入货物类型名称'));
|
||||
} else if (cargoName.length > 50) {
|
||||
callback(new Error('货物类型名称不能超过50字符'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const validateCargoCode = (rule, value, callback) => {
|
||||
const cargoCode = String(value || '').trim();
|
||||
const pattern = this.form.typeLevel === 1 ? /^\d{2}$/ : /^\d{4}$/;
|
||||
if (!pattern.test(cargoCode)) {
|
||||
callback(new Error('货物类型编码格式不正确'));
|
||||
} else if (
|
||||
this.form.typeLevel === 2 &&
|
||||
this.form.parentCargoCode &&
|
||||
!cargoCode.startsWith(this.form.parentCargoCode)
|
||||
) {
|
||||
callback(new Error('二级编码前2位必须与上级货物类型编码一致'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const validateRemark = (rule, value, callback) => {
|
||||
if (String(value || '').length > 200) {
|
||||
callback(new Error('备注不能超过200字'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
loading: true,
|
||||
data: [],
|
||||
excelBox: false,
|
||||
excelForm: {},
|
||||
parentOptions: [],
|
||||
parentLoading: false,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
currentPage: 1,
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 760,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
indexLabel: '序号',
|
||||
indexWidth: 70,
|
||||
viewBtn: false,
|
||||
delBtn: false,
|
||||
editBtn: false,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 220,
|
||||
column: [
|
||||
{
|
||||
label: '类型',
|
||||
prop: 'typeLevel',
|
||||
type: 'radio',
|
||||
dataType: 'number',
|
||||
dicData: typeLevelDic,
|
||||
search: true,
|
||||
value: 1,
|
||||
span: 24,
|
||||
minWidth: 120,
|
||||
rules: [{ required: true, message: '请选择货物类型级别', trigger: 'change' }],
|
||||
change: ({ value }) => this.handleTypeLevelChange(value),
|
||||
},
|
||||
{
|
||||
label: '上级货物类型',
|
||||
prop: 'parentId',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
display: false,
|
||||
span: 24,
|
||||
rules: [{ required: true, message: '请选择上级货物类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '上级货物类型',
|
||||
prop: 'parentCargoName',
|
||||
slot: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 160,
|
||||
},
|
||||
{
|
||||
label: '上级货物类型编码',
|
||||
prop: 'parentCargoCode',
|
||||
slot: true,
|
||||
readonly: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 160,
|
||||
rules: [{ required: true, message: '请选择上级货物类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '货物类型',
|
||||
prop: 'cargoName',
|
||||
minWidth: 150,
|
||||
maxlength: 50,
|
||||
showWordLimit: true,
|
||||
rules: [{ validator: validateCargoName, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '货物类型编码',
|
||||
prop: 'cargoCode',
|
||||
minWidth: 140,
|
||||
search: true,
|
||||
maxlength: 4,
|
||||
rules: [{ validator: validateCargoCode, trigger: 'blur' }],
|
||||
change: ({ value }) => this.handleCargoCodeChange(value),
|
||||
},
|
||||
{
|
||||
label: '创建人',
|
||||
prop: 'createUserName',
|
||||
slot: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
minWidth: 180,
|
||||
maxlength: 200,
|
||||
showWordLimit: true,
|
||||
overHidden: true,
|
||||
rules: [{ validator: validateRemark, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '更新时间',
|
||||
prop: 'updateTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 160,
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
prop: 'createTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 160,
|
||||
},
|
||||
],
|
||||
},
|
||||
excelOption: {
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
{
|
||||
label: '模板上传',
|
||||
prop: 'excelFile',
|
||||
type: 'upload',
|
||||
drag: true,
|
||||
loadText: '模板上传中,请稍等',
|
||||
span: 24,
|
||||
propsHttp: {
|
||||
res: 'data',
|
||||
},
|
||||
headers: {
|
||||
'Blade-Auth': `bearer ${getToken()}`,
|
||||
},
|
||||
tip: '请上传 .xls,.xlsx 标准格式文件',
|
||||
action: '/blade-system/cargo-type/import-cargo-type',
|
||||
},
|
||||
{
|
||||
label: '模板下载',
|
||||
prop: 'excelTemplate',
|
||||
formslot: true,
|
||||
span: 24,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.cargo_type_add, false),
|
||||
};
|
||||
},
|
||||
ids() {
|
||||
return this.selectionList.map(ele => ele.id).join(',');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
findCargoColumn(prop) {
|
||||
return this.option.column.find(item => item.prop === prop) || {};
|
||||
},
|
||||
syncColumnDisplay() {
|
||||
const isChild = this.form.typeLevel === 2;
|
||||
this.findCargoColumn('parentId').display = isChild;
|
||||
this.findCargoColumn('parentCargoCode').display = isChild;
|
||||
this.findCargoColumn('parentCargoCode').addDisplay = isChild;
|
||||
this.findCargoColumn('parentCargoCode').editDisplay = isChild;
|
||||
},
|
||||
handleTypeLevelChange(value) {
|
||||
this.form.typeLevel = value;
|
||||
if (value === 1) {
|
||||
this.form.parentId = undefined;
|
||||
this.form.parentCargoName = '';
|
||||
this.form.parentCargoCode = '';
|
||||
this.form.cargoCode = String(this.form.cargoCode || '')
|
||||
.replace(/\D/g, '')
|
||||
.slice(0, 2);
|
||||
} else {
|
||||
this.form.cargoCode = String(this.form.cargoCode || '')
|
||||
.replace(/\D/g, '')
|
||||
.slice(0, 4);
|
||||
}
|
||||
this.syncColumnDisplay();
|
||||
},
|
||||
handleCargoCodeChange(value) {
|
||||
const maxLength = this.form.typeLevel === 1 ? 2 : 4;
|
||||
this.form.cargoCode = String(value || '')
|
||||
.replace(/\D/g, '')
|
||||
.slice(0, maxLength);
|
||||
},
|
||||
loadParentOptions(keyword = '') {
|
||||
this.parentLoading = true;
|
||||
return getParentOptions(keyword)
|
||||
.then(res => {
|
||||
this.parentOptions = res.data.data || [];
|
||||
})
|
||||
.finally(() => {
|
||||
this.parentLoading = false;
|
||||
});
|
||||
},
|
||||
ensureParentOption(row) {
|
||||
if (!row.parentId || !row.parentCargoName) return;
|
||||
const exists = this.parentOptions.some(item => String(item.id) === String(row.parentId));
|
||||
if (exists) return;
|
||||
this.parentOptions = [
|
||||
...this.parentOptions,
|
||||
{
|
||||
id: row.parentId,
|
||||
cargoName: row.parentCargoName,
|
||||
cargoCode: row.parentCargoCode,
|
||||
},
|
||||
];
|
||||
},
|
||||
handleParentChange(value) {
|
||||
const parent = this.parentOptions.find(item => String(item.id) === String(value));
|
||||
this.form.parentCargoName = parent ? parent.cargoName : '';
|
||||
this.form.parentCargoCode = parent ? parent.cargoCode : '';
|
||||
if (!parent) {
|
||||
this.form.cargoCode = '';
|
||||
return;
|
||||
}
|
||||
getNextCode(parent.cargoCode).then(res => {
|
||||
this.form.cargoCode = res.data.data;
|
||||
});
|
||||
},
|
||||
normalizeRow(row) {
|
||||
row.typeLevel = Number(row.typeLevel || 1);
|
||||
row.cargoName = String(row.cargoName || '').trim();
|
||||
row.cargoCode = String(row.cargoCode || '').trim();
|
||||
row.remark = String(row.remark || '').trim();
|
||||
if (row.typeLevel === 1) {
|
||||
row.parentId = undefined;
|
||||
row.parentCargoName = '';
|
||||
row.parentCargoCode = '';
|
||||
}
|
||||
return row;
|
||||
},
|
||||
validateRow(row) {
|
||||
if (![1, 2].includes(row.typeLevel)) {
|
||||
this.$message.warning('请选择货物类型级别');
|
||||
return false;
|
||||
}
|
||||
if (row.typeLevel === 2 && !row.parentId) {
|
||||
this.$message.warning('请选择上级货物类型');
|
||||
return false;
|
||||
}
|
||||
if (!row.cargoName) {
|
||||
this.$message.warning('请输入货物类型名称');
|
||||
return false;
|
||||
}
|
||||
if (row.cargoName.length > 50) {
|
||||
this.$message.warning('货物类型名称不能超过50字符');
|
||||
return false;
|
||||
}
|
||||
const pattern = row.typeLevel === 1 ? /^\d{2}$/ : /^\d{4}$/;
|
||||
if (!pattern.test(row.cargoCode)) {
|
||||
this.$message.warning('货物类型编码格式不正确');
|
||||
return false;
|
||||
}
|
||||
if (row.typeLevel === 2 && !row.cargoCode.startsWith(row.parentCargoCode)) {
|
||||
this.$message.warning('二级编码前2位必须与上级货物类型编码一致');
|
||||
return false;
|
||||
}
|
||||
if (row.remark.length > 200) {
|
||||
this.$message.warning('备注不能超过200字');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const submitRow = this.normalizeRow(row);
|
||||
if (!this.validateRow(submitRow)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
submit(submitRow).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const submitRow = this.normalizeRow(row);
|
||||
if (!this.validateRow(submitRow)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
submit(submitRow).then(
|
||||
() => {
|
||||
this.onLoad(this.page);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
},
|
||||
rowDel(row) {
|
||||
this.$confirm('确定要删除该货物类型吗?删除后不可恢复。', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(row.id))
|
||||
.then(() => {
|
||||
this.afterDelete(1);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.selectionList.length === 0) {
|
||||
this.$message.warning('请选择至少一条数据');
|
||||
return;
|
||||
}
|
||||
this.$confirm('确定删除所选数据,删除后不可恢复?', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => remove(this.ids))
|
||||
.then(() => {
|
||||
this.afterDelete(this.selectionList.length);
|
||||
this.$message({ type: 'success', message: '操作成功!' });
|
||||
this.$refs.crud.toggleSelection();
|
||||
});
|
||||
},
|
||||
afterDelete(deleteCount) {
|
||||
if (this.page.currentPage > 1 && this.data.length <= deleteCount) {
|
||||
this.page.currentPage -= 1;
|
||||
}
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data || {};
|
||||
this.ensureParentOption(this.form);
|
||||
this.syncColumnDisplay();
|
||||
done();
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.form = {
|
||||
typeLevel: 1,
|
||||
cargoName: '',
|
||||
cargoCode: '',
|
||||
parentId: undefined,
|
||||
parentCargoName: '',
|
||||
parentCargoCode: '',
|
||||
remark: '',
|
||||
};
|
||||
this.syncColumnDisplay();
|
||||
done();
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
},
|
||||
selectionClear() {
|
||||
this.selectionList = [];
|
||||
this.$refs.crud.toggleSelection();
|
||||
},
|
||||
currentChange(currentPage) {
|
||||
this.page.currentPage = currentPage;
|
||||
},
|
||||
sizeChange(pageSize) {
|
||||
this.page.pageSize = pageSize;
|
||||
},
|
||||
refreshChange() {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
onLoad(page, params = {}) {
|
||||
this.loading = true;
|
||||
getList(page.currentPage, page.pageSize, { ...params, ...this.query })
|
||||
.then(res => {
|
||||
const data = res.data.data;
|
||||
this.page.total = data.total;
|
||||
this.data = data.records || [];
|
||||
this.selectionClear();
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
openImportDialog(this, '货物类型');
|
||||
},
|
||||
buildExportParams() {
|
||||
const params = { ...this.query };
|
||||
if (this.ids) {
|
||||
params.ids = this.ids;
|
||||
}
|
||||
return params;
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出货物类型数据?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
NProgress.start();
|
||||
exportBlob('/blade-system/cargo-type/export-cargo-type', this.buildExportParams())
|
||||
.then(res => {
|
||||
downloadXls(res.data, `货物类型${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
});
|
||||
},
|
||||
handleTemplate() {
|
||||
NProgress.start();
|
||||
exportBlob('/blade-system/cargo-type/export-template', {})
|
||||
.then(res => {
|
||||
downloadXls(res.data, '货物类型导入模板.xlsx');
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cargo-type-page {
|
||||
:deep(.avue-crud__header) {
|
||||
margin-top: 12px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:deep(.el-card) {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:deep(.avue-crud__search) {
|
||||
padding: 12px 12px 4px;
|
||||
margin-bottom: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
:deep(.avue-crud__search .el-form-item__label) {
|
||||
min-width: 160px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
--el-table-border-color: #eff1f7;
|
||||
--el-table-row-hover-bg-color: #f5f7fa;
|
||||
}
|
||||
|
||||
:deep(.el-table__body tr:nth-child(even) > td.el-table__cell) {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
:deep(.el-table__body tr:nth-child(even) > td.el-table-fixed-column--left),
|
||||
:deep(.el-table__body tr:nth-child(even) > td.el-table-fixed-column--right) {
|
||||
background: #fafafa;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -133,6 +133,26 @@
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #longitude-form>
|
||||
<el-input
|
||||
v-model="form.longitude"
|
||||
clearable
|
||||
placeholder="请输入经度"
|
||||
:disabled="dialogReadonly || form.addressType !== '常规地址'"
|
||||
@input="handleCoordinateInput('longitude', $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #latitude-form>
|
||||
<el-input
|
||||
v-model="form.latitude"
|
||||
clearable
|
||||
placeholder="请输入纬度"
|
||||
:disabled="dialogReadonly || form.addressType !== '常规地址'"
|
||||
@input="handleCoordinateInput('latitude', $event)"
|
||||
/>
|
||||
</template>
|
||||
</avue-crud>
|
||||
|
||||
<el-dialog
|
||||
@@ -179,6 +199,7 @@ import { createOption } from '@/option/base/common-address';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { isCoordinateInput, normalizeCoordinateInput } from '@/utils/coordinate';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
@@ -190,6 +211,8 @@ export default {
|
||||
const validateLongitude = (rule, value, callback) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback(new Error('经度不能为空'));
|
||||
} else if (!isCoordinateInput(value)) {
|
||||
callback(new Error('经度最多保留6位小数'));
|
||||
} else if (Number(value) < -180 || Number(value) > 180) {
|
||||
callback(new Error('经度范围为 -180 到 180'));
|
||||
} else {
|
||||
@@ -199,6 +222,8 @@ export default {
|
||||
const validateLatitude = (rule, value, callback) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback(new Error('纬度不能为空'));
|
||||
} else if (!isCoordinateInput(value)) {
|
||||
callback(new Error('纬度最多保留6位小数'));
|
||||
} else if (Number(value) < -90 || Number(value) > 90) {
|
||||
callback(new Error('纬度范围为 -90 到 90'));
|
||||
} else {
|
||||
@@ -319,6 +344,9 @@ export default {
|
||||
this.form.longitude = source.longitude;
|
||||
this.form.latitude = source.latitude;
|
||||
},
|
||||
handleCoordinateInput(prop, value) {
|
||||
this.form[prop] = normalizeCoordinateInput(value);
|
||||
},
|
||||
clearTypeRelatedFields(siteCode = '') {
|
||||
this.form.sourceId = undefined;
|
||||
this.form.siteCode = siteCode;
|
||||
@@ -356,6 +384,8 @@ export default {
|
||||
detailAddress: String(row.detailAddress || '').trim(),
|
||||
regionCode: String(row.regionCode || '').trim(),
|
||||
regionName: String(row.regionName || '').trim(),
|
||||
longitude: normalizeCoordinateInput(row.longitude),
|
||||
latitude: normalizeCoordinateInput(row.latitude),
|
||||
contactName: String(row.contactName || '').trim(),
|
||||
contactPhone: String(row.contactPhone || '').trim(),
|
||||
remark: String(row.remark || '').trim(),
|
||||
@@ -400,6 +430,10 @@ export default {
|
||||
return false;
|
||||
}
|
||||
if (row.longitude !== undefined && row.longitude !== null && row.longitude !== '') {
|
||||
if (!isCoordinateInput(row.longitude)) {
|
||||
this.$message.warning('经度最多保留6位小数');
|
||||
return false;
|
||||
}
|
||||
if (Number(row.longitude) < -180 || Number(row.longitude) > 180) {
|
||||
this.$message.warning('经度范围为 -180 到 180');
|
||||
return false;
|
||||
@@ -407,6 +441,10 @@ export default {
|
||||
row.longitude = Number(row.longitude).toFixed(6);
|
||||
}
|
||||
if (row.latitude !== undefined && row.latitude !== null && row.latitude !== '') {
|
||||
if (!isCoordinateInput(row.latitude)) {
|
||||
this.$message.warning('纬度最多保留6位小数');
|
||||
return false;
|
||||
}
|
||||
if (Number(row.latitude) < -90 || Number(row.latitude) > 90) {
|
||||
this.$message.warning('纬度范围为 -90 到 90');
|
||||
return false;
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
</avue-crud>
|
||||
|
||||
<el-dialog title="币种汇率导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<avue-form :option="excelOption" v-model="excelForm">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
@@ -106,6 +106,8 @@ import { changeStatus, getDetail, getList, submit } from '@/api/base/currency';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { openImportDialog } from '@/utils/import-excel';
|
||||
import { formatUpdateUserName } from '@/utils/audit';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
@@ -131,7 +133,7 @@ export default {
|
||||
};
|
||||
const validateExchangeRate = (rule, value, callback) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback(new Error('请输入汇率'));
|
||||
callback();
|
||||
} else if (Number(value) <= 0) {
|
||||
callback(new Error('汇率必须大于0'));
|
||||
} else if (!/^\d+(\.\d{1,6})?$/.test(String(value))) {
|
||||
@@ -157,10 +159,13 @@ export default {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 860,
|
||||
labelPosition: 'top',
|
||||
labelPosition: 'right',
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
indexLabel: '序号',
|
||||
@@ -195,7 +200,6 @@ export default {
|
||||
label: '汇率',
|
||||
prop: 'exchangeRate',
|
||||
type: 'number',
|
||||
search: true,
|
||||
minWidth: 120,
|
||||
precision: 6,
|
||||
rules: [{ validator: validateExchangeRate, trigger: 'blur' }],
|
||||
@@ -216,7 +220,6 @@ export default {
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
searchRange: true,
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
@@ -239,7 +242,6 @@ export default {
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
searchRange: true,
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
@@ -278,6 +280,14 @@ export default {
|
||||
showWordLimit: true,
|
||||
rules: [{ max: 200, message: '最多 200 个字符', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '更新人',
|
||||
prop: 'updateUserName',
|
||||
formatter: formatUpdateUserName,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '更新时间',
|
||||
prop: 'updateTime',
|
||||
@@ -296,7 +306,6 @@ export default {
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
searchRange: true,
|
||||
search: true,
|
||||
hide: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
@@ -344,7 +353,10 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleCodeChange(value) {
|
||||
this.form.code = String(value || '').toUpperCase().replace(/[^A-Z]/g, '').slice(0, 3);
|
||||
this.form.code = String(value || '')
|
||||
.toUpperCase()
|
||||
.replace(/[^A-Z]/g, '')
|
||||
.slice(0, 3);
|
||||
const matched = this.data.find(item => item.code === this.form.code);
|
||||
if (matched) {
|
||||
this.form.name = matched.name;
|
||||
@@ -375,6 +387,7 @@ export default {
|
||||
normalizeRow(row) {
|
||||
const values = { ...row };
|
||||
values.code = String(values.code || '').toUpperCase();
|
||||
values.exchangeRate = values.exchangeRate === '' ? null : values.exchangeRate;
|
||||
values.dataSource = values.dataSource || '手工录入';
|
||||
values.status = values.status || 1;
|
||||
return values;
|
||||
@@ -384,11 +397,21 @@ export default {
|
||||
this.$message.warning('备注不能超过 200 字');
|
||||
return false;
|
||||
}
|
||||
if (Number(row.exchangeRate) <= 0) {
|
||||
if (
|
||||
row.exchangeRate !== undefined &&
|
||||
row.exchangeRate !== null &&
|
||||
row.exchangeRate !== '' &&
|
||||
Number(row.exchangeRate) <= 0
|
||||
) {
|
||||
this.$message.warning('汇率必须大于0');
|
||||
return false;
|
||||
}
|
||||
if (!/^\d+(\.\d{1,6})?$/.test(String(row.exchangeRate))) {
|
||||
if (
|
||||
row.exchangeRate !== undefined &&
|
||||
row.exchangeRate !== null &&
|
||||
row.exchangeRate !== '' &&
|
||||
!/^\d+(\.\d{1,6})?$/.test(String(row.exchangeRate))
|
||||
) {
|
||||
this.$message.warning('汇率最多保留6位小数');
|
||||
return false;
|
||||
}
|
||||
@@ -519,12 +542,7 @@ export default {
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
openImportDialog(this, '币种汇率');
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出币种汇率数据?', '提示', {
|
||||
@@ -550,7 +568,9 @@ export default {
|
||||
};
|
||||
},
|
||||
handleTemplate() {
|
||||
exportBlob(`/blade-system/currency/export-template?${this.website.tokenHeader}=${getToken()}`).then(res => {
|
||||
exportBlob(
|
||||
`/blade-system/currency/export-template?${this.website.tokenHeader}=${getToken()}`
|
||||
).then(res => {
|
||||
downloadXls(res.data, '币种汇率模板.xlsx');
|
||||
});
|
||||
},
|
||||
|
||||
@@ -93,7 +93,10 @@ export default {
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
indexLabel: '序号',
|
||||
|
||||
@@ -80,9 +80,13 @@ export default {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 680,
|
||||
labelPosition: 'right',
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
indexLabel: '序号',
|
||||
@@ -94,15 +98,29 @@ export default {
|
||||
menuWidth: 160,
|
||||
column: [
|
||||
{
|
||||
label: '中文名称',
|
||||
label: '费用类型',
|
||||
prop: 'feeCategory',
|
||||
type: 'select',
|
||||
search: true,
|
||||
filterable: true,
|
||||
dicUrl: '/blade-system/dict/dictionary?code=fee_category',
|
||||
props: {
|
||||
label: 'dictValue',
|
||||
value: 'dictKey',
|
||||
},
|
||||
minWidth: 120,
|
||||
rules: [{ required: true, message: '请选择费用类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '费用项',
|
||||
prop: 'name',
|
||||
minWidth: 140,
|
||||
search: true,
|
||||
maxlength: 50,
|
||||
rules: [{ required: true, message: '请输入中文名称', trigger: 'blur' }],
|
||||
rules: [{ required: true, message: '请输入费用项', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '英文名称',
|
||||
label: '费用项代码',
|
||||
prop: 'englishName',
|
||||
minWidth: 160,
|
||||
search: true,
|
||||
@@ -119,6 +137,8 @@ export default {
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '停用', value: 2 },
|
||||
],
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
@@ -164,6 +184,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
normalizeRow(row) {
|
||||
row.feeCategory = String(row.feeCategory || '').trim();
|
||||
row.name = String(row.name || '').trim();
|
||||
row.englishName = String(row.englishName || '').trim();
|
||||
if (!row.status) row.status = 1;
|
||||
|
||||
@@ -60,6 +60,9 @@
|
||||
<template #parentCode="{ row }">
|
||||
<span>{{ row.category === '港口' ? '-' : row.parentCode }}</span>
|
||||
</template>
|
||||
<template #name-header>
|
||||
<span>港口/码头</span>
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'primary' : 'info'">
|
||||
{{ row.status === 1 ? '启用' : '停用' }}
|
||||
@@ -71,7 +74,7 @@
|
||||
clearable
|
||||
filterable
|
||||
:disabled="isParentRegionLocked"
|
||||
placeholder="请选择国家"
|
||||
:placeholder="isParentRegionLocked ? '自动带出' : '请选择国家'"
|
||||
style="width: 100%"
|
||||
@change="handleCountryChange"
|
||||
>
|
||||
@@ -83,6 +86,32 @@
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
<template #category-form>
|
||||
<el-radio-group v-model="form.category" @change="handleCategoryChange">
|
||||
<el-radio label="港口">港口</el-radio>
|
||||
<el-radio label="码头">码头</el-radio>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
<template #code-form>
|
||||
<el-input
|
||||
v-if="form.category !== '码头'"
|
||||
v-model="form.code"
|
||||
clearable
|
||||
maxlength="5"
|
||||
placeholder="请输入5位大写字母"
|
||||
@input="handleCodeChange"
|
||||
@blur="handleCodeChange(form.code)"
|
||||
/>
|
||||
<div v-else>
|
||||
<el-input
|
||||
v-model="form.terminalCode"
|
||||
clearable
|
||||
maxlength="24"
|
||||
placeholder="请输入码头标识"
|
||||
@input="handleTerminalCodeChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #menu="{ row, index }">
|
||||
<el-button
|
||||
type="primary"
|
||||
@@ -103,27 +132,72 @@
|
||||
{{ row.status === 1 ? '停用' : '启用' }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #regionCode-form>
|
||||
<el-cascader
|
||||
:key="regionCascaderKey"
|
||||
v-model="form.regionCode"
|
||||
:props="regionProps"
|
||||
<template #cityCode-form>
|
||||
<el-select
|
||||
v-model="form.cityCode"
|
||||
clearable
|
||||
filterable
|
||||
:disabled="isRegionDisabled"
|
||||
:loading="cityLoading"
|
||||
:disabled="isCityDisabled"
|
||||
:placeholder="isParentRegionLocked ? '自动带出' : '请先选择国家'"
|
||||
style="width: 100%"
|
||||
@change="handleRegionChange"
|
||||
@change="handleCityChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in cityOptions"
|
||||
:key="item.id"
|
||||
:label="item.title"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
<template #districtCode-form>
|
||||
<el-select
|
||||
v-model="form.districtCode"
|
||||
clearable
|
||||
filterable
|
||||
:loading="districtLoading"
|
||||
:disabled="isDistrictDisabled"
|
||||
:placeholder="
|
||||
isParentRegionLocked ? '自动带出' : form.cityCode ? '请选择区县' : '请先选择城市'
|
||||
"
|
||||
style="width: 100%"
|
||||
@change="handleDistrictChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in districtOptions"
|
||||
:key="item.id"
|
||||
:label="item.title"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
<template #longitude-form>
|
||||
<el-input
|
||||
v-model="form.longitude"
|
||||
clearable
|
||||
placeholder="请输入经度"
|
||||
@input="handleCoordinateInput('longitude', $event)"
|
||||
/>
|
||||
</template>
|
||||
<template #latitude-form>
|
||||
<el-input
|
||||
v-model="form.latitude"
|
||||
clearable
|
||||
placeholder="请输入纬度"
|
||||
@input="handleCoordinateInput('latitude', $event)"
|
||||
/>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<empty-pagination
|
||||
:page="page"
|
||||
@size-change="sizeChange"
|
||||
@current-change="currentChange"
|
||||
@load="onLoad(page, query)"
|
||||
/>
|
||||
|
||||
<el-dialog title="港口码头主数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form
|
||||
:option="excelOption"
|
||||
v-model="excelForm"
|
||||
:upload-before="uploadBefore"
|
||||
:upload-after="uploadAfter"
|
||||
>
|
||||
<avue-form :option="excelOption" v-model="excelForm">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
@@ -147,8 +221,10 @@ import { getLazyTree } from '@/api/base/region';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { openImportDialog } from '@/utils/import-excel';
|
||||
import { formatUpdateUserName } from '@/utils/audit';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { getCoordinateValidationMessage, normalizeCoordinateInput } from '@/utils/coordinate';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
@@ -179,19 +255,17 @@ export default {
|
||||
callback();
|
||||
};
|
||||
const validateLongitude = (rule, value, callback) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback();
|
||||
} else if (Number(value) < -180 || Number(value) > 180) {
|
||||
callback(new Error('经度范围为 -180 到 180'));
|
||||
const message = getCoordinateValidationMessage(value, 'longitude');
|
||||
if (message) {
|
||||
callback(new Error(message));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const validateLatitude = (rule, value, callback) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback();
|
||||
} else if (Number(value) < -90 || Number(value) > 90) {
|
||||
callback(new Error('纬度范围为 -90 到 90'));
|
||||
const message = getCoordinateValidationMessage(value, 'latitude');
|
||||
if (message) {
|
||||
callback(new Error(message));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
@@ -205,17 +279,12 @@ export default {
|
||||
excelForm: {},
|
||||
portOptions: [],
|
||||
countryOptions: [],
|
||||
regionCascaderKey: 0,
|
||||
regionNodeMap: {},
|
||||
regionNameMap: {},
|
||||
regionProps: {
|
||||
lazy: true,
|
||||
lazyLoad: this.loadRegionNode,
|
||||
label: 'title',
|
||||
value: 'id',
|
||||
checkStrictly: false,
|
||||
emitPath: true,
|
||||
},
|
||||
cityOptions: [],
|
||||
cityLoading: false,
|
||||
cityOptionCache: {},
|
||||
districtOptions: [],
|
||||
districtLoading: false,
|
||||
districtOptionCache: {},
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
@@ -227,10 +296,15 @@ export default {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchLabelWidth: 120,
|
||||
searchMenuSpan: 6,
|
||||
searchLabelWidth: 160,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuSpan: 24,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
indexLabel: '序号',
|
||||
@@ -242,31 +316,13 @@ export default {
|
||||
dialogClickModal: false,
|
||||
menuWidth: 160,
|
||||
column: [
|
||||
{
|
||||
label: '编码',
|
||||
prop: 'code',
|
||||
search: true,
|
||||
width: 140,
|
||||
placeholder: '港口如 CNSHG,码头如 CNSHG-CT1',
|
||||
rules: [
|
||||
{ required: true, message: '请输入编码', trigger: 'blur' },
|
||||
{ validator: validateCode, trigger: 'blur' },
|
||||
],
|
||||
change: ({ value }) => this.handleCodeChange(value),
|
||||
blur: ({ value }) => this.handleCodeChange(value),
|
||||
},
|
||||
{
|
||||
label: '港口/码头名称',
|
||||
prop: 'name',
|
||||
minWidth: 140,
|
||||
search: true,
|
||||
rules: [{ required: true, message: '请输入港口/码头名称', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '类型',
|
||||
prop: 'category',
|
||||
type: 'select',
|
||||
search: true,
|
||||
formslot: true,
|
||||
span: 24,
|
||||
dicData: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '港口', value: '港口' },
|
||||
@@ -285,6 +341,8 @@ export default {
|
||||
},
|
||||
dicData: [],
|
||||
hide: true,
|
||||
span: 12,
|
||||
placeholder: '请选择',
|
||||
rules: [{ required: false, message: '请选择上级港口', trigger: 'change' }],
|
||||
change: ({ value }) => this.handleParentChange(value),
|
||||
},
|
||||
@@ -301,50 +359,147 @@ export default {
|
||||
prop: 'parentCode',
|
||||
minWidth: 120,
|
||||
slot: true,
|
||||
disabled: true,
|
||||
span: 12,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
placeholder: '自动带出',
|
||||
rules: [{ required: false, message: '请选择上级港口', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '港口/码头名称',
|
||||
prop: 'name',
|
||||
minWidth: 140,
|
||||
search: true,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
rules: [{ required: true, message: '请输入港口/码头名称', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '编码',
|
||||
prop: 'code',
|
||||
search: true,
|
||||
formslot: true,
|
||||
width: 140,
|
||||
span: 12,
|
||||
placeholder: '请输入编码',
|
||||
rules: [
|
||||
{ required: true, message: '请输入编码', trigger: 'blur' },
|
||||
{ validator: validateCode, trigger: 'blur' },
|
||||
],
|
||||
change: ({ value }) => this.handleCodeChange(value),
|
||||
blur: ({ value }) => this.handleCodeChange(value),
|
||||
},
|
||||
{
|
||||
label: '国家',
|
||||
prop: 'country',
|
||||
type: 'select',
|
||||
search: true,
|
||||
formslot: true,
|
||||
filterable: true,
|
||||
dicData: [],
|
||||
span: 12,
|
||||
minWidth: 100,
|
||||
minWidth: 140,
|
||||
overHidden: false,
|
||||
placeholder: '自动带出',
|
||||
searchPlaceholder: '请选择 国家',
|
||||
change: ({ value }) => this.handleCountryNameChange(value),
|
||||
rules: [{ required: true, message: '请选择国家', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '城市',
|
||||
prop: 'regionCode',
|
||||
prop: 'cityCode',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
span: 12,
|
||||
placeholder: '自动带出',
|
||||
rules: [{ required: true, message: '请选择所属城市', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '城市',
|
||||
prop: 'city',
|
||||
type: 'select',
|
||||
search: true,
|
||||
filterable: true,
|
||||
dicData: [],
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
minWidth: 100,
|
||||
change: ({ value }) => this.handleCityNameChange(value),
|
||||
},
|
||||
{
|
||||
label: '区县',
|
||||
prop: 'districtCode',
|
||||
type: 'select',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
props: {
|
||||
label: 'title',
|
||||
value: 'id',
|
||||
},
|
||||
dicData: [],
|
||||
span: 12,
|
||||
placeholder: '选择城市后再选择区县',
|
||||
rules: [{ required: true, message: '请选择区县', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '区县',
|
||||
prop: 'districtName',
|
||||
type: 'select',
|
||||
search: true,
|
||||
filterable: true,
|
||||
dicData: [],
|
||||
minWidth: 100,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '行政区划编号',
|
||||
prop: 'regionCode',
|
||||
minWidth: 140,
|
||||
span: 12,
|
||||
placeholder: '选择区县后自动填写',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
rules: [{ max: 32, message: '行政区划编码不能超过32字', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '详细地址',
|
||||
prop: 'detailAddress',
|
||||
type: 'textarea',
|
||||
minRows: 3,
|
||||
span: 24,
|
||||
minWidth: 220,
|
||||
overHidden: false,
|
||||
placeholder: '请输入',
|
||||
maxlength: 255,
|
||||
showWordLimit: true,
|
||||
rules: [{ max: 255, message: '详细地址不能超过255字', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '经度',
|
||||
prop: 'longitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
formslot: true,
|
||||
minWidth: 130,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
overHidden: false,
|
||||
rules: [{ validator: validateLongitude, trigger: 'blur' }],
|
||||
rules: [
|
||||
{ required: true, message: '请输入经度', trigger: 'blur' },
|
||||
{ validator: validateLongitude, trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '纬度',
|
||||
prop: 'latitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
formslot: true,
|
||||
minWidth: 130,
|
||||
span: 12,
|
||||
placeholder: '请输入',
|
||||
overHidden: false,
|
||||
rules: [{ validator: validateLatitude, trigger: 'blur' }],
|
||||
rules: [
|
||||
{ required: true, message: '请输入纬度', trigger: 'blur' },
|
||||
{ validator: validateLatitude, trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '数据来源',
|
||||
@@ -353,6 +508,8 @@ export default {
|
||||
search: true,
|
||||
minWidth: 120,
|
||||
overHidden: false,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
dicData: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '初始化导入', value: '初始化导入' },
|
||||
@@ -369,6 +526,8 @@ export default {
|
||||
dataType: 'number',
|
||||
minWidth: 110,
|
||||
overHidden: false,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
dicData: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '启用', value: 1 },
|
||||
@@ -418,6 +577,7 @@ export default {
|
||||
],
|
||||
},
|
||||
excelOption: {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
@@ -459,11 +619,14 @@ export default {
|
||||
: String(authority || '').includes('admin');
|
||||
},
|
||||
isParentRegionLocked() {
|
||||
return this.form.category === '码头' && !!this.form.parentId;
|
||||
return this.form.category === '码头';
|
||||
},
|
||||
isRegionDisabled() {
|
||||
isCityDisabled() {
|
||||
return this.isParentRegionLocked || !this.form.countryCode;
|
||||
},
|
||||
isDistrictDisabled() {
|
||||
return this.isParentRegionLocked || !this.form.cityCode;
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
@@ -487,36 +650,52 @@ export default {
|
||||
return;
|
||||
}
|
||||
if (this.form.category === '码头') {
|
||||
this.form.code = code.replace(/[^A-Z0-9-]/g, '');
|
||||
const terminalCode = this.getTerminalCodePart(code).replace(/[^A-Z0-9]/g, '');
|
||||
if (this.form.parentCode) {
|
||||
this.handleTerminalCodeChange(terminalCode);
|
||||
} else {
|
||||
this.form.terminalCode = terminalCode;
|
||||
this.form.code = code.replace(/[^A-Z0-9-]/g, '');
|
||||
}
|
||||
}
|
||||
},
|
||||
loadRegionNode(node, resolve) {
|
||||
const parentCode = node.level === 0 ? this.form.countryCode : node.value;
|
||||
if (!parentCode) {
|
||||
resolve([]);
|
||||
return;
|
||||
handleTerminalCodeChange(value) {
|
||||
const terminalCode = String(value || '')
|
||||
.toUpperCase()
|
||||
.replace(/[^A-Z0-9]/g, '');
|
||||
this.form.terminalCode = terminalCode;
|
||||
this.form.code =
|
||||
this.form.parentCode && terminalCode ? `${this.form.parentCode}-${terminalCode}` : '';
|
||||
},
|
||||
getTerminalCodePart(code) {
|
||||
const value = String(code || '').toUpperCase();
|
||||
if (this.form.parentCode && value.startsWith(`${this.form.parentCode}-`)) {
|
||||
return value.slice(this.form.parentCode.length + 1);
|
||||
}
|
||||
getLazyTree(parentCode).then(res => {
|
||||
const regionList = res.data.data || [];
|
||||
const nodes = regionList.map(region => {
|
||||
this.regionNodeMap[region.id] = region;
|
||||
this.regionNameMap[region.title] = region.id;
|
||||
return {
|
||||
id: region.id,
|
||||
title: region.title,
|
||||
leaf: node.level >= 1,
|
||||
};
|
||||
});
|
||||
resolve(nodes);
|
||||
});
|
||||
return value.split('-').slice(1).join('-');
|
||||
},
|
||||
syncTerminalCode() {
|
||||
if (this.form.category !== '码头') return;
|
||||
this.form.terminalCode = this.getTerminalCodePart(this.form.code);
|
||||
this.handleTerminalCodeChange(this.form.terminalCode);
|
||||
},
|
||||
setColumnDisplay(prop, display) {
|
||||
const column = this.findColumn(this.option.column, prop);
|
||||
if (!column) return;
|
||||
column.display = display;
|
||||
column.addDisplay = display;
|
||||
column.editDisplay = display;
|
||||
column.viewDisplay = display;
|
||||
},
|
||||
loadCountryOptions() {
|
||||
return getLazyTree('0').then(res => {
|
||||
this.countryOptions = res.data.data || [];
|
||||
this.countryOptions.forEach(country => {
|
||||
this.regionNodeMap[country.id] = country;
|
||||
this.regionNameMap[country.title] = country.id;
|
||||
});
|
||||
this.countryOptions = (res.data.data || []).map(this.normalizeRegionOption);
|
||||
const countryColumn = this.findColumn(this.option.column, 'country');
|
||||
countryColumn.dicData = this.countryOptions.map(country => ({
|
||||
label: country.title,
|
||||
value: country.title,
|
||||
id: country.id,
|
||||
}));
|
||||
});
|
||||
},
|
||||
loadPortOptions() {
|
||||
@@ -525,22 +704,39 @@ export default {
|
||||
this.findColumn(this.option.column, 'parentId').dicData = this.portOptions;
|
||||
});
|
||||
},
|
||||
handleCategoryChange(value) {
|
||||
clearParentPortFields() {
|
||||
this.form.parentId = undefined;
|
||||
this.form.parentCode = '';
|
||||
this.form.parentName = '';
|
||||
this.form.countryCode = '';
|
||||
this.form.country = '';
|
||||
this.clearCityValue();
|
||||
},
|
||||
handleCategoryChange(value, reset = true) {
|
||||
const parentColumn = this.findColumn(this.option.column, 'parentId');
|
||||
parentColumn.rules[0].required = value === '码头';
|
||||
this.setColumnDisplay('parentId', value === '码头');
|
||||
if (value === '港口') {
|
||||
this.form.parentId = undefined;
|
||||
this.form.parentCode = '';
|
||||
this.form.parentName = '';
|
||||
this.form.terminalCode = '';
|
||||
this.form.code = reset ? '' : String(this.form.code || '').split('-')[0];
|
||||
} else if (reset) {
|
||||
this.clearParentPortFields();
|
||||
}
|
||||
this.handleCodeChange(this.form.code);
|
||||
},
|
||||
handleCountryChange(value) {
|
||||
const country = this.countryOptions.find(item => item.id === value);
|
||||
this.form.country = country ? country.title : '';
|
||||
this.form.regionCode = [];
|
||||
this.form.city = '';
|
||||
this.regionCascaderKey += 1;
|
||||
this.clearCityValue();
|
||||
this.loadCityOptions(value);
|
||||
},
|
||||
handleCountryNameChange(value) {
|
||||
const country = this.countryOptions.find(item => item.title === value);
|
||||
this.setDistrictOptions([]);
|
||||
this.loadCityOptions(country ? country.id : '');
|
||||
},
|
||||
syncCountryName(countryCode) {
|
||||
const country = this.countryOptions.find(item => item.id === countryCode);
|
||||
@@ -548,37 +744,177 @@ export default {
|
||||
this.form.country = country.title;
|
||||
}
|
||||
},
|
||||
normalizeRegionOption(region) {
|
||||
return {
|
||||
...region,
|
||||
id: region.id === undefined || region.id === null ? region.id : String(region.id),
|
||||
parentCode:
|
||||
region.parentCode === undefined || region.parentCode === null
|
||||
? region.parentCode
|
||||
: String(region.parentCode),
|
||||
};
|
||||
},
|
||||
setCityOptions(cityOptions) {
|
||||
this.cityOptions = cityOptions;
|
||||
const cityCodeColumn = this.findColumn(this.option.column, 'cityCode');
|
||||
if (cityCodeColumn) {
|
||||
cityCodeColumn.dicData = cityOptions;
|
||||
}
|
||||
const cityColumn = this.findColumn(this.option.column, 'city');
|
||||
if (cityColumn) {
|
||||
cityColumn.dicData = cityOptions.map(city => ({
|
||||
label: city.title,
|
||||
value: city.title,
|
||||
id: city.id,
|
||||
}));
|
||||
}
|
||||
},
|
||||
clearCityValue() {
|
||||
this.form.cityCode = undefined;
|
||||
this.form.city = '';
|
||||
this.clearDistrictValue();
|
||||
},
|
||||
clearDistrictValue() {
|
||||
this.form.districtCode = undefined;
|
||||
this.form.districtName = '';
|
||||
this.form.regionCode = '';
|
||||
},
|
||||
loadCityOptions(countryCode) {
|
||||
if (!countryCode) {
|
||||
this.setCityOptions([]);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
if (this.cityOptionCache[countryCode]) {
|
||||
this.setCityOptions(this.cityOptionCache[countryCode]);
|
||||
return Promise.resolve(this.cityOptionCache[countryCode]);
|
||||
}
|
||||
this.cityLoading = true;
|
||||
return getLazyTree(countryCode)
|
||||
.then(res => {
|
||||
const firstLevelList = (res.data.data || []).map(this.normalizeRegionOption);
|
||||
return Promise.all(
|
||||
firstLevelList.map(parent => {
|
||||
return getLazyTree(parent.id).then(childRes => {
|
||||
const childList = (childRes.data.data || []).map(this.normalizeRegionOption);
|
||||
return childList.map(child => ({
|
||||
...child,
|
||||
parentCode: child.parentCode || parent.id,
|
||||
}));
|
||||
});
|
||||
})
|
||||
).then(cityGroups => {
|
||||
const cityList = cityGroups.flat();
|
||||
const options = cityList.length > 0 ? cityList : firstLevelList;
|
||||
this.cityOptionCache[countryCode] = options;
|
||||
this.setCityOptions(options);
|
||||
return options;
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
this.cityLoading = false;
|
||||
});
|
||||
},
|
||||
resolveCityCode(cityName, regionCode) {
|
||||
const city = this.cityOptions.find(item => item.title === cityName || item.id === regionCode);
|
||||
return city ? city.id : '';
|
||||
},
|
||||
setDistrictOptions(districtOptions) {
|
||||
this.districtOptions = districtOptions;
|
||||
const districtCodeColumn = this.findColumn(this.option.column, 'districtCode');
|
||||
if (districtCodeColumn) {
|
||||
districtCodeColumn.dicData = districtOptions;
|
||||
}
|
||||
const districtNameColumn = this.findColumn(this.option.column, 'districtName');
|
||||
if (districtNameColumn) {
|
||||
districtNameColumn.dicData = districtOptions.map(district => ({
|
||||
label: district.title,
|
||||
value: district.title,
|
||||
id: district.id,
|
||||
}));
|
||||
}
|
||||
},
|
||||
loadDistrictOptions(cityCode) {
|
||||
if (!cityCode) {
|
||||
this.setDistrictOptions([]);
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
if (this.districtOptionCache[cityCode]) {
|
||||
this.setDistrictOptions(this.districtOptionCache[cityCode]);
|
||||
return Promise.resolve(this.districtOptionCache[cityCode]);
|
||||
}
|
||||
this.districtLoading = true;
|
||||
return getLazyTree(cityCode)
|
||||
.then(res => {
|
||||
const options = (res.data.data || []).map(this.normalizeRegionOption);
|
||||
this.districtOptionCache[cityCode] = options;
|
||||
this.setDistrictOptions(options);
|
||||
return options;
|
||||
})
|
||||
.finally(() => {
|
||||
this.districtLoading = false;
|
||||
});
|
||||
},
|
||||
resolveDistrictCode(districtName, regionCode) {
|
||||
const district = this.districtOptions.find(
|
||||
item => item.id === regionCode || item.title === districtName
|
||||
);
|
||||
return district ? district.id : regionCode || '';
|
||||
},
|
||||
handleParentChange(value) {
|
||||
const parent = this.portOptions.find(item => item.id === value);
|
||||
if (!parent) return;
|
||||
const terminalCode = String(this.form.code || '')
|
||||
.split('-')
|
||||
.slice(1)
|
||||
.join('-');
|
||||
if (!parent) {
|
||||
this.clearParentPortFields();
|
||||
this.syncTerminalCode();
|
||||
return;
|
||||
}
|
||||
const terminalCode =
|
||||
this.form.terminalCode ||
|
||||
String(this.form.code || '')
|
||||
.split('-')
|
||||
.slice(1)
|
||||
.join('-');
|
||||
this.form.parentCode = parent.code;
|
||||
this.form.parentName = parent.name;
|
||||
if (this.form.category === '码头' && terminalCode) {
|
||||
this.form.code = `${parent.code}-${terminalCode}`;
|
||||
}
|
||||
this.form.terminalCode = terminalCode;
|
||||
this.handleTerminalCodeChange(terminalCode);
|
||||
this.form.country = parent.country;
|
||||
this.form.city = parent.city;
|
||||
this.form.districtCode = parent.districtCode;
|
||||
this.form.districtName = parent.districtName;
|
||||
this.form.regionCode = parent.regionCode;
|
||||
this.resolveCountryCode(parent.country)
|
||||
.then(countryCode => {
|
||||
this.form.countryCode = countryCode;
|
||||
this.syncCountryName(countryCode);
|
||||
this.regionCascaderKey += 1;
|
||||
return this.resolveRegionCode(parent.country, parent.city);
|
||||
return this.loadCityOptions(countryCode);
|
||||
})
|
||||
.then(regionCode => {
|
||||
this.form.regionCode = regionCode;
|
||||
.then(() => {
|
||||
this.form.cityCode = this.resolveCityCode(parent.city, parent.regionCode);
|
||||
return this.loadDistrictOptions(this.form.cityCode);
|
||||
})
|
||||
.then(() => {
|
||||
this.form.districtCode = this.resolveDistrictCode(parent.districtName, parent.regionCode);
|
||||
this.handleDistrictChange(this.form.districtCode);
|
||||
});
|
||||
},
|
||||
handleRegionChange(value) {
|
||||
const regionCode = Array.isArray(value) ? value : [];
|
||||
const provinceCode = regionCode[0];
|
||||
const cityCode = regionCode[1];
|
||||
const city = this.regionNodeMap[cityCode];
|
||||
handleCityChange(value) {
|
||||
const city = this.cityOptions.find(item => item.id === value);
|
||||
this.form.city = city ? city.title : '';
|
||||
this.clearDistrictValue();
|
||||
this.loadDistrictOptions(value);
|
||||
},
|
||||
handleCityNameChange(value) {
|
||||
const city = this.cityOptions.find(item => item.title === value);
|
||||
this.setDistrictOptions([]);
|
||||
this.loadDistrictOptions(city ? city.id : '');
|
||||
},
|
||||
handleDistrictChange(value) {
|
||||
const district = this.districtOptions.find(item => item.id === value);
|
||||
this.form.districtName = district ? district.title : '';
|
||||
this.form.regionCode = value || '';
|
||||
},
|
||||
handleCoordinateInput(prop, value) {
|
||||
this.form[prop] = normalizeCoordinateInput(value);
|
||||
},
|
||||
resolveCountryCode(country) {
|
||||
if (!country) {
|
||||
@@ -597,55 +933,26 @@ export default {
|
||||
return option ? option.id : DEFAULT_COUNTRY_CODE;
|
||||
});
|
||||
},
|
||||
resolveRegionCode(country, city) {
|
||||
if (!country || !city) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
const countryCode = this.form.countryCode || this.regionNameMap[country];
|
||||
if (!countryCode) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
return this.loadRegionList(countryCode).then(provinceList => {
|
||||
const province = provinceList.find(item => item.title === country || item.title === city);
|
||||
if (province) {
|
||||
return this.loadRegionList(province.id).then(cityList => {
|
||||
const cityNode = cityList.find(item => item.title === city);
|
||||
return cityNode ? [province.id, cityNode.id] : [province.id];
|
||||
});
|
||||
}
|
||||
return provinceList.reduce((promise, item) => {
|
||||
return promise.then(result => {
|
||||
if (result.length > 0) {
|
||||
return result;
|
||||
}
|
||||
return this.loadRegionList(item.id).then(cityList => {
|
||||
const cityNode = cityList.find(child => child.title === city);
|
||||
return cityNode ? [item.id, cityNode.id] : [];
|
||||
});
|
||||
});
|
||||
}, Promise.resolve([]));
|
||||
});
|
||||
},
|
||||
loadRegionList(parentCode) {
|
||||
return getLazyTree(parentCode).then(res => {
|
||||
const regionList = res.data.data || [];
|
||||
regionList.forEach(region => {
|
||||
this.regionNodeMap[region.id] = region;
|
||||
this.regionNameMap[region.title] = region.id;
|
||||
});
|
||||
return regionList;
|
||||
});
|
||||
},
|
||||
normalizeRow(row) {
|
||||
row.code = String(row.code || '').toUpperCase();
|
||||
if (row.category === '港口') {
|
||||
row.parentId = undefined;
|
||||
row.parentCode = undefined;
|
||||
row.parentName = undefined;
|
||||
const submitRow = { ...row };
|
||||
submitRow.code = String(submitRow.code || '').toUpperCase();
|
||||
submitRow.regionCode = String(submitRow.regionCode || '').trim();
|
||||
submitRow.districtCode = String(submitRow.districtCode || '').trim();
|
||||
submitRow.districtName = String(submitRow.districtName || '').trim();
|
||||
submitRow.detailAddress = String(submitRow.detailAddress || '').trim();
|
||||
submitRow.longitude = normalizeCoordinateInput(submitRow.longitude);
|
||||
submitRow.latitude = normalizeCoordinateInput(submitRow.latitude);
|
||||
if (submitRow.category === '港口') {
|
||||
submitRow.parentId = undefined;
|
||||
submitRow.parentCode = undefined;
|
||||
submitRow.parentName = undefined;
|
||||
}
|
||||
if (!row.dataSource) row.dataSource = '手工导入';
|
||||
if (!row.status) row.status = 1;
|
||||
return row;
|
||||
if (!submitRow.dataSource) submitRow.dataSource = '手工导入';
|
||||
if (!submitRow.status) submitRow.status = 1;
|
||||
delete submitRow.terminalCode;
|
||||
delete submitRow.cityCode;
|
||||
delete submitRow.countryCode;
|
||||
return submitRow;
|
||||
},
|
||||
validateRequiredFields(row) {
|
||||
const isBlank = value => value === undefined || value === null || String(value).trim() === '';
|
||||
@@ -665,8 +972,26 @@ export default {
|
||||
this.$message.warning('请选择上级港口');
|
||||
return false;
|
||||
}
|
||||
if (!Array.isArray(row.regionCode) || row.regionCode.length < 2) {
|
||||
this.$message.warning('请选择所属城市');
|
||||
if (isBlank(row.regionCode)) {
|
||||
this.$message.warning('请选择区县');
|
||||
return false;
|
||||
}
|
||||
if (isBlank(row.longitude)) {
|
||||
this.$message.warning('请输入经度');
|
||||
return false;
|
||||
}
|
||||
const longitudeMessage = getCoordinateValidationMessage(row.longitude, 'longitude');
|
||||
if (longitudeMessage) {
|
||||
this.$message.warning(longitudeMessage);
|
||||
return false;
|
||||
}
|
||||
if (isBlank(row.latitude)) {
|
||||
this.$message.warning('请输入纬度');
|
||||
return false;
|
||||
}
|
||||
const latitudeMessage = getCoordinateValidationMessage(row.latitude, 'latitude');
|
||||
if (latitudeMessage) {
|
||||
this.$message.warning(latitudeMessage);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -770,37 +1095,57 @@ export default {
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data;
|
||||
this.form.terminalCode = this.getTerminalCodePart(this.form.code);
|
||||
this.resolveCountryCode(this.form.country)
|
||||
.then(countryCode => {
|
||||
this.form.countryCode = countryCode;
|
||||
this.syncCountryName(countryCode);
|
||||
this.regionCascaderKey += 1;
|
||||
return this.resolveRegionCode(this.form.country, this.form.city);
|
||||
return this.loadCityOptions(countryCode);
|
||||
})
|
||||
.then(regionCode => {
|
||||
this.form.regionCode = regionCode;
|
||||
this.handleCategoryChange(this.form.category);
|
||||
.then(() => {
|
||||
this.form.cityCode = this.resolveCityCode(this.form.city, this.form.regionCode);
|
||||
return this.loadDistrictOptions(this.form.cityCode);
|
||||
})
|
||||
.then(() => {
|
||||
this.form.districtCode = this.resolveDistrictCode(
|
||||
this.form.districtName,
|
||||
this.form.regionCode
|
||||
);
|
||||
if (!this.form.regionCode) {
|
||||
this.handleDistrictChange(this.form.districtCode);
|
||||
}
|
||||
this.handleCategoryChange(this.form.category, false);
|
||||
this.syncTerminalCode();
|
||||
done();
|
||||
});
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
this.resolveCountryCode(this.form.country)
|
||||
.then(countryCode => {
|
||||
this.form.countryCode = countryCode;
|
||||
this.syncCountryName(countryCode);
|
||||
this.regionCascaderKey += 1;
|
||||
return this.resolveRegionCode(this.form.country, this.form.city);
|
||||
})
|
||||
.then(regionCode => {
|
||||
this.form.regionCode = regionCode;
|
||||
done();
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.resolveCountryCode(this.form.country)
|
||||
.then(countryCode => {
|
||||
this.form.countryCode = countryCode;
|
||||
this.syncCountryName(countryCode);
|
||||
return this.loadCityOptions(countryCode);
|
||||
})
|
||||
.then(() => {
|
||||
this.form.cityCode = this.resolveCityCode(this.form.city, this.form.regionCode);
|
||||
return this.loadDistrictOptions(this.form.cityCode);
|
||||
})
|
||||
.then(() => {
|
||||
this.form.districtCode = this.resolveDistrictCode(
|
||||
this.form.districtName,
|
||||
this.form.regionCode
|
||||
);
|
||||
if (!this.form.regionCode) {
|
||||
this.handleDistrictChange(this.form.districtCode);
|
||||
}
|
||||
done();
|
||||
});
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.setCityOptions([]);
|
||||
this.setDistrictOptions([]);
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
@@ -839,24 +1184,9 @@ export default {
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadBefore(file, done) {
|
||||
const fileName = file && file.name ? file.name.toLowerCase() : '';
|
||||
if (!/\.(xls|xlsx)$/.test(fileName)) {
|
||||
this.$message.error('请上传 .xls,.xlsx 标准格式文件');
|
||||
return false;
|
||||
}
|
||||
if (typeof done === 'function') {
|
||||
done();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
this.loadPortOptions();
|
||||
done();
|
||||
openImportDialog(this, '港口码头主数据', () => {
|
||||
this.loadPortOptions();
|
||||
});
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出港口码头主数据?', '提示', {
|
||||
@@ -894,13 +1224,3 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.port-terminal-page {
|
||||
:deep(.avue-crud__header),
|
||||
:deep(.el-table th .cell),
|
||||
:deep(.el-table td .cell) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -79,15 +79,45 @@
|
||||
{{ row.status === 1 ? '停用' : '启用' }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #districtCode-form>
|
||||
<el-select
|
||||
v-model="form.districtCode"
|
||||
clearable
|
||||
filterable
|
||||
:loading="districtLoading"
|
||||
:disabled="!form.cityCode"
|
||||
:placeholder="form.cityCode ? '请选择区县' : '请先选择城市'"
|
||||
style="width: 100%"
|
||||
@change="handleDistrictChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in districtOptions"
|
||||
:key="item.id"
|
||||
:label="item.title"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
<template #longitude-form>
|
||||
<el-input
|
||||
v-model="form.longitude"
|
||||
clearable
|
||||
placeholder="请输入经度"
|
||||
@input="handleCoordinateInput('longitude', $event)"
|
||||
/>
|
||||
</template>
|
||||
<template #latitude-form>
|
||||
<el-input
|
||||
v-model="form.latitude"
|
||||
clearable
|
||||
placeholder="请输入纬度"
|
||||
@input="handleCoordinateInput('latitude', $event)"
|
||||
/>
|
||||
</template>
|
||||
</avue-crud>
|
||||
|
||||
<el-dialog title="铁路车站主数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form
|
||||
:option="excelOption"
|
||||
v-model="excelForm"
|
||||
:upload-before="uploadBefore"
|
||||
:upload-after="uploadAfter"
|
||||
>
|
||||
<avue-form :option="excelOption" v-model="excelForm">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
@@ -104,8 +134,10 @@ import { getLazyTree } from '@/api/base/region';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { openImportDialog } from '@/utils/import-excel';
|
||||
import { formatUpdateUserName } from '@/utils/audit';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { getCoordinateValidationMessage, normalizeCoordinateInput } from '@/utils/coordinate';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
@@ -128,19 +160,17 @@ export default {
|
||||
}
|
||||
};
|
||||
const validateLongitude = (rule, value, callback) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback();
|
||||
} else if (Number(value) < -180 || Number(value) > 180) {
|
||||
callback(new Error('经度范围为 -180 到 180'));
|
||||
const message = getCoordinateValidationMessage(value, 'longitude');
|
||||
if (message) {
|
||||
callback(new Error(message));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const validateLatitude = (rule, value, callback) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
callback();
|
||||
} else if (Number(value) < -90 || Number(value) > 90) {
|
||||
callback(new Error('纬度范围为 -90 到 90'));
|
||||
const message = getCoordinateValidationMessage(value, 'latitude');
|
||||
if (message) {
|
||||
callback(new Error(message));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
@@ -154,6 +184,8 @@ export default {
|
||||
excelForm: {},
|
||||
provinceOptions: [],
|
||||
cityOptions: [],
|
||||
districtOptions: [],
|
||||
districtLoading: false,
|
||||
regionInitializing: false,
|
||||
page: {
|
||||
pageSize: 10,
|
||||
@@ -166,9 +198,14 @@ export default {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 980,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
indexLabel: '序号',
|
||||
@@ -186,8 +223,8 @@ export default {
|
||||
minWidth: 120,
|
||||
overHidden: false,
|
||||
search: true,
|
||||
addDisabled: true,
|
||||
editDisabled: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
placeholder: '系统自动生成',
|
||||
searchPlaceholder: '请输入编码',
|
||||
},
|
||||
@@ -271,14 +308,36 @@ export default {
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '行政区划',
|
||||
prop: 'regionName',
|
||||
search: true,
|
||||
label: '所属区县',
|
||||
prop: 'districtCode',
|
||||
type: 'select',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
props: {
|
||||
label: 'title',
|
||||
value: 'id',
|
||||
},
|
||||
dicData: [],
|
||||
filterable: true,
|
||||
span: 12,
|
||||
rules: [{ required: true, message: '请选择所属区县', trigger: 'change' }],
|
||||
change: ({ value }) => this.handleDistrictChange(value),
|
||||
},
|
||||
{
|
||||
label: '所属区县',
|
||||
prop: 'districtName',
|
||||
minWidth: 140,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
},
|
||||
{
|
||||
label: '行政区划编号',
|
||||
prop: 'regionCode',
|
||||
minWidth: 160,
|
||||
overHidden: false,
|
||||
maxlength: 128,
|
||||
showWordLimit: true,
|
||||
rules: [{ max: 128, message: '行政区划不能超过128字', trigger: 'blur' }],
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
rules: [{ max: 32, message: '行政区划编码不能超过32字', trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '详细地址',
|
||||
@@ -295,20 +354,24 @@ export default {
|
||||
{
|
||||
label: '经度',
|
||||
prop: 'longitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
formslot: true,
|
||||
minWidth: 130,
|
||||
overHidden: false,
|
||||
rules: [{ validator: validateLongitude, trigger: 'blur' }],
|
||||
rules: [
|
||||
{ required: true, message: '请输入经度', trigger: 'blur' },
|
||||
{ validator: validateLongitude, trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '纬度',
|
||||
prop: 'latitude',
|
||||
type: 'number',
|
||||
precision: 6,
|
||||
formslot: true,
|
||||
minWidth: 130,
|
||||
overHidden: false,
|
||||
rules: [{ validator: validateLatitude, trigger: 'blur' }],
|
||||
rules: [
|
||||
{ required: true, message: '请输入纬度', trigger: 'blur' },
|
||||
{ validator: validateLatitude, trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '数据来源',
|
||||
@@ -317,6 +380,8 @@ export default {
|
||||
search: true,
|
||||
minWidth: 120,
|
||||
overHidden: false,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
dicData: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '初始化导入', value: '初始化导入' },
|
||||
@@ -333,6 +398,8 @@ export default {
|
||||
dataType: 'number',
|
||||
minWidth: 110,
|
||||
overHidden: false,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
dicData: [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '启用', value: 1 },
|
||||
@@ -383,6 +450,7 @@ export default {
|
||||
],
|
||||
},
|
||||
excelOption: {
|
||||
labelPosition: 'right',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
@@ -419,7 +487,9 @@ export default {
|
||||
},
|
||||
isAdmin() {
|
||||
const authority = this.userInfo && this.userInfo.authority;
|
||||
return Array.isArray(authority) ? authority.includes('admin') : String(authority || '').includes('admin');
|
||||
return Array.isArray(authority)
|
||||
? authority.includes('admin')
|
||||
: String(authority || '').includes('admin');
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
@@ -456,6 +526,22 @@ export default {
|
||||
this.findColumn('cityCode').dicData = this.cityOptions;
|
||||
});
|
||||
},
|
||||
loadDistrictOptions(cityCode) {
|
||||
if (!cityCode) {
|
||||
this.districtOptions = [];
|
||||
this.findColumn('districtCode').dicData = [];
|
||||
return Promise.resolve();
|
||||
}
|
||||
this.districtLoading = true;
|
||||
return getLazyTree(cityCode)
|
||||
.then(res => {
|
||||
this.districtOptions = (res.data.data || []).map(this.normalizeRegionOption);
|
||||
this.findColumn('districtCode').dicData = this.districtOptions;
|
||||
})
|
||||
.finally(() => {
|
||||
this.districtLoading = false;
|
||||
});
|
||||
},
|
||||
normalizeRegionOption(region) {
|
||||
return {
|
||||
...region,
|
||||
@@ -471,6 +557,10 @@ export default {
|
||||
: String(row.provinceCode),
|
||||
cityCode:
|
||||
row.cityCode === undefined || row.cityCode === null ? row.cityCode : String(row.cityCode),
|
||||
districtCode:
|
||||
row.districtCode === undefined || row.districtCode === null
|
||||
? row.districtCode
|
||||
: String(row.districtCode),
|
||||
};
|
||||
},
|
||||
ensureCityOption(row) {
|
||||
@@ -486,6 +576,19 @@ export default {
|
||||
];
|
||||
this.findColumn('cityCode').dicData = this.cityOptions;
|
||||
},
|
||||
ensureDistrictOption(row) {
|
||||
if (!row.districtCode || !row.districtName) return;
|
||||
const exists = this.districtOptions.some(item => item.id === row.districtCode);
|
||||
if (exists) return;
|
||||
this.districtOptions = [
|
||||
...this.districtOptions,
|
||||
{
|
||||
id: row.districtCode,
|
||||
title: row.districtName,
|
||||
},
|
||||
];
|
||||
this.findColumn('districtCode').dicData = this.districtOptions;
|
||||
},
|
||||
handleTmisChange(value) {
|
||||
this.form.tmisCode = String(value || '')
|
||||
.replace(/\D/g, '')
|
||||
@@ -507,20 +610,61 @@ export default {
|
||||
}
|
||||
this.form.cityCode = undefined;
|
||||
this.form.cityName = '';
|
||||
this.form.districtCode = undefined;
|
||||
this.form.districtName = '';
|
||||
this.form.regionCode = '';
|
||||
this.form.regionName = '';
|
||||
this.loadCityOptions(value);
|
||||
this.loadDistrictOptions();
|
||||
},
|
||||
handleCityChange(value) {
|
||||
const city = this.cityOptions.find(item => item.id === value);
|
||||
this.form.cityName = city ? city.title : '';
|
||||
if (this.regionInitializing) {
|
||||
this.loadDistrictOptions(value).then(() => this.ensureDistrictOption(this.form));
|
||||
return;
|
||||
}
|
||||
this.form.districtCode = undefined;
|
||||
this.form.districtName = '';
|
||||
this.form.regionCode = '';
|
||||
this.form.regionName = '';
|
||||
this.loadDistrictOptions(value);
|
||||
},
|
||||
handleDistrictChange(value) {
|
||||
const district = this.districtOptions.find(item => item.id === value);
|
||||
this.form.districtName = district ? district.title : '';
|
||||
this.form.regionCode = value || '';
|
||||
this.form.regionName = district ? district.title : '';
|
||||
},
|
||||
handleCoordinateInput(prop, value) {
|
||||
this.form[prop] = normalizeCoordinateInput(value);
|
||||
},
|
||||
normalizeRow(row) {
|
||||
row.code = row.tmisCode ? `TL${row.tmisCode}` : row.code;
|
||||
row.regionCode = String(row.regionCode || '').trim();
|
||||
row.regionName = String(row.regionName || '').trim();
|
||||
row.districtCode = String(row.districtCode || '').trim();
|
||||
row.districtName = String(row.districtName || '').trim();
|
||||
row.detailAddress = String(row.detailAddress || '').trim();
|
||||
row.longitude = normalizeCoordinateInput(row.longitude);
|
||||
row.latitude = normalizeCoordinateInput(row.latitude);
|
||||
if (!row.dataSource) row.dataSource = '手动';
|
||||
if (!row.status) row.status = 1;
|
||||
return row;
|
||||
},
|
||||
validateCoordinateFields(row) {
|
||||
const longitudeMessage = getCoordinateValidationMessage(row.longitude, 'longitude', true);
|
||||
if (longitudeMessage) {
|
||||
this.$message.warning(longitudeMessage);
|
||||
return false;
|
||||
}
|
||||
const latitudeMessage = getCoordinateValidationMessage(row.latitude, 'latitude', true);
|
||||
if (latitudeMessage) {
|
||||
this.$message.warning(latitudeMessage);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
validateUnique(row) {
|
||||
const rowId = String(row.id || '');
|
||||
const hasDuplicate = (params, prop) => {
|
||||
@@ -552,6 +696,10 @@ export default {
|
||||
},
|
||||
rowSave(row, done, loading) {
|
||||
const submitRow = this.normalizeRow(row);
|
||||
if (!this.validateCoordinateFields(submitRow)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
this.validateUnique(submitRow)
|
||||
.then(() => submit(submitRow))
|
||||
.then(() => {
|
||||
@@ -563,6 +711,10 @@ export default {
|
||||
},
|
||||
rowUpdate(row, index, done, loading) {
|
||||
const submitRow = this.normalizeRow(row);
|
||||
if (!this.validateCoordinateFields(submitRow)) {
|
||||
loading();
|
||||
return;
|
||||
}
|
||||
this.validateUnique(submitRow)
|
||||
.then(() => submit(submitRow))
|
||||
.then(() => {
|
||||
@@ -620,23 +772,30 @@ export default {
|
||||
this.regionInitializing = true;
|
||||
getDetail(this.form.id).then(res => {
|
||||
const detail = this.normalizeRegionForm(res.data.data || {});
|
||||
this.loadCityOptions(detail.provinceCode).then(() => {
|
||||
this.ensureCityOption(detail);
|
||||
this.form = detail;
|
||||
done();
|
||||
this.$nextTick(() => {
|
||||
window.setTimeout(() => {
|
||||
this.regionInitializing = false;
|
||||
}, 0);
|
||||
this.loadCityOptions(detail.provinceCode)
|
||||
.then(() => {
|
||||
this.ensureCityOption(detail);
|
||||
return this.loadDistrictOptions(detail.cityCode);
|
||||
})
|
||||
.then(() => {
|
||||
this.ensureDistrictOption(detail);
|
||||
this.form = detail;
|
||||
done();
|
||||
this.$nextTick(() => {
|
||||
window.setTimeout(() => {
|
||||
this.regionInitializing = false;
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.form.code = '';
|
||||
this.form.dataSource = '手动';
|
||||
this.form.status = 1;
|
||||
this.loadCityOptions(this.form.provinceCode).then(() => done());
|
||||
this.loadCityOptions(this.form.provinceCode)
|
||||
.then(() => this.loadDistrictOptions(this.form.cityCode))
|
||||
.then(() => done());
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
@@ -678,23 +837,7 @@ export default {
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
uploadBefore(file, done) {
|
||||
const fileName = file && file.name ? file.name.toLowerCase() : '';
|
||||
if (!/\.(xls|xlsx)$/.test(fileName)) {
|
||||
this.$message.error('请上传 .xls,.xlsx 标准格式文件');
|
||||
return false;
|
||||
}
|
||||
if (typeof done === 'function') {
|
||||
done();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
uploadAfter(res, done) {
|
||||
this.excelBox = false;
|
||||
this.onLoad(this.page);
|
||||
done();
|
||||
openImportDialog(this, '铁路车站主数据');
|
||||
},
|
||||
handleExport() {
|
||||
this.$confirm('是否导出铁路车站主数据?', '提示', {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col :span="9">
|
||||
<el-row class="region-page">
|
||||
<el-col class="region-page__tree-panel" :span="6">
|
||||
<div class="box">
|
||||
<el-scrollbar>
|
||||
<basic-container>
|
||||
@@ -9,7 +9,7 @@
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="15">
|
||||
<el-col class="region-page__form-panel" :span="18">
|
||||
<basic-container>
|
||||
<el-button-group>
|
||||
<el-button
|
||||
@@ -55,17 +55,34 @@
|
||||
@click="handleSync"
|
||||
>同步
|
||||
</el-button>
|
||||
<!-- <el-button-->
|
||||
<!-- v-if="permission.region_debug"-->
|
||||
<!-- type="primary"-->
|
||||
<!-- icon="el-icon-video-play"-->
|
||||
<!-- @click="handleDebug"-->
|
||||
<!-- >调试-->
|
||||
<!-- </el-button>-->
|
||||
<!-- <el-button-->
|
||||
<!-- v-if="permission.region_debug"-->
|
||||
<!-- type="primary"-->
|
||||
<!-- icon="el-icon-video-play"-->
|
||||
<!-- @click="handleDebug"-->
|
||||
<!-- >调试-->
|
||||
<!-- </el-button>-->
|
||||
</el-button-group>
|
||||
</basic-container>
|
||||
<basic-container>
|
||||
<avue-form ref="form" :option="regionOption" v-model="regionForm" @submit="handleSubmit">
|
||||
<template #parentName="{}">
|
||||
<el-select
|
||||
class="region-parent-select"
|
||||
v-model="regionForm.parentCode"
|
||||
filterable
|
||||
:disabled="isCountryRegion"
|
||||
placeholder="请选择父区划"
|
||||
@change="handleParentChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in parentOptions"
|
||||
:key="item.code"
|
||||
:label="item.name"
|
||||
:value="item.code"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
<template #code="{}">
|
||||
<el-input
|
||||
v-if="isCountryRegion"
|
||||
@@ -83,7 +100,7 @@
|
||||
</template>
|
||||
</avue-form>
|
||||
<el-dialog title="行政区划数据导入" append-to-body v-model="excelBox" width="555px">
|
||||
<avue-form :option="excelOption" v-model="excelForm" :upload-after="uploadAfter">
|
||||
<avue-form :option="excelOption" v-model="excelForm">
|
||||
<template #excelTemplate>
|
||||
<el-button type="primary" @click="handleTemplate">
|
||||
点击下载<i class="el-icon-download el-icon--right"></i>
|
||||
@@ -105,6 +122,7 @@ import { exportBlob } from '@/api/common';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { validatenull } from '@/utils/validate';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { openImportDialog } from '@/utils/import-excel';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
@@ -118,6 +136,7 @@ export default {
|
||||
treeCode: '',
|
||||
treeParentCode: '',
|
||||
treeData: [],
|
||||
parentOptions: [],
|
||||
syncLoading: false,
|
||||
treeOption: {
|
||||
nodeKey: 'id',
|
||||
@@ -137,7 +156,7 @@ export default {
|
||||
},
|
||||
addBtn: false,
|
||||
menu: false,
|
||||
size: 'small',
|
||||
size: 'default',
|
||||
props: {
|
||||
labelText: '标题',
|
||||
label: 'title',
|
||||
@@ -147,13 +166,13 @@ export default {
|
||||
},
|
||||
regionForm: {},
|
||||
regionOption: {
|
||||
labelWidth: 100,
|
||||
labelWidth: 'auto',
|
||||
column: [
|
||||
{
|
||||
label: '父区划编号',
|
||||
prop: 'parentCode',
|
||||
span: 24,
|
||||
disabled: true,
|
||||
display: false,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
@@ -166,7 +185,7 @@ export default {
|
||||
label: '父区划名称',
|
||||
prop: 'parentName',
|
||||
span: 24,
|
||||
disabled: true,
|
||||
formslot: true,
|
||||
},
|
||||
{
|
||||
label: '区划编号',
|
||||
@@ -295,7 +314,7 @@ export default {
|
||||
debugBox: false,
|
||||
debugForm: {},
|
||||
debugOption: {
|
||||
labelWidth: 50,
|
||||
labelWidth: 'auto',
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
column: [
|
||||
@@ -345,24 +364,25 @@ export default {
|
||||
if (this.isCountryRegion) {
|
||||
return;
|
||||
}
|
||||
const parentCode = this.isProvinceRegion ? '' : this.regionForm.parentCode;
|
||||
this.regionForm.code = parentCode + this.regionForm.subCode;
|
||||
this.syncRegionCode();
|
||||
},
|
||||
'excelForm.isCovered'() {
|
||||
if (this.excelForm.isCovered !== '') {
|
||||
const column = this.findColumn(this.excelOption.column, 'excelFile');
|
||||
column.action = `/blade-system/region/import-region?isCovered=${this.excelForm.isCovered}`;
|
||||
}
|
||||
this.syncImportAction();
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission']),
|
||||
isCountryRegion() {
|
||||
return Number(this.regionForm.regionLevel) === 0 || this.regionForm.parentCode === this.topCode;
|
||||
return (
|
||||
Number(this.regionForm.regionLevel) === 0 || this.regionForm.parentCode === this.topCode
|
||||
);
|
||||
},
|
||||
isProvinceRegion() {
|
||||
return Number(this.regionForm.regionLevel) === 1;
|
||||
},
|
||||
isEditRegion() {
|
||||
return !validatenull(this.regionForm.originalCode);
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.region_add, false),
|
||||
@@ -392,28 +412,30 @@ export default {
|
||||
});
|
||||
},
|
||||
nodeClick(data) {
|
||||
const column = this.findColumn(this.regionOption.column, 'parentCode');
|
||||
column.disabled = true;
|
||||
this.setRegionColumnDisplay('code', true);
|
||||
this.treeCode = data.id;
|
||||
this.treeParentCode = data.parentId;
|
||||
getDetail(this.treeCode).then(res => {
|
||||
this.regionForm = res.data.data;
|
||||
this.regionForm.originalCode = this.regionForm.code;
|
||||
this.regionForm.subCode =
|
||||
this.regionForm.parentCode === this.topCode
|
||||
? this.regionForm.code
|
||||
: this.regionForm.code.replace(this.regionForm.parentCode, '');
|
||||
this.loadParentOptions(this.regionForm.parentCode, this.regionForm.parentName);
|
||||
});
|
||||
},
|
||||
addCountry() {
|
||||
const column = this.findColumn(this.regionOption.column, 'parentCode');
|
||||
column.disabled = true;
|
||||
this.setRegionColumnDisplay('code', true);
|
||||
this.treeCode = '';
|
||||
this.treeParentCode = this.topCode;
|
||||
this.parentOptions = this.getRootParentOptions();
|
||||
this.regionForm = {
|
||||
parentCode: this.topCode,
|
||||
parentName: '根节点',
|
||||
code: '',
|
||||
subCode: '',
|
||||
originalCode: '',
|
||||
name: '',
|
||||
regionLevel: 0,
|
||||
sort: 1,
|
||||
@@ -424,13 +446,84 @@ export default {
|
||||
this.$message.warning('请先选择一项区划');
|
||||
return;
|
||||
}
|
||||
this.regionForm.parentCode = this.regionForm.code;
|
||||
this.regionForm.parentName = this.regionForm.name;
|
||||
const parentCode = this.regionForm.code;
|
||||
const parentName = this.regionForm.name;
|
||||
const parentGroupCode = this.regionForm.parentCode || this.topCode;
|
||||
this.setRegionColumnDisplay('code', true);
|
||||
this.regionForm.parentCode = parentCode;
|
||||
this.regionForm.parentName = parentName;
|
||||
this.regionForm.code = '';
|
||||
this.regionForm.subCode = '';
|
||||
this.regionForm.originalCode = '';
|
||||
this.regionForm.name = '';
|
||||
this.regionForm.regionLevel =
|
||||
this.regionForm.regionLevel === 5 ? 5 : this.regionForm.regionLevel + 1;
|
||||
this.loadParentOptionsByGroup(parentGroupCode, parentCode, parentName);
|
||||
},
|
||||
setRegionColumnDisplay(prop, display) {
|
||||
const column = this.findColumn(this.regionOption.column, prop);
|
||||
if (column) {
|
||||
column.display = display;
|
||||
}
|
||||
},
|
||||
handleParentChange(value) {
|
||||
const parent = this.parentOptions.find(item => item.code === value);
|
||||
this.regionForm.parentName = parent ? parent.name : '';
|
||||
this.syncRegionCode();
|
||||
},
|
||||
syncRegionCode() {
|
||||
if (this.isCountryRegion) {
|
||||
return;
|
||||
}
|
||||
const parentCode = this.isProvinceRegion ? '' : this.regionForm.parentCode || '';
|
||||
this.regionForm.code = parentCode + (this.regionForm.subCode || '');
|
||||
},
|
||||
getRootParentOptions() {
|
||||
return [
|
||||
{
|
||||
code: this.topCode,
|
||||
name: '根节点',
|
||||
},
|
||||
];
|
||||
},
|
||||
normalizeParentOption(region) {
|
||||
return {
|
||||
code: String(region.code || region.id || ''),
|
||||
name: region.name || region.title || '',
|
||||
};
|
||||
},
|
||||
ensureSelectedParent(options, selectedCode, selectedName) {
|
||||
if (validatenull(selectedCode) || options.some(item => item.code === selectedCode)) {
|
||||
return options;
|
||||
}
|
||||
return [
|
||||
...options,
|
||||
{
|
||||
code: String(selectedCode),
|
||||
name: selectedName || selectedCode,
|
||||
},
|
||||
];
|
||||
},
|
||||
loadParentOptions(selectedCode, selectedName) {
|
||||
if (validatenull(selectedCode) || selectedCode === this.topCode) {
|
||||
this.parentOptions = this.getRootParentOptions();
|
||||
return;
|
||||
}
|
||||
getDetail(selectedCode).then(res => {
|
||||
const parent = res.data.data || {};
|
||||
const groupCode = parent.parentCode || this.topCode;
|
||||
this.loadParentOptionsByGroup(groupCode, selectedCode, selectedName);
|
||||
});
|
||||
},
|
||||
loadParentOptionsByGroup(groupCode, selectedCode, selectedName) {
|
||||
if (validatenull(groupCode)) {
|
||||
this.parentOptions = this.ensureSelectedParent([], selectedCode, selectedName);
|
||||
return;
|
||||
}
|
||||
getLazyTree(groupCode).then(res => {
|
||||
const options = res.data.data.map(this.normalizeParentOption).filter(item => item.code);
|
||||
this.parentOptions = this.ensureSelectedParent(options, selectedCode, selectedName);
|
||||
});
|
||||
},
|
||||
handleSubmit(form, done) {
|
||||
if (Number(form.regionLevel) === 0) {
|
||||
@@ -481,18 +574,27 @@ export default {
|
||||
this.$refs.form.resetForm();
|
||||
});
|
||||
},
|
||||
uploadAfter(res, done, loading, column) {
|
||||
window.console.log(column);
|
||||
this.excelBox = false;
|
||||
this.initTree();
|
||||
done();
|
||||
handleImport() {
|
||||
this.syncImportAction();
|
||||
openImportDialog(this, '行政区划', () => {
|
||||
this.initTree();
|
||||
});
|
||||
},
|
||||
syncImportAction() {
|
||||
const column = this.findColumn(this.excelOption.column, 'excelFile');
|
||||
if (!column) {
|
||||
return;
|
||||
}
|
||||
const covered = this.excelForm.isCovered;
|
||||
if (covered === '' || covered === undefined || covered === null) {
|
||||
column.action = '/blade-system/region/import-region';
|
||||
} else {
|
||||
column.action = `/blade-system/region/import-region?isCovered=${covered}`;
|
||||
}
|
||||
},
|
||||
handleDebug() {
|
||||
this.debugBox = true;
|
||||
},
|
||||
handleImport() {
|
||||
this.excelBox = true;
|
||||
},
|
||||
handleSync() {
|
||||
this.$confirm('确定同步最新行政区划数据?', {
|
||||
confirmButtonText: '确定',
|
||||
@@ -553,4 +655,46 @@ export default {
|
||||
.box .el-scrollbar__wrap {
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.region-parent-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.region-page .region-page__tree-panel {
|
||||
flex: 0 0 17.5%;
|
||||
max-width: 17.5%;
|
||||
}
|
||||
|
||||
.region-page .region-page__form-panel {
|
||||
flex: 0 0 82.5%;
|
||||
max-width: 82.5%;
|
||||
}
|
||||
|
||||
.region-page__tree-panel .avue-tree__filter {
|
||||
padding: 0 8px 8px;
|
||||
}
|
||||
|
||||
.region-page__tree-panel .avue-tree__filter .el-input,
|
||||
.region-page__tree-panel .avue-tree__filter .el-input__wrapper {
|
||||
width: 100%;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.region-page__tree-panel .el-tree.el-tree--highlight-current {
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.region-page__form-panel .avue-form__group.avue-form__group--flex {
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.region-page .region-page__tree-panel,
|
||||
.region-page .region-page__form-panel {
|
||||
flex: 0 0 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -89,8 +89,9 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchIndex: 3,
|
||||
searchMenuSpan: 24,
|
||||
searchMenuPosition: 'right',
|
||||
searchIndex: 8,
|
||||
searchIcon: true,
|
||||
border: true,
|
||||
index: true,
|
||||
@@ -99,7 +100,7 @@ export default {
|
||||
addBtn: false,
|
||||
delBtn: false,
|
||||
menuWidth: 120,
|
||||
labelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
dialogType: 'drawer',
|
||||
column: [
|
||||
{
|
||||
|
||||
@@ -59,7 +59,10 @@ const option = reactive({
|
||||
dialogWidth: 950,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
|
||||
@@ -60,7 +60,10 @@ export default {
|
||||
dialogWidth: 950,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
|
||||
@@ -24,7 +24,7 @@ export default {
|
||||
file: {},
|
||||
},
|
||||
option: {
|
||||
labelWidth: 120,
|
||||
labelWidth: 'auto',
|
||||
menuBtn: false,
|
||||
column: [
|
||||
{
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</template>
|
||||
</avue-crud>
|
||||
<el-dialog title="流程删除" append-to-body v-model="followBox" width="20%">
|
||||
<el-form :model="form" ref="form" label-width="80px">
|
||||
<el-form :model="form" ref="form" label-width="auto">
|
||||
<el-form-item label="删除理由">
|
||||
<el-input v-model="deleteReason" placeholder="请输入删除理由" />
|
||||
</el-form-item>
|
||||
@@ -62,7 +62,10 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog title="流程变更" append-to-body v-model="stateBox" width="20%">
|
||||
<el-form :model="form" ref="form" label-width="80px">
|
||||
<el-form :model="form" ref="form" label-width="auto">
|
||||
<el-form-item label="流程状态">
|
||||
<el-select v-model="flowState" placeholder="请选择" value="">
|
||||
<el-option
|
||||
@@ -150,7 +150,10 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
|
||||
@@ -207,7 +207,10 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
|
||||
@@ -41,7 +41,10 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
|
||||
@@ -41,7 +41,10 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
|
||||
@@ -41,7 +41,10 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
|
||||
@@ -69,7 +69,10 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
|
||||
@@ -108,7 +108,10 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
|
||||
@@ -86,14 +86,17 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
grid: true,
|
||||
menuWidth: 350,
|
||||
labelWidth: 100,
|
||||
labelWidth: 'auto',
|
||||
dialogWidth: 880,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
@@ -103,7 +106,7 @@ export default {
|
||||
value: 1,
|
||||
span: 24,
|
||||
width: 120,
|
||||
searchLabelWidth: 50,
|
||||
searchLabelWidth: 160,
|
||||
row: true,
|
||||
dicUrl: '/blade-system/dict/dictionary?code=oss',
|
||||
props: {
|
||||
|
||||
@@ -86,14 +86,17 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
selection: true,
|
||||
grid: true,
|
||||
menuWidth: 350,
|
||||
labelWidth: 100,
|
||||
labelWidth: 'auto',
|
||||
dialogWidth: 880,
|
||||
dialogClickModal: false,
|
||||
column: [
|
||||
@@ -103,7 +106,7 @@ export default {
|
||||
value: 1,
|
||||
span: 24,
|
||||
width: 100,
|
||||
searchLabelWidth: 50,
|
||||
searchLabelWidth: 160,
|
||||
row: true,
|
||||
dicUrl: '/blade-system/dict/dictionary?code=sms',
|
||||
props: {
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
|
||||
<!-- 手动锁定用户 -->
|
||||
<el-dialog title="锁定用户" append-to-body v-model="lockUserBox" width="500px">
|
||||
<el-form :model="lockUserForm" label-width="100px" :rules="lockUserRules" ref="lockUserFormRef">
|
||||
<el-form :model="lockUserForm" label-width="auto" :rules="lockUserRules" ref="lockUserFormRef">
|
||||
<el-form-item label="用户账号">
|
||||
<el-input :model-value="lockUserForm.account" disabled />
|
||||
</el-form-item>
|
||||
@@ -108,7 +108,7 @@
|
||||
|
||||
<!-- 解锁原因确认 -->
|
||||
<el-dialog title="解锁确认" append-to-body v-model="unlockReasonBox" width="450px">
|
||||
<el-form :model="unlockReasonForm" label-width="80px">
|
||||
<el-form :model="unlockReasonForm" label-width="auto">
|
||||
<el-form-item label="解锁原因">
|
||||
<el-input
|
||||
v-model="unlockReasonForm.unlockReason"
|
||||
|
||||
@@ -55,7 +55,10 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
|
||||
@@ -73,7 +73,10 @@ export default {
|
||||
tip: false,
|
||||
simplePage: true,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
tree: true,
|
||||
border: true,
|
||||
index: true,
|
||||
|
||||
@@ -79,7 +79,10 @@ export default {
|
||||
tip: false,
|
||||
simplePage: true,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
dialogWidth: '60%',
|
||||
tree: true,
|
||||
border: true,
|
||||
@@ -137,13 +140,6 @@ export default {
|
||||
type: 'icon',
|
||||
slot: true,
|
||||
iconList: iconList,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入菜单图标',
|
||||
trigger: 'click',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '菜单编号',
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
:before-close="beforeClose"
|
||||
append-to-body
|
||||
>
|
||||
<el-form :disabled="view" ref="form" :model="form" label-width="80px">
|
||||
<el-form :disabled="view" ref="form" :model="form" label-width="auto">
|
||||
<!-- 表单字段 -->
|
||||
<el-form-item label="参数名:" prop="paramName">
|
||||
<el-input v-model="form.paramName" placeholder="请输入参数名" />
|
||||
|
||||
@@ -59,7 +59,10 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
|
||||
@@ -231,7 +231,10 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
selection: true,
|
||||
|
||||
@@ -55,7 +55,10 @@ export default {
|
||||
dialogWidth: 900,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
@@ -84,7 +87,7 @@ export default {
|
||||
value: 1,
|
||||
span: 24,
|
||||
width: 120,
|
||||
searchLabelWidth: 100,
|
||||
searchLabelWidth: 160,
|
||||
row: true,
|
||||
gridRow: true,
|
||||
dicUrl: '/blade-system/dict/dictionary?code=datasource_category',
|
||||
|
||||
@@ -90,7 +90,10 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
|
||||
@@ -165,7 +165,10 @@ export default {
|
||||
calcHeight: 32,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
|
||||
@@ -275,7 +275,7 @@
|
||||
ref="passwordFormRef"
|
||||
:model="passwordForm"
|
||||
:rules="passwordRules"
|
||||
label-width="90px"
|
||||
label-width="auto"
|
||||
>
|
||||
<el-form-item label="登录账号">
|
||||
<el-input v-model="passwordForm.account" disabled />
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
|
||||
<!-- 个人信息 Tab -->
|
||||
<el-tab-pane label="个人信息" name="info">
|
||||
<el-form ref="infoForm" :model="infoForm" label-width="100px">
|
||||
<el-form ref="infoForm" :model="infoForm" label-width="auto">
|
||||
<el-form-item label="头像">
|
||||
<div class="avatar-upload-container">
|
||||
<div class="avatar-wrapper" v-if="infoForm.avatar">
|
||||
@@ -74,7 +74,7 @@
|
||||
|
||||
<!-- 修改密码 Tab -->
|
||||
<el-tab-pane label="修改密码" name="password">
|
||||
<el-form ref="passwordForm" :model="passwordForm" label-width="100px">
|
||||
<el-form ref="passwordForm" :model="passwordForm" label-width="auto">
|
||||
<el-form-item label="原密码">
|
||||
<el-input
|
||||
v-model="passwordForm.oldPassword"
|
||||
|
||||
@@ -57,7 +57,10 @@ export default {
|
||||
dialogWidth: 900,
|
||||
tip: false,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
viewBtn: true,
|
||||
@@ -86,7 +89,7 @@ export default {
|
||||
value: 1,
|
||||
span: 24,
|
||||
width: 120,
|
||||
searchLabelWidth: 100,
|
||||
searchLabelWidth: 160,
|
||||
row: true,
|
||||
gridRow: true,
|
||||
display: false,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user