Compare commits

..

122 Commits

Author SHA1 Message Date
Vben
c82040425d chore: release 2.7.0 2021-08-03 07:41:23 +08:00
Vben
5e17cc8802 refactor: refactor tailwindcss to windicss 2021-08-03 07:40:29 +08:00
Dack Wang
02e7756062 fix(): The Style of tableTitle slot (#1023)
Co-authored-by: 王英琦 <wangyingqi@91bihu.com>
2021-08-03 07:36:40 +08:00
Leo Caan (陈栋)
72f953c8d3 fix(table): recursive updateTableDataRecord (#1024)
无刷新更新表格数据时,支持递归查找,用于树状数据时。同时新增 findTableDataRecord 函数,用于支持无刷新新增数据到树状表格中
2021-08-03 07:36:15 +08:00
yanzhuang
d76cfd7f80 fix: fix iframe heigth error (#1012) 2021-08-01 08:51:52 +08:00
Lowell
2fd0fd281e fix: the position of tinymce upload image is wrong (#1015) 2021-08-01 08:51:01 +08:00
Li Li
be2d11d5d3 fix: Fix the invalid hot update of BasicButton when changing style outside (#1016)
* chore: ignore bak dir

Signed-off-by: LiLi <urfreespace@gmail.com>

* fix: 修复BasicButton在外部改变样式热更新无效问题

Signed-off-by: LiLi <urfreespace@gmail.com>

* chore: remove extra ignore

Signed-off-by: LiLi <urfreespace@gmail.com>

* chore: ignore fix

Signed-off-by: LiLi <urfreespace@gmail.com>
2021-08-01 08:50:21 +08:00
vben
88451565db chore: update deps 2021-08-01 08:46:27 +08:00
无木
26d9476caf feat(use-loading): add setTip method
为useLoading添加setTip方法
2021-07-30 16:59:52 +08:00
无木
ddd1893b11 fix(tree): fix checkAll effects disabled node 2021-07-30 15:25:13 +08:00
Wang Weitao
e8eefd1bca fix: typo for utils/env (#1004) 2021-07-29 22:43:04 +08:00
无木
8879ae8d77 fix(page-wrapper): fix class not working 2021-07-29 20:12:54 +08:00
无木
f2b8bb43a0 fix(table): fix getSelectRows for treeTable
修复getSelectRows不支持树形表格子级数据的问题

fixed: #1003
2021-07-29 18:37:19 +08:00
vben
d2c361803b chore: update .env 2021-07-29 00:09:20 +08:00
vben
e2664f6002 perf: improve legacy compatibility 2021-07-29 00:06:35 +08:00
Lowell
225bd4c39d fix: style property of actionColOpt is invalid (#997) 2021-07-28 23:09:25 +08:00
江麻妞
7e6a89ffeb fix: typo (#980) 2021-07-28 23:08:54 +08:00
周旭
189bc6feb3 feat: Added support for tailwindcss night mode mechanism (#998)
* feat: 新增支持 tailwindcss 的夜间模式支持

(cherry picked from commit 1de8704b61c38c92bc6877d0bec9e6f67766b3c8)

* docs: update changelog
2021-07-28 23:08:41 +08:00
无木
a544dd3e58 fix: auto remove script dom in useScript
修复useScript未能自动移除script节点的问题
2021-07-28 11:24:26 +08:00
无木
0065ab0b2d style(header): fix notify icon color in dark
修复暗色header中的通知图标的颜色问题
2021-07-28 11:09:53 +08:00
无木
e32789373e fix(table): fix pagination props working
修复table的pagination属性无法动态设置数据的问题
2021-07-27 17:10:06 +08:00
无木
c375e32305 fix(demo): fix roles mock data 2021-07-27 12:05:14 +08:00
无木
4c9e8564bd chore: add chinese issue template 2021-07-26 23:11:10 +08:00
无木
bdf0be81b0 type(preview): fix type definition
修复Preview组件的事件参数类型定义
2021-07-26 21:18:55 +08:00
无木
e23bd2696d feat(preview): add more features
为Preview组件添加新的属性及事件
2021-07-26 20:58:18 +08:00
vben
98749ec6fe chore: update deps 2021-07-25 09:07:53 +08:00
Lowell
98c206d9c9 fix: expandIcon slot of BasicTable component is invalid (#975) 2021-07-25 09:01:39 +08:00
江麻妞
fe4eae3714 fix(qrcode): Fix the problem that the QR code cannot be dynamically generated (#974)
* fix: Fix the problem that the QR code cannot be dynamically generated

* Fix the problem that the size of the QR code is automatically changed when dynamically generated
2021-07-25 09:01:09 +08:00
无木
14fb21d0b7 fix(table): fix expand style
修复启用expandRowByClick展开图标样式问题

fixed: #969
2021-07-24 20:09:10 +08:00
无木
dce3fb0f20 fix(table): fix tableSettings popup in fullscreen mode
修复全屏模式下的表格设置组件的弹出层配置
2021-07-24 16:11:17 +08:00
无木
a5a9b3fb34 fix(table): fix dataPicker show in fullscreen mode
修复basicTable编辑组件的校验提示以及日期选择框的弹出层在全屏模式下的显示问题
2021-07-24 14:48:09 +08:00
无木
a07ab6d7aa fix(table): component shown in fullscreen mode
修复表格在全屏模式下编辑组件弹出层的显示问题
2021-07-24 14:36:16 +08:00
无木
9b8f165a36 fix(table-action): stopButtonPropagation not working
修复TableAction组件的stopButtonPropagation属性无效的问题
2021-07-24 13:27:10 +08:00
无木
e1c47233ed fix(demo): multi-modal used with dynamic component
修复以动态组件的方式使用多个modal的演示
2021-07-23 15:55:15 +08:00
loujohn
956ed2e3f7 fix(): Fix vite profile hot update error reporting (#968)
修复vite修改配置文件热更新报错Error: Must provide a proper URL as target

Co-authored-by: lou <825681860@qq.com>
2021-07-23 11:58:02 +08:00
无木
8702965057 fix(demo): account page form validation
修复账号管理页面对不可见表单域的验证问题
2021-07-23 09:57:42 +08:00
无木
cebc6a590e fix(login): fix auto fill style in dark-theme
修复黑暗主题下的自动填充表单域样式
2021-07-23 09:33:14 +08:00
SUPER_AI
525484e7a4 fix(echarts): fix graphic config cannot be used in echarts options (#959) 2021-07-23 07:29:53 +08:00
wl19
350c85accf fix(utils): The date function gets a non-date when the parameter is null (#954)
Co-authored-by: “weilin <“784742294@qq.com>
2021-07-23 07:29:39 +08:00
无木
202aa42b8d fix(table): editable cell display with validation
修复带验证的可编辑表格的组件意外失焦的问题

fixed: #953
2021-07-23 01:18:55 +08:00
无木
8d185bb584 fix(form): remove console error for setFieldsValue
移除setFieldsValue方法输出的控制台错误

fixed: #952
2021-07-23 01:13:10 +08:00
无木
27207a78ca fix: fixed moment locale config
修复moment的英文语言配置
2021-07-23 01:02:05 +08:00
无木
d1e0e8bcea fix(dark-theme): fixed TreeSelect & DatePicker theme
修复黑暗主题下的组件样式

fixed: #955
2021-07-23 00:35:40 +08:00
M69w
d3f08e37c5 fix(style): fix checkbox-checked css in dark mode 2021-07-22 20:52:55 +08:00
vben
ee44d99c74 chore: update deps 2021-07-21 23:45:04 +08:00
Lan
9c4889f085 fix(perm-guard): Fix the problem that the routing query is lost after refreshing the page (#941) 2021-07-21 23:27:24 +08:00
无木
59cf860564 style(alert): fix alert border color
修复Alert组件的默认边框颜色不正确的问题
2021-07-21 23:19:44 +08:00
无木
662b576ac2 fix(dark-theme): style for checked tree nodes
修复黑暗主题下已勾选的Tree的复选框的样式问题
2021-07-21 22:43:47 +08:00
无木
42812162c4 fix(dark-theme): disabled link button color
修复黑暗主题下禁用状态的link类型按钮的颜色
2021-07-21 21:51:48 +08:00
无木
9b7ede09b9 fix(dark-theme): alert color in dark-theme
修复Alert组件在Dark主题下的颜色问题
2021-07-21 21:39:23 +08:00
无木
8cf004a5f5 fix(dark-theme): bgcolor of selected tree node in dark theme
修复tree的被选中节点的背景颜色在黑暗模式下不正确的问题

fixed: #949
2021-07-21 20:34:00 +08:00
无木
32d64dbe81 fix(img-rotate-drag-verify): fix resume method support
修复ImgRotateDragVerify组件的resume方法无效的问题

fixed: #946
2021-07-21 17:52:07 +08:00
无木
8b4b767f4c feat(demo): add async-validator demo
添加表单使用后端接口异步验证的例子
2021-07-21 15:44:46 +08:00
无木
341bd633d8 fix(demo): menu error-log link to 404 page
修复演示菜单的错误日志页面指向了404的问题
2021-07-21 11:20:40 +08:00
无木
571f28138f fix(form): fix validate promise catch
修复自动校验时没有捕获promise catch的错误提示
2021-07-21 11:18:10 +08:00
无木
4bb506fb1f fix(table): fix editable cell not support ellipsis
修复可编辑单元格不支持ellipsis配置的问题

fixed: #944
2021-07-21 00:43:37 +08:00
无木
c734f6858d fix(api-tree-select): auto reload while params changed
修复ApiTreeSelect组件没有正确监听params变化的问题
2021-07-20 18:01:18 +08:00
vben
596670dc88 chore: release 2.6.1 2021-07-19 23:56:14 +08:00
vben
680ad0763c chore: restore vite to version 2.3.6 2021-07-19 23:54:47 +08:00
无木
7a7dab0c4b feat(demo): multi-modal in one page usage
添加使用is动态组件来在页面内使用多个modal的演示
2021-07-19 18:40:02 +08:00
无木
59eb828d4d style: fixed line break style
修正换行符
2021-07-19 16:37:47 +08:00
无木
52af1dd0d4 feat(basic-table): add ApiTreeSelect edit component
为表格添加ApiTreeSelect编辑组件,修复一些其它的已知问题
2021-07-19 16:25:56 +08:00
无木
897bed9729 fix(api-select): fix options-change event data
修复options-change事件参数不是select所使用的标准options数据的问题
2021-07-19 16:19:51 +08:00
无木
a764a95ae9 fix(countdown-input): add slots support
为CountdownInput组件添加Input的插槽支持
2021-07-19 15:27:05 +08:00
无木
535bdddf91 fix(demo): fix display problem of editable table with apiSelect
修复ApiSelect在可编辑表格中的显示问题
2021-07-19 00:51:02 +08:00
vben
18567e13a6 chore: update deps 2021-07-19 00:14:53 +08:00
lzdjack
03b17a8f8b fix(formItem): Fix labelcol type mismatch (#903)
*修复antdv升级后formItem中labelCol没有类型style的bug
2021-07-18 23:25:51 +08:00
无木
8832a074dc fix(code-editor): value not support use as v-model
修复value不支持v-model用法的问题

fixed: #933
2021-07-17 18:01:23 +08:00
无木
61ce25be1b fix(table): value show problem in editable cell
修复可编辑单元格的值不能直接通过修改dataSource来更新显示的问题。

fixed: #922
2021-07-16 13:59:55 +08:00
无木
d9d0071401 fix(api-tree-select): fix event checked in form
修复ApiTreeSelect在BasicForm内使用时可能出现的onChange类型检查失败的警告
2021-07-16 13:17:25 +08:00
无木
f8440175f3 fix(model): auto validate on value change
修复BasicModel的表单值发生变化时未能自动校验

fixed: #920
2021-07-16 13:14:24 +08:00
无木
5baaa58581 fix(modal): fixed fullscreen not worked
修复全屏功能异常的问题

fixed: #918
2021-07-16 11:22:12 +08:00
无木
f707541dda fix(tree): fixed checkedKeys with search mode
修复搜索状态的切换导致的勾选值可能不正确的问题
2021-07-16 10:56:37 +08:00
无木
b06a7ab77b fix(basic-tree): checkedKeys not worked with search
修复搜索功能可能导致`checkedKeys`丢失的问题

fixed: #915
2021-07-15 23:17:31 +08:00
无木
1b3058f825 fix(api-tree-select): auto load data if necessary
修正ApiTreeSelect的数据加载时机
2021-07-15 18:39:54 +08:00
无木
d81db890df feat(api-tree-select): add api options to tree-select
添加ApiTreeSelect组件
2021-07-15 18:05:13 +08:00
无木
c1178027f0 fix: fix homePage affix error
修复当没有通过接口为用户指定首页时,如果默认的首页是一个带有重定向的路由,则可能出现双首页Tab的问题
2021-07-15 17:15:15 +08:00
无木
db7254a5e0 fix(table-action): fix circle button style
修复table-action组件内的圆形按钮内容没有居中的问题
2021-07-15 15:42:47 +08:00
无木
dc51e6a8d4 fix(table-action): fixed icon margin without label
修复当没有label时,icon没有在按钮内居中的问题
2021-07-15 14:36:40 +08:00
无木
4b46a84c2b fix: infinite redirect in BACK mode
修复后端权限模式下的路由无限重定向的问题
2021-07-15 01:59:23 +08:00
无木
87583c8b54 fix: ensure PAGE_NOT_FOUND_ROUTE exist
修复某些情况下404路由可能白屏的问题
2021-07-14 21:37:10 +08:00
无木
1e63379088 fix(multiple-tab): ignore login page
修复标签页可能会创建登录页面标签的问题
2021-07-14 20:44:52 +08:00
无木
237e65eac9 fix: resolving Vue Router warn
移除因为动态加载路由而产生的vue-router警告
2021-07-14 20:32:58 +08:00
无木
6350224a1b style(basic-table): remove scroller patcher
移除table滚动条样式覆盖
2021-07-14 16:37:53 +08:00
Vben
ae7821e296 fix(modal): ensure that props are passed correctly,fix #897 2021-07-13 22:46:01 +08:00
yanzhuang
a1d956d369 fix(useWatermark): fix func call createWatermark call clear to resizeEvent removed (#901) 2021-07-13 22:23:11 +08:00
无木
35e1347029 fix(markdown): set value error
修复markdown组件在完成初始化之前动态设置value可能导致异常的问题
2021-07-13 18:04:42 +08:00
无木
d95815b503 fix(markdown): resolving markdown exceptions
修复markdown组件的异常以及不能正确设置value的问题
2021-07-13 15:28:10 +08:00
无木
0a3683a186 feat: customized user home page
新增自定义的用户首页(可以每个用户都不相同)
2021-07-13 14:10:31 +08:00
无木
f5e31febbd fix(breadcrumb): redirect not worked
修复面包屑组件的重定向菜单不能工作以及eslint警告
2021-07-13 11:09:00 +08:00
周旭
6f830703a2 perf(menu): Optimize the style of the bottom collapse button in the Mix menu layout (#896) 2021-07-13 09:25:46 +08:00
shisan
bfb5ebd7b8 chore: update deps 2021-07-13 00:07:10 +08:00
无木
012020e51c style(notice-list): adjust style
去除通知列表组件标题和内容部分多余的margin-bottom,禁止自动隐藏不可用的翻页按钮
2021-07-12 22:33:28 +08:00
无木
aeebfc4d3d style(notice-list): fix margin-bottom value
去除通知列表组件标题和内容部分多余的margin-bottom
2021-07-12 22:22:33 +08:00
无木
c16be2c499 feat(notice-list): add pagination support
为通知列表组件添加分页、超长自动省略、标题点击响应、标题删除线等功能

fixed: #894
2021-07-12 22:12:16 +08:00
无木
0f28e803d0 fix(table-action): incorrect button color of disabled state
修复表格操作列的按钮在disabled状态下的颜色显示

fixed: #891
2021-07-12 16:46:27 +08:00
无木
cad021c34b fix(menu): fix mix-menu incorrect jumping in hover mode
修复悬停触发模式下左侧混合菜单会在没有子菜单且被激活时直接跳转路由
2021-07-09 19:20:05 +08:00
无木
5ceeefd17d fix(menu): display error when contains hidden items
修复顶栏菜单在包含隐藏项目时的显示问题
2021-07-09 17:25:38 +08:00
无木
a9bbed1973 fix(form): fix suffix slot style
修复suffix插槽的样式问题
2021-07-09 16:39:50 +08:00
无木
0595a72da9 fix(mix-sider): fix mix-sider hover logic
修复左侧混合菜单的悬停处理逻辑
2021-07-09 13:56:33 +08:00
无木
c7c0a7e4c8 fix(table): fix index column style
修复序号列的样式问题
2021-07-09 13:37:13 +08:00
无木
05329ce950 fix(upload): ensure the value type is correct
修复BasicUpload组件在设置null值时的问题
2021-07-09 00:22:00 +08:00
周旭
7b76945bff chore: perf TableAction.vue、build/utils.ts、prettier.config.js (#868)
* perf: 优化 build 时 vite 模式判断

* perf: 优化 TableAction, 仅在 action.tooltip 存在的情况下 才使用 Tooltip 组件

* docs: 仅在 action.tooltip 存在的情况下 才使用 Tooltip 组件

* fix: 在 window 上,拉取代码后 eslint 因 endOfLine 而保错问题

* docs: 修复在 window 上,拉取代码后 eslint 因 endOfLine 而保错问题
2021-07-08 23:20:49 +08:00
无木
540423ecf7 feat(table): add headerTop slot
为表格添加`headerTop`插槽(表格头部的标题之上),以及相关演示

close: #881
2021-07-08 20:16:22 +08:00
无木
9cf070dd63 feat(api-select): clear options before fetch
ApiSelect组件在发起新的请求之前先清空已有的options
2021-07-08 02:50:33 +08:00
无木
41e6d94b3b feat(demo): add search demo for apiSelect
添加ApiSelect的本地搜索和远程搜索例子
2021-07-08 02:46:15 +08:00
Vben
17e47e074e chore: add a multi-environment configuration example 2021-07-07 23:47:47 +08:00
Vben
dafcdd898c fix: ensure that safari is running properly, fix #875 2021-07-07 23:40:29 +08:00
Vben
5bce6528ba chore: update deps 2021-07-07 22:59:35 +08:00
无木
1e61da644f fix(table): fix tree node align
修复树形表格的带有展开图标的单元格的内容对齐问题

fixed: #829
2021-07-07 22:38:00 +08:00
无木
9228282ae2 fix(demo): form pages support keepAlive
修复表单演示页面不支持keepAlive的问题
2021-07-07 21:33:08 +08:00
无木
45a94e41c1 fix(demo): resolve key not exist warnings
修复角色编辑页面可能会出现tree组件报key not exist警告的问题
2021-07-07 21:26:20 +08:00
无木
542121129e feat(demo): add basicTree with async data expand all
演示basicTree使用异步数据并自动展开
2021-07-06 17:10:19 +08:00
Vben
cf840e3e73 perf: image compression configuration optimization 2021-07-06 00:30:16 +08:00
MARVIN
82eb72bbce fix(CountTo): Fix displaying empty string when the value is 0 (#864) 2021-07-05 22:45:13 +08:00
无木
5f1a6cdc59 feat(demo): demo default expanded tree table
演示默认展开树形表格数据
2021-07-05 16:23:58 +08:00
无木
02d3dca57e fix(app-search): exclude items by hideChildrenInMenu
修复菜单搜索组件可能会显示被隐藏的子菜单的问题
2021-07-05 14:50:41 +08:00
无木
faf5c9fd7e fix(app-search): exclude hidden items
修复菜单搜索组件可能会显示被隐藏的菜单的问题
2021-07-05 14:34:49 +08:00
无木
d5d5c4b4bf fix(demo): setup page route config
修复引导页的相关路由配置
2021-07-05 12:18:05 +08:00
无木
993e19dcc3 fix(demo): add mock data account detail route
添加mock数据中缺失的账号详情路由

fixed: #858
2021-07-05 12:04:36 +08:00
无木
808291b503 fix: menuSetting can not set collapsed to false as default
修复无法通过将菜单配置为默认折叠的问题.
2021-07-05 11:07:27 +08:00
无木
d8c38207c0 fix(table): scrollbar style 2021-07-05 00:34:23 +08:00
143 changed files with 3416 additions and 2625 deletions

36
.env.test Normal file
View File

@@ -0,0 +1,36 @@
NODE_ENV=production
# Whether to open mock
VITE_USE_MOCK = true
# public path
VITE_PUBLIC_PATH = /
# Delete console
VITE_DROP_CONSOLE = true
# Whether to enable gzip or brotli compression
# Optional: gzip | brotli | none
# If you need multiple forms, you can use `,` to separate
VITE_BUILD_COMPRESS = 'none'
# Whether to delete origin files when using compress, default false
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
# Basic interface address SPA
VITE_GLOB_API_URL=/basic-api
# File upload address optional
# It can be forwarded by nginx or write the actual address directly
VITE_GLOB_UPLOAD_URL=/upload
# Interface prefix
VITE_GLOB_API_URL_PREFIX=
# Whether to enable image compression
VITE_USE_IMAGEMIN= true
# use pwa
VITE_USE_PWA = false
# Is it compatible with older browsers
VITE_LEGACY = false

View File

@@ -61,6 +61,7 @@ module.exports = defineConfig({
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/require-default-prop': 'off',
'vue/script-setup-uses-vars': 'off',
'vue/html-self-closing': [
'error',
{

28
.github/ISSUE_TEMPLATE/3-bug-cn.md vendored Normal file
View File

@@ -0,0 +1,28 @@
---
name: 🐛 Bug 报告
about: 向我们报告一个Bug以帮助我们改进
title: ''
labels: 'bug: pending triage'
assignees: ''
---
**⚠️ 重要 ⚠️ 在进一步操作之前,请检查下列选项。如果您忽视此模板或者没有提供关键信息,您的 Issue 将直接被关闭**
- [ ] 已阅读 [文档](https://anncwb.github.io/vue-vben-admin-doc/).
- [ ] 确保您的代码已是最新或者所报告的 Bug 在最新版本中可以重现. (部分 Bug 可能已经在最近的代码中修复)
- [ ] 已在 Issues 中搜索了相关的关键词
- [ ] 不是 ant design vue 组件库的 Bug
### 描述 Bug
请清晰地描述此 Bug 的具体表现。
### 复现 Bug
请描述在演示页面中复现 Bug 的详细步骤,以确保我们可以理解并定位问题。部分 Bug 如果未在 Demo 中涉及,请务必提供关键代码
## 系统信息
- 操作系统:
- Node 版本:
- 包管理器 (npm/yarn/pnpm) 及其版本:

21
.vscode/settings.json vendored
View File

@@ -130,6 +130,25 @@
"sider",
"pinia",
"sider",
"nprogress"
"nprogress",
"INTLIFY",
"stylelint",
"esno",
"vitejs",
"sortablejs",
"mockjs",
"codemirror",
"iconify",
"commitlint",
"vditor",
"echarts",
"cropperjs",
"logicflow",
"vueuse",
"zxcvbn",
"lintstagedrc",
"brotli",
"tailwindcss",
"sider"
]
}

View File

@@ -1,3 +1,85 @@
## 2.7.0(2021-08-03)
## (Breaking changes) Breaking changes
- Restore the project `tailwindcss` back to `windicss`, tried `tailwindcss`, there may be a lot of problems, first switch back to `windicss` to improve development efficiency and lower switching costs.
- There are currently incompatible areas of the project
- The wording of `xl:!m-4` needs to be changed to `!xl:m-4`, note that only `!` is incompatible. If you dont use it, you dont need to change it.
- The memory overflow problem may still exist (low frequency, just restart, restart vite faster)
### ✨ Features
- **Preview** Add new properties and events
- **Dark Theme** added support for tailwindcss night mode
- **Others** add setTip method for useLoading
### 🐛 Bug Fixes
- **ApiTreeSelect** Fixed the problem of failing to monitor `params` changes correctly
- **ImgRotateDragVerify** Fix the problem that the component `resume` method cannot be called
- **TableAction** Fix the problem that the stopButtonPropagation property does not work in some cases
- **PageWrapper** Fix the problem of invalid `class` attribute
- **BasicTree** Fix the problem that the `checkAll` method will affect the `disabled` state node
- **BasicTable**
- Fix the issue that editable cells do not support `ellipsis` configuration
- Fixed the problem that the pop-up layer of sub-components (popconfirm and edit components such as select and treeSelect) cannot be seen in full-screen mode
- Fixed an issue where when `expandRowByClick` is enabled, clicking non-expandable rows may cause style errors
- Fix the problem that the dynamic change of `pagination` property does not take effect
- Fix the problem that `getSelectRows` does not support the child data of the tree table -**Dark Theme** Fix the color matching problem under the dark theme
- Fix the background color of the selected node of the `Tree` component
- Fix the color configuration of the `Alert` component
- Fix the problem of the button color of `link` type in the disabled state
- Fix the style problem of checked checkboxes in `Tree` -**Others** Fix the problem that useScript failed to automatically remove the script node
## 2.6.1(2021-07-19)
### ✨ Features
- **NoticeList** Add pagination, auto omit for overlength, title click event, title strikethrough, etc.
- **MixSider** Optimize the style of the bottom collapse button in the Mix menu layout to be consistent with the style of other menu layouts
- **ApiTreeSelect** Extend `TreeSelect` component of `antdv` to support remote data source, similar to `ApiSelect`.
- **BasicTable** New `ApiTreeSelect` editing component
- Different backend home pages can be specified for different users.
- Add `homePath` field (optional) to the user information returned by the `getUserInfo` interface to customize the home page path for the current user
### 🐛 Bug Fixes
- **BasicTable**
- Fix scrollbar style issue (removed scroll style patch)
- Fix the alignment problem of cells with expanded icons in tree tables
- Add `headerTop` slot.
- Fix the color display of the operation column button in disabled state.
- Repair the problem that the values of editable cells cannot be updated by modifying `dataSource` directly.
- Repair the problem of data replay when using `ApiSelect` to edit components.
- Repair the problem that editing components may report `onXXX` type error in some scenarios.
- **TableAction**
- Create Tooltip component only if `action.tooltip` exists.
- Fix the problem that the content of the round button inside the component is not centered
- **AppSearch** Fix the problem that the hidden menu may be searched.
- **BasicUpload** Repair the problem of error when handling non-`array` values.
- **Form** Repair the `suffix` slot style problem of `FormItem`.
- **Menu**
- Repair the hovering trigger logic of the left mixed menu
- Repair the problem that the top bar menu is wrong when displaying menu items that need to be hidden.
- Fix the left mixed menu in hover trigger mode will jump to route directly when there is no submenu and it is activated
- **Breadcrumb** Repair the problem that the menu with redirection cannot be jumped when clicked
- **Markdown** fixes an initialization exception and an issue where value was not set dynamically correctly
- **Modal** Make sure props are passed correctly
- **MultipleTab** fixes an issue that could accidentally create login route tabs
- **BasicTree** Fix the problem that the search function may cause `checkedKeys` to be lost
- **CodeEditor** Fix the problem that value does not support v-model usage.
- **CountdownInput** Fix the problem that `input` slot is not supported.
- **ApiSelect** Fix the problem that the `options-change` event parameter is not the standard `options` data used by `select
- **Other**
- Fix the problem that the configuration of default menu collapse does not work
- Repair the problem that `safari` browser reports an error and the website cannot be opened.
- Repair the problem that eslint keeps error due to endOfLine after pulling the code on window.
- Fix `Vue Router warn` caused by dynamic routing
### 🎫 Chores
- Add test environment test command
## 2.6.0(2021-07-04)
### ✨ Features

View File

@@ -1,3 +1,246 @@
# [2.7.0](https://github.com/anncwb/vue-vben-admin/compare/v2.5.9...v2.7.0) (2021-08-03)
### Bug Fixes
- The Style of tableTitle slot ([#1023](https://github.com/anncwb/vue-vben-admin/issues/1023)) ([02e7756](https://github.com/anncwb/vue-vben-admin/commit/02e77560624cc4a95a5a20ffd5e0601f1f88c6f4))
- **api-select:** ensure that the onchange function parameters are correct ([fa64fc8](https://github.com/anncwb/vue-vben-admin/commit/fa64fc8a622832b87fdf672965d55d543b5929a2))
- **app-search:** exclude hidden items ([faf5c9f](https://github.com/anncwb/vue-vben-admin/commit/faf5c9fd7ea40c407419a5a5c473f9b0c32c2a53))
- **app-search:** exclude items by `hideChildrenInMenu` ([02d3dca](https://github.com/anncwb/vue-vben-admin/commit/02d3dca57efedc1322ae38e3f432cf1f6c2cf839))
- **axios:** option `withToken` not work ([d509e89](https://github.com/anncwb/vue-vben-admin/commit/d509e897be5753c852e912112e70dac6247ba467))
- **breadcrumb:** `redirect` not worked ([f5e31fe](https://github.com/anncwb/vue-vben-admin/commit/f5e31febbd18372a34166cac390b1d9b914fe80e))
- **comp-tree:** support comp-tree-foreach stop,add insertNodesByKey ([#818](https://github.com/anncwb/vue-vben-admin/issues/818)) ([d97aa92](https://github.com/anncwb/vue-vben-admin/commit/d97aa927417bf45a7c127ecfa9b8e835b6b68855))
- **CountTo:** Fix displaying empty string when the value is 0 ([#864](https://github.com/anncwb/vue-vben-admin/issues/864)) ([82eb72b](https://github.com/anncwb/vue-vben-admin/commit/82eb72bbced931ba7f50069211f9511035ad09f4))
- **demo:** `setup` page route config ([d5d5c4b](https://github.com/anncwb/vue-vben-admin/commit/d5d5c4b4bfb3e3a5e54f9993966adc46a09a8b90))
- **demo:** account list fetch loss param ([424b171](https://github.com/anncwb/vue-vben-admin/commit/424b171e0db727f5e0157cbcfd5460f15f8ea609)), closes [#830](https://github.com/anncwb/vue-vben-admin/issues/830)
- **demo:** add mock data `account detail` route ([993e19d](https://github.com/anncwb/vue-vben-admin/commit/993e19dcc319e2b4c68df2ab76174b7b4d7b0428)), closes [#858](https://github.com/anncwb/vue-vben-admin/issues/858)
- **demo:** fix async tree demo, fixed: [#823](https://github.com/anncwb/vue-vben-admin/issues/823) ([5637588](https://github.com/anncwb/vue-vben-admin/commit/5637588fce880b01137191cc82c73e0fce621e8c))
- **demo:** form pages support `keepAlive` ([9228282](https://github.com/anncwb/vue-vben-admin/commit/9228282ae27daaa246f42e441e27b1b05eb30464))
- **demo:** resolve `key not exist` warnings ([45a94e4](https://github.com/anncwb/vue-vben-admin/commit/45a94e41c1397b84d08373f84f766204d2488714))
- **demo:** style error,fix [#806](https://github.com/anncwb/vue-vben-admin/issues/806) ([a2d8be3](https://github.com/anncwb/vue-vben-admin/commit/a2d8be3ab29da88126f3ba971f6893cb12327759))
- **demo-form:** add fieldMapToTime example,fix [#807](https://github.com/anncwb/vue-vben-admin/issues/807) ([a2a75a0](https://github.com/anncwb/vue-vben-admin/commit/a2a75a097ff6c9df12471eff0d62d44d2b88cfff))
- **design:** correct tailwind configuration,fix [#800](https://github.com/anncwb/vue-vben-admin/issues/800) ([aec230c](https://github.com/anncwb/vue-vben-admin/commit/aec230ca19d541079b64c54ba00596ef9cd92ca0))
- **drawer:** openDrawer is not normal in some cases ([941ad59](https://github.com/anncwb/vue-vben-admin/commit/941ad59759cbd5a5e39bcdf29783d8eea85caf72))
- **dropdown:** icon and trigger work unexpected ([60b80c9](https://github.com/anncwb/vue-vben-admin/commit/60b80c96e82da9101d56b2e195e9e7571de11f0a)), closes [#796](https://github.com/anncwb/vue-vben-admin/issues/796) [#787](https://github.com/anncwb/vue-vben-admin/issues/787)
- **form:** fix `suffix` slot style ([a9bbed1](https://github.com/anncwb/vue-vben-admin/commit/a9bbed19739376ab2bf67a14b04e872f14ca84cc))
- **form:** fix some prop declaration ([b5046f0](https://github.com/anncwb/vue-vben-admin/commit/b5046f07a27e8ca7fc8b961b74fa5e1b0d715149))
- **lock-screen:** ensure lock info is saved ([d38ff66](https://github.com/anncwb/vue-vben-admin/commit/d38ff6670a37478b31447f8058e786c4b044e218))
- **lock-screen:** fix lock-screen can skip on new window ([d7b84c7](https://github.com/anncwb/vue-vben-admin/commit/d7b84c78744f7d0077a779b232e1358040b50383))
- **markdown:** resolving markdown exceptions ([d95815b](https://github.com/anncwb/vue-vben-admin/commit/d95815b5031984e224140eb1b1d46e2dbf80abc1))
- **markdown:** set `value` error ([35e1347](https://github.com/anncwb/vue-vben-admin/commit/35e1347029e29a83a9648b6b398e6863cc40fca9))
- **menu:** display error when contains hidden items ([5ceeefd](https://github.com/anncwb/vue-vben-admin/commit/5ceeefd17d9ddc0e8844b900069b100f24d9c00e))
- **menu:** fix mix-menu incorrect jumping in `hover` mode ([cad021c](https://github.com/anncwb/vue-vben-admin/commit/cad021c34b71fa109640af75a0c2b72179e9e257))
- **menu:** make sure the menu is activated correctly ([cdb10cc](https://github.com/anncwb/vue-vben-admin/commit/cdb10cc4ac5e5e8f9cce3ff18d8fbd29ef10c86f))
- **mix-sider:** fix mix-sider hover logic ([0595a72](https://github.com/anncwb/vue-vben-admin/commit/0595a72da9c666af81a0916663e8e6a014e6fa69))
- **modal:** `setModalProps` support `defaultFullscreen` ([c7de65e](https://github.com/anncwb/vue-vben-admin/commit/c7de65ebba53941771153f18b184d3d4d31c0dbf))
- **modal:** maskClosable not work ([f750ff4](https://github.com/anncwb/vue-vben-admin/commit/f750ff435fee06acee78d6b9633e6e18d91685f8))
- **modal:** remove console log ([3dbbde2](https://github.com/anncwb/vue-vben-admin/commit/3dbbde2662352780377a9b216598d9348522f6ba))
- **pop-confirm:** fix event working unexpected ([a6ef771](https://github.com/anncwb/vue-vben-admin/commit/a6ef771fcce14c3644c965afaa69b3a17d0a7087))
- **popconfirm-button:** remove button excess `title` ([73654b7](https://github.com/anncwb/vue-vben-admin/commit/73654b7862c59d623d6d5dc7dcf6ff2704564d9a))
- **sider:** bottom trigger not work ([1bde404](https://github.com/anncwb/vue-vben-admin/commit/1bde4041211229d5d9d01ce0ca806fa99356b6de)), closes [#820](https://github.com/anncwb/vue-vben-admin/issues/820)
- **sider:** custom trigger does not take effect ([5005e6e](https://github.com/anncwb/vue-vben-admin/commit/5005e6e56b1cc7763a1cc23e1162dfb49452013b))
- **svg-icon:** fix SvgIcon style ([99829c7](https://github.com/anncwb/vue-vben-admin/commit/99829c79ab41a2319f40c5595a7d82d9e406ba18))
- **table:** fix index column style ([c7c0a7e](https://github.com/anncwb/vue-vben-admin/commit/c7c0a7e4c88a895000b1621981e4d4b2020c64b1))
- **table:** auto hide unnecessary scrollbar ([735028c](https://github.com/anncwb/vue-vben-admin/commit/735028c43055e8e80ebc7344af0cd0f51c744f98))
- **table:** editComponentProps support onChange ([829b366](https://github.com/anncwb/vue-vben-admin/commit/829b366cb2abf27e69d9665e5be022b3d3f15655))
- **table:** event editCancel loss params ([8d22231](https://github.com/anncwb/vue-vben-admin/commit/8d22231a5fa4afed19201a4a4e5c29d674498516))
- **table:** fix rowSelection.onChange not work ([df0f000](https://github.com/anncwb/vue-vben-admin/commit/df0f00085c1113eddd7a15954818ccece3538068)), closes [#825](https://github.com/anncwb/vue-vben-admin/issues/825)
- **table:** fix table jitter problem ([8eba7fb](https://github.com/anncwb/vue-vben-admin/commit/8eba7fb52786d1977e4cb7b67673d74c91c5c827))
- **table:** fix tree node align ([1e61da6](https://github.com/anncwb/vue-vben-admin/commit/1e61da644f65a79ce10fde98ee017aba7d36be10)), closes [#829](https://github.com/anncwb/vue-vben-admin/issues/829)
- **table:** getDataSource not worked on empty data ([e78af6f](https://github.com/anncwb/vue-vben-admin/commit/e78af6f228e25f052dc4c5a1859a6db50e0b112e)), closes [#752](https://github.com/anncwb/vue-vben-admin/issues/752)
- **table:** global configuration accidentally modified ([b4a3f93](https://github.com/anncwb/vue-vben-admin/commit/b4a3f936cd19bf1fff3a331bacad60e79d2d6c22))
- **table:** param of `handleSearchInfoFn` ([791b323](https://github.com/anncwb/vue-vben-admin/commit/791b323dbd30acd7fabfe9c3fb6e528916311ffd))
- **table:** recursive updateTableDataRecord ([#1024](https://github.com/anncwb/vue-vben-admin/issues/1024)) ([72f953c](https://github.com/anncwb/vue-vben-admin/commit/72f953c8d3413a7f5482793258503017a81cc759))
- auto remove script dom in `useScript` ([a544dd3](https://github.com/anncwb/vue-vben-admin/commit/a544dd3e589329339177dad3d5c1f75dd6e6f0ca))
- fix iframe heigth error ([#1012](https://github.com/anncwb/vue-vben-admin/issues/1012)) ([d76cfd7](https://github.com/anncwb/vue-vben-admin/commit/d76cfd7f809ba48880c950a64cb43a5c9c44176c))
- Fix the invalid hot update of BasicButton when changing style outside ([#1016](https://github.com/anncwb/vue-vben-admin/issues/1016)) ([be2d11d](https://github.com/anncwb/vue-vben-admin/commit/be2d11d5d344a508e94abe3534726c80e1f1f271))
- style property of actionColOpt is invalid ([#997](https://github.com/anncwb/vue-vben-admin/issues/997)) ([225bd4c](https://github.com/anncwb/vue-vben-admin/commit/225bd4c39de377d93c605f33bfdf3d8fd565f12b))
- the position of tinymce upload image is wrong ([#1015](https://github.com/anncwb/vue-vben-admin/issues/1015)) ([2fd0fd2](https://github.com/anncwb/vue-vben-admin/commit/2fd0fd281e65d6d2551478c5f19250347dc14062))
- **api-select:** fix `options-change` event data ([897bed9](https://github.com/anncwb/vue-vben-admin/commit/897bed97295a0b9101d33102340749689a4368de))
- **api-tree-select:** auto load data if necessary ([1b3058f](https://github.com/anncwb/vue-vben-admin/commit/1b3058f8253effe974feaf08a12250a111ab58c0))
- **api-tree-select:** auto reload while `params` changed ([c734f68](https://github.com/anncwb/vue-vben-admin/commit/c734f6858daea6d11cd517463b06fcce58744947))
- **api-tree-select:** fix `event` checked in form ([d9d0071](https://github.com/anncwb/vue-vben-admin/commit/d9d00714011fa7914c61f990ce1159351ee21a1a))
- **basic-tree:** `checkedKeys` not worked with `search` ([b06a7ab](https://github.com/anncwb/vue-vben-admin/commit/b06a7ab77b99abee63dd55770ffd55b594ee42f9)), closes [#915](https://github.com/anncwb/vue-vben-admin/issues/915)
- **code-editor:** `value` not support use as `v-model` ([8832a07](https://github.com/anncwb/vue-vben-admin/commit/8832a074dceb44f057c87289d3a99feef58c08fd)), closes [#933](https://github.com/anncwb/vue-vben-admin/issues/933)
- **countdown-input:** add `slots` support ([a764a95](https://github.com/anncwb/vue-vben-admin/commit/a764a95ae9a6cff831f75aa97b00724cadc48e92))
- **dark-theme:** alert color in dark-theme ([9b7ede0](https://github.com/anncwb/vue-vben-admin/commit/9b7ede09b9efe4d5a15ab0cdeadac480a29c0f62))
- **dark-theme:** bgcolor of `selected tree node` in dark theme ([8cf004a](https://github.com/anncwb/vue-vben-admin/commit/8cf004a5f59895e2487c3a350c83000e585b897e)), closes [#949](https://github.com/anncwb/vue-vben-admin/issues/949)
- **dark-theme:** disabled link `button` color ([4281216](https://github.com/anncwb/vue-vben-admin/commit/42812162c46832ce4d3e332bd579c042309115bc))
- **dark-theme:** fixed `TreeSelect` & `DatePicker` theme ([d1e0e8b](https://github.com/anncwb/vue-vben-admin/commit/d1e0e8bcea1c168631222989969e14f7d0d1b6a4)), closes [#955](https://github.com/anncwb/vue-vben-admin/issues/955)
- **dark-theme:** style for checked tree nodes ([662b576](https://github.com/anncwb/vue-vben-admin/commit/662b576ac2088247cb58e295378f228462508a37))
- **demo:** account page form validation ([8702965](https://github.com/anncwb/vue-vben-admin/commit/87029650570e470431fb94d35a273c5d07a73135))
- **demo:** fix display problem of editable table with `apiSelect` ([535bddd](https://github.com/anncwb/vue-vben-admin/commit/535bdddf91785e20295c18cf80c8a22cc2172681))
- **demo:** fix roles mock data ([c375e32](https://github.com/anncwb/vue-vben-admin/commit/c375e32305eae5128e09ad1bda39ce0cc6afd790))
- **demo:** menu `error-log` link to 404 page ([341bd63](https://github.com/anncwb/vue-vben-admin/commit/341bd633d8ed38a5a357db8f97166c2eba2895d3))
- **demo:** multi-modal used with dynamic component ([e1c4723](https://github.com/anncwb/vue-vben-admin/commit/e1c47233edf7675aede6d5f023726945a510ddf7))
- **echarts:** fix graphic config cannot be used in echarts options ([#959](https://github.com/anncwb/vue-vben-admin/issues/959)) ([525484e](https://github.com/anncwb/vue-vben-admin/commit/525484e7a409b032d22231f90a92e700ef4290ae))
- **form:** fix `validate` promise catch ([571f281](https://github.com/anncwb/vue-vben-admin/commit/571f28138f782553eb39cda2d632e5ac1aa1e145))
- **form:** remove console error for `setFieldsValue` ([8d185bb](https://github.com/anncwb/vue-vben-admin/commit/8d185bb5841c83eb49c78e8342e65067aa6f9b80)), closes [#952](https://github.com/anncwb/vue-vben-admin/issues/952)
- **formItem:** Fix labelcol type mismatch ([#903](https://github.com/anncwb/vue-vben-admin/issues/903)) ([03b17a8](https://github.com/anncwb/vue-vben-admin/commit/03b17a8f8bdb50322aa10e3b614bcc40b9e9dcc8))
- **img-rotate-drag-verify:** fix `resume` method support ([32d64db](https://github.com/anncwb/vue-vben-admin/commit/32d64dbe816a0afda6ee9e91863199afb3e7b48e)), closes [#946](https://github.com/anncwb/vue-vben-admin/issues/946)
- **login:** fix `auto fill` style in dark-theme ([cebc6a5](https://github.com/anncwb/vue-vben-admin/commit/cebc6a590e1a19af7380a55aed43b23af274df0a))
- **modal:** ensure that props are passed correctly,fix [#897](https://github.com/anncwb/vue-vben-admin/issues/897) ([ae7821e](https://github.com/anncwb/vue-vben-admin/commit/ae7821e29690bea8934ea724bfd0ae4e2cf30c77))
- **modal:** fixed `fullscreen` not worked ([5baaa58](https://github.com/anncwb/vue-vben-admin/commit/5baaa58581f22a915cda9fa39e4cb9f094254d3b)), closes [#918](https://github.com/anncwb/vue-vben-admin/issues/918)
- **model:** auto validate on value change ([f844017](https://github.com/anncwb/vue-vben-admin/commit/f8440175f35076073c9f53483cf6c0164d427ff4)), closes [#920](https://github.com/anncwb/vue-vben-admin/issues/920)
- **multiple-tab:** ignore login page ([1e63379](https://github.com/anncwb/vue-vben-admin/commit/1e63379088e1d7c823f29f607ab49d62ca22cb25))
- **page-wrapper:** fix `class` not working ([8879ae8](https://github.com/anncwb/vue-vben-admin/commit/8879ae8d773e8dc4c252c4234eefeab9bc135a30))
- **perm-guard:** Fix the problem that the routing query is lost after refreshing the page ([#941](https://github.com/anncwb/vue-vben-admin/issues/941)) ([9c4889f](https://github.com/anncwb/vue-vben-admin/commit/9c4889f0859bc60decf0ef40c383c1946de1d68a))
- **qrcode:** Fix the problem that the QR code cannot be dynamically generated ([#974](https://github.com/anncwb/vue-vben-admin/issues/974)) ([fe4eae3](https://github.com/anncwb/vue-vben-admin/commit/fe4eae37146068f01ba08f033e0c2e8bd03e48b5))
- **style:** fix checkbox-checked css in dark mode ([d3f08e3](https://github.com/anncwb/vue-vben-admin/commit/d3f08e37c5b6e46f33d525e9ef4b4c235d77a192))
- **table:** `value` show problem in editable cell ([61ce25b](https://github.com/anncwb/vue-vben-admin/commit/61ce25be1b40d7a0e26205ca6a6757c6c43fc21e)), closes [#922](https://github.com/anncwb/vue-vben-admin/issues/922)
- **table:** component shown in `fullscreen` mode ([a07ab6d](https://github.com/anncwb/vue-vben-admin/commit/a07ab6d7aa1060f856649a9bdbec9dfa50b14f26))
- **table:** editable cell display with validation ([202aa42](https://github.com/anncwb/vue-vben-admin/commit/202aa42b8d5a94e84ad386bcf7feab96926c70dd)), closes [#953](https://github.com/anncwb/vue-vben-admin/issues/953)
- **table:** fix `dataPicker` show in `fullscreen` mode ([a5a9b3f](https://github.com/anncwb/vue-vben-admin/commit/a5a9b3fb34c64b6ea9c9ab3d58045f6e5963952b))
- **table:** fix `getSelectRows` for treeTable ([f2b8bb4](https://github.com/anncwb/vue-vben-admin/commit/f2b8bb43a0b9172b9ef9ced8e83bf91143a091d9)), closes [#1003](https://github.com/anncwb/vue-vben-admin/issues/1003)
- **table:** fix `pagination` props working ([e327893](https://github.com/anncwb/vue-vben-admin/commit/e32789373eb5b1b531572b59692bf552dac365dc))
- **table:** fix editable cell not support `ellipsis` ([4bb506f](https://github.com/anncwb/vue-vben-admin/commit/4bb506fb1f6ac7d246f8792d29f337ec003ff426)), closes [#944](https://github.com/anncwb/vue-vben-admin/issues/944)
- **table:** fix expand style ([14fb21d](https://github.com/anncwb/vue-vben-admin/commit/14fb21d0b7b9ac69c7b3c463de6d709bd5713d14)), closes [#969](https://github.com/anncwb/vue-vben-admin/issues/969)
- **table:** fix tableSettings popup in fullscreen mode ([dce3fb0](https://github.com/anncwb/vue-vben-admin/commit/dce3fb0f20516aaf4817281f5d08109e53a73ecb))
- **tree:** fix `checkAll` effects `disabled` node ([ddd1893](https://github.com/anncwb/vue-vben-admin/commit/ddd1893b113e13786037522341abb2e75f8f9d5b))
- ensure PAGE_NOT_FOUND_ROUTE exist ([87583c8](https://github.com/anncwb/vue-vben-admin/commit/87583c8b54d335ddf1c416859ef62bbde189c809))
- expandIcon slot of BasicTable component is invalid ([#975](https://github.com/anncwb/vue-vben-admin/issues/975)) ([98c206d](https://github.com/anncwb/vue-vben-admin/commit/98c206d9c9661a18dde4ec7782cbe1eb6e62ebeb))
- fix homePage affix error ([c117802](https://github.com/anncwb/vue-vben-admin/commit/c1178027f0fab2791d02efcd7c52beff5fc7dc25))
- Fix vite profile hot update error reporting ([#968](https://github.com/anncwb/vue-vben-admin/issues/968)) ([956ed2e](https://github.com/anncwb/vue-vben-admin/commit/956ed2e3f770cc9cf822ce80f71b1e7f179792fb))
- fixed moment locale config ([27207a7](https://github.com/anncwb/vue-vben-admin/commit/27207a78caccb04372e0275c5cee526ec460de0e))
- infinite redirect in `BACK` mode ([4b46a84](https://github.com/anncwb/vue-vben-admin/commit/4b46a84c2b85e8da799426c54b3381ae93183db4))
- resolving `Vue Router warn` ([237e65e](https://github.com/anncwb/vue-vben-admin/commit/237e65eac909368c4b4857da6c8deb1dc18e7684))
- typo ([#980](https://github.com/anncwb/vue-vben-admin/issues/980)) ([7e6a89f](https://github.com/anncwb/vue-vben-admin/commit/7e6a89ffeb8c63467908d5807d3d7c4761620ee3))
- typo for utils/env ([#1004](https://github.com/anncwb/vue-vben-admin/issues/1004)) ([e8eefd1](https://github.com/anncwb/vue-vben-admin/commit/e8eefd1bca41c181ec6395bf1d087d2018e2b1f1))
- **table:** selection-change not triggered on row click ([6f845b5](https://github.com/anncwb/vue-vben-admin/commit/6f845b53bdc4c33fbca3e65f10f64c63166bed0e))
- **table:** treeTable editable error ([4ae39c5](https://github.com/anncwb/vue-vben-admin/commit/4ae39c53b49532fc6c31086a31e30429d2e236ed)), closes [#811](https://github.com/anncwb/vue-vben-admin/issues/811)
- **table-action:** fix `circle` button style ([db7254a](https://github.com/anncwb/vue-vben-admin/commit/db7254a5e0ac6d10a7ea37334ad523b150facb19))
- **table-action:** fixed icon `margin` without label ([dc51e6a](https://github.com/anncwb/vue-vben-admin/commit/dc51e6a8d4e4f2c97b387b37959944c9bb49d779))
- **table-action:** incorrect button color of `disabled` state ([0f28e80](https://github.com/anncwb/vue-vben-admin/commit/0f28e803d0b65537216cd9f40ad5cad63c20db9b)), closes [#891](https://github.com/anncwb/vue-vben-admin/issues/891)
- **table-action:** stopButtonPropagation not working ([9b8f165](https://github.com/anncwb/vue-vben-admin/commit/9b8f165a365758d001e6d86ae7afe4ae3316d485))
- **tree:** fixed `checkedKeys` with `search` mode ([f707541](https://github.com/anncwb/vue-vben-admin/commit/f707541dda78146bda89814ddccbb259d9f5d8a2))
- **upload:** ensure the value type is correct ([05329ce](https://github.com/anncwb/vue-vben-admin/commit/05329ce9501eb899a0bbb45320e5807c83372317))
- **useWatermark:** fix `func` call `createWatermark` call `clear` to resizeEvent removed ([#901](https://github.com/anncwb/vue-vben-admin/issues/901)) ([a1d956d](https://github.com/anncwb/vue-vben-admin/commit/a1d956d3697cd07e0ba8910768f2a73e55f18491))
- `menuSetting` can not set collapsed to false as default ([808291b](https://github.com/anncwb/vue-vben-admin/commit/808291b503d59e3026f5f0b5e7a38b9c69bcc451))
- ensure that safari is running properly, fix [#875](https://github.com/anncwb/vue-vben-admin/issues/875) ([dafcdd8](https://github.com/anncwb/vue-vben-admin/commit/dafcdd898caae57104f1155b0ec660ea333e7b19))
- **table:** scrollbar style ([d8c3820](https://github.com/anncwb/vue-vben-admin/commit/d8c38207c08510d805a8dc66ffbba210e0cf4215))
- **tailwindcss:** remove console warnings ([acacb32](https://github.com/anncwb/vue-vben-admin/commit/acacb32bb592345cd0a90b4bbeb60a9b6ab1ac3c))
- `hasPermission` not work in `ROLE` Mode ([76a5f87](https://github.com/anncwb/vue-vben-admin/commit/76a5f87c0ce871cca48b9e4c32331353a796e7d2))
- fix antdv console warning ([480cfb9](https://github.com/anncwb/vue-vben-admin/commit/480cfb914e78c06eb7784e33465ed91b7d4c3eee))
- fix defHttp baseUrl work ([d5f9919](https://github.com/anncwb/vue-vben-admin/commit/d5f9919b60fdd7d5c435129e8db519c0bbd37529))
- multi windows token sharing ([e5f3788](https://github.com/anncwb/vue-vben-admin/commit/e5f37885ffb32d04d244f0ef39ac660dda6b71e1)), closes [#761](https://github.com/anncwb/vue-vben-admin/issues/761)
- routes filter can't effective when permission mode set to ROUTE_MAPPING ([#836](https://github.com/anncwb/vue-vben-admin/issues/836)) ([3871204](https://github.com/anncwb/vue-vben-admin/commit/3871204d08d481b8984440cd60bbf2bacb58d063))
- support various vite modes of build, not just production ([#832](https://github.com/anncwb/vue-vben-admin/issues/832)) ([95c16a5](https://github.com/anncwb/vue-vben-admin/commit/95c16a5d26f9fd9a1d11894afe1146ca495eee93))
- user drop-down event key loss ([20d7a25](https://github.com/anncwb/vue-vben-admin/commit/20d7a25eb898a5c28351ff269b93bf104b8ac10e))
- user dropdown event response failure ([c73694a](https://github.com/anncwb/vue-vben-admin/commit/c73694ab8b0b6242c4d5e0f30bc7ebe3d69b4e33))
- **upload:** make sure to carry custom parameters, fix [#802](https://github.com/anncwb/vue-vben-admin/issues/802) ([c4b22a2](https://github.com/anncwb/vue-vben-admin/commit/c4b22a225d0088d87be0c0068f543366312521db))
- **utils:** The date function gets a non-date when the parameter is null ([#954](https://github.com/anncwb/vue-vben-admin/issues/954)) ([350c85a](https://github.com/anncwb/vue-vben-admin/commit/350c85accf5033cc5a21b71bc36d5b7a74eb2573))
### Features
- **use-loading:** add `setTip` method ([26d9476](https://github.com/anncwb/vue-vben-admin/commit/26d9476caff41cc355190604af42e0bd2ef0a353))
- Added support for tailwindcss night mode mechanism ([#998](https://github.com/anncwb/vue-vben-admin/issues/998)) ([189bc6f](https://github.com/anncwb/vue-vben-admin/commit/189bc6feb3f2860be8c531dd1ca996f3a2cff018))
- **api-select:** clear options before fetch ([9cf070d](https://github.com/anncwb/vue-vben-admin/commit/9cf070dd6305bb69a67ab6be85ef00bddc86fda0))
- **api-tree-select:** add `api` options to tree-select ([d81db89](https://github.com/anncwb/vue-vben-admin/commit/d81db890dfeb533d60f378ddb86f8ac50a31252b))
- **avatar-cropper:** add action tooltip ([6cbac4b](https://github.com/anncwb/vue-vben-admin/commit/6cbac4b7ece60a1a7c1fda931cfffce42dfe3e51))
- **avatar-cropper:** more props added ([b96ea07](https://github.com/anncwb/vue-vben-admin/commit/b96ea0753bfd769693a368cf1e3d8316688c0dcb))
- **axios:** add `withToken` option ([c99cf5e](https://github.com/anncwb/vue-vben-admin/commit/c99cf5e53f057cdc332ab6c0635adf9c2d27de29))
- **axios:** use `defHttp` like `axios` ([49f39de](https://github.com/anncwb/vue-vben-admin/commit/49f39de7b40e3ec8343bdeaf3eb00fd79d395746)), closes [#850](https://github.com/anncwb/vue-vben-admin/issues/850)
- **basic-table:** add `ApiTreeSelect` edit component ([52af1dd](https://github.com/anncwb/vue-vben-admin/commit/52af1dd0d494e66c0af20f886dcc2b983cbb096f))
- **basic-upload:** `value` support v-model ([16c5d32](https://github.com/anncwb/vue-vben-admin/commit/16c5d327f1209f7c7437acde2ab0fa031da6a641))
- **basic-upload:** add preview-delete event ([49e72a8](https://github.com/anncwb/vue-vben-admin/commit/49e72a8e76b78fe54e19de9e23d7c72a19427f01)), closes [#835](https://github.com/anncwb/vue-vben-admin/issues/835)
- **demo:** add `async-validator` demo ([8b4b767](https://github.com/anncwb/vue-vben-admin/commit/8b4b767f4ca78f7c6f7586d8ba662552c2b7bb51))
- **demo:** add basicTree with async data expand all ([5421211](https://github.com/anncwb/vue-vben-admin/commit/542121129eb5bf65f61e7b484835591756c80f04))
- **demo:** add route multi tabs show ([0e414ba](https://github.com/anncwb/vue-vben-admin/commit/0e414ba3c10b4e47a85feb1a38cae66c815719d8)), closes [#817](https://github.com/anncwb/vue-vben-admin/issues/817)
- **demo:** add search demo for apiSelect ([41e6d94](https://github.com/anncwb/vue-vben-admin/commit/41e6d94b3b64dc0d40b7ec57ecfaa4d966f202ae))
- **demo:** demo default expanded tree table ([5f1a6cd](https://github.com/anncwb/vue-vben-admin/commit/5f1a6cdc599d5840df2dfebdaad029aac093cd81))
- **demo:** multi-modal in one page usage ([7a7dab0](https://github.com/anncwb/vue-vben-admin/commit/7a7dab0c4b3602b7bd3e9381408e4168d7494c52))
- **menu:** the route is automatically mapped to the menu ([913c22c](https://github.com/anncwb/vue-vben-admin/commit/913c22c84fc9a4221fdfff6bae0e79a68fd09b17))
- **modal:** add `tooltip` for action buttons ([c3b9076](https://github.com/anncwb/vue-vben-admin/commit/c3b907656a5fad7a9b241562179f7a0f6fe0e6f0))
- **notice-list:** add `pagination` support ([c16be2c](https://github.com/anncwb/vue-vben-admin/commit/c16be2c499d90126dfa35d699da9294c21a4ab48)), closes [#894](https://github.com/anncwb/vue-vben-admin/issues/894)
- **preview:** add more features ([e23bd26](https://github.com/anncwb/vue-vben-admin/commit/e23bd2696da945291a9b652f1af39ad1936f376b))
- customized user home page ([0a3683a](https://github.com/anncwb/vue-vben-admin/commit/0a3683a186ab55d34a12a5a3c6d794dfa1094ad4))
- **param-menu:** feature: menu with params ([#845](https://github.com/anncwb/vue-vben-admin/issues/845)) ([48fcd76](https://github.com/anncwb/vue-vben-admin/commit/48fcd7684cabff66e8648b71527c6cb4ce7d03be))
- **route:** add `hidePathForChildren` in `meta` ([d52b0de](https://github.com/anncwb/vue-vben-admin/commit/d52b0de83e69f7505c28e6f59ec84bbe526ecd0d))
- **table:** add `headerTop` slot ([540423e](https://github.com/anncwb/vue-vben-admin/commit/540423ecf741a815d28d7a6baa1541ac884efe95)), closes [#881](https://github.com/anncwb/vue-vben-admin/issues/881)
- **table:** support asynchrony in beforeFetch and afterFetch ([#827](https://github.com/anncwb/vue-vben-admin/issues/827)) ([749ba5c](https://github.com/anncwb/vue-vben-admin/commit/749ba5c1daf459625518937c239787b756c0a780))
- **table-action:** support `tooltip` option ([5fab267](https://github.com/anncwb/vue-vben-admin/commit/5fab267a69600fdf5d7a7f9e4d9fff859d09dede)), closes [#848](https://github.com/anncwb/vue-vben-admin/issues/848)
- **tree:** add `insertNodesByKey` method ([5a20df4](https://github.com/anncwb/vue-vben-admin/commit/5a20df45ad36b523d48bf7fe11bdb10a6d03df64))
- add Tree LoadData demo ([9298b3c](https://github.com/anncwb/vue-vben-admin/commit/9298b3c988c10b81d83430ca31b9ce1d98a3fad9))
- routers support `ignoreRoute` option ([72ac240](https://github.com/anncwb/vue-vben-admin/commit/72ac240f2858cd74cb62b7647ca89d63bb71d247))
### Performance Improvements
- improve legacy compatibility ([e2664f6](https://github.com/anncwb/vue-vben-admin/commit/e2664f60029f03642f8b1a6afa9b1998705fce37))
- **menu:** Optimize the style of the bottom collapse button in the Mix menu layout ([#896](https://github.com/anncwb/vue-vben-admin/issues/896)) ([6f83070](https://github.com/anncwb/vue-vben-admin/commit/6f830703a2607c33e5d25d6d17d0e453fc2fac2e))
- image compression configuration optimization ([cf840e3](https://github.com/anncwb/vue-vben-admin/commit/cf840e3e73b9572de0ba7bf7b32d83f6a353a8ad))
- **icon:** remove Icon component global registration ([59d3e8c](https://github.com/anncwb/vue-vben-admin/commit/59d3e8c80f72f029f2b90432b31901ad54ed1ee4))
- **pagewrapper:** 优化 PageWrapper 的高度自适应表现使用 getViewportOffset 替代 useContentViewHeight ([#792](https://github.com/anncwb/vue-vben-admin/issues/792)) ([4d8e398](https://github.com/anncwb/vue-vben-admin/commit/4d8e39857ea59fff99e69832b4a8cabf3a424c24))
- **router:** reduce the number of guard files ([327d71b](https://github.com/anncwb/vue-vben-admin/commit/327d71b8fb4907ae971d040f6b84bbecb0a6d897))
- **scrollbar:** scrollbar update when slot changed ([e9e51b2](https://github.com/anncwb/vue-vben-admin/commit/e9e51b2fdc879a66d8df08504a0955c9c21e3e27))
### Reverts
- **axios:** remove baseUrl config ([61d4efd](https://github.com/anncwb/vue-vben-admin/commit/61d4efd55a8b4f09990b5f1888e23ead43958164))
## [2.6.1](https://github.com/anncwb/vue-vben-admin/compare/v2.6.0...v2.6.1) (2021-07-19)
### Bug Fixes
- **api-select:** fix `options-change` event data ([897bed9](https://github.com/anncwb/vue-vben-admin/commit/897bed97295a0b9101d33102340749689a4368de))
- **api-tree-select:** auto load data if necessary ([1b3058f](https://github.com/anncwb/vue-vben-admin/commit/1b3058f8253effe974feaf08a12250a111ab58c0))
- **api-tree-select:** fix `event` checked in form ([d9d0071](https://github.com/anncwb/vue-vben-admin/commit/d9d00714011fa7914c61f990ce1159351ee21a1a))
- **app-search:** exclude hidden items ([faf5c9f](https://github.com/anncwb/vue-vben-admin/commit/faf5c9fd7ea40c407419a5a5c473f9b0c32c2a53))
- **app-search:** exclude items by `hideChildrenInMenu` ([02d3dca](https://github.com/anncwb/vue-vben-admin/commit/02d3dca57efedc1322ae38e3f432cf1f6c2cf839))
- **basic-tree:** `checkedKeys` not worked with `search` ([b06a7ab](https://github.com/anncwb/vue-vben-admin/commit/b06a7ab77b99abee63dd55770ffd55b594ee42f9)), closes [#915](https://github.com/anncwb/vue-vben-admin/issues/915)
- **breadcrumb:** `redirect` not worked ([f5e31fe](https://github.com/anncwb/vue-vben-admin/commit/f5e31febbd18372a34166cac390b1d9b914fe80e))
- **code-editor:** `value` not support use as `v-model` ([8832a07](https://github.com/anncwb/vue-vben-admin/commit/8832a074dceb44f057c87289d3a99feef58c08fd)), closes [#933](https://github.com/anncwb/vue-vben-admin/issues/933)
- **countdown-input:** add `slots` support ([a764a95](https://github.com/anncwb/vue-vben-admin/commit/a764a95ae9a6cff831f75aa97b00724cadc48e92))
- **CountTo:** Fix displaying empty string when the value is 0 ([#864](https://github.com/anncwb/vue-vben-admin/issues/864)) ([82eb72b](https://github.com/anncwb/vue-vben-admin/commit/82eb72bbced931ba7f50069211f9511035ad09f4))
- **demo:** `setup` page route config ([d5d5c4b](https://github.com/anncwb/vue-vben-admin/commit/d5d5c4b4bfb3e3a5e54f9993966adc46a09a8b90))
- **demo:** add mock data `account detail` route ([993e19d](https://github.com/anncwb/vue-vben-admin/commit/993e19dcc319e2b4c68df2ab76174b7b4d7b0428)), closes [#858](https://github.com/anncwb/vue-vben-admin/issues/858)
- **demo:** fix display problem of editable table with `apiSelect` ([535bddd](https://github.com/anncwb/vue-vben-admin/commit/535bdddf91785e20295c18cf80c8a22cc2172681))
- **demo:** form pages support `keepAlive` ([9228282](https://github.com/anncwb/vue-vben-admin/commit/9228282ae27daaa246f42e441e27b1b05eb30464))
- **demo:** resolve `key not exist` warnings ([45a94e4](https://github.com/anncwb/vue-vben-admin/commit/45a94e41c1397b84d08373f84f766204d2488714))
- **form:** fix `suffix` slot style ([a9bbed1](https://github.com/anncwb/vue-vben-admin/commit/a9bbed19739376ab2bf67a14b04e872f14ca84cc))
- **formItem:** Fix labelcol type mismatch ([#903](https://github.com/anncwb/vue-vben-admin/issues/903)) ([03b17a8](https://github.com/anncwb/vue-vben-admin/commit/03b17a8f8bdb50322aa10e3b614bcc40b9e9dcc8))
- **markdown:** resolving markdown exceptions ([d95815b](https://github.com/anncwb/vue-vben-admin/commit/d95815b5031984e224140eb1b1d46e2dbf80abc1))
- **markdown:** set `value` error ([35e1347](https://github.com/anncwb/vue-vben-admin/commit/35e1347029e29a83a9648b6b398e6863cc40fca9))
- **menu:** display error when contains hidden items ([5ceeefd](https://github.com/anncwb/vue-vben-admin/commit/5ceeefd17d9ddc0e8844b900069b100f24d9c00e))
- **menu:** fix mix-menu incorrect jumping in `hover` mode ([cad021c](https://github.com/anncwb/vue-vben-admin/commit/cad021c34b71fa109640af75a0c2b72179e9e257))
- **mix-sider:** fix mix-sider hover logic ([0595a72](https://github.com/anncwb/vue-vben-admin/commit/0595a72da9c666af81a0916663e8e6a014e6fa69))
- **modal:** ensure that props are passed correctly,fix [#897](https://github.com/anncwb/vue-vben-admin/issues/897) ([ae7821e](https://github.com/anncwb/vue-vben-admin/commit/ae7821e29690bea8934ea724bfd0ae4e2cf30c77))
- **modal:** fixed `fullscreen` not worked ([5baaa58](https://github.com/anncwb/vue-vben-admin/commit/5baaa58581f22a915cda9fa39e4cb9f094254d3b)), closes [#918](https://github.com/anncwb/vue-vben-admin/issues/918)
- **model:** auto validate on value change ([f844017](https://github.com/anncwb/vue-vben-admin/commit/f8440175f35076073c9f53483cf6c0164d427ff4)), closes [#920](https://github.com/anncwb/vue-vben-admin/issues/920)
- **table:** fix index column style ([c7c0a7e](https://github.com/anncwb/vue-vben-admin/commit/c7c0a7e4c88a895000b1621981e4d4b2020c64b1))
- **table:** `value` show problem in editable cell ([61ce25b](https://github.com/anncwb/vue-vben-admin/commit/61ce25be1b40d7a0e26205ca6a6757c6c43fc21e)), closes [#922](https://github.com/anncwb/vue-vben-admin/issues/922)
- **table-action:** fixed icon `margin` without label ([dc51e6a](https://github.com/anncwb/vue-vben-admin/commit/dc51e6a8d4e4f2c97b387b37959944c9bb49d779))
- **tree:** fixed `checkedKeys` with `search` mode ([f707541](https://github.com/anncwb/vue-vben-admin/commit/f707541dda78146bda89814ddccbb259d9f5d8a2))
- fix homePage affix error ([c117802](https://github.com/anncwb/vue-vben-admin/commit/c1178027f0fab2791d02efcd7c52beff5fc7dc25))
- **table-action:** fix `circle` button style ([db7254a](https://github.com/anncwb/vue-vben-admin/commit/db7254a5e0ac6d10a7ea37334ad523b150facb19))
- `menuSetting` can not set collapsed to false as default ([808291b](https://github.com/anncwb/vue-vben-admin/commit/808291b503d59e3026f5f0b5e7a38b9c69bcc451))
- ensure PAGE_NOT_FOUND_ROUTE exist ([87583c8](https://github.com/anncwb/vue-vben-admin/commit/87583c8b54d335ddf1c416859ef62bbde189c809))
- ensure that safari is running properly, fix [#875](https://github.com/anncwb/vue-vben-admin/issues/875) ([dafcdd8](https://github.com/anncwb/vue-vben-admin/commit/dafcdd898caae57104f1155b0ec660ea333e7b19))
- infinite redirect in `BACK` mode ([4b46a84](https://github.com/anncwb/vue-vben-admin/commit/4b46a84c2b85e8da799426c54b3381ae93183db4))
- **multiple-tab:** ignore login page ([1e63379](https://github.com/anncwb/vue-vben-admin/commit/1e63379088e1d7c823f29f607ab49d62ca22cb25))
- resolving `Vue Router warn` ([237e65e](https://github.com/anncwb/vue-vben-admin/commit/237e65eac909368c4b4857da6c8deb1dc18e7684))
- **table:** fix tree node align ([1e61da6](https://github.com/anncwb/vue-vben-admin/commit/1e61da644f65a79ce10fde98ee017aba7d36be10)), closes [#829](https://github.com/anncwb/vue-vben-admin/issues/829)
- **table:** scrollbar style ([d8c3820](https://github.com/anncwb/vue-vben-admin/commit/d8c38207c08510d805a8dc66ffbba210e0cf4215))
- **table-action:** incorrect button color of `disabled` state ([0f28e80](https://github.com/anncwb/vue-vben-admin/commit/0f28e803d0b65537216cd9f40ad5cad63c20db9b)), closes [#891](https://github.com/anncwb/vue-vben-admin/issues/891)
- **upload:** ensure the value type is correct ([05329ce](https://github.com/anncwb/vue-vben-admin/commit/05329ce9501eb899a0bbb45320e5807c83372317))
- **useWatermark:** fix `func` call `createWatermark` call `clear` to resizeEvent removed ([#901](https://github.com/anncwb/vue-vben-admin/issues/901)) ([a1d956d](https://github.com/anncwb/vue-vben-admin/commit/a1d956d3697cd07e0ba8910768f2a73e55f18491))
### Features
- **api-tree-select:** add `api` options to tree-select ([d81db89](https://github.com/anncwb/vue-vben-admin/commit/d81db890dfeb533d60f378ddb86f8ac50a31252b))
- **basic-table:** add `ApiTreeSelect` edit component ([52af1dd](https://github.com/anncwb/vue-vben-admin/commit/52af1dd0d494e66c0af20f886dcc2b983cbb096f))
- **demo:** multi-modal in one page usage ([7a7dab0](https://github.com/anncwb/vue-vben-admin/commit/7a7dab0c4b3602b7bd3e9381408e4168d7494c52))
- customized user home page ([0a3683a](https://github.com/anncwb/vue-vben-admin/commit/0a3683a186ab55d34a12a5a3c6d794dfa1094ad4))
- **api-select:** clear options before fetch ([9cf070d](https://github.com/anncwb/vue-vben-admin/commit/9cf070dd6305bb69a67ab6be85ef00bddc86fda0))
- **demo:** add basicTree with async data expand all ([5421211](https://github.com/anncwb/vue-vben-admin/commit/542121129eb5bf65f61e7b484835591756c80f04))
- **demo:** add search demo for apiSelect ([41e6d94](https://github.com/anncwb/vue-vben-admin/commit/41e6d94b3b64dc0d40b7ec57ecfaa4d966f202ae))
- **demo:** demo default expanded tree table ([5f1a6cd](https://github.com/anncwb/vue-vben-admin/commit/5f1a6cdc599d5840df2dfebdaad029aac093cd81))
- **notice-list:** add `pagination` support ([c16be2c](https://github.com/anncwb/vue-vben-admin/commit/c16be2c499d90126dfa35d699da9294c21a4ab48)), closes [#894](https://github.com/anncwb/vue-vben-admin/issues/894)
- **table:** add `headerTop` slot ([540423e](https://github.com/anncwb/vue-vben-admin/commit/540423ecf741a815d28d7a6baa1541ac884efe95)), closes [#881](https://github.com/anncwb/vue-vben-admin/issues/881)
### Performance Improvements
- **menu:** Optimize the style of the bottom collapse button in the Mix menu layout ([#896](https://github.com/anncwb/vue-vben-admin/issues/896)) ([6f83070](https://github.com/anncwb/vue-vben-admin/commit/6f830703a2607c33e5d25d6d17d0e453fc2fac2e))
- image compression configuration optimization ([cf840e3](https://github.com/anncwb/vue-vben-admin/commit/cf840e3e73b9572de0ba7bf7b32d83f6a353a8ad))
# [2.6.0](https://github.com/anncwb/vue-vben-admin/compare/v2.5.2...v2.6.0) (2021-07-04)
### Bug Fixes

View File

@@ -1,3 +1,88 @@
## 2.7.0(2021-08-03)
## (破坏性更新) Breaking changes
- 将项目`tailwindcss`还原回`windicss`,尝试了`tailwindcss`,问题可能还挺多,先切换回`windicss`提高开发效率,切换成本较低。
- 目前项目不兼容地方有
- `xl:!m-4` 之类的写法需要改为`!xl:m-4`,注意只有`!`这个不兼容,没用到则不用改
- 内存溢出问题可能还在(频率低,重启下即可,重启 vite 较快)
### ✨ Features
- **Preview** 添加新的属性及事件
- **Dark Theme** 新增对 tailwindcss 夜间模式的支持
- **其它** 为 useLoading 添加 setTip 方法
### 🐛 Bug Fixes
- **ApiTreeSelect** 修复未能正确监听`params`变化的问题
- **ImgRotateDragVerify** 修复组件`resume`方法无法调用的问题
- **TableAction** 修复 stopButtonPropagation 属性某些情况下不起作用的问题
- **PageWrapper** 修复`class`属性无效的问题
- **BasicTree** 修复`checkAll`方法会影响到`disabled`状态节点的问题
- **BasicTable**
- 修复可编辑单元格不支持`ellipsis`配置的问题
- 修复全屏模式下看不到子组件弹出层popconfirm 以及 select、treeSelect 等编辑组件)的问题
- 修复启用`expandRowByClick`时,点击不可展开的行可能会导致样式错误的问题
- 修复`pagination`属性动态改变不生效的问题
- 修复`getSelectRows`不支持树形表格子级数据的问题
- **Dark Theme** 黑暗主题下的配色问题修正
- 修复`Tree`组件被选中节点的背景颜色
- 修复`Alert`组件的颜色配置
- 修复禁用状态下的`link`类型的按钮颜色问题
- 修复`Tree`已勾选的复选框的样式问题
- **其它** 修复 useScript 未能自动移除 script 节点的问题
## 2.6.1(2021-07-19)
### ✨ Features
- **NoticeList** 添加分页、超长自动省略、标题点击事件、标题删除线等功能
- **MixSider** 优化 Mix 菜单布局时 底部折叠按钮 的样式,与其它菜单布局时的风格保持一致
- **ApiTreeSelect** 扩展`antdv``TreeSelect`组件,支持远程数据源,用法类似`ApiSelect`
- **BasicTable**
- 新增`ApiTreeSelect`编辑组件
- 新增`headerTop`插槽
- **其它** 可以为不同的用户指定不同的后台首页:
-`getUserInfo`接口返回的用户信息中增加`homePath`字段(可选)即可为当前用户定制首页路径
### 🐛 Bug Fixes
- **BasicTable**
- 修复滚动条样式问题(移除了滚动样式补丁)
- 修复树形表格的带有展开图标的单元格的内容对齐问题
- 修复操作列的按钮在 disabled 状态下的颜色显示
- 修复可编辑单元格的值不能直接通过修改`dataSource`来更新显示的问题
- 修复使用`ApiSelect`编辑组件时的数据回显问题
- 修复在部分场景下编辑组件可能会报`onXXX`类型错误的问题
- **TableAction**
- 仅在 `action.tooltip`存在的情况下 才创建 Tooltip 组件
- 修复组件内的圆形按钮内容没有居中的问题
- **AppSearch** 修复可能会搜索隐藏菜单的问题
- **BasicUpload** 修复处理非`array`值时报错的问题
- **Form** 修复`FormItem``suffix`插槽样式问题
- **Menu**
- 修复左侧混合菜单的悬停触发逻辑
- 修复顶栏菜单在显示包含需要隐藏的菜单项目时出错的问题
- 修复悬停触发模式下左侧混合菜单会在没有子菜单且被激活时直接跳转路由
- **Breadcrumb** 修复带有重定向的菜单点击无法跳转的问题
- **Markdown** 修复初始化异常以及不能正确地动态设置 value 的问题
- **Modal** 确保 props 正确被传递
- **MultipleTab** 修复可能会意外创建登录路由标签的问题
- **BasicTree** 修复搜索功能可能导致`checkedKeys`丢失的问题
- **CodeEditor** 修复 value 不支持 v-model 用法的问题
- **CountdownInput** 修复不支持`input`插槽的问题
- **ApiSelect** 修复`options-change`事件参数不是`select`所使用的标准`options`数据的问题
- **其它**
- 修复菜单默认折叠的配置不起作用的问题
- 修复`safari`浏览器报错导致网站打不开
- 修复在 window 上,拉取代码后 eslint 因 endOfLine 而报错问题
- 修复因动态路由而产生的 `Vue Router warn`
### 🎫 Chores
- 添加 test 环境测试命令
## 2.6.0(2021-07-04)
### ✨ Features

View File

@@ -34,7 +34,11 @@ export function wrapperEnv(envConf: Recordable): ViteEnv {
} catch (error) {}
}
ret[envName] = realName;
process.env[envName] = realName;
if (typeof realName === 'string') {
process.env[envName] = realName;
} else if (typeof realName === 'object') {
process.env[envName] = JSON.stringify(realName);
}
}
return ret;
}
@@ -44,7 +48,7 @@ export function wrapperEnv(envConf: Recordable): ViteEnv {
*/
function getConfFiles() {
const script = process.env.npm_lifecycle_script;
const reg = new RegExp('--mode ([a-z]+) ');
const reg = new RegExp('--mode ([a-z]+)');
const result = reg.exec(script as string) as any;
if (result) {
const mode = result[1] as string;

View File

@@ -13,7 +13,7 @@ export function configImageminPlugin() {
optimizationLevel: 7,
},
mozjpeg: {
quality: 8,
quality: 20,
},
pngquant: {
quality: [0.8, 0.9],
@@ -22,10 +22,11 @@ export function configImageminPlugin() {
svgo: {
plugins: [
{
removeViewBox: false,
name: 'removeViewBox',
},
{
removeEmptyAttrs: false,
name: 'removeEmptyAttrs',
active: false,
},
],
},

View File

@@ -5,7 +5,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx';
import legacy from '@vitejs/plugin-legacy';
import purgeIcons from 'vite-plugin-purge-icons';
import windiCSS from 'vite-plugin-windicss';
import { configHtmlPlugin } from './html';
import { configPwaConfig } from './pwa';
import { configMockPlugin } from './mock';
@@ -33,6 +33,9 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
vueJsx(),
];
// vite-plugin-windicss
vitePlugins.push(windiCSS());
// TODO
!isBuild && vitePlugins.push(configHmrPlugin());

View File

@@ -33,14 +33,17 @@ export function configThemePlugin(isBuild: boolean): Plugin[] {
return s;
case '.ant-steps-item-icon > .ant-steps-icon':
return s;
case '.ant-select-item-option-selected:not(.ant-select-item-option-disabled)':
return s;
}
return `[data-theme] ${s}`;
return s.startsWith('[data-theme') ? s : `[data-theme] ${s}`;
},
colorVariables: [...getThemeColors(), ...colors],
}),
antdDarkThemePlugin({
preloadFiles: [
path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.less'),
//path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.dark.less'),
path.resolve(process.cwd(), 'src/design/index.less'),
],
filter: (id) => (isBuild ? !id.endsWith('antd.less') : true),
@@ -48,8 +51,10 @@ export function configThemePlugin(isBuild: boolean): Plugin[] {
darkModifyVars: {
...generateModifyVars(true),
'text-color': '#c9d1d9',
'primary-1': 'rgb(255 255 255 / 8%)',
'text-color-base': '#c9d1d9',
'component-background': '#151515',
'heading-color': 'rgb(255 255 255 / 65%)',
// black: '#0e1117',
// #8b949e
'text-color-secondary': '#8b949e',
@@ -57,6 +62,20 @@ export function configThemePlugin(isBuild: boolean): Plugin[] {
// 'border-color-split': '#30363d',
'item-active-bg': '#111b26',
'app-content-background': 'rgb(255 255 255 / 4%)',
'tree-node-selected-bg': '#11263c',
'alert-success-border-color': '#274916',
'alert-success-bg-color': '#162312',
'alert-success-icon-color': '#49aa19',
'alert-info-border-color': '#153450',
'alert-info-bg-color': '#111b26',
'alert-info-icon-color': '#177ddc',
'alert-warning-border-color': '#594214',
'alert-warning-bg-color': '#2b2111',
'alert-warning-icon-color': '#d89614',
'alert-error-border-color': '#58181c',
'alert-error-bg-color': '#2a1215',
'alert-error-icon-color': '#a61d24',
},
}),
];

View File

@@ -1,31 +1,6 @@
module.exports = {
ignores: [(commit) => commit.includes('init')],
extends: ['@commitlint/config-conventional'],
parserPreset: {
parserOpts: {
headerPattern: /^(\w*|[\u4e00-\u9fa5]*)(?:[\(\](.*)[\)\])?[\:\] (.*)/,
headerCorrespondence: ['type', 'scope', 'subject'],
referenceActions: [
'close',
'closes',
'closed',
'fix',
'fixes',
'fixed',
'resolve',
'resolves',
'resolved',
],
issuePrefixes: ['#'],
noteKeywords: ['BREAKING CHANGE'],
fieldPattern: /^-(.*?)-$/,
revertPattern: /^Revert\s"([\s\S]*)"\s*This reverts commit (\w*)\./,
revertCorrespondence: ['header', 'hash'],
warn() {},
mergePattern: null,
mergeCorrespondence: null,
},
},
rules: {
'body-leading-blank': [2, 'always'],
'footer-leading-blank': [1, 'always'],

View File

@@ -1,28 +1,28 @@
import { MockMethod } from 'vite-plugin-mock';
import { resultSuccess } from '../_util';
const list: any[] = [];
const demoList = (() => {
const demoList = (keyword) => {
const result = {
list: list,
list: [] as any[],
};
for (let index = 0; index < 20; index++) {
result.list.push({
name: `选项${index}`,
name: `${keyword ?? ''}选项${index}`,
id: `${index}`,
});
}
return result;
})();
};
export default [
{
url: '/basic-api/select/getDemoOptions',
timeout: 1000,
method: 'post',
method: 'get',
response: ({ query }) => {
console.log(query);
return resultSuccess(demoList);
const { keyword } = query;
console.log(keyword);
return resultSuccess(demoList(keyword));
},
},
] as MockMethod[];

View File

@@ -1,5 +1,5 @@
import { MockMethod } from 'vite-plugin-mock';
import { resultPageSuccess, resultSuccess } from '../_util';
import { resultError, resultPageSuccess, resultSuccess } from '../_util';
const accountList = (() => {
const result: any[] = [];
@@ -28,6 +28,7 @@ const roleList = (() => {
roleValue: '@first',
createTime: '@datetime',
remark: '@cword(10,20)',
menu: [['0', '1', '2'], ['0', '1'], ['0', '2'], ['2']][index],
'status|1': ['0', '1'],
});
}
@@ -185,4 +186,17 @@ export default [
return resultSuccess(menuList);
},
},
{
url: '/basic-api/system/accountExist',
timeout: 500,
method: 'post',
response: ({ body }) => {
const { account } = body || {};
if (account && account.indexOf('admin') !== -1) {
return resultError('该字段不能包含admin');
} else {
return resultSuccess(`${account} can use`);
}
},
},
] as MockMethod[];

38
mock/demo/tree-demo.ts Normal file
View File

@@ -0,0 +1,38 @@
import { MockMethod } from 'vite-plugin-mock';
import { resultSuccess } from '../_util';
const demoTreeList = (keyword) => {
const result = {
list: [] as Recordable[],
};
for (let index = 0; index < 5; index++) {
const children: Recordable[] = [];
for (let j = 0; j < 3; j++) {
children.push({
title: `${keyword ?? ''}选项${index}-${j}`,
value: `${index}-${j}`,
key: `${index}-${j}`,
});
}
result.list.push({
title: `${keyword ?? ''}选项${index}`,
value: `${index}`,
key: `${index}`,
children,
});
}
return result;
};
export default [
{
url: '/basic-api/tree/getDemoOptions',
timeout: 1000,
method: 'get',
response: ({ query }) => {
const { keyword } = query;
console.log(keyword);
return resultSuccess(demoTreeList(keyword));
},
},
] as MockMethod[];

View File

@@ -5,13 +5,40 @@ import { createFakeUserList } from './user';
// single
const dashboardRoute = {
path: '/dashboard',
name: 'Welcome',
component: '/dashboard/analysis/index',
name: 'Dashboard',
component: 'LAYOUT',
redirect: '/dashboard/analysis',
meta: {
title: 'routes.dashboard.analysis',
affix: true,
title: 'routes.dashboard.dashboard',
hideChildrenInMenu: true,
icon: 'bx:bx-home',
},
children: [
{
path: 'analysis',
name: 'Analysis',
component: '/dashboard/analysis/index',
meta: {
hideMenu: true,
hideBreadcrumb: true,
title: 'routes.dashboard.analysis',
currentActiveMenu: '/dashboard',
icon: 'bx:bx-home',
},
},
{
path: 'workbench',
name: 'Workbench',
component: '/dashboard/workbench/index',
meta: {
hideMenu: true,
hideBreadcrumb: true,
title: 'routes.dashboard.workbench',
currentActiveMenu: '/dashboard',
icon: 'bx:bx-home',
},
},
],
};
const backRoute = {
@@ -128,6 +155,18 @@ const sysRoute = {
},
component: '/demo/system/account/index',
},
{
path: 'account_detail/:id',
name: 'AccountDetail',
meta: {
hideMenu: true,
title: 'routes.demo.system.account_detail',
ignoreKeepAlive: true,
showMenu: false,
currentActiveMenu: '/system/account',
},
component: '/demo/system/account/AccountDetail',
},
{
path: 'role',
name: 'RoleManagement',
@@ -211,12 +250,21 @@ export default [
return resultError('Invalid user token!');
}
const id = checkUser.userId;
if (!id || id === '1') {
return resultSuccess([dashboardRoute, authRoute, levelRoute, sysRoute, linkRoute]);
}
if (id === '2') {
return resultSuccess([dashboardRoute, authRoute, levelRoute, linkRoute]);
let menu: Object[];
switch (id) {
case '1':
dashboardRoute.redirect = dashboardRoute.path + '/' + dashboardRoute.children[0].path;
menu = [dashboardRoute, authRoute, levelRoute, sysRoute, linkRoute];
break;
case '2':
dashboardRoute.redirect = dashboardRoute.path + '/' + dashboardRoute.children[1].path;
menu = [dashboardRoute, authRoute, levelRoute, linkRoute];
break;
default:
menu = [];
}
return resultSuccess(menu);
},
},
] as MockMethod[];

View File

@@ -11,6 +11,7 @@ export function createFakeUserList() {
desc: 'manager',
password: '123456',
token: 'fakeToken1',
homePath: '/dashboard/analysis',
roles: [
{
roleName: 'Super Admin',
@@ -26,6 +27,7 @@ export function createFakeUserList() {
avatar: 'https://q1.qlogo.cn/g?b=qq&nk=339449197&s=640',
desc: 'tester',
token: 'fakeToken2',
homePath: '/dashboard/workbench',
roles: [
{
roleName: 'Tester',

View File

@@ -1,6 +1,6 @@
{
"name": "vben-admin",
"version": "2.6.0",
"version": "2.7.0",
"author": {
"name": "vben",
"email": "anncwb@126.com",
@@ -11,6 +11,7 @@
"serve": "npm run dev",
"dev": "vite",
"build": "cross-env NODE_ENV=production vite build && esno ./build/script/postBuild.ts",
"build:test": "cross-env vite build --mode test && esno ./build/script/postBuild.ts",
"build:no-cache": "yarn clean:cache && npm run build",
"report": "cross-env REPORT=true npm run build",
"type:check": "vue-tsc --noEmit --skipLibCheck",
@@ -20,7 +21,7 @@
"clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
"clean:lib": "rimraf node_modules",
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
"lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
"lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js",
"lint:pretty": "pretty-quick --staged",
@@ -34,111 +35,111 @@
},
"dependencies": {
"@iconify/iconify": "^2.0.3",
"@logicflow/core": "^0.5.0",
"@logicflow/extension": "^0.5.0",
"@vueuse/core": "^5.0.3",
"@logicflow/core": "^0.6.6",
"@logicflow/extension": "^0.6.6",
"@vueuse/core": "^5.2.0",
"@zxcvbn-ts/core": "^1.0.0-beta.0",
"ant-design-vue": "2.2.0-rc.1",
"ant-design-vue": "2.2.2",
"axios": "^0.21.1",
"codemirror": "^5.62.0",
"codemirror": "^5.62.2",
"cropperjs": "^1.5.12",
"crypto-js": "^4.0.0",
"crypto-js": "^4.1.1",
"echarts": "^5.1.2",
"intro.js": "^4.1.0",
"lodash-es": "^4.17.21",
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
"path-to-regexp": "^6.2.0",
"pinia": "^2.0.0-beta.3",
"pinia": "2.0.0-beta.5",
"print-js": "^1.6.0",
"qrcode": "^1.4.4",
"resize-observer-polyfill": "^1.5.1",
"sortablejs": "^1.13.0",
"sortablejs": "^1.14.0",
"tinymce": "^5.8.2",
"vditor": "^3.8.5",
"vue": "3.1.4",
"vue-i18n": "9.1.6",
"vditor": "^3.8.6",
"vue": "3.1.5",
"vue-i18n": "9.1.7",
"vue-json-pretty": "^2.0.2",
"vue-router": "^4.0.10",
"vue-types": "^4.0.0",
"vue-types": "^4.0.1",
"xlsx": "^0.17.0"
},
"devDependencies": {
"@commitlint/cli": "^12.1.4",
"@commitlint/config-conventional": "^12.1.4",
"@iconify/json": "^1.1.369",
"@commitlint/cli": "^13.1.0",
"@commitlint/config-conventional": "^13.1.0",
"@iconify/json": "^1.1.382",
"@purge-icons/generated": "^0.7.0",
"@types/codemirror": "^5.60.1",
"@types/crypto-js": "^4.0.1",
"@types/fs-extra": "^9.0.11",
"@types/inquirer": "^7.3.2",
"@types/intro.js": "^3.0.1",
"@types/jest": "^26.0.23",
"@types/codemirror": "^5.60.2",
"@types/crypto-js": "^4.0.2",
"@types/fs-extra": "^9.0.12",
"@types/inquirer": "^7.3.3",
"@types/intro.js": "^3.0.2",
"@types/jest": "^26.0.24",
"@types/lodash-es": "^4.17.4",
"@types/mockjs": "^1.0.3",
"@types/node": "^16.0.0",
"@types/mockjs": "^1.0.4",
"@types/node": "^16.4.10",
"@types/nprogress": "^0.2.0",
"@types/qrcode": "^1.4.0",
"@types/qs": "^6.9.6",
"@types/sortablejs": "^1.10.6",
"@typescript-eslint/eslint-plugin": "^4.28.1",
"@typescript-eslint/parser": "^4.28.1",
"@vitejs/plugin-legacy": "^1.4.3",
"@vitejs/plugin-vue": "^1.2.4",
"@vitejs/plugin-vue-jsx": "^1.1.6",
"@vue/compiler-sfc": "3.1.4",
"@vue/test-utils": "^2.0.0-rc.9",
"autoprefixer": "^10.2.6",
"@types/qrcode": "^1.4.1",
"@types/qs": "^6.9.7",
"@types/sortablejs": "^1.10.7",
"@typescript-eslint/eslint-plugin": "^4.29.0",
"@typescript-eslint/parser": "^4.29.0",
"@vitejs/plugin-legacy": "^1.5.0",
"@vitejs/plugin-vue": "^1.3.0",
"@vitejs/plugin-vue-jsx": "^1.1.7",
"@vue/compiler-sfc": "3.1.5",
"@vue/test-utils": "^2.0.0-rc.12",
"autoprefixer": "^10.3.1",
"commitizen": "^4.2.4",
"conventional-changelog-cli": "^2.1.1",
"cross-env": "^7.0.3",
"dotenv": "^10.0.0",
"eslint": "^7.30.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-define-config": "^1.0.9",
"eslint-plugin-jest": "^24.3.6",
"eslint-plugin-jest": "^24.4.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-vue": "^7.12.1",
"esno": "^0.7.3",
"eslint-plugin-vue": "^7.15.0",
"esno": "^0.8.0",
"fs-extra": "^10.0.0",
"http-server": "^0.12.3",
"husky": "^7.0.0",
"inquirer": "^8.1.1",
"husky": "^7.0.1",
"inquirer": "^8.1.2",
"is-ci": "^3.0.0",
"jest": "^27.0.6",
"less": "^4.1.1",
"lint-staged": "^11.0.0",
"lint-staged": "^11.1.1",
"npm-run-all": "^4.1.5",
"postcss": "^8.3.5",
"postcss": "^8.3.6",
"prettier": "^2.3.2",
"pretty-quick": "^3.1.1",
"rimraf": "^3.0.2",
"rollup-plugin-visualizer": "5.5.1",
"rollup-plugin-visualizer": "5.5.2",
"stylelint": "^13.13.1",
"stylelint-config-prettier": "^8.0.2",
"stylelint-config-standard": "^22.0.0",
"stylelint-order": "^4.1.0",
"tailwindcss": "^2.2.4",
"ts-jest": "^27.0.3",
"ts-node": "^10.0.0",
"ts-jest": "^27.0.4",
"ts-node": "^10.1.0",
"typescript": "4.3.5",
"vite": "2.4.0-beta.2",
"vite-plugin-compression": "^0.2.5",
"vite": "2.4.4",
"vite-plugin-compression": "^0.3.3",
"vite-plugin-html": "^2.0.7",
"vite-plugin-imagemin": "^0.3.2",
"vite-plugin-mock": "^2.8.0",
"vite-plugin-imagemin": "^0.4.3",
"vite-plugin-mock": "^2.9.4",
"vite-plugin-purge-icons": "^0.7.0",
"vite-plugin-pwa": "^0.8.1",
"vite-plugin-style-import": "^1.0.1",
"vite-plugin-svg-icons": "^1.0.0",
"vite-plugin-pwa": "^0.9.3",
"vite-plugin-style-import": "^1.1.0",
"vite-plugin-svg-icons": "^1.0.3",
"vite-plugin-theme": "^0.8.1",
"vue-eslint-parser": "^7.7.2",
"vue-tsc": "^0.2.0"
"vite-plugin-windicss": "^1.2.7",
"vue-eslint-parser": "^7.10.0",
"vue-tsc": "^0.2.2"
},
"resolutions": {
"//": "Used to install imagemin dependencies, because imagemin may not be installed in China. If it is abroad, you can delete it",
"bin-wrapper": "npm:bin-wrapper-china",
"rollup": "^2.52.7"
"rollup": "^2.55.1"
},
"repository": {
"type": "git",

View File

@@ -1,6 +1,5 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

@@ -15,6 +15,6 @@ module.exports = {
requirePragma: false,
proseWrap: 'never',
htmlWhitespaceSensitivity: 'strict',
endOfLine: 'lf',
endOfLine: 'auto',
rangeStart: 0,
};

View File

@@ -8,4 +8,4 @@ enum Api {
* @description: Get sample options value
*/
export const optionsListApi = (params?: selectParams) =>
defHttp.post<DemoOptionsItem[]>({ url: Api.OPTIONS_LIST, params });
defHttp.get<DemoOptionsItem[]>({ url: Api.OPTIONS_LIST, params });

View File

@@ -14,6 +14,7 @@ import { defHttp } from '/@/utils/http/axios';
enum Api {
AccountList = '/system/getAccountList',
IsAccountExist = '/system/accountExist',
DeptList = '/system/getDeptList',
setRoleStatus = '/system/setRoleStatus',
MenuList = '/system/getMenuList',
@@ -38,3 +39,6 @@ export const getAllRoleList = (params?: RoleParams) =>
export const setRoleStatus = (id: number, status: string) =>
defHttp.post({ url: Api.setRoleStatus, params: { id, status } });
export const isAccountExist = (account: string) =>
defHttp.post({ url: Api.IsAccountExist, params: { account } }, { errorMessageMode: 'none' });

11
src/api/demo/tree.ts Normal file
View File

@@ -0,0 +1,11 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
TREE_OPTIONS_LIST = '/tree/getDemoOptions',
}
/**
* @description: Get sample options value
*/
export const treeOptionsListApi = (params?: Recordable) =>
defHttp.get<Recordable[]>({ url: Api.TREE_OPTIONS_LIST, params });

View File

@@ -17,6 +17,7 @@
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useDesign } from '/@/hooks/web/useDesign';
import { PageEnum } from '/@/enums/pageEnum';
import { useUserStore } from '/@/store/modules/user';
const props = {
/**
@@ -39,6 +40,7 @@
setup(props) {
const { prefixCls } = useDesign('app-logo');
const { getCollapsedShowTitle } = useMenuSetting();
const userStore = useUserStore();
const { title } = useGlobSetting();
const go = useGo();
@@ -56,7 +58,7 @@
]);
function goHome() {
go(PageEnum.BASE_HOME);
go(userStore.getUserInfo.homePath || PageEnum.BASE_HOME);
}
return {

View File

@@ -55,7 +55,7 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
}
const reg = createSearchReg(unref(keyword));
const filterMenu = filter(menuList, (item) => {
return reg.test(item.name);
return reg.test(item.name) && !item.hideMenu;
});
searchResult.value = handlerSearchResult(filterMenu, reg);
activeIndex.value = 0;
@@ -64,15 +64,15 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
function handlerSearchResult(filterMenu: Menu[], reg: RegExp, parent?: Menu) {
const ret: SearchResult[] = [];
filterMenu.forEach((item) => {
const { name, path, icon, children } = item;
if (reg.test(name) && !children?.length) {
const { name, path, icon, children, hideMenu, meta } = item;
if (!hideMenu && reg.test(name) && (!children?.length || meta?.hideChildrenInMenu)) {
ret.push({
name: parent?.name ? `${parent.name} > ${name}` : name,
path,
icon,
});
}
if (Array.isArray(children) && children.length) {
if (!meta?.hideChildrenInMenu && Array.isArray(children) && children.length) {
ret.push(...handlerSearchResult(children, reg, item));
}
});

View File

@@ -8,18 +8,20 @@
</Button>
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue';
import { defineComponent, computed, unref } from 'vue';
import { Button } from 'ant-design-vue';
import Icon from '/@/components/Icon/src/Icon.vue';
import { buttonProps } from './props';
import { useAttrs } from '/@/hooks/core/useAttrs';
export default defineComponent({
name: 'AButton',
components: { Button, Icon },
inheritAttrs: false,
props: buttonProps,
setup(props, { attrs }) {
setup(props) {
// get component class
const attrs = useAttrs({ excludeDefaultKeys: false });
const getButtonClass = computed(() => {
const { color, disabled } = props;
return [
@@ -27,12 +29,11 @@
[`ant-btn-${color}`]: !!color,
[`is-disabled`]: disabled,
},
attrs.class,
];
});
// get inherit binding value
const getBindValue = computed(() => ({ ...attrs, ...props }));
const getBindValue = computed(() => ({ ...unref(attrs), ...props }));
return { getBindValue, getButtonClass };
},

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { defineComponent, h, unref, computed } from 'vue';
import { computed, defineComponent, h, unref } from 'vue';
import BasicButton from './BasicButton.vue';
import { Popconfirm } from 'ant-design-vue';
import { extendSlots } from '/@/utils/helper/tsxHelper';
@@ -29,19 +29,20 @@
// get inherit binding value
const getBindValues = computed(() => {
const popValues = Object.assign(
return Object.assign(
{
okText: t('common.okText'),
cancelText: t('common.cancelText'),
},
{ ...props, ...unref(attrs) }
);
return popValues;
});
return () => {
const bindValues = omit(unref(getBindValues), 'icon');
const Button = h(BasicButton, omit(bindValues, 'title'), extendSlots(slots));
const btnBind = omit(bindValues, 'title');
if (btnBind.disabled) btnBind.color = '';
const Button = h(BasicButton, btnBind, extendSlots(slots));
// If it is not enabled, it is a normal button
if (!props.enable) {

View File

@@ -29,7 +29,7 @@
name: 'CodeEditor',
components: { CodeMirrorEditor },
props,
emits: ['change'],
emits: ['change', 'update:value'],
setup(props, { emit }) {
const getValue = computed(() => {
const { value, mode } = props;
@@ -42,6 +42,7 @@
});
function handleValueChange(v) {
emit('update:value', v);
emit('change', v);
}

View File

@@ -3,6 +3,9 @@
<template #addonAfter>
<CountButton :size="size" :count="count" :value="state" :beforeStartFunc="sendCodeApi" />
</template>
<template #[item]="data" v-for="item in Object.keys($slots).filter((k) => k !== 'addonAfter')">
<slot :name="item" v-bind="data"></slot>
</template>
</a-input>
</template>
<script lang="ts">

View File

@@ -84,7 +84,7 @@
}
function formatNumber(num: number | string) {
if (!num) {
if (!num && num !== 0) {
return '';
}
const { decimals, decimal, separator, suffix, prefix } = props;

View File

@@ -130,7 +130,7 @@
};
export default defineComponent({
name: 'CropperAvatar',
name: 'CropperModal',
components: { BasicModal, Space, CropperImage, Upload, Avatar, Tooltip },
props,
emits: ['uploadSuccess', 'register'],

View File

@@ -8,5 +8,6 @@ export { useForm } from './src/hooks/useForm';
export { default as ApiSelect } from './src/components/ApiSelect.vue';
export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.vue';
export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue';
export { BasicForm };

View File

@@ -229,6 +229,10 @@
function setFormModel(key: string, value: any) {
formModel[key] = value;
const { validateTrigger } = unref(getBindValue);
if (!validateTrigger || validateTrigger === 'change') {
validateFields([key]).catch((_) => {});
}
}
function handleEnterPress(e: KeyboardEvent) {

View File

@@ -22,6 +22,7 @@ import {
import RadioButtonGroup from './components/RadioButtonGroup.vue';
import ApiSelect from './components/ApiSelect.vue';
import ApiTreeSelect from './components/ApiTreeSelect.vue';
import { BasicUpload } from '/@/components/Upload';
import { StrengthMeter } from '/@/components/StrengthMeter';
import { IconPicker } from '/@/components/Icon';
@@ -40,6 +41,7 @@ componentMap.set('AutoComplete', AutoComplete);
componentMap.set('Select', Select);
componentMap.set('ApiSelect', ApiSelect);
componentMap.set('TreeSelect', TreeSelect);
componentMap.set('ApiTreeSelect', ApiTreeSelect);
componentMap.set('Switch', Switch);
componentMap.set('RadioButtonGroup', RadioButtonGroup);
componentMap.set('RadioGroup', Radio.Group);

View File

@@ -106,7 +106,7 @@
async function fetch() {
const api = props.api;
if (!api || !isFunction(api)) return;
options.value = [];
try {
loading.value = true;
const res = await api(props.params);
@@ -134,7 +134,7 @@
}
function emitChange() {
emit('options-change', unref(options));
emit('options-change', unref(getOptions));
}
function handleChange(_, ...args) {

View File

@@ -0,0 +1,86 @@
<template>
<a-tree-select v-bind="getAttrs" @change="handleChange">
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data"></slot>
</template>
<template #suffixIcon v-if="loading">
<LoadingOutlined spin />
</template>
</a-tree-select>
</template>
<script lang="ts">
import { computed, defineComponent, watch, ref, onMounted, unref } from 'vue';
import { TreeSelect } from 'ant-design-vue';
import { isArray, isFunction } from '/@/utils/is';
import { get } from 'lodash-es';
import { propTypes } from '/@/utils/propTypes';
import { LoadingOutlined } from '@ant-design/icons-vue';
export default defineComponent({
name: 'ApiTreeSelect',
components: { ATreeSelect: TreeSelect, LoadingOutlined },
props: {
api: { type: Function as PropType<(arg?: Recordable) => Promise<Recordable>> },
params: { type: Object },
immediate: { type: Boolean, default: true },
resultField: propTypes.string.def(''),
},
emits: ['options-change', 'change'],
setup(props, { attrs, emit }) {
const treeData = ref<Recordable[]>([]);
const isFirstLoaded = ref<Boolean>(false);
const loading = ref(false);
const getAttrs = computed(() => {
return {
...(props.api ? { treeData: unref(treeData) } : {}),
...attrs,
};
});
function handleChange(...args) {
emit('change', ...args);
}
watch(
() => props.params,
() => {
isFirstLoaded.value && fetch();
},
{ deep: true }
);
watch(
() => props.immediate,
(v) => {
v && !isFirstLoaded.value && fetch();
}
);
onMounted(() => {
props.immediate && fetch();
});
async function fetch() {
const { api } = props;
if (!api || !isFunction(api)) return;
loading.value = true;
treeData.value = [];
let result;
try {
result = await api(props.params);
} catch (e) {
console.error(e);
}
loading.value = false;
if (!result) return;
if (!isArray(result)) {
result = get(result, props.resultField);
}
treeData.value = (result as Recordable[]) || [];
isFirstLoaded.value = true;
emit('options-change', treeData.value);
}
return { getAttrs, loading, handleChange };
},
});
</script>

View File

@@ -1,40 +1,42 @@
<template>
<a-col v-bind="actionColOpt" :style="{ textAlign: 'right' }" v-if="showActionButtonGroup">
<FormItem>
<slot name="resetBefore"></slot>
<Button
type="default"
class="mr-2"
v-bind="getResetBtnOptions"
@click="resetAction"
v-if="showResetButton"
>
{{ getResetBtnOptions.text }}
</Button>
<slot name="submitBefore"></slot>
<a-col v-bind="actionColOpt" v-if="showActionButtonGroup">
<div style="width: 100%" :style="{ textAlign: actionColOpt.style.textAlign }">
<FormItem>
<slot name="resetBefore"></slot>
<Button
type="default"
class="mr-2"
v-bind="getResetBtnOptions"
@click="resetAction"
v-if="showResetButton"
>
{{ getResetBtnOptions.text }}
</Button>
<slot name="submitBefore"></slot>
<Button
type="primary"
class="mr-2"
v-bind="getSubmitBtnOptions"
@click="submitAction"
v-if="showSubmitButton"
>
{{ getSubmitBtnOptions.text }}
</Button>
<Button
type="primary"
class="mr-2"
v-bind="getSubmitBtnOptions"
@click="submitAction"
v-if="showSubmitButton"
>
{{ getSubmitBtnOptions.text }}
</Button>
<slot name="advanceBefore"></slot>
<Button
type="link"
size="small"
@click="toggleAdvanced"
v-if="showAdvancedButton && !hideAdvanceBtn"
>
{{ isAdvanced ? t('component.form.putAway') : t('component.form.unfold') }}
<BasicArrow class="ml-1" :expand="!isAdvanced" up />
</Button>
<slot name="advanceAfter"></slot>
</FormItem>
<slot name="advanceBefore"></slot>
<Button
type="link"
size="small"
@click="toggleAdvanced"
v-if="showAdvancedButton && !hideAdvanceBtn"
>
{{ isAdvanced ? t('component.form.putAway') : t('component.form.unfold') }}
<BasicArrow class="ml-1" :expand="!isAdvanced" up />
</Button>
<slot name="advanceAfter"></slot>
</FormItem>
</div>
</a-col>
</template>
<script lang="ts">
@@ -43,7 +45,7 @@
import { defineComponent, computed, PropType } from 'vue';
import { Form, Col } from 'ant-design-vue';
import { Button, ButtonProps } from '/@/components/Button';
import { BasicArrow } from '/@/components/Basic/index';
import { BasicArrow } from '/@/components/Basic';
import { useFormContext } from '../hooks/useFormContext';
import { useI18n } from '/@/hooks/web/useI18n';
import { propTypes } from '/@/utils/propTypes';
@@ -90,6 +92,7 @@
? { span: actionSpan < 6 ? 24 : actionSpan }
: {};
const actionColOpt: Partial<ColEx> = {
style: { textAlign: 'right' },
span: showAdvancedButton ? 6 : 4,
...advancedSpanObj,
...actionColOptions,

View File

@@ -326,10 +326,10 @@
labelCol={labelCol}
wrapperCol={wrapperCol}
>
<>
{getContent()}
<div style="display:flex">
<div style="flex:1">{getContent()}</div>
{showSuffix && <span class="suffix">{getSuffix}</span>}
</>
</div>
</Form.Item>
);
}

View File

@@ -84,7 +84,7 @@ export function useFormEvents({
validKeys.push(key);
}
});
validateFields(validKeys);
validateFields(validKeys).catch((_) => {});
}
/**
* @description: Delete based on field name

View File

@@ -1,6 +1,6 @@
import type { NamePath } from 'ant-design-vue/lib/form/interface';
import type { ColProps } from 'ant-design-vue/lib/grid/Col';
import type { VNodeChild } from 'vue';
import type { HTMLAttributes, VNodeChild } from 'vue';
export interface FormItem {
/**
@@ -39,7 +39,7 @@ export interface FormItem {
* The layout of label. You can set span offset to something like {span: 3, offset: 12} or sm: {span: 3, offset: 12} same as with <Col>
* @type Col
*/
labelCol?: ColProps;
labelCol?: ColProps & HTMLAttributes;
/**
* Whether provided or not, it will be generated by the validation rule.

View File

@@ -91,6 +91,7 @@ export type ComponentType =
| 'Select'
| 'ApiSelect'
| 'TreeSelect'
| 'ApiTreeSelect'
| 'RadioButtonGroup'
| 'RadioGroup'
| 'Checkbox'

View File

@@ -12,10 +12,12 @@ interface Fn {
(): void;
}
export function useLoading(props: Partial<LoadingProps>): [Fn, Fn];
export function useLoading(opt: Partial<UseLoadingOptions>): [Fn, Fn];
export function useLoading(props: Partial<LoadingProps>): [Fn, Fn, (string) => void];
export function useLoading(opt: Partial<UseLoadingOptions>): [Fn, Fn, (string) => void];
export function useLoading(opt: Partial<LoadingProps> | Partial<UseLoadingOptions>): [Fn, Fn] {
export function useLoading(
opt: Partial<LoadingProps> | Partial<UseLoadingOptions>
): [Fn, Fn, (string) => void] {
let props: Partial<LoadingProps>;
let target: HTMLElement | Ref<ElRef> = document.body;
@@ -39,5 +41,9 @@ export function useLoading(opt: Partial<LoadingProps> | Partial<UseLoadingOption
instance.close();
};
return [open, close];
const setTip = (tip: string) => {
instance.setTip(tip);
};
return [open, close, setTip];
}

View File

@@ -5,18 +5,19 @@
import {
defineComponent,
ref,
onMounted,
unref,
onUnmounted,
nextTick,
computed,
watch,
onBeforeUnmount,
onDeactivated,
} from 'vue';
import Vditor from 'vditor';
import 'vditor/dist/index.css';
import { useLocale } from '/@/locales/useLocale';
import { useModalContext } from '../../Modal';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined;
@@ -26,7 +27,7 @@
height: { type: Number, default: 360 },
value: { type: String, default: '' },
},
emits: ['change', 'get'],
emits: ['change', 'get', 'update:value'],
setup(props, { attrs, emit }) {
const wrapRef = ref<ElRef>(null);
const vditorRef = ref<Nullable<Vditor>>(null);
@@ -36,17 +37,16 @@
const { getLocale } = useLocale();
const { getDarkMode } = useRootSetting();
const valueRef = ref('');
watch(
[() => getDarkMode.value, () => initedRef.value],
([val]) => {
const vditor = unref(vditorRef);
if (!vditor) {
([val, inited]) => {
if (!inited) {
return;
}
const theme = val === 'dark' ? 'dark' : undefined;
vditor.setTheme(theme as 'dark');
const theme = val === 'dark' ? 'dark' : 'classic';
instance.getVditor()?.setTheme(theme);
},
{
immediate: true,
@@ -54,6 +54,16 @@
}
);
watch(
() => props.value,
(v) => {
if (v !== valueRef.value) {
instance.getVditor()?.setValue(v);
}
valueRef.value = v;
}
);
const getCurrentLang = computed((): 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' => {
let lang: Lang;
switch (unref(getLocale)) {
@@ -72,54 +82,58 @@
return lang;
});
function init() {
const wrapEl = unref(wrapRef);
const wrapEl = unref(wrapRef) as HTMLElement;
if (!wrapEl) return;
const bindValue = { ...attrs, ...props };
vditorRef.value = new Vditor(wrapEl, {
theme: 'classic',
const insEditor = new Vditor(wrapEl, {
theme: getDarkMode.value === 'dark' ? 'dark' : 'classic',
lang: unref(getCurrentLang),
mode: 'sv',
preview: {
actions: [],
},
input: (v) => {
// emit('update:value', v);
valueRef.value = v;
emit('update:value', v);
emit('change', v);
},
after: () => {
nextTick(() => {
modalFn?.redoModalHeight?.();
insEditor.setValue(valueRef.value);
vditorRef.value = insEditor;
initedRef.value = true;
emit('get', instance);
});
},
blur: () => {
unref(vditorRef)?.setValue(props.value);
//unref(vditorRef)?.setValue(props.value);
},
...bindValue,
cache: {
enable: false,
},
});
initedRef.value = true;
}
const instance = {
getVditor: (): Vditor => vditorRef.value!,
};
onMounted(() => {
nextTick(() => {
init();
setTimeout(() => {
modalFn?.redoModalHeight?.();
}, 200);
});
emit('get', instance);
});
onUnmounted(() => {
function destroy() {
const vditorInstance = unref(vditorRef);
if (!vditorInstance) return;
try {
vditorInstance?.destroy?.();
} catch (error) {}
});
vditorRef.value = null;
initedRef.value = false;
}
onMountedOrActivated(init);
onBeforeUnmount(destroy);
onDeactivated(destroy);
return {
wrapRef,
...instance,

View File

@@ -18,7 +18,7 @@
</template>
<template #footer v-if="!$slots.footer">
<ModalFooter v-bind="getProps" @ok="handleOk" @cancel="handleCancel">
<ModalFooter v-bind="getBindValue" @ok="handleOk" @cancel="handleCancel">
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data"></slot>
</template>
@@ -82,7 +82,7 @@
setup(props, { emit, attrs }) {
const visibleRef = ref(false);
const propsRef = ref<Partial<ModalProps> | null>(null);
const modalWrapperRef = ref<ComponentRef>(null);
const modalWrapperRef = ref<any>(null);
// modal Bottom and top height
const extHeightRef = ref(0);
@@ -133,7 +133,12 @@
});
const getBindValue = computed((): Recordable => {
const attr = { ...attrs, ...unref(getProps) };
const attr = {
...attrs,
...unref(getMergeProps),
visible: unref(visibleRef),
wrapClassName: unref(getWrapClassName),
};
if (unref(fullScreenRef)) {
return omit(attr, 'height');
}

View File

@@ -3,7 +3,7 @@
<PageHeader
:ghost="ghost"
:title="title"
v-bind="$attrs"
v-bind="omit($attrs, 'class')"
ref="headerRef"
v-if="content || $slots.headerContent || title || getHeaderSlots.length"
>
@@ -61,7 +61,7 @@
contentClass: propTypes.string,
fixedHeight: propTypes.bool,
},
setup(props, { slots }) {
setup(props, { slots, attrs }) {
const wrapperRef = ref(null);
const headerRef = ref(null);
const contentRef = ref(null);
@@ -86,6 +86,7 @@
{
[`${prefixCls}--dense`]: props.dense,
},
attrs.class ?? {},
];
});

View File

@@ -38,13 +38,33 @@
type: Number as PropType<number>,
default: 0,
},
scaleStep: {
type: Number as PropType<number>,
},
defaultWidth: {
type: Number as PropType<number>,
},
maskClosable: {
type: Boolean as PropType<boolean>,
},
rememberState: {
type: Boolean as PropType<boolean>,
},
};
const prefixCls = 'img-preview';
export default defineComponent({
name: 'ImagePreview',
props,
setup(props: Props) {
emits: ['img-load', 'img-error'],
setup(props: Props, { expose, emit }) {
interface stateInfo {
scale: number;
rotate: number;
top: number;
left: number;
}
const stateMap = new Map<string, stateInfo>();
const imgState = reactive<ImgState>({
currentUrl: '',
imgScale: 1,
@@ -96,6 +116,14 @@
};
}
const getScaleStep = computed(() => {
if (props.scaleStep > 0 && props.scaleStep < 100) {
return props.scaleStep / 100;
} else {
return imgState.imgScale / 10;
}
});
// 监听鼠标滚轮
function scrollFunc(e: any) {
e = e || window.event;
@@ -104,11 +132,11 @@
e.preventDefault();
if (e.delta > 0) {
// 滑轮向上滚动
scaleFunc(0.015);
scaleFunc(getScaleStep.value);
}
if (e.delta < 0) {
// 滑轮向下滚动
scaleFunc(-0.015);
scaleFunc(-getScaleStep.value);
}
}
// 缩放函数
@@ -134,11 +162,54 @@
imgState.status = StatueEnum.LOADING;
const img = new Image();
img.src = url;
img.onload = () => {
img.onload = (e: Event) => {
if (imgState.currentUrl !== url) {
const ele: HTMLElement[] = e.composedPath();
if (props.rememberState) {
// 保存当前图片的缩放信息
stateMap.set(imgState.currentUrl, {
scale: imgState.imgScale,
top: imgState.imgTop,
left: imgState.imgLeft,
rotate: imgState.imgRotate,
});
// 如果之前已存储缩放信息,就应用
const stateInfo = stateMap.get(url);
if (stateInfo) {
imgState.imgScale = stateInfo.scale;
imgState.imgTop = stateInfo.top;
imgState.imgRotate = stateInfo.rotate;
imgState.imgLeft = stateInfo.left;
} else {
initState();
if (props.defaultWidth) {
imgState.imgScale = props.defaultWidth / ele[0].naturalWidth;
}
}
} else {
if (props.defaultWidth) {
imgState.imgScale = props.defaultWidth / ele[0].naturalWidth;
}
}
ele &&
emit('img-load', {
index: imgState.currentIndex,
dom: ele[0] as HTMLImageElement,
url,
});
}
imgState.currentUrl = url;
imgState.status = StatueEnum.DONE;
};
img.onerror = () => {
img.onerror = (e: Event) => {
const ele: EventTarget[] = e.composedPath();
ele &&
emit('img-error', {
index: imgState.currentIndex,
dom: ele[0] as HTMLImageElement,
url,
});
imgState.status = StatueEnum.FAIL;
};
}
@@ -146,6 +217,10 @@
// 关闭
function handleClose(e: MouseEvent) {
e && e.stopPropagation();
close();
}
function close() {
imgState.show = false;
// 移除火狐浏览器下的鼠标滚动事件
document.body.removeEventListener('DOMMouseScroll', scrollFunc);
@@ -158,6 +233,19 @@
initState();
}
expose({
resume,
close,
prev: handleChange.bind(null, 'left'),
next: handleChange.bind(null, 'right'),
setScale: (scale: number) => {
if (scale > 0 && scale <= 10) imgState.imgScale = scale;
},
setRotate: (rotate: number) => {
imgState.imgRotate = rotate;
},
} as PreviewActions);
// 上一页下一页
function handleChange(direction: 'left' | 'right') {
const { currentIndex } = imgState;
@@ -205,6 +293,7 @@
transform: `scale(${imgScale}) rotate(${imgRotate}deg)`,
marginTop: `${imgTop}px`,
marginLeft: `${imgLeft}px`,
maxWidth: props.defaultWidth ? 'unset' : '100%',
};
});
@@ -222,6 +311,16 @@
}
});
const handleMaskClick = (e: MouseEvent) => {
if (
props.maskClosable &&
e.target &&
(e.target as HTMLDivElement).classList.contains(`${prefixCls}-content`)
) {
handleClose(e);
}
};
const renderClose = () => {
return (
<div class={`${prefixCls}__close`} onClick={handleClose}>
@@ -246,10 +345,16 @@
const renderController = () => {
return (
<div class={`${prefixCls}__controller`}>
<div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(-0.15)}>
<div
class={`${prefixCls}__controller-item`}
onClick={() => scaleFunc(-getScaleStep.value)}
>
<img src={unScaleSvg} />
</div>
<div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(0.15)}>
<div
class={`${prefixCls}__controller-item`}
onClick={() => scaleFunc(getScaleStep.value)}
>
<img src={scaleSvg} />
</div>
<div class={`${prefixCls}__controller-item`} onClick={resume}>
@@ -279,7 +384,12 @@
return () => {
return (
imgState.show && (
<div class={prefixCls} ref={wrapElRef} onMouseup={handleMouseUp}>
<div
class={prefixCls}
ref={wrapElRef}
onMouseup={handleMouseUp}
onClick={handleMaskClick}
>
<div class={`${prefixCls}-content`}>
{/*<Spin*/}
{/* indicator={<LoadingOutlined style="font-size: 24px" spin />}*/}

View File

@@ -6,15 +6,12 @@ import { createVNode, render } from 'vue';
let instance: ReturnType<typeof createVNode> | null = null;
export function createImgPreview(options: Options) {
if (!isClient) return;
const { imageList, show = true, index = 0 } = options;
const propsData: Partial<Props> = {};
const container = document.createElement('div');
propsData.imageList = imageList;
propsData.show = show;
propsData.index = index;
Object.assign(propsData, { show: true, index: 0, scaleStep: 100 }, options);
instance = createVNode(ImgPreview, propsData);
render(instance, container);
document.body.appendChild(container);
return instance.component?.exposed;
}

View File

@@ -2,6 +2,12 @@ export interface Options {
show?: boolean;
imageList: string[];
index?: number;
scaleStep?: number;
defaultWidth?: number;
maskClosable?: boolean;
rememberState?: boolean;
onImgLoad?: ({ index: number, url: string, dom: HTMLImageElement }) => void;
onImgError?: ({ index: number, url: string, dom: HTMLImageElement }) => void;
}
export interface Props {
@@ -9,6 +15,19 @@ export interface Props {
instance: Props;
imageList: string[];
index: number;
scaleStep: number;
defaultWidth: number;
maskClosable: boolean;
rememberState: boolean;
}
export interface PreviewActions {
resume: () => void;
close: () => void;
prev: () => void;
next: () => void;
setScale: (scale: number) => void;
setRotate: (rotate: number) => void;
}
export interface ImageProps {

View File

@@ -4,7 +4,7 @@
</div>
</template>
<script lang="ts">
import { defineComponent, watchEffect, PropType, ref, unref } from 'vue';
import { defineComponent, watch, PropType, ref, unref } from 'vue';
import { toCanvas, QRCodeRenderersOptions, LogoType } from './qrcodePlus';
import { toDataURL } from 'qrcode';
import { downloadByUrl } from '/@/utils/file/download';
@@ -93,11 +93,16 @@
});
}
watchEffect(() => {
setTimeout(() => {
createQrcode();
}, 30);
});
// 监听参数变化重新生成二维码
watch(
props,
() => {
createQrcode()
},
{
deep: true,
}
)
return { wrapRef, download };
},

View File

@@ -1,7 +1,15 @@
import { toCanvas } from 'qrcode';
import type { QRCodeRenderersOptions } from 'qrcode';
import { RenderQrCodeParams, ContentType } from './typing';
export const renderQrCode = ({ canvas, content, width = 0, options = {} }: RenderQrCodeParams) => {
import { cloneDeep } from 'lodash-es';
export const renderQrCode = ({
canvas,
content,
width = 0,
options: params = {}
}: RenderQrCodeParams) => {
const options = cloneDeep(params)
// 容错率,默认对内容少的二维码采用高容错率,内容多的二维码采用低容错率
options.errorCorrectionLevel = options.errorCorrectionLevel || getErrorCorrectionLevel(content);

View File

@@ -129,6 +129,7 @@
getDataSource,
setTableData,
updateTableDataRecord,
findTableDataRecord,
fetch,
getRowKey,
reload,
@@ -211,7 +212,7 @@
// ...(dataSource.length === 0 ? { getPopupContainer: () => document.body } : {}),
...attrs,
customRow,
expandIcon: expandIcon(),
expandIcon: slots.expandIcon ? null : expandIcon(),
...unref(getProps),
...unref(getHeaderProps),
scroll: unref(getScrollRef),
@@ -266,6 +267,7 @@
setPagination,
setTableData,
updateTableDataRecord,
findTableDataRecord,
redoHeight,
setSelectedRowKeys,
setColumns,

View File

@@ -9,7 +9,7 @@ import {
TimePicker,
} from 'ant-design-vue';
import type { ComponentType } from './types/componentType';
import { ApiSelect } from '/@/components/Form';
import { ApiSelect, ApiTreeSelect } from '/@/components/Form';
const componentMap = new Map<ComponentType, Component>();
@@ -17,6 +17,7 @@ componentMap.set('Input', Input);
componentMap.set('InputNumber', InputNumber);
componentMap.set('Select', Select);
componentMap.set('ApiSelect', ApiSelect);
componentMap.set('ApiTreeSelect', ApiTreeSelect);
componentMap.set('Switch', Switch);
componentMap.set('Checkbox', Checkbox);
componentMap.set('DatePicker', DatePicker);

View File

@@ -3,11 +3,15 @@ import { BasicArrow } from '/@/components/Basic';
export default () => {
return (props: Recordable) => {
if (!props.expandable) {
return <span />;
if (props.needIndentSpaced) {
return <span class="ant-table-row-expand-icon ant-table-row-spaced" />;
} else {
return <span />;
}
}
return (
<BasicArrow
class="mr-1"
style="margin-right: 8px"
iconStyle="margin-top: -2px;"
onClick={(e: Event) => {
props.onExpand(props.record, e);

View File

@@ -1,12 +1,16 @@
<template>
<div :class="[prefixCls, getAlign]" @click="onCellClick">
<template v-for="(action, index) in getActions" :key="`${index}-${action.label}`">
<Tooltip v-bind="getTooltip(action.tooltip)">
<Tooltip v-if="action.tooltip" v-bind="getTooltip(action.tooltip)">
<PopConfirmButton v-bind="action">
<Icon :icon="action.icon" class="mr-1" v-if="action.icon" />
{{ action.label }}
<Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" />
<template v-if="action.label">{{ action.label }}</template>
</PopConfirmButton>
</Tooltip>
<PopConfirmButton v-else v-bind="action">
<Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" />
<template v-if="action.label">{{ action.label }}</template>
</PopConfirmButton>
<Divider
type="vertical"
class="action-divider"
@@ -31,7 +35,7 @@
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, computed, toRaw } from 'vue';
import { defineComponent, PropType, computed, toRaw, unref } from 'vue';
import { MoreOutlined } from '@ant-design/icons-vue';
import { Divider, Tooltip, TooltipProps } from 'ant-design-vue';
import Icon from '/@/components/Icon/index';
@@ -91,6 +95,7 @@
.map((action) => {
const { popConfirm } = action;
return {
getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body,
type: 'link',
size: 'small',
...action,
@@ -126,22 +131,21 @@
return actionColumn?.align ?? 'left';
});
const getTooltip = computed(() => {
return (data: string | TooltipProps): TooltipProps => {
if (isString(data)) {
return { title: data, placement: 'bottom' };
} else {
return Object.assign({ placement: 'bottom' }, data);
}
function getTooltip(data: string | TooltipProps): TooltipProps {
return {
getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body,
placement: 'bottom',
...(isString(data) ? { title: data } : data),
};
});
}
function onCellClick(e: MouseEvent) {
if (!props.stopButtonPropagation) return;
const target = e.target as HTMLElement;
if (target.tagName === 'BUTTON') {
e.stopPropagation();
}
const path = e.composedPath() as HTMLElement[];
const isInButton = path.find((ele) => {
return ele.tagName?.toUpperCase() === 'BUTTON';
});
isInButton && e.stopPropagation();
}
return { prefixCls, getActions, getDropdownList, getAlign, onCellClick, getTooltip };
@@ -180,6 +184,12 @@
}
}
button.ant-btn-circle {
span {
margin: auto !important;
}
}
.ant-divider,
.ant-divider-vertical {
margin: 0 2px;

View File

@@ -1,16 +1,25 @@
<template>
<slot name="tableTitle" v-if="$slots.tableTitle"></slot>
<TableTitle :helpMessage="titleHelpMessage" :title="title" v-if="!$slots.tableTitle && title" />
<div :class="`${prefixCls}__toolbar`">
<slot name="toolbar"></slot>
<Divider type="vertical" v-if="$slots.toolbar && showTableSetting" />
<TableSetting
:setting="tableSetting"
v-if="showTableSetting"
@columns-change="handleColumnChange"
/>
<div style="width: 100%">
<div v-if="$slots.headerTop" style="margin: 5px">
<slot name="headerTop"></slot>
</div>
<div class="flex items-center">
<slot name="tableTitle" v-if="$slots.tableTitle"></slot>
<TableTitle
:helpMessage="titleHelpMessage"
:title="title"
v-if="!$slots.tableTitle && title"
/>
<div :class="`${prefixCls}__toolbar`">
<slot name="toolbar"></slot>
<Divider type="vertical" v-if="$slots.toolbar && showTableSetting" />
<TableSetting
:setting="tableSetting"
v-if="showTableSetting"
@columns-change="handleColumnChange"
/>
</div>
</div>
</div>
</template>
<script lang="ts">

View File

@@ -10,10 +10,17 @@ export interface ComponentProps {
rule: boolean;
popoverVisible: boolean;
ruleMessage: string;
getPopupContainer?: Fn;
}
export const CellComponent: FunctionalComponent = (
{ component = 'Input', rule = true, ruleMessage, popoverVisible }: ComponentProps,
{
component = 'Input',
rule = true,
ruleMessage,
popoverVisible,
getPopupContainer,
}: ComponentProps,
{ attrs }
) => {
const Comp = componentMap.get(component) as typeof defineComponent;
@@ -24,7 +31,11 @@ export const CellComponent: FunctionalComponent = (
}
return h(
Popover,
{ overlayClassName: 'edit-cell-rule-popover', visible: !!popoverVisible },
{
overlayClassName: 'edit-cell-rule-popover',
visible: !!popoverVisible,
...(getPopupContainer ? { getPopupContainer } : {}),
},
{
default: () => DefaultComp,
content: () => ruleMessage,

View File

@@ -1,7 +1,13 @@
<template>
<div :class="prefixCls">
<div v-show="!isEdit" :class="`${prefixCls}__normal`" @click="handleEdit">
{{ getValues || '&nbsp;' }}
<div
v-show="!isEdit"
:class="{ [`${prefixCls}__normal`]: true, 'ellipsis-cell': column.ellipsis }"
@click="handleEdit"
>
<div class="cell-content" :title="column.ellipsis ? getValues || '' : ''">{{
getValues || '&nbsp;'
}}</div>
<FormOutlined :class="`${prefixCls}__normal-icon`" v-if="!column.editRow" />
</div>
@@ -45,6 +51,7 @@
import { isString, isBoolean, isFunction, isNumber, isArray } from '/@/utils/is';
import { createPlaceholderMessage } from './helper';
import { set, omit } from 'lodash-es';
import { treeToList } from '/@/utils/helper/treeHelper';
export default defineComponent({
name: 'EditableCell',
@@ -106,6 +113,8 @@
const value = isCheckValue ? (isNumber(val) && isBoolean(val) ? val : !!val) : val;
return {
getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body,
getCalendarContainer: () => unref(table?.wrapRef.value) ?? document.body,
placeholder: createPlaceholderMessage(unref(getComponent)),
...apiSelectProps,
...omit(compProps, 'onChange'),
@@ -154,6 +163,7 @@
watchEffect(() => {
defaultValueRef.value = props.value;
currentValueRef.value = props.value;
});
watchEffect(() => {
@@ -275,9 +285,23 @@
}
}
// only ApiSelect
// only ApiSelect or TreeSelect
function handleOptionsChange(options: LabelValueOptions) {
optionsRef.value = options;
const { replaceFields } = props.column?.editComponentProps ?? {};
const component = unref(getComponent);
if (component === 'ApiTreeSelect') {
const { title = 'title', value = 'value', children = 'children' } = replaceFields || {};
let listOptions: Recordable[] = treeToList(options, { children });
listOptions = listOptions.map((item) => {
return {
label: item[title],
value: item[value],
};
});
optionsRef.value = listOptions as LabelValueOptions;
} else {
optionsRef.value = options;
}
}
function initCbs(cbs: 'submitCbs' | 'validCbs' | 'cancelCbs', handle: Fn) {
@@ -404,6 +428,16 @@
}
}
.ellipsis-cell {
.cell-content {
overflow-wrap: break-word;
word-break: break-word;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
&__normal {
&-icon {
position: absolute;

View File

@@ -8,6 +8,7 @@
trigger="click"
@visibleChange="handleVisibleChange"
:overlayClassName="`${prefixCls}__cloumn-list`"
:getPopupContainer="getPopupContainer"
>
<template #title>
<div :class="`${prefixCls}__popover-title`">
@@ -47,7 +48,11 @@
{{ item.label }}
</Checkbox>
<Tooltip placement="bottomLeft" :mouseLeaveDelay="0.4">
<Tooltip
placement="bottomLeft"
:mouseLeaveDelay="0.4"
:getPopupContainer="getPopupContainer"
>
<template #title>
{{ t('component.table.settingFixedLeft') }}
</template>
@@ -64,7 +69,11 @@
/>
</Tooltip>
<Divider type="vertical" />
<Tooltip placement="bottomLeft" :mouseLeaveDelay="0.4">
<Tooltip
placement="bottomLeft"
:mouseLeaveDelay="0.4"
:getPopupContainer="getPopupContainer"
>
<template #title>
{{ t('component.table.settingFixedRight') }}
</template>
@@ -109,8 +118,8 @@
import { useTableContext } from '../../hooks/useTableContext';
import { useDesign } from '/@/hooks/web/useDesign';
import { useSortable } from '/@/hooks/web/useSortable';
import { isNullAndUnDef } from '/@/utils/is';
import { getPopupContainer } from '/@/utils';
import { isFunction, isNullAndUnDef } from '/@/utils/is';
import { getPopupContainer as getParentContainer } from '/@/utils';
import { omit } from 'lodash-es';
interface State {
@@ -140,7 +149,7 @@
},
emits: ['columns-change'],
setup(_, { emit }) {
setup(_, { emit, attrs }) {
const { t } = useI18n();
const table = useTableContext();
@@ -350,6 +359,12 @@
emit('columns-change', data);
}
function getPopupContainer() {
return isFunction(attrs.getPopupContainer)
? attrs.getPopupContainer()
: getParentContainer();
}
return {
t,
...toRefs(state),

View File

@@ -1,20 +1,25 @@
<template>
<div class="table-settings">
<RedoSetting v-if="getSetting.redo" />
<SizeSetting v-if="getSetting.size" />
<ColumnSetting v-if="getSetting.setting" @columns-change="handleColumnChange" />
<FullScreenSetting v-if="getSetting.fullScreen" />
<RedoSetting v-if="getSetting.redo" :getPopupContainer="getTableContainer" />
<SizeSetting v-if="getSetting.size" :getPopupContainer="getTableContainer" />
<ColumnSetting
v-if="getSetting.setting"
@columns-change="handleColumnChange"
:getPopupContainer="getTableContainer"
/>
<FullScreenSetting v-if="getSetting.fullScreen" :getPopupContainer="getTableContainer" />
</div>
</template>
<script lang="ts">
import type { PropType } from 'vue';
import type { TableSetting, ColumnChangeParam } from '../../types/table';
import { defineComponent, computed } from 'vue';
import { defineComponent, computed, unref } from 'vue';
import ColumnSetting from './ColumnSetting.vue';
import SizeSetting from './SizeSetting.vue';
import RedoSetting from './RedoSetting.vue';
import FullScreenSetting from './FullScreenSetting.vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext';
export default defineComponent({
name: 'TableSetting',
@@ -33,6 +38,7 @@
emits: ['columns-change'],
setup(props, { emit }) {
const { t } = useI18n();
const table = useTableContext();
const getSetting = computed((): TableSetting => {
return {
@@ -48,7 +54,11 @@
emit('columns-change', data);
}
return { getSetting, t, handleColumnChange };
function getTableContainer() {
return table ? unref(table.wrapRef) : document.body;
}
return { getSetting, t, handleColumnChange, getTableContainer };
},
});
</script>

View File

@@ -149,18 +149,8 @@ export function useDataSource(
rowKey: string | number,
record: Recordable
): Recordable | undefined {
if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
const rowKeyName = unref(getRowKey);
if (!rowKeyName) {
return;
}
const row = dataSourceRef.value.find((r) => {
if (typeof rowKeyName === 'function') {
return (rowKeyName(r) as string) === rowKey;
} else {
return Reflect.has(r, rowKeyName) && r[rowKeyName] === rowKey;
}
});
const row = findTableDataRecord(rowKey);
if (row) {
for (const field in row) {
if (Reflect.has(record, field)) row[field] = record[field];
@@ -169,6 +159,43 @@ export function useDataSource(
}
}
function findTableDataRecord(rowKey: string | number) {
if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
const rowKeyName = unref(getRowKey);
if (!rowKeyName) return;
const { childrenColumnName = 'children' } = unref(propsRef);
const findRow = (array: any[]) => {
let ret;
array.some(function iter(r) {
if (typeof rowKeyName === 'function') {
if ((rowKeyName(r) as string) === rowKey) {
ret = r;
return true;
}
} else {
if (Reflect.has(r, rowKeyName) && r[rowKeyName] === rowKey) {
ret = r;
return true;
}
}
return r[childrenColumnName] && r[childrenColumnName].some(iter);
});
return ret;
};
// const row = dataSourceRef.value.find(r => {
// if (typeof rowKeyName === 'function') {
// return (rowKeyName(r) as string) === rowKey
// } else {
// return Reflect.has(r, rowKeyName) && r[rowKeyName] === rowKey
// }
// })
return findRow(dataSourceRef.value);
}
async function fetch(opt?: FetchParams) {
const { api, searchInfo, fetchSetting, beforeFetch, afterFetch, useSearchForm, pagination } =
unref(propsRef);
@@ -280,6 +307,7 @@ export function useDataSource(
reload,
updateTableData,
updateTableDataRecord,
findTableDataRecord,
handleTableChange,
};
}

View File

@@ -1,6 +1,6 @@
import type { PaginationProps } from '../types/pagination';
import type { BasicTableProps } from '../types/table';
import { computed, unref, ref, ComputedRef } from 'vue';
import { computed, unref, ref, ComputedRef, watchEffect } from 'vue';
import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
import { isBoolean } from '/@/utils/is';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '../const';
@@ -27,6 +27,16 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) {
const configRef = ref<PaginationProps>({});
const show = ref(true);
watchEffect(() => {
const { pagination } = unref(refProps);
if (!isBoolean(pagination) && pagination) {
configRef.value = {
...unref(configRef),
...(pagination ?? {}),
};
}
});
const getPaginationInfo = computed((): PaginationProps | boolean => {
const { pagination } = unref(refProps);

View File

@@ -1,8 +1,9 @@
import { isFunction } from '/@/utils/is';
import type { BasicTableProps, TableRowSelection } from '../types/table';
import { computed, ref, unref, ComputedRef, Ref, toRaw, watch, nextTick } from 'vue';
import { computed, ComputedRef, nextTick, Ref, ref, toRaw, unref, watch } from 'vue';
import { ROW_KEY } from '../const';
import { omit } from 'lodash-es';
import { findNodeAll } from '/@/utils/helper/treeHelper';
export function useRowSelection(
propsRef: ComputedRef<BasicTableProps>,
@@ -21,11 +22,12 @@ export function useRowSelection(
return {
selectedRowKeys: unref(selectedRowKeysRef),
hideDefaultSelections: false,
onChange: (selectedRowKeys: string[], selectedRows: Recordable[]) => {
selectedRowKeysRef.value = selectedRowKeys;
selectedRowRef.value = selectedRows;
onChange: (selectedRowKeys: string[]) => {
setSelectedRowKeys(selectedRowKeys);
// selectedRowKeysRef.value = selectedRowKeys;
// selectedRowRef.value = selectedRows;
},
...omit(rowSelection === undefined ? {} : rowSelection, ['onChange']),
...omit(rowSelection, ['onChange']),
};
});
@@ -64,11 +66,13 @@ export function useRowSelection(
function setSelectedRowKeys(rowKeys: string[]) {
selectedRowKeysRef.value = rowKeys;
const rows = toRaw(unref(tableData)).filter((item) =>
rowKeys.includes(item[unref(getRowKey) as string])
selectedRowRef.value = findNodeAll(
toRaw(unref(tableData)),
(item) => rowKeys.includes(item[unref(getRowKey) as string]),
{
children: propsRef.value.childrenColumnName ?? 'children',
}
);
selectedRowRef.value = rows;
}
function setSelectedRows(rows: Recordable[]) {

View File

@@ -122,6 +122,9 @@ export function useTable(tableProps?: Props): [
updateTableDataRecord: (rowKey: string | number, record: Recordable) => {
return getTableInstance().updateTableDataRecord(rowKey, record);
},
findTableDataRecord: (rowKey: string | number) => {
return getTableInstance().findTableDataRecord(rowKey);
},
getRowSelection: () => {
return toRaw(getTableInstance().getRowSelection());
},

View File

@@ -41,6 +41,11 @@ export function useTableHeader(
tableTitle: () => getSlot(slots, 'tableTitle'),
}
: {}),
...(slots.headerTop
? {
headerTop: () => getSlot(slots, 'headerTop'),
}
: {}),
}
),
};

View File

@@ -68,6 +68,23 @@ export function useTableScroll(
bodyEl = tableEl.querySelector('.ant-table-body');
}
const hasScrollBarY = bodyEl.scrollHeight > bodyEl.clientHeight;
const hasScrollBarX = bodyEl.scrollWidth > bodyEl.clientWidth;
if (hasScrollBarY) {
tableEl.classList.contains('hide-scrollbar-y') &&
tableEl.classList.remove('hide-scrollbar-y');
} else {
!tableEl.classList.contains('hide-scrollbar-y') && tableEl.classList.add('hide-scrollbar-y');
}
if (hasScrollBarX) {
tableEl.classList.contains('hide-scrollbar-x') &&
tableEl.classList.remove('hide-scrollbar-x');
} else {
!tableEl.classList.contains('hide-scrollbar-x') && tableEl.classList.add('hide-scrollbar-x');
}
bodyEl!.style.height = 'unset';
if (!unref(getCanResize) || tableData.length === 0) return;

View File

@@ -3,6 +3,7 @@ export type ComponentType =
| 'InputNumber'
| 'Select'
| 'ApiSelect'
| 'ApiTreeSelect'
| 'Checkbox'
| 'Switch'
| 'DatePicker'

View File

@@ -95,6 +95,7 @@ export interface TableActionType {
setPagination: (info: Partial<PaginationProps>) => void;
setTableData: <T = Recordable>(values: T[]) => void;
updateTableDataRecord: (rowKey: string | number, record: Recordable) => Recordable | void;
findTableDataRecord: (rowKey: string | number) => Recordable | void;
getColumns: (opt?: GetColumnsParams) => BasicColumn[];
setColumns: (columns: BasicColumn[] | string[]) => void;
getDataSource: <T = Recordable>() => T[];

View File

@@ -278,8 +278,9 @@
if (!editor) {
return;
}
editor.execCommand('mceInsertContent', false, getUploadingImgName(name));
const content = editor?.getContent() ?? '';
setValue(editor, `${content}\n${getUploadingImgName(name)}`);
setValue(editor, content);
}
function handleDone(name: string, url: string) {

View File

@@ -18,8 +18,8 @@
import TreeHeader from './TreeHeader.vue';
import { ScrollContainer } from '/@/components/Container';
import { omit, get } from 'lodash-es';
import { isBoolean, isFunction } from '/@/utils/is';
import { omit, get, difference } from 'lodash-es';
import { isArray, isBoolean, isFunction } from '/@/utils/is';
import { extendSlots, getSlot } from '/@/utils/helper/tsxHelper';
import { filter } from '/@/utils/helper/treeHelper';
@@ -90,8 +90,19 @@
emit('update:selectedKeys', v);
},
onCheck: (v: CheckKeys, e: CheckEvent) => {
state.checkedKeys = v;
const rawVal = toRaw(v);
let currentValue = toRaw(state.checkedKeys) as Keys;
if (isArray(currentValue) && searchState.startSearch) {
const { key } = unref(getReplaceFields);
currentValue = difference(currentValue, getChildrenKeys(e.node.$attrs.node[key]));
if (e.checked) {
currentValue.push(e.node.$attrs.node[key]);
}
state.checkedKeys = currentValue;
} else {
state.checkedKeys = v;
}
const rawVal = toRaw(state.checkedKeys);
emit('update:value', rawVal);
emit('check', rawVal, e);
},
@@ -115,6 +126,8 @@
filterByLevel,
updateNodeByKey,
getAllKeys,
getChildrenKeys,
getEnabledKeys,
} = useTree(treeDataRef, getReplaceFields);
function getIcon(params: Recordable, icon?: string) {
@@ -168,7 +181,7 @@
}
function checkAll(checkAll: boolean) {
state.checkedKeys = checkAll ? getAllKeys() : ([] as Keys);
state.checkedKeys = checkAll ? getEnabledKeys() : ([] as Keys);
}
function expandAll(expandAll: boolean) {

View File

@@ -26,6 +26,45 @@ export function useTree(
}
return keys as Keys;
}
// get keys that can be checked and selected
function getEnabledKeys(list?: TreeDataItem[]) {
const keys: string[] = [];
const treeData = list || unref(treeDataRef);
const { key: keyField, children: childrenField } = unref(getReplaceFields);
if (!childrenField || !keyField) return keys;
for (let index = 0; index < treeData.length; index++) {
const node = treeData[index];
node.disabled !== true && node.selectable !== false && keys.push(node[keyField]!);
const children = node[childrenField];
if (children && children.length) {
keys.push(...(getEnabledKeys(children) as string[]));
}
}
return keys as Keys;
}
function getChildrenKeys(nodeKey: string | number, list?: TreeDataItem[]): Keys {
const keys: Keys = [];
const treeData = list || unref(treeDataRef);
const { key: keyField, children: childrenField } = unref(getReplaceFields);
if (!childrenField || !keyField) return keys;
for (let index = 0; index < treeData.length; index++) {
const node = treeData[index];
const children = node[childrenField];
if (nodeKey === node[keyField]) {
keys.push(node[keyField]!);
if (children && children.length) {
keys.push(...(getAllKeys(children) as string[]));
}
} else {
if (children && children.length) {
keys.push(...getChildrenKeys(nodeKey, children));
}
}
}
return keys as Keys;
}
// Update node
function updateNodeByKey(key: string, node: TreeDataItem, list?: TreeDataItem[]) {
@@ -146,5 +185,7 @@ export function useTree(
filterByLevel,
updateNodeByKey,
getAllKeys,
getChildrenKeys,
getEnabledKeys,
};
}

View File

@@ -46,6 +46,7 @@
import { uploadContainerProps } from './props';
import { omit } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
import { isArray } from '/@/utils/is';
export default defineComponent({
name: 'BasicUpload',
@@ -77,7 +78,7 @@
watch(
() => props.value,
(value = []) => {
fileList.value = value;
fileList.value = isArray(value) ? value : [];
},
{ immediate: true }
);

View File

@@ -20,6 +20,7 @@
import { downloadByUrl } from '/@/utils/file/download';
import { createPreviewColumns, createPreviewActionColumn } from './data';
import { useI18n } from '/@/hooks/web/useI18n';
import { isArray } from '/@/utils/is';
export default defineComponent({
components: { BasicModal, FileList },
@@ -33,6 +34,7 @@
watch(
() => props.value,
(value) => {
if (!isArray(value)) value = [];
fileListRef.value = value
.filter((item) => !!item)
.map((item) => {

View File

@@ -1,6 +1,6 @@
<script lang="tsx">
import type { MoveData, DragVerifyActionType } from './typing';
import { defineComponent, computed, unref, reactive, watch, ref, getCurrentInstance } from 'vue';
import { defineComponent, computed, unref, reactive, watch, ref } from 'vue';
import { useTimeoutFn } from '/@/hooks/core/useTimeout';
import BasicDragVerify from './DragVerify.vue';
import { hackCss } from '/@/utils/domUtils';
@@ -8,11 +8,11 @@
import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({
name: 'ImgRotateDargVerify',
name: 'ImgRotateDragVerify',
inheritAttrs: false,
props: rotateProps,
emits: ['success', 'change', 'update:value'],
setup(props, { emit, attrs }) {
setup(props, { emit, attrs, expose }) {
const basicRef = ref<Nullable<DragVerifyActionType>>(null);
const state = reactive({
showTip: false,
@@ -112,10 +112,8 @@
handleImgOnLoad();
}
const instance = getCurrentInstance() as any;
if (instance) {
instance.resume = resume;
}
expose({ resume });
// handleImgOnLoad();
return () => {
const { src } = props;
@@ -138,6 +136,7 @@
onClick={() => {
resume();
}}
alt="verify"
/>
{state.showTip && (
<span class={[`ir-dv-img__tip`, state.isPassing ? 'success' : 'error']}>

View File

@@ -50,15 +50,15 @@
border-color: @button-cancel-hover-border-color;
}
&[disabled],
&[disabled]:hover {
color: fade(@button-cancel-color, 40%);
background: fade(@button-cancel-bg-color, 40%);
border-color: fade(@button-cancel-border-color, 40%);
}
//&[disabled],
//&[disabled]:hover {
// color: fade(@button-cancel-color, 40%);
// background: fade(@button-cancel-bg-color, 40%);
// border-color: fade(@button-cancel-border-color, 40%);
//}
}
&.ant-btn-link.is-disabled {
[data-theme='light'] &.ant-btn-link.is-disabled {
color: rgba(0, 0, 0, 0.25);
text-shadow: none;
cursor: not-allowed !important;
@@ -67,6 +67,15 @@
box-shadow: none;
}
[data-theme='dark'] &.ant-btn-link.is-disabled {
color: rgba(255, 255, 255, 0.25) !important;
text-shadow: none;
cursor: not-allowed !important;
background-color: transparent !important;
border-color: transparent !important;
box-shadow: none;
}
// color: @white;
&-success.ant-btn-link:not([disabled='disabled']) {

View File

@@ -1,7 +1,7 @@
@import './pagination.less';
@import './input.less';
@import './btn.less';
@import './table.less';
// @import './table.less';
// TODO beta.11 fix
.ant-col {
@@ -57,3 +57,9 @@ span.anticon:not(.app-iconify) {
.modal-icon-info {
color: @primary-color !important;
}
.ant-checkbox-checked .ant-checkbox-inner::after,
.ant-tree-checkbox-checked .ant-tree-checkbox-inner::after {
border-top: 0 !important;
border-left: 0 !important;
}

View File

@@ -2,7 +2,7 @@
// fix table unnecessary scrollbar
.@{prefix-cls} {
.ant-table-wrapper {
.hide-scrollbar-y {
.ant-spin-nested-loading {
.ant-spin-container {
.ant-table {
@@ -13,7 +13,7 @@
}
.ant-table-body {
overflow: auto !important;
overflow-y: auto !important;
}
}
@@ -24,6 +24,50 @@
}
}
}
.ant-table-fixed-left {
.ant-table-body-outer {
.ant-table-body-inner {
overflow-y: auto !important;
}
}
}
}
}
}
}
}
.hide-scrollbar-x {
.ant-spin-nested-loading {
.ant-spin-container {
.ant-table {
.ant-table-content {
.ant-table-scroll {
.ant-table-hide-scrollbar {
//overflow-x: auto !important;
}
.ant-table-body {
overflow: auto !important;
}
}
.ant-table-fixed-right {
.ant-table-body-outer {
.ant-table-body-inner {
overflow-x: auto !important;
}
}
}
.ant-table-fixed-left {
.ant-table-body-outer {
.ant-table-body-inner {
overflow-x: auto !important;
}
}
}
}
}
}

View File

@@ -1,4 +0,0 @@
/*! @import */
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -6,9 +6,24 @@ html[data-theme='light'] {
.text-secondary {
color: rgba(0, 0, 0, 0.45);
}
.ant-alert-success {
background-color: #f6ffed;
border: 1px solid #b7eb8f;
}
.ant-alert-error {
background-color: #fff2f0;
border: 1px solid #ffccc7;
}
.ant-alert-warning {
background-color: #fffbe6;
border: 1px solid #ffe58f;
}
}
html[data-theme='dark'] {
[data-theme='dark'] {
.text-secondary {
color: #8b949e;
}
@@ -23,14 +38,11 @@ html[data-theme='dark'] {
0 1px 0 0 #434343 inset;
}
.ant-alert-message,
.ant-alert-with-description .ant-alert-message,
.ant-alert-description {
color: rgba(0, 0, 0, 0.85);
.ant-calendar-selected-day .ant-calendar-date {
color: rgba(0, 0, 0, 0.8);
}
.ant-checkbox-checked .ant-checkbox-inner::after {
border-top: 0;
border-left: 0;
.ant-select-tree li .ant-select-tree-node-content-wrapper.ant-select-tree-node-selected {
color: rgba(0, 0, 0, 0.9);
}
}

View File

@@ -3,6 +3,7 @@ import type { Ref } from 'vue';
interface Params {
excludeListeners?: boolean;
excludeKeys?: string[];
excludeDefaultKeys?: boolean;
}
const DEFAULT_EXCLUDE_KEYS = ['class', 'style'];
@@ -16,9 +17,9 @@ export function useAttrs(params: Params = {}): Ref<Recordable> | {} {
const instance = getCurrentInstance();
if (!instance) return {};
const { excludeListeners = false, excludeKeys = [] } = params;
const { excludeListeners = false, excludeKeys = [], excludeDefaultKeys = true } = params;
const attrs = shallowRef({});
const allExcludeKeys = excludeKeys.concat(DEFAULT_EXCLUDE_KEYS);
const allExcludeKeys = excludeKeys.concat(excludeDefaultKeys ? DEFAULT_EXCLUDE_KEYS : []);
// Since attrs are not reactive, make it reactive instead of doing in `onUpdated` hook for better performance
instance.attrs = reactive(instance.attrs);

View File

@@ -1,4 +1,4 @@
import { onMounted, ref } from 'vue';
import { onMounted, onUnmounted, ref } from 'vue';
interface ScriptOptions {
src: string;
@@ -8,10 +8,11 @@ export function useScript(opts: ScriptOptions) {
const isLoading = ref(false);
const error = ref(false);
const success = ref(false);
let script: HTMLScriptElement;
const promise = new Promise((resolve, reject) => {
onMounted(() => {
const script = document.createElement('script');
script = document.createElement('script');
script.type = 'text/javascript';
script.onload = function () {
isLoading.value = false;
@@ -32,6 +33,10 @@ export function useScript(opts: ScriptOptions) {
});
});
onUnmounted(() => {
script && script.remove();
});
return {
isLoading,
error,

View File

@@ -1,26 +1,36 @@
import { getCurrentInstance, onBeforeUnmount, ref, Ref, unref } from 'vue';
import { getCurrentInstance, onBeforeUnmount, ref, Ref, shallowRef, unref } from 'vue';
import { useRafThrottle } from '/@/utils/domUtils';
import { addResizeListener, removeResizeListener } from '/@/utils/event';
import { isDef } from '/@/utils/is';
const domSymbol = Symbol('watermark-dom');
export function useWatermark(
appendEl: Ref<HTMLElement | null> = ref(document.body) as Ref<HTMLElement>
) {
let func: Fn = () => {};
const func = useRafThrottle(function () {
const el = unref(appendEl);
if (!el) return;
const { clientHeight: height, clientWidth: width } = el;
updateWatermark({ height, width });
});
const id = domSymbol.toString();
const clear = () => {
const domId = document.getElementById(id);
if (domId) {
const el = unref(appendEl);
el && el.removeChild(domId);
}
window.removeEventListener('resize', func);
};
const createWatermark = (str: string) => {
clear();
const watermarkEl = shallowRef<HTMLElement>();
const clear = () => {
const domId = unref(watermarkEl);
watermarkEl.value = undefined;
const el = unref(appendEl);
if (!el) return;
domId && el.removeChild(domId);
removeResizeListener(el, func);
};
function createBase64(str: string) {
const can = document.createElement('canvas');
can.width = 300;
can.height = 240;
const width = 300;
const height = 240;
Object.assign(can, { width, height });
const cans = can.getContext('2d');
if (cans) {
@@ -29,30 +39,55 @@ export function useWatermark(
cans.fillStyle = 'rgba(0, 0, 0, 0.15)';
cans.textAlign = 'left';
cans.textBaseline = 'middle';
cans.fillText(str, can.width / 20, can.height);
cans.fillText(str, width / 20, height);
}
return can.toDataURL('image/png');
}
function updateWatermark(
options: {
width?: number;
height?: number;
str?: string;
} = {}
) {
const el = unref(watermarkEl);
if (!el) return;
if (isDef(options.width)) {
el.style.width = `${options.width}px`;
}
if (isDef(options.height)) {
el.style.height = `${options.height}px`;
}
if (isDef(options.str)) {
el.style.background = `url(${createBase64(options.str)}) left top repeat`;
}
}
const createWatermark = (str: string) => {
if (unref(watermarkEl)) {
updateWatermark({ str });
return id;
}
const div = document.createElement('div');
watermarkEl.value = div;
div.id = id;
div.style.pointerEvents = 'none';
div.style.top = '0px';
div.style.left = '0px';
div.style.position = 'absolute';
div.style.zIndex = '100000';
div.style.width = document.documentElement.clientWidth + 'px';
div.style.height = document.documentElement.clientHeight + 'px';
div.style.background = 'url(' + can.toDataURL('image/png') + ') left top repeat';
const el = unref(appendEl);
el && el.appendChild(div);
if (!el) return id;
const { clientHeight: height, clientWidth: width } = el;
updateWatermark({ str, width, height });
el.appendChild(div);
return id;
};
function setWatermark(str: string) {
createWatermark(str);
func = () => {
createWatermark(str);
};
window.addEventListener('resize', func);
addResizeListener(document.documentElement, func);
const instance = getCurrentInstance();
if (instance) {
onBeforeUnmount(() => {

View File

@@ -1,9 +1,9 @@
<template>
<div :class="[prefixCls, `${prefixCls}--${theme}`]">
<a-breadcrumb :routes="routes">
<template #itemRender="{ route, routes, paths }">
<template #itemRender="{ route, routes: routesMatched, paths }">
<Icon :icon="getIcon(route)" v-if="getShowBreadCrumbIcon && getIcon(route)" />
<span v-if="!hasRedirect(routes, route)">
<span v-if="!hasRedirect(routesMatched, route)">
{{ t(route.name || route.meta.title) }}
</span>
<router-link v-else to="" @click="handleClick(route, paths, $event)">
@@ -15,6 +15,7 @@
</template>
<script lang="ts">
import type { RouteLocationMatched } from 'vue-router';
import { useRouter } from 'vue-router';
import type { Menu } from '/@/router/types';
import { defineComponent, ref, watchEffect } from 'vue';
@@ -26,7 +27,6 @@
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { useGo } from '/@/hooks/web/usePage';
import { useI18n } from '/@/hooks/web/useI18n';
import { useRouter } from 'vue-router';
import { propTypes } from '/@/utils/propTypes';
import { isString } from '/@/utils/is';
@@ -96,7 +96,7 @@
}
function filterItem(list: RouteLocationMatched[]) {
let resultList = filter(list, (item) => {
return filter(list, (item) => {
const { meta, name } = item;
if (!meta) {
return !!name;
@@ -107,8 +107,6 @@
}
return true;
}).filter((item) => !item.meta?.hideBreadcrumb || !item.meta?.hideMenu);
return resultList;
}
function handleClick(route: RouteLocationMatched, paths: string[], e: Event) {
@@ -140,10 +138,7 @@
}
function hasRedirect(routes: RouteLocationMatched[], route: RouteLocationMatched) {
if (routes.indexOf(route) === routes.length - 1) {
return false;
}
return true;
return routes.indexOf(route) !== routes.length - 1;
}
function getIcon(route) {

View File

@@ -1,11 +1,20 @@
<template>
<a-list :class="prefixCls">
<template v-for="item in list" :key="item.id">
<a-list :class="prefixCls" bordered :pagination="getPagination">
<template v-for="item in getData" :key="item.id">
<a-list-item class="list-item">
<a-list-item-meta>
<template #title>
<div class="title">
{{ item.title }}
<a-typography-paragraph
@click="handleTitleClick(item)"
style="width: 100%; margin-bottom: 0 !important"
:style="{ cursor: isTitleClickable ? 'pointer' : '' }"
:delete="!!item.titleDelete"
:ellipsis="
$props.titleRows > 0 ? { rows: $props.titleRows, tooltip: item.title } : false
"
:content="item.title"
/>
<div class="extra" v-if="item.extra">
<a-tag class="tag" :color="item.color">
{{ item.extra }}
@@ -21,8 +30,16 @@
<template #description>
<div>
<div class="description">
{{ item.description }}
<div class="description" v-if="item.description">
<a-typography-paragraph
style="width: 100%; margin-bottom: 0 !important"
:ellipsis="
$props.descRows > 0
? { rows: $props.descRows, tooltip: item.description }
: false
"
:content="item.description"
/>
</div>
<div class="datetime">
{{ item.datetime }}
@@ -35,16 +52,18 @@
</a-list>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import { computed, defineComponent, PropType, ref, watch, unref } from 'vue';
import { ListItem } from './data';
import { useDesign } from '/@/hooks/web/useDesign';
import { List, Avatar, Tag } from 'ant-design-vue';
import { List, Avatar, Tag, Typography } from 'ant-design-vue';
import { isNumber } from '/@/utils/is';
export default defineComponent({
components: {
[Avatar.name]: Avatar,
[List.name]: List,
[List.Item.name]: List.Item,
AListItemMeta: List.Item.Meta,
ATypographyParagraph: Typography.Paragraph,
[Tag.name]: Tag,
},
props: {
@@ -52,10 +71,67 @@
type: Array as PropType<ListItem[]>,
default: () => [],
},
pageSize: {
type: [Boolean, Number] as PropType<Boolean | Number>,
default: 5,
},
currentPage: {
type: Number,
default: 1,
},
titleRows: {
type: Number,
default: 1,
},
descRows: {
type: Number,
default: 2,
},
onTitleClick: {
type: Function as PropType<(Recordable) => void>,
},
},
setup() {
emits: ['update:currentPage'],
setup(props, { emit }) {
const { prefixCls } = useDesign('header-notify-list');
return { prefixCls };
const current = ref(props.currentPage || 1);
const getData = computed(() => {
const { pageSize, list } = props;
console.log('refreshData', list);
if (pageSize === false) return [];
let size = isNumber(pageSize) ? pageSize : 5;
return list.slice(size * (unref(current) - 1), size * unref(current));
});
watch(
() => props.currentPage,
(v) => {
current.value = v;
}
);
const isTitleClickable = computed(() => !!props.onTitleClick);
const getPagination = computed(() => {
const { list, pageSize } = props;
if (pageSize > 0 && list && list.length > pageSize) {
return {
total: list.length,
pageSize,
//size: 'small',
current: unref(current),
onChange(page) {
current.value = page;
emit('update:currentPage', page);
},
};
} else {
return false;
}
});
function handleTitleClick(item: ListItem) {
props.onTitleClick && props.onTitleClick(item);
}
return { prefixCls, getPagination, getData, handleTitleClick, isTitleClickable };
},
});
</script>
@@ -67,6 +143,10 @@
display: none;
}
::v-deep(.ant-pagination-disabled) {
display: inline-block !important;
}
&-item {
padding: 6px;
overflow: hidden;

View File

@@ -1,7 +1,10 @@
export interface ListItem {
id: string;
avatar: string;
// 通知的标题内容
title: string;
// 是否在标题上显示删除线
titleDelete?: boolean;
datetime: string;
type: string;
read?: boolean;
@@ -56,6 +59,55 @@ export const tabListData: TabItem[] = [
datetime: '2017-08-07',
type: '1',
},
{
id: '000000005',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
title:
'标题可以设置自动显示省略号本例中标题行数已设为1行如果内容超过1行将自动截断并支持tooltip显示完整标题。',
description: '',
datetime: '2017-08-07',
type: '1',
},
{
id: '000000006',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
title: '左侧图标用于区分不同的类型',
description: '',
datetime: '2017-08-07',
type: '1',
},
{
id: '000000007',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
title: '左侧图标用于区分不同的类型',
description: '',
datetime: '2017-08-07',
type: '1',
},
{
id: '000000008',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
title: '左侧图标用于区分不同的类型',
description: '',
datetime: '2017-08-07',
type: '1',
},
{
id: '000000009',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
title: '左侧图标用于区分不同的类型',
description: '',
datetime: '2017-08-07',
type: '1',
},
{
id: '000000010',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
title: '左侧图标用于区分不同的类型',
description: '',
datetime: '2017-08-07',
type: '1',
},
],
},
{
@@ -84,7 +136,8 @@ export const tabListData: TabItem[] = [
id: '000000008',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '标题',
description: '这种模板用于提醒谁与你发生了互动',
description:
'请将鼠标移动到此处以便测试超长的消息在此处将如何处理。本例中设置的描述最大行数为2超过2行的描述内容将被省略并且可以通过tooltip查看完整内容',
datetime: '2017-08-07',
type: '2',
clickClose: true,

View File

@@ -6,13 +6,15 @@
</Badge>
<template #content>
<Tabs>
<template v-for="item in tabListData" :key="item.key">
<template v-for="item in listData" :key="item.key">
<TabPane>
<template #tab>
{{ item.name }}
<span v-if="item.list.length !== 0">({{ item.list.length }})</span>
</template>
<NoticeList :list="item.list" />
<!-- 绑定title-click事件的通知列表中标题是可点击-->
<NoticeList :list="item.list" v-if="item.key === '1'" @title-click="onNoticeClick" />
<NoticeList :list="item.list" v-else />
</TabPane>
</template>
</Tabs>
@@ -21,28 +23,40 @@
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { computed, defineComponent, ref } from 'vue';
import { Popover, Tabs, Badge } from 'ant-design-vue';
import { BellOutlined } from '@ant-design/icons-vue';
import { tabListData } from './data';
import { tabListData, ListItem } from './data';
import NoticeList from './NoticeList.vue';
import { useDesign } from '/@/hooks/web/useDesign';
import { useMessage } from '/@/hooks/web/useMessage';
export default defineComponent({
components: { Popover, BellOutlined, Tabs, TabPane: Tabs.TabPane, Badge, NoticeList },
setup() {
const { prefixCls } = useDesign('header-notify');
const { createMessage } = useMessage();
const listData = ref(tabListData);
let count = 0;
const count = computed(() => {
let count = 0;
for (let i = 0; i < tabListData.length; i++) {
count += tabListData[i].list.length;
}
return count;
});
for (let i = 0; i < tabListData.length; i++) {
count += tabListData[i].list.length;
function onNoticeClick(record: ListItem) {
createMessage.success('你点击了通知ID=' + record.id);
// 可以直接将其标记为已读(为标题添加删除线),此处演示的代码会切换删除线状态
record.titleDelete = !record.titleDelete;
}
return {
prefixCls,
tabListData,
listData,
count,
onNoticeClick,
numberStyle: {},
};
},

View File

@@ -181,6 +181,12 @@
font-size: 16px !important;
}
.ant-badge {
span {
color: @white;
}
}
&:hover {
background-color: @header-dark-bg-hover-color;
}

View File

@@ -15,7 +15,6 @@
:collapsedWidth="getCollapsedWidth"
:theme="getMenuTheme"
@breakpoint="onBreakpointChange"
@collapse="toggleCollapsed"
:trigger="getTrigger"
v-bind="getTriggerAttr"
>

View File

@@ -15,7 +15,7 @@
>
<AppLogo :showTitle="false" :class="`${prefixCls}-logo`" />
<Trigger :class="`${prefixCls}-trigger`" />
<LayoutTrigger :class="`${prefixCls}-trigger`" />
<ScrollContainer>
<ul :class="`${prefixCls}-module`">
@@ -63,7 +63,7 @@
</div>
<ScrollContainer :class="`${prefixCls}-menu-list__content`">
<SimpleMenu
:items="chilrenMenus"
:items="childrenMenus"
:theme="getMenuTheme"
mixSider
@menuClick="handleMenuClick"
@@ -80,24 +80,23 @@
<script lang="ts">
import type { Menu } from '/@/router/types';
import type { CSSProperties } from 'vue';
import { computed, defineComponent, onMounted, ref, unref } from 'vue';
import type { RouteLocationNormalized } from 'vue-router';
import { defineComponent, onMounted, ref, computed, unref } from 'vue';
import { ScrollContainer } from '/@/components/Container';
import { SimpleMenuTag } from '/@/components/SimpleMenu';
import { SimpleMenu, SimpleMenuTag } from '/@/components/SimpleMenu';
import { Icon } from '/@/components/Icon';
import { AppLogo } from '/@/components/Application';
import Trigger from '../trigger/HeaderTrigger.vue';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useDragLine } from './useLayoutSider';
import { useGlobSetting } from '/@/hooks/setting';
import { useDesign } from '/@/hooks/web/useDesign';
import { useI18n } from '/@/hooks/web/useI18n';
import { useGo } from '/@/hooks/web/usePage';
import { SIDE_BAR_SHOW_TIT_MINI_WIDTH, SIDE_BAR_MINI_WIDTH } from '/@/enums/appEnum';
import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
import clickOutside from '/@/directives/clickOutside';
import { getShallowMenus, getChildrenMenus, getCurrentParentPath } from '/@/router/menus';
import { getChildrenMenus, getCurrentParentPath, getShallowMenus } from '/@/router/menus';
import { listenerRouteChange } from '/@/logics/mitt/routeChange';
import { SimpleMenu } from '/@/components/SimpleMenu';
import LayoutTrigger from '../trigger/index.vue';
export default defineComponent({
name: 'LayoutMixSider',
@@ -106,7 +105,7 @@
AppLogo,
SimpleMenu,
Icon,
Trigger,
LayoutTrigger,
SimpleMenuTag,
},
directives: {
@@ -115,7 +114,7 @@
setup() {
let menuModules = ref<Menu[]>([]);
const activePath = ref('');
const chilrenMenus = ref<Menu[]>([]);
const childrenMenus = ref<Menu[]>([]);
const openMenu = ref(false);
const dragBarRef = ref<ElRef>(null);
const sideRef = ref<ElRef>(null);
@@ -151,7 +150,7 @@
const getIsFixed = computed(() => {
/* eslint-disable-next-line */
mixSideHasChildren.value = unref(chilrenMenus).length > 0;
mixSideHasChildren.value = unref(childrenMenus).length > 0;
const isFixed = unref(getMixSideFixed) && unref(mixSideHasChildren);
if (isFixed) {
/* eslint-disable-next-line */
@@ -179,6 +178,7 @@
return !unref(getMixSideFixed)
? {
onMouseleave: () => {
setActive(true);
closeMenu();
},
}
@@ -209,9 +209,8 @@
}
// Process module menu click
async function hanldeModuleClick(path: string, hover = false) {
async function handleModuleClick(path: string, hover = false) {
const children = await getChildrenMenus(path);
if (unref(activePath) === path) {
if (!hover) {
if (!unref(openMenu)) {
@@ -219,6 +218,10 @@
} else {
closeMenu();
}
} else {
if (!unref(openMenu)) {
openMenu.value = true;
}
}
if (!unref(openMenu)) {
setActive();
@@ -229,20 +232,19 @@
}
if (!children || children.length === 0) {
go(path);
chilrenMenus.value = [];
if (!hover) go(path);
childrenMenus.value = [];
closeMenu();
return;
}
chilrenMenus.value = children;
childrenMenus.value = children;
}
// Set the currently active menu and submenu
async function setActive(setChildren = false) {
const path = currentRoute.value?.path;
if (!path) return;
const parentPath = await getCurrentParentPath(path);
activePath.value = parentPath;
activePath.value = await getCurrentParentPath(path);
// hanldeModuleClick(parentPath);
if (unref(getIsMixSidebar)) {
const activeMenu = unref(menuModules).find((item) => item.path === unref(activePath));
@@ -250,14 +252,14 @@
if (p) {
const children = await getChildrenMenus(p);
if (setChildren) {
chilrenMenus.value = children;
childrenMenus.value = children;
if (unref(getMixSideFixed)) {
openMenu.value = children.length > 0;
}
}
if (children.length === 0) {
chilrenMenus.value = [];
childrenMenus.value = [];
}
}
}
@@ -275,11 +277,15 @@
function getItemEvents(item: Menu) {
if (unref(getMixSideTrigger) === 'hover') {
return {
onMouseenter: () => hanldeModuleClick(item.path, true),
onMouseenter: () => handleModuleClick(item.path, true),
onClick: async () => {
const children = await getChildrenMenus(item.path);
if (item.path && (!children || children.length === 0)) go(item.path);
},
};
}
return {
onClick: () => hanldeModuleClick(item.path),
onClick: () => handleModuleClick(item.path),
};
}
@@ -300,9 +306,9 @@
t,
prefixCls,
menuModules,
hanldeModuleClick,
handleModuleClick: handleModuleClick,
activePath,
chilrenMenus,
childrenMenus: childrenMenus,
getShowDragBar,
handleMenuClick,
getMenuStyle,
@@ -480,23 +486,24 @@
bottom: 0;
left: 0;
width: 100%;
padding: 6px;
padding-left: 12px;
font-size: 18px;
font-size: 14px;
color: rgba(255, 255, 255, 0.65);
text-align: center;
cursor: pointer;
background-color: @sider-dark-bg-color;
background-color: @trigger-dark-bg-color;
height: 36px;
line-height: 36px;
}
&.light &-trigger {
color: rgba(0, 0, 0, 0.65);
background-color: #fff;
border-top: 1px solid #eee;
}
&-menu-list {
position: fixed;
top: 0;
width: 0;
width: 200px;
height: calc(100%);
background-color: #fff;

View File

@@ -1,6 +1,6 @@
import { genMessage } from '../helper';
import antdLocale from 'ant-design-vue/es/locale/en_US';
import momentLocale from 'moment/dist/locale/eu';
// import momentLocale from 'moment/dist/locale/en-us';
const modules = import.meta.globEager('./en/**/*.ts');
export default {
@@ -8,6 +8,6 @@ export default {
...genMessage(modules, 'en'),
antdLocale,
},
momentLocale,
momentLocaleName: 'eu',
momentLocale: null,
momentLocaleName: 'en',
};

View File

@@ -1,13 +1,24 @@
import { darkCssIsReady, loadDarkThemeCss } from 'vite-plugin-theme/es/client';
import { addClass, hasClass, removeClass } from '/@/utils/domUtils';
export async function updateDarkTheme(mode: string | null = 'light') {
const htmlRoot = document.getElementById('htmlRoot');
if (!htmlRoot) {
return;
}
const hasDarkClass = hasClass(htmlRoot, 'dark');
if (mode === 'dark') {
if (import.meta.env.PROD && !darkCssIsReady) {
await loadDarkThemeCss();
}
htmlRoot?.setAttribute('data-theme', 'dark');
htmlRoot.setAttribute('data-theme', 'dark');
if (!hasDarkClass) {
addClass(htmlRoot, 'dark');
}
} else {
htmlRoot?.setAttribute('data-theme', 'light');
htmlRoot.setAttribute('data-theme', 'light');
if (hasDarkClass) {
removeClass(htmlRoot, 'dark');
}
}
}

View File

@@ -1,5 +1,7 @@
import '/@/design/index.less';
import '/@/design/tailwind.css';
// Register windi
import 'virtual:windi.css';
// Register icon sprite
import 'virtual:svg-icons-register';
import App from './App.vue';

View File

@@ -2,6 +2,8 @@ export const REDIRECT_NAME = 'Redirect';
export const PARENT_LAYOUT_NAME = 'ParentLayout';
export const PAGE_NOT_FOUND_NAME = 'PageNotFound';
export const EXCEPTION_COMPONENT = () => import('../views/sys/exception/Exception.vue');
/**

View File

@@ -7,17 +7,25 @@ import { useUserStoreWithOut } from '/@/store/modules/user';
import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
import { RootRoute } from '/@/router/routes';
const LOGIN_PATH = PageEnum.BASE_LOGIN;
const ROOT_PATH = RootRoute.path;
const whitePathList: PageEnum[] = [LOGIN_PATH];
export function createPermissionGuard(router: Router) {
const userStore = useUserStoreWithOut();
const permissionStore = usePermissionStoreWithOut();
router.beforeEach(async (to, from, next) => {
// Jump to the 404 page after processing the login
if (from.path === LOGIN_PATH && to.name === PAGE_NOT_FOUND_ROUTE.name) {
next(PageEnum.BASE_HOME);
if (
from.path === ROOT_PATH &&
to.path === PageEnum.BASE_HOME &&
userStore.getUserInfo.homePath &&
userStore.getUserInfo.homePath !== PageEnum.BASE_HOME
) {
next(userStore.getUserInfo.homePath);
return;
}
@@ -52,6 +60,17 @@ export function createPermissionGuard(router: Router) {
return;
}
// Jump to the 404 page after processing the login
if (
from.path === LOGIN_PATH &&
to.name === PAGE_NOT_FOUND_ROUTE.name &&
to.fullPath !== (userStore.getUserInfo.homePath || PageEnum.BASE_HOME)
) {
next(userStore.getUserInfo.homePath || PageEnum.BASE_HOME);
console.log({ from, to });
return;
}
if (permissionStore.getIsDynamicAddedRoute) {
next();
return;
@@ -63,10 +82,19 @@ export function createPermissionGuard(router: Router) {
router.addRoute(route as unknown as RouteRecordRaw);
});
const redirectPath = (from.query.redirect || to.path) as string;
const redirect = decodeURIComponent(redirectPath);
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect };
router.addRoute(PAGE_NOT_FOUND_ROUTE as unknown as RouteRecordRaw);
permissionStore.setDynamicAddedRoute(true);
next(nextData);
if (to.name === PAGE_NOT_FOUND_ROUTE.name) {
// 动态添加路由后此处应当重定向到fullPath否则会加载404页面内容
// fix: 添加query以免丢失
next({ path: to.fullPath, replace: true, query: to.query });
} else {
const redirectPath = (from.query.redirect || to.path) as string;
const redirect = decodeURIComponent(redirectPath);
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect };
next(nextData);
}
});
}

View File

@@ -62,6 +62,7 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi
name: title,
hideMenu,
path: node.path,
...(node.redirect ? { redirect: node.redirect } : {}),
};
},
});
@@ -72,14 +73,16 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi
/**
* config menu with given params
*/
const menuParamRegex = /(?<=:)([\s\S]+?)((?=\/)|$)/g;
const menuParamRegex = /(?::)([\s\S]+?)((?=\/)|$)/g;
export function configureDynamicParamsMenu(menu: Menu, params: RouteParams) {
const { path, paramPath } = toRaw(menu);
let realPath = paramPath ? paramPath : path;
const matchArr = realPath.match(menuParamRegex);
matchArr?.forEach((it) => {
if (params[it]) {
realPath = realPath.replace(`:${it}`, params[it] as string);
const realIt = it.substr(1);
if (params[realIt]) {
realPath = realPath.replace(`:${realIt}`, params[realIt] as string);
}
});
// save original param path.

View File

@@ -54,7 +54,7 @@ const staticMenus: Menu[] = [];
async function getAsyncMenus() {
const permissionStore = usePermissionStore();
if (isBackMode()) {
return permissionStore.getBackMenuList;
return permissionStore.getBackMenuList.filter((item) => !item.meta?.hideMenu && !item.hideMenu);
}
if (isRouteMappingMode()) {
return permissionStore.getFrontMenuList.filter((item) => !item.hideMenu);

View File

@@ -1,11 +1,16 @@
import type { AppRouteRecordRaw } from '/@/router/types';
import { t } from '/@/hooks/web/useI18n';
import { REDIRECT_NAME, LAYOUT, EXCEPTION_COMPONENT } from '/@/router/constant';
import {
REDIRECT_NAME,
LAYOUT,
EXCEPTION_COMPONENT,
PAGE_NOT_FOUND_NAME,
} from '/@/router/constant';
// 404 on a page
export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = {
path: '/:path(.*)*',
name: 'ErrorPage',
name: PAGE_NOT_FOUND_NAME,
component: LAYOUT,
meta: {
title: 'ErrorPage',
@@ -15,11 +20,12 @@ export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = {
children: [
{
path: '/:path(.*)*',
name: 'ErrorPage',
name: PAGE_NOT_FOUND_NAME,
component: EXCEPTION_COMPONENT,
meta: {
title: 'ErrorPage',
hideBreadcrumb: true,
hideMenu: true,
},
},
],
@@ -51,9 +57,11 @@ export const ERROR_LOG_ROUTE: AppRouteRecordRaw = {
path: '/error-log',
name: 'ErrorLog',
component: LAYOUT,
redirect: '/error-log/list',
meta: {
title: 'ErrorLog',
hideBreadcrumb: true,
hideChildrenInMenu: true,
},
children: [
{
@@ -63,6 +71,7 @@ export const ERROR_LOG_ROUTE: AppRouteRecordRaw = {
meta: {
title: t('routes.basic.errorLogList'),
hideBreadcrumb: true,
currentActiveMenu: '/error-log',
},
},
],

View File

@@ -37,4 +37,10 @@ export const LoginRoute: AppRouteRecordRaw = {
};
// Basic routing without permission
export const basicRoutes = [LoginRoute, RootRoute, ...mainOutRoutes, REDIRECT_ROUTE];
export const basicRoutes = [
LoginRoute,
RootRoute,
...mainOutRoutes,
REDIRECT_ROUTE,
PAGE_NOT_FOUND_ROUTE,
];

View File

@@ -22,6 +22,7 @@ const dashboard: AppRouteModule = {
meta: {
title: t('routes.dashboard.about'),
icon: 'simple-icons:about-dot-me',
hideMenu: true,
},
},
],

View File

@@ -19,7 +19,7 @@ const dashboard: AppRouteModule = {
name: 'Analysis',
component: () => import('/@/views/dashboard/analysis/index.vue'),
meta: {
affix: true,
// affix: true,
title: t('routes.dashboard.analysis'),
},
},

View File

@@ -181,7 +181,7 @@ const feat: AppRouteModule = {
},
},
{
path: 'error-log',
path: '/error-log',
name: 'ErrorLog',
component: () => import('/@/views/sys/error-log/index.vue'),
meta: {

Some files were not shown because too many files have changed in this diff Show More