|
|
|
|
@ -12,17 +12,42 @@
|
|
|
|
|
<div class="filter-section">
|
|
|
|
|
<el-form :model="filter" label-position="top">
|
|
|
|
|
<el-row :gutter="20">
|
|
|
|
|
<el-col :span="6">
|
|
|
|
|
<el-col :span="4">
|
|
|
|
|
<el-form-item label="时间范围" prop="timeRange">
|
|
|
|
|
<el-select v-model="filter.timeRange" placeholder="请选择时间范围" style="width: 100%;">
|
|
|
|
|
<el-select v-model="filter.timeRange" placeholder="请选择时间范围" style="width: 100%;" @change="handleTimeRangeChange">
|
|
|
|
|
<el-option label="最近7天" :value="7"></el-option>
|
|
|
|
|
<el-option label="最近30天" :value="30"></el-option>
|
|
|
|
|
<el-option label="最近90天" :value="90"></el-option>
|
|
|
|
|
<el-option label="最近一年" :value="365"></el-option>
|
|
|
|
|
<el-option label="自定义时间段" :value="'custom'"></el-option>
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="6">
|
|
|
|
|
<el-col :span="4" v-if="filter.timeRange === 'custom'">
|
|
|
|
|
<el-form-item label="开始日期" prop="startDate">
|
|
|
|
|
<el-date-picker
|
|
|
|
|
v-model="filter.startDate"
|
|
|
|
|
type="date"
|
|
|
|
|
placeholder="选择开始日期"
|
|
|
|
|
style="width: 100%;"
|
|
|
|
|
format="yyyy-MM-dd"
|
|
|
|
|
value-format="yyyy-MM-dd">
|
|
|
|
|
</el-date-picker>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="4" v-if="filter.timeRange === 'custom'">
|
|
|
|
|
<el-form-item label="结束日期" prop="endDate">
|
|
|
|
|
<el-date-picker
|
|
|
|
|
v-model="filter.endDate"
|
|
|
|
|
type="date"
|
|
|
|
|
placeholder="选择结束日期"
|
|
|
|
|
style="width: 100%;"
|
|
|
|
|
format="yyyy-MM-dd"
|
|
|
|
|
value-format="yyyy-MM-dd">
|
|
|
|
|
</el-date-picker>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="4">
|
|
|
|
|
<el-form-item label="供需类型" prop="supplyType">
|
|
|
|
|
<el-select v-model="filter.supplyType" placeholder="请选择供需类型" clearable style="width: 100%;">
|
|
|
|
|
<el-option label="全部" value=""></el-option>
|
|
|
|
|
@ -31,7 +56,7 @@
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="6">
|
|
|
|
|
<el-col :span="4">
|
|
|
|
|
<el-form-item label="操作">
|
|
|
|
|
<el-button type="primary" @click="updateStats" :loading="loading">
|
|
|
|
|
{{ loading ? '更新中...' : '更新统计' }}
|
|
|
|
|
@ -52,7 +77,7 @@
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-value">{{ chartData.totalCount || 0 }}</div>
|
|
|
|
|
<div class="stat-change" :class="getChangeClass(chartData.publishChange)">
|
|
|
|
|
<div class="stat-change" v-if="filter.timeRange !== 'custom'" :class="getChangeClass(chartData.publishChange)">
|
|
|
|
|
<i :class="getChangeIcon(chartData.publishChange)"></i>
|
|
|
|
|
<span>{{ formatChange(chartData.publishChange) }} 较上期</span>
|
|
|
|
|
</div>
|
|
|
|
|
@ -66,7 +91,7 @@
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-value">{{ chartData.messageCount || 0 }}</div>
|
|
|
|
|
<div class="stat-change" :class="getChangeClass(chartData.messageChange)">
|
|
|
|
|
<div class="stat-change" v-if="filter.timeRange !== 'custom'" :class="getChangeClass(chartData.messageChange)">
|
|
|
|
|
<i :class="getChangeIcon(chartData.messageChange)"></i>
|
|
|
|
|
<span>{{ formatChange(chartData.messageChange) }} 较上期</span>
|
|
|
|
|
</div>
|
|
|
|
|
@ -80,10 +105,11 @@
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-value">{{ chartData.interactionCount || 0 }}</div>
|
|
|
|
|
<div class="stat-change" :class="getChangeClass(chartData.interactionChange)">
|
|
|
|
|
<div class="stat-change" v-if="filter.timeRange !== 'custom'" :class="getChangeClass(chartData.interactionChange)">
|
|
|
|
|
<i :class="getChangeIcon(chartData.interactionChange)"></i>
|
|
|
|
|
<span>{{ formatChange(chartData.interactionChange) }} 较上期</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-description">周期内有私信来回,计一次</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
@ -94,26 +120,26 @@
|
|
|
|
|
</div>
|
|
|
|
|
<div class="data-table">
|
|
|
|
|
<el-table :data="interactionList" style="width: 100%" v-loading="tableLoading">
|
|
|
|
|
<el-table-column label="供需信息" min-width="200">
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<div>
|
|
|
|
|
<div style="font-weight: 600; font-size: 14px; margin-bottom: 5px;">{{ scope.row.title || '-' }}</div>
|
|
|
|
|
<div style="font-size: 12px; color: #666; margin-bottom: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" :title="scope.row.content || '-'">{{ scope.row.content || '-' }}</div>
|
|
|
|
|
<el-tag :type="getTypeTagType(scope.row.type)" size="small">
|
|
|
|
|
{{ getTypeDisplayValue(scope.row.type) }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="供需信息" min-width="200">
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<div>
|
|
|
|
|
<div style="font-weight: 600; font-size: 14px; margin-bottom: 5px;">{{ scope.row.title || '-' }}</div>
|
|
|
|
|
<div style="font-size: 12px; color: #666; margin-bottom: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" :title="scope.row.content || '-'">{{ scope.row.content || '-' }}</div>
|
|
|
|
|
<el-tag :type="getTypeTagType(scope.row.type)" size="small">
|
|
|
|
|
{{ getTypeDisplayValue(scope.row.type) }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="发布者" min-width="150">
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<div class="user-info">
|
|
|
|
|
<div class="user-avatar">{{ scope.row.user && scope.row.user.name ? scope.row.user.name.charAt(0) : '-' }}</div>
|
|
|
|
|
<div>
|
|
|
|
|
<div style="font-weight: 600; font-size: 14px;">{{ scope.row.user && scope.row.user.name ? scope.row.user.name : '-' }}</div>
|
|
|
|
|
<div style="font-size: 12px; color: #666;">{{ getPublisherInfo(scope.row.user) }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="user-info">
|
|
|
|
|
<div class="user-avatar">{{ scope.row.user && scope.row.user.name ? scope.row.user.name.charAt(0) : '-' }}</div>
|
|
|
|
|
<div>
|
|
|
|
|
<div style="font-weight: 600; font-size: 14px;">{{ scope.row.user && scope.row.user.name ? scope.row.user.name : '-' }}</div>
|
|
|
|
|
<div style="font-size: 12px; color: #666;">{{ getPublisherInfo(scope.row.user) }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="发布时间" min-width="120">
|
|
|
|
|
@ -125,11 +151,17 @@
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
<div>
|
|
|
|
|
<div class="interaction-detail">
|
|
|
|
|
<div v-if="scope.row.messages && scope.row.messages.length > 0">
|
|
|
|
|
<div v-for="(message, index) in scope.row.messages" :key="index" style="margin-bottom: 8px;">
|
|
|
|
|
<span style="font-size: 12px;">{{ message.to_user.name || '-' }}({{ message.to_user.year || '-' }}届) · {{ formatDateTime(message.created_at) || '-' }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="scope.row.messages && scope.row.messages.length > 0">
|
|
|
|
|
<div v-for="(message, index) in scope.row.messages" :key="index" style="margin-bottom: 8px;">
|
|
|
|
|
<span
|
|
|
|
|
class="clickable-user"
|
|
|
|
|
@click="showInteractionDetail(scope.row, message.to_user)"
|
|
|
|
|
style="font-size: 12px; cursor: pointer; color: #409EFF; text-decoration: underline;">
|
|
|
|
|
{{ (message.to_user && message.to_user.name) || '-' }}({{ (message.to_user && message.to_user.year) || '-' }}届)
|
|
|
|
|
</span>
|
|
|
|
|
<span style="font-size: 12px; color: #666;"> · {{ formatDateTime(message.created_at) || '-' }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-else style="color: #999; font-size: 12px;">暂无交互记录</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
@ -160,11 +192,54 @@
|
|
|
|
|
</el-pagination>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 交互详情对话框 -->
|
|
|
|
|
<el-dialog
|
|
|
|
|
:title="getDialogTitle()"
|
|
|
|
|
:visible.sync="interactionDetailVisible"
|
|
|
|
|
width="800px"
|
|
|
|
|
:before-close="handleCloseDialog">
|
|
|
|
|
<div class="interaction-detail-dialog">
|
|
|
|
|
<div v-if="currentSupplyDemand" class="supply-demand-info">
|
|
|
|
|
<h4>{{ currentSupplyDemand.title || '供需信息' }}</h4>
|
|
|
|
|
<p class="supply-demand-content">{{ currentSupplyDemand.content || '暂无内容' }}</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="conversation-users" v-if="currentSupplyDemand && currentSupplyDemand.user">
|
|
|
|
|
<div class="user-indicator left">
|
|
|
|
|
<div class="user-avatar-small">{{ (currentTargetUser && currentTargetUser.name) ? currentTargetUser.name.charAt(0) : 'U' }}</div>
|
|
|
|
|
<span class="user-name">{{ (currentTargetUser && currentTargetUser.name) || '用户' }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="user-indicator right">
|
|
|
|
|
<div class="user-avatar-small">{{ (currentSupplyDemand.user && currentSupplyDemand.user.name) ? currentSupplyDemand.user.name.charAt(0) : 'P' }}</div>
|
|
|
|
|
<span class="user-name">{{ (currentSupplyDemand.user && currentSupplyDemand.user.name) || '发布者' }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="message-list-container" v-if="currentSupplyDemand && currentSupplyDemand.user">
|
|
|
|
|
<div v-if="messageList.length > 0" class="message-list">
|
|
|
|
|
<div v-for="(message, index) in messageList" :key="index" class="message-item" :class="getMessageItemClass(message)">
|
|
|
|
|
<div class="message-bubble" :class="getMessageBubbleClass(message)">
|
|
|
|
|
<div class="message-content">{{ message.content || '暂无内容' }}</div>
|
|
|
|
|
<div class="message-time">{{ formatDateTime(message.created_at) }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-else class="empty-messages">
|
|
|
|
|
<i class="el-icon-chat-dot-round" style="font-size: 48px; color: #909399;"></i>
|
|
|
|
|
<p style="margin-top: 16px; color: #909399;">暂无消息记录</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<span slot="footer" class="dialog-footer">
|
|
|
|
|
<el-button @click="interactionDetailVisible = false">关闭</el-button>
|
|
|
|
|
</span>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import { supplyDemandChart } from '@/api/student'
|
|
|
|
|
import { supplyDemandChart, getMessageList } from '@/api/student'
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: 'InteractionStats',
|
|
|
|
|
@ -172,13 +247,19 @@ export default {
|
|
|
|
|
return {
|
|
|
|
|
filter: {
|
|
|
|
|
timeRange: 30,
|
|
|
|
|
supplyType: ''
|
|
|
|
|
supplyType: '',
|
|
|
|
|
startDate: '',
|
|
|
|
|
endDate: ''
|
|
|
|
|
},
|
|
|
|
|
currentPage: 1,
|
|
|
|
|
pageSize: 10,
|
|
|
|
|
totalCount: 0,
|
|
|
|
|
loading: false,
|
|
|
|
|
tableLoading: false,
|
|
|
|
|
interactionDetailVisible: false,
|
|
|
|
|
messageList: [],
|
|
|
|
|
currentSupplyDemand: null,
|
|
|
|
|
currentTargetUser: null,
|
|
|
|
|
chartData: {
|
|
|
|
|
totalCount: 0,
|
|
|
|
|
messageCount: 0,
|
|
|
|
|
@ -194,15 +275,41 @@ export default {
|
|
|
|
|
this.fetchChartData()
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
// 处理时间范围变化
|
|
|
|
|
handleTimeRangeChange(value) {
|
|
|
|
|
if (value === 'custom') {
|
|
|
|
|
// 设置默认的自定义时间范围为最近30天
|
|
|
|
|
const endDate = new Date()
|
|
|
|
|
const startDate = new Date()
|
|
|
|
|
startDate.setDate(endDate.getDate() - 30)
|
|
|
|
|
|
|
|
|
|
this.filter.startDate = startDate.toISOString().split('T')[0]
|
|
|
|
|
this.filter.endDate = endDate.toISOString().split('T')[0]
|
|
|
|
|
} else {
|
|
|
|
|
// 清空自定义日期
|
|
|
|
|
this.filter.startDate = ''
|
|
|
|
|
this.filter.endDate = ''
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 获取图表数据
|
|
|
|
|
async fetchChartData() {
|
|
|
|
|
try {
|
|
|
|
|
this.loading = true
|
|
|
|
|
|
|
|
|
|
// 根据timeRange计算开始和结束日期
|
|
|
|
|
const endDate = new Date()
|
|
|
|
|
const startDate = new Date()
|
|
|
|
|
startDate.setDate(endDate.getDate() - this.filter.timeRange)
|
|
|
|
|
let startDate, endDate
|
|
|
|
|
|
|
|
|
|
if (this.filter.timeRange === 'custom') {
|
|
|
|
|
// 使用自定义时间段
|
|
|
|
|
startDate = new Date(this.filter.startDate)
|
|
|
|
|
endDate = new Date(this.filter.endDate)
|
|
|
|
|
} else {
|
|
|
|
|
// 使用预设时间段
|
|
|
|
|
endDate = new Date()
|
|
|
|
|
startDate = new Date()
|
|
|
|
|
startDate.setDate(endDate.getDate() - this.filter.timeRange)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const params = {
|
|
|
|
|
start_date: startDate.toISOString().split('T')[0], // YYYY-MM-DD格式
|
|
|
|
|
@ -222,9 +329,9 @@ export default {
|
|
|
|
|
totalCount: response.supply_demand_count || 0,
|
|
|
|
|
messageCount: response.message_count || 0,
|
|
|
|
|
interactionCount: response.interaction_count || 0,
|
|
|
|
|
publishChange: response.supply_demand_growth_rate?.rate || 0,
|
|
|
|
|
messageChange: response.message_growth_rate?.rate || 0,
|
|
|
|
|
interactionChange: response.interaction_growth_rate?.rate || 0
|
|
|
|
|
publishChange: (response.supply_demand_growth_rate && response.supply_demand_growth_rate.rate) || 0,
|
|
|
|
|
messageChange: (response.message_growth_rate && response.message_growth_rate.rate) || 0,
|
|
|
|
|
interactionChange: (response.interaction_growth_rate && response.interaction_growth_rate.rate) || 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新交互记录列表
|
|
|
|
|
@ -252,6 +359,22 @@ export default {
|
|
|
|
|
|
|
|
|
|
// 更新统计数据
|
|
|
|
|
async updateStats() {
|
|
|
|
|
// 验证自定义时间段的日期
|
|
|
|
|
if (this.filter.timeRange === 'custom') {
|
|
|
|
|
if (!this.filter.startDate || !this.filter.endDate) {
|
|
|
|
|
this.$message.warning('请选择开始和结束日期')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const startDate = new Date(this.filter.startDate)
|
|
|
|
|
const endDate = new Date(this.filter.endDate)
|
|
|
|
|
|
|
|
|
|
if (startDate > endDate) {
|
|
|
|
|
this.$message.error('开始日期不能晚于结束日期')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await this.fetchChartData()
|
|
|
|
|
this.$message.success('统计数据已更新')
|
|
|
|
|
},
|
|
|
|
|
@ -347,6 +470,83 @@ export default {
|
|
|
|
|
return 'info'
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 显示交互详情
|
|
|
|
|
async showInteractionDetail(row, targetUser) {
|
|
|
|
|
// 验证参数
|
|
|
|
|
if (!row || !row.user) {
|
|
|
|
|
this.$message.error('无效的供需信息或用户信息')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.currentSupplyDemand = row
|
|
|
|
|
this.currentTargetUser = targetUser
|
|
|
|
|
this.interactionDetailVisible = true
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 获取该用户与发布者之间的消息列表
|
|
|
|
|
const params = {
|
|
|
|
|
to_user_id: (row.user && row.user.id) || '',
|
|
|
|
|
user_id: (row.user && row.user.id) || '',
|
|
|
|
|
page_size: 50,
|
|
|
|
|
page: 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const response = await getMessageList(params)
|
|
|
|
|
if (response && response.data) {
|
|
|
|
|
this.messageList = response.data
|
|
|
|
|
} else {
|
|
|
|
|
this.messageList = []
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取消息列表失败:', error)
|
|
|
|
|
// this.$message.warning('获取消息列表失败,显示模拟数据')
|
|
|
|
|
|
|
|
|
|
// 生成mock数据
|
|
|
|
|
this.messageList = this.generateMockMessages(row, targetUser)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 关闭对话框
|
|
|
|
|
handleCloseDialog(done) {
|
|
|
|
|
this.interactionDetailVisible = false
|
|
|
|
|
this.messageList = []
|
|
|
|
|
this.currentSupplyDemand = null
|
|
|
|
|
this.currentTargetUser = null
|
|
|
|
|
done()
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 获取对话框标题
|
|
|
|
|
getDialogTitle() {
|
|
|
|
|
if (!this.currentSupplyDemand || !this.currentTargetUser) {
|
|
|
|
|
return '交互详情'
|
|
|
|
|
}
|
|
|
|
|
const publisherName = (this.currentSupplyDemand.user && this.currentSupplyDemand.user.name) || '发布者'
|
|
|
|
|
const targetUserName = (this.currentTargetUser && this.currentTargetUser.name) || '用户'
|
|
|
|
|
return `${publisherName} 与 ${targetUserName} 的交互详情`
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 获取消息气泡样式类
|
|
|
|
|
getMessageBubbleClass(message) {
|
|
|
|
|
// 判断消息发送者是否为发布者
|
|
|
|
|
const isPublisher = this.currentSupplyDemand &&
|
|
|
|
|
this.currentSupplyDemand.user &&
|
|
|
|
|
message.from_user &&
|
|
|
|
|
message.from_user.id === this.currentSupplyDemand.user.id
|
|
|
|
|
|
|
|
|
|
return isPublisher ? 'message-right' : 'message-left'
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 获取消息项样式类
|
|
|
|
|
getMessageItemClass(message) {
|
|
|
|
|
// 判断消息发送者是否为发布者
|
|
|
|
|
const isPublisher = this.currentSupplyDemand &&
|
|
|
|
|
this.currentSupplyDemand.user &&
|
|
|
|
|
message.from_user &&
|
|
|
|
|
message.from_user.id === this.currentSupplyDemand.user.id
|
|
|
|
|
|
|
|
|
|
return isPublisher ? 'message-right' : 'message-left'
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 格式化日期时间
|
|
|
|
|
formatDateTime(dateTime) {
|
|
|
|
|
if (!dateTime) return '-'
|
|
|
|
|
@ -380,6 +580,71 @@ export default {
|
|
|
|
|
console.error('格式化日期时间失败:', error, dateTime)
|
|
|
|
|
return '-'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 生成模拟消息数据
|
|
|
|
|
generateMockMessages(supplyDemand, targetUser) {
|
|
|
|
|
const mockMessages = []
|
|
|
|
|
const now = new Date()
|
|
|
|
|
|
|
|
|
|
// 生成发布者的消息
|
|
|
|
|
const publisherName = (supplyDemand.user && supplyDemand.user.name) || '发布者'
|
|
|
|
|
|
|
|
|
|
// 模拟发布者询问消息
|
|
|
|
|
mockMessages.push({
|
|
|
|
|
id: 'mock_1',
|
|
|
|
|
from_user: {
|
|
|
|
|
id: supplyDemand.user ? supplyDemand.user.id : 'mock_publisher',
|
|
|
|
|
name: publisherName
|
|
|
|
|
},
|
|
|
|
|
to_user: targetUser,
|
|
|
|
|
content: `您好!看到您对我们发布的"${supplyDemand.title || '供需信息'}"感兴趣,请问有什么具体需求吗?`,
|
|
|
|
|
created_at: new Date(now.getTime() - 2 * 60 * 60 * 1000).toISOString() // 2小时前
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 模拟用户回复
|
|
|
|
|
mockMessages.push({
|
|
|
|
|
id: 'mock_2',
|
|
|
|
|
from_user: targetUser,
|
|
|
|
|
to_user: supplyDemand.user,
|
|
|
|
|
content: '您好!我对这个很感兴趣,想了解更多详细信息,可以详细介绍一下吗?',
|
|
|
|
|
created_at: new Date(now.getTime() - 1.5 * 60 * 60 * 1000).toISOString() // 1.5小时前
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 模拟发布者详细回复
|
|
|
|
|
mockMessages.push({
|
|
|
|
|
id: 'mock_3',
|
|
|
|
|
from_user: {
|
|
|
|
|
id: supplyDemand.user ? supplyDemand.user.id : 'mock_publisher',
|
|
|
|
|
name: publisherName
|
|
|
|
|
},
|
|
|
|
|
to_user: targetUser,
|
|
|
|
|
content: '当然可以!具体情况是这样的:' + (supplyDemand.content ? supplyDemand.content.substring(0, 100) + '...' : '这是一个很好的机会,我们提供专业的服务和优质的产品。'),
|
|
|
|
|
created_at: new Date(now.getTime() - 1 * 60 * 60 * 1000).toISOString() // 1小时前
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 模拟用户确认
|
|
|
|
|
mockMessages.push({
|
|
|
|
|
id: 'mock_4',
|
|
|
|
|
from_user: targetUser,
|
|
|
|
|
to_user: supplyDemand.user,
|
|
|
|
|
content: '听起来很不错!我想进一步了解合作细节,方便电话沟通吗?',
|
|
|
|
|
created_at: new Date(now.getTime() - 30 * 60 * 1000).toISOString() // 30分钟前
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 模拟发布者提供联系方式
|
|
|
|
|
mockMessages.push({
|
|
|
|
|
id: 'mock_5',
|
|
|
|
|
from_user: {
|
|
|
|
|
id: supplyDemand.user ? supplyDemand.user.id : 'mock_publisher',
|
|
|
|
|
name: publisherName
|
|
|
|
|
},
|
|
|
|
|
to_user: targetUser,
|
|
|
|
|
content: '好的,我的联系电话是:138****8888,微信:wxid_123,欢迎随时联系!',
|
|
|
|
|
created_at: new Date(now.getTime() - 15 * 60 * 1000).toISOString() // 15分钟前
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return mockMessages
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
@ -506,6 +771,13 @@ export default {
|
|
|
|
|
margin-bottom: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-description {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: #909399;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-change {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
display: flex;
|
|
|
|
|
@ -658,4 +930,164 @@ export default {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 交互详情对话框样式
|
|
|
|
|
.interaction-detail-dialog {
|
|
|
|
|
min-height: 400px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.supply-demand-info {
|
|
|
|
|
background: #f8f9fa;
|
|
|
|
|
padding: 15px;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
border-left: 4px solid #409EFF;
|
|
|
|
|
|
|
|
|
|
h4 {
|
|
|
|
|
margin: 0 0 8px 0;
|
|
|
|
|
color: #2c3e50;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.supply-demand-content {
|
|
|
|
|
margin: 0;
|
|
|
|
|
color: #666;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.conversation-users {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
padding: 15px;
|
|
|
|
|
background: #f8f9fa;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
border: 1px solid #e9ecef;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.user-indicator {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
|
|
|
|
&.left {
|
|
|
|
|
.user-avatar-small {
|
|
|
|
|
background: #f0f0f0;
|
|
|
|
|
color: #333;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.right {
|
|
|
|
|
.user-avatar-small {
|
|
|
|
|
background: #67C23A;
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.user-avatar-small {
|
|
|
|
|
width: 24px;
|
|
|
|
|
height: 24px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.user-name {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
color: #2c3e50;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-list-container {
|
|
|
|
|
max-height: 400px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-list {
|
|
|
|
|
padding: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-item {
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
|
|
|
|
|
&.message-right {
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.message-left {
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-bubble {
|
|
|
|
|
max-width: 70%;
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
border-radius: 18px;
|
|
|
|
|
position: relative;
|
|
|
|
|
word-wrap: break-word;
|
|
|
|
|
|
|
|
|
|
&.message-left {
|
|
|
|
|
background: #f0f0f0;
|
|
|
|
|
color: #333;
|
|
|
|
|
margin-right: auto;
|
|
|
|
|
border-bottom-left-radius: 4px;
|
|
|
|
|
margin-left: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.message-right {
|
|
|
|
|
background: #67C23A;
|
|
|
|
|
color: white;
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
border-bottom-right-radius: 4px;
|
|
|
|
|
margin-right: 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-time {
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
color: #999;
|
|
|
|
|
margin-top: 6px;
|
|
|
|
|
text-align: right;
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-content {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.empty-messages {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
padding: 60px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.empty-content {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
padding: 40px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.clickable-user {
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
color: #66b1ff !important;
|
|
|
|
|
text-decoration: none !important;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|