'admin-22.12.01:同步vue-next-admin-template分支v2.4.1版本内容'

This commit is contained in:
lyt 2022-12-01 17:40:18 +08:00
parent 57c5394d6f
commit 83ef1dc51e
83 changed files with 1449 additions and 2544 deletions

View File

@ -6,21 +6,38 @@ module.exports = {
node: true,
},
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 12,
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
extends: ['plugin:vue/vue3-essential', 'plugin:vue/essential', 'eslint:recommended'],
plugins: ['vue'],
plugins: ['vue', '@typescript-eslint'],
overrides: [
{
files: ['*.ts', '*.tsx', '*.vue'],
rules: {
'no-undef': 'off',
},
},
],
rules: {
// http://eslint.cn/docs/rules/
// https://eslint.vuejs.org/rules/
'@type-eslint/ban-ts-ignore': 'off',
'@type-eslint/explicit-function-return-type': 'off',
'@type-eslint/no-explicit-any': 'off',
'@type-eslint/no-var-requires': 'off',
'@type-eslint/no-empty-function': 'off',
'@type-eslint/no-use-before-define': 'off',
'@type-eslint/ban-ts-comment': 'off',
'@type-eslint/ban-types': 'off',
'@type-eslint/no-non-null-assertion': 'off',
'@type-eslint/explicit-module-boundary-types': 'off',
// https://typescript-eslint.io/rules/no-unused-vars/
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-redeclare': 'error',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
'@typescript-eslint/no-unused-vars': [2],
'vue/custom-event-name-casing': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off',
@ -54,6 +71,6 @@ module.exports = {
'no-v-model-argument': 'off',
'no-case-declarations': 'off',
'no-console': 'error',
'no-undef': 'off',
'no-redeclare': 'off',
},
};

View File

@ -1,6 +1,12 @@
# <a href="https://gitee.com/lyt-top/vue-next-admin" target="_blank">vue-next-admin-template-js不带国际化 更新日志</a>
🎉🎉🔥 `vue-next-admin-template-js` 基于 vue-next-admin-template v2.3.0 版本) vue3.x 、vite、Element plus 等适配手机、平板、pc 的后台开源免费模板库vue2.x 请切换 vue-prev-admin 分支)
🎉🎉🔥 `vue-next-admin-template-js` 基于 vue-next-admin-template v2.4.1 版本) vue3.x 、vite、Element plus 等适配手机、平板、pc 的后台开源免费模板库vue2.x 请切换 vue-prev-admin 分支)
## 2.4.1
`2022.12.01`
- 🎉 同步 vue-next-admin-template 基础版本(不带国际化) 分支 v2.4.1 版本内容,具体查看 master 分支 CHANGELOG.md
## 2.3.0

View File

@ -22,7 +22,7 @@
#### 🌈 介绍 基础版 js不带国际化基于 vue-next-admin-template V2.3.0 版setup 语法糖)
基于 vue3.x + CompositionAPI + vite + element plus + vue-router-next适配手机、平板、pc 的后台开源免费模板,希望减少工作量,帮助大家实现快速开发。
基于 vue3.x + CompositionAPI setup 语法糖 + typescript + vite + element plus + vue-router-next + pinia 技术适配手机、平板、pc 的后台开源免费模板,希望减少工作量,帮助大家实现快速开发。
#### ⛱️ 线上预览
@ -43,13 +43,13 @@
| Edge | Firefox | Chrome | Safari |
| --------- | ------------ | ----------- | ----------- |
| Edge ≥ 79 | Firefox ≥ 78 | Chrome ≥ 64 | Safari ≥ 12 |
| Edge ≥ 79 | Firefox ≥ 78 | Chrome ≥ 76 | Safari ≥ 12 |
> 由于 Vue3 不再支持 IE11故而 ElementPlus 也不支持 IE11 及之前版本。
#### ⚡ 使用说明
建议使用 cnpm因为 yarn 有时会报错。<a href="http://nodejs.cn/" target="_blank">node 版本 > 12xx.xx.x</a>
建议使用 cnpm因为 yarn 有时会报错。<a href="http://nodejs.cn/" target="_blank">node 版本 > 14xx.xx.x</a>
```bash
# 克隆项目
@ -78,20 +78,20 @@ cnpm run build
#### 💯 学习交流加 QQ 群
- 若加群了没同意一般秒过那就是群满了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>
3 群:<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=02EWb5P2JkP-8iwzaDadgFdxA0HSHPpn&jump_from=webapi">795345435</a>
4 群:<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=0gTFO04WwkeZZ6R4lju6gucbeXHK-wNd&jump_from=webapi">736626228</a>
- 1 群:<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=RdUY97Vx0T0vZ_1OOu-X1yFNkWgDwbjC&jump_from=webapi">665452019</a>
- 2 群:<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=zVfy3gNy7pNWVK3kMduDzwU369PZg2fw&jump_from=webapi">766356862</a>
- 3 群:<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=02EWb5P2JkP-8iwzaDadgFdxA0HSHPpn&jump_from=webapi">795345435</a>
- 4 群:<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=0gTFO04WwkeZZ6R4lju6gucbeXHK-wNd&jump_from=webapi">736626228</a>
<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=RdUY97Vx0T0vZ_1OOu-X1yFNkWgDwbjC&jump_from=webapi">
<img src="https://img-blog.csdnimg.cn/35e00f12a3fe4820892ec630ca72f15f.png" width="220" height="220" alt="vue-next-admin 讨论群1" title="vue-next-admin 讨论群1"/>
<img src="https://img-blog.csdnimg.cn/35e00f12a3fe4820892ec630ca72f15f.png" width="220" height="220" alt="vue-next-admin 讨论群1" title="vue-next-admin 讨论群1"/>
</a>
<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=zVfy3gNy7pNWVK3kMduDzwU369PZg2fw&jump_from=webapi">
<img src="https://img-blog.csdnimg.cn/5f1b548abd9f434eb41edde31d1c1fa9.png" width="220" height="220" alt="vue-next-admin 讨论群2" title="vue-next-admin 讨论群2"/>
<img src="https://img-blog.csdnimg.cn/5f1b548abd9f434eb41edde31d1c1fa9.png" width="220" height="220" alt="vue-next-admin 讨论群2" title="vue-next-admin 讨论群2"/>
</a>
<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=02EWb5P2JkP-8iwzaDadgFdxA0HSHPpn&jump_from=webapi">
<img src="https://img-blog.csdnimg.cn/70c8a012dd304246bddeac2184c4ab3a.png" width="220" height="220" alt="vue-next-admin 讨论群3" title="vue-next-admin 讨论群3"/>
<img src="https://img-blog.csdnimg.cn/70c8a012dd304246bddeac2184c4ab3a.png" width="220" height="220" alt="vue-next-admin 讨论群3" title="vue-next-admin 讨论群3"/>
</a>
<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=0gTFO04WwkeZZ6R4lju6gucbeXHK-wNd&jump_from=webapi">
<img src="https://img-blog.csdnimg.cn/e5c9704eed1342bc9d9e74b37203402d.png" width="220" height="220" alt="vue-next-admin 讨论群4" title="vue-next-admin 讨论群4"/>

View File

@ -4,17 +4,18 @@
"module": "esnext",
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
"jsx": "preserve",
"isolatedModules": true,
"strict": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"baseUrl": ".",
"types": ["vite/client"],
"paths": {
"/@/*": ["src/*"]
},
"moduleResolution": "node",
"strict": true
}
}
}

2134
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "vue-next-admin-template-js",
"version": "2.3.0",
"version": "2.4.1",
"description": "vue3 vite next admin template js setup",
"author": "lyt_20201208",
"license": "MIT",
@ -11,13 +11,13 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.0.10",
"axios": "^1.1.3",
"axios": "^1.2.0",
"echarts": "^5.4.0",
"element-plus": "^2.2.21",
"element-plus": "^2.2.25",
"js-cookie": "^3.0.1",
"mitt": "^3.0.0",
"nprogress": "^0.2.0",
"pinia": "^2.0.23",
"pinia": "^2.0.27",
"qrcodejs2-fixes": "^0.0.2",
"screenfull": "^6.0.2",
"sortablejs": "^1.15.0",
@ -28,13 +28,11 @@
"devDependencies": {
"@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",
"eslint": "^8.28.0",
"eslint-plugin-vue": "^9.8.0",
"prettier": "^2.8.0",
"sass": "^1.56.1",
"sass-loader": "^13.2.0",
"unplugin-auto-import": "^0.11.4",
"unplugin-auto-import": "^0.12.0",
"vite": "^3.2.4",
"vite-plugin-vue-setup-extend": "^0.4.0",
"vue-eslint-parser": "^9.1.0"
@ -48,8 +46,8 @@
"url": "https://gitee.com/lyt-top/vue-next-admin/issues"
},
"engines": {
"node": ">=12.0.0",
"npm": ">= 6.0.0"
"node": ">=14.0.0",
"npm": ">= 7.0.0"
},
"keywords": [
"vue",

View File

@ -4,6 +4,7 @@
<LockScreen v-if="themeConfig.isLockScreen" />
<Setings ref="setingsRef" v-show="themeConfig.lockScreenTime > 1" />
<CloseFull v-if="!themeConfig.isLockScreen" />
<Upgrade v-if="getVersion" />
</el-config-provider>
</template>
@ -17,15 +18,27 @@ import { Local, Session } from '/@/utils/storage';
import setIntroduction from '/@/utils/setIconfont';
import mittBus from './utils/mitt';
//
const LockScreen = defineAsyncComponent(() => import('/@/layout/lockScreen/index.vue'));
const Setings = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/setings.vue'));
const CloseFull = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/closeFull.vue'));
const Upgrade = defineAsyncComponent(() => import('/@/layout/upgrade/index.vue'));
//
const setingsRef = ref();
const route = useRoute();
const stores = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
//
const getVersion = computed(() => {
let isVersion = false;
if (route.path !== '/login') {
if ((Local.get('version') && Local.get('version') !== __VERSION__) || !Local.get('version')) isVersion = true;
}
return isVersion;
});
//
const getGlobalComponentSize = computed(() => {
return other.globalComponentSize();
@ -46,7 +59,7 @@ onMounted(() => {
});
//
if (Local.get('themeConfig')) {
storesThemeConfig.setThemeConfig(Local.get('themeConfig'));
storesThemeConfig.setThemeConfig({ themeConfig: Local.get('themeConfig') });
document.documentElement.style.cssText = Local.get('themeConfigStyle');
}
//

View File

@ -1,9 +1,6 @@
import request from '/@/utils/request';
/**
* 用户登录
* @param params 要传的参数值
* @returns 返回接口数据
* 登录api接口集合
* @method signIn 用户登录
* @method signOut 用户退出登录
@ -11,18 +8,10 @@ import request from '/@/utils/request';
export function useLoginApi() {
return {
signIn: (params) => {
return request({
url: '/user/signIn',
method: 'post',
data: params,
});
return request.post('/user/signIn', params);
},
signOut: (params) => {
return request({
url: '/user/signOut',
method: 'post',
data: params,
});
return request.post('/user/signOut', params);
},
};
}

View File

@ -1,26 +1,20 @@
import request from '/@/utils/request';
/**
* 以下为模拟接口地址gitee 的不通就换自己的真实接口地址
*
* 后端控制菜单模拟json路径在 https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu
* 后端控制路由isRequestRoutes true则开启后端控制路由
* @method getMenuAdmin 获取后端动态路由菜单(admin)
* @method getMenuTest 获取后端动态路由菜单(test)
* @method getAdminMenu 获取后端动态路由菜单(admin)
* @method getTestMenu 获取后端动态路由菜单(test)
*/
export function useMenuApi() {
return {
getMenuAdmin: (params) => {
return request({
url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/adminMenu.json',
method: 'get',
params,
});
getAdminMenu: (params) => {
return request.get('/gitee/lyt-top/vue-next-admin-images/raw/master/menu/adminMenu.json', params);
},
getMenuTest: (params) => {
return request({
url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/testMenu.json',
method: 'get',
params,
});
getTestMenu: (params) => {
return request.get('/gitee/lyt-top/vue-next-admin-images/raw/master/menu/testMenu.json', params);
},
};
}

View File

@ -2,7 +2,7 @@
<div class="icon-selector w100 h100">
<el-popover
placement="bottom"
:width="fontIconWidth"
:width="state.fontIconWidth"
trigger="click"
transition="el-zoom-in-top"
popper-class="icon-selector-popper"
@ -10,8 +10,8 @@
>
<template #reference>
<el-input
v-model="fontIconSearch"
:placeholder="fontIconPlaceholder"
v-model="state.fontIconSearch"
:placeholder="state.fontIconPlaceholder"
:clearable="clearable"
:disabled="disabled"
:size="size"
@ -22,11 +22,11 @@
>
<template #prepend>
<SvgIcon
:name="fontIconPrefix === '' ? prepend : fontIconPrefix"
:name="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix"
class="font14"
v-if="fontIconPrefix === '' ? prepend?.indexOf('ele-') > -1 : fontIconPrefix?.indexOf('ele-') > -1"
v-if="state.fontIconPrefix === '' ? prepend?.indexOf('ele-') > -1 : state.fontIconPrefix?.indexOf('ele-') > -1"
/>
<i v-else :class="fontIconPrefix === '' ? prepend : fontIconPrefix" class="font14"></i>
<i v-else :class="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix" class="font14"></i>
</template>
</el-input>
</template>
@ -35,16 +35,22 @@
<div class="icon-selector-warp-title flex">
<div class="flex-auto">{{ title }}</div>
<div class="icon-selector-warp-title-tab" v-if="type === 'all'">
<span :class="{ 'span-active': fontIconType === 'ali' }" @click="onIconChange('ali')" class="ml10" title="iconfont 图标">ali</span>
<span :class="{ 'span-active': fontIconType === 'ele' }" @click="onIconChange('ele')" class="ml10" title="elementPlus 图标">ele</span>
<span :class="{ 'span-active': fontIconType === 'awe' }" @click="onIconChange('awe')" class="ml10" title="fontawesome 图标">awe</span>
<span :class="{ 'span-active': state.fontIconType === 'ali' }" @click="onIconChange('ali')" class="ml10" title="iconfont 图标">
ali
</span>
<span :class="{ 'span-active': state.fontIconType === 'ele' }" @click="onIconChange('ele')" class="ml10" title="elementPlus 图标">
ele
</span>
<span :class="{ 'span-active': state.fontIconType === 'awe' }" @click="onIconChange('awe')" class="ml10" title="fontawesome 图标">
awe
</span>
</div>
</div>
<div class="icon-selector-warp-row">
<el-scrollbar ref="selectorScrollbarRef">
<el-row :gutter="10" v-if="fontIconSheetsFilterList.length > 0">
<el-col :xs="6" :sm="4" :md="4" :lg="4" :xl="4" @click="onColClick(v)" v-for="(v, k) in fontIconSheetsFilterList" :key="k">
<div class="icon-selector-warp-item" :class="{ 'icon-selector-active': fontIconPrefix === v }">
<div class="icon-selector-warp-item" :class="{ 'icon-selector-active': state.fontIconPrefix === v }">
<div class="flex-margin">
<div class="icon-selector-warp-item-value">
<SvgIcon :name="v" />
@ -62,190 +68,179 @@
</div>
</template>
<script>
import { ref, toRefs, reactive, onMounted, nextTick, computed, watch, defineComponent } from 'vue';
<script setup name="iconSelector">
import initIconfont from '/@/utils/getStyleSheets';
import '/@/theme/iconSelector.scss';
export default defineComponent({
name: 'iconSelector',
emits: ['update:modelValue', 'get', 'clear'],
props: {
//
prepend: {
type: String,
default: () => 'ele-Pointer',
},
//
placeholder: {
type: String,
default: () => '请输入内容搜索图标或者选择图标',
},
//
size: {
type: String,
default: () => 'default',
},
//
title: {
type: String,
default: () => '请选择图标',
},
// icon
type: {
type: String,
default: () => 'ele',
},
//
disabled: {
type: Boolean,
default: () => false,
},
//
clearable: {
type: Boolean,
default: () => true,
},
//
emptyDescription: {
type: String,
default: () => '无相关图标',
},
// modelValue
// https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
// https://v3.cn.vuejs.org/guide/component-custom-events.html#%E5%A4%9A%E4%B8%AA-v-model-%E7%BB%91%E5%AE%9A
modelValue: String,
//
const props = defineProps({
//
prepend: {
type,
default: () => 'ele-Pointer',
},
setup(props, { emit }) {
const inputWidthRef = ref();
const selectorScrollbarRef = ref();
const state = reactive({
fontIconPrefix: '',
fontIconWidth: 0,
fontIconSearch: '',
fontIconTabsIndex: 0,
fontIconSheetsList: [],
fontIconPlaceholder: '',
fontIconType: 'ali',
fontIconShow: true,
});
// input modelValue input placeholder
const onIconFocus = () => {
if (!props.modelValue) return false;
state.fontIconSearch = '';
state.fontIconPlaceholder = props.modelValue;
};
// input input
const onIconBlur = () => {
setTimeout(() => {
const icon = state.fontIconSheetsList.filter((icon) => icon === state.fontIconSearch);
if (icon.length <= 0) state.fontIconSearch = '';
}, 300);
};
// icon
const initModeValueEcho = () => {
if (props.modelValue === '') return (state.fontIconPlaceholder = props.placeholder);
state.fontIconPlaceholder = props.modelValue;
state.fontIconPrefix = props.modelValue;
};
// icon type all alieleawe
const initFontIconTypeEcho = () => {
if (props.modelValue?.indexOf('iconfont') > -1) onIconChange('ali');
else if (props.modelValue?.indexOf('ele-') > -1) onIconChange('ele');
else if (props.modelValue?.indexOf('fa') > -1) onIconChange('awe');
else onIconChange('ali');
};
//
const fontIconSheetsFilterList = computed(() => {
if (!state.fontIconSearch) return state.fontIconSheetsList;
let search = state.fontIconSearch.trim().toLowerCase();
return state.fontIconSheetsList.filter((item) => {
if (item.toLowerCase().indexOf(search) !== -1) return item;
});
});
// input
const getInputWidth = () => {
nextTick(() => {
state.fontIconWidth = inputWidthRef.value.$el.offsetWidth;
});
};
//
const initResize = () => {
window.addEventListener('resize', () => {
getInputWidth();
});
};
//
const initFontIconData = async (type) => {
state.fontIconSheetsList = [];
if (type === 'ali') {
await initIconfont.ali().then((res) => {
// 使 `iconfont xxx`
state.fontIconSheetsList = res.map((i) => `iconfont ${i}`);
});
} else if (type === 'ele') {
await initIconfont.ele().then((res) => {
state.fontIconSheetsList = res;
});
} else if (type === 'awe') {
await initIconfont.awe().then((res) => {
// fontawesome使 `fa xxx`
state.fontIconSheetsList = res.map((i) => `fa ${i}`);
});
}
// input placeholder
// https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
state.fontIconPlaceholder = props.placeholder;
//
initModeValueEcho();
};
//
const onIconChange = (type) => {
state.fontIconType = type;
initFontIconData(type);
};
// icon
const onColClick = (v) => {
state.fontIconPlaceholder = v;
state.fontIconPrefix = v;
emit('get', state.fontIconPrefix);
emit('update:modelValue', state.fontIconPrefix);
};
// icon
const onClearFontIcon = () => {
state.fontIconPrefix = '';
emit('clear', state.fontIconPrefix);
emit('update:modelValue', state.fontIconPrefix);
};
// Popover
const onPopoverShow = () => {
initModeValueEcho();
initFontIconTypeEcho();
};
//
onMounted(() => {
initModeValueEcho();
initResize();
getInputWidth();
});
// modelValue
watch(
() => props.modelValue,
() => {
initModeValueEcho();
}
);
return {
inputWidthRef,
selectorScrollbarRef,
fontIconSheetsFilterList,
onColClick,
onIconChange,
onClearFontIcon,
onIconFocus,
onIconBlur,
onPopoverShow,
...toRefs(state),
};
//
placeholder: {
type,
default: () => '请输入内容搜索图标或者选择图标',
},
//
size: {
type,
default: () => 'default',
},
//
title: {
type,
default: () => '请选择图标',
},
// icon
type: {
type,
default: () => 'ele',
},
//
disabled: {
type: Boolean,
default: () => false,
},
//
clearable: {
type: Boolean,
default: () => true,
},
//
emptyDescription: {
type,
default: () => '无相关图标',
},
// modelValue
// https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
// https://v3.cn.vuejs.org/guide/component-custom-events.html#%E5%A4%9A%E4%B8%AA-v-model-%E7%BB%91%E5%AE%9A
modelValue,
});
// /
const emit = defineEmits(['update:modelValue', 'get', 'clear']);
//
const inputWidthRef = ref();
const selectorScrollbarRef = ref();
const state = reactive({
fontIconPrefix: '',
fontIconWidth: 0,
fontIconSearch: '',
fontIconTabsIndex: 0,
fontIconSheetsList: [],
fontIconPlaceholder: '',
fontIconType: 'ali',
fontIconShow: true,
});
// input modelValue input placeholder
const onIconFocus = () => {
if (!props.modelValue) return false;
state.fontIconSearch = '';
state.fontIconPlaceholder = props.modelValue;
};
// input input
const onIconBlur = () => {
setTimeout(() => {
const icon = state.fontIconSheetsList.filter((icon) => icon === state.fontIconSearch);
if (icon.length <= 0) state.fontIconSearch = '';
}, 300);
};
// icon
const initModeValueEcho = () => {
if (props.modelValue === '') return (state.fontIconPlaceholder = props.placeholder);
state.fontIconPlaceholder = props.modelValue;
state.fontIconPrefix = props.modelValue;
};
// icon type all alieleawe
const initFontIconTypeEcho = () => {
if (props.modelValue.indexOf('iconfont') > -1) onIconChange('ali');
else if (props.modelValue.indexOf('ele-') > -1) onIconChange('ele');
else if (props.modelValue.indexOf('fa') > -1) onIconChange('awe');
else onIconChange('ali');
};
//
const fontIconSheetsFilterList = computed(() => {
if (!state.fontIconSearch) return state.fontIconSheetsList;
let search = state.fontIconSearch.trim().toLowerCase();
return state.fontIconSheetsList.filter((item) => {
if (item.toLowerCase().indexOf(search) !== -1) return item;
});
});
// input
const getInputWidth = () => {
nextTick(() => {
state.fontIconWidth = inputWidthRef.value.$el.offsetWidth;
});
};
//
const initResize = () => {
window.addEventListener('resize', () => {
getInputWidth();
});
};
//
const initFontIconData = async (type) => {
state.fontIconSheetsList = [];
if (type === 'ali') {
await initIconfont.ali().then((res) => {
// 使 `iconfont xxx`
state.fontIconSheetsList = res.map((i) => `iconfont ${i}`);
});
} else if (type === 'ele') {
await initIconfont.ele().then((res) => {
state.fontIconSheetsList = res;
});
} else if (type === 'awe') {
await initIconfont.awe().then((res) => {
// fontawesome使 `fa xxx`
state.fontIconSheetsList = res.map((i) => `fa ${i}`);
});
}
// input placeholder
// https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
state.fontIconPlaceholder = props.placeholder;
//
initModeValueEcho();
};
//
const onIconChange = (type) => {
state.fontIconType = type;
initFontIconData(type);
};
// icon
const onColClick = (v) => {
state.fontIconPlaceholder = v;
state.fontIconPrefix = v;
emit('get', state.fontIconPrefix);
emit('update:modelValue', state.fontIconPrefix);
};
// icon
const onClearFontIcon = () => {
state.fontIconPrefix = '';
emit('clear', state.fontIconPrefix);
emit('update:modelValue', state.fontIconPrefix);
};
// Popover
const onPopoverShow = () => {
initModeValueEcho();
initFontIconTypeEcho();
};
//
onMounted(() => {
initModeValueEcho();
initResize();
getInputWidth();
});
// modelValue
watch(
() => props.modelValue,
() => {
initModeValueEcho();
}
);
</script>

View File

@ -8,66 +8,54 @@
<i v-else :class="getIconName" :style="setIconSvgStyle" />
</template>
<script>
import { computed, defineComponent } from 'vue';
export default defineComponent({
name: 'svgIcon',
props: {
// svg
name: {
type: String,
},
// svg
size: {
type: Number,
default: () => 14,
},
// svg
color: {
type: String,
},
<script setup name="svgIcon">
//
const props = defineProps({
// svg
name: {
type: String,
},
setup(props) {
// 线
const linesString = ['https', 'http', '/src', '/assets', import.meta.env.VITE_PUBLIC_PATH];
// icon
const getIconName = computed(() => {
return props?.name;
});
// element plus svg
const isShowIconSvg = computed(() => {
return props?.name?.startsWith('ele-');
});
// 线
const isShowIconImg = computed(() => {
return linesString.find((str) => props.name?.startsWith(str));
});
//
const setIconSvgStyle = computed(() => {
return `font-size: ${props.size}px;color: ${props.color};`;
});
//
const setIconImgOutStyle = computed(() => {
return `width: ${props.size}px;height: ${props.size}px;display: inline-block;overflow: hidden;`;
});
//
// https://gitee.com/lyt-top/vue-next-admin/issues/I59ND0
const setIconSvgInsStyle = computed(() => {
const filterStyle = [];
const compatibles = ['-webkit', '-ms', '-o', '-moz'];
compatibles.forEach((j) => filterStyle.push(`${j}-filter: drop-shadow(${props.color} 30px 0);`));
return `width: ${props.size}px;height: ${props.size}px;position: relative;left: -${props.size}px;${filterStyle.join('')}`;
});
return {
getIconName,
isShowIconSvg,
isShowIconImg,
setIconSvgStyle,
setIconImgOutStyle,
setIconSvgInsStyle,
};
// svg
size: {
type: Number,
default: () => 14,
},
// svg
color: {
type: String,
},
});
// 线
// https://gitee.com/lyt-top/vue-next-admin/issues/I62OVL
const linesString = ['https', 'http', '/src', '/assets', 'data:image', import.meta.env.VITE_PUBLIC_PATH];
// icon
const getIconName = computed(() => {
return props?.name;
});
// element plus svg
const isShowIconSvg = computed(() => {
return props?.name?.startsWith('ele-');
});
// 线
const isShowIconImg = computed(() => {
return linesString.find((str) => props.name?.startsWith(str));
});
//
const setIconSvgStyle = computed(() => {
return `font-size: ${props.size}px;color: ${props.color};`;
});
//
const setIconImgOutStyle = computed(() => {
return `width: ${props.size}px;height: ${props.size}px;display: inline-block;overflow: hidden;`;
});
//
// https://gitee.com/lyt-top/vue-next-admin/issues/I59ND0
const setIconSvgInsStyle = computed(() => {
const filterStyle = [];
const compatibles = ['-webkit', '-ms', '-o', '-moz'];
compatibles.forEach((j) => filterStyle.push(`${j}-filter: drop-shadow(${props.color} 30px 0);`));
return `width: ${props.size}px;height: ${props.size}px;position: relative;left: -${props.size}px;${filterStyle.join('')}`;
});
</script>

View File

@ -1,5 +1,5 @@
import { authDirective } from '/@/utils/authDirective';
import { wavesDirective, dragDirective } from '/@/utils/customDirective';
import { authDirective } from './authDirective';
import { wavesDirective, dragDirective } from '/@/directive/customDirective';
/**
* 导出指令方法v-xxx

View File

@ -17,9 +17,11 @@ import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import mittBus from '/@/utils/mitt';
//
const Logo = defineAsyncComponent(() => import('/@/layout/logo/index.vue'));
const Vertical = defineAsyncComponent(() => import('/@/layout/navMenu/vertical.vue'));
//
const layoutAsideScrollbarRef = ref();
const stores = useRoutesList();
const storesThemeConfig = useThemeConfig();
@ -29,8 +31,9 @@ const { themeConfig } = storeToRefs(storesThemeConfig);
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
const state = reactive({
menuList: [],
clientWidth: '',
clientWidth: 0,
});
// /
const setCollapseStyle = computed(() => {
const { layout, isCollapse, menuBar } = themeConfig.value;
@ -63,19 +66,22 @@ const setCollapseStyle = computed(() => {
}
}
});
//
const closeLayoutAsideMobileMode = () => {
const el = document.querySelector('.layout-aside-mobile-mode');
el && el.parentNode?.removeChild(el);
const clientWidth = document.body.clientWidth;
if (clientWidth < 1000) themeConfig.value.isCollapse = false;
document.body.setAttribute('class', '');
};
// / logo
const setShowLogo = computed(() => {
let { layout, isShowLogo } = themeConfig.value;
return (isShowLogo && layout === 'defaults') || (isShowLogo && layout === 'columns');
});
//
const closeLayoutAsideMobileMode = () => {
const el = document.querySelector('.layout-aside-mobile-mode');
el?.setAttribute('style', 'animation: error-img-two 0.3s');
setTimeout(() => {
el?.parentNode?.removeChild(el);
}, 300);
const clientWidth = document.body.clientWidth;
if (clientWidth < 1000) themeConfig.value.isCollapse = false;
document.body.setAttribute('class', '');
};
// //
const setFilterRoutes = () => {
if (themeConfig.value.layout === 'columns') return false;
@ -84,7 +90,7 @@ const setFilterRoutes = () => {
//
const filterRoutesFun = (arr) => {
return arr
.filter((item) => !item.meta.isHide)
.filter((item) => !item.meta?.isHide)
.map((item) => {
item = Object.assign({}, item);
if (item.children) item.children = filterRoutesFun(item.children);
@ -102,24 +108,6 @@ const onAsideEnterLeave = (bool) => {
if (!bool) mittBus.emit('restoreDefault');
stores.setColumnsMenuHover(bool);
};
// themeConfig el-scrollbar
watch(themeConfig.value, (val) => {
if (val.isShowLogoChange !== val.isShowLogo) {
if (layoutAsideScrollbarRef.value) layoutAsideScrollbarRef.value.update();
}
});
// vuex
watch(
pinia.state,
(val) => {
let { layout, isClassicSplitMenu } = val.themeConfig.themeConfig;
if (layout === 'classic' && isClassicSplitMenu) return false;
setFilterRoutes();
},
{
deep: true,
}
);
//
onBeforeMount(() => {
initMenuFixed(document.body.clientWidth);
@ -144,4 +132,22 @@ onBeforeMount(() => {
closeLayoutAsideMobileMode();
});
});
// themeConfig el-scrollbar
watch(themeConfig.value, (val) => {
if (val.isShowLogoChange !== val.isShowLogo) {
if (layoutAsideScrollbarRef.value) layoutAsideScrollbarRef.value.update();
}
});
// pinia
watch(
pinia.state,
(val) => {
let { layout, isClassicSplitMenu } = val.themeConfig.themeConfig;
if (layout === 'classic' && isClassicSplitMenu) return false;
setFilterRoutes();
},
{
deep: true,
}
);
</script>

View File

@ -52,6 +52,7 @@ import { useRoutesList } from '/@/stores/routesList';
import { useThemeConfig } from '/@/stores/themeConfig';
import mittBus from '/@/utils/mitt';
//
const columnsAsideOffsetTopRefs = ref([]);
const columnsAsideActiveRef = ref();
const stores = useRoutesList();
@ -69,6 +70,7 @@ const state = reactive({
difference: 0,
routeSplit: [],
});
//
const setColumnsAsideMove = (k) => {
state.liIndex = k;
@ -111,17 +113,17 @@ const setFilterRoutes = () => {
state.columnsAsideList = filterRoutesFun(routesList.value);
const resData = setSendChildren(route.path);
if (Object.keys(resData).length <= 0) return false;
onColumnsAsideDown(resData.item[0].k);
onColumnsAsideDown(resData.item?.k);
mittBus.emit('setSendColumnsChildren', resData);
};
//
const setSendChildren = (path) => {
const currentPathSplit = path.split('/');
let currentData = {};
let currentData = { children: [] };
state.columnsAsideList.map((v, k) => {
if (v.path === `/${currentPathSplit[1]}`) {
v['k'] = k;
currentData['item'] = [{ ...v }];
currentData['item'] = { ...v };
currentData['children'] = [{ ...v }];
if (v.children) currentData['children'] = v.children;
}
@ -131,7 +133,7 @@ const setSendChildren = (path) => {
//
const filterRoutesFun = (arr) => {
return arr
.filter((item) => !item.meta.isHide)
.filter((item) => !item.meta?.isHide)
.map((item) => {
item = Object.assign({}, item);
if (item.children) item.children = filterRoutesFun(item.children);
@ -150,24 +152,6 @@ const setColumnsMenuHighlight = (path) => {
onColumnsAsideDown(currentSplitRoute.k);
}, 0);
};
//
watch(
pinia.state,
(val) => {
val.themeConfig.themeConfig.columnsAsideStyle === 'columnsRound' ? (state.difference = 3) : (state.difference = 0);
if (!val.routesList.isColumnsMenuHover && !val.routesList.isColumnsNavHover) {
state.liHoverIndex = null;
mittBus.emit('setSendColumnsChildren', setSendChildren(route.path));
} else {
state.liHoverIndex = state.liOldIndex;
if (!state.liOldPath) return false;
mittBus.emit('setSendColumnsChildren', setSendChildren(state.liOldPath));
}
},
{
deep: true,
}
);
//
onMounted(() => {
setFilterRoutes();
@ -186,6 +170,24 @@ onBeforeRouteUpdate((to) => {
setColumnsMenuHighlight(to.path);
mittBus.emit('setSendColumnsChildren', setSendChildren(to.path));
});
//
watch(
pinia.state,
(val) => {
val.themeConfig.themeConfig.columnsAsideStyle === 'columnsRound' ? (state.difference = 3) : (state.difference = 0);
if (!val.routesList.isColumnsMenuHover && !val.routesList.isColumnsNavHover) {
state.liHoverIndex = null;
mittBus.emit('setSendColumnsChildren', setSendChildren(route.path));
} else {
state.liHoverIndex = state.liOldIndex;
if (!state.liOldPath) return false;
mittBus.emit('setSendColumnsChildren', setSendChildren(state.liOldPath));
}
},
{
deep: true,
}
);
</script>
<style scoped lang="scss">

View File

@ -8,8 +8,10 @@
import { storeToRefs } from 'pinia';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
//
const NavBarsIndex = defineAsyncComponent(() => import('/@/layout/navBars/index.vue'));
//
const storesTagsViewRoutes = useTagsViewRoutes();
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
</script>

View File

@ -1,10 +1,15 @@
<template>
<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">
<el-scrollbar
ref="layoutMainScrollbarRef"
class="layout-main-scroll layout-backtop-header-fixed"
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-backtop :target="setBacktopClass" />
</el-main>
</template>
@ -14,15 +19,18 @@ import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { useThemeConfig } from '/@/stores/themeConfig';
import { NextLoading } from '/@/utils/loading';
//
const LayoutParentView = defineAsyncComponent(() => import('/@/layout/routerView/parent.vue'));
const LayoutFooter = defineAsyncComponent(() => import('/@/layout/footer/index.vue'));
const layoutMainScrollbarRef = ref('');
//
const layoutMainScrollbarRef = ref();
const route = useRoute();
const storesTagsViewRoutes = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
// footer /
const isFooter = computed(() => {
return themeConfig.value.isFooter && !route.meta.isIframe;
@ -31,6 +39,11 @@ const isFooter = computed(() => {
const isFixedHeader = computed(() => {
return themeConfig.value.isFixedHeader;
});
// Backtop
const setBacktopClass = computed(() => {
if (themeConfig.value.isFixedHeader) return `.layout-backtop-header-fixed .el-scrollbar__wrap`;
else return `.layout-backtop .el-scrollbar__wrap`;
});
//
const setMainHeight = computed(() => {
if (isTagsViewCurrenFull.value) return '0px';
@ -38,9 +51,9 @@ const setMainHeight = computed(() => {
if (isTagsview && layout !== 'classic') return '85px';
else return '51px';
});
//
onMounted(async () => {
NextLoading.done();
//
onMounted(() => {
NextLoading.done(600);
});
//

View File

@ -2,12 +2,14 @@
<div class="layout-footer pb15">
<div class="layout-footer-warp">
<div>vue-next-adminMade by lyt with </div>
<div class="mt5">版权所有深圳市xxx软件科技有限公司</div>
<div class="mt5">深圳市 xxx 公司版权所有</div>
</div>
</div>
</template>
<script setup name="layoutFooter"></script>
<script setup name="layoutFooter">
//
</script>
<style scoped lang="scss">
.layout-footer {

View File

@ -1,14 +1,14 @@
<template>
<component :is="layouts[themeConfig.layout]"></component>
<component :is="layouts[themeConfig.layout]" />
</template>
<script setup name="layout">
import { defineAsyncComponent } from 'vue';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { Local } from '/@/utils/storage';
import mittBus from '/@/utils/mitt';
//
const layouts = {
defaults: defineAsyncComponent(() => import('/@/layout/main/defaults.vue')),
classic: defineAsyncComponent(() => import('/@/layout/main/classic.vue')),
@ -16,8 +16,10 @@ const layouts = {
columns: defineAsyncComponent(() => import('/@/layout/main/columns.vue')),
};
//
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// ()
const onLayoutResize = () => {
if (!Local.get('oldLayout')) Local.set('oldLayout', themeConfig.value.layout);

View File

@ -1,16 +1,16 @@
<template>
<div v-show="isShowLockScreen">
<div v-show="state.isShowLockScreen">
<div class="layout-lock-screen-mask"></div>
<div class="layout-lock-screen-img" :class="{ 'layout-lock-screen-filter': state.isShowLoockLogin }"></div>
<div class="layout-lock-screen">
<div
class="layout-lock-screen-date"
ref="layoutLockScreenDateRef"
@mousedown="onDown"
@mousemove="onMove"
@mousedown="onDownPc"
@mousemove="onMovePc"
@mouseup="onEnd"
@touchstart.stop="onDown"
@touchmove.stop="onMove"
@touchstart.stop="onDownApp"
@touchmove.stop="onMoveApp"
@touchend.stop="onEnd"
>
<div class="layout-lock-screen-date-box">
@ -60,14 +60,16 @@
</template>
<script setup name="layoutLockScreen">
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { formatDate } from '/@/utils/formatTime';
import { Local } from '/@/utils/storage';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
//
const layoutLockScreenDateRef = ref();
const layoutLockScreenInputRef = ref();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const layoutLockScreenInputRef = ref();
const state = reactive({
transparency: 1,
downClientY: 0,
@ -85,21 +87,32 @@ const state = reactive({
isShowLockScreenIntervalTime: 0,
lockScreenPassword: '',
});
//
const onDown = (down) => {
// pc
const onDownPc = (down) => {
state.isFlags = true;
state.downClientY = down.touches ? down.touches[0].clientY : down.clientY;
state.downClientY = down.clientY;
};
//
const onMove = (move) => {
// app
const onDownApp = (down) => {
state.isFlags = true;
state.downClientY = down.touches[0].clientY;
};
// pc
const onMovePc = (move) => {
state.moveDifference = move.clientY - state.downClientY;
onMove();
};
// app
const onMoveApp = (move) => {
state.moveDifference = move.touches[0].clientY - state.downClientY;
onMove();
};
//
const onMove = () => {
if (state.isFlags) {
const el = state.querySelectorEl;
const opacitys = (state.transparency -= 1 / 200);
if (move.touches) {
state.moveDifference = move.touches[0].clientY - state.downClientY;
} else {
state.moveDifference = move.clientY - state.downClientY;
}
if (state.moveDifference >= 0) return false;
el.setAttribute('style', `top:${state.moveDifference}px;cursor:pointer;opacity:${opacitys};`);
if (state.moveDifference < -400) {

View File

@ -9,12 +9,14 @@
</template>
<script setup name="layoutLogo">
import logoMini from '/@/assets/logo-mini.svg';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import logoMini from '/@/assets/logo-mini.svg';
//
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// logo classic logo
const setShowLogo = computed(() => {
let { isCollapse, layout } = themeConfig.value;

View File

@ -15,32 +15,40 @@
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
//
const LayoutAside = defineAsyncComponent(() => import('/@/layout/component/aside.vue'));
const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'));
const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'));
const LayoutTagsView = defineAsyncComponent(() => import('/@/layout/navBars/tagsView/tagsView.vue'));
const layoutMainRef = ref('');
//
const layoutMainRef = ref();
const route = useRoute();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// tasgview
const isTagsview = computed(() => {
return themeConfig.value.isTagsview;
});
// scrollbar
const updateScrollbar = () => {
layoutMainRef.value.layoutMainScrollbarRef.update();
layoutMainRef.value?.layoutMainScrollbarRef.update();
};
//
const initScrollBarHeight = () => {
nextTick(() => {
setTimeout(() => {
updateScrollbar();
layoutMainRef.value.layoutMainScrollbarRef.wrap$.scrollTop = 0;
// '!' not null
layoutMainRef.value.layoutMainScrollbarRef.wrapRef.scrollTop = 0;
}, 500);
});
};
//
onMounted(() => {
initScrollBarHeight();
});
//
watch(
() => route.path,
@ -58,8 +66,4 @@ watch(
deep: true,
}
);
//
onMounted(() => {
initScrollBarHeight();
});
</script>

View File

@ -15,16 +15,19 @@
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
//
const LayoutAside = defineAsyncComponent(() => import('/@/layout/component/aside.vue'));
const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'));
const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'));
const ColumnsAside = defineAsyncComponent(() => import('/@/layout/component/columnsAside.vue'));
//
const layoutScrollbarRef = ref('');
const layoutMainRef = ref('');
const layoutMainRef = ref();
const route = useRoute();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
//
const updateScrollbar = () => {
// scrollbar
@ -37,11 +40,15 @@ const initScrollBarHeight = () => {
nextTick(() => {
setTimeout(() => {
updateScrollbar();
layoutScrollbarRef.value.wrap$.scrollTop = 0;
layoutMainRef.value.layoutMainScrollbarRef.wrap$.scrollTop = 0;
layoutScrollbarRef.value.wrapRef.scrollTop = 0;
layoutMainRef.value.layoutMainScrollbarRef.wrapRef.scrollTop = 0;
}, 500);
});
};
//
onMounted(() => {
initScrollBarHeight();
});
//
watch(
() => route.path,
@ -59,8 +66,4 @@ watch(
deep: true,
}
);
//
onMounted(() => {
initScrollBarHeight();
});
</script>

View File

@ -15,15 +15,18 @@ import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { NextLoading } from '/@/utils/loading';
//
const LayoutAside = defineAsyncComponent(() => import('/@/layout/component/aside.vue'));
const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'));
const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'));
//
const layoutScrollbarRef = ref('');
const layoutMainRef = ref('');
const layoutMainRef = ref();
const route = useRoute();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
//
const updateScrollbar = () => {
// scrollbar
@ -36,11 +39,16 @@ const initScrollBarHeight = () => {
nextTick(() => {
setTimeout(() => {
updateScrollbar();
layoutScrollbarRef.value.wrap$.scrollTop = 0;
layoutMainRef.value.layoutMainScrollbarRef.wrap$.scrollTop = 0;
layoutScrollbarRef.value.wrapRef.scrollTop = 0;
layoutMainRef.value.layoutMainScrollbarRef.wrapRef.scrollTop = 0;
}, 500);
});
};
//
onMounted(() => {
initScrollBarHeight();
NextLoading.done(600);
});
//
watch(
() => route.path,
@ -58,9 +66,4 @@ watch(
deep: true,
}
);
//
onMounted(() => {
initScrollBarHeight();
NextLoading.done(600);
});
</script>

View File

@ -9,13 +9,16 @@
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
//
const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'));
const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'));
const layoutMainRef = ref('');
//
const layoutMainRef = ref();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const route = useRoute();
// scrollbar
const updateScrollbar = () => {
layoutMainRef.value.layoutMainScrollbarRef.update();
@ -25,10 +28,14 @@ const initScrollBarHeight = () => {
nextTick(() => {
setTimeout(() => {
updateScrollbar();
layoutMainRef.value.layoutMainScrollbarRef.wrap$.scrollTop = 0;
layoutMainRef.value.layoutMainScrollbarRef.wrapRef.scrollTop = 0;
}, 500);
});
};
//
onMounted(() => {
initScrollBarHeight();
});
//
watch(
() => route.path,
@ -46,8 +53,4 @@ watch(
deep: true,
}
);
//
onMounted(() => {
initScrollBarHeight();
});
</script>

View File

@ -7,7 +7,7 @@
@click="onThemeConfigChange"
/>
<el-breadcrumb class="layout-navbars-breadcrumb-hide">
<transition-group name="breadcrumb" mode="out-in">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(v, k) in state.breadcrumbList" :key="!v.meta.tagsViewName ? v.meta.title : v.meta.tagsViewName">
<span v-if="k === state.breadcrumbList.length - 1" class="layout-navbars-breadcrumb-span">
<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="themeConfig.isBreadcrumbIcon" />
@ -30,8 +30,8 @@ import other from '/@/utils/other';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useRoutesList } from '/@/stores/routesList';
Local;
//
const stores = useRoutesList();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
@ -44,6 +44,7 @@ const state = reactive({
routeSplitFirst: '',
routeSplitIndex: 1,
});
//
const isShowBreadcrumb = computed(() => {
initRouteSplit(route.path);
@ -111,7 +112,6 @@ onBeforeRouteUpdate((to) => {
.layout-navbars-breadcrumb-icon {
cursor: pointer;
font-size: 18px;
margin-right: 15px;
color: var(--next-bg-topBarColor);
height: 100%;
width: 40px;

View File

@ -10,6 +10,7 @@
import { storeToRefs } from 'pinia';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
//
const stores = useTagsViewRoutes();
const { isTagsViewCurrenFull } = storeToRefs(stores);
@ -29,7 +30,6 @@ const onCloseFullscreen = () => {
width: 60px;
height: 60px;
border-radius: 100%;
position: relative;
cursor: pointer;
background: rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
@ -41,12 +41,12 @@ const onCloseFullscreen = () => {
color: #333333;
transition: all 0.3s ease;
}
&:hover {
}
&:hover {
transition: all 0.3s ease;
:deep(i) {
color: var(--el-color-primary);
transition: all 0.3s ease;
:deep(i) {
color: var(--el-color-primary);
transition: all 0.3s ease;
}
}
}
}

View File

@ -13,11 +13,13 @@ import { useRoutesList } from '/@/stores/routesList';
import { useThemeConfig } from '/@/stores/themeConfig';
import mittBus from '/@/utils/mitt';
//
const Breadcrumb = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/breadcrumb.vue'));
const User = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/user.vue'));
const Logo = defineAsyncComponent(() => import('/@/layout/logo/index.vue'));
const Horizontal = defineAsyncComponent(() => import('/@/layout/navMenu/horizontal.vue'));
//
const stores = useRoutesList();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
@ -26,6 +28,7 @@ const route = useRoute();
const state = reactive({
menuList: [],
});
// logo /
const setIsShowLogo = computed(() => {
let { isShowLogo, layout } = themeConfig.value;
@ -57,7 +60,7 @@ const delClassicChildren = (arr) => {
//
const filterRoutesFun = (arr) => {
return arr
.filter((item) => !item.meta.isHide)
.filter((item) => !item.meta?.isHide)
.map((item) => {
item = Object.assign({}, item);
if (item.children) item.children = filterRoutesFun(item.children);
@ -67,11 +70,11 @@ const filterRoutesFun = (arr) => {
//
const setSendClassicChildren = (path) => {
const currentPathSplit = path.split('/');
let currentData = {};
let currentData = { children: [] };
filterRoutesFun(routesList.value).map((v, k) => {
if (v.path === `/${currentPathSplit[1]}`) {
v['k'] = k;
currentData['item'] = [{ ...v }];
currentData['item'] = { ...v };
currentData['children'] = [{ ...v }];
if (v.children) currentData['children'] = v.children;
}

View File

@ -31,6 +31,7 @@
import { storeToRefs } from 'pinia';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
//
const storesTagsViewRoutes = useTagsViewRoutes();
const { tagsViewRoutes } = storeToRefs(storesTagsViewRoutes);
const layoutMenuAutocompleteRef = ref();
@ -40,6 +41,7 @@ const state = reactive({
menuQuery: '',
tagsViewList: [],
});
//
const openSearch = () => {
state.menuQuery = '';
@ -74,13 +76,13 @@ const createFilter = (queryString) => {
const initTageView = () => {
if (state.tagsViewList.length > 0) return false;
tagsViewRoutes.value.map((v) => {
if (!v.meta.isHide) state.tagsViewList.push({ ...v });
if (!v.meta?.isHide) state.tagsViewList.push({ ...v });
});
};
//
const onHandleSelect = (item) => {
let { path, redirect } = item;
if (item.meta.isLink && !item.meta.isIframe) window.open(item.meta.isLink);
if (item.meta?.isLink && !item.meta?.isIframe) window.open(item.meta?.isLink);
else if (redirect) router.push(redirect);
else router.push(path);
closeSearch();
@ -94,6 +96,7 @@ defineExpose({
<style scoped lang="scss">
.layout-search-dialog {
position: relative;
:deep(.el-dialog) {
.el-dialog__header,
.el-dialog__body {

View File

@ -413,7 +413,7 @@
import { ElMessage } from 'element-plus';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { getLightColor, getDarkColor } from '/@/utils/theme';
import { useChangeColor } from '/@/utils/theme';
import { verifyAndSpace } from '/@/utils/toolsValidate';
import { Local } from '/@/utils/storage';
import Watermark from '/@/utils/wartermark';
@ -421,12 +421,15 @@ import commonFunction from '/@/utils/commonFunction';
import other from '/@/utils/other';
import mittBus from '/@/utils/mitt';
//
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { copyText } = commonFunction();
const { getLightColor, getDarkColor } = useChangeColor();
const state = reactive({
isMobile: false,
});
//
const getThemeConfig = computed(() => {
return themeConfig.value;
@ -445,7 +448,7 @@ const onColorPickerChange = () => {
};
// 2 /
const onBgColorPickerChange = (bg) => {
document.documentElement.style.setProperty(`--next-bg-${bg}`, getThemeConfig.value[bg]);
document.documentElement.style.setProperty(`--next-bg-${bg}`, themeConfig.value[bg]);
if (bg === 'menuBar') {
document.documentElement.style.setProperty(`--next-bg-menuBar-light-1`, getLightColor(getThemeConfig.value.menuBar, 0.05));
}
@ -552,6 +555,7 @@ const onWartermarkTextInput = (val) => {
// 5
const onSetLayout = (layout) => {
Local.set('oldLayout', layout);
if (getThemeConfig.value.layout === layout) return false;
if (layout === 'transverse') getThemeConfig.value.isCollapse = false;
getThemeConfig.value.layout = layout;
getThemeConfig.value.isDrawer = false;
@ -566,7 +570,7 @@ const initLayoutChangeFun = () => {
onBgColorPickerChange('columnsMenuBar');
onBgColorPickerChange('columnsMenuBarColor');
};
// layoutScrollbarRef.value.update()
// layoutScrollbarRef.value.update()
const onDrawerClose = () => {
getThemeConfig.value.isFixedHeaderChange = false;
getThemeConfig.value.isShowLogoChange = false;
@ -603,6 +607,8 @@ const onCopyConfigClick = () => {
const onResetConfigClick = () => {
Local.clear();
window.location.reload();
// @ts-ignore
Local.set('version', __VERSION__);
};
//
const initSetStyle = () => {
@ -732,7 +738,7 @@ defineExpose({
line-height: 1;
letter-spacing: 2px;
white-space: nowrap;
color: var(--el-color-primary-light-4);
color: var(--el-color-primary-light-5);
text-align: center;
transform: rotate(30deg);
left: -1px;

View File

@ -6,9 +6,9 @@
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="medium" :disabled="state.disabledSize === 'large'">大型</el-dropdown-item>
<el-dropdown-item command="small" :disabled="state.disabledSize === 'default'">默认</el-dropdown-item>
<el-dropdown-item command="mini" :disabled="state.disabledSize === 'small'">小型</el-dropdown-item>
<el-dropdown-item command="large" :disabled="state.disabledSize === 'large'">大型</el-dropdown-item>
<el-dropdown-item command="default" :disabled="state.disabledSize === 'default'">默认</el-dropdown-item>
<el-dropdown-item command="small" :disabled="state.disabledSize === 'small'">小型</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@ -66,15 +66,17 @@
<script setup name="layoutBreadcrumbUser">
import { ElMessageBox, ElMessage } from 'element-plus';
import screenfull from 'screenfull';
import { Session, Local } from '/@/utils/storage';
import { storeToRefs } from 'pinia';
import { useUserInfo } from '/@/stores/userInfo';
import { useThemeConfig } from '/@/stores/themeConfig';
import mittBus from '/@/utils/mitt';
import { Session, Local } from '/@/utils/storage';
//
const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/userNews.vue'));
const Search = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/search.vue'));
//
const router = useRouter();
const stores = useUserInfo();
const storesThemeConfig = useThemeConfig();
@ -83,9 +85,9 @@ const { themeConfig } = storeToRefs(storesThemeConfig);
const searchRef = ref();
const state = reactive({
isScreenfull: false,
isShowUserNewsPopover: false,
disabledSize: '',
disabledSize: 'large',
});
//
const layoutUserFlexNum = computed(() => {
let num = '';
@ -163,7 +165,6 @@ const onComponentSizeChange = (size) => {
initI18nOrSize('globalComponentSize', 'disabledSize');
window.location.reload();
};
// /i18n
const initI18nOrSize = (value, attr) => {
state[attr] = Local.get('themeConfig')[value];

View File

@ -21,6 +21,7 @@
</template>
<script setup name="layoutBreadcrumbUserNews">
//
const state = reactive({
newsList: [
{
@ -35,6 +36,7 @@ const state = reactive({
},
],
});
//
const onAllReadClick = () => {
state.newsList = [];
@ -73,9 +75,9 @@ const onGoToGiteeClick = () => {
padding-bottom: 12px;
}
.content-box-msg {
color: var(--el-text-color-secondary);
margin-top: 5px;
margin-bottom: 5px;
color: var(--el-text-color-secondary);
}
.content-box-time {
color: var(--el-text-color-secondary);

View File

@ -9,11 +9,14 @@
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
//
const BreadcrumbIndex = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/index.vue'));
const TagsView = defineAsyncComponent(() => import('/@/layout/navBars/tagsView/tagsView.vue'));
//
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// tagsView
const setShowTagsView = computed(() => {
let { layout, isTagsview } = themeConfig.value;

View File

@ -30,6 +30,7 @@
</template>
<script setup name="layoutTagsViewContextmenu">
//
const props = defineProps({
dropdown: {
type: Object,
@ -41,7 +42,11 @@ const props = defineProps({
},
},
});
// /
const emit = defineEmits(['currentContextmenuClick']);
//
const state = reactive({
isShow: false,
dropdownList: [
@ -54,6 +59,7 @@ const state = reactive({
item: {},
arrowLeft: 10,
});
// x,y
const dropdowns = computed(() => {
// 117 `Dropdown `
@ -73,7 +79,7 @@ const onCurrentContextmenuClick = (contextMenuClickId) => {
//
const openContextmenu = (item) => {
state.item = item;
item.meta.isAffix ? (state.dropdownList[1].affix = true) : (state.dropdownList[1].affix = false);
item.meta?.isAffix ? (state.dropdownList[1].affix = true) : (state.dropdownList[1].affix = false);
closeContextmenu();
setTimeout(() => {
state.isShow = true;

View File

@ -60,29 +60,32 @@ import { isObjectValueEqual } from '/@/utils/arrayOperation';
import other from '/@/utils/other';
import mittBus from '/@/utils/mitt';
//
const Contextmenu = defineAsyncComponent(() => import('/@/layout/navBars/tagsView/contextmenu.vue'));
//
const tagsRefs = ref([]);
const scrollbarRef = ref();
const contextmenuRef = ref();
const tagsUlRef = ref();
const route = useRoute();
const router = useRouter();
const stores = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig();
const storesTagsViewRoutes = useTagsViewRoutes();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { tagsViewRoutes } = storeToRefs(storesTagsViewRoutes);
const storesKeepALiveNames = useKeepALiveNames();
const route = useRoute();
const router = useRouter();
const state = reactive({
routeActive: '',
routePath: route.path,
dropdown: { x: '', y: '' },
sortable: '',
tagsRefsIndex: 0,
tagsViewList: [],
sortable: '',
tagsViewRoutesList: [],
});
// tagsView
const setTagsStyle = computed(() => {
return themeConfig.value.tagsStyle;
@ -116,7 +119,7 @@ const isActive = (v) => {
const addBrowserSetSession = (tagsViewList) => {
Session.set('tagsViewList', tagsViewList);
};
// vuex tagsViewRoutes
// pinia tagsViewRoutes
const getTagsViewRoutes = async () => {
state.routeActive = await setTagsViewHighlight(route);
state.routePath = (await route.meta.isDynamic) ? route.meta.isDynamicPath : route.path;
@ -124,13 +127,13 @@ const getTagsViewRoutes = async () => {
state.tagsViewRoutesList = tagsViewRoutes.value;
initTagsView();
};
// vuex isAffix
// pinia isAffix
const initTagsView = async () => {
if (Session.get('tagsViewList') && getThemeConfig.value.isCacheTagsView) {
state.tagsViewList = await Session.get('tagsViewList');
} else {
await state.tagsViewRoutesList.map((v) => {
if (v.meta.isAffix && !v.meta.isHide) {
if (v.meta?.isAffix && !v.meta.isHide) {
v.url = setTagsViewHighlight(v);
state.tagsViewList.push({ ...v });
storesKeepALiveNames.addCachedView(v);
@ -143,20 +146,22 @@ const initTagsView = async () => {
};
// xxx/:id/:name"
const solveAddTagsView = async (path, to) => {
let isDynamicPath = to.meta.isDynamic ? to.meta.isDynamicPath : path;
let isDynamicPath = to?.meta?.isDynamic ? to.meta.isDynamicPath : path;
let current = state.tagsViewList.filter(
(v) =>
v.path === isDynamicPath &&
isObjectValueEqual(
to.meta.isDynamic ? (v.params ? v.params : null) : v.query ? v.query : null,
to.meta.isDynamic ? (to?.params ? to?.params : null) : to?.query ? to?.query : null
to?.meta?.isDynamic ? (v.params ? v.params : null) : v.query ? v.query : null,
to?.meta?.isDynamic ? (to?.params ? to?.params : null) : to?.query ? to?.query : null
)
);
if (current.length <= 0) {
// Avoid app logic that relies on enumerating keys on a component instance. The keys will be empty in production mode to avoid performance overhead.
let findItem = state.tagsViewRoutesList.find((v) => v.path === isDynamicPath);
if (!findItem || findItem.meta.isAffix || (findItem.meta.isLink && !findItem.meta.isIframe)) return false;
to.meta.isDynamic ? (findItem.params = to.params) : (findItem.query = to.query);
if (!findItem) return false;
if (findItem.meta.isAffix) return false;
if (findItem.meta.isLink && !findItem.meta.isIframe) return false;
to?.meta?.isDynamic ? (findItem.params = to.params) : (findItem.query = to?.query);
findItem.url = setTagsViewHighlight(findItem);
state.tagsViewList.push({ ...findItem });
await storesKeepALiveNames.addCachedView(findItem);
@ -165,16 +170,16 @@ const solveAddTagsView = async (path, to) => {
};
// tagsViewList Session Storage
const singleAddTagsView = (path, to) => {
let isDynamicPath = to.meta.isDynamic ? to.meta.isDynamicPath : path;
let isDynamicPath = to?.meta?.isDynamic ? to.meta.isDynamicPath : path;
state.tagsViewList.forEach((v) => {
if (
v.path === isDynamicPath &&
!isObjectValueEqual(
to.meta.isDynamic ? (v.params ? v.params : null) : v.query ? v.query : null,
to.meta.isDynamic ? (to?.params ? to?.params : null) : to?.query ? to?.query : null
to?.meta?.isDynamic ? (v.params ? v.params : null) : v.query ? v.query : null,
to?.meta?.isDynamic ? (to?.params ? to?.params : null) : to?.query ? to?.query : null
)
) {
to.meta.isDynamic ? (v.params = to.params) : (v.query = to.query);
to?.meta?.isDynamic ? (v.params = to.params) : (v.query = to?.query);
v.url = setTagsViewHighlight(v);
addBrowserSetSession(state.tagsViewList);
}
@ -185,17 +190,17 @@ const addTagsView = (path, to) => {
//
nextTick(async () => {
// https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
let item = '';
if (to && to.meta.isDynamic) {
let item;
if (to?.meta?.isDynamic) {
// xxx/:id/:name" tagsview
if (!getThemeConfig.value.isShareTagsView) await solveAddTagsView(path, to);
else await singleAddTagsView(path, to);
if (state.tagsViewList.some((v) => v.path === to.meta.isDynamicPath)) {
if (state.tagsViewList.some((v) => v.path === to?.meta?.isDynamicPath)) {
// () tagsViewList
addBrowserSetSession(state.tagsViewList);
return false;
}
item = state.tagsViewRoutesList.find((v) => v.path === to.meta.isDynamicPath);
item = state.tagsViewRoutesList.find((v) => v.path === to?.meta?.isDynamicPath);
} else {
// tagsview
if (!getThemeConfig.value.isShareTagsView) await solveAddTagsView(path, to);
@ -207,8 +212,9 @@ const addTagsView = (path, to) => {
}
item = state.tagsViewRoutesList.find((v) => v.path === path);
}
if (!item || (item.meta.isLink && !item.meta.isIframe)) return false;
if (to && to.meta.isDynamic) item.params = to?.params ? to?.params : route.params;
if (!item) return false;
if (item?.meta?.isLink && !item.meta.isIframe) return false;
if (to?.meta?.isDynamic) item.params = to?.params ? to?.params : route.params;
else item.query = to?.query ? to?.query : route.query;
item.url = setTagsViewHighlight(item);
await storesKeepALiveNames.addCachedView(item);
@ -231,12 +237,12 @@ const refreshCurrentTagsView = async (fullPath) => {
if (!item) return false;
await storesKeepALiveNames.delCachedView(item);
mittBus.emit('onTagsViewRefreshRouterView', fullPath);
if (item.meta.isKeepAlive) storesKeepALiveNames.addCachedView(item);
if (item.meta?.isKeepAlive) storesKeepALiveNames.addCachedView(item);
};
// 3 tagsViewisAffix
const closeCurrentTagsView = (path) => {
state.tagsViewList.map((v, k, arr) => {
if (!v.meta.isAffix) {
state.tagsViewList.map((v, k, arrs) => {
if (!v.meta?.isAffix) {
if (getThemeConfig.value.isShareTagsView ? v.path === path : v.url === path) {
storesKeepALiveNames.delCachedView(v);
state.tagsViewList.splice(k, 1);
@ -275,7 +281,7 @@ const closeOtherTagsView = (path) => {
if (Session.get('tagsViewList')) {
state.tagsViewList = [];
Session.get('tagsViewList').map((v) => {
if (v.meta.isAffix && !v.meta.isHide) {
if (v.meta?.isAffix && !v.meta.isHide) {
v.url = setTagsViewHighlight(v);
storesKeepALiveNames.delOthersCachedViews(v);
state.tagsViewList.push({ ...v });
@ -291,7 +297,7 @@ const closeAllTagsView = () => {
storesKeepALiveNames.delAllCachedViews();
state.tagsViewList = [];
Session.get('tagsViewList').map((v) => {
if (v.meta.isAffix && !v.meta.isHide) {
if (v.meta?.isAffix && !v.meta.isHide) {
v.url = setTagsViewHighlight(v);
state.tagsViewList.push({ ...v });
router.push({ path: state.tagsViewList[state.tagsViewList.length - 1].path });
@ -315,13 +321,8 @@ const getCurrentRouteItem = (item) => {
state.tagsViewList.forEach((v) => {
v.transUrl = transUrlParams(v);
if (v.transUrl) {
if (v.meta.isDynamic) {
// xxx/:id/:name"isDynamic
if (v.transUrl === transUrlParams(v) && v.transUrl === decodeURI(item.path)) resItem = v;
} else {
// tagsView /
if (v.transUrl === transUrlParams(v) && v.transUrl === item.commonUrl) resItem = v;
}
//
if (v.transUrl === transUrlParams(v) && v.transUrl === item.commonUrl) resItem = v;
} else {
//
if (v.path === decodeURI(item.path)) resItem = v;
@ -371,7 +372,7 @@ const onContextmenu = (v, e) => {
};
// tasgview
const onMousedownMenu = (v, e) => {
if (!v.meta.isAffix && e.which === 2) {
if (!v.meta?.isAffix && e.button === 1) {
const item = Object.assign({}, { contextMenuClickId: 1, ...v });
onCurrentContextmenuClick(item);
}
@ -381,18 +382,32 @@ const onTagsClick = (v, k) => {
state.tagsRefsIndex = k;
router.push(v);
};
// urltagsview
// urltagsview @ZzZz-RIPPER@dejavuuuuu
// https://gitee.com/lyt-top/vue-next-admin/issues/I5K3YO
// https://gitee.com/lyt-top/vue-next-admin/issues/I61VS9
const transUrlParams = (v) => {
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}`;
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(/^&/, '?')}`;
if (v.meta?.isDynamic) {
/**
*
* isFnClick 用于判断是通过方法调用还是直接右键菜单点击此处只针对动态路由
* 原因
* 1右键菜单点击时路由的 path 还是原始定义的路由格式/params/dynamic/details/:t/:id/:tagsViewName
* 2通过事件调用时路由的 path 不是原始定义的路由格式/params/dynamic/details/vue-next-admin/111/我是动态路由测试tagsViewName(非国际化)
*
* 所以右侧菜单点击时需要处理路径拼接 v.path.split(':')[0]得到路径 + 参数的完整路径
*/
return v.isFnClick ? decodeURI(v.path) : `${v.path.split(':')[0]}${path.replace(/^\//, '')}`;
} else {
return `${v.path}${path.replace(/^&/, '?')}`;
}
};
// tagsView 使使
const setTagsViewHighlight = (v) => {
@ -403,11 +418,11 @@ const setTagsViewHighlight = (v) => {
path += params[i];
}
// xxx/:id/:name"
return `${v.meta.isDynamic ? v.meta.isDynamicPath : v.path}-${path}`;
return `${v.meta?.isDynamic ? v.meta.isDynamicPath : v.path}-${path}`;
};
//
const onHandleScroll = (e) => {
scrollbarRef.value.$refs.scrollbarRef.$refs.wrap$.scrollLeft += e.wheelDelta / 4;
scrollbarRef.value.$refs.wrapRef.scrollLeft += e.wheelDelta / 4;
};
// tagsView
const tagsViewmoveToCurrentTag = () => {
@ -424,7 +439,7 @@ const tagsViewmoveToCurrentTag = () => {
// li
let liLast = tagsRefs.value[tagsRefs.value.length - 1];
//
let scrollRefs = scrollbarRef.value.$refs.wrap$;
let scrollRefs = scrollbarRef.value.$refs.wrapRef;
//
let scrollS = scrollRefs.scrollWidth;
//
@ -436,9 +451,9 @@ const tagsViewmoveToCurrentTag = () => {
// tags li dom
let liNextTag = tagsRefs.value[state.tagsRefsIndex + 1];
// tags li dom
let beforePrevL = '';
let beforePrevL = 0;
// tags li dom
let afterNextL = '';
let afterNextL = 0;
if (liDom === liFirst) {
//
scrollRefs.scrollLeft = 0;
@ -510,6 +525,8 @@ onBeforeMount(() => {
window.addEventListener('resize', onSortableResize);
// 0 1 2 3 4
mittBus.on('onCurrentContextmenuClick', (data) => {
// tagsView
data.isFnClick = true;
onCurrentContextmenuClick(data);
});
// /
@ -522,7 +539,7 @@ onBeforeMount(() => {
router.push('/home');
state.tagsViewList = [];
state.tagsViewRoutesList.map((v) => {
if (v.meta.isAffix && !v.meta.isHide) {
if (v.meta?.isAffix && !v.meta.isHide) {
v.url = setTagsViewHighlight(v);
state.tagsViewList.push({ ...v });
}

View File

@ -31,31 +31,36 @@
</template>
<script setup name="navMenuHorizontal">
import { onBeforeRouteUpdate } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useRoutesList } from '/@/stores/routesList';
import { useThemeConfig } from '/@/stores/themeConfig';
import { onBeforeRouteUpdate } from 'vue-router';
import { verifyUrl } from '/@/utils/toolsValidate';
import other from '/@/utils/other';
import mittBus from '/@/utils/mitt';
//
const SubItem = defineAsyncComponent(() => import('/@/layout/navMenu/subItem.vue'));
//
const props = defineProps({
//
menuList: {
type: Array,
default: () => [],
},
});
//
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,
defaultActive: '',
});
//
const menuLists = computed(() => {
return props.menuList;
@ -63,20 +68,20 @@ const menuLists = computed(() => {
//
const onElMenuHorizontalScroll = (e) => {
const eventDelta = e.wheelDelta || -e.deltaY * 40;
elMenuHorizontalScrollRef.value.$refs.wrap$.scrollLeft = elMenuHorizontalScrollRef.value.$refs.wrap$.scrollLeft + eventDelta / 4;
elMenuHorizontalScrollRef.value.$refs.wrapRef.scrollLeft = elMenuHorizontalScrollRef.value.$refs.wrapRef.scrollLeft + eventDelta / 4;
};
//
const initElMenuOffsetLeft = () => {
nextTick(() => {
let els = document.querySelector('.el-menu.el-menu--horizontal li.is-active');
if (!els) return false;
elMenuHorizontalScrollRef.value.$refs.wrap$.scrollLeft = els.offsetLeft;
elMenuHorizontalScrollRef.value.$refs.wrapRef.scrollLeft = els.offsetLeft;
});
};
//
const filterRoutesFun = (arr) => {
return arr
.filter((item) => !item.meta.isHide)
.filter((item) => !item.meta?.isHide)
.map((item) => {
item = Object.assign({}, item);
if (item.children) item.children = filterRoutesFun(item.children);
@ -86,11 +91,11 @@ const filterRoutesFun = (arr) => {
//
const setSendClassicChildren = (path) => {
const currentPathSplit = path.split('/');
let currentData = {};
let currentData = { children: [] };
filterRoutesFun(routesList.value).map((v, k) => {
if (v.path === `/${currentPathSplit[1]}`) {
v['k'] = k;
currentData['item'] = [{ ...v }];
currentData['item'] = { ...v };
currentData['children'] = [{ ...v }];
if (v.children) currentData['children'] = v.children;
}
@ -101,19 +106,16 @@ const setSendClassicChildren = (path) => {
const setCurrentRouterHighlight = (currentRoute) => {
const { path, meta } = currentRoute;
if (themeConfig.value.layout === 'classic') {
state.defaultActive = `/${path.split('/')[1]}`;
state.defaultActive = `/${path?.split('/')[1]}`;
} else {
const pathSplit = meta.isDynamic ? meta.isDynamicPath.split('/') : path.split('/');
if (pathSplit.length >= 4 && meta.isHide) state.defaultActive = pathSplit.splice(0, 3).join('/');
const pathSplit = meta?.isDynamic ? meta.isDynamicPath.split('/') : path.split('/');
if (pathSplit.length >= 4 && meta?.isHide) state.defaultActive = pathSplit.splice(0, 3).join('/');
else state.defaultActive = path;
}
};
//
const onALinkClick = (val) => {
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}`);
other.handleOpenLink(val);
};
//
onBeforeMount(() => {

View File

@ -25,25 +25,23 @@
</template>
<script setup name="navMenuSubItem">
import { useRouter } from 'vue-router';
import { verifyUrl } from '/@/utils/toolsValidate';
import other from '/@/utils/other';
//
const props = defineProps({
//
chil: {
type: Array,
default: () => [],
},
});
const router = useRouter();
//
const chils = computed(() => {
return props.chil;
});
//
const onALinkClick = (val) => {
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}`);
other.handleOpenLink(val);
};
</script>

View File

@ -31,28 +31,33 @@
</template>
<script setup name="navMenuVertical">
import { onBeforeRouteUpdate } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { onBeforeRouteUpdate } from 'vue-router';
import { verifyUrl } from '/@/utils/toolsValidate';
import other from '/@/utils/other';
//
const SubItem = defineAsyncComponent(() => import('/@/layout/navMenu/subItem.vue'));
//
const props = defineProps({
//
menuList: {
type: Array,
default: () => [],
},
});
//
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,
isCollapse: false,
});
//
const menuLists = computed(() => {
return props.menuList;
@ -64,26 +69,13 @@ const getThemeConfig = computed(() => {
//
const setParentHighlight = (currentRoute) => {
const { path, meta } = currentRoute;
const pathSplit = meta.isDynamic ? meta.isDynamicPath.split('/') : path.split('/');
if (pathSplit.length >= 4 && meta.isHide) return pathSplit.splice(0, 3).join('/');
const pathSplit = meta?.isDynamic ? meta.isDynamicPath.split('/') : path.split('/');
if (pathSplit.length >= 4 && meta?.isHide) return pathSplit.splice(0, 3).join('/');
else return path;
};
// /
watch(
themeConfig.value,
() => {
document.body.clientWidth <= 1000 ? (state.isCollapse = false) : (state.isCollapse = themeConfig.value.isCollapse);
},
{
immediate: true,
}
);
//
const onALinkClick = (val) => {
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}`);
other.handleOpenLink(val);
};
//
onMounted(() => {
@ -96,4 +88,14 @@ onBeforeRouteUpdate((to) => {
const clientWidth = document.body.clientWidth;
if (clientWidth < 1000) themeConfig.value.isCollapse = false;
});
// /
watch(
themeConfig.value,
() => {
document.body.clientWidth <= 1000 ? (state.isCollapse = false) : (state.isCollapse = themeConfig.value.isCollapse);
},
{
immediate: true,
}
);
</script>

View File

@ -20,26 +20,33 @@
</div>
</template>
<script setup name="layoutIfameView">
<script setup name="layoutIframeView">
//
const props = defineProps({
// iframe
refreshKey: {
type: String,
default: () => '',
},
// name
name: {
type: String,
default: () => 'slide-right',
},
// iframe
list: {
type: Array,
default: () => [],
},
});
//
const iframeRef = ref();
const route = useRoute();
// list
const setIframeList = computed(() => {
return props.list.filter((v) => v.meta.isIframeOpen);
return props.list.filter((v) => v.meta?.isIframeOpen);
});
// iframe path
const getRoutePath = computed(() => {
@ -52,7 +59,7 @@ const closeIframeLoading = (val, item) => {
iframeRef.value.forEach((v) => {
if (v.dataset.url === val) {
v.onload = () => {
if (item.meta.isIframeOpen && item.meta.loading) item.meta.loading = false;
if (item.meta?.isIframeOpen && item.meta.loading) item.meta.loading = false;
};
}
});

View File

@ -3,7 +3,7 @@
<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">页面 "{{ state.currentRouteMeta.title }}" 已在新窗口中打开</div>
<div class="layout-link-msg">页面 {{ state.title }} 已在新窗口中打开</div>
<el-button class="mt30" round size="default" @click="onGotoFullPage">
<i class="iconfont icon-lianjie"></i>
<span>立即前往体验</span>
@ -16,24 +16,25 @@
<script setup name="layoutLinkView">
import { verifyUrl } from '/@/utils/toolsValidate';
//
const route = useRoute();
const state = reactive({
currentRouteMeta: {
isLink: '',
title: '',
},
title: '',
isLink: '',
});
//
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}`);
if (verifyUrl(state.isLink)) window.open(state.isLink);
else window.open(`${origin}${pathname}#${state.isLink}`);
};
//
watch(
() => route.path,
() => {
state.currentRouteMeta = route.meta;
state.title = route.meta.title;
state.isLink = route.meta.isLink;
},
{
immediate: true,

View File

@ -20,8 +20,10 @@ import { useThemeConfig } from '/@/stores/themeConfig';
import { Session } from '/@/utils/storage';
import mittBus from '/@/utils/mitt';
//
const Iframes = defineAsyncComponent(() => import('/@/layout/routerView/iframes.vue'));
//
const route = useRoute();
const router = useRouter();
const storesKeepAliveNames = useKeepALiveNames();
@ -34,6 +36,7 @@ const state = reactive({
keepAliveNameList: [],
iframeList: [],
});
//
const setTransitionName = computed(() => {
return themeConfig.value.animation;
@ -69,7 +72,8 @@ onBeforeMount(() => {
state.keepAliveNameList = keepAliveNames.value;
});
});
}); //
});
//
onMounted(() => {
getIframeListRoutes();
// https://gitee.com/lyt-top/vue-next-admin/issues/I58U75
@ -82,7 +86,7 @@ onMounted(() => {
});
//
onUnmounted(() => {
mittBus.off('onTagsViewRefreshRouterView');
mittBus.off('onTagsViewRefreshRouterView', () => {});
});
// tagsView
// https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/38/files

View File

@ -0,0 +1,134 @@
<template>
<div class="upgrade-dialog">
<el-dialog
v-model="state.isUpgrade"
width="300px"
destroy-on-close
:show-close="false"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<div class="upgrade-title">
<div class="upgrade-title-warp">
<span class="upgrade-title-warp-txt">新版本升级</span>
<span class="upgrade-title-warp-version">v{{ state.version }}</span>
</div>
</div>
<div class="upgrade-content">
{{ getThemeConfig.globalTitle }} 新版本来啦马上更新尝鲜吧不用担心更新很快的哦
<div class="upgrade-content-desc mt5">提示更新会还原默认配置</div>
</div>
<div class="upgrade-btn">
<el-button round size="default" type="info" text @click="onCancel">残忍拒绝</el-button>
<el-button type="primary" round size="default" @click="onUpgrade" :loading="state.isLoading">{{ state.btnTxt }}</el-button>
</div>
</el-dialog>
</div>
</template>
<script setup name="layoutUpgrade">
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { Local } from '/@/utils/storage';
//
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const state = reactive({
isUpgrade: true,
version: __VERSION__,
isLoading: false,
btnTxt: '',
});
//
const getThemeConfig = computed(() => {
return themeConfig.value;
});
//
const onCancel = () => {
state.isUpgrade = false;
};
//
const onUpgrade = () => {
state.isLoading = true;
state.btnTxt = '更新中';
setTimeout(() => {
Local.clear();
window.location.reload();
Local.set('version', state.version);
}, 2000);
};
//
onMounted(() => {
setTimeout(() => {
state.btnTxt = '马上更新';
}, 200);
});
</script>
<style scoped lang="scss">
.upgrade-dialog {
:deep(.el-dialog) {
.el-dialog__body {
padding: 0 !important;
}
.el-dialog__header {
display: none !important;
}
.upgrade-title {
text-align: center;
height: 130px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
&::after {
content: '';
position: absolute;
background-color: var(--el-color-primary);
width: 130%;
height: 130px;
border-bottom-left-radius: 100%;
border-bottom-right-radius: 100%;
}
.upgrade-title-warp {
z-index: 1;
position: relative;
.upgrade-title-warp-txt {
color: var(--next-color-white);
font-size: 22px;
letter-spacing: 3px;
}
.upgrade-title-warp-version {
background-color: var(--next-color-white);
font-size: 12px;
position: absolute;
display: flex;
top: -2px;
right: -50px;
padding: 2px 4px;
border-radius: 2px;
}
}
}
.upgrade-content {
padding: 20px;
line-height: 22px;
.upgrade-content-desc {
color: var(--el-color-info-light-5);
font-size: 12px;
}
}
.upgrade-btn {
border-top: 1px solid var(--el-border-color-lighter, #ebeef5);
display: flex;
justify-content: space-around;
padding: 15px 20px;
.el-button {
width: 100%;
}
}
}
}
</style>

View File

@ -2,7 +2,7 @@ import { createApp } from 'vue';
import pinia from './stores/index';
import App from './App.vue';
import router from './router';
import { directive } from '/@/utils/directive';
import { directive } from '/@/directive/index';
import other from '/@/utils/other';
import ElementPlus from 'element-plus';

View File

@ -1,5 +1,5 @@
import { storeToRefs } from 'pinia';
import pinia from '../stores/index';
import pinia from '/@/stores/index';
import { useUserInfo } from '/@/stores/userInfo';
import { useRequestOldRoutes } from '/@/stores/requestOldRoutes';
import { Session } from '/@/utils/storage';
@ -10,18 +10,18 @@ import { useRoutesList } from '/@/stores/routesList';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { useMenuApi } from '/@/api/menu/index';
const menuApi = useMenuApi();
const layouModules = import.meta.glob('../layout/routerView/*.{vue,tsx}');
const viewsModules = import.meta.glob('../views/**/*.{vue,tsx}');
// 后端控制路由
// 引入 api 请求接口
const menuApi = useMenuApi();
/**
* 获取目录下的 .vue.tsx 全部文件
* @method import.meta.glob
* @link 参考https://cn.vitejs.dev/guide/features.html#json
*/
const layouModules = import.meta.glob('../layout/routerView/*.{vue,tsx}');
const viewsModules = import.meta.glob('../views/**/*.{vue,tsx}');
const dynamicViewsModules = Object.assign({}, { ...layouModules }, { ...viewsModules });
/**
@ -30,9 +30,8 @@ const dynamicViewsModules = Object.assign({}, { ...layouModules }, { ...viewsMod
* @method useUserInfo().setUserInfos() 触发初始化用户信息 pinia
* @method useRequestOldRoutes().setRequestOldRoutes() 存储接口原始路由未处理component根据需求选择使用
* @method setAddRoute 添加动态路由
* @method setFilterMenuAndCacheTagsViewRoutes 设置路由到 vuex routesList 已处理成多级嵌套路由及缓存多级嵌套数组处理后的一维数组
* @method setFilterMenuAndCacheTagsViewRoutes 设置路由到 pinia routesList 已处理成多级嵌套路由及缓存多级嵌套数组处理后的一维数组
*/
export async function initBackEndControlRoutes() {
// 界面 loading 动画开始执行
if (window.nextLoading === undefined) NextLoading.start();
@ -49,12 +48,12 @@ export async function initBackEndControlRoutes() {
dynamicRoutes[0].children = await backEndComponent(res.data);
// 添加动态路由
await setAddRoute();
// 设置路由到 vuex routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
// 设置路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
await setFilterMenuAndCacheTagsViewRoutes();
}
/**
* 设置路由到 vuex routesList 已处理成多级嵌套路由及缓存多级嵌套数组处理后的一维数组
* 设置路由到 pinia routesList 已处理成多级嵌套路由及缓存多级嵌套数组处理后的一维数组
* @description 用于左侧菜单横向菜单的显示
* @description 用于 tagsView菜单搜索中未过滤隐藏的(isHide)
*/
@ -109,9 +108,9 @@ export function getBackEndControlRoutes() {
const { userInfos } = storeToRefs(stores);
const auth = userInfos.value.roles[0];
// 管理员 admin
if (auth === 'admin') return menuApi.getMenuAdmin();
if (auth === 'admin') return menuApi.getAdminMenu();
// 其它用户 test
else return menuApi.getMenuTest();
else return menuApi.getTestMenu();
}
/**

View File

@ -1,7 +1,7 @@
import { storeToRefs } from 'pinia';
import { formatTwoStageRoutes, formatFlatteningRoutes, router } from '/@/router/index';
import { dynamicRoutes, notFoundAndNoPower } from '/@/router/route';
import pinia from '../stores/index';
import pinia from '/@/stores/index';
import { Session } from '/@/utils/storage';
import { useUserInfo } from '/@/stores/userInfo';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
@ -15,7 +15,7 @@ import { NextLoading } from '/@/utils/loading';
* @method NextLoading 界面 loading 动画开始执行
* @method useUserInfo(pinia).setUserInfos() 触发初始化用户信息 pinia
* @method setAddRoute 添加动态路由
* @method setFilterMenuAndCacheTagsViewRoutes 设置递归过滤有权限的路由到 vuex routesList 已处理成多级嵌套路由及缓存多级嵌套数组处理后的一维数组
* @method setFilterMenuAndCacheTagsViewRoutes 设置递归过滤有权限的路由到 pinia routesList 已处理成多级嵌套路由及缓存多级嵌套数组处理后的一维数组
*/
export async function initFrontEndControlRoutes() {
// 界面 loading 动画开始执行
@ -27,7 +27,7 @@ export async function initFrontEndControlRoutes() {
await useUserInfo(pinia).setUserInfos();
// 添加动态路由
await setAddRoute();
// 设置递归过滤有权限的路由到 vuex routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
// 设置递归过滤有权限的路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
await setFilterMenuAndCacheTagsViewRoutes();
}
@ -107,7 +107,7 @@ export function setCacheTagsViewRoutes() {
}
/**
* 设置递归过滤有权限的路由到 vuex routesList 已处理成多级嵌套路由及缓存多级嵌套数组处理后的一维数组
* 设置递归过滤有权限的路由到 pinia routesList 已处理成多级嵌套路由及缓存多级嵌套数组处理后的一维数组
* @description 用于左侧菜单横向菜单的显示
* @description 用于 tagsView菜单搜索中未过滤隐藏的(isHide)
*/

View File

@ -91,11 +91,11 @@ export const useThemeConfig = defineStore('themeConfig', {
// 是否开启 Footer 底部版权信息
isFooter: true,
// 是否开启灰色模式
isGrayscale: false,
isGrayscale: true,
// 是否开启色弱模式
isInvert: false,
// 是否开启水印
isWartermark: true,
isWartermark: false,
// 水印文案
wartermarkText: 'vue-next-admin',
@ -143,7 +143,7 @@ export const useThemeConfig = defineStore('themeConfig', {
}),
actions: {
setThemeConfig(data) {
this.themeConfig = data;
this.themeConfig = data.themeConfig;
},
},
});

View File

@ -18,6 +18,7 @@ export const useUserInfo = defineStore('userInfo', {
}),
actions: {
async setUserInfos() {
// 存储用户信息到浏览器缓存
if (Session.get('userInfo')) {
this.userInfos = Session.get('userInfo');
} else {
@ -62,8 +63,6 @@ export const useUserInfo = defineStore('userInfo', {
roles: defaultRoles,
authBtnList: defaultAuthBtnList,
};
// 存储用户信息到浏览器缓存
Session.set('userInfo', userInfos);
resolve(userInfos);
}, 0);
});

View File

@ -58,6 +58,7 @@
--el-fill-color-light: var(--next-color-hover-rgba) !important;
--el-bg-color-overlay: var(--el-color-primary-light-9) !important;
--el-mask-color: rgb(42 42 42 / 80%);
--el-fill-color-lighter: var(--next-color-hover-rgba) !important;
// button
.el-button {
@ -167,6 +168,12 @@
.el-pagination.is-background .el-pager li {
background-color: var(--next-bg-color);
}
/*深色模式时分页高亮问题*/
.el-pagination.is-background .btn-next.is-active,
.el-pagination.is-background .btn-prev.is-active,
.el-pagination.is-background .el-pager li.is-active {
color: var(--next-color-white) !important;
}
// radio
.el-radio-button:not(.is-active) .el-radio-button__inner {

View File

@ -3,7 +3,7 @@
/* Button 按钮
------------------------------- */
// 第三方字体图标大小
.el-button i.el-icon,
.el-button:not(.is-circle) i.el-icon,
.el-button i.iconfont,
.el-button i.fa,
.el-button--default i.iconfont,
@ -283,6 +283,19 @@
height: 100%;
}
/* Pagination 分页
------------------------------- */
.el-pagination__editor {
margin-right: 8px;
}
/*深色模式时分页高亮问题*/
.el-pagination.is-background .btn-next.is-active,
.el-pagination.is-background .btn-prev.is-active,
.el-pagination.is-background .el-pager li.is-active {
background-color: var(--el-color-primary) !important;
color: var(--el-color-white) !important;
}
/* Drawer 抽屉
------------------------------- */
.el-drawer {

View File

@ -2,7 +2,6 @@
@import 'common/transition.scss';
@import './other.scss';
@import './element.scss';
@import './iconSelector.scss';
@import './media/media.scss';
@import './waves.scss';
@import './dark.scss';

View File

@ -52,4 +52,8 @@
color: #666666;
}
}
// pagination 分页中的工具栏
.table-footer-tool {
display: none !important;
}
}

View File

@ -7,9 +7,9 @@
.el-pagination__jump {
display: none !important;
}
}
// 默认居中对齐
.el-pagination {
text-align: center !important;
// 默认居中对齐
.el-pagination,
.table-footer {
justify-content: center !important;
}
}

25
src/theme/tableTool.scss Normal file
View File

@ -0,0 +1,25 @@
.table-tool-popper {
padding: 0 !important;
.tool-box {
display: flex;
border-bottom: 1px solid var(--el-border-color-lighter);
box-sizing: border-box;
color: var(--el-text-color-primary);
height: 40px;
align-items: center;
}
.tool-item {
display: flex;
box-sizing: border-box;
color: var(--el-text-color-primary);
height: 35px;
align-items: center;
padding: var(--el-popover-padding);
&:hover {
background: var(--el-fill-color-lighter);
}
i {
opacity: 0.7;
}
}
}

View File

@ -5,21 +5,22 @@ import { formatDate } from '/@/utils/formatTime';
export default function () {
const { toClipboard } = useClipboard();
//百分比格式化
// 百分比格式化
const percentFormat = (row, column, cellValue) => {
return cellValue ? `${cellValue}%` : '-';
};
//列表日期时间格式化
// 列表日期时间格式化
const dateFormatYMD = (row, column, cellValue) => {
if (!cellValue) return '-';
return formatDate(new Date(cellValue), 'YYYY-mm-dd');
};
//列表日期时间格式化
// 列表日期时间格式化
const dateFormatYMDHMS = (row, column, cellValue) => {
if (!cellValue) return '-';
return formatDate(new Date(cellValue), 'YYYY-mm-dd HH:MM:SS');
};
//列表日期时间格式化
// 列表日期时间格式化
const dateFormatHMS = (row, column, cellValue) => {
if (!cellValue) return '-';
let time = 0;
@ -39,14 +40,14 @@ export default function () {
const copyText = (text) => {
return new Promise((resolve, reject) => {
try {
//复制
// 复制
toClipboard(text);
//下面可以设置复制成功的提示框等操作
ElMessage.success('复制成功');
// 下面可以设置复制成功的提示框等操作
ElMessage.success('复制成功!');
resolve(text);
} catch (e) {
//复制失败
ElMessage.error('复制失败');
// 复制失败
ElMessage.error('复制失败!');
reject(e);
}
});

View File

@ -14,18 +14,18 @@ export const NextLoading = {
div.setAttribute('class', 'loading-next');
const htmls = `
<div class="loading-next-box">
<div class="loading-next-box-warp">
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-warp">
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
<div class="loading-next-box-item"></div>
</div>
</div>
</div>
`;
div.innerHTML = htmls;
bodys.insertBefore(div, bodys.childNodes[0]);

View File

@ -1,11 +1,13 @@
import { nextTick } from 'vue';
import { nextTick, defineAsyncComponent } from 'vue';
import * as svg from '@element-plus/icons-vue';
import router from '/@/router/index';
import pinia from '../stores/index';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { Local } from '/@/utils/storage';
import SvgIcon from '/@/components/svgIcon/index.vue';
// 引入组件
const SvgIcon = defineAsyncComponent(() => import('/@/components/svgIcon/index.vue'));
/**
* 导出全局注册 element plus svg 图标
@ -150,6 +152,17 @@ export function handleEmpty(list) {
return arr;
}
/**
* 打开外部链接
* @param val 当前点击项菜单
*/
export function handleOpenLink(val) {
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}`);
}
/**
* 统一批量导出
* @method elSvg 导出全局注册 element plus svg 图标
@ -160,6 +173,7 @@ export function handleEmpty(list) {
* @method deepClone 对象深克隆
* @method isMobile 判断是否是移动端
* @method handleEmpty 判断数组对象中所有属性是否为空为空则删除当前行对象
* @method handleOpenLink 打开外部链接
*/
const other = {
elSvg: (app) => {
@ -186,6 +200,9 @@ const other = {
handleEmpty: (list) => {
return handleEmpty(list);
},
handleOpenLink: (val) => {
handleOpenLink(val);
},
};
// 统一批量导出

View File

@ -14,7 +14,7 @@ service.interceptors.request.use(
(config) => {
// 在发送请求之前做些什么 token
if (Session.get('token')) {
config.headers.common['Authorization'] = `${Session.get('token')}`;
config.headers['Authorization'] = `${Session.get('token')}`;
}
return config;
},

View File

@ -1,5 +1,5 @@
// 字体图标 url
const cssCdnUrlList = ['//at.alicdn.com/t/font_2298093_y6u00apwst.css', '//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css'];
const cssCdnUrlList = ['//at.alicdn.com/t/c/font_2298093_rnp72ifj3ba.css', '//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css'];
// 第三方 js url
const jsCdnUrlList = [];

View File

@ -1,59 +1,63 @@
import { ElMessage } from 'element-plus';
/**
* hex颜色转rgb颜色
* @param str 颜色值字符串
* @returns 返回处理后的颜色值
* 颜色转换函数
* @method hexToRgb hex 颜色转 rgb 颜色
* @method rgbToHex rgb 颜色转 Hex 颜色
* @method getDarkColor 加深颜色值
* @method getLightColor 变浅颜色值
*/
export function hexToRgb(str) {
let hexs = '';
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(str)) return ElMessage.warning('输入错误的hex');
str = str.replace('#', '');
hexs = str.match(/../g);
for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16);
return hexs;
}
/**
* rgb颜色转Hex颜色
* @param r 代表红色
* @param g 代表绿色
* @param b 代表蓝色
* @returns 返回处理后的颜色值
*/
export function rgbToHex(r, g, b) {
let reg = /^\d{1,3}$/;
if (!reg.test(r) || !reg.test(g) || !reg.test(b)) return ElMessage.warning('输入错误的rgb颜色值');
let hexs = [r.toString(16), g.toString(16), b.toString(16)];
for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`;
return `#${hexs.join('')}`;
}
/**
* 加深颜色值
* @param color 颜色值字符串
* @param level 加深的程度限0-1之间
* @returns 返回处理后的颜色值
*/
export function getDarkColor(color, level) {
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return ElMessage.warning('输入错误的hex颜色值');
let rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.floor(rgb[i] * (1 - level));
return rgbToHex(rgb[0], rgb[1], rgb[2]);
}
/**
* 变浅颜色值
* @param color 颜色值字符串
* @param level 加深的程度限0-1之间
* @returns 返回处理后的颜色值
*/
export function getLightColor(color, level) {
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return ElMessage.warning('输入错误的hex颜色值');
let rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i]);
return rgbToHex(rgb[0], rgb[1], rgb[2]);
export function useChangeColor() {
// str 颜色值字符串
const hexToRgb = (str) => {
let hexs = '';
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(str)) {
ElMessage.warning('输入错误的hex');
return '';
}
str = str.replace('#', '');
hexs = str.match(/../g);
for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16);
return hexs;
};
// r 代表红色 | g 代表绿色 | b 代表蓝色
const rgbToHex = (r, g, b) => {
let reg = /^\d{1,3}$/;
if (!reg.test(r) || !reg.test(g) || !reg.test(b)) {
ElMessage.warning('输入错误的rgb颜色值');
return '';
}
let hexs = [r.toString(16), g.toString(16), b.toString(16)];
for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`;
return `#${hexs.join('')}`;
};
// color 颜色值字符串 | level 变浅的程度限0-1之间
const getDarkColor = (color, level) => {
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) {
ElMessage.warning('输入错误的hex颜色值');
return '';
}
let rgb = useChangeColor().hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.floor(rgb[i] * (1 - level));
return useChangeColor().rgbToHex(rgb[0], rgb[1], rgb[2]);
};
// color 颜色值字符串 | level 加深的程度限0-1之间
const getLightColor = (color, level) => {
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) {
ElMessage.warning('输入错误的hex颜色值');
return '';
}
let rgb = useChangeColor().hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i]);
return useChangeColor().rgbToHex(rgb[0], rgb[1], rgb[2]);
};
return {
hexToRgb,
rgbToHex,
getDarkColor,
getLightColor,
};
}

View File

@ -22,7 +22,7 @@
</div>
</template>
<script setup name="401">
<script setup name="noPower">
import { Session } from '/@/utils/storage';
//

View File

@ -22,8 +22,10 @@
</div>
</template>
<script setup name="404">
<script setup name="notFound">
//
const router = useRouter();
//
const onGoHome = () => {
router.push('/');

View File

@ -71,13 +71,7 @@ import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
const globalHome = {
homeChartOne: null,
homeChartTwo: null,
homeCharThree: null,
dispose: [null, '', undefined],
};
//
const homeLineRef = ref();
const homePieRef = ref();
const homeBarRef = ref();
@ -86,6 +80,12 @@ const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
const state = reactive({
global: {
homeChartOne: null,
homeChartTwo: null,
homeCharThree: null,
dispose: [null, '', undefined],
},
homeOne: [
{
num1: '125,12',
@ -190,8 +190,8 @@ const state = reactive({
// 线
const initLineChart = () => {
if (!globalHome.dispose.some((b) => b === globalHome.homeChartOne)) globalHome.homeChartOne.dispose();
globalHome.homeChartOne = echarts.init(homeLineRef.value, state.charts.theme);
if (!state.global.dispose.some((b) => b === state.global.homeChartOne)) state.global.homeChartOne.dispose();
state.global.homeChartOne = markRaw(echarts.init(homeLineRef.value, state.charts.theme));
const option = {
backgroundColor: state.charts.bgColor,
title: {
@ -267,13 +267,13 @@ const initLineChart = () => {
},
],
};
globalHome.homeChartOne.setOption(option);
state.myCharts.push(globalHome.homeChartOne);
state.global.homeChartOne.setOption(option);
state.myCharts.push(state.global.homeChartOne);
};
//
const initPieChart = () => {
if (!globalHome.dispose.some((b) => b === globalHome.homeChartTwo)) globalHome.homeChartTwo.dispose();
globalHome.homeChartTwo = echarts.init(homePieRef.value, state.charts.theme);
if (!state.global.dispose.some((b) => b === state.global.homeChartTwo)) state.global.homeChartTwo.dispose();
state.global.homeChartTwo = markRaw(echarts.init(homePieRef.value, state.charts.theme));
var getname = ['房屋及结构物', '专用设备', '通用设备', '文物和陈列品', '图书、档案'];
var getvalue = [34.2, 38.87, 17.88, 9.05, 2.05];
var data = [];
@ -352,13 +352,13 @@ const initPieChart = () => {
},
],
};
globalHome.homeChartTwo.setOption(option);
state.myCharts.push(globalHome.homeChartTwo);
state.global.homeChartTwo.setOption(option);
state.myCharts.push(state.global.homeChartTwo);
};
//
const initBarChart = () => {
if (!globalHome.dispose.some((b) => b === globalHome.homeCharThree)) globalHome.homeCharThree.dispose();
globalHome.homeCharThree = echarts.init(homeBarRef.value, state.charts.theme);
if (!state.global.dispose.some((b) => b === state.global.homeCharThree)) state.global.homeCharThree.dispose();
state.global.homeCharThree = markRaw(echarts.init(homeBarRef.value, state.charts.theme));
const option = {
backgroundColor: state.charts.bgColor,
title: {
@ -483,8 +483,8 @@ const initBarChart = () => {
},
],
};
globalHome.homeCharThree.setOption(option);
state.myCharts.push(globalHome.homeCharThree);
state.global.homeCharThree.setOption(option);
state.myCharts.push(state.global.homeCharThree);
};
// echarts resize
const initEchartsResizeFun = () => {
@ -508,14 +508,14 @@ onMounted(() => {
onActivated(() => {
initEchartsResizeFun();
});
// vuex tagsview resize /
// pinia tagsview resize /
watch(
() => isTagsViewCurrenFull.value,
() => {
initEchartsResizeFun();
}
);
// vuex
// pinia
watch(
() => themeConfig.value.isIsDark,
(isIsDark) => {
@ -585,7 +585,7 @@ $homeNavLengh: 8;
animation-name: error-num;
animation-duration: 0.5s;
animation-fill-mode: forwards;
animation-delay: calc($i/10) + s;
animation-delay: calc($i/4) + s;
}
}
}

View File

@ -54,6 +54,7 @@ import { Session } from '/@/utils/storage';
import { formatAxis } from '/@/utils/formatTime';
import { NextLoading } from '/@/utils/loading';
//
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const route = useRoute();
@ -69,6 +70,7 @@ const state = reactive({
signIn: false,
},
});
//
const currentTime = computed(() => {
return formatAxis(new Date());

View File

@ -32,6 +32,7 @@
</template>
<script setup name="loginMobile">
//
const state = reactive({
ruleForm: {
userName: '',

View File

@ -11,7 +11,9 @@
<script setup name="loginScan">
import QRCode from 'qrcodejs2-fixes';
//
const qrcodeRef = ref(null);
//
const initQrcode = () => {
nextTick(() => {

View File

@ -50,16 +50,19 @@ import logoMini from '/@/assets/logo-mini.svg';
import loginMain from '/@/assets/login-main.svg';
import loginBg from '/@/assets/login-bg.svg';
//
const Account = defineAsyncComponent(() => import('/@/views/login/component/account.vue'));
const Mobile = defineAsyncComponent(() => import('/@/views/login/component/mobile.vue'));
const Scan = defineAsyncComponent(() => import('/@/views/login/component/scan.vue'));
//
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const state = reactive({
tabsActiveName: 'account',
isScan: false,
});
//
const getThemeConfig = computed(() => {
return themeConfig.value;
@ -202,6 +205,7 @@ onMounted(() => {
letter-spacing: 3px;
animation: logoAnimation 0.3s ease;
animation-delay: 0.3s;
color: var(--el-text-color-primary);
}
.login-right-warp-main-form {
flex: 1;

View File

@ -68,6 +68,7 @@
</template>
<script setup name="systemAddDept">
//
const state = reactive({
isShowDialog: false,
ruleForm: {
@ -82,6 +83,7 @@ const state = reactive({
},
deptData: [], //
});
//
const openDialog = () => {
state.isShowDialog = true;
@ -104,7 +106,7 @@ const initTableData = () => {
deptName: 'vueNextAdmin',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '顶级部门',
id: Math.random(),
children: [
@ -112,7 +114,7 @@ const initTableData = () => {
deptName: 'IT外包服务',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '总部',
id: Math.random(),
},
@ -120,7 +122,7 @@ const initTableData = () => {
deptName: '资本控股',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '分部',
id: Math.random(),
},

View File

@ -67,7 +67,8 @@
</div>
</template>
<script setup name="logsystemEditDeptin">
<script setup name="systemEditDept">
//
const state = reactive({
isShowDialog: false,
ruleForm: {
@ -82,6 +83,7 @@ const state = reactive({
},
deptData: [], //
});
//
const openDialog = (row) => {
row.deptLevel = ['vueNextAdmin'];
@ -109,7 +111,7 @@ const initTableData = () => {
deptName: 'vueNextAdmin',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '顶级部门',
id: Math.random(),
children: [
@ -117,7 +119,7 @@ const initTableData = () => {
deptName: 'IT外包服务',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '总部',
id: Math.random(),
},
@ -125,7 +127,7 @@ const initTableData = () => {
deptName: '资本控股',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '分部',
id: Math.random(),
},

View File

@ -54,9 +54,11 @@
<script setup name="systemDept">
import { ElMessageBox, ElMessage } from 'element-plus';
//
const AddDept = defineAsyncComponent(() => import('/@/views/system/dept/component/addDept.vue'));
const EditDept = defineAsyncComponent(() => import('/@/views/system/dept/component/editDept.vue'));
//
const addDeptRef = ref();
const editDeptRef = ref();
const state = reactive({
@ -70,13 +72,14 @@ const state = reactive({
},
},
});
//
const initTableData = () => {
state.tableData.data.push({
deptName: 'vueNextAdmin',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '顶级部门',
id: Math.random(),
children: [
@ -84,7 +87,7 @@ const initTableData = () => {
deptName: 'IT外包服务',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '总部',
id: Math.random(),
},
@ -92,7 +95,7 @@ const initTableData = () => {
deptName: '资本控股',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '分部',
id: Math.random(),
},

View File

@ -11,7 +11,7 @@
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="字段名">
<el-input v-model="state.ruleForm.fieldName" placeholder="请输入字段名,拼接 state.ruleForm.list" clearable></el-input>
<el-input v-model="state.ruleForm.fieldName" placeholder="请输入字段名,拼接 ruleForm.list" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
@ -64,24 +64,18 @@
</template>
<script setup name="systemAddDic">
//
const state = reactive({
isShowDialog: false,
ruleForm: {
dicName: '', //
fieldName: '', //
status: true, //
list: [
// +
{
id: Math.random(),
label: '',
value: '',
},
],
list: [], // +
describe: '', //
fieldNameList: [], // : [{ + }]
},
});
//
const openDialog = () => {
state.isShowDialog = true;

View File

@ -11,7 +11,7 @@
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="字段名">
<el-input v-model="state.ruleForm.fieldName" placeholder="请输入字段名,拼接 state.ruleForm.list" clearable></el-input>
<el-input v-model="state.ruleForm.fieldName" placeholder="请输入字段名,拼接 ruleForm.list" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
@ -64,24 +64,18 @@
</template>
<script setup name="systemEditDic">
//
const state = reactive({
isShowDialog: false,
ruleForm: {
dicName: '', //
fieldName: '', //
status: true, //
list: [
// +
{
id: Math.random(),
label: '',
value: '',
},
],
list: [], // +
describe: '', //
fieldNameList: [], // : [{ + }]
},
});
//
const openDialog = (row) => {
if (row.fieldName === 'SYS_UERINFO') {

View File

@ -57,9 +57,11 @@
<script setup name="systemDic">
import { ElMessageBox, ElMessage } from 'element-plus';
//
const AddDic = defineAsyncComponent(() => import('/@/views/system/dic/component/addDic.vue'));
const EditDic = defineAsyncComponent(() => import('/@/views/system/dic/component/editDic.vue'));
//
const addDicRef = ref();
const editDicRef = ref();
const state = reactive({
@ -73,6 +75,7 @@ const state = reactive({
},
},
});
//
const initTableData = () => {
const data = [];
@ -83,6 +86,7 @@ const initTableData = () => {
describe: i === 0 ? '这是角色字典' : '这是用户性别字典',
status: true,
createTime: new Date().toLocaleString(),
list: [],
});
}
state.tableData.data = data;

View File

@ -65,7 +65,7 @@
v-model="state.ruleForm.meta.isLink"
placeholder="外链/内嵌时链接地址http:xxx.com"
clearable
:disabled="state.ruleForm.isLink === '' || !state.ruleForm.isLink"
:disabled="!state.ruleForm.isLink"
>
</el-input>
</el-form-item>
@ -148,9 +148,12 @@
<script setup name="systemAddMenu">
import { storeToRefs } from 'pinia';
import { useRoutesList } from '/@/stores/routesList';
import IconSelector from '/@/components/iconSelector/index.vue';
// import { setBackEndControlRefreshRoutes } from "/@/router/backEnd";
//
const IconSelector = defineAsyncComponent(() => import('/@/components/iconSelector/index.vue'));
//
const stores = useRoutesList();
const { routesList } = storeToRefs(stores);
const state = reactive({
@ -173,17 +176,18 @@ const state = reactive({
isAffix: false, //
isLink: '', // /http:xxx.com`1isLink: `
isIframe: false, // `1isIframe:true 2isLink`
roles: '', //
},
btnPower: '', //
},
menuData: [], //
});
// vuex
// pinia
const getMenuData = (routes) => {
const arr = [];
routes.map((val) => {
val['title'] = val.meta.title;
val['id'] = Math.random();
val['title'] = val.meta?.title;
arr.push({ ...val });
if (val.children) getMenuData(val.children);
});
@ -199,11 +203,8 @@ const closeDialog = () => {
};
//
const onSelectIframeChange = () => {
if (state.ruleForm.meta.isIframe) {
state.ruleForm.isLink = true;
} else {
state.ruleForm.isLink = '';
}
if (state.ruleForm.meta.isIframe) state.ruleForm.isLink = true;
else state.ruleForm.isLink = false;
};
//
const onCancel = () => {

View File

@ -65,7 +65,7 @@
v-model="state.ruleForm.meta.isLink"
placeholder="外链/内嵌时链接地址http:xxx.com"
clearable
:disabled="state.ruleForm.isLink === '' || !state.ruleForm.isLink"
:disabled="!state.ruleForm.isLink"
>
</el-input>
</el-form-item>
@ -148,9 +148,12 @@
<script setup name="systemEditMenu">
import { storeToRefs } from 'pinia';
import { useRoutesList } from '/@/stores/routesList';
import IconSelector from '/@/components/iconSelector/index.vue';
// import { setBackEndControlRefreshRoutes } from "/@/router/backEnd";
//
const IconSelector = defineAsyncComponent(() => import('/@/components/iconSelector/index.vue'));
//
const stores = useRoutesList();
const { routesList } = storeToRefs(stores);
const state = reactive({
@ -179,12 +182,12 @@ const state = reactive({
},
menuData: [], //
});
// vuex
// pinia
const getMenuData = (routes) => {
const arr = [];
routes.map((val) => {
val['title'] = val.meta.title;
val['id'] = Math.random();
val['title'] = val.meta?.title;
arr.push({ ...val });
if (val.children) getMenuData(val.children);
});
@ -208,11 +211,8 @@ const closeDialog = () => {
};
//
const onSelectIframeChange = () => {
if (state.ruleForm.meta.isIframe) {
state.ruleForm.isLink = true;
} else {
state.ruleForm.isLink = '';
}
if (state.ruleForm.meta.isIframe) state.ruleForm.isLink = true;
else state.ruleForm.isLink = false;
};
//
const onCancel = () => {

View File

@ -46,7 +46,7 @@
</el-table-column>
<el-table-column label="操作" show-overflow-tooltip width="140">
<template #default="scope">
<el-button size="small" text type="primary" @click="onOpenAddMenu(scope.row)">新增</el-button>
<el-button size="small" text type="primary" @click="onOpenAddMenu">新增</el-button>
<el-button size="small" text type="primary" @click="onOpenEditMenu(scope.row)">修改</el-button>
<el-button size="small" text type="primary" @click="onTabelRowDel(scope.row)">删除</el-button>
</template>
@ -59,17 +59,20 @@
</template>
<script setup name="systemMenu">
import { ElMessageBox, ElMessage } from 'element-plus';
import { storeToRefs } from 'pinia';
import { useRoutesList } from '/@/stores/routesList';
import { ElMessageBox, ElMessage } from 'element-plus';
//
const AddMenu = defineAsyncComponent(() => import('/@/views/system/menu/component/addMenu.vue'));
const EditMenu = defineAsyncComponent(() => import('/@/views/system/menu/component/editMenu.vue'));
const addMenuRef = ref();
const editMenuRef = ref();
//
const stores = useRoutesList();
const { routesList } = storeToRefs(stores);
const addMenuRef = ref();
const editMenuRef = ref();
// pinia
const menuTableData = computed(() => {
return routesList.value;

View File

@ -51,6 +51,7 @@
</template>
<script setup name="systemAddRole">
//
const state = reactive({
isShowDialog: false,
ruleForm: {
@ -66,6 +67,7 @@ const state = reactive({
label: 'label',
},
});
//
const openDialog = () => {
state.isShowDialog = true;

View File

@ -58,6 +58,7 @@
</template>
<script setup name="systemEditRole">
//
const state = reactive({
isShowDialog: false,
ruleForm: {
@ -73,6 +74,7 @@ const state = reactive({
label: 'label',
},
});
//
const openDialog = (row) => {
state.ruleForm = row;

View File

@ -2,7 +2,7 @@
<div class="system-role-container layout-padding">
<div class="system-role-padding layout-padding-auto layout-padding-view">
<div class="system-user-search mb15">
<el-input size="default" placeholder="请输入角色名称" style="max-width: 180px"> </el-input>
<el-input v-model="state.tableData.param.search" size="default" placeholder="请输入角色名称" style="max-width: 180px"> </el-input>
<el-button size="default" type="primary" class="ml10">
<el-icon>
<ele-Search />
@ -17,7 +17,7 @@
</el-button>
</div>
<el-table :data="state.tableData.data" style="width: 100%">
<el-table-column type="index" label="序号" width="50" />
<el-table-column type="index" label="序号" width="60" />
<el-table-column prop="roleName" label="角色名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="roleSign" label="角色标识" show-overflow-tooltip></el-table-column>
<el-table-column prop="sort" label="排序" show-overflow-tooltip></el-table-column>
@ -51,18 +51,20 @@
:total="state.tableData.total"
>
</el-pagination>
<AddRole ref="addRoleRef" />
<EditRole ref="editRoleRef" />
</div>
<AddRole ref="addRoleRef" />
<EditRole ref="editRoleRef" />
</div>
</template>
<script setup name="systemRole">
import { ElMessageBox, ElMessage } from 'element-plus';
//
const AddRole = defineAsyncComponent(() => import('/@/views/system/role/component/addRole.vue'));
const EditRole = defineAsyncComponent(() => import('/@/views/system/role/component/editRole.vue'));
//
const addRoleRef = ref();
const editRoleRef = ref();
const state = reactive({
@ -71,19 +73,17 @@ const state = reactive({
total: 0,
loading: false,
param: {
search: '',
pageNum: 1,
pageSize: 10,
},
},
formInline: {
user: '',
region: '',
},
});
//
const initTableData = () => {
const data = [];
for (let i = 0; i < 2; i++) {
for (let i = 0; i < 20; i++) {
data.push({
roleName: i === 0 ? '超级管理员' : '普通用户',
roleSign: i === 0 ? 'admin' : 'common',

View File

@ -89,6 +89,7 @@
</template>
<script setup name="systemAddUser">
//
const state = reactive({
isShowDialog: false,
ruleForm: {
@ -106,6 +107,7 @@ const state = reactive({
},
deptData: [], //
});
//
const openDialog = () => {
state.isShowDialog = true;
@ -128,7 +130,7 @@ const initTableData = () => {
deptName: 'vueNextAdmin',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '顶级部门',
id: Math.random(),
children: [
@ -136,7 +138,7 @@ const initTableData = () => {
deptName: 'IT外包服务',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '总部',
id: Math.random(),
},
@ -144,7 +146,7 @@ const initTableData = () => {
deptName: '资本控股',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '分部',
id: Math.random(),
},

View File

@ -89,6 +89,7 @@
</template>
<script setup name="systemEditUser">
//
const state = reactive({
isShowDialog: false,
ruleForm: {
@ -106,6 +107,7 @@ const state = reactive({
},
deptData: [], //
});
//
const openDialog = (row) => {
state.ruleForm = row;
@ -129,7 +131,7 @@ const initTableData = () => {
deptName: 'vueNextAdmin',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '顶级部门',
id: Math.random(),
children: [
@ -137,7 +139,7 @@ const initTableData = () => {
deptName: 'IT外包服务',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '总部',
id: Math.random(),
},
@ -145,7 +147,7 @@ const initTableData = () => {
deptName: '资本控股',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '分部',
id: Math.random(),
},

View File

@ -17,7 +17,7 @@
</el-button>
</div>
<el-table :data="state.tableData.data" style="width: 100%">
<el-table-column type="index" label="序号" width="50" />
<el-table-column type="index" label="序号" width="60" />
<el-table-column prop="userName" label="账户名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="userNickname" label="用户昵称" show-overflow-tooltip></el-table-column>
<el-table-column prop="roleSign" label="关联角色" show-overflow-tooltip></el-table-column>
@ -61,9 +61,11 @@
<script setup name="systemUser">
import { ElMessageBox, ElMessage } from 'element-plus';
//
const AddUer = defineAsyncComponent(() => import('/@/views/system/user/component/addUser.vue'));
const EditUser = defineAsyncComponent(() => import('/@/views/system/user/component/editUser.vue'));
//
const addUserRef = ref();
const editUserRef = ref();
const state = reactive({
@ -77,6 +79,7 @@ const state = reactive({
},
},
});
//
const initTableData = () => {
const data = [];
@ -132,3 +135,17 @@ onMounted(() => {
initTableData();
});
</script>
<style scoped lang="scss">
.system-user-container {
:deep(.el-card__body) {
display: flex;
flex-direction: column;
flex: 1;
overflow: auto;
.el-table {
flex: 1;
}
}
}
</style>

View File

@ -56,6 +56,9 @@ const viteConfig = defineConfig((mode) => {
},
},
css: { preprocessorOptions: { css: { charset: false } } },
define: {
__VERSION__: JSON.stringify(process.env.npm_package_version),
},
};
});