提交
This commit is contained in:
parent
7423baea13
commit
b2b8ddf114
@ -21,3 +21,42 @@ export function getMenuList(params?) {
|
|||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 获取菜单详情
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export function getMenuDetail(id) {
|
||||||
|
return http.request({
|
||||||
|
url: '/menu/detail/'+id,
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: 添加菜单
|
||||||
|
*/
|
||||||
|
export function menuAdd(data:any) {
|
||||||
|
return http.request({
|
||||||
|
url: '/menu/add',
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: 更新菜单
|
||||||
|
*/
|
||||||
|
export function menuUpdate(data:any) {
|
||||||
|
return http.request({
|
||||||
|
url: '/menu/update',
|
||||||
|
method: 'PUT',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: 删除菜单
|
||||||
|
*/
|
||||||
|
export function menuDelete(id) {
|
||||||
|
return http.request({
|
||||||
|
url: '/menu/delete/'+id,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
@ -87,3 +87,50 @@ export function getUserList(params) {
|
|||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @description: 根据ID获取详情
|
||||||
|
*/
|
||||||
|
export function getUserDetail(userId) {
|
||||||
|
return http.request({
|
||||||
|
url: '/user/detail/'+userId,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: 添加用户
|
||||||
|
*/
|
||||||
|
export function userAdd(data:any) {
|
||||||
|
return http.request({
|
||||||
|
url: '/user/add',
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: 更新用户
|
||||||
|
*/
|
||||||
|
export function userUpdate(data:any) {
|
||||||
|
return http.request({
|
||||||
|
url: '/user/update',
|
||||||
|
method: 'PUT',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: 删除用户
|
||||||
|
*/
|
||||||
|
export function userDelete(userId) {
|
||||||
|
return http.request({
|
||||||
|
url: '/user/delete/'+userId,
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: 批量删除用户
|
||||||
|
*/
|
||||||
|
export function userBatchDelete(userId) {
|
||||||
|
return http.request({
|
||||||
|
url: '/user/batchDelete/'+userId,
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
|
}
|
13
src/components/icon/index.ts
Normal file
13
src/components/icon/index.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import * as ElementPlusIcons from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
export const EL_ICON_PREFIX = 'el-icon-'
|
||||||
|
|
||||||
|
const elIconsName: string[] = []
|
||||||
|
|
||||||
|
for (const [, component] of Object.entries(ElementPlusIcons)) {
|
||||||
|
elIconsName.push(`${EL_ICON_PREFIX}${component.name}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getElementPlusIconNames() {
|
||||||
|
return elIconsName
|
||||||
|
}
|
37
src/components/icon/index.vue
Normal file
37
src/components/icon/index.vue
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {createVNode, defineComponent, resolveComponent} from "vue";
|
||||||
|
import { ElIcon } from 'element-plus'
|
||||||
|
import { EL_ICON_PREFIX } from './index'
|
||||||
|
import svgIcon from './svg-icon.vue'
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'Icon',
|
||||||
|
props: {
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: '14px'
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: 'inherit'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
if (props.name.indexOf(EL_ICON_PREFIX) === 0) {
|
||||||
|
// el-icon
|
||||||
|
return () =>
|
||||||
|
createVNode(
|
||||||
|
ElIcon,
|
||||||
|
{
|
||||||
|
size: props.size,
|
||||||
|
color: props.color
|
||||||
|
},
|
||||||
|
() => [createVNode(resolveComponent(props.name.replace(EL_ICON_PREFIX, '')))]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
182
src/components/icon/picker.vue
Normal file
182
src/components/icon/picker.vue
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
<template>
|
||||||
|
<div class="icon-select">
|
||||||
|
<el-popover
|
||||||
|
trigger="contextmenu"
|
||||||
|
v-model:visible="state.popoverVisible"
|
||||||
|
:width="state.popoverWidth"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
@mouseover.stop="state.mouseoverSelect = true"
|
||||||
|
@mouseout.stop="state.mouseoverSelect = false"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<div class="mb-3">请选择图标</div>
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
v-for="(item, index) in iconTabsMap"
|
||||||
|
:key="index"
|
||||||
|
class="cursor-pointer text-sm ml-2"
|
||||||
|
:class="{
|
||||||
|
'text-primary': index == tabIndex
|
||||||
|
}"
|
||||||
|
@click="tabIndex = index"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="height: 280px">
|
||||||
|
<el-scrollbar>
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
<div v-for="item in iconNamesFliter" :key="item" style="margin: 4px">
|
||||||
|
<el-button @click="handleSelect(item)">
|
||||||
|
<icon :name="item" :size="18" />
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #reference>
|
||||||
|
<el-input
|
||||||
|
ref="inputRef"
|
||||||
|
v-model.trim="state.inputValue"
|
||||||
|
placeholder="搜索图标"
|
||||||
|
:autofocus="false"
|
||||||
|
:disabled="disabled"
|
||||||
|
@focus="handleFocus"
|
||||||
|
@blur="handleBlur"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<div class="flex items-center" v-if="modelValue">
|
||||||
|
<el-tooltip class="flex-1 w-20" :content="modelValue" placement="top">
|
||||||
|
<icon
|
||||||
|
class="mr-1"
|
||||||
|
:key="modelValue"
|
||||||
|
:name="modelValue"
|
||||||
|
:size="16"
|
||||||
|
/>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-else>无</template>
|
||||||
|
</template>
|
||||||
|
<template #append>
|
||||||
|
<el-button>
|
||||||
|
<icon name="el-icon-Close" :size="18" @click="handleClear" />
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</template>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, nextTick, onMounted, reactive, shallowRef, watch,ref } from 'vue'
|
||||||
|
import { useEventListener } from '@vueuse/core'
|
||||||
|
import { ElInput } from 'element-plus'
|
||||||
|
import { getElementPlusIconNames } from './index'
|
||||||
|
import icon from './index.vue';
|
||||||
|
interface Props {
|
||||||
|
modelValue: string
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
withDefaults(defineProps<Props>(), {
|
||||||
|
modelValue: '',
|
||||||
|
disabled: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits<{
|
||||||
|
(e: 'update:modelValue', value: string): void
|
||||||
|
(e: 'change', value: string): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const tabIndex = ref(0)
|
||||||
|
const iconTabsMap = [
|
||||||
|
{
|
||||||
|
name: 'element图标',
|
||||||
|
icons: getElementPlusIconNames()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const inputRef = shallowRef<InstanceType<typeof ElInput>>()
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
inputValue: '',
|
||||||
|
popoverVisible: false,
|
||||||
|
popoverWidth: 0,
|
||||||
|
mouseoverSelect: false,
|
||||||
|
inputFocus: false
|
||||||
|
})
|
||||||
|
|
||||||
|
// input 框聚焦
|
||||||
|
const handleFocus = () => {
|
||||||
|
state.inputFocus = state.popoverVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// input 框失去焦点
|
||||||
|
const handleBlur = () => {
|
||||||
|
state.inputFocus = false
|
||||||
|
state.popoverVisible = state.mouseoverSelect
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选中图标
|
||||||
|
const handleSelect = (icon: string) => {
|
||||||
|
state.mouseoverSelect = state.popoverVisible = false
|
||||||
|
emits('update:modelValue', icon)
|
||||||
|
emits('change', icon)
|
||||||
|
}
|
||||||
|
//取消选中
|
||||||
|
const handleClear = () => {
|
||||||
|
emits('update:modelValue', '')
|
||||||
|
emits('change', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据输入框内容塞选
|
||||||
|
const iconNamesFliter = computed(() => {
|
||||||
|
const iconNames = iconTabsMap[tabIndex.value]?.icons ?? []
|
||||||
|
if (!state.inputValue) {
|
||||||
|
return iconNames
|
||||||
|
}
|
||||||
|
const inputValue = state.inputValue.toLowerCase()
|
||||||
|
return iconNames.filter((icon: string) => {
|
||||||
|
if (icon.toLowerCase().indexOf(inputValue) !== -1) {
|
||||||
|
return icon
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取 input 的宽度
|
||||||
|
const getInputWidth = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
const inputWidth = inputRef.value?.$el.offsetWidth
|
||||||
|
state.popoverWidth = inputWidth < 300 ? 300 : inputWidth
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//监听body点击事件
|
||||||
|
useEventListener(document.body, 'click', () => {
|
||||||
|
state.popoverVisible = state.inputFocus || state.mouseoverSelect ? true : false
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => state.popoverVisible,
|
||||||
|
async (value) => {
|
||||||
|
await nextTick()
|
||||||
|
if (value) {
|
||||||
|
inputRef.value?.focus()
|
||||||
|
} else {
|
||||||
|
inputRef.value?.blur()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getInputWidth()
|
||||||
|
})
|
||||||
|
</script>
|
39
src/components/icon/svg-icon.vue
Normal file
39
src/components/icon/svg-icon.vue
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<template>
|
||||||
|
<svg aria-hidden="true" :style="styles">
|
||||||
|
<use :xlink:href="symbolId" fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { addUnit } from '@/util/util'
|
||||||
|
import type { CSSProperties } from 'vue'
|
||||||
|
import {computed, defineComponent} from "vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 16
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: 'inherit'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const symbolId = computed(() => `#${props.name}`)
|
||||||
|
const styles = computed<CSSProperties>(() => {
|
||||||
|
return {
|
||||||
|
width: addUnit(props.size),
|
||||||
|
height: addUnit(props.size),
|
||||||
|
color: props.color
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return { symbolId, styles }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
@ -1,19 +1,23 @@
|
|||||||
import { ObjectDirective } from 'vue';
|
import { ObjectDirective } from 'vue';
|
||||||
import { usePermission } from '@/hooks/web/usePermission';
|
import { usePermission } from '@/hooks/web/usePermission';
|
||||||
|
|
||||||
export const permission: ObjectDirective = {
|
export const perm: ObjectDirective = {
|
||||||
mounted(el: HTMLButtonElement, binding) {
|
mounted(el: HTMLButtonElement, binding) {
|
||||||
if (binding.value == undefined) return;
|
if (binding.value == undefined) return;
|
||||||
const { action, effect } = binding.value;
|
const { action, effect } = binding.value;
|
||||||
const { hasPermission } = usePermission();
|
const { hasSomePermission } = usePermission();
|
||||||
if (!hasPermission(action)) {
|
if (!hasSomePermission(binding.value)) {
|
||||||
if (effect == 'disabled') {
|
el.parentNode.removeChild(el)
|
||||||
el.disabled = true;
|
}
|
||||||
el.style['disabled'] = 'disabled';
|
},
|
||||||
el.classList.add('is-disabled');
|
};
|
||||||
} else {
|
export const perms: ObjectDirective = {
|
||||||
el.remove();
|
mounted(el: HTMLButtonElement, binding) {
|
||||||
}
|
if (binding.value == undefined) return;
|
||||||
|
const { action, effect } = binding.value;
|
||||||
|
const { hasEveryPermission } = usePermission();
|
||||||
|
if (!hasEveryPermission(binding.value)) {
|
||||||
|
el.parentNode.removeChild(el)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -30,7 +30,7 @@ export function usePermission() {
|
|||||||
function hasEveryPermission(accesses: string[]): boolean {
|
function hasEveryPermission(accesses: string[]): boolean {
|
||||||
const permissionsList = userStore.getPermissions;
|
const permissionsList = userStore.getPermissions;
|
||||||
if (Array.isArray(accesses)) {
|
if (Array.isArray(accesses)) {
|
||||||
return permissionsList.every((access: any) => accesses.includes(access.value));
|
return accesses.every((access: any) => permissionsList.includes(access));
|
||||||
}
|
}
|
||||||
throw new Error(`[hasEveryPermission]: ${accesses} should be a array !`);
|
throw new Error(`[hasEveryPermission]: ${accesses} should be a array !`);
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ export function usePermission() {
|
|||||||
function hasSomePermission(accesses: string[]): boolean {
|
function hasSomePermission(accesses: string[]): boolean {
|
||||||
const permissionsList = userStore.getPermissions;
|
const permissionsList = userStore.getPermissions;
|
||||||
if (Array.isArray(accesses)) {
|
if (Array.isArray(accesses)) {
|
||||||
return permissionsList.some((access: any) => accesses.includes(access.value));
|
return accesses.some((access: any) => permissionsList.includes(access));
|
||||||
}
|
}
|
||||||
throw new Error(`[hasSomePermission]: ${accesses} should be a array !`);
|
throw new Error(`[hasSomePermission]: ${accesses} should be a array !`);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import './styles/index.scss';
|
|||||||
import 'element-plus/theme-chalk/display.css';
|
import 'element-plus/theme-chalk/display.css';
|
||||||
import 'element-plus/theme-chalk/dark/css-vars.css';
|
import 'element-plus/theme-chalk/dark/css-vars.css';
|
||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
|
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import router, { setupRouter } from './router';
|
import router, { setupRouter } from './router';
|
||||||
@ -12,8 +12,10 @@ import { setupStore } from '@/store';
|
|||||||
import { setupElement, setupDirectives, setupCustomComponents } from '@/plugins';
|
import { setupElement, setupDirectives, setupCustomComponents } from '@/plugins';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = createApp(App);
|
const app=createApp(App)
|
||||||
|
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||||
|
app.component(key, component)
|
||||||
|
}
|
||||||
// 全局完整引入 element 组件
|
// 全局完整引入 element 组件
|
||||||
setupElement(app);
|
setupElement(app);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { App } from 'vue';
|
import { App } from 'vue';
|
||||||
|
|
||||||
import { permission } from '@/directives/permission';
|
import { perm,perms } from '@/directives/permission';
|
||||||
import { scrollBar } from '@/directives/scrollBar';
|
import { scrollBar } from '@/directives/scrollBar';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -8,6 +8,7 @@ import { scrollBar } from '@/directives/scrollBar';
|
|||||||
* @param app
|
* @param app
|
||||||
*/
|
*/
|
||||||
export function setupDirectives(app: App) {
|
export function setupDirectives(app: App) {
|
||||||
app.directive('permission', permission); // 权限控制指令(演示)
|
app.directive('perm', perm); // 权限控制指令 (是否包含其中某个权限)
|
||||||
|
app.directive('perms', perms); // 权限控制指令 (是否包含所有权限)
|
||||||
app.directive('scrollBar', scrollBar); // 滚动条
|
app.directive('scrollBar', scrollBar); // 滚动条
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,11 @@ const modules: any = import.meta.glob('./modules/**/*.ts', { eager: true });
|
|||||||
|
|
||||||
const routeModuleList: RouteRecordRaw[] = [];
|
const routeModuleList: RouteRecordRaw[] = [];
|
||||||
|
|
||||||
|
const modulesVue = import.meta.glob('/src/views/**/*.vue')
|
||||||
|
export function getModulesKey() {
|
||||||
|
return Object.keys(modulesVue).map((item) => item.replace('/src/views/', '').replace('.vue', ''))
|
||||||
|
}
|
||||||
|
|
||||||
Object.keys(modules).forEach((key) => {
|
Object.keys(modules).forEach((key) => {
|
||||||
const mod = modules[key].default || {};
|
const mod = modules[key].default || {};
|
||||||
const modList = Array.isArray(mod) ? [...mod] : [mod];
|
const modList = Array.isArray(mod) ? [...mod] : [mod];
|
||||||
|
233
src/utils/auth.ts
Normal file
233
src/utils/auth.ts
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
import {isObject} from "@vue/shared";
|
||||||
|
import {cloneDeep} from "lodash";
|
||||||
|
import type {RouteLocationNormalizedLoaded} from "vue-router";
|
||||||
|
import {RouteLocationNormalized, useRoute} from "vue-router";
|
||||||
|
import {watch} from "vue";
|
||||||
|
import {
|
||||||
|
ElLoading,
|
||||||
|
ElMessage,
|
||||||
|
ElMessageBox
|
||||||
|
} from "element-plus";
|
||||||
|
import type {LoadingInstance} from "element-plus/es/components/loading/src/loading";
|
||||||
|
|
||||||
|
|
||||||
|
export function isExternal(path: string) {
|
||||||
|
return /^(https?:|mailto:|tel:)/.test(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取正确的路经
|
||||||
|
* @param {String} path 数据
|
||||||
|
*/
|
||||||
|
export function getNormalPath(path: string) {
|
||||||
|
if (path.length === 0 || !path || path == "undefined") {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
const newPath = path.replace("//", "/");
|
||||||
|
const length = newPath.length;
|
||||||
|
if (newPath[length - 1] === "/") {
|
||||||
|
return newPath.slice(0, length - 1);
|
||||||
|
}
|
||||||
|
return newPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isEmpty = (value: unknown) => {
|
||||||
|
return value == null && typeof value == "undefined";
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description对象格式化为Query语法
|
||||||
|
* @param { Object } params
|
||||||
|
* @return {string} Query语法
|
||||||
|
*/
|
||||||
|
export function objectToQuery(params: Record<string, any>): string {
|
||||||
|
let query = "";
|
||||||
|
for (const props of Object.keys(params)) {
|
||||||
|
const value = params[props];
|
||||||
|
const part = encodeURIComponent(props) + "=";
|
||||||
|
if (!isEmpty(value)) {
|
||||||
|
if (isObject(value)) {
|
||||||
|
for (const key of Object.keys(value)) {
|
||||||
|
if (!isEmpty(value[key])) {
|
||||||
|
const params = props + "[" + key + "]";
|
||||||
|
const subPart = encodeURIComponent(params) + "=";
|
||||||
|
query += subPart + encodeURIComponent(value[key]) + "&";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
query += part + encodeURIComponent(value) + "&";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return query.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addUnit = (value: string | number, unit = "px") => {
|
||||||
|
return !Object.is(Number(value), NaN) ? `${value}${unit}` : value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export function useWatchRoute(callback: (route: RouteLocationNormalizedLoaded) => void) {
|
||||||
|
const route = useRoute();
|
||||||
|
watch(
|
||||||
|
route,
|
||||||
|
() => {
|
||||||
|
callback(route);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
route
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// declare type MessageType = '' | 'success' | 'warning' | 'info' | 'error';
|
||||||
|
|
||||||
|
export function confirm(msg: string, type: "warning") {
|
||||||
|
return ElMessageBox.confirm(msg, "温馨提示", {
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
type: type
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function message(msg: string, type: string = "success") {
|
||||||
|
ElMessage[type](msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let loadingInstance = null;
|
||||||
|
|
||||||
|
// 打开全局loading
|
||||||
|
export function loading(msg: string) {
|
||||||
|
loadingInstance = ElLoading.service({
|
||||||
|
lock: true,
|
||||||
|
text: msg
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭全局loading
|
||||||
|
export function closeLoading() {
|
||||||
|
loadingInstance?.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const arrayToTree = (
|
||||||
|
data: any[],
|
||||||
|
props = {id: "id", parentId: "pid", children: "children"}
|
||||||
|
) => {
|
||||||
|
data = cloneDeep(data);
|
||||||
|
const {id, parentId, children} = props;
|
||||||
|
const result: any[] = [];
|
||||||
|
const map = new Map();
|
||||||
|
data.forEach((item) => {
|
||||||
|
map.set(item[id], item);
|
||||||
|
const parent = map.get(item[parentId]);
|
||||||
|
if (parent) {
|
||||||
|
parent[children] = parent[children] ?? [];
|
||||||
|
parent[children].push(item);
|
||||||
|
} else {
|
||||||
|
result.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 树转数组,队列实现广度优先遍历
|
||||||
|
* @param {Array} data 数据
|
||||||
|
* @param {Object} props `{ children: 'children' }`
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const treeToArray = (data: any[], props = {children: "children"}) => {
|
||||||
|
data = cloneDeep(data);
|
||||||
|
const {children} = props;
|
||||||
|
const newData = [];
|
||||||
|
const queue: any[] = [];
|
||||||
|
data.forEach((child: any) => queue.push(child));
|
||||||
|
while (queue.length) {
|
||||||
|
const item: any = queue.shift();
|
||||||
|
if (item[children]) {
|
||||||
|
item[children].forEach((child: any) => queue.push(child));
|
||||||
|
delete item[children];
|
||||||
|
}
|
||||||
|
newData.push(item);
|
||||||
|
}
|
||||||
|
return newData;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 数组转树型结构
|
||||||
|
*/
|
||||||
|
export const buildTree =(array)=> {
|
||||||
|
const tree = {}; // 用于存储树形结构的临时对象
|
||||||
|
const result = []; // 最终的树形结构数组
|
||||||
|
|
||||||
|
array.forEach((item) => {
|
||||||
|
tree[item.id] = { ...item, children: [] }; // 初始化树节点
|
||||||
|
});
|
||||||
|
|
||||||
|
array.forEach((item) => {
|
||||||
|
if (item.parentId === 0) {
|
||||||
|
result.push(tree[item.id]); // 根节点直接添加到结果数组
|
||||||
|
} else {
|
||||||
|
tree[item.parentId].children.push(tree[item.id]); // 子节点添加到父节点的children数组
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
export const getComponentName = (route: RouteLocationNormalized) => {
|
||||||
|
return route.matched[route.matched.length - 1]?.components?.default?.name;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export function streamFileDownload(file: any, fileName = "文件名称.zip") {
|
||||||
|
const blob = new Blob([file], {type: "application/octet-stream;charset=UTF-8"});
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.style.display = "none";
|
||||||
|
link.href = url;
|
||||||
|
link.setAttribute("download", fileName);
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link); // 下载完成移除元素
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function filterName(list: any, value: any, key: string = "value", label: string = "label") {
|
||||||
|
let name = "";
|
||||||
|
let child = list.find(item => item[key] == value);
|
||||||
|
name = child ? child[label] : "";
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function filterDatetime(value: any, format: string = "yyyy-MM-dd") {
|
||||||
|
if (value) {
|
||||||
|
return dateFtt(format, new Date(parseInt(value)));
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function dateFtt(fmt, date) { //author: meizz
|
||||||
|
var o = {
|
||||||
|
"M+": date.getMonth() + 1, //月份
|
||||||
|
"d+": date.getDate(), //日
|
||||||
|
"h+": date.getHours(), //小时
|
||||||
|
"m+": date.getMinutes(), //分
|
||||||
|
"s+": date.getSeconds(), //秒
|
||||||
|
"q+": Math.floor((date.getMonth() + 3) / 3), //季度
|
||||||
|
"S": date.getMilliseconds() //毫秒
|
||||||
|
};
|
||||||
|
if (/(y+)/.test(fmt))
|
||||||
|
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
|
||||||
|
for (var k in o)
|
||||||
|
if (new RegExp("(" + k + ")").test(fmt))
|
||||||
|
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
|
||||||
|
return fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
18
src/utils/useLockFn.ts
Normal file
18
src/utils/useLockFn.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export function useLockFn(fn: (...args: any[]) => Promise<any>) {
|
||||||
|
const isLock = ref(false)
|
||||||
|
const lockFn = async (...args: any[]) => {
|
||||||
|
if (isLock.value) return
|
||||||
|
isLock.value = true
|
||||||
|
try {
|
||||||
|
await fn(...args)
|
||||||
|
} finally{
|
||||||
|
isLock.value=false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
isLock,
|
||||||
|
lockFn
|
||||||
|
}
|
||||||
|
}
|
318
src/views/system/menu/edit.vue
Normal file
318
src/views/system/menu/edit.vue
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="props.visible"
|
||||||
|
:title="props.menuId?'编辑':'新增'"
|
||||||
|
:append-to-body="true"
|
||||||
|
width="600"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:before-close="dialogClose"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
label-width="80px"
|
||||||
|
>
|
||||||
|
<el-form-item label="菜单类型" prop="type" required>
|
||||||
|
<el-radio-group v-model="formData.type">
|
||||||
|
<el-radio :label="0">菜单</el-radio>
|
||||||
|
<el-radio :label="1">按钮</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="父级菜单"
|
||||||
|
prop="pid"
|
||||||
|
:rules="{ required: true, message: '请选择父级菜单', trigger: 'change' }"
|
||||||
|
>
|
||||||
|
<el-tree-select
|
||||||
|
class="flex-1"
|
||||||
|
v-model="formData.pid"
|
||||||
|
:data="menuOptions"
|
||||||
|
clearable
|
||||||
|
node-key="id"
|
||||||
|
:props="{
|
||||||
|
label: 'name',
|
||||||
|
}"
|
||||||
|
:default-expand-all="true"
|
||||||
|
placeholder="请选择父级菜单"
|
||||||
|
check-strictly
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="菜单名称"
|
||||||
|
prop="menuName"
|
||||||
|
:rules="{ required: true, message: '请输入菜单名称', trigger: 'blur' }"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.menuName"
|
||||||
|
placeholder="请输入菜单名称"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="formData.type ==0"
|
||||||
|
label="菜单图标"
|
||||||
|
prop="icon"
|
||||||
|
>
|
||||||
|
<IconPicker class="flex-1" v-model="formData.icon"/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item
|
||||||
|
v-if="formData.type==0"
|
||||||
|
label="路由路径"
|
||||||
|
prop="paths"
|
||||||
|
:rules="{ required: true, message: '请输入路由路径', trigger: 'blur' }"
|
||||||
|
>
|
||||||
|
<div class="flex-1">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.paths"
|
||||||
|
placeholder="请输入路由路径"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
<div class="form-tips">
|
||||||
|
访问的路由地址,如:`admin`,如外网地址需内链访问则以`http(s)://`开头
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="formData.type == 0"
|
||||||
|
label="组件路径"
|
||||||
|
prop="component"
|
||||||
|
:rules="{ required: true, message: '请输入组件路径', trigger: 'blur' }"
|
||||||
|
>
|
||||||
|
<div class="flex-1">
|
||||||
|
<el-autocomplete
|
||||||
|
style="width: 100%"
|
||||||
|
v-model="formData.component"
|
||||||
|
:fetch-suggestions="querySearch"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入组件路径"
|
||||||
|
/>
|
||||||
|
<div class="form-tips">
|
||||||
|
访问的组件路径,如:`permission/admin/index`,默认在`views`目录下
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="选中菜单"
|
||||||
|
prop="selected"
|
||||||
|
v-if="formData.type== 0"
|
||||||
|
>
|
||||||
|
<div class="flex-1">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.selected"
|
||||||
|
placeholder="请输入路由路径"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
<div class="form-tips">
|
||||||
|
访问详情页面,编辑页面时,菜单高亮显示,如`/consumer/lists`
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="权限字符"
|
||||||
|
prop="perms"
|
||||||
|
>
|
||||||
|
<div class="flex-1">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.perms"
|
||||||
|
placeholder="请输入权限字符"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
<div class="form-tips">
|
||||||
|
将作为server端API验权使用,如`system:admin:list`,请谨慎修改
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="formData.type == 0"
|
||||||
|
label="路由参数"
|
||||||
|
prop="params"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.params"
|
||||||
|
placeholder="请输入路由参数"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-tips">
|
||||||
|
访问路由的默认传递参数,如:`{"menuId": 1, "name":
|
||||||
|
"admin"}`或`menuId=1&name=admin`
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="formData.type ==0"
|
||||||
|
label="是否显示"
|
||||||
|
prop="isShow"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<el-radio-group v-model="formData.isShow">
|
||||||
|
<el-radio :label="1">显示</el-radio>
|
||||||
|
<el-radio :label="0">隐藏</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
<div class="form-tips">
|
||||||
|
选择隐藏则路由将不会出现在侧边栏,但仍然可以访问
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="formData.type ==0"
|
||||||
|
label="菜单状态"
|
||||||
|
prop="isDisable"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<el-radio-group v-model="formData.isDisable">
|
||||||
|
<el-radio :label="0">正常</el-radio>
|
||||||
|
<el-radio :label="1">停用</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
<div class="form-tips">
|
||||||
|
选择停用则路由将不会出现在侧边栏,也不能被访问
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="菜单排序" prop="menuSort">
|
||||||
|
<div>
|
||||||
|
<el-input-number v-model="formData.menuSort" :max="9999"/>
|
||||||
|
<div class="form-tips">数值越小越排前</div>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="dialogClose">取消</el-button>
|
||||||
|
<el-button :loading="subLoading" type="primary" @click="submit">
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type {FormInstance} from "element-plus";
|
||||||
|
import { menuAdd,menuUpdate,getMenuList } from '@/api/system/menu';
|
||||||
|
import {onMounted, reactive, readonly, ref, shallowRef} from "vue";
|
||||||
|
import {getModulesKey} from "@/router";
|
||||||
|
import {arrayToTree, treeToArray,message,buildTree} from "@/utils/auth";
|
||||||
|
import {useLockFn} from "@/utils/useLockFn";
|
||||||
|
import IconPicker from "@/components/icon/picker.vue";
|
||||||
|
const props = defineProps({
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
menuId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
pid: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const emit = defineEmits(["success","update:visible"]);
|
||||||
|
const formRef = shallowRef<FormInstance>();
|
||||||
|
|
||||||
|
const componentsOptions = ref(getModulesKey());
|
||||||
|
const querySearch = (queryString: string, cb: any) => {
|
||||||
|
const results = queryString
|
||||||
|
? componentsOptions.value.filter((item) =>
|
||||||
|
item.toLowerCase().includes(queryString.toLowerCase())
|
||||||
|
)
|
||||||
|
: componentsOptions.value;
|
||||||
|
cb(results.map((item) => ({value: item})));
|
||||||
|
};
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
menuId: "",
|
||||||
|
//父级id
|
||||||
|
pid: 0,
|
||||||
|
//类型
|
||||||
|
type: 0,
|
||||||
|
//图标
|
||||||
|
menuIcon: "",
|
||||||
|
//名称
|
||||||
|
menuName: "",
|
||||||
|
//排序号
|
||||||
|
menuSort: 0,
|
||||||
|
// 路由路径
|
||||||
|
paths: "",
|
||||||
|
//权限链接
|
||||||
|
perms: "",
|
||||||
|
//前端组件
|
||||||
|
component: "",
|
||||||
|
//选中路径
|
||||||
|
selected: "",
|
||||||
|
//路由参数
|
||||||
|
params: "",
|
||||||
|
//是否缓存 0=否, 1=是
|
||||||
|
isCache: 0,
|
||||||
|
//是否显示 0=否, 1=是
|
||||||
|
isShow: 1,
|
||||||
|
//是否禁用 0=否, 1=是
|
||||||
|
isDisable: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
const dialogClose = () => {
|
||||||
|
emit("update:visible", false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const menuOptions = ref<any[]>([]);
|
||||||
|
|
||||||
|
const getMenu = async () => {
|
||||||
|
const data: any = await getMenuList();
|
||||||
|
const menu: any = {id: 0, name: "顶级", children: []};
|
||||||
|
const lists = buildTree(data)
|
||||||
|
console.log(lists)
|
||||||
|
menu.children = arrayToTree(
|
||||||
|
treeToArray(lists).filter((item) => item.type ==0)
|
||||||
|
);
|
||||||
|
menuOptions.value.push(menu);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
await formRef.value?.validate();
|
||||||
|
props.menuId ? await menuUpdate(formData) : await menuAdd(formData);
|
||||||
|
message("操作成功");
|
||||||
|
emit("update:visible", false);
|
||||||
|
emit("success");
|
||||||
|
};
|
||||||
|
|
||||||
|
const { isLock:subLoading,lockFn: submit } = useLockFn(handleSubmit);
|
||||||
|
|
||||||
|
|
||||||
|
const setFormData = (data: Record<any, any>) => {
|
||||||
|
for (const key in formData) {
|
||||||
|
if (data[key] != null && data[key] != undefined) {
|
||||||
|
formData[key] = data[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDetail = async () => {
|
||||||
|
const data = await api.menuDetail(
|
||||||
|
props.menuId
|
||||||
|
);
|
||||||
|
setFormData(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
emit("close");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getMenu()
|
||||||
|
if (props.menuId) {
|
||||||
|
getDetail()
|
||||||
|
}else{
|
||||||
|
formData.pid=props.pid
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
@ -1,258 +1,154 @@
|
|||||||
<template>
|
<template>
|
||||||
<PageWrapper title="菜单权限管理">
|
<div class="menu-index">
|
||||||
<el-row :gutter="10" class="mt-3">
|
<el-card :bordered="false" class="pt-3 mb-3 proCard">
|
||||||
<el-col :xs="24" :sm="24" :md="12" :lg="6" :xl="6">
|
<div>
|
||||||
<el-card shadow="hover" class="border-0" size="small">
|
<el-button type="primary" @click="handleAdd()">
|
||||||
<template #header>
|
<template #icon>
|
||||||
<el-space>
|
<el-icon>
|
||||||
<el-dropdown trigger="hover" @command="selectAddMenu">
|
<plus />
|
||||||
<el-button type="primary" ghost icon-placement="right">
|
</el-icon>
|
||||||
添加菜单
|
|
||||||
<div class="flex items-center">
|
|
||||||
<el-icon class="el-input__icon" size="14">
|
|
||||||
<DownOutlined />
|
|
||||||
</el-icon>
|
|
||||||
</div>
|
|
||||||
</el-button>
|
|
||||||
<template #dropdown>
|
|
||||||
<el-dropdown-menu>
|
|
||||||
<el-dropdown-item
|
|
||||||
v-for="item in addMenuOptions"
|
|
||||||
:key="item.key"
|
|
||||||
:disabled="item.disabled"
|
|
||||||
:command="item.key"
|
|
||||||
>{{ item.label }}</el-dropdown-item
|
|
||||||
>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
|
||||||
</el-dropdown>
|
|
||||||
<el-button plain icon-placement="left" @click="packHandle">
|
|
||||||
全部{{ expandAllStatus ? '收起' : '展开' }}
|
|
||||||
<el-icon class="el-icon--right" v-if="expandAllStatus"><arrow-up /> </el-icon>
|
|
||||||
<el-icon class="el-icon--right" v-else><arrow-down /></el-icon>
|
|
||||||
</el-button>
|
|
||||||
</el-space>
|
|
||||||
</template>
|
</template>
|
||||||
<div class="w-full menu">
|
新增
|
||||||
<el-input type="input" v-model="filterText" placeholder="输入菜单名称搜索">
|
</el-button>
|
||||||
<template #suffix>
|
<el-button @click="handleExpand" v-perm="['sys:user:add']"> 展开/折叠</el-button>
|
||||||
<el-icon class="cursor-pointer el-input__icon" :size="18">
|
</div>
|
||||||
<SearchOutlined />
|
</el-card>
|
||||||
</el-icon>
|
<el-card :bordered="false" class="pt-3 mb-3 proCard">
|
||||||
</template>
|
<el-table border v-loading="loading" ref="tableRef" :data="lists" row-key="id"
|
||||||
</el-input>
|
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }">
|
||||||
<div class="py-3 menu-list" v-loading="loading">
|
<el-table-column label="菜单名称" prop="name" min-width="150" show-overflow-tooltip/>
|
||||||
<el-tree
|
<el-table-column align="center" label="类型" prop="type" min-width="80">
|
||||||
ref="treeRef"
|
<template #default="{ row }">
|
||||||
show-checkbox
|
<div v-if="row.type ==0">菜单</div>
|
||||||
node-key="key"
|
<div v-else-if="row.type == 1">按钮</div>
|
||||||
:data="treeData"
|
|
||||||
:filter-node-method="filterNode"
|
|
||||||
style="max-height: 650px; overflow: hidden"
|
|
||||||
@current-change="currentChange"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
<el-col :xs="24" :sm="24" :md="12" :lg="18" :xl="18">
|
|
||||||
<el-card class="border-0" shadow="hover" size="small">
|
|
||||||
<template #header>
|
|
||||||
<el-space>
|
|
||||||
<el-icon class="el-input__icon" size="18">
|
|
||||||
<FormOutlined />
|
|
||||||
</el-icon>
|
|
||||||
<span>编辑菜单{{ treeItemTitle ? `:${treeItemTitle}` : '' }}</span>
|
|
||||||
</el-space>
|
|
||||||
</template>
|
</template>
|
||||||
<el-alert type="info" closable> 从菜单列表选择一项后,进行编辑</el-alert>
|
</el-table-column>
|
||||||
<el-form
|
<el-table-column align="center" label="图标" prop="icon" min-width="80">
|
||||||
:model="formParams"
|
<template #default="{ row }">
|
||||||
:rules="rules"
|
<icon :name="row.icon" :size="20"/>
|
||||||
ref="formRef"
|
</template>
|
||||||
label-placement="left"
|
</el-table-column>
|
||||||
:label-width="100"
|
<el-table-column align="center" label="权限标识" prop="permission" min-width="150" show-overflow-tooltip/>
|
||||||
v-if="isEditMenu"
|
<el-table-column align="center" label="状态" prop="status" min-width="100">
|
||||||
class="py-4"
|
<template #default="{ row }">
|
||||||
>
|
<el-tag v-if="row.status == 0">正常</el-tag>
|
||||||
<el-form-item label="类型" prop="type">
|
<el-tag v-else type="danger">停用</el-tag>
|
||||||
<span>{{ formParams.type === 1 ? '侧边栏菜单' : '' }}</span>
|
</template>
|
||||||
</el-form-item>
|
</el-table-column>
|
||||||
<el-form-item label="标题" prop="label">
|
<el-table-column align="center" label="排序" prop="sort" min-width="100"/>
|
||||||
<el-input placeholder="请输入标题" v-model="formParams.label" />
|
<el-table-column align="center" label="更新时间" prop="updateTime" min-width="180"></el-table-column>
|
||||||
</el-form-item>
|
<el-table-column align="center" label="操作" width="160" fixed="right">
|
||||||
<el-form-item label="副标题" prop="subtitle">
|
<template #default="{ row }">
|
||||||
<el-input placeholder="请输入副标题" v-model="formParams.subtitle" />
|
<el-button v-if="row.type !== 1" type="primary" link
|
||||||
</el-form-item>
|
@click="handleAdd(row.id)">
|
||||||
<el-form-item label="路径" prop="path">
|
新增
|
||||||
<el-input placeholder="请输入路径" v-model="formParams.path" />
|
</el-button>
|
||||||
</el-form-item>
|
<el-button type="primary" link @click="handleEdit(row)">
|
||||||
<el-form-item label="打开方式" prop="openType">
|
编辑
|
||||||
<el-radio-group v-model="formParams.openType" name="openType">
|
</el-button>
|
||||||
<el-radio :label="1">当前窗口</el-radio>
|
<el-button type="danger" link @click="handleDelete(row.id)">
|
||||||
<el-radio :label="2">新窗口</el-radio>
|
删除
|
||||||
</el-radio-group>
|
</el-button>
|
||||||
</el-form-item>
|
</template>
|
||||||
<el-form-item label="菜单权限" prop="auth">
|
</el-table-column>
|
||||||
<el-input placeholder="请输入权限,多个权限用,分割" v-model="formParams.auth" />
|
</el-table>
|
||||||
</el-form-item>
|
</el-card>
|
||||||
<el-form-item>
|
<editDialog
|
||||||
<el-space>
|
ref="editRef"
|
||||||
<el-button type="primary" :loading="subLoading" @click="formSubmit(formRef)"
|
v-if="editVisible"
|
||||||
>保存修改</el-button
|
:menuId="menuId"
|
||||||
>
|
:pid="pid"
|
||||||
<el-button @click="resetForm(formRef)">重置</el-button>
|
v-model:visible="editVisible"
|
||||||
</el-space>
|
@success="getLists"
|
||||||
</el-form-item>
|
>
|
||||||
</el-form>
|
</editDialog>
|
||||||
</el-card>
|
</div>
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<CreateDrawer ref="createDrawerRef" :title="drawerTitle" />
|
|
||||||
</PageWrapper>
|
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref, watch, unref, reactive, onMounted, computed } from 'vue';
|
|
||||||
import { ElMessage } from 'element-plus';
|
|
||||||
import { DownOutlined, SearchOutlined, FormOutlined } from '@vicons/antd';
|
|
||||||
import { getMenuList } from '@/api/system/menu';
|
|
||||||
import { getTreeItem } from '@/utils';
|
|
||||||
import CreateDrawer from './CreateDrawer.vue';
|
|
||||||
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
|
|
||||||
import type { ElForm } from 'element-plus';
|
|
||||||
|
|
||||||
type FormInstance = InstanceType<typeof ElForm>;
|
<script lang="ts" setup name="menu">
|
||||||
|
import {defineAsyncComponent, nextTick, onMounted, readonly, ref, shallowRef,onActivated} from "vue";
|
||||||
|
import {getMenuList,menuDelete} from "@/api/system/menu";
|
||||||
|
import type {ElTable} from "element-plus";
|
||||||
|
|
||||||
interface Tree {
|
const tableRef = shallowRef<InstanceType<typeof ElTable>>();
|
||||||
id: number;
|
import {confirm, message,buildTree} from "@/utils/auth";
|
||||||
label: string;
|
|
||||||
children?: Tree[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const formRef = ref<FormInstance>();
|
const editDialog = defineAsyncComponent(() =>
|
||||||
|
import('./edit.vue')
|
||||||
|
)
|
||||||
|
|
||||||
const rules = {
|
const isExpand = ref(false);
|
||||||
label: {
|
const loading = ref(false);
|
||||||
required: true,
|
const editVisible=ref(false);
|
||||||
message: '请输入标题',
|
const menuId=ref(0);
|
||||||
trigger: 'blur',
|
const pid=ref(0)
|
||||||
},
|
const lists = ref([]);
|
||||||
path: {
|
|
||||||
required: true,
|
|
||||||
message: '请输入路径',
|
|
||||||
trigger: 'blur',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const createDrawerRef = ref();
|
const getLists = async () => {
|
||||||
|
loading.value = true;
|
||||||
let treeItemKey = ref([]);
|
try {
|
||||||
|
const data = await getMenuList();
|
||||||
const treeData = ref([]);
|
lists.value = buildTree(data);
|
||||||
const treeRef = ref();
|
|
||||||
|
|
||||||
const loading = ref(true);
|
|
||||||
const subLoading = ref(false);
|
|
||||||
const isEditMenu = ref(false);
|
|
||||||
const expandAllStatus = ref(false);
|
|
||||||
const treeItemTitle = ref('');
|
|
||||||
const drawerTitle = ref('');
|
|
||||||
const filterText = ref('');
|
|
||||||
|
|
||||||
watch(filterText, (val) => {
|
|
||||||
treeRef.value!.filter(val);
|
|
||||||
});
|
|
||||||
|
|
||||||
const filterNode = (value: string, data: Tree) => {
|
|
||||||
if (!value) return true;
|
|
||||||
return data.label.includes(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const isAddSon = computed(() => {
|
|
||||||
return !treeItemKey.value.length;
|
|
||||||
});
|
|
||||||
|
|
||||||
const addMenuOptions = ref([
|
|
||||||
{
|
|
||||||
label: '添加顶级菜单',
|
|
||||||
key: 'home',
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '添加子菜单',
|
|
||||||
key: 'son',
|
|
||||||
disabled: isAddSon,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const formParams = reactive({
|
|
||||||
type: 1,
|
|
||||||
label: '',
|
|
||||||
subtitle: '',
|
|
||||||
path: '',
|
|
||||||
auth: '',
|
|
||||||
openType: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
function selectAddMenu(key: string) {
|
|
||||||
drawerTitle.value = key === 'home' ? '添加顶栏菜单' : `添加子菜单:${treeItemTitle.value}`;
|
|
||||||
openCreateDrawer();
|
|
||||||
}
|
|
||||||
|
|
||||||
function openCreateDrawer() {
|
|
||||||
const { openDrawer } = createDrawerRef.value;
|
|
||||||
openDrawer();
|
|
||||||
}
|
|
||||||
|
|
||||||
//当前选中节点变化时触发的事件
|
|
||||||
function currentChange(res) {
|
|
||||||
const key = res.key;
|
|
||||||
if (key) {
|
|
||||||
const treeItem = getTreeItem(unref(treeData), key);
|
|
||||||
treeItemKey.value = key;
|
|
||||||
treeItemTitle.value = treeItem.label;
|
|
||||||
Object.assign(formParams, treeItem);
|
|
||||||
isEditMenu.value = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function formSubmit(formEl: FormInstance | undefined) {
|
|
||||||
if (!formEl) return;
|
|
||||||
formEl.validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
ElMessage.success('抱歉,您没有该权限');
|
|
||||||
} else {
|
|
||||||
ElMessage.error('验证失败,请填写完整信息');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetForm(formEl: FormInstance | undefined) {
|
|
||||||
if (!formEl) return;
|
|
||||||
formEl.resetFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
function packHandle() {
|
|
||||||
if (expandAllStatus.value) {
|
|
||||||
treeNodeExpand(false);
|
|
||||||
expandAllStatus.value = false;
|
|
||||||
} else {
|
|
||||||
treeNodeExpand(true);
|
|
||||||
expandAllStatus.value = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function treeNodeExpand(status) {
|
|
||||||
for (var i = 0; i < treeRef.value.store._getAllNodes().length; i++) {
|
|
||||||
treeRef.value.store._getAllNodes()[i].expanded = status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
const treeMenuList = await getMenuList();
|
|
||||||
const keys = treeMenuList.map((item) => item.key);
|
|
||||||
Object.assign(formParams, keys);
|
|
||||||
treeData.value = treeMenuList;
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
});
|
} catch (error) {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAdd = async (parentId:any) => {
|
||||||
|
menuId.value=0
|
||||||
|
pid.value=parentId?parentId:0
|
||||||
|
await nextTick();
|
||||||
|
editVisible.value=true
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEdit = async (data: any) => {
|
||||||
|
menuId.value=data.menuId
|
||||||
|
await nextTick();
|
||||||
|
editVisible.value=true
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = async (menuId: number) => {
|
||||||
|
await confirm("确定要删除?");
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
await menuDelete(menuId);
|
||||||
|
message("删除成功");
|
||||||
|
getLists();
|
||||||
|
} catch (e) {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleExpand = () => {
|
||||||
|
isExpand.value = !isExpand.value;
|
||||||
|
toggleExpand(lists.value, isExpand.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleExpand = (children: any[], unfold = true) => {
|
||||||
|
for (const key in children) {
|
||||||
|
tableRef.value?.toggleRowExpansion(children[key], unfold);
|
||||||
|
if (children[key].children) {
|
||||||
|
toggleExpand(children[key].children!, unfold);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAll=()=>{
|
||||||
|
getLists();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getAll()
|
||||||
|
});
|
||||||
|
|
||||||
|
// onActivated(() => {
|
||||||
|
// getAll()
|
||||||
|
// });
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
258
src/views/system/menu/index2.vue
Normal file
258
src/views/system/menu/index2.vue
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
<template>
|
||||||
|
<PageWrapper title="菜单权限管理">
|
||||||
|
<el-row :gutter="10" class="mt-3">
|
||||||
|
<el-col :xs="24" :sm="24" :md="12" :lg="6" :xl="6">
|
||||||
|
<el-card shadow="hover" class="border-0" size="small">
|
||||||
|
<template #header>
|
||||||
|
<el-space>
|
||||||
|
<el-dropdown trigger="hover" @command="selectAddMenu">
|
||||||
|
<el-button type="primary" ghost icon-placement="right">
|
||||||
|
添加菜单
|
||||||
|
<div class="flex items-center">
|
||||||
|
<el-icon class="el-input__icon" size="14">
|
||||||
|
<DownOutlined />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
</el-button>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item
|
||||||
|
v-for="item in addMenuOptions"
|
||||||
|
:key="item.key"
|
||||||
|
:disabled="item.disabled"
|
||||||
|
:command="item.key"
|
||||||
|
>{{ item.label }}</el-dropdown-item
|
||||||
|
>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<el-button plain icon-placement="left" @click="packHandle">
|
||||||
|
全部{{ expandAllStatus ? '收起' : '展开' }}
|
||||||
|
<el-icon class="el-icon--right" v-if="expandAllStatus"><arrow-up /> </el-icon>
|
||||||
|
<el-icon class="el-icon--right" v-else><arrow-down /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</el-space>
|
||||||
|
</template>
|
||||||
|
<div class="w-full menu">
|
||||||
|
<el-input type="input" v-model="filterText" placeholder="输入菜单名称搜索">
|
||||||
|
<template #suffix>
|
||||||
|
<el-icon class="cursor-pointer el-input__icon" :size="18">
|
||||||
|
<SearchOutlined />
|
||||||
|
</el-icon>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
<div class="py-3 menu-list" v-loading="loading">
|
||||||
|
<el-tree
|
||||||
|
ref="treeRef"
|
||||||
|
show-checkbox
|
||||||
|
node-key="key"
|
||||||
|
:data="treeData"
|
||||||
|
:filter-node-method="filterNode"
|
||||||
|
style="max-height: 650px; overflow: hidden"
|
||||||
|
@current-change="currentChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="24" :md="12" :lg="18" :xl="18">
|
||||||
|
<el-card class="border-0" shadow="hover" size="small">
|
||||||
|
<template #header>
|
||||||
|
<el-space>
|
||||||
|
<el-icon class="el-input__icon" size="18">
|
||||||
|
<FormOutlined />
|
||||||
|
</el-icon>
|
||||||
|
<span>编辑菜单{{ treeItemTitle ? `:${treeItemTitle}` : '' }}</span>
|
||||||
|
</el-space>
|
||||||
|
</template>
|
||||||
|
<el-alert type="info" closable> 从菜单列表选择一项后,进行编辑</el-alert>
|
||||||
|
<el-form
|
||||||
|
:model="formParams"
|
||||||
|
:rules="rules"
|
||||||
|
ref="formRef"
|
||||||
|
label-placement="left"
|
||||||
|
:label-width="100"
|
||||||
|
v-if="isEditMenu"
|
||||||
|
class="py-4"
|
||||||
|
>
|
||||||
|
<el-form-item label="类型" prop="type">
|
||||||
|
<span>{{ formParams.type === 1 ? '侧边栏菜单' : '' }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="标题" prop="label">
|
||||||
|
<el-input placeholder="请输入标题" v-model="formParams.label" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="副标题" prop="subtitle">
|
||||||
|
<el-input placeholder="请输入副标题" v-model="formParams.subtitle" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="路径" prop="path">
|
||||||
|
<el-input placeholder="请输入路径" v-model="formParams.path" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="打开方式" prop="openType">
|
||||||
|
<el-radio-group v-model="formParams.openType" name="openType">
|
||||||
|
<el-radio :label="1">当前窗口</el-radio>
|
||||||
|
<el-radio :label="2">新窗口</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="菜单权限" prop="auth">
|
||||||
|
<el-input placeholder="请输入权限,多个权限用,分割" v-model="formParams.auth" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-space>
|
||||||
|
<el-button type="primary" :loading="subLoading" @click="formSubmit(formRef)"
|
||||||
|
>保存修改</el-button
|
||||||
|
>
|
||||||
|
<el-button @click="resetForm(formRef)">重置</el-button>
|
||||||
|
</el-space>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<CreateDrawer ref="createDrawerRef" :title="drawerTitle" />
|
||||||
|
</PageWrapper>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, watch, unref, reactive, onMounted, computed } from 'vue';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { DownOutlined, SearchOutlined, FormOutlined } from '@vicons/antd';
|
||||||
|
import { getMenuList } from '@/api/system/menu';
|
||||||
|
import { getTreeItem } from '@/utils';
|
||||||
|
import CreateDrawer from './CreateDrawer.vue';
|
||||||
|
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
|
||||||
|
import type { ElForm } from 'element-plus';
|
||||||
|
|
||||||
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
|
|
||||||
|
interface Tree {
|
||||||
|
id: number;
|
||||||
|
label: string;
|
||||||
|
children?: Tree[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
label: {
|
||||||
|
required: true,
|
||||||
|
message: '请输入标题',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
required: true,
|
||||||
|
message: '请输入路径',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const createDrawerRef = ref();
|
||||||
|
|
||||||
|
let treeItemKey = ref([]);
|
||||||
|
|
||||||
|
const treeData = ref([]);
|
||||||
|
const treeRef = ref();
|
||||||
|
|
||||||
|
const loading = ref(true);
|
||||||
|
const subLoading = ref(false);
|
||||||
|
const isEditMenu = ref(false);
|
||||||
|
const expandAllStatus = ref(false);
|
||||||
|
const treeItemTitle = ref('');
|
||||||
|
const drawerTitle = ref('');
|
||||||
|
const filterText = ref('');
|
||||||
|
|
||||||
|
watch(filterText, (val) => {
|
||||||
|
treeRef.value!.filter(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
const filterNode = (value: string, data: Tree) => {
|
||||||
|
if (!value) return true;
|
||||||
|
return data.label.includes(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isAddSon = computed(() => {
|
||||||
|
return !treeItemKey.value.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
const addMenuOptions = ref([
|
||||||
|
{
|
||||||
|
label: '添加顶级菜单',
|
||||||
|
key: 'home',
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '添加子菜单',
|
||||||
|
key: 'son',
|
||||||
|
disabled: isAddSon,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const formParams = reactive({
|
||||||
|
type: 1,
|
||||||
|
label: '',
|
||||||
|
subtitle: '',
|
||||||
|
path: '',
|
||||||
|
auth: '',
|
||||||
|
openType: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
function selectAddMenu(key: string) {
|
||||||
|
drawerTitle.value = key === 'home' ? '添加顶栏菜单' : `添加子菜单:${treeItemTitle.value}`;
|
||||||
|
openCreateDrawer();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCreateDrawer() {
|
||||||
|
const { openDrawer } = createDrawerRef.value;
|
||||||
|
openDrawer();
|
||||||
|
}
|
||||||
|
|
||||||
|
//当前选中节点变化时触发的事件
|
||||||
|
function currentChange(res) {
|
||||||
|
const key = res.key;
|
||||||
|
if (key) {
|
||||||
|
const treeItem = getTreeItem(unref(treeData), key);
|
||||||
|
treeItemKey.value = key;
|
||||||
|
treeItemTitle.value = treeItem.label;
|
||||||
|
Object.assign(formParams, treeItem);
|
||||||
|
isEditMenu.value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formSubmit(formEl: FormInstance | undefined) {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
ElMessage.success('抱歉,您没有该权限');
|
||||||
|
} else {
|
||||||
|
ElMessage.error('验证失败,请填写完整信息');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetForm(formEl: FormInstance | undefined) {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.resetFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
function packHandle() {
|
||||||
|
if (expandAllStatus.value) {
|
||||||
|
treeNodeExpand(false);
|
||||||
|
expandAllStatus.value = false;
|
||||||
|
} else {
|
||||||
|
treeNodeExpand(true);
|
||||||
|
expandAllStatus.value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function treeNodeExpand(status) {
|
||||||
|
for (var i = 0; i < treeRef.value.store._getAllNodes().length; i++) {
|
||||||
|
treeRef.value.store._getAllNodes()[i].expanded = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const treeMenuList = await getMenuList();
|
||||||
|
const keys = treeMenuList.map((item) => item.key);
|
||||||
|
Object.assign(formParams, keys);
|
||||||
|
treeData.value = treeMenuList;
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
</script>
|
@ -1,77 +0,0 @@
|
|||||||
<template>
|
|
||||||
<basicModal @register="modalRegister" ref="modalRef" @ok="okModal">
|
|
||||||
<template #default>
|
|
||||||
<BasicForm @register="registerForm">
|
|
||||||
<template #passwordSlot="{ model, field }">
|
|
||||||
<Password ref="passwordRef" v-model="model[field]" :required="false" block />
|
|
||||||
</template>
|
|
||||||
<template #rePasswordSlot="{ model, field }">
|
|
||||||
<Password ref="passwordRef" v-model="model[field]" :required="false" block />
|
|
||||||
</template>
|
|
||||||
</BasicForm>
|
|
||||||
</template>
|
|
||||||
</basicModal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref } from 'vue';
|
|
||||||
import { BasicForm, useForm } from '@/components/Form/index';
|
|
||||||
import { basicModal, useModal } from '@/components/Modal';
|
|
||||||
import { Password } from '@/components/Password';
|
|
||||||
import { schemas } from './modalSchemas';
|
|
||||||
import { ElMessage } from 'element-plus';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: '添加用户',
|
|
||||||
},
|
|
||||||
isEdit: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const title = ref(props.title);
|
|
||||||
|
|
||||||
//创建form
|
|
||||||
const [registerForm, { submit, setFieldsValue, getFieldsValue }] = useForm({
|
|
||||||
layout: 'horizontal',
|
|
||||||
colProps: { span: 12 },
|
|
||||||
labelWidth: 80,
|
|
||||||
submitButtonText: '确定',
|
|
||||||
showActionButtonGroup: false,
|
|
||||||
schemas,
|
|
||||||
});
|
|
||||||
|
|
||||||
//添加会员弹窗
|
|
||||||
const [modalRegister, { openModal, closeModal, setSubLoading, setProps }] = useModal({
|
|
||||||
title,
|
|
||||||
confirmButText: '保存',
|
|
||||||
width: 650,
|
|
||||||
});
|
|
||||||
|
|
||||||
//提交
|
|
||||||
async function okModal() {
|
|
||||||
const formRes = await submit();
|
|
||||||
if (formRes) {
|
|
||||||
// const { isEdit } = props;
|
|
||||||
console.log('表单值:', getFieldsValue());
|
|
||||||
ElMessage.error('抱歉,您没有操作权限');
|
|
||||||
//ElMessage.success(isEdit ? '修改成功' : '添加成功');
|
|
||||||
setSubLoading(false);
|
|
||||||
closeModal();
|
|
||||||
} else {
|
|
||||||
ElMessage.error('验证失败,请填写完整信息');
|
|
||||||
setSubLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//导出方法
|
|
||||||
defineExpose({
|
|
||||||
openModal,
|
|
||||||
closeModal,
|
|
||||||
setFieldsValue,
|
|
||||||
setProps,
|
|
||||||
});
|
|
||||||
</script>
|
|
206
src/views/system/user/edit.vue
Normal file
206
src/views/system/user/edit.vue
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="props.visible"
|
||||||
|
:title="props.userId?'编辑':'新增'"
|
||||||
|
width="500"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:before-close="dialogClose"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
label-width="84px"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
label="账号"
|
||||||
|
prop="username"
|
||||||
|
:rules="{ required: true, message: '请输入账号', trigger: 'blur' }"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
:disabled="isRoot"
|
||||||
|
v-model="formData.username"
|
||||||
|
placeholder="请输入账号"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="名称"
|
||||||
|
prop="nickname"
|
||||||
|
:rules="{ required: true, message: '请输入名称', trigger: 'blur' }"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.nickname"
|
||||||
|
placeholder="请输入名称"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="邮箱地址"
|
||||||
|
prop="email"
|
||||||
|
:rules="{ required: true, message: '请输入邮箱地址', trigger: 'blur' }"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formData.email"
|
||||||
|
placeholder="请输入邮箱地址"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="角色"
|
||||||
|
prop="roleIds"
|
||||||
|
:rules="{ required: true, message: '请选择角色', trigger: 'change' }"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="formData.roleIds"
|
||||||
|
:disabled="isRoot"
|
||||||
|
multiple
|
||||||
|
class="flex-1"
|
||||||
|
clearable
|
||||||
|
placeholder="请选择角色"
|
||||||
|
>
|
||||||
|
<el-option v-if="isRoot" label="系统管理员" value="0" />
|
||||||
|
<el-option
|
||||||
|
v-for="(item, index) in optionData.role"
|
||||||
|
:key="index"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="密码"
|
||||||
|
prop="password"
|
||||||
|
:rules="{ required: props.userId?false:true, message: '请输入密码', trigger: 'blur' }"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model.trim="formData.password"
|
||||||
|
show-password
|
||||||
|
autocomplete="new-password"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入密码"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item
|
||||||
|
label="确认密码"
|
||||||
|
prop="passwordConfirm"
|
||||||
|
:rules="[{ required: props.userId?false:true, message: '请输入密码', trigger: 'blur' },{validator: props.userId?void(0):passwordConfirmValidator,trigger: 'blur'}]"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model.trim="formData.passwordConfirm"
|
||||||
|
autocomplete="new-password"
|
||||||
|
show-password
|
||||||
|
clearable
|
||||||
|
placeholder="请输入确认密码"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="管理员状态" v-if="!isRoot">
|
||||||
|
<el-switch
|
||||||
|
v-model="formData.userStatus"
|
||||||
|
active-value="enable"
|
||||||
|
inactive-value="disable"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="dialogClose">取消</el-button>
|
||||||
|
<el-button :loading="subLoading" type="primary" @click="submit">
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {getUserDetail,userAdd,userUpdate} from '@/api/system/user';
|
||||||
|
import {computed, onMounted, reactive, shallowRef} from "vue";
|
||||||
|
import {message} from "@/utils/auth";
|
||||||
|
import {FormInstance} from "element-plus";
|
||||||
|
const formRef = shallowRef<FormInstance>();
|
||||||
|
import {useLockFn} from "@/utils/useLockFn";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
roleList: {
|
||||||
|
type: Array,
|
||||||
|
default: []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["success", "update:visible"]);
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
userId: 0,
|
||||||
|
email:'',
|
||||||
|
username: "",
|
||||||
|
nickname: "",
|
||||||
|
roleIds: [],
|
||||||
|
avatar: "",
|
||||||
|
password: "",
|
||||||
|
passwordConfirm: "",
|
||||||
|
userStatus: 'enable'
|
||||||
|
});
|
||||||
|
|
||||||
|
const passwordConfirmValidator = (
|
||||||
|
rule: object,
|
||||||
|
value: string,
|
||||||
|
callback: any
|
||||||
|
) => {
|
||||||
|
if (formData.password) {
|
||||||
|
if (!value) callback(new Error("请再次输入密码"));
|
||||||
|
if (value !== formData.password) callback(new Error("两次输入密码不一致!"));
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
const isRoot = computed(() => {
|
||||||
|
return formData.userId == 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
const dialogClose = () => {
|
||||||
|
emit("update:visible", false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setFormData = async (row: any) => {
|
||||||
|
const data = await getUserDetail(row.userId);
|
||||||
|
for (const key in formData) {
|
||||||
|
if (data[key] != null && data[key] != undefined) {
|
||||||
|
formData[key] = data[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
await formRef.value?.validate();
|
||||||
|
props.userId ? await userUpdate(formData) : await userAdd(formData);
|
||||||
|
emit("update:visible", false);
|
||||||
|
emit("success");
|
||||||
|
};
|
||||||
|
|
||||||
|
const { isLock:subLoading,lockFn: submit } = useLockFn(handleSubmit);
|
||||||
|
|
||||||
|
const optionData = reactive({
|
||||||
|
role:props.roleList
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.userId) {
|
||||||
|
setFormData({userId: props.userId});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
@ -1,7 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<PageWrapper>
|
<PageWrapper>
|
||||||
<el-card :bordered="false" class="pt-3 mb-3 proCard">
|
<el-card :bordered="false" class="pt-3 mb-3 proCard">
|
||||||
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset"></BasicForm>
|
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset">
|
||||||
|
<template #statusSlot="{ model, field }">
|
||||||
|
<el-input v-model="model[field]" />
|
||||||
|
</template>
|
||||||
|
</BasicForm>
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-card :bordered="false" class="proCard">
|
<el-card :bordered="false" class="proCard">
|
||||||
<BasicTable
|
<BasicTable
|
||||||
@ -10,7 +14,7 @@
|
|||||||
:row-key="(row) => row.id"
|
:row-key="(row) => row.id"
|
||||||
ref="basicTableRef"
|
ref="basicTableRef"
|
||||||
:actionColumn="actionColumn"
|
:actionColumn="actionColumn"
|
||||||
@checked-row-change="onCheckedRow"
|
@selection-change="onSelectionChange"
|
||||||
scroll-x="1200"
|
scroll-x="1200"
|
||||||
virtual-scroll
|
virtual-scroll
|
||||||
>
|
>
|
||||||
@ -18,67 +22,55 @@
|
|||||||
<el-space>
|
<el-space>
|
||||||
<el-button type="primary" @click="addUser">
|
<el-button type="primary" @click="addUser">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<el-icon class="el-input__icon">
|
<el-icon style="vertical-align: middle">
|
||||||
<PlusOutlined />
|
<plus />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</template>
|
</template>
|
||||||
新建
|
新建
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button type="danger" @click="openRemoveModal" :disabled="!rowKeys.length">
|
<el-button type="danger" @click="handleDelete" :disabled="!selectionData.length">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<el-icon class="el-input__icon">
|
<el-icon class="el-input__icon">
|
||||||
<DeleteOutlined />
|
<Delete />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</template>
|
</template>
|
||||||
删除
|
删除
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="addTable">
|
|
||||||
<template #icon>
|
|
||||||
<el-icon class="el-input__icon">
|
|
||||||
<ToTopOutlined />
|
|
||||||
</el-icon>
|
|
||||||
</template>
|
|
||||||
导入
|
|
||||||
</el-button>
|
|
||||||
</el-space>
|
</el-space>
|
||||||
</template>
|
</template>
|
||||||
</BasicTable>
|
</BasicTable>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
<editDialog
|
||||||
<basicModal @register="lightModalRegister" ref="modalRef" @ok="removeOkModal">
|
v-if="editVisible"
|
||||||
<template #default>
|
:userId="userId"
|
||||||
<p class="text-gray-600" style="padding-left: 35px"
|
:roleList="roleList"
|
||||||
>您确认要删除用户,<span strong>{{ rowKeysName }} ?</span></p
|
v-model:visible="editVisible"
|
||||||
>
|
@success="reloadTable"
|
||||||
</template>
|
>
|
||||||
</basicModal>
|
</editDialog>
|
||||||
|
|
||||||
<CreateModal ref="createModalRef" :title="createModalTitle" :isEdit="isEdit" />
|
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { h, nextTick, reactive, ref, unref } from 'vue';
|
import { h, nextTick, reactive, ref, unref ,defineAsyncComponent,onMounted} from 'vue';
|
||||||
import { ColProps, ElMessage } from 'element-plus';
|
import { ColProps, ElMessage } from 'element-plus';
|
||||||
import { BasicTable, TableAction } from '@/components/Table';
|
import { BasicTable, TableAction } from '@/components/Table';
|
||||||
import { BasicForm, useForm } from '@/components/Form/index';
|
import { BasicForm, useForm } from '@/components/Form/index';
|
||||||
import { getUserList } from '@/api/system/user';
|
import { getUserList,userDelete,userBatchDelete } from '@/api/system/user';
|
||||||
|
import {message,confirm} from "@/utils/auth";
|
||||||
|
import { getRoleAllList } from '@/api/system/role';
|
||||||
import { columns } from './columns';
|
import { columns } from './columns';
|
||||||
import { PlusOutlined, DeleteOutlined, ToTopOutlined } from '@vicons/antd';
|
|
||||||
import CreateModal from './CreateModal.vue';
|
|
||||||
import { basicModal, useModal } from '@/components/Modal';
|
|
||||||
import { schemas } from './querySchemas';
|
import { schemas } from './querySchemas';
|
||||||
|
const userId = ref(0);
|
||||||
const basicTableRef = ref();
|
const basicTableRef = ref();
|
||||||
const createModalRef = ref();
|
const editVisible = ref(false)
|
||||||
const rowKeys = ref([]);
|
const roleList = ref([])
|
||||||
const rowKeysName = ref([]);
|
const selectionData = ref([])
|
||||||
const tableData = ref();
|
const tableData = ref();
|
||||||
const isEdit = ref(false);
|
const editDialog = defineAsyncComponent(() =>
|
||||||
const createModalTitle = ref('添加用户');
|
import('./edit.vue')
|
||||||
|
)
|
||||||
const showModal = ref(false);
|
|
||||||
const formParams = reactive({
|
const formParams = reactive({
|
||||||
username: '',
|
username: '',
|
||||||
mobile: '',
|
mobile: '',
|
||||||
@ -130,49 +122,29 @@
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function addTable() {
|
|
||||||
showModal.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadDataTable = async (res) => {
|
const loadDataTable = async (res) => {
|
||||||
const result = await getUserList({ ...formParams, ...params.value, ...res });
|
const result = await getUserList({ ...formParams, ...params.value, ...res });
|
||||||
tableData.value = result.records;
|
tableData.value = result.records;
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
function onCheckedRow(keys) {
|
|
||||||
rowKeys.value = keys;
|
|
||||||
rowKeysName.value = tableData.value
|
|
||||||
.filter((item) => {
|
|
||||||
return keys.includes(item.id);
|
|
||||||
})
|
|
||||||
.map((item) => {
|
|
||||||
return item.username;
|
|
||||||
})
|
|
||||||
.join(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
function reloadTable() {
|
function reloadTable() {
|
||||||
basicTableRef.value.reload();
|
basicTableRef.value.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEdit(record: Recordable) {
|
async function handleEdit(record: Recordable) {
|
||||||
record.mobile = parseInt(record.mobile);
|
userId.value=record.row.id
|
||||||
addUser();
|
await nextTick();
|
||||||
nextTick(() => {
|
editVisible.value=true
|
||||||
isEdit.value = true;
|
|
||||||
createModalRef.value.setProps({ title: '编辑用户' });
|
|
||||||
createModalRef.value.setFieldsValue({
|
|
||||||
...unref(record),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDelete(record: Recordable) {
|
async function handleDelete(record: Recordable) {
|
||||||
rowKeysName.value = record.row.username;
|
await confirm('确定要删除?');
|
||||||
openRemoveModal();
|
selectionData.value.length>0? await userBatchDelete(selectionData.value.join()):await userDelete(record.row.id);
|
||||||
|
message("删除成功");
|
||||||
|
reloadTable()
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSubmit(values: Recordable) {
|
function handleSubmit(values: Recordable) {
|
||||||
for (const key in formParams) {
|
for (const key in formParams) {
|
||||||
if (values[key] != null && values[key] != undefined) {
|
if (values[key] != null && values[key] != undefined) {
|
||||||
@ -187,7 +159,9 @@
|
|||||||
formParams[key] ='';
|
formParams[key] ='';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function onSelectionChange(value){
|
||||||
|
selectionData.value = value
|
||||||
|
}
|
||||||
const [register, {}] = useForm({
|
const [register, {}] = useForm({
|
||||||
labelWidth: 80,
|
labelWidth: 80,
|
||||||
layout: 'horizontal',
|
layout: 'horizontal',
|
||||||
@ -196,17 +170,6 @@
|
|||||||
schemas
|
schemas
|
||||||
});
|
});
|
||||||
|
|
||||||
const [
|
|
||||||
lightModalRegister,
|
|
||||||
{ openModal: lightOpenModal, closeModal: lightCloseModal, setSubLoading: lightSetSubLoading },
|
|
||||||
] = useModal({
|
|
||||||
title: '删除确认',
|
|
||||||
showIcon: true,
|
|
||||||
type: 'warning',
|
|
||||||
closable: false,
|
|
||||||
maskClosable: true,
|
|
||||||
width: 380,
|
|
||||||
});
|
|
||||||
|
|
||||||
//删除
|
//删除
|
||||||
function openRemoveModal() {
|
function openRemoveModal() {
|
||||||
@ -215,17 +178,23 @@
|
|||||||
|
|
||||||
//添加
|
//添加
|
||||||
function addUser() {
|
function addUser() {
|
||||||
isEdit.value = false;
|
userId.value =0
|
||||||
createModalRef.value.setProps({ title: '添加用户' });
|
editVisible.value = true
|
||||||
createModalRef.value.openModal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//确认删除
|
//确认删除
|
||||||
function removeOkModal() {
|
function removeOkModal() {
|
||||||
|
lightSetSubLoading(true);
|
||||||
lightCloseModal();
|
lightCloseModal();
|
||||||
lightSetSubLoading();
|
reloadTable()
|
||||||
ElMessage.error('抱歉,您没有操作权限');
|
|
||||||
}
|
}
|
||||||
|
const getAllDict = async () => {
|
||||||
|
const list = await getRoleAllList();
|
||||||
|
roleList.value = list ? list : [];
|
||||||
|
};
|
||||||
|
onMounted(() => {
|
||||||
|
getAllDict()
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
@ -1,122 +0,0 @@
|
|||||||
import { FormSchema } from '@/components/Form/index';
|
|
||||||
import { getRoleAllList } from '@/api/system/role';
|
|
||||||
export const loadSelectData = async(res)=> {
|
|
||||||
//这里可以进行数据转换处理
|
|
||||||
return (await getRoleAllList({ ...res })).map((item, index) => {
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
label:item.name,
|
|
||||||
value:item.id,
|
|
||||||
index,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
export const schemas: FormSchema[] = [
|
|
||||||
{
|
|
||||||
field: 'username',
|
|
||||||
component: 'Input',
|
|
||||||
label: '用户名',
|
|
||||||
componentProps: {
|
|
||||||
placeholder: '请输入用户名',
|
|
||||||
},
|
|
||||||
rules: [{ required: true, message: '请输入用户名', trigger: ['blur'] }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'mobile',
|
|
||||||
component: 'Input',
|
|
||||||
label: '手机号',
|
|
||||||
componentProps: {
|
|
||||||
placeholder: '请输入手机号码',
|
|
||||||
controls: false,
|
|
||||||
},
|
|
||||||
rules: [{ required: true, message: '请输入手机号码', trigger: ['blur'] }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'email',
|
|
||||||
component: 'Input',
|
|
||||||
label: '邮箱',
|
|
||||||
componentProps: {
|
|
||||||
placeholder: '请输入邮箱',
|
|
||||||
},
|
|
||||||
rules: [{ required: true, type: 'email', message: '请输入邮箱', trigger: ['change'] }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'gender',
|
|
||||||
component: 'Select',
|
|
||||||
label: '性别',
|
|
||||||
componentProps: {
|
|
||||||
placeholder: '请选择性别',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: '男',
|
|
||||||
value: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '女',
|
|
||||||
value: 2,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
field: 'status',
|
|
||||||
component: 'Select',
|
|
||||||
label: '状态',
|
|
||||||
componentProps: {
|
|
||||||
placeholder: '请选择角色',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: '正常',
|
|
||||||
value: "1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '禁用',
|
|
||||||
value: "2",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'role',
|
|
||||||
component: 'BasicSelect',
|
|
||||||
label: '角色',
|
|
||||||
componentProps: {
|
|
||||||
placeholder: '请选择角色',
|
|
||||||
block:true,
|
|
||||||
request: loadSelectData,
|
|
||||||
onChange: (e: any) => {
|
|
||||||
console.log(e);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'account',
|
|
||||||
component: 'Input',
|
|
||||||
label: '登录账号',
|
|
||||||
componentProps: {
|
|
||||||
placeholder: '请输入登录账号',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'password',
|
|
||||||
label: '密码',
|
|
||||||
slot: 'passwordSlot',
|
|
||||||
showFeedback: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'rePassword',
|
|
||||||
label: '确认密码',
|
|
||||||
slot: 'rePasswordSlot',
|
|
||||||
showFeedback: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'introduce',
|
|
||||||
component: 'Input',
|
|
||||||
label: '个人介绍',
|
|
||||||
componentProps: {
|
|
||||||
type: 'textarea',
|
|
||||||
placeholder: '请输入登录账号',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
Loading…
Reference in New Issue
Block a user