ZAN
2 years ago
162 changed files with 15570 additions and 10132 deletions
-
3.env.development
-
3.env.production
-
2.eslintignore
-
173.eslintrc.js
-
22.gitignore
-
17.prettier.js
-
2.prettierignore
-
10.prettierrc.js
-
3.vscode/extensions.json
-
153README.md
-
54auto-imports.d.ts
-
17components.d.ts
-
25deploy.sh
-
53index.html
-
4699package-lock.json
-
64package.json
-
4241pnpm-lock.yaml
-
13postcss.config.js
-
61src/App.vue
-
175src/api/axios.js
-
15src/api/http.ts
-
37src/api/request.ts
-
62src/api/resource.js
-
59src/api/role.js
-
72src/api/user.js
-
11src/api/user.ts
-
24src/assets/css/font-awesome.css
-
2335src/assets/css/font-awesome.min.css
-
1src/assets/image/workbench.svg
-
15src/assets/js/dictionarie.ts
-
61src/assets/js/guide.js
-
70src/assets/js/guide.ts
-
201src/assets/js/resource.js
-
191src/assets/js/resource.ts
-
50src/assets/js/versionLog.js
-
41src/assets/js/versionLog.ts
-
12src/assets/scss/_animated.scss
-
38src/assets/scss/_bordered-pulled.scss
-
4src/assets/scss/_core.scss
-
2700src/assets/scss/_icons.scss
-
16src/assets/scss/_larger.scss
-
4src/assets/scss/_list.scss
-
15src/assets/scss/_mixins.scss
-
21src/assets/scss/_path.scss
-
20src/assets/scss/_rotated-flipped.scss
-
8src/assets/scss/_screen-reader.scss
-
15src/assets/scss/_stacked.scss
-
15src/assets/scss/_variables.scss
-
40src/assets/style/element-ui.scss
-
79src/assets/style/index.scss
-
8src/assets/style/main.scss
-
8src/common/event.ts
-
20src/common/mouse.ts
-
60src/common/wartermark.js
-
52src/common/watermark.ts
-
288src/components/Header/index.vue
-
71src/components/Pagination/index.vue
-
192src/components/Setting/baseInfo.vue
-
112src/components/Setting/checkPass.vue
-
178src/components/Tags/index.vue
-
21src/components/abnormal/403.vue
-
21src/components/abnormal/404.vue
-
21src/components/abnormal/build.vue
-
19src/components/abnormal/networkError.vue
-
96src/components/computerMonitor/index.vue
-
54src/components/copy/index.vue
-
309src/components/dashboard/analysis.vue
-
311src/components/dashboard/workbench.vue
-
31src/components/dialogDrag/index.vue
-
224src/components/dropDownItem/baseInfo.vue
-
139src/components/dropDownItem/checkPass.vue
-
62src/components/dropDownItem/versionLog.vue
-
16src/components/editor/markdown.vue
-
77src/components/editor/textEditor.vue
-
53src/components/i18n/index.vue
-
45src/components/infiniteScroll/index.vue
-
71src/components/message/feedbackCenter.vue
-
34src/components/noviceGuide/index.vue
-
19src/components/qrCode/index.vue
-
237src/components/template/cardList.vue
-
162src/components/template/easyForm.vue
-
234src/components/template/tableOperation.vue
-
25src/components/tips/errorTip.vue
-
20src/components/tips/successTip.vue
-
18src/components/tips/warningTip.vue
-
38src/components/watermark/index.vue
-
27src/directives/copy.js
-
27src/directives/copy.ts
-
70src/directives/dialogDrag.js
-
6src/directives/index.js
-
2src/directives/index.ts
-
14src/enums/index.ts
-
8src/env.d.ts
-
20src/filters/index.js
-
18src/filters/index.ts
-
288src/layouts/components/Header/index.vue
-
92src/layouts/components/SideBar/index.vue
-
270src/layouts/components/Tags/index.vue
-
3src/layouts/global/index.vue
-
84src/layouts/index.vue
@ -0,0 +1,3 @@ |
|||
NODE_ENV=development |
|||
|
|||
VITE_APP_WEB_URL= '/api' |
@ -0,0 +1,3 @@ |
|||
NODE_ENV=production |
|||
|
|||
VITE_APP_WEB_URL= '/api' |
@ -0,0 +1,2 @@ |
|||
node_modules |
|||
dist |
@ -1,20 +1,157 @@ |
|||
module.exports = { |
|||
env: { |
|||
browser: true, |
|||
es6: true, |
|||
node: true |
|||
root: true, |
|||
env: { |
|||
browser: true, |
|||
node: true, |
|||
es2021: true, |
|||
}, |
|||
parser: "vue-eslint-parser", |
|||
extends: [ |
|||
"eslint:recommended", |
|||
"plugin:vue/vue3-recommended", |
|||
"plugin:@typescript-eslint/recommended", |
|||
"plugin:prettier/recommended", |
|||
// eslint-config-prettier 的缩写
|
|||
"prettier", |
|||
"vue-global-api", |
|||
], |
|||
parserOptions: { |
|||
ecmaVersion: 12, |
|||
parser: "@typescript-eslint/parser", |
|||
sourceType: "module", |
|||
ecmaFeatures: { |
|||
jsx: true, |
|||
}, |
|||
extends: ['plugin:vue/essential', '@vue/prettier'], |
|||
globals: { |
|||
Atomics: 'readonly', |
|||
SharedArrayBuffer: 'readonly' |
|||
}, |
|||
parserOptions: { |
|||
ecmaVersion: 2018, |
|||
sourceType: 'module' |
|||
}, |
|||
plugins: ['prettier'], |
|||
rules: { |
|||
'prettier/prettier': 'error' |
|||
} |
|||
} |
|||
}, |
|||
// eslint-plugin-vue @typescript-eslint/eslint-plugin eslint-plugin-prettier的缩写
|
|||
plugins: ["vue", "@typescript-eslint", "prettier"], |
|||
rules: { |
|||
"@typescript-eslint/ban-ts-ignore": "off", |
|||
"@typescript-eslint/no-unused-vars": "off", |
|||
"@typescript-eslint/explicit-function-return-type": "off", |
|||
"@typescript-eslint/no-explicit-any": "off", |
|||
"@typescript-eslint/no-var-requires": "off", |
|||
"@typescript-eslint/no-empty-function": "off", |
|||
"@typescript-eslint/no-use-before-define": "off", |
|||
"@typescript-eslint/ban-ts-comment": "off", |
|||
"@typescript-eslint/ban-types": "off", |
|||
"@typescript-eslint/no-non-null-assertion": "off", |
|||
"@typescript-eslint/explicit-module-boundary-types": "off", |
|||
"no-var": "error", |
|||
"prettier/prettier": "error", |
|||
// 禁止出现console
|
|||
"no-console": "warn", |
|||
// 禁用debugger
|
|||
"no-debugger": "warn", |
|||
// 禁止出现重复的 case 标签
|
|||
"no-duplicate-case": "warn", |
|||
// 禁止出现空语句块
|
|||
"no-empty": "warn", |
|||
// 禁止不必要的括号
|
|||
"no-extra-parens": "off", |
|||
// 禁止对 function 声明重新赋值
|
|||
"no-func-assign": "warn", |
|||
// 禁止在 return、throw、continue 和 break 语句之后出现不可达代码
|
|||
"no-unreachable": "warn", |
|||
// 强制所有控制语句使用一致的括号风格
|
|||
curly: "warn", |
|||
// 要求 switch 语句中有 default 分支
|
|||
"default-case": "warn", |
|||
// 强制尽可能地使用点号
|
|||
"dot-notation": "warn", |
|||
// 要求使用 === 和 !==
|
|||
eqeqeq: "warn", |
|||
// 禁止 if 语句中 return 语句之后有 else 块
|
|||
"no-else-return": "warn", |
|||
// 禁止出现空函数
|
|||
"no-empty-function": "warn", |
|||
// 禁用不必要的嵌套块
|
|||
"no-lone-blocks": "warn", |
|||
// 禁止使用多个空格
|
|||
"no-multi-spaces": "warn", |
|||
// 禁止多次声明同一变量
|
|||
"no-redeclare": "warn", |
|||
// 禁止在 return 语句中使用赋值语句
|
|||
"no-return-assign": "warn", |
|||
// 禁用不必要的 return await
|
|||
"no-return-await": "warn", |
|||
// 禁止自我赋值
|
|||
"no-self-assign": "warn", |
|||
// 禁止自身比较
|
|||
"no-self-compare": "warn", |
|||
// 禁止不必要的 catch 子句
|
|||
"no-useless-catch": "warn", |
|||
// 禁止多余的 return 语句
|
|||
"no-useless-return": "warn", |
|||
// 禁止变量声明与外层作用域的变量同名
|
|||
"no-shadow": "off", |
|||
// 允许delete变量
|
|||
"no-delete-var": "off", |
|||
// 强制数组方括号中使用一致的空格
|
|||
"array-bracket-spacing": "warn", |
|||
// 强制在代码块中使用一致的大括号风格
|
|||
"brace-style": "warn", |
|||
// 强制使用骆驼拼写法命名约定
|
|||
camelcase: "warn", |
|||
// 强制使用一致的缩进
|
|||
indent: "off", |
|||
// 强制在 JSX 属性中一致地使用双引号或单引号
|
|||
// 'jsx-quotes': 'warn',
|
|||
// 强制可嵌套的块的最大深度4
|
|||
"max-depth": "warn", |
|||
// 强制最大行数 300
|
|||
// "max-lines": ["warn", { "max": 1200 }],
|
|||
// 强制函数最大代码行数 50
|
|||
// 'max-lines-per-function': ['warn', { max: 70 }],
|
|||
// 强制函数块最多允许的的语句数量20
|
|||
"max-statements": ["warn", 100], |
|||
// 强制回调函数最大嵌套深度
|
|||
"max-nested-callbacks": ["warn", 3], |
|||
// 强制函数定义中最多允许的参数数量
|
|||
"max-params": ["warn", 3], |
|||
// 强制每一行中所允许的最大语句数量
|
|||
"max-statements-per-line": ["warn", { max: 1 }], |
|||
// 要求方法链中每个调用都有一个换行符
|
|||
"newline-per-chained-call": ["warn", { ignoreChainWithDepth: 3 }], |
|||
// 禁止 if 作为唯一的语句出现在 else 语句中
|
|||
"no-lonely-if": "warn", |
|||
// 禁止空格和 tab 的混合缩进
|
|||
"no-mixed-spaces-and-tabs": "warn", |
|||
// 禁止出现多行空行
|
|||
"no-multiple-empty-lines": "warn", |
|||
// 禁止出现;
|
|||
semi: ["warn", "never"], |
|||
// 强制在块之前使用一致的空格
|
|||
"space-before-blocks": "warn", |
|||
// 强制在 function的左括号之前使用一致的空格
|
|||
// 'space-before-function-paren': ['warn', 'never'],
|
|||
// 强制在圆括号内使用一致的空格
|
|||
"space-in-parens": "warn", |
|||
// 要求操作符周围有空格
|
|||
"space-infix-ops": "warn", |
|||
// 强制在一元操作符前后使用一致的空格
|
|||
"space-unary-ops": "warn", |
|||
// 强制在注释中 // 或 /* 使用一致的空格
|
|||
// "spaced-comment": "warn",
|
|||
// 强制在 switch 的冒号左右有空格
|
|||
"switch-colon-spacing": "warn", |
|||
// 强制箭头函数的箭头前后使用一致的空格
|
|||
"arrow-spacing": "warn", |
|||
"no-var": "warn", |
|||
"prefer-const": "warn", |
|||
"prefer-rest-params": "warn", |
|||
"no-useless-escape": "warn", |
|||
"no-irregular-whitespace": "warn", |
|||
"no-prototype-builtins": "warn", |
|||
"no-fallthrough": "warn", |
|||
"no-extra-boolean-cast": "warn", |
|||
"no-case-declarations": "warn", |
|||
"no-async-promise-executor": "warn", |
|||
}, |
|||
globals: { |
|||
defineProps: "readonly", |
|||
defineEmits: "readonly", |
|||
defineExpose: "readonly", |
|||
withDefaults: "readonly", |
|||
}, |
|||
}; |
@ -1,6 +1,24 @@ |
|||
# Logs |
|||
logs |
|||
*.log |
|||
npm-debug.log* |
|||
yarn-debug.log* |
|||
yarn-error.log* |
|||
pnpm-debug.log* |
|||
lerna-debug.log* |
|||
|
|||
node_modules |
|||
.DS_Store |
|||
dist |
|||
dist-ssr |
|||
*.local |
|||
.idea |
|||
|
|||
# Editor directories and files |
|||
.vscode/* |
|||
!.vscode/extensions.json |
|||
.idea |
|||
.DS_Store |
|||
*.suo |
|||
*.ntvs* |
|||
*.njsproj |
|||
*.sln |
|||
*.sw? |
@ -0,0 +1,17 @@ |
|||
module.exports = { |
|||
tabWidth: 2, |
|||
jsxSingleQuote: true, |
|||
jsxBracketSameLine: true, |
|||
printWidth: 100, |
|||
singleQuote: true, |
|||
semi: false, |
|||
overrides: [ |
|||
{ |
|||
files: "*.json", |
|||
options: { |
|||
printWidth: 200, |
|||
}, |
|||
}, |
|||
], |
|||
arrowParens: "always", |
|||
}; |
@ -0,0 +1,2 @@ |
|||
node_modules |
|||
dist |
@ -1,10 +0,0 @@ |
|||
module.exports = { |
|||
tabWidth: 2, |
|||
useTabs: false, |
|||
semi: false, |
|||
arrowParens: 'avoid', |
|||
singleQuote: true, |
|||
bracketSpacing: true, |
|||
endOfLine: 'lf', |
|||
trailingComma: 'none' |
|||
} |
@ -0,0 +1,3 @@ |
|||
{ |
|||
"recommendations": ["johnsoncodehk.volar"] |
|||
} |
@ -1,152 +1,11 @@ |
|||
# Vue 3 + Typescript + Vite |
|||
|
|||
This template should help get you started developing with Vue 3 and Typescript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more. |
|||
|
|||
# Admin-Frame-Vue3 |
|||
## Recommended IDE Setup |
|||
|
|||
- [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) |
|||
|
|||
## Type Support For `.vue` Imports in TS |
|||
|
|||
|
|||
|
|||
## 介绍 |
|||
|
|||
本项目是基于Vue3 + Element-Plus + Vite 开发的实现后台管理系统。 |
|||
|
|||
[访问地址](http://47.96.87.129:8001) |
|||
|
|||
#### 1.1、使用技术 |
|||
Vite + Vue3 + Element-Plus + Sass + Vuex + Vue-router + Vue-i8n + Echarts |
|||
#### 1.2、兼容性注意 |
|||
Vite 需要 Node.js 版本 >= 12.0.0。 |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
## 项目运行 |
|||
|
|||
`git clone https://github.com/ZANJIAHAO1008/Admin-Frame-Vue3.git` |
|||
|
|||
`cd Admin-Frame-Vue3` |
|||
|
|||
`npm install ` |
|||
|
|||
`npm run dev` |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
## 项目截图 |
|||
|
|||
![avatar](http://47.96.87.129:8001/denglu.png) |
|||
|
|||
- ![avatar](http://47.96.87.129:8001/shouye.png) |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
## 目前包含功能 |
|||
|
|||
- 登陆 |
|||
- 注册 |
|||
- 首页 |
|||
- 个人设置 |
|||
- [x] 修改密码 |
|||
- [x] 基础信息修改 |
|||
- [x] 版权信息 |
|||
- 头部 |
|||
- [x] 全屏 |
|||
- [x] 国际化 |
|||
- 功能 |
|||
- [x] 可拖拽弹框 |
|||
- [x] 添加水印 |
|||
- [x] 时间选择器 |
|||
- [x] 地图 |
|||
- [x] 复制 |
|||
- [x] 生成二维码 |
|||
- [x] 无限滚动 |
|||
- [x] 监测电脑信息 |
|||
- 表单模板 |
|||
- [x] 基础表单 |
|||
- [x] 表格表单 |
|||
- [x] 卡片列表 |
|||
- 编辑器 |
|||
- [x] markdown |
|||
- [x] 富文本编辑器 |
|||
- 消息管理 |
|||
- [x] 消息中心 |
|||
- 返回结果 |
|||
- [x] 成功 |
|||
- [x] 异常 |
|||
- [x] 失败 |
|||
- 异常页面 |
|||
- [x] 404 |
|||
- [x] 403 |
|||
- [x] 暂无数据 |
|||
- [x] 功能建设中 |
|||
- [x] 网络不可用 |
|||
- 权限管理 |
|||
- [x] 用户管理 |
|||
- [x] 角色管理 |
|||
- [x] 资源管理 |
|||
- 工作流程 |
|||
- 新手引导 |
|||
- 国际化 |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
## 更新日志 |
|||
|
|||
**V1.0.1** 2021/11/24 |
|||
|
|||
1、vue版本升级至3.2.22 |
|||
|
|||
2、项目中添加vueuse库 |
|||
|
|||
3、新增生成二维码、监测电脑信息功能。 |
|||
|
|||
|
|||
|
|||
**V1.0.2** 2021/11/26 |
|||
|
|||
1、新增卡片列表 |
|||
|
|||
|
|||
|
|||
**V1.0.3** 2021/12/07 |
|||
|
|||
1、新增版本日志. |
|||
|
|||
2、新增工作流程(建设中). |
|||
|
|||
3、添加被动事件监听器来阻止touchstart事件. |
|||
|
|||
|
|||
|
|||
**V1.0.4** 2021/12/08 |
|||
|
|||
1、优化首屏加载等待动画. |
|||
|
|||
2、修复Echarts切换页面不显示问题. |
|||
|
|||
|
|||
|
|||
**V1.0.5** 2021/12/09 |
|||
|
|||
1、新增新手引导功能. |
|||
|
|||
|
|||
|
|||
**V1.0.6** 2021/12/20 |
|||
|
|||
1、配置国际化. |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
#### 有好的意见欢迎提issues 或者联系我 Q974813758 |
|||
|
|||
# 如果觉得有帮助的话 , 可以帮忙点个star吗 |
|||
Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's `.vue` type support plugin by running `Volar: Switch TS Plugin on/off` from VSCode command palette. |
@ -0,0 +1,54 @@ |
|||
// Generated by 'unplugin-auto-import'
|
|||
// We suggest you to commit this file into source control
|
|||
declare global { |
|||
const computed: typeof import('vue')['computed'] |
|||
const createApp: typeof import('vue')['createApp'] |
|||
const customRef: typeof import('vue')['customRef'] |
|||
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] |
|||
const defineComponent: typeof import('vue')['defineComponent'] |
|||
const effectScope: typeof import('vue')['effectScope'] |
|||
const EffectScope: typeof import('vue')['EffectScope'] |
|||
const getCurrentInstance: typeof import('vue')['getCurrentInstance'] |
|||
const getCurrentScope: typeof import('vue')['getCurrentScope'] |
|||
const h: typeof import('vue')['h'] |
|||
const inject: typeof import('vue')['inject'] |
|||
const isReadonly: typeof import('vue')['isReadonly'] |
|||
const isRef: typeof import('vue')['isRef'] |
|||
const markRaw: typeof import('vue')['markRaw'] |
|||
const nextTick: typeof import('vue')['nextTick'] |
|||
const onActivated: typeof import('vue')['onActivated'] |
|||
const onBeforeMount: typeof import('vue')['onBeforeMount'] |
|||
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] |
|||
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] |
|||
const onDeactivated: typeof import('vue')['onDeactivated'] |
|||
const onErrorCaptured: typeof import('vue')['onErrorCaptured'] |
|||
const onMounted: typeof import('vue')['onMounted'] |
|||
const onRenderTracked: typeof import('vue')['onRenderTracked'] |
|||
const onRenderTriggered: typeof import('vue')['onRenderTriggered'] |
|||
const onScopeDispose: typeof import('vue')['onScopeDispose'] |
|||
const onServerPrefetch: typeof import('vue')['onServerPrefetch'] |
|||
const onUnmounted: typeof import('vue')['onUnmounted'] |
|||
const onUpdated: typeof import('vue')['onUpdated'] |
|||
const provide: typeof import('vue')['provide'] |
|||
const reactive: typeof import('vue')['reactive'] |
|||
const readonly: typeof import('vue')['readonly'] |
|||
const ref: typeof import('vue')['ref'] |
|||
const resolveComponent: typeof import('vue')['resolveComponent'] |
|||
const shallowReactive: typeof import('vue')['shallowReactive'] |
|||
const shallowReadonly: typeof import('vue')['shallowReadonly'] |
|||
const shallowRef: typeof import('vue')['shallowRef'] |
|||
const toRaw: typeof import('vue')['toRaw'] |
|||
const toRef: typeof import('vue')['toRef'] |
|||
const toRefs: typeof import('vue')['toRefs'] |
|||
const triggerRef: typeof import('vue')['triggerRef'] |
|||
const unref: typeof import('vue')['unref'] |
|||
const useAttrs: typeof import('vue')['useAttrs'] |
|||
const useCssModule: typeof import('vue')['useCssModule'] |
|||
const useCssVars: typeof import('vue')['useCssVars'] |
|||
const useRoute: typeof import('vue-router')['useRoute'] |
|||
const useRouter: typeof import('vue-router')['useRouter'] |
|||
const useSlots: typeof import('vue')['useSlots'] |
|||
const watch: typeof import('vue')['watch'] |
|||
const watchEffect: typeof import('vue')['watchEffect'] |
|||
} |
|||
export {} |
@ -0,0 +1,17 @@ |
|||
// generated by unplugin-vue-components
|
|||
// We suggest you to commit this file into source control
|
|||
// Read more: https://github.com/vuejs/vue-next/pull/3399
|
|||
|
|||
declare module "vue" { |
|||
export interface GlobalComponents { |
|||
BaseInfo: typeof import("./src/components/Setting/baseInfo.vue")["default"]; |
|||
CheckPass: typeof import("./src/components/Setting/checkPass.vue")["default"]; |
|||
Header: typeof import("./src/components/Header/index.vue")["default"]; |
|||
Pagination: typeof import("./src/components/Pagination/index.vue")["default"]; |
|||
SideBar: typeof import("./src/components/SideBar/index.vue")["default"]; |
|||
Tags: typeof import("./src/components/Tags/index.vue")["default"]; |
|||
VersionLog: typeof import("./src/components/Setting/versionLog.vue")["default"]; |
|||
} |
|||
} |
|||
|
|||
export {}; |
@ -0,0 +1,25 @@ |
|||
#!/usr/bin/env sh |
|||
|
|||
# 发生错误时终止 |
|||
set -e |
|||
|
|||
# 构建 |
|||
npm run build |
|||
|
|||
# 进入构建文件夹 |
|||
cd dist |
|||
|
|||
# 如果你要部署到自定义域名 |
|||
# echo 'www.example.com' > CNAME |
|||
|
|||
git init |
|||
git add -A |
|||
git commit -m 'deploy' |
|||
|
|||
# 如果你要部署在 https://<USERNAME>.github.io |
|||
# git push -f git@github.com:<USERNAME>/<USERNAME>.github.io.git main |
|||
|
|||
# 如果你要部署在 https://<USERNAME>.github.io/<REPO> |
|||
# git push -f git@github.com:<USERNAME>/<REPO>.git master:gh-pages |
|||
|
|||
cd - |
4699
package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
4241
pnpm-lock.yaml
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,13 +0,0 @@ |
|||
// module.exports = {
|
|||
// plugins: {
|
|||
// 'postcss-pxtorem': {
|
|||
// rootValue: 75, //一个rem等于75个px;相当于vue/css文件中写width:75;会转换成width:1rem;再根据html的fontsize计算实际的width:
|
|||
// propList: ['*'], //允许哪些属性转成rem;
|
|||
// unitPrecision: 5, //最多小数位数;
|
|||
// selectorBlackList: [/^\.van-/,/^\.mescroll/], //忽略.van-和.mescroll开头的类名;
|
|||
// replace: true,
|
|||
// mediaQuery: false, //允许在媒体查询中转换px
|
|||
// minPixelValue: 2, //设置要替换的最小像素值
|
|||
// }
|
|||
// }
|
|||
// }
|
@ -1,44 +1,31 @@ |
|||
<template> |
|||
<el-config-provider :locale="locale"> |
|||
<router-view /> |
|||
</el-config-provider> |
|||
<router-view /> |
|||
</template> |
|||
<script> |
|||
import * as echarts from "echarts"; |
|||
import { computed, getCurrentInstance, provide, watch } from "vue"; |
|||
<script setup lang="ts"> |
|||
import * as eCharts from "echarts"; |
|||
import { provide } from "vue"; |
|||
import Cookies from "js-cookie"; |
|||
import { defineComponent, toRefs, reactive } from "vue"; |
|||
import { ElConfigProvider } from "element-plus"; |
|||
import zhCn from "element-plus/lib/locale/lang/zh-cn"; |
|||
import en from "element-plus/lib/locale/lang/en"; |
|||
export default defineComponent({ |
|||
name: "App", |
|||
components: { |
|||
ElConfigProvider, |
|||
}, |
|||
setup() { |
|||
provide("echarts", echarts); //全局穿透Echarts |
|||
const state = reactive({ |
|||
locale: computed(() => { |
|||
return Cookies.get("lang") === "en" ? en : zhCn; |
|||
}), |
|||
}); |
|||
const proxy = getCurrentInstance(); |
|||
const settingDefaultLang = () => { |
|||
//如果未设置过 则默认中文 可根据接口返回的个人信息中 增加默认语言 |
|||
if (!Cookies.get("lang")) { |
|||
Cookies.set("lang", "zh"); |
|||
} |
|||
}; |
|||
settingDefaultLang(); |
|||
import { usePreferredLanguages } from "@vueuse/core"; |
|||
const defaultLanguages = usePreferredLanguages(); |
|||
|
|||
return { |
|||
...toRefs(state), |
|||
}; |
|||
}, |
|||
}); |
|||
provide("eCharts", eCharts); //全局穿透eCharts |
|||
|
|||
const setDefaultSetting = () => { |
|||
//添加一些默认配置 |
|||
if (!Cookies.get("lang")) { |
|||
//如果未设置过 则默认中文 可根据接口返回的个人信息中 增加默认语言 |
|||
Cookies.set("lang", defaultLanguages?.value[1] ?? "zh"); |
|||
} |
|||
|
|||
if (!Cookies.get("size")) { |
|||
//默认添加size大小 |
|||
Cookies.set("size", "default"); |
|||
} |
|||
}; |
|||
|
|||
setDefaultSetting(); |
|||
</script> |
|||
<style lang="scss"> |
|||
@import "./src/style/index.scss"; |
|||
@import "./src/style/element-ui.scss"; |
|||
@import "@/assets/style/index.scss"; |
|||
@import "@/assets/style/element-ui.scss"; |
|||
</style> |
@ -1,175 +0,0 @@ |
|||
import axios from 'axios'; |
|||
import qs from 'qs'; |
|||
import { ElMessage } from 'element-plus' |
|||
import { Loading } from '../util/utils.js'; |
|||
import { getToken } from '../util/auth.js'; |
|||
import router from "../router"; |
|||
const pendingMap = new Map(); |
|||
var loading = null; |
|||
export default function (config, options) { |
|||
//判断是否要展示loading 需要配置则在增加{loading:true} 默认false
|
|||
let loadingStatus = options?.loading || undefined; |
|||
//判断是否要展示消息 需要配置则在增加{message:true} 默认false
|
|||
let messageStatus = options?.message || undefined; |
|||
//判断是否展示未格式化的数据 需要配置则在增加{rawData:true} 默认false
|
|||
let rawData = options?.rawData || false; |
|||
const service = axios.create({ |
|||
// baseURL: 'xxxxx', // 设置统一的请求前缀
|
|||
timeout: 10000, // 设置统一的超时时长
|
|||
headers: { |
|||
'Content-Type': 'application/x-www-form-urlencoded', |
|||
}, |
|||
transformRequest: [function (data, headers) { |
|||
// 对 data 进行任意转换处理
|
|||
return qs.stringify(data) |
|||
}], |
|||
|
|||
// `transformResponse` 在传递给 then/catch 前,允许修改响应数据
|
|||
transformResponse: [function (data) { |
|||
// 对 data 进行任意转换处理
|
|||
let res = JSON.parse(data);//返回数据类型转换
|
|||
|
|||
if (res.code == 200) { |
|||
!messageStatus || ElMessage.success(res.message) |
|||
} else if (res.code == 500) { |
|||
!messageStatus || ElMessage.error(res.message) |
|||
} else { |
|||
!messageStatus || ElMessage.warning(res.message) |
|||
} |
|||
|
|||
return data; |
|||
|
|||
}], |
|||
// `paramsSerializer` 是一个负责 `params` 序列化的函数
|
|||
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
|
|||
paramsSerializer: function (params) { |
|||
return qs.stringify(params, { arrayFormat: 'brackets' }) |
|||
}, |
|||
// `withCredentials` 表示跨域请求时是否需要使用凭证
|
|||
withCredentials: false, // default
|
|||
}); |
|||
|
|||
// 请求拦截
|
|||
service.interceptors.request.use(config => { |
|||
removePending(config); |
|||
addPending(config); |
|||
//发起请求的时候 展示loading
|
|||
!loadingStatus || (loading = Loading()); |
|||
// 自动携带token
|
|||
if (getToken() && typeof window !== "undefined") { |
|||
config.headers.Authorization = getToken(); |
|||
} |
|||
return config; |
|||
}, error => { |
|||
return Promise.reject(error); |
|||
}) |
|||
|
|||
|
|||
// 响应拦截
|
|||
service.interceptors.response.use( |
|||
response => { |
|||
//返回请求的时候关闭loading
|
|||
!loadingStatus || loading.close(), loading = null; |
|||
|
|||
let data = JSON.parse(response.data); |
|||
|
|||
if (data.code == 200) { //如果返回成功则返回数据 错误只能拿到null
|
|||
return rawData ? response : data; |
|||
} else { |
|||
return null; |
|||
} |
|||
}, |
|||
error => { |
|||
//返回请求的时候关闭loading
|
|||
!loadingStatus || loading.close(), loading = null; |
|||
// error.config && removePending(error.config);
|
|||
// if(error.config){
|
|||
// removePending(error.config);
|
|||
// }
|
|||
httpErrorStatusHandle(error);//失败的回应
|
|||
return Promise.reject(error); |
|||
} |
|||
); |
|||
return service(config) |
|||
|
|||
}; |
|||
|
|||
function httpErrorStatusHandle(error) { |
|||
// 处理被取消的请求
|
|||
if (axios.isCancel(error)) return console.error('请求的重复请求:' + error.message); |
|||
let message = ''; |
|||
if (error && error.response) { |
|||
switch (error.response.status) { |
|||
case 302: message = '接口重定向了!'; break; |
|||
case 400: message = '参数不正确!'; break; |
|||
case 401: message = '您未登录,或者登录已经超时,请先登录!'; break; |
|||
case 403: message = '您没有权限操作!'; break; |
|||
case 404: message = `请求地址出错: ${error.response.config.url}`; break; // 在正确域名下
|
|||
case 408: message = '请求超时!'; break; |
|||
case 409: message = '系统已存在相同数据!'; break; |
|||
case 500: message = '服务器内部错误!'; break; |
|||
case 501: message = '服务未实现!'; break; |
|||
case 502: message = '网关错误!'; break; |
|||
case 503: message = '服务不可用!'; break; |
|||
case 504: message = '服务暂时无法访问,请稍后再试!'; break; |
|||
case 505: message = 'HTTP版本不受支持!'; break; |
|||
default: message = '异常问题,请联系管理员!'; break |
|||
} |
|||
} |
|||
if (error.message.includes('timeout')) message = '网络请求超时!'; |
|||
if (error.message.includes('Network')) message = window.navigator.onLine ? '服务端异常!' : '您断网了!'; |
|||
|
|||
ElMessage({ |
|||
type: 'error', |
|||
message |
|||
}) |
|||
|
|||
if (error.response.status == '401') { |
|||
//如果登陆失效立马回到登录页
|
|||
setTimeout(() => { |
|||
router.push({ |
|||
path: "/login", |
|||
}) |
|||
}, 2000) |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 储存每个请求的唯一cancel回调, 以此为标识 |
|||
* @param {*} config |
|||
*/ |
|||
function addPending(config) { |
|||
const pendingKey = getPendingKey(config); |
|||
config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => { |
|||
if (!pendingMap.has(pendingKey)) { |
|||
pendingMap.set(pendingKey, cancel); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 删除重复的请求 |
|||
* @param {*} config |
|||
*/ |
|||
function removePending(config) { |
|||
const pendingKey = getPendingKey(config); |
|||
if (pendingMap.has(pendingKey)) { |
|||
const cancelToken = pendingMap.get(pendingKey); |
|||
cancelToken(pendingKey); |
|||
pendingMap.delete(pendingKey); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 生成唯一的每个请求的唯一key |
|||
* @param {*} config |
|||
* @returns |
|||
*/ |
|||
function getPendingKey(config) { |
|||
let { url, method, params, data } = config; |
|||
if (typeof data === 'string') data = JSON.parse(data); // response里面返回的config.data是个字符串对象
|
|||
return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&'); |
|||
} |
|||
|
|||
|
|||
|
@ -0,0 +1,15 @@ |
|||
import Request from "./request"; |
|||
import * as qs from "qs"; |
|||
export const http = new Request({ |
|||
baseURL: import.meta.env.BASE_URL, |
|||
timeout: 10000, |
|||
headers: { |
|||
"Content-Type": "application/x-www-form-urlencoded", |
|||
}, |
|||
transformRequest: [ |
|||
function (data, headers) { |
|||
// 对 data 进行任意转换处理
|
|||
return qs.stringify(data); |
|||
}, |
|||
], |
|||
}); |
@ -0,0 +1,37 @@ |
|||
import axios from "axios"; |
|||
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"; |
|||
import { getToken, removeToken } from "@/utils/auth"; |
|||
class Request { |
|||
// axios 实例
|
|||
instance: AxiosInstance | undefined; |
|||
|
|||
constructor(config: AxiosRequestConfig) { |
|||
this.instance = axios.create(config); |
|||
this.instance.interceptors.request.use( |
|||
(req: AxiosRequestConfig) => { |
|||
//全局请求拦截器
|
|||
if (getToken()) { |
|||
req.headers.token = getToken(); |
|||
} |
|||
return req; |
|||
}, |
|||
(err: any) => err |
|||
); |
|||
this.instance.interceptors.response.use( |
|||
//全局响应拦截器
|
|||
(res: AxiosResponse) => { |
|||
return res.data; |
|||
}, |
|||
(err: any) => err |
|||
); |
|||
} |
|||
|
|||
request(config: AxiosRequestConfig) { |
|||
if (!this.instance) { |
|||
return; |
|||
} |
|||
return this.instance.request(config); |
|||
} |
|||
} |
|||
|
|||
export default Request; |
@ -1,62 +0,0 @@ |
|||
import request from './axios'; |
|||
|
|||
export function getAll(data) { // 资源列表查询
|
|||
return request({ |
|||
url: 'resource/getResource', |
|||
method: 'get', |
|||
params: data, |
|||
}) |
|||
} |
|||
|
|||
export function saveResource(data) { // 资源列表新增
|
|||
return request({ |
|||
url: 'resource/save', |
|||
method: 'post', |
|||
data: data, |
|||
}, { |
|||
loading: true, |
|||
message: true, |
|||
}) |
|||
} |
|||
|
|||
export function editResource(data) { // 资源列表编辑
|
|||
return request({ |
|||
url: 'resource/edit', |
|||
method: 'put', |
|||
data: data, |
|||
}, { |
|||
loading: true, |
|||
message: true, |
|||
}) |
|||
} |
|||
|
|||
export function delResource(data) { // 资源删除
|
|||
return request({ |
|||
url: 'resource/delete', |
|||
method: 'delete', |
|||
params: data, |
|||
}, { |
|||
loading: true, |
|||
message: true, |
|||
}) |
|||
} |
|||
|
|||
export function relationResource(data) { // 角色资源关联
|
|||
return request({ |
|||
url: 'resource/relation', |
|||
method: 'post', |
|||
data: data, |
|||
}, { |
|||
loading: true, |
|||
message: true, |
|||
}) |
|||
} |
|||
|
|||
export function getRoleRelation(data) { // 角色资源关联查询
|
|||
return request({ |
|||
url: 'resource/getRoleRelation', |
|||
method: 'get', |
|||
params: data, |
|||
}) |
|||
} |
|||
|
@ -1,59 +0,0 @@ |
|||
import request from './axios'; |
|||
|
|||
export function getRole(data) { // 角色查询
|
|||
return request({ |
|||
url: 'role/getRole', |
|||
method: 'get', |
|||
params: data, |
|||
}) |
|||
} |
|||
|
|||
export function saveRole(data) { // 角色新增
|
|||
return request({ |
|||
url: 'role/save', |
|||
method: 'post', |
|||
data: data, |
|||
}, { |
|||
loading: true, |
|||
message: true, |
|||
}) |
|||
} |
|||
|
|||
export function editRole(data) { // 角色编辑
|
|||
return request({ |
|||
url: 'role/edit', |
|||
method: 'put', |
|||
data: data, |
|||
}, { |
|||
loading: true, |
|||
message: true, |
|||
}) |
|||
} |
|||
|
|||
export function delRole(data) { // 角色删除
|
|||
return request({ |
|||
url: 'role/delete', |
|||
method: 'delete', |
|||
params: data, |
|||
}, { |
|||
loading: true, |
|||
message: true, |
|||
}) |
|||
} |
|||
|
|||
export function roleRelation(data) { // 角色关联用户
|
|||
return request({ |
|||
url: 'role/relationUser', |
|||
method: 'post', |
|||
data: data, |
|||
}) |
|||
} |
|||
|
|||
export function getUserRole(data) { // 角色关联用户
|
|||
return request({ |
|||
url: '/role/getUserRole', |
|||
method: 'get', |
|||
params: data, |
|||
}) |
|||
} |
|||
|
@ -1,72 +0,0 @@ |
|||
import request from './axios'; |
|||
|
|||
export function login(data) { //例子 登陆
|
|||
return request({ |
|||
url: '/user/login', |
|||
method: 'post', |
|||
data:data, |
|||
},{ |
|||
message:true, |
|||
}) |
|||
} |
|||
|
|||
export function register(data) { //例子 注册
|
|||
return request({ |
|||
url: '/user/addUser', |
|||
method: 'post', |
|||
data:data, |
|||
},{ |
|||
message:true, |
|||
loading:true, |
|||
}) |
|||
} |
|||
|
|||
|
|||
|
|||
export function getAll(data) { //例子 用户列表查询
|
|||
return request({ |
|||
url: '/user/getAll', |
|||
method: 'get', |
|||
params: data, |
|||
}) |
|||
} |
|||
|
|||
export function getUserInfo(data) { //例子 用户信息查询
|
|||
return request({ |
|||
url: '/user/getUserInfo', |
|||
method: 'get', |
|||
params: data, |
|||
}) |
|||
} |
|||
|
|||
export function delUser(data) { //例子 删除用户
|
|||
return request({ |
|||
url: '/user/delUser', |
|||
method: 'post', |
|||
data: data, |
|||
}, { |
|||
message: true, |
|||
loading: true, |
|||
}) |
|||
} |
|||
|
|||
export function changePass(data) { //密码修改
|
|||
return request({ |
|||
url: '/user/changePass', |
|||
method: 'post', |
|||
data: data, |
|||
}, { |
|||
message: true, |
|||
}) |
|||
} |
|||
|
|||
export function modifyBaseInfo(data) { //用户信息修改
|
|||
return request({ |
|||
url: '/user/modifyBaseInfo', |
|||
method: 'post', |
|||
data: data, |
|||
}, { |
|||
message: true, |
|||
}) |
|||
} |
|||
|
@ -0,0 +1,11 @@ |
|||
import { http } from "./http"; |
|||
|
|||
export function getInfo(data: any) { |
|||
// 资源列表查询
|
|||
return http.request({ |
|||
url: "/api/login", |
|||
method: "post", |
|||
data, |
|||
}); |
|||
} |
|||
|
2335
src/assets/css/font-awesome.min.css
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1
src/assets/image/workbench.svg
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,15 @@ |
|||
export const FONT_SIZE = [ |
|||
//字体大小设置
|
|||
{ |
|||
value: "large", |
|||
label: "大的按钮", |
|||
}, |
|||
{ |
|||
value: "default", |
|||
label: "标准按钮", |
|||
}, |
|||
{ |
|||
value: "small", |
|||
label: "小的按钮", |
|||
}, |
|||
]; |
@ -1,61 +0,0 @@ |
|||
//配置引导设置
|
|||
const steps = [ |
|||
{ |
|||
element: '.noviceGuide', |
|||
popover: { |
|||
title: "Hello", |
|||
description: "欢迎来到Admin-Frame的新手引导", |
|||
position: "bottom" |
|||
} |
|||
}, |
|||
{ |
|||
element: '.zan-sidebar-nav', |
|||
popover: { |
|||
title: "介绍", |
|||
description: "Admin-Frame是一款中/后台管理系统", |
|||
position: "bottom" |
|||
} |
|||
}, |
|||
{ |
|||
element: '.fa-arrows-alt', |
|||
popover: { |
|||
title: "全屏", |
|||
description: "打开全屏模式", |
|||
position: "left" |
|||
} |
|||
}, |
|||
{ |
|||
element: '.g-language', |
|||
popover: { |
|||
title: "国际化", |
|||
description: "点击切换语言", |
|||
position: "left" |
|||
} |
|||
}, |
|||
{ |
|||
element: '.fa-bell-o', |
|||
popover: { |
|||
title: "消息中心", |
|||
description: "点击查看消息中心", |
|||
position: "left" |
|||
} |
|||
}, |
|||
{ |
|||
element: '.fabtn', |
|||
popover: { |
|||
title: "菜单开关", |
|||
description: "控制菜单的显示/隐藏", |
|||
position: "right" |
|||
} |
|||
}, |
|||
{ |
|||
element: '.el-menu', |
|||
popover: { |
|||
title: "快来试试吧~", |
|||
description: "菜单中包含首页图、各种功能、页面模板、编辑器、返回结果、异常页面、消息中心、权限管理等... 详情请点击菜单查看", |
|||
position: "right" |
|||
} |
|||
}, |
|||
]; |
|||
|
|||
export default steps |
@ -0,0 +1,70 @@ |
|||
//配置引导设置
|
|||
const steps = [ |
|||
{ |
|||
element: ".noviceGuide", |
|||
popover: { |
|||
title: "Hello", |
|||
description: "欢迎来到Admin-Frame的新手引导", |
|||
position: "bottom", |
|||
}, |
|||
}, |
|||
{ |
|||
element: ".admin-sidebar-nav", |
|||
popover: { |
|||
title: "介绍", |
|||
description: "Admin-Frame是一款中/后台管理系统", |
|||
position: "bottom", |
|||
}, |
|||
}, |
|||
{ |
|||
element: ".fa-arrows-alt", |
|||
popover: { |
|||
title: "全屏", |
|||
description: "打开全屏模式", |
|||
position: "left", |
|||
}, |
|||
}, |
|||
{ |
|||
element: ".g-language", |
|||
popover: { |
|||
title: "国际化", |
|||
description: "点击切换语言", |
|||
position: "left", |
|||
}, |
|||
}, |
|||
{ |
|||
element: ".g-font", |
|||
popover: { |
|||
title: "按钮设置", |
|||
description: "点击切换按钮大小", |
|||
position: "left", |
|||
}, |
|||
}, |
|||
{ |
|||
element: ".fa-bell-o", |
|||
popover: { |
|||
title: "我的消息", |
|||
description: "点击查看我的消息", |
|||
position: "left", |
|||
}, |
|||
}, |
|||
{ |
|||
element: ".fabtn", |
|||
popover: { |
|||
title: "菜单开关", |
|||
description: "控制菜单的显示/隐藏", |
|||
position: "right", |
|||
}, |
|||
}, |
|||
{ |
|||
element: ".el-menu", |
|||
popover: { |
|||
title: "快来试试吧~", |
|||
description: |
|||
"菜单中包含首页图、各种功能、页面模板、编辑器、返回结果、异常页面、消息中心、权限管理等... 详情请点击菜单查看", |
|||
position: "right", |
|||
}, |
|||
}, |
|||
]; |
|||
|
|||
export default steps; |
@ -1,201 +0,0 @@ |
|||
import { i18n } from '../../locales/i18n'; //国际化
|
|||
const { global: { t, tm, locale } } = i18n; |
|||
export default [ |
|||
//资源信息
|
|||
{ |
|||
resourceName: 'message.resource.homePage.name', |
|||
resourceUrl: "/homePage", |
|||
resourceIcon: 'fa fa-tachometer', |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.function.name", |
|||
resourceUrl: "/gongneng", |
|||
resourceIcon: 'fa fa-wrench', |
|||
children: [ |
|||
{ |
|||
resourceName: "message.resource.function.chlidren.dialogDrag", |
|||
resourceUrl: "/dialogDrag", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.function.chlidren.wartermark", |
|||
resourceUrl: "/wartermark", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.function.chlidren.timePicker", |
|||
resourceUrl: "/timePicker", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.function.chlidren.map", |
|||
resourceUrl: "/map", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.function.chlidren.copy", |
|||
resourceUrl: "/copy", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.function.chlidren.qrcode", |
|||
resourceUrl: "/qrcode", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.function.chlidren.infiniteScroll", |
|||
resourceUrl: "/infiniteScroll", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.function.chlidren.computerMonitor", |
|||
resourceUrl: "/computerMonitor", |
|||
children: [] |
|||
}, |
|||
] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.template.name", |
|||
resourceUrl: "/content", |
|||
resourceIcon: "fa fa-file-text", |
|||
children: [ |
|||
{ |
|||
resourceName: "message.resource.template.chlidren.easyForm", |
|||
resourceUrl: "/easyForm", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.template.chlidren.tableOperation", |
|||
resourceUrl: "/tableOperation", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.template.chlidren.cardList", |
|||
resourceUrl: "/cardList", |
|||
children: [] |
|||
}, |
|||
] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.editor.name", |
|||
resourceUrl: "/editor", |
|||
resourceIcon: "fa fa-bold", |
|||
children: [ |
|||
{ |
|||
resourceName: "message.resource.editor.chlidren.markdown", |
|||
resourceUrl: "/markdown", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.editor.chlidren.textEditor", |
|||
resourceUrl: "/textEditor", |
|||
children: [] |
|||
}, |
|||
] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.result.name", |
|||
resourceUrl: "/result", |
|||
resourceIcon: "fa fa-random", |
|||
children: [ |
|||
{ |
|||
resourceName: "message.resource.result.chlidren.successTip", |
|||
resourceUrl: "/successTip", |
|||
children: [] |
|||
}, { |
|||
resourceName: "message.resource.result.chlidren.warningTip", |
|||
resourceUrl: "/warningTip", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.result.chlidren.errorTip", |
|||
resourceUrl: "/errorTip", |
|||
children: [] |
|||
}, |
|||
] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.abnormalPage.name", |
|||
resourceUrl: "/error", |
|||
resourceIcon: "fa fa-exclamation-triangle", |
|||
children: [{ |
|||
resourceName: "message.resource.abnormalPage.children.404", |
|||
resourceUrl: "/404", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.abnormalPage.children.403", |
|||
resourceUrl: "/403", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.abnormalPage.children.noData", |
|||
resourceUrl: "/noData", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.abnormalPage.children.build", |
|||
resourceUrl: "/build", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.abnormalPage.children.networkError", |
|||
resourceUrl: "/networkError", |
|||
children: [] |
|||
}, |
|||
] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.workflow.name", |
|||
resourceUrl: "/workflow", |
|||
resourceIcon: 'fa fa-crosshairs', |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.message.name", |
|||
resourceUrl: "/messageManagement", |
|||
resourceIcon: "fa fa-comment", |
|||
children: [ |
|||
{ |
|||
resourceName: "message.resource.message.children.messageCenter", |
|||
resourceUrl: "/messageCenter", |
|||
children: [] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.authority.name", |
|||
resourceUrl: "/setting", |
|||
resourceIcon: "fa fa-cog", |
|||
children: [ |
|||
{ |
|||
resourceName: "message.resource.authority.children.user", |
|||
resourceUrl: "/user", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.authority.children.role", |
|||
resourceUrl: "/role", |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.authority.children.resource", |
|||
resourceUrl: "/resource", |
|||
children: [] |
|||
}, |
|||
] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.noviceGuide.name", |
|||
resourceUrl: "/noviceGuide", |
|||
resourceIcon: 'fa fa-question-circle-o', |
|||
children: [] |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.i18n.name", |
|||
resourceUrl: "/i18n", |
|||
resourceIcon: 'fa fa-language', |
|||
children: [] |
|||
}, |
|||
] |
@ -0,0 +1,191 @@ |
|||
export default [ |
|||
//资源信息
|
|||
{ |
|||
resourceName: "message.resource.dashboard.name", |
|||
resourceUrl: "/dashboard", |
|||
resourceIcon: "fa fa-tachometer", |
|||
children: [ |
|||
{ |
|||
resourceName: "message.resource.dashboard.children.workbench", |
|||
resourceUrl: "/dashboard/workbench", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.dashboard.children.analysis", |
|||
resourceUrl: "/dashboard/analysis", |
|||
children: [], |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.function.name", |
|||
resourceUrl: "/components", |
|||
resourceIcon: "fa fa-wrench", |
|||
children: [ |
|||
{ |
|||
resourceName: "message.resource.function.children.dialogDrag", |
|||
resourceUrl: "/components/dialogDrag", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.function.children.watermark", |
|||
resourceUrl: "/components/watermark", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.function.children.copy", |
|||
resourceUrl: "/components/copy", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.function.children.qrCode", |
|||
resourceUrl: "/components/qrCode", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.function.children.infiniteScroll", |
|||
resourceUrl: "/components/infiniteScroll", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.function.children.computerMonitor", |
|||
resourceUrl: "/components/computerMonitor", |
|||
children: [], |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.template.name", |
|||
resourceUrl: "/template", |
|||
resourceIcon: "fa fa-file-text", |
|||
children: [ |
|||
{ |
|||
resourceName: "message.resource.template.children.easyForm", |
|||
resourceUrl: "/template/easyForm", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.template.children.tableOperation", |
|||
resourceUrl: "/template/tableOperation", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.template.children.cardList", |
|||
resourceUrl: "/template/cardList", |
|||
children: [], |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.editor.name", |
|||
resourceUrl: "/editor", |
|||
resourceIcon: "fa fa-bold", |
|||
children: [ |
|||
{ |
|||
resourceName: "message.resource.editor.children.markdown", |
|||
resourceUrl: "/editor/markdown", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.editor.children.textEditor", |
|||
resourceUrl: "/editor/textEditor", |
|||
children: [], |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.result.name", |
|||
resourceUrl: "/tips", |
|||
resourceIcon: "fa fa-random", |
|||
children: [ |
|||
{ |
|||
resourceName: "message.resource.result.children.successTip", |
|||
resourceUrl: "/tips/successTip", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.result.children.warningTip", |
|||
resourceUrl: "/tips/warningTip", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.result.children.errorTip", |
|||
resourceUrl: "/tips/errorTip", |
|||
children: [], |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.abnormalPage.name", |
|||
resourceUrl: "/global/abnormal", |
|||
resourceIcon: "fa fa-exclamation-triangle", |
|||
children: [ |
|||
{ |
|||
resourceName: "message.resource.abnormalPage.children.404", |
|||
resourceUrl: "/global/abnormal/404", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.abnormalPage.children.403", |
|||
resourceUrl: "/global/abnormal/403", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.abnormalPage.children.build", |
|||
resourceUrl: "/global/abnormal/build", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.abnormalPage.children.networkError", |
|||
resourceUrl: "/global/abnormal/networkError", |
|||
children: [], |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.message.name", |
|||
resourceUrl: "/messageManagement", |
|||
resourceIcon: "fa fa-comment", |
|||
children: [ |
|||
{ |
|||
resourceName: "message.resource.message.children.feedbackCenter", |
|||
resourceUrl: "/main/feedbackCenter", |
|||
children: [], |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.authority.name", |
|||
resourceUrl: "/setting", |
|||
resourceIcon: "fa fa-cog", |
|||
children: [ |
|||
{ |
|||
resourceName: "message.resource.authority.children.user", |
|||
resourceUrl: "/setting/user", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.authority.children.role", |
|||
resourceUrl: "/setting/role", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.authority.children.resource", |
|||
resourceUrl: "/setting/resource", |
|||
children: [], |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.noviceGuide.name", |
|||
resourceUrl: "/main/noviceGuide", |
|||
resourceIcon: "fa fa-question-circle-o", |
|||
children: [], |
|||
}, |
|||
{ |
|||
resourceName: "message.resource.i18n.name", |
|||
resourceUrl: "/main/i18n", |
|||
resourceIcon: "fa fa-language", |
|||
children: [], |
|||
}, |
|||
]; |
@ -1,50 +0,0 @@ |
|||
//记录版本日志
|
|||
export const versionLog = [ |
|||
{ |
|||
version: "V1.0.6", |
|||
description: |
|||
['配置国际化.', |
|||
], |
|||
releaseDate: "2021/12/20" |
|||
}, |
|||
{ |
|||
version: "V1.0.5", |
|||
description: |
|||
['新增新手引导功能.', |
|||
], |
|||
releaseDate: "2021/12/09" |
|||
}, |
|||
{ |
|||
version: "V1.0.4", |
|||
description: |
|||
['优化首屏加载等待动画.', |
|||
'修复Echarts切换页面不显示问题.', |
|||
], |
|||
releaseDate: "2021/12/08" |
|||
}, |
|||
{ |
|||
version: "V1.0.3", |
|||
description: |
|||
['新增版本日志.', |
|||
'新增工作流程(建设中).', |
|||
'添加被动事件监听器来阻止touchstart事件.', |
|||
], |
|||
releaseDate: "2021/12/07" |
|||
}, |
|||
{ |
|||
version: "V1.0.2", |
|||
description: |
|||
['新增卡片列表.' |
|||
], |
|||
releaseDate: "2021/11/26" |
|||
}, |
|||
{ |
|||
version: "V1.0.1", |
|||
description: |
|||
['vue版本升级至3.2.22.', |
|||
'项目中添加vueuse库.', |
|||
'新增生成二维码、监测电脑信息功能.' |
|||
], |
|||
releaseDate: "2021/11/24" |
|||
}, |
|||
]; |
@ -0,0 +1,41 @@ |
|||
//记录版本日志
|
|||
export const versionLog = [ |
|||
{ |
|||
version: "V1.0.6", |
|||
description: ["配置国际化."], |
|||
releaseDate: "2021/12/20", |
|||
}, |
|||
{ |
|||
version: "V1.0.5", |
|||
description: ["新增新手引导功能."], |
|||
releaseDate: "2021/12/09", |
|||
}, |
|||
{ |
|||
version: "V1.0.4", |
|||
description: ["优化首屏加载等待动画.", "修复Echarts切换页面不显示问题."], |
|||
releaseDate: "2021/12/08", |
|||
}, |
|||
{ |
|||
version: "V1.0.3", |
|||
description: [ |
|||
"新增版本日志.", |
|||
"新增工作流程(建设中).", |
|||
"添加被动事件监听器来阻止touchstart事件.", |
|||
], |
|||
releaseDate: "2021/12/07", |
|||
}, |
|||
{ |
|||
version: "V1.0.2", |
|||
description: ["新增卡片列表."], |
|||
releaseDate: "2021/11/26", |
|||
}, |
|||
{ |
|||
version: "V1.0.1", |
|||
description: [ |
|||
"vue版本升级至3.2.22.", |
|||
"项目中添加vueUse库.", |
|||
"新增生成二维码、监测电脑信息功能.", |
|||
], |
|||
releaseDate: "2021/11/24", |
|||
}, |
|||
]; |
2700
src/assets/scss/_icons.scss
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,5 +1,9 @@ |
|||
// Screen Readers |
|||
// ------------------------- |
|||
|
|||
.sr-only { @include sr-only(); } |
|||
.sr-only-focusable { @include sr-only-focusable(); } |
|||
.sr-only { |
|||
@include sr-only(); |
|||
} |
|||
.sr-only-focusable { |
|||
@include sr-only-focusable(); |
|||
} |
@ -0,0 +1,8 @@ |
|||
/* 只需要重写你需要的即可 */ |
|||
@forward "element-plus/theme-chalk/src/common/var.scss" with ( |
|||
$colors: ( |
|||
"primary": ( |
|||
"base": green, |
|||
), |
|||
) |
|||
); |
@ -0,0 +1,8 @@ |
|||
// event.js
|
|||
import { onMounted, onUnmounted } from "vue"; |
|||
export function useEventListener(target: any, event: any, callback: any) { |
|||
// 如果你想的话,
|
|||
// 也可以用字符串形式的 CSS 选择器来寻找目标 DOM 元素
|
|||
onMounted(() => target.addEventListener(event, callback)); |
|||
onUnmounted(() => target.removeEventListener(event, callback)); |
|||
} |
@ -0,0 +1,20 @@ |
|||
// mouse.js
|
|||
import { ref } from "vue"; |
|||
import { useEventListener } from "./event"; |
|||
|
|||
export function useMouse() { |
|||
const x = ref<string | number>(0); |
|||
const y = ref<string | number>(0); |
|||
|
|||
interface Event { |
|||
pageX: string | number; |
|||
pageY: string | number; |
|||
} |
|||
|
|||
useEventListener(window, "mousemove", (e: Event) => { |
|||
x.value = e.pageX; |
|||
y.value = e.pageY; |
|||
}) |
|||
|
|||
return { x, y }; |
|||
} |
@ -1,60 +0,0 @@ |
|||
let watermark = {}; |
|||
const idd = '1.234523841642.1234124163'; |
|||
var _interval = null; |
|||
let setWatermark = (str) => { |
|||
let id = idd; |
|||
if (document.getElementById(id) !== null) { |
|||
document.body.removeChild(document.getElementById(id)); |
|||
} |
|||
// 创建一个画布
|
|||
let can = document.createElement('canvas'); |
|||
// 设置画布的长宽
|
|||
can.width = 220; |
|||
can.height = 180; |
|||
let cans = can.getContext('2d'); |
|||
// 设置旋转角度
|
|||
cans.rotate(-20 * Math.PI / 150); |
|||
cans.font = '16px Vedana'; |
|||
// 设置填充绘画的颜色、渐变或者模式
|
|||
cans.fillStyle = 'rgba(200, 200, 200, 0.70)'; |
|||
// 设置文本内容的当前对齐方式
|
|||
cans.textAlign = 'center'; |
|||
// 设置在绘制文本时使用的当前文本基线
|
|||
cans.textBaseline = 'Middle'; |
|||
// 在画布上绘制填色的文本(输出的文本,开始绘制文本的X坐标位置,开始绘制文本的Y坐标位置)
|
|||
cans.fillText(str, can.width / 3, can.height / 2); |
|||
let div = document.createElement('div'); |
|||
div.id = id; |
|||
div.style.pointerEvents = 'none'; |
|||
div.style.top = '20px'; |
|||
div.style.left = '0px'; |
|||
div.style.position = 'fixed'; |
|||
div.style.zIndex = '100000'; |
|||
div.style.width = document.documentElement.clientWidth + 'px'; |
|||
div.style.height = document.documentElement.clientHeight + 'px'; |
|||
div.style.background = 'url(' + can.toDataURL('image/png') + ') left top repeat'; |
|||
document.body.appendChild(div); |
|||
return id; |
|||
}; |
|||
// 该方法只允许调用一次
|
|||
// 添加水印的方法
|
|||
watermark.set = (str) => { |
|||
let id = setWatermark(str); |
|||
_interval = setInterval(() => { |
|||
if (document.getElementById(id) === null) { |
|||
id = setWatermark(str); |
|||
} |
|||
}, 500); |
|||
window.onresize = () => { |
|||
setWatermark(str); |
|||
}; |
|||
}; |
|||
// 移除水印的方法
|
|||
watermark.remove = () => { |
|||
if (document.getElementById(idd) !== null) { |
|||
var box = document.getElementById(idd); |
|||
box.parentNode.removeChild(box); |
|||
_interval ? clearInterval(_interval) : ''; |
|||
} |
|||
}; |
|||
export default watermark; |
@ -0,0 +1,52 @@ |
|||
const id = "1.234523841642.1234124163"; |
|||
const setWatermark = (str: string) => { |
|||
if (document.getElementById(id) !== null) { |
|||
document.body.removeChild(document.getElementById(id) as HTMLElement); |
|||
} |
|||
// 创建一个画布
|
|||
const canvas = document.createElement("canvas"); |
|||
// 设置画布的长宽
|
|||
canvas.width = 220; |
|||
canvas.height = 180; |
|||
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D; |
|||
// 设置旋转角度
|
|||
ctx.rotate((-20 * Math.PI) / 150); |
|||
ctx.font = "15px Vedana"; |
|||
// 设置填充绘画的颜色、渐变或者模式
|
|||
ctx.fillStyle = "rgba(200, 200, 200, 0.70)"; |
|||
// 设置文本内容的当前对齐方式
|
|||
ctx.textAlign = "center"; |
|||
// 设置在绘制文本时使用的当前文本基线
|
|||
ctx.textBaseline = "middle"; |
|||
// 在画布上绘制填色的文本(输出的文本,开始绘制文本的X坐标位置,开始绘制文本的Y坐标位置)
|
|||
ctx.fillText(str, canvas.width / 3, canvas.height / 2); |
|||
const div = document.createElement("div"); |
|||
div.id = id; |
|||
div.style.pointerEvents = "none"; |
|||
div.style.top = "40px"; |
|||
div.style.left = "0px"; |
|||
div.style.opacity = "0.6"; |
|||
div.style.position = "fixed"; |
|||
div.style.zIndex = "100000"; |
|||
div.style.width = document.documentElement.clientWidth + "px"; |
|||
div.style.height = document.documentElement.clientHeight + "px"; |
|||
div.style.background = |
|||
"url(" + canvas.toDataURL("image/png") + ") left top repeat"; |
|||
document.body.appendChild(div); |
|||
return id; |
|||
} |
|||
// 该方法只允许调用一次
|
|||
// 添加水印的方法
|
|||
export const setWaterMarker = (str: string) => { |
|||
let id = setWatermark(str); |
|||
if (document.getElementById(id) === null) { |
|||
id = setWatermark(str); |
|||
} |
|||
}; |
|||
// 移除水印的方法
|
|||
export const removeWaterMarker = () => { |
|||
if (document.getElementById(id) !== null) { |
|||
const box = document.getElementById(id) as HTMLElement; |
|||
box?.parentNode?.removeChild(box); |
|||
} |
|||
}; |
@ -1,288 +0,0 @@ |
|||
<template> |
|||
<div class="zan-header"> |
|||
<div class="collapse-btn" @click="switchCollapse"> |
|||
<i v-if="collapse" class="fa fabtn fa-indent"></i> |
|||
<i v-else class="fa fabtn fa-dedent"></i> |
|||
</div> |
|||
<div class="collapse-right"> |
|||
<el-tooltip |
|||
class="item" |
|||
effect="dark" |
|||
:content="transitionLocal('message.public.fullScreen')" |
|||
placement="bottom" |
|||
> |
|||
<span class="faSpan"> |
|||
<i class="fa fa-arrows-alt" @click="requestFullScreen('body')"></i> |
|||
</span> |
|||
</el-tooltip> |
|||
<el-dropdown @command="changeI18n"> |
|||
<span class="el-dropdown-link faSpan"> |
|||
<el-tooltip |
|||
class="item" |
|||
effect="dark" |
|||
:content="transitionLocal('message.public.languageSwitch')" |
|||
placement="left" |
|||
> |
|||
<i class="fa fa-language g-language" style="font-size: 18px"></i> |
|||
</el-tooltip> |
|||
</span> |
|||
|
|||
<template #dropdown> |
|||
<el-dropdown-menu> |
|||
<el-dropdown-item |
|||
v-for="locale in $i18n.availableLocales" |
|||
:key="`locale-${locale}`" |
|||
:command="locale" |
|||
:style="{ |
|||
color: $i18n.locale == locale ? 'rgb(64, 158, 255)' : '', |
|||
}" |
|||
>{{ $filters.langFilter(locale) }}</el-dropdown-item> |
|||
</el-dropdown-menu> |
|||
</template> |
|||
</el-dropdown> |
|||
<el-tooltip |
|||
class="item" |
|||
effect="dark" |
|||
:content="transitionLocal('message.public.messageCenter')" |
|||
placement="bottom" |
|||
> |
|||
<span class="faSpan"> |
|||
<el-badge is-dot class="item"> |
|||
<i class="fa faPad fa-bell-o" @click="toGetMessage"></i> |
|||
</el-badge> |
|||
</span> |
|||
</el-tooltip> |
|||
<!-- 用户名下拉菜单 --> |
|||
<el-dropdown size="small" trigger="click" @command="handleCommand"> |
|||
<span class="el-dropdown-link btn_username_group"> |
|||
<span class="btn_username" :title="username">{{ username }}</span> |
|||
<i class="el-icon-caret-bottom"></i> |
|||
</span> |
|||
<template #dropdown> |
|||
<el-dropdown-menu> |
|||
<el-dropdown-item |
|||
divided |
|||
command="signOut" |
|||
>{{ transitionLocal('message.public.loggedOut') }}</el-dropdown-item> |
|||
<el-dropdown-item |
|||
command="versionLog" |
|||
divided |
|||
>{{ transitionLocal('message.public.versionLog') }}</el-dropdown-item> |
|||
<el-dropdown-item |
|||
command="baseInfo" |
|||
divided |
|||
>{{ transitionLocal('message.public.basicInfo') }}</el-dropdown-item> |
|||
<el-dropdown-item |
|||
command="checkPass" |
|||
divided |
|||
>{{ transitionLocal('message.public.changePassword') }}</el-dropdown-item> |
|||
</el-dropdown-menu> |
|||
</template> |
|||
</el-dropdown> |
|||
<!-- 用户头像 --> |
|||
<div class="user-avatar"> |
|||
<img src="../../assets/image/touxiang.jpg" /> |
|||
<!-- <el-avatar icon="el-icon-user-solid"> |
|||
</el-avatar>--> |
|||
</div> |
|||
</div> |
|||
<check-pass v-model:passVisible="passVisible"></check-pass> |
|||
<base-info ref="baseInfoRef" v-model:baseVisible="baseVisible"></base-info> |
|||
<version-log v-model:versionVisible="versionVisible"></version-log> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
import { |
|||
defineComponent, |
|||
getCurrentInstance, |
|||
toRefs, |
|||
reactive, |
|||
ref, |
|||
computed, |
|||
watch, |
|||
provide, |
|||
shallowRef, |
|||
} from "vue"; |
|||
import { useStore } from "vuex"; |
|||
import { useRouter } from "vue-router"; |
|||
import { ElMessage } from "element-plus"; |
|||
import Cookies from "js-cookie"; |
|||
import checkPass from "../Setting/checkPass.vue"; |
|||
import baseInfo from "../Setting/baseInfo.vue"; |
|||
import versionLog from "../Setting/versionLog.vue"; |
|||
import { useI18n } from "vue-i18n"; |
|||
import { transitionLocal } from '../../locales/i18n' |
|||
export default defineComponent({ |
|||
name: "zan-header", |
|||
components: { |
|||
checkPass, |
|||
baseInfo, |
|||
versionLog, |
|||
}, |
|||
setup(context, props) { |
|||
let { proxy } = getCurrentInstance(); // vue原型 |
|||
const { t, locale: language } = useI18n(); |
|||
const store = useStore(); //vuex |
|||
const router = useRouter(); //路由 |
|||
const baseInfoRef = ref("null"); |
|||
const state = reactive({ |
|||
collapse: computed(() => store.state.collapse), |
|||
username: computed(() => localStorage.getItem("username") || "待完善"), |
|||
passVisible: false, //修改密码弹框 |
|||
baseVisible: false, //基本信息弹框 |
|||
versionVisible: false, //版本日志弹框 |
|||
driver: null, //引导实例 |
|||
}); |
|||
|
|||
const requestFullScreen = (element) => { |
|||
//进入全屏 退出全屏 |
|||
const isFullScreen = |
|||
document.fullScreen || |
|||
document.mozFullScreen || |
|||
document.webkitIsFullScreen || |
|||
document.msFullscreenElement; //判断窗口是否全屏 |
|||
let ele = document.querySelector(element) || document.documentElement; //获取元素 |
|||
if (!isFullScreen) { |
|||
if (ele.requestFullscreen) { |
|||
ele.requestFullscreen(); |
|||
} else if (ele.mozRequestFullScreen) { |
|||
ele.mozRequestFullScreen(); |
|||
} else if (ele.webkitRequestFullscreen) { |
|||
ele.webkitRequestFullscreen(); |
|||
} else if (ele.msRequestFullscreen) { |
|||
ele.msRequestFullscreen(); |
|||
} |
|||
} else { |
|||
if (document.exitFullScreen) { |
|||
document.exitFullScreen(); |
|||
} else if (document.mozCancelFullScreen) { |
|||
document.mozCancelFullScreen(); |
|||
} else if (document.webkitExitFullscreen) { |
|||
document.webkitExitFullscreen(); |
|||
} else if (element.msExitFullscreen) { |
|||
element.msExitFullscreen(); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
const switchCollapse = () => { |
|||
//菜单栏展开关闭 |
|||
setTimeout(() => { |
|||
store.commit("switchCollapse", !state.collapse); |
|||
}, 0); |
|||
}; |
|||
|
|||
const handleCommand = (command) => { |
|||
//用户下拉菜单 |
|||
if (command == "signOut") { |
|||
Cookies.remove("token"); //清空token 1秒后回到登录页 |
|||
store.commit("delRightMenu", { |
|||
//退出清空所有菜单 |
|||
whiteTags: [], |
|||
}); |
|||
router.push("/login"); |
|||
ElMessage.success("登出成功"); |
|||
} else if (command == "checkPass") { |
|||
//打开修改密码弹框 |
|||
state.passVisible = true; |
|||
} else if (command == "baseInfo") { |
|||
//打开基本信息弹框 |
|||
baseInfoRef.value.openBaseInfo(store.state.user.user); |
|||
} else if (command == "versionLog") { |
|||
//打开版本日志弹框 |
|||
state.versionVisible = true; |
|||
} |
|||
}; |
|||
|
|||
const toGetMessage = () => { |
|||
//进入消息中心 |
|||
router.push("/messageCenter"); |
|||
}; |
|||
|
|||
const changeI18n = (type) => { |
|||
//切换中英文 |
|||
if (type == Cookies.get("lang")) { |
|||
//如果已经是这个语言则提醒 |
|||
ElMessage.warning(`${t("message.public.existence")}`); |
|||
return false; |
|||
} |
|||
Cookies.set("lang", type); //存储国际化 |
|||
language.value = type; //更新i18n配置 |
|||
proxy.$i18n.locale = type; //更新i18n配置 |
|||
ElMessage.success(`${t("message.public.editLang")}`); |
|||
}; |
|||
|
|||
return { |
|||
...toRefs(state), |
|||
baseInfoRef, |
|||
switchCollapse, |
|||
handleCommand, |
|||
toGetMessage, |
|||
changeI18n, |
|||
requestFullScreen, |
|||
transitionLocal |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.zan-header { |
|||
box-sizing: border-box; |
|||
width: 100%; |
|||
height: 64px; |
|||
font-size: 18px; |
|||
color: #616161; |
|||
background: #fff; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
|
|||
.collapse-btn { |
|||
// float: left; |
|||
padding: 0px 15px 0 15px; |
|||
cursor: pointer; |
|||
line-height: 64px; |
|||
} |
|||
|
|||
.el-icon-s-fold, |
|||
.el-icon-s-unfold { |
|||
font-size: 25px; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.collapse-right { |
|||
float: right; |
|||
padding-right: 20px; |
|||
height: 64px; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
.faSpan { |
|||
padding-right: 15px; |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
|
|||
.user-avatar { |
|||
margin: 0 15px 0 5px; |
|||
} |
|||
|
|||
img { |
|||
display: block; |
|||
width: 40px; |
|||
height: 40px; |
|||
border-radius: 50%; |
|||
} |
|||
.btn_username_group { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
.btn_username { |
|||
text-align: right; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
width: 60px; |
|||
padding-right: 8px; |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -1,31 +1,52 @@ |
|||
<template> |
|||
<el-pagination |
|||
@current-change="currentChange" |
|||
:current-page="pagination.currentPage" |
|||
:page-sizes="[10]" |
|||
:page-size="pagination.pageSize" |
|||
layout="total, sizes, prev, pager, next, jumper" |
|||
:total="pagination.total" |
|||
></el-pagination> |
|||
<el-pagination v-model:currentPage="pagination.page" v-model:page-size="pagination.pageSize" background |
|||
:page-sizes="[10, 20, 100, 200]" layout="sizes,prev, pager, next, jumper,total" :total="pagination.total" |
|||
@size-change="sizeChange" @current-change="currentChange" class="p-location" /> |
|||
</template> |
|||
<script> |
|||
import { defineComponent, reactive, toRefs, watch, inject } from "vue"; |
|||
export default defineComponent({ |
|||
setup(props, context) { |
|||
const pagination = inject("pagination"); //接受 |
|||
|
|||
const currentChange = (val) => { |
|||
//切换分页 |
|||
context.emit("change", { |
|||
<script setup lang="ts" name="AdminPagination"> |
|||
import { PaginationState } from "@/types"; |
|||
|
|||
withDefaults( |
|||
defineProps<{ |
|||
pagination: Partial<PaginationState>; |
|||
}>(), |
|||
{ |
|||
pagination: () => { |
|||
return { |
|||
page: 1, |
|||
pageSize: 10, |
|||
page: val, |
|||
}); |
|||
}; |
|||
total: 100, |
|||
}; |
|||
}, |
|||
} |
|||
); |
|||
|
|||
const emits = defineEmits<{ |
|||
( |
|||
e: "change", |
|||
target: { |
|||
page?: number; |
|||
pageSize?: number; |
|||
} |
|||
): void; |
|||
}>(); |
|||
|
|||
const currentChange = (page: number) => { |
|||
//切换分页 |
|||
emits("change", { |
|||
page, |
|||
}); |
|||
} |
|||
|
|||
return { |
|||
pagination, |
|||
currentChange, |
|||
}; |
|||
}, |
|||
}); |
|||
const sizeChange = (pageSize: number) => { |
|||
//切换展现条数 回到第一页 |
|||
emits("change", { pageSize, page: 1 }); |
|||
} |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.p-location { |
|||
display: flex; |
|||
justify-content: right; |
|||
} |
|||
</style> |
@ -1,192 +0,0 @@ |
|||
<template> |
|||
<el-drawer |
|||
:before-close="close" |
|||
:model-value="baseVisible" |
|||
title="基本信息" |
|||
size="800px" |
|||
@open="getInit" |
|||
> |
|||
<el-form ref="baseInfoRef" :model="baseInfo" :rules="baseInfoRules" label-width="114px"> |
|||
<el-row> |
|||
<el-col :span="10"> |
|||
<el-form-item label="姓名:"> |
|||
<el-input v-model="baseInfo.staffName"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="10"> |
|||
<el-form-item label="用户名:" prop="username"> |
|||
<el-input v-model="baseInfo.username"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="10"> |
|||
<el-form-item label="性别:"> |
|||
<el-select v-model="baseInfo.sex" placeholder="请选择性别"> |
|||
<el-option label="女" value="0"></el-option> |
|||
<el-option label="男" value="1"></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="10"> |
|||
<el-form-item label="手机号码:"> |
|||
<el-input v-model="baseInfo.phone"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="10"> |
|||
<el-form-item label="出生日期:"> |
|||
<el-date-picker v-model="baseInfo.birthDate" placeholder="选择日期" type="date"></el-date-picker> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="10"> |
|||
<el-form-item label="账号状态:"> |
|||
<el-select v-model="baseInfo.userState" placeholder="请选择"> |
|||
<el-option label="正常" value="0"></el-option> |
|||
<el-option label="冻结" value="1"></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="10"> |
|||
<el-form-item label="权限分配:" prop="jurisdiction"> |
|||
<el-select v-model="baseInfo.jurisdiction" multiple placeholder="请选择"> |
|||
<el-option |
|||
v-for="item in roleList" |
|||
:key="item.roleId" |
|||
:label="item.marks" |
|||
:value="item.roleId" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="20"> |
|||
<el-form-item label="家庭住址:"> |
|||
<el-input v-model="baseInfo.address"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="20"> |
|||
<el-form-item label="个人说明:"> |
|||
<el-input |
|||
v-model="baseInfo.marks" |
|||
:autosize="{ minRows: 4, maxRows: 6 }" |
|||
type="textarea" |
|||
></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="20"> |
|||
<div class="baseInfo_footer"> |
|||
<el-form-item> |
|||
<el-button size="medium" @click="close">取消</el-button> |
|||
<el-button size="medium" type="primary" @click="saveBaseInfo">确定</el-button> |
|||
</el-form-item> |
|||
</div> |
|||
</el-col> |
|||
</el-row> |
|||
</el-form> |
|||
</el-drawer> |
|||
</template> |
|||
|
|||
<script> |
|||
import { defineComponent, reactive, ref, toRefs } from "vue"; |
|||
import { useStore } from "vuex"; |
|||
import { ElMessage } from "element-plus"; |
|||
import { useRouter } from "vue-router"; |
|||
import Cookies from "js-cookie"; |
|||
export default defineComponent({ |
|||
name: "baseInfo", |
|||
props: { |
|||
baseVisible: Boolean, |
|||
}, |
|||
setup(props, context) { |
|||
const store = useStore(); //vuex |
|||
const router = useRouter(); //路由 |
|||
const baseInfoRef = ref(null); //基本信息ref |
|||
const state = reactive({ |
|||
baseInfo: { |
|||
username: "", |
|||
sex: "", |
|||
staffName: "", |
|||
phone: "", |
|||
marks: "", |
|||
birthDate: "", |
|||
address: "", |
|||
userState: "", |
|||
jurisdiction: "", |
|||
image: "", |
|||
}, |
|||
baseInfoRules: { |
|||
username: [ |
|||
{ required: true, message: "请输入用户名", trigger: "blur" }, |
|||
], |
|||
jurisdiction: [ |
|||
{ required: true, message: "请至少选择一个角色", trigger: "change" }, |
|||
], |
|||
}, |
|||
roleList: [ |
|||
{ |
|||
marks: "普通用户", |
|||
roleId: "1", |
|||
}, |
|||
{ |
|||
marks: "系统管理员", |
|||
roleId: "2", |
|||
}, |
|||
{ |
|||
marks: "超级管理员", |
|||
roleId: "3", |
|||
}, |
|||
], //角色列表 |
|||
}); |
|||
|
|||
const getInit = () => { |
|||
//基本信息初始化 |
|||
console.log("基本信息初始化"); |
|||
}; |
|||
const close = () => { |
|||
context.emit("update:baseVisible", false); |
|||
}; |
|||
|
|||
const openBaseInfo = (row) => { |
|||
context.emit("update:baseVisible", true); |
|||
state.baseInfo = Object.assign({ ...row }, {}); |
|||
}; |
|||
|
|||
const saveBaseInfo = () => { |
|||
//保存用户信息 |
|||
ElMessage.warning({ |
|||
message: "检测到您修改了本账号的信息,3秒后回到登陆页", |
|||
type: "warning", |
|||
}); |
|||
setTimeout(() => { |
|||
router.push({ path: "/login" }); |
|||
context.emit("reFresh", false); |
|||
Cookies.remove("token"); |
|||
}, 3000); |
|||
close(); |
|||
}; |
|||
|
|||
return { |
|||
...toRefs(state), |
|||
close, |
|||
saveBaseInfo, |
|||
openBaseInfo, |
|||
baseInfoRef, |
|||
getInit, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.baseInfo_footer { |
|||
text-align: right; |
|||
} |
|||
</style> |
@ -1,112 +0,0 @@ |
|||
<template> |
|||
<el-dialog :before-close="close" :model-value="passVisible" title="修改密码" width="500px"> |
|||
<el-form |
|||
ref="checkPassRef" |
|||
:hide-required-asterisk="true" |
|||
:model="ruleForm" |
|||
:rules="rules" |
|||
class="demo-ruleForm" |
|||
label-width="100px" |
|||
status-icon |
|||
> |
|||
<el-form-item label="原密码" prop="oldPassword"> |
|||
<el-input v-model="ruleForm.oldPassword" autocomplete="off" type="password"></el-input> |
|||
</el-form-item> |
|||
<el-form-item label="新密码" prop="newPassword"> |
|||
<el-input v-model="ruleForm.newPassword" autocomplete="off" type="password"></el-input> |
|||
</el-form-item> |
|||
<el-form-item label="确认密码" prop="checkPass"> |
|||
<el-input v-model="ruleForm.checkPass" autocomplete="off" type="password"></el-input> |
|||
</el-form-item> |
|||
</el-form> |
|||
<template #footer> |
|||
<span class="dialog-footer"> |
|||
<el-button size="medium" @click="close">取 消</el-button> |
|||
<el-button size="medium" type="primary" @click="ok">确 定</el-button> |
|||
</span> |
|||
</template> |
|||
</el-dialog> |
|||
</template> |
|||
<script> |
|||
import { defineComponent, toRefs, reactive, inject, ref, computed } from "vue"; |
|||
import { useStore } from "vuex"; |
|||
import { ElMessage } from "element-plus"; |
|||
import { useRouter } from "vue-router"; |
|||
import Cookies from "js-cookie"; |
|||
export default defineComponent({ |
|||
name: "checkPass", |
|||
props: { |
|||
passVisible: Boolean, |
|||
}, |
|||
setup(props, context) { |
|||
const checkPassRef = ref(null); //密码验证ref |
|||
const store = useStore(); //vuex |
|||
const router = useRouter(); //路由 |
|||
const validatePass = (rule, value, callback) => { |
|||
//密码验证 |
|||
if (value === "") { |
|||
callback(new Error("请输入新密码")); |
|||
} else { |
|||
if (state.ruleForm.checkPass !== "") { |
|||
checkPassRef.value.validateField("checkPass"); |
|||
} |
|||
callback(); |
|||
} |
|||
}; |
|||
const validateCheckPass = (rule, value, callback) => { |
|||
//重复密码验证 |
|||
if (value === "") { |
|||
callback(new Error("请再次输入密码")); |
|||
} else if (value !== state.ruleForm.newPassword) { |
|||
callback(new Error("两次输入密码不一致!")); |
|||
} else { |
|||
callback(); |
|||
} |
|||
}; |
|||
|
|||
const state = reactive({ |
|||
ruleForm: { |
|||
oldPassword: "", |
|||
newPassword: "", |
|||
checkPass: "", |
|||
}, |
|||
rules: { |
|||
oldPassword: [ |
|||
{ required: true, message: "请输入原密码", trigger: "blur" }, |
|||
], |
|||
newPassword: [{ validator: validatePass, trigger: "blur" }], |
|||
checkPass: [{ validator: validateCheckPass, trigger: "blur" }], |
|||
}, |
|||
userInfo: computed(() => store.state.user.user), //标签仓库 |
|||
}); |
|||
|
|||
const close = () => { |
|||
checkPassRef.value.resetFields(); |
|||
context.emit("update:passVisible", false); |
|||
}; |
|||
|
|||
const ok = () => { |
|||
checkPassRef.value.validate((valid) => { |
|||
if (valid) { |
|||
ElMessage.warning({ |
|||
message: "检测到您修改了密码,请重新登陆", |
|||
type: "warning", |
|||
}); |
|||
close(); //关闭弹框 |
|||
Cookies.remove("token"); //清空token 1秒后回到登录页 |
|||
setTimeout(() => { |
|||
router.push({ path: "/login" }); |
|||
}, 1000); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
return { |
|||
...toRefs(state), |
|||
checkPassRef, |
|||
close, |
|||
ok, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
@ -1,178 +0,0 @@ |
|||
<template> |
|||
<div class="tag_content"> |
|||
<div v-if="tagsList.length > 0" class="tags"> |
|||
<el-tag |
|||
:key="tag" |
|||
type |
|||
:class="path === tag.path ? 'tag_check' : 'tag_nocheck'" |
|||
v-for="(tag, index) in tagsList" |
|||
:closable="tag.path == '/homePage' ? false : true" |
|||
size="medium" |
|||
effect="plain" |
|||
@close="aClosingTag(tag, index)" |
|||
@click="triggerTag(tag, 'go')" |
|||
:disable-transitions="false" |
|||
>{{ transitionLocal('message.' + tag.title) }}</el-tag> |
|||
</div> |
|||
<el-dropdown v-if="tagsList.length > 0" @command="rightMenu"> |
|||
<span class="el-dropdown-link"> |
|||
<i class="el-icon-arrow-down el-icon--right"></i> |
|||
</span> |
|||
<template #dropdown> |
|||
<el-dropdown-menu> |
|||
<el-dropdown-item command="all">{{ transitionLocal('message.public.closeAll') }}</el-dropdown-item> |
|||
<el-dropdown-item command="other">{{ transitionLocal('message.public.closeOther') }}</el-dropdown-item> |
|||
</el-dropdown-menu> |
|||
</template> |
|||
</el-dropdown> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
import { |
|||
defineComponent, |
|||
getCurrentInstance, |
|||
toRefs, |
|||
reactive, |
|||
ref, |
|||
watch, |
|||
computed, |
|||
onMounted, |
|||
} from "vue"; |
|||
import { useRoute, useRouter, onBeforeRouteUpdate } from "vue-router"; |
|||
import { useStore } from "vuex"; |
|||
import { ElMessage } from "element-plus"; |
|||
import { transitionLocal } from '../../locales/i18n' |
|||
export default defineComponent({ |
|||
name: "tags", |
|||
setup() { |
|||
const store = useStore(); //vuex仓库 |
|||
const route = useRoute(); //路由 |
|||
const router = useRouter(); //路由 |
|||
let { proxy } = getCurrentInstance(); // vue原型 |
|||
const state = reactive({ |
|||
tagsList: computed(() => store.state.tagsList), //标签仓库 |
|||
path: "", //选中标签 |
|||
}); |
|||
|
|||
const setTags = (route) => { |
|||
// 设置标签 |
|||
const isExist = state.tagsList.some((item) => { |
|||
return item.path === route.fullPath; |
|||
}); |
|||
if (!isExist) { |
|||
if (state.tagsList.length >= 10) { |
|||
//如果标签到10个再选择就将最初的删除 |
|||
store.commit("delTags", { index: 0 }); |
|||
} |
|||
store.commit("setTags", { |
|||
name: route.name, |
|||
title: route.meta.title, |
|||
path: route.fullPath, |
|||
}); |
|||
} |
|||
}; |
|||
const aClosingTag = (tag, index) => { |
|||
//删除标签 |
|||
if (state.tagsList.length <= 1) { |
|||
//最后一个标签不能删 |
|||
ElMessage.warning({ |
|||
message: "最后一个标签了哦!", |
|||
type: "warning", |
|||
}); |
|||
return false; |
|||
} |
|||
store.commit("delTags", { index: index }); |
|||
triggerTag(state.tagsList[state.tagsList.length - 1], "go"); |
|||
}; |
|||
const triggerTag = (tag, type) => { |
|||
//选择标签 |
|||
// debugger |
|||
proxy._public.debounce(() => { |
|||
state.path = tag.path; |
|||
if (type) { |
|||
//如果是点击标签则进行路由跳转 |
|||
router.push(tag.path); |
|||
} |
|||
}, 100); |
|||
}; |
|||
const rightMenu = (menu) => { |
|||
//右菜单操作 |
|||
let whiteTags = ["/homePage"]; |
|||
if (state.path !== whiteTags[0] && menu === "other") { |
|||
whiteTags.push(state.path); |
|||
} |
|||
store.commit("delRightMenu", { |
|||
whiteTags, |
|||
}); |
|||
router.push(whiteTags[whiteTags.length - 1]); |
|||
}; |
|||
|
|||
onBeforeRouteUpdate((to) => { |
|||
//监听路由变动 |
|||
setTags(to); |
|||
triggerTag(to); |
|||
}); |
|||
onMounted(() => { |
|||
//监听路由变动 |
|||
setTags(route); |
|||
triggerTag(route); |
|||
}); |
|||
|
|||
return { |
|||
...toRefs(state), |
|||
setTags, |
|||
aClosingTag, |
|||
triggerTag, |
|||
rightMenu, |
|||
transitionLocal |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.tag_content { |
|||
padding: 6px 0px; |
|||
margin: 0px 12px; |
|||
box-sizing: border-box; |
|||
white-space: nowrap; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
.tags { |
|||
width: calc(100vw - 310px); |
|||
overflow: auto; |
|||
} |
|||
.el-tag { |
|||
cursor: pointer; |
|||
margin-right: 8px; |
|||
//height: 30px; |
|||
//padding: 0px 13px 0 9px; |
|||
//line-height: 28px; |
|||
//border-radius: 0; |
|||
} |
|||
::v-deep(.el-tag .el-icon-close) { |
|||
color: rgb(97, 97, 97) !important; |
|||
} |
|||
::v-deep(.el-tag .el-icon-close:hover) { |
|||
background: none; |
|||
} |
|||
.tag_check { |
|||
background-color: rgb(255, 255, 255) !important; |
|||
border-color: rgb(255, 255, 255) !important; |
|||
color: #409eff !important; |
|||
} |
|||
.tag_nocheck { |
|||
background-color: rgb(255, 255, 255) !important; |
|||
border-color: rgb(255, 255, 255) !important; |
|||
color: rgb(97, 97, 97) !important; |
|||
} |
|||
.el-dropdown-link { |
|||
text-align: center; |
|||
height: 26px; |
|||
display: block; |
|||
width: 70px; |
|||
background: #fff; |
|||
line-height: 26px; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,21 @@ |
|||
<template> |
|||
<div class="thereIsNo"> |
|||
<el-result :image-size="400"> |
|||
<template #icon> |
|||
<img class="abnormal-img" :src="getImage('403','svg')" /> |
|||
</template> |
|||
<template #extra> |
|||
<h2 class="abnormal-tip">很抱歉,您暂无权限访问</h2> |
|||
<el-button plain @click="router.go(-1)" |
|||
>返回</el-button |
|||
> |
|||
</template> |
|||
</el-result> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts" name="Admin403"> |
|||
import { getImage } from '@/utils'; |
|||
import { useRouter } from "vue-router"; |
|||
const router = useRouter(); //路由 |
|||
</script> |
@ -0,0 +1,21 @@ |
|||
<template> |
|||
<div class="thereIsNo"> |
|||
<el-result :image-size="400"> |
|||
<template #icon> |
|||
<img class="abnormal-img" :src="getImage('404','svg')" /> |
|||
</template> |
|||
<template #extra> |
|||
<h2 class="abnormal-tip">很抱歉,您所访问的页面不存在</h2> |
|||
<el-button plain @click="router.go(-1)" |
|||
>返回</el-button |
|||
> |
|||
</template> |
|||
</el-result> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts" name="Admin404"> |
|||
import { getImage } from '@/utils'; |
|||
import { useRouter } from "vue-router"; |
|||
const router = useRouter(); //路由 |
|||
</script> |
@ -0,0 +1,309 @@ |
|||
<template> |
|||
<div class="analysis"> |
|||
<el-row :gutter="10"> |
|||
<el-col v-for="(item, index) in dataList" :key="index" :xs="24" :sm="24" :md="6" :lg="6" :xl="6"> |
|||
<el-card class=" m-b8" shadow="always" :body-style="{ padding: '35px 20px' }"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
<span class="card-header-title">{{ item.title }}</span> |
|||
<el-tag :type="item.type" effect="dark">{{ |
|||
item.labelTitle |
|||
}}</el-tag> |
|||
</div> |
|||
</template> |
|||
<div class="text item card-h-total">{{ item.total }}</div> |
|||
</el-card> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row :gutter="10"> |
|||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24"> |
|||
<el-card class=" m-b8" shadow="always" :body-style="{ padding: '0' }"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
<span class="card-header-title">各时间段流量监控</span> |
|||
</div> |
|||
</template> |
|||
<div class="text item"> |
|||
<div id="visitChart" class="home_charts"></div> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row :gutter="10"> |
|||
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8"> |
|||
<el-card class="" shadow="always"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
<span class="card-header-title">数据来源</span> |
|||
</div> |
|||
</template> |
|||
<div class="text item"> |
|||
<div id="originChart" class="home_charts"></div> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8"> |
|||
<el-card class="" shadow="always"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
<span class="card-header-title">周活跃量统计</span> |
|||
</div> |
|||
</template> |
|||
<div class="text item"> |
|||
<div id="activeChart" class="home_charts"></div> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8"> |
|||
<el-card class="" shadow="always"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
<span class="card-header-title">用户数据统计</span> |
|||
</div> |
|||
</template> |
|||
<div class="text item"> |
|||
<div id="genderChart" class="home_charts"></div> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
</el-row> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts" name="AdminAnalysis"> |
|||
import type { TagProps } from "element-plus"; |
|||
import { inject, onMounted, onUnmounted, ref } from "vue"; |
|||
type DataList = { |
|||
type: TagProps["type"]; |
|||
title: string; |
|||
labelTitle: string; |
|||
total: number | string; |
|||
}; |
|||
const eCharts: any = inject("eCharts"); |
|||
|
|||
const dataList: DataList[] = [ |
|||
{ |
|||
title: "待办任务", |
|||
total: "300,000", |
|||
labelTitle: "总数", |
|||
type: "danger", |
|||
}, |
|||
{ |
|||
title: "用户数量", |
|||
total: "335,084", |
|||
labelTitle: "总数", |
|||
type: "", |
|||
}, |
|||
{ |
|||
title: "访问量", |
|||
total: "88,846,565", |
|||
labelTitle: "总数", |
|||
type: "success", |
|||
}, |
|||
{ |
|||
title: "商品数量", |
|||
total: "120", |
|||
labelTitle: "总数", |
|||
type: "warning", |
|||
}, |
|||
]; |
|||
|
|||
let visitChart = ref<any>(null); |
|||
let originChart = ref<any>(null); |
|||
let activeChart = ref<any>(null); |
|||
|
|||
onMounted(() => { |
|||
// window.onresize = function () { |
|||
// //页面尺寸变化 自适应大小 |
|||
// chartsInit(); |
|||
// }; |
|||
chartsInit(); |
|||
}) |
|||
|
|||
onUnmounted(() => { |
|||
}); |
|||
|
|||
const chartsInit = () => { |
|||
//图标初始化 |
|||
loadVisitChart(); |
|||
loadOriginChart(); |
|||
loadActiveChart(); |
|||
loadGenderChart(); |
|||
} |
|||
const loadVisitChart = () => { |
|||
//加载访问图表 |
|||
const labels = []; |
|||
const values = []; |
|||
for (let index = 1; index <= 24; index++) { |
|||
labels.push(`${index}:00`); |
|||
if (index % 2 === 0) { |
|||
values.push(index * 104); |
|||
} else { |
|||
values.push(index * 26); |
|||
} |
|||
}; |
|||
|
|||
if (visitChart.value) { |
|||
visitChart.value.dispose() |
|||
} |
|||
visitChart.value = eCharts.init(document.getElementById("visitChart")); |
|||
const option = { |
|||
xAxis: { |
|||
type: "category", |
|||
data: labels, |
|||
}, |
|||
yAxis: { |
|||
type: "value", |
|||
}, |
|||
series: [ |
|||
{ |
|||
data: values, |
|||
type: "line", |
|||
}, |
|||
], |
|||
}; |
|||
visitChart.value.setOption(option); |
|||
} |
|||
|
|||
const loadOriginChart = () => { |
|||
//加载访问图表 |
|||
if (originChart.value) { |
|||
originChart.value.dispose() |
|||
} |
|||
originChart.value = eCharts.init(document.getElementById("originChart")); |
|||
const option = { |
|||
tooltip: { |
|||
trigger: "item", |
|||
}, |
|||
legend: { |
|||
top: "5%", |
|||
left: "center", |
|||
}, |
|||
series: [ |
|||
{ |
|||
name: "访问来源", |
|||
type: "pie", |
|||
radius: ["40%", "70%"], |
|||
avoidLabelOverlap: false, |
|||
itemStyle: { |
|||
borderRadius: 10, |
|||
borderColor: "#fff", |
|||
borderWidth: 2, |
|||
}, |
|||
label: { |
|||
show: false, |
|||
position: "center", |
|||
}, |
|||
emphasis: { |
|||
label: { |
|||
show: true, |
|||
fontSize: "40", |
|||
fontWeight: "bold", |
|||
}, |
|||
}, |
|||
labelLine: { |
|||
show: false, |
|||
}, |
|||
data: [ |
|||
{ value: 1048, name: "搜索引擎" }, |
|||
{ value: 735, name: "直接访问" }, |
|||
{ value: 580, name: "邮件营销" }, |
|||
{ value: 484, name: "联盟广告" }, |
|||
{ value: 300, name: "视频广告" }, |
|||
], |
|||
}, |
|||
], |
|||
}; |
|||
originChart.value.setOption(option); |
|||
} |
|||
|
|||
const loadActiveChart = () => { |
|||
//每周访问量 |
|||
if (activeChart.value) { |
|||
activeChart.value.dispose() |
|||
} |
|||
activeChart.value = eCharts.init(document.getElementById("activeChart")); |
|||
// 绘制图表 |
|||
const option = { |
|||
xAxis: { |
|||
type: "category", |
|||
data: ["周一", "周二", "周三", "周四", "周五", "周六", "周日"], |
|||
}, |
|||
yAxis: { |
|||
type: "value", |
|||
}, |
|||
series: [ |
|||
{ |
|||
data: [120, 200, 150, 80, 70, 110, 130], |
|||
type: "bar", |
|||
showBackground: true, |
|||
backgroundStyle: { |
|||
color: "rgba(180, 180, 180, 0.2)", |
|||
}, |
|||
}, |
|||
], |
|||
}; |
|||
|
|||
activeChart.value.setOption(option); |
|||
} |
|||
|
|||
const loadGenderChart = () => { |
|||
//用户性别统计 |
|||
const myChart = eCharts.init(document.getElementById("genderChart")); |
|||
// 绘制图表 |
|||
const option = { |
|||
xAxis: { |
|||
type: "category", |
|||
data: ["男生", "女生", "未知"], |
|||
}, |
|||
yAxis: { |
|||
type: "value", |
|||
}, |
|||
series: [ |
|||
{ |
|||
data: [ |
|||
{ |
|||
value: 12234, |
|||
itemStyle: { |
|||
color: "#409EFF", |
|||
}, |
|||
}, |
|||
{ |
|||
value: 64132, |
|||
itemStyle: { |
|||
color: "#7B76D6", |
|||
}, |
|||
}, |
|||
{ |
|||
value: 8755, |
|||
itemStyle: { |
|||
color: "#C0C4CC", |
|||
}, |
|||
}, |
|||
], |
|||
type: "bar", |
|||
}, |
|||
], |
|||
}; |
|||
myChart.setOption(option); |
|||
} |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.analysis { |
|||
.home_charts { |
|||
height: 380px; |
|||
} |
|||
|
|||
.card-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
|
|||
.card-header-title { |
|||
font-size: 15px; |
|||
} |
|||
} |
|||
|
|||
.card-h-total { |
|||
font-size: 28px; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,311 @@ |
|||
<template> |
|||
<div class="workbench"> |
|||
<el-card shadow="always" :body-style="{ padding: '10px 30px' }"> |
|||
<div class="workbench-nav"> |
|||
<el-image style="width: 300px; height: 200px" :src="getImage('workbench', 'svg')" fit="fill" /> |
|||
<div class="image-space"> |
|||
<strong class="workbench-nav-title">{{ hoursTip }},欢迎使用{{ siteName }},祝你开开心心每一天 !</strong> |
|||
<small class="workbench-nav-introduce">轻松创建、部署您的中后台系统、提升研发效率、降低业务成本。</small> |
|||
</div> |
|||
</div> |
|||
</el-card> |
|||
<el-row :gutter="10"> |
|||
<el-col :xs="16" :sm="18"> |
|||
<el-card class="box-card m-t16" shadow="always" :body-style="{ padding: '35px 20px' }"> <template |
|||
#header> |
|||
<div class="card-header"> |
|||
<i class="fa fa-th-large module-icon" aria-hidden="true"></i> 项目统计 |
|||
</div> |
|||
</template> |
|||
<ul class="workbench-statistics"> |
|||
<li> |
|||
<span class="workbench-statistics-label">项目总数</span> |
|||
<span class="workbench-statistics-value">5</span> |
|||
</li> |
|||
<li> |
|||
<el-badge type="info" :value="180"> |
|||
<span class="workbench-statistics-label">待处理</span> |
|||
</el-badge> |
|||
<span class="workbench-statistics-value">180</span> |
|||
</li> |
|||
<li> |
|||
<el-badge type="success" :value="5400"> |
|||
<span class="workbench-statistics-label">已处理</span> |
|||
</el-badge> |
|||
<span class="workbench-statistics-value">5400</span> |
|||
</li> |
|||
<li> |
|||
<el-badge type="danger" :value="118"> |
|||
<span class="workbench-statistics-label">用户反馈</span> |
|||
</el-badge> |
|||
<span class="workbench-statistics-value">118</span> |
|||
</li> |
|||
</ul> |
|||
</el-card> |
|||
<el-card class="box-card m-t16" shadow="always" :body-style="{ padding: '20px' }"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
<i class="fa fa-check-square-o module-icon" aria-hidden="true"></i> 待办事项 |
|||
</div> |
|||
</template> |
|||
<el-table :data="taskData" style="width: 100%" height="280"> |
|||
<el-table-column prop="taskName" label="任务名称" width="180" /> |
|||
<el-table-column prop="name" label="发起人" width="180" /> |
|||
<el-table-column prop="taskDetail" label="任务详情" /> |
|||
<el-table-column fixed="right" label="操作" width="120"> |
|||
<template #default> |
|||
<el-button type="text" size="small">查看详情</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
</el-card> |
|||
</el-col> |
|||
<el-col :xs="8" :sm="6"> |
|||
<el-card class="box-card m-t16" shadow="always" :body-style="{ padding: '10px 20px 35px 20px' }"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
<i class="fa fa-bars module-icon" aria-hidden="true"></i>快速导航 / 最近使用 |
|||
</div> |
|||
</template> |
|||
<ul class="workbench-navigation"> |
|||
<li v-for="item in QuickNavigation" :key="item.url" @click="$router.push(item.url)"> |
|||
<i :class="item.icon" aria-hidden="true" :style="{ color: item.color }"></i> |
|||
{{ item.name }} |
|||
</li> |
|||
</ul> |
|||
</el-card> |
|||
<el-card class="box-card m-t16" shadow="always" :body-style="{ padding: '10px 20px 35px 20px' }"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
<i class="fa fa-question-circle module-icon" aria-hidden="true"></i>使用帮助 |
|||
</div> |
|||
</template> |
|||
<div class="workbench-use-help"> |
|||
<ul> |
|||
<li v-for="item in useHelpData" :key="item.url"> |
|||
<el-link type="info" @click="toViewDocument(item.url)">{{ item.name }}</el-link> |
|||
</li> |
|||
</ul> |
|||
<div class="other-help"> |
|||
<span class="other-help-label">其他</span> |
|||
<el-button type="text">新手引导</el-button> |
|||
</div> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
|
|||
</el-row> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts" name="AdminWorkbench"> |
|||
import { |
|||
getCurrentInstance, |
|||
onMounted, |
|||
ref, |
|||
} from "vue"; |
|||
import { siteName } from "@/router/middleware"; |
|||
import { getImage } from "@/utils"; |
|||
import { |
|||
useRoute, |
|||
} from "vue-router"; |
|||
const { proxy } = <any>getCurrentInstance(); |
|||
let hoursTip = ref<string>(''); |
|||
const route = useRoute(); |
|||
const QuickNavigation = [ |
|||
{ |
|||
name: "数据分析", |
|||
url: "/dashboard/analysis", |
|||
icon: "fa fa-area-chart", |
|||
color: "#1890ff" |
|||
}, |
|||
{ |
|||
name: "用户管理", |
|||
url: "/setting/user", |
|||
icon: "fa fa-cog", |
|||
color: "#13c2c2" |
|||
}, |
|||
{ |
|||
name: "表单", |
|||
url: "/template/tableOperation", |
|||
icon: "fa fa-file-text", |
|||
color: "#722ed1" |
|||
}, |
|||
{ |
|||
name: "工具", |
|||
url: "/components/watermark", |
|||
icon: "fa fa-wrench", |
|||
color: "#1d39c4" |
|||
}, |
|||
{ |
|||
name: "反馈中心", |
|||
url: "/main/feedbackCenter", |
|||
icon: "fa fa-comment", |
|||
color: "#a0d911" |
|||
}, |
|||
{ |
|||
name: "新手引导", |
|||
url: "/main/noviceGuide", |
|||
icon: "fa fa-question-circle-o", |
|||
color: "#595959" |
|||
}, |
|||
]; |
|||
const taskData = [ |
|||
{ |
|||
taskName: '办理入职', |
|||
name: 'Tom', |
|||
taskDetail: '带新人办理入职', |
|||
}, |
|||
{ |
|||
taskName: '材料领取', |
|||
name: 'Tom', |
|||
taskDetail: '带新人领取入职材料', |
|||
}, |
|||
{ |
|||
taskName: '办理出入证', |
|||
name: 'Tom', |
|||
taskDetail: '带新人办理出入证', |
|||
}, |
|||
] |
|||
const useHelpData = [ |
|||
{ |
|||
name: "Element-Plus的官方文档", |
|||
url: "https://element-plus.org/zh-CN/guide/design.html" |
|||
}, |
|||
{ |
|||
name: "Vue3的中文文档", |
|||
url: "https://staging-cn.vuejs.org/?mode=light" |
|||
}, |
|||
{ |
|||
name: "VueRouter的中文文档", |
|||
url: "https://router.vuejs.org/zh/" |
|||
}, |
|||
{ |
|||
name: "Pinia的中文文档", |
|||
url: "https://pinia.web3doc.top/" |
|||
}, |
|||
] |
|||
onMounted(() => { |
|||
accessTimeRange() |
|||
}) |
|||
const accessTimeRange = () => { |
|||
//获取时间范围 |
|||
let hour = new Date().getHours(); |
|||
if (hour >= 0 && hour < 12) { |
|||
hoursTip.value = '早上好'; |
|||
} else if (hour >= 12 && hour < 18) { |
|||
hoursTip.value = '下午好'; |
|||
} else { |
|||
hoursTip.value = '晚上好'; |
|||
} |
|||
} |
|||
|
|||
const toViewDocument = (url: string) => { |
|||
//打开文档 |
|||
window.open(url); |
|||
} |
|||
|
|||
|
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.workbench { |
|||
.module-icon { |
|||
padding-right: 4px; |
|||
font-size: 16px; |
|||
} |
|||
|
|||
&-nav { |
|||
background-color: #ffffff; |
|||
border-radius: 4px; |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
.image-space { |
|||
padding-left: 50px; |
|||
} |
|||
|
|||
.workbench-nav-title { |
|||
font-size: 24px; |
|||
} |
|||
|
|||
.workbench-nav-introduce { |
|||
display: block; |
|||
color: #808695; |
|||
padding-top: 30px; |
|||
} |
|||
} |
|||
|
|||
&-statistics { |
|||
display: flex; |
|||
justify-content: space-around; |
|||
list-style: none; |
|||
text-align: center; |
|||
|
|||
&-label { |
|||
font-size: 15px; |
|||
display: block; |
|||
color: #595959; |
|||
padding: 4px 5px; |
|||
} |
|||
|
|||
&-value { |
|||
display: block; |
|||
padding-top: 10px; |
|||
font-size: 25px; |
|||
font-weight: bold; |
|||
color: #000000; |
|||
} |
|||
} |
|||
|
|||
&-navigation { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
justify-content: space-around; |
|||
list-style: none; |
|||
|
|||
li { |
|||
display: flex; |
|||
flex-direction: column; |
|||
cursor: pointer; |
|||
width: 94px; |
|||
text-align: center; |
|||
height: 50px; |
|||
border-radius: 3px; |
|||
line-height: 50px; |
|||
margin-top: 25px; |
|||
color: #606266; |
|||
font-size: 14px; |
|||
|
|||
i { |
|||
font-size: 22px; |
|||
} |
|||
} |
|||
|
|||
li:hover { |
|||
color: #409EFF; |
|||
} |
|||
} |
|||
|
|||
&-use-help { |
|||
ul { |
|||
list-style: none; |
|||
|
|||
li { |
|||
margin-top: 10px; |
|||
} |
|||
} |
|||
|
|||
.other-help { |
|||
border-top: 1px solid #e4e7ed; |
|||
padding-top: 15px; |
|||
margin-top: 20px; |
|||
|
|||
&-label { |
|||
display: block; |
|||
padding-bottom: 10px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,31 @@ |
|||
<template> |
|||
<div> |
|||
<el-card shadow="always" :body-style="{ padding: '30px' }"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
可拖拽弹框 |
|||
</div> |
|||
</template> |
|||
<el-button type="primary" @click="dialogVisible = true">点击打开可拖拽弹框</el-button> |
|||
<el-dialog v-model="dialogVisible" draggable title="可拖拽弹框" width="30%" center> |
|||
<span>可以尝试拖拽我</span> |
|||
<template #footer> |
|||
<span class="dialog-footer"> |
|||
<el-button @click="dialogVisible = false">取消</el-button> |
|||
<el-button type="primary" @click="dialogVisible = false">确认</el-button> |
|||
</span> |
|||
</template> |
|||
</el-dialog> |
|||
</el-card> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts" name="AdminDialogDrag"> |
|||
import { ref } from "vue"; |
|||
|
|||
const dialogVisible = ref<boolean>(false); |
|||
|
|||
// const handleClose = (done: () => void) => { |
|||
// done(); |
|||
// }; |
|||
</script> |
@ -0,0 +1,224 @@ |
|||
<template> |
|||
<el-drawer |
|||
:before-close="close" |
|||
:model-value="baseVisible" |
|||
title="基本信息" |
|||
size="800px" |
|||
@open="getInit" |
|||
> |
|||
<el-form |
|||
ref="baseInfoFormRef" |
|||
:model="state.baseInfoForm" |
|||
:rules="state.baseInfoRules" |
|||
label-width="114px" |
|||
> |
|||
<el-row> |
|||
<el-col :span="10"> |
|||
<el-form-item label="姓名:"> |
|||
<el-input v-model="state.baseInfoForm.staffName"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="10"> |
|||
<el-form-item label="用户名:" prop="username"> |
|||
<el-input v-model="state.baseInfoForm.username"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="10"> |
|||
<el-form-item label="性别:"> |
|||
<el-select |
|||
v-model="state.baseInfoForm.sex" |
|||
placeholder="请选择性别" |
|||
> |
|||
<el-option label="女" value="0"></el-option> |
|||
<el-option label="男" value="1"></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="10"> |
|||
<el-form-item label="手机号码:"> |
|||
<el-input v-model="state.baseInfoForm.phone"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="10"> |
|||
<el-form-item label="出生日期:"> |
|||
<el-date-picker |
|||
v-model="state.baseInfoForm.birthDate" |
|||
placeholder="选择日期" |
|||
type="date" |
|||
></el-date-picker> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="10"> |
|||
<el-form-item label="账号状态:"> |
|||
<el-select |
|||
v-model="state.baseInfoForm.userState" |
|||
placeholder="请选择" |
|||
> |
|||
<el-option label="正常" value="0"></el-option> |
|||
<el-option label="冻结" value="1"></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="10"> |
|||
<el-form-item label="权限分配:" prop="jurisdiction"> |
|||
<el-select |
|||
v-model="state.baseInfoForm.jurisdiction" |
|||
multiple |
|||
placeholder="请选择" |
|||
> |
|||
<el-option |
|||
v-for="item in roleList" |
|||
:key="item.roleId" |
|||
:label="item.marks" |
|||
:value="item.roleId" |
|||
> |
|||
</el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="20"> |
|||
<el-form-item label="家庭住址:"> |
|||
<el-input v-model="state.baseInfoForm.address"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="20"> |
|||
<el-form-item label="个人说明:"> |
|||
<el-input |
|||
v-model="state.baseInfoForm.marks" |
|||
:autosize="{ minRows: 4, maxRows: 6 }" |
|||
type="textarea" |
|||
></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row> |
|||
<el-col :span="20"> |
|||
<div class="baseInfo_footer"> |
|||
<el-form-item> |
|||
<el-button @click="close">取消</el-button> |
|||
<el-button type="primary" @click="saveBaseInfo">确定</el-button> |
|||
</el-form-item> |
|||
</div> |
|||
</el-col> |
|||
</el-row> |
|||
</el-form> |
|||
</el-drawer> |
|||
</template> |
|||
|
|||
<script setup lang="ts" name="AdminBaseInfo"> |
|||
import { reactive, ref } from "vue"; |
|||
import { ElMessage } from "element-plus"; |
|||
import { useRouter } from "vue-router"; |
|||
import type { FormInstance } from "element-plus"; |
|||
import { removeToken } from "@/utils/auth"; |
|||
import { useUserStore } from "@/pinia/modules/user"; |
|||
import { BaseInfoState } from "@/types/setting"; |
|||
import { DeepPartial } from "@/types/index"; |
|||
|
|||
withDefaults( |
|||
defineProps<{ |
|||
baseVisible: boolean; |
|||
}>(), |
|||
{ |
|||
baseVisible: false, //打开基本信息 |
|||
} |
|||
); |
|||
|
|||
const emits = defineEmits<{ |
|||
(e: "update:baseVisible", baseVisible: boolean): void; |
|||
(e: "reFresh", status: boolean): void; |
|||
}>(); |
|||
|
|||
const userStore = useUserStore(); |
|||
|
|||
const router = useRouter(); |
|||
|
|||
const baseInfoFormRef = ref<FormInstance>(); |
|||
|
|||
const roleList = [ |
|||
//角色列表 |
|||
{ |
|||
marks: "普通用户", |
|||
roleId: "1", |
|||
}, |
|||
{ |
|||
marks: "系统管理员", |
|||
roleId: "2", |
|||
}, |
|||
{ |
|||
marks: "超级管理员", |
|||
roleId: "3", |
|||
}, |
|||
]; |
|||
|
|||
const state = reactive<BaseInfoState>({ |
|||
baseInfoForm: { |
|||
username: "", |
|||
sex: "", |
|||
staffName: "", |
|||
phone: "", |
|||
marks: "", |
|||
birthDate: "", |
|||
address: "", |
|||
userState: "", |
|||
jurisdiction: "", |
|||
image: "", |
|||
}, |
|||
baseInfoRules: { |
|||
username: [{ required: true, message: "请输入用户名", trigger: "blur" }], |
|||
jurisdiction: [ |
|||
{ required: true, message: "请至少选择一个角色", trigger: "change" }, |
|||
], |
|||
}, |
|||
}); |
|||
|
|||
const getInit = () => { |
|||
//基本信息初始化 |
|||
state.baseInfoForm = Object.assign(state.baseInfoForm, { |
|||
...userStore.user, |
|||
}); |
|||
} |
|||
const close = () => { |
|||
emits("update:baseVisible", false); |
|||
} |
|||
|
|||
const openBaseInfo = <T extends Partial<BaseInfoState["baseInfoForm"]>>( |
|||
row: T |
|||
) => { |
|||
emits("update:baseVisible", true); |
|||
state.baseInfoForm = Object.assign(state.baseInfoForm, { ...row }); |
|||
} |
|||
|
|||
const saveBaseInfo = () => { |
|||
//保存用户信息 |
|||
ElMessage.warning({ |
|||
message: "检测到您修改了本账号的信息,3秒后回到登陆页", |
|||
type: "warning", |
|||
}); |
|||
setTimeout(() => { |
|||
router.push({ path: "/login" }); |
|||
emits("reFresh", false); |
|||
removeToken(); |
|||
}, 3000); |
|||
close(); |
|||
} |
|||
|
|||
defineExpose({ |
|||
openBaseInfo, |
|||
}); |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.baseInfo_footer { |
|||
text-align: right; |
|||
} |
|||
</style> |
@ -0,0 +1,139 @@ |
|||
<template> |
|||
<el-dialog |
|||
:before-close="close" |
|||
:model-value="passVisible" |
|||
title="修改密码" |
|||
width="500px" |
|||
> |
|||
<el-form |
|||
ref="checkPassFormRef" |
|||
:hide-required-asterisk="true" |
|||
:model="checkPassForm" |
|||
:rules="rules" |
|||
class="demo-checkPassForm" |
|||
label-width="100px" |
|||
status-icon |
|||
> |
|||
<el-form-item label="原密码" prop="oldPassword"> |
|||
<el-input |
|||
v-model="checkPassForm.oldPassword" |
|||
autocomplete="off" |
|||
type="password" |
|||
></el-input> |
|||
</el-form-item> |
|||
<el-form-item label="新密码" prop="newPassword"> |
|||
<el-input |
|||
v-model="checkPassForm.newPassword" |
|||
autocomplete="off" |
|||
type="password" |
|||
></el-input> |
|||
</el-form-item> |
|||
<el-form-item label="确认密码" prop="checkPass"> |
|||
<el-input |
|||
v-model="checkPassForm.checkPass" |
|||
autocomplete="off" |
|||
type="password" |
|||
></el-input> |
|||
</el-form-item> |
|||
</el-form> |
|||
<template #footer> |
|||
<span class="dialog-footer"> |
|||
<el-button @click="close">取 消</el-button> |
|||
<el-button type="primary" @click="submitForm(checkPassFormRef)" |
|||
>确 定</el-button |
|||
> |
|||
</span> |
|||
</template> |
|||
</el-dialog> |
|||
</template> |
|||
<script setup lang="ts" name="AdminCheckPass"> |
|||
import { reactive, ref, toRefs } from "vue"; |
|||
import { ElMessage } from "element-plus"; |
|||
import { useRouter } from "vue-router"; |
|||
import { removeToken } from "@/utils/auth"; |
|||
import type { FormInstance } from "element-plus"; |
|||
import { CheckPassState } from "@/types/setting"; |
|||
withDefaults( |
|||
defineProps<{ |
|||
passVisible: boolean; |
|||
}>(), |
|||
{ |
|||
passVisible: false, //打开修改密码 |
|||
} |
|||
); |
|||
|
|||
const emits = defineEmits<{ |
|||
(e: "update:passVisible", passVisible: boolean): void; |
|||
}>(); |
|||
|
|||
const checkPassFormRef = ref<FormInstance>(); |
|||
const router = useRouter(); |
|||
const validatePass = (rule: any, value: any, callback: Function) => { |
|||
//密码验证 |
|||
if (value === "") { |
|||
callback(new Error("请输入新密码")); |
|||
} else { |
|||
if (state.checkPassForm.checkPass !== "") { |
|||
if (!checkPassFormRef.value) { |
|||
return; |
|||
} |
|||
checkPassFormRef.value.validateField("checkPass", () => null); |
|||
} |
|||
callback(); |
|||
} |
|||
}; |
|||
const validateCheckPass = (rule: any, value: any, callback: Function) => { |
|||
//重复密码验证 |
|||
if (value === "") { |
|||
callback(new Error("请再次输入密码")); |
|||
} else if (value !== state.checkPassForm.newPassword) { |
|||
callback(new Error("两次输入密码不一致!")); |
|||
} else { |
|||
callback(); |
|||
} |
|||
}; |
|||
|
|||
const state = reactive<Required<CheckPassState>>({ |
|||
checkPassForm: { |
|||
oldPassword: "", |
|||
newPassword: "", |
|||
checkPass: "", |
|||
}, |
|||
rules: { |
|||
oldPassword: [{ required: true, message: "请输入原密码", trigger: "blur" }], |
|||
newPassword: [{ validator: validatePass, trigger: "blur" }], |
|||
checkPass: [{ validator: validateCheckPass, trigger: "blur" }], |
|||
}, |
|||
}); |
|||
|
|||
const { checkPassForm, rules } = { ...toRefs(state) }; |
|||
|
|||
const close = () => { |
|||
//关闭 |
|||
if (!checkPassFormRef.value) { |
|||
return; |
|||
} |
|||
checkPassFormRef.value.resetFields(); |
|||
emits("update:passVisible", false); |
|||
} |
|||
|
|||
const submitForm = async (formEl: FormInstance | undefined) => { |
|||
//提交 |
|||
if (!formEl) { |
|||
return; |
|||
} |
|||
await formEl.validate((valid, fields) => { |
|||
if (valid) { |
|||
ElMessage.warning({ |
|||
message: "检测到您修改了密码,请重新登陆", |
|||
type: "warning", |
|||
}); |
|||
close(); //关闭弹框 |
|||
removeToken(); //清空token 1秒后回到登录页 |
|||
setTimeout(() => { |
|||
router.push({ path: "/login" }); |
|||
}, 1000); |
|||
} |
|||
}); |
|||
} |
|||
</script> |
@ -0,0 +1,16 @@ |
|||
<template> |
|||
<el-card shadow="always" :body-style="{ padding: '30px' }" > |
|||
<template #header> |
|||
<div class="card-header"> |
|||
Markdown编辑器,基于vue3,使用jsx和typescript语法开发,支持切换主题、prettier美化文本等。 |
|||
</div> |
|||
</template> |
|||
<md-editor v-model="text" class="m-t8" /> |
|||
</el-card> |
|||
</template> |
|||
<script lang="ts" setup name="AdminMarkdown"> |
|||
import { ref } from "vue"; |
|||
import MdEditor from "md-editor-v3"; |
|||
import "md-editor-v3/lib/style.css"; |
|||
const text = ref<string>("## Admin-Frame"); |
|||
</script> |
@ -0,0 +1,77 @@ |
|||
<template> |
|||
|
|||
<el-card shadow="always" :body-style="{ padding: '30px' }"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
QuillEditor —— 轻量级 web 富文本编辑器,配置方便,使用简单。 |
|||
</div> |
|||
</template> |
|||
<QuillEditor class="m-t8 min-h350" :read-only="readOnly" placeholder="欢迎来到Admin-Frame-Vue3" theme="snow" |
|||
:toolbar="toolbarOptions" content-type="html" :content="content" @text-change="textChange" @ready="ready" |
|||
@update:content="update" /> |
|||
<el-card class="m-t8"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
<span style="font-size: 18px">Html代码</span> |
|||
</div> |
|||
</template> |
|||
<div v-text="content ? content : '输入内容后自动同步'"></div> |
|||
</el-card> |
|||
<el-card class="m-t8"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
<span style="font-size: 18px">Text文本</span> |
|||
</div> |
|||
</template> |
|||
<div v-html="content ? content : '输入内容后自动同步'"></div> |
|||
</el-card> |
|||
</el-card> |
|||
</template> |
|||
|
|||
<script setup lang="ts" name="AdminTextEditor"> |
|||
import { reactive, ref } from "vue"; |
|||
import { QuillEditor } from "@vueup/vue-quill"; |
|||
import "@vueup/vue-quill/dist/vue-quill.snow.css"; |
|||
|
|||
const readOnly = ref<boolean>(false); //只读 |
|||
const content = ref<string>( |
|||
"<h1><strong>欢迎来到Admin-Frame-Vue3</strong></h1>" |
|||
); //内容 |
|||
const toolbarOptions = reactive([ |
|||
["bold", "italic", "underline", "strike"], // toggled buttons |
|||
["blockquote", "code-block"], |
|||
|
|||
[{ header: 1 }, { header: 2 }], // custom button values |
|||
[{ list: "ordered" }, { list: "bullet" }], |
|||
[{ script: "sub" }, { script: "super" }], // superscript/subscript |
|||
[{ indent: "-1" }, { indent: "+1" }], // outdent/indent |
|||
[{ direction: "rtl" }], // text direction |
|||
|
|||
[{ size: ["small", false, "large", "huge"] }], // custom dropdown |
|||
[{ header: [1, 2, 3, 4, 5, 6, false] }], |
|||
|
|||
[{ color: [] }, { background: [] }], // dropdown with defaults from theme |
|||
[{ font: [] }], |
|||
[{ align: [] }], |
|||
|
|||
["clean"], // remove formatting button |
|||
]); |
|||
|
|||
const textChange = (text: string) => { |
|||
//文本改变时触发 |
|||
console.log("文本改变时触发", text); |
|||
} |
|||
|
|||
const update = (text: string) => { |
|||
//当编辑器内容改变时触发 |
|||
console.log("当编辑器内容改变时触发", text); |
|||
content.value = text ? text : ""; |
|||
} |
|||
|
|||
const ready = () => { |
|||
//Quill初始化后触发 |
|||
console.log("Quill初始化后触发"); |
|||
} |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
</style> |
@ -0,0 +1,53 @@ |
|||
<template> |
|||
<div> |
|||
<el-space direction="vertical"> |
|||
<el-card class="box-card" style="width: 60vw"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
<span>{{ t("message.introduce.i18n") }}</span> |
|||
</div> |
|||
</template> |
|||
<div class="text item"> |
|||
<el-radio |
|||
v-for="locale in $i18n.availableLocales" |
|||
:key="`locale-${locale}`" |
|||
v-model="state.chooseI18n" |
|||
:label="locale" |
|||
@change="changLang" |
|||
>{{ $filters.inspectLanguage(locale) }}</el-radio |
|||
> |
|||
</div> |
|||
</el-card> |
|||
</el-space> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts" name="AdminI18n"> |
|||
import { watch, reactive, ref } from "vue"; |
|||
import { useI18n } from "vue-i18n"; |
|||
import Cookies from "js-cookie"; |
|||
import $filters from "@/filters/index"; |
|||
const { t, locale: language } = useI18n(); |
|||
|
|||
const state = reactive({ |
|||
chooseI18n: "", |
|||
}); |
|||
|
|||
watch( |
|||
() => language.value, |
|||
(val) => { |
|||
state.chooseI18n = val; |
|||
} |
|||
); |
|||
|
|||
const getDefaultLang = () => { |
|||
//获取本地默认语言 |
|||
state.chooseI18n = language.value; |
|||
} |
|||
|
|||
const changLang = (lang: any) => { |
|||
Cookies.set("lang", lang); //存储国际化 |
|||
language.value = lang; //更新i18n配置 |
|||
} |
|||
|
|||
getDefaultLang(); |
|||
</script> |
@ -0,0 +1,45 @@ |
|||
<template> |
|||
<el-card shadow="always" :body-style="{ padding: '30px' }"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
无限滚动列表 |
|||
</div> |
|||
</template> |
|||
<ul v-infinite-scroll="load" class="infinite-list" style="overflow: auto"> |
|||
<li v-for="i in count" :key="i" class="infinite-list-item"> |
|||
{{ `我是第${i}` }} |
|||
</li> |
|||
</ul> |
|||
</el-card> |
|||
</template> |
|||
|
|||
<script lang="ts" setup name="AdminInfiniteScroll"> |
|||
import { ref } from "vue"; |
|||
const count = ref(0); |
|||
const load = () => { |
|||
count.value += 1; |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.infinite-list { |
|||
height: calc(100vh - 300px); |
|||
padding: 0; |
|||
margin: 0; |
|||
list-style: none; |
|||
} |
|||
|
|||
.infinite-list .infinite-list-item { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
height: 50px; |
|||
background: var(--el-color-primary-light-9); |
|||
margin: 10px; |
|||
color: var(--el-color-primary); |
|||
} |
|||
|
|||
.infinite-list .infinite-list-item+.list-item { |
|||
margin-top: 10px; |
|||
} |
|||
</style> |
@ -0,0 +1,71 @@ |
|||
<template> |
|||
<div class="feedbackCenter"> |
|||
<el-card shadow="always" :body-style="{ padding: '30px 10px 15px 10px' }"> |
|||
<el-form :inline="true" :model="queryForm" label-position="right" label-width="84px"> |
|||
<el-form-item label="反馈人:"> |
|||
<el-input v-model.trim="queryForm.name" clearable placeholder="请输入反馈人姓名"> |
|||
</el-input> |
|||
</el-form-item> |
|||
<el-form-item label="人员ID:"> |
|||
<el-input v-model.trim="queryForm.id" clearable placeholder="请输入人员ID"> |
|||
</el-input> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary">查 询</el-button> |
|||
<el-button type="primary">新增</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</el-card> |
|||
<el-card shadow="always" :body-style="{ padding: '30px 10px 15px 10px' }" class="m-t16"> |
|||
<el-table :data="tableData" height="calc(100vh - 345px)" style="width: 100%"> |
|||
<el-table-column prop="id" label="ID"></el-table-column> |
|||
<el-table-column prop="name" label="创建人"></el-table-column> |
|||
<el-table-column prop="address" label="反馈问题"></el-table-column> |
|||
<el-table-column label="操作"> |
|||
<template #default="scope"> |
|||
<el-space spacer="|" style="color: #dedede"> |
|||
<el-button type="text">查看详情</el-button> |
|||
<el-button type="text">处理</el-button> |
|||
</el-space> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
<Pagination :pagination="pagination"></Pagination> |
|||
</el-card> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts" name="AdminFeedbackCenter"> |
|||
import Pagination from "@/components/Pagination/index.vue"; |
|||
let queryForm = ref({ |
|||
name: "", |
|||
id: "", |
|||
}); |
|||
|
|||
let pagination = ref({ |
|||
total: 100, |
|||
page: 1, |
|||
pageSize: 10, |
|||
}) |
|||
|
|||
let tableData = ref([ |
|||
{ |
|||
id: "8008208828", |
|||
name: "曹植饭", |
|||
address: "图片无法上传", |
|||
}, |
|||
{ |
|||
id: "8008208828", |
|||
name: "曹植饭", |
|||
address: "图片无法下载", |
|||
}, |
|||
{ |
|||
id: "8008208828", |
|||
name: "曹植饭", |
|||
address: "偶先黑屏问题", |
|||
} |
|||
]) |
|||
|
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.feedbackCenter {} |
|||
</style> |
@ -0,0 +1,34 @@ |
|||
<template> |
|||
<el-card shadow="always" :body-style="{ padding: '30px' }"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
新手引导 |
|||
</div> |
|||
</template> |
|||
<el-button type="primary" @click="toGuide">进入新手引导</el-button> |
|||
</el-card> |
|||
</template> |
|||
<script lang="ts" setup name="AdminNoviceGuide"> |
|||
import Driver from "driver.js"; |
|||
import "driver.js/dist/driver.min.css"; |
|||
import steps from "../../assets/js/guide"; |
|||
const driver = new Driver({ |
|||
className: "scoped-class", // className to wrap driver.js popover |
|||
animate: true, // Animate while changing highlighted element |
|||
opacity: 0.75, // Background opacity (0 means only popovers and without overlay) |
|||
padding: 6, // Distance of element from around the edges |
|||
allowClose: false, // Whether clicking on overlay should close or not |
|||
overlayClickNext: false, // Should it move to next step on overlay click |
|||
doneBtnText: "完成", // Text on the final button |
|||
closeBtnText: "关闭", // Text on the close button for this step |
|||
nextBtnText: "下一步", // Next button text for this step |
|||
prevBtnText: "上一步", // Previous button text for this step |
|||
// Called when moving to next step on any step |
|||
}); |
|||
|
|||
const toGuide = () => { |
|||
//开始新手引导 |
|||
driver.defineSteps(steps); |
|||
driver.start(); |
|||
} |
|||
</script> |
@ -0,0 +1,237 @@ |
|||
<template> |
|||
<div class="cardList"> |
|||
<el-card shadow="always" :body-style="{ padding: '30px 10px 15px 10px' }"> |
|||
<el-form :inline="true" :model="queryParams" class="demo-form-inline" label-position="right" label-width="84px"> |
|||
<el-form-item label="商品名称:"> |
|||
<el-input v-model.trim="queryParams.name" clearable placeholder="请输入商品名称" @keyup.enter="getList"></el-input> |
|||
</el-form-item> |
|||
<el-form-item label="商品ID:"> |
|||
<el-input v-model.trim="queryParams.id" clearable placeholder="请输入商品ID" @keyup.enter="getList"></el-input> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" @click="getList">查 询</el-button> |
|||
<el-button type="primary" @click="handlePopup('add', {})">新增</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</el-card> |
|||
<div class="m-t8"> |
|||
<el-row :gutter="10"> |
|||
<el-col v-for="(item, index) in list" :key="index" :xs="24" :sm="24" :md="12" :lg="12" :xl="6" |
|||
style="margin-top: 9px"> |
|||
<el-card :body-style="{ padding: '24px 0px 6px 0px' }"> |
|||
<div class="list-nav"> |
|||
<el-image style="width: 130px; height: 120px" :src="item.image" fit="fill"></el-image> |
|||
<div class="l-n-body"> |
|||
<div class="l-n-b-title">{{ item.name }}</div> |
|||
<span class="l-n-b-introduce">{{ item.introduce }}</span> |
|||
</div> |
|||
</div> |
|||
<div style="padding: 8px 4px 0px 4px"> |
|||
<div class="bottom"> |
|||
<time class="time">上架日期:{{ item.putWayDate }}</time> |
|||
<el-space :size="10" spacer="|"> |
|||
<el-button type="text" @click="handlePopup('edit', {})">编辑</el-button> |
|||
<el-button type="text" @click="removeDataById(item.id)">删除</el-button> |
|||
</el-space> |
|||
</div> |
|||
</div> |
|||
</el-card> |
|||
</el-col> |
|||
</el-row> |
|||
</div> |
|||
<el-dialog v-model="isDialog" :title="dialogTitle" :before-close=" |
|||
() => { |
|||
handleClose(tableFormRef); |
|||
} |
|||
" width="1000px" :close-on-click-modal="false" :close-on-press-escape="false" top="8vh"> |
|||
<el-form ref="tableFormRef" :model="form" :rules="rules" label-width="108px"> |
|||
<el-row :gutter="20"> |
|||
<el-col :span="12"> |
|||
<el-form-item label="商品名称:" prop="name"> |
|||
<el-input v-model="form.name" placeholder="请输入商品名称" clearable></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<el-row :gutter="20"> |
|||
<el-col :span="12"> |
|||
<el-form-item label="商品类型:" prop="height"> |
|||
<el-input v-model="form.height" placeholder="请输入商品类型" clearable></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row :gutter="20"> |
|||
<el-col :span="24"> |
|||
<el-form-item label="基本介绍:" prop="introduction"> |
|||
<el-input v-model="form.introduction" type="textarea" :autosize="{ minRows: 2, maxRows: 5 }" |
|||
placeholder="请输入基本介绍"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row :gutter="20"> |
|||
<el-col :span="24"> |
|||
<el-form-item label="备注:" prop="marks"> |
|||
<el-input v-model="form.marks" type="textarea" :autosize="{ minRows: 3, maxRows: 5 }"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
</el-form> |
|||
<template #footer> |
|||
<span class="dialog-footer"> |
|||
<el-button @click="handleClose(tableFormRef)">取 消</el-button> |
|||
<el-button type="primary" @click="onSubmit(tableFormRef)">确 定</el-button> |
|||
</span> |
|||
</template> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
<script lang="ts" setup name="AdminCardList"> |
|||
import { reactive, toRefs, onMounted, ref, getCurrentInstance } from "vue"; |
|||
import { ElMessageBox, ElMessage } from "element-plus"; |
|||
import type { FormInstance } from "element-plus"; |
|||
import { operationStatus } from "@/enums"; |
|||
import { TitleFormat } from "@/types/template"; |
|||
const { proxy } = <any>getCurrentInstance(); |
|||
const tableFormRef = ref<FormInstance>(); |
|||
let isDialog = ref<boolean>(false); |
|||
let handleType = ref<string>(operationStatus.add); //操作状态 |
|||
let dialogTitle = ref<string>("新增"); |
|||
interface ListItem { |
|||
name: string; |
|||
introduce: string; |
|||
image: string; |
|||
putWayDate: string; |
|||
id: number | string; |
|||
} |
|||
let list = ref<ListItem[]>([]); |
|||
|
|||
const state = reactive({ |
|||
queryParams: { |
|||
name: "", |
|||
id: "", |
|||
}, |
|||
form: { |
|||
name: "", |
|||
address: "", |
|||
englishName: "", |
|||
height: "", |
|||
weight: "", |
|||
introduction: "", |
|||
marks: "", |
|||
}, |
|||
rules: { |
|||
name: [{ required: true, message: "请输入商品名称", trigger: "blur" }], |
|||
}, |
|||
}); |
|||
|
|||
const { queryParams, form, rules } = { ...toRefs(state) }; |
|||
|
|||
onMounted(() => { |
|||
getList(); |
|||
}); |
|||
|
|||
const getList = () => { |
|||
//查询列表 |
|||
for (let index = 0; index <= 24; index++) { |
|||
list.value.push({ |
|||
name: "汉堡包", |
|||
introduce: |
|||
"小林蜜制小汉堡小林蜜制小汉堡小林蜜制小汉堡小林蜜制小汉堡小林蜜制小汉堡小林蜜制小汉堡小林蜜制小汉堡小林蜜制小汉堡小林蜜制小汉堡小林蜜制小汉堡小林蜜制小汉堡小林蜜制小汉堡小林蜜制小汉堡小林蜜制小汉堡小林蜜制小汉堡小林蜜制小汉堡", |
|||
image: |
|||
"https://shadow.elemecdn.com/app/element/hamburger.9cf7b091-55e9-11e9-a976-7f4d0b07eef6.png", |
|||
putWayDate: "2021/11/26", |
|||
id: index, |
|||
}); |
|||
} |
|||
}; |
|||
|
|||
const handlePopup = <T extends keyof TitleFormat>(type: T, target: any) => { |
|||
//打开新增 编辑 |
|||
handleType.value = type; |
|||
isDialog.value = true; |
|||
const TITLE_ITEM = { |
|||
add: "新增", |
|||
edit: "编辑", |
|||
}; |
|||
dialogTitle.value = proxy._public.getValues(TITLE_ITEM, type); |
|||
if (type == operationStatus.edit) { |
|||
state.form = { ...target }; |
|||
} |
|||
}; |
|||
|
|||
const onSubmit = async (formEl: FormInstance | undefined) => { |
|||
if (!formEl) { |
|||
return; |
|||
} |
|||
await formEl.validate((valid, fields) => { |
|||
if (valid) { |
|||
ElMessage({ |
|||
message: `${dialogTitle.value}成功.`, |
|||
type: "success", |
|||
}); |
|||
isDialog.value = false; |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
const removeDataById = (id: string | number) => { |
|||
//删除 |
|||
ElMessageBox.confirm("此操作将永久删除本商品记录, 是否继续?", "提示", { |
|||
confirmButtonText: "确定", |
|||
cancelButtonText: "取消", |
|||
type: "warning", |
|||
}) |
|||
.then(() => { }) |
|||
.catch(() => { }); |
|||
}; |
|||
|
|||
const handleClose = (formEl: FormInstance | undefined) => { |
|||
//关闭 |
|||
if (!formEl) { |
|||
return; |
|||
} |
|||
formEl.resetFields(); |
|||
isDialog.value = false; |
|||
}; |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.list-nav { |
|||
display: flex; |
|||
justify-content: space-around; |
|||
|
|||
.l-n-body { |
|||
width: 200px; |
|||
|
|||
.l-n-b-titie { |
|||
text-align: center; |
|||
font-size: 17px; |
|||
} |
|||
|
|||
.l-n-b-introduce { |
|||
font-size: 14px; |
|||
padding-top: 12px; |
|||
display: inline-block; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
cursor: pointer; |
|||
display: -webkit-box; |
|||
-webkit-line-clamp: 3; |
|||
-webkit-box-orient: vertical; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.time { |
|||
font-size: 13px; |
|||
color: #999; |
|||
} |
|||
|
|||
.bottom { |
|||
// margin-top: 13px; |
|||
padding: 0 12px; |
|||
line-height: 12px; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
} |
|||
</style> |
@ -0,0 +1,162 @@ |
|||
<template> |
|||
<el-card shadow="always" :body-style="{ padding: '30px' }"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
基础表单 |
|||
</div> |
|||
</template> |
|||
<el-form |
|||
ref="ruleFormRef" |
|||
:model="ruleForm" |
|||
:rules="rules" |
|||
label-width="120px" |
|||
class="demo-ruleForm" |
|||
size="default" |
|||
> |
|||
<el-form-item label="Activity name" prop="name"> |
|||
<el-input v-model="ruleForm.name" /> |
|||
</el-form-item> |
|||
<el-form-item label="Activity zone" prop="region"> |
|||
<el-select v-model="ruleForm.region" placeholder="Activity zone"> |
|||
<el-option label="Zone one" value="shanghai" /> |
|||
<el-option label="Zone two" value="beijing" /> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="Activity time" required> |
|||
<el-col :span="11"> |
|||
<el-form-item prop="date1"> |
|||
<el-date-picker |
|||
v-model="ruleForm.date1" |
|||
type="date" |
|||
placeholder="Pick a date" |
|||
style="width: 100%" |
|||
/> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col class="text-center" :span="2"> |
|||
<span class="text-gray-500">-</span> |
|||
</el-col> |
|||
<el-col :span="11"> |
|||
<el-form-item prop="date2"> |
|||
<el-time-picker |
|||
v-model="ruleForm.date2" |
|||
placeholder="Pick a time" |
|||
style="width: 100%" |
|||
/> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-form-item> |
|||
<el-form-item label="Instant delivery" prop="delivery"> |
|||
<el-switch v-model="ruleForm.delivery" /> |
|||
</el-form-item> |
|||
<el-form-item label="Activity type" prop="type"> |
|||
<el-checkbox-group v-model="ruleForm.type"> |
|||
<el-checkbox label="Online activities" name="type" /> |
|||
<el-checkbox label="Promotion activities" name="type" /> |
|||
<el-checkbox label="Offline activities" name="type" /> |
|||
<el-checkbox label="Simple brand exposure" name="type" /> |
|||
</el-checkbox-group> |
|||
</el-form-item> |
|||
<el-form-item label="Resources" prop="resource"> |
|||
<el-radio-group v-model="ruleForm.resource"> |
|||
<el-radio label="Sponsorship" /> |
|||
<el-radio label="Venue" /> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item label="Activity form" prop="desc"> |
|||
<el-input v-model="ruleForm.desc" type="textarea" /> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" @click="submitForm(ruleFormRef)" |
|||
>提交</el-button |
|||
> |
|||
<el-button @click="resetForm(ruleFormRef)">重置</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</el-card> |
|||
</template> |
|||
|
|||
<script lang="ts" setup name="AdminEasyForm"> |
|||
import { reactive, ref } from "vue"; |
|||
import type { FormInstance } from "element-plus"; |
|||
|
|||
const ruleFormRef = ref<FormInstance>(); |
|||
const ruleForm = reactive({ |
|||
name: "Hello", |
|||
region: "", |
|||
date1: "", |
|||
date2: "", |
|||
delivery: false, |
|||
type: [], |
|||
resource: "", |
|||
desc: "", |
|||
}); |
|||
|
|||
const rules = ref<any>({ |
|||
name: [ |
|||
{ required: true, message: "Please input Activity name", trigger: "blur" }, |
|||
{ min: 3, max: 5, message: "Length should be 3 to 5", trigger: "blur" }, |
|||
], |
|||
region: [ |
|||
{ |
|||
required: true, |
|||
message: "Please select Activity zone", |
|||
trigger: "change", |
|||
}, |
|||
], |
|||
date1: [ |
|||
{ |
|||
type: "date", |
|||
required: true, |
|||
message: "Please pick a date", |
|||
trigger: "change", |
|||
}, |
|||
], |
|||
date2: [ |
|||
{ |
|||
type: "date", |
|||
required: true, |
|||
message: "Please pick a time", |
|||
trigger: "change", |
|||
}, |
|||
], |
|||
type: [ |
|||
{ |
|||
type: "array", |
|||
required: true, |
|||
message: "Please select at least one activity type", |
|||
trigger: "change", |
|||
}, |
|||
], |
|||
resource: [ |
|||
{ |
|||
required: true, |
|||
message: "Please select activity resource", |
|||
trigger: "change", |
|||
}, |
|||
], |
|||
desc: [ |
|||
{ required: true, message: "Please input activity form", trigger: "blur" }, |
|||
], |
|||
}); |
|||
|
|||
const submitForm = async (formEl: FormInstance | undefined) => { |
|||
if (!formEl) { |
|||
return; |
|||
} |
|||
await formEl.validate((valid, fields) => { |
|||
if (valid) { |
|||
console.log("submit!"); |
|||
} else { |
|||
console.log("error submit!", fields); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
const resetForm = (formEl: FormInstance | undefined) => { |
|||
if (!formEl) { |
|||
return; |
|||
} |
|||
formEl.resetFields(); |
|||
} |
|||
</script> |
@ -0,0 +1,234 @@ |
|||
<template> |
|||
<div class="tableOperation"> |
|||
<el-card shadow="always" :body-style="{ padding: '30px 10px 15px 10px' }" > |
|||
<el-form :inline="true" :model="queryForm" label-position="right" |
|||
label-width="84px"> |
|||
<el-form-item label="姓名:"> |
|||
<el-input v-model.trim="queryForm.name" clearable placeholder="请输入姓名" @keyup.enter="getList"></el-input> |
|||
</el-form-item> |
|||
<el-form-item label="ID:"> |
|||
<el-input v-model.trim="queryForm.id" clearable placeholder="请输入ID" @keyup.enter="getList"></el-input> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" @click="getList">查 询</el-button> |
|||
<el-button type="primary" @click="handlePopup(operationStatus.add, null)">新增</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
</el-card> |
|||
|
|||
<el-card shadow="always" :body-style="{ padding: '30px 10px 15px 10px' }" class="m-t16"> |
|||
<el-table :data="tableData" height="calc(100vh - 345px)" style="width: 100%"> |
|||
<el-table-column prop="id" label="ID"></el-table-column> |
|||
<el-table-column prop="name" label="姓名"></el-table-column> |
|||
<el-table-column prop="address" label="地址"></el-table-column> |
|||
<el-table-column label="操作"> |
|||
<template #default="scope"> |
|||
<el-space spacer="|" style="color: #dedede"> |
|||
<el-button type="text" @click="handlePopup(operationStatus.edit, scope.row)">编辑</el-button> |
|||
<el-button type="text">{{ |
|||
scope.row.enabled === "0" ? useStatus.start : useStatus.close |
|||
}}</el-button> |
|||
<el-button type="text" @click="removeDataById(scope.row.id)">删除</el-button> |
|||
</el-space> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
<Pagination :pagination="pagination" @change="handleChangeCurrent"></Pagination> |
|||
</el-card> |
|||
|
|||
<el-dialog v-model="isDialog" :title="dialogTitle" :before-close=" |
|||
() => { |
|||
handleClose(operationFormRef); |
|||
} |
|||
" width="1000px" :close-on-click-modal="false" :close-on-press-escape="false" top="8vh"> |
|||
<el-form ref="operationFormRef" :model="newlyForm" :newly-rules="newlyRules" label-width="108px"> |
|||
<el-row :gutter="20"> |
|||
<el-col :span="12"> |
|||
<el-form-item label="姓名:" prop="name"> |
|||
<el-input v-model="newlyForm.name" placeholder="请输入姓名" clearable></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="英文名:" prop="englishName"> |
|||
<el-input v-model="newlyForm.englishName" placeholder="请输入英文名" clearable></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
|
|||
<el-row :gutter="20"> |
|||
<el-col :span="12"> |
|||
<el-form-item label="身高(cm):" prop="height"> |
|||
<el-input v-model="newlyForm.height" placeholder="请输入身高" clearable></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="体重(kg):" prop="weight"> |
|||
<el-input v-model="newlyForm.weight" placeholder="请输入体重" clearable></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row :gutter="20"> |
|||
<el-col :span="24"> |
|||
<el-form-item label="地址:" prop="address"> |
|||
<el-input v-model="newlyForm.address" placeholder="请输入地址" clearable></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row :gutter="20"> |
|||
<el-col :span="24"> |
|||
<el-form-item label="基本介绍:" prop="introduction"> |
|||
<el-input v-model="newlyForm.introduction" type="textarea" :autosize="{ minRows: 2, maxRows: 5 }" |
|||
placeholder="请输入基本介绍"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
<el-row :gutter="20"> |
|||
<el-col :span="24"> |
|||
<el-form-item label="备注:" prop="marks"> |
|||
<el-input v-model="newlyForm.marks" type="textarea" :autosize="{ minRows: 3, maxRows: 5 }"></el-input> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
</el-form> |
|||
<template #footer> |
|||
<span class="dialog-footer"> |
|||
<el-button @click="handleClose(operationFormRef)">取 消</el-button> |
|||
<el-button type="primary" @click="onSubmit(operationFormRef)">确 定</el-button> |
|||
</span> |
|||
</template> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts" name="AdminTableOperation"> |
|||
import { reactive, toRefs, onMounted, ref, getCurrentInstance } from "vue"; |
|||
import { ElMessageBox, ElMessage } from "element-plus"; |
|||
import type { FormInstance } from "element-plus"; |
|||
import Pagination from "@/components/Pagination/index.vue"; |
|||
import { useStatus, operationStatus } from "@/enums"; |
|||
import { TitleFormat } from "@/types/template"; |
|||
import { PaginationState } from "@/types"; |
|||
const { proxy } = <any>getCurrentInstance(); |
|||
const operationFormRef = ref<FormInstance>(); |
|||
let isDialog = ref<boolean>(false); //是否打开弹窗 |
|||
let handleType = ref<string>(operationStatus.add); //操作状态 |
|||
let dialogTitle = ref<string>("新增"); |
|||
const state = reactive({ |
|||
tableData: [ |
|||
{ |
|||
id: "8008208828", |
|||
name: "曹植饭", |
|||
address: "南京市雨花台区宁双路19号云密城J栋6楼", |
|||
enabled: "0", |
|||
}, |
|||
{ |
|||
id: "8008208828", |
|||
name: "曹植饭", |
|||
address: "南京市雨花台区宁双路19号云密城J栋6楼", |
|||
enabled: "1", |
|||
}, |
|||
{ |
|||
id: "8008208828", |
|||
name: "曹植饭", |
|||
address: "南京市雨花台区宁双路19号云密城J栋6楼", |
|||
}, |
|||
{ |
|||
id: "8008208828", |
|||
name: "曹植饭", |
|||
address: "南京市雨花台区宁双路19号云密城J栋6楼", |
|||
}, |
|||
{ |
|||
id: "8008208828", |
|||
name: "曹植饭", |
|||
address: "南京市雨花台区宁双路19号云密城J栋6楼", |
|||
}, |
|||
], |
|||
queryForm: { |
|||
name: "", |
|||
id: "", |
|||
}, |
|||
newlyForm: { |
|||
name: "", |
|||
address: "", |
|||
englishName: "", |
|||
height: "", |
|||
weight: "", |
|||
introduction: "", |
|||
marks: "", |
|||
}, |
|||
pagination: { |
|||
total: 100, |
|||
page: 1, |
|||
pageSize: 10, |
|||
}, |
|||
newlyRules: { |
|||
name: [{ required: true, message: "请输入姓名", trigger: "blur" }], |
|||
}, |
|||
}); |
|||
|
|||
const { queryForm, pagination, tableData, newlyForm, newlyRules } = { |
|||
...toRefs(state), |
|||
}; |
|||
|
|||
onMounted(() => { |
|||
getList(); |
|||
}); |
|||
|
|||
const handleChangeCurrent = (val: any) => { |
|||
//分页操作 |
|||
state.pagination = Object.assign(state.pagination, { ...val }); |
|||
}; |
|||
const getList = () => { |
|||
//查询列表 |
|||
console.log("我查询了列表"); |
|||
}; |
|||
|
|||
const handlePopup = <T extends keyof TitleFormat>(type: T, target: any) => { |
|||
//打开新增 编辑 |
|||
handleType.value = type; |
|||
isDialog.value = true; |
|||
const TITLE_ITEM = { |
|||
add: "新增", |
|||
edit: "编辑", |
|||
}; |
|||
dialogTitle.value = proxy._public.getValues(TITLE_ITEM, type); |
|||
if (type == operationStatus.edit) { |
|||
state.newlyForm = { ...target }; |
|||
} |
|||
}; |
|||
|
|||
const onSubmit = async (formEl: FormInstance | undefined) => { |
|||
if (!formEl) { |
|||
return; |
|||
} |
|||
await formEl.validate((valid, fields) => { |
|||
if (valid) { |
|||
ElMessage({ |
|||
message: `${dialogTitle.value}成功.`, |
|||
type: "success", |
|||
}); |
|||
isDialog.value = false; |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
const removeDataById = (id: string | number) => { |
|||
//删除 |
|||
ElMessageBox.confirm("此操作将永久删除本条记录, 是否继续?", "提示", { |
|||
confirmButtonText: "确定", |
|||
cancelButtonText: "取消", |
|||
type: "warning", |
|||
}) |
|||
.then(() => { }) |
|||
.catch(() => { }); |
|||
}; |
|||
|
|||
const handleClose = (formEl: FormInstance | undefined) => { |
|||
if (!formEl) { |
|||
return; |
|||
} |
|||
formEl.resetFields(); |
|||
isDialog.value = false; |
|||
}; |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
</style> |
@ -0,0 +1,25 @@ |
|||
<template> |
|||
<el-card shadow="always" :body-style="{ padding: '30px' }"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
错误提示 |
|||
</div> |
|||
</template> |
|||
<el-result icon="error" title="提交失败"> |
|||
<template #extra> |
|||
<div class="bg-default p-default min-w600"> |
|||
<div class="t-l">失败原因分析:</div> |
|||
<div class="t-l m-t8"> |
|||
账号余额不足, |
|||
<span style="color: #409eff; cursor: pointer">请充值</span>。 |
|||
</div> |
|||
</div> |
|||
<div class="m-t8"> |
|||
<el-button type="primary">返回</el-button> |
|||
<el-button type="primary">重新再试</el-button> |
|||
</div> |
|||
</template> |
|||
</el-result> |
|||
</el-card> |
|||
</template> |
|||
<script lang="ts" setup name="AdminError"></script> |
@ -0,0 +1,20 @@ |
|||
<template> |
|||
<el-card shadow="always" :body-style="{ padding: '30px' }"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
成功提示 |
|||
</div> |
|||
</template> |
|||
<el-result icon="success" title="提交成功"> |
|||
<template #extra> |
|||
<div class="bg-default p-default min-w600"> |
|||
<div>提交后会在3个工作日里,将结果发送到您的手机。</div> |
|||
</div> |
|||
<div class="m-t8"> |
|||
<el-button type="primary">返回</el-button> |
|||
</div> |
|||
</template> |
|||
</el-result> |
|||
</el-card> |
|||
</template> |
|||
<script lang="ts" setup name="AdminSuccess"></script> |
@ -0,0 +1,38 @@ |
|||
<template> |
|||
<el-card shadow="always" :body-style="{ padding: '30px' }"> |
|||
<template #header> |
|||
<div class="card-header"> |
|||
添加水印 |
|||
</div> |
|||
</template> |
|||
<el-switch |
|||
v-model="value" |
|||
style="display: block" |
|||
active-color="#13ce66" |
|||
active-text="开启水印" |
|||
inactive-text="关闭水印" |
|||
/> |
|||
</el-card> |
|||
</template> |
|||
<script setup lang="ts" name="AdminWatermark"> |
|||
import { onMounted, onUnmounted, ref, watch } from "vue"; |
|||
import { setWaterMarker, removeWaterMarker } from "@/common/watermark"; // 引入水印 |
|||
|
|||
const value = ref<boolean>(true); |
|||
|
|||
watch(value, (newV, oldV) => { |
|||
if (newV) { |
|||
setWaterMarker("Admin Frame 添加水印"); // 添加水印 |
|||
} else { |
|||
removeWaterMarker(); //卸载水印 |
|||
} |
|||
}); |
|||
|
|||
onMounted(() => { |
|||
setWaterMarker("Admin Frame 添加水印"); // 添加水印 |
|||
}) |
|||
|
|||
onUnmounted(() => { |
|||
removeWaterMarker(); //卸载水印 |
|||
}) |
|||
</script> |
@ -1,27 +0,0 @@ |
|||
import { ElMessage } from "element-plus"; |
|||
import Clipboard from "clipboard"; |
|||
const copy = (app, options) => { |
|||
app.directive('copy', { |
|||
mounted(el, binding) { |
|||
el.addEventListener("click", (() => { |
|||
var clipboard = new Clipboard(`.${binding?.value}`); |
|||
clipboard.on("success", (e) => { |
|||
console.log(`指令方法复制成功`); |
|||
ElMessage({ |
|||
message: `指令方法复制成功`, |
|||
type: "success", |
|||
}); |
|||
// 释放内存
|
|||
clipboard.destroy(); |
|||
}); |
|||
clipboard.on("error", (e) => { |
|||
// 不支持复制
|
|||
//console.log('该浏览器不支持自动复制')
|
|||
// 释放内存
|
|||
clipboard.destroy(); |
|||
}); |
|||
})); |
|||
} |
|||
}) |
|||
} |
|||
export default copy; |
@ -0,0 +1,27 @@ |
|||
import { ElMessage } from "element-plus"; |
|||
import Clipboard from "clipboard"; |
|||
const copy = (app: any, options: any) => { |
|||
app.directive("copy", { |
|||
mounted(el: any, binding: any) { |
|||
el.addEventListener("click", () => { |
|||
const clipboard = new Clipboard(`.${binding?.value}`); |
|||
clipboard.on("success", (e) => { |
|||
console.log(`指令方法复制成功`); |
|||
ElMessage({ |
|||
message: `指令方法复制成功`, |
|||
type: "success", |
|||
}); |
|||
// 释放内存
|
|||
clipboard.destroy(); |
|||
}) |
|||
clipboard.on("error", (e) => { |
|||
// 不支持复制
|
|||
//console.log('该浏览器不支持自动复制')
|
|||
// 释放内存
|
|||
clipboard.destroy(); |
|||
}) |
|||
}); |
|||
}, |
|||
}); |
|||
} |
|||
export default copy; |
@ -1,70 +0,0 @@ |
|||
const dialogDrag = (app, options) => { |
|||
app.directive('dialogdrag', { |
|||
// 渲染完毕
|
|||
mounted(el, binding) { |
|||
// binding.arg
|
|||
// binding.value
|
|||
// 可视窗口的宽度
|
|||
const clientWidth = document.documentElement.clientWidth |
|||
// 可视窗口的高度
|
|||
const clientHeight = document.documentElement.clientHeight |
|||
// 记录坐标
|
|||
let domset = { |
|||
x: clientWidth / 4, // 默认width 50%
|
|||
y: clientHeight * 15 / 100 // 根据 15vh 计算
|
|||
} |
|||
|
|||
// 弹窗的容器
|
|||
const domDrag = el.firstElementChild.firstElementChild |
|||
// 重新设置上、左距离
|
|||
domDrag.style.marginTop = domset.y + 'px' |
|||
domDrag.style.marginLeft = domset.x + 'px' |
|||
|
|||
// 记录拖拽开始的光标坐标,0 表示没有拖拽
|
|||
let start = { x: 0, y: 0 } |
|||
// 移动中记录偏移量
|
|||
let move = { x: 0, y: 0 } |
|||
|
|||
// 鼠标按下,开始拖拽
|
|||
domDrag.onmousedown = (e) => { |
|||
// 判断对话框是否重新打开
|
|||
if (domDrag.style.marginTop === '15vh') { |
|||
// 重新打开,设置 domset.y top
|
|||
domset.y = clientHeight * 15 / 100 |
|||
} |
|||
start.x = e.clientX |
|||
start.y = e.clientY |
|||
domDrag.style.cursor = 'move' // 改变光标形状
|
|||
} |
|||
|
|||
// 鼠标移动,实时跟踪
|
|||
domDrag.onmousemove = (e) => { |
|||
if (start.x === 0) { // 不是拖拽状态
|
|||
return |
|||
} |
|||
move.x = e.clientX - start.x |
|||
move.y = e.clientY - start.y |
|||
|
|||
// 初始位置 + 拖拽距离
|
|||
domDrag.style.marginLeft = (domset.x + move.x) + 'px' |
|||
domDrag.style.marginTop = (domset.y + move.y) + 'px' |
|||
} |
|||
// 鼠标抬起,结束拖拽
|
|||
domDrag.onmouseup = (e) => { |
|||
move.x = e.clientX - start.x |
|||
move.y = e.clientY - start.y |
|||
|
|||
// 记录新坐标,作为下次拖拽的初始位置
|
|||
domset.x += move.x |
|||
domset.y += move.y |
|||
domDrag.style.cursor = '' // 恢复光标形状
|
|||
domDrag.style.marginLeft = domset.x + 'px' |
|||
domDrag.style.marginTop = domset.y + 'px' |
|||
// 结束拖拽
|
|||
start.x = 0 |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
|
|||
export default dialogDrag |
@ -1,6 +0,0 @@ |
|||
import dialogDrag from './dialogDrag'; |
|||
import copy from './copy'; |
|||
export { |
|||
dialogDrag, |
|||
copy |
|||
} |
@ -0,0 +1,2 @@ |
|||
import copy from "./copy"; |
|||
export { copy }; |
@ -0,0 +1,14 @@ |
|||
export enum LoginStatus { //0 注册 1登录
|
|||
register, |
|||
login, |
|||
} |
|||
|
|||
export enum useStatus { //使用状态 0启用 1禁用
|
|||
start = "启用", |
|||
close = "禁用", |
|||
} |
|||
|
|||
export enum operationStatus { //操作状态 0新增 1修改
|
|||
add = "add", |
|||
edit = "edit", |
|||
} |
@ -0,0 +1,8 @@ |
|||
/// <reference types="vite/client" />
|
|||
|
|||
declare module "*.vue" { |
|||
import type { DefineComponent } from "vue"; |
|||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
|||
const component: DefineComponent<{}, {}, any>; |
|||
export default component; |
|||
} |
@ -1,20 +0,0 @@ |
|||
export default { |
|||
Gender(val) { |
|||
//性别过滤 0女 1男 没有则未知
|
|||
const dataWare = [ |
|||
'女', '男' |
|||
] |
|||
return dataWare[val] ?? '未知'; |
|||
}, |
|||
langFilter(val) { |
|||
//过滤国际化中文
|
|||
switch (val) { |
|||
case 'zh': |
|||
return '中文'; |
|||
case 'zh-cn': |
|||
return '中文'; |
|||
case 'en': |
|||
return 'English'; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,18 @@ |
|||
export default { |
|||
Gender(val: any) { |
|||
//性别过滤 0女 1男 没有则未知
|
|||
const dataWare: string[] = ["女", "男"]; |
|||
return dataWare[Number(val)] ?? "未知"; |
|||
}, |
|||
inspectLanguage<T extends string>(val: T) { |
|||
//过滤国际化中文
|
|||
switch (val) { |
|||
case "zh": |
|||
return "中文"; |
|||
case "zh-cn": |
|||
return "中文"; |
|||
case "en": |
|||
return "English"; |
|||
} |
|||
}, |
|||
}; |
@ -0,0 +1,288 @@ |
|||
<template> |
|||
<div class="admin-header"> |
|||
<div class="collapse-left"> |
|||
<div class="collapse-btn"> |
|||
<i title="点击打开关闭菜单" @click="switchCollapse" :class="collapse ? 'fa fabtn fa-indent' : 'fa fabtn fa-dedent'"></i> |
|||
<i title="刷新页面" @click="reload()" class="fa fa-refresh" aria-hidden="true"></i> |
|||
</div> |
|||
<div class="collapse-breadcrumb"> |
|||
<el-breadcrumb> |
|||
<el-breadcrumb-item v-for="(item, index) in route.matched" :key="item.path"> |
|||
<span :style="{ |
|||
'font-weight': route.matched.length === index + 1 ? 700 : 500 |
|||
}">{{ transitionLocal("message." + item.meta.title) }}</span> |
|||
</el-breadcrumb-item> |
|||
</el-breadcrumb> |
|||
</div> |
|||
</div> |
|||
<div class="collapse-right"> |
|||
<!-- <el-tooltip class="item" effect="dark" :content="t('message.public.fullScreen')" placement="bottom"> |
|||
<span class="faSpan"> |
|||
<i class="fa fa-arrows-alt" @click="requestFullScreen()"></i> |
|||
</span> |
|||
</el-tooltip> --> |
|||
<span class="faSpan"> |
|||
<i class="fa fa-arrows-alt" @click="requestFullScreen()"></i> |
|||
</span> |
|||
<el-dropdown @command="changeI18n"> |
|||
<span class="el-dropdown-link faSpan"> |
|||
<!-- <el-tooltip class="item" effect="dark" :content="t('message.public.languageSwitch')" placement="left"> |
|||
<i class="fa fa-language g-language" style="font-size: 18px"></i> |
|||
</el-tooltip> --> |
|||
<i class="fa fa-language g-language" style="font-size: 18px"></i> |
|||
</span> |
|||
|
|||
<template #dropdown> |
|||
<el-dropdown-menu> |
|||
<el-dropdown-item v-for="locale in $i18n.availableLocales" :key="`locale-${locale}`" :command="locale" |
|||
:style="{ |
|||
color: $i18n.locale == locale ? 'rgb(64, 158, 255)' : '', |
|||
}">{{ $filters.inspectLanguage(locale) }}</el-dropdown-item> |
|||
</el-dropdown-menu> |
|||
</template> |
|||
</el-dropdown> |
|||
<el-dropdown @command="settingFontSize"> |
|||
<span class="el-dropdown-link faSpan"> |
|||
<!-- <el-tooltip class="item" effect="dark" :content="t('message.public.settingFontSize')" placement="left"> |
|||
<i class="fa fa-font g-font" style="font-size: 18px"></i> |
|||
</el-tooltip> --> |
|||
<i class="fa fa-font g-font" style="font-size: 18px"></i> |
|||
</span> |
|||
|
|||
<template #dropdown> |
|||
<el-dropdown-menu> |
|||
<el-dropdown-item v-for="(item, key) in FONT_SIZE" :key="`fontsize-${key}`" :command="item.value" :style="{ |
|||
color: useSize == item.value ? 'rgb(64, 158, 255)' : '', |
|||
}">{{ t(`message.public.${item.value}`) }}</el-dropdown-item> |
|||
</el-dropdown-menu> |
|||
</template> |
|||
</el-dropdown> |
|||
<!-- <el-tooltip class="item" effect="dark" :content="t('message.public.messageCenter')" placement="bottom"> |
|||
<span class="faSpan"> |
|||
<el-badge is-dot class="item"> |
|||
<i class="fa faPad fa-bell-o" @click="toGetMessage"></i> |
|||
</el-badge> |
|||
</span> |
|||
</el-tooltip> --> |
|||
<span class="faSpan"> |
|||
<el-badge is-dot class="item"> |
|||
<i class="fa faPad fa-bell-o" @click="toGetMessage"></i> |
|||
</el-badge> |
|||
</span> |
|||
<!-- 用户名下拉菜单 --> |
|||
<el-dropdown trigger="click" @command="handleCommand"> |
|||
<span class="el-dropdown-link btn_username_group"> |
|||
<span class="btn_username" :title="username">{{ username }}</span> |
|||
<el-icon class="el-icon--right"> |
|||
<arrow-down /> |
|||
</el-icon> |
|||
</span> |
|||
<template #dropdown> |
|||
<el-dropdown-menu> |
|||
<el-dropdown-item command="signOut">{{ |
|||
t("message.public.loggedOut") |
|||
}}</el-dropdown-item> |
|||
<el-dropdown-item command="versionLog">{{ |
|||
t("message.public.versionLog") |
|||
}}</el-dropdown-item> |
|||
<el-dropdown-item command="baseInfo">{{ |
|||
t("message.public.basicInfo") |
|||
}}</el-dropdown-item> |
|||
<el-dropdown-item command="checkPass">{{ |
|||
t("message.public.changePassword") |
|||
}}</el-dropdown-item> |
|||
</el-dropdown-menu> |
|||
</template> |
|||
</el-dropdown> |
|||
<!-- 用户头像 --> |
|||
<div class="user-avatar"> |
|||
<img :src="getImage('touxiang', 'jpg')" /> |
|||
</div> |
|||
</div> |
|||
<check-pass v-model:passVisible="passVisible"></check-pass> |
|||
<base-info ref="baseInfoRef" v-model:baseVisible="baseVisible"></base-info> |
|||
<version-log v-model:versionVisible="versionVisible"></version-log> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts" name="AdminHeader"> |
|||
import { |
|||
getCurrentInstance, |
|||
ref, |
|||
computed, |
|||
unref, |
|||
inject |
|||
} from "vue"; |
|||
import $filters from "@/filters/index"; |
|||
import screenFull from "screenfull"; |
|||
import { ArrowDown } from "@element-plus/icons-vue"; |
|||
import { useRouter, useRoute } from "vue-router"; |
|||
import { ElMessage } from "element-plus"; |
|||
import Cookies from "js-cookie"; |
|||
import CheckPass from "@/components/dropDownItem/checkPass.vue"; |
|||
import BaseInfo from "@/components/dropDownItem/baseInfo.vue"; |
|||
import VersionLog from "@/components/dropDownItem/versionLog.vue"; |
|||
import { useI18n } from "vue-i18n"; |
|||
import { useTagStore } from "@/pinia/modules/tag"; |
|||
import { useUserStore } from "@/pinia/modules/user"; |
|||
import { FONT_SIZE } from "@/assets/js/dictionarie"; |
|||
import { removeToken } from "@/utils/auth"; |
|||
import { HeaderState } from "@/types/layout"; |
|||
import { getImage } from "@/utils"; |
|||
import { transitionLocal } from "@/locales/i18n"; //国际化 |
|||
const tagStore = useTagStore(); |
|||
const userStore = useUserStore(); |
|||
const route = useRoute(); |
|||
const { proxy } = <any>getCurrentInstance(); // vue原型 |
|||
const { t, locale: language } = useI18n(); |
|||
const router = useRouter(); //路由 |
|||
const baseInfoRef = ref(); |
|||
const useSize = computed(() => Cookies.get("size")); |
|||
const collapse = computed(() => tagStore.collapse); //打开关闭sidebar |
|||
const username = computed(() => userStore?.user?.username || "待完善"); //用户名 |
|||
let passVisible = ref<boolean>(false); //修改密码弹框 |
|||
let baseVisible = ref<boolean>(false); //基本信息弹框 |
|||
let versionVisible = ref<boolean>(false); //版本日志弹框 |
|||
const reload = inject("reload") as Function; |
|||
const requestFullScreen = () => { |
|||
//进入全屏 退出全屏 |
|||
if (screenFull.isEnabled) { |
|||
//检测是否支持screenFull |
|||
if (screenFull.isFullscreen) { |
|||
//如果是全屏则退出 |
|||
screenFull.exit(); |
|||
} else { |
|||
//不是则进入全屏 |
|||
screenFull.toggle(); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
const switchCollapse = () => { |
|||
//菜单栏展开关闭 |
|||
setTimeout(() => { |
|||
tagStore.switchCollapse(!unref(collapse)); |
|||
}, 0); |
|||
}; |
|||
|
|||
const handleCommand = <T extends string>(command: T) => { |
|||
//用户下拉菜单 |
|||
if (command === "signOut") { |
|||
removeToken(); |
|||
tagStore.delRightMenu({ WHITE_TAGS_LIST: [] }); //退出清空所有菜单 |
|||
router.push("/login"); |
|||
ElMessage.success("登出成功"); |
|||
} else if (command === "checkPass") { |
|||
//打开修改密码弹框 |
|||
passVisible.value = true; |
|||
} else if (command === "baseInfo") { |
|||
//打开基本信息弹框 |
|||
unref(baseInfoRef).openBaseInfo() |
|||
} else if (command === "versionLog") { |
|||
//打开版本日志弹框 |
|||
versionVisible.value = true; |
|||
} |
|||
}; |
|||
|
|||
const toGetMessage = () => { |
|||
//进入反馈中心 |
|||
router.push("/main/feedbackCenter"); |
|||
}; |
|||
|
|||
const changeI18n = <T extends string>(type: T): void | boolean => { |
|||
//切换中英文 |
|||
if (type == Cookies.get("lang")) { |
|||
//如果已经是这个语言则提醒 |
|||
ElMessage.warning(`${t("message.public.recurrentSelection")}`); |
|||
return false; |
|||
} |
|||
Cookies.set("lang", type); //存储国际化 |
|||
language.value = type; //更新i18n配置 |
|||
proxy.$i18n.locale = type; //更新i18n配置 |
|||
ElMessage.success(`${t("message.public.editLang")}`); |
|||
router.go(0); //刷新页面来显示效果 |
|||
}; |
|||
|
|||
const settingFontSize = <T extends string>(type: T): void | boolean => { |
|||
//切换字体大小 |
|||
if (type == Cookies.get("size")) { |
|||
//如果已经是这个语言则提醒 |
|||
ElMessage.warning(`${t("message.public.recurrentSelection")}`); |
|||
return false; |
|||
} |
|||
Cookies.set("size", type ?? "default"); |
|||
ElMessage.success(`${t("message.public.switchSuccess")}`); |
|||
router.go(0); //刷新页面来显示效果 |
|||
}; |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.admin-header { |
|||
box-sizing: border-box; |
|||
width: 100%; |
|||
height: 64px; |
|||
font-size: 18px; |
|||
color: #616161; |
|||
background: #fff; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
|
|||
.collapse-left { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
|
|||
.collapse-btn { |
|||
padding: 0px 0px 0 15px; |
|||
cursor: pointer; |
|||
|
|||
i { |
|||
margin-right: 25px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.el-icon-s-fold, |
|||
.el-icon-s-unfold { |
|||
font-size: 25px; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.collapse-right { |
|||
float: right; |
|||
padding-right: 8px; |
|||
height: 64px; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
|
|||
.faSpan { |
|||
padding-right: 20px; |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
|
|||
.user-avatar { |
|||
margin: 0 15px 0 5px; |
|||
} |
|||
|
|||
img { |
|||
display: block; |
|||
width: 40px; |
|||
height: 40px; |
|||
border-radius: 50%; |
|||
} |
|||
|
|||
.btn_username_group { |
|||
cursor: pointer; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
|
|||
.btn_username { |
|||
text-align: right; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
width: 60px; |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,270 @@ |
|||
<template> |
|||
<div class="tag_content"> |
|||
<TransitionGroup v-if="tagsList.length" name="list" class="tags" tag="div"> |
|||
<el-tag v-for="(tag, index) in tagsList" :key="index" :type="selectPath === tag.path ? '' : 'info'" |
|||
:class="selectPath === tag.path ? 'tag_check' : 'tag_null_check'" |
|||
:closable="tag.path == '/dashboard/workbench' ? false : true" |
|||
:effect="selectPath === tag.path ? 'dark' : 'plain'" :disable-transitions="false" @close="closeTag(index)" |
|||
@click="triggerTag(tag, 'go')" size="large"> |
|||
{{ t("message." + tag.title) }} |
|||
</el-tag> |
|||
</TransitionGroup> |
|||
<div class="right_trigger_box"> |
|||
<span class="el-dropdown-link fullScreen" @click="toFullScreen"> |
|||
<el-icon> |
|||
<full-screen /> |
|||
</el-icon> |
|||
</span> |
|||
<el-dropdown trigger="click" @command="triggerRight"> |
|||
<span class="el-dropdown-link"> |
|||
<i class="fa fa-angle-down"></i> |
|||
</span> |
|||
<template #dropdown> |
|||
<el-dropdown-menu> |
|||
<el-dropdown-item command="reload"> |
|||
<span>{{ t("message.public.reload") }}</span> |
|||
</el-dropdown-item> |
|||
<el-dropdown-item command="all" :disabled="tagsList.length === 1"> |
|||
<span>{{ t("message.public.closeAll") }}</span> |
|||
</el-dropdown-item> |
|||
<el-dropdown-item command="other" :disabled="tagsList.length <= 2">{{ t("message.public.closeOther") }} |
|||
</el-dropdown-item> |
|||
</el-dropdown-menu> |
|||
</template> |
|||
</el-dropdown> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts" name="AdminTags"> |
|||
import { |
|||
getCurrentInstance, |
|||
computed, |
|||
onMounted, |
|||
ref, |
|||
inject, |
|||
toRaw, |
|||
unref, |
|||
isProxy, |
|||
watch |
|||
} from "vue"; |
|||
import { FullScreen } from "@element-plus/icons-vue"; |
|||
import { |
|||
useRoute, |
|||
useRouter, |
|||
} from "vue-router"; |
|||
import { ElMessage, ElNotification } from "element-plus"; |
|||
import { useI18n } from "vue-i18n"; |
|||
import { useTagStore } from "@/pinia/modules/tag"; |
|||
import screenFull from "screenfull"; |
|||
import { Tag } from "@/types/layout"; |
|||
import { storeToRefs } from "pinia"; |
|||
const tagStore = useTagStore(); |
|||
const { tagsList } = storeToRefs(tagStore); |
|||
const { t } = useI18n(); |
|||
const route = useRoute(); |
|||
const router = useRouter(); |
|||
const { proxy } = <any>getCurrentInstance(); |
|||
const reload = inject("reload") as Function; |
|||
let selectPath = ref<string>(""); |
|||
const setTags = <T extends Tag>(target: T) => { |
|||
// 设置标签 |
|||
const isExist = unref(tagsList).some((item: { path: string }) => { |
|||
return item.path === target.fullPath; |
|||
}); |
|||
if (!isExist) { |
|||
if (unref(tagsList).length >= tagStore.MAX_TAG_LENGTH) { |
|||
//如果标签到10个再选择就将最初的删除 |
|||
let index: number = 0; |
|||
for (const [i, v] of unref(tagsList).entries()) { |
|||
if (v.path === "/dashboard/workbench") { |
|||
if (i === 0) { |
|||
index += 1; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
tagStore.delTags({ index }); |
|||
} |
|||
|
|||
|
|||
tagStore.setTags({ |
|||
title: target.meta.title, |
|||
...target, |
|||
}); |
|||
} |
|||
|
|||
}; |
|||
|
|||
|
|||
const closeTag = <K extends number>(index: K) => { |
|||
//关闭标签 |
|||
tagStore.delTags({ index }); |
|||
triggerTag(unref(tagsList)[unref(tagsList).length === index ? index - 1 : index], "go"); |
|||
}; |
|||
|
|||
const triggerTag = <T extends Tag, K extends string>(tag: T, type?: K) => { |
|||
//选择标签 |
|||
proxy._public.debounce(() => { |
|||
selectPath.value = tag.path; |
|||
if (type) { |
|||
//如果是点击标签则进行路由跳转 |
|||
router.push(tag.path); |
|||
} |
|||
}, 150); |
|||
}; |
|||
|
|||
const triggerRight = <V extends string>(menu: V) => { |
|||
//右菜单操作 |
|||
if (["all", "other"].includes(menu)) { |
|||
//如果是关闭标签 |
|||
let WHITE_TAGS_LIST: string[] = ["/dashboard/workbench"]; |
|||
if (unref(selectPath) !== WHITE_TAGS_LIST[0] && menu === "other") { |
|||
//如果是其他 则把自己添加进白名单 |
|||
WHITE_TAGS_LIST.push(unref(selectPath)); |
|||
} |
|||
tagStore.delRightMenu({ WHITE_TAGS_LIST }); |
|||
router.push(WHITE_TAGS_LIST[WHITE_TAGS_LIST.length - 1]); |
|||
} else if (menu === "reload") { |
|||
//如果是重新渲染 |
|||
reload(); |
|||
} |
|||
}; |
|||
|
|||
const toFullScreen = () => { |
|||
//进入全屏 退出全屏 |
|||
const element = document.getElementById("screen-display") as HTMLDivElement; |
|||
if (screenFull.isEnabled) { |
|||
//检测是否支持screenFull |
|||
if (screenFull.isFullscreen) { |
|||
//如果是全屏则退出 |
|||
screenFull.exit(); |
|||
} else { |
|||
//不是则进入全屏 |
|||
screenFull.request(element); |
|||
ElNotification({ |
|||
title: "Success", |
|||
message: "当前为全屏模式,退出按ESC.", |
|||
type: "success", |
|||
duration: 2000, |
|||
appendTo: "#screen-display", |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
|
|||
|
|||
router.beforeEach(async (to, from) => { |
|||
//监听路由变动 |
|||
if (!tagStore.BLACK_LIST.includes(to.path)) { |
|||
await setTags(to); |
|||
await triggerTag(to); |
|||
} |
|||
}); |
|||
|
|||
|
|||
onMounted(() => { |
|||
//监听路由变动 |
|||
setTags(route); |
|||
triggerTag(route); |
|||
}); |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.tag_content { |
|||
padding: 6px 0px; |
|||
margin: 0px 12px; |
|||
box-sizing: border-box; |
|||
white-space: nowrap; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
|
|||
.list-move, |
|||
/* 对移动中的元素应用的过渡 */ |
|||
.list-enter-active, |
|||
.list-leave-active { |
|||
transition: all 0.5s ease; |
|||
} |
|||
|
|||
.list-enter-from, |
|||
.list-leave-to { |
|||
opacity: 0; |
|||
transform: translateX(30px); |
|||
} |
|||
|
|||
/* 确保将离开的元素从布局流中删除 |
|||
以便能够正确地计算移动的动画。 */ |
|||
.list-leave-active { |
|||
position: absolute; |
|||
} |
|||
|
|||
.tags { |
|||
width: calc(100vw - 310px); |
|||
overflow: auto; |
|||
} |
|||
|
|||
.list-move, |
|||
/* 对移动中的元素应用的过渡 */ |
|||
.list-enter-active, |
|||
.list-leave-active { |
|||
transition: all 0.5s ease; |
|||
} |
|||
|
|||
.list-enter-from, |
|||
.list-leave-to { |
|||
opacity: 0; |
|||
transform: translateX(30px); |
|||
} |
|||
|
|||
/* 确保将离开的元素从布局流中删除 |
|||
以便能够正确地计算移动的动画。 */ |
|||
.list-leave-active { |
|||
position: absolute; |
|||
} |
|||
|
|||
.el-tag { |
|||
cursor: pointer; |
|||
margin-right: 8px; |
|||
} |
|||
|
|||
.tag_check { |
|||
border-radius: 1px; |
|||
} |
|||
|
|||
.tag_null_check { |
|||
background-color: #ffffff !important; |
|||
border-color: #e4e7ed !important; |
|||
color: rgb(97, 97, 97) !important; |
|||
border-radius: 1px; |
|||
} |
|||
|
|||
.right_trigger_box { |
|||
display: flex; |
|||
border: 1px solid #e4e7ed; |
|||
|
|||
.fullScreen { |
|||
border-right: 1px solid #e4e7ed; |
|||
} |
|||
|
|||
.el-dropdown-link { |
|||
cursor: pointer; |
|||
height: 26px; |
|||
width: 50px; |
|||
display: flex; |
|||
background: #fff; |
|||
border-radius: 2px; |
|||
align-items: center; |
|||
justify-content: center; |
|||
color: #a8abb2; |
|||
|
|||
.fa-angle-down { |
|||
font-size: 20px; |
|||
} |
|||
} |
|||
|
|||
.el-dropdown-link:hover { |
|||
color: #000; |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,3 @@ |
|||
<template> |
|||
<router-view></router-view> |
|||
</template> |
@ -0,0 +1,84 @@ |
|||
<template> |
|||
<div class="app-wrapper"> |
|||
<el-container style="height: 100vh"> |
|||
<el-aside style="width: auto"> |
|||
<side-bar></side-bar> |
|||
</el-aside> |
|||
<el-container> |
|||
<el-header height="64px"> |
|||
<Header></Header> |
|||
</el-header> |
|||
<el-main> |
|||
<Tags></Tags> |
|||
<div id="screen-display" v-loading="!isReload" class="content"> |
|||
<router-view v-if="isReload" v-slot="{ Component }"> |
|||
<Transition appear name="fade" appear-active-class="animate__animated animate__pulse" |
|||
enter-active-class="animate__animated animate__fadeIn"> |
|||
<!--进入 enter-active-class 移出 leave-active-class 初始 appear-active-class--> |
|||
<component :is="Component" v-if="Component" /> |
|||
</Transition> |
|||
</router-view> |
|||
<el-backtop target=".content"></el-backtop> |
|||
</div> |
|||
</el-main> |
|||
</el-container> |
|||
</el-container> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts" name="AdminMain"> |
|||
import SideBar from "@/layouts/components/SideBar/index.vue"; |
|||
import Header from "@/layouts/components/Header/index.vue"; |
|||
import Tags from "@/layouts/components/Tags/index.vue"; |
|||
import { nextTick, provide, ref } from "vue"; |
|||
const isReload = ref<boolean>(true); |
|||
const reload = () => { |
|||
isReload.value = false; |
|||
nextTick(() => { |
|||
setTimeout(() => { |
|||
isReload.value = true; |
|||
}, 500); |
|||
}) |
|||
} |
|||
provide("reload", reload); |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.app-wrapper { |
|||
width: 100%; |
|||
height: 100%; |
|||
overflow: hidden; |
|||
|
|||
.el-aside { |
|||
position: relative; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.el-header, |
|||
.el-main { |
|||
padding: 0; |
|||
} |
|||
|
|||
.el-main { |
|||
background: #f0f2f5; |
|||
|
|||
.content { |
|||
padding: 0px 5px 8px 5px; |
|||
box-sizing: border-box; |
|||
margin: 0px 8px 0 8px; |
|||
// background: #ffffff; |
|||
overflow-x: hidden; |
|||
overflow-y: auto; |
|||
height: calc(100vh - 115px); |
|||
color: #515a6e; |
|||
min-width: 1000px; |
|||
} |
|||
} |
|||
|
|||
.el-affix--fixed, |
|||
.el-overlay { |
|||
right: 8px !important; |
|||
position: fixed; |
|||
} |
|||
|
|||
|
|||
} |
|||
</style> |
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue