feat: module new feat
This commit is contained in:
parent
aad083bda7
commit
967818c6d0
@ -3,9 +3,13 @@
|
||||
namespace Modules\Develop\Http\Controllers;
|
||||
|
||||
use Catch\Base\CatchController;
|
||||
use Catch\CatchAdmin;
|
||||
use Catch\Support\Module\ModuleRepository;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Modules\Develop\Support\Generate\Module;
|
||||
use Modules\Develop\Support\ModuleInstall;
|
||||
|
||||
class ModuleController extends CatchController
|
||||
{
|
||||
@ -88,4 +92,36 @@ class ModuleController extends CatchController
|
||||
{
|
||||
return $this->repository->delete($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* install
|
||||
*
|
||||
* @param Request $request
|
||||
* @return true
|
||||
*/
|
||||
public function install(Request $request)
|
||||
{
|
||||
$moduleInstall = new ModuleInstall($request->get('type'));
|
||||
|
||||
$moduleInstall->install($request->all());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* upload
|
||||
*
|
||||
* @param Request $request
|
||||
* @return string
|
||||
*/
|
||||
public function upload(Request $request)
|
||||
{ $file = $request->file('file');
|
||||
|
||||
Storage::build([
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app')
|
||||
])->put($file->getClientOriginalName(), $file->getContent());
|
||||
|
||||
return storage_path('app') . DIRECTORY_SEPARATOR . $file->getClientOriginalName();
|
||||
}
|
||||
}
|
||||
|
67
modules/Develop/Support/ModuleInstall.php
Normal file
67
modules/Develop/Support/ModuleInstall.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
namespace Modules\Develop\Support;
|
||||
|
||||
|
||||
use Catch\CatchAdmin;
|
||||
use Catch\Exceptions\FailedException;
|
||||
use Catch\Facade\Zipper;
|
||||
|
||||
/**
|
||||
* module install
|
||||
*/
|
||||
class ModuleInstall
|
||||
{
|
||||
const NORMAL_INSTALL = 1;
|
||||
const ZIP_INSTALL = 2;
|
||||
|
||||
public function __construct(protected readonly int|string $type){}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function install(array $params): void
|
||||
{
|
||||
try {
|
||||
if ($this->type === self::NORMAL_INSTALL) {
|
||||
$this->installWithTitle($params['title']);
|
||||
}
|
||||
|
||||
if ($this->type == self::ZIP_INSTALL) {
|
||||
$this->installWithZip($params['title'], $params['file']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if ($this->type == self::ZIP_INSTALL) {
|
||||
CatchAdmin::deleteModulePath($params['title']);
|
||||
}
|
||||
|
||||
throw new FailedException('安装失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $title
|
||||
*/
|
||||
protected function installWithTitle(string $title): void
|
||||
{
|
||||
$installer = CatchAdmin::getModuleInstaller($title);
|
||||
|
||||
$installer->install();
|
||||
}
|
||||
|
||||
/**
|
||||
* get
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $zip
|
||||
*/
|
||||
protected function installWithZip(string $title, string $zip): void
|
||||
{
|
||||
$zipRepository = Zipper::make($zip)->getRepository();
|
||||
|
||||
$zipRepository->getArchive()->extractTo(CatchAdmin::getModulePath($title));
|
||||
|
||||
$this->installWithTitle($title);
|
||||
}
|
||||
}
|
@ -6,6 +6,8 @@ use Modules\Develop\Http\Controllers\GenerateController;
|
||||
use Modules\Develop\Http\Controllers\SchemaController;
|
||||
|
||||
Route::apiResource('module', ModuleController::class);
|
||||
Route::post('module/install', [ModuleController::class, 'install']);
|
||||
Route::post('module/upload', [ModuleController::class, 'upload']);
|
||||
|
||||
Route::put('module/enable/{name}', [ModuleController::class, 'enable']);
|
||||
|
||||
|
@ -8,7 +8,12 @@
|
||||
</template>
|
||||
</Search>
|
||||
<div class="pl-2 pr-2 bg-white dark:bg-regal-dark rounded-lg mt-4 pb-6">
|
||||
<Operate :show="open" />
|
||||
<Operate :show="open">
|
||||
<template v-slot:operate>
|
||||
<!-- header 插槽的内容放这里 -->
|
||||
<el-button type="success" class="float-right" @click="installVisible = true"><Icon name="cog-6-tooth" class="mr-1 w-4" /> 安装</el-button>
|
||||
</template>
|
||||
</Operate>
|
||||
<el-table :data="tableData" class="mt-3" v-loading="loading">
|
||||
<el-table-column prop="title" label="模块名称" width="180" />
|
||||
<el-table-column prop="path" label="模块目录" width="180" />
|
||||
@ -33,12 +38,18 @@
|
||||
<Dialog v-model="visible" :title="title" destroy-on-close>
|
||||
<Create @close="close(reset)" :primary="id" :api="api" />
|
||||
</Dialog>
|
||||
|
||||
<!-- 安装 -->
|
||||
<Dialog v-model="installVisible" title="安装模块" destroy-on-close>
|
||||
<Install />
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import Create from './create.vue'
|
||||
import Install from './install.vue'
|
||||
import { useGetList } from '/admin/composables/curd/useGetList'
|
||||
import { useDestroy } from '/admin/composables/curd/useDestroy'
|
||||
import { useOpen } from '/admin/composables/curd/useOpen'
|
||||
@ -50,6 +61,7 @@ const { destroy, deleted } = useDestroy('确认删除吗? ⚠️将会删除模
|
||||
const { open, close, title, visible, id } = useOpen()
|
||||
|
||||
const tableData = computed(() => data.value?.data)
|
||||
const installVisible = ref<boolean>(false)
|
||||
|
||||
onMounted(() => {
|
||||
search()
|
||||
|
66
modules/Develop/views/module/install.vue
Normal file
66
modules/Develop/views/module/install.vue
Normal file
@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<el-form :model="formData" label-width="120px" ref="form" v-loading="loading" class="pr-4">
|
||||
<el-form-item label="安装方式" prop="type">
|
||||
<el-radio-group v-model="formData.type">
|
||||
<el-radio-button
|
||||
v-for="item in [
|
||||
{ label: '普通安装', value: 1 },
|
||||
{ label: 'ZIP 安装', value: 2 },
|
||||
]"
|
||||
:key="item.value"
|
||||
:label="item.value"
|
||||
name="type"
|
||||
>{{ item.label }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="模块名称"
|
||||
prop="title"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '模块名称必须填写',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<el-input v-model="formData.title" />
|
||||
</el-form-item>
|
||||
<el-form-item label="上传 ZIP" prop="file" v-if="formData.type === 2">
|
||||
<Upload action="module/upload" :limit="1" accept=".zip" :on-success="moduleUpload">
|
||||
<template #trigger>
|
||||
<el-button type="primary">选择模块文件</el-button>
|
||||
</template>
|
||||
</Upload>
|
||||
</el-form-item>
|
||||
|
||||
<div class="flex justify-end">
|
||||
<el-button type="primary" @click="submitForm(form)">安装</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useCreate } from '/admin/composables/curd/useCreate'
|
||||
|
||||
import { onMounted } from 'vue'
|
||||
import { Code } from '/admin/enum/app'
|
||||
import Message from '/admin/support/message'
|
||||
|
||||
const { formData, form, loading, submitForm, close } = useCreate('module/install')
|
||||
formData.value.type = 1
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
onMounted(() => {
|
||||
close(() => emit('close'))
|
||||
})
|
||||
|
||||
const moduleUpload = (response, uploadFile) => {
|
||||
if (response.code === Code.SUCCESS) {
|
||||
formData.value.file = response.data
|
||||
} else {
|
||||
Message.error(response.message)
|
||||
}
|
||||
}
|
||||
</script>
|
@ -18,18 +18,6 @@ class Installer extends ModuleInstaller
|
||||
];
|
||||
}
|
||||
|
||||
protected function migration(): string
|
||||
{
|
||||
// TODO: Implement migration() method.
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function seeder(): string
|
||||
{
|
||||
// TODO: Implement seeder() method.
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function requirePackages(): void
|
||||
{
|
||||
// TODO: Implement requirePackages() method.
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="flex flex-col sm:flex-row dark:bg-regal-dark w-full">
|
||||
<el-card shadow="never" class="w-full sm:w-[35rem]">
|
||||
<el-card shadow="never" class="w-full sm:w-[35rem] h-[32rem]">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>个人资料</span>
|
||||
@ -13,7 +13,7 @@
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-tabs v-model="activeName" class="pl-3 pr-3 bg-white dark:bg-regal-dark mt-2 sm:mt-0 w-full ml-2">
|
||||
<el-tabs v-model="activeName" class="pl-3 pr-3 bg-white dark:bg-regal-dark mt-2 sm:mt-0 w-full ml-0 sm:ml-2">
|
||||
<el-tab-pane label="登录日志" name="login_log">
|
||||
<LoginLog />
|
||||
</el-tab-pane>
|
||||
|
33
resources/admin/components/admin/upload/index.vue
Normal file
33
resources/admin/components/admin/upload/index.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<el-upload ref="upload" :action="actionApi" :auto-upload="auto" :headers="{ authorization: token }" v-bind="$attrs">
|
||||
<template v-for="(index, name) in $slots" v-slot:[name]>
|
||||
<slot :name="name"></slot>
|
||||
</template>
|
||||
</el-upload>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { env } from '/admin/support/helper'
|
||||
import { getAuthToken } from '/admin/support/helper'
|
||||
|
||||
const props = defineProps({
|
||||
action: {
|
||||
type: String,
|
||||
default: 'upload',
|
||||
},
|
||||
auto: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
})
|
||||
|
||||
const baseURL = env('VITE_BASE_URL')
|
||||
|
||||
const actionApi = ref<string>('')
|
||||
|
||||
actionApi.value = baseURL + props.action
|
||||
|
||||
const token = ref<string>()
|
||||
token.value = 'Bearer ' + getAuthToken()
|
||||
</script>
|
Loading…
x
Reference in New Issue
Block a user