循环发送页面完善 接口调试
This commit is contained in:
parent
d8cccea4c5
commit
5ca1288587
|
|
@ -36,3 +36,11 @@ export function delGroupAPI(data) {
|
|||
})
|
||||
}
|
||||
|
||||
// 获取所有分组
|
||||
export function getAllGroupAPI() {
|
||||
return request({
|
||||
url: '/group/getGroupSelect',
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 循环发送短信 - 查询列表
|
||||
export function listLoopSendAPI(query) {
|
||||
return request({
|
||||
url: '/msgJob/list',
|
||||
method: 'GET',
|
||||
params: query,
|
||||
})
|
||||
}
|
||||
|
||||
// 循环发送短信 - 新增
|
||||
export function addLoopSendAPI(data) {
|
||||
return request({
|
||||
url: '/msgJob/add',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 循环发送短信 - 修改
|
||||
export function updateLoopSendAPI(data) {
|
||||
return request({
|
||||
url: '/msgJob/update',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 循环发送短信 - 删除
|
||||
export function delLoopSendAPI(data) {
|
||||
return request({
|
||||
url: '/msgJob/delete',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 循环发送短信 - 详情
|
||||
export function getLoopSendDetailAPI(id) {
|
||||
return request({
|
||||
url: `/msgJob/getById`,
|
||||
method: 'POST',
|
||||
data: {
|
||||
id,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 循环发送短信 - 更新任务状态
|
||||
export function updateLoopSendStatusAPI(data) {
|
||||
return request({
|
||||
url: '/msgJob/updateLoopTaskStatus',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,610 @@
|
|||
<template>
|
||||
<div class="person-picker">
|
||||
<el-row :gutter="20" style="padding-bottom: 30px">
|
||||
<el-col :span="16">
|
||||
<!-- Tabs 切换 -->
|
||||
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
|
||||
<el-tab-pane label="人员" name="person">
|
||||
<template #label>
|
||||
<span>人员</span>
|
||||
<el-badge
|
||||
v-if="selectedPersonCount > 0"
|
||||
:value="selectedPersonCount"
|
||||
class="tab-badge"
|
||||
/>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="分组" name="group">
|
||||
<template #label>
|
||||
<span>分组</span>
|
||||
<el-badge
|
||||
v-if="selectedGroupCount > 0"
|
||||
:value="selectedGroupCount"
|
||||
class="tab-badge"
|
||||
/>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- 搜索栏 -->
|
||||
<div class="search-bar">
|
||||
<el-input
|
||||
v-if="activeTab === 'person'"
|
||||
v-model.trim="searchKeyword"
|
||||
placeholder="输入关键字"
|
||||
clearable
|
||||
prefix-icon="Search"
|
||||
@input="handleSearch"
|
||||
/>
|
||||
<el-input
|
||||
v-else
|
||||
v-model.trim="groupSearchKeyword"
|
||||
placeholder="输入关键字"
|
||||
clearable
|
||||
prefix-icon="Search"
|
||||
@input="handleGroupSearch"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 人员表格 -->
|
||||
<el-table
|
||||
v-if="activeTab === 'person'"
|
||||
border
|
||||
ref="personTableRef"
|
||||
:data="filteredPersonList"
|
||||
height="400"
|
||||
@selection-change="onPersonSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column
|
||||
show-overflow-tooltip
|
||||
prop="orgName"
|
||||
label="人员所属"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
show-overflow-tooltip
|
||||
prop="workerName"
|
||||
label="姓名"
|
||||
width="120"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column show-overflow-tooltip prop="sex" label="性别" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.sex == 1 ? '男' : '女' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
show-overflow-tooltip
|
||||
prop="phone"
|
||||
label="电话"
|
||||
align="center"
|
||||
/>
|
||||
</el-table>
|
||||
|
||||
<!-- 分组表格 -->
|
||||
<el-table
|
||||
v-else
|
||||
border
|
||||
ref="groupTableRef"
|
||||
:data="filteredGroupList"
|
||||
height="400"
|
||||
@selection-change="onGroupSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column
|
||||
show-overflow-tooltip
|
||||
prop="groupName"
|
||||
label="分组名称"
|
||||
align="center"
|
||||
/>
|
||||
</el-table>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧已选人员 -->
|
||||
<el-col :span="8">
|
||||
<div class="selected-persons">
|
||||
<div class="selected-header">
|
||||
<span>已选人员</span>
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="handleClearAll"
|
||||
:disabled="allSelectedPersons.length === 0"
|
||||
>
|
||||
清空
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="selected-list">
|
||||
<div
|
||||
v-for="person in allSelectedPersons"
|
||||
:key="person.id"
|
||||
class="selected-item"
|
||||
>
|
||||
<span>{{ person.workerName }}</span>
|
||||
<el-icon class="remove-icon" @click="handleRemovePerson(person.id)">
|
||||
<Close />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div v-if="allSelectedPersons.length === 0" class="empty-tip">
|
||||
暂未选择人员
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="PersonPicker">
|
||||
import { ref, computed, watch, nextTick, onMounted } from 'vue'
|
||||
import { Close } from '@element-plus/icons-vue'
|
||||
import { getAllPersonAPI } from '@/api/personManage/person.js'
|
||||
import { getAllGroupAPI } from '@/api/basicManage/groupManage.js'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'confirm'])
|
||||
|
||||
const activeTab = ref('person')
|
||||
const searchKeyword = ref('')
|
||||
const groupSearchKeyword = ref('')
|
||||
const personTableRef = ref(null)
|
||||
const groupTableRef = ref(null)
|
||||
|
||||
// 数据列表
|
||||
const personList = ref([])
|
||||
const groupList = ref([])
|
||||
|
||||
// 直接选择的人员ID列表(只在人员tab中选择的)
|
||||
const directSelectedPersonIds = ref([])
|
||||
|
||||
// 选中的分组ID列表
|
||||
const selectedGroupIds = ref([])
|
||||
|
||||
// 标志位:是否正在同步选中状态
|
||||
const isSyncing = ref(false)
|
||||
|
||||
// 过滤后的人员列表
|
||||
const filteredPersonList = computed(() => {
|
||||
if (!searchKeyword.value) {
|
||||
return personList.value
|
||||
}
|
||||
const keyword = searchKeyword.value.toLowerCase()
|
||||
return personList.value.filter((item) => {
|
||||
const orgName = (item.orgName || '').toLowerCase()
|
||||
const workerName = (item.workerName || '').toLowerCase()
|
||||
return orgName.includes(keyword) || workerName.includes(keyword)
|
||||
})
|
||||
})
|
||||
|
||||
// 过滤后的分组列表
|
||||
const filteredGroupList = computed(() => {
|
||||
if (!groupSearchKeyword.value) {
|
||||
return groupList.value
|
||||
}
|
||||
const keyword = groupSearchKeyword.value.toLowerCase()
|
||||
return groupList.value.filter((item) => {
|
||||
const groupName = (item.groupName || '').toLowerCase()
|
||||
return groupName.includes(keyword)
|
||||
})
|
||||
})
|
||||
|
||||
// 选中的分组数量
|
||||
const selectedGroupCount = computed(() => selectedGroupIds.value.length)
|
||||
|
||||
// 所有已选人员(包括直接选择的人员和从分组中选择的人员)
|
||||
const allSelectedPersons = computed(() => {
|
||||
const personMap = new Map()
|
||||
|
||||
// 添加直接选择的人员
|
||||
directSelectedPersonIds.value.forEach((personId) => {
|
||||
const person = personList.value.find((p) => p.id === personId)
|
||||
if (person) {
|
||||
personMap.set(person.id, {
|
||||
id: person.id,
|
||||
workerName: person.workerName,
|
||||
orgName: person.orgName,
|
||||
sex: person.sex,
|
||||
phone: person.phone,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 添加从分组中选择的人员(去重)
|
||||
selectedGroupIds.value.forEach((groupId) => {
|
||||
const group = groupList.value.find((g) => g.id === groupId)
|
||||
if (group && group.workerList && Array.isArray(group.workerList)) {
|
||||
group.workerList.forEach((worker) => {
|
||||
if (worker && worker.id) {
|
||||
// 如果已存在,不覆盖(保持直接选择的数据)
|
||||
if (!personMap.has(worker.id)) {
|
||||
personMap.set(worker.id, {
|
||||
id: worker.id,
|
||||
workerName: worker.workerName || worker.name || '',
|
||||
orgName: worker.orgName || '',
|
||||
sex: worker.sex,
|
||||
phone: worker.phone || '',
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return Array.from(personMap.values())
|
||||
})
|
||||
|
||||
// 直接选择的人员数量
|
||||
const selectedPersonCount = computed(() => {
|
||||
return directSelectedPersonIds.value.length
|
||||
})
|
||||
|
||||
// Tab切换
|
||||
const handleTabChange = (tabName) => {
|
||||
searchKeyword.value = ''
|
||||
groupSearchKeyword.value = ''
|
||||
nextTick(() => {
|
||||
if (tabName === 'person') {
|
||||
syncPersonTableSelection()
|
||||
} else {
|
||||
syncGroupTableSelection()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
nextTick(() => {
|
||||
syncPersonTableSelection()
|
||||
})
|
||||
}
|
||||
|
||||
const handleGroupSearch = () => {
|
||||
nextTick(() => {
|
||||
syncGroupTableSelection()
|
||||
})
|
||||
}
|
||||
|
||||
// 人员选择变化
|
||||
const onPersonSelectionChange = (selection) => {
|
||||
if (isSyncing.value) {
|
||||
return
|
||||
}
|
||||
|
||||
// 更新直接选择的人员ID列表
|
||||
directSelectedPersonIds.value = selection.map((item) => item.id)
|
||||
|
||||
// 触发更新,合并所有选中的人员
|
||||
updateSelectedPersons()
|
||||
}
|
||||
|
||||
// 更新选中的所有人(合并直接选择的人员和分组中的人员)
|
||||
const updateSelectedPersons = () => {
|
||||
const personMap = new Map()
|
||||
|
||||
// 添加直接选择的人员
|
||||
directSelectedPersonIds.value.forEach((personId) => {
|
||||
const person = personList.value.find((p) => p.id === personId)
|
||||
if (person) {
|
||||
personMap.set(person.id, {
|
||||
id: person.id,
|
||||
workerName: person.workerName,
|
||||
orgName: person.orgName,
|
||||
sex: person.sex,
|
||||
phone: person.phone,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 添加从分组中选择的人员(去重)
|
||||
selectedGroupIds.value.forEach((groupId) => {
|
||||
const group = groupList.value.find((g) => g.id === groupId)
|
||||
if (group && group.workerList && Array.isArray(group.workerList)) {
|
||||
group.workerList.forEach((worker) => {
|
||||
if (worker && worker.id) {
|
||||
// 如果已存在,不覆盖(保持直接选择的数据)
|
||||
if (!personMap.has(worker.id)) {
|
||||
personMap.set(worker.id, {
|
||||
id: worker.id,
|
||||
workerName: worker.workerName || worker.name || '',
|
||||
orgName: worker.orgName || '',
|
||||
sex: worker.sex,
|
||||
phone: worker.phone || '',
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 触发更新事件
|
||||
emit('update:modelValue', Array.from(personMap.values()))
|
||||
}
|
||||
|
||||
// 分组选择变化
|
||||
const onGroupSelectionChange = (selection) => {
|
||||
if (isSyncing.value) {
|
||||
return
|
||||
}
|
||||
|
||||
selectedGroupIds.value = selection.map((group) => group.id)
|
||||
|
||||
// 触发更新,合并所有选中的人员
|
||||
updateSelectedPersons()
|
||||
}
|
||||
|
||||
// 同步人员表格选中状态
|
||||
const syncPersonTableSelection = () => {
|
||||
if (!personTableRef.value || directSelectedPersonIds.value.length === 0) {
|
||||
if (personTableRef.value) {
|
||||
isSyncing.value = true
|
||||
personTableRef.value.clearSelection()
|
||||
nextTick(() => {
|
||||
isSyncing.value = false
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
isSyncing.value = true
|
||||
const selectedIds = new Set(directSelectedPersonIds.value.map((id) => Number(id)))
|
||||
|
||||
nextTick(() => {
|
||||
personTableRef.value.clearSelection()
|
||||
filteredPersonList.value.forEach((row) => {
|
||||
const rowId = Number(row.id)
|
||||
if (selectedIds.has(rowId)) {
|
||||
personTableRef.value.toggleRowSelection(row, true)
|
||||
}
|
||||
})
|
||||
nextTick(() => {
|
||||
isSyncing.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 同步分组表格选中状态
|
||||
const syncGroupTableSelection = () => {
|
||||
if (!groupTableRef.value || selectedGroupIds.value.length === 0) {
|
||||
if (groupTableRef.value) {
|
||||
isSyncing.value = true
|
||||
groupTableRef.value.clearSelection()
|
||||
nextTick(() => {
|
||||
isSyncing.value = false
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
isSyncing.value = true
|
||||
const selectedIds = new Set(selectedGroupIds.value.map((id) => Number(id)))
|
||||
|
||||
nextTick(() => {
|
||||
groupTableRef.value.clearSelection()
|
||||
filteredGroupList.value.forEach((row) => {
|
||||
const rowId = Number(row.id)
|
||||
if (selectedIds.has(rowId)) {
|
||||
groupTableRef.value.toggleRowSelection(row, true)
|
||||
}
|
||||
})
|
||||
nextTick(() => {
|
||||
isSyncing.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 移除单个人员
|
||||
const handleRemovePerson = (personId) => {
|
||||
// 检查该人员是否来自分组
|
||||
const groupsToRemove = []
|
||||
|
||||
selectedGroupIds.value.forEach((groupId) => {
|
||||
const group = groupList.value.find((g) => g.id === groupId)
|
||||
if (group && group.workerList && Array.isArray(group.workerList)) {
|
||||
const hasPerson = group.workerList.some((w) => w.id === personId)
|
||||
if (hasPerson) {
|
||||
groupsToRemove.push(groupId)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (groupsToRemove.length > 0) {
|
||||
// 如果人员来自分组,移除对应的分组
|
||||
selectedGroupIds.value = selectedGroupIds.value.filter((id) => !groupsToRemove.includes(id))
|
||||
updateSelectedPersons()
|
||||
nextTick(() => {
|
||||
syncGroupTableSelection()
|
||||
})
|
||||
} else {
|
||||
// 如果人员是直接选择的,从直接选择列表中移除
|
||||
directSelectedPersonIds.value = directSelectedPersonIds.value.filter(
|
||||
(id) => id !== personId,
|
||||
)
|
||||
updateSelectedPersons()
|
||||
}
|
||||
|
||||
// 同步人员表格
|
||||
nextTick(() => {
|
||||
syncPersonTableSelection()
|
||||
})
|
||||
}
|
||||
|
||||
// 清空所有选择
|
||||
const handleClearAll = () => {
|
||||
isSyncing.value = true
|
||||
directSelectedPersonIds.value = []
|
||||
selectedGroupIds.value = []
|
||||
emit('update:modelValue', [])
|
||||
if (personTableRef.value) {
|
||||
personTableRef.value.clearSelection()
|
||||
}
|
||||
if (groupTableRef.value) {
|
||||
groupTableRef.value.clearSelection()
|
||||
}
|
||||
nextTick(() => {
|
||||
isSyncing.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 获取人员列表
|
||||
const getPersonList = async () => {
|
||||
try {
|
||||
const result = await getAllPersonAPI()
|
||||
if (result.code === 200 && result.rows) {
|
||||
personList.value = result.rows || []
|
||||
nextTick(() => {
|
||||
syncPersonTableSelection()
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取人员列表失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取分组列表
|
||||
const getGroupList = async () => {
|
||||
try {
|
||||
const result = await getAllGroupAPI()
|
||||
if (result.code === 200 && result.rows) {
|
||||
groupList.value = result.rows || []
|
||||
nextTick(() => {
|
||||
syncGroupTableSelection()
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取分组列表失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 监听 modelValue 变化,同步表格选中状态和区分直接选择的人员
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (!newVal || newVal.length === 0) {
|
||||
directSelectedPersonIds.value = []
|
||||
return
|
||||
}
|
||||
|
||||
// 区分哪些是直接选择的人员,哪些是来自分组的
|
||||
const directSelectedIds = []
|
||||
const groupPersonIds = new Set()
|
||||
|
||||
// 收集所有分组中的人员ID
|
||||
selectedGroupIds.value.forEach((groupId) => {
|
||||
const group = groupList.value.find((g) => g.id === groupId)
|
||||
if (group && group.workerList && Array.isArray(group.workerList)) {
|
||||
group.workerList.forEach((worker) => {
|
||||
if (worker && worker.id) {
|
||||
groupPersonIds.add(worker.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 如果人员不在分组中,则认为是直接选择的
|
||||
newVal.forEach((person) => {
|
||||
if (person && person.id && !groupPersonIds.has(person.id)) {
|
||||
directSelectedIds.push(person.id)
|
||||
}
|
||||
})
|
||||
|
||||
directSelectedPersonIds.value = directSelectedIds
|
||||
|
||||
if (activeTab.value === 'person') {
|
||||
syncPersonTableSelection()
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
getPersonList()
|
||||
getGroupList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.person-picker {
|
||||
.search-bar {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.selected-persons {
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.selected-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.selected-list {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
overflow-y: auto;
|
||||
max-height: 400px;
|
||||
|
||||
.selected-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 2px 10px;
|
||||
margin-bottom: 5px;
|
||||
background: #f0f9ff;
|
||||
border: 1px solid #b3d8ff;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
background: #e1f3ff;
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
.remove-icon {
|
||||
color: #909399;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
|
||||
&:hover {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-tabs) {
|
||||
margin-bottom: 10px;
|
||||
|
||||
.el-tabs__header {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tab-badge {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -117,6 +117,22 @@ export const constantRoutes = [
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/sms/loopSendEdit',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: () => import('@/views/sMsSendManage/loopSend/edit.vue'),
|
||||
name: 'LoopSendEdit',
|
||||
meta: {
|
||||
title: '循环发送短信',
|
||||
activeMenu: '/sms/loopSend', // 保持左侧高亮在列表菜单
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
// 动态路由,基于用户权限动态去加载
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
import { reactive } from 'vue'
|
||||
|
||||
// 搜索表单配置
|
||||
export const buildFormColumns = () => [
|
||||
{
|
||||
type: 'input',
|
||||
prop: 'taskName',
|
||||
placeholder: '请输入任务名称',
|
||||
},
|
||||
{
|
||||
type: 'date',
|
||||
prop: 'dateRange',
|
||||
dateType: 'daterange',
|
||||
placeholder: '请选择日期范围',
|
||||
paramsList: ['startTime', 'endTime'], // 将日期范围拆分为开始时间和结束时间
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
prop: 'smsType',
|
||||
placeholder: '请选择短信类型',
|
||||
options: [
|
||||
{ label: '通知', value: '1' },
|
||||
{ label: '计划', value: '2' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
// 表格列配置
|
||||
export const tableColumns = [
|
||||
{
|
||||
prop: 'taskName',
|
||||
label: '任务名称',
|
||||
},
|
||||
{
|
||||
prop: 'createTime',
|
||||
label: '创建时间',
|
||||
},
|
||||
{
|
||||
prop: 'msgType',
|
||||
label: '短信类型',
|
||||
formatter: (row) => {
|
||||
const typeMap = {
|
||||
1: '通知',
|
||||
2: '计划',
|
||||
}
|
||||
return typeMap[row.msgType] || row.smsType
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: 'sendContent',
|
||||
label: '短信内容',
|
||||
showOverflowTooltip: true,
|
||||
},
|
||||
{
|
||||
prop: 'workerCount',
|
||||
label: '短信接收人数',
|
||||
},
|
||||
{
|
||||
prop: 'taskStatus',
|
||||
label: '任务状态',
|
||||
slot: 'taskStatus',
|
||||
},
|
||||
]
|
||||
|
||||
export default {
|
||||
tableColumns,
|
||||
buildFormColumns,
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,596 @@
|
|||
<template>
|
||||
<div class="app-container loop-send-edit">
|
||||
<!-- 顶部返回 + 标题 -->
|
||||
<el-page-header @back="onBack" :content="pageTitle" class="page-header" />
|
||||
|
||||
<!-- 表单内容 -->
|
||||
<el-card shadow="never" class="card-section">
|
||||
<template #header>
|
||||
<div class="card-header">任务信息</div>
|
||||
</template>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="!isDetail ? rules : {}"
|
||||
size="large"
|
||||
label-width="120px"
|
||||
:disabled="isDetail"
|
||||
>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="任务名称" prop="taskName">
|
||||
<el-input
|
||||
v-model.trim="formData.taskName"
|
||||
placeholder="请输入任务名称"
|
||||
maxlength="50"
|
||||
show-word-limit
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item label="短信类型" prop="msgType">
|
||||
<el-select
|
||||
v-model="formData.msgType"
|
||||
placeholder="请选择短信类型"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="通知" value="1" />
|
||||
<el-option label="计划" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="cron表达式" prop="cronExpression">
|
||||
<el-input
|
||||
v-model="formData.cronExpression"
|
||||
placeholder="请输入cron执行表达式"
|
||||
>
|
||||
<template #append>
|
||||
<el-button
|
||||
type="primary"
|
||||
v-if="!isDetail"
|
||||
@click="handleShowCron"
|
||||
>
|
||||
生成表达式
|
||||
<el-icon class="el-icon--right"><Clock /></el-icon>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="执行策略" prop="misfirePolicy">
|
||||
<el-radio-group v-model="formData.misfirePolicy">
|
||||
<el-radio-button value="1">立即执行</el-radio-button>
|
||||
<el-radio-button value="2">执行一次</el-radio-button>
|
||||
<el-radio-button value="3">放弃执行</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否并发" prop="concurrent">
|
||||
<el-radio-group v-model="formData.concurrent">
|
||||
<el-radio-button value="0">允许</el-radio-button>
|
||||
<el-radio-button value="1">禁止</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="发送内容" prop="sendContent">
|
||||
<el-input
|
||||
v-model.trim="formData.sendContent"
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
placeholder="请输入发送内容"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
v-model.trim="formData.remark"
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
placeholder="请输入备注"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="接收人员" prop="recipientList">
|
||||
<div class="recipient-select">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
icon="Plus"
|
||||
@click="onOpenPersonPicker"
|
||||
:disabled="isDetail"
|
||||
v-if="!isDetail"
|
||||
>
|
||||
选择
|
||||
</el-button>
|
||||
<span class="selected-names">{{ selectedRecipientNames }}</span>
|
||||
</div>
|
||||
<el-table
|
||||
v-if="formData.recipientList && formData.recipientList.length > 0"
|
||||
:data="formData.recipientList"
|
||||
border
|
||||
style="margin-top: 10px"
|
||||
max-height="200"
|
||||
>
|
||||
<el-table-column align="center" prop="workerName" label="姓名" />
|
||||
<el-table-column align="center" prop="phone" label="电话" />
|
||||
<el-table-column v-if="!isDetail" label="操作" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
icon="Delete"
|
||||
@click="handleRemoveRecipient($index)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<div class="page-footer">
|
||||
<ComButton plain type="info" @click="onBack">
|
||||
{{ isDetail ? '返回' : '取消' }}
|
||||
</ComButton>
|
||||
<ComButton v-if="!isDetail" type="primary" @click="onSubmit">确认执行</ComButton>
|
||||
</div>
|
||||
|
||||
<!-- Cron表达式生成器弹窗 -->
|
||||
<ComDialog :dialog-config="cronDialogConfig" @closeDialogOuter="onCloseCronDialog">
|
||||
<template #outerContent>
|
||||
<crontab
|
||||
ref="crontabRef"
|
||||
@hide="handleCronHide"
|
||||
@fill="crontabFill"
|
||||
:expression="cronExpression"
|
||||
/>
|
||||
</template>
|
||||
</ComDialog>
|
||||
|
||||
<!-- 人员选择弹窗 -->
|
||||
<ComDialog :dialog-config="managerDialogConfig" @closeDialogOuter="onCloseDialogOuter">
|
||||
<template #outerContent>
|
||||
<PersonPicker v-model="formData.recipientList" />
|
||||
<el-row class="common-btn-row fixed-bottom">
|
||||
<ComButton plain type="info" @click="managerDialogConfig.outerVisible = false">
|
||||
取消
|
||||
</ComButton>
|
||||
<ComButton type="primary" @click="onConfirmRecipients">确定</ComButton>
|
||||
</el-row>
|
||||
</template>
|
||||
</ComDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="LoopSendEdit">
|
||||
import { ref, reactive, computed, getCurrentInstance, nextTick, onMounted, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { Clock } from '@element-plus/icons-vue'
|
||||
import Crontab from '@/components/Crontab'
|
||||
import {
|
||||
addLoopSendAPI,
|
||||
updateLoopSendAPI,
|
||||
getLoopSendDetailAPI,
|
||||
} from '@/api/sMsSendManage/loopSend.js'
|
||||
import ComButton from '@/components/ComButton/index.vue'
|
||||
import ComDialog from '@/components/ComDialog/index.vue'
|
||||
import PersonPicker from '@/components/PersonPicker/index.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const mode = computed(() => route.query.mode || 'add')
|
||||
const isDetail = computed(() => mode.value === 'detail')
|
||||
|
||||
const pageTitle = computed(() => {
|
||||
if (mode.value === 'edit') return '编辑循环发送任务'
|
||||
if (mode.value === 'detail') return '循环发送任务详情'
|
||||
return '新增循环发送任务'
|
||||
})
|
||||
|
||||
const formRef = ref(null)
|
||||
const crontabRef = ref(null)
|
||||
const personTableRef = ref(null)
|
||||
const cronExpression = ref('')
|
||||
|
||||
// Cron表达式生成器弹窗配置
|
||||
const cronDialogConfig = reactive({
|
||||
outerVisible: false,
|
||||
outerTitle: 'Cron表达式生成器',
|
||||
outerWidth: '800px',
|
||||
minHeight: '500px',
|
||||
maxHeight: '80vh',
|
||||
})
|
||||
|
||||
// 定义初始表单数据
|
||||
const getInitFormData = () => ({
|
||||
id: null,
|
||||
taskName: '', // 任务名称
|
||||
msgType: '', // 短信类型:1-通知,2-计划
|
||||
cronExpression: '', // cron表达式
|
||||
misfirePolicy: '1', // 执行策略:1-立即执行,2-执行一次,3-放弃执行
|
||||
concurrent: '1', // 是否并发:0-允许,1-禁止
|
||||
sendContent: '', // 发送内容
|
||||
recipientList: [], // 接收人员列表
|
||||
})
|
||||
|
||||
const formData = ref(getInitFormData())
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
taskName: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
|
||||
msgType: [{ required: true, message: '请选择短信类型', trigger: 'change' }],
|
||||
cronExpression: [{ required: true, message: '请输入cron表达式', trigger: 'change' }],
|
||||
sendContent: [{ required: true, message: '请输入发送内容', trigger: 'blur' }],
|
||||
recipientList: [
|
||||
{
|
||||
required: true,
|
||||
message: '请至少选择一个接收人员',
|
||||
trigger: 'change',
|
||||
validator: (rule, value, callback) => {
|
||||
if (!value || value.length === 0) {
|
||||
callback(new Error('请至少选择一个接收人员'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
// 人员选择弹窗相关
|
||||
const managerDialogConfig = reactive({
|
||||
outerVisible: false,
|
||||
outerTitle: '选择接收人员',
|
||||
outerWidth: '80%',
|
||||
minHeight: '60vh',
|
||||
maxHeight: '80vh',
|
||||
})
|
||||
|
||||
// 已选接收人员名称显示
|
||||
const selectedRecipientNames = computed(() => {
|
||||
if (!formData.value.recipientList || formData.value.recipientList.length === 0) {
|
||||
return '暂未选择人员'
|
||||
}
|
||||
return formData.value.recipientList.map((item) => item.workerName).join('、')
|
||||
})
|
||||
|
||||
// 打开人员选择弹窗
|
||||
const onOpenPersonPicker = () => {
|
||||
managerDialogConfig.outerVisible = true
|
||||
}
|
||||
|
||||
// 确认选择接收人员
|
||||
const onConfirmRecipients = () => {
|
||||
managerDialogConfig.outerVisible = false
|
||||
}
|
||||
|
||||
// 移除接收人员
|
||||
const handleRemoveRecipient = (index) => {
|
||||
formData.value.recipientList.splice(index, 1)
|
||||
}
|
||||
|
||||
// 关闭人员选择弹窗
|
||||
const onCloseDialogOuter = (visible) => {
|
||||
managerDialogConfig.outerVisible = visible
|
||||
}
|
||||
|
||||
// Cron表达式生成器
|
||||
const handleShowCron = () => {
|
||||
cronExpression.value = formData.value.cronExpression || ''
|
||||
cronDialogConfig.outerVisible = true
|
||||
}
|
||||
|
||||
// Cron表达式回填
|
||||
const crontabFill = (value) => {
|
||||
formData.value.cronExpression = value
|
||||
cronDialogConfig.outerVisible = false
|
||||
}
|
||||
|
||||
// Cron组件hide事件处理
|
||||
const handleCronHide = () => {
|
||||
cronDialogConfig.outerVisible = false
|
||||
}
|
||||
|
||||
// 关闭Cron表达式弹窗
|
||||
const onCloseCronDialog = (visible) => {
|
||||
cronDialogConfig.outerVisible = visible
|
||||
}
|
||||
|
||||
// 返回
|
||||
const onBack = () => {
|
||||
router.back()
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const onSubmit = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
const API = mode.value === 'edit' ? updateLoopSendAPI : addLoopSendAPI
|
||||
|
||||
// 组装提交参数
|
||||
const params = {
|
||||
remark: formData.value.remark,
|
||||
taskName: formData.value.taskName,
|
||||
msgType: formData.value.msgType,
|
||||
cronExpression: formData.value.cronExpression,
|
||||
misfirePolicy: formData.value.misfirePolicy,
|
||||
concurrent: formData.value.concurrent,
|
||||
sendContent: formData.value.sendContent,
|
||||
workerList: formData.value.recipientList.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
// 编辑时需要传递id
|
||||
if (mode.value === 'edit' && route.query.id) {
|
||||
params.id = Number(route.query.id)
|
||||
}
|
||||
|
||||
const result = await API(params)
|
||||
if (result.code === 200) {
|
||||
proxy.$modal.msgSuccess(mode.value === 'edit' ? '编辑成功' : '新增成功')
|
||||
onBack()
|
||||
}
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取详情数据
|
||||
const getDetail = async () => {
|
||||
if (!route.query.id) return
|
||||
try {
|
||||
const result = await getLoopSendDetailAPI(route.query.id)
|
||||
if (result.code === 200 && result.data) {
|
||||
const data = result.data
|
||||
formData.value = {
|
||||
id: data.id,
|
||||
taskName: data.taskName || '',
|
||||
msgType: String(data.msgType || ''),
|
||||
cronExpression: data.cronExpression || '',
|
||||
misfirePolicy: String(data.misfirePolicy || '1'),
|
||||
concurrent: String(data.concurrent || '1'),
|
||||
sendContent: data.sendContent || '',
|
||||
recipientList: data.workerList || [],
|
||||
remark: data.remark || '',
|
||||
}
|
||||
cronExpression.value = data.cronExpression || ''
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取详情失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (mode.value === 'edit' || mode.value === 'detail') {
|
||||
getDetail()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.loop-send-edit {
|
||||
padding: 20px;
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 20px;
|
||||
padding: 16px 20px;
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #ffffff 100%);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
||||
|
||||
:deep(.el-page-header__left) {
|
||||
.el-page-header__back {
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s ease;
|
||||
background-color: transparent;
|
||||
|
||||
&:hover {
|
||||
background-color: #f0f2f5;
|
||||
color: #1677ff;
|
||||
transform: translateX(-2px);
|
||||
}
|
||||
|
||||
&::before {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-page-header__content) {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-section {
|
||||
margin-bottom: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
overflow: hidden;
|
||||
transition: box-shadow 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
:deep(.el-card__header) {
|
||||
padding: 16px 20px;
|
||||
background-color: #fafbfc;
|
||||
border-bottom: 1px solid #f0f2f5;
|
||||
}
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 24px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.recipient-select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
.selected-names {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.person-search-bar {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.selected-persons {
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
|
||||
.selected-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.selected-list {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
overflow-y: auto;
|
||||
max-height: 400px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.selected-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
background: #f0f9ff;
|
||||
border: 1px solid #b3d8ff;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
background: #e1f3ff;
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
.remove-icon {
|
||||
color: #909399;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: color 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
color: #9ca3af;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page-footer {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.common-btn-row {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
padding: 0 20px 20px;
|
||||
|
||||
&.fixed-bottom {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
background: #fff;
|
||||
z-index: 10;
|
||||
border-top: 1px solid #e4e7ed;
|
||||
padding-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-header {
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
color: #1f2937;
|
||||
position: relative;
|
||||
padding-left: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 4px;
|
||||
height: 16px;
|
||||
background: linear-gradient(180deg, #1677ff 0%, #4096ff 100%);
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 短信余额提示 -->
|
||||
<el-alert
|
||||
v-if="smsBalance !== null"
|
||||
:title="`短信余额剩余${smsBalance}条,请及时充值!`"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
|
||||
<!-- 循环发送短信列表 -->
|
||||
<ComTable
|
||||
ref="comTableRef"
|
||||
:form-columns="formColumns"
|
||||
:table-columns="tableColumns"
|
||||
:load-data="listLoopSendAPI"
|
||||
:show-toolbar="true"
|
||||
:show-action="true"
|
||||
:action-columns="actionColumns"
|
||||
>
|
||||
<template #toolbar>
|
||||
<ComButton
|
||||
type="primary"
|
||||
icon="Plus"
|
||||
@click="onHandleAdd"
|
||||
v-hasPermi="['sms:loopTask:add']"
|
||||
>
|
||||
新建
|
||||
</ComButton>
|
||||
</template>
|
||||
<template #taskStatus="{ row }">
|
||||
<!-- <el-switch
|
||||
v-model="row.taskStatus"
|
||||
active-value="1"
|
||||
inactive-value="0"
|
||||
@change="onHandleStatusChange($event, row)"
|
||||
/> -->
|
||||
</template>
|
||||
</ComTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="LoopSend">
|
||||
import { ref, computed, getCurrentInstance } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import {
|
||||
listLoopSendAPI,
|
||||
delLoopSendAPI,
|
||||
updateLoopSendStatusAPI,
|
||||
} from '@/api/sMsSendManage/loopSend.js'
|
||||
import config from './config'
|
||||
import ComTable from '@/components/ComTable/index.vue'
|
||||
import ComButton from '@/components/ComButton/index.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const { tableColumns, buildFormColumns } = config
|
||||
const comTableRef = ref(null)
|
||||
const smsBalance = ref(null) // 短信余额,可以从接口获取
|
||||
|
||||
// 根据配置构建搜索表单
|
||||
const formColumns = computed(() => buildFormColumns())
|
||||
|
||||
// 操作列配置
|
||||
const actionColumns = [
|
||||
{
|
||||
label: '详情',
|
||||
type: 'primary',
|
||||
link: true,
|
||||
permission: ['sms:loopTask:detail'],
|
||||
handler: (row) => {
|
||||
router.push({
|
||||
path: '/sms/loopSendEdit/index',
|
||||
query: {
|
||||
id: row.id,
|
||||
mode: 'detail',
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
// {
|
||||
// label: '编辑',
|
||||
// type: 'primary',
|
||||
// link: true,
|
||||
// permission: ['sms:loopTask:edit'],
|
||||
// handler: (row) => {
|
||||
// router.push({
|
||||
// path: '/sms/loopSendEdit/index',
|
||||
// query: {
|
||||
// id: row.id,
|
||||
// mode: 'edit',
|
||||
// },
|
||||
// })
|
||||
// },
|
||||
// },
|
||||
{
|
||||
label: '删除',
|
||||
type: 'danger',
|
||||
link: true,
|
||||
permission: ['sms:loopTask:remove'],
|
||||
handler: (row) => {
|
||||
proxy.$modal.confirm('是否确认删除该循环发送任务?').then(async () => {
|
||||
const result = await delLoopSendAPI({ id: row.id })
|
||||
if (result.code === 200) {
|
||||
proxy.$modal.msgSuccess('删除成功')
|
||||
comTableRef.value?.refresh()
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
// 新增
|
||||
const onHandleAdd = () => {
|
||||
router.push({
|
||||
path: '/sms/loopSendEdit/index',
|
||||
query: {
|
||||
mode: 'add',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 任务状态修改
|
||||
const onHandleStatusChange = async (value, row) => {
|
||||
try {
|
||||
const result = await updateLoopSendStatusAPI({
|
||||
id: row.id,
|
||||
taskStatus: value,
|
||||
})
|
||||
if (result.code === 200) {
|
||||
proxy.$modal.msgSuccess(value === '1' ? '启用成功' : '停用成功')
|
||||
comTableRef.value?.refresh()
|
||||
} else {
|
||||
// 如果失败,恢复原状态
|
||||
row.taskStatus = value === '1' ? '0' : '1'
|
||||
}
|
||||
} catch (error) {
|
||||
// 如果失败,恢复原状态
|
||||
row.taskStatus = value === '1' ? '0' : '1'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<template>
|
||||
<div>单次发送</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
Loading…
Reference in New Issue