@jx3box/jx3box-editor 3.2.6 → 3.2.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jx3box/jx3box-editor",
3
- "version": "3.2.6",
3
+ "version": "3.2.7",
4
4
  "description": "JX3BOX Article & Editor",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -74,9 +74,9 @@
74
74
  "postcss": "^8.5.6",
75
75
  "postcss-safe-parser": "^7.0.1",
76
76
  "prettier": "2.7.1",
77
+ "qrcode.vue": "^3.6.0",
77
78
  "sass": "^1.97.3",
78
79
  "sass-loader": "^16.0.7",
79
- "qrcode.vue": "^3.6.0",
80
80
  "serve": "^14.2.0",
81
81
  "storybook": "^8.6.14",
82
82
  "style-resources-loader": "^1.5.0",
package/src/Article.vue CHANGED
@@ -47,7 +47,12 @@
47
47
  v-show="jx3_element.type == 'skill'"
48
48
  />
49
49
  <jx3-npc :client="npc.client" :id="npc.id" v-show="jx3_element.type === 'npc'" />
50
- <jx3-author :uid="author.id" v-show="jx3_element.type === 'author'" />
50
+ <jx3-author
51
+ :key="author.id"
52
+ :uid="author.id"
53
+ :visible="jx3_element.type === 'author' && jx3_element.style.display !== 'none'"
54
+ v-show="jx3_element.type === 'author'"
55
+ />
51
56
  <jx3-emotion-author :id="emotion.id" v-show="jx3_element.type === 'emotion'" />
52
57
  </div>
53
58
  </div>
@@ -7,7 +7,7 @@
7
7
  class="u-avatar"
8
8
  :uid="uid"
9
9
  :url="data.user_avatar"
10
- :size="68"
10
+ :size="60"
11
11
  :frame="data.user_avatar_frame"
12
12
  />
13
13
  <div class="u-info">
@@ -58,29 +58,61 @@
58
58
  </div>
59
59
  </div>
60
60
  </div>
61
- <div class="u-teams" v-if="teams && teams.length">
62
- <a class="u-team" v-for="(item, i) in teams" :key="i" :href="teamLink(item.team_id)" target="_blank">
63
- <img class="u-team-logo" :src="showTeamLogo(item.team_logo)" />
64
- <span class="u-team-name">{{ item.team_name }}@{{ item.team_server }}</span>
65
- </a>
61
+ <div class="u-author-role" v-if="authorRoles.length">
62
+ <div class="u-author-role-label"></div>
63
+ <div class="u-author-roles">
64
+ <component
65
+ :is="item.url ? 'a' : 'span'"
66
+ class="u-author-role-item"
67
+ :href="item.url"
68
+ :target="item.url ? '_blank' : null"
69
+ :style="roleStyle(item)"
70
+ v-for="item in authorRoles"
71
+ :key="item.key"
72
+ >
73
+ <img class="u-author-role-icon" v-if="item.icon" :src="item.icon" />
74
+ <span class="u-author-role-content">
75
+ <span class="u-author-role-name">{{ item.name }}</span>
76
+ <span class="u-author-role-server" v-if="item.server">@{{ item.server }}</span>
77
+ </span>
78
+ </component>
79
+ </div>
66
80
  </div>
67
81
  </div>
68
82
  </div>
69
83
  </template>
70
84
 
71
85
  <script>
72
- import { authorLink, getLink, getMedalLink, getThumbnail } from "@jx3box/jx3box-common/js/utils";
73
- import { getUserInfo, getUserMedals, getUserPublicTeams } from "../service/author.js";
74
- import { getDecoration, getDecorationJson, getDecorationV2 } from "../service/cms.js";
86
+ import { authorLink, getMedalLink, showSchoolIcon } from "@jx3box/jx3box-common/js/utils";
87
+ import { getUserInfo, getUserMedals } from "../service/author.js";
88
+ import { getDecoration, getDecorationJson, getUserSkin } from "../service/cms.js";
75
89
  import User from "@jx3box/jx3box-common/js/user";
76
90
  import JX3BOX from "@jx3box/jx3box-common/data/jx3box.json";
77
91
  import Avatar from "./Avatar.vue";
78
- const ATCARD_KEY = "decoration_atcard_v2";
92
+ const ATCARD_CACHE_KEY = "skin_atcard_pc_authorcard_v2_";
93
+ const ATCARD_SUBTYPE = "pc_authorcard";
79
94
  const DECORATION_JSON = "decoration_json";
80
95
  const DECORATION_KEY = "decoration_me";
81
96
  const HONOR_KEY = "honor_me";
97
+ const POSITION_MAP = {
98
+ lt: "left top",
99
+ mt: "center top",
100
+ ct: "center top",
101
+ rt: "right top",
102
+ lm: "left center",
103
+ ml: "left center",
104
+ mm: "center center",
105
+ cm: "center center",
106
+ o: "center center",
107
+ rm: "right center",
108
+ mr: "right center",
109
+ lb: "left bottom",
110
+ mb: "center bottom",
111
+ cb: "center bottom",
112
+ rb: "right bottom",
113
+ };
82
114
 
83
- const { __server, __imgPath, __userLevelColor, __cdn, __userLevel } = JX3BOX;
115
+ const { __server, __imgPath, __userLevelColor, __cdn, __Root, __userLevel } = JX3BOX;
84
116
  export default {
85
117
  name: "Author",
86
118
  components: {
@@ -91,13 +123,17 @@ export default {
91
123
  type: [String, Number],
92
124
  required: true,
93
125
  },
126
+ visible: {
127
+ type: Boolean,
128
+ default: true,
129
+ },
94
130
  },
95
131
  data: () => ({
96
132
  data: null,
97
133
  medals: [],
98
- teams: [],
99
134
  loading: false,
100
135
  bg: "",
136
+ bgPosition: "right top",
101
137
  honor: "",
102
138
  honorStyle: {},
103
139
  }),
@@ -123,10 +159,15 @@ export default {
123
159
  isSuperAuthor: function () {
124
160
  return !!this.data?.sign;
125
161
  },
162
+ authorRoles: function () {
163
+ const roles = this.normalizeRoles(this.data?.public_roles);
164
+ return roles.map(this.normalizeRole).filter((item) => item.name);
165
+ },
126
166
  authorCardStyle: function () {
127
167
  return this.bg
128
168
  ? {
129
169
  backgroundImage: `url(${this.bg})`,
170
+ backgroundPosition: this.bgPosition,
130
171
  }
131
172
  : {};
132
173
  },
@@ -137,20 +178,28 @@ export default {
137
178
  handler(val) {
138
179
  if (val) {
139
180
  this.loadData();
140
- this.getAtcard();
181
+ if (this.visible) {
182
+ this.getAtcard();
183
+ }
141
184
  // this.getHonor();
142
185
  }
143
186
  },
144
187
  },
188
+ visible: {
189
+ handler(val) {
190
+ if (val && this.uid) {
191
+ this.getAtcard();
192
+ }
193
+ },
194
+ },
145
195
  },
146
196
  methods: {
147
197
  loadData: function () {
148
- const promises = [getUserInfo(this.uid), getUserMedals(this.uid), getUserPublicTeams(this.uid)];
198
+ const promises = [getUserInfo(this.uid), getUserMedals(this.uid)];
149
199
  this.loading = true;
150
200
  Promise.all(promises).then((res) => {
151
201
  this.data = res[0];
152
202
  this.medals = res[1];
153
- this.teams = res[2];
154
203
  this.loading = false;
155
204
  });
156
205
  },
@@ -164,43 +213,98 @@ export default {
164
213
  this.medals = data;
165
214
  });
166
215
  },
167
- loadTeams: function () {
168
- return getUserPublicTeams(this.uid).then((data) => {
169
- this.teams = data && data.slice(0, 5);
170
- });
171
- },
172
216
  getAtcard() {
173
- let decoration_atcard = sessionStorage.getItem(ATCARD_KEY + this.uid);
174
- if (decoration_atcard == "no") {
175
- this.bg = "";
176
- return;
177
- }
178
- //已有缓存,读取解析
179
- if (decoration_atcard) {
180
- this.setAtcardBackground(decoration_atcard);
217
+ const uid = this.uid;
218
+ this.bg = "";
219
+ this.bgPosition = "right top";
220
+
221
+ const cached = this.getAtcardCache(uid);
222
+ if (cached) {
223
+ this.setAtcardBackground(cached === "no" ? null : cached);
181
224
  return;
182
225
  }
183
- getDecorationV2({
184
- using: 1,
185
- user_id: this.uid,
226
+
227
+ getUserSkin({
228
+ user_id: uid,
186
229
  type: "atcard",
187
- subtype: "pc_atcard",
188
- }).then((data) => {
189
- let res = data?.data?.data || [];
190
- let image = res[0]?.decorations?.[0]?.image;
191
- if (!image) {
192
- //空 则为无主题,不再加载接口,界面设No
193
- sessionStorage.setItem(ATCARD_KEY + this.uid, "no");
194
- this.bg = "";
195
- return;
196
- }
197
- image = this.showDecorationImage(image);
198
- sessionStorage.setItem(ATCARD_KEY + this.uid, image);
199
- this.setAtcardBackground(image);
200
- });
230
+ })
231
+ .then((data) => {
232
+ if (this.uid !== uid) return;
233
+
234
+ let rows = data?.data?.data || [];
235
+ let skin = this.selectAtcardSkin(this.flattenUserSkins(rows));
236
+ let image = this.showDecorationImage(skin?.image);
237
+ if (!image) {
238
+ this.setAtcardCache(uid, "no");
239
+ this.bg = "";
240
+ return;
241
+ }
242
+ let payload = {
243
+ image,
244
+ position: this.resolveAtcardPosition(skin?.position),
245
+ };
246
+ this.setAtcardCache(uid, payload);
247
+ this.setAtcardBackground(payload);
248
+ })
249
+ .catch(() => {
250
+ if (this.uid === uid) {
251
+ this.bg = "";
252
+ }
253
+ });
201
254
  },
202
255
  setAtcardBackground(val) {
203
- this.bg = val || "";
256
+ if (typeof val === "string") {
257
+ this.bg = val || "";
258
+ this.bgPosition = "right top";
259
+ return;
260
+ }
261
+ this.bg = val?.image || "";
262
+ this.bgPosition = val?.position || "right top";
263
+ },
264
+ getAtcardCache(uid) {
265
+ let cached = sessionStorage.getItem(ATCARD_CACHE_KEY + uid);
266
+ if (!cached) return null;
267
+ if (cached === "no") return cached;
268
+ try {
269
+ return JSON.parse(cached);
270
+ } catch (e) {
271
+ sessionStorage.removeItem(ATCARD_CACHE_KEY + uid);
272
+ return null;
273
+ }
274
+ },
275
+ setAtcardCache(uid, val) {
276
+ if (!uid) return;
277
+ sessionStorage.setItem(ATCARD_CACHE_KEY + uid, val === "no" ? "no" : JSON.stringify(val));
278
+ },
279
+ flattenUserSkins(rows = []) {
280
+ return rows.reduce((list, row) => {
281
+ let skins = Array.isArray(row?.skins) ? row.skins : [];
282
+ return list.concat(skins);
283
+ }, []);
284
+ },
285
+ selectAtcardSkin(list = []) {
286
+ let skins = list.filter((item) => item?.subtype === ATCARD_SUBTYPE && item?.image);
287
+ let theme = this.getCurrentTheme();
288
+ return (
289
+ skins.find((item) => item?.theme === "all") ||
290
+ skins.find((item) => item?.theme === theme) ||
291
+ skins.find((item) => !item?.theme) ||
292
+ null
293
+ );
294
+ },
295
+ getCurrentTheme() {
296
+ let theme =
297
+ document.documentElement.getAttribute("data-theme") ||
298
+ document.body?.getAttribute("data-theme") ||
299
+ localStorage.getItem("__theme") ||
300
+ "light";
301
+ return String(theme || "light").toLowerCase() === "dark" ? "dark" : "light";
302
+ },
303
+ resolveAtcardPosition(position) {
304
+ let key = String(position || "rt")
305
+ .trim()
306
+ .toLowerCase();
307
+ return POSITION_MAP[key] || POSITION_MAP.rt;
204
308
  },
205
309
  getHonor() {
206
310
  this.honor = "";
@@ -287,12 +391,6 @@ export default {
287
391
  showMedalDesc: function (item) {
288
392
  return item.medal_desc || medal_map[item.medal];
289
393
  },
290
- teamLink: function (team_id) {
291
- return getLink("org", team_id);
292
- },
293
- showTeamLogo: function (val) {
294
- return getThumbnail(val, 96);
295
- },
296
394
  showLevelColor: function (level) {
297
395
  return __userLevelColor[level];
298
396
  },
@@ -301,6 +399,80 @@ export default {
301
399
  if (/^(https?:)?\/\//.test(val) || /^(data|blob):/.test(val)) return val;
302
400
  return __cdn.replace(/\/$/, "") + "/" + val.replace(/^\//, "");
303
401
  },
402
+ normalizeRoles: function (roles) {
403
+ if (!roles) return [];
404
+ if (Array.isArray(roles)) return roles;
405
+ if (typeof roles === "string") {
406
+ try {
407
+ const parsed = JSON.parse(roles);
408
+ return Array.isArray(parsed) ? parsed : [parsed];
409
+ } catch (e) {
410
+ return roles
411
+ .split(/[,,]/)
412
+ .map((item) => item.trim())
413
+ .filter(Boolean);
414
+ }
415
+ }
416
+ return [roles];
417
+ },
418
+ normalizeRole: function (role, index) {
419
+ if (typeof role === "string") {
420
+ return {
421
+ key: role + index,
422
+ name: role,
423
+ };
424
+ }
425
+
426
+ const info = role?.role_info || role?.info || {};
427
+ const name =
428
+ role?.name ||
429
+ role?.title ||
430
+ role?.label ||
431
+ role?.role_name ||
432
+ role?.display_name ||
433
+ role?.remark ||
434
+ info?.name ||
435
+ info?.title ||
436
+ info?.label ||
437
+ info?.role_name ||
438
+ "";
439
+
440
+ return {
441
+ key: role?.id || role?.key || role?.val || name + index,
442
+ name,
443
+ server: role?.server || role?.role_server || role?.jx3_server || info?.server || info?.role_server || "",
444
+ url: this.normalizeLink(role?.url || role?.link || role?.href || info?.url || info?.link || ""),
445
+ icon: this.normalizeRoleIcon(role, info),
446
+ color: role?.color || info?.color || "",
447
+ backgroundColor: role?.background_color || role?.bg_color || info?.background_color || info?.bg_color || "",
448
+ borderColor: role?.border_color || info?.border_color || "",
449
+ };
450
+ },
451
+ normalizeRoleAsset: function (url) {
452
+ if (!url) return "";
453
+ url = String(url).trim();
454
+ if (/^(https?:)?\/\//.test(url)) return url;
455
+ return __cdn + url.replace(/^\/+/, "");
456
+ },
457
+ normalizeRoleIcon: function (role, info) {
458
+ const mount = role?.mount || role?.school || role?.school_id || info?.mount || info?.school || info?.school_id || "";
459
+ if (mount) return showSchoolIcon(mount);
460
+
461
+ return this.normalizeRoleAsset(role?.icon || role?.logo || role?.image || info?.icon || info?.logo || "");
462
+ },
463
+ normalizeLink: function (url) {
464
+ if (!url) return "";
465
+ url = String(url).trim();
466
+ if (/^(https?:)?\/\//.test(url)) return url;
467
+ return __Root + url.replace(/^\/+/, "");
468
+ },
469
+ roleStyle: function (role) {
470
+ return {
471
+ color: role.color || null,
472
+ backgroundColor: role.backgroundColor || null,
473
+ borderColor: role.borderColor || null,
474
+ };
475
+ },
304
476
  authorLink,
305
477
  },
306
478
  };
@@ -310,19 +482,31 @@ export default {
310
482
  @import "../assets/css/module/author.less";
311
483
  .w-author {
312
484
  .w-author-wrapper {
485
+ width: 300px;
486
+ min-width: 300px;
487
+ max-width: 300px;
488
+ padding: 12px;
489
+ border: 1px solid rgba(132, 146, 166, 0.28);
490
+ border-radius: 8px;
491
+ background-color: #fff;
313
492
  background-repeat: no-repeat;
314
493
  background-position: top right;
315
494
  background-size: 100% auto;
495
+ box-shadow: 0 14px 34px rgba(31, 41, 55, 0.16), 0 2px 8px rgba(31, 41, 55, 0.08);
316
496
  &.is-no-atcard {
317
- background-color: #f8fafc;
318
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
497
+ border-color: rgba(124, 134, 156, 0.34);
498
+ background: linear-gradient(180deg, #ffffff 0%, #f3f6fb 100%);
319
499
  }
320
500
  .u-author {
321
- padding: 5px 0 15px 5px;
501
+ padding: 4px 10px 12px 5px;
322
502
  }
323
503
  .u-avatar {
324
504
  .fl;
325
- .mr(15px);
505
+ .mr(12px);
506
+ .size(60px);
507
+ }
508
+ .u-info {
509
+ height: 60px;
326
510
  }
327
511
  img {
328
512
  border: none;
@@ -340,7 +524,7 @@ export default {
340
524
  .u-displayname {
341
525
  .fz(15px);
342
526
  .nobreak;
343
- max-width: 200px;
527
+ max-width: 190px;
344
528
  color: @color;
345
529
  .bold;
346
530
  }
@@ -356,5 +540,61 @@ export default {
356
540
  .mb(15px);
357
541
  .r(2px);
358
542
  }
543
+ .u-author-role {
544
+ .mt(8px);
545
+ }
546
+ .u-author-role-label {
547
+ margin: 0 0 8px 0;
548
+ height: 1px;
549
+ background-color: rgba(154, 167, 181, 0.28);
550
+ }
551
+ .u-author-roles {
552
+ display: flex;
553
+ flex-direction: column;
554
+ gap: 4px;
555
+ }
556
+ .u-author-role-item {
557
+ .flex(y);
558
+ gap: 7px;
559
+ max-width: 100%;
560
+ min-height: 21px;
561
+ color: #56677a;
562
+ font-size: 12px;
563
+ line-height: 21px;
564
+ box-sizing: border-box;
565
+ text-decoration: none;
566
+
567
+ &:hover {
568
+ .u-author-role-server {
569
+ color: #fba524;
570
+ }
571
+ }
572
+ }
573
+ .u-author-role-icon {
574
+ .size(20px);
575
+ flex-shrink: 0;
576
+ object-fit: contain;
577
+ }
578
+ .u-author-role-content {
579
+ min-width: 0;
580
+ display: flex;
581
+ align-items: baseline;
582
+ }
583
+ .u-author-role-name {
584
+ min-width: 0;
585
+ margin-right: 2px;
586
+ overflow: hidden;
587
+ text-overflow: ellipsis;
588
+ white-space: nowrap;
589
+ color: #42566d;
590
+ font-size: 12px;
591
+ font-weight: 400;
592
+ }
593
+ .u-author-role-server {
594
+ flex-shrink: 0;
595
+ color: #fba524;
596
+ font-size: 12px;
597
+ font-weight: 400;
598
+ }
359
599
  }
360
600
  </style>
@@ -34,6 +34,12 @@ function getDecorationV2(params) {
34
34
  });
35
35
  }
36
36
 
37
+ function getUserSkin(params) {
38
+ return $cms().get(`/api/cms/user/skin`, {
39
+ params,
40
+ });
41
+ }
42
+
37
43
  function getDecorationJson() {
38
44
  let url = __cdn + "design/decoration/index.json";
39
45
  return axios.get(url);
@@ -45,4 +51,4 @@ function getLetterPaper(params) {
45
51
  params,
46
52
  });
47
53
  }
48
- export { uploadFile, loadAuthors, loadEmotions, getDecoration, getDecorationV2, getDecorationJson, getLetterPaper };
54
+ export { uploadFile, loadAuthors, loadEmotions, getDecoration, getDecorationV2, getUserSkin, getDecorationJson, getLetterPaper };