(function () {
|
function authHeaders() {
|
return {
|
token: localStorage.getItem('token') || ''
|
};
|
}
|
|
function isForbidden(res) {
|
return res && Number(res.code) === 403;
|
}
|
|
function isOk(res) {
|
return res && (Number(res.code) === 200 || Number(res.code) === 0);
|
}
|
|
function normalizeId(value) {
|
if (value === null || value === undefined || value === '') {
|
return null;
|
}
|
var numberValue = Number(value);
|
return isNaN(numberValue) ? value : numberValue;
|
}
|
|
function normalizeNumber(value, fallback) {
|
if (value === null || value === undefined || value === '') {
|
return fallback;
|
}
|
var numberValue = Number(value);
|
return isNaN(numberValue) ? fallback : numberValue;
|
}
|
|
function cloneRow(row) {
|
return {
|
id: normalizeId(row.id),
|
code: row.code || '',
|
name: row.name || '',
|
resourceId: normalizeId(row.resourceId),
|
level: normalizeNumber(row.level, 1),
|
sort: normalizeNumber(row.sort, 999),
|
status: normalizeNumber(row.status, 1)
|
};
|
}
|
|
function createDefaultForm() {
|
return {
|
id: null,
|
resourceId: null,
|
code: '',
|
level: 1,
|
name: '',
|
sort: 999,
|
status: 1
|
};
|
}
|
|
function sortNodes(nodes) {
|
nodes.sort(function (left, right) {
|
var sortDiff = normalizeNumber(left.sort, 999) - normalizeNumber(right.sort, 999);
|
if (sortDiff !== 0) {
|
return sortDiff;
|
}
|
return String(left.id).localeCompare(String(right.id), 'zh-Hans-CN', {numeric: true});
|
});
|
nodes.forEach(function (node) {
|
if (node.children && node.children.length > 0) {
|
sortNodes(node.children);
|
}
|
});
|
return nodes;
|
}
|
|
function buildTree(rows) {
|
var nodeMap = {};
|
var roots = [];
|
|
rows.forEach(function (item) {
|
var node = cloneRow(item);
|
node.children = [];
|
nodeMap[node.id] = node;
|
});
|
|
Object.keys(nodeMap).forEach(function (key) {
|
var current = nodeMap[key];
|
if (current.resourceId !== null && nodeMap[current.resourceId] && current.resourceId !== current.id) {
|
nodeMap[current.resourceId].children.push(current);
|
} else {
|
roots.push(current);
|
}
|
});
|
|
return sortNodes(roots);
|
}
|
|
new Vue({
|
el: '#app',
|
data: function () {
|
var vm = this;
|
return {
|
loading: false,
|
tableData: [],
|
tableKey: 0,
|
tableHeight: Math.max(window.innerHeight - 198, 360),
|
expandAll: false,
|
selection: [],
|
flatRows: [],
|
resourceLookup: {},
|
childrenLookup: {},
|
editingId: null,
|
dialog: {
|
visible: false,
|
mode: 'create',
|
submitting: false
|
},
|
dialogForm: createDefaultForm(),
|
parentCascaderProps: {
|
checkStrictly: true,
|
emitPath: false,
|
value: 'id',
|
label: 'name',
|
children: 'children'
|
},
|
dialogRules: {
|
code: [
|
{required: true, message: '请输入菜单编码', trigger: 'blur'}
|
],
|
name: [
|
{required: true, message: '请输入菜单名称', trigger: 'blur'}
|
],
|
level: [
|
{required: true, message: '请选择类型', trigger: 'change'}
|
],
|
resourceId: [
|
{
|
validator: function (rule, value, callback) {
|
if (Number(vm.dialogForm.level) > 1 && (value === null || value === undefined || value === '')) {
|
callback(new Error('请选择上级菜单'));
|
return;
|
}
|
callback();
|
},
|
trigger: 'change'
|
}
|
],
|
sort: [
|
{type: 'number', message: '排序必须为数字', trigger: 'blur'}
|
],
|
status: [
|
{required: true, message: '请选择状态', trigger: 'change'}
|
]
|
}
|
};
|
},
|
computed: {
|
parentOptions: function () {
|
var blocked = {};
|
if (this.editingId !== null) {
|
blocked = this.collectDescendantIds(this.editingId);
|
}
|
return this.buildParentOptions(this.tableData, blocked);
|
}
|
},
|
created: function () {
|
this.loadTree();
|
},
|
mounted: function () {
|
window.addEventListener('resize', this.handleResize);
|
},
|
beforeDestroy: function () {
|
window.removeEventListener('resize', this.handleResize);
|
},
|
methods: {
|
createDefaultForm: function () {
|
return createDefaultForm();
|
},
|
handleResize: function () {
|
this.tableHeight = Math.max(window.innerHeight - 198, 360);
|
this.refreshTableLayout();
|
},
|
handleForbidden: function (res) {
|
if (isForbidden(res)) {
|
top.location.href = baseUrl + '/';
|
return true;
|
}
|
return false;
|
},
|
refreshTableLayout: function () {
|
var vm = this;
|
this.$nextTick(function () {
|
if (vm.$refs.treeTable && vm.$refs.treeTable.doLayout) {
|
vm.$refs.treeTable.doLayout();
|
}
|
});
|
},
|
rebuildIndexes: function (rows) {
|
var lookup = {};
|
var childrenLookup = {};
|
|
rows.forEach(function (item) {
|
var normalized = cloneRow(item);
|
lookup[normalized.id] = normalized;
|
if (normalized.resourceId !== null) {
|
if (!childrenLookup[normalized.resourceId]) {
|
childrenLookup[normalized.resourceId] = [];
|
}
|
childrenLookup[normalized.resourceId].push(normalized.id);
|
}
|
});
|
|
this.flatRows = rows.map(function (item) {
|
return cloneRow(item);
|
});
|
this.resourceLookup = lookup;
|
this.childrenLookup = childrenLookup;
|
},
|
loadTree: function () {
|
var vm = this;
|
vm.loading = true;
|
$.ajax({
|
url: baseUrl + '/resource/tree/auth',
|
method: 'GET',
|
headers: authHeaders(),
|
success: function (res) {
|
vm.loading = false;
|
if (vm.handleForbidden(res)) {
|
return;
|
}
|
if (!isOk(res)) {
|
vm.$message.error(res && res.msg ? res.msg : '菜单加载失败');
|
return;
|
}
|
var rows = Array.isArray(res.data) ? res.data : [];
|
vm.rebuildIndexes(rows);
|
vm.tableData = buildTree(rows);
|
vm.selection = [];
|
vm.tableKey += 1;
|
vm.refreshTableLayout();
|
},
|
error: function () {
|
vm.loading = false;
|
vm.$message.error('菜单加载失败');
|
}
|
});
|
},
|
handleSelectionChange: function (rows) {
|
this.selection = rows || [];
|
},
|
toggleExpandAll: function () {
|
this.expandAll = !this.expandAll;
|
this.tableKey += 1;
|
this.refreshTableLayout();
|
},
|
levelText: function (level) {
|
var currentLevel = Number(level);
|
if (currentLevel === 1) {
|
return '一级菜单';
|
}
|
if (currentLevel === 2) {
|
return '二级菜单';
|
}
|
if (currentLevel === 3) {
|
return '按钮';
|
}
|
return '--';
|
},
|
levelTagType: function (level) {
|
var currentLevel = Number(level);
|
if (currentLevel === 3) {
|
return 'info';
|
}
|
if (currentLevel === 2) {
|
return '';
|
}
|
return 'success';
|
},
|
parentName: function (resourceId) {
|
if (resourceId === null || resourceId === undefined || resourceId === '') {
|
return '';
|
}
|
var parent = this.resourceLookup[normalizeId(resourceId)];
|
return parent ? parent.name : '';
|
},
|
collectDescendantIds: function (resourceId) {
|
var blocked = {};
|
var stack = [normalizeId(resourceId)];
|
while (stack.length > 0) {
|
var currentId = stack.pop();
|
if (currentId === null || blocked[currentId]) {
|
continue;
|
}
|
blocked[currentId] = true;
|
var childIds = this.childrenLookup[currentId] || [];
|
childIds.forEach(function (childId) {
|
stack.push(childId);
|
});
|
}
|
return blocked;
|
},
|
buildParentOptions: function (nodes, blocked) {
|
var vm = this;
|
return (nodes || []).reduce(function (result, node) {
|
if (blocked[node.id]) {
|
return result;
|
}
|
var option = {
|
id: node.id,
|
name: node.name,
|
children: vm.buildParentOptions(node.children || [], blocked)
|
};
|
if (option.children.length === 0) {
|
delete option.children;
|
}
|
result.push(option);
|
return result;
|
}, []);
|
},
|
resetDialogForm: function () {
|
this.editingId = null;
|
this.dialogForm = this.createDefaultForm();
|
var vm = this;
|
this.$nextTick(function () {
|
if (vm.$refs.dialogForm) {
|
vm.$refs.dialogForm.clearValidate();
|
}
|
});
|
},
|
openCreateDialog: function () {
|
this.dialog.mode = 'create';
|
this.dialog.visible = true;
|
this.resetDialogForm();
|
},
|
openEditDialog: function (row) {
|
this.dialog.mode = 'edit';
|
this.dialog.visible = true;
|
this.editingId = normalizeId(row.id);
|
this.dialogForm = {
|
id: normalizeId(row.id),
|
resourceId: normalizeId(row.resourceId),
|
code: row.code || '',
|
level: normalizeNumber(row.level, 1),
|
name: row.name || '',
|
sort: normalizeNumber(row.sort, 999),
|
status: normalizeNumber(row.status, 1)
|
};
|
var vm = this;
|
this.$nextTick(function () {
|
if (vm.$refs.dialogForm) {
|
vm.$refs.dialogForm.clearValidate();
|
}
|
});
|
},
|
removeSelection: function () {
|
if (!this.selection.length) {
|
this.$message.warning('请选择要删除的数据');
|
return;
|
}
|
var ids = this.selection.map(function (row) {
|
return normalizeId(row.id);
|
}).filter(function (id) {
|
return id !== null;
|
});
|
this.removeRows(ids);
|
},
|
removeRows: function (ids) {
|
var vm = this;
|
var uniqueIds = Array.from(new Set((ids || []).map(function (id) {
|
return normalizeId(id);
|
}).filter(function (id) {
|
return id !== null;
|
})));
|
if (!uniqueIds.length) {
|
vm.$message.warning('请选择要删除的数据');
|
return;
|
}
|
vm.$confirm('确定要删除选中数据吗?', '提示', {
|
confirmButtonText: '确定',
|
cancelButtonText: '取消',
|
type: 'warning'
|
}).then(function () {
|
$.ajax({
|
url: baseUrl + '/resource/delete/auth',
|
method: 'POST',
|
headers: authHeaders(),
|
traditional: true,
|
data: {
|
ids: uniqueIds
|
},
|
success: function (res) {
|
if (vm.handleForbidden(res)) {
|
return;
|
}
|
if (!isOk(res)) {
|
vm.$message.error(res && res.msg ? res.msg : '删除失败');
|
return;
|
}
|
vm.$message.success(res.msg || '删除成功');
|
vm.loadTree();
|
},
|
error: function () {
|
vm.$message.error('删除失败');
|
}
|
});
|
}).catch(function () {
|
});
|
},
|
submitDialog: function () {
|
var vm = this;
|
if (!vm.$refs.dialogForm) {
|
return;
|
}
|
vm.$refs.dialogForm.validate(function (valid) {
|
if (!valid) {
|
return false;
|
}
|
var payload = {
|
id: vm.dialog.mode === 'edit' ? normalizeId(vm.dialogForm.id) : null,
|
resourceId: Number(vm.dialogForm.level) === 1 ? null : normalizeId(vm.dialogForm.resourceId),
|
code: $.trim(vm.dialogForm.code),
|
level: normalizeNumber(vm.dialogForm.level, 1),
|
name: $.trim(vm.dialogForm.name),
|
sort: normalizeNumber(vm.dialogForm.sort, 999),
|
status: normalizeNumber(vm.dialogForm.status, 1)
|
};
|
vm.dialog.submitting = true;
|
$.ajax({
|
url: baseUrl + '/resource/' + (vm.dialog.mode === 'create' ? 'add' : 'update') + '/auth',
|
method: 'POST',
|
headers: authHeaders(),
|
data: payload,
|
success: function (res) {
|
vm.dialog.submitting = false;
|
if (vm.handleForbidden(res)) {
|
return;
|
}
|
if (!isOk(res)) {
|
vm.$message.error(res && res.msg ? res.msg : '保存失败');
|
return;
|
}
|
vm.$message.success(res.msg || '保存成功');
|
vm.dialog.visible = false;
|
vm.loadTree();
|
},
|
error: function () {
|
vm.dialog.submitting = false;
|
vm.$message.error('保存失败');
|
}
|
});
|
return true;
|
});
|
}
|
},
|
watch: {
|
'dialogForm.level': function (value) {
|
if (Number(value) === 1) {
|
this.dialogForm.resourceId = null;
|
}
|
},
|
'dialog.visible': function (visible) {
|
if (!visible) {
|
this.resetDialogForm();
|
this.dialog.submitting = false;
|
}
|
}
|
}
|
});
|
})();
|