@lacqjs/nuxt-dict 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/module.d.mts +3 -1
- package/dist/module.d.ts +3 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +133 -12
- package/dist/runtime/composables/useDict.d.ts +6 -0
- package/dist/runtime/composables/useDict.js +36 -7
- package/dist/runtime/composables/useDictTree.d.ts +6 -0
- package/dist/runtime/composables/useDictTree.js +29 -14
- package/dist/runtime/composables/useLocale.d.ts +14 -0
- package/dist/runtime/core/adapter.d.ts +27 -1
- package/dist/runtime/core/adapter.js +6 -2
- package/dist/runtime/core/cache/indexeddb-cache.js +3 -1
- package/dist/runtime/core/dict-manager.d.ts +77 -9
- package/dist/runtime/core/dict-manager.js +81 -20
- package/dist/runtime/plugins/dict.js +6 -4
- package/dist/runtime/types/index.d.ts +302 -19
- package/dist/runtime/utils/dict-translator.d.ts +22 -24
- package/dist/runtime/utils/dict-translator.js +93 -20
- package/dist/types.d.mts +5 -1
- package/package.json +15 -10
- package/dist/runtime/composables/useDictOptions.d.ts +0 -13
- package/dist/runtime/composables/useDictOptions.js +0 -17
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026-PRESENT miaozhongfei
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">@lacqjs/nuxt-dict</h1>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://npmjs.com/package/@lacqjs/nuxt-dict"><img src="https://img.shields.io/npm/v/@lacqjs/nuxt-dict?style=flat-square&colorA=202128&colorB=36936A" alt="Version"></a>
|
|
7
|
+
<a href="https://npmjs.com/package/@lacqjs/nuxt-dict"><img src="https://img.shields.io/npm/dm/@lacqjs/nuxt-dict?style=flat-square&colorA=202128&colorB=36936A" alt="Downloads"></a>
|
|
8
|
+
<a href="https://github.com/miaozhongfei/nuxt-dict/blob/main/LICENSE"><img src="https://img.shields.io/github/license/miaozhongfei/nuxt-dict?style=flat-square&colorA=202128&colorB=36936A" alt="License"></a>
|
|
9
|
+
<a href="https://miaozhongfei.github.io/nuxt-dict/"><img src="https://img.shields.io/badge/Docs-202128?style=flat-square&logo=gitbook&logoColor=DDDDD4" alt="Docs"></a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
> Nuxt 数据字典模块,提供扁平 / 树形字典翻译、多语言国际化、三级缓存与 SSR 预取。
|
|
13
|
+
|
|
14
|
+
## 快速开始
|
|
15
|
+
|
|
16
|
+
### 安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @lacqjs/nuxt-dict
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 注册模块
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// nuxt.config.ts
|
|
26
|
+
export default defineNuxtConfig({
|
|
27
|
+
modules: ['@lacqjs/nuxt-dict'],
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```vue
|
|
32
|
+
<template>
|
|
33
|
+
<el-select v-model="value" placeholder="请选择">
|
|
34
|
+
<el-option v-for="o in options" :key="o.value" :label="o.label" :value="o.value" />
|
|
35
|
+
</el-select>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<script setup lang="ts">
|
|
39
|
+
const { data: options } = useDict('gender');
|
|
40
|
+
const value = ref('');
|
|
41
|
+
</script>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 特性
|
|
45
|
+
|
|
46
|
+
- **扁平字典** — `useDict(type)` 自动加载,返回 `[{ value, label }]`,直接绑定 UI 组件
|
|
47
|
+
- **树形字典** — `useDictTree(type)` 支持任意深度树形结构 + `findPath()` 路径回溯
|
|
48
|
+
- **多语言** — `useLocale()` 切换语言,所有活跃字典实例自动重新请求
|
|
49
|
+
- **多仓库** — 通过 `stores` 配置从不同 API 端点加载,实现数据隔离
|
|
50
|
+
- **三级缓存** — 内存 LRU → IndexedDB → 网络(版本校验),最大化减少请求
|
|
51
|
+
- **SSR 预取** — `ssr.prefetch` 配置服务端预加载,加速首屏渲染
|
|
52
|
+
- **自定义适配器** — 实现 `DictAdapter` 接口即可对接任意数据源
|
|
53
|
+
- **同步翻译** — `$dict.translate()` / `$dict.translatePath()` / `$dict.translateData()` 全场景覆盖
|
|
54
|
+
|
|
55
|
+
完整用法与配置请查看 → [文档](https://miaozhongfei.github.io/nuxt-dict/)
|
|
56
|
+
|
|
57
|
+
## 开发
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pnpm dev # 启动开发服务器
|
|
61
|
+
pnpm prepack # 构建产物
|
|
62
|
+
pnpm lint # 代码检查
|
|
63
|
+
pnpm typecheck # 类型检查
|
|
64
|
+
pnpm e2e # E2E 测试
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 感谢 / 致谢
|
|
68
|
+
|
|
69
|
+
本项目受益于以下优秀开源项目:
|
|
70
|
+
|
|
71
|
+
- [Nuxt](https://nuxt.com/) — Vue 全栈框架
|
|
72
|
+
- [@nuxt/kit](https://github.com/nuxt/nuxt) — Nuxt 模块开发工具包
|
|
73
|
+
- [defu](https://github.com/unjs/defu) — 深度合并配置
|
|
74
|
+
- [consola](https://github.com/unjs/consola) — 日志工具
|
|
75
|
+
- [compare-versions](https://github.com/omichelsen/compare-versions) — 版本比较
|
|
76
|
+
|
|
77
|
+
## 许可证
|
|
78
|
+
|
|
79
|
+
[MIT](./LICENSE) License © 2026-PRESENT [miaozhongfei](https://github.com/miaozhongfei)
|
package/dist/module.d.mts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
import * as __runtime_types from '../dist/runtime/types/index.js';
|
|
3
3
|
import { ModuleOptions } from '../dist/runtime/types/index.js';
|
|
4
|
-
export { ModuleOptions } from '../dist/runtime/types/index.js';
|
|
4
|
+
export { DictItem, DictTranslator, GetDictItemOptions, ModuleOptions, TranslateOptions, TranslatePathOptions, TreeNode } from '../dist/runtime/types/index.js';
|
|
5
|
+
export { DictManager } from '../dist/runtime/core/dict-manager.js';
|
|
6
|
+
export { createDictTranslator } from '../dist/runtime/utils/dict-translator.js';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Nuxt Dict 模块入口。
|
package/dist/module.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
import * as __runtime_types from '../dist/runtime/types/index.js';
|
|
3
3
|
import { ModuleOptions } from '../dist/runtime/types/index.js';
|
|
4
|
-
export { ModuleOptions } from '../dist/runtime/types/index.js';
|
|
4
|
+
export { DictItem, DictTranslator, GetDictItemOptions, ModuleOptions, TranslateOptions, TranslatePathOptions, TreeNode } from '../dist/runtime/types/index.js';
|
|
5
|
+
export { DictManager } from '../dist/runtime/core/dict-manager.js';
|
|
6
|
+
export { createDictTranslator } from '../dist/runtime/utils/dict-translator.js';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Nuxt Dict 模块入口。
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { fileURLToPath } from 'url';
|
|
2
1
|
import { defineNuxtModule, useLogger, createResolver, addPlugin, addImportsDir, addTypeTemplate } from '@nuxt/kit';
|
|
3
2
|
import { defaultOptions } from '../dist/runtime/options.js';
|
|
4
3
|
import { createLogger } from '../dist/runtime/utils/logger.js';
|
|
4
|
+
export { createDictTranslator } from '../dist/runtime/utils/dict-translator.js';
|
|
5
5
|
|
|
6
6
|
const name = "@lacqjs/nuxt-dict";
|
|
7
|
-
const version = "0.0.
|
|
7
|
+
const version = "0.0.4";
|
|
8
8
|
const devDependencies = {
|
|
9
9
|
nuxt: "^4.4.8"};
|
|
10
10
|
const pkg = {
|
|
@@ -15,17 +15,139 @@ const pkg = {
|
|
|
15
15
|
function registerTypeTemplates(resolver, stores) {
|
|
16
16
|
addTypeTemplate({
|
|
17
17
|
filename: "types/nuxt-dict.d.ts",
|
|
18
|
+
// eslint-disable max-lines-per-function
|
|
18
19
|
getContents: () => `
|
|
19
|
-
import type {
|
|
20
|
+
import type { StoreKey } from '#build/types/nuxt-dict-store-names'
|
|
20
21
|
|
|
21
22
|
declare module '#app' {
|
|
22
23
|
interface NuxtApp {
|
|
23
|
-
$dict:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
$dict: {
|
|
25
|
+
/**
|
|
26
|
+
* \u540C\u6B65\u7FFB\u8BD1\u5B57\u5178\u7F16\u7801 \u2192 \u6587\u672C\u3002
|
|
27
|
+
* @description \u540C\u6B65\u7FFB\u8BD1\u5B57\u5178\u7F16\u7801 \u2192 \u6587\u672C\u3002\u4ECE\u5168\u5C40\u5185\u5B58\u7F13\u5B58\u4E2D\u67E5\u627E\u7F16\u7801\u5BF9\u5E94\u7684\u6587\u672C\uFF0C\u7F13\u5B58\u672A\u547D\u4E2D\u65F6\u8FD4\u56DE code \u539F\u6837\u3002\u5148\u901A\u8FC7 useDict \u7B49\u52A0\u8F7D\u6570\u636E\u540E\u8C03\u7528\u3002
|
|
28
|
+
* @param type - \u5B57\u5178\u7C7B\u578B\u540D\uFF0C\u5982 'gender'
|
|
29
|
+
* @param code - \u7F16\u7801\u503C
|
|
30
|
+
* @param opts - \u53EF\u9009\u914D\u7F6E\u5BF9\u8C61
|
|
31
|
+
* @param {StoreKey} [opts.storeName] - \u6307\u5B9A\u4ED3\u5E93\u540D
|
|
32
|
+
* @param {string} [opts.field] - \u6307\u5B9A\u53D6\u503C\u5B57\u6BB5\uFF0C\u9ED8\u8BA4 'label'
|
|
33
|
+
* @returns {string} \u7FFB\u8BD1\u6587\u672C\uFF0C\u7F13\u5B58\u672A\u547D\u4E2D\u65F6\u8FD4\u56DE code \u539F\u6837
|
|
34
|
+
* @example
|
|
35
|
+
* $dict.translate('gender', 'male')
|
|
36
|
+
* $dict.translate('gender', 'male', { storeName: 'dicts2' })
|
|
37
|
+
*/
|
|
38
|
+
translate(type: string, code: string | number, opts?: { storeName?: StoreKey; field?: string }): string
|
|
39
|
+
/**
|
|
40
|
+
* \u6811\u5F62\u5B57\u5178\u4E2D\u67E5\u627E\u7F16\u7801\u7684\u5B8C\u6574\u5C42\u7EA7\u8DEF\u5F84\u3002
|
|
41
|
+
* @description \u6811\u5F62\u5B57\u5178\u4E2D\u67E5\u627E\u7F16\u7801\u7684\u5B8C\u6574\u5C42\u7EA7\u8DEF\u5F84\u3002\u4ECE\u5185\u5B58\u7F13\u5B58\u4E2D\u52A0\u8F7D\u7684\u6811\u5F62\u5B57\u5178\u6570\u636E\u91CC\uFF0C\u901A\u8FC7 DFS \u67E5\u627E\u76EE\u6807\u7F16\u7801\u5E76\u56DE\u6EAF\u5B8C\u6574\u8DEF\u5F84\uFF0C\u7528\u5206\u9694\u7B26\u62FC\u63A5\u540E\u8FD4\u56DE\u3002
|
|
42
|
+
* @param type - \u6811\u5F62\u5B57\u5178\u7C7B\u578B\u540D\uFF0C\u5982 'region'
|
|
43
|
+
* @param code - \u53F6\u5B50\u8282\u70B9\u7F16\u7801\u503C
|
|
44
|
+
* @param opts - \u53EF\u9009\u914D\u7F6E\u5BF9\u8C61
|
|
45
|
+
* @param {StoreKey} [opts.storeName] - \u6307\u5B9A\u4ED3\u5E93\u540D
|
|
46
|
+
* @param {string} [opts.field] - \u6307\u5B9A\u8282\u70B9\u53D6\u503C\u5B57\u6BB5\uFF0C\u9ED8\u8BA4 'label'
|
|
47
|
+
* @param {string} [opts.separator] - \u5C42\u7EA7\u8DEF\u5F84\u5206\u9694\u7B26\uFF0C\u9ED8\u8BA4 ' / '
|
|
48
|
+
* @returns {string} \u7528\u5206\u9694\u7B26\u8FDE\u63A5\u7684\u5B8C\u6574\u5C42\u7EA7\u8DEF\u5F84\uFF0C\u7F13\u5B58\u672A\u547D\u4E2D\u65F6\u8FD4\u56DE code \u539F\u6837
|
|
49
|
+
* @example
|
|
50
|
+
* $dict.translatePath('region', '440104')
|
|
51
|
+
* $dict.translatePath('region', '440104', { separator: ' \u2192 ' })
|
|
52
|
+
*/
|
|
53
|
+
translatePath(type: string, code: string | number, opts?: { storeName?: StoreKey; field?: string; separator?: string }): string
|
|
54
|
+
/**
|
|
55
|
+
* \u6279\u91CF\u7FFB\u8BD1\u6570\u636E\u5BF9\u8C61\u4E2D\u7684\u591A\u4E2A\u7F16\u7801\u5B57\u6BB5\u3002
|
|
56
|
+
* @description \u6279\u91CF\u7FFB\u8BD1\u6570\u636E\u5BF9\u8C61\u4E2D\u7684\u591A\u4E2A\u7F16\u7801\u5B57\u6BB5\u3002\u4F20\u5165\u4E00\u4E2A\u6570\u636E\u5BF9\u8C61\u548C\u5B57\u6BB5\u2192\u5B57\u5178\u7C7B\u578B\u6620\u5C04\u8868\uFF0C\u8FD4\u56DE\u8FFD\u52A0\u4E86\u7FFB\u8BD1\u5B57\u6BB5\u7684\u65B0\u5BF9\u8C61\uFF08\u4E0D\u4FEE\u6539\u539F\u5BF9\u8C61\uFF09\u3002
|
|
57
|
+
* @param data - \u9700\u8981\u7FFB\u8BD1\u7684\u6570\u636E\u5BF9\u8C61\uFF0C\u5982 { gender: 'male', status: 1 }
|
|
58
|
+
* @param mapping - \u5B57\u6BB5\u2192\u5B57\u5178\u7C7B\u578B\u6620\u5C04\u8868\uFF0C\u503C\u4E3A string\uFF08\u9ED8\u8BA4\u4ED3\u5E93\uFF09\u6216 { type, storeName? }\uFF08\u6307\u5B9A\u4ED3\u5E93\uFF09
|
|
59
|
+
* @param suffix - \u7FFB\u8BD1\u5B57\u6BB5\u540E\u7F00\uFF0C\u9ED8\u8BA4 '_label'\uFF0C\u7ED3\u679C\u5199\u5165 \u539F\u5B57\u6BB5\u540D + suffix
|
|
60
|
+
* @returns {Record<string, unknown>} \u65B0\u5BF9\u8C61\uFF0C\u5305\u542B\u539F\u6570\u636E\u6240\u6709\u5B57\u6BB5 + \u4EE5 suffix \u4E3A\u540E\u7F00\u7684\u7FFB\u8BD1\u5B57\u6BB5
|
|
61
|
+
* @example
|
|
62
|
+
* $dict.translateData(
|
|
63
|
+
* { gender: 'male', status: 1, name: '\u5F20\u4E09' },
|
|
64
|
+
* { gender: 'gender', status: 'status' }
|
|
65
|
+
* )
|
|
66
|
+
* // \u2192 { ..., gender_label: '\u7537', status_label: '\u7981\u7528', ... }
|
|
67
|
+
*/
|
|
68
|
+
translateData(data: Record<string, unknown>, mapping: Record<string, string | { type: string; storeName?: StoreKey }>, suffix?: string): Record<string, unknown>
|
|
69
|
+
/**
|
|
70
|
+
* \u4ECE\u5185\u5B58\u7F13\u5B58\u4E2D\u67E5\u627E\u7F16\u7801\u5BF9\u5E94\u7684\u5B8C\u6574\u5B57\u5178\u9879\u5BF9\u8C61\u3002
|
|
71
|
+
* @description \u4ECE\u5DF2\u52A0\u8F7D\u7684\u7F13\u5B58\u4E2D\u67E5\u627E\u7F16\u7801\u5BF9\u5E94\u7684\u5B8C\u6574 DictItem \u5BF9\u8C61\uFF08\u975E\u5B57\u7B26\u4E32\u7FFB\u8BD1\uFF09\u3002\u7F13\u5B58\u672A\u547D\u4E2D\u65F6\u8FD4\u56DE undefined\u3002
|
|
72
|
+
* @param type - \u5B57\u5178\u7C7B\u578B\u540D\uFF0C\u5982 'gender'
|
|
73
|
+
* @param code - \u7F16\u7801\u503C
|
|
74
|
+
* @param opts - \u53EF\u9009\u914D\u7F6E\u5BF9\u8C61
|
|
75
|
+
* @param {StoreKey} [opts.storeName] - \u6307\u5B9A\u4ED3\u5E93\u540D
|
|
76
|
+
* @returns {DictItem | undefined} \u5B8C\u6574\u7684\u5B57\u5178\u9879\u5BF9\u8C61\uFF0C\u7F13\u5B58\u672A\u547D\u4E2D\u65F6\u8FD4\u56DE undefined
|
|
77
|
+
* @example
|
|
78
|
+
* $dict.getDictItem('gender', 'male')
|
|
79
|
+
* $dict.getDictItem('gender', 'male', { storeName: 'dicts2' })
|
|
80
|
+
*/
|
|
81
|
+
getDictItem(type: string, code: string | number, opts?: { storeName?: StoreKey }): DictItem | undefined
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
declare module '@vue/runtime-core' {
|
|
87
|
+
interface ComponentCustomProperties {
|
|
88
|
+
$dict: {
|
|
89
|
+
/**
|
|
90
|
+
* \u540C\u6B65\u7FFB\u8BD1\u5B57\u5178\u7F16\u7801 \u2192 \u6587\u672C\u3002
|
|
91
|
+
* @description \u540C\u6B65\u7FFB\u8BD1\u5B57\u5178\u7F16\u7801 \u2192 \u6587\u672C\u3002\u4ECE\u5168\u5C40\u5185\u5B58\u7F13\u5B58\u4E2D\u67E5\u627E\u7F16\u7801\u5BF9\u5E94\u7684\u6587\u672C\uFF0C\u7F13\u5B58\u672A\u547D\u4E2D\u65F6\u8FD4\u56DE code \u539F\u6837\u3002
|
|
92
|
+
* @param type - \u5B57\u5178\u7C7B\u578B\u540D\uFF0C\u5982 'gender'
|
|
93
|
+
* @param code - \u7F16\u7801\u503C
|
|
94
|
+
* @param opts - \u53EF\u9009\u914D\u7F6E\u5BF9\u8C61
|
|
95
|
+
* @param {StoreKey} [opts.storeName] - \u6307\u5B9A\u4ED3\u5E93\u540D
|
|
96
|
+
* @param {string} [opts.field] - \u6307\u5B9A\u53D6\u503C\u5B57\u6BB5\uFF0C\u9ED8\u8BA4 'label'
|
|
97
|
+
* @returns {string} \u7FFB\u8BD1\u6587\u672C\uFF0C\u7F13\u5B58\u672A\u547D\u4E2D\u65F6\u8FD4\u56DE code \u539F\u6837
|
|
98
|
+
* @example
|
|
99
|
+
* $dict.translate('gender', 'male')
|
|
100
|
+
* $dict.translate('gender', 'male', { storeName: 'dicts2' })
|
|
101
|
+
*/
|
|
102
|
+
translate(type: string, code: string | number, opts?: { storeName?: StoreKey; field?: string }): string
|
|
103
|
+
/**
|
|
104
|
+
* \u6811\u5F62\u5B57\u5178\u4E2D\u67E5\u627E\u7F16\u7801\u7684\u5B8C\u6574\u5C42\u7EA7\u8DEF\u5F84\u3002
|
|
105
|
+
* @description \u6811\u5F62\u5B57\u5178\u4E2D\u67E5\u627E\u7F16\u7801\u7684\u5B8C\u6574\u5C42\u7EA7\u8DEF\u5F84\u3002\u901A\u8FC7 DFS \u67E5\u627E\u76EE\u6807\u7F16\u7801\u5E76\u56DE\u6EAF\u5B8C\u6574\u8DEF\u5F84\uFF0C\u7528\u5206\u9694\u7B26\u62FC\u63A5\u540E\u8FD4\u56DE\u3002
|
|
106
|
+
* @param type - \u6811\u5F62\u5B57\u5178\u7C7B\u578B\u540D\uFF0C\u5982 'region'
|
|
107
|
+
* @param code - \u53F6\u5B50\u8282\u70B9\u7F16\u7801\u503C
|
|
108
|
+
* @param opts - \u53EF\u9009\u914D\u7F6E\u5BF9\u8C61
|
|
109
|
+
* @param {StoreKey} [opts.storeName] - \u6307\u5B9A\u4ED3\u5E93\u540D
|
|
110
|
+
* @param {string} [opts.field] - \u6307\u5B9A\u8282\u70B9\u53D6\u503C\u5B57\u6BB5\uFF0C\u9ED8\u8BA4 'label'
|
|
111
|
+
* @param {string} [opts.separator] - \u5C42\u7EA7\u8DEF\u5F84\u5206\u9694\u7B26\uFF0C\u9ED8\u8BA4 ' / '
|
|
112
|
+
* @returns {string} \u7528\u5206\u9694\u7B26\u8FDE\u63A5\u7684\u5B8C\u6574\u5C42\u7EA7\u8DEF\u5F84\uFF0C\u7F13\u5B58\u672A\u547D\u4E2D\u65F6\u8FD4\u56DE code \u539F\u6837
|
|
113
|
+
* @example
|
|
114
|
+
* $dict.translatePath('region', '440104')
|
|
115
|
+
* $dict.translatePath('region', '440104', { separator: ' \u2192 ' })
|
|
116
|
+
*/
|
|
117
|
+
translatePath(type: string, code: string | number, opts?: { storeName?: StoreKey; field?: string; separator?: string }): string
|
|
118
|
+
/**
|
|
119
|
+
* \u6279\u91CF\u7FFB\u8BD1\u6570\u636E\u5BF9\u8C61\u4E2D\u7684\u591A\u4E2A\u7F16\u7801\u5B57\u6BB5\u3002
|
|
120
|
+
* @description \u6279\u91CF\u7FFB\u8BD1\u6570\u636E\u5BF9\u8C61\u4E2D\u7684\u591A\u4E2A\u7F16\u7801\u5B57\u6BB5\u3002\u4F20\u5165\u5B57\u6BB5\u2192\u5B57\u5178\u7C7B\u578B\u6620\u5C04\u8868\uFF0C\u8FD4\u56DE\u542B\u7FFB\u8BD1\u5B57\u6BB5\u7684\u65B0\u5BF9\u8C61\uFF08\u4E0D\u4FEE\u6539\u539F\u5BF9\u8C61\uFF09\u3002
|
|
121
|
+
* @param data - \u9700\u8981\u7FFB\u8BD1\u7684\u6570\u636E\u5BF9\u8C61\uFF0C\u5982 { gender: 'male', status: 1 }
|
|
122
|
+
* @param mapping - \u5B57\u6BB5\u2192\u5B57\u5178\u7C7B\u578B\u6620\u5C04\u8868\uFF0C\u503C\u4E3A string\uFF08\u9ED8\u8BA4\u4ED3\u5E93\uFF09\u6216 { type, storeName? }\uFF08\u6307\u5B9A\u4ED3\u5E93\uFF09
|
|
123
|
+
* @param suffix - \u7FFB\u8BD1\u5B57\u6BB5\u540E\u7F00\uFF0C\u9ED8\u8BA4 '_label'
|
|
124
|
+
* @returns {Record<string, unknown>} \u65B0\u5BF9\u8C61\uFF0C\u5305\u542B\u539F\u6570\u636E\u6240\u6709\u5B57\u6BB5 + \u4EE5 suffix \u4E3A\u540E\u7F00\u7684\u7FFB\u8BD1\u5B57\u6BB5
|
|
125
|
+
* @example
|
|
126
|
+
* $dict.translateData(
|
|
127
|
+
* { gender: 'male', status: 1, name: '\u5F20\u4E09' },
|
|
128
|
+
* { gender: 'gender', status: 'status' }
|
|
129
|
+
* )
|
|
130
|
+
* // \u2192 { ..., gender_label: '\u7537', status_label: '\u7981\u7528', ... }
|
|
131
|
+
*/
|
|
132
|
+
translateData(data: Record<string, unknown>, mapping: Record<string, string | { type: string; storeName?: StoreKey }>, suffix?: string): Record<string, unknown>
|
|
133
|
+
/**
|
|
134
|
+
* \u4ECE\u5185\u5B58\u7F13\u5B58\u4E2D\u67E5\u627E\u7F16\u7801\u5BF9\u5E94\u7684\u5B8C\u6574\u5B57\u5178\u9879\u5BF9\u8C61\u3002
|
|
135
|
+
* @description \u4ECE\u5DF2\u52A0\u8F7D\u7684\u7F13\u5B58\u4E2D\u67E5\u627E\u7F16\u7801\u5BF9\u5E94\u7684\u5B8C\u6574 DictItem \u5BF9\u8C61\uFF08\u975E\u5B57\u7B26\u4E32\u7FFB\u8BD1\uFF09\u3002\u7F13\u5B58\u672A\u547D\u4E2D\u65F6\u8FD4\u56DE undefined\u3002
|
|
136
|
+
* @param type - \u5B57\u5178\u7C7B\u578B\u540D\uFF0C\u5982 'gender'
|
|
137
|
+
* @param code - \u7F16\u7801\u503C
|
|
138
|
+
* @param opts - \u53EF\u9009\u914D\u7F6E\u5BF9\u8C61
|
|
139
|
+
* @param {StoreKey} [opts.storeName] - \u6307\u5B9A\u4ED3\u5E93\u540D
|
|
140
|
+
* @returns {DictItem | undefined} \u5B8C\u6574\u7684\u5B57\u5178\u9879\u5BF9\u8C61\uFF0C\u7F13\u5B58\u672A\u547D\u4E2D\u65F6\u8FD4\u56DE undefined
|
|
141
|
+
* @example
|
|
142
|
+
* $dict.getDictItem('gender', 'male')
|
|
143
|
+
* $dict.getDictItem('gender', 'male', { storeName: 'dicts2' })
|
|
144
|
+
*/
|
|
145
|
+
getDictItem(type: string, code: string | number, opts?: { storeName?: StoreKey }): DictItem | undefined
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
27
149
|
|
|
28
|
-
export {}
|
|
150
|
+
export {}
|
|
29
151
|
`
|
|
30
152
|
});
|
|
31
153
|
addTypeTemplate({
|
|
@@ -58,11 +180,10 @@ const module$1 = defineNuxtModule().with({
|
|
|
58
180
|
return;
|
|
59
181
|
}
|
|
60
182
|
const resolver = createResolver(import.meta.url);
|
|
61
|
-
const runtimeDir = fileURLToPath(new URL("./runtime", import.meta.url));
|
|
62
183
|
_nuxt.options.runtimeConfig.public.dict = _options;
|
|
63
|
-
_nuxt.options.build.transpile.push(resolver.resolve(
|
|
64
|
-
addPlugin(
|
|
65
|
-
addImportsDir(resolver.resolve(
|
|
184
|
+
_nuxt.options.build.transpile.push(resolver.resolve("./runtime"));
|
|
185
|
+
addPlugin(resolver.resolve("./runtime/plugins/dict"));
|
|
186
|
+
addImportsDir(resolver.resolve("./runtime/composables"));
|
|
66
187
|
registerTypeTemplates(resolver, _options.stores);
|
|
67
188
|
_nuxt.hook("prepare:types", ({ references }) => {
|
|
68
189
|
references.push({ types: pkg.name });
|
|
@@ -4,6 +4,12 @@ import type { UseDictReturn, StoreKey } from '../types/index.js';
|
|
|
4
4
|
* 组件挂载时自动加载,卸载时自动清理引用。
|
|
5
5
|
* 返回翻译函数、加载状态和手动刷新方法。
|
|
6
6
|
*
|
|
7
|
+
* @description 从字典管理器获取扁平字典数据,挂载后自动触发网络请求(或缓存)。
|
|
8
|
+
* @param {string} type - 字典类型名(如 'gender'),一参形式:useDict(type),使用默认仓库 'dicts'
|
|
9
|
+
* @param {StoreKey} storeName - 仓库名(如 'dicts2'),二参形式:useDict(storeName, type)
|
|
10
|
+
* @param {string} type - 字典类型名,二参形式时作为第二参数传入
|
|
11
|
+
* @returns {UseDictReturn} 包含 data(字典项数组)、translate(翻译函数)、loading、error、refresh
|
|
12
|
+
*
|
|
7
13
|
* @example
|
|
8
14
|
* // 默认存储库 'dicts'
|
|
9
15
|
* const { data, translate } = useDict('gender')
|
|
@@ -2,12 +2,18 @@ import { shallowRef, ref, watch, onMounted, onBeforeUnmount } from "vue";
|
|
|
2
2
|
import { useNuxtApp } from "#imports";
|
|
3
3
|
import { DEFAULT_STORE_NAME } from "../core/cache/indexeddb-cache.js";
|
|
4
4
|
const activeInstances = /* @__PURE__ */ new Map();
|
|
5
|
-
async function fetchDictData(manager, dictType, storeName, data, loading, error, mode = "load") {
|
|
5
|
+
async function fetchDictData(manager, dictType, storeName, data, loading, error, mode = "load", itemMap) {
|
|
6
6
|
loading.value = true;
|
|
7
7
|
error.value = null;
|
|
8
8
|
try {
|
|
9
9
|
const entry = await (mode === "refresh" ? manager.refresh(dictType, storeName) : manager.getDict(dictType, storeName));
|
|
10
10
|
data.value = entry.items;
|
|
11
|
+
if (itemMap) {
|
|
12
|
+
itemMap.clear();
|
|
13
|
+
for (const item of entry.items) {
|
|
14
|
+
itemMap.set(String(item.value), item);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
11
17
|
} catch (e) {
|
|
12
18
|
error.value = e instanceof Error ? e.message : String(e);
|
|
13
19
|
} finally {
|
|
@@ -35,25 +41,48 @@ export function useDict(storeOrType, maybeType) {
|
|
|
35
41
|
const storeName = maybeType === void 0 ? DEFAULT_STORE_NAME : storeOrType;
|
|
36
42
|
const dictType = maybeType ?? storeOrType;
|
|
37
43
|
const data = shallowRef(null);
|
|
44
|
+
const itemMap = /* @__PURE__ */ new Map();
|
|
38
45
|
const loading = ref(false);
|
|
39
46
|
const error = ref(null);
|
|
40
47
|
const instanceId = Symbol(dictType);
|
|
41
48
|
const trackKey = `${storeName}:${dictType}`;
|
|
42
49
|
trackInstance(trackKey, instanceId);
|
|
43
|
-
function translate(
|
|
44
|
-
|
|
50
|
+
function translate(value, opts) {
|
|
51
|
+
const targetStore = opts?.storeName ?? storeName;
|
|
52
|
+
const field = opts?.field ?? "label";
|
|
53
|
+
if (targetStore !== storeName) {
|
|
54
|
+
return manager.translate(dictType, value, { storeName: targetStore, field });
|
|
55
|
+
}
|
|
56
|
+
if (!data.value) return String(value);
|
|
57
|
+
const item = itemMap.get(String(value));
|
|
58
|
+
if (!item) return String(value);
|
|
59
|
+
return item[field] ?? item.label;
|
|
60
|
+
}
|
|
61
|
+
function getDictItem(value, opts) {
|
|
62
|
+
if (opts?.storeName && opts.storeName !== storeName) {
|
|
63
|
+
return manager.getDictItem(dictType, value, opts);
|
|
64
|
+
}
|
|
65
|
+
if (!data.value) return void 0;
|
|
66
|
+
return itemMap.get(String(value));
|
|
45
67
|
}
|
|
46
68
|
async function refresh() {
|
|
47
|
-
await fetchDictData(manager, dictType, storeName, data, loading, error, "refresh");
|
|
69
|
+
await fetchDictData(manager, dictType, storeName, data, loading, error, "refresh", itemMap);
|
|
48
70
|
}
|
|
49
71
|
onMounted(() => {
|
|
50
|
-
fetchDictData(manager, dictType, storeName, data, loading, error, "load");
|
|
72
|
+
fetchDictData(manager, dictType, storeName, data, loading, error, "load", itemMap);
|
|
51
73
|
});
|
|
52
74
|
watch(
|
|
53
75
|
() => manager.locale.value,
|
|
54
76
|
() => {
|
|
55
|
-
fetchDictData(manager, dictType, storeName, data, loading, error, "load");
|
|
77
|
+
fetchDictData(manager, dictType, storeName, data, loading, error, "load", itemMap);
|
|
56
78
|
}
|
|
57
79
|
);
|
|
58
|
-
return {
|
|
80
|
+
return {
|
|
81
|
+
data,
|
|
82
|
+
translate,
|
|
83
|
+
getDictItem,
|
|
84
|
+
loading,
|
|
85
|
+
error,
|
|
86
|
+
refresh
|
|
87
|
+
};
|
|
59
88
|
}
|
|
@@ -3,6 +3,12 @@ import type { UseDictTreeReturn, StoreKey } from '../types/index.js';
|
|
|
3
3
|
* 使用树形字典数据,支持翻译和路径查找。
|
|
4
4
|
* 组件挂载时自动加载,适用于区域选择器等级联场景。
|
|
5
5
|
*
|
|
6
|
+
* @description 从字典管理器获取树形字典数据,挂载后自动触发网络请求(或缓存)。
|
|
7
|
+
* @param {string} type - 字典类型名(如 'region'),一参形式:useDictTree(type),使用默认仓库 'dicts'
|
|
8
|
+
* @param {StoreKey} storeName - 仓库名(如 'dicts2'),二参形式:useDictTree(storeName, type)
|
|
9
|
+
* @param {string} type - 字典类型名,二参形式时作为第二参数传入
|
|
10
|
+
* @returns {UseDictTreeReturn} 包含 tree(树节点数组)、translate(翻译函数)、findPath(路径回溯)、loading、refresh
|
|
11
|
+
*
|
|
6
12
|
* @example
|
|
7
13
|
* // 默认存储库 'dicts'
|
|
8
14
|
* const { tree, translate } = useDictTree('region')
|
|
@@ -7,19 +7,30 @@ export function useDictTree(storeOrType, maybeType) {
|
|
|
7
7
|
const storeName = maybeType === void 0 ? DEFAULT_STORE_NAME : storeOrType;
|
|
8
8
|
const dictType = maybeType ?? storeOrType;
|
|
9
9
|
const tree = shallowRef(null);
|
|
10
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
11
|
+
const pathMap = /* @__PURE__ */ new Map();
|
|
10
12
|
const loading = ref(false);
|
|
11
|
-
function translate(
|
|
12
|
-
|
|
13
|
+
function translate(value, opts) {
|
|
14
|
+
const targetStore = opts?.storeName ?? storeName;
|
|
15
|
+
const field = opts?.field ?? "label";
|
|
16
|
+
if (targetStore !== storeName) {
|
|
17
|
+
return manager.translate(dictType, value, { storeName: targetStore, field });
|
|
18
|
+
}
|
|
19
|
+
if (!tree.value) return String(value);
|
|
20
|
+
const node = nodeMap.get(String(value));
|
|
21
|
+
if (!node) return String(value);
|
|
22
|
+
return node[field] ?? node.label;
|
|
13
23
|
}
|
|
14
|
-
function findPath(
|
|
24
|
+
function findPath(value) {
|
|
15
25
|
if (!tree.value) return [];
|
|
16
|
-
return
|
|
26
|
+
return pathMap.get(String(value)) ?? [];
|
|
17
27
|
}
|
|
18
28
|
async function load() {
|
|
19
29
|
loading.value = true;
|
|
20
30
|
try {
|
|
21
31
|
const entry = await manager.getDict(dictType, storeName);
|
|
22
32
|
tree.value = entry.tree ?? null;
|
|
33
|
+
if (entry.tree) buildMaps(entry.tree, nodeMap, pathMap);
|
|
23
34
|
} finally {
|
|
24
35
|
loading.value = false;
|
|
25
36
|
}
|
|
@@ -29,25 +40,29 @@ export function useDictTree(storeOrType, maybeType) {
|
|
|
29
40
|
try {
|
|
30
41
|
const entry = await manager.refresh(dictType, storeName);
|
|
31
42
|
tree.value = entry.tree ?? null;
|
|
43
|
+
if (entry.tree) buildMaps(entry.tree, nodeMap, pathMap);
|
|
32
44
|
} finally {
|
|
33
45
|
loading.value = false;
|
|
34
46
|
}
|
|
35
47
|
}
|
|
36
48
|
onMounted(load);
|
|
37
49
|
watch(() => manager.locale.value, load);
|
|
38
|
-
return {
|
|
50
|
+
return {
|
|
51
|
+
tree,
|
|
52
|
+
translate,
|
|
53
|
+
findPath,
|
|
54
|
+
loading,
|
|
55
|
+
refresh
|
|
56
|
+
};
|
|
39
57
|
}
|
|
40
|
-
function
|
|
58
|
+
function buildMaps(nodes, nodeMap, pathMap, ancestors = []) {
|
|
41
59
|
for (const node of nodes) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
60
|
+
const code = String(node.value);
|
|
61
|
+
const path = [...ancestors, node.label];
|
|
62
|
+
nodeMap.set(code, node);
|
|
63
|
+
pathMap.set(code, path);
|
|
45
64
|
if (node.children && node.children.length > 0) {
|
|
46
|
-
|
|
47
|
-
if (childPath.length > 0) {
|
|
48
|
-
return [node.label, ...childPath];
|
|
49
|
-
}
|
|
65
|
+
buildMaps(node.children, nodeMap, pathMap, path);
|
|
50
66
|
}
|
|
51
67
|
}
|
|
52
|
-
return [];
|
|
53
68
|
}
|
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
* 获取/切换当前语言。
|
|
3
3
|
* 切换语言时会同步更新 cookie(客户端)并通知 DictManager 刷新缓存。
|
|
4
4
|
* locale 为 DictManager 上的响应式 ref,语言切换后所有 useDict / useDictTree 组件自动重取。
|
|
5
|
+
*
|
|
6
|
+
* @description 从 DictManager 获取当前语言信息,提供切换语言并持久化到 cookie 的能力。
|
|
7
|
+
* @returns {{ locale: Ref<string>, setLocale: (newLocale: string) => void, locales: string[] }} 当前语言 ref、语言切换函数、支持的语言列表
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const { locale, setLocale } = useLocale()
|
|
11
|
+
*
|
|
12
|
+
* // 切换语言
|
|
13
|
+
* setLocale('en-US') // 切换到英文
|
|
14
|
+
* console.log(locale.value) // 'en-US'
|
|
15
|
+
*
|
|
16
|
+
* // 模板中直接使用
|
|
17
|
+
* // <p>当前语言:{{ locale }}</p>
|
|
18
|
+
* // <button @click="setLocale('zh-CN')">中文</button>
|
|
5
19
|
*/
|
|
6
20
|
export declare function useLocale(): {
|
|
7
21
|
locale: import("vue").ShallowRef<string, string>;
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import type { DictAdapter } from '../types/index.js';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* 创建默认适配器所需的配置参数。
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* const adapter = createDefaultAdapter({
|
|
7
|
+
* baseURL: '/api',
|
|
8
|
+
* dictEndpoint: '/dict/list',
|
|
9
|
+
* versionEndpoint: '/dict/version',
|
|
10
|
+
* paramKey: 'lang',
|
|
11
|
+
* apiHeaderKey: 'X-Locale',
|
|
12
|
+
* })
|
|
13
|
+
*/
|
|
3
14
|
export interface DefaultAdapterOptions {
|
|
4
15
|
baseURL: string;
|
|
5
16
|
dictEndpoint: string;
|
|
@@ -13,5 +24,20 @@ export interface DefaultAdapterOptions {
|
|
|
13
24
|
* 创建默认的 REST 字典适配器。
|
|
14
25
|
* 底层使用原生 fetch,兼容浏览器和 Node.js 18+(Nuxt 4 要求)。
|
|
15
26
|
* SSR 侧由 resolveBaseURL() 保证 baseURL 为绝对 origin,客户端相对路径由浏览器处理。
|
|
27
|
+
*
|
|
28
|
+
* @param {DefaultAdapterOptions} options - 适配器配置参数(baseURL、dictEndpoint、versionEndpoint、paramKey、apiHeaderKey)
|
|
29
|
+
* @returns {DictAdapter} 实现了 fetchDict / fetchVersion 方法的 DictAdapter 实例
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const adapter = createDefaultAdapter({
|
|
33
|
+
* baseURL: 'https://api.example.com',
|
|
34
|
+
* dictEndpoint: '/v1/dict/list',
|
|
35
|
+
* versionEndpoint: '/v1/dict/version',
|
|
36
|
+
* paramKey: 'lang',
|
|
37
|
+
* apiHeaderKey: 'X-Locale',
|
|
38
|
+
* })
|
|
39
|
+
*
|
|
40
|
+
* // 用于 ModuleOptions.api.adapter
|
|
41
|
+
* // 或 stores.<storeName>.adapter
|
|
16
42
|
*/
|
|
17
43
|
export declare function createDefaultAdapter(options: DefaultAdapterOptions): DictAdapter;
|
|
@@ -20,7 +20,9 @@ async function fetchDictImpl(_storeName, config, types, locale) {
|
|
|
20
20
|
}
|
|
21
21
|
return response.json();
|
|
22
22
|
} catch (e) {
|
|
23
|
-
throw new Error(`Failed to fetch dictionary: ${e instanceof Error ? e.message : String(e)}`, {
|
|
23
|
+
throw new Error(`Failed to fetch dictionary: ${e instanceof Error ? e.message : String(e)}`, {
|
|
24
|
+
cause: e
|
|
25
|
+
});
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
async function fetchVersionImpl(_storeName, config) {
|
|
@@ -36,7 +38,9 @@ async function fetchVersionImpl(_storeName, config) {
|
|
|
36
38
|
}
|
|
37
39
|
return data.version;
|
|
38
40
|
} catch (e) {
|
|
39
|
-
throw new Error(`Failed to fetch version: ${e instanceof Error ? e.message : String(e)}`, {
|
|
41
|
+
throw new Error(`Failed to fetch version: ${e instanceof Error ? e.message : String(e)}`, {
|
|
42
|
+
cause: e
|
|
43
|
+
});
|
|
40
44
|
}
|
|
41
45
|
}
|
|
42
46
|
export function createDefaultAdapter(options) {
|
|
@@ -27,7 +27,9 @@ export class IndexedDBCache {
|
|
|
27
27
|
resolve();
|
|
28
28
|
});
|
|
29
29
|
request.addEventListener("error", () => {
|
|
30
|
-
reject(
|
|
30
|
+
reject(
|
|
31
|
+
new Error("Failed to open IndexedDB: " + (request.error?.message || "unknown error"))
|
|
32
|
+
);
|
|
31
33
|
});
|
|
32
34
|
request.addEventListener("blocked", () => {
|
|
33
35
|
reject(new Error("IndexedDB is blocked by another tab"));
|