@jx3box/jx3box-ui 2.3.2 → 2.3.4

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.
@@ -35,7 +35,10 @@
35
35
  }
36
36
 
37
37
  .u-share2-item {
38
- .dbi;
38
+ display: inline-flex;
39
+ flex-direction: column;
40
+ align-items: center;
41
+
39
42
  cursor: pointer;
40
43
  text-align: center;
41
44
  width: 55px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jx3box/jx3box-ui",
3
- "version": "2.3.2",
3
+ "version": "2.3.4",
4
4
  "description": "JX3BOX Vue3 UI",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/service/cms.js CHANGED
@@ -19,6 +19,11 @@ function getDecoration(params) {
19
19
  params,
20
20
  });
21
21
  }
22
+ function getDecorationV2(params) {
23
+ return $cms().get(`/api/cms/user/decoration/v2`, {
24
+ params,
25
+ });
26
+ }
22
27
  function getDecorationJson() {
23
28
  let url = JX3BOX.__cdn + "design/decoration/index.json";
24
29
  return axios.get(url);
@@ -143,6 +148,7 @@ export {
143
148
  uploadImage,
144
149
  upload,
145
150
  getDecoration,
151
+ getDecorationV2,
146
152
  getDecorationJson,
147
153
  checkTeamMember,
148
154
  getSliders,
package/src/App.vue CHANGED
@@ -18,8 +18,8 @@
18
18
  <AdminDrop :post="community" :isCommunity="false" :user-id="8" :showMove="true" />
19
19
  </template>
20
20
  </breadcrumb>
21
- <LeftSidebar :open="true" :uid="8">
22
- <Author :uid="8" />
21
+ <LeftSidebar :open="true" :uid="8719">
22
+ <Author :uid="8719" />
23
23
  </LeftSidebar>
24
24
  <Main :withoutLeft="false" :withoutRight="false">
25
25
  <el-tabs v-model="tab" type="card">
@@ -232,7 +232,8 @@ export default {
232
232
  return {
233
233
  tab: "content",
234
234
 
235
- post_id: "19382",
235
+ // post_id: "19382",
236
+ post_id: "99808",
236
237
  post: {},
237
238
  client: location.href.includes("origin") ? "origin" : "std",
238
239
  item1: null,
@@ -7,7 +7,7 @@
7
7
  'without-bread': withoutBread,
8
8
  }"
9
9
  v-if="!isApp"
10
- :style="{ backgroundImage: `url(${bg})` }"
10
+ :style="decorationStyles"
11
11
  >
12
12
  <div class="c-sidebar-left-inner">
13
13
  <slot></slot>
@@ -29,10 +29,23 @@
29
29
  <script>
30
30
  import Bus from "../utils/bus";
31
31
  import { isApp } from "../assets/js/app.js";
32
- import { getDecoration } from "../service/cms";
32
+ import { getDecorationV2 } from "../service/cms";
33
33
  import JX3BOX from "@jx3box/jx3box-common/data/jx3box.json";
34
34
  import i18nMixin from "../i18n/mixin";
35
- const DECORATION_SIDEBAR = "decoration_sidebar";
35
+ const { __cdn } = JX3BOX;
36
+ const DECORATION_SIDEBAR = "decoration_sidebar_v2_";
37
+ const DECORATION_EMPTY_VALUE = "no";
38
+ const DECORATION_POSITION_MAP = {
39
+ lt: "left top",
40
+ ct: "center top",
41
+ rt: "right top",
42
+ lc: "left center",
43
+ cc: "center center",
44
+ rc: "right center",
45
+ lb: "left bottom",
46
+ cb: "center bottom",
47
+ rb: "right bottom",
48
+ };
36
49
  export default {
37
50
  name: "LeftSidebar",
38
51
  mixins: [i18nMixin],
@@ -43,12 +56,21 @@ export default {
43
56
  user_id: null,
44
57
  isApp: isApp(),
45
58
  bg: "",
59
+ decorationPosition: "",
46
60
  };
47
61
  },
48
62
  computed: {
49
63
  stickyHeader: function () {
50
64
  return this.withoutBread;
51
65
  },
66
+ decorationStyles() {
67
+ return this.bg
68
+ ? {
69
+ backgroundImage: `url(${this.bg})`,
70
+ backgroundPosition: this.decorationPosition,
71
+ }
72
+ : null;
73
+ },
52
74
  },
53
75
  watch: {
54
76
  open: function (newval) {
@@ -64,32 +86,69 @@ export default {
64
86
  let status = !this.isOpen;
65
87
  Bus.emit("toggleLeftSide", status);
66
88
  },
67
- showDecoration: function (val, type) {
68
- return JX3BOX.__cdn + `design/decoration/images/${val}/${type}.png`;
89
+ resolveDecorationDetail(decoration) {
90
+ if (!decoration) {
91
+ return null;
92
+ }
93
+
94
+ const decorations = Array.isArray(decoration.decorations) ? decoration.decorations : [];
95
+ return decorations.find((item) => item && item.image) || null;
96
+ },
97
+ normalizeDecorationImage(image) {
98
+ if (!image) {
99
+ return "";
100
+ }
101
+
102
+ let url = String(image).trim();
103
+ if (/^(https?:)?\/\//.test(url)) {
104
+ return url;
105
+ }
106
+
107
+ return __cdn + url.replace(/^\/+/, "");
108
+ },
109
+ resolveDecorationPosition(position) {
110
+ return DECORATION_POSITION_MAP[position] || position || "";
111
+ },
112
+ setDecoration(decoration) {
113
+ const decorationDetail = this.resolveDecorationDetail(decoration);
114
+ const image = this.normalizeDecorationImage(decorationDetail?.image);
115
+ if (!image) {
116
+ return false;
117
+ }
118
+
119
+ this.bg = image;
120
+ this.decorationPosition = this.resolveDecorationPosition(decorationDetail.position);
121
+ return true;
69
122
  },
70
123
  getDecoration() {
71
124
  if (!this.user_id) {
72
125
  return;
73
126
  }
74
127
  let decoration_sidebar = sessionStorage.getItem(DECORATION_SIDEBAR + this.user_id) || "";
75
- if (decoration_sidebar == "no") return;
128
+ if (decoration_sidebar == DECORATION_EMPTY_VALUE) return;
76
129
  //已有缓存,读取解析
77
- try {
78
- let sidebar = JSON.parse(decoration_sidebar);
79
- this.bg = this.showDecoration(sidebar.val, "sidebar");
80
- } catch (err) {
81
- getDecoration({ using: 1, user_id: this.user_id, type: "sidebar" }).then((data) => {
82
- let res = data.data.data || [];
83
- if (res.length == 0) {
84
- //空 则为无主题,不再加载接口,界面设No
85
- sessionStorage.setItem(DECORATION_SIDEBAR + this.user_id, "no");
130
+ if (decoration_sidebar) {
131
+ try {
132
+ let sidebar = JSON.parse(decoration_sidebar);
133
+ if (sidebar && this.setDecoration(sidebar)) {
86
134
  return;
87
135
  }
88
- let sidebar = res[0];
89
- this.bg = this.showDecoration(sidebar.val, "sidebar");
90
- sessionStorage.setItem(DECORATION_SIDEBAR + this.user_id, JSON.stringify(sidebar));
91
- });
136
+ sessionStorage.removeItem(DECORATION_SIDEBAR + this.user_id);
137
+ } catch (err) {
138
+ sessionStorage.removeItem(DECORATION_SIDEBAR + this.user_id);
139
+ }
92
140
  }
141
+ getDecorationV2({ using: 1, user_id: this.user_id, type: "sidebar", subtype: "pc_sidebar" }).then((data) => {
142
+ let res = data.data.data || [];
143
+ let sidebar = res.find((item) => item.type == "sidebar" && this.resolveDecorationDetail(item));
144
+ if (sidebar && this.setDecoration(sidebar)) {
145
+ sessionStorage.setItem(DECORATION_SIDEBAR + this.user_id, JSON.stringify(sidebar));
146
+ return;
147
+ }
148
+
149
+ //空 则为无主题,不再加载接口,界面设No
150
+ sessionStorage.setItem(DECORATION_SIDEBAR + this.user_id, DECORATION_EMPTY_VALUE);
151
+ });
93
152
  },
94
153
  },
95
154
  beforeUnmount() {
@@ -217,6 +217,7 @@ export default {
217
217
  .break(3);
218
218
  color: #888;
219
219
  padding: 0 5px;
220
+ white-space: pre-line;
220
221
  }
221
222
  .u-extend {
222
223
  display: flex;
@@ -105,6 +105,11 @@ export default {
105
105
  },
106
106
  },
107
107
  emits: ["update:modelValue"],
108
+ computed: {
109
+ client() {
110
+ return location.href.includes("origin") ? "origin" : "std";
111
+ },
112
+ },
108
113
  data() {
109
114
  return {
110
115
  form: {
@@ -149,6 +154,8 @@ export default {
149
154
  if (User.isTeammate()) {
150
155
  this.loadConfig();
151
156
  }
157
+
158
+ this.form.version = this.client;
152
159
  }
153
160
  },
154
161
  },
@@ -73,11 +73,23 @@ import CommentContent from "./CommentContent.vue";
73
73
  import ReplyList from "./ReplyList.vue";
74
74
  import { POST, DELETE, GET } from "../../service/comment";
75
75
  import CommentAvatar from "../comment/Avatar.vue";
76
- import { getDecoration } from "../../service/cms";
76
+ import { getDecorationV2 } from "../../service/cms";
77
77
  import JX3BOX from "@jx3box/jx3box-common/data/jx3box.json";
78
78
  import i18nMixin from "../../i18n/mixin";
79
- const { __imgPath } = JX3BOX;
79
+ const { __cdn } = JX3BOX;
80
80
  const DECORATION_KEY = "decoration_comment_";
81
+ const DECORATION_EMPTY_VALUE = "no";
82
+ const DECORATION_POSITION_MAP = {
83
+ lt: "left top",
84
+ ct: "center top",
85
+ rt: "right top",
86
+ lc: "left center",
87
+ cc: "center center",
88
+ rc: "right center",
89
+ lb: "left bottom",
90
+ cb: "center bottom",
91
+ rb: "right bottom",
92
+ };
81
93
  export default {
82
94
  mixins: [i18nMixin],
83
95
  props: {
@@ -122,6 +134,7 @@ export default {
122
134
  total: 0,
123
135
  },
124
136
  decoration: null,
137
+ decorationPosition: "",
125
138
  };
126
139
  },
127
140
  computed: {
@@ -132,6 +145,7 @@ export default {
132
145
  return this.decoration
133
146
  ? {
134
147
  backgroundImage: `url(${this.decoration})`,
148
+ backgroundPosition: this.decorationPosition,
135
149
  borderRadius: "8px",
136
150
  }
137
151
  : null;
@@ -142,29 +156,65 @@ export default {
142
156
  this.getDecoration();
143
157
  },
144
158
  methods: {
159
+ resolveDecorationDetail(decoration) {
160
+ if (!decoration) {
161
+ return null;
162
+ }
163
+
164
+ const decorations = Array.isArray(decoration.decorations) ? decoration.decorations : [];
165
+ return decorations.find((item) => item && item.image) || null;
166
+ },
167
+ normalizeDecorationImage(image) {
168
+ if (!image) {
169
+ return "";
170
+ }
171
+
172
+ let url = String(image).trim();
173
+ if (/^(https?:)?\/\//.test(url)) {
174
+ return url;
175
+ }
176
+
177
+ return __cdn + url.replace(/^\/+/, "");
178
+ },
179
+ resolveDecorationPosition(position) {
180
+ return DECORATION_POSITION_MAP[position] || position || "";
181
+ },
145
182
  setDecoration(decoration) {
146
- this.decoration = __imgPath + `decoration/images/${decoration.val}/comment.png`;
183
+ const decorationDetail = this.resolveDecorationDetail(decoration);
184
+ const image = this.normalizeDecorationImage(decorationDetail?.image);
185
+ if (!image) {
186
+ return false;
187
+ }
188
+
189
+ this.decoration = image;
190
+ this.decorationPosition = this.resolveDecorationPosition(decorationDetail.position);
191
+ return true;
147
192
  },
148
193
  getDecoration() {
149
194
  let decoration_local = sessionStorage.getItem(DECORATION_KEY + this.uid);
195
+ if (decoration_local == DECORATION_EMPTY_VALUE) return;
150
196
  if (decoration_local) {
151
- //解析本地缓存
152
- let decoration_parse = JSON.parse(decoration_local);
153
- if (decoration_parse) {
154
- this.setDecoration(decoration_parse);
155
- this.decoration = __imgPath + `decoration/images/${decoration_parse.val}/comment.png`;
156
- return;
197
+ try {
198
+ let decoration_parse = JSON.parse(decoration_local);
199
+ if (decoration_parse && this.setDecoration(decoration_parse)) {
200
+ return;
201
+ }
202
+ sessionStorage.removeItem(DECORATION_KEY + this.uid);
203
+ } catch (err) {
204
+ sessionStorage.removeItem(DECORATION_KEY + this.uid);
157
205
  }
158
206
  }
159
- getDecoration({ using: 1, user_id: this.uid, type: "comment" }).then((res) => {
160
- let decorationList = res.data.data;
161
- //筛选个人装扮
162
- let decoration = decorationList.find((item) => item.type == "comment");
163
- if (decoration) {
164
- this.decoration = __imgPath + `decoration/images/${decoration.val}/comment.png`;
207
+ getDecorationV2({ using: 1, user_id: this.uid, type: "comment", subtype: "pc_comment" }).then((res) => {
208
+ let decorationList = res.data.data || [];
209
+ let decoration = decorationList.find(
210
+ (item) => item.type == "comment" && this.resolveDecorationDetail(item)
211
+ );
212
+ if (decoration && this.setDecoration(decoration)) {
165
213
  sessionStorage.setItem(DECORATION_KEY + this.uid, JSON.stringify(decoration));
166
214
  return;
167
215
  }
216
+
217
+ sessionStorage.setItem(DECORATION_KEY + this.uid, DECORATION_EMPTY_VALUE);
168
218
  });
169
219
  },
170
220
  profileLink: function (uid) {
@@ -19,23 +19,15 @@
19
19
  <div class="w-boxcoin-admin-content">
20
20
  <div class="u-left u-total-left">
21
21
  <em class="u-label">{{ $jx3boxT("jx3boxUi.boxcoinAdmin.yearStatus", "全年额度") }}</em>
22
- {{
23
- $jx3boxT("jx3boxUi.boxcoinAdmin.yearSummary", "已用{used} 剩余{left} 总计{total}", {
24
- used: this.postTypeUsed,
25
- left: this.postTypeLeft,
26
- total: this.totalLimit,
27
- })
28
- }}
22
+ {{ $jx3boxT("jx3boxUi.boxcoinAdmin.used", "已用") }}<b>{{ postTypeUsed }}</b>
23
+ {{ $jx3boxT("jx3boxUi.boxcoinAdmin.left", "剩余") }}<b>{{ postTypeLeft }}</b>
24
+ {{ $jx3boxT("jx3boxUi.boxcoinAdmin.total", "总计") }}<b>{{ totalLimit }}</b>
29
25
  </div>
30
26
  <div class="u-left">
31
27
  <em class="u-label">{{ $jx3boxT("jx3boxUi.boxcoinAdmin.monthStatus", "本月状态") }}</em>
32
- {{
33
- $jx3boxT("jx3boxUi.boxcoinAdmin.monthSummary", "已用{used} 剩余{left} 总计{total}", {
34
- used: this.used,
35
- left: this.left,
36
- total: this.total,
37
- })
38
- }}
28
+ {{ $jx3boxT("jx3boxUi.boxcoinAdmin.used", "已用") }}<b>{{ used }}</b>
29
+ {{ $jx3boxT("jx3boxUi.boxcoinAdmin.left", "剩余") }}<b>{{ left }}</b>
30
+ {{ $jx3boxT("jx3boxUi.boxcoinAdmin.total", "总计") }}<b>{{ total }}</b>
39
31
  <el-progress
40
32
  :percentage="this.total ? 100 - (this.used * 100) / this.total : 0"
41
33
  :stroke-width="15"
@@ -44,7 +36,9 @@
44
36
  </div>
45
37
  <div class="u-list">
46
38
  <em class="u-label">❤️ {{ $jx3boxT("jx3boxUi.boxcoinAdmin.appraise", "品鉴") }}</em>
47
- <Contributors v-if="authors && authors.length" :authors="authors" @chosen="handleChosen" />
39
+ <div class="u-contributors" v-if="authors && authors.length">
40
+ <Contributors :authors="authors" @chosen="handleChosen" />
41
+ </div>
48
42
  <div class="u-points">
49
43
  <el-radio-group v-model="count">
50
44
  <el-radio :value="item" v-for="item in fitPoints" :key="item" border>
@@ -319,4 +313,14 @@ export default {
319
313
  margin-left: 10px;
320
314
  }
321
315
  }
316
+
317
+ .w-boxcoin-admin-content {
318
+ .u-total-left {
319
+ .mb(10px);
320
+ }
321
+
322
+ .u-contributors {
323
+ .mt(20px);
324
+ }
325
+ }
322
326
  </style>
@@ -248,5 +248,13 @@ export default {
248
248
  .underline(var(--el-color-primary));
249
249
  .ml(10px);
250
250
  }
251
+
252
+
253
+ }
254
+ .w-boxcoin-user-content {
255
+ .m-contributor {
256
+ .mt(20px);
257
+ }
258
+
251
259
  }
252
260
  </style>
@@ -24,7 +24,6 @@
24
24
  <boxcoin-admin
25
25
  :postId="postId"
26
26
  :postType="postType"
27
- v-if="hasRight && adminBoxcoinEnable && boxcoin_enable && hasPermission"
28
27
  :userId="userId"
29
28
  :max="admin_max"
30
29
  :min="admin_min"
@@ -0,0 +1,56 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ const root = path.resolve(__dirname, "..");
5
+ const service = fs.readFileSync(path.join(root, "service/cms.js"), "utf8");
6
+ const component = fs.readFileSync(path.join(root, "src/comment/CommentWithReply.vue"), "utf8");
7
+ const leftSidebar = fs.readFileSync(path.join(root, "src/LeftSidebar.vue"), "utf8");
8
+ const app = fs.readFileSync(path.join(root, "src/App.vue"), "utf8");
9
+
10
+ function assert(condition, message) {
11
+ if (!condition) {
12
+ throw new Error(message);
13
+ }
14
+ }
15
+
16
+ assert(service.includes("/api/cms/user/decoration/v2"), "getDecoration should support the decoration v2 endpoint");
17
+
18
+ assert(component.includes("getDecorationV2"), "comment decoration should request the v2 decoration endpoint");
19
+
20
+ assert(component.includes('subtype: "pc_comment"'), "comment decoration should request the pc_comment subtype");
21
+
22
+ assert(component.includes("resolveDecorationDetail"), "comment decoration should resolve v2 decorations payload");
23
+
24
+ assert(component.includes("normalizeDecorationImage"), "comment decoration should normalize returned image URLs");
25
+
26
+ assert(
27
+ !component.includes("decoration/images/${decoration.val}/comment.png"),
28
+ "comment decoration should not depend on the old val/comment.png path"
29
+ );
30
+
31
+ assert(
32
+ !/\bdecoration\.decoration\b/.test(component),
33
+ "comment decoration should not support legacy decoration payloads"
34
+ );
35
+
36
+ assert(!component.includes("markdownMatch"), "comment decoration should not support markdown-link compatibility");
37
+
38
+ assert(!component.includes("decoration.image"), "comment decoration should not fall back to legacy top-level image");
39
+
40
+ assert(leftSidebar.includes("getDecorationV2"), "left sidebar decoration should request the v2 decoration endpoint");
41
+
42
+ assert(leftSidebar.includes('subtype: "pc_sidebar"'), "left sidebar decoration should request the pc_sidebar subtype");
43
+
44
+ assert(leftSidebar.includes("resolveDecorationDetail"), "left sidebar decoration should resolve v2 decorations payload");
45
+
46
+ assert(
47
+ !leftSidebar.includes("design/decoration/images/${val}/${type}.png"),
48
+ "left sidebar decoration should not depend on the old val/sidebar.png path"
49
+ );
50
+
51
+ assert(
52
+ app.includes('<LeftSidebar :open="true" :uid="8719">') && app.includes('<Author :uid="8719" />'),
53
+ "local author fixture should pass the same 8719 uid to LeftSidebar and Author"
54
+ );
55
+
56
+ console.log("decoration v2 checks passed");