@2kog/pkg-editor 0.0.1

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,288 @@
1
+ <template>
2
+ <div class="c-upload">
3
+ <!-- 上传触发按钮 -->
4
+ <el-button type="primary" @click="dialogVisible = true" :disabled="!enable" size="large">
5
+ <el-icon class="c-upload-trigger">
6
+ <UploadFilled />
7
+ </el-icon>
8
+ {{ btn_txt }}
9
+ </el-button>
10
+
11
+ <!-- 弹出界面 -->
12
+ <el-dialog class="c-large-dialog" title="上传" v-model="dialogVisible" @close="closeUpload">
13
+ <div class="c-upload-toolbar">
14
+ <!-- 清空按钮 -->
15
+ <el-button class="u-upload-clear" plain size="small" @click="clear"
16
+ ><el-icon> <Delete /> </el-icon>清空</el-button
17
+ >
18
+ <!-- 限制提示 -->
19
+ <div class="u-upload-tip" :title="tip" type="info" show-icon :closable="false">{{ tip }}</div>
20
+ </div>
21
+
22
+ <!-- 文件区 -->
23
+ <el-upload
24
+ list-type="picture-card"
25
+ :auto-upload="false"
26
+ :limit="limit"
27
+ multiple
28
+ :file-list="fileList"
29
+ :on-change="change"
30
+ ref="uploadbox"
31
+ :accept="accept"
32
+ :on-exceed="onExceed"
33
+ >
34
+ <template #default>
35
+ <el-icon>
36
+ <Plus />
37
+ </el-icon>
38
+ </template>
39
+
40
+ <!-- 文件项 -->
41
+ <template #file="{ file }">
42
+ <div
43
+ class="u-file-wrapper"
44
+ @click="select(file)"
45
+ :class="{
46
+ isSelected: file.selected,
47
+ disabled: file.status != 'success',
48
+ }"
49
+ v-loading="file.status === 'uploading'"
50
+ >
51
+ <span style="display: none">{{ fileList }}</span>
52
+ <!-- 图片类型 -->
53
+ <img v-if="file.is_img" class="el-upload-list__item-thumbnail u-imgbox" :src="file.url" alt />
54
+ <!-- 其他类型 -->
55
+ <div v-else class="u-filebox">
56
+ <img class="u-fileplaceholder" svg-inline src="../assets/img/file.svg" />
57
+ <span class="u-filename">{{ file.name }}</span>
58
+ </div>
59
+ <!-- 勾选角标 -->
60
+ <label v-show="file.selected" class="u-file-select-label">
61
+ <el-icon class="el-icon-upload-success el-icon-check" color="#fff">
62
+ <Check />
63
+ </el-icon>
64
+ </label>
65
+ </div>
66
+ </template>
67
+ </el-upload>
68
+
69
+ <!-- 插入按钮 -->
70
+ <template #footer>
71
+ <span class="dialog-footer">
72
+ <el-button @click="dialogVisible = false" plain>取 消</el-button>
73
+ <el-button type="primary" @click="insert">
74
+ {{ buttonTXT }}
75
+ </el-button>
76
+ </span>
77
+ </template>
78
+ </el-dialog>
79
+ </div>
80
+ </template>
81
+
82
+ <script>
83
+ import { uniqBy } from "lodash";
84
+ import { ElButton, ElDialog, ElIcon } from "element-plus";
85
+ import { Plus, UploadFilled, Delete, Check } from "@element-plus/icons-vue";
86
+ import { imgTypes, videoTypes } from "../../config/global.js";
87
+
88
+ export default {
89
+ name: "Upload",
90
+ components: {
91
+ "el-button": ElButton,
92
+ "el-dialog": ElDialog,
93
+ "el-icon": ElIcon,
94
+ Plus,
95
+ UploadFilled,
96
+ Delete,
97
+ Check,
98
+ },
99
+ props: {
100
+ // 是否启用
101
+ enable: {
102
+ type: Boolean,
103
+ default: true,
104
+ },
105
+ // 按钮文字
106
+ text: {
107
+ type: String,
108
+ },
109
+ // 仅图片上传
110
+ onlyImage: {
111
+ type: Boolean,
112
+ default: false,
113
+ },
114
+ // 上传约束提示
115
+ desc: {
116
+ type: String,
117
+ },
118
+ // 上传数量限制
119
+ limit: {
120
+ type: Number,
121
+ default: 10,
122
+ },
123
+ // 上传方法
124
+ uploadFn: {
125
+ type: Function,
126
+ required: true,
127
+ },
128
+ // CDN拼接域名
129
+ domain: {
130
+ type: String,
131
+ default: "",
132
+ },
133
+ },
134
+ data: function () {
135
+ return {
136
+ dialogVisible: false,
137
+ tip: this.desc || "一次最多同时上传10个文件(单个文件不超过20M),格式限常见的图片、文档、数据表及压缩包",
138
+ btn_txt: this.text || "上传附件",
139
+
140
+ fileList: [],
141
+ selectedCount: 0,
142
+ insertList: "",
143
+
144
+ // accept: allow_types.accept,
145
+ // sizeLimit: allow_types.sizeLimit,
146
+ };
147
+ },
148
+ watch: {
149
+ fileList: {
150
+ deep: true,
151
+ handler: function (newval) {
152
+ this.$emit("update", newval);
153
+ },
154
+ },
155
+ insertList: function (newval) {
156
+ this.$emit("htmlUpdate", newval);
157
+ },
158
+ },
159
+ computed: {
160
+ buttonTXT: function () {
161
+ return this.selectedCount ? "插 入" : "确 定";
162
+ },
163
+ },
164
+ methods: {
165
+ change: function (file) {
166
+ if (file.status != "success") {
167
+ // 判断大小
168
+ // if (file.size > this.sizeLimit) {
169
+ // this.$message.error("文件超出大小限制");
170
+ // this.removeFile(fileList, file.uid);
171
+ // return;
172
+ // }
173
+
174
+ // 分析文件类型
175
+ let ext = file.name?.toLowerCase().split(".").pop();
176
+ const is_img = imgTypes.includes(ext);
177
+ const is_video = videoTypes.includes(ext);
178
+
179
+ if (this.onlyImage && !is_img) return;
180
+
181
+ // 构建数据
182
+ let fdata = new FormData();
183
+ fdata.append("file", file.raw);
184
+
185
+ file.status = "uploading";
186
+ this.uploadFn(file.raw)
187
+ .then((res) => {
188
+ // 提醒
189
+ this.$message({
190
+ message: "上传成功",
191
+ type: "success",
192
+ });
193
+
194
+ // 修改path
195
+ file.url = res?.name && this.domain + "/" + res.name;
196
+
197
+ // 额外赋值
198
+ file.is_img = is_img;
199
+ file.is_video = is_video;
200
+ file.selected = true;
201
+
202
+ // 修改状态加入仓库
203
+ file.status = "success";
204
+ this.fileList.push(file);
205
+ this.fileList = uniqBy(this.fileList, "url");
206
+ this.selectedCount++;
207
+
208
+ this.$forceUpdate();
209
+ })
210
+ .catch((err) => {
211
+ if (err.response.data.code) {
212
+ this.$message.error(`[${err.response.data.code}] ${err.response.data.message}`);
213
+ } else {
214
+ this.$message.error("请求异常");
215
+ }
216
+
217
+ file.status = "fail";
218
+ });
219
+ }
220
+ },
221
+ select: function (file) {
222
+ if (file.status == "success") {
223
+ file.selected = !file.selected;
224
+ file.selected ? this.selectedCount++ : this.selectedCount--;
225
+ }
226
+ },
227
+ buildHTML: function () {
228
+ let list = [];
229
+ this.fileList.forEach((file) => {
230
+ if (file.selected) {
231
+ file.is_img
232
+ ? list.push(`<img src="${file.url}" />`)
233
+ : file.is_video
234
+ ? list.push(`<video src="${file.url}" controls />`)
235
+ : list.push(`<a target="_blank" href="${file.url}">${file.name}</a>`);
236
+ }
237
+ });
238
+ this.insertList = list.join(" \n");
239
+ return this.insertList;
240
+ },
241
+ insert: function () {
242
+ // 关闭窗口
243
+ this.dialogVisible = false;
244
+
245
+ //为空不执行插入
246
+ if (!this.selectedCount) return;
247
+
248
+ // 传递值
249
+ this.$emit("insert", {
250
+ list: this.fileList,
251
+ html: this.buildHTML(),
252
+ });
253
+
254
+ //移除所有选择状态
255
+ this.resetSelectStatus();
256
+ },
257
+ resetSelectStatus: function () {
258
+ this.fileList.forEach((file, i) => {
259
+ this.fileList[i].selected = false;
260
+ });
261
+ this.selectedCount = 0;
262
+ },
263
+ clear: function () {
264
+ this.$refs.uploadbox.clearFiles();
265
+ this.fileList = [];
266
+ },
267
+ removeFile: function (fileList, uid) {
268
+ fileList.forEach((file, i) => {
269
+ if (file.uid == uid) {
270
+ fileList.splice(i, 1);
271
+ }
272
+ });
273
+ },
274
+ isImage(file) {
275
+ let ext = file.name.split(".").pop();
276
+ return imgTypes.includes(ext);
277
+ },
278
+ closeUpload() {
279
+ this.fileList = [];
280
+ this.$refs.uploadbox.clearFiles();
281
+ },
282
+ },
283
+ };
284
+ </script>
285
+
286
+ <style lang="less">
287
+ @import "../assets/css/upload.less";
288
+ </style>
@@ -0,0 +1,12 @@
1
+ // 第三方UI组件
2
+ import { createApp } from "vue";
3
+ // import ElementPlus from "element-plus";
4
+ // import "element-plus/dist/index.css";
5
+ // import zhCn from "element-plus/dist/locale/zh-cn.mjs";
6
+ // app.use(ElementPlus, {
7
+ // locale: zhCn,
8
+ // });
9
+
10
+ import App from "./article.vue";
11
+ const app = createApp(App);
12
+ app.mount("#app");
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <div class="container">
3
+ <ArticleRender class="main"
4
+ :content="content"
5
+ @contentRendered="test1"
6
+ @directoryRendered="test2"
7
+
8
+ directorybox="#directory"
9
+
10
+ :linkWhitelist="linkWhitelist"
11
+ :linkStrict="linkStrict"
12
+
13
+ :cdnDomain="cdnDomain"
14
+ :iframeWhitelist="iframeWhitelist"
15
+ ></ArticleRender>
16
+ <div id="directory"></div>
17
+ </div>
18
+ </template>
19
+
20
+ <script>
21
+ import ArticleRender from "../components/Article.vue";
22
+
23
+ export default {
24
+ name: "APage",
25
+ props: [],
26
+ data: function () {
27
+ return {
28
+ content: "",
29
+
30
+ linkWhitelist: ["*.2kog.com","www.iruxu.com"],
31
+ linkStrict: false,
32
+
33
+ cdnDomain: "https://cdn.2kog.com",
34
+
35
+ iframeWhitelist: ["player.bilibili.com"],
36
+ };
37
+ },
38
+ components: {
39
+ ArticleRender,
40
+ },
41
+ methods: {
42
+ test1() {
43
+ console.log("contentRendered");
44
+ },
45
+ test2() {
46
+ console.log("directoryRendered");
47
+ },
48
+ },
49
+ async mounted() {
50
+ const res = await fetch("/demo/article_basic.html");
51
+ this.content = await res.text();
52
+ },
53
+ };
54
+ </script>
55
+
56
+ <style lang="less">
57
+ html {
58
+ padding: 20px;
59
+ }
60
+ .container{
61
+ display: flex;
62
+ gap: 20px;
63
+ #directory{
64
+ width: 200px;
65
+ flex-shrink: 0;
66
+ }
67
+ .main{
68
+ flex-grow: 1;
69
+ }
70
+
71
+ }
72
+ </style>
@@ -0,0 +1,5 @@
1
+ // 第三方UI组件
2
+ import { createApp } from "vue";
3
+ import App from "./index.vue";
4
+ const app = createApp(App);
5
+ app.mount("#app");
@@ -0,0 +1,8 @@
1
+ <template>
2
+ <div>
3
+ <ul>
4
+ <li><a href="/article">Article - 文章渲染</a></li>
5
+ <li><a href="/tinymce">Tinymce - 编辑器</a></li>
6
+ </ul>
7
+ </div>
8
+ </template>
@@ -0,0 +1,14 @@
1
+ // 第三方UI组件
2
+ import { createApp } from "vue";
3
+ import ElementPlus from "element-plus";
4
+ import "element-plus/dist/index.css";
5
+ import zhCn from "element-plus/dist/locale/zh-cn.mjs";
6
+
7
+ import T from "./tinymce.vue";
8
+ const app = createApp(T);
9
+
10
+ app.use(ElementPlus, {
11
+ locale: zhCn,
12
+ });
13
+
14
+ app.mount("#app");
@@ -0,0 +1,46 @@
1
+ <template>
2
+ <div>
3
+ <Tinymce
4
+ v-model="content"
5
+
6
+ :tinymceUploadFn="uploadFn"
7
+ :tinymceAssetsDomain="domain"
8
+
9
+ :attachmentEnable="true"
10
+ :attachmentUploadFn="uploadFn"
11
+ :attachmentCdnDomain="domain"
12
+ />
13
+ </div>
14
+ </template>
15
+
16
+ <script>
17
+ import Tinymce from "../components/Tinymce.vue";
18
+ export default {
19
+ name: "T",
20
+ data: function () {
21
+ return {
22
+ content: "",
23
+ domain: "https://cdn.2kog.com",
24
+ uploadUrl: "https://dev.api.iruxu.com/api/cms/admin/upload/tinymce",
25
+ };
26
+ },
27
+ components: {
28
+ Tinymce,
29
+ },
30
+ methods:{
31
+ uploadFn(){
32
+
33
+ }
34
+ },
35
+ async mounted() {
36
+ const res = await fetch("/demo/article_basic.html");
37
+ this.content = await res.text();
38
+ },
39
+ };
40
+ </script>
41
+
42
+ <style lang="less">
43
+ html {
44
+ padding: 20px;
45
+ }
46
+ </style>