diff --git a/app/Http/Controllers/System/Admin/Dictionary.php b/app/Http/Controllers/System/Admin/Dictionary.php index 794f5ee..76f3e46 100644 --- a/app/Http/Controllers/System/Admin/Dictionary.php +++ b/app/Http/Controllers/System/Admin/Dictionary.php @@ -140,6 +140,16 @@ class Dictionary extends Controller ]); } + public function getAllItems() + { + $items = $this->dictionaryService->getAllItems(); + return response()->json([ + 'code' => 200, + 'message' => 'success', + 'data' => $items + ]); + } + public function storeItem(Request $request) { try { diff --git a/app/Http/Controllers/System/Api/Dictionary.php b/app/Http/Controllers/System/Api/Dictionary.php index 28a3182..503e7ad 100644 --- a/app/Http/Controllers/System/Api/Dictionary.php +++ b/app/Http/Controllers/System/Api/Dictionary.php @@ -25,6 +25,28 @@ class Dictionary extends Controller ]); } + /** + * 获取所有字典数据(包含字典项) + * 用于前端登录后缓存所有字典数据 + */ + 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) { $code = $request->input('code'); diff --git a/app/Services/Auth/AuthService.php b/app/Services/Auth/AuthService.php index 71f99c6..40854a7 100644 --- a/app/Services/Auth/AuthService.php +++ b/app/Services/Auth/AuthService.php @@ -210,7 +210,7 @@ class AuthService 'path' => $permission->path, 'name' => $permission->name, 'title' => $permission->title, - 'meta' => $permission->meta ? json_decode($permission->meta, true) : [], + 'meta' => $permission->meta ?: [], ]; // 添加组件路径 diff --git a/app/Services/System/DictionaryService.php b/app/Services/System/DictionaryService.php index 0dea17d..2a5ef42 100644 --- a/app/Services/System/DictionaryService.php +++ b/app/Services/System/DictionaryService.php @@ -43,6 +43,7 @@ class DictionaryService if ($dictionaries === null) { $dictionaries = Dictionary::where('status', true) + ->with(['activeItems']) ->orderBy('sort') ->get() ->toArray(); @@ -52,7 +53,7 @@ class DictionaryService return $dictionaries; } - public function getById(int $id): ?Dictionary + public function getById(int $id): ?array { $cacheKey = 'system:dictionaries:' . $id; $dictionary = Cache::get($cacheKey); @@ -60,22 +61,26 @@ class DictionaryService if ($dictionary === null) { $dictionary = Dictionary::with('items')->find($id); 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; $dictionary = Cache::get($cacheKey); if ($dictionary === null) { - $dictionary = Dictionary::where('code', $code)->first(); - if ($dictionary) { - Cache::put($cacheKey, $dictionary->toArray(), 3600); + $dictionaryModel = Dictionary::where('code', $code)->first(); + if ($dictionaryModel) { + $dictionary = $dictionaryModel->toArray(); + Cache::put($cacheKey, $dictionary, 3600); + } else { + $dictionary = null; } } @@ -269,4 +274,36 @@ class DictionaryService 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; + } } diff --git a/resources/admin/src/api/auth.js b/resources/admin/src/api/auth.js index 793f0c9..e352454 100644 --- a/resources/admin/src/api/auth.js +++ b/resources/admin/src/api/auth.js @@ -28,7 +28,7 @@ export default { post: async function (file) { const formData = new FormData() formData.append('file', file) - return await request.post('upload', formData, { + return await request.post('system/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' } }) }, @@ -38,64 +38,64 @@ export default { users: { list: { get: async function (params) { - return await request.get('users', { params }) + return await request.get('auth/users', { params }) }, }, detail: { get: async function (id) { - return await request.get(`users/${id}`) + return await request.get(`auth/users/${id}`) }, }, add: { post: async function (params) { - return await request.post('users', params) + return await request.post('auth/users', params) }, }, edit: { put: async function (id, params) { - return await request.put(`users/${id}`, params) + return await request.put(`auth/users/${id}`, params) }, }, delete: { delete: async function (id) { - return await request.delete(`users/${id}`) + return await request.delete(`auth/users/${id}`) }, }, batchDelete: { post: async function (params) { - return await request.post('users/batch-delete', params) + return await request.post('auth/users/batch-delete', params) }, }, batchStatus: { post: async function (params) { - return await request.post('users/batch-status', params) + return await request.post('auth/users/batch-status', params) }, }, batchDepartment: { post: async function (params) { - return await request.post('users/batch-department', params) + return await request.post('auth/users/batch-department', params) }, }, batchRoles: { post: async function (params) { - return await request.post('users/batch-roles', params) + return await request.post('auth/users/batch-roles', params) }, }, export: { post: async function (params) { - return await request.post('users/export', params, { responseType: 'blob' }) + return await request.post('auth/users/export', params, { responseType: 'blob' }) }, }, import: { 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' } }) }, }, downloadTemplate: { 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: { count: { get: async function () { - return await request.get('online-users/count') + return await request.get('auth/online-users/count') }, }, list: { get: async function (params) { - return await request.get('online-users', { params }) + return await request.get('auth/online-users', { params }) }, }, sessions: { get: async function (userId) { - return await request.get(`online-users/${userId}/sessions`) + return await request.get(`auth/online-users/${userId}/sessions`) }, }, offline: { 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: { 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: { list: { get: async function (params) { - return await request.get('roles', { params }) + return await request.get('auth/roles', { params }) }, }, all: { get: async function () { - return await request.get('roles/all') + return await request.get('auth/roles/all') }, }, detail: { get: async function (id) { - return await request.get(`roles/${id}`) + return await request.get(`auth/roles/${id}`) }, }, add: { post: async function (params) { - return await request.post('roles', params) + return await request.post('auth/roles', params) }, }, edit: { put: async function (id, params) { - return await request.put(`roles/${id}`, params) + return await request.put(`auth/roles/${id}`, params) }, }, delete: { delete: async function (id) { - return await request.delete(`roles/${id}`) + return await request.delete(`auth/roles/${id}`) }, }, batchDelete: { post: async function (params) { - return await request.post('roles/batch-delete', params) + return await request.post('auth/roles/batch-delete', params) }, }, batchStatus: { post: async function (params) { - return await request.post('roles/batch-status', params) + return await request.post('auth/roles/batch-status', params) }, }, permissions: { get: async function (id) { - return await request.get(`roles/${id}/permissions`) + return await request.get(`auth/roles/${id}/permissions`) }, post: async function (id, params) { - return await request.post(`roles/${id}/permissions`, params) + return await request.post(`auth/roles/${id}/permissions`, params) }, }, copy: { post: async function (id, params) { - return await request.post(`roles/${id}/copy`, params) + return await request.post(`auth/roles/${id}/copy`, params) }, }, batchCopy: { post: async function (params) { - return await request.post('roles/batch-copy', params) + return await request.post('auth/roles/batch-copy', params) }, }, export: { post: async function (params) { - return await request.post('roles/export', params, { responseType: 'blob' }) + return await request.post('auth/roles/export', params, { responseType: 'blob' }) }, }, import: { 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' } }) }, }, downloadTemplate: { 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: { list: { get: async function (params) { - return await request.get('permissions', { params }) + return await request.get('auth/permissions', { params }) }, }, tree: { get: async function () { - return await request.get('permissions/tree') + return await request.get('auth/permissions/tree') }, }, menu: { get: async function () { - return await request.get('permissions/menu') + return await request.get('auth/permissions/menu') }, }, detail: { get: async function (id) { - return await request.get(`permissions/${id}`) + return await request.get(`auth/permissions/${id}`) }, }, add: { post: async function (params) { - return await request.post('permissions', params) + return await request.post('auth/permissions', params) }, }, edit: { put: async function (id, params) { - return await request.put(`permissions/${id}`, params) + return await request.put(`auth/permissions/${id}`, params) }, }, delete: { delete: async function (id) { - return await request.delete(`permissions/${id}`) + return await request.delete(`auth/permissions/${id}`) }, }, batchDelete: { post: async function (params) { - return await request.post('permissions/batch-delete', params) + return await request.post('auth/permissions/batch-delete', params) }, }, batchStatus: { post: async function (params) { - return await request.post('permissions/batch-status', params) + return await request.post('auth/permissions/batch-status', params) }, }, export: { post: async function (params) { - return await request.post('permissions/export', params, { responseType: 'blob' }) + return await request.post('auth/permissions/export', params, { responseType: 'blob' }) }, }, import: { 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' } }) }, }, downloadTemplate: { 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: { list: { get: async function (params) { - return await request.get('departments', { params }) + return await request.get('auth/departments', { params }) }, }, tree: { get: async function (params) { - return await request.get('departments/tree', { params }) + return await request.get('auth/departments/tree', { params }) }, }, all: { get: async function () { - return await request.get('departments/all') + return await request.get('auth/departments/all') }, }, detail: { get: async function (id) { - return await request.get(`departments/${id}`) + return await request.get(`auth/departments/${id}`) }, }, add: { post: async function (params) { - return await request.post('departments', params) + return await request.post('auth/departments', params) }, }, edit: { put: async function (id, params) { - return await request.put(`departments/${id}`, params) + return await request.put(`auth/departments/${id}`, params) }, }, delete: { delete: async function (id) { - return await request.delete(`departments/${id}`) + return await request.delete(`auth/departments/${id}`) }, }, batchDelete: { post: async function (params) { - return await request.post('departments/batch-delete', params) + return await request.post('auth/departments/batch-delete', params) }, }, batchStatus: { post: async function (params) { - return await request.post('departments/batch-status', params) + return await request.post('auth/departments/batch-status', params) }, }, export: { post: async function (params) { - return await request.post('departments/export', params, { responseType: 'blob' }) + return await request.post('auth/departments/export', params, { responseType: 'blob' }) }, }, import: { 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' } }) }, }, downloadTemplate: { get: async function () { - return await request.get('departments/download-template', { responseType: 'blob' }) + return await request.get('auth/departments/download-template', { responseType: 'blob' }) }, }, }, diff --git a/resources/admin/src/api/system.js b/resources/admin/src/api/system.js index 602c1f4..ec99c69 100644 --- a/resources/admin/src/api/system.js +++ b/resources/admin/src/api/system.js @@ -5,47 +5,47 @@ export default { configs: { list: { get: async function (params) { - return await request.get('configs', { params }) + return await request.get('system/configs', { params }) }, }, groups: { get: async function () { - return await request.get('configs/groups') + return await request.get('system/configs/groups') }, }, all: { get: async function (params) { - return await request.get('configs/all', { params }) + return await request.get('system/configs/all', { params }) }, }, detail: { get: async function (id) { - return await request.get(`configs/${id}`) + return await request.get(`system/configs/${id}`) }, }, add: { post: async function (params) { - return await request.post('configs', params) + return await request.post('system/configs', params) }, }, edit: { put: async function (id, params) { - return await request.put(`configs/${id}`, params) + return await request.put(`system/configs/${id}`, params) }, }, delete: { delete: async function (id) { - return await request.delete(`configs/${id}`) + return await request.delete(`system/configs/${id}`) }, }, batchDelete: { post: async function (params) { - return await request.post('configs/batch-delete', params) + return await request.post('system/configs/batch-delete', params) }, }, batchStatus: { 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: { list: { get: async function (params) { - return await request.get('logs', { params }) + return await request.get('system/logs', { params }) }, }, detail: { get: async function (id) { - return await request.get(`logs/${id}`) + return await request.get(`system/logs/${id}`) }, }, delete: { delete: async function (id) { - return await request.delete(`logs/${id}`) + return await request.delete(`system/logs/${id}`) }, }, batchDelete: { post: async function (params) { - return await request.post('logs/batch-delete', params) + return await request.post('system/logs/batch-delete', params) }, }, clear: { post: async function (params) { - return await request.post('logs/clear', params) + return await request.post('system/logs/clear', params) }, }, export: { get: async function (params) { - return await request.get('logs/export', { + return await request.get('system/logs/export', { params, responseType: 'blob' }) @@ -87,90 +87,102 @@ export default { }, statistics: { get: async function (params) { - return await request.get('logs/statistics', { params }) + return await request.get('system/logs/statistics', { params }) }, }, }, - // 数据字典管理 - dictionaries: { - list: { - get: async function (params) { - return await request.get('dictionaries', { params }) + // 数据字典管理 + dictionaries: { + list: { + get: async function (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: { list: { 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: { get: async function (id) { - return await request.get(`dictionary-items/${id}`) + return await request.get(`system/dictionary-items/${id}`) }, }, add: { post: async function (params) { - return await request.post('dictionary-items', params) + return await request.post('system/dictionary-items', params) }, }, edit: { put: async function (id, params) { - return await request.put(`dictionary-items/${id}`, params) + return await request.put(`system/dictionary-items/${id}`, params) }, }, delete: { delete: async function (id) { - return await request.delete(`dictionary-items/${id}`) + return await request.delete(`system/dictionary-items/${id}`) }, }, batchDelete: { post: async function (params) { - return await request.post('dictionary-items/batch-delete', params) + return await request.post('system/dictionary-items/batch-delete', params) }, }, batchStatus: { 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: { list: { get: async function (params) { - return await request.get('tasks', { params }) + return await request.get('system/tasks', { params }) }, }, all: { get: async function () { - return await request.get('tasks/all') + return await request.get('system/tasks/all') }, }, detail: { get: async function (id) { - return await request.get(`tasks/${id}`) + return await request.get(`system/tasks/${id}`) }, }, add: { post: async function (params) { - return await request.post('tasks', params) + return await request.post('system/tasks', params) }, }, edit: { put: async function (id, params) { - return await request.put(`tasks/${id}`, params) + return await request.put(`system/tasks/${id}`, params) }, }, delete: { delete: async function (id) { - return await request.delete(`tasks/${id}`) + return await request.delete(`system/tasks/${id}`) }, }, batchDelete: { post: async function (params) { - return await request.post('tasks/batch-delete', params) + return await request.post('system/tasks/batch-delete', params) }, }, batchStatus: { post: async function (params) { - return await request.post('tasks/batch-status', params) + return await request.post('system/tasks/batch-status', params) }, }, run: { post: async function (id) { - return await request.post(`tasks/${id}/run`) + return await request.post(`system/tasks/${id}/run`) }, }, statistics: { get: async function () { - return await request.get('tasks/statistics') + return await request.get('system/tasks/statistics') }, }, }, @@ -233,62 +245,62 @@ export default { cities: { list: { get: async function (params) { - return await request.get('cities', { params }) + return await request.get('system/cities', { params }) }, }, tree: { get: async function () { - return await request.get('cities/tree') + return await request.get('system/cities/tree') }, }, detail: { get: async function (id) { - return await request.get(`cities/${id}`) + return await request.get(`system/cities/${id}`) }, }, children: { get: async function (id) { - return await request.get(`cities/${id}/children`) + return await request.get(`system/cities/${id}/children`) }, }, provinces: { get: async function () { - return await request.get('cities/provinces') + return await request.get('system/cities/provinces') }, }, cities: { get: async function (provinceId) { - return await request.get(`cities/${provinceId}/cities`) + return await request.get(`system/cities/${provinceId}/cities`) }, }, districts: { get: async function (cityId) { - return await request.get(`cities/${cityId}/districts`) + return await request.get(`system/cities/${cityId}/districts`) }, }, add: { post: async function (params) { - return await request.post('cities', params) + return await request.post('system/cities', params) }, }, edit: { put: async function (id, params) { - return await request.put(`cities/${id}`, params) + return await request.put(`system/cities/${id}`, params) }, }, delete: { delete: async function (id) { - return await request.delete(`cities/${id}`) + return await request.delete(`system/cities/${id}`) }, }, batchDelete: { post: async function (params) { - return await request.post('cities/batch-delete', params) + return await request.post('system/cities/batch-delete', params) }, }, batchStatus: { 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: { single: { post: async function (formData) { - return await request.post('upload', formData, { + return await request.post('system/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' } }) }, }, multiple: { 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' } }) }, }, base64: { post: async function (params) { - return await request.post('upload/base64', params) + return await request.post('system/upload/base64', params) }, }, delete: { post: async function (params) { - return await request.post('upload/delete', params) + return await request.post('system/upload/delete', params) }, }, batchDelete: { post: async function (params) { - return await request.post('upload/batch-delete', params) + return await request.post('system/upload/batch-delete', params) }, }, }, diff --git a/resources/admin/src/components/HelloWorld.vue b/resources/admin/src/components/HelloWorld.vue deleted file mode 100644 index 546ebbc..0000000 --- a/resources/admin/src/components/HelloWorld.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - - - diff --git a/resources/admin/src/components/scSelect/README.md b/resources/admin/src/components/scSelect/README.md new file mode 100644 index 0000000..98549ea --- /dev/null +++ b/resources/admin/src/components/scSelect/README.md @@ -0,0 +1,606 @@ +# scSelect 组件使用文档 + +## 组件简介 + +`scSelect` 是一个基于 Ant Design Vue `a-select` 组件封装的增强型选择器组件,支持多种数据源(data、api、dictionary)和智能缓存机制。 + +## 特性 + +- ✅ 支持三种数据源:直接数据(data)、API 接口(api)、字典数据(dictionary) +- ✅ API 数据源自动缓存,减少重复请求 +- ✅ 字典数据源自动从缓存读取,登录后自动预加载 +- ✅ 支持按需加载数据(focus 时触发) +- ✅ 支持立即加载数据(组件挂载时) +- ✅ 支持自定义字段映射 +- ✅ 支持自定义数据处理函数 +- ✅ 完全继承 `a-select` 的所有属性和方法 +- ✅ 支持插槽透传 + +## 安装 + +组件已自动注册,直接使用即可: + +```vue + + + +``` + +## 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 + + + +``` + +### 2. 使用 api 数据源 + +从 API 接口获取数据,自动缓存: + +```vue + + + +``` + +### 3. 使用 dictionary 数据源(推荐) + +从字典缓存获取数据,性能最佳: + +```vue + + + +``` + +### 4. 立即加载数据 + +在组件挂载时立即加载数据,而不是等到 focus 时: + +```vue + + + +``` + +### 5. 自定义数据处理 + +使用 `dataProcessor` 函数自定义数据格式: + +```vue + + + +``` + +### 6. 使用 ref 调用方法 + +```vue + + + +``` + +### 7. 禁用 API 缓存 + +```vue + +``` + +### 8. 多选模式 + +```vue + + + +``` + +### 9. 自定义插槽 + +```vue + + + +``` + +## 数据格式要求 + +### 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 + +``` + +## 完整示例 + +```vue + + + diff --git a/resources/admin/src/components/scSelect/index.vue b/resources/admin/src/components/scSelect/index.vue new file mode 100644 index 0000000..16fb2b1 --- /dev/null +++ b/resources/admin/src/components/scSelect/index.vue @@ -0,0 +1,308 @@ + + + + + diff --git a/resources/admin/src/components/scTable/index.vue b/resources/admin/src/components/scTable/index.vue index ef8681a..b405adf 100644 --- a/resources/admin/src/components/scTable/index.vue +++ b/resources/admin/src/components/scTable/index.vue @@ -2,10 +2,10 @@
- + :size="tableSettings.size" :show-header="showHeader" :locale="locale" :expanded-row-keys="expandedRowKeys" + :expand-row-by-click="expandRowByClick" @change="handleTableChange" @resizeColumn="handleResizeColumn">