├── public └── favicon.ico ├── .env.development ├── src ├── assets │ ├── ccflowRightNextAdmin.png │ └── logo-mini.svg ├── stores │ ├── index.ts │ ├── requestOldRoutes.ts │ ├── tagsViewRoutes.ts │ ├── routesList.ts │ ├── keepAliveNames.ts │ └── userInfo.ts ├── utils │ ├── mitt.ts │ ├── authFunction.ts │ ├── setIconfont.ts │ ├── loading.ts │ ├── watermark.ts │ ├── storage.ts │ ├── request.ts │ ├── arrayOperation.ts │ ├── theme.ts │ ├── commonFunction.ts │ └── getStyleSheets.ts ├── theme │ ├── media │ │ ├── cityLinkage.scss │ │ ├── tagsView.scss │ │ ├── dialog.scss │ │ ├── pagination.scss │ │ ├── personal.scss │ │ ├── index.scss │ │ ├── media.scss │ │ ├── home.scss │ │ ├── date.scss │ │ ├── form.scss │ │ ├── error.scss │ │ ├── layout.scss │ │ ├── scrollbar.scss │ │ ├── login.scss │ │ └── chart.scss │ ├── index.scss │ ├── tableTool.scss │ ├── iconSelector.scss │ ├── mixins │ │ └── index.scss │ ├── other.scss │ ├── loading.scss │ ├── common │ │ └── transition.scss │ └── waves.scss ├── i18n │ ├── pages │ │ ├── formI18n │ │ │ ├── zh-cn.ts │ │ │ ├── zh-tw.ts │ │ │ └── en.ts │ │ └── login │ │ │ ├── zh-tw.ts │ │ │ ├── zh-cn.ts │ │ │ └── en.ts │ └── index.ts ├── types │ ├── axios.d.ts │ ├── mitt.d.ts │ ├── layout.d.ts │ ├── pinia.d.ts │ └── global.d.ts ├── views │ ├── menu │ │ ├── menu2 │ │ │ └── index.vue │ │ └── menu1 │ │ │ ├── menu11 │ │ │ └── index.vue │ │ │ ├── menu12 │ │ │ ├── menu121 │ │ │ │ └── index.vue │ │ │ └── menu122 │ │ │ │ └── index.vue │ │ │ └── menu13 │ │ │ └── index.vue │ ├── pages │ │ ├── filtering │ │ │ ├── details1.vue │ │ │ └── details.vue │ │ ├── preview │ │ │ └── index.vue │ │ ├── workflow │ │ │ ├── component │ │ │ │ ├── tool │ │ │ │ │ ├── help.vue │ │ │ │ │ └── index.vue │ │ │ │ ├── drawer │ │ │ │ │ ├── line.vue │ │ │ │ │ └── index.vue │ │ │ │ └── contextmenu │ │ │ │ │ └── index.vue │ │ │ └── js │ │ │ │ └── config.ts │ │ ├── steps │ │ │ └── index.vue │ │ ├── drag │ │ │ └── index.vue │ │ ├── formRules │ │ │ ├── component │ │ │ │ ├── formRulesTwo.vue │ │ │ │ ├── formRulesThree.vue │ │ │ │ └── formRulesOne.vue │ │ │ └── index.vue │ │ ├── formI18n │ │ │ └── index.vue │ │ ├── dynamicForm │ │ │ └── mock.ts │ │ ├── awesome │ │ │ └── index.vue │ │ ├── iocnfont │ │ │ └── index.vue │ │ └── element │ │ │ └── index.vue │ ├── params │ │ ├── common │ │ │ ├── details.vue │ │ │ └── index.vue │ │ └── dynamic │ │ │ ├── details.vue │ │ │ └── index.vue │ ├── chart │ │ ├── chart.ts │ │ └── head.vue │ ├── fun │ │ ├── clipboard │ │ │ └── index.vue │ │ ├── printJs │ │ │ └── index.vue │ │ ├── splitpanes │ │ │ └── index.vue │ │ ├── cropper │ │ │ └── index.vue │ │ ├── gridLayout │ │ │ └── index.vue │ │ ├── qrcode │ │ │ └── index.vue │ │ ├── wangEditor │ │ │ └── index.vue │ │ ├── echartsMap │ │ │ └── index.vue │ │ └── tagsView │ │ │ └── index.vue │ ├── limits │ │ ├── backEnd │ │ │ └── page │ │ │ │ └── index.vue │ │ └── frontEnd │ │ │ └── page │ │ │ └── index.vue │ ├── make │ │ ├── svgDemo │ │ │ └── index.vue │ │ └── selector │ │ │ └── index.vue │ ├── personal │ │ └── mock.ts │ ├── login │ │ └── component │ │ │ ├── scan.vue │ │ │ └── mobile.vue │ ├── visualizing │ │ └── mock │ │ │ ├── demo1.ts │ │ │ └── demo2.ts │ └── error │ │ ├── 404.vue │ │ └── 401.vue ├── directive │ ├── index.ts │ └── authDirective.ts ├── api │ ├── login │ │ └── index.ts │ └── menu │ │ └── index.ts ├── main.ts ├── layout │ ├── footer │ │ └── index.vue │ ├── component │ │ ├── header.vue │ │ └── main.vue │ ├── navBars │ │ ├── index.vue │ │ └── topBar │ │ │ ├── closeFull.vue │ │ │ └── userNews.vue │ ├── navMenu │ │ ├── subItem.vue │ │ └── vertical.vue │ ├── index.vue │ ├── main │ │ ├── transverse.vue │ │ ├── defaults.vue │ │ ├── classic.vue │ │ └── columns.vue │ ├── logo │ │ └── index.vue │ ├── routerView │ │ ├── link.vue │ │ └── iframes.vue │ └── sponsors │ │ └── index.vue └── components │ ├── auth │ ├── auth.vue │ ├── authAll.vue │ └── auths.vue │ ├── svgIcon │ └── index.vue │ ├── iconSelector │ └── list.vue │ └── editor │ └── index.vue ├── .env.production ├── .eslintignore ├── .env ├── .gitignore ├── LICENSE ├── .prettierrc.js ├── index.html ├── vite.config.ts ├── package.json └── .eslintrc.js /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyt-Top/vue-next-admin/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # 本地环境 2 | ENV = development 3 | 4 | # 本地环境接口地址 5 | VITE_API_URL = http://localhost:8888/ -------------------------------------------------------------------------------- /src/assets/ccflowRightNextAdmin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyt-Top/vue-next-admin/HEAD/src/assets/ccflowRightNextAdmin.png -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | # 线上环境 2 | ENV = production 3 | 4 | # 线上环境接口地址 5 | VITE_API_URL = https://lyt-top.gitee.io/vue-next-admin-preview/ -------------------------------------------------------------------------------- /src/stores/index.ts: -------------------------------------------------------------------------------- 1 | // https://pinia.vuejs.org/ 2 | import { createPinia } from 'pinia'; 3 | 4 | // 创建 5 | const pinia = createPinia(); 6 | 7 | // 导出 8 | export default pinia; 9 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | 2 | *.sh 3 | node_modules 4 | lib 5 | *.md 6 | *.scss 7 | *.woff 8 | *.ttf 9 | .vscode 10 | .idea 11 | dist 12 | mock 13 | public 14 | bin 15 | build 16 | config 17 | index.html 18 | src/assets -------------------------------------------------------------------------------- /src/utils/mitt.ts: -------------------------------------------------------------------------------- 1 | // https://www.npmjs.com/package/mitt 2 | import mitt, { Emitter } from 'mitt'; 3 | 4 | // 类型 5 | const emitter: Emitter = mitt(); 6 | 7 | // 导出 8 | export default emitter; 9 | -------------------------------------------------------------------------------- /src/theme/media/cityLinkage.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于576px 4 | ------------------------------- */ 5 | @media screen and (max-width: $xs) { 6 | .el-cascader__dropdown.el-popper { 7 | overflow: auto; 8 | max-width: 100%; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/theme/media/tagsView.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于768px 4 | ------------------------------- */ 5 | @media screen and (max-width: $sm) { 6 | .tags-view-form { 7 | .tags-view-form-col { 8 | margin-bottom: 20px; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/theme/index.scss: -------------------------------------------------------------------------------- 1 | @import 'element-plus/dist/index.css'; 2 | @import './app.scss'; 3 | @import 'common/transition.scss'; 4 | @import './other.scss'; 5 | @import './element.scss'; 6 | @import './media/media.scss'; 7 | @import './waves.scss'; 8 | @import './dark.scss'; 9 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # port 端口号 2 | VITE_PORT = 8888 3 | 4 | # open 运行 npm run dev 时自动打开浏览器 5 | VITE_OPEN = false 6 | 7 | # 打包是否开启 cdn(源文件 utils/build.ts),可自行修改 8 | VITE_OPEN_CDN = false 9 | 10 | # public path 配置线上环境路径(打包)、本地通过 http-server 访问时,请置空即可 11 | VITE_PUBLIC_PATH = /vue-next-admin-preview/ -------------------------------------------------------------------------------- /src/i18n/pages/formI18n/zh-cn.ts: -------------------------------------------------------------------------------- 1 | // 定义内容 2 | export default { 3 | formI18nLabel: { 4 | name: '姓名', 5 | email: '用户归属部门', 6 | autograph: '登陆账户名', 7 | }, 8 | formI18nPlaceholder: { 9 | name: '请输入姓名', 10 | email: '请输入用户归属部门', 11 | autograph: '请输入登陆账户名', 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/i18n/pages/formI18n/zh-tw.ts: -------------------------------------------------------------------------------- 1 | // 定义内容 2 | export default { 3 | formI18nLabel: { 4 | name: '姓名', 5 | email: '用戶歸屬部門', 6 | autograph: '登入帳戶名', 7 | }, 8 | formI18nPlaceholder: { 9 | name: '請輸入姓名', 10 | email: '請輸入用戶歸屬部門', 11 | autograph: '請輸入登入帳戶名', 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/theme/media/dialog.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于800px 4 | ------------------------------- */ 5 | @media screen and (max-width: 800px) { 6 | .el-dialog { 7 | width: 90% !important; 8 | } 9 | .el-dialog.is-fullscreen { 10 | width: 100% !important; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/types/axios.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import * as axios from 'axios'; 3 | 4 | // 扩展 axios 数据返回类型,可自行扩展 5 | declare module 'axios' { 6 | export interface AxiosResponse { 7 | code: number; 8 | data: T; 9 | message: string; 10 | type?: string; 11 | [key: string]: T; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/views/menu/menu2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /src/views/menu/menu1/menu11/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /src/views/menu/menu1/menu12/menu121/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /src/views/menu/menu1/menu12/menu122/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /src/i18n/pages/formI18n/en.ts: -------------------------------------------------------------------------------- 1 | // 定义内容 2 | export default { 3 | formI18nLabel: { 4 | name: 'name', 5 | email: 'email', 6 | autograph: 'autograph', 7 | }, 8 | formI18nPlaceholder: { 9 | name: 'Please enter your name', 10 | email: 'Please enter the users Department', 11 | autograph: 'Please enter the login account name', 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/theme/media/pagination.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于576px 4 | ------------------------------- */ 5 | @media screen and (max-width: $xs) { 6 | .el-pager, 7 | .el-pagination__jump { 8 | display: none !important; 9 | } 10 | // 默认居中对齐 11 | .el-pagination, 12 | .table-footer { 13 | justify-content: center !important; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/theme/media/personal.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于768px 4 | ------------------------------- */ 5 | @media screen and (max-width: $sm) { 6 | .personal-info { 7 | padding-left: 0 !important; 8 | margin-top: 15px; 9 | } 10 | .personal-recommend-col { 11 | margin-bottom: 15px; 12 | &:last-of-type { 13 | margin-bottom: 0; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/views/pages/filtering/details1.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /src/theme/media/index.scss: -------------------------------------------------------------------------------- 1 | /* 栅格布局(媒体查询变量) 2 | * https://developer.mozilla.org/zh-CN/docs/Learn/CSS/CSS_layout/Media_queries 3 | * $us ≥376px 响应式栅格 4 | * $xs ≥576px 响应式栅格 5 | * $sm ≥768px 响应式栅格 6 | * $md ≥992px 响应式栅格 7 | * $lg ≥1200px 响应式栅格 8 | * $xl ≥1920px 响应式栅格 9 | ------------------------------- */ 10 | $us: 376px; 11 | $xs: 576px; 12 | $sm: 768px; 13 | $md: 992px; 14 | $lg: 1200px; 15 | $xl: 1920px; 16 | -------------------------------------------------------------------------------- /src/theme/media/media.scss: -------------------------------------------------------------------------------- 1 | @import './login.scss'; 2 | @import './error.scss'; 3 | @import './layout.scss'; 4 | @import './personal.scss'; 5 | @import './tagsView.scss'; 6 | @import './home.scss'; 7 | @import './chart.scss'; 8 | @import './form.scss'; 9 | @import './scrollbar.scss'; 10 | @import './pagination.scss'; 11 | @import './dialog.scss'; 12 | @import './cityLinkage.scss'; 13 | @import './date.scss'; 14 | -------------------------------------------------------------------------------- /src/views/pages/filtering/details.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /src/stores/requestOldRoutes.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | /** 4 | * 后端返回原始路由(未处理时) 5 | * @methods setCacheKeepAlive 设置接口原始路由数据 6 | */ 7 | export const useRequestOldRoutes = defineStore('requestOldRoutes', { 8 | state: (): RequestOldRoutesState => ({ 9 | requestOldRoutes: [], 10 | }), 11 | actions: { 12 | async setRequestOldRoutes(routes: Array) { 13 | this.requestOldRoutes = routes; 14 | }, 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /src/theme/media/home.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于768px 4 | ------------------------------- */ 5 | @media screen and (max-width: $sm) { 6 | .home-media, 7 | .home-media-sm { 8 | margin-top: 15px; 9 | } 10 | } 11 | 12 | /* 页面宽度小于1200px 13 | ------------------------------- */ 14 | @media screen and (max-width: $lg) { 15 | .home-media-lg { 16 | margin-top: 15px; 17 | } 18 | .home-monitor { 19 | .flex-warp-item { 20 | width: 33.33% !important; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/views/menu/menu1/menu13/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | -------------------------------------------------------------------------------- /src/directive/index.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue'; 2 | import { authDirective } from '/@/directive/authDirective'; 3 | import { wavesDirective, dragDirective } from '/@/directive/customDirective'; 4 | 5 | /** 6 | * 导出指令方法:v-xxx 7 | * @methods authDirective 用户权限指令,用法:v-auth 8 | * @methods wavesDirective 按钮波浪指令,用法:v-waves 9 | * @methods dragDirective 自定义拖动指令,用法:v-drag 10 | */ 11 | export function directive(app: App) { 12 | // 用户权限指令 13 | authDirective(app); 14 | // 按钮波浪指令 15 | wavesDirective(app); 16 | // 自定义拖动指令 17 | dragDirective(app); 18 | } 19 | -------------------------------------------------------------------------------- /src/views/params/common/details.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /src/views/params/dynamic/details.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /src/api/login/index.ts: -------------------------------------------------------------------------------- 1 | import request from '/@/utils/request'; 2 | 3 | /** 4 | * (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参) 5 | * 6 | * 登录api接口集合 7 | * @method signIn 用户登录 8 | * @method signOut 用户退出登录 9 | */ 10 | export function useLoginApi() { 11 | return { 12 | signIn: (data: object) => { 13 | return request({ 14 | url: '/user/signIn', 15 | method: 'post', 16 | data, 17 | }); 18 | }, 19 | signOut: (data: object) => { 20 | return request({ 21 | url: '/user/signOut', 22 | method: 'post', 23 | data, 24 | }); 25 | }, 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import pinia from '/@/stores/index'; 3 | import App from '/@/App.vue'; 4 | import router from '/@/router'; 5 | import { directive } from '/@/directive/index'; 6 | import { i18n } from '/@/i18n/index'; 7 | import other from '/@/utils/other'; 8 | 9 | import ElementPlus from 'element-plus'; 10 | import '/@/theme/index.scss'; 11 | import VueGridLayout from 'vue-grid-layout'; 12 | 13 | const app = createApp(App); 14 | 15 | directive(app); 16 | other.elSvg(app); 17 | 18 | app.use(pinia).use(router).use(ElementPlus).use(i18n).use(VueGridLayout).mount('#app'); 19 | -------------------------------------------------------------------------------- /src/layout/footer/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | 26 | -------------------------------------------------------------------------------- /src/theme/media/date.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于768px 4 | ------------------------------- */ 5 | @media screen and (max-width: $sm) { 6 | // 时间选择器适配 7 | .el-date-range-picker { 8 | width: 100vw; 9 | .el-picker-panel__body { 10 | min-width: 100%; 11 | .el-date-range-picker__content { 12 | .el-date-range-picker__header div { 13 | margin-left: 22px; 14 | margin-right: 0px; 15 | } 16 | & + .el-date-range-picker__content { 17 | .el-date-range-picker__header div { 18 | margin-left: 0px; 19 | margin-right: 22px; 20 | } 21 | } 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/layout/component/header.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | -------------------------------------------------------------------------------- /src/i18n/pages/login/zh-tw.ts: -------------------------------------------------------------------------------- 1 | // 定义内容 2 | export default { 3 | label: { 4 | one1: '用戶名登入', 5 | two2: '手機號登入', 6 | }, 7 | link: { 8 | one3: '協力廠商登入', 9 | two4: '友情連結', 10 | }, 11 | account: { 12 | accountPlaceholder1: '用戶名admin或不輸均為common', 13 | accountPlaceholder2: '密碼:123456', 14 | accountPlaceholder3: '請輸入驗證碼', 15 | accountBtnText: '登入', 16 | }, 17 | mobile: { 18 | placeholder1: '請輸入手機號', 19 | placeholder2: '請輸入驗證碼', 20 | codeText: '獲取驗證碼', 21 | btnText: '登入', 22 | msgText: '* 溫馨提示:建議使用穀歌、Microsoft Edge,版本79.0.1072.62及以上瀏覽器,360瀏覽器請使用極速模式', 23 | }, 24 | scan: { 25 | text: '打開手機掃一掃,快速登錄/注册', 26 | }, 27 | signInText: '歡迎回來!', 28 | }; 29 | -------------------------------------------------------------------------------- /src/theme/tableTool.scss: -------------------------------------------------------------------------------- 1 | .table-tool-popper { 2 | padding: 0 !important; 3 | .tool-box { 4 | display: flex; 5 | border-bottom: 1px solid var(--el-border-color-lighter); 6 | box-sizing: border-box; 7 | color: var(--el-text-color-primary); 8 | height: 40px; 9 | align-items: center; 10 | } 11 | .tool-sortable { 12 | max-height: 303px; 13 | .tool-sortable-item { 14 | display: flex; 15 | box-sizing: border-box; 16 | color: var(--el-text-color-primary); 17 | align-items: center; 18 | padding: 0 12px; 19 | &:hover { 20 | background: var(--el-fill-color-lighter); 21 | } 22 | i { 23 | opacity: 0.7; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/i18n/pages/login/zh-cn.ts: -------------------------------------------------------------------------------- 1 | // 定义内容 2 | export default { 3 | label: { 4 | one1: '用户名登录', 5 | two2: '手机号登录', 6 | }, 7 | link: { 8 | one3: '第三方登录', 9 | two4: '友情链接', 10 | }, 11 | account: { 12 | accountPlaceholder1: '用户名 admin 或不输均为 common', 13 | accountPlaceholder2: '密码:123456', 14 | accountPlaceholder3: '请输入验证码', 15 | accountBtnText: '登 录', 16 | }, 17 | mobile: { 18 | placeholder1: '请输入手机号', 19 | placeholder2: '请输入验证码', 20 | codeText: '获取验证码', 21 | btnText: '登 录', 22 | msgText: '* 温馨提示:建议使用谷歌、Microsoft Edge,版本 79.0.1072.62 及以上浏览器,360浏览器请使用极速模式', 23 | }, 24 | scan: { 25 | text: '打开手机扫一扫,快速登录/注册', 26 | }, 27 | signInText: '欢迎回来!', 28 | }; 29 | -------------------------------------------------------------------------------- /src/components/auth/auth.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 27 | -------------------------------------------------------------------------------- /src/stores/tagsViewRoutes.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | import { Session } from '/@/utils/storage'; 3 | 4 | /** 5 | * TagsView 路由列表 6 | * @methods setTagsViewRoutes 设置 TagsView 路由列表 7 | * @methods setCurrenFullscreen 设置开启/关闭全屏时的 boolean 状态 8 | */ 9 | export const useTagsViewRoutes = defineStore('tagsViewRoutes', { 10 | state: (): TagsViewRoutesState => ({ 11 | tagsViewRoutes: [], 12 | isTagsViewCurrenFull: false, 13 | }), 14 | actions: { 15 | async setTagsViewRoutes(data: Array) { 16 | this.tagsViewRoutes = data; 17 | }, 18 | setCurrenFullscreen(bool: Boolean) { 19 | Session.set('isTagsViewCurrenFull', bool); 20 | this.isTagsViewCurrenFull = bool; 21 | }, 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /src/components/auth/authAll.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 28 | -------------------------------------------------------------------------------- /src/stores/routesList.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | /** 4 | * 路由列表 5 | * @methods setRoutesList 设置路由数据 6 | * @methods setColumnsMenuHover 设置分栏布局菜单鼠标移入 boolean 7 | * @methods setColumnsNavHover 设置分栏布局最左侧导航鼠标移入 boolean 8 | */ 9 | export const useRoutesList = defineStore('routesList', { 10 | state: (): RoutesListState => ({ 11 | routesList: [], 12 | isColumnsMenuHover: false, 13 | isColumnsNavHover: false, 14 | }), 15 | actions: { 16 | async setRoutesList(data: Array) { 17 | this.routesList = data; 18 | }, 19 | async setColumnsMenuHover(bool: Boolean) { 20 | this.isColumnsMenuHover = bool; 21 | }, 22 | async setColumnsNavHover(bool: Boolean) { 23 | this.isColumnsNavHover = bool; 24 | }, 25 | }, 26 | }); 27 | -------------------------------------------------------------------------------- /src/theme/media/form.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于576px 4 | ------------------------------- */ 5 | @media screen and (max-width: $xs) { 6 | .el-form-item__label { 7 | width: 100% !important; 8 | text-align: left !important; 9 | // 移动端 label 右对齐问题 10 | justify-content: flex-start !important; 11 | } 12 | .el-form-item__content { 13 | margin-left: 0 !important; 14 | } 15 | .el-form-item { 16 | // 响应式表单时,登录页需要重新处理 17 | display: unset !important; 18 | } 19 | // 表格演示中的表单筛选 20 | .table-form-btn { 21 | display: flex !important; 22 | .el-form-item__label { 23 | width: auto !important; 24 | } 25 | } 26 | // 表格演示中的表单筛选最大高度,适配移动端 27 | .table-search-container { 28 | max-height: 160px; 29 | overflow: auto; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/theme/iconSelector.scss: -------------------------------------------------------------------------------- 1 | /* Popover 弹出框(图标选择器) 2 | ------------------------------- */ 3 | .icon-selector-popper { 4 | padding: 0 !important; 5 | .icon-selector-warp { 6 | height: 260px; 7 | overflow: hidden; 8 | position: relative; 9 | .icon-selector-warp-title { 10 | position: absolute; 11 | height: 40px; 12 | line-height: 40px; 13 | left: 15px; 14 | } 15 | .el-tabs__header { 16 | display: flex; 17 | justify-content: flex-end; 18 | padding: 0 15px; 19 | border-bottom: 1px solid var(--el-border-color-light); 20 | margin: 0 !important; 21 | .el-tabs__nav-wrap { 22 | &::after { 23 | height: 0 !important; 24 | } 25 | .el-tabs__item { 26 | padding: 0 5px !important; 27 | } 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/components/auth/auths.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 33 | -------------------------------------------------------------------------------- /src/views/chart/chart.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * sky 天气 3 | * @returns 返回模拟数据 4 | */ 5 | export const skyList = [ 6 | { 7 | v1: '时间', 8 | v2: '天气', 9 | v3: '温度', 10 | v5: '降水', 11 | v7: '风力', 12 | type: 'title', 13 | }, 14 | { 15 | v1: '今天', 16 | v2: 'ele-Sunny', 17 | v3: '20°/26°', 18 | v5: '50%', 19 | v7: '13m/s', 20 | }, 21 | { 22 | v1: '明天', 23 | v2: 'ele-Lightning', 24 | v3: '20°/26°', 25 | v5: '50%', 26 | v7: '13m/s', 27 | }, 28 | ]; 29 | 30 | /** 31 | * 当前设置状态 32 | * @returns 返回模拟数据 33 | */ 34 | export const dBtnList = [ 35 | { 36 | v2: '阳光玫瑰种植', 37 | v3: '126天', 38 | v4: '设备在线', 39 | }, 40 | ]; 41 | 42 | /** 43 | * 当前设备监测 44 | * @returns 返回模拟数据 45 | */ 46 | export const chartData4List = [ 47 | { 48 | label: '温度', 49 | }, 50 | { 51 | label: '光照', 52 | }, 53 | { 54 | label: '湿度', 55 | }, 56 | { 57 | label: '风力', 58 | }, 59 | ]; 60 | -------------------------------------------------------------------------------- /src/api/menu/index.ts: -------------------------------------------------------------------------------- 1 | import request from '/@/utils/request'; 2 | 3 | /** 4 | * 以下为模拟接口地址,gitee 的不通,就换自己的真实接口地址 5 | * 6 | * (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参) 7 | * 8 | * 后端控制菜单模拟json,路径在 https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu 9 | * 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由 10 | * @method getAdminMenu 获取后端动态路由菜单(admin) 11 | * @method getTestMenu 获取后端动态路由菜单(test) 12 | */ 13 | export function useMenuApi() { 14 | return { 15 | getAdminMenu: (params?: object) => { 16 | return request({ 17 | url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/adminMenu.json', 18 | method: 'get', 19 | params, 20 | }); 21 | }, 22 | getTestMenu: (params?: object) => { 23 | return request({ 24 | url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/testMenu.json', 25 | method: 'get', 26 | params, 27 | }); 28 | }, 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /src/i18n/pages/login/en.ts: -------------------------------------------------------------------------------- 1 | // 定义内容 2 | export default { 3 | label: { 4 | one1: 'User name login', 5 | two2: 'Mobile number', 6 | }, 7 | link: { 8 | one3: 'Third party login', 9 | two4: 'Links', 10 | }, 11 | account: { 12 | accountPlaceholder1: 'The user name admin or not is common', 13 | accountPlaceholder2: 'Password: 123456', 14 | accountPlaceholder3: 'Please enter the verification code', 15 | accountBtnText: 'Sign in', 16 | }, 17 | mobile: { 18 | placeholder1: 'Please input mobile phone number', 19 | placeholder2: 'Please enter the verification code', 20 | codeText: 'Get code', 21 | btnText: 'Sign in', 22 | msgText: 23 | 'Warm tip: it is recommended to use Google, Microsoft edge, version 79.0.1072.62 and above browsers, and 360 browser, please use speed mode', 24 | }, 25 | scan: { 26 | text: 'Open the mobile phone to scan and quickly log in / register', 27 | }, 28 | signInText: 'welcome back!', 29 | }; 30 | -------------------------------------------------------------------------------- /src/theme/media/error.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于768px 4 | ------------------------------- */ 5 | @media screen and (max-width: $sm) { 6 | .error { 7 | .error-flex { 8 | flex-direction: column-reverse !important; 9 | height: auto !important; 10 | width: 100% !important; 11 | } 12 | .right, 13 | .left { 14 | flex: unset !important; 15 | display: flex !important; 16 | } 17 | .left-item { 18 | margin: auto !important; 19 | } 20 | .right img { 21 | max-width: 450px !important; 22 | @extend .left-item; 23 | } 24 | } 25 | } 26 | 27 | /* 页面宽度大于768px小于992px 28 | ------------------------------- */ 29 | @media screen and (min-width: $sm) and (max-width: $md) { 30 | .error { 31 | .error-flex { 32 | padding-left: 30px !important; 33 | } 34 | } 35 | } 36 | 37 | /* 页面宽度小于1200px 38 | ------------------------------- */ 39 | @media screen and (max-width: $lg) { 40 | .error { 41 | .error-flex { 42 | padding: 0 30px; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/views/fun/clipboard/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 31 | -------------------------------------------------------------------------------- /src/views/limits/backEnd/page/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 31 | -------------------------------------------------------------------------------- /src/utils/authFunction.ts: -------------------------------------------------------------------------------- 1 | import { useUserInfo } from '/@/stores/userInfo'; 2 | import { judementSameArr } from '/@/utils/arrayOperation'; 3 | 4 | /** 5 | * 单个权限验证 6 | * @param value 权限值 7 | * @returns 有权限,返回 `true`,反之则反 8 | */ 9 | export function auth(value: string): boolean { 10 | const stores = useUserInfo(); 11 | return stores.userInfos.authBtnList.some((v: string) => v === value); 12 | } 13 | 14 | /** 15 | * 多个权限验证,满足一个则为 true 16 | * @param value 权限值 17 | * @returns 有权限,返回 `true`,反之则反 18 | */ 19 | export function auths(value: Array): boolean { 20 | let flag = false; 21 | const stores = useUserInfo(); 22 | stores.userInfos.authBtnList.map((val: string) => { 23 | value.map((v: string) => { 24 | if (val === v) flag = true; 25 | }); 26 | }); 27 | return flag; 28 | } 29 | 30 | /** 31 | * 多个权限验证,全部满足则为 true 32 | * @param value 权限值 33 | * @returns 有权限,返回 `true`,反之则反 34 | */ 35 | export function authAll(value: Array): boolean { 36 | const stores = useUserInfo(); 37 | return judementSameArr(value, stores.userInfos.authBtnList); 38 | } 39 | -------------------------------------------------------------------------------- /src/views/pages/preview/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /src/views/pages/workflow/component/tool/help.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 37 | -------------------------------------------------------------------------------- /src/layout/navBars/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 27 | 28 | 36 | -------------------------------------------------------------------------------- /src/views/fun/printJs/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 lyt-Top 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // 一行最多多少个字符 3 | printWidth: 150, 4 | // 指定每个缩进级别的空格数 5 | tabWidth: 2, 6 | // 使用制表符而不是空格缩进行 7 | useTabs: true, 8 | // 在语句末尾打印分号 9 | semi: true, 10 | // 使用单引号而不是双引号 11 | singleQuote: true, 12 | // 更改引用对象属性的时间 可选值"" 13 | quoteProps: 'as-needed', 14 | // 在JSX中使用单引号而不是双引号 15 | jsxSingleQuote: false, 16 | // 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"",默认none 17 | trailingComma: 'es5', 18 | // 在对象文字中的括号之间打印空格 19 | bracketSpacing: true, 20 | // jsx 标签的反尖括号需要换行 21 | jsxBracketSameLine: false, 22 | // 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x 23 | arrowParens: 'always', 24 | // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码 25 | rangeStart: 0, 26 | rangeEnd: Infinity, 27 | // 指定要使用的解析器,不需要写文件开头的 @prettier 28 | requirePragma: false, 29 | // 不需要自动在文件开头插入 @prettier 30 | insertPragma: false, 31 | // 使用默认的折行标准 always\never\preserve 32 | proseWrap: 'preserve', 33 | // 指定HTML文件的全局空格敏感度 css\strict\ignore 34 | htmlWhitespaceSensitivity: 'css', 35 | // Vue文件脚本和样式标签缩进 36 | vueIndentScriptAndStyle: false, 37 | // 换行符使用 lf 结尾是 可选值"" 38 | endOfLine: 'lf', 39 | }; 40 | -------------------------------------------------------------------------------- /src/stores/keepAliveNames.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | /** 4 | * 路由缓存列表 5 | * @methods setCacheKeepAlive 设置要缓存的路由 names(开启 Tagsview) 6 | * @methods addCachedView 添加要缓存的路由 names(关闭 Tagsview) 7 | * @methods delCachedView 删除要缓存的路由 names(关闭 Tagsview) 8 | * @methods delOthersCachedViews 右键菜单`关闭其它`,删除要缓存的路由 names(关闭 Tagsview) 9 | * @methods delAllCachedViews 右键菜单`全部关闭`,删除要缓存的路由 names(关闭 Tagsview) 10 | */ 11 | export const useKeepALiveNames = defineStore('keepALiveNames', { 12 | state: (): KeepAliveNamesState => ({ 13 | keepAliveNames: [], 14 | cachedViews: [], 15 | }), 16 | actions: { 17 | async setCacheKeepAlive(data: Array) { 18 | this.keepAliveNames = data; 19 | }, 20 | async addCachedView(view: any) { 21 | if (view.meta.isKeepAlive) this.cachedViews?.push(view.name); 22 | }, 23 | async delCachedView(view: any) { 24 | const index = this.cachedViews.indexOf(view.name); 25 | index > -1 && this.cachedViews.splice(index, 1); 26 | }, 27 | async delOthersCachedViews(view: any) { 28 | if (view.meta.isKeepAlive) this.cachedViews = [view.name]; 29 | else this.cachedViews = []; 30 | }, 31 | async delAllCachedViews() { 32 | this.cachedViews = []; 33 | }, 34 | }, 35 | }); 36 | -------------------------------------------------------------------------------- /src/utils/setIconfont.ts: -------------------------------------------------------------------------------- 1 | // 字体图标 url 2 | const cssCdnUrlList: Array = [ 3 | '//at.alicdn.com/t/c/font_2298093_rnp72ifj3ba.css', 4 | '//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css', 5 | ]; 6 | // 第三方 js url 7 | const jsCdnUrlList: Array = []; 8 | 9 | // 动态批量设置字体图标 10 | export function setCssCdn() { 11 | if (cssCdnUrlList.length <= 0) return false; 12 | cssCdnUrlList.map((v) => { 13 | let link = document.createElement('link'); 14 | link.rel = 'stylesheet'; 15 | link.href = v; 16 | link.crossOrigin = 'anonymous'; 17 | document.getElementsByTagName('head')[0].appendChild(link); 18 | }); 19 | } 20 | 21 | // 动态批量设置第三方js 22 | export function setJsCdn() { 23 | if (jsCdnUrlList.length <= 0) return false; 24 | jsCdnUrlList.map((v) => { 25 | let link = document.createElement('script'); 26 | link.src = v; 27 | document.body.appendChild(link); 28 | }); 29 | } 30 | 31 | /** 32 | * 批量设置字体图标、动态js 33 | * @method cssCdn 动态批量设置字体图标 34 | * @method jsCdn 动态批量设置第三方js 35 | */ 36 | const setIntroduction = { 37 | // 设置css 38 | cssCdn: () => { 39 | setCssCdn(); 40 | }, 41 | // 设置js 42 | jsCdn: () => { 43 | setJsCdn(); 44 | }, 45 | }; 46 | 47 | // 导出函数方法 48 | export default setIntroduction; 49 | -------------------------------------------------------------------------------- /src/types/mitt.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * mitt 事件类型定义 3 | * 4 | * @method openSetingsDrawer 打开布局设置弹窗 5 | * @method restoreDefault 分栏布局,鼠标移入、移出数据显示 6 | * @method setSendColumnsChildren 分栏布局,鼠标移入、移出菜单数据传入到 navMenu 下的菜单中 7 | * @method setSendClassicChildren 经典布局,开启切割菜单时,菜单数据传入到 navMenu 下的菜单中 8 | * @method getBreadcrumbIndexSetFilterRoutes 布局设置弹窗,开启切割菜单时,菜单数据传入到 navMenu 下的菜单中 9 | * @method layoutMobileResize 浏览器窗口改变时,用于适配移动端界面显示 10 | * @method openOrCloseSortable 布局设置弹窗,开启 TagsView 拖拽 11 | * @method openShareTagsView 布局设置弹窗,开启 TagsView 共用 12 | * @method onTagsViewRefreshRouterView tagsview 刷新界面 13 | * @method onCurrentContextmenuClick tagsview 右键菜单每项点击时 14 | */ 15 | declare type MittType = { 16 | openSetingsDrawer?: string; 17 | restoreDefault?: string; 18 | setSendColumnsChildren: T; 19 | setSendClassicChildren: T; 20 | getBreadcrumbIndexSetFilterRoutes?: string; 21 | layoutMobileResize: T; 22 | openOrCloseSortable?: string; 23 | openShareTagsView?: string; 24 | onTagsViewRefreshRouterView?: T; 25 | onCurrentContextmenuClick?: T; 26 | }; 27 | 28 | // mitt 参数类型定义 29 | declare type LayoutMobileResize = { 30 | layout: string; 31 | clientWidth: number; 32 | }; 33 | 34 | // mitt 参数菜单类型 35 | declare type MittMenu = { 36 | children: RouteRecordRaw[]; 37 | item?: RouteItem; 38 | }; 39 | -------------------------------------------------------------------------------- /src/theme/mixins/index.scss: -------------------------------------------------------------------------------- 1 | /* 第三方图标字体间距/大小设置 2 | ------------------------------- */ 3 | @mixin generalIcon { 4 | font-size: 14px !important; 5 | display: inline-block; 6 | vertical-align: middle; 7 | margin-right: 5px; 8 | width: 24px; 9 | text-align: center; 10 | justify-content: center; 11 | } 12 | 13 | /* 文本不换行 14 | ------------------------------- */ 15 | @mixin text-no-wrap() { 16 | text-overflow: ellipsis; 17 | overflow: hidden; 18 | white-space: nowrap; 19 | } 20 | 21 | /* 多行文本溢出 22 | ------------------------------- */ 23 | @mixin text-ellipsis($line: 2) { 24 | overflow: hidden; 25 | word-break: break-all; 26 | text-overflow: ellipsis; 27 | display: -webkit-box; 28 | -webkit-line-clamp: $line; 29 | -webkit-box-orient: vertical; 30 | } 31 | 32 | /* 滚动条(页面未使用) div 中使用: 33 | ------------------------------- */ 34 | // .test { 35 | // @include scrollBar; 36 | // } 37 | @mixin scrollBar { 38 | // 滚动条凹槽的颜色,还可以设置边框属性 39 | &::-webkit-scrollbar-track-piece { 40 | background-color: #f8f8f8; 41 | } 42 | // 滚动条的宽度 43 | &::-webkit-scrollbar { 44 | width: 9px; 45 | height: 9px; 46 | } 47 | // 滚动条的设置 48 | &::-webkit-scrollbar-thumb { 49 | background-color: #dddddd; 50 | background-clip: padding-box; 51 | min-height: 28px; 52 | } 53 | &::-webkit-scrollbar-thumb:hover { 54 | background-color: #bbb; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/directive/authDirective.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue'; 2 | import { useUserInfo } from '/@/stores/userInfo'; 3 | import { judementSameArr } from '/@/utils/arrayOperation'; 4 | 5 | /** 6 | * 用户权限指令 7 | * @directive 单个权限验证(v-auth="xxx") 8 | * @directive 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]") 9 | * @directive 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]") 10 | */ 11 | export function authDirective(app: App) { 12 | // 单个权限验证(v-auth="xxx") 13 | app.directive('auth', { 14 | mounted(el, binding) { 15 | const stores = useUserInfo(); 16 | if (!stores.userInfos.authBtnList.some((v: string) => v === binding.value)) el.parentNode.removeChild(el); 17 | }, 18 | }); 19 | // 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]") 20 | app.directive('auths', { 21 | mounted(el, binding) { 22 | let flag = false; 23 | const stores = useUserInfo(); 24 | stores.userInfos.authBtnList.map((val: string) => { 25 | binding.value.map((v: string) => { 26 | if (val === v) flag = true; 27 | }); 28 | }); 29 | if (!flag) el.parentNode.removeChild(el); 30 | }, 31 | }); 32 | // 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]") 33 | app.directive('auth-all', { 34 | mounted(el, binding) { 35 | const stores = useUserInfo(); 36 | const flag = judementSameArr(binding.value, stores.userInfos.authBtnList); 37 | if (!flag) el.parentNode.removeChild(el); 38 | }, 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /src/theme/media/layout.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于576px 4 | ------------------------------- */ 5 | @media screen and (max-width: $xs) { 6 | // MessageBox 弹框 7 | .el-message-box { 8 | width: 80% !important; 9 | } 10 | // 锁屏页 11 | .layout-lock-screen-date-top { 12 | display: none; 13 | } 14 | } 15 | 16 | /* 页面宽度小于768px 17 | ------------------------------- */ 18 | @media screen and (max-width: $sm) { 19 | // Breadcrumb 面包屑 20 | .layout-navbars-breadcrumb-hide { 21 | display: none; 22 | } 23 | // 外链视图 24 | .layout-view-link { 25 | a { 26 | max-width: 80%; 27 | text-align: center; 28 | } 29 | } 30 | // 菜单搜索 31 | .layout-search-dialog { 32 | .el-autocomplete { 33 | width: 80% !important; 34 | } 35 | } 36 | } 37 | 38 | /* 页面宽度小于1000px 39 | ------------------------------- */ 40 | @media screen and (max-width: 1000px) { 41 | // 布局配置 42 | .layout-drawer-content-flex { 43 | position: relative; 44 | &::after { 45 | content: '手机版不支持切换布局'; 46 | position: absolute; 47 | top: 0; 48 | right: 0; 49 | bottom: 0; 50 | left: 0; 51 | z-index: 1; 52 | text-align: center; 53 | height: 140px; 54 | line-height: 140px; 55 | background: rgba(255, 255, 255, 0.9); 56 | color: #666666; 57 | } 58 | } 59 | // pagination 分页中的工具栏 60 | .table-footer-tool { 61 | display: none !important; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/views/fun/splitpanes/index.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 32 | 33 | 45 | -------------------------------------------------------------------------------- /src/types/layout.d.ts: -------------------------------------------------------------------------------- 1 | // aside 2 | declare type AsideState = { 3 | menuList: RouteRecordRaw[]; 4 | clientWidth: number; 5 | }; 6 | 7 | // columnsAside 8 | declare type ColumnsAsideState = { 9 | columnsAsideList: T[]; 10 | liIndex: number; 11 | liOldIndex: null | number; 12 | liHoverIndex: null | number; 13 | liOldPath: null | string; 14 | difference: number; 15 | routeSplit: string[]; 16 | }; 17 | 18 | // navBars breadcrumb 19 | declare type BreadcrumbState = { 20 | breadcrumbList: T[]; 21 | routeSplit: string[]; 22 | routeSplitFirst: string; 23 | routeSplitIndex: number; 24 | }; 25 | 26 | // navBars search 27 | declare type SearchState = { 28 | isShowSearch: boolean; 29 | menuQuery: string; 30 | tagsViewList: T[]; 31 | }; 32 | 33 | // navBars tagsView 34 | declare type TagsViewState = { 35 | routeActive: string | T; 36 | routePath: string | unknown; 37 | dropdown: { 38 | x: string | number; 39 | y: string | number; 40 | }; 41 | sortable: T; 42 | tagsRefsIndex: number; 43 | tagsViewList: T[]; 44 | tagsViewRoutesList: T[]; 45 | }; 46 | 47 | // navBars parent 48 | declare type ParentViewState = { 49 | refreshRouterViewKey: string; 50 | iframeRefreshKey: string; 51 | keepAliveNameList: string[]; 52 | iframeList: T[]; 53 | }; 54 | 55 | // navBars link 56 | declare type LinkViewState = { 57 | title: string; 58 | isLink: string; 59 | }; 60 | -------------------------------------------------------------------------------- /src/layout/navBars/topBar/closeFull.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 22 | 23 | 54 | -------------------------------------------------------------------------------- /src/utils/loading.ts: -------------------------------------------------------------------------------- 1 | import { nextTick } from 'vue'; 2 | import '/@/theme/loading.scss'; 3 | 4 | /** 5 | * 页面全局 Loading 6 | * @method start 创建 loading 7 | * @method done 移除 loading 8 | */ 9 | export const NextLoading = { 10 | // 创建 loading 11 | start: () => { 12 | const bodys: Element = document.body; 13 | const div = document.createElement('div'); 14 | div.setAttribute('class', 'loading-next'); 15 | const htmls = ` 16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | `; 30 | div.innerHTML = htmls; 31 | bodys.insertBefore(div, bodys.childNodes[0]); 32 | window.nextLoading = true; 33 | }, 34 | // 移除 loading 35 | done: (time: number = 0) => { 36 | nextTick(() => { 37 | setTimeout(() => { 38 | window.nextLoading = false; 39 | const el = document.querySelector('.loading-next'); 40 | el?.parentNode?.removeChild(el); 41 | }, time); 42 | }); 43 | }, 44 | }; 45 | -------------------------------------------------------------------------------- /src/theme/other.scss: -------------------------------------------------------------------------------- 1 | /* wangeditor 富文本编辑器 2 | ------------------------------- */ 3 | .editor-container { 4 | z-index: 10; // 用于 wangeditor 点击全屏时 5 | .w-e-toolbar { 6 | border: 1px solid var(--el-border-color-light, #ebeef5) !important; 7 | border-bottom: 1px solid var(--el-border-color-light, #ebeef5) !important; 8 | border-top-left-radius: 3px; 9 | border-top-right-radius: 3px; 10 | z-index: 2 !important; 11 | } 12 | .w-e-text-container { 13 | border: 1px solid var(--el-border-color-light, #ebeef5) !important; 14 | border-top: none !important; 15 | border-bottom-left-radius: 3px; 16 | border-bottom-right-radius: 3px; 17 | z-index: 1 !important; 18 | } 19 | } 20 | 21 | [data-theme='dark'] { 22 | // textarea - css vars 23 | --w-e-textarea-bg-color: var(--el-color-white) !important; 24 | --w-e-textarea-color: var(--el-text-color-primary) !important; 25 | 26 | // toolbar - css vars 27 | --w-e-toolbar-color: var(--el-text-color-primary) !important; 28 | --w-e-toolbar-bg-color: var(--el-color-white) !important; 29 | --w-e-toolbar-active-color: var(--el-text-color-primary) !important; 30 | --w-e-toolbar-active-bg-color: var(--next-color-menu-hover) !important; 31 | --w-e-toolbar-border-color: var(--el-border-color-light, #ebeef5) !important; 32 | 33 | // modal - css vars 34 | --w-e-modal-button-bg-color: var(--el-color-primary) !important; 35 | --w-e-modal-button-border-color: var(--el-color-primary) !important; 36 | } 37 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 15 | 16 | vue-next-admin 17 | 18 | 19 |
20 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/layout/navMenu/subItem.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 50 | -------------------------------------------------------------------------------- /src/views/pages/steps/index.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 43 | -------------------------------------------------------------------------------- /src/theme/media/scrollbar.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于768px 4 | ------------------------------- */ 5 | @media screen and (max-width: $sm) { 6 | // 滚动条的宽度 7 | ::-webkit-scrollbar { 8 | width: 3px !important; 9 | height: 3px !important; 10 | } 11 | ::-webkit-scrollbar-track-piece { 12 | background-color: var(--next-bg-main-color); 13 | } 14 | // 滚动条的设置 15 | ::-webkit-scrollbar-thumb { 16 | background-color: rgba(144, 147, 153, 0.3); 17 | background-clip: padding-box; 18 | min-height: 28px; 19 | border-radius: 5px; 20 | transition: 0.3s background-color; 21 | } 22 | ::-webkit-scrollbar-thumb:hover { 23 | background-color: rgba(144, 147, 153, 0.5); 24 | } 25 | // element plus scrollbar 26 | .el-scrollbar__bar.is-vertical { 27 | width: 2px !important; 28 | } 29 | .el-scrollbar__bar.is-horizontal { 30 | height: 2px !important; 31 | } 32 | } 33 | 34 | /* 页面宽度大于768px 35 | ------------------------------- */ 36 | @media screen and (min-width: 769px) { 37 | // 滚动条的宽度 38 | ::-webkit-scrollbar { 39 | width: 7px; 40 | height: 7px; 41 | } 42 | ::-webkit-scrollbar-track-piece { 43 | background-color: var(--next-bg-main-color); 44 | } 45 | // 滚动条的设置 46 | ::-webkit-scrollbar-thumb { 47 | background-color: rgba(144, 147, 153, 0.3); 48 | background-clip: padding-box; 49 | min-height: 28px; 50 | border-radius: 5px; 51 | transition: 0.3s background-color; 52 | } 53 | ::-webkit-scrollbar-thumb:hover { 54 | background-color: rgba(144, 147, 153, 0.5); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/views/make/svgDemo/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 52 | -------------------------------------------------------------------------------- /src/views/fun/cropper/index.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 43 | 44 | 57 | -------------------------------------------------------------------------------- /src/views/personal/mock.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 消息通知 3 | * @returns 返回模拟数据 4 | */ 5 | export const newsInfoList = [ 6 | { 7 | title: '[发布] 2021年02月28日发布基于 vue3.x + vite v1.0.0 版本', 8 | date: '02/28', 9 | link: 'https://gitee.com/lyt-top/vue-next-admin', 10 | }, 11 | { 12 | title: '[发布] 2021年04月15日发布 vue2.x + webpack 重构版本', 13 | date: '04/15', 14 | link: 'https://gitee.com/lyt-top/vue-next-admin/tree/vue-prev-admin/', 15 | }, 16 | { 17 | title: '[重构] 2021年04月10日 重构 vue2.x + webpack v1.0.0 版本', 18 | date: '04/10', 19 | link: 'https://gitee.com/lyt-top/vue-next-admin/tree/vue-prev-admin/', 20 | }, 21 | { 22 | title: '[预览] 2020年12月08日,基于 vue3.x 版本后台模板的预览', 23 | date: '12/08', 24 | link: 'http://lyt-top.gitee.io/vue-next-admin-preview/#/login', 25 | }, 26 | { 27 | title: '[预览] 2020年11月15日,基于 vue2.x 版本后台模板的预览', 28 | date: '11/15', 29 | link: 'https://lyt-top.gitee.io/vue-prev-admin-preview/#/login', 30 | }, 31 | ]; 32 | 33 | /** 34 | * 营销推荐 35 | * @returns 返回模拟数据 36 | */ 37 | export const recommendList = [ 38 | { 39 | title: '优惠券', 40 | msg: '现金券、折扣券、营销必备', 41 | icon: 'ele-Food', 42 | bg: '#48D18D', 43 | iconColor: '#64d89d', 44 | }, 45 | { 46 | title: '多人拼团', 47 | msg: '社交电商、开辟流量', 48 | icon: 'ele-ShoppingCart', 49 | bg: '#F95959', 50 | iconColor: '#F86C6B', 51 | }, 52 | { 53 | title: '分销中心', 54 | msg: '轻松招募分销员,成功推广奖励', 55 | icon: 'ele-School', 56 | bg: '#8595F4', 57 | iconColor: '#92A1F4', 58 | }, 59 | { 60 | title: '秒杀', 61 | msg: '超低价抢购引导更多销量', 62 | icon: 'ele-AlarmClock', 63 | bg: '#FEBB50', 64 | iconColor: '#FDC566', 65 | }, 66 | ]; 67 | -------------------------------------------------------------------------------- /src/views/pages/workflow/component/drawer/line.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 57 | -------------------------------------------------------------------------------- /src/theme/loading.scss: -------------------------------------------------------------------------------- 1 | .loading-next { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | .loading-next .loading-next-box { 6 | position: absolute; 7 | top: 50%; 8 | left: 50%; 9 | transform: translate(-50%, -50%); 10 | } 11 | .loading-next .loading-next-box-warp { 12 | width: 80px; 13 | height: 80px; 14 | } 15 | .loading-next .loading-next-box-warp .loading-next-box-item { 16 | width: 33.333333%; 17 | height: 33.333333%; 18 | background: var(--el-color-primary); 19 | float: left; 20 | animation: loading-next-animation 1.2s infinite ease; 21 | border-radius: 1px; 22 | } 23 | .loading-next .loading-next-box-warp .loading-next-box-item:nth-child(7) { 24 | animation-delay: 0s; 25 | } 26 | .loading-next .loading-next-box-warp .loading-next-box-item:nth-child(4), 27 | .loading-next .loading-next-box-warp .loading-next-box-item:nth-child(8) { 28 | animation-delay: 0.1s; 29 | } 30 | .loading-next .loading-next-box-warp .loading-next-box-item:nth-child(1), 31 | .loading-next .loading-next-box-warp .loading-next-box-item:nth-child(5), 32 | .loading-next .loading-next-box-warp .loading-next-box-item:nth-child(9) { 33 | animation-delay: 0.2s; 34 | } 35 | .loading-next .loading-next-box-warp .loading-next-box-item:nth-child(2), 36 | .loading-next .loading-next-box-warp .loading-next-box-item:nth-child(6) { 37 | animation-delay: 0.3s; 38 | } 39 | .loading-next .loading-next-box-warp .loading-next-box-item:nth-child(3) { 40 | animation-delay: 0.4s; 41 | } 42 | @keyframes loading-next-animation { 43 | 0%, 44 | 70%, 45 | 100% { 46 | transform: scale3D(1, 1, 1); 47 | } 48 | 35% { 49 | transform: scale3D(0, 0, 1); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/utils/watermark.ts: -------------------------------------------------------------------------------- 1 | // 页面添加水印效果 2 | const setWatermark = (str: string) => { 3 | const id = '1.23452384164.123412416'; 4 | if (document.getElementById(id) !== null) document.body.removeChild(document.getElementById(id)); 5 | const can = document.createElement('canvas'); 6 | can.width = 200; 7 | can.height = 130; 8 | const cans = can.getContext('2d'); 9 | cans.rotate((-20 * Math.PI) / 180); 10 | cans.font = '12px Vedana'; 11 | cans.fillStyle = 'rgba(200, 200, 200, 0.30)'; 12 | cans.textBaseline = 'middle'; 13 | cans.fillText(str, can.width / 10, can.height / 2); 14 | const div = document.createElement('div'); 15 | div.id = id; 16 | div.style.pointerEvents = 'none'; 17 | div.style.top = '0px'; 18 | div.style.left = '0px'; 19 | div.style.position = 'fixed'; 20 | div.style.zIndex = '10000000'; 21 | div.style.width = `${document.documentElement.clientWidth}px`; 22 | div.style.height = `${document.documentElement.clientHeight}px`; 23 | div.style.background = `url(${can.toDataURL('image/png')}) left top repeat`; 24 | document.body.appendChild(div); 25 | return id; 26 | }; 27 | 28 | /** 29 | * 页面添加水印效果 30 | * @method set 设置水印 31 | * @method del 删除水印 32 | */ 33 | const watermark = { 34 | // 设置水印 35 | set: (str: string) => { 36 | let id = setWatermark(str); 37 | if (document.getElementById(id) === null) id = setWatermark(str); 38 | }, 39 | // 删除水印 40 | del: () => { 41 | let id = '1.23452384164.123412416'; 42 | if (document.getElementById(id) !== null) document.body.removeChild(document.getElementById(id)); 43 | }, 44 | }; 45 | 46 | // 导出方法 47 | export default watermark; 48 | -------------------------------------------------------------------------------- /src/views/login/component/scan.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 36 | 37 | 64 | -------------------------------------------------------------------------------- /src/views/fun/gridLayout/index.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 47 | 48 | 56 | -------------------------------------------------------------------------------- /src/theme/media/login.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于1200px 4 | ------------------------------- */ 5 | @media screen and (max-width: $lg) and (min-width: $xs) { 6 | .login-container { 7 | .login-left { 8 | .login-left-img { 9 | top: 90% !important; 10 | left: 12% !important; 11 | width: 30% !important; 12 | height: 18% !important; 13 | } 14 | } 15 | .login-right { 16 | position: absolute; 17 | top: 50%; 18 | left: 50%; 19 | transform: translate(-50%, -50%); 20 | } 21 | } 22 | } 23 | 24 | /* 页面宽度小于576px 25 | ------------------------------- */ 26 | @media screen and (max-width: $xs) { 27 | .login-container { 28 | .login-left { 29 | display: none; 30 | } 31 | .login-right { 32 | width: 100% !important; 33 | .login-right-warp { 34 | width: 100% !important; 35 | height: 100% !important; 36 | border: none !important; 37 | .login-right-warp-mian { 38 | .el-form-item { 39 | display: flex !important; 40 | } 41 | .login-right-warp-main-title { 42 | font-size: 20px !important; 43 | } 44 | } 45 | .login-right-warp-one { 46 | &::after { 47 | right: 0 !important; 48 | } 49 | } 50 | .login-right-warp-two { 51 | &::before { 52 | bottom: 1px !important; 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | /* 页面宽度小于375px 61 | ------------------------------- */ 62 | @media screen and (max-width: $us) { 63 | .login-container { 64 | .login-right { 65 | .login-right-warp { 66 | .login-right-warp-mian { 67 | .login-right-warp-main-title { 68 | font-size: 18px !important; 69 | } 70 | } 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/views/fun/qrcode/index.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 52 | 53 | 70 | -------------------------------------------------------------------------------- /src/layout/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 51 | -------------------------------------------------------------------------------- /src/utils/storage.ts: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie'; 2 | 3 | /** 4 | * window.localStorage 浏览器永久缓存 5 | * @method set 设置永久缓存 6 | * @method get 获取永久缓存 7 | * @method remove 移除永久缓存 8 | * @method clear 移除全部永久缓存 9 | */ 10 | export const Local = { 11 | // 查看 v2.4.3版本更新日志 12 | setKey(key: string) { 13 | // @ts-ignore 14 | return `${__NEXT_NAME__}:${key}`; 15 | }, 16 | // 设置永久缓存 17 | set(key: string, val: T) { 18 | window.localStorage.setItem(Local.setKey(key), JSON.stringify(val)); 19 | }, 20 | // 获取永久缓存 21 | get(key: string) { 22 | let json = window.localStorage.getItem(Local.setKey(key)); 23 | return JSON.parse(json); 24 | }, 25 | // 移除永久缓存 26 | remove(key: string) { 27 | window.localStorage.removeItem(Local.setKey(key)); 28 | }, 29 | // 移除全部永久缓存 30 | clear() { 31 | window.localStorage.clear(); 32 | }, 33 | }; 34 | 35 | /** 36 | * window.sessionStorage 浏览器临时缓存 37 | * @method set 设置临时缓存 38 | * @method get 获取临时缓存 39 | * @method remove 移除临时缓存 40 | * @method clear 移除全部临时缓存 41 | */ 42 | export const Session = { 43 | // 设置临时缓存 44 | set(key: string, val: T) { 45 | if (key === 'token') return Cookies.set(key, val); 46 | window.sessionStorage.setItem(Local.setKey(key), JSON.stringify(val)); 47 | }, 48 | // 获取临时缓存 49 | get(key: string) { 50 | if (key === 'token') return Cookies.get(key); 51 | let json = window.sessionStorage.getItem(Local.setKey(key)); 52 | return JSON.parse(json); 53 | }, 54 | // 移除临时缓存 55 | remove(key: string) { 56 | if (key === 'token') return Cookies.remove(key); 57 | window.sessionStorage.removeItem(Local.setKey(key)); 58 | }, 59 | // 移除全部临时缓存 60 | clear() { 61 | Cookies.remove('token'); 62 | window.sessionStorage.clear(); 63 | }, 64 | }; 65 | -------------------------------------------------------------------------------- /src/layout/main/transverse.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 61 | -------------------------------------------------------------------------------- /src/views/pages/drag/index.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 48 | 49 | 60 | -------------------------------------------------------------------------------- /src/views/pages/workflow/component/drawer/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 64 | -------------------------------------------------------------------------------- /src/views/visualizing/mock/demo1.ts: -------------------------------------------------------------------------------- 1 | // 地图模拟数据 2 | export const echartsMapList = [ 3 | { name: '深圳市人民政府', value: '100' }, 4 | { name: '莲花山公园', value: '100' }, 5 | { name: '世界之窗', value: '100' }, 6 | { name: '华侨城欢乐谷', value: '100' }, 7 | { name: '宝安区西乡', value: '100' }, 8 | ]; 9 | 10 | // 地图经纬度数据 11 | export const echartsMapData: object = { 12 | 深圳市人民政府: [114.064524, 22.549225], 13 | 莲花山公园: [114.0658, 22.560072], 14 | 世界之窗: [113.979419, 22.540579], 15 | 华侨城欢乐谷: [113.986066, 22.548056], 16 | 宝安区西乡: [113.869053, 22.581714], 17 | }; 18 | 19 | // 地图图片显示 20 | export const echartsMapImgs = [ 21 | { 22 | url: 'https://img1.baidu.com/it/u=2425496005,2401702709&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500', 23 | name: '深圳市人民政府', 24 | add: '深圳市福田区福中三路市民中心C区', 25 | dec: '深圳市人民政府是根据《中华人民共和国地方各级人民代表大会和地方各级人民政府组织法》设立的,是深圳市人民代表大会的执行机关,是深圳市的国家行政机关。', 26 | }, 27 | { 28 | url: 'https://img0.baidu.com/it/u=2666213152,2487785512&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500', 29 | name: '莲花山公园', 30 | add: '广东省深圳市福田区莲花街道莲花北社区红荔路6030号', 31 | dec: '莲花山公园筹建于1992年10月10日 ,1997年6月23日正式对外局部开放。', 32 | }, 33 | { 34 | url: 'https://img1.baidu.com/it/u=1595204841,1838139326&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500', 35 | name: '世界之窗', 36 | add: '深圳市南山区深南大道9037号', 37 | dec: '这里,世界首座实景拍摄悬空式球幕影院“飞跃美利坚””,为游客提供集休闲放松于一体的都市时尚生活空间。', 38 | }, 39 | { 40 | url: 'https://img0.baidu.com/it/u=1586832283,2276617306&fm=253&fmt=auto&app=138&f=JPEG?w=476&h=500', 41 | name: '华侨城欢乐谷', 42 | add: '广东省深圳市南山区沙河街道星河街社区侨城西街1号', 43 | dec: '深圳欢乐谷注重满足人们参与、体验的新型诱游需求,营造出自然、清新、活泼、惊奇、热烈、刺激的休闲旅游氛围。', 44 | }, 45 | { 46 | url: 'https://img0.baidu.com/it/u=2899429152,3158963267&fm=253&fmt=auto&app=138&f=JPEG?w=200&h=200', 47 | name: '宝安区西乡', 48 | add: '西乡街道下辖25个社区', 49 | dec: '西乡街道,隶属于广东省深圳市宝安区,位于宝安区西南部,东接石岩街道,南接新安街道,西至珠江口岸边,北接航城街道。', 50 | }, 51 | ]; 52 | -------------------------------------------------------------------------------- /src/utils/request.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosInstance } from 'axios'; 2 | import { ElMessage, ElMessageBox } from 'element-plus'; 3 | import { Session } from '/@/utils/storage'; 4 | import qs from 'qs'; 5 | 6 | // 配置新建一个 axios 实例 7 | const service: AxiosInstance = axios.create({ 8 | baseURL: import.meta.env.VITE_API_URL, 9 | timeout: 50000, 10 | headers: { 'Content-Type': 'application/json' }, 11 | paramsSerializer: { 12 | serialize(params) { 13 | return qs.stringify(params, { allowDots: true }); 14 | }, 15 | }, 16 | }); 17 | 18 | // 添加请求拦截器 19 | service.interceptors.request.use( 20 | (config) => { 21 | // 在发送请求之前做些什么 token 22 | if (Session.get('token')) { 23 | config.headers!['Authorization'] = `${Session.get('token')}`; 24 | } 25 | return config; 26 | }, 27 | (error) => { 28 | // 对请求错误做些什么 29 | return Promise.reject(error); 30 | } 31 | ); 32 | 33 | // 添加响应拦截器 34 | service.interceptors.response.use( 35 | (response) => { 36 | // 对响应数据做点什么 37 | const res = response.data; 38 | if (res.code && res.code !== 0) { 39 | // `token` 过期或者账号已在别处登录 40 | if (res.code === 401 || res.code === 4001) { 41 | Session.clear(); // 清除浏览器全部临时缓存 42 | window.location.href = '/'; // 去登录页 43 | ElMessageBox.alert('你已被登出,请重新登录', '提示', {}) 44 | .then(() => {}) 45 | .catch(() => {}); 46 | } 47 | return Promise.reject(service.interceptors.response); 48 | } else { 49 | return res; 50 | } 51 | }, 52 | (error) => { 53 | // 对响应错误做点什么 54 | if (error.message.indexOf('timeout') != -1) { 55 | ElMessage.error('网络超时'); 56 | } else if (error.message == 'Network Error') { 57 | ElMessage.error('网络连接错误'); 58 | } else { 59 | if (error.response.data) ElMessage.error(error.response.statusText); 60 | else ElMessage.error('接口路径找不到'); 61 | } 62 | return Promise.reject(error); 63 | } 64 | ); 65 | 66 | // 导出 axios 实例 67 | export default service; 68 | -------------------------------------------------------------------------------- /src/views/pages/formRules/component/formRulesTwo.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 46 | -------------------------------------------------------------------------------- /src/utils/arrayOperation.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断两数组字符串是否相同(用于按钮权限验证),数组字符串中存在相同时会自动去重(按钮权限标识不会重复) 3 | * @param news 新数据 4 | * @param old 源数据 5 | * @returns 两数组相同返回 `true`,反之则反 6 | */ 7 | export function judementSameArr(newArr: unknown[] | string[], oldArr: string[]): boolean { 8 | const news = removeDuplicate(newArr); 9 | const olds = removeDuplicate(oldArr); 10 | let count = 0; 11 | const leng = news.length; 12 | for (let i in olds) { 13 | for (let j in news) { 14 | if (olds[i] === news[j]) count++; 15 | } 16 | } 17 | return count === leng ? true : false; 18 | } 19 | 20 | /** 21 | * 判断两个对象是否相同 22 | * @param a 要比较的对象一 23 | * @param b 要比较的对象二 24 | * @returns 相同返回 true,反之则反 25 | */ 26 | export function isObjectValueEqual(a: T, b: T): boolean { 27 | if (!a || !b) return false; 28 | let aProps = Object.getOwnPropertyNames(a); 29 | let bProps = Object.getOwnPropertyNames(b); 30 | if (aProps.length != bProps.length) return false; 31 | for (let i = 0; i < aProps.length; i++) { 32 | let propName = aProps[i]; 33 | let propA = a[propName]; 34 | let propB = b[propName]; 35 | if (!b.hasOwnProperty(propName)) return false; 36 | if (propA instanceof Object) { 37 | if (!isObjectValueEqual(propA, propB)) return false; 38 | } else if (propA !== propB) { 39 | return false; 40 | } 41 | } 42 | return true; 43 | } 44 | 45 | /** 46 | * 数组、数组对象去重 47 | * @param arr 数组内容 48 | * @param attr 需要去重的键值(数组对象) 49 | * @returns 50 | */ 51 | export function removeDuplicate(arr: EmptyArrayType, attr?: string) { 52 | if (!Object.keys(arr).length) { 53 | return arr; 54 | } else { 55 | if (attr) { 56 | const obj: EmptyObjectType = {}; 57 | return arr.reduce((cur: EmptyArrayType[], item: EmptyArrayType) => { 58 | obj[item[attr]] ? '' : (obj[item[attr]] = true && item[attr] && cur.push(item)); 59 | return cur; 60 | }, []); 61 | } else { 62 | return [...new Set(arr)]; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/views/pages/formRules/component/formRulesThree.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 44 | -------------------------------------------------------------------------------- /src/views/fun/wangEditor/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 31 | -------------------------------------------------------------------------------- /src/views/limits/frontEnd/page/index.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 60 | -------------------------------------------------------------------------------- /src/views/pages/formI18n/index.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 54 | -------------------------------------------------------------------------------- /src/components/svgIcon/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 64 | -------------------------------------------------------------------------------- /src/utils/theme.ts: -------------------------------------------------------------------------------- 1 | import { ElMessage } from 'element-plus'; 2 | 3 | /** 4 | * 颜色转换函数 5 | * @method hexToRgb hex 颜色转 rgb 颜色 6 | * @method rgbToHex rgb 颜色转 Hex 颜色 7 | * @method getDarkColor 加深颜色值 8 | * @method getLightColor 变浅颜色值 9 | */ 10 | export function useChangeColor() { 11 | // str 颜色值字符串 12 | const hexToRgb = (str: string): any => { 13 | let hexs: any = ''; 14 | let reg = /^\#?[0-9A-Fa-f]{6}$/; 15 | if (!reg.test(str)) { 16 | ElMessage.warning('输入错误的hex'); 17 | return ''; 18 | } 19 | str = str.replace('#', ''); 20 | hexs = str.match(/../g); 21 | for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16); 22 | return hexs; 23 | }; 24 | // r 代表红色 | g 代表绿色 | b 代表蓝色 25 | const rgbToHex = (r: any, g: any, b: any): string => { 26 | let reg = /^\d{1,3}$/; 27 | if (!reg.test(r) || !reg.test(g) || !reg.test(b)) { 28 | ElMessage.warning('输入错误的rgb颜色值'); 29 | return ''; 30 | } 31 | let hexs = [r.toString(16), g.toString(16), b.toString(16)]; 32 | for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`; 33 | return `#${hexs.join('')}`; 34 | }; 35 | // color 颜色值字符串 | level 变浅的程度,限0-1之间 36 | const getDarkColor = (color: string, level: number): string => { 37 | let reg = /^\#?[0-9A-Fa-f]{6}$/; 38 | if (!reg.test(color)) { 39 | ElMessage.warning('输入错误的hex颜色值'); 40 | return ''; 41 | } 42 | let rgb = useChangeColor().hexToRgb(color); 43 | for (let i = 0; i < 3; i++) rgb[i] = Math.floor(rgb[i] * (1 - level)); 44 | return useChangeColor().rgbToHex(rgb[0], rgb[1], rgb[2]); 45 | }; 46 | // color 颜色值字符串 | level 加深的程度,限0-1之间 47 | const getLightColor = (color: string, level: number): string => { 48 | let reg = /^\#?[0-9A-Fa-f]{6}$/; 49 | if (!reg.test(color)) { 50 | ElMessage.warning('输入错误的hex颜色值'); 51 | return ''; 52 | } 53 | let rgb = useChangeColor().hexToRgb(color); 54 | for (let i = 0; i < 3; i++) rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i]); 55 | return useChangeColor().rgbToHex(rgb[0], rgb[1], rgb[2]); 56 | }; 57 | return { 58 | hexToRgb, 59 | rgbToHex, 60 | getDarkColor, 61 | getLightColor, 62 | }; 63 | } 64 | -------------------------------------------------------------------------------- /src/utils/commonFunction.ts: -------------------------------------------------------------------------------- 1 | // 通用函数 2 | import useClipboard from 'vue-clipboard3'; 3 | import { ElMessage } from 'element-plus'; 4 | import { formatDate } from '/@/utils/formatTime'; 5 | import { useI18n } from 'vue-i18n'; 6 | 7 | export default function () { 8 | const { t } = useI18n(); 9 | const { toClipboard } = useClipboard(); 10 | 11 | // 百分比格式化 12 | const percentFormat = (row: EmptyArrayType, column: number, cellValue: string) => { 13 | return cellValue ? `${cellValue}%` : '-'; 14 | }; 15 | // 列表日期时间格式化 16 | const dateFormatYMD = (row: EmptyArrayType, column: number, cellValue: string) => { 17 | if (!cellValue) return '-'; 18 | return formatDate(new Date(cellValue), 'YYYY-mm-dd'); 19 | }; 20 | // 列表日期时间格式化 21 | const dateFormatYMDHMS = (row: EmptyArrayType, column: number, cellValue: string) => { 22 | if (!cellValue) return '-'; 23 | return formatDate(new Date(cellValue), 'YYYY-mm-dd HH:MM:SS'); 24 | }; 25 | // 列表日期时间格式化 26 | const dateFormatHMS = (row: EmptyArrayType, column: number, cellValue: string) => { 27 | if (!cellValue) return '-'; 28 | let time = 0; 29 | if (typeof row === 'number') time = row; 30 | if (typeof cellValue === 'number') time = cellValue; 31 | return formatDate(new Date(time * 1000), 'HH:MM:SS'); 32 | }; 33 | // 小数格式化 34 | const scaleFormat = (value: string = '0', scale: number = 4) => { 35 | return Number.parseFloat(value).toFixed(scale); 36 | }; 37 | // 小数格式化 38 | const scale2Format = (value: string = '0') => { 39 | return Number.parseFloat(value).toFixed(2); 40 | }; 41 | // 点击复制文本 42 | const copyText = (text: string) => { 43 | return new Promise((resolve, reject) => { 44 | try { 45 | //复制 46 | toClipboard(text); 47 | //下面可以设置复制成功的提示框等操作 48 | ElMessage.success(t('message.layout.copyTextSuccess')); 49 | resolve(text); 50 | } catch (e) { 51 | //复制失败 52 | ElMessage.error(t('message.layout.copyTextError')); 53 | reject(e); 54 | } 55 | }); 56 | }; 57 | return { 58 | percentFormat, 59 | dateFormatYMD, 60 | dateFormatYMDHMS, 61 | dateFormatHMS, 62 | scaleFormat, 63 | scale2Format, 64 | copyText, 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /src/layout/logo/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 32 | 33 | 76 | -------------------------------------------------------------------------------- /src/i18n/index.ts: -------------------------------------------------------------------------------- 1 | import { createI18n } from 'vue-i18n'; 2 | import pinia from '/@/stores/index'; 3 | import { storeToRefs } from 'pinia'; 4 | import { useThemeConfig } from '/@/stores/themeConfig'; 5 | 6 | // 定义语言国际化内容 7 | 8 | /** 9 | * 说明: 10 | * 须在 pages 下新建文件夹(建议 `要国际化界面目录` 与 `i18n 目录` 相同,方便查找), 11 | * 注意国际化定义的字段,不要与原有的定义字段相同。 12 | * 1、/src/i18n/lang 下的 ts 为框架的国际化内容 13 | * 2、/src/i18n/pages 下的 ts 为各界面的国际化内容 14 | */ 15 | 16 | // element plus 自带国际化 17 | import enLocale from 'element-plus/lib/locale/lang/en'; 18 | import zhcnLocale from 'element-plus/lib/locale/lang/zh-cn'; 19 | import zhtwLocale from 'element-plus/lib/locale/lang/zh-tw'; 20 | 21 | // 定义变量内容 22 | const messages = {}; 23 | const element = { en: enLocale, 'zh-cn': zhcnLocale, 'zh-tw': zhtwLocale }; 24 | const itemize = { en: [], 'zh-cn': [], 'zh-tw': [] }; 25 | const modules: Record = import.meta.glob('./**/*.ts', { eager: true }); 26 | 27 | // 对自动引入的 modules 进行分类 en、zh-cn、zh-tw 28 | // https://vitejs.cn/vite3-cn/guide/features.html#glob-import 29 | for (const path in modules) { 30 | const key = path.match(/(\S+)\/(\S+).ts/); 31 | if (itemize[key![2]]) itemize[key![2]].push(modules[path].default); 32 | else itemize[key![2]] = modules[path]; 33 | } 34 | 35 | // 合并数组对象(非标准数组对象,数组中对象的每项 key、value 都不同) 36 | function mergeArrObj(list: T, key: string) { 37 | let obj = {}; 38 | list[key].forEach((i: EmptyObjectType) => { 39 | obj = Object.assign({}, obj, i); 40 | }); 41 | return obj; 42 | } 43 | 44 | // 处理最终格式 45 | for (const key in itemize) { 46 | messages[key] = { 47 | name: key, 48 | el: element[key].el, 49 | message: mergeArrObj(itemize, key), 50 | }; 51 | } 52 | 53 | // 读取 pinia 默认语言 54 | const stores = useThemeConfig(pinia); 55 | const { themeConfig } = storeToRefs(stores); 56 | 57 | // 导出语言国际化 58 | // https://vue-i18n.intlify.dev/guide/essentials/fallback.html#explicit-fallback-with-one-locale 59 | export const i18n = createI18n({ 60 | legacy: false, 61 | silentTranslationWarn: true, 62 | missingWarn: false, 63 | silentFallbackWarn: true, 64 | fallbackWarn: false, 65 | locale: themeConfig.value.globalI18n, 66 | fallbackLocale: zhcnLocale.name, 67 | messages, 68 | }); 69 | -------------------------------------------------------------------------------- /src/views/login/component/mobile.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 43 | 44 | 71 | -------------------------------------------------------------------------------- /src/components/iconSelector/list.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 44 | 45 | 85 | -------------------------------------------------------------------------------- /src/views/pages/workflow/component/tool/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 44 | 45 | 75 | -------------------------------------------------------------------------------- /src/layout/main/defaults.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 74 | -------------------------------------------------------------------------------- /src/views/pages/dynamicForm/mock.ts: -------------------------------------------------------------------------------- 1 | // 表单数据选项(自行扩展) 2 | export const formData = [ 3 | { 4 | label: '姓名', 5 | prop: 'name', 6 | placeholder: '请输入姓名', 7 | clearable: true, 8 | disabled: false, 9 | required: true, 10 | type: 'input', 11 | i18n: false, 12 | i18nText: '', 13 | isShow: true, 14 | xs: 24, 15 | sm: 12, 16 | md: 8, 17 | lg: 6, 18 | xl: 4, 19 | }, 20 | { 21 | label: '邮箱', 22 | prop: 'email', 23 | placeholder: '请输入用户邮箱', 24 | clearable: true, 25 | disabled: false, 26 | required: true, 27 | type: 'input', 28 | i18n: false, 29 | i18nText: '', 30 | isShow: true, 31 | xs: 24, 32 | sm: 12, 33 | md: 8, 34 | lg: 6, 35 | xl: 4, 36 | }, 37 | { 38 | label: '登陆时间', 39 | prop: 'autograph', 40 | placeholder: '选择时间', 41 | clearable: true, 42 | disabled: false, 43 | required: true, 44 | type: 'date', 45 | i18n: false, 46 | i18nText: '', 47 | isShow: true, 48 | xs: 24, 49 | sm: 12, 50 | md: 8, 51 | lg: 6, 52 | xl: 4, 53 | }, 54 | { 55 | label: '职务', 56 | prop: 'occupation', 57 | placeholder: '请选择职务', 58 | clearable: true, 59 | disabled: false, 60 | required: true, 61 | type: 'select', 62 | i18n: false, 63 | i18nText: '', 64 | options: [ 65 | { 66 | label: '计算机 / 互联网 / 通信', 67 | value: '1', 68 | }, 69 | { 70 | label: '生产 / 工艺 / 制造', 71 | value: '2', 72 | }, 73 | { 74 | label: '医疗 / 护理 / 制药', 75 | value: '3', 76 | }, 77 | ], 78 | isShow: true, 79 | xs: 24, 80 | sm: 12, 81 | md: 8, 82 | lg: 6, 83 | xl: 4, 84 | }, 85 | { 86 | label: '', 87 | prop: '', 88 | placeholder: '', 89 | clearable: true, 90 | disabled: false, 91 | required: true, 92 | type: '', 93 | i18n: false, 94 | i18nText: '', 95 | isShow: true, 96 | xs: 24, 97 | sm: 24, 98 | md: 24, 99 | lg: 24, 100 | xl: 24, 101 | }, 102 | { 103 | label: '备注', 104 | prop: 'remarks', 105 | placeholder: '请输入', 106 | clearable: true, 107 | disabled: false, 108 | required: true, 109 | type: 'textarea', 110 | i18n: false, 111 | i18nText: '', 112 | isShow: true, 113 | xs: 24, 114 | sm: 24, 115 | md: 24, 116 | lg: 24, 117 | xl: 24, 118 | }, 119 | ]; 120 | -------------------------------------------------------------------------------- /src/layout/main/classic.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 74 | -------------------------------------------------------------------------------- /src/views/visualizing/mock/demo2.ts: -------------------------------------------------------------------------------- 1 | // 顶部下来菜单 2 | export const dropdownList: Array = [ 3 | { 4 | label: '广东省农业农村厅', 5 | }, 6 | { 7 | label: '广西省农业农村厅', 8 | }, 9 | { 10 | label: '四川省农业农村厅', 11 | }, 12 | { 13 | label: '湖北省农业农村厅', 14 | }, 15 | { 16 | label: '福建省农业农村厅', 17 | }, 18 | { 19 | label: '山东省农业农村厅', 20 | }, 21 | { 22 | label: '江西省农业农村厅', 23 | }, 24 | ]; 25 | 26 | // sky 天气 27 | export const skyList: Array = [ 28 | { 29 | v1: '时间', 30 | v2: '天气', 31 | v3: '温度', 32 | v4: '湿度', 33 | v5: '降水概率', 34 | v6: '风向', 35 | v7: '风力', 36 | type: 'title', 37 | }, 38 | { 39 | v1: '今天', 40 | v2: 'ele-Sunny', 41 | v3: '20°/26°', 42 | v4: '80%', 43 | v5: '50%', 44 | v6: '东南风', 45 | v7: '13m/s', 46 | }, 47 | { 48 | v1: '明天', 49 | v2: 'ele-Lightning', 50 | v3: '20°/26°', 51 | v4: '80%', 52 | v5: '50%', 53 | v6: '东南风', 54 | v7: '13m/s', 55 | }, 56 | { 57 | v1: '后天', 58 | v2: 'ele-Sunny', 59 | v3: '20°/26°', 60 | v4: '80%', 61 | v5: '50%', 62 | v6: '东南风', 63 | v7: '13m/s', 64 | }, 65 | ]; 66 | 67 | // 当前设置状态 68 | export const dBtnList: Array = [ 69 | { 70 | v1: '地块A-灌溉', 71 | v2: '阳光玫瑰种植', 72 | v3: '126天', 73 | v4: '设备在线', 74 | }, 75 | { 76 | v1: '地块B-收割', 77 | v2: '阳光玫瑰种植', 78 | v3: '360天', 79 | v4: '设备预警', 80 | }, 81 | ]; 82 | 83 | // 当前设备监测 84 | export const chartData4List: Array = [ 85 | { 86 | label: '温度', 87 | }, 88 | { 89 | label: '光照', 90 | }, 91 | { 92 | label: '湿度', 93 | }, 94 | { 95 | label: '风力', 96 | }, 97 | { 98 | label: '张力', 99 | }, 100 | { 101 | label: '气压', 102 | }, 103 | ]; 104 | 105 | // 3DEarth 地图周围按钮组 106 | export const earth3DBtnList: Array = [ 107 | { 108 | topLevelClass: 'fixed-top', 109 | icon: 'ele-MagicStick', 110 | label: '环境监测', 111 | type: 0, 112 | }, 113 | { 114 | topLevelClass: 'fixed-right', 115 | icon: 'ele-MoonNight', 116 | label: '精准管理', 117 | type: 1, 118 | }, 119 | { 120 | topLevelClass: 'fixed-bottom', 121 | icon: 'ele-TrendCharts', 122 | label: '数据报表', 123 | type: 2, 124 | }, 125 | { 126 | topLevelClass: 'fixed-left', 127 | icon: 'ele-Van', 128 | label: '产品追溯', 129 | type: 3, 130 | }, 131 | ]; 132 | -------------------------------------------------------------------------------- /src/theme/media/chart.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于768px 4 | ------------------------------- */ 5 | @media screen and (max-width: $sm) { 6 | .big-data-down-left { 7 | width: 100% !important; 8 | flex-direction: unset !important; 9 | flex-wrap: wrap; 10 | .flex-warp-item { 11 | min-height: 196.24px; 12 | padding: 0 7.5px 15px 15px !important; 13 | .flex-warp-item-box { 14 | border: none !important; 15 | border-bottom: 1px solid #ebeef5 !important; 16 | } 17 | } 18 | } 19 | .big-data-down-center { 20 | width: 100% !important; 21 | .big-data-down-center-one, 22 | .big-data-down-center-two { 23 | min-height: 196.24px; 24 | padding-left: 15px !important; 25 | .big-data-down-center-one-content { 26 | border: none !important; 27 | border-bottom: 1px solid #ebeef5 !important; 28 | } 29 | .flex-warp-item-box { 30 | @extend .big-data-down-center-one-content; 31 | } 32 | } 33 | } 34 | .big-data-down-right { 35 | .flex-warp-item { 36 | .flex-warp-item-box { 37 | border: none !important; 38 | border-bottom: 1px solid #ebeef5 !important; 39 | } 40 | &:nth-of-type(2) { 41 | padding-left: 15px !important; 42 | } 43 | &:last-of-type { 44 | .flex-warp-item-box { 45 | border: none !important; 46 | } 47 | } 48 | } 49 | } 50 | } 51 | 52 | /* 页面宽度大于768px小于1200px 53 | ------------------------------- */ 54 | @media screen and (min-width: $sm) and (max-width: $lg) { 55 | .chart-warp-bottom { 56 | .big-data-down-left { 57 | width: 50% !important; 58 | } 59 | .big-data-down-center { 60 | width: 50% !important; 61 | } 62 | .big-data-down-right { 63 | .flex-warp-item { 64 | width: 50% !important; 65 | &:nth-of-type(2) { 66 | padding-left: 7.5px !important; 67 | } 68 | } 69 | } 70 | } 71 | } 72 | 73 | /* 页面宽度小于1200px 74 | ------------------------------- */ 75 | @media screen and (max-width: $lg) { 76 | .chart-warp-top { 77 | .up-left { 78 | display: none; 79 | } 80 | } 81 | .chart-warp-bottom { 82 | overflow-y: auto !important; 83 | flex-wrap: wrap; 84 | .big-data-down-right { 85 | width: 100% !important; 86 | flex-direction: unset !important; 87 | flex-wrap: wrap; 88 | .flex-warp-item { 89 | min-height: 196.24px; 90 | padding: 0 7.5px 15px 15px !important; 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/layout/main/columns.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 74 | -------------------------------------------------------------------------------- /src/layout/component/main.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 66 | -------------------------------------------------------------------------------- /src/types/pinia.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * pinia 类型定义 3 | */ 4 | 5 | // 用户信息 6 | declare interface UserInfos { 7 | authBtnList: string[]; 8 | photo: string; 9 | roles: string[]; 10 | time: number; 11 | userName: string; 12 | [key: string]: T; 13 | } 14 | declare interface UserInfosState { 15 | userInfos: UserInfos; 16 | } 17 | 18 | // 路由缓存列表 19 | declare interface KeepAliveNamesState { 20 | keepAliveNames: string[]; 21 | cachedViews: string[]; 22 | } 23 | 24 | // 后端返回原始路由(未处理时) 25 | declare interface RequestOldRoutesState { 26 | requestOldRoutes: string[]; 27 | } 28 | 29 | // TagsView 路由列表 30 | declare interface TagsViewRoutesState { 31 | tagsViewRoutes: T[]; 32 | isTagsViewCurrenFull: Boolean; 33 | } 34 | 35 | // 路由列表 36 | declare interface RoutesListState { 37 | routesList: T[]; 38 | isColumnsMenuHover: Boolean; 39 | isColumnsNavHover: Boolean; 40 | } 41 | 42 | // 布局配置 43 | declare interface ThemeConfigState { 44 | themeConfig: { 45 | isDrawer: boolean; 46 | primary: string; 47 | topBar: string; 48 | topBarColor: string; 49 | isTopBarColorGradual: boolean; 50 | menuBar: string; 51 | menuBarColor: string; 52 | menuBarActiveColor: string; 53 | isMenuBarColorGradual: boolean; 54 | columnsMenuBar: string; 55 | columnsMenuBarColor: string; 56 | isColumnsMenuBarColorGradual: boolean; 57 | isColumnsMenuHoverPreload: boolean; 58 | isCollapse: boolean; 59 | isUniqueOpened: boolean; 60 | isFixedHeader: boolean; 61 | isFixedHeaderChange: boolean; 62 | isClassicSplitMenu: boolean; 63 | isLockScreen: boolean; 64 | lockScreenTime: number; 65 | isShowLogo: boolean; 66 | isShowLogoChange: boolean; 67 | isBreadcrumb: boolean; 68 | isTagsview: boolean; 69 | isBreadcrumbIcon: boolean; 70 | isTagsviewIcon: boolean; 71 | isCacheTagsView: boolean; 72 | isSortableTagsView: boolean; 73 | isShareTagsView: boolean; 74 | isFooter: boolean; 75 | isGrayscale: boolean; 76 | isInvert: boolean; 77 | isIsDark: boolean; 78 | isWartermark: boolean; 79 | wartermarkText: string; 80 | tagsStyle: string; 81 | animation: string; 82 | columnsAsideStyle: string; 83 | columnsAsideLayout: string; 84 | layout: string; 85 | isRequestRoutes: boolean; 86 | globalTitle: string; 87 | globalViceTitle: string; 88 | globalViceTitleMsg: string; 89 | globalI18n: string; 90 | globalComponentSize: string; 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /src/views/pages/awesome/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 38 | 39 | 82 | -------------------------------------------------------------------------------- /src/views/pages/iocnfont/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 38 | 39 | 82 | -------------------------------------------------------------------------------- /src/views/error/404.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 34 | 35 | 90 | -------------------------------------------------------------------------------- /src/stores/userInfo.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | import Cookies from 'js-cookie'; 3 | import { Session } from '/@/utils/storage'; 4 | 5 | /** 6 | * 用户信息 7 | * @methods setUserInfos 设置用户信息 8 | */ 9 | export const useUserInfo = defineStore('userInfo', { 10 | state: (): UserInfosState => ({ 11 | userInfos: { 12 | userName: '', 13 | photo: '', 14 | time: 0, 15 | roles: [], 16 | authBtnList: [], 17 | }, 18 | }), 19 | actions: { 20 | async setUserInfos() { 21 | // 存储用户信息到浏览器缓存 22 | if (Session.get('userInfo')) { 23 | this.userInfos = Session.get('userInfo'); 24 | } else { 25 | const userInfos = await this.getApiUserInfo(); 26 | this.userInfos = userInfos; 27 | } 28 | }, 29 | // 模拟接口数据 30 | // https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP 31 | async getApiUserInfo() { 32 | return new Promise((resolve) => { 33 | setTimeout(() => { 34 | // 模拟数据,请求接口时,记得删除多余代码及对应依赖的引入 35 | const userName = Cookies.get('userName'); 36 | // 模拟数据 37 | let defaultRoles: Array = []; 38 | let defaultAuthBtnList: Array = []; 39 | // admin 页面权限标识,对应路由 meta.roles,用于控制路由的显示/隐藏 40 | let adminRoles: Array = ['admin']; 41 | // admin 按钮权限标识 42 | let adminAuthBtnList: Array = ['btn.add', 'btn.del', 'btn.edit', 'btn.link']; 43 | // test 页面权限标识,对应路由 meta.roles,用于控制路由的显示/隐藏 44 | let testRoles: Array = ['common']; 45 | // test 按钮权限标识 46 | let testAuthBtnList: Array = ['btn.add', 'btn.link']; 47 | // 不同用户模拟不同的用户权限 48 | if (userName === 'admin') { 49 | defaultRoles = adminRoles; 50 | defaultAuthBtnList = adminAuthBtnList; 51 | } else { 52 | defaultRoles = testRoles; 53 | defaultAuthBtnList = testAuthBtnList; 54 | } 55 | // 用户信息模拟数据 56 | const userInfos = { 57 | userName: userName, 58 | photo: 59 | userName === 'admin' 60 | ? 'https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500' 61 | : 'https://img2.baidu.com/it/u=2370931438,70387529&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500', 62 | time: new Date().getTime(), 63 | roles: defaultRoles, 64 | authBtnList: defaultAuthBtnList, 65 | }; 66 | Session.set('userInfo', userInfos); 67 | resolve(userInfos); 68 | }, 0); 69 | }); 70 | }, 71 | }, 72 | }); 73 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import vue from '@vitejs/plugin-vue'; 2 | import { resolve } from 'path'; 3 | import { defineConfig, loadEnv, ConfigEnv } from 'vite'; 4 | import vueSetupExtend from 'vite-plugin-vue-setup-extend-plus'; 5 | import viteCompression from 'vite-plugin-compression'; 6 | import { buildConfig } from './src/utils/build'; 7 | 8 | const pathResolve = (dir: string) => { 9 | return resolve(__dirname, '.', dir); 10 | }; 11 | 12 | const alias: Record = { 13 | '/@': pathResolve('./src/'), 14 | 'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js', 15 | }; 16 | 17 | const viteConfig = defineConfig((mode: ConfigEnv) => { 18 | const env = loadEnv(mode.mode, process.cwd()); 19 | return { 20 | plugins: [vue(), vueSetupExtend(), viteCompression(), JSON.parse(env.VITE_OPEN_CDN) ? buildConfig.cdn() : null], 21 | root: process.cwd(), 22 | resolve: { alias }, 23 | base: mode.command === 'serve' ? './' : env.VITE_PUBLIC_PATH, 24 | optimizeDeps: { exclude: ['vue-demi'] }, 25 | server: { 26 | host: '0.0.0.0', 27 | port: env.VITE_PORT as unknown as number, 28 | open: JSON.parse(env.VITE_OPEN), 29 | hmr: true, 30 | proxy: { 31 | '/gitee': { 32 | target: 'https://gitee.com', 33 | ws: true, 34 | changeOrigin: true, 35 | rewrite: (path) => path.replace(/^\/gitee/, ''), 36 | }, 37 | }, 38 | }, 39 | build: { 40 | outDir: 'dist', 41 | chunkSizeWarningLimit: 1500, 42 | rollupOptions: { 43 | output: { 44 | chunkFileNames: 'assets/js/[name]-[hash].js', 45 | entryFileNames: 'assets/js/[name]-[hash].js', 46 | assetFileNames: 'assets/[ext]/[name]-[hash].[ext]', 47 | manualChunks(id) { 48 | if (id.includes('node_modules')) { 49 | return id.toString().match(/\/node_modules\/(?!.pnpm)(?[^\/]*)\//)?.groups!.moduleName ?? 'vender'; 50 | } 51 | }, 52 | }, 53 | ...(JSON.parse(env.VITE_OPEN_CDN) ? { external: buildConfig.external } : {}), 54 | }, 55 | }, 56 | css: { preprocessorOptions: { css: { charset: false } } }, 57 | define: { 58 | __VUE_I18N_LEGACY_API__: JSON.stringify(false), 59 | __VUE_I18N_FULL_INSTALL__: JSON.stringify(false), 60 | __INTLIFY_PROD_DEVTOOLS__: JSON.stringify(false), 61 | __NEXT_VERSION__: JSON.stringify(process.env.npm_package_version), 62 | __NEXT_NAME__: JSON.stringify(process.env.npm_package_name), 63 | }, 64 | }; 65 | }); 66 | 67 | export default viteConfig; 68 | -------------------------------------------------------------------------------- /src/views/pages/formRules/component/formRulesOne.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 65 | -------------------------------------------------------------------------------- /src/views/pages/element/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 40 | 41 | 84 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-next-admin", 3 | "version": "2.4.33", 4 | "description": "vue3 vite next admin template", 5 | "author": "lyt_20201208", 6 | "license": "MIT", 7 | "scripts": { 8 | "dev": "vite", 9 | "build": "vite build", 10 | "lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/" 11 | }, 12 | "dependencies": { 13 | "@element-plus/icons-vue": "^2.1.0", 14 | "@wangeditor/editor": "^5.1.23", 15 | "@wangeditor/editor-for-vue": "^5.1.12", 16 | "axios": "^1.3.5", 17 | "countup.js": "^2.6.0", 18 | "cropperjs": "^1.5.13", 19 | "echarts": "^5.4.2", 20 | "echarts-gl": "^2.0.9", 21 | "echarts-wordcloud": "^2.1.0", 22 | "element-plus": "^2.3.3", 23 | "js-cookie": "^3.0.1", 24 | "js-table2excel": "^1.0.3", 25 | "jsplumb": "^2.15.6", 26 | "mitt": "^3.0.0", 27 | "nprogress": "^0.2.0", 28 | "pinia": "^2.0.34", 29 | "print-js": "^1.6.0", 30 | "qrcodejs2-fixes": "^0.0.2", 31 | "qs": "^6.11.1", 32 | "screenfull": "^6.0.2", 33 | "sortablejs": "^1.15.0", 34 | "splitpanes": "^3.1.5", 35 | "vue": "^3.2.47", 36 | "vue-clipboard3": "^2.0.0", 37 | "vue-demi": "^0.13.11", 38 | "vue-grid-layout": "^3.0.0-beta1", 39 | "vue-i18n": "^9.2.2", 40 | "vue-router": "^4.1.6" 41 | }, 42 | "devDependencies": { 43 | "@types/node": "^18.15.11", 44 | "@types/nprogress": "^0.2.0", 45 | "@types/sortablejs": "^1.15.1", 46 | "@typescript-eslint/eslint-plugin": "^5.58.0", 47 | "@typescript-eslint/parser": "^5.58.0", 48 | "@vitejs/plugin-vue": "^4.1.0", 49 | "@vue/compiler-sfc": "^3.2.47", 50 | "eslint": "^8.38.0", 51 | "eslint-plugin-vue": "^9.10.0", 52 | "prettier": "^2.8.7", 53 | "sass": "^1.61.0", 54 | "typescript": "^5.0.4", 55 | "vite": "^4.2.1", 56 | "vite-plugin-cdn-import": "^0.3.5", 57 | "vite-plugin-compression": "^0.5.1", 58 | "vite-plugin-vue-setup-extend-plus": "^0.1.0", 59 | "vue-eslint-parser": "^9.1.1" 60 | }, 61 | "browserslist": [ 62 | "> 1%", 63 | "last 2 versions", 64 | "not dead" 65 | ], 66 | "bugs": { 67 | "url": "https://gitee.com/lyt-top/vue-next-admin/issues" 68 | }, 69 | "engines": { 70 | "node": ">=16.0.0", 71 | "npm": ">= 7.0.0" 72 | }, 73 | "keywords": [ 74 | "vue", 75 | "vue3", 76 | "vuejs/vue-next", 77 | "element-ui", 78 | "element-plus", 79 | "vue-next-admin", 80 | "next-admin" 81 | ], 82 | "repository": { 83 | "type": "git", 84 | "url": "https://gitee.com/lyt-top/vue-next-admin.git" 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/views/error/401.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 34 | 35 | 90 | -------------------------------------------------------------------------------- /src/layout/routerView/link.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 46 | 47 | 94 | -------------------------------------------------------------------------------- /src/components/editor/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 102 | -------------------------------------------------------------------------------- /src/views/pages/formRules/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 72 | -------------------------------------------------------------------------------- /src/views/params/common/index.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 78 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | es2021: true, 6 | node: true, 7 | }, 8 | parser: 'vue-eslint-parser', 9 | parserOptions: { 10 | ecmaVersion: 12, 11 | parser: '@typescript-eslint/parser', 12 | sourceType: 'module', 13 | }, 14 | extends: ['plugin:vue/vue3-essential', 'plugin:vue/essential', 'eslint:recommended'], 15 | plugins: ['vue', '@typescript-eslint'], 16 | overrides: [ 17 | { 18 | files: ['*.ts', '*.tsx', '*.vue'], 19 | rules: { 20 | 'no-undef': 'off', 21 | }, 22 | }, 23 | ], 24 | rules: { 25 | // http://eslint.cn/docs/rules/ 26 | // https://eslint.vuejs.org/rules/ 27 | // https://typescript-eslint.io/rules/no-unused-vars/ 28 | '@typescript-eslint/ban-ts-ignore': 'off', 29 | '@typescript-eslint/explicit-function-return-type': 'off', 30 | '@typescript-eslint/no-explicit-any': 'off', 31 | '@typescript-eslint/no-var-requires': 'off', 32 | '@typescript-eslint/no-empty-function': 'off', 33 | '@typescript-eslint/no-use-before-define': 'off', 34 | '@typescript-eslint/ban-ts-comment': 'off', 35 | '@typescript-eslint/ban-types': 'off', 36 | '@typescript-eslint/no-non-null-assertion': 'off', 37 | '@typescript-eslint/explicit-module-boundary-types': 'off', 38 | '@typescript-eslint/no-redeclare': 'error', 39 | '@typescript-eslint/no-non-null-asserted-optional-chain': 'off', 40 | '@typescript-eslint/no-unused-vars': [2], 41 | 'vue/custom-event-name-casing': 'off', 42 | 'vue/attributes-order': 'off', 43 | 'vue/one-component-per-file': 'off', 44 | 'vue/html-closing-bracket-newline': 'off', 45 | 'vue/max-attributes-per-line': 'off', 46 | 'vue/multiline-html-element-content-newline': 'off', 47 | 'vue/singleline-html-element-content-newline': 'off', 48 | 'vue/attribute-hyphenation': 'off', 49 | 'vue/html-self-closing': 'off', 50 | 'vue/no-multiple-template-root': 'off', 51 | 'vue/require-default-prop': 'off', 52 | 'vue/no-v-model-argument': 'off', 53 | 'vue/no-arrow-functions-in-watch': 'off', 54 | 'vue/no-template-key': 'off', 55 | 'vue/no-v-html': 'off', 56 | 'vue/comment-directive': 'off', 57 | 'vue/no-parsing-error': 'off', 58 | 'vue/no-deprecated-v-on-native-modifier': 'off', 59 | 'vue/multi-word-component-names': 'off', 60 | 'no-useless-escape': 'off', 61 | 'no-sparse-arrays': 'off', 62 | 'no-prototype-builtins': 'off', 63 | 'no-constant-condition': 'off', 64 | 'no-use-before-define': 'off', 65 | 'no-restricted-globals': 'off', 66 | 'no-restricted-syntax': 'off', 67 | 'generator-star-spacing': 'off', 68 | 'no-unreachable': 'off', 69 | 'no-multiple-template-root': 'off', 70 | 'no-unused-vars': 'error', 71 | 'no-v-model-argument': 'off', 72 | 'no-case-declarations': 'off', 73 | 'no-console': 'error', 74 | 'no-redeclare': 'off', 75 | }, 76 | }; 77 | -------------------------------------------------------------------------------- /src/views/pages/workflow/js/config.ts: -------------------------------------------------------------------------------- 1 | // jsplumb 默认配置 2 | export const jsplumbDefaults = { 3 | // 多个锚点 [源锚点,目标锚点] 4 | Anchors: [ 5 | 'Top', 6 | 'TopCenter', 7 | 'TopRight', 8 | 'TopLeft', 9 | 'Right', 10 | 'RightMiddle', 11 | 'Bottom', 12 | 'BottomCenter', 13 | 'BottomRight', 14 | 'BottomLeft', 15 | 'Left', 16 | 'LeftMiddle', 17 | ], 18 | // 连线的容器id 19 | Container: 'workflow-right', 20 | // 设置链接线的形状,如直线或者曲线之类的。anchor可以去设置锚点的位置。可选值"" 21 | Connector: ['Bezier', { curviness: 100 }], 22 | // 节点是否可以用鼠标拖动使其断开,默认为true。即用鼠标链接上的连线,也可以使用鼠标拖动让其断开。设置成false,可以让其拖动也不会自动断开 23 | ConnectionsDetachable: false, 24 | // 删除线的时候节点不删除 25 | DeleteEndpointsOnDetach: false, 26 | // 每当添加或以其他方式创建 Endpoint 并且 jsPlumb 尚未给出任何明确的 Endpoint 定义时将使用 27 | Endpoint: ['Blank', { Overlays: '' }], 28 | // 连接中源和目标端点的默认外观 29 | EndpointStyle: { fill: '#1879ffa1', outlineWidth: 1 }, 30 | // jsPlumb 的内部日志记录是否打开 31 | LogEnabled: true, 32 | // 连接器的默认外观 33 | PaintStyle: { 34 | stroke: '#E0E3E7', 35 | strokeWidth: 1, 36 | outlineStroke: 'transparent', 37 | outlineWidth: 10, 38 | }, 39 | // 用于配置任何可拖动元素的默认选项jsPlumb.draggable 40 | DragOptions: { cursor: 'pointer', zIndex: 2000 }, 41 | // 添加到连接器和端点的默认叠加层。已弃用:从 4.x 开始,将不支持此功能。并非所有叠加层都可以连接到连接器和端点。 42 | Overlays: [ 43 | [ 44 | 'Arrow', 45 | { 46 | width: 10, // 箭头尾部的宽度 47 | length: 8, // 从箭头的尾部到头部的距离 48 | location: 1, // 位置,建议使用0~1之间 49 | direction: 1, // 方向,默认值为1(表示向前),可选-1(表示向后) 50 | foldback: 0.623, // 折回,也就是尾翼的角度,默认0.623,当为1时,为正三角 51 | }, 52 | ], 53 | [ 54 | 'Label', 55 | { 56 | label: '', 57 | location: 0.5, 58 | cssClass: 'aLabel', 59 | }, 60 | ], 61 | ], 62 | // 默认渲染模式 svg、canvas 63 | RenderMode: 'svg', 64 | // 悬停状态下连接的默认外观 65 | HoverPaintStyle: { stroke: '#b0b2b5', strokeWidth: 1 }, 66 | // 悬停状态下端点的默认外观 67 | EndpointHoverStyle: { fill: 'red' }, 68 | // 端点和连接的默认范围。范围提供了对哪些端点可以连接到哪些其他端点的基本控制 69 | Scope: 'jsPlumb_DefaultScope', 70 | }; 71 | 72 | // 整个节点作为source或者target 73 | export const jsplumbMakeSource = { 74 | // 设置可以拖拽的类名,只要鼠标移动到该类名上的DOM,就可以拖拽连线 75 | filter: '.workflow-icon-drag', 76 | filterExclude: false, 77 | anchor: 'Continuous', 78 | // 是否允许自己连接自己 79 | allowLoopback: true, 80 | maxConnections: -1, 81 | }; 82 | 83 | // 整个节点作为source或者target 84 | export const jsplumbMakeTarget = { 85 | filter: '.workflow-icon-drag', 86 | filterExclude: false, 87 | // 是否允许自己连接自己 88 | anchor: 'Continuous', 89 | allowLoopback: true, 90 | dropOptions: { hoverClass: 'ef-drop-hover' }, 91 | }; 92 | 93 | // 连线参数 94 | export const jsplumbConnect = { 95 | isSource: true, 96 | isTarget: true, 97 | // 动态锚点、提供了4个方向 Continuous、AutoDefault 98 | anchor: 'Continuous', 99 | }; 100 | -------------------------------------------------------------------------------- /src/theme/common/transition.scss: -------------------------------------------------------------------------------- 1 | /* 页面切换动画 2 | ------------------------------- */ 3 | .slide-right-enter-active, 4 | .slide-right-leave-active, 5 | .slide-left-enter-active, 6 | .slide-left-leave-active { 7 | will-change: transform; 8 | transition: all 0.3s ease; 9 | } 10 | // slide-right 11 | .slide-right-enter-from { 12 | opacity: 0; 13 | transform: translateX(-20px); 14 | } 15 | .slide-right-leave-to { 16 | opacity: 0; 17 | transform: translateX(20px); 18 | } 19 | // slide-left 20 | .slide-left-enter-from { 21 | @extend .slide-right-leave-to; 22 | } 23 | .slide-left-leave-to { 24 | @extend .slide-right-enter-from; 25 | } 26 | // opacitys 27 | .opacitys-enter-active, 28 | .opacitys-leave-active { 29 | will-change: transform; 30 | transition: all 0.3s ease; 31 | } 32 | .opacitys-enter-from, 33 | .opacitys-leave-to { 34 | opacity: 0; 35 | } 36 | 37 | /* Breadcrumb 面包屑过渡动画 38 | ------------------------------- */ 39 | .breadcrumb-enter-active, 40 | .breadcrumb-leave-active { 41 | transition: all 0.5s ease; 42 | } 43 | .breadcrumb-enter-from, 44 | .breadcrumb-leave-active { 45 | opacity: 0; 46 | transform: translateX(20px); 47 | } 48 | .breadcrumb-leave-active { 49 | position: absolute; 50 | z-index: -1; 51 | } 52 | 53 | /* logo 过渡动画 54 | ------------------------------- */ 55 | @keyframes logoAnimation { 56 | 0% { 57 | transform: scale(0); 58 | } 59 | 80% { 60 | transform: scale(1.2); 61 | } 62 | 100% { 63 | transform: scale(1); 64 | } 65 | } 66 | 67 | /* 404、401 过渡动画 68 | ------------------------------- */ 69 | @keyframes error-num { 70 | 0% { 71 | transform: translateY(60px); 72 | opacity: 0; 73 | } 74 | 100% { 75 | transform: translateY(0); 76 | opacity: 1; 77 | } 78 | } 79 | @keyframes error-img { 80 | 0% { 81 | opacity: 0; 82 | } 83 | 100% { 84 | opacity: 1; 85 | } 86 | } 87 | @keyframes error-img-two { 88 | 0% { 89 | opacity: 1; 90 | } 91 | 100% { 92 | opacity: 0; 93 | } 94 | } 95 | 96 | /* 登录页动画 97 | ------------------------------- */ 98 | @keyframes loginLeft { 99 | 0% { 100 | left: -100%; 101 | } 102 | 50%, 103 | 100% { 104 | left: 100%; 105 | } 106 | } 107 | @keyframes loginTop { 108 | 0% { 109 | top: -100%; 110 | } 111 | 50%, 112 | 100% { 113 | top: 100%; 114 | } 115 | } 116 | @keyframes loginRight { 117 | 0% { 118 | right: -100%; 119 | } 120 | 50%, 121 | 100% { 122 | right: 100%; 123 | } 124 | } 125 | @keyframes loginBottom { 126 | 0% { 127 | bottom: -100%; 128 | } 129 | 50%, 130 | 100% { 131 | bottom: 100%; 132 | } 133 | } 134 | 135 | /* 左右左 link.vue 136 | ------------------------------- */ 137 | @keyframes toRight { 138 | 0% { 139 | left: -5px; 140 | } 141 | 50% { 142 | left: 100%; 143 | } 144 | 100% { 145 | left: -5px; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/views/chart/head.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 41 | 42 | 102 | -------------------------------------------------------------------------------- /src/views/params/dynamic/index.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 80 | -------------------------------------------------------------------------------- /src/types/global.d.ts: -------------------------------------------------------------------------------- 1 | // 申明外部 npm 插件模块 2 | declare module 'vue-grid-layout'; 3 | declare module 'qrcodejs2-fixes'; 4 | declare module 'splitpanes'; 5 | declare module 'js-cookie'; 6 | declare module '@wangeditor/editor-for-vue'; 7 | declare module 'js-table2excel'; 8 | declare module 'qs'; 9 | declare module 'sortablejs'; 10 | 11 | // 声明一个模块,防止引入文件时报错 12 | declare module '*.json'; 13 | declare module '*.png'; 14 | declare module '*.jpg'; 15 | declare module '*.scss'; 16 | declare module '*.ts'; 17 | declare module '*.js'; 18 | 19 | // 声明文件,*.vue 后缀的文件交给 vue 模块来处理 20 | declare module '*.vue' { 21 | import type { DefineComponent } from 'vue'; 22 | const component: DefineComponent<{}, {}, any>; 23 | export default component; 24 | } 25 | 26 | // 声明文件,定义全局变量 27 | /* eslint-disable */ 28 | declare interface Window { 29 | nextLoading: boolean; 30 | BMAP_SATELLITE_MAP: any; 31 | BMap: any; 32 | } 33 | 34 | // 声明路由当前项类型 35 | declare type RouteItem = { 36 | path: string; 37 | name?: string | symbol | undefined | null; 38 | redirect?: string; 39 | k?: T; 40 | meta?: { 41 | title?: string; 42 | isLink?: string; 43 | isHide?: boolean; 44 | isKeepAlive?: boolean; 45 | isAffix?: boolean; 46 | isIframe?: boolean; 47 | roles?: string[]; 48 | icon?: string; 49 | isDynamic?: boolean; 50 | isDynamicPath?: string; 51 | isIframeOpen?: string; 52 | loading?: boolean; 53 | }; 54 | children: T[]; 55 | query?: { [key: string]: T }; 56 | params?: { [key: string]: T }; 57 | contextMenuClickId?: string | number; 58 | commonUrl?: string; 59 | isFnClick?: boolean; 60 | url?: string; 61 | transUrl?: string; 62 | title?: string; 63 | id?: string | number; 64 | }; 65 | 66 | // 声明路由 to from 67 | declare interface RouteToFrom extends RouteItem { 68 | path?: string; 69 | children?: T[]; 70 | } 71 | 72 | // 声明路由当前项类型集合 73 | declare type RouteItems = T[]; 74 | 75 | // 声明 ref 76 | declare type RefType = T | null; 77 | 78 | // 声明 HTMLElement 79 | declare type HtmlType = HTMLElement | string | undefined | null; 80 | 81 | // 申明 children 可选 82 | declare type ChilType = { 83 | children?: T[]; 84 | }; 85 | 86 | // 申明 数组 87 | declare type EmptyArrayType = T[]; 88 | 89 | // 申明 对象 90 | declare type EmptyObjectType = { 91 | [key: string]: T; 92 | }; 93 | 94 | // 申明 select option 95 | declare type SelectOptionType = { 96 | value: string | number; 97 | label: string | number; 98 | }; 99 | 100 | // 鼠标滚轮滚动类型 101 | declare interface WheelEventType extends WheelEvent { 102 | wheelDelta: number; 103 | } 104 | 105 | // table 数据格式公共类型 106 | declare interface TableType { 107 | total: number; 108 | loading: boolean; 109 | param: { 110 | pageNum: number; 111 | pageSize: number; 112 | [key: string]: T; 113 | }; 114 | } 115 | -------------------------------------------------------------------------------- /src/layout/routerView/iframes.vue: -------------------------------------------------------------------------------- 1 |