mirror of https://github.com/certd/certd
perf: 顶部菜单自定义
parent
661293c189
commit
54d136cc6a
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<a-dropdown class="fs-locale-picker">
|
||||
<div>
|
||||
<div style="display: block">
|
||||
<fs-iconify icon="ion-globe-outline" @click.prevent></fs-iconify>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -89,11 +89,12 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
const title: any = () => {
|
||||
if (sub?.meta?.icon) {
|
||||
const icon = sub.icon || sub?.meta?.icon;
|
||||
if (icon) {
|
||||
// @ts-ignore
|
||||
return (
|
||||
<div class={"menu-item-title"}>
|
||||
<fsIcon class={"anticon"} icon={sub.meta.icon} />
|
||||
<fsIcon class={"anticon"} icon={icon} />
|
||||
<span>{sub.title}</span>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
<!-- >-->
|
||||
<!-- Button-->
|
||||
<!-- </button>-->
|
||||
<fs-menu class="header-menu" mode="horizontal" :expand-selected="false" :selectable="false" :menus="settingStore.getHeaderMenus" />
|
||||
|
||||
<fs-menu
|
||||
v-if="!settingStore?.isAgent && !settingStore.isComm"
|
||||
class="header-menu"
|
||||
|
|
|
@ -68,20 +68,20 @@ export const sysResources = [
|
|||
permission: "sys:settings:view"
|
||||
}
|
||||
},
|
||||
// {
|
||||
// title: "顶部菜单设置",
|
||||
// name: "HeaderMenus",
|
||||
// path: "/sys/settings/header-menus",
|
||||
// component: "/sys/settings/header-menus/index.vue",
|
||||
// meta: {
|
||||
// show: () => {
|
||||
// const settingStore = useSettingStore();
|
||||
// return settingStore.isComm;
|
||||
// },
|
||||
// icon: "ion:document-text-outline",
|
||||
// permission: "sys:settings:view"
|
||||
// }
|
||||
// },
|
||||
{
|
||||
title: "顶部菜单设置",
|
||||
name: "HeaderMenus",
|
||||
path: "/sys/settings/header-menus",
|
||||
component: "/sys/settings/header-menus/index.vue",
|
||||
meta: {
|
||||
show: () => {
|
||||
const settingStore = useSettingStore();
|
||||
return settingStore.isComm;
|
||||
},
|
||||
icon: "ion:document-text-outline",
|
||||
permission: "sys:settings:view"
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "系统级授权",
|
||||
name: "SysAccess",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { Modal, notification, theme } from "ant-design-vue";
|
||||
import _ from "lodash-es";
|
||||
import _, { cloneDeep } from "lodash-es";
|
||||
// @ts-ignore
|
||||
import { LocalStorage } from "/src/utils/util.storage";
|
||||
|
||||
|
@ -10,6 +10,7 @@ import { useUserStore } from "/@/store/modules/user";
|
|||
import { mitter } from "/@/utils/util.mitt";
|
||||
import { env } from "/@/utils/util.env";
|
||||
import { toRef } from "vue";
|
||||
import { util } from "/@/utils";
|
||||
|
||||
export type ThemeToken = {
|
||||
token: {
|
||||
|
@ -120,6 +121,9 @@ export const useSettingStore = defineStore({
|
|||
comm: "商业版"
|
||||
};
|
||||
return vipLabelMap[this.plusInfo?.vipType || "free"];
|
||||
},
|
||||
getHeaderMenus() {
|
||||
return this.headerMenus?.menus || [];
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
@ -137,6 +141,7 @@ export const useSettingStore = defineStore({
|
|||
_.merge(this.installInfo, allSettings.installInfo || {});
|
||||
_.merge(this.siteEnv, allSettings.siteEnv || {});
|
||||
_.merge(this.plusInfo, allSettings.plusInfo || {});
|
||||
_.merge(this.headerMenus, allSettings.headerMenus || {});
|
||||
//@ts-ignore
|
||||
this.initSiteInfo(allSettings.siteInfo || {});
|
||||
},
|
||||
|
|
|
@ -63,3 +63,8 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settings-form {
|
||||
width: 800px;
|
||||
margin: 20px;
|
||||
}
|
||||
|
|
|
@ -235,7 +235,7 @@ h1, h2, h3, h4, h5, h6 {
|
|||
}
|
||||
}
|
||||
|
||||
.settings-form {
|
||||
width: 800px;
|
||||
margin: 20px;
|
||||
|
||||
.fs-16{
|
||||
font-size: 16px;
|
||||
}
|
||||
|
|
|
@ -4,11 +4,13 @@ import * as storages from "./util.storage";
|
|||
import commons from "./util.common";
|
||||
import * as mitt from "./util.mitt";
|
||||
import router from "/util.router";
|
||||
import { treeUtils } from "./util.tree";
|
||||
export const util = {
|
||||
...envs,
|
||||
...sites,
|
||||
...storages,
|
||||
...commons,
|
||||
...mitt,
|
||||
...router
|
||||
...router,
|
||||
tree: treeUtils
|
||||
};
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
export function eachTree(tree: any[], callback: (item: any) => void) {
|
||||
tree.forEach((item) => {
|
||||
callback(item);
|
||||
if (item.children) {
|
||||
eachTree(item.children, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const treeUtils = {
|
||||
eachTree
|
||||
};
|
|
@ -1,99 +1,124 @@
|
|||
import { useI18n } from "vue-i18n";
|
||||
import { Ref, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from "@fast-crud/fast-crud";
|
||||
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
import { cloneDeep, find, merge, remove } from "lodash-es";
|
||||
import { nanoid } from "nanoid";
|
||||
import { SettingsSave } from "../api";
|
||||
|
||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const { crudBinding } = crudExpose;
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const settingStore = useSettingStore();
|
||||
const menusRef = ref(cloneDeep(settingStore.headerMenus?.menus || []));
|
||||
|
||||
const selectedRowKeys: Ref<any[]> = ref([]);
|
||||
context.selectedRowKeys = selectedRowKeys;
|
||||
async function saveMenus() {
|
||||
const menus = settingStore.headerMenus;
|
||||
await SettingsSave("sys.header.menus", menus);
|
||||
}
|
||||
|
||||
function eachTree(tree: any[], callback: (item: any) => void) {
|
||||
tree.forEach((item) => {
|
||||
callback(item);
|
||||
if (item.children) {
|
||||
eachTree(item.children, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const expandedRowKeys = ref<string[]>([]);
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
const records = cloneDeep(settingStore.headerMenus?.menus || []);
|
||||
expandedRowKeys.value = [];
|
||||
eachTree(records, (item) => {
|
||||
if (item.children && item.children.length > 0) {
|
||||
expandedRowKeys.value.push(item.id);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
records: records,
|
||||
total: records.length,
|
||||
limit: 9999999,
|
||||
offset: 0
|
||||
};
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
form.id = row.id;
|
||||
let found: any = undefined;
|
||||
eachTree(settingStore.headerMenus?.menus || [], (item) => {
|
||||
if (item.id === row.id) {
|
||||
merge(item, form);
|
||||
found = item;
|
||||
}
|
||||
});
|
||||
await saveMenus();
|
||||
return found;
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
eachTree([{ children: settingStore.headerMenus?.menus }], (item) => {
|
||||
if (item.children) {
|
||||
remove(item.children, (child) => child.id === row.id);
|
||||
}
|
||||
});
|
||||
await saveMenus();
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
form.id = nanoid();
|
||||
if (form.parentId) {
|
||||
eachTree(settingStore.headerMenus?.menus || [], (item) => {
|
||||
if (item.id === form.parentId) {
|
||||
if (!item.children) {
|
||||
item.children = [];
|
||||
}
|
||||
item.children.push(form);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
settingStore.headerMenus?.menus.push(form);
|
||||
}
|
||||
parent.value = null;
|
||||
await saveMenus();
|
||||
return form;
|
||||
};
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
settings: {
|
||||
plugins: {
|
||||
//这里使用行选择插件,生成行选择crudOptions配置,最终会与crudOptions合并
|
||||
rowSelection: {
|
||||
enabled: true,
|
||||
order: -2,
|
||||
before: true,
|
||||
// handle: (pluginProps,useCrudProps)=>CrudOptions,
|
||||
props: {
|
||||
multiple: true,
|
||||
crossPage: true,
|
||||
selectedRowKeys
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
actionbar: {
|
||||
buttons: {
|
||||
add: {
|
||||
show: false
|
||||
},
|
||||
addRow: {
|
||||
show: true,
|
||||
click: () => {
|
||||
crudBinding.value.data.push({ id: nanoid() });
|
||||
}
|
||||
},
|
||||
save: {
|
||||
text: "保存菜单",
|
||||
type: "primary",
|
||||
click: async () => {
|
||||
await settingStore.saveHeaderMenus({ menus: menusRef.value });
|
||||
}
|
||||
}
|
||||
}
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
search: {
|
||||
show: false
|
||||
},
|
||||
toolbar: {
|
||||
buttons: {
|
||||
refresh: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
},
|
||||
mode: {
|
||||
name: "local",
|
||||
isMergeWhenUpdate: true,
|
||||
isAppendWhenAdd: true
|
||||
},
|
||||
table: {
|
||||
defaultExpandAllRows: true,
|
||||
expandRowByClick: true,
|
||||
editable: {
|
||||
enabled: true,
|
||||
mode: "row",
|
||||
activeDefault: true,
|
||||
showAction: true,
|
||||
rowKey: "id"
|
||||
defaultExpandAllRows: true,
|
||||
expandedRowKeys: expandedRowKeys,
|
||||
"onUpdate:expandedRowKeys": (val: string[]) => {
|
||||
expandedRowKeys.value = val;
|
||||
}
|
||||
},
|
||||
pagination: { show: false, pageSize: 9999999 },
|
||||
rowHandle: {
|
||||
width: 300,
|
||||
fixed: "right",
|
||||
group: {
|
||||
editRow: {
|
||||
addChild: {
|
||||
text: "添加子菜单",
|
||||
click: ({ row }) => {
|
||||
if (row.children == null) {
|
||||
row.children = [];
|
||||
buttons: {
|
||||
addChild: {
|
||||
title: "添加子菜单",
|
||||
text: null,
|
||||
type: "link",
|
||||
icon: "ion:add-circle-outline",
|
||||
click: ({ row }) => {
|
||||
crudExpose.openAdd({
|
||||
row: {
|
||||
parentId: row.id
|
||||
}
|
||||
row.children.push({ id: nanoid() });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -104,7 +129,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
key: "id",
|
||||
type: "text",
|
||||
column: {
|
||||
width: 200
|
||||
width: 200,
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
|
@ -115,20 +141,60 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
type: "text",
|
||||
column: {
|
||||
width: 300
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入标题"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
icon: {
|
||||
title: "图标",
|
||||
type: "text",
|
||||
column: {
|
||||
width: 300
|
||||
width: 300,
|
||||
cellRender: ({ row }) => {
|
||||
return <fs-icon class={"fs-16"} icon={row.icon}></fs-icon>;
|
||||
}
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
placeholder: "ion:add-circle"
|
||||
},
|
||||
helper: {
|
||||
render: () => {
|
||||
return (
|
||||
<span>
|
||||
<a href="https://icon-sets.iconify.design/" target="_blank">
|
||||
图标库
|
||||
</a>
|
||||
<span>--搜索--选择图标--复制名称--填到这里</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
link: {
|
||||
path: {
|
||||
title: "链接",
|
||||
type: "text",
|
||||
type: "link",
|
||||
column: {
|
||||
width: 300
|
||||
},
|
||||
form: {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入链接"
|
||||
},
|
||||
{
|
||||
type: "url",
|
||||
message: "请输入正确的链接"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { onMounted } from "vue";
|
|||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
|
||||
defineOptions({
|
||||
name: "SettingsHeaderMenus"
|
||||
});
|
||||
|
@ -21,7 +21,7 @@ const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions
|
|||
const settingStore = useSettingStore();
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudBinding.value.data = cloneDeep(settingStore.headerMenus.menus || []);
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
</script>
|
||||
<style lang="less"></style>
|
||||
|
|
|
@ -64,6 +64,7 @@ import * as api from "./api";
|
|||
import { notification } from "ant-design-vue";
|
||||
import { useSettingStore } from "/src/store/modules/settings";
|
||||
import { useUserStore } from "/@/store/modules/user";
|
||||
import { merge } from "lodash-es";
|
||||
|
||||
defineOptions({
|
||||
name: "SiteSetting"
|
||||
|
@ -85,7 +86,7 @@ async function loadSysSiteSettings() {
|
|||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
Object.assign(formState, data);
|
||||
merge(formState, data);
|
||||
}
|
||||
const saveLoading = ref(false);
|
||||
loadSysSiteSettings();
|
||||
|
|
Loading…
Reference in New Issue