Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
GEO-project-kb
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
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
bos-project-kb
GEO-project-kb
Commits
ee0a68c4
Commit
ee0a68c4
authored
Jul 22, 2026
by
单鹏辉
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 更新 GEO-ops-26 list-config,设置 repo 地址
parent
09412d7b
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
257 additions
and
1 deletion
+257
-1
GEO-ops-26_看板.html
GEO-ops-26/GEO-ops-26_看板.html
+256
-0
list-config.md
GEO-ops-26/list-config.md
+1
-1
No files found.
GEO-ops-26/GEO-ops-26_看板.html
0 → 100644
View file @
ee0a68c4
<!DOCTYPE html>
<html
lang=
"zh"
><head><meta
charset=
"utf-8"
><meta
name=
"viewport"
content=
"width=device-width,initial-scale=1"
>
<title>
BOS标准问题清单 · Ledger 看板
</title>
<style>
*
{
box-sizing
:
border-box
}
body
{
margin
:
0
;
font-family
:
-apple-system
,
"Segoe UI"
,
"Microsoft YaHei"
,
sans-serif
;
background
:
#0f1115
;
color
:
#e7e9ee
;
font-size
:
14px
}
.wrap
{
max-width
:
1280px
;
margin
:
0
auto
;
padding
:
20px
}
h1
{
font-size
:
22px
;
margin
:
0
0
2px
}
.sub
{
color
:
#8b93a7
;
font-size
:
13px
;
margin-bottom
:
18px
}
.sec
{
font-size
:
13px
;
letter-spacing
:
2px
;
color
:
#8b93a7
;
margin
:
22px
0
10px
;
text-transform
:
uppercase
}
.cards
{
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
12px
}
.card
{
background
:
#171a21
;
border
:
1px
solid
#232734
;
border-radius
:
10px
;
padding
:
14px
16px
;
min-width
:
150px
;
flex
:
1
}
.card
.n
{
font-size
:
26px
;
font-weight
:
700
}
.card
.l
{
color
:
#8b93a7
;
font-size
:
12px
;
margin-top
:
3px
}
.card.bad
{
border-color
:
#5a2330
;
background
:
#1d1417
}
.card.bad
.n
{
color
:
#ff6b81
}
.card.good
{
border-color
:
#1f4a36
}
.card.good
.n
{
color
:
#48d597
}
.card
.why
{
color
:
#6f7689
;
font-size
:
11px
;
margin-top
:
6px
;
line-height
:
1.4
}
.bar
{
height
:
8px
;
background
:
#232734
;
border-radius
:
4px
;
overflow
:
hidden
;
margin-top
:
6px
}
.bar
>
i
{
display
:
block
;
height
:
100%
}
.row2
{
display
:
grid
;
grid-template-columns
:
1
fr
1
fr
;
gap
:
16px
}
@media
(
max-width
:
820px
){
.row2
{
grid-template-columns
:
1
fr
}}
.panel
{
background
:
#171a21
;
border
:
1px
solid
#232734
;
border-radius
:
10px
;
padding
:
14px
16px
}
.dist
.item
{
display
:
flex
;
align-items
:
center
;
gap
:
10px
;
margin
:
7px
0
;
font-size
:
13px
}
.dist
.item
span
:first-child
{
width
:
96px
;
color
:
#b9c0d0
}
.dist
.item
span
:last-child
{
width
:
34px
;
text-align
:
right
;
color
:
#8b93a7
}
.dist
.item
.bar
{
flex
:
1
}
.controls
{
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
8px
;
margin
:
14px
0
}
select
,
input
{
background
:
#171a21
;
color
:
#e7e9ee
;
border
:
1px
solid
#2a2f3d
;
border-radius
:
7px
;
padding
:
7px
10px
;
font-size
:
13px
}
input
{
flex
:
1
;
min-width
:
180px
}
.chk
{
display
:
flex
;
align-items
:
center
;
gap
:
5px
;
color
:
#b9c0d0
;
font-size
:
13px
;
padding
:
6px
8px
}
table
{
width
:
100%
;
border-collapse
:
collapse
;
margin-top
:
6px
}
th
,
td
{
text-align
:
left
;
padding
:
8px
9px
;
border-bottom
:
1px
solid
#20242e
;
font-size
:
13px
;
vertical-align
:
top
}
th
{
color
:
#8b93a7
;
font-weight
:
600
;
cursor
:
pointer
;
user-select
:
none
;
position
:
sticky
;
top
:
0
;
background
:
#0f1115
}
tr
.issue
{
cursor
:
pointer
}
tr
.issue
:hover
{
background
:
#171a21
}
.pill
{
display
:
inline-block
;
padding
:
2px
8px
;
border-radius
:
11px
;
font-size
:
11px
;
font-weight
:
600
;
white-space
:
nowrap
}
.f-
已完成
{
background
:
#103a28
;
color
:
#48d597
}
.f-
待用户验证
{
background
:
#3d3414
;
color
:
#e8c24b
}
.f-
进行中
{
background
:
#16304d
;
color
:
#5fa8ff
}
.f-
进行中\
(
返工\
)
{
background
:
#3d2414
;
color
:
#ff9b54
}
.f-
未开始
{
background
:
#262a36
;
color
:
#9aa3b5
}
.f-
已关闭
{
background
:
#22262f
;
color
:
#6f7689
}
.pr-
高
{
color
:
#ff6b81
;
font-weight
:
700
}
.pr-
中
{
color
:
#e8c24b
}
.pr-
低
{
color
:
#8b93a7
}
.od
{
color
:
#ff6b81
;
font-weight
:
700
}
.detail
{
background
:
#12151b
}
.detail
td
{
padding
:
0
}
.dbox
{
padding
:
14px
18px
}
.log
{
border-left
:
2px
solid
#2a2f3d
;
margin-left
:
6px
;
padding-left
:
14px
}
.log
.e
{
margin
:
9px
0
;
font-size
:
13px
;
line-height
:
1.45
}
.log
.meta
{
color
:
#6f7689
;
font-size
:
11px
}
.log
.meta
b
{
color
:
#9aa3b5
;
font-weight
:
600
}
.kv
{
color
:
#8b93a7
;
font-size
:
12px
;
margin
:
4px
0
}
.tag
{
display
:
inline-block
;
background
:
#202637
;
color
:
#8fb0ff
;
border-radius
:
5px
;
padding
:
1px
7px
;
font-size
:
11px
;
margin-right
:
5px
}
.note
{
color
:
#6f7689
;
font-size
:
12px
;
margin-top
:
4px
}
.foot
{
color
:
#5b6172
;
font-size
:
11px
;
margin
:
24px
0
8px
;
text-align
:
center
}
.lang-btn
{
background
:
#232734
;
color
:
#b9c0d0
;
border
:
1px
solid
#2a2f3d
;
border-radius
:
7px
;
padding
:
6px
14px
;
font-size
:
13px
;
cursor
:
pointer
;
font-weight
:
600
}
.lang-btn.active
{
background
:
#5fa8ff
;
color
:
#fff
;
border-color
:
#5fa8ff
}
</style></head><body><div
class=
"wrap"
>
<div
style=
"display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap"
>
<h1
id=
"h1"
>
BOS标准问题清单 · Ledger 看板
</h1>
<div
style=
"display:flex;gap:6px"
>
<button
class=
"lang-btn active"
onclick=
"setLang('zh')"
id=
"btn-zh"
>
中文
</button>
<button
class=
"lang-btn"
onclick=
"setLang('en')"
id=
"btn-en"
>
EN
</button>
</div></div>
<div
class=
"sub"
id=
"sub"
></div>
<div
class=
"sec"
>
① Excel 体检 — 这张表把什么藏起来了
</div>
<div
class=
"cards"
id=
"audit"
></div>
<div
class=
"sec"
>
② 可信汇总(由 206 个对象派生,不是手填)
</div>
<div
class=
"row2"
>
<div
class=
"panel"
><div
class=
"cards"
id=
"topnums"
></div></div>
<div
class=
"panel dist"
id=
"fsdist"
></div>
</div>
<div
class=
"row2"
style=
"margin-top:16px"
>
<div
class=
"panel dist"
id=
"ownerdist"
><div
class=
"sec"
style=
"margin:0 0 8px"
>
各责任顾问 · 未关闭(调度)
</div></div>
<div
class=
"panel dist"
id=
"pridist"
><div
class=
"sec"
style=
"margin:0 0 8px"
>
按优先级
</div></div>
</div>
<div
class=
"sec"
>
③ 工作台 — 点任意一行看 append-only 进展日志
</div>
<div
class=
"controls"
>
<select
id=
"f_owner"
></select><select
id=
"f_final"
></select><select
id=
"f_pri"
></select>
<input
id=
"f_q"
placeholder=
"搜索描述/标题/编号…"
>
<label
class=
"chk"
><input
type=
"checkbox"
id=
"f_od"
>
只看超期
</label>
<label
class=
"chk"
><input
type=
"checkbox"
id=
"f_open"
checked
>
只看未关闭
</label>
</div>
<table><thead><tr>
<th
data-k=
"id"
>
编号
</th><th
data-k=
"owner"
>
责任顾问
</th><th
data-k=
"priority"
>
优先级
</th>
<th
data-k=
"final"
>
最终状态
</th><th
data-k=
"plan"
>
计划完成
</th><th
data-k=
"nlog"
>
进展
</th><th
data-k=
"title"
>
标题
</th>
</tr></thead><tbody
id=
"tb"
></tbody></table>
<div
class=
"foot"
id=
"foot"
></div>
</div>
<script>
const
DATA
=
[],
S
=
{
"total"
:
0
,
"total_logs"
:
0
,
"fs"
:
{},
"pri"
:
{},
"owner_open"
:
{},
"overdue"
:
0
,
"sap_done"
:
0
,
"final_done"
:
0
,
"today"
:
"2026-07-17"
,
"audit"
:
null
};
let
lang
=
'zh'
;
const
h1El
=
document
.
getElementById
(
'h1'
);
const
origH1
=
h1El
.
textContent
;
const
listName
=
origH1
.
replace
(
/
\s
*·
\s
*Ledger
\s
*看板$/i
,
''
).
replace
(
/
\s
*·
\s
*Issue
\/
Task
\s
*Ledger$/i
,
''
);
const
fsOrder_zh
=
[
"进行中(返工)"
,
"进行中"
,
"待用户验证"
,
"未开始"
,
"已完成"
,
"已关闭"
];
const
fsOrder_en
=
[
"Rework"
,
"In Progress"
,
"Pending Verification"
,
"Not Started"
,
"Completed"
,
"Closed"
];
const
fsOrder
=
fsOrder_zh
;
const
colMap
=
{
'已完成'
:
'#48d597'
,
'Completed'
:
'#48d597'
,
'待用户验证'
:
'#e8c24b'
,
'Pending Verification'
:
'#e8c24b'
,
'进行中'
:
'#5fa8ff'
,
'In Progress'
:
'#5fa8ff'
,
'进行中(返工)'
:
'#ff9b54'
,
'Rework'
:
'#ff9b54'
,
'未开始'
:
'#9aa3b5'
,
'Not Started'
:
'#9aa3b5'
,
'已关闭'
:
'#6f7689'
,
'Closed'
:
'#6f7689'
,
'高'
:
'#ff6b81'
,
'High'
:
'#ff6b81'
,
'中'
:
'#e8c24b'
,
'Medium'
:
'#e8c24b'
,
'低'
:
'#7f8aa0'
,
'Low'
:
'#7f8aa0'
,
'未定'
:
'#7f8aa0'
,
'Unset'
:
'#7f8aa0'
};
const
L
=
{
zh
:{
title
:
listName
+
' · 问题清单 Ledger 看板'
,
sub
:
`数据源 =
${
S
.
total
}
个结构化对象 · 实时重算 ·
${
S
.
today
}
`
,
audit
:
`① Excel 体检`
,
summary
:
`② 可信汇总`
,
ownerSec
:
`各责任顾问 · 未关闭`
,
priSec
:
`按优先级`
,
workSec
:
`③ 工作台 — 点行展开日志`
,
fOwner
:
'全部责任顾问'
,
fFinal
:
'全部状态'
,
fPri
:
'全部优先级'
,
fQ
:
'搜索编号/标题/描述…'
,
fOd
:
'只看超期'
,
fOpen
:
'只看未关闭'
,
thId
:
'编号'
,
thOwner
:
'责任顾问'
,
thPri
:
'优先级'
,
thFinal
:
'最终状态'
,
thPlan
:
'计划完成'
,
thNlog
:
'进展'
,
thTitle
:
'标题'
,
total
:
'问题总数'
,
done
:
'最终已完成'
,
sapDone
:
'SAP 已完成'
,
overdueN
:
'超期未关闭'
,
fsDist
:
'按最终状态(派生)'
,
ownerDist
:
'各责任顾问 · 未关闭(调度)'
,
priDist
:
'按优先级'
,
od
:
'⚠超期'
,
descLabel
:
'问题描述'
,
planLabel
:
'当前方案'
,
histLabel
:
'责任人变更历史'
,
logLabel
:
'进展日志(append-only)'
,
footer
:
`显示 {n} / {t} 条 · 点行展开进展日志`
,
auditC
:
'处状态自相矛盾'
,
auditLog
:
'个单元格塞手写流水账'
,
auditPri
:
'缺优先级/缺分类'
,
auditAcc
:
'种"验收反馈"自由写法'
,
auditWhy1
:
'原表"SAP已完成"却"最终状态≠已完成"。Ledger 里最终态是派生函数,结构上不可能矛盾。'
,
auditWhy2
:(
n
)
=>
`已拆成
${
n
}
条带 时间·作者·来源 的 append-only 日志。`
,
auditWhy3
:
'没人强制填。Ledger 受控字段缺失即标红不过门。'
,
auditWhy4
:
'同一个"已解决"68 种写法,机器没法聚合。Ledger 用受控状态+证据指针。'
},
en
:{
title
:
listName
+
' · Issue/Task Ledger'
,
sub
:
`
${
S
.
total
}
structured objects · Real-time ·
${
S
.
today
}
`
,
audit
:
`① Excel Audit`
,
summary
:
`② Summary`
,
ownerSec
:
`Owner · Open Issues`
,
priSec
:
`By Priority`
,
workSec
:
`③ Workbench — Click row to expand`
,
fOwner
:
'All Owners'
,
fFinal
:
'All Status'
,
fPri
:
'All Priority'
,
fQ
:
'Search ID/title/desc…'
,
fOd
:
'Overdue only'
,
fOpen
:
'Open only'
,
thId
:
'ID'
,
thOwner
:
'Owner'
,
thPri
:
'Priority'
,
thFinal
:
'Final Status'
,
thPlan
:
'Plan Date'
,
thNlog
:
'Logs'
,
thTitle
:
'Title'
,
total
:
'Total'
,
done
:
'Completed'
,
sapDone
:
'SAP Done'
,
overdueN
:
'Overdue'
,
fsDist
:
'By Final Status'
,
ownerDist
:
'Owner · Open (Dispatch)'
,
priDist
:
'By Priority'
,
od
:
'⚠Overdue'
,
descLabel
:
'Description'
,
planLabel
:
'Current Plan'
,
histLabel
:
'Owner Change History'
,
logLabel
:
'Progress Log (append-only)'
,
footer
:
`Showing {n} / {t} · Click row to expand`
,
auditC
:
'Status contradictions'
,
auditLog
:
'Cells with handwritten logs'
,
auditPri
:
'Missing priority/category'
,
auditAcc
:
'Different acceptance formats'
,
auditWhy1
:
'SAP=Done but Final≠Done. Ledger derives final status — structurally impossible to contradict.'
,
auditWhy2
:(
n
)
=>
`Split into
${
n
}
append-only logs with time·author·source.`
,
auditWhy3
:
'No enforcement. Ledger marks missing fields red.'
,
auditWhy4
:
'68 ways to write "resolved". Ledger uses controlled states + evidence pointers.'
}
};
function
setLang
(
l
){
lang
=
l
;
document
.
getElementById
(
'btn-zh'
).
className
=
'lang-btn'
+
(
l
===
'zh'
?
' active'
:
''
);
document
.
getElementById
(
'btn-en'
).
className
=
'lang-btn'
+
(
l
===
'en'
?
' active'
:
''
);
const
t
=
L
[
l
];
document
.
getElementById
(
'h1'
).
textContent
=
t
.
title
;
document
.
getElementById
(
'sub'
).
textContent
=
t
.
sub
;
document
.
getElementById
(
'sec-audit'
).
textContent
=
t
.
audit
;
document
.
getElementById
(
'sec-summary'
).
textContent
=
t
.
summary
;
document
.
getElementById
(
'sec-owner'
).
textContent
=
t
.
ownerSec
;
document
.
getElementById
(
'sec-pri'
).
textContent
=
t
.
priSec
;
document
.
getElementById
(
'sec-work'
).
textContent
=
t
.
workSec
;
document
.
getElementById
(
'f_q'
).
placeholder
=
t
.
fQ
;
document
.
getElementById
(
'lbl-od'
).
textContent
=
t
.
fOd
;
document
.
getElementById
(
'lbl-open'
).
textContent
=
t
.
fOpen
;
document
.
querySelectorAll
(
'th[data-k]'
).
forEach
(
th
=>
{
const
k
=
th
.
dataset
.
k
;
th
.
textContent
=
t
[
'th'
+
k
.
charAt
(
0
).
toUpperCase
()
+
k
.
slice
(
1
)]
||
k
;
});
// Update selects
const
fO
=
document
.
getElementById
(
'f_owner'
);
const
fF
=
document
.
getElementById
(
'f_final'
);
const
fP
=
document
.
getElementById
(
'f_pri'
);
fO
.
options
[
0
].
text
=
t
.
fOwner
;
fF
.
options
[
0
].
text
=
t
.
fFinal
;
fP
.
options
[
0
].
text
=
t
.
fPri
;
render
();
}
// ① audit
if
(
S
.
audit
){
const
a
=
S
.
audit
;
const
t
=
L
[
lang
];
const
items
=
[
[
a
.
contradictions
,
t
.
auditC
,
t
.
auditWhy1
],
[
a
.
changelog_cells
,
t
.
auditLog
,
t
.
auditWhy2
(
S
.
total_logs
)],
[
a
.
missing_priority
+
'/'
+
a
.
missing_category
,
t
.
auditPri
,
t
.
auditWhy3
],
[
a
.
acc_distinct
,
t
.
auditAcc
,
t
.
auditWhy4
]];
document
.
getElementById
(
'audit'
).
innerHTML
=
items
.
map
(
x
=>
`<div class="card bad"><div class="n">
${
x
[
0
]}
</div><div class="l">
${
x
[
1
]}
</div><div class="why">
${
x
[
2
]}
</div></div>`
).
join
(
''
);
}
else
{
document
.
getElementById
(
'audit'
).
innerHTML
=
'<div class="card">No source Excel</div>'
;}
// ② top nums
const
pct
=
(
n
)
=>
S
.
total
?
Math
.
round
(
n
/
S
.
total
*
100
):
0
;
function
renderStats
(){
const
t
=
L
[
lang
];
document
.
getElementById
(
'topnums'
).
innerHTML
=
[
[
'good'
,
S
.
total
,
t
.
total
,
''
],
[
'good'
,
S
.
final_done
,
t
.
done
,
`SAP done + verified ·
${
pct
(
S
.
final_done
)}
%`
],
[
''
,
S
.
sap_done
,
t
.
sapDone
,
`
${
pct
(
S
.
sap_done
)}
%`
],
[
'bad'
,
S
.
overdue
,
t
.
overdueN
,
'Plan date passed'
]
].
map
(
x
=>
`<div class="card
${
x
[
0
]}
"><div class="n">
${
x
[
1
]}
</div><div class="l">
${
x
[
2
]}
</div><div class="why">
${
x
[
3
]}
</div></div>`
).
join
(
''
);
}
function
dist
(
el
,
obj
,
order
,
total
,
title
){
const
keys
=
order
?
order
.
filter
(
k
=>
obj
[
k
]):
Object
.
keys
(
obj
).
sort
((
a
,
b
)
=>
obj
[
b
]
-
obj
[
a
]);
const
mx
=
Math
.
max
(
1
,...
Object
.
values
(
obj
));
let
h
=
title
?
`<div class="sec" style="margin:0 0 8px">
${
title
}
</div>`
:
''
;
h
+=
keys
.
map
(
k
=>
`<div class="item"><span>
${
k
}
</span><div class="bar"><i style="width:
${
obj
[
k
]
/
mx
*
100
}
%;background:
${
colMap
[
k
]
||
'#5fa8ff'
}
"></i></div><span>
${
obj
[
k
]}
</span></div>`
).
join
(
''
);
el
.
innerHTML
=
h
;}
function
renderDist
(){
const
t
=
L
[
lang
];
const
fo
=
fsOrder
;
dist
(
document
.
getElementById
(
'fsdist'
),
S
.
fs
,
fo
,
S
.
total
,
t
.
fsDist
);
dist
(
document
.
getElementById
(
'ownerdist'
),
S
.
owner_open
,
null
,
S
.
total
,
t
.
ownerDist
);
dist
(
document
.
getElementById
(
'pridist'
),
S
.
pri
,[
'高'
,
'High'
,
'中'
,
'Medium'
,
'低'
,
'Low'
,
'未定'
,
'Unset'
],
S
.
total
,
t
.
priDist
);
}
// ③ table
function
opts
(
el
,
arr
,
all
){
el
.
innerHTML
=
`<option value="">
${
all
}
</option>`
+
arr
.
map
(
v
=>
`<option>
${
v
}
</option>`
).
join
(
''
);}
opts
(
document
.
getElementById
(
'f_owner'
),[...
new
Set
(
DATA
.
map
(
d
=>
d
.
owner
))].
sort
(),
L
.
zh
.
fOwner
);
opts
(
document
.
getElementById
(
'f_final'
),
fsOrder_zh
,
L
.
zh
.
fFinal
);
opts
(
document
.
getElementById
(
'f_pri'
),[
'高'
,
'中'
,
'低'
,
'未定'
],
L
.
zh
.
fPri
);
let
sortK
=
'id'
,
sortAsc
=
true
;
const
esc
=
s
=>
(
s
||
''
).
replace
(
/
[
&<>
]
/g
,
c
=>
({
'&'
:
'&'
,
'<'
:
'<'
,
'>'
:
'>'
}[
c
]));
function
render
(){
const
t
=
L
[
lang
];
const
isEn
=
lang
===
'en'
;
const
fo
=
f_owner
.
value
,
ff
=
f_final
.
value
,
fp
=
f_pri
.
value
,
q
=
f_q
.
value
.
trim
(),
od
=
f_od
.
checked
,
op
=
f_open
.
checked
;
const
fsNow
=
isEn
?
fsOrder_en
:
fsOrder_zh
;
let
rows
=
DATA
.
filter
(
d
=>
{
const
dPri
=
isEn
?
d
.
priority_en
:
d
.
priority
;
const
dFinal
=
isEn
?
mapFinalEn
(
d
.
final
):
d
.
final
;
return
(
!
fo
||
d
.
owner
===
fo
)
&&
(
!
ff
||
dFinal
===
ff
)
&&
(
!
fp
||
dPri
===
fp
)
&&
(
!
od
||
d
.
overdue
)
&&
(
!
op
||
(
dFinal
!==
'Completed'
&&
dFinal
!==
'Closed'
&&
d
.
final
!==
'已完成'
&&
d
.
final
!==
'已关闭'
))
&&
(
!
q
||
(
d
.
id
+
(
isEn
?
d
.
title_en
:
d
.
title
)
+
(
isEn
?
d
.
desc_en
:
d
.
desc
)).
toLowerCase
().
includes
(
q
.
toLowerCase
()));
});
rows
.
sort
((
a
,
b
)
=>
{
let
x
=
a
[
sortK
],
y
=
b
[
sortK
];
if
(
sortK
===
'nlog'
){
x
=+
x
;
y
=+
y
}
return
(
x
>
y
?
1
:
x
<
y
?
-
1
:
0
)
*
(
sortAsc
?
1
:
-
1
)});
tb
.
innerHTML
=
rows
.
map
(
d
=>
{
const
od
=
d
.
overdue
?
`<span class="od">
${
t
.
od
}
</span>`
:
''
;
const
logs
=
d
.
logs
.
map
(
l
=>
`<div class="e"><div class="meta"><b>
${
l
.
date
}
</b> ·
${
esc
(
l
.
author
)}
· <span class="tag">
${
esc
(
l
.
source
)}
</span></div>
${
esc
(
l
.
text
)}
</div>`
).
join
(
''
);
const
lk
=
(
d
.
linked
||
[]).
map
(
x
=>
`<span class="tag">
${
x
.
rel
}
→
${
x
.
id
}
</span>`
).
join
(
''
);
const
attChips
=
(
d
.
attachments
||
[]).
map
(
a
=>
`<span class="tag">
${
esc
(
a
.
filename
)}
</span>`
).
join
(
''
);
const
ownerHist
=
(
d
.
owner_history
||
[]).
map
(
h
=>
`<div class="e"><div class="meta"><b>
${
esc
(
h
.
date
)}
</b> ·
${
esc
(
h
.
by
)}
· <span class="tag">
${
isEn
?
'Reassigned'
:
'转派'
}
</span></div>
${
esc
(
h
.
from
)}
→
${
esc
(
h
.
to
)}
:
${
esc
(
h
.
reason
||
''
)}
</div>`
).
join
(
''
);
const
title
=
isEn
?
d
.
title_en
:
d
.
title
;
const
cat
=
isEn
?
d
.
category_en
:
d
.
category
;
const
pri
=
isEn
?
d
.
priority_en
:
d
.
priority
;
const
sap
=
isEn
?
d
.
sap_en
:
d
.
sap
;
const
acc
=
isEn
?
d
.
acc_en
:
d
.
acc
;
const
final
=
isEn
?
mapFinalEn
(
d
.
final
):
d
.
final
;
const
descH
=
isEn
?
esc
(
d
.
desc_en
):
d
.
desc_html
;
return
`<tr class="issue" onclick="this.nextElementSibling.style.display=this.nextElementSibling.style.display==='table-row'?'none':'table-row'">
<td>
${
d
.
id
}
</td><td>
${
esc
(
d
.
owner
)}
</td><td class="pr-
${
pri
}
">
${
pri
}
</td>
<td><span class="pill f-
${
final
}
">
${
final
}
</span></td><td>
${
d
.
plan
||
'—'
}${
od
}
</td><td>
${
d
.
nlog
}
</td><td>
${
esc
(
title
)}
</td></tr>
<tr class="detail" style="display:none"><td colspan="7"><div class="dbox">
<div class="kv"><b>
${
d
.
id
}
</b>
${
isEn
?
'Raised'
:
'提出'
}
:
${
esc
(
d
.
raised_dept
)}
/
${
esc
(
d
.
raised_by
)}
SAP:
${
sap
}
${
isEn
?
'Acceptance'
:
'验收'
}
:
${
acc
}
${
isEn
?
'Category'
:
'分类'
}
:
${
esc
(
cat
)}
${
lk
}
</div>
<div class="kv">
${
isEn
?
'Owner'
:
'责任顾问'
}
:
${
esc
(
d
.
owner
)}
${
attChips
}
</div>
<div class="kv" style="white-space:pre-wrap">
${
t
.
descLabel
}
:
${
descH
}
</div>
<div class="kv">
${
t
.
planLabel
}
:
${
esc
(
d
.
plan_txt
)
||
'—'
}
</div>
<div class="sec" style="margin:12px 0 6px">
${
t
.
histLabel
}
</div>
<div class="log">
${
ownerHist
||
'<div class="note">'
+
(
isEn
?
'None'
:
'无'
)
+
'</div>'
}
</div>
<div class="sec" style="margin:12px 0 6px">
${
t
.
logLabel
}
</div>
<div class="log">
${
logs
||
'<div class="note">'
+
(
isEn
?
'None'
:
'无'
)
+
'</div>'
}
</div>
</div></td></tr>`
;}).
join
(
''
);
foot
.
textContent
=
t
.
footer
.
replace
(
'{n}'
,
rows
.
length
).
replace
(
'{t}'
,
DATA
.
length
);
renderStats
();
renderDist
();
}
function
mapFinalEn
(
f
){
const
m
=
{
'已完成'
:
'Completed'
,
'已关闭'
:
'Closed'
,
'进行中'
:
'In Progress'
,
'进行中(返工)'
:
'Rework'
,
'待用户验证'
:
'Pending Verification'
,
'未开始'
:
'Not Started'
};
return
m
[
f
]
||
f
;
}
document
.
querySelectorAll
(
'th[data-k]'
).
forEach
(
th
=>
th
.
onclick
=
()
=>
{
const
k
=
th
.
dataset
.
k
;
if
(
sortK
===
k
)
sortAsc
=!
sortAsc
;
else
{
sortK
=
k
;
sortAsc
=
true
}
render
();});
[
'f_owner'
,
'f_final'
,
'f_pri'
,
'f_q'
,
'f_od'
,
'f_open'
].
forEach
(
id
=>
document
.
getElementById
(
id
).
addEventListener
(
'input'
,
render
));
setLang
(
'zh'
);
</script></body></html>
\ No newline at end of file
GEO-ops-26/list-config.md
View file @
ee0a68c4
...
...
@@ -7,7 +7,7 @@ profile: 问题型
customer
:
GEO
proj_code
:
GEO
id_prefix
:
GEO
repo
:
repo
:
http://118.31.18.126:9001/bos-project-kb/GEO-project-kb.git
branch
:
master
protected_path
:
list-config.md
version
:
2
...
...
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