UNPKG

vue-layout-mcp

Version:

A MCP server for generating Vue dashboard layout components

1,244 lines (1,135 loc) 25.1 kB
export const simpleTools = { // 大屏布局组件 - 两栏布局 'generate-layout-two-columns': { name: 'generate-layout-two-columns', description: '生成一个大屏两栏布局组件', inputSchema: { type: 'object', properties: { leftWidth: { type: 'number', description: '左侧栏宽度百分比' }, rightWidth: { type: 'number', description: '右侧栏宽度百分比' } } }, handler: async ({ leftWidth = 50, rightWidth = 50 }) => { const component = ` <template> <div class="dashboard-layout-two-columns"> <div class="left-column" :style="{ width: leftWidth + '%' }"> <slot name="left"> <div class="placeholder-content"> <h2>左侧区域</h2> <p>这是左侧内容区域,占比 {{ leftWidth }}%</p> </div> </slot> </div> <div class="right-column" :style="{ width: rightWidth + '%' }"> <slot name="right"> <div class="placeholder-content"> <h2>右侧区域</h2> <p>这是右侧内容区域,占比 {{ rightWidth }}%</p> </div> </slot> </div> </div> </template> <script> export default { name: 'DashboardTwoColumns', props: { leftWidth: { type: Number, default: ${leftWidth} }, rightWidth: { type: Number, default: ${rightWidth} } } } </script> <style scoped lang="scss"> .dashboard-layout-two-columns { display: flex; height: 100vh; width: 100vw; margin: 0; padding: 0; box-sizing: border-box; .left-column, .right-column { height: 100%; border: 1px solid #e0e0e0; box-sizing: border-box; overflow: auto; } .placeholder-content { padding: 20px; h2 { margin: 0 0 10px 0; font-size: 18px; color: #333; } p { margin: 0; color: #666; font-size: 14px; } } } </style> `.trim(); return { component: component, fileName: 'DashboardTwoColumns.vue' }; } }, // 大屏布局组件 - 三栏布局 'generate-layout-three-columns': { name: 'generate-layout-three-columns', description: '生成一个大屏三栏布局组件', inputSchema: { type: 'object', properties: { leftWidth: { type: 'number', description: '左侧栏宽度百分比' }, centerWidth: { type: 'number', description: '中间栏宽度百分比' }, rightWidth: { type: 'number', description: '右侧栏宽度百分比' } } }, handler: async ({ leftWidth = 30, centerWidth = 40, rightWidth = 30 }) => { const component = ` <template> <div class="dashboard-layout-three-columns"> <div class="left-column" :style="{ width: leftWidth + '%' }"> <slot name="left"> <div class="placeholder-content"> <h2>左侧区域</h2> <p>占比 {{ leftWidth }}%</p> </div> </slot> </div> <div class="center-column" :style="{ width: centerWidth + '%' }"> <slot name="center"> <div class="placeholder-content"> <h2>中间区域</h2> <p>占比 {{ centerWidth }}%</p> </div> </slot> </div> <div class="right-column" :style="{ width: rightWidth + '%' }"> <slot name="right"> <div class="placeholder-content"> <h2>右侧区域</h2> <p>占比 {{ rightWidth }}%</p> </div> </slot> </div> </div> </template> <script> export default { name: 'DashboardThreeColumns', props: { leftWidth: { type: Number, default: ${leftWidth} }, centerWidth: { type: Number, default: ${centerWidth} }, rightWidth: { type: Number, default: ${rightWidth} } } } </script> <style scoped lang="scss"> .dashboard-layout-three-columns { display: flex; height: 100vh; width: 100vw; margin: 0; padding: 0; box-sizing: border-box; .left-column, .center-column, .right-column { height: 100%; border: 1px solid #e0e0e0; box-sizing: border-box; overflow: auto; } .placeholder-content { padding: 20px; h2 { margin: 0 0 10px 0; font-size: 18px; color: #333; } p { margin: 0; color: #666; font-size: 14px; } } } </style> `.trim(); return { component: component, fileName: 'DashboardThreeColumns.vue' }; } }, // 大屏布局组件 - 九宫格布局 'generate-layout-grid-nine': { name: 'generate-layout-grid-nine', description: '生成一个大屏九宫格布局组件', inputSchema: { type: 'object', properties: {} }, handler: async () => { const component = ` <template> <div class="dashboard-layout-grid-nine"> <div class="grid-item grid-item-1"> <slot name="item1"> <div class="placeholder-content"> <h2>区域 1(大)</h2> <p>占据 2x2 的主要展示区域</p> </div> </slot> </div> <div class="grid-item grid-item-2"> <slot name="item2"> <div class="placeholder-content"> <h2>区域 2</h2> </div> </slot> </div> <div class="grid-item grid-item-3"> <slot name="item3"> <div class="placeholder-content"> <h2>区域 3</h2> </div> </slot> </div> <div class="grid-item grid-item-4"> <slot name="item4"> <div class="placeholder-content"> <h2>区域 4</h2> </div> </slot> </div> <div class="grid-item grid-item-5"> <slot name="item5"> <div class="placeholder-content"> <h2>区域 5</h2> </div> </slot> </div> <div class="grid-item grid-item-6"> <slot name="item6"> <div class="placeholder-content"> <h2>区域 6</h2> </div> </slot> </div> </div> </template> <script> export default { name: 'DashboardGridNine' } </script> <style scoped lang="scss"> .dashboard-layout-grid-nine { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr); gap: 0; height: 100vh; width: 100vw; margin: 0; padding: 0; box-sizing: border-box; .grid-item { border: 1px solid #e0e0e0; box-sizing: border-box; overflow: auto; display: flex; align-items: center; justify-content: center; } .grid-item-1 { grid-column: 1 / 3; grid-row: 1 / 3; } .grid-item-2 { grid-column: 3; grid-row: 1; } .grid-item-3 { grid-column: 3; grid-row: 2; } .grid-item-4 { grid-column: 1; grid-row: 3; } .grid-item-5 { grid-column: 2; grid-row: 3; } .grid-item-6 { grid-column: 3; grid-row: 3; } .placeholder-content { text-align: center; h2 { margin: 0 0 10px 0; font-size: 18px; color: #333; } p { margin: 0; color: #666; font-size: 14px; } } } </style> `.trim(); return { component: component, fileName: 'DashboardGridNine.vue' }; } }, // 大屏布局组件 - 顶部导航+内容区布局 'generate-layout-top-content': { name: 'generate-layout-top-content', description: '生成一个大屏顶部导航+内容区布局组件', inputSchema: { type: 'object', properties: { topHeight: { type: 'number', description: '顶部区域高度' } } }, handler: async ({ topHeight = 80 }) => { const component = ` <template> <div class="dashboard-layout-top-content"> <div class="top-section" :style="{ height: topHeight + 'px' }"> <slot name="top"> <div class="placeholder-content"> <h2>顶部导航区域</h2> <p>高度: {{ topHeight }}px</p> </div> </slot> </div> <div class="content-section"> <slot name="content"> <div class="placeholder-content"> <h2>主内容区域</h2> <p>这是主要内容展示区域</p> </div> </slot> </div> </div> </template> <script> export default { name: 'DashboardTopContent', props: { topHeight: { type: Number, default: ${topHeight} } } } </script> <style scoped lang="scss"> .dashboard-layout-top-content { display: flex; flex-direction: column; height: 100vh; width: 100vw; margin: 0; padding: 0; box-sizing: border-box; .top-section { border: 1px solid #e0e0e0; box-sizing: border-box; overflow: auto; display: flex; align-items: center; } .content-section { flex: 1; border: 1px solid #e0e0e0; box-sizing: border-box; overflow: auto; } .placeholder-content { padding: 20px; h2 { margin: 0 0 10px 0; font-size: 18px; color: #333; } p { margin: 0; color: #666; font-size: 14px; } } } </style> `.trim(); return { component: component, fileName: 'DashboardTopContent.vue' }; } }, // 侧边栏布局 'generate-layout-sidebar': { name: 'generate-layout-sidebar', description: '生成一个侧边栏+内容区布局组件', inputSchema: { type: 'object', properties: { sidebarWidth: { type: 'number', description: '侧边栏宽度(像素)' }, collapsible: { type: 'boolean', description: '是否可折叠' } } }, handler: async ({ sidebarWidth = 240, collapsible = true }) => { const component = ` <template> <div class="dashboard-layout-sidebar"> <div class="sidebar" :class="{ collapsed: isCollapsed }" :style="{ width: isCollapsed ? '64px' : sidebarWidth + 'px' }" > <div class="sidebar-header"> <slot name="sidebar-header"> <div class="placeholder-content"> <h3>侧边栏</h3> </div> </slot> </div> <div class="sidebar-content"> <slot name="sidebar"> <div class="placeholder-content"> <p>菜单内容</p> </div> </slot> </div> ${collapsible ? `<button class="collapse-btn" @click="toggleCollapse"> {{ isCollapsed ? '>' : '<' }} </button>` : ''} </div> <div class="main-content"> <slot name="content"> <div class="placeholder-content"> <h2>主内容区域</h2> <p>这是主要内容展示区域</p> </div> </slot> </div> </div> </template> <script> export default { name: 'DashboardSidebar', props: { sidebarWidth: { type: Number, default: ${sidebarWidth} }, collapsible: { type: Boolean, default: ${collapsible} } }, data() { return { isCollapsed: false } }, methods: { toggleCollapse() { this.isCollapsed = !this.isCollapsed this.$emit('collapse-change', this.isCollapsed) } } } </script> <style scoped lang="scss"> .dashboard-layout-sidebar { display: flex; height: 100vh; width: 100vw; margin: 0; padding: 0; box-sizing: border-box; .sidebar { position: relative; height: 100%; border: 1px solid #e0e0e0; transition: width 0.3s; display: flex; flex-direction: column; box-sizing: border-box; .sidebar-header { border-bottom: 1px solid #e0e0e0; box-sizing: border-box; padding: 15px; h3 { margin: 0; font-size: 16px; color: #333; } } .sidebar-content { flex: 1; overflow-y: auto; box-sizing: border-box; padding: 15px; } .collapse-btn { position: absolute; right: -12px; top: 50%; transform: translateY(-50%); width: 24px; height: 24px; border-radius: 50%; background: #666; color: #fff; border: 1px solid #ddd; cursor: pointer; z-index: 10; &:hover { background: #333; } } &.collapsed { .sidebar-header, .sidebar-content { overflow: hidden; } } } .main-content { flex: 1; border: 1px solid #e0e0e0; overflow: auto; box-sizing: border-box; } .placeholder-content { h2, h3 { margin: 0 0 10px 0; font-size: 18px; color: #333; } p { margin: 0 0 5px 0; color: #666; font-size: 14px; } } } </style> `.trim(); return { component: component, fileName: 'DashboardSidebar.vue' }; } }, // 圣杯布局(Header + Sidebar + Content + Footer) 'generate-layout-holy-grail': { name: 'generate-layout-holy-grail', description: '生成圣杯布局(顶部+侧边栏+内容+底部)', inputSchema: { type: 'object', properties: { headerHeight: { type: 'number', description: '顶部高度(像素)' }, footerHeight: { type: 'number', description: '底部高度(像素)' }, sidebarWidth: { type: 'number', description: '侧边栏宽度(像素)' } } }, handler: async ({ headerHeight = 64, footerHeight = 50, sidebarWidth = 200 }) => { const component = ` <template> <div class="dashboard-layout-holy-grail"> <header class="header" :style="{ height: headerHeight + 'px' }"> <slot name="header"> <div class="placeholder-content"> <h2>顶部导航栏</h2> </div> </slot> </header> <div class="body"> <aside class="sidebar" :style="{ width: sidebarWidth + 'px' }"> <slot name="sidebar"> <div class="placeholder-content"> <h3>侧边栏</h3> <p>菜单内容</p> </div> </slot> </aside> <main class="content"> <slot name="content"> <div class="placeholder-content"> <h2>主内容区域</h2> <p>这是主要内容展示区域</p> </div> </slot> </main> </div> <footer class="footer" :style="{ height: footerHeight + 'px' }"> <slot name="footer"> <div class="placeholder-content"> <p>底部信息栏</p> </div> </slot> </footer> </div> </template> <script> export default { name: 'DashboardHolyGrail', props: { headerHeight: { type: Number, default: ${headerHeight} }, footerHeight: { type: Number, default: ${footerHeight} }, sidebarWidth: { type: Number, default: ${sidebarWidth} } } } </script> <style scoped lang="scss"> .dashboard-layout-holy-grail { display: flex; flex-direction: column; height: 100vh; width: 100vw; margin: 0; padding: 0; box-sizing: border-box; .header { border: 1px solid #e0e0e0; display: flex; align-items: center; box-sizing: border-box; padding: 0 20px; } .body { flex: 1; display: flex; overflow: hidden; box-sizing: border-box; .sidebar { border: 1px solid #e0e0e0; overflow-y: auto; box-sizing: border-box; padding: 20px; } .content { flex: 1; border: 1px solid #e0e0e0; overflow: auto; box-sizing: border-box; padding: 20px; } } .footer { border: 1px solid #e0e0e0; display: flex; align-items: center; justify-content: center; box-sizing: border-box; padding: 0 20px; } .placeholder-content { h2, h3 { margin: 0 0 10px 0; font-size: 18px; color: #333; } p { margin: 0; color: #666; font-size: 14px; } } } </style> `.trim(); return { component: component, fileName: 'DashboardHolyGrail.vue' }; } }, // 卡片网格布局 'generate-layout-card-grid': { name: 'generate-layout-card-grid', description: '生成响应式卡片网格布局组件', inputSchema: { type: 'object', properties: { columns: { type: 'number', description: '列数' }, gap: { type: 'number', description: '间距(像素)' } } }, handler: async ({ columns = 4, gap = 20 }) => { const component = ` <template> <div class="dashboard-layout-card-grid" :style="{ gridTemplateColumns: \`repeat(\${columns}, 1fr)\`, gap: gap + 'px' }" > <div v-for="(item, index) in items" :key="index" class="card-item" > <slot :name="\`card-\${index}\`" :item="item"> <div class="card-content"> <slot name="card" :item="item" :index="index"> <div class="placeholder-content"> <h3>卡片 {{ index + 1 }}</h3> <p>这是第 {{ index + 1 }} 个卡片内容</p> </div> </slot> </div> </slot> </div> </div> </template> <script> export default { name: 'DashboardCardGrid', props: { columns: { type: Number, default: ${columns} }, gap: { type: Number, default: ${gap} }, items: { type: Array, default: () => [] } } } </script> <style scoped lang="scss"> .dashboard-layout-card-grid { display: grid; width: 100vw; height: 100vh; margin: 0; padding: 0; box-sizing: border-box; .card-item { border: 1px solid #e0e0e0; box-sizing: border-box; overflow: auto; display: flex; align-items: center; justify-content: center; .card-content { width: 100%; height: 100%; padding: 20px; } } .placeholder-content { text-align: center; h3 { margin: 0 0 10px 0; font-size: 16px; color: #333; } p { margin: 0; color: #666; font-size: 14px; } } @media (max-width: 1200px) { grid-template-columns: repeat(3, 1fr) !important; } @media (max-width: 768px) { grid-template-columns: repeat(2, 1fr) !important; } @media (max-width: 480px) { grid-template-columns: 1fr !important; } } </style> `.trim(); return { component: component, fileName: 'DashboardCardGrid.vue' }; } }, // Tab切换布局 'generate-layout-tabs': { name: 'generate-layout-tabs', description: '生成Tab标签页切换布局组件', inputSchema: { type: 'object', properties: { tabPosition: { type: 'string', description: 'Tab位置: top, bottom, left, right' } } }, handler: async ({ tabPosition = 'top' }) => { const component = ` <template> <div class="dashboard-layout-tabs" :class="\`tabs-\${tabPosition}\`"> <div class="tabs-header"> <div v-for="(tab, index) in tabs" :key="index" class="tab-item" :class="{ active: activeTab === index }" @click="switchTab(index)" > {{ tab.label }} </div> </div> <div class="tabs-content"> <div v-for="(tab, index) in tabs" :key="index" v-show="activeTab === index" class="tab-pane" > <slot :name="\`tab-\${index}\`" :tab="tab"> <div class="placeholder-content"> <h2>{{ tab.label }}</h2> <p>{{ tab.content }}</p> </div> </slot> </div> </div> </div> </template> <script> export default { name: 'DashboardTabs', props: { tabs: { type: Array, default: () => [ { label: '选项卡1', content: '选项卡1的内容' }, { label: '选项卡2', content: '选项卡2的内容' }, { label: '选项卡3', content: '选项卡3的内容' } ] }, tabPosition: { type: String, default: '${tabPosition}', validator: (value) => ['top', 'bottom', 'left', 'right'].includes(value) }, defaultActive: { type: Number, default: 0 } }, data() { return { activeTab: this.defaultActive } }, methods: { switchTab(index) { this.activeTab = index this.$emit('tab-change', index, this.tabs[index]) } } } </script> <style scoped lang="scss"> .dashboard-layout-tabs { display: flex; width: 100vw; height: 100vh; margin: 0; padding: 0; box-sizing: border-box; overflow: hidden; &.tabs-top, &.tabs-bottom { flex-direction: column; .tabs-header { display: flex; border-bottom: 1px solid #e0e0e0; .tab-item { padding: 12px 24px; cursor: pointer; border-bottom: 2px solid transparent; transition: all 0.3s; user-select: none; &:hover { background: #f5f5f5; } &.active { border-bottom-color: #333; } } } } &.tabs-bottom { flex-direction: column-reverse; .tabs-header { border-bottom: none; border-top: 1px solid #e0e0e0; .tab-item { border-bottom: none; border-top: 2px solid transparent; &.active { border-top-color: #333; } } } } &.tabs-left, &.tabs-right { .tabs-header { display: flex; flex-direction: column; border-right: 1px solid #e0e0e0; .tab-item { padding: 12px 24px; cursor: pointer; border-right: 2px solid transparent; transition: all 0.3s; user-select: none; &:hover { background: #f5f5f5; } &.active { border-right-color: #333; } } } } &.tabs-right { flex-direction: row-reverse; .tabs-header { border-right: none; border-left: 1px solid #e0e0e0; .tab-item { border-right: none; border-left: 2px solid transparent; &.active { border-left-color: #333; } } } } .tabs-content { flex: 1; overflow: auto; box-sizing: border-box; .tab-pane { width: 100%; height: 100%; padding: 20px; } } .placeholder-content { h2 { margin: 0 0 15px 0; font-size: 20px; color: #333; } p { margin: 0; color: #666; font-size: 14px; line-height: 1.6; } } } </style> `.trim(); return { component: component, fileName: 'DashboardTabs.vue' }; } }, // 双栏固定宽度布局 'generate-layout-fixed-sidebar': { name: 'generate-layout-fixed-sidebar', description: '生成固定侧边栏宽度的双栏布局', inputSchema: { type: 'object', properties: { sidebarWidth: { type: 'number', description: '侧边栏固定宽度(像素)' }, position: { type: 'string', description: '侧边栏位置: left 或 right' } } }, handler: async ({ sidebarWidth = 300, position = 'left' }) => { const component = ` <template> <div class="dashboard-layout-fixed-sidebar" :class="\`sidebar-\${position}\`"> <div class="sidebar" :style="{ width: sidebarWidth + 'px' }"> <slot name="sidebar"> <div class="placeholder-content"> <h2>侧边栏</h2> <p>固定宽度: {{ sidebarWidth }}px</p> <p>位置: {{ position === 'left' ? '左侧' : '右侧' }}</p> </div> </slot> </div> <div class="main"> <slot name="main"> <div class="placeholder-content"> <h2>主内容区域</h2> <p>自适应剩余宽度</p> </div> </slot> </div> </div> </template> <script> export default { name: 'DashboardFixedSidebar', props: { sidebarWidth: { type: Number, default: ${sidebarWidth} }, position: { type: String, default: '${position}', validator: (value) => ['left', 'right'].includes(value) } } } </script> <style scoped lang="scss"> .dashboard-layout-fixed-sidebar { display: flex; height: 100vh; width: 100vw; margin: 0; padding: 0; box-sizing: border-box; &.sidebar-right { flex-direction: row-reverse; } .sidebar { height: 100%; border: 1px solid #e0e0e0; overflow-y: auto; flex-shrink: 0; box-sizing: border-box; padding: 20px; } .main { flex: 1; height: 100%; border: 1px solid #e0e0e0; overflow: auto; box-sizing: border-box; padding: 20px; } .placeholder-content { h2 { margin: 0 0 15px 0; font-size: 18px; color: #333; } p { margin: 0 0 8px 0; color: #666; font-size: 14px; line-height: 1.6; } } } </style> `.trim(); return { component: component, fileName: 'DashboardFixedSidebar.vue' }; } } };