1
0
mirror of https://github.com/PanJiaChen/vue-element-admin.git synced 2025-08-07 18:25:45 +08:00

Support Notes (#837)

* Support for notes in mobile and desktop mode

* resolve observations

Co-authored-by: elsiosanchez <elsiossanches@gmail.com>
This commit is contained in:
Elsio Sanchez 2021-05-12 18:29:57 -04:00 committed by GitHub
parent 62cf65bbfa
commit f4541271e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 579 additions and 162 deletions

View File

@ -16,57 +16,12 @@
along with this program. If not, see <https:www.gnu.org/licenses/>.
-->
<template>
<div>
<el-card
v-if="isNote"
class="box-card chat-entries-list-card"
:style="{ 'height': getHeightPanelBottom + 'vh' }"
>
<span
slot="header"
class="clearfix chat-entries-card-title"
>
{{ $t('window.containerInfo.notes') }}
</span>
<el-scrollbar wrap-class="scroll-child" style="height: 100%;">
<el-timeline>
<el-timeline-item
v-for="(chats, key) in chatList"
:key="key"
:timestamp="translateDate(chats.logDate)"
placement="top"
>
<el-card shadow="hover">
<div v-markdown="chats.characterData" />
</el-card>
</el-timeline-item>
</el-timeline>
</el-scrollbar>
</el-card>
<el-card
class="box-card chat-entry-create-card"
>
<span slot="header" class="clearfix chat-entries-card-title">
{{ $t('window.containerInfo.logWorkflow.addNote') }}
</span>
<el-scrollbar>
<input-chat />
<el-button
icon="el-icon-check"
style="float: right; "
type="primary"
@click="sendComment()"
/>
<el-button
icon="el-icon-close"
style="float: right;margin-right: 1%;"
type="danger"
@click="clear()"
/>
</el-scrollbar>
</el-card>
</div>
<component
:is="templateDevice"
:right-panel="rightPanel"
:table-name="tableName"
:record-id="recordId"
/>
</template>
<script>
@ -85,101 +40,22 @@ export default {
recordId: {
type: Number,
default: undefined
},
rightPanel: {
type: Boolean,
default: false
}
},
computed: {
chatList() {
const commentLogs = this.$store.getters.getChatEntries
if (this.isEmptyValue(commentLogs)) {
return commentLogs
isMobile() {
return this.$store.state.app.device === 'mobile'
},
templateDevice() {
if (this.isMobile) {
return () => import('@/components/ADempiere/ChatEntries/modeMobile.vue')
}
commentLogs.sort((a, b) => {
const c = new Date(a.logDate)
const d = new Date(b.logDate)
return c - d
})
return commentLogs
},
language() {
return this.$store.getters.language
},
tableNameToSend() {
if (this.isEmptyValue(this.tableName)) {
return this.$route.params.tableName
}
return this.tableName
},
recordIdToSend() {
if (this.isEmptyValue(this.recordId)) {
return this.$route.params.recordId
}
return this.recordId
},
isNote() {
return this.$store.getters.getIsNote
},
getHeightPanelBottom() {
return this.$store.getters.getSplitHeight - 14
}
},
methods: {
sendComment() {
const comment = this.$store.getters.getChatTextLong
if (!this.isEmptyValue(comment)) {
this.$store.dispatch('createChatEntry', {
tableName: this.tableNameToSend,
recordId: this.recordIdToSend,
comment
})
}
},
clear() {
this.$store.commit('setChatText', '')
},
translateDate(value) {
return this.$d(new Date(value), 'long', this.language)
return () => import('@/components/ADempiere/ChatEntries/modeDesktop.vue')
}
}
}
</script>
<style lang="scss">
.chat-entries-list-card {
// small title of the card
.el-card__header {
max-height: 35px !important;
padding: 10px 20px !important;
}
// brings the card space closer to the timerline
.el-card__body {
padding-left: 0px !important;
}
.el-timeline-item__content {
.el-card {
// remove the right spacing so that it does not overlap with the scroll
margin-right: 20px !important;
// removes excessive card content space from chat logs
.el-card__body {
padding-left: 20px !important;
padding-top: 0px !important;
padding-bottom: 0px !important;
}
}
}
}
.chat-entry-create-card {
// small title of the card
.el-card__header {
max-height: 35px !important;
padding: 10px 20px !important;
}
}
.el-card__body {
padding: 20px;
height: 100%;
}
</style>

View File

@ -0,0 +1,137 @@
<!--
ADempiere-Vue (Frontend) for ADempiere ERP & CRM Smart Business Solution
Copyright (C) 2017-Present E.R.P. Consultores y Asociados, C.A.
Contributor(s): Elsio Sanchez elsiosanches@gmail.com www.erpya.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https:www.gnu.org/licenses/>.
-->
<template>
<div>
<el-card
v-if="!isEmptyValue(chatEntryList)"
style="padding-left: 0px !important"
>
<el-scrollbar wrap-class="chat-scroll-mobile">
<el-timeline>
<el-timeline-item
v-for="(chats, key) in chatEntryList"
:key="key"
icon="el-icon-postcard"
:timestamp="translateDate(chats.logDate)"
placement="top"
>
<el-card shadow="always" style="border: 2px solid #d2e1ffd6;">
<div v-markdown="chats.characterData" style="padding-top: 2%; padding-bottom: 2%" />
</el-card>
</el-timeline-item>
</el-timeline>
</el-scrollbar>
</el-card>
<div v-else>
<p>
{{ $t('data.emptyNote') }}
</p>
</div>
</div>
</template>
<script>
export default {
name: 'ListChatEntry',
computed: {
chatEntryList() {
const commentLogs = this.$store.getters.getChatEntries
if (this.isEmptyValue(commentLogs)) {
return []
}
// commentLogs.sort((a, b) => {
// const c = new Date(a.logDate)
// const d = new Date(b.logDate)
// return c - d
// })
return commentLogs
},
language() {
return this.$store.getters.language
}
},
methods: {
translateDate(value) {
return this.$d(new Date(value), 'long', this.language)
}
}
}
</script>
<style lang="scss">
.chat-entries-list-card {
// small title of the card
.el-card__header {
max-height: 35px !important;
padding: 10px 20px !important;
}
// brings the card space closer to the timerline
.el-card__body {
padding-left: 0px !important;
}
.el-timeline-item__content {
.el-card {
// remove the right spacing so that it does not overlap with the scroll
margin-right: 20px !important;
// removes excessive card content space from chat logs
.el-card__body {
padding-left: 20px !important;
padding-top: 0px !important;
padding-bottom: 0px !important;
}
}
}
}
.chat-entry-create-card {
// small title of the card
.el-card__header {
max-height: 35px !important;
padding: 10px 20px !important;
}
}
.el-card__body {
padding: 0px;
height: 100%;
}
.chat-scroll-mobile {
max-height: 60vh !important;
}
.chat-scroll-panel-right {
max-height: 50vh !important;
padding-bottom: 10% !important;
}
.el-timeline-item__icon {
color: #1890ff;
font-size: 19px;
background: white;
}
.el-timeline {
margin: 0;
font-size: 14px;
padding-left: 5%;
list-style: none;
}
.el-timeline-item__timestamp {
color: #55575b;
line-height: 1;
font-size: 13px;
}
</style>

View File

@ -0,0 +1,103 @@
// ADempiere-Vue (Frontend) for ADempiere ERP & CRM Smart Business Solution
// Copyright (C) 2017-Present E.R.P. Consultores y Asociados, C.A.
// Contributor(s):Elsio Sanchez elsiosanches@gmail.com.com www.erpya.com
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import inputChat from './inputChat'
export default {
name: 'MixinChatEntries',
components: {
inputChat
},
props: {
tableName: {
type: String,
default: undefined
},
recordId: {
type: Number,
default: undefined
},
rightPanel: {
type: Boolean,
default: false
}
},
computed: {
chatList() {
const commentLogs = this.$store.getters.getChatEntries
if (this.isEmptyValue(commentLogs)) {
return []
}
commentLogs.sort((a, b) => {
const c = new Date(a.logDate)
const d = new Date(b.logDate)
return c - d
})
return commentLogs
},
chatListMobile() {
const commentLogs = this.$store.getters.getChatEntries
if (this.isEmptyValue(commentLogs)) {
return []
}
commentLogs.sort((a, b) => {
const c = new Date(a.logDate)
const d = new Date(b.logDate)
return c - d
})
return commentLogs
},
language() {
return this.$store.getters.language
},
tableNameToSend() {
if (this.isEmptyValue(this.tableName)) {
return this.$route.params.tableName
}
return this.tableName
},
recordIdToSend() {
if (this.isEmptyValue(this.recordId)) {
return this.$route.params.recordId
}
return this.recordId
},
isNote() {
return this.$store.getters.getIsNote
},
getHeightPanelBottom() {
return this.$store.getters.getSplitHeight
}
},
methods: {
sendComment() {
const comment = this.$store.getters.getChatTextLong
if (!this.isEmptyValue(comment)) {
this.$store.dispatch('createChatEntry', {
tableName: this.tableNameToSend,
recordId: this.recordIdToSend,
comment
})
}
},
clear() {
this.$store.commit('setChatText', '')
},
translateDate(value) {
return this.$d(new Date(value), 'long', this.language)
}
}
}

View File

@ -0,0 +1,119 @@
<!--
ADempiere-Vue (Frontend) for ADempiere ERP & CRM Smart Business Solution
Copyright (C) 2017-Present E.R.P. Consultores y Asociados, C.A.
Contributor(s): Elsio Sanchez elsiosanches@gmail.com www.erpya.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https:www.gnu.org/licenses/>.
-->
<template>
<div>
<el-card
v-if="isNote"
class="box-card chat-entries-list-card"
>
<span
slot="header"
class="clearfix chat-entries-card-title"
>
<i class="el-icon-notebook-2" style="color: #1890ff;" /> {{ $t('window.containerInfo.notes') }}
</span>
<el-scrollbar wrap-class="chat-scroll-desktop">
<el-timeline>
<el-timeline-item
v-for="(chats, key) in chatList"
:key="key"
icon="el-icon-postcard"
:timestamp="translateDate(chats.logDate)"
placement="top"
>
<el-card shadow="always" style="border: 2px solid rgba(210, 225, 255, 0.84);">
<div v-markdown="chats.characterData" style="padding-top: 2%; padding-bottom: 2%" />
</el-card>
</el-timeline-item>
</el-timeline>
</el-scrollbar>
</el-card>
<el-card
class="box-card chat-entry-create-card"
>
<span slot="header" class="clearfix chat-entries-card-title">
{{ $t('window.containerInfo.logWorkflow.addNote') }}
</span>
<input-chat />
<el-button
icon="el-icon-check"
style="float: right; "
type="primary"
@click="sendComment()"
/>
<el-button
icon="el-icon-close"
style="float: right;margin-right: 1%;"
type="danger"
@click="clear()"
/>
</el-card>
</div>
</template>
<script>
import MixinChatEntries from './mixinChat.js'
export default {
name: 'ChatEntriesModeDesktop',
mixins: [MixinChatEntries]
}
</script>
<style lang="scss">
.chat-entries-list-card {
// small title of the card
.el-card__header {
max-height: 35px !important;
padding: 10px 20px !important;
}
// brings the card space closer to the timerline
.el-card__body {
padding-left: 0px !important;
}
.el-timeline-item__content {
.el-card {
// remove the right spacing so that it does not overlap with the scroll
margin-right: 20px !important;
// removes excessive card content space from chat logs
.el-card__body {
padding-left: 20px !important;
padding-top: 0px !important;
padding-bottom: 0px !important;
}
}
}
}
.chat-entry-create-card {
// small title of the card
.el-card__header {
max-height: 35px !important;
padding: 10px 20px !important;
}
}
.el-card__body {
padding: 20px;
height: 100%;
}
.chat-scroll-desktop {
max-height: 40vh !important;
}
</style>

View File

@ -0,0 +1,145 @@
<!--
ADempiere-Vue (Frontend) for ADempiere ERP & CRM Smart Business Solution
Copyright (C) 2017-Present E.R.P. Consultores y Asociados, C.A.
Contributor(s): Elsio Sanchez elsiosanches@gmail.com www.erpya.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https:www.gnu.org/licenses/>.
-->
<template>
<div>
<el-card
v-if="!isEmptyValue(chatList)"
class="box-card chat-entries-list-card"
shadow="never"
style="padding-left: 0px !important"
>
<span slot="header" class="clearfix" style="padding-left: 0px !important;">
<i class="el-icon-notebook-2" style="color: #1890ff;" /> {{ $t('window.containerInfo.notes') }}
</span>
<el-scrollbar wrap-class="chat-scroll-mobile">
<el-timeline>
<el-timeline-item
v-for="(chats, key) in chatList"
:key="key"
icon="el-icon-postcard"
:timestamp="translateDate(chats.logDate)"
placement="top"
>
<el-card shadow="always" style="border: 2px solid #d2e1ffd6;">
<div v-markdown="chats.characterData" style="padding-top: 2%; padding-bottom: 2%" />
</el-card>
</el-timeline-item>
</el-timeline>
</el-scrollbar>
</el-card>
<el-card
v-if="rightPanel"
class="box-card chat-entry-create-card"
style="padding-left: 2.5%;padding-right: 2.5%;"
>
<span slot="header" class="clearfix chat-entries-card-title">
{{ $t('window.containerInfo.logWorkflow.addNote') }}
</span>
<input-chat />
<el-button
icon="el-icon-check"
style="float: right; "
type="primary"
@click="sendComment()"
/>
<el-button
icon="el-icon-close"
style="float: right;margin-right: 1%;"
type="danger"
@click="clear()"
/>
</el-card>
</div>
</template>
<script>
import MixinChatEntries from './mixinChat.js'
export default {
name: 'ChatEntriesModeMobile',
mixins: [MixinChatEntries],
props: {
rightPanel: {
type: Boolean,
default: false
}
}
}
</script>
<style lang="scss">
.chat-entries-list-card {
// small title of the card
.el-card__header {
max-height: 35px !important;
padding: 10px 20px !important;
}
// brings the card space closer to the timerline
.el-card__body {
padding-left: 0px !important;
}
.el-timeline-item__content {
.el-card {
// remove the right spacing so that it does not overlap with the scroll
margin-right: 20px !important;
// removes excessive card content space from chat logs
.el-card__body {
padding-left: 20px !important;
padding-top: 0px !important;
padding-bottom: 0px !important;
}
}
}
}
.chat-entry-create-card {
// small title of the card
.el-card__header {
max-height: 35px !important;
padding: 10px 20px !important;
}
}
.el-card__body {
padding: 0px;
height: 100%;
}
.chat-scroll-mobile {
max-height: 60vh !important;
}
.chat-scroll-panel-right {
max-height: 50vh !important;
padding-bottom: 10% !important;
}
.el-timeline-item__icon {
color: #1890ff;
font-size: 19px;
background: white;
}
.el-timeline {
margin: 0;
font-size: 14px;
padding-left: 5%;
list-style: none;
}
.el-timeline-item__timestamp {
color: #55575b;
line-height: 1;
font-size: 13px;
}
</style>

View File

@ -49,6 +49,28 @@
</div>
</div>
</el-dropdown-item>
<el-dropdown-item
:command="this.$t('data.addNote')"
:divided="true"
>
<div class="contents">
<div style="margin-right: 5%;margin-top: 10%;">
<i class="el-icon-notebook-2" style="font-weight: bolder;" />
</div>
<div>
<span class="contents">
<b class="label">
{{ $t('data.addNote') }}
</b>
</span>
<p
class="description"
>
{{ $t('data.descriptionNote') }}
</p>
</div>
</div>
</el-dropdown-item>
<el-dropdown-item
v-for="(action, index) in actions"
:key="index"
@ -245,13 +267,11 @@ export default {
} else if (action.action === 'recordAccess') {
this.$store.commit('changeShowRigthPanel', false)
this.$store.commit('setRecordAccess', true)
this.$router.push({
name: this.$route.name,
query: {
...this.$route.query,
typeAction: action.action
}
}, () => {})
} else if (action === this.$t('data.addNote')) {
this.$store.commit('changeShowRigthPanel', true)
this.$store.dispatch('setOptionField', {
name: this.$t('data.addNote')
})
} else {
this.runAction(action)
}

View File

@ -723,6 +723,7 @@ export default {
}
},
handleCommand(command) {
console.log({ command })
this.$store.commit('setRecordAccess', false)
if (command.name === this.$t('table.ProcessActivity.zoomIn')) {
this.redirect({ window: command.fieldAttributes.reference.zoomWindows[0] })

View File

@ -309,7 +309,7 @@ export default {
deleteRecord: 'Delete Record',
undoNew: 'Undo New Record',
containerInfo: {
notes: 'Notes',
notes: 'Notes List',
changeLog: 'ACtivity',
workflowLog: 'Workflow Log',
changeDetail: 'Change detail',
@ -366,11 +366,14 @@ export default {
},
selectionRequired: 'You must select a record',
undo: 'Undo',
unlockRecord: 'Unlock Record',
notification: {
lockRecord: 'The Registry was Locked',
unlockRecord: 'Registry was Unlocked'
}
},
addNote: 'Add Note',
emptyNote: 'Este registro no posee ninguna nota',
descriptionNote: 'Add Note or Comment to Record',
unlockRecord: 'Unlock Record'
},
sequence: {
available: 'Available',

View File

@ -285,7 +285,7 @@ export default {
deleteRecord: 'Eliminar Registro',
undoNew: 'Descartar Nuevo Registro',
containerInfo: {
notes: 'Notas',
notes: 'Listado de Notas',
changeLog: 'Actividad',
workflowLog: 'Histórico de Flujo de Trabajo',
changeDetail: 'Detalle del cambio',
@ -342,12 +342,14 @@ export default {
},
selectionRequired: 'Debe seleccionar un registro',
undo: 'Deshacer',
unlockRecord: 'Desbloquear Registro',
notification: {
lockRecord: 'El Registro fue Bloqueado',
unlockRecord: 'El Registro fue Desbloqueado'
}
},
addNote: 'Agregar Nota',
emptyNote: 'Este registro no posee ninguna nota',
descriptionNote: 'Agregar Nota o Comentario al Registro',
unlockRecord: 'Desbloquear Registro'
},
sequence: {
available: 'Disponibles',

View File

@ -107,7 +107,7 @@
/>
<div v-if="isMobile">
<el-card class="box-card" style="height: 90vh">
<el-tabs v-model="activeInfo" @tab-click="handleClick">
<el-tabs v-model="activeInfo" :stretch="true" @tab-click="handleClick">
<el-tab-pane
name="listChatEntries"
>
@ -115,7 +115,7 @@
<i class="el-icon-s-comment" />
{{ $t('window.containerInfo.notes') }}
</span>
<chat-entries
<list-chat-entry
:table-name="getTableName"
:record-id="recordId"
/>
@ -326,14 +326,20 @@
<right-panel
v-if="panelContextMenu && isMobile"
>
<chat-entries
v-if="contextMenuField.name === $t('data.addNote')"
:table-name="getTableName"
:record-id="recordId"
:right-panel="true"
/>
<record-access
v-if="showRecordAccess"
v-else-if="showRecordAccess && contextMenuField.name !== $t('data.addNote')"
:table-name="getTableName"
:record="getRecord"
/>
<component
:is="componentRender"
v-if="!showRecordAccess"
v-else-if="!showRecordAccess && contextMenuField.name !== $t('data.addNote')"
:field-attributes="contextMenuField.fieldAttributes"
:source-field="contextMenuField.fieldAttributes"
:record-uuid="contextMenuField.fieldAttributes.recordUuid"

View File

@ -25,6 +25,7 @@ import DataTable from '@/components/ADempiere/DataTable'
import splitPane from 'vue-splitpane'
// Container Info
import ChatEntries from '@/components/ADempiere/ChatEntries'
import ListChatEntry from '@/components/ADempiere/ChatEntries/listChatEntry'
import RecordLogs from '@/components/ADempiere/ContainerInfo/recordLogs'
import WorkflowLogs from '@/components/ADempiere/ContainerInfo/workflowLogs'
// Workflow
@ -50,6 +51,7 @@ export default {
ModalDialog,
RightPanel,
ChatEntries,
ListChatEntry,
RecordLogs,
WorkflowLogs,
WorkflowStatusBar,
@ -250,7 +252,10 @@ export default {
return this.$store.getters.getWindow(this.windowUuid)
},
isShowedTabsChildren() {
return this.windowMetadata.isShowedTabsChildren
if (this.windowMetadata && this.windowMetadata.isShowedTabsChildren && this.isEmptyValue(this.$route.query.typeAction)) {
return this.windowMetadata.isShowedTabsChildren
}
return false
},
isShowedRecordNavigation() {
if (this.windowMetadata && this.windowMetadata.isShowedRecordNavigation) {

View File

@ -137,8 +137,8 @@ aside {
background: #008fd3;
}
.close-info {
top: 40%;
position: absolute;
float: left;
padding-top: 50%;
}
.close-info-mobile {
top: 29%;