更新完善字典相关功能
This commit is contained in:
@@ -140,6 +140,16 @@ public function getItemsList(Request $request)
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAllItems()
|
||||||
|
{
|
||||||
|
$items = $this->dictionaryService->getAllItems();
|
||||||
|
return response()->json([
|
||||||
|
'code' => 200,
|
||||||
|
'message' => 'success',
|
||||||
|
'data' => $items
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function storeItem(Request $request)
|
public function storeItem(Request $request)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -25,6 +25,28 @@ public function index()
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有字典数据(包含字典项)
|
||||||
|
* 用于前端登录后缓存所有字典数据
|
||||||
|
*/
|
||||||
|
public function all()
|
||||||
|
{
|
||||||
|
$dictionaries = $this->dictionaryService->getAll();
|
||||||
|
|
||||||
|
// 为每个字典添加 items 字段
|
||||||
|
$result = array_map(function($dictionary) {
|
||||||
|
$items = $this->dictionaryService->getItemsByCode($dictionary['code']);
|
||||||
|
$dictionary['items'] = $items;
|
||||||
|
return $dictionary;
|
||||||
|
}, $dictionaries);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'code' => 200,
|
||||||
|
'message' => 'success',
|
||||||
|
'data' => $result
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function getByCode(Request $request)
|
public function getByCode(Request $request)
|
||||||
{
|
{
|
||||||
$code = $request->input('code');
|
$code = $request->input('code');
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ private function buildMenuTree($permissions, $parentId = 0): array
|
|||||||
'path' => $permission->path,
|
'path' => $permission->path,
|
||||||
'name' => $permission->name,
|
'name' => $permission->name,
|
||||||
'title' => $permission->title,
|
'title' => $permission->title,
|
||||||
'meta' => $permission->meta ? json_decode($permission->meta, true) : [],
|
'meta' => $permission->meta ?: [],
|
||||||
];
|
];
|
||||||
|
|
||||||
// 添加组件路径
|
// 添加组件路径
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public function getAll(): array
|
|||||||
|
|
||||||
if ($dictionaries === null) {
|
if ($dictionaries === null) {
|
||||||
$dictionaries = Dictionary::where('status', true)
|
$dictionaries = Dictionary::where('status', true)
|
||||||
|
->with(['activeItems'])
|
||||||
->orderBy('sort')
|
->orderBy('sort')
|
||||||
->get()
|
->get()
|
||||||
->toArray();
|
->toArray();
|
||||||
@@ -52,7 +53,7 @@ public function getAll(): array
|
|||||||
return $dictionaries;
|
return $dictionaries;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getById(int $id): ?Dictionary
|
public function getById(int $id): ?array
|
||||||
{
|
{
|
||||||
$cacheKey = 'system:dictionaries:' . $id;
|
$cacheKey = 'system:dictionaries:' . $id;
|
||||||
$dictionary = Cache::get($cacheKey);
|
$dictionary = Cache::get($cacheKey);
|
||||||
@@ -60,22 +61,26 @@ public function getById(int $id): ?Dictionary
|
|||||||
if ($dictionary === null) {
|
if ($dictionary === null) {
|
||||||
$dictionary = Dictionary::with('items')->find($id);
|
$dictionary = Dictionary::with('items')->find($id);
|
||||||
if ($dictionary) {
|
if ($dictionary) {
|
||||||
Cache::put($cacheKey, $dictionary->toArray(), 3600);
|
$dictionary = $dictionary->toArray();
|
||||||
|
Cache::put($cacheKey, $dictionary, 3600);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $dictionary ? $dictionary : null;
|
return $dictionary ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getByCode(string $code): ?Dictionary
|
public function getByCode(string $code): ?array
|
||||||
{
|
{
|
||||||
$cacheKey = 'system:dictionaries:code:' . $code;
|
$cacheKey = 'system:dictionaries:code:' . $code;
|
||||||
$dictionary = Cache::get($cacheKey);
|
$dictionary = Cache::get($cacheKey);
|
||||||
|
|
||||||
if ($dictionary === null) {
|
if ($dictionary === null) {
|
||||||
$dictionary = Dictionary::where('code', $code)->first();
|
$dictionaryModel = Dictionary::where('code', $code)->first();
|
||||||
if ($dictionary) {
|
if ($dictionaryModel) {
|
||||||
Cache::put($cacheKey, $dictionary->toArray(), 3600);
|
$dictionary = $dictionaryModel->toArray();
|
||||||
|
Cache::put($cacheKey, $dictionary, 3600);
|
||||||
|
} else {
|
||||||
|
$dictionary = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,4 +274,36 @@ public function batchUpdateItemsStatus(array $ids, bool $status): bool
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有字典项(按字典分类)
|
||||||
|
* @return array 按字典code分类的字典项数据
|
||||||
|
*/
|
||||||
|
public function getAllItems(): array
|
||||||
|
{
|
||||||
|
$cacheKey = 'system:dictionary-items:all';
|
||||||
|
$allItems = Cache::get($cacheKey);
|
||||||
|
|
||||||
|
if ($allItems === null) {
|
||||||
|
// 获取所有启用的字典
|
||||||
|
$dictionaries = Dictionary::where('status', true)
|
||||||
|
->orderBy('sort')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
foreach ($dictionaries as $dictionary) {
|
||||||
|
$result[] = [
|
||||||
|
'code' => $dictionary->code,
|
||||||
|
'name' => $dictionary->name,
|
||||||
|
'description' => $dictionary->description,
|
||||||
|
'items' => $dictionary->activeItems->toArray()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$allItems = $result;
|
||||||
|
Cache::put($cacheKey, $allItems, 3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $allItems;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default {
|
|||||||
post: async function (file) {
|
post: async function (file) {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('file', file)
|
formData.append('file', file)
|
||||||
return await request.post('upload', formData, {
|
return await request.post('system/upload', formData, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' }
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -38,64 +38,64 @@ export default {
|
|||||||
users: {
|
users: {
|
||||||
list: {
|
list: {
|
||||||
get: async function (params) {
|
get: async function (params) {
|
||||||
return await request.get('users', { params })
|
return await request.get('auth/users', { params })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
detail: {
|
detail: {
|
||||||
get: async function (id) {
|
get: async function (id) {
|
||||||
return await request.get(`users/${id}`)
|
return await request.get(`auth/users/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
add: {
|
add: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('users', params)
|
return await request.post('auth/users', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
put: async function (id, params) {
|
put: async function (id, params) {
|
||||||
return await request.put(`users/${id}`, params)
|
return await request.put(`auth/users/${id}`, params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
delete: async function (id) {
|
delete: async function (id) {
|
||||||
return await request.delete(`users/${id}`)
|
return await request.delete(`auth/users/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchDelete: {
|
batchDelete: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('users/batch-delete', params)
|
return await request.post('auth/users/batch-delete', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchStatus: {
|
batchStatus: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('users/batch-status', params)
|
return await request.post('auth/users/batch-status', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchDepartment: {
|
batchDepartment: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('users/batch-department', params)
|
return await request.post('auth/users/batch-department', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchRoles: {
|
batchRoles: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('users/batch-roles', params)
|
return await request.post('auth/users/batch-roles', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
export: {
|
export: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('users/export', params, { responseType: 'blob' })
|
return await request.post('auth/users/export', params, { responseType: 'blob' })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
import: {
|
import: {
|
||||||
post: async function (formData) {
|
post: async function (formData) {
|
||||||
return await request.post('users/import', formData, {
|
return await request.post('auth/users/import', formData, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' }
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
downloadTemplate: {
|
downloadTemplate: {
|
||||||
get: async function () {
|
get: async function () {
|
||||||
return await request.get('users/download-template', { responseType: 'blob' })
|
return await request.get('auth/users/download-template', { responseType: 'blob' })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -104,27 +104,27 @@ export default {
|
|||||||
onlineUsers: {
|
onlineUsers: {
|
||||||
count: {
|
count: {
|
||||||
get: async function () {
|
get: async function () {
|
||||||
return await request.get('online-users/count')
|
return await request.get('auth/online-users/count')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
get: async function (params) {
|
get: async function (params) {
|
||||||
return await request.get('online-users', { params })
|
return await request.get('auth/online-users', { params })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
sessions: {
|
sessions: {
|
||||||
get: async function (userId) {
|
get: async function (userId) {
|
||||||
return await request.get(`online-users/${userId}/sessions`)
|
return await request.get(`auth/online-users/${userId}/sessions`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
offline: {
|
offline: {
|
||||||
post: async function (userId, params) {
|
post: async function (userId, params) {
|
||||||
return await request.post(`online-users/${userId}/offline`, params)
|
return await request.post(`auth/online-users/${userId}/offline`, params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
offlineAll: {
|
offlineAll: {
|
||||||
post: async function (userId) {
|
post: async function (userId) {
|
||||||
return await request.post(`online-users/${userId}/offline-all`)
|
return await request.post(`auth/online-users/${userId}/offline-all`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -133,77 +133,77 @@ export default {
|
|||||||
roles: {
|
roles: {
|
||||||
list: {
|
list: {
|
||||||
get: async function (params) {
|
get: async function (params) {
|
||||||
return await request.get('roles', { params })
|
return await request.get('auth/roles', { params })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
all: {
|
all: {
|
||||||
get: async function () {
|
get: async function () {
|
||||||
return await request.get('roles/all')
|
return await request.get('auth/roles/all')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
detail: {
|
detail: {
|
||||||
get: async function (id) {
|
get: async function (id) {
|
||||||
return await request.get(`roles/${id}`)
|
return await request.get(`auth/roles/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
add: {
|
add: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('roles', params)
|
return await request.post('auth/roles', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
put: async function (id, params) {
|
put: async function (id, params) {
|
||||||
return await request.put(`roles/${id}`, params)
|
return await request.put(`auth/roles/${id}`, params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
delete: async function (id) {
|
delete: async function (id) {
|
||||||
return await request.delete(`roles/${id}`)
|
return await request.delete(`auth/roles/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchDelete: {
|
batchDelete: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('roles/batch-delete', params)
|
return await request.post('auth/roles/batch-delete', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchStatus: {
|
batchStatus: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('roles/batch-status', params)
|
return await request.post('auth/roles/batch-status', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
permissions: {
|
permissions: {
|
||||||
get: async function (id) {
|
get: async function (id) {
|
||||||
return await request.get(`roles/${id}/permissions`)
|
return await request.get(`auth/roles/${id}/permissions`)
|
||||||
},
|
},
|
||||||
post: async function (id, params) {
|
post: async function (id, params) {
|
||||||
return await request.post(`roles/${id}/permissions`, params)
|
return await request.post(`auth/roles/${id}/permissions`, params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
copy: {
|
copy: {
|
||||||
post: async function (id, params) {
|
post: async function (id, params) {
|
||||||
return await request.post(`roles/${id}/copy`, params)
|
return await request.post(`auth/roles/${id}/copy`, params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchCopy: {
|
batchCopy: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('roles/batch-copy', params)
|
return await request.post('auth/roles/batch-copy', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
export: {
|
export: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('roles/export', params, { responseType: 'blob' })
|
return await request.post('auth/roles/export', params, { responseType: 'blob' })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
import: {
|
import: {
|
||||||
post: async function (formData) {
|
post: async function (formData) {
|
||||||
return await request.post('roles/import', formData, {
|
return await request.post('auth/roles/import', formData, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' }
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
downloadTemplate: {
|
downloadTemplate: {
|
||||||
get: async function () {
|
get: async function () {
|
||||||
return await request.get('roles/download-template', { responseType: 'blob' })
|
return await request.get('auth/roles/download-template', { responseType: 'blob' })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -212,64 +212,64 @@ export default {
|
|||||||
permissions: {
|
permissions: {
|
||||||
list: {
|
list: {
|
||||||
get: async function (params) {
|
get: async function (params) {
|
||||||
return await request.get('permissions', { params })
|
return await request.get('auth/permissions', { params })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tree: {
|
tree: {
|
||||||
get: async function () {
|
get: async function () {
|
||||||
return await request.get('permissions/tree')
|
return await request.get('auth/permissions/tree')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
menu: {
|
menu: {
|
||||||
get: async function () {
|
get: async function () {
|
||||||
return await request.get('permissions/menu')
|
return await request.get('auth/permissions/menu')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
detail: {
|
detail: {
|
||||||
get: async function (id) {
|
get: async function (id) {
|
||||||
return await request.get(`permissions/${id}`)
|
return await request.get(`auth/permissions/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
add: {
|
add: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('permissions', params)
|
return await request.post('auth/permissions', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
put: async function (id, params) {
|
put: async function (id, params) {
|
||||||
return await request.put(`permissions/${id}`, params)
|
return await request.put(`auth/permissions/${id}`, params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
delete: async function (id) {
|
delete: async function (id) {
|
||||||
return await request.delete(`permissions/${id}`)
|
return await request.delete(`auth/permissions/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchDelete: {
|
batchDelete: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('permissions/batch-delete', params)
|
return await request.post('auth/permissions/batch-delete', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchStatus: {
|
batchStatus: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('permissions/batch-status', params)
|
return await request.post('auth/permissions/batch-status', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
export: {
|
export: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('permissions/export', params, { responseType: 'blob' })
|
return await request.post('auth/permissions/export', params, { responseType: 'blob' })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
import: {
|
import: {
|
||||||
post: async function (formData) {
|
post: async function (formData) {
|
||||||
return await request.post('permissions/import', formData, {
|
return await request.post('auth/permissions/import', formData, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' }
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
downloadTemplate: {
|
downloadTemplate: {
|
||||||
get: async function () {
|
get: async function () {
|
||||||
return await request.get('permissions/download-template', { responseType: 'blob' })
|
return await request.get('auth/permissions/download-template', { responseType: 'blob' })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -278,64 +278,64 @@ export default {
|
|||||||
departments: {
|
departments: {
|
||||||
list: {
|
list: {
|
||||||
get: async function (params) {
|
get: async function (params) {
|
||||||
return await request.get('departments', { params })
|
return await request.get('auth/departments', { params })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tree: {
|
tree: {
|
||||||
get: async function (params) {
|
get: async function (params) {
|
||||||
return await request.get('departments/tree', { params })
|
return await request.get('auth/departments/tree', { params })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
all: {
|
all: {
|
||||||
get: async function () {
|
get: async function () {
|
||||||
return await request.get('departments/all')
|
return await request.get('auth/departments/all')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
detail: {
|
detail: {
|
||||||
get: async function (id) {
|
get: async function (id) {
|
||||||
return await request.get(`departments/${id}`)
|
return await request.get(`auth/departments/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
add: {
|
add: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('departments', params)
|
return await request.post('auth/departments', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
put: async function (id, params) {
|
put: async function (id, params) {
|
||||||
return await request.put(`departments/${id}`, params)
|
return await request.put(`auth/departments/${id}`, params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
delete: async function (id) {
|
delete: async function (id) {
|
||||||
return await request.delete(`departments/${id}`)
|
return await request.delete(`auth/departments/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchDelete: {
|
batchDelete: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('departments/batch-delete', params)
|
return await request.post('auth/departments/batch-delete', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchStatus: {
|
batchStatus: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('departments/batch-status', params)
|
return await request.post('auth/departments/batch-status', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
export: {
|
export: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('departments/export', params, { responseType: 'blob' })
|
return await request.post('auth/departments/export', params, { responseType: 'blob' })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
import: {
|
import: {
|
||||||
post: async function (formData) {
|
post: async function (formData) {
|
||||||
return await request.post('departments/import', formData, {
|
return await request.post('auth/departments/import', formData, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' }
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
downloadTemplate: {
|
downloadTemplate: {
|
||||||
get: async function () {
|
get: async function () {
|
||||||
return await request.get('departments/download-template', { responseType: 'blob' })
|
return await request.get('auth/departments/download-template', { responseType: 'blob' })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,47 +5,47 @@ export default {
|
|||||||
configs: {
|
configs: {
|
||||||
list: {
|
list: {
|
||||||
get: async function (params) {
|
get: async function (params) {
|
||||||
return await request.get('configs', { params })
|
return await request.get('system/configs', { params })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
groups: {
|
groups: {
|
||||||
get: async function () {
|
get: async function () {
|
||||||
return await request.get('configs/groups')
|
return await request.get('system/configs/groups')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
all: {
|
all: {
|
||||||
get: async function (params) {
|
get: async function (params) {
|
||||||
return await request.get('configs/all', { params })
|
return await request.get('system/configs/all', { params })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
detail: {
|
detail: {
|
||||||
get: async function (id) {
|
get: async function (id) {
|
||||||
return await request.get(`configs/${id}`)
|
return await request.get(`system/configs/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
add: {
|
add: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('configs', params)
|
return await request.post('system/configs', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
put: async function (id, params) {
|
put: async function (id, params) {
|
||||||
return await request.put(`configs/${id}`, params)
|
return await request.put(`system/configs/${id}`, params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
delete: async function (id) {
|
delete: async function (id) {
|
||||||
return await request.delete(`configs/${id}`)
|
return await request.delete(`system/configs/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchDelete: {
|
batchDelete: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('configs/batch-delete', params)
|
return await request.post('system/configs/batch-delete', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchStatus: {
|
batchStatus: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('configs/batch-status', params)
|
return await request.post('system/configs/batch-status', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -54,32 +54,32 @@ export default {
|
|||||||
logs: {
|
logs: {
|
||||||
list: {
|
list: {
|
||||||
get: async function (params) {
|
get: async function (params) {
|
||||||
return await request.get('logs', { params })
|
return await request.get('system/logs', { params })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
detail: {
|
detail: {
|
||||||
get: async function (id) {
|
get: async function (id) {
|
||||||
return await request.get(`logs/${id}`)
|
return await request.get(`system/logs/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
delete: async function (id) {
|
delete: async function (id) {
|
||||||
return await request.delete(`logs/${id}`)
|
return await request.delete(`system/logs/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchDelete: {
|
batchDelete: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('logs/batch-delete', params)
|
return await request.post('system/logs/batch-delete', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
clear: {
|
clear: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('logs/clear', params)
|
return await request.post('system/logs/clear', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
export: {
|
export: {
|
||||||
get: async function (params) {
|
get: async function (params) {
|
||||||
return await request.get('logs/export', {
|
return await request.get('system/logs/export', {
|
||||||
params,
|
params,
|
||||||
responseType: 'blob'
|
responseType: 'blob'
|
||||||
})
|
})
|
||||||
@@ -87,90 +87,102 @@ export default {
|
|||||||
},
|
},
|
||||||
statistics: {
|
statistics: {
|
||||||
get: async function (params) {
|
get: async function (params) {
|
||||||
return await request.get('logs/statistics', { params })
|
return await request.get('system/logs/statistics', { params })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// 数据字典管理
|
// 数据字典管理
|
||||||
dictionaries: {
|
dictionaries: {
|
||||||
list: {
|
list: {
|
||||||
get: async function (params) {
|
get: async function (params) {
|
||||||
return await request.get('dictionaries', { params })
|
return await request.get('system/dictionaries', { params })
|
||||||
|
},
|
||||||
|
},
|
||||||
|
all: {
|
||||||
|
get: async function () {
|
||||||
|
return await request.get('system/dictionaries/all')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
detail: {
|
||||||
|
get: async function (id) {
|
||||||
|
return await request.get(`system/dictionaries/${id}`)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
add: {
|
||||||
|
post: async function (params) {
|
||||||
|
return await request.post('system/dictionaries', params)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
put: async function (id, params) {
|
||||||
|
return await request.put(`system/dictionaries/${id}`, params)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
delete: async function (id) {
|
||||||
|
return await request.delete(`system/dictionaries/${id}`)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
batchDelete: {
|
||||||
|
post: async function (params) {
|
||||||
|
return await request.post('system/dictionaries/batch-delete', params)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
batchStatus: {
|
||||||
|
post: async function (params) {
|
||||||
|
return await request.post('system/dictionaries/batch-status', params)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
all: {
|
||||||
|
get: async function (code) {
|
||||||
|
return await request.get(`system/dictionaries/code`, { params: { code } })
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
all: {
|
|
||||||
get: async function () {
|
|
||||||
return await request.get('dictionaries/all')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
detail: {
|
|
||||||
get: async function (id) {
|
|
||||||
return await request.get(`dictionaries/${id}`)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
add: {
|
|
||||||
post: async function (params) {
|
|
||||||
return await request.post('dictionaries', params)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
edit: {
|
|
||||||
put: async function (id, params) {
|
|
||||||
return await request.put(`dictionaries/${id}`, params)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
delete: {
|
|
||||||
delete: async function (id) {
|
|
||||||
return await request.delete(`dictionaries/${id}`)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
batchDelete: {
|
|
||||||
post: async function (params) {
|
|
||||||
return await request.post('dictionaries/batch-delete', params)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
batchStatus: {
|
|
||||||
post: async function (params) {
|
|
||||||
return await request.post('dictionaries/batch-status', params)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// 数据字典项管理
|
// 数据字典项管理
|
||||||
dictionaryItems: {
|
dictionaryItems: {
|
||||||
list: {
|
list: {
|
||||||
get: async function (params) {
|
get: async function (params) {
|
||||||
return await request.get('dictionary-items', { params })
|
return await request.get('system/dictionary-items', { params })
|
||||||
|
},
|
||||||
|
},
|
||||||
|
all: {
|
||||||
|
get: async function () {
|
||||||
|
return await request.get('system/dictionary-items/all')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
detail: {
|
detail: {
|
||||||
get: async function (id) {
|
get: async function (id) {
|
||||||
return await request.get(`dictionary-items/${id}`)
|
return await request.get(`system/dictionary-items/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
add: {
|
add: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('dictionary-items', params)
|
return await request.post('system/dictionary-items', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
put: async function (id, params) {
|
put: async function (id, params) {
|
||||||
return await request.put(`dictionary-items/${id}`, params)
|
return await request.put(`system/dictionary-items/${id}`, params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
delete: async function (id) {
|
delete: async function (id) {
|
||||||
return await request.delete(`dictionary-items/${id}`)
|
return await request.delete(`system/dictionary-items/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchDelete: {
|
batchDelete: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('dictionary-items/batch-delete', params)
|
return await request.post('system/dictionary-items/batch-delete', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchStatus: {
|
batchStatus: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('dictionary-items/batch-status', params)
|
return await request.post('system/dictionary-items/batch-status', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -179,52 +191,52 @@ export default {
|
|||||||
tasks: {
|
tasks: {
|
||||||
list: {
|
list: {
|
||||||
get: async function (params) {
|
get: async function (params) {
|
||||||
return await request.get('tasks', { params })
|
return await request.get('system/tasks', { params })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
all: {
|
all: {
|
||||||
get: async function () {
|
get: async function () {
|
||||||
return await request.get('tasks/all')
|
return await request.get('system/tasks/all')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
detail: {
|
detail: {
|
||||||
get: async function (id) {
|
get: async function (id) {
|
||||||
return await request.get(`tasks/${id}`)
|
return await request.get(`system/tasks/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
add: {
|
add: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('tasks', params)
|
return await request.post('system/tasks', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
put: async function (id, params) {
|
put: async function (id, params) {
|
||||||
return await request.put(`tasks/${id}`, params)
|
return await request.put(`system/tasks/${id}`, params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
delete: async function (id) {
|
delete: async function (id) {
|
||||||
return await request.delete(`tasks/${id}`)
|
return await request.delete(`system/tasks/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchDelete: {
|
batchDelete: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('tasks/batch-delete', params)
|
return await request.post('system/tasks/batch-delete', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchStatus: {
|
batchStatus: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('tasks/batch-status', params)
|
return await request.post('system/tasks/batch-status', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
run: {
|
run: {
|
||||||
post: async function (id) {
|
post: async function (id) {
|
||||||
return await request.post(`tasks/${id}/run`)
|
return await request.post(`system/tasks/${id}/run`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
statistics: {
|
statistics: {
|
||||||
get: async function () {
|
get: async function () {
|
||||||
return await request.get('tasks/statistics')
|
return await request.get('system/tasks/statistics')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -233,62 +245,62 @@ export default {
|
|||||||
cities: {
|
cities: {
|
||||||
list: {
|
list: {
|
||||||
get: async function (params) {
|
get: async function (params) {
|
||||||
return await request.get('cities', { params })
|
return await request.get('system/cities', { params })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tree: {
|
tree: {
|
||||||
get: async function () {
|
get: async function () {
|
||||||
return await request.get('cities/tree')
|
return await request.get('system/cities/tree')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
detail: {
|
detail: {
|
||||||
get: async function (id) {
|
get: async function (id) {
|
||||||
return await request.get(`cities/${id}`)
|
return await request.get(`system/cities/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
children: {
|
children: {
|
||||||
get: async function (id) {
|
get: async function (id) {
|
||||||
return await request.get(`cities/${id}/children`)
|
return await request.get(`system/cities/${id}/children`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
provinces: {
|
provinces: {
|
||||||
get: async function () {
|
get: async function () {
|
||||||
return await request.get('cities/provinces')
|
return await request.get('system/cities/provinces')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cities: {
|
cities: {
|
||||||
get: async function (provinceId) {
|
get: async function (provinceId) {
|
||||||
return await request.get(`cities/${provinceId}/cities`)
|
return await request.get(`system/cities/${provinceId}/cities`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
districts: {
|
districts: {
|
||||||
get: async function (cityId) {
|
get: async function (cityId) {
|
||||||
return await request.get(`cities/${cityId}/districts`)
|
return await request.get(`system/cities/${cityId}/districts`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
add: {
|
add: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('cities', params)
|
return await request.post('system/cities', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
put: async function (id, params) {
|
put: async function (id, params) {
|
||||||
return await request.put(`cities/${id}`, params)
|
return await request.put(`system/cities/${id}`, params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
delete: async function (id) {
|
delete: async function (id) {
|
||||||
return await request.delete(`cities/${id}`)
|
return await request.delete(`system/cities/${id}`)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchDelete: {
|
batchDelete: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('cities/batch-delete', params)
|
return await request.post('system/cities/batch-delete', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchStatus: {
|
batchStatus: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('cities/batch-status', params)
|
return await request.post('system/cities/batch-status', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -297,31 +309,31 @@ export default {
|
|||||||
upload: {
|
upload: {
|
||||||
single: {
|
single: {
|
||||||
post: async function (formData) {
|
post: async function (formData) {
|
||||||
return await request.post('upload', formData, {
|
return await request.post('system/upload', formData, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' }
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
multiple: {
|
multiple: {
|
||||||
post: async function (formData) {
|
post: async function (formData) {
|
||||||
return await request.post('upload/multiple', formData, {
|
return await request.post('system/upload/multiple', formData, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' }
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
base64: {
|
base64: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('upload/base64', params)
|
return await request.post('system/upload/base64', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('upload/delete', params)
|
return await request.post('system/upload/delete', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
batchDelete: {
|
batchDelete: {
|
||||||
post: async function (params) {
|
post: async function (params) {
|
||||||
return await request.post('upload/batch-delete', params)
|
return await request.post('system/upload/batch-delete', params)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
msg: String,
|
|
||||||
})
|
|
||||||
|
|
||||||
const count = ref(0)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<h1>{{ msg }}</h1>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<button type="button" @click="count++">count is {{ count }}</button>
|
|
||||||
<p>
|
|
||||||
Edit
|
|
||||||
<code>components/HelloWorld.vue</code> to test HMR
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Check out
|
|
||||||
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
|
|
||||||
>create-vue</a
|
|
||||||
>, the official Vue + Vite starter
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Learn more about IDE Support for Vue in the
|
|
||||||
<a
|
|
||||||
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
|
|
||||||
target="_blank"
|
|
||||||
>Vue Docs Scaling up Guide</a
|
|
||||||
>.
|
|
||||||
</p>
|
|
||||||
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.read-the-docs {
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,606 @@
|
|||||||
|
# scSelect 组件使用文档
|
||||||
|
|
||||||
|
## 组件简介
|
||||||
|
|
||||||
|
`scSelect` 是一个基于 Ant Design Vue `a-select` 组件封装的增强型选择器组件,支持多种数据源(data、api、dictionary)和智能缓存机制。
|
||||||
|
|
||||||
|
## 特性
|
||||||
|
|
||||||
|
- ✅ 支持三种数据源:直接数据(data)、API 接口(api)、字典数据(dictionary)
|
||||||
|
- ✅ API 数据源自动缓存,减少重复请求
|
||||||
|
- ✅ 字典数据源自动从缓存读取,登录后自动预加载
|
||||||
|
- ✅ 支持按需加载数据(focus 时触发)
|
||||||
|
- ✅ 支持立即加载数据(组件挂载时)
|
||||||
|
- ✅ 支持自定义字段映射
|
||||||
|
- ✅ 支持自定义数据处理函数
|
||||||
|
- ✅ 完全继承 `a-select` 的所有属性和方法
|
||||||
|
- ✅ 支持插槽透传
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
组件已自动注册,直接使用即可:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<sc-select v-model:value="value" source-type="data" :data="options" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import scSelect from '@/components/scSelect/index.vue'
|
||||||
|
|
||||||
|
const value = ref('')
|
||||||
|
const options = ref([
|
||||||
|
{ label: '选项1', value: '1' },
|
||||||
|
{ label: '选项2', value: '2' },
|
||||||
|
])
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Props
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||||
|
|------|------|------|--------|--------|
|
||||||
|
| sourceType | 数据源类型 | String | 'data' \| 'api' \| 'dictionary' | 'data' |
|
||||||
|
| data | 直接数据(当 sourceType 为 data 时使用) | Array | - | [] |
|
||||||
|
| api | API 接口地址(当 sourceType 为 api 时使用) | String | - | '' |
|
||||||
|
| apiParams | API 请求参数(当 sourceType 为 api 时使用) | Object | - | {} |
|
||||||
|
| dictionaryCode | 字典编码(当 sourceType 为 dictionary 时使用) | String | - | '' |
|
||||||
|
| enableApiCache | 是否启用 API 数据缓存 | Boolean | - | true |
|
||||||
|
| apiCacheTime | API 缓存时间(毫秒) | Number | - | 300000(5分钟) |
|
||||||
|
| fieldNames | 字段映射配置 | Object | - | { label: 'label', value: 'value' } |
|
||||||
|
| immediate | 是否在组件挂载时立即加载数据 | Boolean | - | false |
|
||||||
|
| dataProcessor | 数据处理函数 | Function | - | null |
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
组件完全继承 `a-select` 的所有事件,包括但不限于:
|
||||||
|
|
||||||
|
- `@change` - 选中值变化时触发
|
||||||
|
- `@focus` - 获得焦点时触发
|
||||||
|
- `@blur` - 失去焦点时触发
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
|
||||||
|
通过 ref 可以调用以下方法:
|
||||||
|
|
||||||
|
| 方法名 | 说明 | 参数 |
|
||||||
|
|--------|------|------|
|
||||||
|
| refresh | 强制刷新数据 | - |
|
||||||
|
| loadApiData | 手动加载 API 数据 | - |
|
||||||
|
| loadDictionaryData | 手动加载字典数据 | - |
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### 1. 使用 data 数据源
|
||||||
|
|
||||||
|
最简单的使用方式,直接提供数据数组:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<a-form>
|
||||||
|
<a-form-item label="状态">
|
||||||
|
<sc-select
|
||||||
|
v-model:value="form.status"
|
||||||
|
source-type="data"
|
||||||
|
:data="statusOptions"
|
||||||
|
placeholder="请选择状态"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
import scSelect from '@/components/scSelect/index.vue'
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
status: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const statusOptions = [
|
||||||
|
{ label: '启用', value: 1 },
|
||||||
|
{ label: '禁用', value: 0 },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 使用 api 数据源
|
||||||
|
|
||||||
|
从 API 接口获取数据,自动缓存:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<a-form>
|
||||||
|
<a-form-item label="用户">
|
||||||
|
<sc-select
|
||||||
|
v-model:value="form.userId"
|
||||||
|
source-type="api"
|
||||||
|
api="users"
|
||||||
|
:api-params="{ page: 1, page_size: 100 }"
|
||||||
|
:enable-api-cache="true"
|
||||||
|
:api-cache-time="600000"
|
||||||
|
placeholder="请选择用户"
|
||||||
|
:field-names="{ label: 'username', value: 'id' }"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
import scSelect from '@/components/scSelect/index.vue'
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
userId: ''
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 使用 dictionary 数据源(推荐)
|
||||||
|
|
||||||
|
从字典缓存获取数据,性能最佳:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<a-form>
|
||||||
|
<a-form-item label="性别">
|
||||||
|
<sc-select
|
||||||
|
v-model:value="form.gender"
|
||||||
|
source-type="dictionary"
|
||||||
|
dictionary-code="gender"
|
||||||
|
placeholder="请选择性别"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="用户状态">
|
||||||
|
<sc-select
|
||||||
|
v-model:value="form.userStatus"
|
||||||
|
source-type="dictionary"
|
||||||
|
dictionary-code="user_status"
|
||||||
|
placeholder="请选择用户状态"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
import scSelect from '@/components/scSelect/index.vue'
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
gender: '',
|
||||||
|
userStatus: ''
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 立即加载数据
|
||||||
|
|
||||||
|
在组件挂载时立即加载数据,而不是等到 focus 时:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<sc-select
|
||||||
|
v-model:value="value"
|
||||||
|
source-type="api"
|
||||||
|
api="roles/all"
|
||||||
|
:immediate="true"
|
||||||
|
placeholder="请选择角色"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import scSelect from '@/components/scSelect/index.vue'
|
||||||
|
|
||||||
|
const value = ref('')
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 自定义数据处理
|
||||||
|
|
||||||
|
使用 `dataProcessor` 函数自定义数据格式:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<sc-select
|
||||||
|
v-model:value="value"
|
||||||
|
source-type="api"
|
||||||
|
api="roles"
|
||||||
|
:data-processor="processData"
|
||||||
|
placeholder="请选择角色"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import scSelect from '@/components/scSelect/index.vue'
|
||||||
|
|
||||||
|
const value = ref('')
|
||||||
|
|
||||||
|
// 自定义数据处理函数
|
||||||
|
function processData(data) {
|
||||||
|
return data.map(item => ({
|
||||||
|
label: `${item.name} (${item.description})`,
|
||||||
|
value: item.id,
|
||||||
|
extra: item.description
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. 使用 ref 调用方法
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<sc-select
|
||||||
|
ref="selectRef"
|
||||||
|
v-model:value="value"
|
||||||
|
source-type="api"
|
||||||
|
api="users"
|
||||||
|
placeholder="请选择用户"
|
||||||
|
/>
|
||||||
|
<a-button @click="handleRefresh">刷新数据</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
import scSelect from '@/components/scSelect/index.vue'
|
||||||
|
|
||||||
|
const selectRef = ref(null)
|
||||||
|
const value = ref('')
|
||||||
|
|
||||||
|
// 刷新数据
|
||||||
|
function handleRefresh() {
|
||||||
|
if (selectRef.value) {
|
||||||
|
selectRef.value.refresh()
|
||||||
|
message.success('数据已刷新')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. 禁用 API 缓存
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<sc-select
|
||||||
|
v-model:value="value"
|
||||||
|
source-type="api"
|
||||||
|
api="users"
|
||||||
|
:enable-api-cache="false"
|
||||||
|
placeholder="请选择用户"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. 多选模式
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<sc-select
|
||||||
|
v-model:value="value"
|
||||||
|
mode="multiple"
|
||||||
|
source-type="dictionary"
|
||||||
|
dictionary-code="tags"
|
||||||
|
placeholder="请选择标签"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import scSelect from '@/components/scSelect/index.vue'
|
||||||
|
|
||||||
|
const value = ref([])
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9. 自定义插槽
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<sc-select
|
||||||
|
v-model:value="value"
|
||||||
|
source-type="data"
|
||||||
|
:data="options"
|
||||||
|
placeholder="请选择"
|
||||||
|
>
|
||||||
|
<template #suffixIcon>
|
||||||
|
<SearchOutlined />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #notFoundContent>
|
||||||
|
<a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" description="暂无数据" />
|
||||||
|
</template>
|
||||||
|
</sc-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { SearchOutlined } from '@ant-design/icons-vue'
|
||||||
|
import { Empty } from 'ant-design-vue'
|
||||||
|
import scSelect from '@/components/scSelect/index.vue'
|
||||||
|
|
||||||
|
const value = ref('')
|
||||||
|
const options = ref([
|
||||||
|
{ label: '选项1', value: '1' },
|
||||||
|
{ label: '选项2', value: '2' },
|
||||||
|
])
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数据格式要求
|
||||||
|
|
||||||
|
### data 数据源
|
||||||
|
|
||||||
|
支持以下格式:
|
||||||
|
|
||||||
|
1. 字符串/数字数组
|
||||||
|
```javascript
|
||||||
|
['选项1', '选项2', '选项3']
|
||||||
|
[1, 2, 3]
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 对象数组(标准格式)
|
||||||
|
```javascript
|
||||||
|
[
|
||||||
|
{ label: '选项1', value: '1' },
|
||||||
|
{ label: '选项2', value: '2' },
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 对象数组(自定义字段)
|
||||||
|
```javascript
|
||||||
|
[
|
||||||
|
{ name: '选项1', id: 1 },
|
||||||
|
{ name: '选项2', id: 2 },
|
||||||
|
]
|
||||||
|
// 使用 field-names 映射
|
||||||
|
field-names="{ label: 'name', value: 'id' }"
|
||||||
|
```
|
||||||
|
|
||||||
|
### api 数据源
|
||||||
|
|
||||||
|
API 接口需要返回以下格式:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "success",
|
||||||
|
"data": [
|
||||||
|
{ label: '选项1', value: '1' },
|
||||||
|
{ label: '选项2', value: '2' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 或者
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "success",
|
||||||
|
"list": [
|
||||||
|
{ label: '选项1', value: '1' },
|
||||||
|
{ label: '选项2', value: '2' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### dictionary 数据源
|
||||||
|
|
||||||
|
字典数据通过后台管理系统的"数据字典管理"模块配置,登录后会自动缓存到前端。
|
||||||
|
|
||||||
|
字典数据格式:
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
code: 'user_status',
|
||||||
|
name: '用户状态',
|
||||||
|
items: [
|
||||||
|
{ name: '启用', value: '1', sort: 1, status: 1 },
|
||||||
|
{ name: '禁用', value: '0', sort: 2, status: 1 },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
组件会自动转换为:
|
||||||
|
```javascript
|
||||||
|
[
|
||||||
|
{ label: '启用', value: '1', name: '启用', sort: 1, status: 1 },
|
||||||
|
{ label: '禁用', value: '0', name: '禁用', sort: 2, status: 1 },
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 字典数据缓存机制
|
||||||
|
|
||||||
|
### 缓存流程
|
||||||
|
|
||||||
|
1. **登录时**:用户登录成功后,自动调用 `dictionaryStore.loadAllDictionaries()` 加载所有字典数据
|
||||||
|
2. **存储**:字典数据存储在 Pinia Store 中,并持久化到 localStorage
|
||||||
|
3. **使用**:组件通过 `dictionaryCode` 从 Store 获取数据,无需重复请求
|
||||||
|
4. **刷新**:可以调用 `refresh()` 方法强制刷新字典数据
|
||||||
|
|
||||||
|
### 清空缓存
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { useDictionaryStore } from '@/stores/modules/dictionary'
|
||||||
|
|
||||||
|
const dictionaryStore = useDictionaryStore()
|
||||||
|
|
||||||
|
// 清空字典缓存
|
||||||
|
dictionaryStore.clearCache()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取缓存信息
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { useDictionaryStore } from '@/stores/modules/dictionary'
|
||||||
|
|
||||||
|
const dictionaryStore = useDictionaryStore()
|
||||||
|
|
||||||
|
// 获取缓存信息
|
||||||
|
const info = dictionaryStore.getCacheInfo()
|
||||||
|
console.log('字典数量:', info.count)
|
||||||
|
console.log('最后加载时间:', new Date(info.lastLoadTime))
|
||||||
|
```
|
||||||
|
|
||||||
|
## API 数据缓存机制
|
||||||
|
|
||||||
|
### 缓存策略
|
||||||
|
|
||||||
|
- 默认启用缓存,缓存时间为 5 分钟
|
||||||
|
- 缓存 key 由 `api` 地址和 `apiParams` 参数组成
|
||||||
|
- 缓存过期后自动重新请求
|
||||||
|
- 可以通过 `enableApiCache` 关闭缓存
|
||||||
|
- 可以通过 `apiCacheTime` 自定义缓存时间
|
||||||
|
|
||||||
|
### 缓存 key 示例
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 同一个 API,不同参数会有不同的缓存
|
||||||
|
api: 'users'
|
||||||
|
apiParams: { page: 1, page_size: 20 }
|
||||||
|
// 缓存 key: 'users{"page":1,"page_size":20}'
|
||||||
|
|
||||||
|
apiParams: { page: 2, page_size: 20 }
|
||||||
|
// 缓存 key: 'users{"page":2,"page_size":20}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **字典数据源**:确保在后台管理系统中配置了对应的字典数据
|
||||||
|
2. **API 数据源**:确保 API 接口返回的数据格式正确
|
||||||
|
3. **缓存失效**:修改字典数据后,需要调用 `refresh()` 方法刷新缓存
|
||||||
|
4. **字段映射**:如果后端返回的字段名不是 `label` 和 `value`,需要使用 `fieldNames` 映射
|
||||||
|
5. **按需加载**:默认在 focus 时加载数据,如果需要立即加载,设置 `immediate=true`
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
1. **优先使用字典数据源**:对于固定选项(如状态、类型等),优先使用字典数据源
|
||||||
|
2. **合理使用缓存**:API 数据源建议启用缓存,减少服务器压力
|
||||||
|
3. **字段映射**:明确指定 `fieldNames`,避免数据格式不一致
|
||||||
|
4. **错误处理**:API 请求失败时,组件会自动处理错误并记录日志
|
||||||
|
5. **性能优化**:对于大量数据,建议使用分页加载或虚拟滚动
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### Q: 字典数据不显示?
|
||||||
|
|
||||||
|
A: 检查以下几点:
|
||||||
|
1. 后台管理系统中是否配置了对应的字典
|
||||||
|
2. 字典编码是否正确
|
||||||
|
3. 字典状态是否为启用
|
||||||
|
4. 字典项状态是否为启用
|
||||||
|
|
||||||
|
### Q: API 数据加载失败?
|
||||||
|
|
||||||
|
A: 检查以下几点:
|
||||||
|
1. API 地址是否正确
|
||||||
|
2. API 是否需要认证
|
||||||
|
3. 返回数据格式是否符合要求
|
||||||
|
4. 控制台是否有错误信息
|
||||||
|
|
||||||
|
### Q: 如何刷新字典数据?
|
||||||
|
|
||||||
|
A: 使用 `refresh()` 方法:
|
||||||
|
```javascript
|
||||||
|
const selectRef = ref(null)
|
||||||
|
selectRef.value?.refresh()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: 如何禁用缓存?
|
||||||
|
|
||||||
|
A: 设置 `enableApiCache` 为 `false`:
|
||||||
|
```vue
|
||||||
|
<sc-select
|
||||||
|
source-type="api"
|
||||||
|
api="users"
|
||||||
|
:enable-api-cache="false"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 完整示例
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<a-form :model="form" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
||||||
|
<!-- 使用数据源 -->
|
||||||
|
<a-form-item label="数据源示例">
|
||||||
|
<sc-select
|
||||||
|
v-model:value="form.dataSource"
|
||||||
|
source-type="data"
|
||||||
|
:data="[
|
||||||
|
{ label: '字典数据', value: 'dictionary' },
|
||||||
|
{ label: 'API数据', value: 'api' },
|
||||||
|
{ label: '直接数据', value: 'data' },
|
||||||
|
]"
|
||||||
|
placeholder="请选择数据源"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 使用字典数据 -->
|
||||||
|
<a-form-item label="用户状态">
|
||||||
|
<sc-select
|
||||||
|
v-model:value="form.status"
|
||||||
|
source-type="dictionary"
|
||||||
|
dictionary-code="user_status"
|
||||||
|
placeholder="请选择用户状态"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 使用 API 数据 -->
|
||||||
|
<a-form-item label="所属角色">
|
||||||
|
<sc-select
|
||||||
|
v-model:value="form.roleId"
|
||||||
|
source-type="api"
|
||||||
|
api="roles/all"
|
||||||
|
:enable-api-cache="true"
|
||||||
|
:immediate="true"
|
||||||
|
:field-names="{ label: 'name', value: 'id' }"
|
||||||
|
placeholder="请选择角色"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 多选模式 -->
|
||||||
|
<a-form-item label="标签">
|
||||||
|
<sc-select
|
||||||
|
v-model:value="form.tags"
|
||||||
|
mode="multiple"
|
||||||
|
source-type="dictionary"
|
||||||
|
dictionary-code="tags"
|
||||||
|
placeholder="请选择标签"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 带搜索 -->
|
||||||
|
<a-form-item label="省份">
|
||||||
|
<sc-select
|
||||||
|
v-model:value="form.province"
|
||||||
|
source-type="api"
|
||||||
|
api="cities/provinces"
|
||||||
|
show-search
|
||||||
|
:filter-option="filterOption"
|
||||||
|
placeholder="请选择省份"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
import scSelect from '@/components/scSelect/index.vue'
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
dataSource: '',
|
||||||
|
status: '',
|
||||||
|
roleId: '',
|
||||||
|
tags: [],
|
||||||
|
province: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 搜索过滤
|
||||||
|
function filterOption(input, option) {
|
||||||
|
return option.label.toLowerCase().includes(input.toLowerCase())
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,308 @@
|
|||||||
|
<template>
|
||||||
|
<a-select
|
||||||
|
v-bind="$attrs"
|
||||||
|
:loading="loading"
|
||||||
|
:options="options"
|
||||||
|
:field-names="fieldNames"
|
||||||
|
@focus="handleFocus"
|
||||||
|
>
|
||||||
|
<template v-for="(_, slot) of $slots" #[slot]="scope">
|
||||||
|
<slot :name="slot" v-bind="scope || {}" />
|
||||||
|
</template>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, watch } from 'vue'
|
||||||
|
import { useDictionaryStore } from '@/stores/modules/dictionary'
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'ScSelect',
|
||||||
|
inheritAttrs: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// 数据源类型:data(直接数据)、api(接口数据)、dictionary(字典数据)
|
||||||
|
sourceType: {
|
||||||
|
type: String,
|
||||||
|
default: 'data',
|
||||||
|
validator: (value) => ['data', 'api', 'dictionary'].includes(value),
|
||||||
|
},
|
||||||
|
// 直接数据(当 sourceType 为 data 时使用)
|
||||||
|
data: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
// API 接口地址(当 sourceType 为 api 时使用)
|
||||||
|
api: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
// API 请求参数(当 sourceType 为 api 时使用)
|
||||||
|
apiParams: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
// 字典编码(当 sourceType 为 dictionary 时使用)
|
||||||
|
dictionaryCode: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
// 是否启用 API 数据缓存(当 sourceType 为 api 时使用)
|
||||||
|
enableApiCache: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
// API 缓存时间(毫秒,当 sourceType 为 api 时使用)
|
||||||
|
apiCacheTime: {
|
||||||
|
type: Number,
|
||||||
|
default: 5 * 60 * 1000, // 默认 5 分钟
|
||||||
|
},
|
||||||
|
// 字段映射配置
|
||||||
|
fieldNames: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
label: 'label',
|
||||||
|
value: 'value',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
// 是否在组件挂载时立即加载数据
|
||||||
|
immediate: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
// 数据处理函数,用于自定义数据格式转换
|
||||||
|
dataProcessor: {
|
||||||
|
type: Function,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const dictionaryStore = useDictionaryStore()
|
||||||
|
const loading = ref(false)
|
||||||
|
const apiData = ref(null)
|
||||||
|
const apiCacheTime = ref(null)
|
||||||
|
const options = computed(() => {
|
||||||
|
switch (props.sourceType) {
|
||||||
|
case 'data':
|
||||||
|
return processData(props.data)
|
||||||
|
case 'api':
|
||||||
|
return processData(apiData.value || [])
|
||||||
|
case 'dictionary':
|
||||||
|
return processData(getDictionaryData())
|
||||||
|
default:
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// API 数据缓存
|
||||||
|
const apiCache = new Map()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理数据格式
|
||||||
|
*/
|
||||||
|
function processData(data) {
|
||||||
|
if (!data || !Array.isArray(data)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有自定义处理函数,使用自定义处理
|
||||||
|
if (props.dataProcessor && typeof props.dataProcessor === 'function') {
|
||||||
|
return props.dataProcessor(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认处理:确保数据有 label 和 value 字段
|
||||||
|
return data.map((item) => {
|
||||||
|
if (typeof item === 'string' || typeof item === 'number') {
|
||||||
|
return {
|
||||||
|
label: item,
|
||||||
|
value: item,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果已经有 label 和 value 字段,直接返回
|
||||||
|
if (item.label !== undefined && item.value !== undefined) {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
// 尝试使用 fieldNames 映射
|
||||||
|
const labelKey = props.fieldNames.label || 'label'
|
||||||
|
const valueKey = props.fieldNames.value || 'value'
|
||||||
|
return {
|
||||||
|
label: item[labelKey] || item.name || item.title || item.id,
|
||||||
|
value: item[valueKey] !== undefined ? item[valueKey] : item.id,
|
||||||
|
...item,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字典数据
|
||||||
|
*/
|
||||||
|
function getDictionaryData() {
|
||||||
|
if (!props.dictionaryCode) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return dictionaryStore.dictionaries[props.dictionaryCode] || []
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载 API 数据
|
||||||
|
*/
|
||||||
|
async function loadApiData() {
|
||||||
|
if (!props.api) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查缓存
|
||||||
|
if (props.enableApiCache) {
|
||||||
|
const cacheKey = props.api + JSON.stringify(props.apiParams)
|
||||||
|
const cached = apiCache.get(cacheKey)
|
||||||
|
|
||||||
|
if (cached && Date.now() - cached.time < props.apiCacheTime) {
|
||||||
|
apiData.value = cached.data
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await request.get(props.api, { params: props.apiParams })
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
const data = res.data || res.list || []
|
||||||
|
apiData.value = data
|
||||||
|
|
||||||
|
// 缓存数据
|
||||||
|
if (props.enableApiCache) {
|
||||||
|
const cacheKey = props.api + JSON.stringify(props.apiParams)
|
||||||
|
apiCache.set(cacheKey, {
|
||||||
|
data,
|
||||||
|
time: Date.now(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载 API 数据失败:', error)
|
||||||
|
apiData.value = []
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载字典数据
|
||||||
|
*/
|
||||||
|
async function loadDictionaryData() {
|
||||||
|
if (!props.dictionaryCode) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查缓存
|
||||||
|
if (dictionaryStore.dictionaries[props.dictionaryCode]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
await dictionaryStore.getDictionary(props.dictionaryCode)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载字典数据失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理 focus 事件,按需加载数据
|
||||||
|
*/
|
||||||
|
async function handleFocus() {
|
||||||
|
switch (props.sourceType) {
|
||||||
|
case 'api':
|
||||||
|
if (!apiData.value || apiData.value.length === 0) {
|
||||||
|
await loadApiData()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'dictionary':
|
||||||
|
if (!dictionaryStore.dictionaries[props.dictionaryCode]) {
|
||||||
|
await loadDictionaryData()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 强制刷新数据
|
||||||
|
*/
|
||||||
|
async function refresh() {
|
||||||
|
switch (props.sourceType) {
|
||||||
|
case 'api':
|
||||||
|
await loadApiData()
|
||||||
|
break
|
||||||
|
case 'dictionary':
|
||||||
|
await dictionaryStore.getDictionary(props.dictionaryCode, true)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听数据源变化
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
(newVal) => {
|
||||||
|
if (props.sourceType === 'data') {
|
||||||
|
// data 类型不需要特殊处理,计算属性会自动更新
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.api,
|
||||||
|
() => {
|
||||||
|
if (props.sourceType === 'api') {
|
||||||
|
apiData.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.apiParams,
|
||||||
|
() => {
|
||||||
|
if (props.sourceType === 'api') {
|
||||||
|
apiData.value = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.dictionaryCode,
|
||||||
|
() => {
|
||||||
|
if (props.sourceType === 'dictionary') {
|
||||||
|
// dictionary 变化时,数据会自动从 store 获取
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 组件挂载时立即加载数据
|
||||||
|
if (props.immediate) {
|
||||||
|
switch (props.sourceType) {
|
||||||
|
case 'api':
|
||||||
|
loadApiData()
|
||||||
|
break
|
||||||
|
case 'dictionary':
|
||||||
|
loadDictionaryData()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暴露方法供外部调用
|
||||||
|
defineExpose({
|
||||||
|
refresh,
|
||||||
|
loadApiData,
|
||||||
|
loadDictionaryData,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
// 组件样式继承自 a-select
|
||||||
|
</style>
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
<div class="sc-table" ref="tableWrapper">
|
<div class="sc-table" ref="tableWrapper">
|
||||||
<!-- 表格内容 -->
|
<!-- 表格内容 -->
|
||||||
<div class="sc-table-content" ref="tableContent">
|
<div class="sc-table-content" ref="tableContent">
|
||||||
<a-table :columns="tableColumns" :data-source="dataSource" :loading="loading" :pagination="false"
|
<a-table v-if="dataSource.length > 0" :columns="tableColumns" :data-source="dataSource" :loading="loading" :pagination="false"
|
||||||
:row-key="rowKey" :row-selection="rowSelection" :scroll="scroll" :bordered="tableSettings.bordered"
|
:row-key="rowKey" :row-selection="rowSelection" :scroll="scroll" :bordered="tableSettings.bordered"
|
||||||
:size="tableSettings.size" :show-header="showHeader" :locale="locale" @change="handleTableChange"
|
:size="tableSettings.size" :show-header="showHeader" :locale="locale" :expanded-row-keys="expandedRowKeys"
|
||||||
@resizeColumn="handleResizeColumn">
|
:expand-row-by-click="expandRowByClick" @change="handleTableChange" @resizeColumn="handleResizeColumn">
|
||||||
<!-- 自定义单元格内容 -->
|
<!-- 自定义单元格内容 -->
|
||||||
<template #bodyCell="{ text, record, index, column }">
|
<template #bodyCell="{ text, record, index, column }">
|
||||||
<!-- 序号列 -->
|
<!-- 序号列 -->
|
||||||
@@ -216,8 +216,20 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: '暂无数据',
|
default: '暂无数据',
|
||||||
},
|
},
|
||||||
|
// 树形表格配置
|
||||||
|
defaultExpandAll: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
expandRowByClick: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 展开的行keys
|
||||||
|
const expandedRowKeys = ref([])
|
||||||
|
|
||||||
const tableContent = useTemplateRef('tableContent')
|
const tableContent = useTemplateRef('tableContent')
|
||||||
const tableWrapper = useTemplateRef('tableWrapper')
|
const tableWrapper = useTemplateRef('tableWrapper')
|
||||||
let scroll = ref({
|
let scroll = ref({
|
||||||
@@ -226,6 +238,36 @@ let scroll = ref({
|
|||||||
y: true,
|
y: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 递归获取所有节点的key
|
||||||
|
const getAllNodeKeys = (nodes) => {
|
||||||
|
const keys = []
|
||||||
|
const traverse = (list) => {
|
||||||
|
list.forEach(node => {
|
||||||
|
// 如果节点有children且不为空,则该节点需要展开
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
const key = typeof props.rowKey === 'function' ? props.rowKey(node) : node[props.rowKey]
|
||||||
|
keys.push(key)
|
||||||
|
traverse(node.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
traverse(nodes)
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听数据变化,自动展开所有节点
|
||||||
|
watch(
|
||||||
|
() => props.dataSource,
|
||||||
|
(newData) => {
|
||||||
|
if (props.defaultExpandAll && newData && newData.length > 0) {
|
||||||
|
expandedRowKeys.value = getAllNodeKeys(newData)
|
||||||
|
} else {
|
||||||
|
expandedRowKeys.value = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
updateTableHeight()
|
updateTableHeight()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -70,6 +70,7 @@
|
|||||||
:pagination="false"
|
:pagination="false"
|
||||||
:row-key="rowKey"
|
:row-key="rowKey"
|
||||||
:row-selection="rowSelection"
|
:row-selection="rowSelection"
|
||||||
|
:default-expand-all="true"
|
||||||
@refresh="refreshTable"
|
@refresh="refreshTable"
|
||||||
@select="handleSelectChange"
|
@select="handleSelectChange"
|
||||||
@selectAll="handleSelectAll"
|
@selectAll="handleSelectAll"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<a-input-number v-model:value="form.sort" :min="0" :step="1" style="width: 100%" placeholder="请输入排序" />
|
<a-input-number v-model:value="form.sort" :min="0" :step="1" style="width: 100%" placeholder="请输入排序" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="状态" name="status">
|
<a-form-item label="状态" name="status">
|
||||||
<a-switch v-model:checked="statusChecked" checked-children="启用" un-checked-children="禁用" />
|
<sc-select v-model:value="form.status" source-type="dictionary" dictionary-code="role_status" placeholder="请选择状态" allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -30,6 +30,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, computed } from 'vue'
|
import { ref, reactive, computed } from 'vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
|
import scSelect from '@/components/scSelect/index.vue'
|
||||||
import authApi from '@/api/auth'
|
import authApi from '@/api/auth'
|
||||||
|
|
||||||
const emit = defineEmits(['success', 'closed'])
|
const emit = defineEmits(['success', 'closed'])
|
||||||
@@ -50,15 +51,7 @@ const form = reactive({
|
|||||||
code: '',
|
code: '',
|
||||||
description: '',
|
description: '',
|
||||||
sort: 1,
|
sort: 1,
|
||||||
status: 1
|
status: null
|
||||||
})
|
|
||||||
|
|
||||||
// 状态开关计算属性
|
|
||||||
const statusChecked = computed({
|
|
||||||
get: () => form.status === 1,
|
|
||||||
set: (val) => {
|
|
||||||
form.status = val ? 1 : 0
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 表单引用
|
// 表单引用
|
||||||
@@ -132,7 +125,7 @@ const setData = (data) => {
|
|||||||
form.code = data.code
|
form.code = data.code
|
||||||
form.description = data.description || ''
|
form.description = data.description || ''
|
||||||
form.sort = data.sort
|
form.sort = data.sort
|
||||||
form.status = data.status !== undefined ? data.status : 1
|
form.status = data.status !== undefined ? data.status : null
|
||||||
}
|
}
|
||||||
|
|
||||||
// 暴露方法给父组件
|
// 暴露方法给父组件
|
||||||
|
|||||||
@@ -38,8 +38,11 @@
|
|||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label="性别" name="gender">
|
||||||
|
<sc-select v-model:value="form.gender" source-type="dictionary" dictionary-code="gender" placeholder="请选择性别" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
<a-form-item label="状态" name="status">
|
<a-form-item label="状态" name="status">
|
||||||
<a-switch v-model:checked="statusChecked" checked-children="启用" un-checked-children="禁用" />
|
<sc-select v-model:value="form.status" source-type="dictionary" dictionary-code="user_status" placeholder="请选择状态" allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -53,6 +56,7 @@
|
|||||||
import { ref, reactive, computed } from 'vue'
|
import { ref, reactive, computed } from 'vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import scUpload from '@/components/scUpload/index.vue'
|
import scUpload from '@/components/scUpload/index.vue'
|
||||||
|
import scSelect from '@/components/scSelect/index.vue'
|
||||||
import authApi from '@/api/auth'
|
import authApi from '@/api/auth'
|
||||||
|
|
||||||
const emit = defineEmits(['success', 'closed'])
|
const emit = defineEmits(['success', 'closed'])
|
||||||
@@ -76,15 +80,8 @@ const form = reactive({
|
|||||||
phone: '',
|
phone: '',
|
||||||
department_id: null,
|
department_id: null,
|
||||||
role_ids: [],
|
role_ids: [],
|
||||||
status: 1
|
gender: null,
|
||||||
})
|
status: null
|
||||||
|
|
||||||
// 状态开关计算属性
|
|
||||||
const statusChecked = computed({
|
|
||||||
get: () => form.status === 1,
|
|
||||||
set: (val) => {
|
|
||||||
form.status = val ? 1 : 0
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 表单引用
|
// 表单引用
|
||||||
@@ -204,6 +201,7 @@ const submit = async () => {
|
|||||||
phone: form.phone,
|
phone: form.phone,
|
||||||
department_id: form.department_id,
|
department_id: form.department_id,
|
||||||
role_ids: form.role_ids,
|
role_ids: form.role_ids,
|
||||||
|
gender: form.gender,
|
||||||
status: form.status
|
status: form.status
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +240,8 @@ const setData = (data) => {
|
|||||||
form.phone = data.phone
|
form.phone = data.phone
|
||||||
form.department_id = data.department_id
|
form.department_id = data.department_id
|
||||||
form.role_ids = data.roles ? data.roles.map(item => item.id) : []
|
form.role_ids = data.roles ? data.roles.map(item => item.id) : []
|
||||||
form.status = data.status !== undefined ? data.status : 1
|
form.gender = data.gender !== undefined ? data.gender : null
|
||||||
|
form.status = data.status !== undefined ? data.status : null
|
||||||
}
|
}
|
||||||
|
|
||||||
// 组件挂载时加载数据
|
// 组件挂载时加载数据
|
||||||
|
|||||||
@@ -9,13 +9,14 @@
|
|||||||
</a-input>
|
</a-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<a-tree v-model:selectedKeys="selectedDeptKeys" :tree-data="filteredDepartmentTree"
|
<a-tree v-if="filteredDepartmentTree.length > 0" v-model:selectedKeys="selectedDeptKeys" v-model:expandedKeys="expandedDeptKeys" :tree-data="filteredDepartmentTree"
|
||||||
:field-names="{ title: 'name', key: 'id', children: 'children' }" show-line default-expand-all @select="onDeptSelect">
|
:field-names="{ title: 'name', key: 'id', children: 'children' }" show-line @select="onDeptSelect">
|
||||||
<template #icon="{ dataRef }">
|
<template #icon="{ dataRef }">
|
||||||
<ApartmentOutlined v-if="dataRef.children && dataRef.children.length > 0" />
|
<ApartmentOutlined v-if="dataRef.children && dataRef.children.length > 0" />
|
||||||
<UserOutlined v-else />
|
<UserOutlined v-else />
|
||||||
</template>
|
</template>
|
||||||
</a-tree>
|
</a-tree>
|
||||||
|
<a-empty v-else description="暂无部门数据" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-box">
|
<div class="right-box">
|
||||||
@@ -144,7 +145,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, onMounted } from 'vue'
|
import { ref, reactive, onMounted, watch } from 'vue'
|
||||||
import { message, Modal } from 'ant-design-vue'
|
import { message, Modal } from 'ant-design-vue'
|
||||||
import {
|
import {
|
||||||
SearchOutlined,
|
SearchOutlined,
|
||||||
@@ -225,10 +226,40 @@ const departmentTree = ref([])
|
|||||||
const filteredDepartmentTree = ref([])
|
const filteredDepartmentTree = ref([])
|
||||||
const selectedDeptKeys = ref([])
|
const selectedDeptKeys = ref([])
|
||||||
const departmentKeyword = ref('')
|
const departmentKeyword = ref('')
|
||||||
|
const expandedDeptKeys = ref([])
|
||||||
|
|
||||||
// 行key
|
// 行key
|
||||||
const rowKey = 'id'
|
const rowKey = 'id'
|
||||||
|
|
||||||
|
// 递归获取所有部门节点的key
|
||||||
|
const getAllDepartmentKeys = (nodes) => {
|
||||||
|
const keys = []
|
||||||
|
const traverse = (list) => {
|
||||||
|
list.forEach(node => {
|
||||||
|
// 如果节点有children且不为空,则该节点需要展开
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
keys.push(node.id)
|
||||||
|
traverse(node.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
traverse(nodes)
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听部门树数据变化,自动展开所有节点
|
||||||
|
watch(
|
||||||
|
() => filteredDepartmentTree.value,
|
||||||
|
(newData) => {
|
||||||
|
if (newData && newData.length > 0) {
|
||||||
|
expandedDeptKeys.value = getAllDepartmentKeys(newData)
|
||||||
|
} else {
|
||||||
|
expandedDeptKeys.value = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const columns = [
|
const columns = [
|
||||||
{ title: '头像', dataIndex: 'avatar', key: 'avatar', width: 80, align: 'center', slot: 'avatar' },
|
{ title: '头像', dataIndex: 'avatar', key: 'avatar', width: 80, align: 'center', slot: 'avatar' },
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ import { useRouter, useRoute } from 'vue-router'
|
|||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'
|
import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'
|
||||||
import { useUserStore } from '@/stores/modules/user'
|
import { useUserStore } from '@/stores/modules/user'
|
||||||
|
import { useDictionaryStore } from '@/stores/modules/dictionary'
|
||||||
import auth from '@/api/auth'
|
import auth from '@/api/auth'
|
||||||
import config from '@/config'
|
import config from '@/config'
|
||||||
import '@/assets/style/auth.scss'
|
import '@/assets/style/auth.scss'
|
||||||
@@ -71,6 +72,7 @@ const loginFormRef = ref(null)
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
const dictionaryStore = useDictionaryStore()
|
||||||
|
|
||||||
// Login form data
|
// Login form data
|
||||||
const loginForm = reactive({
|
const loginForm = reactive({
|
||||||
@@ -133,6 +135,11 @@ const handleLogin = async () => {
|
|||||||
userStore.setPermissions(loginData.permissions)
|
userStore.setPermissions(loginData.permissions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. Load dictionary data (缓存字典数据)
|
||||||
|
dictionaryStore.loadAllDictionaries().catch(error => {
|
||||||
|
console.error('加载字典数据失败:', error)
|
||||||
|
})
|
||||||
|
|
||||||
// Success message
|
// Success message
|
||||||
message.success('登录成功!')
|
message.success('登录成功!')
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import systemApi from '@/api/system'
|
import systemApi from '@/api/system'
|
||||||
import dictionaryCache from '@/utils/dictionaryCache'
|
import { useDictionaryStore } from '@/stores/modules/dictionary'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@@ -146,9 +146,12 @@ const valueChecked = computed({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 初始化字典 store
|
||||||
|
const dictionaryStore = useDictionaryStore()
|
||||||
|
|
||||||
// 加载配置分组
|
// 加载配置分组
|
||||||
const loadGroups = async () => {
|
const loadGroups = async () => {
|
||||||
const groups = await dictionaryCache.getItemsByCode('config_group')
|
const groups = await dictionaryStore.getDictionary('config_group')
|
||||||
groupOptions.value = groups.map(item => ({
|
groupOptions.value = groups.map(item => ({
|
||||||
label: item.label,
|
label: item.label,
|
||||||
value: item.value
|
value: item.value
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<!-- 状态 -->
|
<!-- 状态 -->
|
||||||
<a-form-item label="状态" name="status">
|
<a-form-item label="状态" name="status">
|
||||||
<a-switch v-model:checked="statusChecked" checked-children="启用" un-checked-children="禁用" />
|
<sc-select v-model:value="form.status" source-type="dictionary" dictionary-code="dictionary_status" placeholder="请选择状态" allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<!-- 描述 -->
|
<!-- 描述 -->
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
|
import scSelect from '@/components/scSelect/index.vue'
|
||||||
import systemApi from '@/api/system'
|
import systemApi from '@/api/system'
|
||||||
|
|
||||||
// ===== Props =====
|
// ===== Props =====
|
||||||
@@ -79,18 +80,10 @@ const form = ref({
|
|||||||
name: '',
|
name: '',
|
||||||
code: '',
|
code: '',
|
||||||
description: '',
|
description: '',
|
||||||
status: true,
|
status: null,
|
||||||
sort: 0
|
sort: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
// ===== 计算属性:状态开关 =====
|
|
||||||
const statusChecked = computed({
|
|
||||||
get: () => form.value.status === true,
|
|
||||||
set: (val) => {
|
|
||||||
form.value.status = val ? true : false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// ===== 验证规则 =====
|
// ===== 验证规则 =====
|
||||||
// 编码唯一性验证
|
// 编码唯一性验证
|
||||||
const validateCodeUnique = async (rule, value) => {
|
const validateCodeUnique = async (rule, value) => {
|
||||||
@@ -131,7 +124,7 @@ const resetForm = () => {
|
|||||||
name: '',
|
name: '',
|
||||||
code: '',
|
code: '',
|
||||||
description: '',
|
description: '',
|
||||||
status: true,
|
status: null,
|
||||||
sort: 0
|
sort: 0
|
||||||
}
|
}
|
||||||
formRef.value?.clearValidate()
|
formRef.value?.clearValidate()
|
||||||
@@ -145,7 +138,7 @@ const setData = (data) => {
|
|||||||
name: data.name || '',
|
name: data.name || '',
|
||||||
code: data.code || '',
|
code: data.code || '',
|
||||||
description: data.description || '',
|
description: data.description || '',
|
||||||
status: data.status !== undefined ? data.status : true,
|
status: data.status !== undefined ? data.status : null,
|
||||||
sort: data.sort !== undefined ? data.sort : 0
|
sort: data.sort !== undefined ? data.sort : 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ import systemApi from '@/api/system'
|
|||||||
import scTable from '@/components/scTable/index.vue'
|
import scTable from '@/components/scTable/index.vue'
|
||||||
import DictionaryDialog from './components/DictionaryDialog.vue'
|
import DictionaryDialog from './components/DictionaryDialog.vue'
|
||||||
import ItemDialog from './components/ItemDialog.vue'
|
import ItemDialog from './components/ItemDialog.vue'
|
||||||
import dictionaryCache from '@/utils/dictionaryCache'
|
import { useDictionaryStore } from '@/stores/modules/dictionary'
|
||||||
|
|
||||||
// ===== 字典列表相关 =====
|
// ===== 字典列表相关 =====
|
||||||
const dictionaryList = ref([])
|
const dictionaryList = ref([])
|
||||||
@@ -250,6 +250,8 @@ const loadDictionaryList = async () => {
|
|||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
dictionaryList.value = res.data || []
|
dictionaryList.value = res.data || []
|
||||||
filteredDictionaries.value = res.data || []
|
filteredDictionaries.value = res.data || []
|
||||||
|
// 建立字典ID到Code的映射
|
||||||
|
dictionaryStore.buildIdToCodeMap(res.data || [])
|
||||||
} else {
|
} else {
|
||||||
message.error(res.message || '加载字典列表失败')
|
message.error(res.message || '加载字典列表失败')
|
||||||
}
|
}
|
||||||
@@ -388,6 +390,8 @@ const handleDeleteItem = async (record) => {
|
|||||||
const res = await systemApi.dictionaryItems.delete.delete(record.id)
|
const res = await systemApi.dictionaryItems.delete.delete(record.id)
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
message.success('删除成功')
|
message.success('删除成功')
|
||||||
|
// 清除字典缓存
|
||||||
|
dictionaryStore.clearDictionary(selectedDictionaryId.value)
|
||||||
refreshTable()
|
refreshTable()
|
||||||
// 刷新字典列表以更新项数量
|
// 刷新字典列表以更新项数量
|
||||||
loadDictionaryList()
|
loadDictionaryList()
|
||||||
@@ -420,6 +424,8 @@ const handleBatchDelete = () => {
|
|||||||
const res = await systemApi.dictionaryItems.batchDelete.post({ ids })
|
const res = await systemApi.dictionaryItems.batchDelete.post({ ids })
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
message.success('删除成功')
|
message.success('删除成功')
|
||||||
|
// 清除字典缓存
|
||||||
|
dictionaryStore.clearDictionary(selectedDictionaryId.value)
|
||||||
selectedRows.value = []
|
selectedRows.value = []
|
||||||
refreshTable()
|
refreshTable()
|
||||||
loadDictionaryList()
|
loadDictionaryList()
|
||||||
@@ -458,6 +464,8 @@ const handleBatchStatus = () => {
|
|||||||
})
|
})
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
message.success(`${statusText}成功`)
|
message.success(`${statusText}成功`)
|
||||||
|
// 清除字典缓存
|
||||||
|
dictionaryStore.clearDictionary(selectedDictionaryId.value)
|
||||||
selectedRows.value = []
|
selectedRows.value = []
|
||||||
refreshTable()
|
refreshTable()
|
||||||
} else {
|
} else {
|
||||||
@@ -471,11 +479,14 @@ const handleBatchStatus = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化字典 store
|
||||||
|
const dictionaryStore = useDictionaryStore()
|
||||||
|
|
||||||
// ===== 方法:字典操作成功回调 =====
|
// ===== 方法:字典操作成功回调 =====
|
||||||
const handleDictionarySuccess = () => {
|
const handleDictionarySuccess = () => {
|
||||||
dialog.dictionary = false
|
dialog.dictionary = false
|
||||||
// 清理字典缓存
|
// 清理字典缓存
|
||||||
dictionaryCache.clearDictionary()
|
dictionaryStore.clearCache()
|
||||||
loadDictionaryList()
|
loadDictionaryList()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -483,7 +494,7 @@ const handleDictionarySuccess = () => {
|
|||||||
const handleItemSuccess = () => {
|
const handleItemSuccess = () => {
|
||||||
dialog.item = false
|
dialog.item = false
|
||||||
// 清理字典缓存
|
// 清理字典缓存
|
||||||
dictionaryCache.clearDictionary(selectedDictionaryId.value)
|
dictionaryStore.clearDictionary(selectedDictionaryId.value)
|
||||||
refreshTable()
|
refreshTable()
|
||||||
loadDictionaryList()
|
loadDictionaryList()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
|
|
||||||
<!-- 启用状态 -->
|
<!-- 启用状态 -->
|
||||||
<a-form-item label="启用状态" name="is_active">
|
<a-form-item label="启用状态" name="is_active">
|
||||||
<a-switch v-model:checked="isActiveChecked" checked-children="启用" un-checked-children="禁用" />
|
<sc-select v-model:value="form.is_active" source-type="dictionary" dictionary-code="yes_no" placeholder="请选择状态" allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<!-- 排序 -->
|
<!-- 排序 -->
|
||||||
@@ -93,6 +93,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
|
import scSelect from '@/components/scSelect/index.vue'
|
||||||
import systemApi from '@/api/system'
|
import systemApi from '@/api/system'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -125,42 +126,13 @@ const form = ref({
|
|||||||
expression: '* * * * *',
|
expression: '* * * * *',
|
||||||
timezone: 'Asia/Shanghai',
|
timezone: 'Asia/Shanghai',
|
||||||
description: '',
|
description: '',
|
||||||
is_active: true,
|
is_active: null,
|
||||||
run_in_background: false,
|
run_in_background: false,
|
||||||
without_overlapping: false,
|
without_overlapping: false,
|
||||||
only_one: false,
|
only_one: false,
|
||||||
sort: 0
|
sort: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
// 计算属性:开关
|
|
||||||
const isActiveChecked = computed({
|
|
||||||
get: () => form.value.is_active === true,
|
|
||||||
set: (val) => {
|
|
||||||
form.value.is_active = val ? true : false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const runInBackgroundChecked = computed({
|
|
||||||
get: () => form.value.run_in_background === true,
|
|
||||||
set: (val) => {
|
|
||||||
form.value.run_in_background = val ? true : false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const withoutOverlappingChecked = computed({
|
|
||||||
get: () => form.value.without_overlapping === true,
|
|
||||||
set: (val) => {
|
|
||||||
form.value.without_overlapping = val ? true : false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const onlyOneChecked = computed({
|
|
||||||
get: () => form.value.only_one === true,
|
|
||||||
set: (val) => {
|
|
||||||
form.value.only_one = val ? true : false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Cron 表达式验证函数
|
// Cron 表达式验证函数
|
||||||
const validateCronExpression = (rule, value) => {
|
const validateCronExpression = (rule, value) => {
|
||||||
if (!value || !value.trim()) {
|
if (!value || !value.trim()) {
|
||||||
@@ -304,7 +276,7 @@ const resetForm = () => {
|
|||||||
expression: '* * * * *',
|
expression: '* * * * *',
|
||||||
timezone: 'Asia/Shanghai',
|
timezone: 'Asia/Shanghai',
|
||||||
description: '',
|
description: '',
|
||||||
is_active: true,
|
is_active: null,
|
||||||
run_in_background: false,
|
run_in_background: false,
|
||||||
without_overlapping: false,
|
without_overlapping: false,
|
||||||
only_one: false,
|
only_one: false,
|
||||||
@@ -324,7 +296,7 @@ const setData = (data) => {
|
|||||||
expression: data.expression || '* * * * *',
|
expression: data.expression || '* * * * *',
|
||||||
timezone: data.timezone || 'Asia/Shanghai',
|
timezone: data.timezone || 'Asia/Shanghai',
|
||||||
description: data.description || '',
|
description: data.description || '',
|
||||||
is_active: data.is_active !== undefined ? data.is_active : true,
|
is_active: data.is_active !== undefined ? data.is_active : null,
|
||||||
run_in_background: data.run_in_background || false,
|
run_in_background: data.run_in_background || false,
|
||||||
without_overlapping: data.without_overlapping || false,
|
without_overlapping: data.without_overlapping || false,
|
||||||
only_one: data.only_one || false,
|
only_one: data.only_one || false,
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { customStorage } from '../persist'
|
||||||
|
import systemApi from '@/api/system'
|
||||||
|
|
||||||
|
export const useDictionaryStore = defineStore(
|
||||||
|
'dictionary',
|
||||||
|
() => {
|
||||||
|
// 字典数据缓存(按 code 缓存字典项列表)
|
||||||
|
const dictionaries = ref({})
|
||||||
|
// 字典元数据缓存(按 code 缓存字典信息)
|
||||||
|
const dictionaryMeta = ref({})
|
||||||
|
// 字典ID到Code的映射(用于通过ID清除缓存)
|
||||||
|
const dictionaryIdToCodeMap = ref({})
|
||||||
|
// 字典数据加载状态
|
||||||
|
const loading = ref(false)
|
||||||
|
// 最后加载时间
|
||||||
|
const lastLoadTime = ref(null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载所有字典数据
|
||||||
|
*/
|
||||||
|
async function loadAllDictionaries(forceRefresh = false) {
|
||||||
|
// 如果已加载且不是强制刷新,直接返回
|
||||||
|
if (!forceRefresh && Object.keys(dictionaries.value).length > 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loading.value) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await systemApi.dictionaryItems.all.get()
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
// 将字典数据按 code 缓存
|
||||||
|
const dictMap = {}
|
||||||
|
const metaMap = {}
|
||||||
|
|
||||||
|
res.data.forEach(dict => {
|
||||||
|
if (dict.code) {
|
||||||
|
// 缓存字典项列表
|
||||||
|
dictMap[dict.code] = dict.items || []
|
||||||
|
// 缓存字典元数据(包含id以便通过id清除缓存)
|
||||||
|
metaMap[dict.code] = {
|
||||||
|
id: dict.code, // 使用code作为唯一标识
|
||||||
|
name: dict.name,
|
||||||
|
code: dict.code,
|
||||||
|
description: dict.description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
dictionaries.value = dictMap
|
||||||
|
dictionaryMeta.value = metaMap
|
||||||
|
lastLoadTime.value = new Date().getTime()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载字典数据失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据字典 code 获取字典数据
|
||||||
|
* @param {string} code 字典编码
|
||||||
|
* @param {boolean} forceRefresh 是否强制刷新
|
||||||
|
* @returns {Array} 字典数据数组
|
||||||
|
*/
|
||||||
|
async function getDictionary(code, forceRefresh = false) {
|
||||||
|
// 如果缓存为空或强制刷新,则重新加载所有字典
|
||||||
|
if (!dictionaries.value || Object.keys(dictionaries.value).length === 0 || forceRefresh) {
|
||||||
|
await loadAllDictionaries()
|
||||||
|
}
|
||||||
|
|
||||||
|
return dictionaries.value[code] || []
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量获取字典数据
|
||||||
|
* @param {Array<string>} codes 字典编码数组
|
||||||
|
* @param {boolean} forceRefresh 是否强制刷新
|
||||||
|
* @returns {Object} 字典数据对象
|
||||||
|
*/
|
||||||
|
async function getDictionaries(codes, forceRefresh = false) {
|
||||||
|
// 如果缓存为空或强制刷新,则重新加载所有字典
|
||||||
|
if (!dictionaries.value || Object.keys(dictionaries.value).length === 0 || forceRefresh) {
|
||||||
|
await loadAllDictionaries()
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = {}
|
||||||
|
codes.forEach(code => {
|
||||||
|
result[code] = dictionaries.value[code] || []
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据字典 code 和 value 获取 label
|
||||||
|
* @param {string} code 字典编码
|
||||||
|
* @param {any} value 字典值
|
||||||
|
* @returns {string} 字典标签
|
||||||
|
*/
|
||||||
|
function getLabelByValue(code, value) {
|
||||||
|
const dict = dictionaries.value[code]
|
||||||
|
if (!dict) return value
|
||||||
|
|
||||||
|
const item = dict.find(item => item.value === value)
|
||||||
|
return item ? item.label : value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空字典缓存
|
||||||
|
*/
|
||||||
|
function clearCache() {
|
||||||
|
dictionaries.value = {}
|
||||||
|
dictionaryMeta.value = {}
|
||||||
|
dictionaryIdToCodeMap.value = {}
|
||||||
|
lastLoadTime.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除特定字典的缓存
|
||||||
|
* @param {string|number} dictionaryIdOrCode 字典ID或编码
|
||||||
|
*/
|
||||||
|
function clearDictionary(dictionaryIdOrCode) {
|
||||||
|
// 如果传入的是字典ID,需要先找到对应的code
|
||||||
|
let code = dictionaryIdOrCode
|
||||||
|
|
||||||
|
if (typeof dictionaryIdOrCode === 'number') {
|
||||||
|
// 直接从ID映射表中查找
|
||||||
|
code = dictionaryIdToCodeMap.value[dictionaryIdOrCode]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除对应字典的缓存
|
||||||
|
if (code && dictionaries.value[code]) {
|
||||||
|
delete dictionaries.value[code]
|
||||||
|
delete dictionaryMeta.value[code]
|
||||||
|
if (dictionaryIdOrCode && typeof dictionaryIdOrCode === 'number') {
|
||||||
|
delete dictionaryIdToCodeMap.value[dictionaryIdOrCode]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 建立字典ID到Code的映射
|
||||||
|
* @param {Array} dictionaryList 字典列表(包含id和code)
|
||||||
|
*/
|
||||||
|
function buildIdToCodeMap(dictionaryList) {
|
||||||
|
dictionaryList.forEach(dict => {
|
||||||
|
if (dict.id && dict.code) {
|
||||||
|
dictionaryIdToCodeMap.value[dict.id] = dict.code
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新字典缓存
|
||||||
|
* @param {boolean} force 是否强制刷新
|
||||||
|
*/
|
||||||
|
async function refresh(force = true) {
|
||||||
|
await loadAllDictionaries(force)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缓存信息
|
||||||
|
*/
|
||||||
|
function getCacheInfo() {
|
||||||
|
return {
|
||||||
|
count: Object.keys(dictionaries.value).length,
|
||||||
|
lastLoadTime: lastLoadTime.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
dictionaries,
|
||||||
|
dictionaryMeta,
|
||||||
|
dictionaryIdToCodeMap,
|
||||||
|
loading,
|
||||||
|
lastLoadTime,
|
||||||
|
loadAllDictionaries,
|
||||||
|
getDictionary,
|
||||||
|
getDictionaries,
|
||||||
|
getLabelByValue,
|
||||||
|
clearCache,
|
||||||
|
clearDictionary,
|
||||||
|
buildIdToCodeMap,
|
||||||
|
refresh,
|
||||||
|
getCacheInfo
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
persist: {
|
||||||
|
key: 'dictionary-store',
|
||||||
|
storage: customStorage,
|
||||||
|
pick: ['dictionaries', 'lastLoadTime']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
/**
|
|
||||||
* 数据字典缓存工具
|
|
||||||
* 用于缓存和管理数据字典数据
|
|
||||||
*/
|
|
||||||
|
|
||||||
import systemApi from '@/api/system'
|
|
||||||
|
|
||||||
// 缓存存储
|
|
||||||
const cacheStorage = new Map()
|
|
||||||
|
|
||||||
// 缓存过期时间(毫秒)默认1小时
|
|
||||||
const CACHE_EXPIRE_TIME = 3600 * 1000
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 字典缓存管理类
|
|
||||||
*/
|
|
||||||
class DictionaryCacheManager {
|
|
||||||
/**
|
|
||||||
* 获取所有字典(带缓存)
|
|
||||||
*/
|
|
||||||
async getAll() {
|
|
||||||
const cacheKey = 'all'
|
|
||||||
const cached = this.get(cacheKey)
|
|
||||||
|
|
||||||
if (cached) {
|
|
||||||
return cached
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await systemApi.dictionaries.all.get()
|
|
||||||
if (res.code === 200) {
|
|
||||||
const data = res.data || []
|
|
||||||
this.set(cacheKey, data)
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据编码获取字典项(带缓存)
|
|
||||||
*/
|
|
||||||
async getItemsByCode(code) {
|
|
||||||
const cacheKey = `items:${code}`
|
|
||||||
const cached = this.get(cacheKey)
|
|
||||||
|
|
||||||
if (cached) {
|
|
||||||
return cached
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await systemApi.public.dictionaries.code.get({ code })
|
|
||||||
if (res.code === 200) {
|
|
||||||
const data = res.data || []
|
|
||||||
this.set(cacheKey, data)
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据编码获取字典(带缓存)
|
|
||||||
*/
|
|
||||||
async getByCode(code) {
|
|
||||||
const cacheKey = `code:${code}`
|
|
||||||
const cached = this.get(cacheKey)
|
|
||||||
|
|
||||||
if (cached) {
|
|
||||||
return cached
|
|
||||||
}
|
|
||||||
|
|
||||||
const all = await this.getAll()
|
|
||||||
const dictionary = all.find((item) => item.code === code)
|
|
||||||
|
|
||||||
if (dictionary) {
|
|
||||||
this.set(cacheKey, dictionary)
|
|
||||||
}
|
|
||||||
|
|
||||||
return dictionary
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据编码获取字典项的标签
|
|
||||||
*/
|
|
||||||
async getLabelByCode(code, value) {
|
|
||||||
const items = await this.getItemsByCode(code)
|
|
||||||
const item = items.find((item) => item.value === value)
|
|
||||||
return item ? item.label : value
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清除所有缓存
|
|
||||||
*/
|
|
||||||
clear() {
|
|
||||||
cacheStorage.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清除指定缓存
|
|
||||||
*/
|
|
||||||
delete(key) {
|
|
||||||
cacheStorage.delete(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清除字典相关缓存
|
|
||||||
*/
|
|
||||||
clearDictionary(dictionaryId) {
|
|
||||||
// 清除特定字典的缓存(暂时清除所有,因为前端不知道字典ID对应的code)
|
|
||||||
this.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取缓存
|
|
||||||
*/
|
|
||||||
get(key) {
|
|
||||||
const item = cacheStorage.get(key)
|
|
||||||
|
|
||||||
if (!item) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否过期
|
|
||||||
if (Date.now() > item.expires) {
|
|
||||||
cacheStorage.delete(key)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return item.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置缓存
|
|
||||||
*/
|
|
||||||
set(key, data) {
|
|
||||||
const item = {
|
|
||||||
data,
|
|
||||||
expires: Date.now() + CACHE_EXPIRE_TIME
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheStorage.set(key, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建单例实例
|
|
||||||
const dictionaryCache = new DictionaryCacheManager()
|
|
||||||
|
|
||||||
export default dictionaryCache
|
|
||||||
+153
-148
@@ -7,167 +7,172 @@
|
|||||||
|
|
||||||
// 需要认证的路由
|
// 需要认证的路由
|
||||||
Route::middleware(['auth.check:admin', 'log.request'])->group(function () {
|
Route::middleware(['auth.check:admin', 'log.request'])->group(function () {
|
||||||
// 认证相关
|
// Auth 模块
|
||||||
Route::prefix('auth')->group(function () {
|
Route::prefix('auth')->group(function () {
|
||||||
|
// 认证相关
|
||||||
Route::post('/logout', [\App\Http\Controllers\Auth\Admin\Auth::class, 'logout']);
|
Route::post('/logout', [\App\Http\Controllers\Auth\Admin\Auth::class, 'logout']);
|
||||||
Route::post('/refresh', [\App\Http\Controllers\Auth\Admin\Auth::class, 'refresh']);
|
Route::post('/refresh', [\App\Http\Controllers\Auth\Admin\Auth::class, 'refresh']);
|
||||||
Route::get('/me', [\App\Http\Controllers\Auth\Admin\Auth::class, 'me']);
|
Route::get('/me', [\App\Http\Controllers\Auth\Admin\Auth::class, 'me']);
|
||||||
Route::post('/change-password', [\App\Http\Controllers\Auth\Admin\Auth::class, 'changePassword']);
|
Route::post('/change-password', [\App\Http\Controllers\Auth\Admin\Auth::class, 'changePassword']);
|
||||||
|
|
||||||
|
// 用户管理
|
||||||
|
Route::prefix('users')->group(function () {
|
||||||
|
Route::get('/', [\App\Http\Controllers\Auth\Admin\User::class, 'index']);
|
||||||
|
Route::get('/{id}', [\App\Http\Controllers\Auth\Admin\User::class, 'show']);
|
||||||
|
Route::post('/', [\App\Http\Controllers\Auth\Admin\User::class, 'store']);
|
||||||
|
Route::put('/{id}', [\App\Http\Controllers\Auth\Admin\User::class, 'update']);
|
||||||
|
Route::delete('/{id}', [\App\Http\Controllers\Auth\Admin\User::class, 'destroy']);
|
||||||
|
Route::post('/batch-delete', [\App\Http\Controllers\Auth\Admin\User::class, 'batchDelete']);
|
||||||
|
Route::post('/batch-status', [\App\Http\Controllers\Auth\Admin\User::class, 'batchUpdateStatus']);
|
||||||
|
Route::post('/batch-department', [\App\Http\Controllers\Auth\Admin\User::class, 'batchAssignDepartment']);
|
||||||
|
Route::post('/batch-roles', [\App\Http\Controllers\Auth\Admin\User::class, 'batchAssignRoles']);
|
||||||
|
Route::post('/export', [\App\Http\Controllers\Auth\Admin\User::class, 'export']);
|
||||||
|
Route::post('/import', [\App\Http\Controllers\Auth\Admin\User::class, 'import']);
|
||||||
|
Route::get('/download-template', [\App\Http\Controllers\Auth\Admin\User::class, 'downloadTemplate']);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 角色管理
|
||||||
|
Route::prefix('roles')->group(function () {
|
||||||
|
Route::get('/', [\App\Http\Controllers\Auth\Admin\Role::class, 'index']);
|
||||||
|
Route::get('/all', [\App\Http\Controllers\Auth\Admin\Role::class, 'getAll']);
|
||||||
|
Route::get('/{id}', [\App\Http\Controllers\Auth\Admin\Role::class, 'show']);
|
||||||
|
Route::post('/', [\App\Http\Controllers\Auth\Admin\Role::class, 'store']);
|
||||||
|
Route::put('/{id}', [\App\Http\Controllers\Auth\Admin\Role::class, 'update']);
|
||||||
|
Route::delete('/{id}', [\App\Http\Controllers\Auth\Admin\Role::class, 'destroy']);
|
||||||
|
Route::post('/batch-delete', [\App\Http\Controllers\Auth\Admin\Role::class, 'batchDelete']);
|
||||||
|
Route::post('/batch-status', [\App\Http\Controllers\Auth\Admin\Role::class, 'batchUpdateStatus']);
|
||||||
|
Route::post('/{id}/permissions', [\App\Http\Controllers\Auth\Admin\Role::class, 'assignPermissions']);
|
||||||
|
Route::get('/{id}/permissions', [\App\Http\Controllers\Auth\Admin\Role::class, 'getPermissions']);
|
||||||
|
Route::post('/{id}/copy', [\App\Http\Controllers\Auth\Admin\Role::class, 'copy']);
|
||||||
|
Route::post('/batch-copy', [\App\Http\Controllers\Auth\Admin\Role::class, 'batchCopy']);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 权限管理
|
||||||
|
Route::prefix('permissions')->group(function () {
|
||||||
|
Route::get('/', [\App\Http\Controllers\Auth\Admin\Permission::class, 'index']);
|
||||||
|
Route::get('/tree', [\App\Http\Controllers\Auth\Admin\Permission::class, 'tree']);
|
||||||
|
Route::get('/menu', [\App\Http\Controllers\Auth\Admin\Permission::class, 'menu']);
|
||||||
|
Route::get('/{id}', [\App\Http\Controllers\Auth\Admin\Permission::class, 'show']);
|
||||||
|
Route::post('/', [\App\Http\Controllers\Auth\Admin\Permission::class, 'store']);
|
||||||
|
Route::put('/{id}', [\App\Http\Controllers\Auth\Admin\Permission::class, 'update']);
|
||||||
|
Route::delete('/{id}', [\App\Http\Controllers\Auth\Admin\Permission::class, 'destroy']);
|
||||||
|
Route::post('/batch-delete', [\App\Http\Controllers\Auth\Admin\Permission::class, 'batchDelete']);
|
||||||
|
Route::post('/batch-status', [\App\Http\Controllers\Auth\Admin\Permission::class, 'batchUpdateStatus']);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 部门管理
|
||||||
|
Route::prefix('departments')->group(function () {
|
||||||
|
Route::get('/', [\App\Http\Controllers\Auth\Admin\Department::class, 'index']);
|
||||||
|
Route::get('/tree', [\App\Http\Controllers\Auth\Admin\Department::class, 'tree']);
|
||||||
|
Route::get('/all', [\App\Http\Controllers\Auth\Admin\Department::class, 'getAll']);
|
||||||
|
Route::get('/{id}', [\App\Http\Controllers\Auth\Admin\Department::class, 'show']);
|
||||||
|
Route::post('/', [\App\Http\Controllers\Auth\Admin\Department::class, 'store']);
|
||||||
|
Route::put('/{id}', [\App\Http\Controllers\Auth\Admin\Department::class, 'update']);
|
||||||
|
Route::delete('/{id}', [\App\Http\Controllers\Auth\Admin\Department::class, 'destroy']);
|
||||||
|
Route::post('/batch-delete', [\App\Http\Controllers\Auth\Admin\Department::class, 'batchDelete']);
|
||||||
|
Route::post('/batch-status', [\App\Http\Controllers\Auth\Admin\Department::class, 'batchUpdateStatus']);
|
||||||
|
Route::post('/export', [\App\Http\Controllers\Auth\Admin\Department::class, 'export']);
|
||||||
|
Route::post('/import', [\App\Http\Controllers\Auth\Admin\Department::class, 'import']);
|
||||||
|
Route::get('/download-template', [\App\Http\Controllers\Auth\Admin\Department::class, 'downloadTemplate']);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 在线用户管理
|
||||||
|
Route::prefix('online-users')->group(function () {
|
||||||
|
Route::get('/count', [\App\Http\Controllers\Auth\Admin\User::class, 'getOnlineCount']);
|
||||||
|
Route::get('/', [\App\Http\Controllers\Auth\Admin\User::class, 'getOnlineUsers']);
|
||||||
|
Route::get('/{userId}/sessions', [\App\Http\Controllers\Auth\Admin\User::class, 'getUserSessions']);
|
||||||
|
Route::post('/{userId}/offline', [\App\Http\Controllers\Auth\Admin\User::class, 'setUserOffline']);
|
||||||
|
Route::post('/{userId}/offline-all', [\App\Http\Controllers\Auth\Admin\User::class, 'setUserAllOffline']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 用户管理
|
// 系统管理模块
|
||||||
Route::prefix('users')->group(function () {
|
Route::prefix('system')->group(function () {
|
||||||
Route::get('/', [\App\Http\Controllers\Auth\Admin\User::class, 'index']);
|
// 系统配置管理
|
||||||
Route::get('/{id}', [\App\Http\Controllers\Auth\Admin\User::class, 'show']);
|
Route::prefix('configs')->group(function () {
|
||||||
Route::post('/', [\App\Http\Controllers\Auth\Admin\User::class, 'store']);
|
Route::get('/', [\App\Http\Controllers\System\Admin\Config::class, 'index']);
|
||||||
Route::put('/{id}', [\App\Http\Controllers\Auth\Admin\User::class, 'update']);
|
Route::get('/all', [\App\Http\Controllers\System\Admin\Config::class, 'getByGroup']);
|
||||||
Route::delete('/{id}', [\App\Http\Controllers\Auth\Admin\User::class, 'destroy']);
|
Route::get('/groups', [\App\Http\Controllers\System\Admin\Config::class, 'getGroups']);
|
||||||
Route::post('/batch-delete', [\App\Http\Controllers\Auth\Admin\User::class, 'batchDelete']);
|
Route::get('/{id}', [\App\Http\Controllers\System\Admin\Config::class, 'show']);
|
||||||
Route::post('/batch-status', [\App\Http\Controllers\Auth\Admin\User::class, 'batchUpdateStatus']);
|
Route::post('/', [\App\Http\Controllers\System\Admin\Config::class, 'store']);
|
||||||
Route::post('/batch-department', [\App\Http\Controllers\Auth\Admin\User::class, 'batchAssignDepartment']);
|
Route::put('/{id}', [\App\Http\Controllers\System\Admin\Config::class, 'update']);
|
||||||
Route::post('/batch-roles', [\App\Http\Controllers\Auth\Admin\User::class, 'batchAssignRoles']);
|
Route::delete('/{id}', [\App\Http\Controllers\System\Admin\Config::class, 'destroy']);
|
||||||
Route::post('/export', [\App\Http\Controllers\Auth\Admin\User::class, 'export']);
|
Route::post('/batch-delete', [\App\Http\Controllers\System\Admin\Config::class, 'batchDelete']);
|
||||||
Route::post('/import', [\App\Http\Controllers\Auth\Admin\User::class, 'import']);
|
Route::post('/batch-status', [\App\Http\Controllers\System\Admin\Config::class, 'batchUpdateStatus']);
|
||||||
Route::get('/download-template', [\App\Http\Controllers\Auth\Admin\User::class, 'downloadTemplate']);
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// 角色管理
|
// 系统操作日志
|
||||||
Route::prefix('roles')->group(function () {
|
Route::prefix('logs')->group(function () {
|
||||||
Route::get('/', [\App\Http\Controllers\Auth\Admin\Role::class, 'index']);
|
Route::get('/', [\App\Http\Controllers\System\Admin\Log::class, 'index']);
|
||||||
Route::get('/all', [\App\Http\Controllers\Auth\Admin\Role::class, 'getAll']);
|
Route::get('/export', [\App\Http\Controllers\System\Admin\Log::class, 'export']);
|
||||||
Route::get('/{id}', [\App\Http\Controllers\Auth\Admin\Role::class, 'show']);
|
Route::get('/statistics', [\App\Http\Controllers\System\Admin\Log::class, 'getStatistics']);
|
||||||
Route::post('/', [\App\Http\Controllers\Auth\Admin\Role::class, 'store']);
|
Route::get('/{id}', [\App\Http\Controllers\System\Admin\Log::class, 'show']);
|
||||||
Route::put('/{id}', [\App\Http\Controllers\Auth\Admin\Role::class, 'update']);
|
Route::delete('/{id}', [\App\Http\Controllers\System\Admin\Log::class, 'destroy']);
|
||||||
Route::delete('/{id}', [\App\Http\Controllers\Auth\Admin\Role::class, 'destroy']);
|
Route::post('/batch-delete', [\App\Http\Controllers\System\Admin\Log::class, 'batchDelete']);
|
||||||
Route::post('/batch-delete', [\App\Http\Controllers\Auth\Admin\Role::class, 'batchDelete']);
|
Route::post('/clear', [\App\Http\Controllers\System\Admin\Log::class, 'clearLogs']);
|
||||||
Route::post('/batch-status', [\App\Http\Controllers\Auth\Admin\Role::class, 'batchUpdateStatus']);
|
});
|
||||||
Route::post('/{id}/permissions', [\App\Http\Controllers\Auth\Admin\Role::class, 'assignPermissions']);
|
|
||||||
Route::get('/{id}/permissions', [\App\Http\Controllers\Auth\Admin\Role::class, 'getPermissions']);
|
|
||||||
Route::post('/{id}/copy', [\App\Http\Controllers\Auth\Admin\Role::class, 'copy']);
|
|
||||||
Route::post('/batch-copy', [\App\Http\Controllers\Auth\Admin\Role::class, 'batchCopy']);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 权限管理
|
// 数据字典管理
|
||||||
Route::prefix('permissions')->group(function () {
|
Route::prefix('dictionaries')->group(function () {
|
||||||
Route::get('/', [\App\Http\Controllers\Auth\Admin\Permission::class, 'index']);
|
Route::get('/', [\App\Http\Controllers\System\Admin\Dictionary::class, 'index']);
|
||||||
Route::get('/tree', [\App\Http\Controllers\Auth\Admin\Permission::class, 'tree']);
|
Route::get('/all', [\App\Http\Controllers\System\Admin\Dictionary::class, 'all']);
|
||||||
Route::get('/menu', [\App\Http\Controllers\Auth\Admin\Permission::class, 'menu']);
|
Route::get('/{id}', [\App\Http\Controllers\System\Admin\Dictionary::class, 'show']);
|
||||||
Route::get('/{id}', [\App\Http\Controllers\Auth\Admin\Permission::class, 'show']);
|
Route::post('/', [\App\Http\Controllers\System\Admin\Dictionary::class, 'store']);
|
||||||
Route::post('/', [\App\Http\Controllers\Auth\Admin\Permission::class, 'store']);
|
Route::put('/{id}', [\App\Http\Controllers\System\Admin\Dictionary::class, 'update']);
|
||||||
Route::put('/{id}', [\App\Http\Controllers\Auth\Admin\Permission::class, 'update']);
|
Route::delete('/{id}', [\App\Http\Controllers\System\Admin\Dictionary::class, 'destroy']);
|
||||||
Route::delete('/{id}', [\App\Http\Controllers\Auth\Admin\Permission::class, 'destroy']);
|
Route::post('/batch-delete', [\App\Http\Controllers\System\Admin\Dictionary::class, 'batchDelete']);
|
||||||
Route::post('/batch-delete', [\App\Http\Controllers\Auth\Admin\Permission::class, 'batchDelete']);
|
Route::post('/batch-status', [\App\Http\Controllers\System\Admin\Dictionary::class, 'batchUpdateStatus']);
|
||||||
Route::post('/batch-status', [\App\Http\Controllers\Auth\Admin\Permission::class, 'batchUpdateStatus']);
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// 部门管理
|
// 数据字典项管理
|
||||||
Route::prefix('departments')->group(function () {
|
Route::prefix('dictionary-items')->group(function () {
|
||||||
Route::get('/', [\App\Http\Controllers\Auth\Admin\Department::class, 'index']);
|
Route::get('/', [\App\Http\Controllers\System\Admin\Dictionary::class, 'getItemsList']);
|
||||||
Route::get('/tree', [\App\Http\Controllers\Auth\Admin\Department::class, 'tree']);
|
Route::get('/all', [\App\Http\Controllers\System\Admin\Dictionary::class, 'getAllItems']);
|
||||||
Route::get('/all', [\App\Http\Controllers\Auth\Admin\Department::class, 'getAll']);
|
Route::post('/', [\App\Http\Controllers\System\Admin\Dictionary::class, 'storeItem']);
|
||||||
Route::get('/{id}', [\App\Http\Controllers\Auth\Admin\Department::class, 'show']);
|
Route::put('/{id}', [\App\Http\Controllers\System\Admin\Dictionary::class, 'updateItem']);
|
||||||
Route::post('/', [\App\Http\Controllers\Auth\Admin\Department::class, 'store']);
|
Route::delete('/{id}', [\App\Http\Controllers\System\Admin\Dictionary::class, 'destroyItem']);
|
||||||
Route::put('/{id}', [\App\Http\Controllers\Auth\Admin\Department::class, 'update']);
|
Route::post('/batch-delete', [\App\Http\Controllers\System\Admin\Dictionary::class, 'batchDeleteItems']);
|
||||||
Route::delete('/{id}', [\App\Http\Controllers\Auth\Admin\Department::class, 'destroy']);
|
Route::post('/batch-status', [\App\Http\Controllers\System\Admin\Dictionary::class, 'batchUpdateItemsStatus']);
|
||||||
Route::post('/batch-delete', [\App\Http\Controllers\Auth\Admin\Department::class, 'batchDelete']);
|
});
|
||||||
Route::post('/batch-status', [\App\Http\Controllers\Auth\Admin\Department::class, 'batchUpdateStatus']);
|
|
||||||
Route::post('/export', [\App\Http\Controllers\Auth\Admin\Department::class, 'export']);
|
|
||||||
Route::post('/import', [\App\Http\Controllers\Auth\Admin\Department::class, 'import']);
|
|
||||||
Route::get('/download-template', [\App\Http\Controllers\Auth\Admin\Department::class, 'downloadTemplate']);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 在线用户管理
|
// 任务管理
|
||||||
Route::prefix('online-users')->group(function () {
|
Route::prefix('tasks')->group(function () {
|
||||||
Route::get('/count', [\App\Http\Controllers\Auth\Admin\User::class, 'getOnlineCount']);
|
Route::get('/', [\App\Http\Controllers\System\Admin\Task::class, 'index']);
|
||||||
Route::get('/', [\App\Http\Controllers\Auth\Admin\User::class, 'getOnlineUsers']);
|
Route::get('/all', [\App\Http\Controllers\System\Admin\Task::class, 'all']);
|
||||||
Route::get('/{userId}/sessions', [\App\Http\Controllers\Auth\Admin\User::class, 'getUserSessions']);
|
Route::get('/statistics', [\App\Http\Controllers\System\Admin\Task::class, 'getStatistics']);
|
||||||
Route::post('/{userId}/offline', [\App\Http\Controllers\Auth\Admin\User::class, 'setUserOffline']);
|
Route::get('/{id}', [\App\Http\Controllers\System\Admin\Task::class, 'show']);
|
||||||
Route::post('/{userId}/offline-all', [\App\Http\Controllers\Auth\Admin\User::class, 'setUserAllOffline']);
|
Route::post('/', [\App\Http\Controllers\System\Admin\Task::class, 'store']);
|
||||||
});
|
Route::put('/{id}', [\App\Http\Controllers\System\Admin\Task::class, 'update']);
|
||||||
|
Route::delete('/{id}', [\App\Http\Controllers\System\Admin\Task::class, 'destroy']);
|
||||||
|
Route::post('/batch-delete', [\App\Http\Controllers\System\Admin\Task::class, 'batchDelete']);
|
||||||
|
Route::post('/batch-status', [\App\Http\Controllers\System\Admin\Task::class, 'batchUpdateStatus']);
|
||||||
|
Route::post('/{id}/run', [\App\Http\Controllers\System\Admin\Task::class, 'run']);
|
||||||
|
});
|
||||||
|
|
||||||
// 系统配置管理
|
// 城市数据管理
|
||||||
Route::prefix('configs')->group(function () {
|
Route::prefix('cities')->group(function () {
|
||||||
Route::get('/', [\App\Http\Controllers\System\Admin\Config::class, 'index']);
|
Route::get('/', [\App\Http\Controllers\System\Admin\City::class, 'index']);
|
||||||
Route::get('/all', [\App\Http\Controllers\System\Admin\Config::class, 'getByGroup']);
|
Route::get('/tree', [\App\Http\Controllers\System\Admin\City::class, 'tree']);
|
||||||
Route::get('/groups', [\App\Http\Controllers\System\Admin\Config::class, 'getGroups']);
|
Route::get('/{id}', [\App\Http\Controllers\System\Admin\City::class, 'show']);
|
||||||
Route::get('/{id}', [\App\Http\Controllers\System\Admin\Config::class, 'show']);
|
Route::get('/{id}/children', [\App\Http\Controllers\System\Admin\City::class, 'children']);
|
||||||
Route::post('/', [\App\Http\Controllers\System\Admin\Config::class, 'store']);
|
Route::get('/provinces', [\App\Http\Controllers\System\Admin\City::class, 'provinces']);
|
||||||
Route::put('/{id}', [\App\Http\Controllers\System\Admin\Config::class, 'update']);
|
Route::get('/{provinceId}/cities', [\App\Http\Controllers\System\Admin\City::class, 'cities']);
|
||||||
Route::delete('/{id}', [\App\Http\Controllers\System\Admin\Config::class, 'destroy']);
|
Route::get('/{cityId}/districts', [\App\Http\Controllers\System\Admin\City::class, 'districts']);
|
||||||
Route::post('/batch-delete', [\App\Http\Controllers\System\Admin\Config::class, 'batchDelete']);
|
Route::post('/', [\App\Http\Controllers\System\Admin\City::class, 'store']);
|
||||||
Route::post('/batch-status', [\App\Http\Controllers\System\Admin\Config::class, 'batchUpdateStatus']);
|
Route::put('/{id}', [\App\Http\Controllers\System\Admin\City::class, 'update']);
|
||||||
});
|
Route::delete('/{id}', [\App\Http\Controllers\System\Admin\City::class, 'destroy']);
|
||||||
|
Route::post('/batch-delete', [\App\Http\Controllers\System\Admin\City::class, 'batchDelete']);
|
||||||
|
Route::post('/batch-status', [\App\Http\Controllers\System\Admin\City::class, 'batchUpdateStatus']);
|
||||||
|
});
|
||||||
|
|
||||||
// 系统操作日志
|
// 文件上传管理
|
||||||
Route::prefix('logs')->group(function () {
|
Route::prefix('upload')->group(function () {
|
||||||
Route::get('/', [\App\Http\Controllers\System\Admin\Log::class, 'index']);
|
Route::post('/', [\App\Http\Controllers\System\Admin\Upload::class, 'upload']);
|
||||||
Route::get('/export', [\App\Http\Controllers\System\Admin\Log::class, 'export']);
|
Route::post('/multiple', [\App\Http\Controllers\System\Admin\Upload::class, 'uploadMultiple']);
|
||||||
Route::get('/statistics', [\App\Http\Controllers\System\Admin\Log::class, 'getStatistics']);
|
Route::post('/base64', [\App\Http\Controllers\System\Admin\Upload::class, 'uploadBase64']);
|
||||||
Route::get('/{id}', [\App\Http\Controllers\System\Admin\Log::class, 'show']);
|
Route::post('/delete', [\App\Http\Controllers\System\Admin\Upload::class, 'delete']);
|
||||||
Route::delete('/{id}', [\App\Http\Controllers\System\Admin\Log::class, 'destroy']);
|
Route::post('/batch-delete', [\App\Http\Controllers\System\Admin\Upload::class, 'batchDelete']);
|
||||||
Route::post('/batch-delete', [\App\Http\Controllers\System\Admin\Log::class, 'batchDelete']);
|
});
|
||||||
Route::post('/clear', [\App\Http\Controllers\System\Admin\Log::class, 'clearLogs']);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 数据字典管理
|
|
||||||
Route::prefix('dictionaries')->group(function () {
|
|
||||||
Route::get('/', [\App\Http\Controllers\System\Admin\Dictionary::class, 'index']);
|
|
||||||
Route::get('/all', [\App\Http\Controllers\System\Admin\Dictionary::class, 'all']);
|
|
||||||
Route::get('/{id}', [\App\Http\Controllers\System\Admin\Dictionary::class, 'show']);
|
|
||||||
Route::post('/', [\App\Http\Controllers\System\Admin\Dictionary::class, 'store']);
|
|
||||||
Route::put('/{id}', [\App\Http\Controllers\System\Admin\Dictionary::class, 'update']);
|
|
||||||
Route::delete('/{id}', [\App\Http\Controllers\System\Admin\Dictionary::class, 'destroy']);
|
|
||||||
Route::post('/batch-delete', [\App\Http\Controllers\System\Admin\Dictionary::class, 'batchDelete']);
|
|
||||||
Route::post('/batch-status', [\App\Http\Controllers\System\Admin\Dictionary::class, 'batchUpdateStatus']);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 数据字典项管理
|
|
||||||
Route::prefix('dictionary-items')->group(function () {
|
|
||||||
Route::get('/', [\App\Http\Controllers\System\Admin\Dictionary::class, 'getItemsList']);
|
|
||||||
Route::post('/', [\App\Http\Controllers\System\Admin\Dictionary::class, 'storeItem']);
|
|
||||||
Route::put('/{id}', [\App\Http\Controllers\System\Admin\Dictionary::class, 'updateItem']);
|
|
||||||
Route::delete('/{id}', [\App\Http\Controllers\System\Admin\Dictionary::class, 'destroyItem']);
|
|
||||||
Route::post('/batch-delete', [\App\Http\Controllers\System\Admin\Dictionary::class, 'batchDeleteItems']);
|
|
||||||
Route::post('/batch-status', [\App\Http\Controllers\System\Admin\Dictionary::class, 'batchUpdateItemsStatus']);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 任务管理
|
|
||||||
Route::prefix('tasks')->group(function () {
|
|
||||||
Route::get('/', [\App\Http\Controllers\System\Admin\Task::class, 'index']);
|
|
||||||
Route::get('/all', [\App\Http\Controllers\System\Admin\Task::class, 'all']);
|
|
||||||
Route::get('/statistics', [\App\Http\Controllers\System\Admin\Task::class, 'getStatistics']);
|
|
||||||
Route::get('/{id}', [\App\Http\Controllers\System\Admin\Task::class, 'show']);
|
|
||||||
Route::post('/', [\App\Http\Controllers\System\Admin\Task::class, 'store']);
|
|
||||||
Route::put('/{id}', [\App\Http\Controllers\System\Admin\Task::class, 'update']);
|
|
||||||
Route::delete('/{id}', [\App\Http\Controllers\System\Admin\Task::class, 'destroy']);
|
|
||||||
Route::post('/batch-delete', [\App\Http\Controllers\System\Admin\Task::class, 'batchDelete']);
|
|
||||||
Route::post('/batch-status', [\App\Http\Controllers\System\Admin\Task::class, 'batchUpdateStatus']);
|
|
||||||
Route::post('/{id}/run', [\App\Http\Controllers\System\Admin\Task::class, 'run']);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 城市数据管理
|
|
||||||
Route::prefix('cities')->group(function () {
|
|
||||||
Route::get('/', [\App\Http\Controllers\System\Admin\City::class, 'index']);
|
|
||||||
Route::get('/tree', [\App\Http\Controllers\System\Admin\City::class, 'tree']);
|
|
||||||
Route::get('/{id}', [\App\Http\Controllers\System\Admin\City::class, 'show']);
|
|
||||||
Route::get('/{id}/children', [\App\Http\Controllers\System\Admin\City::class, 'children']);
|
|
||||||
Route::get('/provinces', [\App\Http\Controllers\System\Admin\City::class, 'provinces']);
|
|
||||||
Route::get('/{provinceId}/cities', [\App\Http\Controllers\System\Admin\City::class, 'cities']);
|
|
||||||
Route::get('/{cityId}/districts', [\App\Http\Controllers\System\Admin\City::class, 'districts']);
|
|
||||||
Route::post('/', [\App\Http\Controllers\System\Admin\City::class, 'store']);
|
|
||||||
Route::put('/{id}', [\App\Http\Controllers\System\Admin\City::class, 'update']);
|
|
||||||
Route::delete('/{id}', [\App\Http\Controllers\System\Admin\City::class, 'destroy']);
|
|
||||||
Route::post('/batch-delete', [\App\Http\Controllers\System\Admin\City::class, 'batchDelete']);
|
|
||||||
Route::post('/batch-status', [\App\Http\Controllers\System\Admin\City::class, 'batchUpdateStatus']);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 文件上传管理
|
|
||||||
Route::prefix('upload')->group(function () {
|
|
||||||
Route::post('/', [\App\Http\Controllers\System\Admin\Upload::class, 'upload']);
|
|
||||||
Route::post('/multiple', [\App\Http\Controllers\System\Admin\Upload::class, 'uploadMultiple']);
|
|
||||||
Route::post('/base64', [\App\Http\Controllers\System\Admin\Upload::class, 'uploadBase64']);
|
|
||||||
Route::post('/delete', [\App\Http\Controllers\System\Admin\Upload::class, 'delete']);
|
|
||||||
Route::post('/batch-delete', [\App\Http\Controllers\System\Admin\Upload::class, 'batchDelete']);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// WebSocket 管理
|
// WebSocket 管理
|
||||||
|
|||||||
@@ -1,3 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use App\Http\Controllers\System\Api\Dictionary;
|
||||||
|
|
||||||
|
// 公开路由(不需要认证)
|
||||||
|
Route::prefix('system')->group(function () {
|
||||||
|
// 获取所有系统配置
|
||||||
|
Route::get('/configs', [\App\Http\Controllers\System\Api\Config::class, 'all']);
|
||||||
|
Route::get('/configs/group', [\App\Http\Controllers\System\Api\Config::class, 'getByGroup']);
|
||||||
|
Route::get('/configs/key', [\App\Http\Controllers\System\Api\Config::class, 'getByKey']);
|
||||||
|
|
||||||
|
// 获取所有字典数据(用于前端缓存)
|
||||||
|
Route::get('/dictionaries', [Dictionary::class, 'all']);
|
||||||
|
Route::get('/dictionaries/code', [Dictionary::class, 'getByCode']);
|
||||||
|
Route::get('/dictionaries/{id}', [Dictionary::class, 'show']);
|
||||||
|
|
||||||
|
// 获取城市数据
|
||||||
|
Route::get('/cities/tree', [\App\Http\Controllers\System\Api\City::class, 'tree']);
|
||||||
|
Route::get('/cities/provinces', [\App\Http\Controllers\System\Api\City::class, 'provinces']);
|
||||||
|
Route::get('/cities/{provinceId}/cities', [\App\Http\Controllers\System\Api\City::class, 'cities']);
|
||||||
|
Route::get('/cities/{cityId}/districts', [\App\Http\Controllers\System\Api\City::class, 'districts']);
|
||||||
|
Route::get('/cities/{id}', [\App\Http\Controllers\System\Api\City::class, 'show']);
|
||||||
|
|
||||||
|
// 文件上传
|
||||||
|
Route::post('/upload', [\App\Http\Controllers\System\Api\Upload::class, 'upload']);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user