@jx3box/jx3box-editor 0.9.2 → 0.9.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.
@@ -0,0 +1,32 @@
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-toolbar-item {
15
+ .mr(5px);
16
+ .pointer;
17
+ padding: 2px 5px 3px 5px;
18
+
19
+ &:hover {
20
+ background: #e9e9eb;
21
+ .r(4px);
22
+ }
23
+ }
24
+ .c-markdown-store-item {
25
+ .none;
26
+ }
27
+
28
+ .c-markdown {
29
+ .markdown-body .hljs {
30
+ background-color: #f6f8fa;
31
+ }
32
+ }
@@ -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.2",
3
+ "version": "0.9.7",
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.7",
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">
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,34 +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
- >
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">
32
16
  <!-- :accept="accept" -->
33
17
  <i slot="default" class="el-icon-plus"></i>
34
18
 
@@ -44,12 +28,7 @@
44
28
  }"
45
29
  >
46
30
  <!-- 图片类型 -->
47
- <img
48
- v-if="file.is_img"
49
- class="el-upload-list__item-thumbnail u-imgbox"
50
- :src="file.url"
51
- alt
52
- />
31
+ <img v-if="file.is_img" class="el-upload-list__item-thumbnail u-imgbox" :src="file.url" alt />
53
32
  <!-- 其他类型 -->
54
33
  <div v-else class="u-filebox">
55
34
  <img class="u-fileplaceholder" svg-inline src="../assets/img/file.svg" />
@@ -66,9 +45,7 @@
66
45
  <span slot="footer" class="dialog-footer">
67
46
  <el-button @click="dialogVisible = false">取 消</el-button>
68
47
  <el-button type="primary" @click="insert">
69
- {{
70
- buttonTXT
71
- }}
48
+ {{ buttonTXT }}
72
49
  </el-button>
73
50
  </span>
74
51
  </el-dialog>
@@ -86,8 +63,25 @@ const imgtypes = ["jpg", "png", "gif", "bmp", "webp"];
86
63
 
87
64
  export default {
88
65
  name: "Upload",
89
- props: ["text",'onlyImage','desc'],
90
- 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() {
91
85
  return {
92
86
  API: API,
93
87
  dialogVisible: false,
@@ -105,21 +99,21 @@ export default {
105
99
  watch: {
106
100
  fileList: {
107
101
  deep: true,
108
- handler: function (newval) {
102
+ handler: function(newval) {
109
103
  this.$emit("update", newval);
110
104
  },
111
105
  },
112
- insertList: function (newval) {
106
+ insertList: function(newval) {
113
107
  this.$emit("htmlUpdate", newval);
114
108
  },
115
109
  },
116
110
  computed: {
117
- buttonTXT: function () {
111
+ buttonTXT: function() {
118
112
  return this.selectedCount ? "插 入" : "确 定";
119
113
  },
120
114
  },
121
115
  methods: {
122
- change: function (file, fileList) {
116
+ change: function(file, fileList) {
123
117
  if (file.status != "success") {
124
118
  // 判断大小
125
119
  // if (file.size > this.sizeLimit) {
@@ -132,7 +126,7 @@ export default {
132
126
  let ext = file.name.split(".").pop();
133
127
  let is_img = imgtypes.includes(ext);
134
128
 
135
- if(this.onlyImage && !is_img) return
129
+ if (this.onlyImage && !is_img) return;
136
130
 
137
131
  // 构建数据
138
132
  let fdata = new FormData();
@@ -165,36 +159,30 @@ export default {
165
159
  })
166
160
  .catch((err) => {
167
161
  if (err.response.data.code) {
168
- this.$message.error(
169
- `[${err.response.data.code}] ${err.response.data.message}`
170
- );
162
+ this.$message.error(`[${err.response.data.code}] ${err.response.data.message}`);
171
163
  } else {
172
164
  this.$message.error("请求异常");
173
165
  }
174
166
  });
175
167
  }
176
168
  },
177
- select: function (file) {
169
+ select: function(file) {
178
170
  if (file.status == "success") {
179
171
  this.$set(file, "selected", !file.selected);
180
172
  file.selected ? this.selectedCount++ : this.selectedCount--;
181
173
  }
182
174
  },
183
- buildHTML: function () {
175
+ buildHTML: function() {
184
176
  let list = [];
185
177
  this.fileList.forEach((file) => {
186
178
  if (file.selected) {
187
- file.is_img
188
- ? list.push(`<img src="${file.url}" />`)
189
- : list.push(
190
- `<a target="_blank" href="${file.url}">${file.name}</a>`
191
- );
179
+ file.is_img ? list.push(`<img src="${file.url}" />`) : list.push(`<a target="_blank" href="${file.url}">${file.name}</a>`);
192
180
  }
193
181
  });
194
182
  this.insertList = list.join(" \n");
195
183
  return this.insertList;
196
184
  },
197
- insert: function () {
185
+ insert: function() {
198
186
  // 关闭窗口
199
187
  this.dialogVisible = false;
200
188
 
@@ -210,17 +198,17 @@ export default {
210
198
  //移除所有选择状态
211
199
  this.resetSelectStatus();
212
200
  },
213
- resetSelectStatus: function () {
201
+ resetSelectStatus: function() {
214
202
  this.fileList.forEach((file, i) => {
215
203
  this.$set(this.fileList[i], "selected", false);
216
204
  });
217
205
  this.selectedCount = 0;
218
206
  },
219
- clear: function () {
207
+ clear: function() {
220
208
  this.$refs.uploadbox.clearFiles();
221
209
  this.fileList = [];
222
210
  },
223
- removeFile: function (fileList, uid) {
211
+ removeFile: function(fileList, uid) {
224
212
  fileList.forEach((file, i) => {
225
213
  if (file.uid == uid) {
226
214
  fileList.splice(i, 1);
@@ -228,7 +216,7 @@ export default {
228
216
  });
229
217
  },
230
218
  },
231
- mounted: function () {},
219
+ mounted: function() {},
232
220
  components: {},
233
221
  };
234
222
  </script>
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