chore: sync Saber@3.4.0

master
ssc 2 years ago
parent 5f3f2344c0
commit cbc0e99036
  1. BIN
      .git.zip
  2. 9
      LICENSE
  3. 2
      package.json
  4. 9
      public/cdn/avue/2.10.16/avue.min.js
  5. 1
      public/cdn/avue/2.10.16/index.css
  6. 9
      public/cdn/avue/2.11.0/avue.min.js
  7. 1
      public/cdn/avue/2.11.0/index.css
  8. 6
      public/index.html
  9. 15
      src/api/desk/notice.js
  10. 78
      src/api/job/jobinfo.js
  11. 57
      src/api/job/jobserver.js
  12. 2
      src/lang/zh.js
  13. 514
      src/option/job/jobinfo.js
  14. 77
      src/option/job/jobserver.js
  15. 2
      src/page/index/top/index.vue
  16. 21
      src/router/axios.js
  17. 20
      src/util/crypto.js
  18. 23
      src/util/func.js
  19. 297
      src/views/job/jobinfo.vue
  20. 241
      src/views/job/jobserver.vue
  21. 19
      src/views/resource/oss.vue
  22. 76
      src/views/tool/datasource.vue
  23. 68
      src/views/wel/index.vue

Binary file not shown.

@ -5,9 +5,10 @@ BladeX系列产品知识产权归上海布雷德科技有限公司独立所有
二、 许可:
1. 在您完全接受并遵守本协议的基础上,本协议授予您使用BladeX的某些权利和非独占性许可。
2. 本协议中,将本产品使用用途分为“专业版用途”和“企业版用途”。
3. “专业版用途”定义:指个人在非团体机构中出于任何目的使用本产品(任何目的包括商业目的或非盈利目的)。
4. “企业版用途”定义:指团体机构(例如公司企业、政府、学校、军队、医院、社会团体等各类组织)(不包含集团,若集团使用则需为各个子公司分别购买企业授权)出于任何目的使用本产品(任何目的包括商业目的或非盈利目的)。
2. 本协议中,将本产品使用用途分为"专业版用途"和"企业版用途"。
3. "专业版用途"定义:指个人在非团体机构中出于任何合法目的使用本产品(任何目的包括商业目的或非盈利目的)。
4. "企业版用途"定义:指拥有合法执照的团体机构(例如公司企业、政府、学校、军队、医院、社会团体等各类组织)(不包含集团,若集团使用则需为各个子公司分别购买企业授权)出于任何合法目的使用本产品(任何目的包括商业目的或非盈利目的)。
5. 若您不能以拥有合法执照的团体机构名义购买企业版,则视为个人名义购买,仅可行使专业版用途。在遵守此协议的前提下,后续有一次机会将企业版授权免费绑定至法人为购买人的新公司,并从专业版用途转为企业版用途。
三、 约束和限制:
1. 本产品只能由您为本协议许可的目的而使用,您不得透露给任何第三方;
@ -20,7 +21,7 @@ BladeX系列产品知识产权归上海布雷德科技有限公司独立所有
您在使用本产品或服务时,不得将本产品产品或服务用于任何非法用途或本协议条款、条件和声明禁止的用途。
五、 免责说明:
1. 本产品按“现状”授予许可,您须自行承担使用本产品的风险。BladeX团队不对此提供任何明示、暗示或任何其它形式的担保和表示。在任何情况下,对于因使用或无法使用本软件而导致的任何损失(包括但不仅限于商业利润损失、业务中断或业务信息丢失),BladeX团队无需向您或任何第三方负责,即使BladeX团队已被告知可能会造成此类损失。在任何情况下, BladeX团队均不就任何直接的、间接的、附带的、后果性的、特别的、惩戒性的和处罚性的损害赔偿承担任何责任,无论该主张是基于保证、合同、侵权(包括疏忽)或是基于其他原因作出。
1. 本产品按"现状"授予许可,您须自行承担使用本产品的风险。BladeX团队不对此提供任何明示、暗示或任何其它形式的担保和表示。在任何情况下,对于因使用或无法使用本软件而导致的任何损失(包括但不仅限于商业利润损失、业务中断或业务信息丢失),BladeX团队无需向您或任何第三方负责,即使BladeX团队已被告知可能会造成此类损失。在任何情况下, BladeX团队均不就任何直接的、间接的、附带的、后果性的、特别的、惩戒性的和处罚性的损害赔偿承担任何责任,无论该主张是基于保证、合同、侵权(包括疏忽)或是基于其他原因作出。
2. 本产品可能内置有第三方服务,您应自行评估使用这些第三方服务的风险,由使用此类第三方服务而产生的纠纷,全部责任由您自行承担。
3. BladeX团队不对使用本产品构建的网站中任何信息内容以及导致的任何版权纠纷、法律争议和后果承担任何责任,全部责任由您自行承担。
4. BladeX团队可能会经常提供产品更新或升级,但BladeX团队没有为根据本协议许可的产品提供维护或更新的责任。

@ -1,6 +1,6 @@
{
"name": "saber-admin",
"version": "3.2.0",
"version": "3.4.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -15,11 +15,11 @@
<link rel="stylesheet" href="<%= BASE_URL %>cdn/iconfont/index.css">
<link rel="stylesheet" href="<%= BASE_URL %>cdn/iconfont/avue/iconfont.css">
<link rel="stylesheet" href="<%= BASE_URL %>cdn/iconfont/saber/iconfont.css">
<link rel="stylesheet" href="<%= BASE_URL %>cdn/avue/2.10.16/index.css">
<link rel="stylesheet" href="<%= BASE_URL %>cdn/avue/2.11.0/index.css">
<script src="<%= BASE_URL %>cdn/xlsx/FileSaver.min.js"></script>
<script src="<%= BASE_URL %>cdn/xlsx/xlsx.full.min.js"></script>
<link rel="icon" href="<%= BASE_URL %>favicon.png">
<title>Saber企业平台</title>
<title>Saber企业级开发平台</title>
<style>
html,
body,
@ -108,7 +108,7 @@
<script src="<%= BASE_URL %>cdn/vue-router/3.0.1/vue-router.min.js" charset="utf-8"></script>
<script src="<%= BASE_URL %>cdn/axios/1.0.0/axios.min.js" charset="utf-8"></script>
<script src="<%= BASE_URL %>cdn/element-ui/2.15.6/index.js" charset="utf-8"></script>
<script src="<%= BASE_URL %>cdn/avue/2.10.16/avue.min.js" charset="utf-8"></script>
<script src="<%= BASE_URL %>cdn/avue/2.11.0/avue.min.js" charset="utf-8"></script>
<script src="<%= BASE_URL %>cdn/nutflow/wf-design-base/index.umd.min.js" charset="utf-8"></script>
<script src="<%= BASE_URL %>cdn/avue-form-design/index.umd.min.js" charset="utf-8"></script>
<script src="<%= BASE_URL %>cdn/wf-design/index.umd.min.js" charset="utf-8"></script>

@ -9,7 +9,8 @@ export const getList = (current, size, params) => {
current,
size,
},
cryptoToken: true
cryptoToken: false,
cryptoData: false
})
}
@ -20,7 +21,8 @@ export const remove = (ids) => {
params: {
ids,
},
cryptoToken: true
cryptoToken: false,
cryptoData: false
})
}
@ -29,7 +31,8 @@ export const add = (row) => {
url: '/api/blade-desk/notice/submit',
method: 'post',
data: row,
cryptoToken: true
cryptoToken: false,
cryptoData: false
})
}
@ -38,7 +41,8 @@ export const update = (row) => {
url: '/api/blade-desk/notice/submit',
method: 'post',
data: row,
cryptoToken: true
cryptoToken: false,
cryptoData: false
})
}
@ -49,7 +53,8 @@ export const getNotice = (id) => {
params: {
id
},
cryptoToken: true
cryptoToken: false,
cryptoData: false
})
}

@ -0,0 +1,78 @@
import request from '@/router/axios';
export const getList = (current, size, params) => {
return request({
url: '/api/blade-job/job-info/list',
method: 'get',
params: {
...params,
current,
size,
},
});
};
export const getDetail = id => {
return request({
url: '/api/blade-job/job-info/detail',
method: 'get',
params: {
id,
},
});
};
export const remove = ids => {
return request({
url: '/api/blade-job/job-info/remove',
method: 'post',
params: {
ids,
},
});
};
export const add = row => {
return request({
url: '/api/blade-job/job-info/submit',
method: 'post',
data: row,
});
};
export const update = row => {
return request({
url: '/api/blade-job/job-info/submit',
method: 'post',
data: row,
});
};
export const change = row => {
return request({
url: '/api/blade-job/job-info/change',
method: 'post',
params: {
id: row.id,
enable: row.enable,
},
});
};
export const run = row => {
return request({
url: '/api/blade-job/job-info/run',
method: 'post',
params: {
id: row.id,
},
});
};
export const sync = row => {
return request({
url: '/api/blade-job/job-info/sync',
method: 'post',
data: row,
});
};

@ -0,0 +1,57 @@
import request from '@/router/axios';
export const getList = (current, size, params) => {
return request({
url: '/api/blade-job/job-server/list',
method: 'get',
params: {
...params,
current,
size,
},
});
};
export const getDetail = id => {
return request({
url: '/api/blade-job/job-server/detail',
method: 'get',
params: {
id,
},
});
};
export const remove = ids => {
return request({
url: '/api/blade-job/job-server/remove',
method: 'post',
params: {
ids,
},
});
};
export const add = row => {
return request({
url: '/api/blade-job/job-server/submit',
method: 'post',
data: row,
});
};
export const update = row => {
return request({
url: '/api/blade-job/job-server/submit',
method: 'post',
data: row,
});
};
export const sync = row => {
return request({
url: '/api/blade-job/job-server/sync',
method: 'post',
data: row,
});
};

@ -1,5 +1,5 @@
export default {
title: 'Saber企业管理平台',
title: 'Saber企业级开发平台',
logoutTip: '退出系统, 是否继续?',
submitText: '确定',
cancelText: '取消',

@ -0,0 +1,514 @@
export const timeExpressionTypeDic = [
{
label: 'API',
value: 1,
},
{
label: 'CRON',
value: 2,
},
{
label: '固定频率(毫秒)',
value: 3,
},
{
label: '固定延迟(毫秒)',
value: 4,
},
{
label: '工作流',
value: 5,
},
];
export const executeTypeDic = [
{
label: '单机执行',
value: 1,
},
{
label: '广播执行',
value: 2,
},
{
label: 'MapReduce',
value: 3,
},
];
export const processorTypeDic = [
{
label: '内建处理器',
value: 1,
},
{
label: '外部处理器(动态加载)',
value: 4,
},
];
export const dispatchStrategyDic = [
{
label: 'HEALTH_FIRST',
value: 1,
},
{
label: 'RANDOM',
value: 2,
},
];
export const enableDic = [
{
label: '暂停',
value: 0,
},
{
label: '启用',
value: 1,
},
];
export const logTypeDic = [
{
label: 'ONLINE',
value: 1,
},
{
label: 'LOCAL',
value: 2,
},
{
label: 'STDOUT',
value: 3,
},
{
label: 'LOCAL_AND_ONLINE',
value: 4,
},
{
label: 'NULL',
value: 999,
},
];
export const logLevelDic = [
{
label: 'DEBUG',
value: 1,
},
{
label: 'INFO',
value: 2,
},
{
label: 'WARN',
value: 3,
},
{
label: 'ERROR',
value: 4,
},
{
label: 'OFF',
value: 99,
},
];
export default {
height: 'auto',
calcHeight: 30,
tip: false,
searchShow: true,
searchMenuSpan: 6,
border: true,
index: true,
viewBtn: true,
selection: true,
labelWidth: 180,
menuWidth: 350,
dialogWidth: 1200,
dialogClickModal: false,
tabs: true,
column: [
{
label: '任务应用',
prop: 'jobServerId',
type: 'select',
dicUrl: '/api/blade-job/job-server/select',
props: {
label: 'jobAppName',
value: 'id',
},
search: true,
display: false,
},
{
label: '任务ID',
prop: 'jobId',
type: 'input',
search: true,
width: 80,
display: false,
},
{
label: '任务名称',
prop: 'jobName',
type: 'input',
search: true,
width: 200,
display: false,
},
{
label: '定时信息',
labelTip: '时间表达式类型',
prop: 'timeExpressionType',
type: 'select',
dicData: timeExpressionTypeDic,
rules: [
{
required: true,
message: '请选择定时信息',
trigger: 'blur',
},
],
width: 120,
display: false,
},
{
label: '执行类型',
labelTip: '枚举值',
prop: 'executeType',
type: 'select',
dicData: executeTypeDic,
rules: [
{
required: true,
message: '请选择执行器类型',
trigger: 'blur',
},
],
width: 110,
display: false,
},
{
label: '处理器类型',
labelTip: '枚举值',
prop: 'processorType',
type: 'select',
dicData: processorTypeDic,
rules: [
{
required: true,
message: '请选择处理器类型',
trigger: 'blur',
},
],
width: 180,
display: false,
},
{
label: '任务状态',
labelTip: '未启用的任务不会被调度',
prop: 'enable',
type: 'switch',
dicData: enableDic,
slot: true,
width: 100,
display: false,
},
],
group: [
{
label: '基础配置',
prop: 'modelSetting',
icon: 'el-icon-tickets',
column: [
{
label: '任务应用',
prop: 'jobServerId',
type: 'select',
dicUrl: '/api/blade-job/job-server/select',
props: {
label: 'jobAppName',
value: 'id',
},
editDisabled: true,
rules: [
{
required: true,
message: '请选择任务应用',
trigger: 'blur',
},
],
},
{
label: '任务状态',
labelTip: '未启用的任务不会被调度',
prop: 'enable',
type: 'switch',
value: 1,
rules: [
{
required: true,
message: '请选择是否开启',
trigger: 'blur',
},
],
},
{
label: '任务名称',
prop: 'jobName',
type: 'input',
rules: [
{
required: true,
message: '请输入任务名称',
trigger: 'blur',
},
],
},
{
label: '生命周期',
labelTip: '预留,用于指定定时调度任务的生效时间范围)',
prop: 'lifecycle',
type: 'datetimerange',
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
startPlaceholder: '任务开始时间',
endPlaceholder: '任务结束时间',
},
{
label: '定时类型',
labelTip: '时间表达式类型',
prop: 'timeExpressionType',
type: 'select',
dicData: timeExpressionTypeDic,
value: 2,
rules: [
{
required: true,
message: '请选择定时信息',
trigger: 'blur',
},
],
},
{
label: '时间表达式',
labelTip: '填写类型由 定时类型 决定,比如 CRON 需要填写 CRON 表达式',
prop: 'timeExpression',
type: 'input',
},
{
label: '执行类型',
labelTip: '枚举值',
prop: 'executeType',
type: 'select',
dicData: executeTypeDic,
value: 1,
rules: [
{
required: true,
message: '请选择执行器类型',
trigger: 'blur',
},
],
},
{
label: '运行时配置',
labelTip: '目前支持随机(RANDOM)和健康度优先(HEALTH_FIRST)',
prop: 'dispatchStrategy',
type: 'select',
dicData: dispatchStrategyDic,
value: 1,
rules: [
{
required: true,
message: '请选择运行时配置',
trigger: 'blur',
},
],
},
{
label: '处理器类型',
labelTip: '枚举值',
prop: 'processorType',
type: 'select',
dicData: processorTypeDic,
value: 1,
rules: [
{
required: true,
message: '请选择处理器类型',
trigger: 'blur',
},
],
},
{
label: '处理器参数',
labelTip:
'如Java处理器需要填写全限定类名,如:org.springblade.demo.MapReduceProcessorDemo',
prop: 'processorInfo',
type: 'input',
rules: [
{
required: true,
message: '请输入处理器参数',
trigger: 'blur',
},
],
},
{
label: '任务参数',
labelTip: '方法入参 TaskContext 对象的 json 字段',
prop: 'jobParams',
type: 'input',
span: 24,
},
{
label: '任务描述',
prop: 'jobDescription',
type: 'textarea',
minRows: 3,
span: 24,
},
],
},
{
label: '高级配置',
prop: 'templateSetting',
icon: 'el-icon-copy-document',
column: [
{
label: '最大实例数',
labelTip:
'该任务同时执行的数量(任务和实例就像是类和对象的关系,任务被调度执行后被称为实例)',
prop: 'maxInstanceNum',
type: 'number',
value: 0,
},
{
label: '单机线程并发数',
labelTip: '该实例执行过程中每个 Worker 使用的线程数量',
prop: 'concurrency',
type: 'number',
value: 5,
},
{
label: '任务实例运行时间限制',
labelTip: '0 代表无任何限制,超时会被打断并判定为执行失败',
prop: 'instanceTimeLimit',
type: 'number',
value: 0,
},
{
label: '任务实例重试次数',
labelTip: '任务实例重试次数,整个任务失败时重试,代价大,不推荐使用',
prop: 'instanceRetryNum',
type: 'number',
value: 0,
},
{
label: '任务作业重试次数',
labelTip: 'Task 重试次数,每个子 Task 失败后单独重试,代价小,推荐使用',
prop: 'taskRetryNum',
type: 'number',
value: 0,
},
{
label: '最低CPU核心',
labelTip:
'最小可用 CPU 核心数,CPU 可用核心数小于该值的 Worker 将不会执行该任务,0 代表无任何限制',
prop: 'minCpuCores',
type: 'number',
value: 0,
},
{
label: '最低内存(GB)',
labelTip: '可用内存小于该值的Worker 将不会执行该任务,0 代表无任何限制',
prop: 'minMemorySpace',
type: 'number',
value: 0,
},
{
label: '最低磁盘空间(GB)',
labelTip: '可用磁盘空间小于该值的Worker 将不会执行该任务,0 代表无任何限制',
prop: 'minDiskSpace',
type: 'number',
value: 0,
},
{
label: '执行机器地址',
labelTip: '设置该参数后只有列表中的机器允许执行该任务,空代表不指定机器',
prop: 'designatedWorkers',
type: 'input',
},
{
label: '最大执行机器数量',
labelTip: '限定调动执行的机器数量,0代表无限制',
prop: 'maxWorkerCount',
type: 'number',
value: 0,
},
],
},
{
label: '其他配置',
prop: 'codingSetting',
icon: 'el-icon-printer',
column: [
{
label: '报警人员ID列表',
labelTip: '接收报警的用户ID列表',
prop: 'notifyUserIds',
type: 'input',
},
{
label: '错误阈值',
labelTip: '错误阈值,0代表不限制',
prop: 'alertThreshold',
type: 'number',
value: 0,
},
{
label: '统计窗口(s)',
labelTip: '统计的窗口长度(s),0代表不限制',
prop: 'statisticWindowLen',
type: 'number',
value: 0,
},
{
label: '沉默窗口(s)',
labelTip: '沉默时间窗口(s),0代表不限制',
prop: 'silenceWindowLen',
type: 'number',
value: 0,
},
{
label: '日志配置',
labelTip: '日志配置',
prop: 'logType',
type: 'select',
value: 1,
dicData: logTypeDic,
},
{
label: '日志级别',
labelTip: '日志级别',
prop: 'logLevel',
type: 'select',
value: 2,
dicData: logLevelDic,
},
{
label: '扩展字段',
labelTip: '供开发者使用,用于功能扩展,powerjob 自身不会使用该字段',
prop: 'extra',
type: 'textarea',
minRows: 3,
span: 24,
},
],
},
],
};

@ -0,0 +1,77 @@
export default {
height: 'auto',
calcHeight: 30,
tip: false,
searchShow: true,
searchMenuSpan: 6,
border: true,
index: true,
viewBtn: true,
selection: true,
labelWidth: 100,
menuWidth: 350,
dialogClickModal: false,
column: [
{
label: '服务名称',
prop: 'jobServerName',
type: 'input',
span: 24,
search: true,
rules: [
{
required: true,
message: '请输入服务名称',
trigger: 'blur',
},
],
},
{
label: '服务器地址',
prop: 'jobServerUrl',
type: 'input',
span: 24,
rules: [
{
required: true,
message: '请输入服务器地址',
trigger: 'blur',
},
],
},
{
label: '应用名称',
prop: 'jobAppName',
type: 'input',
span: 24,
search: true,
rules: [
{
required: true,
message: '请输入应用名称',
trigger: 'blur',
},
],
},
{
label: '应用密码',
prop: 'jobAppPassword',
type: 'input',
span: 24,
rules: [
{
required: true,
message: '请输入应用密码',
trigger: 'blur',
},
],
},
{
label: '任务备注',
prop: 'jobRemark',
type: 'textarea',
minRows: 3,
span: 24,
},
],
};

@ -78,7 +78,7 @@
:src="userInfo.avatar">
<el-dropdown>
<span class="el-dropdown-link">
{{userInfo.userName}}
{{userInfo.real_name}}
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">

@ -49,12 +49,25 @@ axios.interceptors.request.use(config => {
const isToken = meta.isToken === false;
//headers传递token是否加密
const cryptoToken = config.cryptoToken === true;
//判断传递数据是否加密
const cryptoData = config.cryptoData === true;
const token = getToken();
if (token && !isToken) {
config.headers[website.tokenHeader] = cryptoToken
? 'crypto ' + crypto.encrypt(token)
? 'crypto ' + crypto.encryptAES(token, crypto.cryptoKey)
: 'bearer ' + token;
}
// 开启报文加密
if (cryptoData) {
if (config.params) {
const data = crypto.encryptAES(JSON.stringify(config.params), crypto.aesKey);
config.params = { data };
}
if (config.data) {
config.text = true;
config.data = crypto.encryptAES(JSON.stringify(config.data), crypto.aesKey);
}
}
//headers中配置text请求
if (config.text === true) {
config.headers["Content-Type"] = "text/plain";
@ -75,6 +88,8 @@ axios.interceptors.response.use(res => {
const status = res.data.code || res.status;
const statusWhiteList = website.statusWhiteList || [];
const message = res.data.msg || res.data.error_description || '未知错误';
const config = res.config;
const cryptoData = config.cryptoData === true;
//如果在白名单里则自行catch逻辑处理
if (statusWhiteList.includes(status)) return Promise.reject(res);
//如果是401则跳转到登录页面
@ -87,6 +102,10 @@ axios.interceptors.response.use(res => {
});
return Promise.reject(new Error(message))
}
// 解析加密报文
if (cryptoData) {
res.data = JSON.parse(crypto.decryptAES(res.data, crypto.aesKey));
}
return res;
}, error => {
NProgress.done();

@ -1,11 +1,21 @@
import CryptoJS from 'crypto-js'
export default class crypto {
// 使用AesUtil.genAesKey()生成,需和后端配置保持一致
static aesKey = "O2BEeIv399qHQNhD6aGW8R8DEj4bqHXm";
// 使用DesUtil.genDesKey()生成,需和后端配置保持一致
static desKey = "jMVCBsFGDQr1USHo";
/**
* token加密key 使用@org.springblade.test.CryptoKeyGenerator获取,需和后端配置保持一致
* @type {string}
*/
static cryptoKey = '请配置cryptoKey';
/**
* 报文加密key 使用@org.springblade.test.CryptoKeyGenerator获取,需和后端配置保持一致
* @type {string}
*/
static aesKey = '请配置aesKey';
/**
* 报文加密key 使用@org.springblade.test.CryptoKeyGenerator获取,需和后端配置保持一致
* @type {string}
*/
static desKey = '请配置desKey';
/**
* aes 加密方法

@ -102,4 +102,27 @@ export default class func {
}
return str;
}
/**
* 判断是否为数组
* @param param
* @returns {boolean}
*/
static isArrayAndNotEmpty(param) {
return Array.isArray(param) && param.length > 0;
}
/**
* 格式化URL
* @param url
* @returns {*|string}
*/
static formatUrl(url) {
if (!url) return url;
if (url.startsWith('http://') || url.startsWith('https://')) {
return url;
} else {
return `http://${url}`;
}
}
}

@ -0,0 +1,297 @@
<template>
<basic-container>
<avue-crud :option="option"
:table-loading="loading"
:data="data"
:page.sync="page"
:search.sync="search"
ref="crud"
v-model="form"
:permission="permissionList"
@row-update="rowUpdate"
@row-save="rowSave"
@row-del="rowDel"
@search-change="searchChange"
@search-reset="searchReset"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@refresh-change="refreshChange"
@on-load="onLoad"
>
<template slot="menuLeft">
<el-button
type="danger"
size="small"
icon="el-icon-delete"
plain
v-if="permission.jobinfo_delete"
@click="handleDelete"
>
</el-button>
<el-button
type="info"
size="small"
plain
icon="el-icon-sort"
@click="handleSync"
>
</el-button>
</template>
<template slot-scope="{row}" slot="menu">
<el-button type="text" size="small" icon="el-icon-video-play" @click="handleRun(scope.row)"
>
</el-button>
</template>
<template slot-scope="{row}" slot="enable">
<el-switch
v-model="row.enable"
inline-prompt
@change="slotChange(row)"
active-text=""
inactive-text=""
:active-value="1"
:inactive-value="0"
/>
</template>
</avue-crud>
</basic-container>
</template>
<script>
import { getList, getDetail, add, update, remove, change, run, sync } from '@/api/job/jobinfo';
import option from '@/option/job/jobinfo';
import { mapGetters } from 'vuex';
import 'nprogress/nprogress.css';
import func from '@/util/func';
export default {
data() {
return {
form: {},
query: {},
search: {},
loading: true,
page: {
pageSize: 10,
currentPage: 1,
total: 0,
},
selectionList: [],
option: option,
data: [],
};
},
computed: {
...mapGetters(['permission']),
permissionList() {
return {
addBtn: this.vaildData(this.permission.jobinfo_add, false),
viewBtn: this.vaildData(this.permission.jobinfo_view, false),
delBtn: this.vaildData(this.permission.jobinfo_delete, false),
editBtn: this.vaildData(this.permission.jobinfo_edit, false),
};
},
ids() {
let ids = [];
this.selectionList.forEach(ele => {
ids.push(ele.id);
});
return ids.join(',');
},
},
methods: {
rowSave(row, done, loading) {
if (func.isArrayAndNotEmpty(row.lifecycle)) {
const lifecycleStart = row.lifecycle[0];
const lifecycleEnd = row.lifecycle[1];
if (!func.isUndefined(lifecycleStart) && !func.isUndefined(lifecycleEnd)) {
row.lifecycle = lifecycleStart + ',' + lifecycleEnd;
}
} else {
row.lifecycle = '';
}
add(row).then(
() => {
this.onLoad(this.page);
this.$message({
type: 'success',
message: '操作成功!',
});
done();
},
error => {
loading();
window.console.log(error);
}
);
},
rowUpdate(row, index, done, loading) {
if (func.isArrayAndNotEmpty(row.lifecycle)) {
const lifecycleStart = row.lifecycle[0];
const lifecycleEnd = row.lifecycle[1];
if (!func.isUndefined(lifecycleStart) && !func.isUndefined(lifecycleEnd)) {
row.lifecycle = lifecycleStart + ',' + lifecycleEnd;
}
}
update(row).then(
() => {
this.onLoad(this.page);
this.$message({
type: 'success',
message: '操作成功!',
});
done();
},
error => {
loading();
console.log(error);
}
);
},
rowDel(row) {
this.$confirm('确定将选择数据删除?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
return remove(row.id);
})
.then(() => {
this.onLoad(this.page);
this.$message({
type: 'success',
message: '操作成功!',
});
});
},
handleDelete() {
if (this.selectionList.length === 0) {
this.$message.warning('请选择至少一条数据');
return;
}
this.$confirm('确定将选择数据删除?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
return remove(this.ids);
})
.then(() => {
this.onLoad(this.page);
this.$message({
type: 'success',
message: '操作成功!',
});
this.$refs.crud.toggleSelection();
});
},
handleSync() {
this.$confirm('确定进行数据双向同步?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
return sync();
})
.then(() => {
this.onLoad(this.page);
this.$message({
type: 'success',
message: '操作成功!',
});
});
},
handleRun(row) {
this.$confirm('运行后将创建一个实例执行,是否继续?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
return run(row);
})
.then(() => {
this.$message({
type: 'success',
message: '运行成功!',
});
});
},
slotChange(row) {
if (!row.id) {
return;
}
change(row).then(
() => {
this.onLoad(this.page);
this.$message({
type: 'success',
message: '操作成功!',
});
},
error => {
window.console.log(error);
}
);
},
beforeOpen(done, type) {
if (['edit', 'view'].includes(type)) {
getDetail(this.form.id).then(res => {
this.form = res.data.data;
});
}
done();
},
searchReset() {
this.query = {};
this.onLoad(this.page);
},
searchChange(params, done) {
this.query = params;
this.page.currentPage = 1;
this.onLoad(this.page, params);
done();
},
selectionChange(list) {
this.selectionList = list;
},
selectionClear() {
this.selectionList = [];
this.$refs.crud.toggleSelection();
},
currentChange(currentPage) {
this.page.currentPage = currentPage;
},
sizeChange(pageSize) {
this.page.pageSize = pageSize;
},
refreshChange() {
this.onLoad(this.page, this.query);
},
onLoad(page) {
this.loading = true;
const { jobServerId, jobName } = this.query;
let values = {
jobServerId_like: jobServerId,
jobName_like: jobName,
};
getList(page.currentPage, page.pageSize, values).then(res => {
const data = res.data.data;
this.page.total = data.total;
this.data = data.records;
this.loading = false;
this.selectionClear();
});
},
},
};
</script>
<style></style>

@ -0,0 +1,241 @@
<template>
<basic-container>
<avue-crud :option="option"
:table-loading="loading"
:data="data"
:page.sync="page"
:search.sync="search"
ref="crud"
v-model="form"
:permission="permissionList"
@row-update="rowUpdate"
@row-save="rowSave"
@row-del="rowDel"
:before-open="beforeOpen"
@search-change="searchChange"
@search-reset="searchReset"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@refresh-change="refreshChange"
@on-load="onLoad"
>
<template slot="menuLeft">
<el-button
type="danger"
size="small"
icon="el-icon-delete"
plain
v-if="permission.jobserver_delete"
@click="handleDelete"
>
</el-button>
<el-button
type="info"
size="small"
plain
icon="el-icon-sort"
@click="handleSync"
>
</el-button>
</template>
<template slot-scope="{row}" slot="menu">
<el-button type="text" size="small" icon="el-icon-video-play" @click="handleLink(row)"
>服务跳转
</el-button>
</template>
</avue-crud>
</basic-container>
</template>
<script>
import { getList, getDetail, add, update, remove, sync } from '@/api/job/jobserver';
import option from '@/option/job/jobserver';
import { mapGetters } from 'vuex';
import 'nprogress/nprogress.css';
import func from '@/util/func';
export default {
data() {
return {
form: {},
query: {},
search: {},
loading: true,
page: {
pageSize: 10,
currentPage: 1,
total: 0,
},
selectionList: [],
option: option,
data: [],
};
},
computed: {
...mapGetters(['permission']),
permissionList() {
return {
addBtn: this.vaildData(this.permission.jobserver_add, false),
viewBtn: this.vaildData(this.permission.jobserver_view, false),
delBtn: this.vaildData(this.permission.jobserver_delete, false),
editBtn: this.vaildData(this.permission.jobserver_edit, false),
};
},
ids() {
let ids = [];
this.selectionList.forEach(ele => {
ids.push(ele.id);
});
return ids.join(',');
},
},
methods: {
rowSave(row, done, loading) {
add(row).then(
() => {
this.onLoad(this.page);
this.$message({
type: 'success',
message: '操作成功!',
});
done();
},
error => {
loading();
window.console.log(error);
}
);
},
rowUpdate(row, index, done, loading) {
update(row).then(
() => {
this.onLoad(this.page);
this.$message({
type: 'success',
message: '操作成功!',
});
done();
},
error => {
loading();
console.log(error);
}
);
},
rowDel(row) {
this.$confirm('确定将选择数据删除?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
return remove(row.id);
})
.then(() => {
this.onLoad(this.page);
this.$message({
type: 'success',
message: '操作成功!',
});
});
},
handleDelete() {
if (this.selectionList.length === 0) {
this.$message.warning('请选择至少一条数据');
return;
}
this.$confirm('确定将选择数据删除?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
return remove(this.ids);
})
.then(() => {
this.onLoad(this.page);
this.$message({
type: 'success',
message: '操作成功!',
});
this.$refs.crud.toggleSelection();
});
},
handleLink(row) {
window.open(func.formatUrl(row.jobServerUrl));
},
handleSync() {
this.$confirm('确定进行数据双向同步?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
return sync();
})
.then(() => {
this.onLoad(this.page);
this.$message({
type: 'success',
message: '操作成功!',
});
});
},
beforeOpen(done, type) {
if (['edit', 'view'].includes(type)) {
getDetail(this.form.id).then(res => {
this.form = res.data.data;
});
}
done();
},
searchReset() {
this.query = {};
this.onLoad(this.page);
},
searchChange(params, done) {
this.query = params;
this.page.currentPage = 1;
this.onLoad(this.page, params);
done();
},
selectionChange(list) {
this.selectionList = list;
},
selectionClear() {
this.selectionList = [];
this.$refs.crud.toggleSelection();
},
currentChange(currentPage) {
this.page.currentPage = currentPage;
},
sizeChange(pageSize) {
this.page.pageSize = pageSize;
},
refreshChange() {
this.onLoad(this.page, this.query);
},
onLoad(page) {
this.loading = true;
const { jobServerName, jobAppName } = this.query;
let values = {
jobServerName_like: jobServerName,
jobAppName_like: jobAppName,
};
getList(page.currentPage, page.pageSize, values).then(res => {
const data = res.data.data;
this.page.total = data.total;
this.data = data.records;
this.loading = false;
this.selectionClear();
});
},
},
};
</script>
<style></style>

@ -129,13 +129,22 @@
},
{
label: "资源地址",
labelTip: "对象存储通用资源地址,可以是内网也可以是外网",
prop: "endpoint",
span: 24,
rules: [{
required: true,
message: "请输入资源地址",
trigger: "blur"
}]
rules: [
{
required: true,
message: "请输入资源地址",
trigger: "blur",
},
],
},
{
label: "外网地址",
labelTip: "资源地址设置为内网上传,则外部访问需要配置外网映射地址",
prop: "transformEndpoint",
span: 24,
},
{
label: "空间名",

@ -34,6 +34,7 @@
<script>
import {getList, getDetail, add, update, remove} from "@/api/tool/datasource";
import {mapGetters} from "vuex";
import func from "@/util/func";
export default {
data() {
@ -60,20 +61,63 @@
selection: true,
dialogClickModal: false,
column: [
{
label: '数据类型',
type: 'radio',
value: 1,
span: 24,
width: 120,
searchLabelWidth: 100,
row: true,
dicUrl: '/blade-system/dict/dictionary?code=datasource_category',
props: {
label: 'dictValue',
value: 'dictKey',
},
dataType: 'number',
prop: 'category',
search: true,
rules: [
{
required: true,
message: '请选择分类',
trigger: 'blur',
},
],
},
{
label: "名称",
prop: "name",
width: 120,
span: 24,
search: true,
rules: [{
required: true,
message: "请输入数据源名称",
trigger: "blur"
}]
},
{
label: 'YAML',
prop: 'shardingConfig',
span: 24,
minRows: 5,
hide: false,
display: false,
type: 'textarea',
rules: [
{
required: true,
message: '请输入YAML配置',
trigger: 'blur',
},
],
},
{
label: "驱动类",
prop: "driverClass",
type: 'select',
span: 24,
dicData: [
{
label: 'com.mysql.cj.jdbc.Driver',
@ -84,15 +128,10 @@
}, {
label: 'oracle.jdbc.OracleDriver',
value: 'oracle.jdbc.OracleDriver',
}, {
label: 'com.microsoft.sqlserver.jdbc.SQLServerDriver',
value: 'com.microsoft.sqlserver.jdbc.SQLServerDriver',
}, {
label: 'dm.jdbc.driver.DmDriver',
value: 'dm.jdbc.driver.DmDriver',
}
],
width: 200,
display: true,
rules: [{
required: true,
message: "请输入驱动类",
@ -103,6 +142,7 @@
label: "用户名",
prop: "username",
width: 120,
display: true,
rules: [{
required: true,
message: "请输入用户名",
@ -113,6 +153,7 @@
label: "密码",
prop: "password",
hide: true,
display: true,
rules: [{
required: true,
message: "请输入密码",
@ -123,6 +164,7 @@
label: "连接地址",
prop: "url",
span: 24,
display: true,
rules: [{
required: true,
message: "请输入连接地址",
@ -142,6 +184,28 @@
data: []
};
},
watch: {
'form.category'() {
const category = func.toInt(this.form.category);
this.$refs.crud.option.column.filter(item => {
if (item.prop === 'driverClass') {
item.display = category === 1;
}
if (item.prop === 'url') {
item.display = category === 1;
}
if (item.prop === 'username') {
item.display = category === 1;
}
if (item.prop === 'password') {
item.display = category === 1;
}
if (item.prop === 'shardingConfig') {
item.display = category === 2;
}
});
},
},
computed: {
...mapGetters(["permission"]),
permissionList() {

@ -9,7 +9,7 @@
<el-col :span="24">
<basic-container>
<p style="text-align: center">
<img src="https://img.shields.io/badge/Release-V3.2.0-green.svg" alt="Downloads"/>
<img src="https://img.shields.io/badge/Release-V3.4.0-green.svg" alt="Downloads"/>
<img src="https://img.shields.io/badge/JDK-1.8+-green.svg" alt="Build Status"/>
<img src="https://img.shields.io/badge/Spring%20Cloud-2021-blue.svg" alt="Coverage Status"/>
<img src="https://img.shields.io/badge/Spring%20Boot-2.7-blue.svg" alt="Downloads"/>
@ -139,10 +139,66 @@
<el-row>
<basic-container>
<el-collapse v-model="logActiveNames" @change="handleChange">
<el-collapse-item
title="3.2.0.RELEASE发布,新增规则引擎、token加密、skywalking集成"
name="31"
>
<el-collapse-item title="3.4.0.RELEASE发布,新增分布式任务调度客户端" name="34">
<div>1.[升级]Jackson 2.16.1</div>
<div>2.[升级]Mybatis 3.5.15</div>
<div>3.[升级]MybatisSpring 2.1.2</div>
<div>4.[升级]MybatisPlus 3.5.5</div>
<div>5.[升级]MybatisPlusGenerator 3.5.5</div>
<div>6.[升级]MinioClient 8.5.7</div>
<div>7.[新增]分布式任务PowJob集成</div>
<div>8.[新增]分布式任务客户端开发</div>
<div>9.[优化]Saber3加载动画</div>
<div>10.[修复]代码生成主子表路径引用问题</div>
</el-collapse-item>
<el-collapse-item title="3.3.1.RELEASE发布,优化Token加密、独立分库分表配置" name="33">
<div>1.[升级]Nacos 2.3.0</div>
<div>2.[升级]FastJson 2.0.43</div>
<div>3.[升级]EasyExcel 3.3.3</div>
<div>4.[升级]JustAuth 1.16.6</div>
<div>5.[升级]MySql驱动 mysql-connector-java mysql-connector-j</div>
<div>6.[新增]Nacos认证启动注册</div>
<div>7.[新增]使用 flatten-maven-plugin 重构版本管理</div>
<div>8.[新增]CryptoKeyGenerator提供加密签名获取</div>
<div>9.[新增]cryptoData配置支持前端报文加密自动化</div>
<div>10.[新增]分库分表独立配置</div>
<div>11.[优化]Token加密单独出配置blade.token.crypto-key防止与报文加密配置逻辑冲突</div>
<div>12.[优化]tenant数据库隔离与sharding分库分表依赖分离减少默认jar包容量</div>
<div>13.[优化]默认关闭Notice模块的token加密降低启动难度</div>
<div>14.[优化]关闭aesKey与desKey的默认值防止外部以此攻击加密接口获取信息</div>
<div>15.[修复]修复菜单新增层级问题</div>
</el-collapse-item>
<el-collapse-item title="3.3.0.RELEASE发布,多租户数据库隔离集成ShardingSphere支持分库分表" name="32">
<div>1.[升级]SpringBoot 2.7.18</div>
<div>2.[升级]SpringBootAdmin 2.7.4</div>
<div>3.[升级]Spring 5.3.31</div>
<div>4.[升级]Druid 1.2.20</div>
<div>5.[升级]Mybatis-Plus 3.5.4.1</div>
<div>6.[升级]Avue2 2.11.0</div>
<div>7.[升级]Knife4j 4.3.0</div>
<div>8.[升级]Jackson 2.16.0</div>
<div>9.[升级]Log4j2 2.22.0</div>
<div>10.[升级]Logback 1.2.13</div>
<div>11.[升级]LiteFlow 2.11.3</div>
<div>12.[新增]多租户数据库隔离支持分库分表功能</div>
<div>13.[新增]动态数据源与分库分表集成</div>
<div>14.[新增]多租户对象存储支持内外网地址映射</div>
<div>15.[优化]Sql防注入逻辑避免双写等情况攻击</div>
<div>16.[优化]Redis序列化逻辑</div>
<div>17.[优化]RequestInterceptor增加初始化指定类名</div>
<div>18.[优化]antlr4版本指定避免模版解析冲突失效</div>
<div>19.[优化]调整对象存储枚举类别名与配置类名称一致</div>
<div>20.[优化]部分重要API调用权限提高至管理员角色</div>
<div>21.[优化]数据源配置提交逻辑</div>
<div>
22.[优化]数据权限处理器强制指定Master数据源避免动态数据源无法正确读取数据
</div>
<div>23.[修复]单点登录退出失效的问题</div>
<div>24.[修复]oss缓存判断有概率空指针的问题</div>
<div>25.[修复]ApiVersion在 SpringBoot2.7.x下报错的问题</div>
<div>26.[修复]修复用户名称读取错误的问题</div>
</el-collapse-item>
<el-collapse-item title="3.2.0.RELEASE发布,新增规则引擎、token加密、skywalking集成" name="31">
<div>1.[升级]SpringCloud 2021.0.8</div>
<div>2.[升级]SpringBoot 2.7.15</div>
<div>3.[升级]Spring 5.3.29</div>
@ -874,7 +930,7 @@
data() {
return {
activeNames: ['1', '2', '3', '5'],
logActiveNames: ['31']
logActiveNames: ['34']
};
},
computed: {

Loading…
Cancel
Save