vue-layout-mcp
Version:
A MCP server for generating Vue dashboard layout components
1,244 lines (1,135 loc) • 25.1 kB
JavaScript
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) ;
}
@media (max-width: 768px) {
grid-template-columns: repeat(2, 1fr) ;
}
@media (max-width: 480px) {
grid-template-columns: 1fr ;
}
}
</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'
};
}
}
};