定时任务
This commit is contained in:
parent
89e792dcca
commit
40e8bcb05d
|
|
@ -6,4 +6,4 @@ VITE_APP_ENV='development'
|
|||
|
||||
# 开发环境
|
||||
VITE_API_BASE='https://server.leapy.cn/admin/'
|
||||
VITE_WS_URL='wss://dev.api.leapy.cn/mms'
|
||||
VITE_WS_URL='wss://server.leapy.cn/ws'
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ VITE_APP_ENV='production'
|
|||
|
||||
# 生产环境
|
||||
VITE_API_BASE='https://server.leapy.cn/admin/'
|
||||
VITE_WS_URL='wss://dev.api.leapy.cn/mms'
|
||||
VITE_WS_URL='wss://server.leapy.cn/ws'
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@
|
|||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.2",
|
||||
"axios": "1.12.0",
|
||||
"cron-parser": "^4.9",
|
||||
"cropperjs": "^1.6.2",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.18",
|
||||
"echarts": "^6.0.0",
|
||||
"element-plus": "2.11.3",
|
||||
"image-conversion": "^2.1.1",
|
||||
|
|
|
|||
|
|
@ -101,5 +101,22 @@ export default {
|
|||
quit: async function (data, config = {}) {
|
||||
return await http.get("online/quit", data, config);
|
||||
}
|
||||
},
|
||||
crontab: {
|
||||
list: async function (data = {}) {
|
||||
return await http.get("crontab/list", data);
|
||||
},
|
||||
add: async function (data = {}) {
|
||||
return await http.post("crontab/add", data);
|
||||
},
|
||||
edit: async function (data = {}) {
|
||||
return await http.put("crontab/edit", data);
|
||||
},
|
||||
del: async function (data = {}) {
|
||||
return await http.delete("crontab/del", data);
|
||||
},
|
||||
option: async function (data = {}) {
|
||||
return await http.get("crontab/option", data);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<svg t="1761117902048" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10379" width="256" height="256"><path d="M512 952.32c-242.176 0-440.32-198.144-440.32-440.32s198.144-440.32 440.32-440.32 440.32 198.144 440.32 440.32-198.144 440.32-440.32 440.32z m0-792.576c-193.536 0-352.256 158.72-352.256 352.256s158.72 352.256 352.256 352.256 352.256-158.72 352.256-352.256-158.72-352.256-352.256-352.256zM335.872 512c0 97.28 78.848 176.128 176.128 176.128s176.128-78.848 176.128-176.128S609.28 335.872 512 335.872 335.872 414.72 335.872 512z" p-id="10380"></path></svg>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
<template>
|
||||
<div style="width: 100%;">
|
||||
<el-input v-model="value">
|
||||
<template #suffix>
|
||||
<el-icon size="15" @click="showPick">
|
||||
<component :is="'pi-icon-choice'"/>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<pi-dialog title="Cron 表达式生成器" v-model="visible" :width="800" destroy-on-close @closed="visible=false">
|
||||
<el-form :model="form" label-width="120px">
|
||||
<el-form-item label="类型" prop="type">
|
||||
<el-select v-model="form.type" placeholder="请选择" @change="generateCron">
|
||||
<el-option label="每 N 秒" value="every_n_seconds"/>
|
||||
<el-option label="每 N 分钟" value="every_n_minutes"/>
|
||||
<el-option label="每天在指定时间" value="daily_at"/>
|
||||
<el-option label="每周在指定星期和时间" value="weekly_at"/>
|
||||
<el-option label="每月在指定日和时间" value="monthly_at"/>
|
||||
<el-option label="自定义表达式" value="custom"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="间隔 (秒)" v-if="form.type === 'every_n_seconds'" prop="everySeconds">
|
||||
<el-input-number v-model="form.everySeconds" :min="0" :max="59" @change="generateCron"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="间隔 (分钟)" v-if="form.type === 'every_n_minutes'" prop="everyMinutes">
|
||||
<el-input-number v-model="form.everyMinutes" :min="1" @change="generateCron"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="起始秒" v-if="form.type === 'every_n_minutes'" prop="startSecond">
|
||||
<el-input-number v-model="form.startSecond" :min="0" :max="59" @change="generateCron"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="星期" v-if="form.type === 'weekly_at'" prop="weekday">
|
||||
<el-select v-model="form.weekday" placeholder="选择星期" @change="generateCron">
|
||||
<el-option v-for="(label, idx) in weekOptions" :key="idx" :label="label" :value="idx"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="日期 (1-31)" v-if="form.type === 'monthly_at'" prop="dayOfMonth">
|
||||
<el-input-number v-model="form.dayOfMonth" :min="1" :max="31" @change="generateCron"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择时间"
|
||||
v-if="form.type === 'daily_at' || form.type === 'weekly_at' || form.type === 'monthly_at'"
|
||||
prop="dailyTime">
|
||||
<el-time-picker v-model="form.dailyTime" placeholder="选择时间" @change="generateCron"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="Cron 表达式" v-if="form.type === 'custom'" prop="customCron">
|
||||
<el-input v-model="form.customCron" placeholder="例: 0 0/30 9-17 * * 1-5" @change="generateCron"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-divider/>
|
||||
<div>
|
||||
<div class="mb-2 font-medium">生成的 Cron 表达式:</div>
|
||||
<el-input type="textarea" v-model="value" readonly :rows="2"/>
|
||||
</div>
|
||||
<el-alert v-if="error" type="error" class="mt-2" :closable="false" :title="error"/>
|
||||
<el-divider/>
|
||||
<div>
|
||||
<div class="mb-2 font-medium">下 5 次执行时间</div>
|
||||
<el-table :data="nextRuns.map((t, i) => ({ index: i + 1, time: t }))" stripe>
|
||||
<el-table-column prop="index" label="#" width="50"/>
|
||||
<el-table-column prop="time" label="执行时间"/>
|
||||
</el-table>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="visible=false">取 消</el-button>
|
||||
<el-button type="primary" @click="confirm()">确 定</el-button>
|
||||
</template>
|
||||
</pi-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {getCurrentInstance, ref, watch} from "vue";
|
||||
import parser from 'cron-parser'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const {proxy} = getCurrentInstance()
|
||||
|
||||
let value = ref("")
|
||||
let visible = ref(false)
|
||||
|
||||
const prop = defineProps({
|
||||
modelValue: {type: String, default: ""}
|
||||
})
|
||||
|
||||
let form = ref({
|
||||
type: 'every_n_seconds',
|
||||
everySeconds: 0,
|
||||
everyMinutes: 1,
|
||||
startSecond: 0,
|
||||
dailyTime: null,
|
||||
dayOfMonth: 1,
|
||||
customCron: null
|
||||
})
|
||||
|
||||
const error = ref('')
|
||||
const nextRuns = ref([])
|
||||
const tzName = Intl.DateTimeFormat().resolvedOptions().timeZone || 'local'
|
||||
const weekOptions = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
|
||||
|
||||
watch(() => prop.modelValue, (val) => {
|
||||
value.value = val
|
||||
if (val) {
|
||||
computeNextRuns()
|
||||
}
|
||||
}, {immediate: true})
|
||||
|
||||
function confirm() {
|
||||
proxy.$emit('update:modelValue', value.value)
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
function showPick() {
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
function generateCron() {
|
||||
try {
|
||||
error.value = ''
|
||||
let expression = ''
|
||||
if (form.value.type === 'every_n_seconds') {
|
||||
if (form.value.everySeconds === null) return;
|
||||
expression = `*/${form.value.everySeconds} * * * * *`
|
||||
} else if (form.value.type === 'every_n_minutes') {
|
||||
if (form.value.startSecond === null) return;
|
||||
if (form.value.everyMinutes === null) return;
|
||||
expression = `${form.value.startSecond} */${form.value.everyMinutes} * * * *`
|
||||
} else if (form.value.type === 'daily_at' && form.value.dailyTime) {
|
||||
const h = dayjs(form.value.dailyTime).hour()
|
||||
const m = dayjs(form.value.dailyTime).minute()
|
||||
expression = `0 ${m} ${h} * * *`
|
||||
} else if (form.value.type === 'weekly_at' && form.value.dailyTime) {
|
||||
if (form.value.weekday == null) return;
|
||||
const h = dayjs(form.value.dailyTime).hour()
|
||||
const m = dayjs(form.value.dailyTime).minute()
|
||||
expression = `0 ${m} ${h} * * ${form.value.weekday}`
|
||||
} else if (form.value.type === 'monthly_at' && form.value.dailyTime) {
|
||||
if (form.value.dayOfMonth == null) return;
|
||||
const h = dayjs(form.value.dailyTime).hour()
|
||||
const m = dayjs(form.value.dailyTime).minute()
|
||||
expression = `0 ${m} ${h} ${form.value.dayOfMonth} * *`
|
||||
} else if (form.value.type === 'custom') {
|
||||
if (form.value.customCron == null) return;
|
||||
expression = form.value.customCron.trim()
|
||||
}
|
||||
value.value = expression
|
||||
computeNextRuns()
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
}
|
||||
}
|
||||
|
||||
function computeNextRuns() {
|
||||
nextRuns.value = []
|
||||
try {
|
||||
let expr = value.value.trim()
|
||||
if (!expr) return
|
||||
const parts = expr.split(/\s+/)
|
||||
if (parts.length === 5) expr = '0 ' + expr
|
||||
const interval = parser.parseExpression(expr, {iterator: true, tz: tzName})
|
||||
const arr = []
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const obj = interval.next()
|
||||
arr.push(dayjs(obj.value.toDate()).format('YYYY-MM-DD HH:mm:ss'))
|
||||
}
|
||||
nextRuns.value = arr
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="pi-dialog" ref="piDialog">
|
||||
<el-dialog ref="dialog" v-model="dialogVisible" :fullscreen="isFullscreen" v-bind="$attrs" :show-close="false">
|
||||
<el-dialog ref="dialog" v-model="dialogVisible" :fullscreen="isFullscreen" v-bind="attrs" :show-close="false">
|
||||
<template #header>
|
||||
<slot name="header">
|
||||
<span class="el-dialog__title">{{ title }}</span>
|
||||
|
|
@ -26,13 +26,14 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, onMounted, watch} from "vue";
|
||||
import {ref, onMounted, watch, useAttrs} from "vue";
|
||||
const attrs = useAttrs()
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Boolean, default: false },
|
||||
title: { type: String, default: "" },
|
||||
showClose: { type: Boolean, default: true },
|
||||
showFullscreen: { type: Boolean, default: true },
|
||||
showFullscreen: { type: Boolean, default: false },
|
||||
loading: { type: Boolean, default: false }
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
<template>
|
||||
<pi-table ref="tableRef" :apiObj="api.system.crontab.list" @selection-change="selectionChange">
|
||||
<template #do>
|
||||
<el-button v-auth="'crontab:add'" type="primary" icon="el-icon-plus" @click="add"></el-button>
|
||||
<el-button v-auth="'crontab:edit'" type="primary" icon="el-icon-edit" @click="edit()"
|
||||
:disabled="selection.length!==1"></el-button>
|
||||
<el-button v-auth="'crontab:del'" type="danger" plain icon="el-icon-delete" :disabled="selection.length===0"
|
||||
@click="batch_del"></el-button>
|
||||
</template>
|
||||
<template #search>
|
||||
<el-input v-model="search.crontab_name" placeholder="任务名称" clearable style="width: 200px;"
|
||||
@keydown.enter="upsearch"></el-input>
|
||||
<el-select v-model="search.enable" placeholder="任务状态" clearable style="width: 200px;">
|
||||
<el-option label="启用" :value="1"></el-option>
|
||||
<el-option label="停用" :value="0"></el-option>
|
||||
</el-select>
|
||||
<el-button type="primary" icon="el-icon-search" @click="upsearch"></el-button>
|
||||
</template>
|
||||
<el-table-column type="selection" width="50"></el-table-column>
|
||||
<el-table-column label="任务编号" prop="crontab_id" width="80"></el-table-column>
|
||||
<el-table-column label="任务名称" prop="crontab_name"></el-table-column>
|
||||
<el-table-column label="调用目标" prop="crontab_name"></el-table-column>
|
||||
<el-table-column label="cron执行表达式" prop="rule"></el-table-column>
|
||||
<el-table-column label="状态" prop="enable">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.enable===1" type="success">启用</el-tag>
|
||||
<el-tag v-if="scope.row.enable===0" type="danger">停用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" prop="memo"></el-table-column>
|
||||
<el-table-column label="操作" fixed="right" align="right" width="170">
|
||||
<template #default="scope">
|
||||
<el-button-group>
|
||||
<el-button text type="primary" size="small" @click="show(scope.row, scope.$index)">查看
|
||||
</el-button>
|
||||
<el-button v-auth="'post:edit'" text type="primary" size="small"
|
||||
@click="edit(scope.row, scope.$index)">编辑
|
||||
</el-button>
|
||||
<el-popconfirm title="确定删除吗?" @confirm="del(scope.row, scope.$index)">
|
||||
<template #reference>
|
||||
<el-button v-auth="'post:del'" text type="primary" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</el-button-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</pi-table>
|
||||
<save-dialog v-if="dialogShow" ref="dialogRef" @success="tableRef.refresh()"
|
||||
@closed="dialogShow=false"></save-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import saveDialog from './save'
|
||||
import api from "@/api/index";
|
||||
import {getCurrentInstance, nextTick, ref} from "vue";
|
||||
|
||||
defineOptions({
|
||||
name: "monitorCrontab"
|
||||
})
|
||||
|
||||
const {proxy} = getCurrentInstance()
|
||||
const tableRef = ref(null)
|
||||
const dialogRef = ref(null)
|
||||
|
||||
let dialogShow = ref(false)
|
||||
let selection = ref([])
|
||||
let search = ref({
|
||||
crontab_name: null,
|
||||
enable: null
|
||||
})
|
||||
|
||||
//添加
|
||||
function add() {
|
||||
dialogShow.value = true
|
||||
nextTick(() => {
|
||||
dialogRef.value.open()
|
||||
})
|
||||
}
|
||||
|
||||
//编辑
|
||||
async function edit(row) {
|
||||
dialogShow.value = true
|
||||
nextTick(() => {
|
||||
dialogRef.value.open('edit', row ?? selection.value[0])
|
||||
})
|
||||
}
|
||||
|
||||
//查看
|
||||
async function show(row) {
|
||||
dialogShow.value = true
|
||||
nextTick(() => {
|
||||
dialogRef.value.open('show', row)
|
||||
})
|
||||
}
|
||||
|
||||
//删除
|
||||
async function del(row) {
|
||||
const loading = proxy.$loading();
|
||||
const res = await api.system.crontab.del({ids: row.crontab_id});
|
||||
tableRef.value.refresh()
|
||||
loading.close();
|
||||
proxy.$message.success(res.msg)
|
||||
}
|
||||
|
||||
//批量删除
|
||||
async function batch_del() {
|
||||
proxy.$confirm(`确定删除选中的 ${selection.value.length} 项吗?如果删除项中含有子集将会被一并删除`, '提示', {
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
const loading = proxy.$loading();
|
||||
const res = await api.system.crontab.del({ids: selection.value.map(item => item.crontab_id).toString()});
|
||||
tableRef.value.refresh()
|
||||
loading.close();
|
||||
proxy.$message.success(res.msg)
|
||||
})
|
||||
}
|
||||
|
||||
//表格选择后回调事件
|
||||
function selectionChange(e) {
|
||||
selection.value = e;
|
||||
}
|
||||
|
||||
//搜索
|
||||
function upsearch() {
|
||||
tableRef.value.upData(search.value)
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
<template>
|
||||
<el-dialog :title="titleMap[mode]" v-model="visible" :width="500" destroy-on-close @closed="$emit('closed')">
|
||||
<el-form :model="form" :rules="rules" :disabled="mode==='show'" ref="formRef" label-width="100px">
|
||||
<el-form-item label="任务名称" prop="crontab_name">
|
||||
<el-input type="text" v-model="form.crontab_name" placeholder="请输入任务名称" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="调用方法" prop="callback">
|
||||
<el-input type="text" v-model="form.callback" placeholder="请输入调用方法" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="调用参数" prop="params">
|
||||
<el-input type="textarea" v-model="form.params" placeholder="请输入调用参数" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="cron表达式" prop="rule">
|
||||
<pi-cron v-model="form.rule"></pi-cron>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否并发" prop="singleton">
|
||||
<el-switch v-model="form.singleton" :active-value="1" :inactive-value="0"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否记录日志" prop="skip_log">
|
||||
<el-switch v-model="form.skip_log" :active-value="1" :inactive-value="0"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="memo">
|
||||
<el-input type="textarea" v-model="form.memo" placeholder="请输入备注" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="visible=false" >取 消</el-button>
|
||||
<el-button v-if="mode!=='show'" type="primary" :loading="isSaveing" @click="submit()">保 存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {getCurrentInstance, ref} from 'vue'
|
||||
import api from "@/api/index.js"
|
||||
import piCron from "@/components/piCron"
|
||||
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
})
|
||||
const emit = defineEmits(['success', 'closed'])
|
||||
const formRef = ref(null)
|
||||
const {proxy} = getCurrentInstance()
|
||||
|
||||
let mode = ref('add')
|
||||
let titleMap = ref({
|
||||
add: '新增',
|
||||
edit: '编辑',
|
||||
show: '查看'
|
||||
})
|
||||
let visible = ref(false)
|
||||
let isSaveing = ref(false)
|
||||
let form = ref({
|
||||
crontab_id: null,
|
||||
crontab_name: '',
|
||||
singleton: 1,
|
||||
enable: 0,
|
||||
skip_log: 1,
|
||||
rule: '',
|
||||
params: '',
|
||||
callback: ''
|
||||
})
|
||||
const rules = ref({
|
||||
crontab_name:[
|
||||
{required: true, message: '请填写任务名称'}
|
||||
],
|
||||
rule:[
|
||||
{required: true, message: '请填写cron表达式'}
|
||||
],
|
||||
params:[
|
||||
{required: true, message: '请填写调用参数'}
|
||||
],
|
||||
callback:[
|
||||
{required: true, message: '请填写调用方法'}
|
||||
]
|
||||
})
|
||||
|
||||
function open(m = 'add', data = null) {
|
||||
mode.value = m
|
||||
visible.value = true
|
||||
Object.assign(form.value, data)
|
||||
}
|
||||
|
||||
async function submit(){
|
||||
// 校验登录
|
||||
const validate = await formRef.value.validate().catch(() => {});
|
||||
if(!validate){ return false }
|
||||
isSaveing.value = true;
|
||||
const res = form.value.crontab_id? await api.system.crontab.edit(form.value) : await api.system.crontab.add(form.value);
|
||||
isSaveing.value = false;
|
||||
emit('success')
|
||||
visible.value = false;
|
||||
proxy.$message.success(res.msg)
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<pi-table ref="tableRef" :apiObj="api.system.online.list">
|
||||
<template #do>
|
||||
<el-input v-model="search.username" placeholder="用户名" clearable style="width: 200px;" @keyup.enter="upsearch"></el-input>
|
||||
<el-button type="primary" icon="el-icon-search" @click="upsearch"></el-button>
|
||||
</template>
|
||||
<el-table-column label="#" type="index" width="50"></el-table-column>
|
||||
<el-table-column label="会话编号" prop="session_id"></el-table-column>
|
||||
<el-table-column label="用户名" prop="username"></el-table-column>
|
||||
<el-table-column label="部门名称" prop="account.dept.dept_name"></el-table-column>
|
||||
<el-table-column label="主机" prop="ip"></el-table-column>
|
||||
<el-table-column label="登录地点" prop="location"></el-table-column>
|
||||
<el-table-column label="浏览器" prop="os.browser"></el-table-column>
|
||||
<el-table-column label="操作系统" prop="os.os"></el-table-column>
|
||||
<el-table-column label="登录时间" prop="online_time"></el-table-column>
|
||||
<el-table-column label="操作" fixed="right" align="right" width="100">
|
||||
<template #default="scope">
|
||||
<el-button-group>
|
||||
<el-popconfirm title="确定强制退出吗?" @confirm="quit(scope.row, scope.$index)">
|
||||
<template #reference>
|
||||
<el-button v-auth="'online:quit'" text type="primary" size="small">强退</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</el-button-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</pi-table>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import api from "@/api/index";
|
||||
import {getCurrentInstance, ref} from "vue";
|
||||
|
||||
defineOptions({
|
||||
name: "monitorOnline"
|
||||
})
|
||||
|
||||
const {proxy} = getCurrentInstance()
|
||||
const tableRef = ref(null)
|
||||
|
||||
let search = ref({
|
||||
username: null
|
||||
})
|
||||
|
||||
//删除
|
||||
async function quit(row) {
|
||||
const loading = proxy.$loading();
|
||||
const res = await api.system.online.quit({session_id: row.session_id});
|
||||
tableRef.value.refresh()
|
||||
loading.close();
|
||||
proxy.$message.success(res.msg)
|
||||
}
|
||||
|
||||
//搜索
|
||||
function upsearch() {
|
||||
tableRef.value.upData(search.value)
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
<template>
|
||||
<pi-table ref="tableRef" :apiObj="api.system.online.list" @selection-change="selectionChange">
|
||||
<pi-table ref="tableRef" :apiObj="api.system.online.list">
|
||||
<template #do>
|
||||
<el-input v-model="search.username" placeholder="用户名" clearable style="width: 200px;" @keyup.enter="upsearch"></el-input>
|
||||
<el-button type="primary" icon="el-icon-search" @click="upsearch"></el-button>
|
||||
</template>
|
||||
<el-table-column type="selection" width="50"></el-table-column>
|
||||
<el-table-column label="#" type="index" width="50"></el-table-column>
|
||||
<el-table-column label="会话编号" prop="session_id"></el-table-column>
|
||||
<el-table-column label="用户名" prop="username"></el-table-column>
|
||||
|
|
@ -17,7 +16,7 @@
|
|||
<el-table-column label="操作" fixed="right" align="right" width="100">
|
||||
<template #default="scope">
|
||||
<el-button-group>
|
||||
<el-popconfirm title="确定删除吗?" @confirm="table_del(scope.row, scope.$index)">
|
||||
<el-popconfirm title="确定强制退出吗?" @confirm="quit(scope.row, scope.$index)">
|
||||
<template #reference>
|
||||
<el-button v-auth="'online:quit'" text type="primary" size="small">强退</el-button>
|
||||
</template>
|
||||
|
|
@ -30,7 +29,7 @@
|
|||
|
||||
<script setup>
|
||||
import api from "@/api/index";
|
||||
import {getCurrentInstance, nextTick, ref} from "vue";
|
||||
import {getCurrentInstance, ref} from "vue";
|
||||
|
||||
defineOptions({
|
||||
name: "monitorOnline"
|
||||
|
|
@ -38,41 +37,20 @@ defineOptions({
|
|||
|
||||
const {proxy} = getCurrentInstance()
|
||||
const tableRef = ref(null)
|
||||
const dialogRef = ref(null)
|
||||
|
||||
let dialogShow = ref(false)
|
||||
let selection = ref([])
|
||||
let search = ref({
|
||||
username: null
|
||||
})
|
||||
|
||||
//删除
|
||||
async function table_del(row) {
|
||||
async function quit(row) {
|
||||
const loading = proxy.$loading();
|
||||
const res = await api.system.post.del({ids: row.post_id});
|
||||
const res = await api.system.online.quit({session_id: row.session_id});
|
||||
tableRef.value.refresh()
|
||||
loading.close();
|
||||
proxy.$message.success(res.msg)
|
||||
}
|
||||
|
||||
//批量删除
|
||||
async function batch_del() {
|
||||
proxy.$confirm(`确定删除选中的 ${selection.value.length} 项吗?如果删除项中含有子集将会被一并删除`, '提示', {
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
const loading = proxy.$loading();
|
||||
const res = await api.system.post.del({ids: selection.value.map(item => item.post_id).toString()});
|
||||
tableRef.value.refresh()
|
||||
loading.close();
|
||||
proxy.$message.success(res.msg)
|
||||
})
|
||||
}
|
||||
|
||||
//表格选择后回调事件
|
||||
function selectionChange(e) {
|
||||
selection.value = e;
|
||||
}
|
||||
|
||||
//搜索
|
||||
function upsearch() {
|
||||
tableRef.value.upData(search.value)
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@
|
|||
</el-card>
|
||||
</el-col>
|
||||
<el-col :lg="16" :xs="24" :sm="24" :md="24">
|
||||
<el-card shadow="never">
|
||||
<el-tabs class="pi-right">
|
||||
<el-card shadow="never" class="pi-right">
|
||||
<el-tabs>
|
||||
<el-tab-pane label="基本信息" name="0">
|
||||
<el-form ref="infoRef" :model="form" style="max-width: 500px;" label-width="120px">
|
||||
<el-form-item label="头像" prop="avatar">
|
||||
|
|
|
|||
Loading…
Reference in New Issue