20260920173439

This commit is contained in:
oneao committed 2026-09-20 17:34:40 +08:00
1 parent f4bfc1ddfc
commit 6f03d92425
174 files changed
+23490 -157

No files matched your search

@@ -66,6 +66,9 @@ const props = defineProps({
rowDraggable: { type: Boolean, default: false },
// 行拖拽排序字段;未显式传入时不启用业务序号排序。
orderField: { type: String, default: '' },
// 临时放开的隐藏列(view JSON 里 visible:false 的字段):传入字段名即强制显示该列,
// 供「查看密码」这类按需揭示敏感列的页面开关使用。只影响本次渲染,不回写 view 配置。
revealedColumns: { type: Array, default: () => [] },
})
const emit = defineEmits([
@@ -128,6 +131,78 @@ const columnDirty = ref(false)
const columnPrefRowExists = ref(false)
// 通用栏位行(s_module_schema.view 展开结果):个人增量与其比较,「保存为通用」也以它为底
const columnGlobalRows = shallowRef([])
// view JSON 里 visible:false 的列(如 b_password):默认不渲染,由 revealedColumns 开关按需追加
const hiddenColumnRows = shallowRef([])
// 个人层栏位增量(s_user_module_pref):revealedColumns 开关重算列时复用同一份,避免丢个人宽度/顺序
const savedColumnPref = shallowRef(null)
/**
* 从隐藏列里挑出本次要揭示的行。
*
* 关键:必须把 b_visible 改写成 1 再交出去。FmsModuleTable.buildColumns 会再过滤一次
* `b_visible === 1`,原样透传 b_visible:0 会在那一层被丢掉,列永远出不来。
* 返回新对象,不改 hiddenColumnRows 里的原行,保证关掉开关后仍是隐藏态。
*
* @param orderedRows view JSON 展开后的完整行序(listRows),用于保持列的原声明顺序
* @param revealed 要揭示的字段名
*/
function revealedColumnsOf(orderedRows, revealed) {
const wanted = new Set((revealed || []).map((field) => String(field)))
if (!wanted.size) return []
return (orderedRows || [])
.filter((row) => wanted.has(String(row.b_field)))
.map((row) => ({ ...row, b_visible: 1 }))
}
/**
* 把揭示出来的列按 view JSON 的声明顺序插回可见列里。
*
* 不能直接 [...visible, ...revealed] 追加到末尾:那样列位置由开关决定而不是配置决定,
* 配置里写在 b_id 之后的列会跑到最后一列。这里按 b_xh(声明序号)做一次归并。
*/
function mergeRevealedColumns(visibleRows, revealedRows) {
if (!revealedRows.length) return visibleRows
const rank = (row) => {
const xh = Number(row?.b_xh)
// 无声明序号的排在最后,保持稳定
return Number.isFinite(xh) ? xh : Number.MAX_SAFE_INTEGER
}
return [...visibleRows, ...revealedRows]
.map((row, index) => ({ row, index }))
.toSorted((a, b) => {
const ra = rank(a.row)
const rb = rank(b.row)
return ra === rb ? a.index - b.index : ra - rb
})
.map((item) => item.row)
}
/**
* 把个人层已保存的栏位顺序,重写成「含揭示列」的新顺序。
*
* 个人层 order 是整体排列。用户早先保存过顺序时,列表里只含当时的可见列;
* 揭示列(如 b_password)不在其中,mergeColumnPref 会按「未列入的字段追加在后」
* 把它排到最后一列 —— 上游插得再对也会被这一步覆盖。
* 这里按揭示列的 b_xh 找到它该在的位置,把 order 补全后再交给 mergeColumnPref。
*
* @param pref 个人层增量(含 order,可能为 null)
* @param visibleRows 可见列(通用顺序,含 b_xh)
* @param revealedRows 本次揭示的列(含 b_xh)
*/
function withRevealedInOrder(pref, visibleRows, revealedRows) {
if (!pref || !revealedRows.length) return pref
const orderList = Array.isArray(pref.order) ? pref.order.map(String) : []
if (!orderList.length) return pref
// 按 b_xh 归并:揭示列插到第一个声明序号比它大的可见列之前,与真实列序一致
const sequence = mergeRevealedColumns(visibleRows, revealedRows).map((row) =>
String(row.b_field),
)
// 只保留确实存在于本次列集合里的字段,并保持个人层的相对顺序
const kept = orderList.filter((field) => sequence.includes(field))
const merged = sequence.filter((field) => kept.includes(field) || !orderList.includes(field))
return { ...pref, order: merged }
}
// 「保存为通用」会改到所有人的默认视图,限系统管理员(与 /module/* 的 meta.adminOnly 同一判据)
const canSaveColumnGlobal = computed(() => permissions.isG3soft)
// 无查询权限的字段(s_user_field_power.b_query = 0),设置抽屉中灰显
@@ -313,12 +388,23 @@ async function loadModuleConfig() {
}
editConfigById.value = editConfigByIdMap
// 通用栏位基线(lists)恒为「常规可见列」,揭示列不进基线:
// 否则开关重算时会与隐藏列副本重复拼接,出现两列表头。
const lists = []
hiddenColumnRows.value = []
for (const item of listRows) {
if (Number(item.b_visible) !== 1) continue
if (!fieldById.has(String(item.b_field))) continue
if (Number(item.b_visible) !== 1) {
// view 里显式隐藏的列(如 b_password):默认不渲染,只留副本供揭示开关取用
hiddenColumnRows.value.push(item)
continue
}
lists.push(item)
}
// 已揭示的隐藏列:按 view JSON 声明的原始顺序插回,而不是一律追加到末尾;
// 基线仍是纯可见列,揭示列只参与本次渲染。
const revealedLists = revealedColumnsOf(listRows, props.revealedColumns)
const renderLists = mergeRevealedColumns(lists, revealedLists)
// 通用栏位(lists)即「保存为通用」的基线与个人增量的比较基准;
// 个人层只叠加宽度与顺序,不增删字段,因此先裁到同一字段集再合并。
columnGlobalRows.value = lists
@@ -347,7 +433,7 @@ async function loadModuleConfig() {
// 两层配置(系统默认 → 个人)+ 字段查询权限;权限在偏好合并之后裁剪,不得放宽权限
const userId = currentUserId()
// 查询布局(query)与栏位布局(view)各有一份个人层增量,互不影响
const [savedPref, savedColumnPref] = await Promise.all([
const [savedPref, savedColumnPrefDelta] = await Promise.all([
loadModulePref(userId, dataId),
loadModulePref(userId, dataId, PREF_SCHEMA_TYPES.view),
])
@@ -374,9 +460,15 @@ async function loadModuleConfig() {
queryLayout.value = mergeQueryLayout(defaultQueryLayout.value, savedPref, allowedFields)
// 栏位:通用(lists)→ 个人层叠加宽度与顺序;个人层不增删字段(通用没有的字段一律忽略)
const mergedLists = mergeColumnPref(lists, savedColumnPref)
columnPrefRowExists.value = Boolean(savedColumnPref)
columnBaseline.value = columnRowsOf(mergedLists)
// 首次渲染带上已揭示的隐藏列(按声明顺序插回);基线只取通用可见列,避免把揭示列固化进栏位设置
// 个人层 order 不含揭示列时会把列挤到末尾,先补进 order 再合并
const mergedLists = mergeColumnPref(
renderLists,
withRevealedInOrder(savedColumnPrefDelta, lists, revealedLists),
)
columnPrefRowExists.value = Boolean(savedColumnPrefDelta)
savedColumnPref.value = savedColumnPrefDelta
columnBaseline.value = columnRowsOf(mergeColumnPref(lists, savedColumnPrefDelta))
columnDirty.value = false
// 默认条件取全部启用字段:简单查询与高级查询共用同一批启用字段
@@ -1260,6 +1352,22 @@ onActivated(async () => {
watch(() => props.dataCode, initialize)
// 按需揭示的隐藏列(如「查看密码」):开关变化时不重新请求数据,
// 只在已有的通用栏位基线上重算可见列,避免整表刷新造成闪烁。
watch(
() => props.revealedColumns,
() => {
if (!columnGlobalRows.value.length && !props.revealedColumns?.length) return
// 按 view JSON 声明顺序插回,与 loadModuleConfig 走同一套归并,避免两处顺序不一致
const next = revealedColumnsOf(hiddenColumnRows.value, props.revealedColumns)
listConfig.value = mergeColumnPref(
mergeRevealedColumns(columnGlobalRows.value, next),
// 个人层 order 不含揭示列时会把列挤到末尾,先补进 order 再合并
withRevealedInOrder(savedColumnPref.value, columnGlobalRows.value, next),
)
},
)
watch(
() => props.fixedSearchCondition,
() => {