客户超额消息通知
This commit is contained in:
@@ -180,7 +180,7 @@
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="290" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['customersupplier:customerinfo:edit']">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['customersupplier:customerinfo:remove']">删除</el-button>
|
||||
<!-- <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['customersupplier:customerinfo:remove']">删除</el-button>-->
|
||||
<el-button link type="primary" icon="Wallet" @click="handleCustomerMoney(scope.row)">客户余额</el-button>
|
||||
<el-button link type="primary" icon="Document" @click="handleViewLog(scope.row)" v-hasPermi="['customersupplier:customerinfo:query']">日志</el-button>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,399 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-tabs v-model="activeTab" class="notify-tabs">
|
||||
<!-- ============ 通知规则 ============ -->
|
||||
<el-tab-pane label="通知配置" name="rule">
|
||||
<el-form :model="ruleQuery" ref="ruleQueryRef" :inline="true" v-show="showSearch" label-width="80px">
|
||||
<el-form-item label="规则名称" prop="ruleName">
|
||||
<el-input v-model="ruleQuery.ruleName" placeholder="请输入规则名称" clearable style="width: 200px"
|
||||
@keyup.enter="handleRuleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="ruleQuery.status" placeholder="全部" clearable style="width: 140px">
|
||||
<el-option v-for="d in sys_normal_disable" :key="d.value" :label="d.label" :value="d.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleRuleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetRuleQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAddRule" v-hasPermi="['system:notify:add']">新增配置</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="RefreshRight" :loading="scanLoading"
|
||||
@click="handleScanNow" v-hasPermi="['system:notify:edit']">立即扫描超额客户</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Refresh" @click="getRuleList">刷新</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getRuleList" />
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="ruleLoading" :data="ruleList" border>
|
||||
<el-table-column label="规则名称" prop="ruleName" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="通知事件" align="center" width="150">
|
||||
<template #default>
|
||||
<el-tag type="danger" size="small">客户已超额(欠费)</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="通知对象" align="left" min-width="260">
|
||||
<template #default="scope">
|
||||
<el-tag v-for="t in scope.row.targets" :key="t.targetType + t.refId"
|
||||
:type="t.targetType === 'USER' ? 'primary' : 'success'" size="small"
|
||||
style="margin: 2px;">
|
||||
{{ t.targetType === 'USER' ? '用户' : '角色' }}:{{ t.refName }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" width="90">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1"
|
||||
@change="handleStatusChange(scope.row)" :disabled="!checkPermi(['system:notify:edit'])" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
|
||||
<template #default="scope">{{ parseTime(scope.row.createTime) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="150" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Edit" @click="handleEditRule(scope.row)"
|
||||
v-hasPermi="['system:notify:edit']">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDeleteRule(scope.row)"
|
||||
v-hasPermi="['system:notify:remove']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-show="ruleTotal > 0" :total="ruleTotal" v-model:page="ruleQuery.pageNum"
|
||||
v-model:limit="ruleQuery.pageSize" @pagination="getRuleList" />
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- ============ 通知记录 ============ -->
|
||||
<el-tab-pane label="通知记录" name="message">
|
||||
<el-form :model="messageQuery" :inline="true" label-width="90px">
|
||||
<el-form-item label="客户名称">
|
||||
<el-input v-model="messageQuery.customerName" placeholder="客户名称" clearable style="width: 180px"
|
||||
@keyup.enter="getMessageList" />
|
||||
</el-form-item>
|
||||
<el-form-item label="标题">
|
||||
<el-input v-model="messageQuery.title" placeholder="标题关键字" clearable style="width: 180px"
|
||||
@keyup.enter="getMessageList" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleMessageQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetMessageQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="messageLoading" :data="messageList" border>
|
||||
<el-table-column label="标题" prop="title" min-width="220" show-overflow-tooltip />
|
||||
<el-table-column label="客户编号" align="center" prop="customerCode" width="140" show-overflow-tooltip />
|
||||
<el-table-column label="客户名称" align="center" prop="customerName" width="200" show-overflow-tooltip />
|
||||
<el-table-column label="超额金额(CNY)" align="center" width="130">
|
||||
<template #default="scope"><span class="money-out">{{ formatMoney(scope.row.overdueAmount) }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已读/接收" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.readCount === scope.row.receiveCount ? 'info' : 'warning'" size="small">
|
||||
{{ scope.row.readCount }}/{{ scope.row.receiveCount }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="通知时间" align="center" prop="createTime" width="160">
|
||||
<template #default="scope">{{ parseTime(scope.row.createTime) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="80">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="handleViewMessage(scope.row)"
|
||||
v-hasPermi="['system:notify:query']">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-show="messageTotal > 0" :total="messageTotal" v-model:page="messageQuery.pageNum"
|
||||
v-model:limit="messageQuery.pageSize" @pagination="getMessageList" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- ====== 规则新增/修改弹窗 ====== -->
|
||||
<el-dialog :title="ruleDialog.title" v-model="ruleDialog.visible" width="640px" append-to-body>
|
||||
<el-form ref="ruleFormRef" :model="ruleDialog.form" :rules="ruleRules" label-width="100px">
|
||||
<el-form-item label="规则名称" prop="ruleName">
|
||||
<el-input v-model="ruleDialog.form.ruleName" placeholder="例如:超额通知-财务与销售" maxlength="100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="通知事件">
|
||||
<el-tag type="danger">客户已超额(欠费)</el-tag>
|
||||
<span class="form-tip">每日 09:00 自动扫描,客户恢复正常后再次超额会再次通知;同一客户每天最多通知一次。</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="通知用户" prop="userIds">
|
||||
<el-select v-model="ruleDialog.form.userIds" multiple filterable collapse-tags collapse-tags-tooltip
|
||||
placeholder="选择要通知的用户(可多选)" style="width: 100%;">
|
||||
<el-option v-for="u in userOptions" :key="u.userId"
|
||||
:label="(u.nickName || u.userName) + '(' + u.userName + ')'" :value="u.userId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="通知角色" prop="roleIds">
|
||||
<el-select v-model="ruleDialog.form.roleIds" multiple filterable collapse-tags collapse-tags-tooltip
|
||||
placeholder="选择角色后,该角色下所有启用用户都会收到" style="width: 100%;">
|
||||
<el-option v-for="r in roleOptions" :key="r.roleId" :label="r.roleName" :value="r.roleId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="ruleDialog.form.status">
|
||||
<el-radio v-for="d in sys_normal_disable" :key="d.value" :value="d.value">{{ d.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="ruleDialog.form.remark" type="textarea" :rows="2" maxlength="500" />
|
||||
</el-form-item>
|
||||
<div class="form-tip" style="margin-left: 100px;">
|
||||
用户与角色合并去重通知;被停用的用户/角色不能选择。通知本身不涉及任何余额、额度变动。
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button type="primary" :loading="ruleDialog.loading" @click="submitRule">确 定</el-button>
|
||||
<el-button @click="ruleDialog.visible = false">取 消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- ====== 消息详情弹窗 ====== -->
|
||||
<el-dialog title="通知详情" v-model="messageDialog.visible" width="680px" append-to-body>
|
||||
<el-descriptions :column="2" border size="small" v-if="messageDialog.data">
|
||||
<el-descriptions-item label="标题" :span="2">{{ messageDialog.data.title }}</el-descriptions-item>
|
||||
<el-descriptions-item label="客户">{{ messageDialog.data.customerName }}({{ messageDialog.data.customerCode }})</el-descriptions-item>
|
||||
<el-descriptions-item label="超额金额(CNY)">{{ formatMoney(messageDialog.data.overdueAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="通知时间" :span="2">{{ parseTime(messageDialog.data.createTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="内容" :span="2">{{ messageDialog.data.content }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div class="recv-title">接收情况</div>
|
||||
<el-table :data="messageDialog.receives" border size="small" max-height="260">
|
||||
<el-table-column label="接收人" prop="nickName" align="center" width="160">
|
||||
<template #default="scope">{{ scope.row.nickName || scope.row.userName }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="账号" prop="userName" align="center" width="160" />
|
||||
<el-table-column label="状态" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.readFlag === '1' ? 'success' : 'info'" size="small">
|
||||
{{ scope.row.readFlag === '1' ? '已读' : '未读' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="阅读时间" align="center">
|
||||
<template #default="scope">{{ scope.row.readTime ? parseTime(scope.row.readTime) : '-' }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="messageDialog.visible = false">关 闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Notify">
|
||||
import { optionUserList } from '@/api/system/user'
|
||||
import request from '@/utils/request'
|
||||
import {
|
||||
listNotifyRule, getNotifyRule, addNotifyRule, updateNotifyRule, delNotifyRule,
|
||||
scanOverdueNow, listNotifyMessage, getNotifyMessage
|
||||
} from '@/api/system/notify'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { sys_normal_disable } = useDict('sys_normal_disable')
|
||||
const checkPermi = (keys) => proxy.$auth.hasPermi(keys)
|
||||
const parseTime = proxy.parseTime
|
||||
const formatMoney = (v) => (v === null || v === undefined || v === '' ? '0.00' : Number(v).toFixed(2))
|
||||
|
||||
const activeTab = ref('rule')
|
||||
const showSearch = ref(true)
|
||||
|
||||
/* ---------- 规则列表 ---------- */
|
||||
const ruleLoading = ref(false)
|
||||
const ruleTotal = ref(0)
|
||||
const ruleList = ref([])
|
||||
const ruleQuery = reactive({ pageNum: 1, pageSize: 10, ruleName: '', status: '' })
|
||||
|
||||
function getRuleList() {
|
||||
ruleLoading.value = true
|
||||
listNotifyRule(ruleQuery).then(res => {
|
||||
ruleList.value = res.rows
|
||||
ruleTotal.value = res.total
|
||||
}).finally(() => { ruleLoading.value = false })
|
||||
}
|
||||
function handleRuleQuery() { ruleQuery.pageNum = 1; getRuleList() }
|
||||
function resetRuleQuery() { proxy.resetForm('ruleQueryRef'); handleRuleQuery() }
|
||||
|
||||
/* ---------- 规则编辑 ---------- */
|
||||
const ruleFormRef = ref(null)
|
||||
const ruleDialog = reactive({ visible: false, title: '', loading: false, form: {} })
|
||||
const userOptions = ref([])
|
||||
const roleOptions = ref([])
|
||||
const ruleRules = {
|
||||
ruleName: [{ required: true, message: '请输入规则名称', trigger: 'blur' }],
|
||||
userIds: [{ type: 'array', required: false }],
|
||||
roleIds: [{ type: 'array', required: false }]
|
||||
}
|
||||
|
||||
function loadOptions() {
|
||||
if (!userOptions.value.length) {
|
||||
optionUserList().then(res => { userOptions.value = res.data || [] })
|
||||
}
|
||||
if (!roleOptions.value.length) {
|
||||
request({ url: '/system/role/optionselect', method: 'get' })
|
||||
.then(res => { roleOptions.value = res.data || [] })
|
||||
}
|
||||
}
|
||||
|
||||
function resetRuleForm() {
|
||||
ruleDialog.form = {
|
||||
ruleId: undefined,
|
||||
ruleName: '',
|
||||
eventType: 'CUSTOMER_OVERDUE',
|
||||
status: '0',
|
||||
remark: '',
|
||||
userIds: [],
|
||||
roleIds: [],
|
||||
targets: []
|
||||
}
|
||||
proxy.resetForm('ruleFormRef')
|
||||
}
|
||||
|
||||
function handleAddRule() {
|
||||
resetRuleForm()
|
||||
loadOptions()
|
||||
ruleDialog.title = '新增通知配置'
|
||||
ruleDialog.visible = true
|
||||
}
|
||||
|
||||
function handleEditRule(row) {
|
||||
resetRuleForm()
|
||||
loadOptions()
|
||||
getNotifyRule(row.ruleId).then(res => {
|
||||
const d = res.data || {}
|
||||
ruleDialog.form = {
|
||||
ruleId: d.ruleId,
|
||||
ruleName: d.ruleName,
|
||||
eventType: d.eventType || 'CUSTOMER_OVERDUE',
|
||||
status: d.status,
|
||||
remark: d.remark || '',
|
||||
userIds: (d.targets || []).filter(t => t.targetType === 'USER').map(t => t.refId),
|
||||
roleIds: (d.targets || []).filter(t => t.targetType === 'ROLE').map(t => t.refId),
|
||||
targets: d.targets || []
|
||||
}
|
||||
ruleDialog.title = '修改通知配置'
|
||||
ruleDialog.visible = true
|
||||
})
|
||||
}
|
||||
|
||||
function submitRule() {
|
||||
ruleFormRef.value.validate(valid => {
|
||||
if (!valid) return
|
||||
const f = ruleDialog.form
|
||||
if (!f.userIds.length && !f.roleIds.length) {
|
||||
proxy.$modal.msgError('请至少选择一个通知用户或角色')
|
||||
return
|
||||
}
|
||||
const targets = []
|
||||
f.userIds.forEach(uid => {
|
||||
const u = userOptions.value.find(x => x.userId === uid)
|
||||
targets.push({ targetType: 'USER', refId: uid, refName: u ? (u.nickName || u.userName) : String(uid) })
|
||||
})
|
||||
f.roleIds.forEach(rid => {
|
||||
const r = roleOptions.value.find(x => x.roleId === rid)
|
||||
targets.push({ targetType: 'ROLE', refId: rid, refName: r ? r.roleName : String(rid) })
|
||||
})
|
||||
const payload = {
|
||||
ruleId: f.ruleId,
|
||||
ruleName: f.ruleName,
|
||||
eventType: 'CUSTOMER_OVERDUE',
|
||||
status: f.status,
|
||||
remark: f.remark,
|
||||
targets
|
||||
}
|
||||
ruleDialog.loading = true
|
||||
const action = f.ruleId ? updateNotifyRule(payload) : addNotifyRule(payload)
|
||||
action.then(() => {
|
||||
proxy.$modal.msgSuccess(f.ruleId ? '修改成功' : '新增成功')
|
||||
ruleDialog.visible = false
|
||||
getRuleList()
|
||||
}).finally(() => { ruleDialog.loading = false })
|
||||
})
|
||||
}
|
||||
|
||||
function handleStatusChange(row) {
|
||||
const text = row.status === '0' ? '启用' : '停用'
|
||||
proxy.$modal.confirm('确认' + text + '规则「' + row.ruleName + '」?').then(() => {
|
||||
return updateNotifyRule({
|
||||
ruleId: row.ruleId,
|
||||
ruleName: row.ruleName,
|
||||
eventType: row.eventType,
|
||||
status: row.status,
|
||||
targets: (row.targets || []).map(t => ({ targetType: t.targetType, refId: t.refId, refName: t.refName }))
|
||||
})
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess(text + '成功')
|
||||
}).catch(() => {
|
||||
row.status = row.status === '0' ? '1' : '0'
|
||||
})
|
||||
}
|
||||
|
||||
function handleDeleteRule(row) {
|
||||
proxy.$modal.confirm('确认删除规则「' + row.ruleName + '」?删除后不再产生新通知,历史通知记录保留。').then(() => {
|
||||
return delNotifyRule(row.ruleId)
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
getRuleList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
const scanLoading = ref(false)
|
||||
function handleScanNow() {
|
||||
proxy.$modal.confirm('立即扫描所有超额(欠费)客户并按启用配置投递消息?同一客户当天只通知一次。').then(() => {
|
||||
scanLoading.value = true
|
||||
return scanOverdueNow()
|
||||
}).then(res => {
|
||||
proxy.$modal.msgSuccess(res.msg || '扫描完成')
|
||||
if (activeTab.value === 'message') getMessageList()
|
||||
}).catch(() => {}).finally(() => { scanLoading.value = false })
|
||||
}
|
||||
|
||||
/* ---------- 消息记录 ---------- */
|
||||
const messageLoading = ref(false)
|
||||
const messageTotal = ref(0)
|
||||
const messageList = ref([])
|
||||
const messageQuery = reactive({ pageNum: 1, pageSize: 10, customerName: '', title: '' })
|
||||
|
||||
function getMessageList() {
|
||||
messageLoading.value = true
|
||||
listNotifyMessage(messageQuery).then(res => {
|
||||
messageList.value = res.rows
|
||||
messageTotal.value = res.total
|
||||
}).finally(() => { messageLoading.value = false })
|
||||
}
|
||||
function handleMessageQuery() { messageQuery.pageNum = 1; getMessageList() }
|
||||
function resetMessageQuery() {
|
||||
messageQuery.customerName = ''
|
||||
messageQuery.title = ''
|
||||
handleMessageQuery()
|
||||
}
|
||||
|
||||
const messageDialog = reactive({ visible: false, data: null, receives: [] })
|
||||
function handleViewMessage(row) {
|
||||
getNotifyMessage(row.messageId).then(res => {
|
||||
messageDialog.data = res.data
|
||||
messageDialog.receives = (res.data && res.data.receives) || []
|
||||
messageDialog.visible = true
|
||||
})
|
||||
}
|
||||
|
||||
getRuleList()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.notify-tabs { margin-top: -8px; }
|
||||
.mb8 { margin-bottom: 12px; }
|
||||
.money-out { color: #f56c6c; font-weight: 600; }
|
||||
.form-tip { color: #909399; font-size: 12px; line-height: 1.6; margin-left: 8px; }
|
||||
.recv-title { font-weight: 600; margin: 14px 0 8px; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user