'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 分支)
## 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
`2022.07.10`

View File

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

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

View File

@ -1,5 +1,5 @@
<template>
<el-config-provider :size="getGlobalComponentSize" :locale="i18nLocale">
<el-config-provider :size="getGlobalComponentSize" :locale="getGlobalI18n">
<router-view v-show="themeConfig.lockScreenTime > 1" />
<LockScreen v-if="themeConfig.isLockScreen" />
<Setings ref="setingsRef" v-show="themeConfig.lockScreenTime > 1" />
@ -8,39 +8,39 @@
</template>
<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 { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { useThemeConfig } from '/@/stores/themeConfig';
import other from '/@/utils/other';
import { Local, Session } from '/@/utils/storage';
import mittBus from '/@/utils/mitt';
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({
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() {
const { proxy } = <any>getCurrentInstance();
const { messages, locale } = useI18n();
const setingsRef = ref();
const route = useRoute();
const stores = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const state = reactive({
i18nLocale: null,
});
//
const getGlobalComponentSize = computed(() => {
return other.globalComponentSize();
});
//
const openSetingsDrawer = () => {
setingsRef.value.openDrawer();
};
// i18n
const getGlobalI18n = computed(() => {
return messages.value[locale.value];
});
//
onBeforeMount(() => {
// icon
@ -52,12 +52,8 @@ export default defineComponent({
onMounted(() => {
nextTick(() => {
//
proxy.mittBus.on('openSetingsDrawer', () => {
openSetingsDrawer();
});
// i18nApp.vue el-config-provider
proxy.mittBus.on('getI18nConfig', (locale: string) => {
(state.i18nLocale as string | null) = locale;
mittBus.on('openSetingsDrawer', () => {
setingsRef.value.openDrawer();
});
//
if (Local.get('themeConfig')) {
@ -72,8 +68,7 @@ export default defineComponent({
});
// /i18n
onUnmounted(() => {
proxy.mittBus.off('openSetingsDrawer', () => {});
proxy.mittBus.off('getI18nConfig', () => {});
mittBus.off('openSetingsDrawer', () => {});
});
//
watch(
@ -89,7 +84,7 @@ export default defineComponent({
themeConfig,
setingsRef,
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>
<div class="editor-container">
<div ref="editorToolbar"></div>
<div ref="editorContent" :style="{ height }"></div>
<Toolbar :editor="editorRef" :mode="mode" />
<Editor :mode="mode" :defaultConfig="editorConfig" :style="{ height }" v-model="editorVal" @onCreated="handleCreated" @onChange="handleChange" />
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, watch, defineComponent } from 'vue';
import { createEditor, createToolbar, IEditorConfig, IToolbarConfig, IDomEditor } from '@wangeditor/editor';
// https://www.wangeditor.com/v5/for-frame.html#vue3
import '@wangeditor/editor/dist/css/style.css';
import { toolbarKeys } from './toolbar';
//
interface WangeditorState {
editorToolbar: HTMLDivElement | null;
editorContent: HTMLDivElement | null;
editor: any;
}
import { defineComponent, reactive, toRefs, shallowRef, watch, onBeforeUnmount } from 'vue';
import { Toolbar, Editor } from '@wangeditor/editor-for-vue';
export default defineComponent({
name: 'wngEditor',
components: { Toolbar, Editor },
props: {
// id
id: {
type: String,
default: () => 'wangeditor',
},
//
isDisable: {
disable: {
type: Boolean,
default: () => false,
default: () => true,
},
// placeholder
placeholder: {
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
@ -52,62 +41,46 @@ export default defineComponent({
},
},
setup(props, { emit }) {
const state = reactive<WangeditorState>({
editorToolbar: null,
editor: null,
editorContent: null,
const editorRef = shallowRef();
const state = reactive({
editorConfig: {
placeholder: props.placeholder,
},
editorVal: props.modelValue,
});
//
const wangeditorConfig = () => {
const editorConfig: Partial<IEditorConfig> = { MENU_CONF: {} };
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());
//
const handleCreated = (editor: any) => {
editorRef.value = editor;
};
//
const handleChange = (editor: any) => {
// console.log(editor.getText());
// console.log(editor.getHtml());
emit('update:modelValue', editor.getHtml());
};
(<any>editorConfig).MENU_CONF['uploadImage'] = {
base64LimitSize: 10 * 1024 * 1024,
};
return editorConfig;
};
//
const toolbarConfig = () => {
const toolbarConfig: Partial<IToolbarConfig> = {};
toolbarConfig.toolbarKeys = toolbarKeys;
return toolbarConfig;
};
//
// 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
watch(
() => props.modelValue,
(value) => {
state.editor.clear();
state.editor.dangerouslyInsertHtml(value);
() => props.disable,
(bool) => {
const editor = editorRef.value;
if (editor == null) return;
bool ? editor.disable() : editor.enable();
},
{
deep: true,
}
);
//
onBeforeUnmount(() => {
const editor = editorRef.value;
if (editor == null) return;
editor.destroy();
});
return {
editorRef,
handleCreated,
handleChange,
...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
export const i18n = createI18n({
legacy: false,
silentTranslationWarn: true,
missingWarn: false,
silentFallbackWarn: true,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,100 +1,58 @@
<template>
<el-main class="layout-main">
<el-scrollbar
ref="layoutScrollbarRef"
:class="{
'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-main class="layout-main" :style="isFixedHeader ? `height: calc(100% - ${setMainHeight})` : `minHeight: calc(100% - ${setMainHeight})`">
<el-scrollbar ref="layoutMainScrollbarRef" class="layout-main-scroll" wrap-class="layout-main-scroll" view-class="layout-main-scroll">
<LayoutParentView />
<LayoutFooter v-if="isFooter" />
</el-scrollbar>
<el-backtop target=".layout-backtop .el-scrollbar__wrap" />
</el-main>
</template>
<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 { storeToRefs } from 'pinia';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { useThemeConfig } from '/@/stores/themeConfig';
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({
name: 'layoutMain',
components: { LayoutParentView, Footer },
components: {
LayoutParentView: defineAsyncComponent(() => import('/@/layout/routerView/parent.vue')),
LayoutFooter: defineAsyncComponent(() => import('/@/layout/footer/index.vue')),
},
setup() {
const { proxy } = <any>getCurrentInstance();
const layoutMainScrollbarRef = ref('');
const route = useRoute();
const storesTagsViewRoutes = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const route = useRoute();
const state = reactive<MainState>({
headerHeight: '',
currentRouteMeta: {},
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
// footer /
const isFooter = computed(() => {
return themeConfig.value.isFooter && !route.meta.isIframe;
});
//
const isClassicOrTransverse = computed(() => {
const { layout } = themeConfig.value;
return layout === 'classic' || layout === 'transverse';
// header
const isFixedHeader = computed(() => {
return themeConfig.value.isFixedHeader;
});
//
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 () => {
await initGetMeta();
initHeaderHeight();
NextLoading.done();
onMounted(() => {
NextLoading.done(600);
});
//
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 {
themeConfig,
isClassicOrTransverse,
...toRefs(state),
layoutMainScrollbarRef,
isFooter,
isFixedHeader,
setMainHeight,
};
},
});

View File

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

View File

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

View File

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

View File

@ -1,34 +1,76 @@
<template>
<el-container class="layout-container flex-center">
<Header />
<LayoutHeader />
<el-container class="layout-mian-height-50">
<Aside />
<LayoutAside />
<div class="flex-center layout-backtop">
<TagsView v-if="themeConfig.isTagsview" />
<Main />
<LayoutTagsView v-if="isTagsview" />
<LayoutMain ref="layoutMainRef" />
</div>
</el-container>
<el-backtop target=".layout-backtop .el-main .el-scrollbar__wrap"></el-backtop>
</el-container>
</template>
<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 { 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({
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() {
const layoutMainRef = ref<any>('');
const route = useRoute();
const storesThemeConfig = useThemeConfig();
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,
() => {
updateScrollbar();
},
{
deep: true,
}
);
//
onMounted(() => {
initScrollBarHeight();
});
return {
layoutMainRef,
isTagsview,
};
},
});

View File

@ -1,40 +1,77 @@
<template>
<el-container class="layout-container">
<ColumnsAside />
<div class="layout-columns-warp">
<Aside />
<el-container class="flex-center layout-backtop" :class="{ 'layout-backtop': !isFixedHeader }">
<Header v-if="isFixedHeader" />
<el-scrollbar :class="{ 'layout-backtop': isFixedHeader }">
<Header v-if="!isFixedHeader" />
<Main />
<el-container class="layout-columns-warp layout-container-view h100">
<LayoutAside />
<el-scrollbar ref="layoutScrollbarRef" class="layout-backtop">
<LayoutHeader />
<LayoutMain ref="layoutMainRef" />
</el-scrollbar>
</el-container>
</div>
<el-backtop target=".layout-backtop .el-scrollbar__wrap"></el-backtop>
</el-container>
</template>
<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 { 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({
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() {
const layoutScrollbarRef = ref<any>('');
const layoutMainRef = ref<any>('');
const route = useRoute();
const storesThemeConfig = useThemeConfig();
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 {
isFixedHeader,
layoutScrollbarRef,
layoutMainRef,
};
},
});

View File

@ -1,46 +1,77 @@
<template>
<el-container class="layout-container">
<Aside />
<el-container class="flex-center" :class="{ 'layout-backtop': !isFixedHeader }">
<Header v-if="isFixedHeader" />
<el-scrollbar ref="layoutDefaultsScrollbarRef" :class="{ 'layout-backtop': isFixedHeader }">
<Header v-if="!isFixedHeader" />
<Main />
<LayoutAside />
<el-container class="layout-container-view h100">
<el-scrollbar ref="layoutScrollbarRef" class="layout-backtop">
<LayoutHeader />
<LayoutMain ref="layoutMainRef" />
</el-scrollbar>
</el-container>
<el-backtop target=".layout-backtop .el-scrollbar__wrap"></el-backtop>
</el-container>
</template>
<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 { storeToRefs } from 'pinia';
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 { NextLoading } from '/@/utils/loading';
export default defineComponent({
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() {
const { proxy } = <any>getCurrentInstance();
const layoutScrollbarRef = ref<any>('');
const layoutMainRef = ref<any>('');
const route = useRoute();
const storesThemeConfig = useThemeConfig();
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,
() => {
proxy.$refs.layoutDefaultsScrollbarRef.wrap$.scrollTop = 0;
initScrollBarHeight();
}
);
// themeConfig el-scrollbar
watch(
themeConfig,
() => {
updateScrollbar();
},
{
deep: true,
}
);
//
onMounted(() => {
initScrollBarHeight();
NextLoading.done(600);
});
return {
isFixedHeader,
layoutScrollbarRef,
layoutMainRef,
};
},
});

View File

@ -1,17 +1,64 @@
<template>
<el-container class="layout-container flex-center layout-backtop">
<Header />
<Main />
<el-backtop target=".layout-backtop .el-main .el-scrollbar__wrap"></el-backtop>
<LayoutHeader />
<LayoutMain ref="layoutMainRef" />
</el-container>
</template>
<script lang="ts">
import Header from '/@/layout/component/header.vue';
import Main from '/@/layout/component/main.vue';
import { defineAsyncComponent, defineComponent, ref, watch, nextTick, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
export default {
export default defineComponent({
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>

View File

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

View File

@ -1,13 +1,14 @@
<template>
<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">
<template #footer>
<el-autocomplete
v-model="menuQuery"
:fetch-suggestions="menuSearch"
:placeholder="$t('message.user.searchPlaceholder')"
ref="layoutMenuAutocompleteRef"
@select="onHandleSelect"
@blur="onSearchBlur"
:fit-input-width="true"
>
<template #prefix>
<el-icon class="el-input__icon">
@ -21,6 +22,7 @@
</div>
</template>
</el-autocomplete>
</template>
</el-dialog>
</div>
</template>
@ -103,17 +105,12 @@ export default defineComponent({
else router.push(path);
closeSearch();
};
// input
const onSearchBlur = () => {
closeSearch();
};
return {
layoutMenuAutocompleteRef,
openSearch,
closeSearch,
menuSearch,
onHandleSelect,
onSearchBlur,
...toRefs(state),
};
},
@ -122,15 +119,23 @@ export default defineComponent({
<style scoped lang="scss">
.layout-search-dialog {
position: relative;
:deep(.el-dialog) {
box-shadow: unset !important;
border-radius: 0 !important;
background: rgba(0, 0, 0, 0.5);
.el-dialog__header,
.el-dialog__body {
display: none;
}
.el-dialog__footer {
position: absolute;
left: 50%;
transform: translateX(-50%);
top: -53vh;
}
}
:deep(.el-autocomplete) {
width: 560px;
position: absolute;
top: 100px;
top: 150px;
left: 50%;
transform: translateX(-50%);
}

View File

@ -105,6 +105,17 @@
></el-switch>
</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>
@ -408,8 +419,9 @@
</template>
<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 { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { getLightColor, getDarkColor } from '/@/utils/theme';
@ -418,11 +430,12 @@ import { Local } from '/@/utils/storage';
import Watermark from '/@/utils/wartermark';
import commonFunction from '/@/utils/commonFunction';
import other from '/@/utils/other';
import mittBus from '/@/utils/mitt';
export default defineComponent({
name: 'layoutBreadcrumbSeting',
setup() {
const { proxy } = <any>getCurrentInstance();
const { locale } = useI18n();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { copyText } = commonFunction();
@ -479,6 +492,11 @@ export default defineComponent({
setLocalThemeConfig();
}, 200);
};
// 2 ->
const onColumnsMenuHoverPreloadChange = () => {
mittBus.emit('setHoverPreload');
setLocalThemeConfig();
};
// 3 -->
const onThemeConfigChange = () => {
setDispatchThemeConfig();
@ -492,7 +510,7 @@ export default defineComponent({
const onClassicSplitMenuChange = () => {
getThemeConfig.value.isBreadcrumb = false;
setLocalThemeConfig();
proxy.mittBus.emit('getBreadcrumbIndexSetFilterRoutes');
mittBus.emit('getBreadcrumbIndexSetFilterRoutes');
};
// 4 --> Logo
const onIsShowLogoChange = () => {
@ -508,12 +526,12 @@ export default defineComponent({
};
// 4 --> TagsView
const onSortableTagsViewChange = () => {
proxy.mittBus.emit('openOrCloseSortable');
mittBus.emit('openOrCloseSortable');
setLocalThemeConfig();
};
// 4 --> TagsView
const onShareTagsViewChange = () => {
proxy.mittBus.emit('openShareTagsView');
mittBus.emit('openShareTagsView');
setLocalThemeConfig();
};
// 4 --> /
@ -565,7 +583,7 @@ export default defineComponent({
onBgColorPickerChange('columnsMenuBar');
onBgColorPickerChange('columnsMenuBarColor');
};
// proxy.$refs.layoutScrollbarRef.update()
// layoutScrollbarRef.value.update()
const onDrawerClose = () => {
getThemeConfig.value.isFixedHeaderChange = false;
getThemeConfig.value.isShowLogoChange = false;
@ -618,7 +636,7 @@ export default defineComponent({
if (!Local.get('frequency')) initLayoutChangeFun();
Local.set('frequency', 1);
//
proxy.mittBus.on('layoutMobileResize', (res: any) => {
mittBus.on('layoutMobileResize', (res: any) => {
getThemeConfig.value.layout = res.layout;
getThemeConfig.value.isDrawer = false;
initLayoutChangeFun();
@ -636,14 +654,14 @@ export default defineComponent({
//
onWartermarkChange();
//
if (Local.get('themeConfig')) proxy.$i18n.locale = Local.get('themeConfig').globalI18n;
if (Local.get('themeConfig')) locale.value = Local.get('themeConfig').globalI18n;
//
initSetStyle();
}, 100);
});
});
onUnmounted(() => {
proxy.mittBus.off('layoutMobileResize', () => {});
mittBus.off('layoutMobileResize', () => {});
});
return {
openDrawer,
@ -652,6 +670,7 @@ export default defineComponent({
onTopBarGradualChange,
onMenuBarGradualChange,
onColumnsMenuBarGradualChange,
onColumnsMenuHoverPreloadChange,
onThemeConfigChange,
onIsFixedHeaderChange,
onIsShowLogoChange,

View File

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

View File

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

View File

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

View File

@ -17,7 +17,7 @@
{{ $t(val.meta.title) }}
</template>
<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" />
{{ $t(val.meta.title) }}
</a>
@ -31,16 +31,19 @@
</template>
<script lang="ts">
import { toRefs, reactive, computed, defineComponent, getCurrentInstance, onMounted, nextTick, onBeforeMount } from 'vue';
import { useRoute, onBeforeRouteUpdate } from 'vue-router';
import { defineAsyncComponent, toRefs, reactive, computed, defineComponent, onMounted, nextTick, onBeforeMount, ref } from 'vue';
import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useRoutesList } from '/@/stores/routesList';
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({
name: 'navMenuHorizontal',
components: { SubItem },
components: {
SubItem: defineAsyncComponent(() => import('/@/layout/navMenu/subItem.vue')),
},
props: {
menuList: {
type: Array,
@ -48,12 +51,13 @@ export default defineComponent({
},
},
setup(props) {
const { proxy } = <any>getCurrentInstance();
const elMenuHorizontalScrollRef = ref();
const stores = useRoutesList();
const storesThemeConfig = useThemeConfig();
const { routesList } = storeToRefs(stores);
const { themeConfig } = storeToRefs(storesThemeConfig);
const route = useRoute();
const router = useRouter();
const state = reactive({
defaultActive: null,
});
@ -64,14 +68,14 @@ export default defineComponent({
//
const onElMenuHorizontalScroll = (e: any) => {
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 = () => {
nextTick(() => {
let els: any = document.querySelector('.el-menu.el-menu--horizontal li.is-active');
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;
}
};
//
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(() => {
setCurrentRouterHighlight(route);
@ -124,12 +135,14 @@ export default defineComponent({
// tagsView
let { layout, isClassicSplitMenu } = themeConfig.value;
if (layout === 'classic' && isClassicSplitMenu) {
proxy.mittBus.emit('setSendClassicChildren', setSendClassicChildren(to.path));
mittBus.emit('setSendClassicChildren', setSendClassicChildren(to.path));
}
});
return {
elMenuHorizontalScrollRef,
menuLists,
onElMenuHorizontalScroll,
onALinkClick,
...toRefs(state),
};
},

View File

@ -14,7 +14,7 @@
<span>{{ $t(val.meta.title) }}</span>
</template>
<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" />
{{ $t(val.meta.title) }}
</a>
@ -26,6 +26,8 @@
<script lang="ts">
import { computed, defineComponent } from 'vue';
import { useRouter } from 'vue-router';
import { verifyUrl } from '/@/utils/toolsValidate';
export default defineComponent({
name: 'navMenuSubItem',
@ -36,12 +38,21 @@ export default defineComponent({
},
},
setup(props) {
const router = useRouter();
//
const chils = computed(() => {
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 {
chils,
onALinkClick,
};
},
});

View File

@ -22,7 +22,7 @@
<span>{{ $t(val.meta.title) }}</span>
</template>
<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>
</el-menu-item>
</template>
@ -31,15 +31,17 @@
</template>
<script lang="ts">
import { toRefs, reactive, computed, defineComponent, onMounted, watch } from 'vue';
import { useRoute, onBeforeRouteUpdate } from 'vue-router';
import { defineAsyncComponent, toRefs, reactive, computed, defineComponent, onMounted, watch } from 'vue';
import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import SubItem from '/@/layout/navMenu/subItem.vue';
import { verifyUrl } from '/@/utils/toolsValidate';
export default defineComponent({
name: 'navMenuVertical',
components: { SubItem },
components: {
SubItem: defineAsyncComponent(() => import('/@/layout/navMenu/subItem.vue')),
},
props: {
menuList: {
type: Array,
@ -50,6 +52,7 @@ export default defineComponent({
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const route = useRoute();
const router = useRouter();
const state = reactive({
// https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
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('/');
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(
themeConfig.value,
@ -94,6 +104,7 @@ export default defineComponent({
return {
menuLists,
getThemeConfig,
onALinkClick,
...toRefs(state),
};
},

View File

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

View File

@ -1,16 +1,22 @@
<template>
<div class="layout-view-bg-white flex layout-view-link" :style="{ height: `calc(100vh - ${setLinkHeight}` }">
<a :href="currentRouteMeta.isLink" target="_blank" rel="opener" class="flex-margin">
{{ $t(currentRouteMeta.title) }}{{ currentRouteMeta.isLink }}
</a>
<div class="layout-padding layout-link-container">
<div class="layout-padding-auto layout-padding-view">
<div class="layout-link-warp">
<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>
</template>
<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 { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { verifyUrl } from '/@/utils/toolsValidate';
//
interface LinkViewState {
@ -27,8 +33,6 @@ interface LinkViewRouteMeta extends RouteMeta {
export default defineComponent({
name: 'layoutLinkView',
setup() {
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const route = useRoute();
const state = reactive<LinkViewState>({
currentRouteMeta: {
@ -36,12 +40,12 @@ export default defineComponent({
title: '',
},
});
// link
const setLinkHeight = computed(() => {
let { isTagsview } = themeConfig.value;
if (isTagsview) return `115px`;
else return `80px`;
});
//
const onGotoFullPage = () => {
const { origin, pathname } = window.location;
if (verifyUrl(state.currentRouteMeta.isLink)) window.open(state.currentRouteMeta.isLink);
else window.open(`${origin}${pathname}#${state.currentRouteMeta.isLink}`);
};
//
watch(
() => route.path,
@ -53,9 +57,57 @@ export default defineComponent({
}
);
return {
setLinkHeight,
onGotoFullPage,
...toRefs(state),
};
},
});
</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>
<div class="h100">
<div class="layout-parent">
<router-view v-slot="{ Component }">
<transition :name="setTransitionName" mode="out-in">
<keep-alive :include="getKeepAliveNames">
<component :is="Component" :key="refreshRouterViewKey" class="w100" />
<component :is="Component" :key="refreshRouterViewKey" class="w100" v-show="!isIframePage" />
</keep-alive>
</transition>
</router-view>
<transition :name="setTransitionName" mode="out-in">
<Iframes class="w100" v-show="isIframePage" :refreshKey="iframeRefreshKey" :name="setTransitionName" :list="iframeList" />
</transition>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, toRefs, reactive, getCurrentInstance, onBeforeMount, onUnmounted, nextTick, watch, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { defineAsyncComponent, computed, defineComponent, toRefs, reactive, onBeforeMount, onUnmounted, nextTick, watch, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useKeepALiveNames } from '/@/stores/keepAliveNames';
import { useThemeConfig } from '/@/stores/themeConfig';
import { Session } from '/@/utils/storage';
import mittBus from '/@/utils/mitt';
//
interface ParentViewState {
refreshRouterViewKey: null | string;
refreshRouterViewKey: string;
iframeRefreshKey: string;
keepAliveNameList: string[];
iframeList: string[];
}
export default defineComponent({
name: 'layoutParentView',
components: {
Iframes: defineAsyncComponent(() => import('/@/layout/routerView/iframes.vue')),
},
setup() {
const { proxy } = <any>getCurrentInstance();
const route = useRoute();
const router = useRouter();
const storesKeepAliveNames = useKeepALiveNames();
const storesThemeConfig = useThemeConfig();
const { keepAliveNames, cachedViews } = storeToRefs(storesKeepAliveNames);
const { themeConfig } = storeToRefs(storesThemeConfig);
const state = reactive<ParentViewState>({
refreshRouterViewKey: null,
refreshRouterViewKey: '', // iframe tagsview
iframeRefreshKey: '', // iframe tagsview
keepAliveNameList: [],
iframeList: [],
});
//
const setTransitionName = computed(() => {
@ -45,20 +56,37 @@ export default defineComponent({
const getKeepAliveNames = computed(() => {
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(() => {
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.refreshRouterViewKey = null;
state.refreshRouterViewKey = '';
state.iframeRefreshKey = '';
nextTick(() => {
state.refreshRouterViewKey = fullPath;
state.iframeRefreshKey = fullPath;
state.keepAliveNameList = keepAliveNames.value;
});
});
});
//
onMounted(() => {
getIframeListRoutes();
// https://gitee.com/lyt-top/vue-next-admin/issues/I58U75
// https://gitee.com/lyt-top/vue-next-admin/issues/I59RXK
nextTick(() => {
@ -69,18 +97,24 @@ export default defineComponent({
});
//
onUnmounted(() => {
proxy.mittBus.off('onTagsViewRefreshRouterView', () => {});
mittBus.off('onTagsViewRefreshRouterView', () => {});
});
// tagsView
// https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/38/files
watch(
() => route.fullPath,
() => {
state.refreshRouterViewKey = decodeURI(route.fullPath);
},
{
immediate: true,
}
);
return {
route,
setTransitionName,
getKeepAliveNames,
isIframePage,
...toRefs(state),
};
},

View File

@ -9,7 +9,6 @@ import other from '/@/utils/other';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import '/@/theme/index.scss';
import mitt from 'mitt';
import VueGridLayout from 'vue-grid-layout';
const app = createApp(App);
@ -18,5 +17,3 @@ directive(app);
other.elSvg(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() {
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];
return filterRouteEnd;
}

View File

@ -64,6 +64,8 @@ export async function frontEndsResetRoute() {
*/
export function setFilterRouteEnd() {
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];
return filterRouteEnd;
}

View File

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

View File

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

View File

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

View File

@ -19,8 +19,7 @@ export const useKeepALiveNames = defineStore('keepALiveNames', {
this.keepAliveNames = data;
},
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) {
const index = this.cachedViews.indexOf(view.name);

View File

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

View File

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

View File

@ -46,6 +46,14 @@ body,
.layout-container {
width: 100%;
height: 100%;
.layout-pd {
padding: 15px !important;
}
.layout-flex {
display: flex;
flex-direction: column;
flex: 1;
}
.layout-aside {
background: var(--next-bg-menuBar);
box-shadow: 2px 0 6px rgb(0 21 41 / 1%);
@ -61,23 +69,62 @@ body,
}
.layout-header {
padding: 0 !important;
height: auto !important;
}
.layout-main {
padding: 0 !important;
overflow: hidden;
width: 100%;
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;
}
.el-scrollbar {
width: 100%;
}
// 此字段多次用到建议不删除如需修改请重写覆盖样式
.layout-view-bg-white {
}
// 用于界面高度自适应
.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 {
width: 100%;
}
.layout-el-aside-br-color {
border-right: 1px solid var(--el-border-color-light, #ebeef5);
@ -122,10 +169,6 @@ body,
z-index: 9999998;
animation: error-img 0.3s;
}
.layout-scrollbar {
@extend .el-scrollbar;
padding: 15px;
}
.layout-mian-height-50 {
height: calc(100vh - 50px);
}

View File

@ -92,3 +92,56 @@
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;
}
}
// loading
.el-loading-mask {
background-color: var(--next-bg-main) !important;
}
}

View File

@ -44,6 +44,10 @@
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 警告
@ -259,17 +263,24 @@
.el-scrollbar__bar {
z-index: 4;
}
/*防止页面切换时,滚动条高度不变的问题(滚动条高度非滚动条滚动高度)*/
.el-scrollbar__wrap {
max-height: 100%; /*防止页面切换时,滚动条高度不变的问题(滚动条高度非滚动条滚动高度)*/
max-height: 100%;
}
.el-select-dropdown .el-scrollbar__wrap {
overflow-x: scroll !important;
}
/*修复Select 选择器高度问题*/
.el-select-dropdown__wrap {
max-height: 274px !important; /*修复Select 选择器高度问题*/
max-height: 274px !important;
}
/*修复Cascader 级联选择器高度问题*/
.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 抽屉

View File

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

View File

@ -1,53 +1,59 @@
@import './index.scss';
/* 页面宽度小于992px
/* 页面宽度小于1200px
------------------------------- */
@media screen and (max-width: $lg) {
@media screen and (max-width: $lg) and (min-width: $xs) {
.login-container {
.login-icon-group {
&::before {
content: '';
height: 70% !important;
transition: all 0.3s ease;
}
&::after {
content: '';
width: 100px !important;
height: 200px !important;
transition: all 0.3s ease;
.login-left {
.login-left-img {
top: 90% !important;
left: 12% !important;
width: 30% !important;
height: 18% !important;
}
}
.login-right {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
/* 页面宽度小于992px
------------------------------- */
@media screen and (max-width: $md) {
.login-content {
right: unset !important;
left: 50% !important;
transform: translate(-50%, -50%) translate3d(0, 0, 0) !important;
}
}
/* 页面宽度小于576px
------------------------------- */
@media screen and (max-width: $xs) {
.login-container {
.login-icon-group {
display: none !important;
.login-left {
display: none;
}
.login-content {
.login-right {
width: 100% !important;
.login-right-warp {
width: 100% !important;
height: 100% !important;
padding: 20px 0 !important;
border-radius: 0 !important;
box-shadow: unset !important;
border: none !important;
}
.login-right-warp-mian {
.el-form-item {
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) {
.login-container {
.login-content-title {
.login-right {
.login-right-warp {
.login-right-warp-mian {
.login-right-warp-main-title {
font-size: 18px !important;
transition: all 0.3s ease;
}
}
}
}
}
}

View File

@ -1,7 +1,7 @@
/* wangeditor 富文本编辑器
------------------------------- */
.editor-container {
z-index: 9999;
z-index: 10; // 用于 wangeditor 点击全屏时
.w-e-toolbar {
border: 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;
},
// 移除 loading
done: () => {
done: (time: number = 0) => {
nextTick(() => {
setTimeout(() => {
window.nextLoading = false;
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 = '';
const { query, params, meta } = item;
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));
tagsViewName = urlTagsParams[i18n.global.locale];
tagsViewName = urlTagsParams[i18n.global.locale.value];
} else {
// 非国际化
tagsViewName = query?.tagsViewName || params?.tagsViewName;

View File

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

View File

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

View File

@ -1,5 +1,6 @@
<template>
<div class="error layout-view-bg-white" :style="{ height: `calc(100vh - ${initTagViewHeight}` }">
<div class="error layout-padding">
<div class="layout-padding-auto layout-padding-view">
<div class="error-flex">
<div class="left">
<div class="left-item">
@ -7,7 +8,7 @@
<div class="left-item-animation left-item-title">{{ $t('message.notFound.foundTitle') }}</div>
<div class="left-item-animation left-item-msg">{{ $t('message.notFound.foundMsg') }}</div>
<div class="left-item-animation left-item-btn">
<el-button type="primary" round @click="onGoHome">{{ $t('message.notFound.foundBtn') }}</el-button>
<el-button type="primary" size="default" round @click="onGoHome">{{ $t('message.notFound.foundBtn') }}</el-button>
</div>
</div>
</div>
@ -18,39 +19,22 @@
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue';
import { defineComponent } from 'vue';
import { useRouter } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
export default defineComponent({
name: '404',
setup() {
const storesThemeConfig = useThemeConfig();
const storesTagsViewRoutes = useTagsViewRoutes();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
const router = useRouter();
const onGoHome = () => {
router.push('/');
};
//
const initTagViewHeight = computed(() => {
let { isTagsview } = themeConfig.value;
if (isTagsViewCurrenFull.value) {
return `30px`;
} else {
if (isTagsview) return `114px`;
else return `80px`;
}
});
return {
onGoHome,
initTagViewHeight,
};
},
});
@ -59,8 +43,6 @@ export default defineComponent({
<style scoped lang="scss">
.error {
height: 100%;
background-color: var(--el-color-white);
display: flex;
.error-flex {
margin: auto;
display: flex;

View File

@ -1,4 +1,5 @@
<template>
<div class="layout-pd">
<el-card shadow="hover" header="复制剪切演示">
<el-alert
title="感谢优秀的 `vue-clipboard3`项目地址https://github.com/JamieCurnow/vue-clipboard3`"
@ -13,6 +14,7 @@
</el-input>
<el-input placeholder="先点击上方 `复制链接` 按钮,然后 `Ctrl + V` 进行粘贴! " v-model="shearVal" class="mt15"> </el-input>
</el-card>
</div>
</template>
<script lang="ts">

View File

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

View File

@ -1,5 +1,5 @@
<template>
<div class="croppers-container">
<div class="croppers-container layout-pd">
<el-card shadow="hover" header="cropper 图片裁剪">
<el-alert
title="感谢优秀的 `cropperjs`项目地址https://github.com/fengyuanchen/cropperjs"
@ -24,16 +24,17 @@
</template>
<script lang="ts">
import { ref, toRefs, reactive, defineComponent } from 'vue';
import CropperDialog from '/@/components/cropper/index.vue';
import { defineAsyncComponent, ref, toRefs, reactive, defineComponent } from 'vue';
export default defineComponent({
name: 'funCropper',
components: { CropperDialog },
components: {
CropperDialog: defineAsyncComponent(() => import('/@/components/cropper/index.vue')),
},
setup() {
const cropperDialogRef = ref();
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 = () => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
<template>
<div class="editor-container">
<div class="editor-container layout-pd">
<el-card shadow="hover" header="wangeditor富文本编辑器">
<el-alert
title="感谢优秀的 `wangeditor`项目地址https://github.com/wangeditor-team/wangEditor"
@ -7,24 +7,26 @@
:closable="false"
class="mb15"
></el-alert>
<Editor :is-disable="false" v-model="editorVal" />
<Editor v-model="editor.value" :disable="editor.disable" />
</el-card>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
import Editor from '/@/components/editor/index.vue';
import { defineAsyncComponent, toRefs, reactive, defineComponent } from 'vue';
export default defineComponent({
name: 'funWangEditor',
components: { Editor },
components: {
Editor: defineAsyncComponent(() => import('/@/components/editor/index.vue')),
},
setup() {
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 {
...toRefs(state),
};

View File

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

View File

@ -1,5 +1,5 @@
<template>
<div>
<div class="layout-pd">
<el-alert
title="温馨提示1此页面无法模拟后端控制路由因为 `gitee` 上所请求的 `json` 菜单数据线上会出现跨域的情况json地址
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>
<div>
<LimitsFrontEndPage />
<div class="layout-pd">
<LimitsFrontEndPage style="padding: 0 !important" />
<!-- 演示1组件方式 -->
<el-card shadow="hover" header="演示1组件方式" class="mt15">
<el-row class="mb10" style="color: #808080">单个权限验证:value="xxx"</el-row>
@ -335,17 +335,18 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineAsyncComponent, defineComponent } from 'vue';
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';
export default defineComponent({
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() {
//
const onAuthClick = () => {

View File

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

View File

@ -1,7 +1,7 @@
<template>
<el-form size="large" class="login-content-form">
<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>
<el-icon class="el-input__icon"><ele-User /></el-icon>
</template>
@ -29,14 +29,7 @@
</el-form-item>
<el-form-item class="login-animation3">
<el-col :span="15">
<el-input
type="text"
maxlength="4"
:placeholder="$t('message.account.accountPlaceholder3')"
v-model="ruleForm.code"
clearable
autocomplete="off"
>
<el-input text maxlength="4" :placeholder="$t('message.account.accountPlaceholder3')" v-model="ruleForm.code" clearable autocomplete="off">
<template #prefix>
<el-icon class="el-input__icon"><ele-Position /></el-icon>
</template>
@ -44,11 +37,11 @@
</el-col>
<el-col :span="1"></el-col>
<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-form-item>
<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>
</el-button>
</el-form-item>

View File

@ -1,7 +1,7 @@
<template>
<el-form size="large" class="login-content-form">
<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>
<i class="iconfont icon-dianhua el-input__icon"></i>
</template>
@ -9,7 +9,7 @@
</el-form-item>
<el-form-item class="login-animation2">
<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>
<el-icon class="el-input__icon"><ele-Position /></el-icon>
</template>
@ -17,11 +17,11 @@
</el-col>
<el-col :span="1"></el-col>
<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-form-item>
<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>
</el-button>
</el-form-item>

View File

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

View File

@ -1,15 +1,25 @@
<template>
<div class="login-container">
<div class="login-icon-group">
<div class="login-icon-group-title">
<div class="login-container flex">
<div class="login-left">
<div class="login-left-logo">
<img :src="logoMini" />
<div class="login-icon-group-title-text font25">{{ getThemeConfig.globalViceTitle }}</div>
<div class="login-left-logo-text">
<span>{{ getThemeConfig.globalViceTitle }}</span>
<span class="login-left-logo-text-msg">{{ getThemeConfig.globalViceTitleMsg }}</span>
</div>
<img :src="loginIconTwo" class="login-icon-group-icon" />
</div>
<div class="login-content">
<div class="login-content-main">
<h4 class="login-content-title ml15">{{ getThemeConfig.globalTitle }}后台模板</h4>
<div class="login-left-img">
<img :src="loginMain" />
</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">
@ -28,32 +38,30 @@
</div>
</div>
</div>
</div>
</div>
</template>
<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 { 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 Account from '/@/views/login/component/account.vue';
import Mobile from '/@/views/login/component/mobile.vue';
import Scan from '/@/views/login/component/scan.vue';
//
interface LoginState {
tabsActiveName: string;
isScan: boolean;
}
import logoMini from '/@/assets/logo-mini.svg';
import loginMain from '/@/assets/login-main.svg';
import loginBg from '/@/assets/login-bg.svg';
export default defineComponent({
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() {
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const state = reactive<LoginState>({
const state = reactive({
tabsActiveName: 'account',
isScan: false,
});
@ -67,7 +75,8 @@ export default defineComponent({
});
return {
logoMini,
loginIconTwo,
loginBg,
loginMain,
getThemeConfig,
...toRefs(state),
};
@ -77,89 +86,140 @@ export default defineComponent({
<style scoped lang="scss">
.login-container {
width: 100%;
height: 100%;
position: relative;
background: var(--el-color-white);
.login-icon-group {
width: 100%;
height: 100%;
.login-left {
flex: 1;
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;
top: 50px;
left: 80px;
display: flex;
align-items: center;
z-index: 1;
animation: logoAnimation 0.3s ease;
img {
width: 30px;
height: 30px;
width: 52px;
height: 52px;
}
&-text {
padding-left: 15px;
color: var(--el-color-primary);
.login-left-logo-text {
display: flex;
flex-direction: column;
span {
margin-left: 10px;
font-size: 28px;
color: #26a59a;
}
.login-left-logo-text-msg {
font-size: 12px;
color: #32a99e;
}
}
&::before {
content: '';
}
.login-left-img {
position: absolute;
bottom: 0;
left: 0;
width: 60%;
overflow: hidden;
height: 80%;
-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");
background: var(--el-color-primary-light-5);
transition: all 0.3s ease;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
height: 52%;
img {
width: 100%;
height: 100%;
animation: error-num 0.6s ease;
}
}
.login-left-waves {
position: absolute;
top: 0;
right: -100px;
}
}
.login-right {
width: 700px;
.login-right-warp {
border: 1px solid var(--el-color-primary-light-3);
border-radius: 3px;
width: 500px;
height: 500px;
position: relative;
overflow: hidden;
background-color: var(--el-color-white);
.login-right-warp-one,
.login-right-warp-two {
position: absolute;
display: block;
width: inherit;
height: inherit;
&::before,
&::after {
content: '';
width: 150px;
height: 300px;
position: absolute;
right: 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");
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 {
width: 500px;
padding: 20px;
position: absolute;
right: 200px;
top: 50%;
transform: translateY(-50%) translate3d(0, 0, 0);
background-color: var(--el-color-white);
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;
}
}
.login-right-warp-one {
&::before {
filter: hue-rotate(0deg);
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;
}
}
.login-right-warp-two {
&::before {
filter: hue-rotate(120deg);
bottom: 2px;
right: -100%;
width: 100%;
height: 3px;
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: 4px;
margin: 15px 0 30px;
white-space: nowrap;
z-index: 5;
position: relative;
transition: all 0.3s ease;
}
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;
@ -169,7 +229,7 @@ export default defineComponent({
overflow: hidden;
cursor: pointer;
transition: all ease 0.3s;
color: var(--el-text-color-primary);
color: var(--el-color-primary);
&-delta {
position: absolute;
width: 35px;
@ -191,8 +251,11 @@ export default defineComponent({
display: inline-block;
font-size: 48px;
position: absolute;
right: 2px;
top: -1px;
right: 1px;
top: 0px;
}
}
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
<template>
<div class="awesome-container">
<div class="awesome-container layout-pd">
<el-card shadow="hover" :header="`fontawesome 字体图标(自动载入)${sheetsIconList.length - 24}个`">
<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">

View File

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

View File

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

View File

@ -1,5 +1,5 @@
<template>
<div class="element-container">
<div class="element-container layout-pd">
<el-card shadow="hover" :header="`element plus 字体图标(自动载入,增加了 ele- 前缀使用时ele-Aim)${sheetsIconList.length}个`">
<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">

View File

@ -1,6 +1,6 @@
<template>
<div :style="{ height: `calc(100vh - ${initTagViewHeight}` }">
<div class="layout-view-bg-white">
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<div class="w100 h100 flex">
<div class="flex-margin color-primary">filtering-details 测试界面</div>
</div>
@ -9,31 +9,9 @@
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { defineComponent } from 'vue';
export default defineComponent({
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>

View File

@ -1,6 +1,6 @@
<template>
<div :style="{ height: `calc(100vh - ${initTagViewHeight}` }">
<div class="layout-view-bg-white">
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<div class="w100 h100 flex">
<div class="flex-margin color-primary">测试界面</div>
</div>
@ -9,31 +9,9 @@
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { defineComponent } from 'vue';
export default defineComponent({
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>

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
<template>
<div class="iconfont-container">
<div class="iconfont-container layout-pd">
<el-card shadow="hover" :header="`iconfont 字体图标(自动载入)${sheetsIconList.length}个`">
<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">

View File

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

View File

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

View File

@ -1,5 +1,5 @@
<template>
<div class="preview-container">
<div class="preview-container layout-pd">
<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-card>

View File

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

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