'admin-22.03.04:同步master分支v2.0.2版本内容,具体查看master的CHANGELOG.md'

This commit is contained in:
lyt
2022-03-04 18:26:27 +08:00
parent 2ec65911ee
commit 79482ddbea
100 changed files with 2492 additions and 2777 deletions

View File

@ -2,6 +2,10 @@
🎉🎉🔥 `vue-next-admin-template` 基于 vue-next-admin-v1.1.2 版本) vue3.x 、Typescript、vite、Element plus 等适配手机、平板、pc 的后台开源免费模板库vue2.x 请切换 vue-prev-admin 分支)
## 2.0.2
- 🎉 同步 master 分支 v2.0.2 版本内容,具体查看 master CHANGELOG.md
## 0.2.2
`2021.12.21`

View File

@ -82,18 +82,22 @@ cnpm run build
#### 💯 学习交流加 QQ 群
- 若加群了没同意(一般不会超过一天),那就是群满了,请换一个群试试
- 若加群了没同意(一般秒过),那就是群满了500 人群),请换一个群试试3 群未满
- 查看开发文档、<a href="https://lyt-top.gitee.io/vue-next-admin-preview/#/login" target="_blank">vue-next-admin</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>
<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=RdUY97Vx0T0vZ_1OOu-X1yFNkWgDwbjC&jump_from=webapi">
<img src="https://gitee.com/lyt-top/vue-next-admin-images/raw/master/user/qq1.png" width="220" height="220" alt="vue-next-admin 讨论群" title="vue-next-admin 讨论群1"/>
<img src="https://gitee.com/lyt-top/vue-next-admin-images/raw/master/user/qq1.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://gitee.com/lyt-top/vue-next-admin-images/raw/master/user/qq2.png" width="220" height="220" alt="vue-next-admin 讨论群" 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://gitee.com/lyt-top/vue-next-admin-images/raw/master/user/qq3.png" width="220" height="220" alt="vue-next-admin 讨论群3" title="vue-next-admin 讨论群3"/>
</a>
#### ❤️ 鸣谢列表

View File

@ -1,6 +1,6 @@
{
"name": "vue-next-admin-template",
"version": "0.2.2",
"version": "2.0.3",
"description": "vue3 vite next admin template",
"author": "lyt_20201208",
"license": "MIT",
@ -10,38 +10,38 @@
"lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
},
"dependencies": {
"@element-plus/icons-vue": "^0.2.4",
"axios": "^0.24.0",
"echarts": "^5.2.2",
"element-plus": "^1.2.0-beta.6",
"@element-plus/icons-vue": "^1.0.0",
"axios": "^0.26.0",
"echarts": "^5.3.0",
"element-plus": "^2.0.4",
"mitt": "^3.0.0",
"nprogress": "^0.2.0",
"qrcodejs2-fixes": "^0.0.2",
"screenfull": "^6.0.0",
"screenfull": "^6.0.1",
"sortablejs": "^1.14.0",
"vue": "^3.2.20",
"vue": "^3.2.31",
"vue-clipboard3": "^1.0.1",
"vue-router": "^4.0.12",
"vue-router": "^4.0.13",
"vuex": "^4.0.2"
},
"devDependencies": {
"@types/axios": "^0.14.0",
"@types/clipboard": "^2.0.1",
"@types/node": "^17.0.2",
"@types/node": "^17.0.21",
"@types/nprogress": "^0.2.0",
"@typescript-eslint/eslint-plugin": "^5.8.0",
"@typescript-eslint/parser": "^5.8.0",
"@vitejs/plugin-vue": "^2.0.1",
"@vue/compiler-sfc": "^3.2.26",
"dotenv": "^10.0.0",
"eslint": "^8.5.0",
"eslint-plugin-vue": "^8.2.0",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"@vitejs/plugin-vue": "^2.2.4",
"@vue/compiler-sfc": "^3.2.31",
"dotenv": "^16.0.0",
"eslint": "^8.10.0",
"eslint-plugin-vue": "^8.5.0",
"prettier": "^2.5.1",
"sass": "^1.45.1",
"sass-loader": "^12.4.0",
"typescript": "^4.5.4",
"vite": "^2.7.4",
"vue-eslint-parser": "^8.0.1"
"sass": "^1.49.9",
"sass-loader": "^12.6.0",
"typescript": "^4.6.2",
"vite": "^2.8.6",
"vue-eslint-parser": "^8.3.0"
},
"browserslist": [
"> 1%",

2
plugins.d.ts vendored
View File

@ -1 +1,3 @@
declare module 'vue-grid-layout';
declare module 'sortablejs';
declare module 'qrcodejs2-fixes';

View File

@ -1,14 +1,17 @@
<template>
<router-view v-show="getThemeConfig.lockScreenTime !== 0" />
<LockScreen v-if="getThemeConfig.isLockScreen" />
<Setings ref="setingsRef" v-show="getThemeConfig.lockScreenTime !== 0" />
<CloseFull />
<el-config-provider :size="getGlobalComponentSize" :locale="zhCn">
<router-view v-show="getThemeConfig.lockScreenTime !== 0" />
<LockScreen v-if="getThemeConfig.isLockScreen" />
<Setings ref="setingsRef" v-show="getThemeConfig.lockScreenTime !== 0" />
<CloseFull />
</el-config-provider>
</template>
<script lang="ts">
import { computed, ref, getCurrentInstance, onBeforeMount, onMounted, nextTick, defineComponent, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useStore } from '/@/store/index';
import zhCn from 'element-plus/es/locale/lang/zh-cn';
import other from '/@/utils/other';
import { Local, Session } from '/@/utils/storage';
import setIntroduction from '/@/utils/setIconfont';
@ -19,7 +22,7 @@ export default defineComponent({
name: 'app',
components: { LockScreen, Setings, CloseFull },
setup() {
const { proxy } = getCurrentInstance() as any;
const { proxy } = <any>getCurrentInstance();
const setingsRef = ref();
const route = useRoute();
const store = useStore();
@ -27,6 +30,10 @@ export default defineComponent({
const getThemeConfig = computed(() => {
return store.state.themeConfig.themeConfig;
});
// 获取全局组件大小
const getGlobalComponentSize = computed(() => {
return other.globalComponentSize;
});
// 布局配置弹窗打开
const openSetingsDrawer = () => {
setingsRef.value.openDrawer();
@ -64,7 +71,9 @@ export default defineComponent({
}
);
return {
zhCn,
setingsRef,
getGlobalComponentSize,
getThemeConfig,
};
},

View File

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

View File

@ -3,32 +3,24 @@ import request from '/@/utils/request';
/**
* 后端控制菜单模拟json路径在 https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu
* 后端控制路由isRequestRoutes 为 true则开启后端控制路由
* @method getMenuAdmin 获取后端动态路由菜单(admin)
* @method getMenuTest 获取后端动态路由菜单(test)
*/
/**
* 获取后端动态路由菜单(admin)
* @link 参考https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu
* @param params 要传的参数值,非必传
* @returns 返回接口数据
*/
export function getMenuAdmin(params?: object) {
return request({
url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/adminMenu-template.json',
method: 'get',
params,
});
}
/**
* 获取后端动态路由菜单(test)
* @link 参考https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu
* @param params 要传的参数值,非必传
* @returns 返回接口数据
*/
export function getMenuTest(params?: object) {
return request({
url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/testMenu-template.json',
method: 'get',
params,
});
export function useMenuApi() {
return {
getMenuAdmin: (params?: object) => {
return request({
url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/adminMenu.json',
method: 'get',
params,
});
},
getMenuTest: (params?: object) => {
return request({
url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/testMenu.json',
method: 'get',
params,
});
},
};
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 26 KiB

9
src/assets/logo-mini.svg Normal file
View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 50" width="64" height="50">
<defs>
<image width="64" height="50" id="img1" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAyCAYAAADsg90UAAAAAXNSR0IB2cksfwAACI5JREFUeJy1WgtsW9UZdvNq0xU6WhZoCCVx7Gunbld1GSsMdYpGREhwr+2IMbQWqRoTk+iWPZi2wWhm1kwEmqV52feRJrbvvd6DiUnbQCAemzY2xh4tSB0wOqZIqAtLYzultIWEpt13btLq+sap/R8nRzpK4vh+5/++85///8851yEk1V+hX6B0tyE3OWwt0x789VR78AKlZ0KBz1oxBE3+BdUW9PNuQ7nJbk/BDQ/v4hi0x44z1R64jyrAVCjQ/fLNN8/ZoUWdgqGc57AFE6I86giH+QRw6XI9QE4RBz3iTAyVW3EyQdEDUmeIIhyeFNtMHO/o0B4e8qYASfVvLk2p4BKgdnioFCAvEQeddhnyFivOpOgvBaFXiAKcygQDrk927S/zjkae5xUA/X3YI/C5AFwHLrSPw+2+YYfCMugiCvDaCbGt0hc9uAXuP1OEAPACZQ+fAGguQ9nGgglx0OfsOAhq24lBcG/YYQa/kWLIz3XlCW4B6mKRCoCMEQdNu3VpnRVnMhBYA2L/LVCATCooXuNJSHXFzv58P+mMyyu5RQDAKNntdLnNjoN0+NMCM0D8901NDkGXuyBAseRZn3Ubagu3AFjTd1GXAdZdpCqurMgSIBS4BwRn8wgwje/d4pV6yz2adGQJyM/bo/ZyC+DSlWqApIiDHqtPyFdlCyBeD4Lv5xHgr5mAWO4ZjTRh9s8tlQDoryIbrOISoLYvvAIALxIHnIHn3GnFmfDfztLhH/IEv/vYd72xyKElJM/6Gdjj4fYCuNA3yYMa6ogdBwQfvIwAk+mguHZz3+MbMPsfLrEALD3v5RfAkH3UOID+Jqqwj2ULIDZeJvhF2He8I0NfX2ry8wI85VSy41LBzaUPsXT4L/Iy0OUbrTgpsXUlyI7lEOAsvGOboPRdKejK28shAPqpek1etxjHArxAiXCo/pAdB2QPLRQg8BLcv8ITi4h4bnaZBGD2+IsQQG0DCDUyv2LfjcHVQyB93iLAecz+bvY/T1x6YrnImwIkVakIAeSrAfIucdCz7qRcY8VJh8RrQDplWfvHxttaVvkGe6vg/h8tpwDoR926wpcOb9CkEhQ4Mbrqym4rTkrcydLhs5bU95Wwwwx+311m8qxPQ4CtvE6AdKjcLlCrQkNJXNXdnYUD0t+eF+AE1v76rV0/qvTEo/8kkkGJq/yFLIKhPsgtgEszl8E4cdAxPHdllgDBwGZW9k6Fguaa9A4PtMEwkrDe0chhCPAZ/E5dNn/kPiWqGe5mVSH1rJCdze2w4rDNDsi/gdn/FPtb0KTfUmeyQenvuE45WCLQd6unsc3fwKeAw0yHX+Zwu247DpbB3emQv3R7x7du8GgybdtrKGc39Ty23rQnqfKk5y9wCwD1XACZJg56xGUMZFVhx1taytjPBnWAfuihyT+3TEiAQwC1Mhzmqwrr9QhbBoeJg36A6Ou0Y/kGe2qQ+mhiGspHDcODTZfs0eRr8fkE0Z4xpyGt4RJgTnX1carq8Jw9dhzv6BB5k+VJyK9t6+y8dNK74bGHMSHK00Scc/CC7fwC6MotAjX6GuqTzsTBkosYW/f9YKUnIb1OFiAW/c4Ce5Lq18jLKKnu5xaApTWBHn0n8Nz6ixhw4zvJ5HXlvU2R3gWui02XizwhrEznbTW9vQ64nUEckBUut13EQOHzFFUA70jErBv+09xcYrXHqUXK8H9qIXXapSs1dm4FN1SF95DdzlD72LMNUt/15CMvXT65NfzIRtQOq5FCv7jAHkPppdqDZ3YvZFZgY8WEQN8dvvFxpB8hLvWT3T8uyWxckG9CEfX38fk0ahGgmboMMIk/2xg5UJKbYZ62sf9RbI7UPxGJzLgT0R2Y/RM0z1HO+QZ6Gl/3+dh2Wmdb6HQwUJc9IfI6fPc40Z5xCLeW2wvw8Pfoy0B5mfoMAuYLn+7oKE0FxZqp+YvWTHugw24PvvtLIjZLh83cAghzmxHqASb5qntz/4EvsfEy7UHroepvJoKtWdUcPPKrVGwsA6k2FuYTAPXAGoC8RfYCStflqRvv3VuB4Hc1SI9bBJjAZ1VWe+p1+TqBHpfG+M8Ksa0EgLacAnhiETNzIPjdbztLPIfPWrME0CSkQ+UfxDFmkQ5v5RPAYcYBckFTcDfUWc+hwZsw06tA+EiOi5RoDns66ctA/Qm3AE4tuhYgZ5Zl9uPR32165PtlINoMwjM5jtPfSvn9WTe/iEufw7PUW+U3a2NSKZ8CTU0s+DyzHAJgq9yuNDaywxNtkQuVWaTDBqs5SIdsQt4hjsXOCvmvzrDu6Fdnebo3Hj3u69xXngoFqkH09KL3ie2BB6y2XCsfZNt1apnOqsL7uY/KoB57k+SDJRUhIZlX2iD4UJ4b5ef+3dqabY9hlunUdPt0XXyIryqs1yKVAv3qbPFuKNNedaAR5FeD4NE8AqRTwZ1ZmxqnHv0EcI4Rxz3hNqSqxThevrEXqpJq31IJgOD3TN3+h0tYmmPpLt9bJex80W4SRBzmEF7kE8Bhbo5aONwutwCx6K4JUVyByu/JfOTn+6EFAiTNN1uoAgxyC1BvqFcAZLJo8pr8rlvuq8wExfpFUl+u/s7/dt6R9UKkS5ergHeaOP6Yc2SQLw7Mqa6SDznsHakvzLAyoeAPCyRvVoXpUGCb1ZZqRWFnhdQXPWcg3Jac5App7C2MImf/5Kb+nupJ0X8FSL1NEOACEyyHPQ9QbWA73CIEkBuEIqrCzUO9CsNB6esCqQ8pAqD/Oe33l9vs2SLQd6svuhJSWW6GeVptLFKGbMD9eltjZ2cjw5m4o60ChF4lCvBeJhjIuntAYF4N3KNEO9Lwgmo+F/D5mNt1cbr/2OeD916qx+HSB4gCsDdNdmXZM7dbjRJtYXeZd/EJ4DDX3Q4uARLSj604yO23UgVA2jRy2EO+OkNX/w+fZNm8pw5QbAAAAABJRU5ErkJggg=="/>
</defs>
<style>
tspan { white-space:pre }
</style>
<use id="Background" href="#img1" x="0" y="0" />
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -17,7 +17,7 @@
<SvgIcon
:name="fontIconPrefix === '' ? prepend : fontIconPrefix"
class="font14"
v-if="fontIconPrefix === '' ? prepend?.indexOf('element') > -1 : fontIconPrefix?.indexOf('element') > -1"
v-if="fontIconPrefix === '' ? prepend?.indexOf('ele-') > -1 : fontIconPrefix?.indexOf('ele-') > -1"
/>
<i v-else :class="fontIconPrefix === '' ? prepend : fontIconPrefix" class="font14"></i>
</template>
@ -56,16 +56,16 @@
</template>
<script lang="ts">
import { ref, toRefs, reactive, onMounted, nextTick, computed, watch } from 'vue';
import { ref, toRefs, reactive, onMounted, nextTick, computed, watch, defineComponent } from 'vue';
import initIconfont from '/@/utils/getStyleSheets';
export default {
export default defineComponent({
name: 'iconSelector',
emits: ['update:modelValue', 'get', 'clear'],
props: {
// 输入框前置内容
prepend: {
type: String,
default: () => 'elementPointer',
default: () => 'ele-Pointer',
},
// 输入框占位文本
placeholder: {
@ -75,7 +75,7 @@ export default {
// 输入框占位文本
size: {
type: String,
default: () => 'small',
default: () => 'default',
},
// 弹窗标题
title: {
@ -109,7 +109,7 @@ export default {
setup(props, { emit }) {
const inputWidthRef = ref();
const selectorScrollbarRef = ref();
const state: any = reactive({
const state = reactive({
fontIconPrefix: '',
fontIconVisible: false,
fontIconWidth: 0,
@ -122,6 +122,7 @@ export default {
});
// 处理 input 获取焦点时modelValue 有值时,改变 input 的 placeholder 值
const onIconFocus = () => {
state.fontIconVisible = true;
if (!props.modelValue) return false;
state.fontIconSearch = '';
state.fontIconPlaceholder = props.modelValue;
@ -136,8 +137,8 @@ export default {
// 处理 icon 双向绑定数值回显
const initModeValueEcho = () => {
if (props.modelValue === '') return false;
state.fontIconPlaceholder = props.modelValue;
state.fontIconPrefix = props.modelValue;
(<string | undefined>state.fontIconPlaceholder) = props.modelValue;
(<string | undefined>state.fontIconPrefix) = props.modelValue;
};
// 图标搜索及图标数据显示
const fontIconSheetsFilterList = computed(() => {
@ -165,7 +166,7 @@ export default {
if (type === 'ali') {
await initIconfont.ali().then((res: any) => {
// 阿里字体图标使用 `iconfont xxx`
state.fontIconSheetsList = res.map((i) => `iconfont ${i}`);
state.fontIconSheetsList = res.map((i: string) => `iconfont ${i}`);
});
} else if (type === 'ele') {
await initIconfont.ele().then((res: any) => {
@ -174,7 +175,7 @@ export default {
} else if (type === 'awe') {
await initIconfont.awe().then((res: any) => {
// fontawesome字体图标使用 `fa xxx`
state.fontIconSheetsList = res.map((i) => `fa ${i}`);
state.fontIconSheetsList = res.map((i: string) => `fa ${i}`);
});
}
// 初始化 input 的 placeholder
@ -208,9 +209,9 @@ export default {
onMounted(() => {
// 判断默认进来是什么类型图标,进行 tab 回显
if (props.type === 'all') {
if (props.modelValue?.indexOf('iconfont') > -1) onIconChange('ali');
else if (props.modelValue?.indexOf('element') > -1) onIconChange('ele');
else if (props.modelValue?.indexOf('fa') > -1) onIconChange('awe');
if ((<any>props.modelValue)?.indexOf('iconfont') > -1) onIconChange('ali');
else if ((<any>props.modelValue)?.indexOf('ele-') > -1) onIconChange('ele');
else if ((<any>props.modelValue)?.indexOf('fa') > -1) onIconChange('awe');
else onIconChange('ali');
} else {
onIconChange(props.type);
@ -237,5 +238,5 @@ export default {
...toRefs(state),
};
},
};
});
</script>

View File

@ -1,7 +1,15 @@
<script lang="ts">
// 渲染函数https://v3.cn.vuejs.org/guide/render-function.html
import { h, resolveComponent, defineComponent } from 'vue';
export default defineComponent({
import { h, resolveComponent } from 'vue';
// 定义接口来定义对象的类型
interface SvgIconProps {
name: string;
size: number;
color: string;
}
export default {
name: 'svgIcon',
props: {
// svg 图标组件名字
@ -11,18 +19,24 @@ export default defineComponent({
// svg 大小
size: {
type: Number,
default: () => 14,
},
// svg 颜色
color: {
type: String,
},
},
setup(props) {
if (props.name?.indexOf('element') > -1) {
return () => h('i', { class: 'el-icon', style: `--font-size: ${props.size};--color: ${props.color}` }, [h(resolveComponent(props.name))]);
} else {
return () => h('i', { class: props.name, style: `font-size: ${props.size};color: ${props.color}` });
}
setup(props: SvgIconProps) {
// 定义变量
const linesString: any[] = ['https', 'http', '/src', '/assets', import.meta.env.VITE_PUBLIC_PATH];
const onLineStyle: string = `font-size: ${props.size}px;color: ${props.color}`;
const localsStyle: string = `width: ${props.size}px;height: ${props.size}px`;
const eleSetStyle = { class: 'el-icon', style: onLineStyle };
// 逻辑判断
if (props.name?.startsWith('ele-')) return () => h('i', eleSetStyle, [props.name === 'ele-' ? '' : h(resolveComponent(props.name))]);
else if (linesString.find((str) => props.name?.startsWith(str))) return () => h('img', { src: props.name, style: localsStyle });
else return () => h('i', { class: props.name, style: onLineStyle });
},
});
};
</script>

View File

@ -10,23 +10,19 @@
</template>
<script lang="ts">
import { toRefs, reactive, computed, watch, getCurrentInstance, onBeforeMount } from 'vue';
import { toRefs, reactive, computed, watch, getCurrentInstance, onBeforeMount, defineComponent } from 'vue';
import { useStore } from '/@/store/index';
import Logo from '/@/layout/logo/index.vue';
import Vertical from '/@/layout/navMenu/vertical.vue';
export default {
export default defineComponent({
name: 'layoutAside',
components: { Logo, Vertical },
setup() {
const { proxy } = getCurrentInstance() as any;
const { proxy } = <any>getCurrentInstance();
const store = useStore();
const state: any = reactive({
const state = reactive({
menuList: [],
clientWidth: '',
});
// 获取布局配置信息
const getThemeConfig = computed(() => {
return store.state.themeConfig.themeConfig;
clientWidth: 0,
});
// 获取卡片全屏信息
const isTagsViewCurrenFull = computed(() => {
@ -35,8 +31,8 @@ export default {
// 设置菜单展开/收起时的宽度
const setCollapseStyle = computed(() => {
const { layout, isCollapse, menuBar } = store.state.themeConfig.themeConfig;
const asideBrColor =
menuBar === '#FFFFFF' || menuBar === '#FFF' || menuBar === '#fff' || menuBar === '#ffffff' ? 'layout-el-aside-br-color' : '';
const asideBrTheme = ['#FFFFFF', '#FFF', '#fff', '#ffffff'];
const asideBrColor = asideBrTheme.includes(menuBar) ? 'layout-el-aside-br-color' : '';
// 判断是否是手机端
if (state.clientWidth <= 1000) {
if (isCollapse) {
@ -73,7 +69,10 @@ export default {
// 关闭移动端蒙版
const closeLayoutAsideMobileMode = () => {
const el = document.querySelector('.layout-aside-mobile-mode');
el && el.parentNode?.removeChild(el);
el?.setAttribute('style', 'animation: error-img-two 0.3s');
setTimeout(() => {
el?.parentNode?.removeChild(el);
}, 300);
const clientWidth = document.body.clientWidth;
if (clientWidth < 1000) store.state.themeConfig.themeConfig.isCollapse = false;
document.body.setAttribute('class', '');
@ -86,7 +85,7 @@ export default {
// 设置/过滤路由(非静态路由/是否显示在菜单中)
const setFilterRoutes = () => {
if (store.state.themeConfig.themeConfig.layout === 'columns') return false;
state.menuList = filterRoutesFun(store.state.routesList.routesList);
(state.menuList as any) = filterRoutesFun(store.state.routesList.routesList);
};
// 路由过滤递归函数
const filterRoutesFun = (arr: Array<object>) => {
@ -149,11 +148,10 @@ export default {
return {
setCollapseStyle,
setShowLogo,
getThemeConfig,
isTagsViewCurrenFull,
onAsideEnterLeave,
...toRefs(state),
};
},
};
});
</script>

View File

@ -43,19 +43,32 @@
</template>
<script lang="ts">
import { reactive, toRefs, ref, computed, onMounted, nextTick, getCurrentInstance, watch, onUnmounted } from 'vue';
import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
import { reactive, toRefs, ref, computed, onMounted, nextTick, getCurrentInstance, watch, onUnmounted, defineComponent } from 'vue';
import { useRoute, useRouter, onBeforeRouteUpdate, RouteRecordRaw } from 'vue-router';
import { useStore } from '/@/store/index';
export default {
// 定义接口来定义对象的类型
interface ColumnsAsideState {
columnsAsideList: any[];
liIndex: number;
liOldIndex: null | number;
liHoverIndex: null | number;
liOldPath: null | string;
difference: number;
routeSplit: string[];
isNavHover: boolean;
}
export default defineComponent({
name: 'layoutColumnsAside',
setup() {
const columnsAsideOffsetTopRefs: any = ref([]);
const columnsAsideActiveRef = ref();
const { proxy } = getCurrentInstance() as any;
const { proxy } = <any>getCurrentInstance();
const store = useStore();
const route = useRoute();
const router = useRouter();
const state: any = reactive({
const state = reactive<ColumnsAsideState>({
columnsAsideList: [],
liIndex: 0,
liOldIndex: null,
@ -86,7 +99,7 @@ export default {
else router.push(path);
};
// 鼠标移入时,显示当前的子级菜单
const onColumnsAsideMenuMouseenter = (v: Object, k: number) => {
const onColumnsAsideMenuMouseenter = (v: RouteRecordRaw, k: number) => {
let { path } = v;
state.liOldPath = path;
state.liOldIndex = k;
@ -153,7 +166,7 @@ export default {
if (!currentSplitRoute) return false;
// 延迟拿值,防止取不到
setTimeout(() => {
onColumnsAsideDown(currentSplitRoute.k);
onColumnsAsideDown((<any>currentSplitRoute).k);
}, 0);
};
// 监听布局配置信息的变化,动态增加菜单高亮位置移动像素
@ -198,18 +211,18 @@ export default {
...toRefs(state),
};
},
};
});
</script>
<style scoped lang="scss">
.layout-columns-aside {
width: 70px;
height: 100%;
background: var(--bg-columnsMenuBar);
background: var(--next-bg-columnsMenuBar);
ul {
position: relative;
li {
color: var(--bg-columnsMenuBarColor);
color: var(--next-bg-columnsMenuBarColor);
width: 100%;
height: 50px;
text-align: center;
@ -241,22 +254,22 @@ export default {
}
a {
text-decoration: none;
color: var(--bg-columnsMenuBarColor);
color: var(--next-bg-columnsMenuBarColor);
}
}
.layout-columns-active {
color: var(--color-whites) !important;
color: var(--el-color-white);
transition: 0.3s ease-in-out;
}
.layout-columns-hover {
color: var(--color-primary);
color: var(--el-color-primary);
a {
color: var(--color-primary);
color: var(--el-color-primary);
}
}
.columns-round {
background: var(--color-primary);
color: var(--color-whites);
background: var(--el-color-primary);
color: var(--el-color-white);
position: absolute;
left: 50%;
top: 2px;

View File

@ -5,10 +5,10 @@
</template>
<script lang="ts">
import { computed } from 'vue';
import { computed, defineComponent } from 'vue';
import { useStore } from '/@/store/index';
import NavBarsIndex from '/@/layout/navBars/index.vue';
export default {
export default defineComponent({
name: 'layoutHeader',
components: { NavBarsIndex },
setup() {
@ -28,5 +28,5 @@ export default {
isTagsViewCurrenFull,
};
},
};
});
</script>

View File

@ -3,32 +3,35 @@
<el-scrollbar
class="layout-scrollbar"
ref="layoutScrollbarRef"
:style="{
minHeight: `calc(100vh - ${headerHeight})`,
padding: currentRouteMeta.isLink && currentRouteMeta.isIframe ? 0 : '',
transition: 'padding 0.3s ease-in-out',
}"
:style="{ padding: currentRouteMeta.isLink && currentRouteMeta.isIframe ? 0 : '', transition: 'padding 0.3s ease-in-out' }"
>
<LayoutParentView />
<LayoutParentView :style="{ minHeight: `calc(100vh - ${headerHeight})` }" />
<Footer v-if="getThemeConfig.isFooter" />
</el-scrollbar>
</el-main>
</template>
<script lang="ts">
import { computed, defineComponent, toRefs, reactive, getCurrentInstance, watch, onBeforeMount } from 'vue';
import { computed, defineComponent, toRefs, reactive, getCurrentInstance, watch, onMounted } from 'vue';
import { useStore } from '/@/store/index';
import { useRoute } from 'vue-router';
import LayoutParentView from '/@/layout/routerView/parent.vue';
import Footer from '/@/layout/footer/index.vue';
// 定义接口来定义对象的类型
interface MainState {
headerHeight: string | number;
currentRouteMeta: any;
}
export default defineComponent({
name: 'layoutMain',
components: { LayoutParentView, Footer },
setup() {
const { proxy } = getCurrentInstance() as any;
const { proxy } = <any>getCurrentInstance();
const route = useRoute();
const store = useStore();
const state = reactive({
const state = reactive<MainState>({
headerHeight: '',
currentRouteMeta: {},
});
@ -47,25 +50,31 @@ export default defineComponent({
state.currentRouteMeta = route.meta;
};
// 页面加载前
onBeforeMount(() => {
onMounted(async () => {
await initGetMeta();
initHeaderHeight();
initGetMeta();
});
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
watch(store.state.themeConfig.themeConfig, (val) => {
state.headerHeight = val.isTagsview ? '84px' : '50px';
if (val.isFixedHeaderChange !== val.isFixedHeader) {
if (!proxy.$refs.layoutScrollbarRef) return false;
proxy.$refs.layoutScrollbarRef.update();
}
});
// 监听路由变化
watch(
() => route.path,
() => {
state.currentRouteMeta = route.meta;
const bool = state.currentRouteMeta.isLink && state.currentRouteMeta.isIframe;
state.headerHeight = bool ? `85px` : `114px`;
proxy.$refs.layoutScrollbarRef.update();
}
);
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
watch(store.state.themeConfig.themeConfig, (val) => {
state.currentRouteMeta = route.meta;
const bool = state.currentRouteMeta.isLink && state.currentRouteMeta.isIframe;
state.headerHeight = val.isTagsview ? (bool ? `85px` : `114px`) : '51px';
if (val.isFixedHeaderChange !== val.isFixedHeader) {
if (!proxy.$refs.layoutScrollbarRef) return false;
proxy.$refs.layoutScrollbarRef.update();
}
});
return {
getThemeConfig,
...toRefs(state),

View File

@ -8,9 +8,9 @@
</template>
<script lang="ts">
import { toRefs, reactive } from 'vue';
import { toRefs, reactive, defineComponent } from 'vue';
import { onBeforeRouteUpdate } from 'vue-router';
export default {
export default defineComponent({
name: 'layoutFooter',
setup() {
const state = reactive({
@ -18,16 +18,18 @@ export default {
});
// 路由改变时,等主界面动画加载完毕再显示 footer
onBeforeRouteUpdate(() => {
state.isDelayFooter = false;
setTimeout(() => {
state.isDelayFooter = true;
}, 800);
state.isDelayFooter = false;
setTimeout(() => {
state.isDelayFooter = true;
}, 800);
}, 0);
});
return {
...toRefs(state),
};
},
};
});
</script>
<style scoped lang="scss">
@ -38,7 +40,7 @@ export default {
margin: auto;
color: var(--el-text-color-secondary);
text-align: center;
animation: logoAnimation 0.3s ease-in-out;
animation: error-num 1s ease-in-out;
}
}
</style>

View File

@ -1,23 +1,21 @@
<template>
<Defaults v-if="getThemeConfig.layout === 'defaults'" />
<Classic v-else-if="getThemeConfig.layout === 'classic'" />
<Transverse v-else-if="getThemeConfig.layout === 'transverse'" />
<Columns v-else-if="getThemeConfig.layout === 'columns'" />
<component :is="getThemeConfig.layout" />
</template>
<script lang="ts">
import { computed, onBeforeMount, onUnmounted, getCurrentInstance } from 'vue';
import { computed, onBeforeMount, onUnmounted, getCurrentInstance, defineComponent, defineAsyncComponent } from 'vue';
import { useStore } from '/@/store/index';
import { Local } from '/@/utils/storage';
import Defaults from '/@/layout/main/defaults.vue';
import Classic from '/@/layout/main/classic.vue';
import Transverse from '/@/layout/main/transverse.vue';
import Columns from '/@/layout/main/columns.vue';
export default {
export default defineComponent({
name: 'layout',
components: { Defaults, Classic, Transverse, Columns },
components: {
defaults: defineAsyncComponent(() => import('/@/layout/main/defaults.vue')),
classic: defineAsyncComponent(() => import('/@/layout/main/classic.vue')),
transverse: defineAsyncComponent(() => import('/@/layout/main/transverse.vue')),
columns: defineAsyncComponent(() => import('/@/layout/main/columns.vue')),
},
setup() {
const { proxy } = getCurrentInstance() as any;
const { proxy } = <any>getCurrentInstance();
const store = useStore();
// 获取布局配置信息
const getThemeConfig = computed(() => {
@ -53,5 +51,5 @@ export default {
getThemeConfig,
};
},
};
});
</script>

View File

@ -20,7 +20,7 @@
<div class="layout-lock-screen-date-box-info">{{ time.mdq }}</div>
</div>
<div class="layout-lock-screen-date-top">
<SvgIcon name="elementTop" />
<SvgIcon name="ele-Top" />
<div class="layout-lock-screen-date-top-text">上滑解锁</div>
</div>
</div>
@ -41,7 +41,7 @@
<template #append>
<el-button @click="onLockScreenSubmit">
<el-icon>
<elementRight />
<ele-Right />
</el-icon>
</el-button>
</template>
@ -49,9 +49,9 @@
</div>
</div>
<div class="layout-lock-screen-login-icon">
<SvgIcon name="elementMicrophone" />
<SvgIcon name="elementClock" />
<SvgIcon name="elementSwitchButton" />
<SvgIcon name="ele-Microphone" />
<SvgIcon name="ele-AlarmClock" />
<SvgIcon name="ele-SwitchButton" />
</div>
</div>
</transition>
@ -64,13 +64,33 @@ import { nextTick, onMounted, reactive, toRefs, ref, onUnmounted, getCurrentInst
import { useStore } from '/@/store/index';
import { formatDate } from '/@/utils/formatTime';
import { Local } from '/@/utils/storage';
// 定义接口来定义对象的类型
interface LockScreenState {
transparency: number;
downClientY: number;
moveDifference: number;
isShowLoockLogin: boolean;
isFlags: boolean;
querySelectorEl: HTMLElement | string;
time: {
hm: string;
s: string;
mdq: string;
};
setIntervalTime: number;
isShowLockScreen: boolean;
isShowLockScreenIntervalTime: number;
lockScreenPassword: string;
}
export default defineComponent({
name: 'layoutLockScreen',
setup() {
const { proxy } = getCurrentInstance() as any;
const { proxy } = <any>getCurrentInstance();
const layoutLockScreenInputRef = ref();
const store = useStore();
const state: any = reactive({
const state = reactive<LockScreenState>({
transparency: 1,
downClientY: 0,
moveDifference: 0,
@ -95,7 +115,7 @@ export default defineComponent({
// 鼠标移动
const onMove = (move: any) => {
if (state.isFlags) {
const el = state.querySelectorEl;
const el = <HTMLElement>state.querySelectorEl;
const opacitys = (state.transparency -= 1 / 200);
if (move.touches) {
state.moveDifference = move.touches[0].clientY - state.downClientY;
@ -122,7 +142,7 @@ export default defineComponent({
state.isFlags = false;
state.transparency = 1;
if (state.moveDifference >= -400) {
state.querySelectorEl.setAttribute('style', `top:0px;opacity:1;transition:all 0.3s ease;`);
(<HTMLElement>state.querySelectorEl).setAttribute('style', `top:0px;opacity:1;transition:all 0.3s ease;`);
}
};
// 获取要拖拽的初始元素

View File

@ -1,20 +1,21 @@
<template>
<div class="layout-logo" v-if="setShowLogo" @click="onThemeConfigChange">
<img src="https://gitee.com/lyt-top/vue-next-admin-images/raw/master/logo/logo-mini.svg" class="layout-logo-medium-img" />
<img :src="logoMini" class="layout-logo-medium-img" />
<span>{{ getThemeConfig.globalTitle }}</span>
</div>
<div class="layout-logo-size" v-else @click="onThemeConfigChange">
<img src="https://gitee.com/lyt-top/vue-next-admin-images/raw/master/logo/logo-mini.svg" class="layout-logo-size-img" />
<img :src="logoMini" class="layout-logo-size-img" />
</div>
</template>
<script lang="ts">
import { computed, getCurrentInstance } from 'vue';
import { computed, defineComponent } from 'vue';
import { useStore } from '/@/store/index';
export default {
import logoMini from '/@/assets/logo-mini.svg';
export default defineComponent({
name: 'layoutLogo',
setup() {
const { proxy } = getCurrentInstance() as any;
const store = useStore();
// 获取布局配置信息
const getThemeConfig = computed(() => {
@ -28,16 +29,16 @@ export default {
// logo 点击实现菜单展开/收起
const onThemeConfigChange = () => {
if (store.state.themeConfig.themeConfig.layout === 'transverse') return false;
proxy.mittBus.emit('onMenuClick');
store.state.themeConfig.themeConfig.isCollapse = !store.state.themeConfig.themeConfig.isCollapse;
};
return {
logoMini,
setShowLogo,
getThemeConfig,
onThemeConfigChange,
};
},
};
});
</script>
<style scoped lang="scss">
@ -48,7 +49,7 @@ export default {
align-items: center;
justify-content: center;
box-shadow: rgb(0 21 41 / 2%) 0px 1px 4px;
color: var(--color-primary);
color: var(--el-color-primary);
font-size: 16px;
cursor: pointer;
animation: logoAnimation 0.3s ease-in-out;

View File

@ -13,13 +13,13 @@
</template>
<script lang="ts">
import { computed } from 'vue';
import { computed, defineComponent } from 'vue';
import { useStore } from '/@/store/index';
import Aside from '/@/layout/component/aside.vue';
import Header from '/@/layout/component/header.vue';
import Main from '/@/layout/component/main.vue';
import TagsView from '/@/layout/navBars/tagsView/tagsView.vue';
export default {
export default defineComponent({
name: 'layoutClassic',
components: { Aside, Header, Main, TagsView },
setup() {
@ -32,5 +32,5 @@ export default {
getThemeConfig,
};
},
};
});
</script>

View File

@ -16,13 +16,13 @@
</template>
<script lang="ts">
import { computed } from 'vue';
import { computed, defineComponent } from 'vue';
import { useStore } from '/@/store/index';
import Aside from '/@/layout/component/aside.vue';
import Header from '/@/layout/component/header.vue';
import Main from '/@/layout/component/main.vue';
import ColumnsAside from '/@/layout/component/columnsAside.vue';
export default {
export default defineComponent({
name: 'layoutColumns',
components: { Aside, Header, Main, ColumnsAside },
setup() {
@ -34,5 +34,5 @@ export default {
isFixedHeader,
};
},
};
});
</script>

View File

@ -13,17 +13,17 @@
</template>
<script lang="ts">
import { computed, getCurrentInstance, watch } from 'vue';
import { computed, getCurrentInstance, watch, defineComponent } from 'vue';
import { useRoute } from 'vue-router';
import { useStore } from '/@/store/index';
import Aside from '/@/layout/component/aside.vue';
import Header from '/@/layout/component/header.vue';
import Main from '/@/layout/component/main.vue';
export default {
export default defineComponent({
name: 'layoutDefaults',
components: { Aside, Header, Main },
setup() {
const { proxy } = getCurrentInstance() as any;
const { proxy } = <any>getCurrentInstance();
const store = useStore();
const route = useRoute();
const isFixedHeader = computed(() => {
@ -40,5 +40,5 @@ export default {
isFixedHeader,
};
},
};
});
</script>

View File

@ -2,7 +2,8 @@
<div class="layout-navbars-breadcrumb" :style="{ display: isShowBreadcrumb }">
<SvgIcon
class="layout-navbars-breadcrumb-icon"
:name="getThemeConfig.isCollapse ? 'elementExpand' : 'elementFold'"
:name="getThemeConfig.isCollapse ? 'ele-Expand' : 'ele-Fold'"
:size="16"
@click="onThemeConfigChange"
/>
<el-breadcrumb class="layout-navbars-breadcrumb-hide">
@ -21,17 +22,26 @@
</template>
<script lang="ts">
import { toRefs, reactive, computed, getCurrentInstance, onMounted } from 'vue';
import { toRefs, reactive, computed, onMounted, defineComponent } from 'vue';
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
import { useStore } from '/@/store/index';
export default {
import { Local } from '/@/utils/storage';
// 定义接口来定义对象的类型
interface BreadcrumbState {
breadcrumbList: Array<any>;
routeSplit: Array<string>;
routeSplitFirst: string;
routeSplitIndex: number;
}
export default defineComponent({
name: 'layoutBreadcrumb',
setup() {
const { proxy } = getCurrentInstance() as any;
const store = useStore();
const route = useRoute();
const router = useRouter();
const state: any = reactive({
const state = reactive<BreadcrumbState>({
breadcrumbList: [],
routeSplit: [],
routeSplitFirst: '',
@ -45,11 +55,8 @@ export default {
const isShowBreadcrumb = computed(() => {
initRouteSplit(route.path);
const { layout, isBreadcrumb } = store.state.themeConfig.themeConfig;
if (layout === 'classic' || layout === 'transverse') {
return 'none';
} else {
return isBreadcrumb ? '' : 'none';
}
if (layout === 'classic' || layout === 'transverse') return 'none';
else return isBreadcrumb ? '' : 'none';
});
// 面包屑点击时
const onBreadcrumbClick = (v: any) => {
@ -59,8 +66,13 @@ export default {
};
// 展开/收起左侧菜单点击
const onThemeConfigChange = () => {
proxy.mittBus.emit('onMenuClick');
store.state.themeConfig.themeConfig.isCollapse = !store.state.themeConfig.themeConfig.isCollapse;
setLocalThemeConfig();
};
// 存储布局配置
const setLocalThemeConfig = () => {
Local.remove('themeConfig');
Local.set('themeConfig', getThemeConfig.value);
};
// 处理面包屑数据
const getBreadcrumbList = (arr: Array<object>) => {
@ -101,7 +113,7 @@ export default {
...toRefs(state),
};
},
};
});
</script>
<style scoped lang="scss">
@ -115,11 +127,11 @@ export default {
cursor: pointer;
font-size: 18px;
margin-right: 15px;
color: var(--bg-topBarColor);
color: var(--next-bg-topBarColor);
}
.layout-navbars-breadcrumb-span {
opacity: 0.7;
color: var(--bg-topBarColor);
color: var(--next-bg-topBarColor);
}
.layout-navbars-breadcrumb-iconfont {
font-size: 14px;
@ -127,7 +139,14 @@ export default {
}
::v-deep(.el-breadcrumb__separator) {
opacity: 0.7;
color: var(--bg-topBarColor);
color: var(--next-bg-topBarColor);
}
::v-deep(.el-breadcrumb__inner a, .el-breadcrumb__inner.is-link) {
font-weight: unset !important;
color: var(--next-bg-topBarColor);
&:hover {
color: var(--el-color-primary) !important;
}
}
}
</style>

View File

@ -1,19 +1,18 @@
<template>
<div class="layout-navbars-close-full" v-if="isTagsViewCurrenFull">
<div class="layout-navbars-close-full-box" title="关闭全屏" @click="onCloseFullscreen">
<SvgIcon name="elementClose" />
<SvgIcon name="ele-Close" />
</div>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, computed } from 'vue';
import { computed, defineComponent } from 'vue';
import { useStore } from '/@/store/index';
export default {
export default defineComponent({
name: 'layoutCloseFull',
setup() {
const store = useStore();
const state: any = reactive({});
// 获取卡片全屏信息
const isTagsViewCurrenFull = computed(() => {
return store.state.tagsViewRoutes.isTagsViewCurrenFull;
@ -25,10 +24,9 @@ export default {
return {
isTagsViewCurrenFull,
onCloseFullscreen,
...toRefs(state),
};
},
};
});
</script>
<style scoped lang="scss">
@ -56,7 +54,7 @@ export default {
background: rgba(0, 0, 0, 0.2);
transition: all 0.3s ease;
i {
color: var(--color-primary);
color: var(--el-color-primary);
transition: all 0.3s ease;
}
}

View File

@ -8,27 +8,29 @@
</template>
<script lang="ts">
import { computed, reactive, toRefs, onMounted, onUnmounted, getCurrentInstance } from 'vue';
import { computed, reactive, toRefs, onMounted, onUnmounted, getCurrentInstance, defineComponent } from 'vue';
import { useRoute } from 'vue-router';
import { useStore } from '/@/store/index';
import Breadcrumb from '/@/layout/navBars/breadcrumb/breadcrumb.vue';
import User from '/@/layout/navBars/breadcrumb/user.vue';
import Logo from '/@/layout/logo/index.vue';
import Horizontal from '/@/layout/navMenu/horizontal.vue';
export default {
// 定义接口来定义对象的类型
interface IndexState {
menuList: object[];
}
export default defineComponent({
name: 'layoutBreadcrumbIndex',
components: { Breadcrumb, User, Logo, Horizontal },
setup() {
const { proxy } = getCurrentInstance() as any;
const { proxy } = <any>getCurrentInstance();
const store = useStore();
const route = useRoute();
const state: any = reactive({
const state = reactive<IndexState>({
menuList: [],
});
// 获取布局配置信息
const getThemeConfig = computed(() => {
return store.state.themeConfig.themeConfig;
});
// 设置 logo 显示/隐藏
const setIsShowLogo = computed(() => {
let { isShowLogo, layout } = store.state.themeConfig.themeConfig;
@ -93,13 +95,12 @@ export default {
proxy.mittBus.off('getBreadcrumbIndexSetFilterRoutes');
});
return {
getThemeConfig,
setIsShowLogo,
isLayoutTransverse,
...toRefs(state),
};
},
};
});
</script>
<style scoped lang="scss">
@ -108,7 +109,7 @@ export default {
display: flex;
align-items: center;
padding-right: 15px;
background: var(--bg-topBar);
border-bottom: 1px solid #f1f2f3;
background: var(--next-bg-topBar);
border-bottom: 1px solid var(--next-border-color-light);
}
</style>

View File

@ -11,7 +11,7 @@
>
<template #prefix>
<el-icon class="el-input__icon">
<elementSearch />
<ele-Search />
</el-icon>
</template>
<template #default="{ item }">
@ -26,13 +26,27 @@
import { reactive, toRefs, defineComponent, ref, nextTick } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from '/@/store/index';
// 定义接口来定义对象的类型
interface SearchState {
isShowSearch: boolean;
menuQuery: string;
tagsViewList: object[];
}
interface Restaurant {
path: string;
meta: {
title: string;
};
}
export default defineComponent({
name: 'layoutBreadcrumbSearch',
setup() {
const layoutMenuAutocompleteRef = ref();
const store = useStore();
const router = useRouter();
const state: any = reactive({
const state = reactive<SearchState>({
isShowSearch: false,
menuQuery: '',
tagsViewList: [],
@ -51,13 +65,13 @@ export default defineComponent({
state.isShowSearch = false;
};
// 菜单搜索数据过滤
const menuSearch = (queryString: any, cb: any) => {
const menuSearch = (queryString: string, cb: Function) => {
let results = queryString ? state.tagsViewList.filter(createFilter(queryString)) : state.tagsViewList;
cb(results);
};
// 菜单搜索过滤
const createFilter = (queryString: any) => {
return (restaurant: any) => {
const createFilter: any = (queryString: string) => {
return (restaurant: Restaurant) => {
return (
restaurant.path.toLowerCase().indexOf(queryString.toLowerCase()) > -1 ||
restaurant.meta.title.toLowerCase().indexOf(queryString.toLowerCase()) > -1

View File

@ -1,102 +1,99 @@
<template>
<div class="layout-breadcrumb-seting">
<el-drawer title="布局配置" v-model="getThemeConfig.isDrawer" direction="rtl" destroy-on-close size="240px" @close="onDrawerClose">
<el-drawer title="布局配置" v-model="getThemeConfig.isDrawer" direction="rtl" destroy-on-close size="260px" @close="onDrawerClose">
<el-scrollbar class="layout-breadcrumb-seting-bar">
<!-- 全局主题 -->
<el-divider content-position="left">全局主题</el-divider>
<div class="layout-breadcrumb-seting-bar-flex">
<div class="layout-breadcrumb-seting-bar-flex-label">primary</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-color-picker v-model="getThemeConfig.primary" size="small" @change="onColorPickerChange('primary')"> </el-color-picker>
<el-color-picker v-model="getThemeConfig.primary" size="default" @change="onColorPickerChange"> </el-color-picker>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex">
<div class="layout-breadcrumb-seting-bar-flex-label">success</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">深色模式</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-color-picker v-model="getThemeConfig.success" size="small" @change="onColorPickerChange('success')"> </el-color-picker>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex">
<div class="layout-breadcrumb-seting-bar-flex-label">info</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-color-picker v-model="getThemeConfig.info" size="small" @change="onColorPickerChange('info')"> </el-color-picker>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex">
<div class="layout-breadcrumb-seting-bar-flex-label">warning</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-color-picker v-model="getThemeConfig.warning" size="small" @change="onColorPickerChange('warning')"> </el-color-picker>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex">
<div class="layout-breadcrumb-seting-bar-flex-label">danger</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-color-picker v-model="getThemeConfig.danger" size="small" @change="onColorPickerChange('danger')"> </el-color-picker>
<el-switch v-model="getThemeConfig.isIsDark" size="small" @change="onAddDarkChange"></el-switch>
</div>
</div>
<!-- 菜单 / 顶栏 -->
<el-divider content-position="left">菜单 / 顶栏</el-divider>
<!-- 顶栏设置 -->
<el-divider content-position="left">顶栏设置</el-divider>
<div class="layout-breadcrumb-seting-bar-flex">
<div class="layout-breadcrumb-seting-bar-flex-label">顶栏背景</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-color-picker v-model="getThemeConfig.topBar" size="small" @change="onBgColorPickerChange('topBar')"> </el-color-picker>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex">
<div class="layout-breadcrumb-seting-bar-flex-label">菜单背景</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-color-picker v-model="getThemeConfig.menuBar" size="small" @change="onBgColorPickerChange('menuBar')"> </el-color-picker>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex">
<div class="layout-breadcrumb-seting-bar-flex-label">分栏菜单背景</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-color-picker v-model="getThemeConfig.columnsMenuBar" size="small" @change="onBgColorPickerChange('columnsMenuBar')">
</el-color-picker>
<el-color-picker v-model="getThemeConfig.topBar" size="default" @change="onBgColorPickerChange('topBar')"> </el-color-picker>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex">
<div class="layout-breadcrumb-seting-bar-flex-label">顶栏默认字体颜色</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-color-picker v-model="getThemeConfig.topBarColor" size="small" @change="onBgColorPickerChange('topBarColor')"> </el-color-picker>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex">
<div class="layout-breadcrumb-seting-bar-flex-label">菜单默认字体颜色</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-color-picker v-model="getThemeConfig.menuBarColor" size="small" @change="onBgColorPickerChange('menuBarColor')"> </el-color-picker>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex">
<div class="layout-breadcrumb-seting-bar-flex-label">分栏菜单默认字体颜色</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-color-picker v-model="getThemeConfig.columnsMenuBarColor" size="small" @change="onBgColorPickerChange('columnsMenuBarColor')">
</el-color-picker>
<el-color-picker v-model="getThemeConfig.topBarColor" size="default" @change="onBgColorPickerChange('topBarColor')"> </el-color-picker>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt10">
<div class="layout-breadcrumb-seting-bar-flex-label">顶栏背景渐变</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isTopBarColorGradual" @change="onTopBarGradualChange"></el-switch>
<el-switch v-model="getThemeConfig.isTopBarColorGradual" size="small" @change="onTopBarGradualChange"></el-switch>
</div>
</div>
<!-- 菜单设置 -->
<el-divider content-position="left">菜单设置</el-divider>
<div class="layout-breadcrumb-seting-bar-flex">
<div class="layout-breadcrumb-seting-bar-flex-label">菜单背景</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-color-picker v-model="getThemeConfig.menuBar" size="default" @change="onBgColorPickerChange('menuBar')"> </el-color-picker>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex">
<div class="layout-breadcrumb-seting-bar-flex-label">菜单默认字体颜色</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-color-picker v-model="getThemeConfig.menuBarColor" size="default" @change="onBgColorPickerChange('menuBarColor')"> </el-color-picker>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt14">
<div class="layout-breadcrumb-seting-bar-flex-label">菜单背景渐变</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isMenuBarColorGradual" @change="onMenuBarGradualChange"></el-switch>
<el-switch v-model="getThemeConfig.isMenuBarColorGradual" size="small" @change="onMenuBarGradualChange"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt14">
<!-- 分栏设置 -->
<el-divider content-position="left" :style="{ opacity: getThemeConfig.layout !== 'columns' ? 0.5 : 1 }">分栏设置</el-divider>
<div class="layout-breadcrumb-seting-bar-flex" :style="{ opacity: getThemeConfig.layout !== 'columns' ? 0.5 : 1 }">
<div class="layout-breadcrumb-seting-bar-flex-label">分栏菜单背景</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-color-picker
v-model="getThemeConfig.columnsMenuBar"
size="default"
@change="onBgColorPickerChange('columnsMenuBar')"
:disabled="getThemeConfig.layout !== 'columns'"
>
</el-color-picker>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex" :style="{ opacity: getThemeConfig.layout !== 'columns' ? 0.5 : 1 }">
<div class="layout-breadcrumb-seting-bar-flex-label">分栏菜单默认字体颜色</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-color-picker
v-model="getThemeConfig.columnsMenuBarColor"
size="default"
@change="onBgColorPickerChange('columnsMenuBarColor')"
:disabled="getThemeConfig.layout !== 'columns'"
>
</el-color-picker>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt14" :style="{ opacity: getThemeConfig.layout !== 'columns' ? 0.5 : 1 }">
<div class="layout-breadcrumb-seting-bar-flex-label">分栏菜单背景渐变</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isColumnsMenuBarColorGradual" @change="onColumnsMenuBarGradualChange"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt14">
<div class="layout-breadcrumb-seting-bar-flex-label">菜单字体背景高亮</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isMenuBarColorHighlight" @change="onMenuBarHighlightChange"></el-switch>
<el-switch
v-model="getThemeConfig.isColumnsMenuBarColorGradual"
size="small"
@change="onColumnsMenuBarGradualChange"
:disabled="getThemeConfig.layout !== 'columns'"
></el-switch>
</div>
</div>
@ -105,32 +102,37 @@
<div class="layout-breadcrumb-seting-bar-flex">
<div class="layout-breadcrumb-seting-bar-flex-label">菜单水平折叠</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isCollapse" @change="onThemeConfigChange"></el-switch>
<el-switch v-model="getThemeConfig.isCollapse" size="small" @change="onThemeConfigChange"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">菜单手风琴</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isUniqueOpened" @change="setLocalThemeConfig"></el-switch>
<el-switch v-model="getThemeConfig.isUniqueOpened" size="small" @change="setLocalThemeConfig"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">固定 Header</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isFixedHeader" @change="onIsFixedHeaderChange"></el-switch>
<el-switch v-model="getThemeConfig.isFixedHeader" size="small" @change="onIsFixedHeaderChange"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15" :style="{ opacity: getThemeConfig.layout !== 'classic' ? 0.5 : 1 }">
<div class="layout-breadcrumb-seting-bar-flex-label">经典布局分割菜单</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isClassicSplitMenu" :disabled="getThemeConfig.layout !== 'classic'" @change="onClassicSplitMenuChange">
<el-switch
v-model="getThemeConfig.isClassicSplitMenu"
:disabled="getThemeConfig.layout !== 'classic'"
size="small"
@change="onClassicSplitMenuChange"
>
</el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">开启锁屏</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isLockScreen" @change="setLocalThemeConfig"></el-switch>
<el-switch v-model="getThemeConfig.isLockScreen" size="small" @change="setLocalThemeConfig"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt11">
@ -142,7 +144,7 @@
:min="1"
:max="9999"
@change="setLocalThemeConfig"
size="mini"
size="default"
style="width: 90px"
>
</el-input-number>
@ -154,7 +156,7 @@
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">侧边栏 Logo</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isShowLogo" @change="onIsShowLogoChange"></el-switch>
<el-switch v-model="getThemeConfig.isShowLogo" size="small" @change="onIsShowLogoChange"></el-switch>
</div>
</div>
<div
@ -166,6 +168,7 @@
<el-switch
v-model="getThemeConfig.isBreadcrumb"
:disabled="getThemeConfig.layout === 'classic' || getThemeConfig.layout === 'transverse'"
size="small"
@change="onIsBreadcrumbChange"
></el-switch>
</div>
@ -173,73 +176,72 @@
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">开启 Breadcrumb 图标</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isBreadcrumbIcon" @change="setLocalThemeConfig"></el-switch>
<el-switch v-model="getThemeConfig.isBreadcrumbIcon" size="small" @change="setLocalThemeConfig"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">开启 Tagsview</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isTagsview" @change="setLocalThemeConfig"></el-switch>
<el-switch v-model="getThemeConfig.isTagsview" size="small" @change="setLocalThemeConfig"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">开启 Tagsview 图标</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isTagsviewIcon" @change="setLocalThemeConfig"></el-switch>
<el-switch v-model="getThemeConfig.isTagsviewIcon" size="small" @change="setLocalThemeConfig"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">开启 TagsView 缓存</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isCacheTagsView" @change="setLocalThemeConfig"></el-switch>
<el-switch v-model="getThemeConfig.isCacheTagsView" size="small" @change="setLocalThemeConfig"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15" :style="{ opacity: isMobile ? 0.5 : 1 }">
<div class="layout-breadcrumb-seting-bar-flex-label">开启 TagsView 拖拽</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isSortableTagsView" :disabled="isMobile ? true : false" @change="onSortableTagsViewChange"></el-switch>
<el-switch
v-model="getThemeConfig.isSortableTagsView"
:disabled="isMobile ? true : false"
size="small"
@change="onSortableTagsViewChange"
></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">开启 TagsView 共用</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isShareTagsView" @change="onShareTagsViewChange"></el-switch>
<el-switch v-model="getThemeConfig.isShareTagsView" size="small" @change="onShareTagsViewChange"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">开启 Footer</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isFooter" @change="setLocalThemeConfig"></el-switch>
<el-switch v-model="getThemeConfig.isFooter" size="small" @change="setLocalThemeConfig"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">灰色模式</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isGrayscale" @change="onAddFilterChange('grayscale')"></el-switch>
<el-switch v-model="getThemeConfig.isGrayscale" size="small" @change="onAddFilterChange('grayscale')"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">色弱模式</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isInvert" @change="onAddFilterChange('invert')"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">{深色模式</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isIsDark" @change="onAddDarkChange"></el-switch>
<el-switch v-model="getThemeConfig.isInvert" size="small" @change="onAddFilterChange('invert')"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">开启水印</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isWartermark" @change="onWartermarkChange"></el-switch>
<el-switch v-model="getThemeConfig.isWartermark" size="small" @change="onWartermarkChange"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt14">
<div class="layout-breadcrumb-seting-bar-flex-label">水印文案</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-input v-model="getThemeConfig.wartermarkText" size="mini" style="width: 90px" @input="onWartermarkTextInput($event)"></el-input>
<el-input v-model="getThemeConfig.wartermarkText" size="default" style="width: 90px" @input="onWartermarkTextInput($event)"></el-input>
</div>
</div>
@ -248,37 +250,50 @@
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">Tagsview 风格</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-select v-model="getThemeConfig.tagsStyle" placeholder="请选择" size="mini" style="width: 90px" @change="setLocalThemeConfig">
<el-select v-model="getThemeConfig.tagsStyle" placeholder="请选择" size="default" style="width: 90px" @change="setLocalThemeConfig">
<el-option label="风格1" value="tags-style-one"></el-option>
<el-option label="风格2" value="tags-style-two"></el-option>
<el-option label="风格3" value="tags-style-three"></el-option>
<el-option label="风格4" value="tags-style-four"></el-option>
<el-option label="风格5" value="tags-style-five"></el-option>
</el-select>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">主页面切换动画</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-select v-model="getThemeConfig.animation" placeholder="请选择" size="mini" style="width: 90px" @change="setLocalThemeConfig">
<el-select v-model="getThemeConfig.animation" placeholder="请选择" size="default" style="width: 90px" @change="setLocalThemeConfig">
<el-option label="slide-right" value="slide-right"></el-option>
<el-option label="slide-left" value="slide-left"></el-option>
<el-option label="opacitys" value="opacitys"></el-option>
</el-select>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex mt15" :style="{ opacity: getThemeConfig.layout !== 'columns' ? 0.5 : 1 }">
<div class="layout-breadcrumb-seting-bar-flex-label">分栏高亮风格</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-select v-model="getThemeConfig.columnsAsideStyle" placeholder="请选择" size="mini" style="width: 90px" @change="setLocalThemeConfig">
<el-select
v-model="getThemeConfig.columnsAsideStyle"
placeholder="请选择"
size="default"
style="width: 90px"
:disabled="getThemeConfig.layout !== 'columns' ? true : false"
@change="setLocalThemeConfig"
>
<el-option label="圆角" value="columns-round"></el-option>
<el-option label="卡片" value="columns-card"></el-option>
</el-select>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15 mb27">
<div class="layout-breadcrumb-seting-bar-flex mt15 mb27" :style="{ opacity: getThemeConfig.layout !== 'columns' ? 0.5 : 1 }">
<div class="layout-breadcrumb-seting-bar-flex-label">分栏布局风格</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-select v-model="getThemeConfig.columnsAsideLayout" placeholder="请选择" size="mini" style="width: 90px" @change="setLocalThemeConfig">
<el-select
v-model="getThemeConfig.columnsAsideLayout"
placeholder="请选择"
size="default"
style="width: 90px"
:disabled="getThemeConfig.layout !== 'columns' ? true : false"
@change="setLocalThemeConfig"
>
<el-option label="水平" value="columns-horizontal"></el-option>
<el-option label="垂直" value="columns-vertical"></el-option>
</el-select>
@ -355,15 +370,15 @@
</div>
<div class="copy-config">
<el-alert title="点击下方按钮,复制布局配置去 `src/store/modules/themeConfig.ts` 中修改。" type="warning" :closable="false"> </el-alert>
<el-button size="small" class="copy-config-btn" type="primary" ref="copyConfigBtnRef" @click="onCopyConfigClick">
<el-icon>
<elementCopyDocument />
<el-button size="default" class="copy-config-btn" type="primary" ref="copyConfigBtnRef" @click="onCopyConfigClick">
<el-icon class="mr5">
<ele-CopyDocument />
</el-icon>
一键复制配置
</el-button>
<el-button size="small" class="copy-config-btn-reset" type="info" @click="onResetConfigClick">
<el-icon>
<elementRefreshRight />
<el-button size="default" class="copy-config-btn-reset" type="info" @click="onResetConfigClick">
<el-icon class="mr5">
<ele-RefreshRight />
</el-icon>
一键恢复默认
</el-button>
@ -376,7 +391,7 @@
<script lang="ts">
import { nextTick, onUnmounted, onMounted, getCurrentInstance, defineComponent, computed, reactive, toRefs } from 'vue';
import { useStore } from '/@/store/index';
import { getLightColor } from '/@/utils/theme';
import { getLightColor, getDarkColor } from '/@/utils/theme';
import { verifyAndSpace } from '/@/utils/toolsValidate';
import { Local } from '/@/utils/storage';
import Watermark from '/@/utils/wartermark';
@ -385,7 +400,7 @@ import other from '/@/utils/other';
export default defineComponent({
name: 'layoutBreadcrumbSeting',
setup() {
const { proxy } = getCurrentInstance() as any;
const { proxy } = <any>getCurrentInstance();
const store = useStore();
const { copyText } = commonFunction();
const state = reactive({
@ -396,20 +411,19 @@ export default defineComponent({
return store.state.themeConfig.themeConfig;
});
// 1、全局主题
const onColorPickerChange = (color: string) => {
setPropertyFun(`--color-${color}`, getThemeConfig.value[color]);
setDispatchThemeConfig();
};
// 1、全局主题设置函数
const setPropertyFun = (color: string, targetVal: any) => {
document.documentElement.style.setProperty(color, targetVal);
const onColorPickerChange = () => {
// 颜色加深
document.documentElement.style.setProperty('--el-color-primary-dark-2', `${getDarkColor(getThemeConfig.value.primary, 0.1)}`);
document.documentElement.style.setProperty('--el-color-primary', getThemeConfig.value.primary);
// 颜色变浅
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(`${color}-light-${i}`, getLightColor(targetVal, i / 10));
document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, `${getLightColor(getThemeConfig.value.primary, i / 10)}`);
}
setDispatchThemeConfig();
};
// 2、菜单 / 顶栏
const onBgColorPickerChange = (bg: string) => {
document.documentElement.style.setProperty(`--bg-${bg}`, getThemeConfig.value[bg]);
document.documentElement.style.setProperty(`--next-bg-${bg}`, (<any>getThemeConfig.value)[bg]);
onTopBarGradualChange();
onMenuBarGradualChange();
onColumnsMenuBarGradualChange();
@ -429,35 +443,17 @@ export default defineComponent({
};
// 2、菜单 / 顶栏 --> 背景渐变函数
const setGraduaFun = (el: string, bool: boolean, color: string) => {
nextTick(() => {
setTimeout(() => {
let els = document.querySelector(el);
if (!els) return false;
if (bool) els.setAttribute('style', `background-image:linear-gradient(to bottom left , ${color}, ${getLightColor(color, 0.6)})`);
else els.setAttribute('style', `background-image:${color}`);
document.documentElement.style.setProperty('--el-menu-bg-color', document.documentElement.style.getPropertyValue('--next-bg-menuBar'));
if (bool) els.setAttribute('style', `background:linear-gradient(to bottom left , ${color}, ${getLightColor(color, 0.6)}) !important;`);
else els.setAttribute('style', ``);
setLocalThemeConfig();
});
};
// 2、菜单 / 顶栏 --> 菜单字体背景高亮
const onMenuBarHighlightChange = () => {
nextTick(() => {
setTimeout(() => {
let elsItems = document.querySelectorAll('.el-menu-item');
let elActive = document.querySelector('.el-menu-item.is-active');
if (!elActive) return false;
if (getThemeConfig.value.isMenuBarColorHighlight) {
elsItems.forEach((el: any) => el.setAttribute('id', ``));
elActive.setAttribute('id', `add-is-active`);
Local.set('menuBarHighlightId', elActive.getAttribute('id'));
} else {
elActive.setAttribute('id', ``);
}
setLocalThemeConfig();
}, 0);
});
}, 200);
};
// 3、界面设置 --> 菜单水平折叠
const onThemeConfigChange = () => {
onMenuBarHighlightChange();
setDispatchThemeConfig();
};
// 3、界面设置 --> 固定 Header
@ -518,7 +514,7 @@ export default defineComponent({
setLocalThemeConfig();
};
// 4、界面显示 --> 水印文案
const onWartermarkTextInput = (val: string) => {
const onWartermarkTextInput = (val: any) => {
getThemeConfig.value.wartermarkText = verifyAndSpace(val);
if (getThemeConfig.value.wartermarkText === '') return false;
if (getThemeConfig.value.isWartermark) Watermark.set(getThemeConfig.value.wartermarkText);
@ -531,7 +527,6 @@ export default defineComponent({
getThemeConfig.value.layout = layout;
getThemeConfig.value.isDrawer = false;
initLayoutChangeFun();
onMenuBarHighlightChange();
};
// 设置布局切换函数
const initLayoutChangeFun = () => {
@ -539,6 +534,8 @@ export default defineComponent({
onBgColorPickerChange('menuBarColor');
onBgColorPickerChange('topBar');
onBgColorPickerChange('topBarColor');
onBgColorPickerChange('columnsMenuBar');
onBgColorPickerChange('columnsMenuBarColor');
};
// 关闭弹窗时,初始化变量。变量用于处理 proxy.$refs.layoutScrollbarRef.update()
const onDrawerClose = () => {
@ -578,43 +575,28 @@ export default defineComponent({
Local.clear();
window.location.reload();
};
// 修复防止退出登录再进入界面时,需要刷新样式才生效的问题,初始化布局样式等(登录的时候触发,目前方案)
// 初始化菜单样式等
const initSetStyle = () => {
setTimeout(() => {
// 2、菜单 / 顶栏 --> 顶栏背景渐变
onTopBarGradualChange();
// 2、菜单 / 顶栏 --> 菜单背景渐变
onMenuBarGradualChange();
// 2、菜单 / 顶栏 --> 分栏菜单背景渐变
onColumnsMenuBarGradualChange();
// 2、菜单 / 顶栏 --> 菜单字体背景高亮
onMenuBarHighlightChange();
}, 1300);
// 2、菜单 / 顶栏 --> 顶栏背景渐变
onTopBarGradualChange();
// 2、菜单 / 顶栏 --> 菜单背景渐变
onMenuBarGradualChange();
// 2、菜单 / 顶栏 --> 分栏菜单背景渐变
onColumnsMenuBarGradualChange();
};
onMounted(() => {
nextTick(() => {
// 判断当前布局是否不相同不相同则初始化当前布局的样式防止监听窗口大小改变时布局配置logo、菜单背景等部分布局失效问题
if (!Local.get('frequency')) initLayoutChangeFun();
Local.set('frequency', 1);
// 修复防止退出登录再进入界面时,需要刷新样式才生效的问题,初始化布局样式等(登录的时候触发,目前方案)
proxy.mittBus.on('onSignInClick', () => {
initSetStyle();
});
// 监听菜单点击,菜单字体背景高亮
proxy.mittBus.on('onMenuClick', () => {
onMenuBarHighlightChange();
});
// 监听窗口大小改变,非默认布局,设置成默认布局(适配移动端)
proxy.mittBus.on('layoutMobileResize', (res: any) => {
getThemeConfig.value.layout = res.layout;
getThemeConfig.value.isDrawer = false;
initLayoutChangeFun();
onMenuBarHighlightChange();
state.isMobile = other.isMobile();
});
setTimeout(() => {
// 修复防止退出登录再进入界面时,需要刷新样式才生效的问题,初始化布局样式等(登录的时候触发,目前方案)
initSetStyle();
// 灰色模式
if (getThemeConfig.value.isGrayscale) onAddFilterChange('grayscale');
// 色弱模式
@ -623,13 +605,12 @@ export default defineComponent({
if (getThemeConfig.value.isIsDark) onAddDarkChange();
// 开启水印
onWartermarkChange();
// 初始化菜单样式等
initSetStyle();
}, 100);
});
});
onUnmounted(() => {
// 取消监听菜单点击,菜单字体背景高亮
proxy.mittBus.off('onMenuClick');
proxy.mittBus.off('onSignInClick');
proxy.mittBus.off('layoutMobileResize');
});
return {
@ -639,7 +620,6 @@ export default defineComponent({
onTopBarGradualChange,
onMenuBarGradualChange,
onColumnsMenuBarGradualChange,
onMenuBarHighlightChange,
onThemeConfigChange,
onIsFixedHeaderChange,
onIsShowLogoChange,
@ -673,9 +653,10 @@ export default defineComponent({
.layout-breadcrumb-seting-bar-flex {
display: flex;
align-items: center;
margin-bottom: 5px;
&-label {
flex: 1;
color: #666666;
color: var(--el-text-color-primary);
}
}
.layout-drawer-content-flex {
@ -694,16 +675,16 @@ export default defineComponent({
.el-container {
height: 100%;
.el-aside-dark {
background-color: #b3c0d1;
background-color: var(--next-color-seting-header);
}
.el-aside {
background-color: #d3dce6;
background-color: var(--next-color-seting-aside);
}
.el-header {
background-color: #b3c0d1;
background-color: var(--next-color-seting-header);
}
.el-main {
background-color: #e9eef3;
background-color: var(--next-color-seting-main);
}
}
.el-circular {
@ -714,7 +695,7 @@ export default defineComponent({
}
.drawer-layout-active {
border: 1px solid;
border-color: var(--color-primary);
border-color: var(--el-color-primary);
}
.layout-tips-warp,
.layout-tips-warp-active {
@ -724,7 +705,7 @@ export default defineComponent({
top: 50%;
transform: translate(-50%, -50%);
border: 1px solid;
border-color: var(--color-primary-light-4);
border-color: var(--el-color-primary-light-4);
border-radius: 100%;
padding: 4px;
.layout-tips-box {
@ -733,7 +714,7 @@ export default defineComponent({
height: 30px;
z-index: 9;
border: 1px solid;
border-color: var(--color-primary-light-4);
border-color: var(--el-color-primary-light-4);
border-radius: 100%;
.layout-tips-txt {
transition: inherit;
@ -743,11 +724,11 @@ export default defineComponent({
line-height: 1;
letter-spacing: 2px;
white-space: nowrap;
color: var(--color-primary-light-4);
color: var(--el-color-primary-light-4);
text-align: center;
transform: rotate(30deg);
left: -1px;
background-color: #e9eef3;
background-color: var(--next-color-seting-main);
width: 32px;
height: 17px;
line-height: 17px;
@ -756,13 +737,13 @@ export default defineComponent({
}
.layout-tips-warp-active {
border: 1px solid;
border-color: var(--color-primary);
border-color: var(--el-color-primary);
.layout-tips-box {
border: 1px solid;
border-color: var(--color-primary);
border-color: var(--el-color-primary);
.layout-tips-txt {
color: var(--color-primary) !important;
background-color: #e9eef3 !important;
color: var(--el-color-primary) !important;
background-color: var(--next-color-seting-main) !important;
}
}
}
@ -770,18 +751,18 @@ export default defineComponent({
.el-circular {
transition: all 0.3s ease-in-out;
border: 1px solid;
border-color: var(--color-primary);
border-color: var(--el-color-primary);
}
.layout-tips-warp {
transition: all 0.3s ease-in-out;
border-color: var(--color-primary);
border-color: var(--el-color-primary);
.layout-tips-box {
transition: inherit;
border-color: var(--color-primary);
border-color: var(--el-color-primary);
.layout-tips-txt {
transition: inherit;
color: var(--color-primary) !important;
background-color: #e9eef3 !important;
color: var(--el-color-primary) !important;
background-color: var(--next-color-seting-main) !important;
}
}
}

View File

@ -6,33 +6,30 @@
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="" :disabled="disabledSize === ''">默认</el-dropdown-item>
<el-dropdown-item command="medium" :disabled="disabledSize === 'medium'">中等</el-dropdown-item>
<el-dropdown-item command="small" :disabled="disabledSize === 'small'">小型</el-dropdown-item>
<el-dropdown-item command="mini" :disabled="disabledSize === 'mini'">超小</el-dropdown-item>
<el-dropdown-item command="medium" :disabled="disabledSize === 'large'">大型</el-dropdown-item>
<el-dropdown-item command="small" :disabled="disabledSize === 'default'">默认</el-dropdown-item>
<el-dropdown-item command="mini" :disabled="disabledSize === 'small'">小型</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick">
<el-icon title="菜单搜索">
<elementSearch />
<ele-Search />
</el-icon>
</div>
<div class="layout-navbars-breadcrumb-user-icon" @click="onLayoutSetingClick">
<i class="icon-skin iconfont" title="布局配置"></i>
</div>
<div class="layout-navbars-breadcrumb-user-icon">
<el-popover placement="bottom" trigger="click" v-model:visible="isShowUserNewsPopover" :width="300" popper-class="el-popover-pupop-user-news">
<el-popover placement="bottom" trigger="click">
<template #reference>
<el-badge :is-dot="true" @click="isShowUserNewsPopover = !isShowUserNewsPopover">
<el-badge :is-dot="true">
<el-icon title="消息">
<elementBell />
<ele-Bell />
</el-icon>
</el-badge>
</template>
<transition name="el-zoom-in-top">
<UserNews v-show="isShowUserNewsPopover" />
</transition>
<UserNews />
</el-popover>
</div>
<div class="layout-navbars-breadcrumb-user-icon mr10" @click="onScreenfullClick">
@ -41,9 +38,9 @@
<el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
<span class="layout-navbars-breadcrumb-user-link">
<img :src="getUserInfos.photo" class="layout-navbars-breadcrumb-user-link-photo mr5" />
{{ getUserInfos.userName === '' ? 'test' : getUserInfos.userName }}
{{ getUserInfos.userName === '' ? 'common' : getUserInfos.userName }}
<el-icon class="el-icon--right">
<elementArrowDown />
<ele-ArrowDown />
</el-icon>
</span>
<template #dropdown>
@ -61,7 +58,7 @@
</template>
<script lang="ts">
import { ref, getCurrentInstance, computed, reactive, toRefs, onMounted } from 'vue';
import { ref, getCurrentInstance, computed, reactive, toRefs, onMounted, defineComponent } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessageBox, ElMessage } from 'element-plus';
import screenfull from 'screenfull';
@ -70,22 +67,21 @@ import { useStore } from '/@/store/index';
import { Session, Local } from '/@/utils/storage';
import UserNews from '/@/layout/navBars/breadcrumb/userNews.vue';
import Search from '/@/layout/navBars/breadcrumb/search.vue';
export default {
export default defineComponent({
name: 'layoutBreadcrumbUser',
components: { UserNews, Search },
setup() {
const { proxy } = getCurrentInstance() as any;
const { proxy } = <any>getCurrentInstance();
const router = useRouter();
const store = useStore();
const searchRef = ref();
const state = reactive({
isScreenfull: false,
isShowUserNewsPopover: false,
disabledSize: '',
disabledSize: 'large',
});
// 获取用户信息 vuex
const getUserInfos = computed(() => {
return store.state.userInfos.userInfos;
return <any>store.state.userInfos.userInfos;
});
// 获取布局配置信息
const getThemeConfig = computed(() => {
@ -93,10 +89,11 @@ export default {
});
// 设置分割样式
const layoutUserFlexNum = computed(() => {
let { layout, isClassicSplitMenu } = getThemeConfig.value;
let num = '';
if (layout === 'defaults' || (layout === 'classic' && !isClassicSplitMenu) || layout === 'columns') num = 1;
else num = null;
let num: string | number = '';
const { layout, isClassicSplitMenu } = getThemeConfig.value;
const layoutArr: string[] = ['defaults', 'columns'];
if (layoutArr.includes(layout) || (layout === 'classic' && !isClassicSplitMenu)) num = '1';
else num = '';
return num;
});
// 全屏点击时
@ -126,6 +123,7 @@ export default {
showCancelButton: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
buttonSize: 'default',
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true;
@ -141,13 +139,13 @@ export default {
}
},
})
.then(() => {
.then(async () => {
Session.clear(); // 清除缓存/token等
resetRoute(); // 删除/重置路由
router.push('/login');
await resetRoute(); // 删除/重置路由
ElMessage.success('安全退出成功!');
setTimeout(() => {
ElMessage.success('安全退出成功!');
}, 300);
window.location.href = ''; // 去登录页
}, 500);
})
.catch(() => {});
} else if (path === 'wareHouse') {
@ -172,18 +170,15 @@ export default {
// 初始化全局组件大小
const initComponentSize = () => {
switch (Local.get('themeConfig').globalComponentSize) {
case '':
state.disabledSize = '';
case 'large':
state.disabledSize = 'large';
break;
case 'medium':
state.disabledSize = 'medium';
case 'default':
state.disabledSize = 'default';
break;
case 'small':
state.disabledSize = 'small';
break;
case 'mini':
state.disabledSize = 'mini';
break;
}
};
// 页面加载时
@ -204,7 +199,7 @@ export default {
...toRefs(state),
};
},
};
});
</script>
<style scoped lang="scss">
@ -226,13 +221,13 @@ export default {
&-icon {
padding: 0 10px;
cursor: pointer;
color: var(--bg-topBarColor);
color: var(--next-bg-topBarColor);
height: 50px;
line-height: 50px;
display: flex;
align-items: center;
&:hover {
background: rgba(0, 0, 0, 0.04);
background: var(--next-color-user-hover);
i {
display: inline-block;
animation: logoAnimation 0.3s ease-in-out;
@ -240,7 +235,7 @@ export default {
}
}
::v-deep(.el-dropdown) {
color: var(--bg-topBarColor);
color: var(--next-bg-topBarColor);
}
::v-deep(.el-badge) {
height: 40px;

View File

@ -21,8 +21,8 @@
</template>
<script lang="ts">
import { reactive, toRefs } from 'vue';
export default {
import { reactive, toRefs, defineComponent } from 'vue';
export default defineComponent({
name: 'layoutBreadcrumbUserNews',
setup() {
const state = reactive({
@ -53,21 +53,21 @@ export default {
...toRefs(state),
};
},
};
});
</script>
<style scoped lang="scss">
.layout-navbars-breadcrumb-user-news {
.head-box {
display: flex;
border-bottom: 1px solid #ebeef5;
border-bottom: 1px solid var(--el-border-color-lighter);
box-sizing: border-box;
color: #333333;
color: var(--el-text-color-primary);
justify-content: space-between;
height: 35px;
align-items: center;
.head-box-btn {
color: var(--color-primary);
color: var(--el-color-primary);
font-size: 13px;
cursor: pointer;
opacity: 0.8;
@ -84,25 +84,25 @@ export default {
padding-bottom: 12px;
}
.content-box-msg {
color: #999999;
color: var(--el-text-color-secondary);
margin-top: 5px;
margin-bottom: 5px;
}
.content-box-time {
color: #999999;
color: var(--el-text-color-secondary);
}
}
}
.foot-box {
height: 35px;
color: var(--color-primary);
color: var(--el-color-primary);
font-size: 13px;
cursor: pointer;
opacity: 0.8;
display: flex;
align-items: center;
justify-content: center;
border-top: 1px solid #ebeef5;
border-top: 1px solid var(--el-border-color-lighter);
&:hover {
opacity: 1;
}

View File

@ -6,11 +6,11 @@
</template>
<script lang="ts">
import { computed } from 'vue';
import { computed, defineComponent } from 'vue';
import { useStore } from '/@/store/index';
import BreadcrumbIndex from '/@/layout/navBars/breadcrumb/index.vue';
import TagsView from '/@/layout/navBars/tagsView/tagsView.vue';
export default {
export default defineComponent({
name: 'layoutNavBars',
components: { BreadcrumbIndex, TagsView },
setup() {
@ -24,7 +24,7 @@ export default {
setShowTagsView,
};
},
};
});
</script>
<style scoped lang="scss">

View File

@ -36,16 +36,22 @@ export default defineComponent({
props: {
dropdown: {
type: Object,
default: () => {
return {
x: 0,
y: 0,
};
},
},
},
setup(props, { emit }) {
const state = reactive({
isShow: false,
dropdownList: [
{ contextMenuClickId: 0, txt: '刷新', affix: false, icon: 'elementRefreshRight' },
{ contextMenuClickId: 1, txt: '关闭', affix: false, icon: 'elementClose' },
{ contextMenuClickId: 2, txt: '关闭其它', affix: false, icon: 'elementCircleClose' },
{ contextMenuClickId: 3, txt: '全部关闭', affix: false, icon: 'elementFolderDelete' },
{ contextMenuClickId: 0, txt: '刷新', affix: false, icon: 'ele-RefreshRight' },
{ contextMenuClickId: 1, txt: '关闭', affix: false, icon: 'ele-Close' },
{ contextMenuClickId: 2, txt: '关闭其它', affix: false, icon: 'ele-CircleClose' },
{ contextMenuClickId: 3, txt: '全部关闭', affix: false, icon: 'ele-FolderDelete' },
{ contextMenuClickId: 4, txt: '当前页全屏', affix: false, icon: 'iconfont icon-fullscreen' },
],
item: {},

View File

@ -16,20 +16,24 @@
}
"
>
<i class="iconfont icon-webicon318 layout-navbars-tagsview-ul-li-iconfont font14" v-if="isActive(v)"></i>
<i class="layout-navbars-tagsview-ul-li-iconfont" :class="v.meta.icon" v-if="!isActive(v) && getThemeConfig.isTagsviewIcon"></i>
<i class="iconfont icon-webicon318 layout-navbars-tagsview-ul-li-iconfont" v-if="isActive(v)"></i>
<SvgIcon :name="v.meta.icon" v-if="!isActive(v) && getThemeConfig.isTagsviewIcon" class="pr5" />
<span>{{ v.meta.title }}</span>
<template v-if="isActive(v)">
<SvgIcon name="elementRefreshRight" class="ml5" @click.stop="refreshCurrentTagsView($route.fullPath)" />
<SvgIcon
name="elementClose"
name="ele-RefreshRight"
class="ml5 layout-navbars-tagsview-ul-li-refresh"
@click.stop="refreshCurrentTagsView($route.fullPath)"
/>
<SvgIcon
name="ele-Close"
class="layout-navbars-tagsview-ul-li-icon layout-icon-active"
v-if="!v.meta.isAffix"
@click.stop="closeCurrentTagsView(getThemeConfig.isShareTagsView ? v.path : v.url)"
/>
</template>
<SvgIcon
name="elementClose"
name="ele-Close"
class="layout-navbars-tagsview-ul-li-icon layout-icon-three"
v-if="!v.meta.isAffix"
@click.stop="closeCurrentTagsView(getThemeConfig.isShareTagsView ? v.path : v.url)"
@ -42,7 +46,20 @@
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, computed, ref, nextTick, onBeforeUpdate, onBeforeMount, onUnmounted, getCurrentInstance, watch } from 'vue';
import {
toRefs,
reactive,
onMounted,
computed,
ref,
nextTick,
onBeforeUpdate,
onBeforeMount,
onUnmounted,
getCurrentInstance,
watch,
defineComponent,
} from 'vue';
import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
import Sortable from 'sortablejs';
import { ElMessage } from 'element-plus';
@ -51,19 +68,47 @@ import { Session } from '/@/utils/storage';
import { isObjectValueEqual } from '/@/utils/arrayOperation';
import other from '/@/utils/other';
import Contextmenu from '/@/layout/navBars/tagsView/contextmenu.vue';
export default {
// 定义接口来定义对象的类型
interface TagsViewState {
routeActive: string;
routePath: string | unknown;
dropdown: {
x: string | number;
y: string | number;
};
tagsRefsIndex: number;
tagsViewList: any[];
sortable: any;
tagsViewRoutesList: any[];
}
interface RouteParams {
path: string;
url: string;
}
interface CurrentContextmenu {
meta: {
isDynamic: boolean;
};
params: any;
query: any;
path: string;
contextMenuClickId: string | number;
}
export default defineComponent({
name: 'layoutTagsView',
components: { Contextmenu },
setup() {
const { proxy } = getCurrentInstance() as any;
const tagsRefs = ref([]);
const { proxy } = <any>getCurrentInstance();
const tagsRefs = ref<any[]>([]);
const scrollbarRef = ref();
const contextmenuRef = ref();
const tagsUlRef = ref();
const store = useStore();
const route = useRoute();
const router = useRouter();
const state: any = reactive({
const state = reactive<TagsViewState>({
routeActive: '',
routePath: route.path,
dropdown: { x: '', y: '' },
@ -81,7 +126,7 @@ export default {
return store.state.themeConfig.themeConfig;
});
// 设置 tagsView 高亮
const isActive = (v) => {
const isActive = (v: RouteParams) => {
if (getThemeConfig.value.isShareTagsView) {
return v.path === state.routePath;
} else {
@ -160,7 +205,7 @@ export default {
// 防止拿取不到路由信息
nextTick(async () => {
// 修复https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
let item = '';
let item: any = '';
if (to && to.meta.isDynamic) {
// 动态路由xxx/:id/:name"):参数不同,开启多个 tagsview
if (!getThemeConfig.value.isShareTagsView) await solveAddTagsView(path, to);
@ -250,7 +295,7 @@ export default {
};
// 当前项右键菜单点击,拿当前点击的路由路径对比 浏览器缓存中的 tagsView 路由数组,取当前点击项的详细路由信息
// 防止 tagsView 非当前页演示时,操作异常
const getCurrentRouteItem = (path: string, cParams: { [key: string]: any }) => {
const getCurrentRouteItem = (path: string, cParams: any) => {
const itemRoute = Session.get('tagsViewList') ? Session.get('tagsViewList') : state.tagsViewList;
return itemRoute.find((v: any) => {
if (
@ -267,7 +312,7 @@ export default {
});
};
// 当前项右键菜单点击
const onCurrentContextmenuClick = async (item) => {
const onCurrentContextmenuClick = async (item: CurrentContextmenu) => {
const cParams = item.meta.isDynamic ? item.params : item.query;
if (!getCurrentRouteItem(item.path, cParams)) return ElMessage({ type: 'warning', message: '请正确输入路径及完整参数query、params' });
const { path, name, params, query, meta, url } = getCurrentRouteItem(item.path, cParams);
@ -382,7 +427,7 @@ export default {
});
};
// 获取 tagsView 的下标:用于处理 tagsView 点击时的横向滚动
const getTagsRefsIndex = (path: string) => {
const getTagsRefsIndex = (path: string | unknown) => {
nextTick(async () => {
// await 使用该写法,防止拿取不到 tagsViewList 列表数据不完整
let tagsViewList = await state.tagsViewList;
@ -399,7 +444,7 @@ export default {
};
// 设置 tagsView 可以进行拖拽
const initSortable = async () => {
const el = document.querySelector('.layout-navbars-tagsview-ul') as HTMLElement;
const el = <HTMLElement>document.querySelector('.layout-navbars-tagsview-ul');
if (!el) return false;
state.sortable.el && state.sortable.destroy();
state.sortable = Sortable.create(el, {
@ -429,7 +474,7 @@ export default {
// 拖动问题https://gitee.com/lyt-top/vue-next-admin/issues/I3ZRRI
window.addEventListener('resize', onSortableResize);
// 监听非本页面调用 0 刷新当前1 关闭当前2 关闭其它3 关闭全部 4 当前页全屏
proxy.mittBus.on('onCurrentContextmenuClick', (data: object) => {
proxy.mittBus.on('onCurrentContextmenuClick', (data: CurrentContextmenu) => {
onCurrentContextmenuClick(data);
});
// 监听布局配置界面开启/关闭拖拽
@ -486,7 +531,6 @@ export default {
return {
isActive,
onContextmenu,
getTagsViewRoutes,
onTagsClick,
tagsRefs,
contextmenuRef,
@ -501,13 +545,15 @@ export default {
...toRefs(state),
};
},
};
});
</script>
<style scoped lang="scss">
.layout-navbars-tagsview {
background-color: var(--el-color-white);
border-bottom: 1px solid #f1f2f3;
border-bottom: 1px solid var(--next-border-color-light);
position: relative;
z-index: 4;
::v-deep(.el-scrollbar__wrap) {
overflow-x: auto !important;
}
@ -536,9 +582,9 @@ export default {
cursor: pointer;
justify-content: space-between;
&:hover {
background-color: var(--color-primary-light-9);
color: var(--color-primary);
border-color: var(--color-primary-light-6);
background-color: var(--el-color-primary-light-9);
color: var(--el-color-primary);
border-color: var(--el-color-primary-light-6);
}
&-iconfont {
position: relative;
@ -554,8 +600,8 @@ export default {
line-height: 14px;
right: -5px;
&:hover {
color: var(--color-whites);
background-color: var(--color-primary-light-3);
color: var(--el-color-white);
background-color: var(--el-color-primary-light-3);
}
}
.layout-icon-active {
@ -566,64 +612,12 @@ export default {
}
}
.is-active {
color: var(--color-whites);
background: var(--color-primary);
border-color: var(--color-primary);
color: var(--el-color-white);
background: var(--el-color-primary);
border-color: var(--el-color-primary);
transition: border-color 3s ease;
}
}
// 风格2
.tags-style-two {
.layout-navbars-tagsview-ul-li {
height: 34px !important;
line-height: 34px !important;
border: none !important;
.layout-navbars-tagsview-ul-li-iconfont {
display: none;
}
.layout-icon-active {
display: none;
}
.layout-icon-three {
display: block;
}
}
.is-active {
background: none !important;
color: var(--color-primary) !important;
border-bottom: 2px solid !important;
border-color: var(--color-primary) !important;
border-radius: 0 !important;
}
}
// 风格3
.tags-style-three {
.layout-navbars-tagsview-ul-li {
height: 34px !important;
line-height: 34px !important;
border-right: 1px solid #f6f6f6 !important;
border-top: none !important;
border-bottom: none !important;
border-left: none !important;
border-radius: 0 !important;
margin-right: 0 !important;
&:first-of-type {
border-left: 1px solid #f6f6f6 !important;
}
.layout-icon-active {
display: none;
}
.layout-icon-three {
display: block;
}
}
.is-active {
background: var(--el-color-white) !important;
color: var(--color-primary) !important;
border-top: 1px solid !important;
border-top-color: var(--color-primary) !important;
}
}
// 风格4
.tags-style-four {
.layout-navbars-tagsview-ul-li {
@ -643,7 +637,41 @@ export default {
}
.is-active {
background: none !important;
color: var(--color-primary) !important;
color: var(--el-color-primary) !important;
}
}
// 风格5
.tags-style-five {
align-items: flex-end;
.tags-style-five-svg {
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='68' height='34' viewBox='0 0 68 34' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m27,0c-7.99582,0 -11.95105,0.00205 -12,12l0,6c0,8.284 -0.48549,16.49691 -8.76949,16.49691l54.37857,-0.11145c-8.284,0 -8.60908,-8.10146 -8.60908,-16.38546l0,-6c0.11145,-12.08445 -4.38441,-12 -12,-12l-13,0z' fill='%23409eff'/%3E%3C/svg%3E")
12 27 15;
}
.layout-navbars-tagsview-ul-li {
padding: 0 5px;
border-width: 15px 27px 15px;
border-style: solid;
border-color: transparent;
margin: 0 -15px;
.layout-icon-active,
.layout-navbars-tagsview-ul-li-iconfont,
.layout-navbars-tagsview-ul-li-refresh {
display: none;
}
.layout-icon-three {
display: block;
}
&:hover {
@extend .tags-style-five-svg;
background: var(--el-color-primary-light-9);
color: unset;
}
}
.is-active {
@extend .tags-style-five-svg;
background: var(--el-color-primary-light-9) !important;
color: var(--el-color-primary) !important;
z-index: 1;
}
}
}

View File

@ -10,18 +10,20 @@
</template>
<SubItem :chil="val.children" />
</el-sub-menu>
<el-menu-item :index="val.path" :key="val.path" v-else>
<template #title v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
<SvgIcon :name="val.meta.icon" />
{{ val.meta.title }}
</template>
<template #title v-else>
<a :href="val.meta.isLink" target="_blank" rel="opener">
<template v-else>
<el-menu-item :index="val.path" :key="val.path">
<template #title v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
<SvgIcon :name="val.meta.icon" />
{{ val.meta.title }}
</a>
</template>
</el-menu-item>
</template>
<template #title v-else>
<a :href="val.meta.isLink" target="_blank" rel="opener">
<SvgIcon :name="val.meta.icon" />
{{ val.meta.title }}
</a>
</template>
</el-menu-item>
</template>
</template>
</el-menu>
</el-scrollbar>
@ -43,7 +45,7 @@ export default defineComponent({
},
},
setup(props) {
const { proxy } = getCurrentInstance() as any;
const { proxy } = <any>getCurrentInstance();
const route = useRoute();
const store = useStore();
const state = reactive({
@ -51,19 +53,19 @@ export default defineComponent({
});
// 获取父级菜单数据
const menuLists = computed(() => {
return props.menuList;
return <any>props.menuList;
});
// 设置横向滚动条可以鼠标滚轮滚动
const onElMenuHorizontalScroll = (e: any) => {
const eventDelta = e.wheelDelta || -e.deltaY * 40;
proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap.scrollLeft = proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap.scrollLeft + eventDelta / 4;
proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap$.scrollLeft = proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap.scrollLeft + eventDelta / 4;
};
// 初始化数据,页面刷新时,滚动条滚动到对应位置
const initElMenuOffsetLeft = () => {
nextTick(() => {
let els: any = document.querySelector('.el-menu.el-menu--horizontal li.is-active');
if (!els) return false;
proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap.scrollLeft = els.offsetLeft;
proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap$.scrollLeft = els.offsetLeft;
});
};
// 路由过滤递归函数
@ -91,10 +93,10 @@ export default defineComponent({
return currentData;
};
// 设置页面当前路由高亮
const setCurrentRouterHighlight = (currentRoute) => {
const setCurrentRouterHighlight = (currentRoute: any) => {
const { path, meta } = currentRoute;
if (store.state.themeConfig.themeConfig.layout === 'classic') {
state.defaultActive = `/${path.split('/')[1]}`;
(<any>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('/');
@ -113,7 +115,6 @@ export default defineComponent({
onBeforeRouteUpdate((to) => {
// 修复https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
setCurrentRouterHighlight(to);
proxy.mittBus.emit('onMenuClick');
// 修复经典布局开启切割菜单时点击tagsView后左侧导航菜单数据不变的问题
let { layout, isClassicSplitMenu } = store.state.themeConfig.themeConfig;
if (layout === 'classic' && isClassicSplitMenu) {

View File

@ -7,18 +7,20 @@
</template>
<sub-item :chil="val.children" />
</el-sub-menu>
<el-menu-item :index="val.path" :key="val.path" v-else>
<template v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
<SvgIcon :name="val.meta.icon" />
<span>{{ val.meta.title }}</span>
</template>
<template v-else>
<a :href="val.meta.isLink" target="_blank" rel="opener">
<template v-else>
<el-menu-item :index="val.path" :key="val.path">
<template v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
<SvgIcon :name="val.meta.icon" />
{{ val.meta.title }}
</a>
</template>
</el-menu-item>
<span>{{ val.meta.title }}</span>
</template>
<template v-else>
<a :href="val.meta.isLink" target="_blank" rel="opener">
<SvgIcon :name="val.meta.icon" />
{{ val.meta.title }}
</a>
</template>
</el-menu-item>
</template>
</template>
</template>
@ -35,7 +37,7 @@ export default defineComponent({
setup(props) {
// 获取父级菜单数据
const chils = computed(() => {
return props.chil;
return <any>props.chil;
});
return {
chils,

View File

@ -15,21 +15,23 @@
</template>
<SubItem :chil="val.children" />
</el-sub-menu>
<el-menu-item :index="val.path" :key="val.path" v-else>
<SvgIcon :name="val.meta.icon" />
<template #title v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
<span>{{ val.meta.title }}</span>
</template>
<template #title v-else>
<a :href="val.meta.isLink" target="_blank" rel="opener">{{ val.meta.title }}</a></template
>
</el-menu-item>
<template v-else>
<el-menu-item :index="val.path" :key="val.path">
<SvgIcon :name="val.meta.icon" />
<template #title v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
<span>{{ val.meta.title }}</span>
</template>
<template #title v-else>
<a :href="val.meta.isLink" target="_blank" rel="opener" class="w100">{{ val.meta.title }}</a>
</template>
</el-menu-item>
</template>
</template>
</el-menu>
</template>
<script lang="ts">
import { toRefs, reactive, computed, defineComponent, getCurrentInstance, onMounted, watch } from 'vue';
import { toRefs, reactive, computed, defineComponent, onMounted, watch } from 'vue';
import { useRoute, onBeforeRouteUpdate } from 'vue-router';
import { useStore } from '/@/store/index';
import SubItem from '/@/layout/navMenu/subItem.vue';
@ -43,7 +45,6 @@ export default defineComponent({
},
},
setup(props) {
const { proxy } = getCurrentInstance() as any;
const store = useStore();
const route = useRoute();
const state = reactive({
@ -53,14 +54,14 @@ export default defineComponent({
});
// 获取父级菜单数据
const menuLists = computed(() => {
return props.menuList;
return <any>props.menuList;
});
// 获取布局配置信息
const getThemeConfig = computed(() => {
return store.state.themeConfig.themeConfig;
});
// 菜单高亮(详情时,父级高亮)
const setParentHighlight = (currentRoute) => {
const setParentHighlight = (currentRoute: any) => {
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('/');
@ -84,7 +85,6 @@ export default defineComponent({
onBeforeRouteUpdate((to) => {
// 修复https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
state.defaultActive = setParentHighlight(to);
proxy.mittBus.emit('onMenuClick');
const clientWidth = document.body.clientWidth;
if (clientWidth < 1000) getThemeConfig.value.isCollapse = false;
});

View File

@ -19,7 +19,7 @@ export default defineComponent({
});
// 初始化页面加载 loading
const initIframeLoad = () => {
state.iframeUrl = route.meta.isLink;
state.iframeUrl = <any>route.meta.isLink;
nextTick(() => {
state.iframeLoading = true;
const iframe = document.getElementById('iframe');

View File

@ -1,22 +1,38 @@
<template>
<div class="layout-view-bg-white flex layout-view-link" :style="{ height: `calc(100vh - ${setLinkHeight}` }">
<a :href="currentRouteMeta.isLink" target="_blank" rel="opener" class="flex-margin"
>{{ currentRouteMeta.title }}{{ currentRouteMeta.isLink }}</a
>
<a :href="currentRouteMeta.isLink" target="_blank" rel="opener" class="flex-margin">
{{ currentRouteMeta.title }}{{ currentRouteMeta.isLink }}
</a>
</div>
</template>
<script lang="ts">
import { defineComponent, toRefs, reactive, computed, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useRoute, RouteMeta } from 'vue-router';
import { useStore } from '/@/store/index';
// 定义接口来定义对象的类型
interface LinkViewState {
currentRouteMeta: {
isLink: string;
title: string;
};
}
interface LinkViewRouteMeta extends RouteMeta {
isLink: string;
title: string;
}
export default defineComponent({
name: 'layoutLinkView',
setup() {
const route = useRoute();
const store = useStore();
const state = reactive({
currentRouteMeta: {},
const state = reactive<LinkViewState>({
currentRouteMeta: {
isLink: '',
title: '',
},
});
// 设置 link 的高度
const setLinkHeight = computed(() => {
@ -28,7 +44,7 @@ export default defineComponent({
watch(
() => route.path,
() => {
state.currentRouteMeta = route.meta;
state.currentRouteMeta = <LinkViewRouteMeta>route.meta;
},
{
immediate: true,

View File

@ -2,8 +2,8 @@
<div class="h100">
<router-view v-slot="{ Component }">
<transition :name="setTransitionName" mode="out-in">
<keep-alive :include="keepAliveNameList">
<component :is="Component" :key="refreshRouterViewKey" class="w100" />
<keep-alive :include="keepAliveNameList"
><component :is="Component" :key="refreshRouterViewKey" class="w100" :style="{ minHeight }" />
</keep-alive>
</transition>
</router-view>
@ -14,16 +14,28 @@
import { computed, defineComponent, toRefs, reactive, getCurrentInstance, onBeforeMount, onUnmounted, nextTick, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useStore } from '/@/store/index';
// 定义接口来定义对象的类型
interface ParentViewState {
refreshRouterViewKey: null | string;
keepAliveNameList: string[];
}
export default defineComponent({
name: 'layoutParentView',
props: {
minHeight: {
type: String,
default: '',
},
},
setup() {
const { proxy } = getCurrentInstance() as any;
const { proxy } = <any>getCurrentInstance();
const route = useRoute();
const store = useStore();
const state: any = reactive({
const state = reactive<ParentViewState>({
refreshRouterViewKey: null,
keepAliveNameList: [],
keepAliveNameNewList: [],
});
// 设置主界面切换动画
const setTransitionName = computed(() => {

View File

@ -6,7 +6,6 @@ import { directive } from '/@/utils/directive';
import other from '/@/utils/other';
import ElementPlus from 'element-plus';
import zhCn from 'element-plus/es/locale/lang/zh-cn';
import 'element-plus/dist/index.css';
import '/@/theme/index.scss';
import mitt from 'mitt';
@ -16,6 +15,6 @@ const app = createApp(App);
directive(app);
other.elSvg(app);
app.use(router).use(store, key).use(ElementPlus, { size: other.globalComponentSize, locale: zhCn }).mount('#app');
app.use(router).use(store, key).use(ElementPlus).mount('#app');
app.config.globalProperties.mittBus = mitt();

View File

@ -3,7 +3,9 @@ import { Session } from '/@/utils/storage';
import { NextLoading } from '/@/utils/loading';
import { setAddRoute, setFilterMenuAndCacheTagsViewRoutes } from '/@/router/index';
import { dynamicRoutes } from '/@/router/route';
import { getMenuAdmin, getMenuTest } from '/@/api/menu/index';
import { useMenuApi } from '/@/api/menu/index';
const menuApi = useMenuApi();
const layouModules: any = import.meta.glob('../layout/routerView/*.{vue,tsx}');
const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}');
@ -50,9 +52,9 @@ export function getBackEndControlRoutes() {
// 模拟 admin 与 test
const auth = store.state.userInfos.userInfos.roles[0];
// 管理员 admin
if (auth === 'admin') return getMenuAdmin();
if (auth === 'admin') return menuApi.getMenuAdmin();
// 其它用户 test
else return getMenuTest();
else return menuApi.getMenuTest();
}
/**

View File

@ -163,8 +163,8 @@ export function setFilterRouteEnd() {
* @description 此处循环为 dynamicRoutes/@/router/route第一个顶级 children 的路由一维数组,非多级嵌套
* @link 参考https://next.router.vuejs.org/zh/api/#addroute
*/
export function setAddRoute() {
setFilterRouteEnd().forEach((route: RouteRecordRaw) => {
export async function setAddRoute() {
await setFilterRouteEnd().forEach((route: RouteRecordRaw) => {
const routeName: any = route.name;
if (!router.hasRoute(routeName)) router.addRoute(route);
});
@ -176,8 +176,8 @@ export function setAddRoute() {
* @description 此处循环为 dynamicRoutes/@/router/route第一个顶级 children 的路由一维数组,非多级嵌套
* @link 参考https://next.router.vuejs.org/zh/api/#push
*/
export function resetRoute() {
setFilterRouteEnd().forEach((route: RouteRecordRaw) => {
export async function resetRoute() {
await setFilterRouteEnd().forEach((route: RouteRecordRaw) => {
const routeName: any = route.name;
router.hasRoute(routeName) && router.removeRoute(routeName);
});

View File

@ -88,7 +88,7 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
isAffix: false,
isIframe: false,
roles: ['admin'],
icon: 'elementColdDrink',
icon: 'ele-ColdDrink',
},
},
{
@ -118,7 +118,7 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
isAffix: false,
isIframe: false,
roles: ['admin'],
icon: 'elementOfficeBuilding',
icon: 'ele-OfficeBuilding',
},
},
{
@ -133,7 +133,7 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
isAffix: false,
isIframe: false,
roles: ['admin'],
icon: 'elementSetUp',
icon: 'ele-SetUp',
},
},
],

View File

@ -5,20 +5,15 @@ export interface ThemeConfigState {
themeConfig: {
isDrawer: boolean;
primary: string;
success: string;
info: string;
warning: string;
danger: string;
topBar: string;
menuBar: string;
columnsMenuBar: string;
topBarColor: string;
menuBarColor: string;
columnsMenuBarColor: string;
isTopBarColorGradual: boolean;
menuBar: string;
menuBarColor: string;
isMenuBarColorGradual: boolean;
columnsMenuBar: string;
columnsMenuBarColor: string;
isColumnsMenuBarColorGradual: boolean;
isMenuBarColorHighlight: boolean;
isCollapse: boolean;
isUniqueOpened: boolean;
isFixedHeader: boolean;
@ -55,30 +50,36 @@ export interface ThemeConfigState {
// 路由列表
export interface RoutesListState {
routesList: Array<object>;
routesList: object[];
isColumnsMenuHover: Boolean;
isColumnsNavHover: Boolean;
}
// 路由缓存列表
export interface KeepAliveNamesState {
keepAliveNames: Array<string>;
keepAliveNames: string[];
}
// TagsView 路由列表
export interface TagsViewRoutesState {
tagsViewRoutes: Array<object>;
tagsViewRoutes: object[];
isTagsViewCurrenFull: Boolean;
}
// 用户信息
export interface UserInfosState {
userInfos: object;
userInfos: {
authBtnList: string[];
photo: string;
roles: string[];
time: number;
userName: string;
};
}
// 后端返回原始路由(未处理时)
export interface RequestOldRoutesState {
requestOldRoutes: Array<object>;
requestOldRoutes: object[];
}
// 主接口(顶级类型声明)

View File

@ -1,5 +1,4 @@
import { Module } from 'vuex';
// 此处加上 `.ts` 后缀报错,具体原因不详
import { KeepAliveNamesState, RootStateTypes } from '/@/store/interface/index';
const keepAliveNamesModule: Module<KeepAliveNamesState, RootStateTypes> = {

View File

@ -1,5 +1,4 @@
import { Module } from 'vuex';
// 此处加上 `.ts` 后缀报错,具体原因不详
import { RequestOldRoutesState, RootStateTypes } from '/@/store/interface/index';
const requestOldRoutesModule: Module<RequestOldRoutesState, RootStateTypes> = {

View File

@ -1,5 +1,4 @@
import { Module } from 'vuex';
// 此处加上 `.ts` 后缀报错,具体原因不详
import { RoutesListState, RootStateTypes } from '/@/store/interface/index';
const routesListModule: Module<RoutesListState, RootStateTypes> = {

View File

@ -1,5 +1,4 @@
import { Module } from 'vuex';
// 此处加上 `.ts` 后缀报错,具体原因不详
import { TagsViewRoutesState, RootStateTypes } from '/@/store/interface/index';
import { Session } from '/@/utils/storage';

View File

@ -1,5 +1,4 @@
import { Module } from 'vuex';
// 此处加上 `.ts` 后缀报错,具体原因不详
import { ThemeConfigState, RootStateTypes } from '/@/store/interface/index';
/**
@ -17,16 +16,8 @@ const themeConfigModule: Module<ThemeConfigState, RootStateTypes> = {
/**
* 全局主题
*/
// 默认 primary 颜色,请注意:需要同时修改 `/@/theme/common/var.scss` 对应的值
// 默认 primary 主题颜色
primary: '#409eff',
// 默认 success 颜色,请注意:需要同时修改 `/@/theme/common/var.scss` 对应的值
success: '#67c23a',
// 默认 info 颜色,请注意:需要同时修改 `/@/theme/common/var.scss` 对应的值
info: '#909399',
// 默认 warning 颜色,请注意:需要同时修改 `/@/theme/common/var.scss` 对应的值
warning: '#e6a23c',
// 默认 danger 颜色,请注意:需要同时修改 `/@/theme/common/var.scss` 对应的值
danger: '#f56c6c',
/**
* 菜单 / 顶栏
@ -34,26 +25,24 @@ const themeConfigModule: Module<ThemeConfigState, RootStateTypes> = {
* 切换布局需手动设置样式,设置的样式自动同步各布局,
* 代码位置:/@/layout/navBars/breadcrumb/setings.vue
*/
// 默认顶栏导航背景颜色,请注意:需要同时修改 `/@/theme/common/var.scss` 对应的值
// 默认顶栏导航背景颜色
topBar: '#ffffff',
// 默认菜单导航背景颜色,请注意:需要同时修改 `/@/theme/common/var.scss` 对应的值
menuBar: '#545c64',
// 默认分栏菜单背景颜色,请注意:需要同时修改 `/@/theme/common/var.scss` 对应的值
columnsMenuBar: '#545c64',
// 默认顶栏导航字体颜色,请注意:需要同时修改 `/@/theme/common/var.scss` 对应的值
// 默认顶栏导航字体颜色
topBarColor: '#606266',
// 默认菜单导航字体颜色,请注意:需要同时修改 `/@/theme/common/var.scss` 对应的值
menuBarColor: '#eaeaea',
// 默认分栏菜单字体颜色,请注意:需要同时修改 `/@/theme/common/var.scss` 对应的值
columnsMenuBarColor: '#e6e6e6',
// 是否开启顶栏背景颜色渐变
isTopBarColorGradual: false,
// 默认菜单导航背景颜色
menuBar: '#545c64',
// 默认菜单导航字体颜色
menuBarColor: '#eaeaea',
// 是否开启菜单背景颜色渐变
isMenuBarColorGradual: false,
// 默认分栏菜单背景颜色
columnsMenuBar: '#545c64',
// 默认分栏菜单字体颜色
columnsMenuBarColor: '#e6e6e6',
// 是否开启分栏菜单背景颜色渐变
isColumnsMenuBarColorGradual: false,
// 是否开启菜单字体背景高亮
isMenuBarColorHighlight: false,
/**
* 界面设置
@ -110,9 +99,9 @@ const themeConfigModule: Module<ThemeConfigState, RootStateTypes> = {
/**
* 其它设置
*/
// Tagsview 风格:可选值"<tags-style-one|tags-style-two|tags-style-three|tags-style-four>",默认 tags-style-one
// Tagsview 风格:可选值"<tags-style-one|tags-style-four|tags-style-five>",默认 tags-style-five
// 定义的值与 `/src/layout/navBars/tagsView/tagsView.vue` 中的 class 同名
tagsStyle: 'tags-style-one',
tagsStyle: 'tags-style-five',
// 主页面切换动画:可选值"<slide-right|slide-left|opacitys>",默认 slide-right
animation: 'slide-right',
// 分栏高亮风格:可选值"<columns-round|columns-card>",默认 columns-round
@ -140,9 +129,9 @@ const themeConfigModule: Module<ThemeConfigState, RootStateTypes> = {
// 网站主标题(菜单导航、浏览器当前网页标题)
globalTitle: 'vue-next-admin',
// 网站副标题(登录页顶部文字)
globalViceTitle: 'SMALL@小柒',
// 默认全局组件大小,可选值"<|medium|small|mini>",默认 ''
globalComponentSize: '',
globalViceTitle: 'vueNextAdmin',
// 默认全局组件大小,可选值"<large|'default'|small>",默认 'large'
globalComponentSize: 'large',
},
},
mutations: {

View File

@ -1,22 +1,27 @@
import { Module } from 'vuex';
import { Session } from '/@/utils/storage';
// 此处加上 `.ts` 后缀报错,具体原因不详
import { UserInfosState, RootStateTypes } from '/@/store/interface/index';
const userInfosModule: Module<UserInfosState, RootStateTypes> = {
namespaced: true,
state: {
userInfos: {},
userInfos: {
authBtnList: [],
photo: '',
roles: [],
time: 0,
userName: '',
},
},
mutations: {
// 设置用户信息
getUserInfos(state: any, data: object) {
getUserInfos(state, data: any) {
state.userInfos = data;
},
},
actions: {
// 设置用户信息
async setUserInfos({ commit }, data: object) {
async setUserInfos({ commit }, data: UserInfosState) {
if (data) {
commit('getUserInfos', data);
} else {

View File

@ -7,6 +7,19 @@
outline: none !important;
}
:root {
--next-bg-main-color: #f8f8f8;
--next-bg-color: #f5f5ff;
--next-border-color-light: #f1f2f3;
--next-color-primary-lighter: #ecf5ff;
--next-color-dark-hover: #0000001a;
--next-color-menu-hover: rgba(0, 0, 0, 0.1);
--next-color-user-hover: rgba(0, 0, 0, 0.04);
--next-color-seting-main: #e9eef3;
--next-color-seting-aside: #d3dce6;
--next-color-seting-header: #b3c0d1;
}
html,
body,
#app {
@ -18,7 +31,7 @@ body,
font-weight: 400;
-webkit-font-smoothing: antialiased;
-webkit-tap-highlight-color: transparent;
background-color: #f8f8f8;
background-color: var(--next-bg-main-color);
font-size: 14px;
overflow: hidden;
position: relative;
@ -30,7 +43,7 @@ body,
width: 100%;
height: 100%;
.layout-aside {
background: var(--bg-menuBar);
background: var(--next-bg-menuBar);
box-shadow: 2px 0 6px rgb(0 21 41 / 1%);
height: inherit;
position: relative;
@ -49,7 +62,7 @@ body,
padding: 0 !important;
overflow: hidden;
width: 100%;
background-color: #f8f8f8;
background-color: var(--next-bg-main-color);
}
.el-scrollbar {
width: 100%;
@ -63,7 +76,7 @@ body,
border: 1px solid var(--el-border-color-light, #ebeef5);
}
.layout-el-aside-br-color {
border-right: 1px solid rgb(238, 238, 238);
border-right: 1px solid var(--el-border-color-light, #ebeef5);
}
// pc端左侧导航样式
.layout-aside-pc-220 {
@ -88,11 +101,11 @@ body,
}
.layout-aside-mobile-close {
left: -220px;
transition: all 0.3s cubic-bezier(1, -0.04, 0, 1.32);
transition: all 0.3s cubic-bezier(0.39, 0.58, 0.57, 1);
}
.layout-aside-mobile-open {
left: 0;
transition: all 0.3s cubic-bezier(0.53, -0.26, 0.42, 1.18);
transition: all 0.3s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.layout-aside-mobile-mode {
position: fixed;
@ -103,6 +116,7 @@ body,
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9999998;
animation: error-img 0.3s;
}
.layout-scrollbar {
@extend .el-scrollbar;
@ -133,7 +147,7 @@ body,
------------------------------- */
#nprogress {
.bar {
background: var(--color-primary) !important;
background: var(--el-color-primary) !important;
z-index: 9999999 !important;
}
}
@ -145,6 +159,7 @@ body,
}
.flex-auto {
flex: 1;
overflow: hidden;
}
.flex-center {
@extend .flex;
@ -209,19 +224,19 @@ body,
/* 颜色值
------------------------------- */
.color-primary {
color: var(--color-primary);
color: var(--el-color-primary);
}
.color-success {
color: var(--color-success);
color: var(--el-color-success);
}
.color-warning {
color: var(--color-warning);
color: var(--el-color-warning);
}
.color-danger {
color: var(--color-danger);
color: var(--el-color-danger);
}
.color-info {
color: var(--color-info);
color: var(--el-color-info);
}
/* 字体大小全局样式

View File

@ -1,2 +0,0 @@
@import 'common/transition.scss';
@import 'common/var.scss';

View File

@ -38,7 +38,7 @@
------------------------------- */
.breadcrumb-enter-active,
.breadcrumb-leave-active {
transition: all 0.3s;
transition: all 0.5s ease;
}
.breadcrumb-enter-from,
.breadcrumb-leave-active {
@ -47,6 +47,7 @@
}
.breadcrumb-leave-active {
position: absolute;
z-index: -1;
}
/* logo 过渡动画
@ -83,3 +84,11 @@
opacity: 1;
}
}
@keyframes error-img-two {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}

View File

@ -1,127 +0,0 @@
/**
* scss 怎么动态创建变量
* 本来想用 @function@for 好像不可以动态创建
* 2020.12.19 lyt 记录
**/
/* 定义初始颜色
------------------------------- */
$--color-primary: #409eff !default;
$--color-whites: #ffffff !default;
$--color-primary-light-1: mix($--color-whites, $--color-primary, 10%) !default;
$--color-primary-light-2: mix($--color-whites, $--color-primary, 20%) !default;
$--color-primary-light-3: mix($--color-whites, $--color-primary, 30%) !default;
$--color-primary-light-4: mix($--color-whites, $--color-primary, 40%) !default;
$--color-primary-light-5: mix($--color-whites, $--color-primary, 50%) !default;
$--color-primary-light-6: mix($--color-whites, $--color-primary, 60%) !default;
$--color-primary-light-7: mix($--color-whites, $--color-primary, 70%) !default;
$--color-primary-light-8: mix($--color-whites, $--color-primary, 80%) !default;
$--color-primary-light-9: mix($--color-whites, $--color-primary, 90%) !default;
$--color-success: #67c23a !default;
$--color-success-light-1: mix($--color-whites, $--color-success, 10%) !default;
$--color-success-light-2: mix($--color-whites, $--color-success, 20%) !default;
$--color-success-light-3: mix($--color-whites, $--color-success, 30%) !default;
$--color-success-light-4: mix($--color-whites, $--color-success, 40%) !default;
$--color-success-light-5: mix($--color-whites, $--color-success, 50%) !default;
$--color-success-light-6: mix($--color-whites, $--color-success, 60%) !default;
$--color-success-light-7: mix($--color-whites, $--color-success, 70%) !default;
$--color-success-light-8: mix($--color-whites, $--color-success, 80%) !default;
$--color-success-light-9: mix($--color-whites, $--color-success, 90%) !default;
$--color-info: #909399 !default;
$--color-info-light-1: mix($--color-whites, $--color-info, 10%) !default;
$--color-info-light-2: mix($--color-whites, $--color-info, 20%) !default;
$--color-info-light-3: mix($--color-whites, $--color-info, 30%) !default;
$--color-info-light-4: mix($--color-whites, $--color-info, 40%) !default;
$--color-info-light-5: mix($--color-whites, $--color-info, 50%) !default;
$--color-info-light-6: mix($--color-whites, $--color-info, 60%) !default;
$--color-info-light-7: mix($--color-whites, $--color-info, 70%) !default;
$--color-info-light-8: mix($--color-whites, $--color-info, 80%) !default;
$--color-info-light-9: mix($--color-whites, $--color-info, 90%) !default;
$--color-warning: #e6a23c !default;
$--color-warning-light-1: mix($--color-whites, $--color-warning, 10%) !default;
$--color-warning-light-2: mix($--color-whites, $--color-warning, 20%) !default;
$--color-warning-light-3: mix($--color-whites, $--color-warning, 30%) !default;
$--color-warning-light-4: mix($--color-whites, $--color-warning, 40%) !default;
$--color-warning-light-5: mix($--color-whites, $--color-warning, 50%) !default;
$--color-warning-light-6: mix($--color-whites, $--color-warning, 60%) !default;
$--color-warning-light-7: mix($--color-whites, $--color-warning, 70%) !default;
$--color-warning-light-8: mix($--color-whites, $--color-warning, 80%) !default;
$--color-warning-light-9: mix($--color-whites, $--color-warning, 90%) !default;
$--color-danger: #f56c6c !default;
$--color-danger-light-1: mix($--color-whites, $--color-danger, 10%) !default;
$--color-danger-light-2: mix($--color-whites, $--color-danger, 20%) !default;
$--color-danger-light-3: mix($--color-whites, $--color-danger, 30%) !default;
$--color-danger-light-4: mix($--color-whites, $--color-danger, 40%) !default;
$--color-danger-light-5: mix($--color-whites, $--color-danger, 50%) !default;
$--color-danger-light-6: mix($--color-whites, $--color-danger, 60%) !default;
$--color-danger-light-7: mix($--color-whites, $--color-danger, 70%) !default;
$--color-danger-light-8: mix($--color-whites, $--color-danger, 80%) !default;
$--color-danger-light-9: mix($--color-whites, $--color-danger, 90%) !default;
$--bg-topBar: #ffffff;
$--bg-menuBar: #545c64;
$--bg-columnsMenuBar: #545c64;
$--bg-topBarColor: #606266;
$--bg-menuBarColor: #eaeaea;
$--bg-columnsMenuBarColor: #e6e6e6;
/* 赋值给:root
------------------------------- */
:root {
--color-primary: #{$--color-primary};
--color-whites: #{$--color-whites};
--color-primary-light-1: #{$--color-primary-light-1};
--color-primary-light-2: #{$--color-primary-light-2};
--color-primary-light-3: #{$--color-primary-light-3};
--color-primary-light-4: #{$--color-primary-light-4};
--color-primary-light-5: #{$--color-primary-light-5};
--color-primary-light-6: #{$--color-primary-light-6};
--color-primary-light-7: #{$--color-primary-light-7};
--color-primary-light-8: #{$--color-primary-light-8};
--color-primary-light-9: #{$--color-primary-light-9};
--color-success: #{$--color-success};
--color-success-light-1: #{$--color-success-light-1};
--color-success-light-2: #{$--color-success-light-2};
--color-success-light-3: #{$--color-success-light-3};
--color-success-light-4: #{$--color-success-light-4};
--color-success-light-5: #{$--color-success-light-5};
--color-success-light-6: #{$--color-success-light-6};
--color-success-light-7: #{$--color-success-light-7};
--color-success-light-8: #{$--color-success-light-8};
--color-success-light-9: #{$--color-success-light-9};
--color-info: #{$--color-info};
--color-info-light-1: #{$--color-info-light-1};
--color-info-light-2: #{$--color-info-light-2};
--color-info-light-3: #{$--color-info-light-3};
--color-info-light-4: #{$--color-info-light-4};
--color-info-light-5: #{$--color-info-light-5};
--color-info-light-6: #{$--color-info-light-6};
--color-info-light-7: #{$--color-info-light-7};
--color-info-light-8: #{$--color-info-light-8};
--color-info-light-9: #{$--color-info-light-9};
--color-warning: #{$--color-warning};
--color-warning-light-1: #{$--color-warning-light-1};
--color-warning-light-2: #{$--color-warning-light-2};
--color-warning-light-3: #{$--color-warning-light-3};
--color-warning-light-4: #{$--color-warning-light-4};
--color-warning-light-5: #{$--color-warning-light-5};
--color-warning-light-6: #{$--color-warning-light-6};
--color-warning-light-7: #{$--color-warning-light-7};
--color-warning-light-8: #{$--color-warning-light-8};
--color-warning-light-9: #{$--color-warning-light-9};
--color-danger: #{$--color-danger};
--color-danger-light-1: #{$--color-danger-light-1};
--color-danger-light-2: #{$--color-danger-light-2};
--color-danger-light-3: #{$--color-danger-light-3};
--color-danger-light-4: #{$--color-danger-light-4};
--color-danger-light-5: #{$--color-danger-light-5};
--color-danger-light-6: #{$--color-danger-light-6};
--color-danger-light-7: #{$--color-danger-light-7};
--color-danger-light-8: #{$--color-danger-light-8};
--color-danger-light-9: #{$--color-danger-light-9};
--bg-topBar: #{$--bg-topBar};
--bg-menuBar: #{$--bg-menuBar};
--bg-columnsMenuBar: #{$--bg-columnsMenuBar};
--bg-topBarColor: #{$--bg-topBarColor};
--bg-menuBarColor: #{$--bg-menuBarColor};
--bg-columnsMenuBarColor: #{$--bg-columnsMenuBarColor};
}

View File

@ -1,57 +1,219 @@
/* 深色模式样式
------------------------------- */
[data-theme='dark'] {
// 全局
filter: invert(0.9) hue-rotate(180deg);
img,
.layout-lock-screen-img,
.visualizing-demo2,
.w-e-panel-tab-content {
filter: invert(1) hue-rotate(180deg);
}
.error img {
filter: unset;
}
// 变量(自定义时,只需修改这里的值)
--next-bg-main: #1f1f1f;
--next-color-white: #ffffff;
--next-color-disabled: #191919;
--next-color-bar: #dadada;
--next-color-primary: #303030;
--next-border-color: #424242;
--next-border-black: #333333;
--next-border-columns: #2a2a2a;
--next-color-seting: #505050;
--next-text-color-regular: #9b9da1;
--next-text-color-placeholder: #7a7a7a;
--next-color-hover: #3c3c3c;
--next-color-hover-rgba: rgba(0, 0, 0, 0.3);
// root
--next-bg-main-color: var(--next-bg-main) !important;
--next-bg-topBar: var(--next-color-disabled) !important;
--next-bg-topBarColor: var(--next-color-bar) !important;
--next-bg-menuBar: var(--next-color-disabled) !important;
--next-bg-menuBarColor: var(--next-color-bar) !important;
--next-bg-columnsMenuBar: var(--next-color-disabled) !important;
--next-bg-columnsMenuBarColor: var(--next-color-bar) !important;
--next-border-color-light: var(--next-border-black) !important;
--next-color-primary-lighter: var(--next-color-primary) !important;
--next-bg-color: var(--next-color-primary) !important;
--next-color-dark-hover: var(--next-color-hover) !important;
--next-color-menu-hover: var(--next-color-hover-rgba) !important;
--next-color-user-hover: var(--next-color-hover-rgba) !important;
--next-color-seting-main: var(--next-color-seting) !important;
--next-color-seting-aside: var(--next-color-hover) !important;
--next-color-seting-header: var(--next-color-primary) !important;
// element plus
.el-radio-button__original-radio:checked + .el-radio-button__inner,
.el-image-viewer__close,
.el-image-viewer__actions__inner,
.el-image-viewer__next,
.el-image-viewer__prev {
color: #000000 !important;
}
.el-overlay {
background-color: rgba(0, 0, 0, 0.05) !important;
}
.el-drawer {
box-shadow: 0 8px 10px -5px rgb(0 0 0 / 1%), 0 16px 24px 2px rgb(0 0 0 / 2%), 0 6px 30px 5px rgb(0 0 0 / 1%);
}
--el-color-white: var(--next-color-disabled) !important;
--el-text-color-primary: var(--next-color-bar) !important;
--el-border-color-base: var(--next-border-black) !important;
--el-border-color-light: var(--next-border-black) !important;
--el-text-color-regular: var(--next-text-color-regular) !important;
--el-bg-color: var(--next-color-hover-rgba) !important;
--el-color-success-lighter: var(--next-color-primary) !important;
--el-color-warning-lighter: var(--next-color-primary) !important;
--el-color-danger-lighter: var(--next-color-primary) !important;
--el-color-primary-lighter: var(--next-color-primary) !important;
--el-color-primary-light-9: var(--next-color-hover) !important;
--el-text-color-disabled-base: var(--el-color-primary) !important;
--el-border-color-lighter: var(--next-border-black) !important;
--el-text-color-placeholder: var(--next-text-color-placeholder) !important;
--el-disabled-bg-color: var(--next-color-disabled) !important;
--el-fill-base: var(--next-color-white) !important;
// 数据可视化演示
.visualizing-container-head {
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.02)) !important;
.visualizing-container-head-left-text-box {
color: #000000 !important;
// button
.el-button {
&:hover {
border-color: var(--next-border-color) !important;
}
}
.visualizing-container-content-left {
background: linear-gradient(to right, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.01)) !important;
}
.visualizing-container-content-center {
background: linear-gradient(to top, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.01)) !important;
}
.visualizing-container-content-right {
background: linear-gradient(to left, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.01)) !important;
}
.cropper-modal {
background-color: #ffffff;
.el-button--primary,
.el-button--info,
.el-button--danger,
.el-button--success,
.el-button--warning {
--el-button-text-color: var(--next-color-white) !important;
--el-button-hover-text-color: var(--next-color-white) !important;
--el-button-disabled-text-color: var(--next-color-white) !important;
&:hover {
border-color: var(--el-button-hover-border-color, var(--el-button-hover-bg-color)) !important;
}
}
// 其它菜单等
--bg-menuBar: #ffffff !important;
--bg-menuBarColor: #303133 !important;
--bg-columnsMenuBar: #ffffff !important;
--bg-columnsMenuBarColor: #303133 !important;
--color-whites: #000000 !important;
// drawer
.el-divider__text {
background-color: var(--el-color-white) !important;
}
.el-drawer {
border-left: 1px solid var(--next-border-color-light) !important;
}
// tabs
.el-tabs--border-card {
background-color: var(--el-color-white) !important;
}
.el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active {
background: var(--next-color-primary-lighter);
}
// alert / notice-bar
.home-card-item {
border: 1px solid var(--next-border-color-light) !important;
}
.el-alert,
.notice-bar {
border: 1px solid var(--next-border-color) !important;
background-color: var(--next-color-disabled) !important;
}
// menu
.layout-aside {
border-right: 1px solid var(--next-border-color-light) !important;
}
// colorPicker
.el-color-picker__mask {
background: unset !important;
}
.el-color-picker__trigger {
border: 1px solid var(--next-border-color-light) !important;
}
// popper / dropdown
.el-popper {
border: 1px solid var(--next-border-color) !important;
color: var(--el-text-color-primary) !important;
.el-popper__arrow:before {
background: var(--el-color-white) !important;
border: 1px solid var(--next-border-color);
}
a {
color: var(--el-text-color-primary) !important;
}
}
.el-popper,
.el-dropdown-menu {
background: var(--el-color-white) !important;
}
.el-dropdown-menu__item:hover:not(.is-disabled) {
background: var(--el-bg-color) !important;
}
.el-dropdown-menu__item.is-disabled {
font-weight: 700 !important;
}
// input
.el-input-group__append,
.el-input-group__prepend {
border: var(--el-input-border) !important;
border-right: none !important;
background: var(--next-color-disabled) !important;
border-left: 0 !important;
}
.el-input-number__decrease,
.el-input-number__increase {
background: var(--next-color-disabled) !important;
}
// tag
.el-select .el-select__tags .el-tag {
background-color: var(--next-bg-color) !important;
}
// pagination
.el-pagination.is-background .el-pager li:not(.disabled).active {
color: var(--next-color-white) !important;
}
.el-pagination.is-background .btn-next,
.el-pagination.is-background .btn-prev,
.el-pagination.is-background .el-pager li {
background-color: var(--next-bg-color);
}
// radio
.el-radio-button:not(.is-active) .el-radio-button__inner {
border: 1px solid var(--next-border-color-light) !important;
border-left: 0 !important;
}
.el-radio-button.is-active .el-radio-button__inner {
color: var(--next-color-white) !important;
}
// countup
.countup-card-item-flex {
color: var(--el-text-color-primary) !important;
}
// editor
.editor-container {
.w-e-toolbar {
background: var(--el-color-white) !important;
border: 1px solid var(--next-border-color-light) !important;
.w-e-menu:hover {
background: var(--next-color-user-hover) !important;
i {
color: var(--el-text-color-primary) !important;
}
}
}
.w-e-text-container {
border: 1px solid var(--next-border-color-light) !important;
border-top: none !important;
.w-e-text {
background: var(--el-color-white) !important;
}
}
}
// date-picker
.el-picker-panel {
background: var(--el-color-white) !important;
}
// dialog
.el-dialog {
border: 1px solid var(--el-border-color-lighter);
.el-dialog__header {
color: var(--el-text-color-primary) !important;
}
}
// columns
.layout-columns-aside ul .layout-columns-active {
color: var(--next-color-white) !important;
}
.layout-columns-aside {
border-right: 1px solid var(--next-border-columns);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -13,12 +13,12 @@
span {
cursor: pointer;
&:hover {
color: var(--color-primary);
color: var(--el-color-primary);
text-decoration: underline;
}
}
.span-active {
color: var(--color-primary);
color: var(--el-color-primary);
text-decoration: underline;
}
}
@ -47,21 +47,21 @@
}
&:hover {
cursor: pointer;
background-color: var(--color-primary-light-9);
border: 1px solid var(--color-primary-light-6);
background-color: var(--el-color-primary-light-9);
border: 1px solid var(--el-color-primary-light-6);
.icon-selector-warp-item-value {
i {
color: var(--color-primary);
color: var(--el-color-primary);
}
}
}
}
.icon-selector-active {
background-color: var(--color-primary-light-9);
border: 1px solid var(--color-primary-light-6);
background-color: var(--el-color-primary-light-9);
border: 1px solid var(--el-color-primary-light-6);
.icon-selector-warp-item-value {
i {
color: var(--color-primary);
color: var(--el-color-primary);
}
}
}

View File

@ -1,5 +1,5 @@
@import './app.scss';
@import './base.scss';
@import 'common/transition.scss';
@import './other.scss';
@import './element.scss';
@import './iconSelector.scss';

View File

@ -15,7 +15,7 @@
.loading-next .loading-next-box-warp .loading-next-box-item {
width: 33.333333%;
height: 33.333333%;
background: var(--color-primary);
background: var(--el-color-primary);
float: left;
animation: loading-next-animation 1.2s infinite ease;
border-radius: 1px;

25
src/theme/media/date.scss Normal file
View File

@ -0,0 +1,25 @@
@import './index.scss';
/* 页面宽度小于768px
------------------------------- */
@media screen and (max-width: $sm) {
// 时间选择器适配
.el-date-range-picker {
width: 100vw;
.el-picker-panel__body {
min-width: 100%;
.el-date-range-picker__content {
.el-date-range-picker__header div {
margin-left: 22px;
margin-right: 0px;
}
& + .el-date-range-picker__content {
.el-date-range-picker__header div {
margin-left: 0px;
margin-right: 22px;
}
}
}
}
}
}

View File

@ -3,8 +3,21 @@
/* 页面宽度小于768px
------------------------------- */
@media screen and (max-width: $sm) {
.home-warning-media,
.home-dynamic-media {
.home-media,
.home-media-sm {
margin-top: 15px;
}
}
/* 页面宽度小于1200px
------------------------------- */
@media screen and (max-width: $lg) {
.home-media-lg {
margin-top: 15px;
}
.home-monitor {
.flex-warp-item {
width: 33.33% !important;
}
}
}

View File

@ -1,37 +1,15 @@
/* 栅格布局(媒体查询变量)
* $xs <768px 响应式栅格
* https://developer.mozilla.org/zh-CN/docs/Learn/CSS/CSS_layout/Media_queries
* $us ≥376px 响应式栅格
* $xs ≥576px 响应式栅格
* $sm ≥768px 响应式栅格
* $md ≥992px 响应式栅格
* $lg ≥1200px 响应式栅格
* $xl ≥1920px 响应式栅格
------------------------------- */
$us: 376px;
$xs: 576px;
$sm: 768px;
$md: 992px;
$lg: 1200px;
$xl: 1920px;
/* 页面宽度小于576px
------------------------------- */
@media screen and (max-width: $xs) {
}
/* 页面宽度小于768px
------------------------------- */
@media screen and (max-width: $sm) {
}
/* 页面宽度大于768px小于992px
------------------------------- */
@media screen and (min-width: $sm) and (max-width: $md) {
}
/* 页面宽度大于992px小于1200px
------------------------------- */
@media screen and (min-width: $md) and (max-width: $lg) {
}
/* 页面宽度大于1920px
------------------------------- */
@media screen and (min-width: $xl) {
}

View File

@ -1,12 +1,41 @@
@import './index.scss';
/* 页面宽度小于992px
------------------------------- */
@media screen and (max-width: $lg) {
.login-container {
.login-icon-group {
&::before {
content: '';
height: 70% !important;
transition: all 0.3s ease;
}
&::after {
content: '';
width: 100px !important;
height: 200px !important;
transition: all 0.3s ease;
}
}
}
}
/* 页面宽度小于992px
------------------------------- */
@media screen and (max-width: $md) {
.login-content {
right: unset !important;
left: 50% !important;
transform: translate(-50%, -50%) translate3d(0, 0, 0) !important;
}
}
/* 页面宽度小于576px
------------------------------- */
@media screen and (max-width: $xs) {
.login-container {
background: none !important;
.login-logo {
display: none;
.login-icon-group {
display: none !important;
}
.login-content {
width: 100% !important;
@ -16,9 +45,6 @@
box-shadow: unset !important;
border: none !important;
}
.login-copyright {
display: none !important;
}
.el-form-item {
display: flex !important;
}
@ -27,7 +53,7 @@
/* 页面宽度小于375px
------------------------------- */
@media screen and (max-width: 376px) {
@media screen and (max-width: $us) {
.login-container {
.login-content-title {
font-size: 18px !important;

View File

@ -10,3 +10,4 @@
@import './pagination.scss';
@import './dialog.scss';
@import './cityLinkage.scss';
@import './date.scss';

View File

@ -9,7 +9,7 @@
height: 3px !important;
}
::-webkit-scrollbar-track-piece {
background-color: #f8f8f8;
background-color: var(--next-bg-main-color);
}
// 滚动条的设置
::-webkit-scrollbar-thumb {
@ -40,7 +40,7 @@
height: 7px;
}
::-webkit-scrollbar-track-piece {
background-color: #f8f8f8;
background-color: var(--next-bg-main-color);
}
// 滚动条的设置
::-webkit-scrollbar-thumb {

View File

@ -1,34 +0,0 @@
/* Button 按钮
------------------------------- */
@mixin Button($main, $c1, $c2) {
color: set-color($main);
background: set-color($c1);
border-color: set-color($c2);
}
/* Radio 单选框、Checkbox 多选框
------------------------------- */
@mixin RadioCheckbox($name) {
background-color: set-color($name);
border-color: set-color($name);
}
/* Tag 标签
------------------------------- */
@mixin Tag($main, $c1, $c2) {
color: set-color($main);
background-color: set-color($c1);
border-color: set-color($c2);
}
@mixin TagDark($main, $c1) {
color: set-color($main);
background-color: set-color($c1);
}
/* Alert 警告
------------------------------- */
@mixin Alert($main, $c1, $c2) {
color: set-color($main);
background: set-color($c1);
border: 1px solid set-color($c2);
}

View File

@ -1,5 +0,0 @@
/* 颜色调用函数
------------------------------- */
@function set-color($key) {
@return var(--color-#{$key});
}

View File

@ -4,7 +4,7 @@
* @param old 源数据
* @returns 两数组相同返回 `true`,反之则反
*/
export function judementSameArr(news: Array<string>, old: Array<string>): boolean {
export function judementSameArr(news: unknown[] | string[], old: string[]): boolean {
let count = 0;
const leng = old.length;
for (let i in old) {

View File

@ -5,25 +5,21 @@ import { formatDate } from '/@/utils/formatTime';
export default function () {
const { toClipboard } = useClipboard();
// 百分比格式化
//百分比格式化
const percentFormat = (row: any, column: number, cellValue: any) => {
return cellValue ? `${cellValue}%` : '-';
};
// 列表日期时间格式化
//列表日期时间格式化
const dateFormatYMD = (row: any, column: number, cellValue: any) => {
if (!cellValue) return '-';
return formatDate(new Date(cellValue), 'YYYY-mm-dd');
};
// 列表日期时间格式化
//列表日期时间格式化
const dateFormatYMDHMS = (row: any, column: number, cellValue: any) => {
if (!cellValue) return '-';
return formatDate(new Date(cellValue), 'YYYY-mm-dd HH:MM:SS');
};
// 列表日期时间格式化
//列表日期时间格式化
const dateFormatHMS = (row: any, column: number, cellValue: any) => {
if (!cellValue) return '-';
let time = 0;
@ -31,34 +27,30 @@ export default function () {
if (typeof cellValue === 'number') time = cellValue;
return formatDate(new Date(time * 1000), 'HH:MM:SS');
};
// 小数格式化
const scaleFormat = (value: any = 0, scale: number = 4) => {
return Number.parseFloat(value).toFixed(scale);
};
// 小数格式化
const scale2Format = (value: any = 0) => {
return Number.parseFloat(value).toFixed(2);
};
// 点击复制文本
const copyText = (text: string) => {
return new Promise((resolve, reject) => {
try {
// 复制
//复制
toClipboard(text);
// 下面可以设置复制成功的提示框等操作
//下面可以设置复制成功的提示框等操作
ElMessage.success('复制成功!');
resolve(text);
} catch (e) {
// 复制失败
ElMessage.error('复制失败');
//复制失败
ElMessage.error('复制失败');
reject(e);
}
});
};
return {
percentFormat,
dateFormatYMD,

View File

@ -28,14 +28,14 @@ const getAlicdnIconfont = () => {
});
};
// 初始化获取 css 样式,获取 element plus 自带 svg 图标,增加了 element 前缀使用时elementAim
// 初始化获取 css 样式,获取 element plus 自带 svg 图标,增加了 ele- 前缀使用时ele-Aim
const getElementPlusIconfont = () => {
return new Promise((resolve, reject) => {
nextTick(() => {
const icons = svg as any;
const sheetsIconList = [];
for (const i in icons) {
sheetsIconList.push(`element${icons[i].name}`);
sheetsIconList.push(`ele-${icons[i].name}`);
}
if (sheetsIconList.length > 0) resolve(sheetsIconList);
else reject('未获取到值,请刷新重试');

View File

@ -1,25 +1,16 @@
import { nextTick } from 'vue';
import loadingCss from '/@/theme/loading.scss';
import '/@/theme/loading.scss';
/**
* 页面全局 Loading
* @method setCss 载入 css
* @method start 创建 loading
* @method done 移除 loading
*/
export const NextLoading = {
// 载入 css
setCss: () => {
let link = document.createElement('link');
link.rel = 'stylesheet';
link.href = loadingCss;
link.crossOrigin = 'anonymous';
document.getElementsByTagName('head')[0].appendChild(link);
},
// 创建 loading
start: () => {
const bodys: Element = document.body;
const div = document.createElement('div');
const div = <HTMLElement>document.createElement('div');
div.setAttribute('class', 'loading-next');
const htmls = `
<div class="loading-next-box">
@ -43,11 +34,9 @@ export const NextLoading = {
// 移除 loading
done: () => {
nextTick(() => {
setTimeout(() => {
window.nextLoading = false;
const el = document.querySelector('.loading-next');
el && el.parentNode?.removeChild(el);
}, 1000);
window.nextLoading = false;
const el = <HTMLElement>document.querySelector('.loading-next');
el?.parentNode?.removeChild(el);
});
},
};

View File

@ -14,14 +14,14 @@ import SvgIcon from '/@/components/svgIcon/index.vue';
export function elSvg(app: App) {
const icons = svg as any;
for (const i in icons) {
app.component(`element${icons[i].name}`, icons[i]);
app.component(`ele-${icons[i].name}`, icons[i]);
}
app.component('SvgIcon', SvgIcon);
}
/**
* 设置浏览器标题
* @method other.useTitle();
* 设置浏览器标题国际化
* @method const title = useTitle(); ==> title()
*/
export function useTitle() {
nextTick(() => {
@ -99,6 +99,27 @@ export function isMobile() {
}
}
/**
* 判断数组对象中所有属性是否为空,为空则删除当前行对象
* @description @感谢大黄
* @param list 数组对象
* @returns 删除空值后的数组对象
*/
export function handleEmpty(list: any) {
const arr = [];
for (const i in list) {
const d = [];
for (const j in list[i]) {
d.push(list[i][j]);
}
const leng = d.filter((item) => item === '').length;
if (leng !== d.length) {
arr.push(list[i]);
}
}
return arr;
}
/**
* 统一批量导出
* @method elSvg 导出全局注册 element plus svg 图标
@ -107,6 +128,7 @@ export function isMobile() {
* @method globalComponentSize element plus 全局组件大小
* @method deepClone 对象深克隆
* @method isMobile 判断是否是移动端
* @method handleEmpty 判断数组对象中所有属性是否为空,为空则删除当前行对象
*/
const other = {
elSvg: (app: App) => {
@ -125,6 +147,9 @@ const other = {
isMobile: () => {
return isMobile();
},
handleEmpty: (list: any) => {
return handleEmpty(list);
},
};
// 统一批量导出

View File

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

View File

@ -36,7 +36,7 @@ export function rgbToHex(r: any, g: any, b: any) {
* @param level 加深的程度限0-1之间
* @returns 返回处理后的颜色值
*/
export function getDarkColor(color: any, level: number) {
export function getDarkColor(color: string, level: number) {
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return ElMessage.warning('输入错误的hex颜色值');
let rgb = hexToRgb(color);
@ -50,7 +50,7 @@ export function getDarkColor(color: any, level: number) {
* @param level 加深的程度限0-1之间
* @returns 返回处理后的颜色值
*/
export function getLightColor(color: any, level: number) {
export function getLightColor(color: string, level: number) {
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return ElMessage.warning('输入错误的hex颜色值');
let rgb = hexToRgb(color);

View File

@ -1,31 +0,0 @@
import dotenv from 'dotenv';
// 定义接口类型声明
export interface ViteEnv {
VITE_PORT: number;
VITE_OPEN: boolean;
VITE_PUBLIC_PATH: string;
}
/**
* vite 打包相关
* @link 参考https://cn.vitejs.dev/guide/env-and-mode.html
* @returns 返回 `VITE_xxx` 环境变量和模式信息
*/
export function loadEnv(): ViteEnv {
const env = process.env.NODE_ENV;
const ret: any = {};
const envList = [`.env.${env}.local`, `.env.${env}`, '.env.local', '.env'];
envList.forEach((e) => {
dotenv.config({ path: e });
});
for (const envName of Object.keys(process.env)) {
let realName = (process.env as any)[envName].replace(/\\n/g, '\n');
realName = realName === 'true' ? true : realName === 'false' ? false : realName;
if (envName === 'VITE_PORT') realName = Number(realName);
if (envName === 'VITE_OPEN') realName = Boolean(realName);
ret[envName] = realName;
process.env[envName] = realName;
}
return ret;
}

View File

@ -1,7 +1,7 @@
// 页面添加水印效果
const setWatermark = (str: string) => {
const id = '1.23452384164.123412416';
if (document.getElementById(id) !== null) document.body.removeChild(document.getElementById(id) as any);
if (document.getElementById(id) !== null) document.body.removeChild(<HTMLElement>document.getElementById(id));
const can = document.createElement('canvas');
can.width = 200;
can.height = 130;
@ -39,7 +39,7 @@ const watermark = {
// 删除水印
del: () => {
let id = '1.23452384164.123412416';
if (document.getElementById(id) !== null) document.body.removeChild(document.getElementById(id) as any);
if (document.getElementById(id) !== null) document.body.removeChild(<HTMLElement>document.getElementById(id));
},
};

View File

@ -19,9 +19,10 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useRouter } from 'vue-router';
import { Session } from '/@/utils/storage';
export default {
export default defineComponent({
name: '401',
setup() {
const router = useRouter();
@ -33,7 +34,7 @@ export default {
onSetAuth,
};
},
};
});
</script>
<style scoped lang="scss">

View File

@ -19,8 +19,9 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useRouter } from 'vue-router';
export default {
export default defineComponent({
name: '404',
setup() {
const router = useRouter();
@ -31,7 +32,7 @@ export default {
onGoHome,
};
},
};
});
</script>
<style scoped lang="scss">

File diff suppressed because one or more lines are too long

View File

@ -1,89 +0,0 @@
/**
* 最顶部 card
* @returns 返回模拟数据
*/
export const topCardItemList = [
{
title: '今日访问人数',
titleNum: '123',
tip: '在场人数',
tipNum: '911',
color: '#F95959',
iconColor: '#F86C6B',
icon: 'iconfont icon-jinridaiban',
},
{
title: '实验室总数',
titleNum: '123',
tip: '使用中',
tipNum: '611',
color: '#8595F4',
iconColor: '#92A1F4',
icon: 'iconfont icon-AIshiyanshi',
},
{
title: '申请人数(月)',
titleNum: '123',
tip: '通过人数',
tipNum: '911',
color: '#FEBB50',
iconColor: '#FDC566',
icon: 'iconfont icon-shenqingkaiban',
},
];
/**
* 环境监测
* @returns 返回模拟数据
*/
export const environmentList = [
{
icon: 'iconfont icon-yangan',
label: '烟感',
value: '2.1%OBS/M',
iconColor: '#F72B3F',
},
{
icon: 'iconfont icon-wendu',
label: '温度',
value: '30℃',
iconColor: '#91BFF8',
},
{
icon: 'iconfont icon-shidu',
label: '湿度',
value: '57%RH',
iconColor: '#88D565',
},
{
icon: 'iconfont icon-zaosheng',
label: '噪声',
value: '57DB',
iconColor: '#FBD4A0',
},
];
/**
* 动态信息
* @returns 返回模拟数据
*/
export const activitiesList = [
{
time1: '今天',
time2: '12:20:30',
title: '更名',
label: '正式更名为 vue-next-admin',
},
{
time1: '02-17',
time2: '12:20:30',
title: '页面',
label: '完成对首页的开发',
},
{
time1: '02-14',
time2: '12:20:30',
title: '页面',
label: '新增个人中心',
},
];

View File

@ -1,16 +1,16 @@
<template>
<el-form class="login-content-form">
<el-form-item>
<el-input type="text" placeholder="用户名 admin 或不输均为 test" v-model="ruleForm.userName" clearable autocomplete="off">
<el-form size="large" class="login-content-form">
<el-form-item class="login-animation1">
<el-input type="text" placeholder="用户名 admin 或不输均为 common" v-model="ruleForm.userName" clearable autocomplete="off">
<template #prefix>
<el-icon class="el-input__icon"><elementUser /></el-icon>
<el-icon class="el-input__icon"><ele-User /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item>
<el-form-item class="login-animation2">
<el-input :type="isShowPassword ? 'text' : 'password'" placeholder="密码123456" v-model="ruleForm.password" autocomplete="off">
<template #prefix>
<el-icon class="el-input__icon"><elementUnlock /></el-icon>
<el-icon class="el-input__icon"><ele-Unlock /></el-icon>
</template>
<template #suffix>
<i
@ -22,23 +22,20 @@
</template>
</el-input>
</el-form-item>
<el-form-item>
<el-row :gutter="15">
<el-col :span="16">
<el-input type="text" maxlength="4" placeholder="请输入验证码" v-model="ruleForm.code" clearable autocomplete="off">
<template #prefix>
<el-icon class="el-input__icon"><elementPosition /></el-icon>
</template>
</el-input>
</el-col>
<el-col :span="8">
<div class="login-content-code">
<span class="login-content-code-img">1234</span>
</div>
</el-col>
</el-row>
<el-form-item class="login-animation3">
<el-col :span="15">
<el-input type="text" maxlength="4" placeholder="请输入验证码" v-model="ruleForm.code" clearable autocomplete="off">
<template #prefix>
<el-icon class="el-input__icon"><ele-Position /></el-icon>
</template>
</el-input>
</el-col>
<el-col :span="1"></el-col>
<el-col :span="8">
<el-button class="login-content-code">1234</el-button>
</el-col>
</el-form-item>
<el-form-item>
<el-form-item class="login-animation4">
<el-button type="primary" class="login-content-submit" round @click="onSignIn" :loading="loading.signIn">
<span> </span>
</el-button>
@ -47,7 +44,7 @@
</template>
<script lang="ts">
import { toRefs, reactive, defineComponent, computed, getCurrentInstance } from 'vue';
import { toRefs, reactive, defineComponent, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import { initFrontEndControlRoutes } from '/@/router/frontEnd';
@ -56,9 +53,8 @@ import { useStore } from '/@/store/index';
import { Session } from '/@/utils/storage';
import { formatAxis } from '/@/utils/formatTime';
export default defineComponent({
name: 'login',
name: 'loginAccount',
setup() {
const { proxy } = getCurrentInstance() as any;
const store = useStore();
const route = useRoute();
const router = useRouter();
@ -88,7 +84,7 @@ export default defineComponent({
// admin 按钮权限标识
let adminAuthBtnList: Array<string> = ['btn.add', 'btn.del', 'btn.edit', 'btn.link'];
// test 页面权限标识,对应路由 meta.roles用于控制路由的显示/隐藏
let testAuthPageList: Array<string> = ['common'];
let testRoles: Array<string> = ['common'];
// test 按钮权限标识
let testAuthBtnList: Array<string> = ['btn.add', 'btn.link'];
// 不同用户模拟不同的用户权限
@ -96,7 +92,7 @@ export default defineComponent({
defaultRoles = adminRoles;
defaultAuthBtnList = adminAuthBtnList;
} else {
defaultRoles = testAuthPageList;
defaultRoles = testRoles;
defaultAuthBtnList = testAuthBtnList;
}
// 用户信息模拟数据
@ -137,23 +133,19 @@ export default defineComponent({
// 如果是复制粘贴的路径,非首页/登录页,那么登录成功后重定向到对应的路径中
if (route.query?.redirect) {
router.push({
path: route.query?.redirect,
query: Object.keys(route.query?.params).length > 0 ? JSON.parse(route.query?.params) : '',
path: <string>route.query?.redirect,
query: Object.keys(<string>route.query?.params).length > 0 ? JSON.parse(<string>route.query?.params) : '',
});
} else {
router.push('/');
}
// 登录成功提示
setTimeout(() => {
// 关闭 loading
state.loading.signIn = true;
ElMessage.success(`${currentTimeInfo}欢迎回来!`);
// 修复防止退出登录再进入界面时,需要刷新样式才生效的问题,初始化布局样式等(登录的时候触发,目前方案)
proxy.mittBus.emit('onSignInClick');
}, 300);
// 关闭 loading
state.loading.signIn = true;
const signInText = '欢迎回来!';
ElMessage.success(`${currentTimeInfo}${signInText}`);
};
return {
currentTime,
onSignIn,
...toRefs(state),
};
@ -164,39 +156,28 @@ export default defineComponent({
<style scoped lang="scss">
.login-content-form {
margin-top: 20px;
@for $i from 1 through 4 {
.login-animation#{$i} {
opacity: 0;
animation-name: error-num;
animation-duration: 0.5s;
animation-fill-mode: forwards;
animation-delay: calc($i/10) + s;
}
}
.login-content-password {
display: inline-block;
width: 25px;
width: 20px;
cursor: pointer;
&:hover {
color: #909399;
}
}
.login-content-code {
display: flex;
align-items: center;
justify-content: space-around;
.login-content-code-img {
width: 100%;
height: 40px;
line-height: 40px;
background-color: #ffffff;
border: 1px solid rgb(220, 223, 230);
color: #333;
font-size: 16px;
font-weight: 700;
letter-spacing: 5px;
text-indent: 5px;
text-align: center;
cursor: pointer;
transition: all ease 0.2s;
border-radius: 4px;
user-select: none;
&:hover {
border-color: #c0c4cc;
transition: all ease 0.2s;
}
}
width: 100%;
padding: 0;
font-weight: bold;
letter-spacing: 5px;
}
.login-content-submit {
width: 100%;

View File

@ -1,45 +1,55 @@
<template>
<el-form class="login-content-form">
<el-form-item>
<el-form size="large" class="login-content-form">
<el-form-item class="login-animation1">
<el-input type="text" placeholder="请输入手机号" v-model="ruleForm.userName" clearable autocomplete="off">
<template #prefix>
<i class="iconfont icon-dianhua el-input__icon"></i>
</template>
</el-input>
</el-form-item>
<el-form-item>
<el-row :gutter="15">
<el-col :span="16">
<el-input type="text" maxlength="4" placeholder="请输入验证码" v-model="ruleForm.code" clearable autocomplete="off">
<template #prefix>
<el-icon class="el-input__icon"><elementPosition /></el-icon>
</template>
</el-input>
</el-col>
<el-col :span="8">
<el-button class="login-content-code">获取验证码</el-button>
</el-col>
</el-row>
<el-form-item class="login-animation2">
<el-col :span="15">
<el-input type="text" maxlength="4" placeholder="请输入验证码" v-model="ruleForm.code" clearable autocomplete="off">
<template #prefix>
<el-icon class="el-input__icon"><ele-Position /></el-icon>
</template>
</el-input>
</el-col>
<el-col :span="1"></el-col>
<el-col :span="8">
<el-button class="login-content-code">获取验证码</el-button>
</el-col>
</el-form-item>
<el-form-item>
<el-button type="primary" class="login-content-submit" round>
<el-form-item class="login-animation3">
<el-button round type="primary" class="login-content-submit">
<span> </span>
</el-button>
</el-form-item>
<div class="font12 mt30 login-animation4 login-msg">
* 温馨提示建议使用谷歌Microsoft Edge版本 79.0.1072.62 及以上浏览器360浏览器请使用极速模式
</div>
</el-form>
</template>
<script lang="ts">
import { toRefs, reactive, defineComponent } from 'vue';
// 定义接口来定义对象的类型
interface LoginMobileState {
userName: any;
code: string | number | undefined;
}
// 定义对象与类型
const ruleForm: LoginMobileState = {
userName: '',
code: '',
};
export default defineComponent({
name: 'login',
name: 'loginMobile',
setup() {
const state = reactive({
ruleForm: {
userName: '',
code: '',
},
});
const state = reactive({ ruleForm });
return {
...toRefs(state),
};
@ -50,6 +60,15 @@ export default defineComponent({
<style scoped lang="scss">
.login-content-form {
margin-top: 20px;
@for $i from 1 through 4 {
.login-animation#{$i} {
opacity: 0;
animation-name: error-num;
animation-duration: 0.5s;
animation-fill-mode: forwards;
animation-delay: calc($i/10) + s;
}
}
.login-content-code {
width: 100%;
padding: 0;
@ -60,5 +79,8 @@ export default defineComponent({
font-weight: 300;
margin-top: 15px;
}
.login-msg {
color: var(--el-text-color-placeholder);
}
}
</style>

View File

@ -1,21 +1,21 @@
<template>
<div class="login-scan-container">
<div class="login-scan-qrcode" ref="qrcodeRef"></div>
<div ref="qrcodeRef"></div>
<div class="font12 mt20 login-msg">打开手机扫一扫快速登录/注册</div>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, defineComponent, onMounted, getCurrentInstance } from 'vue';
import { ref, defineComponent, onMounted } from 'vue';
import QRCode from 'qrcodejs2-fixes';
export default defineComponent({
name: 'login11',
name: 'loginScan',
setup() {
const { proxy } = getCurrentInstance() as any;
const state = reactive({});
const qrcodeRef = ref<HTMLElement | null>(null);
// 初始化生成二维码
const initQrcode = () => {
proxy.$refs.qrcodeRef.innerHTML = '';
new QRCode(proxy.$refs.qrcodeRef, {
(qrcodeRef.value as HTMLElement).innerHTML = '';
new QRCode(qrcodeRef.value, {
text: `https://qm.qq.com/cgi-bin/qm/qr?k=RdUY97Vx0T0vZ_1OOu-X1yFNkWgDwbjC&jump_from=webapi`,
width: 260,
height: 260,
@ -27,20 +27,32 @@ export default defineComponent({
onMounted(() => {
initQrcode();
});
return {
...toRefs(state),
};
return { qrcodeRef };
},
});
</script>
<style scoped lang="scss">
.login-scan-animation {
opacity: 0;
animation-name: error-num;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
.login-scan-container {
.login-scan-qrcode {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -40%);
padding: 20px;
display: flex;
flex-direction: column;
text-align: center;
@extend .login-scan-animation;
animation-delay: 0.1s;
::v-deep(img) {
margin: auto;
}
.login-msg {
color: var(--el-text-color-placeholder);
@extend .login-scan-animation;
animation-delay: 0.2s;
}
}
</style>

View File

@ -1,114 +1,148 @@
<template>
<div class="login-container">
<div class="login-logo">
<span>{{ getThemeConfig.globalViceTitle }}</span>
<div class="login-icon-group">
<div class="login-icon-group-title">
<img :src="logoMini" />
<div class="login-icon-group-title-text font25">{{ getThemeConfig.globalViceTitle }}</div>
</div>
<img :src="loginIconTwo" class="login-icon-group-icon" />
</div>
<div class="login-content" :class="{ 'login-content-mobile': tabsActiveName === 'mobile' }">
<div class="login-content">
<div class="login-content-main">
<h4 class="login-content-title">{{ getThemeConfig.globalTitle }}后台模板</h4>
<h4 class="login-content-title ml15">{{ getThemeConfig.globalTitle }}后台模板</h4>
<div v-if="!isScan">
<el-tabs v-model="tabsActiveName" @tab-click="onTabsClick">
<el-tab-pane label="账号密码登录" name="account" :disabled="tabsActiveName === 'account'">
<transition name="el-zoom-in-center">
<Account v-show="isTabPaneShow" />
</transition>
<el-tabs v-model="tabsActiveName">
<el-tab-pane label="账号密码登录" name="account">
<Account />
</el-tab-pane>
<el-tab-pane label="手机号登录" name="mobile" :disabled="tabsActiveName === 'mobile'">
<transition name="el-zoom-in-center">
<Mobile v-show="!isTabPaneShow" />
</transition>
<el-tab-pane label="手机号登录" name="mobile">
<Mobile />
</el-tab-pane>
</el-tabs>
<div class="mt10">
<el-button type="text" size="small">第三方登录</el-button>
<el-button type="text" size="small">友情链接</el-button>
</div>
</div>
<Scan v-else />
<Scan v-if="isScan" />
<div class="login-content-main-sacn" @click="isScan = !isScan">
<i class="iconfont" :class="isScan ? 'icon-diannao1' : 'icon-barcode-qr'"></i>
<div class="login-content-main-sacn-delta"></div>
</div>
</div>
</div>
<div class="login-copyright">
<div class="mb5 login-copyright-company">版权所有深圳市xxx软件科技有限公司</div>
<div class="login-copyright-msg">Copyright: Shenzhen XXX Software Technology 粤ICP备05010000号</div>
</div>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, computed } from 'vue';
import { toRefs, reactive, computed, defineComponent } from 'vue';
import Account from '/@/views/login/component/account.vue';
import Mobile from '/@/views/login/component/mobile.vue';
import Scan from '/@/views/login/component/scan.vue';
import { useStore } from '/@/store/index';
export default {
name: 'login',
import logoMini from '/@/assets/logo-mini.svg';
import loginIconTwo from '/@/assets/login-icon-two.svg';
// 定义接口来定义对象的类型
interface LoginState {
tabsActiveName: string;
isScan: boolean;
}
export default defineComponent({
name: 'loginIndex',
components: { Account, Mobile, Scan },
setup() {
const store = useStore();
const state = reactive({
const state = reactive<LoginState>({
tabsActiveName: 'account',
isTabPaneShow: true,
isScan: false,
});
// 获取布局配置信息
const getThemeConfig = computed(() => {
return store.state.themeConfig.themeConfig;
});
// 切换密码、手机登录
const onTabsClick = () => {
state.isTabPaneShow = !state.isTabPaneShow;
};
return {
onTabsClick,
logoMini,
loginIconTwo,
getThemeConfig,
...toRefs(state),
};
},
};
});
</script>
<style scoped lang="scss">
.login-container {
width: 100%;
height: 100%;
background: url('https://gitee.com/lyt-top/vue-next-admin-images/raw/master/login/bg-login.png') no-repeat;
background-size: 100% 100%;
.login-logo {
position: absolute;
top: 30px;
left: 50%;
height: 50px;
display: flex;
align-items: center;
font-size: 20px;
color: var(--color-primary);
letter-spacing: 2px;
width: 90%;
transform: translateX(-50%);
position: relative;
background: var(--el-color-white);
.login-icon-group {
width: 100%;
height: 100%;
position: relative;
.login-icon-group-title {
position: absolute;
top: 50px;
left: 80px;
display: flex;
align-items: center;
img {
width: 30px;
height: 30px;
}
&-text {
padding-left: 15px;
color: var(--el-color-primary);
}
}
&::before {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 60%;
overflow: hidden;
height: 80%;
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='1200' height='770' xmlns='http://www.w3.org/2000/svg' fill='none'%3E%3Cg%3E%3Cpath id='svg_1' d='M58.4 47.77C104.6 59.51 135.26 67.37 162.11 78.04C188.97 88.72 226.33 102.69 265.92 123.55C305.51 144.4 366.96 167.09 441.43 121.52C515.9 75.95 546.48 61.01 577.69 46.27C608.9 31.53 625.86 23.69 680.26 12.28C734.65 0.87 837.29 10.7 867.29 21.8C897.29 32.9 935.51 51.9 962.21 95.45C988.9 139.01 972.91 177.36 951.37 221.39C929.83 265.43 883.49 306 890.44 337.33C897.4 368.66 974.73 412.18 974.73 411.47C974.73 412.18 1066.36 457.62 1106.36 491.06C1146.36 524.5 1178.8 563.36 1184.03 579.63C1189.26 595.9 1200.4 622.49 1181.55 676.88C1162.71 731.26 1127.16 764.32 1115.31 778.64C1103.45 792.96 5.34 783.61 4.32 784.63C3.3 785.65 -172.34 2.38 1.13 35.04L58.4 47.77L58.4 47.77Z' fill='%23409eff'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
background: var(--el-color-primary-light-5);
transition: all 0.3s ease;
}
&::after {
content: '';
width: 150px;
height: 300px;
position: absolute;
right: 0;
top: 0;
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='150' height='300' xmlns='http://www.w3.org/2000/svg' fill='none'%3E%3Cg%3E%3Cpath id='svg_1' d='M-0.56 -0.28C41.94 36.17 67.73 18.94 93.33 33.96C118.93 48.98 107.58 73.56 101.94 89.76C96.29 105.96 50.09 217.83 47.87 231.18C45.64 244.52 46.02 255.2 64.4 270.05C82.79 284.91 121.99 292.31 111.98 289.81C101.97 287.32 153.96 301.48 151.83 299.9C149.69 298.32 149.98 -1.36 149.71 -1.18C149.98 -1.36 -43.06 -36.74 -0.56 -0.28L-0.56 -0.28Z' fill='%23409eff'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
background: var(--el-color-primary-light-5);
transition: all 0.3s ease;
}
&-icon {
width: 60%;
height: 70%;
position: absolute;
left: 0;
bottom: 0;
}
}
.login-content {
width: 500px;
padding: 20px;
position: absolute;
right: 200px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) translate3d(0, 0, 0);
background-color: rgba(255, 255, 255, 0.99);
border: 5px solid var(--color-primary-light-8);
border-radius: 4px;
transition: height 0.2s linear;
height: 480px;
transform: translateY(-50%) translate3d(0, 0, 0);
background-color: var(--el-color-white);
border: 5px solid var(--el-color-primary-light-8);
border-radius: 5px;
overflow: hidden;
z-index: 1;
height: 460px;
.login-content-main {
margin: 0 auto;
width: 80%;
.login-content-title {
color: #333;
color: var(--el-text-color-primary);
font-weight: 500;
font-size: 22px;
text-align: center;
@ -129,6 +163,7 @@ export default {
overflow: hidden;
cursor: pointer;
transition: all ease 0.3s;
color: var(--el-text-color-primary);
&-delta {
position: absolute;
width: 35px;
@ -142,7 +177,7 @@ export default {
&:hover {
opacity: 1;
transition: all ease 0.3s;
color: var(--color-primary);
color: var(--el-color-primary) !important;
}
i {
width: 47px;
@ -155,24 +190,5 @@ export default {
}
}
}
.login-content-mobile {
height: 418px;
}
.login-copyright {
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 30px;
text-align: center;
color: var(--color-whites);
font-size: 12px;
opacity: 0.8;
.login-copyright-company {
white-space: nowrap;
}
.login-copyright-msg {
@extend .login-copyright-company;
}
}
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="system-add-dept-container">
<el-dialog title="新增部门" v-model="isShowDialog" width="769px">
<el-form :model="ruleForm" size="small" label-width="90px">
<el-form :model="ruleForm" size="default" label-width="90px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="上级部门">
@ -59,8 +59,8 @@
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="small"> </el-button>
<el-button type="primary" @click="onSubmit" size="small"> </el-button>
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
@ -68,11 +68,37 @@
</template>
<script lang="ts">
import { reactive, toRefs, onMounted } from 'vue';
export default {
import { reactive, toRefs, onMounted, defineComponent } from 'vue';
// 定义接口来定义对象的类型
interface TableDataRow {
deptName: string;
createTime: string;
status: boolean;
sort: number;
describe: string;
id: number;
children?: TableDataRow[];
}
interface DeptSate {
isShowDialog: boolean;
ruleForm: {
deptLevel: Array<string>;
deptName: string;
person: string;
phone: string | number;
email: string;
sort: number;
status: boolean;
describe: string;
};
deptData: Array<TableDataRow>;
}
export default defineComponent({
name: 'systemAddDept',
setup() {
const state = reactive({
const state = reactive<DeptSate>({
isShowDialog: false,
ruleForm: {
deptLevel: [], // 上级部门
@ -108,7 +134,7 @@ export default {
deptName: 'vueNextAdmin',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '顶级部门',
id: Math.random(),
children: [
@ -116,7 +142,7 @@ export default {
deptName: 'IT外包服务',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '总部',
id: Math.random(),
},
@ -124,7 +150,7 @@ export default {
deptName: '资本控股',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '分部',
id: Math.random(),
},
@ -143,5 +169,5 @@ export default {
...toRefs(state),
};
},
};
});
</script>

View File

@ -1,7 +1,7 @@
<template>
<div class="system-edit-dept-container">
<el-dialog title="修改部门" v-model="isShowDialog" width="769px">
<el-form :model="ruleForm" size="small" label-width="90px">
<el-form :model="ruleForm" size="default" label-width="90px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="上级部门">
@ -59,8 +59,8 @@
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="small"> </el-button>
<el-button type="primary" @click="onSubmit" size="small"> </el-button>
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
@ -68,11 +68,38 @@
</template>
<script lang="ts">
import { reactive, toRefs, onMounted } from 'vue';
export default {
import { reactive, toRefs, onMounted, defineComponent } from 'vue';
// 定义接口来定义对象的类型
interface TableDataRow {
deptName: string;
createTime: string;
status: boolean;
sort: number;
describe: string;
id: number;
children?: TableDataRow[];
}
interface RuleFormState {
deptLevel: Array<string>;
deptName: string;
person: string;
phone: string | number;
email: string;
sort: number;
status: boolean;
describe: string;
}
interface DeptSate {
isShowDialog: boolean;
ruleForm: RuleFormState;
deptData: Array<TableDataRow>;
}
export default defineComponent({
name: 'systemEditDept',
setup() {
const state = reactive({
const state = reactive<DeptSate>({
isShowDialog: false,
ruleForm: {
deptLevel: [], // 上级部门
@ -87,7 +114,7 @@ export default {
deptData: [], // 部门数据
});
// 打开弹窗
const openDialog = (row: Object) => {
const openDialog = (row: RuleFormState) => {
row.deptLevel = ['vueNextAdmin'];
row.person = 'lyt';
row.phone = '12345678910';
@ -113,7 +140,7 @@ export default {
deptName: 'vueNextAdmin',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '顶级部门',
id: Math.random(),
children: [
@ -121,7 +148,7 @@ export default {
deptName: 'IT外包服务',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '总部',
id: Math.random(),
},
@ -129,7 +156,7 @@ export default {
deptName: '资本控股',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '分部',
id: Math.random(),
},
@ -148,5 +175,5 @@ export default {
...toRefs(state),
};
},
};
});
</script>

View File

@ -2,16 +2,16 @@
<div class="system-dept-container">
<el-card shadow="hover">
<div class="system-dept-search mb15">
<el-input size="small" placeholder="请输入部门名称" style="max-width: 180px"> </el-input>
<el-button size="small" type="primary" class="ml10">
<el-input size="default" placeholder="请输入部门名称" style="max-width: 180px"> </el-input>
<el-button size="default" type="primary" class="ml10">
<el-icon>
<elementSearch />
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="small" type="success" class="ml10" @click="onOpenAddDept">
<el-button size="default" type="success" class="ml10" @click="onOpenAddDept">
<el-icon>
<elementFolderAdd />
<ele-FolderAdd />
</el-icon>
新增部门
</el-button>
@ -39,9 +39,9 @@
<el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" show-overflow-tooltip width="140">
<template #default="scope">
<el-button size="mini" type="text" @click="onOpenAddDept(scope.row)">新增</el-button>
<el-button size="mini" type="text" @click="onOpenEditDept(scope.row)">修改</el-button>
<el-button size="mini" type="text" @click="onTabelRowDel(scope.row)">删除</el-button>
<el-button size="small" type="text" @click="onOpenAddDept">新增</el-button>
<el-button size="small" type="text" @click="onOpenEditDept(scope.row)">修改</el-button>
<el-button size="small" type="text" @click="onTabelRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
@ -52,17 +52,40 @@
</template>
<script lang="ts">
import { ref, toRefs, reactive, onMounted } from 'vue';
import { ref, toRefs, reactive, onMounted, defineComponent } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import AddDept from '/@/views/system/dept/component/addDept.vue';
import EditDept from '/@/views/system/dept/component/editDept.vue';
export default {
// 定义接口来定义对象的类型
interface TableDataRow {
deptName: string;
createTime: string;
status: boolean;
sort: number;
describe: string;
id: number;
children?: TableDataRow[];
}
interface TableDataState {
tableData: {
data: Array<TableDataRow>;
total: number;
loading: boolean;
param: {
pageNum: number;
pageSize: number;
};
};
}
export default defineComponent({
name: 'systemDept',
components: { AddDept, EditDept },
setup() {
const addDeptRef = ref();
const editDeptRef = ref();
const state = reactive({
const state = reactive<TableDataState>({
tableData: {
data: [],
total: 0,
@ -79,7 +102,7 @@ export default {
deptName: 'vueNextAdmin',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '顶级部门',
id: Math.random(),
children: [
@ -87,7 +110,7 @@ export default {
deptName: 'IT外包服务',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '总部',
id: Math.random(),
},
@ -95,7 +118,7 @@ export default {
deptName: '资本控股',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '分部',
id: Math.random(),
},
@ -108,11 +131,11 @@ export default {
addDeptRef.value.openDialog();
};
// 打开编辑菜单弹窗
const onOpenEditDept = (row: object) => {
const onOpenEditDept = (row: TableDataRow) => {
editDeptRef.value.openDialog(row);
};
// 删除当前行
const onTabelRowDel = (row: object) => {
const onTabelRowDel = (row: TableDataRow) => {
ElMessageBox.confirm(`此操作将永久删除部门:${row.deptName}, 是否继续?`, '提示', {
confirmButtonText: '删除',
cancelButtonText: '取消',
@ -136,5 +159,5 @@ export default {
...toRefs(state),
};
},
};
});
</script>

View File

@ -2,7 +2,7 @@
<div class="system-add-dic-container">
<el-dialog title="新增字典" v-model="isShowDialog" width="769px">
<el-alert title="半成品,交互过于复杂,请自行扩展!" type="warning" :closable="false" class="mb20"> </el-alert>
<el-form :model="ruleForm" size="small" label-width="90px">
<el-form :model="ruleForm" size="default" label-width="90px">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="字典名称">
@ -24,14 +24,14 @@
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item :prop="`list[${k}].label`">
<template #label>
<el-button type="primary" circle size="mini" @click="onAddRow" v-if="k === 0">
<el-button type="primary" circle size="small" @click="onAddRow" v-if="k === 0">
<el-icon>
<elementPlus />
<ele-Plus />
</el-icon>
</el-button>
<el-button type="danger" circle size="mini" @click="onDelRow(k)" v-else>
<el-button type="danger" circle size="small" @click="onDelRow(k)" v-else>
<el-icon>
<elementDelete />
<ele-Delete />
</el-icon>
</el-button>
<span class="ml10">字段</span>
@ -55,8 +55,8 @@
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="small"> </el-button>
<el-button type="primary" @click="onSubmit" size="small"> </el-button>
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
@ -64,8 +64,8 @@
</template>
<script lang="ts">
import { reactive, toRefs } from 'vue';
export default {
import { reactive, toRefs, defineComponent } from 'vue';
export default defineComponent({
name: 'systemAddDic',
setup() {
const state = reactive({
@ -111,7 +111,7 @@ export default {
});
};
// 删除行
const onDelRow = (k) => {
const onDelRow = (k: number) => {
state.ruleForm.list.splice(k, 1);
};
return {
@ -124,5 +124,5 @@ export default {
...toRefs(state),
};
},
};
});
</script>

View File

@ -2,7 +2,7 @@
<div class="system-edit-dic-container">
<el-dialog title="修改字典" v-model="isShowDialog" width="769px">
<el-alert title="半成品,交互过于复杂,请自行扩展!" type="warning" :closable="false" class="mb20"> </el-alert>
<el-form :model="ruleForm" size="small" label-width="90px">
<el-form :model="ruleForm" size="default" label-width="90px">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="字典名称">
@ -24,14 +24,14 @@
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item :prop="`list[${k}].label`">
<template #label>
<el-button type="primary" circle size="mini" @click="onAddRow" v-if="k === 0">
<el-button type="primary" circle size="small" @click="onAddRow" v-if="k === 0">
<el-icon>
<elementPlus />
<ele-Plus />
</el-icon>
</el-button>
<el-button type="danger" circle size="mini" @click="onDelRow(k)" v-else>
<el-button type="danger" circle size="small" @click="onDelRow(k)" v-else>
<el-icon>
<elementDelete />
<ele-Delete />
</el-icon>
</el-button>
<span class="ml10">字段</span>
@ -55,8 +55,8 @@
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="small"> </el-button>
<el-button type="primary" @click="onSubmit" size="small"> </el-button>
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
@ -64,11 +64,31 @@
</template>
<script lang="ts">
import { reactive, toRefs } from 'vue';
export default {
import { reactive, toRefs, defineComponent } from 'vue';
// 定义接口来定义对象的类型
interface RuleFormList {
id: number;
label: string;
value: string;
}
interface RuleFormState {
dicName: string;
fieldName: string;
status: boolean;
list: RuleFormList[];
describe: string;
fieldNameList: Array<any>;
}
interface DicState {
isShowDialog: boolean;
ruleForm: RuleFormState;
}
export default defineComponent({
name: 'systemEditDic',
setup() {
const state = reactive({
const state = reactive<DicState>({
isShowDialog: false,
ruleForm: {
dicName: '', // 字典名称
@ -87,7 +107,7 @@ export default {
},
});
// 打开弹窗
const openDialog = (row: Object) => {
const openDialog = (row: RuleFormState) => {
if (row.fieldName === 'SYS_UERINFO') {
row.list = [
{ id: Math.random(), label: 'sex', value: '1' },
@ -125,7 +145,7 @@ export default {
});
};
// 删除行
const onDelRow = (k) => {
const onDelRow = (k: number) => {
state.ruleForm.list.splice(k, 1);
};
return {
@ -138,5 +158,5 @@ export default {
...toRefs(state),
};
},
};
});
</script>

View File

@ -2,16 +2,16 @@
<div class="system-dic-container">
<el-card shadow="hover">
<div class="system-user-search mb15">
<el-input size="small" placeholder="请输入字典名称" style="max-width: 180px"> </el-input>
<el-button size="small" type="primary" class="ml10">
<el-input size="default" placeholder="请输入字典名称" style="max-width: 180px"> </el-input>
<el-button size="default" type="primary" class="ml10">
<el-icon>
<elementSearch />
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="small" type="success" class="ml10" @click="onOpenAddDic">
<el-button size="default" type="success" class="ml10" @click="onOpenAddDic">
<el-icon>
<elementFolderAdd />
<ele-FolderAdd />
</el-icon>
新增字典
</el-button>
@ -30,8 +30,8 @@
<el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="100">
<template #default="scope">
<el-button size="mini" type="text" @click="onOpenEditDic(scope.row)">修改</el-button>
<el-button size="mini" type="text" @click="onRowDel(scope.row)">删除</el-button>
<el-button size="small" type="text" @click="onOpenEditDic(scope.row)">修改</el-button>
<el-button size="small" type="text" @click="onRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
@ -55,17 +55,38 @@
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, ref } from 'vue';
import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import AddDic from '/@/views/system/dic/component/addDic.vue';
import EditDic from '/@/views/system/dic/component/editDic.vue';
export default {
// 定义接口来定义对象的类型
interface TableDataRow {
dicName: string;
fieldName: string;
describe: string;
status: boolean;
createTime: string;
}
interface TableDataState {
tableData: {
data: Array<TableDataRow>;
total: number;
loading: boolean;
param: {
pageNum: number;
pageSize: number;
};
};
}
export default defineComponent({
name: 'systemDic',
components: { AddDic, EditDic },
setup() {
const addDicRef = ref();
const editDicRef = ref();
const state: any = reactive({
const state = reactive<TableDataState>({
tableData: {
data: [],
total: 0,
@ -78,7 +99,7 @@ export default {
});
// 初始化表格数据
const initTableData = () => {
const data: Array<object> = [];
const data: Array<TableDataRow> = [];
for (let i = 0; i < 2; i++) {
data.push({
dicName: i === 0 ? '角色标识' : '用户性别',
@ -96,11 +117,11 @@ export default {
addDicRef.value.openDialog();
};
// 打开修改字典弹窗
const onOpenEditDic = (row: Object) => {
const onOpenEditDic = (row: TableDataRow) => {
editDicRef.value.openDialog(row);
};
// 删除字典
const onRowDel = (row: Object) => {
const onRowDel = (row: TableDataRow) => {
ElMessageBox.confirm(`此操作将永久删除字典名称:“${row.dicName}”,是否继续?`, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
@ -134,10 +155,5 @@ export default {
...toRefs(state),
};
},
};
});
</script>
<style scoped lang="scss">
.system-dic-container {
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="system-add-menu-container">
<el-dialog title="新增菜单" v-model="isShowDialog" width="769px">
<el-form :model="ruleForm" size="small" label-width="80px">
<el-form :model="ruleForm" size="default" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="上级菜单">
@ -61,12 +61,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="ruleForm.meta.isLink"
placeholder="外链/内嵌时链接地址http:xxx.com"
clearable
:disabled="ruleForm.isLink === '' || !ruleForm.isLink"
>
<el-input v-model="ruleForm.meta.isLink" placeholder="外链/内嵌时链接地址http:xxx.com" clearable :disabled="!ruleForm.isLink">
</el-input>
</el-form-item>
</el-col>
@ -137,8 +132,8 @@
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="small"> </el-button>
<el-button type="primary" @click="onSubmit" size="small"> </el-button>
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
@ -146,11 +141,11 @@
</template>
<script lang="ts">
import { reactive, toRefs, onMounted } from 'vue';
import { reactive, toRefs, onMounted, defineComponent } from 'vue';
import { useStore } from '/@/store/index';
import IconSelector from '/@/components/iconSelector/index.vue';
// import { setBackEndControlRefreshRoutes } from "/@/router/backEnd";
export default {
export default defineComponent({
name: 'systemAddMenu',
components: { IconSelector },
setup() {
@ -182,9 +177,9 @@ export default {
menuData: [], // 上级菜单数据
});
// 获取 vuex 中的路由
const getMenuData = (routes) => {
const arr = [];
routes.map((val) => {
const getMenuData = (routes: any) => {
const arr: any = [];
routes.map((val: any) => {
val['title'] = val.meta.title;
val['id'] = Math.random();
arr.push({ ...val });
@ -202,11 +197,8 @@ export default {
};
// 是否内嵌下拉改变
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 = () => {
@ -230,5 +222,5 @@ export default {
...toRefs(state),
};
},
};
});
</script>

View File

@ -1,7 +1,7 @@
<template>
<div class="system-edit-menu-container">
<el-dialog title="修改菜单" v-model="isShowDialog" width="769px">
<el-form :model="ruleForm" size="small" label-width="80px">
<el-form :model="ruleForm" size="default" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="上级菜单">
@ -61,12 +61,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="ruleForm.meta.isLink"
placeholder="外链/内嵌时链接地址http:xxx.com"
clearable
:disabled="ruleForm.isLink === '' || !ruleForm.isLink"
>
<el-input v-model="ruleForm.meta.isLink" placeholder="外链/内嵌时链接地址http:xxx.com" clearable :disabled="!ruleForm.isLink">
</el-input>
</el-form-item>
</el-col>
@ -137,8 +132,8 @@
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="small"> </el-button>
<el-button type="primary" @click="onSubmit" size="small"> </el-button>
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
@ -146,11 +141,11 @@
</template>
<script lang="ts">
import { reactive, toRefs, onMounted } from 'vue';
import { reactive, toRefs, onMounted, defineComponent } from 'vue';
import { useStore } from '/@/store/index';
import IconSelector from '/@/components/iconSelector/index.vue';
// import { setBackEndControlRefreshRoutes } from "/@/router/backEnd";
export default {
export default defineComponent({
name: 'systemEditMenu',
components: { IconSelector },
setup() {
@ -182,9 +177,9 @@ export default {
menuData: [], // 上级菜单数据
});
// 获取 vuex 中的路由
const getMenuData = (routes) => {
const arr = [];
routes.map((val) => {
const getMenuData = (routes: any) => {
const arr: any = [];
routes.map((val: any) => {
val['title'] = val.meta.title;
val['id'] = Math.random();
arr.push({ ...val });
@ -193,7 +188,7 @@ export default {
return arr;
};
// 打开弹窗
const openDialog = (row: Object) => {
const openDialog = (row: any) => {
row.menuType = 'menu';
row.menuSort = Math.random();
state.ruleForm = row;
@ -205,11 +200,8 @@ export default {
};
// 是否内嵌下拉改变
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 = () => {
@ -233,5 +225,5 @@ export default {
...toRefs(state),
};
},
};
});
</script>

View File

@ -2,16 +2,16 @@
<div class="system-menu-container">
<el-card shadow="hover">
<div class="system-menu-search mb15">
<el-input size="small" placeholder="请输入菜单名称" style="max-width: 180px"> </el-input>
<el-button size="small" type="primary" class="ml10">
<el-input size="default" placeholder="请输入菜单名称" style="max-width: 180px"> </el-input>
<el-button size="default" type="primary" class="ml10">
<el-icon>
<elementSearch />
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="small" type="success" class="ml10" @click="onOpenAddMenu">
<el-button size="default" type="success" class="ml10" @click="onOpenAddMenu">
<el-icon>
<elementFolderAdd />
<ele-FolderAdd />
</el-icon>
新增菜单
</el-button>
@ -46,9 +46,9 @@
</el-table-column>
<el-table-column label="操作" show-overflow-tooltip width="140">
<template #default="scope">
<el-button size="mini" type="text" @click="onOpenAddMenu(scope.row)">新增</el-button>
<el-button size="mini" type="text" @click="onOpenEditMenu(scope.row)">修改</el-button>
<el-button size="mini" type="text" @click="onTabelRowDel(scope.row)">删除</el-button>
<el-button size="small" type="text" @click="onOpenAddMenu">新增</el-button>
<el-button size="small" type="text" @click="onOpenEditMenu(scope.row)">修改</el-button>
<el-button size="small" type="text" @click="onTabelRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
@ -59,12 +59,13 @@
</template>
<script lang="ts">
import { ref, toRefs, reactive, computed } from 'vue';
import { ref, toRefs, reactive, computed, defineComponent } from 'vue';
import { RouteRecordRaw } from 'vue-router';
import { ElMessageBox, ElMessage } from 'element-plus';
import { useStore } from '/@/store/index';
import AddMenu from '/@/views/system/menu/component/addMenu.vue';
import EditMenu from '/@/views/system/menu/component/editMenu.vue';
export default {
export default defineComponent({
name: 'systemMenu',
components: { AddMenu, EditMenu },
setup() {
@ -81,11 +82,11 @@ export default {
addMenuRef.value.openDialog();
};
// 打开编辑菜单弹窗
const onOpenEditMenu = (row: object) => {
const onOpenEditMenu = (row: RouteRecordRaw) => {
editMenuRef.value.openDialog(row);
};
// 删除当前行
const onTabelRowDel = (row: object) => {
const onTabelRowDel = (row: RouteRecordRaw) => {
ElMessageBox.confirm(`此操作将永久删除路由:${row.path}, 是否继续?`, '提示', {
confirmButtonText: '删除',
cancelButtonText: '取消',
@ -106,5 +107,5 @@ export default {
...toRefs(state),
};
},
};
});
</script>

View File

@ -1,7 +1,7 @@
<template>
<div class="system-add-role-container">
<el-dialog title="新增角色" v-model="isShowDialog" width="769px">
<el-form :model="ruleForm" size="small" label-width="90px">
<el-form :model="ruleForm" size="default" label-width="90px">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="角色名称">
@ -42,8 +42,8 @@
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="small"> </el-button>
<el-button type="primary" @click="onSubmit" size="small"> </el-button>
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
@ -51,11 +51,34 @@
</template>
<script lang="ts">
import { reactive, toRefs } from 'vue';
export default {
import { reactive, toRefs, defineComponent } from 'vue';
// 定义接口来定义对象的类型
interface MenuDataTree {
id: number;
label: string;
children?: MenuDataTree[];
}
interface RoleState {
isShowDialog: boolean;
ruleForm: {
roleName: string;
roleSign: string;
sort: number;
status: boolean;
describe: string;
};
menuData: Array<MenuDataTree>;
menuProps: {
children: string;
label: string;
};
}
export default defineComponent({
name: 'systemAddRole',
setup() {
const state = reactive({
const state = reactive<RoleState>({
isShowDialog: false,
ruleForm: {
roleName: '', // 角色名称
@ -202,7 +225,7 @@ export default {
...toRefs(state),
};
},
};
});
</script>
<style scoped lang="scss">

View File

@ -1,7 +1,7 @@
<template>
<div class="system-edit-role-container">
<el-dialog title="修改角色" v-model="isShowDialog" width="769px">
<el-form :model="ruleForm" size="small" label-width="90px">
<el-form :model="ruleForm" size="default" label-width="90px">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="角色名称">
@ -42,8 +42,8 @@
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="small"> </el-button>
<el-button type="primary" @click="onSubmit" size="small"> </el-button>
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
@ -51,11 +51,35 @@
</template>
<script lang="ts">
import { reactive, toRefs } from 'vue';
export default {
import { reactive, toRefs, defineComponent } from 'vue';
// 定义接口来定义对象的类型
interface MenuDataTree {
id: number;
label: string;
children?: MenuDataTree[];
}
interface DialogRow {
roleName: string;
roleSign: string;
sort: number;
status: boolean;
describe: string;
}
interface RoleState {
isShowDialog: boolean;
ruleForm: DialogRow;
menuData: Array<MenuDataTree>;
menuProps: {
children: string;
label: string;
};
}
export default defineComponent({
name: 'systemEditRole',
setup() {
const state = reactive({
const state = reactive<RoleState>({
isShowDialog: false,
ruleForm: {
roleName: '', // 角色名称
@ -71,7 +95,7 @@ export default {
},
});
// 打开弹窗
const openDialog = (row: Object) => {
const openDialog = (row: DialogRow) => {
state.ruleForm = row;
state.isShowDialog = true;
getMenuData();
@ -203,7 +227,7 @@ export default {
...toRefs(state),
};
},
};
});
</script>
<style scoped lang="scss">

View File

@ -2,22 +2,22 @@
<div class="system-role-container">
<el-card shadow="hover">
<div class="system-user-search mb15">
<el-input size="small" placeholder="请输入角色名称" style="max-width: 180px"> </el-input>
<el-button size="small" type="primary" class="ml10">
<el-input size="default" placeholder="请输入角色名称" style="max-width: 180px"> </el-input>
<el-button size="default" type="primary" class="ml10">
<el-icon>
<elementSearch />
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="small" type="success" class="ml10" @click="onOpenAddRole">
<el-button size="default" type="success" class="ml10" @click="onOpenAddRole">
<el-icon>
<elementFolderAdd />
<ele-FolderAdd />
</el-icon>
新增角色
</el-button>
</div>
<el-table :data="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>
@ -31,8 +31,8 @@
<el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="100">
<template #default="scope">
<el-button :disabled="scope.row.roleName === '超级管理员'" size="mini" type="text" @click="onOpenEditRole(scope.row)">修改</el-button>
<el-button :disabled="scope.row.roleName === '超级管理员'" size="mini" type="text" @click="onRowDel(scope.row)">删除</el-button>
<el-button :disabled="scope.row.roleName === '超级管理员'" size="small" type="text" @click="onOpenEditRole(scope.row)">修改</el-button>
<el-button :disabled="scope.row.roleName === '超级管理员'" size="small" type="text" @click="onRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
@ -56,17 +56,39 @@
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, ref } from 'vue';
import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import AddRole from '/@/views/system/role/component/addRole.vue';
import EditRole from '/@/views/system/role/component/editRole.vue';
export default {
// 定义接口来定义对象的类型
interface TableData {
roleName: string;
roleSign: string;
describe: string;
sort: number;
status: boolean;
createTime: string;
}
interface TableDataState {
tableData: {
data: Array<TableData>;
total: number;
loading: boolean;
param: {
pageNum: number;
pageSize: number;
};
};
}
export default defineComponent({
name: 'systemRole',
components: { AddRole, EditRole },
setup() {
const addRoleRef = ref();
const editRoleRef = ref();
const state: any = reactive({
const state = reactive<TableDataState>({
tableData: {
data: [],
total: 0,
@ -79,7 +101,7 @@ export default {
});
// 初始化表格数据
const initTableData = () => {
const data: Array<object> = [];
const data: Array<TableData> = [];
for (let i = 0; i < 2; i++) {
data.push({
roleName: i === 0 ? '超级管理员' : '普通用户',
@ -102,7 +124,7 @@ export default {
editRoleRef.value.openDialog(row);
};
// 删除角色
const onRowDel = (row: Object) => {
const onRowDel = (row: any) => {
ElMessageBox.confirm(`此操作将永久删除角色名称:“${row.roleName}”,是否继续?`, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
@ -136,10 +158,5 @@ export default {
...toRefs(state),
};
},
};
});
</script>
<style scoped lang="scss">
.system-role-container {
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="system-add-user-container">
<el-dialog title="新增用户" v-model="isShowDialog" width="769px">
<el-form :model="ruleForm" size="small" label-width="90px">
<el-form :model="ruleForm" size="default" label-width="90px">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="账户名称">
@ -80,8 +80,8 @@
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="small"> </el-button>
<el-button type="primary" @click="onSubmit" size="small"> </el-button>
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
@ -89,11 +89,40 @@
</template>
<script lang="ts">
import { reactive, toRefs, onMounted } from 'vue';
export default {
import { reactive, toRefs, onMounted, defineComponent } from 'vue';
// 定义接口来定义对象的类型
interface DeptData {
deptName: string;
createTime: string;
status: boolean;
sort: number | string;
describe: string;
id: number;
children?: DeptData[];
}
interface UserState {
isShowDialog: boolean;
ruleForm: {
userName: string;
userNickname: string;
roleSign: string;
department: any;
phone: string;
email: string;
sex: string;
password: string;
overdueTime: string;
status: boolean;
describe: string;
};
deptData: Array<DeptData>;
}
export default defineComponent({
name: 'systemAddUser',
setup() {
const state = reactive({
const state = reactive<UserState>({
isShowDialog: false,
ruleForm: {
userName: '', // 账户名称
@ -132,7 +161,7 @@ export default {
deptName: 'vueNextAdmin',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '顶级部门',
id: Math.random(),
children: [
@ -140,7 +169,7 @@ export default {
deptName: 'IT外包服务',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '总部',
id: Math.random(),
},
@ -148,7 +177,7 @@ export default {
deptName: '资本控股',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '分部',
id: Math.random(),
},
@ -167,5 +196,5 @@ export default {
...toRefs(state),
};
},
};
});
</script>

View File

@ -1,7 +1,7 @@
<template>
<div class="system-edit-user-container">
<el-dialog title="修改用户" v-model="isShowDialog" width="769px">
<el-form :model="ruleForm" size="small" label-width="90px">
<el-form :model="ruleForm" size="default" label-width="90px">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="账户名称">
@ -80,8 +80,8 @@
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="small"> </el-button>
<el-button type="primary" @click="onSubmit" size="small"> </el-button>
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
@ -89,11 +89,41 @@
</template>
<script lang="ts">
import { reactive, toRefs, onMounted } from 'vue';
export default {
import { reactive, toRefs, onMounted, defineComponent } from 'vue';
// 定义接口来定义对象的类型
interface DeptData {
deptName: string;
createTime: string;
status: boolean;
sort: number | string;
describe: string;
id: number;
children?: DeptData[];
}
interface RuleFormRow {
userName: string;
userNickname: string;
roleSign: string;
department: any;
phone: string;
email: string;
sex: string;
password: string;
overdueTime: string;
status: boolean;
describe: string;
}
interface UserState {
isShowDialog: boolean;
ruleForm: RuleFormRow;
deptData: Array<DeptData>;
}
export default defineComponent({
name: 'systemEditUser',
setup() {
const state = reactive({
const state = reactive<UserState>({
isShowDialog: false,
ruleForm: {
userName: '', // 账户名称
@ -111,7 +141,7 @@ export default {
deptData: [], // 部门数据
});
// 打开弹窗
const openDialog = (row: Object) => {
const openDialog = (row: RuleFormRow) => {
state.ruleForm = row;
state.isShowDialog = true;
};
@ -133,7 +163,7 @@ export default {
deptName: 'vueNextAdmin',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '顶级部门',
id: Math.random(),
children: [
@ -141,7 +171,7 @@ export default {
deptName: 'IT外包服务',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '总部',
id: Math.random(),
},
@ -149,7 +179,7 @@ export default {
deptName: '资本控股',
createTime: new Date().toLocaleString(),
status: true,
sort: Number.parseInt(Math.random()),
sort: Math.random(),
describe: '分部',
id: Math.random(),
},
@ -168,5 +198,5 @@ export default {
...toRefs(state),
};
},
};
});
</script>

View File

@ -2,22 +2,22 @@
<div class="system-user-container">
<el-card shadow="hover">
<div class="system-user-search mb15">
<el-input size="small" placeholder="请输入用户名称" style="max-width: 180px"> </el-input>
<el-button size="small" type="primary" class="ml10">
<el-input size="default" placeholder="请输入用户名称" style="max-width: 180px"> </el-input>
<el-button size="default" type="primary" class="ml10">
<el-icon>
<elementSearch />
<ele-Search />
</el-icon>
查询
</el-button>
<el-button size="small" type="success" class="ml10" @click="onOpenAddUser">
<el-button size="default" type="success" class="ml10" @click="onOpenAddUser">
<el-icon>
<elementFolderAdd />
<ele-FolderAdd />
</el-icon>
新增用户
</el-button>
</div>
<el-table :data="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>
@ -34,8 +34,8 @@
<el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="100">
<template #default="scope">
<el-button :disabled="scope.row.userName === 'admin'" size="mini" type="text" @click="onOpenEditUser(scope.row)">修改</el-button>
<el-button :disabled="scope.row.userName === 'admin'" size="mini" type="text" @click="onRowDel(scope.row)">删除</el-button>
<el-button :disabled="scope.row.userName === 'admin'" size="small" type="text" @click="onOpenEditUser(scope.row)">修改</el-button>
<el-button :disabled="scope.row.userName === 'admin'" size="small" type="text" @click="onRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
@ -59,17 +59,45 @@
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, ref } from 'vue';
import { toRefs, reactive, onMounted, ref, defineComponent } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import AddUer from '/@/views/system/user/component/addUser.vue';
import EditUser from '/@/views/system/user/component/editUser.vue';
export default {
// 定义接口来定义对象的类型
interface TableDataRow {
userName: string;
userNickname: string;
roleSign: string;
department: string[];
phone: string;
email: string;
sex: string;
password: string;
overdueTime: Date;
status: boolean;
describe: string;
createTime: string;
}
interface TableDataState {
tableData: {
data: Array<TableDataRow>;
total: number;
loading: boolean;
param: {
pageNum: number;
pageSize: number;
};
};
}
export default defineComponent({
name: 'systemUser',
components: { AddUer, EditUser },
setup() {
const addUserRef = ref();
const editUserRef = ref();
const state: any = reactive({
const state = reactive<TableDataState>({
tableData: {
data: [],
total: 0,
@ -82,7 +110,7 @@ export default {
});
// 初始化表格数据
const initTableData = () => {
const data: Array<object> = [];
const data: Array<TableDataRow> = [];
for (let i = 0; i < 2; i++) {
data.push({
userName: i === 0 ? 'admin' : 'test',
@ -107,11 +135,11 @@ export default {
addUserRef.value.openDialog();
};
// 打开修改用户弹窗
const onOpenEditUser = (row: Object) => {
const onOpenEditUser = (row: TableDataRow) => {
editUserRef.value.openDialog(row);
};
// 删除用户
const onRowDel = (row: Object) => {
const onRowDel = (row: TableDataRow) => {
ElMessageBox.confirm(`此操作将永久删除账户名称:“${row.userName}”,是否继续?`, '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
@ -145,10 +173,5 @@ export default {
...toRefs(state),
};
},
};
});
</script>
<style scoped lang="scss">
.system-user-container {
}
</style>

View File

@ -1,65 +1,79 @@
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
import type { UserConfig } from 'vite';
import { loadEnv } from './src/utils/viteBuild';
import { defineConfig, loadEnv, ConfigEnv } from 'vite';
const pathResolve = (dir: string): any => {
return resolve(__dirname, '.', dir);
};
const { VITE_PORT, VITE_OPEN, VITE_PUBLIC_PATH } = loadEnv();
const alias: Record<string, string> = {
'/@': pathResolve('./src/'),
};
const viteConfig: UserConfig = {
plugins: [vue()],
root: process.cwd(),
resolve: { alias },
base: process.env.NODE_ENV === 'production' ? VITE_PUBLIC_PATH : './',
server: {
host: '0.0.0.0',
port: VITE_PORT,
open: VITE_OPEN,
proxy: {
'/gitee': {
target: 'https://gitee.com',
ws: true,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/gitee/, ''),
const viteConfig = defineConfig((mode: ConfigEnv) => {
const env = loadEnv(mode.mode, process.cwd());
return {
plugins: [vue()],
root: process.cwd(),
resolve: { alias },
base: mode.command === 'serve' ? './' : env.VITE_PUBLIC_PATH,
server: {
host: '0.0.0.0',
port: env.VITE_PORT as unknown as number,
open: env.VITE_OPEN,
proxy: {
'/gitee': {
target: 'https://gitee.com',
ws: true,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/gitee/, ''),
},
},
},
},
build: {
outDir: 'dist',
minify: 'esbuild',
sourcemap: false,
chunkSizeWarningLimit: 1500,
rollupOptions: {
output: {
entryFileNames: `assets/[name].${new Date().getTime()}.js`,
chunkFileNames: `assets/[name].${new Date().getTime()}.js`,
assetFileNames: `assets/[name].${new Date().getTime()}.[ext]`,
},
},
},
css: {
postcss: {
plugins: [
{
postcssPlugin: 'internal:charset-removal',
AtRule: {
charset: (atRule) => {
if (atRule.name === 'charset') {
atRule.remove();
}
},
build: {
outDir: 'dist',
sourcemap: false,
chunkSizeWarningLimit: 1500,
rollupOptions: {
output: {
entryFileNames: `assets/[name].${new Date().getTime()}.js`,
chunkFileNames: `assets/[name].${new Date().getTime()}.js`,
assetFileNames: `assets/[name].${new Date().getTime()}.[ext]`,
compact: true,
manualChunks: {
vue: ['vue', 'vue-router', 'vuex'],
echarts: ['echarts'],
},
},
],
},
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
},
ie8: true,
output: {
comments: true,
},
},
},
},
};
css: {
postcss: {
plugins: [
{
postcssPlugin: 'internal:charset-removal',
AtRule: {
charset: (atRule) => {
if (atRule.name === 'charset') {
atRule.remove();
}
},
},
},
],
},
},
};
});
export default viteConfig;