'admin-22.12.10:同步master分支v2.4.2版本内容,具体查看masterCHANGELOG.md'

This commit is contained in:
lyt 2022-12-10 10:22:15 +08:00
parent c35077eef3
commit 8a5919c0c4
36 changed files with 1547 additions and 2040 deletions

View File

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

View File

@ -43,13 +43,15 @@
| Edge | Firefox | Chrome | Safari |
| --------- | ------------ | ----------- | ----------- |
| Edge ≥ 79 | Firefox ≥ 78 | Chrome ≥ 78 | Safari ≥ 12 |
| Edge ≥ 88 | Firefox ≥ 78 | Chrome ≥ 87 | Safari ≥ 13 |
> 由于 Vue3 不再支持 IE11故而 ElementPlus 也不支持 IE11 及之前版本。
#### ⚡ 使用说明
建议使用 cnpm因为 yarn 有时会报错。<a href="http://nodejs.cn/" target="_blank">node 版本 > 14xx.xx.x</a>
建议使用 cnpm因为 yarn 有时会报错。<a href="http://nodejs.cn/" target="_blank">node 版本 > 14.18+/16+</a>
> Vite 不再支持 Node 12 / 13 / 15因为上述版本已经进入了 EOL 阶段。现在你必须使用 Node 14.18+ / 16+ 版本。
```bash
# 克隆项目

1619
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "vue-next-admin-template",
"version": "2.4.1",
"version": "2.4.2",
"description": "vue3 vite next admin template",
"author": "lyt_20201208",
"license": "MIT",
@ -11,33 +11,34 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.0.10",
"axios": "^1.2.0",
"echarts": "^5.4.0",
"element-plus": "^2.2.25",
"axios": "^1.2.1",
"echarts": "^5.4.1",
"element-plus": "^2.2.26",
"js-cookie": "^3.0.1",
"mitt": "^3.0.0",
"nprogress": "^0.2.0",
"pinia": "^2.0.27",
"pinia": "^2.0.28",
"qrcodejs2-fixes": "^0.0.2",
"screenfull": "^6.0.2",
"sortablejs": "^1.15.0",
"vue": "^3.2.45",
"vue-clipboard3": "^2.0.0",
"qs": "^6.11.0",
"vue-router": "^4.1.6"
},
"devDependencies": {
"@types/node": "^18.11.9",
"@types/node": "^18.11.12",
"@types/nprogress": "^0.2.0",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"@vitejs/plugin-vue": "^3.2.0",
"@typescript-eslint/eslint-plugin": "^5.46.0",
"@typescript-eslint/parser": "^5.46.0",
"@vitejs/plugin-vue": "^4.0.0",
"@vue/compiler-sfc": "^3.2.45",
"eslint": "^8.28.0",
"eslint": "^8.29.0",
"eslint-plugin-vue": "^9.8.0",
"prettier": "^2.8.0",
"sass": "^1.56.1",
"typescript": "^4.9.3",
"vite": "^3.2.4",
"prettier": "^2.8.1",
"sass": "^1.56.2",
"typescript": "^4.9.4",
"vite": "^4.0.0",
"vite-plugin-vue-setup-extend": "^0.4.0",
"vue-eslint-parser": "^9.1.0"
},
@ -50,7 +51,7 @@
"url": "https://gitee.com/lyt-top/vue-next-admin/issues"
},
"engines": {
"node": ">=14.0.0",
"node": ">=16.0.0",
"npm": ">= 7.0.0"
},
"keywords": [

View File

@ -1,17 +1,27 @@
import request from '/@/utils/request';
/**
* request.post(xxx) post params data
*
* api接口集合
* @method signIn
* @method signOut 退
*/
export function useLoginApi() {
return {
signIn: (params: object) => {
return request.post('/user/signIn', params);
signIn: (data: object) => {
return request({
url: '/user/signIn',
method: 'post',
data,
});
},
signOut: (params: object) => {
return request.post('/user/signOut', params);
signOut: (data: object) => {
return request({
url: '/user/signOut',
method: 'post',
data,
});
},
};
}

View File

@ -3,6 +3,8 @@ import request from '/@/utils/request';
/**
* gitee
*
* request.post(xxx) post params data
*
* json https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu
* isRequestRoutes true
* @method getAdminMenu (admin)
@ -11,10 +13,18 @@ import request from '/@/utils/request';
export function useMenuApi() {
return {
getAdminMenu: (params?: object) => {
return request.get('/gitee/lyt-top/vue-next-admin-images/raw/master/menu/adminMenu.json', params);
return request({
url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/adminMenu.json',
method: 'get',
params,
});
},
getTestMenu: (params?: object) => {
return request.get('/gitee/lyt-top/vue-next-admin-images/raw/master/menu/testMenu.json', params);
return request({
url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/testMenu.json',
method: 'get',
params,
});
},
};
}

View File

@ -1,67 +1,48 @@
<template>
<div class="icon-selector w100 h100">
<el-input
v-model="state.fontIconSearch"
:placeholder="state.fontIconPlaceholder"
:clearable="clearable"
:disabled="disabled"
:size="size"
ref="inputWidthRef"
@clear="onClearFontIcon"
@focus="onIconFocus"
@blur="onIconBlur"
>
<template #prepend>
<SvgIcon
:name="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix"
class="font14"
v-if="state.fontIconPrefix === '' ? prepend?.indexOf('ele-') > -1 : state.fontIconPrefix?.indexOf('ele-') > -1"
/>
<i v-else :class="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix" class="font14"></i>
</template>
</el-input>
<el-popover
placement="bottom"
:width="state.fontIconWidth"
trigger="click"
transition="el-zoom-in-top"
popper-class="icon-selector-popper"
@show="onPopoverShow"
trigger="click"
:virtual-ref="inputWidthRef"
virtual-triggering
>
<template #reference>
<el-input
v-model="state.fontIconSearch"
:placeholder="state.fontIconPlaceholder"
:clearable="clearable"
:disabled="disabled"
:size="size"
ref="inputWidthRef"
@clear="onClearFontIcon"
@focus="onIconFocus"
@blur="onIconBlur"
>
<template #prepend>
<SvgIcon
:name="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix"
class="font14"
v-if="state.fontIconPrefix === '' ? prepend?.indexOf('ele-') > -1 : state.fontIconPrefix?.indexOf('ele-') > -1"
/>
<i v-else :class="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix" class="font14"></i>
</template>
</el-input>
</template>
<template #default>
<div class="icon-selector-warp">
<div class="icon-selector-warp-title flex">
<div class="flex-auto">{{ title }}</div>
<div class="icon-selector-warp-title-tab" v-if="type === 'all'">
<span :class="{ 'span-active': state.fontIconType === 'ali' }" @click="onIconChange('ali')" class="ml10" title="iconfont 图标">
ali
</span>
<span :class="{ 'span-active': state.fontIconType === 'ele' }" @click="onIconChange('ele')" class="ml10" title="elementPlus 图标">
ele
</span>
<span :class="{ 'span-active': state.fontIconType === 'awe' }" @click="onIconChange('awe')" class="ml10" title="fontawesome 图标">
awe
</span>
</div>
</div>
<div class="icon-selector-warp-row">
<el-scrollbar ref="selectorScrollbarRef">
<el-row :gutter="10" v-if="fontIconSheetsFilterList.length > 0">
<el-col :xs="6" :sm="4" :md="4" :lg="4" :xl="4" @click="onColClick(v)" v-for="(v, k) in fontIconSheetsFilterList" :key="k">
<div class="icon-selector-warp-item" :class="{ 'icon-selector-active': state.fontIconPrefix === v }">
<div class="flex-margin">
<div class="icon-selector-warp-item-value">
<SvgIcon :name="v" />
</div>
</div>
</div>
</el-col>
</el-row>
<el-empty :image-size="100" v-if="fontIconSheetsFilterList.length <= 0" :description="emptyDescription"></el-empty>
</el-scrollbar>
</div>
<div class="icon-selector-warp-title">{{ title }}</div>
<el-tabs v-model="state.fontIconTabActive" @tab-click="onIconClick">
<el-tab-pane lazy label="ali" name="ali">
<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
</el-tab-pane>
<el-tab-pane lazy label="ele" name="ele">
<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
</el-tab-pane>
<el-tab-pane lazy label="awe" name="awe">
<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
</el-tab-pane>
</el-tabs>
</div>
</template>
</el-popover>
@ -69,7 +50,8 @@
</template>
<script setup lang="ts" name="iconSelector">
import { ref, reactive, onMounted, nextTick, computed, watch } from 'vue';
import { defineAsyncComponent, ref, reactive, onMounted, nextTick, computed, watch } from 'vue';
import type { TabsPaneContext } from 'element-plus';
import initIconfont from '/@/utils/getStyleSheets';
import '/@/theme/iconSelector.scss';
@ -95,11 +77,6 @@ const props = defineProps({
type: String,
default: () => '请选择图标',
},
// icon
type: {
type: String,
default: () => 'ele',
},
//
disabled: {
type: Boolean,
@ -124,18 +101,22 @@ const props = defineProps({
// /
const emit = defineEmits(['update:modelValue', 'get', 'clear']);
//
const IconList = defineAsyncComponent(() => import('/@/components/iconSelector/list.vue'));
//
const inputWidthRef = ref();
const selectorScrollbarRef = ref();
const state = reactive({
fontIconPrefix: '',
fontIconWidth: 0,
fontIconSearch: '',
fontIconTabsIndex: 0,
fontIconSheetsList: [],
fontIconPlaceholder: '',
fontIconType: 'ali',
fontIconShow: true,
fontIconTabActive: 'ali',
fontIconList: {
ali: [],
ele: [],
awe: [],
},
});
// input modelValue input placeholder
@ -146,32 +127,91 @@ const onIconFocus = () => {
};
// input input
const onIconBlur = () => {
const list = fontIconTabNameList();
setTimeout(() => {
const icon = state.fontIconSheetsList.filter((icon: string) => icon === state.fontIconSearch);
const icon = list.filter((icon: string) => icon === state.fontIconSearch);
if (icon.length <= 0) state.fontIconSearch = '';
}, 300);
};
//
const fontIconSheetsFilterList = computed(() => {
const list = fontIconTabNameList();
if (!state.fontIconSearch) return list;
let search = state.fontIconSearch.trim().toLowerCase();
return list.filter((item: string) => {
if (item.toLowerCase().indexOf(search) !== -1) return item;
});
});
// tab name
const fontIconTabNameList = () => {
let iconList: any = [];
if (state.fontIconTabActive === 'ali') iconList = state.fontIconList.ali;
else if (state.fontIconTabActive === 'ele') iconList = state.fontIconList.ele;
else if (state.fontIconTabActive === 'awe') iconList = state.fontIconList.awe;
return iconList;
};
// icon
const initModeValueEcho = () => {
if (props.modelValue === '') return ((<string | undefined>state.fontIconPlaceholder) = props.placeholder);
(<string | undefined>state.fontIconPlaceholder) = props.modelValue;
(<string | undefined>state.fontIconPrefix) = props.modelValue;
};
// icon type all alieleawe
const initFontIconTypeEcho = () => {
if (props.modelValue!.indexOf('iconfont') > -1) onIconChange('ali');
else if (props.modelValue!.indexOf('ele-') > -1) onIconChange('ele');
else if (props.modelValue!.indexOf('fa') > -1) onIconChange('awe');
else onIconChange('ali');
// icon tab
const initFontIconName = () => {
let name = 'ali';
if (props.modelValue!.indexOf('iconfont') > -1) name = 'ali';
else if (props.modelValue!.indexOf('ele-') > -1) name = 'ele';
else if (props.modelValue!.indexOf('fa') > -1) name = 'awe';
// tab
state.fontIconTabActive = name;
return name;
};
//
const initFontIconData = async (name: string) => {
if (name === 'ali') {
// 使 `iconfont xxx`
if (state.fontIconList.ali.length > 0) return;
await initIconfont.ali().then((res: any) => {
state.fontIconList.ali = res.map((i: string) => `iconfont ${i}`);
});
} else if (name === 'ele') {
// element plus
if (state.fontIconList.ele.length > 0) return;
await initIconfont.ele().then((res: any) => {
state.fontIconList.ele = res;
});
} else if (name === 'awe') {
// fontawesome使 `fa xxx`
if (state.fontIconList.awe.length > 0) return;
await initIconfont.awe().then((res: any) => {
state.fontIconList.awe = res.map((i: string) => `fa ${i}`);
});
}
// input placeholder
// https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
state.fontIconPlaceholder = props.placeholder;
//
initModeValueEcho();
};
//
const onIconClick = (pane: TabsPaneContext) => {
initFontIconData(pane.paneName as string);
inputWidthRef.value.focus();
};
// icon
const onColClick = (v: string) => {
state.fontIconPlaceholder = v;
state.fontIconPrefix = v;
emit('get', state.fontIconPrefix);
emit('update:modelValue', state.fontIconPrefix);
inputWidthRef.value.focus();
};
// icon
const onClearFontIcon = () => {
state.fontIconPrefix = '';
emit('clear', state.fontIconPrefix);
emit('update:modelValue', state.fontIconPrefix);
};
//
const fontIconSheetsFilterList = computed(() => {
if (!state.fontIconSearch) return state.fontIconSheetsList;
let search = state.fontIconSearch.trim().toLowerCase();
return state.fontIconSheetsList.filter((item: string) => {
if (item.toLowerCase().indexOf(search) !== -1) return item;
});
});
// input
const getInputWidth = () => {
nextTick(() => {
@ -184,56 +224,9 @@ const initResize = () => {
getInputWidth();
});
};
//
const initFontIconData = async (type: string) => {
state.fontIconSheetsList = [];
if (type === 'ali') {
await initIconfont.ali().then((res: any) => {
// 使 `iconfont xxx`
state.fontIconSheetsList = res.map((i: string) => `iconfont ${i}`);
});
} else if (type === 'ele') {
await initIconfont.ele().then((res: any) => {
state.fontIconSheetsList = res;
});
} else if (type === 'awe') {
await initIconfont.awe().then((res: any) => {
// fontawesome使 `fa xxx`
state.fontIconSheetsList = res.map((i: string) => `fa ${i}`);
});
}
// input placeholder
// https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
state.fontIconPlaceholder = props.placeholder;
//
initModeValueEcho();
};
//
const onIconChange = (type: string) => {
state.fontIconType = type;
initFontIconData(type);
};
// icon
const onColClick = (v: string) => {
state.fontIconPlaceholder = v;
state.fontIconPrefix = v;
emit('get', state.fontIconPrefix);
emit('update:modelValue', state.fontIconPrefix);
};
// icon
const onClearFontIcon = () => {
state.fontIconPrefix = '';
emit('clear', state.fontIconPrefix);
emit('update:modelValue', state.fontIconPrefix);
};
// Popover
const onPopoverShow = () => {
initModeValueEcho();
initFontIconTypeEcho();
};
//
onMounted(() => {
initModeValueEcho();
initFontIconData(initFontIconName());
initResize();
getInputWidth();
});
@ -242,6 +235,7 @@ watch(
() => props.modelValue,
() => {
initModeValueEcho();
initFontIconName();
}
);
</script>

View File

@ -0,0 +1,84 @@
<template>
<div class="icon-selector-warp-row">
<el-scrollbar ref="selectorScrollbarRef">
<el-row :gutter="10" v-if="props.list.length > 0">
<el-col :xs="6" :sm="4" :md="4" :lg="4" :xl="4" v-for="(v, k) in list" :key="k" @click="onColClick(v)">
<div class="icon-selector-warp-item" :class="{ 'icon-selector-active': prefix === v }">
<SvgIcon :name="v" />
</div>
</el-col>
</el-row>
<el-empty :image-size="100" v-if="list.length <= 0" :description="empty"></el-empty>
</el-scrollbar>
</div>
</template>
<script setup lang="ts" name="iconSelectorList">
//
const props = defineProps({
//
list: {
type: Array,
default: () => [],
},
//
empty: {
type: String,
default: () => '无相关图标',
},
//
prefix: {
type: String,
default: () => '',
},
});
// /
const emit = defineEmits(['get-icon']);
// icon
const onColClick = (v: unknown | string) => {
emit('get-icon', v);
};
</script>
<style scoped lang="scss">
.icon-selector-warp-row {
height: 230px;
overflow: hidden;
.el-row {
padding: 15px;
}
.el-scrollbar__bar.is-horizontal {
display: none;
}
.icon-selector-warp-item {
display: flex;
justify-content: center;
align-items: center;
border: 1px solid var(--el-border-color);
border-radius: 5px;
margin-bottom: 10px;
height: 30px;
i {
font-size: 20px;
color: var(--el-text-color-regular);
}
&:hover {
cursor: pointer;
background-color: var(--el-color-primary-light-9);
border: 1px solid var(--el-color-primary-light-5);
i {
color: var(--el-color-primary);
}
}
}
.icon-selector-active {
background-color: var(--el-color-primary-light-9);
border: 1px solid var(--el-color-primary-light-5);
i {
color: var(--el-color-primary);
}
}
}
</style>

View File

@ -80,9 +80,13 @@ onMounted(() => {
getIframeListRoutes();
// https://gitee.com/lyt-top/vue-next-admin/issues/I58U75
// https://gitee.com/lyt-top/vue-next-admin/issues/I59RXK
// https://gitee.com/lyt-top/vue-next-admin/pulls/40
nextTick(() => {
setTimeout(() => {
if (themeConfig.value.isCacheTagsView) cachedViews.value = Session.get('tagsViewList')?.map((item: RouteItem) => item.name);
if (themeConfig.value.isCacheTagsView) {
let tagsViewArr: RouteItem[] = Session.get('tagsViewList') || [];
cachedViews.value = tagsViewArr.filter((item) => item.meta?.isKeepAlive).map((item) => item.name as string);
}
}, 0);
});
});

View File

@ -16,6 +16,11 @@
</div>
<div class="upgrade-content">
{{ getThemeConfig.globalTitle }} 新版本来啦马上更新尝鲜吧不用担心更新很快的哦
<div class="mt5">
<el-link type="primary" class="font12" href="https://gitee.com/lyt-top/vue-next-admin/blob/master/CHANGELOG.md" target="_black">
CHANGELOG.md
</el-link>
</div>
<div class="upgrade-content-desc mt5">提示更新会还原默认配置</div>
</div>
<div class="upgrade-btn">
@ -36,7 +41,7 @@ import { Local } from '/@/utils/storage';
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const state = reactive({
isUpgrade: true,
isUpgrade: false,
// @ts-ignore
version: __VERSION__,
isLoading: false,
@ -61,8 +66,15 @@ const onUpgrade = () => {
Local.set('version', state.version);
}, 2000);
};
//
const delayShow = () => {
setTimeout(() => {
state.isUpgrade = true;
}, 2000);
};
//
onMounted(() => {
delayShow();
setTimeout(() => {
state.btnTxt = '马上更新';
}, 200);
@ -88,7 +100,7 @@ onMounted(() => {
&::after {
content: '';
position: absolute;
background-color: var(--el-color-primary);
background-color: var(--el-color-primary-light-1);
width: 130%;
height: 130px;
border-bottom-left-radius: 100%;
@ -103,7 +115,8 @@ onMounted(() => {
letter-spacing: 3px;
}
.upgrade-title-warp-version {
background-color: var(--next-color-white);
color: var(--next-color-white);
background-color: var(--el-color-primary-light-4);
font-size: 12px;
position: absolute;
display: flex;

View File

@ -43,6 +43,9 @@ export async function initBackEndControlRoutes() {
await useUserInfo().setUserInfos();
// 获取路由菜单数据
const res = await getBackEndControlRoutes();
// 无登录权限时,添加判断
// https://gitee.com/lyt-top/vue-next-admin/issues/I64HVO
if (res.data.length <= 0) return Promise.resolve(true);
// 存储接口原始路由未处理component根据需求选择使用
useRequestOldRoutes().setRequestOldRoutes(JSON.parse(JSON.stringify(res.data)));
// 处理路由component替换 dynamicRoutes/@/router/route第一个顶级 children 的路由
@ -58,7 +61,7 @@ export async function initBackEndControlRoutes() {
* @description
* @description tagsView(isHide)
*/
export function setFilterMenuAndCacheTagsViewRoutes() {
export async function setFilterMenuAndCacheTagsViewRoutes() {
const storesRoutesList = useRoutesList(pinia);
storesRoutesList.setRoutesList(dynamicRoutes[0].children as any);
setCacheTagsViewRoutes();
@ -119,8 +122,8 @@ export function getBackEndControlRoutes() {
* @description
* @description /src/views/system/menu/component/addMenu.vue
*/
export function setBackEndControlRefreshRoutes() {
getBackEndControlRoutes();
export async function setBackEndControlRefreshRoutes() {
await getBackEndControlRoutes();
}
/**

View File

@ -26,6 +26,9 @@ export async function initFrontEndControlRoutes() {
// 触发初始化用户信息 pinia
// https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP
await useUserInfo(pinia).setUserInfos();
// 无登录权限时,添加判断
// https://gitee.com/lyt-top/vue-next-admin/issues/I64HVO
if (useUserInfo().userInfos.roles.length <= 0) return Promise.resolve(true);
// 添加动态路由
await setAddRoute();
// 设置递归过滤有权限的路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组

View File

@ -89,13 +89,13 @@ export const useThemeConfig = defineStore('themeConfig', {
// 是否开启 TagsView 共用
isShareTagsView: false,
// 是否开启 Footer 底部版权信息
isFooter: true,
isFooter: false,
// 是否开启灰色模式
isGrayscale: true,
isGrayscale: false,
// 是否开启色弱模式
isInvert: false,
// 是否开启水印
isWartermark: false,
isWartermark: true,
// 水印文案
wartermarkText: 'vue-next-admin',

View File

@ -19,9 +19,6 @@
/* Input 输入框InputNumber 计数器
------------------------------- */
.el-input {
height: 100%;
}
// 菜单搜索
.el-autocomplete-suggestion__wrap {
max-height: 280px !important;

View File

@ -5,64 +5,25 @@
.icon-selector-warp {
height: 260px;
overflow: hidden;
position: relative;
.icon-selector-warp-title {
position: absolute;
height: 40px;
line-height: 40px;
padding: 0 15px;
.icon-selector-warp-title-tab {
span {
cursor: pointer;
&:hover {
color: var(--el-color-primary);
text-decoration: underline;
}
}
.span-active {
color: var(--el-color-primary);
text-decoration: underline;
}
}
left: 15px;
}
.icon-selector-warp-row {
height: 230px;
overflow: hidden;
border-top: 1px solid var(--el-border-color);
.el-row {
padding: 15px;
}
.el-scrollbar__bar.is-horizontal {
display: none;
}
.icon-selector-warp-item {
display: flex;
border: 1px solid var(--el-border-color);
padding: 5px;
border-radius: 5px;
margin-bottom: 10px;
.icon-selector-warp-item-value {
i {
font-size: 20px;
color: var(--el-text-color-regular);
}
.el-tabs__header {
display: flex;
justify-content: flex-end;
padding: 0 15px;
border-bottom: 1px solid var(--el-border-color-light);
margin: 0 !important;
.el-tabs__nav-wrap {
&::after {
height: 0 !important;
}
&:hover {
cursor: pointer;
background-color: var(--el-color-primary-light-9);
border: 1px solid var(--el-color-primary-light-5);
.icon-selector-warp-item-value {
i {
color: var(--el-color-primary);
}
}
}
}
.icon-selector-active {
background-color: var(--el-color-primary-light-9);
border: 1px solid var(--el-color-primary-light-5);
.icon-selector-warp-item-value {
i {
color: var(--el-color-primary);
}
.el-tabs__item {
padding: 0 5px !important;
}
}
}

View File

@ -16,4 +16,16 @@
// 响应式表单时登录页需要重新处理
display: unset !important;
}
// 表格演示中的表单筛选
.table-form-btn {
display: flex !important;
.el-form-item__label {
width: auto !important;
}
}
// 表格演示中的表单筛选最大高度适配移动端
.table-search-container {
max-height: 160px;
overflow: auto;
}
}

View File

@ -5,7 +5,7 @@ declare module 'splitpanes';
declare module 'js-cookie';
declare module '@wangeditor/editor-for-vue';
declare module 'js-table2excel';
declare module 'sortablejs';
declare module 'qs';
// 声明一个模块,防止引入文件时报错
declare module '*.json';

13
src/types/views.d.ts vendored
View File

@ -43,7 +43,6 @@ declare type Demo2State<T = any> = {
earth3DBtnList: T[];
chartData4List: T[];
myCharts: T[];
the3DEarth: HTMLDivElement | null;
};
/**
@ -305,11 +304,19 @@ declare type TableHeaderType = {
isCheck: boolean;
};
declare type TableSearchType = {
label: string;
prop: string;
placeholder: string;
required: boolean;
type: string;
options?: SelectOptionType[];
};
declare type TableDemoState = {
tableData: {
data: EmptyObjectType[];
header: TableHeaderType[];
param: EmptyObjectType;
config: {
total: number;
loading: boolean;
@ -318,5 +325,7 @@ declare type TableDemoState = {
isSerialNo: boolean;
isOperate: boolean;
};
search: TableSearchType[];
param: EmptyObjectType;
};
};

View File

@ -1,12 +1,18 @@
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Session } from '/@/utils/storage';
import qs from 'qs';
// 配置新建一个 axios 实例
const service: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_URL,
timeout: 50000,
headers: { 'Content-Type': 'application/json' },
paramsSerializer: {
serialize(params) {
return qs.stringify(params, { allowDots: true });
},
},
});
// 添加请求拦截器

View File

@ -86,37 +86,41 @@ const onSignIn = async () => {
Cookies.set('userName', state.ruleForm.userName);
if (!themeConfig.value.isRequestRoutes) {
// 2
await initFrontEndControlRoutes();
signInSuccess();
const isNoPower = await initFrontEndControlRoutes();
signInSuccess(isNoPower);
} else {
// isRequestRoutes true
// router No match found for location with path "/"
await initBackEndControlRoutes();
const isNoPower = await initBackEndControlRoutes();
// initBackEndControlRoutes signInSuccess
signInSuccess();
signInSuccess(isNoPower);
}
};
//
const signInSuccess = () => {
//
let currentTimeInfo = currentTime.value;
//
// /
if (route.query?.redirect) {
router.push({
path: <string>route.query?.redirect,
query: Object.keys(<string>route.query?.params).length > 0 ? JSON.parse(<string>route.query?.params) : '',
});
const signInSuccess = (isNoPower: boolean | undefined) => {
if (isNoPower) {
ElMessage.warning('抱歉,您没有登录权限');
Session.clear();
} else {
router.push('/');
//
let currentTimeInfo = currentTime.value;
//
// /
if (route.query?.redirect) {
router.push({
path: <string>route.query?.redirect,
query: Object.keys(<string>route.query?.params).length > 0 ? JSON.parse(<string>route.query?.params) : '',
});
} else {
router.push('/');
}
//
const signInText = '欢迎回来!';
ElMessage.success(`${currentTimeInfo}${signInText}`);
// loading
NextLoading.start();
}
//
// loading
state.loading.signIn = true;
const signInText = '欢迎回来!';
ElMessage.success(`${currentTimeInfo}${signInText}`);
// loading
NextLoading.start();
state.loading.signIn = false;
};
</script>

View File

@ -1,143 +0,0 @@
<template>
<div class="system-add-dept-container">
<el-dialog title="新增部门" v-model="state.isShowDialog" width="769px">
<el-form :model="state.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="上级部门">
<el-cascader
:options="state.deptData"
:props="{ checkStrictly: true, value: 'deptName', label: 'deptName' }"
placeholder="请选择部门"
clearable
class="w100"
v-model="state.ruleForm.deptLevel"
>
<template #default="{ node, data }">
<span>{{ data.deptName }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="部门名称">
<el-input v-model="state.ruleForm.deptName" placeholder="请输入部门名称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="负责人">
<el-input v-model="state.ruleForm.person" placeholder="请输入负责人" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="手机号">
<el-input v-model="state.ruleForm.phone" placeholder="请输入手机号" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="邮箱">
<el-input v-model="state.ruleForm.email" placeholder="请输入" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="排序">
<el-input-number v-model="state.ruleForm.sort" :min="0" :max="999" controls-position="right" placeholder="请输入排序" class="w100" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="部门状态">
<el-switch v-model="state.ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="部门描述">
<el-input v-model="state.ruleForm.describe" type="textarea" placeholder="请输入部门描述" maxlength="150"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="systemAddDept">
import { reactive, onMounted } from 'vue';
//
const state = reactive({
isShowDialog: false,
ruleForm: {
deptLevel: [], //
deptName: '', //
person: '', //
phone: '', //
email: '', //
sort: 0, //
status: true, //
describe: '', //
},
deptData: [] as DeptTreeType[], //
});
//
const openDialog = () => {
state.isShowDialog = true;
};
//
const closeDialog = () => {
state.isShowDialog = false;
};
//
const onCancel = () => {
closeDialog();
};
//
const onSubmit = () => {
closeDialog();
};
//
const initTableData = () => {
state.deptData.push({
deptName: 'vueNextAdmin',
createTime: new Date().toLocaleString(),
status: true,
sort: Math.random(),
describe: '顶级部门',
id: Math.random(),
children: [
{
deptName: 'IT外包服务',
createTime: new Date().toLocaleString(),
status: true,
sort: Math.random(),
describe: '总部',
id: Math.random(),
},
{
deptName: '资本控股',
createTime: new Date().toLocaleString(),
status: true,
sort: Math.random(),
describe: '分部',
id: Math.random(),
},
],
});
};
//
onMounted(() => {
initTableData();
});
//
defineExpose({
openDialog,
});
</script>

View File

@ -1,7 +1,7 @@
<template>
<div class="system-edit-dept-container">
<el-dialog title="修改部门" v-model="state.isShowDialog" width="769px">
<el-form :model="state.ruleForm" size="default" label-width="90px">
<div class="system-dept-dialog-container">
<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="769px">
<el-form ref="deptDialogFormRef" :model="state.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="上级部门">
@ -60,19 +60,22 @@
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="systemEditDept">
import { reactive, onMounted } from 'vue';
<script setup lang="ts" name="systemDeptDialog">
import { reactive, ref } from 'vue';
// /
const emit = defineEmits(['refresh']);
//
const deptDialogFormRef = ref();
const state = reactive({
isShowDialog: false,
ruleForm: {
deptLevel: [] as string[], //
deptName: '', //
@ -84,31 +87,51 @@ const state = reactive({
describe: '', //
},
deptData: [] as DeptTreeType[], //
dialog: {
isShowDialog: false,
type: '',
title: '',
submitTxt: '',
},
});
//
const openDialog = (row: RowDeptType) => {
row.deptLevel = ['vueNextAdmin'];
row.person = 'lyt';
row.phone = '12345678910';
row.email = 'vueNextAdmin@123.com';
state.ruleForm = row;
state.isShowDialog = true;
const openDialog = (type: string, row: RowDeptType) => {
if (type === 'edit') {
row.deptLevel = ['vueNextAdmin'];
row.person = 'lyt';
row.phone = '12345678910';
row.email = 'vueNextAdmin@123.com';
state.ruleForm = row;
state.dialog.title = '修改部门';
state.dialog.submitTxt = '修 改';
} else {
state.dialog.title = '新增部门';
state.dialog.submitTxt = '新 增';
// 使
// nextTick(() => {
// deptDialogFormRef.value.resetFields();
// });
}
state.dialog.isShowDialog = true;
getMenuData();
};
//
const closeDialog = () => {
state.isShowDialog = false;
state.dialog.isShowDialog = false;
};
//
const onCancel = () => {
closeDialog();
};
//
//
const onSubmit = () => {
closeDialog();
emit('refresh');
// if (state.dialog.type === 'add') { }
};
//
const initTableData = () => {
const getMenuData = () => {
state.deptData.push({
deptName: 'vueNextAdmin',
createTime: new Date().toLocaleString(),
@ -136,10 +159,6 @@ const initTableData = () => {
],
});
};
//
onMounted(() => {
initTableData();
});
//
defineExpose({

View File

@ -9,7 +9,7 @@
</el-icon>
查询
</el-button>
<el-button size="default" type="success" class="ml10" @click="onOpenAddDept">
<el-button size="default" type="success" class="ml10" @click="onOpenAddDept('add')">
<el-icon>
<ele-FolderAdd />
</el-icon>
@ -18,6 +18,7 @@
</div>
<el-table
:data="state.tableData.data"
v-loading="state.tableData.loading"
style="width: 100%"
row-key="id"
default-expand-all
@ -39,15 +40,14 @@
<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="small" text type="primary" @click="onOpenAddDept">新增</el-button>
<el-button size="small" text type="primary" @click="onOpenEditDept(scope.row)">修改</el-button>
<el-button size="small" text type="primary" @click="onOpenAddDept('add')">新增</el-button>
<el-button size="small" text type="primary" @click="onOpenEditDept('edit', scope.row)">修改</el-button>
<el-button size="small" text type="primary" @click="onTabelRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<AddDept ref="addDeptRef" />
<EditDept ref="editDeptRef" />
<DeptDialog ref="deptDialogRef" @refresh="getTableData()" />
</div>
</template>
@ -56,12 +56,10 @@ import { defineAsyncComponent, ref, reactive, onMounted } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
//
const AddDept = defineAsyncComponent(() => import('/@/views/system/dept/component/addDept.vue'));
const EditDept = defineAsyncComponent(() => import('/@/views/system/dept/component/editDept.vue'));
const DeptDialog = defineAsyncComponent(() => import('/@/views/system/dept/dialog.vue'));
//
const addDeptRef = ref();
const editDeptRef = ref();
const deptDialogRef = ref();
const state = reactive<SysDeptState>({
tableData: {
data: [],
@ -75,7 +73,9 @@ const state = reactive<SysDeptState>({
});
//
const initTableData = () => {
const getTableData = () => {
state.tableData.loading = true;
state.tableData.data = [];
state.tableData.data.push({
deptName: 'vueNextAdmin',
createTime: new Date().toLocaleString(),
@ -103,14 +103,17 @@ const initTableData = () => {
],
});
state.tableData.total = state.tableData.data.length;
setTimeout(() => {
state.tableData.loading = false;
}, 500);
};
//
const onOpenAddDept = () => {
addDeptRef.value.openDialog();
const onOpenAddDept = (type: string) => {
deptDialogRef.value.openDialog(type);
};
//
const onOpenEditDept = (row: DeptTreeType) => {
editDeptRef.value.openDialog(row);
const onOpenEditDept = (type: string, row: DeptTreeType) => {
deptDialogRef.value.openDialog(type, row);
};
//
const onTabelRowDel = (row: DeptTreeType) => {
@ -120,12 +123,13 @@ const onTabelRowDel = (row: DeptTreeType) => {
type: 'warning',
})
.then(() => {
getTableData();
ElMessage.success('删除成功');
})
.catch(() => {});
};
//
onMounted(() => {
initTableData();
getTableData();
});
</script>

View File

@ -1,114 +0,0 @@
<template>
<div class="system-add-dic-container">
<el-dialog title="新增字典" v-model="state.isShowDialog" width="769px">
<el-alert title="半成品,交互过于复杂,请自行扩展!" type="warning" :closable="false" class="mb20"> </el-alert>
<el-form :model="state.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="字典名称">
<el-input v-model="state.ruleForm.dicName" placeholder="请输入字典名称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="字段名">
<el-input v-model="state.ruleForm.fieldName" placeholder="请输入字段名,拼接 ruleForm.list" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="字典状态">
<el-switch v-model="state.ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-row :gutter="35" v-for="(v, k) in state.ruleForm.list" :key="k">
<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="small" @click="onAddRow" v-if="k === 0">
<el-icon>
<ele-Plus />
</el-icon>
</el-button>
<el-button type="danger" circle size="small" @click="onDelRow(k)" v-else>
<el-icon>
<ele-Delete />
</el-icon>
</el-button>
<span class="ml10">字段</span>
</template>
<el-input v-model="v.label" style="width: 100%" placeholder="请输入字段名"> </el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="属性" :prop="`list[${k}].value`">
<el-input v-model="v.value" style="width: 100%" placeholder="请输入属性值"> </el-input>
</el-form-item>
</el-col>
</el-row>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="字典描述">
<el-input v-model="state.ruleForm.describe" type="textarea" placeholder="请输入字典描述" maxlength="150"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="systemAddDic">
import { reactive } from 'vue';
//
const state = reactive({
isShowDialog: false,
ruleForm: {
dicName: '', //
fieldName: '', //
status: true, //
list: [] as ListType[], // +
describe: '', //
},
});
//
const openDialog = () => {
state.isShowDialog = true;
};
//
const closeDialog = () => {
state.isShowDialog = false;
};
//
const onCancel = () => {
closeDialog();
};
//
const onSubmit = () => {
closeDialog();
};
//
const onAddRow = () => {
state.ruleForm.list.push({
id: Math.random(),
label: '',
value: '',
});
};
//
const onDelRow = (k: number) => {
state.ruleForm.list.splice(k, 1);
};
//
defineExpose({
openDialog,
});
</script>

View File

@ -1,8 +1,8 @@
<template>
<div class="system-edit-dic-container">
<el-dialog title="修改字典" v-model="state.isShowDialog" width="769px">
<div class="system-dic-dialog-container">
<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="769px">
<el-alert title="半成品,交互过于复杂,请自行扩展!" type="warning" :closable="false" class="mb20"> </el-alert>
<el-form :model="state.ruleForm" size="default" label-width="90px">
<el-form ref="dicDialogFormRef" :model="state.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="字典名称">
@ -56,19 +56,22 @@
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="systemEditDic">
import { reactive } from 'vue';
<script setup lang="ts" name="systemDicDialog">
import { reactive, ref } from 'vue';
// /
const emit = defineEmits(['refresh']);
//
const dicDialogFormRef = ref();
const state = reactive({
isShowDialog: false,
ruleForm: {
dicName: '', //
fieldName: '', //
@ -76,37 +79,56 @@ const state = reactive({
list: [] as ListType[], // +
describe: '', //
},
dialog: {
isShowDialog: false,
type: '',
title: '',
submitTxt: '',
},
});
//
const openDialog = (row: RowDicType) => {
if (row.fieldName === 'SYS_UERINFO') {
row.list = [
{ id: Math.random(), label: 'sex', value: '1' },
{ id: Math.random(), label: 'sex', value: '0' },
];
const openDialog = (type: string, row: RowDicType) => {
if (type === 'edit') {
if (row.fieldName === 'SYS_UERINFO') {
row.list = [
{ id: Math.random(), label: 'sex', value: '1' },
{ id: Math.random(), label: 'sex', value: '0' },
];
} else {
row.list = [
{ id: Math.random(), label: 'role', value: 'admin' },
{ id: Math.random(), label: 'role', value: 'common' },
{ id: Math.random(), label: 'roleName', value: '超级管理员' },
{ id: Math.random(), label: 'roleName', value: '普通用户' },
];
}
state.ruleForm = row;
state.dialog.title = '修改字典';
state.dialog.submitTxt = '修 改';
} else {
row.list = [
{ id: Math.random(), label: 'role', value: 'admin' },
{ id: Math.random(), label: 'role', value: 'common' },
{ id: Math.random(), label: 'roleName', value: '超级管理员' },
{ id: Math.random(), label: 'roleName', value: '普通用户' },
];
state.dialog.title = '新增字典';
state.dialog.submitTxt = '新 增';
// 使
// nextTick(() => {
// dicDialogFormRef.value.resetFields();
// });
}
state.ruleForm = row;
state.isShowDialog = true;
state.dialog.isShowDialog = true;
};
//
const closeDialog = () => {
state.isShowDialog = false;
state.dialog.isShowDialog = false;
};
//
const onCancel = () => {
closeDialog();
};
//
//
const onSubmit = () => {
closeDialog();
emit('refresh');
// if (state.dialog.type === 'add') { }
};
//
const onAddRow = () => {

View File

@ -9,14 +9,14 @@
</el-icon>
查询
</el-button>
<el-button size="default" type="success" class="ml10" @click="onOpenAddDic">
<el-button size="default" type="success" class="ml10" @click="onOpenAddDic('add')">
<el-icon>
<ele-FolderAdd />
</el-icon>
新增字典
</el-button>
</div>
<el-table :data="state.tableData.data" style="width: 100%">
<el-table :data="state.tableData.data" v-loading="state.tableData.loading" style="width: 100%">
<el-table-column type="index" label="序号" width="50" />
<el-table-column prop="dicName" label="字典名称" show-overflow-tooltip></el-table-column>
<el-table-column prop="fieldName" label="字段名" show-overflow-tooltip></el-table-column>
@ -30,7 +30,7 @@
<el-table-column prop="createTime" label="创建时间" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" width="100">
<template #default="scope">
<el-button size="small" text type="primary" @click="onOpenEditDic(scope.row)">修改</el-button>
<el-button size="small" text type="primary" @click="onOpenEditDic('edit', scope.row)">修改</el-button>
<el-button size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
@ -49,8 +49,7 @@
>
</el-pagination>
</el-card>
<AddDic ref="addDicRef" />
<EditDic ref="editDicRef" />
<DicDialog ref="dicDialogRef" @refresh="getTableData()" />
</div>
</template>
@ -59,12 +58,10 @@ import { defineAsyncComponent, reactive, onMounted, ref } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
//
const AddDic = defineAsyncComponent(() => import('/@/views/system/dic/component/addDic.vue'));
const EditDic = defineAsyncComponent(() => import('/@/views/system/dic/component/editDic.vue'));
const DicDialog = defineAsyncComponent(() => import('/@/views/system/dic/dialog.vue'));
//
const addDicRef = ref();
const editDicRef = ref();
const dicDialogRef = ref();
const state = reactive<SysDicState>({
tableData: {
data: [],
@ -78,7 +75,8 @@ const state = reactive<SysDicState>({
});
//
const initTableData = () => {
const getTableData = () => {
state.tableData.loading = true;
const data = [];
for (let i = 0; i < 2; i++) {
data.push({
@ -92,14 +90,17 @@ const initTableData = () => {
}
state.tableData.data = data;
state.tableData.total = state.tableData.data.length;
setTimeout(() => {
state.tableData.loading = false;
}, 500);
};
//
const onOpenAddDic = () => {
addDicRef.value.openDialog();
const onOpenAddDic = (type: string) => {
dicDialogRef.value.openDialog(type);
};
//
const onOpenEditDic = (row: RowDicType) => {
editDicRef.value.openDialog(row);
const onOpenEditDic = (type: string, row: RowDicType) => {
dicDialogRef.value.openDialog(type, row);
};
//
const onRowDel = (row: RowDicType) => {
@ -109,6 +110,7 @@ const onRowDel = (row: RowDicType) => {
type: 'warning',
})
.then(() => {
getTableData();
ElMessage.success('删除成功');
})
.catch(() => {});
@ -116,13 +118,15 @@ const onRowDel = (row: RowDicType) => {
//
const onHandleSizeChange = (val: number) => {
state.tableData.param.pageSize = val;
getTableData();
};
//
const onHandleCurrentChange = (val: number) => {
state.tableData.param.pageNum = val;
getTableData();
};
//
onMounted(() => {
initTableData();
getTableData();
});
</script>

View File

@ -1,236 +0,0 @@
<template>
<div class="system-edit-menu-container">
<el-dialog title="修改菜单" v-model="state.isShowDialog" width="769px">
<el-form :model="state.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="上级菜单">
<el-cascader
:options="state.menuData"
:props="{ checkStrictly: true, value: 'path', label: 'title' }"
placeholder="请选择上级菜单"
clearable
class="w100"
v-model="state.ruleForm.menuSuperior"
>
<template #default="{ node, data }">
<span>{{ data.title }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="菜单类型">
<el-radio-group v-model="state.ruleForm.menuType">
<el-radio label="menu">菜单</el-radio>
<el-radio label="btn">按钮</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="菜单名称">
<el-input v-model="state.ruleForm.meta.title" placeholder="格式message.router.xxx" clearable></el-input>
</el-form-item>
</el-col>
<template v-if="state.ruleForm.menuType === 'menu'">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="路由名称">
<el-input v-model="state.ruleForm.name" placeholder="路由中的 name 值" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="路由路径">
<el-input v-model="state.ruleForm.path" placeholder="路由中的 path 值" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="重定向">
<el-input v-model="state.ruleForm.redirect" placeholder="请输入路由重定向" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="菜单图标">
<IconSelector placeholder="请输入菜单图标" v-model="state.ruleForm.meta.icon" type="all" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="组件路径">
<el-input v-model="state.ruleForm.component" placeholder="组件路径" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="链接地址">
<el-input
v-model="state.ruleForm.meta.isLink"
placeholder="外链/内嵌时链接地址http:xxx.com"
clearable
:disabled="!state.ruleForm.isLink"
>
</el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="权限标识">
<el-select v-model="state.ruleForm.meta.roles" multiple placeholder="取角色管理" clearable class="w100">
<el-option label="admin" value="admin"></el-option>
<el-option label="common" value="common"></el-option>
</el-select>
</el-form-item>
</el-col>
</template>
<template v-if="state.ruleForm.menuType === 'btn'">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="权限标识">
<el-input v-model="state.ruleForm.btnPower" placeholder="请输入权限标识" clearable></el-input>
</el-form-item>
</el-col>
</template>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="菜单排序">
<el-input-number v-model="state.ruleForm.menuSort" controls-position="right" placeholder="请输入排序" class="w100" />
</el-form-item>
</el-col>
<template v-if="state.ruleForm.menuType === 'menu'">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="是否隐藏">
<el-radio-group v-model="state.ruleForm.meta.isHide">
<el-radio :label="true">隐藏</el-radio>
<el-radio :label="false">不隐藏</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="页面缓存">
<el-radio-group v-model="state.ruleForm.meta.isKeepAlive">
<el-radio :label="true">缓存</el-radio>
<el-radio :label="false">不缓存</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="是否固定">
<el-radio-group v-model="state.ruleForm.meta.isAffix">
<el-radio :label="true">固定</el-radio>
<el-radio :label="false">不固定</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="是否外链">
<el-radio-group v-model="state.ruleForm.isLink" :disabled="state.ruleForm.meta.isIframe">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="是否内嵌">
<el-radio-group v-model="state.ruleForm.meta.isIframe" @change="onSelectIframeChange">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</template>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="systemEditMenu">
import { defineAsyncComponent, reactive, onMounted } from 'vue';
import { storeToRefs } from 'pinia';
import { useRoutesList } from '/@/stores/routesList';
// import { setBackEndControlRefreshRoutes } from "/@/router/backEnd";
//
const IconSelector = defineAsyncComponent(() => import('/@/components/iconSelector/index.vue'));
//
const stores = useRoutesList();
const { routesList } = storeToRefs(stores);
const state = reactive({
isShowDialog: false,
// `/src/router/route.ts` `dynamicRoutes`
ruleForm: {
menuSuperior: [], //
menuType: 'menu', //
name: '', //
component: '', //
isLink: false, //
menuSort: 0, //
path: '', //
redirect: '', // children
meta: {
title: '', //
icon: '', //
isHide: false, //
isKeepAlive: true, //
isAffix: false, //
isLink: '', // /http:xxx.com`1isLink: `
isIframe: false, // `1isIframe:true 2isLink`
roles: '', //
},
btnPower: '', //
},
menuData: [] as RouteItems, //
});
// pinia
const getMenuData = (routes: RouteItems) => {
const arr: RouteItems = [];
routes.map((val: RouteItem) => {
val['title'] = val.meta?.title as string;
arr.push({ ...val });
if (val.children) getMenuData(val.children);
});
return arr;
};
//
const openDialog = (row: any) => {
//
row.menuType = 'menu';
row.menuSort = Math.random();
row.component = `${row.component} `
.match(/\'(.+)\'/g)
?.join('')
.replace(/\'/g, '');
state.ruleForm = row;
state.isShowDialog = true;
};
//
const closeDialog = () => {
state.isShowDialog = false;
};
//
const onSelectIframeChange = () => {
if (state.ruleForm.meta.isIframe) state.ruleForm.isLink = true;
else state.ruleForm.isLink = false;
};
//
const onCancel = () => {
closeDialog();
};
//
const onSubmit = () => {
closeDialog(); //
// setBackEndControlRefreshRoutes() //
};
//
onMounted(() => {
state.menuData = getMenuData(routesList.value);
});
//
defineExpose({
openDialog,
});
</script>

View File

@ -1,7 +1,7 @@
<template>
<div class="system-add-menu-container">
<el-dialog title="新增菜单" v-model="state.isShowDialog" width="769px">
<el-form :model="state.ruleForm" size="default" label-width="80px">
<div class="system-menu-dialog-container">
<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="769px">
<el-form ref="menuDialogFormRef" :model="state.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="上级菜单">
@ -51,7 +51,7 @@
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="菜单图标">
<IconSelector placeholder="请输入菜单图标" v-model="state.ruleForm.meta.icon" type="all" />
<IconSelector placeholder="请输入菜单图标" v-model="state.ruleForm.meta.icon" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
@ -138,27 +138,30 @@
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="systemAddMenu">
import { defineAsyncComponent, reactive, onMounted } from 'vue';
<script setup lang="ts" name="systemMenuDialog">
import { defineAsyncComponent, reactive, onMounted, ref } from 'vue';
import { storeToRefs } from 'pinia';
import { useRoutesList } from '/@/stores/routesList';
// import { setBackEndControlRefreshRoutes } from "/@/router/backEnd";
// /
const emit = defineEmits(['refresh']);
//
const IconSelector = defineAsyncComponent(() => import('/@/components/iconSelector/index.vue'));
//
const menuDialogFormRef = ref();
const stores = useRoutesList();
const { routesList } = storeToRefs(stores);
const state = reactive({
isShowDialog: false,
// `/src/router/route.ts` `dynamicRoutes`
ruleForm: {
menuSuperior: [], //
@ -182,6 +185,12 @@ const state = reactive({
btnPower: '', //
},
menuData: [] as RouteItems, //
dialog: {
isShowDialog: false,
type: '',
title: '',
submitTxt: '',
},
});
// pinia
@ -195,12 +204,32 @@ const getMenuData = (routes: RouteItems) => {
return arr;
};
//
const openDialog = () => {
state.isShowDialog = true;
const openDialog = (type: string, row?: any) => {
if (type === 'edit') {
//
row.menuType = 'menu';
row.menuSort = Math.random();
row.component = `${row.component} `
.match(/\'(.+)\'/g)
?.join('')
.replace(/\'/g, '');
state.ruleForm = row;
state.dialog.title = '修改菜单';
state.dialog.submitTxt = '修 改';
} else {
state.dialog.title = '新增菜单';
state.dialog.submitTxt = '新 增';
// 使
// nextTick(() => {
// menuDialogFormRef.value.resetFields();
// });
}
state.dialog.type = type;
state.dialog.isShowDialog = true;
};
//
const closeDialog = () => {
state.isShowDialog = false;
state.dialog.isShowDialog = false;
};
//
const onSelectIframeChange = () => {
@ -211,9 +240,11 @@ const onSelectIframeChange = () => {
const onCancel = () => {
closeDialog();
};
//
//
const onSubmit = () => {
closeDialog(); //
emit('refresh');
// if (state.dialog.type === 'add') { }
// setBackEndControlRefreshRoutes() //
};
//

View File

@ -16,7 +16,13 @@
新增菜单
</el-button>
</div>
<el-table :data="menuTableData" style="width: 100%" row-key="path" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }">
<el-table
:data="state.tableData.data"
v-loading="state.tableData.loading"
style="width: 100%"
row-key="path"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column label="菜单名称" show-overflow-tooltip>
<template #default="scope">
<SvgIcon :name="scope.row.meta.icon" />
@ -46,46 +52,54 @@
</el-table-column>
<el-table-column label="操作" show-overflow-tooltip width="140">
<template #default="scope">
<el-button size="small" text type="primary" @click="onOpenAddMenu">新增</el-button>
<el-button size="small" text type="primary" @click="onOpenEditMenu(scope.row)">修改</el-button>
<el-button size="small" text type="primary" @click="onOpenAddMenu('add')">新增</el-button>
<el-button size="small" text type="primary" @click="onOpenEditMenu('edit', scope.row)">修改</el-button>
<el-button size="small" text type="primary" @click="onTabelRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<AddMenu ref="addMenuRef" />
<EditMenu ref="editMenuRef" />
<MenuDialog ref="menuDialogRef" @refresh="getTableData()" />
</div>
</template>
<script setup lang="ts" name="systemMenu">
import { defineAsyncComponent, ref, computed } from 'vue';
import { defineAsyncComponent, ref, onMounted, reactive } from 'vue';
import { RouteRecordRaw } from 'vue-router';
import { ElMessageBox, ElMessage } from 'element-plus';
import { storeToRefs } from 'pinia';
import { useRoutesList } from '/@/stores/routesList';
// import { setBackEndControlRefreshRoutes } from "/@/router/backEnd";
//
const AddMenu = defineAsyncComponent(() => import('/@/views/system/menu/component/addMenu.vue'));
const EditMenu = defineAsyncComponent(() => import('/@/views/system/menu/component/editMenu.vue'));
const MenuDialog = defineAsyncComponent(() => import('/@/views/system/menu/dialog.vue'));
//
const stores = useRoutesList();
const { routesList } = storeToRefs(stores);
const addMenuRef = ref();
const editMenuRef = ref();
// pinia
const menuTableData = computed(() => {
return routesList.value;
const menuDialogRef = ref();
const state = reactive({
tableData: {
data: [] as RouteRecordRaw[],
loading: true,
},
});
//
const getTableData = () => {
state.tableData.loading = true;
state.tableData.data = routesList.value;
setTimeout(() => {
state.tableData.loading = false;
}, 500);
};
//
const onOpenAddMenu = () => {
addMenuRef.value.openDialog();
const onOpenAddMenu = (type: string) => {
menuDialogRef.value.openDialog(type);
};
//
const onOpenEditMenu = (row: RouteRecordRaw) => {
editMenuRef.value.openDialog(row);
const onOpenEditMenu = (type: string, row: RouteRecordRaw) => {
menuDialogRef.value.openDialog(type, row);
};
//
const onTabelRowDel = (row: RouteRecordRaw) => {
@ -96,7 +110,13 @@ const onTabelRowDel = (row: RouteRecordRaw) => {
})
.then(() => {
ElMessage.success('删除成功');
getTableData();
//await setBackEndControlRefreshRoutes() //
})
.catch(() => {});
};
//
onMounted(() => {
getTableData();
});
</script>

View File

@ -1,221 +0,0 @@
<template>
<div class="system-edit-role-container">
<el-dialog title="修改角色" v-model="state.isShowDialog" width="769px">
<el-form :model="state.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="角色名称">
<el-input v-model="state.ruleForm.roleName" placeholder="请输入角色名称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="角色标识">
<template #label>
<el-tooltip effect="dark" content="用于 `router/route.ts` meta.roles" placement="top-start">
<span>角色标识</span>
</el-tooltip>
</template>
<el-input v-model="state.ruleForm.roleSign" placeholder="请输入角色标识" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="排序">
<el-input-number v-model="state.ruleForm.sort" :min="0" :max="999" controls-position="right" placeholder="请输入排序" class="w100" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="角色状态">
<el-switch v-model="state.ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="角色描述">
<el-input v-model="state.ruleForm.describe" type="textarea" placeholder="请输入角色描述" maxlength="150"></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="菜单权限">
<el-tree
:data="state.menuData"
:props="state.menuProps"
:default-checked-keys="[112, 113]"
node-key="id"
show-checkbox
class="menu-data-tree"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="systemEditRole">
import { reactive } from 'vue';
//
const state = reactive({
isShowDialog: false,
ruleForm: {
roleName: '', //
roleSign: '', //
sort: 0, //
status: true, //
describe: '', //
},
menuData: [] as TreeType[],
menuProps: {
children: 'children',
label: 'label',
},
});
//
const openDialog = (row: RowRoleType) => {
state.ruleForm = row;
state.isShowDialog = true;
getMenuData();
};
//
const closeDialog = () => {
state.isShowDialog = false;
};
//
const onCancel = () => {
closeDialog();
};
//
const onSubmit = () => {
closeDialog();
};
//
const getMenuData = () => {
state.menuData = [
{
id: 1,
label: '系统管理',
children: [
{
id: 11,
label: '菜单管理',
children: [
{
id: 111,
label: '菜单新增',
},
{
id: 112,
label: '菜单修改',
},
{
id: 113,
label: '菜单删除',
},
{
id: 114,
label: '菜单查询',
},
],
},
{
id: 12,
label: '角色管理',
children: [
{
id: 121,
label: '角色新增',
},
{
id: 122,
label: '角色修改',
},
{
id: 123,
label: '角色删除',
},
{
id: 124,
label: '角色查询',
},
],
},
{
id: 13,
label: '用户管理',
children: [
{
id: 131,
label: '用户新增',
},
{
id: 132,
label: '用户修改',
},
{
id: 133,
label: '用户删除',
},
{
id: 134,
label: '用户查询',
},
],
},
],
},
{
id: 2,
label: '权限管理',
children: [
{
id: 21,
label: '前端控制',
children: [
{
id: 211,
label: '页面权限',
},
{
id: 212,
label: '页面权限',
},
],
},
{
id: 22,
label: '后端控制',
children: [
{
id: 221,
label: '页面权限',
},
],
},
],
},
];
};
//
defineExpose({
openDialog,
});
</script>
<style scoped lang="scss">
.system-edit-role-container {
.menu-data-tree {
width: 100%;
border: 1px solid var(--el-border-color);
border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
padding: 5px;
}
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="system-add-role-container">
<el-dialog title="新增角色" v-model="state.isShowDialog" width="769px">
<el-form :model="state.ruleForm" size="default" label-width="90px">
<div class="system-role-dialog-container">
<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="769px">
<el-form ref="roleDialogFormRef" :model="state.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="角色名称">
@ -43,19 +43,22 @@
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="systemAddRole">
import { reactive } from 'vue';
<script setup lang="ts" name="systemRoleDialog">
import { reactive, ref } from 'vue';
// /
const emit = defineEmits(['refresh']);
//
const roleDialogFormRef = ref();
const state = reactive({
isShowDialog: false,
ruleForm: {
roleName: '', //
roleSign: '', //
@ -68,24 +71,44 @@ const state = reactive({
children: 'children',
label: 'label',
},
dialog: {
isShowDialog: false,
type: '',
title: '',
submitTxt: '',
},
});
//
const openDialog = () => {
state.isShowDialog = true;
const openDialog = (type: string, row: RowRoleType) => {
if (type === 'edit') {
state.ruleForm = row;
state.dialog.title = '修改角色';
state.dialog.submitTxt = '修 改';
} else {
state.dialog.title = '新增角色';
state.dialog.submitTxt = '新 增';
// 使
// nextTick(() => {
// roleDialogFormRef.value.resetFields();
// });
}
state.dialog.isShowDialog = true;
getMenuData();
};
//
const closeDialog = () => {
state.isShowDialog = false;
state.dialog.isShowDialog = false;
};
//
const onCancel = () => {
closeDialog();
};
//
//
const onSubmit = () => {
closeDialog();
emit('refresh');
// if (state.dialog.type === 'add') { }
};
//
const getMenuData = () => {
@ -202,7 +225,7 @@ defineExpose({
</script>
<style scoped lang="scss">
.system-add-role-container {
.system-role-dialog-container {
.menu-data-tree {
width: 100%;
border: 1px solid var(--el-border-color);

View File

@ -9,14 +9,14 @@
</el-icon>
查询
</el-button>
<el-button size="default" type="success" class="ml10" @click="onOpenAddRole">
<el-button size="default" type="success" class="ml10" @click="onOpenAddRole('add')">
<el-icon>
<ele-FolderAdd />
</el-icon>
新增角色
</el-button>
</div>
<el-table :data="state.tableData.data" style="width: 100%">
<el-table :data="state.tableData.data" v-loading="state.tableData.loading" style="width: 100%">
<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>
@ -31,7 +31,7 @@
<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="small" text type="primary" @click="onOpenEditRole(scope.row)"
<el-button :disabled="scope.row.roleName === '超级管理员'" size="small" text type="primary" @click="onOpenEditRole('edit', scope.row)"
>修改</el-button
>
<el-button :disabled="scope.row.roleName === '超级管理员'" size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
@ -52,8 +52,7 @@
>
</el-pagination>
</div>
<AddRole ref="addRoleRef" />
<EditRole ref="editRoleRef" />
<RoleDialog ref="roleDialogRef" @refresh="getTableData()" />
</div>
</template>
@ -62,12 +61,10 @@ import { defineAsyncComponent, reactive, onMounted, ref } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
//
const AddRole = defineAsyncComponent(() => import('/@/views/system/role/component/addRole.vue'));
const EditRole = defineAsyncComponent(() => import('/@/views/system/role/component/editRole.vue'));
const RoleDialog = defineAsyncComponent(() => import('/@/views/system/role/dialog.vue'));
//
const addRoleRef = ref();
const editRoleRef = ref();
const roleDialogRef = ref();
const state = reactive<SysRoleState>({
tableData: {
data: [],
@ -81,7 +78,8 @@ const state = reactive<SysRoleState>({
},
});
//
const initTableData = () => {
const getTableData = () => {
state.tableData.loading = true;
const data = [];
for (let i = 0; i < 20; i++) {
data.push({
@ -95,14 +93,17 @@ const initTableData = () => {
}
state.tableData.data = data;
state.tableData.total = state.tableData.data.length;
setTimeout(() => {
state.tableData.loading = false;
}, 500);
};
//
const onOpenAddRole = () => {
addRoleRef.value.openDialog();
const onOpenAddRole = (type: string) => {
roleDialogRef.value.openDialog(type);
};
//
const onOpenEditRole = (row: Object) => {
editRoleRef.value.openDialog(row);
const onOpenEditRole = (type: string, row: Object) => {
roleDialogRef.value.openDialog(type, row);
};
//
const onRowDel = (row: RowRoleType) => {
@ -112,6 +113,7 @@ const onRowDel = (row: RowRoleType) => {
type: 'warning',
})
.then(() => {
getTableData();
ElMessage.success('删除成功');
})
.catch(() => {});
@ -119,14 +121,16 @@ const onRowDel = (row: RowRoleType) => {
//
const onHandleSizeChange = (val: number) => {
state.tableData.param.pageSize = val;
getTableData();
};
//
const onHandleCurrentChange = (val: number) => {
state.tableData.param.pageNum = val;
getTableData();
};
//
onMounted(() => {
initTableData();
getTableData();
});
</script>

View File

@ -1,167 +0,0 @@
<template>
<div class="system-add-user-container">
<el-dialog title="新增用户" v-model="state.isShowDialog" width="769px">
<el-form :model="state.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="账户名称">
<el-input v-model="state.ruleForm.userName" placeholder="请输入账户名称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="用户昵称">
<el-input v-model="state.ruleForm.userNickname" placeholder="请输入用户昵称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="关联角色">
<el-select v-model="state.ruleForm.roleSign" placeholder="请选择" clearable class="w100">
<el-option label="超级管理员" value="admin"></el-option>
<el-option label="普通用户" value="common"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="部门">
<el-cascader
:options="state.deptData"
:props="{ checkStrictly: true, value: 'deptName', label: 'deptName' }"
placeholder="请选择部门"
clearable
class="w100"
v-model="state.ruleForm.department"
>
<template #default="{ node, data }">
<span>{{ data.deptName }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="手机号">
<el-input v-model="state.ruleForm.phone" placeholder="请输入手机号" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="邮箱">
<el-input v-model="state.ruleForm.email" placeholder="请输入" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="性别">
<el-select v-model="state.ruleForm.sex" placeholder="请选择" clearable class="w100">
<el-option label="男" value="男"></el-option>
<el-option label="女" value="女"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="账户密码">
<el-input v-model="state.ruleForm.password" placeholder="请输入" type="password" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="账户过期">
<el-date-picker v-model="state.ruleForm.overdueTime" type="date" placeholder="请选择" class="w100"> </el-date-picker>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="用户状态">
<el-switch v-model="state.ruleForm.status" inline-prompt active-text="启" inactive-text="禁"></el-switch>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="用户描述">
<el-input v-model="state.ruleForm.describe" type="textarea" placeholder="请输入用户描述" maxlength="150"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="systemAddUser">
import { reactive, onMounted } from 'vue';
//
const state = reactive({
isShowDialog: false,
ruleForm: {
userName: '', //
userNickname: '', //
roleSign: '', //
department: [], //
phone: '', //
email: '', //
sex: '', //
password: '', //
overdueTime: '', //
status: true, //
describe: '', //
},
deptData: [] as DeptTreeType[], //
});
//
const openDialog = () => {
state.isShowDialog = true;
};
//
const closeDialog = () => {
state.isShowDialog = false;
};
//
const onCancel = () => {
closeDialog();
};
//
const onSubmit = () => {
closeDialog();
};
//
const initTableData = () => {
state.deptData.push({
deptName: 'vueNextAdmin',
createTime: new Date().toLocaleString(),
status: true,
sort: Math.random(),
describe: '顶级部门',
id: Math.random(),
children: [
{
deptName: 'IT外包服务',
createTime: new Date().toLocaleString(),
status: true,
sort: Math.random(),
describe: '总部',
id: Math.random(),
},
{
deptName: '资本控股',
createTime: new Date().toLocaleString(),
status: true,
sort: Math.random(),
describe: '分部',
id: Math.random(),
},
],
});
};
//
onMounted(() => {
initTableData();
});
//
defineExpose({
openDialog,
});
</script>

View File

@ -1,7 +1,7 @@
<template>
<div class="system-edit-user-container">
<el-dialog title="修改用户" v-model="state.isShowDialog" width="769px">
<el-form :model="state.ruleForm" size="default" label-width="90px">
<div class="system-user-dialog-container">
<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="769px">
<el-form ref="userDialogFormRef" :model="state.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="账户名称">
@ -81,19 +81,22 @@
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default"> </el-button>
<el-button type="primary" @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="systemEditUser">
import { reactive, onMounted } from 'vue';
<script setup lang="ts" name="systemUserDialog">
import { reactive, ref } from 'vue';
// /
const emit = defineEmits(['refresh']);
//
const userDialogFormRef = ref();
const state = reactive({
isShowDialog: false,
ruleForm: {
userName: '', //
userNickname: '', //
@ -108,27 +111,47 @@ const state = reactive({
describe: '', //
},
deptData: [] as DeptTreeType[], //
dialog: {
isShowDialog: false,
type: '',
title: '',
submitTxt: '',
},
});
//
const openDialog = (row: RowUserType) => {
state.ruleForm = row;
state.isShowDialog = true;
const openDialog = (type: string, row: RowUserType) => {
if (type === 'edit') {
state.ruleForm = row;
state.dialog.title = '修改用户';
state.dialog.submitTxt = '修 改';
} else {
state.dialog.title = '新增用户';
state.dialog.submitTxt = '新 增';
// 使
// nextTick(() => {
// userDialogFormRef.value.resetFields();
// });
}
state.dialog.isShowDialog = true;
getMenuData();
};
//
const closeDialog = () => {
state.isShowDialog = false;
state.dialog.isShowDialog = false;
};
//
const onCancel = () => {
closeDialog();
};
//
//
const onSubmit = () => {
closeDialog();
emit('refresh');
// if (state.dialog.type === 'add') { }
};
//
const initTableData = () => {
const getMenuData = () => {
state.deptData.push({
deptName: 'vueNextAdmin',
createTime: new Date().toLocaleString(),
@ -156,10 +179,6 @@ const initTableData = () => {
],
});
};
//
onMounted(() => {
initTableData();
});
//
defineExpose({

View File

@ -9,14 +9,14 @@
</el-icon>
查询
</el-button>
<el-button size="default" type="success" class="ml10" @click="onOpenAddUser">
<el-button size="default" type="success" class="ml10" @click="onOpenAddUser('add')">
<el-icon>
<ele-FolderAdd />
</el-icon>
新增用户
</el-button>
</div>
<el-table :data="state.tableData.data" style="width: 100%">
<el-table :data="state.tableData.data" v-loading="state.tableData.loading" style="width: 100%">
<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>
@ -34,7 +34,9 @@
<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="small" text type="primary" @click="onOpenEditUser(scope.row)">修改</el-button>
<el-button :disabled="scope.row.userName === 'admin'" size="small" text type="primary" @click="onOpenEditUser('edit', scope.row)"
>修改</el-button
>
<el-button :disabled="scope.row.userName === 'admin'" size="small" text type="primary" @click="onRowDel(scope.row)">删除</el-button>
</template>
</el-table-column>
@ -53,8 +55,7 @@
>
</el-pagination>
</el-card>
<AddUer ref="addUserRef" />
<EditUser ref="editUserRef" />
<UserDialog ref="userDialogRef" @refresh="getTableData()" />
</div>
</template>
@ -63,12 +64,10 @@ import { defineAsyncComponent, reactive, onMounted, ref } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
//
const AddUer = defineAsyncComponent(() => import('/@/views/system/user/component/addUser.vue'));
const EditUser = defineAsyncComponent(() => import('/@/views/system/user/component/editUser.vue'));
const UserDialog = defineAsyncComponent(() => import('/@/views/system/user/dialog.vue'));
//
const addUserRef = ref();
const editUserRef = ref();
const userDialogRef = ref();
const state = reactive<SysUserState>({
tableData: {
data: [],
@ -82,7 +81,8 @@ const state = reactive<SysUserState>({
});
//
const initTableData = () => {
const getTableData = () => {
state.tableData.loading = true;
const data = [];
for (let i = 0; i < 2; i++) {
data.push({
@ -102,14 +102,17 @@ const initTableData = () => {
}
state.tableData.data = data;
state.tableData.total = state.tableData.data.length;
setTimeout(() => {
state.tableData.loading = false;
}, 500);
};
//
const onOpenAddUser = () => {
addUserRef.value.openDialog();
const onOpenAddUser = (type: string) => {
userDialogRef.value.openDialog(type);
};
//
const onOpenEditUser = (row: RowUserType) => {
editUserRef.value.openDialog(row);
const onOpenEditUser = (type: string, row: RowUserType) => {
userDialogRef.value.openDialog(type, row);
};
//
const onRowDel = (row: RowUserType) => {
@ -119,6 +122,7 @@ const onRowDel = (row: RowUserType) => {
type: 'warning',
})
.then(() => {
getTableData();
ElMessage.success('删除成功');
})
.catch(() => {});
@ -126,14 +130,16 @@ const onRowDel = (row: RowUserType) => {
//
const onHandleSizeChange = (val: number) => {
state.tableData.param.pageSize = val;
getTableData();
};
//
const onHandleCurrentChange = (val: number) => {
state.tableData.param.pageNum = val;
getTableData();
};
//
onMounted(() => {
initTableData();
getTableData();
});
</script>

View File

@ -3,7 +3,7 @@ import { resolve } from 'path';
import { defineConfig, loadEnv, ConfigEnv } from 'vite';
import vueSetupExtend from 'vite-plugin-vue-setup-extend';
const pathResolve = (dir: string): any => {
const pathResolve = (dir: string) => {
return resolve(__dirname, '.', dir);
};
@ -22,6 +22,7 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
host: '0.0.0.0',
port: env.VITE_PORT as unknown as number,
open: env.VITE_OPEN,
hmr: true,
proxy: {
'/gitee': {
target: 'https://gitee.com',
@ -33,7 +34,6 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
},
build: {
outDir: 'dist',
sourcemap: false,
chunkSizeWarningLimit: 1500,
rollupOptions: {
output: {