mirror of
https://github.com/nsnail/NetAdmin.git
synced 2025-07-05 10:08:15 +08:00
@ -3,7 +3,8 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"build": "vite build --mode production",
|
||||
"build:test": "vite build --mode test",
|
||||
"preview": "vite preview",
|
||||
"prettier": "prettier --write ."
|
||||
},
|
||||
@ -12,34 +13,34 @@
|
||||
"@tinymce/tinymce-vue": "^5.1.1",
|
||||
"axios": "^1.6.7",
|
||||
"clipboard": "^2.0.11",
|
||||
"core-js": "^3.35.1",
|
||||
"core-js": "^3.36.0",
|
||||
"cropperjs": "^1.6.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"echarts": "^5.4.3",
|
||||
"element-plus": "^2.5.3",
|
||||
"element-plus": "^2.5.5",
|
||||
"json-bigint": "^1.0.0",
|
||||
"json5-to-table": "^0.1.8",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinyin-match": "^1.2.5",
|
||||
"qrcodejs2": "^0.0.2",
|
||||
"sortablejs": "^1.15.2",
|
||||
"tinymce": "^6.8.2",
|
||||
"vue": "^3.4.15",
|
||||
"vue-i18n": "^9.9.0",
|
||||
"tinymce": "^6.8.3",
|
||||
"vue": "^3.4.19",
|
||||
"vue-i18n": "^9.9.1",
|
||||
"vue-router": "^4.2.5",
|
||||
"vue3-json-viewer": "^2.2.2",
|
||||
"vuedraggable": "^4.0.3",
|
||||
"vuex": "^4.1.0",
|
||||
"xgplayer": "^3.0.11",
|
||||
"xgplayer-hls": "^3.0.11"
|
||||
"xgplayer": "^3.0.12",
|
||||
"xgplayer-hls": "^3.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.0.3",
|
||||
"prettier": "^3.2.4",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-organize-attributes": "^1.0.0",
|
||||
"sass": "^1.70.0",
|
||||
"terser": "^5.27.0",
|
||||
"vite": "^5.0.12"
|
||||
"sass": "^1.71.0",
|
||||
"terser": "^5.27.1",
|
||||
"vite": "^5.1.3"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
@ -82,6 +82,28 @@ export default {
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取单个作业记录
|
||||
*/
|
||||
recordGet: {
|
||||
url: `${config.API_URL}/api/sys/job/record.get`,
|
||||
name: `获取单个作业记录`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 分页查询作业记录
|
||||
*/
|
||||
recordPagedQuery: {
|
||||
url: `${config.API_URL}/api/sys/job/record.paged.query`,
|
||||
name: `分页查询作业记录`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 启用/禁用作业
|
||||
*/
|
||||
|
10
src/frontend/admin/src/assets/icons/Daily.vue
Normal file
10
src/frontend/admin/src/assets/icons/Daily.vue
Normal file
@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<svg class="icon" height="128" p-id="4304" t="1708217899083" version="1.1" viewBox="0 0 1024 1024" width="128" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M497.3 409.6c-1.6 3.7-4.8 7-9.6 10s-9.3 4.8-13.6 5.5c-4.2 0.7-9 1-14.3 0.8h-25.7v57.4h59.3V740h64.4V400.2L502 400l-4.7 9.6z"
|
||||
p-id="4305"></path>
|
||||
<path
|
||||
d="M880 160H738v-56c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v56H350v-56c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v56H144c-17.7 0-32 14.3-32 32v688c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V192c0-17.7-14.3-32-32-32z m-32 680c0 4.4-3.6 8-8 8H184c-4.4 0-8-3.6-8-8V232c0-4.4 3.6-8 8-8h102v48c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8v-48h324v48c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8v-48h110v616z"
|
||||
p-id="4306"></path>
|
||||
</svg>
|
||||
</template>
|
7
src/frontend/admin/src/assets/icons/Report.vue
Normal file
7
src/frontend/admin/src/assets/icons/Report.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg class="icon" height="128" p-id="5158" t="1708217788265" version="1.1" viewBox="0 0 1024 1024" width="128" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M228.254587 913.306922 66.10251 913.306922l0-240.783948 162.152077 0L228.254587 913.306922zM471.464795 913.306922 309.318858 913.306922 309.318858 351.483166l162.145937 0L471.464795 913.306922zM714.681142 913.306922 552.540322 913.306922 552.540322 592.267115 714.681142 592.267115 714.681142 913.306922zM957.896466 913.306922 795.744389 913.306922 795.744389 110.693078l162.152077 0L957.896466 913.306922z"
|
||||
p-id="5159"></path>
|
||||
</svg>
|
||||
</template>
|
@ -53,4 +53,6 @@ export { default as Unlink } from './Unlink.vue'
|
||||
export { default as Upload } from './Upload.vue'
|
||||
export { default as Vue } from './Vue.vue'
|
||||
export { default as Warning } from './Warning.vue'
|
||||
export { default as Wechat } from './Wechat.vue'
|
||||
export { default as Wechat } from './Wechat.vue'
|
||||
export { default as Report } from './Report.vue'
|
||||
export { default as Daily } from './Daily.vue'
|
@ -3,7 +3,7 @@
|
||||
<template #default="scope">
|
||||
<template v-for="(item, i) in Array.isArray(scope.row[prop]) ? scope.row[prop] : [scope.row[prop]]" :key="i">
|
||||
<el-tag v-if="item" @click="$emit('click', item)">
|
||||
{{ item ? item[field] : console.error(scope.row) }}
|
||||
{{ item ? item[field] : '' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</template>
|
||||
|
50
src/frontend/admin/src/components/naColUser/index.vue
Normal file
50
src/frontend/admin/src/components/naColUser/index.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<el-table-column v-bind="$attrs">
|
||||
<template #default="scope">
|
||||
<div @click="click(tool.getNestedProperty(scope.row, $attrs.prop))" class="avatar" style="cursor: pointer">
|
||||
<el-avatar :src="getAvatar(scope)" size="small"></el-avatar>
|
||||
<el-text tag="ins">{{ tool.getNestedProperty(scope.row, $attrs.nestProp) }}</el-text>
|
||||
</div>
|
||||
<save-dialog v-if="dialog.save" @closed="dialog.save = false" ref="saveDialog"></save-dialog>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
<style scoped>
|
||||
.avatar {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import saveDialog from '@/views/sys/user/save.vue'
|
||||
import tool from '@/utils/tool'
|
||||
|
||||
export default {
|
||||
emits: ['click'],
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
dialog: { save: false },
|
||||
}
|
||||
},
|
||||
mounted() {},
|
||||
created() {},
|
||||
components: { saveDialog },
|
||||
computed: {
|
||||
tool() {
|
||||
return tool
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async click(id) {
|
||||
this.dialog.save = true
|
||||
await this.$nextTick()
|
||||
await this.$refs.saveDialog.open('view', { id: id })
|
||||
},
|
||||
//获取头像
|
||||
getAvatar(scope) {
|
||||
return scope.row.avatar ? scope.row.avatar : this.$CONFIG.DEFAULT_AVATAR
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
File diff suppressed because one or more lines are too long
@ -75,7 +75,7 @@ Object.assign(DEFAULT_CONFIG, MY_CONFIG)
|
||||
|
||||
// 如果生产模式,就合并动态的APP_CONFIG
|
||||
// public/config.js
|
||||
if (import.meta.env.PROD) {
|
||||
if (import.meta.env.MODE === 'production' || import.meta.env.MODE === 'test') {
|
||||
Object.assign(DEFAULT_CONFIG, APP_CONFIG)
|
||||
}
|
||||
|
||||
|
@ -1,59 +1,60 @@
|
||||
<template>
|
||||
<el-container v-loading="loading">
|
||||
<el-main>
|
||||
<el-empty v-if="tasks.length === 0" :image-size="120">
|
||||
<el-empty v-if="jobs.length === 0" :image-size="120">
|
||||
<template #description>
|
||||
<h2>没有正在执行的任务</h2>
|
||||
</template>
|
||||
<p style="font-size: 14px; color: #999; line-height: 1.5; margin: 0 40px">
|
||||
<p style="color: #999; line-height: 1.5; margin: 0 3rem">
|
||||
在处理耗时过久的任务时为了不阻碍正在处理的工作,可在任务中心进行异步执行。
|
||||
</p>
|
||||
</el-empty>
|
||||
<el-card v-for="task in tasks" :key="task.id" class="user-bar-tasks-item" shadow="hover">
|
||||
<div class="user-bar-tasks-item-body">
|
||||
<div class="taskIcon">
|
||||
<el-icon v-if="task.type === 'export'" :size="20">
|
||||
<el-icon-paperclip />
|
||||
</el-icon>
|
||||
<el-icon v-if="task.type === 'report'" :size="20">
|
||||
<el-icon-dataAnalysis />
|
||||
</el-icon>
|
||||
<el-card v-for="job in jobs" :key="job.id" class="user-bar-jobs-item" shadow="hover">
|
||||
<div class="user-bar-jobs-item-body">
|
||||
<div class="jobIcon">
|
||||
{{ job.lastStatusCode }}
|
||||
</div>
|
||||
<div class="taskMain">
|
||||
<div class="jobMain">
|
||||
<div class="title">
|
||||
<h2>{{ task.taskName }}</h2>
|
||||
<p><span v-time.tip="task.createDate"></span> 创建</p>
|
||||
<h2>{{ job.jobName }}</h2>
|
||||
<p>上次执行:<span v-time.tip="job.lastExecTime"></span></p>
|
||||
<p>
|
||||
下次执行:<span>{{ job.nextExecTime }}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="state">
|
||||
<el-tag v-if="task.state === '0'" type="info">执行中</el-tag>
|
||||
<el-tag v-if="task.state === '1'">完成</el-tag>
|
||||
<div class="status">
|
||||
<el-tag v-if="job.status === 'running'" type="info">执行中</el-tag>
|
||||
<el-tag v-if="job.status === 'idle'">空闲</el-tag>
|
||||
</div>
|
||||
<div class="handler">
|
||||
<el-button
|
||||
v-if="task.state === '1'"
|
||||
@click="download(task)"
|
||||
circle
|
||||
icon="el-icon-download"
|
||||
type="primary"></el-button>
|
||||
<el-button v-if="job.status === 'idle'" @click="view(job)" circle icon="el-icon-view" type="primary"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-main>
|
||||
<el-footer style="padding: 10px; text-align: right">
|
||||
<el-footer style="text-align: right">
|
||||
<el-button @click="refresh" circle icon="el-icon-refresh"></el-button>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
<save-dialog v-if="dialog.save" @closed="dialog.save = false" ref="saveDialog"></save-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import saveDialog from '@/views/sys/job/save.vue'
|
||||
export default {
|
||||
components: {
|
||||
saveDialog,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialog: {
|
||||
save: false,
|
||||
},
|
||||
loading: false,
|
||||
tasks: [],
|
||||
jobs: [],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -62,69 +63,64 @@ export default {
|
||||
methods: {
|
||||
async getData() {
|
||||
this.loading = true
|
||||
var res = await this.$API.system.tasks.list.get()
|
||||
this.tasks = res.data
|
||||
const res = await this.$API.sys_job.query.post({ prop: 'lastExecTime', order: 'descending' })
|
||||
this.jobs = res.data
|
||||
this.loading = false
|
||||
},
|
||||
refresh() {
|
||||
this.getData()
|
||||
},
|
||||
download(row) {
|
||||
let a = document.createElement('a')
|
||||
a.style = 'display: none'
|
||||
a.target = '_blank'
|
||||
a.href = row.result
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
async view(job) {
|
||||
this.dialog.save = true
|
||||
await this.$nextTick()
|
||||
await this.$refs.saveDialog.open('view', { id: job.id })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.user-bar-tasks-item {
|
||||
margin-bottom: 10px;
|
||||
.user-bar-jobs-item {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.user-bar-tasks-item:hover {
|
||||
.user-bar-jobs-item:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.user-bar-tasks-item-body {
|
||||
.user-bar-jobs-item-body {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.user-bar-tasks-item-body .taskIcon {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
.user-bar-jobs-item-body .jobIcon {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
background: var(--el-color-primary-light-9);
|
||||
margin-right: 20px;
|
||||
margin-right: 2rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--el-color-primary);
|
||||
border-radius: 20px;
|
||||
border-radius: 1.5rem;
|
||||
}
|
||||
|
||||
.user-bar-tasks-item-body .taskMain {
|
||||
.user-bar-jobs-item-body .jobMain {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-bar-tasks-item-body .title h2 {
|
||||
.user-bar-jobs-item-body .title h2 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.user-bar-tasks-item-body .title p {
|
||||
font-size: 12px;
|
||||
.user-bar-jobs-item-body .title p {
|
||||
font-size: 1rem;
|
||||
color: #999;
|
||||
margin-top: 5px;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.user-bar-tasks-item-body .bottom {
|
||||
.user-bar-jobs-item-body .bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 20px;
|
||||
}
|
||||
</style>
|
@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<div class="user-bar">
|
||||
<div @click="configDark" class="tasks panel-item">
|
||||
<el-icon>
|
||||
<component :is="config.dark ? 'el-icon-sunny' : 'el-icon-moon'" />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div @click="search" class="panel-item hidden-sm-and-down">
|
||||
<el-icon>
|
||||
<el-icon-search />
|
||||
@ -12,7 +17,7 @@
|
||||
</div>
|
||||
<div @click="tasks" class="tasks panel-item">
|
||||
<el-icon>
|
||||
<el-icon-sort />
|
||||
<sc-icon-ScheduledJob />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div @click="showMsg" class="msg panel-item">
|
||||
@ -69,8 +74,22 @@ export default {
|
||||
tasks,
|
||||
message,
|
||||
},
|
||||
watch: {
|
||||
'config.dark'(val) {
|
||||
if (val) {
|
||||
document.documentElement.classList.add('dark')
|
||||
this.$TOOL.data.set('APP_DARK', val)
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
this.$TOOL.data.remove('APP_DARK')
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
config: {
|
||||
dark: this.$TOOL.data.get('APP_DARK') || false,
|
||||
},
|
||||
user: {},
|
||||
userName: '',
|
||||
userNameF: '',
|
||||
@ -86,6 +105,9 @@ export default {
|
||||
this.unreadCnt = res.data
|
||||
},
|
||||
methods: {
|
||||
configDark() {
|
||||
this.config.dark = !this.config.dark
|
||||
},
|
||||
gotoMsgCenter() {
|
||||
this.$router.push({ path: '/profile/message' })
|
||||
this.msg = false
|
||||
|
@ -17,7 +17,7 @@
|
||||
return { value: x[0], label: x[1][1] }
|
||||
}),
|
||||
placeholder: $t('作业状态'),
|
||||
style: 'width:15rem',
|
||||
style: 'width:10rem',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
@ -26,7 +26,7 @@
|
||||
return { value: x[0], label: x[1][1] }
|
||||
}),
|
||||
placeholder: $t('请求方式'),
|
||||
style: 'width:15rem',
|
||||
style: 'width:10rem',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
@ -36,7 +36,7 @@
|
||||
{ label: '禁用', value: false },
|
||||
],
|
||||
placeholder: '状态',
|
||||
style: 'width:15rem',
|
||||
style: 'width:10rem',
|
||||
},
|
||||
]"
|
||||
:vue="this"
|
||||
@ -51,7 +51,7 @@
|
||||
<sc-table
|
||||
v-loading="loading"
|
||||
:apiObj="$API.sys_job.pagedQuery"
|
||||
:default-sort="{ prop: 'createdTime', order: 'descending' }"
|
||||
:default-sort="{ prop: 'lastExecTime', order: 'descending' }"
|
||||
:params="query"
|
||||
@selection-change="
|
||||
(items) => {
|
||||
@ -61,6 +61,7 @@
|
||||
ref="table"
|
||||
remote-filter
|
||||
remote-sort
|
||||
row-key="id"
|
||||
stripe>
|
||||
<el-table-column type="selection"></el-table-column>
|
||||
<el-table-column :label="$t('作业编号')" prop="id" width="150" />
|
||||
@ -80,16 +81,17 @@
|
||||
return { value: x[0], text: x[1][1] }
|
||||
})
|
||||
"
|
||||
prop="httpMethod" />
|
||||
<el-table-column :label="$t('上次执行时间')" prop="lastExecTime" sortable="custom" />
|
||||
<el-table-column :label="$t('上次执行状态')" prop="lastStatusCode" sortable="custom" />
|
||||
<el-table-column :label="$t('下次执行时间')" prop="nextExecTime" sortable="custom" />
|
||||
<el-table-column :label="$t('创建时间')" prop="createdTime" sortable="custom" />
|
||||
<el-table-column :label="$t('启用')" prop="enabled">
|
||||
prop="httpMethod"
|
||||
width="100" />
|
||||
<el-table-column :label="$t('上次执行时间')" prop="lastExecTime" sortable="custom" width="170" />
|
||||
<el-table-column :label="$t('上次执行状态')" prop="lastStatusCode" sortable="custom" width="150" />
|
||||
<el-table-column :label="$t('下次执行时间')" prop="nextExecTime" sortable="custom" width="170" />
|
||||
<el-table-column :label="$t('启用')" prop="enabled" sortable="custom" width="100">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.enabled" @change="changeSwitch($event, scope.row)"></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('创建时间')" prop="createdTime" sortable="custom" width="170" />
|
||||
<na-col-operation
|
||||
:buttons="
|
||||
naColOperation.buttons.concat({
|
||||
@ -128,6 +130,7 @@ export default {
|
||||
filters: [],
|
||||
},
|
||||
filter: {},
|
||||
prop: 'lastExecTime',
|
||||
},
|
||||
dialog: {
|
||||
save: false,
|
||||
|
200
src/frontend/admin/src/views/sys/job/record/index.vue
Normal file
200
src/frontend/admin/src/views/sys/job/record/index.vue
Normal file
@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<el-container>
|
||||
<el-header>
|
||||
<div class="left-panel">
|
||||
<na-search
|
||||
:controls="[
|
||||
{
|
||||
type: 'input',
|
||||
field: ['root', 'keywords'],
|
||||
placeholder: $t('作业编号 / 执行编号'),
|
||||
style: 'width:20rem',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
field: ['dy', 'httpMethod'],
|
||||
options: Object.entries(this.$GLOBAL.enums.httpMethods).map((x) => {
|
||||
return { value: x[0], label: x[1][1] }
|
||||
}),
|
||||
placeholder: $t('请求方式'),
|
||||
style: 'width:10rem',
|
||||
},
|
||||
{
|
||||
multiple: true,
|
||||
type: 'select',
|
||||
field: ['dy', 'httpStatusCode'],
|
||||
options: [
|
||||
{ label: '20x', value: '200,299' },
|
||||
{ label: '30x', value: '300,399' },
|
||||
{ label: '40x', value: '400,499' },
|
||||
{ label: '50x', value: '500,599' },
|
||||
{ label: '90x', value: '900,999' },
|
||||
],
|
||||
placeholder: '状态码',
|
||||
style: 'width:20rem',
|
||||
},
|
||||
]"
|
||||
:vue="this"
|
||||
@search="onSearch"
|
||||
ref="search" />
|
||||
</div>
|
||||
<div class="right-panel"></div>
|
||||
</el-header>
|
||||
<el-main class="nopadding">
|
||||
<sc-table
|
||||
v-loading="loading"
|
||||
:apiObj="$API.sys_job.recordPagedQuery"
|
||||
:default-sort="{ prop: 'createdTime', order: 'descending' }"
|
||||
:params="query"
|
||||
@selection-change="
|
||||
(items) => {
|
||||
selection = items
|
||||
}
|
||||
"
|
||||
ref="table"
|
||||
remote-filter
|
||||
remote-sort
|
||||
row-key="id"
|
||||
stripe>
|
||||
<el-table-column :label="$t('唯一编码')" prop="id" sortable="custom" width="150" />
|
||||
<el-table-column :label="$t('执行耗时(毫秒)')" align="right" prop="duration" sortable="custom" width="150" />
|
||||
<el-table-column :label="$t('请求方法')" prop="httpMethod" sortable="custom" width="100" />
|
||||
<el-table-column :label="$t('HTTP 状态码')" align="right" prop="httpStatusCode" sortable="custom" width="150" />
|
||||
<el-table-column :label="$t('请求的网络地址')" prop="requestUrl" sortable="custom" />
|
||||
<el-table-column :label="$t('创建时间')" prop="createdTime" sortable="custom" width="170" />
|
||||
<na-col-operation :buttons="[naColOperation.buttons[0]]" :vue="this" width="100" />
|
||||
</sc-table>
|
||||
</el-main>
|
||||
</el-container>
|
||||
|
||||
<save-dialog
|
||||
v-if="dialog.save"
|
||||
@closed="dialog.save = false"
|
||||
@success="(data, mode) => table.handleUpdate($refs.table, data, mode)"
|
||||
ref="saveDialog"></save-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import saveDialog from './save'
|
||||
import table from '@/config/table'
|
||||
import naColOperation from '@/config/naColOperation'
|
||||
|
||||
export default {
|
||||
props: ['keywords'],
|
||||
components: {
|
||||
saveDialog,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
query: {
|
||||
dynamicFilter: {
|
||||
filters: [],
|
||||
},
|
||||
filter: {},
|
||||
},
|
||||
dialog: {
|
||||
save: false,
|
||||
},
|
||||
selection: [],
|
||||
}
|
||||
},
|
||||
watch: {},
|
||||
computed: {
|
||||
naColOperation() {
|
||||
return naColOperation
|
||||
},
|
||||
table() {
|
||||
return table
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.keywords) {
|
||||
this.$refs.search.form.root.keywords = this.keywords
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.keywords) {
|
||||
this.query.keywords = this.keywords
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//表格内开关事件
|
||||
async changeSwitch(event, row) {
|
||||
try {
|
||||
await this.$API.sys_job.setEnabled.post(row)
|
||||
this.$message.success(`操作成功`)
|
||||
} catch {
|
||||
//
|
||||
}
|
||||
this.$refs.table.refresh()
|
||||
},
|
||||
//删除
|
||||
async rowDel(row) {
|
||||
try {
|
||||
const res = await this.$API.sys_job.delete.post({ id: row.id })
|
||||
this.$refs.table.refresh()
|
||||
this.$message.success(`删除 ${res.data} 项`)
|
||||
} catch {
|
||||
//
|
||||
}
|
||||
},
|
||||
//批量删除
|
||||
async batchDel() {
|
||||
let loading
|
||||
try {
|
||||
await this.$confirm(`确定删除选中的 ${this.selection.length} 项吗?`, '提示', {
|
||||
type: 'warning',
|
||||
})
|
||||
loading = this.$loading()
|
||||
const res = await this.$API.sys_job.bulkDelete.post({
|
||||
items: this.selection,
|
||||
})
|
||||
this.$refs.table.refresh()
|
||||
this.$message.success(`删除 ${res.data} 项`)
|
||||
} catch {
|
||||
//
|
||||
}
|
||||
loading?.close()
|
||||
},
|
||||
|
||||
//搜索
|
||||
onSearch(form) {
|
||||
if (Array.isArray(form.dy.createdTime)) {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'createdTime',
|
||||
operator: 'dateRange',
|
||||
value: form.dy.createdTime,
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof form.dy.httpMethod === 'string' && form.dy.httpMethod.trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'httpMethod',
|
||||
operator: 'eq',
|
||||
value: form.dy.httpMethod,
|
||||
})
|
||||
}
|
||||
|
||||
if (Array.isArray(form.dy.httpStatusCode) && form.dy.httpStatusCode.length !== 0) {
|
||||
const filters = []
|
||||
for (const code of form.dy.httpStatusCode) {
|
||||
filters.push({
|
||||
field: 'httpStatusCode',
|
||||
operator: 'range',
|
||||
value: code,
|
||||
})
|
||||
}
|
||||
this.query.dynamicFilter.filters.push({
|
||||
logic: 'or',
|
||||
filters: filters,
|
||||
})
|
||||
}
|
||||
|
||||
this.$refs.table.upData()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
87
src/frontend/admin/src/views/sys/job/record/save.vue
Normal file
87
src/frontend/admin/src/views/sys/job/record/save.vue
Normal file
@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<sc-dialog v-model="visible" :title="titleMap[mode]" :width="800" @closed="$emit('closed')" destroy-on-close full-screen>
|
||||
<el-form
|
||||
v-loading="loading"
|
||||
:disabled="mode === 'view'"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="right"
|
||||
label-width="150px"
|
||||
ref="dialogForm">
|
||||
<el-tabs tab-position="top">
|
||||
<el-tab-pane :label="$t('基本信息')">
|
||||
<el-form-item :label="$t('唯一编码')" prop="id"><el-input v-model="form.id" clearable /></el-form-item
|
||||
><el-form-item :label="$t('执行耗时(毫秒)')" prop="duration"><el-input v-model="form.duration" clearable /></el-form-item
|
||||
><el-form-item :label="$t('请求方法')" prop="httpMethod"><el-input v-model="form.httpMethod" clearable /></el-form-item
|
||||
><el-form-item :label="$t('HTTP 状态码')" prop="httpStatusCode"><el-input v-model="form.httpStatusCode" clearable /></el-form-item
|
||||
><el-form-item :label="$t('作业编号')" prop="jobId"><el-input v-model="form.jobId" clearable /></el-form-item
|
||||
><el-form-item :label="$t('请求体')" prop="requestBody"
|
||||
><el-input v-model="form.requestBody" clearable rows="5" type="textarea" /></el-form-item
|
||||
><el-form-item :label="$t('请求头')" prop="requestHeader">
|
||||
<el-input v-model="form.requestHeader" clearable rows="5" type="textarea" /></el-form-item
|
||||
><el-form-item :label="$t('请求的网络地址')" prop="requestUrl"><el-input v-model="form.requestUrl" clearable /></el-form-item
|
||||
><el-form-item :label="$t('响应体')" prop="responseBody"
|
||||
><el-input v-model="form.responseBody" clearable rows="5" type="textarea" /></el-form-item
|
||||
><el-form-item :label="$t('响应头')" prop="responseHeader">
|
||||
<el-input v-model="form.responseHeader" clearable rows="5" type="textarea" /></el-form-item
|
||||
><el-form-item :label="$t('执行时间编号')" prop="timeId"><el-input v-model="form.timeId" clearable /></el-form-item
|
||||
><el-form-item :label="$t('创建时间')" prop="createdTime"><el-input v-model="form.createdTime" clearable /></el-form-item>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('原始数据')">
|
||||
<json-viewer
|
||||
:expand-depth="5"
|
||||
:expanded="true"
|
||||
:theme="this.$TOOL.data.get('APP_DARK') ? 'dark' : 'light'"
|
||||
:value="form"
|
||||
copyable
|
||||
sort></json-viewer>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
</template>
|
||||
</sc-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import scEditor from '@/components/scEditor/index.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
scEditor,
|
||||
},
|
||||
emits: ['success', 'closed'],
|
||||
data() {
|
||||
return {
|
||||
mode: 'add',
|
||||
titleMap: {
|
||||
view: this.$t('查看作业记录'),
|
||||
add: this.$t('新增作业记录'),
|
||||
edit: this.$t('编辑作业记录'),
|
||||
},
|
||||
visible: false,
|
||||
loading: false,
|
||||
//表单数据
|
||||
form: {},
|
||||
//验证规则
|
||||
rules: {},
|
||||
}
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
//显示
|
||||
async open(mode = 'add', data) {
|
||||
this.visible = true
|
||||
this.loading = true
|
||||
this.mode = mode
|
||||
if (data) {
|
||||
const res = await this.$API.sys_job.recordGet.post({ id: data.id })
|
||||
Object.assign(this.form, res.data)
|
||||
}
|
||||
this.loading = false
|
||||
return this
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
@ -1,15 +1,15 @@
|
||||
<template>
|
||||
<sc-dialog v-model="visible" :title="titleMap[mode]" :width="800" @closed="$emit('closed')" destroy-on-close full-screen>
|
||||
<el-form
|
||||
v-loading="loading"
|
||||
:disabled="mode === 'view'"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="right"
|
||||
label-width="150px"
|
||||
ref="dialogForm">
|
||||
<el-tabs tab-position="top">
|
||||
<el-tab-pane :label="$t('基本信息')">
|
||||
<el-tabs v-model="tabIndex" tab-position="top">
|
||||
<el-tab-pane :label="$t('基本信息')" :name="0">
|
||||
<el-form
|
||||
v-loading="loading"
|
||||
:disabled="mode === 'view'"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="right"
|
||||
label-width="150px"
|
||||
ref="dialogForm">
|
||||
<el-form-item v-if="mode === 'view'" :label="$t('作业编号')" prop="id">
|
||||
<el-input v-model="form.id" clearable />
|
||||
</el-form-item>
|
||||
@ -48,8 +48,8 @@
|
||||
<el-form-item v-if="mode === 'view'" :label="$t('执行用户编号')" prop="userId">
|
||||
<el-input v-model="form.userId" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item v-else :label="$t('执行用户')" prop="userId">
|
||||
<na-user-select v-model="form.userId"></na-user-select>
|
||||
<el-form-item v-else :label="$t('执行用户')" prop="user">
|
||||
<na-user-select v-model="form.user"></na-user-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="mode === 'view'" :label="$t('创建时间')" prop="createdTime">
|
||||
<el-input v-model="form.createdTime" clearable />
|
||||
@ -72,18 +72,22 @@
|
||||
<el-form-item v-if="mode === 'view'" :label="$t('修改者用户名')" prop="modifiedUserName">
|
||||
<el-input v-model="form.modifiedUserName" clearable />
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('原始数据')">
|
||||
<json-viewer
|
||||
:expand-depth="5"
|
||||
:expanded="true"
|
||||
:theme="this.$TOOL.data.get('APP_DARK') ? 'dark' : 'light'"
|
||||
:value="form"
|
||||
copyable
|
||||
sort></json-viewer>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('执行记录')" :name="1">
|
||||
<record v-if="tabIndex === 1" :keywords="form.id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('原始数据')" :name="2">
|
||||
<json-viewer
|
||||
:expand-depth="5"
|
||||
:expanded="true"
|
||||
:theme="this.$TOOL.data.get('APP_DARK') ? 'dark' : 'light'"
|
||||
:value="form"
|
||||
copyable
|
||||
sort></json-viewer>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
<el-button v-if="mode !== 'view'" :loading="loading" @click="submit" type="primary">保 存</el-button>
|
||||
@ -93,14 +97,17 @@
|
||||
|
||||
<script>
|
||||
import scEditor from '@/components/scEditor/index.vue'
|
||||
import Record from '@/views/sys/job/record/index.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Record,
|
||||
scEditor,
|
||||
},
|
||||
emits: ['success', 'closed'],
|
||||
data() {
|
||||
return {
|
||||
tabIndex: 0,
|
||||
mode: 'add',
|
||||
titleMap: {
|
||||
view: this.$t('查看作业'),
|
||||
@ -110,7 +117,12 @@ export default {
|
||||
visible: false,
|
||||
loading: false,
|
||||
//表单数据
|
||||
form: {},
|
||||
form: {
|
||||
executionCron: '* * * * *',
|
||||
httpMethod: 'Post',
|
||||
requestHeader: `{ "Content-Type": "application/json" }`,
|
||||
requestBody: '{}',
|
||||
},
|
||||
//验证规则
|
||||
rules: {
|
||||
executionCron: [
|
||||
@ -132,6 +144,19 @@ export default {
|
||||
message: this.$t('作业名称不能为空'),
|
||||
},
|
||||
],
|
||||
requestHeader: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (!value) return callback()
|
||||
try {
|
||||
JSON.parse(value)
|
||||
} catch {
|
||||
return callback(this.$t('请求头不正确'))
|
||||
}
|
||||
return callback()
|
||||
},
|
||||
},
|
||||
],
|
||||
requestUrl: [
|
||||
{
|
||||
required: true,
|
||||
@ -139,7 +164,7 @@ export default {
|
||||
message: this.$t('请求的网络地址不正确'),
|
||||
},
|
||||
],
|
||||
userId: [
|
||||
user: [
|
||||
{
|
||||
required: true,
|
||||
trigger: 'blur',
|
||||
@ -178,7 +203,9 @@ export default {
|
||||
try {
|
||||
const method = this.mode === 'add' ? this.$API.sys_job.create : this.$API.sys_job.update
|
||||
this.loading = true
|
||||
const res = await method.post(Object.assign({}, this.form, { userId: this.form.userId.id }))
|
||||
const res = await method.post(
|
||||
Object.assign({}, this.form, { userId: this.form.user.id, requestHeaders: JSON.parse(this.form.requestHeader) }),
|
||||
)
|
||||
this.loading = false
|
||||
this.$emit('success', res.data, this.mode)
|
||||
this.visible = false
|
||||
|
@ -44,6 +44,7 @@
|
||||
ref="table"
|
||||
remoteFilter
|
||||
remoteSort
|
||||
row-key="id"
|
||||
stripe>
|
||||
<el-table-column :label="$t('日志编号')" prop="id" sortable="custom"></el-table-column>
|
||||
<el-table-column :label="$t('日志时间')" prop="createdTime" sortable="custom"></el-table-column>
|
||||
|
@ -61,6 +61,7 @@
|
||||
ref="table"
|
||||
remoteFilter
|
||||
remoteSort
|
||||
row-key="id"
|
||||
stripe>
|
||||
<el-table-column :label="$t('日志编号')" prop="id" sortable="custom" width="150"></el-table-column>
|
||||
<el-table-column :label="$t('日志时间')" prop="createdTime" sortable="custom" width="170"></el-table-column>
|
||||
|
@ -42,6 +42,7 @@
|
||||
ref="table"
|
||||
remote-filter
|
||||
remote-sort
|
||||
row-key="id"
|
||||
stripe>
|
||||
<el-table-column type="selection"></el-table-column>
|
||||
<el-table-column :label="$t('消息编号')" prop="id" width="150" />
|
||||
|
@ -58,6 +58,7 @@
|
||||
ref="table"
|
||||
remote-filter
|
||||
remote-sort
|
||||
row-key="id"
|
||||
stripe>
|
||||
<el-table-column type="selection"></el-table-column>
|
||||
<el-table-column :label="$t('用户编号')" prop="id" sortable="custom" width="150"></el-table-column>
|
||||
|
Reference in New Issue
Block a user