优化更新
This commit is contained in:
@@ -53,15 +53,21 @@ class DepartmentExport implements FromCollection, WithHeadings, WithMapping, Sho
|
||||
*/
|
||||
public function map($department): array
|
||||
{
|
||||
$parentName = '';
|
||||
if ($department->parent_id) {
|
||||
$parent = Department::find($department->parent_id);
|
||||
$parentName = $parent ? $parent->name : '';
|
||||
}
|
||||
|
||||
return [
|
||||
$department->id,
|
||||
$department->name,
|
||||
$department->parent_id ? Department::find($department->parent_id)?->name : '',
|
||||
$parentName,
|
||||
$department->leader,
|
||||
$department->phone,
|
||||
$department->sort,
|
||||
(int)$department->sort,
|
||||
$department->status == 1 ? '启用' : '禁用',
|
||||
$department->created_at ? $department->created_at->toDateTimeString() : '',
|
||||
$department->created_at ? (string)$department->created_at : '',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
85
app/Exports/PermissionExport.php
Normal file
85
app/Exports/PermissionExport.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exports;
|
||||
|
||||
use App\Models\Auth\Permission;
|
||||
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
|
||||
|
||||
class PermissionExport implements FromCollection, WithHeadings, WithMapping, ShouldAutoSize
|
||||
{
|
||||
protected $permissionIds;
|
||||
|
||||
public function __construct(array $permissionIds = [])
|
||||
{
|
||||
$this->permissionIds = $permissionIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据集合
|
||||
*/
|
||||
public function collection()
|
||||
{
|
||||
$query = Permission::query();
|
||||
|
||||
if (!empty($this->permissionIds)) {
|
||||
$query->whereIn('id', $this->permissionIds);
|
||||
}
|
||||
|
||||
return $query->orderBy('sort')->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置表头
|
||||
*/
|
||||
public function headings(): array
|
||||
{
|
||||
return [
|
||||
'ID',
|
||||
'权限标题',
|
||||
'权限编码',
|
||||
'权限类型',
|
||||
'父级ID',
|
||||
'路由路径',
|
||||
'前端组件',
|
||||
'排序',
|
||||
'状态',
|
||||
'创建时间',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 映射数据
|
||||
*/
|
||||
public function map($permission): array
|
||||
{
|
||||
return [
|
||||
$permission->id,
|
||||
$permission->title,
|
||||
$permission->name,
|
||||
$this->getTypeName($permission->type),
|
||||
$permission->parent_id ?: 0,
|
||||
$permission->path,
|
||||
$permission->component,
|
||||
(int)$permission->sort,
|
||||
$permission->status == 1 ? '启用' : '禁用',
|
||||
$permission->created_at ? (string)$permission->created_at : '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型名称
|
||||
*/
|
||||
protected function getTypeName($type): string
|
||||
{
|
||||
$types = [
|
||||
'menu' => '菜单',
|
||||
'api' => 'API接口',
|
||||
'button' => '按钮',
|
||||
'url' => '链接',
|
||||
];
|
||||
return $types[$type] ?? $type;
|
||||
}
|
||||
}
|
||||
67
app/Exports/RoleExport.php
Normal file
67
app/Exports/RoleExport.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exports;
|
||||
|
||||
use App\Models\Auth\Role;
|
||||
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
|
||||
|
||||
class RoleExport implements FromCollection, WithHeadings, WithMapping, ShouldAutoSize
|
||||
{
|
||||
protected $roleIds;
|
||||
|
||||
public function __construct(array $roleIds = [])
|
||||
{
|
||||
$this->roleIds = $roleIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据集合
|
||||
*/
|
||||
public function collection()
|
||||
{
|
||||
$query = Role::with(['permissions']);
|
||||
|
||||
if (!empty($this->roleIds)) {
|
||||
$query->whereIn('id', $this->roleIds);
|
||||
}
|
||||
|
||||
return $query->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置表头
|
||||
*/
|
||||
public function headings(): array
|
||||
{
|
||||
return [
|
||||
'ID',
|
||||
'角色名称',
|
||||
'角色编码',
|
||||
'描述',
|
||||
'权限',
|
||||
'排序',
|
||||
'状态',
|
||||
'创建时间',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 映射数据
|
||||
*/
|
||||
public function map($role): array
|
||||
{
|
||||
return [
|
||||
$role->id,
|
||||
$role->name,
|
||||
$role->code,
|
||||
$role->description,
|
||||
$role->permissions->pluck('title')->implode(','),
|
||||
(int)$role->sort,
|
||||
$role->status == 1 ? '启用' : '禁用',
|
||||
$role->created_at ? (string)$role->created_at : '',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -64,8 +64,8 @@ class UserExport implements FromCollection, WithHeadings, WithMapping, ShouldAut
|
||||
$user->department ? $user->department->name : '',
|
||||
$user->roles->pluck('name')->implode(','),
|
||||
$user->status == 1 ? '启用' : '禁用',
|
||||
$user->last_login_at ? $user->last_login_at->toDateTimeString() : '',
|
||||
$user->created_at ? $user->created_at->toDateTimeString() : '',
|
||||
$user->last_login_at ? (string)$user->last_login_at : '',
|
||||
$user->created_at ? (string)$user->created_at : '',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,20 @@ namespace App\Http\Controllers\Auth\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\Auth\PermissionService;
|
||||
use App\Services\Auth\ImportExportService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Permission extends Controller
|
||||
{
|
||||
protected $permissionService;
|
||||
protected $importExportService;
|
||||
|
||||
public function __construct(PermissionService $permissionService)
|
||||
{
|
||||
public function __construct(
|
||||
PermissionService $permissionService,
|
||||
ImportExportService $importExportService
|
||||
) {
|
||||
$this->permissionService = $permissionService;
|
||||
$this->importExportService = $importExportService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,4 +182,54 @@ class Permission extends Controller
|
||||
'data' => ['count' => $count],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出权限
|
||||
*/
|
||||
public function export(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'ids' => 'nullable|array',
|
||||
'ids.*' => 'integer',
|
||||
]);
|
||||
|
||||
$filename = $this->importExportService->exportPermissions($validated['ids'] ?? []);
|
||||
|
||||
$filePath = $this->importExportService->getExportFilePath($filename);
|
||||
|
||||
return response()->download($filePath, $filename)->deleteFileAfterSend();
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入权限
|
||||
*/
|
||||
public function import(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'file' => 'required|file|mimes:xlsx,xls',
|
||||
]);
|
||||
|
||||
$file = $request->file('file');
|
||||
$realPath = $file->getRealPath();
|
||||
$filename = $file->getClientOriginalName();
|
||||
|
||||
$result = $this->importExportService->importPermissions($filename, $realPath);
|
||||
|
||||
return response()->json([
|
||||
'code' => 200,
|
||||
'message' => "导入完成,成功 {$result['success_count']} 条,失败 {$result['error_count']} 条",
|
||||
'data' => $result,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载权限导入模板
|
||||
*/
|
||||
public function downloadTemplate()
|
||||
{
|
||||
$filename = $this->importExportService->downloadPermissionTemplate();
|
||||
$filePath = $this->importExportService->getExportFilePath($filename);
|
||||
|
||||
return response()->download($filePath, $filename)->deleteFileAfterSend();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,20 @@ namespace App\Http\Controllers\Auth\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\Auth\RoleService;
|
||||
use App\Services\Auth\ImportExportService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Role extends Controller
|
||||
{
|
||||
protected $roleService;
|
||||
protected $importExportService;
|
||||
|
||||
public function __construct(RoleService $roleService)
|
||||
{
|
||||
public function __construct(
|
||||
RoleService $roleService,
|
||||
ImportExportService $importExportService
|
||||
) {
|
||||
$this->roleService = $roleService;
|
||||
$this->importExportService = $importExportService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,4 +242,54 @@ class Role extends Controller
|
||||
'data' => $result,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出角色
|
||||
*/
|
||||
public function export(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'ids' => 'nullable|array',
|
||||
'ids.*' => 'integer',
|
||||
]);
|
||||
|
||||
$filename = $this->importExportService->exportRoles($validated['ids'] ?? []);
|
||||
|
||||
$filePath = $this->importExportService->getExportFilePath($filename);
|
||||
|
||||
return response()->download($filePath, $filename)->deleteFileAfterSend();
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入角色
|
||||
*/
|
||||
public function import(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'file' => 'required|file|mimes:xlsx,xls',
|
||||
]);
|
||||
|
||||
$file = $request->file('file');
|
||||
$realPath = $file->getRealPath();
|
||||
$filename = $file->getClientOriginalName();
|
||||
|
||||
$result = $this->importExportService->importRoles($filename, $realPath);
|
||||
|
||||
return response()->json([
|
||||
'code' => 200,
|
||||
'message' => "导入完成,成功 {$result['success_count']} 条,失败 {$result['error_count']} 条",
|
||||
'data' => $result,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载角色导入模板
|
||||
*/
|
||||
public function downloadTemplate()
|
||||
{
|
||||
$filename = $this->importExportService->downloadRoleTemplate();
|
||||
$filePath = $this->importExportService->getExportFilePath($filename);
|
||||
|
||||
return response()->download($filePath, $filename)->deleteFileAfterSend();
|
||||
}
|
||||
}
|
||||
|
||||
166
app/Imports/PermissionImport.php
Normal file
166
app/Imports/PermissionImport.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
namespace App\Imports;
|
||||
|
||||
use App\Models\Auth\Permission;
|
||||
use Illuminate\Support\Collection;
|
||||
use Maatwebsite\Excel\Concerns\ToCollection;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadingRow;
|
||||
use Maatwebsite\Excel\Concerns\WithValidation;
|
||||
|
||||
class PermissionImport implements ToCollection, WithHeadingRow, WithValidation
|
||||
{
|
||||
protected $successCount = 0;
|
||||
protected $errorCount = 0;
|
||||
protected $errors = [];
|
||||
|
||||
/**
|
||||
* 处理导入数据
|
||||
*/
|
||||
public function collection(Collection $rows)
|
||||
{
|
||||
foreach ($rows as $index => $row) {
|
||||
try {
|
||||
// 跳过空行
|
||||
if (empty($row['权限标题'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查权限编码是否已存在
|
||||
$exists = Permission::where('name', $row['权限编码'])->exists();
|
||||
if ($exists) {
|
||||
$this->addError($index + 2, '权限编码已存在');
|
||||
continue;
|
||||
}
|
||||
|
||||
// 查找父级权限
|
||||
$parentId = null;
|
||||
if (!empty($row['父级ID']) && $row['父级ID'] != 0) {
|
||||
$parent = Permission::find($row['父级ID']);
|
||||
if (!$parent) {
|
||||
$this->addError($index + 2, '父级权限不存在');
|
||||
continue;
|
||||
}
|
||||
$parentId = $parent->id;
|
||||
}
|
||||
|
||||
// 解析类型
|
||||
$type = $this->parseType($row['权限类型']);
|
||||
|
||||
// 解析元数据
|
||||
$meta = null;
|
||||
if ($type === 'menu' && !empty($row['元数据'])) {
|
||||
// 元数据格式: icon:Setting,hidden:false,keepAlive:false
|
||||
$meta = $this->parseMeta($row['元数据']);
|
||||
}
|
||||
|
||||
// 创建权限
|
||||
Permission::create([
|
||||
'title' => $row['权限标题'],
|
||||
'name' => $row['权限编码'],
|
||||
'type' => $type,
|
||||
'parent_id' => $parentId,
|
||||
'path' => $row['路由路径'] ?? null,
|
||||
'component' => $row['前端组件'] ?? null,
|
||||
'meta' => $meta,
|
||||
'sort' => $row['排序'] ?? 0,
|
||||
'status' => 1,
|
||||
]);
|
||||
|
||||
$this->successCount++;
|
||||
} catch (\Exception $e) {
|
||||
$this->addError($index + 2, $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证规则
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'权限标题' => 'required|string|max:50',
|
||||
'权限编码' => 'required|string|max:100',
|
||||
'权限类型' => 'required|in:菜单,API接口,按钮,链接,menu,api,button,url',
|
||||
'父级ID' => 'nullable|integer|min:0',
|
||||
'路由路径' => 'nullable|string|max:200',
|
||||
'前端组件' => 'nullable|string|max:200',
|
||||
'排序' => 'nullable|integer|min:0',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义验证消息
|
||||
*/
|
||||
public function customValidationMessages(): array
|
||||
{
|
||||
return [
|
||||
'权限标题.required' => '权限标题不能为空',
|
||||
'权限编码.required' => '权限编码不能为空',
|
||||
'权限类型.required' => '权限类型不能为空',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析类型
|
||||
*/
|
||||
protected function parseType($type): string
|
||||
{
|
||||
$typeMap = [
|
||||
'菜单' => 'menu',
|
||||
'API接口' => 'api',
|
||||
'接口' => 'api',
|
||||
'按钮' => 'button',
|
||||
'链接' => 'url',
|
||||
];
|
||||
|
||||
return $typeMap[$type] ?? $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析元数据
|
||||
*/
|
||||
protected function parseMeta($metaString): ?string
|
||||
{
|
||||
if (empty($metaString)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 简单解析,实际项目中可能需要更复杂的解析逻辑
|
||||
return $metaString;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加错误
|
||||
*/
|
||||
protected function addError(int $row, string $message): void
|
||||
{
|
||||
$this->errorCount++;
|
||||
$this->errors[] = "第 {$row} 行: {$message}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成功数量
|
||||
*/
|
||||
public function getSuccessCount(): int
|
||||
{
|
||||
return $this->successCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误数量
|
||||
*/
|
||||
public function getErrorCount(): int
|
||||
{
|
||||
return $this->errorCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
*/
|
||||
public function getErrors(): array
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
}
|
||||
129
app/Imports/RoleImport.php
Normal file
129
app/Imports/RoleImport.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace App\Imports;
|
||||
|
||||
use App\Models\Auth\Role;
|
||||
use App\Models\Auth\Permission;
|
||||
use Illuminate\Support\Collection;
|
||||
use Maatwebsite\Excel\Concerns\ToCollection;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadingRow;
|
||||
use Maatwebsite\Excel\Concerns\WithValidation;
|
||||
|
||||
class RoleImport implements ToCollection, WithHeadingRow, WithValidation
|
||||
{
|
||||
protected $successCount = 0;
|
||||
protected $errorCount = 0;
|
||||
protected $errors = [];
|
||||
|
||||
/**
|
||||
* 处理导入数据
|
||||
*/
|
||||
public function collection(Collection $rows)
|
||||
{
|
||||
foreach ($rows as $index => $row) {
|
||||
try {
|
||||
// 跳过空行
|
||||
if (empty($row['角色名称'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查角色编码是否已存在
|
||||
$exists = Role::where('code', $row['角色编码'])->exists();
|
||||
if ($exists) {
|
||||
$this->addError($index + 2, '角色编码已存在');
|
||||
continue;
|
||||
}
|
||||
|
||||
// 查找权限
|
||||
$permissionIds = [];
|
||||
if (!empty($row['权限(多个用逗号分隔)'])) {
|
||||
$permissionNames = array_map('trim', explode(',', $row['权限(多个用逗号分隔)']));
|
||||
$permissions = Permission::whereIn('title', $permissionNames)->get();
|
||||
|
||||
if ($permissions->count() != count($permissionNames)) {
|
||||
$existingNames = $permissions->pluck('title')->toArray();
|
||||
$notFound = array_diff($permissionNames, $existingNames);
|
||||
$this->addError($index + 2, '权限不存在: ' . implode(', ', $notFound));
|
||||
continue;
|
||||
}
|
||||
$permissionIds = $permissions->pluck('id')->toArray();
|
||||
}
|
||||
|
||||
// 创建角色
|
||||
$role = Role::create([
|
||||
'name' => $row['角色名称'],
|
||||
'code' => $row['角色编码'],
|
||||
'description' => $row['描述'] ?? null,
|
||||
'sort' => $row['排序'] ?? 0,
|
||||
'status' => 1,
|
||||
]);
|
||||
|
||||
// 分配权限
|
||||
if (!empty($permissionIds)) {
|
||||
$role->permissions()->attach($permissionIds);
|
||||
}
|
||||
|
||||
$this->successCount++;
|
||||
} catch (\Exception $e) {
|
||||
$this->addError($index + 2, $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证规则
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'角色名称' => 'required|string|max:50',
|
||||
'角色编码' => 'required|string|max:50',
|
||||
'描述' => 'nullable|string|max:200',
|
||||
'排序' => 'nullable|integer|min:0',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义验证消息
|
||||
*/
|
||||
public function customValidationMessages(): array
|
||||
{
|
||||
return [
|
||||
'角色名称.required' => '角色名称不能为空',
|
||||
'角色编码.required' => '角色编码不能为空',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加错误
|
||||
*/
|
||||
protected function addError(int $row, string $message): void
|
||||
{
|
||||
$this->errorCount++;
|
||||
$this->errors[] = "第 {$row} 行: {$message}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成功数量
|
||||
*/
|
||||
public function getSuccessCount(): int
|
||||
{
|
||||
return $this->successCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误数量
|
||||
*/
|
||||
public function getErrorCount(): int
|
||||
{
|
||||
return $this->errorCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
*/
|
||||
public function getErrors(): array
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,14 @@
|
||||
|
||||
namespace App\Models\Auth;
|
||||
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Department extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
use ModelTrait, SoftDeletes;
|
||||
|
||||
protected $table = 'auth_departments';
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
|
||||
namespace App\Models\Auth;
|
||||
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Permission extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
use ModelTrait, SoftDeletes;
|
||||
|
||||
protected $table = 'auth_permissions';
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
|
||||
namespace App\Models\Auth;
|
||||
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Role extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
use ModelTrait, SoftDeletes;
|
||||
|
||||
protected $table = 'auth_roles';
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models\Auth;
|
||||
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
@@ -11,7 +12,7 @@ use Tymon\JWTAuth\Contracts\JWTSubject;
|
||||
|
||||
class User extends Authenticatable implements JWTSubject
|
||||
{
|
||||
use SoftDeletes;
|
||||
use ModelTrait, SoftDeletes;
|
||||
|
||||
protected $table = 'auth_users';
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
namespace App\Models\System;
|
||||
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class City extends Model
|
||||
{
|
||||
use ModelTrait;
|
||||
protected $table = 'system_cities';
|
||||
|
||||
protected $fillable = [
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
namespace App\Models\System;
|
||||
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Config extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
use ModelTrait, SoftDeletes;
|
||||
|
||||
protected $table = 'system_configs';
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
|
||||
namespace App\Models\System;
|
||||
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Dictionary extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
use ModelTrait, SoftDeletes;
|
||||
|
||||
protected $table = 'system_dictionaries';
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
namespace App\Models\System;
|
||||
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class DictionaryItem extends Model
|
||||
{
|
||||
use ModelTrait;
|
||||
protected $table = 'system_dictionary_items';
|
||||
|
||||
protected $fillable = [
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
namespace App\Models\System;
|
||||
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Log extends Model
|
||||
{
|
||||
use ModelTrait;
|
||||
protected $table = 'system_logs';
|
||||
|
||||
protected $fillable = [
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
namespace App\Models\System;
|
||||
|
||||
use App\Traits\ModelTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Task extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
use ModelTrait, SoftDeletes;
|
||||
|
||||
protected $table = 'system_tasks';
|
||||
|
||||
|
||||
@@ -10,8 +10,12 @@ use Illuminate\Validation\ValidationException;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use App\Exports\UserExport;
|
||||
use App\Exports\DepartmentExport;
|
||||
use App\Exports\RoleExport;
|
||||
use App\Exports\PermissionExport;
|
||||
use App\Imports\UserImport;
|
||||
use App\Imports\DepartmentImport;
|
||||
use App\Imports\RoleImport;
|
||||
use App\Imports\PermissionImport;
|
||||
|
||||
class ImportExportService
|
||||
{
|
||||
@@ -187,6 +191,166 @@ class ImportExportService
|
||||
return storage_path('app/exports/' . $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载角色导入模板
|
||||
*/
|
||||
public function downloadRoleTemplate(): string
|
||||
{
|
||||
$filename = 'role_import_template_' . date('YmdHis') . '.xlsx';
|
||||
|
||||
// 确保目录存在
|
||||
if (!is_dir(storage_path('app/exports'))) {
|
||||
mkdir(storage_path('app/exports'), 0755, true);
|
||||
}
|
||||
|
||||
$templateData = [
|
||||
[
|
||||
'角色名称*',
|
||||
'角色编码*',
|
||||
'描述',
|
||||
'权限(多个用逗号分隔)',
|
||||
'排序',
|
||||
],
|
||||
[
|
||||
'测试角色',
|
||||
'test_role',
|
||||
'这是一个测试角色',
|
||||
'查看用户,编辑用户',
|
||||
'1',
|
||||
],
|
||||
];
|
||||
|
||||
Excel::store(new \App\Exports\GenericExport($templateData), 'exports/' . $filename);
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出角色数据
|
||||
*/
|
||||
public function exportRoles(array $roleIds = []): string
|
||||
{
|
||||
$filename = 'roles_export_' . date('YmdHis') . '.xlsx';
|
||||
|
||||
// 确保目录存在
|
||||
if (!is_dir(storage_path('app/exports'))) {
|
||||
mkdir(storage_path('app/exports'), 0755, true);
|
||||
}
|
||||
|
||||
Excel::store(new RoleExport($roleIds), 'exports/' . $filename);
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入角色数据
|
||||
*/
|
||||
public function importRoles(string $filePath, string $realPath): array
|
||||
{
|
||||
if (!file_exists($realPath)) {
|
||||
throw ValidationException::withMessages([
|
||||
'file' => ['文件不存在'],
|
||||
]);
|
||||
}
|
||||
|
||||
$import = new RoleImport();
|
||||
Excel::import($import, $realPath);
|
||||
|
||||
// 删除临时文件
|
||||
if (file_exists($realPath)) {
|
||||
unlink($realPath);
|
||||
}
|
||||
|
||||
return [
|
||||
'success_count' => $import->getSuccessCount(),
|
||||
'error_count' => $import->getErrorCount(),
|
||||
'errors' => $import->getErrors(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载权限导入模板
|
||||
*/
|
||||
public function downloadPermissionTemplate(): string
|
||||
{
|
||||
$filename = 'permission_import_template_' . date('YmdHis') . '.xlsx';
|
||||
|
||||
// 确保目录存在
|
||||
if (!is_dir(storage_path('app/exports'))) {
|
||||
mkdir(storage_path('app/exports'), 0755, true);
|
||||
}
|
||||
|
||||
$templateData = [
|
||||
[
|
||||
'权限标题*',
|
||||
'权限编码*',
|
||||
'权限类型*',
|
||||
'父级ID',
|
||||
'路由路径',
|
||||
'前端组件',
|
||||
'元数据',
|
||||
'排序',
|
||||
],
|
||||
[
|
||||
'系统管理',
|
||||
'system',
|
||||
'菜单',
|
||||
'0',
|
||||
'/system',
|
||||
null,
|
||||
'icon:Setting',
|
||||
'1',
|
||||
],
|
||||
];
|
||||
|
||||
Excel::store(new \App\Exports\GenericExport($templateData), 'exports/' . $filename);
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出权限数据
|
||||
*/
|
||||
public function exportPermissions(array $permissionIds = []): string
|
||||
{
|
||||
$filename = 'permissions_export_' . date('YmdHis') . '.xlsx';
|
||||
|
||||
// 确保目录存在
|
||||
if (!is_dir(storage_path('app/exports'))) {
|
||||
mkdir(storage_path('app/exports'), 0755, true);
|
||||
}
|
||||
|
||||
Excel::store(new PermissionExport($permissionIds), 'exports/' . $filename);
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入权限数据
|
||||
*/
|
||||
public function importPermissions(string $filePath, string $realPath): array
|
||||
{
|
||||
if (!file_exists($realPath)) {
|
||||
throw ValidationException::withMessages([
|
||||
'file' => ['文件不存在'],
|
||||
]);
|
||||
}
|
||||
|
||||
$import = new PermissionImport();
|
||||
Excel::import($import, $realPath);
|
||||
|
||||
// 删除临时文件
|
||||
if (file_exists($realPath)) {
|
||||
unlink($realPath);
|
||||
}
|
||||
|
||||
return [
|
||||
'success_count' => $import->getSuccessCount(),
|
||||
'error_count' => $import->getErrorCount(),
|
||||
'errors' => $import->getErrors(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除导出文件
|
||||
*/
|
||||
|
||||
230
app/Support/Regex.php
Normal file
230
app/Support/Regex.php
Normal file
@@ -0,0 +1,230 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace App\Support;
|
||||
|
||||
class Regex {
|
||||
|
||||
|
||||
/**
|
||||
* 验证用户名
|
||||
*
|
||||
* @param string $value 验证的值
|
||||
* @param int $minLen 最小长度
|
||||
* @param int $maxLen 最大长度
|
||||
* @param string $type 验证类型,默认‘ALL’,EN.验证英文,CN.验证中文,ALL.验证中文和英文
|
||||
* @return bool
|
||||
*/
|
||||
public static function isUsername($value, $minLen = 2, $maxLen = 48, $type = 'ALL')
|
||||
{
|
||||
if (empty ($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'EN' :
|
||||
$match = '/^[_\w\d]{' . $minLen . ',' . $maxLen . '}$/iu';
|
||||
break;
|
||||
case 'CN' :
|
||||
$match = '/^[_\x{4e00}-\x{9fa5}\d]{' . $minLen . ',' . $maxLen . '}$/iu';
|
||||
break;
|
||||
default :
|
||||
$match = '/^[_\w\d\x{4e00}-\x{9fa5}]{' . $minLen . ',' . $maxLen . '}$/iu';
|
||||
}
|
||||
|
||||
return preg_match($match, $value) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证密码
|
||||
*
|
||||
* @param string $value 验证的值
|
||||
* @param int $minLen 最小长度
|
||||
* @param int $maxLen 最大长度
|
||||
* @return bool
|
||||
*/
|
||||
public static function isPassword($value, $minLen = 6, $maxLen = 16)
|
||||
{
|
||||
$value = trim($value);
|
||||
if (empty ($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$match = '/^[\\~!@#$%^&*()-_=+|{}\[\],.?\/:;\'\"\d\w]{' . $minLen . ',' . $maxLen . '}$/';
|
||||
|
||||
return preg_match($match, $value) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证eamil
|
||||
*
|
||||
* @param string $value 验证的值
|
||||
* @return bool
|
||||
*/
|
||||
public static function isEmail($value)
|
||||
{
|
||||
$value = trim($value);
|
||||
if (empty ($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$match = '/^[\w\d]+[\w\d-.]*@[\w\d-.]+\.[\w\d]{2,10}$/i';
|
||||
|
||||
return preg_match($match, $value) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证电话号码
|
||||
*
|
||||
* @param string $value 验证的值
|
||||
* @return bool
|
||||
*/
|
||||
public static function isTelephone($value)
|
||||
{
|
||||
$value = trim($value);
|
||||
if (empty ($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$match = '/^0[0-9]{2,3}[-]?\d{7,8}$/';
|
||||
|
||||
return preg_match($match, $value) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证手机
|
||||
*
|
||||
* @param string $value 验证的值
|
||||
* @return bool
|
||||
*/
|
||||
public static function isMobile($value)
|
||||
{
|
||||
$value = trim($value);
|
||||
if (empty ($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$match = '/^[(86)|0]?(1\d{10})$/';
|
||||
|
||||
return preg_match($match, $value) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证邮政编码
|
||||
*
|
||||
* @param string $value 验证的值
|
||||
* @return bool
|
||||
*/
|
||||
public static function isPostCode($value)
|
||||
{
|
||||
$value = trim($value);
|
||||
if (empty ($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$match = '/\d{6}/';
|
||||
|
||||
return preg_match($match, $value) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证IP
|
||||
*
|
||||
* @param string $value 验证的值
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isIp($value)
|
||||
{
|
||||
$value = trim($value);
|
||||
if (empty ($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$match = '/^(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])' .
|
||||
'\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)' .
|
||||
'\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)' .
|
||||
'\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$/';
|
||||
|
||||
return preg_match($match, $value) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证身份证号码
|
||||
*
|
||||
* @param string $value 验证的值
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isIDCard($value)
|
||||
{
|
||||
$value = trim($value);
|
||||
if (empty ($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen($value) > 18) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$match = '/^\d{6}((1[89])|(2\d))\d{2}((0\d)|(1[0-2]))((3[01])|([0-2]\d))\d{3}(\d|X)$/i';
|
||||
|
||||
return preg_match($match, $value) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证URL
|
||||
*
|
||||
* @param string $value 验证的值
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isUrl($value)
|
||||
{
|
||||
$value = strtolower(trim($value));
|
||||
if (empty ($value)) {
|
||||
return false;
|
||||
}
|
||||
$match = '/^(http:\/\/)?(https:\/\/)?([\w\d-]+\.)+[\w-]+(\/[\d\w-.\/?%&=]*)?$/';
|
||||
|
||||
return preg_match($match, $value) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有数字
|
||||
* 说明:如果字符串中含有非法字符返回假,没有返回真
|
||||
*
|
||||
* @param string $value 验证的值
|
||||
* @return int
|
||||
*/
|
||||
public static function hasNumber($value)
|
||||
{
|
||||
return preg_match("/[0-9]/", $value) != false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否含有英文
|
||||
* 说明:如果字符串中含有非法字符返回假,没有返回真
|
||||
*
|
||||
* @param string $value 验证的值
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasEnglish($value)
|
||||
{
|
||||
return preg_match("/[a-zA-Z]/", $value) != false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有中文
|
||||
* 说明:如果字符串中含有非法字符返回假,没有返回真
|
||||
*
|
||||
* @param string $value 验证的值
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasChinese($value)
|
||||
{
|
||||
return preg_match("/[\x7f-\xff]/", $value) != false;
|
||||
}
|
||||
}
|
||||
298
app/Support/Time.php
Normal file
298
app/Support/Time.php
Normal file
@@ -0,0 +1,298 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace App\Support;
|
||||
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class Time {
|
||||
|
||||
/**
|
||||
* 返回今日开始和结束的时间戳
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function today($data = '') {
|
||||
[$y, $m, $d] = explode('-', $data ? $data : date('Y-m-d'));
|
||||
|
||||
return [
|
||||
mktime(0, 0, 0, $m, $d, $y),
|
||||
mktime(23, 59, 59, $m, $d, $y),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回昨日开始和结束的时间戳
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function yesterday() {
|
||||
$yesterday = date('d') - 1;
|
||||
|
||||
return [
|
||||
mktime(0, 0, 0, date('m'), $yesterday, date('Y')),
|
||||
mktime(23, 59, 59, date('m'), $yesterday, date('Y')),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回本周开始和结束的时间戳
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function week() {
|
||||
[$y, $m, $d, $w] = explode('-', date('Y-m-d-w'));
|
||||
if ($w == 0) $w = 7; //修正周日的问题
|
||||
|
||||
return [
|
||||
mktime(0, 0, 0, $m, $d - $w + 1, $y), mktime(23, 59, 59, $m, $d - $w + 7, $y),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回上周开始和结束的时间戳
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function lastWeek() {
|
||||
$timestamp = time();
|
||||
|
||||
return [
|
||||
strtotime(date('Y-m-d', strtotime("last week Monday", $timestamp))),
|
||||
strtotime(date('Y-m-d', strtotime("last week Sunday", $timestamp))) + 24 * 3600 - 1,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回本月开始和结束的时间戳
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function month($data = '') {
|
||||
$res = [];
|
||||
$data = $data ? $data : date('Y-m-d');
|
||||
$nextMoth = date('Y-m-d', strtotime($data . ' +1 month'));
|
||||
|
||||
return [
|
||||
mktime(0, 0, 0, date('m', strtotime($data)), 1, date('Y', strtotime($data))),
|
||||
mktime(0, 0, 0, date('m', strtotime($nextMoth)), 1, date('Y', strtotime($nextMoth))),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回上个月开始和结束的时间戳
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function lastMonth() {
|
||||
$y = date('Y');
|
||||
$m = date('m');
|
||||
$begin = mktime(0, 0, 0, $m - 1, 1, $y);
|
||||
$end = mktime(23, 59, 59, $m - 1, date('t', $begin), $y);
|
||||
|
||||
return [$begin, $end];
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回今年开始和结束的时间戳
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function year() {
|
||||
$y = date('Y');
|
||||
|
||||
return [
|
||||
mktime(0, 0, 0, 1, 1, $y),
|
||||
mktime(23, 59, 59, 12, 31, $y),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回去年开始和结束的时间戳
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function lastYear() {
|
||||
$year = date('Y') - 1;
|
||||
|
||||
return [
|
||||
mktime(0, 0, 0, 1, 1, $year),
|
||||
mktime(23, 59, 59, 12, 31, $year),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取几天前零点到现在/昨日结束的时间戳
|
||||
*
|
||||
* @param int $day 天数
|
||||
* @param bool $now 返回现在或者昨天结束时间戳
|
||||
* @return array
|
||||
*/
|
||||
public static function dayToNow($day = 1, $now = true) {
|
||||
$end = time();
|
||||
if (!$now) {
|
||||
[$foo, $end] = self::yesterday();
|
||||
}
|
||||
|
||||
return [
|
||||
mktime(0, 0, 0, date('m'), date('d') - $day, date('Y')),
|
||||
$end,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回几天前的时间戳
|
||||
*
|
||||
* @param int $day
|
||||
* @return int
|
||||
*/
|
||||
public static function daysAgo($day = 1) {
|
||||
$nowTime = time();
|
||||
|
||||
return $nowTime - self::daysToSecond($day);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回几天后的时间戳
|
||||
*
|
||||
* @param int $day
|
||||
* @return int
|
||||
*/
|
||||
public static function daysAfter($day = 1) {
|
||||
$nowTime = time();
|
||||
|
||||
return $nowTime + self::daysToSecond($day);
|
||||
}
|
||||
|
||||
/**
|
||||
* 天数转换成秒数
|
||||
*
|
||||
* @param int $day
|
||||
* @return int
|
||||
*/
|
||||
public static function daysToSecond($day = 1) {
|
||||
return $day * 86400;
|
||||
}
|
||||
|
||||
/**
|
||||
* 周数转换成秒数
|
||||
*
|
||||
* @param int $week
|
||||
* @return int
|
||||
*/
|
||||
public static function weekToSecond($week = 1) {
|
||||
return self::daysToSecond() * 7 * $week;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取毫秒级别的时间戳
|
||||
*/
|
||||
public static function getMillisecond() {
|
||||
$time = explode(" ", microtime());
|
||||
$time = $time[1] . ($time[0] * 1000);
|
||||
$time2 = explode(".", $time);
|
||||
$time = $time2[0];
|
||||
|
||||
return $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相对时间
|
||||
*
|
||||
* @param int $timeStamp
|
||||
* @return string
|
||||
*/
|
||||
public static function formatRelative($timeStamp) {
|
||||
$currentTime = time();
|
||||
|
||||
// 判断传入时间戳是否早于当前时间戳
|
||||
$isEarly = $timeStamp <= $currentTime;
|
||||
|
||||
// 获取两个时间戳差值
|
||||
$diff = abs($currentTime - $timeStamp);
|
||||
|
||||
$dirStr = $isEarly ? '前' : '后';
|
||||
|
||||
if ($diff < 60) { // 一分钟之内
|
||||
$resStr = $diff . '秒' . $dirStr;
|
||||
} elseif ($diff >= 60 && $diff < 3600) { // 多于59秒,少于等于59分钟59秒
|
||||
$resStr = floor($diff / 60) . '分钟' . $dirStr;
|
||||
} elseif ($diff >= 3600 && $diff < 86400) { // 多于59分钟59秒,少于等于23小时59分钟59秒
|
||||
$resStr = floor($diff / 3600) . '小时' . $dirStr;
|
||||
} elseif ($diff >= 86400 && $diff < 2623860) { // 多于23小时59分钟59秒,少于等于29天59分钟59秒
|
||||
$resStr = floor($diff / 86400) . '天' . $dirStr;
|
||||
} elseif ($diff >= 2623860 && $diff <= 31567860 && $isEarly) { // 多于29天59分钟59秒,少于364天23小时59分钟59秒,且传入的时间戳早于当前
|
||||
$resStr = date('m-d H:i', $timeStamp);
|
||||
} else {
|
||||
$resStr = date('Y-m-d', $timeStamp);
|
||||
}
|
||||
|
||||
return $resStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 范围日期转换时间戳
|
||||
*
|
||||
* @param string $rangeDatetime
|
||||
* @param int $maxRange 最大时间间隔
|
||||
* @param string $delimiter
|
||||
* @return array
|
||||
*/
|
||||
public static function parseRange($rangeDatetime, $maxRange = 0, $delimiter = ' - ') {
|
||||
$rangeDatetime = explode($delimiter, $rangeDatetime, 2);
|
||||
$rangeDatetime[0] = strtotime($rangeDatetime[0]);
|
||||
$rangeDatetime[1] = isset($rangeDatetime[1]) ? strtotime($rangeDatetime[1]) : time();
|
||||
$rangeDatetime = [
|
||||
min($rangeDatetime[0], $rangeDatetime[1]),
|
||||
max($rangeDatetime[0], $rangeDatetime[1]),
|
||||
];
|
||||
|
||||
// 如果结束时间小于或等于开始时间 直接返回null
|
||||
// if ($rangeDatetime[1] < $rangeDatetime[0]) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// 如果大于最大时间间隔 则用结束时间减去最大时间间隔获得开始时间
|
||||
if ($maxRange > 0 && $rangeDatetime[1] - $rangeDatetime[0] > $maxRange) {
|
||||
$rangeDatetime[0] = $rangeDatetime[1] - $maxRange;
|
||||
}
|
||||
|
||||
return $rangeDatetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定时间范围内的日期数组
|
||||
* @param int $startTime
|
||||
* @param int $endTime
|
||||
* @return \Carbon\CarbonPeriod
|
||||
*/
|
||||
public static function daysUntilOfTimestamp($startTime, $endTime) {
|
||||
$startTime = Carbon::createFromTimestamp($startTime);
|
||||
$endTime = Carbon::createFromTimestamp($endTime);
|
||||
|
||||
return $startTime->daysUntil($endTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间排序
|
||||
*
|
||||
* @param array $times
|
||||
* @return array
|
||||
*/
|
||||
public static function sort($times) {
|
||||
usort($times, function ($com1, $com2) {
|
||||
$com1 = strtotime($com1);
|
||||
$com2 = strtotime($com2);
|
||||
|
||||
return $com1 < $com2 ? -1 : 1;
|
||||
});
|
||||
|
||||
return $times;
|
||||
}
|
||||
|
||||
}
|
||||
43
app/Traits/ModelTrait.php
Normal file
43
app/Traits/ModelTrait.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace App\Traits;
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
trait ModelTrait {
|
||||
|
||||
protected function casts(): array {
|
||||
return [
|
||||
'created_at' => 'datetime:Y-m-d H:i:s',
|
||||
'updated_at' => 'datetime:Y-m-d H:i:s',
|
||||
'deleted_at' => 'datetime:Y-m-d H:i:s',
|
||||
];
|
||||
}
|
||||
|
||||
protected function serializeDate($data){
|
||||
return $data->timezone('Asia/Shanghai')->format('Y-m-d H:i:s');
|
||||
}
|
||||
/**
|
||||
* 过滤移除非当前表的字段参数
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function setFilterFields(array $params) : array {
|
||||
$fields = Schema::getColumnListing($this->getTable());
|
||||
foreach ($params as $key => $param) {
|
||||
if ( !in_array($key, $fields) ) unset($params[$key]);
|
||||
}
|
||||
// 同时过滤时间戳字段【时间戳只允许自动更改,不允许手动设置】
|
||||
if ( $this->timestamps === true && isset($params[self::CREATED_AT]) ) unset($params[self::CREATED_AT]);
|
||||
if ( $this->timestamps === true && isset($params[self::UPDATED_AT]) ) unset($params[self::UPDATED_AT]);
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
@@ -101,7 +101,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'ttl' => env('JWT_TTL', 60 * 60 * 24 * 7),
|
||||
'ttl' => (int) env('JWT_TTL', 60 * 60 * 24 * 7),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@@ -178,6 +178,23 @@ export default {
|
||||
return await request.post('roles/batch-copy', params)
|
||||
},
|
||||
},
|
||||
export: {
|
||||
post: async function (params) {
|
||||
return await request.post('roles/export', params, { responseType: 'blob' })
|
||||
},
|
||||
},
|
||||
import: {
|
||||
post: async function (formData) {
|
||||
return await request.post('roles/import', formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
})
|
||||
},
|
||||
},
|
||||
downloadTemplate: {
|
||||
get: async function () {
|
||||
return await request.get('roles/download-template', { responseType: 'blob' })
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// 权限管理
|
||||
@@ -227,6 +244,23 @@ export default {
|
||||
return await request.post('permissions/batch-status', params)
|
||||
},
|
||||
},
|
||||
export: {
|
||||
post: async function (params) {
|
||||
return await request.post('permissions/export', params, { responseType: 'blob' })
|
||||
},
|
||||
},
|
||||
import: {
|
||||
post: async function (formData) {
|
||||
return await request.post('permissions/import', formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
})
|
||||
},
|
||||
},
|
||||
downloadTemplate: {
|
||||
get: async function () {
|
||||
return await request.get('permissions/download-template', { responseType: 'blob' })
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// 部门管理
|
||||
|
||||
@@ -104,34 +104,22 @@
|
||||
<!-- 新增/编辑/查看部门弹窗 -->
|
||||
<save-dialog v-if="dialog.save" ref="saveDialogRef" @success="handleSaveSuccess" @closed="dialog.save = false" />
|
||||
|
||||
<!-- 导入弹窗 -->
|
||||
<a-modal v-model:open="dialog.import" title="导入部门" :width="500" :footer="null">
|
||||
<a-space direction="vertical" style="width: 100%">
|
||||
<a-alert message="导入说明" description="请先下载模板,按照模板格式填写数据后上传。如果部门名称已存在则跳过。" type="info" show-icon />
|
||||
<a-button @click="handleDownloadTemplate" style="width: 100%">
|
||||
<template #icon><download-outlined /></template>
|
||||
下载导入模板
|
||||
</a-button>
|
||||
<a-upload
|
||||
:file-list="importFileList"
|
||||
:before-upload="handleImportUpload"
|
||||
@remove="() => importFileList = []"
|
||||
accept=".xlsx,.xls"
|
||||
:max-count="1"
|
||||
>
|
||||
<a-button>
|
||||
<template #icon><upload-outlined /></template>
|
||||
选择文件
|
||||
</a-button>
|
||||
</a-upload>
|
||||
</a-space>
|
||||
</a-modal>
|
||||
<!-- 导入部门弹窗 -->
|
||||
<sc-import v-model:open="dialog.import" title="导入部门" :api="authApi.departments.import.post"
|
||||
:template-api="authApi.departments.downloadTemplate.get" filename="部门" @success="handleImportSuccess" />
|
||||
|
||||
<!-- 导出部门弹窗 -->
|
||||
<sc-export v-model:open="dialog.export" title="导出部门" :api="handleExportApi"
|
||||
:default-filename="`部门列表_${Date.now()}`" :show-options="false" tip="导出当前选中或所有部门数据"
|
||||
@success="handleExportSuccess" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
import scTable from '@/components/scTable/index.vue'
|
||||
import scImport from '@/components/scImport/index.vue'
|
||||
import scExport from '@/components/scExport/index.vue'
|
||||
import saveDialog from './components/SaveDialog.vue'
|
||||
import authApi from '@/api/auth'
|
||||
import { useTable } from '@/hooks/useTable'
|
||||
@@ -168,15 +156,13 @@ const {
|
||||
// 对话框状态
|
||||
const dialog = reactive({
|
||||
save: false,
|
||||
import: false
|
||||
import: false,
|
||||
export: false
|
||||
})
|
||||
|
||||
// 弹窗引用
|
||||
const saveDialogRef = ref(null)
|
||||
|
||||
// 导入文件列表
|
||||
const importFileList = ref([])
|
||||
|
||||
// 行key
|
||||
const rowKey = 'id'
|
||||
|
||||
@@ -304,82 +290,47 @@ const handleBatchStatus = (status) => {
|
||||
}
|
||||
|
||||
// 导出部门
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
let ids = []
|
||||
if (selectedRows.value.length > 0) {
|
||||
ids = selectedRows.value.map(item => item.id)
|
||||
}
|
||||
const res = await authApi.departments.export.post({ ids }, { responseType: 'blob' })
|
||||
const handleExport = () => {
|
||||
dialog.export = true
|
||||
}
|
||||
|
||||
// 创建下载链接
|
||||
const url = window.URL.createObjectURL(new Blob([res]))
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', `部门列表_${new Date().getTime()}.xlsx`)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
// 导出API封装
|
||||
const handleExportApi = async () => {
|
||||
const ids = selectedRows.value.map(item => item.id)
|
||||
return await authApi.departments.export.post({ ids: ids.length > 0 ? ids : undefined })
|
||||
}
|
||||
|
||||
message.success('导出成功')
|
||||
selectedRows.value = []
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error)
|
||||
message.error('导出失败')
|
||||
}
|
||||
// 导出成功回调
|
||||
const handleExportSuccess = () => {
|
||||
selectedRows.value = []
|
||||
}
|
||||
|
||||
// 导入部门
|
||||
const handleImport = () => {
|
||||
importFileList.value = []
|
||||
dialog.import = true
|
||||
}
|
||||
|
||||
// 上传导入文件
|
||||
const handleImportUpload = async (file) => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
|
||||
try {
|
||||
importFileList.value = [{ uid: file.uid, name: file.name, status: 'uploading' }]
|
||||
const res = await authApi.departments.import.post(formData)
|
||||
|
||||
importFileList.value = []
|
||||
dialog.import = false
|
||||
|
||||
if (res.code === 200) {
|
||||
message.success(res.message || '导入成功')
|
||||
refreshTable()
|
||||
} else {
|
||||
message.error(res.message || '导入失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('导入失败:', error)
|
||||
importFileList.value = []
|
||||
message.error(error.response?.data?.message || '导入失败')
|
||||
}
|
||||
|
||||
return false // 阻止自动上传
|
||||
// 导入成功回调
|
||||
const handleImportSuccess = () => {
|
||||
refreshTable()
|
||||
}
|
||||
|
||||
// 下载导入模板
|
||||
// 下载模板
|
||||
const handleDownloadTemplate = async () => {
|
||||
try {
|
||||
const res = await authApi.departments.downloadTemplate.get({ responseType: 'blob' })
|
||||
|
||||
// 创建下载链接
|
||||
const url = window.URL.createObjectURL(new Blob([res]))
|
||||
const blob = await authApi.departments.downloadTemplate.get()
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.setAttribute('download', '部门导入模板.xlsx')
|
||||
link.download = '部门导入模板.xlsx'
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
message.success('下载成功')
|
||||
} catch (error) {
|
||||
console.error('下载模板失败:', error)
|
||||
message.error('下载模板失败')
|
||||
message.error('下载失败')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,14 +12,12 @@
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a-space size="small">
|
||||
<a-tooltip title="展开全部">
|
||||
<a-button type="text" size="small" @click="handleExpandAll">
|
||||
<template #icon><UnorderedListOutlined /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="折叠全部">
|
||||
<a-button type="text" size="small" @click="handleCollapseAll">
|
||||
<template #icon><OrderedListOutlined /></template>
|
||||
<a-tooltip :title="isAllExpanded ? '折叠全部' : '展开全部'">
|
||||
<a-button type="text" size="small" @click="handleToggleExpand">
|
||||
<template #icon>
|
||||
<UnorderedListOutlined v-if="!isAllExpanded" />
|
||||
<OrderedListOutlined v-else />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="添加根权限">
|
||||
@@ -43,7 +41,6 @@
|
||||
checkable
|
||||
:check-strictly="false"
|
||||
:expand-on-click-node="false"
|
||||
block-node
|
||||
@select="onMenuSelect"
|
||||
@check="onMenuCheck">
|
||||
<template #icon="{ dataRef }">
|
||||
@@ -81,6 +78,25 @@
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
批量删除 ({{ checkedMenuKeys.length }})
|
||||
</a-button>
|
||||
<a-dropdown>
|
||||
<a-button type="link" size="small">
|
||||
<template #icon><MoreOutlined /></template>
|
||||
更多
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item @click="handleImport">
|
||||
<ImportOutlined />导入权限
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="handleExport">
|
||||
<ExportOutlined />导出权限
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="handleDownloadTemplate">
|
||||
<DownloadOutlined />下载模板
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-button type="link" size="small" @click="handleRefresh">
|
||||
<template #icon><ReloadOutlined /></template>
|
||||
刷新
|
||||
@@ -96,6 +112,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 导入权限弹窗 -->
|
||||
<sc-import v-model:open="dialog.import" title="导入权限" :api="authApi.permissions.import.post"
|
||||
:template-api="authApi.permissions.downloadTemplate.get" filename="权限" @success="handleImportSuccess" />
|
||||
|
||||
<!-- 导出权限弹窗 -->
|
||||
<sc-export v-model:open="dialog.export" title="导出权限" :api="handleExportApi"
|
||||
:default-filename="`权限列表_${Date.now()}`" :show-options="false" tip="导出当前选中或所有权限数据"
|
||||
@success="handleExportSuccess" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -112,9 +137,16 @@ import {
|
||||
UnorderedListOutlined,
|
||||
OrderedListOutlined,
|
||||
DeleteOutlined,
|
||||
StopOutlined
|
||||
StopOutlined,
|
||||
ImportOutlined,
|
||||
ExportOutlined,
|
||||
DownloadOutlined,
|
||||
MoreOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import { computed } from 'vue'
|
||||
import saveForm from './components/SaveForm.vue'
|
||||
import scImport from '@/components/scImport/index.vue'
|
||||
import scExport from '@/components/scExport/index.vue'
|
||||
import authApi from '@/api/auth'
|
||||
|
||||
defineOptions({
|
||||
@@ -137,9 +169,30 @@ const parentId = ref(null)
|
||||
const loading = ref(false)
|
||||
const detailLoading = ref(false)
|
||||
|
||||
// 对话框状态
|
||||
const dialog = ref({
|
||||
import: false,
|
||||
export: false
|
||||
})
|
||||
|
||||
// 树引用
|
||||
const treeRef = ref()
|
||||
|
||||
// 是否全部展开
|
||||
const isAllExpanded = computed(() => {
|
||||
const allKeys = getAllKeys(filteredMenuTree.value)
|
||||
return allKeys.length > 0 && expandedKeys.value.length === allKeys.length
|
||||
})
|
||||
|
||||
// 切换展开/折叠
|
||||
const handleToggleExpand = () => {
|
||||
if (isAllExpanded.value) {
|
||||
handleCollapseAll()
|
||||
} else {
|
||||
handleExpandAll()
|
||||
}
|
||||
}
|
||||
|
||||
// 加载权限树
|
||||
const loadMenuTree = async () => {
|
||||
try {
|
||||
@@ -363,6 +416,52 @@ const handleSaveSuccess = async () => {
|
||||
message.success('保存成功')
|
||||
}
|
||||
|
||||
// 导出权限
|
||||
const handleExport = () => {
|
||||
dialog.value.export = true
|
||||
}
|
||||
|
||||
// 导出API封装
|
||||
const handleExportApi = async () => {
|
||||
return await authApi.permissions.export.post({
|
||||
ids: checkedMenuKeys.value.length > 0 ? checkedMenuKeys.value : undefined
|
||||
})
|
||||
}
|
||||
|
||||
// 导出成功回调
|
||||
const handleExportSuccess = () => {
|
||||
checkedMenuKeys.value = []
|
||||
}
|
||||
|
||||
// 导入权限
|
||||
const handleImport = () => {
|
||||
dialog.value.import = true
|
||||
}
|
||||
|
||||
// 导入成功回调
|
||||
const handleImportSuccess = () => {
|
||||
loadMenuTree()
|
||||
}
|
||||
|
||||
// 下载模板
|
||||
const handleDownloadTemplate = async () => {
|
||||
try {
|
||||
const blob = await authApi.permissions.downloadTemplate.get()
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = '权限导入模板.xlsx'
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
message.success('下载成功')
|
||||
} catch (error) {
|
||||
console.error('下载模板失败:', error)
|
||||
message.error('下载失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
loadMenuTree()
|
||||
@@ -401,8 +500,11 @@ defineExpose({
|
||||
background: transparent;
|
||||
|
||||
.ant-tree-node-content-wrapper {
|
||||
padding: 4px 0;
|
||||
padding: 2px 4px;
|
||||
transition: background-color 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 28px;
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f5f5;
|
||||
@@ -411,22 +513,37 @@ defineExpose({
|
||||
|
||||
.ant-tree-switcher {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.ant-tree-iconEle {
|
||||
margin-right: 6px;
|
||||
margin-right: 4px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ant-tree-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-content {
|
||||
display: inline-flex;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
max-width: 100%;
|
||||
|
||||
.tree-node-title {
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
color: #262626;
|
||||
flex-shrink: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.tree-node-code {
|
||||
@@ -434,14 +551,17 @@ defineExpose({
|
||||
font-size: 11px;
|
||||
background: #f0f0f0;
|
||||
border: none;
|
||||
padding: 1px 6px;
|
||||
padding: 0 4px;
|
||||
border-radius: 2px;
|
||||
color: #595959;
|
||||
flex-shrink: 0;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.tree-node-disabled {
|
||||
color: #ff4d4f;
|
||||
margin-left: 4px;
|
||||
font-size: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,25 @@
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-dropdown>
|
||||
<a-button>
|
||||
更多
|
||||
<DownOutlined />
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item @click="handleImport">
|
||||
<ImportOutlined />导入角色
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="handleExport">
|
||||
<ExportOutlined />导出角色
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="handleDownloadTemplate">
|
||||
<DownloadOutlined />下载模板
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-button type="primary" @click="handleAdd">
|
||||
<template #icon><PlusOutlined /></template>
|
||||
新增
|
||||
@@ -72,6 +91,15 @@
|
||||
<!-- 权限设置弹窗 -->
|
||||
<permission-dialog v-if="dialog.permission" ref="permissionDialogRef" @success="permissionSuccess"
|
||||
@closed="dialog.permission = false" />
|
||||
|
||||
<!-- 导入角色弹窗 -->
|
||||
<sc-import v-model:open="dialog.import" title="导入角色" :api="authApi.roles.import.post"
|
||||
:template-api="authApi.roles.downloadTemplate.get" filename="角色" @success="handleImportSuccess" />
|
||||
|
||||
<!-- 导出角色弹窗 -->
|
||||
<sc-export v-model:open="dialog.export" title="导出角色" :api="handleExportApi"
|
||||
:default-filename="`角色列表_${Date.now()}`" :show-options="false" tip="导出当前选中或所有角色数据"
|
||||
@success="handleExportSuccess" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -84,9 +112,14 @@ import {
|
||||
DownOutlined,
|
||||
CheckCircleOutlined,
|
||||
CopyOutlined,
|
||||
DeleteOutlined
|
||||
DeleteOutlined,
|
||||
ImportOutlined,
|
||||
ExportOutlined,
|
||||
DownloadOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import scTable from '@/components/scTable/index.vue'
|
||||
import scImport from '@/components/scImport/index.vue'
|
||||
import scExport from '@/components/scExport/index.vue'
|
||||
import saveDialog from './components/SaveDialog.vue'
|
||||
import permissionDialog from './components/PermissionDialog.vue'
|
||||
import authApi from '@/api/auth'
|
||||
@@ -125,7 +158,9 @@ const {
|
||||
// 对话框状态
|
||||
const dialog = reactive({
|
||||
save: false,
|
||||
permission: false
|
||||
permission: false,
|
||||
import: false,
|
||||
export: false
|
||||
})
|
||||
|
||||
// 弹窗引用
|
||||
@@ -296,4 +331,49 @@ const handleSaveSuccess = () => {
|
||||
const permissionSuccess = () => {
|
||||
refreshTable()
|
||||
}
|
||||
|
||||
// 导出角色
|
||||
const handleExport = async () => {
|
||||
dialog.export = true
|
||||
}
|
||||
|
||||
// 导出API封装
|
||||
const handleExportApi = async () => {
|
||||
const ids = selectedRows.value.map(item => item.id)
|
||||
return await authApi.roles.export.post({ ids: ids.length > 0 ? ids : undefined })
|
||||
}
|
||||
|
||||
// 导出成功回调
|
||||
const handleExportSuccess = () => {
|
||||
selectedRows.value = []
|
||||
}
|
||||
|
||||
// 导入角色
|
||||
const handleImport = () => {
|
||||
dialog.import = true
|
||||
}
|
||||
|
||||
// 导入成功回调
|
||||
const handleImportSuccess = () => {
|
||||
refreshTable()
|
||||
}
|
||||
|
||||
// 下载模板
|
||||
const handleDownloadTemplate = async () => {
|
||||
try {
|
||||
const blob = await authApi.roles.downloadTemplate.get()
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = '角色导入模板.xlsx'
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
message.success('下载成功')
|
||||
} catch (error) {
|
||||
console.error('下载模板失败:', error)
|
||||
message.error('下载失败')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -126,6 +126,15 @@
|
||||
|
||||
<!-- 角色设置弹窗 -->
|
||||
<role-dialog v-if="dialog.role" ref="roleDialogRef" @success="handleRoleSuccess" @closed="dialog.role = false" />
|
||||
|
||||
<!-- 导入用户弹窗 -->
|
||||
<sc-import v-model:open="dialog.import" title="导入用户" :api="authApi.users.import.post"
|
||||
:template-api="authApi.users.downloadTemplate.get" filename="用户" @success="handleImportSuccess" />
|
||||
|
||||
<!-- 导出用户弹窗 -->
|
||||
<sc-export v-model:open="dialog.export" title="导出用户" :api="handleExportApi"
|
||||
:default-filename="`用户列表_${Date.now()}`" :show-options="false" tip="导出当前选中或所有用户数据"
|
||||
@success="handleExportSuccess" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -146,6 +155,8 @@ import {
|
||||
UserOutlined
|
||||
} from '@ant-design/icons-vue'
|
||||
import scTable from '@/components/scTable/index.vue'
|
||||
import scImport from '@/components/scImport/index.vue'
|
||||
import scExport from '@/components/scExport/index.vue'
|
||||
import saveDialog from './components/SaveDialog.vue'
|
||||
import roleDialog from './components/RoleDialog.vue'
|
||||
import authApi from '@/api/auth'
|
||||
@@ -188,7 +199,9 @@ const {
|
||||
// 对话框状态
|
||||
const dialog = reactive({
|
||||
save: false,
|
||||
role: false
|
||||
role: false,
|
||||
import: false,
|
||||
export: false
|
||||
})
|
||||
|
||||
// 弹窗引用
|
||||
@@ -368,53 +381,50 @@ const handleBatchRoles = () => {
|
||||
}
|
||||
|
||||
// 导出数据
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
const ids = selectedRows.value.map(item => item.id)
|
||||
const res = await authApi.users.export.post({ ids: ids.length > 0 ? ids : undefined })
|
||||
if (res) {
|
||||
// 创建下载链接
|
||||
const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = `users_${Date.now()}.xlsx`
|
||||
link.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
message.success('导出成功')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error)
|
||||
message.error('导出失败')
|
||||
}
|
||||
const handleExport = () => {
|
||||
dialog.export = true
|
||||
}
|
||||
|
||||
// 导出API封装
|
||||
const handleExportApi = async () => {
|
||||
const ids = selectedRows.value.map(item => item.id)
|
||||
return await authApi.users.export.post({ ids: ids.length > 0 ? ids : undefined })
|
||||
}
|
||||
|
||||
// 导出成功回调
|
||||
const handleExportSuccess = () => {
|
||||
selectedRows.value = []
|
||||
}
|
||||
|
||||
// 导入用户
|
||||
const handleImport = () => {
|
||||
dialog.import = true
|
||||
}
|
||||
|
||||
// 导入成功回调
|
||||
const handleImportSuccess = () => {
|
||||
refreshTable()
|
||||
}
|
||||
|
||||
// 下载模板
|
||||
const handleDownloadTemplate = async () => {
|
||||
try {
|
||||
const res = await authApi.users.downloadTemplate.get()
|
||||
if (res) {
|
||||
const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = 'user_template.xlsx'
|
||||
link.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
message.success('下载成功')
|
||||
}
|
||||
const blob = await authApi.users.downloadTemplate.get()
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = '用户导入模板.xlsx'
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
message.success('下载成功')
|
||||
} catch (error) {
|
||||
console.error('下载模板失败:', error)
|
||||
message.error('下载失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 导入用户
|
||||
const handleImport = () => {
|
||||
// TODO: 实现导入弹窗
|
||||
message.info('导入功能开发中...')
|
||||
}
|
||||
|
||||
// 重置密码
|
||||
const handleResetPassword = (record) => {
|
||||
Modal.confirm({
|
||||
|
||||
Reference in New Issue
Block a user