vome-core 0.0.6 → 0.0.8
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/dist/index.js +236 -1
- package/dist/server/index.js +3261 -1
- package/package.json +10 -2
- package/src/admin/components/vm-auto-perm-dialog.vue +3 -0
- package/src/admin/components/vm-check-tree.vue +1 -0
- package/src/admin/components/vm-dept-tree.vue +3 -0
- package/src/admin/components/vm-empty.vue +1 -0
- package/src/admin/components/vm-eps-perm-picker.vue +2 -0
- package/src/admin/components/vm-group-cascader.vue +1 -0
- package/src/admin/components/vm-icon-picker.vue +1 -0
- package/src/admin/components/vm-markdown.vue +1 -0
- package/src/admin/components/vm-ri-icon.vue +2 -0
- package/src/admin/components/vm-role-picker.vue +2 -0
- package/src/admin/components/vm-view-path-picker.vue +1 -0
- package/src/admin/crud/components/vm-column-custom.vue +1 -0
- package/src/admin/crud/components/vm-context-menu.vue +1 -0
- package/src/admin/crud/components/vm-date-picker.vue +1 -0
- package/src/admin/crud/components/vm-date-range.vue +1 -0
- package/src/admin/crud/components/vm-date-text.vue +1 -0
- package/src/admin/crud/components/vm-dict-tag.vue +1 -0
- package/src/admin/crud/components/vm-file-icon.vue +1 -0
- package/src/admin/crud/components/vm-multi-select.vue +1 -0
- package/src/admin/crud/components/vm-select.vue +2 -0
- package/src/admin/crud/components/vm-switch.vue +1 -0
- package/src/admin/crud/components/vm-tree-select.vue +1 -0
- package/src/admin/crud/components/vm-upload-item.vue +1 -0
- package/src/admin/crud/components/vm-upload.vue +2 -0
- package/src/admin/crud/components/vm-user-select.vue +1 -0
- package/src/admin/crud/vm-add-btn.vue +1 -0
- package/src/admin/crud/vm-adv-search.vue +1 -0
- package/src/admin/crud/vm-crud.vue +2 -0
- package/src/admin/crud/vm-dialog.vue +1 -0
- package/src/admin/crud/vm-form.vue +1 -0
- package/src/admin/crud/vm-pagination.vue +1 -0
- package/src/admin/crud/vm-search-key.vue +1 -0
- package/src/admin/crud/vm-search.vue +1 -0
- package/src/admin/crud/vm-table.vue +1 -0
- package/src/admin/crud/vm-toolbar.vue +1 -0
- package/src/admin/crud/vm-upsert.vue +2 -0
- package/src/admin/lib/eps.ts +3 -2
- package/src/admin/service/index.ts +143 -46
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vome-core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "Vome framework — shared + Bun/Elysia server + Vue admin",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -48,7 +48,9 @@
|
|
|
48
48
|
"vue-sonner": ">=2.0.0",
|
|
49
49
|
"@vueuse/core": ">=10.0.0",
|
|
50
50
|
"clsx": ">=2.0.0",
|
|
51
|
-
"tailwind-merge": ">=2.0.0"
|
|
51
|
+
"tailwind-merge": ">=2.0.0",
|
|
52
|
+
"dompurify": ">=3.0.0",
|
|
53
|
+
"marked": ">=9.0.0"
|
|
52
54
|
},
|
|
53
55
|
"peerDependenciesMeta": {
|
|
54
56
|
"drizzle-orm": {
|
|
@@ -77,6 +79,12 @@
|
|
|
77
79
|
},
|
|
78
80
|
"tailwind-merge": {
|
|
79
81
|
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"dompurify": {
|
|
84
|
+
"optional": true
|
|
85
|
+
},
|
|
86
|
+
"marked": {
|
|
87
|
+
"optional": true
|
|
80
88
|
}
|
|
81
89
|
},
|
|
82
90
|
"devDependencies": {
|
|
@@ -71,6 +71,9 @@
|
|
|
71
71
|
</template>
|
|
72
72
|
|
|
73
73
|
<script setup lang="ts">
|
|
74
|
+
import { ref, computed, watch } from 'vue'
|
|
75
|
+
import { api } from '../api/client'
|
|
76
|
+
import { toast } from 'vue-sonner'
|
|
74
77
|
import { isDialogFloatTarget } from '../lib/dialog-float'
|
|
75
78
|
|
|
76
79
|
defineOptions({ name: 'vm-auto-perm-dialog' })
|
|
@@ -101,6 +101,9 @@
|
|
|
101
101
|
</template>
|
|
102
102
|
|
|
103
103
|
<script setup lang="ts">
|
|
104
|
+
import { useVome } from '../hooks/useVome'
|
|
105
|
+
import { ref, computed, watch, onMounted } from 'vue'
|
|
106
|
+
import { toast } from 'vue-sonner'
|
|
104
107
|
import { deepTree } from '../crud'
|
|
105
108
|
import { vmConfirm } from '../crud/confirm'
|
|
106
109
|
|
|
@@ -73,6 +73,8 @@
|
|
|
73
73
|
</template>
|
|
74
74
|
|
|
75
75
|
<script setup lang="ts">
|
|
76
|
+
import { ref, computed, watch } from 'vue'
|
|
77
|
+
import { toast } from 'vue-sonner'
|
|
76
78
|
import { useUpload } from '../../hooks/useUpload'
|
|
77
79
|
import { getFileType, getUrls, uploadUid } from '../../lib/upload'
|
|
78
80
|
import type { UploadItem } from '../../../../typings/admin/comm/upload'
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
7
|
<script setup lang="ts">
|
|
8
|
+
import { ref, computed, watch, shallowRef, provide } from 'vue'
|
|
9
|
+
import { service } from '../service/index'
|
|
8
10
|
import { toast } from 'vue-sonner'
|
|
9
11
|
import { resolveService } from '../service'
|
|
10
12
|
import { deepTree } from '../lib/tree'
|
|
@@ -224,6 +224,7 @@
|
|
|
224
224
|
</template>
|
|
225
225
|
|
|
226
226
|
<script setup lang="ts">
|
|
227
|
+
import { ref, computed, watch, onMounted, onBeforeUnmount, provide, useSlots } from 'vue'
|
|
227
228
|
import { useCrud } from './useCrud'
|
|
228
229
|
import { injectTableOptions, TABLE_API_KEY } from './key'
|
|
229
230
|
import { flattenTree } from '../lib/tree'
|
|
@@ -113,6 +113,8 @@
|
|
|
113
113
|
</template>
|
|
114
114
|
|
|
115
115
|
<script setup lang="ts">
|
|
116
|
+
import { ref, computed, watch, useSlots } from 'vue'
|
|
117
|
+
import { toast } from 'vue-sonner'
|
|
116
118
|
import { useCrud } from './useCrud'
|
|
117
119
|
import { injectUpsertOptions } from './key'
|
|
118
120
|
import { getCrudStyle } from './style'
|
package/src/admin/lib/eps.ts
CHANGED
|
@@ -12,10 +12,11 @@ const cache: Record<EpsSide, EpsModuleMap | null> = {
|
|
|
12
12
|
app: null,
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
/** 拉取 EPS(默认 admin
|
|
15
|
+
/** 拉取 EPS(默认 admin);公开接口失败不弹 toast */
|
|
16
16
|
export async function loadEps(force = false, side: EpsSide = 'admin') {
|
|
17
17
|
if (cache[side] && !force) return cache[side]
|
|
18
|
-
cache[side] =
|
|
18
|
+
cache[side] =
|
|
19
|
+
(await request<EpsModuleMap>(EPS_URL[side], { toast: false })) || {}
|
|
19
20
|
return cache[side]
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -1,7 +1,57 @@
|
|
|
1
1
|
import { loadEps } from '../lib/eps'
|
|
2
2
|
import { BaseService } from './base'
|
|
3
|
+
|
|
3
4
|
const SIDE_ID = 'admin'
|
|
4
5
|
|
|
6
|
+
/** 标准 CRUD 之外的常用接口(EPS 未就绪时也可用) */
|
|
7
|
+
const FALLBACK_EXTRA_APIS: Record<
|
|
8
|
+
string,
|
|
9
|
+
Array<{ method: string; path: string; perms?: string[] }>
|
|
10
|
+
> = {
|
|
11
|
+
'/admin/base/department': [{ method: 'get', path: '/tree' }],
|
|
12
|
+
'/admin/base/menu': [{ method: 'get', path: '/tree' }],
|
|
13
|
+
'/admin/base/role': [
|
|
14
|
+
{ method: 'get', path: '/menus' },
|
|
15
|
+
{ method: 'post', path: '/setMenus' },
|
|
16
|
+
{ method: 'get', path: '/departments' },
|
|
17
|
+
{ method: 'post', path: '/setDepartments' },
|
|
18
|
+
],
|
|
19
|
+
'/admin/base/user': [
|
|
20
|
+
{ method: 'get', path: '/roles' },
|
|
21
|
+
{ method: 'get', path: '/roleMap' },
|
|
22
|
+
{ method: 'post', path: '/setRoles' },
|
|
23
|
+
{ method: 'post', path: '/transferSuper' },
|
|
24
|
+
{ method: 'post', path: '/moveDepartment' },
|
|
25
|
+
],
|
|
26
|
+
'/admin/base/task': [
|
|
27
|
+
{ method: 'post', path: '/start' },
|
|
28
|
+
{ method: 'post', path: '/stop' },
|
|
29
|
+
{ method: 'post', path: '/once' },
|
|
30
|
+
{ method: 'get', path: '/log' },
|
|
31
|
+
],
|
|
32
|
+
'/admin/base/log': [
|
|
33
|
+
{ method: 'post', path: '/clear' },
|
|
34
|
+
{ method: 'post', path: '/setKeep' },
|
|
35
|
+
{ method: 'post', path: '/getKeep' },
|
|
36
|
+
],
|
|
37
|
+
'/admin/base/dict/info': [
|
|
38
|
+
{ method: 'get', path: '/types' },
|
|
39
|
+
{ method: 'post', path: '/data' },
|
|
40
|
+
{ method: 'post', path: '/get' },
|
|
41
|
+
{ method: 'post', path: '/find' },
|
|
42
|
+
{ method: 'post', path: '/getValues' },
|
|
43
|
+
{ method: 'post', path: '/pathValue' },
|
|
44
|
+
{ method: 'post', path: '/childValue' },
|
|
45
|
+
],
|
|
46
|
+
'/admin/base/module': [{ method: 'post', path: '/install' }],
|
|
47
|
+
'/admin/base/comm': [{ method: 'post', path: '/upload' }],
|
|
48
|
+
'/admin/user/info': [
|
|
49
|
+
{ method: 'get', path: '/roles' },
|
|
50
|
+
{ method: 'get', path: '/roleMap' },
|
|
51
|
+
{ method: 'post', path: '/setRoles' },
|
|
52
|
+
],
|
|
53
|
+
}
|
|
54
|
+
|
|
5
55
|
/** EPS 不可用时仍保证业务页链式可调 */
|
|
6
56
|
const FALLBACK_PREFIXES = [
|
|
7
57
|
'/admin/base/menu',
|
|
@@ -11,8 +61,13 @@ const FALLBACK_PREFIXES = [
|
|
|
11
61
|
'/admin/base/tenant',
|
|
12
62
|
'/admin/base/task',
|
|
13
63
|
'/admin/base/log',
|
|
14
|
-
'/admin/base/plugin
|
|
15
|
-
'/admin/
|
|
64
|
+
'/admin/base/plugin',
|
|
65
|
+
'/admin/base/dict/info',
|
|
66
|
+
'/admin/base/dict/type',
|
|
67
|
+
'/admin/base/module',
|
|
68
|
+
'/admin/base/comm',
|
|
69
|
+
'/admin/user/info',
|
|
70
|
+
'/admin/user/role',
|
|
16
71
|
]
|
|
17
72
|
|
|
18
73
|
function toCamel(str: string) {
|
|
@@ -30,7 +85,6 @@ function isValidName(name: string) {
|
|
|
30
85
|
/** 无 EPS perms 时生成权限码 */
|
|
31
86
|
function resolvePermCode(prefix: string, action: string) {
|
|
32
87
|
const parts = prefix.replace(/^\//, '').split('/').filter(Boolean)
|
|
33
|
-
// admin / module / resource...
|
|
34
88
|
const module = parts[1] || parts[0] || 'base'
|
|
35
89
|
let resource = parts.slice(2).join(':')
|
|
36
90
|
if (resource === module) {
|
|
@@ -51,14 +105,7 @@ function getMethodKeys(leaf: Record<string, unknown>) {
|
|
|
51
105
|
)
|
|
52
106
|
}
|
|
53
107
|
|
|
54
|
-
function
|
|
55
|
-
const base = new BaseService(namespace)
|
|
56
|
-
const leaf = base as unknown as ServiceLeaf
|
|
57
|
-
leaf.namespace = namespace.replace(/^\//, '')
|
|
58
|
-
leaf.permission = {}
|
|
59
|
-
leaf._permission = {}
|
|
60
|
-
|
|
61
|
-
// 挂到实例自身,避免业务侧 `...leaf` 丢失原型方法
|
|
108
|
+
function bindCrud(leaf: ServiceLeaf, base: BaseService) {
|
|
62
109
|
const proto = BaseService.prototype as unknown as Record<string, unknown>
|
|
63
110
|
for (const name of [
|
|
64
111
|
'request',
|
|
@@ -72,15 +119,31 @@ function createLeaf(namespace: string, apis: EpsApiItem[] = []): ServiceLeaf {
|
|
|
72
119
|
]) {
|
|
73
120
|
const fn = proto[name]
|
|
74
121
|
if (typeof fn === 'function') {
|
|
75
|
-
;(leaf as Record<string, unknown>)[name] = (
|
|
122
|
+
;(leaf as Record<string, unknown>)[name] = (
|
|
123
|
+
fn as (...a: unknown[]) => unknown
|
|
124
|
+
).bind(base)
|
|
76
125
|
}
|
|
77
126
|
}
|
|
127
|
+
}
|
|
78
128
|
|
|
129
|
+
function bindApis(leaf: ServiceLeaf, base: BaseService, apis: EpsApiItem[]) {
|
|
130
|
+
leaf.permission ||= {}
|
|
79
131
|
for (const api of apis) {
|
|
80
132
|
const name = methodName(api.path)
|
|
81
133
|
if (!isValidName(name) || /[-:]/g.test(name)) continue
|
|
82
134
|
const method = (api.method || 'get').toLowerCase()
|
|
83
|
-
|
|
135
|
+
// 已有同名方法则保留(避免 fallback 覆盖 EPS 绑定)
|
|
136
|
+
if (typeof (leaf as Record<string, unknown>)[name] === 'function') {
|
|
137
|
+
if (!leaf.permission[name]) {
|
|
138
|
+
leaf.permission[name] =
|
|
139
|
+
api.perms?.[0] || resolvePermCode(`/${leaf.namespace}`, name)
|
|
140
|
+
}
|
|
141
|
+
continue
|
|
142
|
+
}
|
|
143
|
+
;(leaf as Record<string, unknown>)[name] = function (
|
|
144
|
+
this: BaseService,
|
|
145
|
+
data?: unknown,
|
|
146
|
+
) {
|
|
84
147
|
return this.request({
|
|
85
148
|
url: api.path.startsWith('/') ? api.path : `/${api.path}`,
|
|
86
149
|
method,
|
|
@@ -90,8 +153,18 @@ function createLeaf(namespace: string, apis: EpsApiItem[] = []): ServiceLeaf {
|
|
|
90
153
|
leaf.permission[name] =
|
|
91
154
|
api.perms?.[0] || resolvePermCode(`/${leaf.namespace}`, name)
|
|
92
155
|
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function createLeaf(namespace: string, apis: EpsApiItem[] = []): ServiceLeaf {
|
|
159
|
+
const base = new BaseService(namespace)
|
|
160
|
+
const leaf = base as unknown as ServiceLeaf
|
|
161
|
+
leaf.namespace = namespace.replace(/^\//, '')
|
|
162
|
+
leaf.permission = {}
|
|
163
|
+
leaf._permission = {}
|
|
164
|
+
|
|
165
|
+
bindCrud(leaf, base)
|
|
166
|
+
bindApis(leaf, base, apis)
|
|
93
167
|
|
|
94
|
-
// 标准 CRUD 权限兜底(EPS 未列时仍有码)
|
|
95
168
|
for (const name of getMethodKeys(leaf as unknown as Record<string, unknown>)) {
|
|
96
169
|
if (!leaf.permission[name]) {
|
|
97
170
|
leaf.permission[name] = resolvePermCode(`/${leaf.namespace}`, name)
|
|
@@ -101,7 +174,28 @@ function createLeaf(namespace: string, apis: EpsApiItem[] = []): ServiceLeaf {
|
|
|
101
174
|
return leaf
|
|
102
175
|
}
|
|
103
176
|
|
|
104
|
-
|
|
177
|
+
/** 合并到已有叶子:补方法,不抹掉已有自定义 API */
|
|
178
|
+
function mergeLeaf(existing: ServiceLeaf, namespace: string, apis: EpsApiItem[]) {
|
|
179
|
+
const base = new BaseService(namespace)
|
|
180
|
+
// 确保 CRUD 在实例上(兼容旧 fallback)
|
|
181
|
+
bindCrud(existing, base)
|
|
182
|
+
bindApis(existing, base, apis)
|
|
183
|
+
existing.namespace = namespace.replace(/^\//, '')
|
|
184
|
+
existing.permission ||= {}
|
|
185
|
+
existing._permission ||= {}
|
|
186
|
+
for (const name of getMethodKeys(existing as unknown as Record<string, unknown>)) {
|
|
187
|
+
if (!existing.permission[name]) {
|
|
188
|
+
existing.permission[name] = resolvePermCode(`/${existing.namespace}`, name)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return existing
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function attachLeaf(
|
|
195
|
+
tree: ServiceTree | Eps.Service,
|
|
196
|
+
prefix: string,
|
|
197
|
+
apis: EpsApiItem[] = [],
|
|
198
|
+
) {
|
|
105
199
|
const raw = prefix.replace(/^\//, '')
|
|
106
200
|
const arr = raw
|
|
107
201
|
.replace(new RegExp(`^${SIDE_ID}/?`), '')
|
|
@@ -122,10 +216,7 @@ function attachLeaf(tree: ServiceTree | Eps.Service, prefix: string, apis: EpsAp
|
|
|
122
216
|
}
|
|
123
217
|
const existing = node[key] as ServiceLeaf | undefined
|
|
124
218
|
if (existing?.namespace) {
|
|
125
|
-
|
|
126
|
-
const next = createLeaf(raw, apis)
|
|
127
|
-
Object.assign(existing, next)
|
|
128
|
-
return existing
|
|
219
|
+
return mergeLeaf(existing, raw, apis)
|
|
129
220
|
}
|
|
130
221
|
const leaf = createLeaf(raw, apis)
|
|
131
222
|
node[key] = leaf
|
|
@@ -156,39 +247,41 @@ function buildFromEps(map: EpsModuleMap) {
|
|
|
156
247
|
|
|
157
248
|
function buildFallback() {
|
|
158
249
|
for (const prefix of FALLBACK_PREFIXES) {
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (!node[key]) {
|
|
170
|
-
exists = false
|
|
171
|
-
break
|
|
172
|
-
}
|
|
173
|
-
node = node[key] as Record<string, unknown>
|
|
174
|
-
}
|
|
175
|
-
if (!exists || !(node as ServiceLeaf).namespace) {
|
|
176
|
-
attachLeaf(service, prefix)
|
|
177
|
-
}
|
|
250
|
+
const extra = FALLBACK_EXTRA_APIS[prefix] || []
|
|
251
|
+
const apis = extra.map(
|
|
252
|
+
(a) =>
|
|
253
|
+
({
|
|
254
|
+
method: a.method,
|
|
255
|
+
path: a.path,
|
|
256
|
+
perms: a.perms,
|
|
257
|
+
}) as EpsApiItem,
|
|
258
|
+
)
|
|
259
|
+
attachLeaf(service, prefix, apis)
|
|
178
260
|
}
|
|
179
261
|
}
|
|
180
262
|
|
|
181
|
-
|
|
263
|
+
type ServiceRoot = Eps.Service & { request: BaseService['request'] }
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* 全局单例:npm 包经多路径解析时避免出现两份 service(一份有 tree、一份没有)。
|
|
267
|
+
*/
|
|
268
|
+
const g = globalThis as typeof globalThis & { __vome_admin_service__?: ServiceRoot }
|
|
269
|
+
|
|
270
|
+
function createServiceRoot(): ServiceRoot {
|
|
271
|
+
const root = new BaseService()
|
|
272
|
+
return {
|
|
273
|
+
request: root.request.bind(root),
|
|
274
|
+
} as ServiceRoot
|
|
275
|
+
}
|
|
182
276
|
|
|
183
277
|
/** 全局链式 service(service.base.user.page()) */
|
|
184
|
-
export const service =
|
|
185
|
-
|
|
186
|
-
} as unknown as Eps.Service
|
|
278
|
+
export const service: ServiceRoot =
|
|
279
|
+
g.__vome_admin_service__ ?? (g.__vome_admin_service__ = createServiceRoot())
|
|
187
280
|
|
|
188
|
-
//
|
|
281
|
+
// 同步兜底(含 tree / menus 等),页面 import 时即可调用
|
|
189
282
|
buildFallback()
|
|
190
283
|
|
|
191
|
-
/** 从 EPS 构建/刷新 service 树;失败时用兜底 prefix */
|
|
284
|
+
/** 从 EPS 构建/刷新 service 树;失败时用兜底 prefix + 自定义 API */
|
|
192
285
|
export async function createEps(force = false) {
|
|
193
286
|
try {
|
|
194
287
|
const map = (await loadEps(force)) || {}
|
|
@@ -200,7 +293,6 @@ export async function createEps(force = false) {
|
|
|
200
293
|
return service
|
|
201
294
|
}
|
|
202
295
|
|
|
203
|
-
// Vite 保存源码 / 刷新时:插件重拉 EPS 后通知客户端热更新 service 树
|
|
204
296
|
if (import.meta.hot) {
|
|
205
297
|
import.meta.hot.on('eps-update', () => {
|
|
206
298
|
void createEps(true).then(() => {
|
|
@@ -269,7 +361,12 @@ export function serviceOf(prefix: string): ServiceLeaf {
|
|
|
269
361
|
.join('.')
|
|
270
362
|
const hit = resolveService(path)
|
|
271
363
|
if (hit) return hit
|
|
272
|
-
|
|
364
|
+
const extra = FALLBACK_EXTRA_APIS[raw.startsWith('/') ? raw : `/${raw}`] || []
|
|
365
|
+
return attachLeaf(
|
|
366
|
+
service,
|
|
367
|
+
raw,
|
|
368
|
+
extra.map((a) => ({ method: a.method, path: a.path, perms: a.perms }) as EpsApiItem),
|
|
369
|
+
) as ServiceLeaf
|
|
273
370
|
}
|
|
274
371
|
|
|
275
372
|
/** @deprecated 请用 service.base.xxx */
|