|
|
|
@ -55,60 +55,16 @@
|
|
|
|
|
</el-tree>
|
|
|
|
|
</el-scrollbar>
|
|
|
|
|
</el-card>
|
|
|
|
|
<el-card v-show="state.condition2.menuId" class="feature">
|
|
|
|
|
<template #header>
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
<div class="title">功能</div>
|
|
|
|
|
<div class="action">
|
|
|
|
|
<el-button type="text" @click="handlePickFeature()">新增</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<el-scrollbar v-loading="loading2" class="card-body">
|
|
|
|
|
<el-tree
|
|
|
|
|
:data="featureList"
|
|
|
|
|
default-expand-all
|
|
|
|
|
:expand-on-click-node="false"
|
|
|
|
|
highlight-current
|
|
|
|
|
node-key="id"
|
|
|
|
|
:props="{
|
|
|
|
|
label: 'name',
|
|
|
|
|
value: 'id',
|
|
|
|
|
}"
|
|
|
|
|
@node-click="(data) => handleCreateFeature(data)"
|
|
|
|
|
>
|
|
|
|
|
<template #default="{ data }">
|
|
|
|
|
<div class="flex">
|
|
|
|
|
<!-- <el-tooltip :content="data.name" placement="right"> -->
|
|
|
|
|
<div class="name" :title="data.name">
|
|
|
|
|
{{ data.name }}
|
|
|
|
|
</div>
|
|
|
|
|
<!-- </el-tooltip> -->
|
|
|
|
|
<div class="btns">
|
|
|
|
|
<!-- <el-button type="text" @click.stop="handleDeleteFeature([data])">
|
|
|
|
|
<el-icon name="Delete" />
|
|
|
|
|
</el-button> -->
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-tree>
|
|
|
|
|
</el-scrollbar>
|
|
|
|
|
</el-card>
|
|
|
|
|
<el-card v-show="menuState.formVisible" class="form">
|
|
|
|
|
<template #header>
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
<div class="title">菜单表单</div>
|
|
|
|
|
<div class="action"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<el-scrollbar v-loading="loading2" class="card-body">
|
|
|
|
|
<el-dialog v-model="menuState.formVisible" class="form" title="菜单表单">
|
|
|
|
|
<el-form
|
|
|
|
|
ref="refsMenuForm"
|
|
|
|
|
v-loading="menuState.submitting"
|
|
|
|
|
label-width="100px"
|
|
|
|
|
v-loading="menuState.submitting"
|
|
|
|
|
:model="menuState.form"
|
|
|
|
|
:rules="menuState.rules"
|
|
|
|
|
>
|
|
|
|
|
<el-row>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="所属系统" prop="systemId">
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="menuState.form.systemId"
|
|
|
|
@ -116,6 +72,8 @@
|
|
|
|
|
:opts="opts.system"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="上级菜单" prop="parentId">
|
|
|
|
|
<el-cascader
|
|
|
|
|
v-model="menuState.form.parentId"
|
|
|
|
@ -130,119 +88,62 @@
|
|
|
|
|
}"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="菜单类型" prop="type">
|
|
|
|
|
<el-radio-group v-model="menuState.form.type" :opts="opts.type" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="菜单名称" prop="title">
|
|
|
|
|
<el-input v-model="menuState.form.title" maxlength="6" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-row>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="权限标识" prop="permission">
|
|
|
|
|
<el-input v-model="menuState.form.permission" maxlength="20" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="路由路径" prop="path">
|
|
|
|
|
<el-input v-model="menuState.form.path" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="前端页面" prop="page">
|
|
|
|
|
<el-input v-model="menuState.form.page" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="菜单图标" prop="icon">
|
|
|
|
|
<el-input v-model="menuState.form.icon" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="激活菜单" prop="activeMenu">
|
|
|
|
|
<el-input v-model="menuState.form.activeMenu" />
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="10">
|
|
|
|
|
<el-form-item label="菜单名称" prop="title">
|
|
|
|
|
<el-input v-model="menuState.form.title" maxlength="6" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
|
|
|
|
|
<el-form-item label="菜单排序" prop="sort">
|
|
|
|
|
<el-input-number v-model="menuState.form.sort" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item>
|
|
|
|
|
<el-button type="primary" @click="handleSaveMenu">保存</el-button>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
</el-scrollbar>
|
|
|
|
|
</el-card>
|
|
|
|
|
<el-card v-show="featureState.formVisible" class="form">
|
|
|
|
|
<template #header>
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
<div class="title">功能表单</div>
|
|
|
|
|
<div class="action"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<el-scrollbar v-loading="loading2" class="card-body">
|
|
|
|
|
<el-form
|
|
|
|
|
ref="refsFeatureForm"
|
|
|
|
|
v-loading="featureState.submitting"
|
|
|
|
|
label-width="100px"
|
|
|
|
|
:model="featureState.form"
|
|
|
|
|
:rules="featureState.rules"
|
|
|
|
|
>
|
|
|
|
|
<el-form-item label="所属系统" prop="systemId">
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="featureState.form.systemId"
|
|
|
|
|
:config="{ label: 'systemName', value: 'id' }"
|
|
|
|
|
:opts="opts.system"
|
|
|
|
|
@change="state.condition.systemId = $event"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="所属菜单" prop="menuId">
|
|
|
|
|
<el-cascader
|
|
|
|
|
v-model="featureState.form.menuId"
|
|
|
|
|
:options="list"
|
|
|
|
|
:props="{
|
|
|
|
|
label: 'title',
|
|
|
|
|
value: 'id',
|
|
|
|
|
children: 'menuChild',
|
|
|
|
|
checkStrictly: true,
|
|
|
|
|
expandTrigger: 'hover',
|
|
|
|
|
emitPath: false,
|
|
|
|
|
}"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="功能名称" prop="name">
|
|
|
|
|
<el-input v-model="featureState.form.name" maxlength="50" type="textarea" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="服务名称" prop="service">
|
|
|
|
|
<el-input v-model="featureState.form.service" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="请求方式" prop="method">
|
|
|
|
|
<el-select v-model="featureState.form.method" :opts="opts.method" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="接口路径" prop="uri">
|
|
|
|
|
<el-input v-model="featureState.form.uri" :rows="3" type="textarea" />
|
|
|
|
|
<el-scrollbar maxHeight="180px">
|
|
|
|
|
<el-form-item label="接口权限" v-if="!menuState.form.permissionListVO.length">
|
|
|
|
|
<el-button @click="handelAddPermission(0)">添加关联接口</el-button>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="是否启用" prop="isEnable">
|
|
|
|
|
<el-switch v-model="featureState.form.isEnable" />
|
|
|
|
|
<template v-else>
|
|
|
|
|
<el-row v-for="(item, idx) in menuState.form.permissionListVO" :key="idx">
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
<el-form-item label="关联接口">
|
|
|
|
|
<el-input v-model="item.service" placeholder="请输入服务名" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="权限类型" prop="type">
|
|
|
|
|
<el-radio-group v-model="featureState.form.type" :opts="opts2.type" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item>
|
|
|
|
|
<el-button type="primary" @click="handleSaveFeature">保存</el-button>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="13">
|
|
|
|
|
<el-form-item :labelWidth="5">
|
|
|
|
|
<el-input v-model="item.uri" placeholder="请输入请求URL">
|
|
|
|
|
<template #prepend>
|
|
|
|
|
<el-select v-model="item.method" style="width: 80px">
|
|
|
|
|
<el-option value="get">get</el-option>
|
|
|
|
|
<el-option value="post">post</el-option>
|
|
|
|
|
<el-option value="put">put</el-option>
|
|
|
|
|
<el-option value="delete">delete</el-option>
|
|
|
|
|
</el-select>
|
|
|
|
|
</template>
|
|
|
|
|
</el-input>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="2">
|
|
|
|
|
<div class="icon">
|
|
|
|
|
<el-icon name="CirclePlus" @click="handelAddPermission(idx)" />
|
|
|
|
|
<el-icon name="Remove" @click="handelDelPermission(idx)" />
|
|
|
|
|
</div>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
</template>
|
|
|
|
|
</el-scrollbar>
|
|
|
|
|
</el-card>
|
|
|
|
|
<el-dialog v-model="featureState.pickVisible" title="选择功能权限">
|
|
|
|
|
<el-form
|
|
|
|
|
ref="refsPickForm"
|
|
|
|
|
v-loading="featureState.submitting"
|
|
|
|
|
label-width="100px"
|
|
|
|
|
:model="featureState.pick"
|
|
|
|
|
:rules="featureState.pickRules"
|
|
|
|
|
>
|
|
|
|
|
<el-form-item label="功能菜单" prop="permissionId">
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="featureState.pick.permissionId"
|
|
|
|
|
:config="{ label: 'name', value: 'id' }"
|
|
|
|
|
:opts="opts.free"
|
|
|
|
|
/>
|
|
|
|
|
<el-form-item>
|
|
|
|
|
<el-button type="primary" @click="handleSaveMenu">保存</el-button>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
<template #footer>
|
|
|
|
|
<el-button @click="handleCancelPick">取消</el-button>
|
|
|
|
|
<el-button :loading="featureState.submitting" type="primary" @click="handleSavePick">
|
|
|
|
|
保存
|
|
|
|
|
</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
@ -253,7 +154,6 @@
|
|
|
|
|
const store = useStore();
|
|
|
|
|
const { proxy } = getCurrentInstance();
|
|
|
|
|
const opts = computed(() => store.state.menu.opts);
|
|
|
|
|
const opts2 = computed(() => store.state.feature.opts);
|
|
|
|
|
if (!unref(opts).init) {
|
|
|
|
|
store.dispatch('menu/load');
|
|
|
|
|
}
|
|
|
|
@ -290,20 +190,6 @@
|
|
|
|
|
|
|
|
|
|
/* 查询功能 */
|
|
|
|
|
const loading2 = ref(false);
|
|
|
|
|
const featureList = computed(() => _.cloneDeep(store.state.feature.list));
|
|
|
|
|
const handleSearchFeature = async () => {
|
|
|
|
|
loading2.value = true;
|
|
|
|
|
await store.dispatch('feature/search');
|
|
|
|
|
loading2.value = false;
|
|
|
|
|
};
|
|
|
|
|
watch(
|
|
|
|
|
() => state.condition2,
|
|
|
|
|
(value) => {
|
|
|
|
|
store.commit('feature/setCondition', _.cloneDeep(value));
|
|
|
|
|
handleSearchFeature();
|
|
|
|
|
},
|
|
|
|
|
{ immediate: true, deep: true }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/* 菜单表单 */
|
|
|
|
|
const refsMenuForm = ref(null);
|
|
|
|
@ -321,15 +207,16 @@
|
|
|
|
|
page: '',
|
|
|
|
|
icon: '',
|
|
|
|
|
activeMenu: '',
|
|
|
|
|
permissionListVO: [],
|
|
|
|
|
sort: 0,
|
|
|
|
|
},
|
|
|
|
|
rules: {
|
|
|
|
|
systemId: [{ required: true, message: '所属系统不能为空' }],
|
|
|
|
|
type: [{ required: true, message: '菜单类型不能为空' }],
|
|
|
|
|
title: [{ required: true, message: '菜单名称不能为空' }],
|
|
|
|
|
// type: [{ required: true, message: '菜单类型不能为空' }],
|
|
|
|
|
// title: [{ required: true, message: '菜单名称不能为空' }],
|
|
|
|
|
permission: [{ required: true, message: '权限标识不能为空' }],
|
|
|
|
|
path: [{ required: true, message: '路由路径不能为空' }],
|
|
|
|
|
page: [{ required: true, message: '前端页面不能为空' }],
|
|
|
|
|
// path: [{ required: true, message: '路由路径不能为空' }],
|
|
|
|
|
// page: [{ required: true, message: '前端页面不能为空' }],
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
const handleCreateMenu = (row, parent) => {
|
|
|
|
@ -337,7 +224,6 @@
|
|
|
|
|
state.condition2.menuId = row.id;
|
|
|
|
|
}
|
|
|
|
|
menuState.formVisible = true;
|
|
|
|
|
featureState.formVisible = false;
|
|
|
|
|
Object.assign(
|
|
|
|
|
menuState.form,
|
|
|
|
|
row || {
|
|
|
|
@ -351,6 +237,7 @@
|
|
|
|
|
page: '',
|
|
|
|
|
icon: '',
|
|
|
|
|
activeMenu: '',
|
|
|
|
|
permissionListVO: [],
|
|
|
|
|
sort: 0,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
@ -363,6 +250,7 @@
|
|
|
|
|
try {
|
|
|
|
|
await proxy.$validate(refsMenuForm);
|
|
|
|
|
let data = _.cloneDeep(menuState.form);
|
|
|
|
|
data.permissionList = data.permissionListVO.filter((i) => i.method && i.service && i.uri);
|
|
|
|
|
let res = await store.dispatch('menu/save', data);
|
|
|
|
|
if (res) {
|
|
|
|
|
menuState.formVisible = false;
|
|
|
|
@ -378,113 +266,12 @@
|
|
|
|
|
data.map((item) => item.id)
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* 功能表单 */
|
|
|
|
|
const refsFeatureForm = ref(null);
|
|
|
|
|
const refsPickForm = ref(null);
|
|
|
|
|
const featureState = reactive({
|
|
|
|
|
formVisible: false,
|
|
|
|
|
submitting: false,
|
|
|
|
|
form: {
|
|
|
|
|
id: null,
|
|
|
|
|
systemId: null,
|
|
|
|
|
menuId: null,
|
|
|
|
|
name: null,
|
|
|
|
|
service: null,
|
|
|
|
|
method: 'GET',
|
|
|
|
|
uri: null,
|
|
|
|
|
type: 1,
|
|
|
|
|
isEnable: true,
|
|
|
|
|
},
|
|
|
|
|
rules: {
|
|
|
|
|
systemId: [{ required: true, message: '所属系统不能为空' }],
|
|
|
|
|
menuId: [{ required: true, message: '所属菜单不能为空' }],
|
|
|
|
|
name: [{ required: true, message: '功能名称不能为空' }],
|
|
|
|
|
service: [{ required: true, message: '服务名称不能为空' }],
|
|
|
|
|
method: [{ required: true, message: '请求方式不能为空' }],
|
|
|
|
|
uri: [{ required: true, message: '接口路径不能为空' }],
|
|
|
|
|
type: [{ required: true, message: '权限类型不能为空' }],
|
|
|
|
|
isEnable: [{ required: true, message: '是否启用不能为空' }],
|
|
|
|
|
},
|
|
|
|
|
pickVisible: false,
|
|
|
|
|
pick: {
|
|
|
|
|
permissionId: null,
|
|
|
|
|
menuId: null,
|
|
|
|
|
},
|
|
|
|
|
pickRules: {
|
|
|
|
|
permissionId: [{ required: true, message: '功能权限不能为空' }],
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
const handleCancelPick = () => {
|
|
|
|
|
featureState.pickVisible = false;
|
|
|
|
|
featureState.pick = {
|
|
|
|
|
permissionId: null,
|
|
|
|
|
menuId: null,
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
const handleSavePick = async () => {
|
|
|
|
|
featureState.submitting = true;
|
|
|
|
|
try {
|
|
|
|
|
await proxy.$validate(refsPickForm);
|
|
|
|
|
let res = await store.dispatch('feature/assign', featureState.pick);
|
|
|
|
|
if (res) {
|
|
|
|
|
handleSearchFeature();
|
|
|
|
|
handleCancelPick();
|
|
|
|
|
store.dispatch('menu/load');
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.info('取消保存', e);
|
|
|
|
|
}
|
|
|
|
|
featureState.submitting = false;
|
|
|
|
|
const handelAddPermission = (idx) => {
|
|
|
|
|
menuState.form.permissionListVO.splice(idx, 0, { method: 'get' });
|
|
|
|
|
};
|
|
|
|
|
const handlePickFeature = async () => {
|
|
|
|
|
try {
|
|
|
|
|
await proxy.$confirm('是否选择尚未分配菜单的功能权限?');
|
|
|
|
|
featureState.formVisible = false;
|
|
|
|
|
featureState.pickVisible = true;
|
|
|
|
|
featureState.pick.menuId = state.condition2.menuId;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
handleCreateFeature();
|
|
|
|
|
}
|
|
|
|
|
const handelDelPermission = (idx) => {
|
|
|
|
|
menuState.form.permissionListVO.splice(idx, 1);
|
|
|
|
|
};
|
|
|
|
|
const handleCreateFeature = async (row) => {
|
|
|
|
|
featureState.formVisible = true;
|
|
|
|
|
menuState.formVisible = false;
|
|
|
|
|
Object.assign(
|
|
|
|
|
featureState.form,
|
|
|
|
|
row || {
|
|
|
|
|
id: null,
|
|
|
|
|
systemId: state.condition.systemId,
|
|
|
|
|
menuId: state.condition2.menuId,
|
|
|
|
|
name: null,
|
|
|
|
|
service: null,
|
|
|
|
|
method: 'GET',
|
|
|
|
|
uri: null,
|
|
|
|
|
type: 1,
|
|
|
|
|
isEnable: true,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
const handleSaveFeature = async () => {
|
|
|
|
|
featureState.submitting = true;
|
|
|
|
|
try {
|
|
|
|
|
await proxy.$validate(refsFeatureForm);
|
|
|
|
|
let data = _.cloneDeep(featureState.form);
|
|
|
|
|
let res = await store.dispatch('feature/save', data);
|
|
|
|
|
if (res) {
|
|
|
|
|
featureState.formVisible = false;
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.info('取消保存', e);
|
|
|
|
|
}
|
|
|
|
|
featureState.submitting = false;
|
|
|
|
|
};
|
|
|
|
|
// const handleDeleteFeature = (data) => {
|
|
|
|
|
// store.dispatch(
|
|
|
|
|
// 'feature/remove',
|
|
|
|
|
// data.map((item) => item.id)
|
|
|
|
|
// );
|
|
|
|
|
// };
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="less" scoped>
|
|
|
|
@ -557,4 +344,15 @@
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.icon {
|
|
|
|
|
margin: 10px 0 10px;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-evenly;
|
|
|
|
|
:deep .el-icon {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
&:hover {
|
|
|
|
|
color: var(--el-color-primary);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|