Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
risk-monitor
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
蔡建
risk-monitor
Commits
b8845665
提交
b8845665
authored
3月 19, 2026
作者:
张伊明
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat 完善法案对比页面
上级
02341b24
显示空白字符变更
内嵌
并排
正在显示
4 个修改的文件
包含
446 行增加
和
111 行删除
+446
-111
bill.js
src/api/bill.js
+13
-0
index.vue
src/views/bill/template/index.vue
+2
-2
translate-icons.svg
...iews/bill/versionCompare/assert/icons/translate-icons.svg
+3
-0
index.vue
src/views/bill/versionCompare/index.vue
+428
-109
没有找到文件。
src/api/bill.js
浏览文件 @
b8845665
...
...
@@ -206,3 +206,16 @@ export function getBillTermsCompare(params) {
params
,
});
}
// 版本对比-根据两版版本与筛选条件获取条款列表(分页)
/**
* @param {billId,content,currentPage,currentVersion,isCn,originalVersion,pageSize,status}
* @header token
*/
export
function
getBillVersionCompare
(
params
)
{
return
request
({
method
:
"GET"
,
url
:
"/api/billInfoBean/versionCompare"
,
params
,
});
}
src/views/bill/template/index.vue
浏览文件 @
b8845665
...
...
@@ -404,8 +404,8 @@ const handleGetBillList = async () => {
billList
.
value
=
rawList
.
map
(
item
=>
{
return
{
label
:
item
.
bbmc
,
value
:
item
.
bbmc
label
:
item
.
contentZh
,
value
:
item
.
contentZh
};
})
.
filter
(
item
=>
{
...
...
src/views/bill/versionCompare/assert/icons/translate-icons.svg
0 → 100644
浏览文件 @
b8845665
<svg
viewBox=
"0 0 13.5996 12.666"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
width=
"13.599609"
height=
"12.666016"
fill=
"none"
customFrame=
"#000000"
>
<path
id=
"矢量 455"
d=
"M2 8.66667L2 10C1.99999 10.0416 2.00192 10.0831 2.00578 10.1245C2.00965 10.1659 2.01545 10.207 2.02317 10.2479C2.03089 10.2888 2.0405 10.3292 2.052 10.3691C2.0635 10.4091 2.07684 10.4484 2.09202 10.4872C2.1072 10.5259 2.12416 10.5638 2.14289 10.6009C2.16162 10.6381 2.18204 10.6742 2.20416 10.7095C2.22627 10.7447 2.24998 10.7788 2.27529 10.8118C2.3006 10.8448 2.32739 10.8765 2.35566 10.907C2.38394 10.9375 2.41358 10.9666 2.44458 10.9944C2.47558 11.0221 2.50781 11.0483 2.54126 11.073C2.57472 11.0977 2.60925 11.1208 2.64487 11.1423C2.68048 11.1637 2.71702 11.1835 2.75448 11.2016C2.79195 11.2196 2.83017 11.2359 2.86916 11.2503C2.90814 11.2648 2.94772 11.2775 2.98788 11.2882C3.02805 11.299 3.06863 11.3079 3.10963 11.3149C3.15063 11.3218 3.19186 11.3269 3.23333 11.33L3.33333 11.3333L5.33333 11.3333L5.33333 12.6667L3.33333 12.6667C3.246 12.6667 3.15887 12.6624 3.07195 12.6538C2.98504 12.6453 2.89875 12.6325 2.81309 12.6154C2.72743 12.5984 2.64282 12.5772 2.55924 12.5518C2.47566 12.5265 2.39353 12.4971 2.31284 12.4637C2.23216 12.4303 2.1533 12.393 2.07627 12.3518C1.99925 12.3106 1.92443 12.2658 1.85181 12.2173C1.77919 12.1687 1.70913 12.1168 1.64162 12.0614C1.57411 12.006 1.50947 11.9474 1.44771 11.8856C1.38596 11.8239 1.32738 11.7592 1.27197 11.6917C1.21657 11.6242 1.1646 11.5541 1.11608 11.4815C1.06756 11.4089 1.02271 11.3341 0.981543 11.2571C0.940373 11.18 0.903077 11.1012 0.869654 11.0205C0.836232 10.9398 0.806845 10.8577 0.781492 10.7741C0.75614 10.6905 0.734944 10.6059 0.717906 10.5202C0.700867 10.4346 0.688068 10.3483 0.679507 10.2614C0.670947 10.1745 0.666666 10.0873 0.666666 10L0.666666 8.66667L2 8.66667L2 8.66667ZM10.6667 5.33333L13.6 12.6667L12.1633 12.6667L11.3627 10.6667L8.636 10.6667L7.83667 12.6667L6.40067 12.6667L9.33333 5.33333L10.6667 5.33333L10.6667 5.33333ZM10 7.25667L9.16867 9.33333L10.83 9.33333L10 7.25667ZM4 0L4 1.33333L6.66667 1.33333L6.66667 6L4 6L4 8L2.66667 8L2.66667 6L0 6L0 1.33333L2.66667 1.33333L2.66667 0L4 0ZM10 0.666667C10.0873 0.666667 10.1745 0.670947 10.2614 0.679507C10.3483 0.688068 10.4346 0.700867 10.5202 0.717906C10.6059 0.734944 10.6905 0.75614 10.7741 0.781492C10.8577 0.806845 10.9398 0.836232 11.0205 0.869654C11.1012 0.903077 11.18 0.940373 11.2571 0.981543C11.3341 1.02271 11.4089 1.06756 11.4815 1.11608C11.5541 1.1646 11.6242 1.21657 11.6917 1.27197C11.7592 1.32738 11.8239 1.38596 11.8856 1.44772C11.9474 1.50947 12.006 1.57411 12.0614 1.64162C12.1168 1.70913 12.1687 1.77919 12.2173 1.85181C12.2658 1.92443 12.3106 1.99925 12.3518 2.07628C12.393 2.1533 12.4303 2.23216 12.4637 2.31284C12.4971 2.39353 12.5265 2.47566 12.5518 2.55924C12.5772 2.64282 12.5984 2.72743 12.6154 2.81309C12.6325 2.89875 12.6453 2.98504 12.6538 3.07195C12.6624 3.15887 12.6667 3.246 12.6667 3.33333L12.6667 4.66667L11.3333 4.66667L11.3333 3.33333C11.3333 3.28966 11.3312 3.2461 11.3269 3.20264C11.3226 3.15919 11.3162 3.11604 11.3077 3.07321C11.2992 3.03038 11.2886 2.98807 11.2759 2.94629C11.2632 2.9045 11.2486 2.86343 11.2318 2.82309C11.2151 2.78274 11.1965 2.74332 11.1759 2.7048C11.1553 2.66629 11.1329 2.62888 11.1086 2.59257C11.0844 2.55626 11.0584 2.52123 11.0307 2.48748C11.003 2.45372 10.9737 2.4214 10.9428 2.39052C10.9119 2.35965 10.8796 2.33036 10.8459 2.30265C10.8121 2.27495 10.7771 2.24897 10.7408 2.22471C10.7045 2.20045 10.667 2.17802 10.6285 2.15744C10.59 2.13685 10.5506 2.1182 10.5102 2.10149C10.4699 2.08478 10.4288 2.07009 10.387 2.05741C10.3453 2.04474 10.303 2.03414 10.2601 2.02562C10.2173 2.0171 10.1741 2.0107 10.1307 2.00642C10.0872 2.00214 10.0437 2 10 2L8 2L8 0.666667L10 0.666667L10 0.666667ZM2.66667 2.66667L1.33333 2.66667L1.33333 4.66667L2.66667 4.66667L2.66667 2.66667ZM5.33333 2.66667L4 2.66667L4 4.66667L5.33333 4.66667L5.33333 2.66667Z"
fill=
"rgb(95,101,108)"
fill-rule=
"nonzero"
/>
</svg>
src/views/bill/versionCompare/index.vue
浏览文件 @
b8845665
...
...
@@ -4,37 +4,65 @@
<div
class=
"compare-top-col"
>
<div
class=
"compare-top-label"
>
原版本:
</div>
<el-select
v-model=
"oldVersionId"
placeholder=
"请选择版本"
class=
"compare-top-select"
clearable
>
<el-option
v-for=
"item in versionOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
<el-option
v-for=
"item in versionOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
:disabled=
"item.value === newVersionId"
/>
</el-select>
</div>
<div
class=
"compare-top-col"
>
<div
class=
"compare-top-label"
>
现版本:
</div>
<el-select
v-model=
"newVersionId"
placeholder=
"请选择版本"
class=
"compare-top-select"
clearable
>
<el-option
v-for=
"item in versionOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
<el-option
v-for=
"item in versionOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
:disabled=
"item.value === oldVersionId"
/>
</el-select>
</div>
</div>
<div
class=
"compare-tools"
>
<el-tabs
v-model=
"diffType"
class=
"compare-tools-tabs"
>
<el-tab-pane
label=
"变更"
name=
"CHANGE"
/>
<el-tab-pane
label=
"新增"
name=
"ADD"
/>
<el-tab-pane
label=
"删除"
name=
"DELETE"
/>
</el-tabs>
<div
class=
"compare-tools-tabs"
ref=
"tabsWrapRef"
>
<button
v-for=
"(tab, index) in diffTabs"
:key=
"tab.value"
class=
"compare-tools-tab"
:class=
"
{ 'is-active': diffType === tab.value }"
type="button"
@click="handleDiffTabClick(tab.value)"
:ref="el => setTabRef(el, index)"
>
<span
class=
"label"
>
{{
tab
.
label
}}
</span>
<span
class=
"count"
v-if=
"diffCounts[tab.value] !== null && diffCounts[tab.value] !== undefined"
>
{{
diffCounts
[
tab
.
value
]
}}
</span>
</button>
<span
class=
"compare-tools-tabs-active"
:style=
"activeBarStyle"
></span>
</div>
<div
class=
"compare-tools-actions"
>
<el-checkbox
v-model=
"onlyChinaRelated"
label=
"只看涉华条款"
size=
"large"
/>
<div
class=
"compare-tools-switches"
>
<div
class=
"compare-tools-switch"
>
<el-switch
v-model=
"termsHighlight"
inline-prompt
/>
<span
class=
"label"
>
高亮实体
</span>
<el-switch
v-model=
"termsHighlight"
inline-prompt
active-text=
"开"
inactive-text=
"关"
/>
</div>
<div
class=
"compare-tools-switch"
>
<span
class=
"label"
>
显示原文
</span>
<el-switch
v-model=
"termsShowOriginal"
inline-prompt
active-text=
"开"
inactive-text=
"关"
/>
<el-switch
v-model=
"termsShowOriginal"
inline-prompt
/>
<span
class=
"label"
>
<img
class=
"label-icon"
:src=
"translateIcon"
alt=
""
/>
显示原文
</span>
</div>
</div>
...
...
@@ -56,51 +84,45 @@
</div>
</div>
<el-button
type=
"primary"
plain
class=
"find-word-open-btn"
@
click=
"handleFindWord('open')"
>
查找
</el-button>
@
click=
"handleFindWord('open')"
>
<el-icon
class=
"find-word-open-icon"
><Search
/></el-icon>
查找
</el-button>
</div>
</div>
</div>
<div
class=
"compare-columns"
v-loading=
"isLoading"
>
<div
class=
"compare-col"
>
<div
class=
"compare-col-body"
>
<div
class=
"term-card"
v-for=
"(pair, index) in comparePairs"
:key=
"getPairKey(pair, index)"
>
<div
class=
"compare-columns"
v-loading=
"isLoading"
@
click=
"handleEntityClick"
>
<div
class=
"compare-row"
v-for=
"(pair, index) in comparePairs"
:key=
"getPairKey(pair, index)"
>
<div
class=
"compare-cell"
>
<template
v-if=
"pair?.oldTerm"
>
<div
class=
"term-body"
>
<div
class=
"term-main"
>
<div
class=
"term-row term-row-cn"
>
<div
class=
"term-no-cn"
>
第
{{
pair
.
oldTerm
.
tkxh
}}
条.
</div>
<div
class=
"term-content-cn"
v-html=
"getTermContentHtml(pair.oldTerm, 'cn')"
></div>
<div
class=
"term-content-cn"
v-html=
"getTermContentHtml(pair.oldTerm, 'cn')"
></div>
</div>
<div
class=
"term-row term-row-en"
v-if=
"termsShowOriginal"
>
<div
class=
"term-no-en"
>
Sec.
{{
pair
.
oldTerm
.
tkxh
}}
</div>
<div
class=
"term-content-en"
v-html=
"getTermContentHtml(pair.oldTerm, 'en')"
></div>
<div
class=
"term-content-en"
v-html=
"getTermContentHtml(pair.oldTerm, 'en')"
></div>
</div>
</div>
</div>
</
template
>
<div
v-else
class=
"term-empty"
>
—
</div>
</div>
</div>
</div>
<div
class=
"compare-col"
>
<div
class=
"compare-col-body"
>
<div
class=
"term-card"
v-for=
"(pair, index) in comparePairs"
:key=
"getPairKey(pair, index)"
>
<div
class=
"compare-cell"
>
<
template
v-if=
"pair?.newTerm"
>
<div
class=
"term-body"
>
<div
class=
"term-main"
>
<div
class=
"term-row term-row-cn"
>
<div
class=
"term-no-cn"
>
第
{{
pair
.
newTerm
.
tkxh
}}
条.
</div>
<div
class=
"term-content-cn"
v-html=
"getTermContentHtml(pair.newTerm, 'cn')"
></div>
<div
class=
"term-content-cn"
v-html=
"getTermContentHtml(pair.newTerm, 'cn')"
></div>
</div>
<div
class=
"term-row term-row-en"
v-if=
"termsShowOriginal"
>
<div
class=
"term-no-en"
>
Sec.
{{
pair
.
newTerm
.
tkxh
}}
</div>
<div
class=
"term-content-en"
v-html=
"getTermContentHtml(pair.newTerm, 'en')"
></div>
<div
class=
"term-content-en"
v-html=
"getTermContentHtml(pair.newTerm, 'en')"
></div>
</div>
</div>
</div>
...
...
@@ -109,6 +131,19 @@
</div>
</div>
</div>
<div
class=
"compare-footer"
>
<div
class=
"compare-footer-text"
>
{{ `共 ${total} 项` }}
</div>
<div
class=
"compare-footer-right"
>
<el-pagination
background
layout=
"prev, pager, next"
:total=
"total"
v-model:current-page=
"currentPage"
v-model:page-size=
"pageSize"
@
current-change=
"handleCurrentChange"
/>
</div>
</div>
</div>
</template>
...
...
@@ -116,9 +151,10 @@
<
script
setup
>
import
{
computed
,
nextTick
,
onMounted
,
ref
,
watch
}
from
"vue"
;
import
{
useRoute
}
from
"vue-router"
;
import
{
getBillContentId
,
getBill
Terms
Compare
}
from
"@/api/bill"
;
import
{
getBillContentId
,
getBill
Version
Compare
}
from
"@/api/bill"
;
import
{
extractTextEntity
}
from
"@/api/intelligent/index"
;
import
{
ArrowDown
,
ArrowUp
,
Close
}
from
"@element-plus/icons-vue"
;
import
{
ArrowDown
,
ArrowUp
,
Close
,
Search
}
from
"@element-plus/icons-vue"
;
import
translateIcon
from
"./assert/icons/translate-icons.svg"
;
const
route
=
useRoute
();
...
...
@@ -130,13 +166,26 @@ const newVersionId = ref("");
const
diffType
=
ref
(
"CHANGE"
);
const
onlyChinaRelated
=
ref
(
false
);
const
keywordInput
=
ref
(
""
);
const
keyword
=
ref
(
""
);
const
termsHighlight
=
ref
(
true
);
const
termsShowOriginal
=
ref
(
true
);
const
findWordBox
=
ref
(
true
);
const
currentPage
=
ref
(
1
);
const
pageSize
=
ref
(
10
);
const
total
=
ref
(
0
);
const
diffTabs
=
[
{
label
:
"变更"
,
value
:
"CHANGE"
},
{
label
:
"新增"
,
value
:
"ADD"
},
{
label
:
"删除"
,
value
:
"DELETE"
}
];
const
diffCounts
=
ref
({
CHANGE
:
0
,
ADD
:
0
,
DELETE
:
0
});
const
tabsWrapRef
=
ref
(
null
);
const
tabItemRefs
=
ref
([]);
const
activeBarStyle
=
ref
({});
const
findWordBox
=
ref
(
false
);
const
findWordInputRef
=
ref
();
const
findWordTxt
=
ref
(
""
);
const
findWordKeyword
=
ref
(
""
);
...
...
@@ -152,6 +201,8 @@ const compareRequestToken = ref(0);
const
handleLoadVersionOptions
=
async
()
=>
{
if
(
!
billId
.
value
)
{
versionOptions
.
value
=
[];
oldVersionId
.
value
=
""
;
newVersionId
.
value
=
""
;
return
;
}
...
...
@@ -161,8 +212,8 @@ const handleLoadVersionOptions = async () => {
versionOptions
.
value
=
rawList
.
map
(
item
=>
{
return
{
label
:
item
?.
bbmc
,
value
:
item
?.
bbmc
label
:
item
?.
contentZh
,
value
:
item
?.
contentZh
};
})
.
filter
(
item
=>
item
.
value
)
...
...
@@ -178,16 +229,14 @@ const handleLoadVersionOptions = async () => {
return
;
}
if
(
!
oldVersionId
.
value
)
{
if
(
versionOptions
.
value
.
length
===
1
)
{
oldVersionId
.
value
=
versionOptions
.
value
[
0
].
value
;
newVersionId
.
value
=
""
;
return
;
}
if
(
!
newVersionId
.
value
)
{
newVersionId
.
value
=
versionOptions
.
value
[
versionOptions
.
value
.
length
-
1
].
value
;
}
};
const
handleSearchSubmit
=
()
=>
{
keyword
.
value
=
(
keywordInput
.
value
||
""
).
trim
()
;
oldVersionId
.
value
=
versionOptions
.
value
[
0
].
value
;
newVersionId
.
value
=
versionOptions
.
value
[
versionOptions
.
value
.
length
-
1
].
value
;
};
const
normalizeDiffType
=
value
=>
{
...
...
@@ -196,32 +245,143 @@ const normalizeDiffType = value => {
return
"CHANGE"
;
};
const
setTabRef
=
(
el
,
index
)
=>
{
if
(
el
)
tabItemRefs
.
value
[
index
]
=
el
;
};
const
updateActiveBar
=
()
=>
{
const
index
=
diffTabs
.
findIndex
(
tab
=>
tab
.
value
===
diffType
.
value
);
const
target
=
tabItemRefs
.
value
[
index
];
const
wrap
=
tabsWrapRef
.
value
;
if
(
!
target
||
!
wrap
)
{
activeBarStyle
.
value
=
{};
return
;
}
const
wrapRect
=
wrap
.
getBoundingClientRect
();
const
targetRect
=
target
.
getBoundingClientRect
();
activeBarStyle
.
value
=
{
width
:
`
${
targetRect
.
width
}
px`
,
transform
:
`translateX(
${
targetRect
.
left
-
wrapRect
.
left
}
px)`
};
};
const
handleDiffTabClick
=
value
=>
{
if
(
diffType
.
value
===
value
)
return
;
diffType
.
value
=
value
;
nextTick
(()
=>
{
updateActiveBar
();
});
};
const
updateDiffCounts
=
list
=>
{
const
counts
=
{
CHANGE
:
0
,
ADD
:
0
,
DELETE
:
0
};
for
(
const
pair
of
list
)
{
const
isOld
=
Boolean
(
pair
?.
oldTerm
);
const
isNew
=
Boolean
(
pair
?.
newTerm
);
if
(
isOld
&&
isNew
)
{
counts
.
CHANGE
+=
1
;
}
else
if
(
isNew
&&
!
isOld
)
{
counts
.
ADD
+=
1
;
}
else
if
(
isOld
&&
!
isNew
)
{
counts
.
DELETE
+=
1
;
}
}
diffCounts
.
value
=
counts
;
};
const
mapDiffTypeToStatus
=
value
=>
{
const
diff
=
normalizeDiffType
(
value
);
if
(
diff
===
"ADD"
)
return
"add"
;
if
(
diff
===
"DELETE"
)
return
"del"
;
return
"update"
;
};
const
mapStatusToDiffType
=
value
=>
{
if
(
value
===
"add"
)
return
"ADD"
;
if
(
value
===
"del"
)
return
"DELETE"
;
return
"CHANGE"
;
};
const
mapVersionCompareItemToPair
=
item
=>
{
const
oldTerm
=
item
?.
originalClauseMainId
?
{
id
:
item
.
originalClauseMainId
,
ywid
:
item
.
originalClauseMainId
,
tkxh
:
item
?.
originalClauseNumber
??
""
,
fynr
:
item
?.
originalContentZh
??
""
,
ywnr
:
item
?.
originalContent
??
""
}
:
null
;
const
newTerm
=
item
?.
currentClauseMainId
?
{
id
:
item
.
currentClauseMainId
,
ywid
:
item
.
currentClauseMainId
,
tkxh
:
item
?.
currentClauseNumber
??
""
,
fynr
:
item
?.
currentContentZh
??
""
,
ywnr
:
item
?.
currentContent
??
""
}
:
null
;
return
{
oldTerm
,
newTerm
};
};
const
fetchComparePage
=
async
({
diff
,
page
,
size
})
=>
{
const
params
=
{
billId
:
billId
.
value
,
content
:
keyword
.
value
,
currentPage
:
Math
.
max
(
1
,
Number
(
page
)
||
1
),
currentVersion
:
newVersionId
.
value
,
isCn
:
onlyChinaRelated
.
value
?
"Y"
:
"N"
,
originalVersion
:
oldVersionId
.
value
,
pageSize
:
Math
.
max
(
1
,
Number
(
size
)
||
10
),
status
:
mapDiffTypeToStatus
(
diff
)
};
const
res
=
await
getBillVersionCompare
(
params
);
const
data
=
res
?.
data
?.
data
??
res
?.
data
??
{};
const
raw
=
Array
.
isArray
(
data
?.
content
)
?
data
.
content
:
[];
const
countType
=
Array
.
isArray
(
data
?.
countType
)
?
data
.
countType
:
[];
return
{
list
:
raw
.
map
(
mapVersionCompareItemToPair
),
total
:
Number
(
data
?.
totalElements
??
0
)
||
0
,
countType
};
};
const
getCountByChangeType
=
(
countTypeList
,
changeTypeLabel
)
=>
{
const
list
=
Array
.
isArray
(
countTypeList
)
?
countTypeList
:
[];
const
target
=
list
.
find
(
item
=>
String
(
item
?.
changeType
??
""
)
===
changeTypeLabel
);
return
Number
(
target
?.
count
??
0
)
||
0
;
};
const
loadComparePairs
=
async
()
=>
{
if
(
!
billId
.
value
||
!
oldVersionId
.
value
||
!
newVersionId
.
value
)
{
comparePairs
.
value
=
[];
updateDiffCounts
([]);
total
.
value
=
0
;
return
;
}
const
currentToken
=
++
compareRequestToken
.
value
;
isLoading
.
value
=
true
;
try
{
const
params
=
{
billId
:
billId
.
value
,
oldVersionId
:
oldVersionId
.
value
,
newVersionId
:
newVersionId
.
value
,
diffType
:
normalizeDiffType
(
diffType
.
value
),
cRelated
:
onlyChinaRelated
.
value
?
"Y"
:
"N"
,
keyword
:
keyword
.
value
};
const
res
=
await
getBillTermsCompare
(
params
);
const
currentRes
=
await
fetchComparePage
({
diff
:
diffType
.
value
,
page
:
currentPage
.
value
,
size
:
pageSize
.
value
});
if
(
currentToken
!==
compareRequestToken
.
value
)
return
;
const
list
=
Array
.
isArray
(
res
?.
data
?.
list
)
?
res
.
data
.
list
:
Array
.
isArray
(
res
?.
list
)
?
res
.
list
:
[];
comparePairs
.
value
=
list
;
comparePairs
.
value
=
currentRes
.
list
;
total
.
value
=
currentRes
.
total
;
diffCounts
.
value
=
{
CHANGE
:
getCountByChangeType
(
currentRes
.
countType
,
"更新"
),
ADD
:
getCountByChangeType
(
currentRes
.
countType
,
"新增"
),
DELETE
:
getCountByChangeType
(
currentRes
.
countType
,
"删除"
)
};
await
ensureEntitiesForPairs
(
comparePairs
.
value
);
}
catch
(
error
)
{
if
(
currentToken
!==
compareRequestToken
.
value
)
return
;
comparePairs
.
value
=
[];
updateDiffCounts
([]);
total
.
value
=
0
;
}
finally
{
if
(
currentToken
===
compareRequestToken
.
value
)
{
isLoading
.
value
=
false
;
...
...
@@ -229,6 +389,10 @@ const loadComparePairs = async () => {
}
};
const
handleCurrentChange
=
page
=>
{
currentPage
.
value
=
Number
(
page
)
||
1
;
};
const
getPairKey
=
(
pair
,
index
)
=>
{
const
oldKey
=
pair
?.
oldTerm
?.
ywid
??
pair
?.
oldTerm
?.
id
??
pair
?.
oldTerm
?.
tkxh
??
""
;
const
newKey
=
pair
?.
newTerm
?.
ywid
??
pair
?.
newTerm
?.
id
??
pair
?.
newTerm
?.
tkxh
??
""
;
...
...
@@ -336,7 +500,8 @@ const buildHighlightedHtml = (text, entities, enableHighlight, searchTerm) => {
const
spanText
=
rawText
.
slice
(
r
.
start
,
r
.
end
);
if
(
r
.
type
===
"entity"
)
{
const
type
=
escapeHtml
(
r
.
ent
?.
type
??
""
);
html
+=
`<span class="term-entity" data-entity-type="
${
type
}
">
${
escapeHtml
(
spanText
)}
</span>`
;
const
text
=
escapeHtml
(
spanText
);
html
+=
`<span class="term-entity" data-entity-type="
${
type
}
" data-entity-text="
${
text
}
">
${
text
}
</span>`
;
}
else
{
html
+=
`<span class="term-find-highlight">
${
escapeHtml
(
spanText
)}
</span>`
;
}
...
...
@@ -348,6 +513,17 @@ const buildHighlightedHtml = (text, entities, enableHighlight, searchTerm) => {
return
html
.
replace
(
/
\n
/g
,
"<br />"
);
};
const
handleEntityClick
=
event
=>
{
const
el
=
event
?.
target
?.
closest
?.(
"span.term-entity"
);
if
(
!
el
)
return
;
const
q
=
String
(
el
.
getAttribute
(
"data-entity-text"
)
??
""
).
trim
();
if
(
!
q
)
return
;
const
url
=
`https://www.bing.com/search?q=
${
encodeURIComponent
(
q
)}
`
;
window
.
open
(
url
,
"_blank"
,
"noopener"
);
};
const
termEntityCache
=
ref
(
new
Map
());
const
entityRequestToken
=
ref
(
0
);
...
...
@@ -500,13 +676,24 @@ const handleFindWord = event => {
};
watch
(
[
billId
,
oldVersionId
,
newVersionId
,
diffType
,
onlyChinaRelated
,
keyword
],
[
billId
,
oldVersionId
,
newVersionId
,
diffType
,
onlyChinaRelated
,
keyword
,
currentPage
,
pageSize
],
()
=>
{
loadComparePairs
();
},
{
immediate
:
true
}
);
watch
(
diffType
,
()
=>
{
currentPage
.
value
=
1
;
nextTick
(()
=>
{
updateActiveBar
();
});
});
watch
([
oldVersionId
,
newVersionId
,
onlyChinaRelated
,
keyword
],
()
=>
{
currentPage
.
value
=
1
;
});
watch
(
termsHighlight
,
()
=>
{
ensureEntitiesForPairs
(
comparePairs
.
value
);
});
...
...
@@ -522,6 +709,9 @@ watch(
onMounted
(
async
()
=>
{
await
handleLoadVersionOptions
();
nextTick
(()
=>
{
updateActiveBar
();
});
});
</
script
>
...
...
@@ -555,9 +745,9 @@ onMounted(async () => {
}
.compare-top-label
{
font-size
:
1
8
px
;
font-weight
:
7
00
;
color
:
var
(
--
text-primary-
80
-color
);
font-size
:
1
6
px
;
font-weight
:
4
00
;
color
:
var
(
--
text-primary-
65
-color
);
white-space
:
nowrap
;
}
...
...
@@ -594,26 +784,88 @@ onMounted(async () => {
background
:
transparent
;
display
:
flex
;
align-items
:
center
;
gap
:
16px
;
flex-wrap
:
wrap
;
gap
:
24px
;
flex-wrap
:
nowrap
;
white-space
:
nowrap
;
}
.compare-tools-tabs
{
flex
:
0
0
auto
;
position
:
relative
;
display
:
flex
;
align-items
:
center
;
gap
:
28px
;
height
:
100%
;
}
:deep
(
.el-tabs__item
)
{
font-size
:
18px
;
font-weight
:
700
;
.compare-tools-tab
{
background
:
transparent
;
border
:
none
;
padding
:
0
;
cursor
:
pointer
;
display
:
inline-flex
;
align-items
:
center
;
gap
:
8px
;
font-size
:
16px
;
font-weight
:
400
;
color
:
var
(
--
text-primary-65-color
);
position
:
relative
;
&
.is-active
{
color
:
var
(
--
color-primary-100
);
}
&
:focus-visible
{
outline
:
2px
solid
rgba
(
66
,
133
,
244
,
0
.5
);
outline-offset
:
4px
;
border-radius
:
6px
;
}
.label
{
line-height
:
1
;
}
.count
{
display
:
inline-flex
;
align-items
:
center
;
justify-content
:
center
;
min-width
:
22px
;
height
:
22px
;
padding
:
0
6px
;
border-radius
:
999px
;
background
:
var
(
--
color-primary-100
);
color
:
var
(
--
bg-white-100
);
font-size
:
12px
;
font-weight
:
600
;
}
}
.compare-tools-tabs-active
{
position
:
absolute
;
left
:
0
;
bottom
:
-1px
;
height
:
2px
;
background
:
var
(
--
color-primary-100
);
border-radius
:
2px
;
transition
:
transform
0
.2s
ease
,
width
0
.2s
ease
;
}
.compare-tools-actions
{
display
:
flex
;
align-items
:
center
;
gap
:
16
px
;
gap
:
24
px
;
flex-wrap
:
wrap
;
margin-left
:
auto
;
justify-content
:
flex-end
;
font-size
:
16px
;
font-weight
:
400
;
color
:
var
(
--
text-primary-65-color
);
:deep
(
.el-checkbox__label
)
{
font-size
:
16px
;
font-weight
:
400
;
color
:
var
(
--
text-primary-65-color
);
}
}
.compare-tools-switches
{
...
...
@@ -628,11 +880,20 @@ onMounted(async () => {
gap
:
8px
;
.label
{
font-size
:
14px
;
color
:
rgba
(
95
,
101
,
108
,
1
);
display
:
inline-flex
;
align-items
:
center
;
gap
:
6px
;
font-size
:
16px
;
font-weight
:
400
;
color
:
var
(
--
text-primary-65-color
);
}
}
.label-icon
{
width
:
16px
;
height
:
16px
;
}
.compare-tools-search
{
display
:
inline-flex
;
align-items
:
center
;
...
...
@@ -682,6 +943,25 @@ onMounted(async () => {
.find-word-open-btn
{
height
:
32px
;
background-color
:
var
(
--
bg-white-100
);
border-color
:
var
(
--
bg-black-10
);
color
:
var
(
--
text-primary-80-color
);
&
:hover
{
background-color
:
var
(
--
bg-black-5
);
border-color
:
var
(
--
bg-black-10
);
color
:
var
(
--
text-primary-80-color
);
}
&
:active
{
background-color
:
var
(
--
bg-black-10
);
border-color
:
var
(
--
bg-black-10
);
color
:
var
(
--
text-primary-80-color
);
}
}
.find-word-open-icon
{
margin-right
:
4px
;
}
:deep
(
span
.term-find-highlight
)
{
...
...
@@ -694,51 +974,50 @@ onMounted(async () => {
.compare-columns
{
margin-top
:
16px
;
display
:
grid
;
grid-template-columns
:
1fr
1fr
;
gap
:
16px
;
}
.compare-col
{
background
:
transparent
;
border-radius
:
0
;
box-shadow
:
none
;
display
:
flex
;
flex-direction
:
column
;
min-height
:
520px
;
}
.compare-col-title
{
padding
:
0
0
12px
;
border-bottom
:
1px
solid
rgba
(
240
,
242
,
244
,
1
);
font-size
:
16px
;
font-weight
:
700
;
color
:
rgba
(
59
,
65
,
75
,
1
);
}
.compare-col-body
{
flex
:
1
;
min-height
:
0
;
overflow
:
auto
;
padding
:
16px
0
0
;
display
:
flex
;
flex-direction
:
column
;
gap
:
12px
;
.compare-row
{
width
:
100%
;
display
:
grid
;
grid-template-columns
:
1fr
1fr
;
}
.
term-card
{
.
compare-cell
{
width
:
100%
;
box-sizing
:
border-box
;
border-radius
:
2px
;
background
:
rgb
(
247
,
248
,
249
);
background
:
var
(
--
bg-white-100
);
display
:
flex
;
align-items
:
flex-start
;
position
:
relative
;
padding
:
16px
;
}
.term-card
:nth-child
(
2n-1
)
{
background
:
rgba
(
249
,
250
,
252
,
1
);
.compare-row
:nth-child
(
2n-1
)
.compare-cell
{
background
:
var
(
--
bg-black-2
);
}
.compare-footer
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
gap
:
16px
;
padding
:
12px
0
0
;
}
.compare-footer-text
{
font-size
:
14px
;
color
:
var
(
--
text-primary-65-color
);
white-space
:
nowrap
;
}
.compare-footer-right
{
display
:
flex
;
justify-content
:
flex-end
;
flex
:
1
;
min-width
:
0
;
}
.term-empty
{
...
...
@@ -802,11 +1081,31 @@ onMounted(async () => {
color
:
var
(
--
text-primary-80-color
);
:deep
(
.term-entity
)
{
display
:
inline
;
padding
:
0
2px
;
border-radius
:
4px
;
background
:
rgba
(
255
,
213
,
79
,
0
.35
);
box-shadow
:
inset
0
0
0
1px
rgba
(
255
,
193
,
7
,
0
.25
);
display
:
inline-flex
;
align-items
:
center
;
gap
:
4px
;
padding
:
0
6px
0
4px
;
background
:
var
(
--
color-primary-10
);
cursor
:
pointer
;
transition
:
background-color
0
.15s
ease
,
transform
0
.15s
ease
;
}
:deep
(
.term-entity
:hover
)
{
background
:
var
(
--
color-primary-35
);
transform
:
translateY
(
-1px
);
}
:deep
(
.term-entity
::before
)
{
content
:
""
;
display
:
inline-block
;
width
:
14px
;
height
:
14px
;
flex
:
0
0
14px
;
background-color
:
var
(
--
color-primary-100
);
mask-image
:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M448 768c176.736 0 320-143.264 320-320S624.736 128 448 128 128 271.264 128 448s143.264 320 320 320zm0 64C236.288 832 64 659.712 64 448S236.288 64 448 64s384 172.288 384 384-172.288 384-384 384z'/%3E%3Cpath d='M832 832a32 32 0 0 1-22.624-9.376l-160-160a32 32 0 0 1 45.248-45.248l160 160A32 32 0 0 1 832 832z'/%3E%3C/svg%3E")
;
mask-repeat
:
no-repeat
;
mask-position
:
center
;
mask-size
:
contain
;
}
}
...
...
@@ -818,17 +1117,37 @@ onMounted(async () => {
color
:
var
(
--
text-primary-65-color
);
:deep
(
.term-entity
)
{
display
:
inline
;
padding
:
0
2px
;
border-radius
:
4px
;
background
:
rgba
(
255
,
213
,
79
,
0
.28
);
box-shadow
:
inset
0
0
0
1px
rgba
(
255
,
193
,
7
,
0
.2
);
display
:
inline-flex
;
align-items
:
center
;
gap
:
4px
;
padding
:
0
6px
0
4px
;
background
:
var
(
--
color-primary-10
);
cursor
:
pointer
;
transition
:
background-color
0
.15s
ease
,
transform
0
.15s
ease
;
}
:deep
(
.term-entity
:hover
)
{
background
:
var
(
--
color-primary-35
);
transform
:
translateY
(
-1px
);
}
:deep
(
.term-entity
::before
)
{
content
:
""
;
display
:
inline-block
;
width
:
14px
;
height
:
14px
;
flex
:
0
0
14px
;
background-color
:
var
(
--
color-primary-100
);
mask-image
:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M448 768c176.736 0 320-143.264 320-320S624.736 128 448 128 128 271.264 128 448s143.264 320 320 320zm0 64C236.288 832 64 659.712 64 448S236.288 64 448 64s384 172.288 384 384-172.288 384-384 384z'/%3E%3Cpath d='M832 832a32 32 0 0 1-22.624-9.376l-160-160a32 32 0 0 1 45.248-45.248l160 160A32 32 0 0 1 832 832z'/%3E%3C/svg%3E")
;
mask-repeat
:
no-repeat
;
mask-position
:
center
;
mask-size
:
contain
;
}
}
@media
(
max-width
:
1680px
)
{
.compare-
columns
,
.compare-
row
,
.compare-top
{
grid-template-columns
:
1fr
;
}
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论