@jx3box/jx3box-editor 1.8.6 → 1.8.8

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.
@@ -23,6 +23,7 @@
23
23
  @import "tinymce/macro.less";
24
24
  @import "tinymce/qixue.less";
25
25
  @import "tinymce/pz.less";
26
+ @import "tinymce/combo.less";
26
27
 
27
28
  @import "module/directory.less";
28
29
  @import "module/icon.less";
@@ -0,0 +1,311 @@
1
+ .scrollbar() {
2
+ &::-webkit-scrollbar {
3
+ width: 4px;
4
+ }
5
+ &::-webkit-scrollbar-track,
6
+ &::-webkit-scrollbar-track-piece {
7
+ background-color: #fafafa;
8
+ border-radius: 6px;
9
+ }
10
+ &::-webkit-scrollbar-thumb {
11
+ background-color: #eee;
12
+ border-radius: 6px;
13
+ }
14
+ &::-webkit-scrollbar-button,
15
+ &::-webkit-scrollbar-corner,
16
+ &::-webkit-resizer {
17
+ display: none;
18
+ }
19
+ }
20
+
21
+ .m-resource-combo {
22
+ .m-combo-list {
23
+ max-height: 230px;
24
+ overflow: auto;
25
+ .scrollbar();
26
+ }
27
+ .m-database-search {
28
+ position: sticky;
29
+ z-index: 100;
30
+ top: 0;
31
+ // padding: 10px 20px 10px 20px;
32
+ background-color: #fff;
33
+ .pr;
34
+
35
+ .u-client {
36
+ // .pa;.lt(20px,10px);
37
+ margin-right: 15px;
38
+ }
39
+ .u-input {
40
+ flex: 1;
41
+ }
42
+ display: flex;
43
+ }
44
+
45
+ .m-database-tip {
46
+ .x;
47
+ // .fz(12px);
48
+ color: #aaa;
49
+ margin: 80px auto;
50
+ // font-weight:300;
51
+ letter-spacing: 1px;
52
+ min-height: 120px;
53
+ }
54
+
55
+ // 加载更多
56
+ .m-archive-more {
57
+ .none;
58
+ margin: 0 20px 10px 20px;
59
+ box-sizing: border-box;
60
+ width: calc(100% - 40px);
61
+ }
62
+ .m-archive-more.show {
63
+ .db;
64
+ }
65
+
66
+ // 分页
67
+ .m-archive-pages {
68
+ overflow-x: auto;
69
+ margin: 0 20px;
70
+ .x;
71
+ }
72
+
73
+ .m-resource-count {
74
+ .mt(0);
75
+ .mb(10px);
76
+
77
+ .lh(28px);
78
+
79
+ .pr;
80
+ }
81
+
82
+ .m-resource-list {
83
+ .mt(10px);
84
+ padding: 0;
85
+ margin: 0;
86
+ .scrollbar();
87
+
88
+ .u-item {
89
+ .db;
90
+ border: 2px solid @border-hr;
91
+ background: #fafbfc;
92
+ margin-bottom: 10px;
93
+ padding: 10px 10px 10px 70px;
94
+ min-height: 60px;
95
+ .r(6px);
96
+ &:hover {
97
+ background-color: #e7f9ff;
98
+ }
99
+ &.on {
100
+ border-color: #34d058;
101
+ background-color: #fff;
102
+ box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
103
+ // &:hover{
104
+ // border:1px dashed #ddd;
105
+ // background-color:#eee;
106
+ // }
107
+ }
108
+ .pr;
109
+ .pointer;
110
+ }
111
+ .u-link {
112
+ .db;
113
+ color: @color;
114
+ }
115
+ .u-pic {
116
+ .db;
117
+ .pa;
118
+ .lt(10px);
119
+ .size(48px);
120
+ .r(6px);
121
+ box-shadow: 0 0 1px inset rgba(0, 0, 0, 0.2);
122
+ }
123
+ .u-id {
124
+ .fz(10px);
125
+ color: #555;
126
+ .pa;
127
+ .rt(10px);
128
+ .x(right);
129
+ }
130
+ .u-detach {
131
+ .mt(4px);
132
+ .db;
133
+ }
134
+ .u-primary {
135
+ .pr;
136
+ }
137
+ .u-raw {
138
+ .pa;
139
+ .rb(0);
140
+ }
141
+ .u-name {
142
+ .fz(14px, 2);
143
+ .bold;
144
+ .db;
145
+ em {
146
+ .fz(12px);
147
+ color: #999;
148
+ font-weight: normal;
149
+ font-style: normal;
150
+ }
151
+ }
152
+ .u-content {
153
+ .fz(13px, 1.8);
154
+ .mb(5px);
155
+ .db;
156
+
157
+ .u-map,
158
+ .u-life {
159
+ .mr(20px);
160
+ }
161
+ }
162
+ .u-remark {
163
+ .fz(12px, 1.8);
164
+ color: #aaa;
165
+ .db;
166
+
167
+ em {
168
+ background-color: #eee;
169
+ padding: 0 5px;
170
+ margin: 0 3px;
171
+ .r(3px);
172
+ }
173
+ }
174
+ .u-desc {
175
+ .fz(12px, 1.8);
176
+ .db;
177
+ white-space: pre-wrap;
178
+ margin: 4px 0;
179
+
180
+ em {
181
+ background-color: #f3f3f3;
182
+ padding: 2px 5px;
183
+ margin-right: 5px;
184
+ color: #999;
185
+ .r(3px);
186
+ .fz(12px);
187
+ font-style: normal;
188
+ }
189
+
190
+ sub {
191
+ font-size: 100%;
192
+ .ps;
193
+ }
194
+ }
195
+ .u-props {
196
+ .mt(10px);
197
+ border-top: 1px dashed #ddd;
198
+ padding: 10px 0;
199
+ .clearfix;
200
+ .u-desc {
201
+ .w(50%);
202
+ .fl;
203
+ }
204
+ .none;
205
+ &.on {
206
+ .db;
207
+ }
208
+ }
209
+ }
210
+ .m-select-content {
211
+ height: calc(100% - 200px);
212
+ overflow: auto;
213
+ }
214
+ // 已选数据
215
+ .m-selected-skills {
216
+ height: 70px;
217
+ border: 1px solid #ddd;
218
+ padding: 10px;
219
+ .scrollbar();
220
+ overflow-y: auto;
221
+ .m-skills-list {
222
+ .flex;
223
+ gap: 30px;
224
+ list-style: none;
225
+ padding: 0;
226
+ margin: 0;
227
+ .flex;
228
+ flex-wrap: wrap;
229
+ }
230
+ .m-skill {
231
+ // position: relative;
232
+ // display: flex;
233
+ // justify-content: center;
234
+ .pr;
235
+
236
+ &:not(:last-of-type)::after {
237
+ content: "▲";
238
+ .pa;
239
+ right: -25px;
240
+ top: 15px;
241
+ transform: rotate(90deg);
242
+ }
243
+
244
+ .u-remove-icon {
245
+ .pa;
246
+ right: -4px;
247
+ top: -4px;
248
+ cursor: pointer;
249
+ // .none;
250
+
251
+ background-color:#c00;
252
+ .size(12px);
253
+ .r(50%);
254
+ .db;
255
+ i{
256
+ color:#fff;
257
+ .fz(12px);
258
+ .db;
259
+ }
260
+ &:hover {
261
+ background-color:#f00;
262
+ }
263
+ }
264
+
265
+ .u-gcd-icon {
266
+ .pa;
267
+ // 左上角
268
+ left: -4px;
269
+ top: -4px;
270
+ // background-color: #fcf003;
271
+ background-color: #0366d6;
272
+ .size(12px);
273
+ .r(50%);
274
+ .db;
275
+
276
+ i{
277
+ color:#fff;
278
+ .fz(12px);
279
+ .db;
280
+ }
281
+ }
282
+
283
+ .u-skill-icon {
284
+ .size(48px, 48px);
285
+ }
286
+
287
+ .u-name {
288
+ display: block;
289
+ font-size: 12px;
290
+ text-align: center;
291
+ overflow: hidden;
292
+ white-space: nowrap;
293
+ width: 48px;
294
+ }
295
+
296
+ .u-mask {
297
+ .pa;
298
+ .lt(0);
299
+ .size(48px, 48px);
300
+ background-color: rgba(0, 0, 0, 0.5);
301
+ .r(6px);
302
+ .none;
303
+ .pointer;
304
+ }
305
+
306
+ &:hover {
307
+ cursor: move;
308
+ }
309
+ }
310
+ }
311
+ }
@@ -0,0 +1,74 @@
1
+ .w-skill-combo {
2
+ .flex;
3
+ gap: 20px;
4
+ flex-wrap: wrap;
5
+ }
6
+ .w-skill-combo-item {
7
+ .pr;
8
+ .flex;
9
+ flex-direction: column;
10
+ align-items: center;
11
+ &:not(:last-of-type)::after {
12
+ content: "▲";
13
+ .pa;
14
+ right: -20px;
15
+ top: 5px;
16
+ transform: rotate(90deg);
17
+ }
18
+
19
+ img {
20
+ border: none;
21
+ padding: 0;
22
+ margin: 0;
23
+ }
24
+
25
+ .u-gcd-icon {
26
+ .pa;
27
+ // 左上角
28
+ left: -4px;
29
+ top: -4px;
30
+ background-color: #0366d6;
31
+ .size(12px);
32
+ .r(50%);
33
+ .db;
34
+
35
+ i {
36
+ color: #fff;
37
+ .fz(12px);
38
+ .db;
39
+ }
40
+ }
41
+ .u-skill-name {
42
+ .fz(13px,1.5);
43
+ width: 56px;
44
+ overflow: hidden;
45
+ text-overflow: clip;
46
+ white-space: pre-wrap;
47
+ text-align: center;
48
+ }
49
+ }
50
+
51
+ .e-skill-combo{
52
+ font-size: 14px;
53
+ width: 100%;
54
+ box-sizing: border-box;
55
+ background-color: #f1f8ff;
56
+ border: 1px solid #c8e1ff;
57
+ color: #62a9ff;
58
+ padding: 10px;
59
+ font-family: Monaco, Consolas, "Lucida Console", "Courier New", serif;
60
+ position: relative;
61
+ *zoom: 1;
62
+ border-radius: 3px;
63
+ }
64
+ .e-skill-combo:after {
65
+ content: "JX3BOX·连招套路";
66
+ position: absolute;
67
+ right: 8px;
68
+ top: 8px;
69
+ background-color: #62a9ff;
70
+ color: #fff;
71
+ border-radius: 3px;
72
+ padding: 2px 8px;
73
+ line-height: 21px;
74
+ }
@@ -39,7 +39,6 @@
39
39
 
40
40
  .c-editor-tinymce {
41
41
 
42
-
43
42
  .u-tutorial {
44
43
  padding:0 10px;
45
44
  .mt(10px);
@@ -0,0 +1,33 @@
1
+ import $ from "jquery";
2
+ import { iconLink } from "@jx3box/jx3box-common/js/utils";
3
+
4
+ function renderCombo(selector = ".e-skill-combo .w-skill-combo-item") {
5
+ try {
6
+ let html = ''
7
+ $(selector).each(function(i, ele) {
8
+ // 获取嵌入源地址
9
+ let url = $(this).text();
10
+
11
+ const [id, name, icon, extend] = url.split(",");
12
+
13
+ // 渲染
14
+ let code = `
15
+ <span class="w-skill-combo-item">
16
+ <img class="u-skill-icon" src="${iconLink(icon)}" alt="${icon}" title="${name}" />
17
+ <span class="u-skill-name" title="${name}">${name}</span>
18
+ <i class="u-gcd-icon ${extend && extend.gcd ? 'is-show' : ''}" title="无GCD技能">
19
+ <i class="el-icon-time"></i>
20
+ </i>
21
+ </span>
22
+ `;
23
+ html += code;
24
+ });
25
+
26
+ // 挂载点
27
+ $(selector).parent().html(html);
28
+ } catch(e) {
29
+ console.log(e)
30
+ }
31
+ }
32
+
33
+ export default renderCombo;
@@ -10,7 +10,7 @@ function talent2(selector = ".e-jx3talent2-area") {
10
10
  // 内容解析
11
11
  let talent = $(this).text();
12
12
  let code = Base64.encode(talent);
13
- let url = "https://oss.jx3box.com/static/jx3box-talent2/index.html" + "?code=" + code;
13
+ let url = "https://cdn.jx3box.com/static/jx3box-talent2/index.html" + "?code=" + code;
14
14
 
15
15
  container.html(`<iframe src="${url}" width="100%" height="100%" style="overflow:hidden;border:none;"></iframe>`);
16
16
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jx3box/jx3box-editor",
3
- "version": "1.8.6",
3
+ "version": "1.8.8",
4
4
  "description": "JX3BOX Article & Editor",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -51,9 +51,12 @@
51
51
  "lodash": "^4.17.15",
52
52
  "photoswipe": "^4.1.2",
53
53
  "prismjs": "^1.20.0",
54
+ "sortablejs": "^1.15.0",
54
55
  "vue": "^2.6.11",
56
+ "vue-contextmenujs": "^1.4.9",
55
57
  "vue-gallery-slideshow": "^1.5.2",
56
58
  "vue-photoswipe.js": "^2.0.23",
59
+ "vue-plugin-load-script": "^1.3.6",
57
60
  "xss": "^1.0.8"
58
61
  },
59
62
  "devDependencies": {
@@ -7,7 +7,7 @@
7
7
  <meta name="renderer" content="webkit" />
8
8
  <title><%= htmlWebpackPlugin.options.title %></title>
9
9
  <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
10
- <script src="https://oss.jx3box.com/static/tinymce/tinymce.min.js?v=0.1.0"></script>
10
+ <script src="https://oss.jx3box.com/static/tinymce/tinymce.min.js?v=1.8.8"></script>
11
11
  <!-- TODO: -->
12
12
  <!-- <script src="http://localhost:3000/tinymce.min.js"></script> -->
13
13
  </head>
@@ -17,4 +17,15 @@ function getResource(type, ids, client = "std") {
17
17
  return $.post(`/resource/${client}/${type}/`, { ids });
18
18
  }
19
19
 
20
- export { getResource };
20
+ function getSkill(query, params) {
21
+ let condition = isNaN(query) ? "name" : "id";
22
+ return $node().get(`/skill/${condition}/${query}`, {
23
+ params: params,
24
+ }).then(res => {
25
+ return res.data;
26
+ }).catch((err) => {
27
+ console.log(err);
28
+ });
29
+ }
30
+
31
+ export { getResource, getSkill };
package/src/Article.vue CHANGED
@@ -65,6 +65,7 @@ import renderKatex from "../assets/js/katex";
65
65
  import renderCode from "../assets/js/code";
66
66
  import renderImgPreview from "../assets/js/renderImgPreview";
67
67
  import renderPzIframe from "../assets/js/pz_iframe";
68
+ import renderCombo from "../assets/js/combo";
68
69
 
69
70
  // 剑三
70
71
  import Item from "./Item";
@@ -183,6 +184,8 @@ export default {
183
184
  renderJx3Element(this);
184
185
  // 配装
185
186
  renderPzIframe();
187
+ // 连招
188
+ renderCombo();
186
189
  },
187
190
  doDir: function () {
188
191
  // 显示局部
@@ -1,63 +1,152 @@
1
1
  <template>
2
2
  <div class="c-resource c-resource__jx3box">
3
3
  <!-- 上传触发按钮 -->
4
- <el-button class="u-switch" type="primary" @click="openDialog" :disabled="!enable"> <img class="u-icon" svg-inline :src="boxIcon" />魔盒资源 </el-button>
4
+ <el-button
5
+ class="u-switch"
6
+ type="primary"
7
+ @click="openDialog"
8
+ :disabled="!enable"
9
+ >
10
+ <img class="u-icon" svg-inline :src="boxIcon" />魔盒资源
11
+ </el-button>
5
12
 
6
13
  <!-- 弹出界面 -->
7
- <el-dialog class="c-large-dialog" title="魔盒资源库" :visible.sync="dialogVisible">
14
+ <el-dialog
15
+ class="c-large-dialog"
16
+ title="魔盒资源库"
17
+ :visible.sync="dialogVisible"
18
+ >
8
19
  <div class="c-resource-content" v-loading="loading">
9
20
  <div class="m-database-search">
10
- <el-input class="u-input" :placeholder="placeholderText" v-model="query" @change="search" @keyup.enter.native="search">
21
+ <el-radio-group
22
+ class="u-client"
23
+ v-model="comboClient"
24
+ @change="search"
25
+ v-if="type === 'combo'"
26
+ >
27
+ <el-radio-button label="std">重制</el-radio-button>
28
+ <el-radio-button label="origin">缘起</el-radio-button>
29
+ </el-radio-group>
30
+ <el-input
31
+ class="u-input"
32
+ :placeholder="placeholderText"
33
+ v-model="query"
34
+ @change="search"
35
+ @keyup.enter.native="search"
36
+ >
11
37
  <template slot="prepend">关键词</template>
12
38
  </el-input>
13
39
  </div>
14
40
 
15
- <el-tabs class="m-database-tabs" v-model="type" type="card" @tab-click="changeType">
41
+ <el-tabs
42
+ class="m-database-tabs"
43
+ v-model="type"
44
+ type="card"
45
+ @tab-click="changeType"
46
+ >
16
47
  <el-tab-pane label="魔盒用户" name="authors">
17
48
  <span slot="label" class="u-tab-label">
18
- <i class="el-icon-s-custom" style="margin-right:5px;"></i>
49
+ <i
50
+ class="el-icon-s-custom"
51
+ style="margin-right: 5px"
52
+ ></i>
19
53
  <b>用户</b>
20
54
  <i class="u-lv-box">Lv2+</i>
21
55
  </span>
22
56
  <p v-if="total && done" class="m-resource-count">
23
- <i class="el-icon-s-data"></i> 共找到 <b>{{ total }}</b> 条记录
57
+ <i class="el-icon-s-data"></i> 共找到
58
+ <b>{{ total }}</b> 条记录
24
59
  </p>
25
60
  <ul class="m-resource-list">
26
- <li v-for="(o, i) in authors" class="u-item" :key="i" :class="{ on: !!o.isSelected }" @click="selectAuthor(o, i)" ref="author">
61
+ <li
62
+ v-for="(o, i) in authors"
63
+ class="u-item"
64
+ :key="i"
65
+ :class="{ on: !!o.isSelected }"
66
+ @click="selectAuthor(o, i)"
67
+ ref="author"
68
+ >
27
69
  <span class="u-id">ID:{{ o.ID }}</span>
28
- <img class="u-pic" :title="'AuthorID:' + o.display_name" :src="userAvatar(o.user_avatar)" />
70
+ <img
71
+ class="u-pic"
72
+ :title="'AuthorID:' + o.display_name"
73
+ :src="userAvatar(o.user_avatar)"
74
+ />
29
75
  <span class="u-primary">
30
76
  <span class="u-name">
31
77
  {{ o.display_name }}
32
78
  </span>
33
79
  <div class="u-remark">
34
- {{o.user_bio}}
80
+ {{ o.user_bio }}
35
81
  </div>
36
82
  </span>
37
83
  </li>
38
84
  </ul>
39
- <el-alert v-if="!authors.length && done" title="没有找到相关条目" type="info" show-icon></el-alert>
85
+ <el-alert
86
+ v-if="!authors.length && done"
87
+ title="没有找到相关条目"
88
+ type="info"
89
+ show-icon
90
+ ></el-alert>
40
91
  </el-tab-pane>
41
92
  <el-tab-pane label="剑三趣图" name="emotions">
42
93
  <span slot="label" class="u-tab-label">
43
- <i class="el-icon-sugar" style="margin-right: 5px;"></i>
94
+ <i
95
+ class="el-icon-sugar"
96
+ style="margin-right: 5px"
97
+ ></i>
44
98
  <b>趣图</b>
45
99
  </span>
46
100
  <p v-if="total && done" class="m-resource-count">
47
- <i class="el-icon-s-data"></i> 共找到 <b>{{ total }}</b> 条记录
101
+ <i class="el-icon-s-data"></i> 共找到
102
+ <b>{{ total }}</b> 条记录
48
103
  </p>
49
104
  <ul class="m-resource-emotion">
50
- <li v-for="o in emotions" class="u-item" :key="o.id" :class="{ on: !!o.isSelected }" @click="selectEmotion(o)" ref="emotion">
51
- <img class="e-jx3-emotion" :src="resolveImagePath(o.url)" :alt="query" />
105
+ <li
106
+ v-for="o in emotions"
107
+ class="u-item"
108
+ :key="o.id"
109
+ :class="{ on: !!o.isSelected }"
110
+ @click="selectEmotion(o)"
111
+ ref="emotion"
112
+ >
113
+ <img
114
+ class="e-jx3-emotion"
115
+ :src="resolveImagePath(o.url)"
116
+ :alt="query"
117
+ />
52
118
  </li>
53
119
  </ul>
54
- <el-alert v-if="!emotions.length && done" title="没有找到相关条目" type="info" show-icon></el-alert>
120
+ <el-alert
121
+ v-if="!emotions.length && done"
122
+ title="没有找到相关条目"
123
+ type="info"
124
+ show-icon
125
+ ></el-alert>
126
+ </el-tab-pane>
127
+ <el-tab-pane label="技能连招" name="combo">
128
+ <span slot="label" class="u-tab-label">
129
+ <i
130
+ class="el-icon-tickets"
131
+ style="margin-right: 5px"
132
+ ></i>
133
+ <b>技能连招</b>
134
+ </span>
135
+ <ComboVue :query="query" ref="combo" :client="comboClient"></ComboVue>
55
136
  </el-tab-pane>
56
137
  </el-tabs>
57
138
 
58
- <template v-if="multipage">
139
+ <template v-if="multipage && type !== 'combo'">
140
+ {{ multipage && type !== "combo" }}
59
141
  <!-- 下一页 -->
60
- <el-button class="m-archive-more" :class="{ show: hasNextPage }" type="primary" icon="el-icon-arrow-down" @click="appendPage">加载更多</el-button>
142
+ <el-button
143
+ class="m-archive-more"
144
+ :class="{ show: hasNextPage }"
145
+ type="primary"
146
+ icon="el-icon-arrow-down"
147
+ @click="appendPage"
148
+ >加载更多</el-button
149
+ >
61
150
  <!-- 分页 -->
62
151
  <el-pagination
63
152
  class="m-archive-pages"
@@ -71,7 +160,9 @@
71
160
  ></el-pagination>
72
161
  </template>
73
162
 
74
- <div class="m-database-tip" v-show="isBlank">❤ 请输入搜索条件查询</div>
163
+ <div class="m-database-tip" v-show="isBlank && type !== 'combo'">
164
+ ❤ 请输入搜索条件查询
165
+ </div>
75
166
  </div>
76
167
 
77
168
  <!-- 插入按钮 -->
@@ -86,21 +177,35 @@
86
177
  </template>
87
178
 
88
179
  <script>
89
- import { loadStat, } from "../service/database";
180
+ import { loadStat } from "../service/database";
90
181
  import { loadAuthors, loadEmotions } from "../service/cms";
91
182
  import { getUserInfo } from "../service/author";
92
- import { __iconPath, __Root, __OriginRoot, __imgPath } from "@jx3box/jx3box-common/data/jx3box.json";
93
- import { getLink, showAvatar, resolveImagePath } from "@jx3box/jx3box-common/js/utils";
183
+ import {
184
+ __iconPath,
185
+ __Root,
186
+ __OriginRoot,
187
+ __imgPath,
188
+ } from "@jx3box/jx3box-common/data/jx3box.json";
189
+ import {
190
+ getLink,
191
+ showAvatar,
192
+ resolveImagePath,
193
+ } from "@jx3box/jx3box-common/js/utils";
94
194
  import User from "@jx3box/jx3box-common/js/user";
195
+
196
+ import ComboVue from "./components/Combo.vue";
95
197
  export default {
96
198
  name: "BoxResource",
199
+ components: {
200
+ ComboVue,
201
+ },
97
202
  props: {
98
203
  enable: {
99
204
  type: Boolean,
100
205
  default: true,
101
206
  },
102
207
  },
103
- data: function() {
208
+ data: function () {
104
209
  return {
105
210
  dialogVisible: false,
106
211
  actived: false,
@@ -110,6 +215,7 @@ export default {
110
215
  query: "",
111
216
  strict: false,
112
217
  client: location.hostname.includes("origin") ? "origin" : "std",
218
+ comboClient: "std",
113
219
 
114
220
  skill: [],
115
221
  buff: [],
@@ -132,57 +238,58 @@ export default {
132
238
  total: 1,
133
239
  pages: 1,
134
240
 
135
- placeholderTexts : {
136
- 'authors' : '请输入 ID 或 名称'
137
- }
138
-
241
+ placeholderTexts: {
242
+ authors: "请输入 ID 或 名称",
243
+ },
139
244
  };
140
245
  },
141
246
  computed: {
142
- placeholderText : function (){
143
- return this.placeholderTexts[this.type]
247
+ placeholderText: function () {
248
+ return this.placeholderTexts[this.type];
144
249
  },
145
- buttonTXT: function() {
250
+ buttonTXT: function () {
146
251
  return this.selectedCount ? "插 入" : "确 定";
147
252
  },
148
- isBlank: function() {
149
- return !this.query && !this[this.type]["length"];
253
+ isBlank: function () {
254
+ return !this.query && !this[this.type]?.["length"];
150
255
  },
151
- selectedCount: function() {
256
+ selectedCount: function () {
152
257
  return !!this.html;
153
258
  },
154
- isNumber: function() {
259
+ isNumber: function () {
155
260
  return !isNaN(this.query);
156
261
  },
157
- hasNextPage: function() {
262
+ hasNextPage: function () {
158
263
  return this.total > 1 && this.page < this.pages;
159
264
  },
160
- multipage: function() {
265
+ multipage: function () {
161
266
  return this.type !== "icon" && this.done && this.pages > 1;
162
267
  },
163
- iconDir: function() {
268
+ iconDir: function () {
164
269
  return this.client === "origin" ? "origin_icon" : "icon";
165
270
  },
166
- userStatus: function (){
167
- return User.getInfo().status
271
+ userStatus: function () {
272
+ return User.getInfo().status;
168
273
  },
169
- uid: function (){
170
- return User.getInfo().uid
274
+ uid: function () {
275
+ return User.getInfo().uid;
171
276
  },
172
- canInsertAuthor: function() {
173
- return User.getLevel(this.userInfo && this.userInfo.experience) >= 2;
277
+ canInsertAuthor: function () {
278
+ return (
279
+ User.getLevel(this.userInfo && this.userInfo.experience) >= 2
280
+ );
174
281
  },
175
- boxIcon: function() {
176
- return __imgPath + 'image/common/jx3box_white.svg'
282
+ boxIcon: function () {
283
+ return __imgPath + "image/common/jx3box_white.svg";
177
284
  },
178
285
  },
179
286
  watch: {
180
- html: function(newval) {
287
+ html: function (newval) {
181
288
  this.$emit("update", newval);
182
289
  },
183
290
  },
184
291
  methods: {
185
- getData: function(page = 1, append = false) {
292
+ getData: function (page = 1, append = false) {
186
293
  this.loading = true;
187
294
  this.per = 10;
188
295
  this.done = false;
@@ -195,20 +302,20 @@ export default {
195
302
  };
196
303
 
197
304
  // 图标
198
- if (this.type === 'authors') {
305
+ if (this.type === "authors") {
199
306
  if (!this.query) {
200
307
  this.loading = false;
201
308
  return;
202
- };
309
+ }
203
310
 
204
311
  params = {
205
312
  ...params,
206
313
  name: query,
207
- }
314
+ };
208
315
  loadAuthors(params)
209
316
  .then((res) => {
210
- if (!append) this.authors = [];
211
- let list = this.transformData(res.data.data.list)
317
+ if (!append) this.authors = [];
318
+ let list = this.transformData(res.data.data.list);
212
319
  this.authors = this.authors.concat(list);
213
320
  this.pages = res.data.data.pages;
214
321
  this.total = res.data.data.total;
@@ -217,18 +324,17 @@ export default {
217
324
  this.done = true;
218
325
  this.loading = false;
219
326
  });
220
-
221
- } else if (this.type === 'emotions') {
327
+ } else if (this.type === "emotions") {
222
328
  this.per = 24;
223
329
  params = {
224
330
  per: this.per,
225
331
  page: page,
226
332
  search: query,
227
- }
333
+ };
228
334
  loadEmotions(params)
229
335
  .then((res) => {
230
- if (!append) this.emotions = [];
231
- let list = this.transformData(res.data.data.list)
336
+ if (!append) this.emotions = [];
337
+ let list = this.transformData(res.data.data.list);
232
338
  this.emotions = this.emotions.concat(list);
233
339
  this.pages = res.data.data.pages;
234
340
  this.total = res.data.data.total;
@@ -239,39 +345,56 @@ export default {
239
345
  });
240
346
  }
241
347
  },
242
- search: function() {
243
- this.page = 1;
244
- this.getData();
348
+ search: function () {
349
+ if (this.type === "combo") {
350
+ this.$refs.combo?.search();
351
+ } else {
352
+ this.page = 1;
353
+ this.getData();
354
+ }
245
355
  },
246
- appendPage: function() {
356
+ appendPage: function () {
247
357
  this.getData(++this.page, true);
248
358
  },
249
- changePage: function(i) {
359
+ changePage: function (i) {
250
360
  this.getData(i);
251
361
  },
252
- changeType: function() {
362
+ changeType: function () {
253
363
  this.page = 1;
364
+ if (this.type === "combo") {
365
+ this.done = false;
366
+ return;
367
+ }
254
368
  this.getData();
255
369
  },
256
- setAuthors: function() {
370
+ setAuthors: function () {
257
371
  try {
258
372
  let author = sessionStorage.getItem("atAuthor");
259
373
  if (author) {
260
374
  author = JSON.parse(author);
261
- author = author.split(',') || [];
262
- if (this.selectedAuthor.ID && !author.includes(String(this.selectedAuthor.ID))) {
375
+ author = author.split(",") || [];
376
+ if (
377
+ this.selectedAuthor.ID &&
378
+ !author.includes(String(this.selectedAuthor.ID))
379
+ ) {
263
380
  author.push(this.selectedAuthor.ID);
264
381
  }
265
- sessionStorage.setItem("atAuthor", JSON.stringify(author.join(',')));
382
+ sessionStorage.setItem(
383
+ "atAuthor",
384
+ JSON.stringify(author.join(","))
385
+ );
266
386
  } else {
267
- sessionStorage.setItem("atAuthor", JSON.stringify(String(this.selectedAuthor.ID)));
387
+ sessionStorage.setItem(
388
+ "atAuthor",
389
+ JSON.stringify(String(this.selectedAuthor.ID))
390
+ );
268
391
  }
269
392
  } catch (error) {
270
- console.log(error)
393
+ console.log(error);
271
394
  }
272
395
  },
273
- insert: function() {
274
- if (this.type === 'authors') {
396
+ insert: function () {
397
+ if (this.type === "authors") {
275
398
  if (this.userStatus == 0 && this.canInsertAuthor) {
276
399
  if (this.selectedAuthor.ID) {
277
400
  this.setAuthors();
@@ -282,54 +405,59 @@ export default {
282
405
  this.$message.warning("请选择一个用户");
283
406
  }
284
407
  } else {
285
- this.$alert('您的等级不足或无权限(Lv2以上可用)', '消息');
408
+ this.$alert("您的等级不足或无权限(Lv2以上可用)", "消息");
286
409
  }
287
410
  } else {
288
- this.$emit("insert", this.html);
411
+ if (this.type === 'combo') {
412
+ const html = this.$refs.combo.renderVal();
413
+ this.$emit("insert", html);
414
+ } else {
415
+ this.$emit("insert", this.html);
416
+ }
289
417
  this.dialogVisible = false;
290
418
  }
291
419
  },
292
- transformData: function(data) {
420
+ transformData: function (data) {
293
421
  data.forEach((item) => {
294
422
  item.isSelected = false;
295
423
  });
296
424
  return data;
297
425
  },
298
- selectAuthor: function (o){
426
+ selectAuthor: function (o) {
299
427
  this.resetItems();
300
428
  this.selectedAuthor = o;
301
429
  o.isSelected = true;
302
- this.html = `<a data-type="author" class="e-jx3-author w-jx3-element" data-mode="" data-id="${o.ID}" target="_blank" href="/author/${o.ID}">@${o.display_name}</a>`
430
+ this.html = `<a data-type="author" class="e-jx3-author w-jx3-element" data-mode="" data-id="${o.ID}" target="_blank" href="/author/${o.ID}">@${o.display_name}</a>`;
303
431
  },
304
- selectEmotion: function (o){
432
+ selectEmotion: function (o) {
305
433
  this.resetItems();
306
434
  o.isSelected = true;
307
- this.html = `<a data-type="emotion" class="e-jx3-emotion w-jx3-element" data-id="${o.id}" target="_blank" href="/emotion/${o.id}"><img class="e-jx3-emotion-img" data-type="emotion" data-id="${o.id}" style="width:180px;" src="${o.url}" alt="${o.id}"/></a>`
435
+ this.html = `<a data-type="emotion" class="e-jx3-emotion w-jx3-element" data-id="${o.id}" target="_blank" href="/emotion/${o.id}"><img class="e-jx3-emotion-img" data-type="emotion" data-id="${o.id}" style="width:180px;" src="${o.url}" alt="${o.id}"/></a>`;
308
436
  },
309
- resetItems: function() {
437
+ resetItems: function () {
310
438
  let data = this[this.type];
311
439
  data.forEach((item) => {
312
440
  item.isSelected = false;
313
441
  });
314
442
  this.html = "";
315
443
  },
316
- getLink : function (type,id){
444
+ getLink: function (type, id) {
317
445
  let domain = this.client == "origin" ? __OriginRoot : __Root;
318
- return domain + getLink(type,id).slice(1)
446
+ return domain + getLink(type, id).slice(1);
319
447
  },
320
- userAvatar: function(url) {
321
- return showAvatar(url,'m');
448
+ userAvatar: function (url) {
449
+ return showAvatar(url, "m");
322
450
  },
323
- loadUserInfo: function (){
451
+ loadUserInfo: function () {
324
452
  if (!this.uid) return;
325
- getUserInfo(this.uid).then(res => {
326
- this.userInfo = res
327
- })
453
+ getUserInfo(this.uid).then((res) => {
454
+ this.userInfo = res;
455
+ });
328
456
  },
329
457
 
330
458
  // 杂项
331
459
  // ==============================
332
- openDialog: function() {
460
+ openDialog: function () {
333
461
  this.dialogVisible = true;
334
462
  if (!this.actived) {
335
463
  loadStat().then((data) => {
@@ -338,9 +466,9 @@ export default {
338
466
  });
339
467
  }
340
468
  },
341
- resolveImagePath
469
+ resolveImagePath,
342
470
  },
343
- created: function() {
471
+ created: function () {
344
472
  this.loadUserInfo();
345
473
  },
346
474
  };
package/src/Resource.vue CHANGED
@@ -201,7 +201,7 @@
201
201
  <script>
202
202
  import { loadResource, loadStat, getIcons } from "../service/database";
203
203
  import { loadEmotions } from "../service/cms";
204
- import { __ossRoot, __iconPath, __Root, __OriginRoot } from "@jx3box/jx3box-common/data/jx3box.json";
204
+ import { __iconPath, __Root, __OriginRoot } from "@jx3box/jx3box-common/data/jx3box.json";
205
205
  import detach_types from "../assets/data/detach_type.json";
206
206
  import { iconLink, getLink, showAvatar } from "@jx3box/jx3box-common/js/utils";
207
207
  import GameText from "./GameText.vue";
package/src/Tinymce.vue CHANGED
@@ -39,7 +39,7 @@ import Upload from "./Upload";
39
39
  import Resource from "./Resource";
40
40
  import BoxResource from "./BoxResource";
41
41
  import { __cms } from "@jx3box/jx3box-common/data/jx3box.json";
42
- import { __ossRoot, __imgPath } from "@jx3box/jx3box-common/data/jx3box.json";
42
+ import { __imgPath } from "@jx3box/jx3box-common/data/jx3box.json";
43
43
  import Emotion from "@jx3box/jx3box-emotion/src/Emotion.vue"
44
44
  import hljs_languages from "../assets/js/item/hljs_languages.js"
45
45
  const API_Root = process.env.NODE_ENV === "production" ? __cms : "/";
@@ -64,7 +64,7 @@ export default {
64
64
  // 样式
65
65
  // TODO:
66
66
  content_css: `https://oss.jx3box.com/static/tinymce/skins/content/default/content.min.css`,
67
- // content_css: `http://localhost:5000/skins/content/default/content.min.css`,
67
+ // content_css: `http://localhost:3000/skins/content/default/content.min.css`,
68
68
  body_class: "c-article c-article-editor c-article-tinymce",
69
69
  height: this.height || 800,
70
70
  autosave_ask_before_unload: false,
@@ -218,4 +218,5 @@ export default {
218
218
 
219
219
  <style lang="less">
220
220
  @import "../assets/css/tinymce.less";
221
+ @import "../assets/css/tinymce/combo.less";
221
222
  </style>
@@ -0,0 +1,258 @@
1
+ <template>
2
+ <div class="m-resource-combo">
3
+ <div class="m-combo-list">
4
+ <div v-if="total && done" class="m-resource-count">
5
+ <i class="el-icon-s-data"></i> 共找到 <b>{{ total }}</b> 条记录
6
+ </div>
7
+ <ul class="m-resource-list">
8
+ <li
9
+ v-for="(o, i) in skill"
10
+ class="u-item"
11
+ :key="i"
12
+ @click="selectSkill(o, i)"
13
+ ref="skill"
14
+ >
15
+ <span class="u-id">ID:{{ o.SkillID }}</span>
16
+ <img
17
+ class="u-pic"
18
+ :title="'IconID:' + o.IconID"
19
+ :src="iconURL(o.IconID)"
20
+ />
21
+ <span class="u-primary">
22
+ <span class="u-name">
23
+ {{ o.Name }}
24
+ <em v-if="o.SkillName">({{ o.SkillName }})</em>
25
+ </span>
26
+ <span class="u-content">
27
+ {{ filterRaw(o.Desc) }}
28
+ </span>
29
+ </span>
30
+ </li>
31
+ </ul>
32
+ <el-alert
33
+ v-if="!skill.length && done"
34
+ title="没有找到相关条目"
35
+ type="info"
36
+ show-icon
37
+ ></el-alert>
38
+
39
+ <template v-if="multipage">
40
+ <!-- 下一页 -->
41
+ <el-button
42
+ class="m-archive-more"
43
+ :class="{ show: hasNextPage }"
44
+ type="primary"
45
+ icon="el-icon-arrow-down"
46
+ @click="appendPage"
47
+ >加载更多</el-button
48
+ >
49
+ <!-- 分页 -->
50
+ <el-pagination
51
+ class="m-archive-pages"
52
+ background
53
+ layout="total, prev, pager, next,jumper"
54
+ :hide-on-single-page="true"
55
+ :page-size="per"
56
+ :total="total"
57
+ :current-page.sync="page"
58
+ @current-change="changePage"
59
+ ></el-pagination>
60
+ </template>
61
+
62
+ <div class="m-database-tip" v-show="isBlank">
63
+ ❤ 请输入搜索条件查询
64
+ </div>
65
+ </div>
66
+
67
+ <el-divider>已选技能</el-divider>
68
+ <div class="m-selected-skills">
69
+ <ul class="m-skills-list">
70
+ <li
71
+ v-for="(skill, index) in selected"
72
+ :key="index + skill.SkillID"
73
+ class="m-skill"
74
+ @contextmenu.prevent="(event) => onContextmenu(event, skill)"
75
+ >
76
+ <div class="u-skill" v-if="skill && skill.IconID">
77
+ <img
78
+ class="u-skill-icon"
79
+ :src="iconURL(skill.IconID)"
80
+ :alt="skill.IconID"
81
+ />
82
+ <i class="u-gcd-icon" v-show="skill.WithoutGcd">
83
+ <i class="el-icon-time"></i>
84
+ </i>
85
+ <i
86
+ class="u-remove-icon"
87
+ title="移除"
88
+ @click="removeSelected(index)"
89
+ ><i class="el-icon-close"></i
90
+ ></i>
91
+ </div>
92
+ </li>
93
+ </ul>
94
+ </div>
95
+ </div>
96
+ </template>
97
+
98
+ <script>
99
+ import Vue from "vue";
100
+ import { iconLink } from "@jx3box/jx3box-common/js/utils";
101
+ import { getSkill } from "../../service/resource";
102
+ import Sortable from "sortablejs";
103
+ import { cloneDeep } from "lodash";
104
+
105
+ import LoadScript from 'vue-plugin-load-script';
106
+ Vue.use(LoadScript);
107
+
108
+ import contextmenu from "vue-contextmenujs"
109
+ Vue.use(contextmenu)
110
+ export default {
111
+ name: "Combo",
112
+ props: {
113
+ query: {
114
+ type: String,
115
+ default: "",
116
+ },
117
+ client: {
118
+ type: String,
119
+ default: "std",
120
+ },
121
+ },
122
+ data() {
123
+ return {
124
+ done: false,
125
+ per: 10,
126
+ page: 1,
127
+ total: 1,
128
+ pages: 1,
129
+
130
+ selected: [],
131
+ skill: [],
132
+ };
133
+ },
134
+ computed: {
135
+ multipage: function () {
136
+ return this.done && this.pages > 1;
137
+ },
138
+ isBlank: function () {
139
+ return !this.query && !this.skill?.["length"];
140
+ },
141
+ hasNextPage: function () {
142
+ return this.total > 1 && this.page < this.pages;
143
+ },
144
+ },
145
+ mounted() {
146
+ this.$nextTick(() => {
147
+ this.initSkillSort();
148
+
149
+ });
150
+ },
151
+ methods: {
152
+ getData(page = 1, append = false) {
153
+ if (!this.query) return;
154
+
155
+ this.loading = true;
156
+ this.per = 10;
157
+ this.done = false;
158
+ let query = this.query;
159
+ let params = {
160
+ strict: ~~this.strict,
161
+ per: this.per,
162
+ page: page,
163
+ client: this.client,
164
+ };
165
+
166
+ getSkill(query, params)
167
+ .then((data) => {
168
+ if (!append) this.skill = [];
169
+ const list = (data.list || [])?.map((item) => {
170
+ item.isSelected = false;
171
+ return item;
172
+ });
173
+ this.pages = data.pages;
174
+ this.total = data.total;
175
+ this.skill = this.skill.concat(list);
176
+ })
177
+ .finally(() => {
178
+ this.done = true;
179
+ this.loading = false;
180
+ });
181
+ },
182
+ submit() {
183
+ this.$emit("submit", this.selected);
184
+ this.close();
185
+ this.selected = [];
186
+ },
187
+ close() {
188
+ this.$emit("update:modelValue", false);
189
+ },
190
+ iconURL: function (id) {
191
+ return iconLink(id, this.client);
192
+ },
193
+ filterRaw: function (str) {
194
+ return str && str.replace(/\\n/g, "\n");
195
+ },
196
+ search: function () {
197
+ this.page = 1;
198
+ this.getData();
199
+ },
200
+ appendPage: function () {
201
+ this.getData(++this.page, true);
202
+ },
203
+ changePage: function (i) {
204
+ this.getData(i);
205
+ },
206
+ selectSkill: function (o) {
207
+ this.selected.push(o);
208
+ },
209
+ removeSelected: function (index) {
210
+ this.selected.splice(index, 1);
211
+ },
212
+ initSkillSort() {
213
+ const el = document.querySelector(`.m-selected-skills .m-skills-list`);
214
+ if (!el) return;
215
+ const _this = this;
216
+ const sortSkills = Sortable.create(el, {
217
+ animation: 200,
218
+ onEnd({ newIndex, oldIndex }) {
219
+ const newSkills = cloneDeep(_this.selected);
220
+ const [removed] = newSkills.splice(oldIndex, 1);
221
+ newSkills.splice(newIndex, 0, removed);
222
+ _this.selected = newSkills;
223
+ },
224
+ });
225
+ },
226
+ onContextmenu(event, skill) {
227
+ // console.log(skill)
228
+ this.$contextmenu({
229
+ items: [
230
+ {
231
+ label: !skill?.WithoutGcd ? "设置为无GCD技能" : "取消无GCD技能",
232
+ onClick: () => {
233
+ this.$set(skill, "WithoutGcd", !skill.WithoutGcd);
234
+ },
235
+ icon: !skill?.WithoutGcd ? "el-icon-check" : "el-icon-close"
236
+ },
237
+ ],
238
+ event,
239
+ customClass: "custom-class",
240
+ zIndex: 99999,
241
+ minWidth: 200,
242
+ });
243
+ return false;
244
+ },
245
+
246
+ renderVal() {
247
+ const {selected} = this;
248
+ return `<ul class="e-skill-combo w-skill-combo">${selected.map(item => {
249
+ return `<li class="w-skill-combo-item">${item.SkillID},${item.Name},${item.IconID},{gcd:${item.WithoutGcd ? 1: 0}}</li>`
250
+ })}</ul>`
251
+ },
252
+ },
253
+ };
254
+ </script>
255
+
256
+ <style lang="less">
257
+ @import "../../assets/css/combo.less";
258
+ </style>