NEZ-2297 feat:asset评论内容支持富文本

This commit is contained in:
zyh
2022-10-28 10:33:00 +08:00
parent 2261e71560
commit 8c3506ec58
6 changed files with 156 additions and 97 deletions

View File

@@ -273,8 +273,11 @@
height: 100%; height: 100%;
} }
.textarea-box { .textarea-box {
// border: 1px solid $--border-color-light;
background-color: $--background-color-empty;
.textarea-small.el-textarea { .textarea-small.el-textarea {
height: 100% !important; height: 100% !important;
border: 1px solid $--border-color-light;
} }
.textarea-big.el-textarea { .textarea-big.el-textarea {
height: calc(100% - 40px) !important; height: calc(100% - 40px) !important;
@@ -283,9 +286,31 @@
height: 100%; height: 100%;
border-color: transparent; border-color: transparent;
} }
margin-bottom: 10px; }
.comment-editor{
border: 1px solid $--border-color-light; border: 1px solid $--border-color-light;
background-color: $--background-color-empty; display: flex;
flex-direction: column;
.ql-toolbar.ql-snow{
border-top: unset !important;
border-left: unset !important;
border-right: unset !important;
}
.editor-core{
height: auto !important;
flex: 1;
overflow: hidden;
}
.ql-container.ql-snow {
border: unset !important
}
.text-too-long{
display: none;
}
.ql-editor.ql-blank::before{
font-style: normal;
color: $--color-text-placeholder;
}
} }
#assetCommentTable { #assetCommentTable {
height: calc(100% - 25px) !important; height: calc(100% - 25px) !important;

View File

@@ -330,7 +330,7 @@ export default {
} }
this.scrollTopTimer = setTimeout(() => { this.scrollTopTimer = setTimeout(() => {
this.copyDataList.forEach(item => { this.copyDataList.forEach(item => {
if (this.$refs['chart' + item.id] && this.$refs['chart' + item.id][0].$refs.chart && this.$refs['chart' + item.id][0].$refs.chart.$refs['chart' + item.id]) { if (this.$refs['chart' + item.id] && this.$refs['chart' + item.id][0] && this.$refs['chart' + item.id][0].$refs.chart && this.$refs['chart' + item.id][0].$refs.chart.$refs['chart' + item.id]) {
if (this.$refs['chart' + item.id][0].$refs.chart.$refs['chart' + item.id].tooltip && this.$refs['chart' + item.id][0].$refs.chart.$refs['chart' + item.id].tooltip.show) { if (this.$refs['chart' + item.id][0].$refs.chart.$refs['chart' + item.id].tooltip && this.$refs['chart' + item.id][0].$refs.chart.$refs['chart' + item.id].tooltip.show) {
this.$refs['chart' + item.id][0].$refs.chart.$refs['chart' + item.id].tooltip.show = false this.$refs['chart' + item.id][0].$refs.chart.$refs['chart' + item.id].tooltip.show = false
} }

View File

@@ -1,17 +1,18 @@
<template> <template>
<div style="position: relative" class="rich-text-editor" :class="{'rich-text-editor-error':tooLong}"> <div style="position: relative" class="rich-text-editor" :class="{'rich-text-editor-error':tooLong}">
<div :id="id" class="editor-core" ref="editor" style="height:350px;"></div> <div :id="id" class="editor-core" ref="editor" style="height:350px;"></div>
<div class="text-too-long" v-if="tooLong">{{$t('validate.tooLong')}}</div> <slot></slot>
<el-upload <div class="text-too-long" v-if="tooLong">{{$t('validate.tooLong')}}</div>
action="" <el-upload
:auto-upload="false" action=""
accept=".jpg,.png" :auto-upload="false"
:on-change="uploadChange" accept=".jpg,.png"
style="display: none" :on-change="uploadChange"
ref="upload" style="display: none"
> ref="upload"
</el-upload> >
</div> </el-upload>
</div>
</template> </template>
<script> <script>
@@ -20,7 +21,11 @@ import Quill from 'quill'
export default { export default {
name: 'richTextEditor', name: 'richTextEditor',
props: { props: {
editData: String editData: String,
placeholder: {
default: '',
type: String
}
}, },
data () { data () {
return { return {
@@ -38,7 +43,8 @@ export default {
[{ align: [] }], [{ align: [] }],
['image'] // 上传图片 ['image'] // 上传图片
] ]
} },
placeholder: this.placeholder
}, },
maxLength: 0, // 记录最大长度 maxLength: 0, // 记录最大长度
tooLong: false tooLong: false
@@ -72,13 +78,15 @@ export default {
this.quill.on('text-change', function (delta, oldDelta, source) { this.quill.on('text-change', function (delta, oldDelta, source) {
const length = $self.getHtml().length const length = $self.getHtml().length
if (length > 60000) { if (length > 60000) {
$self.quill.deleteText($self.maxLength - 1, $self.quill.getText().length - 1, 'api') const start = $self.maxLength > 0 ? $self.maxLength - 1 : 0
const end = $self.quill.getText().length > 1 ? $self.quill.getText().length - 1 : 1
$self.quill.deleteText(start, end, 'api')
$self.tooLong = true $self.tooLong = true
} else { } else {
$self.maxLength = $self.quill.getText().length $self.maxLength = $self.quill.getText().length
$self.tooLong = false $self.tooLong = false
} }
$self.$emit('textChange', $self.quill.root.innerHTML) $self.$emit('textChange', $self.quill.root.innerHTML, $self.quill.getText())
}) })
this.$nextTick(() => { this.$nextTick(() => {
this.$emit('after-init') this.$emit('after-init')
@@ -108,7 +116,6 @@ export default {
// 图片小于4M // 图片小于4M
const isLt4M = (file.size / 1024 / 1024) < 4 const isLt4M = (file.size / 1024 / 1024) < 4
if (isLt4M) { if (isLt4M) {
console.log(this.$axios.defaults.baseURL + 'file/download/')
const form = new FormData() const form = new FormData()
form.append('file', file.raw) form.append('file', file.raw)
const res = await this.$post('/file/upload', form, { 'Content-Type': 'multipart/form-data' }) const res = await this.$post('/file/upload', form, { 'Content-Type': 'multipart/form-data' })
@@ -158,14 +165,13 @@ export default {
.rich-text-editor-error { .rich-text-editor-error {
border: 1px solid #F56C6C; border: 1px solid #F56C6C;
} }
.rich-text-editor-error .ql-container.ql-snow {
border: unset !important
}
.rich-text-editor-error .ql-toolbar.ql-snow{ .rich-text-editor-error .ql-toolbar.ql-snow{
border-top: unset !important; border-top: unset !important;
border-left: unset !important; border-left: unset !important;
border-right: unset !important; border-right: unset !important;
} }
.rich-text-editor-error .ql-container.ql-snow {
border: unset !important
}
</style> </style>

View File

@@ -1,8 +1,8 @@
<template> <template>
<nz-bottom-data-list <nz-bottom-data-list
:showTitle='showTitle' :showTitle='showTitle'
:obj='obj' :obj='obj'
id="commentBottomTab" id="commentBottomTab"
:api="url" :api="url"
:custom-table-title.sync="tools.customTableTitle" :custom-table-title.sync="tools.customTableTitle"
:tabs="tabs" :tabs="tabs"
@@ -13,35 +13,38 @@
> >
<template v-slot:title><span :title="obj.name">{{obj.name}}</span></template> <template v-slot:title><span :title="obj.name">{{obj.name}}</span></template>
<template v-slot> <template v-slot>
<div class="textarea-box" :style="{height: rowBox ?'122px':'33px'}"> <div class="textarea-box" :style="{height: rowBox ?'184px':'33px','margin-bottom': '10px'}">
<el-input <el-input
v-if="!rowBox"
type="textarea" type="textarea"
:class="rowBox ? 'textarea-big' : 'textarea-small'" class="textarea-small"
:placeholder="$t('overall.addComment') + '...'" :placeholder="$t('overall.addComment') + '...'"
v-model="editComment.content"
@focus="stageZoomIn"> @focus="stageZoomIn">
</el-input> </el-input>
<div class="textarea-button" v-if="rowBox"> <rich-text-editor v-else class="comment-editor" ref="richTextEditor" :placeholder="$t('overall.addComment') + '...'" :edit-data="editComment.html" @textChange="textChange(arguments)" style="height:100%;">
<button class="nz-btn nz-btn-size-mini nz-btn-style-normal" style="margin-right: 10px" type="button" @click="save(editComment,false)"><span>{{$t('overall.save')}}</span></button> <div class="textarea-button">
<button class="nz-btn nz-btn-size-mini nz-btn-style-light" type="button" @click="cancel()"><span>{{$t('overall.cancel')}}</span></button> <button class="nz-btn nz-btn-size-mini nz-btn-style-normal" style="margin-right: 10px" type="button" @click="save(editComment,false)"><span>{{$t('overall.save')}}</span></button>
</div> <button class="nz-btn nz-btn-size-mini nz-btn-style-light" type="button" @click="cancel()"><span>{{$t('overall.cancel')}}</span></button>
</div>
</rich-text-editor>
</div> </div>
<div :style="{height: rowBox ?'calc(100% - 124px)':'calc(100% - 35px)'}"> <div :style="{height: rowBox ?'calc(100% - 184px)':'calc(100% - 35px)'}">
<assetCommentTable <assetCommentTable
ref="dataTable" ref="dataTable"
:orderByFa="'id'" :orderByFa="'id'"
v-my-loading="tools.loading" v-my-loading="tools.loading"
:loading="tools.loading" :loading="tools.loading"
:api="url" :api="url"
:custom-table-title="tools.customTableTitle" :custom-table-title="tools.customTableTitle"
:height="subTableHeight" :height="subTableHeight"
:table-data="tableData" :table-data="tableData"
:terminaLogTab="true" :terminaLogTab="true"
@del="del" @del="del"
@save="save" @save="save"
@orderBy="tableDataSort" @orderBy="tableDataSort"
@reload="getTableData" @reload="getTableData"
@selectionChange="selectionChange"></assetCommentTable> @selectionChange="selectionChange">
</assetCommentTable>
</div> </div>
</template> </template>
<template v-slot:pagination> <template v-slot:pagination>
@@ -56,13 +59,15 @@ import subDataListMixin from '@/components/common/mixin/subDataList'
import nzBottomDataList from '@/components/common/bottomBox/nzBottomDataList' import nzBottomDataList from '@/components/common/bottomBox/nzBottomDataList'
import assetCommentTable from '@/components/common/table/asset/assetCommentTable' import assetCommentTable from '@/components/common/table/asset/assetCommentTable'
import detailViewRightMixin from '@/components/common/mixin/detailViewRightMixin' import detailViewRightMixin from '@/components/common/mixin/detailViewRightMixin'
import richTextEditor from '@/components/chart/richTextEditor'
export default { export default {
name: 'networkBottomTab', name: 'commentsBottomTab',
mixins: [dataListMixin, subDataListMixin, detailViewRightMixin], mixins: [dataListMixin, subDataListMixin, detailViewRightMixin],
components: { components: {
nzBottomDataList, nzBottomDataList,
assetCommentTable assetCommentTable,
richTextEditor
}, },
props: { props: {
obj: Object, obj: Object,
@@ -85,12 +90,18 @@ export default {
editComment: { editComment: {
id: '', id: '',
content: '', content: '',
html: '',
assetId: '', assetId: '',
replyId: '' replyId: ''
} }
} }
}, },
methods: { methods: {
textChange (val) {
const html = `<div class="editor-core ql-container ql-snow"><div class="ql-editor">${val[0]}</div></div>`
this.editComment.html = html
this.editComment.content = val[1].substr(0, val[1].length - 1)
},
getTableData () { getTableData () {
this.$set(this.searchLabel, 'pageSize', '-1') this.$set(this.searchLabel, 'pageSize', '-1')
this.$set(this.searchLabel, 'assetId', this.obj.id) this.$set(this.searchLabel, 'assetId', this.obj.id)
@@ -107,11 +118,15 @@ export default {
// 输入框聚焦事件 // 输入框聚焦事件
stageZoomIn () { stageZoomIn () {
this.rowBox = true this.rowBox = true
this.$nextTick(() => {
// 聚焦
this.$refs.richTextEditor.quill.focus()
})
}, },
// 取消事件 // 取消事件
cancel () { cancel () {
this.editComment.content = '' this.editComment.content = ''
this.editComment.id = '' this.editComment.html = ''
this.rowBox = false this.rowBox = false
}, },
save (data, flag) { save (data, flag) {
@@ -157,15 +172,13 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.textarea-box >>>.el-textarea__inner { .textarea-box >>>.el-textarea__inner {
resize: none; resize: none;
} }
.textarea-box{ .comment-editor>>>.textarea-button{
position: relative; display: flex;
} justify-content: flex-end;
.textarea-button{ padding-bottom: 10px;
position: absolute; padding-right: 10px;
bottom: 10px;
right: 10px;
.nz-btn.nz-btn-size-mini{ .nz-btn.nz-btn-size-mini{
width: 74px; width: 74px;
height: 30px; height: 30px;

View File

@@ -408,6 +408,7 @@ export default {
textChange (val) { textChange (val) {
const html = `<div class="editor-core ql-container ql-snow"><div class="ql-editor">${val}</div></div>` const html = `<div class="editor-core ql-container ql-snow"><div class="ql-editor">${val}</div></div>`
this.chartConfig.param.text = html this.chartConfig.param.text = html
this.$refs.chartForm.validateField('param.text')
this.change() this.change()
} }
}, },

View File

@@ -10,11 +10,9 @@
@header-dragend="dragend" @header-dragend="dragend"
@sort-change="tableDataSort" @sort-change="tableDataSort"
@selection-change="selectionChange" @selection-change="selectionChange"
@row-dblclick="(row)=>{queryMessage(row)}"
> >
<el-table-column <el-table-column
v-for="(item, index) in customTableTitle" v-for="(item, index) in customTableTitle"
v-if="item.show"
:key="`col-${index}`" :key="`col-${index}`"
:fixed="item.fixed" :fixed="item.fixed"
:label="item.label" :label="item.label"
@@ -33,26 +31,23 @@
</template> </template>
<template slot-scope="scope" :column="item"> <template slot-scope="scope" :column="item">
<div class="descriptions-info"> <div class="descriptions-info">
<div> <div>
<i class="nz-icon nz-icon-user" style="margin-right: 4px;"></i> <i class="nz-icon nz-icon-user" style="margin-right: 4px;"></i>
<span class="descriptions-name">{{scope.row.createUser.name}}</span> <span class="descriptions-name">{{scope.row.createUser.name}}</span>
<span class="descriptions-name-small">@{{scope.row.createUser.name}}</span> <span class="descriptions-name-small">@{{scope.row.createUser.name}}</span>
<span class="descriptions-time">{{momentTz(scope.row.cts)}}</span> <span class="descriptions-time">{{momentTz(scope.row.cts)}}</span>
<i v-if="scope.row.createUser.id == userId" class="nz-icon nz-icon-edit descriptions-button" @click="edit(scope.row.id)" :title="$t('overall.edit')" style="margin-right: 18px;cursor: pointer;"></i> <i v-if="scope.row.createUser.id == userId" class="nz-icon nz-icon-edit descriptions-button" @click="edit(scope.row.id)" :title="$t('overall.edit')" style="margin-right: 18px;cursor: pointer;"></i>
<i v-if="scope.row.createUser.id == userId" class="nz-icon nz-icon-delete descriptions-button" @click="$emit('del', scope.row)" :title="$t('overall.delete')" style="cursor: pointer;"></i> <i v-if="scope.row.createUser.id == userId" class="nz-icon nz-icon-delete descriptions-button" @click="$emit('del', scope.row)" :title="$t('overall.delete')" style="cursor: pointer;"></i>
</div> </div>
<div v-if="editContentId == scope.row.id" class="textarea-box" style="height:122px;margin-top: 5px;margin-bottom: 0px;"> <div v-if="editContentId == scope.row.id" class="textarea-box" style="height:184px;margin-top: 5px;">
<el-input <rich-text-editor class="comment-editor" ref="richTextEditor" :edit-data="editComment.html" @textChange="textChange(arguments)" style="height:100%;">
type="textarea" <div class="textarea-button">
style="height:calc(100% - 40px)"
v-model="editComment.content">
</el-input>
<div class="textarea-button">
<button class="nz-btn nz-btn-size-mini nz-btn-style-normal" style="margin-right: 10px" type="button" @click="$emit('save', editComment,true)"><span>{{$t('overall.save')}}</span></button> <button class="nz-btn nz-btn-size-mini nz-btn-style-normal" style="margin-right: 10px" type="button" @click="$emit('save', editComment,true)"><span>{{$t('overall.save')}}</span></button>
<button class="nz-btn nz-btn-size-mini nz-btn-style-light" type="button" @click="cancelEdit()"><span>{{$t('overall.cancel')}}</span></button> <button class="nz-btn nz-btn-size-mini nz-btn-style-light" type="button" @click="cancelEdit()"><span>{{$t('overall.cancel')}}</span></button>
</div> </div>
</div> </rich-text-editor>
<div v-else class="descriptions-content" >{{scope.row.content}}</div> </div>
<div v-else class="descriptions-content" v-html="scope.row.html"></div>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@@ -70,9 +65,13 @@
<script> <script>
import table from '@/components/common/mixin/table' import table from '@/components/common/mixin/table'
import richTextEditor from '@/components/chart/richTextEditor'
export default { export default {
name: 'assetCommentTable', name: 'assetCommentTable',
mixins: [table], mixins: [table],
components: {
richTextEditor
},
props: { props: {
loading: Boolean loading: Boolean
}, },
@@ -83,54 +82,69 @@ export default {
editComment: { editComment: {
id: '', id: '',
content: '', content: '',
html: '',
assetId: '', assetId: '',
replyId: '' replyId: ''
}, },
tableTitle: [ tableTitle: [
{ {
label: 'ID', label: 'ID',
prop: 'id', prop: 'id'
show: true }
}], ],
// 当前用户id // 当前用户id
userId: localStorage.getItem('nz-user-id') userId: localStorage.getItem('nz-user-id')
} }
}, },
methods: { methods: {
textChange (val) {
const html = `<div class="editor-core ql-container ql-snow"><div class="ql-editor">${val[0]}</div></div>`
this.editComment.html = html
this.editComment.content = val[1].substr(0, val[1].length - 1)
},
// 修改 // 修改
async edit (id) { async edit (id) {
if (this.editContentId == id) return
const response = await this.$get('asset/comment/' + id) const response = await this.$get('asset/comment/' + id)
if (response.code === 200) { if (response.code === 200) {
this.editComment = { this.editComment = {
id: response.data.id, id: response.data.id,
content: response.data.content, content: response.data.content,
html: response.data.html,
assetId: response.data.assetId, assetId: response.data.assetId,
replyId: response.data.replyId replyId: response.data.replyId
} }
} }
this.editContentId = id this.editContentId = id
this.$nextTick(() => {
this.$refs.dataTable.doLayout()
})
}, },
cancelEdit () { cancelEdit () {
this.editContentId = '' this.editContentId = ''
this.editComment.content = '' this.editComment.content = ''
this.editComment.html = ''
} }
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
>>>.el-textarea__inner { .comment-editor>>>.textarea-button{
resize: none; display: flex;
} justify-content: flex-end;
.textarea-box{ padding-bottom: 10px;
position: relative; padding-right: 10px;
}
.textarea-button{
position: absolute;
bottom: 10px;
right: 10px;
.nz-btn.nz-btn-size-mini{ .nz-btn.nz-btn-size-mini{
width: 74px; width: 74px;
height: 30px; height: 30px;
} }
} }
#assetCommentTable>>>.descriptions-content {
.ql-container.ql-snow{
border: unset;
}
.ql-editor{
padding: 0;
}
}
</style> </style>