Compare commits

...

82 Commits

Author SHA1 Message Date
小诺
39d745536c 【新增】新增前端批量处理按钮(可配置类型、图标、名称),新增正数金额校验规则 2023-04-19 21:55:15 +08:00
小诺
2335f1f34b 【更新】去掉前端加密工具类中的私钥以及基本无用的方法 2023-04-19 21:40:04 +08:00
xiaonuobase
aa8cbb847d 【升级】退出后路由回到登陆前使用的页面,感谢码云 冬天 建议 2023-04-19 17:54:43 +08:00
小诺
3e28c838ee 【更新】字典功能被选中后列表包含自己,且自动区分上下级 2023-04-14 00:25:35 +08:00
xuyuxiang
18ee9d4ec8 【修复】修复过滤器sa-token校验规则bug 2023-04-13 15:06:01 +08:00
jiwangyu
1943c3985f 【修复】修复TinyMCE6段落样式、字体、列表编号控件不显示问题 2023-04-10 05:58:38 +00:00
woyuno
4155d160cb 【优化】过滤器sa-token校验规则优化 2023-04-10 05:08:32 +00:00
guaiwu
95c86975ac 提交postgresql数据库最新的初始化脚本 2023-04-10 04:14:29 +00:00
xiaonuobase
85a96962e0 【更新】操作日志详情高亮组件增加滚动条 2023-04-07 11:44:41 +08:00
xuyuxiang
308d08b81f 【优化】优化角色和用户授权逻辑判断 2023-04-07 11:13:02 +08:00
xiaonuobase
5a46563a47 【更新】授权菜单资源加入提示更改 2023-04-07 11:02:45 +08:00
xiaonuobase
e5d7a8aef4 【更新】授权菜单资源加入提示 2023-04-07 10:51:16 +08:00
小诺
a050e25025 Merge branch 'master' of gitee.com:xiaonuobase/snowy into dev
Signed-off-by: 小诺 <1253070437@qq.com>
2023-04-06 02:27:17 +00:00
xuyuxiang
5b64dd0a07 【修复】修复邮箱校验错误的提示语 2023-04-06 09:23:50 +08:00
xiaonuobase
1521fcc529 【新增】新增左上角折叠菜单功能 2023-04-03 15:01:01 +08:00
小诺
a4cfc69126 【更新】顶部模块坞样式闪动改动 2023-03-29 21:16:11 +08:00
小诺
f24bb37e96 【更新】一个影响其他布局的css调整 2023-03-28 22:27:39 +08:00
徐玉祥
f5f84b1469 【更新】更新sql说明 2023-03-28 19:40:30 +08:00
dongxiayu
ffeb363126 【修复】修复 #I6FSBE 菜单树搜索 bug 2023-03-28 00:23:25 +08:00
小诺
8d43c83eef 【更新】表格{ x: 1200, y: 300 }不生效问题修复 2023-03-27 16:07:50 +00:00
xlzy
21decad50c 【更新】修复清除缓存之后,头像没有更新的问题 2023-03-27 16:07:50 +00:00
小诺
0dc2d9dcf6 【更新】关闭多标签功能后,移除其他被缓存的标签 2023-03-27 16:07:50 +00:00
小诺
89ae5eff78 【优化】调整顶部模块坞间距 2023-03-27 16:07:50 +00:00
xlzy
b845a0c253 【更新】删除无用配置,默认值就是这些 2023-03-27 16:07:50 +00:00
xlzy
9195f0ea36 【更新】显示面包屑时,间距过大 2023-03-27 16:07:50 +00:00
小诺
40d10734a2 【优化】优化css代码格式 2023-03-27 16:07:50 +00:00
xlzy
ac59260a49 【更新】顶部栏应用主题色时修正样式 2023-03-27 16:07:50 +00:00
xlzy
9b5f2502f8 【更新】首页打开控制台的显示优化 2023-03-27 16:07:50 +00:00
小诺
cc06c12f8c 【修复】修复首页两个日志模块超管不显示更多按钮的问题 2023-03-27 16:07:50 +00:00
小诺
183ea32be6 【优化】字典界面样式调整,更协调 2023-03-27 16:07:50 +00:00
xlzy
a2b7d7b713 【更新】修复最大化标签之后,模块坞不显示的问题 2023-03-27 16:07:50 +00:00
xlzy
991fba682f 【更新】超时时间设置1分钟 2023-03-27 16:07:50 +00:00
xlzy
99d66df3dc 【更新】修复修改分页之后,刷新表格时传递的pageSize参数不正确的问题 2023-03-27 16:07:50 +00:00
xlzy
8e8e59ae47 【更新】安装qs库 2023-03-27 16:07:50 +00:00
xlzy
fa6a10e7cd 【更新】解决tailwindcss控制台警告 2023-03-27 16:07:50 +00:00
xlzy
c2041c2782 【更新】兼容更多的请求method,增加moduleRequest,更方便添加前缀 2023-03-27 16:07:50 +00:00
xlzy
65fe9fce81 【更新】修复tailwindcss部分颜色不生效 2023-03-27 16:07:50 +00:00
xlzy
c4df5d65e2 【更新】移除style 2023-03-27 16:07:50 +00:00
小诺
283e42f0d2 【更新】设置抽屉内的一些bug修复,以及少数功能代码优化 2023-03-27 16:07:50 +00:00
xlzy
c016c136ae 【更新】升级vite以及其他依赖并锁定版本 2023-03-27 16:07:50 +00:00
xlzy
fa91de42b9 【更新】忽略lock文件 2023-03-27 16:07:50 +00:00
xlzy
7384ed0ce3 【更新】调整XnFormContainer 2023-03-27 16:07:50 +00:00
xuyuxiang
23d0791e4e 【修复】修复 #I6PK10 登录菜单取并集bug 2023-03-27 16:07:50 +00:00
xlzy
0ba4a8c4d0 【更新】升级vite版本 2023-03-27 16:07:50 +00:00
xlzy
e5c7326a38 【更新】替换旧的$store 2023-03-27 16:07:50 +00:00
xlzy
96fa681a91 【更新】兼容formStyle 2023-03-27 16:07:50 +00:00
xlzy
48736f6519 【更新】使用Pinia代替Vuex 2023-03-27 16:07:50 +00:00
xuyuxiang
1cdd40be28 【修复】修复 #I6NVHY 手机验证码登录验证bug 2023-03-27 16:07:50 +00:00
xuyuxiang
9f66eb52a3 【优化】优化异常打印,不使用e.printStackTrace方式 2023-03-27 16:07:50 +00:00
xuyuxiang
60049933ae 【优化】优化异常打印,不使用e.printStackTrace方式 2023-03-27 16:07:50 +00:00
小诺
50ff1b8f9f 【更新】表格{ x: 1200, y: 300 }不生效问题修复 2023-03-27 23:44:49 +08:00
xlzy
ae73f1bb14 【更新】修复清除缓存之后,头像没有更新的问题 2023-03-27 23:14:26 +08:00
小诺
cc8c7b1e07 【更新】关闭多标签功能后,移除其他被缓存的标签 2023-03-27 00:10:11 +08:00
小诺
6a74374f71 【优化】调整顶部模块坞间距 2023-03-26 23:08:20 +08:00
xlzy
3aefc5fd8b 【更新】删除无用配置,默认值就是这些 2023-03-26 23:01:31 +08:00
xlzy
1a4ec87b69 【更新】显示面包屑时,间距过大 2023-03-26 22:54:13 +08:00
小诺
3cc44e4ec1 【优化】优化css代码格式 2023-03-26 22:13:28 +08:00
xlzy
a29da7fac0 【更新】顶部栏应用主题色时修正样式 2023-03-26 20:48:58 +08:00
xlzy
9884e6bb1e 【更新】首页打开控制台的显示优化 2023-03-26 20:36:47 +08:00
小诺
a085116ae9 【修复】修复首页两个日志模块超管不显示更多按钮的问题 2023-03-26 16:06:20 +08:00
小诺
516e5f9566 【优化】字典界面样式调整,更协调 2023-03-26 15:32:40 +08:00
xlzy
4c1a31661e 【更新】修复最大化标签之后,模块坞不显示的问题 2023-03-26 14:58:00 +08:00
xlzy
3005f8e7a5 【更新】超时时间设置1分钟 2023-03-26 14:39:19 +08:00
xlzy
2c4c321590 【更新】修复修改分页之后,刷新表格时传递的pageSize参数不正确的问题 2023-03-26 13:57:34 +08:00
xlzy
d67f24ef5c 【更新】安装qs库 2023-03-26 13:07:54 +08:00
xlzy
5c43c869aa 【更新】解决tailwindcss控制台警告 2023-03-26 13:07:11 +08:00
xlzy
9db45c2297 【更新】兼容更多的请求method,增加moduleRequest,更方便添加前缀 2023-03-26 12:49:14 +08:00
xlzy
363c9b7b2b 【更新】修复tailwindcss部分颜色不生效 2023-03-26 12:09:52 +08:00
xlzy
7daf8b05de 【更新】移除style 2023-03-26 12:02:29 +08:00
小诺
9d0c481f85 【更新】设置抽屉内的一些bug修复,以及少数功能代码优化 2023-03-26 01:57:08 +08:00
xlzy
ea0ffa53ef 【更新】升级vite以及其他依赖并锁定版本 2023-03-25 17:44:35 +08:00
xlzy
2b80d75327 【更新】忽略lock文件 2023-03-25 17:42:41 +08:00
xlzy
51fe3e9a2d 【更新】调整XnFormContainer 2023-03-25 17:41:16 +08:00
xuyuxiang
da4f5f1dd3 【修复】修复 #I6PK10 登录菜单取并集bug 2023-03-24 09:18:59 +08:00
xlzy
40a0dafb4e 【更新】升级vite版本 2023-03-23 23:14:12 +08:00
xlzy
6913945e1b 【更新】替换旧的$store 2023-03-23 23:11:14 +08:00
xlzy
1ea621bf5a 【更新】兼容formStyle 2023-03-23 17:00:47 +08:00
xlzy
2c6ec39122 【更新】使用Pinia代替Vuex 2023-03-23 17:00:47 +08:00
xuyuxiang
df11702b38 【修复】修复 #I6NVHY 手机验证码登录验证bug 2023-03-23 16:57:19 +08:00
xuyuxiang
2e87918730 【优化】优化异常打印,不使用e.printStackTrace方式 2023-03-23 14:16:32 +08:00
xuyuxiang
8f2fa66647 【优化】优化异常打印,不使用e.printStackTrace方式 2023-03-23 14:13:00 +08:00
小诺
3febc21536
!96 【修复】修复登录界面刷新后无缓冲情况下报错问题
Merge pull request !96 from 小诺/dev
2023-03-23 04:11:52 +00:00
76 changed files with 3092 additions and 15809 deletions

View File

@ -102,7 +102,7 @@ snowy
|-layout == 基础布局
|-locales == 多语言配置
|-router == 基础路由配置
|-store == VUEX缓存配置
|-store == Pinia缓存配置
|-style == 样式风格配置
|-utils == 工具类
|-views == 所有视图界面
@ -232,4 +232,4 @@ QQ技术群732230670已满、685395081
- 二次开发如用于开源竞品请先联系群主沟通,未经审核视为侵权
- 请不要删除和修改Snowy源码头部的版权与作者声明及出处
- 请不要删除和修改Snowy源码头部的版权与作者声明及出处

View File

@ -1,6 +1,8 @@
.DS_Store
node_modules
/dist
*.lock
package-lock.json
# local env files
.env.local
@ -14,7 +16,6 @@ pnpm-debug.log*
# other files
stats.html
package-lock.json
auto-imports.d.ts
# Editor directories and files

File diff suppressed because it is too large Load Diff

View File

@ -10,72 +10,73 @@
"license": "Apache-2.0",
"author": "yubaoshan",
"scripts": {
"serve": "vite",
"dev": "vite --mode development",
"serve": "vite --host 0.0.0.0",
"dev": "vite --mode development --host 0.0.0.0",
"preview": "vite preview",
"build": "vite build --mode production",
"prod": "vite --mode production"
},
"dependencies": {
"@ant-design/colors": "6.0.0",
"@ant-design/icons-vue": "^6.1.0",
"@antv/g2plot": "2.4.10",
"@ant-design/colors": "7.0.0",
"@ant-design/icons-vue": "6.1.0",
"@antv/g2plot": "2.4.28",
"@chenfengyuan/vue-qrcode": "2.0.0",
"@highlightjs/vue-plugin": "^2.1.0",
"@highlightjs/vue-plugin": "2.1.0",
"@tinymce/tinymce-vue": "5.0.0",
"ant-design-vue": "3.2.14",
"axios": "1.1.3",
"cropperjs": "1.5.12",
"dayjs": "^1.11.6",
"echarts": "^5.4.0",
"echarts-stat": "^1.2.0",
"enquire.js": "^2.1.6",
"fuse.js": "^6.6.2",
"highlight.js": "^11.6.0",
"hotkeys-js": "^3.10.0",
"js-pinyin": "^0.1.9",
"lodash-es": "^4.17.21",
"dayjs": "1.11.7",
"echarts": "5.4.0",
"echarts-stat": "1.2.0",
"enquire.js": "2.1.6",
"fuse.js": "6.6.2",
"highlight.js": "11.6.0",
"hotkeys-js": "3.10.1",
"js-pinyin": "0.1.9",
"lodash-es": "4.17.21",
"nprogress": "0.2.0",
"screenfull": "^6.0.2",
"sm-crypto": "^0.3.11",
"snowflake-id": "^1.1.0",
"sortablejs": "^1.15.0",
"pinia": "2.0.33",
"qs": "6.11.1",
"screenfull": "6.0.2",
"sm-crypto": "0.3.11",
"snowflake-id": "1.1.0",
"sortablejs": "1.15.0",
"tinymce": "6.2.0",
"vue": "3.2.44",
"vue-cropper": "^1.0.1",
"vue-i18n": "^9.2.2",
"vue-router": "^4.1.6",
"vue-cropper": "1.0.5",
"vue-i18n": "9.2.2",
"vue-router": "4.1.6",
"vue3-colorpicker": "2.0.4",
"vue3-tree-org": "^4.1.1",
"vuedraggable-es": "^4.1.1",
"vuex": "^4.1.0"
"vue3-tree-org": "4.1.1",
"vuedraggable-es": "4.1.1"
},
"devDependencies": {
"@antfu/eslint-config": "^0.29.3",
"@babel/eslint-parser": "^7.19.1",
"@vitejs/plugin-legacy": "^1.6.4",
"@vitejs/plugin-vue": "^2.1.0",
"@vitejs/plugin-vue-jsx": "^1.3.8",
"@vue/compiler-sfc": "^3.2.41",
"@vue/eslint-config-standard": "^8.0.1",
"antd-less-to-css-variable": "^1.0.5",
"autoprefixer": "^10.4.13",
"eslint": "^8.26.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.7.0",
"less": "^4.1.3",
"postcss": "^8.4.18",
"prettier": "^2.7.1",
"rollup-plugin-visualizer": "^5.8.3",
"tailwindcss": "^3.2.1",
"typescript": "^4.8.4",
"unplugin-auto-import": "^0.11.4",
"unplugin-vue-components": "^0.22.9",
"vite": "2.8.6",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-vue-setup-extend": "^0.4.0",
"vue-eslint-parser": "^9.1.0"
"@antfu/eslint-config": "0.29.4",
"@babel/eslint-parser": "7.19.1",
"@vitejs/plugin-legacy": "3.0.2",
"@vitejs/plugin-vue": "4.1.0",
"@vitejs/plugin-vue-jsx": "3.0.1",
"@vue/compiler-sfc": "3.2.47",
"@vue/eslint-config-standard": "8.0.1",
"antd-less-to-css-variable": "1.0.5",
"autoprefixer": "10.4.13",
"eslint": "8.26.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-vue": "9.7.0",
"less": "4.1.3",
"postcss": "8.4.21",
"prettier": "2.8.7",
"rollup-plugin-visualizer": "5.8.3",
"tailwindcss": "3.2.7",
"typescript": "4.9.5",
"unplugin-auto-import": "0.15.2",
"unplugin-vue-components": "0.24.1",
"vite": "4.2.1",
"vite-plugin-compression": "0.5.1",
"vite-plugin-vue-setup-extend": "0.4.0",
"vue-eslint-parser": "9.1.0"
},
"browserslist": [
"> 1%",

View File

@ -31,15 +31,6 @@ module.exports = {
jsxBracketSameLine: true,
// 箭头函数,只有一个参数的时候,也需要括号
arrowParens: 'always',
// 每个文件格式化的范围是文件的全部内容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准
proseWrap: 'preserve',
// 根据显示样式决定 html 要不要折行
htmlWhitespaceSensitivity: 'css',
// 换行符使用 lf

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 31 KiB

View File

@ -1,57 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 750 750" style="enable-background:new 0 0 750 750;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.35;fill:#B3B3B3;}
.st1{opacity:0.1;fill:#B3B3B3;}
.st2{opacity:0.3;fill:#B3B3B3;}
.st3{opacity:0.1;}
.st4{fill:#B3B3B3;}
</style>
<g>
<path class="st0" d="M465.1,261.4H264c-1.3,0-2.4,1.1-2.4,2.4v255.6c0,1.3,1.1,2.4,2.4,2.4h201.1c1.3,0,2.4-1.1,2.4-2.4V263.8
C467.5,262.4,466.4,261.4,465.1,261.4z M417.9,443c0,1.3-1.1,2.4-2.4,2.4h-102c-1.3,0-2.4-1.1-2.4-2.4v-11.3c0-1.3,1.1-2.4,2.4-2.4
h102c1.3,0,2.4,1.1,2.4,2.4V443z M417.9,397.2c0,1.3-1.1,2.4-2.4,2.4h-102c-1.3,0-2.4-1.1-2.4-2.4v-11.3c0-1.3,1.1-2.4,2.4-2.4h102
c1.3,0,2.4,1.1,2.4,2.4V397.2z M417.9,351.5c0,1.3-1.1,2.4-2.4,2.4h-102c-1.3,0-2.4-1.1-2.4-2.4v-11.3c0-1.3,1.1-2.4,2.4-2.4h102
c1.3,0,2.4,1.1,2.4,2.4V351.5z"/>
<g>
<path class="st1" d="M462.1,236.8L462.1,236.8C384.8,236.2,321,295.1,314,370.7c-18.5-19.1-44.4-31.1-73.1-31.3h0
c-56.8-0.4-103.2,45.3-103.6,102.1l-0.8,101.4l175.6,1.3l30.1,0.2l265.1,2l1.2-160.9C609.2,304,543.6,237.4,462.1,236.8z"/>
<path class="st2" d="M216.9,227.4c-3.4,0-6.5,1.1-9,2.9c0.2-1,0.3-2,0.3-3c0.1-8.3-6.6-15.1-15-15.2s-15.1,6.6-15.2,15
c0,0.3,0,0.6,0,0.9c-1.6-0.6-3.4-1-5.2-1c-8.3-0.1-15.1,6.6-15.2,15c-0.1,8.2,6.4,14.9,14.5,15.2l0,0l44.6,0.3
c8.3,0.1,15.1-6.6,15.2-15S225.2,227.5,216.9,227.4z"/>
<path class="st2" d="M596.4,194.2c-3.4,0-6.5,1.1-9,2.9c0.2-1,0.3-2,0.3-3c0.1-8.3-6.6-15.1-15-15.2s-15.1,6.6-15.2,15
c0,0.3,0,0.6,0,0.9c-1.6-0.6-3.4-1-5.2-1c-8.3-0.1-15.1,6.6-15.2,15c-0.1,8.2,6.4,14.9,14.5,15.2l0,0l44.6,0.3
c8.3,0.1,15.1-6.6,15.2-15S604.7,194.3,596.4,194.2z"/>
<g>
<g class="st3">
<path class="st4" d="M496.9,497.5c-2.1,0-3.7,1.6-3.7,3.7c0,1.5,0.8,2.7,2,3.3l-0.5,65.1l3.5,0l0.5-65.3
c1.1-0.6,1.8-1.8,1.8-3.1C500.6,499.1,499,497.6,496.9,497.5z"/>
<path class="st4" d="M572.3,501.7c0-1.9-1.6-3.6-3.7-3.7c-2.1,0-3.7,1.6-3.7,3.7c0,1.4,0.8,2.6,1.9,3.2l-0.5,65.2l3.5,0
l0.5-65.2C571.5,504.3,572.2,503.1,572.3,501.7z"/>
</g>
<rect x="522.7" y="472.2" transform="matrix(7.448311e-03 -1 1 7.448311e-03 8.6828 1045.4733)" class="st1" width="16.5" height="92.3"/>
<polygon class="st1" points="495.4,509.8 495.2,510.1 485.5,526.3 484.8,526.3 484.9,509.8 "/>
<polygon class="st1" points="518.7,510 508.8,526.5 496.3,526.4 500.2,519.8 506,509.9 "/>
<polygon class="st1" points="542,510.2 532.1,526.6 519.6,526.5 529.3,510.1 "/>
<polygon class="st1" points="565.3,510.4 555.5,526.8 542.9,526.7 552.7,510.3 "/>
<polygon class="st1" points="577.2,510.4 577.1,527 566.2,526.9 576,510.4 "/>
<rect x="522.5" y="497.7" transform="matrix(7.448311e-03 -1 1 7.448311e-03 -17.0149 1070.603)" class="st1" width="16.5" height="92.3"/>
<polygon class="st1" points="495.2,535.3 495,535.6 485.3,551.8 484.6,551.8 484.7,535.3 "/>
<polygon class="st1" points="518.5,535.5 508.6,552 496.1,551.9 500,545.3 505.8,535.4 "/>
<polygon class="st1" points="541.8,535.7 531.9,552.1 519.4,552 529.1,535.6 "/>
<polygon class="st1" points="565.1,535.9 555.4,552.3 542.7,552.2 552.5,535.8 "/>
<polygon class="st1" points="577,536 576.9,552.5 566,552.4 575.8,536 "/>
<path class="st1" d="M577.1,527c0,0,0-0.1,0-0.3l0-0.9c0-0.7,0-1.8,0-3.2c0-2.8,0.1-6.9,0.1-12.2l0.1,0.1l-92.3-0.5l0,0l0.1-0.1
c0,5.6-0.1,11.2-0.1,16.5l-0.1-0.1l65.8,0.6l19.5,0.1l5.3,0l0,0l-5.3,0l-19.5-0.1l-65.8-0.3l-0.3,0l0.1-16.8l0.1,0l92.3,0.8
l0.1,0l0,0.1c0,5.3-0.1,9.4-0.1,12.2c0,1.5,0,2.5,0,3.2l0,0.7C577.1,526.8,577.1,527,577.1,527z"/>
<path class="st1" d="M576.9,552.5c0,0,0-0.1,0-0.3l0-0.9c0-0.7,0-1.8,0-3.2c0-2.8,0.1-6.9,0.1-12.2l0.1,0.1l-92.3-0.5l0,0
l0.1-0.1c0,5.6-0.1,11.2-0.1,16.5l-0.1-0.1l65.8,0.6l19.5,0.1l5.3,0l0,0l-5.3,0l-19.5-0.1l-65.8-0.3l-0.3,0l0.1-16.7l0.1,0
l92.3,0.8l0.1,0l0,0.1c0,5.3-0.1,9.4-0.1,12.2c0,1.5,0,2.5,0,3.2l0,0.7C576.9,552.3,576.9,552.5,576.9,552.5z"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.1 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,236 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 456 262.1" style="enable-background:new 0 0 456 262.1;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.4;fill:url(#SVGID_1_);enable-background:new ;}
.st1{opacity:0.7;}
.st2{opacity:0.4;fill:url(#SVGID_2_);enable-background:new ;}
.st3{opacity:0.4;fill:url(#SVGID_3_);enable-background:new ;}
.st4{opacity:0.4;fill:url(#SVGID_4_);enable-background:new ;}
.st5{opacity:0.4;fill:url(#SVGID_5_);enable-background:new ;}
.st6{opacity:0.6;}
.st7{fill:#0073CD;}
.st8{fill:#40A8F5;}
.st9{fill:#53B9F5;}
.st10{fill:#85D3FF;}
.st11{fill:#8CD7FF;}
.st12{fill:#EBFCFF;}
.st13{fill:none;stroke:url(#SVGID_6_);stroke-width:2;stroke-miterlimit:10;}
.st14{fill:none;stroke:url(#SVGID_7_);stroke-width:2;stroke-miterlimit:10;}
.st15{fill:none;stroke:url(#SVGID_8_);stroke-width:2;stroke-miterlimit:10;}
.st16{fill:none;stroke:url(#SVGID_9_);stroke-width:2;stroke-miterlimit:10;}
.st17{fill:none;stroke:url(#SVGID_10_);stroke-width:2;stroke-miterlimit:10;}
.st18{fill:none;stroke:url(#SVGID_11_);stroke-width:2;stroke-miterlimit:10;}
</style>
<title>升级中</title>
<g id="图层_2_1_">
<g id="图层_1-2">
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="232.745" y1="39.57" x2="232.745" y2="1.88" gradientTransform="matrix(1 0 0 -1 0 264)">
<stop offset="0" style="stop-color:#81CFFF"/>
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
</linearGradient>
<path class="st0" d="M412.3,262.1c-23-23-61-37.7-179.5-37.7S76.2,239.1,53.2,262.1H412.3z"/>
<g class="st1">
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="349.365" y1="237.3224" x2="349.365" y2="59.9676" gradientTransform="matrix(1 0 0 -1 0 264)">
<stop offset="0" style="stop-color:#81CFFF"/>
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
</linearGradient>
<path class="st2" d="M380.7,26.7h-62.6c-1.5-0.1-2.8,1.1-2.8,2.6v172.2c0.1,1.5,1.3,2.7,2.8,2.6h62.6c1.5,0.1,2.7-1.1,2.8-2.6
V29.3C383.4,27.8,382.2,26.6,380.7,26.7z M328.3,147c0,0.5-0.4,0.9-0.9,0.9c0,0,0,0,0,0h-3.6c-0.5,0-0.9-0.4-0.9-0.8c0,0,0,0,0,0
v-19.7c0-0.5,0.4-0.9,0.9-0.9c0,0,0,0,0,0h3.6c0.5,0,0.9,0.4,0.9,0.9c0,0,0,0,0,0V147z M328.3,116.8c0,0.5-0.4,0.9-0.9,0.9
c0,0,0,0,0,0h-3.6c-0.5,0-0.9-0.4-0.9-0.9c0,0,0,0,0,0V97c0-0.5,0.4-0.9,0.9-0.9c0,0,0,0,0,0h3.6c0.5,0,0.9,0.4,0.9,0.9
c0,0,0,0,0,0V116.8z M328.3,86.5c0,0.5-0.4,0.9-0.9,0.9c0,0,0,0,0,0h-3.6c-0.5,0-0.9-0.4-0.9-0.9c0,0,0,0,0,0V66.8
c0-0.5,0.4-0.9,0.9-0.9c0,0,0,0,0,0h3.6c0.5,0,0.9,0.4,0.9,0.9c0,0,0,0,0,0V86.5z M328.3,56.3c0,0.5-0.4,0.9-0.9,0.9c0,0,0,0,0,0
h-3.6c-0.5,0-0.9-0.4-0.9-0.9c0,0,0,0,0,0V36.6c0-0.5,0.4-0.9,0.9-0.9c0,0,0,0,0,0h3.6c0.5,0,0.9,0.4,0.9,0.9c0,0,0,0,0,0V56.3z
M340,147c0,0.5-0.4,0.9-0.9,0.9c0,0,0,0,0,0h-3.6c-0.5,0-0.9-0.4-0.9-0.9c0,0,0,0,0,0v-19.7c0-0.5,0.4-0.9,1-0.9h3.6
c0.5,0,0.9,0.4,0.9,0.9V147z M340,116.8c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-1-0.9V97c0-0.5,0.4-0.9,1-0.9h3.6
c0.5,0,0.9,0.4,0.9,0.9V116.8z M340,86.5c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-1-0.9V66.8c0-0.5,0.4-0.9,1-0.9h3.6
c0.5,0,0.9,0.4,0.9,0.9V86.5z M340,56.3c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-1-0.9V36.6c0-0.5,0.4-0.9,1-0.9h3.6
c0.5,0,0.9,0.4,0.9,0.9V56.3z M351.7,147c0,0.5-0.4,0.9-0.9,0.9c0,0,0,0,0,0h-3.6c-0.5,0-0.9-0.4-0.9-0.9c0,0,0,0,0,0v-19.7
c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V147z M351.7,116.8c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V97
c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V116.8z M351.7,86.5c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V66.8
c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V86.5z M351.7,56.3c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V36.6
c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V56.3z M363.4,147c0,0.5-0.4,0.9-0.9,0.9c0,0,0,0,0,0h-3.6
c-0.5,0-0.9-0.4-0.9-0.9c0,0,0,0,0,0v-19.7c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V147z M363.4,116.8
c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V97c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V116.8z M363.4,86.5
c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V66.8c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V86.5z M363.4,56.3
c0,0.5-0.4,0.9-0.9,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V36.6c0-0.5,0.4-0.9,0.9-0.9h3.6c0.5,0,0.9,0.4,0.9,0.9V56.3z M375.1,147
c0,0.5-0.4,0.9-0.9,0.9c0,0,0,0,0,0h-3.6c-0.5,0-0.9-0.4-0.9-0.9c0,0,0,0,0,0v-19.7c0-0.5,0.4-0.9,0.9-0.9h3.6
c0.5,0,0.9,0.4,1,0.9V147z M375.1,116.8c0,0.5-0.4,0.9-1,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V97c0-0.5,0.4-0.9,0.9-0.9h3.6
c0.5,0,0.9,0.4,1,0.9V116.8z M375.1,86.5c0,0.5-0.4,0.9-1,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V66.8c0-0.5,0.4-0.9,0.9-0.9h3.6
c0.5,0,0.9,0.4,1,0.9V86.5z M375.1,56.3c0,0.5-0.4,0.9-1,0.9h-3.6c-0.5,0-0.9-0.4-0.9-0.9V36.6c0-0.5,0.4-0.9,0.9-0.9h3.6
c0.5,0,0.9,0.4,1,0.9V56.3z"/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="201.46" y1="208.3924" x2="201.46" y2="59.9976" gradientTransform="matrix(1 0 0 -1 0 264)">
<stop offset="0" style="stop-color:#81CFFF"/>
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
</linearGradient>
<path class="st3" d="M231.1,55.6h-59.3c-1.5-0.1-2.7,1.1-2.8,2.6v143.2c0.1,1.5,1.3,2.6,2.8,2.6h59.3c1.5,0.1,2.8-1.1,2.8-2.6
V58.2C233.9,56.7,232.6,55.5,231.1,55.6z M182.5,159.4c0,0.6-0.6,1.1-1.2,1.1h-5.4c-0.6,0-1.2-0.5-1.2-1.1v-5.2
c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V159.4z M182.5,146.5c0,0.6-0.6,1.1-1.2,1.1
c0,0,0,0,0,0h-5.4c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1V146.5z
M182.5,133.6c0,0.6-0.6,1.1-1.2,1.1h-5.4c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4
c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V133.6z M182.5,120.7c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0h-5.4c-0.6,0-1.2-0.5-1.2-1.1
c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1V120.7z M182.5,107.8c0,0.6-0.6,1.1-1.2,1.1h-5.4
c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V107.8z M182.5,94.9
c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0h-5.4c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4
c0.6,0,1.2,0.5,1.2,1.1V94.9z M182.5,82.1c0,0.6-0.6,1.1-1.2,1.1h-5.4c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1
c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V82.1z M182.5,69.2c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0h-5.4
c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0V64c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1V69.2z M192.6,159.4
c0,0.6-0.6,1.1-1.2,1.1H186c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1
c0,0,0,0,0,0L192.6,159.4z M192.6,146.5c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H186c-0.6,0-1.2-0.5-1.2-1.1v-5.2
c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1L192.6,146.5z M192.6,133.6c0,0.6-0.6,1.1-1.2,1.1H186
c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L192.6,133.6z
M192.6,120.7c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H186c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4
c0.6,0,1.2,0.5,1.2,1.1L192.6,120.7z M192.6,107.8c0,0.6-0.6,1.1-1.2,1.1H186c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2
c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L192.6,107.8z M192.6,94.9c0,0.6-0.6,1.1-1.2,1.1
c0,0,0,0,0,0H186c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1L192.6,94.9z
M192.6,82.1c0,0.6-0.6,1.1-1.2,1.1H186c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4
c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L192.6,82.1z M192.6,69.2c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H186c-0.6,0-1.2-0.5-1.2-1.1V64
c0-0.6,0.5-1.2,1.2-1.2c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1L192.6,69.2z M202.6,159.4c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H196
c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1L202.6,159.4z M202.6,146.5
c0,0.6-0.6,1.1-1.2,1.1H196c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1
c0,0,0,0,0,0L202.6,146.5z M202.6,133.6c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H196c-0.6,0-1.2-0.5-1.2-1.1v-5.2
c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1L202.6,133.6z M202.6,120.7c0,0.6-0.6,1.1-1.2,1.1H196
c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L202.6,120.7z
M202.6,107.8c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H196c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4
c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L202.6,107.8z M202.6,94.9c0,0.6-0.6,1.1-1.2,1.1H196c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0
v-5.2c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L202.6,94.9z M202.6,82.1c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0
H196c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h5.4c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L202.6,82.1z
M202.6,69.2c0,0.6-0.6,1.1-1.2,1.1H196c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0V64c0-0.6,0.6-1.1,1.2-1.1h5.4c0.6,0,1.2,0.4,1.2,1.1
c0,0,0,0,0,0V69.2z M227.8,159.4c0,0.6-0.6,1.1-1.2,1.1h-20.5c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0
h20.5c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L227.8,159.4z M227.8,146.5c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0h-20.5
c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h20.5c0.6,0,1.2,0.5,1.2,1.1L227.8,146.5z M227.8,133.6
c0,0.6-0.6,1.1-1.2,1.1h-20.5c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h20.5c0.6,0,1.2,0.5,1.2,1.1
c0,0,0,0,0,0L227.8,133.6z M227.8,120.7c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0h-20.5c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2
c0-0.6,0.6-1.1,1.2-1.1h20.5c0.6,0,1.2,0.5,1.2,1.1L227.8,120.7z M227.8,107.8c0,0.6-0.6,1.1-1.2,1.1h-20.5
c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h20.5c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L227.8,107.8z
M227.8,94.9c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0h-20.5c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1h20.5
c0.6,0,1.2,0.5,1.2,1.1L227.8,94.9z M227.8,82.1c0,0.6-0.6,1.1-1.2,1.1h-20.5c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1
c0,0,0,0,0,0h20.5c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0L227.8,82.1z M227.8,69.2c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0h-20.5
c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0V64c0-0.6,0.6-1.1,1.2-1.1h20.5c0.6,0,1.2,0.5,1.2,1.1L227.8,69.2z"/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="122.975" y1="237.3228" x2="122.975" y2="59.9971" gradientTransform="matrix(1 0 0 -1 0 264)">
<stop offset="0" style="stop-color:#81CFFF"/>
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
</linearGradient>
<path class="st4" d="M161.1,26.7H84.8c-1.5-0.1-2.8,1.1-2.8,2.6c0,0,0,0,0,0v172.2c0.1,1.5,1.3,2.6,2.8,2.6h76.3
c1.5,0.1,2.8-1.1,2.8-2.6V29.3C163.9,27.8,162.6,26.6,161.1,26.7z M154.3,161c0,0.6-0.6,1.1-1.2,1.1H92.8c-0.6,0-1.2-0.5-1.2-1.1
c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h60.3c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V161z M154.3,146.3
c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H92.8c-0.6,0-1.2-0.5-1.2-1.1v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h60.3
c0.6,0,1.2,0.5,1.2,1.1V146.3z M154.3,131.6c0,0.6-0.6,1.1-1.2,1.1H92.8c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2
c0-0.6,0.6-1.1,1.2-1.1h60.3c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V131.6z M154.3,117c0,0.6-0.6,1.1-1.2,1.1H92.8
c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.2c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h60.3c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V117z
M154.3,102.3c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H92.8c-0.6,0-1.2-0.5-1.2-1.1V97c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h60.3
c0.6,0,1.2,0.5,1.2,1.1V102.3z M154.3,87.6c0,0.6-0.6,1.1-1.2,1.1H92.8c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.3
c0-0.6,0.6-1.1,1.2-1.1h60.3c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V87.6z M154.3,72.9c0,0.6-0.6,1.1-1.2,1.1H92.8
c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.3c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h60.3c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V72.9z
M154.3,58.3c0,0.6-0.6,1.1-1.2,1.1c0,0,0,0,0,0H92.8c-0.6,0-1.2-0.5-1.2-1.1V53c0-0.6,0.6-1.1,1.2-1.1c0,0,0,0,0,0h60.3
c0.6,0,1.2,0.5,1.2,1.1V58.3z M154.3,43.6c0,0.6-0.6,1.1-1.2,1.1H92.8c-0.6,0-1.2-0.5-1.2-1.1c0,0,0,0,0,0v-5.3
c0-0.6,0.6-1.1,1.2-1.1h60.3c0.6,0,1.2,0.5,1.2,1.1c0,0,0,0,0,0V43.6z"/>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="273.51" y1="264" x2="273.51" y2="54.46" gradientTransform="matrix(1 0 0 -1 0 264)">
<stop offset="0" style="stop-color:#81CFFF"/>
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
</linearGradient>
<path class="st5" d="M306.2,0h-65.4c-1.4,0-2.5,1.2-2.4,2.6v204.4c-0.1,1.4,1,2.5,2.4,2.6c0,0,0,0,0,0h65.4
c1.4-0.1,2.5-1.2,2.4-2.6V2.6C308.7,1.2,307.6,0.1,306.2,0z M300.4,119.6c0,0.6-0.4,1.1-1,1.1h-51.7c-0.6,0-1-0.5-1-1.1v-5.2
c0-0.6,0.4-1.1,1-1.1h51.7c0.6,0,1,0.5,1,1.1L300.4,119.6z M300.4,90.3c0,0.6-0.4,1.1-1,1.1h-51.7c-0.6,0-1-0.5-1-1.1V85
c0-0.6,0.4-1.1,1-1.1h51.7c0.6,0,1,0.5,1,1.1L300.4,90.3z M300.4,60.9c0,0.6-0.4,1.1-1,1.1h-51.7c-0.6,0-1-0.5-1-1.1v-5.2
c0-0.6,0.4-1.1,1-1.1h51.7c0.6,0,1,0.5,1,1.1L300.4,60.9z M300.4,31.5c0,0.6-0.4,1.1-1,1.1h-51.7c-0.6,0-1-0.5-1-1.1v-5.2
c0-0.6,0.4-1.1,1-1.1h51.7c0.6,0,1,0.5,1,1.1L300.4,31.5z M300.4,16.9c0,0.6-0.4,1.1-1,1.1h-51.7c-0.6,0-1-0.5-1-1.1v-5.2
c0-0.6,0.4-1.1,1-1.1h51.7c0.6,0,1,0.5,1,1.1L300.4,16.9z"/>
</g>
<g class="st6">
<path class="st7" d="M244.2,130.7c3.3,1.9,5.7,13.6,1,20.7c-5.3,6.8-7,9-6.1,14c-4.3-7.3-0.6-7.4-5.8-14.2
c-4.6-6.2-2.5-17.4,0.7-20.5C237.4,131.3,240.8,131.3,244.2,130.7L244.2,130.7z"/>
<path class="st8" d="M280.4,211.9c-0.1,0.6-0.1,1.2-0.1,1.7c0,0.7,0.1,1.3,0.2,2c-2.1-0.8-4.3-1.2-6.5-1.2
c-2.6,0-5.2,0.6-7.5,1.7c-0.8-4.2-4.8-7-9-6.2c-2.8,0.5-5,2.5-5.9,5.2c-3.4-2.1-7.7-2-11.1,0.1c-0.4-5.7-4.1-0.8-9.8-0.8
c-6,0-11.8-4.4-11.8,1.7c0,1.1,0.2,2.2,0.4,3.2c-1.1,0.1-2.1,0.5-3,1c-0.9-7-6.8-12.4-13.9-12.7c22.6-10.3,22.4-44.6,24.8-74.6
c0.1-1.7,1.2-4.1,1.8-4.4l0,0c1.5,1.1,3.2,1.8,5.1,2.1c-3.2,3.1-5.4,14.3-0.7,20.5c5.3,6.8,1.6,6.9,5.8,14.2
c-0.9-5,0.8-7.2,6.1-14c4.8-7.2,2.3-18.8-1-20.7c1.8-0.3,3.6-1,5.1-2.1l0,0c0.6,0.3,1.7,2.7,1.8,4.4
C253.6,164.6,250.4,198.5,280.4,211.9z"/>
<path class="st9" d="M303.3,203.2c-5.4,0-9.8,4.4-9.8,9.8c0,0.3,0,0.6,0,0.9c-1.5-1.7-3.7-2.6-6-2.5c-0.7,0-1.4,0.1-2.1,0.3
c0.7-1.3,1.1-2.8,1.1-4.3c-0.1-4.6-3.9-8.3-8.5-8.2c-4,0.1-7.4,3-8.1,6.9c-0.1,0.4-0.1,0.9-0.1,1.3c0,0.5,0,1,0.1,1.6
c-3.5-1.4-7.4-1.3-10.8,0.4c-0.7-3.2-3.9-5.3-7.2-4.6c-2,0.4-3.6,1.9-4.3,3.8c-1.3-0.8-2.7-1.2-4.2-1.2c-1.5,0-3.1,0.4-4.4,1.3
c-0.2-4.6-4.1-8.2-8.7-8s-8.2,4.1-8,8.7l0,0c0,0.8,0.1,1.7,0.3,2.5c-0.8,0.1-1.6,0.4-2.3,0.8c-0.7-5.4-5.2-9.6-10.7-9.8h-0.4
c-3.4,0-6.7,1.6-8.8,4.3c-3-6.2-10.4-8.9-16.7-5.9c-2.9,1.4-5.2,3.9-6.3,7c4.1,3,6.6,7.8,6.6,12.9c0,2.3-0.5,4.6-1.5,6.6
c2-2.2,4.8-3.4,7.8-3.4c0.9,0,1.8,0.1,2.7,0.3c-2.9-5-1.2-11.5,3.8-14.4c1.2-0.7,2.6-1.2,3.9-1.3c0.5-0.1,0.9-0.1,1.4-0.1
c5.8,0,10.6,4.7,10.6,10.6l0,0c0,0.6-0.1,1.3-0.2,1.9c4.4-1.7,9.3-1.6,13.6,0.4c0.9-4.1,4.9-6.7,9-5.8c2.6,0.6,4.7,2.4,5.5,4.9
c1.6-0.9,3.5-1.4,5.3-1.4c1.9,0,3.8,0.5,5.4,1.5l0.1,0.1c0.3-4.5,3.6-8.4,8-9.4c0.8-0.2,1.7-0.3,2.5-0.3
c5.8,0,10.6,4.7,10.6,10.6c0,1-0.2,2-0.4,3c1.1,0.1,2.1,0.5,3,1c1-7,7-12.1,14-12.1c1.5,0,3,0.2,4.4,0.7c2.6,0.8,5,2.5,6.7,4.6
c2.4-4.8,7-8.2,12.4-8.8C311.4,205.9,307.6,203.2,303.3,203.2z"/>
<path class="st10" d="M314.4,209.9c-0.6,0-1.2,0-1.8,0.1c-5.4,0.6-10.1,3.9-12.5,8.7c-1.7-2.2-4-3.8-6.7-4.6
c-1.4-0.5-2.9-0.7-4.4-0.7c-7,0-13,5.2-14,12.1c-0.9-0.5-1.9-0.9-3-1c0.3-1,0.4-2,0.4-3c0-5.8-4.7-10.5-10.5-10.5
c-0.8,0-1.7,0.1-2.5,0.3c-4.4,1.1-7.7,4.9-8,9.4l-0.1-0.1c-1.6-1-3.5-1.5-5.4-1.5c-1.9,0-3.7,0.5-5.3,1.4c-1.4-3.9-5.7-6-9.6-4.6
c-2.5,0.9-4.3,3-4.9,5.5c-4.3-2-9.2-2.1-13.6-0.4c0.1-0.6,0.2-1.3,0.2-1.9c0-5.8-4.7-10.6-10.6-10.6l0,0c-0.5,0-0.9,0-1.4,0.1
c-5.8,0.8-9.9,6.1-9.1,11.8c0.2,1.4,0.6,2.7,1.3,3.9c-0.9-0.2-1.8-0.3-2.7-0.3c-2.9,0-5.8,1.2-7.8,3.4c1-2.1,1.5-4.3,1.5-6.6
c0-5.1-2.4-9.9-6.6-12.9l0,0c-7.1-5.2-17-3.6-22.2,3.5c-5.2,7.1-3.6,17,3.5,22.2c6.8,5,16.4,3.7,21.7-2.8
c-2.1,5.5,0.7,11.6,6.1,13.6c5.5,2.1,11.6-0.7,13.6-6.1c0.5-1.2,0.7-2.5,0.7-3.7c0-1.9,0.4-0.1,0.4,2.7c0,9.7,7.8,17.6,17.5,17.6
c9.7,0,17.6-7.8,17.6-17.5c0-2.4-0.5-4.8-1.4-7l0.5-0.1c0.5,5.8,5.7,10.1,11.5,9.5c5-0.5,9-4.4,9.5-9.4c1.7,1,3.6,1.6,5.5,1.6
c0.6,0,1.2,0,1.7-0.1v0.1c0,4.2,3.4,7.6,7.6,7.5c2.6,0,5-1.4,6.4-3.6c4.7,6.3,13.5,7.5,19.8,2.9c1.5-1.1,2.8-2.5,3.7-4.2
c4.9,7.3,14.8,9.1,22.1,4.2s9.1-14.8,4.2-22.1C324.5,212.5,319.6,209.9,314.4,209.9L314.4,209.9z"/>
</g>
<path class="st11" d="M239.1,70c6.4,0,11.6,5.2,11.6,11.6c0,6.4-5.2,11.6-11.6,11.6c-6.4,0-11.6-5.2-11.6-11.6c0,0,0,0,0,0
C227.5,75.2,232.7,70,239.1,70z"/>
<path class="st8" d="M239.1,137.2c8.3,0,13.5-1.7,16.6-7.9c0,3.2-1.5,6.2-4.1,8.1c-1.9,1.3-4,2.2-6.2,2.6c-4.1,0.8-8.4,0.8-12.5,0
c-2.2-0.4-4.3-1.3-6.2-2.6c-2.6-1.9-4.2-4.9-4.2-8.1C225.6,135.5,230.7,137.2,239.1,137.2z"/>
<path class="st8" d="M288.4,151.7c0,0.5-0.3,0.8-0.8,0.8c-0.3,0-0.5-0.1-0.7-0.3c-7.6-10.3-25.5-25-30.4-24.7
c2.4-6.1,3.4-15.9,3.6-31.2C271.5,100.3,288.3,122.4,288.4,151.7z"/>
<path class="st8" d="M218.1,96.3c0.3,15.2,1.2,25,3.6,31.1l-0.2,0.1c-4.9-0.3-22.8,14.5-30.4,24.7c-0.3,0.4-0.8,0.5-1.1,0.2
c-0.2-0.2-0.4-0.4-0.3-0.7C189.7,122.3,206.6,100.2,218.1,96.3L218.1,96.3z"/>
<path class="st8" d="M250.7,81.6c0-6.4-5.2-11.6-11.6-11.6c-6.4,0-11.6,5.2-11.6,11.6s5.2,11.6,11.6,11.6l0,0
C245.5,93.2,250.7,88,250.7,81.6z M252.9,81.6c0,7.6-6.2,13.8-13.8,13.8c-7.6,0-13.8-6.2-13.8-13.8c0-7.6,6.2-13.8,13.8-13.8
c0,0,0,0,0,0C246.7,67.8,252.9,74,252.9,81.6z"/>
<path class="st8" d="M239.1,48.7c5.7,0,10.7-2,13.8-4.9c0.5,1.3,1,2.6,1.4,4c-3.3,3.3-8.9,5.4-15.3,5.4s-11.9-2.1-15.3-5.4
c0.5-1.3,1-2.7,1.4-4C228.4,46.8,233.4,48.7,239.1,48.7z"/>
<path class="st9" d="M252.9,43.8c-3.1,3-8.1,4.9-13.8,4.9s-10.7-2-13.8-4.9c2-5.1,4.5-10,7.6-14.5c0.5-0.7,0.9-1.3,1.4-1.9
c2-2.7,5.8-3.2,8.5-1.2c0.4,0.3,0.8,0.7,1.2,1.2c0.5,0.6,0.9,1.2,1.4,1.9C248.4,33.8,250.9,38.7,252.9,43.8z"/>
<path class="st10" d="M260.1,96.4c-0.3,15.3-1.2,25-3.6,31.2c-0.2,0.6-0.5,1.2-0.8,1.7c-3.1,6.2-8.3,7.9-16.6,7.9
s-13.5-1.7-16.6-7.9c-0.3-0.6-0.5-1.1-0.8-1.8c-2.4-6.2-3.4-15.9-3.6-31.1c-0.1-3.4-0.1-7.1-0.1-11.1c-0.1-12.7,1.8-25.4,5.8-37.5
c3.3,3.3,8.9,5.4,15.3,5.4s11.9-2.1,15.3-5.4c4,12.1,6,24.8,5.8,37.5C260.2,89.2,260.1,92.9,260.1,96.4z M252.9,81.6
c0-7.6-6.2-13.8-13.8-13.8c-7.6,0-13.8,6.2-13.8,13.8c0,7.6,6.2,13.8,13.8,13.8c0,0,0,0,0,0C246.7,95.4,252.9,89.2,252.9,81.6
L252.9,81.6z"/>
<path class="st12" d="M139.2,246.1l18.4,0.4v0.7l-19.4-0.4v-0.7V246C138.4,246.1,138.6,246.1,139.2,246.1z"/>
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="112.2357" y1="190.775" x2="112.2357" y2="101.005" gradientTransform="matrix(1 0 0 -1 0 264)">
<stop offset="0" style="stop-color:#81CFFF"/>
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
</linearGradient>
<line class="st13" x1="112.2" y1="73.2" x2="112.2" y2="163"/>
<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="348.955" y1="195.605" x2="348.955" y2="105.835" gradientTransform="matrix(1 0 0 -1 0 264)">
<stop offset="0" style="stop-color:#81CFFF"/>
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
</linearGradient>
<line class="st14" x1="349" y1="68.4" x2="349" y2="158.2"/>
<linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="40.9" y1="120.12" x2="40.9" y2="64.49" gradientTransform="matrix(1 0 0 -1 0 264)">
<stop offset="0" style="stop-color:#81CFFF"/>
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
</linearGradient>
<line class="st15" x1="40.9" y1="143.9" x2="40.9" y2="199.5"/>
<linearGradient id="SVGID_9_" gradientUnits="userSpaceOnUse" x1="64.97" y1="168.64" x2="64.97" y2="140.83" gradientTransform="matrix(1 0 0 -1 0 264)">
<stop offset="0" style="stop-color:#81CFFF"/>
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
</linearGradient>
<line class="st16" x1="65" y1="95.4" x2="65" y2="123.2"/>
<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="397.23" y1="159.8" x2="397.23" y2="131.98" gradientTransform="matrix(1 0 0 -1 0 264)">
<stop offset="0" style="stop-color:#81CFFF"/>
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
</linearGradient>
<line class="st17" x1="397.2" y1="104.2" x2="397.2" y2="132"/>
<linearGradient id="SVGID_11_" gradientUnits="userSpaceOnUse" x1="424.75" y1="130.51" x2="424.75" y2="74.87" gradientTransform="matrix(1 0 0 -1 0 264)">
<stop offset="0" style="stop-color:#81CFFF"/>
<stop offset="1" style="stop-color:#5ECFFF;stop-opacity:0"/>
</linearGradient>
<line class="st18" x1="424.8" y1="133.5" x2="424.8" y2="189.1"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 20 KiB

View File

@ -6,8 +6,9 @@
<script setup name="App">
import i18n from '@/locales'
import store from '@/store'
import { globalStore } from '@/store'
store.commit('initTheme')
const store = globalStore()
store.initTheme()
const locale = i18n.global.messages[i18n.global.locale].lang
</script>

View File

@ -8,9 +8,9 @@
* 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/
import { baseRequest } from '@/utils/request'
import { moduleRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/auth/b/${url}`, ...arg)
const request = moduleRequest(`/auth/b/`)
/**
* 登录
*

View File

@ -15,6 +15,8 @@
import 'tinymce/plugins/link' //
import 'tinymce/plugins/preview' //
import 'tinymce/plugins/table' //
import 'tinymce/plugins/lists' //
import 'tinymce/plugins/advlist' //
export default {
components: {
@ -39,12 +41,12 @@
},
plugins: {
type: [String, Array],
default: 'code image link preview table'
default: 'code image link preview table lists advlist'
},
toolbar: {
type: [String, Array],
default:
'undo redo | forecolor backcolor bold italic underline strikethrough link | formatselect fontselect fontsizeselect | \
'undo redo | forecolor backcolor bold italic underline strikethrough link | blocks fontfamily fontsize | \
alignleft aligncenter alignright alignjustify outdent indent lineheight | bullist numlist | \
image table preview | code selectall'
}

View File

@ -6,6 +6,7 @@
import './index.less'
export default {
name: 'STable',
components: {
draggable,
columnSetting
@ -42,8 +43,8 @@
default: 1
},
size: {
type: String,
default: '10'
type: Number,
default: 10
},
showSizeChanger: {
type: Boolean,
@ -143,7 +144,7 @@
(['auto', true].includes(this.showPagination) &&
Object.assign({}, this.localPagination, {
current: localPageNum,
size: this.size, //this.compSize, size//
pageSize: this.size, //this.compSize, size//
showSizeChanger: this.showSizeChanger,
defaultPageSize: this.defaultPageSize,
pageSizeOptions: this.pageSizeOptions,
@ -168,7 +169,7 @@
{},
{
current: 1,
size: this.size
pageSize: this.localPagination.pageSize
}
))
this.loadData()
@ -187,11 +188,12 @@
(pagination && pagination.current) ||
(this.showPagination && this.localPagination.current) ||
this.pageNum,
// 使size
size:
(pagination && pagination.pageSize) ||
(this.showPagination && this.localPagination.pageSize) ||
this.pageSize ||
this.localPagination.size
this.localPagination.pageSize
},
(sorter &&
sorter.field && {
@ -227,7 +229,7 @@
showTotal: (total, range) => {
return `${range[0]}-${range[1]}${total}`
},
size: (pagination && pagination.size) || this.localPagination.size
pageSize: (pagination && pagination.pageSize) || this.localPagination.pageSize
})) ||
false
// recordsnull

View File

@ -0,0 +1,67 @@
<template>
<a-popconfirm
title="批量处理此信息?"
:visible="batchVisible"
@visibleChange="batchVisibleChange"
@confirm="deleteBatch"
>
<a-button :type="props.buttonType" :danger="props.buttonDanger">
<template #icon v-if="props.icon">
<component :is="props.icon" />
</template>
{{ props.buttonName }}
</a-button>
</a-popconfirm>
</template>
<script setup name="commonBatchButton">
import { message } from 'ant-design-vue'
const batchVisible = ref(false)
const emit = defineEmits({ batchCallBack: null })
const props = defineProps({
buttonName: {
type: String,
default: () => '批量操作'
},
buttonDanger: {
type: Boolean,
default: () => false
},
buttonType: {
type: String,
default: () => ''
},
icon: {
type: String,
default: () => ''
},
selectedRowKeys: {
type: Array,
default: () => []
}
})
//
const batchVisibleChange = () => {
if (batchVisible.value) {
batchVisible.value = false
return false
}
if (props.selectedRowKeys.length < 1) {
message.warning('请选择一条或多条数据')
batchVisible.value = false
return false
} else {
batchVisible.value = true
}
}
//
const deleteBatch = () => {
const params = props.selectedRowKeys.map((m) => {
return {
id: m
}
})
//
emit('batchCallBack', params)
}
</script>

View File

@ -1,20 +1,19 @@
<template>
<Modal v-if="isModal" :visible="visible" @cancel="cancel" v-bind="$attrs">
<a-modal v-if="isModal" :visible="visible" @cancel="cancel" v-bind="$attrs">
<template v-for="slotKey in slotKeys" #[slotKey]>
<slot :name="slotKey" />
</template>
</Modal>
<Drawer v-else :visible="visible" v-bind="$attrs" :footer-style="{ textAlign: 'right' }">
</a-modal>
<a-drawer v-else :visible="visible" v-bind="$attrs" :footer-style="{ textAlign: 'right' }">
<template v-for="slotKey in slotKeys" #[slotKey]>
<slot :name="slotKey" />
</template>
</Drawer>
</a-drawer>
</template>
<script>
import { Drawer, Modal } from 'ant-design-vue'
import { modalProps } from 'ant-design-vue/es/modal/Modal'
import { drawerProps } from 'ant-design-vue/es/drawer'
import { mapState } from 'pinia'
import { globalStore } from '@/store'
const FormContainerTypeEnum = {
DRAWER: 'drawer',
@ -22,26 +21,21 @@
}
export default {
name: 'XnFormContainer',
components: {
Drawer,
Modal
},
inheritAttrs: false,
props: {
visible: {
type: Boolean,
default: false,
required: true
},
...modalProps,
...drawerProps
}
},
computed: {
...mapState(globalStore, ['formStyle']),
slotKeys() {
return Object.keys(this.$slots)
},
isModal() {
return FormContainerTypeEnum.MODAL === this.$store.state.global.formStyle
return FormContainerTypeEnum.MODAL === this.formStyle
}
},
methods: {

View File

@ -54,7 +54,7 @@
}*/
/** 滚动条 */
/*:deep(.hljs,.hljs-container) {
:deep(.hljs,.hljs-container) {
max-height: 300px!important;
overflow-x: auto;
}
@ -87,5 +87,5 @@
::-webkit-scrollbar-button {
display: none;
}*/
}
</style>

View File

@ -1,5 +1,12 @@
<template>
<xn-form-container ref="signModel" v-model:visible="visible" :width="700" title="电子签名" @close="handleClear" @ok="handleOk">
<xn-form-container
ref="signModel"
v-model:visible="visible"
:width="700"
title="电子签名"
@close="handleClear"
@ok="handleOk"
>
<a-row :gutter="5">
<a-col :span="15">
<div style="border: 1px solid rgb(236 236 236)">

View File

@ -27,19 +27,6 @@ body {
margin: 24px 0 0;
}
}
/**
* ant-table-wrapper
* 覆盖的表格手机模式样式,如果想修改在手机上表格最低宽度,可以在这里改动
*/
.ant-table-wrapper {
.ant-table-content {
overflow-y: auto;
}
.ant-table-body {
min-width: 800px;
}
}
.topmenu {
/* 必须为 topmenu 才能启用流式布局 */
&.content-width-Fluid {

View File

@ -16,7 +16,7 @@ const DEFAULT_CONFIG = {
API_URL: import.meta.env.VITE_API_BASEURL,
// 请求超时
TIMEOUT: 10000,
TIMEOUT: 60000,
// TokenName // Authorization
TOKEN_NAME: 'token',

View File

@ -1,18 +1,8 @@
/* eslint-disable eqeqeq */
<!--
* @Descripttion: 处理iframe持久化涉及store(VUEX)
* @version: 1.0
* @Author: sakuya
* @Date: 2021年6月30日13:20:41
* @LastEditors:
* @LastEditTime:
-->
<template>
<div v-show="$route.meta.type == 'iframe'" class="iframe-pages">
<div v-show="$route.meta.type === 'iframe'" class="iframe-pages">
<iframe
v-for="item in iframeList"
v-show="$route.meta.url == item.meta.url"
v-show="$route.meta.url === item.meta.url"
:key="item.meta.url"
:src="item.meta.url"
frameborder="0"
@ -21,20 +11,16 @@
</template>
<script>
import { mapState, mapActions } from 'pinia'
import { iframeStore, globalStore } from '@/store'
export default {
data() {
return {}
},
computed: {
iframeList() {
return this.$store.state.iframe.iframeList
},
ismobile() {
return this.$store.state.global.ismobile
},
layoutTags() {
return this.$store.state.global.layoutTags
}
...mapState(iframeStore, ['iframeList']),
...mapState(globalStore, ['ismobile', 'layoutTags'])
},
watch: {
$route(e) {
@ -46,16 +32,16 @@
},
mounted() {},
methods: {
...mapActions(iframeStore, ['setIframeList', 'pushIframeList', 'clearIframeList']),
push(route) {
// eslint-disable-next-line eqeqeq
if (route.meta.type == 'iframe') {
if (route.meta.type === 'iframe') {
if (this.ismobile || !this.layoutTags) {
this.$store.commit('setIframeList', route)
this.setIframeList(route)
} else {
this.$store.commit('pushIframeList', route)
this.pushIframeList(route)
}
} else if (this.ismobile || !this.layoutTags) {
this.$store.commit('clearIframeList')
this.clearIframeList()
}
}
}

View File

@ -1,5 +1,6 @@
import { mapState, mapMutations } from 'vuex'
import { mapState, mapActions } from 'pinia'
import hotkeys from 'hotkeys-js'
import { searchStore } from '@/store'
export default {
mounted() {
@ -19,19 +20,16 @@ export default {
hotkeys.unbind(this.searchHotkey.close)
},
computed: {
...mapState('search', {
...mapState(searchStore, {
searchActive: (state) => state.active,
searchHotkey: (state) => state.hotkey
})
},
methods: {
...mapMutations({
searchToggle: 'search/toggle',
searchSet: 'search/set'
}),
...mapActions(searchStore, ['toggleActive', 'setActive']),
// 接收点击搜索按钮
handleSearchClick() {
this.searchToggle()
this.toggleActive()
if (this.searchActive) {
setTimeout(() => {
if (this.$refs.panelSearch) {
@ -42,7 +40,7 @@ export default {
},
searchPanelOpen() {
if (!this.searchActive) {
this.searchSet(true)
this.setActive(true)
setTimeout(() => {
if (this.$refs.panelSearch) {
this.$refs.panelSearch.focus()
@ -53,7 +51,7 @@ export default {
// 关闭搜索面板
searchPanelClose() {
if (this.searchActive) {
this.searchSet(false)
this.setActive(false)
}
}
}

View File

@ -1,15 +1,27 @@
<template>
<div v-if="moduleUnfoldOpen">
<a-menu v-model:selectedKeys="selectedKeys" mode="horizontal" v-if="menu && menu.length > 1" class="module-menu" id="moduleMunu">
<a-menu-item v-for="item in menu" :key="item.id" style="padding-right: 5px;position: relative;" @click="moduleClick(item.id)">
<div class="layout-items-center" v-if="moduleUnfoldOpen">
<a-menu
v-model:selectedKeys="selectedKeys"
mode="horizontal"
v-if="menu && menu.length > 1"
class="module-menu"
id="moduleMunu"
>
<a-menu-item
v-for="item in menu"
:key="item.id"
class="!px-3"
style="position: relative"
@click="moduleClick(item.id)"
>
<template #icon>
<component :is="item.meta.icon"/>
<component :is="item.meta.icon" />
</template>
<span style="margin-left:-5px">{{ item.meta.title }}</span>
<span style="margin-left: -5px">{{ item.meta.title }}</span>
</a-menu-item>
</a-menu>
</div>
<div v-else>
<div v-else class="panel-item hidden-sm-and-down">
<a-popover v-if="menu.length > 1" placement="bottomLeft">
<template #content>
<a-row :gutter="[0, 5]" class="module-row">
@ -23,9 +35,7 @@
</div>
</a-row>
</template>
<div class="module-comp">
<appstore-outlined />
</div>
<appstore-outlined />
</a-popover>
</div>
</template>
@ -33,18 +43,23 @@
<script setup>
import router from '@/router'
import tool from '@/utils/tool'
import store from '@/store'
import { globalStore } from '@/store'
import { watch } from 'vue'
import { storeToRefs } from 'pinia'
const store = globalStore()
const { moduleUnfoldOpen, topHanderThemeColorOpen } = storeToRefs(store)
const moduleBackColor = ref(topHanderThemeColorOpen)
//
watch(() => store.state.global.moduleUnfoldOpen, (newValue) => {
moduleUnfoldOpen.value = newValue
watch(moduleUnfoldOpen, (newValue) => {
nextTick(() => {
setModuleBackColor()
})
})
//
watch(() => store.state.global.topHanderThemeColorOpen, (newValue) => {
watch(topHanderThemeColorOpen, (newValue) => {
moduleBackColor.value = newValue
setModuleBackColor()
})
@ -60,9 +75,6 @@
})
}
const moduleUnfoldOpen = ref(store.state.global.moduleUnfoldOpen)
const moduleBackColor = ref(store.state.global.topHanderThemeColorOpen)
onMounted(() => {
setModuleBackColor()
})
@ -71,17 +83,19 @@
if (moduleUnfoldOpen.value) {
try {
const moduleMunu = document.getElementById('moduleMunu')
moduleBackColor.value? moduleMunu.classList.add('module-menu-color')
moduleBackColor.value
? moduleMunu.classList.add('module-menu-color')
: moduleMunu.classList.remove('module-menu-color')
} catch (err) { }
} catch (err) {}
setSelectedKeys()
}
}
//
const setSelectedKeys = () => {
//
moduleBackColor.value? selectedKeys.value = new Array([])
: selectedKeys.value = [tool.data.get('SNOWY_MENU_MODULE_ID')]
moduleBackColor.value
? (selectedKeys.value = new Array([]))
: (selectedKeys.value = [tool.data.get('SNOWY_MENU_MODULE_ID')])
}
</script>
@ -107,27 +121,17 @@
color: white;
font-size: 8px;
}
.module-comp {
display: flex;
padding: 0 15px;
height: 49px;
text-align: center;
justify-content: center;
align-items: center;
cursor: pointer;
}
.module-comp:hover {
background: var(--header-color-split);
}
.ant-menu-horizontal > .ant-menu-item::after, .ant-menu-horizontal > .ant-menu-submenu::after {
.ant-menu-horizontal > .ant-menu-item::after,
.ant-menu-horizontal > .ant-menu-submenu::after {
content: none;
}
.module-menu{
.module-menu {
line-height: 50px;
border-bottom: 0px;
width: 105%;
flex: 0 0 auto;
}
.module-menu-color{
.module-menu-color {
color: white;
background-color: var(--primary-color);
}

View File

@ -78,7 +78,8 @@
<script>
import Fuse from 'fuse.js'
import { mapState } from 'vuex'
import { mapState } from 'pinia'
import { searchStore } from '@/store'
export default {
data() {
@ -89,7 +90,7 @@
}
},
computed: {
...mapState('search', ['pool']),
...mapState(searchStore, ['pool']),
//
resultsList() {
return this.results.length === 0 || this.searchText === '' ? this.pool : this.results
@ -207,22 +208,22 @@
</script>
<style lang="less" scoped>
:deep(.ant-input){
:deep(.ant-input) {
height: 35px;
}
:deep(.ant-input:not(:first-child)){
:deep(.ant-input:not(:first-child)) {
padding-left: 10px;
}
:deep(.ant-input-prefix){
:deep(.ant-input-prefix) {
font-size: 20px;
}
:deep(.ant-list-sm .ant-list-item){
:deep(.ant-list-sm .ant-list-item) {
padding: 4px 16px;
}
:deep(.ant-list-item-meta){
:deep(.ant-list-item-meta) {
align-items: center;
}
:deep(.ant-list-item.active){
:deep(.ant-list-item.active) {
background-color: var(--primary-1);
}
.search-box {

View File

@ -4,20 +4,21 @@
<h3>整体风格设置</h3>
<div class="snowy-setting-checkbox">
<a-tooltip v-for="(a, i) in sideStyleList" :key="i" placement="top">
<template #title
><span>{{ a.tips }}</span></template
>
<template #title>
<span>{{ a.tips }}</span>
</template>
<div :class="['snowy-setting-checkbox-item', a.style]" @click="setSideStyle(a.value)">
<check-outlined v-if="sideStyle === a.value" class="snowy-setting-checkbox-item-select-icon" />
<check-outlined v-if="theme === a.value" class="snowy-setting-checkbox-item-select-icon" />
</div>
</a-tooltip>
</div>
<h3>整体界面布局</h3>
<div class="snowy-setting-checkbox">
<a-tooltip v-for="(a, i) in layoutList" :key="i" placement="top">
<template #title
><span>{{ a.tips }}</span></template
>
<template #title>
<span>{{ a.tips }}</span>
</template>
<div :class="['snowy-setting-checkbox-item', a.style]" @click="layoutStyle(a.value)">
<div class="snowy-setting-layout-menu-doublerow-inner" />
<check-outlined v-if="layout === a.value" class="snowy-setting-checkbox-item-select-icon" />
@ -25,9 +26,9 @@
</a-tooltip>
</div>
<a-divider />
<div :style="{ marginBottom: '24px' }">
<div class="mb-4">
<h3>主题色</h3>
<div style="height: 50px">
<div class="h-[50px]">
<a-tooltip v-for="(item, index) in colorList" :key="index" class="snowy-setting-theme-color-colorBlock">
<template #title>
<span>{{ item.key }}</span>
@ -38,42 +39,40 @@
</a-tooltip>
</div>
</div>
<div :style="{ marginBottom: '24px' }">
<span
><h4>顶栏应用主题色<a-switch style="float: right" v-model:checked="topHanderThemeColorOpen" /></h4
></span>
<div class="mb-4 layout-slide">
<h4 class="">顶栏应用主题色</h4>
<a-switch :checked="topHanderThemeColorOpen" @change="changeTopHanderThemeColorOpen" />
</div>
<div :style="{ marginBottom: '24px' }">
<span
><h4>
顶栏主题色通栏<a-switch
style="float: right"
v-model:checked="topHanderThemeColorSpread"
:disabled="!topHanderThemeColorOpen"
/></h4
></span>
<div class="mb-4 layout-slide">
<h4>顶栏主题色通栏</h4>
<a-switch
style="float: right"
:checked="topHanderThemeColorSpread"
:disabled="!topHanderThemeColorOpen"
@change="changeTopHanderThemeColorSpread"
/>
</div>
<a-divider />
<a-form ref="form" style="text-align: right">
<a-form ref="form" class="text-right">
<a-form-item label="模块坞">
<a-switch v-model:checked="moduleUnfoldOpen" />
<a-switch :checked="moduleUnfoldOpen" @change="toggleState('moduleUnfoldOpen')" />
</a-form-item>
<a-form-item label="面包屑">
<a-switch v-model:checked="breadcrumbOpen" />
<a-switch :checked="breadcrumbOpen" @change="toggleState('breadcrumbOpen')" />
</a-form-item>
<a-form-item label="多标签">
<a-switch v-model:checked="layoutTagsOpen" />
<a-switch :checked="layoutTagsOpen" @change="toggleState('layoutTagsOpen')" />
</a-form-item>
<a-form-item label="折叠菜单">
<a-switch v-model:checked="menuIsCollapse" />
<a-switch :checked="menuIsCollapse" @change="toggleState('menuIsCollapse')" />
</a-form-item>
<a-form-item label="菜单排他展开">
<a-switch v-model:checked="sideUniqueOpen" />
<a-switch :checked="sideUniqueOpen" @change="toggleState('sideUniqueOpen')" />
</a-form-item>
<a-form-item label="表单风格">
<a-select
v-model:value="formStyle"
style="width: 80px"
:value="formStyle"
class="!w-[80px]"
size="small"
:options="xnFormStyleOptions"
@change="formStyleChange"
@ -91,13 +90,22 @@
<script>
import { colorList } from '@/config/settingConfig'
import { ThemeModeEnum } from '@/utils/enum'
import { globalStore } from '@/store'
import { mapState, mapStores } from 'pinia'
import tool from '@/utils/tool'
const toolDataNameMap = {
menuIsCollapse: 'MENU_COLLAPSE',
sideUniqueOpen: 'SIDE_UNIQUE_OPEN',
layoutTagsOpen: 'LAYOUT_TAGS_OPEN',
breadcrumbOpen: 'BREADCRUMD_OPEN',
topHanderThemeColorOpen: 'TOP_HANDER_THEME_COLOR_OPEN',
topHanderThemeColorSpread: 'TOP_HANDER_THEME_COLOR_SPREAD',
moduleUnfoldOpen: 'MODULE_UNFOLD_OPEN'
}
export default defineComponent({
data() {
return {
//
sideStyle: tool.data.get('SNOWY_THEME') || this.$store.state.global.theme,
sideStyleList: [
{
tips: '暗色主题风格',
@ -115,7 +123,6 @@
style: 'snowy-setting-checkbox-item-realdark'
}
],
layout: tool.data.get('SNOWY_LAYOUT') || this.$store.state.global.layout,
layoutList: [
{
tips: '经典',
@ -138,106 +145,61 @@
value: 'modal'
}
],
topHanderThemeColorOpen:
tool.data.get('SNOWY_TOP_HANDER_THEME_COLOR_OPEN') || this.$store.state.global.topHanderThemeColorOpen,
topHanderThemeColorSpread:
tool.data.get('SNOWY_TOP_HANDER_THEME_COLOR_SPREAD') ||
this.$store.state.global.topHanderThemeColorSpread,
menuIsCollapse: tool.data.get('SNOWY_MENU_COLLAPSE') || this.$store.state.global.menuIsCollapse,
sideUniqueOpen: tool.data.get('SNOWY_SIDE_UNIQUE_OPEN') || this.$store.state.global.sideUniqueOpen,
layoutTagsOpen: tool.data.get('SNOWY_LAYOUT_TAGS_OPEN') || this.$store.state.global.layoutTagsOpen,
breadcrumbOpen: tool.data.get('SNOWY_BREADCRUMD_OPEN') || this.$store.state.global.breadcrumbOpen,
moduleUnfoldOpen: tool.data.get('SNOWY_MODULE_UNFOLD_OPEN') || this.$store.state.global.moduleUnfoldOpen,
theme: tool.data.get('APP_THEME') || this.$store.state.global.theme,
themeColor: tool.data.get('SNOWY_THEME_COLOR') || this.$store.state.global.themeColor,
formStyle: tool.data.get('SNOWY_FORM_STYLE') || this.$store.state.global.formStyle,
colorList
}
},
watch: {
menuIsCollapse() {
this.$store.commit('TOGGLE_menuIsCollapse')
if (this.$store.state.global.menuIsCollapse) {
tool.data.set('SNOWY_MENU_COLLAPSE', true)
} else {
tool.data.set('SNOWY_MENU_COLLAPSE', false)
}
},
sideUniqueOpen() {
this.$store.commit('TOGGLE_sideUniqueOpen')
if (this.$store.state.global.sideUniqueOpen) {
tool.data.set('SNOWY_SIDE_UNIQUE_OPEN', true)
} else {
tool.data.set('SNOWY_SIDE_UNIQUE_OPEN', false)
}
},
layoutTagsOpen() {
this.$store.commit('TOGGLE_layoutTagsOpen')
if (this.$store.state.global.layoutTagsOpen) {
tool.data.set('SNOWY_LAYOUT_TAGS_OPEN', true)
} else {
tool.data.set('SNOWY_LAYOUT_TAGS_OPEN', false)
}
},
breadcrumbOpen() {
this.$store.commit('TOGGLE_breadcrumbOpen')
if (this.$store.state.global.breadcrumbOpen) {
tool.data.set('SNOWY_BREADCRUMD_OPEN', true)
} else {
tool.data.set('SNOWY_BREADCRUMD_OPEN', false)
}
},
topHanderThemeColorOpen() {
this.$store.commit('TOGGLE_topHanderThemeColorOpen')
if (this.$store.state.global.topHanderThemeColorOpen) {
tool.data.set('SNOWY_TOP_HANDER_THEME_COLOR_OPEN', true)
} else {
//
tool.data.set('SNOWY_TOP_HANDER_THEME_COLOR_OPEN', false)
// false
this.topHanderThemeColorSpread = false
}
},
topHanderThemeColorSpread() {
this.$store.commit('TOGGLE_topHanderThemeColorSpread')
if (this.$store.state.global.topHanderThemeColorSpread) {
tool.data.set('SNOWY_TOP_HANDER_THEME_COLOR_SPREAD', true)
} else {
computed: {
...mapStores(globalStore),
...mapState(globalStore, [
'theme',
'themeColor',
'layout',
'menuIsCollapse',
'sideUniqueOpen',
'layoutTagsOpen',
'breadcrumbOpen',
'moduleUnfoldOpen',
'topHanderThemeColorOpen',
'topHanderThemeColorSpread',
'formStyle'
])
},
mounted() {},
methods: {
changeTopHanderThemeColorOpen() {
this.toggleState('topHanderThemeColorOpen')
if (!this.topHanderThemeColorOpen) {
this.globalStore.topHanderThemeColorSpread = false
tool.data.set('SNOWY_TOP_HANDER_THEME_COLOR_SPREAD', false)
}
},
moduleUnfoldOpen() {
this.$store.commit('TOGGLE_moduleUnfoldOpen')
if (this.$store.state.global.moduleUnfoldOpen) {
tool.data.set('SNOWY_MODULE_UNFOLD_OPEN', true)
} else {
tool.data.set('SNOWY_MODULE_UNFOLD_OPEN', false)
}
changeTopHanderThemeColorSpread() {
this.toggleState('topHanderThemeColorSpread')
},
toggleState(stateName) {
this.globalStore.toggleConfig(stateName)
const toolDataName = toolDataNameMap[stateName]
tool.data.set(`SNOWY_${toolDataName}`, this.globalStore[stateName])
},
},
methods: {
//
setSideStyle(value) {
this.$store.commit('SET_theme', value)
this.sideStyle = value
this.globalStore.setTheme(value)
tool.data.set('SNOWY_THEME', value)
},
//
layoutStyle(value) {
this.$store.commit('SET_layout', value)
this.globalStore.setLayout(value)
tool.data.set('SNOWY_LAYOUT', value)
this.layout = value
},
//
tagColor(value) {
this.themeColor = value
tool.data.set('SNOWY_THEME_COLOR', value)
this.$store.commit('SET_themeColor', value)
this.globalStore.setThemeColor(value)
},
//
formStyleChange(value) {
tool.data.set('SNOWY_FORM_STYLE', value)
this.$store.commit('SET_formStyle', value)
this.globalStore.setFormStyle(value)
}
}
})

View File

@ -19,8 +19,8 @@
<script>
import NavMenu from './NavMenu.vue'
import tool from '@/utils/tool'
import store from '@/store'
import { globalStore } from '@/store'
import { mapState } from 'pinia'
export default {
components: {
@ -69,10 +69,12 @@
data() {
return {
visible: false,
menu: [],
sysBaseConfig: tool.data.get('SNOWY_SYS_BASE_CONFIG') || store.state.global.sysBaseConfig
menu: []
}
},
computed: {
...mapState(globalStore, ['sysBaseConfig'])
},
created() {
const menu = this.$router.getMenu()
this.menu = this.filterUrl(menu)

View File

@ -60,6 +60,8 @@
<script>
import tool from '@/utils/tool'
import XnContextMenu from '@/components/XnContextMenu/index.vue'
import {globalStore, iframeStore, keepAliveStore, viewTagsStore} from '@/store'
import { mapState, mapActions } from 'pinia'
export default {
name: 'Tags',
@ -67,7 +69,7 @@
props: {},
data() {
return {
tagList: this.$store.state.viewTags.viewTags,
// tagList: [],
activeKey: this.$route.fullPath,
maxTabs: 20,
contextMenuTarget: null,
@ -75,9 +77,19 @@
currentContextMenuTabIndex: 0
}
},
computed: {
...mapState(viewTagsStore, ['viewTags']),
...mapState(globalStore, ['layoutTagsOpen']),
tagList() {
return this.viewTags
}
},
watch: {
$route(to) {
this.addViewTags(to)
},
layoutTagsOpen() {
this.closeOtherCacheTabs()
}
},
created() {
@ -98,6 +110,9 @@
}
},
methods: {
...mapActions(viewTagsStore, ['addViewTags', 'pushViewTags', 'removeViewTags']),
...mapActions(iframeStore, ['addIframe', 'removeIframeList', 'refreshIframe']),
...mapActions(keepAliveStore, ['pushKeepLive', 'removeKeepLive', 'setRouteShow']),
handleTabContextMenu(evt) {
evt.preventDefault()
let target = evt.target
@ -150,14 +165,13 @@
// tag
addViewTags(route) {
this.activeKey = route.fullPath
if (route.name && !route.meta.fullpage) {
this.$store.commit('pushViewTags', route)
this.$store.commit('pushKeepLive', route.name)
this.pushViewTags(route)
this.pushKeepLive(route.name)
}
if (this.tagList.length - 1 > this.maxTabs) {
const firstTag = this.tagList[1]
this.$store.commit('removeViewTags', firstTag)
this.removeViewTags(firstTag)
}
},
// tag
@ -166,9 +180,9 @@
},
// tag
closeSelectedTag(tag, autoPushLatestView = true) {
this.$store.commit('removeViewTags', tag)
this.$store.commit('removeIframeList', tag)
this.$store.commit('removeKeepLive', tag.name)
this.removeViewTags(tag)
this.removeIframeList(tag)
this.removeKeepLive(tag.name)
if (autoPushLatestView && this.isActive(tag)) {
const latestView = this.tagList.slice(-1)[0]
if (latestView) {
@ -190,13 +204,13 @@
query: nowTag.query
})
}
this.$store.commit('refreshIframe', nowTag)
this.refreshIframe(nowTag)
setTimeout(() => {
this.$store.commit('removeKeepLive', nowTag.name)
this.$store.commit('setRouteShow', false)
this.removeKeepLive(nowTag.name)
this.setRouteShow(false)
this.$nextTick(() => {
this.$store.commit('pushKeepLive', nowTag.name)
this.$store.commit('setRouteShow', true)
this.pushKeepLive(nowTag.name)
this.setRouteShow(true)
})
}, 0)
},
@ -230,6 +244,13 @@
}
})
},
//
closeOtherCacheTabs () {
const tags = [...this.tagList]
tags.forEach((tag) => {
this.closeSelectedTag(tag, false)
})
},
// TAB
maximize() {
this.contextMenuVisible = false

View File

@ -81,6 +81,8 @@
import devUserMessage from './message.vue'
import panelSearch from './panel-search/index.vue'
import mixinSearch from './mixins/search'
import { mapState } from 'pinia'
import { globalStore } from '@/store'
export default {
components: {
setting,
@ -92,29 +94,18 @@
return {
lang: [],
settingDialog: false,
userInfo: {},
userName: '',
userNameF: '',
setDeawer: import.meta.env.VITE_SET_DRAWER
}
},
computed: {
ismobile() {
return this.$store.state.global.ismobile
},
userInfoWatch() {
return this.$store.state.global.userInfo
}
},
watch: {
userInfoWatch(newVal, oldVal) {
this.userInfo = newVal
}
...mapState(globalStore, ['ismobile', 'userInfo'])
},
created() {
//
this.lang = new Array(this.$TOOL.data.get('APP_LANG') || this.$CONFIG.LANG)
this.userInfo = this.$TOOL.data.get('USER_INFO')
this.userName = this.userInfo?.userName || ''
this.userNameF = this.userName.substring(0, 1)
},
@ -213,18 +204,6 @@
align-items: center;
height: 100%;
}
.user-bar .panel-item {
padding: 0 10px;
cursor: pointer;
height: 100%;
display: flex;
align-items: center;
}
.user-bar .panel-item i {
}
.user-bar .panel-item:hover {
background: var(--header-color-split);
}
.user-bar .user-avatar {
height: 49px;
display: flex;

View File

@ -4,7 +4,7 @@
<a-layout>
<a-layout-sider
v-if="!ismobile"
v-model:collapsed="$store.state.global.menuIsCollapse"
v-model:collapsed="menuIsCollapse"
:trigger="null"
collapsible
:theme="sideTheme"
@ -18,7 +18,7 @@
</div>
</div>
</header>
<div :class="$store.state.global.menuIsCollapse ? 'aminui-side isCollapse' : 'aminui-side'">
<div :class="menuIsCollapse ? 'aminui-side isCollapse' : 'aminui-side'">
<div class="adminui-side-scroll">
<a-menu
v-model:openKeys="openKeys"
@ -28,17 +28,21 @@
@select="onSelect"
@openChange="onOpenChange"
>
<NavMenu :nav-menus="menu"></NavMenu>
<NavMenu :nav-menus="menu" />
</a-menu>
</div>
</div>
</a-layout-sider>
<!-- 手机端情况下的左侧菜单 -->
<Side-m v-if="ismobile"></Side-m>
<Side-m v-if="ismobile" />
<!-- 右侧布局 -->
<a-layout>
<div id="snowyHeader" class="snowy-header">
<div class="snowy-header-left" style="padding-left: 0px">
<div v-if="!ismobile" class="panel-item hidden-sm-and-down" @click="menuIsCollapseClick">
<MenuUnfoldOutlined v-if="menuIsCollapse" />
<MenuFoldOutlined v-else />
</div>
<moduleMenu @switchModule="switchModule" />
<Topbar v-if="!ismobile && breadcrumbOpen" />
</div>
@ -47,15 +51,15 @@
</div>
</div>
<!-- 多标签 -->
<Tags v-if="!ismobile && layoutTagsOpen"></Tags>
<Tags v-if="!ismobile && layoutTagsOpen" />
<a-layout-content class="main-content-wrapper">
<div id="adminui-main" class="adminui-main">
<router-view v-slot="{ Component }">
<keep-alive :include="$store.state.keepAlive.keepLiveRoute">
<component :is="Component" :key="$route.name" v-if="$store.state.keepAlive.routeShow" />
<keep-alive :include="keepLiveRoute">
<component :is="Component" :key="$route.name" v-if="routeShow" />
</keep-alive>
</router-view>
<iframe-view></iframe-view>
<iframe-view />
<div class="main-bottom-wrapper">
<a style="color: #a0a0a0" :href="sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL" target="_blank">{{
sysBaseConfig.SNOWY_SYS_COPYRIGHT
@ -115,7 +119,7 @@
<a-layout-sider
v-if="!ismobile"
v-show="layoutSiderDowbleMenu"
v-model:collapsed="$store.state.global.menuIsCollapse"
v-model:collapsed="menuIsCollapse"
:trigger="null"
width="170"
collapsible
@ -125,21 +129,25 @@
<h2 class="snowy-title">{{ pmenu.meta.title }}</h2>
</div>
<a-menu
v-model:collapsed="$store.state.global.menuIsCollapse"
v-model:collapsed="menuIsCollapse"
v-model:openKeys="openKeys"
v-model:selectedKeys="selectedKeys"
mode="inline"
:theme="secondMenuSideTheme"
@select="onSelect"
>
<NavMenu :nav-menus="nextMenu"></NavMenu>
<NavMenu :nav-menus="nextMenu" />
</a-menu>
</a-layout-sider>
<!-- 手机端情况下的左侧菜单 -->
<Side-m v-if="ismobile"></Side-m>
<Side-m v-if="ismobile" />
<a-layout>
<div id="snowyHeader" class="snowy-header">
<div class="snowy-header-left" style="padding-left: 0px">
<div v-if="!ismobile" class="panel-item hidden-sm-and-down" @click="menuIsCollapseClick">
<MenuUnfoldOutlined v-if="menuIsCollapse" />
<MenuFoldOutlined v-else />
</div>
<moduleMenu @switchModule="switchModule" />
<Topbar v-if="!ismobile && breadcrumbOpen" />
</div>
@ -152,14 +160,14 @@
<a-layout-content class="main-content-wrapper">
<div id="adminui-main" class="adminui-main">
<router-view v-slot="{ Component }">
<keep-alive :include="$store.state.keepAlive.keepLiveRoute">
<component :is="Component" v-if="$store.state.keepAlive.routeShow" :key="$route.name" />
<keep-alive :include="keepLiveRoute">
<component :is="Component" v-if="routeShow" :key="$route.name" />
</keep-alive>
</router-view>
<iframe-view></iframe-view>
<iframe-view />
<div class="main-bottom-wrapper">
<a style="color: #a0a0a0" :href="sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL" target="_blank">{{
sysBaseConfig.SNOWY_SYS_COPYRIGHT
sysBaseConfig.SNOWY_SYS_COPYRIGHT
}}</a>
</div>
</div>
@ -183,8 +191,9 @@
import iframeView from './components/iframeView.vue'
import moduleMenu from './components/moduleMenu.vue'
import { ThemeModeEnum } from '@/utils/enum'
import { globalStore, keepAliveStore } from '@/store'
import { mapState, mapStores, mapActions } from 'pinia'
import tool from '@/utils/tool'
import store from '@/store'
export default defineComponent({
name: 'Index',
@ -208,39 +217,32 @@
onSelectTag: false,
selectedKeys: [],
openKeys: [],
openKeysOther: [],
sysBaseConfig: tool.data.get('SNOWY_SYS_BASE_CONFIG') || store.state.global.sysBaseConfig
openKeysOther: []
}
},
computed: {
...mapStores(globalStore),
...mapState(globalStore, [
'theme',
'ismobile',
'layout',
'layoutTagsOpen',
'menuIsCollapse',
'breadcrumbOpen',
'topHanderThemeColorOpen',
'topHanderThemeColorSpread',
'topHanderThemeColor',
'sideUniqueOpen',
'sysBaseConfig'
]),
...mapState(keepAliveStore, ['keepLiveRoute', 'routeShow']),
sideTheme() {
const theme = this.$store.state.global.theme
const theme = this.theme
return theme === ThemeModeEnum.REAL_DARK ? ThemeModeEnum.DARK : theme
},
secondMenuSideTheme() {
const theme = this.$store.state.global.theme
const theme = this.theme
return theme === ThemeModeEnum.REAL_DARK ? ThemeModeEnum.DARK : ThemeModeEnum.LIGHT
},
ismobile() {
return this.$store.state.global.ismobile
},
layout() {
return this.$store.state.global.layout
},
layoutTagsOpen() {
return this.$store.state.global.layoutTagsOpen
},
menuIsCollapse() {
return this.$store.state.global.menuIsCollapse
},
breadcrumbOpen() {
return this.$store.state.global.breadcrumbOpen
},
topHanderThemeColorOpen() {
return this.$store.state.global.topHanderThemeColorOpen
},
topHanderThemeColorSpread() {
return this.$store.state.global.topHanderThemeColorSpread
}
},
watch: {
@ -273,7 +275,7 @@
window.addEventListener('resize', this.onLayoutResize)
this.moduleMenu = this.$router.getMenu()
//
const menuModuleId = this.$TOOL.data.get('SNOWY_MENU_MODULE_ID')
const menuModuleId = tool.data.get('SNOWY_MENU_MODULE_ID')
let menu = []
if (menuModuleId) {
//
@ -294,6 +296,7 @@
this.switchoverTopHanderThemeColor()
},
methods: {
...mapActions(globalStore, ['setTheme', 'setIsmobile', 'setLayout', 'setMenuIsCollapse']),
//
switchModule(id) {
const menu = this.moduleMenu
@ -301,7 +304,7 @@
const menus = menu.filter((item) => item.id === id)[0].children
if (menus.length > 0) {
//
this.$TOOL.data.set('SNOWY_MENU_MODULE_ID', id)
tool.data.set('SNOWY_MENU_MODULE_ID', id)
//
this.menu = this.filterUrl(menus)
//
@ -335,11 +338,7 @@
},
onLayoutResize() {
const clientWidth = document.body.clientWidth
if (clientWidth < 992) {
this.$store.commit('SET_ismobile', true)
} else {
this.$store.commit('SET_ismobile', false)
}
this.setIsmobile(clientWidth < 992)
},
//
showThis() {
@ -361,7 +360,7 @@
if (!this.onSelectTag) {
const pidKey = this.getParentKeys(this.menu, active)
this.openKeys = pidKey
} else if (this.$store.state.global.sideUniqueOpen) {
} else if (this.sideUniqueOpen) {
const pidKey = this.getParentKeys(this.menu, active)
this.openKeys = pidKey
}
@ -412,7 +411,7 @@
},
// /
onOpenChange(keys) {
if (this.$store.state.global.sideUniqueOpen) {
if (this.sideUniqueOpen) {
//
const openKey = keys[keys.length - 1]
if (keys.length > 1) {
@ -464,6 +463,9 @@
})
return newMap
},
menuIsCollapseClick() {
this.globalStore.toggleConfig('menuIsCollapse')
},
// 退
exitMaximize() {
document.getElementById('app').classList.remove('main-maximize')
@ -477,19 +479,19 @@
: header.classList.remove('snowy-header-primary-color')
//
const headerLogin = document.getElementById('snowyHeaderLogo')
try{
try {
this.topHanderThemeColorSpread
? headerLogin.classList.add('snowy-header-logo-primary-color')
: headerLogin.classList.remove('snowy-header-logo-primary-color')
}catch (e) { }
} catch (e) {}
//
if (this.layout === 'doublerow') {
const snowyDoublerowSideTop = document.getElementById('snowyDoublerowSideTop')
try{
try {
this.topHanderThemeColorSpread
? snowyDoublerowSideTop.classList.add('snowy-doublerow-side-top-primary-color')
: snowyDoublerowSideTop.classList.remove('snowy-doublerow-side-top-primary-color')
}catch (e) { }
} catch (e) {}
}
}
}

View File

@ -1,15 +1,16 @@
import { createApp } from 'vue'
import Antd from 'ant-design-vue'
import { createPinia } from 'pinia'
import './style/index.less'
import snowy from './snowy'
import i18n from './locales'
import router from './router'
import store from './store'
import App from './App.vue'
import './tailwind.css'
const app = createApp(App)
app.use(store)
app.use(createPinia())
app.use(router)
app.use(Antd)
app.use(i18n)

View File

@ -19,8 +19,7 @@ import userRoutes from '@/config/route'
import tool from '@/utils/tool'
import { cloneDeep } from 'lodash-es'
const modules = import.meta.glob('/src/views/**/**.vue')
import store from '@/store'
const sysBaseConfig = tool.data.get('SNOWY_SYS_BASE_CONFIG') || store.state.global.sysBaseConfig
import { globalStore, searchStore } from '@/store'
// 进度条配置
NProgress.configure({ showSpinner: false, speed: 500 })
@ -42,7 +41,7 @@ const router = createRouter({
})
// 设置标题
document.title = sysBaseConfig.SNOWY_SYS_NAME
// document.title = sysBaseConfig.SNOWY_SYS_NAME
// 判断是否已加载过动态/静态路由
const isGetRouter = ref(false)
@ -57,6 +56,9 @@ const whiteList = exportWhiteListFromRouter(whiteListRouters)
router.beforeEach(async (to, from, next) => {
NProgress.start()
const store = globalStore()
const sysBaseConfig = tool.data.get('SNOWY_SYS_BASE_CONFIG') || store.sysBaseConfig
// 动态标题
document.title = to.meta.title
? `${to.meta.title} - ${sysBaseConfig.SNOWY_SYS_NAME}`
@ -83,6 +85,9 @@ router.beforeEach(async (to, from, next) => {
isGetRouter.value = false
next()
return false
} else {
// 这里需要使用 localStorage 保存登录之前要访问的页面
tool.data.set('LAST_VIEWS_PATH', to.fullPath)
}
if (!token) {
next({
@ -109,7 +114,9 @@ router.beforeEach(async (to, from, next) => {
menuRouter.forEach((item) => {
router.addRoute('layout', item)
})
store.commit('search/init', menuRouter)
const search_store = searchStore()
search_store.init(menuRouter)
isGetRouter.value = true
next({ ...to, replace: true })
return false

View File

@ -9,14 +9,15 @@
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/
import { nextTick } from 'vue'
import store from '@/store'
import { viewTagsStore } from '@/store'
export function beforeEach(to, from) {
const adminMain = document.querySelector('#adminui-main')
if (!adminMain) {
return false
}
store.commit('updateViewTags', {
const store = viewTagsStore()
store.updateViewTags({
fullPath: from.fullPath,
scrollTop: adminMain.scrollTop
})
@ -28,7 +29,8 @@ export function afterEach(to) {
return false
}
nextTick(() => {
const beforeRoute = store.state.viewTags.viewTags.filter((v) => v.fullPath == to.fullPath)[0]
const store = viewTagsStore()
const beforeRoute = store.viewTags.filter((v) => v.fullPath == to.fullPath)[0]
if (beforeRoute) {
adminMain.scrollTop = beforeRoute.scrollTop || 0
}

View File

@ -8,7 +8,8 @@
* 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/
import { changeColor, getLocalSetting } from '@/utils/themeUtil'
import { defineStore } from 'pinia'
import { changeColor } from '@/utils/themeUtil'
import config from '@/config'
import { message } from 'ant-design-vue'
import tool from '@/utils/tool'
@ -25,8 +26,13 @@ const getCacheConfig = (value) => {
}
return data
}
export default {
state: {
/**
* deprecated 请使用 useGlobalStore
*/
export const globalStore = defineStore({
id: 'global',
state: () => ({
// 移动端布局
ismobile: false,
// 布局
@ -42,8 +48,7 @@ export default {
// 顶栏是否应用主题色
topHanderThemeColorOpen: getCacheConfig('SNOWY_TOP_HANDER_THEME_COLOR_OPEN'),
// 顶栏主题色通栏
topHanderThemeColorSpread:
getCacheConfig('SNOWY_TOP_HANDER_THEME_COLOR_SPREAD'),
topHanderThemeColorSpread: getCacheConfig('SNOWY_TOP_HANDER_THEME_COLOR_SPREAD'),
// 模块坞
moduleUnfoldOpen: getCacheConfig('SNOWY_MODULE_UNFOLD_OPEN'),
// 主题
@ -56,57 +61,42 @@ export default {
userInfo: toolDataGet('USER_INFO') || {},
// 系统配置
sysBaseConfig: toolDataGet('SNOWY_SYS_BASE_CONFIG') || config.SYS_BASE_CONFIG
},
mutations: {
SET_ismobile(state, key) {
state.ismobile = key
}),
getters: {},
actions: {
setIsmobile(key) {
this.ismobile = key
},
SET_layout(state, key) {
state.layout = key
setLayout(key) {
this.layout = key
},
SET_theme(state, key) {
state.theme = key
setTheme(key) {
this.theme = key
const closeMessage = message.loading(`加载中...`)
changeColor(state.themeColor, key).then(closeMessage)
changeColor(this.themeColor, key).then(closeMessage)
},
SET_themeColor(state, key) {
state.themeColor = key
setThemeColor(key) {
this.themeColor = key
const closeMessage = message.loading(`加载中...`)
changeColor(key, state.theme).then(closeMessage)
changeColor(key, this.theme).then(closeMessage)
},
initTheme(state) {
initTheme() {
const closeMessage = message.loading(`加载中...`)
changeColor(state.themeColor, state.theme).then(closeMessage)
changeColor(this.themeColor, this.theme).then(closeMessage)
},
TOGGLE_menuIsCollapse(state) {
state.menuIsCollapse = !state.menuIsCollapse
toggleConfig(key) {
this[key] = !this[key]
},
TOGGLE_sideUniqueOpen(state) {
state.sideUniqueOpen = !state.sideUniqueOpen
setFormStyle(key) {
this.formStyle = key
},
TOGGLE_layoutTagsOpen(state) {
state.layoutTagsOpen = !state.layoutTagsOpen
setUserInfo(key) {
this.userInfo = key
},
TOGGLE_breadcrumbOpen(state) {
state.breadcrumbOpen = !state.breadcrumbOpen
},
TOGGLE_topHanderThemeColorOpen(state) {
state.topHanderThemeColorOpen = !state.topHanderThemeColorOpen
},
TOGGLE_topHanderThemeColorSpread(state) {
state.topHanderThemeColorSpread = !state.topHanderThemeColorSpread
},
TOGGLE_moduleUnfoldOpen(state) {
state.moduleUnfoldOpen = !state.moduleUnfoldOpen
},
SET_formStyle(state, key) {
state.formStyle = key
},
SET_userInfo(state, key) {
state.userInfo = key
},
SET_sysBaseConfig(state, key) {
state.sysBaseConfig = key
setSysBaseConfig(key) {
this.sysBaseConfig = key
}
}
}
})
export const useGlobalStore = globalStore

View File

@ -8,31 +8,34 @@
* 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/
/* eslint-disable eqeqeq */
export default {
state: {
import { defineStore } from 'pinia'
export const iframeStore = defineStore({
id: 'iframe',
state: () => ({
iframeList: []
},
mutations: {
setIframeList(state, route) {
state.iframeList = []
state.iframeList.push(route)
}),
getters: {},
actions: {
setIframeList(route) {
this.iframeList = []
this.iframeList.push(route)
},
pushIframeList(state, route) {
const target = state.iframeList.find((item) => item.path === route.path)
pushIframeList(route) {
const target = this.iframeList.find((item) => item.path === route.path)
if (!target) {
state.iframeList.push(route)
this.iframeList.push(route)
}
},
removeIframeList(state, route) {
state.iframeList.forEach((item, index) => {
removeIframeList(route) {
this.iframeList.forEach((item, index) => {
if (item.path === route.path) {
state.iframeList.splice(index, 1)
this.iframeList.splice(index, 1)
}
})
},
refreshIframe(state, route) {
state.iframeList.forEach((item) => {
refreshIframe(route) {
this.iframeList.forEach((item) => {
if (item.path === route.path) {
const url = route.meta.url
item.meta.url = ''
@ -42,8 +45,8 @@ export default {
}
})
},
clearIframeList(state) {
state.iframeList = []
clearIframeList() {
this.iframeList = []
}
}
}
})

View File

@ -1,27 +1,5 @@
/**
* Copyright [2022] [https://www.xiaonuo.vip]
* Snowy采用APACHE LICENSE 2.0开源协议您在使用过程中需要注意以下几点
* 1.请不要删除和修改根目录下的LICENSE文件
* 2.请不要删除和修改Snowy源码头部的版权声明
* 3.本项目代码可免费商业使用商业使用请保留源码和相关描述文件的项目出处作者声明等
* 4.分发源码时候请注明软件出处 https://www.xiaonuo.vip
* 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/
import { createStore } from 'vuex'
import global from './modules/global'
import iframe from './modules/iframe'
import keepAlive from './modules/keepAlive'
import viewTags from './modules/viewTags'
import search from './modules/search'
// 自动import导入所有 vuex 模块
export default createStore({
modules: {
global,
iframe,
keepAlive,
viewTags,
search
}
})
export * from './global'
export * from './search'
export * from './iframe'
export * from './keepAlive'
export * from './viewTags'

View File

@ -8,37 +8,39 @@
* 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/
export default {
state: {
import { defineStore } from 'pinia'
export const keepAliveStore = defineStore({
id: 'keepAlive',
state: () => ({
keepLiveRoute: [],
routeKey: null,
routeShow: true
},
mutations: {
pushKeepLive(state, component) {
if (!state.keepLiveRoute.includes(component)) {
state.keepLiveRoute.push(component)
}
},
removeKeepLive(state, component) {
const index = state.keepLiveRoute.indexOf(component)
if (index !== -1) {
state.keepLiveRoute.splice(index, 1)
}
},
clearKeepLive(state) {
state.keepLiveRoute = []
},
setRouteKey(state, key) {
state.routeKey = key
},
setRouteShow(state, key) {
state.routeShow = key
}
},
}),
getters: {},
actions: {
setRouteKey({ commit }, key) {
commit('setRouteKey', key)
pushKeepLive(component) {
if (!this.keepLiveRoute.includes(component)) {
this.keepLiveRoute.push(component)
}
},
removeKeepLive(component) {
const index = this.keepLiveRoute.indexOf(component)
if (index !== -1) {
this.keepLiveRoute.splice(index, 1)
}
},
clearKeepLive() {
this.keepLiveRoute = []
},
setRouteKey(key) {
this.routeKey = key
},
setRouteShow(key) {
this.routeShow = key
},
setRouteKeyAction(key) {
this.setRouteKey(key)
}
}
}
})

View File

@ -1,29 +1,25 @@
import '@/utils/objects'
import { defineStore } from 'pinia'
export default {
namespaced: true,
state: {
// 搜索面板激活状态
export const searchStore = defineStore({
id: 'search',
state: () => ({
active: false,
// 快捷键
hotkey: {
open: 's',
close: 'esc'
},
// 所有可以搜索的页面
pool: []
},
mutations: {
// 切换激活状态
toggle(state) {
state.active = !state.active
}),
getters: {},
actions: {
toggleActive() {
this.active = !this.active
},
// 设置激活模式
set(state, active) {
state.active = active
setActive(active) {
this.active = active
},
// 初始化
init(state, menu) {
init(menu) {
const pool = []
const getFullName = function (meta) {
if (meta.breadcrumb) {
@ -40,7 +36,7 @@ export default {
if ('menu' === m.meta.type) {
if (m.children) {
push(m.children)
} else if (m.children === null){
} else if (m.children === null) {
pool.push({
icon: m.meta.icon,
path: m.path,
@ -55,7 +51,7 @@ export default {
})
}
push(menu)
state.pool = pool
this.pool = pool
}
}
}
})

View File

@ -8,43 +8,46 @@
* 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/
/* eslint-disable eqeqeq */
export default {
state: {
import { defineStore } from 'pinia'
export const viewTagsStore = defineStore({
id: 'viewTags',
state: () => ({
viewTags: []
},
mutations: {
pushViewTags(state, route) {
const target = state.viewTags.find((item) => item.fullPath === route.fullPath)
}),
getters: {},
actions: {
pushViewTags(route) {
const target = this.viewTags.find((item) => item.fullPath === route.fullPath)
const isName = route.name
if (!target && isName) {
state.viewTags.push(route)
this.viewTags.push(route)
}
},
removeViewTags(state, route) {
state.viewTags.forEach((item, index) => {
removeViewTags(route) {
this.viewTags.forEach((item, index) => {
if (item.fullPath === route.fullPath) {
state.viewTags.splice(index, 1)
this.viewTags.splice(index, 1)
}
})
},
updateViewTags(state, route) {
state.viewTags.forEach((item) => {
updateViewTags(route) {
this.viewTags.forEach((item) => {
if (item.fullPath == route.fullPath) {
item = Object.assign(item, route)
Object.assign(item, route)
}
})
},
updateViewTagsTitle(state, title = '') {
updateViewTagsTitle(title = '') {
const nowFullPath = location.hash.substring(1)
state.viewTags.forEach((item) => {
this.viewTags.forEach((item) => {
if (item.fullPath == nowFullPath) {
item.meta.title = title
}
})
},
clearViewTags(state) {
state.viewTags = []
clearViewTags() {
this.viewTags = []
}
}
}
})

View File

@ -150,6 +150,11 @@ a, button, input, textarea {
border-bottom: 1px solid var(--border-color-split);
box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
background-color: var(--body-background);
.ant-menu-item{
height: 48px;
line-height: 48px;
}
}
// 应用主题色
.snowy-header-primary-color {
@ -164,6 +169,10 @@ a, button, input, textarea {
.ant-breadcrumb-separator {
color: white;
}
.ant-menu-light .ant-menu-item:hover{
color: #ccc;
background-color: var(--primary-7);
}
}
.ant-layout-sider-dark {
@ -227,6 +236,17 @@ a, button, input, textarea {
align-items: center;
}
.panel-item {
padding: 0 10px;
cursor: pointer;
height: 100%;
display: flex;
align-items: center;
}
.panel-item:hover {
background: var(--header-color-split);
}
/* 多标签 */
.snowy-tags {
height: 40px;
@ -388,6 +408,24 @@ a, button, input, textarea {
padding: 12px 0!important;
}
/* 重写antdv的表格滚动条 */
.ant-table-body {
&::-webkit-scrollbar {
height: 10px;
width: 10px;
}
&::-webkit-scrollbar-thumb {
border-radius: 10px;
-webkit-box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.1);
background: @border-color-split;
}
&::-webkit-scrollbar-track {
-webkit-box-shadow: 0;
border-radius: 10px;
background: @background-color-base;
}
}
// 滚动条需要哪里加哪个class
body,
.ant-drawer-wrapper-body,
@ -407,7 +445,6 @@ body,
.org-table,
.pos-table,
.poi-list,
.ant-table-body,
.snowy-orgpos-vis,
.index-message-list,
.ant-picker-time-panel-column,

View File

@ -42,5 +42,10 @@ export const rules = {
pattern: /^\d{1,}$/,
message: '填写内容必须是纯数字',
trigger: 'blur'
},
price: {
pattern: /(?:^[1-9]([0-9]+)?(?:\.[0-9]{1,2})?$)|(?:^(?:0)$)|(?:^[0-9]\.[0-9](?:[0-9])?$)/,
message: '只支持正数金额',
trigger: 'blur'
}
}

View File

@ -10,6 +10,7 @@
*/
// 统一的请求发送
import axios from 'axios'
import qs from 'qs'
import { Modal, message, notification } from 'ant-design-vue'
import sysConfig from '@/config/index'
import tool from '@/utils/tool'
@ -108,8 +109,25 @@ service.interceptors.response.use(
} else {
// 统一成功提示
const responseUrl = response.config.url
const apiNameArray = ['add', 'edit', 'delete', 'update', 'grant', 'reset', 'start', 'stop',
'pass', 'disable', 'enable', 'revoke', 'suspend', 'active', 'turn', 'adjust', 'reject']
const apiNameArray = [
'add',
'edit',
'delete',
'update',
'grant',
'reset',
'start',
'stop',
'pass',
'disable',
'enable',
'revoke',
'suspend',
'active',
'turn',
'adjust',
'reject'
]
apiNameArray.forEach((apiName) => {
if (responseUrl.includes(apiName)) {
message.success(data.msg)
@ -131,37 +149,37 @@ service.interceptors.response.use(
}
)
// 适配器, 用于适配不同的请求方式
export const baseRequest = (url, value = {}, method = 'post', options = {}) => {
url = sysConfig.API_URL + url
if (method === 'post') {
return service.post(url, value, options)
} else if (method === 'get') {
return service.get(url, {
params: value,
...options
})
return service.get(url, { params: value, ...options })
} else if (method === 'formdata') {
return service({
method: 'post',
url,
data: value,
// 转换数据的方法
transformRequest: [
function (data) {
let ret = ''
for (const it in data) {
ret += `${encodeURIComponent(it)}=${encodeURIComponent(data[it])}&`
}
ret = ret.substring(0, ret.length - 1)
return ret
}
],
// 设置请求头
// form-data表单提交的方式
return service.post(url, qs.stringify(value), {
headers: {
'Content-Type': 'multipart/form-data'
}
},
...options
})
} else {
// 其他请求方式例如put、delete
return service({
method: method,
url: url,
data: value,
...options
})
}
}
// 模块内的请求, 会自动加上模块的前缀
export const moduleRequest =
(moduleUrl) =>
(url, ...arg) => {
return baseRequest(moduleUrl + url, ...arg)
}
export default service

View File

@ -17,13 +17,9 @@
import smCrypto from 'sm-crypto'
const sm2 = smCrypto.sm2
const sm3 = smCrypto.sm3
const sm4 = smCrypto.sm4
const cipherMode = 1 // 1 - C1C3C20 - C1C2C3默认为1
const publicKey =
'04298364ec840088475eae92a591e01284d1abefcda348b47eb324bb521bb03b0b2a5bc393f6b71dabb8f15c99a0050818b56b23f31743b93df9cf8948f15ddb54'
const privateKey = '3037723d47292171677ec8bd7dc9af696c7472bc5f251b2cec07e65fdef22e25'
const key = '0123456789abcdeffedcba9876543210'
/**
* 国密加解密工具类
@ -33,36 +29,8 @@ export default {
doSm2Encrypt(msgString) {
return sm2.doEncrypt(msgString, publicKey, cipherMode)
},
// SM2解密
doSm2Decrypt(encryptData) {
return sm2.doDecrypt(encryptData, privateKey, cipherMode)
},
// SM2数组加密
doSm2ArrayEncrypt(msgString) {
return sm2.doEncrypt(msgString, publicKey, cipherMode)
},
// SM2数组解密
doSm2ArrayDecrypt(encryptData) {
return sm2.doDecrypt(encryptData, privateKey, cipherMode, { output: 'array' })
},
// SM3哈希
doSm3Hash(msgString) {
return sm3(msgString)
},
// SM4 加密
doSm4Encrypt(msgString) {
return sm4.encrypt(msgString, key)
},
// SM4 CBC加密
doSm4CbcEncrypt(msgString) {
return sm4.encrypt(msgString, key, { mode: 'cbc', iv: 'fedcba98765432100123456789abcdef' })
},
// SM4 解密
doSm4Decrypt(encryptData) {
return sm4.decrypt(encryptData, key)
},
// SM4 CBC解密
doSm4CbcDecrypt(encryptData) {
return sm4.decrypt(encryptData, key, { mode: 'cbc', iv: 'fedcba98765432100123456789abcdef' })
}
}

View File

@ -12,28 +12,29 @@ import { nextTick } from 'vue'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import router from '@/router'
import store from '@/store'
import { iframeStore, keepAliveStore, viewTagsStore } from '@/store'
export default {
// 刷新标签
refresh() {
NProgress.start()
const keepAlive = keepAliveStore()
const route = router.currentRoute.value
store.commit('removeKeepLive', route.name)
store.commit('setRouteShow', false)
keepAlive.removeKeepLive(route.name)
keepAlive.setRouteShow(false)
nextTick(() => {
store.commit('pushKeepLive', route.name)
store.commit('setRouteShow', true)
keepAlive.pushKeepLive(route.name)
keepAlive.setRouteShow(true)
NProgress.done()
})
},
// 关闭标签
close(tag) {
const route = tag || router.currentRoute.value
store.commit('removeViewTags', route)
store.commit('removeIframeList', route)
store.commit('removeKeepLive', route.name)
const tagList = store.state.viewTags.viewTags
const store = viewTagsStore()
store.removeViewTags(route)
iframeStore().removeIframeList(route)
keepAliveStore().removeKeepLive(route.name)
const tagList = store.viewTags
const latestView = tagList.slice(-1)[0]
if (latestView) {
router.push(latestView)
@ -44,21 +45,23 @@ export default {
// 关闭标签后处理
closeNext(next) {
const route = router.currentRoute.value
store.commit('removeViewTags', route)
store.commit('removeIframeList', route)
store.commit('removeKeepLive', route.name)
const store = viewTagsStore()
store.removeViewTags(route)
iframeStore().removeIframeList(route)
keepAliveStore().removeKeepLive(route.name)
if (next) {
const tagList = store.state.viewTags.viewTags
const tagList = store.viewTags
next(tagList)
}
},
// 关闭其他
closeOther() {
const route = router.currentRoute.value
const tagList = [...store.state.viewTags.viewTags]
const store = viewTagsStore()
const tagList = [...store.viewTags]
tagList.forEach((tag) => {
// eslint-disable-next-line prettier/prettier
if (tag.meta && tag.meta.affix || route.fullPath == tag.fullPath) {
if ((tag.meta && tag.meta.affix) || route.fullPath == tag.fullPath) {
return true
} else {
this.close(tag)
@ -67,6 +70,6 @@ export default {
},
// 设置标题
setTitle(title) {
store.commit('updateViewTagsTitle', title)
viewTagsStore().updateViewTagsTitle(title)
}
}

View File

@ -108,7 +108,8 @@
import config from '@/config'
import configApi from '@/api/dev/configApi'
import tool from '@/utils/tool'
import store from '@/store'
import { globalStore, iframeStore, keepAliveStore, viewTagsStore } from '@/store'
import { mapActions, mapState } from 'pinia'
export default {
name: 'Login',
@ -119,7 +120,6 @@
data() {
return {
activeKey: 'userAccount',
sysBaseConfig: store.state.global.sysBaseConfig || tool.data.get('SNOWY_SYS_BASE_CONFIG'),
captchaOpen: config.SYS_BASE_CONFIG.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN,
validCodeBase64: '',
ruleForm: {
@ -150,6 +150,9 @@
]
}
},
computed: {
...mapState(globalStore, ['sysBaseConfig']),
},
watch: {
'config.theme': function (val) {
document.body.setAttribute('data-theme', val)
@ -160,9 +163,9 @@
}
},
created() {
store.commit('clearViewTags')
store.commit('clearKeepLive')
store.commit('clearIframeList')
this.clearViewTags()
this.clearKeepLive()
this.clearIframeList()
},
mounted() {
let formData = ref(config.SYS_BASE_CONFIG)
@ -173,12 +176,16 @@
})
this.captchaOpen = formData.value.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN
tool.data.set('SNOWY_SYS_BASE_CONFIG', formData.value)
store.commit('SET_sysBaseConfig', formData.value)
this.setSysBaseConfig(formData.value)
this.refreshSwitch()
}
})
},
methods: {
...mapActions(keepAliveStore, ['clearKeepLive']),
...mapActions(viewTagsStore, ['clearViewTags']),
...mapActions(iframeStore, ['clearIframeList']),
...mapActions(globalStore, ['setSysBaseConfig']),
//
refreshSwitch() {
//

View File

@ -4,21 +4,28 @@ import dictApi from '@/api/dev/dictApi'
import router from '@/router'
import tool from '@/utils/tool'
import { message } from 'ant-design-vue'
import { useGlobalStore } from '@/store'
export const afterLogin = async (loginToken) => {
tool.data.set('TOKEN', loginToken)
// 获取登录的用户信息
const loginUser = await loginApi.getLoginUser()
const globalStore = useGlobalStore()
globalStore.setUserInfo(loginUser)
tool.data.set('USER_INFO', loginUser)
// 获取用户的菜单
const menu = await userCenterApi.userLoginMenu()
const indexMenu = menu[0].children[0].path
let indexMenu = menu[0].children[0].path
tool.data.set('MENU', menu)
// 重置系统默认应用
tool.data.set('SNOWY_MENU_MODULE_ID', menu[0].id)
message.success('登录成功')
router.replace({
if (!!tool.data.get('LAST_VIEWS_PATH')) {
// 如果有缓存,将其登录跳转到最后访问的路由
indexMenu = tool.data.get('LAST_VIEWS_PATH')
}
await router.replace({
path: indexMenu
})
dictApi.dictTree().then((data) => {

View File

@ -12,42 +12,46 @@
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
</a-col>
<a-col :span="19">
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form mb-3" :model="searchFormState">
<a-row :gutter="24">
<a-col :span="8">
<a-form-item name="searchKey" label="字典名称">
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入字典名称" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="$refs.table.refresh(true)">
<template #icon><SearchOutlined /></template>
查询
</a-button>
<a-button class="snowy-buttom-left" @click="reset">
<template #icon><redo-outlined /></template>
重置
</a-button>
</a-col>
</a-row>
</a-form>
<a-divider class="m-3 mx-0" />
<s-table
ref="table"
:columns="columns"
:data="loadData"
:expand-row-by-click="true"
bordered
:tool-config="toolConfig"
:row-key="(record) => record.id"
>
<template #operator class="table-operator">
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
<a-row :gutter="24">
<a-col :span="8">
<a-form-item name="searchKey" label="字典名称">
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入字典名称"></a-input>
</a-form-item>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="$refs.table.refresh(true)">
<template #icon><SearchOutlined /></template>
查询
</a-button>
<a-button class="snowy-buttom-left" @click="reset">
<template #icon><redo-outlined /></template>
重置
</a-button>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="form.onOpen(undefined, 'BIZ', searchFormState.parentId)">
<template #icon><plus-outlined /></template>
新增
</a-button>
</a-col>
</a-row>
</a-form>
<a-button type="primary" @click="form.onOpen(undefined, 'BIZ', searchFormState.parentId)">
<template #icon><plus-outlined /></template>
新增
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'level'">
<a-tag color="blue" v-if="record.level">{{ record.level }}</a-tag>
<a-tag color="green" v-else>子级</a-tag>
</template>
<template v-if="column.dataIndex === 'action'">
<a @click="form.onOpen(record, 'BIZ')">编辑</a>
<a-divider type="vertical" />
@ -70,11 +74,13 @@
const columns = [
{
title: '字典名称',
dataIndex: 'dictLabel'
dataIndex: 'dictLabel',
width: 350
},
{
title: '字典值',
dataIndex: 'dictValue'
dataIndex: 'dictValue',
width: 350
},
{
title: '排序',
@ -97,13 +103,33 @@
const treeData = ref([])
// treeNode title,key,children
const treeFieldNames = { children: 'children', title: 'dictLabel', key: 'id' }
const toolConfig = { refresh: true, height: true, columnSetting: true, striped: false }
// Promise
const loadData = (parameter) => {
loadTreeData()
parameter.category = 'BIZ'
return dictApi.dictPage(Object.assign(parameter, searchFormState)).then((res) => {
return res
return dictApi.dictPage(Object.assign(parameter, searchFormState)).then((data) => {
if (data.records) {
if (searchFormState.parentId) {
let dataArray = []
data.records.forEach((item) => {
const obj = data.records.find((f) => f.id === item.parentId)
if (!obj) {
dataArray.push(item)
}
})
if (dataArray.length === 1) {
data.records.forEach((item) => {
if (item.id === dataArray[0].id) {
item.level = '上级'
}
})
}
dataArray = []
}
}
return data
})
}
//
@ -126,8 +152,16 @@
const treeSelect = (selectedKeys) => {
if (selectedKeys && selectedKeys.length > 0) {
searchFormState.parentId = selectedKeys.toString()
if (!columns.find((f) => f.title === '层级')) {
columns.splice(2, 0, {
title: '层级',
dataIndex: 'level',
width: 100
})
}
} else {
delete searchFormState.parentId
columns.splice(2, 1)
}
table.value.refresh(true)
}

View File

@ -12,44 +12,45 @@
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
</a-col>
<a-col :span="19">
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
<a-row :gutter="24">
<a-col :span="8">
<a-form-item name="searchKey" label="字典名称">
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入字典名称" />
</a-form-item>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="$refs.table.refresh(true)">
<template #icon><SearchOutlined /></template>
查询
</a-button>
<a-button class="snowy-buttom-left" @click="reset">
<template #icon><redo-outlined /></template>
重置
</a-button>
</a-col>
</a-row>
</a-form>
<a-divider class="m-3 mx-0" />
<s-table
ref="table"
:columns="columns"
:data="loadData"
:expand-row-by-click="true"
bordered
:tool-config="toolConfig"
:row-key="(record) => record.id"
>
<template #operator class="table-operator">
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
<a-row :gutter="24">
<a-col :span="8">
<a-form-item name="searchKey" label="字典名称">
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入字典名称"></a-input>
</a-form-item>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="$refs.table.refresh(true)">
<template #icon><SearchOutlined /></template>
查询
</a-button>
<a-button class="snowy-buttom-left" @click="reset">
<template #icon><redo-outlined /></template>
重置
</a-button>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="form.onOpen(undefined, 'FRM', searchFormState.parentId)">
<template #icon><plus-outlined /></template>
新增
</a-button>
</a-col>
</a-row>
</a-form>
<a-button type="primary" @click="form.onOpen(undefined, 'FRM', searchFormState.parentId)">
<template #icon><plus-outlined /></template>
新增
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'category'">
{{ $TOOL.dictTypeData('DICT_CATEGORY', record.category) }}
<template v-if="column.dataIndex === 'level'">
<a-tag color="blue" v-if="record.level">{{ record.level }}</a-tag>
<a-tag color="green" v-else>子级</a-tag>
</template>
<template v-if="column.dataIndex === 'action'">
<a @click="form.onOpen(record, 'FRM')">编辑</a>
@ -66,14 +67,17 @@
import dictApi from '@/api/dev/dictApi'
import Form from './form.vue'
const { proxy } = getCurrentInstance()
let searchFormState = reactive({})
const columns = [
{
title: '字典名称',
dataIndex: 'dictLabel'
dataIndex: 'dictLabel',
width: 350
},
{
title: '字典值',
dataIndex: 'dictValue'
dataIndex: 'dictValue',
width: 350
},
{
title: '排序',
@ -90,19 +94,38 @@
const table = ref(null)
const form = ref()
const searchFormRef = ref()
let searchFormState = reactive({})
//
let defaultExpandedKeys = ref([])
const treeData = ref([])
// treeNode title,key,children
const treeFieldNames = { children: 'children', title: 'dictLabel', key: 'id' }
const toolConfig = { refresh: true, height: true, columnSetting: true, striped: false }
// Promise
const loadData = (parameter) => {
loadTreeData()
parameter.category = 'FRM'
return dictApi.dictPage(Object.assign(parameter, searchFormState)).then((res) => {
return res
return dictApi.dictPage(Object.assign(parameter, searchFormState)).then((data) => {
if (data.records) {
if (searchFormState.parentId) {
let dataArray = []
data.records.forEach((item) => {
const obj = data.records.find((f) => f.id === item.parentId)
if (!obj) {
dataArray.push(item)
}
})
if (dataArray.length === 1) {
data.records.forEach((item) => {
if (item.id === dataArray[0].id) {
item.level = '上级'
}
})
}
dataArray = []
}
}
return data
})
}
//
@ -125,8 +148,16 @@
const treeSelect = (selectedKeys) => {
if (selectedKeys && selectedKeys.length > 0) {
searchFormState.parentId = selectedKeys.toString()
if (!columns.find((f) => f.title === '层级')) {
columns.splice(2, 0, {
title: '层级',
dataIndex: 'level',
width: 100
})
}
} else {
delete searchFormState.parentId
columns.splice(2, 1)
}
table.value.refresh(true)
}

View File

@ -25,7 +25,7 @@
//
const displayMore = () => {
return userInfo.roleCodeList && userInfo.roleCodeList.indexOf('super') !== -1
return userInfo.roleCodeList && userInfo.roleCodeList.toString().indexOf('superAdmin') !== -1
}
const seleOpLogList = () => {
indexApi.indexOpLogList().then((data) => {

View File

@ -25,7 +25,7 @@
})
//
const displayMore = () => {
return userInfo.roleCodeList && userInfo.roleCodeList.indexOf('super') !== -1
return userInfo.roleCodeList && userInfo.roleCodeList.toString().indexOf('superAdmin') !== -1
}
//
const seleVisLogList = () => {

View File

@ -7,6 +7,7 @@
:show-pagination="false"
@close="onClose"
>
<a-alert message="非超管角色不可被授权系统模块菜单资源" banner class="mb-2" />
<a-spin :spinning="spinningLoading">
<a-radio-group v-model:value="moduleId" button-style="solid" style="padding-bottom: 10px">
<a-radio-button

View File

@ -7,6 +7,7 @@
:show-pagination="false"
@close="onClose"
>
<a-alert message="非超管角色用户不可被授权系统模块菜单资源" banner class="mb-2" />
<a-spin :spinning="spinningLoading">
<a-radio-group v-model:value="moduleId" button-style="solid" style="padding-bottom: 10px">
<a-radio-button

View File

@ -1,11 +1,5 @@
<template>
<xn-form-container
title="导入导出"
:width="700"
:visible="visible"
:destroy-on-close="true"
@close="onClose"
>
<xn-form-container title="导入导出" :width="700" :visible="visible" :destroy-on-close="true" @close="onClose">
<span
>导入数据格式严格按照系统模板进行数据录入请点击
<a-button type="primary" size="small" @click="downloadImportUserTemplate">下载模板</a-button>

View File

@ -66,7 +66,7 @@
import { onMounted } from 'vue'
import { useRoute } from 'vue-router'
import tool from '@/utils/tool'
import store from '@/store'
import { globalStore } from '@/store'
import userCenterApi from '@/api/sys/userCenterApi'
import accountBasic from './userTab/accountBasic.vue'
import CropUpload from '@/components/CropUpload/index.vue'
@ -75,6 +75,8 @@
import accountBind from './userTab/accountBind.vue'
import userMessage from './userTab/userMessage.vue'
const global_store = globalStore()
const userInfo = ref(tool.data.get('USER_INFO'))
const cropUpload = ref()
const avatarLoading = ref(false)
@ -126,7 +128,7 @@
userInfo.value.avatar = data
//
tool.data.set('USER_INFO', userInfo.value)
store.commit('SET_userInfo', userInfo.value)
global_store.setUserInfo(userInfo.value)
})
}
//
@ -138,7 +140,7 @@
userInfo.value.signature = value
//
tool.data.set('USER_INFO', userInfo.value)
store.commit('SET_userInfo', userInfo.value)
global_store.setUserInfo(userInfo.value)
})
}
</script>

View File

@ -39,7 +39,10 @@
import { required } from '@/utils/formRules'
import userCenterApi from '@/api/sys/userCenterApi'
import tool from '@/utils/tool'
import store from '@/store'
import { globalStore } from '@/store'
const store = globalStore()
const formRef = ref()
//
const userInfo = tool.data.get('USER_INFO')
@ -61,7 +64,7 @@
userCenterApi.userUpdateUserInfo(formData.value).then(() => {
submitLoading.value = false
//
store.commit('SET_userInfo', formData.value)
store.setUserInfo(formData.value)
tool.data.set('USER_INFO', formData.value)
})
})

View File

@ -1,11 +1,5 @@
<template>
<xn-form-container
title="修改密码"
:width="550"
:visible="visible"
:destroy-on-close="true"
@close="onClose"
>
<xn-form-container title="修改密码" :width="550" :visible="visible" :destroy-on-close="true" @close="onClose">
<a-form ref="formRef" :model="formState" :rules="rules" layout="vertical">
<a-form-item label="旧密码:" name="password" has-feedback>
<a-input

View File

@ -1,11 +1,5 @@
<template>
<xn-form-container
title="详情"
:width="700"
:visible="visible"
:destroy-on-close="true"
@close="onClose"
>
<xn-form-container title="详情" :width="700" :visible="visible" :destroy-on-close="true" @close="onClose">
<a-descriptions :column="1" size="middle" bordered class="mb-2">
<a-descriptions-item label="主题">{{ formData.subject }}</a-descriptions-item>
<a-descriptions-item label="发送时间">{{ formData.createTime }}</a-descriptions-item>

View File

@ -28,6 +28,16 @@ const generateFontSize = () => {
const colors = require('tailwindcss/colors')
const filterWarnColors = (colors) => {
const result = {}
for (const key in colors) {
if (['lightBlue', 'warmGray', 'trueGray', 'coolGray', 'blueGray'].indexOf(key) === -1) {
result[key] = colors[key]
}
}
return result
}
module.exports = {
content: ['./src/**/*.vue', './src/**/*.js'],
darkMode: 'class', // or 'media' or 'class'
@ -39,12 +49,7 @@ module.exports = {
colors: {
transparent: 'transparent',
current: 'currentColor',
black: colors.black,
white: colors.white,
gray: colors.neutral,
indigo: colors.indigo,
red: colors.rose,
yellow: colors.amber,
...filterWarnColors(colors),
...generatePrimaryColors()
},
fontWeight: {

View File

@ -78,7 +78,7 @@ export default defineConfig(({ command, mode }) => {
manualChunks: {
echarts: ['echarts'],
'ant-design-vue': ['ant-design-vue'],
vue: ['vue', 'vue-router', 'vuex', 'vue-i18n']
vue: ['vue', 'vue-router', 'pinia', 'vue-i18n']
}
}
},

View File

@ -16,6 +16,7 @@ import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import vip.xiaonuo.common.util.CommonServletUtil;
import java.util.List;
@ -26,6 +27,7 @@ import java.util.List;
* @author xuyuxiang
* @date 2021/12/18 14:43
*/
@Slf4j
public class CommonPageRequest {
private static final String PAGE_SIZE_PARAM_NAME = "size";
@ -53,7 +55,7 @@ public class CommonPageRequest {
size = PAGE_SIZE_MAX_VALUE;
}
} catch (Exception e) {
e.printStackTrace();
log.error(">>> 分页条数转换异常:", e);
size = 20;
}
}
@ -64,7 +66,7 @@ public class CommonPageRequest {
try {
page = Convert.toInt(pageString);
} catch (Exception e) {
e.printStackTrace();
log.error(">>> 分页页数转换异常:", e);
page = 1;
}
}

View File

@ -15,6 +15,7 @@ package vip.xiaonuo.common.util;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.URLUtil;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
@ -26,6 +27,7 @@ import java.io.IOException;
* @author xuyuxiang
* @date 2020/8/5 21:45
*/
@Slf4j
public class CommonDownloadUtil {
/**
@ -55,7 +57,7 @@ public class CommonDownloadUtil {
response.setContentType("application/octet-stream;charset=UTF-8");
IoUtil.write(response.getOutputStream(), true, fileBytes);
} catch (IOException e) {
e.printStackTrace();
log.error(">>> 文件下载异常:", e);
}
}
}

View File

@ -56,16 +56,16 @@ public class CommonIpAddressUtil {
try {
cBuff = Searcher.loadContentFromFile(dbPath);
} catch (Exception e) {
e.printStackTrace();
throw new CommonException("CommonIpAddressUtil初始化失败,原因:", e.getMessage());
log.error(">>> CommonIpAddressUtil初始化异常", e);
throw new CommonException("CommonIpAddressUtil初始化异常");
}
// 2使用上述的 cBuff 创建一个完全基于内存的查询对象
try {
searcher = Searcher.newWithBuffer(cBuff);
} catch (Exception e) {
e.printStackTrace();
throw new CommonException("CommonIpAddressUtil初始化失败,原因:", e.getMessage());
log.error(">>> CommonIpAddressUtil初始化异常", e);
throw new CommonException("CommonIpAddressUtil初始化异常");
}
}
@ -83,7 +83,7 @@ public class CommonIpAddressUtil {
String remoteHost = ServletUtil.getClientIP(request);
return LOCAL_REMOTE_HOST.equals(remoteHost) ? Ipv4Util.LOCAL_IP : remoteHost;
} catch (Exception e) {
e.printStackTrace();
log.error(">>> 获取客户端ip异常", e);
return Ipv4Util.LOCAL_IP;
}
}

View File

@ -67,14 +67,13 @@ public class CommonNetWorkInfoUtil {
result.put("UP", upSpeed + (upSpeed.endsWith("B")?"/S":"B/S"));
result.put("DOWN", downSpeed + (downSpeed.endsWith("B")?"/S":"B/S"));
} catch (Exception e) {
log.info(">>> 网络测速失败,原因:");
e.printStackTrace();
log.info(">>> 网络测速失败:", e);
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
log.info(">>> 网络测速失败:", e);
}
}
Optional.ofNullable(pro).ifPresent(Process::destroy);
@ -119,7 +118,7 @@ public class CommonNetWorkInfoUtil {
arr[1] = tx;
}
} catch (Exception e) {
e.printStackTrace();
log.error(">>> 网络测速异常:", e);
}
return arr;
}

View File

@ -13,6 +13,7 @@
package vip.xiaonuo.common.util;
import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import vip.xiaonuo.common.exception.CommonException;
@ -27,6 +28,7 @@ import javax.servlet.http.HttpServletResponse;
* @author xuyuxiang
* @date 2020/3/30 15:09
*/
@Slf4j
public class CommonServletUtil {
/**
@ -66,7 +68,7 @@ public class CommonServletUtil {
try {
servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
} catch (Exception e) {
e.printStackTrace();
log.error(">>> 非Web上下文无法获取Request", e);
throw new CommonException("非Web上下文无法获取Request");
}
if (servletRequestAttributes == null) {
@ -81,7 +83,7 @@ public class CommonServletUtil {
try {
servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
} catch (Exception e) {
e.printStackTrace();
log.error(">>> 非Web上下文无法获取Response", e);
throw new CommonException("非Web上下文无法获取Response");
}
if (servletRequestAttributes == null) {

View File

@ -58,11 +58,10 @@ public class AuthExceptionUtil {
commonResult = CommonResult.error(saTokenException.getMessage());
} else {
// 未知异常才打印
e.printStackTrace();
log.error(">>> 服务器未知异常:{},请求地址:{}", e.getMessage(), SaHolder.getRequest().getUrl());
// 未知异常返回服务器异常此处不可能执行进入因为本方法处理的一定是SaToken的异常此处仅为安全性考虑
commonResult = CommonResult.error("服务器异常");
}
log.error(">>> {},请求地址:{}", commonResult.getMsg(), SaHolder.getRequest().getUrl());
return commonResult;
}
}

View File

@ -21,6 +21,7 @@ import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.springframework.stereotype.Service;
@ -41,6 +42,7 @@ import vip.xiaonuo.auth.modular.login.service.AuthService;
import vip.xiaonuo.common.cache.CommonCacheOperator;
import vip.xiaonuo.common.exception.CommonException;
import vip.xiaonuo.common.util.CommonCryptogramUtil;
import vip.xiaonuo.common.util.CommonEmailUtil;
import vip.xiaonuo.dev.api.DevConfigApi;
import vip.xiaonuo.dev.api.DevSmsApi;
@ -59,7 +61,7 @@ public class AuthServiceImpl implements AuthService {
private static final String SNOWY_SYS_DEFAULT_CAPTCHA_OPEN_KEY = "SNOWY_SYS_DEFAULT_CAPTCHA_OPEN";
private static final String AUTH_CACHE_KEY = "auth-validCode:";
private static final String AUTH_VALID_CODE_CACHE_KEY = "auth-validCode:";
@Resource(name = "loginUserApi")
private SaBaseLoginUserApi loginUserApi;
@ -93,7 +95,7 @@ public class AuthServiceImpl implements AuthService {
// 将请求号返回前端
authPicValidCodeResult.setValidCodeReqNo(validCodeReqNo);
// 将请求号作为key验证码的值作为value放到redis用于校验5分钟有效
commonCacheOperator.put(AUTH_CACHE_KEY + validCodeReqNo, validCode, 5 * 60);
commonCacheOperator.put(AUTH_VALID_CODE_CACHE_KEY + validCodeReqNo, validCode, 5 * 60);
return authPicValidCodeResult;
}
@ -106,7 +108,7 @@ public class AuthServiceImpl implements AuthService {
// 验证码请求号
String validCodeReqNo = authGetPhoneValidCodeParam.getValidCodeReqNo();
// 校验参数
validPhoneValidCodeParam(phone, validCode, validCodeReqNo, type);
validPhoneValidCodeParam(null, validCode, validCodeReqNo, type);
// 生成手机验证码的值随机6为数字
String phoneValidCode = RandomUtil.randomNumbers(6);
// 生成手机验证码的请求号
@ -118,10 +120,10 @@ public class AuthServiceImpl implements AuthService {
// TODO 使用腾讯云执行发送验证码将验证码作为短信内容的参数变量放入
// TODO sdkAppId和签名不传则使用系统默认配置的sdkAppId和签名支持传入多个参数逗号拼接示例"张三,15038****76,进行中"
devSmsApi.sendSmsTencent("1400522364", phone, "小诺开源技术", "1502357", phoneValidCode);
devSmsApi.sendSmsTencent("sdkAppId", phone, "签名", "模板编码", phoneValidCode);
// 将请求号作为key验证码的值作为value放到redis用于校验5分钟有效
commonCacheOperator.put(AUTH_CACHE_KEY + phoneValidCodeReqNo, phoneValidCode, 5 * 60);
commonCacheOperator.put(AUTH_VALID_CODE_CACHE_KEY + phone + StrUtil.UNDERLINE + phoneValidCodeReqNo, phoneValidCode, 5 * 60);
// 返回请求号
return phoneValidCodeReqNo;
}
@ -132,21 +134,28 @@ public class AuthServiceImpl implements AuthService {
* @author xuyuxiang
* @date 2022/8/25 15:26
**/
private void validValidCode(String validCode, String validCodeReqNo) {
private void validValidCode(String phoneOrEmail, String validCode, String validCodeReqNo) {
// 依据请求号取出缓存中的验证码进行校验
Object existValidCode = commonCacheOperator.get(AUTH_CACHE_KEY + validCodeReqNo);
Object existValidCode;
if(ObjectUtil.isEmpty(phoneOrEmail)) {
existValidCode = commonCacheOperator.get(AUTH_VALID_CODE_CACHE_KEY + validCodeReqNo);
} else {
existValidCode = commonCacheOperator.get(AUTH_VALID_CODE_CACHE_KEY + phoneOrEmail + StrUtil.UNDERLINE + validCodeReqNo);
}
// 为空则直接验证码错误
if(ObjectUtil.isEmpty(existValidCode)) {
throw new CommonException(AuthExceptionEnum.VALID_CODE_ERROR.getValue());
}
// 不一致则直接验证码错误
if(!validCode.equals(Convert.toStr(existValidCode).toLowerCase())) {
// 移除该验证码
commonCacheOperator.remove(AUTH_CACHE_KEY + validCodeReqNo);
throw new CommonException(AuthExceptionEnum.VALID_CODE_ERROR.getValue());
}
// 移除该验证码
commonCacheOperator.remove(AUTH_CACHE_KEY + validCodeReqNo);
if(ObjectUtil.isEmpty(phoneOrEmail)) {
commonCacheOperator.remove(AUTH_VALID_CODE_CACHE_KEY + validCodeReqNo);
} else {
commonCacheOperator.remove(AUTH_VALID_CODE_CACHE_KEY + phoneOrEmail + StrUtil.UNDERLINE + validCodeReqNo);
}
// 不一致则直接验证码错误
if (!validCode.equals(Convert.toStr(existValidCode).toLowerCase())) {
throw new CommonException("验证码错误");
}
}
/**
@ -155,20 +164,25 @@ public class AuthServiceImpl implements AuthService {
* @author xuyuxiang
* @date 2022/8/25 14:29
**/
private void validPhoneValidCodeParam(String phone, String validCode, String validCodeReqNo, String type) {
private void validPhoneValidCodeParam(String phoneOrEmail, String validCode, String validCodeReqNo, String type) {
// 验证码正确则校验手机号格式
if(!PhoneUtil.isMobile(phone)) {
throw new CommonException(AuthExceptionEnum.PHONE_FORMAT_ERROR.getValue());
if(ObjectUtil.isEmpty(phoneOrEmail)) {
// 执行校验验证码
validValidCode(null, validCode, validCodeReqNo);
} else {
if(!PhoneUtil.isMobile(phoneOrEmail) && !CommonEmailUtil.isEmail(phoneOrEmail)) {
throw new CommonException(AuthExceptionEnum.PHONE_FORMAT_ERROR.getValue());
}
// 执行校验验证码
validValidCode(phoneOrEmail, validCode, validCodeReqNo);
}
// 执行校验验证码
validValidCode(validCode, validCodeReqNo);
// 根据手机号获取用户信息判断用户是否存在根据B端或C端判断
if(SaClientTypeEnum.B.getValue().equals(type)) {
if(ObjectUtil.isEmpty(loginUserApi.getUserByPhone(phone))) {
if(ObjectUtil.isEmpty(loginUserApi.getUserByPhone(phoneOrEmail))) {
throw new CommonException(AuthExceptionEnum.PHONE_ERROR.getValue());
}
} else {
if(ObjectUtil.isEmpty(clientLoginUserApi.getClientUserByPhone(phone))) {
if(ObjectUtil.isEmpty(clientLoginUserApi.getClientUserByPhone(phoneOrEmail))) {
throw new CommonException(AuthExceptionEnum.PHONE_ERROR.getValue());
}
}
@ -205,7 +219,7 @@ public class AuthServiceImpl implements AuthService {
throw new CommonException(AuthExceptionEnum.VALID_CODE_REQ_NO_EMPTY.getValue());
}
// 执行校验验证码
validValidCode(validCode, validCodeReqNo);
validValidCode(null, validCode, validCodeReqNo);
}
}
// SM2解密并获得前端传来的密码哈希值

View File

@ -192,7 +192,7 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
}
if(ObjectUtil.isNotEmpty(bizUserAddParam.getEmail())) {
if(!CommonEmailUtil.isEmail(bizUserAddParam.getEmail())) {
throw new CommonException("邮箱:{}格式错误", bizUserAddParam.getPhone());
throw new CommonException("邮箱:{}格式错误", bizUserAddParam.getEmail());
}
if (this.count(new LambdaQueryWrapper<BizUser>()
.eq(BizUser::getEmail, bizUserAddParam.getEmail())) > 0) {
@ -247,7 +247,7 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
}
if(ObjectUtil.isNotEmpty(bizUserEditParam.getEmail())) {
if(!CommonEmailUtil.isEmail(bizUserEditParam.getEmail())) {
throw new CommonException("邮箱:{}格式错误", bizUserEditParam.getPhone());
throw new CommonException("邮箱:{}格式错误", bizUserEditParam.getEmail());
}
if (this.count(new LambdaQueryWrapper<BizUser>()
.eq(BizUser::getEmail, bizUserEditParam.getEmail())
@ -528,7 +528,7 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
.doWrite(bizUserExportResultList);
CommonDownloadUtil.download(tempFile, response);
} catch (Exception e) {
e.printStackTrace();
log.error(">>> 人员导出异常:", e);
CommonResponseUtil.renderError(response, "导出失败");
} finally {
FileUtil.del(tempFile);
@ -582,7 +582,7 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
// 下载
CommonDownloadUtil.download(resultFile, response);
} catch (Exception e) {
e.printStackTrace();
log.error(">>> 导出人员个人信息异常:", e);
CommonResponseUtil.renderError(response, "导出失败");
} finally {
// 删除临时文件

View File

@ -153,7 +153,7 @@ public class ClientUserServiceImpl extends ServiceImpl<ClientUserMapper, ClientU
}
if(ObjectUtil.isNotEmpty(clientUserAddParam.getEmail())) {
if(!CommonEmailUtil.isEmail(clientUserAddParam.getEmail())) {
throw new CommonException("邮箱:{}格式错误", clientUserAddParam.getPhone());
throw new CommonException("邮箱:{}格式错误", clientUserAddParam.getEmail());
}
if (this.count(new LambdaQueryWrapper<ClientUser>()
.eq(ClientUser::getEmail, clientUserAddParam.getEmail())) > 0) {
@ -188,7 +188,7 @@ public class ClientUserServiceImpl extends ServiceImpl<ClientUserMapper, ClientU
}
if(ObjectUtil.isNotEmpty(clientUserEditParam.getEmail())) {
if(!CommonEmailUtil.isEmail(clientUserEditParam.getEmail())) {
throw new CommonException("邮箱:{}格式错误", clientUserEditParam.getPhone());
throw new CommonException("邮箱:{}格式错误", clientUserEditParam.getEmail());
}
if (this.count(new LambdaQueryWrapper<ClientUser>()
.eq(ClientUser::getEmail, clientUserEditParam.getEmail())

View File

@ -63,7 +63,8 @@ public class DevDictServiceImpl extends ServiceImpl<DevDictMapper, DevDict> impl
queryWrapper.lambda().select(DevDict::getId, DevDict::getParentId, DevDict::getCategory, DevDict::getDictLabel,
DevDict::getDictValue, DevDict::getSortCode);
if (ObjectUtil.isNotEmpty(devDictPageParam.getParentId())) {
queryWrapper.lambda().eq(DevDict::getParentId, devDictPageParam.getParentId());
queryWrapper.lambda().eq(DevDict::getParentId, devDictPageParam.getParentId())
.or().eq(DevDict::getId, devDictPageParam.getParentId());
}
if (ObjectUtil.isNotEmpty(devDictPageParam.getCategory())) {
queryWrapper.lambda().eq(DevDict::getCategory, devDictPageParam.getCategory());

View File

@ -400,7 +400,7 @@ public class GenBasicServiceImpl extends ServiceImpl<GenBasicMapper, GenBasic> i
// 移动完毕删除临时目录
FileUtil.del(tempFolder);
} catch (Exception e) {
e.printStackTrace();
log.error(">>> 代码生成异常:", e);
throw new CommonException("代码生成异常");
}
}
@ -517,7 +517,7 @@ public class GenBasicServiceImpl extends ServiceImpl<GenBasicMapper, GenBasic> i
});
genBasicPreviewResult.setGenBasicCodeBackendResultList(genBasicCodeBackendResultList);
} catch (Exception e) {
e.printStackTrace();
log.error(">>> 代码生成异常:", e);
throw new CommonException("代码生成异常");
}
return genBasicPreviewResult;

View File

@ -14,6 +14,7 @@ package vip.xiaonuo.sys.modular.resource.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
@ -48,6 +49,10 @@ import vip.xiaonuo.sys.modular.resource.service.SysModuleService;
import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
@ -97,6 +102,10 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
lambdaQueryWrapper.like(SysMenu::getTitle, sysMenuTreeParam.getSearchKey());
}
List<SysMenu> resourceList = this.list(lambdaQueryWrapper);
// 填充上层的父级菜单
this.fillParentSysMenuInfo(resourceList);
List<TreeNode<String>> treeNodeList = resourceList.stream().map(sysMenu ->
new TreeNode<>(sysMenu.getId(), sysMenu.getParentId(),
sysMenu.getTitle(), sysMenu.getSortCode()).setExtra(JSONUtil.parseObj(sysMenu)))
@ -104,6 +113,36 @@ public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> impl
return TreeUtil.build(treeNodeList, "0");
}
private void fillParentSysMenuInfo(List<SysMenu> resourceList) {
if(CollUtil.isNotEmpty(resourceList)){
List<SysMenu> parentRelationSysMenus = resourceList.stream().filter(distinctByKey(SysMenu::getParentId)).collect(Collectors.toList());
List<String> parentIds = null;
if(CollUtil.isNotEmpty(parentRelationSysMenus)){
parentIds = CollUtil.newArrayList();
for(SysMenu parentRelationSysMenu : parentRelationSysMenus){
if(!StrUtil.equals(parentRelationSysMenu.getParentId(),"0")){
parentIds.add(parentRelationSysMenu.getParentId());
}
}
}
if(CollUtil.isNotEmpty(parentIds)){
LambdaQueryWrapper<SysMenu> parentSysMenuLambdaQueryWrapper = new LambdaQueryWrapper<>();
parentSysMenuLambdaQueryWrapper.in(SysMenu::getId,parentIds);
List<SysMenu> parentSysMenus = this.list(parentSysMenuLambdaQueryWrapper);
if(CollUtil.isNotEmpty(parentSysMenus)){
this.fillParentSysMenuInfo(parentSysMenus);
resourceList.addAll(parentSysMenus);
}
}
}
}
private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> seen = ConcurrentHashMap.newKeySet();
return t -> seen.add(keyExtractor.apply(t));
}
@Transactional(rollbackFor = Exception.class)
@Override
public void add(SysMenuAddParam sysMenuAddParam) {

View File

@ -48,8 +48,10 @@ import vip.xiaonuo.sys.modular.relation.entity.SysRelation;
import vip.xiaonuo.sys.modular.relation.enums.SysRelationCategoryEnum;
import vip.xiaonuo.sys.modular.relation.service.SysRelationService;
import vip.xiaonuo.sys.modular.resource.entity.SysMenu;
import vip.xiaonuo.sys.modular.resource.entity.SysModule;
import vip.xiaonuo.sys.modular.resource.enums.SysResourceCategoryEnum;
import vip.xiaonuo.sys.modular.resource.service.SysMenuService;
import vip.xiaonuo.sys.modular.resource.service.SysModuleService;
import vip.xiaonuo.sys.modular.role.entity.SysRole;
import vip.xiaonuo.sys.modular.role.enums.SysRoleCategoryEnum;
import vip.xiaonuo.sys.modular.role.mapper.SysRoleMapper;
@ -63,6 +65,7 @@ import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
@ -80,6 +83,9 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
@Resource
private SysOrgService sysOrgService;
@Resource
private SysModuleService sysModuleService;
@Resource
private SysMenuService sysMenuService;
@ -218,8 +224,19 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
@Override
public void grantResource(SysRoleGrantResourceParam sysRoleGrantResourceParam) {
String id = sysRoleGrantResourceParam.getId();
SysRole sysRole = this.queryEntity(id);
List<String> menuIdList = sysRoleGrantResourceParam.getGrantInfoList().stream()
.map(SysRoleGrantResourceParam.SysRoleGrantResource::getMenuId).collect(Collectors.toList());
if(!SysBuildInEnum.BUILD_IN_ROLE_CODE.getValue().equals(sysRole.getCode())) {
if(ObjectUtil.isNotEmpty(menuIdList)) {
Set<String> sysModuleIdList = sysMenuService.listByIds(menuIdList).stream().map(SysMenu::getModule).collect(Collectors.toSet());
boolean containsSystemModule = sysModuleService.listByIds(sysModuleIdList).stream().map(SysModule::getCode)
.collect(Collectors.toSet()).contains(SysBuildInEnum.BUILD_IN_MODULE_CODE.getValue());
if(containsSystemModule) {
throw new CommonException("非超管角色不可被授权系统模块菜单资源");
}
}
}
List<String> extJsonList = sysRoleGrantResourceParam.getGrantInfoList().stream()
.map(JSONUtil::toJsonStr).collect(Collectors.toList());
sysRelationService.saveRelationBatchWithClear(id, menuIdList, SysRelationCategoryEnum.SYS_ROLE_HAS_RESOURCE.getValue(),

View File

@ -85,10 +85,12 @@ import vip.xiaonuo.sys.modular.relation.enums.SysRelationCategoryEnum;
import vip.xiaonuo.sys.modular.relation.service.SysRelationService;
import vip.xiaonuo.sys.modular.resource.entity.SysButton;
import vip.xiaonuo.sys.modular.resource.entity.SysMenu;
import vip.xiaonuo.sys.modular.resource.entity.SysModule;
import vip.xiaonuo.sys.modular.resource.enums.SysResourceCategoryEnum;
import vip.xiaonuo.sys.modular.resource.enums.SysResourceMenuTypeEnum;
import vip.xiaonuo.sys.modular.resource.service.SysButtonService;
import vip.xiaonuo.sys.modular.resource.service.SysMenuService;
import vip.xiaonuo.sys.modular.resource.service.SysModuleService;
import vip.xiaonuo.sys.modular.role.entity.SysRole;
import vip.xiaonuo.sys.modular.role.enums.SysRoleDataScopeCategoryEnum;
import vip.xiaonuo.sys.modular.role.service.SysRoleService;
@ -121,7 +123,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
private static final String SNOWY_SYS_DEFAULT_WORKBENCH_DATA_KEY = "SNOWY_SYS_DEFAULT_WORKBENCH_DATA";
private static final String USER_CACHE_KEY = "user-validCode:";
private static final String USER_VALID_CODE_CACHE_KEY = "user-validCode:";
public static final String USER_CACHE_ALL_KEY = "sys-user:all";
@ -155,6 +157,9 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
@Resource
private SysRoleService sysRoleService;
@Resource
private SysModuleService sysModuleService;
@Resource
private SysMenuService sysMenuService;
@ -265,7 +270,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
}
if (ObjectUtil.isNotEmpty(sysUserAddParam.getEmail())) {
if (!CommonEmailUtil.isEmail(sysUserAddParam.getEmail())) {
throw new CommonException("邮箱:{}格式错误", sysUserAddParam.getPhone());
throw new CommonException("邮箱:{}格式错误", sysUserAddParam.getEmail());
}
if (this.count(new LambdaQueryWrapper<SysUser>()
.eq(SysUser::getEmail, sysUserAddParam.getEmail())) > 0) {
@ -309,7 +314,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
}
if (ObjectUtil.isNotEmpty(sysUserEditParam.getEmail())) {
if (!CommonEmailUtil.isEmail(sysUserEditParam.getEmail())) {
throw new CommonException("邮箱:{}格式错误", sysUserEditParam.getPhone());
throw new CommonException("邮箱:{}格式错误", sysUserEditParam.getEmail());
}
if (this.count(new LambdaQueryWrapper<SysUser>()
.eq(SysUser::getEmail, sysUserEditParam.getEmail())
@ -399,7 +404,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
// 将请求号返回前端
sysUserPicValidCodeResult.setValidCodeReqNo(validCodeReqNo);
// 将请求号作为key验证码的值作为value放到redis用于校验5分钟有效
commonCacheOperator.put(USER_CACHE_KEY + validCodeReqNo, validCode, 5 * 60);
commonCacheOperator.put(USER_VALID_CODE_CACHE_KEY + validCodeReqNo, validCode, 5 * 60);
return sysUserPicValidCodeResult;
}
@ -409,21 +414,28 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
* @author xuyuxiang
* @date 2022/8/25 14:29
**/
private void validValidCode(String validCode, String validCodeReqNo) {
private void validValidCode(String phoneOrEmail, String validCode, String validCodeReqNo) {
// 依据请求号取出缓存中的验证码进行校验
Object existValidCode = commonCacheOperator.get(USER_CACHE_KEY + validCodeReqNo);
Object existValidCode;
if(ObjectUtil.isEmpty(phoneOrEmail)) {
existValidCode = commonCacheOperator.get(USER_VALID_CODE_CACHE_KEY + validCodeReqNo);
} else {
existValidCode = commonCacheOperator.get(USER_VALID_CODE_CACHE_KEY + phoneOrEmail + StrUtil.UNDERLINE + validCodeReqNo);
}
// 为空则直接验证码错误
if (ObjectUtil.isEmpty(existValidCode)) {
throw new CommonException("验证码错误");
}
// 移除该验证码
if(ObjectUtil.isEmpty(phoneOrEmail)) {
commonCacheOperator.remove(USER_VALID_CODE_CACHE_KEY + validCodeReqNo);
} else {
commonCacheOperator.remove(USER_VALID_CODE_CACHE_KEY + phoneOrEmail + StrUtil.UNDERLINE + validCodeReqNo);
}
// 不一致则直接验证码错误
if (!validCode.equals(Convert.toStr(existValidCode).toLowerCase())) {
// 移除该验证码
commonCacheOperator.remove(USER_CACHE_KEY + validCodeReqNo);
throw new CommonException("验证码错误");
}
// 移除该验证码
commonCacheOperator.remove(USER_CACHE_KEY + validCodeReqNo);
}
@Override
@ -435,7 +447,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
throw new CommonException("手机号码:{}格式错误", phone);
}
// 执行校验验证码
validValidCode(sysUserGetPhoneValidCodeParam.getValidCode(), sysUserGetPhoneValidCodeParam.getValidCodeReqNo());
validValidCode(null, sysUserGetPhoneValidCodeParam.getValidCode(), sysUserGetPhoneValidCodeParam.getValidCodeReqNo());
// 根据手机号获取用户信息判断用户是否存在
if (ObjectUtil.isEmpty(this.getUserByPhone(phone))) {
throw new CommonException("手机码:{}不存在", phone);
@ -454,7 +466,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
//devSmsApi.sendSmsTencent(null, phone, null, "验证码模板号", phoneValidCode);
// 将请求号作为key验证码的值作为value放到redis用于校验5分钟有效
commonCacheOperator.put(USER_CACHE_KEY + phoneValidCodeReqNo, phoneValidCode, 5 * 60);
commonCacheOperator.put(USER_VALID_CODE_CACHE_KEY + phone + StrUtil.UNDERLINE + phoneValidCodeReqNo, phoneValidCode, 5 * 60);
// 返回请求号
return phoneValidCodeReqNo;
}
@ -463,12 +475,12 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
public String findPasswordGetEmailValidCode(SysUserGetEmailValidCodeParam sysUserGetEmailValidCodeParam) {
// 邮箱
String email = sysUserGetEmailValidCodeParam.getEmail();
// 验证码正确则校验手机号格式
// 验证码正确则校验邮箱格式
if (!CommonEmailUtil.isEmail(email)) {
throw new CommonException("邮箱:{}格式错误", email);
}
// 执行校验验证码
validValidCode(sysUserGetEmailValidCodeParam.getValidCode(), sysUserGetEmailValidCodeParam.getValidCodeReqNo());
validValidCode(null, sysUserGetEmailValidCodeParam.getValidCode(), sysUserGetEmailValidCodeParam.getValidCodeReqNo());
// 根据邮箱获取用户信息判断用户是否存在
if (ObjectUtil.isEmpty(this.getUserByEmail(email))) {
throw new CommonException("邮箱:{}不存在", email);
@ -483,7 +495,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
devEmailApi.sendTextEmailLocal(email, "找回密码邮件", content, CollectionUtil.newArrayList());
// 将请求号作为key验证码的值作为value放到redis用于校验5分钟有效
commonCacheOperator.put(USER_CACHE_KEY + emailValidCodeReqNo, emailValidCode, 5 * 60);
commonCacheOperator.put(USER_VALID_CODE_CACHE_KEY + email + StrUtil.UNDERLINE + emailValidCodeReqNo, emailValidCode, 5 * 60);
// 返回请求号
return emailValidCodeReqNo;
}
@ -491,7 +503,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
@Override
public void findPasswordByPhone(SysUserFindPwdByPhoneParam sysUserFindPwdByPhoneParam) {
// 执行校验验证码
validValidCode(sysUserFindPwdByPhoneParam.getValidCode(), sysUserFindPwdByPhoneParam.getValidCodeReqNo());
validValidCode(sysUserFindPwdByPhoneParam.getPhone(), sysUserFindPwdByPhoneParam.getValidCode(), sysUserFindPwdByPhoneParam.getValidCodeReqNo());
this.update(new LambdaUpdateWrapper<SysUser>().eq(SysUser::getPhone,
sysUserFindPwdByPhoneParam.getPhone()).set(SysUser::getPassword,
CommonCryptogramUtil.doHashValue(CommonCryptogramUtil.doSm2Decrypt(sysUserFindPwdByPhoneParam.getNewPassword()))));
@ -500,7 +512,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
@Override
public void findPasswordByEmail(SysUserFindPwdByEmailParam sysUserFindPwdByEmailParam) {
// 执行校验验证码
validValidCode(sysUserFindPwdByEmailParam.getValidCode(), sysUserFindPwdByEmailParam.getValidCodeReqNo());
validValidCode(sysUserFindPwdByEmailParam.getEmail(), sysUserFindPwdByEmailParam.getValidCode(), sysUserFindPwdByEmailParam.getValidCodeReqNo());
this.update(new LambdaUpdateWrapper<SysUser>().eq(SysUser::getEmail,
sysUserFindPwdByEmailParam.getEmail()).set(SysUser::getPassword,
CommonCryptogramUtil.doHashValue(CommonCryptogramUtil.doSm2Decrypt(sysUserFindPwdByEmailParam.getNewPassword()))));
@ -530,7 +542,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
sysUser.getId()).set(SysUser::getAvatar, base64));
return base64;
} catch (IOException e) {
e.printStackTrace();
log.error(">>> 头像修改失败:", e);
throw new CommonException("头像修改失败用户id值为{}", sysUser.getId());
}
}
@ -575,8 +587,8 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
SysRelationCategoryEnum.SYS_USER_HAS_RESOURCE.getValue());
if (ObjectUtil.isNotEmpty(roleIdList)) {
menuIdList = sysRelationService.getRelationTargetIdListByObjectIdListAndCategory(roleIdList,
SysRelationCategoryEnum.SYS_ROLE_HAS_RESOURCE.getValue());
menuIdList.addAll(sysRelationService.getRelationTargetIdListByObjectIdListAndCategory(roleIdList,
SysRelationCategoryEnum.SYS_ROLE_HAS_RESOURCE.getValue()));
}
// 获取所有的菜单和模块以及单页面列表并按分类和排序码排序
@ -611,9 +623,8 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
List<SysMenu> resultList = CollectionUtil.newArrayList();
// 获取拥有的菜单列表
List<String> finalMenuIdList = menuIdList;
List<SysMenu> menuList = allMenuList.stream().filter(sysMenu ->
finalMenuIdList.contains(sysMenu.getId())).collect(Collectors.toList());
menuIdList.contains(sysMenu.getId())).collect(Collectors.toList());
// 对获取到的角色对应的菜单列表进行处理获取父列表
menuList.forEach(sysMenu -> execRecursionFindParent(allMenuList, sysMenu.getId(), resultList));
@ -774,8 +785,25 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
@Override
public void grantResource(SysUserGrantResourceParam sysUserGrantResourceParam) {
String id = sysUserGrantResourceParam.getId();
SysUserIdParam sysUserIdParam = new SysUserIdParam();
List<String> roleIdList = this.ownRole(sysUserIdParam);
if(ObjectUtil.isEmpty(roleIdList)) {
throw new CommonException("非超管角色用户不可被授权系统模块菜单资源");
}
boolean hasSuperAdminRole = sysRoleService.listByIds(roleIdList).stream().map(SysRole::getCode).collect(Collectors.toSet())
.contains(SysBuildInEnum.BUILD_IN_ROLE_CODE.getValue());
List<String> menuIdList = sysUserGrantResourceParam.getGrantInfoList().stream()
.map(SysUserGrantResourceParam.SysUserGrantResource::getMenuId).collect(Collectors.toList());
if(!hasSuperAdminRole) {
if(ObjectUtil.isNotEmpty(menuIdList)) {
Set<String> sysModuleIdList = sysMenuService.listByIds(menuIdList).stream().map(SysMenu::getModule).collect(Collectors.toSet());
boolean containsSystemModule = sysModuleService.listByIds(sysModuleIdList).stream().map(SysModule::getCode)
.collect(Collectors.toSet()).contains(SysBuildInEnum.BUILD_IN_MODULE_CODE.getValue());
if(containsSystemModule) {
throw new CommonException("非超管角色用户不可被授权系统模块菜单资源");
}
}
}
List<String> extJsonList = sysUserGrantResourceParam.getGrantInfoList().stream()
.map(JSONUtil::toJsonStr).collect(Collectors.toList());
sysRelationService.saveRelationBatchWithClear(id, menuIdList, SysRelationCategoryEnum.SYS_USER_HAS_RESOURCE.getValue(),
@ -974,8 +1002,8 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
byte[] bytes = IoUtil.readBytes(inputStream);
CommonDownloadUtil.download("SNOWY2.0系统B端用户导入模板.xlsx", bytes, response);
} catch (Exception e) {
e.printStackTrace();
CommonResponseUtil.renderError(response, "导出失败");
log.error(">>> 下载用户导入模板失败:", e);
CommonResponseUtil.renderError(response, "下载用户导入模板失败");
}
}
@ -1008,8 +1036,8 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
.set("errorCount", errorCount)
.set("errorDetail", errorDetail);
} catch (Exception e) {
e.printStackTrace();
throw new CommonException("文件导入失败");
log.error(">>> 用户导入失败:", e);
throw new CommonException("用户导入失败");
}
}
@ -1134,7 +1162,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
// 返回成功
return JSONUtil.createObj().set("success", true);
} catch (Exception e) {
e.printStackTrace();
log.error(">>> 数据导入异常:", e);
return JSONUtil.createObj().set("success", false).set("index", i + 1).set("msg", "数据导入异常");
}
}
@ -1265,8 +1293,8 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
.doWrite(sysUserExportResultList);
CommonDownloadUtil.download(tempFile, response);
} catch (Exception e) {
e.printStackTrace();
CommonResponseUtil.renderError(response, "导出失败");
log.error(">>> 用户导出失败:", e);
CommonResponseUtil.renderError(response, "用户导出失败");
} finally {
FileUtil.del(tempFile);
}
@ -1319,7 +1347,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
// 下载
CommonDownloadUtil.download(resultFile, response);
} catch (Exception e) {
e.printStackTrace();
log.error(">>> 导出用户个人信息失败:", e);
CommonResponseUtil.renderError(response, "导出失败");
} finally {
// 删除临时文件

View File

@ -53,10 +53,11 @@ public class GlobalErrorViewController {
} else {
commonResult = CommonResult.error();
}
Exception exception = (Exception) model;
exception.printStackTrace();
if(model instanceof Exception){
Exception exception = (Exception) model;
log.error(">>> 服务器未知异常:", exception);
}
}
log.error(">>> {}", commonResult.getMsg());
return commonResult;
}
}

View File

@ -69,17 +69,16 @@ public class GlobalExceptionUtil {
commonResult = CommonResult.get(HttpStatus.HTTP_BAD_METHOD, "请求方法仅支持GET或POST", null);
}
} else if (e instanceof HttpMessageNotReadableException) {
log.error(">>> 参数传递格式异常:", e);
// 如果是参数传递格式不支持异常 415
if (e.getMessage().contains("JSON parse error")) {
e.printStackTrace();
//JSON格式转换错误特殊提示
commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, "参数格式错误", null);
} else {
commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, "请使用JSON方式传参", null);
}
} else if (e instanceof HttpMediaTypeNotSupportedException) {
e.printStackTrace();
log.error(">>> 参数传递格式异常:", e);
// 如果是JSON参数格式错误异常 415
commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, "参数格式错误", null);
} else if (e instanceof MethodArgumentNotValidException) {
@ -104,7 +103,7 @@ public class GlobalExceptionUtil {
commonResult = CommonResult.get(HttpStatus.HTTP_UNSUPPORTED_TYPE, missingServletRequestParameterException.getMessage(), null);
}
else if (e instanceof MultipartException) {
log.error(">>> 文件上传参数异常:", e);
//文件上传错误特殊提示
commonResult = CommonResult.error("请使用multipart/form-data方式上传文件");
} else if (e instanceof MissingServletRequestPartException) {
@ -125,11 +124,11 @@ public class GlobalExceptionUtil {
CommonException commonException = (CommonException) secondCause;
commonResult = CommonResult.get(commonException.getCode(), commonException.getMsg(), null);
} else {
e.printStackTrace();
log.error(">>> 数据操作异常:", e);
commonResult = CommonResult.error("数据操作异常");
}
} else {
e.printStackTrace();
log.error(">>> 数据操作异常:", e);
commonResult = CommonResult.error("数据操作异常");
}
} else if (e instanceof CommonException) {
@ -139,12 +138,10 @@ public class GlobalExceptionUtil {
commonResult = CommonResult.get(commonException.getCode(), commonException.getMsg(), null);
} else {
// 未知异常打印详情
e.printStackTrace();
log.error(">>> 服务器未知异常:{}, 请求地址:{}", e, CommonServletUtil.getRequest().getRequestURL());
// 未知异常返回服务器异常
commonResult = CommonResult.error("服务器异常");
}
log.error(">>> {},请求地址:{}", commonResult.getMsg(), CommonServletUtil.getRequest().getRequestURL());
return commonResult;
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
# 数据库说明
1.snowy_mysql.sqlmysql版本的主sql文件如果你是mysql运行此sql并启动项目即可
2.snowy_oracle.sqloracle版本的主sql文件如果你是oracle运行此sql并启动项目即可