'admin-22.11.16:发布v2.3.0,具体更新内容查看CHANGELOG.md'

This commit is contained in:
lyt 2022-11-16 15:34:23 +08:00
parent aba9f3947d
commit cdf1b715df
126 changed files with 3531 additions and 2759 deletions

View File

@ -2,6 +2,27 @@
🎉🎉🔥 `vue-next-admin` 基于 vue3.x 、Typescript、vite、Element plus 等适配手机、平板、pc 的后台开源免费模板库vue2.x 请切换 vue-prev-admin 分支) 🎉🎉🔥 `vue-next-admin` 基于 vue3.x 、Typescript、vite、Element plus 等适配手机、平板、pc 的后台开源免费模板库vue2.x 请切换 vue-prev-admin 分支)
## 2.3.0
`2022.11.16`
- 🌟 更新 依赖更新最新版本
- 🎉 新增 新版登录页
- 🎉 新增 tagsview 鼠标中键 `关闭当前 tagsview`
- 🎉 新增 `分栏菜单鼠标悬停预加载`。[分栏模式如何去掉鼠标悬浮父级菜单,分栏菜单自动加载的功能啊](https://gitee.com/lyt-top/vue-next-admin/issues/I5RUY7)。操作路径:`布局配置 -> 分栏设置`
- 🐞 修复 [vue-i18n](https://vue-i18n.intlify.dev/api/general.html#createi18n) 报错,[!39 修复 i18n 兼容性问题](https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/39/files),感谢[@随心](https://toscode.gitee.com/jiangqiang1996)
- 🐞 修复 顶栏搜索功能点击蒙蔽弹窗不关闭
- 🐞 修复 [!38 fix: bug refreshRouterViewKey 值为 null 导致路由缓存第一次无效](https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/38/files),感谢[@P)](https://toscode.gitee.com/foxp8y)
- 🐞 修复 `路由参数 -> 普通路由/动态路由` 国际化演示时,`tagsView` 和 `浏览器标题` 显示异常。[演示中:路由参数界面 -> 动态路由,国际化显示时面包屑、浏览器标题有 bug](https://gitee.com/lyt-top/vue-next-admin/issues/I5JRJG)
- 🐞 修复 `路由参数 -> 普通路由/动态路由` 动态设置 `tagsViewName` 时,`tagsView 右键菜单刷新` 功能失效也就是路由后面有参数时query、params。[普通或动态路由新建页面后点击 tagview 刷新无效](https://gitee.com/lyt-top/vue-next-admin/issues/I5K3YO),感谢[@dejavuuuuu](https://gitee.com/zc19951010)
- 🐞 修复 [表单el-form字体图标偏移问题](https://gitee.com/lyt-top/vue-next-admin/issues/I5K1PM)
- 🐞 修复 路由 `router.addRoute` 时,一直提示 `No match found for location with path 'xxx'`
- 🎯 优化 全局 `getCurrentInstance` 替换成 [`provide/inject`](https://cn.vuejs.org/api/application.html#app-provide) 或通过 `ref` 处理
- 🎯 优化 引入组件方式 `(import xxx from xxx)` 改成 `defineAsyncComponent(() => import(xxx))`
- 🎯 优化 页面高度 100% 问题,重写布局配置 `界面设置 -> 固定 Header` 多余的 `el-scrollbar` 逻辑、重写各界面需 `计算属性 computed` 设置动态高度问题(改为 css `flex` 设置自适应高度,具体查看文档:[设置可视区高度 100%](https://lyt-top.gitee.io/vue-next-admin-doc-preview/config/otherIssues/#%E8%AE%BE%E7%BD%AE%E5%8F%AF%E8%A7%86%E5%8C%BA%E9%AB%98%E5%BA%A6-100)。[!31 修复页面样式无法通过百分比设置的问题](https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/31),感谢[@LostDeer](https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/31/files)。`(改动较大,删除多余代码)`
- 🎯 优化 [wangeditor](https://www.wangeditor.com/) 组件,`@wangeditor/editor-for-vue`。可自行修改,组件位置:`/src/components/editor`。相关 Issues[wangeditor 编辑器多个菜单不能回弹](https://gitee.com/lyt-top/vue-next-admin/issues/I5M5H7)
- 🌈 重构 外链、内嵌 iframe 逻辑 + 美化iframe 支持缓存
## 2.2.0 ## 2.2.0
`2022.07.10` `2022.07.10`

View File

@ -68,10 +68,13 @@ cnpm run dev
cnpm run build cnpm run build
``` ```
#### 📚 开发文档
- 查看开发文档:<a href="https://lyt-top.gitee.io/vue-next-admin-doc-preview" target="_blank">vue-next-admin-doc</a>
#### 💯 学习交流加 QQ 群 #### 💯 学习交流加 QQ 群
- 若加群了没同意一般秒过那就是群满了500 人群请换一个群试试。群会定期清理半年6 个月)未发言的群友,资源有限,请谅解。建议勿加多群,可能会误伤! - 若加群了没同意一般秒过那就是群满了500 人群请换一个群试试。群会定期清理半年6 个月)未发言的群友,资源有限,请谅解。建议勿加多群,可能会误伤!微信群由于只有 `7天有效` 就不放这里了。
- 查看开发文档:<a href="https://lyt-top.gitee.io/vue-next-admin-doc-preview" target="_blank">vue-next-admin-doc</a>
- 群号码: - 群号码:
1 群:<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=RdUY97Vx0T0vZ_1OOu-X1yFNkWgDwbjC&jump_from=webapi">665452019</a> 1 群:<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=RdUY97Vx0T0vZ_1OOu-X1yFNkWgDwbjC&jump_from=webapi">665452019</a>
2 群:<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=zVfy3gNy7pNWVK3kMduDzwU369PZg2fw&jump_from=webapi">766356862</a> 2 群:<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=zVfy3gNy7pNWVK3kMduDzwU369PZg2fw&jump_from=webapi">766356862</a>

2680
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "vue-next-admin", "name": "vue-next-admin",
"version": "2.2.0", "version": "2.3.0",
"description": "vue3 vite next admin template", "description": "vue3 vite next admin template",
"author": "lyt_20201208", "author": "lyt_20201208",
"license": "MIT", "license": "MIT",
@ -10,48 +10,48 @@
"lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/" "lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^2.0.6", "@element-plus/icons-vue": "^2.0.10",
"@wangeditor/editor": "^5.1.11", "@wangeditor/editor-for-vue": "^5.1.11",
"axios": "^0.27.2", "axios": "^1.1.3",
"countup.js": "^2.3.2", "countup.js": "^2.3.2",
"cropperjs": "^1.5.12", "cropperjs": "^1.5.12",
"echarts": "^5.3.3", "echarts": "^5.4.0",
"echarts-gl": "^2.0.9", "echarts-gl": "^2.0.9",
"echarts-wordcloud": "^2.0.0", "echarts-wordcloud": "^2.0.0",
"element-plus": "^2.2.9", "element-plus": "^2.2.21",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"jsplumb": "^2.15.6", "jsplumb": "^2.15.6",
"mitt": "^3.0.0", "mitt": "^3.0.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^2.0.16", "pinia": "^2.0.23",
"print-js": "^1.6.0", "print-js": "^1.6.0",
"qrcodejs2-fixes": "^0.0.2", "qrcodejs2-fixes": "^0.0.2",
"screenfull": "^6.0.2", "screenfull": "^6.0.2",
"sortablejs": "^1.15.0", "sortablejs": "^1.15.0",
"splitpanes": "^3.1.1", "splitpanes": "^3.1.5",
"vue": "^3.2.37", "vue": "^3.2.45",
"vue-clipboard3": "^2.0.0", "vue-clipboard3": "^2.0.0",
"vue-grid-layout": "^3.0.0-beta1", "vue-grid-layout": "^3.0.0-beta1",
"vue-i18n": "^9.1.10", "vue-i18n": "^9.2.2",
"vue-router": "^4.1.2" "vue-router": "^4.1.6"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^18.0.6", "@types/node": "^18.11.9",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.0",
"@types/sortablejs": "^1.13.0", "@types/sortablejs": "^1.15.0",
"@typescript-eslint/eslint-plugin": "^5.30.7", "@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.30.7", "@typescript-eslint/parser": "^5.43.0",
"@vitejs/plugin-vue": "^2.3.3", "@vitejs/plugin-vue": "^3.2.0",
"@vue/compiler-sfc": "^3.2.37", "@vue/compiler-sfc": "^3.2.45",
"dotenv": "^16.0.1", "dotenv": "^16.0.3",
"eslint": "^8.20.0", "eslint": "^8.27.0",
"eslint-plugin-vue": "^9.2.0", "eslint-plugin-vue": "^9.7.0",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"sass": "^1.53.0", "sass": "^1.56.1",
"sass-loader": "^13.0.2", "sass-loader": "^13.2.0",
"typescript": "^4.7.4", "typescript": "^4.9.3",
"vite": "^2.9.14", "vite": "^3.2.4",
"vue-eslint-parser": "^9.0.3" "vue-eslint-parser": "^9.1.0"
}, },
"browserslist": [ "browserslist": [
"> 1%", "> 1%",

View File

@ -1,5 +1,5 @@
<template> <template>
<el-config-provider :size="getGlobalComponentSize" :locale="i18nLocale"> <el-config-provider :size="getGlobalComponentSize" :locale="getGlobalI18n">
<router-view v-show="themeConfig.lockScreenTime > 1" /> <router-view v-show="themeConfig.lockScreenTime > 1" />
<LockScreen v-if="themeConfig.isLockScreen" /> <LockScreen v-if="themeConfig.isLockScreen" />
<Setings ref="setingsRef" v-show="themeConfig.lockScreenTime > 1" /> <Setings ref="setingsRef" v-show="themeConfig.lockScreenTime > 1" />
@ -8,39 +8,39 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, ref, getCurrentInstance, onBeforeMount, onMounted, onUnmounted, nextTick, defineComponent, watch, reactive, toRefs } from 'vue'; import { defineAsyncComponent, computed, ref, onBeforeMount, onMounted, onUnmounted, nextTick, defineComponent, watch } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes'; import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import other from '/@/utils/other'; import other from '/@/utils/other';
import { Local, Session } from '/@/utils/storage'; import { Local, Session } from '/@/utils/storage';
import mittBus from '/@/utils/mitt';
import setIntroduction from '/@/utils/setIconfont'; import setIntroduction from '/@/utils/setIconfont';
import LockScreen from '/@/layout/lockScreen/index.vue';
import Setings from '/@/layout/navBars/breadcrumb/setings.vue';
import CloseFull from '/@/layout/navBars/breadcrumb/closeFull.vue';
export default defineComponent({ export default defineComponent({
name: 'app', name: 'app',
components: { LockScreen, Setings, CloseFull }, components: {
LockScreen: defineAsyncComponent(() => import('/@/layout/lockScreen/index.vue')),
Setings: defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/setings.vue')),
CloseFull: defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/closeFull.vue')),
},
setup() { setup() {
const { proxy } = <any>getCurrentInstance(); const { messages, locale } = useI18n();
const setingsRef = ref(); const setingsRef = ref();
const route = useRoute(); const route = useRoute();
const stores = useTagsViewRoutes(); const stores = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);
const state = reactive({
i18nLocale: null,
});
// //
const getGlobalComponentSize = computed(() => { const getGlobalComponentSize = computed(() => {
return other.globalComponentSize(); return other.globalComponentSize();
}); });
// // i18n
const openSetingsDrawer = () => { const getGlobalI18n = computed(() => {
setingsRef.value.openDrawer(); return messages.value[locale.value];
}; });
// //
onBeforeMount(() => { onBeforeMount(() => {
// icon // icon
@ -52,12 +52,8 @@ export default defineComponent({
onMounted(() => { onMounted(() => {
nextTick(() => { nextTick(() => {
// //
proxy.mittBus.on('openSetingsDrawer', () => { mittBus.on('openSetingsDrawer', () => {
openSetingsDrawer(); setingsRef.value.openDrawer();
});
// i18nApp.vue el-config-provider
proxy.mittBus.on('getI18nConfig', (locale: string) => {
(state.i18nLocale as string | null) = locale;
}); });
// //
if (Local.get('themeConfig')) { if (Local.get('themeConfig')) {
@ -72,8 +68,7 @@ export default defineComponent({
}); });
// /i18n // /i18n
onUnmounted(() => { onUnmounted(() => {
proxy.mittBus.off('openSetingsDrawer', () => {}); mittBus.off('openSetingsDrawer', () => {});
proxy.mittBus.off('getI18nConfig', () => {});
}); });
// //
watch( watch(
@ -89,7 +84,7 @@ export default defineComponent({
themeConfig, themeConfig,
setingsRef, setingsRef,
getGlobalComponentSize, getGlobalComponentSize,
...toRefs(state), getGlobalI18n,
}; };
}, },
}); });

19
src/assets/login-bg.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -1,40 +1,29 @@
<template> <template>
<div class="editor-container"> <div class="editor-container">
<div ref="editorToolbar"></div> <Toolbar :editor="editorRef" :mode="mode" />
<div ref="editorContent" :style="{ height }"></div> <Editor :mode="mode" :defaultConfig="editorConfig" :style="{ height }" v-model="editorVal" @onCreated="handleCreated" @onChange="handleChange" />
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { toRefs, reactive, onMounted, watch, defineComponent } from 'vue'; // https://www.wangeditor.com/v5/for-frame.html#vue3
import { createEditor, createToolbar, IEditorConfig, IToolbarConfig, IDomEditor } from '@wangeditor/editor';
import '@wangeditor/editor/dist/css/style.css'; import '@wangeditor/editor/dist/css/style.css';
import { toolbarKeys } from './toolbar'; import { defineComponent, reactive, toRefs, shallowRef, watch, onBeforeUnmount } from 'vue';
import { Toolbar, Editor } from '@wangeditor/editor-for-vue';
//
interface WangeditorState {
editorToolbar: HTMLDivElement | null;
editorContent: HTMLDivElement | null;
editor: any;
}
export default defineComponent({ export default defineComponent({
name: 'wngEditor', name: 'wngEditor',
components: { Toolbar, Editor },
props: { props: {
// id
id: {
type: String,
default: () => 'wangeditor',
},
// //
isDisable: { disable: {
type: Boolean, type: Boolean,
default: () => false, default: () => true,
}, },
// placeholder // placeholder
placeholder: { placeholder: {
type: String, type: String,
default: () => '请输入内容', default: () => '请输入内容...',
}, },
// //
// https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5 // https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
@ -52,62 +41,46 @@ export default defineComponent({
}, },
}, },
setup(props, { emit }) { setup(props, { emit }) {
const state = reactive<WangeditorState>({ const editorRef = shallowRef();
editorToolbar: null, const state = reactive({
editor: null, editorConfig: {
editorContent: null, placeholder: props.placeholder,
},
editorVal: props.modelValue,
}); });
// //
const wangeditorConfig = () => { const handleCreated = (editor: any) => {
const editorConfig: Partial<IEditorConfig> = { MENU_CONF: {} }; editorRef.value = editor;
props.isDisable ? (editorConfig.readOnly = true) : (editorConfig.readOnly = false);
editorConfig.placeholder = props.placeholder;
editorConfig.onChange = (editor: IDomEditor) => {
// console.log('content', editor.children);
// console.log('html', editor.getHtml());
emit('update:modelValue', editor.getHtml());
};
(<any>editorConfig).MENU_CONF['uploadImage'] = {
base64LimitSize: 10 * 1024 * 1024,
};
return editorConfig;
}; };
// //
const toolbarConfig = () => { const handleChange = (editor: any) => {
const toolbarConfig: Partial<IToolbarConfig> = {}; // console.log(editor.getText());
toolbarConfig.toolbarKeys = toolbarKeys; // console.log(editor.getHtml());
return toolbarConfig; emit('update:modelValue', editor.getHtml());
}; };
// //
// https://www.wangeditor.com/
const initWangeditor = () => {
state.editor = createEditor({
html: props.modelValue,
selector: state.editorContent!,
config: wangeditorConfig(),
mode: props.mode,
});
createToolbar({
editor: state.editor,
selector: state.editorToolbar!,
mode: props.mode,
config: toolbarConfig(),
});
};
//
onMounted(() => {
initWangeditor();
});
//
// https://gitee.com/lyt-top/vue-next-admin/issues/I4LM7I // https://gitee.com/lyt-top/vue-next-admin/issues/I4LM7I
watch( watch(
() => props.modelValue, () => props.disable,
(value) => { (bool) => {
state.editor.clear(); const editor = editorRef.value;
state.editor.dangerouslyInsertHtml(value); if (editor == null) return;
bool ? editor.disable() : editor.enable();
},
{
deep: true,
} }
); );
//
onBeforeUnmount(() => {
const editor = editorRef.value;
if (editor == null) return;
editor.destroy();
});
return { return {
editorRef,
handleCreated,
handleChange,
...toRefs(state), ...toRefs(state),
}; };
}, },

View File

@ -1,60 +0,0 @@
/**
*
*/
export const toolbarKeys = [
'headerSelect',
'blockquote',
'|',
'bold',
'underline',
'italic',
{
key: 'group-more-style',
title: '更多',
iconSvg:
'<svg viewBox="0 0 1024 1024"><path d="M204.8 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path><path d="M505.6 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path><path d="M806.4 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path></svg>',
menuKeys: ['through', 'code', 'sup', 'sub', 'clearStyle'],
},
'color',
'bgColor',
'|',
'fontSize',
'fontFamily',
'lineHeight',
'|',
'bulletedList',
'numberedList',
'todo',
{
key: 'group-justify',
title: '对齐',
iconSvg:
'<svg viewBox="0 0 1024 1024"><path d="M768 793.6v102.4H51.2v-102.4h716.8z m204.8-230.4v102.4H51.2v-102.4h921.6z m-204.8-230.4v102.4H51.2v-102.4h716.8zM972.8 102.4v102.4H51.2V102.4h921.6z"></path></svg>',
menuKeys: ['justifyLeft', 'justifyRight', 'justifyCenter', 'justifyJustify'],
},
{
key: 'group-indent',
title: '缩进',
iconSvg:
'<svg viewBox="0 0 1024 1024"><path d="M0 64h1024v128H0z m384 192h640v128H384z m0 192h640v128H384z m0 192h640v128H384zM0 832h1024v128H0z m0-128V320l256 192z"></path></svg>',
menuKeys: ['indent', 'delIndent'],
},
'|',
'emotion',
'insertLink',
{
key: 'group-image',
title: '图片',
iconSvg:
'<svg viewBox="0 0 1024 1024"><path d="M959.877 128l0.123 0.123v767.775l-0.123 0.122H64.102l-0.122-0.122V128.123l0.122-0.123h895.775zM960 64H64C28.795 64 0 92.795 0 128v768c0 35.205 28.795 64 64 64h896c35.205 0 64-28.795 64-64V128c0-35.205-28.795-64-64-64zM832 288.01c0 53.023-42.988 96.01-96.01 96.01s-96.01-42.987-96.01-96.01S682.967 192 735.99 192 832 234.988 832 288.01zM896 832H128V704l224.01-384 256 320h64l224.01-192z"></path></svg>',
menuKeys: ['uploadImage'],
},
'insertTable',
'codeBlock',
'divider',
'|',
'undo',
'redo',
'|',
'fullScreen',
];

View File

@ -57,6 +57,7 @@ const { themeConfig } = storeToRefs(stores);
// 导出语言国际化 // 导出语言国际化
// https://vue-i18n.intlify.dev/guide/essentials/fallback.html#explicit-fallback-with-one-locale // https://vue-i18n.intlify.dev/guide/essentials/fallback.html#explicit-fallback-with-one-locale
export const i18n = createI18n({ export const i18n = createI18n({
legacy: false,
silentTranslationWarn: true, silentTranslationWarn: true,
missingWarn: false, missingWarn: false,
silentFallbackWarn: true, silentFallbackWarn: true,

View File

@ -71,7 +71,8 @@ export default {
personal: 'personal', personal: 'personal',
tools: 'tools', tools: 'tools',
layoutLinkView: 'LinkView', layoutLinkView: 'LinkView',
layoutIfameView: 'IfameView', layoutIframeViewOne: 'IframeViewOne',
layoutIframeViewTwo: 'IframeViewTwo',
}, },
staticRoutes: { staticRoutes: {
signIn: 'signIn', signIn: 'signIn',
@ -139,6 +140,7 @@ export default {
twoColumnsMenuBar: 'Column menu background', twoColumnsMenuBar: 'Column menu background',
twoColumnsMenuBarColor: 'Default font color bar menu', twoColumnsMenuBarColor: 'Default font color bar menu',
twoIsColumnsMenuBarColorGradual: 'Column gradient', twoIsColumnsMenuBarColorGradual: 'Column gradient',
twoIsColumnsMenuHoverPreload: 'Column Menu Hover Preload',
threeTitle: 'Interface settings', threeTitle: 'Interface settings',
threeIsCollapse: 'Menu horizontal collapse', threeIsCollapse: 'Menu horizontal collapse',
threeIsUniqueOpened: 'Menu accordion', threeIsUniqueOpened: 'Menu accordion',

View File

@ -71,7 +71,8 @@ export default {
personal: '个人中心', personal: '个人中心',
tools: '工具类集合', tools: '工具类集合',
layoutLinkView: '外链', layoutLinkView: '外链',
layoutIfameView: '内嵌 iframe', layoutIframeViewOne: '内嵌 iframe1',
layoutIframeViewTwo: '内嵌 iframe2',
}, },
staticRoutes: { staticRoutes: {
signIn: '登录', signIn: '登录',
@ -139,6 +140,7 @@ export default {
twoColumnsMenuBar: '分栏菜单背景', twoColumnsMenuBar: '分栏菜单背景',
twoColumnsMenuBarColor: '分栏菜单默认字体颜色', twoColumnsMenuBarColor: '分栏菜单默认字体颜色',
twoIsColumnsMenuBarColorGradual: '分栏菜单背景渐变', twoIsColumnsMenuBarColorGradual: '分栏菜单背景渐变',
twoIsColumnsMenuHoverPreload: '分栏菜单鼠标悬停预加载',
threeTitle: '界面设置', threeTitle: '界面设置',
threeIsCollapse: '菜单水平折叠', threeIsCollapse: '菜单水平折叠',
threeIsUniqueOpened: '菜单手风琴', threeIsUniqueOpened: '菜单手风琴',

View File

@ -71,7 +71,8 @@ export default {
personal: '個人中心', personal: '個人中心',
tools: '工具類集合', tools: '工具類集合',
layoutLinkView: '外鏈', layoutLinkView: '外鏈',
layoutIfameView: '内嵌 iframe', layoutIframeViewOne: '内嵌 iframe1',
layoutIframeViewTwo: '内嵌 iframe2',
}, },
staticRoutes: { staticRoutes: {
signIn: '登入', signIn: '登入',
@ -139,6 +140,7 @@ export default {
twoColumnsMenuBar: '分欄選單背景', twoColumnsMenuBar: '分欄選單背景',
twoColumnsMenuBarColor: '分欄選單默認字體顏色', twoColumnsMenuBarColor: '分欄選單默認字體顏色',
twoIsColumnsMenuBarColorGradual: '分欄選單背景漸變', twoIsColumnsMenuBarColorGradual: '分欄選單背景漸變',
twoIsColumnsMenuHoverPreload: '分欄選單滑鼠懸停預加載',
threeTitle: '介面設定', threeTitle: '介面設定',
threeIsCollapse: '選單水准折疊', threeIsCollapse: '選單水准折疊',
threeIsUniqueOpened: '選單手風琴', threeIsUniqueOpened: '選單手風琴',

View File

@ -10,20 +10,22 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { toRefs, reactive, computed, watch, getCurrentInstance, onBeforeMount, defineComponent } from 'vue'; import { defineAsyncComponent, toRefs, reactive, computed, watch, onBeforeMount, defineComponent, ref } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import pinia from '/@/stores/index'; import pinia from '/@/stores/index';
import { useRoutesList } from '/@/stores/routesList'; import { useRoutesList } from '/@/stores/routesList';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes'; import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import Logo from '/@/layout/logo/index.vue'; import mittBus from '/@/utils/mitt';
import Vertical from '/@/layout/navMenu/vertical.vue';
export default defineComponent({ export default defineComponent({
name: 'layoutAside', name: 'layoutAside',
components: { Logo, Vertical }, components: {
Logo: defineAsyncComponent(() => import('/@/layout/logo/index.vue')),
Vertical: defineAsyncComponent(() => import('/@/layout/navMenu/vertical.vue')),
},
setup() { setup() {
const { proxy } = <any>getCurrentInstance(); const layoutAsideScrollbarRef = ref();
const stores = useRoutesList(); const stores = useRoutesList();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const storesTagsViewRoutes = useTagsViewRoutes(); const storesTagsViewRoutes = useTagsViewRoutes();
@ -105,14 +107,13 @@ export default defineComponent({
const onAsideEnterLeave = (bool: Boolean) => { const onAsideEnterLeave = (bool: Boolean) => {
let { layout } = themeConfig.value; let { layout } = themeConfig.value;
if (layout !== 'columns') return false; if (layout !== 'columns') return false;
if (!bool) proxy.mittBus.emit('restoreDefault'); if (!bool) mittBus.emit('restoreDefault');
stores.setColumnsMenuHover(bool); stores.setColumnsMenuHover(bool);
}; };
// themeConfig el-scrollbar // themeConfig el-scrollbar
watch(themeConfig.value, (val) => { watch(themeConfig.value, (val) => {
if (val.isShowLogoChange !== val.isShowLogo) { if (val.isShowLogoChange !== val.isShowLogo) {
if (!proxy.$refs.layoutAsideScrollbarRef) return false; if (layoutAsideScrollbarRef.value) layoutAsideScrollbarRef.value.update();
proxy.$refs.layoutAsideScrollbarRef.update();
} }
}); });
// vuex // vuex
@ -131,27 +132,28 @@ export default defineComponent({
onBeforeMount(() => { onBeforeMount(() => {
initMenuFixed(document.body.clientWidth); initMenuFixed(document.body.clientWidth);
setFilterRoutes(); setFilterRoutes();
// (proxy.mittBus.off('setSendColumnsChildren)) // (mittBus.off('setSendColumnsChildren))
// 使 // 使
proxy.mittBus.on('setSendColumnsChildren', (res: any) => { mittBus.on('setSendColumnsChildren', (res: any) => {
state.menuList = res.children; state.menuList = res.children;
}); });
proxy.mittBus.on('setSendClassicChildren', (res: any) => { mittBus.on('setSendClassicChildren', (res: any) => {
let { layout, isClassicSplitMenu } = themeConfig.value; let { layout, isClassicSplitMenu } = themeConfig.value;
if (layout === 'classic' && isClassicSplitMenu) { if (layout === 'classic' && isClassicSplitMenu) {
state.menuList = []; state.menuList = [];
state.menuList = res.children; state.menuList = res.children;
} }
}); });
proxy.mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => { mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
setFilterRoutes(); setFilterRoutes();
}); });
proxy.mittBus.on('layoutMobileResize', (res: any) => { mittBus.on('layoutMobileResize', (res: any) => {
initMenuFixed(res.clientWidth); initMenuFixed(res.clientWidth);
closeLayoutAsideMobileMode(); closeLayoutAsideMobileMode();
}); });
}); });
return { return {
layoutAsideScrollbarRef,
setCollapseStyle, setCollapseStyle,
setShowLogo, setShowLogo,
isTagsViewCurrenFull, isTagsViewCurrenFull,

View File

@ -45,12 +45,13 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { reactive, toRefs, ref, onMounted, nextTick, getCurrentInstance, watch, onUnmounted, defineComponent } from 'vue'; import { reactive, toRefs, ref, onMounted, nextTick, watch, onUnmounted, defineComponent } from 'vue';
import { useRoute, useRouter, onBeforeRouteUpdate, RouteRecordRaw } from 'vue-router'; import { useRoute, useRouter, onBeforeRouteUpdate, RouteRecordRaw } from 'vue-router';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import pinia from '/@/stores/index'; import pinia from '/@/stores/index';
import { useRoutesList } from '/@/stores/routesList'; import { useRoutesList } from '/@/stores/routesList';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import mittBus from '/@/utils/mitt';
// //
interface ColumnsAsideState { interface ColumnsAsideState {
@ -68,7 +69,6 @@ export default defineComponent({
setup() { setup() {
const columnsAsideOffsetTopRefs: any = ref([]); const columnsAsideOffsetTopRefs: any = ref([]);
const columnsAsideActiveRef = ref(); const columnsAsideActiveRef = ref();
const { proxy } = <any>getCurrentInstance();
const stores = useRoutesList(); const stores = useRoutesList();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { routesList, isColumnsMenuHover, isColumnsNavHover } = storeToRefs(stores); const { routesList, isColumnsMenuHover, isColumnsNavHover } = storeToRefs(stores);
@ -98,11 +98,12 @@ export default defineComponent({
}; };
// //
const onColumnsAsideMenuMouseenter = (v: RouteRecordRaw, k: number) => { const onColumnsAsideMenuMouseenter = (v: RouteRecordRaw, k: number) => {
if (!themeConfig.value.isColumnsMenuHoverPreload) return false;
let { path } = v; let { path } = v;
state.liOldPath = path; state.liOldPath = path;
state.liOldIndex = k; state.liOldIndex = k;
state.liHoverIndex = k; state.liHoverIndex = k;
proxy.mittBus.emit('setSendColumnsChildren', setSendChildren(path)); mittBus.emit('setSendColumnsChildren', setSendChildren(path));
stores.setColumnsMenuHover(false); stores.setColumnsMenuHover(false);
stores.setColumnsNavHover(true); stores.setColumnsNavHover(true);
}; };
@ -111,7 +112,7 @@ export default defineComponent({
await stores.setColumnsNavHover(false); await stores.setColumnsNavHover(false);
// store.state.routesList // store.state.routesList
setTimeout(() => { setTimeout(() => {
if (!isColumnsMenuHover && !isColumnsNavHover) proxy.mittBus.emit('restoreDefault'); if (!isColumnsMenuHover && !isColumnsNavHover) mittBus.emit('restoreDefault');
}, 100); }, 100);
}; };
// //
@ -126,7 +127,7 @@ export default defineComponent({
const resData: any = setSendChildren(route.path); const resData: any = setSendChildren(route.path);
if (Object.keys(resData).length <= 0) return false; if (Object.keys(resData).length <= 0) return false;
onColumnsAsideDown(resData.item[0].k); onColumnsAsideDown(resData.item[0].k);
proxy.mittBus.emit('setSendColumnsChildren', resData); mittBus.emit('setSendColumnsChildren', resData);
}; };
// //
const setSendChildren = (path: string) => { const setSendChildren = (path: string) => {
@ -171,11 +172,11 @@ export default defineComponent({
val.themeConfig.themeConfig.columnsAsideStyle === 'columnsRound' ? (state.difference = 3) : (state.difference = 0); val.themeConfig.themeConfig.columnsAsideStyle === 'columnsRound' ? (state.difference = 3) : (state.difference = 0);
if (!val.routesList.isColumnsMenuHover && !val.routesList.isColumnsNavHover) { if (!val.routesList.isColumnsMenuHover && !val.routesList.isColumnsNavHover) {
state.liHoverIndex = null; state.liHoverIndex = null;
proxy.mittBus.emit('setSendColumnsChildren', setSendChildren(route.path)); mittBus.emit('setSendColumnsChildren', setSendChildren(route.path));
} else { } else {
state.liHoverIndex = state.liOldIndex; state.liHoverIndex = state.liOldIndex;
if (!state.liOldPath) return false; if (!state.liOldPath) return false;
proxy.mittBus.emit('setSendColumnsChildren', setSendChildren(state.liOldPath)); mittBus.emit('setSendColumnsChildren', setSendChildren(state.liOldPath));
} }
}, },
{ {
@ -186,19 +187,19 @@ export default defineComponent({
onMounted(() => { onMounted(() => {
setFilterRoutes(); setFilterRoutes();
// //
proxy.mittBus.on('restoreDefault', () => { mittBus.on('restoreDefault', () => {
state.liOldIndex = null; state.liOldIndex = null;
state.liOldPath = null; state.liOldPath = null;
}); });
}); });
// //
onUnmounted(() => { onUnmounted(() => {
proxy.mittBus.off('restoreDefault', () => {}); mittBus.off('restoreDefault', () => {});
}); });
// //
onBeforeRouteUpdate((to) => { onBeforeRouteUpdate((to) => {
setColumnsMenuHighlight(to.path); setColumnsMenuHighlight(to.path);
proxy.mittBus.emit('setSendColumnsChildren', setSendChildren(to.path)); mittBus.emit('setSendColumnsChildren', setSendChildren(to.path));
}); });
return { return {
themeConfig, themeConfig,
@ -221,6 +222,16 @@ export default defineComponent({
background: var(--next-bg-columnsMenuBar); background: var(--next-bg-columnsMenuBar);
ul { ul {
position: relative; position: relative;
.layout-columns-active {
color: var(--next-bg-columnsMenuBarColor) !important;
transition: 0.3s ease-in-out;
}
.layout-columns-hover {
color: var(--el-color-primary);
a {
color: var(--el-color-primary);
}
}
li { li {
color: var(--next-bg-columnsMenuBarColor); color: var(--next-bg-columnsMenuBarColor);
width: 100%; width: 100%;
@ -230,6 +241,9 @@ export default defineComponent({
cursor: pointer; cursor: pointer;
position: relative; position: relative;
z-index: 1; z-index: 1;
&:hover {
@extend .layout-columns-hover;
}
.columns-vertical { .columns-vertical {
margin: auto; margin: auto;
.columns-vertical-title { .columns-vertical-title {
@ -257,16 +271,6 @@ export default defineComponent({
color: var(--next-bg-columnsMenuBarColor); color: var(--next-bg-columnsMenuBarColor);
} }
} }
.layout-columns-active {
color: var(--next-bg-columnsMenuBarColor) !important;
transition: 0.3s ease-in-out;
}
.layout-columns-hover {
color: var(--el-color-primary);
a {
color: var(--el-color-primary);
}
}
.columns-round { .columns-round {
background: var(--el-color-primary); background: var(--el-color-primary);
color: var(--el-color-white); color: var(--el-color-white);

View File

@ -1,32 +1,23 @@
<template> <template>
<el-header class="layout-header" :height="setHeaderHeight" v-show="!isTagsViewCurrenFull"> <el-header class="layout-header" v-show="!isTagsViewCurrenFull">
<NavBarsIndex /> <NavBarsIndex />
</el-header> </el-header>
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent } from 'vue'; import { defineAsyncComponent, defineComponent } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes'; import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import NavBarsIndex from '/@/layout/navBars/index.vue';
export default defineComponent({ export default defineComponent({
name: 'layoutHeader', name: 'layoutHeader',
components: { NavBarsIndex }, components: {
NavBarsIndex: defineAsyncComponent(() => import('/@/layout/navBars/index.vue')),
},
setup() { setup() {
const storesTagsViewRoutes = useTagsViewRoutes(); const storesTagsViewRoutes = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes); const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
// header
const setHeaderHeight = computed(() => {
let { isTagsview, layout } = themeConfig.value;
if (isTagsview && layout !== 'classic') return '84px';
else return '50px';
});
return { return {
setHeaderHeight,
isTagsViewCurrenFull, isTagsViewCurrenFull,
}; };
}, },

View File

@ -1,100 +1,58 @@
<template> <template>
<el-main class="layout-main"> <el-main class="layout-main" :style="isFixedHeader ? `height: calc(100% - ${setMainHeight})` : `minHeight: calc(100% - ${setMainHeight})`">
<el-scrollbar <el-scrollbar ref="layoutMainScrollbarRef" class="layout-main-scroll" wrap-class="layout-main-scroll" view-class="layout-main-scroll">
ref="layoutScrollbarRef" <LayoutParentView />
:class="{ <LayoutFooter v-if="isFooter" />
'layout-scrollbar':
(!isClassicOrTransverse && !currentRouteMeta.isLink && !currentRouteMeta.isIframe) ||
(!isClassicOrTransverse && currentRouteMeta.isLink && !currentRouteMeta.isIframe),
}"
>
<LayoutParentView
:style="{
padding: !isClassicOrTransverse || (currentRouteMeta.isLink && currentRouteMeta.isIframe) ? '0' : '15px',
transition: 'padding 0.3s ease-in-out',
}"
/>
<Footer v-if="themeConfig.isFooter" />
</el-scrollbar> </el-scrollbar>
<el-backtop target=".layout-backtop .el-scrollbar__wrap" />
</el-main> </el-main>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, toRefs, reactive, getCurrentInstance, watch, onMounted, computed } from 'vue'; import { defineAsyncComponent, defineComponent, onMounted, computed, ref } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import { NextLoading } from '/@/utils/loading'; import { NextLoading } from '/@/utils/loading';
import LayoutParentView from '/@/layout/routerView/parent.vue';
import Footer from '/@/layout/footer/index.vue';
//
interface MainState {
headerHeight: string | number;
currentRouteMeta: any;
}
export default defineComponent({ export default defineComponent({
name: 'layoutMain', name: 'layoutMain',
components: { LayoutParentView, Footer }, components: {
LayoutParentView: defineAsyncComponent(() => import('/@/layout/routerView/parent.vue')),
LayoutFooter: defineAsyncComponent(() => import('/@/layout/footer/index.vue')),
},
setup() { setup() {
const { proxy } = <any>getCurrentInstance(); const layoutMainScrollbarRef = ref('');
const route = useRoute();
const storesTagsViewRoutes = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);
const route = useRoute(); const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
const state = reactive<MainState>({ // footer /
headerHeight: '', const isFooter = computed(() => {
currentRouteMeta: {}, return themeConfig.value.isFooter && !route.meta.isIframe;
}); });
// // header
const isClassicOrTransverse = computed(() => { const isFixedHeader = computed(() => {
const { layout } = themeConfig.value; return themeConfig.value.isFixedHeader;
return layout === 'classic' || layout === 'transverse'; });
//
const setMainHeight = computed(() => {
if (isTagsViewCurrenFull.value) return '0px';
const { isTagsview, layout } = themeConfig.value;
if (isTagsview && layout !== 'classic') return '85px';
else return '51px';
}); });
// main
const initHeaderHeight = () => {
const bool = state.currentRouteMeta.isLink && state.currentRouteMeta.isIframe;
let { isTagsview } = themeConfig.value;
if (isTagsview) return (state.headerHeight = bool ? `86px` : `115px`);
else return (state.headerHeight = `80px`);
};
// meta iframes padding
const initGetMeta = () => {
state.currentRouteMeta = route.meta;
};
// //
onMounted(async () => { onMounted(() => {
await initGetMeta(); NextLoading.done(600);
initHeaderHeight();
NextLoading.done();
}); });
//
watch(
() => route.path,
() => {
state.currentRouteMeta = route.meta;
const bool = state.currentRouteMeta.isLink && state.currentRouteMeta.isIframe;
state.headerHeight = bool ? `86px` : `115px`;
proxy.$refs.layoutScrollbarRef.update();
}
);
// themeConfig el-scrollbar
watch(
themeConfig,
(val) => {
state.currentRouteMeta = route.meta;
const bool = state.currentRouteMeta.isLink && state.currentRouteMeta.isIframe;
state.headerHeight = val.isTagsview ? (bool ? `86px` : `115px`) : '51px';
proxy.$refs?.layoutScrollbarRef?.update();
},
{
deep: true,
}
);
return { return {
themeConfig, layoutMainScrollbarRef,
isClassicOrTransverse, isFooter,
...toRefs(state), isFixedHeader,
setMainHeight,
}; };
}, },
}); });

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="layout-footer mt15" v-show="isDelayFooter"> <div class="layout-footer pb15">
<div class="layout-footer-warp"> <div class="layout-footer-warp">
<div>vue-next-adminMade by lyt with </div> <div>vue-next-adminMade by lyt with </div>
<div class="mt5">深圳市 xxx 公司版权所有</div> <div class="mt5">深圳市 xxx 公司版权所有</div>
@ -8,28 +8,10 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { toRefs, reactive, defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { onBeforeRouteUpdate } from 'vue-router';
export default defineComponent({ export default defineComponent({
name: 'layoutFooter', name: 'layoutFooter',
setup() {
const state = reactive({
isDelayFooter: true,
});
// footer
onBeforeRouteUpdate(() => {
setTimeout(() => {
state.isDelayFooter = false;
setTimeout(() => {
state.isDelayFooter = true;
}, 800);
}, 0);
});
return {
...toRefs(state),
};
},
}); });
</script> </script>
@ -41,7 +23,7 @@ export default defineComponent({
margin: auto; margin: auto;
color: var(--el-text-color-secondary); color: var(--el-text-color-secondary);
text-align: center; text-align: center;
animation: error-num 1s ease-in-out; animation: error-num 0.3s ease;
} }
} }
</style> </style>

View File

@ -3,10 +3,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { onBeforeMount, onUnmounted, getCurrentInstance, defineComponent, defineAsyncComponent } from 'vue'; import { onBeforeMount, onUnmounted, defineComponent, defineAsyncComponent } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import { Local } from '/@/utils/storage'; import { Local } from '/@/utils/storage';
import mittBus from '/@/utils/mitt';
export default defineComponent({ export default defineComponent({
name: 'layout', name: 'layout',
@ -17,7 +18,6 @@ export default defineComponent({
columns: defineAsyncComponent(() => import('/@/layout/main/columns.vue')), columns: defineAsyncComponent(() => import('/@/layout/main/columns.vue')),
}, },
setup() { setup() {
const { proxy } = <any>getCurrentInstance();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);
// () // ()
@ -26,12 +26,12 @@ export default defineComponent({
const clientWidth = document.body.clientWidth; const clientWidth = document.body.clientWidth;
if (clientWidth < 1000) { if (clientWidth < 1000) {
themeConfig.value.isCollapse = false; themeConfig.value.isCollapse = false;
proxy.mittBus.emit('layoutMobileResize', { mittBus.emit('layoutMobileResize', {
layout: 'defaults', layout: 'defaults',
clientWidth, clientWidth,
}); });
} else { } else {
proxy.mittBus.emit('layoutMobileResize', { mittBus.emit('layoutMobileResize', {
layout: Local.get('oldLayout') ? Local.get('oldLayout') : themeConfig.value.layout, layout: Local.get('oldLayout') ? Local.get('oldLayout') : themeConfig.value.layout,
clientWidth, clientWidth,
}); });

View File

@ -60,7 +60,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { nextTick, onMounted, reactive, toRefs, ref, onUnmounted, getCurrentInstance, defineComponent } from 'vue'; import { nextTick, onMounted, reactive, toRefs, ref, onUnmounted, defineComponent } from 'vue';
import { formatDate } from '/@/utils/formatTime'; import { formatDate } from '/@/utils/formatTime';
import { Local } from '/@/utils/storage'; import { Local } from '/@/utils/storage';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
@ -88,7 +88,7 @@ interface LockScreenState {
export default defineComponent({ export default defineComponent({
name: 'layoutLockScreen', name: 'layoutLockScreen',
setup() { setup() {
const { proxy } = <any>getCurrentInstance(); const layoutLockScreenDateRef = ref();
const layoutLockScreenInputRef = ref(); const layoutLockScreenInputRef = ref();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);
@ -150,7 +150,7 @@ export default defineComponent({
// //
const initGetElement = () => { const initGetElement = () => {
nextTick(() => { nextTick(() => {
state.querySelectorEl = proxy.$refs.layoutLockScreenDateRef; state.querySelectorEl = layoutLockScreenDateRef.value;
}); });
}; };
// //
@ -204,6 +204,7 @@ export default defineComponent({
window.clearInterval(state.isShowLockScreenIntervalTime); window.clearInterval(state.isShowLockScreenIntervalTime);
}); });
return { return {
layoutLockScreenDateRef,
layoutLockScreenInputRef, layoutLockScreenInputRef,
onDown, onDown,
onMove, onMove,

View File

@ -1,34 +1,76 @@
<template> <template>
<el-container class="layout-container flex-center"> <el-container class="layout-container flex-center">
<Header /> <LayoutHeader />
<el-container class="layout-mian-height-50"> <el-container class="layout-mian-height-50">
<Aside /> <LayoutAside />
<div class="flex-center layout-backtop"> <div class="flex-center layout-backtop">
<TagsView v-if="themeConfig.isTagsview" /> <LayoutTagsView v-if="isTagsview" />
<Main /> <LayoutMain ref="layoutMainRef" />
</div> </div>
</el-container> </el-container>
<el-backtop target=".layout-backtop .el-main .el-scrollbar__wrap"></el-backtop>
</el-container> </el-container>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineAsyncComponent, defineComponent, computed, ref, watch, nextTick, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import Aside from '/@/layout/component/aside.vue';
import Header from '/@/layout/component/header.vue';
import Main from '/@/layout/component/main.vue';
import TagsView from '/@/layout/navBars/tagsView/tagsView.vue';
export default defineComponent({ export default defineComponent({
name: 'layoutClassic', name: 'layoutClassic',
components: { Aside, Header, Main, TagsView }, components: {
LayoutAside: defineAsyncComponent(() => import('/@/layout/component/aside.vue')),
LayoutHeader: defineAsyncComponent(() => import('/@/layout/component/header.vue')),
LayoutMain: defineAsyncComponent(() => import('/@/layout/component/main.vue')),
LayoutTagsView: defineAsyncComponent(() => import('/@/layout/navBars/tagsView/tagsView.vue')),
},
setup() { setup() {
const layoutMainRef = ref<any>('');
const route = useRoute();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);
return { // tasgview
const isTagsview = computed(() => {
return themeConfig.value.isTagsview;
});
// scrollbar
const updateScrollbar = () => {
layoutMainRef.value.layoutMainScrollbarRef.update();
};
//
const initScrollBarHeight = () => {
nextTick(() => {
setTimeout(() => {
updateScrollbar();
layoutMainRef.value.layoutMainScrollbarRef.wrap$.scrollTop = 0;
}, 500);
});
};
//
watch(
() => route.path,
() => {
initScrollBarHeight();
}
);
// themeConfig el-scrollbar
watch(
themeConfig, themeConfig,
() => {
updateScrollbar();
},
{
deep: true,
}
);
//
onMounted(() => {
initScrollBarHeight();
});
return {
layoutMainRef,
isTagsview,
}; };
}, },
}); });

View File

@ -1,40 +1,77 @@
<template> <template>
<el-container class="layout-container"> <el-container class="layout-container">
<ColumnsAside /> <ColumnsAside />
<div class="layout-columns-warp"> <el-container class="layout-columns-warp layout-container-view h100">
<Aside /> <LayoutAside />
<el-container class="flex-center layout-backtop" :class="{ 'layout-backtop': !isFixedHeader }"> <el-scrollbar ref="layoutScrollbarRef" class="layout-backtop">
<Header v-if="isFixedHeader" /> <LayoutHeader />
<el-scrollbar :class="{ 'layout-backtop': isFixedHeader }"> <LayoutMain ref="layoutMainRef" />
<Header v-if="!isFixedHeader" /> </el-scrollbar>
<Main /> </el-container>
</el-scrollbar>
</el-container>
</div>
<el-backtop target=".layout-backtop .el-scrollbar__wrap"></el-backtop>
</el-container> </el-container>
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent } from 'vue'; import { defineAsyncComponent, watch, defineComponent, onMounted, nextTick, ref } from 'vue';
import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import Aside from '/@/layout/component/aside.vue';
import Header from '/@/layout/component/header.vue';
import Main from '/@/layout/component/main.vue';
import ColumnsAside from '/@/layout/component/columnsAside.vue';
export default defineComponent({ export default defineComponent({
name: 'layoutColumns', name: 'layoutColumns',
components: { Aside, Header, Main, ColumnsAside }, components: {
LayoutAside: defineAsyncComponent(() => import('/@/layout/component/aside.vue')),
LayoutHeader: defineAsyncComponent(() => import('/@/layout/component/header.vue')),
LayoutMain: defineAsyncComponent(() => import('/@/layout/component/main.vue')),
ColumnsAside: defineAsyncComponent(() => import('/@/layout/component/columnsAside.vue')),
},
setup() { setup() {
const layoutScrollbarRef = ref<any>('');
const layoutMainRef = ref<any>('');
const route = useRoute();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);
const isFixedHeader = computed(() => { //
return themeConfig.value.isFixedHeader; const updateScrollbar = () => {
// scrollbar
layoutScrollbarRef.value.update();
// scrollbar
layoutMainRef.value.layoutMainScrollbarRef.update();
};
//
const initScrollBarHeight = () => {
nextTick(() => {
setTimeout(() => {
updateScrollbar();
layoutScrollbarRef.value.wrap$.scrollTop = 0;
layoutMainRef.value.layoutMainScrollbarRef.wrap$.scrollTop = 0;
}, 500);
});
};
//
watch(
() => route.path,
() => {
initScrollBarHeight();
}
);
// themeConfig el-scrollbar
watch(
themeConfig,
() => {
updateScrollbar();
},
{
deep: true,
}
);
//
onMounted(() => {
initScrollBarHeight();
}); });
return { return {
isFixedHeader, layoutScrollbarRef,
layoutMainRef,
}; };
}, },
}); });

View File

@ -1,46 +1,77 @@
<template> <template>
<el-container class="layout-container"> <el-container class="layout-container">
<Aside /> <LayoutAside />
<el-container class="flex-center" :class="{ 'layout-backtop': !isFixedHeader }"> <el-container class="layout-container-view h100">
<Header v-if="isFixedHeader" /> <el-scrollbar ref="layoutScrollbarRef" class="layout-backtop">
<el-scrollbar ref="layoutDefaultsScrollbarRef" :class="{ 'layout-backtop': isFixedHeader }"> <LayoutHeader />
<Header v-if="!isFixedHeader" /> <LayoutMain ref="layoutMainRef" />
<Main />
</el-scrollbar> </el-scrollbar>
</el-container> </el-container>
<el-backtop target=".layout-backtop .el-scrollbar__wrap"></el-backtop>
</el-container> </el-container>
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, getCurrentInstance, watch, defineComponent } from 'vue'; import { defineAsyncComponent, watch, defineComponent, onMounted, nextTick, ref } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import Aside from '/@/layout/component/aside.vue'; import { NextLoading } from '/@/utils/loading';
import Header from '/@/layout/component/header.vue';
import Main from '/@/layout/component/main.vue';
export default defineComponent({ export default defineComponent({
name: 'layoutDefaults', name: 'layoutDefaults',
components: { Aside, Header, Main }, components: {
LayoutAside: defineAsyncComponent(() => import('/@/layout/component/aside.vue')),
LayoutHeader: defineAsyncComponent(() => import('/@/layout/component/header.vue')),
LayoutMain: defineAsyncComponent(() => import('/@/layout/component/main.vue')),
},
setup() { setup() {
const { proxy } = <any>getCurrentInstance(); const layoutScrollbarRef = ref<any>('');
const layoutMainRef = ref<any>('');
const route = useRoute(); const route = useRoute();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);
const isFixedHeader = computed(() => { //
return themeConfig.value.isFixedHeader; const updateScrollbar = () => {
}); // scrollbar
// layoutScrollbarRef.value.update();
// scrollbar
layoutMainRef.value.layoutMainScrollbarRef.update();
};
//
const initScrollBarHeight = () => {
nextTick(() => {
setTimeout(() => {
updateScrollbar();
layoutScrollbarRef.value.wrap$.scrollTop = 0;
layoutMainRef.value.layoutMainScrollbarRef.wrap$.scrollTop = 0;
}, 500);
});
};
//
watch( watch(
() => route.path, () => route.path,
() => { () => {
proxy.$refs.layoutDefaultsScrollbarRef.wrap$.scrollTop = 0; initScrollBarHeight();
} }
); );
// themeConfig el-scrollbar
watch(
themeConfig,
() => {
updateScrollbar();
},
{
deep: true,
}
);
//
onMounted(() => {
initScrollBarHeight();
NextLoading.done(600);
});
return { return {
isFixedHeader, layoutScrollbarRef,
layoutMainRef,
}; };
}, },
}); });

View File

@ -1,17 +1,64 @@
<template> <template>
<el-container class="layout-container flex-center layout-backtop"> <el-container class="layout-container flex-center layout-backtop">
<Header /> <LayoutHeader />
<Main /> <LayoutMain ref="layoutMainRef" />
<el-backtop target=".layout-backtop .el-main .el-scrollbar__wrap"></el-backtop>
</el-container> </el-container>
</template> </template>
<script lang="ts"> <script lang="ts">
import Header from '/@/layout/component/header.vue'; import { defineAsyncComponent, defineComponent, ref, watch, nextTick, onMounted } from 'vue';
import Main from '/@/layout/component/main.vue'; import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
export default { export default defineComponent({
name: 'layoutTransverse', name: 'layoutTransverse',
components: { Header, Main }, components: {
}; LayoutHeader: defineAsyncComponent(() => import('/@/layout/component/header.vue')),
LayoutMain: defineAsyncComponent(() => import('/@/layout/component/main.vue')),
},
setup() {
const layoutMainRef = ref<any>('');
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const route = useRoute();
// scrollbar
const updateScrollbar = () => {
layoutMainRef.value.layoutMainScrollbarRef.update();
};
//
const initScrollBarHeight = () => {
nextTick(() => {
setTimeout(() => {
updateScrollbar();
layoutMainRef.value.layoutMainScrollbarRef.wrap$.scrollTop = 0;
}, 500);
});
};
//
watch(
() => route.path,
() => {
initScrollBarHeight();
}
);
// themeConfig el-scrollbar
watch(
themeConfig,
() => {
updateScrollbar();
},
{
deep: true,
}
);
//
onMounted(() => {
initScrollBarHeight();
});
return {
layoutMainRef,
};
},
});
</script> </script>

View File

@ -8,15 +8,12 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, reactive, toRefs, onMounted, onUnmounted, getCurrentInstance, defineComponent } from 'vue'; import { defineAsyncComponent, computed, reactive, toRefs, onMounted, onUnmounted, defineComponent } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useRoutesList } from '/@/stores/routesList'; import { useRoutesList } from '/@/stores/routesList';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import Breadcrumb from '/@/layout/navBars/breadcrumb/breadcrumb.vue'; import mittBus from '/@/utils/mitt';
import User from '/@/layout/navBars/breadcrumb/user.vue';
import Logo from '/@/layout/logo/index.vue';
import Horizontal from '/@/layout/navMenu/horizontal.vue';
// //
interface IndexState { interface IndexState {
@ -25,9 +22,13 @@ interface IndexState {
export default defineComponent({ export default defineComponent({
name: 'layoutBreadcrumbIndex', name: 'layoutBreadcrumbIndex',
components: { Breadcrumb, User, Logo, Horizontal }, components: {
Breadcrumb: defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/breadcrumb.vue')),
User: defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/user.vue')),
Logo: defineAsyncComponent(() => import('/@/layout/logo/index.vue')),
Horizontal: defineAsyncComponent(() => import('/@/layout/navMenu/horizontal.vue')),
},
setup() { setup() {
const { proxy } = <any>getCurrentInstance();
const stores = useRoutesList(); const stores = useRoutesList();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);
@ -52,7 +53,7 @@ export default defineComponent({
if (layout === 'classic' && isClassicSplitMenu) { if (layout === 'classic' && isClassicSplitMenu) {
state.menuList = delClassicChildren(filterRoutesFun(routesList.value)); state.menuList = delClassicChildren(filterRoutesFun(routesList.value));
const resData = setSendClassicChildren(route.path); const resData = setSendClassicChildren(route.path);
proxy.mittBus.emit('setSendClassicChildren', resData); mittBus.emit('setSendClassicChildren', resData);
} else { } else {
state.menuList = filterRoutesFun(routesList.value); state.menuList = filterRoutesFun(routesList.value);
} }
@ -91,13 +92,13 @@ export default defineComponent({
// //
onMounted(() => { onMounted(() => {
setFilterRoutes(); setFilterRoutes();
proxy.mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => { mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
setFilterRoutes(); setFilterRoutes();
}); });
}); });
// //
onUnmounted(() => { onUnmounted(() => {
proxy.mittBus.off('getBreadcrumbIndexSetFilterRoutes', () => {}); mittBus.off('getBreadcrumbIndexSetFilterRoutes', () => {});
}); });
return { return {
setIsShowLogo, setIsShowLogo,

View File

@ -1,26 +1,28 @@
<template> <template>
<div class="layout-search-dialog"> <div class="layout-search-dialog">
<el-dialog v-model="isShowSearch" width="300px" destroy-on-close :modal="false" fullscreen :show-close="false"> <el-dialog v-model="isShowSearch" destroy-on-close :show-close="false">
<el-autocomplete <template #footer>
v-model="menuQuery" <el-autocomplete
:fetch-suggestions="menuSearch" v-model="menuQuery"
:placeholder="$t('message.user.searchPlaceholder')" :fetch-suggestions="menuSearch"
ref="layoutMenuAutocompleteRef" :placeholder="$t('message.user.searchPlaceholder')"
@select="onHandleSelect" ref="layoutMenuAutocompleteRef"
@blur="onSearchBlur" @select="onHandleSelect"
> :fit-input-width="true"
<template #prefix> >
<el-icon class="el-input__icon"> <template #prefix>
<ele-Search /> <el-icon class="el-input__icon">
</el-icon> <ele-Search />
</template> </el-icon>
<template #default="{ item }"> </template>
<div> <template #default="{ item }">
<SvgIcon :name="item.meta.icon" class="mr5" /> <div>
{{ $t(item.meta.title) }} <SvgIcon :name="item.meta.icon" class="mr5" />
</div> {{ $t(item.meta.title) }}
</template> </div>
</el-autocomplete> </template>
</el-autocomplete>
</template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
@ -103,17 +105,12 @@ export default defineComponent({
else router.push(path); else router.push(path);
closeSearch(); closeSearch();
}; };
// input
const onSearchBlur = () => {
closeSearch();
};
return { return {
layoutMenuAutocompleteRef, layoutMenuAutocompleteRef,
openSearch, openSearch,
closeSearch, closeSearch,
menuSearch, menuSearch,
onHandleSelect, onHandleSelect,
onSearchBlur,
...toRefs(state), ...toRefs(state),
}; };
}, },
@ -122,15 +119,23 @@ export default defineComponent({
<style scoped lang="scss"> <style scoped lang="scss">
.layout-search-dialog { .layout-search-dialog {
position: relative;
:deep(.el-dialog) { :deep(.el-dialog) {
box-shadow: unset !important; .el-dialog__header,
border-radius: 0 !important; .el-dialog__body {
background: rgba(0, 0, 0, 0.5); display: none;
}
.el-dialog__footer {
position: absolute;
left: 50%;
transform: translateX(-50%);
top: -53vh;
}
} }
:deep(.el-autocomplete) { :deep(.el-autocomplete) {
width: 560px; width: 560px;
position: absolute; position: absolute;
top: 100px; top: 150px;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
} }

View File

@ -105,6 +105,17 @@
></el-switch> ></el-switch>
</div> </div>
</div> </div>
<div class="layout-breadcrumb-seting-bar-flex mt14" :style="{ opacity: getThemeConfig.layout !== 'columns' ? 0.5 : 1 }">
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.twoIsColumnsMenuHoverPreload') }}</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch
v-model="getThemeConfig.isColumnsMenuHoverPreload"
size="small"
@change="onColumnsMenuHoverPreloadChange"
:disabled="getThemeConfig.layout !== 'columns'"
></el-switch>
</div>
</div>
<!-- 界面设置 --> <!-- 界面设置 -->
<el-divider content-position="left">{{ $t('message.layout.threeTitle') }}</el-divider> <el-divider content-position="left">{{ $t('message.layout.threeTitle') }}</el-divider>
@ -408,8 +419,9 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { nextTick, onUnmounted, onMounted, getCurrentInstance, defineComponent, computed, reactive, toRefs } from 'vue'; import { nextTick, onUnmounted, onMounted, defineComponent, computed, reactive, toRefs } from 'vue';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import { getLightColor, getDarkColor } from '/@/utils/theme'; import { getLightColor, getDarkColor } from '/@/utils/theme';
@ -418,11 +430,12 @@ import { Local } from '/@/utils/storage';
import Watermark from '/@/utils/wartermark'; import Watermark from '/@/utils/wartermark';
import commonFunction from '/@/utils/commonFunction'; import commonFunction from '/@/utils/commonFunction';
import other from '/@/utils/other'; import other from '/@/utils/other';
import mittBus from '/@/utils/mitt';
export default defineComponent({ export default defineComponent({
name: 'layoutBreadcrumbSeting', name: 'layoutBreadcrumbSeting',
setup() { setup() {
const { proxy } = <any>getCurrentInstance(); const { locale } = useI18n();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);
const { copyText } = commonFunction(); const { copyText } = commonFunction();
@ -479,6 +492,11 @@ export default defineComponent({
setLocalThemeConfig(); setLocalThemeConfig();
}, 200); }, 200);
}; };
// 2 ->
const onColumnsMenuHoverPreloadChange = () => {
mittBus.emit('setHoverPreload');
setLocalThemeConfig();
};
// 3 --> // 3 -->
const onThemeConfigChange = () => { const onThemeConfigChange = () => {
setDispatchThemeConfig(); setDispatchThemeConfig();
@ -492,7 +510,7 @@ export default defineComponent({
const onClassicSplitMenuChange = () => { const onClassicSplitMenuChange = () => {
getThemeConfig.value.isBreadcrumb = false; getThemeConfig.value.isBreadcrumb = false;
setLocalThemeConfig(); setLocalThemeConfig();
proxy.mittBus.emit('getBreadcrumbIndexSetFilterRoutes'); mittBus.emit('getBreadcrumbIndexSetFilterRoutes');
}; };
// 4 --> Logo // 4 --> Logo
const onIsShowLogoChange = () => { const onIsShowLogoChange = () => {
@ -508,12 +526,12 @@ export default defineComponent({
}; };
// 4 --> TagsView // 4 --> TagsView
const onSortableTagsViewChange = () => { const onSortableTagsViewChange = () => {
proxy.mittBus.emit('openOrCloseSortable'); mittBus.emit('openOrCloseSortable');
setLocalThemeConfig(); setLocalThemeConfig();
}; };
// 4 --> TagsView // 4 --> TagsView
const onShareTagsViewChange = () => { const onShareTagsViewChange = () => {
proxy.mittBus.emit('openShareTagsView'); mittBus.emit('openShareTagsView');
setLocalThemeConfig(); setLocalThemeConfig();
}; };
// 4 --> / // 4 --> /
@ -565,7 +583,7 @@ export default defineComponent({
onBgColorPickerChange('columnsMenuBar'); onBgColorPickerChange('columnsMenuBar');
onBgColorPickerChange('columnsMenuBarColor'); onBgColorPickerChange('columnsMenuBarColor');
}; };
// proxy.$refs.layoutScrollbarRef.update() // layoutScrollbarRef.value.update()
const onDrawerClose = () => { const onDrawerClose = () => {
getThemeConfig.value.isFixedHeaderChange = false; getThemeConfig.value.isFixedHeaderChange = false;
getThemeConfig.value.isShowLogoChange = false; getThemeConfig.value.isShowLogoChange = false;
@ -618,7 +636,7 @@ export default defineComponent({
if (!Local.get('frequency')) initLayoutChangeFun(); if (!Local.get('frequency')) initLayoutChangeFun();
Local.set('frequency', 1); Local.set('frequency', 1);
// //
proxy.mittBus.on('layoutMobileResize', (res: any) => { mittBus.on('layoutMobileResize', (res: any) => {
getThemeConfig.value.layout = res.layout; getThemeConfig.value.layout = res.layout;
getThemeConfig.value.isDrawer = false; getThemeConfig.value.isDrawer = false;
initLayoutChangeFun(); initLayoutChangeFun();
@ -636,14 +654,14 @@ export default defineComponent({
// //
onWartermarkChange(); onWartermarkChange();
// //
if (Local.get('themeConfig')) proxy.$i18n.locale = Local.get('themeConfig').globalI18n; if (Local.get('themeConfig')) locale.value = Local.get('themeConfig').globalI18n;
// //
initSetStyle(); initSetStyle();
}, 100); }, 100);
}); });
}); });
onUnmounted(() => { onUnmounted(() => {
proxy.mittBus.off('layoutMobileResize', () => {}); mittBus.off('layoutMobileResize', () => {});
}); });
return { return {
openDrawer, openDrawer,
@ -652,6 +670,7 @@ export default defineComponent({
onTopBarGradualChange, onTopBarGradualChange,
onMenuBarGradualChange, onMenuBarGradualChange,
onColumnsMenuBarGradualChange, onColumnsMenuBarGradualChange,
onColumnsMenuHoverPreloadChange,
onThemeConfigChange, onThemeConfigChange,
onIsFixedHeaderChange, onIsFixedHeaderChange,
onIsShowLogoChange, onIsShowLogoChange,

View File

@ -77,7 +77,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { ref, getCurrentInstance, computed, reactive, toRefs, onMounted, defineComponent } from 'vue'; import { defineAsyncComponent, ref, computed, reactive, toRefs, onMounted, defineComponent } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { ElMessageBox, ElMessage } from 'element-plus'; import { ElMessageBox, ElMessage } from 'element-plus';
import screenfull from 'screenfull'; import screenfull from 'screenfull';
@ -86,16 +86,17 @@ import { storeToRefs } from 'pinia';
import { useUserInfo } from '/@/stores/userInfo'; import { useUserInfo } from '/@/stores/userInfo';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import other from '/@/utils/other'; import other from '/@/utils/other';
import mittBus from '/@/utils/mitt';
import { Session, Local } from '/@/utils/storage'; import { Session, Local } from '/@/utils/storage';
import UserNews from '/@/layout/navBars/breadcrumb/userNews.vue';
import Search from '/@/layout/navBars/breadcrumb/search.vue';
export default defineComponent({ export default defineComponent({
name: 'layoutBreadcrumbUser', name: 'layoutBreadcrumbUser',
components: { UserNews, Search }, components: {
UserNews: defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/userNews.vue')),
Search: defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/search.vue')),
},
setup() { setup() {
const { t } = useI18n(); const { locale, t } = useI18n();
const { proxy } = <any>getCurrentInstance();
const router = useRouter(); const router = useRouter();
const stores = useUserInfo(); const stores = useUserInfo();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
@ -130,7 +131,7 @@ export default defineComponent({
}; };
// icon // icon
const onLayoutSetingClick = () => { const onLayoutSetingClick = () => {
proxy.mittBus.emit('openSetingsDrawer'); mittBus.emit('openSetingsDrawer');
}; };
// //
const onHandleCommandClick = (path: string) => { const onHandleCommandClick = (path: string) => {
@ -181,7 +182,7 @@ export default defineComponent({
Local.remove('themeConfig'); Local.remove('themeConfig');
themeConfig.value.globalComponentSize = size; themeConfig.value.globalComponentSize = size;
Local.set('themeConfig', themeConfig.value); Local.set('themeConfig', themeConfig.value);
initComponentSize(); initI18nOrSize('globalComponentSize', 'disabledSize');
window.location.reload(); window.location.reload();
}; };
// //
@ -189,50 +190,19 @@ export default defineComponent({
Local.remove('themeConfig'); Local.remove('themeConfig');
themeConfig.value.globalI18n = lang; themeConfig.value.globalI18n = lang;
Local.set('themeConfig', themeConfig.value); Local.set('themeConfig', themeConfig.value);
proxy.$i18n.locale = lang; locale.value = lang;
initI18n();
other.useTitle(); other.useTitle();
initI18nOrSize('globalI18n', 'disabledI18n');
}; };
// element plus // /i18n
const setI18nConfig = (locale: string) => { const initI18nOrSize = (value: string, attr: string) => {
proxy.mittBus.emit('getI18nConfig', proxy.$i18n.messages[locale]); (<any>state)[attr] = Local.get('themeConfig')[value];
};
//
const initI18n = () => {
switch (Local.get('themeConfig').globalI18n) {
case 'zh-cn':
state.disabledI18n = 'zh-cn';
setI18nConfig('zh-cn');
break;
case 'en':
state.disabledI18n = 'en';
setI18nConfig('en');
break;
case 'zh-tw':
state.disabledI18n = 'zh-tw';
setI18nConfig('zh-tw');
break;
}
};
//
const initComponentSize = () => {
switch (Local.get('themeConfig').globalComponentSize) {
case 'large':
state.disabledSize = 'large';
break;
case 'default':
state.disabledSize = 'default';
break;
case 'small':
state.disabledSize = 'small';
break;
}
}; };
// //
onMounted(() => { onMounted(() => {
if (Local.get('themeConfig')) { if (Local.get('themeConfig')) {
initI18n(); initI18nOrSize('globalComponentSize', 'disabledSize');
initComponentSize(); initI18nOrSize('globalI18n', 'disabledI18n');
} }
}); });
return { return {

View File

@ -6,15 +6,16 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent } from 'vue'; import { defineAsyncComponent, computed, defineComponent } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import BreadcrumbIndex from '/@/layout/navBars/breadcrumb/index.vue';
import TagsView from '/@/layout/navBars/tagsView/tagsView.vue';
export default defineComponent({ export default defineComponent({
name: 'layoutNavBars', name: 'layoutNavBars',
components: { BreadcrumbIndex, TagsView }, components: {
BreadcrumbIndex: defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/index.vue')),
TagsView: defineAsyncComponent(() => import('/@/layout/navBars/tagsView/tagsView.vue')),
},
setup() { setup() {
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);

View File

@ -9,6 +9,7 @@
:data-url="v.url" :data-url="v.url"
:class="{ 'is-active': isActive(v) }" :class="{ 'is-active': isActive(v) }"
@contextmenu.prevent="onContextmenu(v, $event)" @contextmenu.prevent="onContextmenu(v, $event)"
@mousedown="onMousedownMenu(v, $event)"
@click="onTagsClick(v, k)" @click="onTagsClick(v, k)"
:ref=" :ref="
(el) => { (el) => {
@ -47,6 +48,7 @@
<script lang="ts"> <script lang="ts">
import { import {
defineAsyncComponent,
toRefs, toRefs,
reactive, reactive,
onMounted, onMounted,
@ -56,7 +58,6 @@ import {
onBeforeUpdate, onBeforeUpdate,
onBeforeMount, onBeforeMount,
onUnmounted, onUnmounted,
getCurrentInstance,
watch, watch,
defineComponent, defineComponent,
} from 'vue'; } from 'vue';
@ -71,7 +72,7 @@ import { useKeepALiveNames } from '/@/stores/keepAliveNames';
import { Session } from '/@/utils/storage'; import { Session } from '/@/utils/storage';
import { isObjectValueEqual } from '/@/utils/arrayOperation'; import { isObjectValueEqual } from '/@/utils/arrayOperation';
import other from '/@/utils/other'; import other from '/@/utils/other';
import Contextmenu from '/@/layout/navBars/tagsView/contextmenu.vue'; import mittBus from '/@/utils/mitt';
// //
interface TagsViewState { interface TagsViewState {
@ -104,9 +105,10 @@ interface CurrentContextmenu {
export default defineComponent({ export default defineComponent({
name: 'layoutTagsView', name: 'layoutTagsView',
components: { Contextmenu }, components: {
Contextmenu: defineAsyncComponent(() => import('/@/layout/navBars/tagsView/contextmenu.vue')),
},
setup() { setup() {
const { proxy } = <any>getCurrentInstance();
const tagsRefs = ref<any[]>([]); const tagsRefs = ref<any[]>([]);
const scrollbarRef = ref(); const scrollbarRef = ref();
const contextmenuRef = ref(); const contextmenuRef = ref();
@ -237,13 +239,21 @@ export default defineComponent({
// xxx/:id/:name" tagsview // xxx/:id/:name" tagsview
if (!getThemeConfig.value.isShareTagsView) await solveAddTagsView(path, to); if (!getThemeConfig.value.isShareTagsView) await solveAddTagsView(path, to);
else await singleAddTagsView(path, to); else await singleAddTagsView(path, to);
if (state.tagsViewList.some((v: any) => v.path === to.meta.isDynamicPath)) return false; if (state.tagsViewList.some((v: any) => v.path === to.meta.isDynamicPath)) {
// () tagsViewList
addBrowserSetSession(state.tagsViewList);
return false;
}
item = state.tagsViewRoutesList.find((v: any) => v.path === to.meta.isDynamicPath); item = state.tagsViewRoutesList.find((v: any) => v.path === to.meta.isDynamicPath);
} else { } else {
// tagsview // tagsview
if (!getThemeConfig.value.isShareTagsView) await solveAddTagsView(path, to); if (!getThemeConfig.value.isShareTagsView) await solveAddTagsView(path, to);
else await singleAddTagsView(path, to); else await singleAddTagsView(path, to);
if (state.tagsViewList.some((v: any) => v.path === path)) return false; if (state.tagsViewList.some((v: any) => v.path === path)) {
// () tagsViewList
addBrowserSetSession(state.tagsViewList);
return false;
}
item = state.tagsViewRoutesList.find((v: any) => v.path === path); item = state.tagsViewRoutesList.find((v: any) => v.path === path);
} }
if (!item) return false; if (!item) return false;
@ -258,12 +268,20 @@ export default defineComponent({
}; };
// 2 tagsView // 2 tagsView
const refreshCurrentTagsView = async (fullPath: string) => { const refreshCurrentTagsView = async (fullPath: string) => {
const item = state.tagsViewList.find((v: any) => (getThemeConfig.value.isShareTagsView ? v.path === fullPath : v.url === fullPath)); const decodeURIPath = decodeURI(fullPath);
if (item != null) { let item: any = {};
await storesKeepALiveNames.delCachedView(item); state.tagsViewList.forEach((v: any) => {
proxy.mittBus.emit('onTagsViewRefreshRouterView', fullPath); v.transUrl = transUrlParams(v);
if (item.meta.isKeepAlive) storesKeepALiveNames.addCachedView(item); if (v.transUrl) {
} if (v.transUrl === transUrlParams(v)) item = v;
} else {
if (v.path === decodeURIPath) item = v;
}
});
if (!item) return false;
await storesKeepALiveNames.delCachedView(item);
mittBus.emit('onTagsViewRefreshRouterView', fullPath);
if (item.meta.isKeepAlive) storesKeepALiveNames.addCachedView(item);
}; };
// 3 tagsViewisAffix // 3 tagsViewisAffix
const closeCurrentTagsView = (path: string) => { const closeCurrentTagsView = (path: string) => {
@ -396,11 +414,31 @@ export default defineComponent({
state.dropdown.y = clientY; state.dropdown.y = clientY;
contextmenuRef.value.openContextmenu(v); contextmenuRef.value.openContextmenu(v);
}; };
// tasgview
const onMousedownMenu = (v: any, e: any) => {
if (!v.meta.isAffix && e.which === 2) {
const item = Object.assign({}, { contextMenuClickId: 1, ...v });
onCurrentContextmenuClick(item);
}
};
// tagsView // tagsView
const onTagsClick = (v: any, k: number) => { const onTagsClick = (v: any, k: number) => {
state.tagsRefsIndex = k; state.tagsRefsIndex = k;
router.push(v); router.push(v);
}; };
// urltagsview
// https://gitee.com/lyt-top/vue-next-admin/issues/I5K3YO
const transUrlParams = (v: any) => {
let params = v.query && Object.keys(v.query).length > 0 ? v.query : v.params;
if (!params) return '';
let path = '';
for (let [key, value] of Object.entries(params)) {
if (v.meta.isDynamic) path += `/${value}`;
else path += `&${key}=${value}`;
}
// xxx/:id/:name"isDynamic
return v.meta.isDynamic ? `${v.path.split(':')[0]}${path.replace(/^\//, '')}` : `${v.path}${path.replace(/^&/, '?')}`;
};
// tagsView 使使 // tagsView 使使
const setTagsViewHighlight = (v: any) => { const setTagsViewHighlight = (v: any) => {
let params = v.query && Object.keys(v.query).length > 0 ? v.query : v.params; let params = v.query && Object.keys(v.query).length > 0 ? v.query : v.params;
@ -412,13 +450,9 @@ export default defineComponent({
// xxx/:id/:name" // xxx/:id/:name"
return `${v.meta.isDynamic ? v.meta.isDynamicPath : v.path}-${path}`; return `${v.meta.isDynamic ? v.meta.isDynamicPath : v.path}-${path}`;
}; };
//
const updateScrollbar = () => {
proxy.$refs.scrollbarRef.update();
};
// //
const onHandleScroll = (e: any) => { const onHandleScroll = (e: any) => {
proxy.$refs.scrollbarRef.$refs.wrap$.scrollLeft += e.wheelDelta / 4; scrollbarRef.value.$refs.wrap$.scrollLeft += e.wheelDelta / 4;
}; };
// tagsView // tagsView
const tagsViewmoveToCurrentTag = () => { const tagsViewmoveToCurrentTag = () => {
@ -435,7 +469,7 @@ export default defineComponent({
// li // li
let liLast: any = tagsRefs.value[tagsRefs.value.length - 1]; let liLast: any = tagsRefs.value[tagsRefs.value.length - 1];
// //
let scrollRefs = proxy.$refs.scrollbarRef.$refs.wrap$; let scrollRefs = scrollbarRef.value.$refs.wrap$;
// //
let scrollS = scrollRefs.scrollWidth; let scrollS = scrollRefs.scrollWidth;
// //
@ -469,7 +503,7 @@ export default defineComponent({
} }
} }
// //
updateScrollbar(); scrollbarRef.value.update();
}); });
}; };
// tagsView tagsView // tagsView tagsView
@ -520,15 +554,15 @@ export default defineComponent({
// https://gitee.com/lyt-top/vue-next-admin/issues/I3ZRRI // https://gitee.com/lyt-top/vue-next-admin/issues/I3ZRRI
window.addEventListener('resize', onSortableResize); window.addEventListener('resize', onSortableResize);
// 0 1 2 3 4 // 0 1 2 3 4
proxy.mittBus.on('onCurrentContextmenuClick', (data: CurrentContextmenu) => { mittBus.on('onCurrentContextmenuClick', (data: CurrentContextmenu) => {
onCurrentContextmenuClick(data); onCurrentContextmenuClick(data);
}); });
// / // /
proxy.mittBus.on('openOrCloseSortable', () => { mittBus.on('openOrCloseSortable', () => {
initSortable(); initSortable();
}); });
// TagsView // TagsView
proxy.mittBus.on('openShareTagsView', () => { mittBus.on('openShareTagsView', () => {
if (getThemeConfig.value.isShareTagsView) { if (getThemeConfig.value.isShareTagsView) {
router.push('/home'); router.push('/home');
state.tagsViewList = []; state.tagsViewList = [];
@ -544,11 +578,11 @@ export default defineComponent({
// //
onUnmounted(() => { onUnmounted(() => {
// //
proxy.mittBus.off('onCurrentContextmenuClick', () => {}); mittBus.off('onCurrentContextmenuClick', () => {});
// / // /
proxy.mittBus.off('openOrCloseSortable', () => {}); mittBus.off('openOrCloseSortable', () => {});
// TagsView // TagsView
proxy.mittBus.off('openShareTagsView', () => {}); mittBus.off('openShareTagsView', () => {});
// resize // resize
window.removeEventListener('resize', onSortableResize); window.removeEventListener('resize', onSortableResize);
}); });
@ -583,6 +617,7 @@ export default defineComponent({
return { return {
isActive, isActive,
onContextmenu, onContextmenu,
onMousedownMenu,
onTagsClick, onTagsClick,
tagsRefs, tagsRefs,
contextmenuRef, contextmenuRef,

View File

@ -17,7 +17,7 @@
{{ $t(val.meta.title) }} {{ $t(val.meta.title) }}
</template> </template>
<template #title v-else> <template #title v-else>
<a :href="val.meta.isLink" target="_blank" rel="opener" class="w100"> <a class="w100" @click.prevent="onALinkClick(val)">
<SvgIcon :name="val.meta.icon" /> <SvgIcon :name="val.meta.icon" />
{{ $t(val.meta.title) }} {{ $t(val.meta.title) }}
</a> </a>
@ -31,16 +31,19 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { toRefs, reactive, computed, defineComponent, getCurrentInstance, onMounted, nextTick, onBeforeMount } from 'vue'; import { defineAsyncComponent, toRefs, reactive, computed, defineComponent, onMounted, nextTick, onBeforeMount, ref } from 'vue';
import { useRoute, onBeforeRouteUpdate } from 'vue-router'; import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useRoutesList } from '/@/stores/routesList'; import { useRoutesList } from '/@/stores/routesList';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import SubItem from '/@/layout/navMenu/subItem.vue'; import { verifyUrl } from '/@/utils/toolsValidate';
import mittBus from '/@/utils/mitt';
export default defineComponent({ export default defineComponent({
name: 'navMenuHorizontal', name: 'navMenuHorizontal',
components: { SubItem }, components: {
SubItem: defineAsyncComponent(() => import('/@/layout/navMenu/subItem.vue')),
},
props: { props: {
menuList: { menuList: {
type: Array, type: Array,
@ -48,12 +51,13 @@ export default defineComponent({
}, },
}, },
setup(props) { setup(props) {
const { proxy } = <any>getCurrentInstance(); const elMenuHorizontalScrollRef = ref();
const stores = useRoutesList(); const stores = useRoutesList();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { routesList } = storeToRefs(stores); const { routesList } = storeToRefs(stores);
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);
const route = useRoute(); const route = useRoute();
const router = useRouter();
const state = reactive({ const state = reactive({
defaultActive: null, defaultActive: null,
}); });
@ -64,14 +68,14 @@ export default defineComponent({
// //
const onElMenuHorizontalScroll = (e: any) => { const onElMenuHorizontalScroll = (e: any) => {
const eventDelta = e.wheelDelta || -e.deltaY * 40; const eventDelta = e.wheelDelta || -e.deltaY * 40;
proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap$.scrollLeft = proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap$.scrollLeft + eventDelta / 4; elMenuHorizontalScrollRef.value.$refs.wrap$.scrollLeft = elMenuHorizontalScrollRef.value.$refs.wrap$.scrollLeft + eventDelta / 4;
}; };
// //
const initElMenuOffsetLeft = () => { const initElMenuOffsetLeft = () => {
nextTick(() => { nextTick(() => {
let els: any = document.querySelector('.el-menu.el-menu--horizontal li.is-active'); let els: any = document.querySelector('.el-menu.el-menu--horizontal li.is-active');
if (!els) return false; if (!els) return false;
proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap$.scrollLeft = els.offsetLeft; elMenuHorizontalScrollRef.value.$refs.wrap$.scrollLeft = els.offsetLeft;
}); });
}; };
// //
@ -109,6 +113,13 @@ export default defineComponent({
else state.defaultActive = path; else state.defaultActive = path;
} }
}; };
//
const onALinkClick = (val: any) => {
const { origin, pathname } = window.location;
router.push(val.path);
if (verifyUrl(val.meta.isLink)) window.open(val.meta.isLink);
else window.open(`${origin}${pathname}#${val.meta.isLink}`);
};
// //
onBeforeMount(() => { onBeforeMount(() => {
setCurrentRouterHighlight(route); setCurrentRouterHighlight(route);
@ -124,12 +135,14 @@ export default defineComponent({
// tagsView // tagsView
let { layout, isClassicSplitMenu } = themeConfig.value; let { layout, isClassicSplitMenu } = themeConfig.value;
if (layout === 'classic' && isClassicSplitMenu) { if (layout === 'classic' && isClassicSplitMenu) {
proxy.mittBus.emit('setSendClassicChildren', setSendClassicChildren(to.path)); mittBus.emit('setSendClassicChildren', setSendClassicChildren(to.path));
} }
}); });
return { return {
elMenuHorizontalScrollRef,
menuLists, menuLists,
onElMenuHorizontalScroll, onElMenuHorizontalScroll,
onALinkClick,
...toRefs(state), ...toRefs(state),
}; };
}, },

View File

@ -14,7 +14,7 @@
<span>{{ $t(val.meta.title) }}</span> <span>{{ $t(val.meta.title) }}</span>
</template> </template>
<template v-else> <template v-else>
<a :href="val.meta.isLink" target="_blank" rel="opener" class="w100"> <a class="w100" @click.prevent="onALinkClick(val)">
<SvgIcon :name="val.meta.icon" /> <SvgIcon :name="val.meta.icon" />
{{ $t(val.meta.title) }} {{ $t(val.meta.title) }}
</a> </a>
@ -26,6 +26,8 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent } from 'vue'; import { computed, defineComponent } from 'vue';
import { useRouter } from 'vue-router';
import { verifyUrl } from '/@/utils/toolsValidate';
export default defineComponent({ export default defineComponent({
name: 'navMenuSubItem', name: 'navMenuSubItem',
@ -36,12 +38,21 @@ export default defineComponent({
}, },
}, },
setup(props) { setup(props) {
const router = useRouter();
// //
const chils = computed(() => { const chils = computed(() => {
return <any>props.chil; return <any>props.chil;
}); });
//
const onALinkClick = (val: any) => {
const { origin, pathname } = window.location;
router.push(val.path);
if (verifyUrl(val.meta.isLink)) window.open(val.meta.isLink);
else window.open(`${origin}${pathname}#${val.meta.isLink}`);
};
return { return {
chils, chils,
onALinkClick,
}; };
}, },
}); });

View File

@ -22,7 +22,7 @@
<span>{{ $t(val.meta.title) }}</span> <span>{{ $t(val.meta.title) }}</span>
</template> </template>
<template #title v-else> <template #title v-else>
<a :href="val.meta.isLink" target="_blank" rel="opener" class="w100">{{ $t(val.meta.title) }}</a> <a class="w100" @click.prevent="onALinkClick(val)">{{ $t(val.meta.title) }}</a>
</template> </template>
</el-menu-item> </el-menu-item>
</template> </template>
@ -31,15 +31,17 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { toRefs, reactive, computed, defineComponent, onMounted, watch } from 'vue'; import { defineAsyncComponent, toRefs, reactive, computed, defineComponent, onMounted, watch } from 'vue';
import { useRoute, onBeforeRouteUpdate } from 'vue-router'; import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import SubItem from '/@/layout/navMenu/subItem.vue'; import { verifyUrl } from '/@/utils/toolsValidate';
export default defineComponent({ export default defineComponent({
name: 'navMenuVertical', name: 'navMenuVertical',
components: { SubItem }, components: {
SubItem: defineAsyncComponent(() => import('/@/layout/navMenu/subItem.vue')),
},
props: { props: {
menuList: { menuList: {
type: Array, type: Array,
@ -50,6 +52,7 @@ export default defineComponent({
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);
const route = useRoute(); const route = useRoute();
const router = useRouter();
const state = reactive({ const state = reactive({
// https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G // https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
defaultActive: route.meta.isDynamic ? route.meta.isDynamicPath : route.path, defaultActive: route.meta.isDynamic ? route.meta.isDynamicPath : route.path,
@ -70,6 +73,13 @@ export default defineComponent({
if (pathSplit.length >= 4 && meta.isHide) return pathSplit.splice(0, 3).join('/'); if (pathSplit.length >= 4 && meta.isHide) return pathSplit.splice(0, 3).join('/');
else return path; else return path;
}; };
//
const onALinkClick = (val: any) => {
const { origin, pathname } = window.location;
router.push(val.path);
if (verifyUrl(val.meta.isLink)) window.open(val.meta.isLink);
else window.open(`${origin}${pathname}#${val.meta.isLink}`);
};
// / // /
watch( watch(
themeConfig.value, themeConfig.value,
@ -94,6 +104,7 @@ export default defineComponent({
return { return {
menuLists, menuLists,
getThemeConfig, getThemeConfig,
onALinkClick,
...toRefs(state), ...toRefs(state),
}; };
}, },

View File

@ -1,65 +1,89 @@
<template> <template>
<div class="layout-view-bg-white flex mt1" :style="{ height: `calc(100vh - ${setIframeHeight}`, border: 'none' }" v-loading="iframeLoading"> <div class="layout-padding layout-padding-unset layout-iframe">
<iframe :src="iframeUrl" frameborder="0" height="100%" width="100%" ref="iframeDom" v-show="!iframeLoading"></iframe> <div class="layout-padding-auto layout-padding-view">
<div class="w100" v-for="v in setIframeList" :key="v.path" v-loading="v.meta.loading" element-loading-background="white">
<transition-group :name="name" mode="out-in">
<iframe
:src="v.meta.isLink"
:key="v.path"
frameborder="0"
height="100%"
width="100%"
style="position: absolute"
:data-url="v.path"
v-show="getRoutePath === v.path"
ref="iframeRef"
/>
</transition-group>
</div>
</div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, reactive, toRefs, onMounted, nextTick, watch, computed } from 'vue'; import { defineComponent, computed, watch, ref, nextTick } from 'vue';
import { storeToRefs } from 'pinia';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
export default defineComponent({ export default defineComponent({
name: 'layoutIfameView', name: 'layoutIframeView',
setup() { props: {
const storesThemeConfig = useThemeConfig(); refreshKey: {
const storesTagsViewRoutes = useTagsViewRoutes(); type: String,
const { themeConfig } = storeToRefs(storesThemeConfig); default: () => '',
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes); },
name: {
type: String,
default: () => 'slide-right',
},
list: {
type: Array,
default: () => [],
},
},
setup(props) {
const iframeRef = ref();
const route = useRoute(); const route = useRoute();
const state = reactive({ // list
iframeDom: null as HTMLIFrameElement | null, const setIframeList = computed(() => {
iframeLoading: true, return (<any>props.list).filter((v: any) => v.meta.isIframeOpen);
iframeUrl: '',
}); });
// loading // iframe path
const initIframeLoad = () => { const getRoutePath = computed(() => {
state.iframeUrl = <any>route.meta.isLink; return route.path;
nextTick(() => {
state.iframeLoading = true;
const iframe = state.iframeDom;
if (!iframe) return false;
iframe.onload = () => {
state.iframeLoading = false;
};
});
};
// iframe
const setIframeHeight = computed(() => {
let { isTagsview } = themeConfig.value;
if (isTagsViewCurrenFull.value) {
return `1px`;
} else {
if (isTagsview) return `86px`;
else return `51px`;
}
}); });
// // iframe iframe
onMounted(() => {
initIframeLoad();
});
// iframe 使
watch( watch(
() => route.path, () => route.fullPath,
() => { (val) => {
initIframeLoad(); const item: any = props.list.find((v: any) => v.path === val);
if (item && !item.meta.isIframeOpen) item.meta.isIframeOpen = true;
nextTick(() => {
if (!iframeRef.value) return false;
iframeRef.value.forEach((v: any) => {
if (v.dataset.url === val) {
v.onload = () => {
if (item && item.meta.isIframeOpen && item.meta.loading) item.meta.loading = false;
};
}
});
});
},
{
immediate: true,
}
);
// iframe refreshKey tagsview
watch(
() => props.refreshKey,
() => {},
{
deep: true,
} }
); );
return { return {
setIframeHeight, iframeRef,
...toRefs(state), setIframeList,
getRoutePath,
}; };
}, },
}); });

View File

@ -1,16 +1,22 @@
<template> <template>
<div class="layout-view-bg-white flex layout-view-link" :style="{ height: `calc(100vh - ${setLinkHeight}` }"> <div class="layout-padding layout-link-container">
<a :href="currentRouteMeta.isLink" target="_blank" rel="opener" class="flex-margin"> <div class="layout-padding-auto layout-padding-view">
{{ $t(currentRouteMeta.title) }}{{ currentRouteMeta.isLink }} <div class="layout-link-warp">
</a> <i class="layout-link-icon iconfont icon-xingqiu"></i>
<div class="layout-link-msg">页面 "{{ $t(currentRouteMeta.title) }}" 已在新窗口中打开</div>
<el-button class="mt30" round size="default" @click="onGotoFullPage">
<i class="iconfont icon-lianjie"></i>
<span>立即前往体验</span>
</el-button>
</div>
</div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, toRefs, reactive, computed, watch } from 'vue'; import { defineComponent, toRefs, reactive, watch } from 'vue';
import { useRoute, RouteMeta } from 'vue-router'; import { useRoute, RouteMeta } from 'vue-router';
import { storeToRefs } from 'pinia'; import { verifyUrl } from '/@/utils/toolsValidate';
import { useThemeConfig } from '/@/stores/themeConfig';
// //
interface LinkViewState { interface LinkViewState {
@ -27,8 +33,6 @@ interface LinkViewRouteMeta extends RouteMeta {
export default defineComponent({ export default defineComponent({
name: 'layoutLinkView', name: 'layoutLinkView',
setup() { setup() {
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const route = useRoute(); const route = useRoute();
const state = reactive<LinkViewState>({ const state = reactive<LinkViewState>({
currentRouteMeta: { currentRouteMeta: {
@ -36,12 +40,12 @@ export default defineComponent({
title: '', title: '',
}, },
}); });
// link //
const setLinkHeight = computed(() => { const onGotoFullPage = () => {
let { isTagsview } = themeConfig.value; const { origin, pathname } = window.location;
if (isTagsview) return `115px`; if (verifyUrl(state.currentRouteMeta.isLink)) window.open(state.currentRouteMeta.isLink);
else return `80px`; else window.open(`${origin}${pathname}#${state.currentRouteMeta.isLink}`);
}); };
// //
watch( watch(
() => route.path, () => route.path,
@ -53,9 +57,57 @@ export default defineComponent({
} }
); );
return { return {
setLinkHeight, onGotoFullPage,
...toRefs(state), ...toRefs(state),
}; };
}, },
}); });
</script> </script>
<style scoped lang="scss">
.layout-link-container {
.layout-link-warp {
margin: auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
i.layout-link-icon {
position: relative;
font-size: 100px;
color: var(--el-color-primary);
&::after {
content: '';
position: absolute;
left: 50px;
top: 0;
width: 15px;
height: 100px;
background: linear-gradient(
rgba(255, 255, 255, 0.01),
rgba(255, 255, 255, 0.01),
rgba(255, 255, 255, 0.01),
rgba(255, 255, 255, 0.05),
rgba(255, 255, 255, 0.05),
rgba(255, 255, 255, 0.05),
rgba(235, 255, 255, 0.5),
rgba(255, 255, 255, 0.05),
rgba(255, 255, 255, 0.05),
rgba(255, 255, 255, 0.05),
rgba(255, 255, 255, 0.01),
rgba(255, 255, 255, 0.01),
rgba(255, 255, 255, 0.01)
);
transform: rotate(-15deg);
animation: toRight 5s linear infinite;
}
}
.layout-link-msg {
font-size: 12px;
color: var(--next-bg-topBarColor);
opacity: 0.7;
margin-top: 15px;
}
}
}
</style>

View File

@ -1,41 +1,52 @@
<template> <template>
<div class="h100"> <div class="layout-parent">
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
<transition :name="setTransitionName" mode="out-in"> <transition :name="setTransitionName" mode="out-in">
<keep-alive :include="getKeepAliveNames"> <keep-alive :include="getKeepAliveNames">
<component :is="Component" :key="refreshRouterViewKey" class="w100" /> <component :is="Component" :key="refreshRouterViewKey" class="w100" v-show="!isIframePage" />
</keep-alive> </keep-alive>
</transition> </transition>
</router-view> </router-view>
<transition :name="setTransitionName" mode="out-in">
<Iframes class="w100" v-show="isIframePage" :refreshKey="iframeRefreshKey" :name="setTransitionName" :list="iframeList" />
</transition>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, toRefs, reactive, getCurrentInstance, onBeforeMount, onUnmounted, nextTick, watch, onMounted } from 'vue'; import { defineAsyncComponent, computed, defineComponent, toRefs, reactive, onBeforeMount, onUnmounted, nextTick, watch, onMounted } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useKeepALiveNames } from '/@/stores/keepAliveNames'; import { useKeepALiveNames } from '/@/stores/keepAliveNames';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import { Session } from '/@/utils/storage'; import { Session } from '/@/utils/storage';
import mittBus from '/@/utils/mitt';
// //
interface ParentViewState { interface ParentViewState {
refreshRouterViewKey: null | string; refreshRouterViewKey: string;
iframeRefreshKey: string;
keepAliveNameList: string[]; keepAliveNameList: string[];
iframeList: string[];
} }
export default defineComponent({ export default defineComponent({
name: 'layoutParentView', name: 'layoutParentView',
components: {
Iframes: defineAsyncComponent(() => import('/@/layout/routerView/iframes.vue')),
},
setup() { setup() {
const { proxy } = <any>getCurrentInstance();
const route = useRoute(); const route = useRoute();
const router = useRouter();
const storesKeepAliveNames = useKeepALiveNames(); const storesKeepAliveNames = useKeepALiveNames();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { keepAliveNames, cachedViews } = storeToRefs(storesKeepAliveNames); const { keepAliveNames, cachedViews } = storeToRefs(storesKeepAliveNames);
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);
const state = reactive<ParentViewState>({ const state = reactive<ParentViewState>({
refreshRouterViewKey: null, refreshRouterViewKey: '', // iframe tagsview
iframeRefreshKey: '', // iframe tagsview
keepAliveNameList: [], keepAliveNameList: [],
iframeList: [],
}); });
// //
const setTransitionName = computed(() => { const setTransitionName = computed(() => {
@ -45,20 +56,37 @@ export default defineComponent({
const getKeepAliveNames = computed(() => { const getKeepAliveNames = computed(() => {
return themeConfig.value.isTagsview ? cachedViews.value : state.keepAliveNameList; return themeConfig.value.isTagsview ? cachedViews.value : state.keepAliveNameList;
}); });
// iframe /
const isIframePage = computed(() => {
return route.meta.isIframe;
});
// iframe ()
const getIframeListRoutes = async () => {
router.getRoutes().forEach((v: any) => {
if (v.meta.isIframe) {
v.meta.isIframeOpen = false;
v.meta.loading = true;
state.iframeList.push({ ...v });
}
});
};
// //
onBeforeMount(() => { onBeforeMount(() => {
state.keepAliveNameList = keepAliveNames.value; state.keepAliveNameList = keepAliveNames.value;
proxy.mittBus.on('onTagsViewRefreshRouterView', (fullPath: string) => { mittBus.on('onTagsViewRefreshRouterView', (fullPath: string) => {
state.keepAliveNameList = keepAliveNames.value.filter((name: string) => route.name !== name); state.keepAliveNameList = keepAliveNames.value.filter((name: string) => route.name !== name);
state.refreshRouterViewKey = null; state.refreshRouterViewKey = '';
state.iframeRefreshKey = '';
nextTick(() => { nextTick(() => {
state.refreshRouterViewKey = fullPath; state.refreshRouterViewKey = fullPath;
state.iframeRefreshKey = fullPath;
state.keepAliveNameList = keepAliveNames.value; state.keepAliveNameList = keepAliveNames.value;
}); });
}); });
}); });
// //
onMounted(() => { onMounted(() => {
getIframeListRoutes();
// https://gitee.com/lyt-top/vue-next-admin/issues/I58U75 // https://gitee.com/lyt-top/vue-next-admin/issues/I58U75
// https://gitee.com/lyt-top/vue-next-admin/issues/I59RXK // https://gitee.com/lyt-top/vue-next-admin/issues/I59RXK
nextTick(() => { nextTick(() => {
@ -69,18 +97,24 @@ export default defineComponent({
}); });
// //
onUnmounted(() => { onUnmounted(() => {
proxy.mittBus.off('onTagsViewRefreshRouterView', () => {}); mittBus.off('onTagsViewRefreshRouterView', () => {});
}); });
// tagsView // tagsView
// https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/38/files
watch( watch(
() => route.fullPath, () => route.fullPath,
() => { () => {
state.refreshRouterViewKey = decodeURI(route.fullPath); state.refreshRouterViewKey = decodeURI(route.fullPath);
},
{
immediate: true,
} }
); );
return { return {
route,
setTransitionName, setTransitionName,
getKeepAliveNames, getKeepAliveNames,
isIframePage,
...toRefs(state), ...toRefs(state),
}; };
}, },

View File

@ -9,7 +9,6 @@ import other from '/@/utils/other';
import ElementPlus from 'element-plus'; import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css'; import 'element-plus/dist/index.css';
import '/@/theme/index.scss'; import '/@/theme/index.scss';
import mitt from 'mitt';
import VueGridLayout from 'vue-grid-layout'; import VueGridLayout from 'vue-grid-layout';
const app = createApp(App); const app = createApp(App);
@ -18,5 +17,3 @@ directive(app);
other.elSvg(app); other.elSvg(app);
app.use(pinia).use(router).use(ElementPlus, { i18n: i18n.global.t }).use(i18n).use(VueGridLayout).mount('#app'); app.use(pinia).use(router).use(ElementPlus, { i18n: i18n.global.t }).use(i18n).use(VueGridLayout).mount('#app');
app.config.globalProperties.mittBus = mitt();

View File

@ -80,6 +80,8 @@ export function setCacheTagsViewRoutes() {
*/ */
export function setFilterRouteEnd() { export function setFilterRouteEnd() {
let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes)); let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes));
// notFoundAndNoPower 防止 404、401 不在 layout 布局中不设置的话404、401 界面将全屏显示
// 关联问题 No match found for location with path 'xxx'
filterRouteEnd[0].children = [...filterRouteEnd[0].children, ...notFoundAndNoPower]; filterRouteEnd[0].children = [...filterRouteEnd[0].children, ...notFoundAndNoPower];
return filterRouteEnd; return filterRouteEnd;
} }

View File

@ -64,6 +64,8 @@ export async function frontEndsResetRoute() {
*/ */
export function setFilterRouteEnd() { export function setFilterRouteEnd() {
let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes)); let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes));
// notFoundAndNoPower 防止 404、401 不在 layout 布局中不设置的话404、401 界面将全屏显示
// 关联问题 No match found for location with path 'xxx'
filterRouteEnd[0].children = [...setFilterRoute(filterRouteEnd[0].children), ...notFoundAndNoPower]; filterRouteEnd[0].children = [...setFilterRoute(filterRouteEnd[0].children), ...notFoundAndNoPower];
return filterRouteEnd; return filterRouteEnd;
} }

View File

@ -7,7 +7,7 @@ import { useKeepALiveNames } from '/@/stores/keepAliveNames';
import { useRoutesList } from '/@/stores/routesList'; import { useRoutesList } from '/@/stores/routesList';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import { Session } from '/@/utils/storage'; import { Session } from '/@/utils/storage';
import { staticRoutes } from '/@/router/route'; import { staticRoutes, notFoundAndNoPower } from '/@/router/route';
import { initFrontEndControlRoutes } from '/@/router/frontEnd'; import { initFrontEndControlRoutes } from '/@/router/frontEnd';
import { initBackEndControlRoutes } from '/@/router/backEnd'; import { initBackEndControlRoutes } from '/@/router/backEnd';
@ -32,7 +32,13 @@ const { isRequestRoutes } = themeConfig.value;
*/ */
export const router = createRouter({ export const router = createRouter({
history: createWebHashHistory(), history: createWebHashHistory(),
routes: staticRoutes, /**
*
* 1notFoundAndNoPower 404401 No match found for location with path 'xxx'
* 2backEnd.ts()frontEnd.ts() notFoundAndNoPower 404401
* 404401 layout 404401
*/
routes: [...notFoundAndNoPower, ...staticRoutes],
}); });
/** /**
@ -107,13 +113,12 @@ router.beforeEach(async (to, from, next) => {
if (isRequestRoutes) { if (isRequestRoutes) {
// 后端控制路由:路由数据初始化,防止刷新时丢失 // 后端控制路由:路由数据初始化,防止刷新时丢失
await initBackEndControlRoutes(); await initBackEndControlRoutes();
// 动态添加路由:防止非首页刷新时跳转回首页的问题 // 解决刷新时,一直跳 404 页面问题,关联问题 No match found for location with path 'xxx'
// 确保 addRoute() 时动态添加的路由已经被完全加载上去 next({ path: to.path });
next({ ...to, replace: true });
} else { } else {
// https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP // https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP
await initFrontEndControlRoutes(); await initFrontEndControlRoutes();
next({ ...to, replace: true }); next({ path: to.path });
} }
} else { } else {
next(); next();

View File

@ -4,7 +4,7 @@ import { RouteRecordRaw } from 'vue-router';
* meta对象参数说明 * meta对象参数说明
* meta: { * meta: {
* title: 菜单栏及 tagsView * title: 菜单栏及 tagsView
* isLink `1、isLink: 链接地址不为空` * isLink `1、isLink: 链接地址不为空 2、isIframe:false`
* isHide * isHide
* isKeepAlive * isKeepAlive
* isAffix tagsView * isAffix tagsView
@ -1020,6 +1020,11 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
roles: ['admin'], roles: ['admin'],
icon: 'ele-ChatLineRound', icon: 'ele-ChatLineRound',
}, },
/**
*
* component `() => import('/@/layout/routerView/link.vue')`
* isLink staticRoutes
*/
children: [ children: [
{ {
path: '/visualizing/visualizingLinkDemo1', path: '/visualizing/visualizingLinkDemo1',
@ -1027,7 +1032,7 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
component: () => import('/@/layout/routerView/link.vue'), component: () => import('/@/layout/routerView/link.vue'),
meta: { meta: {
title: 'message.router.visualizingLinkDemo1', title: 'message.router.visualizingLinkDemo1',
isLink: `${import.meta.env.VITE_API_URL}#/visualizingDemo1`, isLink: '/visualizingDemo1',
isHide: false, isHide: false,
isKeepAlive: false, isKeepAlive: false,
isAffix: false, isAffix: false,
@ -1042,7 +1047,7 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
component: () => import('/@/layout/routerView/link.vue'), component: () => import('/@/layout/routerView/link.vue'),
meta: { meta: {
title: 'message.router.visualizingLinkDemo2', title: 'message.router.visualizingLinkDemo2',
isLink: `${import.meta.env.VITE_API_URL}#/visualizingDemo2`, isLink: '/visualizingDemo2',
isHide: false, isHide: false,
isKeepAlive: false, isKeepAlive: false,
isAffix: false, isAffix: false,
@ -1114,14 +1119,29 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
}, },
}, },
{ {
path: '/iframes', path: '/iframesOne',
name: 'layoutIfameView', name: 'layoutIframeViewOne',
component: () => import('/@/layout/routerView/iframes.vue'), component: () => import('/@/layout/routerView/iframes.vue'),
meta: { meta: {
title: 'message.router.layoutIfameView', title: 'message.router.layoutIframeViewOne',
isLink: 'https://nodejs.org/zh-cn/', isLink: 'https://nodejs.org/zh-cn/',
isHide: false, isHide: false,
isKeepAlive: false, isKeepAlive: true,
isAffix: true,
isIframe: true,
roles: ['admin'],
icon: 'iconfont icon-neiqianshujuchucun',
},
},
{
path: '/iframesTwo',
name: 'layoutIframeViewTwo',
component: () => import('/@/layout/routerView/iframes.vue'),
meta: {
title: 'message.router.layoutIframeViewTwo',
isLink: 'https://undraw.co/illustrations',
isHide: false,
isKeepAlive: true,
isAffix: true, isAffix: true,
isIframe: true, isIframe: true,
roles: ['admin'], roles: ['admin'],

View File

@ -52,6 +52,7 @@ export interface ThemeConfigState {
columnsMenuBar: string; columnsMenuBar: string;
columnsMenuBarColor: string; columnsMenuBarColor: string;
isColumnsMenuBarColorGradual: boolean; isColumnsMenuBarColorGradual: boolean;
isColumnsMenuHoverPreload: boolean;
isCollapse: boolean; isCollapse: boolean;
isUniqueOpened: boolean; isUniqueOpened: boolean;
isFixedHeader: boolean; isFixedHeader: boolean;
@ -82,6 +83,7 @@ export interface ThemeConfigState {
isRequestRoutes: boolean; isRequestRoutes: boolean;
globalTitle: string; globalTitle: string;
globalViceTitle: string; globalViceTitle: string;
globalViceTitleMsg: string;
globalI18n: string; globalI18n: string;
globalComponentSize: string; globalComponentSize: string;
} }

View File

@ -19,8 +19,7 @@ export const useKeepALiveNames = defineStore('keepALiveNames', {
this.keepAliveNames = data; this.keepAliveNames = data;
}, },
async addCachedView(view: any) { async addCachedView(view: any) {
if (this.cachedViews.includes(view.name)) return; if (view.meta.isKeepAlive) this.cachedViews?.push(view.name);
if (view.meta.isKeepAlive) this.cachedViews.push(view.name);
}, },
async delCachedView(view: any) { async delCachedView(view: any) {
const index = this.cachedViews.indexOf(view.name); const index = this.cachedViews.indexOf(view.name);

View File

@ -47,6 +47,8 @@ export const useThemeConfig = defineStore('themeConfig', {
columnsMenuBarColor: '#e6e6e6', columnsMenuBarColor: '#e6e6e6',
// 是否开启分栏菜单背景颜色渐变 // 是否开启分栏菜单背景颜色渐变
isColumnsMenuBarColorGradual: false, isColumnsMenuBarColorGradual: false,
// 是否开启分栏菜单鼠标悬停预加载(预览菜单)
isColumnsMenuHoverPreload: false,
/** /**
* *
@ -54,7 +56,7 @@ export const useThemeConfig = defineStore('themeConfig', {
// 是否开启菜单水平折叠效果 // 是否开启菜单水平折叠效果
isCollapse: false, isCollapse: false,
// 是否开启菜单手风琴效果 // 是否开启菜单手风琴效果
isUniqueOpened: false, isUniqueOpened: true,
// 是否开启固定 Header // 是否开启固定 Header
isFixedHeader: false, isFixedHeader: false,
// 初始化变量,用于更新菜单 el-scrollbar 的高度,请勿删除 // 初始化变量,用于更新菜单 el-scrollbar 的高度,请勿删除
@ -88,15 +90,15 @@ export const useThemeConfig = defineStore('themeConfig', {
// 是否开启 TagsView 共用 // 是否开启 TagsView 共用
isShareTagsView: false, isShareTagsView: false,
// 是否开启 Footer 底部版权信息 // 是否开启 Footer 底部版权信息
isFooter: false, isFooter: true,
// 是否开启灰色模式 // 是否开启灰色模式
isGrayscale: false, isGrayscale: false,
// 是否开启色弱模式 // 是否开启色弱模式
isInvert: false, isInvert: false,
// 是否开启水印 // 是否开启水印
isWartermark: false, isWartermark: true,
// 水印文案 // 水印文案
wartermarkText: 'small@小柒', wartermarkText: 'vue-next-admin',
/** /**
* *
@ -132,6 +134,8 @@ export const useThemeConfig = defineStore('themeConfig', {
globalTitle: 'vue-next-admin', globalTitle: 'vue-next-admin',
// 网站副标题(登录页顶部文字) // 网站副标题(登录页顶部文字)
globalViceTitle: 'vueNextAdmin', globalViceTitle: 'vueNextAdmin',
// 网站副标题(登录页顶部文字)
globalViceTitleMsg: '专注、免费、开源、维护、解疑',
// 默认初始语言,可选值"<zh-cn|en|zh-tw>",默认 zh-cn // 默认初始语言,可选值"<zh-cn|en|zh-tw>",默认 zh-cn
globalI18n: 'zh-cn', globalI18n: 'zh-cn',
// 默认全局组件大小,可选值"<large|'default'|small>",默认 'large' // 默认全局组件大小,可选值"<large|'default'|small>",默认 'large'

View File

@ -65,7 +65,7 @@ export const useUserInfo = defineStore('userInfo', {
authBtnList: defaultAuthBtnList, authBtnList: defaultAuthBtnList,
}; };
resolve(userInfos); resolve(userInfos);
}, 3000); }, 0);
}); });
}, },
}, },

View File

@ -46,6 +46,14 @@ body,
.layout-container { .layout-container {
width: 100%; width: 100%;
height: 100%; height: 100%;
.layout-pd {
padding: 15px !important;
}
.layout-flex {
display: flex;
flex-direction: column;
flex: 1;
}
.layout-aside { .layout-aside {
background: var(--next-bg-menuBar); background: var(--next-bg-menuBar);
box-shadow: 2px 0 6px rgb(0 21 41 / 1%); box-shadow: 2px 0 6px rgb(0 21 41 / 1%);
@ -61,24 +69,63 @@ body,
} }
.layout-header { .layout-header {
padding: 0 !important; padding: 0 !important;
height: auto !important;
} }
.layout-main { .layout-main {
padding: 0 !important; padding: 0 !important;
overflow: hidden; overflow: hidden;
width: 100%; width: 100%;
background-color: var(--next-bg-main-color); background-color: var(--next-bg-main-color);
display: flex;
flex-direction: column;
// 内层 el-scrollbar样式用于界面高度自适应main.vue
.layout-main-scroll {
@extend .layout-flex;
.layout-parent {
@extend .layout-flex;
position: relative;
}
}
}
// 用于界面高度自适应
.layout-padding {
@extend .layout-pd;
position: absolute;
left: 0;
top: 0;
height: 100%;
overflow: hidden;
@extend .layout-flex;
&-auto {
height: inherit;
@extend .layout-flex;
}
&-view {
background: var(--el-color-white);
width: 100%;
height: 100%;
border-radius: 4px;
border: 1px solid var(--el-border-color-light, #ebeef5);
overflow: hidden;
}
}
// 用于界面高度自适应主视图区 main 的内边距用于 iframe
.layout-padding-unset {
padding: 0 !important;
&-view {
border-radius: 0 !important;
border: none !important;
}
}
// 用于设置 iframe loading 时的高度loading 垂直居中显示
.layout-iframe {
.el-loading-parent--relative {
height: 100%;
}
} }
.el-scrollbar { .el-scrollbar {
width: 100%; width: 100%;
} }
// 此字段多次用到建议不删除如需修改请重写覆盖样式
.layout-view-bg-white {
background: var(--el-color-white);
width: 100%;
height: 100%;
border-radius: 4px;
border: 1px solid var(--el-border-color-light, #ebeef5);
}
.layout-el-aside-br-color { .layout-el-aside-br-color {
border-right: 1px solid var(--el-border-color-light, #ebeef5); border-right: 1px solid var(--el-border-color-light, #ebeef5);
} }
@ -122,10 +169,6 @@ body,
z-index: 9999998; z-index: 9999998;
animation: error-img 0.3s; animation: error-img 0.3s;
} }
.layout-scrollbar {
@extend .el-scrollbar;
padding: 15px;
}
.layout-mian-height-50 { .layout-mian-height-50 {
height: calc(100vh - 50px); height: calc(100vh - 50px);
} }

View File

@ -92,3 +92,56 @@
opacity: 0; opacity: 0;
} }
} }
/* 登录页动画
------------------------------- */
@keyframes loginLeft {
0% {
left: -100%;
}
50%,
100% {
left: 100%;
}
}
@keyframes loginTop {
0% {
top: -100%;
}
50%,
100% {
top: 100%;
}
}
@keyframes loginRight {
0% {
right: -100%;
}
50%,
100% {
right: 100%;
}
}
@keyframes loginBottom {
0% {
bottom: -100%;
}
50%,
100% {
bottom: 100%;
}
}
/* 左右左 link.vue
------------------------------- */
@keyframes toRight {
0% {
left: -5px;
}
50% {
left: 100%;
}
100% {
left: -5px;
}
}

View File

@ -233,4 +233,9 @@
border-color: var(--el-border-color-lighter) !important; border-color: var(--el-border-color-lighter) !important;
} }
} }
// loading
.el-loading-mask {
background-color: var(--next-bg-main) !important;
}
} }

View File

@ -44,6 +44,10 @@
margin-bottom: 18px !important; margin-bottom: 18px !important;
} }
} }
// https://gitee.com/lyt-top/vue-next-admin/issues/I5K1PM
.el-form-item .el-form-item__label .el-icon {
margin-right: 0px;
}
} }
/* Alert 警告 /* Alert 警告
@ -259,17 +263,24 @@
.el-scrollbar__bar { .el-scrollbar__bar {
z-index: 4; z-index: 4;
} }
/*防止页面切换时,滚动条高度不变的问题(滚动条高度非滚动条滚动高度)*/
.el-scrollbar__wrap { .el-scrollbar__wrap {
max-height: 100%; /*防止页面切换时,滚动条高度不变的问题(滚动条高度非滚动条滚动高度)*/ max-height: 100%;
} }
.el-select-dropdown .el-scrollbar__wrap { .el-select-dropdown .el-scrollbar__wrap {
overflow-x: scroll !important; overflow-x: scroll !important;
} }
/*修复Select 选择器高度问题*/
.el-select-dropdown__wrap { .el-select-dropdown__wrap {
max-height: 274px !important; /*修复Select 选择器高度问题*/ max-height: 274px !important;
} }
/*修复Cascader 级联选择器高度问题*/
.el-cascader-menu__wrap.el-scrollbar__wrap { .el-cascader-menu__wrap.el-scrollbar__wrap {
height: 204px !important; /*修复Cascader 级联选择器高度问题*/ height: 204px !important;
}
/*用于界面高度自适应main.vue区分 scrollbar__view防止其它使用 scrollbar 的地方出现滚动条消失*/
.layout-container-view .el-scrollbar__view {
height: 100%;
} }
/* Drawer 抽屉 /* Drawer 抽屉

View File

@ -13,6 +13,7 @@
margin-left: 0 !important; margin-left: 0 !important;
} }
.el-form-item { .el-form-item {
// 响应式表单时登录页需要重新处理
display: unset !important; display: unset !important;
} }
} }

View File

@ -1,32 +1,23 @@
@import './index.scss'; @import './index.scss';
/* 页面宽度小于992px /* 页面宽度小于1200px
------------------------------- */ ------------------------------- */
@media screen and (max-width: $lg) { @media screen and (max-width: $lg) and (min-width: $xs) {
.login-container { .login-container {
.login-icon-group { .login-left {
&::before { .login-left-img {
content: ''; top: 90% !important;
height: 70% !important; left: 12% !important;
transition: all 0.3s ease; width: 30% !important;
} height: 18% !important;
&::after {
content: '';
width: 100px !important;
height: 200px !important;
transition: all 0.3s ease;
} }
} }
} .login-right {
} position: absolute;
top: 50%;
/* 页面宽度小于992px left: 50%;
------------------------------- */ transform: translate(-50%, -50%);
@media screen and (max-width: $md) { }
.login-content {
right: unset !important;
left: 50% !important;
transform: translate(-50%, -50%) translate3d(0, 0, 0) !important;
} }
} }
@ -34,19 +25,34 @@
------------------------------- */ ------------------------------- */
@media screen and (max-width: $xs) { @media screen and (max-width: $xs) {
.login-container { .login-container {
.login-icon-group { .login-left {
display: none !important; display: none;
} }
.login-content { .login-right {
width: 100% !important; width: 100% !important;
height: 100% !important; .login-right-warp {
padding: 20px 0 !important; width: 100% !important;
border-radius: 0 !important; height: 100% !important;
box-shadow: unset !important; border: none !important;
border: none !important; .login-right-warp-mian {
} .el-form-item {
.el-form-item { display: flex !important;
display: flex !important; }
.login-right-warp-main-title {
font-size: 20px !important;
}
}
.login-right-warp-one {
&::after {
right: 0 !important;
}
}
.login-right-warp-two {
&::before {
bottom: 1px !important;
}
}
}
} }
} }
} }
@ -55,9 +61,14 @@
------------------------------- */ ------------------------------- */
@media screen and (max-width: $us) { @media screen and (max-width: $us) {
.login-container { .login-container {
.login-content-title { .login-right {
font-size: 18px !important; .login-right-warp {
transition: all 0.3s ease; .login-right-warp-mian {
.login-right-warp-main-title {
font-size: 18px !important;
}
}
}
} }
} }
} }

View File

@ -1,7 +1,7 @@
/* wangeditor富文本编辑器 /* wangeditor 富文本编辑器
------------------------------- */ ------------------------------- */
.editor-container { .editor-container {
z-index: 9999; z-index: 10; // 用于 wangeditor 点击全屏时
.w-e-toolbar { .w-e-toolbar {
border: 1px solid var(--el-border-color-light, #ebeef5) !important; border: 1px solid var(--el-border-color-light, #ebeef5) !important;
border-bottom: 1px solid var(--el-border-color-light, #ebeef5) !important; border-bottom: 1px solid var(--el-border-color-light, #ebeef5) !important;

14
src/types/mitt.ts Normal file
View File

@ -0,0 +1,14 @@
// mitt 事件类型定义
export type MittType = {
openSetingsDrawer?: string; // 打开布局设置弹窗
restoreDefault?: string; // 分栏布局,鼠标移入、移出数据显示
setSendColumnsChildren?: string; // 分栏布局,鼠标移入、移出菜单数据传入到 navMenu 下的菜单中
setSendClassicChildren?: string; // 经典布局,开启切割菜单时,菜单数据传入到 navMenu 下的菜单中
getBreadcrumbIndexSetFilterRoutes?: string; // 布局设置弹窗,开启切割菜单时,菜单数据传入到 navMenu 下的菜单中
layoutMobileResize?: object; // 浏览器窗口改变时,用于适配移动端界面显示
openOrCloseSortable?: string; // 布局设置弹窗,开启 TagsView 拖拽
openShareTagsView?: string; // 布局设置弹窗,开启 TagsView 共用
onTagsViewRefreshRouterView?: any; // tagsview 刷新界面
onCurrentContextmenuClick?: any; // tagsview 右键菜单每项点击时
setHoverPreload?: string; // 分栏菜单鼠标悬停预加载
};

View File

@ -32,11 +32,13 @@ export const NextLoading = {
window.nextLoading = true; window.nextLoading = true;
}, },
// 移除 loading // 移除 loading
done: () => { done: (time: number = 0) => {
nextTick(() => { nextTick(() => {
window.nextLoading = false; setTimeout(() => {
const el = <HTMLElement>document.querySelector('.loading-next'); window.nextLoading = false;
el?.parentNode?.removeChild(el); const el = <HTMLElement>document.querySelector('.loading-next');
el?.parentNode?.removeChild(el);
}, time);
}); });
}, },
}; };

9
src/utils/mitt.ts Normal file
View File

@ -0,0 +1,9 @@
// https://www.npmjs.com/package/mitt
import mitt, { Emitter } from 'mitt';
import { MittType } from '/@/types/mitt';
// 类型
const emitter: Emitter<MittType> = mitt<MittType>();
// 导出
export default emitter;

View File

@ -51,10 +51,10 @@ export function setTagsViewNameI18n(item: any) {
let tagsViewName: any = ''; let tagsViewName: any = '';
const { query, params, meta } = item; const { query, params, meta } = item;
if (query?.tagsViewName || params?.tagsViewName) { if (query?.tagsViewName || params?.tagsViewName) {
if (/\/zh-cn|en|zh-tw\//.test(query?.tagsViewName) || /\/(zh-cn|en|zh-tw)\//.test(params?.tagsViewName)) { if (/\/zh-cn|en|zh-tw\//.test(query?.tagsViewName) || /\/zh-cn|en|zh-tw\//.test(params?.tagsViewName)) {
// 国际化 // 国际化
const urlTagsParams = (query?.tagsViewName && JSON.parse(query?.tagsViewName)) || (params?.tagsViewName && JSON.parse(params?.tagsViewName)); const urlTagsParams = (query?.tagsViewName && JSON.parse(query?.tagsViewName)) || (params?.tagsViewName && JSON.parse(params?.tagsViewName));
tagsViewName = urlTagsParams[i18n.global.locale]; tagsViewName = urlTagsParams[i18n.global.locale.value];
} else { } else {
// 非国际化 // 非国际化
tagsViewName = query?.tagsViewName || params?.tagsViewName; tagsViewName = query?.tagsViewName || params?.tagsViewName;

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="chart-scrollbar layout-view-bg-white" :style="{ height: `calc(100vh - ${initTagViewHeight}` }"> <div class="chart-scrollbar layout-padding">
<div class="chart-warp"> <div class="chart-warp layout-padding-auto layout-padding-view">
<div class="chart-warp-top"> <div class="chart-warp-top">
<ChartHead /> <ChartHead />
</div> </div>
@ -202,12 +202,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { toRefs, reactive, computed, onMounted, getCurrentInstance, watch, nextTick, onActivated, defineComponent } from 'vue'; import { toRefs, reactive, onMounted, watch, nextTick, onActivated, defineComponent, ref } from 'vue';
import ChartHead from '/@/views/chart/head.vue'; import ChartHead from '/@/views/chart/head.vue';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import 'echarts-wordcloud'; import 'echarts-wordcloud';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes'; import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { skyList, dBtnList, chartData4List } from '/@/views/chart/chart'; import { skyList, dBtnList, chartData4List } from '/@/views/chart/chart';
@ -215,10 +214,12 @@ export default defineComponent({
name: 'chartIndex', name: 'chartIndex',
components: { ChartHead }, components: { ChartHead },
setup() { setup() {
const { proxy } = <any>getCurrentInstance(); const chartsCenterOneRef = ref();
const storesThemeConfig = useThemeConfig(); const chartsSevenDaysRef = ref();
const chartsWarningRef = ref();
const chartsMonitorRef = ref();
const chartsInvestmentRef = ref();
const storesTagsViewRoutes = useTagsViewRoutes(); const storesTagsViewRoutes = useTagsViewRoutes();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes); const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
const state = reactive({ const state = reactive({
skyList, skyList,
@ -226,19 +227,9 @@ export default defineComponent({
chartData4List, chartData4List,
myCharts: [], myCharts: [],
}); });
//
const initTagViewHeight = computed(() => {
let { isTagsview } = themeConfig.value;
if (isTagsViewCurrenFull.value) {
return `30px`;
} else {
if (isTagsview) return `114px`;
else return `80px`;
}
});
// 1 // 1
const initChartsCenterOne = () => { const initChartsCenterOne = () => {
const myChart = echarts.init(proxy.$refs.chartsCenterOneRef); const myChart = echarts.init(chartsCenterOneRef.value);
const option = { const option = {
grid: { grid: {
top: 15, top: 15,
@ -299,7 +290,7 @@ export default defineComponent({
}; };
// 7 // 7
const initChartsSevenDays = () => { const initChartsSevenDays = () => {
const myChart = echarts.init(proxy.$refs.chartsSevenDaysRef); const myChart = echarts.init(chartsSevenDaysRef.value);
const option = { const option = {
grid: { grid: {
top: 15, top: 15,
@ -344,7 +335,7 @@ export default defineComponent({
}; };
// 30 // 30
const initChartsWarning = () => { const initChartsWarning = () => {
const myChart = echarts.init(proxy.$refs.chartsWarningRef); const myChart = echarts.init(chartsWarningRef.value);
const option = { const option = {
grid: { grid: {
top: 50, top: 50,
@ -379,7 +370,7 @@ export default defineComponent({
}; };
// //
const initChartsMonitor = () => { const initChartsMonitor = () => {
const myChart = echarts.init(proxy.$refs.chartsMonitorRef); const myChart = echarts.init(chartsMonitorRef.value);
const option = { const option = {
grid: { grid: {
top: 15, top: 15,
@ -419,7 +410,7 @@ export default defineComponent({
}; };
// 7 // 7
const initChartsInvestment = () => { const initChartsInvestment = () => {
const myChart = echarts.init(proxy.$refs.chartsInvestmentRef); const myChart = echarts.init(chartsInvestmentRef.value);
const option = { const option = {
grid: { grid: {
top: 15, top: 15,
@ -480,7 +471,11 @@ export default defineComponent({
} }
); );
return { return {
initTagViewHeight, chartsCenterOneRef,
chartsSevenDaysRef,
chartsWarningRef,
chartsMonitorRef,
chartsInvestmentRef,
...toRefs(state), ...toRefs(state),
}; };
}, },

View File

@ -1,39 +1,34 @@
<template> <template>
<div class="error layout-view-bg-white" :style="{ height: `calc(100vh - ${initTagViewHeight}` }"> <div class="error layout-padding">
<div class="error-flex"> <div class="layout-padding-auto layout-padding-view">
<div class="left"> <div class="error-flex">
<div class="left-item"> <div class="left">
<div class="left-item-animation left-item-num">401</div> <div class="left-item">
<div class="left-item-animation left-item-title">{{ $t('message.noAccess.accessTitle') }}</div> <div class="left-item-animation left-item-num">401</div>
<div class="left-item-animation left-item-msg">{{ $t('message.noAccess.accessMsg') }}</div> <div class="left-item-animation left-item-title">{{ $t('message.noAccess.accessTitle') }}</div>
<div class="left-item-animation left-item-btn"> <div class="left-item-animation left-item-msg">{{ $t('message.noAccess.accessMsg') }}</div>
<el-button type="primary" round @click="onSetAuth">{{ $t('message.noAccess.accessBtn') }}</el-button> <div class="left-item-animation left-item-btn">
<el-button type="primary" size="default" round @click="onSetAuth">{{ $t('message.noAccess.accessBtn') }}</el-button>
</div>
</div> </div>
</div> </div>
</div> <div class="right">
<div class="right"> <img
<img src="https://img-blog.csdnimg.cn/3333f265772a4fa89287993500ecbf96.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbHl0LXRvcA==,size_16,color_FFFFFF,t_70,g_se,x_16"
src="https://img-blog.csdnimg.cn/3333f265772a4fa89287993500ecbf96.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbHl0LXRvcA==,size_16,color_FFFFFF,t_70,g_se,x_16" />
/> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, computed } from 'vue'; import { defineComponent } from 'vue';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { Session } from '/@/utils/storage'; import { Session } from '/@/utils/storage';
export default defineComponent({ export default defineComponent({
name: '401', name: '401',
setup() { setup() {
const storesThemeConfig = useThemeConfig();
const storesTagsViewRoutes = useTagsViewRoutes();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
const onSetAuth = () => { const onSetAuth = () => {
// https://gitee.com/lyt-top/vue-next-admin/issues/I5C3JS // https://gitee.com/lyt-top/vue-next-admin/issues/I5C3JS
// /token // /token
@ -41,19 +36,8 @@ export default defineComponent({
// 使 reload resetRoute() // 使 reload resetRoute()
window.location.reload(); window.location.reload();
}; };
//
const initTagViewHeight = computed(() => {
let { isTagsview } = themeConfig.value;
if (isTagsViewCurrenFull.value) {
return `30px`;
} else {
if (isTagsview) return `114px`;
else return `80px`;
}
});
return { return {
onSetAuth, onSetAuth,
initTagViewHeight,
}; };
}, },
}); });
@ -62,8 +46,6 @@ export default defineComponent({
<style scoped lang="scss"> <style scoped lang="scss">
.error { .error {
height: 100%; height: 100%;
background-color: var(--el-color-white);
display: flex;
.error-flex { .error-flex {
margin: auto; margin: auto;
display: flex; display: flex;

View File

@ -1,56 +1,40 @@
<template> <template>
<div class="error layout-view-bg-white" :style="{ height: `calc(100vh - ${initTagViewHeight}` }"> <div class="error layout-padding">
<div class="error-flex"> <div class="layout-padding-auto layout-padding-view">
<div class="left"> <div class="error-flex">
<div class="left-item"> <div class="left">
<div class="left-item-animation left-item-num">404</div> <div class="left-item">
<div class="left-item-animation left-item-title">{{ $t('message.notFound.foundTitle') }}</div> <div class="left-item-animation left-item-num">404</div>
<div class="left-item-animation left-item-msg">{{ $t('message.notFound.foundMsg') }}</div> <div class="left-item-animation left-item-title">{{ $t('message.notFound.foundTitle') }}</div>
<div class="left-item-animation left-item-btn"> <div class="left-item-animation left-item-msg">{{ $t('message.notFound.foundMsg') }}</div>
<el-button type="primary" round @click="onGoHome">{{ $t('message.notFound.foundBtn') }}</el-button> <div class="left-item-animation left-item-btn">
<el-button type="primary" size="default" round @click="onGoHome">{{ $t('message.notFound.foundBtn') }}</el-button>
</div>
</div> </div>
</div> </div>
</div> <div class="right">
<div class="right"> <img
<img src="https://img-blog.csdnimg.cn/9eb1d85a417f4ed1ba7107f149ce3da1.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbHl0LXRvcA==,size_16,color_FFFFFF,t_70,g_se,x_16"
src="https://img-blog.csdnimg.cn/9eb1d85a417f4ed1ba7107f149ce3da1.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbHl0LXRvcA==,size_16,color_FFFFFF,t_70,g_se,x_16" />
/> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, computed } from 'vue'; import { defineComponent } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
export default defineComponent({ export default defineComponent({
name: '404', name: '404',
setup() { setup() {
const storesThemeConfig = useThemeConfig();
const storesTagsViewRoutes = useTagsViewRoutes();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
const router = useRouter(); const router = useRouter();
const onGoHome = () => { const onGoHome = () => {
router.push('/'); router.push('/');
}; };
//
const initTagViewHeight = computed(() => {
let { isTagsview } = themeConfig.value;
if (isTagsViewCurrenFull.value) {
return `30px`;
} else {
if (isTagsview) return `114px`;
else return `80px`;
}
});
return { return {
onGoHome, onGoHome,
initTagViewHeight,
}; };
}, },
}); });
@ -59,8 +43,6 @@ export default defineComponent({
<style scoped lang="scss"> <style scoped lang="scss">
.error { .error {
height: 100%; height: 100%;
background-color: var(--el-color-white);
display: flex;
.error-flex { .error-flex {
margin: auto; margin: auto;
display: flex; display: flex;

View File

@ -1,18 +1,20 @@
<template> <template>
<el-card shadow="hover" header="复制剪切演示"> <div class="layout-pd">
<el-alert <el-card shadow="hover" header="复制剪切演示">
title="感谢优秀的 `vue-clipboard3`项目地址https://github.com/JamieCurnow/vue-clipboard3`" <el-alert
type="success" title="感谢优秀的 `vue-clipboard3`项目地址https://github.com/JamieCurnow/vue-clipboard3`"
:closable="false" type="success"
class="mb15" :closable="false"
></el-alert> class="mb15"
<el-input placeholder="请输入内容" v-model="copyVal"> ></el-alert>
<template #append> <el-input placeholder="请输入内容" v-model="copyVal">
<el-button @click="copyText(copyVal)">复制链接</el-button> <template #append>
</template> <el-button @click="copyText(copyVal)">复制链接</el-button>
</el-input> </template>
<el-input placeholder="先点击上方 `复制链接` 按钮,然后 `Ctrl + V` 进行粘贴! " v-model="shearVal" class="mt15"> </el-input> </el-input>
</el-card> <el-input placeholder="先点击上方 `复制链接` 按钮,然后 `Ctrl + V` 进行粘贴! " v-model="shearVal" class="mt15"> </el-input>
</el-card>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="layout-pd">
<el-card shadow="hover" header="数字滚动演示"> <el-card shadow="hover" header="数字滚动演示">
<el-alert <el-alert
title="感谢优秀的 `countup.js`项目地址https://github.com/inorganik/countUp.js" title="感谢优秀的 `countup.js`项目地址https://github.com/inorganik/countUp.js"

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="croppers-container"> <div class="croppers-container layout-pd">
<el-card shadow="hover" header="cropper 图片裁剪"> <el-card shadow="hover" header="cropper 图片裁剪">
<el-alert <el-alert
title="感谢优秀的 `cropperjs`项目地址https://github.com/fengyuanchen/cropperjs" title="感谢优秀的 `cropperjs`项目地址https://github.com/fengyuanchen/cropperjs"
@ -24,16 +24,17 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { ref, toRefs, reactive, defineComponent } from 'vue'; import { defineAsyncComponent, ref, toRefs, reactive, defineComponent } from 'vue';
import CropperDialog from '/@/components/cropper/index.vue';
export default defineComponent({ export default defineComponent({
name: 'funCropper', name: 'funCropper',
components: { CropperDialog }, components: {
CropperDialog: defineAsyncComponent(() => import('/@/components/cropper/index.vue')),
},
setup() { setup() {
const cropperDialogRef = ref(); const cropperDialogRef = ref();
const state = reactive({ const state = reactive({
cropperImg: 'https://img1.baidu.com/it/u=2813520958,2218166536&fm=26&fmt=auto&gp=0.jpg', cropperImg: 'https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500',
}); });
// //
const onCropperDialogOpen = () => { const onCropperDialogOpen = () => {

View File

@ -1,42 +1,25 @@
<template> <template>
<div :style="{ height: `calc(100vh - ${initTagViewHeight}` }"> <div class="layout-padding">
<div class="layout-view-bg-white"> <div class="layout-padding-auto layout-padding-view">
<div ref="echartsMap" style="height: 100%"></div> <div ref="echartsMap" style="height: 100%"></div>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { toRefs, reactive, computed, onMounted, defineComponent } from 'vue'; import { toRefs, reactive, onMounted, defineComponent } from 'vue';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import 'echarts/extension/bmap/bmap'; import 'echarts/extension/bmap/bmap';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { echartsMapList, echartsMapData } from './mock'; import { echartsMapList, echartsMapData } from './mock';
export default defineComponent({ export default defineComponent({
name: 'funEchartsMap', name: 'funEchartsMap',
setup() { setup() {
const storesThemeConfig = useThemeConfig();
const storesTagsViewRoutes = useTagsViewRoutes();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
const state: any = reactive({ const state: any = reactive({
echartsMap: null, echartsMap: null,
echartsMapList, echartsMapList,
echartsMapData, echartsMapData,
}); });
//
const initTagViewHeight = computed(() => {
let { isTagsview } = themeConfig.value;
if (isTagsViewCurrenFull.value) {
return `30px`;
} else {
if (isTagsview) return `114px`;
else return `80px`;
}
});
// echartsMap // echartsMap
const convertData = (data: any) => { const convertData = (data: any) => {
let res = []; let res = [];
@ -133,7 +116,6 @@ export default defineComponent({
initEchartsMap(); initEchartsMap();
}); });
return { return {
initTagViewHeight,
...toRefs(state), ...toRefs(state),
}; };
}, },

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="grid-layout-container"> <div class="grid-layout-container layout-pd">
<el-card shadow="hover" header="vue-grid-layout 拖拽布局演示"> <el-card shadow="hover" header="vue-grid-layout 拖拽布局演示">
<el-alert <el-alert
title="感谢优秀的 `vue-grid-layout`项目地址https://github.com/jbaysolutions/vue-grid-layout" title="感谢优秀的 `vue-grid-layout`项目地址https://github.com/jbaysolutions/vue-grid-layout"

View File

@ -1,5 +1,5 @@
<template> <template>
<div ref="printRef"> <div ref="printRef" class="layout-pd">
<el-card shadow="hover" header="打印演示"> <el-card shadow="hover" header="打印演示">
<el-alert <el-alert
title="感谢优秀的 `print-js`项目地址https://github.com/crabbly/Print.js。请在打印弹窗 `更多设置` 中开启 `背景图形。`" title="感谢优秀的 `print-js`项目地址https://github.com/crabbly/Print.js。请在打印弹窗 `更多设置` 中开启 `背景图形。`"

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="qrcode-container"> <div class="qrcode-container layout-pd">
<el-card shadow="hover" header="qrcodejs2 二维码生成"> <el-card shadow="hover" header="qrcodejs2 二维码生成">
<el-alert <el-alert
title="感谢优秀的 `qrcodejs2`项目地址https://github.com/davidshimjs/qrcodejs" title="感谢优秀的 `qrcodejs2`项目地址https://github.com/davidshimjs/qrcodejs"
@ -23,19 +23,19 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { toRefs, reactive, onMounted, getCurrentInstance, defineComponent } from 'vue'; import { toRefs, reactive, onMounted, defineComponent, ref } from 'vue';
import QRCode from 'qrcodejs2-fixes'; import QRCode from 'qrcodejs2-fixes';
export default defineComponent({ export default defineComponent({
name: 'funQrcode', name: 'funQrcode',
setup() { setup() {
const { proxy } = getCurrentInstance() as any; const qrcodeRef = ref();
const state = reactive({ const state = reactive({
qrcode: '', qrcode: '',
}); });
// //
const initQrcode = () => { const initQrcode = () => {
new QRCode(proxy.$refs.qrcodeRef, { new QRCode(qrcodeRef.value, {
text: `https://lyt-top.gitee.io/vue-next-admin-preview/#/login?t=${new Date().getTime()}`, text: `https://lyt-top.gitee.io/vue-next-admin-preview/#/login?t=${new Date().getTime()}`,
width: 125, width: 125,
height: 125, height: 125,
@ -45,7 +45,7 @@ export default defineComponent({
}; };
// //
const onInitQrcode = () => { const onInitQrcode = () => {
proxy.$refs.qrcodeRef.innerHTML = ''; qrcodeRef.value.innerHTML = '';
initQrcode(); initQrcode();
}; };
// //
@ -53,6 +53,7 @@ export default defineComponent({
initQrcode(); initQrcode();
}); });
return { return {
qrcodeRef,
onInitQrcode, onInitQrcode,
...toRefs(state), ...toRefs(state),
}; };

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="splitpanes-container"> <div class="splitpanes-container layout-pd">
<el-card shadow="hover" header="splitpanes 窗格拆分器"> <el-card shadow="hover" header="splitpanes 窗格拆分器">
<el-alert <el-alert
title="感谢优秀的 `splitpanes`项目地址https://github.com/antoniandre/splitpanes" title="感谢优秀的 `splitpanes`项目地址https://github.com/antoniandre/splitpanes"

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="fun-tagsview"> <div class="fun-tagsview layout-pd">
<NoticeBar <NoticeBar
text="已删除非当前页 tagsView 演示后续有时间可以再加回来tagsview 支持多标签(参数不同)、单标签共用(参数不同)" text="已删除非当前页 tagsView 演示后续有时间可以再加回来tagsview 支持多标签(参数不同)、单标签共用(参数不同)"
background="#ecf5ff" background="#ecf5ff"
@ -63,37 +63,38 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { getCurrentInstance, reactive, toRefs, defineComponent } from 'vue'; import { defineAsyncComponent, reactive, toRefs, defineComponent } from 'vue';
import NoticeBar from '/@/components/noticeBar/index.vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import mittBus from '/@/utils/mitt';
export default defineComponent({ export default defineComponent({
name: 'funTagsView', name: 'funTagsView',
components: { NoticeBar }, components: {
NoticeBar: defineAsyncComponent(() => import('/@/components/noticeBar/index.vue')),
},
setup() { setup() {
const { proxy } = getCurrentInstance() as any;
const route = useRoute(); const route = useRoute();
const state = reactive({}); const state = reactive({});
// 0 1 2 3 4 // 0 1 2 3 4
// 1 tagsView // 1 tagsView
const refreshCurrentTagsView = () => { const refreshCurrentTagsView = () => {
proxy.mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 0, ...route })); mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 0, ...route }));
}; };
// 2 tagsView // 2 tagsView
const closeCurrentTagsView = () => { const closeCurrentTagsView = () => {
proxy.mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 1, ...route })); mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 1, ...route }));
}; };
// 3 tagsView // 3 tagsView
const closeOtherTagsView = () => { const closeOtherTagsView = () => {
proxy.mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 2, ...route })); mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 2, ...route }));
}; };
// 4 tagsView // 4 tagsView
const closeAllTagsView = () => { const closeAllTagsView = () => {
proxy.mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 3, ...route })); mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 3, ...route }));
}; };
// 5 // 5
const openCurrenFullscreen = () => { const openCurrenFullscreen = () => {
proxy.mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 4, ...route })); mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 4, ...route }));
}; };
return { return {
refreshCurrentTagsView, refreshCurrentTagsView,

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="editor-container"> <div class="editor-container layout-pd">
<el-card shadow="hover" header="wangeditor富文本编辑器"> <el-card shadow="hover" header="wangeditor富文本编辑器">
<el-alert <el-alert
title="感谢优秀的 `wangeditor`项目地址https://github.com/wangeditor-team/wangEditor" title="感谢优秀的 `wangeditor`项目地址https://github.com/wangeditor-team/wangEditor"
@ -7,24 +7,26 @@
:closable="false" :closable="false"
class="mb15" class="mb15"
></el-alert> ></el-alert>
<Editor :is-disable="false" v-model="editorVal" /> <Editor v-model="editor.value" :disable="editor.disable" />
</el-card> </el-card>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { toRefs, reactive, onMounted, defineComponent } from 'vue'; import { defineAsyncComponent, toRefs, reactive, defineComponent } from 'vue';
import Editor from '/@/components/editor/index.vue';
export default defineComponent({ export default defineComponent({
name: 'funWangEditor', name: 'funWangEditor',
components: { Editor }, components: {
Editor: defineAsyncComponent(() => import('/@/components/editor/index.vue')),
},
setup() { setup() {
const state = reactive({ const state = reactive({
editorVal: '', editor: {
value: '<p><span style="color: rgb(51, 51, 51); background-color: rgb(255, 255, 255); font-size: 14px;">胡歌1982年9月20日出生于上海市徐汇区中国内地影视男演员、流行乐歌手</span><a href="https://baike.baidu.com/item/%E6%B0%91%E7%9B%9F/1971441?fromModule=lemma_inlink" target="_blank" style="text-indent: 28px; text-align: start;">民盟</a><span style="color: rgb(51, 51, 51); background-color: rgb(255, 255, 255); font-size: 14px;">盟员</span><span style="color: rgb(51, 102, 204); background-color: rgb(255, 255, 255); font-size: 12px;"><sup> [1]</sup></span><a href="" target="" style="text-indent: 28px; text-align: start;"> </a><span style="color: rgb(51, 51, 51); background-color: rgb(255, 255, 255); font-size: 14px;"> ,毕业于</span><a href="https://baike.baidu.com/item/%E4%B8%8A%E6%B5%B7%E6%88%8F%E5%89%A7%E5%AD%A6%E9%99%A2/1736818?fromModule=lemma_inlink" target="_blank" style="text-indent: 28px; text-align: start;">上海戏剧学院</a><span style="color: rgb(51, 51, 51); background-color: rgb(255, 255, 255); font-size: 14px;">表演系。</span></p>',
disable: false,
},
}); });
//
onMounted(() => {});
return { return {
...toRefs(state), ...toRefs(state),
}; };

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="home-container"> <div class="home-container layout-pd">
<el-row :gutter="15" class="home-card-one mb15"> <el-row :gutter="15" class="home-card-one mb15">
<el-col <el-col
:xs="24" :xs="24"

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="layout-pd">
<el-alert <el-alert
title="温馨提示1此页面无法模拟后端控制路由因为 `gitee` 上所请求的 `json` 菜单数据线上会出现跨域的情况json地址 title="温馨提示1此页面无法模拟后端控制路由因为 `gitee` 上所请求的 `json` 菜单数据线上会出现跨域的情况json地址
https://gitee.com/lyt-top/vue-next-admin-images/raw/master/menu/adminMenu.json2`/src/api/menu/index.ts` https://gitee.com/lyt-top/vue-next-admin-images/raw/master/menu/adminMenu.json2`/src/api/menu/index.ts`

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div class="layout-pd">
<LimitsFrontEndPage /> <LimitsFrontEndPage style="padding: 0 !important" />
<!-- 演示1组件方式 --> <!-- 演示1组件方式 -->
<el-card shadow="hover" header="演示1组件方式" class="mt15"> <el-card shadow="hover" header="演示1组件方式" class="mt15">
<el-row class="mb10" style="color: #808080">单个权限验证:value="xxx"</el-row> <el-row class="mb10" style="color: #808080">单个权限验证:value="xxx"</el-row>
@ -335,17 +335,18 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineAsyncComponent, defineComponent } from 'vue';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import LimitsFrontEndPage from '/@/views/limits/frontEnd/page/index.vue';
import Auth from '/@/components/auth/auth.vue';
import Auths from '/@/components/auth/auths.vue';
import AuthAll from '/@/components/auth/authAll.vue';
import { auth, auths, authAll } from '/@/utils/authFunction'; import { auth, auths, authAll } from '/@/utils/authFunction';
export default defineComponent({ export default defineComponent({
name: 'limitsFrontEndBtn', name: 'limitsFrontEndBtn',
components: { LimitsFrontEndPage, Auth, Auths, AuthAll }, components: {
LimitsFrontEndPage: defineAsyncComponent(() => import('/@/views/limits/frontEnd/page/index.vue')),
Auth: defineAsyncComponent(() => import('/@/components/auth/auth.vue')),
Auths: defineAsyncComponent(() => import('/@/components/auth/auths.vue')),
AuthAll: defineAsyncComponent(() => import('/@/components/auth/authAll.vue')),
},
setup() { setup() {
// //
const onAuthClick = () => { const onAuthClick = () => {

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="layout-pd">
<el-alert <el-alert
title="温馨提示此权限页面代码及效果只作为演示使用若出现不可逆转的bug请尝试 `F5` 刷新页面若实际项目中非要实现此用户权限切换功能 title="温馨提示此权限页面代码及效果只作为演示使用若出现不可逆转的bug请尝试 `F5` 刷新页面若实际项目中非要实现此用户权限切换功能
请在切换方法 `onRadioChange` 最后面添加刷新代码 `window.location.reload()` 请注意按钮权限页面中的演示2指令模式演示3函数模式 请在切换方法 `onRadioChange` 最后面添加刷新代码 `window.location.reload()` 请注意按钮权限页面中的演示2指令模式演示3函数模式

View File

@ -1,7 +1,7 @@
<template> <template>
<el-form size="large" class="login-content-form"> <el-form size="large" class="login-content-form">
<el-form-item class="login-animation1"> <el-form-item class="login-animation1">
<el-input type="text" :placeholder="$t('message.account.accountPlaceholder1')" v-model="ruleForm.userName" clearable autocomplete="off"> <el-input text :placeholder="$t('message.account.accountPlaceholder1')" v-model="ruleForm.userName" clearable autocomplete="off">
<template #prefix> <template #prefix>
<el-icon class="el-input__icon"><ele-User /></el-icon> <el-icon class="el-input__icon"><ele-User /></el-icon>
</template> </template>
@ -29,14 +29,7 @@
</el-form-item> </el-form-item>
<el-form-item class="login-animation3"> <el-form-item class="login-animation3">
<el-col :span="15"> <el-col :span="15">
<el-input <el-input text maxlength="4" :placeholder="$t('message.account.accountPlaceholder3')" v-model="ruleForm.code" clearable autocomplete="off">
type="text"
maxlength="4"
:placeholder="$t('message.account.accountPlaceholder3')"
v-model="ruleForm.code"
clearable
autocomplete="off"
>
<template #prefix> <template #prefix>
<el-icon class="el-input__icon"><ele-Position /></el-icon> <el-icon class="el-input__icon"><ele-Position /></el-icon>
</template> </template>
@ -44,11 +37,11 @@
</el-col> </el-col>
<el-col :span="1"></el-col> <el-col :span="1"></el-col>
<el-col :span="8"> <el-col :span="8">
<el-button class="login-content-code">1234</el-button> <el-button class="login-content-code" v-waves>1234</el-button>
</el-col> </el-col>
</el-form-item> </el-form-item>
<el-form-item class="login-animation4"> <el-form-item class="login-animation4">
<el-button type="primary" class="login-content-submit" round @click="onSignIn" :loading="loading.signIn"> <el-button type="primary" class="login-content-submit" round v-waves @click="onSignIn" :loading="loading.signIn">
<span>{{ $t('message.account.accountBtnText') }}</span> <span>{{ $t('message.account.accountBtnText') }}</span>
</el-button> </el-button>
</el-form-item> </el-form-item>

View File

@ -1,7 +1,7 @@
<template> <template>
<el-form size="large" class="login-content-form"> <el-form size="large" class="login-content-form">
<el-form-item class="login-animation1"> <el-form-item class="login-animation1">
<el-input type="text" :placeholder="$t('message.mobile.placeholder1')" v-model="ruleForm.userName" clearable autocomplete="off"> <el-input text :placeholder="$t('message.mobile.placeholder1')" v-model="ruleForm.userName" clearable autocomplete="off">
<template #prefix> <template #prefix>
<i class="iconfont icon-dianhua el-input__icon"></i> <i class="iconfont icon-dianhua el-input__icon"></i>
</template> </template>
@ -9,7 +9,7 @@
</el-form-item> </el-form-item>
<el-form-item class="login-animation2"> <el-form-item class="login-animation2">
<el-col :span="15"> <el-col :span="15">
<el-input type="text" maxlength="4" :placeholder="$t('message.mobile.placeholder2')" v-model="ruleForm.code" clearable autocomplete="off"> <el-input text maxlength="4" :placeholder="$t('message.mobile.placeholder2')" v-model="ruleForm.code" clearable autocomplete="off">
<template #prefix> <template #prefix>
<el-icon class="el-input__icon"><ele-Position /></el-icon> <el-icon class="el-input__icon"><ele-Position /></el-icon>
</template> </template>
@ -17,11 +17,11 @@
</el-col> </el-col>
<el-col :span="1"></el-col> <el-col :span="1"></el-col>
<el-col :span="8"> <el-col :span="8">
<el-button class="login-content-code">{{ $t('message.mobile.codeText') }}</el-button> <el-button v-waves class="login-content-code">{{ $t('message.mobile.codeText') }}</el-button>
</el-col> </el-col>
</el-form-item> </el-form-item>
<el-form-item class="login-animation3"> <el-form-item class="login-animation3">
<el-button round type="primary" class="login-content-submit"> <el-button round type="primary" v-waves class="login-content-submit">
<span>{{ $t('message.mobile.btnText') }}</span> <span>{{ $t('message.mobile.btnText') }}</span>
</el-button> </el-button>
</el-form-item> </el-form-item>

View File

@ -1,12 +1,15 @@
<template> <template>
<div class="login-scan-container"> <div class="login-scan-container">
<div ref="qrcodeRef"></div> <div ref="qrcodeRef"></div>
<div class="font12 mt20 login-msg">{{ $t('message.scan.text') }}</div> <div class="font12 mt20 login-msg">
<i class="iconfont icon-saoyisao mr5"></i>
<span>{{ $t('message.scan.text') }}</span>
</div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { ref, defineComponent, onMounted } from 'vue'; import { defineComponent, ref, onMounted, nextTick } from 'vue';
import QRCode from 'qrcodejs2-fixes'; import QRCode from 'qrcodejs2-fixes';
export default defineComponent({ export default defineComponent({
@ -15,20 +18,24 @@ export default defineComponent({
const qrcodeRef = ref<HTMLElement | null>(null); const qrcodeRef = ref<HTMLElement | null>(null);
// //
const initQrcode = () => { const initQrcode = () => {
(qrcodeRef.value as HTMLElement).innerHTML = ''; nextTick(() => {
new QRCode(qrcodeRef.value, { (<HTMLElement>qrcodeRef.value).innerHTML = '';
text: `https://qm.qq.com/cgi-bin/qm/qr?k=RdUY97Vx0T0vZ_1OOu-X1yFNkWgDwbjC&jump_from=webapi`, new QRCode(qrcodeRef.value, {
width: 260, text: `https://qm.qq.com/cgi-bin/qm/qr?k=RdUY97Vx0T0vZ_1OOu-X1yFNkWgDwbjC&jump_from=webapi`,
height: 260, width: 260,
colorDark: '#000000', height: 260,
colorLight: '#ffffff', colorDark: '#000000',
colorLight: '#ffffff',
});
}); });
}; };
// //
onMounted(() => { onMounted(() => {
initQrcode(); initQrcode();
}); });
return { qrcodeRef }; return {
qrcodeRef,
};
}, },
}); });
</script> </script>
@ -41,7 +48,7 @@ export default defineComponent({
animation-fill-mode: forwards; animation-fill-mode: forwards;
} }
.login-scan-container { .login-scan-container {
padding: 20px; padding: 0 20px 20px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
text-align: center; text-align: center;
@ -51,6 +58,9 @@ export default defineComponent({
margin: auto; margin: auto;
} }
.login-msg { .login-msg {
display: flex;
align-items: center;
justify-content: center;
color: var(--el-text-color-placeholder); color: var(--el-text-color-placeholder);
@extend .login-scan-animation; @extend .login-scan-animation;
animation-delay: 0.2s; animation-delay: 0.2s;

View File

@ -1,29 +1,41 @@
<template> <template>
<div class="login-container"> <div class="login-container flex">
<div class="login-icon-group"> <div class="login-left">
<div class="login-icon-group-title"> <div class="login-left-logo">
<img :src="logoMini" /> <img :src="logoMini" />
<div class="login-icon-group-title-text font25">{{ getThemeConfig.globalViceTitle }}</div> <div class="login-left-logo-text">
</div> <span>{{ getThemeConfig.globalViceTitle }}</span>
<img :src="loginIconTwo" class="login-icon-group-icon" /> <span class="login-left-logo-text-msg">{{ getThemeConfig.globalViceTitleMsg }}</span>
</div>
<div class="login-content">
<div class="login-content-main">
<h4 class="login-content-title ml15">{{ getThemeConfig.globalTitle }}后台模板</h4>
<div v-if="!isScan">
<el-tabs v-model="tabsActiveName">
<el-tab-pane :label="$t('message.label.one1')" name="account">
<Account />
</el-tab-pane>
<el-tab-pane :label="$t('message.label.two2')" name="mobile">
<Mobile />
</el-tab-pane>
</el-tabs>
</div> </div>
<Scan v-if="isScan" /> </div>
<div class="login-content-main-sacn" @click="isScan = !isScan"> <div class="login-left-img">
<i class="iconfont" :class="isScan ? 'icon-diannao1' : 'icon-barcode-qr'"></i> <img :src="loginMain" />
<div class="login-content-main-sacn-delta"></div> </div>
<img :src="loginBg" class="login-left-waves" />
</div>
<div class="login-right flex">
<div class="login-right-warp flex-margin">
<span class="login-right-warp-one"></span>
<span class="login-right-warp-two"></span>
<div class="login-right-warp-mian">
<div class="login-right-warp-main-title">{{ getThemeConfig.globalTitle }} 欢迎您</div>
<div class="login-right-warp-main-form">
<div v-if="!isScan">
<el-tabs v-model="tabsActiveName">
<el-tab-pane :label="$t('message.label.one1')" name="account">
<Account />
</el-tab-pane>
<el-tab-pane :label="$t('message.label.two2')" name="mobile">
<Mobile />
</el-tab-pane>
</el-tabs>
</div>
<Scan v-if="isScan" />
<div class="login-content-main-sacn" @click="isScan = !isScan">
<i class="iconfont" :class="isScan ? 'icon-diannao1' : 'icon-barcode-qr'"></i>
<div class="login-content-main-sacn-delta"></div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -31,29 +43,25 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { toRefs, reactive, computed, defineComponent, onMounted } from 'vue'; import { defineAsyncComponent, defineComponent, onMounted, reactive, toRefs, computed } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig'; import { useThemeConfig } from '/@/stores/themeConfig';
import logoMini from '/@/assets/logo-mini.svg';
import loginIconTwo from '/@/assets/login-icon-two.svg';
import { NextLoading } from '/@/utils/loading'; import { NextLoading } from '/@/utils/loading';
import Account from '/@/views/login/component/account.vue'; import logoMini from '/@/assets/logo-mini.svg';
import Mobile from '/@/views/login/component/mobile.vue'; import loginMain from '/@/assets/login-main.svg';
import Scan from '/@/views/login/component/scan.vue'; import loginBg from '/@/assets/login-bg.svg';
//
interface LoginState {
tabsActiveName: string;
isScan: boolean;
}
export default defineComponent({ export default defineComponent({
name: 'loginIndex', name: 'loginIndex',
components: { Account, Mobile, Scan }, components: {
Account: defineAsyncComponent(() => import('/@/views/login/component/account.vue')),
Mobile: defineAsyncComponent(() => import('/@/views/login/component/mobile.vue')),
Scan: defineAsyncComponent(() => import('/@/views/login/component/scan.vue')),
},
setup() { setup() {
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig); const { themeConfig } = storeToRefs(storesThemeConfig);
const state = reactive<LoginState>({ const state = reactive({
tabsActiveName: 'account', tabsActiveName: 'account',
isScan: false, isScan: false,
}); });
@ -67,7 +75,8 @@ export default defineComponent({
}); });
return { return {
logoMini, logoMini,
loginIconTwo, loginBg,
loginMain,
getThemeConfig, getThemeConfig,
...toRefs(state), ...toRefs(state),
}; };
@ -77,122 +86,176 @@ export default defineComponent({
<style scoped lang="scss"> <style scoped lang="scss">
.login-container { .login-container {
width: 100%;
height: 100%; height: 100%;
position: relative;
background: var(--el-color-white); background: var(--el-color-white);
.login-icon-group { .login-left {
width: 100%; flex: 1;
height: 100%;
position: relative; position: relative;
.login-icon-group-title { background-color: rgba(211, 239, 255, 1);
margin-right: 100px;
.login-left-logo {
display: flex;
align-items: center;
position: absolute; position: absolute;
top: 50px; top: 50px;
left: 80px; left: 80px;
display: flex; z-index: 1;
align-items: center; animation: logoAnimation 0.3s ease;
img { img {
width: 30px; width: 52px;
height: 30px; height: 52px;
} }
&-text { .login-left-logo-text {
padding-left: 15px; display: flex;
color: var(--el-color-primary); flex-direction: column;
span {
margin-left: 10px;
font-size: 28px;
color: #26a59a;
}
.login-left-logo-text-msg {
font-size: 12px;
color: #32a99e;
}
} }
} }
&::before { .login-left-img {
content: '';
position: absolute; position: absolute;
bottom: 0; top: 50%;
left: 0; left: 50%;
width: 60%; transform: translate(-50%, -50%);
overflow: hidden; width: 100%;
height: 80%; height: 52%;
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='1200' height='770' xmlns='http://www.w3.org/2000/svg' fill='none'%3E%3Cg%3E%3Cpath id='svg_1' d='M58.4 47.77C104.6 59.51 135.26 67.37 162.11 78.04C188.97 88.72 226.33 102.69 265.92 123.55C305.51 144.4 366.96 167.09 441.43 121.52C515.9 75.95 546.48 61.01 577.69 46.27C608.9 31.53 625.86 23.69 680.26 12.28C734.65 0.87 837.29 10.7 867.29 21.8C897.29 32.9 935.51 51.9 962.21 95.45C988.9 139.01 972.91 177.36 951.37 221.39C929.83 265.43 883.49 306 890.44 337.33C897.4 368.66 974.73 412.18 974.73 411.47C974.73 412.18 1066.36 457.62 1106.36 491.06C1146.36 524.5 1178.8 563.36 1184.03 579.63C1189.26 595.9 1200.4 622.49 1181.55 676.88C1162.71 731.26 1127.16 764.32 1115.31 778.64C1103.45 792.96 5.34 783.61 4.32 784.63C3.3 785.65 -172.34 2.38 1.13 35.04L58.4 47.77L58.4 47.77Z' fill='%23409eff'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"); img {
background: var(--el-color-primary-light-5); width: 100%;
transition: all 0.3s ease; height: 100%;
animation: error-num 0.6s ease;
}
} }
&::after { .login-left-waves {
content: '';
width: 150px;
height: 300px;
position: absolute; position: absolute;
right: 0;
top: 0; top: 0;
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='150' height='300' xmlns='http://www.w3.org/2000/svg' fill='none'%3E%3Cg%3E%3Cpath id='svg_1' d='M-0.56 -0.28C41.94 36.17 67.73 18.94 93.33 33.96C118.93 48.98 107.58 73.56 101.94 89.76C96.29 105.96 50.09 217.83 47.87 231.18C45.64 244.52 46.02 255.2 64.4 270.05C82.79 284.91 121.99 292.31 111.98 289.81C101.97 287.32 153.96 301.48 151.83 299.9C149.69 298.32 149.98 -1.36 149.71 -1.18C149.98 -1.36 -43.06 -36.74 -0.56 -0.28L-0.56 -0.28Z' fill='%23409eff'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"); right: -100px;
background: var(--el-color-primary-light-5);
transition: all 0.3s ease;
}
&-icon {
width: 60%;
height: 70%;
position: absolute;
left: 0;
bottom: 0;
} }
} }
.login-content { .login-right {
width: 500px; width: 700px;
padding: 20px; .login-right-warp {
position: absolute; border: 1px solid var(--el-color-primary-light-3);
right: 200px; border-radius: 3px;
top: 50%; width: 500px;
transform: translateY(-50%) translate3d(0, 0, 0); height: 500px;
background-color: var(--el-color-white); position: relative;
border: 5px solid var(--el-color-primary-light-8);
border-radius: 5px;
overflow: hidden;
z-index: 1;
height: 460px;
.login-content-main {
margin: 0 auto;
width: 80%;
.login-content-title {
color: var(--el-text-color-primary);
font-weight: 500;
font-size: 22px;
text-align: center;
letter-spacing: 4px;
margin: 15px 0 30px;
white-space: nowrap;
z-index: 5;
position: relative;
transition: all 0.3s ease;
}
}
.login-content-main-sacn {
position: absolute;
top: 0;
right: 0;
width: 50px;
height: 50px;
overflow: hidden; overflow: hidden;
cursor: pointer; background-color: var(--el-color-white);
transition: all ease 0.3s; .login-right-warp-one,
color: var(--el-text-color-primary); .login-right-warp-two {
&-delta {
position: absolute; position: absolute;
width: 35px; display: block;
height: 70px; width: inherit;
z-index: 2; height: inherit;
top: 2px; &::before,
right: 21px; &::after {
background: var(--el-color-white); content: '';
transform: rotate(-45deg); position: absolute;
z-index: 1;
}
} }
&:hover { .login-right-warp-one {
opacity: 1; &::before {
transition: all ease 0.3s; filter: hue-rotate(0deg);
color: var(--el-color-primary) !important; top: 0px;
left: 0;
width: 100%;
height: 3px;
background: linear-gradient(90deg, transparent, var(--el-color-primary));
animation: loginLeft 3s linear infinite;
}
&::after {
filter: hue-rotate(60deg);
top: -100%;
right: 2px;
width: 3px;
height: 100%;
background: linear-gradient(180deg, transparent, var(--el-color-primary));
animation: loginTop 3s linear infinite;
animation-delay: 0.7s;
}
} }
i { .login-right-warp-two {
width: 47px; &::before {
height: 50px; filter: hue-rotate(120deg);
display: inline-block; bottom: 2px;
font-size: 48px; right: -100%;
position: absolute; width: 100%;
right: 2px; height: 3px;
top: -1px; background: linear-gradient(270deg, transparent, var(--el-color-primary));
animation: loginRight 3s linear infinite;
animation-delay: 1.4s;
}
&::after {
filter: hue-rotate(300deg);
bottom: -100%;
left: 0px;
width: 3px;
height: 100%;
background: linear-gradient(360deg, transparent, var(--el-color-primary));
animation: loginBottom 3s linear infinite;
animation-delay: 2.1s;
}
}
.login-right-warp-mian {
display: flex;
flex-direction: column;
height: 100%;
.login-right-warp-main-title {
height: 130px;
line-height: 130px;
font-size: 27px;
text-align: center;
letter-spacing: 3px;
animation: logoAnimation 0.3s ease;
animation-delay: 0.3s;
}
.login-right-warp-main-form {
flex: 1;
padding: 0 50px 50px;
.login-content-main-sacn {
position: absolute;
top: 0;
right: 0;
width: 50px;
height: 50px;
overflow: hidden;
cursor: pointer;
transition: all ease 0.3s;
color: var(--el-color-primary);
&-delta {
position: absolute;
width: 35px;
height: 70px;
z-index: 2;
top: 2px;
right: 21px;
background: var(--el-color-white);
transform: rotate(-45deg);
}
&:hover {
opacity: 1;
transition: all ease 0.3s;
color: var(--el-color-primary) !important;
}
i {
width: 47px;
height: 50px;
display: inline-block;
font-size: 48px;
position: absolute;
right: 1px;
top: 0px;
}
}
}
} }
} }
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="notice-bar-container"> <div class="notice-bar-container layout-pd">
<el-card shadow="hover" header="滚动通知栏:默认"> <el-card shadow="hover" header="滚动通知栏:默认">
<NoticeBar <NoticeBar
text="🎉🎉🔥基于vue3.x TypescriptviteElement plus等适配手机平板pc text="🎉🎉🔥基于vue3.x TypescriptviteElement plus等适配手机平板pc
@ -48,12 +48,13 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { toRefs, reactive, defineComponent } from 'vue'; import { defineAsyncComponent, toRefs, reactive, defineComponent } from 'vue';
import NoticeBar from '/@/components/noticeBar/index.vue';
export default defineComponent({ export default defineComponent({
name: 'makeNoticeBar', name: 'makeNoticeBar',
components: { NoticeBar }, components: {
NoticeBar: defineAsyncComponent(() => import('/@/components/noticeBar/index.vue')),
},
setup() { setup() {
const state = reactive({ const state = reactive({
noticeList: [ noticeList: [

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="selector-container"> <div class="selector-container layout-pd">
<el-card shadow="hover" header="图标选择器(宽度自动)"> <el-card shadow="hover" header="图标选择器(宽度自动)">
<IconSelector @get="onGetIcon" @clear="onClearIcon" v-model="modelIcon" /> <IconSelector @get="onGetIcon" @clear="onClearIcon" v-model="modelIcon" />
</el-card> </el-card>
@ -26,12 +26,13 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { toRefs, reactive, defineComponent } from 'vue'; import { defineAsyncComponent, toRefs, reactive, defineComponent } from 'vue';
import IconSelector from '/@/components/iconSelector/index.vue';
export default defineComponent({ export default defineComponent({
name: 'makeSelector', name: 'makeSelector',
components: { IconSelector }, components: {
IconSelector: defineAsyncComponent(() => import('/@/components/iconSelector/index.vue')),
},
setup() { setup() {
const state = reactive({ const state = reactive({
modelIcon: '', modelIcon: '',

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="svg-demo-container"> <div class="svg-demo-container layout-pd">
<el-card shadow="hover" header="svgIcon演示支持本地svg"> <el-card shadow="hover" header="svgIcon演示支持本地svg">
<SvgIcon name="iconfont icon-shuju1" color="red" :size="30" /> <SvgIcon name="iconfont icon-shuju1" color="red" :size="30" />
<SvgIcon name="ele-Trophy" color="var(--el-color-primary)" :size="30" /> <SvgIcon name="ele-Trophy" color="var(--el-color-primary)" :size="30" />

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="layout-pd">
<el-input v-model="val" placeholder="menu11请输入内容测试路由缓存"></el-input> <el-input v-model="val" placeholder="menu11请输入内容测试路由缓存"></el-input>
</div> </div>
</template> </template>

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="layout-pd">
<el-input v-model="val" placeholder="menu121请输入内容测试路由缓存"></el-input> <el-input v-model="val" placeholder="menu121请输入内容测试路由缓存"></el-input>
</div> </div>
</template> </template>

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="layout-pd">
<el-input v-model="val" placeholder="menu122请输入内容测试路由缓存"></el-input> <el-input v-model="val" placeholder="menu122请输入内容测试路由缓存"></el-input>
</div> </div>
</template> </template>

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="layout-pd">
<el-input v-model="val" placeholder="menu13请输入内容测试路由缓存"></el-input> <el-input v-model="val" placeholder="menu13请输入内容测试路由缓存"></el-input>
</div> </div>
</template> </template>

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="layout-pd">
<el-input v-model="val" placeholder="menu2请输入内容测试路由缓存"></el-input> <el-input v-model="val" placeholder="menu2请输入内容测试路由缓存"></el-input>
</div> </div>
</template> </template>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="awesome-container"> <div class="awesome-container layout-pd">
<el-card shadow="hover" :header="`fontawesome 字体图标(自动载入)${sheetsIconList.length - 24}个`"> <el-card shadow="hover" :header="`fontawesome 字体图标(自动载入)${sheetsIconList.length - 24}个`">
<el-row class="iconfont-row"> <el-row class="iconfont-row">
<el-col :xs="12" :sm="8" :md="6" :lg="4" :xl="2" v-for="(v, k) in sheetsIconList" :key="k"> <el-col :xs="12" :sm="8" :md="6" :lg="4" :xl="2" v-for="(v, k) in sheetsIconList" :key="k">

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="drag-container"> <div class="drag-container layout-pd">
<el-card shadow="hover" header="拖动指令效果v-drag作用于 Dialog 对话框"> <el-card shadow="hover" header="拖动指令效果v-drag作用于 Dialog 对话框">
<el-button type="primary" @click="dialogVisible = true" size="default"> <el-button type="primary" @click="dialogVisible = true" size="default">
<el-icon> <el-icon>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="dynamic-form-container"> <div class="dynamic-form-container layout-pd">
<el-card shadow="hover" header="动态复杂表单"> <el-card shadow="hover" header="动态复杂表单">
<el-form :model="form" ref="formRulesOneRef" size="default" label-width="100px" class="mt35"> <el-form :model="form" ref="formRulesOneRef" size="default" label-width="100px" class="mt35">
<el-row :gutter="35"> <el-row :gutter="35">
@ -112,7 +112,8 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { toRefs, reactive, onMounted, getCurrentInstance, defineComponent } from 'vue'; import { toRefs, reactive, onMounted, defineComponent, ref } from 'vue';
import { ElMessage } from 'element-plus';
import { formData } from './mock'; import { formData } from './mock';
// //
@ -146,7 +147,7 @@ interface DynamicFormState {
export default defineComponent({ export default defineComponent({
name: 'pagesDynamicForm', name: 'pagesDynamicForm',
setup() { setup() {
const { proxy } = <any>getCurrentInstance(); const formRulesOneRef = ref();
const state = reactive<DynamicFormState>({ const state = reactive<DynamicFormState>({
formData, formData,
form: { form: {
@ -178,9 +179,9 @@ export default defineComponent({
}; };
// //
const onSubmitForm = () => { const onSubmitForm = () => {
proxy.$refs.formRulesOneRef.validate((valid: boolean) => { formRulesOneRef.value.validate((valid: boolean) => {
if (valid) { if (valid) {
proxy.$message.success('验证成功'); ElMessage.success('验证成功');
} else { } else {
return false; return false;
} }
@ -188,11 +189,12 @@ export default defineComponent({
}; };
// //
const onResetForm = () => { const onResetForm = () => {
proxy.$refs.formRulesOneRef.resetFields(); formRulesOneRef.value.resetFields();
}; };
// //
onMounted(() => {}); onMounted(() => {});
return { return {
formRulesOneRef,
onAddRow, onAddRow,
onDelRow, onDelRow,
onSubmitForm, onSubmitForm,

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="element-container"> <div class="element-container layout-pd">
<el-card shadow="hover" :header="`element plus 字体图标(自动载入,增加了 ele- 前缀使用时ele-Aim)${sheetsIconList.length}个`"> <el-card shadow="hover" :header="`element plus 字体图标(自动载入,增加了 ele- 前缀使用时ele-Aim)${sheetsIconList.length}个`">
<el-row class="iconfont-row"> <el-row class="iconfont-row">
<el-col :xs="12" :sm="8" :md="6" :lg="4" :xl="2" v-for="(v, k) in sheetsIconList" :key="k"> <el-col :xs="12" :sm="8" :md="6" :lg="4" :xl="2" v-for="(v, k) in sheetsIconList" :key="k">

View File

@ -1,6 +1,6 @@
<template> <template>
<div :style="{ height: `calc(100vh - ${initTagViewHeight}` }"> <div class="layout-padding">
<div class="layout-view-bg-white"> <div class="layout-padding-auto layout-padding-view">
<div class="w100 h100 flex"> <div class="w100 h100 flex">
<div class="flex-margin color-primary">filtering-details 测试界面</div> <div class="flex-margin color-primary">filtering-details 测试界面</div>
</div> </div>
@ -9,31 +9,9 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
export default defineComponent({ export default defineComponent({
name: 'pagesFilteringDetails', name: 'pagesFilteringDetails',
setup() {
const storesTagsViewRoutes = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
//
const initTagViewHeight = computed(() => {
let { isTagsview } = themeConfig.value;
if (isTagsViewCurrenFull.value) {
return `30px`;
} else {
if (isTagsview) return `114px`;
else return `80px`;
}
});
return {
initTagViewHeight,
};
},
}); });
</script> </script>

View File

@ -1,6 +1,6 @@
<template> <template>
<div :style="{ height: `calc(100vh - ${initTagViewHeight}` }"> <div class="layout-padding">
<div class="layout-view-bg-white"> <div class="layout-padding-auto layout-padding-view">
<div class="w100 h100 flex"> <div class="w100 h100 flex">
<div class="flex-margin color-primary">测试界面</div> <div class="flex-margin color-primary">测试界面</div>
</div> </div>
@ -9,31 +9,9 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
export default defineComponent({ export default defineComponent({
name: 'pagesFilteringDetails1', name: 'pagesFilteringDetails1',
setup() {
const storesTagsViewRoutes = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
//
const initTagViewHeight = computed(() => {
let { isTagsview } = themeConfig.value;
if (isTagsViewCurrenFull.value) {
return `30px`;
} else {
if (isTagsview) return `114px`;
else return `80px`;
}
});
return {
initTagViewHeight,
};
},
}); });
</script> </script>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="filtering"> <div class="filtering layout-pd">
<el-card <el-card
shadow="hover" shadow="hover"
class="filtering-list br-top-no" class="filtering-list br-top-no"

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="form-adapt-container"> <div class="form-adapt-container layout-pd">
<el-card shadow="hover" header="表单自适应演示(改变窗口查看效果)"> <el-card shadow="hover" header="表单自适应演示(改变窗口查看效果)">
<el-form :model="form" size="default" label-width="100px" class="mt35 mb35"> <el-form :model="form" size="default" label-width="100px" class="mt35 mb35">
<el-row :gutter="35"> <el-row :gutter="35">

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="form-i18n-container"> <div class="form-i18n-container layout-pd">
<el-card shadow="hover" header="表单国际化演示(不适用于动态项 form-item)"> <el-card shadow="hover" header="表单国际化演示(不适用于动态项 form-item)">
<div style="text-align: center; margin-top: 15px"> <div style="text-align: center; margin-top: 15px">
<el-radio-group v-model="radio" size="default" @change="onRadioChange"> <el-radio-group v-model="radio" size="default" @change="onRadioChange">
@ -32,12 +32,13 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { toRefs, reactive, defineComponent, getCurrentInstance } from 'vue'; import { toRefs, reactive, defineComponent } from 'vue';
import { useI18n } from 'vue-i18n';
export default defineComponent({ export default defineComponent({
name: 'pagesFormI18n', name: 'pagesFormI18n',
setup() { setup() {
const { proxy } = <any>getCurrentInstance(); const { locale } = useI18n();
const state = reactive({ const state = reactive({
radio: 'zh-cn', radio: 'zh-cn',
form: { form: {
@ -48,7 +49,7 @@ export default defineComponent({
}); });
// //
const onRadioChange = () => { const onRadioChange = () => {
proxy.$i18n.locale = state.radio; locale.value = state.radio;
}; };
return { return {
onRadioChange, onRadioChange,

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="form-rules-container"> <div class="form-rules-container layout-pd">
<el-card shadow="hover" header="表单组件1"> <FormRulesOne :data="formRulesOneData" ref="pagesFormRulesOneRef" /></el-card> <el-card shadow="hover" header="表单组件1"> <FormRulesOne :data="formRulesOneData" ref="pagesFormRulesOneRef" /></el-card>
<el-card shadow="hover" header="表单组件2" class="mt15"><FormRulesTwo ref="pagesFormRulesTwoRef" /> </el-card> <el-card shadow="hover" header="表单组件2" class="mt15"><FormRulesTwo ref="pagesFormRulesTwoRef" /> </el-card>
<el-card shadow="hover" header="表单组件3" class="mt15"> <FormRulesThree ref="pagesFormRulesThreeRef" /></el-card> <el-card shadow="hover" header="表单组件3" class="mt15"> <FormRulesThree ref="pagesFormRulesThreeRef" /></el-card>
@ -19,21 +19,20 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { toRefs, reactive, defineComponent, getCurrentInstance } from 'vue'; import { defineAsyncComponent, toRefs, reactive, defineComponent, ref } from 'vue';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import FormRulesOne from '/@/views/pages/formRules/component/formRulesOne.vue';
import FormRulesTwo from '/@/views/pages/formRules/component/formRulesTwo.vue';
import FormRulesThree from '/@/views/pages/formRules/component/formRulesThree.vue';
export default defineComponent({ export default defineComponent({
name: 'pagesFormRules', name: 'pagesFormRules',
components: { components: {
FormRulesOne, FormRulesOne: defineAsyncComponent(() => import('/@/views/pages/formRules/component/formRulesOne.vue')),
FormRulesTwo, FormRulesTwo: defineAsyncComponent(() => import('/@/views/pages/formRules/component/formRulesTwo.vue')),
FormRulesThree, FormRulesThree: defineAsyncComponent(() => import('/@/views/pages/formRules/component/formRulesThree.vue')),
}, },
setup() { setup() {
const { proxy } = <any>getCurrentInstance(); const pagesFormRulesOneRef = ref();
const pagesFormRulesTwoRef = ref();
const pagesFormRulesThreeRef = ref();
const state = reactive({ const state = reactive({
formRulesOneData: { formRulesOneData: {
name: 'lyt', name: 'lyt',
@ -43,25 +42,25 @@ export default defineComponent({
}, },
}); });
// //
const formRulesValidate = (pageRef: string, sonRef: string) => { const formRulesValidate = (pageRef: any, sonRef: string) => {
return new Promise((resolve) => { return new Promise((resolve) => {
proxy.$refs[pageRef].$refs[sonRef].validate((valid: boolean) => { pageRef.value.$refs[sonRef].validate((valid: boolean) => {
if (valid) resolve(valid); if (valid) resolve(valid);
}); });
}); });
}; };
// //
const formRulesResetFields = () => { const formRulesResetFields = () => {
proxy.$refs.pagesFormRulesOneRef.$refs.formRulesOneRef.resetFields(); pagesFormRulesOneRef.value.$refs.formRulesOneRef.resetFields();
proxy.$refs.pagesFormRulesTwoRef.$refs.formRulesTwoRef.resetFields(); pagesFormRulesTwoRef.value.$refs.formRulesTwoRef.resetFields();
proxy.$refs.pagesFormRulesThreeRef.$refs.formRulesThreeRef.resetFields(); pagesFormRulesThreeRef.value.$refs.formRulesThreeRef.resetFields();
}; };
// //
const onSubmitForm = () => { const onSubmitForm = () => {
Promise.all([ Promise.all([
formRulesValidate('pagesFormRulesOneRef', 'formRulesOneRef'), formRulesValidate(pagesFormRulesOneRef, 'formRulesOneRef'),
formRulesValidate('pagesFormRulesTwoRef', 'formRulesTwoRef'), formRulesValidate(pagesFormRulesTwoRef, 'formRulesTwoRef'),
formRulesValidate('pagesFormRulesThreeRef', 'formRulesThreeRef'), formRulesValidate(pagesFormRulesThreeRef, 'formRulesThreeRef'),
]).then(() => { ]).then(() => {
ElMessage.success('表单全部验证成功'); ElMessage.success('表单全部验证成功');
}); });
@ -71,6 +70,9 @@ export default defineComponent({
formRulesResetFields(); formRulesResetFields();
}; };
return { return {
pagesFormRulesOneRef,
pagesFormRulesTwoRef,
pagesFormRulesThreeRef,
onSubmitForm, onSubmitForm,
onResetForm, onResetForm,
...toRefs(state), ...toRefs(state),

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="iconfont-container"> <div class="iconfont-container layout-pd">
<el-card shadow="hover" :header="`iconfont 字体图标(自动载入)${sheetsIconList.length}个`"> <el-card shadow="hover" :header="`iconfont 字体图标(自动载入)${sheetsIconList.length}个`">
<el-row class="iconfont-row"> <el-row class="iconfont-row">
<el-col :xs="12" :sm="8" :md="6" :lg="4" :xl="2" v-for="(v, k) in sheetsIconList" :key="k"> <el-col :xs="12" :sm="8" :md="6" :lg="4" :xl="2" v-for="(v, k) in sheetsIconList" :key="k">

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="lazy-img-container"> <div class="lazy-img-container layout-pd">
<el-card shadow="hover" header="图片懒加载演示F12 切换到 Network Img下进行图片加载查看"> <el-card shadow="hover" header="图片懒加载演示F12 切换到 Network Img下进行图片加载查看">
<div class="flex-warp" v-if="tableData.data.length > 0"> <div class="flex-warp" v-if="tableData.data.length > 0">
<el-row :gutter="15"> <el-row :gutter="15">

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="list-adapt-container"> <div class="list-adapt-container layout-pd">
<el-card shadow="hover" header="列表自适应演示(改变窗口查看效果)"> <el-card shadow="hover" header="列表自适应演示(改变窗口查看效果)">
<div class="flex-warp" v-if="tableData.data.length > 0"> <div class="flex-warp" v-if="tableData.data.length > 0">
<el-row :gutter="15"> <el-row :gutter="15">

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="preview-container"> <div class="preview-container layout-pd">
<el-card shadow="hover" header="element-plus 大图预览"> <el-card shadow="hover" header="element-plus 大图预览">
<el-image style="width: 100px; height: 100px; border-radius: 5px" :src="url" :preview-src-list="srcList" title="点击查看大图预览"> </el-image> <el-image style="width: 100px; height: 100px; border-radius: 5px" :src="url" :preview-src-list="srcList" title="点击查看大图预览"> </el-image>
</el-card> </el-card>

View File

@ -1,20 +1,20 @@
<template> <template>
<div class="steps-container"> <div class="steps-container layout-pd">
<el-card shadow="hover" header="element-plus 步骤条"> <el-card shadow="hover" header="element-plus 步骤条">
<el-steps :active="stepsActive"> <el-steps :active="stepsActive">
<el-step title="第一步"> <el-step title="第一步">
<template #icon> <template #icon>
<SvgIcon name="iconfont icon-0_round_solid" :size="20" /> <SvgIcon name="iconfont icon-0_round_solid" :size="40" />
</template> </template>
</el-step> </el-step>
<el-step title="第二步"> <el-step title="第二步">
<template #icon> <template #icon>
<SvgIcon name="iconfont icon-2_round_solid" :size="20" /> <SvgIcon name="iconfont icon-2_round_solid" :size="40" />
</template> </template>
</el-step> </el-step>
<el-step title="第三步"> <el-step title="第三步">
<template #icon> <template #icon>
<SvgIcon name="iconfont icon-3_round_solid" :size="20" /> <SvgIcon name="iconfont icon-3_round_solid" :size="40" />
</template> </template>
</el-step> </el-step>
</el-steps> </el-steps>

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