|
@@ -0,0 +1,370 @@
|
|
|
|
+let editIdIndex = 0
|
|
|
|
+
|
|
|
|
+function deepClone(obj, cache = new WeakMap()) {
|
|
|
|
+ if (typeof obj !== 'object') return obj // 普通类型,直接返回
|
|
|
|
+ if (obj === null) return obj
|
|
|
|
+ if (cache.get(obj)) return cache.get(obj) // 防止循环引用,程序进入死循环
|
|
|
|
+ if (obj instanceof Date) return new Date(obj)
|
|
|
|
+ if (obj instanceof RegExp) return new RegExp(obj)
|
|
|
|
+
|
|
|
|
+ // 找到所属原型上的constructor,所属原型上的constructor指向当前对象的构造函数
|
|
|
|
+ let cloneObj = new obj.constructor()
|
|
|
|
+ cache.set(obj, cloneObj) // 缓存拷贝的对象,用于处理循环引用的情况
|
|
|
|
+ for (let key in obj) {
|
|
|
|
+ if (obj.hasOwnProperty(key)) {
|
|
|
|
+ cloneObj[key] = deepClone(obj[key], cache) // 递归拷贝
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return cloneObj
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const contentEdit = {
|
|
|
|
+ name: 'content-edit',
|
|
|
|
+ template: `
|
|
|
|
+ <div class="el-input" :id="editId">
|
|
|
|
+ <div class="bk-toolbar" @click="handleToolBarClick" v-show="isFocus"></div>
|
|
|
|
+ <div class="bk-bubble" @click="handleBubbleClick" v-if="bubbleNum>0">{{bubbleNum}}</div>
|
|
|
|
+ <div class="el-input__inner bk-input" contenteditable="true" @focus="onFocus" @blur="onBlur" @input="onInput"></div>
|
|
|
|
+ <ul class="bk-literature" v-show="showBubble && bubbleNum>0">
|
|
|
|
+ <li v-for="item in refList" :key="item.ctrlId">
|
|
|
|
+ <div>{{item | reference}}</div>
|
|
|
|
+ <div class="bk-ref-control">
|
|
|
|
+ <span class="edit" @click="$emit('reference-edit', item)"></span>
|
|
|
|
+ <span class="delete" @click="$emit('reference-delete', item)"></span>
|
|
|
|
+ </div>
|
|
|
|
+ </li>
|
|
|
|
+ </ul>
|
|
|
|
+ <div class="bk-mask" @click="showBubble=false" v-if="showBubble && bubbleNum>0"></div>
|
|
|
|
+ </div>
|
|
|
|
+ `,
|
|
|
|
+ filters: {
|
|
|
|
+ reference(data) {
|
|
|
|
+ if (data.referenceType === 1) return data.articleName
|
|
|
|
+ if (data.referenceType === 2) return data.author + '.' + data.workName
|
|
|
|
+ return data.referenceDescription + '.' + data.imageDescription
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ props: {
|
|
|
|
+ referenceList: {
|
|
|
|
+ type: Array,
|
|
|
|
+ default: () => []
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ computed: {
|
|
|
|
+ refList() {
|
|
|
|
+ if (!this.sm) return []
|
|
|
|
+ return this.referenceList.filter(item => this.sm.ctridList.includes(item.ctrlId))
|
|
|
|
+ },
|
|
|
|
+ bubbleNum() {
|
|
|
|
+ return this.refList.length
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ data() {
|
|
|
|
+ return {
|
|
|
|
+ editId: `myEdit` + ++editIdIndex,
|
|
|
|
+ isFocus: false,
|
|
|
|
+ showBubble: false,
|
|
|
|
+ sm: null
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ mounted() {
|
|
|
|
+ this.initEdit();
|
|
|
|
+ },
|
|
|
|
+ methods: {
|
|
|
|
+ initEdit() {
|
|
|
|
+ this.sm = new SelectionManager({
|
|
|
|
+ el: `#${this.editId} .bk-input`,
|
|
|
|
+ toolbar: `#${this.editId} .bk-toolbar`,
|
|
|
|
+ hiddenMark: false
|
|
|
|
+ })
|
|
|
|
+ console.log(this.sm);
|
|
|
|
+ },
|
|
|
|
+ onFocus() {
|
|
|
|
+ this.isFocus = true
|
|
|
|
+ },
|
|
|
|
+ onBlur() {
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ this.isFocus = false
|
|
|
|
+ }, 200)
|
|
|
|
+ },
|
|
|
|
+ handleToolBarClick() {
|
|
|
|
+ this.$emit('toolbar', this.sm)
|
|
|
|
+ },
|
|
|
|
+ onInput(e) {
|
|
|
|
+ this.$emit('change', e.target.innerHTML);
|
|
|
|
+ },
|
|
|
|
+ handleBubbleClick() {
|
|
|
|
+ if (this.bubbleNum === 0) return
|
|
|
|
+ this.showBubble = true
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const ImageUpload = {
|
|
|
|
+ name: 'file-upload',
|
|
|
|
+ template: `
|
|
|
|
+ <div>
|
|
|
|
+ <input type="file" v-show="false" ref="fileInput" @change="handleFileChange" accept=".png,.jpg,.jpeg,.gif,.mp4"/>
|
|
|
|
+ <div class="bk-file-upload">
|
|
|
|
+ <template v-if="limit > list.length">
|
|
|
|
+ <div class="bk-upload" @click="handleChooseFile"><div>+</div><div>添加图片</div></div>
|
|
|
|
+ </template>
|
|
|
|
+ <ul class="bk-file-wrap" v-if="list.length > 0">
|
|
|
|
+ <template v-for="(item, index) in list">
|
|
|
|
+ <li :key="index">
|
|
|
|
+ <span @click="handleRemove(item)">×</span>
|
|
|
|
+ <img :src="item.url" v-if="isImage(item)"/>
|
|
|
|
+ <video :src="item.url" v-else></video>
|
|
|
|
+ </li>
|
|
|
|
+ </template>
|
|
|
|
+ </ul>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ `,
|
|
|
|
+ props: {
|
|
|
|
+ limit: {
|
|
|
|
+ type: Number,
|
|
|
|
+ default: 1
|
|
|
|
+ },
|
|
|
|
+ list: {
|
|
|
|
+ type: Array,
|
|
|
|
+ default: () => []
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ data() {
|
|
|
|
+ return {
|
|
|
|
+ fileList: []
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ methods: {
|
|
|
|
+ isImage(file) {
|
|
|
|
+ return file.type.indexOf('image') > -1
|
|
|
|
+ },
|
|
|
|
+ handleChooseFile() {
|
|
|
|
+ this.$refs.fileInput.click();
|
|
|
|
+ },
|
|
|
|
+ handleRemove(file) {
|
|
|
|
+ this.$emit('remove', file)
|
|
|
|
+ },
|
|
|
|
+ generageRowfile(file) {
|
|
|
|
+ return {
|
|
|
|
+ uuid: uuidv4(),
|
|
|
|
+ fileName: file.name,
|
|
|
|
+ size: file.size,
|
|
|
|
+ type: file.type,
|
|
|
|
+ file: file,
|
|
|
|
+ percentage: 0,
|
|
|
|
+ status: -1, // 0 上次失败 1 上传成功 2 上传中 -1 待上传
|
|
|
|
+ response: null,
|
|
|
|
+ url: '',
|
|
|
|
+ checkpoint: null
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ handleFileChange(e) {
|
|
|
|
+ const self = this
|
|
|
|
+ let flag = true
|
|
|
|
+ this.fileList = Array.from(e.target.files).map(function (file) {
|
|
|
|
+ if (file.size > 1024 * 1024 * 50) {
|
|
|
|
+ CAIMEI.dialog('请上传50MB以内的图片或视频', false);
|
|
|
|
+ flag = false
|
|
|
|
+ }
|
|
|
|
+ return self.generageRowfile(file)
|
|
|
|
+ })
|
|
|
|
+ if (!flag) return
|
|
|
|
+ this.onUploadFile(this.fileList)
|
|
|
|
+ },
|
|
|
|
+ // 文件上传成功
|
|
|
|
+ onUploadSuccess(file, response, url) {
|
|
|
|
+ file.response = response
|
|
|
|
+ file.status = 1
|
|
|
|
+ file.url = url
|
|
|
|
+ this.$emit('upload-success', this.fileList)
|
|
|
|
+ },
|
|
|
|
+ // 文件上传失败
|
|
|
|
+ onUploadError(file, error) {
|
|
|
|
+ file.response = error
|
|
|
|
+ file.status = 0
|
|
|
|
+ this.$emit('upload-error', this.fileList)
|
|
|
|
+ },
|
|
|
|
+ // 文件上传进度条
|
|
|
|
+ onProgress(file, response) {
|
|
|
|
+ const {p, cpt, res} = response
|
|
|
|
+ file.percentage = p * 100
|
|
|
|
+ file.checkpoint = cpt
|
|
|
|
+ file.response = res
|
|
|
|
+ file.status = 2
|
|
|
|
+ this.$emit('upload-progress', this.fileList)
|
|
|
|
+ },
|
|
|
|
+ // 上传文件
|
|
|
|
+ onUploadFile(fileList) {
|
|
|
|
+ upload(fileList, {
|
|
|
|
+ success: this.onUploadSuccess,
|
|
|
|
+ faild: this.onUploadError,
|
|
|
|
+ progress: this.onProgress
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const edit = new Vue({
|
|
|
|
+ el: '#edit',
|
|
|
|
+ components: {
|
|
|
|
+ [contentEdit.name]: contentEdit,
|
|
|
|
+ [ImageUpload.name]: ImageUpload
|
|
|
|
+ },
|
|
|
|
+ filters: {
|
|
|
|
+ reference(data) {
|
|
|
|
+ if (data.referenceType === 1) return data.articleName
|
|
|
|
+ if (data.referenceType === 2) return data.author + '.' + data.workName
|
|
|
|
+ return data.referenceDescription + '.' + data.imageDescription
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ data: {
|
|
|
|
+ isPC: window.innerWidth > 768,
|
|
|
|
+ referenceType: 1,
|
|
|
|
+ dateType: 1,
|
|
|
|
+ referenceDialog: false,
|
|
|
|
+ referenceEditType: 'add',
|
|
|
|
+ sm: null,
|
|
|
|
+ formData: {
|
|
|
|
+ id: '', // 词条id
|
|
|
|
+ name: '', // 词条名称
|
|
|
|
+ alias: '', // 义项名
|
|
|
|
+ description: '', // 词条概述
|
|
|
|
+ img: '', // 头图地址
|
|
|
|
+ typeId: '', // 分类id
|
|
|
|
+ seoKeyword: '', // seo关键字
|
|
|
|
+ status: 0, // 状态0保存草稿箱 1已发布
|
|
|
|
+ imageList: [], // 概述图册集合
|
|
|
|
+ infoList: [], // 信息栏集合
|
|
|
|
+ referenceList: [], // 参考资料集合
|
|
|
|
+ videoList: [], // 视频集合
|
|
|
|
+ },
|
|
|
|
+ rules: {
|
|
|
|
+ name: [{required: true, message: '词条名称不能为空', trigger: ['blur']}],
|
|
|
|
+ alias: [{required: true, message: '词条名称不能为空', trigger: ['change']}],
|
|
|
|
+ description: [{required: true, message: '词条名称不能为空', trigger: ['change']}],
|
|
|
|
+ img: [{required: true, message: '请上传词条头图', trigger: ['change']}],
|
|
|
|
+ },
|
|
|
|
+ referenceList: [{
|
|
|
|
+ id: 1,
|
|
|
|
+ ctrlId: 'SDAJKLJ4642', // 关联id
|
|
|
|
+ /* (网络资料) */
|
|
|
|
+ referenceType: 1, // '参考类型资料(1.网络;2.著作;3.其他)'
|
|
|
|
+ website: '网址',
|
|
|
|
+ articleName: '文章名称',
|
|
|
|
+ websiteName: '网址名称',
|
|
|
|
+ publishTimeStr: '发表时间',
|
|
|
|
+ acitationTimeStr: '引文时间',
|
|
|
|
+ /* 著作资料)*/
|
|
|
|
+ author: '作者',
|
|
|
|
+ workName: '著作名',
|
|
|
|
+ publicationPlace: '出版地',
|
|
|
|
+ press: '出版社',
|
|
|
|
+ publicationYearStr: '出版年',
|
|
|
|
+ acitationWeb: '引文编码',
|
|
|
|
+ /* (其他资料) */
|
|
|
|
+ referenceDescription: '参考资料说明',
|
|
|
|
+ imageDescription: '图片描述',
|
|
|
|
+ imageUrl: '图片地址',
|
|
|
|
+ entryId: '词条id',
|
|
|
|
+ entryType: '词条类型(先不传)'
|
|
|
|
+ }],
|
|
|
|
+ referenceData: {
|
|
|
|
+ id: 1,
|
|
|
|
+ ctrlId: '', // 关联id
|
|
|
|
+ /* (网络资料) */
|
|
|
|
+ referenceType: 1, // '参考类型资料(1.网络;2.著作;3.其他)'
|
|
|
|
+ website: '网址',
|
|
|
|
+ articleName: '文章名称',
|
|
|
|
+ websiteName: '网址名称',
|
|
|
|
+ publishTimeStr: '发表时间',
|
|
|
|
+ acitationTimeStr: '引文时间',
|
|
|
|
+ /* 著作资料)*/
|
|
|
|
+ author: '作者',
|
|
|
|
+ workName: '著作名',
|
|
|
|
+ publicationPlace: '出版地',
|
|
|
|
+ press: '出版社',
|
|
|
|
+ publicationYearStr: '出版年',
|
|
|
|
+ acitationWeb: '引文编码',
|
|
|
|
+ /* (其他资料) */
|
|
|
|
+ referenceDescription: '参考资料说明',
|
|
|
|
+ imageDescription: '图片描述',
|
|
|
|
+ imageUrl: '图片地址',
|
|
|
|
+ entryId: '词条id',
|
|
|
|
+ entryType: '词条类型(先不传)'
|
|
|
|
+ },
|
|
|
|
+ referenceRule: {
|
|
|
|
+ referenceType: [{required: true, message: '请选择参考类型资料', trigger: ['change']}],
|
|
|
|
+ website: [{required: true, message: '请输入网址', trigger: ['blur']}],
|
|
|
|
+ articleName: [{required: true, message: '请输入文章名字', trigger: ['blur']}],
|
|
|
|
+ websiteName: [{required: true, message: '请输入网站名称', trigger: ['blur']}],
|
|
|
|
+ author: [{required: true, message: '请输入作者名称', trigger: ['blur']}],
|
|
|
|
+ workName: [{required: true, message: '请输入著作名', trigger: ['blur']}],
|
|
|
|
+ press: [{required: true, message: '请输入出版社', trigger: ['blur']}],
|
|
|
|
+ publicationYearStr: [{required: true, message: '请输入出版年', trigger: ['blur']}],
|
|
|
|
+ referenceDescription: [{required: true, message: '请输入参考资料说明', trigger: ['blur']}],
|
|
|
|
+ },
|
|
|
|
+ fileList: [],
|
|
|
|
+ coverList: []
|
|
|
|
+ },
|
|
|
|
+ methods: {
|
|
|
|
+ // ref dialog 隐藏
|
|
|
|
+ handleRefDialogClose() {
|
|
|
|
+ this.referenceDialog = false
|
|
|
|
+ this.referenceType = 1
|
|
|
|
+ },
|
|
|
|
+ // 提交确定ref
|
|
|
|
+ handleConfirmReference() {
|
|
|
|
+ if (this.referenceEditType === 'add') {
|
|
|
|
+ this.referenceData.ctrlId = this.sm.parcel()
|
|
|
|
+ this.referenceList.push(deepClone(this.referenceData))
|
|
|
|
+ } else {
|
|
|
|
+ const index = this.referenceList.findIndex(ref => ref.ctrlId === this.referenceData.ctrlId);
|
|
|
|
+ this.referenceList.splice(index, 1, deepClone(this.referenceData))
|
|
|
|
+ }
|
|
|
|
+ this.referenceDialog = false
|
|
|
|
+ this.referenceEditType = 'add'
|
|
|
|
+ },
|
|
|
|
+ handleSelectReference(item) {
|
|
|
|
+ this.referenceDialog = false
|
|
|
|
+ this.sm.parcel(item.ctrlId)
|
|
|
|
+ },
|
|
|
|
+ // ref tab 切换
|
|
|
|
+ handleTabChange(index) {
|
|
|
|
+ this.referenceType = index;
|
|
|
|
+ },
|
|
|
|
+ handleToolbarClick(e) {
|
|
|
|
+ this.sm = e
|
|
|
|
+ this.referenceDialog = true
|
|
|
|
+ },
|
|
|
|
+ handleReferenceEdit(item) {
|
|
|
|
+ this.referenceEditType = 'edit'
|
|
|
|
+ this.referenceData = item
|
|
|
|
+ this.referenceDialog = true
|
|
|
|
+ },
|
|
|
|
+ handleReferenceDelete(item) {
|
|
|
|
+ console.log(item);
|
|
|
|
+ this.sm.markRemove(item.ctrlId);
|
|
|
|
+ },
|
|
|
|
+ // 文件上传成功
|
|
|
|
+ handleUploadSuccess(fileList) {
|
|
|
|
+ this.fileList = [...this.fileList, ...fileList]
|
|
|
|
+ },
|
|
|
|
+ // 文件移除
|
|
|
|
+ handleFileRemove(file) {
|
|
|
|
+ this.fileList = this.fileList.filter(item => item.uuid !== file.uuid);
|
|
|
|
+ },
|
|
|
|
+ // 封面上传成功
|
|
|
|
+ handleCoverUploadSuccess(fileList) {
|
|
|
|
+ this.coverList = fileList
|
|
|
|
+ this.formData.img = fileList[0].url
|
|
|
|
+ },
|
|
|
|
+ // 封面删除
|
|
|
|
+ handleCoverRemove(file) {
|
|
|
|
+ this.formData.img = ''
|
|
|
|
+ this.coverList = this.coverList.filter(item => item.uuid !== file.uuid);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+})
|