插件支持任意网页拖放安装

This commit is contained in:
icssoa 2025-04-17 16:41:49 +08:00
parent db9f96baad
commit 5d15ddefe5
10 changed files with 223 additions and 165 deletions

View File

@ -55,3 +55,4 @@ export function useCool() {
// 导出其他模块的功能
export * from './browser';
export * from './hmr';
export * from './mitt';

View File

@ -1,7 +1,7 @@
import Mitt, { type Emitter } from 'mitt';
import { hmr } from './hmr';
const mitt: Emitter<any> = hmr.getData('mitt', Mitt());
export const mitt: Emitter<any> = hmr.getData('mitt', Mitt());
// 返回 mitt 实例,用于在应用中进行事件的发布和订阅
export function useMitt() {

View File

@ -42,6 +42,7 @@ function toCode() {
animation-fill-mode: forwards;
-webkit-text-size-adjust: none;
font-size: 12px;
text-align: center;
}
.t2 {

View File

@ -1,3 +1,4 @@
import { usePlugin } from './hooks';
import { type ModuleConfig } from '/@/cool';
export default (): ModuleConfig => {
@ -20,6 +21,10 @@ export default (): ModuleConfig => {
},
component: () => import('./views/ai-code.vue')
}
]
],
onLoad() {
const { register } = usePlugin();
register();
}
};
};

View File

@ -1 +1,2 @@
export * from './menu';
export * from './plugin';

View File

@ -0,0 +1,118 @@
import { ElMessage, ElMessageBox } from 'element-plus';
import { mitt, router, service } from '/@/cool';
import { t } from '/@/plugins/i18n';
export const usePlugin = () => {
function getName(file: File) {
return file.name.replace('.cool', '');
}
// 注册
function register() {
document.body.addEventListener('dragover', e => {
e.preventDefault();
});
document.body.addEventListener('drop', e => {
e.preventDefault();
if (e.dataTransfer) {
const file = e.dataTransfer.files[0];
ElMessageBox.confirm(
t('检测到插件「{name}」,是否安装?', { name: getName(file) }),
t('提示'),
{
type: 'warning',
confirmButtonText: t('安装')
}
)
.then(() => {
install(file);
})
.catch(() => null);
}
});
}
// 安装
function install(file: File) {
const next = (force: boolean) => {
const data = new FormData();
data.append('files', file);
data.append('force', String(force));
service.plugin.info
.request({
url: '/install',
method: 'POST',
data,
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(res => {
if (!res) {
// 发送事件
mitt.emit('plugin.refresh');
// 标题
const title = t('插件「{name}」安装成功', { name: getName(file) });
// 是否插件页面
if (router.currentRoute.value.path == '/helper/plugins') {
ElMessage.success(title);
} else {
ElMessageBox.alert(title, t('提示'), {
type: 'success',
confirmButtonText: t('点击查看'),
showCancelButton: true
})
.then(() => {
router.push('/helper/plugins');
})
.catch(() => null);
}
return;
}
if (res.type == 0) {
ElMessageBox.confirm(res.message, t('提示'), {
type: 'error',
showConfirmButton: false
})
.then(() => {
next(true);
})
.catch(() => null);
}
if (res.type == 1 || res.type == 2) {
ElMessageBox.confirm(res.message, t('提示'), {
type: 'warning',
confirmButtonText: t('继续')
})
.then(() => {
next(true);
})
.catch(() => null);
}
if (res.type == 3) {
next(true);
}
})
.catch(err => {
ElMessage.error(err.message);
});
};
next(false);
}
return {
register,
install
};
};

View File

@ -1,64 +1,66 @@
{
"搜索插件名称": "Search Plugin Name",
"后端": "Backend",
"未知": "Unknown",
"暂无描述": "No description yet",
"示例": "Example",
"预览": "Preview",
"文档": "Documentation",
"图片预览": "Image Preview",
"说明文档": "Instruction Document",
"作者": "Author",
"更新时间": "Update Time",
"格式化": "Format",
"插入文件链接": "Insert File Link",
"已安装": "Installed",
"全部插件": "All Plugins",
"插件开发": "Plugin Development",
"插件安装成功": "Plugin Installed Successfully",
"提示": "Tip",
"继续": "Continue",
"确定要卸载插件【{name}】吗?": "Are you sure you want to uninstall the plugin [{name}]?",
"卸载成功": "Uninstall success",
"启用成功": "Enable success",
"禁用成功": "Disable success",
"参数格式错误": "Parameter format error",
"设置": "Settings",
"参数": "Parameter",
"修改成功": "Modify success",
"检测到插件,是否安装": "Plugin detected. Install?",
"安装": "Install",
"确定要退出编码吗?": "Are you sure you want to exit encoding?",
"创建目录:{name}": "Create directory: {name}",
"创建菜单:{name}": "Create menu: {name}",
"创建 Node 文件": "Create Node file",
"正在重启服务": "Restarting service",
"创建 Vue 文件": "Create Vue file",
"自动添加": "Auto add",
"权限名称": "Permission name",
"实体数据": "Entity data",
"自动添加权限": "Auto add permission",
"一键添加": "Add in one click",
"权限列表": "Permission List",
"请选择实体数据": "Please Select Entity Data",
"请填写权限名称": "Please Fill in Permission Name",
"请至少选择一个权限": "Please Select at Least One Permission",
"添加权限成功": "Permission Added Successfully",
"快速创建": "Quick Create",
"请选择数据结构": "Please Select Data Structure",
"数据结构": "Data Structure",
"上级节点": "Parent Node",
"请选择上级节点": "Please Select Parent Node",
"菜单名称": "Menu Name",
"请输入菜单名称": "Please Enter Menu Name",
"菜单路由": "Menu Route",
"请输入菜单路由,如:/test": "Please Enter Menu Route, e.g.: /test",
"必须以 / 开头": "Must Start with /",
"菜单排序": "Menu Sort",
"请填写菜单排序": "Please Fill in Menu Sort",
"菜单图标": "Menu Icon",
"请选择图标": "Please Select Icon",
"路由缓存": "Route Cache",
"开始创建": "Start Creation",
"极速编码": "Fast Encoding"
}
"请选择图标": "Please select an icon",
"路由缓存": "Routing cache",
"开始创建": "Start creating",
"AI极速编码": "AI Coding",
"搜索插件名称": "Search plugin name",
"后端": "Backend",
"未知": "Unknown",
"暂无描述": "No description available",
"示例": "Example",
"预览": "Preview",
"文档": "Documentation",
"图片预览": "Image preview",
"说明文档": "Instruction document",
"作者": "Author",
"更新时间": "Update time",
"格式化": "Formatting",
"插入文件链接": "Insert file link",
"已安装": "Installed",
"全部插件": "All plugins",
"插件开发": "Plugin development",
"确定要卸载插件【{name}】吗?": "Are you sure you want to uninstall the plugin [{name}]?",
"提示": "Prompt",
"卸载成功": "Uninstallation successful",
"启用成功": "Enablement successful",
"禁用成功": "Disable successfully",
"参数格式错误": "Parameter format error",
"设置": "Settings",
"参数": "Parameter",
"修改成功": "Modify successfully",
"确定要退出编码吗?": "Are you sure you want to exit the encoding?",
"创建目录:{name}": "Create directory: {name}",
"创建菜单:{name}": "Create menu: {name}",
"创建 Java 文件": "Create Java file",
"创建 Node 文件": "Create Node file",
"正在重启服务": "Restarting service",
"创建 Vue 文件": "Create Vue file",
"检测到插件「{name}」,是否安装?": "Plugin 「{name}」 detected. Do you want to install it?",
"安装": "Install",
"插件「{name}」安装成功": "Plugin 「{name}」 installed successfully",
"点击查看": "Click to view",
"继续": "Continue",
"自动添加": "Automatically add",
"权限名称": "Permission name",
"实体数据": "Entity data",
"自动添加权限": "Automatically add permissions",
"一键添加": "Add in one click",
"权限列表": "Permission list",
"请选择实体数据": "Please select entity data",
"请填写权限名称": "Please fill in the permission name",
"请至少选择一个权限": "Please select at least one permission",
"添加权限成功": "Successfully added permissions",
"快速创建": "Quick creation",
"请选择数据结构": "Please select the data structure",
"数据结构": "Data structure",
"上级节点": "Parent node",
"请选择上级节点": "Please select the parent node",
"菜单名称": "Menu name",
"请输入菜单名称": "Please enter the menu name",
"菜单路由": "Menu route",
"请输入菜单路由,如:/test": "Please enter the menu route, e.g.: /test",
"必须以 / 开头": "Must start with /",
"菜单排序": "Menu sorting",
"请填写菜单排序": "Please fill in the menu sorting",
"菜单图标": "Menu icon"
}

View File

@ -15,10 +15,8 @@
"已安装": "已安装",
"全部插件": "全部插件",
"插件开发": "插件开发",
"插件安装成功": "插件安装成功",
"提示": "提示",
"继续": "继续",
"确定要卸载插件【{name}】吗?": "确定要卸载插件【{name}】吗?",
"提示": "提示",
"卸载成功": "卸载成功",
"启用成功": "启用成功",
"禁用成功": "禁用成功",
@ -26,14 +24,18 @@
"设置": "设置",
"参数": "参数",
"修改成功": "修改成功",
"检测到插件,是否安装": "检测到插件,是否安装",
"安装": "安装",
"确定要退出编码吗?": "确定要退出编码吗?",
"创建目录:{name}": "创建目录:{name}",
"创建菜单:{name}": "创建菜单:{name}",
"创建 Java 文件": "创建 Java 文件",
"创建 Node 文件": "创建 Node 文件",
"正在重启服务": "正在重启服务",
"创建 Vue 文件": "创建 Vue 文件",
"检测到插件「{name}」,是否安装?": "检测到插件「{name}」,是否安装?",
"安装": "安装",
"插件「{name}」安装成功": "插件「{name}」安装成功",
"点击查看": "点击查看",
"继续": "继续",
"自动添加": "自动添加",
"权限名称": "权限名称",
"实体数据": "实体数据",
@ -60,5 +62,5 @@
"请选择图标": "请选择图标",
"路由缓存": "路由缓存",
"开始创建": "开始创建",
"极速编码": "极速编码"
"AI极速编码": "AI极速编码"
}

View File

@ -1,6 +1,8 @@
{
"请选择图标": "請選擇圖標",
"路由缓存": "路由緩存",
"开始创建": "開始創建",
"极速编码": "極速編碼",
"AI极速编码": "AI極速編碼",
"搜索插件名称": "搜尋插件名稱",
"后端": "後端",
"未知": "未知",
@ -17,10 +19,8 @@
"已安装": "已安裝",
"全部插件": "全部插件",
"插件开发": "插件開發",
"插件安装成功": "插件安裝成功",
"提示": "提示",
"继续": "繼續",
"确定要卸载插件【{name}】吗?": "確定要卸載插件【{name}】嗎?",
"提示": "提示",
"卸载成功": "卸載成功",
"启用成功": "啟用成功",
"禁用成功": "禁用成功",
@ -28,14 +28,18 @@
"设置": "設置",
"参数": "參數",
"修改成功": "修改成功",
"检测到插件,是否安装": "檢測到插件,是否安裝",
"安装": "安裝",
"确定要退出编码吗?": "確定要退出編碼嗎?",
"创建目录:{name}": "創建目錄:{name}",
"创建菜单:{name}": "創建菜單:{name}",
"创建 Java 文件": "創建 Java 文件",
"创建 Node 文件": "創建 Node 文件",
"正在重启服务": "正在重啟服務",
"创建 Vue 文件": "創建 Vue 文件",
"检测到插件「{name}」,是否安装?": "檢測到插件「{name}」,是否安裝?",
"安装": "安裝",
"插件「{name}」安装成功": "插件「{name}」安裝成功",
"点击查看": "點擊查看",
"继续": "繼續",
"自动添加": "自動添加",
"权限名称": "權限名稱",
"实体数据": "實體數據",
@ -58,7 +62,5 @@
"必须以 / 开头": "必須以 / 開頭",
"菜单排序": "菜單排序",
"请填写菜单排序": "請填寫菜單排序",
"菜单图标": "菜單圖標",
"请选择图标": "請選擇圖標",
"路由缓存": "路由緩存"
"菜单图标": "菜單圖標"
}

View File

@ -27,8 +27,6 @@
:class="{
'is-installed': isInstalled
}"
@dragover="onDragover"
@drop="onDrop"
@mousemove="onMousemove"
>
<el-row :gutter="10">
@ -279,6 +277,7 @@ import {
import { ElMessage, ElMessageBox } from 'element-plus';
import { marked } from 'marked';
import { useI18n } from 'vue-i18n';
import { usePlugin } from '../hooks';
interface Plugin {
name?: string;
@ -297,9 +296,10 @@ interface Plugin {
[key: string]: any;
}
const { router, service, refs, setRefs } = useCool();
const { router, service, refs, setRefs, mitt } = useCool();
const helper = module.get('helper');
const { t } = useI18n();
const { install } = usePlugin();
//
const tab = reactive({
@ -354,66 +354,15 @@ const plugin = reactive({
//
init() {
plugin.refresh();
},
//
install(file: File) {
const next = (force: boolean) => {
const data = new FormData();
//
mitt.off('plugin.refresh');
data.append('files', file);
data.append('force', String(force));
service.plugin.info
.request({
url: '/install',
method: 'POST',
data,
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(res => {
if (!res) {
ElMessage.success(t('插件安装成功'));
tab.change('installed');
plugin.refresh();
return;
}
if (res.type == 0) {
ElMessageBox.confirm(res.message, t('提示'), {
type: 'error',
showConfirmButton: false
})
.then(() => {
next(true);
})
.catch(() => null);
}
if (res.type == 1 || res.type == 2) {
ElMessageBox.confirm(res.message, t('提示'), {
type: 'warning',
confirmButtonText: t('继续')
})
.then(() => {
next(true);
})
.catch(() => null);
}
if (res.type == 3) {
next(true);
}
})
.catch(err => {
ElMessage.error(err.message);
});
};
next(false);
//
mitt.on('plugin.refresh', () => {
tab.change('installed');
plugin.refresh();
});
},
//
@ -786,33 +735,10 @@ const pic = reactive({
//
function onBeforeUpload(file: File) {
plugin.install(file);
install(file);
return false;
}
//
function onDragover(e: DragEvent) {
e.preventDefault();
}
//
function onDrop(e: DragEvent) {
e.preventDefault();
if (e.dataTransfer) {
const file = e.dataTransfer.files[0];
ElMessageBox.confirm(t('检测到插件,是否安装'), t('提示'), {
type: 'warning',
confirmButtonText: t('安装')
})
.then(() => {
plugin.install(file);
})
.catch(() => null);
}
}
//
function onMousemove(e: MouseEvent) {
if (!isInstalled.value) {