Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c48607c123 | ||
![]() |
035ba22f52 | ||
![]() |
403501b214 | ||
![]() |
c56a01df56 | ||
![]() |
ac5e0957b9 | ||
![]() |
948082f4ce | ||
![]() |
35622b164c | ||
![]() |
cbb3c156a6 | ||
![]() |
2d61786ec6 | ||
![]() |
95fae0dc28 | ||
![]() |
f91e434a83 | ||
![]() |
3f4bbb70d3 | ||
![]() |
3fc1e07de9 | ||
![]() |
23348b3a6b |
@@ -40,6 +40,11 @@
|
||||
- 账户: `catch@admin.com`
|
||||
- 密码: `catchadmin`
|
||||
|
||||
## 视频教程(😂记得一键三连哦)
|
||||
- [catchadmin 安装](https://www.bilibili.com/video/BV1eY411v71J/)
|
||||
- [catchadmin 开发之模块创建](https://www.bilibili.com/video/BV1jP41127aW/)
|
||||
- [catchadmin 之快速开发](https://www.bilibili.com/video/BV1Qh4y1J7eB/)
|
||||
|
||||
## 赞助
|
||||
如果项目对你有帮助,或者在工作上帮你节省了开发时间。在力所能及的情况下,可以支持下`Catchadmin`项目, 非常感谢🙏
|
||||
|
||||
|
@@ -18,7 +18,7 @@
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"laravel/framework": "^10.0",
|
||||
"laravel/tinker": "^2.8",
|
||||
"catchadmin/core": "^0.1.10"
|
||||
"catchadmin/core": "^0.1.12"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
|
@@ -4,6 +4,7 @@ namespace Modules\Common\Support\Upload\Uses;
|
||||
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class LocalUpload extends Upload
|
||||
{
|
||||
@@ -25,7 +26,9 @@ class LocalUpload extends Upload
|
||||
*/
|
||||
protected function addUrl($path): mixed
|
||||
{
|
||||
$path['path'] = config('app.url') . '/'. $path['path'];
|
||||
$path['path'] = config('app.url') . '/'.
|
||||
|
||||
Str::of($path['path'])->replace('\\', '/')->toString();
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
@@ -13,7 +13,6 @@ class {controller} extends Controller
|
||||
){}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function index(): mixed
|
||||
|
@@ -6,6 +6,7 @@ namespace Modules\Permissions\Http\Controllers;
|
||||
|
||||
use Catch\Base\CatchController as Controller;
|
||||
use Catch\Exceptions\FailedException;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Permissions\Enums\DataRange;
|
||||
use Modules\Permissions\Models\Roles;
|
||||
use Modules\Permissions\Http\Requests\RoleRequest;
|
||||
@@ -39,13 +40,15 @@ class RolesController extends Controller
|
||||
*/
|
||||
public function store(RoleRequest $request)
|
||||
{
|
||||
$dataRange = $request->get('data_range');
|
||||
$data = $request->all();
|
||||
|
||||
if ($dataRange && ! DataRange::Personal_Choose->assert($request->get('data_range'))) {
|
||||
$request['departments'] = [];
|
||||
if ($request->get('data_range') && ! DataRange::Personal_Choose->assert($data['data_range'])) {
|
||||
$data['departments'] = [];
|
||||
} else {
|
||||
$data['data_range'] = 0;
|
||||
}
|
||||
|
||||
return $this->model->storeBy($request->all());
|
||||
return $this->model->storeBy($data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,11 +56,15 @@ class RolesController extends Controller
|
||||
* @param $id
|
||||
* @return \Illuminate\Database\Eloquent\Model|null
|
||||
*/
|
||||
public function show($id)
|
||||
public function show($id, Request $request)
|
||||
{
|
||||
$role = $this->model->firstBy($id);
|
||||
|
||||
if ($request->has('from') && $request->get('from') == 'parent_role') {
|
||||
$role->setAttribute('permissions', $role->permissions()->get()->toTree());
|
||||
} else {
|
||||
$role->setAttribute('permissions', $role->permissions()->get()->pluck('id'));
|
||||
}
|
||||
|
||||
$role->setAttribute('departments', $role->departments()->pluck('id'));
|
||||
|
||||
@@ -76,6 +83,8 @@ class RolesController extends Controller
|
||||
|
||||
if ($request->get('data_range') && ! DataRange::Personal_Choose->assert($data['data_range'])) {
|
||||
$data['departments'] = [];
|
||||
} else {
|
||||
$data['data_range'] = 0;
|
||||
}
|
||||
|
||||
return $this->model->updateBy($id, $data);
|
||||
|
@@ -10,6 +10,7 @@ use Catch\Enums\Status;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Modules\Permissions\Enums\MenuStatus;
|
||||
use Modules\Permissions\Enums\MenuType;
|
||||
|
||||
@@ -199,6 +200,7 @@ class Permissions extends Model
|
||||
$data['route'] = '/'.trim($data['route'], '/');
|
||||
}
|
||||
|
||||
$data['component'] = Str::of($data['component'])->replace('\\', '/')->toString();
|
||||
return parent::storeBy($data);
|
||||
});
|
||||
}
|
||||
@@ -244,6 +246,7 @@ class Permissions extends Model
|
||||
$data['permission_mark'] = $parentMenu->permission_mark.'@'.$data['permission_mark'];
|
||||
}
|
||||
|
||||
$data['component'] = Str::of($data['component'])->replace('\\', '/')->toString();
|
||||
return parent::updateBy($id, $data);
|
||||
}
|
||||
}
|
||||
|
@@ -42,7 +42,14 @@
|
||||
<Select v-model="formData.permission_mark" allow-create placeholder="请选择" api="controllers" :query="{ module: formData.module }" v-else />
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单Icon" prop="icon" v-if="!isAction">
|
||||
<el-input v-model="formData.icon" name="icon" clearable @click="open" />
|
||||
<el-popover placement="right" :width="400" trigger="click">
|
||||
<template #reference>
|
||||
<el-input v-model="formData.icon" name="icon" clearable />
|
||||
</template>
|
||||
<div>
|
||||
<Icons v-model="formData.icon" @close="closeSelectIcon" />
|
||||
</div>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属组件" prop="component" v-if="!isAction">
|
||||
<Select v-model="formData.component" placeholder="请选择" allow-create api="components" :query="{ module: formData.module }" />
|
||||
@@ -92,10 +99,6 @@
|
||||
<el-button type="primary" @click="submitForm(form)">{{ $t('system.confirm') }}</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<Dialog v-model="visible" title="选择 Icon" width="1000px" destroy-on-close>
|
||||
<Icons v-model="formData.icon" @close="closeSelectIcon" />
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
@@ -105,7 +105,7 @@ const { formData, form, loading, submitForm, close, beforeCreate, beforeUpdate }
|
||||
|
||||
if (props.primary) {
|
||||
const { afterShow } = useShow(props.api, props.primary, formData)
|
||||
|
||||
// 更新角色值
|
||||
afterShow.value = formData => {
|
||||
const data = unref(formData)
|
||||
data.parent_id = data.parent_id ? [data.parent_id] : 0
|
||||
@@ -115,6 +115,8 @@ if (props.primary) {
|
||||
}
|
||||
|
||||
formData.value = data
|
||||
// 这里需要获取角色的上级的权限以限制可用权限范围
|
||||
getPermissions(data.parent_id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,9 +129,12 @@ const departments = ref()
|
||||
const showDepartments = ref<boolean>(false)
|
||||
|
||||
const permissionLoadingText = ref<string>('加载中...')
|
||||
|
||||
// 获取权限
|
||||
const getPermissions = async (value: number = 0) => {
|
||||
if (value) {
|
||||
http.get('permissions/roles/' + getParent(value)).then(r => {
|
||||
// 获取角色权限
|
||||
http.get('permissions/roles/' + getParent(value), { from: 'parent_role' }).then(r => {
|
||||
permissions.value = r.data.data.permissions
|
||||
setCheckedPermissions()
|
||||
})
|
||||
@@ -141,6 +146,7 @@ const getPermissions = async (value: number = 0) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 设置已选权限
|
||||
const setCheckedPermissions = () => {
|
||||
nextTick(() => {
|
||||
props.hasPermissions.forEach(p => {
|
||||
@@ -152,6 +158,8 @@ const setCheckedPermissions = () => {
|
||||
permissionLoadingText.value = '暂无数据'
|
||||
}
|
||||
}
|
||||
|
||||
// 获取角色信息
|
||||
const getRoles = () => {
|
||||
http.get(props.api, { id: props.primary ? props.primary : '' }).then(r => {
|
||||
roles.value = r.data.data
|
||||
@@ -163,9 +171,15 @@ const getDepartments = () => {
|
||||
departments.value = r.data.data
|
||||
})
|
||||
}
|
||||
|
||||
// 新增默认获取全部权限
|
||||
if (!props.primary) {
|
||||
getPermissions()
|
||||
}
|
||||
|
||||
// 页面挂载完成后
|
||||
onMounted(() => {
|
||||
getRoles()
|
||||
getPermissions()
|
||||
getDepartments()
|
||||
close(() => emit('close'))
|
||||
watch(
|
||||
|
@@ -63,18 +63,14 @@ trait UserRelations
|
||||
$permissions = $permissionsModel->get();
|
||||
} else {
|
||||
$permissions = Collection::make();
|
||||
|
||||
app($this->getRolesModel())->with(['permissions'])->get()
|
||||
|
||||
$this->roles()->with('permissions')->get()
|
||||
->each(function ($role) use (&$permissions) {
|
||||
$permissions = $permissions->concat($role->permissions);
|
||||
});
|
||||
|
||||
$permissions = $permissions->unique();
|
||||
}
|
||||
|
||||
$this->setAttribute('permissions', $permissions->each(fn ($permission) => $permission->setAttribute('hidden', $permission->isHidden())));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -103,14 +99,15 @@ trait UserRelations
|
||||
if ($permission->isAction()) {
|
||||
[$controller, $action] = explode('@', $permission->permission_mark);
|
||||
|
||||
$actions->add(CatchAdmin::getModuleControllerNamespace($permission->module).$controller.'Controller@'.$action);
|
||||
$actions->add(CatchAdmin::getModuleControllerNamespace($permission->module). ucfirst($controller).'Controller@'.$action);
|
||||
}
|
||||
});
|
||||
|
||||
// 自定义权限判断
|
||||
if ($permission) {
|
||||
[$module, $controller, $action] = explode('@', $permission);
|
||||
|
||||
$permission = CatchAdmin::getModuleControllerNamespace($module).$controller.'Controller@'.$action;
|
||||
$permission = CatchAdmin::getModuleControllerNamespace($module). ucfirst($controller) .'Controller@'.$action;
|
||||
}
|
||||
|
||||
return $actions->contains($permission ?: Route::currentRouteAction());
|
||||
|
@@ -73,7 +73,8 @@ class User extends Model implements AuthenticatableContract
|
||||
protected function DepartmentId(): Attribute
|
||||
{
|
||||
return new Attribute(
|
||||
get: fn($value) => $value ? : null
|
||||
get: fn($value) => $value ? : null,
|
||||
set: fn($value) => $value ? : 0
|
||||
);
|
||||
}
|
||||
|
||||
|
44
package.json
44
package.json
@@ -8,46 +8,46 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@heroicons/vue": "^2.0.14",
|
||||
"@tinymce/tinymce-vue": "^5.0.1",
|
||||
"@vueuse/core": "^9.12.0",
|
||||
"@heroicons/vue": "^2.0.18",
|
||||
"@tinymce/tinymce-vue": "^5.1.0",
|
||||
"@vueuse/core": "^10.1.2",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"element-plus": "^2.2.33",
|
||||
"element-plus": "^2.3.4",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.0.32",
|
||||
"postcss": "^8.4.21",
|
||||
"tailwindcss": "^3.2.2",
|
||||
"terser": "^5.16.5",
|
||||
"pinia": "^2.0.36",
|
||||
"postcss": "^8.4.23",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"terser": "^5.17.3",
|
||||
"vue": "^3.2.47",
|
||||
"vue-i18n": "9",
|
||||
"vue-router": "4.1.6",
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/logos": "^1.1.22",
|
||||
"@rollup/plugin-alias": "^4.0.3",
|
||||
"@iconify-json/logos": "^1.1.28",
|
||||
"@rollup/plugin-alias": "^5.0.0",
|
||||
"@types/mockjs": "^1.0.7",
|
||||
"@types/node": "^18.14.6",
|
||||
"@types/node": "^20.1.1",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
||||
"@typescript-eslint/parser": "^5.54.0",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.5",
|
||||
"@typescript-eslint/parser": "^5.59.5",
|
||||
"@vitejs/plugin-vue": "^4.2.1",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||
"axios": "^1.3.4",
|
||||
"eslint": "^8.35.0",
|
||||
"axios": "^1.4.0",
|
||||
"eslint": "^8.40.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-n": "^15.6.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-vue": "^9.9.0",
|
||||
"eslint-plugin-vue": "^9.11.1",
|
||||
"prettier": "2.8.4",
|
||||
"sass": "^1.58.0",
|
||||
"typescript": "^4.9.5",
|
||||
"sass": "^1.62.1",
|
||||
"typescript": "^5.0.4",
|
||||
"unplugin-auto-import": "^0.14.4",
|
||||
"unplugin-icons": "^0.15.2",
|
||||
"unplugin-icons": "^0.16.1",
|
||||
"unplugin-vue-components": "^0.24.0",
|
||||
"vite": "^4.1.4",
|
||||
"vite": "^4.3.5",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vue-tsc": "^1.2.0"
|
||||
"vue-tsc": "^1.6.4"
|
||||
}
|
||||
}
|
||||
|
@@ -1,20 +1,28 @@
|
||||
<template>
|
||||
<div :class="`grid ${grid} gap-y-6`">
|
||||
<div class="h-84 pl-2 pr-2">
|
||||
<div :class="`grid ${grid} gap-y-4 gap-x-4` + ' mt-3 h-72'">
|
||||
<div v-for="icon in icons" :key="icon" class="flex justify-center hover:cursor-pointer" @click="selectIcon(icon)">
|
||||
<div v-if="modelValue === icon">
|
||||
<div class="flex justify-center w-full text-violet-700"><Icon :name="icon" /></div>
|
||||
<div class="text-sm text-violet-700">{{ icon }}</div>
|
||||
<div class="flex justify-center w-full text-violet-700"><Icon :name="icon" className="w-5 h-5" /></div>
|
||||
<div class="text-[1px] text-violet-700">{{ icon }}</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<div class="flex justify-center w-full"><Icon :name="icon" /></div>
|
||||
<div class="text-sm">{{ icon }}</div>
|
||||
<div class="flex justify-center w-full"><Icon :name="icon" className="w-5 h-5" /></div>
|
||||
<div class="text-[1px]">{{ icon }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center mt-6">
|
||||
<el-pagination layout="prev,next" :page-size="limit" :total="total" prev-text="上一页" next-text="下一页" @next-click="handleNext" @prev-click="handlePrev" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
@@ -22,18 +30,38 @@ const props = defineProps({
|
||||
},
|
||||
grid: {
|
||||
type: String,
|
||||
default: 'grid-cols-5',
|
||||
default: 'grid-cols-4',
|
||||
},
|
||||
})
|
||||
|
||||
const emits = defineEmits(['update:modelValue', 'close'])
|
||||
|
||||
const limit = ref(16)
|
||||
const icons = ref([])
|
||||
const total = ref(0)
|
||||
function getIcons(page = 1) {
|
||||
const start = (page - 1) * limit.value
|
||||
const end = start + limit.value
|
||||
icons.value = constIcons.slice(start, end)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getIcons()
|
||||
total.value = constIcons.length
|
||||
})
|
||||
const handleNext = value => {
|
||||
getIcons(value)
|
||||
}
|
||||
|
||||
const handlePrev = value => {
|
||||
getIcons(value)
|
||||
}
|
||||
const selectIcon = (icon: string) => {
|
||||
emits('update:modelValue', icon)
|
||||
emits('close')
|
||||
}
|
||||
// icons
|
||||
const icons = [
|
||||
const constIcons = [
|
||||
'academic-cap',
|
||||
'adjustments-horizontal',
|
||||
'adjustments-vertical',
|
||||
|
@@ -57,11 +57,7 @@ export function useGetList(path: string, isPaginate: boolean = true) {
|
||||
// reset
|
||||
function reset() {
|
||||
resetPage()
|
||||
|
||||
if (isPaginate) {
|
||||
query.value = Object.assign({ page: page.value, limit: limit.value })
|
||||
}
|
||||
|
||||
query.value = Object.assign(isPaginate ? { page: page.value, limit: limit.value } : {})
|
||||
getList()
|
||||
}
|
||||
|
||||
|
@@ -5,12 +5,12 @@
|
||||
<!-- Tag view -->
|
||||
<!--<div class=""></div>-->
|
||||
<!-- Container -->
|
||||
<div class="p-1 sm:p-2 max-w-full h-screen overflow-auto sm:overflow-x-hidden">
|
||||
<div class="p-1 sm:p-3 max-w-full h-screen overflow-auto sm:overflow-x-hidden">
|
||||
<div class="min-h-[calc(100vh-8rem)]">
|
||||
<router-view />
|
||||
</div>
|
||||
<div class="w-full text-center text-gray-400 h-4 leading-10">
|
||||
<el-link href="https://catchadmin.com/">CatchAdmin 管理系统 </el-link> @copyright 2018 ~ {{ year }}
|
||||
<el-link href="https://catchadmin.com/" target="_blank">CatchAdmin 管理系统 </el-link> @copyright 2018 ~ {{ year }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user