为了端到端验证 Shrimp 框架,把 hand-admin 前端拷进 examples/basic-admin/web
作为临时演示用前端,@shrimp alias 直接指向同仓 src/,免构建。
修改要点:
- web/vite.config.ts: @shrimp 改成 ../../../src; proxy /admin-api 到 :3100
- web/tsconfig.json: 同步 @shrimp 路径; 删掉 hand-server-2 的 paths
- web/src/locales: 删掉 matter_*/zerobase 业务 key
- web/src/module: 删掉 matter_*/zerobase 业务 vue,只留 user/role/FieldRelativeTime
- examples/basic-admin/index.ts:
- mount 到 /admin-api/* 而不是 /api/*
- 加 /config /login /me 端点 (mock super-admin operator,固定 token)
- DemoShrimp 子类覆盖 getMenuTree 用模块自身 label
- MemoryShrimpCrud 的 getRelatedCrud 手动 wire 到 crudRegistry,
populate 才能跑 (后续 70→90 改进项)
curl 已验证 /config /login /me /product/list 全通,tagIds_label 正确旁挂。
99 lines
3.3 KiB
TypeScript
99 lines
3.3 KiB
TypeScript
import { ref, watch } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
|
|
export interface UseTableUrlSyncOptions {
|
|
pageSize: number
|
|
}
|
|
|
|
export function useTableUrlSync(options: UseTableUrlSyncOptions) {
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
|
|
const currentPage = ref(1)
|
|
const currentPageSize = ref(options.pageSize)
|
|
const keyword = ref('')
|
|
const searchKeyword = ref('')
|
|
const currentSortBy = ref('')
|
|
const currentSortOrder = ref<'asc' | 'desc'>('asc')
|
|
const filterData = ref<Record<string, any>>({})
|
|
|
|
function restoreFromQuery() {
|
|
const q = route.query
|
|
if (q.page) currentPage.value = Number(q.page) || 1
|
|
if (q.pageSize) currentPageSize.value = Number(q.pageSize) || options.pageSize
|
|
if (q.keyword) {
|
|
keyword.value = String(q.keyword)
|
|
searchKeyword.value = String(q.keyword)
|
|
}
|
|
if (q.sortBy) currentSortBy.value = String(q.sortBy)
|
|
if (q.sortOrder && (q.sortOrder === 'asc' || q.sortOrder === 'desc')) {
|
|
currentSortOrder.value = q.sortOrder
|
|
}
|
|
if (q.filter) {
|
|
try {
|
|
const parsed = JSON.parse(String(q.filter))
|
|
if (typeof parsed === 'object' && parsed !== null) {
|
|
filterData.value = parsed
|
|
}
|
|
} catch { /* ignore invalid JSON */ }
|
|
}
|
|
}
|
|
|
|
// 表格自身管理的 query key,sync 时只覆盖这几个,其它保留(如 ?id= 子表父记录、自定义参数)
|
|
const TABLE_QUERY_KEYS = ['page', 'pageSize', 'keyword', 'sortBy', 'sortOrder', 'filter']
|
|
|
|
function syncToQuery() {
|
|
const preserved: Record<string, any> = {}
|
|
for (const [k, v] of Object.entries(route.query)) {
|
|
if (!TABLE_QUERY_KEYS.includes(k)) preserved[k] = v
|
|
}
|
|
const query: Record<string, any> = { ...preserved }
|
|
if (currentPage.value > 1) query.page = String(currentPage.value)
|
|
if (currentPageSize.value !== options.pageSize) query.pageSize = String(currentPageSize.value)
|
|
if (searchKeyword.value) query.keyword = searchKeyword.value
|
|
if (currentSortBy.value) {
|
|
query.sortBy = currentSortBy.value
|
|
query.sortOrder = currentSortOrder.value
|
|
}
|
|
const cleanFilter: Record<string, any> = {}
|
|
for (const [key, val] of Object.entries(filterData.value)) {
|
|
if (val === '' || val === null || val === undefined) continue
|
|
if (Array.isArray(val) && val.length === 0) continue
|
|
cleanFilter[key] = val
|
|
}
|
|
if (Object.keys(cleanFilter).length > 0) {
|
|
query.filter = JSON.stringify(cleanFilter)
|
|
}
|
|
router.replace({ query })
|
|
}
|
|
|
|
let isRestoring = false
|
|
|
|
watch(
|
|
[currentPage, currentPageSize, searchKeyword, currentSortBy, currentSortOrder, filterData],
|
|
() => {
|
|
if (!isRestoring) {
|
|
syncToQuery()
|
|
}
|
|
},
|
|
{ deep: true }
|
|
)
|
|
|
|
function restore() {
|
|
isRestoring = true
|
|
restoreFromQuery()
|
|
isRestoring = false
|
|
}
|
|
|
|
return {
|
|
currentPage,
|
|
currentPageSize,
|
|
keyword,
|
|
searchKeyword,
|
|
currentSortBy,
|
|
currentSortOrder,
|
|
filterData,
|
|
restore,
|
|
}
|
|
}
|