Compare commits

...

91 Commits

Author SHA1 Message Date
Aresn 4f5d88d83b
update Sponsors 2019-04-26 10:37:19 +08:00
Aresn c8a051da23
update sponsors 2019-03-22 19:19:20 +08:00
Aresn ade4e82369
Merge pull request #1205 from baiyutang/master
fix wrongly written character avator
2019-03-07 11:25:46 +08:00
baiyutang d5193dc265 avator => avatar 2019-02-22 21:24:45 +08:00
Aresn 5a866808b3
update sponsor info 2019-02-21 14:47:18 +08:00
Aresn 1fc57cf030
update sponsors info 2019-02-18 15:58:29 +08:00
Aresn 43af3929aa
Update README.md 2019-02-06 09:20:25 +08:00
Aresn a76a3b65c9
Update README.md 2019-02-01 08:40:39 +08:00
Aresn cc302124a3
add base template 2019-01-29 17:56:56 +08:00
梁灏 32722d4382 update social page 2019-01-28 16:05:11 +08:00
梁灏 722fc36b7f remove package-lock, close #1183 #1182 #1184 2019-01-28 15:31:35 +08:00
Aresn 35aca729f0
Update README.md 2019-01-28 15:00:26 +08:00
Aresn 24c58b9d7a
Update README.md 2019-01-28 14:59:32 +08:00
Lison e1e0dc0b3e
Update README.md 2019-01-25 11:28:49 +08:00
Lison b904d7f497
Update README.md 2019-01-25 11:28:14 +08:00
zhigang.li 456acef2d6 Merge branch '2.0' 2019-01-25 11:25:23 +08:00
zhigang.li 1b5c9a49c8 update 2019-01-25 11:25:03 +08:00
zhigang.li 3c5880a201 Merge branch '2.0' 2019-01-24 16:51:53 +08:00
zhigang.li bfb8f3543a style 2019-01-24 16:51:40 +08:00
zhigang.li f1737683df 修复直接输入url跳转没有新增标签的bug 2019-01-24 16:51:25 +08:00
zhigang.li 6a24b17c6b 删除无用文件 2019-01-24 16:42:06 +08:00
zhigang.li 2592e3b43a style 2019-01-24 16:34:34 +08:00
zhigang.li 91f2362629 新增tree-select 2019-01-24 16:30:49 +08:00
zhigang.li 65da8286d7 增加tree-select组件 2019-01-24 16:29:14 +08:00
zhigang.li 362f0827e0 Merge branch '2.0' 2019-01-04 15:11:09 +08:00
zhigang.li 07726d1ec8 修复标签栏右键关闭菜单被覆盖的bug 2019-01-04 15:10:47 +08:00
zhigang.li 9257201a83 Merge branch '2.0' 2019-01-04 15:06:30 +08:00
zhigang.li 8165cbd697 修复关闭其他标签没有滑到可视区域的bug 2019-01-04 15:06:02 +08:00
zhigang.li 16a44053c8 Merge branch '2.0' 2019-01-04 14:46:45 +08:00
zhigang.li 8f48604956 修复点击关闭所以报错的bug 2019-01-04 14:46:33 +08:00
zhigang.li 1124878133 Merge branch '2.0' 2018-12-27 15:20:26 +08:00
zhigang.li 0ee24af85e 修复多级页面关闭后再打开还缓存的bug 2018-12-27 15:20:13 +08:00
zhigang.li 0b58996a4d Merge branch '2.0' 2018-12-24 10:38:40 +08:00
zhigang.li c336aef320 修复组织结构树在非内容区域也能拖动结构树的bug 2018-12-24 10:38:16 +08:00
zhigang.li dde4576bf2 Merge branch '2.0' 2018-12-21 13:46:14 +08:00
zhigang.li 666639083d 更新README 2018-12-21 13:45:57 +08:00
zhigang.li 7a75e24849 Merge branch '2.0' 2018-12-21 13:36:48 +08:00
zhigang.li 11209b0ada 增加可拖动抽屉组件drag-drawer 2018-12-21 13:32:47 +08:00
zhigang.li 96d4982c94 Merge branch '2.0' 2018-12-20 19:08:11 +08:00
zhigang.li e78f77fc76 新增组织结构树页面 2018-12-20 19:07:47 +08:00
zhigang.li aa52d9559b Update package.json 2018-12-17 20:08:46 +08:00
lison16 c2d3e0be50 commit 2018-12-17 20:07:31 +08:00
zhigang.li 1c5d33bdcd Update vue.config.js 2018-12-14 16:22:03 +08:00
zhigang.li ec949dace3 Merge branch '2.0' 2018-12-06 11:30:29 +08:00
zhigang.li 5e345e02f5 lint 2018-12-06 11:29:21 +08:00
zhigang.li fc8cf99e59 Merge branch '2.0' 2018-12-06 10:53:49 +08:00
zhigang.li 7c7fb7e73e update 2018-12-06 10:53:32 +08:00
zhigang.li c85574b795 Merge branch '2.0' 2018-11-28 11:34:23 +08:00
zhigang.li 42c153ff74 在动态路由匹配和带参路由匹配页面增加closeTag关闭页面示例 2018-11-28 11:33:44 +08:00
zhigang.li 17b9ef22aa Merge branch '2.0' 2018-11-21 18:37:23 +08:00
zhigang.li 3ec36b6b8e 更新tree-table-vue版本,更改了个别api命名 2018-11-21 18:36:48 +08:00
zhigang.li 0f6f038d49 修复三级菜单之间切换页面无法缓存的bug 2018-11-21 18:36:25 +08:00
zhigang.li 037c8e820d Merge branch '2.0' 2018-11-20 18:17:01 +08:00
zhigang.li 6106e500f4 优化api请求的错误收集 2018-11-20 18:16:47 +08:00
zhigang.li 2c08ea160f Merge branch '2.0' 2018-11-19 17:30:46 +08:00
zhigang.li 8552ce1267 增加clipboard指令 2018-11-19 17:30:29 +08:00
zhigang.li 37e29bc4f7 Merge branch '2.0' 2018-11-19 16:31:43 +08:00
zhigang.li 228cd68748 config/index.js添加配置浏览器标签显示的基本标题title的配置,会拼接为 '${title} - ${当前页标题}' 2018-11-19 16:31:19 +08:00
zhigang.li a1803b0995 Merge branch '2.0' 2018-11-19 12:05:23 +08:00
zhigang.li e15918c532 修复富文本编辑器由于内部z-index设置过高,遮住iview模态框的问题 2018-11-19 12:05:03 +08:00
zhigang.li a30a6e55a6 Merge branch '2.0' 2018-11-19 11:50:37 +08:00
zhigang.li 853f3d3fb8 增加ABackTop组件,支持在自定义滚动容器 2018-11-19 11:50:04 +08:00
Lison 590aa7677c
Merge pull request #1096 from iview/2.0
update baseUrl
2018-11-16 17:29:37 +08:00
zhigang.li 39e8fa75f6 update baseUrl 2018-11-16 16:55:09 +08:00
zhigang.li c9fb7f42ea Merge branch '2.0' 2018-11-16 16:46:02 +08:00
zhigang.li 387b0b354d 优化消息中心数据获取 2018-11-16 16:45:41 +08:00
zhigang.li 22ceb98ab4 udpate 2018-11-16 16:31:48 +08:00
zhigang.li bd4ffc49c8 Merge branch '2.0' 2018-11-16 16:30:23 +08:00
zhigang.li e33acdc8d2 新增树状表格组件 2018-11-16 16:29:45 +08:00
Lison cb88361a0d
Merge pull request #1092 from Baoyx007/patch-3
Update error-content.vue
2018-11-16 10:42:48 +08:00
Haven 855d0fe02a
fix(error-page): add missing event for back 2018-11-16 10:27:35 +08:00
Haven 12f29b7d16
Update error-content.vue 2018-11-15 17:32:05 +08:00
zhigang.li f4f4a82dcf Merge branch '2.0'
修复多语言路由配置,当title的回调函数返回一个不带{{}}的字符串时title被多语言配置的字符串覆盖的bug
2018-11-14 18:41:56 +08:00
zhigang.li 56742b707e 修复多语言路由配置bug 2018-11-14 18:40:53 +08:00
zhigang.li@tendcloud.com 59a72ae1c0 Merge branch '2.0'
去掉main.js里重复引入的iview样式文件
2018-11-13 14:51:14 +08:00
zhigang.li@tendcloud.com 21b2c15577 去掉main.js里重复引入的iview样式文件 2018-11-13 14:50:39 +08:00
zhigang.li@tendcloud.com ac71d28739 Merge branch '2.0'
新增消息中心
2018-11-08 15:15:53 +08:00
zhigang.li@tendcloud.com a72be82a9b 新增消息中心 2018-11-08 15:15:25 +08:00
zhigang.li@tendcloud.com 585c4028f5 Merge branch '2.0'
fix error word
2018-11-08 10:20:32 +08:00
zhigang.li@tendcloud.com e82724c16f fix error word 2018-11-08 10:19:24 +08:00
zhigang.li@tendcloud.com ae5edc07c5 Merge branch '2.0'
添加图片裁剪组件Cropper
2018-11-07 17:00:39 +08:00
zhigang.li@tendcloud.com afdbeacac2 新增图片裁剪组件Cropper 2018-11-07 17:00:09 +08:00
zhigang.li@tendcloud.com f1feef19ed commit 2018-11-07 10:29:24 +08:00
zhigang.li@tendcloud.com 70fe4ee16a Merge branch '2.0' 2018-11-06 17:55:01 +08:00
zhigang.li@tendcloud.com a7b7a20d33 修复多功能表格组件在未开启搜索功能时报错的bug 2018-11-06 17:53:38 +08:00
zhigang.li@tendcloud.com b5b0b0c123 Merge branch '2.0' 2018-11-05 14:33:57 +08:00
zhigang.li@tendcloud.com 612abab7df 补充notCache注释 2018-11-05 14:33:28 +08:00
zhigang.li@tendcloud.com 5b085204f3 Merge branch '2.0' 2018-11-05 10:11:43 +08:00
zhigang.li@tendcloud.com dc302d0f12 修复页面缓存不起作用的bug 2018-11-05 10:04:38 +08:00
zhigang.li@tendcloud.com 6ac6bf5644 Merge branch '2.0' 2018-11-02 14:45:06 +08:00
zhigang.li@tendcloud.com 9c6f2ee1f2 修复收缩菜单的多语言显示 2018-11-02 14:44:45 +08:00
91 changed files with 2875 additions and 15538 deletions

View File

@ -9,8 +9,11 @@ module.exports = {
'generator-star-spacing': 'off',
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }],
'no-undef': 'off'
'vue/no-parsing-error': [2, {
'x-invalid-end-tag': false
}],
'no-undef': 'off',
'camelcase': 'off'
},
parserOptions: {
parser: 'babel-eslint'

2
.gitignore vendored
View File

@ -2,6 +2,8 @@
node_modules
/dist
package-lock.json
/tests/e2e/videos/
/tests/e2e/screenshots/

242
README.md
View File

@ -4,183 +4,111 @@
</a>
</p>
# iView Admin
<h1>
iView Admin
<h3>Vue.js 2.0 admin management system template based on iView.</h3>
</h1>
[![](https://img.shields.io/github/release/iview/iview-admin.svg)](https://github.com/iview/iview-admin/releases)
[![](https://img.shields.io/travis/iview/iview-admin.svg?style=flat-square)](https://travis-ci.org/iview/iview-admin)
[![vue](https://img.shields.io/badge/vue-2.5.10-brightgreen.svg?style=flat-square)](https://github.com/vuejs/vue)
[![iview ui](https://img.shields.io/badge/iview-3.0.0-brightgreen.svg?style=flat-square)](https://github.com/iview/iview)
[![vue](https://img.shields.io/badge/vue-2.5.17-brightgreen.svg?style=flat-square)](https://github.com/vuejs/vue)
[![iview ui](https://img.shields.io/badge/iview-3.2.2-brightgreen.svg?style=flat-square)](https://github.com/iview/iview)
[![npm](https://img.shields.io/npm/l/express.svg)]()
[更新日志](https://github.com/iview/iview-admin/releases)
<h2 align="center">Special Sponsors</h2>
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://segmentfault.com/ls/1650000016424063" target="_blank">
<img width="300" src="https://file.iviewui.com/asd/asd-i-2.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://e.coding.net/?utm_source=iview" target="_blank">
<img width="300" src="https://file.iviewui.com/asd/asd-coding4.png">
</a>
</td>
<td align="center" valign="middle">
<a href="https://cn.udacity.com/fend/?utm_source=iviewui&utm_medium=banner&utm_campaign=fend" target="_blank">
<img width="300" src="https://file.iviewui.com/asd/asd-u-new-2.png">
</a>
</td>
</tr>
</tbody>
</table>
[使用文档](https://lison16.github.io/iview-admin-doc/#/)
> If you'd like be a sponsor, to show your ads in GitHub and iView doc, please email admin@aresn.com to get more infomation.
[在线访问](https://admin.iviewui.com/)
## Introduction
[简化版模板](https://github.com/iview/iview-admin/tree/template)
iView Admin is a front-end management background integration solution. It based on [Vue.js](https://github.com/vuejs/vue) and use the UI Toolkit [iView](https://github.com/iview/iview).
[教学视频(26课时)](https://segmentfault.com/ls/1650000016221751?utm_source=banner)
- [Document](https://lison16.github.io/iview-admin-doc/)
- [Preview](https://admin.iviewui.com/)
- [Base template recommends using](https://github.com/iview/iview-admin/tree/template)
`注在线版本会在开发版本新小版本发布后更新到相应版本所以如果想体验最新版本iview-admin请clone完整项目代码到本地运行。`
![image](https://file.iviewui.com/admin-dist/admin-preview.png)
## Install
## Features
- Login / Logout
- Permission Authentication
- A list of filters
- Permission to switch
- i18n
- Components
- Rich Text Editor
- Markdown Editor
- City Cascader
- Photos preview and edit
- Draggable list
- File upload
- Digital gradient
- split-pane
- Form
- The article published
- Workflow
- Table
- Drag-and-drop sort
- Searchable form
- Table export data
- Export to Csv file
- Export to Xls file
- Table to picture
- Error Page
- 403
- 404
- 500
- Router
- Dynamic routing
- With reference page
- Theme
- Shrink the sidebar
- Tag navigation
- Breadcrumb navigation
- Full screen / exit full screen
- Lock screen
- The message center
- Personal center
## Getting started
```bush
# clone the project
git clone https://github.com/iview/iview-admin.git
// install dependencies
npm install
```
## Run
### Development
```bush
// develop
npm run dev
```
### Production(Build)
## Build
```bush
npm run build
```
## 简介
&emsp;&emsp;iView admin是基于Vue.js搭配使用[iView](https://www.iviewui.com) UI组件库形成的一套后台集成解决方案由TalkingData前端可视化团队部分成员开发维护。iView admin遵守iView设计和开发约定风格统一设计考究并且更多功能在不停开发中。
如果您想查看iview-admin的更新动态您可以到[更新日志](https://github.com/iview/iview-admin/releases)查看了解最新更新如果您是新手想快速入手iview-admin您可以到[使用教程](https://github.com/iview/iview-admin/wiki)查看讲解如果您想在线体验iview-admin您可以到[在线访问](https://admin.iviewui.com/)体验。如果你只是想要一个清醒爽朗的界面,那你可以下载[简化版模板](https://github.com/iview/iview-admin/tree/template)来做开发。
## 功能
- 登录/登出
- 权限管理
- 列表过滤
- 权限切换
- 多语言切换
- 组件
- 富文本编辑器
- Markdown编辑器
- 城市级联
- 图片预览编辑
- 可拖拽列表
- 文件上传
- 数字渐变
- split-pane
- 表单编辑
- 文章发布
- 工作流
- 表格
- 可拖拽排序
- 可编辑表格
- 行内编辑
- 单元格编辑
- 可搜索表格
- 表格导出数据
- 导出为Csv文件
- 导出为Xls文件
- 表格转图片
- 错误页面
- 403页面
- 404页面
- 500页面
- 高级路由
- 动态路由
- 带参页面
- 换肤
- 收缩侧边栏
- tag标签导航
- 面包屑导航
- 全屏/退出全屏
- 锁屏
- 消息中心
- 个人中心
## 文件结构
```shell
.
├── build 项目构建配置
├── config 开发相关配置
├── public 打包所需静态资源
└── src
├── api AJAX请求
└── assets 项目静态资源
├── icons 自定义图标资源
└── images 图片资源
├── components 业务组件
├── config 项目运行配置
├── directive 自定义指令
├── libs 封装工具函数
├── locale 多语言文件
├── mock mock模拟数据
├── router 路由配置
├── store Vuex配置
├── view 页面文件
└── tests 测试相关
```
## Links
- [TalkingData](https://github.com/TalkingData)
- [iView](https://github.com/iview/iview)
- [Vue](https://github.com/vuejs/vue)
- [Webpack](https://github.com/webpack/webpack)
## 效果展示
- 响应式布局首页
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/home.gif)
- 标签导航
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/page-tags.gif)
- 权限管理
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/access.gif)
- 可拖拽列表
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/dragable-list.gif)
- 图片预览编辑
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/image-editor.gif)
- 文件上传
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/upload.gif)
- 数字渐变
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/count-to.gif)
- split-pane
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/split-pane.gif)
- 文章发布
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/article-publish.gif)
- 工作流
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/workflow.gif)
- 可拖拽表格
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/dragable-table.gif)
- 可编辑表格
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/editable-table.gif)
- 表格导出数据
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/exportable-table.gif)
- 表格转图片
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/table2image.gif)
- 错误页面
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/error-page.gif)
- 锁屏
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/locking.gif)
- 可收缩侧边栏
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/sidebarmenu.gif)
- 主题切换
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/theme.gif)
- 消息中心
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/message.gif)
### 💖💖 If you find this project helpful, maybe you can buy me a coffee. 💖💖
![image](https://github.com/iview/iview-admin/raw/dev/github-gif/code.png)
## License
[MIT](http://opensource.org/licenses/MIT)

15178
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "iview-admin",
"version": "2.0.0",
"author": "Lison<lison.modern@gmail.com>",
"author": "Lison<lison16new@163.com>",
"private": false,
"scripts": {
"dev": "vue-cli-service serve --open",
@ -19,11 +19,13 @@
"dayjs": "^1.7.7",
"echarts": "^4.0.4",
"html2canvas": "^1.0.0-alpha.12",
"iview": "^3.1.3",
"iview": "^3.2.2",
"iview-area": "^1.5.17",
"js-cookie": "^2.2.0",
"simplemde": "^1.11.2",
"sortablejs": "^1.7.0",
"tree-table-vue": "^1.1.0",
"v-org-tree": "^1.0.6",
"vue": "^2.5.10",
"vue-i18n": "^7.8.0",
"vue-router": "^3.0.1",

View File

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>iview-admin</title>
<title></title>
</head>
<body>
<noscript>

View File

@ -28,3 +28,24 @@ export const saveErrorLogger = info => {
method: 'post'
})
}
export const uploadImg = formData => {
return axios.request({
url: 'image/upload',
data: formData
})
}
export const getOrgData = () => {
return axios.request({
url: 'get_org_data',
method: 'get'
})
}
export const getTreeSelectData = () => {
return axios.request({
url: 'get_tree_select_data',
method: 'get'
})
}

View File

@ -28,3 +28,57 @@ export const logout = (token) => {
method: 'post'
})
}
export const getUnreadCount = () => {
return axios.request({
url: 'message/count',
method: 'get'
})
}
export const getMessage = () => {
return axios.request({
url: 'message/init',
method: 'get'
})
}
export const getContentByMsgId = msg_id => {
return axios.request({
url: 'message/content',
method: 'get',
params: {
msg_id
}
})
}
export const hasRead = msg_id => {
return axios.request({
url: 'message/has_read',
method: 'post',
data: {
msg_id
}
})
}
export const removeReaded = msg_id => {
return axios.request({
url: 'message/remove_readed',
method: 'post',
data: {
msg_id
}
})
}
export const restoreTrash = msg_id => {
return axios.request({
url: 'message/restore',
method: 'post',
data: {
msg_id
}
})
}

View File

@ -1,10 +1,10 @@
@font-face {font-family: "iconfont";
src: url('iconfont.eot?t=1530874958372'); /* IE9*/
src: url('iconfont.eot?t=1530874958372#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff'),
url('iconfont.ttf?t=1530874958372') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('iconfont.svg?t=1530874958372#iconfont') format('svg'); /* iOS 4.1- */
src: url('iconfont.eot?t=1541579316141'); /* IE9*/
src: url('iconfont.eot?t=1541579316141#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAiEAAsAAAAADmgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFY8eUnXY21hcAAAAYAAAACjAAACLi+YJuBnbHlmAAACJAAABAgAAAcg4dRWHmhlYWQAAAYsAAAAMQAAADYTL8piaGhlYQAABmAAAAAgAAAAJAfdA4xobXR4AAAGgAAAABQAAAAsLAD//2xvY2EAAAaUAAAAGAAAABgImgpGbWF4cAAABqwAAAAfAAAAIAEcAG5uYW1lAAAGzAAAAUUAAAJtPlT+fXBvc3QAAAgUAAAAbgAAAI54roygeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BkYWCcwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGByeMTx/ytzwv4EhhrmBoRkozAiSAwDuUwzMeJzlkUEKwkAMRd/YabXFhQvxFF6qPYPrUujGY7jyIr1JoZNjtMnEhag3MOEN5MMk8D9QAoVyVSKEJwGrh6oh6wVN1iM3nc+cVImJVKdOehlklElmWdYVstp+ql8VdIv15a1NLW0zFXsO7Kjz3erH/3+rY37vr6kxnx1LKNWOJZlaxxJNnWOpSu+ot8jgqMvI6KjfyOSo88jsaAbI4tBsig89rQB4nLVUTWwbRRSeNzO767i2g7N/FP9s7MRrE5ON4/V6rSZyU0PiINSSNImES4IUoapWz6hEiqiMBDQqEojkAkiFStyKRC+9VSoFCeUEyqESVUAqEkcu3OAQb3hrJxAXwSGI3X0/szPz5vvm2x0i7O/vf8IJe5VkSJnUyUtklRBQJE1VIjRtUafkmk6pSu2ipleh4+xikkKxSksWTUeo8m8NoagpYtoslTmxrLl37z64e33esuJjU8P5Wd262LxoPVnPZ06Pxfe+C0YjkhSJygPhQCA8ABPOykwuN7NyuRvgUnAgLEnhATkaCQQiUe/7XKUyV6nQz+t2o7l66+rs7NVbq82GXTdrdjxjRGU5amTids2bUDMFtzCsqsMYMqr3IDY6OT05GjsI8Exv/6CSkOWEQigh+y3clxY5QVTcEZFIGtHLxDUJs6WsHR1y9SFKdr1HggCp3V1ICYL36OOpVmvKN9bC1u6R3vZ0qwWtVovgJfqOfUvfIYxIWL+fyETHNVJqSkIT1JTjW8ZWh3yDJDz0ctvsyt51etvrg9/QHhqGlzMM+vbmizPnDWPLMNbW19e7tffvsBzL99aWEfBRY46t+tbe3PypXv/IMDYN43WsQBe9HL2NC33RuxABrPsG+xH3o4bVRE2KgCRqulbWNf8W/UYVHM129aKra24VshZkq+CWD/Oy6Xt8cGYEthgHVlVliCfynAlqjo6oysTKlYUAD4docMI5/1ZioN+GwZNBcTwWUmTdBUqhTwX29QebXzF4An4JJMzwfMl+WQ01+IlQZVR4yhie53ycA16pOI/ODiYNGK4MChdCgXNnX5gIJXPCSYnf2OF850aQ+zJIyOs+u8+mMO8jQdwtg1TIWVRjKAnFcslMi8KfGUPoSUCergUyUk77dMyS69Ms6tijKZKYwUGKbpfdzu+iYeZYAHMFiOVi+MD7h9mb99qC0L7X8c+XatMfTj97KZ5IxJt/pd43tYYQKEjAnXMOB6kQEBrwg+LPjindAPOHNdC3q3ait0I3/ZIunZEARLNYNEUA6czSP3N/7j9wz6ZESdX0VNl1zGNS/szbQaQSIGk4DtVPcZf8AgXpf9A2OyTit5s2syZmand46bhEe2WtodLHkvaoqtTXuXN2/c42WADP9HGfbUcUW7JgqHss4xHtlMys679FqUomdP9VJBQBdnlPABBubpuNwqnmQj6/0HwNQzKxDUJFgKiXurBG6dqFjmeBzsvtRPJgGIZThYa5fdOvsReOticPh6JHHXxsv7ItJpOniYPYsmZ/x0QD/o5P105DeQwF6MH33ogoLi+KQp7zpY3HQV5bFMURzheXeds7gpP+jKNXljjHuYvXHke7cdCxLLZf6YX7B63UcCV4nGNgZGBgAOKAN2ZR8fw2Xxm4WRhA4AbHYRMY/f///1oWBuYGIJeDgQkkCgAvWgs2AAAAeJxjYGRgYG7438AQw8Lw/z8DAwsDA1AEBXADAHXiBHJ4nGNhYGBgYfj/nwVM48cATwECKwAAAAAAjAC6AOgBFAGAAf4CbgLqAzgDkHicY2BkYGDgZkhiYGcAASYg5gJCBob/YD4DABOmAYsAeJxlj01OwzAQhV/6B6QSqqhgh+QFYgEo/RGrblhUavdddN+mTpsqiSPHrdQDcB6OwAk4AtyAO/BIJ5s2lsffvHljTwDc4Acejt8t95E9XDI7cg0XuBeuU38QbpBfhJto41W4Rf1N2MczpsJtdGF5g9e4YvaEd2EPHXwI13CNT+E69S/hBvlbuIk7/Aq30PHqwj7mXle4jUcv9sdWL5xeqeVBxaHJIpM5v4KZXu+Sha3S6pxrW8QmU4OgX0lTnWlb3VPs10PnIhVZk6oJqzpJjMqt2erQBRvn8lGvF4kehCblWGP+tsYCjnEFhSUOjDFCGGSIyujoO1Vm9K+xQ8Jee1Y9zed0WxTU/3OFAQL0z1xTurLSeTpPgT1fG1J1dCtuy56UNJFezUkSskJe1rZUQuoBNmVXjhF6XNGJPyhnSP8ACVpuyAAAAHicbYhdDoIwEAb3a6k/YIIX8VArWewmdJFWJOnpJTG+OQ+TzJCjLy39p4ODR4OAA4444YwWHS7U3IVzn6Voldtb8ksHnvohrlqjjmw1rmzXsvdT7fEbblnCmOfNfJIYStJJfGIL27yb6AOCGR89AAA=') format('woff'),
url('iconfont.ttf?t=1541579316141') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('iconfont.svg?t=1541579316141#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
@ -19,6 +19,10 @@
.icon-resize-vertical:before { content: "\e7c3"; }
.icon-chuizhifanzhuan:before { content: "\e661"; }
.icon-shuipingfanzhuan:before { content: "\e662"; }
.icon-qq:before { content: "\e609"; }
.icon-frown:before { content: "\e77e"; }

Binary file not shown.

View File

@ -20,19 +20,18 @@ Created by iconfont
/>
<missing-glyph />
<glyph glyph-name="x" unicode="x" horiz-adv-x="1001"
d="M281 543q-27 -1 -53 -1h-83q-18 0 -36.5 -6t-32.5 -18.5t-23 -32t-9 -45.5v-76h912v41q0 16 -0.5 30t-0.5 18q0 13 -5 29t-17 29.5t-31.5 22.5t-49.5 9h-133v-97h-438v97zM955 310v-52q0 -23 0.5 -52t0.5 -58t-10.5 -47.5t-26 -30t-33 -16t-31.5 -4.5q-14 -1 -29.5 -0.5
t-29.5 0.5h-32l-45 128h-439l-44 -128h-29h-34q-20 0 -45 1q-25 0 -41 9.5t-25.5 23t-13.5 29.5t-4 30v167h911zM163 247q-12 0 -21 -8.5t-9 -21.5t9 -21.5t21 -8.5q13 0 22 8.5t9 21.5t-9 21.5t-22 8.5zM316 123q-8 -26 -14 -48q-5 -19 -10.5 -37t-7.5 -25t-3 -15t1 -14.5
t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q-5 19 -11 39h-368zM336 498v228q0 11 2.5 23t10 21.5t20.5 15.5t34 6h188q31 0 51.5 -14.5t20.5 -52.5v-227h-327z" />
<glyph glyph-name="bear" unicode="&#58880;" d="M1024 683.008q0-70.656-46.08-121.856 46.08-89.088 46.08-193.536 0-96.256-39.936-181.248t-109.568-147.968-162.816-99.328-199.68-36.352-199.68 36.352-162.304 99.328-109.568 147.968-40.448 181.248q0 104.448 46.08 193.536-46.08 51.2-46.08 121.856 0 37.888 13.824 71.168t37.376 58.368 55.808 39.424 68.096 14.336q43.008 0 78.848-18.432t59.392-50.176q46.08 17.408 96.256 26.624t102.4 9.216 102.4-9.216 96.256-26.624q24.576 31.744 59.904 50.176t78.336 18.432q36.864 0 68.608-14.336t55.296-39.424 37.376-58.368 13.824-71.168zM205.824 268.288q10.24 0 18.944 10.24t15.36 28.672 10.24 42.496 3.584 51.712-3.584 51.712-10.24 41.984-15.36 28.16-18.944 10.24q-9.216 0-17.92-10.24t-15.36-28.16-10.752-41.984-4.096-51.712 4.096-51.712 10.752-42.496 15.36-28.672 17.92-10.24zM512-31.744000000000028q53.248 0 99.84 13.312t81.408 35.84 54.784 52.736 19.968 65.024q0 33.792-19.968 64t-54.784 52.736-81.408 35.84-99.84 13.312-99.84-13.312-81.408-35.84-54.784-52.736-19.968-64q0-34.816 19.968-65.024t54.784-52.736 81.408-35.84 99.84-13.312zM818.176 268.288q10.24 0 18.944 10.24t15.36 28.672 10.24 42.496 3.584 51.712-3.584 51.712-10.24 41.984-15.36 28.16-18.944 10.24q-9.216 0-17.92-10.24t-15.36-28.16-10.752-41.984-4.096-51.712 4.096-51.712 10.752-42.496 15.36-28.672 17.92-10.24zM512 235.51999999999998q39.936 0 68.096-9.728t28.16-24.064-28.16-24.064-68.096-9.728-68.096 9.728-28.16 24.064 28.16 24.064 68.096 9.728z" horiz-adv-x="1024" />
<glyph glyph-name="resize-vertical" unicode="&#59331;" d="M512 896C229.248 896 0 666.752 0 384s229.248-512 512-512 512 229.248 512 512S794.752 896 512 896zM576 192l64 0-128-128-128 128 64 0L448 576l-64 0 128 128 128-128-64 0L576 192z" horiz-adv-x="1024" />
<glyph glyph-name="chuizhifanzhuan" unicode="&#58977;" d="M286.01856 645.08416l472.4224 0 0-146.2784-472.4224 0 0 146.2784ZM87.19872 420.37248l885.80096 0 0-70.87104-885.80096 0 0 70.87104ZM773.55008 268.05248l0-31.0016L270.6688 237.05088l0 31.0016L773.55008 268.05248zM773.55008 121.4208l0-31.0016L270.6688 90.4192l0 31.0016L773.55008 121.4208zM742.54848 240.75776l31.0016 0 0-123.04896-31.0016 0L742.54848 240.75776zM270.70464 240.57856l31.0016 0 0-123.04896-31.0016 0L270.70464 240.57856z" horiz-adv-x="1024" />
<glyph glyph-name="shuipingfanzhuan" unicode="&#58978;" d="M252.76928 596.096l146.2784 0 0-472.42752-146.2784 0 0 472.42752ZM477.48096 810.65472l70.87104 0 0-885.80608-70.87104 0 0 885.80608ZM629.80096 611.2l31.0016 0 0-502.88128-31.0016 0L629.80096 611.2zM776.42752 611.2l31.0016 0 0-502.88128-31.0016 0L776.42752 611.2zM657.09056 580.1984l0 31.0016 123.04896 0 0-31.0016L657.09056 580.1984zM657.27488 108.35456l0 31.0016 123.04896 0 0-31.0016L657.27488 108.35456z" horiz-adv-x="1024" />
<glyph glyph-name="qq" unicode="&#58889;" d="M147.372058 491.394284c-5.28997-13.909921 2.431986-22.698872 0-75.732573-0.682996-14.25092-62.165649-78.762555-86.569511-145.791177-24.192863-66.517625-27.519845-135.978232 9.811944-163.285078 37.419789-27.305846 72.191593 90.879487 76.757567 73.685584 1.961989-7.509958 4.436975-15.317914 7.423958-23.338868a331.945126 331.945126 0 0 1 61.140655-101.162429c5.929967-6.783962-36.009797-19.199892-61.140655-61.99365-25.173858-42.751759 7.209959-120.49032 132.223254-120.49032 161.27909 0 197.288886 56.70368 200.574868 56.447681 12.031932-0.895995 12.841928 0 25.599855 0 15.572912 0 9.129948-1.279993 23.593867 0 7.807956 0.682996 86.186514-67.839617 194.686901-56.447681 184.873956 19.45589 156.586116 81.40754 142.079198 120.48932-15.103915 40.83277-68.692612 59.946662-66.303626 62.549647 44.28775 48.938724 51.285711 79.018554 66.346626 123.9463 6.143965 18.473896 49.066723-101.674426 82.089537-73.685584 13.781922 11.690934 41.301767 60.24566 13.781922 163.285078-27.519845 102.996419-80.767544 126.505286-79.615551 145.791177 2.389987 40.191773 1.023994 68.436614-1.023994 75.732573-9.812945 35.4128-30.378829 27.604844-30.378829 35.4128C858.450044 730.752933 705.10691 896 515.966978 896s-342.398067-165.289067-342.398068-369.192916c0-16.169909-14.378919-4.223976-26.154852-35.4128z" horiz-adv-x="1024" />

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1547543883733" class="icon" style="" viewBox="0 0 1272 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16831" xmlns:xlink="http://www.w3.org/1999/xlink" width="496.875" height="400"><defs><style type="text/css"></style></defs><path d="M729.64116345 165.27693991L634.32650881 90.125l-99.5625 78.52693991-5.17887981 4.16056009 104.74137981 83.50215546 105.09051682-83.50215546-9.77586218-7.53556009z m361.21228445 291.47198236l-456.78879245 360.19396555-456.49784537-359.99030128L110.125 511.12715547l523.93965546 413.11745671 524.23060335-413.35021555-67.44181091-54.14547436z m-456.78879245 29.21120673L385.4784479 290.00646554 318.06573237 344.12284454l315.96982771 249.16810336 316.28987101-249.40086136-67.41271555-54.14547436-248.84806008 196.21551682z" fill="#006cff" p-id="16832"></path></svg>

After

Width:  |  Height:  |  Size: 955 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1547543874130" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16498" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M511.8 0.6C229.2 0.6 0.1 229.7 0.1 512.3S229.2 1024 511.8 1024s511.7-229.1 511.7-511.7S794.4 0.6 511.8 0.6z m264.9 375.3c-0.1 0.1-0.1 0.2-0.2 0.3h0.2c-9 14.3-21.2 28.8-34.2 39.2-5.2 4.2-10.5 8.3-15.7 12.5v0.5c0.3 22.9-0.3 44.9-4.7 64.2-25.2 113.1-91.9 189.9-197.4 222.8-37.9 11.8-99.1 16.7-142.6 5.9-21.5-5.4-41-11.4-59.3-19.3-10.2-4.5-19.6-9.3-28.5-14.7l-8.8-5.3h0.5l-0.5-0.3c9.8 0.2 21.3 2.9 32.2 1.2 9.8-1.6 19.6-1.2 28.7-3.2 22.7-5 43-11.6 60.4-21.8 8.3-4.8 20.9-10.6 27-17.6-11.2 0.2-21.4-2.4-29.7-5.4-32.6-11.5-51.6-32.7-63.9-64.4h0.1c0-0.1-0.1-0.2-0.1-0.3 9.7 1.1 37.3 3.5 44.6-1.7-12.3-0.8-24.1-7.9-32.5-13.2-26.2-16.4-47.6-43.9-47.4-86.2v-0.3c3.5 1.7 6.9 3.3 10.4 4.9 6.6 2.8 13.3 4.3 21.1 5.9 3.1 0.7 9.3 2.4 13.2 1.4-5.1-5.8-13.2-9.7-18.4-16-14.7-18.3-28.7-45.3-25.2-77.4 0.5-4.7 1.3-9.4 2.6-14.3 2.5-9.7 6.5-18.3 10.8-26.2 0.2 0.1 0.4 0.2 0.5 0.3 2 4.2 6.4 7.2 9.1 10.6 8.6 10.7 19.2 20.3 30 28.7 36.7 28.7 69.9 46.4 123.1 59.5 13.5 3.3 29 5.9 45.1 5.9-1.9-5.8-2.7-13-2.7-20.5 0-9.7 1.3-19.6 3.3-26.8 9-32.1 28.5-55.2 57-67.6 6.9-3 14.4-5.2 22.4-6.9 4.1-0.6 8.2-1.1 12.3-1.6 39-0.7 59.8 13.5 79.6 31.6 16.8-1.4 38.7-10.8 51.6-17.4l12.6-6.9c0 0.1-0.1 0.3-0.1 0.4l0.1-0.1c-7.3 19.9-17.4 35.5-32.7 47.3-3.1 2.4-6.3 5.6-10 7.4 21.5-0.4 39.3-10 56.1-15.4v0.3z" fill="#2EB1EB" p-id="16499"></path><path d="M719.7 391.1s0.1 0 0.1-0.1c0 0-0.1 0-0.1 0.1zM726.8 428.4v-0.5 0.5zM336.4 479.9c3.3 0.7 9.9 2.7 13.8 1.2h-0.5l-0.1-0.1c-3.9 1-10.1-0.7-13.2-1.4-7.8-1.6-14.5-3.1-21.1-5.9-3.5-1.6-6.9-3.2-10.4-4.9v0.3c3.4 1.6 6.9 3.2 10.4 4.9 6.6 2.8 13.3 4.3 21.1 5.9zM719.6 391.4v0.2c21.9-0.2 39.8-10.1 56.9-15.4 0.1-0.1 0.1-0.2 0.2-0.3v-0.3c-16.9 5.3-34.6 14.9-56.1 15.4-0.4 0.1-0.7 0.3-1 0.4zM584.8 337.5c6.9-3 14.4-5.1 22.4-6.9 4.1-0.6 8.2-1 12.3-1.6 39-0.7 59.8 13.5 79.6 31.6 16.8-1.4 38.7-10.8 51.6-17.4 4.2-2.3 8.3-4.5 12.5-6.8 0-0.1 0.1-0.3 0.1-0.4l-12.6 6.9c-12.9 6.6-34.8 16-51.6 17.4-19.8-18.1-40.6-32.3-79.6-31.6-4.1 0.5-8.2 1-12.3 1.6-8 1.7-15.5 3.9-22.4 6.9-28.5 12.4-48 35.5-57 67.6-2 7.2-3.4 17.2-3.3 26.8 0-9.6 1.3-19.4 3.3-26.5 9-32.1 28.5-55.2 57-67.6zM385.2 568.5h-0.4c-7.3 5.2-34.9 2.8-44.6 1.7 0 0.1 0.1 0.2 0.1 0.3 10 1.1 38.2 3.6 44.9-2zM319.4 347.4c0.1 0.1 0.3 0.1 0.5 0.3 2 4.1 6.3 7.1 9.1 10.6 8.6 10.6 19.2 20.2 30 28.7 36.8 28.7 69.9 46.4 123.1 59.5 13.5 3.3 29.1 5.9 45.2 5.9 0-0.1-0.1-0.2-0.1-0.3-16.1 0-31.6-2.6-45.1-5.9-53.2-13.1-86.4-30.8-123.1-59.5-10.8-8.4-21.4-18-30-28.7-2.7-3.4-7.1-6.4-9.1-10.6-0.1-0.1-0.3-0.2-0.5-0.3-4.3 7.9-8.3 16.5-10.8 26.2-1.3 4.9-2.1 9.6-2.6 14.3 0.5-4.6 1.3-9.2 2.6-14 2.5-9.7 6.5-18.3 10.8-26.2zM317.7 683.2c9.9-1.6 19.6-1.2 28.7-3.2 22.8-5 43-11.6 60.4-21.8 8.4-4.9 21.3-10.8 27.3-17.9h-0.3c-6.1 7-18.7 12.8-27 17.6-17.4 10.2-37.7 16.8-60.4 21.8-9.1 2-18.9 1.6-28.7 3.2-10.9 1.7-22.4-1-32.2-1.2l0.5 0.3c9.7 0.4 21 2.9 31.7 1.2z" fill="#FFFFFF" p-id="16500"></path></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1547543863835" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16165" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400"><defs><style type="text/css"></style></defs><path d="M512 1024C229.236364 1024 0 794.763636 0 512S229.236364 0 512 0s512 229.236364 512 512-229.236364 512-512 512z m-129.861818-756.48s-36.212364 2.094545-48.989091 24.482909c-12.8 22.365091-54.318545 137.378909-54.318546 137.378909s13.847273 6.376727 37.28291-10.658909c23.435636-17.035636 30.882909-46.848 30.882909-46.848l42.589091-2.117818 1.070545 121.390545s-73.495273-1.070545-88.413091 0c-14.894545 1.047273-23.412364 40.448-23.412364 40.448h111.825455s-9.588364 67.095273-38.353455 116.084364c-28.741818 48.989091-83.060364 87.319273-83.060363 87.319273s39.424 15.965091 77.730909-6.4c38.353455-22.341818 66.629818-120.692364 66.629818-120.692364l89.925818 110.056727s8.192-52.386909-1.466182-67.188363c-9.658182-14.778182-62.208-74.286545-62.208-74.286546l-22.946909 20.247273 16.337455-65.117091h97.954909s0-38.353455-19.153455-40.494545c-19.176727-2.094545-78.801455 0-78.801454 0V371.898182h88.389818s-1.070545-39.400727-18.106182-39.400727h-143.755636l22.341818-64.954182z m169.984 61.184v358.562909h36.002909l13.102545 45.009455 63.348364-45.009455h89.064727V328.704h-201.518545z" fill="#0f84fd" p-id="16166"></path><path d="M594.781091 368.64h117.899636v277.876364h-41.890909l-53.364363 40.261818-11.636364-40.261818h-11.008V368.64z" fill="#0f84fd" p-id="16167"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

View File

@ -6,7 +6,7 @@
import Icons from '_c/icons'
export default {
name: 'CommonIcon',
components: {Icons},
components: { Icons },
props: {
type: {
type: String,

View File

@ -0,0 +1,2 @@
import Cropper from './index.vue'
export default Cropper

View File

@ -0,0 +1,35 @@
.bg{
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC")
}
.cropper-wrapper{
width: 600px;
height: 340px;
.img-box{
height: 340px;
width: 430px;
border: 1px solid #ebebeb;
display: inline-block;
.bg;
img{
max-width: 100%;
display: block;
}
}
.right-con{
display: inline-block;
width: 170px;
vertical-align: top;
box-sizing: border-box;
padding: 0 10px;
.preview-box{
height: 150px !important;
width: 100% !important;
overflow: hidden;
border: 1px solid #ebebeb;
.bg;
}
.button-box{
padding: 10px 0 0;
}
}
}

View File

@ -0,0 +1,139 @@
<template>
<div class="cropper-wrapper">
<div class="img-box">
<img class="cropper-image" :id="imgId" alt="">
</div>
<div class="right-con">
<div v-if="preview" class="preview-box" :id="previewId"></div>
<div class="button-box">
<slot>
<Upload action="image/upload" :before-upload="beforeUpload">
<Button style="width: 150px;" type="primary">上传图片</Button>
</Upload>
</slot>
<div v-show="insideSrc">
<Button type="primary" @click="rotate">
<Icon type="md-refresh" :size="18"/>
</Button>
<Button type="primary" @click="shrink">
<Icon type="md-remove" :size="18"/>
</Button>
<Button type="primary" @click="magnify">
<Icon type="md-add" :size="18"/>
</Button>
<Button type="primary" @click="scale('X')">
<Icon custom="iconfont icon-shuipingfanzhuan" :size="18"/>
</Button>
<Button type="primary" @click="scale('Y')">
<Icon custom="iconfont icon-chuizhifanzhuan" :size="18"/>
</Button>
<Button type="primary" @click="move(0, -moveStep)">
<Icon type="md-arrow-round-up" :size="18"/>
</Button>
<Button type="primary" @click="move(-moveStep, 0)">
<Icon type="md-arrow-round-back" :size="18"/>
</Button>
<Button type="primary" @click="move(0, moveStep)">
<Icon type="md-arrow-round-down" :size="18"/>
</Button>
<Button type="primary" @click="move(moveStep, 0)">
<Icon type="md-arrow-round-forward" :size="18"/>
</Button>
<Button style="width: 150px;margin-top: 10px;" type="primary" @click="crop">{{ cropButtonText }}</Button>
</div>
</div>
</div>
</div>
</template>
<script>
import Cropper from 'cropperjs'
import './index.less'
import 'cropperjs/dist/cropper.min.css'
export default {
name: 'Cropper',
props: {
src: {
type: String,
default: ''
},
preview: {
type: Boolean,
default: true
},
moveStep: {
type: Number,
default: 4
},
cropButtonText: {
type: String,
default: '裁剪'
}
},
data () {
return {
cropper: null,
insideSrc: ''
}
},
computed: {
imgId () {
return `cropper${this._uid}`
},
previewId () {
return `cropper_preview${this._uid}`
}
},
watch: {
src (src) {
this.replace(src)
},
insideSrc (src) {
this.replace(src)
}
},
methods: {
beforeUpload (file) {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = (event) => {
this.insideSrc = event.srcElement.result
}
return false
},
replace (src) {
this.cropper.replace(src)
this.insideSrc = src
},
rotate () {
this.cropper.rotate(90)
},
shrink () {
this.cropper.zoom(-0.1)
},
magnify () {
this.cropper.zoom(0.1)
},
scale (d) {
this.cropper[`scale${d}`](-this.cropper.getData()[`scale${d}`])
},
move (...argu) {
this.cropper.move(...argu)
},
crop () {
this.cropper.getCroppedCanvas().toBlob(blob => {
this.$emit('on-crop', blob)
})
}
},
mounted () {
this.$nextTick(() => {
let dom = document.getElementById(this.imgId)
this.cropper = new Cropper(dom, {
preview: `#${this.previewId}`,
checkCrossOrigin: true
})
})
}
}
</script>

View File

@ -0,0 +1,18 @@
<template>
<div :class="`${prefix}-move-trigger`">
<div :class="`${prefix}-move-trigger-point`">
<i></i><i></i><i></i><i></i><i></i>
</div>
</div>
</template>
<script>
import Mixin from './mixin'
export default {
name: 'DragDrawerTrigger',
mixins: [Mixin]
}
</script>
<style>
</style>

View File

@ -0,0 +1,156 @@
<template>
<Drawer ref="drawerWrapper"
:value="value"
@input="handleInput"
:width="width"
:class-name="outerClasses"
v-bind="$attrs"
v-on="$listeners">
<!-- 所有插槽内容显示在这里 -->
<template v-for="(slots, slotsName) in $slots">
<template v-if="slotsName !== 'default'">
<render-dom v-for="(render, index) in slots"
:key="`b_drawer_${slotsName}_${index}`"
:render="() => render"
:slot="slotsName">
</render-dom>
</template>
<template v-else>
<div :class="`${prefix}-body-wrapper`"
:key="`b_drawer_${slotsName}`">
<render-dom v-for="(render, index) in slots"
:key="`b_drawer_${slotsName}_${index}`"
:render="() => render"
:slot="slotsName">
</render-dom>
</div>
</template>
</template>
<!-- 所有插槽内容显示在这里 -->
<div v-if="draggable"
:style="triggerStyle"
:class="`${prefix}-trigger-wrapper`"
@mousedown="handleTriggerMousedown">
<slot name="trigger">
<drag-drawer-trigger></drag-drawer-trigger>
</slot>
</div>
<div v-if="$slots.footer"
:class="`${prefix}-footer`">
<slot name="footer"></slot>
</div>
</Drawer>
</template>
<script>
import RenderDom from '@/libs/render-dom'
import DragDrawerTrigger from './drag-drawer-trigger.vue'
import Mixin from './mixin'
import { on, off } from '@/libs/tools'
import './index.less'
export default {
name: 'BDrawer',
components: {
RenderDom,
DragDrawerTrigger
},
mixins: [Mixin],
props: {
value: {
type: Boolean,
default: false
},
width: {
type: [String, Number],
default: 256
},
//
draggable: {
type: Boolean,
default: false
},
//
minWidth: {
type: [String, Number],
default: 256
}
},
data () {
return {
canMove: false,
wrapperWidth: 0,
wrapperLeft: 0
}
},
computed: {
outerClasses () {
const classesArray = [
`${this.prefix}-wrapper`,
this.canMove ? 'no-select pointer-events-none' : ''
]
return classesArray.join(' ')
},
placement () {
return this.$attrs.placement
},
innerWidth () {
const width = this.width
return width <= 100 ? (this.wrapperWidth * width) / 100 : width
},
triggerStyle () {
return {
[this.placement]: `${this.innerWidth}px`,
position: this.$attrs.inner ? 'absolute' : 'fixed'
}
}
},
methods: {
handleInput (status) {
this.$emit('input', status)
},
handleTriggerMousedown (event) {
this.canMove = true
this.$emit('on-resize-start')
// trigger
window.getSelection().removeAllRanges()
},
handleMousemove (event) {
if (!this.canMove) return
// window0
this.setWrapperWidth()
const left = event.pageX - this.wrapperLeft
// left
let width = this.placement === 'right' ? this.wrapperWidth - left : left
//
width = Math.max(width, parseFloat(this.minWidth))
event.atMin = width === parseFloat(this.minWidth)
// width100
if (width <= 100) width = (width / this.wrapperWidth) * 100
this.$emit('update:width', parseInt(width))
this.$emit('on-resize', event)
},
handleMouseup (event) {
this.canMove = false
this.$emit('on-resize-end')
},
setWrapperWidth () {
const {
width,
left
} = this.$refs.drawerWrapper.$el.getBoundingClientRect()
this.wrapperWidth = width
this.wrapperLeft = left
}
},
mounted () {
on(document, 'mousemove', this.handleMousemove)
on(document, 'mouseup', this.handleMouseup)
this.setWrapperWidth()
},
beforeDestroy () {
off(document, 'mousemove', this.handleMousemove)
off(document, 'mouseup', this.handleMouseup)
}
}
</script>

View File

@ -0,0 +1,2 @@
import DragDrawer from './drag-drawer.vue'
export default DragDrawer

View File

@ -0,0 +1,70 @@
@prefix: ~"drag-drawer";
@drag-drawer-trigger-height: 100px;
@drag-drawer-trigger-width: 8px;
.@{prefix}-wrapper{
&.no-select{
user-select: none;
}
&.pointer-events-none{
pointer-events: none;
& .@{prefix}-trigger-wrapper{
pointer-events: all;
}
}
.ivu-drawer{
&-header{
overflow: hidden !important;
box-sizing: border-box;
}
&-body{
padding: 0;
overflow: visible;
position: static;
display: flex;
flex-direction: column;
}
}
.@{prefix}-body-wrapper{
width: 100%;
height: 100%;
padding: 16px;
overflow: auto;
}
.@{prefix}-trigger-wrapper{
top: 0;
height: 100%;
width: 0;
.@{prefix}-move-trigger{
position: absolute;
top: 50%;
height: @drag-drawer-trigger-height;
width: @drag-drawer-trigger-width;
background: rgb(243, 243, 243);
transform: translate(-50%, -50%);
border-radius: ~"4px / 6px";
box-shadow: 0 0 1px 1px rgba(0, 0, 0, .2);
line-height: @drag-drawer-trigger-height;
cursor: col-resize;
&-point{
display: inline-block;
width: 50%;
transform: translateX(50%);
i{
display: block;
border-bottom: 1px solid rgb(192, 192, 192);
padding-bottom: 2px;
}
}
}
}
.@{prefix}-footer{
flex-grow: 1;
width: 100%;
bottom: 0;
left: 0;
border-top: 1px solid #e8e8e8;
padding: 10px 16px;
background: #fff;
}
}

View File

@ -0,0 +1,7 @@
export default {
data () {
return {
prefix: 'drag-drawer'
}
}
}

View File

@ -68,6 +68,8 @@ export default {
}
</script>
<style>
<style lang="less">
.editor-wrapper *{
z-index: 100 !important;
}
</style>

View File

@ -0,0 +1,2 @@
import ABackTop from './index.vue'
export default ABackTop

View File

@ -0,0 +1,90 @@
<template>
<div :class="classes" :style="styles" @click="back">
<slot>
<div :class="innerClasses">
<i class="ivu-icon ivu-icon-ios-arrow-up"></i>
</div>
</slot>
</div>
</template>
<script>
import { scrollTop } from '@/libs/util'
import { on, off } from '@/libs/tools'
const prefixCls = 'ivu-back-top'
export default {
name: 'ABackTop',
props: {
height: {
type: Number,
default: 400
},
bottom: {
type: Number,
default: 30
},
right: {
type: Number,
default: 30
},
duration: {
type: Number,
default: 1000
},
container: {
type: null,
default: window
}
},
data () {
return {
backTop: false
}
},
mounted () {
// window.addEventListener('scroll', this.handleScroll, false)
// window.addEventListener('resize', this.handleScroll, false)
on(this.containerEle, 'scroll', this.handleScroll)
on(this.containerEle, 'resize', this.handleScroll)
},
beforeDestroy () {
// window.removeEventListener('scroll', this.handleScroll, false)
// window.removeEventListener('resize', this.handleScroll, false)
off(this.containerEle, 'scroll', this.handleScroll)
off(this.containerEle, 'resize', this.handleScroll)
},
computed: {
classes () {
return [
`${prefixCls}`,
{
[`${prefixCls}-show`]: this.backTop
}
]
},
styles () {
return {
bottom: `${this.bottom}px`,
right: `${this.right}px`
}
},
innerClasses () {
return `${prefixCls}-inner`
},
containerEle () {
return this.container === window ? window : document.querySelector(this.container)
}
},
methods: {
handleScroll () {
this.backTop = this.containerEle.scrollTop >= this.height
},
back () {
let target = typeof this.container === 'string' ? this.containerEle : (document.documentElement || document.body)
const sTop = target.scrollTop
scrollTop(this.containerEle, sTop, 0, this.duration)
this.$emit('on-click')
}
}
}
</script>

View File

@ -16,7 +16,7 @@
<div class="menu-collapsed" v-show="collapsed" :list="menuList">
<template v-for="item in menuList">
<collapsed-menu v-if="item.children && item.children.length > 1" @on-click="handleSelect" hide-title :root-icon-size="rootIconSize" :icon-size="iconSize" :theme="theme" :parent-item="item" :key="`drop-menu-${item.name}`"></collapsed-menu>
<Tooltip transfer v-else :content="(item.meta && item.meta.title) || (item.children && item.children[0] && item.children[0].meta.title)" placement="right" :key="`drop-menu-${item.name}`">
<Tooltip transfer v-else :content="showTitle(item.children && item.children[0] ? item.children[0] : item)" placement="right" :key="`drop-menu-${item.name}`">
<a @click="handleSelect(getNameOrHref(item, true))" class="drop-menu-a" :style="{textAlign: 'center'}"><common-icon :size="rootIconSize" :color="textColor" :type="item.icon || (item.children && item.children[0].icon)"/></a>
</Tooltip>
</template>

View File

@ -71,10 +71,10 @@
margin: 0;
padding: 5px 0;
background: #fff;
z-index: 100;
z-index: 1000;
list-style-type: none;
border-radius: 4px;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .1);
li {
margin: 0;
padding: 5px 15px;

View File

@ -116,7 +116,7 @@ export default {
let res = this.list.filter(item => routeEqual(this.currentRouteObj, item) || item.name === this.$config.homeName)
this.$emit('on-close', res, 'others', this.currentRouteObj)
setTimeout(() => {
this.getTagElementByName(this.currentRouteObj.name)
this.getTagElementByRoute(this.currentRouteObj)
}, 100)
}
},
@ -160,7 +160,7 @@ export default {
this.tagBodyLeft = -(tag.offsetLeft - (outerWidth - this.outerPadding - tag.offsetWidth))
}
},
getTagElementByName (route) {
getTagElementByRoute (route) {
this.$nextTick(() => {
this.refsTag = this.$refs.tagsPageOpened
this.refsTag.forEach((item, index) => {
@ -186,7 +186,7 @@ export default {
},
watch: {
'$route' (to) {
this.getTagElementByName(to)
this.getTagElementByRoute(to)
},
visible (value) {
if (value) {
@ -198,7 +198,7 @@ export default {
},
mounted () {
setTimeout(() => {
this.getTagElementByName(this.$route)
this.getTagElementByRoute(this.$route)
}, 200)
}
}

View File

@ -1,9 +1,12 @@
.user{
&-avator-dropdown{
&-avatar-dropdown{
cursor: pointer;
display: inline-block;
// height: 64px;
vertical-align: middle;
// line-height: 64px;
.ivu-badge-dot{
top: 16px;
}
}
}

View File

@ -1,9 +1,14 @@
<template>
<div class="user-avator-dropdown">
<div class="user-avatar-dropdown">
<Dropdown @on-click="handleClick">
<Avatar :src="userAvator"/>
<Badge :dot="!!messageUnreadCount">
<Avatar :src="userAvatar"/>
</Badge>
<Icon :size="18" type="md-arrow-dropdown"></Icon>
<DropdownMenu slot="list">
<DropdownItem name="message">
消息中心<Badge style="margin-left: 10px" :count="messageUnreadCount"></Badge>
</DropdownItem>
<DropdownItem name="logout">退出登录</DropdownItem>
</DropdownMenu>
</Dropdown>
@ -16,23 +21,36 @@ import { mapActions } from 'vuex'
export default {
name: 'User',
props: {
userAvator: {
userAvatar: {
type: String,
default: ''
},
messageUnreadCount: {
type: Number,
default: 0
}
},
methods: {
...mapActions([
'handleLogOut'
]),
handleClick (name) {
switch (name) {
case 'logout':
logout () {
this.handleLogOut().then(() => {
this.$router.push({
name: 'login'
})
})
},
message () {
this.$router.push({
name: 'message_page'
})
},
handleClick (name) {
switch (name) {
case 'logout': this.logout()
break
case 'message': this.message()
break
}
}

View File

@ -12,7 +12,7 @@
<Layout>
<Header class="header-con">
<header-bar :collapsed="collapsed" @on-coll-change="handleCollapsedChange">
<user :user-avator="userAvator"/>
<user :message-unread-count="unreadCount" :user-avatar="userAvatar"/>
<language v-if="$config.useI18n" @on-lang-change="setLocal" style="margin-right: 10px;" :lang="local"/>
<error-store v-if="$config.plugin['error-store'] && $config.plugin['error-store'].showInHeader" :has-read="hasReadErrorPage" :count="errorCount"></error-store>
<fullscreen v-model="isFullscreen" style="margin-right: 10px;"/>
@ -27,6 +27,7 @@
<keep-alive :include="cacheList">
<router-view/>
</keep-alive>
<ABackTop :height="100" :bottom="80" :right="50" container=".content-wrapper"></ABackTop>
</Content>
</Layout>
</Content>
@ -38,11 +39,12 @@ import SideMenu from './components/side-menu'
import HeaderBar from './components/header-bar'
import TagsNav from './components/tags-nav'
import User from './components/user'
import ABackTop from './components/a-back-top'
import Fullscreen from './components/fullscreen'
import Language from './components/language'
import ErrorStore from './components/error-store'
import { mapMutations, mapActions, mapGetters } from 'vuex'
import { getNewTagList, getNextRoute, routeEqual } from '@/libs/util'
import { getNewTagList, routeEqual } from '@/libs/util'
import routers from '@/router/routers'
import minLogo from '@/assets/images/logo-min.jpg'
import maxLogo from '@/assets/images/logo.jpg'
@ -56,7 +58,8 @@ export default {
TagsNav,
Fullscreen,
ErrorStore,
User
User,
ABackTop
},
data () {
return {
@ -76,11 +79,12 @@ export default {
tagRouter () {
return this.$store.state.app.tagRouter
},
userAvator () {
return this.$store.state.user.avatorImgPath
userAvatar () {
return this.$store.state.user.avatarImgPath
},
cacheList () {
return this.tagNavList.length ? this.tagNavList.filter(item => !(item.meta && item.meta.notCache)).map(item => item.name) : []
const list = ['ParentView', ...this.tagNavList.length ? this.tagNavList.filter(item => !(item.meta && item.meta.notCache)).map(item => item.name) : []]
return list
},
menuList () {
return this.$store.getters.menuList
@ -90,6 +94,9 @@ export default {
},
hasReadErrorPage () {
return this.$store.state.app.hasReadErrorPage
},
unreadCount () {
return this.$store.state.user.unreadCount
}
},
methods: {
@ -98,10 +105,12 @@ export default {
'setTagNavList',
'addTag',
'setLocal',
'setHomeRoute'
'setHomeRoute',
'closeTag'
]),
...mapActions([
'handleLogin'
'handleLogin',
'getUnreadMessageCount'
]),
turnToPage (route) {
let { name, params, query } = {}
@ -125,12 +134,13 @@ export default {
this.collapsed = state
},
handleCloseTag (res, type, route) {
if (type !== 'others') {
if (type === 'all') {
this.turnToPage(this.$config.homeName)
} else if (routeEqual(this.$route, route)) {
if (type !== 'others') {
const nextRoute = getNextRoute(this.tagNavList, route)
this.$router.push(nextRoute)
} else {
if (routeEqual(this.$route, route)) {
this.closeTag(route)
}
}
}
this.setTagNavList(res)
@ -157,8 +167,9 @@ export default {
*/
this.setTagNavList()
this.setHomeRoute(routers)
const { name, params, query, meta } = this.$route
this.addTag({
route: this.$store.state.app.homeRoute
route: { name, params, query, meta }
})
this.setBreadCrumb(this.$route)
//
@ -169,6 +180,8 @@ export default {
name: this.$config.homeName
})
}
//
this.getUnreadMessageCount()
}
}
</script>

View File

@ -8,7 +8,7 @@
import Simplemde from 'simplemde'
import 'simplemde/dist/simplemde.min.css'
export default {
naem: 'MarkdownEditor',
name: 'MarkdownEditor',
props: {
value: {
type: String,

View File

@ -1,8 +1,21 @@
<template>
<router-view/>
<keep-alive :include="cacheList" :exclude="notCacheName">
<router-view ref="child"/>
</keep-alive>
</template>
<script>
export default {
name: 'ParentView'
name: 'ParentView',
computed: {
tagNavList () {
return this.$store.state.app.tagNavList
},
notCacheName () {
return [(this.$route.meta && this.$route.meta.notCache) ? this.$route.name : '']
},
cacheList () {
return ['ParentView', ...this.tagNavList.length ? this.tagNavList.filter(item => !(item.meta && item.meta.notCache)).map(item => item.name) : []]
}
}
}
</script>

View File

@ -176,7 +176,7 @@ export default {
'on-save-edit': (params) => {
this.value[params.row.initRowIndex][params.column.key] = this.edittingText
this.$emit('input', this.value)
this.$emit('on-save-edit', Object.assign(params, {value: this.edittingText}))
this.$emit('on-save-edit', Object.assign(params, { value: this.edittingText }))
this.edittingCellId = ''
}
}
@ -265,7 +265,7 @@ export default {
},
value (val) {
this.handleTableData()
this.handleSearch()
if (this.searchable) this.handleSearch()
}
},
mounted () {

View File

@ -0,0 +1 @@
export { default } from './tree-select.vue'

View File

@ -0,0 +1,135 @@
<template>
<Tree
:data="data"
@on-check-change="handleCheckSelect"
v-on="parent.$listeners"
v-bind="parent.$attrs"
:load-data="loadDataCallback"
show-checkbox
></Tree>
</template>
<script>
import Emitter from 'iview/src/mixins/emitter.js'
const arrayEqual = (arr1, arr2) => {
//
if (arr1.length !== arr2.length) {
return false
} else {
//
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) {
return false
}
}
return true
}
}
export default {
name: 'TreeSelectTree',
mixins: [Emitter],
props: {
data: {
type: Array,
default: () => []
},
selectedArray: {
type: Array,
default: () => []
},
loadData: Function
},
data () {
return {
flatDic: {},
checkedArray: []
}
},
inject: ['parent'],
computed: {
expandAll () {
return this.parent.$attrs['expand-all']
}
},
watch: {
data (newData, oldVal) {
this.updateFlagDic(newData)
let selectArray = []
this.selectedArray.forEach(id => {
if (id in this.flatDic) selectArray.push(id)
})
this.$emit('on-check', selectArray.map(id => this.flatDic[id]))
if (this.expandAll) this.checkData(newData, false, true)
},
selectedArray (newVal, oldVal) {
if (arrayEqual(newVal, oldVal)) return
const filtedNewVal = newVal.filter(id => id in this.flatDic)
this.$emit('on-check', filtedNewVal.map(id => this.flatDic[id]))
this.$emit('on-clear')
this.$nextTick(() => {
this.checkData(this.data, true)
})
}
},
methods: {
checkEmit (value, label) {
this.dispatch('iSelect', 'on-select-selected', {
value,
label
})
this.$emit('on-select-selected', {
value,
label
})
},
updateFlagDic (newData) {
let newFlagDic = {}
this.setFlagDic(newData, item => {
newFlagDic[item.id] = item
})
this.flatDic = newFlagDic
},
setFlagDic (data, callback) {
data.forEach(item => {
if (item.children && item.children.length) { this.setFlagDic(item.children, callback) }
callback(item)
})
},
handleCheckSelect (selectArray, selectItem) {
this.$emit('on-check', selectArray)
this.parent.$emit('on-change', selectArray)
},
checkData (data, emit, expandAll) {
data.forEach(item => {
if (this.selectedArray.includes(item.id)) {
this.$set(item, 'checked', true)
this.checkedArray.push(item)
if (emit) this.checkEmit(item.id, item.title)
} else this.$set(item, 'checked', false)
if (item.children && item.children.length) {
if (this.expandAll && expandAll) this.$set(item, 'expand', true)
this.checkData(item.children, emit, expandAll)
}
})
},
loadDataCallback (item, callback) {
this.loadData(item, data => {
return (() => {
callback(data)
this.updateFlagDic(this.data)
})(data)
})
}
},
mounted () {
this.checkData(this.data, false, true)
this.$nextTick(() => {
this.$emit('on-check', this.checkedArray)
})
}
}
</script>
<style></style>

View File

@ -0,0 +1,72 @@
<template>
<Select
ref="select"
class="tree-select"
v-bind="$attrs"
@on-change="handleChange"
multiple
>
<tree-select-tree-item
:selectedArray="value"
:data="data"
@on-clear="handleClear"
:load-data="loadData"
@on-check="handleTreeCheck"
></tree-select-tree-item>
</Select>
</template>
<script>
import Emitter from 'iview/src/mixins/emitter'
import TreeSelectTreeItem from './tree-select-tree.vue'
export default {
name: 'TreeSelect',
mixins: [Emitter],
components: {
TreeSelectTreeItem
},
props: {
value: {
type: Array,
default: () => []
},
data: {
type: Array,
default: () => []
},
loadData: Function
},
data () {
return {
isChangedByTree: true,
isInit: true
}
},
provide () {
return {
parent: this
}
},
methods: {
handleChange (selected) {
if (!this.isChangedByTree) this.$emit('input', selected)
this.isChangedByTree = false
},
handleTreeCheck (selectedArray) {
this.isChangedByTree = true
this.$emit('input', selectedArray.map(item => item.id))
},
handleClear () {
this.$refs.select.reset()
}
}
}
</script>
<style lang="less">
.tree-select {
.ivu-select-dropdown {
padding: 0 6px;
}
}
</style>

View File

@ -1,4 +1,8 @@
export default {
/**
* @description 配置显示在浏览器标签的title
*/
title: 'iView-admin',
/**
* @description token在Cookie中存储的天数默认1天
*/

View File

@ -1,45 +1,9 @@
import { on } from '@/libs/tools'
import draggable from './module/draggable'
import clipboard from './module/clipboard'
const directives = {
draggable: {
inserted: (el, binding, vnode) => {
let triggerDom = document.querySelector(binding.value.trigger)
triggerDom.style.cursor = 'move'
let bodyDom = document.querySelector(binding.value.body)
let pageX = 0
let pageY = 0
let transformX = 0
let transformY = 0
let canMove = false
const handleMousedown = e => {
let transform = /\(.*\)/.exec(bodyDom.style.transform)
if (transform) {
transform = transform[0].slice(1, transform[0].length - 1)
let splitxy = transform.split('px, ')
transformX = parseFloat(splitxy[0])
transformY = parseFloat(splitxy[1].split('px')[0])
}
pageX = e.pageX
pageY = e.pageY
canMove = true
}
const handleMousemove = e => {
let xOffset = e.pageX - pageX + transformX
let yOffset = e.pageY - pageY + transformY
if (canMove) bodyDom.style.transform = `translate(${xOffset}px, ${yOffset}px)`
}
const handleMouseup = e => {
canMove = false
}
on(triggerDom, 'mousedown', handleMousedown)
on(document, 'mousemove', handleMousemove)
on(document, 'mouseup', handleMouseup)
},
update: (el, binding, vnode) => {
if (!binding.value.recover) return
let bodyDom = document.querySelector(binding.value.body)
bodyDom.style.transform = ''
}
}
draggable,
clipboard
}
export default directives

View File

@ -10,6 +10,15 @@ const importDirective = Vue => {
* }
*/
Vue.directive('draggable', directive.draggable)
/**
* clipboard指令 v-draggable="options"
* options = {
* value: /在输入框中使用v-model绑定的值/,
* success: /复制成功后的回调/,
* error: /复制失败后的回调/
* }
*/
Vue.directive('clipboard', directive.clipboard)
}
export default importDirective

View File

@ -0,0 +1,30 @@
import Clipboard from 'clipboard'
export default {
bind: (el, binding) => {
const clipboard = new Clipboard(el, {
text: () => binding.value.value
})
el.__success_callback__ = binding.value.success
el.__error_callback__ = binding.value.error
clipboard.on('success', e => {
const callback = el.__success_callback__
callback && callback(e)
})
clipboard.on('error', e => {
const callback = el.__error_callback__
callback && callback(e)
})
el.__clipboard__ = clipboard
},
update: (el, binding) => {
el.__clipboard__.text = () => binding.value.value
el.__success_callback__ = binding.value.success
el.__error_callback__ = binding.value.error
},
unbind: (el, binding) => {
delete el.__success_callback__
delete el.__error_callback__
el.__clipboard__.destroy()
delete el.__clipboard__
}
}

View File

@ -0,0 +1,41 @@
import { on } from '@/libs/tools'
export default {
inserted: (el, binding, vnode) => {
let triggerDom = document.querySelector(binding.value.trigger)
triggerDom.style.cursor = 'move'
let bodyDom = document.querySelector(binding.value.body)
let pageX = 0
let pageY = 0
let transformX = 0
let transformY = 0
let canMove = false
const handleMousedown = e => {
let transform = /\(.*\)/.exec(bodyDom.style.transform)
if (transform) {
transform = transform[0].slice(1, transform[0].length - 1)
let splitxy = transform.split('px, ')
transformX = parseFloat(splitxy[0])
transformY = parseFloat(splitxy[1].split('px')[0])
}
pageX = e.pageX
pageY = e.pageY
canMove = true
}
const handleMousemove = e => {
let xOffset = e.pageX - pageX + transformX
let yOffset = e.pageY - pageY + transformY
if (canMove) bodyDom.style.transform = `translate(${xOffset}px, ${yOffset}px)`
}
const handleMouseup = e => {
canMove = false
}
on(triggerDom, 'mousedown', handleMousedown)
on(document, 'mousemove', handleMousemove)
on(document, 'mouseup', handleMouseup)
},
update: (el, binding, vnode) => {
if (!binding.value.recover) return
let bodyDom = document.querySelector(binding.value.body)
bodyDom.style.transform = ''
}
}

View File

@ -51,7 +51,16 @@ class HttpRequest {
return { data, status }
}, error => {
this.destroy(url)
addErrorLog(error.response)
let errorInfo = error.response
if (!errorInfo) {
const { request: { statusText, status }, config } = JSON.parse(JSON.stringify(error))
errorInfo = {
statusText,
status,
request: { responseURL: config.url }
}
}
addErrorLog(errorInfo)
return Promise.reject(error)
})
}

10
src/libs/render-dom.js Normal file
View File

@ -0,0 +1,10 @@
export default {
name: 'RenderDom',
functional: true,
props: {
render: Function
},
render: (h, ctx) => {
return ctx.props.render(h)
}
}

View File

@ -2,11 +2,12 @@ import Cookies from 'js-cookie'
// cookie保存的天数
import config from '@/config'
import { forEach, hasOneOf, objEqual } from '@/libs/tools'
const { title, cookieExpires, useI18n } = config
export const TOKEN_KEY = 'token'
export const setToken = (token) => {
Cookies.set(TOKEN_KEY, token, {expires: config.cookieExpires || 1})
Cookies.set(TOKEN_KEY, token, { expires: cookieExpires || 1 })
}
export const getToken = () => {
@ -59,8 +60,11 @@ export const getBreadCrumbList = (route, homeRoute) => {
let res = routeMetched.filter(item => {
return item.meta === undefined || !item.meta.hideInBread
}).map(item => {
let meta = {...item.meta}
if (meta.title && typeof meta.title === 'function') meta.title = meta.title(route)
let meta = { ...item.meta }
if (meta.title && typeof meta.title === 'function') {
meta.__titleIsFunction__ = true
meta.title = meta.title(route)
}
let obj = {
icon: (item.meta && item.meta.icon) || '',
name: item.name,
@ -71,16 +75,18 @@ export const getBreadCrumbList = (route, homeRoute) => {
res = res.filter(item => {
return !item.meta.hideInMenu
})
return [{...homeItem, to: homeRoute.path}, ...res]
return [{ ...homeItem, to: homeRoute.path }, ...res]
}
export const getRouteTitleHandled = (route) => {
let router = {...route}
let meta = {...route.meta}
let router = { ...route }
let meta = { ...route.meta }
let title = ''
if (meta.title) {
if (typeof meta.title === 'function') title = meta.title(router)
else title = meta.title
if (typeof meta.title === 'function') {
meta.__titleIsFunction__ = true
title = meta.title(router)
} else title = meta.title
}
meta.title = title
router.meta = meta
@ -88,10 +94,11 @@ export const getRouteTitleHandled = (route) => {
}
export const showTitle = (item, vm) => {
let title = item.meta.title
let { title, __titleIsFunction__ } = item.meta
if (!title) return
if (vm.$config.useI18n) {
if (title.includes('{{') && title.includes('}}') && vm.$config.useI18n) title = title.replace(/({{[\s\S]+?}})/, (m, str) => str.replace(/{{([\s\S]*)}}/, (m, _) => vm.$t(_.trim())))
if (useI18n) {
if (title.includes('{{') && title.includes('}}') && useI18n) title = title.replace(/({{[\s\S]+?}})/, (m, str) => str.replace(/{{([\s\S]*)}}/, (m, _) => vm.$t(_.trim())))
else if (__titleIsFunction__) title = item.meta.title
else title = vm.$t(item.name)
} else title = (item.meta && item.meta.title) || item.name
return title
@ -342,3 +349,51 @@ export const localSave = (key, value) => {
export const localRead = (key) => {
return localStorage.getItem(key) || ''
}
// scrollTop animation
export const scrollTop = (el, from = 0, to, duration = 500, endCallback) => {
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = (
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
return window.setTimeout(callback, 1000 / 60)
}
)
}
const difference = Math.abs(from - to)
const step = Math.ceil(difference / duration * 50)
const scroll = (start, end, step) => {
if (start === end) {
endCallback && endCallback()
return
}
let d = (start + step > end) ? end : start + step
if (start > end) {
d = (start - step < end) ? end : start - step
}
if (el === window) {
window.scrollTo(d, d)
} else {
el.scrollTop = d
}
window.requestAnimationFrame(() => scroll(d, end, step))
}
scroll(from, to, step)
}
/**
* @description 根据当前跳转的路由设置显示在浏览器标签的title
* @param {Object} routeItem 路由对象
* @param {Object} vm Vue实例
*/
export const setTitle = (routeItem, vm) => {
const handledRoute = getRouteTitleHandled(routeItem)
const pageTitle = showTitle(handledRoute, vm)
const resTitle = pageTitle ? `${title} - ${pageTitle}` : title
window.document.title = resTitle
}

View File

@ -1,5 +1,6 @@
export default {
home: 'Home',
login: 'Login',
components: 'Components',
count_to_page: 'Count-to',
tables_page: 'Table',
@ -21,6 +22,7 @@ export default {
level_2_3: 'Level-2-3',
level_2_2: 'Level-2-2',
level_2_2_1: 'Level-2-2-1',
level_2_2_2: 'Level-2-2-2',
excel: 'Excel',
'upload-excel': 'Upload Excel',
'export-excel': 'Export Excel',
@ -34,5 +36,11 @@ export default {
error_store_page: 'Error Collection',
error_logger_page: 'Error Logger',
query: 'Query',
params: 'Params'
params: 'Params',
cropper_page: 'Cropper',
message_page: 'Message Center',
tree_table_page: 'Tree Table',
org_tree_page: 'Org Tree',
drag_drawer_page: 'Draggable Drawer',
tree_select_page: 'Tree Selector'
}

View File

@ -1,5 +1,6 @@
export default {
home: '首页',
login: '登录',
components: '组件',
count_to_page: '数字渐变',
tables_page: '多功能表格',
@ -21,6 +22,7 @@ export default {
level_2_3: 'Level-2-3',
level_2_2: 'Level-2-2',
level_2_2_1: 'Level-2-2-1',
level_2_2_2: 'Level-2-2-2',
excel: 'Excel',
'upload-excel': '上传excel',
'export-excel': '导出excel',
@ -34,5 +36,11 @@ export default {
error_store_page: '错误收集',
error_logger_page: '错误日志',
query: '带参路由',
params: '动态路由'
params: '动态路由',
cropper_page: '图片裁剪',
message_page: '消息中心',
tree_table_page: '树状表格',
org_tree_page: '组织结构树',
drag_drawer_page: '可拖动抽屉',
tree_select_page: '树状下拉选择器'
}

View File

@ -1,5 +1,6 @@
export default {
home: '首頁',
login: '登錄',
components: '组件',
count_to_page: '数字渐变',
tables_page: '多功能表格',
@ -21,6 +22,7 @@ export default {
level_2_3: 'Level-2-3',
level_2_2: 'Level-2-2',
level_2_2_1: 'Level-2-2-1',
level_2_2_2: 'Level-2-2-2',
excel: 'Excel',
'upload-excel': '上傳excel',
'export-excel': '導出excel',
@ -34,5 +36,11 @@ export default {
error_store_page: '錯誤收集',
error_logger_page: '錯誤日誌',
query: '帶參路由',
params: '動態路由'
params: '動態路由',
cropper_page: '圖片裁剪',
message_page: '消息中心',
tree_table_page: '樹狀表格',
org_tree_page: '組織結構樹',
drag_drawer_page: '可拖動抽屜',
tree_select_page: '樹狀下拉選擇器'
}

View File

@ -8,10 +8,13 @@ import iView from 'iview'
import i18n from '@/locale'
import config from '@/config'
import importDirective from '@/directive'
import { directive as clickOutside } from 'v-click-outside-x'
import installPlugin from '@/plugin'
import 'iview/dist/styles/iview.css'
import './index.less'
import '@/assets/icons/iconfont.css'
import TreeTable from 'tree-table-vue'
import VOrgTree from 'v-org-tree'
import 'v-org-tree/dist/v-org-tree.css'
// 实际打包时应该不引入mock
/* eslint-disable */
if (process.env.NODE_ENV !== 'production') require('@/mock')
@ -19,6 +22,8 @@ if (process.env.NODE_ENV !== 'production') require('@/mock')
Vue.use(iView, {
i18n: (key, value) => i18n.t(key, value)
})
Vue.use(TreeTable)
Vue.use(VOrgTree)
/**
* @description 注册admin内置插件
*/
@ -35,6 +40,7 @@ Vue.prototype.$config = config
* 注册指令
*/
importDirective(Vue)
Vue.directive('clickOutside', clickOutside)
/* eslint-disable no-new */
new Vue({

View File

@ -1,5 +1,7 @@
import Mock from 'mockjs'
import { doCustomTimes } from '@/libs/util'
import orgData from './data/org-data'
import { treeData } from './data/tree-select'
const Random = Mock.Random
export const getTableData = req => {
@ -24,3 +26,15 @@ export const getDragList = req => {
})
return dragList
}
export const uploadImage = req => {
return Promise.resolve()
}
export const getOrgData = req => {
return orgData
}
export const getTreeSelectData = req => {
return treeData
}

45
src/mock/data/org-data.js Normal file
View File

@ -0,0 +1,45 @@
export default {
id: 0,
label: 'XXX科技有限公司',
children: [
{
id: 2,
label: '产品研发部',
children: [
{
id: 5,
label: '研发-前端'
}, {
id: 6,
label: '研发-后端'
}, {
id: 9,
label: 'UI设计'
}, {
id: 10,
label: '产品经理'
}
]
},
{
id: 3,
label: '销售部',
children: [
{
id: 7,
label: '销售一部'
}, {
id: 8,
label: '销售二部'
}
]
},
{
id: 4,
label: '财务部'
}, {
id: 11,
label: 'HR人事'
}
]
}

View File

@ -0,0 +1,82 @@
export const treeData = [
{
id: 1,
title: '1',
children: [
{
id: 11,
title: '1-1',
loading: false,
children: [
// {
// id: 111,
// title: '1-1-1'
// },
// {
// id: 112,
// title: '1-1-2'
// },
// {
// id: 113,
// title: '1-1-3'
// },
// {
// id: 114,
// title: '1-1-4'
// }
]
},
{
id: 12,
title: '1-2',
children: [
{
id: 121,
title: '1-2-1'
}
]
}
]
}
]
export const newTreeData = [
{
id: 'a',
title: 'a',
children: [
{
id: 'a1',
title: 'a-1',
children: [
{
id: 112,
title: '1-1-2'
},
{
id: 'a12',
title: 'a-1-2'
},
{
id: 'a13',
title: 'a-1-3'
},
{
id: 'a14',
title: 'a-1-4'
}
]
},
{
id: 'a2',
title: 'a-2',
children: [
{
id: 'a21',
title: 'b-2-1'
}
]
}
]
}
]

View File

@ -1,6 +1,12 @@
import Mock from 'mockjs'
import { login, logout, getUserInfo } from './login'
import { getTableData, getDragList } from './data'
import { getTableData, getDragList, uploadImage, getOrgData, getTreeSelectData } from './data'
import { getMessageInit, getContentByMsgId, hasRead, removeReaded, restoreTrash, messageCount } from './user'
// 配置Ajax请求延时可用来测试网络延迟大时项目中一些效果
Mock.setup({
timeout: 1000
})
// 登录相关和获取用户信息
Mock.mock(/\/login/, login)
@ -9,5 +15,14 @@ Mock.mock(/\/logout/, logout)
Mock.mock(/\/get_table_data/, getTableData)
Mock.mock(/\/get_drag_list/, getDragList)
Mock.mock(/\/save_error_logger/, 'success')
Mock.mock(/\/image\/upload/, uploadImage)
Mock.mock(/\/message\/init/, getMessageInit)
Mock.mock(/\/message\/content/, getContentByMsgId)
Mock.mock(/\/message\/has_read/, hasRead)
Mock.mock(/\/message\/remove_readed/, removeReaded)
Mock.mock(/\/message\/restore/, restoreTrash)
Mock.mock(/\/message\/count/, messageCount)
Mock.mock(/\/get_org_data/, getOrgData)
Mock.mock(/\/get_tree_select_data/, getTreeSelectData)
export default Mock

View File

@ -5,20 +5,20 @@ const USER_MAP = {
user_id: '1',
access: ['super_admin', 'admin'],
token: 'super_admin',
avator: 'https://file.iviewui.com/dist/a0e88e83800f138b94d2414621bd9704.png'
avatar: 'https://file.iviewui.com/dist/a0e88e83800f138b94d2414621bd9704.png'
},
admin: {
name: 'admin',
user_id: '2',
access: ['admin'],
token: 'admin',
avator: 'https://avatars0.githubusercontent.com/u/20942571?s=460&v=4'
avatar: 'https://avatars0.githubusercontent.com/u/20942571?s=460&v=4'
}
}
export const login = req => {
req = JSON.parse(req.body)
return {token: USER_MAP[req.userName].token}
return { token: USER_MAP[req.userName].token }
}
export const getUserInfo = req => {

55
src/mock/user.js Normal file
View File

@ -0,0 +1,55 @@
import Mock from 'mockjs'
import { doCustomTimes } from '@/libs/util'
const Random = Mock.Random
export const getMessageInit = () => {
let unreadList = []
doCustomTimes(3, () => {
unreadList.push(Mock.mock({
title: Random.cword(10, 15),
create_time: '@date',
msg_id: Random.increment(100)
}))
})
let readedList = []
doCustomTimes(4, () => {
readedList.push(Mock.mock({
title: Random.cword(10, 15),
create_time: '@date',
msg_id: Random.increment(100)
}))
})
let trashList = []
doCustomTimes(2, () => {
trashList.push(Mock.mock({
title: Random.cword(10, 15),
create_time: '@date',
msg_id: Random.increment(100)
}))
})
return {
unread: unreadList,
readed: readedList,
trash: trashList
}
}
export const getContentByMsgId = () => {
return `<divcourier new',="" monospace;font-weight:="" normal;font-size:="" 12px;line-height:="" 18px;white-space:="" pre;"=""><div>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: medium;">这是消息内容,这个内容是使用<span style="color: rgb(255, 255, 255); background-color: rgb(28, 72, 127);">富文本编辑器</span>编辑的,所以你可以看到一些<span style="text-decoration-line: underline; font-style: italic; color: rgb(194, 79, 74);">格式</span></span></div><ol><li>你可以查看Mock返回的数据格式和api请求的接口来确定你的后端接口的开发</li><li>使用你的真实接口后,前端页面基本不需要修改即可满足基本需求</li><li>快来试试吧</li></ol><p>${Random.csentence(100, 200)}</p></divcourier>`
}
export const hasRead = () => {
return true
}
export const removeReaded = () => {
return true
}
export const restoreTrash = () => {
return true
}
export const messageCount = () => {
return 3
}

View File

@ -3,7 +3,7 @@ import Router from 'vue-router'
import routes from './routers'
import store from '@/store'
import iView from 'iview'
import { setToken, getToken, canTurnTo } from '@/libs/util'
import { setToken, getToken, canTurnTo, setTitle } from '@/libs/util'
import config from '@/config'
const { homeName } = config
@ -53,6 +53,7 @@ router.beforeEach((to, from, next) => {
})
router.afterEach(to => {
setTitle(to, router.app)
iView.LoadingBar.finish()
window.scrollTo(0, 0)
})

View File

@ -10,7 +10,7 @@ import parentView from '@/components/parent-view'
* 可以传入一个回调函数参数是当前路由对象例子看动态路由和带参路由
* hideInBread: (false) 设为true后此级路由将不会出现在面包屑中示例看QQ群路由配置
* hideInMenu: (false) 设为true后在左侧菜单不会显示该页面选项
* notCache: (false) 设为true后页面不会缓存
* notCache: (false) 设为true后页面在切换标签后不会缓存如果需要缓存无需设置这个字段而且需要设置页面组件name属性和路由配置的name一致
* access: (null) 可访问该页面的权限数组当前路由设置的权限会影响子路由
* icon: (-) 该页面在左侧菜单面包屑和标签导航处显示的图标如果是自定义图标需要在图标名称前加下划线'_'
* beforeCloseName: (-) 设置该字段则在关闭当前tab页时会去'@/router/before-close.js'里寻找该字段名对应的方法作为关闭前的钩子函数
@ -78,6 +78,26 @@ export default [
}
]
},
{
path: '/message',
name: 'message',
component: Main,
meta: {
hideInBread: true,
hideInMenu: true
},
children: [
{
path: 'message_page',
name: 'message_page',
meta: {
icon: 'md-notifications',
title: '消息中心'
},
component: () => import('@/view/single-page/message/index.vue')
}
]
},
{
path: '/components',
name: 'components',
@ -87,6 +107,15 @@ export default [
},
component: Main,
children: [
{
path: 'tree_select_page',
name: 'tree_select_page',
meta: {
icon: 'md-arrow-dropdown-circle',
title: '树状下拉选择器'
},
component: () => import('@/view/components/tree-select/index.vue')
},
{
path: 'count_to_page',
name: 'count_to_page',
@ -105,6 +134,42 @@ export default [
},
component: () => import('@/view/components/drag-list/drag-list.vue')
},
{
path: 'drag_drawer_page',
name: 'drag_drawer_page',
meta: {
icon: 'md-list',
title: '可拖拽抽屉'
},
component: () => import('@/view/components/drag-drawer')
},
{
path: 'org_tree_page',
name: 'org_tree_page',
meta: {
icon: 'ios-people',
title: '组织结构树'
},
component: () => import('@/view/components/org-tree')
},
{
path: 'tree_table_page',
name: 'tree_table_page',
meta: {
icon: 'md-git-branch',
title: '树状表格'
},
component: () => import('@/view/components/tree-table/index.vue')
},
{
path: 'cropper_page',
name: 'cropper_page',
meta: {
icon: 'md-crop',
title: '图片裁剪'
},
component: () => import('@/view/components/cropper/cropper.vue')
},
{
path: 'tables_page',
name: 'tables_page',
@ -343,7 +408,16 @@ export default [
icon: 'md-funnel',
title: '三级'
},
component: () => import('@/view/multilevel/level-2-2/level-3-1.vue')
component: () => import('@/view/multilevel/level-2-2/level-2-2-1.vue')
},
{
path: 'level_2_2_2',
name: 'level_2_2_2',
meta: {
icon: 'md-funnel',
title: '三级'
},
component: () => import('@/view/multilevel/level-2-2/level-2-2-2.vue')
}
]
},

View File

@ -11,7 +11,6 @@ import {
localSave,
localRead
} from '@/libs/util'
import beforeClose from '@/router/before-close'
import { saveErrorLogger } from '@/api/data'
import router from '@/router'
import routers from '@/router/routers'
@ -64,15 +63,7 @@ export default {
let tag = state.tagNavList.filter(item => routeEqual(item, route))
route = tag[0] ? tag[0] : null
if (!route) return
if (route.meta && route.meta.beforeCloseName && route.meta.beforeCloseName in beforeClose) {
new Promise(beforeClose[route.meta.beforeCloseName]).then(close => {
if (close) {
closePage(state, route)
}
})
} else {
closePage(state, route)
}
},
addTag (state, { route, type = 'unshift' }) {
let router = getRouteTitleHandled(route)

View File

@ -1,18 +1,33 @@
import { login, logout, getUserInfo } from '@/api/user'
import {
login,
logout,
getUserInfo,
getMessage,
getContentByMsgId,
hasRead,
removeReaded,
restoreTrash,
getUnreadCount
} from '@/api/user'
import { setToken, getToken } from '@/libs/util'
export default {
state: {
userName: '',
userId: '',
avatorImgPath: '',
avatarImgPath: '',
token: getToken(),
access: '',
hasGetInfo: false
hasGetInfo: false,
unreadCount: 0,
messageUnreadList: [],
messageReadedList: [],
messageTrashList: [],
messageContentStore: {}
},
mutations: {
setAvator (state, avatorPath) {
state.avatorImgPath = avatorPath
setAvatar (state, avatarPath) {
state.avatarImgPath = avatarPath
},
setUserId (state, id) {
state.userId = id
@ -29,11 +44,37 @@ export default {
},
setHasGetInfo (state, status) {
state.hasGetInfo = status
},
setMessageCount (state, count) {
state.unreadCount = count
},
setMessageUnreadList (state, list) {
state.messageUnreadList = list
},
setMessageReadedList (state, list) {
state.messageReadedList = list
},
setMessageTrashList (state, list) {
state.messageTrashList = list
},
updateMessageContentStore (state, { msg_id, content }) {
state.messageContentStore[msg_id] = content
},
moveMsg (state, { from, to, msg_id }) {
const index = state[from].findIndex(_ => _.msg_id === msg_id)
const msgItem = state[from].splice(index, 1)[0]
msgItem.loading = false
state[to].unshift(msgItem)
}
},
getters: {
messageUnreadCount: state => state.messageUnreadList.length,
messageReadedCount: state => state.messageReadedList.length,
messageTrashCount: state => state.messageTrashList.length
},
actions: {
// 登录
handleLogin ({ commit }, {userName, password}) {
handleLogin ({ commit }, { userName, password }) {
userName = userName.trim()
return new Promise((resolve, reject) => {
login({
@ -70,7 +111,7 @@ export default {
try {
getUserInfo(state.token).then(res => {
const data = res.data
commit('setAvator', data.avator)
commit('setAvatar', data.avatar)
commit('setUserName', data.name)
commit('setUserId', data.user_id)
commit('setAccess', data.access)
@ -83,6 +124,94 @@ export default {
reject(error)
}
})
},
// 此方法用来获取未读消息条数,接口只返回数值,不返回消息列表
getUnreadMessageCount ({ state, commit }) {
getUnreadCount().then(res => {
const { data } = res
commit('setMessageCount', data)
})
},
// 获取消息列表,其中包含未读、已读、回收站三个列表
getMessageList ({ state, commit }) {
return new Promise((resolve, reject) => {
getMessage().then(res => {
const { unread, readed, trash } = res.data
commit('setMessageUnreadList', unread.sort((a, b) => new Date(b.create_time) - new Date(a.create_time)))
commit('setMessageReadedList', readed.map(_ => {
_.loading = false
return _
}).sort((a, b) => new Date(b.create_time) - new Date(a.create_time)))
commit('setMessageTrashList', trash.map(_ => {
_.loading = false
return _
}).sort((a, b) => new Date(b.create_time) - new Date(a.create_time)))
resolve()
}).catch(error => {
reject(error)
})
})
},
// 根据当前点击的消息的id获取内容
getContentByMsgId ({ state, commit }, { msg_id }) {
return new Promise((resolve, reject) => {
let contentItem = state.messageContentStore[msg_id]
if (contentItem) {
resolve(contentItem)
} else {
getContentByMsgId(msg_id).then(res => {
const content = res.data
commit('updateMessageContentStore', { msg_id, content })
resolve(content)
})
}
})
},
// 把一个未读消息标记为已读
hasRead ({ state, commit }, { msg_id }) {
return new Promise((resolve, reject) => {
hasRead(msg_id).then(() => {
commit('moveMsg', {
from: 'messageUnreadList',
to: 'messageReadedList',
msg_id
})
commit('setMessageCount', state.unreadCount - 1)
resolve()
}).catch(error => {
reject(error)
})
})
},
// 删除一个已读消息到回收站
removeReaded ({ commit }, { msg_id }) {
return new Promise((resolve, reject) => {
removeReaded(msg_id).then(() => {
commit('moveMsg', {
from: 'messageReadedList',
to: 'messageTrashList',
msg_id
})
resolve()
}).catch(error => {
reject(error)
})
})
},
// 还原一个已删除消息到已读消息
restoreTrash ({ commit }, { msg_id }) {
return new Promise((resolve, reject) => {
restoreTrash(msg_id).then(() => {
commit('moveMsg', {
from: 'messageTrashList',
to: 'messageReadedList',
msg_id
})
resolve()
}).catch(error => {
reject(error)
})
})
}
}
}

View File

@ -2,13 +2,32 @@
<div>
<Card>
<h2>ID: {{ $route.params.id }}</h2>
<Button @click="close">调用closeTag方法关闭本页</Button>
</Card>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
name: 'argu_page'
name: 'params',
methods: {
...mapMutations([
'closeTag'
]),
close () {
/**
* 如果是调用closeTag方法普通的页面传入的对象参数只需要写name字段即可
* 如果是动态路由和带参路由需要传入query或params字段用来区别关闭的是参数为多少的页面
*/
this.closeTag({
name: 'params',
params: {
id: this.$route.params.id
}
})
}
}
}
</script>

View File

@ -2,13 +2,32 @@
<div>
<Card>
<h2>ID: {{ $route.query.id }}</h2>
<Button @click="close">调用closeTag方法关闭本页</Button>
</Card>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
name: 'query'
name: 'query',
methods: {
...mapMutations([
'closeTag'
]),
close () {
/**
* 如果是调用closeTag方法普通的页面传入的对象参数只需要写name字段即可
* 如果是动态路由和带参路由需要传入query或params字段用来区别关闭的是参数为多少的页面
*/
this.closeTag({
name: 'query',
query: {
id: this.$route.query.id
}
})
}
}
}
</script>

View File

@ -0,0 +1,48 @@
<template>
<div>
<Row>
<i-col span="12">
<Card>
<div class="cropper-example cropper-first">
<cropper
:src="exampleImageSrc"
crop-button-text="确认提交"
@on-crop="handleCroped"
></cropper>
</div>
</Card>
</i-col>
</Row>
</div>
</template>
<script>
import Cropper from '@/components/cropper'
import { uploadImg } from '@/api/data'
export default {
name: 'cropper_page',
components: {
Cropper
},
data () {
return {
exampleImageSrc: ''
}
},
methods: {
handleCroped (blob) {
const formData = new FormData()
formData.append('croppedImg', blob)
uploadImg(formData).then(() => {
this.$Message.success('Upload success~')
})
}
}
}
</script>
<style lang="less">
.cropper-example{
height: 400px;
}
</style>

View File

@ -0,0 +1,97 @@
<template>
<Card>
<h3 style="padding: 10px 0;">drag-drawer组件是对iview的drawer组件的封装在支持drawer所有api的基础上支持可拖动和footer底部插槽</h3>
<div style="padding: 10px 0">
<b>
方向
<i-switch v-model="placement">
<span slot="open"></span>
<span slot="close"></span>
</i-switch>
</b>
<b>
是否可拖动
<i-switch v-model="draggable"></i-switch>
</b>
<Button @click="showContainerBDrawer = !showContainerBDrawer" type="primary" style="margin-left: 10px">{{ showContainerBDrawer ? '关闭' : '打开' }}容器内抽屉</Button>
<Button @click="showWindowBDrawer = true" type="primary" style="margin-left: 10px">打开全屏抽屉</Button>
</div>
<div class="drag-drawer-inner-box">
<drag-drawer v-model="showContainerBDrawer"
:width.sync="width2"
min-width="30px"
:inner="true"
:transfer="false"
:placement="placementComputed"
:draggable="draggable"
@on-resize="handleResize"
:scrollable="true">
<div slot="header">
<Icon type="md-aperture" :size="18"></Icon>
<b>这是标题</b>
</div>
<p v-for="n in 200" :key="n">{{ n }}</p>
<div slot="footer">
<p>123123</p>
<p>21312</p>
</div>
</drag-drawer>
</div>
<drag-drawer v-model="showWindowBDrawer"
:width.sync="width1"
:min-width="300"
:placement="placementComputed"
:draggable="draggable"
:scrollable="true">
<div slot="header">
<Icon type="md-aperture" :size="18"></Icon>
<b>这是标题</b>
</div>
<Button @click="showBDrawer3 = true">显示多层</Button>
<p v-for="n in 200" :key="n">{{ n }}</p>
</drag-drawer>
</Card>
</template>
<script>
import DragDrawer from '_c/drag-drawer'
export default {
name: 'drag_drawer_page',
components: {
DragDrawer
},
data () {
return {
showWindowBDrawer: false,
showContainerBDrawer: false,
showBDrawer3: false,
width1: 300,
width2: 200,
placement: false,
draggable: true
}
},
computed: {
placementComputed () {
return this.placement ? 'left' : 'right'
}
},
methods: {
handleResize (event) {
const { atMin } = event
/* eslint-disable */
console.log(atMin);
},
}
}
</script>
<style lang="less">
.drag-drawer-inner-box{
position: relative;
width: 500px;
height: 400px;
background: pink;
border: 1px solid pink;
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div>
<editor ref="editor" :value="content" @on-change="handleChange"/>
<button @click="changeContent">修改编辑器内容</button>
<Button @click="changeContent">修改编辑器内容</Button>
</div>
</template>

View File

@ -0,0 +1,172 @@
<template>
<div
ref="dragWrapper"
class="org-tree-drag-wrapper"
@mousedown="mousedownView"
@contextmenu="handleDocumentContextmenu"
>
<div class="org-tree-wrapper" :style="orgTreeStyle">
<v-org-tree
v-if="data"
:data="data"
:node-render="nodeRender"
:expand-all="true"
@on-node-click="handleNodeClick"
collapsable
></v-org-tree>
</div>
</div>
</template>
<script>
import { on, off } from '@/libs/tools'
const menuList = [
{
key: 'edit',
label: '编辑部门'
},
{
key: 'detail',
label: '查看部门'
},
{
key: 'new',
label: '新增子部门'
},
{
key: 'delete',
label: '删除部门'
}
]
export default {
name: 'OrgView',
props: {
zoomHandled: {
type: Number,
default: 1
},
data: Object
},
data () {
return {
currentContextMenuId: '',
orgTreeOffsetLeft: 0,
orgTreeOffsetTop: 0,
initPageX: 0,
initPageY: 0,
oldMarginLeft: 0,
oldMarginTop: 0,
canMove: false
}
},
computed: {
orgTreeStyle () {
return {
transform: `translate(-50%, -50%) scale(${this.zoomHandled}, ${
this.zoomHandled
})`,
marginLeft: `${this.orgTreeOffsetLeft}px`,
marginTop: `${this.orgTreeOffsetTop}px`
}
}
},
methods: {
handleNodeClick (e, data, expand) {
expand()
},
closeMenu () {
this.currentContextMenuId = ''
},
getBgColor (data) {
return this.currentContextMenuId === data.id
? data.isRoot
? '#0d7fe8'
: '#5d6c7b'
: ''
},
nodeRender (h, data) {
return (
<div
class={[
'custom-org-node',
data.children && data.children.length ? 'has-children-label' : ''
]}
on-mousedown={event => event.stopPropagation()}
on-contextmenu={this.contextmenu.bind(this, data)}
>
{data.label}
<dropdown
trigger="custom"
class="context-menu"
visible={this.currentContextMenuId === data.id}
nativeOn-click={this.handleDropdownClick}
on-on-click={this.handleContextMenuClick.bind(this, data)}
style={{
transform: `scale(${1 / this.zoomHandled}, ${1 /
this.zoomHandled})`
}}
v-click-outside={this.closeMenu}
>
<dropdown-menu slot="list">
{menuList.map(item => {
return (
<dropdown-item name={item.key}>{item.label}</dropdown-item>
)
})}
</dropdown-menu>
</dropdown>
</div>
)
},
contextmenu (data, $event) {
let event = $event || window.event
event.preventDefault
? event.preventDefault()
: (event.returnValue = false)
this.currentContextMenuId = data.id
},
setDepartmentData (data) {
data.isRoot = true
this.departmentData = data
},
mousedownView (event) {
this.canMove = true
this.initPageX = event.pageX
this.initPageY = event.pageY
this.oldMarginLeft = this.orgTreeOffsetLeft
this.oldMarginTop = this.orgTreeOffsetTop
on(document, 'mousemove', this.mousemoveView)
on(document, 'mouseup', this.mouseupView)
},
mousemoveView (event) {
if (!this.canMove) return
const { pageX, pageY } = event
this.orgTreeOffsetLeft = this.oldMarginLeft + pageX - this.initPageX
this.orgTreeOffsetTop = this.oldMarginTop + pageY - this.initPageY
},
mouseupView () {
this.canMove = false
off(document, 'mousemove', this.mousemoveView)
off(document, 'mouseup', this.mouseupView)
},
handleDropdownClick (event) {
event.stopPropagation()
},
handleDocumentContextmenu () {
this.canMove = false
},
handleContextMenuClick (data, key) {
this.$emit('on-menu-click', { data, key })
}
},
mounted () {
on(document, 'contextmenu', this.handleDocumentContextmenu)
},
beforeDestroy () {
off(document, 'contextmenu', this.handleDocumentContextmenu)
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,81 @@
<template>
<div class="zoom-wrapper">
<button class="zoom-button" @click="scale('down')">
<Icon type="md-remove" :size="14" color="#fff"/>
</button>
<span class="zoom-number">{{ value }}%</span>
<button class="zoom-button" @click="scale('up')">
<Icon type="md-add" :size="14" color="#fff"/>
</button>
</div>
</template>
<script>
export default {
name: 'ZoomController',
props: {
value: {
type: Number,
default: 100
},
step: {
type: Number,
default: 20
},
min: {
type: Number,
default: 10
},
max: {
type: Number,
default: 200
}
},
methods: {
scale (type) {
const zoom = this.value + (type === 'down' ? -this.step : this.step)
if (
(zoom < this.min && type === 'down') ||
(zoom > this.max && type === 'up')
) {
return
}
this.$emit('input', zoom)
}
}
}
</script>
<style lang="less">
.trans(@duration) {
transition: ~"all @{duration} ease-in";
}
.zoom-wrapper {
.zoom-button {
width: 20px;
height: 20px;
line-height: 10px;
border-radius: 50%;
background: rgba(157, 162, 172, 1);
box-shadow: 0px 2px 8px 0px rgba(218, 220, 223, 0.7);
border: none;
cursor: pointer;
outline: none;
&:active {
box-shadow: 0px 0px 2px 2px rgba(218, 220, 223, 0.2) inset;
}
.trans(0.1s);
&:hover {
background: #1890ff;
.trans(0.1s);
}
}
.zoom-number {
color: #657180;
padding: 0 8px;
display: inline-block;
width: 46px;
text-align: center;
}
}
</style>

View File

@ -0,0 +1,75 @@
@wrapper: ~'department';
.percent-100 {
width: 100%;
height: 100%;
}
.@{wrapper}-outer {
.percent-100;
overflow: hidden;
.tip-box{
position: absolute;
left: 20px;
top: 20px;
z-index: 12;
}
.zoom-box {
position: absolute;
right: 30px;
bottom: 30px;
z-index: 2;
}
.view-box {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
cursor: move;
.org-tree-drag-wrapper {
width: 100%;
height: 100%;
}
.org-tree-wrapper {
display: inline-block;
position: absolute;
left: 50%;
top: 50%;
transition: transform 0.2s ease-out;
.org-tree-node-label {
box-shadow: 0px 2px 12px 0px rgba(143, 154, 165, 0.4);
border-radius: 4px;
.org-tree-node-label-inner {
padding: 0;
.custom-org-node {
padding: 14px 41px;
background: #738699;
user-select: none;
word-wrap: none;
white-space: nowrap;
border-radius: 4px;
color: #ffffff;
font-size: 14px;
font-weight: 500;
line-height: 20px;
transition: background 0.1s ease-in;
cursor: default;
&:hover {
background: #5d6c7b;
transition: background 0.1s ease-in;
}
&.has-children-label {
cursor: pointer;
}
.context-menu{
position: absolute;
right: -10px;
bottom: 20px;
z-index: 10;
}
}
}
}
}
}
}

View File

@ -0,0 +1,76 @@
<template>
<Card shadow style="height: 100%;width: 100%;overflow:hidden">
<div class="department-outer">
<div class="tip-box">
<b style="margin-right: 20px;">powered by <a target="blank" href="https://github.com/lison16">Lison</a></b>
<a target="blank" href="https://github.com/lison16/v-org-tree" style="margin-right: 10px;">v-org-tree文档</a>
</div>
<div class="zoom-box">
<zoom-controller v-model="zoom" :min="20" :max="200"></zoom-controller>
</div>
<div class="view-box">
<org-view
v-if="data"
:data="data"
:zoom-handled="zoomHandled"
@on-menu-click="handleMenuClick"
></org-view>
</div>
</div>
</Card>
</template>
<script>
import OrgView from './components/org-view.vue'
import ZoomController from './components/zoom-controller.vue'
import { getOrgData } from '@/api/data'
import './index.less'
const menuDic = {
edit: '编辑部门',
detail: '查看部门',
new: '新增子部门',
delete: '删除部门'
}
export default {
name: 'org_tree_page',
components: {
OrgView,
ZoomController
},
data () {
return {
data: null,
zoom: 100
}
},
computed: {
zoomHandled () {
return this.zoom / 100
}
},
methods: {
setDepartmentData (data) {
data.isRoot = true
return data
},
handleMenuClick ({ data, key }) {
this.$Message.success({
duration: 5,
content: `点击了《${data.label}》节点的'${menuDic[key]}'菜单`
})
},
getDepartmentData () {
getOrgData().then(res => {
const { data } = res
this.data = data
})
}
},
mounted () {
this.getDepartmentData()
}
}
</script>
<style>
</style>

View File

@ -18,9 +18,9 @@ export default {
data () {
return {
columns: [
{title: 'Name', key: 'name', sortable: true},
{title: 'Email', key: 'email', editable: true},
{title: 'Create-Time', key: 'createTime'},
{ title: 'Name', key: 'name', sortable: true },
{ title: 'Email', key: 'email', editable: true },
{ title: 'Create-Time', key: 'createTime' },
{
title: 'Handle',
key: 'handle',

View File

@ -0,0 +1,90 @@
<template>
<div>
<tree-select
v-model="treeSelected"
style="width: 300px;"
check-strictly
:expand-all="true"
:load-data="loadData"
@on-change="handleTreeSelectChange"
@on-toggle-expand="handleTreeSelectExpand"
@on-check-change="handleTreeSelectCheckChange"
@on-select-change="handleTreeSelectClick"
:data="treeData"
></tree-select>
<Button @click="changeTreeSelectData">更新选中数据</Button>
<Button @click="changeTreeData">更新树数据</Button>
</div>
</template>
<script>
import TreeSelect from '_c/tree-select'
import { newTreeData } from '@/mock/data/tree-select'
import { getTreeSelectData } from '@/api/data'
export default {
name: 'tree_select_page',
components: {
TreeSelect
},
data () {
return {
treeSelected: [112, 113],
treeData: []
}
},
mounted () {
getTreeSelectData().then(res => {
const { data } = res
this.treeData = data
})
},
methods: {
changeTreeSelectData () {
this.treeSelected = [111, 114]
},
changeTreeData () {
this.treeData = newTreeData
// this.treeSelected = [];
},
handleTreeSelectChange (list) {
// console.log('=-========', list);
},
handleTreeSelectExpand (item) {
// console.log('toggle expand', item);
},
handleTreeSelectCheckChange (selectedArray, item) {
// console.log(selectedArray, item);
},
handleTreeSelectClick (selectArray, item) {
// console.log(selectArray, item);
},
loadData (item, callback) {
setTimeout(() => {
let data = [
{
id: 111,
title: '1-1-1'
},
{
id: 112,
title: '1-1-2'
},
{
id: 113,
title: '1-1-3'
},
{
id: 114,
title: '1-1-4'
}
]
callback(data)
}, 1000)
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,194 @@
<template>
<div>
<Card shadow>
树状表格组件tree-table-vue基于<a href="https://github.com/MisterTaki/vue-table-with-tree-grid">vue-table-with-tree-grid</a>进行开发修复了一些bug添加了一些新属性
<p><b>支持使用slot-scope进行自定义列渲染内容</b></p>
<p>文档请看<a href="https://github.com/lison16/tree-table-vue">https://github.com/lison16/tree-table-vue</a></p>
<tree-table expand-key="sex" :expand-type="false" :selectable="false" :columns="columns" :data="data" >
<template slot="likes" slot-scope="scope">
<Button @click="handle(scope)">123</Button>
</template>
</tree-table>
</Card>
</div>
</template>
<script>
export default {
name: 'tree_table_page',
data () {
return {
columns: [
{
title: 'name',
key: 'name',
width: '400px'
},
{
title: 'sex',
key: 'sex',
minWidth: '50px'
},
{
title: 'score',
key: 'score'
},
{
title: 'likes',
key: 'likes',
minWidth: '200px',
type: 'template',
template: 'likes'
}
],
data: [
{
name: 'Jack',
sex: 'male',
likes: ['football', 'basketball'],
score: 10,
children: [
{
name: 'Ashley',
sex: 'female',
likes: ['football', 'basketball'],
score: 20,
children: [
{
name: 'Ashley',
sex: 'female',
likes: ['football', 'basketball'],
score: 20
},
{
name: 'Taki',
sex: 'male',
likes: ['football', 'basketball'],
score: 10,
children: [
{
name: 'Ashley',
sex: 'female',
likes: ['football', 'basketball'],
score: 20
},
{
name: 'Taki',
sex: 'male',
likes: ['football', 'basketball'],
score: 10,
children: [
{
name: 'Ashley',
sex: 'female',
likes: ['football', 'basketball'],
score: 20
},
{
name: 'Taki',
sex: 'male',
likes: ['football', 'basketball'],
score: 10
}
]
}
]
}
]
},
{
name: 'Taki',
sex: 'male',
likes: ['football', 'basketball'],
score: 10
}
]
},
{
name: 'Tom',
sex: 'male',
likes: ['football', 'basketball'],
score: 20,
children: [
{
name: 'Ashley',
sex: 'female',
likes: ['football', 'basketball'],
score: 20,
children: [
{
name: 'Ashley',
sex: 'female',
likes: ['football', 'basketball'],
score: 20
},
{
name: 'Taki',
sex: 'male',
likes: ['football', 'basketball'],
score: 10
}
]
},
{
name: 'Taki',
sex: 'male',
likes: ['football', 'basketball'],
score: 10,
children: [
{
name: 'Ashley',
sex: 'female',
likes: ['football', 'basketball'],
score: 20
},
{
name: 'Taki',
sex: 'male',
likes: ['football', 'basketball'],
score: 10
}
]
}
]
},
{
name: 'Tom',
sex: 'male',
likes: ['football', 'basketball'],
score: 20
},
{
name: 'Tom',
sex: 'male',
likes: ['football', 'basketball'],
score: 20,
children: [
{
name: 'Ashley',
sex: 'female',
likes: ['football', 'basketball'],
score: 20
},
{
name: 'Taki',
sex: 'male',
likes: ['football', 'basketball'],
score: 10
}
]
}
]
}
},
methods: {
handle (scope) {
console.log(scope)
}
}
}
</script>
<style>
</style>

View File

@ -4,12 +4,12 @@
<i-col>
<Card>
<Row>
<i-col span="5">
<i-col span="8">
<Button type="primary" @click="showModal">显示可拖动弹窗</Button>
<br/>
<Button v-draggable="buttonOptions" class="draggable-btn">这个按钮也是可以拖动的</Button>
</i-col>
<i-col span="19">
<i-col span="16">
<div class="intro-con">
&lt;Modal v-draggable="options" v-model="visible"&gt;标题&lt;/Modal&gt;
<pre class="code-con">
@ -27,6 +27,42 @@
拖动这里即可拖动整个弹窗
</Modal>
</Row>
<Row style="margin-top: 10px;">
<i-col>
<Card>
<Row>
<i-col span="8">
<Input style="width: 60%" v-model="inputValue">
<Button slot="append" v-clipboard="clipOptions">copy</Button>
</Input>
</i-col>
<i-col span="16">
<div class="intro-con">
&lt;Input style="width: 60%" v-model="inputValue"&gt;
<br/>
&nbsp;&nbsp;&nbsp;&lt;Button slot="append" v-clipboard="clipOptions"&gt;copy&lt;/Button&gt;
<br/>
&lt;/Input&gt;
<pre class="code-con">
clipOptions: {
value: this.inputValue,
success: (e) => {
this.$Message.success('复制成功')
},
error: () => {
this.$Message.error('复制失败')
}
}
</pre>
</div>
</i-col>
</Row>
</Card>
</i-col>
<Modal v-draggable="options" v-model="modalVisible">
拖动这里即可拖动整个弹窗
</Modal>
</Row>
</div>
</template>
@ -45,7 +81,21 @@ export default {
trigger: '.draggable-btn',
body: '.draggable-btn'
},
statu: 1
statu: 1,
inputValue: '这是输入的内容'
}
},
computed: {
clipOptions () {
return {
value: this.inputValue,
success: (e) => {
this.$Message.success('复制成功')
},
error: () => {
this.$Message.error('复制失败')
}
}
}
},
methods: {

View File

@ -1,7 +1,7 @@
<template>
<div>
<Button size="large" type="text" @click="backHome">返回首页</Button>
<Button size="large" type="text">返回上一页({{ second }}s)</Button>
<Button size="large" type="text" @click="backPrev">返回上一页({{ second }}s)</Button>
</div>
</template>

View File

@ -1,7 +1,7 @@
<template>
<div class="error-page">
<div class="content-con">
<img :src="src" alt="404">
<img :src="src" :alt="code">
<div class="text-con">
<h4>{{ code }}</h4>
<h5>{{ desc }}</h5>

View File

@ -1,44 +1,66 @@
<template>
<div>
<Row :gutter="20">
<Card shadow title="社区">
<row class="join-page" :gutter="32">
<i-col span="10">
<img class="qq-group-img" src="../assets/images/icon-qr-qq-wechat.png">
<row type="flex" justify="center">
<i-col span="12">
<Card title="iview-admin交流群(已满)" shadow>
<img class="qq-group-img" :src="qqFans" alt="">
<p class="qq-group-intro">本群为使用iview-admin或者对iview-admin感兴趣的开发者提供交流平台在这里解决你开发中的疑惑共同进步</p>
</Card>
<p>QQ 群号621780943</p>
</i-col>
<i-col span="12">
<Card title="iview-admin交流群2" shadow>
<img class="qq-group-img" :src="qqFans2" alt="">
<p class="qq-group-intro">本群为使用iview-admin或者对iview-admin感兴趣的开发者提供交流平台在这里解决你开发中的疑惑共同进步</p>
</Card>
<i-col span="12"></i-col>
</row>
</i-col>
</Row>
<i-col span="14">
<div class="join-page-other">
<Button to="https://zhuanlan.zhihu.com/feview" target="_blank" size="large">
<img src="../assets/images/icon-social-zhihu.svg" class="join-page-other-icon">
iView 知乎专栏
</Button>
<Button to="https://juejin.im/user/56fe494539b0570054f2e032" target="_blank" size="large">
<img src="../assets/images/icon-social-juejin.svg" class="join-page-other-icon">
掘金
</Button>
<Button to="https://live.bilibili.com/1353202" target="_blank" size="large">
<img src="../assets/images/icon-social-bilibili.svg" class="join-page-other-icon">
活动直播间
</Button>
<Button to="https://twitter.com/iViewUI" target="_blank" size="large">
<img src="../assets/images/icon-social-twitter.svg" class="join-page-other-icon">
Twitter
</Button>
</div>
</i-col>
</row>
</Card>
</div>
</template>
<script>
import qqFans from '@/assets/images/qq-group1.jpg'
import qqFans2 from '@/assets/images/qq-group2.jpg'
export default {
name: 'join_page',
data () {
return {
qqFans,
qqFans2
}
}
}
</script>
<style>
.qq-group-img{
display: block;
margin: 0 auto;
width: 240px;
}
.qq-group-intro{
padding: 20px;
font-size: 16px;
}
.join-page{
text-align: center;
}
.qq-group-img{
width: 100%;
}
.join-page-other-icon{
width: 20px;
vertical-align: middle;
margin-right: 6px;
}
.join-page-other{
text-align: left;
}
.join-page-other .ivu-btn{
margin-right: 6px;
}
</style>

View File

@ -0,0 +1,16 @@
<template>
<div>
<h3>多级菜单 -> 二级-2 -> 3级1</h3>
<Input v-model="val" style="width: 200px"></Input>
</div>
</template>
<script>
export default {
name: 'level_2_2_1',
data () {
return {
val: ''
}
}
}
</script>

View File

@ -0,0 +1,16 @@
<template>
<div>
<h3>多级菜单 -> 二级-2 -> 3级2</h3>
<Input v-model="val" style="width: 200px"></Input>
</div>
</template>
<script>
export default {
name: 'level_2_2_2',
data () {
return {
val: ''
}
}
}
</script>

View File

@ -1,8 +0,0 @@
<template>
<div>多级菜单 -> 二级-2 -> 3</div>
</template>
<script>
export default {
name: 'level_3_1'
}
</script>

View File

@ -52,36 +52,36 @@ export default {
name: '运营商/网络服务',
type: 'line',
stack: '总量',
areaStyle: {normal: {
areaStyle: { normal: {
color: '#2d8cf0'
}},
} },
data: [120, 132, 101, 134, 90, 230, 210]
},
{
name: '银行/证券',
type: 'line',
stack: '总量',
areaStyle: {normal: {
areaStyle: { normal: {
color: '#10A6FF'
}},
} },
data: [257, 358, 278, 234, 290, 330, 310]
},
{
name: '游戏/视频',
type: 'line',
stack: '总量',
areaStyle: {normal: {
areaStyle: { normal: {
color: '#0C17A6'
}},
} },
data: [379, 268, 354, 269, 310, 478, 358]
},
{
name: '餐饮/外卖',
type: 'line',
stack: '总量',
areaStyle: {normal: {
areaStyle: { normal: {
color: '#4608A6'
}},
} },
data: [320, 332, 301, 334, 390, 330, 320]
},
{
@ -94,9 +94,9 @@ export default {
position: 'top'
}
},
areaStyle: {normal: {
areaStyle: { normal: {
color: '#398DBF'
}},
} },
data: [820, 645, 546, 745, 872, 624, 258]
}
]

View File

@ -53,11 +53,11 @@ export default {
{ title: '新增页面', icon: 'md-map', count: 14, color: '#9A66E4' }
],
pieData: [
{value: 335, name: '直接访问'},
{value: 310, name: '邮件营销'},
{value: 234, name: '联盟广告'},
{value: 135, name: '视频广告'},
{value: 1548, name: '搜索引擎'}
{ value: 335, name: '直接访问' },
{ value: 310, name: '邮件营销' },
{ value: 234, name: '联盟广告' },
{ value: 135, name: '视频广告' },
{ value: 1548, name: '搜索引擎' }
],
barData: {
Mon: 13253,

View File

@ -0,0 +1,194 @@
<template>
<Card shadow>
<div>
<div class="message-page-con message-category-con">
<Menu width="auto" active-name="unread" @on-select="handleSelect">
<MenuItem name="unread">
<span class="category-title">未读消息</span><Badge style="margin-left: 10px" :count="messageUnreadCount"></Badge>
</MenuItem>
<MenuItem name="readed">
<span class="category-title">已读消息</span><Badge style="margin-left: 10px" class-name="gray-dadge" :count="messageReadedCount"></Badge>
</MenuItem>
<MenuItem name="trash">
<span class="category-title">回收站</span><Badge style="margin-left: 10px" class-name="gray-dadge" :count="messageTrashCount"></Badge>
</MenuItem>
</Menu>
</div>
<div class="message-page-con message-list-con">
<Spin fix v-if="listLoading" size="large"></Spin>
<Menu
width="auto"
active-name=""
:class="titleClass"
@on-select="handleView"
>
<MenuItem v-for="item in messageList" :name="item.msg_id" :key="`msg_${item.msg_id}`">
<div>
<p class="msg-title">{{ item.title }}</p>
<Badge status="default" :text="item.create_time" />
<Button
style="float: right;margin-right: 20px;"
:style="{ display: item.loading ? 'inline-block !important' : '' }"
:loading="item.loading"
size="small"
:icon="currentMessageType === 'readed' ? 'md-trash' : 'md-redo'"
:title="currentMessageType === 'readed' ? '删除' : '还原'"
type="text"
v-show="currentMessageType !== 'unread'"
@click.native.stop="removeMsg(item)"></Button>
</div>
</MenuItem>
</Menu>
</div>
<div class="message-page-con message-view-con">
<Spin fix v-if="contentLoading" size="large"></Spin>
<div class="message-view-header">
<h2 class="message-view-title">{{ showingMsgItem.title }}</h2>
<time class="message-view-time">{{ showingMsgItem.create_time }}</time>
</div>
<div v-html="messageContent"></div>
</div>
</div>
</Card>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
const listDic = {
unread: 'messageUnreadList',
readed: 'messageReadedList',
trash: 'messageTrashList'
}
export default {
name: 'message_page',
data () {
return {
listLoading: true,
contentLoading: false,
currentMessageType: 'unread',
messageContent: '',
showingMsgItem: {}
}
},
computed: {
...mapState({
messageUnreadList: state => state.user.messageUnreadList,
messageReadedList: state => state.user.messageReadedList,
messageTrashList: state => state.user.messageTrashList,
messageList () {
return this[listDic[this.currentMessageType]]
},
titleClass () {
return {
'not-unread-list': this.currentMessageType !== 'unread'
}
}
}),
...mapGetters([
'messageUnreadCount',
'messageReadedCount',
'messageTrashCount'
])
},
methods: {
...mapMutations([
//
]),
...mapActions([
'getContentByMsgId',
'getMessageList',
'hasRead',
'removeReaded',
'restoreTrash'
]),
stopLoading (name) {
this[name] = false
},
handleSelect (name) {
this.currentMessageType = name
},
handleView (msg_id) {
this.contentLoading = true
this.getContentByMsgId({ msg_id }).then(content => {
this.messageContent = content
const item = this.messageList.find(item => item.msg_id === msg_id)
if (item) this.showingMsgItem = item
if (this.currentMessageType === 'unread') this.hasRead({ msg_id })
this.stopLoading('contentLoading')
}).catch(() => {
this.stopLoading('contentLoading')
})
},
removeMsg (item) {
item.loading = true
const msg_id = item.msg_id
if (this.currentMessageType === 'readed') this.removeReaded({ msg_id })
else this.restoreTrash({ msg_id })
}
},
mounted () {
this.listLoading = true
//
this.getMessageList().then(() => this.stopLoading('listLoading')).catch(() => this.stopLoading('listLoading'))
}
}
</script>
<style lang="less">
.message-page{
&-con{
height: ~"calc(100vh - 176px)";
display: inline-block;
vertical-align: top;
position: relative;
&.message-category-con{
border-right: 1px solid #e6e6e6;
width: 200px;
}
&.message-list-con{
border-right: 1px solid #e6e6e6;
width: 230px;
}
&.message-view-con{
position: absolute;
left: 446px;
top: 16px;
right: 16px;
bottom: 16px;
overflow: auto;
padding: 12px 20px 0;
.message-view-header{
margin-bottom: 20px;
.message-view-title{
display: inline-block;
}
.message-view-time{
margin-left: 20px;
}
}
}
.category-title{
display: inline-block;
width: 65px;
}
.gray-dadge{
background: gainsboro;
}
.not-unread-list{
.msg-title{
color: rgb(170, 169, 169);
}
.ivu-menu-item{
.ivu-btn.ivu-btn-text.ivu-btn-small.ivu-btn-icon-only{
display: none;
}
&:hover{
.ivu-btn.ivu-btn-text.ivu-btn-small.ivu-btn-icon-only{
display: inline-block;
}
}
}
}
}
}
</style>

View File

@ -30,7 +30,7 @@ export default {
methods: {
beforeUpload (file) {
getArrayFromFile(file).then(data => {
let {columns, tableData} = getTableDataFromArray(data)
let { columns, tableData } = getTableDataFromArray(data)
this.columns = columns
this.tableData = tableData
}).catch(() => {

View File

@ -11,6 +11,7 @@ const resolve = dir => {
// 如果您的应用程序部署在子路径中,则需要在这指定子路径
// 例如https://www.foobar.com/my-app/
// 需要将它改为'/my-app/'
// iview-admin线上演示打包路径 https://file.iviewui.com/admin-dist/
const BASE_URL = process.env.NODE_ENV === 'production'
? '/'
: '/'
@ -33,7 +34,7 @@ module.exports = {
.set('@', resolve('src')) // key,value自行定义比如.set('@@', resolve('src/components'))
.set('_c', resolve('src/components'))
},
// 打包时不生成.map文件
// 设为false打包时不生成.map文件
productionSourceMap: false
// 这里写你调用接口的基础路径来解决跨域如果设置了代理那你本地开发环境的axios的baseUrl要写为 '' ,即空字符串
// devServer: {