<template>
|
<div>
|
<el-card style="margin: 10px;" class="affix-container">
|
<div>
|
<div style="display: flex;justify-content: space-between">
|
<div>
|
<b style="margin-right: 20px">{{ $t('menu.MenuList') }}</b>
|
<el-button link size="default" type="primary" :icon="icon.CirclePlus" @click="showAddView">
|
{{ $t('button.New') }}
|
</el-button>
|
</div>
|
<div>
|
<el-input :placeholder="$t('message.PleaseInputKeyWords')" :prefix-icon="icon.Search" maxlength="64"
|
clearable size="default" @clear="SearchData" style="width: 200px;margin-right: 10px"
|
v-model="searchValue.filter" @keydown.enter.native="SearchData"
|
:disabled="showAdvanceSearchView"></el-input>
|
<el-button :icon="icon.Search" type="primary" size="default" @click="SearchData"
|
:disabled="showAdvanceSearchView">
|
{{ $t('button.Search') }}
|
</el-button>
|
<el-button type="warning" size="default"
|
:icon="showAdvanceSearchView ? icon.CaretTop : icon.CaretBottom"
|
@click="showAdvanceSearchView = !showAdvanceSearchView">
|
{{ $t('button.AdvancedSearch') }}
|
</el-button>
|
</div>
|
</div>
|
<transition name="slide-fade">
|
<div v-show="showAdvanceSearchView" class="advanced-search-box">
|
<el-form size="default" label-width="135px">
|
<el-row :style="{ margin: '10px 0px 0px 0px' }">
|
<el-col :span="7">
|
<el-form-item :label="$t('label.Name')" :style="{ marginBottom: '0px' }">
|
<el-input size="default" :prefix-icon="icon.Edit" maxlength="64"
|
v-model="searchValue.name" placeholder=""/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.Component')" :style="{ marginBottom: '0px' }">
|
<el-input size="default" :prefix-icon="icon.Edit" maxlength="64"
|
v-model="searchValue.component"/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.Type')" :style="{ marginBottom: '0px' }">
|
<el-select size="default" v-model="searchValue.type" filterable>
|
<el-option v-for="item in menutypes" :key="item.id" :label="item.name"
|
:value="item.id">
|
</el-option>
|
</el-select>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :style="{ margin: '10px 0px 0px 0px' }">
|
<el-col :span="7">
|
<el-form-item :label="$t('label.LinkUrl')" :style="{ marginBottom: '0px' }">
|
<el-input size="default" :prefix-icon="icon.Edit" maxlength="64"
|
v-model="searchValue.url"/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.ComponentPath')" :style="{ marginBottom: '0px' }">
|
<el-input size="default" :prefix-icon="icon.Edit" maxlength="64"
|
v-model="searchValue.path"/>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :style="{ margin: '10px 0px 0px 0px' }">
|
<el-col :span="21" :style="{ textAlign: 'right' }" :offset="0">
|
<el-button size="default" :icon="icon.Search" type="primary" @click="SearchData">
|
{{ $t('button.Search') }}
|
</el-button>
|
<el-button size="default" :icon="icon.Delete" type="info" plain
|
@click="ClearSearchData">
|
{{ $t('button.Clear') }}
|
</el-button>
|
<el-button size="default" :icon="icon.CircleClose" type="danger" plain
|
@click="showAdvanceSearchView = !showAdvanceSearchView">
|
{{ $t('button.Close') }}
|
</el-button>
|
</el-col>
|
</el-row>
|
</el-form>
|
</div>
|
</transition>
|
</div>
|
<el-divider :style="{ 'margin': '10px 0px' }"></el-divider>
|
<el-table v-loading="loading" :data="menus" stripe border size="default" highlight-current-row
|
element-loading-text="Loading..." element-loading-spinner="el-icon-loading"
|
element-loading-background="rgba(0, 0, 0, 0.8)"
|
:header-row-style="{ height: '45px', color: 'var(--el-color-primary)', background: 'var(--el-color-info-light)', }"
|
:header-cell-style="{ height: '45px', margin: '0px', padding: '0px', background: 'var(--el-color-info-light)' }"
|
@row-dblclick="showEditView">
|
<el-table-column prop="name" sortable :label="$t('label.Name')" width="200"></el-table-column>
|
<el-table-column prop="level" sortable :label="$t('label.Level')" width="150"></el-table-column>
|
<el-table-column prop="languagekey" align="left" :label="$t('label.LanguageKey')" width="200">
|
<template #default="scope">
|
<div>
|
{{ scope.row.languagekey }}
|
</div>
|
</template>
|
</el-table-column>
|
<el-table-column prop="icon" align="left" :label="$t('label.Icon')" width="230">
|
<template #default="scope">
|
<div>
|
<svg-icon width="18" height="18" color="" :name="scope.row.icon"></svg-icon>
|
{{ scope.row.icon }}
|
</div>
|
</template>
|
</el-table-column>
|
<el-table-column prop="component" align="left" :label="$t('label.Component')" width="200"></el-table-column>
|
<el-table-column prop="url" align="left" :label="$t('label.LinkUrl')" min-width="600"
|
width="*"></el-table-column>
|
<el-table-column fixed="right" width="110" :label="$t('label.Operation')">
|
<template #default="scope">
|
<el-button @click="showEditView(scope.row)" :icon="icon.Edit" size="small" type="primary" circle>
|
</el-button>
|
<el-button @click="deleteItem(scope.row)" :icon="icon.Delete" size="small" type="danger" circle>
|
</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
</el-card>
|
<div v-dialogdrag>
|
<el-dialog :header="title" id="detailDialog" v-model="dialogVisible" :close-on-click-modal="false"
|
class="el-dialog-customer" width="1200px">
|
<template #header>
|
<el-row>
|
<el-col :span="12">
|
<el-icon style="font:normal bold 20px arial,sans-serif;vertical-align: -10%"
|
v-if="dialogMode === 'new'">
|
<plus/>
|
</el-icon>
|
<el-icon v-else style="font:normal bold 20px arial,serif;vertical-align: -10%">
|
<edit/>
|
</el-icon>
|
<span style="margin-left: 10px; font:normal bold 20px arial,serif;">{{ title }}</span>
|
</el-col>
|
</el-row>
|
</template>
|
<el-card style="margin-top: 0px;">
|
<el-form :model="menu" size="default" :rules="rules" ref="dialogForm" label-width="150px">
|
<el-tabs style="min-height: 300px">
|
<el-tab-pane :label="$t('label.BasicInformation')">
|
<el-row>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.Name')" prop="name">
|
<el-input size="default" :prefix-icon="icon.Edit" maxlength="64"
|
v-model="menu.name"></el-input>
|
</el-form-item>
|
</el-col>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.LanguageKey')" prop="languagekey">
|
<el-input size="default" :prefix-icon="icon.Edit" maxlength="64"
|
v-model="menu.languagekey"></el-input>
|
</el-form-item>
|
</el-col>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.Type')" prop="type">
|
<el-select size="default" style="width: 100%" v-model="menu.type" filterable>
|
<el-option v-for="item in menutypes" :key="item.id" :label="item.name"
|
:value="item.id" :disabled="item.disabled">
|
{{ item.name }}
|
</el-option>
|
</el-select>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.Component')" prop="component">
|
<el-input size="default" :prefix-icon="icon.Edit" maxlength="64"
|
v-model="menu.component"></el-input>
|
</el-form-item>
|
</el-col>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.Level')" prop="level">
|
<el-input-number size="default" v-model="menu.level" maxlength="64"
|
:prefix-icon="icon.Edit"/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.Icon')" prop="icon">
|
<el-input size="default" :prefix-icon="icon.Edit" maxlength="64"
|
v-model="menu.icon">
|
</el-input>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row>
|
<el-col :span="14">
|
<el-form-item :label="$t('label.LinkUrl')" prop="url">
|
<el-input size="default" :prefix-icon="icon.Edit" maxlength="64"
|
v-model="menu.url"></el-input>
|
</el-form-item>
|
</el-col>
|
<el-col :span="7">
|
<el-form-item :label="$t('label.Parent')" prop="parent">
|
<el-popover placement="right" trigger="click" width="auto">
|
<template #reference>
|
<el-input size="default" :prefix-icon="icon.Edit" maxlength="64"
|
style="width: 100%" @change="parentChange"
|
v-model="menu.parentname">
|
</el-input>
|
</template>
|
<el-tree :data="menutree" :props="defaultProps" accordion
|
@node-click="handleParentTreeNodeClick"></el-tree>
|
</el-popover>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row>
|
<el-col :span="14">
|
<el-form-item :label="$t('label.ComponentPath')" prop="path">
|
<el-input size="default" :prefix-icon="icon.Edit" maxlength="64"
|
v-model="menu.path"/>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row>
|
<el-col :span="14">
|
<el-form-item :label="$t('label.Remark')" prop="remark">
|
<el-input type="textarea" :rows="3" maxlength="500"
|
v-model="menu.remark"></el-input>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :style="{ margin: '10px 0px 10px 0px' }">
|
<el-col :span="24">
|
<el-alert :title="usernotetitle" type="warning" :description="usernotedescription"
|
:closable="false" show-icon/>
|
</el-col>
|
</el-row>
|
</el-tab-pane>
|
</el-tabs>
|
</el-form>
|
</el-card>
|
<template #footer>
|
<el-button type="primary" @click="onSubmit" :icon="icon.CircleCheck" size="default">
|
{{ $t('button.Submit') }}
|
</el-button>
|
<el-button type="danger" @click="dialogVisible = false" :icon="icon.CircleClose" size="default" plain>
|
{{ $t('button.Close') }}
|
</el-button>
|
</template>
|
</el-dialog>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
|
import * as icon from '@element-plus/icons-vue'
|
import {onMounted, onBeforeUnmount, getCurrentInstance, reactive, toRefs} from "vue";
|
import {formatDate} from '@/utils/common'
|
import {GetMenus, GetMenuTree, UpdateMenu, CreateMenu, DeleteMenu} from "@/api/admin/system"
|
import {useBasicInfoStore} from "@/store/basicInfo";
|
|
export default {
|
name: 'Menu-List',
|
components: {},
|
setup() {
|
let {proxy} = getCurrentInstance();
|
const store = useBasicInfoStore()
|
const state = reactive({
|
staticDomain: WGURL.staticDomain,
|
//common properties
|
title: '',
|
total: 0,
|
loading: false,
|
windowWidth: document.documentElement.clientWidth,
|
windowHeight: document.documentElement.clientHeight,
|
dialogVisible: false,
|
dialogMode: 'new',
|
showAdvanceSearchView: false,
|
//ParentTree
|
defaultProps: {
|
children: 'children',
|
label: 'name'
|
},
|
menutree: [],
|
//other
|
searchValue: {
|
filter: "",
|
name: '',
|
url: '',
|
path: '',
|
component: '',
|
type: null,
|
},
|
menus: [],
|
menutypes: [
|
{
|
id: 0,
|
name: `${proxy.$t('label.Undefined')}`,
|
},
|
{
|
id: 1,
|
name: `${proxy.$t('label.LinkUrl')}`,
|
},
|
{
|
id: 2,
|
name: `${proxy.$t('label.Menu')}`,
|
}],
|
menu: {
|
name: '',
|
languagekey: '',
|
parentid: '',
|
parentcode: '',
|
parentname: '',
|
url: '/admin',
|
path: '/admin',
|
level: 0,
|
component: 'LayoutAdmin',
|
type: 0,
|
icon: '',
|
keepalive: true,
|
isdeleted: false,
|
remark: '',
|
createtime: '',
|
updatetime: '',
|
},
|
rules: {
|
name: [{
|
required: true,
|
message: `${proxy.$t('message.IsRequired', {name: proxy.$t('label.Name')})}`,
|
}],
|
languagekey: [{
|
required: true,
|
message: `${proxy.$t('message.IsRequired', {name: proxy.$t('label.LanguageKey')})}`,
|
trigger: 'blur'
|
}],
|
type: [{
|
required: true,
|
message: `${proxy.$t('message.IsRequired', {name: proxy.$t('label.Type')})}`,
|
trigger: 'blur'
|
}],
|
component: [{
|
required: true,
|
message: `${proxy.$t('message.IsRequired', {name: proxy.$t('label.Component')})}`,
|
trigger: 'blur'
|
}],
|
icon: [{
|
required: true,
|
message: `${proxy.$t('message.IsRequired', {name: proxy.$t('label.Icon')})}`,
|
trigger: 'blur'
|
}],
|
},
|
usernotetitle: `${proxy.$t('message.UserNoteTitle')}`,
|
usernotedescription: `${proxy.$t('message.UserNoteDescription')}`,
|
});
|
|
onMounted(() => {
|
Init();
|
LoadData();
|
LoadMenuTree();
|
});
|
onBeforeUnmount(() => {
|
|
});
|
|
const Init = () => {
|
|
};
|
const clearAddOrEditModel = () => {
|
state.menu = {
|
name: '',
|
languagekey: '',
|
parentid: '',
|
parentcode: '',
|
parentname: '',
|
url: '/admin',
|
path: '/admin',
|
level: 0,
|
component: 'LayoutAdmin',
|
type: 0,
|
icon: '',
|
keepalive: true,
|
isdeleted: false,
|
remark: '',
|
createtime: '',
|
updatetime: '',
|
}
|
state.menutree = [];
|
|
LoadMenuTree();
|
};
|
const getSearchCondition = () => {
|
let condition;
|
if (!state.showAdvanceSearchView) {
|
condition = {
|
filter: state.searchValue.filter,
|
permissionLevel: store.permissionLevel,
|
}
|
} else {
|
condition = {
|
name: state.searchValue.name,
|
url: state.searchValue.url,
|
path: state.searchValue.path,
|
component: state.searchValue.component,
|
type: state.searchValue.type,
|
}
|
}
|
|
return condition;
|
};
|
const LoadData = () => {
|
state.loading = true
|
let condition = getSearchCondition();
|
|
GetMenus(condition).then(resp => {
|
state.loading = false
|
if (resp.data) {
|
state.menus = resp.data.data
|
state.total = resp.data.total
|
}
|
}).catch(err => {
|
console.error(err)
|
})
|
};
|
const LoadMenuTree = () => {
|
GetMenuTree().then(resp => {
|
state.loading = false
|
if (resp.data.code === 200) {
|
state.menutree = resp.data.data;
|
}
|
}).catch(err => {
|
console.error(err)
|
})
|
};
|
const ClearSearchData = () => {
|
state.searchValue = {
|
filter: "",
|
name: '',
|
url: '',
|
path: '',
|
component: '',
|
type: null,
|
};
|
};
|
const SearchData = () => {
|
LoadData()
|
};
|
const showAddView = () => {
|
clearAddOrEditModel();
|
state.title = proxy.$t('button.New');
|
state.dialogMode = 'new';
|
state.dialogVisible = true;
|
};
|
const onSubmit = () => {
|
console.log(state.menu)
|
if (state.menu.id) {
|
proxy.$refs['dialogForm'].validate(valid => {
|
if (valid) {
|
proxy.$confirm(`${proxy.$t('message.AreYouSureToSubmit')}`, `${proxy.$t('label.SystemConfirm')}`, {
|
confirmButtonText: `${proxy.$t('button.Yes')}`,
|
cancelButtonText: `${proxy.$t('button.No')}`,
|
type: 'warning',
|
center: true
|
}).then(() => {
|
const rLoading = proxy.openLoading("#detailDialog");
|
UpdateMenu(state.menu)
|
.then(resp => {
|
if (resp.data && resp.data.code === 200) {
|
state.dialogVisible = false
|
LoadData()
|
} else {
|
proxy.$message.error({message: `${proxy.$t('message.' + resp.data.message)}`});
|
}
|
rLoading.close();
|
})
|
.catch(error => {
|
console.error(error);
|
proxy.$message.error({message: error});
|
});
|
}).catch((error) => {
|
console.error(error)
|
});
|
}
|
});
|
} else {
|
proxy.$refs['dialogForm'].validate(valid => {
|
if (valid) {
|
proxy.$confirm(`${proxy.$t('message.AreYouSureToSubmit')}`, `${proxy.$t('label.SystemConfirm')}`, {
|
confirmButtonText: `${proxy.$t('button.Yes')}`,
|
cancelButtonText: `${proxy.$t('button.No')}`,
|
type: 'warning',
|
center: true
|
}).then(() => {
|
const rLoading = proxy.openLoading("#detailDialog");
|
CreateMenu(state.menu)
|
.then(resp => {
|
if (resp.data && resp.data.code === 200) {
|
state.dialogVisible = false
|
LoadData()
|
} else {
|
proxy.$message.error({message: `${proxy.$t('message.' + resp.data.message)}`});
|
}
|
rLoading.close();
|
})
|
.catch(error => {
|
console.error(error);
|
proxy.$message.error({message: error});
|
});
|
}).catch((error) => {
|
console.error(error)
|
});
|
}
|
});
|
}
|
};
|
const showEditView = (data) => {
|
state.title = `${proxy.$t('button.Edit')} [${data.name}]`;
|
state.dialogMode = 'edit';
|
state.menu = data;
|
state.dialogVisible = true;
|
};
|
const deleteItem = (data) => {
|
proxy.$confirm(`${proxy.$t('message.AreYouSureToRemove')}`, `${proxy.$t('label.SystemConfirm')}`, {
|
confirmButtonText: `${proxy.$t('button.Yes')}`,
|
cancelButtonText: `${proxy.$t('button.No')}`,
|
confirmButtonClass: 'confirmButtonClass',
|
cancelButtonClass: 'cancelButtonClass',
|
type: 'warning',
|
center: true
|
}).then(() => {
|
state.loading = true;
|
DeleteMenu(data.id)
|
.then(resp => {
|
if (resp.data && resp.data.code === 200) {
|
LoadData()
|
} else {
|
state.loading = false;
|
proxy.$message.error({message: `${proxy.$t('message.' + resp.data.message)}`});
|
}
|
})
|
.catch(error => {
|
proxy.$message.error({message: error});
|
});
|
}).catch((error) => {
|
console.error(error)
|
state.loading = false;
|
});
|
};
|
//Pick parent for auth atom
|
const handleParentTreeNodeClick = (data) => {
|
console.log(data)
|
if (state.menu.id !== data.id) {
|
state.menu.parentid = data.id;
|
state.menu.parentcode = data.code;
|
state.menu.parentname = data.name;
|
|
if (state.menu.component === 'LayoutAdmin') {
|
state.menu.component = '';
|
}
|
}
|
};
|
const parentChange = (data) => {
|
if (!data) {
|
state.menu.parentid = "";
|
state.menu.parentcode = "";
|
state.menu.parentname = "";
|
}
|
};
|
|
//formatter
|
const formatMenuType = (row, column) => {
|
switch (row.type) {
|
case 0:
|
return `${proxy.$t('label.Undefined')}`
|
case 1:
|
return `${proxy.$t('label.Page')}`
|
case 2:
|
return `${proxy.$t('label.Menu')}`
|
case 3:
|
return `${proxy.$t('label.Button')}`
|
}
|
};
|
const formatterDatetime = (row, column) => {
|
let date = null;
|
switch (column.property) {
|
case "createtime":
|
if (!row.createtime)
|
return null;
|
date = new Date(row.createtime);
|
break;
|
case "birthday":
|
if (!row.birthday)
|
return null;
|
date = new Date(row.birthday);
|
break;
|
case "actiontime":
|
if (!row.actiontime)
|
return null;
|
date = new Date(row.actiontime);
|
break;
|
case "validdateto":
|
if (!row.validdateto)
|
return null;
|
date = new Date(row.validdateto);
|
break;
|
case "validdatefrom":
|
if (!row.validdatefrom)
|
return null;
|
date = new Date(row.validdatefrom);
|
break;
|
}
|
|
return formatDate(date, 'yyyy-MM-dd hh:mm');
|
};
|
const formatterStatusBoolean = (row, column) => {
|
var ret = ''
|
if (row.status) {
|
ret = "Valid";
|
} else {
|
ret = "Invalid";
|
}
|
return ret;
|
};
|
const capitalize = (value) => {
|
if (!value) return ''
|
value = value.toString()
|
return value.charAt(0).toUpperCase() + value.slice(1)
|
};
|
|
return {
|
...toRefs(state),
|
LoadData,
|
clearAddOrEditModel,
|
ClearSearchData,
|
SearchData,
|
showAddView,
|
onSubmit,
|
showEditView,
|
deleteItem,
|
handleParentTreeNodeClick,
|
formatMenuType,
|
formatterDatetime,
|
formatterStatusBoolean,
|
capitalize,
|
icon,
|
parentChange
|
}
|
},
|
}
|
</script>
|
|
<style>
|
|
</style>
|