Commit 7ebe2e6e authored by 陈浩玮's avatar 陈浩玮

Merge branch 'feature/shuiluo' into 'master'

feat: 菜单管理页面大体完成

See merge request product/kim3-web-vue/starter-web-vue!2
parents 87594b7c 9df83c42
import { request, METHOD } from '@/utils';
export * from './menu';
export function getUserDetailInfoApi() {
return request('/api/v1/detail', METHOD.GET);
......
import { delReq, getReq, postReq } from '@/utils';
export function delMenuApi(id) {
return delReq(`/api/v1/menus/${id}`);
}
export function getMenuDataApi() {
return getReq('/api/v1/menus');
}
export function addMenuApi(data) {
return postReq('/api/v1/menus', data);
}
export function addRoleApi(data) {
return postReq('/api/v1/roles', data);
}
<template>
<a-tree
:checkedKeys="value"
:selectable="false"
checkable
:replaceFields="replaceFields"
:tree-data="treeData"
style="max-height:400px"
class="tw-overflow-y-auto"
v-bind="$attrs"
v-on="$listeners"
/>
</template>
<script>
import { getMenuDataApi } from '@/api';
import { convertListToTree } from '@/utils';
export default {
model: {
prop: 'value',
event: 'check',
},
props: {
value: [Object, Array],
showMenu: Boolean,
},
data() {
return {
treeData: [],
replaceFields: {
title: 'menuName',
key: 'menuId',
},
};
},
async mounted() {
this.rawData = await getMenuDataApi();
this.treeData = convertListToTree(this.rawData, !this.showMenu);
},
methods: {
get() {
if (Array.isArray(this.value)) return this.rawData.filter(m => this.value.includes(m.menuId));
return [];
},
},
};
</script>
<template>
<a-popconfirm title="确认是否删除" ok-text="确认" cancel-text="取消" @confirm="onOk">
<a>删除</a>
</a-popconfirm>
</template>
<script>
import { EMPTY_FUN } from '@/utils';
import { delReq } from '@/utils';
export default {
props: {
url: String,
cb: {
type: Function,
default: EMPTY_FUN,
},
},
methods: {
async onOk() {
await delReq(this.url);
this.cb();
},
},
};
</script>
......@@ -23,7 +23,7 @@
</template>
</a-space>
<a-table :data-source="data" :loading="loading" v-bind="$attrs">
<a-table :data-source="data" :loading="loading" v-bind="$attrs" :pagination="!noPage">
<slot />
</a-table>
</div>
......@@ -35,9 +35,23 @@
:visible="addVisible"
@close="addDrawerClose"
v-if="addBtn"
:maskClosable="addBtn.maskClosable"
:maskClosable="!!addBtn.maskClosable"
:closable="false"
:drawerStyle="drawerStyle"
:bodyStyle="bodyStyle"
:width="addBtn.width"
destroyOnClose
>
<slot name="add" />
<div class="tw-overflow-y-hidden">
<div class="tw-overflow-y-auto tw-h-full">
<slot name="add" />
</div>
</div>
<a-divider />
<a-space>
<a-button @click="addVisible = false">取消</a-button>
<a-button type="primary" @click="submit" :loading="submitLoading">确认</a-button>
</a-space>
</a-drawer>
</div>
</template>
......@@ -45,71 +59,6 @@
<script>
import { request, METHOD } from '@/utils/requestUtil';
const data = [
{
key: 1,
name: 'John Brown sr.',
age: 60,
address: 'New York No. 1 Lake Park',
children: [
{
key: 11,
name: 'John Brown',
age: 42,
address: 'New York No. 2 Lake Park',
},
{
key: 12,
name: 'John Brown jr.',
age: 30,
address: 'New York No. 3 Lake Park',
children: [
{
key: 121,
name: 'Jimmy Brown',
age: 16,
address: 'New York No. 3 Lake Park',
},
],
},
{
key: 13,
name: 'Jim Green sr.',
age: 72,
address: 'London No. 1 Lake Park',
children: [
{
key: 131,
name: 'Jim Green',
age: 42,
address: 'London No. 2 Lake Park',
children: [
{
key: 1311,
name: 'Jim Green jr.',
age: 25,
address: 'London No. 3 Lake Park',
},
{
key: 1312,
name: 'Jimmy Green sr.',
age: 18,
address: 'London No. 4 Lake Park',
},
],
},
],
},
],
},
{
key: 2,
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
];
export default {
props: {
url: String,
......@@ -121,6 +70,7 @@ export default {
type: Object,
},
formatData: Function,
noPage: Boolean,
},
data() {
return {
......@@ -128,8 +78,25 @@ export default {
queryForm: {},
loading: false,
addVisible: false,
submitLoading: false,
drawerStyle: {
display: 'flex',
flexDirection: 'column',
overflowY: 'hidden',
},
bodyStyle: {
flex: 1,
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',
},
};
},
watch: {
addVisible(val) {
if (!val && this.addBtn.onCancel) this.addBtn.onCancel();
},
},
mounted() {
console.log(this.addBtn);
this.getData();
......@@ -153,6 +120,20 @@ export default {
addDrawerClose() {
this.addVisible = false;
},
async submit() {
this.submitLoading = true;
try {
await this.addBtn?.onOk();
this.getData();
} catch (error) {
// todo
}
this.submitLoading = false;
this.addVisible = false;
},
showAdd() {
this.addVisible = true;
},
},
};
</script>
......
......@@ -34,7 +34,7 @@ const store = new Vuex.Store({
Vue.use(Router);
const isAsynRount = store.state.settingModule.asyncRoutes;
const options = initRouter(isAsynRount);
const router = new Router(options);
const router = new Router({ mode: 'history', ...options });
//装载vue-i18n控件 如果语言优先级 请直接修改这里localeLang和fallbackLang
Vue.use(VueI18n);
......
<template>
<my-table url="/api/v1/menus" :addBtn="addBtn" :formatData="formatData" rowKey="menuId">
<template #search="{query}">
<a-form-model-item label="菜单、目录名称">
<a-input v-model="query.name" />
</a-form-model-item>
</template>
<my-table
url="/api/v1/menus"
:addBtn="addBtn"
:formatData="formatData"
rowKey="menuId"
noPage
ref="table"
>
<template #add>
<a-input v-model="add.name" />
<Form ref="addForm" />
</template>
<a-table-column title="名称" data-index="menuName" />
<a-table-column title="类型" data-index="menuTypeName" />
<a-table-column title="显示排序" data-index="viewIndex" :sorter="sorter" sortOrder="ascend" />
<a-table-column title="显示图标" data-index="menuIcon" />
<a-table-column title="模块URL" data-index="menuUrl" />
<a-table-column title="操作">
<template #default>
<span>
<a>编辑</a>
<a-divider type="vertical" />
<a>删除</a>
</span>
<template #default="row">
<a @click="() => edit(row)">编辑</a>
<a-divider type="vertical" />
<PopconfirmDelete :onOk="() => delMenu(row.menuId)" />
</template>
</a-table-column>
</my-table>
......@@ -27,19 +28,44 @@
<script>
import { convertListToTree } from '@/utils';
import PopconfirmDelete from '@/components/popconfirm_delete/index.vue';
import { delMenuApi } from '@/api';
import Form from './form.vue';
export default {
data: () => ({
add: {},
addBtn: { text: '新建', title: '菜单配置' },
data: vm => ({
addBtn: {
text: '新建',
title: '菜单配置',
width: 400,
onOk() {
return vm.$refs['addForm']?.submit();
},
},
sortOrder: 'ascend',
}),
components: { PopconfirmDelete, Form },
methods: {
formatData: convertListToTree,
refreshTable() {
this.$refs['table'].getData();
},
async delMenu(id) {
await delMenuApi(id);
this.refreshTable();
},
sorter(pre, next) {
return pre.viewIndex - next.viewIndex;
},
edit(data) {
console.log(data);
this.$refs['table']?.showAdd();
this.$nextTick(() => {
this.$refs['addForm'].setEdit(data);
});
},
},
};
</script>
<template>
<a-form-model layout="vertical" :model="form" :rules="rules">
<a-form-model-item label="父级" v-if="isAdd">
<MenuTree @check="onCheck" :value="checkedKeys" checkStrictly />
</a-form-model-item>
<a-form-model-item label="类型">
<a-radio-group v-model="form.menuType" :disabled="isEdit">
<a-radio value="CATALOG">目录</a-radio>
<a-radio value="MENU">菜单</a-radio>
</a-radio-group>
</a-form-model-item>
<a-form-model-item label="中文名称">
<a-input v-model="form.menuName" />
</a-form-model-item>
<a-form-model-item label="英文名称">
<a-input />
</a-form-model-item>
<a-form-model-item label="模块URL" prop="menuUrl">
<a-input v-model="form.menuUrl" />
</a-form-model-item>
<a-form-model-item label="显示排序" prop="viewIndex">
<a-input-number v-model="form.viewIndex" />
</a-form-model-item>
<a-form-model-item label="显示图标" prop="menuIcon">
<a-input v-model="form.menuIcon" />
</a-form-model-item>
<a-form-model-item label="说明" prop="menuRemark">
<a-textarea v-model="form.menuRemark" />
</a-form-model-item>
</a-form-model>
</template>
<script>
import { addMenuApi } from '@/api';
import MenuTree from '@/components/menu_tree/index.vue';
export default {
components: { MenuTree },
data: () => ({
type: 0,
form: {
menuType: 'CATALOG',
parentMenuId: 0,
menuName: '',
},
rules: {
menuUrl: [{ required: true }],
},
checkedKeys: {
checked: [],
halfChecked: [],
},
}),
computed: {
isAdd() {
return this.type === 0;
},
isEdit() {
return this.type === 1;
},
},
methods: {
submit() {
return addMenuApi({ ...this.form, parentMenuId: this.checkedKeys.checked[0] ?? 0 });
},
onCheck(checkedKeys) {
const checked = checkedKeys.checked.slice(-1);
this.checkedKeys = { checked };
},
setEdit(data) {
this.type = 1;
this.form = data;
},
},
};
</script>
<template>
<h1>Menu Management</h1>
<my-table url="/api/v1/roles" rowKey="roleId" :addBtn="addBtn" ref="table">
<template #add>
<Form ref="form" />
</template>
<a-table-column title="名称" data-index="roleName" />
<a-table-column title="说明" data-index="remark" />
<a-table-column title="操作">
<template #default="row">
<a @click="() => view(row, 2)">查看</a>
<a-divider type="vertical" />
<a @click="() => view(row, 1)">编辑</a>
<a-divider type="vertical" />
<PopconfirmDelete :url="`/api/v1/roles/${row.roleId}`" :cb="refreshTable" />
</template>
</a-table-column>
</my-table>
</template>
\ No newline at end of file
<script>
import Form from './form.vue';
import PopconfirmDelete from '@/components/popconfirm_delete/index.vue';
export default {
components: { Form, PopconfirmDelete },
data() {
return {
addBtn: { width: 400, onOk: () => this.$refs['form']?.submit() },
};
},
methods: {
refreshTable() {
this.$refs['table']?.getData();
},
view(data, type) {
this.$refs['table']?.showAdd();
this.$nextTick(() => {
this.$refs['form'].setData(data, type);
});
},
},
};
</script>
<template>
<a-form-model layout="vertical" :model="form" :rules="rules">
<a-form-model-item label="角色名称">
<a-input v-model="form.roleName" :disabled="isView" />
</a-form-model-item>
<a-form-model-item label="角色编码">
<a-input v-model="form.roleCode" :disabled="isView" />
</a-form-model-item>
<a-form-model-item label="角色说明" prop="remark">
<a-input v-model="form.remark" :disabled="isView" />
</a-form-model-item>
<a-form-model-item label="菜单权限">
<MenuTree v-model="checkedKeys" showMenu ref="menuTree" :disabled="isView" />
</a-form-model-item>
</a-form-model>
</template>
<script>
import MenuTree from '@/components/menu_tree/index.vue';
import { addRoleApi } from '@/api';
export default {
components: { MenuTree },
data() {
return {
type: 0,
form: {},
rules: {},
checkedKeys: [],
};
},
computed: {
isEdit() {
return this.type === 1;
},
isView() {
return this.type === 2;
},
},
methods: {
submit() {
return addRoleApi({ ...this.form, authorityList: this.$refs['menuTree'].get() });
},
setData(data, type) {
this.form = data;
this.type = type;
},
},
};
</script>
......@@ -11,7 +11,6 @@ import { getI18nKey } from '@/utils/routerUtil';
function generateI18n(lang, routes, valueKey) {
routes.forEach(route => {
let keys = getI18nKey(route.fullPath).split('.');
console.log(keys);
let value =
valueKey === 'path'
? route[valueKey]
......
......@@ -13,9 +13,13 @@ export function setUserId(val) {
/**
* 转变菜单列表为tree结构
* @param {Array} menuList 菜单列表
* @param {Boolean} filterMenu 是否过滤掉菜单,只保留目录
*/
export function convertListToTree(menuList) {
const tempMenu = [...menuList];
export function convertListToTree(menuList, filterMenu = false) {
let tempMenu = [...menuList];
if (filterMenu) {
tempMenu = tempMenu.filter(m => m.menuType !== 'MENU');
}
for (const menu of menuList) {
if (menu.parentMenuId === 0) continue;
const parent = menuList.find(m => m.menuId === menu.parentMenuId);
......@@ -23,3 +27,5 @@ export function convertListToTree(menuList) {
}
return tempMenu.filter(m => m.parentMenuId === 0);
}
export function EMPTY_FUN() {}
......@@ -133,4 +133,27 @@ function checkAuthorization() {
return !!getToken();
}
export { METHOD, request, parseUrlParams, loadResponseInterceptor, setToken, checkAuthorization, clearToken };
function delReq(url, config) {
return request(url, METHOD.DELETE, config);
}
function getReq(url, params, config) {
return request(url, METHOD.GET, params, config);
}
function postReq(url, data, config) {
return request(url, METHOD.POST, data, config);
}
export {
METHOD,
request,
parseUrlParams,
loadResponseInterceptor,
setToken,
checkAuthorization,
clearToken,
delReq,
getReq,
postReq,
};
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment