refactor: 代码规范、Bug修复
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
|
||||
is_weekend() {
|
||||
day=$(date +%u)
|
||||
[ "$day" -eq 6 ] || [ "$day" -eq 7 ]
|
||||
}
|
||||
|
||||
if is_weekend; then
|
||||
# 如果是周末,使用当前时间
|
||||
git add .
|
||||
git commit -m "$1"
|
||||
git push
|
||||
else
|
||||
# 如果不是周末,设置为最近的周末时间
|
||||
day=$(date +%u)
|
||||
if [ "$day" -le 5 ]; then
|
||||
# 周一到周五,设置为即将到来的周六
|
||||
days_until_saturday=$((6 - day))
|
||||
weekend_date=$(date -d "$days_until_saturday days" "+%Y-%m-%d 12:00:00")
|
||||
else
|
||||
# 周日,使用昨天(周六)
|
||||
weekend_date=$(date -d "yesterday" "+%Y-%m-%d 12:00:00")
|
||||
fi
|
||||
|
||||
export GIT_AUTHOR_DATE="$weekend_date"
|
||||
export GIT_COMMITTER_DATE="$weekend_date"
|
||||
|
||||
git add .
|
||||
git commit -m "$1"
|
||||
git push
|
||||
|
||||
unset GIT_AUTHOR_DATE
|
||||
unset GIT_COMMITTER_DATE
|
||||
fi
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cutejava-front",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.0",
|
||||
"description": "CuteJava 前端源码",
|
||||
"author": "Odboy",
|
||||
"license": "Apache-2.0",
|
||||
@@ -48,6 +48,7 @@
|
||||
"element-ui": "^2.15.14",
|
||||
"file-saver": "1.3.8",
|
||||
"fuse.js": "3.4.4",
|
||||
"html2canvas": "^1.4.1",
|
||||
"js-beautify": "^1.10.2",
|
||||
"js-cookie": "2.2.0",
|
||||
"jsencrypt": "^3.0.0-rc.1",
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function startPipeline(data) {
|
||||
return request({
|
||||
url: 'api/devops/pipelineInstance/start',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function stopPipeline(data) {
|
||||
return request({
|
||||
url: 'api/devops/pipelineInstance/stop',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function retryPipeline(data) {
|
||||
return request({
|
||||
url: 'api/devops/pipelineInstance/retry',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function restartPipeline(data) {
|
||||
return request({
|
||||
url: 'api/devops/pipelineInstance/restart',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function queryLastPipelineDetail(instanceId) {
|
||||
return request({
|
||||
url: 'api/devops/pipelineInstance/last',
|
||||
method: 'post',
|
||||
data: {
|
||||
instanceId
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function queryLastPipelineDetailWs(instanceId) {
|
||||
return request({
|
||||
url: 'api/devops/pipelineInstance/lastWs',
|
||||
method: 'post',
|
||||
data: {
|
||||
instanceId
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getPipelineTemplate(id) {
|
||||
return request({
|
||||
url: 'api/devops/pipelineTemplate/getPipelineTemplate',
|
||||
method: 'post',
|
||||
data: { id }
|
||||
})
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 236 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 227 KiB |
@@ -34,6 +34,7 @@
|
||||
:disabled="disabledDle"
|
||||
type="text"
|
||||
size="mini"
|
||||
style="color: #F56C6C"
|
||||
@click.stop="toDelete"
|
||||
>删除</el-button>
|
||||
</el-popover>
|
||||
|
||||
@@ -88,6 +88,19 @@ Vue.filter('priceFormat', function(data) {
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
Vue.config.errorHandler = function(err, vm, info) {
|
||||
// err: 错误对象
|
||||
// vm: 发生错误的 Vue 实例
|
||||
// info: Vue 特定的错误信息,如生命周期钩子、事件等
|
||||
|
||||
console.error('Global Vue error:', err)
|
||||
console.log('Component:', vm)
|
||||
console.log('Error info:', info)
|
||||
|
||||
// 可以在这里发送错误日志到服务器
|
||||
// logErrorToServer(err, vm, info);
|
||||
}
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router,
|
||||
|
||||
@@ -70,8 +70,21 @@ export function DeepCopyObject(obj) {
|
||||
* @returns {*|number}
|
||||
*/
|
||||
export function CountArraysObjectByPropKey(arr, propKey, targetValue) {
|
||||
if (!Array.isArray(arr)) return 0
|
||||
return arr.filter(item => item[propKey] === targetValue).length
|
||||
if (!Array.isArray(arr)) {
|
||||
return 0
|
||||
}
|
||||
let cnt = 0
|
||||
for (const element of arr) {
|
||||
if (element.hasOwnProperty(propKey)) {
|
||||
// console.error('that.pipelineInstance.propKey', propKey)
|
||||
// console.error('that.pipelineInstance.element[propKey]', element[propKey])
|
||||
// console.error('that.pipelineInstance.targetValue', targetValue)
|
||||
if (element[propKey] === targetValue) {
|
||||
cnt = cnt + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return cnt
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,6 +303,7 @@ export function MergeObject(target, source) {
|
||||
})
|
||||
return target
|
||||
}
|
||||
|
||||
/**
|
||||
* 防抖动
|
||||
* @param {Function} func
|
||||
@@ -328,3 +342,46 @@ export function DeBounce(func, wait, immediate = false) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化表格行列中的环境字符串
|
||||
* @param row
|
||||
* @param column
|
||||
* @param cellValue
|
||||
* @param index
|
||||
* @returns {string}
|
||||
* @constructor
|
||||
*/
|
||||
export function FormatRowEnvStr(row, column, cellValue, index) {
|
||||
if (cellValue === undefined) {
|
||||
return ''
|
||||
}
|
||||
const data = {
|
||||
daily: '日常环境',
|
||||
stage: '预发环境',
|
||||
gray: '灰度环境',
|
||||
production: '生产环境'
|
||||
}
|
||||
const title = data[cellValue]
|
||||
if (title) {
|
||||
return title
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
export function IsEmpty(obj) {
|
||||
if (obj === undefined) {
|
||||
// console.error('is undefined')
|
||||
return true
|
||||
}
|
||||
if (obj === '') {
|
||||
// console.error('is ""')
|
||||
return true
|
||||
}
|
||||
if (obj === 'undefined') {
|
||||
// console.error('is "undefined"')
|
||||
return true
|
||||
}
|
||||
// console.error('is null')
|
||||
return obj === null
|
||||
}
|
||||
|
||||
@@ -147,16 +147,12 @@ export function IsValidTelephone(rule, value, callback) {
|
||||
}
|
||||
|
||||
/* 是否手机号码*/
|
||||
export function IsValidPhone(rule, value, callback) {
|
||||
export function IsValidPhone(value) {
|
||||
const reg = /^1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}$/
|
||||
if (value === '' || value === undefined || value == null) {
|
||||
callback()
|
||||
return false
|
||||
} else {
|
||||
if ((!reg.test(value)) && value !== '') {
|
||||
callback(new Error('请输入正确的电话号码'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
return !((!reg.test(value)) && value !== '')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import Vue from 'vue'
|
||||
|
||||
/**
|
||||
* 全屏锁定加载动画
|
||||
*/
|
||||
// export default function CsLoading() {
|
||||
// this.instance = null
|
||||
// this.show = function() {
|
||||
// this.instance = Vue.prototype.$loading({
|
||||
// lock: true,
|
||||
// text: 'Loading',
|
||||
// spinner: 'el-icon-loading',
|
||||
// background: 'rgba(0, 0, 0, 0.7)'
|
||||
// })
|
||||
// }
|
||||
// this.close = function() {
|
||||
// if (this.instance) {
|
||||
// this.instance.close()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
export default class CsLoading {
|
||||
constructor() {
|
||||
this.instance = null
|
||||
}
|
||||
|
||||
show() {
|
||||
this.instance = Vue.prototype.$loading({
|
||||
lock: true,
|
||||
text: 'Loading',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
})
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.instance) {
|
||||
this.instance.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div>Demo</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'Demo',
|
||||
components: { },
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
watch: {},
|
||||
mounted() {},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
<!--
|
||||
* 部门树组件
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-07-29
|
||||
-->
|
||||
<template>
|
||||
<el-col :xs="9" :sm="6" :md="5" :lg="4" :xl="4">
|
||||
<div class="head-container">
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
<!--
|
||||
* 文件上传组件:支持拖拽上传
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-07-29
|
||||
-->
|
||||
<template>
|
||||
<el-upload
|
||||
ref="upload"
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
<!--
|
||||
* 文件上传组件
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-07-29
|
||||
-->
|
||||
<template>
|
||||
<el-upload
|
||||
ref="upload"
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
<!--
|
||||
* 单用户选择组件
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-07-29
|
||||
-->
|
||||
<template>
|
||||
<el-select
|
||||
v-model="users"
|
||||
v-model="user"
|
||||
style="width: 100%"
|
||||
filterable
|
||||
remote
|
||||
clearable
|
||||
reserve-keyword
|
||||
placeholder="支持用户姓名、手机号、邮箱、用户名查询"
|
||||
:remote-method="remoteMethod"
|
||||
@@ -34,14 +41,14 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
users: this.value,
|
||||
user: this.value,
|
||||
loading: false,
|
||||
options: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.users = newVal
|
||||
this.user = newVal
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
<!--
|
||||
* 产品线选择组件
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<el-select
|
||||
v-model="selectedValue"
|
||||
v-model="productLine"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择产品线"
|
||||
@@ -37,12 +43,12 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
deptOptions: [],
|
||||
selectedValue: this.value
|
||||
productLine: this.value
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.selectedValue = newVal
|
||||
this.productLine = newVal
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -58,7 +64,7 @@ export default {
|
||||
})
|
||||
},
|
||||
handleChange(value) {
|
||||
this.selectedValue = value
|
||||
this.productLine = value
|
||||
for (const item of this.deptOptions) {
|
||||
if (item.value === value) {
|
||||
this.$emit('detail', item)
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
<!--
|
||||
* 产品线级联选择组件
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<el-cascader
|
||||
v-model="value"
|
||||
v-model="productLine"
|
||||
:disabled="disabled"
|
||||
:options="deptOptions"
|
||||
style="width: 100%"
|
||||
@@ -28,12 +34,12 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
deptOptions: [],
|
||||
selectedValue: this.value
|
||||
productLine: this.value
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.selectedValue = newVal
|
||||
this.productLine = newVal
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -57,14 +63,15 @@ export default {
|
||||
fetchDeptData() {
|
||||
queryDeptSelectProDataSource().then(data => {
|
||||
this.deptOptions = this.cleanEmptyChildren(data)
|
||||
console.error('deptOptions', this.deptOptions)
|
||||
// console.error('deptOptions', this.deptOptions)
|
||||
}).catch(error => {
|
||||
console.error('获取部门数据失败:', error)
|
||||
CsMessage.Error('获取部门数据失败')
|
||||
})
|
||||
},
|
||||
handleChange(value) {
|
||||
this.selectedValue = value
|
||||
this.productLine = value
|
||||
// console.error('value', value)
|
||||
// 绑定change事件
|
||||
this.$emit('change', value)
|
||||
// 绑定form value
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
<!--
|
||||
* 多用户选择组件
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-07
|
||||
-->
|
||||
<template>
|
||||
<el-select
|
||||
v-model="users"
|
||||
@@ -31,6 +37,11 @@ export default {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
dataSource: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -45,6 +56,11 @@ export default {
|
||||
this.users = newVal
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.dataSource && this.dataSource.length > 0) {
|
||||
this.options = this.dataSource
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
remoteMethod(query) {
|
||||
if (query !== '') {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<!--Yaml编辑器 基于-->
|
||||
<!--<a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirror</a>,-->
|
||||
<!--主题预览地址 <a href="https://codemirror.net/demo/theme.html#idea" target="_blank">Theme</a>-->
|
||||
<!--</p>-->
|
||||
<!-- mode取值如下:yaml、java、go、swift、dockerfile、groovy、lua、perl、python、ruby、sql、xml、vue -->
|
||||
<!--
|
||||
* 代码编辑器
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
* @description 基于Yaml编辑器 https://github.com/codemirror/CodeMirror,主题预览地址 https://codemirror.net/demo/theme.html#idea
|
||||
* @参数mode取值范围 yaml、java、go、swift、dockerfile、groovy、lua、perl、python、ruby、sql、xml、vue
|
||||
-->
|
||||
<template>
|
||||
<textarea ref="textarea" />
|
||||
</template>
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
<!--
|
||||
* 拖拽表格组件:封装自定义逻辑与参数,表格支持拖拽排序
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<el-table
|
||||
@@ -151,6 +157,9 @@ export default {
|
||||
data.forEach((item, index) => {
|
||||
item.orderNum = index + 1
|
||||
})
|
||||
},
|
||||
getDataSource() {
|
||||
return this.crud.dataSource
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
<!--
|
||||
* 简单表单弹窗:封装了常用方法和参数
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="title"
|
||||
@@ -17,7 +23,7 @@
|
||||
<div class="dialog-footer" style="padding-right: 30px;text-align: right">
|
||||
<el-button type="danger" @click="hidden">取 消</el-button>
|
||||
<!--<el-button @click="resetForm('form')">重 置</el-button>-->
|
||||
<el-button type="primary" @click="submitForm('form')">提 交</el-button>
|
||||
<el-button v-prevent-re-click type="primary" @click="submitForm('form')">提 交</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
<!--
|
||||
* 简单表单抽屉:封装了常用方法和参数
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<el-drawer
|
||||
:title="title"
|
||||
@@ -6,8 +12,9 @@
|
||||
:before-close="beforeClose"
|
||||
:append-to-body="true"
|
||||
:close-on-press-escape="false"
|
||||
:wrapper-closable="false"
|
||||
:destroy-on-close="true"
|
||||
:show-close="false"
|
||||
:show-close="true"
|
||||
:modal="false"
|
||||
>
|
||||
<div class="container-form">
|
||||
@@ -19,7 +26,7 @@
|
||||
<el-divider />
|
||||
<div class="dialog-footer" style="padding-right: 30px;text-align: right">
|
||||
<el-button type="danger" @click="hidden">取 消</el-button>
|
||||
<el-button type="primary" @click="submitForm('form')">提 交</el-button>
|
||||
<el-button v-prevent-re-click type="primary" @click="submitForm('form')">提 交</el-button>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<!--
|
||||
* 横向滚动条容器
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-20
|
||||
-->
|
||||
<template>
|
||||
<div class="overflow-panel">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteHorizontalScrollBar'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.overflow-panel {
|
||||
width: 1500px; /* 固定宽度 */
|
||||
overflow-x: auto; /* 允许水平滚动 */
|
||||
white-space: nowrap; /* 防止内容换行,使所有内容在一行显示 */
|
||||
padding: 30px 20px 30px 20px;
|
||||
}
|
||||
|
||||
.overflow-panel::-webkit-scrollbar {
|
||||
height: 5px; /* 设置水平滚动条高度 */
|
||||
}
|
||||
|
||||
.overflow-panel::-webkit-scrollbar-track {
|
||||
background: #f1f1f1; /* 滚动条底色 */
|
||||
}
|
||||
|
||||
.overflow-panel::-webkit-scrollbar-thumb {
|
||||
background: #DCDFE6; /* 滚动条颜色 */
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<!--Yaml编辑器 基于-->
|
||||
<!--<a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirror</a>,-->
|
||||
<!--主题预览地址 <a href="https://codemirror.net/demo/theme.html#idea" target="_blank">Theme</a>-->
|
||||
<!--</p>-->
|
||||
<!--
|
||||
* Java代码编辑器
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<textarea ref="textarea" />
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<!--
|
||||
* 文字超链接
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-26
|
||||
-->
|
||||
<template>
|
||||
<el-link
|
||||
type="primary"
|
||||
:underline="false"
|
||||
:href="href"
|
||||
target="_blank"
|
||||
>{{ title }}</el-link>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteLink',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '默认标题'
|
||||
},
|
||||
href: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: 'https://github.com/odboy-tianjun'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
<!--<p class="warn-content">-->
|
||||
<!--Markdown 基于-->
|
||||
<!--<el-link type="primary" href="https://github.com/hinesboy/mavonEditor" target="_blank">MavonEditor</el-link>-->
|
||||
<!--<el-link type="primary" href="" target="_blank">MavonEditor</el-link>-->
|
||||
<!--</p>-->
|
||||
<!--
|
||||
* Markdown编辑器
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
* @description 基于MavonEditor https://github.com/hinesboy/mavonEditor
|
||||
-->
|
||||
<template>
|
||||
<mavon-editor
|
||||
ref="md"
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
* 无数据
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-20
|
||||
-->
|
||||
<template>
|
||||
<el-empty :description="desc" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteNoData',
|
||||
props: {
|
||||
desc: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '暂无匹配数据'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<!-- 一键复制组件 -->
|
||||
<!-- 使用方式: <one-key-copy style="margin-left: 10pt" :content.sync="data" /> -->
|
||||
<!--
|
||||
* 一键复制组件
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
* @description 使用方式:<cute-one-key-copy style="margin-left: 10pt" :content.sync="data" />
|
||||
-->
|
||||
<template>
|
||||
<el-button
|
||||
v-clipboard:copy="content"
|
||||
|
||||
@@ -1,355 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="box-card"
|
||||
:style="cardStyle"
|
||||
>
|
||||
<div>
|
||||
<div style="float: left;">
|
||||
<i :class="statusIconClass" :style="statusIconStyle" />
|
||||
</div>
|
||||
<div class="box-name">{{ currentTemplateData.name }}</div>
|
||||
<div style="clear: both" />
|
||||
</div>
|
||||
<div class="box-current-node">
|
||||
<a @click="onCurrentNodeClick">
|
||||
{{ currentNodeDisplayText }}
|
||||
</a>
|
||||
</div>
|
||||
<el-row>
|
||||
<el-col :span="12" style="text-align: left">
|
||||
<!-- 流水线节点状态为fail,且节点支持重试 -->
|
||||
<div v-if="showRetryButton">
|
||||
<div class="box-buttons">
|
||||
<el-button
|
||||
v-prevent-re-click="5000"
|
||||
type="text"
|
||||
size="medium"
|
||||
:style="getButtonStyle()"
|
||||
@click="onNodeRetryClick"
|
||||
>
|
||||
重试
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 流水线节点状态为success,且有操作按钮 -->
|
||||
<div v-else-if="showOperationButtons">
|
||||
<div class="box-buttons">
|
||||
<el-button
|
||||
v-for="buttonItem in currentTemplateData.buttons"
|
||||
:key="buttonItem.service"
|
||||
size="medium"
|
||||
type="text"
|
||||
:style="getButtonStyle(buttonItem)"
|
||||
@click="onNodeOperationClick(buttonItem)"
|
||||
>
|
||||
{{ buttonItem.title }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 流水线节点状态为running,且有操作按钮 -->
|
||||
<div v-else-if="showOperationButtons">
|
||||
<div class="box-buttons">
|
||||
<el-button
|
||||
v-for="buttonItem in currentTemplateData.buttons"
|
||||
:key="buttonItem.service"
|
||||
size="medium"
|
||||
type="text"
|
||||
:style="getButtonStyle(buttonItem)"
|
||||
@click="onNodeOperationClick(buttonItem)"
|
||||
>
|
||||
{{ buttonItem.title }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 占位用,为了前端样式美观些 -->
|
||||
<div v-else>
|
||||
<div style="width: 110px;height: 30px" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="box-execute-time">
|
||||
{{ executeTimeStr }}
|
||||
</div>
|
||||
</el-col>
|
||||
<div style="clear: both" />
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import dayjs from 'dayjs'
|
||||
import { mapGetters } from 'vuex'
|
||||
import CsMessage from '@/utils/elementui/CsMessage'
|
||||
export default {
|
||||
name: 'CutePipelineNode',
|
||||
props: {
|
||||
templateData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: function() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
instanceData: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: function() {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentTemplateData: {},
|
||||
currentInstanceData: {}
|
||||
}
|
||||
},
|
||||
// 动态计算
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'statusConst'
|
||||
]),
|
||||
cardStyle() {
|
||||
const statusKey = this.currentTemplateData.currentNodeStatus || this.statusConst.pending.code
|
||||
const statusConfig = this.statusConst[statusKey] || this.statusConst.pending
|
||||
return {
|
||||
borderTop: `5px solid ${statusConfig.color}`
|
||||
}
|
||||
},
|
||||
statusIconClass() {
|
||||
const status = this.currentTemplateData.currentNodeStatus || this.statusConst.pending.code
|
||||
return this.statusConst[status].icon || 'el-icon-time'
|
||||
},
|
||||
statusIconStyle() {
|
||||
return {
|
||||
fontSize: '22px',
|
||||
color: this.getStatusColor()
|
||||
}
|
||||
},
|
||||
currentNodeDisplayText() {
|
||||
return this.currentTemplateData.currentNodeMsg || this.statusConst.pending.label
|
||||
},
|
||||
showRetryButton() {
|
||||
return this.currentTemplateData.currentNodeStatus === this.statusConst.fail.code &&
|
||||
this.currentTemplateData.retry === true
|
||||
},
|
||||
showOperationButtons() {
|
||||
const status = this.currentTemplateData.currentNodeStatus
|
||||
const hasButtons = this.currentTemplateData.buttons && this.currentTemplateData.buttons.length > 0
|
||||
return hasButtons && (
|
||||
status === this.statusConst.success.code ||
|
||||
status === this.statusConst.running.code
|
||||
)
|
||||
},
|
||||
executeTimeStr() {
|
||||
return this.renderDateTimeStr(this.currentTemplateData)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
templateData: {
|
||||
handler(newVal, oldVal) {
|
||||
this.currentTemplateData = { ...newVal }
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
instanceData: {
|
||||
handler(newVal, oldVal) {
|
||||
this.currentInstanceData = { ...newVal }
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化时,设置流水线模板
|
||||
this.currentTemplateData = this.templateData
|
||||
this.currentInstanceData = this.instanceData
|
||||
},
|
||||
methods: {
|
||||
getStatusColor() {
|
||||
const status = this.currentTemplateData.currentNodeStatus || this.statusConst.pending.code
|
||||
const statusConfig = this.statusConst[status] || this.statusConst.pending
|
||||
return statusConfig.color
|
||||
},
|
||||
getButtonStyle(buttonItem) {
|
||||
if (buttonItem) {
|
||||
const isPositiveAction = ['apply', 'agree', 'ok', 'success'].includes(buttonItem.code)
|
||||
const status = this.currentTemplateData.currentNodeStatus
|
||||
const color = isPositiveAction
|
||||
? this.statusConst.running.color
|
||||
: this.statusConst.fail.color
|
||||
let display = ''
|
||||
if (status === this.statusConst.running.code) {
|
||||
display = this.getRunningNodeButtonVisibleStatus(buttonItem)
|
||||
} else if (status === this.statusConst.success.code) {
|
||||
display = this.getSuccessNodeButtonVisibleStatus(buttonItem)
|
||||
}
|
||||
return {
|
||||
padding: 0,
|
||||
margin: '0 10px 0 10px',
|
||||
color,
|
||||
display
|
||||
}
|
||||
}
|
||||
return {
|
||||
padding: 0,
|
||||
margin: '0 10px 0 10px',
|
||||
color: this.statusConst.running.color
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 运行中节点的按钮显示
|
||||
* @param buttonItem
|
||||
* @returns {string}
|
||||
*/
|
||||
getRunningNodeButtonVisibleStatus(buttonItem) {
|
||||
if (buttonItem.type === 'qrCodeDialog') {
|
||||
return 'none'
|
||||
}
|
||||
return ''
|
||||
},
|
||||
/**
|
||||
* 成功节点的按钮显示
|
||||
* @param buttonItem
|
||||
* @returns {string}
|
||||
*/
|
||||
getSuccessNodeButtonVisibleStatus(buttonItem) {
|
||||
const that = this
|
||||
// 根据按钮编码
|
||||
if (buttonItem.code === that.currentTemplateData.currentNodeStatus) {
|
||||
return ''
|
||||
}
|
||||
return 'none'
|
||||
},
|
||||
renderDateTimeStr(newVal) {
|
||||
let executeTimeStr = ''
|
||||
switch (newVal.status) {
|
||||
case 'pending':
|
||||
executeTimeStr = ''
|
||||
break
|
||||
case 'running':
|
||||
case 'success':
|
||||
case 'fail':
|
||||
if (newVal.currentNodeStatus !== this.statusConst.pending.code) {
|
||||
if (newVal.startTime && newVal.finishTime) {
|
||||
executeTimeStr = this.formatTimeDifference(newVal.startTime, newVal.finishTime)
|
||||
return executeTimeStr
|
||||
}
|
||||
if (newVal.startTime) {
|
||||
executeTimeStr = this.formatTimeDifference(newVal.startTime, new Date())
|
||||
return executeTimeStr
|
||||
}
|
||||
if (newVal.createTime) {
|
||||
executeTimeStr = this.formatTimeDifference(newVal.createTime, new Date())
|
||||
return executeTimeStr
|
||||
}
|
||||
}
|
||||
break
|
||||
default:
|
||||
executeTimeStr = ''
|
||||
break
|
||||
}
|
||||
return executeTimeStr
|
||||
},
|
||||
formatTimeDifference(createTimeStr, updateTimeStr) {
|
||||
const startTime = dayjs(createTimeStr)
|
||||
const endTime = dayjs(updateTimeStr)
|
||||
// 计算两个时间点之间的差异(毫秒)
|
||||
const diffInMilliseconds = endTime.diff(startTime)
|
||||
// 将毫秒转换为秒
|
||||
let totalSeconds = Math.floor(diffInMilliseconds / 1000)
|
||||
const days = Math.floor(totalSeconds / (3600 * 24))
|
||||
totalSeconds %= 3600 * 24
|
||||
const hours = Math.floor(totalSeconds / 3600)
|
||||
totalSeconds %= 3600
|
||||
const minutes = Math.floor(totalSeconds / 60)
|
||||
const seconds = totalSeconds % 60
|
||||
// 格式化为字符串
|
||||
if (days > 0) {
|
||||
return `${days} 天 ${hours} 时 ${minutes} 分 ${seconds} 秒`
|
||||
}
|
||||
if (hours > 0) {
|
||||
return `${hours} 时 ${minutes} 分 ${seconds} 秒`
|
||||
}
|
||||
if (minutes > 0) {
|
||||
return `${minutes} 分 ${seconds} 秒`
|
||||
}
|
||||
if (seconds >= 0) {
|
||||
return `${seconds} 秒`
|
||||
}
|
||||
return `${diffInMilliseconds} 毫秒`
|
||||
},
|
||||
onCurrentNodeClick() {
|
||||
const currentTemplateData = this.currentTemplateData || { click: false }
|
||||
if (!currentTemplateData.click) {
|
||||
CsMessage.Warning('当前流水线节点不支持查看明细')
|
||||
return
|
||||
}
|
||||
this.$emit('node-click', this.templateData)
|
||||
},
|
||||
onNodeRetryClick() {
|
||||
const currentTemplateData = this.currentTemplateData || { retry: false }
|
||||
if (!currentTemplateData.retry) {
|
||||
CsMessage.Warning('当前流水线节点不支持重试')
|
||||
return
|
||||
}
|
||||
const data = {
|
||||
...this.currentInstanceData,
|
||||
code: this.currentTemplateData.code
|
||||
}
|
||||
this.$emit('node-retry', data)
|
||||
},
|
||||
onNodeOperationClick(buttonInfo) {
|
||||
const currentTemplateData = this.currentTemplateData || {}
|
||||
this.$emit('node-opera', currentTemplateData, buttonInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.box-card {
|
||||
width: 300px; /* 卡片宽度 */
|
||||
max-width: 300px; /* 卡片宽度 */
|
||||
padding: 10px;
|
||||
background-color: #fff; /* 背景颜色 */
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
||||
border-radius: 8px; /* 圆角效果 */
|
||||
overflow: hidden; /* 防止子元素溢出 */
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease; /* 过渡效果 */
|
||||
display: inline-block;
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
.box-card:hover {
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.box-name {
|
||||
float: left;
|
||||
margin-left: 5px;
|
||||
font-size: 16px;
|
||||
line-height: 22px
|
||||
}
|
||||
|
||||
.box-current-node {
|
||||
padding: 20px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-wrap: nowrap;
|
||||
font-size: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.box-buttons {
|
||||
padding: 5px;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.box-execute-time {
|
||||
text-align: right;
|
||||
font-size: 12px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,3 +1,9 @@
|
||||
<!--
|
||||
* 预览抽屉:封装了常用的方法和属性
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<el-drawer
|
||||
:title="title"
|
||||
@@ -6,8 +12,9 @@
|
||||
:before-close="beforeClose"
|
||||
:append-to-body="true"
|
||||
:close-on-press-escape="false"
|
||||
:wrapper-closable="wrapperClosable"
|
||||
:destroy-on-close="true"
|
||||
:show-close="false"
|
||||
:show-close="true"
|
||||
:modal="false"
|
||||
>
|
||||
<!-- 这里是插槽,用于渲染传入的组件 -->
|
||||
@@ -29,6 +36,11 @@ export default {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '40%'
|
||||
},
|
||||
wrapperClosable: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<!--富文本基于-->
|
||||
<!--<el-link type="primary" href="https://www.wangeditor.com/v5/getting-started.html" target="_blank">wangEditor</el-link>-->
|
||||
<!--
|
||||
* 富文本编辑器
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<div ref="editor" style="border: 1px solid #ccc;">
|
||||
<Toolbar
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<!--
|
||||
* 截屏工具
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-20
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<div style="text-align: right">
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-camera"
|
||||
:loading="capturing"
|
||||
size="small"
|
||||
@click="capture"
|
||||
>
|
||||
截图并下载
|
||||
</el-button>
|
||||
</div>
|
||||
<div id="elementIdSelector">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import CsMessage from '@/utils/elementui/CsMessage'
|
||||
import { FormatDateTimeShortStr } from '@/utils/CsUtil'
|
||||
import html2canvas from 'html2canvas'
|
||||
|
||||
export default {
|
||||
name: 'CuteScreenShot',
|
||||
data() {
|
||||
return {
|
||||
capturing: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async capture() {
|
||||
this.capturing = true
|
||||
const element = document.querySelector('#elementIdSelector')
|
||||
if (!element) {
|
||||
CsMessage.Error('未找到匹配的元素,请检查选择器是否正确')
|
||||
this.capturing = false
|
||||
return
|
||||
}
|
||||
const canvas = await html2canvas(element)
|
||||
// const base64 = canvas.toDataURL('image/png')
|
||||
const link = document.createElement('a')
|
||||
link.download = `screenshot_${FormatDateTimeShortStr(new Date())}.png`
|
||||
link.href = canvas.toDataURL('image/png')
|
||||
link.click()
|
||||
this.capturing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<!--
|
||||
* 查询组件
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-20
|
||||
-->
|
||||
<template>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
:inline="true"
|
||||
:model.sync="model"
|
||||
label-position="right"
|
||||
label-width="90px"
|
||||
>
|
||||
<slot name="suffix" />
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onFormSubmit('searchForm')">查询</el-button>
|
||||
<el-button @click="onFormReset('searchForm')">重置</el-button>
|
||||
</el-form-item>
|
||||
<slot name="operation" />
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteSearchForm',
|
||||
components: {},
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onFormSubmit(formName) {
|
||||
const that = this
|
||||
that.$refs[formName].validate((valid) => {
|
||||
if (valid) {
|
||||
that.$emit('submit', that.model)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
onFormReset(formName) {
|
||||
try {
|
||||
this.$refs[formName].resetFields()
|
||||
this.$emit('reset')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<!--
|
||||
* 段落标题组件:占一行,支持嵌套按钮
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-13
|
||||
-->
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<h4>
|
||||
<i class="el-icon-s-help" style="color: #409EFF;font-size: 20px" />
|
||||
<i style="color: #303133;">{{ title }}</i>
|
||||
</h4>
|
||||
</el-col>
|
||||
<el-col :span="12" style="text-align: right;line-height: 64px;height: 64px;padding-right: 30px">
|
||||
<slot />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteSectionTitle',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '默认标题'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,3 +1,9 @@
|
||||
<!--
|
||||
* 简单表格组件:封装自定义逻辑与参数
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<el-table
|
||||
@@ -68,6 +74,11 @@ export default {
|
||||
total: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
customHeight: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -89,6 +100,9 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
height() {
|
||||
if (this.customHeight) {
|
||||
return this.customHeight
|
||||
}
|
||||
return document.documentElement.clientHeight - 300
|
||||
}
|
||||
},
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<!--
|
||||
* 纵向滚动条容器
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-20
|
||||
-->
|
||||
<template>
|
||||
<div class="overflow-panel">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CuteVerticalScrollBar'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.overflow-panel {
|
||||
height: 800px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
/* 滚动条宽度 */
|
||||
.overflow-panel::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
/* 滚动条轨道 */
|
||||
.overflow-panel::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
/* 滚动条滑块 */
|
||||
.overflow-panel::-webkit-scrollbar-thumb {
|
||||
background: #409EFF;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* 鼠标悬停在滑块上时 */
|
||||
.overflow-panel::-webkit-scrollbar-thumb:hover {
|
||||
background: #F56C6C;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<!--Yaml编辑器 基于-->
|
||||
<!--<a href="https://github.com/codemirror/CodeMirror" target="_blank">CodeMirror</a>,-->
|
||||
<!--主题预览地址 <a href="https://codemirror.net/demo/theme.html#idea" target="_blank">Theme</a>-->
|
||||
<!--</p>-->
|
||||
<!--
|
||||
* YAML编辑器
|
||||
* @author odboy
|
||||
* @email tianjun@odboy.cn
|
||||
* @created 2025-08-01
|
||||
-->
|
||||
<template>
|
||||
<textarea ref="textarea" />
|
||||
</template>
|
||||
|
||||
@@ -69,7 +69,8 @@ export default {
|
||||
dept: null,
|
||||
productLine: null,
|
||||
productLinePro: null
|
||||
}
|
||||
},
|
||||
searchFormModel: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -1,304 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-divider>演示:{{ dynamicTemplate.description ? dynamicTemplate.description: '动态流水线演示' }}
|
||||
<el-button
|
||||
v-prevent-re-click
|
||||
style="margin-left: 10px"
|
||||
type="primary"
|
||||
:loading="dynamicStartButtonLoading"
|
||||
:disabled="dynamicStartButtonLoading"
|
||||
@click="doStartPipeline"
|
||||
>{{ dynamicStartupStatusMap[dynamicStartupStatus].label }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-prevent-re-click
|
||||
:disabled="!dynamicRunning"
|
||||
style="margin-left: 10px"
|
||||
type="danger"
|
||||
@click="doStopPipeline"
|
||||
>停止
|
||||
</el-button>
|
||||
</el-divider>
|
||||
<div class="box-pipeline">
|
||||
<div v-if="dynamicInstance.nodes && dynamicInstance.nodes.length > 0" class="box-pipeline-content">
|
||||
<cute-pipeline-node
|
||||
v-for="(template, index) in dynamicInstance.nodes"
|
||||
:key="template.code"
|
||||
:template-data.sync="dynamicInstance.nodes[index]"
|
||||
:instance-data.sync="dynamicInstance"
|
||||
@node-click="onPipelineNodeClick"
|
||||
@node-retry="onPipelineNodeRetryClick"
|
||||
@node-opera="onPipelineNodeOperationClick"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="box-pipeline-content">
|
||||
<cute-pipeline-node
|
||||
v-for="(template, index) in dynamicTemplateList"
|
||||
:key="template.code"
|
||||
:template-data.sync="dynamicTemplateList[index]"
|
||||
:instance-data.sync="dynamicInstance"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<cute-preview-drawer ref="detailDrawer" :title="currentNodeData.name">
|
||||
<el-timeline>
|
||||
<el-timeline-item timestamp="2018/4/12" placement="top">
|
||||
<el-card>
|
||||
<h4>更新 Github 模板</h4>
|
||||
<p>王小虎 提交于 2018/4/12 20:46</p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item timestamp="2018/4/3" placement="top">
|
||||
<el-card>
|
||||
<h4>更新 Github 模板</h4>
|
||||
<p>王小虎 提交于 2018/4/3 20:46</p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item timestamp="2018/4/2" placement="top">
|
||||
<el-card>
|
||||
<h4>更新 Github 模板</h4>
|
||||
<p>王小虎 提交于 2018/4/2 20:46</p>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</cute-preview-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import CutePipelineNode from '@/views/components/dev/CutePipelineNode'
|
||||
import { getPipelineTemplate } from '@/api/pipeline/pipelineTemplate'
|
||||
import {
|
||||
queryLastPipelineDetailWs,
|
||||
restartPipeline,
|
||||
retryPipeline,
|
||||
startPipeline,
|
||||
stopPipeline
|
||||
} from '@/api/pipeline/pipelineInstance'
|
||||
import { CountArraysObjectByPropKey } from '@/utils/CsUtil'
|
||||
import CsMessage from '@/utils/elementui/CsMessage'
|
||||
import CsWsClient from '@/utils/CsWsClient'
|
||||
import { mapGetters } from 'vuex'
|
||||
import CutePreviewDrawer from '@/views/components/dev/CutePreviewDrawer.vue'
|
||||
|
||||
export default {
|
||||
name: 'CutePipelineNodeDemo',
|
||||
components: { CutePreviewDrawer, CutePipelineNode },
|
||||
data() {
|
||||
return {
|
||||
testAppInfo: {
|
||||
appName: 'cuteops',
|
||||
pipelineTemplateEnv: 'daily',
|
||||
iterationName: '流水线测试'
|
||||
},
|
||||
// ============== 动态流水线
|
||||
dynamicStartupStatusMap: {
|
||||
start: { label: '启动', code: 'start' },
|
||||
restart: { label: '重新启动', code: 'restart' }
|
||||
},
|
||||
// 按钮状态
|
||||
dynamicStartupStatus: 'start',
|
||||
dynamicStartButtonLoading: false,
|
||||
// 当前模板
|
||||
dynamicTemplate: {
|
||||
id: 21
|
||||
},
|
||||
// 当前流水线节点模板
|
||||
dynamicTemplateList: [],
|
||||
// dynamicHook: null,
|
||||
// 当前流水线实例
|
||||
dynamicInstance: {},
|
||||
// 当前ws客户端
|
||||
dynamicWsClient: null,
|
||||
dynamicRunning: false,
|
||||
// ============== 交互变量
|
||||
currentNodeData: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'user'
|
||||
])
|
||||
},
|
||||
mounted() {
|
||||
this.initPipelineTemplate(this.dynamicTemplate.id)
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 初始化流水线模板
|
||||
* @param templateId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async initPipelineTemplate(templateId) {
|
||||
const pipelineTemplate = await getPipelineTemplate(templateId)
|
||||
if (pipelineTemplate && pipelineTemplate.template) {
|
||||
try {
|
||||
this.dynamicTemplate = pipelineTemplate
|
||||
this.dynamicTemplateList = JSON.parse(pipelineTemplate.template)
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 连接WebSocket服务
|
||||
*/
|
||||
connectWebSocketServer(instanceId) {
|
||||
const that = this
|
||||
// {username}_{bizCode}_{contextParams}
|
||||
const sid = `${that.user.username}_FetchPipelineLastDetail_${instanceId}`
|
||||
if (that.dynamicWsClient) {
|
||||
try {
|
||||
that.dynamicWsClient.close()
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
that.dynamicWsClient = new CsWsClient(sid)
|
||||
that.dynamicWsClient.connect(that.handleWebSocketError, that.handleWebSocketMessage)
|
||||
setTimeout(() => {
|
||||
// 请求推送流水线数据
|
||||
queryLastPipelineDetailWs(sid)
|
||||
that.dynamicStartButtonLoading = false
|
||||
that.dynamicStartupStatus = that.dynamicStartupStatusMap.restart.code
|
||||
}, 1000)
|
||||
},
|
||||
handleWebSocketError(e) {
|
||||
console.error('连接WebSocket服务失败', e)
|
||||
},
|
||||
handleWebSocketMessage(e) {
|
||||
const that = this
|
||||
const data = e.data
|
||||
// console.log('来自服务器的数据:data', data)
|
||||
const callbackData = JSON.parse(data)
|
||||
that.dynamicRunning = true
|
||||
// console.log('callbackData', callbackData)
|
||||
try {
|
||||
that.dynamicInstance = JSON.parse(callbackData.data)
|
||||
// 判断流水线是否结束
|
||||
const successCount = CountArraysObjectByPropKey(that.dynamicInstance.nodes, 'status', 'success')
|
||||
if (that.dynamicTemplateList && that.dynamicTemplateList.length === successCount) {
|
||||
// 所有节点执行成功
|
||||
if (that.dynamicWsClient) {
|
||||
sessionStorage.removeItem('pipelineInstanceId')
|
||||
that.dynamicWsClient.close()
|
||||
that.dynamicRunning = false
|
||||
}
|
||||
that.dynamicStartupStatus = that.dynamicStartupStatusMap.start.code
|
||||
} else {
|
||||
const failCount = CountArraysObjectByPropKey(that.dynamicInstance.nodes, 'status', 'fail')
|
||||
// 存在失败的节点
|
||||
if (that.dynamicWsClient && failCount > 0) {
|
||||
that.dynamicWsClient.close()
|
||||
that.dynamicRunning = false
|
||||
}
|
||||
that.dynamicStartupStatus = that.dynamicStartupStatusMap.restart.code
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
that.dynamicRunning = false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 启动流水线
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async doStartPipeline() {
|
||||
const that = this
|
||||
try {
|
||||
that.dynamicStartButtonLoading = true
|
||||
// 这里的 dynamicStartupStatus状态,需要后期与上下文关联后,初始化的时候带入最近一个流水线实例
|
||||
const args = {
|
||||
templateId: that.dynamicTemplate.id,
|
||||
instanceName: that.testAppInfo.iterationName,
|
||||
contextName: that.testAppInfo.appName,
|
||||
env: that.testAppInfo.pipelineTemplateEnv
|
||||
}
|
||||
let result
|
||||
const pipelineInstanceId = sessionStorage.getItem('pipelineInstanceId')
|
||||
if (pipelineInstanceId) {
|
||||
result = await restartPipeline({
|
||||
...args,
|
||||
instanceId: pipelineInstanceId
|
||||
})
|
||||
CsMessage.Success('流水线重启成功')
|
||||
} else {
|
||||
result = await startPipeline(args)
|
||||
CsMessage.Success('流水线启动成功')
|
||||
}
|
||||
sessionStorage.setItem('pipelineInstanceId', result.instanceId)
|
||||
that.connectWebSocketServer(result.instanceId)
|
||||
} catch (e) {
|
||||
console.error('error', e)
|
||||
that.dynamicStartButtonLoading = false
|
||||
CsMessage.Error('流水线启动失败')
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 停止流水线
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async doStopPipeline() {
|
||||
const that = this
|
||||
const args = {
|
||||
templateId: that.dynamicTemplate.id,
|
||||
instanceName: that.testAppInfo.iterationName,
|
||||
contextName: that.testAppInfo.appName,
|
||||
env: that.testAppInfo.pipelineTemplateEnv
|
||||
}
|
||||
const pipelineInstanceId = sessionStorage.getItem('pipelineInstanceId')
|
||||
if (pipelineInstanceId) {
|
||||
await stopPipeline({
|
||||
...args,
|
||||
instanceId: pipelineInstanceId
|
||||
})
|
||||
CsMessage.Success('流水线停止成功')
|
||||
}
|
||||
},
|
||||
onPipelineNodeClick(data) {
|
||||
console.log('data', data)
|
||||
this.currentNodeData = data
|
||||
this.$refs.detailDrawer.show()
|
||||
},
|
||||
async onPipelineNodeRetryClick(data) {
|
||||
console.log('data', data)
|
||||
const result = await retryPipeline({
|
||||
instanceId: data.instanceId,
|
||||
code: data.code
|
||||
})
|
||||
sessionStorage.setItem('pipelineInstanceId', result.instanceId)
|
||||
this.connectWebSocketServer(data.instanceId)
|
||||
},
|
||||
async onPipelineNodeOperationClick(data, buttonInfo) {
|
||||
console.log('data', data, buttonInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.box-pipeline {
|
||||
width: 1500px; /* 固定宽度 */
|
||||
overflow-x: auto; /* 允许水平滚动 */
|
||||
white-space: nowrap; /* 防止内容换行,使所有内容在一行显示 */
|
||||
padding: 30px 20px 30px 20px;
|
||||
}
|
||||
|
||||
.box-pipeline::-webkit-scrollbar {
|
||||
height: 5px; /* 设置水平滚动条高度 */
|
||||
}
|
||||
|
||||
.box-pipeline::-webkit-scrollbar-track {
|
||||
background: #f1f1f1; /* 滚动条底色 */
|
||||
}
|
||||
|
||||
.box-pipeline::-webkit-scrollbar-thumb {
|
||||
background: #DCDFE6; /* 滚动条颜色 */
|
||||
}
|
||||
|
||||
.box-pipeline-content {
|
||||
display: inline-block; /* 使内容水平排列 */
|
||||
white-space: nowrap; /* 防止内容换行 */
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -50,8 +50,8 @@ export default {
|
||||
' shutdown: graceful\n' +
|
||||
'spring:\n' +
|
||||
' application:\n' +
|
||||
' name: kenaito-devops\n' +
|
||||
' title: Kenaito DevOps\n' +
|
||||
' name: cutejava\n' +
|
||||
' title: CuteJava\n' +
|
||||
' lifecycle:\n' +
|
||||
' # 采用java.time.Duration格式的值,如果在这个时间内,优雅停机没有停掉应用,超过这个时间就会强制停止应用\n' +
|
||||
' timeout-per-shutdown-phase: 30s\n' +
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<div class="login" :style="'background-image:url('+ Background +');'">
|
||||
<div class="login-box">
|
||||
<div class="login-form-box">
|
||||
<el-form
|
||||
ref="loginForm"
|
||||
:model="loginForm"
|
||||
@@ -8,9 +10,7 @@
|
||||
label-width="0px"
|
||||
class="login-form"
|
||||
>
|
||||
<h3 class="title">
|
||||
CuteJava 后台管理系统
|
||||
</h3>
|
||||
<h3 class="title">CuteJava 管理系统</h3>
|
||||
<el-form-item prop="username">
|
||||
<el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号">
|
||||
<svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />
|
||||
@@ -49,7 +49,7 @@
|
||||
:loading="loading"
|
||||
size="medium"
|
||||
type="primary"
|
||||
style="width:100%;"
|
||||
style="width:100%;background-color: #737bfe;border-color: #737bfe;"
|
||||
@click.native.prevent="handleLogin"
|
||||
>
|
||||
<span v-if="!loading">登 录</span>
|
||||
@@ -57,6 +57,7 @@
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<!-- 底部 -->
|
||||
<div v-if="$store.state.settings.showFooter" id="el-login-footer">
|
||||
<span v-html="$store.state.settings.footerTxt" />
|
||||
@@ -64,6 +65,7 @@
|
||||
<a href="https://beian.miit.gov.cn/#/Integrated/index" target="_blank">{{ $store.state.settings.caseNumber }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -72,7 +74,7 @@ import Config from '@/settings'
|
||||
import { getCodeImg } from '@/api/login'
|
||||
import Cookies from 'js-cookie'
|
||||
import qs from 'qs'
|
||||
import Background from '@/assets/images/background.jpeg'
|
||||
import Background from '@/assets/images/background.png'
|
||||
|
||||
export default {
|
||||
name: 'Login',
|
||||
@@ -205,12 +207,24 @@ export default {
|
||||
margin: 0 auto 30px auto;
|
||||
text-align: center;
|
||||
font-size: xx-large;
|
||||
color: white;
|
||||
color: #7179fe;
|
||||
}
|
||||
|
||||
.login-box {
|
||||
background: rgba(242, 246, 252, 0.3);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.login-form-box {
|
||||
width: 360px;
|
||||
height: 370px;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
background: rgba(255, 255, 255, 0.40);
|
||||
width: 400px;
|
||||
padding: 10% 25px 5px 25px;
|
||||
|
||||
.el-input {
|
||||
@@ -226,8 +240,6 @@ export default {
|
||||
width: 14px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
float: right;
|
||||
}
|
||||
|
||||
.login-tip {
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
@@ -98,7 +98,7 @@
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
:disabled="scope.row.id === 1"
|
||||
active-color="#409EFF"
|
||||
active-color="#67C23A"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled,)"
|
||||
/>
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
active-color="#409EFF"
|
||||
active-color="#67C23A"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled)"
|
||||
/>
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
@@ -146,7 +146,7 @@
|
||||
@selection-change="crud.selectionChangeHandler"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column :show-overflow-tooltip="true" label="菜单标题" width="125px" prop="title" />
|
||||
<el-table-column :show-overflow-tooltip="true" label="菜单标题" width="220px" prop="title" />
|
||||
<el-table-column prop="icon" label="图标" align="center" width="60px">
|
||||
<template slot-scope="scope">
|
||||
<svg-icon :icon-class="scope.row.icon ? scope.row.icon : ''" />
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button @click="crud.cancelCU">取消</el-button>
|
||||
<el-button v-if="crud.status.add" :loading="loading" type="primary" @click="upload">确认</el-button>
|
||||
<el-button v-else :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button @click="crud.cancelCU">取消</el-button>
|
||||
<el-button v-if="crud.status.add" :loading="loading" type="primary" @click="upload">确认</el-button>
|
||||
<el-button v-else :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
|
||||
@@ -149,7 +149,7 @@ import updatePass from './center/updatePass'
|
||||
import updateEmail from './center/updateEmail'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import store from '@/store'
|
||||
import { isvalidPhone } from '@/utils/CsValidateUtil'
|
||||
import { IsValidPhone } from '@/utils/CsValidateUtil'
|
||||
import crud from '@/mixins/crud'
|
||||
import { editUser } from '@/api/system/user'
|
||||
import Avatar from '@/assets/images/avatar.png'
|
||||
@@ -163,7 +163,7 @@ export default {
|
||||
const validPhone = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('请输入电话号码'))
|
||||
} else if (!isvalidPhone(value)) {
|
||||
} else if (!IsValidPhone(value)) {
|
||||
callback(new Error('请输入正确的11位手机号码'))
|
||||
} else {
|
||||
callback()
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
:before-close="crud.cancelCU"
|
||||
:visible.sync="crud.status.cu > 0"
|
||||
:title="crud.status.title"
|
||||
width="555px"
|
||||
width="640px"
|
||||
>
|
||||
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="66px">
|
||||
<el-form-item label="用户名" prop="username">
|
||||
@@ -119,7 +119,6 @@
|
||||
<el-select
|
||||
v-model="roleDatas"
|
||||
:disabled="form.id === user.id"
|
||||
style="width: 426px"
|
||||
multiple
|
||||
placeholder="请选择"
|
||||
@remove-tag="deleteTag"
|
||||
@@ -136,7 +135,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
@@ -164,7 +163,7 @@
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
:disabled="user.id === scope.row.id"
|
||||
active-color="#409EFF"
|
||||
active-color="#67C23A"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled)"
|
||||
/>
|
||||
@@ -196,7 +195,7 @@
|
||||
|
||||
<script>
|
||||
import crudUser from '@/api/system/user'
|
||||
import { isvalidPhone } from '@/utils/CsValidateUtil'
|
||||
import { IsValidPhone } from '@/utils/CsValidateUtil'
|
||||
import { queryDeptList, queryDeptSuperiorTree } from '@/api/system/dept'
|
||||
import { getLevel, queryRoleList } from '@/api/system/role'
|
||||
import { queryAllEnableJob } from '@/api/system/job'
|
||||
@@ -239,7 +238,7 @@ export default {
|
||||
const validPhone = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('请输入电话号码'))
|
||||
} else if (!isvalidPhone(value)) {
|
||||
} else if (!IsValidPhone(value)) {
|
||||
callback(new Error('请输入正确的11位手机号码'))
|
||||
} else {
|
||||
callback()
|
||||
|
||||
+1
-1
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.annotation;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
@@ -71,5 +72,4 @@ public @interface AnonymousDeleteMapping {
|
||||
* Alias for {@link RequestMapping#produces}.
|
||||
*/
|
||||
@AliasFor(annotation = RequestMapping.class) String[] produces() default {};
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.annotation;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
@@ -71,5 +72,4 @@ public @interface AnonymousGetMapping {
|
||||
* Alias for {@link RequestMapping#produces}.
|
||||
*/
|
||||
@AliasFor(annotation = RequestMapping.class) String[] produces() default {};
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.annotation;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
@@ -71,5 +72,4 @@ public @interface AnonymousPatchMapping {
|
||||
* Alias for {@link RequestMapping#produces}.
|
||||
*/
|
||||
@AliasFor(annotation = RequestMapping.class) String[] produces() default {};
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.annotation;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
@@ -71,5 +72,4 @@ public @interface AnonymousPostMapping {
|
||||
* Alias for {@link RequestMapping#produces}.
|
||||
*/
|
||||
@AliasFor(annotation = RequestMapping.class) String[] produces() default {};
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.annotation;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
@@ -71,5 +72,4 @@ public @interface AnonymousPutMapping {
|
||||
* Alias for {@link RequestMapping#produces}.
|
||||
*/
|
||||
@AliasFor(annotation = RequestMapping.class) String[] produces() default {};
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.base;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.base;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.base;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.base;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2021-2025 Odboy
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.base;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.springframework.data.annotation.CreatedBy;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Date;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class CsBaseUserCreateTimeTb extends CsObject {
|
||||
|
||||
@CreatedBy
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@ApiModelProperty(value = "创建人", hidden = true)
|
||||
private String createBy;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
@ApiModelProperty(value = "创建时间: yyyy-MM-dd HH:mm:ss", hidden = true)
|
||||
private Date createTime;
|
||||
|
||||
/* 分组校验 */
|
||||
public @interface Create {
|
||||
}
|
||||
|
||||
/* 分组校验 */
|
||||
public @interface Update {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder builder = new ToStringBuilder(this);
|
||||
Field[] fields = this.getClass().getDeclaredFields();
|
||||
try {
|
||||
for (Field f : fields) {
|
||||
f.setAccessible(true);
|
||||
builder.append(f.getName(), f.get(this)).append("\n");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
builder.append("toString builder encounter an error");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.base;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.base;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.base;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.base;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.base;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.base;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.constant;
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.constant;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -22,7 +23,11 @@ import lombok.Getter;
|
||||
@AllArgsConstructor
|
||||
public enum FileTypeEnum {
|
||||
/* 文件类型 */
|
||||
IMAGE("image", "图片"), DOC("doc", "文档"), VOICE("voice", "音频"), VIDEO("video", "视频"), OTHER("other", "其他");
|
||||
IMAGE("image", "图片"),
|
||||
DOC("doc", "文档"),
|
||||
VOICE("voice", "音频"),
|
||||
VIDEO("video", "视频"),
|
||||
OTHER("other", "其他");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.constant;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.constant;
|
||||
|
||||
/**
|
||||
|
||||
+1
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.context;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
+8
-2
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.context;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@@ -41,8 +42,13 @@ public class BootApplication {
|
||||
Environment env = application.getEnvironment();
|
||||
String ip = InetAddress.getLocalHost().getHostAddress();
|
||||
String port = env.getProperty("server.port");
|
||||
String path = StrUtil.isEmpty(env.getProperty("config.servlet.context-path")) ? "" : env.getProperty("config.servlet.context-path");
|
||||
String path = StrUtil.isEmpty(env.getProperty("config.servlet.context-path")) ?
|
||||
"" :
|
||||
env.getProperty("config.servlet.context-path");
|
||||
log.info(
|
||||
"\n----------------------------------------------------------\n\t" + "Application is running! Access URLs:\n\t" + "Local: \t\thttp://localhost:" + port + path + "/\n\t" + "External: \thttp://" + ip + ":" + port + path + "/\n\t" + "Swagger文档: \thttp://" + ip + ":" + port + path + "/doc.html\n" + "----------------------------------------------------------");
|
||||
"\n----------------------------------------------------------\n\t" +
|
||||
"Application is running! Access URLs:\n\t" + "Local: \t\thttp://localhost:" + port + path + "/\n\t" +
|
||||
"External: \thttp://" + ip + ":" + port + path + "/\n\t" + "Swagger文档: \thttp://" + ip + ":" + port +
|
||||
path + "/doc.html\n" + "----------------------------------------------------------");
|
||||
}
|
||||
}
|
||||
+17
@@ -1,3 +1,20 @@
|
||||
|
||||
/*
|
||||
* Copyright 2021-2025 Odboy
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.context;
|
||||
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
|
||||
+28
-63
@@ -13,86 +13,51 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.context;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.script.ScriptRuntimeException;
|
||||
import cn.odboy.framework.exception.BadRequestException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.script.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 脚本工具
|
||||
*
|
||||
* @author odboy
|
||||
* @date 2023-12-05
|
||||
* @date 2025-09-23
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class CsScriptHelper {
|
||||
private static final String FUNCTION_TAG = "function";
|
||||
private final ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
|
||||
|
||||
/**
|
||||
* @param script 函数表达式
|
||||
* @return 返回值
|
||||
*/
|
||||
public Object evalObj(String script, Object compareObj) {
|
||||
try {
|
||||
if (script.contains(FUNCTION_TAG)) {
|
||||
throw new BadRequestException("script格式异常");
|
||||
}
|
||||
// 构建一个不同的脚本上下文
|
||||
ScriptContext newContext = new SimpleScriptContext();
|
||||
if (compareObj != null) {
|
||||
Field[] fields = ReflectUtil.getFields(compareObj.getClass());
|
||||
Map<String, Object> filedNameValueMap = new HashMap<>(3);
|
||||
Arrays.stream(fields).parallel().forEach(f -> filedNameValueMap.put(f.getName(), ReflectUtil.getFieldValue(compareObj, f)));
|
||||
Set<Map.Entry<String, Object>> entries = filedNameValueMap.entrySet();
|
||||
long count = entries.stream().filter(f -> f.getValue() != null).count();
|
||||
if (count == 0) {
|
||||
throw new BadRequestException("比较对象中没有有效值");
|
||||
}
|
||||
Bindings engineScope = engine.createBindings();
|
||||
entries.forEach(f -> {
|
||||
// 在新的执行引擎作用域中添加与上面相同名称的变量
|
||||
engineScope.put(f.getKey(), f.getValue());
|
||||
});
|
||||
newContext.setBindings(engineScope, ScriptContext.ENGINE_SCOPE);
|
||||
}
|
||||
// 执行脚本, 注意这里指定新的执行作用域上下文
|
||||
return engine.eval(script, newContext);
|
||||
} catch (ScriptException e) {
|
||||
throw new ScriptRuntimeException(e);
|
||||
public Object evalObj(String script, Map<String, Object> args) {
|
||||
try (Context rhino = Context.enter()) {
|
||||
Scriptable scope = rhino.initStandardObjects();
|
||||
bindArgumentsToScope(scope, args);
|
||||
Object result = rhino.evaluateString(scope, script, "JavaScript", 1, null);
|
||||
log.info("执行结果: {}", Context.toString(result));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param script 函数表达式, 例如: function hello(name) { print('Hello, ' + name); }
|
||||
* @param functionName 函数名称, 例如: "hello"
|
||||
* @param args 函数参数, 例如: "小明"
|
||||
* @return 返回值
|
||||
*/
|
||||
public Object evalFunction(String script, String functionName, Object... args) {
|
||||
if (!script.contains(FUNCTION_TAG)) {
|
||||
throw new BadRequestException("script格式异常");
|
||||
private void bindArgumentsToScope(Scriptable scope, Map<?, ?> args) {
|
||||
if (args == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 执行脚本
|
||||
engine.eval(script);
|
||||
// 由ScriptEngines实现的接口, 其方法允许调用以前已执行的脚本中的过程。
|
||||
Invocable inv = (Invocable)engine;
|
||||
// 调用全局函数, 并传入参数
|
||||
return inv.invokeFunction(functionName, args);
|
||||
} catch (ScriptException e) {
|
||||
throw new ScriptRuntimeException(e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new ScriptRuntimeException(e);
|
||||
for (Map.Entry<?, ?> entry : args.entrySet()) {
|
||||
Object jsValue = Context.javaToJS(entry.getValue(), scope);
|
||||
scope.put(entry.getKey().toString(), scope, jsValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void bindArgumentsToScope(Scriptable scope, Object args) {
|
||||
if (args == null) {
|
||||
return;
|
||||
}
|
||||
// 单个对象参数
|
||||
Object jsValue = Context.javaToJS(args, scope);
|
||||
scope.put("args", scope, jsValue);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.context;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -109,7 +110,8 @@ public class CsSpringBeanHolder implements ApplicationContextAware, DisposableBe
|
||||
private static void assertContextInjected() {
|
||||
if (applicationContext == null) {
|
||||
throw new IllegalStateException(
|
||||
"applicaitonContext属性未注入, 请在applicationContext" + ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
|
||||
"applicaitonContext属性未注入, 请在applicationContext" +
|
||||
".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +131,8 @@ public class CsSpringBeanHolder implements ApplicationContextAware, DisposableBe
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
if (CsSpringBeanHolder.applicationContext != null) {
|
||||
log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + CsSpringBeanHolder.applicationContext);
|
||||
log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" +
|
||||
CsSpringBeanHolder.applicationContext);
|
||||
}
|
||||
CsSpringBeanHolder.applicationContext = applicationContext;
|
||||
if (addCallback) {
|
||||
|
||||
+16
-5
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.doc;
|
||||
|
||||
import cn.odboy.constant.SystemConst;
|
||||
@@ -59,14 +60,23 @@ public class SwaggerConfig {
|
||||
|
||||
@Bean
|
||||
public Docket createRestApi() {
|
||||
return new Docket(DocumentationType.SWAGGER_2).enable(properties.getSwagger().getEnabled()).pathMapping("/").apiInfo(apiInfo()).select()
|
||||
.paths(PathSelectors.regex("^(?!/error).*")).paths(PathSelectors.any()).build()
|
||||
return new Docket(DocumentationType.SWAGGER_2).enable(properties.getSwagger().getEnabled())
|
||||
.pathMapping("/")
|
||||
.apiInfo(apiInfo())
|
||||
.select()
|
||||
.paths(PathSelectors.regex("^(?!/error).*"))
|
||||
.paths(PathSelectors.any())
|
||||
.build()
|
||||
//添加登陆认证
|
||||
.securitySchemes(securitySchemes()).securityContexts(securityContexts());
|
||||
.securitySchemes(securitySchemes())
|
||||
.securityContexts(securityContexts());
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo() {
|
||||
return new ApiInfoBuilder().description("一个简单且易上手的 Spring boot 后台管理框架").title("CuteJava 接口文档").version("1.4.1").build();
|
||||
return new ApiInfoBuilder().description("一个简单且易上手的自动化运维平台")
|
||||
.title("CuteJava 接口文档")
|
||||
.version("1.4.1")
|
||||
.build();
|
||||
}
|
||||
|
||||
private List<SecurityScheme> securitySchemes() {
|
||||
@@ -121,7 +131,8 @@ public class SwaggerConfig {
|
||||
}
|
||||
|
||||
private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
|
||||
List<T> filteredMappings = mappings.stream().filter(mapping -> mapping.getPatternParser() == null).collect(Collectors.toList());
|
||||
List<T> filteredMappings =
|
||||
mappings.stream().filter(mapping -> mapping.getPatternParser() == null).collect(Collectors.toList());
|
||||
mappings.clear();
|
||||
mappings.addAll(filteredMappings);
|
||||
}
|
||||
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021-2025 Odboy
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.odboy.framework.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static org.springframework.http.HttpStatus.BAD_REQUEST;
|
||||
|
||||
/**
|
||||
* 统一异常处理
|
||||
*/
|
||||
@Getter
|
||||
public class BadRequestException extends RuntimeException {
|
||||
|
||||
private Integer status = BAD_REQUEST.value();
|
||||
|
||||
public BadRequestException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public BadRequestException(HttpStatus status, String msg) {
|
||||
super(msg);
|
||||
this.status = status.value();
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021-2025 Odboy
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.odboy.framework.exception.handler;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public final class ApiError {
|
||||
|
||||
private Integer status = 400;
|
||||
private Long timestamp;
|
||||
private String message;
|
||||
|
||||
private ApiError() {
|
||||
timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public static ApiError error(final String message) {
|
||||
ApiError apiError = new ApiError();
|
||||
apiError.setMessage(message);
|
||||
return apiError;
|
||||
}
|
||||
|
||||
public static ApiError error(final Integer status, final String message) {
|
||||
ApiError apiError = new ApiError();
|
||||
apiError.setStatus(status);
|
||||
apiError.setMessage(message);
|
||||
return apiError;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// /*
|
||||
// * Copyright 2021-2025 Odboy
|
||||
// *
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// */
|
||||
//
|
||||
// package cn.odboy.framework.exception.thirdapi;
|
||||
//
|
||||
// import java.lang.annotation.Documented;
|
||||
// import java.lang.annotation.ElementType;
|
||||
// import java.lang.annotation.Inherited;
|
||||
// import java.lang.annotation.Retention;
|
||||
// import java.lang.annotation.RetentionPolicy;
|
||||
// import java.lang.annotation.Target;
|
||||
//
|
||||
// /**
|
||||
// * 用于捕捉三方异常
|
||||
// *
|
||||
// * @author odboy
|
||||
// */
|
||||
// @Inherited
|
||||
// @Documented
|
||||
// @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
// @Retention(RetentionPolicy.RUNTIME)
|
||||
// public @interface ThirdApiExceptionCatch {
|
||||
// /**
|
||||
// * 模块名称
|
||||
// */
|
||||
// String modelName();
|
||||
//
|
||||
// /**
|
||||
// * 接口描述
|
||||
// */
|
||||
// String description();
|
||||
//
|
||||
// /**
|
||||
// * 是否抛出异常
|
||||
// */
|
||||
// boolean throwException() default true;
|
||||
// }
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
// /*
|
||||
// * Copyright 2021-2025 Odboy
|
||||
// *
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// */
|
||||
//
|
||||
// package cn.odboy.framework.exception.thirdapi;
|
||||
//
|
||||
// import cn.hutool.core.date.TimeInterval;
|
||||
// import cn.odboy.framework.exception.thirdapi.vo.KubernetesApiExceptionVo;
|
||||
// import cn.odboy.framework.exception.web.BadRequestException;
|
||||
// import cn.odboy.util.CsReturnValueHandleUtil;
|
||||
// import cn.odboy.util.LogFmtUtil;
|
||||
// import com.alibaba.fastjson2.JSON;
|
||||
// import com.aliyun.tea.TeaException;
|
||||
// import io.kubernetes.client.openapi.ApiException;
|
||||
// import lombok.extern.slf4j.Slf4j;
|
||||
// import org.aspectj.lang.ProceedingJoinPoint;
|
||||
// import org.aspectj.lang.annotation.Around;
|
||||
// import org.aspectj.lang.annotation.Aspect;
|
||||
// import org.gitlab4j.api.GitLabApiException;
|
||||
// import org.springframework.stereotype.Component;
|
||||
//
|
||||
// /**
|
||||
// * DingtalkApi调用异常
|
||||
// *
|
||||
// * @author odboy
|
||||
// * @date 2025-05-07
|
||||
// */
|
||||
// @Slf4j
|
||||
// @Aspect
|
||||
// @Component
|
||||
// public class ThirdExceptionCatchAspect {
|
||||
// @Around("@annotation(thirdApiExceptionCatch)")
|
||||
// public Object aroundDingtalkApiExceptionCatch(ProceedingJoinPoint joinPoint, ThirdApiExceptionCatch thirdApiExceptionCatch) throws Throwable {
|
||||
// return handleThirdApiException(joinPoint, thirdApiExceptionCatch);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 三方服务调用异常处理
|
||||
// */
|
||||
// private Object handleThirdApiException(ProceedingJoinPoint joinPoint, ThirdApiExceptionCatch annotation) {
|
||||
// if ("dingtalk".equals(annotation.modelName())) {
|
||||
// return handleDingtalkApiException(joinPoint, annotation);
|
||||
// }
|
||||
// if ("gitlab".equals(annotation.modelName())) {
|
||||
// return handleGitlabApiException(joinPoint, annotation);
|
||||
// }
|
||||
// if ("kubernetes".equals(annotation.modelName())) {
|
||||
// return handleKubernetesApiException(joinPoint, annotation);
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// private Object handleDingtalkApiException(ProceedingJoinPoint joinPoint, ThirdApiExceptionCatch annotation) {
|
||||
// TimeInterval timeInterval = new TimeInterval();
|
||||
// try {
|
||||
// Object result = joinPoint.proceed();
|
||||
// log.info("接口 [{}] 执行耗时: {} ms", annotation.description(), timeInterval.intervalMs());
|
||||
// return result;
|
||||
// } catch (TeaException teaException) {
|
||||
// if (!com.aliyun.teautil.Common.empty(teaException.code) && !com.aliyun.teautil.Common.empty(teaException.message)) {
|
||||
// String exceptionMessage = annotation.description() + "失败, code={}, message={}";
|
||||
// log.error(exceptionMessage, teaException.code, teaException.message, teaException);
|
||||
// if (annotation.throwException()) {
|
||||
// throw new BadRequestException(LogFmtUtil.format(exceptionMessage, teaException.code, teaException.message));
|
||||
// }
|
||||
// }
|
||||
// String exceptionMessage = annotation.description() + "失败";
|
||||
// log.error(exceptionMessage, teaException);
|
||||
// if (annotation.throwException()) {
|
||||
// throw new BadRequestException(exceptionMessage);
|
||||
// }
|
||||
// } catch (Throwable exception) {
|
||||
// TeaException err = new TeaException(exception.getMessage(), exception);
|
||||
// if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
|
||||
// String exceptionMessage = annotation.description() + "失败, code={}, message={}";
|
||||
// log.error(exceptionMessage, err.code, err.message, err);
|
||||
// if (annotation.throwException()) {
|
||||
// throw new BadRequestException(LogFmtUtil.format(exceptionMessage, err.code, err.message));
|
||||
// }
|
||||
// }
|
||||
// String exceptionMessage = annotation.description() + "失败";
|
||||
// log.error(exceptionMessage, exception);
|
||||
// if (annotation.throwException()) {
|
||||
// throw new BadRequestException(exceptionMessage);
|
||||
// }
|
||||
// }
|
||||
// return CsReturnValueHandleUtil.getDefaultValue(joinPoint);
|
||||
// }
|
||||
//
|
||||
// private Object handleGitlabApiException(ProceedingJoinPoint joinPoint, ThirdApiExceptionCatch annotation) {
|
||||
// TimeInterval timeInterval = new TimeInterval();
|
||||
// try {
|
||||
// Object result = joinPoint.proceed();
|
||||
// log.info("接口 [{}] 执行耗时: {} ms", annotation.description(), timeInterval.intervalMs());
|
||||
// return result;
|
||||
// } catch (GitLabApiException gitLabApiException) {
|
||||
// String exceptionMessage = annotation.description() + "失败,原因:" + gitLabApiException.getMessage();
|
||||
// log.error(exceptionMessage, gitLabApiException);
|
||||
// if (annotation.throwException()) {
|
||||
// throw new BadRequestException(exceptionMessage);
|
||||
// }
|
||||
// } catch (Throwable exception) {
|
||||
// String exceptionMessage = annotation.description() + "失败";
|
||||
// log.error(exceptionMessage, exception);
|
||||
// if (annotation.throwException()) {
|
||||
// throw new BadRequestException(exceptionMessage);
|
||||
// }
|
||||
// }
|
||||
// return CsReturnValueHandleUtil.getDefaultValue(joinPoint);
|
||||
// }
|
||||
//
|
||||
// private Object handleKubernetesApiException(ProceedingJoinPoint joinPoint, ThirdApiExceptionCatch annotation) {
|
||||
// TimeInterval timeInterval = new TimeInterval();
|
||||
// try {
|
||||
// Object result = joinPoint.proceed();
|
||||
// log.info("接口 [{}] 执行耗时: {} ms", annotation.description(), timeInterval.intervalMs());
|
||||
// return result;
|
||||
// } catch (ApiException e) {
|
||||
// String responseBody = e.getResponseBody();
|
||||
// log.error("接口 [{}] 执行异常,耗时: {} ms,异常信息: {}", annotation.description(), timeInterval.intervalMs(), responseBody, e);
|
||||
// if (annotation.throwException()) {
|
||||
// KubernetesApiExceptionVo actionExceptionBody = JSON.parseObject(responseBody, KubernetesApiExceptionVo.class);
|
||||
// if (actionExceptionBody != null) {
|
||||
// throw new BadRequestException(annotation.description() + "失败, 原因:" + actionExceptionBody.getReason());
|
||||
// }
|
||||
// throw new BadRequestException(annotation.description() + "失败");
|
||||
// }
|
||||
// } catch (Throwable e) {
|
||||
// log.error("接口 [{}] 执行异常,耗时: {} ms", annotation.description(), timeInterval.intervalMs(), e);
|
||||
// if (annotation.throwException()) {
|
||||
// throw new BadRequestException(annotation.description() + "失败");
|
||||
// }
|
||||
// }
|
||||
// return CsReturnValueHandleUtil.getDefaultValue(joinPoint);
|
||||
// }
|
||||
// }
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// /*
|
||||
// * Copyright 2021-2025 Odboy
|
||||
// *
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// */
|
||||
//
|
||||
// package cn.odboy.framework.exception.thirdapi.vo;
|
||||
//
|
||||
// import cn.odboy.base.CsObject;
|
||||
// import lombok.Data;
|
||||
// import lombok.EqualsAndHashCode;
|
||||
//
|
||||
// /**
|
||||
// * Kubernetes异常信息
|
||||
// *
|
||||
// * @author odboy
|
||||
// * @date 2025-08-03
|
||||
// */
|
||||
// @Data
|
||||
// @EqualsAndHashCode(callSuper = false)
|
||||
// public class KubernetesApiExceptionVo extends CsObject {
|
||||
// private String kind;
|
||||
// private String apiVersion;
|
||||
// private String status;
|
||||
// private String message;
|
||||
// private String reason;
|
||||
// private Details details;
|
||||
// private int code;
|
||||
//
|
||||
// @Data
|
||||
// public static class Details {
|
||||
// private String name;
|
||||
// private String kind;
|
||||
// }
|
||||
// }
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2021-2025 Odboy
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.exception.web;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static org.springframework.http.HttpStatus.BAD_REQUEST;
|
||||
|
||||
/**
|
||||
* 统一异常处理
|
||||
*/
|
||||
@Getter
|
||||
public class BadRequestException extends RuntimeException {
|
||||
|
||||
private Integer status = BAD_REQUEST.value();
|
||||
|
||||
public BadRequestException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public BadRequestException(HttpStatus status, String msg) {
|
||||
super(msg);
|
||||
this.status = status.value();
|
||||
}
|
||||
|
||||
public BadRequestException(Throwable cause) {
|
||||
// FIXME 20250827 修复完整异常链暴露给用户的问题
|
||||
super(cause.getMessage());
|
||||
this.status = BAD_REQUEST.value();
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2021-2025 Odboy
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.exception.web.handler;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public final class ApiError {
|
||||
|
||||
private Integer status = 400;
|
||||
private Long timestamp;
|
||||
private String message;
|
||||
|
||||
private ApiError() {
|
||||
timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public static ApiError error(final String message) {
|
||||
ApiError apiError = new ApiError();
|
||||
apiError.setMessage(message);
|
||||
return apiError;
|
||||
}
|
||||
|
||||
public static ApiError error(final Integer status, final String message) {
|
||||
ApiError apiError = new ApiError();
|
||||
apiError.setStatus(status);
|
||||
apiError.setMessage(message);
|
||||
return apiError;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -13,10 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.odboy.framework.exception.handler;
|
||||
|
||||
package cn.odboy.framework.exception.web.handler;
|
||||
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.odboy.framework.exception.BadRequestException;
|
||||
import cn.odboy.framework.exception.web.BadRequestException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2021-2025 Odboy
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.logging;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 统计执行时长
|
||||
*
|
||||
* @author odboy
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ExecutionTimeStatisticsFace {
|
||||
/**
|
||||
* 接口描述
|
||||
*/
|
||||
String description();
|
||||
|
||||
/**
|
||||
* 是否展示异常日志
|
||||
*/
|
||||
boolean errorLog() default false;
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2021-2025 Odboy
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.logging;
|
||||
|
||||
import cn.hutool.core.date.TimeInterval;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* DingtalkApi调用异常
|
||||
*
|
||||
* @author odboy
|
||||
* @date 2025-05-07
|
||||
*/
|
||||
@Slf4j
|
||||
@Aspect
|
||||
@Component
|
||||
public class ExecutionTimeStatisticsFaceAspect {
|
||||
@Around("@annotation(executionTimeStatisticsFace)")
|
||||
public Object aroundFace(ProceedingJoinPoint joinPoint, ExecutionTimeStatisticsFace executionTimeStatisticsFace) throws Throwable {
|
||||
TimeInterval timeInterval = new TimeInterval();
|
||||
try {
|
||||
Object proceed = joinPoint.proceed();
|
||||
log.info("[{}] 接口执行成功,耗时: {} ms", executionTimeStatisticsFace.description(), timeInterval.intervalMs());
|
||||
return proceed;
|
||||
} catch (Exception e) {
|
||||
if (executionTimeStatisticsFace.errorLog()) {
|
||||
log.info("[{}] 接口执行失败,耗时: {} ms", executionTimeStatisticsFace.description(), timeInterval.intervalMs());
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.monitor.controller;
|
||||
|
||||
import cn.odboy.annotation.AnonymousGetMapping;
|
||||
|
||||
+1
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.monitor.service;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
+25
-12
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.mybatisplus.config;
|
||||
|
||||
/**
|
||||
@@ -26,49 +27,61 @@ public enum CsMpQTypeEnum {
|
||||
/**
|
||||
* 大于等于
|
||||
*/
|
||||
, GREATER_THAN
|
||||
,
|
||||
GREATER_THAN
|
||||
/**
|
||||
* 小于等于
|
||||
*/
|
||||
, LESS_THAN
|
||||
,
|
||||
LESS_THAN
|
||||
/**
|
||||
* 中模糊查询
|
||||
*/
|
||||
, INNER_LIKE
|
||||
,
|
||||
INNER_LIKE
|
||||
/**
|
||||
* 左模糊查询
|
||||
*/
|
||||
, LEFT_LIKE
|
||||
,
|
||||
LEFT_LIKE
|
||||
/**
|
||||
* 右模糊查询
|
||||
*/
|
||||
, RIGHT_LIKE
|
||||
,
|
||||
RIGHT_LIKE
|
||||
/**
|
||||
* 小于
|
||||
*/
|
||||
, LESS_THAN_NQ
|
||||
,
|
||||
LESS_THAN_NQ
|
||||
/**
|
||||
* 包含
|
||||
*/
|
||||
, IN
|
||||
,
|
||||
IN
|
||||
/**
|
||||
* 不包含
|
||||
*/
|
||||
, NOT_IN
|
||||
,
|
||||
NOT_IN
|
||||
/**
|
||||
* 不等于
|
||||
*/
|
||||
, NOT_EQUAL
|
||||
,
|
||||
NOT_EQUAL
|
||||
/**
|
||||
* between(注:被注解的属性一定得是数组或者集合, 长度大于等于2)
|
||||
*/
|
||||
, BETWEEN
|
||||
,
|
||||
BETWEEN
|
||||
/**
|
||||
* 不为空
|
||||
*/
|
||||
, NOT_NULL
|
||||
,
|
||||
NOT_NULL
|
||||
/**
|
||||
* 为空
|
||||
*/
|
||||
, IS_NULL
|
||||
,
|
||||
IS_NULL
|
||||
}
|
||||
|
||||
+1
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.mybatisplus.config;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
|
||||
+10
-2
@@ -13,9 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.mybatisplus.config;
|
||||
|
||||
import cn.odboy.framework.exception.BadRequestException;
|
||||
import cn.odboy.framework.exception.web.BadRequestException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
@@ -93,7 +94,14 @@ public enum DataTypeEnum {
|
||||
private String aliasSuffix;
|
||||
private static final String JDBC_URL_PREFIX = "jdbc:";
|
||||
|
||||
DataTypeEnum(String feature, String desc, String driver, String keywordPrefix, String keywordSuffix, String aliasPrefix, String aliasSuffix) {
|
||||
DataTypeEnum(
|
||||
String feature,
|
||||
String desc,
|
||||
String driver,
|
||||
String keywordPrefix,
|
||||
String keywordSuffix,
|
||||
String aliasPrefix,
|
||||
String aliasSuffix) {
|
||||
this.feature = feature;
|
||||
this.desc = desc;
|
||||
this.driver = driver;
|
||||
|
||||
+2
-2
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.mybatisplus.config;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@@ -60,7 +61,6 @@ public class MpCustomP6SpyLogger implements MessageFormattingStrategy {
|
||||
sql = sql.replaceAll("\\s+", " ").trim();
|
||||
}
|
||||
// 格式化并加上颜色
|
||||
return String.format("CuteJava - %s%s%s %s[%s] %sREVIEW_SQL [Interval: %dms]%s -> %s;", RED, now, RESET, GREEN, Thread.currentThread().getName(), BLUE,
|
||||
elapsed, RESET, sql);
|
||||
return String.format("CuteJava - %s%s%s %s[%s] %sREVIEW_SQL [Interval: %dms]%s -> %s;", RED, now, RESET, GREEN, Thread.currentThread().getName(), BLUE, elapsed, RESET, sql);
|
||||
}
|
||||
}
|
||||
+1
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.mybatisplus.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
|
||||
+1
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.mybatisplus.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
|
||||
+3
-1
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.mybatisplus.config;
|
||||
|
||||
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
|
||||
@@ -61,7 +62,8 @@ public class RemoveDruidAdConfig {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
if (httpRequest.getRequestURI().endsWith("js/common.js")) {
|
||||
|
||||
+6
-1
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.mybatisplus.core;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
@@ -68,7 +69,11 @@ public class CsMpAnyQUtil {
|
||||
return baseMapper.selectPage(page, queryWrapper);
|
||||
}
|
||||
|
||||
public static <T, M> Page<M> selectPage(BaseMapper<T> baseMapper, Page<T> page, Object queryParams, Class<M> mapperClazz) {
|
||||
public static <T, M> Page<M> selectPage(
|
||||
BaseMapper<T> baseMapper,
|
||||
Page<T> page,
|
||||
Object queryParams,
|
||||
Class<M> mapperClazz) {
|
||||
QueryWrapper<T> queryWrapper = CsMpQUtil.build(queryParams);
|
||||
Page<T> tPage = baseMapper.selectPage(page, queryWrapper);
|
||||
Page<M> mapperTarget = new Page<>();
|
||||
|
||||
+43
-15
@@ -13,11 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.odboy.framework.mybatisplus.core;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.odboy.base.CsBaseUserTimeTb;
|
||||
import cn.odboy.framework.exception.BadRequestException;
|
||||
import cn.odboy.framework.exception.web.BadRequestException;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
|
||||
import com.baomidou.mybatisplus.generator.config.*;
|
||||
@@ -74,7 +75,8 @@ public class CsMpCmdGenUtil {
|
||||
this.parentModuleName = moduleName;
|
||||
// 1.数据库配置
|
||||
DataSourceConfig.Builder dataSourceConfigBuilder =
|
||||
new DataSourceConfig.Builder(this.databaseUrl, this.databaseUsername, this.databasePassword).driverClassName(this.driverClassName);
|
||||
new DataSourceConfig.Builder(this.databaseUrl, this.databaseUsername, this.databasePassword).driverClassName(
|
||||
this.driverClassName);
|
||||
dataSourceConfigBuilder.dbQuery(DB_QUERY);
|
||||
dataSourceConfigBuilder.keyWordsHandler(new MySqlKeyWordsHandler());
|
||||
// 1.1.快速生成器
|
||||
@@ -114,10 +116,13 @@ public class CsMpCmdGenUtil {
|
||||
fastAutoGenerator.strategyConfig(getEntityConfigConsumer());
|
||||
// 5.2.Controller策略配置
|
||||
// 开启生成@RestController控制器
|
||||
fastAutoGenerator.strategyConfig(strategyConfigBuilder -> strategyConfigBuilder.controllerBuilder().enableRestStyle());
|
||||
fastAutoGenerator.strategyConfig(strategyConfigBuilder -> strategyConfigBuilder.controllerBuilder()
|
||||
.enableRestStyle());
|
||||
// 如果不需要生成Controller, fastAutoGenerator.templateConfig(templateConfig -> templateConfig.controller(""))。5.3.Service策略配置:格式化service接口和实现类的文件名称, 去掉默认的ServiceName前面的I
|
||||
fastAutoGenerator.strategyConfig(
|
||||
strategyConfigBuilder -> strategyConfigBuilder.serviceBuilder().formatServiceFileName("%sService").formatServiceImplFileName("%sServiceImpl"));
|
||||
strategyConfigBuilder -> strategyConfigBuilder.serviceBuilder()
|
||||
.formatServiceFileName("%sService")
|
||||
.formatServiceImplFileName("%sServiceImpl"));
|
||||
// 5.4.Mapper策略配置
|
||||
// 格式化 mapper文件名,格式化xml实现类文件名称
|
||||
fastAutoGenerator.strategyConfig(getStrategyConfigConsumer());
|
||||
@@ -126,19 +131,29 @@ public class CsMpCmdGenUtil {
|
||||
}
|
||||
|
||||
private static Consumer<StrategyConfig.Builder> getEntityConfigConsumer() {
|
||||
return strategyConfigBuilder -> strategyConfigBuilder.entityBuilder().enableLombok()
|
||||
return strategyConfigBuilder -> strategyConfigBuilder.entityBuilder()
|
||||
.enableLombok()
|
||||
// 如果不需要生成注解, 去掉.enableTableFieldAnnotation()
|
||||
.enableTableFieldAnnotation().enableFileOverride().naming(NamingStrategy.underline_to_camel).columnNaming(NamingStrategy.underline_to_camel)
|
||||
.enableTableFieldAnnotation()
|
||||
.enableFileOverride()
|
||||
.naming(NamingStrategy.underline_to_camel)
|
||||
.columnNaming(NamingStrategy.underline_to_camel)
|
||||
.idType(IdType.AUTO);
|
||||
}
|
||||
|
||||
private static Consumer<StrategyConfig.Builder> getStrategyConfigConsumer(List<String> tableNames) {
|
||||
return strategyConfigBuilder -> strategyConfigBuilder.enableCapitalMode().enableSkipView().disableSqlFilter().addInclude(tableNames)
|
||||
return strategyConfigBuilder -> strategyConfigBuilder.enableCapitalMode()
|
||||
.enableSkipView()
|
||||
.disableSqlFilter()
|
||||
.addInclude(tableNames)
|
||||
// 实体类生成策略
|
||||
.entityBuilder().superClass(CsBaseUserTimeTb.class).addIgnoreColumns("create_time", "update_time", "create_by", "update_by")
|
||||
.entityBuilder()
|
||||
.superClass(CsBaseUserTimeTb.class)
|
||||
.addIgnoreColumns("create_time", "update_time", "create_by", "update_by")
|
||||
// 开启生成实体时生成字段注解。
|
||||
// 会在实体类的属性前,添加[@TableField("nickname")]
|
||||
.enableTableFieldAnnotation().disableSerialVersionUID()
|
||||
.enableTableFieldAnnotation()
|
||||
.disableSerialVersionUID()
|
||||
// 阶段2:Mapper策略配置
|
||||
.mapperBuilder()
|
||||
// 开启 @Mapper 注解。
|
||||
@@ -155,7 +170,8 @@ public class CsMpCmdGenUtil {
|
||||
// 会在控制类中加[@RestController]注解。
|
||||
.enableRestStyle()
|
||||
// 开启驼峰转连字符
|
||||
.enableHyphenStyle().build();
|
||||
.enableHyphenStyle()
|
||||
.build();
|
||||
}
|
||||
|
||||
private Consumer<PackageConfig.Builder> getPackageConfigConsumer() {
|
||||
@@ -163,20 +179,32 @@ public class CsMpCmdGenUtil {
|
||||
if (StrUtil.isBlank(this.parentModuleName)) {
|
||||
throw new BadRequestException("模块名称必填");
|
||||
}
|
||||
return packageConfigBuilder -> packageConfigBuilder.parent(this.parentPackageName).moduleName(this.parentModuleName).entity("dal.dataobject")
|
||||
.mapper("dal.mysql").service("service").serviceImpl("service.impl").controller("controller")
|
||||
return packageConfigBuilder -> packageConfigBuilder.parent(this.parentPackageName)
|
||||
.moduleName(this.parentModuleName)
|
||||
.entity("dal.dataobject")
|
||||
.mapper("dal.mysql")
|
||||
.service("service")
|
||||
.serviceImpl("service.impl")
|
||||
.controller("controller")
|
||||
.pathInfo(Collections.singletonMap(OutputFile.xml, mapperPath));
|
||||
}
|
||||
|
||||
private static Consumer<GlobalConfig.Builder> getGlobalConfigConsumer() {
|
||||
String outputDir = projectRootPath + "CodeGen";
|
||||
return globalConfigBuilder -> globalConfigBuilder.enableSwagger().outputDir(outputDir).author("codegen").commentDate("yyyy-MM-dd")
|
||||
return globalConfigBuilder -> globalConfigBuilder.enableSwagger()
|
||||
.outputDir(outputDir)
|
||||
.author("codegen")
|
||||
.commentDate("yyyy-MM-dd")
|
||||
.dateType(DateType.TIME_PACK);
|
||||
}
|
||||
|
||||
private static Consumer<StrategyConfig.Builder> getStrategyConfigConsumer() {
|
||||
return strategyConfigBuilder -> strategyConfigBuilder.mapperBuilder().mapperAnnotation(Mapper.class).formatMapperFileName("%sMapper")
|
||||
.formatXmlFileName("%sMapper").entityBuilder().formatFileName("%sTb");
|
||||
return strategyConfigBuilder -> strategyConfigBuilder.mapperBuilder()
|
||||
.mapperAnnotation(Mapper.class)
|
||||
.formatMapperFileName("%sMapper")
|
||||
.formatXmlFileName("%sMapper")
|
||||
.entityBuilder()
|
||||
.formatFileName("%sTb");
|
||||
}
|
||||
|
||||
private static Consumer<DataSourceConfig.Builder> getDataSourceConfigConsumer() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user