xinli/xinlidsj/pages/message/send.vue
2026-02-24 16:49:05 +08:00

523 lines
17 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="page" :class="{ big: isH5 }">
<view class="panel">
<view class="title">发送消息</view>
<view class="subtitle">选择接收人并关联报告一键发送通知/消息</view>
</view>
<view class="panel">
<view class="section-title">发送给</view>
<view class="seg" style="margin-bottom: 18rpx;">
<view class="seg-item" :class="sendMode==='role'?'active':''" @tap="setSendMode('role')">按角色群发</view>
<view class="seg-item" :class="sendMode==='single'?'active':''" @tap="setSendMode('single')">指定用户单发</view>
</view>
<view v-if="sendMode==='single'">
<view class="section-title">收件人</view>
<input class="input" v-model="receiverKeyword" placeholder="输入姓名/编号查询收件人" />
<button class="btn" type="default" :disabled="receiverLoading" @click="searchReceivers">查询收件人</button>
<view v-if="receiverErrorMsg" class="error">{{ receiverErrorMsg }}</view>
<view v-if="receiverLoading" class="placeholder">加载中...</view>
<view v-else class="list">
<view v-if="receiverRows.length" class="placeholder">请选择收件人</view>
<view v-if="receiverRows.length">
<view class="item" v-for="u in receiverRows" :key="u.userId" @tap="selectReceiver(u)">
<view class="item-title">{{ u.nickName || ('用户#' + u.userId) }}</view>
<view class="item-desc">编号:{{ u.infoNumber || '—' }} userId{{ u.userId }}</view>
</view>
</view>
<view v-if="receiverId" class="hint">已选择收件人:{{ receiverLabel }}</view>
</view>
</view>
<view class="seg">
<view class="seg-item" :class="recipient==='area_manager'?'active':''" @tap="selectRecipient('area_manager')">监区负责人</view>
<view class="seg-item" :class="recipient==='psy_counselor'?'active':''" @tap="selectRecipient('psy_counselor')">心理辅导员</view>
</view>
<view class="section-title">标题(通知必填)</view>
<input class="input" v-model="title" placeholder="例如 下周全员测评" />
<view class="section-title">内容</view>
<textarea class="textarea" v-model="content" placeholder="请输入内容"></textarea>
<view class="section-title">选择报告(自动填充)</view>
<input class="input" v-model="reportKeyword" placeholder="输入姓名/编号,先查询人员" />
<button class="btn" type="default" :disabled="reportLoading" @click="searchPeople">查询人员</button>
<view v-if="reportErrorMsg" class="error">{{ reportErrorMsg }}</view>
<view v-if="reportLoading" class="placeholder">加载中...</view>
<view v-else class="list">
<view class="section-title">报告类型</view>
<view class="seg">
<view class="seg-item" :class="reportType==='assessment'?'active':''" @tap="setReportType('assessment')">测评报告</view>
<view class="seg-item" :class="reportType==='comprehensive'?'active':''" @tap="setReportType('comprehensive')">综合报告</view>
</view>
<view v-if="peopleRows.length" class="placeholder">请选择人员</view>
<view v-if="peopleRows.length">
<view class="item" v-for="p in peopleRows" :key="p.userId" @tap="selectPerson(p)">
<view class="item-title">{{ p.nickName || ('用户#' + p.userId) }}</view>
<view class="item-desc">编号:{{ p.infoNumber || '—' }} userId{{ p.userId }}</view>
</view>
</view>
<view v-if="reportRows.length === 0" class="placeholder">暂无报告</view>
<view v-else>
<view class="item" v-for="row in reportRows" :key="row.reportId" @tap="selectReport(row)">
<view class="item-title">{{ row.reportTitle || (reportType === 'assessment' ? `测评报告#${row.reportId}` : `综合报告#${row.reportId}`) }}</view>
<view class="item-desc">创建:{{ formatTime(row.createTime) }}</view>
</view>
</view>
</view>
<view v-if="bizType || linkUrl" class="hint">
已关联:{{ bizTypeLabel }}
</view>
<button class="btn" type="primary" :disabled="submitting" @click="submit">发送</button>
</view>
</view>
</template>
<script>
import { getToken } from '../../utils/auth'
import { sendNoticeToRoleKey } from '../../api/app/notice'
import { sendMessageToRoleKey, sendMessageToUser } from '../../api/app/message'
import { searchProfileOptions } from '../../api/app/profile'
import { listAssessmentReportOptionsByUserId } from '../../api/app/report'
import { listComprehensiveReportsByUserId } from '../../api/psychology/comprehensiveReport'
export default {
data() {
return {
isH5: false,
sendMode: 'role',
recipient: 'area_manager',
title: '',
content: '',
linkUrl: '',
bizType: '',
bizId: '',
receiverKeyword: '',
receiverLoading: false,
receiverErrorMsg: '',
receiverRows: [],
receiverId: null,
receiverLabel: '',
selectedUserId: null,
reportKeyword: '',
reportType: 'assessment',
reportLoading: false,
reportErrorMsg: '',
peopleRows: [],
reportRows: [],
submitting: false
}
},
onLoad() {
try {
const info = uni.getSystemInfoSync()
const p = info ? (info.uniPlatform || info.platform) : ''
this.isH5 = p === 'web' || p === 'h5'
} catch (e) {
this.isH5 = false
}
const token = getToken()
if (!token) {
uni.showToast({ title: '请先到“我的”页面登录', icon: 'none' })
}
this.selectRecipient(this.recipient)
},
computed: {
bizTypeLabel() {
if (this.bizType === 'assessment_report' && this.bizId) {
return `测评报告#${this.bizId}`
}
if (this.bizType === 'report' && this.bizId) {
return `综合报告#${this.bizId}`
}
if (this.bizType === 'warning') {
return '预警中心'
}
if (this.linkUrl) {
return this.linkUrl
}
return ''
}
},
methods: {
setSendMode(m) {
if (m !== 'role' && m !== 'single') return
if (this.sendMode === m) return
this.sendMode = m
this.receiverErrorMsg = ''
this.receiverRows = []
this.receiverId = null
this.receiverLabel = ''
this.receiverKeyword = ''
},
async searchReceivers() {
this.receiverErrorMsg = ''
this.receiverRows = []
const kw = (this.receiverKeyword || '').trim()
if (!kw) {
this.receiverErrorMsg = '请输入姓名/编号'
return
}
this.receiverLoading = true
try {
const res = await searchProfileOptions(kw, 10)
const data = res && res.data ? res.data : null
if (!data || data.code !== 200) {
this.receiverErrorMsg = (data && data.msg) ? data.msg : '查询收件人失败'
return
}
this.receiverRows = data.data || []
if (!this.receiverRows.length) {
this.receiverErrorMsg = '未找到匹配收件人'
}
} catch (e) {
this.receiverErrorMsg = e && e.message ? e.message : '网络错误'
} finally {
this.receiverLoading = false
}
},
selectReceiver(u) {
if (!u || !u.userId) return
this.receiverId = u.userId
this.receiverRows = []
this.receiverErrorMsg = ''
this.receiverLabel = `${u.nickName || ('用户#' + u.userId)}${u.infoNumber ? '' + u.infoNumber + '' : ''}`
this.receiverKeyword = this.receiverLabel
},
setReportType(t) {
if (t !== 'assessment' && t !== 'comprehensive') return
if (this.reportType === t) return
this.reportType = t
this.reportErrorMsg = ''
this.reportRows = []
this.peopleRows = []
// 如果已选过人员,切换类型后自动重载报告列表
if (this.selectedUserId) {
this.loadReportsByUserId(this.selectedUserId)
}
},
async loadReportsByUserId(userId) {
if (!userId) return
this.reportErrorMsg = ''
this.reportRows = []
this.reportLoading = true
try {
if (this.reportType === 'assessment') {
const repRes = await listAssessmentReportOptionsByUserId(userId, 50)
const repData = repRes && repRes.data ? repRes.data : null
if (!repData || repData.code !== 200) {
this.reportErrorMsg = (repData && repData.msg) ? repData.msg : '获取测评报告列表失败'
return
}
this.reportRows = repData.data || []
} else {
const repRes = await listComprehensiveReportsByUserId(userId)
const repData = repRes && repRes.data ? repRes.data : null
if (!repData || repData.code !== 200) {
this.reportErrorMsg = (repData && repData.msg) ? repData.msg : '获取综合报告列表失败'
return
}
this.reportRows = repData.data || []
}
if (!this.reportRows.length) {
this.reportErrorMsg = this.reportType === 'assessment' ? '该人员暂无测评报告' : '该人员暂无综合报告'
}
} catch (e) {
this.reportErrorMsg = e && e.message ? e.message : '网络错误'
} finally {
this.reportLoading = false
}
},
selectRecipient(key) {
this.recipient = key
if (key === 'area_manager') {
this.linkUrl = '/pages/warning/index'
this.bizType = 'warning'
this.bizId = ''
if (!this.title) this.title = '通知'
if (!this.content) this.content = '请及时查看并处理。'
return
}
if (key === 'psy_counselor') {
this.linkUrl = '/pages/warning/index'
this.bizType = 'warning'
this.bizId = ''
if (!this.title) this.title = '异常预警'
if (!this.content) this.content = '某人员数据异常,请协助处理。'
}
},
formatTime(val) {
if (!val) return '—'
try {
const d = new Date(val)
if (isNaN(d.getTime())) return '—'
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
const hh = String(d.getHours()).padStart(2, '0')
const mm = String(d.getMinutes()).padStart(2, '0')
return `${y}-${m}-${day} ${hh}:${mm}`
} catch (e) {
return '—'
}
},
async searchPeople() {
this.reportErrorMsg = ''
this.selectedUserId = null
this.peopleRows = []
this.reportRows = []
const kw = (this.reportKeyword || '').trim()
if (!kw) {
this.reportErrorMsg = '请输入姓名/编号'
return
}
this.reportLoading = true
try {
const res = await searchProfileOptions(kw, 10)
const data = res && res.data ? res.data : null
if (!data || data.code !== 200) {
this.reportErrorMsg = (data && data.msg) ? data.msg : '查询人员失败'
return
}
this.peopleRows = data.data || []
if (!this.peopleRows.length) {
this.reportErrorMsg = '未找到匹配人员'
}
} catch (e) {
this.reportErrorMsg = e && e.message ? e.message : '网络错误'
} finally {
this.reportLoading = false
}
},
async selectPerson(p) {
if (!p || !p.userId) return
this.selectedUserId = p.userId
this.reportErrorMsg = ''
this.peopleRows = []
if (p.nickName || p.infoNumber) {
this.reportKeyword = `${p.nickName || ''}${p.infoNumber ? '' + p.infoNumber + '' : ''}`
}
await this.loadReportsByUserId(p.userId)
},
selectReport(row) {
if (!row || !row.reportId) return
this.bizType = this.reportType === 'assessment' ? 'assessment_report' : 'report'
this.bizId = String(row.reportId)
this.linkUrl = this.reportType === 'assessment'
? `/pages/report/detail?reportId=${encodeURIComponent(row.reportId)}&sourceType=assessment`
: `/pages/comprehensive/detail?reportId=${encodeURIComponent(row.reportId)}`
this.title = row.reportTitle || (this.reportType === 'assessment' ? `测评报告#${row.reportId}` : `综合报告#${row.reportId}`)
const reportLabel = this.reportType === 'assessment' ? '测评报告' : '综合报告'
this.content = this.recipient === 'area_manager' ? `请查看${reportLabel}并关注风险。` : `请查看${reportLabel}并给出建议。`
uni.showToast({ title: '已选择报告', icon: 'success' })
},
async submit() {
if (this.submitting) return
if (!this.content) {
uni.showToast({ title: '内容不能为空', icon: 'none' })
return
}
if (this.sendMode === 'role' && this.recipient === 'area_manager' && !this.title) {
uni.showToast({ title: '通知标题不能为空', icon: 'none' })
return
}
if (this.sendMode === 'single' && !this.receiverId) {
uni.showToast({ title: '请选择收件人', icon: 'none' })
return
}
this.submitting = true
uni.showLoading({ title: '发送中...' })
try {
let res
if (this.sendMode === 'single') {
const payload = {
receiverId: this.receiverId,
title: this.title,
content: this.content,
linkUrl: this.linkUrl,
bizType: this.bizType,
bizId: this.bizId
}
res = await sendMessageToUser(payload)
} else {
const payload = {
roleKey: this.recipient,
title: this.title,
content: this.content,
linkUrl: this.linkUrl,
bizType: this.bizType,
bizId: this.bizId
}
if (this.recipient === 'area_manager') {
res = await sendNoticeToRoleKey(payload)
} else {
res = await sendMessageToRoleKey(payload)
}
}
const data = res && res.data ? res.data : null
if (data && data.code === 200) {
uni.showToast({ title: '已发送', icon: 'success' })
} else {
uni.showToast({ title: (data && data.msg) || '发送失败', icon: 'none' })
}
} catch (e) {
uni.showToast({ title: '发送失败', icon: 'none' })
} finally {
uni.hideLoading()
this.submitting = false
}
}
}
}
</script>
<style>
.page {
min-height: 100vh;
padding: 32rpx;
box-sizing: border-box;
background: #f6f7fb;
}
.page.big {
padding: 14rpx 14rpx 120rpx;
background-image:
radial-gradient(1100rpx 520rpx at 50% 14%, rgba(43, 107, 255, 0.30) 0%, rgba(6, 16, 40, 0.0) 65%),
linear-gradient(180deg, rgba(5, 11, 24, 0.90) 0%, rgba(8, 20, 45, 0.85) 42%, rgba(6, 16, 40, 0.92) 100%),
url('/static/bg.png');
background-size: auto, auto, cover;
background-position: center, center, center;
background-repeat: no-repeat, no-repeat, no-repeat;
}
.panel {
background: #ffffff;
border-radius: 20rpx;
padding: 24rpx;
border: 1px solid rgba(0, 0, 0, 0.05);
margin-bottom: 24rpx;
}
.title {
font-size: 36rpx;
font-weight: 700;
color: #1f2329;
}
.subtitle {
margin-top: 10rpx;
font-size: 24rpx;
color: #646a73;
line-height: 36rpx;
}
.section-title {
font-size: 28rpx;
font-weight: 700;
color: #1f2329;
margin: 14rpx 0 12rpx;
}
.input {
background: #f7f8fa;
border-radius: 16rpx;
padding: 26rpx 22rpx;
font-size: 28rpx;
min-height: 96rpx;
line-height: 44rpx;
box-sizing: border-box;
}
.textarea {
background: #f7f8fa;
border-radius: 16rpx;
padding: 26rpx 22rpx;
font-size: 28rpx;
box-sizing: border-box;
min-height: 360rpx;
line-height: 44rpx;
}
.btn {
width: 100%;
margin-top: 18rpx;
}
.row {
display: flex;
justify-content: space-between;
margin-top: 14rpx;
}
.btn.half {
width: 48%;
}
.seg {
display: flex;
background: #f7f8fa;
border-radius: 16rpx;
overflow: hidden;
}
.seg-item {
flex: 1;
text-align: center;
padding: 18rpx 0;
font-size: 26rpx;
color: #646a73;
}
.seg-item.active {
background: #1677ff;
color: #ffffff;
font-weight: 700;
}
.placeholder {
margin-top: 12rpx;
font-size: 24rpx;
color: #646a73;
}
.error {
margin-top: 12rpx;
font-size: 24rpx;
color: #ef4444;
}
.list {
margin-top: 12rpx;
}
.item {
padding: 18rpx;
border-radius: 16rpx;
background: #f7f8fa;
margin-bottom: 12rpx;
}
.item-title {
font-size: 28rpx;
font-weight: 700;
color: #1f2329;
}
.item-desc {
margin-top: 8rpx;
font-size: 24rpx;
color: #646a73;
}
.page.big .panel,
.page.big .item {
border: 1px solid rgba(116, 216, 255, 0.22);
background: linear-gradient(180deg, rgba(10, 18, 38, 0.75) 0%, rgba(5, 10, 22, 0.55) 100%);
box-shadow: 0 12rpx 24rpx rgba(0, 0, 0, 0.35);
}
.page.big .title,
.page.big .section-title,
.page.big .card-title,
.page.big .item-title { color: rgba(235, 248, 255, 0.92); }
.page.big .subtitle,
.page.big .placeholder,
.page.big .item-desc { color: rgba(201, 242, 255, 0.65); }
.page.big .input,
.page.big .textarea {
background: rgba(7, 13, 28, 0.35);
border: 1px solid rgba(116, 216, 255, 0.18);
color: rgba(235, 248, 255, 0.92);
}
.page.big .seg { background: rgba(7, 13, 28, 0.35); border: 1px solid rgba(116, 216, 255, 0.18); }
.page.big .seg-item { color: rgba(201, 242, 255, 0.65); }
.page.big .seg-item.active { background: rgba(10, 18, 38, 0.75); color: rgba(235, 248, 255, 0.92); }
.page.big .btn[type="primary"],
.page.big .btn.primary { background: linear-gradient(90deg, rgba(116, 216, 255, 0.95) 0%, rgba(43, 107, 255, 0.92) 100%); color: #0b1226; }
.page.big .btn[type="default"],
.page.big .btn.ghost { background: rgba(7, 13, 28, 0.35); color: rgba(201, 242, 255, 0.86); border: 1px solid rgba(116, 216, 255, 0.18); }
</style>