Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
P
problem_rectification
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
David Yang
problem_rectification
Commits
c536e180
Commit
c536e180
authored
Jun 15, 2026
by
David Yang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: applications 切换
parent
88f229a6
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
334 additions
and
12 deletions
+334
-12
manifest.json
public/manifest.json
+1
-1
ReferenceRecordsTable.jsx
...s/RecordDetailDrawer/components/ReferenceRecordsTable.jsx
+244
-0
TailwindEditableField.jsx
...s/RecordDetailDrawer/components/TailwindEditableField.jsx
+32
-2
useSubjectRecord.js
src/pages/RecordDetailDrawer/hooks/useSubjectRecord.js
+2
-1
ApplicationSwitcherDialog.jsx
...pages/problem_rectification/ApplicationSwitcherDialog.jsx
+0
-0
Layout.jsx
src/pages/problem_rectification/Layout.jsx
+47
-6
problem-rectification.business-flow.md
...blem_rectification/problem-rectification.business-flow.md
+2
-0
subjectView.js
src/utils/subjectView.js
+6
-2
No files found.
public/manifest.json
View file @
c536e180
{
{
"name"
:
"problem_rectification_new"
,
"name"
:
"problem_rectification_new"
,
"version"
:
"1.0.
3
"
,
"version"
:
"1.0.
4
"
,
"build"
:
"abc"
,
"build"
:
"abc"
,
"description"
:
"merge master"
,
"description"
:
"merge master"
,
"date"
:
"2025-07-14 19:27:20"
,
"date"
:
"2025-07-14 19:27:20"
,
...
...
src/pages/RecordDetailDrawer/components/ReferenceRecordsTable.jsx
0 → 100644
View file @
c536e180
import
{
useEffect
,
useMemo
,
useState
}
from
'react'
;
import
{
Table
,
TableBody
,
TableCell
,
TableHead
,
TableHeader
,
TableRow
,
}
from
'@/catalyst-ui-kit/javascript/table'
;
import
{
loadSubject
,
queryRecords
}
from
'@/services/DataService'
;
import
{
formatFieldValue
,
getFieldValue
,
getReferenceSubjectName
,
normalizeRecordId
,
toArray
,
}
from
'@/utils/subjectView'
;
const
TITLE_COLUMN
=
{
field
:
'title'
,
label
:
'标题'
,
fieldDef
:
{
name
:
'title'
,
label
:
'标题'
,
type
:
'text'
},
};
function
hasRecordBody
(
value
)
{
return
value
&&
typeof
value
===
'object'
&&
Boolean
(
value
.
metadata
||
value
.
title
||
value
.
name
);
}
function
getReferenceIds
(
value
)
{
return
toArray
(
value
).
map
(
normalizeRecordId
).
filter
(
Boolean
);
}
function
inferColumnsFromRecords
(
records
)
{
const
metadataKeys
=
[];
const
seenKeys
=
new
Set
();
records
.
forEach
((
record
)
=>
{
Object
.
keys
(
record
?.
metadata
||
{}).
forEach
((
key
)
=>
{
if
(
!
seenKeys
.
has
(
key
))
{
seenKeys
.
add
(
key
);
metadataKeys
.
push
(
key
);
}
});
});
const
metadataColumns
=
metadataKeys
.
map
((
key
)
=>
({
field
:
`metadata.
${
key
}
`
,
label
:
key
,
fieldDef
:
{
name
:
key
,
label
:
key
,
type
:
'text'
},
}));
return
metadataColumns
.
length
>
0
?
metadataColumns
:
[
TITLE_COLUMN
];
}
function
buildColumns
(
subject
,
records
)
{
if
(
!
subject
?.
fields
)
{
return
inferColumnsFromRecords
(
records
);
}
const
metadataColumns
=
toArray
(
subject
?.
fields
).
map
((
field
)
=>
({
field
:
`metadata.
${
field
.
name
}
`
,
label
:
field
.
label
||
field
.
name
,
fieldDef
:
field
,
}));
return
metadataColumns
.
length
>
0
?
metadataColumns
:
[
TITLE_COLUMN
];
}
function
sortRecordsByReferenceOrder
(
records
,
referenceIds
)
{
const
orderMap
=
new
Map
(
referenceIds
.
map
((
id
,
index
)
=>
[
id
,
index
]));
return
[...
records
].
sort
((
left
,
right
)
=>
{
const
leftOrder
=
orderMap
.
get
(
normalizeRecordId
(
left
))
??
Number
.
MAX_SAFE_INTEGER
;
const
rightOrder
=
orderMap
.
get
(
normalizeRecordId
(
right
))
??
Number
.
MAX_SAFE_INTEGER
;
return
leftOrder
-
rightOrder
;
});
}
function
CellValue
({
value
,
fieldDef
})
{
const
text
=
formatFieldValue
(
value
,
fieldDef
);
if
(
!
text
)
{
return
<
span
className=
"text-zinc-400"
>
-
</
span
>;
}
if
(
fieldDef
?.
type
===
'object'
||
fieldDef
?.
type
===
'form'
||
fieldDef
?.
type
===
'subject'
)
{
return
(
<
span
className=
"block max-w-80 truncate font-mono text-xs text-zinc-600"
title=
{
text
}
>
{
text
}
</
span
>
);
}
return
(
<
span
className=
"block max-w-80 whitespace-normal break-words text-zinc-700"
>
{
text
}
</
span
>
);
}
export
default
function
ReferenceRecordsTable
({
fieldDef
,
value
,
pageSize
=
1000
})
{
const
subjectName
=
getReferenceSubjectName
(
fieldDef
);
const
referenceIds
=
useMemo
(()
=>
getReferenceIds
(
value
),
[
value
]);
const
populatedRecords
=
useMemo
(
()
=>
toArray
(
value
).
filter
(
hasRecordBody
),
[
value
],
);
const
[
subject
,
setSubject
]
=
useState
(
null
);
const
[
records
,
setRecords
]
=
useState
([]);
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
error
,
setError
]
=
useState
(
''
);
useEffect
(()
=>
{
let
cancelled
=
false
;
const
loadReferenceData
=
async
()
=>
{
setError
(
''
);
setLoading
(
false
);
setSubject
(
null
);
setRecords
([]);
if
(
referenceIds
.
length
===
0
)
{
return
;
}
if
(
!
subjectName
)
{
setRecords
(
populatedRecords
);
return
;
}
setLoading
(
true
);
try
{
const
shouldQueryRecords
=
populatedRecords
.
length
<
referenceIds
.
length
;
const
[
subjectData
,
recordsResult
]
=
await
Promise
.
all
([
loadSubject
(
subjectName
),
shouldQueryRecords
?
queryRecords
({
subject
:
subjectName
,
page
:
0
,
page_size
:
pageSize
,
filter
:
{
_id
:
{
$in
:
referenceIds
},
},
mode
:
'full'
,
})
:
Promise
.
resolve
({
records
:
populatedRecords
}),
]);
if
(
cancelled
)
{
return
;
}
const
loadedRecords
=
recordsResult
?.
records
||
populatedRecords
;
setSubject
(
subjectData
||
null
);
setRecords
(
sortRecordsByReferenceOrder
(
loadedRecords
,
referenceIds
));
}
catch
(
requestError
)
{
if
(
!
cancelled
)
{
setError
(
requestError
?.
message
||
'引用记录加载失败,请稍后重试。'
);
setSubject
(
null
);
setRecords
(
populatedRecords
);
}
}
finally
{
if
(
!
cancelled
)
{
setLoading
(
false
);
}
}
};
loadReferenceData
();
return
()
=>
{
cancelled
=
true
;
};
},
[
pageSize
,
populatedRecords
,
referenceIds
,
subjectName
]);
if
(
referenceIds
.
length
===
0
)
{
return
(
<
div
className=
"rounded-2xl border border-dashed border-zinc-200 bg-zinc-50 px-4 py-5 text-sm text-zinc-500"
>
暂无关联记录
</
div
>
);
}
if
(
!
subjectName
&&
populatedRecords
.
length
===
0
)
{
return
(
<
div
className=
"rounded-2xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-700"
>
引用字段缺少目标 Subject 配置,暂不能加载关联明细。
</
div
>
);
}
if
(
loading
)
{
return
(
<
div
className=
"rounded-2xl border border-zinc-200 bg-white px-4 py-5 text-sm text-zinc-500 shadow-sm"
>
正在加载关联明细...
</
div
>
);
}
const
columns
=
buildColumns
(
subject
,
records
);
return
(
<
div
className=
"space-y-2"
>
{
error
?
(
<
div
className=
"rounded-2xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-700"
>
{
error
}
</
div
>
)
:
null
}
<
div
className=
"overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm"
>
<
div
className=
"border-b border-zinc-100 px-4 py-3 text-xs text-zinc-500"
>
共
{
records
.
length
}
条关联记录
</
div
>
{
records
.
length
===
0
?
(
<
div
className=
"px-4 py-5 text-sm text-zinc-500"
>
暂无可展示的关联明细
</
div
>
)
:
(
<
Table
dense
grid
className=
"max-h-96 overflow-auto"
>
<
TableHead
>
<
TableRow
>
{
columns
.
map
((
column
)
=>
(
<
TableHeader
key=
{
column
.
field
}
className=
"bg-zinc-50 text-xs font-semibold text-zinc-600"
>
{
column
.
label
}
</
TableHeader
>
))
}
</
TableRow
>
</
TableHead
>
<
TableBody
>
{
records
.
map
((
record
,
rowIndex
)
=>
(
<
TableRow
key=
{
normalizeRecordId
(
record
)
||
`${record?.name || 'reference'}-${rowIndex}`
}
>
{
columns
.
map
((
column
)
=>
(
<
TableCell
key=
{
column
.
field
}
className=
"align-top text-sm"
>
<
CellValue
value=
{
getFieldValue
(
record
,
column
.
field
)
}
fieldDef=
{
column
.
fieldDef
}
/>
</
TableCell
>
))
}
</
TableRow
>
))
}
</
TableBody
>
</
Table
>
)
}
</
div
>
</
div
>
);
}
src/pages/RecordDetailDrawer/components/TailwindEditableField.jsx
View file @
c536e180
...
@@ -6,10 +6,12 @@ import { Select } from '@/catalyst-ui-kit/javascript/select';
...
@@ -6,10 +6,12 @@ import { Select } from '@/catalyst-ui-kit/javascript/select';
import
{
Textarea
}
from
'@/catalyst-ui-kit/javascript/textarea'
;
import
{
Textarea
}
from
'@/catalyst-ui-kit/javascript/textarea'
;
import
{
queryUsers
,
uploadFile
}
from
'@/services/DataService'
;
import
{
queryUsers
,
uploadFile
}
from
'@/services/DataService'
;
import
ReferenceSearchSelect
from
'./ReferenceSearchSelect'
;
import
ReferenceSearchSelect
from
'./ReferenceSearchSelect'
;
import
ReferenceRecordsTable
from
'./ReferenceRecordsTable'
;
import
{
import
{
dateInputValue
,
dateInputValue
,
formatFieldValue
,
formatFieldValue
,
getSelectOptions
,
getSelectOptions
,
isDateFieldType
,
isReferenceFieldType
,
isReferenceFieldType
,
normalizeUserLabel
,
normalizeUserLabel
,
toArray
,
toArray
,
...
@@ -222,6 +224,7 @@ export default function TailwindEditableField({
...
@@ -222,6 +224,7 @@ export default function TailwindEditableField({
const
canEdit
=
!
readonly
&&
!
disabled
;
const
canEdit
=
!
readonly
&&
!
disabled
;
const
fieldType
=
fieldDef
?.
type
||
'text'
;
const
fieldType
=
fieldDef
?.
type
||
'text'
;
const
isDateField
=
isDateFieldType
(
fieldType
);
const
isLongText
=
[
'long_text'
,
'rich_text'
].
includes
(
fieldType
);
const
isLongText
=
[
'long_text'
,
'rich_text'
].
includes
(
fieldType
);
const
isJsonLike
=
[
'object'
,
'form'
,
'subject'
].
includes
(
fieldType
);
const
isJsonLike
=
[
'object'
,
'form'
,
'subject'
].
includes
(
fieldType
);
...
@@ -300,6 +303,33 @@ export default function TailwindEditableField({
...
@@ -300,6 +303,33 @@ export default function TailwindEditableField({
};
};
if
(
!
editing
)
{
if
(
!
editing
)
{
if
(
isReferenceFieldType
(
fieldType
))
{
return
(
<
div
className=
{
clsx
(
'group min-h-10 w-full rounded-2xl px-3 py-2.5 text-left text-sm transition'
,
canEdit
?
'bg-transparent hover:bg-rose-50/70 hover:ring-1 hover:ring-rose-100'
:
'bg-transparent opacity-80'
,
)
}
>
<
ReferenceRecordsTable
value=
{
value
}
fieldDef=
{
fieldDef
}
/>
<
div
className=
"mt-2 flex items-center justify-between gap-3"
>
{
disabled
?
<
div
className=
"text-xs text-amber-600"
>
当前视图无编辑权限
</
div
>
:
<
span
/>
}
{
canEdit
?
(
<
button
type=
"button"
onClick=
{
()
=>
setEditing
(
true
)
}
className=
"text-[11px] font-medium text-zinc-400 transition hover:text-rose-700 group-hover:text-rose-600"
>
点击编辑引用
</
button
>
)
:
null
}
</
div
>
</
div
>
);
}
return
(
return
(
<
button
<
button
type=
"button"
type=
"button"
...
@@ -497,8 +527,8 @@ export default function TailwindEditableField({
...
@@ -497,8 +527,8 @@ export default function TailwindEditableField({
return
(
return
(
<
div
className=
"space-y-3"
>
<
div
className=
"space-y-3"
>
<
Input
<
Input
type=
{
fieldType
===
'number'
?
'number'
:
fieldType
===
'date'
?
'date'
:
'text'
}
type=
{
fieldType
===
'number'
?
'number'
:
isDateField
?
'date'
:
'text'
}
value=
{
fieldType
===
'date'
?
dateInputValue
(
draft
)
:
draft
??
''
}
value=
{
isDateField
?
dateInputValue
(
draft
)
:
draft
??
''
}
onChange=
{
(
event
)
=>
{
onChange=
{
(
event
)
=>
{
const
nextValue
=
fieldType
===
'number'
const
nextValue
=
fieldType
===
'number'
?
event
.
target
.
value
===
''
?
''
:
Number
(
event
.
target
.
value
)
?
event
.
target
.
value
===
''
?
''
:
Number
(
event
.
target
.
value
)
...
...
src/pages/RecordDetailDrawer/hooks/useSubjectRecord.js
View file @
c536e180
...
@@ -7,6 +7,7 @@ import {
...
@@ -7,6 +7,7 @@ import {
}
from
'@/services/DataService'
;
}
from
'@/services/DataService'
;
import
{
import
{
isFieldDisabledByView
,
isFieldDisabledByView
,
isDateFieldType
,
isReferenceFieldType
,
isReferenceFieldType
,
setFieldValue
,
setFieldValue
,
toIsoDateString
,
toIsoDateString
,
...
@@ -28,7 +29,7 @@ function transformSaveValue(newValue, fieldDef) {
...
@@ -28,7 +29,7 @@ function transformSaveValue(newValue, fieldDef) {
return
newValue
?.
_id
||
newValue
;
return
newValue
?.
_id
||
newValue
;
}
}
if
(
fieldDef
?.
type
===
'date'
)
{
if
(
isDateFieldType
(
fieldDef
?.
type
)
)
{
return
toIsoDateString
(
newValue
);
return
toIsoDateString
(
newValue
);
}
}
...
...
src/pages/problem_rectification/ApplicationSwitcherDialog.jsx
0 → 100644
View file @
c536e180
This diff is collapsed.
Click to expand it.
src/pages/problem_rectification/Layout.jsx
View file @
c536e180
import
ApplicationSwitcherDialog
from
'@/pages/problem_rectification/ApplicationSwitcherDialog'
;
import
xyLogoSmall
from
'@/assets/xy-logo_small.png'
;
import
xyLogoSmall
from
'@/assets/xy-logo_small.png'
;
import
{
UserContext
}
from
'@/wrapper/Auth'
;
import
{
UserContext
}
from
'@/wrapper/Auth'
;
import
{
SiteContext
}
from
'@/wrapper/Site'
;
import
{
import
{
ArchiveBoxIcon
,
ArchiveBoxIcon
,
Bars3Icon
,
Bars3Icon
,
ClipboardDocumentCheckIcon
,
ClipboardDocumentCheckIcon
,
HomeModernIcon
,
HomeModernIcon
,
Squares2X2Icon
,
XMarkIcon
,
XMarkIcon
,
}
from
'@heroicons/react/24/outline'
;
}
from
'@heroicons/react/24/outline'
;
import
clsx
from
'clsx'
;
import
clsx
from
'clsx'
;
...
@@ -180,7 +183,9 @@ function MobileSidebar({ open, close, children }) {
...
@@ -180,7 +183,9 @@ function MobileSidebar({ open, close, children }) {
export
default
function
ProblemRectificationLayout
()
{
export
default
function
ProblemRectificationLayout
()
{
const
location
=
useLocation
();
const
location
=
useLocation
();
const
userInfo
=
useContext
(
UserContext
);
const
userInfo
=
useContext
(
UserContext
);
const
siteInfo
=
useContext
(
SiteContext
);
const
[
isMobile
,
setIsMobile
]
=
useState
(
false
);
const
[
isMobile
,
setIsMobile
]
=
useState
(
false
);
const
[
isApplicationDialogOpen
,
setIsApplicationDialogOpen
]
=
useState
(
false
);
const
[
isSidebarCollapsed
,
setIsSidebarCollapsed
]
=
useState
(
false
);
const
[
isSidebarCollapsed
,
setIsSidebarCollapsed
]
=
useState
(
false
);
const
[
isMobileSidebarOpen
,
setIsMobileSidebarOpen
]
=
useState
(
false
);
const
[
isMobileSidebarOpen
,
setIsMobileSidebarOpen
]
=
useState
(
false
);
...
@@ -224,6 +229,21 @@ export default function ProblemRectificationLayout() {
...
@@ -224,6 +229,21 @@ export default function ProblemRectificationLayout() {
);
);
},
[
location
.
pathname
]);
},
[
location
.
pathname
]);
const
applicationSwitcherButton
=
(
<
button
type=
"button"
onClick=
{
()
=>
setIsApplicationDialogOpen
(
true
)
}
aria
-
label=
"Open application switcher"
title=
"应用切换"
className=
{
clsx
(
'inline-flex items-center justify-center rounded-[20px] border border-[#f97316]/70 bg-[linear-gradient(180deg,rgba(39,39,42,0.92),rgba(24,24,27,0.96))] text-[#fb923c] shadow-[0_16px_30px_rgba(249,115,22,0.16)] transition duration-200 hover:-translate-y-0.5 hover:border-[#fb923c] hover:text-[#fdba74] hover:shadow-[0_18px_36px_rgba(249,115,22,0.26)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[#fb923c]/50'
,
sidebarCollapsed
?
'size-11'
:
'size-14'
,
)
}
>
<
Squares2X2Icon
className=
{
clsx
(
sidebarCollapsed
?
'size-5'
:
'size-7'
)
}
/>
</
button
>
);
return
(
return
(
<
div
<
div
className=
"flex h-screen overflow-hidden bg-[radial-gradient(circle_at_top_left,_rgba(251,191,188,0.22),_transparent_26%),radial-gradient(circle_at_bottom_right,_rgba(251,207,232,0.2),_transparent_30%),#f8fafc]"
className=
"flex h-screen overflow-hidden bg-[radial-gradient(circle_at_top_left,_rgba(251,191,188,0.22),_transparent_26%),radial-gradient(circle_at_bottom_right,_rgba(251,207,232,0.2),_transparent_30%),#f8fafc]"
...
@@ -239,19 +259,24 @@ export default function ProblemRectificationLayout() {
...
@@ -239,19 +259,24 @@ export default function ProblemRectificationLayout() {
>
>
<
div
<
div
className=
{
clsx
(
className=
{
clsx
(
'mb-7 flex items-center'
,
'mb-7'
,
sidebarCollapsed
?
'justify-center'
:
'gap-3 px-1'
,
sidebarCollapsed
?
'flex flex-col items-center gap-3'
:
'flex items-start justify-between gap-4 px-1'
,
)
}
)
}
>
>
{
sidebarCollapsed
?
(
{
sidebarCollapsed
?
(
<>
<
img
<
img
src=
{
xyLogoSmall
}
src=
{
xyLogoSmall
}
alt=
"XY Logo"
alt=
"XY Logo"
className=
"h-8 w-auto max-w-8 shrink-0 object-contain"
className=
"h-8 w-auto max-w-8 shrink-0 object-contain"
/>
/>
)
:
null
}
{
applicationSwitcherButton
}
{
!
sidebarCollapsed
?
(
</>
<
div
className=
"min-w-0"
>
)
:
(
<>
<
div
className=
"min-w-0 flex-1"
>
<
h1
className=
"truncate text-sm font-semibold tracking-[0.02em] text-white/95"
>
<
h1
className=
"truncate text-sm font-semibold tracking-[0.02em] text-white/95"
>
{
APP_NAME
}
{
APP_NAME
}
</
h1
>
</
h1
>
...
@@ -259,7 +284,9 @@ export default function ProblemRectificationLayout() {
...
@@ -259,7 +284,9 @@ export default function ProblemRectificationLayout() {
替代旧多维表格版问题整改应用
替代旧多维表格版问题整改应用
</
div
>
</
div
>
</
div
>
</
div
>
)
:
null
}
{
applicationSwitcherButton
}
</>
)
}
</
div
>
</
div
>
<
nav
className=
"flex-1 space-y-5 overflow-y-auto"
>
<
nav
className=
"flex-1 space-y-5 overflow-y-auto"
>
...
@@ -365,12 +392,26 @@ export default function ProblemRectificationLayout() {
...
@@ -365,12 +392,26 @@ export default function ProblemRectificationLayout() {
<
Bars3Icon
className=
"size-5"
/>
<
Bars3Icon
className=
"size-5"
/>
</
NavbarItem
>
</
NavbarItem
>
<
div
className=
"text-sm text-zinc-500"
>
{
currentGroupTitle
}
</
div
>
<
div
className=
"text-sm text-zinc-500"
>
{
currentGroupTitle
}
</
div
>
<
NavbarItem
onClick=
{
()
=>
setIsApplicationDialogOpen
(
true
)
}
aria
-
label=
"Open application switcher"
className=
"border border-rose-200 bg-white text-rose-500 shadow-sm hover:bg-rose-50 hover:text-rose-600"
>
<
Squares2X2Icon
className=
"size-5"
/>
</
NavbarItem
>
</
div
>
</
div
>
<
div
className=
"flex-1 overflow-y-auto rounded-[28px] border border-white/80 bg-white/80 p-4 shadow-sm backdrop-blur sm:p-6"
>
<
div
className=
"flex-1 overflow-y-auto rounded-[28px] border border-white/80 bg-white/80 p-4 shadow-sm backdrop-blur sm:p-6"
>
<
Outlet
/>
<
Outlet
/>
</
div
>
</
div
>
</
main
>
</
main
>
<
ApplicationSwitcherDialog
open=
{
isApplicationDialogOpen
}
onClose=
{
()
=>
setIsApplicationDialogOpen
(
false
)
}
userInfo=
{
userInfo
}
siteInfo=
{
siteInfo
}
/>
</
div
>
</
div
>
);
);
}
}
src/pages/problem_rectification/problem-rectification.business-flow.md
View file @
c536e180
...
@@ -50,6 +50,7 @@
...
@@ -50,6 +50,7 @@
-
问题整改主列表使用普通数据表格,不再依赖多维表格编辑器
-
问题整改主列表使用普通数据表格,不再依赖多维表格编辑器
-
新增、编辑、查看详情优先采用抽屉或弹窗,不走重型嵌套表格交互
-
新增、编辑、查看详情优先采用抽屉或弹窗,不走重型嵌套表格交互
-
批量操作放在顶部工具栏,不再依赖右键菜单作为唯一入口
-
批量操作放在顶部工具栏,不再依赖右键菜单作为唯一入口
-
侧栏左上角提供“应用切换”入口,点击后以弹框展示当前账号可访问的全部应用,支持从问题整改模块快速跳转到其它应用工作区
### 3.3 视图配置驱动约定
### 3.3 视图配置驱动约定
...
@@ -72,6 +73,7 @@
...
@@ -72,6 +73,7 @@
-
页面本身只负责提供 subject 对应的数据加载、统计口径、筛选 schema、行操作与批量操作
-
页面本身只负责提供 subject 对应的数据加载、统计口径、筛选 schema、行操作与批量操作
-
详情侧栏统一复用
`RecordDetailDrawer`
,通过
`recordName`
、
`subjectName`
、
`preferredViewName`
驱动,不再在各页面重复实现抽屉骨架
-
详情侧栏统一复用
`RecordDetailDrawer`
,通过
`recordName`
、
`subjectName`
、
`preferredViewName`
驱动,不再在各页面重复实现抽屉骨架
-
详情侧栏顶部不展示记录标题、状态和摘要卡片,只保留详情视图切换与全屏 / 关闭操作,字段主体作为主要阅读区域
-
详情侧栏顶部不展示记录标题、状态和摘要卡片,只保留详情视图切换与全屏 / 关闭操作,字段主体作为主要阅读区域
-
详情侧栏内
`reference`
类型字段只读态统一展示为关联明细表:按目标 Subject 的
`title`
与全部 metadata 字段展开,字段值按各自类型格式化;编辑态仍使用引用选择器,保存时只写入关联记录
`_id`
或
`_id[]`
-
列表行操作列使用图标按钮横向排列,查看、复制、删除等常用操作通过 tooltip / aria-label 标明含义,避免文字按钮纵向堆叠
-
列表行操作列使用图标按钮横向排列,查看、复制、删除等常用操作通过 tooltip / aria-label 标明含义,避免文字按钮纵向堆叠
-
新增同类列表页时,优先复用工作台与详情抽屉组合,避免再次复制问题整改页 JSX
-
新增同类列表页时,优先复用工作台与详情抽屉组合,避免再次复制问题整改页 JSX
...
...
src/utils/subjectView.js
View file @
c536e180
...
@@ -193,6 +193,10 @@ export function isReferenceFieldType(fieldType) {
...
@@ -193,6 +193,10 @@ export function isReferenceFieldType(fieldType) {
return
REFERENCE_FIELD_TYPES
.
has
(
fieldType
);
return
REFERENCE_FIELD_TYPES
.
has
(
fieldType
);
}
}
export
function
isDateFieldType
(
fieldType
)
{
return
[
'date'
,
'datetime'
,
'date_time'
].
includes
(
normalizeText
(
fieldType
).
toLowerCase
());
}
export
function
normalizeRecordId
(
value
)
{
export
function
normalizeRecordId
(
value
)
{
if
(
!
value
)
{
if
(
!
value
)
{
return
''
;
return
''
;
...
@@ -423,7 +427,7 @@ export function formatFieldValue(value, fieldDef) {
...
@@ -423,7 +427,7 @@ export function formatFieldValue(value, fieldDef) {
return
''
;
return
''
;
}
}
if
(
fieldDef
?.
type
===
'date'
)
{
if
(
isDateFieldType
(
fieldDef
?.
type
)
)
{
return
formatDateText
(
value
);
return
formatDateText
(
value
);
}
}
...
@@ -506,7 +510,7 @@ export function normalizeValueForSubmit(value, fieldDef) {
...
@@ -506,7 +510,7 @@ export function normalizeValueForSubmit(value, fieldDef) {
return
null
;
return
null
;
}
}
if
(
fieldDef
?.
type
===
'date'
)
{
if
(
isDateFieldType
(
fieldDef
?.
type
)
)
{
return
toIsoDateString
(
value
);
return
toIsoDateString
(
value
);
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment