feat: module new feat

This commit is contained in:
JaguarJack 2023-01-09 18:19:20 +08:00
parent aad083bda7
commit 967818c6d0
9 changed files with 220 additions and 16 deletions

View File

@ -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();
}
}

View 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);
}
}

View File

@ -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']);

View File

@ -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()

View 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>

View File

@ -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.

View File

@ -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>

View 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>