This commit is contained in:
min.jiang 2024-07-24 03:03:53 +08:00
parent 1ba337ac4f
commit 9b1f18282b
9 changed files with 117 additions and 47 deletions

View File

@ -113,6 +113,7 @@ export default {
Apply: 'Apply', Apply: 'Apply',
'Empty conversation': 'Empty conversation', 'Empty conversation': 'Empty conversation',
'Please choose conversation': 'Please choose conversation', 'Please choose conversation': 'Please choose conversation',
'Please input message': 'Please input message',
Name: 'Name', Name: 'Name',
'Stay on Freeleaps': 'Stay on Freeleaps', 'Stay on Freeleaps': 'Stay on Freeleaps',
'Delivered projects': 'Delivered projects', 'Delivered projects': 'Delivered projects',
@ -211,4 +212,5 @@ export default {
'Issues management': 'Issues management', 'Issues management': 'Issues management',
'Payment progress': 'Payment progress', 'Payment progress': 'Payment progress',
'Goal': 'Goal', 'Goal': 'Goal',
'Message': 'Message'
} }

View File

@ -112,6 +112,7 @@ export default {
Apply: '应用', Apply: '应用',
'Empty conversation': '空对话', 'Empty conversation': '空对话',
'Please choose conversation': '请选择对话', 'Please choose conversation': '请选择对话',
'Please input message': '请输入消息',
Name: '名字', Name: '名字',
'Stay on Freeleaps': 'Freeleaps注册时长', 'Stay on Freeleaps': 'Freeleaps注册时长',
'Delivered projects': '已交付项目', 'Delivered projects': '已交付项目',
@ -194,4 +195,5 @@ export default {
'Issues management': '问题管理', 'Issues management': '问题管理',
'Payment progress': '付款进度', 'Payment progress': '付款进度',
'Goal': '项目目标', 'Goal': '项目目标',
'Message': '消息'
} }

View File

@ -1,38 +1,40 @@
<template> <template>
<div class="message-container"> <div class="message-container">
<div class="message-hub-conainter"> <div class="message-hub-conainter">
<div v-if="conversations && conversations.length > 0" class="conversation-list-container"> <template v-if="!receiver_id">
<div <div v-if="conversations && conversations.length > 0" class="conversation-list-container">
v-for="(conversation, index) in conversations" <div
:key="index" v-for="(conversation, index) in conversations"
class="conversation-container" :key="index"
:class="{ class="conversation-container"
selected: selConversation?.id === conversation?.id :class="{
}" selected: selConversation?.id === conversation?.id
@click="selectConversation(conversation)" }"
> @click="selectConversation(conversation)"
<img class="participant-portrait" alt="user portrait" src="@/assets/profile.png" /> >
<div class="conversation-summary-container"> <img class="participant-portrait" alt="user portrait" src="@/assets/profile.png" />
<div class="conversation-summary-header-container"> <div class="conversation-summary-container">
<span class="participant-fullname"> <div class="conversation-summary-header-container">
{{ conversation.subject }} <span class="participant-fullname">
</span> {{ conversation.subject }}
<span class="conversation-last-update-date">{{ </span>
getDateFromFulltimeString(conversation.create_time) <span class="conversation-last-update-date">{{
}}</span> getDateFromFulltimeString(conversation.create_time)
</div> }}</span>
<div class="conversation-summary-highlight-container"> </div>
{{ conversation.last_message?.message_body }} <div class="conversation-summary-highlight-container">
{{ conversation.last_message?.message_body }}
</div>
</div> </div>
</div> </div>
</div> </div>
</div> <div
<div v-if="!conversations || conversations.length == 0"
v-if="!conversations || conversations.length == 0" class="conversation-list-empty-container"
class="conversation-list-empty-container" >
> {{ $t('Empty conversation') }}
{{ $t('Empty conversation') }} </div>
</div> </template>
<div class="message-panel-container"> <div class="message-panel-container">
<div v-if="messages && messages.length > 0" class="message-thread-container"> <div v-if="messages && messages.length > 0" class="message-thread-container">
<div <div
@ -59,7 +61,7 @@
</div> </div>
</div> </div>
<div v-if="!messages || messages.length == 0" class="message-thread-empty-container"> <div v-if="!messages || messages.length == 0" class="message-thread-empty-container">
{{ $t('Please choose conversation') }} {{ receiver_id ? $t('Please input message') : $t('Please choose conversation') }}
</div> </div>
<div class="message-writing-panel-container"> <div class="message-writing-panel-container">
<svg-icon icon="msg-enter" class-name="writing-message-enter" /> <svg-icon icon="msg-enter" class-name="writing-message-enter" />
@ -67,7 +69,7 @@
class="writing-message-input" class="writing-message-input"
type="text" type="text"
v-model="writtenMessage" v-model="writtenMessage"
@keypress.enter="sendMessage(selConversation.id)" @keypress.enter="sendMessage(selConversation?.id)"
/> />
</div> </div>
</div> </div>
@ -76,26 +78,27 @@
</template> </template>
<script> <script>
import SvgIcon from '@/components/SvgIcon.vue' import SvgIcon from '@/components/SvgIcon.vue'
import { MessageHubApi, DateUtils } from '@/utils/index' import { MessageHubApi, DateUtils, requestHubUtils } from '@/utils/index'
import { userUtils } from '@/utils/store/index' import { userUtils } from '@/utils/store/index'
export default { export default {
components: { SvgIcon }, components: { SvgIcon },
name: 'MessageHub', name: 'MessageHub',
props: {}, props: {},
mounted() { mounted() {
this.$store.dispatch('basic/updateConversationsPage', { const request = requestHubUtils.fetchRequestToMessage()
token: this.mnx_getUserAuthToken(), if (request && request.user_id) {
cb: error => { this.receiver_id = request.user_id
this.mnx_backendErrorHandler(error) } else {
} this.fetchConversations()
}) }
}, },
data() { data() {
return { return {
userIdentityNote: this.mnx_getUserIdentity(), userIdentityNote: this.mnx_getUserIdentity(),
selConversation: null, selConversation: null,
messages: [], messages: [],
writtenMessage: null writtenMessage: null,
receiver_id: null
} }
}, },
computed: { computed: {
@ -105,19 +108,28 @@ export default {
}, },
watch: { watch: {
conversations(n_val) { conversations(n_val) {
console.log('nval', n_val, this.selConversation, n_val[0].messages)
if (!this.selConversation && n_val[0]) { if (!this.selConversation && n_val[0]) {
this.selConversation = n_val[0] this.selConversation = n_val[0]
this.messages = n_val[0].messages || [] this.messages = n_val[0].messages || []
this.clearUnreadMessageBy(n_val[0]) this.clearUnreadMessageBy(n_val[0])
} else { } else {
if (n_val?.[0] && this.selConversation?.id === n_val?.[0]?.id) { if (n_val?.[0] && this.selConversation?.id === n_val?.[0]?.id) {
this.messages = n_val[0].messages || [] // this.messages = n_val[0].messages || []
this.clearUnreadMessageBy(n_val[0]) this.clearUnreadMessageBy(n_val[0])
} }
} }
} }
}, },
methods: { methods: {
fetchConversations() {
this.$store.dispatch('basic/updateConversationsPage', {
token: this.mnx_getUserAuthToken(),
cb: error => {
this.mnx_backendErrorHandler(error)
}
})
},
fetchMessageForConversation(conversation_id) { fetchMessageForConversation(conversation_id) {
const jwt = userUtils.getJwtToken() const jwt = userUtils.getJwtToken()
MessageHubApi.fetchMessages( MessageHubApi.fetchMessages(
@ -145,7 +157,18 @@ export default {
}, },
sendMessage(conversation_id) { sendMessage(conversation_id) {
const jwt = userUtils.getJwtToken() const jwt = userUtils.getJwtToken()
MessageHubApi.sendMessageToConversation(conversation_id, this.writtenMessage, jwt) if (this.receiver_id) {
MessageHubApi.sendMessageToUser(this.receiver_id, this.writtenMessage, jwt)
.then(() => {
requestHubUtils.fillRequestToMessage(null)
this.receiver_id = null
this.writtenMessage = null
})
.catch((error) => {
this.mnx_backendErrorHandler(error)
})
} else {
MessageHubApi.sendMessageToConversation(conversation_id, this.writtenMessage, jwt)
.then((response) => { .then((response) => {
let new_message = response.data let new_message = response.data
this.messages.push({ this.messages.push({
@ -159,6 +182,7 @@ export default {
.catch((error) => { .catch((error) => {
this.mnx_backendErrorHandler(error) this.mnx_backendErrorHandler(error)
}) })
}
}, },
getDateFromFulltimeString(fulltime) { getDateFromFulltimeString(fulltime) {
// FromJsonToHMDateString // FromJsonToHMDateString

View File

@ -76,6 +76,9 @@
<button class="make-proposal-button" type="button" @click="Propose(request)"> <button class="make-proposal-button" type="button" @click="Propose(request)">
{{ $t('Propose') }} {{ $t('Propose') }}
</button> </button>
<button class="make-proposal-button" type="button" @click="messageTo(request)">
{{ $t('Message') }}
</button>
<div class="request-description-content" v-html="request.content"></div> <div class="request-description-content" v-html="request.content"></div>
<div <div
class="pdf-actions" class="pdf-actions"
@ -251,6 +254,10 @@ export default {
requestHubUtils.fillRequestToPropose(request) requestHubUtils.fillRequestToPropose(request)
this.mnx_navToMakeProposal(request.id, proposingModelEnum.NO_LOADING) this.mnx_navToMakeProposal(request.id, proposingModelEnum.NO_LOADING)
}, },
messageTo(request) {
requestHubUtils.fillRequestToMessage(request)
this.mnx_navToMessageHub()
},
getDateFromFulltimeString(fulltime) { getDateFromFulltimeString(fulltime) {
return DateUtils.FromJsonToDateString(fulltime) return DateUtils.FromJsonToDateString(fulltime)
}, },

View File

@ -279,7 +279,7 @@
<td> <td>
<div class="project-milestones-table-content"> <div class="project-milestones-table-content">
<span class="project-milestones-table-label">{{ $t('Action') }}</span> <span class="project-milestones-table-label">{{ $t('Action') }}</span>
<button class="btn btn-link" :disabled="isMilestoneActionButtonDisabled(project.project, milestone) <button class="project-milestones-table-btn" :disabled="isMilestoneActionButtonDisabled(project.project, milestone)
" :hidden="isMilestoneActionButtonHidden(project.project, milestone)" " :hidden="isMilestoneActionButtonHidden(project.project, milestone)"
@click="handleMilestoneAction(project.project, milestone)"> @click="handleMilestoneAction(project.project, milestone)">
{{ fetchMilestoneActionButtonText(project.project, milestone) }} {{ fetchMilestoneActionButtonText(project.project, milestone) }}
@ -1482,4 +1482,19 @@ export default {
width: 100%; width: 100%;
height: 357px; height: 357px;
} }
.project-milestones-table-btn {
@extend .btn;
@extend .btn-primary;
width: fit-content;
padding: 0 8px;
border-radius: 0;
&:disabled {
color: #000000;
border-color: #D9D9D9;
background-color: #D9D9D9;
opacity: 1;
}
}
</style> </style>

View File

@ -239,17 +239,17 @@ export default {
showIssueActionButton(project, issue, button_text) { showIssueActionButton(project, issue, button_text) {
switch (issue.status) { switch (issue.status) {
case projectIssueStatusEnum.OPEN: case projectIssueStatusEnum.OPEN:
if (button_text === this.$t('Resolve')) { if (button_text === 'Resolve') {
return project.providers.includes(project.current_user_id) return project.providers.includes(project.current_user_id)
} }
break break
case 1: case 1:
if (button_text === this.$t('Reopen') || button_text === this.$t('Confirm')) { if (button_text === 'Reopen' || button_text === 'Confirm') {
return project.issuers.includes(project.current_user_id) return project.issuers.includes(project.current_user_id)
} }
break break
case projectIssueStatusEnum.CLOSED: case projectIssueStatusEnum.CLOSED:
if (button_text === this.$t('Reopen')) { if (button_text === 'Reopen') {
return project.issuers.includes(project.current_user_id) return project.issuers.includes(project.current_user_id)
} }
break break

View File

@ -50,9 +50,9 @@ const updateConversations =(state, {token ,cb}, data) => {
).then((response) => { ).then((response) => {
conversations[0].messages = response?.data || [] conversations[0].messages = response?.data || []
conversations[0].message_update_time = new Date().toISOString() conversations[0].message_update_time = new Date().toISOString()
// state.conversations = conversations state.conversations = conversations
state.unreadConversationCount = updateLength state.unreadConversationCount = updateLength
// localStorage.setItem('conversations', JSON.stringify(conversations)) localStorage.setItem('conversations', JSON.stringify(conversations))
localStorage.setItem('unreadConversationCount', updateLength) localStorage.setItem('unreadConversationCount', updateLength)
}) })
} }

View File

@ -44,6 +44,18 @@ class MessageHubApi {
) )
return request return request
} }
static sendMessageToUser(receiver_id, message, jwt) {
// let jwt = userUtils.getJwtToken()
const request = backendAxios.post(
'/api/messages/send-message-to-user',
{ receiver_id, message },
{
headers: { Authorization: `Bearer ${jwt}` }
}
)
return request
}
} }
export { MessageHubApi } export { MessageHubApi }

View File

@ -13,6 +13,14 @@ class RequestHubUtils {
return this.request_to_propose return this.request_to_propose
} }
fillRequestToMessage(request) {
this.request_to_message = request
}
fetchRequestToMessage() {
return this.request_to_message
}
fillExistingProposal(proposal) { fillExistingProposal(proposal) {
this.existing_proposal = proposal this.existing_proposal = proposal
} }