@jx3box/jx3box-ui 2.3.21 → 2.3.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/css/wiki/wiki-comments.less +244 -0
- package/assets/css/wiki/wiki-panel.less +291 -0
- package/assets/css/wiki/wiki-revisions.less +78 -0
- package/i18n/messages/en-US.js +3 -1
- package/i18n/messages/vi.js +8 -1
- package/i18n/messages/zh-CN.js +3 -1
- package/i18n/messages/zh-TW.js +3 -1
- package/package.json +2 -1
- package/src/App.vue +53 -10
- package/src/interact/QRcode.vue +21 -3
- package/src/wiki/GamePrice.vue +68 -11
- package/src/wiki/WikiComment.vue +46 -15
- package/src/wiki/WikiComments.vue +19 -4
- package/src/wiki/WikiPanel.vue +22 -4
- package/src/wiki/WikiRevisions.vue +6 -1
- package/tests/wiki-i18n-contract.test.js +113 -0
package/i18n/messages/zh-TW.js
CHANGED
|
@@ -486,7 +486,9 @@ export default {
|
|
|
486
486
|
migrateCommunity: { title: "遷回原版塊", postTitle: "標題", author: "作者", originalSection: "原版塊", originalLink: "原連結", notAvailable: "此貼文不可遷移,不存在原貼文", confirm: "確認將此貼文遷回原版塊?", success: "遷移成功" },
|
|
487
487
|
moveCommunity: { title: "遷移至論壇", category: "分類", categoryPlaceholder: "請選擇貼文分類", introduction: "簡介", contentPlaceholder: "請輸入內容", images: "附圖", irreversibleTip: "請確認此操作不可逆", irreversible: "我已確認此操作不可逆", missingPost: "文章ID不存在!", selectCategory: "請選擇分類!" },
|
|
488
488
|
wiki: { loading: "🎉 資料載入中...", loadFailed: "⚠️ 資料載入異常", empty: "💧 暫無資料", reply: "回覆", nickname: "暱稱:", mysterious: "神祕俠士" },
|
|
489
|
-
wikiPanel: { contributors: "參與貢獻", difficulty: "綜合難度", popularity: "熱度", updatedAt: "更新時間" },
|
|
489
|
+
wikiPanel: { contributors: "參與貢獻", difficulty: "綜合難度", popularity: "熱度", updatedAt: "更新時間", qrTrigger: "QR Code", qrScanToVisit: "掃描即可瀏覽" },
|
|
490
490
|
wikiComments: { title: "百科評論", empty: "💧 暫無評論", contentRequired: "請先填寫評論內容再嘗試提交", submitSuccess: "提交成功,請等待審核" },
|
|
491
491
|
wikiRevisions: { title: "歷史版本", version: "版本", contributor: "貢獻者", remark: "修訂說明" },
|
|
492
|
+
wikiComment: { top: "置頂", star: "精華", collapse: "收起", setStar: "設為精華", cancelStar: "取消精華", setTop: "置頂", cancelTop: "取消置頂", publishUnavailable: "發布評論異常,請聯絡管理員", actionUnavailable: "操作異常,請聯絡管理員" },
|
|
493
|
+
gamePrice: { zhuan: "磚", jin: "金", yin: "銀", tong: "銅" },
|
|
492
494
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jx3box/jx3box-ui",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.22",
|
|
4
4
|
"description": "JX3BOX Vue3 UI",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"build:dev": "cross-env NODE_OPTIONS=--max-old-space-size=4096 vue-cli-service build --mode development",
|
|
14
14
|
"build:prod": "cross-env NODE_OPTIONS=--max-old-space-size=4096 vue-cli-service build --mode production",
|
|
15
15
|
"lint": "vue-cli-service lint",
|
|
16
|
+
"test:i18n": "node --test tests/wiki-i18n-contract.test.js",
|
|
16
17
|
"update": "npm --registry https://registry.npmjs.org install @jx3box/jx3box-common@latest @jx3box/jx3box-data@latest @jx3box/jx3box-editor@latest @jx3box/jx3box-emotion@latest @jx3box/jx3box-macro@latest @jx3box/jx3box-talent@latest"
|
|
17
18
|
},
|
|
18
19
|
"eslintConfig": {
|
package/src/App.vue
CHANGED
|
@@ -111,27 +111,36 @@
|
|
|
111
111
|
</el-radio-group>
|
|
112
112
|
<singlebox :post="post" />
|
|
113
113
|
</el-tab-pane>
|
|
114
|
-
<el-tab-pane label="百科组件" name="wiki">
|
|
114
|
+
<el-tab-pane class="m-pvx-wiki-demo" label="百科组件" name="wiki">
|
|
115
115
|
<h1 class="m-title">游戏价格组件</h1>
|
|
116
|
-
<div class="m-block">
|
|
117
|
-
<GamePrice
|
|
116
|
+
<div class="m-block m-game-price-demo">
|
|
117
|
+
<GamePrice
|
|
118
|
+
:price="100009999"
|
|
119
|
+
:align="true"
|
|
120
|
+
variant="surface"
|
|
121
|
+
></GamePrice>
|
|
122
|
+
<GamePrice
|
|
123
|
+
:price="-20800"
|
|
124
|
+
variant="surface"
|
|
125
|
+
style="--jx3box-ui-game-price-negative-color: #e11d48"
|
|
126
|
+
></GamePrice>
|
|
118
127
|
</div>
|
|
119
128
|
|
|
120
|
-
<h1 class="m-title">百科面板</h1>
|
|
121
129
|
<div class="m-block">
|
|
122
|
-
<WikiPanel :wiki-post="wiki_post">
|
|
123
|
-
<template #head-title
|
|
130
|
+
<WikiPanel :wiki-post="wiki_post" variant="surface">
|
|
131
|
+
<template #head-title>
|
|
132
|
+
<i class="u-icon el-icon-document"></i>
|
|
133
|
+
<span class="u-txt">奇遇攻略</span>
|
|
134
|
+
</template>
|
|
124
135
|
</WikiPanel>
|
|
125
136
|
</div>
|
|
126
137
|
|
|
127
|
-
<h1 class="m-title">百科评论</h1>
|
|
128
138
|
<div class="m-block">
|
|
129
|
-
<WikiComments :source-id="12572" type="achievement" />
|
|
139
|
+
<WikiComments :source-id="12572" type="achievement" variant="surface" />
|
|
130
140
|
</div>
|
|
131
141
|
|
|
132
|
-
<h1 class="m-title">百科修订</h1>
|
|
133
142
|
<div class="m-block">
|
|
134
|
-
<WikiRevisions type="achievement" :source-id="12572" />
|
|
143
|
+
<WikiRevisions type="achievement" :source-id="12572" variant="surface" />
|
|
135
144
|
</div>
|
|
136
145
|
</el-tab-pane>
|
|
137
146
|
<el-tab-pane label="移动组件" name="mobile">
|
|
@@ -379,4 +388,38 @@ export default {
|
|
|
379
388
|
flex-direction: column;
|
|
380
389
|
gap: 20px;
|
|
381
390
|
}
|
|
391
|
+
|
|
392
|
+
.m-pvx-wiki-demo {
|
|
393
|
+
padding: 24px;
|
|
394
|
+
background: #f6f8fa;
|
|
395
|
+
|
|
396
|
+
> .m-title:first-child {
|
|
397
|
+
margin-top: 0;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
> .m-block {
|
|
401
|
+
padding: 0;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
> .m-block + .m-block {
|
|
405
|
+
margin-top: 24px;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.m-game-price-demo {
|
|
410
|
+
display: flex;
|
|
411
|
+
flex-wrap: wrap;
|
|
412
|
+
align-items: flex-start;
|
|
413
|
+
gap: 20px;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
@media screen and (max-width: 720px) {
|
|
417
|
+
.m-pvx-wiki-demo {
|
|
418
|
+
padding: 16px;
|
|
419
|
+
|
|
420
|
+
> .m-block + .m-block {
|
|
421
|
+
margin-top: 20px;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
382
425
|
</style>
|
package/src/interact/QRcode.vue
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div ref="trigger" class="w-qrcode" v-bind="$attrs" @click="togglePic" v-if="mode == 'cms'">
|
|
3
3
|
<img class="u-icon" svg-inline src="../../assets/img/widget/qr-code.svg" />
|
|
4
|
-
<span class="u-text"
|
|
4
|
+
<span class="u-text">{{ triggerText }}</span>
|
|
5
5
|
</div>
|
|
6
6
|
<teleport to="body">
|
|
7
|
-
<div
|
|
7
|
+
<div
|
|
8
|
+
v-if="mode == 'cms' && active"
|
|
9
|
+
class="u-qrcode u-qrcode-popup w-qrcode-static"
|
|
10
|
+
:class="popupClass"
|
|
11
|
+
:style="popupStyle"
|
|
12
|
+
@click.stop
|
|
13
|
+
>
|
|
8
14
|
<qrcode-vue class="u-pic" :value="value" :size="size" level="H"></qrcode-vue>
|
|
9
|
-
<span class="u-txt"
|
|
15
|
+
<span class="u-txt">{{ popupText }}</span>
|
|
10
16
|
</div>
|
|
11
17
|
</teleport>
|
|
12
18
|
<div class="w-qrcode-static" v-bind="$attrs" v-if="mode == 'static'">
|
|
@@ -38,6 +44,18 @@ export default {
|
|
|
38
44
|
type: Number,
|
|
39
45
|
default: 100,
|
|
40
46
|
},
|
|
47
|
+
triggerText: {
|
|
48
|
+
type: String,
|
|
49
|
+
default: "二维码",
|
|
50
|
+
},
|
|
51
|
+
popupText: {
|
|
52
|
+
type: String,
|
|
53
|
+
default: "扫一扫即可访问",
|
|
54
|
+
},
|
|
55
|
+
popupClass: {
|
|
56
|
+
type: [String, Array, Object],
|
|
57
|
+
default: "",
|
|
58
|
+
},
|
|
41
59
|
},
|
|
42
60
|
data: function () {
|
|
43
61
|
return {
|
package/src/wiki/GamePrice.vue
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<span class="c-game-price" :class="
|
|
2
|
+
<span class="c-game-price" :class="rootClasses">
|
|
3
3
|
<span class="u-neg" v-if="numericPrice < 0">- </span>
|
|
4
4
|
<span class="u-price">
|
|
5
5
|
<template v-for="part in priceParts" :key="part.unit">
|
|
6
6
|
<span class="u-part" :class="`u-${part.unit}`">
|
|
7
7
|
<span class="u-price-value">{{ part.value }}</span>
|
|
8
|
-
<img :src="part.icon" :alt="part.alt" />
|
|
8
|
+
<img :src="part.icon" :alt="$jx3boxT(`jx3boxUi.gamePrice.${part.unit}`, part.alt)" />
|
|
9
9
|
</span>
|
|
10
10
|
</template>
|
|
11
11
|
</span>
|
|
@@ -36,16 +36,38 @@ export default {
|
|
|
36
36
|
type: [Boolean, String],
|
|
37
37
|
default: false,
|
|
38
38
|
},
|
|
39
|
+
variant: {
|
|
40
|
+
type: String,
|
|
41
|
+
default: "default",
|
|
42
|
+
validator: (value) => ["default", "surface", "plain"].includes(value),
|
|
43
|
+
},
|
|
44
|
+
size: {
|
|
45
|
+
type: String,
|
|
46
|
+
default: "medium",
|
|
47
|
+
validator: (value) => ["small", "medium", "large"].includes(value),
|
|
48
|
+
},
|
|
49
|
+
layout: {
|
|
50
|
+
type: String,
|
|
51
|
+
default: "inline",
|
|
52
|
+
validator: (value) => ["inline", "block"].includes(value),
|
|
53
|
+
},
|
|
54
|
+
wrap: {
|
|
55
|
+
type: Boolean,
|
|
56
|
+
default: true,
|
|
57
|
+
},
|
|
39
58
|
},
|
|
40
59
|
computed: {
|
|
41
60
|
numericPrice() {
|
|
42
61
|
const value = Number(this.price);
|
|
43
62
|
return Number.isFinite(value) ? Math.trunc(value) : 0;
|
|
44
63
|
},
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
|
|
64
|
+
rootClasses() {
|
|
65
|
+
const classes = [`is-${this.variant}`, `is-size-${this.size}`, `is-layout-${this.layout}`];
|
|
66
|
+
if (this.align === true) classes.push("is-align");
|
|
67
|
+
if (typeof this.align === "string" && this.align) classes.push(`is-${this.align}`);
|
|
68
|
+
if (!this.wrap) classes.push("is-nowrap");
|
|
69
|
+
if (this.numericPrice < 0) classes.push("is-negative");
|
|
70
|
+
return classes;
|
|
49
71
|
},
|
|
50
72
|
priceParts() {
|
|
51
73
|
const value = Math.abs(this.numericPrice);
|
|
@@ -75,21 +97,28 @@ export default {
|
|
|
75
97
|
|
|
76
98
|
<style lang="less">
|
|
77
99
|
.c-game-price {
|
|
100
|
+
--jx3box-ui-game-price-color: #333;
|
|
101
|
+
--jx3box-ui-game-price-icon-size: 18px;
|
|
102
|
+
--jx3box-ui-game-price-font-size: 12px;
|
|
103
|
+
--jx3box-ui-game-price-gap: 6px;
|
|
104
|
+
--jx3box-ui-game-price-negative-color: var(--jx3box-ui-game-price-color);
|
|
105
|
+
|
|
78
106
|
display: inline-flex;
|
|
79
107
|
align-items: center;
|
|
80
|
-
color:
|
|
108
|
+
color: var(--jx3box-ui-game-price-color);
|
|
81
109
|
font-weight: 500;
|
|
82
110
|
|
|
83
111
|
.u-price {
|
|
84
112
|
display: inline-flex;
|
|
85
113
|
align-items: center;
|
|
86
114
|
flex-wrap: wrap;
|
|
87
|
-
gap:
|
|
115
|
+
gap: var(--jx3box-ui-game-price-gap);
|
|
88
116
|
}
|
|
89
117
|
|
|
90
118
|
.u-neg {
|
|
91
119
|
display: inline-flex;
|
|
92
120
|
align-items: center;
|
|
121
|
+
color: var(--jx3box-ui-game-price-negative-color);
|
|
93
122
|
}
|
|
94
123
|
|
|
95
124
|
.u-part {
|
|
@@ -100,14 +129,15 @@ export default {
|
|
|
100
129
|
}
|
|
101
130
|
|
|
102
131
|
img {
|
|
103
|
-
width:
|
|
104
|
-
height:
|
|
132
|
+
width: var(--jx3box-ui-game-price-icon-size);
|
|
133
|
+
height: var(--jx3box-ui-game-price-icon-size);
|
|
105
134
|
object-fit: contain;
|
|
106
135
|
vertical-align: middle;
|
|
107
136
|
}
|
|
108
137
|
|
|
109
138
|
.u-price-value {
|
|
110
|
-
|
|
139
|
+
font-size: var(--jx3box-ui-game-price-font-size);
|
|
140
|
+
line-height: var(--jx3box-ui-game-price-icon-size);
|
|
111
141
|
}
|
|
112
142
|
|
|
113
143
|
&.is-align {
|
|
@@ -130,5 +160,32 @@ export default {
|
|
|
130
160
|
justify-content: flex-end;
|
|
131
161
|
width: 100%;
|
|
132
162
|
}
|
|
163
|
+
|
|
164
|
+
&.is-size-small {
|
|
165
|
+
--jx3box-ui-game-price-icon-size: 14px;
|
|
166
|
+
--jx3box-ui-game-price-font-size: 11px;
|
|
167
|
+
--jx3box-ui-game-price-gap: 4px;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
&.is-size-large {
|
|
171
|
+
--jx3box-ui-game-price-icon-size: 22px;
|
|
172
|
+
--jx3box-ui-game-price-font-size: 14px;
|
|
173
|
+
--jx3box-ui-game-price-gap: 8px;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
&.is-layout-block {
|
|
177
|
+
display: flex;
|
|
178
|
+
width: 100%;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
&.is-nowrap {
|
|
182
|
+
.u-price {
|
|
183
|
+
flex-wrap: nowrap;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
&.is-surface {
|
|
188
|
+
--jx3box-ui-game-price-color: #334155;
|
|
189
|
+
}
|
|
133
190
|
}
|
|
134
191
|
</style>
|
package/src/wiki/WikiComment.vue
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
v-text="comment.user_nickname"
|
|
12
12
|
></a>
|
|
13
13
|
<template v-if="comment.parent_id">
|
|
14
|
-
<span> 
|
|
14
|
+
<span> {{ $jx3boxT("jx3boxUi.wiki.reply", "回复") }} </span>
|
|
15
15
|
<a
|
|
16
16
|
class="u-nickname"
|
|
17
17
|
:href="comment.parent.user_id ? author_url(comment.parent.user_id) : null"
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
</template>
|
|
22
22
|
<span class="u-mark u-top" v-if="comment.is_top">
|
|
23
23
|
<i class="el-icon-download"></i>
|
|
24
|
-
置顶
|
|
24
|
+
{{ $jx3boxT("jx3boxUi.wikiComment.top", "置顶") }}
|
|
25
25
|
</span>
|
|
26
26
|
<span class="u-mark u-star" v-if="comment.is_star">
|
|
27
27
|
<i class="el-icon-star-on"></i>
|
|
28
|
-
精华
|
|
28
|
+
{{ $jx3boxT("jx3boxUi.wikiComment.star", "精华") }}
|
|
29
29
|
</span>
|
|
30
30
|
</div>
|
|
31
31
|
<p class="u-content" v-html="comment.content"></p>
|
|
@@ -38,9 +38,11 @@
|
|
|
38
38
|
class="u-reply"
|
|
39
39
|
@click="comment.reply_form.show = !comment.reply_form.show"
|
|
40
40
|
size="small"
|
|
41
|
+
:aria-expanded="true"
|
|
42
|
+
:aria-controls="`wiki-comment-reply-${comment.id}`"
|
|
41
43
|
>
|
|
42
44
|
<i class="el-icon-arrow-up"></i>
|
|
43
|
-
<span
|
|
45
|
+
<span>{{ $jx3boxT("jx3boxUi.wikiComment.collapse", "收起") }}</span>
|
|
44
46
|
</el-button>
|
|
45
47
|
<el-button
|
|
46
48
|
type="primary"
|
|
@@ -50,8 +52,10 @@
|
|
|
50
52
|
@click="comment.reply_form.show = !comment.reply_form.show"
|
|
51
53
|
icon="ChatDotRound"
|
|
52
54
|
size="small"
|
|
55
|
+
:aria-expanded="false"
|
|
56
|
+
:aria-controls="`wiki-comment-reply-${comment.id}`"
|
|
53
57
|
>
|
|
54
|
-
<span
|
|
58
|
+
<span>{{ $jx3boxT("jx3boxUi.wiki.reply", "回复") }}</span>
|
|
55
59
|
</el-button>
|
|
56
60
|
<template v-if="isEditor && !comment.parent_id">
|
|
57
61
|
<el-button
|
|
@@ -61,25 +65,43 @@
|
|
|
61
65
|
plain
|
|
62
66
|
:icon="comment.is_star ? 'StarFilled' : 'Star'"
|
|
63
67
|
size="small"
|
|
64
|
-
>{{
|
|
68
|
+
>{{
|
|
69
|
+
comment.is_star
|
|
70
|
+
? $jx3boxT("jx3boxUi.wikiComment.cancelStar", "取消加精")
|
|
71
|
+
: $jx3boxT("jx3boxUi.wikiComment.setStar", "加精")
|
|
72
|
+
}}</el-button
|
|
65
73
|
>
|
|
66
74
|
<el-button type="primary" class="u-reply" @click="onTop(comment)" plain icon="Top" size="small">{{
|
|
67
|
-
comment.is_top
|
|
75
|
+
comment.is_top
|
|
76
|
+
? $jx3boxT("jx3boxUi.wikiComment.cancelTop", "取消置顶")
|
|
77
|
+
: $jx3boxT("jx3boxUi.wikiComment.setTop", "置顶")
|
|
68
78
|
}}</el-button>
|
|
69
79
|
</template>
|
|
70
80
|
<!-- 更新时间 -->
|
|
71
81
|
<span class="u-time" v-text="ts2str(comment.updated)"></span>
|
|
72
82
|
</div>
|
|
73
83
|
<!-- 评论回复表单 -->
|
|
74
|
-
<div
|
|
75
|
-
|
|
84
|
+
<div
|
|
85
|
+
class="m-reply-form"
|
|
86
|
+
:id="`wiki-comment-reply-${comment.id}`"
|
|
87
|
+
v-if="comment.reply_form && comment.reply_form.show"
|
|
88
|
+
>
|
|
89
|
+
<textarea
|
|
90
|
+
class="u-reply-content"
|
|
91
|
+
v-model="comment.reply_form.content"
|
|
92
|
+
:aria-label="$jx3boxT('jx3boxUi.wiki.reply', '回复')"
|
|
93
|
+
></textarea>
|
|
76
94
|
<div class="u-author">
|
|
77
|
-
<span
|
|
78
|
-
<input
|
|
95
|
+
<span>{{ $jx3boxT("jx3boxUi.wiki.nickname", "昵称:") }}</span>
|
|
96
|
+
<input
|
|
97
|
+
v-model="comment.reply_form.user_nickname"
|
|
98
|
+
type="text"
|
|
99
|
+
:aria-label="$jx3boxT('jx3boxUi.wiki.nickname', '昵称:')"
|
|
100
|
+
/>
|
|
79
101
|
</div>
|
|
80
102
|
<el-button type="primary" class="u-submit" @click="create_comment(comment.reply_form, comment.id)">
|
|
81
103
|
<i class="el-icon-check"></i>
|
|
82
|
-
<span
|
|
104
|
+
<span>{{ $jx3boxT("jx3boxUi.common.submit", "提交") }}</span>
|
|
83
105
|
</el-button>
|
|
84
106
|
</div>
|
|
85
107
|
</div>
|
|
@@ -117,7 +139,10 @@ export default {
|
|
|
117
139
|
if (!app.create_comment) app = app.$parent;
|
|
118
140
|
if (!app.create_comment) {
|
|
119
141
|
this.$message({
|
|
120
|
-
message:
|
|
142
|
+
message: this.$jx3boxT(
|
|
143
|
+
"jx3boxUi.wikiComment.publishUnavailable",
|
|
144
|
+
"发布评论异常,请联系管理员"
|
|
145
|
+
),
|
|
121
146
|
type: "warning",
|
|
122
147
|
});
|
|
123
148
|
return;
|
|
@@ -132,7 +157,10 @@ export default {
|
|
|
132
157
|
if (!app.star_comment) app = app.$parent;
|
|
133
158
|
if (!app.star_comment) {
|
|
134
159
|
this.$message({
|
|
135
|
-
message:
|
|
160
|
+
message: this.$jx3boxT(
|
|
161
|
+
"jx3boxUi.wikiComment.actionUnavailable",
|
|
162
|
+
"操作异常,请联系管理员"
|
|
163
|
+
),
|
|
136
164
|
type: "warning",
|
|
137
165
|
});
|
|
138
166
|
return;
|
|
@@ -144,7 +172,10 @@ export default {
|
|
|
144
172
|
if (!app.top_comment) app = app.$parent;
|
|
145
173
|
if (!app.top_comment) {
|
|
146
174
|
this.$message({
|
|
147
|
-
message:
|
|
175
|
+
message: this.$jx3boxT(
|
|
176
|
+
"jx3boxUi.wikiComment.actionUnavailable",
|
|
177
|
+
"操作异常,请联系管理员"
|
|
178
|
+
),
|
|
148
179
|
type: "warning",
|
|
149
180
|
});
|
|
150
181
|
return;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<WikiPanel class="c-wiki-comments" scene="detail">
|
|
2
|
+
<WikiPanel class="c-wiki-comments" scene="detail" :variant="variant">
|
|
3
3
|
<template #head-title>
|
|
4
4
|
<i class="u-icon el-icon-chat-line-round"></i>
|
|
5
5
|
<span class="u-txt">{{ $jx3boxT('jx3boxUi.wikiComments.title', '百科评论') }}</span>
|
|
@@ -29,10 +29,18 @@
|
|
|
29
29
|
<i class="el-icon-chat-dot-round"></i>
|
|
30
30
|
<span>{{ $jx3boxT('jx3boxUi.wiki.reply', '回复') }}</span>
|
|
31
31
|
</h4>
|
|
32
|
-
<textarea
|
|
32
|
+
<textarea
|
|
33
|
+
class="u-reply-content"
|
|
34
|
+
v-model="reply_form.content"
|
|
35
|
+
:aria-label="$jx3boxT('jx3boxUi.wiki.reply', '回复')"
|
|
36
|
+
></textarea>
|
|
33
37
|
<div class="u-author">
|
|
34
38
|
<span>{{ $jx3boxT('jx3boxUi.wiki.nickname', '昵称:') }}</span>
|
|
35
|
-
<input
|
|
39
|
+
<input
|
|
40
|
+
v-model="reply_form.user_nickname"
|
|
41
|
+
type="text"
|
|
42
|
+
:aria-label="$jx3boxT('jx3boxUi.wiki.nickname', '昵称:')"
|
|
43
|
+
/>
|
|
36
44
|
</div>
|
|
37
45
|
<el-button type="primary" class="u-submit" @click="create_comment(reply_form)">
|
|
38
46
|
<i class="el-icon-check"></i>
|
|
@@ -61,6 +69,11 @@ export default {
|
|
|
61
69
|
type: [Number, String],
|
|
62
70
|
default: 0,
|
|
63
71
|
},
|
|
72
|
+
variant: {
|
|
73
|
+
type: String,
|
|
74
|
+
default: "default",
|
|
75
|
+
validator: (value) => ["default", "surface", "plain"].includes(value),
|
|
76
|
+
},
|
|
64
77
|
},
|
|
65
78
|
data() {
|
|
66
79
|
return {
|
|
@@ -163,7 +176,9 @@ export default {
|
|
|
163
176
|
});
|
|
164
177
|
},
|
|
165
178
|
getDefaultNickname() {
|
|
166
|
-
return User.isLogin()
|
|
179
|
+
return User.isLogin()
|
|
180
|
+
? User.getInfo().name
|
|
181
|
+
: this.$jx3boxT("jx3boxUi.wiki.mysterious", "神秘侠士");
|
|
167
182
|
},
|
|
168
183
|
handleCurrentChange(page) {
|
|
169
184
|
this.page = page;
|
package/src/wiki/WikiPanel.vue
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
class="c-wiki-panel"
|
|
4
|
-
:class="
|
|
5
|
-
border: border,
|
|
6
|
-
}"
|
|
4
|
+
:class="rootClasses"
|
|
7
5
|
>
|
|
8
6
|
<div class="m-panel-head">
|
|
9
7
|
<slot name="head-before"></slot>
|
|
@@ -11,7 +9,13 @@
|
|
|
11
9
|
<slot name="head-title"></slot>
|
|
12
10
|
</div>
|
|
13
11
|
<div class="m-panel-actions">
|
|
14
|
-
<QRcode
|
|
12
|
+
<QRcode
|
|
13
|
+
v-if="wikiPost && showQR"
|
|
14
|
+
class="u-qr"
|
|
15
|
+
:trigger-text="$jx3boxT('jx3boxUi.wikiPanel.qrTrigger', '二维码')"
|
|
16
|
+
:popup-text="$jx3boxT('jx3boxUi.wikiPanel.qrScanToVisit', '扫一扫即可访问')"
|
|
17
|
+
popup-class="u-wiki-panel-qrcode-popup"
|
|
18
|
+
/>
|
|
15
19
|
<slot name="head-actions"></slot>
|
|
16
20
|
</div>
|
|
17
21
|
<slot name="head-after"></slot>
|
|
@@ -72,6 +76,11 @@ export default {
|
|
|
72
76
|
type: String,
|
|
73
77
|
default: "default",
|
|
74
78
|
},
|
|
79
|
+
variant: {
|
|
80
|
+
type: String,
|
|
81
|
+
default: "default",
|
|
82
|
+
validator: (value) => ["default", "surface", "plain"].includes(value),
|
|
83
|
+
},
|
|
75
84
|
border: {
|
|
76
85
|
type: Boolean,
|
|
77
86
|
default: true,
|
|
@@ -86,6 +95,15 @@ export default {
|
|
|
86
95
|
stat: null,
|
|
87
96
|
};
|
|
88
97
|
},
|
|
98
|
+
computed: {
|
|
99
|
+
rootClasses() {
|
|
100
|
+
return {
|
|
101
|
+
border: this.border,
|
|
102
|
+
[`is-${this.variant}`]: true,
|
|
103
|
+
[`is-scene-${this.scene}`]: true,
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
},
|
|
89
107
|
watch: {
|
|
90
108
|
wikiPost: {
|
|
91
109
|
immediate: true,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<WikiPanel class="c-wiki-revisions" scene="detail">
|
|
2
|
+
<WikiPanel class="c-wiki-revisions" scene="detail" :variant="variant">
|
|
3
3
|
<template #head-title>
|
|
4
4
|
<i class="u-icon el-icon-time"></i>
|
|
5
5
|
<span class="u-txt">{{ $jx3boxT('jx3boxUi.wikiRevisions.title', '历史版本') }}</span>
|
|
@@ -62,6 +62,11 @@ export default {
|
|
|
62
62
|
type: [Boolean, Number],
|
|
63
63
|
default: false,
|
|
64
64
|
},
|
|
65
|
+
variant: {
|
|
66
|
+
type: String,
|
|
67
|
+
default: "default",
|
|
68
|
+
validator: (value) => ["default", "surface", "plain"].includes(value),
|
|
69
|
+
},
|
|
65
70
|
},
|
|
66
71
|
data: function () {
|
|
67
72
|
return {
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const assert = require("node:assert/strict");
|
|
2
|
+
const test = require("node:test");
|
|
3
|
+
|
|
4
|
+
require("@babel/register")({
|
|
5
|
+
babelrc: false,
|
|
6
|
+
configFile: false,
|
|
7
|
+
extensions: [".js"],
|
|
8
|
+
ignore: [/node_modules/],
|
|
9
|
+
plugins: ["@babel/plugin-transform-modules-commonjs"],
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
createJx3boxUiI18n,
|
|
14
|
+
getJx3boxUiMessages,
|
|
15
|
+
mergeJx3boxUiMessages,
|
|
16
|
+
setJx3boxUiI18n,
|
|
17
|
+
} = require("../i18n");
|
|
18
|
+
const i18nMixin = require("../i18n/mixin").default;
|
|
19
|
+
const { createI18n } = require("vue-i18n");
|
|
20
|
+
|
|
21
|
+
const TARGET_PREFIXES = [
|
|
22
|
+
"wiki.",
|
|
23
|
+
"wikiPanel.",
|
|
24
|
+
"wikiComments.",
|
|
25
|
+
"wikiComment.",
|
|
26
|
+
"wikiRevisions.",
|
|
27
|
+
"gamePrice.",
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
function flattenMessages(value, prefix = "", output = {}) {
|
|
31
|
+
Object.entries(value || {}).forEach(([key, child]) => {
|
|
32
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
33
|
+
if (child && typeof child === "object" && !Array.isArray(child)) {
|
|
34
|
+
flattenMessages(child, path, output);
|
|
35
|
+
} else {
|
|
36
|
+
output[path] = child;
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return output;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function placeholders(value) {
|
|
43
|
+
return [...String(value ?? "").matchAll(/\{(\w+)\}/g)].map((match) => match[1]).sort();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
test("loads the target component messages for each supported locale", () => {
|
|
47
|
+
const messages = getJx3boxUiMessages();
|
|
48
|
+
Object.entries(messages).forEach(([locale, message]) => {
|
|
49
|
+
const flattened = flattenMessages(message);
|
|
50
|
+
TARGET_PREFIXES.forEach((prefix) => {
|
|
51
|
+
assert.ok(
|
|
52
|
+
Object.keys(flattened).some((key) => key.startsWith(prefix)),
|
|
53
|
+
`${locale} is missing ${prefix}`
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const i18n = createJx3boxUiI18n({ locale: "en-US" });
|
|
59
|
+
assert.equal(i18n.global.t("jx3boxUi.wikiComment.setTop"), "Pin");
|
|
60
|
+
assert.equal(i18n.global.t("jx3boxUi.wikiPanel.qrTrigger"), "QR code");
|
|
61
|
+
assert.equal(i18n.global.t("jx3boxUi.wikiPanel.qrScanToVisit"), "Scan to open this page");
|
|
62
|
+
assert.equal(i18n.global.t("jx3boxUi.gamePrice.jin"), "gold");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("merges target messages into a host i18n instance", () => {
|
|
66
|
+
const hostI18n = createI18n({
|
|
67
|
+
legacy: false,
|
|
68
|
+
locale: "en-US",
|
|
69
|
+
fallbackLocale: "zh-CN",
|
|
70
|
+
messages: {
|
|
71
|
+
"en-US": {
|
|
72
|
+
host: {
|
|
73
|
+
title: "Host",
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
mergeJx3boxUiMessages(hostI18n);
|
|
80
|
+
setJx3boxUiI18n(hostI18n);
|
|
81
|
+
|
|
82
|
+
assert.equal(hostI18n.global.t("host.title"), "Host");
|
|
83
|
+
assert.equal(hostI18n.global.t("jx3boxUi.wikiComments.title"), "Wiki Comments");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("component mixin keeps the explicit fallback when the host misses a key", () => {
|
|
87
|
+
setJx3boxUiI18n(null);
|
|
88
|
+
const context = {
|
|
89
|
+
$t(key) {
|
|
90
|
+
return key;
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
assert.equal(
|
|
95
|
+
i18nMixin.methods.$jx3boxT.call(context, "jx3boxUi.missing.example", "欢迎 {name}", { name: "侠士" }),
|
|
96
|
+
"欢迎 侠士"
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("keeps four locale message keys and placeholders aligned", () => {
|
|
101
|
+
const messages = getJx3boxUiMessages();
|
|
102
|
+
const locales = Object.keys(messages);
|
|
103
|
+
const baseline = flattenMessages(messages["zh-CN"]);
|
|
104
|
+
const baselineKeys = Object.keys(baseline).sort();
|
|
105
|
+
|
|
106
|
+
locales.forEach((locale) => {
|
|
107
|
+
const current = flattenMessages(messages[locale]);
|
|
108
|
+
assert.deepEqual(Object.keys(current).sort(), baselineKeys, `${locale} message keys differ from zh-CN`);
|
|
109
|
+
baselineKeys.forEach((key) => {
|
|
110
|
+
assert.deepEqual(placeholders(current[key]), placeholders(baseline[key]), `${locale}:${key} placeholders differ`);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|