mirror of
https://github.com/insistence/RuoYi-Vue3-FastAPI.git
synced 2026-09-21 12:52:27 +00:00
* feat: 初始化插件系统 * refactor: 收口插件系统运行时重构 * perf: 优化插件系统类型提示 * fix&perf: 修复和优化插件系统 * fix: 修复gitignore规则误忽略插件文件的问题 * fix: 修复运行时插件根路径算错的问题 * fix: 加强插件发现和路由注册的防护措施 * revert: 回滚定时任务白名单 * fix: 移除未使用的应用路由注册探测 * revert: 恢复部分代码 * perf: 优化插件系统 * docs: 新增插件开发文档 * perf: 优化插件管理模块 * perf: 提升插件系统核心能力 * refactor: 重构生命周期 step runner * fix: 修复lint错误 * test: 清理测试用例 * test: 调整测试目录名称 * fix: 修复前后端目录硬编码的问题 * fix: 修复插件系统安全性缺口 * refactor: 重新设计插件生命周期 Migration 事务与回滚 * perf: 优化插件系统边界问题 * refactor: 重构当前插件系统的依赖体系设计 * perf: 优化代码 * perf: 优化代码 * fix: 修复代码合并问题 * fix: 修复bug * perf: 优化代码 * perf&fix: 优化代码和修复bug * docs: 优化文档格式 * feat: 适配Vue2版本 * docs: 更新README文档 * fix: 修复ruff lint错误 * chore: 更新后端依赖文件
236 lines
7.2 KiB
Python
236 lines
7.2 KiB
Python
from .naming import PluginScaffoldNaming
|
|
|
|
|
|
class PluginVue2FrontendScaffoldTemplateBuilder:
|
|
"""
|
|
Vue 2 前端插件模板内容构建器。
|
|
"""
|
|
|
|
@staticmethod
|
|
def build_view(plugin_id: str) -> str:
|
|
"""
|
|
构建 Vue 2 前端视图模板内容。
|
|
|
|
:param plugin_id: 插件ID
|
|
:return: 前端视图模板内容
|
|
"""
|
|
return f"""<template>
|
|
<div class=\"app-container\">
|
|
<el-card shadow=\"never\">
|
|
<div slot=\"header\">{plugin_id}</div>
|
|
<div>{plugin_id} plugin</div>
|
|
</el-card>
|
|
</div>
|
|
</template>
|
|
"""
|
|
|
|
@staticmethod
|
|
def build_crud_view(plugin_id: str) -> str:
|
|
"""
|
|
构建 Vue 2 前端 CRUD 视图模板内容。
|
|
|
|
:param plugin_id: 插件ID
|
|
:return: 前端 CRUD 视图模板内容
|
|
"""
|
|
class_name = PluginScaffoldNaming.to_class_name(plugin_id)
|
|
return f"""<template>
|
|
<div class=\"app-container\">
|
|
<el-form ref=\"queryForm\" :model=\"queryParams\" size=\"small\" :inline=\"true\">
|
|
<el-form-item label=\"名称\">
|
|
<el-input v-model=\"queryParams.keyword\" placeholder=\"请输入名称\" clearable />
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type=\"primary\" icon=\"el-icon-search\" size=\"mini\" @click=\"getList\">搜索</el-button>
|
|
<el-button icon=\"el-icon-refresh\" size=\"mini\" @click=\"resetQuery\">重置</el-button>
|
|
<el-button type=\"primary\" plain icon=\"el-icon-plus\" size=\"mini\" @click=\"handleAdd\">新增</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
|
|
<el-table v-loading=\"loading\" :data=\"itemList\" border>
|
|
<el-table-column label=\"ID\" prop=\"itemId\" width=\"90\" align=\"center\" />
|
|
<el-table-column label=\"名称\" prop=\"itemName\" min-width=\"180\" />
|
|
<el-table-column label=\"状态\" prop=\"status\" width=\"90\" align=\"center\">
|
|
<template slot-scope=\"scope\">
|
|
<el-tag :type=\"scope.row.status === '0' ? 'success' : 'info'\">{{{{ scope.row.status === '0' ? '正常' : '停用' }}}}</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label=\"备注\" prop=\"remark\" min-width=\"220\" />
|
|
<el-table-column label=\"操作\" width=\"150\" align=\"center\">
|
|
<template slot-scope=\"scope\">
|
|
<el-button type=\"text\" icon=\"el-icon-edit\" size=\"mini\" @click=\"handleUpdate(scope.row)\">修改</el-button>
|
|
<el-button type=\"text\" icon=\"el-icon-delete\" size=\"mini\" @click=\"handleDelete(scope.row)\">删除</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<el-dialog :title=\"dialogTitle\" :visible.sync=\"open\" width=\"520px\" append-to-body>
|
|
<el-form ref=\"itemForm\" :model=\"form\" :rules=\"rules\" label-width=\"90px\">
|
|
<el-form-item label=\"名称\" prop=\"itemName\">
|
|
<el-input v-model=\"form.itemName\" placeholder=\"请输入名称\" />
|
|
</el-form-item>
|
|
<el-form-item label=\"状态\" prop=\"status\">
|
|
<el-radio-group v-model=\"form.status\">
|
|
<el-radio label=\"0\">正常</el-radio>
|
|
<el-radio label=\"1\">停用</el-radio>
|
|
</el-radio-group>
|
|
</el-form-item>
|
|
<el-form-item label=\"备注\" prop=\"remark\">
|
|
<el-input v-model=\"form.remark\" type=\"textarea\" :rows=\"3\" />
|
|
</el-form-item>
|
|
</el-form>
|
|
<div slot=\"footer\" class=\"dialog-footer\">
|
|
<el-button type=\"primary\" @click=\"submitForm\">确 定</el-button>
|
|
<el-button @click=\"open = false\">取 消</el-button>
|
|
</div>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import {{
|
|
add{class_name}Item,
|
|
del{class_name}Item,
|
|
list{class_name}Items,
|
|
update{class_name}Item
|
|
}} from '../api/{plugin_id}'
|
|
|
|
export default {{
|
|
name: '{class_name}Plugin',
|
|
data() {{
|
|
return {{
|
|
loading: false,
|
|
open: false,
|
|
dialogTitle: '',
|
|
itemList: [],
|
|
queryParams: {{
|
|
keyword: ''
|
|
}},
|
|
form: {{
|
|
itemId: undefined,
|
|
itemName: '',
|
|
status: '0',
|
|
remark: ''
|
|
}},
|
|
rules: {{
|
|
itemName: [{{ required: true, message: '名称不能为空', trigger: 'blur' }}]
|
|
}}
|
|
}}
|
|
}},
|
|
created() {{
|
|
this.getList()
|
|
}},
|
|
methods: {{
|
|
resetFormData() {{
|
|
this.form = {{
|
|
itemId: undefined,
|
|
itemName: '',
|
|
status: '0',
|
|
remark: ''
|
|
}}
|
|
}},
|
|
getList() {{
|
|
this.loading = true
|
|
list{class_name}Items(this.queryParams).then(response => {{
|
|
const data = response.data || response
|
|
this.itemList = data.rows || []
|
|
}}).finally(() => {{
|
|
this.loading = false
|
|
}})
|
|
}},
|
|
resetQuery() {{
|
|
this.queryParams.keyword = ''
|
|
this.getList()
|
|
}},
|
|
handleAdd() {{
|
|
this.resetFormData()
|
|
this.dialogTitle = '新增{plugin_id}'
|
|
this.open = true
|
|
}},
|
|
handleUpdate(row) {{
|
|
this.resetFormData()
|
|
this.form = {{
|
|
itemId: row.itemId,
|
|
itemName: row.itemName,
|
|
status: row.status,
|
|
remark: row.remark
|
|
}}
|
|
this.dialogTitle = '修改{plugin_id}'
|
|
this.open = true
|
|
}},
|
|
submitForm() {{
|
|
this.$refs.itemForm.validate(valid => {{
|
|
if (!valid) {{
|
|
return
|
|
}}
|
|
const request = this.form.itemId
|
|
? update{class_name}Item(this.form.itemId, this.form)
|
|
: add{class_name}Item(this.form)
|
|
request.then(() => {{
|
|
this.$modal.msgSuccess('保存成功')
|
|
this.open = false
|
|
this.getList()
|
|
}})
|
|
}})
|
|
}},
|
|
handleDelete(row) {{
|
|
this.$modal.confirm('确认删除数据\"' + row.itemName + '\"吗?').then(function () {{
|
|
return del{class_name}Item(row.itemId)
|
|
}}).then(() => {{
|
|
this.$modal.msgSuccess('删除成功')
|
|
this.getList()
|
|
}})
|
|
}}
|
|
}}
|
|
}}
|
|
</script>
|
|
"""
|
|
|
|
@staticmethod
|
|
def build_readme(plugin_id: str) -> str:
|
|
"""
|
|
构建 Vue 2 前端 README 内容。
|
|
|
|
:param plugin_id: 插件ID
|
|
:return: 前端 README 内容
|
|
"""
|
|
return f"""# {plugin_id} frontend plugin
|
|
|
|
Vue 2 frontend plugin scaffold generated by `ruoyi plugin create`.
|
|
|
|
## Structure
|
|
|
|
- `api/`: request wrappers used by plugin pages.
|
|
- `views/`: Vue pages loaded by backend menu component paths.
|
|
- `../../tests/plugins/{plugin_id}/`: frontend node tests for plugin view resolving.
|
|
|
|
## Route Component
|
|
|
|
The backend menu component `plugin/{plugin_id}/index` maps to:
|
|
|
|
```text
|
|
plugins/{plugin_id}/views/index.vue
|
|
```
|
|
"""
|
|
|
|
@staticmethod
|
|
def build_test(plugin_id: str) -> str:
|
|
"""
|
|
构建 Vue 2 前端插件 node 测试样例。
|
|
|
|
:param plugin_id: 插件ID
|
|
:return: 前端插件测试样例内容
|
|
"""
|
|
return f"""const assert = require('assert').strict
|
|
const {{ existsSync }} = require('fs')
|
|
const {{ resolve }} = require('path')
|
|
const {{ resolvePluginViewPath }} = require('../../../src/utils/pluginViewResolver')
|
|
|
|
const frontendRoot = resolve(__dirname, '../../..')
|
|
const viewPath = resolve(frontendRoot, 'plugins', '{plugin_id}', 'views', 'index.vue')
|
|
|
|
assert.equal(resolvePluginViewPath('plugin/{plugin_id}/index'), './{plugin_id}/views/index.vue')
|
|
assert.equal(existsSync(viewPath), true)
|
|
|
|
console.log('{plugin_id} plugin frontend tests passed')
|
|
"""
|