@jx3box/jx3box-editor 0.9.3 → 0.9.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.
@@ -0,0 +1,36 @@
1
+ .c-editor-header {
2
+ .mb(10px);
3
+ .clearfix;
4
+
5
+ .c-upload,
6
+ .c-resource {
7
+ .fl;
8
+ }
9
+ .c-upload {
10
+ .mr(5px);
11
+ }
12
+ }
13
+
14
+ .c-markdown{
15
+ .h(780px);
16
+ }
17
+
18
+ .c-markdown-toolbar-item {
19
+ .mr(5px);
20
+ .pointer;
21
+ padding: 2px 5px 3px 5px;
22
+
23
+ &:hover {
24
+ background: #e9e9eb;
25
+ .r(4px);
26
+ }
27
+ }
28
+ .c-markdown-store-item {
29
+ .none;
30
+ }
31
+
32
+ .c-markdown {
33
+ .markdown-body .hljs {
34
+ background-color: #f6f8fa;
35
+ }
36
+ }
@@ -1,21 +1,22 @@
1
- .c-editor-tinymce {
2
-
3
- .c-editor-header{
4
- .mb(10px);
5
- .clearfix;
1
+ .c-editor-header{
2
+ .mb(10px);
3
+ .clearfix;
6
4
 
7
- .c-upload,.c-resource{
8
- .fl;
9
- }
10
- .c-upload{
11
- .mr(5px);
12
- }
5
+ .c-upload,.c-resource{
6
+ .fl;
13
7
  }
14
-
15
- .c-editor-emotion{
16
- max-height: 168px;
17
- overflow: auto;
8
+ .c-upload{
9
+ .mr(5px);
18
10
  }
11
+ }
12
+
13
+ .c-editor-emotion{
14
+ max-height: 168px;
15
+ overflow: auto;
16
+ }
17
+
18
+ .c-editor-tinymce {
19
+
19
20
 
20
21
  .u-tutorial {
21
22
  padding:0 10px;
@@ -0,0 +1,16 @@
1
+ ## Markdown
2
+
3
+ 这组件真是垃圾,有时间我一定自己重写一个。。。
4
+
5
+
6
+ ## main.js引入
7
+ ```javascript
8
+ import mavonEditor from "@jx3box/markdown";
9
+ import "@jx3box/markdown/dist/css/index.css";
10
+ Vue.use(mavonEditor)
11
+ ```
12
+
13
+ ## 封装模块引入
14
+ ```javascript
15
+ import Markdown from '@jx3box/jx3box-editor/src/Markdown.vue'
16
+ ```
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "@jx3box/jx3box-editor",
3
- "version": "0.9.3",
3
+ "version": "0.9.8",
4
4
  "description": "JX3BOX Article & Editor",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
+ "dev": "env DEV_SERVER=true vue-cli-service serve",
7
8
  "serve": "vue-cli-service serve",
8
9
  "build": "vue-cli-service build",
9
10
  "lint": "vue-cli-service lint",
10
11
  "inspect": "vue inspect > output.js",
11
- "update": "npm --registry https://registry.npmjs.org install @jx3box/jx3box-common@latest @jx3box/jx3box-macro@latest @jx3box/jx3box-talent@latest @jx3box/jx3box-emotion@latest @jx3box/jx3box-data@latest",
12
+ "update": "npm --registry https://registry.npmjs.org install @jx3box/jx3box-common@latest @jx3box/jx3box-macro@latest @jx3box/jx3box-talent@latest @jx3box/jx3box-emotion@latest @jx3box/jx3box-data@latest @jx3box/markdown@latest",
12
13
  "article": "vue-cli-service build --target lib --name jx3box_article src/Article.vue --mode production",
13
14
  "tinymce": "vue-cli-service build --target lib --name tinymce src/Tinymce.vue && cp public/tinymce.html dist/tinymce.html"
14
15
  },
@@ -35,6 +36,7 @@
35
36
  "@jx3box/jx3box-emotion": "^1.0.10",
36
37
  "@jx3box/jx3box-macro": "^1.0.1",
37
38
  "@jx3box/jx3box-talent": "^1.1.2",
39
+ "@jx3box/markdown": "0.0.8",
38
40
  "@tinymce/tinymce-vue": "^3.2.2",
39
41
  "axios": "^0.19.2",
40
42
  "core-js": "^3.6.5",
package/readme.md CHANGED
@@ -7,7 +7,8 @@
7
7
  $ npm install
8
8
  $ npm run serve
9
9
  $ open http://localhost:8080 //文章渲染
10
- $ open http://localhost:8080/tinymce //编辑器
10
+ $ open http://localhost:8080/tinymce //tinymce编辑器
11
+ $ open http://localhost:8080/markdown //markdown编辑器
11
12
  $ tinymce http://localhost:5000
12
13
  ```
13
14
 
package/service/cms.js ADDED
@@ -0,0 +1,7 @@
1
+ import { $cms } from "@jx3box/jx3box-common/js/https";
2
+
3
+ function uploadFile(data) {
4
+ return $cms().post(`/api/cms/upload`, data);
5
+ }
6
+
7
+ export { uploadFile };
@@ -0,0 +1,184 @@
1
+ <template>
2
+ <div class="c-editor-markdown">
3
+
4
+ <slot name="prepend"></slot>
5
+
6
+ <div class="c-editor-header">
7
+ <Upload v-if="attachmentEnable" @insert="insertAttachments" :enable="false" />
8
+ <Resource v-if="resourceEnable" @insert="insertResource" :enable="false" />
9
+ </div>
10
+
11
+ <slot></slot>
12
+
13
+ <mavon-editor class="c-markdown" ref="md" v-model="data" :editable="editable" :navigation="false" @change="updateData" :subfield="false" :readOnly="false">
14
+ <template slot="left-toolbar-after">
15
+ <span class="c-markdown-toolbar-image c-markdown-toolbar-item" title="上传图片" @click="selectImages"><i class="el-icon-picture-outline-round"></i></span>
16
+ <span class="c-markdown-toolbar-file c-markdown-toolbar-item" title="上传附件" @click="selectFiles"><i class="el-icon-paperclip"></i></span>
17
+ </template>
18
+ </mavon-editor>
19
+ <input class="c-markdown-store-item" id="c-markdown-store-images" type="file" @change="uploadImages" ref="markdownImages" multiple :accept="allow_image_types" />
20
+ <input class="c-markdown-store-item" id="c-markdown-store-files" type="file" @change="uploadFiles" ref="markdownFiles" multiple />
21
+
22
+ <slot name="append"></slot>
23
+ </div>
24
+ </template>
25
+
26
+ <script>
27
+ import Upload from "./Upload";
28
+ import Resource from "./Resource";
29
+ import { uploadFile } from "../service/cms";
30
+ export default {
31
+ name: "Markdown",
32
+ props: {
33
+ content: {
34
+ type: String,
35
+ },
36
+ editable: {
37
+ type: Boolean,
38
+ default: true,
39
+ },
40
+ attachmentEnable: {
41
+ type: Boolean,
42
+ default: true,
43
+ },
44
+ resourceEnable: {
45
+ type: Boolean,
46
+ default: true,
47
+ },
48
+ },
49
+ components: {
50
+ Upload,
51
+ Resource,
52
+ },
53
+ data: function() {
54
+ return {
55
+ data: this.content,
56
+ allow_image_types: ["image/png", "image/jpeg", "image/gif", "image/bmp", "image/webp"],
57
+ allow_file_types: [],
58
+ image_ext: ["png", "jpg", "gif", "bmp", "webp"],
59
+ files: [],
60
+ resolved_files: [],
61
+ };
62
+ },
63
+ model: {
64
+ prop: "content",
65
+ event: "update",
66
+ },
67
+ watch: {
68
+ data: function(newval) {
69
+ this.$emit("update", newval);
70
+ },
71
+ content: function(newval) {
72
+ this.data = newval;
73
+ },
74
+ // 监听过滤后的文件列表
75
+ files_list: function(list) {
76
+ this.bulkUpload(list);
77
+ },
78
+ },
79
+ computed: {
80
+ files_list: function() {
81
+ let files = Array.from(this.files);
82
+ return files;
83
+ },
84
+ $md: function() {
85
+ return this.$refs.md;
86
+ },
87
+ },
88
+ methods: {
89
+ // 点击上传按钮
90
+ selectImages: function() {
91
+ document.getElementById("c-markdown-store-images").dispatchEvent(new MouseEvent("click"));
92
+ },
93
+ selectFiles: function() {
94
+ document.getElementById("c-markdown-store-files").dispatchEvent(new MouseEvent("click"));
95
+ },
96
+ // 监听选择结果变化
97
+ uploadImages: function(e) {
98
+ this.files = this.$refs.markdownImages.files;
99
+ },
100
+ uploadFiles: function(e) {
101
+ this.files = this.$refs.markdownFiles.files;
102
+ },
103
+ // 批量上传
104
+ bulkUpload: function(list) {
105
+ // 存在有效数据队列时
106
+ if (!list || !list.length) return;
107
+
108
+ // 上传队列
109
+ let queue = [];
110
+ for (let item of list) {
111
+ let formdata = new FormData();
112
+ formdata.append("file", item);
113
+ queue.push(uploadFile(formdata));
114
+ }
115
+
116
+ // 回调处理
117
+ Promise.allSettled(queue)
118
+ .then((results) => {
119
+ results.forEach((result, i) => {
120
+ if (result.status == "fulfilled") {
121
+ let url = result.value.data?.data?.[0];
122
+ this.resolved_files.push({
123
+ url: url,
124
+ filename: list[i]["name"],
125
+ type: list[i]["type"],
126
+ ext: list[i]["name"].split(".").pop(),
127
+ });
128
+ }
129
+ });
130
+ this.insertFiles();
131
+ })
132
+ .finally(() => {
133
+ // 上传完成后清空input
134
+ this.images = [];
135
+ this.files = [];
136
+ this.resolved_files = [];
137
+ });
138
+ },
139
+ // 插入正文
140
+ insertFiles: function() {
141
+ for (let item of this.resolved_files) {
142
+ // 插入图片
143
+ if (this.image_ext.includes(item.ext)) {
144
+ this.$md.insertText(this.$md.getTextareaDom(), {
145
+ prefix: `![${item.filename}](${item.url})`,
146
+ subfix: "",
147
+ str: "",
148
+ });
149
+ // 插入文字链接
150
+ } else {
151
+ this.$md.insertText(this.$md.getTextareaDom(), {
152
+ prefix: `[${item.filename}](${item.url})`,
153
+ subfix: "",
154
+ str: "",
155
+ });
156
+ }
157
+ }
158
+ },
159
+ // 更新触发
160
+ updateData: function(data, render) {
161
+ this.$emit("updateData", {
162
+ data,
163
+ render,
164
+ });
165
+ },
166
+ // 插入附件
167
+ insertAttachments: function(data) {
168
+ // TODO:
169
+ // tinyMCE.editors["tinymce"].insertContent(data.html);
170
+ },
171
+ insertResource: function(data) {
172
+ // TODO:
173
+ // tinyMCE.editors["tinymce"].insertContent(data);
174
+ },
175
+ },
176
+ filters: {},
177
+ created: function() {},
178
+ mounted: function() {},
179
+ };
180
+ </script>
181
+
182
+ <style lang="less">
183
+ @import "../assets/css/markdown.less";
184
+ </style>
package/src/Resource.vue CHANGED
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="c-resource">
3
3
  <!-- 上传触发按钮 -->
4
- <el-button class="u-switch" type="primary" @click="dialogVisible = true">
4
+ <el-button class="u-switch" type="primary" @click="dialogVisible = true" :disabled="!enable">
5
5
  <img class="u-icon" svg-inline src="../assets/img/jx3.svg" />剑三资源
6
6
  </el-button>
7
7
 
@@ -268,7 +268,12 @@ import User from "@jx3box/jx3box-common/js/user";
268
268
  import { iconLink } from "@jx3box/jx3box-common/js/utils";
269
269
  export default {
270
270
  name: "Resource",
271
- props: [],
271
+ props: {
272
+ enable : {
273
+ type: Boolean,
274
+ default : true,
275
+ }
276
+ },
272
277
  data: function () {
273
278
  return {
274
279
  dialogVisible: false,
package/src/Tinymce.vue CHANGED
@@ -1,11 +1,16 @@
1
1
  <template>
2
2
  <div class="c-editor-tinymce">
3
+
4
+ <slot name="prepend"></slot>
5
+
3
6
  <div class="c-editor-header">
4
7
  <Upload v-if="attachmentEnable" @insert="insertAttachments" />
5
8
  <Resource v-if="resourceEnable" @insert="insertResource" />
6
- <slot></slot>
7
9
  </div>
8
10
  <Emotion class="c-editor-emotion" @selected="emotionSelected"></Emotion>
11
+
12
+ <slot></slot>
13
+
9
14
  <editor
10
15
  id="tinymce"
11
16
  v-model="data"
@@ -22,6 +27,8 @@
22
27
  >[编辑器使用指南]</a
23
28
  >
24
29
  </el-alert>
30
+
31
+ <slot name="append"></slot>
25
32
  </div>
26
33
  </template>
27
34
 
@@ -157,6 +164,7 @@ export default {
157
164
  // },
158
165
  // ],
159
166
  },
167
+ mode : 'tinymce'
160
168
  };
161
169
  },
162
170
  model: {
@@ -188,7 +196,7 @@ export default {
188
196
  const pathKey = key.slice(1);
189
197
  const IMAGE = `<img class="t-emotion" src="${__ossRoot}image/emotion/${pathKey}.gif" alt="${key}" />`
190
198
  tinyMCE.editors["tinymce"].insertContent(IMAGE)
191
- }
199
+ },
192
200
  },
193
201
  mounted: function() {},
194
202
  components: {
package/src/Upload.vue CHANGED
@@ -1,35 +1,18 @@
1
1
  <template>
2
2
  <div class="c-upload">
3
3
  <!-- 上传触发按钮 -->
4
- <el-button type="primary" @click="dialogVisible = true" icon="el-icon-upload">{{btn_txt}}</el-button>
4
+ <el-button type="primary" @click="dialogVisible = true" icon="el-icon-upload" :disabled="!enable">{{ btn_txt }}</el-button>
5
5
 
6
6
  <!-- 弹出界面 -->
7
7
  <el-dialog class="c-large-dialog" title="上传" :visible.sync="dialogVisible">
8
8
  <!-- 清空按钮 -->
9
- <el-button
10
- class="u-upload-clear"
11
- plain
12
- icon="el-icon-delete"
13
- size="mini"
14
- @click="clear"
15
- >清空</el-button>
9
+ <el-button class="u-upload-clear" plain icon="el-icon-delete" size="mini" @click="clear">清空</el-button>
16
10
 
17
11
  <!-- 限制提示 -->
18
12
  <el-alert class="u-upload-tip" :title="tip" type="info" show-icon :closable="false"></el-alert>
19
13
 
20
14
  <!-- 文件区 -->
21
- <el-upload
22
- :action="API"
23
- list-type="picture-card"
24
- :auto-upload="false"
25
- :limit="10"
26
- multiple
27
- with-credentials
28
- :file-list="fileList"
29
- :on-change="change"
30
- ref="uploadbox"
31
- :accept="accept"
32
- >
15
+ <el-upload :action="API" list-type="picture-card" :auto-upload="false" :limit="10" multiple with-credentials :file-list="fileList" :on-change="change" ref="uploadbox" :accept="accept">
33
16
  <!-- :accept="accept" -->
34
17
  <i slot="default" class="el-icon-plus"></i>
35
18
 
@@ -45,12 +28,7 @@
45
28
  }"
46
29
  >
47
30
  <!-- 图片类型 -->
48
- <img
49
- v-if="file.is_img"
50
- class="el-upload-list__item-thumbnail u-imgbox"
51
- :src="file.url"
52
- alt
53
- />
31
+ <img v-if="file.is_img" class="el-upload-list__item-thumbnail u-imgbox" :src="file.url" alt />
54
32
  <!-- 其他类型 -->
55
33
  <div v-else class="u-filebox">
56
34
  <img class="u-fileplaceholder" svg-inline src="../assets/img/file.svg" />
@@ -67,9 +45,7 @@
67
45
  <span slot="footer" class="dialog-footer">
68
46
  <el-button @click="dialogVisible = false">取 消</el-button>
69
47
  <el-button type="primary" @click="insert">
70
- {{
71
- buttonTXT
72
- }}
48
+ {{ buttonTXT }}
73
49
  </el-button>
74
50
  </span>
75
51
  </el-dialog>
@@ -87,8 +63,25 @@ const imgtypes = ["jpg", "png", "gif", "bmp", "webp"];
87
63
 
88
64
  export default {
89
65
  name: "Upload",
90
- props: ["text",'onlyImage','desc','accept'],
91
- data: function () {
66
+ props: {
67
+ text: {
68
+ type: String,
69
+ },
70
+ onlyImage: {
71
+ type: Boolean,
72
+ },
73
+ desc: {
74
+ type: String,
75
+ },
76
+ accept: {
77
+ type: String,
78
+ },
79
+ enable : {
80
+ type: Boolean,
81
+ default : true,
82
+ }
83
+ },
84
+ data: function() {
92
85
  return {
93
86
  API: API,
94
87
  dialogVisible: false,
@@ -106,21 +99,21 @@ export default {
106
99
  watch: {
107
100
  fileList: {
108
101
  deep: true,
109
- handler: function (newval) {
102
+ handler: function(newval) {
110
103
  this.$emit("update", newval);
111
104
  },
112
105
  },
113
- insertList: function (newval) {
106
+ insertList: function(newval) {
114
107
  this.$emit("htmlUpdate", newval);
115
108
  },
116
109
  },
117
110
  computed: {
118
- buttonTXT: function () {
111
+ buttonTXT: function() {
119
112
  return this.selectedCount ? "插 入" : "确 定";
120
113
  },
121
114
  },
122
115
  methods: {
123
- change: function (file, fileList) {
116
+ change: function(file, fileList) {
124
117
  if (file.status != "success") {
125
118
  // 判断大小
126
119
  // if (file.size > this.sizeLimit) {
@@ -133,7 +126,7 @@ export default {
133
126
  let ext = file.name.split(".").pop();
134
127
  let is_img = imgtypes.includes(ext);
135
128
 
136
- if(this.onlyImage && !is_img) return
129
+ if (this.onlyImage && !is_img) return;
137
130
 
138
131
  // 构建数据
139
132
  let fdata = new FormData();
@@ -166,36 +159,30 @@ export default {
166
159
  })
167
160
  .catch((err) => {
168
161
  if (err.response.data.code) {
169
- this.$message.error(
170
- `[${err.response.data.code}] ${err.response.data.message}`
171
- );
162
+ this.$message.error(`[${err.response.data.code}] ${err.response.data.message}`);
172
163
  } else {
173
164
  this.$message.error("请求异常");
174
165
  }
175
166
  });
176
167
  }
177
168
  },
178
- select: function (file) {
169
+ select: function(file) {
179
170
  if (file.status == "success") {
180
171
  this.$set(file, "selected", !file.selected);
181
172
  file.selected ? this.selectedCount++ : this.selectedCount--;
182
173
  }
183
174
  },
184
- buildHTML: function () {
175
+ buildHTML: function() {
185
176
  let list = [];
186
177
  this.fileList.forEach((file) => {
187
178
  if (file.selected) {
188
- file.is_img
189
- ? list.push(`<img src="${file.url}" />`)
190
- : list.push(
191
- `<a target="_blank" href="${file.url}">${file.name}</a>`
192
- );
179
+ file.is_img ? list.push(`<img src="${file.url}" />`) : list.push(`<a target="_blank" href="${file.url}">${file.name}</a>`);
193
180
  }
194
181
  });
195
182
  this.insertList = list.join(" \n");
196
183
  return this.insertList;
197
184
  },
198
- insert: function () {
185
+ insert: function() {
199
186
  // 关闭窗口
200
187
  this.dialogVisible = false;
201
188
 
@@ -211,17 +198,17 @@ export default {
211
198
  //移除所有选择状态
212
199
  this.resetSelectStatus();
213
200
  },
214
- resetSelectStatus: function () {
201
+ resetSelectStatus: function() {
215
202
  this.fileList.forEach((file, i) => {
216
203
  this.$set(this.fileList[i], "selected", false);
217
204
  });
218
205
  this.selectedCount = 0;
219
206
  },
220
- clear: function () {
207
+ clear: function() {
221
208
  this.$refs.uploadbox.clearFiles();
222
209
  this.fileList = [];
223
210
  },
224
- removeFile: function (fileList, uid) {
211
+ removeFile: function(fileList, uid) {
225
212
  fileList.forEach((file, i) => {
226
213
  if (file.uid == uid) {
227
214
  fileList.splice(i, 1);
@@ -229,7 +216,7 @@ export default {
229
216
  });
230
217
  },
231
218
  },
232
- mounted: function () {},
219
+ mounted: function() {},
233
220
  components: {},
234
221
  };
235
222
  </script>
File without changes
File without changes
package/vue.config.js CHANGED
@@ -18,6 +18,12 @@ module.exports = {
18
18
  template : 'public/tinymce.html',
19
19
  filename:'tinymce/index.html',
20
20
  },
21
+ markdown : {
22
+ title : 'Markdown编辑器',
23
+ entry:'demo/M.js',
24
+ template : 'public/article.html',
25
+ filename:'markdown/index.html',
26
+ },
21
27
  },
22
28
 
23
29
 
@@ -48,16 +54,22 @@ module.exports = {
48
54
  '/',
49
55
 
50
56
  //❤️ Porxy ~
51
- // devServer: {
52
- // proxy: {
53
- // "/api": {
54
- // "target": process.env["DEV_SERVER"] == "true" ? "http://localhost:51818" : "https://next.jx3box.com",
55
- // "onProxyReq": function (request) {
56
- // request.setHeader("origin", "");
57
- // }
58
- // }
59
- // }
60
- // },
57
+ devServer: {
58
+ proxy: {
59
+ "/api/cms": {
60
+ "target": process.env["DEV_SERVER"] == "true" ? "http://localhost:5120" : "https://cms.jx3box.com",
61
+ "onProxyReq": function (request) {
62
+ request.setHeader("origin", "");
63
+ }
64
+ },
65
+ "/api": {
66
+ "target": process.env["DEV_SERVER"] == "true" ? "http://localhost:51818" : "https://next.jx3box.com",
67
+ "onProxyReq": function (request) {
68
+ request.setHeader("origin", "");
69
+ }
70
+ }
71
+ }
72
+ },
61
73
 
62
74
  chainWebpack: config => {
63
75