初始化

This commit is contained in:
2026-03-26 20:18:20 +08:00
commit 120a5b4dfd
368 changed files with 35926 additions and 0 deletions

View File

@@ -0,0 +1,203 @@
export interface IconOptionGroup {
label: string;
value: string;
icons: string[];
}
export const menuIconGroups: IconOptionGroup[] = [
{
label: '导航布局',
value: 'navigation',
icons: [
'mdi:home-outline',
'mdi:view-dashboard-outline',
'mdi:menu-open',
'mdi:map',
'mdi:compass-outline',
'mdi:application-outline',
'mdi:monitor-dashboard',
'material-symbols:dashboard-outline',
'material-symbols:space-dashboard-outline',
'material-symbols:route',
'material-symbols:account-tree-outline',
'icon-park-outline:all-application',
'tabler:layout-dashboard',
'tabler:sitemap',
'tabler:apps',
'tabler:browser',
'carbon:network-overlay',
'hugeicons:flow-square'
]
},
{
label: '系统管理',
value: 'system',
icons: [
'ep:setting',
'ep:tools',
'ep:operation',
'ep:management',
'ep:monitor',
'ep:platform',
'ep:connection',
'ep:office-building',
'mdi:cog-outline',
'mdi:tune-variant',
'mdi:server-outline',
'mdi:cloud-outline',
'mdi:shield-crown-outline',
'carbon:cloud-service-management',
'carbon:network-overlay',
'ic:round-manage-accounts',
'ic:round-supervisor-account',
'carbon:user-role',
'material-symbols:admin-panel-settings-outline',
'material-symbols:settings-outline-rounded'
]
},
{
label: '用户权限',
value: 'user',
icons: [
'ep:user',
'ep:user-filled',
'ep:avatar',
'mdi:account-outline',
'mdi:account-group-outline',
'mdi:account-key-outline',
'mdi:badge-account-outline',
'mdi:card-account-details-outline',
'mdi:key-outline',
'mdi:shield-account-outline',
'mdi:lock-outline',
'ri:admin-line',
'ri:user-settings-line',
'carbon:user-avatar',
'carbon:user-role',
'carbon:user-admin',
'carbon:user-multiple',
'ic:round-supervisor-account'
]
},
{
label: '文档表单',
value: 'document',
icons: [
'mdi:file-document-outline',
'mdi:file-document-edit-outline',
'mdi:file-document-multiple-outline',
'mdi:clipboard-outline',
'mdi:book-open-page-variant-outline',
'mdi:form-select',
'mdi:notebook-outline',
'ri:file-excel-2-line',
'ri:markdown-line',
'uiw:file-pdf',
'gridicons:posts',
'icon-park-outline:editor',
'material-symbols:article-outline',
'material-symbols:description-outline',
'ph:archive-box-light',
'ph:note-pencil',
'tabler:files',
'tabler:report'
]
},
{
label: '数据分析',
value: 'data',
icons: [
'ant-design:bar-chart-outlined',
'mdi:chart-areaspline',
'mdi:chart-bar',
'mdi:chart-box-outline',
'mdi:database-outline',
'mdi:table-large',
'mdi:table-search',
'icon-park-outline:table',
'material-symbols:analytics-outline',
'material-symbols:table-chart-outline',
'material-symbols:dataset-outline',
'simple-icons:apacheecharts',
'simple-icons:swiper',
'ri:pie-chart-2-line',
'ri:line-chart-line',
'tabler:chart-bar',
'tabler:chart-donut',
'tabler:database'
]
},
{
label: '业务工具',
value: 'business',
icons: [
'clarity:plugin-line',
'ic:round-barcode',
'mdi:printer',
'mdi:typewriter',
'mdi:video',
'mdi:map-marker-path',
'mdi:tools',
'mdi:wrench-outline',
'mdi:qrcode-scan',
'mdi:hammer-wrench',
'material-symbols:construction-outline',
'material-symbols:extension-outline',
'material-symbols:inventory-2-outline',
'carbon:tool-kit',
'carbon:settings-adjust',
'ph:toolbox',
'ph:package',
'hugeicons:flow-square'
]
},
{
label: '状态反馈',
value: 'status',
icons: [
'ant-design:exception-outlined',
'ic:baseline-block',
'ic:baseline-web-asset-off',
'ic:baseline-wifi-off',
'material-symbols:filter-list-off',
'mdi:alert-outline',
'mdi:check-circle-outline',
'mdi:close-circle-outline',
'mdi:information-outline',
'mdi:timer-sand',
'mdi:progress-question',
'tabler:alert-circle',
'tabler:circle-check',
'tabler:circle-x',
'tabler:info-circle'
]
},
{
label: '通用备选',
value: 'common',
icons: [
'mdi:apps-box',
'mdi:emoticon',
'mdi:ab-testing',
'mdi:alert',
'mdi:airballoon',
'mdi:airplane-edit',
'mdi:alpha-f-box-outline',
'mdi:arm-flex-outline',
'ph:alarm',
'ph:android-logo',
'ph:align-bottom',
'uil:basketball',
'uil:brightness-plus',
'uil:capture',
'ic:baseline-10mp',
'ic:baseline-access-time',
'ic:baseline-brightness-4',
'ic:baseline-brightness-5',
'ic:baseline-credit-card',
'entypo-social:google-hangouts'
]
}
];
export const menuIconOptions = Array.from(new Set(menuIconGroups.flatMap(group => group.icons)));

View File

@@ -0,0 +1,62 @@
import { defineComponent, ref, watch } from 'vue';
import { loadIcon } from '@iconify/vue';
import SvgIcon from '@/components/custom/svg-icon.vue';
export default defineComponent({
name: 'MenuIconCell',
props: {
icon: {
type: String,
default: ''
}
},
setup(props) {
const failed = ref(false);
watch(
() => props.icon,
(value, _, onCleanup) => {
const icon = value.trim();
failed.value = !icon;
if (!icon) {
return;
}
let active = true;
onCleanup(() => {
active = false;
});
loadIcon(icon)
.then(() => {
if (active) {
failed.value = false;
}
})
.catch(() => {
if (active) {
failed.value = true;
}
});
},
{ immediate: true }
);
return () => {
const icon = props.icon.trim();
if (!icon || failed.value) {
return <div class="flex-center text-[#909399]">--</div>;
}
return (
<div class="flex-center">
<SvgIcon icon={icon} class="text-18px text-[#303133]" />
</div>
);
};
}
});

View File

@@ -0,0 +1,58 @@
import { computed, defineComponent } from 'vue';
import type { PropType } from 'vue';
import BusinessTableActionCell, { type BusinessTableAction } from '@/components/custom/business-table-action-cell';
import { $t } from '@/locales';
export default defineComponent({
name: 'MenuOperateCell',
props: {
row: {
type: Object as PropType<Api.SystemManage.Menu>,
required: true
},
onEdit: {
type: Function as PropType<(row: Api.SystemManage.Menu) => void>,
required: true
},
onAddChild: {
type: Function as PropType<(row: Api.SystemManage.Menu) => void>,
required: true
},
onDelete: {
type: Function as PropType<(row: Api.SystemManage.Menu) => Promise<void>>,
required: true
}
},
setup(props) {
const actions = computed<BusinessTableAction[]>(() => {
const list: BusinessTableAction[] = [
{
key: 'edit',
label: $t('common.edit'),
buttonType: 'primary',
onClick: () => props.onEdit(props.row)
}
];
if (props.row.type !== 3) {
list.push({
key: 'addChild',
label: $t('page.system.menu.addChildMenu'),
buttonType: 'primary',
onClick: () => props.onAddChild(props.row)
});
}
list.push({
key: 'delete',
label: $t('common.delete'),
buttonType: 'danger',
onClick: () => props.onDelete(props.row)
});
return list;
});
return () => <BusinessTableActionCell actions={actions.value} />;
}
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
<script setup lang="ts">
import TableSearchPanel from '@/components/custom/table-search-panel.vue';
import { $t } from '@/locales';
defineOptions({ name: 'MenuSearch' });
interface Emits {
(e: 'reset'): void;
(e: 'search'): void;
}
const emit = defineEmits<Emits>();
const model = defineModel<Api.SystemManage.MenuSearchParams>('model', { required: true });
function reset() {
emit('reset');
}
function search() {
emit('search');
}
</script>
<template>
<TableSearchPanel :model="model" :action-col-lg="6" :action-col-md="8" @reset="reset" @search="search">
<ElCol :lg="6" :md="8" :sm="12">
<ElFormItem :label="$t('page.system.menu.menuName')" prop="name">
<ElInput v-model="model.name" clearable :placeholder="$t('page.system.menu.form.menuName')" />
</ElFormItem>
</ElCol>
</TableSearchPanel>
</template>
<style scoped></style>

View File

@@ -0,0 +1,79 @@
const LAYOUT_PREFIX = 'layout.';
const VIEW_PREFIX = 'view.';
const FIRST_LEVEL_ROUTE_COMPONENT_SPLIT = '$';
export function getLayoutAndPage(component?: string | null) {
let layout = '';
let page = '';
const [layoutOrPage = '', pageItem = ''] = component?.split(FIRST_LEVEL_ROUTE_COMPONENT_SPLIT) || [];
layout = getLayout(layoutOrPage);
page = getPage(pageItem || layoutOrPage);
return { layout, page };
}
function getLayout(layout: string) {
return layout.startsWith(LAYOUT_PREFIX) ? layout.replace(LAYOUT_PREFIX, '') : '';
}
function getPage(page: string) {
return page.startsWith(VIEW_PREFIX) ? page.replace(VIEW_PREFIX, '') : '';
}
export function transformLayoutAndPageToComponent(layout: string, page: string) {
const hasLayout = Boolean(layout);
const hasPage = Boolean(page);
if (hasLayout && hasPage) {
return `${LAYOUT_PREFIX}${layout}${FIRST_LEVEL_ROUTE_COMPONENT_SPLIT}${VIEW_PREFIX}${page}`;
}
if (hasLayout) {
return `${LAYOUT_PREFIX}${layout}`;
}
if (hasPage) {
return `${VIEW_PREFIX}${page}`;
}
return '';
}
/**
* Get route name by route path
*
* @param routeName
*/
export function getRoutePathByRouteName(routeName: string) {
return `/${routeName.replace(/_/g, '/')}`;
}
/**
* Get path param from route path
*
* @param routePath route path
*/
export function getPathParamFromRoutePath(routePath: string) {
const [path, param = ''] = routePath.split('/:');
return {
path,
param
};
}
/**
* Get route path with param
*
* @param routePath route path
* @param param path param
*/
export function getRoutePathWithParam(routePath: string, param: string) {
if (param.trim()) {
return `${routePath}/:${param}`;
}
return routePath;
}