init commit & 混料程序模块
2
.env.development
Normal file
@ -0,0 +1,2 @@
|
||||
NODE_ENV=development
|
||||
VUE_APP_NODE_ENV=dev
|
2
.env.production
Normal file
@ -0,0 +1,2 @@
|
||||
NODE_ENV=production
|
||||
VUE_APP_NODE_ENV=prod
|
2
.env.production.sit
Normal file
@ -0,0 +1,2 @@
|
||||
NODE_ENV=production
|
||||
VUE_APP_NODE_ENV=prod:sit
|
2
.env.production.uat
Normal file
@ -0,0 +1,2 @@
|
||||
NODE_ENV=production
|
||||
VUE_APP_NODE_ENV=prod:uat
|
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw*
|
||||
|
||||
# lock
|
||||
package-lock.json
|
201
LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
104
README.md
Normal file
@ -0,0 +1,104 @@
|
||||
## renren-ui
|
||||
- renren-ui基于vue、element-ui构建开发,实现 【[renren-security](https://gitee.com/renrenio/renren-security)】 后台管理前端功能,提供一套更优的前端解决方案
|
||||
- 前后端分离,通过token进行数据交互,可独立部署
|
||||
- 动态菜单,通过菜单管理统一管理访问路由
|
||||
- 后端地址:https://gitee.com/renrenio/renren-security
|
||||
- 演示地址:[http://demo.open.renren.io/renren-security](http://demo.open.renren.io/renren-security) (账号密码:admin/admin)
|
||||
|
||||
<br>
|
||||
|
||||
![输入图片说明](public/1.png)
|
||||
|
||||
## 安装
|
||||
|
||||
您需要提前在本地安装[Node.js](https://nodejs.org/en/),版本号为:14.x,再使用[Git](https://git-scm.com/)克隆项目或者直接下载项目后,然后通过`终端命令行`执行以下命令。
|
||||
|
||||
```bash
|
||||
# 切换到项目根目录
|
||||
|
||||
# 安装插件
|
||||
npm install
|
||||
|
||||
# 启动项目
|
||||
npm run serve
|
||||
```
|
||||
|
||||
> 如网络不稳定,安装时出错或进度过慢!请移步 [cnpm](https://npmmirror.com/) 淘宝镜像进行安装。
|
||||
|
||||
启动完成后,会自动打开浏览器访问 [http://localhost:8001](http://localhost:8001),如您看到下面的页面代表`前端项目`运行成功!因为前后端分离项目,需保证`前端项目`和`后台项目`分别独立正常运行。
|
||||
|
||||
请留意下面的页面,其中`验证码`未能正常显示,控制台有`API请求`报错信息!这时需检查`后台项目`是否正常运行。
|
||||
|
||||
|
||||
## 技术栈
|
||||
|
||||
提前了解和学习这些知识会对使用本项目有很大的帮助。
|
||||
|
||||
* [Node.js](https://nodejs.org/)
|
||||
* [ES6](http://es6.ruanyifeng.com/)
|
||||
* [Vue-cli](https://github.com/vuejs/vue-cli)
|
||||
* [Vue](https://cn.vuejs.org/)
|
||||
* [Vue-router](https://router.vuejs.org/zh/)
|
||||
* [Vuex](https://vuex.vuejs.org/zh/)
|
||||
* [Vue-i18n](https://github.com/kazupon/vue-i18n)
|
||||
* [Axios](https://github.com/axios/axios)
|
||||
* [Element](https://element.eleme.cn/#/zh-CN)
|
||||
* [JS-cookie](https://github.com/js-cookie/js-cookie)
|
||||
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
├── src
|
||||
│ ├── assets // 静态资源
|
||||
│ ├── components // 公共组件
|
||||
│ ├── element-ui // element样式
|
||||
│ ├── i18n // 国际化
|
||||
│ ├── icons // 图标
|
||||
│ ├── mixins // 混入
|
||||
│ ├── router // 路由
|
||||
│ ├── store // 状态管理
|
||||
│ ├── utils // 工具类
|
||||
│ ├── views // 业务相关
|
||||
│ ├── App.vue
|
||||
│ ├── main.js // 入口
|
||||
├── ...
|
||||
├── package-lock.json
|
||||
├── package.json
|
||||
└── vue.config.js // vue-cli脚手架配置
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
## 常见问题
|
||||
|
||||
如何修改API请求地址?
|
||||
* 修改`/src/pubilc/index.html`文件中`<!-- 开发环境 -->`注释下的`window.SITE_CONFIG['apiURL']`变量值。
|
||||
```
|
||||
<!-- 开发环境 -->
|
||||
<% if (process.env.VUE_APP_NODE_ENV === 'dev') { %>
|
||||
<script>
|
||||
window.SITE_CONFIG['apiURL'] = 'http://localhost:8080/renren-admin';
|
||||
</script>
|
||||
<% } %>
|
||||
```
|
||||
<br>
|
||||
|
||||
## 如何交流、反馈、参与贡献?
|
||||
- 开发文档:https://www.renren.io/guide/security
|
||||
- 官方社区:https://www.renren.io/community
|
||||
- Gitee仓库:https://gitee.com/renrenio/renren-ui
|
||||
- [人人开源](https://www.renren.io):https://www.renren.io
|
||||
- 如需关注项目最新动态,请Watch、Star项目,同时也是对项目最好的支持
|
||||
- 技术讨论、二次开发等咨询、问题和建议,请移步到官方社区,我会在第一时间进行解答和回复!
|
||||
- 微信扫码并关注【人人开源】,获得项目最新动态及更新提醒<br>
|
||||
|
||||
<br>
|
||||
|
||||
## 微信交流群
|
||||
我们提供了微信交流群,扫码下面的二维码,关注【人人开源】公众号,回复【加群】,即可根据提示加入微信群!
|
||||
<br><br>
|
||||
![输入图片说明](public/wechat.jpg)
|
||||
|
||||
<br>
|
||||
<br>
|
5
babel.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
54
package.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "renren-ui",
|
||||
"version": "5.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"build:prod": "vue-cli-service build --mode production",
|
||||
"build:sit": "vue-cli-service build --mode production.sit",
|
||||
"build:uat": "vue-cli-service build --mode production.uat"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/x6": "^1.33.1",
|
||||
"@antv/x6-react-components": "^1.1.15",
|
||||
"axios": "^0.19.2",
|
||||
"babel-eslint": "^8.0.1",
|
||||
"babel-plugin-component": "^1.1.1",
|
||||
"code-brick-zj": "^0.0.7",
|
||||
"core-js": "^3.6.5",
|
||||
"echarts": "^5.3.3",
|
||||
"element-theme": "^2.0.1",
|
||||
"element-ui": "^2.15.7",
|
||||
"js-cookie": "^2.2.1",
|
||||
"lodash": "^4.17.19",
|
||||
"moment": "^2.29.4",
|
||||
"qs": "^6.9.4",
|
||||
"quill": "^1.3.7",
|
||||
"sass": "^1.26.5",
|
||||
"sass-loader": "^9.0.2",
|
||||
"screenfull": "^4.2.1",
|
||||
"svg-sprite-loader": "^5.2.1",
|
||||
"vue": "^2.6.11",
|
||||
"vue-cron": "^1.0.9",
|
||||
"vue-i18n": "^8.18.2",
|
||||
"vue-router": "3.0.7",
|
||||
"vuex": "^3.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.4.6",
|
||||
"@vue/cli-service": "^4.4.6",
|
||||
"element-theme-chalk": "^2.15.7",
|
||||
"natives": "^1.1.6",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie <= 10"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 8.11.1",
|
||||
"npm": ">= 5.6.0"
|
||||
}
|
||||
}
|
BIN
public/1.png
Normal file
After Width: | Height: | Size: 293 KiB |
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 1.9 KiB |
65
public/index.html
Normal file
@ -0,0 +1,65 @@
|
||||
<!--
|
||||
* @Author: zwq
|
||||
* @Date: 2022-08-22 14:57:50
|
||||
* @LastEditors: zwq
|
||||
* @LastEditTime: 2023-01-04 11:27:34
|
||||
* @Description:
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<link rel="shortcut icon" href="<%= BASE_URL %>spc.jpg">
|
||||
<!-- 站点配置 -->
|
||||
<script>
|
||||
window.SITE_CONFIG = {};
|
||||
window.SITE_CONFIG['version'] = 'v5.0.0';
|
||||
window.SITE_CONFIG['nodeEnv'] = '<%= process.env.VUE_APP_NODE_ENV %>';
|
||||
window.SITE_CONFIG['apiURL'] = ''; // api请求地址
|
||||
window.SITE_CONFIG['storeState'] = {}; // vuex本地储存初始化状态(用于不刷新页面的情况下,也能重置初始化项目中所有状态)
|
||||
window.SITE_CONFIG['contentTabDefault'] = { // 内容标签页默认属性对象
|
||||
'name': '', // 名称, 由 this.$route.name 自动赋值(默认,名称 === 路由名称 === 路由路径)
|
||||
'params': {}, // 参数, 由 this.$route.params 自动赋值
|
||||
'query': {}, // 查询参数, 由 this.$route.query 自动赋值
|
||||
'menuId': '', // 菜单id(用于选中侧边栏菜单,与this.$store.state.sidebarMenuActiveName进行匹配)
|
||||
'title': '', // 标题
|
||||
'isTab': true, // 是否通过tab展示内容?
|
||||
'iframeURL': '' // 是否通过iframe嵌套展示内容? (以http[s]://开头, 自动匹配)
|
||||
};
|
||||
window.SITE_CONFIG['menuList'] = []; // 左侧菜单列表(后台返回,未做处理)
|
||||
window.SITE_CONFIG['permissions'] = []; // 页面按钮操作权限(后台返回,未做处理)
|
||||
window.SITE_CONFIG['dynamicRoutes'] = []; // 动态路由列表
|
||||
window.SITE_CONFIG['dynamicMenuRoutes'] = []; // 动态(菜单)路由列表
|
||||
window.SITE_CONFIG['dynamicMenuRoutesHasAdded'] = false; // 动态(菜单)路由是否已经添加的状态标示(用于判断是否需要重新拉取数据并进行动态添加操作)
|
||||
</script>
|
||||
|
||||
<!-- 开发环境 -->
|
||||
<% if (process.env.VUE_APP_NODE_ENV === 'dev') { %>
|
||||
<script>
|
||||
window.SITE_CONFIG['apiURL'] = 'http://192.168.1.103:8080/pms-am';
|
||||
</script>
|
||||
<% } %>
|
||||
<!-- 集成测试环境 -->
|
||||
<% if (process.env.VUE_APP_NODE_ENV === 'prod:sit') { %>
|
||||
<script>
|
||||
window.SITE_CONFIG['apiURL'] = 'http://localhost:8080/renren-admin';
|
||||
</script>
|
||||
<% } %>
|
||||
<!-- 验收测试环境 -->
|
||||
<% if (process.env.VUE_APP_NODE_ENV === 'prod:uat') { %>
|
||||
<script>
|
||||
window.SITE_CONFIG['apiURL'] = 'http://localhost:8080/renren-admin';
|
||||
</script>
|
||||
<% } %>
|
||||
<!-- 生产环境 -->
|
||||
<% if (process.env.VUE_APP_NODE_ENV === 'prod') { %>
|
||||
<script>
|
||||
window.SITE_CONFIG['apiURL'] = 'http://localhost:8080/renren-admin';
|
||||
</script>
|
||||
<% } %>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
BIN
public/spc.jpg
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
public/wechat.jpg
Normal file
After Width: | Height: | Size: 27 KiB |
34
src/App.vue
Normal file
@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<transition name="el-fade-in-linear">
|
||||
<router-view />
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.el-table th.gutter{
|
||||
display: table-cell!important;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import Cookies from 'js-cookie'
|
||||
import { messages } from '@/i18n'
|
||||
export default {
|
||||
watch: {
|
||||
'$i18n.locale': 'i18nHandle'
|
||||
},
|
||||
created () {
|
||||
this.i18nHandle(this.$i18n.locale)
|
||||
},
|
||||
methods: {
|
||||
i18nHandle (val, oldVal) {
|
||||
Cookies.set('language', val)
|
||||
document.querySelector('html').setAttribute('lang', val)
|
||||
document.title = messages[val].brand.lg
|
||||
// 非登录页面,切换语言刷新页面
|
||||
if (this.$route.name !== 'login' && oldVal) {
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
BIN
src/assets/img/avatar.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
src/assets/img/cnbm.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/assets/img/login-back.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
src/assets/img/login.gif
Normal file
After Width: | Height: | Size: 4.6 MiB |
BIN
src/assets/img/login_bg.jpg
Normal file
After Width: | Height: | Size: 135 KiB |
11
src/assets/scss/aui.scss
Normal file
@ -0,0 +1,11 @@
|
||||
// 变量
|
||||
@import "~@/element-ui/theme-variables.scss";
|
||||
@import "./variables.scss";
|
||||
// 公共
|
||||
@import "./normalize.scss";
|
||||
@import "./common.scss";
|
||||
// 页面
|
||||
@import "./pages/login.scss";
|
||||
@import "./pages/404.scss";
|
||||
// 模块
|
||||
@import "./modules/home.scss";
|
726
src/assets/scss/common.scss
Normal file
@ -0,0 +1,726 @@
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
|
||||
"微软雅黑", Arial, sans-serif;
|
||||
font-size: $--font-size-base;
|
||||
line-height: $base--line-height;
|
||||
color: $--color-text-primary;
|
||||
background-color: #fff;
|
||||
}
|
||||
a {
|
||||
color: mix(#fff, $--color-primary, 20%);
|
||||
text-decoration: none;
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: $--color-primary;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
:focus,
|
||||
:hover {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Utils
|
||||
------------------------------ */
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
.clearfix:before,
|
||||
.clearfix:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.clearfix:after {
|
||||
clear: both;
|
||||
}
|
||||
.fr {
|
||||
float: right !important;
|
||||
}
|
||||
.fl {
|
||||
float: left !important;
|
||||
}
|
||||
.fi {
|
||||
float: initial !important;
|
||||
}
|
||||
.m-auto {
|
||||
margin: auto !important;
|
||||
}
|
||||
.mt-auto {
|
||||
margin-top: auto !important;
|
||||
}
|
||||
.mr-auto {
|
||||
margin-right: auto !important;
|
||||
}
|
||||
.mb-auto {
|
||||
margin-bottom: auto !important;
|
||||
}
|
||||
.ml-auto {
|
||||
margin-left: auto !important;
|
||||
}
|
||||
.text-right {
|
||||
text-align: right !important;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center !important;
|
||||
}
|
||||
.text-left {
|
||||
text-align: left !important;
|
||||
}
|
||||
.w-percent-100 {
|
||||
width: 100% !important;
|
||||
}
|
||||
.base-line-height {
|
||||
line-height: $base--line-height !important;
|
||||
}
|
||||
|
||||
/* Reset element-ui
|
||||
------------------------------ */
|
||||
.aui-wrapper {
|
||||
.el-card + .el-card {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.el-input__prefix .el-input__icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.el-date-editor .el-range-separator {
|
||||
width: 8%;
|
||||
}
|
||||
.el-table th {
|
||||
color: $--color-text-primary;
|
||||
background-color: $--background-color-base;
|
||||
}
|
||||
.el-pagination {
|
||||
margin-top: 15px;
|
||||
text-align: right;
|
||||
}
|
||||
.el-table__expand-icon {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
vertical-align: middle;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Common
|
||||
------------------------------ */
|
||||
// 图标
|
||||
.icon-svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
fill: currentColor;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
}
|
||||
// 卡片
|
||||
.aui-card--fill .el-card__header {
|
||||
height: $content--card-header-height;
|
||||
line-height: $content--card-header-height - 36px;
|
||||
}
|
||||
.aui-card__title {
|
||||
font-size: 16px;
|
||||
}
|
||||
// 表单
|
||||
.aui-form__label-icon {
|
||||
display: inline-block;
|
||||
margin: 0 3px;
|
||||
vertical-align: middle;
|
||||
font-size: 18px;
|
||||
color: $--color-text-secondary;
|
||||
}
|
||||
// 按钮
|
||||
.aui-button--dashed {
|
||||
border-style: dashed;
|
||||
&:focus,
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
&-add {
|
||||
> span > *[class*="el-icon-"],
|
||||
> span > *[class*="icon"] {
|
||||
vertical-align: middle;
|
||||
font-size: 18px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 主题工具
|
||||
.aui-theme-tools {
|
||||
position: fixed;
|
||||
top: $navbar--height + $content--tabs-header-height + 15px;
|
||||
right: -210px;
|
||||
bottom: 0;
|
||||
z-index: 1010;
|
||||
width: 210px;
|
||||
transition: right 0.3s;
|
||||
&--open {
|
||||
right: 0;
|
||||
}
|
||||
&__toggle {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
left: -40px;
|
||||
width: 40px;
|
||||
padding: 10px 8px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
border-right: 0;
|
||||
border-radius: $--border-radius-base 0 0 $--border-radius-base;
|
||||
color: #fff;
|
||||
background-color: $--color-primary;
|
||||
cursor: pointer;
|
||||
}
|
||||
&__content {
|
||||
height: 100%;
|
||||
padding: 5px 20px 20px;
|
||||
border: 1px solid $--border-color-lighter;
|
||||
border-radius: $--border-radius-base 0 0 $--border-radius-base;
|
||||
background-color: #fff;
|
||||
.el-radio {
|
||||
display: block;
|
||||
margin-left: 0 !important;
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
&__item + &__item {
|
||||
margin-top: 15px;
|
||||
border-top: 1px solid $--border-color-lighter;
|
||||
}
|
||||
}
|
||||
|
||||
/* Layout
|
||||
------------------------------ */
|
||||
.aui-wrapper {
|
||||
position: relative;
|
||||
padding-top: $navbar--height;
|
||||
}
|
||||
|
||||
/* Sidebar fold
|
||||
------------------------------ */
|
||||
.aui-sidebar--fold {
|
||||
.aui-navbar {
|
||||
&__header,
|
||||
&__brand {
|
||||
width: $sidebar--width-fold;
|
||||
}
|
||||
&__brand {
|
||||
&-lg {
|
||||
display: none;
|
||||
}
|
||||
&-mini {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
&__icon-menu--switch {
|
||||
transform: rotateZ(180deg);
|
||||
}
|
||||
}
|
||||
.aui-sidebar {
|
||||
&__inner {
|
||||
width: $sidebar--width-fold + 20px;
|
||||
}
|
||||
&,
|
||||
&__menu {
|
||||
width: $sidebar--width-fold;
|
||||
}
|
||||
&__menu > li > .el-submenu__title {
|
||||
text-align: center;
|
||||
}
|
||||
&__menu-icon {
|
||||
margin-right: 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
.aui-content {
|
||||
&__wrapper {
|
||||
margin-left: $sidebar--width-fold;
|
||||
}
|
||||
&--tabs > .el-tabs > .el-tabs__header {
|
||||
left: $sidebar--width-fold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Navbar
|
||||
------------------------------ */
|
||||
.aui-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 1030;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
height: $navbar--height;
|
||||
background-color: $--color-primary;
|
||||
box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.05);
|
||||
&--colorful {
|
||||
.aui-navbar__body {
|
||||
}
|
||||
.aui-navbar__menu {
|
||||
> .el-menu-item,
|
||||
> .el-submenu > .el-submenu__title {
|
||||
color: #fff;
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: #fff;
|
||||
background-color: mix(#fff, $--color-primary, 15%);
|
||||
}
|
||||
}
|
||||
> .el-menu-item.is-active,
|
||||
> .el-submenu.is-active > .el-submenu__title {
|
||||
color: #fff;
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.el-menu-item i,
|
||||
.el-submenu__title i,
|
||||
.el-menu-item svg,
|
||||
.el-submenu__title svg,
|
||||
.el-menu-item .el-dropdown {
|
||||
color: grey;
|
||||
}
|
||||
.el-button {
|
||||
color: #fff;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
.aui-navbar__search {
|
||||
&-txt {
|
||||
.el-input__inner {
|
||||
color: #fff;
|
||||
border-color: #fff;
|
||||
&::-webkit-input-placeholder {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&__header {
|
||||
position: relative;
|
||||
width: $sidebar--width;
|
||||
height: $navbar--height;
|
||||
transition: width 0.3s;
|
||||
}
|
||||
&__brand {
|
||||
background-color: rgba(0, 21, 41, 1);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 20px;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
color: #fff;
|
||||
overflow: hidden;
|
||||
transition: width 0.3s;
|
||||
&-lg,
|
||||
&-mini {
|
||||
max-width: 100%;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
&-mini {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&__body {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
background-color: #fff;
|
||||
overflow: hidden;
|
||||
}
|
||||
&__menu {
|
||||
background-color: transparent;
|
||||
border-bottom: 0 !important;
|
||||
a:focus,
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.el-menu-item,
|
||||
.el-submenu > .el-submenu__title {
|
||||
height: $navbar--height;
|
||||
padding: 0 15px;
|
||||
line-height: $navbar--height;
|
||||
border-color: transparent !important;
|
||||
}
|
||||
.el-menu-item.is-active,
|
||||
.el-submenu.is-active > .el-submenu__title {
|
||||
color: $--color-text-secondary;
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
}
|
||||
.el-menu-item {
|
||||
&:focus,
|
||||
&:hover {
|
||||
.aui-navbar__icon-menu {
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
.el-dropdown {
|
||||
color: $--color-text-primary;
|
||||
.el-icon-arrow-down {
|
||||
transform: rotateZ(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
* {
|
||||
vertical-align: initial;
|
||||
}
|
||||
.aui-navbar__icon-menu {
|
||||
vertical-align: middle;
|
||||
font-size: 16px;
|
||||
}
|
||||
.el-dropdown {
|
||||
color: $--color-text-secondary;
|
||||
.el-icon-arrow-down {
|
||||
width: auto;
|
||||
font-size: 12px;
|
||||
margin: 0 0 0 5px;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-badge {
|
||||
display: inline;
|
||||
z-index: 2;
|
||||
&__content {
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__search {
|
||||
> *[class*="el-icon-"],
|
||||
> *[class*="icon"] {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
&-txt {
|
||||
width: 0;
|
||||
transition: width 0.3s, margin-left 0.3s;
|
||||
&.is-show {
|
||||
width: 210px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.el-input__inner {
|
||||
height: $navbar--height - 20px;
|
||||
padding: 0;
|
||||
line-height: $navbar--height - 20px;
|
||||
border-color: $--color-text-primary;
|
||||
border-top: 0;
|
||||
border-right: 0;
|
||||
border-left: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__avatar {
|
||||
.el-dropdown-link {
|
||||
> img {
|
||||
width: 36px;
|
||||
height: auto;
|
||||
margin-right: 5px;
|
||||
border-radius: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sidebar
|
||||
------------------------------ */
|
||||
.aui-sidebar {
|
||||
position: fixed;
|
||||
top: $navbar--height;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 1020;
|
||||
width: $sidebar--width;
|
||||
background-color: #fff;
|
||||
box-shadow: 1px 0 2px 0 rgba(0, 0, 0, 0.05);
|
||||
overflow: hidden;
|
||||
transition: width 0.3s;
|
||||
&--dark {
|
||||
background-color: $sidebar--background-color-dark;
|
||||
.aui-sidebar__menu,
|
||||
> .el-menu--popup {
|
||||
background-color: $sidebar--background-color-dark;
|
||||
.el-menu-item,
|
||||
.el-submenu > .el-submenu__title {
|
||||
color: $sidebar--text-color-dark;
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: #fff;
|
||||
background-color: rgba(11, 88, 255, 1);
|
||||
.aui-sidebar__menu-icon-son{
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-menu,
|
||||
.el-submenu.is-opened {
|
||||
background-color: rgba(11, 37, 63, 1);
|
||||
}
|
||||
.el-menu-item.is-active,
|
||||
.el-submenu.is-active > .el-submenu__title {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__inner {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: $sidebar--width + 20px;
|
||||
height: 100%;
|
||||
padding-bottom: 15px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
transition: width 0.3s;
|
||||
}
|
||||
&__menu {
|
||||
width: $sidebar--width;
|
||||
border-right: 0;
|
||||
transition: width 0.3s;
|
||||
.el-menu-item,
|
||||
.el-submenu__title {
|
||||
height: $sidebar--menu-item-height;
|
||||
line-height: $sidebar--menu-item-height;
|
||||
}
|
||||
}
|
||||
&__menu-icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 24px !important;
|
||||
margin-right: 5px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
color: inherit !important;
|
||||
transition: font-size 0.3s;
|
||||
}
|
||||
&__menu-icon-son {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 8px !important;
|
||||
margin-right: 15px;
|
||||
text-align: center;
|
||||
font-size: 8px;
|
||||
color: inherit !important;
|
||||
transition: font-size 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
/* Content
|
||||
------------------------------ */
|
||||
.aui-content {
|
||||
position: relative;
|
||||
padding: $content--padding;
|
||||
min-height: calc(100vh - #{$navbar--height});
|
||||
&__wrapper {
|
||||
position: relative;
|
||||
margin-left: $sidebar--width;
|
||||
min-height: calc(100vh - #{$navbar--height});
|
||||
background-color: $content--background-color;
|
||||
transition: margin-left 0.3s;
|
||||
}
|
||||
> .aui-card--fill > .el-card__body {
|
||||
min-height: calc(#{$content--fill-height} - 2px);
|
||||
}
|
||||
> .aui-card--fill > .el-card__header + .el-card__body {
|
||||
min-height: calc(#{$content--fill-height} - #{$content--card-header-height} - 2px);
|
||||
}
|
||||
&--tabs {
|
||||
padding: $content--tabs-header-height 0 0;
|
||||
}
|
||||
&--tabs-tools {
|
||||
position: fixed;
|
||||
top: $navbar--height;
|
||||
right: 0;
|
||||
z-index: 931;
|
||||
min-width: $content--tabs-header-height;
|
||||
height: $content--tabs-header-height;
|
||||
padding: 0 12px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
line-height: $content--tabs-header-height;
|
||||
background-color: $--background-color-base;
|
||||
cursor: pointer;
|
||||
}
|
||||
&--tabs-icon-nav {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
font-size: 16px;
|
||||
}
|
||||
> .el-tabs {
|
||||
> .el-tabs__header {
|
||||
position: fixed;
|
||||
top: $navbar--height;
|
||||
left: $sidebar--width;
|
||||
right: 0;
|
||||
z-index: 930;
|
||||
padding: 0 55px 0 15px;
|
||||
margin: 0;
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
background-color: #fff;
|
||||
transition: left 0.3s;
|
||||
> .el-tabs__nav-wrap {
|
||||
margin-bottom: 0;
|
||||
&:after {
|
||||
display: none;
|
||||
}
|
||||
> .el-tabs__nav-next,
|
||||
> .el-tabs__nav-prev {
|
||||
line-height: $content--tabs-header-height;
|
||||
}
|
||||
> .el-tabs__nav-scroll > .el-tabs__nav {
|
||||
& > .el-tabs__active-bar {
|
||||
display: none;
|
||||
}
|
||||
& > .el-tabs__item {
|
||||
height: $content--tabs-header-height;
|
||||
border: 0;
|
||||
padding: 0 10px;
|
||||
color: rgba(89, 89, 89, 1);
|
||||
&:focus,
|
||||
&:hover,
|
||||
&.is-active {
|
||||
&:after {
|
||||
display: block;
|
||||
}
|
||||
> .el-icon-close {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
}
|
||||
&:after {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: $--color-primary;
|
||||
}
|
||||
+ .el-tabs__item {
|
||||
margin-left: 5px;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
border-radius: 5px;
|
||||
background-color: rgba(239, 239, 239, 1);
|
||||
&.is-active {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
background-color: rgba(62, 142, 247, 1);
|
||||
}
|
||||
}
|
||||
> .el-icon-close {
|
||||
width: 14px;
|
||||
margin-left: 6px;
|
||||
color: $--color-text-secondary;
|
||||
}
|
||||
> i.icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
> .el-tabs__content {
|
||||
padding: $content--padding;
|
||||
.el-loading-mask {
|
||||
z-index: 830;
|
||||
}
|
||||
> .el-tab-pane {
|
||||
min-height: calc(#{$content--fill-height-tabs});
|
||||
> .aui-card--fill > .el-card__body {
|
||||
min-height: calc(#{$content--fill-height-tabs} - 2px);
|
||||
}
|
||||
> .aui-card--fill > .el-card__header + .el-card__body {
|
||||
min-height: calc(#{$content--fill-height-tabs} - #{$content--card-header-height} - 2px);
|
||||
}
|
||||
&.is-iframe {
|
||||
height: calc(#{$content--fill-height-tabs} + #{$content--padding * 2});
|
||||
margin: -$content--padding;
|
||||
min-height: auto;
|
||||
> .aui-card--fill {
|
||||
background-color: transparent;
|
||||
}
|
||||
> .aui-card--fill > .el-card__header {
|
||||
background-color: #fff;
|
||||
}
|
||||
> .aui-card--fill > .el-card__body {
|
||||
height: calc(#{$content--fill-height-tabs} - 2px);
|
||||
margin: $content--padding;
|
||||
min-height: auto;
|
||||
border: $--border-base;
|
||||
border-color: $--border-color-lighter;
|
||||
border-radius: $--border-radius-base;
|
||||
background-color: #fff;
|
||||
}
|
||||
> .aui-card--fill > .el-card__header + .el-card__body {
|
||||
height: calc(#{$content--fill-height-tabs} - #{$content--card-header-height} - 2px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// quill富文本编辑器
|
||||
.ql-toolbar {
|
||||
line-height: 20px;
|
||||
&.ql-snow {
|
||||
border-color: $--border-color-base;
|
||||
}
|
||||
.ql-formats {
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
.ql-container {
|
||||
height: 150px;
|
||||
&.ql-snow {
|
||||
border-color: $--border-color-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Page
|
||||
------------------------------ */
|
||||
*[class*="aui-page__"] {
|
||||
padding-top: 0;
|
||||
.aui-content {
|
||||
min-height: auto;
|
||||
&__wrapper {
|
||||
min-height: 100vh;
|
||||
margin-left: 0;
|
||||
}
|
||||
> .aui-card--fill > .el-card__body {
|
||||
min-height: calc(100vh - #{$content--padding * 2} - 2px);
|
||||
}
|
||||
> .aui-card--fill > .el-card__header + .el-card__body {
|
||||
min-height: calc(100vh - #{$content--padding * 2} - #{$content--card-header-height} - 2px);
|
||||
}
|
||||
}
|
||||
}
|
15
src/assets/scss/modules/home.scss
Normal file
@ -0,0 +1,15 @@
|
||||
.mod-home {
|
||||
table {
|
||||
width: 100%;
|
||||
border: 1px solid $--border-color-lighter;
|
||||
border-collapse: collapse;
|
||||
th,
|
||||
td {
|
||||
padding: 12px 10px;
|
||||
border: 1px solid $--border-color-lighter;
|
||||
}
|
||||
th {
|
||||
width: 30%;
|
||||
}
|
||||
}
|
||||
}
|
447
src/assets/scss/normalize.scss
vendored
Normal file
@ -0,0 +1,447 @@
|
||||
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in
|
||||
* IE on Windows Phone and in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15; /* 1 */
|
||||
-ms-text-size-adjust: 100%; /* 2 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers (opinionated).
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
article,
|
||||
aside,
|
||||
footer,
|
||||
header,
|
||||
nav,
|
||||
section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
* 1. Add the correct display in IE.
|
||||
*/
|
||||
|
||||
figcaption,
|
||||
figure,
|
||||
main { /* 1 */
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct margin in IE 8.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Remove the gray background on active links in IE 10.
|
||||
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent; /* 1 */
|
||||
-webkit-text-decoration-skip: objects; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57- and Firefox 39-.
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font style in Android 4.3-.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct background and color in IE 9-.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background-color: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
audio,
|
||||
video {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in iOS 4-7.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10-.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the overflow in IE.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers (opinionated).
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: sans-serif; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
||||
* controls in Android 4.
|
||||
* 2. Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
html [type="button"], /* 1 */
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct display in IE 9-.
|
||||
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
display: inline-block; /* 1 */
|
||||
vertical-align: baseline; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10-.
|
||||
* 2. Remove the padding in IE 10-.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-cancel-button,
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in IE 9-.
|
||||
* 1. Add the correct display in Edge, IE, and Firefox.
|
||||
*/
|
||||
|
||||
details, /* 1 */
|
||||
menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Scripting
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
canvas {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hidden
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10-.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
48
src/assets/scss/pages/404.scss
Normal file
@ -0,0 +1,48 @@
|
||||
.aui-page__not-found {
|
||||
.aui-content {
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
padding: 15% 50px 50px;
|
||||
text-align: center;
|
||||
&__wrapper {
|
||||
height: 100vh;
|
||||
background-color: transparent;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
margin: 0 0 15px;
|
||||
font-size: 10em;
|
||||
font-weight: 400;
|
||||
color: $--color-text-regular;
|
||||
}
|
||||
.desc {
|
||||
margin: 0 0 20px;
|
||||
font-size: 26px;
|
||||
color: $--color-text-secondary;
|
||||
> em {
|
||||
margin: 0 5px;
|
||||
font-style: normal;
|
||||
color: $--color-warning;
|
||||
}
|
||||
}
|
||||
.btn-bar .el-button {
|
||||
margin: 0 15px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.aui-page__not-found {
|
||||
.title {
|
||||
font-size: 8em;
|
||||
}
|
||||
.desc {
|
||||
font-size: 20px;
|
||||
}
|
||||
.btn-bar .el-button {
|
||||
margin: 0 7.5px;
|
||||
}
|
||||
}
|
||||
}
|
220
src/assets/scss/pages/login.scss
Normal file
@ -0,0 +1,220 @@
|
||||
.aui-page__login {
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
content: "";
|
||||
}
|
||||
&::before {
|
||||
background-image: url(~@/assets/img/login_bg.jpg);
|
||||
background-size: cover;
|
||||
}
|
||||
&::after {
|
||||
background-color: rgba(38, 50, 56, .4);
|
||||
}
|
||||
.aui-content {
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
padding: 50px 20px 150px;
|
||||
text-align: center;
|
||||
&__wrapper {
|
||||
height: 100vh;
|
||||
background-color: transparent;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
.login-header {
|
||||
padding: 20px;
|
||||
color: #fff;
|
||||
.login-brand {
|
||||
margin: 0 0 15px;
|
||||
font-size: 40px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.login-intro {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
> li {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
color: rgba(255, 255, 255, .6);
|
||||
& + li {
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.login-body,
|
||||
.login-footer {
|
||||
width: 460px;
|
||||
}
|
||||
.login-body {
|
||||
padding: 20px 30px;
|
||||
background-color: #fff;
|
||||
.login-title {
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.el-input__prefix .el-input__icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
.login-captcha {
|
||||
height: $--input-height;
|
||||
line-height: $--input-height -2px;
|
||||
> img {
|
||||
max-width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.login-shortcut {
|
||||
margin-bottom: 20px;
|
||||
&__title {
|
||||
position: relative;
|
||||
margin: 0 0 15px;
|
||||
font-weight: 400;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
content: "";
|
||||
height: 1px;
|
||||
margin-top: -.5px;
|
||||
background-color: $--border-color-base;
|
||||
overflow: hidden;
|
||||
}
|
||||
> span {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 0 20px;
|
||||
color: rgba(0, 0, 0, .3);
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
&__list {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
font-size: 0;
|
||||
> li {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin: 0 10px;
|
||||
font-size: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.login-guide {
|
||||
color: rgba(0, 0, 0, .3);
|
||||
}
|
||||
}
|
||||
.login-footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
padding: 20px;
|
||||
color: rgba(255, 255, 255, .6);
|
||||
p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
a {
|
||||
padding: 0 5px;
|
||||
color: rgba(255, 255, 255, .6);
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 右侧垂直风格
|
||||
&--right-vertical {
|
||||
.aui-content {
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
padding: 0;
|
||||
}
|
||||
.login-header {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
justify-content: center;
|
||||
padding: 30px 120px;
|
||||
text-align: left;
|
||||
}
|
||||
.login-body {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
justify-content: center;
|
||||
padding: 120px 30px 150px;
|
||||
text-align: center;
|
||||
.login-guide {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
.login-footer {
|
||||
right: 0;
|
||||
color: $--color-text-regular;
|
||||
a {
|
||||
color: $--color-text-regular;
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: $--color-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 991px) {
|
||||
.aui-page__login {
|
||||
&--right-vertical {
|
||||
.login-header {
|
||||
padding: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.aui-page__login {
|
||||
&--right-vertical {
|
||||
.login-header {
|
||||
.login-brand,
|
||||
.login-intro {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 575px) {
|
||||
.aui-page__login {
|
||||
.login-body,
|
||||
.login-footer {
|
||||
width: 100%;
|
||||
}
|
||||
.login-captcha {
|
||||
text-align: left;
|
||||
> img {
|
||||
width: 136px;
|
||||
}
|
||||
}
|
||||
&--right-vertical {
|
||||
.login-header {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
22
src/assets/scss/variables.scss
Normal file
@ -0,0 +1,22 @@
|
||||
// Base
|
||||
$base--line-height: 1.15;
|
||||
|
||||
// Navbar
|
||||
$navbar--height: 50px;
|
||||
|
||||
// Sidebar
|
||||
$sidebar--width: 248px;
|
||||
$sidebar--width-fold: 64px;
|
||||
$sidebar--background-color-dark: rgba(0, 21, 41, 1);
|
||||
$sidebar--text-color-dark: rgba(255, 255, 255, 1);
|
||||
$sidebar--menu-item-height: 48px;
|
||||
|
||||
// Content
|
||||
$content--padding: 15px;
|
||||
$content--background-color: #f1f4f5;
|
||||
$content--card-header-height: 60px;
|
||||
$content--tabs-header-height: 38px;
|
||||
// Content, 填充整屏高度(非tabs状态) = 整屏高度 - 导航条高度 - aui-content上下内边距高度
|
||||
$content--fill-height: calc(100vh - #{$navbar--height} - #{$content--padding * 2});
|
||||
// Content, 填充整屏高度(是tabs状态) = 整屏高度 - 导航条高度 - tabs组件header高度 - tabs组件content上下内边距高度
|
||||
$content--fill-height-tabs: calc(100vh - #{$navbar--height} - #{$content--tabs-header-height} - #{$content--padding * 2});
|
0
src/components/.gitkeep
Normal file
301
src/components/AttrForm.vue
Normal file
@ -0,0 +1,301 @@
|
||||
<template>
|
||||
<div class="attr-form">
|
||||
<h3>
|
||||
{{ title }} <el-button style="margin-left: 8px;" type="text" v-if="!isDetail && !showAddAttr" @click="showAddAttr = true">{{ $t('add') }}</el-button>
|
||||
</h3>
|
||||
<div v-if="!showAddAttr">
|
||||
<component
|
||||
key="sub-table"
|
||||
:is="require('./BaseTable.vue').default"
|
||||
:table-head-configs="filterTableConfigs()"
|
||||
:data="dataList"
|
||||
:page="pageIndex"
|
||||
:size="pageSize"
|
||||
:max-height="calcMaxHeight(8)"
|
||||
@operate-event="handleOperations"
|
||||
/>
|
||||
<el-pagination
|
||||
@size-change="sizeChangeHandle"
|
||||
@current-change="currentChangeHandle"
|
||||
:current-page="pageIndex"
|
||||
:page-sizes="[5, 10, 20, 50]"
|
||||
:page-size="pageSize"
|
||||
:total="totalPage"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
<div v-else style="background: #eee; border-radius: 8px; padding: 12px;">
|
||||
<el-row>
|
||||
<el-col>
|
||||
<el-form ref="AttrForm" :model="AttrForm" :rules="AttrFormRules" :inline="true" label-position="top">
|
||||
<el-row :gutter="20" style="padding: 0 24px;">
|
||||
<el-col :span="attrFormFields.length > 6 ? 6 : 12" v-for="field in attrFormFields" :key="field.prop + 'col'">
|
||||
<el-form-item :key="field.prop" :prop="field.prop" :label="field.name" style="width: 100%">
|
||||
<el-input v-if="field.formType === 'input' || !field.formType" v-model="AttrForm[field.prop]" :placeholder="$t('hints.input')" clearable />
|
||||
<el-select v-if="field.formType === 'select'" v-model="AttrForm[field.prop]" clearable>
|
||||
<el-option v-for="opt in field.formOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||||
</el-select>
|
||||
<!-- add more... -->
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row style="text-align: right;">
|
||||
<el-button size="small" @click="handleCloseAttrForm">{{ $t('cancel') }}</el-button>
|
||||
<el-button type="success" size="small" @click="handleSaveAttrForm">{{ $t('save') }}</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import i18n from '@/i18n'
|
||||
import BaseTable from '@/components/base-table'
|
||||
import { pick } from 'lodash/object'
|
||||
|
||||
|
||||
/** 计算表格的最大高 */
|
||||
function calcMaxHeight(num) {
|
||||
const FIXED_HEIGHT = 220
|
||||
let clientHeight = 0
|
||||
const bodyHeight = document.body.clientHeight || null
|
||||
const documentHeight = document.documentElement.clientHeight || null
|
||||
if (bodyHeight && documentHeight) {
|
||||
clientHeight = Math.max(bodyHeight, documentHeight)
|
||||
} else {
|
||||
clientHeight = documentHeight ? documentHeight : bodyHeight
|
||||
}
|
||||
|
||||
const finalHeight = clientHeight - num - FIXED_HEIGHT
|
||||
return finalHeight > 0 ? finalHeight : -finalHeight
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
name: 'AttrForm',
|
||||
components: { BaseTable },
|
||||
props: {
|
||||
isDetail: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/** subtable 需要设置的属性 */
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
tableConfigs: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
/** 表单提交需要的属性 */
|
||||
relatedId: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: null
|
||||
},
|
||||
relatedField: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
calcMaxHeight,
|
||||
showAddAttr: false,
|
||||
dataList: [],
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
totalPage: 0,
|
||||
AttrForm: {}, // 需动态设置
|
||||
AttrFormRules: {} // 需动态设置
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
attrFormFields() {
|
||||
const _ = this.tableConfigs.filter(item => item.formField)
|
||||
/** 顺带配置 AttrForm */
|
||||
_.forEach(item => {
|
||||
this.$set(this.AttrForm, [item.prop], '')
|
||||
})
|
||||
|
||||
this.$set(this.AttrForm, 'id', null)
|
||||
return _
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getDataList()
|
||||
/** 设置 AttrForm 的 rules */
|
||||
for (const config of this.tableConfigs) {
|
||||
if (config.rules) {
|
||||
this.$set(this.AttrFormRules, [config.prop], config.rules)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
filterTableConfigs() {
|
||||
if (this.isDetail) {
|
||||
/** 如果是查看详情,就屏蔽操作列 */
|
||||
return this.tableConfigs.filter(opt => opt.prop !== 'operations')
|
||||
}
|
||||
return this.tableConfigs
|
||||
},
|
||||
/** init dataform */
|
||||
initAttrForm() {
|
||||
Object.entries(this.AttrForm).forEach(([key, value]) => {
|
||||
if (typeof value === 'object' || typeof value === 'number') {
|
||||
this.AttrForm[key] = null
|
||||
} else if (Array.isArray(value)) {
|
||||
this.AttrForm[key] = []
|
||||
} else {
|
||||
this.AttrForm[key] = ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** requests */
|
||||
getDataList() {
|
||||
this.dataListLoading = true
|
||||
// 获取动态属性列表
|
||||
this.$http({
|
||||
url: this.$http.adornUrl(`${this.url}/page`),
|
||||
method: 'get',
|
||||
params: this.$http.adornParams({
|
||||
page: this.pageIndex,
|
||||
limit: this.pageSize,
|
||||
[this.relatedField]: this.relatedId
|
||||
// order: 'asc/desc',
|
||||
// orderField: 'name'
|
||||
})
|
||||
}).then(({ data: res }) => {
|
||||
if (res && res.code === 0) {
|
||||
this.dataList = res.data.list
|
||||
this.totalPage = res.data.total
|
||||
} else {
|
||||
this.dataList = []
|
||||
this.totalPage = 0
|
||||
}
|
||||
this.dataListLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** handlers */
|
||||
handleOperations({ type, data: id }) {
|
||||
switch (type) {
|
||||
case 'edit':
|
||||
{
|
||||
this.showAddAttr = true
|
||||
this.$nextTick(() => {
|
||||
this.$http.get(this.$http.adornUrl(`${this.url}/${id}`)).then(({ data: res }) => {
|
||||
if (res && res.code === 0 && res.data) {
|
||||
const neededFields = [...this.attrFormFields.map(item => item.prop), 'id']
|
||||
const filtered = pick(res.data, neededFields)
|
||||
for (let field of neededFields) {
|
||||
this.AttrForm[field] = filtered[field]
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'delete':
|
||||
return this.deleteHandle(id)
|
||||
}
|
||||
},
|
||||
deleteHandle(id) {
|
||||
var ids = id ? [id] : []
|
||||
|
||||
this.$confirm(`${i18n.t('prompt.info', { handle: id ? i18n.t('delete').toLowerCase() : i18n.t('deleteBatch').toLowerCase() })}`, i18n.t('prompt.title'), {
|
||||
confirmButtonText: i18n.t('confirm'),
|
||||
cancelButtonText: i18n.t('cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$http({
|
||||
url: this.$http.adornUrl(this.url),
|
||||
method: 'delete',
|
||||
data: this.$http.adornData(ids, false, 'raw')
|
||||
}).then(({ data }) => {
|
||||
if (data && data.code === 0) {
|
||||
this.$message({
|
||||
message: i18n.t('prompt.success'),
|
||||
type: 'success',
|
||||
duration: 1500,
|
||||
onClose: () => {
|
||||
this.getDataList()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$message.error(data.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
handleCloseAttrForm() {
|
||||
this.showAddAttr = false
|
||||
this.initAttrForm()
|
||||
},
|
||||
|
||||
handleSaveAttrForm() {
|
||||
this.$refs['AttrForm'].validate(valid => {
|
||||
if (valid) {
|
||||
this.$http({
|
||||
// url: this.$http.adornUrl(`${this.url}/${!this.AttrForm.id ? '' : this.AttrForm.id}`),
|
||||
url: this.$http.adornUrl(this.url),
|
||||
method: this.AttrForm.id ? 'put' : 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: JSON.stringify({ ...this.AttrForm, [this.relatedField]: this.relatedId })
|
||||
}).then(({ data }) => {
|
||||
if (data && data.code === 0) {
|
||||
this.$message({
|
||||
message: i18n.t('prompt.success'),
|
||||
type: 'success',
|
||||
duration: 1500,
|
||||
onClose: () => {
|
||||
this.showAddAttr = false
|
||||
this.getDataList()
|
||||
this.initAttrForm()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$message.error(data.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 每页数
|
||||
sizeChangeHandle(val) {
|
||||
this.pageSize = val
|
||||
this.pageIndex = 1
|
||||
this.getDataList()
|
||||
},
|
||||
// 当前页
|
||||
currentChangeHandle(val) {
|
||||
this.pageIndex = val
|
||||
this.getDataList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.attr-form >>> .el-form .el-form-item__label {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
30
src/components/BaseAgreeOrNot.vue
Normal file
@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<!-- 同意或不同意组件,点击不同意的时候出现额外的输入框说明原因 -->
|
||||
<el-row>
|
||||
<el-button type="primary" @click="doAgree">同意</el-button>
|
||||
<el-button @click="dontAgree">不同意</el-button>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: '',
|
||||
props: {},
|
||||
emits: ['not-agree', 'agree'],
|
||||
data() {
|
||||
return {
|
||||
reason: 'dont agree reason',
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
doAgree() {
|
||||
this.$emit('agree');
|
||||
},
|
||||
dontAgree() {
|
||||
this.$emit('not-agree', this.reason);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
649
src/components/BaseDialog.vue
Normal file
@ -0,0 +1,649 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
class="super-flexible-dialog"
|
||||
:title="isDetail ? title.detail : !dataForm.id ? title.add : title.edit"
|
||||
:visible.sync="visible"
|
||||
@close="handleClose"
|
||||
:distory-on-close="true"
|
||||
:close-on-click-modal="false">
|
||||
<div style="max-height: 60vh; overflow-y: scroll; overflow-x: hidden">
|
||||
<el-form ref="dataForm" :model="dataForm" :rules="dataFormRules">
|
||||
<!-- 如果需要更精细一点的布局,可以根据配置项实现地再复杂一点,但此处暂时全部采用一行两列布局 -->
|
||||
<el-row v-for="n in rows" :key="n" :gutter="20">
|
||||
<el-col v-for="c in COLUMN_PER_ROW" :key="`${n}+'col'+${c}`" :span="getSpan(n, c)">
|
||||
<!-- <el-col v-for="c in COLUMN_PER_ROW" :key="`${n}+'col'+${c}`" :span="24 / COLUMN_PER_ROW"> -->
|
||||
<!-- :class="{ 'hidden-input': configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].hidden }" -->
|
||||
<el-form-item
|
||||
v-if="configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)]"
|
||||
:prop="configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name"
|
||||
:key="`${n}-col-${c}-item`"
|
||||
:label="getLabel(n, c)">
|
||||
<!-- 暂时先不实现部分输入方式 -->
|
||||
<el-input
|
||||
v-if="getType(n, c) === 'input'"
|
||||
:placeholder="getPlaceholder(n, c)"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
clearable
|
||||
:disabled="isDetail" />
|
||||
<el-radio
|
||||
v-if="getType(n, c) === 'radio'"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
:disabled="isDetail" />
|
||||
<el-checkbox
|
||||
v-if="getType(n, c) === 'check'"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
:disabled="isDetail" />
|
||||
<el-select
|
||||
v-if="getType(n, c) === 'select'"
|
||||
:placeholder="getPlaceholder(n, c)"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
clearable
|
||||
:disabled="isDetail"
|
||||
@change="emitSelectChange(configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name, $event)">
|
||||
<el-option
|
||||
v-for="opt in configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].options"
|
||||
:key="opt.label + Math.random()"
|
||||
:label="opt.label"
|
||||
:value="opt.value" />
|
||||
</el-select>
|
||||
<el-switch
|
||||
v-if="getType(n, c) === 'switch'"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
:disabled="isDetail" />
|
||||
<el-cascader
|
||||
v-if="getType(n, c) === 'cascader'"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
:options="configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].options"
|
||||
:props="configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].props"
|
||||
:disabled="isDetail"
|
||||
clearable />
|
||||
<el-time-select
|
||||
v-if="getType(n, c) === 'time'"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
:disabled="isDetail" />
|
||||
<el-date-picker
|
||||
v-if="getType(n, c) === 'date'"
|
||||
v-bind="configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].props"
|
||||
:placeholder="getPlaceholder(n, c)"
|
||||
v-model="dataForm[configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)].name]"
|
||||
:disabled="isDetail" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- extra components , like Markdown or RichEdit -->
|
||||
<template v-if="configs.extraComponents && configs.extraComponents.length > 0">
|
||||
<el-form-item
|
||||
v-for="(ec, index) in configs.extraComponents"
|
||||
:key="ec.name + index"
|
||||
:label="ec.label"
|
||||
class="extra-components">
|
||||
<component
|
||||
style="margin-top: 40px"
|
||||
v-if="ec.hasModel"
|
||||
:is="ec.component"
|
||||
v-bind="ec.props"
|
||||
v-model="dataForm[ec.name]"
|
||||
@ready="handleEditorReady"
|
||||
:read-only="isDetail" />
|
||||
<!-- <component v-if="ec.hasModel" :is="ec.component" v-bind="ec.props" v-model="dataForm[ec.name]" /> -->
|
||||
<component
|
||||
v-else
|
||||
:is="ec.component"
|
||||
v-bind="ec.props"
|
||||
@uploader-update-filelist="handleUploadListUpdate($event, ec.props.extraParams.typeCode)"
|
||||
:uploader-inject-file-list="/*用于设备分流的*/ fileList[ec.props.extraParams.typeCode]"
|
||||
:read-only="isDetail" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
|
||||
<!-- <template v-if="dataForm.id && configs.subtable">
|
||||
<attr-form :related-id="dataForm.id" v-bind="configs.subtable" :is-detail="isDetail" />
|
||||
</template> -->
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<template v-for="(operate, index) in configs.operations">
|
||||
<!-- {{ operate.name | btnNameFilter }} -->
|
||||
<el-button
|
||||
v-if="
|
||||
!isDetail &&
|
||||
(operate.showAlways ||
|
||||
(((dataForm.id && operate.showOnEdit) || (!dataForm.id && !operate.showOnEdit)) &&
|
||||
(operate.permission ? $hasPermission(operate.permission) : true)))
|
||||
"
|
||||
:key="`operate-${index}`"
|
||||
:type="btnType[operate.name]"
|
||||
@click="handleClick(operate)"
|
||||
>{{ btnName[operate.name] }}</el-button
|
||||
>
|
||||
</template>
|
||||
<el-button v-if="isDetail" @click="handleClick({ name: 'cancel' })">{{ $t('cancel') }}</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import CKEditor from 'ckeditor4-vue'
|
||||
// import AttrForm from './AttrForm'
|
||||
// import { pick } from 'lodash/object'
|
||||
import { pick as __pick } from '@/utils/filters';
|
||||
// import i18n from '@/i18n'
|
||||
import $http from '@/utils/request';
|
||||
|
||||
const i18n = {
|
||||
t: (a) => a,
|
||||
};
|
||||
|
||||
// 标题 for i18n
|
||||
const title = {
|
||||
// detail: i18n.t('detail'),
|
||||
// add: i18n.t('add'),
|
||||
// edit: i18n.t('edit'),
|
||||
detail: '详情',
|
||||
add: '添加',
|
||||
edit: '编辑',
|
||||
};
|
||||
|
||||
// 或者也可以改造成自定义颜色:
|
||||
const btnType = {
|
||||
save: 'success',
|
||||
update: 'primary',
|
||||
reset: 'text',
|
||||
// cancel: 'text'
|
||||
// add more...
|
||||
};
|
||||
|
||||
const btnName = {
|
||||
save: '保存',
|
||||
update: '更新',
|
||||
reset: '重置',
|
||||
cancel: '取消',
|
||||
// for i18n
|
||||
// save: i18n.t('save'),
|
||||
// update: i18n.t('update'),
|
||||
// reset: i18n.t('reset'),
|
||||
// cancel: i18n.t('cancel'),
|
||||
// add more...
|
||||
};
|
||||
|
||||
// 每行的列数
|
||||
const COLUMN_PER_ROW = 2;
|
||||
|
||||
export default {
|
||||
name: 'AddOrUpdateDialog',
|
||||
// components: { AttrForm },
|
||||
props: {
|
||||
configs: {
|
||||
type: Object,
|
||||
default: () => ({}), // 此处省去类型检查,使用者自行注意就好
|
||||
},
|
||||
url: {
|
||||
type: Object,
|
||||
default: () => ({}), // 此处省去类型检查,使用者自行注意就好
|
||||
},
|
||||
},
|
||||
filters: {
|
||||
nameFilter: function (name) {
|
||||
if (!name) return null;
|
||||
// for i18n
|
||||
const defaultNames = {
|
||||
name: '名称',
|
||||
code: '编码',
|
||||
remark: '备注',
|
||||
description: '描述',
|
||||
specifications: '规格',
|
||||
// name: i18n.t('name'),
|
||||
// code: i18n.t('code'),
|
||||
// remark: i18n.t('remark'),
|
||||
// description: i18n.t('desc'),
|
||||
// specifications: i18n.t('prod.spec'),
|
||||
// add more...
|
||||
};
|
||||
|
||||
return defaultNames[name];
|
||||
},
|
||||
},
|
||||
// provide() {
|
||||
// return {
|
||||
// _df: this.dataForm
|
||||
// }
|
||||
// },
|
||||
data() {
|
||||
return {
|
||||
COLUMN_PER_ROW,
|
||||
title,
|
||||
/** 按钮相关属性 */
|
||||
btnName,
|
||||
btnType,
|
||||
defaultNames: {
|
||||
name: '名称',
|
||||
code: '编码',
|
||||
remark: '备注',
|
||||
description: '描述',
|
||||
specifications: '规格',
|
||||
// add more...
|
||||
// name: i18n.t('name'),
|
||||
// code: i18n.t('code'),
|
||||
// remark: i18n.t('remark'),
|
||||
// description: i18n.t('desc'),
|
||||
// specifications: i18n.t('prod.spec'),
|
||||
// add more...
|
||||
},
|
||||
defaultPlaceholders: {}, // 自动根据 defaultNames 计算得来
|
||||
/** 表单相关属性 */
|
||||
visible: false,
|
||||
isEdit: false,
|
||||
isDetail: false,
|
||||
dataForm: {},
|
||||
dataFormRules: {},
|
||||
tempForm: [], // 临时保存自动生成的code,或其他数据
|
||||
shouldWait: null,
|
||||
fileForm: {}, // 文件上传分流用、合并用的表单,根据 typeCode 进行分流,在请求时合并
|
||||
fileList: {}, // 文件加载时分流,依据 typeCode
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
rows() {
|
||||
// 本组件只实现了'一行两列'的表单布局
|
||||
return Math.ceil(this.configs.fields.length / COLUMN_PER_ROW);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
/** load lang */
|
||||
// CKEditor.load()
|
||||
// console.log('lang', CKEditor.component.props.config.defaultLanguage = 'en' )
|
||||
},
|
||||
mounted() {
|
||||
/** 计算 defaultPlaceholders */
|
||||
// const prefix = i18n.t('hints.input');
|
||||
const prefix = '请输入';
|
||||
Object.entries(this.defaultNames).map(([key, value]) => {
|
||||
this.defaultPlaceholders[key] = prefix + value;
|
||||
});
|
||||
|
||||
/** 转换 configs.fields 的结构,把纯字符串转为对象 */
|
||||
this.$nextTick(() => {
|
||||
this.configs.fields = this.configs.fields.map((item) => {
|
||||
if (typeof item === 'string') {
|
||||
return { name: item };
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
/** 动态设置dataForm字段 */
|
||||
this.configs.fields.forEach((item) => {
|
||||
this.$set(this.dataForm, [item.name], '');
|
||||
|
||||
/** select 的默认值设置 */
|
||||
if (item.type === 'select') {
|
||||
const opts = item.options || [];
|
||||
const dft = opts.find((item) => item.default || false);
|
||||
if (dft) {
|
||||
this.$set(this.dataForm, [item.name], dft.value);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.api) {
|
||||
/** 自动请求并填充 */
|
||||
// or this.shouldWaitPool = []
|
||||
this.shouldWait =
|
||||
// this.$http({
|
||||
// url: this.$http.adornUrl(item.api),
|
||||
// method: 'POST', // 也可以改成动态决定
|
||||
// })
|
||||
$http
|
||||
.get(item.api)
|
||||
// .then(({ data: res }) => {
|
||||
.then((res) => {
|
||||
if (res && res.code === 0) {
|
||||
// this.dataForm[item.name] = res.data // <=== 此处需要对接口
|
||||
this.tempForm.push({ name: item.name, data: res.data });
|
||||
}
|
||||
});
|
||||
} // end if (item.api)
|
||||
|
||||
// 如果有 relatedField,就需要在当前item的数据加载后,刷新 relatedField 的列表
|
||||
if (item.relatedField) {
|
||||
this.$watch(
|
||||
function () {
|
||||
return this.dataForm[item.name];
|
||||
},
|
||||
function (val, old) {
|
||||
if (val && val !== old) {
|
||||
this.$emit('select-change', { name: item.name, id: val });
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
}
|
||||
|
||||
if (item.required) {
|
||||
const requiredRule = {
|
||||
required: true,
|
||||
message: '必填项',
|
||||
// message: i18n.t('validate.required'),
|
||||
// trigger: 'change'
|
||||
trigger: 'blur',
|
||||
};
|
||||
/** 检查是否已经存在该字段的规则 */
|
||||
const exists = this.dataFormRules[item.name] || null;
|
||||
/** 设置验证规则 */
|
||||
if (exists) {
|
||||
const unset = true;
|
||||
for (const rule of exists) {
|
||||
if (rule.required) unset = false;
|
||||
}
|
||||
if (unset) {
|
||||
exists.push(requiredRule);
|
||||
}
|
||||
} else {
|
||||
/** 不存在已有规则 */
|
||||
this.$set(this.dataFormRules, [item.name], [requiredRule]);
|
||||
}
|
||||
} // end if (item.required)
|
||||
|
||||
if (item.rules) {
|
||||
const exists = this.dataFormRules[item.name] || null;
|
||||
if (exists) {
|
||||
// 浅拷贝过去
|
||||
exists.push(...item.rules);
|
||||
} else {
|
||||
this.$set(this.dataFormRules, [item.name], [...item.rules]);
|
||||
}
|
||||
} // end if (item.rules)
|
||||
});
|
||||
|
||||
/** 计算默认值 */
|
||||
function calDefault(type) {
|
||||
switch (type) {
|
||||
case 'array':
|
||||
return [];
|
||||
// more case...
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
/** 检查是否需要额外的组件 */
|
||||
this.configs.extraComponents &&
|
||||
this.configs.extraComponents.forEach((item) => {
|
||||
// if (Object.hasOwn(this.dataForm, [item.name])) {
|
||||
if (this.dataForm.hasOwnProperty(item.name)) {
|
||||
return;
|
||||
} else {
|
||||
this.$set(this.dataForm, [item.name], calDefault(item.fieldType));
|
||||
}
|
||||
});
|
||||
|
||||
/** 单独设置 id */
|
||||
this.$set(this.dataForm, 'id', null);
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
getSpan(n, c) {
|
||||
const opt = this.configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)];
|
||||
return opt && opt.span ? opt.span : 24 / COLUMN_PER_ROW;
|
||||
},
|
||||
|
||||
getLabel(n, c) {
|
||||
const opt = this.configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)];
|
||||
if (opt) {
|
||||
// if opt is valid
|
||||
return opt.label ? opt.label : this.defaultNames[opt.name];
|
||||
}
|
||||
},
|
||||
|
||||
getPlaceholder(n, c) {
|
||||
if (this.isDetail) {
|
||||
/** 如果是详情,就不展示 提示文本 */
|
||||
return '';
|
||||
}
|
||||
|
||||
const opt = this.configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)];
|
||||
if (opt) {
|
||||
// if opt is valid
|
||||
return opt.placeholder
|
||||
? opt.placeholder
|
||||
: this.defaultPlaceholders[opt.name]
|
||||
? this.defaultPlaceholders[opt.name]
|
||||
: opt.label
|
||||
? (opt.type === 'select' ? '请选择' : '请输入') + opt.label
|
||||
: // ? (opt.type === 'select' ? i18n.t('choose') : i18n.t('hints.input')) + opt.label
|
||||
null;
|
||||
|
||||
// : opt.type === 'select'
|
||||
// ? i18n.t('choose')
|
||||
// : '请输入'
|
||||
}
|
||||
},
|
||||
|
||||
getType(n, c) {
|
||||
const opt = this.configs.fields[(n - 1) * COLUMN_PER_ROW + (c - 1)];
|
||||
if (opt) {
|
||||
if (!opt.type || ['input', 'number' /** add more.. */].includes(opt.type)) {
|
||||
return 'input';
|
||||
} else if (['select' /** add more.. */].includes(opt.type)) {
|
||||
return 'select';
|
||||
} else if (['cascader'].includes(opt.type)) {
|
||||
return 'cascader';
|
||||
} else if (['date'].includes(opt.type)) {
|
||||
return 'date';
|
||||
}
|
||||
// add more...
|
||||
} else {
|
||||
return 'input';
|
||||
}
|
||||
},
|
||||
|
||||
init(id, isdetail = false) {
|
||||
this.isDetail = isdetail;
|
||||
this.visible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].resetFields();
|
||||
this.dataForm.id = id || null;
|
||||
if (this.dataForm.id) {
|
||||
// this.$http({
|
||||
// url: this.$http.adornUrl(`${this.configs.infoUrl}/${this.dataForm.id}`),
|
||||
// method: 'get'
|
||||
// })
|
||||
$http.get(this.url.base + `/${this.dataForm.id}`).then((res) => {
|
||||
console.log('[base dialog init] ', res);
|
||||
if (res && res.code === 0) {
|
||||
const dataFormKeys = Object.keys(this.dataForm);
|
||||
console.log('keys ===> ', dataFormKeys);
|
||||
// console.log('data form keys: ', dataFormKeys, pick(res.data, dataFormKeys))
|
||||
this.dataForm = __pick(res.data, dataFormKeys);
|
||||
console.log('pick(res.data, dataFormKeys) ===> ', __pick(res.data, dataFormKeys));
|
||||
// LABEL: FILE_RELATED
|
||||
/** 对文件下载进行分流 */
|
||||
this.fileList = {};
|
||||
if (this.dataForm.files) {
|
||||
// console.log('files: ', this.dataForm.files)
|
||||
this.dataForm.files.forEach((file) => {
|
||||
// const fileName = file.fileUrl.split('/').pop()
|
||||
/** [1] 处理 fileList */
|
||||
// if (Object.hasOwn(this.fileList, file.typeCode)) {
|
||||
if (this.fileList.hasOwnProperty(file.typeCode)) {
|
||||
/** 已存在 */
|
||||
// this.fileList[file.typeCode].push({ id: file.id, name: fileName, typeCode: file.typeCode })
|
||||
this.fileList[file.typeCode].push(file);
|
||||
} else {
|
||||
// this.fileList[file.typeCode] = [{ id: file.id, name: fileName, typeCode: file.typeCode }]
|
||||
this.fileList[file.typeCode] = [file];
|
||||
}
|
||||
|
||||
/** [2] 处理 fileForm */
|
||||
// if (Object.hasOwn(this.fileForm, file.typeCode)) {
|
||||
if (this.fileForm.hasOwnProperty(file.typeCode)) {
|
||||
this.fileForm[file.typeCode].push(file.id);
|
||||
} else {
|
||||
this.fileForm[file.typeCode] = [file.id];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
/** 如果不是编辑,就填充自动生成的数据 */
|
||||
if (this.shouldWait)
|
||||
this.shouldWait.then(() => {
|
||||
if (this.tempForm.length) {
|
||||
// console.log('create new, tempform', JSON.stringify(this.tempForm.length))
|
||||
this.tempForm.forEach((item) => {
|
||||
// console.log('item data', item.data)
|
||||
this.dataForm[item.name] = item.data;
|
||||
});
|
||||
// console.log('create new, dataform', JSON.stringify(this.dataForm))
|
||||
}
|
||||
this.shouldWait = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
emitSelectChange(name, id) {
|
||||
const currentField = this.configs.fields.find((item) => item.name === name);
|
||||
if (currentField.relatedField) {
|
||||
this.dataForm[currentField.relatedField] = null;
|
||||
}
|
||||
this.$emit('select-change', { name, id });
|
||||
},
|
||||
|
||||
handleEditorReady(val) {},
|
||||
|
||||
handleClick(btn) {
|
||||
/** 提取url */
|
||||
// const urls = {};
|
||||
// this.configs.operations.map((item) => {
|
||||
// urls[item.name] = {};
|
||||
// urls[item.name].url = item.url;
|
||||
// urls[item.name].extraFields = item.extraFields || {};
|
||||
// });
|
||||
/** 操作 */
|
||||
switch (btn.name) {
|
||||
case 'save':
|
||||
case 'update':
|
||||
/** 需要验证表单的操作 */
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
/** 对于文件上传的单独处理(合并处理) */
|
||||
if (Object.keys(this.fileForm).length) {
|
||||
// LABEL: FILE_RELATED
|
||||
let fileIds = [];
|
||||
for (const [key, item] of Object.entries(this.fileForm)) {
|
||||
if (Array.isArray(item)) {
|
||||
fileIds = fileIds.concat(item);
|
||||
} else {
|
||||
console.error('handleClick(): 上传文件数组类型不正确');
|
||||
}
|
||||
}
|
||||
this.$set(this.dataForm, 'fileIds', fileIds);
|
||||
}
|
||||
|
||||
// console.log('before send: ', this.dataForm)
|
||||
|
||||
// this.$http({
|
||||
// url: this.$http.adornUrl(urls[btn.name].url),
|
||||
// method: btn.name === 'save' ? 'POST' : 'PUT',
|
||||
// data: { ...this.dataForm, ...urls[btn.name].extraFields },
|
||||
// })
|
||||
$http[btn.name === 'save' ? 'post' : 'put'](btn.url, { ...this.dataForm, ...btn.extraFields })
|
||||
.then((res) => {
|
||||
// .then(({ data: res }) => {
|
||||
console.log('save res: ', res, btn.name, this.dataForm);
|
||||
if (res && res.code === 0) {
|
||||
this.$message({
|
||||
message: '操作成功!',
|
||||
// message: i18n.t('prompt.success'),
|
||||
// message: btn.name === 'save' ? i18n.t('prompt.success') : '更新成功!',
|
||||
type: 'success',
|
||||
duration: 1500,
|
||||
onClose: () => {
|
||||
this.$emit('refreshDataList');
|
||||
this.visible = false;
|
||||
},
|
||||
});
|
||||
} else {
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$message({
|
||||
message: err,
|
||||
type: 'error',
|
||||
duration: 2000,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
return;
|
||||
case 'reset':
|
||||
for (const key of Object.keys(this.dataForm)) {
|
||||
if (typeof this.dataForm[key] === 'string') {
|
||||
this.dataForm[key] = '';
|
||||
} else if (this.dataForm[key] instanceof Array) {
|
||||
this.dataForm[key].splice(0);
|
||||
} else {
|
||||
this.dataForm[key] = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'cancel':
|
||||
this.handleClose();
|
||||
// add more..
|
||||
}
|
||||
},
|
||||
|
||||
// LABEL: FILE_RELATED
|
||||
handleUploadListUpdate(filelist, typeCode = 'DefaultTypeCode') {
|
||||
// console.log('before handleUploadListUpdate(): ', JSON.parse(JSON.stringify(this.fileForm)))
|
||||
// 设备类型 typeCode: EquipmentTypeFile
|
||||
// 设备信息 typeCode: EquipmentInfoFile | EquipmentInfoImage
|
||||
|
||||
// 原先写法:直接更新 dataForm 对象,不适用于有多个上传组件的需求
|
||||
// this.$set(
|
||||
// this.dataForm,
|
||||
// 'fileIds',
|
||||
// filelist.map(item => item.id)
|
||||
// )
|
||||
// console.log('handleUploadListUpdate(): ', this.dataForm)
|
||||
// 现更改为分流写法
|
||||
this.$set(
|
||||
this.fileForm,
|
||||
typeCode,
|
||||
filelist.map((item) => item.id)
|
||||
);
|
||||
// console.log('after handleUploadListUpdate(): ', this.fileForm)
|
||||
},
|
||||
|
||||
handleClose() {
|
||||
this.$emit('destory-dialog');
|
||||
this.visible = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.super-flexible-dialog >>> .el-select,
|
||||
.super-flexible-dialog >>> .el-cascader {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.super-flexible-dialog >>> ::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.super-flexible-dialog >>> ::-webkit-scrollbar-thumb {
|
||||
width: 4px;
|
||||
border-radius: 4px;
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
.super-flexible-dialog >>> .hidden-input {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
19
src/components/BaseDrawer.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<!-- 基本抽屉 -->
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: '',
|
||||
props: {},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
120
src/components/BaseListTable.vue
Normal file
@ -0,0 +1,120 @@
|
||||
<!-- 这里单纯的配置表格就好了-->
|
||||
<template>
|
||||
<div class="base-list-table w-full">
|
||||
<el-table :data="tableData" v-bind="tableConfig" ref="base-list-table">
|
||||
<!-- @cell-mouse-enter="(row, col, cell, event) => $emit('cell-mouse-enter', row, col, cell, event)"> -->
|
||||
<!-- @cell-mouse-leave="(row, col, cell, event) => $emit('cell-mouse-leave', row, col, cell, event)"> -->
|
||||
<!-- 表格头定义 -->
|
||||
<template v-for="(head, idx) in columnConfig">
|
||||
<!-- 索引列 -->
|
||||
<el-table-column
|
||||
:key="idx"
|
||||
v-if="head.type"
|
||||
:type="head.type"
|
||||
:label="head.label || head.name || ''"
|
||||
:header-align="head.align || 'center'"
|
||||
:align="head.align || 'center'"
|
||||
:width="head.width || 50"
|
||||
:index="
|
||||
head.type === 'index'
|
||||
? (val) => {
|
||||
return val + 1 + (page - 1) * size;
|
||||
}
|
||||
: null
|
||||
"
|
||||
v-bind="head.more"></el-table-column>
|
||||
<!-- 普通的表头 -->
|
||||
<el-table-column
|
||||
v-else
|
||||
:key="idx + 'else'"
|
||||
:label="head.label ? head.label : head.name"
|
||||
:prop="head.prop || null"
|
||||
:width="head.width || null"
|
||||
:min-width="head.minWidth || null"
|
||||
:fixed="head.fixed || null"
|
||||
:show-overflow-tooltip="head.showOverflowTooltip || true"
|
||||
:tooltip-effect="head.tooltipEffect || 'light'"
|
||||
filter-placement="top"
|
||||
:align="head.align || null"
|
||||
v-bind="head.more">
|
||||
<!-- 子组件 -->
|
||||
<template v-if="head.prop" slot-scope="scope">
|
||||
<component
|
||||
v-if="head.subcomponent"
|
||||
:is="head.subcomponent"
|
||||
:key="idx + 'sub'"
|
||||
:inject-data="{ ...scope.row, head }"
|
||||
@emit-data="handleSubEmitData" />
|
||||
<!-- 直接展示数据或应用过滤器 -->
|
||||
<span v-else>{{ scope.row[head.prop] | commonFilter(head.filter) }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 多级表头 -->
|
||||
<template v-if="!head.prop && head.children">
|
||||
<TableHead
|
||||
v-for="(subhead, subindex) in head.children"
|
||||
:key="'subhead-' + idx + '-subindex-' + subindex"
|
||||
:opt="subhead" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TableHead from './TableHead.vue';
|
||||
import $http from '@/utils/request'
|
||||
// TODO:
|
||||
// 1. 表格拖拽开启/关闭
|
||||
// 2. 表格的样式'
|
||||
// 3. more...
|
||||
|
||||
export default {
|
||||
name: 'BaseListTable',
|
||||
components: { TableHead },
|
||||
filters: {
|
||||
commonFilter: (source, filterType = (a) => a) => {
|
||||
return filterType(source);
|
||||
},
|
||||
},
|
||||
props: {
|
||||
tableConfig: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
columnConfig: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
tableData: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
}
|
||||
},
|
||||
inject: ['urls'],
|
||||
data() {
|
||||
return {
|
||||
page: 1,
|
||||
size: 20, // 默认20
|
||||
dataList: []
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 'props.tableData': {
|
||||
// handler: () => {
|
||||
// this.$refs['base-list-table'].doLayout();
|
||||
// },
|
||||
// immediate: true,
|
||||
// },
|
||||
},
|
||||
methods: {
|
||||
handleSubEmitData(payload) {
|
||||
console.log('[component] BaseListTable handleSubEmitData(): ', payload);
|
||||
this.$emit('operate-event', payload);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
167
src/components/BaseSearchForm.vue
Normal file
@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<el-row class="base-search-form rounded">
|
||||
<el-form :inline="true" :model="searchForm" :rules="headConfig.rules">
|
||||
<!-- <el-col :span="opt.span ? +opt.span : 4" v-for="opt in options" :key="opt.id"> -->
|
||||
<el-form-item
|
||||
v-for="(opt, index) in headConfig.fields"
|
||||
:key="opt.prop"
|
||||
:label="opt.label ? opt.label : null"
|
||||
:prop="'' + index"
|
||||
:rules="opt.elconfig?.rules ? opt.elconfig.rules : undefined"
|
||||
>
|
||||
<el-input
|
||||
v-if="opt.input"
|
||||
v-model="searchForm[index]"
|
||||
v-bind="opt.elconfig"
|
||||
/>
|
||||
<el-select
|
||||
v-if="opt.select"
|
||||
v-model="searchForm[index]"
|
||||
v-bind="opt.elconfig"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in opt.select"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
v-if="opt.timerange"
|
||||
v-model="searchForm[index]"
|
||||
v-bind="opt.elconfig"
|
||||
/>
|
||||
<el-upload
|
||||
v-if="opt.upload"
|
||||
:key="'upload_' + Math.random().toString()"
|
||||
class="inline-block pl-3"
|
||||
action="https://jsonplaceholder.typicode.com/posts/"
|
||||
>
|
||||
<el-button type="primary">上传文件</el-button>
|
||||
</el-upload>
|
||||
<el-button
|
||||
v-if="opt.button && (!opt.button.permission || $hasPermission(opt.button.permission))"
|
||||
:key="'button' + Math.random().toString()"
|
||||
:type="opt.button.type"
|
||||
@click="handleBtnClick(opt.button.name)"
|
||||
>{{ opt.button.name }}</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "",
|
||||
props: {
|
||||
headConfig: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchForm: {
|
||||
// 最大参数个数:10个,不够则在这里加
|
||||
0: null, // 参数一
|
||||
1: null,
|
||||
2: null,
|
||||
3: null,
|
||||
4: null,
|
||||
5: null,
|
||||
6: null,
|
||||
7: null,
|
||||
8: null,
|
||||
9: null,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
searchForm: {
|
||||
handler: (val) => {
|
||||
console.log("new val", val);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
created() {},
|
||||
mounted() {
|
||||
console.log(
|
||||
"head form config",
|
||||
JSON.parse(JSON.stringify(this.headConfig))
|
||||
);
|
||||
this.headConfig.fields.forEach((field, index) => {
|
||||
if (field.default) {
|
||||
// default 必须有个 value 属性!哪怕是 input 输入框
|
||||
this.searchForm[index] = field.default.value;
|
||||
}
|
||||
|
||||
// 更新选项列表
|
||||
if (!field.watch && field.fn && typeof field.fn === "function") {
|
||||
// 设置自身的选项列表
|
||||
field.fn().then((res) => {
|
||||
field.select = res.map((_) => {
|
||||
// 找到默认项
|
||||
if (_.default) {
|
||||
this.searchForm[index] = _.value;
|
||||
}
|
||||
return _;
|
||||
});
|
||||
console.log("[update] 更新选项列表", res, field.select);
|
||||
});
|
||||
}
|
||||
|
||||
// 模拟请求:
|
||||
const axios = function(url, data) {
|
||||
return new Promise((resolve) => {
|
||||
resolve("success");
|
||||
});
|
||||
};
|
||||
|
||||
// 如果需要监听关联字段
|
||||
if (field.watch) {
|
||||
const { index: innerIdx, condition } = field.watch;
|
||||
console.log(
|
||||
"=====field.watch=====",
|
||||
innerIdx,
|
||||
this.searchForm[innerIdx],
|
||||
this.headConfig.fields[innerIdx].default
|
||||
);
|
||||
// 设置监听器
|
||||
this.$watch(
|
||||
() => this.searchForm[innerIdx],
|
||||
(val) => {
|
||||
const queryParams = { [condition]: val };
|
||||
// field.watch.condition.forEach(c => {
|
||||
// queryParams
|
||||
// })
|
||||
axios(field.url, queryParams).then((res) => {
|
||||
console.log("[==>] 更新有前置条件的字段!!!", queryParams, res);
|
||||
// 此处是外部的 index
|
||||
this.searchForm[index] = Math.floor(Math.random() * 10);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
console.log("xxxxxxxxxx", this.searchForm);
|
||||
// 如果此时已经有默认值了,就立马根据这个默认值来发起请求
|
||||
if (this.searchForm[innerIdx]) {
|
||||
// TODO: 这个判断好像不太需要...
|
||||
console.log("TODO: 这个判断好像不太需要...");
|
||||
} else {
|
||||
console.log("TODO: 监听的字段还没来得及设置值呢...");
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
handleBtnClick(name) {
|
||||
this.$emit("btn-click", { btnName: name, payload: null });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
206
src/components/BaseTable.vue
Normal file
@ -0,0 +1,206 @@
|
||||
<!--
|
||||
* @Date: 2020-12-14 09:07:03
|
||||
* @LastEditors: gtz
|
||||
* @LastEditTime: 2022-06-13 08:59:21
|
||||
* @FilePath: \mt-bus-fe\src\components\BaseTable\index.vue
|
||||
* @Description:
|
||||
-->
|
||||
<template>
|
||||
<div class="">
|
||||
<el-table
|
||||
:header-cell-style="{
|
||||
background: '#FAFAFA',
|
||||
color: '#606266',
|
||||
height: '40px',
|
||||
}"
|
||||
:data="renderData"
|
||||
:show-header="showHeader"
|
||||
:border="border"
|
||||
fit
|
||||
highlight-current-rows
|
||||
style="width: 100%"
|
||||
@row-click="handleRowClick"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<!-- :max-height="height ? height : tableHeight(325)" -->
|
||||
<el-table-column v-if="sbox" type="selection" width="55" fixed />
|
||||
<el-table-column
|
||||
v-if="page && limit && !toggleCustomIndex"
|
||||
prop="_pageIndex"
|
||||
:label="'tableHeader.index' | i18nFilter"
|
||||
width="70"
|
||||
align="center"
|
||||
fixed
|
||||
/>
|
||||
<el-table-column
|
||||
v-for="item in renderTableHeadList"
|
||||
:key="item.prop"
|
||||
v-bind="item"
|
||||
:align="item.align ? item.align : 'left'"
|
||||
:min-width="item.minWidth ? item.minWidth : 150"
|
||||
:fixed="item.isFixed ? true : false"
|
||||
:show-overflow-tooltip="true"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<component
|
||||
:is="item.subcomponent"
|
||||
v-if="item.subcomponent"
|
||||
:key="scope.row.id"
|
||||
:inject-data="{ ...scope.row, ...item }"
|
||||
@emitData="emitData"
|
||||
/>
|
||||
<span v-else>{{
|
||||
scope.row[item.prop] | commonFilter(item.filter)
|
||||
}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<slot name="content" />
|
||||
<slot name="handleBtn" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "BaseTable",
|
||||
filters: {
|
||||
commonFilter: (source, filterType = (a) => a) => {
|
||||
return filterType(source);
|
||||
},
|
||||
},
|
||||
props: {
|
||||
toggleCustomIndex: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
topBtnConfig: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
showHeader: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
tableData: {
|
||||
type: Array,
|
||||
required: true,
|
||||
// validator: (val) => val.filter((item) => !isObject(item)).length === 0,
|
||||
},
|
||||
tableConfig: {
|
||||
type: Array,
|
||||
required: true,
|
||||
// validator: (val) =>
|
||||
// val.filter((item) => !isString(item.prop) || !isString(item.label))
|
||||
// .length === 0,
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
page: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
sbox: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
highIndex: {
|
||||
// 首行是否默认高亮
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableConfigBak: [],
|
||||
selectedBox: new Array(100).fill(true),
|
||||
// tableHeight,
|
||||
tableRowIndex: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
renderData() {
|
||||
return this.tableData.map((item, index) => {
|
||||
return {
|
||||
...item,
|
||||
_pageIndex: (this.page - 1) * this.limit + index + 1,
|
||||
};
|
||||
});
|
||||
},
|
||||
renderTableHeadList() {
|
||||
return this.tableConfig.filter((item, index) => {
|
||||
return this.selectedBox[index];
|
||||
});
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
tableData: function (val) {
|
||||
if (this.highIndex) {
|
||||
// 默认高亮行的,重置默认高亮行为第一行
|
||||
this.tableRowIndex = 0;
|
||||
}
|
||||
},
|
||||
},
|
||||
beforeMount() {
|
||||
this.selectedBox = new Array(100).fill(true);
|
||||
if (this.highIndex) {
|
||||
this.tableRowIndex = 0;
|
||||
}
|
||||
},
|
||||
// mounted() {
|
||||
// this.tableConfigBak = cloneDeep(this.tableConfig).map(item => {
|
||||
// return {
|
||||
// ...item,
|
||||
// selected: true
|
||||
// }
|
||||
// })
|
||||
// },
|
||||
methods: {
|
||||
emitData(val) {
|
||||
this.$emit("emitFun", val);
|
||||
},
|
||||
clickTopButton(val) {
|
||||
this.$emit("clickTopBtn", val);
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.$emit("selectChangeFun", val);
|
||||
},
|
||||
handleRowClick(row) {
|
||||
this.tableRowIndex = this.getArrayIndex(this.tableData, row); // 获取当前点击行下标
|
||||
this.$emit("selectRow", row);
|
||||
},
|
||||
tableRowClassName({ row, rowIndex }) {
|
||||
if (rowIndex === this.tableRowIndex) {
|
||||
return "success-row";
|
||||
}
|
||||
return "";
|
||||
},
|
||||
getArrayIndex(arr, obj) {
|
||||
var i = arr.length;
|
||||
while (i--) {
|
||||
if (arr[i].id === obj.id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
19
src/components/BaseUpload.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<!-- 基本上传 -->
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: '',
|
||||
props: {},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
433
src/components/DialogWithMenu.vue
Normal file
@ -0,0 +1,433 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
class="dialog-with-menu"
|
||||
:visible="selfVisible"
|
||||
@close="handleClose"
|
||||
:distory-on-close="true"
|
||||
>
|
||||
<!-- title -->
|
||||
<div slot="title" class="">
|
||||
<h1 class="">编辑</h1>
|
||||
</div>
|
||||
<!-- menu -->
|
||||
<el-tabs v-model="activeMenu" type="card" @tab-click="handleTabClick">
|
||||
<!-- <el-tab-pane v-for="(tab, index) in configs.menu" :key="index" :label="tab.name" :name="tab.name"> -->
|
||||
<el-tab-pane
|
||||
v-for="(tab, index) in actualMenus"
|
||||
:key="index"
|
||||
:label="tab.name"
|
||||
:name="tab.name"
|
||||
>
|
||||
<div v-if="index === 0">
|
||||
<!-- form -->
|
||||
<el-form ref="dataForm" :model="dataForm">
|
||||
<el-row
|
||||
v-for="(row, rowIndex) in configs.form.rows"
|
||||
:key="'row_' + rowIndex"
|
||||
:gutter="20"
|
||||
>
|
||||
<el-col
|
||||
v-for="(col, colIndex) in row"
|
||||
:key="colIndex"
|
||||
:span="24 / row.length"
|
||||
>
|
||||
<el-form-item
|
||||
:label="col.label"
|
||||
:prop="col.prop"
|
||||
:rules="col.rules || null"
|
||||
>
|
||||
<el-input
|
||||
v-if="col.input"
|
||||
v-model="dataForm[col.prop]"
|
||||
clearable
|
||||
:disabled="detailMode"
|
||||
v-bind="col.elparams"
|
||||
/>
|
||||
<el-select
|
||||
v-if="col.select"
|
||||
v-model="dataForm[col.prop]"
|
||||
clearable
|
||||
:disabled="detailMode"
|
||||
v-bind="col.elparams"
|
||||
@change="handleSelectChange(col, $event)"
|
||||
>
|
||||
<el-option
|
||||
v-for="(opt, optIdx) in col.options"
|
||||
:key="'option_' + optIdx"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-switch
|
||||
v-if="col.switch"
|
||||
v-model="dataForm[col.prop]"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
@change="handleSwitchChange"
|
||||
:disabled="detailMode"
|
||||
/>
|
||||
<el-input
|
||||
v-if="col.textarea"
|
||||
type="textarea"
|
||||
v-model="dataForm[col.prop]"
|
||||
:disabled="detailMode"
|
||||
v-bind="col.elparams"
|
||||
/>
|
||||
<!-- add more... -->
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<div v-if="dataForm.id && index === 1">
|
||||
<el-button
|
||||
v-if="!detailMode"
|
||||
type="primary"
|
||||
style="margin-bottom: 16px"
|
||||
@click="handleAddParam()"
|
||||
>添加</el-button
|
||||
>
|
||||
<BaseListTable
|
||||
:table-config="null"
|
||||
:column-config="filteredTableProps"
|
||||
:table-data="subList"
|
||||
@operate-event="handleTableRowOperate"
|
||||
/>
|
||||
<!-- paginator -->
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- sub dialog -->
|
||||
<small-dialog
|
||||
:append-to-body="true"
|
||||
v-if="showSubDialog"
|
||||
ref="subDialog"
|
||||
:url="urls.subase"
|
||||
:configs="configs.subDialog"
|
||||
:related-id="dataForm.id"
|
||||
@refreshDataList="getSubList"
|
||||
></small-dialog>
|
||||
|
||||
<!-- footer -->
|
||||
<div slot="footer">
|
||||
<template v-for="(operate, index) in configs.form.operations">
|
||||
<el-button
|
||||
v-if="showButton(operate)"
|
||||
:key="'operation_' + index"
|
||||
:type="operate.type"
|
||||
@click="handleBtnClick(operate)"
|
||||
>{{ operate.label }}</el-button
|
||||
>
|
||||
</template>
|
||||
<el-button @click="handleBtnClick({ name: 'cancel' })">取消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pick as __pick } from "@/utils/filters";
|
||||
import SmallDialog from "@/components/SmallDialog.vue";
|
||||
import BaseListTable from "@/components/BaseListTable.vue";
|
||||
|
||||
export default {
|
||||
name: "DialogWithMenu",
|
||||
components: { SmallDialog, BaseListTable },
|
||||
props: {
|
||||
configs: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
inject: ["urls"],
|
||||
data() {
|
||||
const dataForm = {};
|
||||
this.configs.form.rows.forEach((row) => {
|
||||
row.forEach((col) => {
|
||||
dataForm[col.prop] = col.default ?? "";
|
||||
console.log("==========>", col.prop, dataForm[col.prop]);
|
||||
});
|
||||
});
|
||||
return {
|
||||
// configs,
|
||||
activeMenu: this.configs.menu[0].name,
|
||||
dataForm,
|
||||
detailMode: false,
|
||||
selfVisible: false,
|
||||
showBaseDialog: false,
|
||||
baseDialogConfig: null,
|
||||
subList: [],
|
||||
showSubDialog: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
actualMenus() {
|
||||
return this.configs.menu.filter((m) => {
|
||||
if (m.onlyEditMode && !this.dataForm.id) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
},
|
||||
filteredTableProps() {
|
||||
return this.detailMode
|
||||
? this.configs.table.props.filter((v) => v.prop !== "operations")
|
||||
: this.configs.table.props;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/** utitilities */
|
||||
showButton(operate) {
|
||||
const notDetailMode = !this.detailMode;
|
||||
const showAlways = operate.showAlways ?? false;
|
||||
const editMode = operate.showOnEdit && this.dataForm.id;
|
||||
const addMode = !operate.showOnEdit && !this.dataForm.id;
|
||||
const permission = operate.permission
|
||||
? this.$hasPermission(operate.permission)
|
||||
: true;
|
||||
return (
|
||||
notDetailMode && (showAlways || ((editMode || addMode) && permission))
|
||||
);
|
||||
},
|
||||
resetForm(excludeId = false) {
|
||||
setTimeout(() => {
|
||||
Object.keys(this.dataForm).forEach((key) => {
|
||||
if (excludeId && key === "id") return;
|
||||
this.dataForm[key] = null;
|
||||
});
|
||||
this.activeMenu = this.configs.menu[0].name;
|
||||
}, 500);
|
||||
},
|
||||
|
||||
/** init **/
|
||||
init(id, detailMode) {
|
||||
console.log("[dialog] DialogWithHead init():", id, detailMode);
|
||||
|
||||
this.detailMode = detailMode ?? false;
|
||||
this.$nextTick(() => {
|
||||
// this.$refs['dataForm'].resetFields();
|
||||
|
||||
this.dataForm.id = id || null;
|
||||
if (this.dataForm.id) {
|
||||
// 如果是编辑
|
||||
this.$http
|
||||
.get(this.urls.base + `/${this.dataForm.id}`)
|
||||
.then(({ data: res }) => {
|
||||
// dev env:
|
||||
// if (LOCAL) res.data.id = res.data._id;
|
||||
// end dev env
|
||||
console.log("[base dialog init] ", res);
|
||||
if (res && res.code === 0) {
|
||||
const dataFormKeys = Object.keys(this.dataForm);
|
||||
console.log("keys ===> ", dataFormKeys);
|
||||
// console.log('data form keys: ', dataFormKeys, pick(res.data, dataFormKeys))
|
||||
this.dataForm = __pick(res.data, dataFormKeys);
|
||||
console.log(
|
||||
"pick(res.data, dataFormKeys) ===> ",
|
||||
__pick(res.data, dataFormKeys)
|
||||
);
|
||||
// LABEL: FILE_RELATED
|
||||
/** 对文件下载进行分流 */
|
||||
// this.fileList = {};
|
||||
// if (this.dataForm.files) {
|
||||
// // console.log('files: ', this.dataForm.files)
|
||||
// this.dataForm.files.forEach((file) => {
|
||||
// // const fileName = file.fileurls.split('/').pop()
|
||||
// /** [1] 处理 fileList */
|
||||
// // if (Object.hasOwn(this.fileList, file.typeCode)) {
|
||||
// if (this.fileList.hasOwnProperty(file.typeCode)) {
|
||||
// /** 已存在 */
|
||||
// // this.fileList[file.typeCode].push({ id: file.id, name: fileName, typeCode: file.typeCode })
|
||||
// this.fileList[file.typeCode].push(file);
|
||||
// } else {
|
||||
// // this.fileList[file.typeCode] = [{ id: file.id, name: fileName, typeCode: file.typeCode }]
|
||||
// this.fileList[file.typeCode] = [file];
|
||||
// }
|
||||
|
||||
// /** [2] 处理 fileForm */
|
||||
// // if (Object.hasOwn(this.fileForm, file.typeCode)) {
|
||||
// if (this.fileForm.hasOwnProperty(file.typeCode)) {
|
||||
// this.fileForm[file.typeCode].push(file.id);
|
||||
// } else {
|
||||
// this.fileForm[file.typeCode] = [file.id];
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 如果不是编辑
|
||||
}
|
||||
});
|
||||
|
||||
this.selfVisible = true;
|
||||
},
|
||||
|
||||
/** handlers */
|
||||
handleSelectChange(col, eventValue) {
|
||||
console.log("[dialog] select change: ", col, eventValue);
|
||||
},
|
||||
handleSwitchChange(val) {
|
||||
console.log("[dialog] switch change: ", val, this.dataForm);
|
||||
},
|
||||
handleBtnClick(payload) {
|
||||
console.log("btn click payload: ", payload);
|
||||
|
||||
if ("name" in payload) {
|
||||
switch (payload.name) {
|
||||
case "cancel":
|
||||
this.handleClose();
|
||||
break;
|
||||
case "reset":
|
||||
this.resetForm(true); // true means exclude id
|
||||
break;
|
||||
case "add":
|
||||
case "update": {
|
||||
const method = payload.name === "add" ? "POST" : "PUT";
|
||||
this.$http({
|
||||
url: this.urls.base,
|
||||
method,
|
||||
data: this.dataForm,
|
||||
}).then(({ data: res }) => {
|
||||
console.log("[add&update] res is: ", res);
|
||||
this.$message.success(
|
||||
payload.name === "add" ? "添加成功" : "更新成功"
|
||||
);
|
||||
this.$emit("refreshDataList");
|
||||
this.handleClose();
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("[x] 不是这么用的! 缺少name属性");
|
||||
}
|
||||
},
|
||||
handleTabClick(payload) {
|
||||
console.log("tab click payload: ", this.activeMenu);
|
||||
if (this.activeMenu === this.configs.menu[1].name) {
|
||||
// 获取数据
|
||||
this.getSubList();
|
||||
}
|
||||
},
|
||||
|
||||
getSubList(page = 1, size = 20) {
|
||||
const params = {};
|
||||
params.page = page;
|
||||
params.limit = size;
|
||||
|
||||
const requiredParams = this.configs.table.extraParams;
|
||||
if (requiredParams) {
|
||||
if (Array.isArray(requiredParams)) {
|
||||
requiredParams.forEach((str) => {
|
||||
if (/id/i.test(str)) {
|
||||
params[str] = this.dataForm.id;
|
||||
} else {
|
||||
params[str] = "";
|
||||
}
|
||||
});
|
||||
} else if (typeof requiredParams === "string") {
|
||||
// 如果需要额外参数,一般肯定需要
|
||||
params[this.configs.table.extraParams] = this.dataForm.id;
|
||||
// 此时 dataForm.id 一定是存在的
|
||||
}
|
||||
}
|
||||
this.$http.get(this.urls.subpage, { params }).then(({ data: res }) => {
|
||||
console.log("[subList] getSubList:", res);
|
||||
if (res.code === 0 && res.data?.list) {
|
||||
// 有数据
|
||||
this.subList = res.data.list;
|
||||
} else {
|
||||
this.subList.splice(0);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
handleAddParam(id) {
|
||||
this.showSubDialog = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.subDialog.init(id ?? null);
|
||||
});
|
||||
},
|
||||
|
||||
handleClose() {
|
||||
this.resetForm();
|
||||
this.selfVisible = false;
|
||||
},
|
||||
|
||||
/** 列表handlers */
|
||||
handleAddItem() {
|
||||
// console.log('[dialog] handleAddItem ot list...');
|
||||
this.showBaseDialog = true;
|
||||
this.$nextTick(() => {
|
||||
console.log("[sub-dialog] ", this.$refs["sub-dialog"].init());
|
||||
});
|
||||
},
|
||||
handleTableRowOperate({ type, data }) {
|
||||
console.log("handleTableRowOperate", type, data);
|
||||
|
||||
switch (type) {
|
||||
case "delete": {
|
||||
// 确认是否删除
|
||||
return this.$confirm(`是否删除条目: ${data}`, "提示", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "我再想想",
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
// this.$http.delete(this.urls.base + `/${data}`).then((res) => {
|
||||
this.$http({
|
||||
url: this.urls.subase,
|
||||
method: "DELETE",
|
||||
data: [`${data}`],
|
||||
}).then(({ data: res }) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success({
|
||||
message: "删除成功!",
|
||||
duration: 1000,
|
||||
onClose: () => {
|
||||
this.getSubList(1, 20);
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch((err) => {});
|
||||
}
|
||||
case "edit": {
|
||||
this.handleAddParam(data); /** data is ==> id */
|
||||
break;
|
||||
}
|
||||
// case 'view-detail-action':
|
||||
// this.openDialog(data, true);
|
||||
// break;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-menu {
|
||||
margin: 16px 0 !important;
|
||||
}
|
||||
|
||||
.el-menu.el-menu--horizontal {
|
||||
border: none !important;
|
||||
/* background: #0f02 !important; */
|
||||
}
|
||||
|
||||
/* .el-menu--horizontal > .el-menu-item.is-active { */
|
||||
/* border-bottom-color: #0b58ff; */
|
||||
/* } */
|
||||
|
||||
.dialog-with-menu >>> .el-dialog__body {
|
||||
/* padding-top: 16px !important;
|
||||
padding-bottom: 16px !important; */
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.el-select {
|
||||
width: 100% !important;
|
||||
}
|
||||
</style>
|
2
src/components/README.config.md
Normal file
@ -0,0 +1,2 @@
|
||||
配置文件选项总结
|
||||
|
220
src/components/SmallDialog.vue
Normal file
@ -0,0 +1,220 @@
|
||||
<template>
|
||||
<!-- :title="isDetail ? title.detail : !dataForm.id ? title.add : title.edit" -->
|
||||
<el-dialog
|
||||
class="a-small-dialog"
|
||||
:visible.sync="visible"
|
||||
@close="handleClose"
|
||||
:distory-on-close="true"
|
||||
:close-on-click-modal="false"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<!-- :append-to-body="appendToBody"> -->
|
||||
<div>
|
||||
<el-form ref="dataForm" :model="dataForm">
|
||||
<el-row
|
||||
v-for="(row, rowIndex) in configs.rows"
|
||||
:key="'row_' + rowIndex"
|
||||
:gutter="20"
|
||||
>
|
||||
<el-col
|
||||
v-for="(col, colIndex) in row"
|
||||
:key="colIndex"
|
||||
:span="col.span ?? 24 / row.length"
|
||||
>
|
||||
<el-form-item
|
||||
:prop="col.prop"
|
||||
:rules="col.rules || null"
|
||||
:label="col.label"
|
||||
>
|
||||
<el-input
|
||||
v-if="col.input"
|
||||
v-model="dataForm[col.prop]"
|
||||
clearable
|
||||
:disabled="detailMode"
|
||||
v-bind="col.elparams"
|
||||
/>
|
||||
<el-select
|
||||
v-if="col.select"
|
||||
v-model="dataForm[col.prop]"
|
||||
clearable
|
||||
:disabled="detailMode"
|
||||
v-bind="col.elparams"
|
||||
@change="handleSelectChange(col, $event)"
|
||||
>
|
||||
<el-option
|
||||
v-for="(opt, optIdx) in col.options"
|
||||
:key="'option_' + optIdx"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-switch
|
||||
v-if="col.switch"
|
||||
v-model="dataForm[col.prop]"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
@change="handleSwitchChange"
|
||||
:disabled="detailMode"
|
||||
/>
|
||||
<el-input
|
||||
v-if="col.textarea"
|
||||
type="textarea"
|
||||
v-model="dataForm[col.prop]"
|
||||
:disabled="detailMode"
|
||||
v-bind="col.elparams"
|
||||
/>
|
||||
<!-- add more... -->
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- footer -->
|
||||
<div slot="footer">
|
||||
<template v-for="(operate, index) in configs.operations">
|
||||
<el-button
|
||||
v-if="showButton(operate)"
|
||||
:key="'operation_' + index"
|
||||
:type="operate.type"
|
||||
@click="handleBtnClick(operate)"
|
||||
>{{ operate.label }}</el-button
|
||||
>
|
||||
</template>
|
||||
<el-button @click="handleBtnClick({ name: 'cancel' })">取消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import CKEditor from 'ckeditor4-vue'
|
||||
// import AttrForm from './AttrForm'
|
||||
// import { pick } from 'lodash/object'
|
||||
import { pick as __pick } from "@/utils/filters";
|
||||
// import i18n from '@/i18n'
|
||||
|
||||
export default {
|
||||
name: "SmallDialog",
|
||||
props: {
|
||||
configs: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
relatedId: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
inject: ["urls"],
|
||||
data() {
|
||||
const dataForm = {};
|
||||
this.configs.rows.forEach((row) => {
|
||||
row.forEach((col) => {
|
||||
dataForm[col.prop] = col.default ?? "";
|
||||
console.log("[small dialog]==========>", col.prop, dataForm[col.prop]);
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
visible: false,
|
||||
detailMode: false,
|
||||
dataForm,
|
||||
dataFormRules: {},
|
||||
tempForm: [], // 临时保存自动生成的code,或其他数据
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/** utitilities */
|
||||
showButton(operate) {
|
||||
const notDetailMode = !this.detailMode;
|
||||
const showAlways = operate.showAlways ?? false;
|
||||
const editMode = operate.showOnEdit && this.dataForm.id;
|
||||
const addMode = !operate.showOnEdit && !this.dataForm.id;
|
||||
const permission = operate.permission
|
||||
? this.$hasPermission(operate.permission)
|
||||
: true;
|
||||
return (
|
||||
notDetailMode && (showAlways || ((editMode || addMode) && permission))
|
||||
);
|
||||
},
|
||||
resetForm(excludeId = false) {
|
||||
setTimeout(() => {
|
||||
Object.keys(this.dataForm).forEach((key) => {
|
||||
console.log(">>> clearing key: ", key);
|
||||
if (excludeId && key === "id") return;
|
||||
this.dataForm[key] = null;
|
||||
});
|
||||
this.detailMode = false;
|
||||
}, 500);
|
||||
},
|
||||
|
||||
init(id, isdetail = false) {
|
||||
this.detailMode = isdetail;
|
||||
console.log("[small dialog] init", id, isdetail);
|
||||
|
||||
this.$nextTick(() => {
|
||||
// this.$refs['dataForm'].resetFields();
|
||||
this.dataForm.id = id || null;
|
||||
|
||||
if (this.dataForm.id) {
|
||||
// 如果是编辑
|
||||
$http.get(this.urls.subase + `/${this.dataForm.id}`).then((res) => {
|
||||
// dev env:
|
||||
if (LOCAL) res.data.id = res.data._id;
|
||||
// end dev env
|
||||
if (res && res.code === 0) {
|
||||
const dataFormKeys = Object.keys(this.dataForm);
|
||||
this.dataForm = __pick(res.data, dataFormKeys);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 如果不是编辑
|
||||
}
|
||||
});
|
||||
|
||||
this.visible = true;
|
||||
},
|
||||
|
||||
handleSelectChange(col, event) {},
|
||||
handleSwitchChange() {},
|
||||
|
||||
handleBtnClick(payload) {
|
||||
console.log("btn click payload: ", payload);
|
||||
console.log("configs", this.configs);
|
||||
if ("name" in payload) {
|
||||
switch (payload.name) {
|
||||
case "cancel":
|
||||
this.handleClose();
|
||||
break;
|
||||
case "add":
|
||||
case "update": {
|
||||
const method = payload.name === "add" ? "POST" : "PUT";
|
||||
$http({
|
||||
url: this.urls.subase,
|
||||
method,
|
||||
data: {
|
||||
...this.dataForm,
|
||||
[this.configs.extraParams]: this.relatedId,
|
||||
},
|
||||
}).then((res) => {
|
||||
console.log("[add&update] res is: ", res);
|
||||
this.$message.success(
|
||||
payload.name === "add" ? "添加成功" : "更新成功"
|
||||
);
|
||||
this.$emit("refreshDataList");
|
||||
this.handleClose();
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("[x] 不是这么用的! 缺少name属性");
|
||||
}
|
||||
},
|
||||
|
||||
handleClose() {
|
||||
this.resetForm();
|
||||
this.visible = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
40
src/components/TableHead.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<el-table-column
|
||||
:label="opt.label ? opt.label : opt.name"
|
||||
:prop="opt.prop || null"
|
||||
:width="opt.width || null"
|
||||
:min-width="opt.minWidth || null"
|
||||
:fixed="opt.fixed || null"
|
||||
:show-overflow-tooltip="opt.showOverflowTooltip || false"
|
||||
filter-placement="top"
|
||||
:align="opt.align || null"
|
||||
v-bind="opt.more"
|
||||
>
|
||||
<template v-if="opt.prop" slot-scope="scope">
|
||||
<component v-if="opt.subcomponent" :is="opt.subcomponent" :key="idx + 'sub'" :inject-data="{ ...scope.row, head: opt }" @emit-data="handleSubEmitData" />
|
||||
<!-- 直接展示数据或应用过滤器 -->
|
||||
<span v-else>{{ scope.row[opt.prop] | commonFilter(opt.filter) }}</span>
|
||||
</template>
|
||||
<!-- 递归 -->
|
||||
<template v-if="!opt.prop && opt.children">
|
||||
<TableHead v-for="(subhead, index) in opt.children" :key="'subhead' + index" :opt="subhead" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TableHead',
|
||||
filters: {
|
||||
commonFilter: (source, filterType = a => a) => {
|
||||
return filterType(source)
|
||||
}
|
||||
},
|
||||
props: {
|
||||
opt: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
1
src/components/noTemplateComponents/README.md
Normal file
@ -0,0 +1 @@
|
||||
用于存放没有模板的组件,一般用于表格中或弹窗里的组件插入
|
31
src/components/noTemplateComponents/detailComponent.js
Normal file
@ -0,0 +1,31 @@
|
||||
// import i18n from '@/i18n'
|
||||
|
||||
export default {
|
||||
name: 'TableTextComponent',
|
||||
props: {
|
||||
injectData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// for i18n inject:
|
||||
// defaultText: i18n.t('viewdetail')
|
||||
defaultText: '查看详情'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
emitClick() {
|
||||
// console.log('inject data:' ,this.injectData)
|
||||
this.$emit('emit-data', {
|
||||
type: this.injectData.head?.actionName || 'view-detail-action',
|
||||
data: this.injectData.head?.emitFullData ? this.injectData : this.injectData.id
|
||||
})
|
||||
}
|
||||
},
|
||||
render: function (h) {
|
||||
// console.log('button content:', this.injectData)
|
||||
return h('span', null, [h('el-button', { props: { type: 'text' }, style: { paddingLeft: 0 }, on: { click: this.emitClick } }, this.injectData.head?.buttonContent || this.defaultText)])
|
||||
}
|
||||
}
|
98
src/components/noTemplateComponents/operationComponent.js
Normal file
@ -0,0 +1,98 @@
|
||||
// import i18n from '@/i18n'
|
||||
|
||||
export default {
|
||||
name: 'TableOperations',
|
||||
props: {
|
||||
injectData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
btnTypes: {
|
||||
add: 'primary',
|
||||
delete: 'danger',
|
||||
detail: 'info'
|
||||
// add more...
|
||||
},
|
||||
colors: {
|
||||
delete: '#FF5454',
|
||||
preview: '#f09843',
|
||||
design: '#99089f',
|
||||
// 'view-trend': 'red'
|
||||
// add more...
|
||||
},
|
||||
text: {
|
||||
// TODO: i18n
|
||||
// edit: i18n.t('edit'),
|
||||
// detail: i18n.t('detail'),
|
||||
// delete: i18n.t('delete'),
|
||||
// viewAttr: i18n.t('viewattr'),
|
||||
// preview: i18n.t('preview'),
|
||||
// design: i18n.t('design'),
|
||||
edit: '编辑',
|
||||
detail: '详情',
|
||||
delete: '删除',
|
||||
viewAttr: '查看属性',
|
||||
preview: '预览',
|
||||
design: '设计',
|
||||
'view-trend': '查看趋势'
|
||||
// add more...
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 发射事件
|
||||
emit(opt) {
|
||||
let emitFull = false
|
||||
let eventType = 'default'
|
||||
let customField
|
||||
if (typeof opt === 'object') {
|
||||
eventType = opt.name
|
||||
customField = opt.emitField || 'id'
|
||||
emitFull = opt.emitFull || false
|
||||
} else {
|
||||
eventType = opt
|
||||
}
|
||||
this.$emit('emit-data', { type: eventType, data: emitFull ? this.injectData : customField ? this.injectData[customField] : this.injectData.id })
|
||||
}
|
||||
},
|
||||
render: function (h) {
|
||||
let btns = []
|
||||
for (const opt of this.injectData.head?.options) {
|
||||
const optIsObj = typeof opt === 'object'
|
||||
|
||||
if (optIsObj) {
|
||||
// 可能需要验证权限,如 opt.permission 选项
|
||||
// 注意:为空字符串或null/undefined都会不验证权限
|
||||
if (!opt.permission || (opt.permission && this.$hasPermission(opt.permission))) {
|
||||
btns.push(
|
||||
h('el-button',
|
||||
{
|
||||
props: { type: 'text' },
|
||||
style: { color: this.colors[optionStr.name] || '#409EFF' },
|
||||
on: { click: this.emit.bind(null, opt) }
|
||||
},
|
||||
this.text[opt.name]
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// 此时 opt 是一个 string,且默认有操作权限
|
||||
btns.push(
|
||||
h('el-button',
|
||||
{
|
||||
props: { type: 'text' },
|
||||
style: { color: this.colors[opt] || '#409EFF' },
|
||||
on: { click: this.emit.bind(null, opt) }
|
||||
},
|
||||
this.text[opt]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
return h('span', null, btns)
|
||||
}
|
||||
}
|
2
src/components/noTemplateComponents/richInput.js
Normal file
@ -0,0 +1,2 @@
|
||||
// 富文本组件
|
||||
export default {}
|
47
src/components/noTemplateComponents/statusComponent.js
Normal file
@ -0,0 +1,47 @@
|
||||
// import i18n from '@/i18n'
|
||||
|
||||
export default {
|
||||
name: 'StatusComponent',
|
||||
props: {
|
||||
injectData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
statusText: [
|
||||
'正常',
|
||||
'异常',
|
||||
'损坏',
|
||||
// more...
|
||||
],
|
||||
statusType: [
|
||||
'success',
|
||||
'warning',
|
||||
'danger',
|
||||
// more...
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isEnabled() {
|
||||
return this.injectData && (this.injectData.enabled === 1 || this.injectData.enabled.toString() === '1')
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.log('[component] StatusComponent: ', this.injectData)
|
||||
},
|
||||
methods: {
|
||||
// 发射事件
|
||||
emit(opt) { }
|
||||
},
|
||||
render: function (h) {
|
||||
return h('el-tag',
|
||||
{
|
||||
props:
|
||||
{ type: this.isEnabled ? this.statusType[0] : this.statusType[1] }
|
||||
},
|
||||
this.isEnabled ? this.statusText[0] : this.statusText[1])
|
||||
}
|
||||
}
|
2
src/components/noTemplateComponents/switchBtn.js
Normal file
@ -0,0 +1,2 @@
|
||||
// 表格中的开关
|
||||
export default {}
|
2
src/components/noTemplateComponents/textBtn.js
Normal file
@ -0,0 +1,2 @@
|
||||
// 表格中的可点击文本
|
||||
export default {}
|
7
src/components/ren-dept-tree/index.js
Normal file
@ -0,0 +1,7 @@
|
||||
import RenDeptTree from './src/ren-dept-tree'
|
||||
|
||||
RenDeptTree.install = function (Vue) {
|
||||
Vue.component(RenDeptTree.name, RenDeptTree)
|
||||
}
|
||||
|
||||
export default RenDeptTree
|
118
src/components/ren-dept-tree/src/ren-dept-tree.vue
Normal file
@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-input v-model="showDeptName" :placeholder="placeholder" @focus="deptDialog">
|
||||
<el-button slot="append" icon="el-icon-search" @click="deptDialog"></el-button>
|
||||
</el-input>
|
||||
<el-input :value="value" style="display: none"></el-input>
|
||||
<el-dialog :visible.sync="visibleDept" width="30%" :modal="false" :title="placeholder" :close-on-click-modal="false" :close-on-press-escape="false">
|
||||
<el-form size="mini" :inline="true">
|
||||
<el-form-item :label="$t('keyword')">
|
||||
<el-input v-model="filterText"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="default">{{ $t('query') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-tree
|
||||
class="filter-tree"
|
||||
:data="deptList"
|
||||
:default-expanded-keys="expandedKeys"
|
||||
:props="{ label: 'name', children: 'children' }"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
:highlight-current="true"
|
||||
node-key="id"
|
||||
ref="tree">
|
||||
</el-tree>
|
||||
<template slot="footer">
|
||||
<el-button type="default" @click="cancelHandle()" size="mini">{{ $t('cancel') }}</el-button>
|
||||
<el-button v-if="query" type="info" @click="clearHandle()" size="mini">{{ $t('clear') }}</el-button>
|
||||
<el-button type="primary" @click="commitHandle()" size="mini">{{ $t('confirm') }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'RenDeptTree',
|
||||
data () {
|
||||
return {
|
||||
filterText: '',
|
||||
visibleDept: false,
|
||||
deptList: [],
|
||||
showDeptName: '',
|
||||
expandedKeys: null,
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'label'
|
||||
}
|
||||
}
|
||||
},
|
||||
props: {
|
||||
value: [Number, String],
|
||||
deptName: String,
|
||||
query: Boolean,
|
||||
placeholder: String
|
||||
},
|
||||
watch: {
|
||||
filterText (val) {
|
||||
this.$refs.tree.filter(val)
|
||||
},
|
||||
deptName (val) {
|
||||
this.showDeptName = val
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deptDialog () {
|
||||
this.expandedKeys = null
|
||||
if (this.$refs.tree) {
|
||||
this.$refs.tree.setCurrentKey(null)
|
||||
}
|
||||
this.visibleDept = true
|
||||
this.getDeptList(this.value)
|
||||
},
|
||||
filterNode (value, data) {
|
||||
if (!value) return true
|
||||
return data.name.indexOf(value) !== -1
|
||||
},
|
||||
getDeptList (id) {
|
||||
return this.$http.get('/sys/dept/list').then(({ data: res }) => {
|
||||
if (res.code !== 0) {
|
||||
return this.$message.error(res.msg)
|
||||
}
|
||||
this.deptList = res.data
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tree.setCurrentKey(id)
|
||||
this.expandedKeys = [id]
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
cancelHandle () {
|
||||
this.visibleDept = false
|
||||
this.deptList = []
|
||||
this.filterText = ''
|
||||
},
|
||||
clearHandle () {
|
||||
this.$emit('input', '')
|
||||
this.$emit('update:deptName', '')
|
||||
this.showDeptName = ''
|
||||
this.visibleDept = false
|
||||
this.deptList = []
|
||||
this.filterText = ''
|
||||
},
|
||||
commitHandle () {
|
||||
const node = this.$refs.tree.getCurrentNode()
|
||||
if (!node) {
|
||||
this.$message.error(this.$t('dept.chooseerror'))
|
||||
return
|
||||
}
|
||||
this.$emit('input', node.id)
|
||||
this.$emit('update:deptName', node.name)
|
||||
this.showDeptName = node.name
|
||||
this.visibleDept = false
|
||||
this.deptList = []
|
||||
this.filterText = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
7
src/components/ren-radio-group/index.js
Normal file
@ -0,0 +1,7 @@
|
||||
import RenRadioGroup from './src/ren-radio-group'
|
||||
|
||||
RenRadioGroup.install = function (Vue) {
|
||||
Vue.component(RenRadioGroup.name, RenRadioGroup)
|
||||
}
|
||||
|
||||
export default RenRadioGroup
|
20
src/components/ren-radio-group/src/ren-radio-group.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<el-radio-group :value="value+''" @input="$emit('input', $event)">
|
||||
<el-radio :label="data.dictValue" v-for="data in dataList" :key="data.dictValue">{{data.dictLabel}}</el-radio>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
<script>
|
||||
import { getDictDataList } from '@/utils'
|
||||
export default {
|
||||
name: 'RenRadioGroup',
|
||||
data () {
|
||||
return {
|
||||
dataList: getDictDataList(this.dictType)
|
||||
}
|
||||
},
|
||||
props: {
|
||||
value: [Number, String],
|
||||
dictType: String
|
||||
}
|
||||
}
|
||||
</script>
|
7
src/components/ren-region-tree/index.js
Normal file
@ -0,0 +1,7 @@
|
||||
import RenRegionTree from './src/ren-region-tree'
|
||||
|
||||
RenRegionTree.install = function (Vue) {
|
||||
Vue.component(RenRegionTree.name, RenRegionTree)
|
||||
}
|
||||
|
||||
export default RenRegionTree
|
132
src/components/ren-region-tree/src/ren-region-tree.vue
Normal file
@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div class="ren-region">
|
||||
<el-input v-model="showName" :placeholder="placeholder" @focus="treeDialog">
|
||||
<el-button slot="append" icon="el-icon-search" @click="treeDialog"></el-button>
|
||||
</el-input>
|
||||
<el-input :value="value" style="display: none"></el-input>
|
||||
<el-dialog :visible.sync="visibleTree" width="360px" :modal="false" :title="placeholder" :close-on-click-modal="false" :close-on-press-escape="false">
|
||||
<el-form size="mini" :inline="true">
|
||||
<el-form-item :label="$t('keyword')">
|
||||
<el-input v-model="filterText"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="default">{{ $t('query') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-tree
|
||||
class="filter-tree"
|
||||
:data="dataList"
|
||||
:default-expanded-keys="expandedKeys"
|
||||
:props="{ label: 'name', children: 'children' }"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
:highlight-current="true"
|
||||
node-key="id"
|
||||
ref="tree">
|
||||
</el-tree>
|
||||
<template slot="footer">
|
||||
<el-button type="default" @click="cancelHandle()" size="mini">{{ $t('cancel') }}</el-button>
|
||||
<el-button type="info" @click="clearHandle()" size="mini">{{ $t('clear') }}</el-button>
|
||||
<el-button type="primary" @click="commitHandle()" size="mini">{{ $t('confirm') }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
.ren-region {
|
||||
.filter-tree {
|
||||
max-height: 230px;
|
||||
overflow: auto;
|
||||
}
|
||||
.el-dialog__body {
|
||||
padding: 0px 0px 0px 20px;
|
||||
}
|
||||
.el-dialog__footer {
|
||||
padding: 10px 20px 8px 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import { treeDataTranslate } from '@/utils'
|
||||
export default {
|
||||
name: 'RenRegionTree',
|
||||
data () {
|
||||
return {
|
||||
filterText: '',
|
||||
visibleTree: false,
|
||||
dataList: [],
|
||||
showName: '',
|
||||
expandedKeys: null,
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'name'
|
||||
}
|
||||
}
|
||||
},
|
||||
props: {
|
||||
value: [Number, String],
|
||||
parentName: String,
|
||||
placeholder: String
|
||||
},
|
||||
watch: {
|
||||
filterText (val) {
|
||||
this.$refs.tree.filter(val)
|
||||
},
|
||||
parentName (val) {
|
||||
this.showName = val
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
treeDialog () {
|
||||
this.expandedKeys = null
|
||||
if (this.$refs.tree) {
|
||||
this.$refs.tree.setCurrentKey(null)
|
||||
}
|
||||
this.visibleTree = true
|
||||
this.getDataList(this.value)
|
||||
},
|
||||
filterNode (value, data) {
|
||||
if (!value) return true
|
||||
return data.name.indexOf(value) !== -1
|
||||
},
|
||||
getDataList (id) {
|
||||
return this.$http.get('/sys/region/tree').then(({ data: res }) => {
|
||||
if (res.code !== 0) {
|
||||
return this.$message.error(res.msg)
|
||||
}
|
||||
this.dataList = treeDataTranslate(res.data)
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tree.setCurrentKey(id)
|
||||
this.expandedKeys = [id]
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
cancelHandle () {
|
||||
this.visibleTree = false
|
||||
this.dataList = []
|
||||
this.filterText = ''
|
||||
},
|
||||
clearHandle () {
|
||||
this.$emit('input', '0')
|
||||
this.$emit('update:parentName', '')
|
||||
this.showName = ''
|
||||
this.visibleTree = false
|
||||
this.dataList = []
|
||||
this.filterText = ''
|
||||
},
|
||||
commitHandle () {
|
||||
const node = this.$refs.tree.getCurrentNode()
|
||||
if (!node) {
|
||||
this.$message.error(this.$t('choose'))
|
||||
return
|
||||
}
|
||||
this.$emit('input', node.id)
|
||||
this.$emit('update:parentName', node.name)
|
||||
this.showName = node.name
|
||||
this.visibleTree = false
|
||||
this.dataList = []
|
||||
this.filterText = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
7
src/components/ren-select/index.js
Normal file
@ -0,0 +1,7 @@
|
||||
import RenSelect from './src/ren-select'
|
||||
|
||||
RenSelect.install = function (Vue) {
|
||||
Vue.component(RenSelect.name, RenSelect)
|
||||
}
|
||||
|
||||
export default RenSelect
|
21
src/components/ren-select/src/ren-select.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<el-select :value="value+''" @input="$emit('input', $event)" :placeholder="placeholder" clearable>
|
||||
<el-option :label="data.dictLabel" v-for="data in dataList" :key="data.dictValue" :value ="data.dictValue">{{data.dictLabel}}</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
<script>
|
||||
import { getDictDataList } from '@/utils'
|
||||
export default {
|
||||
name: 'RenSelect',
|
||||
data () {
|
||||
return {
|
||||
dataList: getDictDataList(this.dictType)
|
||||
}
|
||||
},
|
||||
props: {
|
||||
value: [Number, String],
|
||||
dictType: String,
|
||||
placeholder: String
|
||||
}
|
||||
}
|
||||
</script>
|
1030
src/element-ui/theme-variables.scss
Normal file
1
src/element-ui/theme/alert.css
Normal file
@ -0,0 +1 @@
|
||||
.el-alert{width:100%;padding:8px 16px;margin:0;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:4px;position:relative;background-color:#FFF;overflow:hidden;opacity:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-transition:opacity .2s;transition:opacity .2s}.el-alert.is-light .el-alert__closebtn{color:#C0C4CC}.el-alert.is-dark .el-alert__closebtn,.el-alert.is-dark .el-alert__description{color:#FFF}.el-alert.is-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-alert--success.is-light{background-color:#f0f9eb;color:#67C23A}.el-alert--success.is-light .el-alert__description{color:#67C23A}.el-alert--success.is-dark{background-color:#67C23A;color:#FFF}.el-alert--info.is-light{background-color:#f4f4f5;color:#909399}.el-alert--info.is-dark{background-color:#909399;color:#FFF}.el-alert--info .el-alert__description{color:#909399}.el-alert--warning.is-light{background-color:#fdf6ec;color:#E6A23C}.el-alert--warning.is-light .el-alert__description{color:#E6A23C}.el-alert--warning.is-dark{background-color:#E6A23C;color:#FFF}.el-alert--error.is-light{background-color:#fef0f0;color:#F56C6C}.el-alert--error.is-light .el-alert__description{color:#F56C6C}.el-alert--error.is-dark{background-color:#F56C6C;color:#FFF}.el-alert__content{display:table-cell;padding:0 8px}.el-alert__icon{font-size:16px;width:16px}.el-alert__icon.is-big{font-size:28px;width:28px}.el-alert__title{font-size:13px;line-height:18px}.el-alert__title.is-bold{font-weight:700}.el-alert .el-alert__description{font-size:12px;margin:5px 0 0}.el-alert__closebtn{font-size:12px;opacity:1;position:absolute;top:12px;right:15px;cursor:pointer}.el-alert__closebtn.is-customed{font-style:normal;font-size:13px;top:9px}.el-alert-fade-enter,.el-alert-fade-leave-active{opacity:0}
|
1
src/element-ui/theme/aside.css
Normal file
@ -0,0 +1 @@
|
||||
.el-aside{overflow:auto;-webkit-box-sizing:border-box;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0}
|
1
src/element-ui/theme/autocomplete.css
Normal file
1
src/element-ui/theme/avatar.css
Normal file
@ -0,0 +1 @@
|
||||
.el-avatar{display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;text-align:center;overflow:hidden;color:#fff;background:#C0C4CC;width:40px;height:40px;line-height:40px;font-size:14px}.el-avatar>img{display:block;height:100%;vertical-align:middle}.el-avatar--circle{border-radius:50%}.el-avatar--square{border-radius:4px}.el-avatar--icon{font-size:18px}.el-avatar--large{width:40px;height:40px;line-height:40px}.el-avatar--medium{width:36px;height:36px;line-height:36px}.el-avatar--small{width:28px;height:28px;line-height:28px}
|
1
src/element-ui/theme/backtop.css
Normal file
@ -0,0 +1 @@
|
||||
.el-backtop{position:fixed;background-color:#FFF;width:40px;height:40px;border-radius:50%;color:#409EFF;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;font-size:20px;-webkit-box-shadow:0 0 6px rgba(0,0,0,.12);box-shadow:0 0 6px rgba(0,0,0,.12);cursor:pointer;z-index:5}.el-backtop:hover{background-color:#F2F6FC}
|
1
src/element-ui/theme/badge.css
Normal file
@ -0,0 +1 @@
|
||||
.el-badge{position:relative;vertical-align:middle;display:inline-block}.el-badge__content{background-color:#F56C6C;border-radius:10px;color:#FFF;display:inline-block;font-size:12px;height:18px;line-height:18px;padding:0 6px;text-align:center;white-space:nowrap;border:1px solid #FFF}.el-badge__content.is-fixed{position:absolute;top:0;right:10px;-webkit-transform:translateY(-50%) translateX(100%);transform:translateY(-50%) translateX(100%)}.el-badge__content.is-fixed.is-dot{right:5px}.el-badge__content.is-dot{height:8px;width:8px;padding:0;right:0;border-radius:50%}.el-badge__content--primary{background-color:#409EFF}.el-badge__content--success{background-color:#67C23A}.el-badge__content--warning{background-color:#E6A23C}.el-badge__content--info{background-color:#909399}.el-badge__content--danger{background-color:#F56C6C}
|
1
src/element-ui/theme/base.css
Normal file
0
src/element-ui/theme/breadcrumb-item.css
Normal file
1
src/element-ui/theme/breadcrumb.css
Normal file
@ -0,0 +1 @@
|
||||
.el-breadcrumb{font-size:14px;line-height:1}.el-breadcrumb::after,.el-breadcrumb::before{display:table;content:""}.el-breadcrumb::after{clear:both}.el-breadcrumb__separator{margin:0 9px;font-weight:700;color:#C0C4CC}.el-breadcrumb__separator[class*=icon]{margin:0 6px;font-weight:400}.el-breadcrumb__item{float:left}.el-breadcrumb__inner{color:#606266}.el-breadcrumb__inner a,.el-breadcrumb__inner.is-link{font-weight:700;text-decoration:none;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1);color:#303133}.el-breadcrumb__inner a:hover,.el-breadcrumb__inner.is-link:hover{color:#409EFF;cursor:pointer}.el-breadcrumb__item:last-child .el-breadcrumb__inner,.el-breadcrumb__item:last-child .el-breadcrumb__inner a,.el-breadcrumb__item:last-child .el-breadcrumb__inner a:hover,.el-breadcrumb__item:last-child .el-breadcrumb__inner:hover{font-weight:400;color:#606266;cursor:text}.el-breadcrumb__item:last-child .el-breadcrumb__separator{display:none}
|
0
src/element-ui/theme/button-group.css
Normal file
1
src/element-ui/theme/button.css
Normal file
1
src/element-ui/theme/calendar.css
Normal file
1
src/element-ui/theme/card.css
Normal file
@ -0,0 +1 @@
|
||||
.el-card{border-radius:4px;border:1px solid #EBEEF5;background-color:#FFF;overflow:hidden;color:#303133;-webkit-transition:.3s;transition:.3s}.el-card.is-always-shadow,.el-card.is-hover-shadow:focus,.el-card.is-hover-shadow:hover{-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-card__header{padding:18px 20px;border-bottom:1px solid #EBEEF5;-webkit-box-sizing:border-box;box-sizing:border-box}.el-card__body{padding:20px}
|
1
src/element-ui/theme/carousel-item.css
Normal file
@ -0,0 +1 @@
|
||||
.el-carousel__item,.el-carousel__mask{position:absolute;height:100%;top:0;left:0}.el-carousel__item{width:100%;display:inline-block;overflow:hidden;z-index:0}.el-carousel__item.is-active{z-index:2}.el-carousel__item.is-animating{-webkit-transition:-webkit-transform .4s ease-in-out;transition:-webkit-transform .4s ease-in-out;transition:transform .4s ease-in-out;transition:transform .4s ease-in-out,-webkit-transform .4s ease-in-out}.el-carousel__item--card{width:50%;-webkit-transition:-webkit-transform .4s ease-in-out;transition:-webkit-transform .4s ease-in-out;transition:transform .4s ease-in-out;transition:transform .4s ease-in-out,-webkit-transform .4s ease-in-out}.el-carousel__item--card.is-in-stage{cursor:pointer;z-index:1}.el-carousel__item--card.is-in-stage.is-hover .el-carousel__mask,.el-carousel__item--card.is-in-stage:hover .el-carousel__mask{opacity:.12}.el-carousel__item--card.is-active{z-index:2}.el-carousel__mask{width:100%;background-color:#FFF;opacity:.24;-webkit-transition:.2s;transition:.2s}
|
1
src/element-ui/theme/carousel.css
Normal file
@ -0,0 +1 @@
|
||||
.el-carousel{position:relative}.el-carousel--horizontal{overflow-x:hidden}.el-carousel--vertical{overflow-y:hidden}.el-carousel__container{position:relative;height:300px}.el-carousel__arrow{border:none;outline:0;padding:0;margin:0;height:36px;width:36px;cursor:pointer;-webkit-transition:.3s;transition:.3s;border-radius:50%;background-color:rgba(31,45,61,.11);color:#FFF;position:absolute;top:50%;z-index:10;-webkit-transform:translateY(-50%);transform:translateY(-50%);text-align:center;font-size:12px}.el-carousel__arrow--left{left:16px}.el-carousel__arrow--right{right:16px}.el-carousel__arrow:hover{background-color:rgba(31,45,61,.23)}.el-carousel__arrow i{cursor:pointer}.el-carousel__indicators{position:absolute;list-style:none;margin:0;padding:0;z-index:2}.el-carousel__indicators--horizontal{bottom:0;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.el-carousel__indicators--vertical{right:0;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-carousel__indicators--outside{bottom:26px;text-align:center;position:static;-webkit-transform:none;transform:none}.el-carousel__indicators--outside .el-carousel__indicator:hover button{opacity:.64}.el-carousel__indicators--outside button{background-color:#C0C4CC;opacity:.24}.el-carousel__indicators--labels{left:0;right:0;-webkit-transform:none;transform:none;text-align:center}.el-carousel__indicators--labels .el-carousel__button{height:auto;width:auto;padding:2px 18px;font-size:12px}.el-carousel__indicators--labels .el-carousel__indicator{padding:6px 4px}.el-carousel__indicator{background-color:transparent;cursor:pointer}.el-carousel__indicator:hover button{opacity:.72}.el-carousel__indicator--horizontal{display:inline-block;padding:12px 4px}.el-carousel__indicator--vertical{padding:4px 12px}.el-carousel__indicator--vertical .el-carousel__button{width:2px;height:15px}.el-carousel__indicator.is-active button{opacity:1}.el-carousel__button{display:block;opacity:.48;width:30px;height:2px;background-color:#FFF;border:none;outline:0;padding:0;margin:0;cursor:pointer;-webkit-transition:.3s;transition:.3s}.carousel-arrow-left-enter,.carousel-arrow-left-leave-active{-webkit-transform:translateY(-50%) translateX(-10px);transform:translateY(-50%) translateX(-10px);opacity:0}.carousel-arrow-right-enter,.carousel-arrow-right-leave-active{-webkit-transform:translateY(-50%) translateX(10px);transform:translateY(-50%) translateX(10px);opacity:0}
|
1
src/element-ui/theme/cascader-panel.css
Normal file
1
src/element-ui/theme/cascader.css
Normal file
0
src/element-ui/theme/checkbox-button.css
Normal file
0
src/element-ui/theme/checkbox-group.css
Normal file
1
src/element-ui/theme/checkbox.css
Normal file
1
src/element-ui/theme/col.css
Normal file
0
src/element-ui/theme/collapse-item.css
Normal file
1
src/element-ui/theme/collapse.css
Normal file
1
src/element-ui/theme/color-picker.css
Normal file
1
src/element-ui/theme/container.css
Normal file
@ -0,0 +1 @@
|
||||
.el-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-flex:1;-ms-flex:1;flex:1;-ms-flex-preferred-size:auto;flex-basis:auto;-webkit-box-sizing:border-box;box-sizing:border-box;min-width:0}.el-container.is-vertical{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}
|
1
src/element-ui/theme/date-picker.css
Normal file
1
src/element-ui/theme/descriptions-item.css
Normal file
@ -0,0 +1 @@
|
||||
.el-descriptions-item__container{display:-webkit-box;display:-ms-flexbox;display:flex}.el-descriptions-item__label.has-colon::after{content:':';position:relative;top:-.5px}.el-descriptions-item__label.is-bordered-label{font-weight:700;color:#909399;background:#fafafa}.el-descriptions-item__label:not(.is-bordered-label){margin-right:10px}
|
1
src/element-ui/theme/descriptions.css
Normal file
@ -0,0 +1 @@
|
||||
.el-descriptions-item__container{display:-webkit-box;display:-ms-flexbox;display:flex}.el-descriptions-item__label.has-colon::after{content:':';position:relative;top:-.5px}.el-descriptions-item__label.is-bordered-label{font-weight:700;color:#909399;background:#fafafa}.el-descriptions-item__label:not(.is-bordered-label){margin-right:10px}.el-descriptions{-webkit-box-sizing:border-box;box-sizing:border-box;font-size:14px;color:#303133}.el-descriptions__header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin-bottom:20px}.el-descriptions__title{font-size:16px;font-weight:700}.el-descriptions--mini,.el-descriptions--small{font-size:12px}.el-descriptions__body{color:#606266;background-color:#FFF}.el-descriptions__body table{border-collapse:collapse;width:100%;table-layout:fixed}.el-descriptions__body table td,.el-descriptions__body table th{-webkit-box-sizing:border-box;box-sizing:border-box;text-align:left;font-weight:400;line-height:1.5}.el-descriptions__body table td.is-left,.el-descriptions__body table th.is-left{text-align:left}.el-descriptions__body table td.is-center,.el-descriptions__body table th.is-center{text-align:center}.el-descriptions__body table td.is-right,.el-descriptions__body table th.is-right{text-align:right}.el-descriptions .is-bordered{table-layout:auto}.el-descriptions .is-bordered td,.el-descriptions .is-bordered th{border:1px solid #EBEEF5;padding:12px 10px}.el-descriptions :not(.is-bordered) td,.el-descriptions :not(.is-bordered) th{padding-bottom:12px}.el-descriptions--medium.is-bordered td,.el-descriptions--medium.is-bordered th{padding:10px}.el-descriptions--medium:not(.is-bordered) td,.el-descriptions--medium:not(.is-bordered) th{padding-bottom:10px}.el-descriptions--small.is-bordered td,.el-descriptions--small.is-bordered th{padding:8px 10px}.el-descriptions--small:not(.is-bordered) td,.el-descriptions--small:not(.is-bordered) th{padding-bottom:8px}.el-descriptions--mini.is-bordered td,.el-descriptions--mini.is-bordered th{padding:6px 10px}.el-descriptions--mini:not(.is-bordered) td,.el-descriptions--mini:not(.is-bordered) th{padding-bottom:6px}
|
1
src/element-ui/theme/dialog.css
Normal file
@ -0,0 +1 @@
|
||||
.v-modal-enter{-webkit-animation:v-modal-in .2s ease;animation:v-modal-in .2s ease}.v-modal-leave{-webkit-animation:v-modal-out .2s ease forwards;animation:v-modal-out .2s ease forwards}@-webkit-keyframes v-modal-in{0%{opacity:0}}@keyframes v-modal-in{0%{opacity:0}}@-webkit-keyframes v-modal-out{100%{opacity:0}}@keyframes v-modal-out{100%{opacity:0}}.v-modal{position:fixed;left:0;top:0;width:100%;height:100%;opacity:.5;background:#000}.el-popup-parent--hidden{overflow:hidden}.el-dialog{position:relative;margin:0 auto 50px;background:#FFF;border-radius:2px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.3);box-shadow:0 1px 3px rgba(0,0,0,.3);-webkit-box-sizing:border-box;box-sizing:border-box;width:50%}.el-dialog.is-fullscreen{width:100%;margin-top:0;margin-bottom:0;height:100%;overflow:auto}.el-dialog__wrapper{position:fixed;top:0;right:0;bottom:0;left:0;overflow:auto;margin:0}.el-dialog__header{padding:20px 20px 10px}.el-dialog__headerbtn{position:absolute;top:20px;right:20px;padding:0;background:0 0;border:none;outline:0;cursor:pointer;font-size:16px}.el-dialog__headerbtn .el-dialog__close{color:#909399}.el-dialog__headerbtn:focus .el-dialog__close,.el-dialog__headerbtn:hover .el-dialog__close{color:#409EFF}.el-dialog__title{line-height:24px;font-size:18px;color:#303133}.el-dialog__body{padding:30px 20px;color:#606266;font-size:14px;word-break:break-all}.el-dialog__footer{padding:10px 20px 20px;text-align:right;-webkit-box-sizing:border-box;box-sizing:border-box}.el-dialog--center{text-align:center}.el-dialog--center .el-dialog__body{text-align:initial;padding:25px 25px 30px}.el-dialog--center .el-dialog__footer{text-align:inherit}.dialog-fade-enter-active{-webkit-animation:dialog-fade-in .3s;animation:dialog-fade-in .3s}.dialog-fade-leave-active{-webkit-animation:dialog-fade-out .3s;animation:dialog-fade-out .3s}@-webkit-keyframes dialog-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes dialog-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes dialog-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes dialog-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}
|
1
src/element-ui/theme/display.css
Normal file
@ -0,0 +1 @@
|
||||
@media only screen and (max-width:767px){.hidden-xs-only{display:none!important}}@media only screen and (min-width:768px){.hidden-sm-and-up{display:none!important}}@media only screen and (min-width:768px) and (max-width:991px){.hidden-sm-only{display:none!important}}@media only screen and (max-width:991px){.hidden-sm-and-down{display:none!important}}@media only screen and (min-width:992px){.hidden-md-and-up{display:none!important}}@media only screen and (min-width:992px) and (max-width:1199px){.hidden-md-only{display:none!important}}@media only screen and (max-width:1199px){.hidden-md-and-down{display:none!important}}@media only screen and (min-width:1200px){.hidden-lg-and-up{display:none!important}}@media only screen and (min-width:1200px) and (max-width:1919px){.hidden-lg-only{display:none!important}}@media only screen and (max-width:1919px){.hidden-lg-and-down{display:none!important}}@media only screen and (min-width:1920px){.hidden-xl-only{display:none!important}}
|
1
src/element-ui/theme/divider.css
Normal file
@ -0,0 +1 @@
|
||||
.el-divider{background-color:#DCDFE6;position:relative}.el-divider--horizontal{display:block;height:1px;width:100%;margin:24px 0}.el-divider--vertical{display:inline-block;width:1px;height:1em;margin:0 8px;vertical-align:middle;position:relative}.el-divider__text{position:absolute;background-color:#FFF;padding:0 20px;font-weight:500;color:#303133;font-size:14px}.el-divider__text.is-left{left:20px;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-divider__text.is-center{left:50%;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%)}.el-divider__text.is-right{right:20px;-webkit-transform:translateY(-50%);transform:translateY(-50%)}
|
1
src/element-ui/theme/drawer.css
Normal file
0
src/element-ui/theme/dropdown-item.css
Normal file
0
src/element-ui/theme/dropdown-menu.css
Normal file
1
src/element-ui/theme/dropdown.css
Normal file
1
src/element-ui/theme/empty.css
Normal file
@ -0,0 +1 @@
|
||||
.el-empty{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;padding:40px 0}.el-empty__image{width:160px}.el-empty__image img,.el-empty__image svg{width:100%;height:100%;vertical-align:top}.el-empty__image img{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-o-object-fit:contain;object-fit:contain}.el-empty__image svg{fill:#DCDDE0}.el-empty__description{margin-top:20px}.el-empty__description p{margin:0;font-size:14px;color:#909399}.el-empty__bottom{margin-top:20px}
|
BIN
src/element-ui/theme/fonts/element-icons.ttf
Normal file
BIN
src/element-ui/theme/fonts/element-icons.woff
Normal file
1
src/element-ui/theme/footer.css
Normal file
@ -0,0 +1 @@
|
||||
.el-footer{padding:0 20px;-webkit-box-sizing:border-box;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0}
|
0
src/element-ui/theme/form-item.css
Normal file
1
src/element-ui/theme/form.css
Normal file
@ -0,0 +1 @@
|
||||
.el-form--inline .el-form-item,.el-form--inline .el-form-item__content{display:inline-block;vertical-align:top}.el-form-item::after,.el-form-item__content::after{clear:both}.el-form--label-left .el-form-item__label{text-align:left}.el-form--label-top .el-form-item__label{float:none;display:inline-block;text-align:left;padding:0 0 10px}.el-form--inline .el-form-item{margin-right:10px}.el-form--inline .el-form-item__label{float:none;display:inline-block}.el-form--inline.el-form--label-top .el-form-item__content{display:block}.el-form-item{margin-bottom:22px}.el-form-item::after,.el-form-item::before{display:table;content:""}.el-form-item .el-form-item{margin-bottom:0}.el-form-item--mini.el-form-item,.el-form-item--small.el-form-item{margin-bottom:18px}.el-form-item .el-input__validateIcon{display:none}.el-form-item--medium .el-form-item__content,.el-form-item--medium .el-form-item__label{line-height:36px}.el-form-item--small .el-form-item__content,.el-form-item--small .el-form-item__label{line-height:32px}.el-form-item--small .el-form-item__error{padding-top:2px}.el-form-item--mini .el-form-item__content,.el-form-item--mini .el-form-item__label{line-height:28px}.el-form-item--mini .el-form-item__error{padding-top:1px}.el-form-item__label-wrap{float:left}.el-form-item__label-wrap .el-form-item__label{display:inline-block;float:none}.el-form-item__label{text-align:right;vertical-align:middle;float:left;font-size:14px;color:#606266;line-height:40px;padding:0 12px 0 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-form-item__content{line-height:40px;position:relative;font-size:14px}.el-form-item__content::after,.el-form-item__content::before{display:table;content:""}.el-form-item__content .el-input-group{vertical-align:top}.el-form-item__error{color:#F56C6C;font-size:12px;line-height:1;padding-top:4px;position:absolute;top:100%;left:0}.el-form-item__error--inline{position:relative;top:auto;left:auto;display:inline-block;margin-left:10px}.el-form-item.is-required:not(.is-no-asterisk) .el-form-item__label-wrap>.el-form-item__label:before,.el-form-item.is-required:not(.is-no-asterisk)>.el-form-item__label:before{content:'*';color:#F56C6C;margin-right:4px}.el-form-item.is-error .el-input__inner,.el-form-item.is-error .el-input__inner:focus,.el-form-item.is-error .el-textarea__inner,.el-form-item.is-error .el-textarea__inner:focus{border-color:#F56C6C}.el-form-item.is-error .el-input-group__append .el-input__inner,.el-form-item.is-error .el-input-group__prepend .el-input__inner{border-color:transparent}.el-form-item.is-error .el-input__validateIcon{color:#F56C6C}.el-form-item--feedback .el-input__validateIcon{display:inline-block}
|
1
src/element-ui/theme/header.css
Normal file
@ -0,0 +1 @@
|
||||
.el-header{padding:0 20px;-webkit-box-sizing:border-box;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0}
|
1
src/element-ui/theme/icon.css
Normal file
1
src/element-ui/theme/image.css
Normal file
@ -0,0 +1 @@
|
||||
.el-image__error,.el-image__placeholder{background:#F5F7FA}.el-image__error,.el-image__inner,.el-image__placeholder{width:100%;height:100%}.el-image{position:relative;display:inline-block;overflow:hidden}.el-image__inner{vertical-align:top}.el-image__inner--center{position:relative;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);display:block}.el-image__error{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;font-size:14px;color:#C0C4CC;vertical-align:middle}.el-image__preview{cursor:pointer}.el-image-viewer__wrapper{position:fixed;top:0;right:0;bottom:0;left:0}.el-image-viewer__btn{position:absolute;z-index:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;border-radius:50%;opacity:.8;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.el-image-viewer__close{top:40px;right:40px;width:40px;height:40px;font-size:24px;color:#fff;background-color:#606266}.el-image-viewer__canvas{width:100%;height:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-image-viewer__actions{left:50%;bottom:30px;-webkit-transform:translateX(-50%);transform:translateX(-50%);width:282px;height:44px;padding:0 23px;background-color:#606266;border-color:#fff;border-radius:22px}.el-image-viewer__actions__inner{width:100%;height:100%;text-align:justify;cursor:default;font-size:23px;color:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-pack:distribute;justify-content:space-around}.el-image-viewer__next,.el-image-viewer__prev{top:50%;width:44px;height:44px;font-size:24px;color:#fff;background-color:#606266;border-color:#fff}.el-image-viewer__prev{-webkit-transform:translateY(-50%);transform:translateY(-50%);left:40px}.el-image-viewer__next{-webkit-transform:translateY(-50%);transform:translateY(-50%);right:40px;text-indent:2px}.el-image-viewer__mask{position:absolute;width:100%;height:100%;top:0;left:0;opacity:.5;background:#000}.viewer-fade-enter-active{-webkit-animation:viewer-fade-in .3s;animation:viewer-fade-in .3s}.viewer-fade-leave-active{-webkit-animation:viewer-fade-out .3s;animation:viewer-fade-out .3s}@-webkit-keyframes viewer-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes viewer-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes viewer-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes viewer-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}
|