feat: module new feat
This commit is contained in:
parent
aad083bda7
commit
967818c6d0
@ -3,9 +3,13 @@
|
|||||||
namespace Modules\Develop\Http\Controllers;
|
namespace Modules\Develop\Http\Controllers;
|
||||||
|
|
||||||
use Catch\Base\CatchController;
|
use Catch\Base\CatchController;
|
||||||
|
use Catch\CatchAdmin;
|
||||||
use Catch\Support\Module\ModuleRepository;
|
use Catch\Support\Module\ModuleRepository;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Modules\Develop\Support\Generate\Module;
|
||||||
|
use Modules\Develop\Support\ModuleInstall;
|
||||||
|
|
||||||
class ModuleController extends CatchController
|
class ModuleController extends CatchController
|
||||||
{
|
{
|
||||||
@ -88,4 +92,36 @@ class ModuleController extends CatchController
|
|||||||
{
|
{
|
||||||
return $this->repository->delete($name);
|
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;
|
use Modules\Develop\Http\Controllers\SchemaController;
|
||||||
|
|
||||||
Route::apiResource('module', ModuleController::class);
|
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']);
|
Route::put('module/enable/{name}', [ModuleController::class, 'enable']);
|
||||||
|
|
||||||
|
@ -8,7 +8,12 @@
|
|||||||
</template>
|
</template>
|
||||||
</Search>
|
</Search>
|
||||||
<div class="pl-2 pr-2 bg-white dark:bg-regal-dark rounded-lg mt-4 pb-6">
|
<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 :data="tableData" class="mt-3" v-loading="loading">
|
||||||
<el-table-column prop="title" label="模块名称" width="180" />
|
<el-table-column prop="title" label="模块名称" width="180" />
|
||||||
<el-table-column prop="path" label="模块目录" width="180" />
|
<el-table-column prop="path" label="模块目录" width="180" />
|
||||||
@ -33,12 +38,18 @@
|
|||||||
<Dialog v-model="visible" :title="title" destroy-on-close>
|
<Dialog v-model="visible" :title="title" destroy-on-close>
|
||||||
<Create @close="close(reset)" :primary="id" :api="api" />
|
<Create @close="close(reset)" :primary="id" :api="api" />
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
<!-- 安装 -->
|
||||||
|
<Dialog v-model="installVisible" title="安装模块" destroy-on-close>
|
||||||
|
<Install />
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import Create from './create.vue'
|
import Create from './create.vue'
|
||||||
|
import Install from './install.vue'
|
||||||
import { useGetList } from '/admin/composables/curd/useGetList'
|
import { useGetList } from '/admin/composables/curd/useGetList'
|
||||||
import { useDestroy } from '/admin/composables/curd/useDestroy'
|
import { useDestroy } from '/admin/composables/curd/useDestroy'
|
||||||
import { useOpen } from '/admin/composables/curd/useOpen'
|
import { useOpen } from '/admin/composables/curd/useOpen'
|
||||||
@ -50,6 +61,7 @@ const { destroy, deleted } = useDestroy('确认删除吗? ⚠️将会删除模
|
|||||||
const { open, close, title, visible, id } = useOpen()
|
const { open, close, title, visible, id } = useOpen()
|
||||||
|
|
||||||
const tableData = computed(() => data.value?.data)
|
const tableData = computed(() => data.value?.data)
|
||||||
|
const installVisible = ref<boolean>(false)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
search()
|
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
|
protected function requirePackages(): void
|
||||||
{
|
{
|
||||||
// TODO: Implement requirePackages() method.
|
// TODO: Implement requirePackages() method.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col sm:flex-row dark:bg-regal-dark w-full">
|
<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>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span>个人资料</span>
|
<span>个人资料</span>
|
||||||
@ -13,7 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-card>
|
</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">
|
<el-tab-pane label="登录日志" name="login_log">
|
||||||
<LoginLog />
|
<LoginLog />
|
||||||
</el-tab-pane>
|
</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