init
This commit is contained in:
37
src/views/compare/dcDbConfig/dcDbConfig.vue
Normal file
37
src/views/compare/dcDbConfig/dcDbConfig.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div className="app-container">
|
||||
<transition name="el-zoom-in-center">
|
||||
<dc-db-config-list v-if="options.showList" @showCard="showCard" />
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import dcDbConfigList from './dcDbConfigList'
|
||||
|
||||
export default {
|
||||
name: 'DcDbConfig',
|
||||
components: { dcDbConfigList },
|
||||
data() {
|
||||
return {
|
||||
options: {
|
||||
data: {},
|
||||
showList: true,
|
||||
showAdd: false,
|
||||
showEdit: false,
|
||||
showDetail: false
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showCard(data) {
|
||||
Object.assign(this.options, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
|
||||
395
src/views/compare/dcDbConfig/dcDbConfigList.vue
Normal file
395
src/views/compare/dcDbConfig/dcDbConfigList.vue
Normal file
@@ -0,0 +1,395 @@
|
||||
<template>
|
||||
<el-card class="box-card" shadow="always">
|
||||
<el-form ref="queryForm" :model="queryParams" :inline="true">
|
||||
<el-form-item label="connectName" prop="id">
|
||||
<el-input
|
||||
v-model="queryParams.connectName"
|
||||
placeholder="connectName"
|
||||
clearable
|
||||
size="small"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleCreate"
|
||||
>新增</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableDataList"
|
||||
border
|
||||
tooltip-effect="dark"
|
||||
:size="tableSize"
|
||||
:height="tableHeight"
|
||||
style="width: 100%;margin: 15px 0;"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<template v-for="(item, index) in tableColumns">
|
||||
<el-table-column
|
||||
v-if="item.show"
|
||||
:key="index"
|
||||
:prop="item.prop"
|
||||
:label="item.label"
|
||||
:formatter="item.formatter"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
</template>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-popover
|
||||
placement="left"
|
||||
trigger="click"
|
||||
>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit-outline"
|
||||
@click="handleUpdate(scope.row)"
|
||||
>修改</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
>删除</el-button>
|
||||
<el-button slot="reference">操作</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 模态框:添加数据-->
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="800px">
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="150px">
|
||||
<el-form-item label="connectName" prop="connectName" required>
|
||||
<el-input v-model="temp.connectName" placeholder="请输入connectName" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="type" prop="type" required>
|
||||
<el-select v-model="temp.type" placeholder="请选择数据库类型">
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="url" prop="url" required>
|
||||
<el-input v-model="temp.url" placeholder="请输入url" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="userName" prop="userName" required>
|
||||
<el-input v-model="temp.userName" placeholder="请输入userName" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="pwd" prop="pwd">
|
||||
<el-input v-model="temp.pwd" placeholder="请输入pwd" style="width: 70%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-pagination
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:current-page.sync="queryParams.pageNum"
|
||||
:page-size.sync="queryParams.pageSize"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pageDataModel, delDataModel, addDataModel, updateDataModel } from '@/api/compare/dcDbConfig/datamodel'
|
||||
export default {
|
||||
name: 'DcDbConfigList',
|
||||
data() {
|
||||
return {
|
||||
tableHeight: document.body.offsetHeight - 310 + 'px',
|
||||
// 展示切换
|
||||
showOptions: {
|
||||
data: {},
|
||||
showList: true,
|
||||
showAdd: false,
|
||||
showEdit: false,
|
||||
showDetail: false
|
||||
},
|
||||
options: [{
|
||||
value: 'MySQL',
|
||||
label: 'MySQL'
|
||||
}, {
|
||||
value: 'Hive',
|
||||
label: 'Hive'
|
||||
}, {
|
||||
value: 'Doris',
|
||||
label: 'Doris'
|
||||
}],
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
dialogFormVisible: false,
|
||||
dialogFormVisible2: false,
|
||||
textMap: {
|
||||
update: '编辑数据库配置',
|
||||
create: '添加数据库配置'
|
||||
},
|
||||
dialogStatus: '',
|
||||
temp: {
|
||||
connectName: '',
|
||||
type: '',
|
||||
url: '',
|
||||
userName: '',
|
||||
pwd: ''
|
||||
},
|
||||
rules: {
|
||||
id: [{ required: true, message: '不能为空', trigger: 'blur' }],
|
||||
connectName: [{ required: true, message: '不能为空', trigger: 'blur' }],
|
||||
type: [{ required: true, message: '不能为空', trigger: 'blur' }],
|
||||
url: [{ required: true, message: '不能为空', trigger: 'blur' }]
|
||||
},
|
||||
// 表格头
|
||||
tableColumns: [
|
||||
{ prop: 'id', label: 'id', show: true },
|
||||
{ prop: 'connectName', label: 'connectName', show: true },
|
||||
{ prop: 'type', label: 'type', show: true },
|
||||
{
|
||||
prop: 'url',
|
||||
label: 'url',
|
||||
show: true
|
||||
}
|
||||
],
|
||||
// 默认选择中表格头
|
||||
checkedTableColumns: [],
|
||||
tableSize: 'medium',
|
||||
// 状态数据字典
|
||||
statusOptions: [],
|
||||
// 表格数据
|
||||
tableDataList: [],
|
||||
// 总数据条数
|
||||
total: 0,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
connectName: ''
|
||||
},
|
||||
// 流程状态数据字典
|
||||
flowStatusOptions: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getDicts('sys_common_status').then(response => {
|
||||
if (response.success) {
|
||||
this.statusOptions = response.data
|
||||
}
|
||||
})
|
||||
this.getDicts('sys_flow_status').then(response => {
|
||||
if (response.success) {
|
||||
this.flowStatusOptions = response.data
|
||||
}
|
||||
})
|
||||
this.getList()
|
||||
},
|
||||
mounted() {
|
||||
this.initCols()
|
||||
},
|
||||
methods: {
|
||||
/** 查询数据源列表 */
|
||||
getList() {
|
||||
const _this = this
|
||||
// console.log("hello world1")
|
||||
_this.loading = true
|
||||
pageDataModel(this.queryParams).then(response => {
|
||||
console.log(response)
|
||||
_this.loading = false
|
||||
if (response.code ===0) {
|
||||
_this.tableDataList = response.rows
|
||||
_this.total = response.total
|
||||
}
|
||||
})
|
||||
},
|
||||
initCols() {
|
||||
this.checkedTableColumns = this.tableColumns.map(col => col.prop)
|
||||
},
|
||||
handleCheckedColsChange(val) {
|
||||
this.tableColumns.forEach(col => {
|
||||
if (!this.checkedTableColumns.includes(col.prop)) {
|
||||
col.show = false
|
||||
} else {
|
||||
col.show = true
|
||||
}
|
||||
})
|
||||
},
|
||||
handleCommand(command) {
|
||||
this.tableSize = command
|
||||
},
|
||||
resetTemp() {
|
||||
console.log('添加数据前,先清空之前输入的值')
|
||||
this.temp = {
|
||||
connectName: '',
|
||||
type: '',
|
||||
url: '',
|
||||
userName: '',
|
||||
pwd: ''
|
||||
}
|
||||
},
|
||||
createData() {
|
||||
console.log('开始添加数据')
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
console.log(valid)
|
||||
if (valid) {
|
||||
console.log(this.temp)
|
||||
addDataModel(this.temp).then((response) => {
|
||||
console.log(response)
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '添加成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
this.getList()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
// 新增按钮操作
|
||||
handleCreate() {
|
||||
this.resetTemp()
|
||||
this.dialogStatus = 'create'
|
||||
this.dialogFormVisible = true
|
||||
console.log('新增数据前。看是否已清空之前的数据', this.temp)
|
||||
},
|
||||
// 修改按钮操作
|
||||
handleUpdate(row) {
|
||||
// console.log(row)
|
||||
this.temp = Object.assign({}, row) // copy obj
|
||||
this.dialogStatus = 'update'
|
||||
this.dialogFormVisible = true
|
||||
},
|
||||
updateData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
const tempData = Object.assign({}, this.temp)
|
||||
console.log(tempData)
|
||||
const tempData2 = {}
|
||||
tempData2.id = tempData.id
|
||||
tempData2.connectName = tempData.connectName
|
||||
tempData2.type = tempData.type
|
||||
tempData2.url = tempData.url
|
||||
tempData2.userName = tempData.userName
|
||||
tempData2.pwd = tempData.pwd
|
||||
updateDataModel(tempData2).then(() => {
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '修改成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
this.getList()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.queryParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
connectName: ''
|
||||
}
|
||||
this.handleQuery()
|
||||
},
|
||||
/** 刷新列表 */
|
||||
handleRefresh() {
|
||||
this.getList()
|
||||
},
|
||||
/** 详情按钮操作 */
|
||||
handleDetail(row) {
|
||||
this.showOptions.data.id = row.id
|
||||
this.showOptions.showList = false
|
||||
this.showOptions.showAdd = false
|
||||
this.showOptions.showEdit = false
|
||||
this.showOptions.showDetail = true
|
||||
this.$emit('showCard', this.showOptions)
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
console.log('进入到删除按钮操作')
|
||||
console.log(row.id)
|
||||
this.$confirm('选中数据将被永久删除, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delDataModel(row.id).then(response => {
|
||||
console.log(response)
|
||||
if (response.code ===0) {
|
||||
this.$message.success('删除成功')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
/** 删除全部,按钮操作 */
|
||||
handleDelAll(row) {
|
||||
console.log('进入到删除按钮操作')
|
||||
console.log(row)
|
||||
this.$confirm('选中数据将被永久删除, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delDataModel(row.id).then(response => {
|
||||
console.log(response)
|
||||
if (response.code === 0) {
|
||||
this.$message.success('删除成功')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
console.log(`每页 ${val} 条`)
|
||||
this.queryParams.pageNum = 1
|
||||
this.queryParams.pageSize = val
|
||||
this.getList()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
console.log(`当前页: ${val}`)
|
||||
this.queryParams.pageNum = val
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.right-toolbar {
|
||||
float: right;
|
||||
}
|
||||
.el-card ::v-deep .el-card__body {
|
||||
height: calc(100vh - 170px);
|
||||
}
|
||||
</style>
|
||||
37
src/views/compare/dcJob/dcJob.vue
Normal file
37
src/views/compare/dcJob/dcJob.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div className="app-container">
|
||||
<transition name="el-zoom-in-center">
|
||||
<dc-job-list v-if="options.showList" @showCard="showCard" />
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import dcJobList from './dcJobList'
|
||||
|
||||
export default {
|
||||
name: 'DcJob',
|
||||
components: { dcJobList },
|
||||
data() {
|
||||
return {
|
||||
options: {
|
||||
data: {},
|
||||
showList: true,
|
||||
showAdd: false,
|
||||
showEdit: false,
|
||||
showDetail: false
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showCard(data) {
|
||||
Object.assign(this.options, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
|
||||
449
src/views/compare/dcJob/dcJobList.vue
Normal file
449
src/views/compare/dcJob/dcJobList.vue
Normal file
@@ -0,0 +1,449 @@
|
||||
<template>
|
||||
<el-card class="box-card" shadow="always">
|
||||
<el-form ref="queryForm" :model="queryParams" :inline="true">
|
||||
<el-form-item label="JobId" prop="id">
|
||||
<el-input
|
||||
v-model="queryParams.id"
|
||||
placeholder="请输入JobId"
|
||||
clearable
|
||||
size="small"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleCreate"
|
||||
>新增</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableDataList"
|
||||
border
|
||||
tooltip-effect="dark"
|
||||
:size="tableSize"
|
||||
:height="tableHeight"
|
||||
style="width: 100%;margin: 15px 0;"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<template v-for="(item, index) in tableColumns">
|
||||
<el-table-column
|
||||
v-if="item.show"
|
||||
:key="index"
|
||||
:prop="item.prop"
|
||||
:label="item.label"
|
||||
:formatter="item.formatter"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
</template>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-popover
|
||||
placement="left"
|
||||
trigger="click"
|
||||
>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit-outline"
|
||||
@click="handleUpdate(scope.row)"
|
||||
>修改</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
>删除</el-button>
|
||||
<el-button slot="reference">操作</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 模态框:添加数据-->
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="800px">
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="150px">
|
||||
<el-form-item label="任务编号" prop="jobId" required>
|
||||
<el-input v-model="temp.jobId" placeholder="请输入任务编号" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任务名称" prop="jobName" required>
|
||||
<el-input v-model="temp.jobName" placeholder="请输入任务名称" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任务分组" prop="jobGroup" required>
|
||||
<el-input v-model="temp.jobGroup" placeholder="请输入任务分组" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="调用目标字符串" prop="invokeTarget">
|
||||
<el-input v-model="temp.invokeTarget" placeholder="请输入任务目标字符串" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="执行表达式" prop="cronExpression">
|
||||
<el-input v-model="temp.cronExpression" placeholder="请输入执行表达式" style="width: 70%" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="任务状态" prop="status" required>
|
||||
<el-input v-model="temp.status" placeholder="任务状态" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime" required>
|
||||
<el-input v-model="temp.createTime" placeholder="创建时间" style="width: 70%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 模态框:修改数据-->
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible2" width="800px">
|
||||
<el-form ref="dataForm2" :rules="rules" :model="temp2" label-position="left" label-width="150px">
|
||||
<el-form-item label="任务编号" prop="jobId" required>
|
||||
<el-input v-model="temp2.jobId" placeholder="请输入任务编号" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任务名称" prop="jobName" required>
|
||||
<el-input v-model="temp2.jobName" placeholder="请输入任务名称" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任务分组" prop="jobGroup" required>
|
||||
<el-input v-model="temp2.jobGroup" placeholder="请输入任务分组" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="调用目标字符串" prop="invokeTarget">
|
||||
<el-input v-model="temp2.invokeTarget" placeholder="请输入任务目标字符串" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="执行表达式" prop="cronExpression">
|
||||
<el-input v-model="temp2.cronExpression" placeholder="请输入执行表达式" style="width: 70%" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="任务状态" prop="status" required>
|
||||
<el-input v-model="temp2.status" placeholder="任务状态" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime" required>
|
||||
<el-input v-model="temp2.createTime" placeholder="创建时间" style="width: 70%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible2 = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-pagination
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:current-page.sync="queryParams.pageNum"
|
||||
:page-size.sync="queryParams.pageSize"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pageDataModel, delDataModel, addDataModel, updateDataModel } from '@/api/compare/dcJob/datamodel'
|
||||
export default {
|
||||
name: 'DcJobList',
|
||||
data() {
|
||||
return {
|
||||
tableHeight: document.body.offsetHeight - 310 + 'px',
|
||||
// 展示切换
|
||||
showOptions: {
|
||||
data: {},
|
||||
showList: true,
|
||||
showAdd: false,
|
||||
showEdit: false,
|
||||
showDetail: false
|
||||
},
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
dialogFormVisible: false,
|
||||
dialogFormVisible2: false,
|
||||
textMap: {
|
||||
update: '编辑Job配置',
|
||||
create: '添加Job配置'
|
||||
},
|
||||
dialogStatus: '',
|
||||
temp: {
|
||||
jobId: '',
|
||||
jobName: '',
|
||||
jobGroup: '',
|
||||
invokeTarget: '',
|
||||
cronExpression: '',
|
||||
status: '',
|
||||
createTime: ''
|
||||
},
|
||||
temp2: {
|
||||
jobId: '',
|
||||
jobName: '',
|
||||
jobGroup: '',
|
||||
invokeTarget: '',
|
||||
cronExpression: '',
|
||||
status: '',
|
||||
createTime: ''
|
||||
},
|
||||
rules: {
|
||||
jobId: [{ required: true, message: '不能为空', trigger: 'blur' }],
|
||||
jobName: [{ required: true, message: '不能为空', trigger: 'blur' }],
|
||||
jobGroup: [{ required: true, message: '不能为空', trigger: 'blur' }],
|
||||
invokeTarget: [{ required: true, message: '不能为空', trigger: 'blur' }],
|
||||
cronExpression: [{ required: true, message: '不能为空', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '不能为空', trigger: 'blur' }],
|
||||
createTime: [{ required: true, message: '不能为空', trigger: 'blur' }]
|
||||
},
|
||||
// 表格头
|
||||
tableColumns: [
|
||||
{ prop: 'jobId', label: '任务编号', show: true },
|
||||
{ prop: 'jobName', label: '任务名称', show: true },
|
||||
{ prop: 'jobGroup', label: '任务分组', show: true },
|
||||
{
|
||||
prop: 'invokeTarget',
|
||||
label: '调用目标字符串',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
prop: 'cronExpression',
|
||||
label: '执行表达式',
|
||||
show: true
|
||||
},
|
||||
{ prop: 'status', label: '任务状态', show: true },
|
||||
{ prop: 'createTime', label: '创建时间', show: true }
|
||||
],
|
||||
// 默认选择中表格头
|
||||
checkedTableColumns: [],
|
||||
tableSize: 'medium',
|
||||
// 状态数据字典
|
||||
statusOptions: [],
|
||||
// 表格数据
|
||||
tableDataList: [],
|
||||
// 总数据条数
|
||||
total: 0,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
id: ''
|
||||
},
|
||||
// 流程状态数据字典
|
||||
flowStatusOptions: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getDicts('sys_common_status').then(response => {
|
||||
if (response.success) {
|
||||
this.statusOptions = response.data
|
||||
}
|
||||
})
|
||||
this.getDicts('sys_flow_status').then(response => {
|
||||
if (response.success) {
|
||||
this.flowStatusOptions = response.data
|
||||
}
|
||||
})
|
||||
this.getList()
|
||||
},
|
||||
mounted() {
|
||||
this.initCols()
|
||||
},
|
||||
methods: {
|
||||
/** 查询数据源列表 */
|
||||
getList() {
|
||||
const _this = this
|
||||
// console.log("hello world1")
|
||||
_this.loading = true
|
||||
pageDataModel(this.queryParams).then(response => {
|
||||
_this.loading = false
|
||||
if (response.code ===0) {
|
||||
_this.tableDataList = response.rows
|
||||
_this.total = response.total
|
||||
}
|
||||
})
|
||||
},
|
||||
initCols() {
|
||||
this.checkedTableColumns = this.tableColumns.map(col => col.prop)
|
||||
},
|
||||
handleCheckedColsChange(val) {
|
||||
this.tableColumns.forEach(col => {
|
||||
if (!this.checkedTableColumns.includes(col.prop)) {
|
||||
col.show = false
|
||||
} else {
|
||||
col.show = true
|
||||
}
|
||||
})
|
||||
},
|
||||
handleCommand(command) {
|
||||
this.tableSize = command
|
||||
},
|
||||
resetTemp() {
|
||||
console.log('添加数据前,先清空之前输入的值')
|
||||
this.temp = {
|
||||
jobId: '',
|
||||
jobName: '',
|
||||
jobGroup: '',
|
||||
invokeTarget: '',
|
||||
cronExpression: '',
|
||||
status: '',
|
||||
createTime: ''
|
||||
}
|
||||
},
|
||||
createData() {
|
||||
console.log('开始添加数据')
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
console.log(valid)
|
||||
if (valid) {
|
||||
console.log(this.temp)
|
||||
addDataModel(this.temp).then((response) => {
|
||||
console.log(response)
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '添加成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
this.getList()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
// 新增按钮操作
|
||||
handleCreate() {
|
||||
this.resetTemp()
|
||||
this.dialogStatus = 'create'
|
||||
this.dialogFormVisible = true
|
||||
console.log('新增数据前。看是否已清空之前的数据', this.temp)
|
||||
},
|
||||
// 修改按钮操作
|
||||
handleUpdate(row) {
|
||||
// console.log(row)
|
||||
this.temp2 = Object.assign({}, row) // copy obj
|
||||
this.dialogStatus = 'update'
|
||||
this.dialogFormVisible2 = true
|
||||
},
|
||||
updateData() {
|
||||
this.$refs['dataForm2'].validate((valid) => {
|
||||
if (valid) {
|
||||
const tempData = Object.assign({}, this.temp2)
|
||||
console.log(tempData)
|
||||
const tempData2 = {}
|
||||
tempData2.id = tempData.id
|
||||
tempData2.originTableName = tempData.originTableName
|
||||
tempData2.originTablePrimary = tempData.originTablePrimary
|
||||
tempData2.originTableFields = tempData.originTableFields
|
||||
tempData2.originTableFilter = tempData.originTableFilter
|
||||
tempData2.originTableGroup = tempData.originTableGroup
|
||||
tempData2.toTableName = tempData.toTableName
|
||||
tempData2.toTablePrimary = tempData.toTablePrimary
|
||||
tempData2.toTableFields = tempData.toTableFields
|
||||
tempData2.toTableFilter = tempData.toTableFilter
|
||||
tempData2.toTableGroup = tempData.toTableGroup
|
||||
updateDataModel(tempData2).then(() => {
|
||||
this.dialogFormVisible2 = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '修改成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
this.getList()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.queryParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
id: ''
|
||||
}
|
||||
this.handleQuery()
|
||||
},
|
||||
/** 刷新列表 */
|
||||
handleRefresh() {
|
||||
this.getList()
|
||||
},
|
||||
/** 详情按钮操作 */
|
||||
handleDetail(row) {
|
||||
this.showOptions.data.id = row.id
|
||||
this.showOptions.showList = false
|
||||
this.showOptions.showAdd = false
|
||||
this.showOptions.showEdit = false
|
||||
this.showOptions.showDetail = true
|
||||
this.$emit('showCard', this.showOptions)
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
console.log('进入到删除按钮操作')
|
||||
console.log(row.id)
|
||||
this.$confirm('选中数据将被永久删除, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delDataModel(row.id).then(response => {
|
||||
console.log(response)
|
||||
if (response.code ===0) {
|
||||
this.$message.success('删除成功')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
/** 删除全部,按钮操作 */
|
||||
handleDelAll(row) {
|
||||
console.log('进入到删除按钮操作')
|
||||
console.log(row)
|
||||
this.$confirm('选中数据将被永久删除, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delDataModel(row.id).then(response => {
|
||||
console.log(response)
|
||||
if (response.code === 0) {
|
||||
this.$message.success('删除成功')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
console.log(`每页 ${val} 条`)
|
||||
this.queryParams.pageNum = 1
|
||||
this.queryParams.pageSize = val
|
||||
this.getList()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
console.log(`当前页: ${val}`)
|
||||
this.queryParams.pageNum = val
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.right-toolbar {
|
||||
float: right;
|
||||
}
|
||||
.el-card ::v-deep .el-card__body {
|
||||
height: calc(100vh - 170px);
|
||||
}
|
||||
</style>
|
||||
37
src/views/compare/dcJobConfig/dcJobConfig.vue
Normal file
37
src/views/compare/dcJobConfig/dcJobConfig.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div className="app-container">
|
||||
<transition name="el-zoom-in-center">
|
||||
<dc-job-config-list v-if="options.showList" @showCard="showCard"/>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import dcJobConfigList from './dcJobConfigList'
|
||||
|
||||
export default {
|
||||
name: 'dcJobConfig',
|
||||
components: {dcJobConfigList},
|
||||
data() {
|
||||
return {
|
||||
options: {
|
||||
data: {},
|
||||
showList: true,
|
||||
showAdd: false,
|
||||
showEdit: false,
|
||||
showDetail: false
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showCard(data) {
|
||||
Object.assign(this.options, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
|
||||
526
src/views/compare/dcJobConfig/dcJobConfigList.vue
Normal file
526
src/views/compare/dcJobConfig/dcJobConfigList.vue
Normal file
@@ -0,0 +1,526 @@
|
||||
<template>
|
||||
<el-card class="box-card" shadow="always">
|
||||
<el-form ref="queryForm" :model="queryParams" :inline="true">
|
||||
<el-form-item label="JobId" prop="id">
|
||||
<el-input
|
||||
v-model="queryParams.id"
|
||||
placeholder="请输入JobId"
|
||||
clearable
|
||||
size="small"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleCreate"
|
||||
>新增</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableDataList"
|
||||
border
|
||||
tooltip-effect="dark"
|
||||
:size="tableSize"
|
||||
:height="tableHeight"
|
||||
style="width: 100%;margin: 15px 0;"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<template v-for="(item, index) in tableColumns">
|
||||
<el-table-column
|
||||
v-if="item.show"
|
||||
:key="index"
|
||||
:prop="item.prop"
|
||||
:label="item.label"
|
||||
:formatter="item.formatter"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
</template>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-popover
|
||||
placement="left"
|
||||
trigger="click"
|
||||
>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-edit-outline"
|
||||
@click="handleUpdate(scope.row)"
|
||||
>修改</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-video-play"
|
||||
@click="handleRun(scope.row)"
|
||||
>运行</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
>删除</el-button>
|
||||
<el-button slot="reference">操作</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 模态框:添加数据-->
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="800px">
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="150px">
|
||||
<el-form-item label="connectName" prop="dbConfigId" required>
|
||||
<el-select v-model="temp.dbConfigId" placeholder="请选择数据配置">
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="源表" prop="originTableName" required>
|
||||
<el-input v-model="temp.originTableName" placeholder="请填写库名和表名(例如:[db].[table])" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="源表主键" prop="originTablePrimary" required>
|
||||
<el-input v-model="temp.originTablePrimary" placeholder="源表主键" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="源表字段" prop="originTableFields" required>
|
||||
<el-input v-model="temp.originTableFields" placeholder="请填写字段名,用逗号分隔" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="源表过滤条件" prop="originTableFilter">
|
||||
<el-input v-model="temp.originTableFilter" placeholder="源表过滤条件" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="源表分组条件" prop="originTableGroup">
|
||||
<el-input v-model="temp.originTableGroup" placeholder="源表分组条件" style="width: 70%" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="目标表" prop="toTableName" required>
|
||||
<el-input v-model="temp.toTableName" placeholder="目标表" disabled style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="目标表主键" prop="toTablePrimary" required>
|
||||
<el-input v-model="temp.toTablePrimary" placeholder="目标表主键" disabled style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="目标表字段" prop="toTableFields" required>
|
||||
<el-input v-model="temp.toTableFields" placeholder="目标表字段" disabled style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="目标表过滤条件" prop="toTableFilter">
|
||||
<el-input v-model="temp.toTableFilter" placeholder="目标表过滤条件" disabled style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="目标表分组条件" prop="toTableGroup">
|
||||
<el-input v-model="temp.toTableGroup" placeholder="目标表分组条件" disabled style="width: 70%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 模态框:修改数据-->
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible2" width="800px">
|
||||
<el-form ref="dataForm2" :rules="rules" :model="temp2" label-position="left" label-width="150px">
|
||||
<el-form-item label="源表" prop="originTableName" required>
|
||||
<el-input v-model="temp2.originTableName" placeholder="请填写库名和表名(例如:[db].[table])" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="源表主键" prop="originTablePrimary" required>
|
||||
<el-input v-model="temp2.originTablePrimary" placeholder="源表主键" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="源表字段" prop="originTableFields" required>
|
||||
<el-input v-model="temp2.originTableFields" placeholder="请填写字段名,用逗号分隔" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="源表过滤条件" prop="originTableFilter">
|
||||
<el-input v-model="temp2.originTableFilter" placeholder="源表过滤条件" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="源表分组条件" prop="originTableGroup">
|
||||
<el-input v-model="temp2.originTableGroup" placeholder="源表分组条件" style="width: 70%" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="目标表" prop="toTableName" required>
|
||||
<el-input v-model="temp2.toTableName" placeholder="目标表" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="目标表主键" prop="toTablePrimary" required>
|
||||
<el-input v-model="temp2.toTablePrimary" placeholder="目标表主键" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="目标表字段" prop="toTableFields" required>
|
||||
<el-input v-model="temp2.toTableFields" placeholder="目标表字段" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="目标表过滤条件" prop="toTableFilter">
|
||||
<el-input v-model="temp2.toTableFilter" placeholder="目标表过滤条件" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="目标表分组条件" prop="toTableGroup">
|
||||
<el-input v-model="temp2.toTableGroup" placeholder="目标表分组条件" style="width: 70%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible2 = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-pagination
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:current-page.sync="queryParams.pageNum"
|
||||
:page-size.sync="queryParams.pageSize"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pageDataModel, delDataModel, addDataModel, updateDataModel, getDbConfig, dcJobConfigRun } from '@/api/compare/dcJobConfig/datamodel'
|
||||
export default {
|
||||
name: 'DcJobConfigList',
|
||||
data() {
|
||||
return {
|
||||
options: [],
|
||||
tableHeight: document.body.offsetHeight - 310 + 'px',
|
||||
// 展示切换
|
||||
showOptions: {
|
||||
data: {},
|
||||
showList: true,
|
||||
showAdd: false,
|
||||
showEdit: false,
|
||||
showDetail: false
|
||||
},
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
dialogFormVisible: false,
|
||||
dialogFormVisible2: false,
|
||||
textMap: {
|
||||
update: '编辑Job配置',
|
||||
create: '添加Job配置'
|
||||
},
|
||||
dialogStatus: '',
|
||||
temp: {
|
||||
dbConfigId: '',
|
||||
originTableName: '',
|
||||
originTablePrimary: '',
|
||||
originTableFields: '',
|
||||
originTableFilter: '',
|
||||
originTableGroup: '',
|
||||
toTableName: '',
|
||||
toTablePrimary: '',
|
||||
toTableFields: '',
|
||||
toTableFilter: '',
|
||||
toTableGroup: ''
|
||||
},
|
||||
temp2: {
|
||||
dbConfigId: '',
|
||||
originTableName: '',
|
||||
originTablePrimary: '',
|
||||
originTableFields: '',
|
||||
originTableFilter: '',
|
||||
originTableGroup: '',
|
||||
toTableName: '',
|
||||
toTablePrimary: '',
|
||||
toTableFields: '',
|
||||
toTableFilter: '',
|
||||
toTableGroup: ''
|
||||
},
|
||||
rules: {
|
||||
name: [{ required: true, message: '不能为空', trigger: 'blur' }],
|
||||
description: [{ required: true, message: '不能为空', trigger: 'blur' }]
|
||||
},
|
||||
// 表格头
|
||||
tableColumns: [
|
||||
{ prop: 'id', label: 'jobconfigId', show: true },
|
||||
{ prop: 'originTableName', label: '源表', show: true },
|
||||
{ prop: 'originTablePrimary', label: '源表主键', show: true },
|
||||
{
|
||||
prop: 'originTableFields',
|
||||
label: '对比字段',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
prop: 'toTableName',
|
||||
label: '目标表',
|
||||
show: true
|
||||
},
|
||||
{ prop: 'toTablePrimary', label: '目标主键', show: true },
|
||||
{ prop: 'toTableFields', label: '目标对比字段', show: true },
|
||||
{ prop: 'schduleStatus', label: '是否启动调度', show: true,
|
||||
formatter: function (value, row, index) {
|
||||
console.log(value)
|
||||
if (value.schduleStatus =='1') {
|
||||
return '是'
|
||||
} else {
|
||||
return '否'
|
||||
}
|
||||
} },
|
||||
{ prop: 'createTime', label: '创建时间', show: true }
|
||||
],
|
||||
// 默认选择中表格头
|
||||
checkedTableColumns: [],
|
||||
tableSize: 'medium',
|
||||
// 状态数据字典
|
||||
statusOptions: [],
|
||||
// 表格数据
|
||||
tableDataList: [],
|
||||
// 总数据条数
|
||||
total: 0,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
id: ''
|
||||
},
|
||||
// 流程状态数据字典
|
||||
flowStatusOptions: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
temp: {
|
||||
handler(newName, oldName) {
|
||||
this.temp.dbConfigId = newName.dbConfigId
|
||||
this.temp.toTableName = newName.originTableName
|
||||
this.temp.toTablePrimary = newName.originTablePrimary
|
||||
this.temp.toTableFields = newName.originTableFields
|
||||
this.temp.toTableFilter = newName.originTableFilter
|
||||
this.temp.toTableGroup = newName.originTableGroup
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
this.getDbConfig()
|
||||
},
|
||||
mounted() {
|
||||
this.initCols()
|
||||
},
|
||||
methods: {
|
||||
/** 查询数据源列表 */
|
||||
getList() {
|
||||
const _this = this
|
||||
// console.log("hello world1")
|
||||
_this.loading = true
|
||||
pageDataModel(this.queryParams).then(response => {
|
||||
// console.log("hello world2")
|
||||
// console.log(response.total)
|
||||
// console.log(response.code)
|
||||
_this.loading = false
|
||||
if (response.code ===0) {
|
||||
_this.tableDataList = response.rows
|
||||
_this.total = response.total
|
||||
// console.log("hello world3")
|
||||
// console.log(_this.tableDataList)
|
||||
// console.log(_this.total)
|
||||
}
|
||||
})
|
||||
},
|
||||
initCols() {
|
||||
this.checkedTableColumns = this.tableColumns.map(col => col.prop)
|
||||
},
|
||||
// 获取DbconfigId
|
||||
getDbConfig() {
|
||||
let _this =this
|
||||
console.log('添加数据时,获取DbconfigId')
|
||||
getDbConfig().then((response) => {
|
||||
// console.log("response",response)
|
||||
// console.log("total",response.total)
|
||||
for(let i = 0; i < response.total; i++){
|
||||
// console.log(response.rows[i].id)
|
||||
let obj={ value :'', label : '' }
|
||||
console.log(response.rows[i].connectName)
|
||||
obj.value = response.rows[i].id
|
||||
obj.label = response.rows[i].connectName
|
||||
console.log(i)
|
||||
console.log(obj)
|
||||
_this.options.push(obj)
|
||||
}
|
||||
})
|
||||
},
|
||||
// 添加数据前,清空数据
|
||||
resetTemp() {
|
||||
console.log('添加数据前,先清空之前输入的值')
|
||||
this.temp = {
|
||||
dbConfigId: '',
|
||||
originTableName: '',
|
||||
originTablePrimary: '',
|
||||
originTableFields: '',
|
||||
originTableFilter: '',
|
||||
originTableGroup: '',
|
||||
toTableName: '',
|
||||
toTablePrimary: '',
|
||||
toTableFields: '',
|
||||
toTableFilter: '',
|
||||
toTableGroup: ''
|
||||
}
|
||||
},
|
||||
// 开始添加数据
|
||||
createData() {
|
||||
console.log('开始添加数据')
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
console.log(valid)
|
||||
if (valid) {
|
||||
console.log(this.temp)
|
||||
addDataModel(this.temp).then((response) => {
|
||||
console.log(response)
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '添加成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
this.getList()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
// 新增按钮操作
|
||||
handleCreate() {
|
||||
this.resetTemp()
|
||||
this.dialogStatus = 'create'
|
||||
this.dialogFormVisible = true
|
||||
console.log('新增数据前。看是否已清空之前的数据', this.temp)
|
||||
},
|
||||
// 修改按钮操作
|
||||
handleUpdate(row) {
|
||||
// console.log(row)
|
||||
this.temp2 = Object.assign({}, row) // copy obj
|
||||
this.dialogStatus = 'update'
|
||||
this.dialogFormVisible2 = true
|
||||
},
|
||||
updateData() {
|
||||
this.$refs['dataForm2'].validate((valid) => {
|
||||
if (valid) {
|
||||
const tempData = Object.assign({}, this.temp2)
|
||||
console.log(tempData)
|
||||
const tempData2 = {}
|
||||
tempData2.id = tempData.id
|
||||
tempData2.originTableName = tempData.originTableName
|
||||
tempData2.originTablePrimary = tempData.originTablePrimary
|
||||
tempData2.originTableFields = tempData.originTableFields
|
||||
tempData2.originTableFilter = tempData.originTableFilter
|
||||
tempData2.originTableGroup = tempData.originTableGroup
|
||||
tempData2.toTableName = tempData.toTableName
|
||||
tempData2.toTablePrimary = tempData.toTablePrimary
|
||||
tempData2.toTableFields = tempData.toTableFields
|
||||
tempData2.toTableFilter = tempData.toTableFilter
|
||||
tempData2.toTableGroup = tempData.toTableGroup
|
||||
updateDataModel(tempData2).then(() => {
|
||||
this.dialogFormVisible2 = false
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '修改成功',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
this.getList()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
// 运行按钮操作
|
||||
handleRun(data){
|
||||
console.log(data.id)
|
||||
dcJobConfigRun(data.id).then((response) => {
|
||||
console.log("response",response)
|
||||
if (response.code ===0) {
|
||||
this.$message.success('运行成功')
|
||||
this.getList()
|
||||
}else{
|
||||
this.$message.success('运行失败')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.queryParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
id: ''
|
||||
}
|
||||
this.handleQuery()
|
||||
},
|
||||
/** 刷新列表 */
|
||||
handleRefresh() {
|
||||
this.getList()
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
console.log('进入到删除按钮操作')
|
||||
console.log(row.id)
|
||||
this.$confirm('选中数据将被永久删除, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delDataModel(row.id).then(response => {
|
||||
console.log(response)
|
||||
if (response.code ===0) {
|
||||
this.$message.success('删除成功')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
/** 删除全部,按钮操作 */
|
||||
handleDelAll(row) {
|
||||
console.log('进入到删除按钮操作')
|
||||
console.log(row)
|
||||
this.$confirm('选中数据将被永久删除, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delDataModel(row.id).then(response => {
|
||||
console.log(response)
|
||||
if (response.code === 0) {
|
||||
this.$message.success('删除成功')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
console.log(`每页 ${val} 条`)
|
||||
this.queryParams.pageNum = 1
|
||||
this.queryParams.pageSize = val
|
||||
this.getList()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
console.log(`当前页: ${val}`)
|
||||
this.queryParams.pageNum = val
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.right-toolbar {
|
||||
float: right;
|
||||
}
|
||||
.el-card ::v-deep .el-card__body {
|
||||
height: calc(100vh - 170px);
|
||||
}
|
||||
</style>
|
||||
37
src/views/compare/dcJobInstance/dcJobInstance.vue
Normal file
37
src/views/compare/dcJobInstance/dcJobInstance.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div className="app-container">
|
||||
<transition name="el-zoom-in-center">
|
||||
<dc-job-instance-list v-if="options.showList" @showCard="showCard" />
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import dcJobInstanceList from './dcJobInstanceList'
|
||||
|
||||
export default {
|
||||
name: 'DcJobInstance',
|
||||
components: { dcJobInstanceList },
|
||||
data() {
|
||||
return {
|
||||
options: {
|
||||
data: {},
|
||||
showList: true,
|
||||
showAdd: false,
|
||||
showEdit: false,
|
||||
showDetail: false
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showCard(data) {
|
||||
Object.assign(this.options, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
|
||||
447
src/views/compare/dcJobInstance/dcJobInstanceList.vue
Normal file
447
src/views/compare/dcJobInstance/dcJobInstanceList.vue
Normal file
@@ -0,0 +1,447 @@
|
||||
<template>
|
||||
<el-card class="box-card" shadow="always">
|
||||
<el-form ref="queryForm" :model="queryParams" :inline="true">
|
||||
<el-form-item label="jobconfigId" prop="id">
|
||||
<el-input
|
||||
v-model="queryParams.jobconfigId"
|
||||
placeholder="请输入JobconfigId"
|
||||
clearable
|
||||
size="small"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableDataList"
|
||||
border
|
||||
tooltip-effect="dark"
|
||||
:size="tableSize"
|
||||
:height="tableHeight"
|
||||
style="width: 100%;margin: 15px 0;"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<template v-for="(item, index) in tableColumns">
|
||||
<el-table-column
|
||||
v-if="item.show"
|
||||
:key="index"
|
||||
:prop="item.prop"
|
||||
:label="item.label"
|
||||
:formatter="item.formatter"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
</template>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
size="medium"
|
||||
type="text"
|
||||
icon="el-icon-document"
|
||||
@click="handleDetail(scope.row)"
|
||||
>详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 模态框:查看详情-->
|
||||
<el-dialog title="Job实例列表详细" :visible.sync="dialogFormVisible" width="1000px">
|
||||
<div style="text-align: center;font-size: 18pt;margin-bottom: 20px">量级对比</div>
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="180px">
|
||||
<div style="display: flex;align-items: center;justify-content: center">
|
||||
<el-form-item style="width: 70%;" label="源表" prop="originTableName" required>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 70%" label="目标表" prop="originTablePrimary" required>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div style="display: flex">
|
||||
<el-form-item style="width: 70%" label="originTablePv" prop="originTablePv" required>
|
||||
<el-input v-model="temp.originTablePv" style="width: 85%" disabled/>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 70%" label="toTablePv" prop="toTablePv" required>
|
||||
<el-input v-model="temp.toTablePv" style="width: 85%" disabled/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div style="display: flex">
|
||||
<el-form-item style="width: 70%" label="originTableUv" prop="originTableUv" required>
|
||||
<el-input v-model="temp.originTableUv" style="width: 85%" disabled/>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 70%" label="toTableUv" prop="toTableUv" required>
|
||||
<el-input v-model="temp.toTableUv" style="width: 85%" disabled/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div style="display: flex">
|
||||
<el-form-item style="width: 70%" label="pvDiff" prop="pvDiff" required>
|
||||
<el-input v-model="temp.pvDiff" style="width: 85%" disabled/>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 70%" label="uvDiff" prop="uvDiff" required>
|
||||
<el-input v-model="temp.uvDiff" style="width: 85%" disabled/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div style="display: flex">
|
||||
<el-form-item style="width: 95%" label="magnitudeSql" prop="magnitudeSql" required>
|
||||
<el-input
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 2, maxRows: 8}"
|
||||
disabled
|
||||
height="150"
|
||||
v-model="temp.magnitudeSql">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div style="display: flex">
|
||||
<el-form-item style="width: 95%" label="量级是否通过" prop="magnitudeSql" required>
|
||||
<el-input
|
||||
disabled
|
||||
height="150"
|
||||
style="color:red"
|
||||
:value="temp.pvDiff==0 &&temp.uvDiff==0 ? 'true':'false'"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<!--一致性对比-->
|
||||
<div style="text-align: center;font-size: 18pt;margin-bottom: 20px">一致性对比</div>
|
||||
<div style="display: flex">
|
||||
<el-form-item style="width: 70%" label="originTableCount" prop="originTableCount" required>
|
||||
<el-input v-model="temp.originTableCount" style="width: 85%" disabled/>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 70%" label="uvDiff" prop="uvDiff" required>
|
||||
<el-input v-model="temp.uvDiff" style="width: 85%" disabled/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div style="display: flex">
|
||||
<el-form-item style="width: 50%" label="countDiff" prop="countDiff" required>
|
||||
<el-input v-model="temp.countDiff" style="width: 85%" disabled/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div style="display: flex">
|
||||
<el-form-item style="width: 95%" label="magnitudeSql" prop="magnitudeSql" required>
|
||||
<el-input
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 2, maxRows: 8}"
|
||||
disabled
|
||||
height="150"
|
||||
v-model="temp.consistencySql">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div style="display: flex">
|
||||
<el-form-item style="width: 95%" label="一致性是否通过" prop="magnitudeSql" required>
|
||||
<el-input
|
||||
disabled
|
||||
height="150"
|
||||
style="color:red"
|
||||
:value="temp.countDiff == temp.originTableCount && temp.countDiff == temp.toTableCount ? 'true':'false'"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<div style="text-align: center;margin: 10px 0">
|
||||
<el-button style="font-size: 16pt;background-color: #13ce66;border-radius: 5px" @click="getDiffDetail(temp.id)">
|
||||
查看差异
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 模态框:查看差异-->
|
||||
<el-dialog title="差异case" :visible.sync="dialogFormVisible2" width="1000px">
|
||||
<el-table
|
||||
:data="tableData"
|
||||
style="width: 100%;"
|
||||
height="250"
|
||||
stripe
|
||||
>
|
||||
<el-table-column
|
||||
prop="base_dict_code"
|
||||
label="base_dict_code"
|
||||
width="140">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="base_dict_label"
|
||||
label="base_dict_label"
|
||||
width="140">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="verify_dict_label"
|
||||
label="verify_dict_label"
|
||||
width="140">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="dict_label_is_pass"
|
||||
label="dict_label_is_pass"
|
||||
width="140">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="base_dict_value"
|
||||
label="base_dict_value"
|
||||
width="140">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="verify_dict_value"
|
||||
label="verify_dict_value"
|
||||
width="140">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="dict_value_is_pass"
|
||||
label="dict_value_is_pass"
|
||||
width="140">
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
<el-pagination
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:current-page.sync="queryParams.pageNum"
|
||||
:page-size.sync="queryParams.pageSize"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pageDataModel, getDiffDetail } from '@/api/compare/dcJobInstance/datamodel'
|
||||
export default {
|
||||
name: 'DcJobInstanceList',
|
||||
data() {
|
||||
return {
|
||||
tableHeight: document.body.offsetHeight - 310 + 'px',
|
||||
// 展示切换
|
||||
showOptions: {
|
||||
data: {},
|
||||
showList: true,
|
||||
showAdd: false,
|
||||
showEdit: false,
|
||||
showDetail: false
|
||||
},
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
dialogFormVisible: false,
|
||||
dialogFormVisible2: false,
|
||||
textMap: {
|
||||
update: '编辑Job配置',
|
||||
detail: 'Jobconfig详情'
|
||||
},
|
||||
dialogStatus: '',
|
||||
// 表单提交内容
|
||||
temp: {
|
||||
id: '',
|
||||
originTablePv: '',
|
||||
toTablePv: '',
|
||||
originTableUv: '',
|
||||
toTableUv: '',
|
||||
pvDiff: '',
|
||||
uvDiff: '',
|
||||
magnitudeSql: '',
|
||||
countDiff: '',
|
||||
originTableCount: '',
|
||||
consistencySql: ''
|
||||
},
|
||||
// 规则
|
||||
rules: {
|
||||
name: [{ required: true, message: '不能为空', trigger: 'blur' }],
|
||||
description: [{ required: true, message: '不能为空', trigger: 'blur' }]
|
||||
},
|
||||
// 表格头
|
||||
tableColumns: [
|
||||
{ prop: 'id', label: 'id', show: true },
|
||||
{ prop: 'jobconfigId', label: 'jobconfigId', show: true },
|
||||
{ prop: 'originTableName', label: 'originTableName', show: true },
|
||||
{
|
||||
prop: 'toTableName',
|
||||
label: 'toTableName',
|
||||
show: true
|
||||
},
|
||||
{ prop: 'countDiff', label: '量级校验通过', show: true,
|
||||
formatter: function (value, row, index) {
|
||||
if (value.pvDiff == '0' && value.uvDiff == '0') {
|
||||
return '是'
|
||||
} else {
|
||||
return '否'
|
||||
}
|
||||
}
|
||||
},
|
||||
{ prop: 'countDiff', label: '一致性校验通过', show: true,
|
||||
formatter: function (value, row, index) {
|
||||
if (value.countDiff == value.originTableCount && value.countDiff == value.toTableCount) {
|
||||
return '是'
|
||||
} else {
|
||||
return '否'
|
||||
}
|
||||
}
|
||||
},
|
||||
{ prop: 'createTime', label: '创建时间', show: true }
|
||||
],
|
||||
// 默认选择中表格头
|
||||
checkedTableColumns: [],
|
||||
tableSize: 'medium',
|
||||
// 状态数据字典
|
||||
statusOptions: [],
|
||||
// 表格数据
|
||||
tableDataList: [],
|
||||
// 总数据条数
|
||||
total: 0,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
jobconfigId: ''
|
||||
},
|
||||
// 流程状态数据字典
|
||||
flowStatusOptions: [],
|
||||
// 查看差异数组
|
||||
tableData: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getDicts('sys_common_status').then(response => {
|
||||
if (response.success) {
|
||||
this.statusOptions = response.data
|
||||
}
|
||||
})
|
||||
this.getDicts('sys_flow_status').then(response => {
|
||||
if (response.success) {
|
||||
this.flowStatusOptions = response.data
|
||||
}
|
||||
})
|
||||
this.getList()
|
||||
},
|
||||
mounted() {
|
||||
this.initCols()
|
||||
},
|
||||
methods: {
|
||||
/** 查询数据源列表 */
|
||||
getList() {
|
||||
const _this = this
|
||||
// console.log("hello world1")
|
||||
_this.loading = true
|
||||
pageDataModel(this.queryParams).then(response => {
|
||||
_this.loading = false
|
||||
if (response.code ===0) {
|
||||
_this.tableDataList = response.rows
|
||||
_this.total = response.total
|
||||
}
|
||||
})
|
||||
},
|
||||
initCols() {
|
||||
this.checkedTableColumns = this.tableColumns.map(col => col.prop)
|
||||
},
|
||||
handleCheckedColsChange(val) {
|
||||
this.tableColumns.forEach(col => {
|
||||
if (!this.checkedTableColumns.includes(col.prop)) {
|
||||
col.show = false
|
||||
} else {
|
||||
col.show = true
|
||||
}
|
||||
})
|
||||
},
|
||||
handleCommand(command) {
|
||||
this.tableSize = command
|
||||
},
|
||||
// 查看差异
|
||||
getDiffDetail(id) {
|
||||
|
||||
console.log('查看差异', id)
|
||||
const _this = this
|
||||
// console.log("hello world1")
|
||||
_this.loading = true
|
||||
getDiffDetail(id).then(response => {
|
||||
_this.loading = false
|
||||
console.log('response', response)
|
||||
if (response.code ===0) {
|
||||
for (let i = 0; i < response.total; i++) {
|
||||
let array = {
|
||||
base_dict_code: '--',
|
||||
base_dict_label: '--',
|
||||
verify_dict_label: '--',
|
||||
dict_label_is_pass: '--',
|
||||
base_dict_value: '--',
|
||||
verify_dict_value: '--',
|
||||
dict_value_is_pass: '--'
|
||||
}
|
||||
array.base_dict_code = response.rows[i].base_dict_code || '--'
|
||||
array.base_dict_label = response.rows[i].base_dict_label || '--'
|
||||
array.verify_dict_label = response.rows[i].verify_dict_label || '--'
|
||||
array.dict_label_is_pass = response.rows[i].dict_label_is_pass || '--'
|
||||
array.base_dict_value = response.rows[i].base_dict_value || '--'
|
||||
array.verify_dict_value = response.rows[i].verify_dict_value || '--'
|
||||
array.dict_value_is_pass = response.rows[i].dict_value_is_pass || '--'
|
||||
console.log('array', array)
|
||||
_this.tableData.push(array)
|
||||
}
|
||||
this.dialogFormVisible2 = true
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 详情按钮操作 */
|
||||
handleDetail(row) {
|
||||
console.log(row)
|
||||
console.log('详情展开')
|
||||
this.temp = Object.assign({}, row)
|
||||
this.dialogStatus = 'detail'
|
||||
this.dialogFormVisible = true
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.queryParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
jobconfigId: ''
|
||||
}
|
||||
this.handleQuery()
|
||||
},
|
||||
/** 刷新列表 */
|
||||
handleRefresh() {
|
||||
this.getList()
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
console.log(`每页 ${val} 条`)
|
||||
this.queryParams.pageNum = 1
|
||||
this.queryParams.pageSize = val
|
||||
this.getList()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
console.log(`当前页: ${val}`)
|
||||
this.queryParams.pageNum = val
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.right-toolbar {
|
||||
float: right;
|
||||
}
|
||||
.el-card ::v-deep .el-card__body {
|
||||
height: calc(100vh - 170px);
|
||||
}
|
||||
.el-table .warning-row {
|
||||
background: oldlace;
|
||||
}
|
||||
|
||||
.el-table .success-row {
|
||||
background: #f0f9eb;
|
||||
}
|
||||
</style>
|
||||
37
src/views/compare/dcJobLog/dcJobLog.vue
Normal file
37
src/views/compare/dcJobLog/dcJobLog.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div className="app-container">
|
||||
<transition name="el-zoom-in-center">
|
||||
<dc-job-log-list v-if="options.showList" @showCard="showCard" />
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import dcJobLogList from './dcJobLogList'
|
||||
|
||||
export default {
|
||||
name: 'DcJobLog',
|
||||
components: { dcJobLogList },
|
||||
data() {
|
||||
return {
|
||||
options: {
|
||||
data: {},
|
||||
showList: true,
|
||||
showAdd: false,
|
||||
showEdit: false,
|
||||
showDetail: false
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showCard(data) {
|
||||
Object.assign(this.options, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
|
||||
301
src/views/compare/dcJobLog/dcJobLogList.vue
Normal file
301
src/views/compare/dcJobLog/dcJobLogList.vue
Normal file
@@ -0,0 +1,301 @@
|
||||
<template>
|
||||
<el-card class="box-card" shadow="always">
|
||||
<el-form ref="queryForm" :model="queryParams" :inline="true">
|
||||
<el-form-item label="jobLogId" prop="jobLogId">
|
||||
<el-input
|
||||
v-model="queryParams.jobLogId"
|
||||
placeholder="jobLogId"
|
||||
clearable
|
||||
size="small"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableDataList"
|
||||
border
|
||||
tooltip-effect="dark"
|
||||
:size="tableSize"
|
||||
:height="tableHeight"
|
||||
style="width: 100%;margin: 15px 0;"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<template v-for="(item, index) in tableColumns">
|
||||
<el-table-column
|
||||
v-if="item.show"
|
||||
:key="index"
|
||||
:prop="item.prop"
|
||||
:label="item.label"
|
||||
:formatter="item.formatter"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
</template>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-popover
|
||||
placement="left"
|
||||
trigger="click"
|
||||
>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-document"
|
||||
@click="handleDetail(scope.row)"
|
||||
>详情</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
>删除</el-button>
|
||||
<el-button slot="reference">操作</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 模态框:添加数据-->
|
||||
<el-dialog title="调度日志详情" :visible.sync="dialogFormVisible" width="800px">
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="150px">
|
||||
<el-form-item label="日志编号" prop="jobLogId" required>
|
||||
<el-input v-model="temp.jobLogId" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任务名称" prop="jobName" required>
|
||||
<el-input v-model="temp.jobName" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="源表主键" prop="jobGroup" required>
|
||||
<el-input v-model="temp.jobGroup" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="调用目标字符串" prop="invokeTarget">
|
||||
<el-input v-model="temp.invokeTarget" style="width: 70%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="日志信息" prop="jobMessage">
|
||||
<el-input v-model="temp.jobMessage" style="width: 70%" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="状态" prop="status" required>
|
||||
<el-input v-model="temp.status" placeholder="目标表" disabled style="width: 70%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-pagination
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:current-page.sync="queryParams.pageNum"
|
||||
:page-size.sync="queryParams.pageSize"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pageDataModel, delDataModel } from '@/api/compare/dcJobLog/datamodel'
|
||||
export default {
|
||||
name: 'DcJobLogList',
|
||||
data() {
|
||||
return {
|
||||
tableHeight: document.body.offsetHeight - 310 + 'px',
|
||||
// 展示切换
|
||||
showOptions: {
|
||||
data: {},
|
||||
showList: true,
|
||||
showAdd: false,
|
||||
showEdit: false,
|
||||
showDetail: false
|
||||
},
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
dialogFormVisible: false,
|
||||
dialogFormVisible2: false,
|
||||
dialogStatus: '',
|
||||
temp: {
|
||||
jobLogId: '',
|
||||
jobName: '',
|
||||
jobGroup: '',
|
||||
invokeTarget: '',
|
||||
jobMessage: '',
|
||||
status: ''
|
||||
},
|
||||
rules: {
|
||||
name: [{ required: true, message: '不能为空', trigger: 'blur' }],
|
||||
description: [{ required: true, message: '不能为空', trigger: 'blur' }]
|
||||
},
|
||||
// 表格头
|
||||
tableColumns: [
|
||||
{ prop: 'jobLogId', label: '日志编号', show: true },
|
||||
{ prop: 'jobName', label: '任务名称', show: true },
|
||||
{ prop: 'jobGroup', label: '源表主键', show: true },
|
||||
{
|
||||
prop: 'invokeTarget',
|
||||
label: '调用目标字符串',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
prop: 'jobMessage',
|
||||
label: '日志信息',
|
||||
show: true
|
||||
},
|
||||
{ prop: 'status', label: '状态', show: true,
|
||||
formatter: function (value, row, index) {
|
||||
if (value.status==0) {
|
||||
return '成功'
|
||||
} else {
|
||||
return '失败'
|
||||
}
|
||||
}}
|
||||
],
|
||||
// 默认选择中表格头
|
||||
checkedTableColumns: [],
|
||||
tableSize: 'medium',
|
||||
// 状态数据字典
|
||||
statusOptions: [],
|
||||
// 表格数据
|
||||
tableDataList: [],
|
||||
// 总数据条数
|
||||
total: 0,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
jobLogId: ''
|
||||
},
|
||||
// 流程状态数据字典
|
||||
flowStatusOptions: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getDicts('sys_common_status').then(response => {
|
||||
if (response.success) {
|
||||
this.statusOptions = response.data
|
||||
}
|
||||
})
|
||||
this.getDicts('sys_flow_status').then(response => {
|
||||
if (response.success) {
|
||||
this.flowStatusOptions = response.data
|
||||
}
|
||||
})
|
||||
this.getList()
|
||||
},
|
||||
mounted() {
|
||||
this.initCols()
|
||||
},
|
||||
methods: {
|
||||
/** 查询数据源列表 */
|
||||
getList() {
|
||||
const _this = this
|
||||
// console.log("hello world1")
|
||||
_this.loading = true
|
||||
pageDataModel(this.queryParams).then(response => {
|
||||
// console.log("hello world2")
|
||||
// console.log(response.total)
|
||||
// console.log(response.code)
|
||||
_this.loading = false
|
||||
if (response.code ===0) {
|
||||
_this.tableDataList = response.rows
|
||||
_this.total = response.total
|
||||
// console.log("hello world3")
|
||||
// console.log(_this.tableDataList)
|
||||
// console.log(_this.total)
|
||||
}
|
||||
})
|
||||
},
|
||||
initCols() {
|
||||
this.checkedTableColumns = this.tableColumns.map(col => col.prop)
|
||||
},
|
||||
handleCheckedColsChange(val) {
|
||||
this.tableColumns.forEach(col => {
|
||||
if (!this.checkedTableColumns.includes(col.prop)) {
|
||||
col.show = false
|
||||
} else {
|
||||
col.show = true
|
||||
}
|
||||
})
|
||||
},
|
||||
handleCommand(command) {
|
||||
this.tableSize = command
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.queryParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
jobLogId: ''
|
||||
}
|
||||
this.handleQuery()
|
||||
},
|
||||
/** 刷新列表 */
|
||||
handleRefresh() {
|
||||
this.getList()
|
||||
},
|
||||
/** 详情按钮操作 */
|
||||
handleDetail(row) {
|
||||
this.temp = Object.assign({}, row) // copy obj
|
||||
console.log(row)
|
||||
if (row.status == 0) {
|
||||
this.temp.status = '成功'
|
||||
} else {
|
||||
this.temp.status = '失败'
|
||||
}
|
||||
this.dialogStatus = 'detail'
|
||||
this.dialogFormVisible = true
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
console.log('进入到删除按钮操作')
|
||||
console.log(row)
|
||||
this.$confirm('选中数据将被永久删除, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delDataModel(row.jobLogId).then(response => {
|
||||
console.log(response)
|
||||
if (response.code ===0) {
|
||||
this.$message.success('删除成功')
|
||||
this.getList()
|
||||
}
|
||||
})
|
||||
}).catch(() => {
|
||||
})
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
console.log(`每页 ${val} 条`)
|
||||
this.queryParams.pageNum = 1
|
||||
this.queryParams.pageSize = val
|
||||
this.getList()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
console.log(`当前页: ${val}`)
|
||||
this.queryParams.pageNum = val
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.right-toolbar {
|
||||
float: right;
|
||||
}
|
||||
.el-card ::v-deep .el-card__body {
|
||||
height: calc(100vh - 170px);
|
||||
}
|
||||
</style>
|
||||
110
src/views/components/Echarts.vue
Normal file
110
src/views/components/Echarts.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="dashboard-container">
|
||||
<div class="dashboard-editor-container">
|
||||
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
|
||||
<heat-map />
|
||||
</el-row>
|
||||
<el-row :gutter="32">
|
||||
<el-col :xs="24" :sm="24" :lg="8">
|
||||
<div class="chart-wrapper">
|
||||
<radar-chart />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :lg="8">
|
||||
<div class="chart-wrapper">
|
||||
<sunburst />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :lg="8">
|
||||
<div class="chart-wrapper">
|
||||
<gauge />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<div class="chart-wrapper">
|
||||
<rich />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="chart-wrapper">
|
||||
<theme-river />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="32">
|
||||
<el-col :xs="24" :sm="24" :lg="12">
|
||||
<div class="chart-wrapper">
|
||||
<graph />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :lg="12">
|
||||
<div class="chart-wrapper">
|
||||
<sankey />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<div class="chart-wrapper">
|
||||
<scatter />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="chart-wrapper">
|
||||
<point />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
|
||||
<div class="chart-wrapper">
|
||||
<category />
|
||||
</div>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RadarChart from '@/components/Echarts/RadarChart'
|
||||
import HeatMap from '@/components/Echarts/HeatMap'
|
||||
import Gauge from '@/components/Echarts/Gauge'
|
||||
import Rich from '@/components/Echarts/Rich'
|
||||
import ThemeRiver from '@/components/Echarts/ThemeRiver'
|
||||
import Sunburst from '@/components/Echarts/Sunburst'
|
||||
import Graph from '@/components/Echarts/Graph'
|
||||
import Sankey from '@/components/Echarts/Sankey'
|
||||
import Scatter from '@/components/Echarts/Scatter'
|
||||
import Category from '@/components/Echarts/Category'
|
||||
import Point from '@/components/Echarts/Point'
|
||||
|
||||
export default {
|
||||
name: 'Echarts',
|
||||
components: {
|
||||
Point,
|
||||
Category,
|
||||
Graph,
|
||||
HeatMap,
|
||||
RadarChart,
|
||||
Sunburst,
|
||||
Gauge,
|
||||
Rich,
|
||||
ThemeRiver,
|
||||
Sankey,
|
||||
Scatter
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.dashboard-editor-container {
|
||||
padding: 18px 22px 22px 22px;
|
||||
background-color: rgb(240, 242, 245);
|
||||
.chart-wrapper {
|
||||
background: #fff;
|
||||
padding: 16px 16px 0;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
74
src/views/components/Editor.vue
Normal file
74
src/views/components/Editor.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<p class="warn-content">
|
||||
富文本基于
|
||||
<el-link type="primary" href="https://www.kancloud.cn/wangfupeng/wangeditor3/332599" target="_blank">wangEditor</el-link>
|
||||
</p>
|
||||
<el-row :gutter="10">
|
||||
<el-col :xs="24" :sm="24" :md="15" :lg="15" :xl="15">
|
||||
<div ref="editor" class="text" />
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="9" :lg="9" :xl="9">
|
||||
<div v-html="editorContent" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { upload } from '@/utils/upload'
|
||||
import E from 'wangeditor'
|
||||
export default {
|
||||
name: 'Editor',
|
||||
data() {
|
||||
return {
|
||||
editorContent:
|
||||
`
|
||||
<ul>
|
||||
<li>更多帮助请查看官方文档:<a style="color: #42b983" target="_blank" href="https://www.wangeditor.com/doc/">wangEditor</a></li>
|
||||
</ul>
|
||||
`
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'imagesUploadApi',
|
||||
'baseApi'
|
||||
])
|
||||
},
|
||||
mounted() {
|
||||
const _this = this
|
||||
var editor = new E(this.$refs.editor)
|
||||
// 自定义菜单配置
|
||||
editor.config.zIndex = 5
|
||||
// 文件上传
|
||||
editor.config.customUploadImg = function(files, insert) {
|
||||
// files 是 input 中选中的文件列表
|
||||
// insert 是获取图片 url 后,插入到编辑器的方法
|
||||
files.forEach(image => {
|
||||
upload(_this.imagesUploadApi, image).then(res => {
|
||||
const data = res.data
|
||||
const url = _this.baseApi + '/file/' + data.type + '/' + data.realName
|
||||
insert(url)
|
||||
})
|
||||
})
|
||||
}
|
||||
editor.config.onchange = (html) => {
|
||||
this.editorContent = html
|
||||
}
|
||||
editor.create()
|
||||
// 初始化数据
|
||||
editor.txt.html(this.editorContent)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.text {
|
||||
text-align:left;
|
||||
}
|
||||
::v-deep .w-e-text-container {
|
||||
height: 420px !important;
|
||||
}
|
||||
</style>
|
||||
55
src/views/components/MarkDown.vue
Normal file
55
src/views/components/MarkDown.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<p class="warn-content">
|
||||
Markdown 基于
|
||||
<el-link type="primary" href="https://github.com/hinesboy/mavonEditor" target="_blank">MavonEditor</el-link>
|
||||
</p>
|
||||
<mavon-editor ref="md" :style="'height:' + height" @imgAdd="imgAdd" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { upload } from '@/utils/upload'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { mavonEditor } from 'mavon-editor'
|
||||
import 'mavon-editor/dist/css/index.css'
|
||||
|
||||
export default {
|
||||
name: 'Markdown',
|
||||
components: {
|
||||
mavonEditor
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
height: document.documentElement.clientHeight - 200 + 'px'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'imagesUploadApi',
|
||||
'baseApi'
|
||||
])
|
||||
},
|
||||
mounted() {
|
||||
const that = this
|
||||
window.onresize = function temp() {
|
||||
that.height = document.documentElement.clientHeight - 200 + 'px'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
imgAdd(pos, $file) {
|
||||
upload(this.imagesUploadApi, $file).then(res => {
|
||||
const data = res.data
|
||||
const url = this.baseApi + '/file/' + data.type + '/' + data.realName
|
||||
this.$refs.md.$img2Url(pos, url)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.v-note-wrapper.shadow {
|
||||
z-index: 5;
|
||||
}
|
||||
</style>
|
||||
207
src/views/components/YamlEdit.vue
Normal file
207
src/views/components/YamlEdit.vue
Normal file
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<p class="warn-content">
|
||||
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 :value="value" :height="height" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Yaml from '@/components/YamlEdit/index'
|
||||
export default {
|
||||
name: 'YamlEdit',
|
||||
components: { Yaml },
|
||||
data() {
|
||||
return {
|
||||
height: document.documentElement.clientHeight - 210 + 'px',
|
||||
value: '# 展示数据,如需更换主题,请在src/components/YamlEdit 目录中搜索原主题名称进行替换\n' +
|
||||
'\n' +
|
||||
'# ===================================================================\n' +
|
||||
'# Spring Boot configuration.\n' +
|
||||
'#\n' +
|
||||
'# This configuration will be overridden by the Spring profile you use,\n' +
|
||||
'# for example application-dev.yml if you use the "dev" profile.\n' +
|
||||
'#\n' +
|
||||
'# More information on profiles: https://www.jhipster.tech/profiles/\n' +
|
||||
'# More information on configuration properties: https://www.jhipster.tech/common-application-properties/\n' +
|
||||
'# ===================================================================\n' +
|
||||
'\n' +
|
||||
'# ===================================================================\n' +
|
||||
'# Standard Spring Boot properties.\n' +
|
||||
'# Full reference is available at:\n' +
|
||||
'# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html\n' +
|
||||
'# ===================================================================\n' +
|
||||
'\n' +
|
||||
'eureka:\n' +
|
||||
' client:\n' +
|
||||
' enabled: true\n' +
|
||||
' healthcheck:\n' +
|
||||
' enabled: true\n' +
|
||||
' fetch-registry: true\n' +
|
||||
' register-with-eureka: true\n' +
|
||||
' instance-info-replication-interval-seconds: 10\n' +
|
||||
' registry-fetch-interval-seconds: 10\n' +
|
||||
' instance:\n' +
|
||||
' appname: product\n' +
|
||||
' instanceId: product:${spring.application.instance-id:${random.value}}\n' +
|
||||
' #instanceId: 127.0.0.1:9080\n' +
|
||||
' lease-renewal-interval-in-seconds: 5\n' +
|
||||
' lease-expiration-duration-in-seconds: 10\n' +
|
||||
' status-page-url-path: ${management.endpoints.web.base-path}/info\n' +
|
||||
' health-check-url-path: ${management.endpoints.web.base-path}/health\n' +
|
||||
' metadata-map:\n' +
|
||||
' zone: primary # This is needed for the load balancer\n' +
|
||||
' profile: ${spring.profiles.active}\n' +
|
||||
' version: ${info.project.version:}\n' +
|
||||
' git-version: ${git.commit.id.describe:}\n' +
|
||||
' git-commit: ${git.commit.id.abbrev:}\n' +
|
||||
' git-branch: ${git.branch:}\n' +
|
||||
'ribbon:\n' +
|
||||
' ReadTimeout: 120000\n' +
|
||||
' ConnectTimeout: 300000\n' +
|
||||
' eureka:\n' +
|
||||
' enabled: true\n' +
|
||||
'zuul:\n' +
|
||||
' host:\n' +
|
||||
' connect-timeout-millis: 5000\n' +
|
||||
' max-per-route-connections: 10000\n' +
|
||||
' max-total-connections: 5000\n' +
|
||||
' socket-timeout-millis: 60000\n' +
|
||||
' semaphore:\n' +
|
||||
' max-semaphores: 500\n' +
|
||||
'\n' +
|
||||
'feign:\n' +
|
||||
' hystrix:\n' +
|
||||
' enabled: true\n' +
|
||||
' client:\n' +
|
||||
' config:\n' +
|
||||
' default:\n' +
|
||||
' connectTimeout: 500000\n' +
|
||||
' readTimeout: 500000\n' +
|
||||
'\n' +
|
||||
'# See https://github.com/Netflix/Hystrix/wiki/Configuration\n' +
|
||||
'hystrix:\n' +
|
||||
' command:\n' +
|
||||
' default:\n' +
|
||||
' circuitBreaker:\n' +
|
||||
' sleepWindowInMilliseconds: 100000\n' +
|
||||
' forceClosed: true\n' +
|
||||
' execution:\n' +
|
||||
' isolation:\n' +
|
||||
'# strategy: SEMAPHORE\n' +
|
||||
'# See https://github.com/spring-cloud/spring-cloud-netflix/issues/1330\n' +
|
||||
' thread:\n' +
|
||||
' timeoutInMilliseconds: 60000\n' +
|
||||
' shareSecurityContext: true\n' +
|
||||
'\n' +
|
||||
'management:\n' +
|
||||
' endpoints:\n' +
|
||||
' web:\n' +
|
||||
' base-path: /management\n' +
|
||||
' exposure:\n' +
|
||||
' include: ["configprops", "env", "health", "info", "threaddump"]\n' +
|
||||
' endpoint:\n' +
|
||||
' health:\n' +
|
||||
' show-details: when_authorized\n' +
|
||||
' info:\n' +
|
||||
' git:\n' +
|
||||
' mode: full\n' +
|
||||
' health:\n' +
|
||||
' mail:\n' +
|
||||
' enabled: false # When using the MailService, configure an SMTP server and set this to true\n' +
|
||||
' metrics:\n' +
|
||||
' enabled: false # http://micrometer.io/ is disabled by default, as we use http://metrics.dropwizard.io/ instead\n' +
|
||||
'\n' +
|
||||
'spring:\n' +
|
||||
' application:\n' +
|
||||
' name: product\n' +
|
||||
' jpa:\n' +
|
||||
' open-in-view: false\n' +
|
||||
' hibernate:\n' +
|
||||
' ddl-auto: update\n' +
|
||||
' naming:\n' +
|
||||
' physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy\n' +
|
||||
' implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy\n' +
|
||||
' messages:\n' +
|
||||
' basename: i18n/messages\n' +
|
||||
' mvc:\n' +
|
||||
' favicon:\n' +
|
||||
' enabled: false\n' +
|
||||
' thymeleaf:\n' +
|
||||
' mode: HTML\n' +
|
||||
'security:\n' +
|
||||
' oauth2:\n' +
|
||||
' resource:\n' +
|
||||
' filter-order: 3\n' +
|
||||
'\n' +
|
||||
'server:\n' +
|
||||
' servlet:\n' +
|
||||
' session:\n' +
|
||||
' cookie:\n' +
|
||||
' http-only: true\n' +
|
||||
'\n' +
|
||||
'# Properties to be exposed on the /info management endpoint\n' +
|
||||
'info:\n' +
|
||||
' # Comma separated list of profiles that will trigger the ribbon to show\n' +
|
||||
' display-ribbon-on-profiles: "dev"\n' +
|
||||
'\n' +
|
||||
'# ===================================================================\n' +
|
||||
'# JHipster specific properties\n' +
|
||||
'#\n' +
|
||||
'# Full reference is available at: https://www.jhipster.tech/common-application-properties/\n' +
|
||||
'# ===================================================================\n' +
|
||||
'\n' +
|
||||
'jhipster:\n' +
|
||||
' async:\n' +
|
||||
' core-pool-size: 2\n' +
|
||||
' max-pool-size: 50\n' +
|
||||
' queue-capacity: 10000\n' +
|
||||
' # By default CORS is disabled. Uncomment to enable.\n' +
|
||||
' #cors:\n' +
|
||||
' #allowed-origins: "*"\n' +
|
||||
' #allowed-methods: "*"\n' +
|
||||
' #allowed-headers: "*"\n' +
|
||||
' #exposed-headers: "Authorization,Link,X-Total-Count"\n' +
|
||||
' #allow-credentials: true\n' +
|
||||
' #max-age: 1800\n' +
|
||||
' mail:\n' +
|
||||
' from: product@localhost\n' +
|
||||
' swagger:\n' +
|
||||
' default-include-pattern: /api/.*\n' +
|
||||
' title: product API\n' +
|
||||
' description: product API documentation\n' +
|
||||
' version: 0.0.1\n' +
|
||||
' terms-of-service-url:\n' +
|
||||
' contact-name:\n' +
|
||||
' contact-url:\n' +
|
||||
' contact-email:\n' +
|
||||
' license:\n' +
|
||||
' license-url:\n' +
|
||||
'\n' +
|
||||
'# ===================================================================\n' +
|
||||
'# Application specific properties\n' +
|
||||
'# Add your own application properties here, see the ApplicationProperties class\n' +
|
||||
'# to have type-safe configuration, like in the JHipsterProperties above\n' +
|
||||
'#\n' +
|
||||
'# More documentation is available at:\n' +
|
||||
'# https://www.jhipster.tech/common-application-properties/\n' +
|
||||
'# ===================================================================\n' +
|
||||
'\n' +
|
||||
'# application:\n'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const that = this
|
||||
window.onresize = function temp() {
|
||||
that.height = document.documentElement.clientHeight - 210 + 'px'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
41
src/views/components/excel/upload-excel.vue
Normal file
41
src/views/components/excel/upload-excel.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<upload-excel-component :on-success="handleSuccess" :before-upload="beforeUpload" />
|
||||
<el-table :data="tableData" border highlight-current-row style="width: 100%;margin-top:20px;">
|
||||
<el-table-column v-for="item of tableHeader" :key="item" :prop="item" :label="item" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import UploadExcelComponent from '@/components/UploadExcel/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'UploadExcel',
|
||||
components: { UploadExcelComponent },
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
tableHeader: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
beforeUpload(file) {
|
||||
const isLt1M = file.size / 1024 / 1024 < 1
|
||||
if (isLt1M) {
|
||||
return true
|
||||
}
|
||||
|
||||
this.$message({
|
||||
message: '请不要上传大于1m的文件.',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
},
|
||||
handleSuccess({ results, header }) {
|
||||
this.tableData = results
|
||||
this.tableHeader = header
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
74
src/views/components/icons/element-icons.js
Normal file
74
src/views/components/icons/element-icons.js
Normal file
@@ -0,0 +1,74 @@
|
||||
const elementIcons = [
|
||||
'info',
|
||||
'error',
|
||||
'success',
|
||||
'warning',
|
||||
'question',
|
||||
'back',
|
||||
'arrow-left',
|
||||
'arrow-down',
|
||||
'arrow-right',
|
||||
'arrow-up',
|
||||
'caret-left',
|
||||
'caret-bottom',
|
||||
'caret-top',
|
||||
'caret-right',
|
||||
'd-arrow-left',
|
||||
'd-arrow-right',
|
||||
'minus',
|
||||
'plus',
|
||||
'remove',
|
||||
'circle-plus',
|
||||
'remove-outline',
|
||||
'circle-plus-outline',
|
||||
'close',
|
||||
'check',
|
||||
'circle-close',
|
||||
'circle-check',
|
||||
'circle-close-outline',
|
||||
'circle-check-outline',
|
||||
'zoom-out',
|
||||
'zoom-in',
|
||||
'd-caret',
|
||||
'sort',
|
||||
'sort-down',
|
||||
'sort-up',
|
||||
'tickets',
|
||||
'document',
|
||||
'goods',
|
||||
'sold-out',
|
||||
'news',
|
||||
'message',
|
||||
'date',
|
||||
'printer',
|
||||
'time',
|
||||
'bell',
|
||||
'mobile-phone',
|
||||
'service',
|
||||
'view',
|
||||
'menu',
|
||||
'more',
|
||||
'more-outline',
|
||||
'star-on',
|
||||
'star-off',
|
||||
'location',
|
||||
'location-outline',
|
||||
'phone',
|
||||
'phone-outline',
|
||||
'picture',
|
||||
'picture-outline',
|
||||
'delete',
|
||||
'search',
|
||||
'edit',
|
||||
'edit-outline',
|
||||
'rank',
|
||||
'refresh',
|
||||
'share',
|
||||
'setting',
|
||||
'upload',
|
||||
'upload2',
|
||||
'download',
|
||||
'loading'
|
||||
]
|
||||
|
||||
export default elementIcons
|
||||
97
src/views/components/icons/index.vue
Normal file
97
src/views/components/icons/index.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div class="icons-container">
|
||||
<aside>
|
||||
<a href="https://panjiachen.github.io/vue-element-admin-site/guide/advanced/icon.html" target="_blank">Add and use
|
||||
</a>
|
||||
</aside>
|
||||
<el-tabs type="border-card">
|
||||
<el-tab-pane label="Icons">
|
||||
<div class="grid">
|
||||
<div v-for="item of svgIcons" :key="item" @click="handleClipboard(generateIconCode(item),$event)">
|
||||
<el-tooltip placement="top">
|
||||
<div slot="content">
|
||||
{{ generateIconCode(item) }}
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<svg-icon :icon-class="item" class-name="disabled" />
|
||||
<span>{{ item }}</span>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="Element-UI Icons">
|
||||
<div class="grid">
|
||||
<div v-for="item of elementIcons" :key="item" @click="handleClipboard(generateElementIconCode(item),$event)">
|
||||
<el-tooltip placement="top">
|
||||
<div slot="content">
|
||||
{{ generateElementIconCode(item) }}
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<i :class="'el-icon-' + item" />
|
||||
<span>{{ item }}</span>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import clipboard from '@/utils/clipboard'
|
||||
import svgIcons from './svg-icons'
|
||||
import elementIcons from './element-icons'
|
||||
export default {
|
||||
name: 'Icons',
|
||||
data() {
|
||||
return {
|
||||
svgIcons,
|
||||
elementIcons
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateIconCode(symbol) {
|
||||
return `<svg-icon icon-class="${symbol}" />`
|
||||
},
|
||||
generateElementIconCode(symbol) {
|
||||
return `<i class="el-icon-${symbol}" />`
|
||||
},
|
||||
handleClipboard(text, event) {
|
||||
clipboard(text, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.icons-container {
|
||||
margin: 10px 20px 0;
|
||||
overflow: hidden;
|
||||
|
||||
.grid {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
}
|
||||
.icon-item {
|
||||
margin: 20px;
|
||||
height: 85px;
|
||||
text-align: center;
|
||||
width: 100px;
|
||||
float: left;
|
||||
font-size: 30px;
|
||||
color: #24292e;
|
||||
cursor: pointer;
|
||||
}
|
||||
span {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
10
src/views/components/icons/svg-icons.js
Normal file
10
src/views/components/icons/svg-icons.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const req = require.context('../../../assets/icons/svg', false, /\.svg$/)
|
||||
const requireAll = requireContext => requireContext.keys()
|
||||
|
||||
const re = /\.\/(.*)\.svg/
|
||||
|
||||
const svgIcons = requireAll(req).map(i => {
|
||||
return i.match(re)[1]
|
||||
})
|
||||
|
||||
export default svgIcons
|
||||
135
src/views/dashboard/LineChart.vue
Normal file
135
src/views/dashboard/LineChart.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<div :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
require('echarts/theme/macarons') // echarts theme
|
||||
import resize from './mixins/resize'
|
||||
|
||||
export default {
|
||||
mixins: [resize],
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '350px'
|
||||
},
|
||||
autoResize: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
chartData: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
chartData: {
|
||||
deep: true,
|
||||
handler(val) {
|
||||
this.setOptions(val)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.initChart()
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (!this.chart) {
|
||||
return
|
||||
}
|
||||
this.chart.dispose()
|
||||
this.chart = null
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
this.chart = echarts.init(this.$el, 'macarons')
|
||||
this.setOptions(this.chartData)
|
||||
},
|
||||
setOptions({ expectedData, actualData } = {}) {
|
||||
this.chart.setOption({
|
||||
xAxis: {
|
||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||
boundaryGap: false,
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: 10,
|
||||
right: 10,
|
||||
bottom: 20,
|
||||
top: 30,
|
||||
containLabel: true
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
},
|
||||
padding: [5, 10]
|
||||
},
|
||||
yAxis: {
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['expected', 'actual']
|
||||
},
|
||||
series: [{
|
||||
name: 'expected', itemStyle: {
|
||||
normal: {
|
||||
color: '#FF005A',
|
||||
lineStyle: {
|
||||
color: '#FF005A',
|
||||
width: 2
|
||||
}
|
||||
}
|
||||
},
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
data: expectedData,
|
||||
animationDuration: 2800,
|
||||
animationEasing: 'cubicInOut'
|
||||
},
|
||||
{
|
||||
name: 'actual',
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: '#3888fa',
|
||||
lineStyle: {
|
||||
color: '#3888fa',
|
||||
width: 2
|
||||
},
|
||||
areaStyle: {
|
||||
color: '#f3f8ff'
|
||||
}
|
||||
}
|
||||
},
|
||||
data: actualData,
|
||||
animationDuration: 2800,
|
||||
animationEasing: 'quadraticOut'
|
||||
}]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
181
src/views/dashboard/PanelGroup.vue
Normal file
181
src/views/dashboard/PanelGroup.vue
Normal file
@@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<el-row :gutter="40" class="panel-group">
|
||||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
||||
<div class="card-panel" @click="handleSetLineChartData('newVisitis')">
|
||||
<div class="card-panel-icon-wrapper icon-people">
|
||||
<svg-icon icon-class="peoples" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">
|
||||
New Visits
|
||||
</div>
|
||||
<count-to :start-val="0" :end-val="102400" :duration="2600" class="card-panel-num" />
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
||||
<div class="card-panel" @click="handleSetLineChartData('messages')">
|
||||
<div class="card-panel-icon-wrapper icon-message">
|
||||
<svg-icon icon-class="message" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">
|
||||
Messages
|
||||
</div>
|
||||
<count-to :start-val="0" :end-val="81212" :duration="3000" class="card-panel-num" />
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
||||
<div class="card-panel" @click="handleSetLineChartData('purchases')">
|
||||
<div class="card-panel-icon-wrapper icon-money">
|
||||
<svg-icon icon-class="money" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">
|
||||
Purchases
|
||||
</div>
|
||||
<count-to :start-val="0" :end-val="9280" :duration="3200" class="card-panel-num" />
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
||||
<div class="card-panel" @click="handleSetLineChartData('shoppings')">
|
||||
<div class="card-panel-icon-wrapper icon-shopping">
|
||||
<svg-icon icon-class="shopping" class-name="card-panel-icon" />
|
||||
</div>
|
||||
<div class="card-panel-description">
|
||||
<div class="card-panel-text">
|
||||
Shoppings
|
||||
</div>
|
||||
<count-to :start-val="0" :end-val="13600" :duration="3600" class="card-panel-num" />
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CountTo from 'vue-count-to'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CountTo
|
||||
},
|
||||
methods: {
|
||||
handleSetLineChartData(type) {
|
||||
this.$emit('handleSetLineChartData', type)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.panel-group {
|
||||
margin-top: 18px;
|
||||
|
||||
.card-panel-col {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.card-panel {
|
||||
height: 108px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
color: #666;
|
||||
background: #fff;
|
||||
box-shadow: 4px 4px 40px rgba(0, 0, 0, .05);
|
||||
border-color: rgba(0, 0, 0, .05);
|
||||
|
||||
&:hover {
|
||||
.card-panel-icon-wrapper {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.icon-people {
|
||||
background: #40c9c6;
|
||||
}
|
||||
|
||||
.icon-message {
|
||||
background: #36a3f7;
|
||||
}
|
||||
|
||||
.icon-money {
|
||||
background: #f4516c;
|
||||
}
|
||||
|
||||
.icon-shopping {
|
||||
background: #34bfa3
|
||||
}
|
||||
}
|
||||
|
||||
.icon-people {
|
||||
color: #40c9c6;
|
||||
}
|
||||
|
||||
.icon-message {
|
||||
color: #36a3f7;
|
||||
}
|
||||
|
||||
.icon-money {
|
||||
color: #f4516c;
|
||||
}
|
||||
|
||||
.icon-shopping {
|
||||
color: #34bfa3
|
||||
}
|
||||
|
||||
.card-panel-icon-wrapper {
|
||||
float: left;
|
||||
margin: 14px 0 0 14px;
|
||||
padding: 16px;
|
||||
transition: all 0.38s ease-out;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.card-panel-icon {
|
||||
float: left;
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
.card-panel-description {
|
||||
float: right;
|
||||
font-weight: bold;
|
||||
margin: 26px;
|
||||
margin-left: 0px;
|
||||
|
||||
.card-panel-text {
|
||||
line-height: 18px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 16px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.card-panel-num {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width:550px) {
|
||||
.card-panel-description {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.card-panel-icon-wrapper {
|
||||
float: none !important;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0 !important;
|
||||
|
||||
.svg-icon {
|
||||
display: block;
|
||||
margin: 14px auto !important;
|
||||
float: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
55
src/views/dashboard/mixins/resize.js
Normal file
55
src/views/dashboard/mixins/resize.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
$_sidebarElm: null,
|
||||
$_resizeHandler: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$_resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
this.$_initResizeEvent()
|
||||
this.$_initSidebarResizeEvent()
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$_destroyResizeEvent()
|
||||
this.$_destroySidebarResizeEvent()
|
||||
},
|
||||
// to fixed bug when cached by keep-alive
|
||||
// https://github.com/PanJiaChen/vue-element-admin/issues/2116
|
||||
activated() {
|
||||
this.$_initResizeEvent()
|
||||
this.$_initSidebarResizeEvent()
|
||||
},
|
||||
deactivated() {
|
||||
this.$_destroyResizeEvent()
|
||||
this.$_destroySidebarResizeEvent()
|
||||
},
|
||||
methods: {
|
||||
// use $_ for mixins properties
|
||||
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
|
||||
$_initResizeEvent() {
|
||||
window.addEventListener('resize', this.$_resizeHandler)
|
||||
},
|
||||
$_destroyResizeEvent() {
|
||||
window.removeEventListener('resize', this.$_resizeHandler)
|
||||
},
|
||||
$_sidebarResizeHandler(e) {
|
||||
if (e.propertyName === 'width') {
|
||||
this.$_resizeHandler()
|
||||
}
|
||||
},
|
||||
$_initSidebarResizeEvent() {
|
||||
this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
|
||||
this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
|
||||
},
|
||||
$_destroySidebarResizeEvent() {
|
||||
this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
|
||||
}
|
||||
}
|
||||
}
|
||||
228
src/views/dts/executor/index.vue
Normal file
228
src/views/dts/executor/index.vue
Normal file
@@ -0,0 +1,228 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="filter-container">
|
||||
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleCreate">
|
||||
添加
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
element-loading-text="Loading"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column label="排序" width="50" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.order }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="名称" width="120" align="center" :show-overflow-tooltip="true">
|
||||
<template slot-scope="scope">{{ scope.row.title }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分组标识" width="200" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.appName }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="注册方式" width="110" align="center" :show-overflow-tooltip="true">
|
||||
<template slot-scope="scope"> {{ addressTypes.find(t => t.value === scope.row.addressType).label }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="在线机器" align="center" :show-overflow-tooltip="true">
|
||||
<template slot-scope="scope">{{ scope.row.addressList }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="230" class-name="small-padding fixed-width">
|
||||
<template slot-scope="{row}">
|
||||
<el-button type="primary" size="mini" @click="handleUpdate(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button v-if="row.status!='deleted'" size="mini" type="danger" @click="handleDelete(row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-show="total>0" :total="total" :page.sync="listQuery.current" :limit.sync="listQuery.size" @pagination="fetchData" />
|
||||
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="80px" style="width: 400px; margin-left:50px;">
|
||||
<el-form-item label="AppName" prop="appName">
|
||||
<el-input v-model="temp.appName" placeholder="AppName" />
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" prop="title">
|
||||
<el-input v-model="temp.title" placeholder="请输入执行器名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="prop">
|
||||
<el-input v-model="temp.order" placeholder="执行器序号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="注册方式" prop="addressType">
|
||||
<el-radio-group v-model="temp.addressType">
|
||||
<el-radio :label="0">自动注册</el-radio>
|
||||
<el-radio :label="1">手动录入</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="机器地址" prop="addressList">
|
||||
<el-input v-model="temp.addressList" :disabled="dialogStatus!=='create'" placeholder="多个以逗号分隔" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as executor from '@/api/dts/datax-executor'
|
||||
import waves from '@/directive/waves' // waves directive
|
||||
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
|
||||
|
||||
export default {
|
||||
name: 'Executor',
|
||||
components: { Pagination },
|
||||
directives: { waves },
|
||||
filters: {
|
||||
statusFilter(status) {
|
||||
const statusMap = {
|
||||
published: 'success',
|
||||
draft: 'gray',
|
||||
deleted: 'danger'
|
||||
}
|
||||
return statusMap[status]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: null,
|
||||
listLoading: true,
|
||||
total: 0,
|
||||
listQuery: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
name: undefined,
|
||||
jobGroup: undefined
|
||||
},
|
||||
editJsonVisible: false,
|
||||
pluginData: [],
|
||||
dialogFormVisible: false,
|
||||
dialogStatus: '',
|
||||
textMap: {
|
||||
update: 'Edit',
|
||||
create: 'Create'
|
||||
},
|
||||
rules: {
|
||||
appName: [{ required: true, message: 'appName is required', trigger: 'blur' }],
|
||||
title: [{ required: true, message: 'title is required', trigger: 'blur' }],
|
||||
order: [{ required: true, message: 'title is required', trigger: 'blur' }],
|
||||
addressType: [{ required: true, message: 'title is required', trigger: 'change' }]
|
||||
},
|
||||
temp: {
|
||||
id: undefined,
|
||||
appName: undefined,
|
||||
title: undefined,
|
||||
order: undefined,
|
||||
addressType: undefined,
|
||||
addressList: undefined
|
||||
},
|
||||
addressTypes: [
|
||||
{ value: 0, label: '自动注册' },
|
||||
{ value: 1, label: '手动录入' }
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
fetchData() {
|
||||
this.listLoading = true
|
||||
executor.getList().then(response => {
|
||||
const { content } = response
|
||||
this.list = content
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
resetTemp() {
|
||||
this.temp = {
|
||||
id: undefined,
|
||||
appName: undefined,
|
||||
title: undefined,
|
||||
order: undefined,
|
||||
addressType: undefined,
|
||||
addressList: undefined
|
||||
}
|
||||
},
|
||||
handleCreate() {
|
||||
this.resetTemp()
|
||||
this.dialogStatus = 'create'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
createData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
executor.created(this.temp).then(() => {
|
||||
this.fetchData()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Created Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleUpdate(row) {
|
||||
this.temp = Object.assign({}, row) // copy obj
|
||||
this.dialogStatus = 'update'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
updateData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
const tempData = Object.assign({}, this.temp)
|
||||
tempData.configJson = this.configJson
|
||||
executor.updated(tempData).then(() => {
|
||||
this.fetchData()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Update Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleDelete(row) {
|
||||
executor.deleted(row.id).then(response => {
|
||||
this.fetchData()
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Delete Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
// const index = this.list.indexOf(row)
|
||||
},
|
||||
handleFetchPv(id) {
|
||||
executor.fetch(id).then(response => {
|
||||
this.pluginData = response
|
||||
this.dialogPvVisible = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
426
src/views/dts/jdbc-datasource/index.vue
Normal file
426
src/views/dts/jdbc-datasource/index.vue
Normal file
@@ -0,0 +1,426 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="filter-container">
|
||||
<el-input
|
||||
v-model="listQuery.datasourceName"
|
||||
clearable
|
||||
placeholder="数据源名称"
|
||||
style="width: 200px;"
|
||||
class="filter-item"
|
||||
@keyup.enter.native="handleFilter"
|
||||
/>
|
||||
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="fetchData">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleCreate">
|
||||
添加
|
||||
</el-button>
|
||||
<!-- <el-checkbox v-model="showReviewer" class="filter-item" style="margin-left:15px;" @change="tableKey=tableKey+1">
|
||||
reviewer
|
||||
</el-checkbox> -->
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
element-loading-text="Loading"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
>
|
||||
<!-- <el-table-column align="center" label="序号" width="95">
|
||||
<template slot-scope="scope">{{ scope.$index }}</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="数据源" width="120" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.datasource }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数据源名称" width="150" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.datasourceName }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数据源分组" width="120" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.datasourceGroup }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!--<el-table-column label="用户名" width="150" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.jdbcUsername ? scope.row.jdbcUsername:'-' }}</template>
|
||||
</el-table-column>-->
|
||||
<el-table-column label="jdbc连接串" width="300" align="center" :show-overflow-tooltip="true">
|
||||
<template slot-scope="scope">{{ scope.row.jdbcUrl ? scope.row.jdbcUrl:'-' }}</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="jdbc驱动类" width="200" align="center" :show-overflow-tooltip="true">
|
||||
<template slot-scope="scope">{{ scope.row.jdbcDriverClass ? scope.row.jdbcDriverClass:'-' }}</template>
|
||||
</el-table-column>-->
|
||||
<el-table-column label="ZK地址" width="200" align="center" :show-overflow-tooltip="true">
|
||||
<template slot-scope="scope">{{ scope.row.zkAdress ? scope.row.zkAdress:'-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数据库名" width="200" align="center" :show-overflow-tooltip="true">-->
|
||||
<template slot-scope="scope">{{ scope.row.databaseName ? scope.row.databaseName:'-' }}</template>-->
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="备注" width="150" align="center">-->
|
||||
<!-- <template slot-scope="scope">{{ scope.row.comments }}</template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="操作" align="center" width="230" class-name="small-padding fixed-width">
|
||||
<template slot-scope="{row}">
|
||||
<el-button type="primary" size="mini" @click="handleUpdate(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button v-if="row.status!='deleted'" size="mini" type="danger" @click="handleDelete(row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="listQuery.current"
|
||||
:limit.sync="listQuery.size"
|
||||
@pagination="fetchData"
|
||||
/>
|
||||
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="800px">
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="100px">
|
||||
<el-form-item label="数据源" prop="datasource">
|
||||
<el-select
|
||||
v-model="temp.datasource"
|
||||
placeholder="数据源"
|
||||
style="width: 200px"
|
||||
@change="selectDataSource(temp.datasource)"
|
||||
>
|
||||
<el-option v-for="item in dataSources" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据源名称" prop="datasourceName">
|
||||
<el-input v-model="temp.datasourceName" placeholder="数据源名称" style="width: 40%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="数据源分组" prop="datasourceGroup">
|
||||
<el-input v-model="temp.datasourceGroup" placeholder="数据源分组" style="width: 40%" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="jdbc" label="用户名">
|
||||
<el-input v-model="temp.jdbcUsername" placeholder="用户名" style="width: 40%" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="visible" v-show="jdbc" label="密码">
|
||||
<el-input v-model="temp.jdbcPassword" type="password" placeholder="密码" style="width: 40%">
|
||||
<i slot="suffix" title="显示密码" style="cursor:pointer" class="el-icon-view" @click="changePass('show')" />
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="jdbc" v-else label="密码">
|
||||
<el-input v-model="temp.jdbcPassword" type="text" placeholder="密码" style="width: 40%">
|
||||
<i slot="suffix" title="隐藏密码" style="cursor:pointer" class="el-icon-check" @click="changePass('hide')" />
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="jdbc" label="jdbc url" prop="jdbcUrl">
|
||||
<el-input
|
||||
v-model="temp.jdbcUrl"
|
||||
:autosize="{ minRows: 3, maxRows: 6}"
|
||||
type="textarea"
|
||||
placeholder="jdbc url"
|
||||
style="width: 60%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="mongodb" label="地址" prop="jdbcUrl">
|
||||
<el-input
|
||||
v-model="temp.jdbcUrl"
|
||||
:autosize="{ minRows: 3, maxRows: 6}"
|
||||
type="textarea"
|
||||
placeholder="localhost:27017"
|
||||
style="width: 60%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="jdbc" label="jdbc驱动类" prop="jdbcDriverClass">
|
||||
<el-input v-model="temp.jdbcDriverClass" placeholder="jdbc驱动类" style="width: 60%" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="hbase" label="ZK地址" prop="zkAdress">
|
||||
<el-input v-model="temp.zkAdress" placeholder="localhost:2181" style="width: 60%" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="mongodb" label="数据库名称" prop="databaseName">
|
||||
<el-input v-model="temp.databaseName" placeholder="数据库名称" style="width: 60%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="注释">
|
||||
<el-input
|
||||
v-model="temp.comments"
|
||||
:autosize="{ minRows: 2, maxRows: 4}"
|
||||
type="textarea"
|
||||
placeholder="Please input"
|
||||
style="width: 60%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
|
||||
确认
|
||||
</el-button>
|
||||
<el-button type="primary" @click="testDataSource()">
|
||||
测试连接
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog :visible.sync="dialogPluginVisible" title="Reading statistics">
|
||||
<el-table :data="pluginData" border fit highlight-current-row style="width: 100%">
|
||||
<el-table-column prop="key" label="Channel" />
|
||||
<el-table-column prop="pv" label="Pv" />
|
||||
</el-table>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="dialogPvVisible = false">Confirm</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as datasourceApi from '@/api/dts/datax-jdbcDatasource'
|
||||
import waves from '@/directive/waves' // waves directive
|
||||
import { parseTime } from '@/utils'
|
||||
import Pagination from '@/components/Pagination'
|
||||
|
||||
export default {
|
||||
name: 'JdbcDatasource',
|
||||
components: { Pagination },
|
||||
directives: { waves },
|
||||
filters: {
|
||||
statusFilter(status) {
|
||||
const statusMap = {
|
||||
published: 'success',
|
||||
draft: 'gray',
|
||||
deleted: 'danger'
|
||||
}
|
||||
return statusMap[status]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: null,
|
||||
listLoading: true,
|
||||
total: 0,
|
||||
listQuery: {
|
||||
current: 1,
|
||||
size: 10
|
||||
},
|
||||
pluginTypeOptions: ['reader', 'writer'],
|
||||
dialogPluginVisible: false,
|
||||
pluginData: [],
|
||||
dialogFormVisible: false,
|
||||
dialogStatus: '',
|
||||
textMap: {
|
||||
update: 'Edit',
|
||||
create: 'Create'
|
||||
},
|
||||
rules: {
|
||||
datasourceName: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
jdbcUsername: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
jdbcPassword: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
jdbcUrl: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
jdbcDriverClass: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
datasource: [{ required: true, message: 'this is required', trigger: 'change' }],
|
||||
zkAdress: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
databaseName: [{ required: true, message: 'this is required', trigger: 'blur' }]
|
||||
},
|
||||
temp: {
|
||||
id: undefined,
|
||||
datasourceName: '',
|
||||
datasourceGroup: 'Default',
|
||||
jdbcUsername: '',
|
||||
jdbcPassword: '',
|
||||
jdbcUrl: '',
|
||||
jdbcDriverClass: '',
|
||||
comments: '',
|
||||
datasource: '',
|
||||
zkAdress: '',
|
||||
databaseName: ''
|
||||
},
|
||||
visible: true,
|
||||
dataSources: [
|
||||
{ value: 'mysql', label: 'mysql' },
|
||||
{ value: 'oracle', label: 'oracle' },
|
||||
{ value: 'hana', label: 'hana' },
|
||||
{ value: 'postgresql', label: 'postgresql' },
|
||||
{ value: 'sqlserver', label: 'sqlserver' },
|
||||
{ value: 'hive', label: 'hive' },
|
||||
{ value: 'hbase', label: 'hbase' },
|
||||
{ value: 'mongodb', label: 'mongodb' },
|
||||
{ value: 'clickhouse', label: 'clickhouse' }
|
||||
],
|
||||
jdbc: true,
|
||||
hbase: false,
|
||||
mongodb: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
selectDataSource(datasource) {
|
||||
if (datasource === 'mysql') {
|
||||
this.temp.jdbcUrl = 'jdbc:mysql://{host}:{port}/{database}'
|
||||
this.temp.jdbcDriverClass = 'com.mysql.jdbc.Driver'
|
||||
} else if (datasource === 'hana') {
|
||||
this.temp.jdbcUrl = 'jdbc:sap://{host}:{port}/{instanceNumber}'
|
||||
this.temp.jdbcDriverClass = 'com.sap.db.jdbc.Driver'
|
||||
} else if (datasource === 'oracle') {
|
||||
this.temp.jdbcUrl = 'jdbc:oracle:thin:@//{host}:{port}/{serviceName}'
|
||||
this.temp.jdbcDriverClass = 'oracle.jdbc.OracleDriver'
|
||||
} else if (datasource === 'postgresql') {
|
||||
this.temp.jdbcUrl = 'jdbc:postgresql://{host}:{port}/{database}'
|
||||
this.temp.jdbcDriverClass = 'org.postgresql.Driver'
|
||||
} else if (datasource === 'sqlserver') {
|
||||
this.temp.jdbcUrl = 'jdbc:jtds:sqlserver://{host}:{port};DatabaseName={database}'
|
||||
this.temp.jdbcDriverClass = 'net.sourceforge.jtds.jdbc.Driver'
|
||||
} else if (datasource === 'clickhouse') {
|
||||
this.temp.jdbcUrl = 'jdbc:clickhouse://{host}:{port}/{database}'
|
||||
this.temp.jdbcDriverClass = 'ru.yandex.clickhouse.ClickHouseDriver'
|
||||
} else if (datasource === 'hive') {
|
||||
this.temp.jdbcUrl = 'jdbc:hive2://{host}:{port}/{database}'
|
||||
this.temp.jdbcDriverClass = 'org.apache.hive.jdbc.HiveDriver'
|
||||
this.hbase = this.mongodb = false
|
||||
this.jdbc = true
|
||||
}
|
||||
this.getShowStrategy(datasource)
|
||||
},
|
||||
fetchData() {
|
||||
this.listLoading = true
|
||||
datasourceApi.list(this.listQuery).then(response => {
|
||||
const { data } = response
|
||||
const { total } = data.total
|
||||
this.total = total
|
||||
this.list = data.records
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
resetTemp() {
|
||||
this.temp = {
|
||||
id: undefined,
|
||||
datasourceName: '',
|
||||
datasourceGroup: 'Default',
|
||||
jdbcUsername: '',
|
||||
jdbcPassword: '',
|
||||
jdbcUrl: '',
|
||||
jdbcDriverClass: '',
|
||||
comments: ''
|
||||
}
|
||||
},
|
||||
handleCreate() {
|
||||
this.resetTemp()
|
||||
this.dialogStatus = 'create'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
createData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
datasourceApi.created(this.temp).then(() => {
|
||||
this.fetchData()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Created Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
testDataSource() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
datasourceApi.test(this.temp).then(response => {
|
||||
if (response.data === false) {
|
||||
this.$notify({
|
||||
title: 'Fail',
|
||||
message: response.data.msg,
|
||||
type: 'fail',
|
||||
duration: 2000
|
||||
})
|
||||
} else {
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Tested Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleUpdate(row) {
|
||||
this.getShowStrategy(row.datasource)
|
||||
this.temp = Object.assign({}, row) // copy obj
|
||||
this.dialogStatus = 'update'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
updateData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
const tempData = Object.assign({}, this.temp)
|
||||
datasourceApi.updated(tempData).then(() => {
|
||||
this.fetchData()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Update Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
getShowStrategy(datasource) {
|
||||
if (datasource === 'hbase') {
|
||||
this.jdbc = this.mongodb = false
|
||||
this.hbase = true
|
||||
} else if (datasource === 'mongodb') {
|
||||
this.jdbc = this.hbase = false
|
||||
this.mongodb = true
|
||||
this.temp.jdbcUrl = 'mongodb://[username:password@]host1[:port1][,...hostN[:portN]]][/[database][?options]]'
|
||||
} else {
|
||||
this.hbase = this.mongodb = false
|
||||
this.jdbc = true
|
||||
}
|
||||
},
|
||||
handleDelete(row) {
|
||||
console.log('删除')
|
||||
const idList = []
|
||||
idList.push(row.id)
|
||||
// 拼成 idList=xx
|
||||
// 多个比较麻烦,这里不处理
|
||||
datasourceApi.deleted({ idList: row.id }).then(response => {
|
||||
this.fetchData()
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Delete Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
// const index = this.list.indexOf(row)
|
||||
},
|
||||
handleFetchPv(id) {
|
||||
datasourceApi.fetched(id).then(response => {
|
||||
this.pluginData = response
|
||||
this.dialogPvVisible = true
|
||||
})
|
||||
},
|
||||
formatJson(filterVal, jsonData) {
|
||||
return jsonData.map(v => filterVal.map(j => {
|
||||
if (j === 'timestamp') {
|
||||
return parseTime(v[j])
|
||||
} else {
|
||||
return v[j]
|
||||
}
|
||||
}))
|
||||
},
|
||||
changePass(value) {
|
||||
this.visible = !(value === 'show')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
758
src/views/dts/jobInfo/index.vue
Normal file
758
src/views/dts/jobInfo/index.vue
Normal file
@@ -0,0 +1,758 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="filter-container">
|
||||
<el-input v-model="listQuery.jobDesc" placeholder="任务名称" style="width: 200px;" class="filter-item" />
|
||||
<el-select v-model="projectIds" multiple placeholder="所属项目" class="filter-item">
|
||||
<el-option v-for="item in jobProjectList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
<el-select v-model="listQuery.glueType" placeholder="任务类型" style="width: 200px" class="filter-item">
|
||||
<el-option v-for="item in glueTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="fetchData">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleCreate">
|
||||
添加
|
||||
</el-button>
|
||||
<!-- <el-checkbox v-model="showReviewer" class="filter-item" style="margin-left:15px;" @change="tableKey=tableKey+1">
|
||||
reviewer
|
||||
</el-checkbox> -->
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
element-loading-text="Loading"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
style="width: 100%"
|
||||
size="medium"
|
||||
>
|
||||
<el-table-column align="center" label="ID" width="80">
|
||||
<template slot-scope="scope">{{ scope.row.id }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="任务名称" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.jobDesc }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="所属项目" align="center" width="120">
|
||||
<template slot-scope="scope">{{ scope.row.projectName }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Cron" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.jobCron }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="路由策略" align="center" width="130">
|
||||
<template slot-scope="scope"> {{ routeStrategies.find(t => t.value === scope.row.executorRouteStrategy).label }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.triggerStatus"
|
||||
active-color="#00A854"
|
||||
active-text="启动"
|
||||
:active-value="1"
|
||||
inactive-color="#F04134"
|
||||
inactive-text="停止"
|
||||
:inactive-value="0"
|
||||
@change="changeSwitch(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="注册节点" align="center" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
width="500"
|
||||
@show="loadById(scope.row)"
|
||||
>
|
||||
<el-table :data="registerNode">
|
||||
<el-table-column width="150" property="title" label="执行器名称" />
|
||||
<el-table-column width="150" property="appName" label="分组标识" />
|
||||
<el-table-column width="150" property="registryList" label="机器地址" />
|
||||
</el-table>
|
||||
<el-button slot="reference" size="small">查看</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="下次触发时间" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
width="300"
|
||||
@show="nextTriggerTime(scope.row)"
|
||||
>
|
||||
<h5 v-html="triggerNextTimes" />
|
||||
<el-button slot="reference" size="small">查看</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="执行状态" align="center" width="80">
|
||||
<template slot-scope="scope"> {{ statusList.find(t => t.value === scope.row.lastHandleCode).label }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right">
|
||||
<template slot-scope="{row}">
|
||||
<!-- <el-dropdown type="primary" size="small"> -->
|
||||
<!-- 操作 -->
|
||||
<el-dropdown trigger="click">
|
||||
<span class="el-dropdown-link">
|
||||
操作<i class="el-icon-arrow-down el-icon--right" />
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click.native="handlerExecute(row)">执行一次</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handlerViewLog(row)">查询日志</el-dropdown-item>
|
||||
<el-dropdown-item divided @click.native="handlerUpdate(row)">编辑</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handlerDelete(row)">删除</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-show="total>0" :total="total" :page.sync="listQuery.current" :limit.sync="listQuery.size" @pagination="fetchData" />
|
||||
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="1000px" :before-close="handleClose">
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="110px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="执行器" prop="jobGroup">
|
||||
<el-select v-model="temp.jobGroup" placeholder="请选择执行器">
|
||||
<el-option v-for="item in executorList" :key="item.id" :label="item.title" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="任务名称" prop="jobDesc">
|
||||
<el-input v-model="temp.jobDesc" size="medium" placeholder="请输入任务描述" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="路由策略" prop="executorRouteStrategy">
|
||||
<el-select v-model="temp.executorRouteStrategy" placeholder="请选择路由策略">
|
||||
<el-option v-for="item in routeStrategies" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-dialog
|
||||
title="提示"
|
||||
:visible.sync="showCronBox"
|
||||
width="60%"
|
||||
append-to-body
|
||||
>
|
||||
<cron v-model="temp.jobCron" />
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="showCronBox = false;">关闭</el-button>
|
||||
<el-button type="primary" @click="showCronBox = false">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-form-item label="Cron" prop="jobCron">
|
||||
<el-input v-model="temp.jobCron" auto-complete="off" placeholder="请输入Cron表达式">
|
||||
<el-button v-if="!showCronBox" slot="append" icon="el-icon-turn-off" title="打开图形配置" @click="showCronBox = true" />
|
||||
<el-button v-else slot="append" icon="el-icon-open" title="关闭图形配置" @click="showCronBox = false" />
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="阻塞处理" prop="executorBlockStrategy">
|
||||
<el-select v-model="temp.executorBlockStrategy" placeholder="请选择阻塞处理策略">
|
||||
<el-option v-for="item in blockStrategies" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="报警邮件">
|
||||
<el-input v-model="temp.alarmEmail" placeholder="请输入报警邮件,多个用逗号分隔" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="任务类型" prop="glueType">
|
||||
<el-select v-model="temp.glueType" placeholder="任务脚本类型">
|
||||
<el-option v-for="item in glueTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="失败重试次数">
|
||||
<el-input-number v-model="temp.executorFailRetryCount" :min="0" :max="20" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属项目" prop="projectId">
|
||||
<el-select v-model="temp.projectId" placeholder="所属项目" class="filter-item">
|
||||
<el-option v-for="item in jobProjectList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="超时时间(分钟)">
|
||||
<el-input-number v-model="temp.executorTimeout" :min="0" :max="120" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="子任务">
|
||||
<el-select v-model="temp.childJobId" multiple placeholder="子任务" value-key="id">
|
||||
<el-option v-for="item in jobIdList" :key="item.id" :label="item.jobDesc" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" />
|
||||
</el-row>
|
||||
<el-row v-if="temp.glueType==='BEAN'" :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="辅助参数" prop="incrementType">
|
||||
<el-select v-model="temp.incrementType" placeholder="请选择参数类型" value="">
|
||||
<el-option v-for="item in incrementTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="temp.glueType==='BEAN' && temp.incrementType === 1" :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="增量主键开始ID" prop="incStartId">
|
||||
<el-input v-model="temp.incStartId" placeholder="首次增量使用" style="width: 56%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="ID增量参数" prop="replaceParam">
|
||||
<el-input v-model="temp.replaceParam" placeholder="-DstartId='%s' -DendId='%s'" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="reader数据源" prop="datasourceId">
|
||||
<el-select v-model="temp.datasourceId" placeholder="reader数据源" class="filter-item">
|
||||
<el-option v-for="item in dataSourceList" :key="item.id" :label="item.datasourceName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-form-item label="reader表" prop="readerTable">
|
||||
<el-input v-model="temp.readerTable" placeholder="读表的表名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<el-form-item label="主键" label-width="40px" prop="primaryKey">
|
||||
<el-input v-model="temp.primaryKey" placeholder="请填写主键字段名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="temp.glueType==='BEAN' && temp.incrementType === 2" :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="增量开始时间" prop="incStartTime">
|
||||
<el-date-picker
|
||||
v-model="temp.incStartTime"
|
||||
type="datetime"
|
||||
placeholder="首次增量使用"
|
||||
format="yyyy-MM-dd HH:mm:ss"
|
||||
style="width: 57%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="增量时间字段" prop="replaceParam">
|
||||
<el-input v-model="temp.replaceParam" placeholder="-DlastTime='%s' -DcurrentTime='%s'" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="增量时间格式" prop="replaceParamType">
|
||||
<el-select v-model="temp.replaceParamType" placeholder="增量时间格式" @change="incStartTimeFormat">
|
||||
<el-option v-for="item in replaceFormatTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
<el-row v-if="temp.glueType==='BEAN' && temp.incrementType === 3" :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="分区字段" prop="partitionField">
|
||||
<el-input v-model="partitionField" placeholder="请输入分区字段" style="width: 56%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-form-item label="分区时间">
|
||||
<el-select v-model="timeFormatType" placeholder="分区时间格式">
|
||||
<el-option v-for="item in timeFormatTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<el-input-number v-model="timeOffset" :min="-20" :max="0" style="width: 65%" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="temp.glueType==='BEAN'" :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="JVM启动参数">
|
||||
<el-input v-model="temp.jvmParam" placeholder="-Xms1024m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<json-editor v-if="temp.glueType==='BEAN'" ref="jsonEditor" v-model="jobJson" />
|
||||
<shell-editor v-if="temp.glueType==='GLUE_SHELL'" ref="shellEditor" v-model="glueSource" />
|
||||
<python-editor v-if="temp.glueType==='GLUE_PYTHON'" ref="pythonEditor" v-model="glueSource" />
|
||||
<powershell-editor v-if="temp.glueType==='GLUE_POWERSHELL'" ref="powershellEditor" v-model="glueSource" />
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as executor from '@/api/dts/datax-executor'
|
||||
import * as job from '@/api/dts/datax-job-info'
|
||||
import waves from '@/directive/waves' // waves directive
|
||||
import Cron from '@/components/Cron'
|
||||
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
|
||||
import JsonEditor from '@/components/JsonEditor'
|
||||
import ShellEditor from '@/components/ShellEditor'
|
||||
import PythonEditor from '@/components/PythonEditor'
|
||||
import PowershellEditor from '@/components/PowershellEditor'
|
||||
import * as datasourceApi from '@/api/dts/datax-jdbcDatasource'
|
||||
import * as jobProjectApi from '@/api/dts/datax-job-project'
|
||||
import { isJSON } from '@/utils/validate'
|
||||
|
||||
export default {
|
||||
name: 'JobInfo',
|
||||
components: { Pagination, JsonEditor, ShellEditor, PythonEditor, PowershellEditor, Cron },
|
||||
directives: { waves },
|
||||
filters: {
|
||||
statusFilter(status) {
|
||||
const statusMap = {
|
||||
published: 'success',
|
||||
draft: 'gray',
|
||||
deleted: 'danger'
|
||||
}
|
||||
return statusMap[status]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const validateIncParam = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('Increment parameters is required'))
|
||||
}
|
||||
callback()
|
||||
}
|
||||
const validatePartitionParam = (rule, value, callback) => {
|
||||
if (!this.partitionField) {
|
||||
callback(new Error('Partition parameters is required'))
|
||||
}
|
||||
callback()
|
||||
}
|
||||
return {
|
||||
projectIds: '',
|
||||
list: null,
|
||||
listLoading: true,
|
||||
total: 0,
|
||||
listQuery: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
jobGroup: 0,
|
||||
projectIds: '',
|
||||
triggerStatus: -1,
|
||||
jobDesc: '',
|
||||
glueType: ''
|
||||
},
|
||||
showCronBox: false,
|
||||
dialogPluginVisible: false,
|
||||
pluginData: [],
|
||||
dialogFormVisible: false,
|
||||
dialogStatus: '',
|
||||
textMap: {
|
||||
update: 'Edit',
|
||||
create: 'Create'
|
||||
},
|
||||
rules: {
|
||||
jobGroup: [{ required: true, message: 'jobGroup is required', trigger: 'change' }],
|
||||
executorRouteStrategy: [{ required: true, message: 'executorRouteStrategy is required', trigger: 'change' }],
|
||||
executorBlockStrategy: [{ required: true, message: 'executorBlockStrategy is required', trigger: 'change' }],
|
||||
glueType: [{ required: true, message: 'jobType is required', trigger: 'change' }],
|
||||
projectId: [{ required: true, message: 'projectId is required', trigger: 'change' }],
|
||||
jobDesc: [{ required: true, message: 'jobDesc is required', trigger: 'blur' }],
|
||||
jobProject: [{ required: true, message: 'jobProject is required', trigger: 'blur' }],
|
||||
jobCron: [{ required: true, message: 'jobCron is required', trigger: 'blur' }],
|
||||
incStartId: [{ trigger: 'blur', validator: validateIncParam }],
|
||||
replaceParam: [{ trigger: 'blur', validator: validateIncParam }],
|
||||
primaryKey: [{ trigger: 'blur', validator: validateIncParam }],
|
||||
incStartTime: [{ trigger: 'change', validator: validateIncParam }],
|
||||
replaceParamType: [{ trigger: 'change', validator: validateIncParam }],
|
||||
partitionField: [{ trigger: 'blur', validator: validatePartitionParam }],
|
||||
datasourceId: [{ trigger: 'change', validator: validateIncParam }],
|
||||
readerTable: [{ trigger: 'blur', validator: validateIncParam }]
|
||||
},
|
||||
temp: {
|
||||
id: undefined,
|
||||
jobGroup: '',
|
||||
jobCron: '',
|
||||
jobDesc: '',
|
||||
executorRouteStrategy: '',
|
||||
executorBlockStrategy: '',
|
||||
childJobId: '',
|
||||
executorFailRetryCount: '',
|
||||
alarmEmail: '',
|
||||
executorTimeout: '',
|
||||
userId: 0,
|
||||
jobConfigId: '',
|
||||
executorHandler: '',
|
||||
glueType: '',
|
||||
glueSource: '',
|
||||
jobJson: '',
|
||||
executorParam: '',
|
||||
replaceParam: '',
|
||||
replaceParamType: 'Timestamp',
|
||||
jvmParam: '',
|
||||
incStartTime: '',
|
||||
partitionInfo: '',
|
||||
incrementType: 0,
|
||||
incStartId: '',
|
||||
primaryKey: '',
|
||||
projectId: '',
|
||||
datasourceId: '',
|
||||
readerTable: ''
|
||||
},
|
||||
resetTemp() {
|
||||
// this.temp = this.$options.data().temp
|
||||
this.jobJson = ''
|
||||
this.glueSource = ''
|
||||
this.timeOffset = 0
|
||||
this.timeFormatType = 'yyyy-MM-dd'
|
||||
this.partitionField = ''
|
||||
},
|
||||
executorList: '',
|
||||
jobIdList: '',
|
||||
jobProjectList: '',
|
||||
dataSourceList: '',
|
||||
blockStrategies: [
|
||||
{ value: 'SERIAL_EXECUTION', label: '单机串行' },
|
||||
{ value: 'DISCARD_LATER', label: '丢弃后续调度' },
|
||||
{ value: 'COVER_EARLY', label: '覆盖之前调度' }
|
||||
],
|
||||
routeStrategies: [
|
||||
{ value: 'FIRST', label: '第一个' },
|
||||
{ value: 'LAST', label: '最后一个' },
|
||||
{ value: 'ROUND', label: '轮询' },
|
||||
{ value: 'RANDOM', label: '随机' },
|
||||
{ value: 'CONSISTENT_HASH', label: '一致性HASH' },
|
||||
{ value: 'LEAST_FREQUENTLY_USED', label: '最不经常使用' },
|
||||
{ value: 'LEAST_RECENTLY_USED', label: '最近最久未使用' },
|
||||
{ value: 'FAILOVER', label: '故障转移' },
|
||||
{ value: 'BUSYOVER', label: '忙碌转移' }
|
||||
// { value: 'SHARDING_BROADCAST', label: '分片广播' }
|
||||
],
|
||||
glueTypes: [
|
||||
{ value: 'BEAN', label: 'FlinkX任务' },
|
||||
{ value: 'GLUE_SHELL', label: 'Shell任务' },
|
||||
{ value: 'GLUE_PYTHON', label: 'Python任务' },
|
||||
{ value: 'GLUE_POWERSHELL', label: 'PowerShell任务' }
|
||||
],
|
||||
incrementTypes: [
|
||||
{ value: 0, label: '无' },
|
||||
{ value: 1, label: '主键自增' },
|
||||
{ value: 2, label: '时间自增' },
|
||||
{ value: 3, label: 'HIVE分区' }
|
||||
],
|
||||
triggerNextTimes: '',
|
||||
registerNode: [],
|
||||
jobJson: '',
|
||||
glueSource: '',
|
||||
timeOffset: 0,
|
||||
timeFormatType: 'yyyy-MM-dd',
|
||||
partitionField: '',
|
||||
timeFormatTypes: [
|
||||
{ value: 'yyyy-MM-dd', label: 'yyyy-MM-dd' },
|
||||
{ value: 'yyyyMMdd', label: 'yyyyMMdd' },
|
||||
{ value: 'yyyy/MM/dd', label: 'yyyy/MM/dd' }
|
||||
],
|
||||
replaceFormatTypes: [
|
||||
{ value: 'yyyy/MM/dd', label: 'yyyy/MM/dd' },
|
||||
{ value: 'yyyy-MM-dd', label: 'yyyy-MM-dd' },
|
||||
{ value: 'HH:mm:ss', label: 'HH:mm:ss' },
|
||||
{ value: 'yyyy/MM/dd HH:mm:ss', label: 'yyyy/MM/dd HH:mm:ss' },
|
||||
{ value: 'yyyy-MM-dd HH:mm:ss', label: 'yyyy-MM-dd HH:mm:ss' },
|
||||
{ value: 'Timestamp', label: '时间戳' }
|
||||
],
|
||||
statusList: [
|
||||
{ value: 500, label: '失败' },
|
||||
{ value: 502, label: '失败(超时)' },
|
||||
{ value: 200, label: '成功' },
|
||||
{ value: 0, label: '无' }
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
this.getExecutor()
|
||||
this.getJobIdList()
|
||||
this.getJobProject()
|
||||
this.getDataSourceList()
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleClose(done) {
|
||||
this.$confirm('确认关闭?')
|
||||
.then(_ => {
|
||||
done()
|
||||
})
|
||||
.catch(_ => {})
|
||||
},
|
||||
getExecutor() {
|
||||
job.getExecutorList().then(response => {
|
||||
const { content } = response
|
||||
this.executorList = content
|
||||
})
|
||||
},
|
||||
getJobIdList() {
|
||||
job.getJobIdList().then(response => {
|
||||
const { content } = response
|
||||
this.jobIdList = content
|
||||
})
|
||||
},
|
||||
getJobProject() {
|
||||
jobProjectApi.getJobProjectList().then(response => {
|
||||
this.jobProjectList = response.data
|
||||
})
|
||||
},
|
||||
getDataSourceList() {
|
||||
datasourceApi.getDataSourceList().then(response => {
|
||||
this.dataSourceList = response
|
||||
})
|
||||
},
|
||||
fetchData() {
|
||||
this.listLoading = true
|
||||
if (this.projectIds) {
|
||||
this.listQuery.projectIds = this.projectIds.toString()
|
||||
}
|
||||
|
||||
job.getList(this.listQuery).then(response => {
|
||||
const { content } = response
|
||||
this.total = content.recordsTotal
|
||||
this.list = content.data
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
incStartTimeFormat(vData) {
|
||||
},
|
||||
handleCreate() {
|
||||
this.resetTemp()
|
||||
this.dialogStatus = 'create'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
createData() {
|
||||
if (this.temp.glueType === 'BEAN' && !isJSON(this.jobJson)) {
|
||||
this.$notify({
|
||||
title: 'Fail',
|
||||
message: 'json格式错误',
|
||||
type: 'error',
|
||||
duration: 2000
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.temp.childJobId) {
|
||||
const auth = []
|
||||
for (const i in this.temp.childJobId) {
|
||||
auth.push(this.temp.childJobId[i].id)
|
||||
}
|
||||
this.temp.childJobId = auth.toString()
|
||||
}
|
||||
this.temp.jobJson = this.jobJson
|
||||
this.temp.glueSource = this.glueSource
|
||||
this.temp.executorHandler = this.temp.glueType === 'BEAN' ? 'executorJobHandler' : ''
|
||||
if (this.partitionField) this.temp.partitionInfo = this.partitionField + ',' + this.timeOffset + ',' + this.timeFormatType
|
||||
job.createJob(this.temp).then(() => {
|
||||
this.fetchData()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Created Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handlerUpdate(row) {
|
||||
// this.resetTemp()
|
||||
this.temp = Object.assign({}, row) // copy obj
|
||||
if (this.temp.jobJson) this.jobJson = JSON.parse(this.temp.jobJson)
|
||||
this.glueSource = this.temp.glueSource
|
||||
const arrchildSet = []
|
||||
const arrJobIdList = []
|
||||
if (this.jobIdList) {
|
||||
for (const n in this.jobIdList) {
|
||||
if (this.jobIdList[n].id !== this.temp.id) {
|
||||
arrJobIdList.push(this.jobIdList[n])
|
||||
}
|
||||
}
|
||||
this.JobIdList = arrJobIdList
|
||||
}
|
||||
|
||||
if (this.temp.childJobId) {
|
||||
const arrString = this.temp.childJobId.split(',')
|
||||
for (const i in arrString) {
|
||||
for (const n in this.jobIdList) {
|
||||
if (this.jobIdList[n].id === parseInt(arrString[i])) {
|
||||
arrchildSet.push(this.jobIdList[n])
|
||||
}
|
||||
}
|
||||
}
|
||||
this.temp.childJobId = arrchildSet
|
||||
}
|
||||
if (this.temp.partitionInfo) {
|
||||
const partition = this.temp.partitionInfo.split(',')
|
||||
this.partitionField = partition[0]
|
||||
this.timeOffset = partition[1]
|
||||
this.timeFormatType = partition[2]
|
||||
}
|
||||
this.dialogStatus = 'update'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
updateData() {
|
||||
this.temp.jobJson = typeof (this.jobJson) !== 'string' ? JSON.stringify(this.jobJson) : this.jobJson
|
||||
if (this.temp.glueType === 'BEAN' && !isJSON(this.temp.jobJson)) {
|
||||
this.$notify({
|
||||
title: 'Fail',
|
||||
message: 'json格式错误',
|
||||
type: 'error',
|
||||
duration: 2000
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.temp.childJobId) {
|
||||
const auth = []
|
||||
for (const i in this.temp.childJobId) {
|
||||
auth.push(this.temp.childJobId[i].id)
|
||||
}
|
||||
this.temp.childJobId = auth.toString()
|
||||
}
|
||||
this.temp.executorHandler = this.temp.glueType === 'BEAN' ? 'executorJobHandler' : ''
|
||||
this.temp.glueSource = this.glueSource
|
||||
if (this.partitionField) this.temp.partitionInfo = this.partitionField + ',' + this.timeOffset + ',' + this.timeFormatType
|
||||
job.updateJob(this.temp).then(() => {
|
||||
this.fetchData()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Update Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handlerDelete(row) {
|
||||
this.$confirm('确定删除吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
job.removeJob(row.id).then(response => {
|
||||
this.fetchData()
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Delete Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// const index = this.list.indexOf(row)
|
||||
},
|
||||
handlerExecute(row) {
|
||||
this.$confirm('确定执行吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
const param = {}
|
||||
param.jobId = row.id
|
||||
param.executorParam = row.executorParam
|
||||
job.triggerJob(param).then(response => {
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Execute Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
// 查看日志
|
||||
handlerViewLog(row) {
|
||||
this.$router.push({ path: '/data/log', query: { jobId: row.id }})
|
||||
},
|
||||
handlerStart(row) {
|
||||
job.startJob(row.id).then(response => {
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Start Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
},
|
||||
handlerStop(row) {
|
||||
job.stopJob(row.id).then(response => {
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Start Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
},
|
||||
changeSwitch(row) {
|
||||
row.triggerStatus === 1 ? this.handlerStart(row) : this.handlerStop(row)
|
||||
},
|
||||
nextTriggerTime(row) {
|
||||
job.nextTriggerTime(row.jobCron).then(response => {
|
||||
const { content } = response
|
||||
this.triggerNextTimes = content.join('<br>')
|
||||
})
|
||||
},
|
||||
loadById(row) {
|
||||
executor.loadById(row.jobGroup).then(response => {
|
||||
this.registerNode = []
|
||||
const { content } = response
|
||||
this.registerNode.push(content)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.el-dropdown-link {
|
||||
cursor: pointer;
|
||||
color: #409EFF;
|
||||
}
|
||||
.el-dropdown + .el-dropdown {
|
||||
margin-left: 15px;
|
||||
}
|
||||
</style>
|
||||
319
src/views/dts/jobLog/index.vue
Normal file
319
src/views/dts/jobLog/index.vue
Normal file
@@ -0,0 +1,319 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="filter-container">
|
||||
<el-input v-model="listQuery.jobId" placeholder="全部" style="width: 200px" />
|
||||
<el-select v-model="listQuery.jobGroup" placeholder="执行器">
|
||||
<el-option v-for="item in executorList" :key="item.id" :label="item.title" :value="item.id" />
|
||||
</el-select>
|
||||
<el-select v-model="listQuery.logStatus" placeholder="类型" style="width: 200px">
|
||||
<el-option v-for="item in logStatusList" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="fetchData">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button
|
||||
class="filter-item"
|
||||
style="margin-left: 10px;"
|
||||
type="primary"
|
||||
icon="el-icon-edit"
|
||||
@click="handlerDelete"
|
||||
>
|
||||
清除
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
element-loading-text="Loading"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column align="center" label="任务ID" width="80">
|
||||
<template slot-scope="scope">{{ scope.row.jobId }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="任务描述">
|
||||
<template slot-scope="scope">{{ scope.row.jobDesc }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="调度时间" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.triggerTime }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="执行结果" align="center">
|
||||
<template slot-scope="scope"> <span :style="`color:${scope.row.handleCode==500?'red':''}`">{{ statusList.find(t => t.value === scope.row.handleCode).label }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="300">
|
||||
<template slot-scope="{row}">
|
||||
<el-button type="primary" @click="handleViewJobLog(row)">日志查看</el-button>
|
||||
<el-button type="primary" @click="killRunningJob(row)">
|
||||
终止任务
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="listQuery.current"
|
||||
:limit.sync="listQuery.size"
|
||||
@pagination="fetchData"
|
||||
/>
|
||||
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="600px">
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="center" label-width="100px">
|
||||
<el-row>
|
||||
<el-col :span="14" :offset="5">
|
||||
<el-form-item label="执行器">
|
||||
<el-input size="medium" value="全部" :disabled="true" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="14" :offset="5">
|
||||
<el-form-item label="任务">
|
||||
<el-input size="medium" value="全部" :disabled="true" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="14" :offset="5">
|
||||
<el-form-item label="执行器">
|
||||
<el-select v-model="temp.deleteType" placeholder="请选择执行器" style="width: 230px">
|
||||
<el-option v-for="item in deleteTypeList" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="deleteLog">
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog title="日志查看" :visible.sync="dialogVisible" width="95%">
|
||||
<div class="log-container">
|
||||
<pre :loading="logLoading" v-text="logContent" />
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">
|
||||
关闭
|
||||
</el-button>
|
||||
<el-button type="primary" @click="loadLog">
|
||||
刷新日志
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as log from '@/api/dts/datax-job-log'
|
||||
import * as job from '@/api/dts/datax-job-info'
|
||||
import waves from '@/directive/waves' // waves directive
|
||||
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
|
||||
|
||||
export default {
|
||||
name: 'JobLog',
|
||||
components: { Pagination },
|
||||
directives: { waves },
|
||||
filters: {
|
||||
statusFilter(status) {
|
||||
const statusMap = {
|
||||
published: 'success',
|
||||
draft: 'gray',
|
||||
deleted: 'danger'
|
||||
}
|
||||
return statusMap[status]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
list: null,
|
||||
listLoading: true,
|
||||
total: 0,
|
||||
listQuery: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
jobGroup: 0,
|
||||
jobId: '',
|
||||
logStatus: -1,
|
||||
filterTime: ''
|
||||
},
|
||||
dialogPluginVisible: false,
|
||||
pluginData: [],
|
||||
dialogFormVisible: false,
|
||||
dialogStatus: '',
|
||||
executorList: '',
|
||||
textMap: {
|
||||
create: 'Clear'
|
||||
},
|
||||
rules: {},
|
||||
temp: {
|
||||
deleteType: 1,
|
||||
jobGroup: 0,
|
||||
jobId: 0
|
||||
},
|
||||
statusList: [
|
||||
{ value: 500, label: '失败' },
|
||||
{ value: 502, label: '失败(超时)' },
|
||||
{ value: 200, label: '成功' },
|
||||
{ value: 0, label: '无' }
|
||||
],
|
||||
deleteTypeList: [
|
||||
{ value: 1, label: '清理一个月之前日志数据' },
|
||||
{ value: 2, label: '清理三个月之前日志数据' },
|
||||
{ value: 3, label: '清理六个月之前日志数据' },
|
||||
{ value: 4, label: '清理一年之前日志数据' },
|
||||
{ value: 5, label: '清理一千条以前日志数据' },
|
||||
{ value: 6, label: '清理一万条以前日志数据' },
|
||||
{ value: 7, label: '清理三万条以前日志数据' },
|
||||
{ value: 8, label: '清理十万条以前日志数据' },
|
||||
{ value: 9, label: '清理所有日志数据' }
|
||||
],
|
||||
logStatusList: [
|
||||
{ value: -1, label: '全部' },
|
||||
{ value: 1, label: '成功' },
|
||||
{ value: 2, label: '失败' },
|
||||
{ value: 3, label: '进行中' }
|
||||
],
|
||||
// 日志查询参数
|
||||
jobLogQuery: {
|
||||
executorAddress: '',
|
||||
triggerTime: '',
|
||||
id: '',
|
||||
fromLineNum: 1
|
||||
},
|
||||
// 日志内容
|
||||
logContent: '',
|
||||
// 显示日志
|
||||
logShow: false,
|
||||
// 日志显示加载中效果
|
||||
logLoading: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
this.getExecutor()
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchData() {
|
||||
this.listLoading = true
|
||||
const param = Object.assign({}, this.listQuery)
|
||||
const urlJobId = this.$route.query.jobId
|
||||
if (urlJobId > 0 && !param.jobId) {
|
||||
param.jobId = urlJobId
|
||||
} else if (!urlJobId && !param.jobId) {
|
||||
param.jobId = 0
|
||||
}
|
||||
log.getList(param).then(response => {
|
||||
const { content } = response
|
||||
this.total = content.recordsTotal
|
||||
this.list = content.data
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
getExecutor() {
|
||||
job.getExecutorList().then(response => {
|
||||
const { content } = response
|
||||
this.executorList = content
|
||||
const defaultParam = { id: 0, title: '全部' }
|
||||
this.executorList.unshift(defaultParam)
|
||||
this.listQuery.jobGroup = this.executorList[0].id
|
||||
})
|
||||
},
|
||||
handlerDelete() {
|
||||
this.dialogStatus = 'create'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
deleteLog() {
|
||||
log.clearLog(this.temp.jobGroup, this.temp.jobId, this.temp.deleteType).then(response => {
|
||||
this.fetchData()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Delete Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
// const index = this.list.indexOf(row)
|
||||
},
|
||||
// 查看日志
|
||||
handleViewJobLog(row) {
|
||||
// const str = location.href.split('#')[0]
|
||||
// window.open(`${str}#/ router的name `)
|
||||
this.dialogVisible = true
|
||||
|
||||
this.jobLogQuery.executorAddress = row.executorAddress
|
||||
this.jobLogQuery.id = row.id
|
||||
this.jobLogQuery.triggerTime = Date.parse(row.triggerTime)
|
||||
if (this.logShow === false) {
|
||||
this.logShow = true
|
||||
}
|
||||
// window.open(`#/data/log?executorAddress=${this.jobLogQuery.executorAddress}&triggerTime=${this.jobLogQuery.triggerTime}&id=${this.jobLogQuery.id}&fromLineNum=${this.jobLogQuery.fromLineNum}`)
|
||||
this.loadLog()
|
||||
},
|
||||
// 获取日志
|
||||
loadLog() {
|
||||
this.logLoading = true
|
||||
log.viewJobLog(this.jobLogQuery.executorAddress, this.jobLogQuery.triggerTime, this.jobLogQuery.id,
|
||||
this.jobLogQuery.fromLineNum).then(response => {
|
||||
// 判断是否是 '\n',如果是表示显示完成,不重新加载
|
||||
if (response.content.logContent === '\n') {
|
||||
// this.jobLogQuery.fromLineNum = response.toLineNum - 20;
|
||||
// 重新加载
|
||||
// setTimeout(() => {
|
||||
// this.loadLog()
|
||||
// }, 2000);
|
||||
} else {
|
||||
this.logContent = response.content.logContent
|
||||
}
|
||||
this.logLoading = false
|
||||
})
|
||||
},
|
||||
killRunningJob(row) {
|
||||
log.killJob(row).then(response => {
|
||||
this.fetchData()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Kill Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.log-container {
|
||||
margin-bottom: 20px;
|
||||
background: #f5f5f5;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
overflow: scroll;
|
||||
pre {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
margin: 0 0 10.5px;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
color: #334851;
|
||||
background-color: #f5f5f5;
|
||||
// border: 1px solid #ccd1d3;
|
||||
border-radius: 1px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
62
src/views/dts/jobLog/log.vue
Normal file
62
src/views/dts/jobLog/log.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div>
|
||||
<div style="background-color: #304156; padding:10px 0; text-align:right;"><el-button type="primary" style="margin-right:20px;" @click="loadLog">刷新日志</el-button></div>
|
||||
<div class="log-container">
|
||||
<pre :loading="logLoading" v-text="logContent" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import * as log from '@/api/dts/datax-job-log'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
logContent: '',
|
||||
logLoading: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadLog()
|
||||
},
|
||||
methods: {
|
||||
loadLog() {
|
||||
this.logLoading = true
|
||||
|
||||
log.viewJobLog(this.$route.query.executorAddress, this.$route.query.triggerTime, this.$route.query.id, this.$route.query.fromLineNum).then(response => {
|
||||
// 判断是否是 '\n',如果是表示显示完成,不重新加载
|
||||
if (response.content.logContent === '\n') {
|
||||
// this.jobLogQuery.fromLineNum = response.toLineNum - 20;
|
||||
// 重新加载
|
||||
// setTimeout(() => {
|
||||
// this.loadLog()
|
||||
// }, 2000);
|
||||
} else {
|
||||
this.logContent = response.content.logContent
|
||||
}
|
||||
this.logLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.log-container {
|
||||
background: #f5f5f5;
|
||||
height: 500px;
|
||||
overflow: scroll;
|
||||
margin:20px;
|
||||
border:1px solid #ddd;
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
margin: 0 0 10.5px;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
color: #334851;
|
||||
background-color: #f5f5f5;
|
||||
// border: 1px solid #ccd1d3;
|
||||
border-radius: 1px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
227
src/views/dts/jobProject/index.vue
Normal file
227
src/views/dts/jobProject/index.vue
Normal file
@@ -0,0 +1,227 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="filter-container">
|
||||
<el-input
|
||||
v-model="listQuery.searchVal"
|
||||
clearable
|
||||
placeholder="项目名称"
|
||||
style="width: 200px;"
|
||||
class="filter-item"
|
||||
@keyup.enter.native="handleFilter"
|
||||
/>
|
||||
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="fetchData">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleCreate">
|
||||
添加
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
element-loading-text="Loading"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column align="center" label="序号" width="95">
|
||||
<template slot-scope="scope">{{ scope.$index+1 }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="项目名称" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.name }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="项目描述" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.description }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="所属用户" width="200" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.userName }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" width="200" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.createTime }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="230" class-name="small-padding fixed-width">
|
||||
<template slot-scope="{row}">
|
||||
<el-button type="primary" size="mini" @click="handleUpdate(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button v-if="row.status!=='deleted'" size="mini" type="danger" @click="handleDelete(row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="listQuery.pageNo"
|
||||
:limit.sync="listQuery.pageSize"
|
||||
@pagination="fetchData"
|
||||
/>
|
||||
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="800px">
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="100px">
|
||||
<el-form-item label="项目名称" prop="name">
|
||||
<el-input v-model="temp.name" placeholder="项目名称" style="width: 40%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="项目描述" prop="description">
|
||||
<el-input v-model="temp.description" placeholder="项目描述" style="width: 40%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog :visible.sync="dialogPluginVisible" title="Reading statistics">
|
||||
<el-table :data="pluginData" border fit highlight-current-row style="width: 100%">
|
||||
<el-table-column prop="key" label="Channel" />
|
||||
<el-table-column prop="pv" label="Pv" />
|
||||
</el-table>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="dialogPvVisible = false">Confirm</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as jobProjectApi from '@/api/dts/datax-job-project'
|
||||
import waves from '@/directive/waves'
|
||||
import Pagination from '@/components/Pagination'
|
||||
|
||||
export default {
|
||||
name: 'JobProject',
|
||||
components: { Pagination },
|
||||
directives: { waves },
|
||||
filters: {
|
||||
statusFilter(status) {
|
||||
const statusMap = {
|
||||
published: 'success',
|
||||
draft: 'gray',
|
||||
deleted: 'danger'
|
||||
}
|
||||
return statusMap[status]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: null,
|
||||
listLoading: true,
|
||||
total: 0,
|
||||
listQuery: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
searchVal: ''
|
||||
},
|
||||
pluginTypeOptions: ['reader', 'writer'],
|
||||
dialogPluginVisible: false,
|
||||
pluginData: [],
|
||||
dialogFormVisible: false,
|
||||
dialogStatus: '',
|
||||
textMap: {
|
||||
update: 'Edit',
|
||||
create: 'Create'
|
||||
},
|
||||
rules: {
|
||||
name: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
description: [{ required: true, message: 'this is required', trigger: 'blur' }]
|
||||
},
|
||||
temp: {
|
||||
id: undefined,
|
||||
name: '',
|
||||
description: ''
|
||||
},
|
||||
visible: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
fetchData() {
|
||||
this.listLoading = true
|
||||
jobProjectApi.list(this.listQuery).then(response => {
|
||||
const { records, total } = response.data
|
||||
this.total = parseInt(total || 0)
|
||||
this.list = records
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
resetTemp() {
|
||||
this.temp = {
|
||||
id: undefined,
|
||||
name: '',
|
||||
description: ''
|
||||
}
|
||||
},
|
||||
handleCreate() {
|
||||
this.resetTemp()
|
||||
this.dialogStatus = 'create'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
createData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
jobProjectApi.created(this.temp).then(() => {
|
||||
this.fetchData()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Created Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleUpdate(row) {
|
||||
this.temp = Object.assign({}, row) // copy obj
|
||||
this.dialogStatus = 'update'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
updateData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
const tempData = Object.assign({}, this.temp)
|
||||
jobProjectApi.updated(tempData).then(() => {
|
||||
this.fetchData()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Update Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleDelete(row) {
|
||||
console.log('删除')
|
||||
const idList = []
|
||||
idList.push(row.id)
|
||||
jobProjectApi.deleted({ idList: row.id }).then(response => {
|
||||
this.fetchData()
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Delete Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
529
src/views/dts/jobTemplate/index.vue
Normal file
529
src/views/dts/jobTemplate/index.vue
Normal file
@@ -0,0 +1,529 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="filter-container">
|
||||
<el-input v-model="listQuery.jobDesc" placeholder="任务描述" style="width: 200px;" class="filter-item" />
|
||||
<el-select v-model="projectIds" multiple placeholder="所属项目" class="filter-item">
|
||||
<el-option v-for="item in jobProjectList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="fetchData">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleCreate">
|
||||
添加
|
||||
</el-button>
|
||||
<!-- <el-checkbox v-model="showReviewer" class="filter-item" style="margin-left:15px;" @change="tableKey=tableKey+1">
|
||||
reviewer
|
||||
</el-checkbox> -->
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
element-loading-text="Loading"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column align="center" label="任务ID" width="80">
|
||||
<template slot-scope="scope">{{ scope.row.id }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="任务描述" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.jobDesc }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="所属项目" align="center" width="120">
|
||||
<template slot-scope="scope">{{ scope.row.projectName }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Cron" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.jobCron }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="路由策略" align="center">
|
||||
<template slot-scope="scope"> {{ routeStrategies.find(t => t.value === scope.row.executorRouteStrategy).label }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="修改用户" align="center" width="80">
|
||||
<template slot-scope="scope">{{ scope.row.userName }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="注册节点" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
width="500"
|
||||
@show="loadById(scope.row)"
|
||||
>
|
||||
<el-table :data="registerNode">
|
||||
<el-table-column width="150" property="title" label="执行器名称" />
|
||||
<el-table-column width="150" property="appName" label="appName" />
|
||||
<el-table-column width="150" property="registryList" label="机器地址" />
|
||||
</el-table>
|
||||
<el-button slot="reference" size="small">查看</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="下次触发时间" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
width="300"
|
||||
@show="nextTriggerTime(scope.row)"
|
||||
>
|
||||
<h5 v-html="triggerNextTimes" />
|
||||
<el-button slot="reference" size="small">查看</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-dropdown trigger="click">
|
||||
<span class="el-dropdown-link">
|
||||
操作<i class="el-icon-arrow-down el-icon--right" />
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item divided @click.native="handlerUpdate(row)">编辑</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="handlerDelete(row)">删除</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-show="total>0" :total="total" :page.sync="listQuery.current" :limit.sync="listQuery.size" @pagination="fetchData" />
|
||||
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="1000px" :before-close="handleClose">
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="110px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="执行器" prop="jobGroup">
|
||||
<el-select v-model="temp.jobGroup" placeholder="请选择执行器">
|
||||
<el-option v-for="item in executorList" :key="item.id" :label="item.title" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="任务描述" prop="jobDesc">
|
||||
<el-input v-model="temp.jobDesc" size="medium" placeholder="请输入任务描述" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="路由策略" prop="executorRouteStrategy">
|
||||
<el-select v-model="temp.executorRouteStrategy" placeholder="请选择路由策略">
|
||||
<el-option v-for="item in routeStrategies" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-dialog
|
||||
title="提示"
|
||||
:visible.sync="showCronBox"
|
||||
width="60%"
|
||||
append-to-body
|
||||
>
|
||||
<cron v-model="temp.jobCron" />
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="showCronBox = false;">关闭</el-button>
|
||||
<el-button type="primary" @click="showCronBox = false">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-form-item label="Cron" prop="jobCron">
|
||||
<el-input v-model="temp.jobCron" auto-complete="off" placeholder="请输入Cron表达式">
|
||||
<el-button v-if="!showCronBox" slot="append" icon="el-icon-turn-off" title="打开图形配置" @click="showCronBox = true" />
|
||||
<el-button v-else slot="append" icon="el-icon-open" title="关闭图形配置" @click="showCronBox = false" />
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="阻塞处理" prop="executorBlockStrategy">
|
||||
<el-select v-model="temp.executorBlockStrategy" placeholder="请选择阻塞处理策略">
|
||||
<el-option v-for="item in blockStrategies" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="报警邮件">
|
||||
<el-input v-model="temp.alarmEmail" placeholder="请输入报警邮件,多个用逗号分隔" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="任务类型" prop="glueType">
|
||||
<el-select v-model="temp.glueType" placeholder="任务脚本类型">
|
||||
<el-option v-for="item in glueTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="失败重试次数">
|
||||
<el-input-number v-model="temp.executorFailRetryCount" :min="0" :max="20" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属项目" prop="projectId">
|
||||
<el-select v-model="temp.projectId" placeholder="所属项目" class="filter-item">
|
||||
<el-option v-for="item in jobProjectList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="超时时间(分钟)">
|
||||
<el-input-number v-model="temp.executorTimeout" :min="0" :max="120" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="子任务">
|
||||
<el-select v-model="temp.childJobId" multiple placeholder="子任务" value-key="id">
|
||||
<el-option v-for="item in jobIdList" :key="item.id" :label="item.jobDesc" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" />
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="JVM启动参数">
|
||||
<el-input
|
||||
v-model="temp.jvmParam"
|
||||
placeholder="-Xms1024m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as executor from '@/api/dts/datax-executor'
|
||||
import Cron from '@/components/Cron'
|
||||
import * as jobTemp from '@/api/dts/datax-job-template'
|
||||
import waves from '@/directive/waves' // waves directive
|
||||
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
|
||||
import * as datasourceApi from '@/api/dts/datax-jdbcDatasource'
|
||||
import * as jobProjectApi from '@/api/dts/datax-job-project'
|
||||
import * as job from '@/api/dts/datax-job-info'
|
||||
|
||||
export default {
|
||||
name: 'JobTemplate',
|
||||
components: { Pagination, Cron },
|
||||
directives: { waves },
|
||||
filters: {
|
||||
statusFilter(status) {
|
||||
const statusMap = {
|
||||
published: 'success',
|
||||
draft: 'gray',
|
||||
deleted: 'danger'
|
||||
}
|
||||
return statusMap[status]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const validateIncParam = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('Increment parameters is required'))
|
||||
}
|
||||
callback()
|
||||
}
|
||||
const validatePartitionParam = (rule, value, callback) => {
|
||||
if (!this.partitionField) {
|
||||
callback(new Error('Partition parameters is required'))
|
||||
}
|
||||
callback()
|
||||
}
|
||||
return {
|
||||
projectIds: '',
|
||||
list: null,
|
||||
listLoading: true,
|
||||
total: 0,
|
||||
listQuery: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
jobGroup: 0,
|
||||
triggerStatus: -1,
|
||||
jobDesc: '',
|
||||
executorHandler: '',
|
||||
userId: 0,
|
||||
projectIds: ''
|
||||
},
|
||||
showCronBox: false,
|
||||
dialogPluginVisible: false,
|
||||
pluginData: [],
|
||||
dialogFormVisible: false,
|
||||
dialogStatus: '',
|
||||
textMap: {
|
||||
update: 'Edit',
|
||||
create: 'Create'
|
||||
},
|
||||
rules: {
|
||||
jobGroup: [{ required: true, message: 'jobGroup is required', trigger: 'change' }],
|
||||
executorRouteStrategy: [{ required: true, message: 'executorRouteStrategy is required', trigger: 'change' }],
|
||||
executorBlockStrategy: [{ required: true, message: 'executorBlockStrategy is required', trigger: 'change' }],
|
||||
jobDesc: [{ required: true, message: 'jobDesc is required', trigger: 'blur' }],
|
||||
jobProject: [{ required: true, message: 'jobProject is required', trigger: 'blur' }],
|
||||
jobCron: [{ required: true, message: 'jobCron is required', trigger: 'blur' }],
|
||||
incStartId: [{ trigger: 'blur', validator: validateIncParam }],
|
||||
replaceParam: [{ trigger: 'blur', validator: validateIncParam }],
|
||||
primaryKey: [{ trigger: 'blur', validator: validateIncParam }],
|
||||
incStartTime: [{ trigger: 'change', validator: validateIncParam }],
|
||||
replaceParamType: [{ trigger: 'change', validator: validateIncParam }],
|
||||
partitionField: [{ trigger: 'blur', validator: validatePartitionParam }],
|
||||
datasourceId: [{ trigger: 'change', validator: validateIncParam }],
|
||||
readerTable: [{ trigger: 'blur', validator: validateIncParam }],
|
||||
projectId: [{ required: true, message: 'projectId is required', trigger: 'change' }]
|
||||
},
|
||||
temp: {
|
||||
id: undefined,
|
||||
jobGroup: '',
|
||||
jobCron: '',
|
||||
jobDesc: '',
|
||||
executorRouteStrategy: 'RANDOM',
|
||||
executorBlockStrategy: 'DISCARD_LATER',
|
||||
childJobId: '',
|
||||
executorFailRetryCount: '',
|
||||
alarmEmail: '',
|
||||
executorTimeout: '',
|
||||
userId: 0,
|
||||
jobConfigId: '',
|
||||
executorHandler: 'executorJobHandler',
|
||||
glueType: 'BEAN',
|
||||
executorParam: '',
|
||||
jvmParam: '',
|
||||
projectId: '',
|
||||
datasourceId: 0,
|
||||
readerTable: ''
|
||||
},
|
||||
resetTemp() {
|
||||
this.temp = this.$options.data().temp
|
||||
},
|
||||
executorList: '',
|
||||
jobIdList: '',
|
||||
jobProjectList: '',
|
||||
dataSourceList: '',
|
||||
blockStrategies: [
|
||||
{ value: 'SERIAL_EXECUTION', label: '单机串行' },
|
||||
{ value: 'DISCARD_LATER', label: '丢弃后续调度' },
|
||||
{ value: 'COVER_EARLY', label: '覆盖之前调度' }
|
||||
],
|
||||
routeStrategies: [
|
||||
{ value: 'FIRST', label: '第一个' },
|
||||
{ value: 'LAST', label: '最后一个' },
|
||||
{ value: 'ROUND', label: '轮询' },
|
||||
{ value: 'RANDOM', label: '随机' },
|
||||
{ value: 'CONSISTENT_HASH', label: '一致性HASH' },
|
||||
{ value: 'LEAST_FREQUENTLY_USED', label: '最不经常使用' },
|
||||
{ value: 'LEAST_RECENTLY_USED', label: '最近最久未使用' },
|
||||
{ value: 'FAILOVER', label: '故障转移' },
|
||||
{ value: 'BUSYOVER', label: '忙碌转移' }
|
||||
// { value: 'SHARDING_BROADCAST', label: '分片广播' }
|
||||
],
|
||||
glueTypes: [
|
||||
{ value: 'BEAN', label: 'FlinkX任务' }
|
||||
],
|
||||
triggerNextTimes: '',
|
||||
registerNode: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
this.getExecutor()
|
||||
this.getJobIdList()
|
||||
this.getJobProject()
|
||||
this.getDataSourceList()
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleClose(done) {
|
||||
this.$confirm('确认关闭?')
|
||||
.then(_ => {
|
||||
done()
|
||||
})
|
||||
.catch(_ => {})
|
||||
},
|
||||
getExecutor() {
|
||||
jobTemp.getExecutorList().then(response => {
|
||||
const { content } = response
|
||||
this.executorList = content
|
||||
})
|
||||
},
|
||||
getJobIdList() {
|
||||
job.getJobIdList().then(response => {
|
||||
const { content } = response
|
||||
this.jobIdList = content
|
||||
})
|
||||
},
|
||||
getJobProject() {
|
||||
jobProjectApi.getJobProjectList().then(response => {
|
||||
this.jobProjectList = response.data
|
||||
})
|
||||
},
|
||||
getDataSourceList() {
|
||||
datasourceApi.getDataSourceList().then(response => {
|
||||
this.dataSourceList = response
|
||||
})
|
||||
},
|
||||
fetchData() {
|
||||
this.listLoading = true
|
||||
if (this.projectIds) {
|
||||
this.listQuery.projectIds = this.projectIds.toString()
|
||||
}
|
||||
jobTemp.getList(this.listQuery).then(response => {
|
||||
const { content } = response
|
||||
this.total = content.recordsTotal
|
||||
this.list = content.data
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
handleCreate() {
|
||||
//this.resetTemp()
|
||||
this.dialogStatus = 'create'
|
||||
this.dialogFormVisible = true
|
||||
this.temp.jobGroup = this.executorList[0]['id']
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
createData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.temp.childJobId) {
|
||||
const childJobs = []
|
||||
for (const i in this.temp.childJobId) {
|
||||
childJobs.push(this.temp.childJobId[i].id)
|
||||
}
|
||||
this.temp.childJobId = childJobs.toString()
|
||||
}
|
||||
if (this.partitionField) this.temp.partitionInfo = this.partitionField + ',' + this.timeOffset + ',' + this.timeFormatType
|
||||
jobTemp.createJob(this.temp).then(() => {
|
||||
this.fetchData()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Created Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handlerUpdate(row) {
|
||||
//this.resetTemp()
|
||||
this.temp = Object.assign({}, row) // copy obj
|
||||
this.dialogStatus = 'update'
|
||||
this.dialogFormVisible = true
|
||||
const arrchildSet = []
|
||||
const arrJobIdList = []
|
||||
if (this.JobIdList) {
|
||||
for (const n in this.JobIdList) {
|
||||
if (this.JobIdList[n].id !== this.temp.id) {
|
||||
arrJobIdList.push(this.JobIdList[n])
|
||||
}
|
||||
}
|
||||
this.JobIdList = arrJobIdList
|
||||
}
|
||||
|
||||
if (this.temp.childJobId) {
|
||||
const arrString = this.temp.childJobId.split(',')
|
||||
for (const i in arrString) {
|
||||
for (const n in this.jobIdList) {
|
||||
if (this.jobIdList[n].id === parseInt(arrString[i])) {
|
||||
arrchildSet.push(this.jobIdList[n])
|
||||
}
|
||||
}
|
||||
}
|
||||
this.temp.childJobId = arrchildSet
|
||||
}
|
||||
|
||||
if (this.temp.partitionInfo) {
|
||||
const partition = this.temp.partitionInfo.split(',')
|
||||
this.partitionField = partition[0]
|
||||
this.timeOffset = partition[1]
|
||||
this.timeFormatType = partition[2]
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
updateData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.temp.childJobId) {
|
||||
const childJobs = []
|
||||
for (const i in this.temp.childJobId) {
|
||||
childJobs.push(this.temp.childJobId[i].id)
|
||||
}
|
||||
this.temp.childJobId = childJobs.toString()
|
||||
}
|
||||
if (this.partitionField) this.temp.partitionInfo = this.partitionField + ',' + this.timeOffset + ',' + this.timeFormatType
|
||||
jobTemp.updateJob(this.temp).then(() => {
|
||||
this.fetchData()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Update Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handlerDelete(row) {
|
||||
this.$confirm('确定删除吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
jobTemp.removeJob(row.id).then(response => {
|
||||
this.fetchData()
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Delete Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// const index = this.list.indexOf(row)
|
||||
},
|
||||
nextTriggerTime(row) {
|
||||
jobTemp.nextTriggerTime(row.jobCron).then(response => {
|
||||
const { content } = response
|
||||
this.triggerNextTimes = content.join('<br>')
|
||||
})
|
||||
},
|
||||
loadById(row) {
|
||||
executor.loadById(row.jobGroup).then(response => {
|
||||
this.registerNode = []
|
||||
const { content } = response
|
||||
this.registerNode.push(content)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.el-dropdown-link {
|
||||
cursor: pointer;
|
||||
color: #409EFF;
|
||||
}
|
||||
.el-dropdown + .el-dropdown {
|
||||
margin-left: 15px;
|
||||
}
|
||||
</style>
|
||||
9
src/views/dts/json-build-batch/busReader.js
Normal file
9
src/views/dts/json-build-batch/busReader.js
Normal file
@@ -0,0 +1,9 @@
|
||||
// 公共的组件,来做为中间传达的工具
|
||||
import Vue from 'vue'
|
||||
export default new Vue({
|
||||
data() {
|
||||
return {
|
||||
dataSourceId: ''
|
||||
}
|
||||
}
|
||||
})
|
||||
9
src/views/dts/json-build-batch/busWriter.js
Normal file
9
src/views/dts/json-build-batch/busWriter.js
Normal file
@@ -0,0 +1,9 @@
|
||||
// 公共的组件,来做为中间传达的工具
|
||||
import Vue from 'vue'
|
||||
export default new Vue({
|
||||
data() {
|
||||
return {
|
||||
dataSourceId: ''
|
||||
}
|
||||
}
|
||||
})
|
||||
76
src/views/dts/json-build-batch/components/tableMapper.vue
Normal file
76
src/views/dts/json-build-batch/components/tableMapper.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form label-position="left" label-width="80px" :model="readerForm">
|
||||
<el-form-item label="源端表">
|
||||
<el-checkbox
|
||||
v-model="readerForm.lcheckAll"
|
||||
:indeterminate="readerForm.isIndeterminate"
|
||||
@change="lHandleCheckAllChange"
|
||||
>全选</el-checkbox>
|
||||
<div style="margin: 15px 0;" />
|
||||
<el-checkbox-group v-model="readerForm.ltables" @change="lHandleCheckedChange">
|
||||
<el-checkbox v-for="c in fromTablesList" :key="c" :label="c">{{ c }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="目标表">
|
||||
<el-checkbox
|
||||
v-model="readerForm.rcheckAll"
|
||||
:indeterminate="readerForm.isIndeterminate"
|
||||
@change="rHandleCheckAllChange"
|
||||
>全选</el-checkbox>
|
||||
<div style="margin: 20px 0;" />
|
||||
<el-checkbox-group v-model="readerForm.rtables" @change="rHandleCheckedChange">
|
||||
<el-checkbox v-for="c in toTablesList" :key="c" :label="c">{{ c }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TableMapper',
|
||||
data() {
|
||||
return {
|
||||
mapperJson: {},
|
||||
fromTablesList: [],
|
||||
toTablesList: [],
|
||||
readerForm: {
|
||||
ltables: [],
|
||||
rtables: [],
|
||||
lcheckAll: false,
|
||||
rcheckAll: false,
|
||||
isIndeterminate: true
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
lHandleCheckAllChange(val) {
|
||||
this.readerForm.ltables = val ? this.fromTablesList : []
|
||||
this.readerForm.isIndeterminate = false
|
||||
},
|
||||
rHandleCheckAllChange(val) {
|
||||
this.readerForm.rtables = val ? this.toTablesList : []
|
||||
this.readerForm.isIndeterminate = false
|
||||
},
|
||||
lHandleCheckedChange(value) {
|
||||
const checkedCount = value.length
|
||||
this.readerForm.checkAll = checkedCount === this.fromTablesList.length
|
||||
this.readerForm.isIndeterminate = checkedCount > 0 && checkedCount < this.fromTablesList.length
|
||||
},
|
||||
rHandleCheckedChange(value) {
|
||||
const checkedCount = value.length
|
||||
this.readerForm.checkAll = checkedCount === this.toTablesList.length
|
||||
this.readerForm.isIndeterminate = checkedCount > 0 && checkedCount < this.toTablesList.length
|
||||
},
|
||||
getLTables() {
|
||||
return this.readerForm.ltables
|
||||
},
|
||||
getRTables() {
|
||||
return this.readerForm.rtables
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
250
src/views/dts/json-build-batch/index.vue
Normal file
250
src/views/dts/json-build-batch/index.vue
Normal file
@@ -0,0 +1,250 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="build-container">
|
||||
<el-steps :active="active" finish-status="success">
|
||||
<el-step title="步骤 1" description="构建reader">1</el-step>
|
||||
<el-step title="步骤 2" description="构建writer">2</el-step>
|
||||
<el-step title="步骤 3" description="表映射">3</el-step>
|
||||
<el-step title="步骤 4" description="批量创建">4</el-step>
|
||||
</el-steps>
|
||||
|
||||
<div v-show="active===1" class="step1">
|
||||
<Reader ref="reader" />
|
||||
</div>
|
||||
<div v-show="active===2" class="step2">
|
||||
<Writer ref="writer" />
|
||||
</div>
|
||||
<div v-show="active===3" class="step3">
|
||||
<Mapper ref="mapper" />
|
||||
</div>
|
||||
<div v-show="active===4" class="step4">
|
||||
<el-button type="primary" @click="handleJobTemplateSelectDrawer">{{ jobTemplate ? jobTemplate : "1.选择模板" }}</el-button>
|
||||
<el-button type="primary" @click="createJob">2.批量创建任务</el-button>
|
||||
(步骤:选择模板->批量创建任务)
|
||||
<el-drawer
|
||||
ref="jobTemplateSelectDrawer"
|
||||
title="选择模板"
|
||||
:visible.sync="jobTemplateSelectDrawer"
|
||||
direction="rtl"
|
||||
size="50%"
|
||||
>
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
element-loading-text="Loading"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
destroy-on-close="true"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<el-table-column align="center" label="任务ID" width="80">
|
||||
<template slot-scope="scope">{{ scope.row.id }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="任务描述" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.jobDesc }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="所属项目" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.jobProject }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Cron" align="center">
|
||||
<template slot-scope="scope"><span>{{ scope.row.jobCron }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="路由策略" align="center">
|
||||
<template slot-scope="scope"> {{ routeStrategies.find(t => t.value === scope.row.executorRouteStrategy).label }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-show="total>0" :total="total" :page.sync="listQuery.current" :limit.sync="listQuery.size" @pagination="fetchData" />
|
||||
</el-drawer>
|
||||
<div style="margin-bottom: 20px;" />
|
||||
</div>
|
||||
|
||||
<el-button :disabled="active===1" style="margin-top: 12px;" @click="last">上一步</el-button>
|
||||
<el-button v-show="active!==4" type="primary" style="margin-top: 12px;margin-bottom: 12px;" @click="next">下一步</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as jobTemplate from '@/api/dts/datax-job-template'
|
||||
import * as job from '@/api/dts/datax-job-info'
|
||||
import Pagination from '@/components/Pagination'
|
||||
import Reader from './reader'
|
||||
import Writer from './writer'
|
||||
import clip from '@/utils/clipboard'
|
||||
import Mapper from './mapper'
|
||||
|
||||
export default {
|
||||
name: 'JsonBuild',
|
||||
components: { Reader, Writer, Pagination, Mapper },
|
||||
data() {
|
||||
return {
|
||||
configJson: '',
|
||||
active: 1,
|
||||
jobTemplate: '',
|
||||
jobTemplateSelectDrawer: false,
|
||||
list: null,
|
||||
currentRow: null,
|
||||
listLoading: true,
|
||||
total: 0,
|
||||
listQuery: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
jobGroup: 0,
|
||||
triggerStatus: -1,
|
||||
jobDesc: '',
|
||||
executorHandler: '',
|
||||
userId: 0
|
||||
},
|
||||
blockStrategies: [
|
||||
{ value: 'SERIAL_EXECUTION', label: '单机串行' },
|
||||
{ value: 'DISCARD_LATER', label: '丢弃后续调度' },
|
||||
{ value: 'COVER_EARLY', label: '覆盖之前调度' }
|
||||
],
|
||||
routeStrategies: [
|
||||
{ value: 'FIRST', label: '第一个' },
|
||||
{ value: 'LAST', label: '最后一个' },
|
||||
{ value: 'ROUND', label: '轮询' },
|
||||
{ value: 'RANDOM', label: '随机' },
|
||||
{ value: 'CONSISTENT_HASH', label: '一致性HASH' },
|
||||
{ value: 'LEAST_FREQUENTLY_USED', label: '最不经常使用' },
|
||||
{ value: 'LEAST_RECENTLY_USED', label: '最近最久未使用' },
|
||||
{ value: 'FAILOVER', label: '故障转移' },
|
||||
{ value: 'BUSYOVER', label: '忙碌转移' }
|
||||
// { value: 'SHARDING_BROADCAST', label: '分片广播' }
|
||||
],
|
||||
triggerNextTimes: '',
|
||||
registerNode: [],
|
||||
jobJson: '',
|
||||
temp: {
|
||||
id: undefined,
|
||||
jobGroup: '',
|
||||
jobCron: '',
|
||||
jobDesc: '',
|
||||
executorRouteStrategy: '',
|
||||
executorBlockStrategy: '',
|
||||
childJobId: '',
|
||||
executorFailRetryCount: '',
|
||||
alarmEmail: '',
|
||||
executorTimeout: '',
|
||||
userId: 0,
|
||||
jobConfigId: '',
|
||||
executorHandler: 'executorJobHandler',
|
||||
glueType: 'BEAN',
|
||||
jobJson: '',
|
||||
executorParam: '',
|
||||
replaceParam: '',
|
||||
jvmParam: '',
|
||||
incStartTime: '',
|
||||
templateId: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// this.getJdbcDs()
|
||||
},
|
||||
methods: {
|
||||
next() {
|
||||
const fromTableList = this.$refs.reader.getData().tables
|
||||
const toTableList = this.$refs.writer.getData().tables
|
||||
// const fromTableName = this.$refs.reader.getData().tableName
|
||||
// 第一步 reader 判断是否已选字段
|
||||
if (this.active === 1) {
|
||||
// 实现第一步骤读取的表和字段直接带到第二步骤
|
||||
// this.$refs.writer.sendTableNameAndColumns(fromTableName, fromColumnList)
|
||||
// 取子组件的数据
|
||||
// console.info(this.$refs.reader.getData())
|
||||
this.active++
|
||||
} else {
|
||||
// 将第一步和第二步得到的字段名字发送到第三步
|
||||
if (this.active === 2) {
|
||||
this.$refs.mapper.sendTables(fromTableList, toTableList)
|
||||
}
|
||||
if (this.active !== 4) {
|
||||
this.active++
|
||||
}
|
||||
}
|
||||
},
|
||||
last() {
|
||||
if (this.active > 1) {
|
||||
this.active--
|
||||
}
|
||||
},
|
||||
// Create job
|
||||
createJob() {
|
||||
const readerData = this.$refs.reader.getData()
|
||||
const writeData = this.$refs.writer.getData()
|
||||
const readerTables = this.$refs.mapper.getLTables()
|
||||
const writerTables = this.$refs.mapper.getRTables()
|
||||
const rdbmsReader = {
|
||||
readerSplitPk: readerData.splitPk
|
||||
}
|
||||
const rdbmsWriter = {}
|
||||
const obj = {
|
||||
readerDatasourceId: readerData.datasourceId,
|
||||
readerTables: readerTables,
|
||||
writerDatasourceId: writeData.datasourceId,
|
||||
writerTables: writerTables,
|
||||
rdbmsReader: rdbmsReader,
|
||||
rdbmsWriter: rdbmsWriter,
|
||||
templateId: this.temp.templateId
|
||||
}
|
||||
// 调api
|
||||
job.batchAddJob(obj).then(response => {
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Created Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
// 切回第一步
|
||||
this.active = 1
|
||||
})
|
||||
},
|
||||
handleCopy(text, event) {
|
||||
clip(this.configJson, event)
|
||||
this.$message({
|
||||
message: 'copy success',
|
||||
type: 'success'
|
||||
})
|
||||
},
|
||||
handleJobTemplateSelectDrawer() {
|
||||
this.jobTemplateSelectDrawer = !this.jobTemplateSelectDrawer
|
||||
if (this.jobTemplateSelectDrawer) {
|
||||
this.fetchData()
|
||||
this.getExecutor()
|
||||
}
|
||||
},
|
||||
getReaderData() {
|
||||
return this.$refs.reader.getData()
|
||||
},
|
||||
getExecutor() {
|
||||
jobTemplate.getExecutorList().then(response => {
|
||||
const { content } = response
|
||||
this.executorList = content
|
||||
})
|
||||
},
|
||||
fetchData() {
|
||||
this.listLoading = true
|
||||
jobTemplate.getList(this.listQuery).then(response => {
|
||||
const { content } = response
|
||||
this.total = content.recordsTotal
|
||||
this.list = content.data
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.temp = Object.assign({}, val)
|
||||
this.temp.id = undefined
|
||||
this.temp.jobDesc = this.getReaderData().tableName
|
||||
this.$refs.jobTemplateSelectDrawer.closeDrawer()
|
||||
this.jobTemplate = val.id + '(' + val.jobDesc + ')'
|
||||
this.temp.templateId = val.id
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
24
src/views/dts/json-build-batch/mapper.vue
Normal file
24
src/views/dts/json-build-batch/mapper.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<TableMapper ref="mapper" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import TableMapper from './components/tableMapper'
|
||||
export default {
|
||||
name: 'Mapper',
|
||||
components: { TableMapper },
|
||||
methods: {
|
||||
sendTables(fromTablesList, toTablesList) {
|
||||
this.$refs.mapper.fromTablesList = fromTablesList
|
||||
this.$refs.mapper.toTablesList = toTablesList
|
||||
},
|
||||
getLTables() {
|
||||
return this.$refs.mapper.getLTables()
|
||||
},
|
||||
getRTables() {
|
||||
return this.$refs.mapper.getRTables()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
27
src/views/dts/json-build-batch/reader.vue
Normal file
27
src/views/dts/json-build-batch/reader.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<TableReader ref="tablereader" @selectDataSource="showDataSource" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TableReader from './reader/tableReader'
|
||||
export default {
|
||||
name: 'Reader',
|
||||
components: { TableReader },
|
||||
data() {
|
||||
return {
|
||||
dataSource: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
return this.$refs.tablereader.getData()
|
||||
},
|
||||
showDataSource(data) {
|
||||
this.dataSource = data
|
||||
this.getData()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
170
src/views/dts/json-build-batch/reader/tableReader.vue
Normal file
170
src/views/dts/json-build-batch/reader/tableReader.vue
Normal file
@@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form label-position="right" label-width="120px" :model="readerForm" :rules="rules">
|
||||
<el-form-item label="数据库源:" prop="datasourceId">
|
||||
<el-select v-model="readerForm.datasourceId" filterable style="width: 300px" @change="rDsChange">
|
||||
<el-option
|
||||
v-for="item in rDsList"
|
||||
:key="item.id"
|
||||
:label="item.datasourceName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="dataSource==='postgresql' || dataSource==='oracle' ||dataSource==='sqlserver' ||dataSource==='hana'" label="Schema:">
|
||||
<el-select v-model="readerForm.tableSchema" filterable style="width: 300px" @change="schemaChange">
|
||||
<el-option
|
||||
v-for="item in schemaList"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="切分字段:">
|
||||
<el-input v-model="readerForm.splitPk" placeholder="切分主键" style="width: 13%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="数据库表名:">
|
||||
<el-checkbox
|
||||
v-model="readerForm.checkAll"
|
||||
:indeterminate="readerForm.isIndeterminate"
|
||||
@change="rHandleCheckAllChange"
|
||||
>全选
|
||||
</el-checkbox>
|
||||
<div style="margin: 15px 0;" />
|
||||
<el-checkbox-group v-model="readerForm.tables" @change="rHandleCheckedChange">
|
||||
<el-checkbox v-for="c in rTbList" :key="c" :label="c">{{ c }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as dsQueryApi from '@/api/dts/metadata-query'
|
||||
import { list as jdbcDsList } from '@/api/dts/datax-jdbcDatasource'
|
||||
import Bus from '../busReader'
|
||||
|
||||
export default {
|
||||
name: 'TableReader',
|
||||
data() {
|
||||
return {
|
||||
jdbcDsQuery: {
|
||||
current: 1,
|
||||
size: 200,
|
||||
ascs: 'datasource_name'
|
||||
},
|
||||
rDsList: [],
|
||||
rTbList: [],
|
||||
schemaList: [],
|
||||
loading: false,
|
||||
active: 1,
|
||||
customFields: '',
|
||||
customType: '',
|
||||
customValue: '',
|
||||
dataSource: '',
|
||||
readerForm: {
|
||||
datasourceId: undefined,
|
||||
tables: [],
|
||||
checkAll: false,
|
||||
isIndeterminate: true,
|
||||
splitPk: '',
|
||||
tableSchema: ''
|
||||
},
|
||||
rules: {
|
||||
datasourceId: [{ required: true, message: 'this is required', trigger: 'change' }],
|
||||
tableName: [{ required: true, message: 'this is required', trigger: 'change' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'readerForm.datasourceId': function(oldVal, newVal) {
|
||||
if (this.dataSource === 'postgresql' || this.dataSource === 'oracle' || this.dataSource === 'sqlserver' || this.dataSource === 'hana') {
|
||||
this.getSchema()
|
||||
} else {
|
||||
this.getTables('reader')
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getJdbcDs()
|
||||
},
|
||||
methods: {
|
||||
// 获取可用数据源
|
||||
getJdbcDs() {
|
||||
this.loading = true
|
||||
jdbcDsList(this.jdbcDsQuery).then(response => {
|
||||
const { records } = response.data
|
||||
this.rDsList = records
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 获取表名
|
||||
getTables(type) {
|
||||
if (type === 'reader') {
|
||||
let obj = {}
|
||||
if (this.dataSource === 'postgresql' || this.dataSource === 'oracle' || this.dataSource === 'sqlserver' || this.dataSource === 'hana') {
|
||||
obj = {
|
||||
datasourceId: this.readerForm.datasourceId,
|
||||
tableSchema: this.readerForm.tableSchema
|
||||
}
|
||||
} else {
|
||||
obj = {
|
||||
datasourceId: this.readerForm.datasourceId
|
||||
}
|
||||
}
|
||||
// 组装
|
||||
dsQueryApi.getTables(obj).then(response => {
|
||||
if (response) {
|
||||
this.rTbList = response.data
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
getSchema() {
|
||||
const obj = {
|
||||
datasourceId: this.readerForm.datasourceId
|
||||
}
|
||||
dsQueryApi.getTableSchema(obj).then(response => {
|
||||
this.schemaList = response
|
||||
})
|
||||
},
|
||||
// schema 切换
|
||||
schemaChange(e) {
|
||||
this.readerForm.tableSchema = e
|
||||
// 获取可用表
|
||||
this.getTables('reader')
|
||||
},
|
||||
// reader 数据源切换
|
||||
rDsChange(e) {
|
||||
// 清空
|
||||
this.readerForm.tableName = ''
|
||||
this.readerForm.datasourceId = e
|
||||
this.rDsList.find((item) => {
|
||||
if (item.id === e) {
|
||||
this.dataSource = item.datasource
|
||||
}
|
||||
})
|
||||
Bus.dataSourceId = e
|
||||
this.$emit('selectDataSource', this.dataSource)
|
||||
// 获取可用表
|
||||
this.getTables('reader')
|
||||
},
|
||||
rHandleCheckAllChange(val) {
|
||||
this.readerForm.tables = val ? this.rTbList : []
|
||||
this.readerForm.isIndeterminate = false
|
||||
},
|
||||
rHandleCheckedChange(value) {
|
||||
const checkedCount = value.length
|
||||
this.readerForm.checkAll = checkedCount === this.rTbList.length
|
||||
this.readerForm.isIndeterminate = checkedCount > 0 && checkedCount < this.rTbList.length
|
||||
},
|
||||
getData() {
|
||||
if (Bus.dataSourceId) {
|
||||
this.readerForm.datasourceId = Bus.dataSourceId
|
||||
}
|
||||
return this.readerForm
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
38
src/views/dts/json-build-batch/writer.vue
Normal file
38
src/views/dts/json-build-batch/writer.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<TableWriter ref="tablewriter" @selectDataSource="showDataSource" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TableWriter from './writer/tableWriter'
|
||||
|
||||
export default {
|
||||
name: 'Writer',
|
||||
components: { TableWriter },
|
||||
data() {
|
||||
return {
|
||||
dataSource: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
return this.$refs.tablewriter.getData()
|
||||
},
|
||||
getTableName() {
|
||||
return this.$refs.tablewriter.getTableName()
|
||||
},
|
||||
getReaderData() {
|
||||
return this.$parent.getReaderData()
|
||||
},
|
||||
showDataSource(data) {
|
||||
this.dataSource = data
|
||||
this.getData()
|
||||
},
|
||||
sendTableNameAndColumns(fromTableName, fromColumnList) {
|
||||
this.$refs.hivewriter.fromTableName = fromTableName
|
||||
this.$refs.hivewriter.fromColumnList = fromColumnList
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
181
src/views/dts/json-build-batch/writer/tableWriter.vue
Normal file
181
src/views/dts/json-build-batch/writer/tableWriter.vue
Normal file
@@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form label-position="right" label-width="150px" :model="writerForm" :rules="rules">
|
||||
<el-form-item label="数据库源:" prop="datasourceId">
|
||||
<el-select
|
||||
v-model="writerForm.datasourceId"
|
||||
filterable
|
||||
style="width: 300px;"
|
||||
@change="wDsChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in wDsList"
|
||||
:key="item.id"
|
||||
:label="item.datasourceName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="dataSource==='postgresql' || dataSource==='oracle' ||dataSource==='sqlserver' ||dataSource==='hana'" label="Schema:">
|
||||
<el-select v-model="writerForm.tableSchema" filterable style="width: 300px" @change="schemaChange">
|
||||
<el-option
|
||||
v-for="item in schemaList"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<div style="margin: 5px 0;" />
|
||||
<el-form-item label="数据库表名:">
|
||||
<el-checkbox v-model="writerForm.checkAll" :indeterminate="writerForm.isIndeterminate" @change="wHandleCheckAllChange">全选</el-checkbox>
|
||||
<div style="margin: 15px 0;" />
|
||||
<el-checkbox-group v-model="writerForm.tables" @change="wHandleCheckedChange">
|
||||
<el-checkbox v-for="c in wTbList" :key="c" :label="c">{{ c }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as dsQueryApi from '@/api/dts/metadata-query'
|
||||
import { list as jdbcDsList } from '@/api/dts/datax-jdbcDatasource'
|
||||
import Bus from '../busWriter'
|
||||
export default {
|
||||
name: 'TableWriter',
|
||||
data() {
|
||||
return {
|
||||
jdbcDsQuery: {
|
||||
current: 1,
|
||||
size: 200,
|
||||
ascs: 'datasource_name'
|
||||
},
|
||||
wDsList: [],
|
||||
schemaList: [],
|
||||
fromTableName: '',
|
||||
wTbList: [],
|
||||
dataSource: '',
|
||||
createTableName: '',
|
||||
writerForm: {
|
||||
datasourceId: undefined,
|
||||
tables: [],
|
||||
checkAll: false,
|
||||
isIndeterminate: true,
|
||||
tableSchema: ''
|
||||
},
|
||||
readerForm: this.getReaderData(),
|
||||
rules: {
|
||||
datasourceId: [{ required: true, message: 'this is required', trigger: 'change' }],
|
||||
tableName: [{ required: true, message: 'this is required', trigger: 'change' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'writerForm.datasourceId': function(oldVal, newVal) {
|
||||
if (this.dataSource === 'postgresql' || this.dataSource === 'oracle' || this.dataSource === 'sqlserver' || this.dataSource === 'hana') {
|
||||
this.getSchema()
|
||||
} else {
|
||||
this.getTables('writer')
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getJdbcDs()
|
||||
},
|
||||
methods: {
|
||||
// 获取可用数据源
|
||||
getJdbcDs() {
|
||||
this.loading = true
|
||||
jdbcDsList(this.jdbcDsQuery).then(response => {
|
||||
const { records } = response.data
|
||||
this.wDsList = records
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 获取表名
|
||||
getTables(type) {
|
||||
if (type === 'writer') {
|
||||
let obj = {}
|
||||
if (this.dataSource === 'postgresql' || this.dataSource === 'oracle' || this.dataSource === 'sqlserver' || this.dataSource === 'hana') {
|
||||
obj = {
|
||||
datasourceId: this.writerForm.datasourceId,
|
||||
tableSchema: this.writerForm.tableSchema
|
||||
}
|
||||
} else {
|
||||
obj = {
|
||||
datasourceId: this.writerForm.datasourceId
|
||||
}
|
||||
}
|
||||
// 组装
|
||||
dsQueryApi.getTables(obj).then(response => {
|
||||
this.wTbList = response.data
|
||||
})
|
||||
}
|
||||
},
|
||||
getSchema() {
|
||||
const obj = {
|
||||
datasourceId: this.writerForm.datasourceId
|
||||
}
|
||||
dsQueryApi.getTableSchema(obj).then(response => {
|
||||
this.schemaList = response
|
||||
})
|
||||
},
|
||||
// schema 切换
|
||||
schemaChange(e) {
|
||||
this.writerForm.tableSchema = e
|
||||
// 获取可用表
|
||||
this.getTables('writer')
|
||||
},
|
||||
wDsChange(e) {
|
||||
// 清空
|
||||
this.writerForm.tableName = ''
|
||||
this.writerForm.datasourceId = e
|
||||
this.wDsList.find((item) => {
|
||||
if (item.id === e) {
|
||||
this.dataSource = item.datasource
|
||||
}
|
||||
})
|
||||
Bus.dataSourceId = e
|
||||
this.$emit('selectDataSource', this.dataSource)
|
||||
// 获取可用表
|
||||
this.getTables()
|
||||
},
|
||||
wHandleCheckAllChange(val) {
|
||||
this.writerForm.tables = val ? this.wTbList : []
|
||||
this.writerForm.isIndeterminate = false
|
||||
},
|
||||
wHandleCheckedChange(value) {
|
||||
const checkedCount = value.length
|
||||
this.writerForm.checkAll = checkedCount === this.wTbList.length
|
||||
this.writerForm.isIndeterminate = checkedCount > 0 && checkedCount < this.wTbList.length
|
||||
},
|
||||
getData() {
|
||||
if (Bus.dataSourceId) {
|
||||
this.writerForm.datasourceId = Bus.dataSourceId
|
||||
}
|
||||
return this.writerForm
|
||||
},
|
||||
getReaderData() {
|
||||
return this.$parent.getReaderData()
|
||||
},
|
||||
getTableName() {
|
||||
return this.fromTableName
|
||||
},
|
||||
createTable() {
|
||||
const obj = {
|
||||
datasourceId: this.writerForm.datasourceId,
|
||||
tableName: this.createTableName
|
||||
}
|
||||
dsQueryApi.createTable(obj).then(response => {
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Create Table Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
}).catch(() => console.log('promise catch err'))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
9
src/views/dts/json-build/busReader.js
Normal file
9
src/views/dts/json-build/busReader.js
Normal file
@@ -0,0 +1,9 @@
|
||||
// 公共的组件,来做为中间传达的工具
|
||||
import Vue from 'vue'
|
||||
export default new Vue({
|
||||
data() {
|
||||
return {
|
||||
dataSourceId: ''
|
||||
}
|
||||
}
|
||||
})
|
||||
9
src/views/dts/json-build/busWriter.js
Normal file
9
src/views/dts/json-build/busWriter.js
Normal file
@@ -0,0 +1,9 @@
|
||||
// 公共的组件,来做为中间传达的工具
|
||||
import Vue from 'vue'
|
||||
export default new Vue({
|
||||
data() {
|
||||
return {
|
||||
dataSourceId: ''
|
||||
}
|
||||
}
|
||||
})
|
||||
76
src/views/dts/json-build/components/fieldMapper.vue
Normal file
76
src/views/dts/json-build/components/fieldMapper.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form label-position="left" label-width="80px" :model="readerForm">
|
||||
<el-form-item label="源端字段">
|
||||
<el-checkbox
|
||||
v-model="readerForm.lcheckAll"
|
||||
:indeterminate="readerForm.isIndeterminate"
|
||||
@change="lHandleCheckAllChange"
|
||||
>全选</el-checkbox>
|
||||
<div style="margin: 15px 0;" />
|
||||
<el-checkbox-group v-model="readerForm.lcolumns" @change="lHandleCheckedChange">
|
||||
<el-checkbox v-for="c in fromColumnsList" :key="c" :label="c">{{ c }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="目标字段">
|
||||
<el-checkbox
|
||||
v-model="readerForm.rcheckAll"
|
||||
:indeterminate="readerForm.isIndeterminate"
|
||||
@change="rHandleCheckAllChange"
|
||||
>全选</el-checkbox>
|
||||
<div style="margin: 20px 0;" />
|
||||
<el-checkbox-group v-model="readerForm.rcolumns" @change="rHandleCheckedChange">
|
||||
<el-checkbox v-for="c in toColumnsList" :key="c" :label="c">{{ c }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'FieldMapper',
|
||||
data() {
|
||||
return {
|
||||
mapperJson: {},
|
||||
fromColumnsList: [],
|
||||
toColumnsList: [],
|
||||
readerForm: {
|
||||
lcolumns: [],
|
||||
rcolumns: [],
|
||||
lcheckAll: false,
|
||||
rcheckAll: false,
|
||||
isIndeterminate: true
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
lHandleCheckAllChange(val) {
|
||||
this.readerForm.lcolumns = val ? this.fromColumnsList : []
|
||||
this.readerForm.isIndeterminate = false
|
||||
},
|
||||
rHandleCheckAllChange(val) {
|
||||
this.readerForm.rcolumns = val ? this.toColumnsList : []
|
||||
this.readerForm.isIndeterminate = false
|
||||
},
|
||||
lHandleCheckedChange(value) {
|
||||
const checkedCount = value.length
|
||||
this.readerForm.checkAll = checkedCount === this.fromColumnsList.length
|
||||
this.readerForm.isIndeterminate = checkedCount > 0 && checkedCount < this.fromColumnsList.length
|
||||
},
|
||||
rHandleCheckedChange(value) {
|
||||
const checkedCount = value.length
|
||||
this.readerForm.checkAll = checkedCount === this.toColumnsList.length
|
||||
this.readerForm.isIndeterminate = checkedCount > 0 && checkedCount < this.toColumnsList.length
|
||||
},
|
||||
getLColumns() {
|
||||
return this.readerForm.lcolumns
|
||||
},
|
||||
getRColumns() {
|
||||
return this.readerForm.rcolumns
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
299
src/views/dts/json-build/index.vue
Normal file
299
src/views/dts/json-build/index.vue
Normal file
@@ -0,0 +1,299 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="build-container">
|
||||
<el-steps :active="active" finish-status="success">
|
||||
<el-step title="步骤 1" description="构建reader">1</el-step>
|
||||
<el-step title="步骤 2" description="构建writer">2</el-step>
|
||||
<el-step title="步骤 3" description="字段映射">3</el-step>
|
||||
<el-step title="步骤 4" description="构建">4</el-step>
|
||||
</el-steps>
|
||||
|
||||
<div v-show="active===1" class="step1">
|
||||
<Reader ref="reader" />
|
||||
</div>
|
||||
<div v-show="active===2" class="step2">
|
||||
<Writer ref="writer" />
|
||||
</div>
|
||||
<div v-show="active===3" class="step3">
|
||||
<Mapper ref="mapper" />
|
||||
</div>
|
||||
<div v-show="active===4" class="step4">
|
||||
<el-button type="primary" @click="buildJson">1.构建</el-button>
|
||||
<el-button type="primary" @click="handleJobTemplateSelectDrawer">{{ jobTemplate ? jobTemplate : "2.选择模板" }}</el-button>
|
||||
<el-button type="info" @click="handleCopy(inputData,$event)">复制json</el-button>
|
||||
(步骤:构建->选择模板->下一步)
|
||||
<el-drawer
|
||||
ref="jobTemplateSelectDrawer"
|
||||
title="选择模板"
|
||||
:visible.sync="jobTemplateSelectDrawer"
|
||||
direction="rtl"
|
||||
size="50%"
|
||||
>
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
element-loading-text="Loading"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
destroy-on-close="true"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<el-table-column align="center" label="任务ID" width="80">
|
||||
<template slot-scope="scope">{{ scope.row.id }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="任务描述" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.jobDesc }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="所属项目" align="center" width="120">
|
||||
<template slot-scope="scope">{{ scope.row.projectName }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Cron" align="center">
|
||||
<template slot-scope="scope"><span>{{ scope.row.jobCron }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="路由策略" align="center">
|
||||
<template slot-scope="scope"> {{ routeStrategies.find(t => t.value === scope.row.executorRouteStrategy).label }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-show="total>0" :total="total" :page.sync="listQuery.current" :limit.sync="listQuery.size" @pagination="fetchData" />
|
||||
</el-drawer>
|
||||
<div style="margin-bottom: 20px;" />
|
||||
<json-editor v-show="active===4" ref="jsonEditor" v-model="configJson" />
|
||||
</div>
|
||||
|
||||
<el-button :disabled="active===1" style="margin-top: 12px;" @click="last">上一步</el-button>
|
||||
<el-button type="primary" style="margin-top: 12px;margin-bottom: 12px;" @click="next">下一步</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as dataxJsonApi from '@/api/dts/datax-json'
|
||||
import * as jobTemplate from '@/api/dts/datax-job-template'
|
||||
import * as job from '@/api/dts/datax-job-info'
|
||||
import Pagination from '@/components/Pagination'
|
||||
import JsonEditor from '@/components/JsonEditor'
|
||||
import Reader from './reader'
|
||||
import Writer from './writer'
|
||||
import clip from '@/utils/clipboard'
|
||||
import Mapper from './mapper'
|
||||
|
||||
export default {
|
||||
name: 'JsonBuild',
|
||||
components: { Reader, Writer, Pagination, JsonEditor, Mapper },
|
||||
data() {
|
||||
return {
|
||||
configJson: '',
|
||||
active: 1,
|
||||
jobTemplate: '',
|
||||
jobTemplateSelectDrawer: false,
|
||||
list: null,
|
||||
currentRow: null,
|
||||
listLoading: true,
|
||||
total: 0,
|
||||
listQuery: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
jobGroup: 0,
|
||||
triggerStatus: -1,
|
||||
jobDesc: '',
|
||||
executorHandler: '',
|
||||
userId: 0
|
||||
},
|
||||
blockStrategies: [
|
||||
{ value: 'SERIAL_EXECUTION', label: '单机串行' },
|
||||
{ value: 'DISCARD_LATER', label: '丢弃后续调度' },
|
||||
{ value: 'COVER_EARLY', label: '覆盖之前调度' }
|
||||
],
|
||||
routeStrategies: [
|
||||
{ value: 'FIRST', label: '第一个' },
|
||||
{ value: 'LAST', label: '最后一个' },
|
||||
{ value: 'ROUND', label: '轮询' },
|
||||
{ value: 'RANDOM', label: '随机' },
|
||||
{ value: 'CONSISTENT_HASH', label: '一致性HASH' },
|
||||
{ value: 'LEAST_FREQUENTLY_USED', label: '最不经常使用' },
|
||||
{ value: 'LEAST_RECENTLY_USED', label: '最近最久未使用' },
|
||||
{ value: 'FAILOVER', label: '故障转移' },
|
||||
{ value: 'BUSYOVER', label: '忙碌转移' }
|
||||
// { value: 'SHARDING_BROADCAST', label: '分片广播' }
|
||||
],
|
||||
triggerNextTimes: '',
|
||||
registerNode: [],
|
||||
jobJson: '',
|
||||
temp: {
|
||||
id: undefined,
|
||||
jobGroup: '',
|
||||
jobCron: '',
|
||||
jobDesc: '',
|
||||
executorRouteStrategy: '',
|
||||
executorBlockStrategy: '',
|
||||
childJobId: '',
|
||||
executorFailRetryCount: '',
|
||||
alarmEmail: '',
|
||||
executorTimeout: '',
|
||||
userId: 0,
|
||||
jobConfigId: '',
|
||||
executorHandler: 'executorJobHandler',
|
||||
glueType: 'BEAN',
|
||||
jobJson: '',
|
||||
executorParam: '',
|
||||
replaceParam: '',
|
||||
jvmParam: '',
|
||||
incStartTime: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// this.getJdbcDs()
|
||||
},
|
||||
methods: {
|
||||
next() {
|
||||
const fromColumnList = this.$refs.reader.getData().columns
|
||||
const toColumnsList = this.$refs.writer.getData().columns
|
||||
// const fromTableName = this.$refs.reader.getData().tableName
|
||||
// 第一步 reader 判断是否已选字段
|
||||
if (this.active === 1) {
|
||||
// 实现第一步骤读取的表和字段直接带到第二步骤
|
||||
// this.$refs.writer.sendTableNameAndColumns(fromTableName, fromColumnList)
|
||||
// 取子组件的数据
|
||||
// console.info(this.$refs.reader.getData())
|
||||
this.active++
|
||||
} else {
|
||||
// 将第一步和第二步得到的字段名字发送到第三步
|
||||
if (this.active === 2) {
|
||||
this.$refs.mapper.sendColumns(fromColumnList, toColumnsList)
|
||||
}
|
||||
if (this.active === 4) {
|
||||
this.temp.jobJson = this.configJson
|
||||
job.createJob(this.temp).then(() => {
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Created Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
// 切回第一步
|
||||
this.active = 1
|
||||
})
|
||||
} else {
|
||||
this.active++
|
||||
}
|
||||
}
|
||||
},
|
||||
last() {
|
||||
if (this.active > 1) {
|
||||
this.active--
|
||||
}
|
||||
},
|
||||
// 构建json
|
||||
buildJson() {
|
||||
const readerData = this.$refs.reader.getData()
|
||||
const writeData = this.$refs.writer.getData()
|
||||
const readerColumns = this.$refs.mapper.getLColumns()
|
||||
const writerColumns = this.$refs.mapper.getRColumns()
|
||||
const hiveReader = {
|
||||
readerPath: readerData.path,
|
||||
readerDefaultFS: readerData.defaultFS,
|
||||
readerFileType: readerData.fileType,
|
||||
readerFieldDelimiter: readerData.fieldDelimiter,
|
||||
readerSkipHeader: readerData.skipHeader
|
||||
}
|
||||
const hiveWriter = {
|
||||
writerDefaultFS: writeData.defaultFS,
|
||||
writerFileType: writeData.fileType,
|
||||
writerPath: writeData.path,
|
||||
writerFileName: writeData.fileName,
|
||||
writeMode: writeData.writeMode,
|
||||
writeFieldDelimiter: writeData.fieldDelimiter
|
||||
}
|
||||
const hbaseReader = {
|
||||
readerMode: readerData.mode,
|
||||
readerMaxVersion: readerData.maxVersion,
|
||||
readerRange: readerData.range
|
||||
}
|
||||
const hbaseWriter = {
|
||||
writerMode: writeData.mode,
|
||||
writerRowkeyColumn: writeData.rowkeyColumn,
|
||||
writerVersionColumn: writeData.versionColumn,
|
||||
writeNullMode: writeData.nullMode
|
||||
}
|
||||
const mongoDBReader = {}
|
||||
const mongoDBWriter = {
|
||||
upsertInfo: writeData.upsertInfo
|
||||
}
|
||||
const rdbmsReader = {
|
||||
readerSplitPk: readerData.splitPk,
|
||||
whereParams: readerData.where,
|
||||
querySql: readerData.querySql
|
||||
}
|
||||
const rdbmsWriter = {
|
||||
preSql: writeData.preSql,
|
||||
postSql: writeData.postSql
|
||||
}
|
||||
const obj = {
|
||||
readerDatasourceId: readerData.datasourceId,
|
||||
readerTables: [readerData.tableName],
|
||||
readerColumns: readerColumns,
|
||||
writerDatasourceId: writeData.datasourceId,
|
||||
writerTables: [writeData.tableName],
|
||||
writerColumns: writerColumns,
|
||||
hiveReader: hiveReader,
|
||||
hiveWriter: hiveWriter,
|
||||
rdbmsReader: rdbmsReader,
|
||||
rdbmsWriter: rdbmsWriter,
|
||||
hbaseReader: hbaseReader,
|
||||
hbaseWriter: hbaseWriter,
|
||||
mongoDBReader: mongoDBReader,
|
||||
mongoDBWriter: mongoDBWriter
|
||||
}
|
||||
// 调api
|
||||
dataxJsonApi.buildJobJson(obj).then(response => {
|
||||
this.configJson = JSON.parse(response.data)
|
||||
})
|
||||
},
|
||||
handleCopy(text, event) {
|
||||
clip(this.configJson, event)
|
||||
this.$message({
|
||||
message: 'copy success',
|
||||
type: 'success'
|
||||
})
|
||||
},
|
||||
handleJobTemplateSelectDrawer() {
|
||||
this.jobTemplateSelectDrawer = !this.jobTemplateSelectDrawer
|
||||
if (this.jobTemplateSelectDrawer) {
|
||||
this.fetchData()
|
||||
this.getExecutor()
|
||||
}
|
||||
},
|
||||
getReaderData() {
|
||||
return this.$refs.reader.getData()
|
||||
},
|
||||
getExecutor() {
|
||||
jobTemplate.getExecutorList().then(response => {
|
||||
const { content } = response
|
||||
this.executorList = content
|
||||
})
|
||||
},
|
||||
fetchData() {
|
||||
this.listLoading = true
|
||||
jobTemplate.getList(this.listQuery).then(response => {
|
||||
const { content } = response
|
||||
this.total = content.recordsTotal
|
||||
this.list = content.data
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.temp = Object.assign({}, val)
|
||||
this.temp.id = undefined
|
||||
this.temp.jobDesc = this.getReaderData().tableName
|
||||
this.$refs.jobTemplateSelectDrawer.closeDrawer()
|
||||
this.jobTemplate = val.id + '(' + val.jobDesc + ')'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
24
src/views/dts/json-build/mapper.vue
Normal file
24
src/views/dts/json-build/mapper.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<FieldMapper ref="mapper" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import FieldMapper from './components/fieldMapper'
|
||||
export default {
|
||||
name: 'Mapper',
|
||||
components: { FieldMapper },
|
||||
methods: {
|
||||
sendColumns(fromColumnsList, toColumnsList) {
|
||||
this.$refs.mapper.fromColumnsList = fromColumnsList
|
||||
this.$refs.mapper.toColumnsList = toColumnsList
|
||||
},
|
||||
getLColumns() {
|
||||
return this.$refs.mapper.getLColumns()
|
||||
},
|
||||
getRColumns() {
|
||||
return this.$refs.mapper.getRColumns()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
41
src/views/dts/json-build/reader.vue
Normal file
41
src/views/dts/json-build/reader.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<RDBMSReader v-show="dataSource!=='hive' && dataSource!=='hbase' && dataSource!=='mongodb'" ref="rdbmsreader" @selectDataSource="showDataSource" />
|
||||
<HiveReader v-show="dataSource==='hive'" ref="hivereader" @selectDataSource="showDataSource" />
|
||||
<HBaseReader v-show="dataSource==='hbase'" ref="hbasereader" @selectDataSource="showDataSource" />
|
||||
<MongoDBReader v-show="dataSource==='mongodb'" ref="mongodbreader" @selectDataSource="showDataSource" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RDBMSReader from './reader/RDBMSReader'
|
||||
import HiveReader from './reader/HiveReader'
|
||||
import HBaseReader from './reader/HBaseReader'
|
||||
import MongoDBReader from './reader/MongoDBReader'
|
||||
export default {
|
||||
name: 'Reader',
|
||||
components: { RDBMSReader, HiveReader, HBaseReader, MongoDBReader },
|
||||
data() {
|
||||
return {
|
||||
dataSource: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
if (this.dataSource === 'hive') {
|
||||
return this.$refs.hivereader.getData()
|
||||
} else if (this.dataSource === 'hbase') {
|
||||
return this.$refs.hbasereader.getData()
|
||||
} else if (this.dataSource === 'mongodb') {
|
||||
return this.$refs.mongodbreader.getData()
|
||||
} else {
|
||||
return this.$refs.rdbmsreader.getData()
|
||||
}
|
||||
},
|
||||
showDataSource(data) {
|
||||
this.dataSource = data
|
||||
this.getData()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
203
src/views/dts/json-build/reader/HBaseReader.vue
Normal file
203
src/views/dts/json-build/reader/HBaseReader.vue
Normal file
@@ -0,0 +1,203 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form label-position="left" label-width="105px" :model="readerForm" :rules="rules">
|
||||
<el-form-item label="数据源" prop="datasourceId">
|
||||
<el-select v-model="readerForm.datasourceId" filterable @change="rDsChange">
|
||||
<el-option
|
||||
v-for="item in rDsList"
|
||||
:key="item.id"
|
||||
:label="item.datasourceName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="表" prop="tableName">
|
||||
<el-select v-model="readerForm.tableName" filterable @change="rTbChange">
|
||||
<el-option v-for="item in rTbList" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="mode" prop="mode">
|
||||
<el-select v-model="readerForm.mode" placeholder="读取hbase的模式">
|
||||
<el-option v-for="item in modeTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="maxVersion">
|
||||
<el-input v-model="readerForm.maxVersion" placeholder="多版本模式下读取的版本数,取值只能为-1或者大于1的数字" style="width: 50%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="range">
|
||||
<el-input v-model="readerForm.range.startRowkey" placeholder="startRowkey指定开始rowkey" style="width: 50%" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input v-model="readerForm.range.endRowkey" placeholder="endRowkey指定结束rowkey" style="width: 50%" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-select v-model="readerForm.range.isBinaryRowkey" placeholder="转换方式">
|
||||
<el-option v-for="item in binaryRowkeyTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="字段">
|
||||
<el-checkbox
|
||||
v-model="readerForm.checkAll"
|
||||
:indeterminate="readerForm.isIndeterminate"
|
||||
@change="rHandleCheckAllChange"
|
||||
>全选</el-checkbox>
|
||||
<div style="margin: 15px 0;" />
|
||||
<el-checkbox-group v-model="readerForm.columns" @change="rHandleCheckedChange">
|
||||
<el-checkbox v-for="c in rColumnList" :key="c" :label="c">{{ c }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as dsQueryApi from '@/api/dts/metadata-query'
|
||||
import { list as jdbcDsList } from '@/api/dts/datax-jdbcDatasource'
|
||||
import Bus from '../busReader'
|
||||
|
||||
export default {
|
||||
name: 'HBaseReader',
|
||||
data() {
|
||||
return {
|
||||
jdbcDsQuery: {
|
||||
current: 1,
|
||||
size: 200
|
||||
},
|
||||
rDsList: [],
|
||||
rTbList: [],
|
||||
rColumnList: [],
|
||||
loading: false,
|
||||
active: 1,
|
||||
customFields: '',
|
||||
customType: '',
|
||||
customValue: '',
|
||||
dataSource: '',
|
||||
readerForm: {
|
||||
datasourceId: undefined,
|
||||
tableName: '',
|
||||
columns: [],
|
||||
checkAll: false,
|
||||
isIndeterminate: true,
|
||||
mode: '',
|
||||
maxVersion: '',
|
||||
range: {
|
||||
startRowkey: '',
|
||||
endRowkey: '',
|
||||
isBinaryRowkey: ''
|
||||
}
|
||||
},
|
||||
modeTypes: [
|
||||
{ value: 'normal', label: 'normal' },
|
||||
{ value: 'multiVersionFixedColumn', label: 'multiVersionFixedColumn' }
|
||||
],
|
||||
binaryRowkeyTypes: [
|
||||
{ value: 'true', label: '调用Bytes.toBytesBinary(rowkey)' },
|
||||
{ value: 'false', label: '调用Bytes.toBytes(rowkey)' }
|
||||
],
|
||||
rules: {
|
||||
mode: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
datasourceId: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
tableName: [{ required: true, message: 'this is required', trigger: 'blur' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'readerForm.datasourceId': function(oldVal, newVal) {
|
||||
this.getTables('hbaseReader')
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getJdbcDs()
|
||||
},
|
||||
methods: {
|
||||
// 获取可用数据源
|
||||
getJdbcDs() {
|
||||
this.loading = true
|
||||
jdbcDsList(this.jdbcDsQuery).then(response => {
|
||||
const { records } = response
|
||||
this.rDsList = records
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 获取表名
|
||||
getTables(type) {
|
||||
if (type === 'hbaseReader') {
|
||||
const obj = {
|
||||
datasourceId: this.readerForm.datasourceId
|
||||
}
|
||||
// 组装
|
||||
dsQueryApi.getTables(obj).then(response => {
|
||||
this.rTbList = response
|
||||
})
|
||||
}
|
||||
},
|
||||
// reader 数据源切换
|
||||
rDsChange(e) {
|
||||
// 清空
|
||||
this.readerForm.tableName = ''
|
||||
this.readerForm.datasourceId = e
|
||||
this.rDsList.find((item) => {
|
||||
if (item.id === e) {
|
||||
this.dataSource = item.datasource
|
||||
}
|
||||
})
|
||||
Bus.dataSourceId = e
|
||||
this.$emit('selectDataSource', this.dataSource)
|
||||
// 获取可用表
|
||||
this.getTables('reader')
|
||||
},
|
||||
getTableColumns() {
|
||||
const obj = {
|
||||
datasourceId: this.readerForm.datasourceId,
|
||||
tableName: this.readerForm.tableName
|
||||
}
|
||||
dsQueryApi.getColumns(obj).then(response => {
|
||||
this.rColumnList = response
|
||||
this.readerForm.columns = response
|
||||
this.readerForm.checkAll = true
|
||||
this.readerForm.isIndeterminate = false
|
||||
})
|
||||
},
|
||||
getColumnsByQuerySql() {
|
||||
const obj = {
|
||||
datasourceId: this.readerForm.datasourceId,
|
||||
querySql: this.readerForm.querySql
|
||||
}
|
||||
dsQueryApi.getColumnsByQuerySql(obj).then(response => {
|
||||
this.rColumnList = response
|
||||
this.readerForm.columns = response
|
||||
this.readerForm.checkAll = true
|
||||
this.readerForm.isIndeterminate = false
|
||||
})
|
||||
},
|
||||
// 获取表字段
|
||||
getColumns(type) {
|
||||
if (type === 'reader') {
|
||||
this.getTableColumns()
|
||||
}
|
||||
},
|
||||
// 表切换
|
||||
rTbChange(t) {
|
||||
this.readerForm.tableName = t
|
||||
this.rColumnList = []
|
||||
this.readerForm.columns = []
|
||||
this.getColumns('reader')
|
||||
},
|
||||
rHandleCheckAllChange(val) {
|
||||
this.readerForm.columns = val ? this.rColumnList : []
|
||||
this.readerForm.isIndeterminate = false
|
||||
},
|
||||
rHandleCheckedChange(value) {
|
||||
const checkedCount = value.length
|
||||
this.readerForm.checkAll = checkedCount === this.rColumnList.length
|
||||
this.readerForm.isIndeterminate = checkedCount > 0 && checkedCount < this.rColumnList.length
|
||||
},
|
||||
getData() {
|
||||
if (Bus.dataSourceId) {
|
||||
this.readerForm.datasourceId = Bus.dataSourceId
|
||||
}
|
||||
return this.readerForm
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
206
src/views/dts/json-build/reader/HiveReader.vue
Normal file
206
src/views/dts/json-build/reader/HiveReader.vue
Normal file
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form label-position="left" label-width="105px" :model="readerForm" :rules="rules">
|
||||
<el-form-item label="数据源" prop="datasourceId">
|
||||
<el-select v-model="readerForm.datasourceId" filterable @change="rDsChange">
|
||||
<el-option
|
||||
v-for="item in rDsList"
|
||||
:key="item.id"
|
||||
:label="item.datasourceName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="表" prop="tableName">
|
||||
<el-select v-model="readerForm.tableName" filterable @change="rTbChange">
|
||||
<el-option v-for="item in rTbList" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="path" prop="path">
|
||||
<el-input v-model="readerForm.path" :autosize="{ minRows: 2, maxRows: 20}" type="textarea" placeholder="要读取的文件路径,如果要读取多个文件,可以使用正则表达式'*'" style="width: 42%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="defaultFS" prop="defaultFS">
|
||||
<el-input v-model="readerForm.defaultFS" placeholder="Hadoop hdfs文件系统namenode节点地址" style="width: 42%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="fileType" prop="fileType">
|
||||
<el-select v-model="readerForm.fileType" placeholder="文件的类型">
|
||||
<el-option v-for="item in fileTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="fieldDelimiter" prop="fieldDelimiter">
|
||||
<el-input v-model="readerForm.fieldDelimiter" placeholder="读取的字段分隔符" style="width: 42%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="skipHeader">
|
||||
<el-select v-model="readerForm.skipHeader" placeholder="是否跳过表头">
|
||||
<el-option v-for="item in skipHeaderTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="字段">
|
||||
<el-checkbox
|
||||
v-model="readerForm.checkAll"
|
||||
:indeterminate="readerForm.isIndeterminate"
|
||||
@change="rHandleCheckAllChange"
|
||||
>全选</el-checkbox>
|
||||
<div style="margin: 15px 0;" />
|
||||
<el-checkbox-group v-model="readerForm.columns" @change="rHandleCheckedChange">
|
||||
<el-checkbox v-for="c in rColumnList" :key="c" :label="c">{{ c }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as dsQueryApi from '@/api/dts/metadata-query'
|
||||
import { list as jdbcDsList } from '@/api/dts/datax-jdbcDatasource'
|
||||
import Bus from '../busReader'
|
||||
|
||||
export default {
|
||||
name: 'HiveReader',
|
||||
data() {
|
||||
return {
|
||||
jdbcDsQuery: {
|
||||
current: 1,
|
||||
size: 200
|
||||
},
|
||||
rDsList: [],
|
||||
rTbList: [],
|
||||
rColumnList: [],
|
||||
loading: false,
|
||||
active: 1,
|
||||
customFields: '',
|
||||
customType: '',
|
||||
customValue: '',
|
||||
dataSource: '',
|
||||
readerForm: {
|
||||
datasourceId: undefined,
|
||||
tableName: '',
|
||||
columns: [],
|
||||
checkAll: false,
|
||||
isIndeterminate: true,
|
||||
path: '',
|
||||
defaultFS: '',
|
||||
fileType: '',
|
||||
fieldDelimiter: '',
|
||||
skipHeader: ''
|
||||
},
|
||||
rules: {
|
||||
path: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
defaultFS: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
fileType: [{ required: true, message: 'this is required', trigger: 'change' }],
|
||||
datasourceId: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
tableName: [{ required: true, message: 'this is required', trigger: 'blur' }]
|
||||
},
|
||||
fileTypes: [
|
||||
{ value: 'text', label: 'text' },
|
||||
{ value: 'orc', label: 'orc' },
|
||||
{ value: 'rc', label: 'rc' },
|
||||
{ value: 'seq', label: 'seq' },
|
||||
{ value: 'csv', label: 'csv' }
|
||||
],
|
||||
skipHeaderTypes: [
|
||||
{ value: 'true', label: '读取跳过表头' },
|
||||
{ value: 'false', label: '读取包含表头' }
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'readerForm.datasourceId': function(oldVal, newVal) {
|
||||
this.getTables('hiveReader')
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getJdbcDs()
|
||||
},
|
||||
methods: {
|
||||
// 获取可用数据源
|
||||
getJdbcDs(type) {
|
||||
this.loading = true
|
||||
jdbcDsList(this.jdbcDsQuery).then(response => {
|
||||
const { records } = response
|
||||
this.rDsList = records
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 获取表名
|
||||
getTables(type) {
|
||||
if (type === 'hiveReader') {
|
||||
const obj = {
|
||||
datasourceId: this.readerForm.datasourceId
|
||||
}
|
||||
// 组装
|
||||
dsQueryApi.getTables(obj).then(response => {
|
||||
this.rTbList = response
|
||||
})
|
||||
}
|
||||
},
|
||||
// reader 数据源切换
|
||||
rDsChange(e) {
|
||||
// 清空
|
||||
this.readerForm.tableName = ''
|
||||
this.readerForm.datasourceId = e
|
||||
this.rDsList.find((item) => {
|
||||
if (item.id === e) {
|
||||
this.dataSource = item.datasource
|
||||
}
|
||||
})
|
||||
Bus.dataSourceId = e
|
||||
this.$emit('selectDataSource', this.dataSource)
|
||||
// 获取可用表
|
||||
this.getTables('reader')
|
||||
},
|
||||
getTableColumns() {
|
||||
const obj = {
|
||||
datasourceId: this.readerForm.datasourceId,
|
||||
tableName: this.readerForm.tableName
|
||||
}
|
||||
dsQueryApi.getColumns(obj).then(response => {
|
||||
this.rColumnList = response
|
||||
this.readerForm.columns = response
|
||||
this.readerForm.checkAll = true
|
||||
this.readerForm.isIndeterminate = false
|
||||
})
|
||||
},
|
||||
getColumnsByQuerySql() {
|
||||
const obj = {
|
||||
datasourceId: this.readerForm.datasourceId,
|
||||
querySql: this.readerForm.querySql
|
||||
}
|
||||
dsQueryApi.getColumnsByQuerySql(obj).then(response => {
|
||||
this.rColumnList = response
|
||||
this.readerForm.columns = response
|
||||
this.readerForm.checkAll = true
|
||||
this.readerForm.isIndeterminate = false
|
||||
})
|
||||
},
|
||||
// 获取表字段
|
||||
getColumns(type) {
|
||||
if (type === 'reader') {
|
||||
this.getTableColumns()
|
||||
}
|
||||
},
|
||||
// 表切换
|
||||
rTbChange(t) {
|
||||
this.readerForm.tableName = t
|
||||
this.rColumnList = []
|
||||
this.readerForm.columns = []
|
||||
this.getColumns('reader')
|
||||
},
|
||||
rHandleCheckAllChange(val) {
|
||||
this.readerForm.columns = val ? this.rColumnList : []
|
||||
this.readerForm.isIndeterminate = false
|
||||
},
|
||||
rHandleCheckedChange(value) {
|
||||
const checkedCount = value.length
|
||||
this.readerForm.checkAll = checkedCount === this.rColumnList.length
|
||||
this.readerForm.isIndeterminate = checkedCount > 0 && checkedCount < this.rColumnList.length
|
||||
},
|
||||
getData() {
|
||||
if (Bus.dataSourceId) {
|
||||
this.readerForm.datasourceId = Bus.dataSourceId
|
||||
}
|
||||
return this.readerForm
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
169
src/views/dts/json-build/reader/MongoDBReader.vue
Normal file
169
src/views/dts/json-build/reader/MongoDBReader.vue
Normal file
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form label-position="left" label-width="105px" :model="readerForm" :rules="rules">
|
||||
<el-form-item label="数据源" prop="datasourceId">
|
||||
<el-select v-model="readerForm.datasourceId" filterable @change="rDsChange">
|
||||
<el-option
|
||||
v-for="item in rDsList"
|
||||
:key="item.id"
|
||||
:label="item.datasourceName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="文档" prop="tableName">
|
||||
<el-select v-model="readerForm.tableName" filterable @change="rTbChange">
|
||||
<el-option v-for="item in rTbList" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="字段">
|
||||
<el-checkbox
|
||||
v-model="readerForm.checkAll"
|
||||
:indeterminate="readerForm.isIndeterminate"
|
||||
@change="rHandleCheckAllChange"
|
||||
>全选</el-checkbox>
|
||||
<div style="margin: 15px 0;" />
|
||||
<el-checkbox-group v-model="readerForm.columns" @change="rHandleCheckedChange">
|
||||
<el-checkbox v-for="c in rColumnList" :key="c" :label="c">{{ c }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as dsQueryApi from '@/api/dts/metadata-query'
|
||||
import { list as jdbcDsList } from '@/api/dts/datax-jdbcDatasource'
|
||||
import Bus from '../busReader'
|
||||
|
||||
export default {
|
||||
name: 'MongoDBReader',
|
||||
data() {
|
||||
return {
|
||||
jdbcDsQuery: {
|
||||
current: 1,
|
||||
size: 200
|
||||
},
|
||||
rDsList: [],
|
||||
rTbList: [],
|
||||
rColumnList: [],
|
||||
loading: false,
|
||||
active: 1,
|
||||
customFields: '',
|
||||
customType: '',
|
||||
customValue: '',
|
||||
dataSource: '',
|
||||
readerForm: {
|
||||
datasourceId: undefined,
|
||||
tableName: '',
|
||||
columns: [],
|
||||
checkAll: false,
|
||||
isIndeterminate: true
|
||||
},
|
||||
rules: {
|
||||
mode: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
datasourceId: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
tableName: [{ required: true, message: 'this is required', trigger: 'blur' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'readerForm.datasourceId': function(oldVal, newVal) {
|
||||
this.getTables('mongodbReader')
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getJdbcDs()
|
||||
},
|
||||
methods: {
|
||||
// 获取可用数据源
|
||||
getJdbcDs() {
|
||||
this.loading = true
|
||||
jdbcDsList(this.jdbcDsQuery).then(response => {
|
||||
const { records } = response
|
||||
this.rDsList = records
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 获取表名
|
||||
getTables(type) {
|
||||
if (type === 'mongodbReader') {
|
||||
const obj = {
|
||||
datasourceId: this.readerForm.datasourceId
|
||||
}
|
||||
// 组装
|
||||
dsQueryApi.getTables(obj).then(response => {
|
||||
this.rTbList = response
|
||||
})
|
||||
}
|
||||
},
|
||||
// reader 数据源切换
|
||||
rDsChange(e) {
|
||||
// 清空
|
||||
this.readerForm.tableName = ''
|
||||
this.readerForm.datasourceId = e
|
||||
this.rDsList.find((item) => {
|
||||
if (item.id === e) {
|
||||
this.dataSource = item.datasource
|
||||
}
|
||||
})
|
||||
Bus.dataSourceId = e
|
||||
this.$emit('selectDataSource', this.dataSource)
|
||||
// 获取可用表
|
||||
this.getTables('reader')
|
||||
},
|
||||
getTableColumns() {
|
||||
const obj = {
|
||||
datasourceId: this.readerForm.datasourceId,
|
||||
tableName: this.readerForm.tableName
|
||||
}
|
||||
dsQueryApi.getColumns(obj).then(response => {
|
||||
this.rColumnList = response
|
||||
this.readerForm.columns = response
|
||||
this.readerForm.checkAll = true
|
||||
this.readerForm.isIndeterminate = false
|
||||
})
|
||||
},
|
||||
getColumnsByQuerySql() {
|
||||
const obj = {
|
||||
datasourceId: this.readerForm.datasourceId,
|
||||
querySql: this.readerForm.querySql
|
||||
}
|
||||
dsQueryApi.getColumnsByQuerySql(obj).then(response => {
|
||||
this.rColumnList = response
|
||||
this.readerForm.columns = response
|
||||
this.readerForm.checkAll = true
|
||||
this.readerForm.isIndeterminate = false
|
||||
})
|
||||
},
|
||||
// 获取表字段
|
||||
getColumns(type) {
|
||||
if (type === 'reader') {
|
||||
this.getTableColumns()
|
||||
}
|
||||
},
|
||||
// 表切换
|
||||
rTbChange(t) {
|
||||
this.readerForm.tableName = t
|
||||
this.rColumnList = []
|
||||
this.readerForm.columns = []
|
||||
this.getColumns('reader')
|
||||
},
|
||||
rHandleCheckAllChange(val) {
|
||||
this.readerForm.columns = val ? this.rColumnList : []
|
||||
this.readerForm.isIndeterminate = false
|
||||
},
|
||||
rHandleCheckedChange(value) {
|
||||
const checkedCount = value.length
|
||||
this.readerForm.checkAll = checkedCount === this.rColumnList.length
|
||||
this.readerForm.isIndeterminate = checkedCount > 0 && checkedCount < this.rColumnList.length
|
||||
},
|
||||
getData() {
|
||||
if (Bus.dataSourceId) {
|
||||
this.readerForm.datasourceId = Bus.dataSourceId
|
||||
}
|
||||
return this.readerForm
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
226
src/views/dts/json-build/reader/RDBMSReader.vue
Normal file
226
src/views/dts/json-build/reader/RDBMSReader.vue
Normal file
@@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form label-position="right" label-width="120px" :model="readerForm" :rules="rules">
|
||||
<el-form-item label="数据库源:" prop="datasourceId">
|
||||
<el-select v-model="readerForm.datasourceId" filterable style="width: 300px" @change="rDsChange">
|
||||
<el-option
|
||||
v-for="item in rDsList"
|
||||
:key="item.id"
|
||||
:label="item.datasourceName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="dataSource==='postgresql' || dataSource==='oracle' || dataSource==='sqlserver' || dataSource==='hana'" label="Schema:" prop="tableSchema">
|
||||
<el-select v-model="readerForm.tableSchema" allow-create default-first-option filterable style="width: 300px" @change="schemaChange">
|
||||
<el-option
|
||||
v-for="item in schemaList"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据库表名:" prop="tableName">
|
||||
<el-select v-model="readerForm.tableName" allow-create default-first-option filterable style="width: 300px" @change="rTbChange">
|
||||
<el-option v-for="item in rTbList" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="SQL语句:">
|
||||
<el-input v-model="readerForm.querySql" :autosize="{ minRows: 3, maxRows: 20}" type="textarea" placeholder="sql查询,一般用于多表关联查询时才用" style="width: 42%" />
|
||||
<el-button type="primary" @click.prevent="getColumns('reader')">解析字段</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="切分字段:">
|
||||
<el-input v-model="readerForm.splitPk" placeholder="切分主键" style="width: 13%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="表所有字段:">
|
||||
<el-checkbox
|
||||
v-model="readerForm.checkAll"
|
||||
:indeterminate="readerForm.isIndeterminate"
|
||||
@change="rHandleCheckAllChange"
|
||||
>全选
|
||||
</el-checkbox>
|
||||
<div style="margin: 15px 0;" />
|
||||
<el-checkbox-group v-model="readerForm.columns" @change="rHandleCheckedChange">
|
||||
<el-checkbox v-for="c in rColumnList" :key="c" :label="c">{{ c }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="where条件:" prop="where">
|
||||
<el-input v-model="readerForm.where" placeholder="where条件,不需要再加where" type="textarea" style="width: 42%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as dsQueryApi from '@/api/dts/metadata-query'
|
||||
import { list as jdbcDsList } from '@/api/dts/datax-jdbcDatasource'
|
||||
import Bus from '../busReader'
|
||||
|
||||
export default {
|
||||
name: 'RDBMSReader',
|
||||
data() {
|
||||
return {
|
||||
jdbcDsQuery: {
|
||||
current: 1,
|
||||
size: 200,
|
||||
ascs: 'datasource_name'
|
||||
},
|
||||
rDsList: [],
|
||||
rTbList: [],
|
||||
schemaList: [],
|
||||
rColumnList: [],
|
||||
loading: false,
|
||||
active: 1,
|
||||
customFields: '',
|
||||
customType: '',
|
||||
customValue: '',
|
||||
dataSource: '',
|
||||
readerForm: {
|
||||
datasourceId: undefined,
|
||||
tableName: '',
|
||||
columns: [],
|
||||
where: '',
|
||||
querySql: '',
|
||||
checkAll: false,
|
||||
isIndeterminate: true,
|
||||
splitPk: '',
|
||||
tableSchema: ''
|
||||
},
|
||||
rules: {
|
||||
datasourceId: [{ required: true, message: 'this is required', trigger: 'change' }],
|
||||
tableName: [{ required: true, message: 'this is required', trigger: 'change' }],
|
||||
tableSchema: [{ required: true, message: 'this is required', trigger: 'change' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'readerForm.datasourceId': function(oldVal, newVal) {
|
||||
if (this.dataSource === 'postgresql' || this.dataSource === 'oracle' || this.dataSource === 'sqlserver' || this.dataSource === 'hana') {
|
||||
this.getSchema()
|
||||
} else {
|
||||
this.getTables('rdbmsReader')
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getJdbcDs()
|
||||
},
|
||||
methods: {
|
||||
// 获取可用数据源
|
||||
getJdbcDs(type) {
|
||||
this.loading = true
|
||||
jdbcDsList(this.jdbcDsQuery).then(response => {
|
||||
const { records } = response.data
|
||||
this.rDsList = records
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 获取表名
|
||||
getTables(type) {
|
||||
if (type === 'rdbmsReader') {
|
||||
let obj = {}
|
||||
if (this.dataSource === 'postgresql' || this.dataSource === 'oracle' || this.dataSource === 'sqlserver' || this.dataSource === 'hana') {
|
||||
obj = {
|
||||
datasourceId: this.readerForm.datasourceId,
|
||||
tableSchema: this.readerForm.tableSchema
|
||||
}
|
||||
} else {
|
||||
obj = {
|
||||
datasourceId: this.readerForm.datasourceId
|
||||
}
|
||||
}
|
||||
// 组装
|
||||
dsQueryApi.getTables(obj).then(response => {
|
||||
if (response) {
|
||||
this.rTbList = response.data
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
getSchema() {
|
||||
const obj = {
|
||||
datasourceId: this.readerForm.datasourceId
|
||||
}
|
||||
dsQueryApi.getTableSchema(obj).then(response => {
|
||||
this.schemaList = response
|
||||
})
|
||||
},
|
||||
// schema 切换
|
||||
schemaChange(e) {
|
||||
this.readerForm.tableSchema = e
|
||||
// 获取可用表
|
||||
this.getTables('rdbmsReader')
|
||||
},
|
||||
// reader 数据源切换
|
||||
rDsChange(e) {
|
||||
// 清空
|
||||
this.readerForm.tableName = ''
|
||||
this.readerForm.datasourceId = e
|
||||
this.rDsList.find((item) => {
|
||||
if (item.id === e) {
|
||||
this.dataSource = item.datasource
|
||||
}
|
||||
})
|
||||
Bus.dataSourceId = e
|
||||
this.$emit('selectDataSource', this.dataSource)
|
||||
},
|
||||
getTableColumns() {
|
||||
const obj = {
|
||||
datasourceId: this.readerForm.datasourceId,
|
||||
tableName: this.readerForm.tableName
|
||||
}
|
||||
dsQueryApi.getColumns(obj).then(response => {
|
||||
this.rColumnList = response.data
|
||||
this.readerForm.columns = response.data
|
||||
this.readerForm.checkAll = true
|
||||
this.readerForm.isIndeterminate = false
|
||||
})
|
||||
},
|
||||
getColumnsByQuerySql() {
|
||||
const obj = {
|
||||
datasourceId: this.readerForm.datasourceId,
|
||||
querySql: this.readerForm.querySql
|
||||
}
|
||||
dsQueryApi.getColumnsByQuerySql(obj).then(response => {
|
||||
this.rColumnList = response
|
||||
this.readerForm.columns = response
|
||||
this.readerForm.checkAll = true
|
||||
this.readerForm.isIndeterminate = false
|
||||
})
|
||||
},
|
||||
// 获取表字段
|
||||
getColumns(type) {
|
||||
if (type === 'reader') {
|
||||
if (this.readerForm.querySql !== '') {
|
||||
this.getColumnsByQuerySql()
|
||||
} else {
|
||||
this.getTableColumns()
|
||||
}
|
||||
}
|
||||
},
|
||||
// 表切换
|
||||
rTbChange(t) {
|
||||
this.readerForm.tableName = t
|
||||
this.rColumnList = []
|
||||
this.readerForm.columns = []
|
||||
this.getColumns('reader')
|
||||
},
|
||||
rHandleCheckAllChange(val) {
|
||||
this.readerForm.columns = val ? this.rColumnList : []
|
||||
this.readerForm.isIndeterminate = false
|
||||
},
|
||||
rHandleCheckedChange(value) {
|
||||
const checkedCount = value.length
|
||||
this.readerForm.checkAll = checkedCount === this.rColumnList.length
|
||||
this.readerForm.isIndeterminate = checkedCount > 0 && checkedCount < this.rColumnList.length
|
||||
},
|
||||
getData() {
|
||||
if (Bus.dataSourceId) {
|
||||
this.readerForm.datasourceId = Bus.dataSourceId
|
||||
}
|
||||
return this.readerForm
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
60
src/views/dts/json-build/writer.vue
Normal file
60
src/views/dts/json-build/writer.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<RDBMSWriter v-show="dataSource!=='hive' && dataSource!=='hbase' && dataSource!=='mongodb'" ref="rdbmswriter" @selectDataSource="showDataSource" />
|
||||
<HiveWriter v-show="dataSource==='hive'" ref="hivewriter" @selectDataSource="showDataSource" />
|
||||
<HBaseWriter v-show="dataSource==='hbase'" ref="hbasewriter" @selectDataSource="showDataSource" />
|
||||
<MongoDBWriter v-show="dataSource==='mongodb'" ref="mongodbwriter" @selectDataSource="showDataSource" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RDBMSWriter from './writer/RDBMSWriter'
|
||||
import HiveWriter from './writer/HiveWriter'
|
||||
import HBaseWriter from './writer/HBaseWriter'
|
||||
import MongoDBWriter from './writer/MongoDBWriter'
|
||||
export default {
|
||||
name: 'Writer',
|
||||
components: { RDBMSWriter, HiveWriter, HBaseWriter, MongoDBWriter },
|
||||
data() {
|
||||
return {
|
||||
dataSource: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
if (this.dataSource === 'hive') {
|
||||
return this.$refs.hivewriter.getData()
|
||||
} else if (this.dataSource === 'hbase') {
|
||||
return this.$refs.hbasewriter.getData()
|
||||
} else if (this.dataSource === 'mongodb') {
|
||||
return this.$refs.mongodbwriter.getData()
|
||||
} else {
|
||||
return this.$refs.rdbmswriter.getData()
|
||||
}
|
||||
},
|
||||
getTableName() {
|
||||
if (this.dataSource === 'hive') {
|
||||
return this.$refs.hivewriter.getTableName()
|
||||
} else if (this.dataSource === 'hbase') {
|
||||
return this.$refs.hbasewriter.getData()
|
||||
} else if (this.dataSource === 'mongodb') {
|
||||
return this.$refs.mongodbwriter.getData()
|
||||
} else {
|
||||
return this.$refs.rdbmswriter.getTableName()
|
||||
}
|
||||
},
|
||||
getReaderData() {
|
||||
return this.$parent.getReaderData()
|
||||
},
|
||||
showDataSource(data) {
|
||||
this.dataSource = data
|
||||
this.getData()
|
||||
},
|
||||
sendTableNameAndColumns(fromTableName, fromColumnList) {
|
||||
this.$refs.hivewriter.fromTableName = fromTableName
|
||||
this.$refs.hivewriter.fromColumnList = fromColumnList
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
223
src/views/dts/json-build/writer/HBaseWriter.vue
Normal file
223
src/views/dts/json-build/writer/HBaseWriter.vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form label-position="left" label-width="115px" :model="writerForm" :rules="rules">
|
||||
<el-form-item label="数据源" prop="datasourceId">
|
||||
<el-select
|
||||
v-model="writerForm.datasourceId"
|
||||
filterable
|
||||
@change="wDsChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in wDsList"
|
||||
:key="item.id"
|
||||
:label="item.datasourceName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="表" prop="fromTableName">
|
||||
<el-select
|
||||
v-model="fromTableName"
|
||||
:disabled="writerForm.ifCreateTable"
|
||||
filterable
|
||||
@change="wTbChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in wTbList"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="rowkeyColumn" prop="rowkeyColumn">
|
||||
<el-input v-model="writerForm.rowkeyColumn" :autosize="{ minRows: 5, maxRows: 20}" type="textarea" style="width: 42%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="versionColumn">
|
||||
<el-input v-model="writerForm.versionColumn.index" placeholder="index指定对应reader端column的索引" style="width: 42%" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input v-model="writerForm.versionColumn.value" placeholder="value指定时间的值,long值" style="width: 42%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="nullMode">
|
||||
<el-select v-model="writerForm.nullMode" placeholder="null值转换方式">
|
||||
<el-option v-for="item in nullModeTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="字段">
|
||||
<el-checkbox v-model="writerForm.checkAll" :indeterminate="writerForm.isIndeterminate" @change="wHandleCheckAllChange">全选</el-checkbox>
|
||||
<div style="margin: 15px 0;" />
|
||||
<el-checkbox-group v-model="writerForm.columns" @change="wHandleCheckedChange">
|
||||
<el-checkbox v-for="c in fromColumnList" :key="c" :label="c">{{ c }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as dsQueryApi from '@/api/dts/metadata-query'
|
||||
import { list as jdbcDsList } from '@/api/dts/datax-jdbcDatasource'
|
||||
import Bus from '../busWriter'
|
||||
export default {
|
||||
name: 'HBaseWriter',
|
||||
data() {
|
||||
const checkJson = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('不能为空'))
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
try {
|
||||
var obj = JSON.parse(value)
|
||||
if (typeof obj !== 'object' || !obj) {
|
||||
callback(new Error('JSON格式错误'))
|
||||
}
|
||||
if (!(obj instanceof Array)) {
|
||||
callback(new Error('JSON必须为数组'))
|
||||
}
|
||||
} catch (e) {
|
||||
callback(new Error('JSON格式错误'))
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
jdbcDsQuery: {
|
||||
current: 1,
|
||||
size: 200
|
||||
},
|
||||
wDsList: [],
|
||||
fromTableName: '',
|
||||
fromColumnList: [],
|
||||
wTbList: [],
|
||||
dataSource: '',
|
||||
writerForm: {
|
||||
datasourceId: undefined,
|
||||
tableName: '',
|
||||
columns: [],
|
||||
checkAll: false,
|
||||
isIndeterminate: true,
|
||||
ifCreateTable: false,
|
||||
mode: 'normal',
|
||||
rowkeyColumn: '[{\n' +
|
||||
'\t"index": 0,\n' +
|
||||
'\t"type": "string"\n' +
|
||||
'}]',
|
||||
versionColumn: {
|
||||
index: '',
|
||||
value: ''
|
||||
},
|
||||
nullMode: ''
|
||||
},
|
||||
nullModeTypes: [
|
||||
{ value: 'skip', label: '不向hbase写这列' },
|
||||
{ value: 'empty', label: '写入new byte [0]' }
|
||||
],
|
||||
rules: {
|
||||
mode: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
datasourceId: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
fromTableName: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
rowkeyColumn: [{ required: true, trigger: 'blur', validator: checkJson }]
|
||||
},
|
||||
readerForm: this.getReaderData()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'writerForm.datasourceId': function(oldVal, newVal) {
|
||||
this.getTables('hbaseWriter')
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getJdbcDs()
|
||||
},
|
||||
methods: {
|
||||
// 获取可用数据源
|
||||
getJdbcDs(type) {
|
||||
this.loading = true
|
||||
jdbcDsList(this.jdbcDsQuery).then(response => {
|
||||
const { records } = response
|
||||
this.wDsList = records
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 获取表名
|
||||
getTables(type) {
|
||||
if (type === 'hbaseWriter') {
|
||||
const obj = {
|
||||
datasourceId: this.writerForm.datasourceId
|
||||
}
|
||||
// 组装
|
||||
dsQueryApi.getTables(obj).then(response => {
|
||||
this.wTbList = response
|
||||
})
|
||||
}
|
||||
},
|
||||
wDsChange(e) {
|
||||
// 清空
|
||||
this.writerForm.tableName = ''
|
||||
this.writerForm.datasourceId = e
|
||||
this.wDsList.find((item) => {
|
||||
if (item.id === e) {
|
||||
this.dataSource = item.datasource
|
||||
}
|
||||
})
|
||||
Bus.dataSourceId = e
|
||||
this.$emit('selectDataSource', this.dataSource)
|
||||
// 获取可用表
|
||||
this.getTables()
|
||||
},
|
||||
// 获取表字段
|
||||
getColumns() {
|
||||
const obj = {
|
||||
datasourceId: this.writerForm.datasourceId,
|
||||
tableName: this.writerForm.tableName
|
||||
}
|
||||
dsQueryApi.getColumns(obj).then(response => {
|
||||
this.fromColumnList = response
|
||||
this.writerForm.columns = response
|
||||
this.writerForm.checkAll = true
|
||||
this.writerForm.isIndeterminate = false
|
||||
})
|
||||
},
|
||||
// 表切换
|
||||
wTbChange(t) {
|
||||
this.writerForm.tableName = t
|
||||
this.fromColumnList = []
|
||||
this.writerForm.columns = []
|
||||
this.getColumns('writer')
|
||||
},
|
||||
wHandleCheckAllChange(val) {
|
||||
this.writerForm.columns = val ? this.fromColumnList : []
|
||||
this.writerForm.isIndeterminate = false
|
||||
},
|
||||
wHandleCheckedChange(value) {
|
||||
const checkedCount = value.length
|
||||
this.writerForm.checkAll = checkedCount === this.fromColumnList.length
|
||||
this.writerForm.isIndeterminate = checkedCount > 0 && checkedCount < this.fromColumnList.length
|
||||
},
|
||||
createTableCheckedChange(val) {
|
||||
this.writerForm.tableName = val ? this.readerForm.tableName : ''
|
||||
this.fromColumnList = this.readerForm.columns
|
||||
this.writerForm.columns = this.readerForm.columns
|
||||
this.writerForm.checkAll = true
|
||||
this.writerForm.isIndeterminate = false
|
||||
},
|
||||
getData() {
|
||||
if (Bus.dataSourceId) {
|
||||
this.writerForm.datasourceId = Bus.dataSourceId
|
||||
}
|
||||
return this.writerForm
|
||||
},
|
||||
getReaderData() {
|
||||
return this.$parent.getReaderData()
|
||||
},
|
||||
getTableName() {
|
||||
return this.fromTableName
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
240
src/views/dts/json-build/writer/HiveWriter.vue
Normal file
240
src/views/dts/json-build/writer/HiveWriter.vue
Normal file
@@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form label-position="left" label-width="105px" :model="writerForm" :rules="rules">
|
||||
<el-form-item label="数据源" prop="datasourceId">
|
||||
<el-select
|
||||
v-model="writerForm.datasourceId"
|
||||
filterable
|
||||
@change="wDsChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in wDsList"
|
||||
:key="item.id"
|
||||
:label="item.datasourceName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="表" prop="fromTableName">
|
||||
<el-select
|
||||
v-model="fromTableName"
|
||||
:disabled="writerForm.ifCreateTable"
|
||||
filterable
|
||||
@change="wTbChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in wTbList"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!--<el-col :span="6">
|
||||
<el-form-item>
|
||||
<el-button type="primary" :disabled="!this.fromTableName" @click="createTable()">一键生成目标表</el-button>
|
||||
</el-form-item>
|
||||
</el-col>-->
|
||||
</el-row>
|
||||
<el-form-item label="path" prop="path">
|
||||
<el-input v-model="writerForm.path" :autosize="{ minRows: 2, maxRows: 20}" type="textarea" placeholder="为与hive表关联,请填写hive表在hdfs上的存储路径" style="width: 42%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="defaultFS" prop="defaultFS">
|
||||
<el-input v-model="writerForm.defaultFS" placeholder="Hadoop hdfs文件系统namenode节点地址" style="width: 42%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="fileName" prop="fileName">
|
||||
<el-input v-model="writerForm.fileName" placeholder="HdfsWriter写入时的文件名" style="width: 42%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="fileType" prop="fileType">
|
||||
<el-select v-model="writerForm.fileType" placeholder="文件的类型">
|
||||
<el-option v-for="item in fileTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="writeMode" prop="writeMode">
|
||||
<el-select v-model="writerForm.writeMode" placeholder="文件的类型">
|
||||
<el-option v-for="item in writeModes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="fieldDelimiter" prop="fieldDelimiter">
|
||||
<el-input v-model="writerForm.fieldDelimiter" placeholder="与创建表的分隔符一致" style="width: 13%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="字段">
|
||||
<el-checkbox v-model="writerForm.checkAll" :indeterminate="writerForm.isIndeterminate" @change="wHandleCheckAllChange">全选</el-checkbox>
|
||||
<div style="margin: 15px 0;" />
|
||||
<el-checkbox-group v-model="writerForm.columns" @change="wHandleCheckedChange">
|
||||
<el-checkbox v-for="c in fromColumnList" :key="c" :label="c">{{ c }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as dsQueryApi from '@/api/dts/metadata-query'
|
||||
import { list as jdbcDsList } from '@/api/dts/datax-jdbcDatasource'
|
||||
import Bus from '../busWriter'
|
||||
export default {
|
||||
name: 'HiveWriter',
|
||||
data() {
|
||||
return {
|
||||
jdbcDsQuery: {
|
||||
current: 1,
|
||||
size: 200
|
||||
},
|
||||
wDsList: [],
|
||||
fromTableName: '',
|
||||
fromColumnList: [],
|
||||
wTbList: [],
|
||||
dataSource: '',
|
||||
writerForm: {
|
||||
datasourceId: undefined,
|
||||
tableName: '',
|
||||
columns: [],
|
||||
checkAll: false,
|
||||
isIndeterminate: true,
|
||||
ifCreateTable: false,
|
||||
defaultFS: '',
|
||||
fileType: '',
|
||||
path: '',
|
||||
fileName: '',
|
||||
writeMode: '',
|
||||
fieldDelimiter: ''
|
||||
},
|
||||
rules: {
|
||||
path: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
defaultFS: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
fileName: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
fileType: [{ required: true, message: 'this is required', trigger: 'change' }],
|
||||
writeMode: [{ required: true, message: 'this is required', trigger: 'change' }],
|
||||
fieldDelimiter: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
datasourceId: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
fromTableName: [{ required: true, message: 'this is required', trigger: 'blur' }]
|
||||
},
|
||||
readerForm: this.getReaderData(),
|
||||
fileTypes: [
|
||||
{ value: 'text', label: 'text' },
|
||||
{ value: 'orc', label: 'orc' }
|
||||
],
|
||||
writeModes: [
|
||||
{ value: 'append', label: 'append 写入前不做任何处理' },
|
||||
{ value: 'nonConflict', label: 'nonConflict 目录下有fileName前缀的文件,直接报错' }
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'writerForm.datasourceId': function(oldVal, newVal) {
|
||||
this.getTables('hiveWriter')
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getJdbcDs()
|
||||
},
|
||||
methods: {
|
||||
// 获取可用数据源
|
||||
getJdbcDs(type) {
|
||||
this.loading = true
|
||||
jdbcDsList(this.jdbcDsQuery).then(response => {
|
||||
const { records } = response
|
||||
this.wDsList = records
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 获取表名
|
||||
getTables(type) {
|
||||
if (type === 'hiveWriter') {
|
||||
const obj = {
|
||||
datasourceId: this.writerForm.datasourceId
|
||||
}
|
||||
// 组装
|
||||
dsQueryApi.getTables(obj).then(response => {
|
||||
this.wTbList = response
|
||||
})
|
||||
}
|
||||
},
|
||||
wDsChange(e) {
|
||||
// 清空
|
||||
this.writerForm.tableName = ''
|
||||
this.writerForm.datasourceId = e
|
||||
this.wDsList.find((item) => {
|
||||
if (item.id === e) {
|
||||
this.dataSource = item.datasource
|
||||
}
|
||||
})
|
||||
Bus.dataSourceId = e
|
||||
this.$emit('selectDataSource', this.dataSource)
|
||||
// 获取可用表
|
||||
this.getTables()
|
||||
},
|
||||
// 获取表字段
|
||||
getColumns() {
|
||||
const obj = {
|
||||
datasourceId: this.writerForm.datasourceId,
|
||||
tableName: this.writerForm.tableName
|
||||
}
|
||||
dsQueryApi.getColumns(obj).then(response => {
|
||||
this.fromColumnList = response
|
||||
this.writerForm.columns = response
|
||||
this.writerForm.checkAll = true
|
||||
this.writerForm.isIndeterminate = false
|
||||
})
|
||||
},
|
||||
// 表切换
|
||||
wTbChange(t) {
|
||||
this.writerForm.tableName = t
|
||||
this.fromColumnList = []
|
||||
this.writerForm.columns = []
|
||||
this.getColumns('writer')
|
||||
},
|
||||
wHandleCheckAllChange(val) {
|
||||
this.writerForm.columns = val ? this.fromColumnList : []
|
||||
this.writerForm.isIndeterminate = false
|
||||
},
|
||||
wHandleCheckedChange(value) {
|
||||
const checkedCount = value.length
|
||||
this.writerForm.checkAll = checkedCount === this.fromColumnList.length
|
||||
this.writerForm.isIndeterminate = checkedCount > 0 && checkedCount < this.fromColumnList.length
|
||||
},
|
||||
createTableCheckedChange(val) {
|
||||
this.writerForm.tableName = val ? this.readerForm.tableName : ''
|
||||
this.fromColumnList = this.readerForm.columns
|
||||
this.writerForm.columns = this.readerForm.columns
|
||||
this.writerForm.checkAll = true
|
||||
this.writerForm.isIndeterminate = false
|
||||
},
|
||||
getData() {
|
||||
if (Bus.dataSourceId) {
|
||||
this.writerForm.datasourceId = Bus.dataSourceId
|
||||
}
|
||||
return this.writerForm
|
||||
},
|
||||
getReaderData() {
|
||||
return this.$parent.getReaderData()
|
||||
},
|
||||
getTableName() {
|
||||
return this.fromTableName
|
||||
},
|
||||
createTable() {
|
||||
const tableName = this.fromTableName
|
||||
const datasourceId = this.writerForm.datasourceId
|
||||
const columns = this.fromColumnList
|
||||
const jsonString = {}
|
||||
jsonString['datasourceId'] = datasourceId
|
||||
jsonString['tableName'] = tableName
|
||||
jsonString['columns'] = columns
|
||||
console.info(jsonString)
|
||||
dsQueryApi.createTable(jsonString).then(response => {
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Create Table Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
}).catch(() => console.log('promise catch err'))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
192
src/views/dts/json-build/writer/MongoDBWriter.vue
Normal file
192
src/views/dts/json-build/writer/MongoDBWriter.vue
Normal file
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form label-position="left" label-width="105px" :model="writerForm" :rules="rules">
|
||||
<el-form-item label="数据源" prop="datasourceId">
|
||||
<el-select
|
||||
v-model="writerForm.datasourceId"
|
||||
filterable
|
||||
@change="wDsChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in wDsList"
|
||||
:key="item.id"
|
||||
:label="item.datasourceName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="表" prop="fromTableName">
|
||||
<el-select
|
||||
v-model="fromTableName"
|
||||
:disabled="writerForm.ifCreateTable"
|
||||
filterable
|
||||
@change="wTbChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in wTbList"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="更新信息">
|
||||
<el-select v-model="writerForm.upsertInfo.isUpsert" placeholder="是否更新">
|
||||
<el-option v-for="item in upsertType" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input v-model="writerForm.upsertInfo.upsertKey" placeholder="更新的业务主键" style="width: 42%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="字段">
|
||||
<el-checkbox v-model="writerForm.checkAll" :indeterminate="writerForm.isIndeterminate" @change="wHandleCheckAllChange">全选</el-checkbox>
|
||||
<div style="margin: 15px 0;" />
|
||||
<el-checkbox-group v-model="writerForm.columns" @change="wHandleCheckedChange">
|
||||
<el-checkbox v-for="c in fromColumnList" :key="c" :label="c">{{ c }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as dsQueryApi from '@/api/dts/metadata-query'
|
||||
import { list as jdbcDsList } from '@/api/dts/datax-jdbcDatasource'
|
||||
import Bus from '../busWriter'
|
||||
export default {
|
||||
name: 'MongoDBWriter',
|
||||
data() {
|
||||
return {
|
||||
jdbcDsQuery: {
|
||||
current: 1,
|
||||
size: 200
|
||||
},
|
||||
wDsList: [],
|
||||
fromTableName: '',
|
||||
fromColumnList: [],
|
||||
wTbList: [],
|
||||
dataSource: '',
|
||||
writerForm: {
|
||||
datasourceId: undefined,
|
||||
columns: [],
|
||||
tableName: '',
|
||||
checkAll: false,
|
||||
isIndeterminate: true,
|
||||
ifCreateTable: false,
|
||||
upsertInfo: {
|
||||
isUpsert: '',
|
||||
upsertKey: ''
|
||||
}
|
||||
},
|
||||
upsertType: [
|
||||
{ value: true, label: '针对相同的upsertKey做更新' },
|
||||
{ value: false, label: '不做更新' }
|
||||
],
|
||||
rules: {
|
||||
mode: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
datasourceId: [{ required: true, message: 'this is required', trigger: 'blur' }],
|
||||
fromTableName: [{ required: true, message: 'this is required', trigger: 'blur' }]
|
||||
},
|
||||
readerForm: this.getReaderData()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'writerForm.datasourceId': function(oldVal, newVal) {
|
||||
this.getTables('mongodbWriter')
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getJdbcDs()
|
||||
},
|
||||
methods: {
|
||||
// 获取可用数据源
|
||||
getJdbcDs(type) {
|
||||
this.loading = true
|
||||
jdbcDsList(this.jdbcDsQuery).then(response => {
|
||||
const { records } = response
|
||||
this.wDsList = records
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 获取表名
|
||||
getTables(type) {
|
||||
if (type === 'mongodbWriter') {
|
||||
const obj = {
|
||||
datasourceId: this.writerForm.datasourceId
|
||||
}
|
||||
// 组装
|
||||
dsQueryApi.getTables(obj).then(response => {
|
||||
this.wTbList = response
|
||||
})
|
||||
}
|
||||
},
|
||||
wDsChange(e) {
|
||||
// 清空
|
||||
this.writerForm.tableName = ''
|
||||
this.writerForm.datasourceId = e
|
||||
this.wDsList.find((item) => {
|
||||
if (item.id === e) {
|
||||
this.dataSource = item.datasource
|
||||
}
|
||||
})
|
||||
Bus.dataSourceId = e
|
||||
this.$emit('selectDataSource', this.dataSource)
|
||||
// 获取可用表
|
||||
this.getTables()
|
||||
},
|
||||
// 获取表字段
|
||||
getColumns() {
|
||||
const obj = {
|
||||
datasourceId: this.writerForm.datasourceId,
|
||||
tableName: this.writerForm.tableName
|
||||
}
|
||||
dsQueryApi.getColumns(obj).then(response => {
|
||||
this.fromColumnList = response
|
||||
this.writerForm.columns = response
|
||||
this.writerForm.checkAll = true
|
||||
this.writerForm.isIndeterminate = false
|
||||
})
|
||||
},
|
||||
// 表切换
|
||||
wTbChange(t) {
|
||||
this.writerForm.tableName = t
|
||||
this.fromColumnList = []
|
||||
this.writerForm.columns = []
|
||||
this.getColumns('writer')
|
||||
},
|
||||
wHandleCheckAllChange(val) {
|
||||
this.writerForm.columns = val ? this.fromColumnList : []
|
||||
this.writerForm.isIndeterminate = false
|
||||
},
|
||||
wHandleCheckedChange(value) {
|
||||
const checkedCount = value.length
|
||||
this.writerForm.checkAll = checkedCount === this.fromColumnList.length
|
||||
this.writerForm.isIndeterminate = checkedCount > 0 && checkedCount < this.fromColumnList.length
|
||||
},
|
||||
createTableCheckedChange(val) {
|
||||
this.writerForm.tableName = val ? this.readerForm.tableName : ''
|
||||
this.fromColumnList = this.readerForm.columns
|
||||
this.writerForm.columns = this.readerForm.columns
|
||||
this.writerForm.checkAll = true
|
||||
this.writerForm.isIndeterminate = false
|
||||
},
|
||||
getData() {
|
||||
if (Bus.dataSourceId) {
|
||||
this.writerForm.datasourceId = Bus.dataSourceId
|
||||
}
|
||||
return this.writerForm
|
||||
},
|
||||
getReaderData() {
|
||||
return this.$parent.getReaderData()
|
||||
},
|
||||
getTableName() {
|
||||
return this.fromTableName
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
232
src/views/dts/json-build/writer/RDBMSWriter.vue
Normal file
232
src/views/dts/json-build/writer/RDBMSWriter.vue
Normal file
@@ -0,0 +1,232 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form label-position="right" label-width="150px" :model="writerForm" :rules="rules">
|
||||
<el-form-item label="数据库源:" prop="datasourceId">
|
||||
<el-select
|
||||
v-model="writerForm.datasourceId"
|
||||
filterable
|
||||
style="width: 300px;"
|
||||
@change="wDsChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in wDsList"
|
||||
:key="item.id"
|
||||
:label="item.datasourceName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="dataSource==='postgresql' || dataSource==='oracle' ||dataSource==='sqlserver' || dataSource==='hana'" label="Schema:" prop="tableSchema">
|
||||
<el-select v-model="writerForm.tableSchema" filterable style="width: 300px" @change="schemaChange">
|
||||
<el-option
|
||||
v-for="item in schemaList"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据库表名:" prop="tableName">
|
||||
<el-select
|
||||
v-model="fromTableName"
|
||||
allow-create
|
||||
default-first-option
|
||||
filterable
|
||||
:disabled="writerForm.ifCreateTable"
|
||||
style="width: 300px"
|
||||
@change="wTbChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in wTbList"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
<el-input v-show="writerForm.ifCreateTable" v-model="writerForm.tableName" style="width: 200px;" :placeholder="readerForm.tableName" />
|
||||
<!--<el-input v-model="createTableName" style="width: 195px" />
|
||||
<el-button type="primary" @click="createTable">新增</el-button>-->
|
||||
</el-form-item>
|
||||
<div style="margin: 5px 0;" />
|
||||
<el-form-item label="字段:">
|
||||
<el-checkbox v-model="writerForm.checkAll" :indeterminate="writerForm.isIndeterminate" @change="wHandleCheckAllChange">全选</el-checkbox>
|
||||
<div style="margin: 15px 0;" />
|
||||
<el-checkbox-group v-model="writerForm.columns" @change="wHandleCheckedChange">
|
||||
<el-checkbox v-for="c in fromColumnList" :key="c" :label="c">{{ c }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="前置sql语句:">
|
||||
<el-input v-model="writerForm.preSql" placeholder="前置sql在insert之前执行" type="textarea" style="width: 42%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="postSql">
|
||||
<el-input v-model="writerForm.postSql" placeholder="多个用;分隔" type="textarea" style="width: 42%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as dsQueryApi from '@/api/dts/metadata-query'
|
||||
import { list as jdbcDsList } from '@/api/dts/datax-jdbcDatasource'
|
||||
import Bus from '../busWriter'
|
||||
export default {
|
||||
name: 'RDBMSWriter',
|
||||
data() {
|
||||
return {
|
||||
jdbcDsQuery: {
|
||||
current: 1,
|
||||
size: 200,
|
||||
ascs: 'datasource_name'
|
||||
},
|
||||
wDsList: [],
|
||||
schemaList: [],
|
||||
fromTableName: '',
|
||||
fromColumnList: [],
|
||||
wTbList: [],
|
||||
dataSource: '',
|
||||
createTableName: '',
|
||||
writerForm: {
|
||||
datasourceId: undefined,
|
||||
tableName: '',
|
||||
columns: [],
|
||||
checkAll: false,
|
||||
isIndeterminate: true,
|
||||
preSql: '',
|
||||
postSql: '',
|
||||
ifCreateTable: false,
|
||||
tableSchema: ''
|
||||
},
|
||||
readerForm: this.getReaderData(),
|
||||
rules: {
|
||||
datasourceId: [{ required: true, message: 'this is required', trigger: 'change' }],
|
||||
tableName: [{ required: true, message: 'this is required', trigger: 'change' }],
|
||||
tableSchema: [{ required: true, message: 'this is required', trigger: 'change' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'writerForm.datasourceId': function(oldVal, newVal) {
|
||||
if (this.dataSource === 'postgresql' || this.dataSource === 'oracle' || this.dataSource === 'sqlserver' || this.dataSource === 'hana') {
|
||||
this.getSchema()
|
||||
} else {
|
||||
this.getTables('rdbmsWriter')
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getJdbcDs()
|
||||
},
|
||||
methods: {
|
||||
// 获取可用数据源
|
||||
getJdbcDs() {
|
||||
this.loading = true
|
||||
jdbcDsList(this.jdbcDsQuery).then(response => {
|
||||
const { records } = response.data
|
||||
this.wDsList = records
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 获取表名
|
||||
getTables(type) {
|
||||
if (type === 'rdbmsWriter') {
|
||||
let obj = {}
|
||||
if (this.dataSource === 'postgresql' || this.dataSource === 'oracle' || this.dataSource === 'sqlserver' || this.dataSource === 'hana') {
|
||||
obj = {
|
||||
datasourceId: this.writerForm.datasourceId,
|
||||
tableSchema: this.writerForm.tableSchema
|
||||
}
|
||||
} else {
|
||||
obj = {
|
||||
datasourceId: this.writerForm.datasourceId
|
||||
}
|
||||
}
|
||||
// 组装
|
||||
dsQueryApi.getTables(obj).then(response => {
|
||||
this.wTbList = response.data
|
||||
})
|
||||
}
|
||||
},
|
||||
getSchema() {
|
||||
const obj = {
|
||||
datasourceId: this.writerForm.datasourceId
|
||||
}
|
||||
dsQueryApi.getTableSchema(obj).then(response => {
|
||||
this.schemaList = response
|
||||
})
|
||||
},
|
||||
// schema 切换
|
||||
schemaChange(e) {
|
||||
this.writerForm.tableSchema = e
|
||||
// 获取可用表
|
||||
this.getTables('rdbmsWriter')
|
||||
},
|
||||
wDsChange(e) {
|
||||
// 清空
|
||||
this.writerForm.tableName = ''
|
||||
this.writerForm.datasourceId = e
|
||||
this.wDsList.find((item) => {
|
||||
if (item.id === e) {
|
||||
this.dataSource = item.datasource
|
||||
}
|
||||
})
|
||||
Bus.dataSourceId = e
|
||||
this.$emit('selectDataSource', this.dataSource)
|
||||
},
|
||||
// 获取表字段
|
||||
getColumns() {
|
||||
const obj = {
|
||||
datasourceId: this.writerForm.datasourceId,
|
||||
tableName: this.writerForm.tableName
|
||||
}
|
||||
dsQueryApi.getColumns(obj).then(response => {
|
||||
this.fromColumnList = response.data
|
||||
this.writerForm.columns = response.data
|
||||
this.writerForm.checkAll = true
|
||||
this.writerForm.isIndeterminate = false
|
||||
})
|
||||
},
|
||||
// 表切换
|
||||
wTbChange(t) {
|
||||
this.writerForm.tableName = t
|
||||
this.fromColumnList = []
|
||||
this.writerForm.columns = []
|
||||
this.getColumns('writer')
|
||||
},
|
||||
wHandleCheckAllChange(val) {
|
||||
this.writerForm.columns = val ? this.fromColumnList : []
|
||||
this.writerForm.isIndeterminate = false
|
||||
},
|
||||
wHandleCheckedChange(value) {
|
||||
const checkedCount = value.length
|
||||
this.writerForm.checkAll = checkedCount === this.fromColumnList.length
|
||||
this.writerForm.isIndeterminate = checkedCount > 0 && checkedCount < this.fromColumnList.length
|
||||
},
|
||||
getData() {
|
||||
if (Bus.dataSourceId) {
|
||||
this.writerForm.datasourceId = Bus.dataSourceId
|
||||
}
|
||||
return this.writerForm
|
||||
},
|
||||
getReaderData() {
|
||||
return this.$parent.getReaderData()
|
||||
},
|
||||
getTableName() {
|
||||
return this.fromTableName
|
||||
},
|
||||
createTable() {
|
||||
const obj = {
|
||||
datasourceId: this.writerForm.datasourceId,
|
||||
tableName: this.createTableName
|
||||
}
|
||||
dsQueryApi.createTable(obj).then(response => {
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Create Table Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
}).catch(() => console.log('promise catch err'))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
174
src/views/dts/registry/index.vue
Normal file
174
src/views/dts/registry/index.vue
Normal file
@@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="filter-container">
|
||||
<el-input v-model="listQuery.registryKey" placeholder="执行器" style="width: 200px;" class="filter-item" />
|
||||
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="fetchData">
|
||||
Search
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-for="item in list" :key="item.id" class="container">
|
||||
<p><span class="fl">执行器:{{ item.registryKey }}</span><span class="fl"> 注册地址:{{ item.registryValue }}</span><span class="fr">更新时间:{{ item.updateTime }}</span></p>
|
||||
<div :class="item.id + ' fl'" style="width: 30%;height: 300px" />
|
||||
<div :class="item.id + ' fl'" style="width: 30%;height: 300px" />
|
||||
<div :class="item.id + ' fl' + ' loadAverage'" style="width: 30%;height: 300px ">
|
||||
<p class="title">平均负载</p>
|
||||
<p class="number">{{ item.loadAverage >= 0 ? item.loadAverage : 0 }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList } from '@/api/dts/datax-registry'
|
||||
import waves from '@/directive/waves' // waves directive
|
||||
export default {
|
||||
name: 'Registry',
|
||||
directives: { waves },
|
||||
filters: {
|
||||
statusFilter(status) {
|
||||
const statusMap = {
|
||||
published: 'success',
|
||||
draft: 'gray',
|
||||
deleted: 'danger'
|
||||
}
|
||||
return statusMap[status]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: null,
|
||||
listLoading: true,
|
||||
total: 0,
|
||||
listQuery: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
registryKey: undefined
|
||||
},
|
||||
dialogPluginVisible: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
fetchData() {
|
||||
this.listLoading = true
|
||||
this.list = []
|
||||
getList(this.listQuery).then(response => {
|
||||
const { data } = response
|
||||
const { total } = data.total
|
||||
this.total = total
|
||||
this.list = data.records
|
||||
this.listLoading = false
|
||||
|
||||
this.$nextTick(function() {
|
||||
if (this.list) {
|
||||
for (let i = 0; i < this.list.length; i++) {
|
||||
this.initEcharts(this.list[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
initEcharts(data) {
|
||||
const myChart1 = this.$echarts.init(document.getElementsByClassName(data.id)[0])
|
||||
// 绘制图表
|
||||
var option1 = {
|
||||
title: {
|
||||
text: 'cpu使用率',
|
||||
subtext: '',
|
||||
x: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
formatter: '{a} <br/>{b} : {c}%'
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
},
|
||||
show: false
|
||||
},
|
||||
series: [{
|
||||
name: 'cpu使用率',
|
||||
type: 'gauge',
|
||||
max: 100,
|
||||
radius: '70%', // 半径
|
||||
startAngle: 215, // 起始位置
|
||||
endAngle: -35, // 终点位置
|
||||
detail: {
|
||||
formatter: '{value}%'
|
||||
},
|
||||
data: [{
|
||||
value: data.cpuUsage,
|
||||
name: ''
|
||||
}]
|
||||
}]
|
||||
}
|
||||
myChart1.setOption(option1)
|
||||
const myChart2 = this.$echarts.init(document.getElementsByClassName(data.id)[1])
|
||||
// 绘制图表
|
||||
var option2 = {
|
||||
title: {
|
||||
text: '内存使用率',
|
||||
subtext: '',
|
||||
x: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
formatter: '{a} <br/>{b} : {c}%'
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
},
|
||||
show: false
|
||||
},
|
||||
series: [{
|
||||
name: '内存使用率',
|
||||
type: 'gauge',
|
||||
max: 100,
|
||||
radius: '70%', // 半径
|
||||
startAngle: 215, // 起始位置
|
||||
endAngle: -35, // 终点位置
|
||||
detail: {
|
||||
formatter: '{value}%'
|
||||
},
|
||||
data: [{
|
||||
value: data.memoryUsage,
|
||||
name: ''
|
||||
}]
|
||||
}]
|
||||
}
|
||||
myChart2.setOption(option2)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
.container{
|
||||
overflow: hidden;
|
||||
p{
|
||||
font-size: 14px;color: #666;padding: 10px 0;
|
||||
.fl{
|
||||
float: left;
|
||||
}
|
||||
.fr{
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
.loadAverage{
|
||||
p{
|
||||
text-align: center;
|
||||
}
|
||||
.title{
|
||||
font-size: 18px;font-weight: bold;color: #333;padding: 5px 0;margin: 0;
|
||||
}
|
||||
.number{
|
||||
font-size: 50px;color: #3d90d0
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
215
src/views/dts/resource/index.vue
Normal file
215
src/views/dts/resource/index.vue
Normal file
@@ -0,0 +1,215 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="filter-container">
|
||||
<el-input v-model="listQuery.username" placeholder="资源名称" style="width: 200px;" class="filter-item" />
|
||||
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="fetchData">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button
|
||||
class="filter-item"
|
||||
style="margin-left: 10px;"
|
||||
type="primary"
|
||||
icon="el-icon-edit"
|
||||
@click="handleCreate"
|
||||
>
|
||||
添加
|
||||
</el-button>
|
||||
<!-- <el-checkbox v-model="showReviewer" class="filter-item" style="margin-left:15px;" @change="tableKey=tableKey+1">
|
||||
reviewer
|
||||
</el-checkbox> -->
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="list"
|
||||
element-loading-text="Loading"
|
||||
border
|
||||
fit
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column align="center" label="序号" width="95">
|
||||
<template slot-scope="scope">{{ scope.$index+1 }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="资源名称" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.username }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="资源地址" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.username }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="{row}">
|
||||
<el-button type="primary" size="mini" @click="handleUpdate(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button v-if="row.status!=='deleted'" size="mini" type="danger" @click="handleDelete(row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
:page.sync="listQuery.current"
|
||||
:limit.sync="listQuery.size"
|
||||
@pagination="fetchData"
|
||||
/>
|
||||
|
||||
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
|
||||
<el-form
|
||||
ref="dataForm"
|
||||
:rules="rules"
|
||||
:model="temp"
|
||||
label-position="right"
|
||||
label-width="100px"
|
||||
style="width: 400px; margin-left:50px;"
|
||||
>
|
||||
<el-form-item label="资源名称" prop="resourceName">
|
||||
<el-input v-model="temp.resourceName" placeholder="资源名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="资源地址" prop="resourcePath">
|
||||
<el-input v-model="temp.resourcePath" placeholder="资源地址" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as user from '@/api/dts/datax-user'
|
||||
import waves from '@/directive/waves' // waves directive
|
||||
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
|
||||
|
||||
export default {
|
||||
name: 'User',
|
||||
components: { Pagination },
|
||||
directives: { waves },
|
||||
filters: {
|
||||
statusFilter(status) {
|
||||
const statusMap = {
|
||||
published: 'success',
|
||||
draft: 'gray',
|
||||
deleted: 'danger'
|
||||
}
|
||||
return statusMap[status]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: null,
|
||||
listLoading: true,
|
||||
total: 0,
|
||||
listQuery: {
|
||||
current: 1,
|
||||
size: 10,
|
||||
username: undefined
|
||||
},
|
||||
roles: ['ROLE_USER', 'ROLE_ADMIN'],
|
||||
dialogPluginVisible: false,
|
||||
pluginData: [],
|
||||
dialogFormVisible: false,
|
||||
dialogStatus: '',
|
||||
textMap: {
|
||||
update: 'Edit',
|
||||
create: 'Create'
|
||||
},
|
||||
rules: {
|
||||
role: [{ required: true, message: 'role is required', trigger: 'change' }],
|
||||
username: [{ required: true, message: 'username is required', trigger: 'blur' }],
|
||||
password: [{ required: false, message: 'password is required', trigger: 'blur' }]
|
||||
},
|
||||
temp: {
|
||||
id: undefined,
|
||||
role: '',
|
||||
username: '',
|
||||
password: '',
|
||||
permission: ''
|
||||
},
|
||||
resetTemp() {
|
||||
this.temp = this.$options.data().temp
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
fetchData() {
|
||||
this.listLoading = true
|
||||
user.getList(this.listQuery).then(response => {
|
||||
const { content } = response
|
||||
this.total = content.recordsTotal
|
||||
this.list = content.data
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
handleCreate() {
|
||||
this.resetTemp()
|
||||
this.dialogStatus = 'create'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
createData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
user.createUser(this.temp).then(() => {
|
||||
this.fetchData()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Created Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleUpdate(row) {
|
||||
this.temp = Object.assign({}, row) // copy obj
|
||||
this.dialogStatus = 'update'
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs['dataForm'].clearValidate()
|
||||
})
|
||||
},
|
||||
updateData() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
const tempData = Object.assign({}, this.temp)
|
||||
user.updateUser(tempData).then(() => {
|
||||
this.fetchData()
|
||||
this.dialogFormVisible = false
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Update Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleDelete(row) {
|
||||
user.deleteUser(row.id).then(response => {
|
||||
this.fetchData()
|
||||
this.$notify({
|
||||
title: 'Success',
|
||||
message: 'Delete Successfully',
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
89
src/views/features/401.vue
Normal file
89
src/views/features/401.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="errPage-container">
|
||||
<el-button icon="arrow-left" class="pan-back-btn" @click="back">
|
||||
返回
|
||||
</el-button>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<h1 class="text-jumbo text-ginormous">
|
||||
Oops!
|
||||
</h1>
|
||||
<h2>你没有权限去该页面</h2>
|
||||
<h6>如有不满请联系你领导</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li>或者你可以去:</li>
|
||||
<li class="link-type">
|
||||
<router-link to="/dashboard">
|
||||
回首页
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import errGif from '@/assets/401_images/401.gif'
|
||||
|
||||
export default {
|
||||
name: 'Page401',
|
||||
data() {
|
||||
return {
|
||||
errGif: errGif + '?' + +new Date()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
back() {
|
||||
if (this.$route.query.noGoBack) {
|
||||
this.$router.push({ path: '/dashboard' })
|
||||
} else {
|
||||
this.$router.go(-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.errPage-container {
|
||||
width: 800px;
|
||||
max-width: 100%;
|
||||
margin: 100px auto;
|
||||
.pan-back-btn {
|
||||
background: #008489;
|
||||
color: #fff;
|
||||
border: none!important;
|
||||
}
|
||||
.pan-gif {
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
.pan-img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
.text-jumbo {
|
||||
font-size: 60px;
|
||||
font-weight: 700;
|
||||
color: #484848;
|
||||
}
|
||||
.list-unstyled {
|
||||
font-size: 14px;
|
||||
li {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
a {
|
||||
color: #008489;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
225
src/views/features/404.vue
Normal file
225
src/views/features/404.vue
Normal file
@@ -0,0 +1,225 @@
|
||||
<template>
|
||||
<div class="wscn-http404-container">
|
||||
<div class="wscn-http404">
|
||||
<div class="pic-404">
|
||||
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
|
||||
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
|
||||
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
|
||||
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
|
||||
</div>
|
||||
<div class="bullshit">
|
||||
<div class="bullshit__oops">OOPS!</div>
|
||||
<div class="bullshit__headline">{{ message }}</div>
|
||||
<div class="bullshit__info">请检查您输入的网址是否正确,请点击以下按钮返回主页或者发送错误报告</div>
|
||||
<a href="/" class="bullshit__return-home">返回首页</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'Page404',
|
||||
computed: {
|
||||
message() {
|
||||
return '网管说这个页面你不能进......'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.wscn-http404-container{
|
||||
transform: translate(-50%,-50%);
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
}
|
||||
.wscn-http404 {
|
||||
position: relative;
|
||||
width: 1200px;
|
||||
padding: 0 50px;
|
||||
overflow: hidden;
|
||||
.pic-404 {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 600px;
|
||||
overflow: hidden;
|
||||
&__parent {
|
||||
width: 100%;
|
||||
}
|
||||
&__child {
|
||||
position: absolute;
|
||||
&.left {
|
||||
width: 80px;
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
opacity: 0;
|
||||
animation-name: cloudLeft;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
&.mid {
|
||||
width: 46px;
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
opacity: 0;
|
||||
animation-name: cloudMid;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
&.right {
|
||||
width: 62px;
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
opacity: 0;
|
||||
animation-name: cloudRight;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
@keyframes cloudLeft {
|
||||
0% {
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
opacity: 0;
|
||||
}
|
||||
20% {
|
||||
top: 33px;
|
||||
left: 188px;
|
||||
opacity: 1;
|
||||
}
|
||||
80% {
|
||||
top: 81px;
|
||||
left: 92px;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
top: 97px;
|
||||
left: 60px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes cloudMid {
|
||||
0% {
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
opacity: 0;
|
||||
}
|
||||
20% {
|
||||
top: 40px;
|
||||
left: 360px;
|
||||
opacity: 1;
|
||||
}
|
||||
70% {
|
||||
top: 130px;
|
||||
left: 180px;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
top: 160px;
|
||||
left: 120px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes cloudRight {
|
||||
0% {
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
opacity: 0;
|
||||
}
|
||||
20% {
|
||||
top: 120px;
|
||||
left: 460px;
|
||||
opacity: 1;
|
||||
}
|
||||
80% {
|
||||
top: 180px;
|
||||
left: 340px;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
top: 200px;
|
||||
left: 300px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.bullshit {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 300px;
|
||||
padding: 30px 0;
|
||||
overflow: hidden;
|
||||
&__oops {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
line-height: 40px;
|
||||
color: #1482f0;
|
||||
opacity: 0;
|
||||
margin-bottom: 20px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
&__headline {
|
||||
font-size: 20px;
|
||||
line-height: 24px;
|
||||
color: #222;
|
||||
font-weight: bold;
|
||||
opacity: 0;
|
||||
margin-bottom: 10px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
&__info {
|
||||
font-size: 13px;
|
||||
line-height: 21px;
|
||||
color: grey;
|
||||
opacity: 0;
|
||||
margin-bottom: 30px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.2s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
&__return-home {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 110px;
|
||||
height: 36px;
|
||||
background: #1482f0;
|
||||
border-radius: 100px;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
opacity: 0;
|
||||
font-size: 14px;
|
||||
line-height: 36px;
|
||||
cursor: pointer;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.3s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
@keyframes slideUp {
|
||||
0% {
|
||||
transform: translateY(60px);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
12
src/views/features/redirect.vue
Normal file
12
src/views/features/redirect.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<script>
|
||||
export default {
|
||||
created() {
|
||||
const { params, query } = this.$route
|
||||
const { path } = params
|
||||
this.$router.replace({ path: '/' + path, query })
|
||||
},
|
||||
render: function(h) {
|
||||
return h() // avoid warning message
|
||||
}
|
||||
}
|
||||
</script>
|
||||
325
src/views/generator/config.vue
Normal file
325
src/views/generator/config.vue
Normal file
@@ -0,0 +1,325 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="15">
|
||||
<el-col style="margin-bottom: 10px">
|
||||
<el-card class="box-card" shadow="never">
|
||||
<div slot="header" class="clearfix">
|
||||
<span class="role-span">字段配置:{{ tableName }}</span>
|
||||
<el-button
|
||||
:loading="genLoading"
|
||||
icon="el-icon-s-promotion"
|
||||
size="mini"
|
||||
style="float: right; padding: 6px 9px;"
|
||||
type="success"
|
||||
@click="toGen"
|
||||
>保存&生成</el-button>
|
||||
<el-button
|
||||
:loading="columnLoading"
|
||||
icon="el-icon-check"
|
||||
size="mini"
|
||||
style="float: right; padding: 6px 9px;margin-right: 9px"
|
||||
type="primary"
|
||||
@click="saveColumnConfig"
|
||||
>保存</el-button>
|
||||
<el-tooltip class="item" effect="dark" content="数据库中表字段变动时使用该功能" placement="top-start">
|
||||
<el-button
|
||||
:loading="syncLoading"
|
||||
icon="el-icon-refresh"
|
||||
size="mini"
|
||||
style="float: right; padding: 6px 9px;"
|
||||
type="info"
|
||||
@click="sync"
|
||||
>同步</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-form size="small" label-width="90px">
|
||||
<el-table v-loading="loading" :data="data" :max-height="tableHeight" size="small" style="width: 100%;margin-bottom: 15px">
|
||||
<el-table-column prop="columnName" label="字段名称" />
|
||||
<el-table-column prop="columnType" label="字段类型" />
|
||||
<el-table-column prop="remark" label="字段描述">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="data[scope.$index].remark" size="mini" class="edit-input" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="必填" width="70px">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="data[scope.$index].notNull" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="列表" width="70px">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="data[scope.$index].listShow" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="表单" width="70px">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="data[scope.$index].formShow" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="表单类型">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="data[scope.$index].formType" filterable class="edit-input" clearable size="mini" placeholder="请选择">
|
||||
<el-option
|
||||
label="文本框"
|
||||
value="Input"
|
||||
/>
|
||||
<el-option
|
||||
label="文本域"
|
||||
value="Textarea"
|
||||
/>
|
||||
<el-option
|
||||
label="单选框"
|
||||
value="Radio"
|
||||
/>
|
||||
<el-option
|
||||
label="下拉框"
|
||||
value="Select"
|
||||
/>
|
||||
<el-option
|
||||
label="日期框"
|
||||
value="Date"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="查询方式">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="data[scope.$index].queryType" filterable class="edit-input" clearable size="mini" placeholder="请选择">
|
||||
<el-option
|
||||
label="="
|
||||
value="="
|
||||
/>
|
||||
<el-option
|
||||
label="!="
|
||||
value="!="
|
||||
/>
|
||||
<el-option
|
||||
label=">="
|
||||
value=">="
|
||||
/>
|
||||
<el-option
|
||||
label="<="
|
||||
value="<="
|
||||
/>
|
||||
<el-option
|
||||
label="Like"
|
||||
value="Like"
|
||||
/>
|
||||
<el-option
|
||||
label="NotNull"
|
||||
value="NotNull"
|
||||
/>
|
||||
<el-option
|
||||
label="BetWeen"
|
||||
value="BetWeen"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="日期注解">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="data[scope.$index].dateAnnotation" filterable class="edit-input" clearable size="mini" placeholder="请选择">
|
||||
<el-option
|
||||
label="自动创建时间"
|
||||
value="CreationTimestamp"
|
||||
/>
|
||||
<el-option
|
||||
label="自动更新时间"
|
||||
value="UpdateTimestamp"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="关联字典">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="data[scope.$index].dictName" filterable class="edit-input" clearable size="mini" placeholder="请选择">
|
||||
<el-option v-for="item in dicts" :key="item.id" :label="item.remark === '' ? item.name : item.remark" :value="item.name" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<el-card class="box-card" shadow="never">
|
||||
<div slot="header" class="clearfix">
|
||||
<span class="role-span">生成配置</span>
|
||||
<el-button
|
||||
:loading="configLoading"
|
||||
icon="el-icon-check"
|
||||
size="mini"
|
||||
style="float: right; padding: 6px 9px"
|
||||
type="primary"
|
||||
@click="doSubmit"
|
||||
>保存</el-button>
|
||||
</div>
|
||||
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="78px">
|
||||
<el-form-item label="作者名称" prop="author">
|
||||
<el-input v-model="form.author" style="width: 40%" />
|
||||
<span style="color: #C0C0C0;margin-left: 10px;">类上面的作者名称</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="模块名称" prop="moduleName">
|
||||
<el-input v-model="form.moduleName" style="width: 40%" />
|
||||
<span style="color: #C0C0C0;margin-left: 10px;">模块的名称,请选择项目中已存在的模块</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="至于包下" prop="pack">
|
||||
<el-input v-model="form.pack" style="width: 40%" />
|
||||
<span style="color: #C0C0C0;margin-left: 10px;">项目包的名称,生成的代码放到哪个包里面</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="接口名称" prop="apiAlias">
|
||||
<el-input v-model="form.apiAlias" style="width: 40%" />
|
||||
<span style="color: #C0C0C0;margin-left: 10px;">接口的名称,用于控制器与接口文档中</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="前端路径" prop="path">
|
||||
<el-input v-model="form.path" style="width: 40%" />
|
||||
<span style="color: #C0C0C0;margin-left: 10px;">输入views文件夹下的目录,不存在即创建</span>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="接口目录">-->
|
||||
<!-- <el-input v-model="form.apiPath" style="width: 40%" />-->
|
||||
<!-- <span style="color: #C0C0C0;margin-left: 10px;">Api存放路径[src/api],为空则自动生成路径</span>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="去表前缀" prop="prefix">
|
||||
<el-input v-model="form.prefix" placeholder="默认不去除表前缀" style="width: 40%" />
|
||||
<span style="color: #C0C0C0;margin-left: 10px;">默认不去除表前缀,可自定义</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否覆盖" prop="cover">
|
||||
<el-radio-group v-model="form.cover" size="mini" style="width: 40%">
|
||||
<el-radio-button label="true">是</el-radio-button>
|
||||
<el-radio-button label="false">否</el-radio-button>
|
||||
</el-radio-group>
|
||||
<span style="color: #C0C0C0;margin-left: 10px;">谨防误操作,请慎重选择</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crud from '@/mixins/crud'
|
||||
import { update, get } from '@/api/generator/genConfig'
|
||||
import { save, sync, generator } from '@/api/generator/generator'
|
||||
import { getDicts } from '@/api/system/dict'
|
||||
export default {
|
||||
name: 'GeneratorConfig',
|
||||
components: {},
|
||||
mixins: [crud],
|
||||
data() {
|
||||
return {
|
||||
activeName: 'first', tableName: '', tableHeight: 550, columnLoading: false, configLoading: false, dicts: [], syncLoading: false, genLoading: false,
|
||||
form: { id: null, tableName: '', author: '', pack: '', path: '', moduleName: '', cover: 'false', apiPath: '', prefix: '', apiAlias: null },
|
||||
rules: {
|
||||
author: [
|
||||
{ required: true, message: '作者不能为空', trigger: 'blur' }
|
||||
],
|
||||
pack: [
|
||||
{ required: true, message: '包路径不能为空', trigger: 'blur' }
|
||||
],
|
||||
moduleName: [
|
||||
{ required: true, message: '包路径不能为空', trigger: 'blur' }
|
||||
],
|
||||
path: [
|
||||
{ required: true, message: '前端路径不能为空', trigger: 'blur' }
|
||||
],
|
||||
apiAlias: [
|
||||
{ required: true, message: '接口名称不能为空', trigger: 'blur' }
|
||||
],
|
||||
cover: [
|
||||
{ required: true, message: '不能为空', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.tableHeight = document.documentElement.clientHeight - 385
|
||||
this.tableName = this.$route.params.tableName
|
||||
this.$nextTick(() => {
|
||||
this.init()
|
||||
get(this.tableName).then(data => {
|
||||
this.form = data
|
||||
this.form.cover = this.form.cover.toString()
|
||||
})
|
||||
getDicts().then(data => {
|
||||
this.dicts = data
|
||||
})
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
beforeInit() {
|
||||
this.url = '/system/api/generator/columns'
|
||||
const tableName = this.tableName
|
||||
this.params = { tableName }
|
||||
return true
|
||||
},
|
||||
saveColumnConfig() {
|
||||
this.columnLoading = true
|
||||
save(this.data).then(res => {
|
||||
this.notify('保存成功', 'success')
|
||||
this.columnLoading = false
|
||||
}).catch(err => {
|
||||
this.columnLoading = false
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
},
|
||||
doSubmit() {
|
||||
this.$refs['form'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.configLoading = true
|
||||
update(this.form).then(res => {
|
||||
this.notify('保存成功', 'success')
|
||||
this.form = res
|
||||
this.form.cover = this.form.cover.toString()
|
||||
this.configLoading = false
|
||||
}).catch(err => {
|
||||
this.configLoading = false
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
sync() {
|
||||
this.syncLoading = true
|
||||
sync([this.tableName]).then(() => {
|
||||
this.init()
|
||||
this.notify('同步成功', 'success')
|
||||
this.syncLoading = false
|
||||
}).then(() => {
|
||||
this.syncLoading = false
|
||||
})
|
||||
},
|
||||
toGen() {
|
||||
this.genLoading = true
|
||||
save(this.data).then(res => {
|
||||
this.notify('保存成功', 'success')
|
||||
// 生成代码
|
||||
generator(this.tableName, 0).then(data => {
|
||||
this.genLoading = false
|
||||
this.notify('生成成功', 'success')
|
||||
}).catch(err => {
|
||||
this.genLoading = false
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}).catch(err => {
|
||||
this.genLoading = false
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
.edit-input {
|
||||
.el-input__inner {
|
||||
border: 1px solid #e5e6e7;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
::v-deep .input-with-select .el-input-group__prepend {
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
114
src/views/generator/index.vue
Normal file
114
src/views/generator/index.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<el-input v-model="query.name" clearable size="small" placeholder="请输入表名" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation>
|
||||
<el-tooltip slot="right" class="item" effect="dark" content="数据库中表字段变动时使用该功能" placement="top-start">
|
||||
<el-button
|
||||
class="filter-item"
|
||||
size="mini"
|
||||
type="success"
|
||||
icon="el-icon-refresh"
|
||||
:loading="syncLoading"
|
||||
:disabled="crud.selections.length === 0"
|
||||
@click="sync"
|
||||
>同步</el-button>
|
||||
</el-tooltip>
|
||||
</crudOperation>
|
||||
</div>
|
||||
<!--表格渲染-->
|
||||
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="tableName" label="表名" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="engine" label="数据库引擎" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="coding" label="字符编码集" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="remark" label="备注" />
|
||||
<el-table-column prop="createTime" label="创建日期" />
|
||||
<el-table-column label="操作" width="160px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" style="margin-right: 2px" type="text">
|
||||
<router-link :to="'/sys-tools/generator/preview/' + scope.row.tableName">
|
||||
预览
|
||||
</router-link>
|
||||
</el-button>
|
||||
<el-button size="mini" style="margin-left: -1px;margin-right: 2px" type="text" @click="toDownload(scope.row.tableName)">下载</el-button>
|
||||
<el-button size="mini" style="margin-left: -1px;margin-right: 2px" type="text">
|
||||
<router-link :to="'/sys-tools/generator/config/' + scope.row.tableName">
|
||||
配置
|
||||
</router-link>
|
||||
</el-button>
|
||||
<el-button type="text" style="margin-left: -1px" size="mini" @click="toGen(scope.row.tableName)">生成</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<pagination />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { generator, sync } from '@/api/generator/generator'
|
||||
import { downloadFile } from '@/utils/index'
|
||||
import CRUD, { presenter, header } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import pagination from '@crud/Pagination'
|
||||
|
||||
export default {
|
||||
name: 'GeneratorIndex',
|
||||
components: { pagination, crudOperation, rrOperation },
|
||||
cruds() {
|
||||
return CRUD({ url: '/system/api/generator/tables' })
|
||||
},
|
||||
mixins: [presenter(), header()],
|
||||
data() {
|
||||
return {
|
||||
syncLoading: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.crud.optShow = { add: false, edit: false, del: false, download: false }
|
||||
},
|
||||
methods: {
|
||||
toGen(tableName) {
|
||||
// 生成代码
|
||||
generator(tableName, 0).then(data => {
|
||||
this.$notify({
|
||||
title: '生成成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
})
|
||||
},
|
||||
toDownload(tableName) {
|
||||
// 打包下载
|
||||
generator(tableName, 2).then(data => {
|
||||
downloadFile(data, tableName, 'zip')
|
||||
})
|
||||
},
|
||||
sync() {
|
||||
const tables = []
|
||||
this.crud.selections.forEach(val => {
|
||||
tables.push(val.tableName)
|
||||
})
|
||||
this.syncLoading = true
|
||||
sync(tables).then(() => {
|
||||
this.crud.refresh()
|
||||
this.crud.notify('同步成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
this.syncLoading = false
|
||||
}).then(() => {
|
||||
this.syncLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
30
src/views/generator/preview.vue
Normal file
30
src/views/generator/preview.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<el-tabs v-model="activeName" type="card">
|
||||
<el-tab-pane v-for="item in data" :key="item.name" :lazy="true" :label="item.name" :name="item.name">
|
||||
<Java :value="item.content" :height="height" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Java from '@/components/JavaEdit/index'
|
||||
import { generator } from '@/api/generator/generator'
|
||||
export default {
|
||||
name: 'Preview',
|
||||
components: { Java },
|
||||
data() {
|
||||
return {
|
||||
data: null, height: '', activeName: 'Entity'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.height = document.documentElement.clientHeight - 180 + 'px'
|
||||
const tableName = this.$route.params.tableName
|
||||
generator(tableName, 1).then(data => {
|
||||
this.data = data
|
||||
}).catch(() => {
|
||||
this.$router.go(-1)
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
254
src/views/govern/dept/index.vue
Normal file
254
src/views/govern/dept/index.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.name" clearable size="small" placeholder="输入部门名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
|
||||
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
|
||||
</el-select>
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表单组件-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
|
||||
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input v-model="form.name" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门排序" prop="deptSort">
|
||||
<el-input-number
|
||||
v-model.number="form.deptSort"
|
||||
:min="0"
|
||||
:max="999"
|
||||
controls-position="right"
|
||||
style="width: 370px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="顶级部门">
|
||||
<el-radio-group v-model="form.isTop" style="width: 140px">
|
||||
<el-radio label="1">是</el-radio>
|
||||
<el-radio label="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="enabled">
|
||||
<el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.isTop === '0'" style="margin-bottom: 0;" label="上级部门" prop="pid">
|
||||
<treeselect
|
||||
v-model="form.pid"
|
||||
:load-options="loadDepts"
|
||||
:options="depts"
|
||||
style="width: 370px;"
|
||||
placeholder="选择上级类目"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
lazy
|
||||
:load="getDeptDatas"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
:data="crud.data"
|
||||
row-key="id"
|
||||
@select="crud.selectChange"
|
||||
@select-all="crud.selectAllChange"
|
||||
@selection-change="crud.selectionChangeHandler"
|
||||
>
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column label="名称" prop="name" />
|
||||
<el-table-column label="排序" prop="deptSort" />
|
||||
<el-table-column label="状态" align="center" prop="enabled">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
:disabled="scope.row.id === 1"
|
||||
active-color="#409EFF"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled,)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" />
|
||||
<el-table-column v-if="checkPer(['admin','dept:edit','dept:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
:disabled-dle="scope.row.id === 1"
|
||||
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudDept from '@/api/system/dept'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
const defaultForm = { id: null, name: null, isTop: '1', subCount: 0, pid: null, deptSort: 999, enabled: 'true' }
|
||||
export default {
|
||||
name: 'Dept',
|
||||
components: { Treeselect, crudOperation, rrOperation, udOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '部门', url: '/system/api/dept', crudMethod: { ...crudDept }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
// 设置数据字典
|
||||
dicts: ['dept_status'],
|
||||
data() {
|
||||
return {
|
||||
depts: [],
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
deptSort: [
|
||||
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
|
||||
]
|
||||
},
|
||||
permission: {
|
||||
add: ['admin', 'dept:add'],
|
||||
edit: ['admin', 'dept:edit'],
|
||||
del: ['admin', 'dept:del']
|
||||
},
|
||||
enabledTypeOptions: [
|
||||
{ key: 'true', display_name: '正常' },
|
||||
{ key: 'false', display_name: '禁用' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDeptDatas(tree, treeNode, resolve) {
|
||||
const params = { pid: tree.id }
|
||||
setTimeout(() => {
|
||||
crudDept.getDepts(params).then(res => {
|
||||
resolve(res.content)
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
// 新增与编辑前做的操作
|
||||
[CRUD.HOOK.afterToCU](crud, form) {
|
||||
if (form.pid !== null) {
|
||||
form.isTop = '0'
|
||||
} else if (form.id !== null) {
|
||||
form.isTop = '1'
|
||||
}
|
||||
form.enabled = `${form.enabled}`
|
||||
if (form.id != null) {
|
||||
this.getSupDepts(form.id)
|
||||
} else {
|
||||
this.getDepts()
|
||||
}
|
||||
},
|
||||
getSupDepts(id) {
|
||||
crudDept.getDeptSuperior(id).then(res => {
|
||||
const date = res.content
|
||||
this.buildDepts(date)
|
||||
this.depts = date
|
||||
})
|
||||
},
|
||||
buildDepts(depts) {
|
||||
depts.forEach(data => {
|
||||
if (data.children) {
|
||||
this.buildDepts(data.children)
|
||||
}
|
||||
if (data.hasChildren && !data.children) {
|
||||
data.children = null
|
||||
}
|
||||
})
|
||||
},
|
||||
getDepts() {
|
||||
crudDept.getDepts({ enabled: true }).then(res => {
|
||||
this.depts = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取弹窗内部门数据
|
||||
loadDepts({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
crudDept.getDepts({ enabled: true, pid: parentNode.id }).then(res => {
|
||||
parentNode.children = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 提交前的验证
|
||||
[CRUD.HOOK.afterValidateCU]() {
|
||||
if (this.form.pid !== null && this.form.pid === this.form.id) {
|
||||
this.$message({
|
||||
message: '上级部门不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (this.form.isTop === '1') {
|
||||
this.form.pid = null
|
||||
}
|
||||
return true
|
||||
},
|
||||
// 改变状态
|
||||
changeEnabled(data, val) {
|
||||
this.$confirm('此操作将 "' + this.dict.label.dept_status[val] + '" ' + data.name + '部门, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
crudDept.edit(data).then(res => {
|
||||
this.crud.notify(this.dict.label.dept_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(err => {
|
||||
data.enabled = !data.enabled
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
},
|
||||
checkboxT(row, rowIndex) {
|
||||
return row.id !== 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
115
src/views/govern/dict/dictDetail.vue
Normal file
115
src/views/govern/dict/dictDetail.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="query.dictName === ''">
|
||||
<div class="my-code">点击字典查看详情</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.label" clearable size="small" placeholder="输入字典标签查询" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
|
||||
<rrOperation />
|
||||
</div>
|
||||
</div>
|
||||
<!--表单组件-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title" width="500px">
|
||||
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="字典标签" prop="label">
|
||||
<el-input v-model="form.label" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="字典值" prop="value">
|
||||
<el-input v-model="form.value" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="dictSort">
|
||||
<el-input-number v-model.number="form.dictSort" :min="0" :max="999" controls-position="right" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%;" @selection-change="crud.selectionChangeHandler">
|
||||
<el-table-column label="所属字典">
|
||||
{{ query.dictName }}
|
||||
</el-table-column>
|
||||
<el-table-column prop="label" label="字典标签" />
|
||||
<el-table-column prop="value" label="字典值" />
|
||||
<el-table-column prop="dictSort" label="排序" />
|
||||
<el-table-column v-if="checkPer(['admin','dict:edit','dict:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<pagination />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudDictDetail from '@/api/system/dictDetail'
|
||||
import CRUD, { presenter, header, form } from '@crud/crud'
|
||||
import pagination from '@crud/Pagination'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
|
||||
const defaultForm = { id: null, label: null, value: null, dictSort: 999 }
|
||||
|
||||
export default {
|
||||
components: { pagination, rrOperation, udOperation },
|
||||
cruds() {
|
||||
return [
|
||||
CRUD({ title: '字典详情', url: '/system/api/dictDetail', query: { dictName: '' }, sort: ['dictSort,asc', 'id,desc'],
|
||||
crudMethod: { ...crudDictDetail },
|
||||
optShow: {
|
||||
add: true,
|
||||
edit: true,
|
||||
del: true,
|
||||
reset: false
|
||||
},
|
||||
queryOnPresenterCreated: false
|
||||
})
|
||||
]
|
||||
},
|
||||
mixins: [
|
||||
presenter(),
|
||||
header(),
|
||||
form(function() {
|
||||
return Object.assign({ dict: { id: this.dictId }}, defaultForm)
|
||||
})],
|
||||
data() {
|
||||
return {
|
||||
dictId: null,
|
||||
rules: {
|
||||
label: [
|
||||
{ required: true, message: '请输入字典标签', trigger: 'blur' }
|
||||
],
|
||||
value: [
|
||||
{ required: true, message: '请输入字典值', trigger: 'blur' }
|
||||
],
|
||||
dictSort: [
|
||||
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
|
||||
]
|
||||
},
|
||||
permission: {
|
||||
add: ['admin', 'dict:add'],
|
||||
edit: ['admin', 'dict:edit'],
|
||||
del: ['admin', 'dict:del']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
135
src/views/govern/dict/index.vue
Normal file
135
src/views/govern/dict/index.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--表单组件-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title" width="500px">
|
||||
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="字典名称" prop="name">
|
||||
<el-input v-model="form.name" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="描述">
|
||||
<el-input v-model="form.description" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!-- 字典列表 -->
|
||||
<el-row :gutter="10">
|
||||
<el-col :xs="24" :sm="24" :md="10" :lg="11" :xl="11" style="margin-bottom: 10px">
|
||||
<el-card class="box-card">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.blurry" clearable size="small" placeholder="输入名称或者描述搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表格渲染-->
|
||||
<el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%;" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="name" label="名称" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="description" label="描述" />
|
||||
<el-table-column v-if="checkPer(['admin','dict:edit','dict:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<pagination />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<!-- 字典详情列表 -->
|
||||
<el-col :xs="24" :sm="24" :md="14" :lg="13" :xl="13">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>字典详情</span>
|
||||
<el-button
|
||||
v-if="checkPer(['admin','dict:add']) && this.$refs.dictDetail && this.$refs.dictDetail.query.dictName"
|
||||
class="filter-item"
|
||||
size="mini"
|
||||
style="float: right;padding: 4px 10px"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
@click="$refs.dictDetail && $refs.dictDetail.crud.toAdd()"
|
||||
>新增</el-button>
|
||||
</div>
|
||||
<dictDetail ref="dictDetail" :permission="permission" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import dictDetail from './dictDetail'
|
||||
import crudDict from '@/api/system/dict'
|
||||
import CRUD, { presenter, header, form } from '@crud/crud'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import pagination from '@crud/Pagination'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
|
||||
const defaultForm = { id: null, name: null, description: null, dictDetails: [] }
|
||||
|
||||
export default {
|
||||
name: 'Dict',
|
||||
components: { crudOperation, pagination, rrOperation, udOperation, dictDetail },
|
||||
cruds() {
|
||||
return [
|
||||
CRUD({ title: '字典', url: '/system/api/dict', crudMethod: { ...crudDict }})
|
||||
]
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm)],
|
||||
data() {
|
||||
return {
|
||||
queryTypeOptions: [
|
||||
{ key: 'name', display_name: '字典名称' },
|
||||
{ key: 'description', display_name: '描述' }
|
||||
],
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
permission: {
|
||||
add: ['admin', 'dict:add'],
|
||||
edit: ['admin', 'dict:edit'],
|
||||
del: ['admin', 'dict:del']
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取数据前设置好接口地址
|
||||
[CRUD.HOOK.beforeRefresh]() {
|
||||
if (this.$refs.dictDetail) {
|
||||
this.$refs.dictDetail.query.dictName = ''
|
||||
}
|
||||
return true
|
||||
},
|
||||
// 选中字典后,设置字典详情数据
|
||||
handleCurrentChange(val) {
|
||||
if (val) {
|
||||
this.$refs.dictDetail.query.dictName = val.name
|
||||
this.$refs.dictDetail.dictId = val.id
|
||||
this.$refs.dictDetail.crud.toQuery()
|
||||
}
|
||||
},
|
||||
// 编辑前将字典明细临时清空,避免日志入库数据过长
|
||||
[CRUD.HOOK.beforeToEdit](crud, form) {
|
||||
// 将角色的菜单清空,避免日志入库数据过长
|
||||
form.dictDetails = null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
110
src/views/govern/job/index.vue
Normal file
110
src/views/govern/job/index.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<eHeader :dict="dict" :permission="permission" />
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表格渲染-->
|
||||
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="name" label="名称" />
|
||||
<el-table-column prop="jobSort" label="排序">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.jobSort }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
active-color="#409EFF"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" />
|
||||
<!-- 编辑与删除 -->
|
||||
<el-table-column
|
||||
v-if="checkPer(['admin','job:edit','job:del'])"
|
||||
label="操作"
|
||||
width="130px"
|
||||
align="center"
|
||||
fixed="right"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<pagination />
|
||||
<!--表单渲染-->
|
||||
<eForm :job-status="dict.job_status" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudJob from '@/api/system/job'
|
||||
import eHeader from './module/header'
|
||||
import eForm from './module/form'
|
||||
import CRUD, { presenter } from '@crud/crud'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import pagination from '@crud/Pagination'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
export default {
|
||||
name: 'Job',
|
||||
components: { eHeader, eForm, crudOperation, pagination, udOperation },
|
||||
cruds() {
|
||||
return CRUD({
|
||||
title: '岗位',
|
||||
url: '/system/api/job',
|
||||
sort: ['jobSort,asc', 'id,desc'],
|
||||
crudMethod: { ...crudJob }
|
||||
})
|
||||
},
|
||||
mixins: [presenter()],
|
||||
// 数据字典
|
||||
dicts: ['job_status'],
|
||||
data() {
|
||||
return {
|
||||
permission: {
|
||||
add: ['admin', 'job:add'],
|
||||
edit: ['admin', 'job:edit'],
|
||||
del: ['admin', 'job:del']
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 改变状态
|
||||
changeEnabled(data, val) {
|
||||
this.$confirm('此操作将 "' + this.dict.label.job_status[val] + '" ' + data.name + '岗位, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// eslint-disable-next-line no-undef
|
||||
crudJob.edit(data).then(() => {
|
||||
// eslint-disable-next-line no-undef
|
||||
this.crud.notify(this.dict.label.job_status[val] + '成功', 'success')
|
||||
}).catch(err => {
|
||||
data.enabled = !data.enabled
|
||||
console.log(err.data.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
110
src/views/govern/job/module/form.vue
Normal file
110
src/views/govern/job/module/form.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
:before-close="crud.cancelCU"
|
||||
:visible="crud.status.cu > 0"
|
||||
:title="crud.status.title"
|
||||
width="500px"
|
||||
>
|
||||
<el-form
|
||||
ref="form"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
size="small"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-form-item
|
||||
label="名称"
|
||||
prop="name"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.name"
|
||||
style="width: 370px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="排序"
|
||||
prop="jobSort"
|
||||
>
|
||||
<el-input-number
|
||||
v-model.number="form.jobSort"
|
||||
:min="0"
|
||||
:max="999"
|
||||
controls-position="right"
|
||||
style="width: 370px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="form.pid !== 0"
|
||||
label="状态"
|
||||
prop="enabled"
|
||||
>
|
||||
<el-radio
|
||||
v-for="item in jobStatus"
|
||||
:key="item.id"
|
||||
v-model="form.enabled"
|
||||
:label="item.value === 'true'"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div
|
||||
slot="footer"
|
||||
class="dialog-footer"
|
||||
>
|
||||
<el-button
|
||||
type="text"
|
||||
@click="crud.cancelCU"
|
||||
>
|
||||
取消
|
||||
</el-button>
|
||||
<el-button
|
||||
:loading="crud.status.cu === 2"
|
||||
type="primary"
|
||||
@click="crud.submitCU"
|
||||
>
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { form } from '@crud/crud'
|
||||
|
||||
const defaultForm = {
|
||||
id: null,
|
||||
name: '',
|
||||
jobSort: 999,
|
||||
enabled: true
|
||||
}
|
||||
export default {
|
||||
mixins: [form(defaultForm)],
|
||||
props: {
|
||||
jobStatus: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
jobSort: [
|
||||
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
32
src/views/govern/job/module/header.vue
Normal file
32
src/views/govern/job/module/header.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="crud.props.searchToggle"
|
||||
>
|
||||
<el-input v-model="query.name" clearable size="small" placeholder="输入岗位名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
|
||||
<el-option v-for="item in dict.dict.job_status" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<rrOperation />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { header } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
export default {
|
||||
components: { rrOperation, DateRangePicker },
|
||||
mixins: [header()],
|
||||
props: {
|
||||
dict: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
permission: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
252
src/views/govern/menu/index.vue
Normal file
252
src/views/govern/menu/index.vue
Normal file
@@ -0,0 +1,252 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.blurry" clearable size="small" placeholder="模糊搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表单渲染-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="580px">
|
||||
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="菜单类型" prop="type">
|
||||
<el-radio-group v-model="form.type" size="mini" style="width: 178px">
|
||||
<el-radio-button label="0">目录</el-radio-button>
|
||||
<el-radio-button label="1">菜单</el-radio-button>
|
||||
<el-radio-button label="2">按钮</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.type.toString() !== '2'" label="菜单图标" prop="icon">
|
||||
<el-popover
|
||||
placement="bottom-start"
|
||||
width="450"
|
||||
trigger="click"
|
||||
@show="$refs['iconSelect'].reset()"
|
||||
>
|
||||
<IconSelect ref="iconSelect" @selected="selected" />
|
||||
<el-input slot="reference" v-model="form.icon" style="width: 450px;" placeholder="点击选择图标" readonly>
|
||||
<svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" class="el-input__icon" style="height: 32px;width: 16px;" />
|
||||
<i v-else slot="prefix" class="el-icon-search el-input__icon" />
|
||||
</el-input>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.type.toString() !== '2'" label="外链菜单" prop="iFrame">
|
||||
<el-radio-group v-model="form.iFrame" size="mini">
|
||||
<el-radio-button label="true">是</el-radio-button>
|
||||
<el-radio-button label="false">否</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.type.toString() === '1'" label="菜单缓存" prop="cache">
|
||||
<el-radio-group v-model="form.cache" size="mini">
|
||||
<el-radio-button label="true">是</el-radio-button>
|
||||
<el-radio-button label="false">否</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.type.toString() !== '2'" label="菜单可见" prop="hidden">
|
||||
<el-radio-group v-model="form.hidden" size="mini">
|
||||
<el-radio-button label="false">是</el-radio-button>
|
||||
<el-radio-button label="true">否</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.type.toString() !== '2'" label="菜单标题" prop="title">
|
||||
<el-input v-model="form.title" :style=" form.type.toString() === '0' ? 'width: 450px' : 'width: 178px'" placeholder="菜单标题" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.type.toString() === '2'" label="按钮名称" prop="title">
|
||||
<el-input v-model="form.title" placeholder="按钮名称" style="width: 178px;" />
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.type.toString() !== '0'" label="权限标识" prop="permission">
|
||||
<el-input v-model="form.permission" :disabled="form.iFrame.toString() === 'true'" placeholder="权限标识" style="width: 178px;" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.type.toString() !== '2'" label="路由地址" prop="path">
|
||||
<el-input v-model="form.path" placeholder="路由地址" style="width: 178px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单排序" prop="menuSort">
|
||||
<el-input-number v-model.number="form.menuSort" :min="0" :max="999" controls-position="right" style="width: 178px;" />
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'" label="组件名称" prop="componentName">
|
||||
<el-input v-model="form.componentName" style="width: 178px;" placeholder="匹配组件内Name字段" />
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'" label="组件路径" prop="component">
|
||||
<el-input v-model="form.component" style="width: 178px;" placeholder="组件路径" />
|
||||
</el-form-item>
|
||||
<el-form-item label="上级类目" prop="pid">
|
||||
<treeselect
|
||||
v-model="form.pid"
|
||||
:options="menus"
|
||||
:load-options="loadMenus"
|
||||
style="width: 450px;"
|
||||
placeholder="选择上级类目"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
lazy
|
||||
:load="getMenus"
|
||||
:data="crud.data"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
row-key="id"
|
||||
@select="crud.selectChange"
|
||||
@select-all="crud.selectAllChange"
|
||||
@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 prop="icon" label="图标" align="center" width="60px">
|
||||
<template slot-scope="scope">
|
||||
<svg-icon :icon-class="scope.row.icon ? scope.row.icon : ''" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="menuSort" align="center" label="排序">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.menuSort }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" prop="permission" label="权限标识" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="component" label="组件路径" />
|
||||
<el-table-column prop="iFrame" label="外链" width="75px">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.iFrame">是</span>
|
||||
<span v-else>否</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="cache" label="缓存" width="75px">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.cache">是</span>
|
||||
<span v-else>否</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="hidden" label="可见" width="75px">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.hidden">否</span>
|
||||
<span v-else>是</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" width="135px" />
|
||||
<el-table-column v-if="checkPer(['admin','menu:edit','menu:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudMenu from '@/api/system/menu'
|
||||
import IconSelect from '@/components/IconSelect'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
// crud交由presenter持有
|
||||
const defaultForm = { id: null, title: null, menuSort: 999, path: null, component: null, componentName: null, iFrame: false, roles: [], pid: 0, icon: null, cache: false, hidden: false, type: 0, permission: null }
|
||||
export default {
|
||||
name: 'Menu',
|
||||
components: { Treeselect, IconSelect, crudOperation, rrOperation, udOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '菜单', url: '/system/api/menus', crudMethod: { ...crudMenu }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
data() {
|
||||
return {
|
||||
menus: [],
|
||||
permission: {
|
||||
add: ['admin', 'menu:add'],
|
||||
edit: ['admin', 'menu:edit'],
|
||||
del: ['admin', 'menu:del']
|
||||
},
|
||||
rules: {
|
||||
title: [
|
||||
{ required: true, message: '请输入标题', trigger: 'blur' }
|
||||
],
|
||||
path: [
|
||||
{ required: true, message: '请输入地址', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 新增与编辑前做的操作
|
||||
[CRUD.HOOK.afterToCU](crud, form) {
|
||||
this.menus = []
|
||||
if (form.id != null) {
|
||||
if (form.pid === null) {
|
||||
form.pid = 0
|
||||
}
|
||||
this.getSupDepts(form.id)
|
||||
} else {
|
||||
this.menus.push({ id: 0, label: '顶级类目', children: null })
|
||||
}
|
||||
},
|
||||
getMenus(tree, treeNode, resolve) {
|
||||
const params = { pid: tree.id }
|
||||
setTimeout(() => {
|
||||
crudMenu.getMenus(params).then(res => {
|
||||
resolve(res.content)
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
getSupDepts(id) {
|
||||
crudMenu.getMenuSuperior(id).then(res => {
|
||||
const children = res.map(function(obj) {
|
||||
if (!obj.leaf && !obj.children) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
this.menus = [{ id: 0, label: '顶级类目', children: children }]
|
||||
})
|
||||
},
|
||||
loadMenus({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
crudMenu.getMenusTree(parentNode.id).then(res => {
|
||||
parentNode.children = res.map(function(obj) {
|
||||
if (!obj.leaf) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 选中图标
|
||||
selected(name) {
|
||||
this.form.icon = name
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
254
src/views/govern/metadata/analyse/attr/index.vue
Normal file
254
src/views/govern/metadata/analyse/attr/index.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.name" clearable size="small" placeholder="输入部门名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
|
||||
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
|
||||
</el-select>
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表单组件-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
|
||||
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input v-model="form.name" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门排序" prop="deptSort">
|
||||
<el-input-number
|
||||
v-model.number="form.deptSort"
|
||||
:min="0"
|
||||
:max="999"
|
||||
controls-position="right"
|
||||
style="width: 370px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="顶级部门">
|
||||
<el-radio-group v-model="form.isTop" style="width: 140px">
|
||||
<el-radio label="1">是</el-radio>
|
||||
<el-radio label="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="enabled">
|
||||
<el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.isTop === '0'" style="margin-bottom: 0;" label="上级部门" prop="pid">
|
||||
<treeselect
|
||||
v-model="form.pid"
|
||||
:load-options="loadDepts"
|
||||
:options="depts"
|
||||
style="width: 370px;"
|
||||
placeholder="选择上级类目"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
lazy
|
||||
:load="getDeptDatas"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
:data="crud.data"
|
||||
row-key="id"
|
||||
@select="crud.selectChange"
|
||||
@select-all="crud.selectAllChange"
|
||||
@selection-change="crud.selectionChangeHandler"
|
||||
>
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column label="名称" prop="name" />
|
||||
<el-table-column label="排序" prop="deptSort" />
|
||||
<el-table-column label="状态" align="center" prop="enabled">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
:disabled="scope.row.id === 1"
|
||||
active-color="#409EFF"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled,)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" />
|
||||
<el-table-column v-if="checkPer(['admin','dept:edit','dept:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
:disabled-dle="scope.row.id === 1"
|
||||
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudDept from '@/api/system/dept'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
const defaultForm = { id: null, name: null, isTop: '1', subCount: 0, pid: null, deptSort: 999, enabled: 'true' }
|
||||
export default {
|
||||
name: 'Dept',
|
||||
components: { Treeselect, crudOperation, rrOperation, udOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '部门', url: '/system/api/dept', crudMethod: { ...crudDept }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
// 设置数据字典
|
||||
dicts: ['dept_status'],
|
||||
data() {
|
||||
return {
|
||||
depts: [],
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
deptSort: [
|
||||
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
|
||||
]
|
||||
},
|
||||
permission: {
|
||||
add: ['admin', 'dept:add'],
|
||||
edit: ['admin', 'dept:edit'],
|
||||
del: ['admin', 'dept:del']
|
||||
},
|
||||
enabledTypeOptions: [
|
||||
{ key: 'true', display_name: '正常' },
|
||||
{ key: 'false', display_name: '禁用' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDeptDatas(tree, treeNode, resolve) {
|
||||
const params = { pid: tree.id }
|
||||
setTimeout(() => {
|
||||
crudDept.getDepts(params).then(res => {
|
||||
resolve(res.content)
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
// 新增与编辑前做的操作
|
||||
[CRUD.HOOK.afterToCU](crud, form) {
|
||||
if (form.pid !== null) {
|
||||
form.isTop = '0'
|
||||
} else if (form.id !== null) {
|
||||
form.isTop = '1'
|
||||
}
|
||||
form.enabled = `${form.enabled}`
|
||||
if (form.id != null) {
|
||||
this.getSupDepts(form.id)
|
||||
} else {
|
||||
this.getDepts()
|
||||
}
|
||||
},
|
||||
getSupDepts(id) {
|
||||
crudDept.getDeptSuperior(id).then(res => {
|
||||
const date = res.content
|
||||
this.buildDepts(date)
|
||||
this.depts = date
|
||||
})
|
||||
},
|
||||
buildDepts(depts) {
|
||||
depts.forEach(data => {
|
||||
if (data.children) {
|
||||
this.buildDepts(data.children)
|
||||
}
|
||||
if (data.hasChildren && !data.children) {
|
||||
data.children = null
|
||||
}
|
||||
})
|
||||
},
|
||||
getDepts() {
|
||||
crudDept.getDepts({ enabled: true }).then(res => {
|
||||
this.depts = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取弹窗内部门数据
|
||||
loadDepts({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
crudDept.getDepts({ enabled: true, pid: parentNode.id }).then(res => {
|
||||
parentNode.children = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 提交前的验证
|
||||
[CRUD.HOOK.afterValidateCU]() {
|
||||
if (this.form.pid !== null && this.form.pid === this.form.id) {
|
||||
this.$message({
|
||||
message: '上级部门不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (this.form.isTop === '1') {
|
||||
this.form.pid = null
|
||||
}
|
||||
return true
|
||||
},
|
||||
// 改变状态
|
||||
changeEnabled(data, val) {
|
||||
this.$confirm('此操作将 "' + this.dict.label.dept_status[val] + '" ' + data.name + '部门, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
crudDept.edit(data).then(res => {
|
||||
this.crud.notify(this.dict.label.dept_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(err => {
|
||||
data.enabled = !data.enabled
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
},
|
||||
checkboxT(row, rowIndex) {
|
||||
return row.id !== 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
254
src/views/govern/metadata/analyse/index.vue
Normal file
254
src/views/govern/metadata/analyse/index.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.name" clearable size="small" placeholder="输入部门名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
|
||||
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
|
||||
</el-select>
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表单组件-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
|
||||
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input v-model="form.name" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门排序" prop="deptSort">
|
||||
<el-input-number
|
||||
v-model.number="form.deptSort"
|
||||
:min="0"
|
||||
:max="999"
|
||||
controls-position="right"
|
||||
style="width: 370px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="顶级部门">
|
||||
<el-radio-group v-model="form.isTop" style="width: 140px">
|
||||
<el-radio label="1">是</el-radio>
|
||||
<el-radio label="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="enabled">
|
||||
<el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.isTop === '0'" style="margin-bottom: 0;" label="上级部门" prop="pid">
|
||||
<treeselect
|
||||
v-model="form.pid"
|
||||
:load-options="loadDepts"
|
||||
:options="depts"
|
||||
style="width: 370px;"
|
||||
placeholder="选择上级类目"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
lazy
|
||||
:load="getDeptDatas"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
:data="crud.data"
|
||||
row-key="id"
|
||||
@select="crud.selectChange"
|
||||
@select-all="crud.selectAllChange"
|
||||
@selection-change="crud.selectionChangeHandler"
|
||||
>
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column label="名称" prop="name" />
|
||||
<el-table-column label="排序" prop="deptSort" />
|
||||
<el-table-column label="状态" align="center" prop="enabled">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
:disabled="scope.row.id === 1"
|
||||
active-color="#409EFF"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled,)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" />
|
||||
<el-table-column v-if="checkPer(['admin','dept:edit','dept:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
:disabled-dle="scope.row.id === 1"
|
||||
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudDept from '@/api/system/dept'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
const defaultForm = { id: null, name: null, isTop: '1', subCount: 0, pid: null, deptSort: 999, enabled: 'true' }
|
||||
export default {
|
||||
name: 'Dept',
|
||||
components: { Treeselect, crudOperation, rrOperation, udOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '部门', url: '/system/api/dept', crudMethod: { ...crudDept }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
// 设置数据字典
|
||||
dicts: ['dept_status'],
|
||||
data() {
|
||||
return {
|
||||
depts: [],
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
deptSort: [
|
||||
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
|
||||
]
|
||||
},
|
||||
permission: {
|
||||
add: ['admin', 'dept:add'],
|
||||
edit: ['admin', 'dept:edit'],
|
||||
del: ['admin', 'dept:del']
|
||||
},
|
||||
enabledTypeOptions: [
|
||||
{ key: 'true', display_name: '正常' },
|
||||
{ key: 'false', display_name: '禁用' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDeptDatas(tree, treeNode, resolve) {
|
||||
const params = { pid: tree.id }
|
||||
setTimeout(() => {
|
||||
crudDept.getDepts(params).then(res => {
|
||||
resolve(res.content)
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
// 新增与编辑前做的操作
|
||||
[CRUD.HOOK.afterToCU](crud, form) {
|
||||
if (form.pid !== null) {
|
||||
form.isTop = '0'
|
||||
} else if (form.id !== null) {
|
||||
form.isTop = '1'
|
||||
}
|
||||
form.enabled = `${form.enabled}`
|
||||
if (form.id != null) {
|
||||
this.getSupDepts(form.id)
|
||||
} else {
|
||||
this.getDepts()
|
||||
}
|
||||
},
|
||||
getSupDepts(id) {
|
||||
crudDept.getDeptSuperior(id).then(res => {
|
||||
const date = res.content
|
||||
this.buildDepts(date)
|
||||
this.depts = date
|
||||
})
|
||||
},
|
||||
buildDepts(depts) {
|
||||
depts.forEach(data => {
|
||||
if (data.children) {
|
||||
this.buildDepts(data.children)
|
||||
}
|
||||
if (data.hasChildren && !data.children) {
|
||||
data.children = null
|
||||
}
|
||||
})
|
||||
},
|
||||
getDepts() {
|
||||
crudDept.getDepts({ enabled: true }).then(res => {
|
||||
this.depts = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取弹窗内部门数据
|
||||
loadDepts({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
crudDept.getDepts({ enabled: true, pid: parentNode.id }).then(res => {
|
||||
parentNode.children = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 提交前的验证
|
||||
[CRUD.HOOK.afterValidateCU]() {
|
||||
if (this.form.pid !== null && this.form.pid === this.form.id) {
|
||||
this.$message({
|
||||
message: '上级部门不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (this.form.isTop === '1') {
|
||||
this.form.pid = null
|
||||
}
|
||||
return true
|
||||
},
|
||||
// 改变状态
|
||||
changeEnabled(data, val) {
|
||||
this.$confirm('此操作将 "' + this.dict.label.dept_status[val] + '" ' + data.name + '部门, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
crudDept.edit(data).then(res => {
|
||||
this.crud.notify(this.dict.label.dept_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(err => {
|
||||
data.enabled = !data.enabled
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
},
|
||||
checkboxT(row, rowIndex) {
|
||||
return row.id !== 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
254
src/views/govern/metadata/analyse/relation/index.vue
Normal file
254
src/views/govern/metadata/analyse/relation/index.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.name" clearable size="small" placeholder="输入部门名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
|
||||
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
|
||||
</el-select>
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表单组件-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
|
||||
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input v-model="form.name" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门排序" prop="deptSort">
|
||||
<el-input-number
|
||||
v-model.number="form.deptSort"
|
||||
:min="0"
|
||||
:max="999"
|
||||
controls-position="right"
|
||||
style="width: 370px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="顶级部门">
|
||||
<el-radio-group v-model="form.isTop" style="width: 140px">
|
||||
<el-radio label="1">是</el-radio>
|
||||
<el-radio label="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="enabled">
|
||||
<el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.isTop === '0'" style="margin-bottom: 0;" label="上级部门" prop="pid">
|
||||
<treeselect
|
||||
v-model="form.pid"
|
||||
:load-options="loadDepts"
|
||||
:options="depts"
|
||||
style="width: 370px;"
|
||||
placeholder="选择上级类目"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
lazy
|
||||
:load="getDeptDatas"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
:data="crud.data"
|
||||
row-key="id"
|
||||
@select="crud.selectChange"
|
||||
@select-all="crud.selectAllChange"
|
||||
@selection-change="crud.selectionChangeHandler"
|
||||
>
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column label="名称" prop="name" />
|
||||
<el-table-column label="排序" prop="deptSort" />
|
||||
<el-table-column label="状态" align="center" prop="enabled">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
:disabled="scope.row.id === 1"
|
||||
active-color="#409EFF"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled,)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" />
|
||||
<el-table-column v-if="checkPer(['admin','dept:edit','dept:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
:disabled-dle="scope.row.id === 1"
|
||||
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudDept from '@/api/system/dept'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
const defaultForm = { id: null, name: null, isTop: '1', subCount: 0, pid: null, deptSort: 999, enabled: 'true' }
|
||||
export default {
|
||||
name: 'Dept',
|
||||
components: { Treeselect, crudOperation, rrOperation, udOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '部门', url: '/system/api/dept', crudMethod: { ...crudDept }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
// 设置数据字典
|
||||
dicts: ['dept_status'],
|
||||
data() {
|
||||
return {
|
||||
depts: [],
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
deptSort: [
|
||||
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
|
||||
]
|
||||
},
|
||||
permission: {
|
||||
add: ['admin', 'dept:add'],
|
||||
edit: ['admin', 'dept:edit'],
|
||||
del: ['admin', 'dept:del']
|
||||
},
|
||||
enabledTypeOptions: [
|
||||
{ key: 'true', display_name: '正常' },
|
||||
{ key: 'false', display_name: '禁用' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDeptDatas(tree, treeNode, resolve) {
|
||||
const params = { pid: tree.id }
|
||||
setTimeout(() => {
|
||||
crudDept.getDepts(params).then(res => {
|
||||
resolve(res.content)
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
// 新增与编辑前做的操作
|
||||
[CRUD.HOOK.afterToCU](crud, form) {
|
||||
if (form.pid !== null) {
|
||||
form.isTop = '0'
|
||||
} else if (form.id !== null) {
|
||||
form.isTop = '1'
|
||||
}
|
||||
form.enabled = `${form.enabled}`
|
||||
if (form.id != null) {
|
||||
this.getSupDepts(form.id)
|
||||
} else {
|
||||
this.getDepts()
|
||||
}
|
||||
},
|
||||
getSupDepts(id) {
|
||||
crudDept.getDeptSuperior(id).then(res => {
|
||||
const date = res.content
|
||||
this.buildDepts(date)
|
||||
this.depts = date
|
||||
})
|
||||
},
|
||||
buildDepts(depts) {
|
||||
depts.forEach(data => {
|
||||
if (data.children) {
|
||||
this.buildDepts(data.children)
|
||||
}
|
||||
if (data.hasChildren && !data.children) {
|
||||
data.children = null
|
||||
}
|
||||
})
|
||||
},
|
||||
getDepts() {
|
||||
crudDept.getDepts({ enabled: true }).then(res => {
|
||||
this.depts = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取弹窗内部门数据
|
||||
loadDepts({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
crudDept.getDepts({ enabled: true, pid: parentNode.id }).then(res => {
|
||||
parentNode.children = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 提交前的验证
|
||||
[CRUD.HOOK.afterValidateCU]() {
|
||||
if (this.form.pid !== null && this.form.pid === this.form.id) {
|
||||
this.$message({
|
||||
message: '上级部门不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (this.form.isTop === '1') {
|
||||
this.form.pid = null
|
||||
}
|
||||
return true
|
||||
},
|
||||
// 改变状态
|
||||
changeEnabled(data, val) {
|
||||
this.$confirm('此操作将 "' + this.dict.label.dept_status[val] + '" ' + data.name + '部门, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
crudDept.edit(data).then(res => {
|
||||
this.crud.notify(this.dict.label.dept_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(err => {
|
||||
data.enabled = !data.enabled
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
},
|
||||
checkboxT(row, rowIndex) {
|
||||
return row.id !== 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
221
src/views/govern/metadata/center.vue
Normal file
221
src/views/govern/metadata/center.vue
Normal file
@@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="24" :md="8" :lg="6" :xl="5" style="margin-bottom: 10px">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>个人信息</span>
|
||||
</div>
|
||||
<div>
|
||||
<div style="text-align: center">
|
||||
<div class="el-upload">
|
||||
<img :src="user.avatarName ? baseApi + '/system/avatar/' + user.avatarName : Avatar" title="点击上传头像" class="avatar" @click="toggleShow">
|
||||
<myUpload
|
||||
v-model="show"
|
||||
:headers="headers"
|
||||
:url="updateAvatarApi"
|
||||
@crop-upload-success="cropUploadSuccess"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="user-info">
|
||||
<li><div style="height: 100%"><svg-icon icon-class="login" /> 登录账号<div class="user-right">{{ user.username }}</div></div></li>
|
||||
<li><svg-icon icon-class="user1" /> 用户昵称 <div class="user-right">{{ user.nickName }}</div></li>
|
||||
<li><svg-icon icon-class="dept" /> 所属部门 <div class="user-right"> {{ user.dept.name }}</div></li>
|
||||
<li><svg-icon icon-class="phone" /> 手机号码 <div class="user-right">{{ user.phone }}</div></li>
|
||||
<li><svg-icon icon-class="email" /> 用户邮箱 <div class="user-right">{{ user.email }}</div></li>
|
||||
<li>
|
||||
<svg-icon icon-class="anq" /> 安全设置
|
||||
<div class="user-right">
|
||||
<a @click="$refs.pass.dialog = true">修改密码</a>
|
||||
<a @click="$refs.email.dialog = true">修改邮箱</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="16" :lg="18" :xl="19">
|
||||
<!-- 用户资料 -->
|
||||
<el-card class="box-card">
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick">
|
||||
<el-tab-pane label="用户资料" name="first">
|
||||
<el-form ref="form" :model="form" :rules="rules" style="margin-top: 10px;" size="small" label-width="65px">
|
||||
<el-form-item label="昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" style="width: 35%" />
|
||||
<span style="color: #C0C0C0;margin-left: 10px;">用户昵称不作为登录使用</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input v-model="form.phone" style="width: 35%;" />
|
||||
<span style="color: #C0C0C0;margin-left: 10px;">手机号码不能重复</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-radio-group v-model="form.gender" style="width: 178px">
|
||||
<el-radio label="男">男</el-radio>
|
||||
<el-radio label="女">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="">
|
||||
<el-button :loading="saveLoading" size="mini" type="primary" @click="doSubmit">保存配置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<!-- 操作日志 -->
|
||||
<el-tab-pane label="操作日志" name="second">
|
||||
<el-table v-loading="loading" :data="data" style="width: 100%;">
|
||||
<el-table-column prop="description" label="行为" />
|
||||
<el-table-column prop="requestIp" label="IP" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="address" label="IP来源" />
|
||||
<el-table-column prop="browser" label="浏览器" />
|
||||
<el-table-column prop="time" label="请求耗时" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.time <= 300">{{ scope.row.time }}ms</el-tag>
|
||||
<el-tag v-else-if="scope.row.time <= 1000" type="warning">{{ scope.row.time }}ms</el-tag>
|
||||
<el-tag v-else type="danger">{{ scope.row.time }}ms</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="right"
|
||||
>
|
||||
<template slot="header">
|
||||
<div style="display:inline-block;float: right;cursor: pointer" @click="init">创建日期<i class="el-icon-refresh" style="margin-left: 40px" /></div>
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.createTime }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<el-pagination
|
||||
:total="total"
|
||||
:current-page="page + 1"
|
||||
style="margin-top: 8px;"
|
||||
layout="total, prev, pager, next, sizes"
|
||||
@size-change="sizeChange"
|
||||
@current-change="pageChange"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<updateEmail ref="email" :email="user.email" />
|
||||
<updatePass ref="pass" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import myUpload from 'vue-image-crop-upload'
|
||||
import { mapGetters } from 'vuex'
|
||||
import updatePass from './center/updatePass'
|
||||
import updateEmail from './center/updateEmail'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import store from '@/store'
|
||||
import { isvalidPhone } from '@/utils/validate'
|
||||
import crud from '@/mixins/crud'
|
||||
import { editUser } from '@/api/system/user'
|
||||
import Avatar from '@/assets/images/avatar.png'
|
||||
export default {
|
||||
name: 'Center',
|
||||
components: { updatePass, updateEmail, myUpload },
|
||||
mixins: [crud],
|
||||
data() {
|
||||
// 自定义验证
|
||||
const validPhone = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('请输入电话号码'))
|
||||
} else if (!isvalidPhone(value)) {
|
||||
callback(new Error('请输入正确的11位手机号码'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
show: false,
|
||||
Avatar: Avatar,
|
||||
activeName: 'first',
|
||||
saveLoading: false,
|
||||
headers: {
|
||||
'Authorization': getToken()
|
||||
},
|
||||
form: {},
|
||||
rules: {
|
||||
nickName: [
|
||||
{ required: true, message: '请输入用户昵称', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
phone: [
|
||||
{ required: true, trigger: 'blur', validator: validPhone }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'user',
|
||||
'updateAvatarApi',
|
||||
'baseApi'
|
||||
])
|
||||
},
|
||||
created() {
|
||||
this.form = { id: this.user.id, nickName: this.user.nickName, gender: this.user.gender, phone: this.user.phone }
|
||||
store.dispatch('GetInfo').then(() => {})
|
||||
},
|
||||
methods: {
|
||||
toggleShow() {
|
||||
this.show = !this.show
|
||||
},
|
||||
handleClick(tab, event) {
|
||||
if (tab.name === 'second') {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
beforeInit() {
|
||||
this.url = '/system/api/logs/user'
|
||||
return true
|
||||
},
|
||||
cropUploadSuccess(jsonData, field) {
|
||||
store.dispatch('GetInfo').then(() => {})
|
||||
},
|
||||
doSubmit() {
|
||||
if (this.$refs['form']) {
|
||||
this.$refs['form'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.saveLoading = true
|
||||
editUser(this.form).then(() => {
|
||||
this.editSuccessNotify()
|
||||
store.dispatch('GetInfo').then(() => {})
|
||||
this.saveLoading = false
|
||||
}).catch(() => {
|
||||
this.saveLoading = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
.avatar {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.user-info {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
li{
|
||||
border-bottom: 1px solid #F0F3F4;
|
||||
padding: 11px 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
.user-right {
|
||||
float: right;
|
||||
a{
|
||||
color: #317EF3;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
137
src/views/govern/metadata/center/updateEmail.vue
Normal file
137
src/views/govern/metadata/center/updateEmail.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<div style="display: inline-block;">
|
||||
<el-dialog :visible.sync="dialog" :close-on-click-modal="false" :before-close="cancel" :title="title" append-to-body width="475px" @close="cancel">
|
||||
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="88px">
|
||||
<el-form-item label="新邮箱" prop="email">
|
||||
<el-input v-model="form.email" auto-complete="on" style="width: 200px;" />
|
||||
<el-button :loading="codeLoading" :disabled="isDisabled" size="small" @click="sendCode">{{ buttonName }}</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="验证码" prop="code">
|
||||
<el-input v-model="form.code" style="width: 320px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="当前密码" prop="pass">
|
||||
<el-input v-model="form.pass" type="password" style="width: 320px;" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="cancel">取消</el-button>
|
||||
<el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
import { validEmail } from '@/utils/validate'
|
||||
import { updateEmail } from '@/api/system/user'
|
||||
import { resetEmail } from '@/api/system/code'
|
||||
export default {
|
||||
props: {
|
||||
email: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const validMail = (rule, value, callback) => {
|
||||
if (value === '' || value === null) {
|
||||
callback(new Error('新邮箱不能为空'))
|
||||
} else if (value === this.email) {
|
||||
callback(new Error('新邮箱不能与旧邮箱相同'))
|
||||
} else if (validEmail(value)) {
|
||||
callback()
|
||||
} else {
|
||||
callback(new Error('邮箱格式错误'))
|
||||
}
|
||||
}
|
||||
return {
|
||||
loading: false, dialog: false, title: '修改邮箱', form: { pass: '', email: '', code: '' },
|
||||
user: { email: '', password: '' }, codeLoading: false,
|
||||
buttonName: '获取验证码', isDisabled: false, time: 60,
|
||||
rules: {
|
||||
pass: [
|
||||
{ required: true, message: '当前密码不能为空', trigger: 'blur' }
|
||||
],
|
||||
email: [
|
||||
{ required: true, validator: validMail, trigger: 'blur' }
|
||||
],
|
||||
code: [
|
||||
{ required: true, message: '验证码不能为空', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cancel() {
|
||||
this.resetForm()
|
||||
},
|
||||
sendCode() {
|
||||
if (this.form.email && this.form.email !== this.email) {
|
||||
this.codeLoading = true
|
||||
this.buttonName = '验证码发送中'
|
||||
const _this = this
|
||||
resetEmail(this.form.email).then(res => {
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: '发送成功,验证码有效期5分钟',
|
||||
type: 'success'
|
||||
})
|
||||
this.codeLoading = false
|
||||
this.isDisabled = true
|
||||
this.buttonName = this.time-- + '秒后重新发送'
|
||||
this.timer = window.setInterval(function() {
|
||||
_this.buttonName = _this.time + '秒后重新发送'
|
||||
--_this.time
|
||||
if (_this.time < 0) {
|
||||
_this.buttonName = '重新发送'
|
||||
_this.time = 60
|
||||
_this.isDisabled = false
|
||||
window.clearInterval(_this.timer)
|
||||
}
|
||||
}, 1000)
|
||||
}).catch(err => {
|
||||
this.resetForm()
|
||||
this.codeLoading = false
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}
|
||||
},
|
||||
doSubmit() {
|
||||
this.$refs['form'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.loading = true
|
||||
updateEmail(this.form).then(res => {
|
||||
this.loading = false
|
||||
this.resetForm()
|
||||
this.$notify({
|
||||
title: '邮箱修改成功',
|
||||
type: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
store.dispatch('GetInfo').then(() => {})
|
||||
}).catch(err => {
|
||||
this.loading = false
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.dialog = false
|
||||
this.$refs['form'].resetFields()
|
||||
window.clearInterval(this.timer)
|
||||
this.time = 60
|
||||
this.buttonName = '获取验证码'
|
||||
this.isDisabled = false
|
||||
this.form = { pass: '', email: '', code: '' }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
95
src/views/govern/metadata/center/updatePass.vue
Normal file
95
src/views/govern/metadata/center/updatePass.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div style="display: inline-block">
|
||||
<el-dialog :visible.sync="dialog" :close-on-click-modal="false" :before-close="cancel" :title="title" append-to-body width="500px" @close="cancel">
|
||||
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="88px">
|
||||
<el-form-item label="旧密码" prop="oldPass">
|
||||
<el-input v-model="form.oldPass" type="password" auto-complete="on" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" prop="newPass">
|
||||
<el-input v-model="form.newPass" type="password" auto-complete="on" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="confirmPass">
|
||||
<el-input v-model="form.confirmPass" type="password" auto-complete="on" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="cancel">取消</el-button>
|
||||
<el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
import { updatePass } from '@/api/system/user'
|
||||
export default {
|
||||
data() {
|
||||
const confirmPass = (rule, value, callback) => {
|
||||
if (value) {
|
||||
if (this.form.newPass !== value) {
|
||||
callback(new Error('两次输入的密码不一致'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
} else {
|
||||
callback(new Error('请再次输入密码'))
|
||||
}
|
||||
}
|
||||
return {
|
||||
loading: false, dialog: false, title: '修改密码', form: { oldPass: '', newPass: '', confirmPass: '' },
|
||||
rules: {
|
||||
oldPass: [
|
||||
{ required: true, message: '请输入旧密码', trigger: 'blur' }
|
||||
],
|
||||
newPass: [
|
||||
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||
{ min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
confirmPass: [
|
||||
{ required: true, validator: confirmPass, trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cancel() {
|
||||
this.resetForm()
|
||||
},
|
||||
doSubmit() {
|
||||
this.$refs['form'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.loading = true
|
||||
updatePass(this.form).then(res => {
|
||||
this.resetForm()
|
||||
this.$notify({
|
||||
title: '密码修改成功,请重新登录',
|
||||
type: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
setTimeout(() => {
|
||||
store.dispatch('LogOut').then(() => {
|
||||
location.reload() // 为了重新实例化vue-router对象 避免bug
|
||||
})
|
||||
}, 1500)
|
||||
}).catch(err => {
|
||||
this.loading = false
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.dialog = false
|
||||
this.$refs['form'].resetFields()
|
||||
this.form = { oldPass: '', newPass: '', confirmPass: '' }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
254
src/views/govern/metadata/detail/index.vue
Normal file
254
src/views/govern/metadata/detail/index.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.name" clearable size="small" placeholder="输入部门名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
|
||||
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
|
||||
</el-select>
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表单组件-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
|
||||
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input v-model="form.name" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门排序" prop="deptSort">
|
||||
<el-input-number
|
||||
v-model.number="form.deptSort"
|
||||
:min="0"
|
||||
:max="999"
|
||||
controls-position="right"
|
||||
style="width: 370px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="顶级部门">
|
||||
<el-radio-group v-model="form.isTop" style="width: 140px">
|
||||
<el-radio label="1">是</el-radio>
|
||||
<el-radio label="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="enabled">
|
||||
<el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.isTop === '0'" style="margin-bottom: 0;" label="上级部门" prop="pid">
|
||||
<treeselect
|
||||
v-model="form.pid"
|
||||
:load-options="loadDepts"
|
||||
:options="depts"
|
||||
style="width: 370px;"
|
||||
placeholder="选择上级类目"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
lazy
|
||||
:load="getDeptDatas"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
:data="crud.data"
|
||||
row-key="id"
|
||||
@select="crud.selectChange"
|
||||
@select-all="crud.selectAllChange"
|
||||
@selection-change="crud.selectionChangeHandler"
|
||||
>
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column label="名称" prop="name" />
|
||||
<el-table-column label="排序" prop="deptSort" />
|
||||
<el-table-column label="状态" align="center" prop="enabled">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
:disabled="scope.row.id === 1"
|
||||
active-color="#409EFF"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled,)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" />
|
||||
<el-table-column v-if="checkPer(['admin','dept:edit','dept:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
:disabled-dle="scope.row.id === 1"
|
||||
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudDept from '@/api/system/dept'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
const defaultForm = { id: null, name: null, isTop: '1', subCount: 0, pid: null, deptSort: 999, enabled: 'true' }
|
||||
export default {
|
||||
name: 'Dept',
|
||||
components: { Treeselect, crudOperation, rrOperation, udOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '部门', url: '/system/api/dept', crudMethod: { ...crudDept }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
// 设置数据字典
|
||||
dicts: ['dept_status'],
|
||||
data() {
|
||||
return {
|
||||
depts: [],
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
deptSort: [
|
||||
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
|
||||
]
|
||||
},
|
||||
permission: {
|
||||
add: ['admin', 'dept:add'],
|
||||
edit: ['admin', 'dept:edit'],
|
||||
del: ['admin', 'dept:del']
|
||||
},
|
||||
enabledTypeOptions: [
|
||||
{ key: 'true', display_name: '正常' },
|
||||
{ key: 'false', display_name: '禁用' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDeptDatas(tree, treeNode, resolve) {
|
||||
const params = { pid: tree.id }
|
||||
setTimeout(() => {
|
||||
crudDept.getDepts(params).then(res => {
|
||||
resolve(res.content)
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
// 新增与编辑前做的操作
|
||||
[CRUD.HOOK.afterToCU](crud, form) {
|
||||
if (form.pid !== null) {
|
||||
form.isTop = '0'
|
||||
} else if (form.id !== null) {
|
||||
form.isTop = '1'
|
||||
}
|
||||
form.enabled = `${form.enabled}`
|
||||
if (form.id != null) {
|
||||
this.getSupDepts(form.id)
|
||||
} else {
|
||||
this.getDepts()
|
||||
}
|
||||
},
|
||||
getSupDepts(id) {
|
||||
crudDept.getDeptSuperior(id).then(res => {
|
||||
const date = res.content
|
||||
this.buildDepts(date)
|
||||
this.depts = date
|
||||
})
|
||||
},
|
||||
buildDepts(depts) {
|
||||
depts.forEach(data => {
|
||||
if (data.children) {
|
||||
this.buildDepts(data.children)
|
||||
}
|
||||
if (data.hasChildren && !data.children) {
|
||||
data.children = null
|
||||
}
|
||||
})
|
||||
},
|
||||
getDepts() {
|
||||
crudDept.getDepts({ enabled: true }).then(res => {
|
||||
this.depts = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取弹窗内部门数据
|
||||
loadDepts({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
crudDept.getDepts({ enabled: true, pid: parentNode.id }).then(res => {
|
||||
parentNode.children = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 提交前的验证
|
||||
[CRUD.HOOK.afterValidateCU]() {
|
||||
if (this.form.pid !== null && this.form.pid === this.form.id) {
|
||||
this.$message({
|
||||
message: '上级部门不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (this.form.isTop === '1') {
|
||||
this.form.pid = null
|
||||
}
|
||||
return true
|
||||
},
|
||||
// 改变状态
|
||||
changeEnabled(data, val) {
|
||||
this.$confirm('此操作将 "' + this.dict.label.dept_status[val] + '" ' + data.name + '部门, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
crudDept.edit(data).then(res => {
|
||||
this.crud.notify(this.dict.label.dept_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(err => {
|
||||
data.enabled = !data.enabled
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
},
|
||||
checkboxT(row, rowIndex) {
|
||||
return row.id !== 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
484
src/views/govern/metadata/index.vue
Normal file
484
src/views/govern/metadata/index.vue
Normal file
@@ -0,0 +1,484 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<!--侧边部门数据-->
|
||||
<el-col :xs="9" :sm="6" :md="5" :lg="4" :xl="4">
|
||||
<div class="head-container">
|
||||
<el-input
|
||||
v-model="deptName"
|
||||
clearable
|
||||
size="small"
|
||||
placeholdfer="输入部门名称搜索"
|
||||
prefix-icon="el-icon-search"
|
||||
class="filter-item"
|
||||
@input="getDeptDatas"
|
||||
/>
|
||||
</div>
|
||||
<el-tree
|
||||
:data="deptDatas"
|
||||
:load="getDeptDatas"
|
||||
:props="defaultProps"
|
||||
:expand-on-click-node="false"
|
||||
lazy
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</el-col>
|
||||
<!--用户数据-->
|
||||
<el-col :xs="15" :sm="18" :md="19" :lg="20" :xl="20">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input
|
||||
v-model="query.blurry"
|
||||
clearable
|
||||
size="small"
|
||||
placeholder="输入名称或者邮箱搜索"
|
||||
style="width: 200px;"
|
||||
class="filter-item"
|
||||
@keyup.enter.native="crud.toQuery"
|
||||
/>
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select
|
||||
v-model="query.enabled"
|
||||
clearable
|
||||
size="small"
|
||||
placeholder="状态"
|
||||
class="filter-item"
|
||||
style="width: 90px"
|
||||
@change="crud.toQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in enabledTypeOptions"
|
||||
:key="item.key"
|
||||
:label="item.display_name"
|
||||
:value="item.key"
|
||||
/>
|
||||
</el-select>
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation show="" :permission="permission" />
|
||||
</div>
|
||||
<!--表单渲染-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="570px">
|
||||
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="66px">
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-input v-model="form.username" @keydown.native="keydown($event)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="电话" prop="phone">
|
||||
<el-input v-model.number="form.phone" />
|
||||
</el-form-item>
|
||||
<el-form-item label="昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" @keydown.native="keydown($event)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="form.email" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门" prop="dept.id">
|
||||
<treeselect
|
||||
v-model="form.dept.id"
|
||||
:options="depts"
|
||||
:load-options="loadDepts"
|
||||
style="width: 178px"
|
||||
placeholder="选择部门"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="岗位" prop="jobs">
|
||||
<el-select
|
||||
v-model="jobDatas"
|
||||
style="width: 178px"
|
||||
multiple
|
||||
placeholder="请选择"
|
||||
@remove-tag="deleteTag"
|
||||
@change="changeJob"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in jobs"
|
||||
:key="item.name"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-radio-group v-model="form.gender" style="width: 178px">
|
||||
<el-radio label="男">男</el-radio>
|
||||
<el-radio label="女">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="form.enabled" :disabled="form.id === user.id">
|
||||
<el-radio
|
||||
v-for="item in dict.user_status"
|
||||
:key="item.id"
|
||||
:label="item.value"
|
||||
>{{ item.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item style="margin-bottom: 0;" label="角色" prop="roles">
|
||||
<el-select
|
||||
v-model="roleDatas"
|
||||
style="width: 437px"
|
||||
multiple
|
||||
placeholder="请选择"
|
||||
@remove-tag="deleteTag"
|
||||
@change="changeRole"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in roles"
|
||||
:key="item.name"
|
||||
:disabled="level !== 1 && item.level <= level"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="username" label="用户名" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="nickName" label="昵称" />
|
||||
<el-table-column prop="gender" label="性别" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="phone" width="100" label="电话" />
|
||||
<el-table-column :show-overflow-tooltip="true" width="135" prop="email" label="邮箱" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="dept" label="部门">
|
||||
<template slot-scope="scope">
|
||||
<div>{{ scope.row.dept.name }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="enabled">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
:disabled="user.id === scope.row.id"
|
||||
active-color="#409EFF"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" prop="createTime" width="135" label="创建日期" />
|
||||
<el-table-column
|
||||
v-if="checkPer(['admin','user:edit','user:del'])"
|
||||
label="操作"
|
||||
width="115"
|
||||
align="center"
|
||||
fixed="right"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
:disabled-dle="scope.row.id === user.id"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<pagination />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudUser from '@/api/system/user'
|
||||
import { isvalidPhone } from '@/utils/validate'
|
||||
import { getDepts, getDeptSuperior } from '@/api/system/dept'
|
||||
import { getAll, getLevel } from '@/api/system/role'
|
||||
import { getAllJob } from '@/api/system/job'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import pagination from '@crud/Pagination'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import { mapGetters } from 'vuex'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
let userRoles = []
|
||||
let userJobs = []
|
||||
const defaultForm = { id: null, username: null, nickName: null, gender: '男', email: null, enabled: 'false', roles: [], jobs: [], dept: { id: null }, phone: null }
|
||||
export default {
|
||||
name: 'User',
|
||||
components: { Treeselect, crudOperation, rrOperation, udOperation, pagination, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '用户', url: '/system/api/users', crudMethod: { ...crudUser }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
// 数据字典
|
||||
dicts: ['user_status'],
|
||||
data() {
|
||||
// 自定义验证
|
||||
const validPhone = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('请输入电话号码'))
|
||||
} else if (!isvalidPhone(value)) {
|
||||
callback(new Error('请输入正确的11位手机号码'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
height: document.documentElement.clientHeight - 180 + 'px;',
|
||||
deptName: '', depts: [], deptDatas: [], jobs: [], level: 3, roles: [],
|
||||
jobDatas: [], roleDatas: [], // 多选时使用
|
||||
defaultProps: { children: 'children', label: 'name', isLeaf: 'leaf' },
|
||||
permission: {
|
||||
add: ['admin', 'user:add'],
|
||||
edit: ['admin', 'user:edit'],
|
||||
del: ['admin', 'user:del']
|
||||
},
|
||||
enabledTypeOptions: [
|
||||
{ key: 'true', display_name: '激活' },
|
||||
{ key: 'false', display_name: '锁定' }
|
||||
],
|
||||
rules: {
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
nickName: [
|
||||
{ required: true, message: '请输入用户昵称', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
email: [
|
||||
{ required: true, message: '请输入邮箱地址', trigger: 'blur' },
|
||||
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
|
||||
],
|
||||
phone: [
|
||||
{ required: true, trigger: 'blur', validator: validPhone }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'user'
|
||||
])
|
||||
},
|
||||
created() {
|
||||
this.crud.msg.add = '新增成功,默认密码:123456'
|
||||
},
|
||||
mounted: function() {
|
||||
const that = this
|
||||
window.onresize = function temp() {
|
||||
that.height = document.documentElement.clientHeight - 180 + 'px;'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 禁止输入空格
|
||||
keydown(e) {
|
||||
if (e.keyCode === 32) {
|
||||
e.returnValue = false
|
||||
}
|
||||
},
|
||||
changeRole(value) {
|
||||
userRoles = []
|
||||
value.forEach(function(data, index) {
|
||||
const role = { id: data }
|
||||
userRoles.push(role)
|
||||
})
|
||||
},
|
||||
changeJob(value) {
|
||||
userJobs = []
|
||||
value.forEach(function(data, index) {
|
||||
const job = { id: data }
|
||||
userJobs.push(job)
|
||||
})
|
||||
},
|
||||
deleteTag(value) {
|
||||
userRoles.forEach(function(data, index) {
|
||||
if (data.id === value) {
|
||||
userRoles.splice(index, value)
|
||||
}
|
||||
})
|
||||
},
|
||||
// 新增与编辑前做的操作
|
||||
[CRUD.HOOK.afterToCU](crud, form) {
|
||||
this.getRoles()
|
||||
if (form.id == null) {
|
||||
this.getDepts()
|
||||
} else {
|
||||
this.getSupDepts(form.dept.id)
|
||||
}
|
||||
this.getRoleLevel()
|
||||
this.getJobs()
|
||||
form.enabled = form.enabled.toString()
|
||||
},
|
||||
// 新增前将多选的值设置为空
|
||||
[CRUD.HOOK.beforeToAdd]() {
|
||||
this.jobDatas = []
|
||||
this.roleDatas = []
|
||||
},
|
||||
// 初始化编辑时候的角色与岗位
|
||||
[CRUD.HOOK.beforeToEdit](crud, form) {
|
||||
this.getJobs(this.form.dept.id)
|
||||
this.jobDatas = []
|
||||
this.roleDatas = []
|
||||
userRoles = []
|
||||
userJobs = []
|
||||
const _this = this
|
||||
form.roles.forEach(function(role, index) {
|
||||
_this.roleDatas.push(role.id)
|
||||
const rol = { id: role.id }
|
||||
userRoles.push(rol)
|
||||
})
|
||||
form.jobs.forEach(function(job, index) {
|
||||
_this.jobDatas.push(job.id)
|
||||
const data = { id: job.id }
|
||||
userJobs.push(data)
|
||||
})
|
||||
},
|
||||
// 提交前做的操作
|
||||
[CRUD.HOOK.afterValidateCU](crud) {
|
||||
if (!crud.form.dept.id) {
|
||||
this.$message({
|
||||
message: '部门不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
} else if (this.jobDatas.length === 0) {
|
||||
this.$message({
|
||||
message: '岗位不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
} else if (this.roleDatas.length === 0) {
|
||||
this.$message({
|
||||
message: '角色不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
crud.form.roles = userRoles
|
||||
crud.form.jobs = userJobs
|
||||
return true
|
||||
},
|
||||
// 获取左侧部门数据
|
||||
getDeptDatas(node, resolve) {
|
||||
const sort = 'id,desc'
|
||||
const params = { sort: sort }
|
||||
if (typeof node !== 'object') {
|
||||
if (node) {
|
||||
params['name'] = node
|
||||
}
|
||||
} else if (node.level !== 0) {
|
||||
params['pid'] = node.data.id
|
||||
}
|
||||
setTimeout(() => {
|
||||
getDepts(params).then(res => {
|
||||
if (resolve) {
|
||||
resolve(res.content)
|
||||
} else {
|
||||
this.deptDatas = res.content
|
||||
}
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
getDepts() {
|
||||
getDepts({ enabled: true }).then(res => {
|
||||
this.depts = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
},
|
||||
getSupDepts(deptId) {
|
||||
getDeptSuperior(deptId).then(res => {
|
||||
const date = res.content
|
||||
this.buildDepts(date)
|
||||
this.depts = date
|
||||
})
|
||||
},
|
||||
buildDepts(depts) {
|
||||
depts.forEach(data => {
|
||||
if (data.children) {
|
||||
this.buildDepts(data.children)
|
||||
}
|
||||
if (data.hasChildren && !data.children) {
|
||||
data.children = null
|
||||
}
|
||||
})
|
||||
},
|
||||
// 获取弹窗内部门数据
|
||||
loadDepts({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
getDepts({ enabled: true, pid: parentNode.id }).then(res => {
|
||||
parentNode.children = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 200)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 切换部门
|
||||
handleNodeClick(data) {
|
||||
if (data.pid === 0) {
|
||||
this.query.deptId = null
|
||||
} else {
|
||||
this.query.deptId = data.id
|
||||
}
|
||||
this.crud.toQuery()
|
||||
},
|
||||
// 改变状态
|
||||
changeEnabled(data, val) {
|
||||
this.$confirm('此操作将 "' + this.dict.label.user_status[val] + '" ' + data.username + ', 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
crudUser.edit(data).then(res => {
|
||||
this.crud.notify(this.dict.label.user_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
},
|
||||
// 获取弹窗内角色数据
|
||||
getRoles() {
|
||||
getAll().then(res => {
|
||||
this.roles = res
|
||||
}).catch(() => { })
|
||||
},
|
||||
// 获取弹窗内岗位数据
|
||||
getJobs() {
|
||||
getAllJob().then(res => {
|
||||
this.jobs = res.content
|
||||
}).catch(() => { })
|
||||
},
|
||||
// 获取权限级别
|
||||
getRoleLevel() {
|
||||
getLevel().then(res => {
|
||||
this.level = res.level
|
||||
}).catch(() => { })
|
||||
},
|
||||
checkboxT(row, rowIndex) {
|
||||
return row.id !== this.user.id
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
254
src/views/govern/metadata/overview/index.vue
Normal file
254
src/views/govern/metadata/overview/index.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.name" clearable size="small" placeholder="输入部门名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
|
||||
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
|
||||
</el-select>
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表单组件-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
|
||||
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input v-model="form.name" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门排序" prop="deptSort">
|
||||
<el-input-number
|
||||
v-model.number="form.deptSort"
|
||||
:min="0"
|
||||
:max="999"
|
||||
controls-position="right"
|
||||
style="width: 370px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="顶级部门">
|
||||
<el-radio-group v-model="form.isTop" style="width: 140px">
|
||||
<el-radio label="1">是</el-radio>
|
||||
<el-radio label="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="enabled">
|
||||
<el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.isTop === '0'" style="margin-bottom: 0;" label="上级部门" prop="pid">
|
||||
<treeselect
|
||||
v-model="form.pid"
|
||||
:load-options="loadDepts"
|
||||
:options="depts"
|
||||
style="width: 370px;"
|
||||
placeholder="选择上级类目"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
lazy
|
||||
:load="getDeptDatas"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
:data="crud.data"
|
||||
row-key="id"
|
||||
@select="crud.selectChange"
|
||||
@select-all="crud.selectAllChange"
|
||||
@selection-change="crud.selectionChangeHandler"
|
||||
>
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column label="名称" prop="name" />
|
||||
<el-table-column label="排序" prop="deptSort" />
|
||||
<el-table-column label="状态" align="center" prop="enabled">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
:disabled="scope.row.id === 1"
|
||||
active-color="#409EFF"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled,)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" />
|
||||
<el-table-column v-if="checkPer(['admin','dept:edit','dept:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
:disabled-dle="scope.row.id === 1"
|
||||
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudDept from '@/api/system/dept'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
const defaultForm = { id: null, name: null, isTop: '1', subCount: 0, pid: null, deptSort: 999, enabled: 'true' }
|
||||
export default {
|
||||
name: 'Dept',
|
||||
components: { Treeselect, crudOperation, rrOperation, udOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '部门', url: '/system/api/dept', crudMethod: { ...crudDept }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
// 设置数据字典
|
||||
dicts: ['dept_status'],
|
||||
data() {
|
||||
return {
|
||||
depts: [],
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
deptSort: [
|
||||
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
|
||||
]
|
||||
},
|
||||
permission: {
|
||||
add: ['admin', 'dept:add'],
|
||||
edit: ['admin', 'dept:edit'],
|
||||
del: ['admin', 'dept:del']
|
||||
},
|
||||
enabledTypeOptions: [
|
||||
{ key: 'true', display_name: '正常' },
|
||||
{ key: 'false', display_name: '禁用' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDeptDatas(tree, treeNode, resolve) {
|
||||
const params = { pid: tree.id }
|
||||
setTimeout(() => {
|
||||
crudDept.getDepts(params).then(res => {
|
||||
resolve(res.content)
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
// 新增与编辑前做的操作
|
||||
[CRUD.HOOK.afterToCU](crud, form) {
|
||||
if (form.pid !== null) {
|
||||
form.isTop = '0'
|
||||
} else if (form.id !== null) {
|
||||
form.isTop = '1'
|
||||
}
|
||||
form.enabled = `${form.enabled}`
|
||||
if (form.id != null) {
|
||||
this.getSupDepts(form.id)
|
||||
} else {
|
||||
this.getDepts()
|
||||
}
|
||||
},
|
||||
getSupDepts(id) {
|
||||
crudDept.getDeptSuperior(id).then(res => {
|
||||
const date = res.content
|
||||
this.buildDepts(date)
|
||||
this.depts = date
|
||||
})
|
||||
},
|
||||
buildDepts(depts) {
|
||||
depts.forEach(data => {
|
||||
if (data.children) {
|
||||
this.buildDepts(data.children)
|
||||
}
|
||||
if (data.hasChildren && !data.children) {
|
||||
data.children = null
|
||||
}
|
||||
})
|
||||
},
|
||||
getDepts() {
|
||||
crudDept.getDepts({ enabled: true }).then(res => {
|
||||
this.depts = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取弹窗内部门数据
|
||||
loadDepts({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
crudDept.getDepts({ enabled: true, pid: parentNode.id }).then(res => {
|
||||
parentNode.children = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 提交前的验证
|
||||
[CRUD.HOOK.afterValidateCU]() {
|
||||
if (this.form.pid !== null && this.form.pid === this.form.id) {
|
||||
this.$message({
|
||||
message: '上级部门不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (this.form.isTop === '1') {
|
||||
this.form.pid = null
|
||||
}
|
||||
return true
|
||||
},
|
||||
// 改变状态
|
||||
changeEnabled(data, val) {
|
||||
this.$confirm('此操作将 "' + this.dict.label.dept_status[val] + '" ' + data.name + '部门, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
crudDept.edit(data).then(res => {
|
||||
this.crud.notify(this.dict.label.dept_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(err => {
|
||||
data.enabled = !data.enabled
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
},
|
||||
checkboxT(row, rowIndex) {
|
||||
return row.id !== 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
254
src/views/govern/metadata/pickup/datasource/index.vue
Normal file
254
src/views/govern/metadata/pickup/datasource/index.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.name" clearable size="small" placeholder="输入部门名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
|
||||
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
|
||||
</el-select>
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表单组件-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
|
||||
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input v-model="form.name" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门排序" prop="deptSort">
|
||||
<el-input-number
|
||||
v-model.number="form.deptSort"
|
||||
:min="0"
|
||||
:max="999"
|
||||
controls-position="right"
|
||||
style="width: 370px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="顶级部门">
|
||||
<el-radio-group v-model="form.isTop" style="width: 140px">
|
||||
<el-radio label="1">是</el-radio>
|
||||
<el-radio label="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="enabled">
|
||||
<el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.isTop === '0'" style="margin-bottom: 0;" label="上级部门" prop="pid">
|
||||
<treeselect
|
||||
v-model="form.pid"
|
||||
:load-options="loadDepts"
|
||||
:options="depts"
|
||||
style="width: 370px;"
|
||||
placeholder="选择上级类目"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
lazy
|
||||
:load="getDeptDatas"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
:data="crud.data"
|
||||
row-key="id"
|
||||
@select="crud.selectChange"
|
||||
@select-all="crud.selectAllChange"
|
||||
@selection-change="crud.selectionChangeHandler"
|
||||
>
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column label="名称" prop="name" />
|
||||
<el-table-column label="排序" prop="deptSort" />
|
||||
<el-table-column label="状态" align="center" prop="enabled">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
:disabled="scope.row.id === 1"
|
||||
active-color="#409EFF"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled,)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" />
|
||||
<el-table-column v-if="checkPer(['admin','dept:edit','dept:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
:disabled-dle="scope.row.id === 1"
|
||||
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudDept from '@/api/system/dept'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
const defaultForm = { id: null, name: null, isTop: '1', subCount: 0, pid: null, deptSort: 999, enabled: 'true' }
|
||||
export default {
|
||||
name: 'Dept',
|
||||
components: { Treeselect, crudOperation, rrOperation, udOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '部门', url: '/system/api/dept', crudMethod: { ...crudDept }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
// 设置数据字典
|
||||
dicts: ['dept_status'],
|
||||
data() {
|
||||
return {
|
||||
depts: [],
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
deptSort: [
|
||||
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
|
||||
]
|
||||
},
|
||||
permission: {
|
||||
add: ['admin', 'dept:add'],
|
||||
edit: ['admin', 'dept:edit'],
|
||||
del: ['admin', 'dept:del']
|
||||
},
|
||||
enabledTypeOptions: [
|
||||
{ key: 'true', display_name: '正常' },
|
||||
{ key: 'false', display_name: '禁用' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDeptDatas(tree, treeNode, resolve) {
|
||||
const params = { pid: tree.id }
|
||||
setTimeout(() => {
|
||||
crudDept.getDepts(params).then(res => {
|
||||
resolve(res.content)
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
// 新增与编辑前做的操作
|
||||
[CRUD.HOOK.afterToCU](crud, form) {
|
||||
if (form.pid !== null) {
|
||||
form.isTop = '0'
|
||||
} else if (form.id !== null) {
|
||||
form.isTop = '1'
|
||||
}
|
||||
form.enabled = `${form.enabled}`
|
||||
if (form.id != null) {
|
||||
this.getSupDepts(form.id)
|
||||
} else {
|
||||
this.getDepts()
|
||||
}
|
||||
},
|
||||
getSupDepts(id) {
|
||||
crudDept.getDeptSuperior(id).then(res => {
|
||||
const date = res.content
|
||||
this.buildDepts(date)
|
||||
this.depts = date
|
||||
})
|
||||
},
|
||||
buildDepts(depts) {
|
||||
depts.forEach(data => {
|
||||
if (data.children) {
|
||||
this.buildDepts(data.children)
|
||||
}
|
||||
if (data.hasChildren && !data.children) {
|
||||
data.children = null
|
||||
}
|
||||
})
|
||||
},
|
||||
getDepts() {
|
||||
crudDept.getDepts({ enabled: true }).then(res => {
|
||||
this.depts = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取弹窗内部门数据
|
||||
loadDepts({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
crudDept.getDepts({ enabled: true, pid: parentNode.id }).then(res => {
|
||||
parentNode.children = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 提交前的验证
|
||||
[CRUD.HOOK.afterValidateCU]() {
|
||||
if (this.form.pid !== null && this.form.pid === this.form.id) {
|
||||
this.$message({
|
||||
message: '上级部门不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (this.form.isTop === '1') {
|
||||
this.form.pid = null
|
||||
}
|
||||
return true
|
||||
},
|
||||
// 改变状态
|
||||
changeEnabled(data, val) {
|
||||
this.$confirm('此操作将 "' + this.dict.label.dept_status[val] + '" ' + data.name + '部门, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
crudDept.edit(data).then(res => {
|
||||
this.crud.notify(this.dict.label.dept_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(err => {
|
||||
data.enabled = !data.enabled
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
},
|
||||
checkboxT(row, rowIndex) {
|
||||
return row.id !== 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
254
src/views/govern/metadata/pickup/index.vue
Normal file
254
src/views/govern/metadata/pickup/index.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.name" clearable size="small" placeholder="输入部门名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
|
||||
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
|
||||
</el-select>
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表单组件-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
|
||||
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input v-model="form.name" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门排序" prop="deptSort">
|
||||
<el-input-number
|
||||
v-model.number="form.deptSort"
|
||||
:min="0"
|
||||
:max="999"
|
||||
controls-position="right"
|
||||
style="width: 370px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="顶级部门">
|
||||
<el-radio-group v-model="form.isTop" style="width: 140px">
|
||||
<el-radio label="1">是</el-radio>
|
||||
<el-radio label="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="enabled">
|
||||
<el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.isTop === '0'" style="margin-bottom: 0;" label="上级部门" prop="pid">
|
||||
<treeselect
|
||||
v-model="form.pid"
|
||||
:load-options="loadDepts"
|
||||
:options="depts"
|
||||
style="width: 370px;"
|
||||
placeholder="选择上级类目"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
lazy
|
||||
:load="getDeptDatas"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
:data="crud.data"
|
||||
row-key="id"
|
||||
@select="crud.selectChange"
|
||||
@select-all="crud.selectAllChange"
|
||||
@selection-change="crud.selectionChangeHandler"
|
||||
>
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column label="名称" prop="name" />
|
||||
<el-table-column label="排序" prop="deptSort" />
|
||||
<el-table-column label="状态" align="center" prop="enabled">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
:disabled="scope.row.id === 1"
|
||||
active-color="#409EFF"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled,)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" />
|
||||
<el-table-column v-if="checkPer(['admin','dept:edit','dept:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
:disabled-dle="scope.row.id === 1"
|
||||
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudDept from '@/api/system/dept'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
const defaultForm = { id: null, name: null, isTop: '1', subCount: 0, pid: null, deptSort: 999, enabled: 'true' }
|
||||
export default {
|
||||
name: 'Dept',
|
||||
components: { Treeselect, crudOperation, rrOperation, udOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '部门', url: '/system/api/dept', crudMethod: { ...crudDept }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
// 设置数据字典
|
||||
dicts: ['dept_status'],
|
||||
data() {
|
||||
return {
|
||||
depts: [],
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
deptSort: [
|
||||
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
|
||||
]
|
||||
},
|
||||
permission: {
|
||||
add: ['admin', 'dept:add'],
|
||||
edit: ['admin', 'dept:edit'],
|
||||
del: ['admin', 'dept:del']
|
||||
},
|
||||
enabledTypeOptions: [
|
||||
{ key: 'true', display_name: '正常' },
|
||||
{ key: 'false', display_name: '禁用' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDeptDatas(tree, treeNode, resolve) {
|
||||
const params = { pid: tree.id }
|
||||
setTimeout(() => {
|
||||
crudDept.getDepts(params).then(res => {
|
||||
resolve(res.content)
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
// 新增与编辑前做的操作
|
||||
[CRUD.HOOK.afterToCU](crud, form) {
|
||||
if (form.pid !== null) {
|
||||
form.isTop = '0'
|
||||
} else if (form.id !== null) {
|
||||
form.isTop = '1'
|
||||
}
|
||||
form.enabled = `${form.enabled}`
|
||||
if (form.id != null) {
|
||||
this.getSupDepts(form.id)
|
||||
} else {
|
||||
this.getDepts()
|
||||
}
|
||||
},
|
||||
getSupDepts(id) {
|
||||
crudDept.getDeptSuperior(id).then(res => {
|
||||
const date = res.content
|
||||
this.buildDepts(date)
|
||||
this.depts = date
|
||||
})
|
||||
},
|
||||
buildDepts(depts) {
|
||||
depts.forEach(data => {
|
||||
if (data.children) {
|
||||
this.buildDepts(data.children)
|
||||
}
|
||||
if (data.hasChildren && !data.children) {
|
||||
data.children = null
|
||||
}
|
||||
})
|
||||
},
|
||||
getDepts() {
|
||||
crudDept.getDepts({ enabled: true }).then(res => {
|
||||
this.depts = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取弹窗内部门数据
|
||||
loadDepts({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
crudDept.getDepts({ enabled: true, pid: parentNode.id }).then(res => {
|
||||
parentNode.children = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 提交前的验证
|
||||
[CRUD.HOOK.afterValidateCU]() {
|
||||
if (this.form.pid !== null && this.form.pid === this.form.id) {
|
||||
this.$message({
|
||||
message: '上级部门不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (this.form.isTop === '1') {
|
||||
this.form.pid = null
|
||||
}
|
||||
return true
|
||||
},
|
||||
// 改变状态
|
||||
changeEnabled(data, val) {
|
||||
this.$confirm('此操作将 "' + this.dict.label.dept_status[val] + '" ' + data.name + '部门, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
crudDept.edit(data).then(res => {
|
||||
this.crud.notify(this.dict.label.dept_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(err => {
|
||||
data.enabled = !data.enabled
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
},
|
||||
checkboxT(row, rowIndex) {
|
||||
return row.id !== 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
254
src/views/govern/metadata/pickup/log/index.vue
Normal file
254
src/views/govern/metadata/pickup/log/index.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.name" clearable size="small" placeholder="输入部门名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
|
||||
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
|
||||
</el-select>
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表单组件-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
|
||||
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input v-model="form.name" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门排序" prop="deptSort">
|
||||
<el-input-number
|
||||
v-model.number="form.deptSort"
|
||||
:min="0"
|
||||
:max="999"
|
||||
controls-position="right"
|
||||
style="width: 370px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="顶级部门">
|
||||
<el-radio-group v-model="form.isTop" style="width: 140px">
|
||||
<el-radio label="1">是</el-radio>
|
||||
<el-radio label="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="enabled">
|
||||
<el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.isTop === '0'" style="margin-bottom: 0;" label="上级部门" prop="pid">
|
||||
<treeselect
|
||||
v-model="form.pid"
|
||||
:load-options="loadDepts"
|
||||
:options="depts"
|
||||
style="width: 370px;"
|
||||
placeholder="选择上级类目"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
lazy
|
||||
:load="getDeptDatas"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
:data="crud.data"
|
||||
row-key="id"
|
||||
@select="crud.selectChange"
|
||||
@select-all="crud.selectAllChange"
|
||||
@selection-change="crud.selectionChangeHandler"
|
||||
>
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column label="名称" prop="name" />
|
||||
<el-table-column label="排序" prop="deptSort" />
|
||||
<el-table-column label="状态" align="center" prop="enabled">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
:disabled="scope.row.id === 1"
|
||||
active-color="#409EFF"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled,)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" />
|
||||
<el-table-column v-if="checkPer(['admin','dept:edit','dept:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
:disabled-dle="scope.row.id === 1"
|
||||
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudDept from '@/api/system/dept'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
const defaultForm = { id: null, name: null, isTop: '1', subCount: 0, pid: null, deptSort: 999, enabled: 'true' }
|
||||
export default {
|
||||
name: 'Dept',
|
||||
components: { Treeselect, crudOperation, rrOperation, udOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '部门', url: '/system/api/dept', crudMethod: { ...crudDept }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
// 设置数据字典
|
||||
dicts: ['dept_status'],
|
||||
data() {
|
||||
return {
|
||||
depts: [],
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
deptSort: [
|
||||
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
|
||||
]
|
||||
},
|
||||
permission: {
|
||||
add: ['admin', 'dept:add'],
|
||||
edit: ['admin', 'dept:edit'],
|
||||
del: ['admin', 'dept:del']
|
||||
},
|
||||
enabledTypeOptions: [
|
||||
{ key: 'true', display_name: '正常' },
|
||||
{ key: 'false', display_name: '禁用' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDeptDatas(tree, treeNode, resolve) {
|
||||
const params = { pid: tree.id }
|
||||
setTimeout(() => {
|
||||
crudDept.getDepts(params).then(res => {
|
||||
resolve(res.content)
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
// 新增与编辑前做的操作
|
||||
[CRUD.HOOK.afterToCU](crud, form) {
|
||||
if (form.pid !== null) {
|
||||
form.isTop = '0'
|
||||
} else if (form.id !== null) {
|
||||
form.isTop = '1'
|
||||
}
|
||||
form.enabled = `${form.enabled}`
|
||||
if (form.id != null) {
|
||||
this.getSupDepts(form.id)
|
||||
} else {
|
||||
this.getDepts()
|
||||
}
|
||||
},
|
||||
getSupDepts(id) {
|
||||
crudDept.getDeptSuperior(id).then(res => {
|
||||
const date = res.content
|
||||
this.buildDepts(date)
|
||||
this.depts = date
|
||||
})
|
||||
},
|
||||
buildDepts(depts) {
|
||||
depts.forEach(data => {
|
||||
if (data.children) {
|
||||
this.buildDepts(data.children)
|
||||
}
|
||||
if (data.hasChildren && !data.children) {
|
||||
data.children = null
|
||||
}
|
||||
})
|
||||
},
|
||||
getDepts() {
|
||||
crudDept.getDepts({ enabled: true }).then(res => {
|
||||
this.depts = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取弹窗内部门数据
|
||||
loadDepts({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
crudDept.getDepts({ enabled: true, pid: parentNode.id }).then(res => {
|
||||
parentNode.children = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 提交前的验证
|
||||
[CRUD.HOOK.afterValidateCU]() {
|
||||
if (this.form.pid !== null && this.form.pid === this.form.id) {
|
||||
this.$message({
|
||||
message: '上级部门不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (this.form.isTop === '1') {
|
||||
this.form.pid = null
|
||||
}
|
||||
return true
|
||||
},
|
||||
// 改变状态
|
||||
changeEnabled(data, val) {
|
||||
this.$confirm('此操作将 "' + this.dict.label.dept_status[val] + '" ' + data.name + '部门, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
crudDept.edit(data).then(res => {
|
||||
this.crud.notify(this.dict.label.dept_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(err => {
|
||||
data.enabled = !data.enabled
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
},
|
||||
checkboxT(row, rowIndex) {
|
||||
return row.id !== 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
254
src/views/govern/metadata/pickup/task/index.vue
Normal file
254
src/views/govern/metadata/pickup/task/index.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.name" clearable size="small" placeholder="输入部门名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
|
||||
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
|
||||
</el-select>
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表单组件-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
|
||||
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input v-model="form.name" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门排序" prop="deptSort">
|
||||
<el-input-number
|
||||
v-model.number="form.deptSort"
|
||||
:min="0"
|
||||
:max="999"
|
||||
controls-position="right"
|
||||
style="width: 370px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="顶级部门">
|
||||
<el-radio-group v-model="form.isTop" style="width: 140px">
|
||||
<el-radio label="1">是</el-radio>
|
||||
<el-radio label="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="enabled">
|
||||
<el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.isTop === '0'" style="margin-bottom: 0;" label="上级部门" prop="pid">
|
||||
<treeselect
|
||||
v-model="form.pid"
|
||||
:load-options="loadDepts"
|
||||
:options="depts"
|
||||
style="width: 370px;"
|
||||
placeholder="选择上级类目"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
lazy
|
||||
:load="getDeptDatas"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
:data="crud.data"
|
||||
row-key="id"
|
||||
@select="crud.selectChange"
|
||||
@select-all="crud.selectAllChange"
|
||||
@selection-change="crud.selectionChangeHandler"
|
||||
>
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column label="名称" prop="name" />
|
||||
<el-table-column label="排序" prop="deptSort" />
|
||||
<el-table-column label="状态" align="center" prop="enabled">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
:disabled="scope.row.id === 1"
|
||||
active-color="#409EFF"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled,)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" />
|
||||
<el-table-column v-if="checkPer(['admin','dept:edit','dept:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
:disabled-dle="scope.row.id === 1"
|
||||
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudDept from '@/api/system/dept'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
const defaultForm = { id: null, name: null, isTop: '1', subCount: 0, pid: null, deptSort: 999, enabled: 'true' }
|
||||
export default {
|
||||
name: 'Dept',
|
||||
components: { Treeselect, crudOperation, rrOperation, udOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '部门', url: '/system/api/dept', crudMethod: { ...crudDept }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
// 设置数据字典
|
||||
dicts: ['dept_status'],
|
||||
data() {
|
||||
return {
|
||||
depts: [],
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
deptSort: [
|
||||
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
|
||||
]
|
||||
},
|
||||
permission: {
|
||||
add: ['admin', 'dept:add'],
|
||||
edit: ['admin', 'dept:edit'],
|
||||
del: ['admin', 'dept:del']
|
||||
},
|
||||
enabledTypeOptions: [
|
||||
{ key: 'true', display_name: '正常' },
|
||||
{ key: 'false', display_name: '禁用' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDeptDatas(tree, treeNode, resolve) {
|
||||
const params = { pid: tree.id }
|
||||
setTimeout(() => {
|
||||
crudDept.getDepts(params).then(res => {
|
||||
resolve(res.content)
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
// 新增与编辑前做的操作
|
||||
[CRUD.HOOK.afterToCU](crud, form) {
|
||||
if (form.pid !== null) {
|
||||
form.isTop = '0'
|
||||
} else if (form.id !== null) {
|
||||
form.isTop = '1'
|
||||
}
|
||||
form.enabled = `${form.enabled}`
|
||||
if (form.id != null) {
|
||||
this.getSupDepts(form.id)
|
||||
} else {
|
||||
this.getDepts()
|
||||
}
|
||||
},
|
||||
getSupDepts(id) {
|
||||
crudDept.getDeptSuperior(id).then(res => {
|
||||
const date = res.content
|
||||
this.buildDepts(date)
|
||||
this.depts = date
|
||||
})
|
||||
},
|
||||
buildDepts(depts) {
|
||||
depts.forEach(data => {
|
||||
if (data.children) {
|
||||
this.buildDepts(data.children)
|
||||
}
|
||||
if (data.hasChildren && !data.children) {
|
||||
data.children = null
|
||||
}
|
||||
})
|
||||
},
|
||||
getDepts() {
|
||||
crudDept.getDepts({ enabled: true }).then(res => {
|
||||
this.depts = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取弹窗内部门数据
|
||||
loadDepts({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
crudDept.getDepts({ enabled: true, pid: parentNode.id }).then(res => {
|
||||
parentNode.children = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 提交前的验证
|
||||
[CRUD.HOOK.afterValidateCU]() {
|
||||
if (this.form.pid !== null && this.form.pid === this.form.id) {
|
||||
this.$message({
|
||||
message: '上级部门不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (this.form.isTop === '1') {
|
||||
this.form.pid = null
|
||||
}
|
||||
return true
|
||||
},
|
||||
// 改变状态
|
||||
changeEnabled(data, val) {
|
||||
this.$confirm('此操作将 "' + this.dict.label.dept_status[val] + '" ' + data.name + '部门, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
crudDept.edit(data).then(res => {
|
||||
this.crud.notify(this.dict.label.dept_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(err => {
|
||||
data.enabled = !data.enabled
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
},
|
||||
checkboxT(row, rowIndex) {
|
||||
return row.id !== 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
360
src/views/govern/role/index.vue
Normal file
360
src/views/govern/role/index.vue
Normal file
@@ -0,0 +1,360 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.blurry" size="small" clearable placeholder="输入名称或者描述搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!-- 表单渲染 -->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="520px">
|
||||
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="角色名称" prop="name">
|
||||
<el-input v-model="form.name" style="width: 380px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色级别" prop="level">
|
||||
<el-input-number v-model.number="form.level" :min="1" controls-position="right" style="width: 145px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="数据范围" prop="dataScope">
|
||||
<el-select v-model="form.dataScope" style="width: 140px" placeholder="请选择数据范围" @change="changeScope">
|
||||
<el-option
|
||||
v-for="item in dateScopes"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.dataScope === '自定义'" label="数据权限" prop="depts">
|
||||
<treeselect
|
||||
v-model="deptDatas"
|
||||
:load-options="loadDepts"
|
||||
:options="depts"
|
||||
multiple
|
||||
style="width: 380px"
|
||||
placeholder="请选择"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述信息" prop="description">
|
||||
<el-input v-model="form.description" style="width: 380px;" rows="5" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-row :gutter="15">
|
||||
<!--角色管理-->
|
||||
<el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="17" style="margin-bottom: 10px">
|
||||
<el-card class="box-card" shadow="never">
|
||||
<div slot="header" class="clearfix">
|
||||
<span class="role-span">角色列表</span>
|
||||
</div>
|
||||
<el-table ref="table" v-loading="crud.loading" highlight-current-row style="width: 100%;" :data="crud.data" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column prop="name" label="名称" />
|
||||
<el-table-column prop="dataScope" label="数据权限" />
|
||||
<el-table-column prop="level" label="角色级别" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="description" label="描述" />
|
||||
<el-table-column :show-overflow-tooltip="true" width="135px" prop="createTime" label="创建日期" />
|
||||
<el-table-column v-if="checkPer(['admin','roles:edit','roles:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
v-if="scope.row.level >= level"
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<pagination />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<!-- 菜单授权 -->
|
||||
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="7">
|
||||
<el-card class="box-card" shadow="never">
|
||||
<div slot="header" class="clearfix">
|
||||
<el-tooltip class="item" effect="dark" content="选择指定角色分配菜单" placement="top">
|
||||
<span class="role-span">菜单分配</span>
|
||||
</el-tooltip>
|
||||
<el-button
|
||||
v-permission="['admin','roles:edit']"
|
||||
:disabled="!showButton"
|
||||
:loading="menuLoading"
|
||||
icon="el-icon-check"
|
||||
size="mini"
|
||||
style="float: right; padding: 6px 9px"
|
||||
type="primary"
|
||||
@click="saveMenu"
|
||||
>保存</el-button>
|
||||
</div>
|
||||
<el-tree
|
||||
ref="menu"
|
||||
lazy
|
||||
:data="menus"
|
||||
:default-checked-keys="menuIds"
|
||||
:load="getMenuDatas"
|
||||
:props="defaultProps"
|
||||
check-strictly
|
||||
accordion
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
@check="menuChange"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudRoles from '@/api/system/role'
|
||||
import { getDepts, getDeptSuperior } from '@/api/system/dept'
|
||||
import { getMenusTree, getChild } from '@/api/system/menu'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import pagination from '@crud/Pagination'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
const defaultForm = { id: null, name: null, depts: [], description: null, dataScope: '全部', level: 3 }
|
||||
export default {
|
||||
name: 'Role',
|
||||
components: { Treeselect, pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '角色', url: '/system/api/roles', sort: 'level,asc', crudMethod: { ...crudRoles }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
data() {
|
||||
return {
|
||||
defaultProps: { children: 'children', label: 'label', isLeaf: 'leaf' },
|
||||
dateScopes: ['全部', '本级', '自定义'], level: 3,
|
||||
currentId: 0, menuLoading: false, showButton: false,
|
||||
menus: [], menuIds: [], depts: [], deptDatas: [], // 多选时使用
|
||||
permission: {
|
||||
add: ['admin', 'roles:add'],
|
||||
edit: ['admin', 'roles:edit'],
|
||||
del: ['admin', 'roles:del']
|
||||
},
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
permission: [
|
||||
{ required: true, message: '请输入权限', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
crudRoles.getLevel().then(data => {
|
||||
this.level = data.level
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getMenuDatas(node, resolve) {
|
||||
setTimeout(() => {
|
||||
getMenusTree(node.data.id ? node.data.id : 0).then(res => {
|
||||
resolve(res)
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
[CRUD.HOOK.afterRefresh]() {
|
||||
this.$refs.menu.setCheckedKeys([])
|
||||
},
|
||||
// 新增前初始化部门信息
|
||||
[CRUD.HOOK.beforeToAdd](crud, form) {
|
||||
this.deptDatas = []
|
||||
form.menus = null
|
||||
},
|
||||
// 编辑前初始化自定义数据权限的部门信息
|
||||
[CRUD.HOOK.beforeToEdit](crud, form) {
|
||||
this.deptDatas = []
|
||||
if (form.dataScope === '自定义') {
|
||||
this.getSupDepts(form.depts)
|
||||
}
|
||||
const _this = this
|
||||
form.depts.forEach(function(dept) {
|
||||
_this.deptDatas.push(dept.id)
|
||||
})
|
||||
// 将角色的菜单清空,避免日志入库数据过长
|
||||
form.menus = null
|
||||
},
|
||||
// 提交前做的操作
|
||||
[CRUD.HOOK.afterValidateCU](crud) {
|
||||
if (crud.form.dataScope === '自定义' && this.deptDatas.length === 0) {
|
||||
this.$message({
|
||||
message: '自定义数据权限不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
} else if (crud.form.dataScope === '自定义') {
|
||||
const depts = []
|
||||
this.deptDatas.forEach(function(data) {
|
||||
const dept = { id: data }
|
||||
depts.push(dept)
|
||||
})
|
||||
crud.form.depts = depts
|
||||
} else {
|
||||
crud.form.depts = []
|
||||
}
|
||||
return true
|
||||
},
|
||||
// 触发单选
|
||||
handleCurrentChange(val) {
|
||||
if (val) {
|
||||
const _this = this
|
||||
// 清空菜单的选中
|
||||
this.$refs.menu.setCheckedKeys([])
|
||||
// 保存当前的角色id
|
||||
this.currentId = val.id
|
||||
// 初始化默认选中的key
|
||||
this.menuIds = []
|
||||
val.menus.forEach(function(data) {
|
||||
_this.menuIds.push(data.id)
|
||||
})
|
||||
this.showButton = true
|
||||
}
|
||||
},
|
||||
menuChange(menu) {
|
||||
// 获取该节点的所有子节点,id 包含自身
|
||||
getChild(menu.id).then(childIds => {
|
||||
// 判断是否在 menuIds 中,如果存在则删除,否则添加
|
||||
if (this.menuIds.indexOf(menu.id) !== -1) {
|
||||
for (let i = 0; i < childIds.length; i++) {
|
||||
const index = this.menuIds.indexOf(childIds[i])
|
||||
if (index !== -1) {
|
||||
this.menuIds.splice(index, 1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < childIds.length; i++) {
|
||||
const index = this.menuIds.indexOf(childIds[i])
|
||||
if (index === -1) {
|
||||
this.menuIds.push(childIds[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
this.$refs.menu.setCheckedKeys(this.menuIds)
|
||||
})
|
||||
},
|
||||
// 保存菜单
|
||||
saveMenu() {
|
||||
this.menuLoading = true
|
||||
const role = { id: this.currentId, menus: [] }
|
||||
// 得到已选中的 key 值
|
||||
this.menuIds.forEach(function(id) {
|
||||
const menu = { id: id }
|
||||
role.menus.push(menu)
|
||||
})
|
||||
crudRoles.editMenu(role).then(() => {
|
||||
this.crud.notify('保存成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
this.menuLoading = false
|
||||
this.update()
|
||||
}).catch(err => {
|
||||
this.menuLoading = false
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
},
|
||||
// 改变数据
|
||||
update() {
|
||||
// 无刷新更新 表格数据
|
||||
crudRoles.get(this.currentId).then(res => {
|
||||
for (let i = 0; i < this.crud.data.length; i++) {
|
||||
if (res.id === this.crud.data[i].id) {
|
||||
this.crud.data[i] = res
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
// 获取部门数据
|
||||
getDepts() {
|
||||
getDepts({ enabled: true }).then(res => {
|
||||
this.depts = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
},
|
||||
getSupDepts(depts) {
|
||||
const ids = []
|
||||
depts.forEach(dept => {
|
||||
ids.push(dept.id)
|
||||
})
|
||||
getDeptSuperior(ids).then(res => {
|
||||
const date = res.content
|
||||
this.buildDepts(date)
|
||||
this.depts = date
|
||||
})
|
||||
},
|
||||
buildDepts(depts) {
|
||||
depts.forEach(data => {
|
||||
if (data.children) {
|
||||
this.buildDepts(data.children)
|
||||
}
|
||||
if (data.hasChildren && !data.children) {
|
||||
data.children = null
|
||||
}
|
||||
})
|
||||
},
|
||||
// 获取弹窗内部门数据
|
||||
loadDepts({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
getDepts({ enabled: true, pid: parentNode.id }).then(res => {
|
||||
parentNode.children = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 200)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 如果数据权限为自定义则获取部门数据
|
||||
changeScope() {
|
||||
if (this.form.dataScope === '自定义') {
|
||||
this.getDepts()
|
||||
}
|
||||
},
|
||||
checkboxT(row) {
|
||||
return row.level >= this.level
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
.role-span {
|
||||
font-weight: bold;color: #303133;
|
||||
font-size: 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
::v-deep .vue-treeselect__multi-value{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
::v-deep .vue-treeselect__multi-value-item{
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
210
src/views/govern/timing/index.vue
Normal file
210
src/views/govern/timing/index.vue
Normal file
@@ -0,0 +1,210 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.jobName" clearable size="small" placeholder="输入任务名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission">
|
||||
<!-- 任务日志 -->
|
||||
<el-button
|
||||
slot="right"
|
||||
class="filter-item"
|
||||
size="mini"
|
||||
type="info"
|
||||
icon="el-icon-tickets"
|
||||
@click="doLog"
|
||||
>日志</el-button>
|
||||
</crudOperation>
|
||||
<Log ref="log" />
|
||||
</div>
|
||||
<!--Form表单-->
|
||||
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" append-to-body width="730px">
|
||||
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="100px">
|
||||
<el-form-item label="任务名称" prop="jobName">
|
||||
<el-input v-model="form.jobName" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任务描述" prop="description">
|
||||
<el-input v-model="form.description" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Bean名称" prop="beanName">
|
||||
<el-input v-model="form.beanName" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="执行方法" prop="methodName">
|
||||
<el-input v-model="form.methodName" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Cron表达式" prop="cronExpression">
|
||||
<el-input v-model="form.cronExpression" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="子任务ID">
|
||||
<el-input v-model="form.subTask" placeholder="多个用逗号隔开,按顺序执行" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任务负责人" prop="personInCharge">
|
||||
<el-input v-model="form.personInCharge" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="告警邮箱" prop="email">
|
||||
<el-input v-model="form.email" placeholder="多个邮箱用逗号隔开" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="失败后暂停">
|
||||
<el-radio-group v-model="form.pauseAfterFailure" style="width: 220px">
|
||||
<el-radio :label="true">是</el-radio>
|
||||
<el-radio :label="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="任务状态">
|
||||
<el-radio-group v-model="form.isPause" style="width: 220px">
|
||||
<el-radio :label="false">启用</el-radio>
|
||||
<el-radio :label="true">暂停</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="参数内容">
|
||||
<el-input v-model="form.params" style="width: 556px;" rows="4" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="id" label="任务ID" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="jobName" label="任务名称" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="beanName" label="Bean名称" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="methodName" label="执行方法" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="params" label="参数" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="cronExpression" label="cron表达式" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="isPause" width="90px" label="状态">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.isPause ? 'warning' : 'success'">{{ scope.row.isPause ? '已暂停' : '运行中' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" prop="description" width="150px" label="描述" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="createTime" width="136px" label="创建日期" />
|
||||
<el-table-column v-if="checkPer(['admin','timing:edit','timing:del'])" label="操作" width="170px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-permission="['admin','timing:edit']" size="mini" style="margin-right: 3px;" type="text" @click="crud.toEdit(scope.row)">编辑</el-button>
|
||||
<el-button v-permission="['admin','timing:edit']" style="margin-left: -2px" type="text" size="mini" @click="execute(scope.row.id)">执行</el-button>
|
||||
<el-button v-permission="['admin','timing:edit']" style="margin-left: 3px" type="text" size="mini" @click="updateStatus(scope.row.id,scope.row.isPause ? '恢复' : '暂停')">
|
||||
{{ scope.row.isPause ? '恢复' : '暂停' }}
|
||||
</el-button>
|
||||
<el-popover
|
||||
:ref="scope.row.id"
|
||||
v-permission="['admin','timing:del']"
|
||||
placement="top"
|
||||
width="200"
|
||||
>
|
||||
<p>确定停止并删除该任务吗?</p>
|
||||
<div style="text-align: right; margin: 0">
|
||||
<el-button size="mini" type="text" @click="$refs[scope.row.id].doClose()">取消</el-button>
|
||||
<el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.id)">确定</el-button>
|
||||
</div>
|
||||
<el-button slot="reference" type="text" size="mini">删除</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<pagination />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudJob from '@/api/system/timing'
|
||||
import Log from './log'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import pagination from '@crud/Pagination'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
const defaultForm = { id: null, jobName: null, subTask: null, beanName: null, methodName: null, params: null, cronExpression: null, pauseAfterFailure: true, isPause: false, personInCharge: null, email: null, description: null }
|
||||
export default {
|
||||
name: 'Timing',
|
||||
components: { Log, pagination, crudOperation, rrOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '定时任务', url: '/system/api/jobs', crudMethod: { ...crudJob }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
data() {
|
||||
return {
|
||||
delLoading: false,
|
||||
permission: {
|
||||
add: ['admin', 'timing:add'],
|
||||
edit: ['admin', 'timing:edit'],
|
||||
del: ['admin', 'timing:del']
|
||||
},
|
||||
rules: {
|
||||
jobName: [
|
||||
{ required: true, message: '请输入任务名称', trigger: 'blur' }
|
||||
],
|
||||
description: [
|
||||
{ required: true, message: '请输入任务描述', trigger: 'blur' }
|
||||
],
|
||||
beanName: [
|
||||
{ required: true, message: '请输入Bean名称', trigger: 'blur' }
|
||||
],
|
||||
methodName: [
|
||||
{ required: true, message: '请输入方法名称', trigger: 'blur' }
|
||||
],
|
||||
cronExpression: [
|
||||
{ required: true, message: '请输入Cron表达式', trigger: 'blur' }
|
||||
],
|
||||
personInCharge: [
|
||||
{ required: true, message: '请输入负责人名称', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 执行
|
||||
execute(id) {
|
||||
crudJob.execution(id).then(res => {
|
||||
this.crud.notify('执行成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(err => {
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
},
|
||||
// 改变状态
|
||||
updateStatus(id, status) {
|
||||
if (status === '恢复') {
|
||||
this.updateParams(id)
|
||||
}
|
||||
crudJob.updateIsPause(id).then(res => {
|
||||
this.crud.toQuery()
|
||||
this.crud.notify(status + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(err => {
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
},
|
||||
updateParams(id) {
|
||||
console.log(id)
|
||||
},
|
||||
delMethod(id) {
|
||||
this.delLoading = true
|
||||
crudJob.del([id]).then(() => {
|
||||
this.delLoading = false
|
||||
this.$refs[id].doClose()
|
||||
this.crud.dleChangePage(1)
|
||||
this.crud.delSuccessNotify()
|
||||
this.crud.toQuery()
|
||||
}).catch(() => {
|
||||
this.delLoading = false
|
||||
this.$refs[id].doClose()
|
||||
})
|
||||
},
|
||||
// 显示日志
|
||||
doLog() {
|
||||
this.$refs.log.dialog = true
|
||||
this.$refs.log.doInit()
|
||||
},
|
||||
checkboxT(row, rowIndex) {
|
||||
return row.id !== 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
104
src/views/govern/timing/log.vue
Normal file
104
src/views/govern/timing/log.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<el-dialog :visible.sync="dialog" append-to-body title="执行日志" width="88%">
|
||||
<!-- 搜索 -->
|
||||
<div class="head-container">
|
||||
<el-input v-model="query.jobName" clearable size="small" placeholder="输入任务名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select v-model="query.isSuccess" placeholder="日志状态" clearable size="small" class="filter-item" style="width: 110px" @change="toQuery">
|
||||
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
|
||||
</el-select>
|
||||
<el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="toQuery">搜索</el-button>
|
||||
<!-- 导出 -->
|
||||
<div style="display: inline-block;">
|
||||
<el-button
|
||||
:loading="downloadLoading"
|
||||
size="mini"
|
||||
class="filter-item"
|
||||
type="warning"
|
||||
icon="el-icon-download"
|
||||
@click="downloadMethod"
|
||||
>导出</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!--表格渲染-->
|
||||
<el-table v-loading="loading" :data="data" style="width: 100%;margin-top: -10px;">
|
||||
<el-table-column :show-overflow-tooltip="true" prop="jobName" label="任务名称" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="beanName" label="Bean名称" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="methodName" label="执行方法" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="params" width="120px" label="参数" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="cronExpression" label="cron表达式" />
|
||||
<el-table-column prop="createTime" label="异常详情" width="110px">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-show="scope.row.exceptionDetail" size="mini" type="text" @click="info(scope.row.exceptionDetail)">查看详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" align="center" prop="time" width="100px" label="耗时(毫秒)" />
|
||||
<el-table-column align="center" prop="isSuccess" width="80px" label="状态">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.isSuccess ? 'success' : 'danger'">{{ scope.row.isSuccess ? '成功' : '失败' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" prop="createTime" label="创建日期" />
|
||||
</el-table>
|
||||
<el-dialog :visible.sync="errorDialog" append-to-body title="异常详情" width="85%">
|
||||
<pre>{{ errorInfo }}</pre>
|
||||
</el-dialog>
|
||||
<!--分页组件-->
|
||||
<el-pagination
|
||||
:total="total"
|
||||
:current-page="page + 1"
|
||||
:page-size="6"
|
||||
style="margin-top:8px;"
|
||||
layout="total, prev, pager, next"
|
||||
@size-change="sizeChange"
|
||||
@current-change="pageChange"
|
||||
/>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crud from '@/mixins/crud'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
export default {
|
||||
components: { DateRangePicker },
|
||||
mixins: [crud],
|
||||
data() {
|
||||
return {
|
||||
title: '任务日志',
|
||||
errorInfo: '', errorDialog: false,
|
||||
enabledTypeOptions: [
|
||||
{ key: 'true', display_name: '成功' },
|
||||
{ key: 'false', display_name: '失败' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doInit() {
|
||||
this.$nextTick(() => {
|
||||
this.init()
|
||||
})
|
||||
},
|
||||
// 获取数据前设置好接口地址
|
||||
beforeInit() {
|
||||
this.url = '/system/api/jobs/logs'
|
||||
this.size = 6
|
||||
return true
|
||||
},
|
||||
// 异常详情
|
||||
info(errorInfo) {
|
||||
this.errorInfo = errorInfo
|
||||
this.errorDialog = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.java.hljs{
|
||||
color: #444;
|
||||
background: #ffffff !important;
|
||||
}
|
||||
::v-deep .el-dialog__body{
|
||||
padding: 0 20px 10px 20px !important;
|
||||
}
|
||||
</style>
|
||||
221
src/views/govern/user/center.vue
Normal file
221
src/views/govern/user/center.vue
Normal file
@@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="24" :md="8" :lg="6" :xl="5" style="margin-bottom: 10px">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>个人信息</span>
|
||||
</div>
|
||||
<div>
|
||||
<div style="text-align: center">
|
||||
<div class="el-upload">
|
||||
<img :src="user.avatarName ? baseApi + '/system/avatar/' + user.avatarName : Avatar" title="点击上传头像" class="avatar" @click="toggleShow">
|
||||
<myUpload
|
||||
v-model="show"
|
||||
:headers="headers"
|
||||
:url="updateAvatarApi"
|
||||
@crop-upload-success="cropUploadSuccess"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="user-info">
|
||||
<li><div style="height: 100%"><svg-icon icon-class="login" /> 登录账号<div class="user-right">{{ user.username }}</div></div></li>
|
||||
<li><svg-icon icon-class="user1" /> 用户昵称 <div class="user-right">{{ user.nickName }}</div></li>
|
||||
<li><svg-icon icon-class="dept" /> 所属部门 <div class="user-right"> {{ user.dept.name }}</div></li>
|
||||
<li><svg-icon icon-class="phone" /> 手机号码 <div class="user-right">{{ user.phone }}</div></li>
|
||||
<li><svg-icon icon-class="email" /> 用户邮箱 <div class="user-right">{{ user.email }}</div></li>
|
||||
<li>
|
||||
<svg-icon icon-class="anq" /> 安全设置
|
||||
<div class="user-right">
|
||||
<a @click="$refs.pass.dialog = true">修改密码</a>
|
||||
<a @click="$refs.email.dialog = true">修改邮箱</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="16" :lg="18" :xl="19">
|
||||
<!-- 用户资料 -->
|
||||
<el-card class="box-card">
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick">
|
||||
<el-tab-pane label="用户资料" name="first">
|
||||
<el-form ref="form" :model="form" :rules="rules" style="margin-top: 10px;" size="small" label-width="65px">
|
||||
<el-form-item label="昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" style="width: 35%" />
|
||||
<span style="color: #C0C0C0;margin-left: 10px;">用户昵称不作为登录使用</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input v-model="form.phone" style="width: 35%;" />
|
||||
<span style="color: #C0C0C0;margin-left: 10px;">手机号码不能重复</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-radio-group v-model="form.gender" style="width: 178px">
|
||||
<el-radio label="男">男</el-radio>
|
||||
<el-radio label="女">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="">
|
||||
<el-button :loading="saveLoading" size="mini" type="primary" @click="doSubmit">保存配置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<!-- 操作日志 -->
|
||||
<el-tab-pane label="操作日志" name="second">
|
||||
<el-table v-loading="loading" :data="data" style="width: 100%;">
|
||||
<el-table-column prop="description" label="行为" />
|
||||
<el-table-column prop="requestIp" label="IP" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="address" label="IP来源" />
|
||||
<el-table-column prop="browser" label="浏览器" />
|
||||
<el-table-column prop="time" label="请求耗时" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.time <= 300">{{ scope.row.time }}ms</el-tag>
|
||||
<el-tag v-else-if="scope.row.time <= 1000" type="warning">{{ scope.row.time }}ms</el-tag>
|
||||
<el-tag v-else type="danger">{{ scope.row.time }}ms</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="right"
|
||||
>
|
||||
<template slot="header">
|
||||
<div style="display:inline-block;float: right;cursor: pointer" @click="init">创建日期<i class="el-icon-refresh" style="margin-left: 40px" /></div>
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.createTime }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<el-pagination
|
||||
:total="total"
|
||||
:current-page="page + 1"
|
||||
style="margin-top: 8px;"
|
||||
layout="total, prev, pager, next, sizes"
|
||||
@size-change="sizeChange"
|
||||
@current-change="pageChange"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<updateEmail ref="email" :email="user.email" />
|
||||
<updatePass ref="pass" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import myUpload from 'vue-image-crop-upload'
|
||||
import { mapGetters } from 'vuex'
|
||||
import updatePass from './center/updatePass'
|
||||
import updateEmail from './center/updateEmail'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import store from '@/store'
|
||||
import { isvalidPhone } from '@/utils/validate'
|
||||
import crud from '@/mixins/crud'
|
||||
import { editUser } from '@/api/system/user'
|
||||
import Avatar from '@/assets/images/avatar.png'
|
||||
export default {
|
||||
name: 'Center',
|
||||
components: { updatePass, updateEmail, myUpload },
|
||||
mixins: [crud],
|
||||
data() {
|
||||
// 自定义验证
|
||||
const validPhone = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('请输入电话号码'))
|
||||
} else if (!isvalidPhone(value)) {
|
||||
callback(new Error('请输入正确的11位手机号码'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
show: false,
|
||||
Avatar: Avatar,
|
||||
activeName: 'first',
|
||||
saveLoading: false,
|
||||
headers: {
|
||||
'Authorization': getToken()
|
||||
},
|
||||
form: {},
|
||||
rules: {
|
||||
nickName: [
|
||||
{ required: true, message: '请输入用户昵称', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
phone: [
|
||||
{ required: true, trigger: 'blur', validator: validPhone }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'user',
|
||||
'updateAvatarApi',
|
||||
'baseApi'
|
||||
])
|
||||
},
|
||||
created() {
|
||||
this.form = { id: this.user.id, nickName: this.user.nickName, gender: this.user.gender, phone: this.user.phone }
|
||||
store.dispatch('GetInfo').then(() => {})
|
||||
},
|
||||
methods: {
|
||||
toggleShow() {
|
||||
this.show = !this.show
|
||||
},
|
||||
handleClick(tab, event) {
|
||||
if (tab.name === 'second') {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
beforeInit() {
|
||||
this.url = '/system/api/logs/user'
|
||||
return true
|
||||
},
|
||||
cropUploadSuccess(jsonData, field) {
|
||||
store.dispatch('GetInfo').then(() => {})
|
||||
},
|
||||
doSubmit() {
|
||||
if (this.$refs['form']) {
|
||||
this.$refs['form'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.saveLoading = true
|
||||
editUser(this.form).then(() => {
|
||||
this.editSuccessNotify()
|
||||
store.dispatch('GetInfo').then(() => {})
|
||||
this.saveLoading = false
|
||||
}).catch(() => {
|
||||
this.saveLoading = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
.avatar {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.user-info {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
li{
|
||||
border-bottom: 1px solid #F0F3F4;
|
||||
padding: 11px 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
.user-right {
|
||||
float: right;
|
||||
a{
|
||||
color: #317EF3;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
137
src/views/govern/user/center/updateEmail.vue
Normal file
137
src/views/govern/user/center/updateEmail.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<div style="display: inline-block;">
|
||||
<el-dialog :visible.sync="dialog" :close-on-click-modal="false" :before-close="cancel" :title="title" append-to-body width="475px" @close="cancel">
|
||||
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="88px">
|
||||
<el-form-item label="新邮箱" prop="email">
|
||||
<el-input v-model="form.email" auto-complete="on" style="width: 200px;" />
|
||||
<el-button :loading="codeLoading" :disabled="isDisabled" size="small" @click="sendCode">{{ buttonName }}</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="验证码" prop="code">
|
||||
<el-input v-model="form.code" style="width: 320px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="当前密码" prop="pass">
|
||||
<el-input v-model="form.pass" type="password" style="width: 320px;" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="cancel">取消</el-button>
|
||||
<el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
import { validEmail } from '@/utils/validate'
|
||||
import { updateEmail } from '@/api/system/user'
|
||||
import { resetEmail } from '@/api/system/code'
|
||||
export default {
|
||||
props: {
|
||||
email: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const validMail = (rule, value, callback) => {
|
||||
if (value === '' || value === null) {
|
||||
callback(new Error('新邮箱不能为空'))
|
||||
} else if (value === this.email) {
|
||||
callback(new Error('新邮箱不能与旧邮箱相同'))
|
||||
} else if (validEmail(value)) {
|
||||
callback()
|
||||
} else {
|
||||
callback(new Error('邮箱格式错误'))
|
||||
}
|
||||
}
|
||||
return {
|
||||
loading: false, dialog: false, title: '修改邮箱', form: { pass: '', email: '', code: '' },
|
||||
user: { email: '', password: '' }, codeLoading: false,
|
||||
buttonName: '获取验证码', isDisabled: false, time: 60,
|
||||
rules: {
|
||||
pass: [
|
||||
{ required: true, message: '当前密码不能为空', trigger: 'blur' }
|
||||
],
|
||||
email: [
|
||||
{ required: true, validator: validMail, trigger: 'blur' }
|
||||
],
|
||||
code: [
|
||||
{ required: true, message: '验证码不能为空', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cancel() {
|
||||
this.resetForm()
|
||||
},
|
||||
sendCode() {
|
||||
if (this.form.email && this.form.email !== this.email) {
|
||||
this.codeLoading = true
|
||||
this.buttonName = '验证码发送中'
|
||||
const _this = this
|
||||
resetEmail(this.form.email).then(res => {
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: '发送成功,验证码有效期5分钟',
|
||||
type: 'success'
|
||||
})
|
||||
this.codeLoading = false
|
||||
this.isDisabled = true
|
||||
this.buttonName = this.time-- + '秒后重新发送'
|
||||
this.timer = window.setInterval(function() {
|
||||
_this.buttonName = _this.time + '秒后重新发送'
|
||||
--_this.time
|
||||
if (_this.time < 0) {
|
||||
_this.buttonName = '重新发送'
|
||||
_this.time = 60
|
||||
_this.isDisabled = false
|
||||
window.clearInterval(_this.timer)
|
||||
}
|
||||
}, 1000)
|
||||
}).catch(err => {
|
||||
this.resetForm()
|
||||
this.codeLoading = false
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}
|
||||
},
|
||||
doSubmit() {
|
||||
this.$refs['form'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.loading = true
|
||||
updateEmail(this.form).then(res => {
|
||||
this.loading = false
|
||||
this.resetForm()
|
||||
this.$notify({
|
||||
title: '邮箱修改成功',
|
||||
type: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
store.dispatch('GetInfo').then(() => {})
|
||||
}).catch(err => {
|
||||
this.loading = false
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.dialog = false
|
||||
this.$refs['form'].resetFields()
|
||||
window.clearInterval(this.timer)
|
||||
this.time = 60
|
||||
this.buttonName = '获取验证码'
|
||||
this.isDisabled = false
|
||||
this.form = { pass: '', email: '', code: '' }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
95
src/views/govern/user/center/updatePass.vue
Normal file
95
src/views/govern/user/center/updatePass.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div style="display: inline-block">
|
||||
<el-dialog :visible.sync="dialog" :close-on-click-modal="false" :before-close="cancel" :title="title" append-to-body width="500px" @close="cancel">
|
||||
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="88px">
|
||||
<el-form-item label="旧密码" prop="oldPass">
|
||||
<el-input v-model="form.oldPass" type="password" auto-complete="on" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" prop="newPass">
|
||||
<el-input v-model="form.newPass" type="password" auto-complete="on" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="confirmPass">
|
||||
<el-input v-model="form.confirmPass" type="password" auto-complete="on" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="cancel">取消</el-button>
|
||||
<el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
import { updatePass } from '@/api/system/user'
|
||||
export default {
|
||||
data() {
|
||||
const confirmPass = (rule, value, callback) => {
|
||||
if (value) {
|
||||
if (this.form.newPass !== value) {
|
||||
callback(new Error('两次输入的密码不一致'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
} else {
|
||||
callback(new Error('请再次输入密码'))
|
||||
}
|
||||
}
|
||||
return {
|
||||
loading: false, dialog: false, title: '修改密码', form: { oldPass: '', newPass: '', confirmPass: '' },
|
||||
rules: {
|
||||
oldPass: [
|
||||
{ required: true, message: '请输入旧密码', trigger: 'blur' }
|
||||
],
|
||||
newPass: [
|
||||
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||
{ min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
confirmPass: [
|
||||
{ required: true, validator: confirmPass, trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cancel() {
|
||||
this.resetForm()
|
||||
},
|
||||
doSubmit() {
|
||||
this.$refs['form'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.loading = true
|
||||
updatePass(this.form).then(res => {
|
||||
this.resetForm()
|
||||
this.$notify({
|
||||
title: '密码修改成功,请重新登录',
|
||||
type: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
setTimeout(() => {
|
||||
store.dispatch('LogOut').then(() => {
|
||||
location.reload() // 为了重新实例化vue-router对象 避免bug
|
||||
})
|
||||
}, 1500)
|
||||
}).catch(err => {
|
||||
this.loading = false
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.dialog = false
|
||||
this.$refs['form'].resetFields()
|
||||
this.form = { oldPass: '', newPass: '', confirmPass: '' }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
484
src/views/govern/user/index.vue
Normal file
484
src/views/govern/user/index.vue
Normal file
@@ -0,0 +1,484 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<!--侧边部门数据-->
|
||||
<el-col :xs="9" :sm="6" :md="5" :lg="4" :xl="4">
|
||||
<div class="head-container">
|
||||
<el-input
|
||||
v-model="deptName"
|
||||
clearable
|
||||
size="small"
|
||||
placeholdfer="输入部门名称搜索"
|
||||
prefix-icon="el-icon-search"
|
||||
class="filter-item"
|
||||
@input="getDeptDatas"
|
||||
/>
|
||||
</div>
|
||||
<el-tree
|
||||
:data="deptDatas"
|
||||
:load="getDeptDatas"
|
||||
:props="defaultProps"
|
||||
:expand-on-click-node="false"
|
||||
lazy
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</el-col>
|
||||
<!--用户数据-->
|
||||
<el-col :xs="15" :sm="18" :md="19" :lg="20" :xl="20">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input
|
||||
v-model="query.blurry"
|
||||
clearable
|
||||
size="small"
|
||||
placeholder="输入名称或者邮箱搜索"
|
||||
style="width: 200px;"
|
||||
class="filter-item"
|
||||
@keyup.enter.native="crud.toQuery"
|
||||
/>
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select
|
||||
v-model="query.enabled"
|
||||
clearable
|
||||
size="small"
|
||||
placeholder="状态"
|
||||
class="filter-item"
|
||||
style="width: 90px"
|
||||
@change="crud.toQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in enabledTypeOptions"
|
||||
:key="item.key"
|
||||
:label="item.display_name"
|
||||
:value="item.key"
|
||||
/>
|
||||
</el-select>
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation show="" :permission="permission" />
|
||||
</div>
|
||||
<!--表单渲染-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="570px">
|
||||
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="66px">
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-input v-model="form.username" @keydown.native="keydown($event)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="电话" prop="phone">
|
||||
<el-input v-model.number="form.phone" />
|
||||
</el-form-item>
|
||||
<el-form-item label="昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" @keydown.native="keydown($event)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="form.email" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门" prop="dept.id">
|
||||
<treeselect
|
||||
v-model="form.dept.id"
|
||||
:options="depts"
|
||||
:load-options="loadDepts"
|
||||
style="width: 178px"
|
||||
placeholder="选择部门"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="岗位" prop="jobs">
|
||||
<el-select
|
||||
v-model="jobDatas"
|
||||
style="width: 178px"
|
||||
multiple
|
||||
placeholder="请选择"
|
||||
@remove-tag="deleteTag"
|
||||
@change="changeJob"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in jobs"
|
||||
:key="item.name"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-radio-group v-model="form.gender" style="width: 178px">
|
||||
<el-radio label="男">男</el-radio>
|
||||
<el-radio label="女">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="form.enabled" :disabled="form.id === user.id">
|
||||
<el-radio
|
||||
v-for="item in dict.user_status"
|
||||
:key="item.id"
|
||||
:label="item.value"
|
||||
>{{ item.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item style="margin-bottom: 0;" label="角色" prop="roles">
|
||||
<el-select
|
||||
v-model="roleDatas"
|
||||
style="width: 437px"
|
||||
multiple
|
||||
placeholder="请选择"
|
||||
@remove-tag="deleteTag"
|
||||
@change="changeRole"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in roles"
|
||||
:key="item.name"
|
||||
:disabled="level !== 1 && item.level <= level"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="username" label="用户名" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="nickName" label="昵称" />
|
||||
<el-table-column prop="gender" label="性别" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="phone" width="100" label="电话" />
|
||||
<el-table-column :show-overflow-tooltip="true" width="135" prop="email" label="邮箱" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="dept" label="部门">
|
||||
<template slot-scope="scope">
|
||||
<div>{{ scope.row.dept.name }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="enabled">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
:disabled="user.id === scope.row.id"
|
||||
active-color="#409EFF"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" prop="createTime" width="135" label="创建日期" />
|
||||
<el-table-column
|
||||
v-if="checkPer(['admin','user:edit','user:del'])"
|
||||
label="操作"
|
||||
width="115"
|
||||
align="center"
|
||||
fixed="right"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
:disabled-dle="scope.row.id === user.id"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<pagination />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudUser from '@/api/system/user'
|
||||
import { isvalidPhone } from '@/utils/validate'
|
||||
import { getDepts, getDeptSuperior } from '@/api/system/dept'
|
||||
import { getAll, getLevel } from '@/api/system/role'
|
||||
import { getAllJob } from '@/api/system/job'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import pagination from '@crud/Pagination'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import { mapGetters } from 'vuex'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
let userRoles = []
|
||||
let userJobs = []
|
||||
const defaultForm = { id: null, username: null, nickName: null, gender: '男', email: null, enabled: 'false', roles: [], jobs: [], dept: { id: null }, phone: null }
|
||||
export default {
|
||||
name: 'User',
|
||||
components: { Treeselect, crudOperation, rrOperation, udOperation, pagination, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '用户', url: '/system/api/users', crudMethod: { ...crudUser }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
// 数据字典
|
||||
dicts: ['user_status'],
|
||||
data() {
|
||||
// 自定义验证
|
||||
const validPhone = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('请输入电话号码'))
|
||||
} else if (!isvalidPhone(value)) {
|
||||
callback(new Error('请输入正确的11位手机号码'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
height: document.documentElement.clientHeight - 180 + 'px;',
|
||||
deptName: '', depts: [], deptDatas: [], jobs: [], level: 3, roles: [],
|
||||
jobDatas: [], roleDatas: [], // 多选时使用
|
||||
defaultProps: { children: 'children', label: 'name', isLeaf: 'leaf' },
|
||||
permission: {
|
||||
add: ['admin', 'user:add'],
|
||||
edit: ['admin', 'user:edit'],
|
||||
del: ['admin', 'user:del']
|
||||
},
|
||||
enabledTypeOptions: [
|
||||
{ key: 'true', display_name: '激活' },
|
||||
{ key: 'false', display_name: '锁定' }
|
||||
],
|
||||
rules: {
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
nickName: [
|
||||
{ required: true, message: '请输入用户昵称', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
email: [
|
||||
{ required: true, message: '请输入邮箱地址', trigger: 'blur' },
|
||||
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
|
||||
],
|
||||
phone: [
|
||||
{ required: true, trigger: 'blur', validator: validPhone }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'user'
|
||||
])
|
||||
},
|
||||
created() {
|
||||
this.crud.msg.add = '新增成功,默认密码:123456'
|
||||
},
|
||||
mounted: function() {
|
||||
const that = this
|
||||
window.onresize = function temp() {
|
||||
that.height = document.documentElement.clientHeight - 180 + 'px;'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 禁止输入空格
|
||||
keydown(e) {
|
||||
if (e.keyCode === 32) {
|
||||
e.returnValue = false
|
||||
}
|
||||
},
|
||||
changeRole(value) {
|
||||
userRoles = []
|
||||
value.forEach(function(data, index) {
|
||||
const role = { id: data }
|
||||
userRoles.push(role)
|
||||
})
|
||||
},
|
||||
changeJob(value) {
|
||||
userJobs = []
|
||||
value.forEach(function(data, index) {
|
||||
const job = { id: data }
|
||||
userJobs.push(job)
|
||||
})
|
||||
},
|
||||
deleteTag(value) {
|
||||
userRoles.forEach(function(data, index) {
|
||||
if (data.id === value) {
|
||||
userRoles.splice(index, value)
|
||||
}
|
||||
})
|
||||
},
|
||||
// 新增与编辑前做的操作
|
||||
[CRUD.HOOK.afterToCU](crud, form) {
|
||||
this.getRoles()
|
||||
if (form.id == null) {
|
||||
this.getDepts()
|
||||
} else {
|
||||
this.getSupDepts(form.dept.id)
|
||||
}
|
||||
this.getRoleLevel()
|
||||
this.getJobs()
|
||||
form.enabled = form.enabled.toString()
|
||||
},
|
||||
// 新增前将多选的值设置为空
|
||||
[CRUD.HOOK.beforeToAdd]() {
|
||||
this.jobDatas = []
|
||||
this.roleDatas = []
|
||||
},
|
||||
// 初始化编辑时候的角色与岗位
|
||||
[CRUD.HOOK.beforeToEdit](crud, form) {
|
||||
this.getJobs(this.form.dept.id)
|
||||
this.jobDatas = []
|
||||
this.roleDatas = []
|
||||
userRoles = []
|
||||
userJobs = []
|
||||
const _this = this
|
||||
form.roles.forEach(function(role, index) {
|
||||
_this.roleDatas.push(role.id)
|
||||
const rol = { id: role.id }
|
||||
userRoles.push(rol)
|
||||
})
|
||||
form.jobs.forEach(function(job, index) {
|
||||
_this.jobDatas.push(job.id)
|
||||
const data = { id: job.id }
|
||||
userJobs.push(data)
|
||||
})
|
||||
},
|
||||
// 提交前做的操作
|
||||
[CRUD.HOOK.afterValidateCU](crud) {
|
||||
if (!crud.form.dept.id) {
|
||||
this.$message({
|
||||
message: '部门不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
} else if (this.jobDatas.length === 0) {
|
||||
this.$message({
|
||||
message: '岗位不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
} else if (this.roleDatas.length === 0) {
|
||||
this.$message({
|
||||
message: '角色不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
crud.form.roles = userRoles
|
||||
crud.form.jobs = userJobs
|
||||
return true
|
||||
},
|
||||
// 获取左侧部门数据
|
||||
getDeptDatas(node, resolve) {
|
||||
const sort = 'id,desc'
|
||||
const params = { sort: sort }
|
||||
if (typeof node !== 'object') {
|
||||
if (node) {
|
||||
params['name'] = node
|
||||
}
|
||||
} else if (node.level !== 0) {
|
||||
params['pid'] = node.data.id
|
||||
}
|
||||
setTimeout(() => {
|
||||
getDepts(params).then(res => {
|
||||
if (resolve) {
|
||||
resolve(res.content)
|
||||
} else {
|
||||
this.deptDatas = res.content
|
||||
}
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
getDepts() {
|
||||
getDepts({ enabled: true }).then(res => {
|
||||
this.depts = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
},
|
||||
getSupDepts(deptId) {
|
||||
getDeptSuperior(deptId).then(res => {
|
||||
const date = res.content
|
||||
this.buildDepts(date)
|
||||
this.depts = date
|
||||
})
|
||||
},
|
||||
buildDepts(depts) {
|
||||
depts.forEach(data => {
|
||||
if (data.children) {
|
||||
this.buildDepts(data.children)
|
||||
}
|
||||
if (data.hasChildren && !data.children) {
|
||||
data.children = null
|
||||
}
|
||||
})
|
||||
},
|
||||
// 获取弹窗内部门数据
|
||||
loadDepts({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
getDepts({ enabled: true, pid: parentNode.id }).then(res => {
|
||||
parentNode.children = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 200)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 切换部门
|
||||
handleNodeClick(data) {
|
||||
if (data.pid === 0) {
|
||||
this.query.deptId = null
|
||||
} else {
|
||||
this.query.deptId = data.id
|
||||
}
|
||||
this.crud.toQuery()
|
||||
},
|
||||
// 改变状态
|
||||
changeEnabled(data, val) {
|
||||
this.$confirm('此操作将 "' + this.dict.label.user_status[val] + '" ' + data.username + ', 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
crudUser.edit(data).then(res => {
|
||||
this.crud.notify(this.dict.label.user_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
},
|
||||
// 获取弹窗内角色数据
|
||||
getRoles() {
|
||||
getAll().then(res => {
|
||||
this.roles = res
|
||||
}).catch(() => { })
|
||||
},
|
||||
// 获取弹窗内岗位数据
|
||||
getJobs() {
|
||||
getAllJob().then(res => {
|
||||
this.jobs = res.content
|
||||
}).catch(() => { })
|
||||
},
|
||||
// 获取权限级别
|
||||
getRoleLevel() {
|
||||
getLevel().then(res => {
|
||||
this.level = res.level
|
||||
}).catch(() => { })
|
||||
},
|
||||
checkboxT(row, rowIndex) {
|
||||
return row.id !== this.user.id
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
107
src/views/home.vue
Normal file
107
src/views/home.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div class="dashboard-container">
|
||||
<div class="dashboard-editor-container">
|
||||
<github-corner class="github-corner" />
|
||||
|
||||
<panel-group @handleSetLineChartData="handleSetLineChartData" />
|
||||
|
||||
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
|
||||
<line-chart :chart-data="lineChartData" />
|
||||
</el-row>
|
||||
<el-row :gutter="32">
|
||||
<el-col :xs="24" :sm="24" :lg="8">
|
||||
<div class="chart-wrapper">
|
||||
<radar-chart />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :lg="8">
|
||||
<div class="chart-wrapper">
|
||||
<pie-chart />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :lg="8">
|
||||
<div class="chart-wrapper">
|
||||
<bar-chart />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GithubCorner from '@/components/GithubCorner'
|
||||
import PanelGroup from './dashboard/PanelGroup'
|
||||
import LineChart from './dashboard/LineChart'
|
||||
import RadarChart from '@/components/Echarts/RadarChart'
|
||||
import PieChart from '@/components/Echarts/PieChart'
|
||||
import BarChart from '@/components/Echarts/BarChart'
|
||||
|
||||
const lineChartData = {
|
||||
newVisitis: {
|
||||
expectedData: [100, 120, 161, 134, 105, 160, 165],
|
||||
actualData: [120, 82, 91, 154, 162, 140, 145]
|
||||
},
|
||||
messages: {
|
||||
expectedData: [200, 192, 120, 144, 160, 130, 140],
|
||||
actualData: [180, 160, 151, 106, 145, 150, 130]
|
||||
},
|
||||
purchases: {
|
||||
expectedData: [80, 100, 121, 104, 105, 90, 100],
|
||||
actualData: [120, 90, 100, 138, 142, 130, 130]
|
||||
},
|
||||
shoppings: {
|
||||
expectedData: [130, 140, 141, 142, 145, 150, 160],
|
||||
actualData: [120, 82, 91, 154, 162, 140, 130]
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'Dashboard',
|
||||
components: {
|
||||
GithubCorner,
|
||||
PanelGroup,
|
||||
LineChart,
|
||||
RadarChart,
|
||||
PieChart,
|
||||
BarChart
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lineChartData: lineChartData.newVisitis
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSetLineChartData(type) {
|
||||
this.lineChartData = lineChartData[type]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.dashboard-editor-container {
|
||||
padding: 32px;
|
||||
background-color: rgb(240, 242, 245);
|
||||
position: relative;
|
||||
|
||||
.github-corner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
border: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.chart-wrapper {
|
||||
background: #fff;
|
||||
padding: 16px 16px 0;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width:1024px) {
|
||||
.chart-wrapper {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
254
src/views/lakehouse/dept/index.vue
Normal file
254
src/views/lakehouse/dept/index.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.name" clearable size="small" placeholder="输入部门名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
|
||||
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
|
||||
</el-select>
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表单组件-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
|
||||
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input v-model="form.name" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="部门排序" prop="deptSort">
|
||||
<el-input-number
|
||||
v-model.number="form.deptSort"
|
||||
:min="0"
|
||||
:max="999"
|
||||
controls-position="right"
|
||||
style="width: 370px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="顶级部门">
|
||||
<el-radio-group v-model="form.isTop" style="width: 140px">
|
||||
<el-radio label="1">是</el-radio>
|
||||
<el-radio label="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="enabled">
|
||||
<el-radio v-for="item in dict.dept_status" :key="item.id" v-model="form.enabled" :label="item.value">{{ item.label }}</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.isTop === '0'" style="margin-bottom: 0;" label="上级部门" prop="pid">
|
||||
<treeselect
|
||||
v-model="form.pid"
|
||||
:load-options="loadDepts"
|
||||
:options="depts"
|
||||
style="width: 370px;"
|
||||
placeholder="选择上级类目"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
lazy
|
||||
:load="getDeptDatas"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
:data="crud.data"
|
||||
row-key="id"
|
||||
@select="crud.selectChange"
|
||||
@select-all="crud.selectAllChange"
|
||||
@selection-change="crud.selectionChangeHandler"
|
||||
>
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column label="名称" prop="name" />
|
||||
<el-table-column label="排序" prop="deptSort" />
|
||||
<el-table-column label="状态" align="center" prop="enabled">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
:disabled="scope.row.id === 1"
|
||||
active-color="#409EFF"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled,)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" />
|
||||
<el-table-column v-if="checkPer(['admin','dept:edit','dept:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
:disabled-dle="scope.row.id === 1"
|
||||
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudDept from '@/api/system/dept'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
const defaultForm = { id: null, name: null, isTop: '1', subCount: 0, pid: null, deptSort: 999, enabled: 'true' }
|
||||
export default {
|
||||
name: 'Dept',
|
||||
components: { Treeselect, crudOperation, rrOperation, udOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '部门', url: '/system/api/dept', crudMethod: { ...crudDept }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
// 设置数据字典
|
||||
dicts: ['dept_status'],
|
||||
data() {
|
||||
return {
|
||||
depts: [],
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
deptSort: [
|
||||
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
|
||||
]
|
||||
},
|
||||
permission: {
|
||||
add: ['admin', 'dept:add'],
|
||||
edit: ['admin', 'dept:edit'],
|
||||
del: ['admin', 'dept:del']
|
||||
},
|
||||
enabledTypeOptions: [
|
||||
{ key: 'true', display_name: '正常' },
|
||||
{ key: 'false', display_name: '禁用' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDeptDatas(tree, treeNode, resolve) {
|
||||
const params = { pid: tree.id }
|
||||
setTimeout(() => {
|
||||
crudDept.getDepts(params).then(res => {
|
||||
resolve(res.content)
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
// 新增与编辑前做的操作
|
||||
[CRUD.HOOK.afterToCU](crud, form) {
|
||||
if (form.pid !== null) {
|
||||
form.isTop = '0'
|
||||
} else if (form.id !== null) {
|
||||
form.isTop = '1'
|
||||
}
|
||||
form.enabled = `${form.enabled}`
|
||||
if (form.id != null) {
|
||||
this.getSupDepts(form.id)
|
||||
} else {
|
||||
this.getDepts()
|
||||
}
|
||||
},
|
||||
getSupDepts(id) {
|
||||
crudDept.getDeptSuperior(id).then(res => {
|
||||
const date = res.content
|
||||
this.buildDepts(date)
|
||||
this.depts = date
|
||||
})
|
||||
},
|
||||
buildDepts(depts) {
|
||||
depts.forEach(data => {
|
||||
if (data.children) {
|
||||
this.buildDepts(data.children)
|
||||
}
|
||||
if (data.hasChildren && !data.children) {
|
||||
data.children = null
|
||||
}
|
||||
})
|
||||
},
|
||||
getDepts() {
|
||||
crudDept.getDepts({ enabled: true }).then(res => {
|
||||
this.depts = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取弹窗内部门数据
|
||||
loadDepts({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
crudDept.getDepts({ enabled: true, pid: parentNode.id }).then(res => {
|
||||
parentNode.children = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 提交前的验证
|
||||
[CRUD.HOOK.afterValidateCU]() {
|
||||
if (this.form.pid !== null && this.form.pid === this.form.id) {
|
||||
this.$message({
|
||||
message: '上级部门不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (this.form.isTop === '1') {
|
||||
this.form.pid = null
|
||||
}
|
||||
return true
|
||||
},
|
||||
// 改变状态
|
||||
changeEnabled(data, val) {
|
||||
this.$confirm('此操作将 "' + this.dict.label.dept_status[val] + '" ' + data.name + '部门, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
crudDept.edit(data).then(res => {
|
||||
this.crud.notify(this.dict.label.dept_status[val] + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(err => {
|
||||
data.enabled = !data.enabled
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
},
|
||||
checkboxT(row, rowIndex) {
|
||||
return row.id !== 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
115
src/views/lakehouse/dict/dictDetail.vue
Normal file
115
src/views/lakehouse/dict/dictDetail.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="query.dictName === ''">
|
||||
<div class="my-code">点击字典查看详情</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.label" clearable size="small" placeholder="输入字典标签查询" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
|
||||
<rrOperation />
|
||||
</div>
|
||||
</div>
|
||||
<!--表单组件-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title" width="500px">
|
||||
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="字典标签" prop="label">
|
||||
<el-input v-model="form.label" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="字典值" prop="value">
|
||||
<el-input v-model="form.value" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="dictSort">
|
||||
<el-input-number v-model.number="form.dictSort" :min="0" :max="999" controls-position="right" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%;" @selection-change="crud.selectionChangeHandler">
|
||||
<el-table-column label="所属字典">
|
||||
{{ query.dictName }}
|
||||
</el-table-column>
|
||||
<el-table-column prop="label" label="字典标签" />
|
||||
<el-table-column prop="value" label="字典值" />
|
||||
<el-table-column prop="dictSort" label="排序" />
|
||||
<el-table-column v-if="checkPer(['admin','dict:edit','dict:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<pagination />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudDictDetail from '@/api/system/dictDetail'
|
||||
import CRUD, { presenter, header, form } from '@crud/crud'
|
||||
import pagination from '@crud/Pagination'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
|
||||
const defaultForm = { id: null, label: null, value: null, dictSort: 999 }
|
||||
|
||||
export default {
|
||||
components: { pagination, rrOperation, udOperation },
|
||||
cruds() {
|
||||
return [
|
||||
CRUD({ title: '字典详情', url: '/system/api/dictDetail', query: { dictName: '' }, sort: ['dictSort,asc', 'id,desc'],
|
||||
crudMethod: { ...crudDictDetail },
|
||||
optShow: {
|
||||
add: true,
|
||||
edit: true,
|
||||
del: true,
|
||||
reset: false
|
||||
},
|
||||
queryOnPresenterCreated: false
|
||||
})
|
||||
]
|
||||
},
|
||||
mixins: [
|
||||
presenter(),
|
||||
header(),
|
||||
form(function() {
|
||||
return Object.assign({ dict: { id: this.dictId }}, defaultForm)
|
||||
})],
|
||||
data() {
|
||||
return {
|
||||
dictId: null,
|
||||
rules: {
|
||||
label: [
|
||||
{ required: true, message: '请输入字典标签', trigger: 'blur' }
|
||||
],
|
||||
value: [
|
||||
{ required: true, message: '请输入字典值', trigger: 'blur' }
|
||||
],
|
||||
dictSort: [
|
||||
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
|
||||
]
|
||||
},
|
||||
permission: {
|
||||
add: ['admin', 'dict:add'],
|
||||
edit: ['admin', 'dict:edit'],
|
||||
del: ['admin', 'dict:del']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
135
src/views/lakehouse/dict/index.vue
Normal file
135
src/views/lakehouse/dict/index.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--表单组件-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible="crud.status.cu > 0" :title="crud.status.title" width="500px">
|
||||
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="字典名称" prop="name">
|
||||
<el-input v-model="form.name" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="描述">
|
||||
<el-input v-model="form.description" style="width: 370px;" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!-- 字典列表 -->
|
||||
<el-row :gutter="10">
|
||||
<el-col :xs="24" :sm="24" :md="10" :lg="11" :xl="11" style="margin-bottom: 10px">
|
||||
<el-card class="box-card">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.blurry" clearable size="small" placeholder="输入名称或者描述搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表格渲染-->
|
||||
<el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row style="width: 100%;" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="name" label="名称" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="description" label="描述" />
|
||||
<el-table-column v-if="checkPer(['admin','dict:edit','dict:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<pagination />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<!-- 字典详情列表 -->
|
||||
<el-col :xs="24" :sm="24" :md="14" :lg="13" :xl="13">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>字典详情</span>
|
||||
<el-button
|
||||
v-if="checkPer(['admin','dict:add']) && this.$refs.dictDetail && this.$refs.dictDetail.query.dictName"
|
||||
class="filter-item"
|
||||
size="mini"
|
||||
style="float: right;padding: 4px 10px"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
@click="$refs.dictDetail && $refs.dictDetail.crud.toAdd()"
|
||||
>新增</el-button>
|
||||
</div>
|
||||
<dictDetail ref="dictDetail" :permission="permission" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import dictDetail from './dictDetail'
|
||||
import crudDict from '@/api/system/dict'
|
||||
import CRUD, { presenter, header, form } from '@crud/crud'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import pagination from '@crud/Pagination'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
|
||||
const defaultForm = { id: null, name: null, description: null, dictDetails: [] }
|
||||
|
||||
export default {
|
||||
name: 'Dict',
|
||||
components: { crudOperation, pagination, rrOperation, udOperation, dictDetail },
|
||||
cruds() {
|
||||
return [
|
||||
CRUD({ title: '字典', url: '/system/api/dict', crudMethod: { ...crudDict }})
|
||||
]
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm)],
|
||||
data() {
|
||||
return {
|
||||
queryTypeOptions: [
|
||||
{ key: 'name', display_name: '字典名称' },
|
||||
{ key: 'description', display_name: '描述' }
|
||||
],
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
permission: {
|
||||
add: ['admin', 'dict:add'],
|
||||
edit: ['admin', 'dict:edit'],
|
||||
del: ['admin', 'dict:del']
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取数据前设置好接口地址
|
||||
[CRUD.HOOK.beforeRefresh]() {
|
||||
if (this.$refs.dictDetail) {
|
||||
this.$refs.dictDetail.query.dictName = ''
|
||||
}
|
||||
return true
|
||||
},
|
||||
// 选中字典后,设置字典详情数据
|
||||
handleCurrentChange(val) {
|
||||
if (val) {
|
||||
this.$refs.dictDetail.query.dictName = val.name
|
||||
this.$refs.dictDetail.dictId = val.id
|
||||
this.$refs.dictDetail.crud.toQuery()
|
||||
}
|
||||
},
|
||||
// 编辑前将字典明细临时清空,避免日志入库数据过长
|
||||
[CRUD.HOOK.beforeToEdit](crud, form) {
|
||||
// 将角色的菜单清空,避免日志入库数据过长
|
||||
form.dictDetails = null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
110
src/views/lakehouse/job/index.vue
Normal file
110
src/views/lakehouse/job/index.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<eHeader :dict="dict" :permission="permission" />
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表格渲染-->
|
||||
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="name" label="名称" />
|
||||
<el-table-column prop="jobSort" label="排序">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.jobSort }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
active-color="#409EFF"
|
||||
inactive-color="#F56C6C"
|
||||
@change="changeEnabled(scope.row, scope.row.enabled)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" />
|
||||
<!-- 编辑与删除 -->
|
||||
<el-table-column
|
||||
v-if="checkPer(['admin','job:edit','job:del'])"
|
||||
label="操作"
|
||||
width="130px"
|
||||
align="center"
|
||||
fixed="right"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<pagination />
|
||||
<!--表单渲染-->
|
||||
<eForm :job-status="dict.job_status" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudJob from '@/api/system/job'
|
||||
import eHeader from './module/header'
|
||||
import eForm from './module/form'
|
||||
import CRUD, { presenter } from '@crud/crud'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import pagination from '@crud/Pagination'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
export default {
|
||||
name: 'Job',
|
||||
components: { eHeader, eForm, crudOperation, pagination, udOperation },
|
||||
cruds() {
|
||||
return CRUD({
|
||||
title: '岗位',
|
||||
url: '/system/api/job',
|
||||
sort: ['jobSort,asc', 'id,desc'],
|
||||
crudMethod: { ...crudJob }
|
||||
})
|
||||
},
|
||||
mixins: [presenter()],
|
||||
// 数据字典
|
||||
dicts: ['job_status'],
|
||||
data() {
|
||||
return {
|
||||
permission: {
|
||||
add: ['admin', 'job:add'],
|
||||
edit: ['admin', 'job:edit'],
|
||||
del: ['admin', 'job:del']
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 改变状态
|
||||
changeEnabled(data, val) {
|
||||
this.$confirm('此操作将 "' + this.dict.label.job_status[val] + '" ' + data.name + '岗位, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// eslint-disable-next-line no-undef
|
||||
crudJob.edit(data).then(() => {
|
||||
// eslint-disable-next-line no-undef
|
||||
this.crud.notify(this.dict.label.job_status[val] + '成功', 'success')
|
||||
}).catch(err => {
|
||||
data.enabled = !data.enabled
|
||||
console.log(err.data.message)
|
||||
})
|
||||
}).catch(() => {
|
||||
data.enabled = !data.enabled
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
110
src/views/lakehouse/job/module/form.vue
Normal file
110
src/views/lakehouse/job/module/form.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
:before-close="crud.cancelCU"
|
||||
:visible="crud.status.cu > 0"
|
||||
:title="crud.status.title"
|
||||
width="500px"
|
||||
>
|
||||
<el-form
|
||||
ref="form"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
size="small"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-form-item
|
||||
label="名称"
|
||||
prop="name"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.name"
|
||||
style="width: 370px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="排序"
|
||||
prop="jobSort"
|
||||
>
|
||||
<el-input-number
|
||||
v-model.number="form.jobSort"
|
||||
:min="0"
|
||||
:max="999"
|
||||
controls-position="right"
|
||||
style="width: 370px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="form.pid !== 0"
|
||||
label="状态"
|
||||
prop="enabled"
|
||||
>
|
||||
<el-radio
|
||||
v-for="item in jobStatus"
|
||||
:key="item.id"
|
||||
v-model="form.enabled"
|
||||
:label="item.value === 'true'"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div
|
||||
slot="footer"
|
||||
class="dialog-footer"
|
||||
>
|
||||
<el-button
|
||||
type="text"
|
||||
@click="crud.cancelCU"
|
||||
>
|
||||
取消
|
||||
</el-button>
|
||||
<el-button
|
||||
:loading="crud.status.cu === 2"
|
||||
type="primary"
|
||||
@click="crud.submitCU"
|
||||
>
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { form } from '@crud/crud'
|
||||
|
||||
const defaultForm = {
|
||||
id: null,
|
||||
name: '',
|
||||
jobSort: 999,
|
||||
enabled: true
|
||||
}
|
||||
export default {
|
||||
mixins: [form(defaultForm)],
|
||||
props: {
|
||||
jobStatus: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
jobSort: [
|
||||
{ required: true, message: '请输入序号', trigger: 'blur', type: 'number' }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
32
src/views/lakehouse/job/module/header.vue
Normal file
32
src/views/lakehouse/job/module/header.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="crud.props.searchToggle"
|
||||
>
|
||||
<el-input v-model="query.name" clearable size="small" placeholder="输入岗位名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select v-model="query.enabled" clearable size="small" placeholder="状态" class="filter-item" style="width: 90px" @change="crud.toQuery">
|
||||
<el-option v-for="item in dict.dict.job_status" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<rrOperation />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { header } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
export default {
|
||||
components: { rrOperation, DateRangePicker },
|
||||
mixins: [header()],
|
||||
props: {
|
||||
dict: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
permission: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
252
src/views/lakehouse/menu/index.vue
Normal file
252
src/views/lakehouse/menu/index.vue
Normal file
@@ -0,0 +1,252 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.blurry" clearable size="small" placeholder="模糊搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!--表单渲染-->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="580px">
|
||||
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="菜单类型" prop="type">
|
||||
<el-radio-group v-model="form.type" size="mini" style="width: 178px">
|
||||
<el-radio-button label="0">目录</el-radio-button>
|
||||
<el-radio-button label="1">菜单</el-radio-button>
|
||||
<el-radio-button label="2">按钮</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.type.toString() !== '2'" label="菜单图标" prop="icon">
|
||||
<el-popover
|
||||
placement="bottom-start"
|
||||
width="450"
|
||||
trigger="click"
|
||||
@show="$refs['iconSelect'].reset()"
|
||||
>
|
||||
<IconSelect ref="iconSelect" @selected="selected" />
|
||||
<el-input slot="reference" v-model="form.icon" style="width: 450px;" placeholder="点击选择图标" readonly>
|
||||
<svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" class="el-input__icon" style="height: 32px;width: 16px;" />
|
||||
<i v-else slot="prefix" class="el-icon-search el-input__icon" />
|
||||
</el-input>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.type.toString() !== '2'" label="外链菜单" prop="iFrame">
|
||||
<el-radio-group v-model="form.iFrame" size="mini">
|
||||
<el-radio-button label="true">是</el-radio-button>
|
||||
<el-radio-button label="false">否</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.type.toString() === '1'" label="菜单缓存" prop="cache">
|
||||
<el-radio-group v-model="form.cache" size="mini">
|
||||
<el-radio-button label="true">是</el-radio-button>
|
||||
<el-radio-button label="false">否</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.type.toString() !== '2'" label="菜单可见" prop="hidden">
|
||||
<el-radio-group v-model="form.hidden" size="mini">
|
||||
<el-radio-button label="false">是</el-radio-button>
|
||||
<el-radio-button label="true">否</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.type.toString() !== '2'" label="菜单标题" prop="title">
|
||||
<el-input v-model="form.title" :style=" form.type.toString() === '0' ? 'width: 450px' : 'width: 178px'" placeholder="菜单标题" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.type.toString() === '2'" label="按钮名称" prop="title">
|
||||
<el-input v-model="form.title" placeholder="按钮名称" style="width: 178px;" />
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.type.toString() !== '0'" label="权限标识" prop="permission">
|
||||
<el-input v-model="form.permission" :disabled="form.iFrame.toString() === 'true'" placeholder="权限标识" style="width: 178px;" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.type.toString() !== '2'" label="路由地址" prop="path">
|
||||
<el-input v-model="form.path" placeholder="路由地址" style="width: 178px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单排序" prop="menuSort">
|
||||
<el-input-number v-model.number="form.menuSort" :min="0" :max="999" controls-position="right" style="width: 178px;" />
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'" label="组件名称" prop="componentName">
|
||||
<el-input v-model="form.componentName" style="width: 178px;" placeholder="匹配组件内Name字段" />
|
||||
</el-form-item>
|
||||
<el-form-item v-show="form.iFrame.toString() !== 'true' && form.type.toString() === '1'" label="组件路径" prop="component">
|
||||
<el-input v-model="form.component" style="width: 178px;" placeholder="组件路径" />
|
||||
</el-form-item>
|
||||
<el-form-item label="上级类目" prop="pid">
|
||||
<treeselect
|
||||
v-model="form.pid"
|
||||
:options="menus"
|
||||
:load-options="loadMenus"
|
||||
style="width: 450px;"
|
||||
placeholder="选择上级类目"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="crud.loading"
|
||||
lazy
|
||||
:load="getMenus"
|
||||
:data="crud.data"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
row-key="id"
|
||||
@select="crud.selectChange"
|
||||
@select-all="crud.selectAllChange"
|
||||
@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 prop="icon" label="图标" align="center" width="60px">
|
||||
<template slot-scope="scope">
|
||||
<svg-icon :icon-class="scope.row.icon ? scope.row.icon : ''" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="menuSort" align="center" label="排序">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.menuSort }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" prop="permission" label="权限标识" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="component" label="组件路径" />
|
||||
<el-table-column prop="iFrame" label="外链" width="75px">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.iFrame">是</span>
|
||||
<span v-else>否</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="cache" label="缓存" width="75px">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.cache">是</span>
|
||||
<span v-else>否</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="hidden" label="可见" width="75px">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.hidden">否</span>
|
||||
<span v-else>是</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建日期" width="135px" />
|
||||
<el-table-column v-if="checkPer(['admin','menu:edit','menu:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
msg="确定删除吗,如果存在下级节点则一并删除,此操作不能撤销!"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudMenu from '@/api/system/menu'
|
||||
import IconSelect from '@/components/IconSelect'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
// crud交由presenter持有
|
||||
const defaultForm = { id: null, title: null, menuSort: 999, path: null, component: null, componentName: null, iFrame: false, roles: [], pid: 0, icon: null, cache: false, hidden: false, type: 0, permission: null }
|
||||
export default {
|
||||
name: 'Menu',
|
||||
components: { Treeselect, IconSelect, crudOperation, rrOperation, udOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '菜单', url: '/system/api/menus', crudMethod: { ...crudMenu }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
data() {
|
||||
return {
|
||||
menus: [],
|
||||
permission: {
|
||||
add: ['admin', 'menu:add'],
|
||||
edit: ['admin', 'menu:edit'],
|
||||
del: ['admin', 'menu:del']
|
||||
},
|
||||
rules: {
|
||||
title: [
|
||||
{ required: true, message: '请输入标题', trigger: 'blur' }
|
||||
],
|
||||
path: [
|
||||
{ required: true, message: '请输入地址', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 新增与编辑前做的操作
|
||||
[CRUD.HOOK.afterToCU](crud, form) {
|
||||
this.menus = []
|
||||
if (form.id != null) {
|
||||
if (form.pid === null) {
|
||||
form.pid = 0
|
||||
}
|
||||
this.getSupDepts(form.id)
|
||||
} else {
|
||||
this.menus.push({ id: 0, label: '顶级类目', children: null })
|
||||
}
|
||||
},
|
||||
getMenus(tree, treeNode, resolve) {
|
||||
const params = { pid: tree.id }
|
||||
setTimeout(() => {
|
||||
crudMenu.getMenus(params).then(res => {
|
||||
resolve(res.content)
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
getSupDepts(id) {
|
||||
crudMenu.getMenuSuperior(id).then(res => {
|
||||
const children = res.map(function(obj) {
|
||||
if (!obj.leaf && !obj.children) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
this.menus = [{ id: 0, label: '顶级类目', children: children }]
|
||||
})
|
||||
},
|
||||
loadMenus({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
crudMenu.getMenusTree(parentNode.id).then(res => {
|
||||
parentNode.children = res.map(function(obj) {
|
||||
if (!obj.leaf) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 选中图标
|
||||
selected(name) {
|
||||
this.form.icon = name
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
::v-deep .vue-treeselect__control,::v-deep .vue-treeselect__placeholder,::v-deep .vue-treeselect__single-value {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
</style>
|
||||
360
src/views/lakehouse/role/index.vue
Normal file
360
src/views/lakehouse/role/index.vue
Normal file
@@ -0,0 +1,360 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.blurry" size="small" clearable placeholder="输入名称或者描述搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission" />
|
||||
</div>
|
||||
<!-- 表单渲染 -->
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="520px">
|
||||
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="80px">
|
||||
<el-form-item label="角色名称" prop="name">
|
||||
<el-input v-model="form.name" style="width: 380px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色级别" prop="level">
|
||||
<el-input-number v-model.number="form.level" :min="1" controls-position="right" style="width: 145px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="数据范围" prop="dataScope">
|
||||
<el-select v-model="form.dataScope" style="width: 140px" placeholder="请选择数据范围" @change="changeScope">
|
||||
<el-option
|
||||
v-for="item in dateScopes"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.dataScope === '自定义'" label="数据权限" prop="depts">
|
||||
<treeselect
|
||||
v-model="deptDatas"
|
||||
:load-options="loadDepts"
|
||||
:options="depts"
|
||||
multiple
|
||||
style="width: 380px"
|
||||
placeholder="请选择"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述信息" prop="description">
|
||||
<el-input v-model="form.description" style="width: 380px;" rows="5" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-row :gutter="15">
|
||||
<!--角色管理-->
|
||||
<el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="17" style="margin-bottom: 10px">
|
||||
<el-card class="box-card" shadow="never">
|
||||
<div slot="header" class="clearfix">
|
||||
<span class="role-span">角色列表</span>
|
||||
</div>
|
||||
<el-table ref="table" v-loading="crud.loading" highlight-current-row style="width: 100%;" :data="crud.data" @selection-change="crud.selectionChangeHandler" @current-change="handleCurrentChange">
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column prop="name" label="名称" />
|
||||
<el-table-column prop="dataScope" label="数据权限" />
|
||||
<el-table-column prop="level" label="角色级别" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="description" label="描述" />
|
||||
<el-table-column :show-overflow-tooltip="true" width="135px" prop="createTime" label="创建日期" />
|
||||
<el-table-column v-if="checkPer(['admin','roles:edit','roles:del'])" label="操作" width="130px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<udOperation
|
||||
v-if="scope.row.level >= level"
|
||||
:data="scope.row"
|
||||
:permission="permission"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<pagination />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<!-- 菜单授权 -->
|
||||
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="7">
|
||||
<el-card class="box-card" shadow="never">
|
||||
<div slot="header" class="clearfix">
|
||||
<el-tooltip class="item" effect="dark" content="选择指定角色分配菜单" placement="top">
|
||||
<span class="role-span">菜单分配</span>
|
||||
</el-tooltip>
|
||||
<el-button
|
||||
v-permission="['admin','roles:edit']"
|
||||
:disabled="!showButton"
|
||||
:loading="menuLoading"
|
||||
icon="el-icon-check"
|
||||
size="mini"
|
||||
style="float: right; padding: 6px 9px"
|
||||
type="primary"
|
||||
@click="saveMenu"
|
||||
>保存</el-button>
|
||||
</div>
|
||||
<el-tree
|
||||
ref="menu"
|
||||
lazy
|
||||
:data="menus"
|
||||
:default-checked-keys="menuIds"
|
||||
:load="getMenuDatas"
|
||||
:props="defaultProps"
|
||||
check-strictly
|
||||
accordion
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
@check="menuChange"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudRoles from '@/api/system/role'
|
||||
import { getDepts, getDeptSuperior } from '@/api/system/dept'
|
||||
import { getMenusTree, getChild } from '@/api/system/menu'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import udOperation from '@crud/UD.operation'
|
||||
import pagination from '@crud/Pagination'
|
||||
import Treeselect from '@riophae/vue-treeselect'
|
||||
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
||||
import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
const defaultForm = { id: null, name: null, depts: [], description: null, dataScope: '全部', level: 3 }
|
||||
export default {
|
||||
name: 'Role',
|
||||
components: { Treeselect, pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '角色', url: '/system/api/roles', sort: 'level,asc', crudMethod: { ...crudRoles }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
data() {
|
||||
return {
|
||||
defaultProps: { children: 'children', label: 'label', isLeaf: 'leaf' },
|
||||
dateScopes: ['全部', '本级', '自定义'], level: 3,
|
||||
currentId: 0, menuLoading: false, showButton: false,
|
||||
menus: [], menuIds: [], depts: [], deptDatas: [], // 多选时使用
|
||||
permission: {
|
||||
add: ['admin', 'roles:add'],
|
||||
edit: ['admin', 'roles:edit'],
|
||||
del: ['admin', 'roles:del']
|
||||
},
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||
],
|
||||
permission: [
|
||||
{ required: true, message: '请输入权限', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
crudRoles.getLevel().then(data => {
|
||||
this.level = data.level
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getMenuDatas(node, resolve) {
|
||||
setTimeout(() => {
|
||||
getMenusTree(node.data.id ? node.data.id : 0).then(res => {
|
||||
resolve(res)
|
||||
})
|
||||
}, 100)
|
||||
},
|
||||
[CRUD.HOOK.afterRefresh]() {
|
||||
this.$refs.menu.setCheckedKeys([])
|
||||
},
|
||||
// 新增前初始化部门信息
|
||||
[CRUD.HOOK.beforeToAdd](crud, form) {
|
||||
this.deptDatas = []
|
||||
form.menus = null
|
||||
},
|
||||
// 编辑前初始化自定义数据权限的部门信息
|
||||
[CRUD.HOOK.beforeToEdit](crud, form) {
|
||||
this.deptDatas = []
|
||||
if (form.dataScope === '自定义') {
|
||||
this.getSupDepts(form.depts)
|
||||
}
|
||||
const _this = this
|
||||
form.depts.forEach(function(dept) {
|
||||
_this.deptDatas.push(dept.id)
|
||||
})
|
||||
// 将角色的菜单清空,避免日志入库数据过长
|
||||
form.menus = null
|
||||
},
|
||||
// 提交前做的操作
|
||||
[CRUD.HOOK.afterValidateCU](crud) {
|
||||
if (crud.form.dataScope === '自定义' && this.deptDatas.length === 0) {
|
||||
this.$message({
|
||||
message: '自定义数据权限不能为空',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
} else if (crud.form.dataScope === '自定义') {
|
||||
const depts = []
|
||||
this.deptDatas.forEach(function(data) {
|
||||
const dept = { id: data }
|
||||
depts.push(dept)
|
||||
})
|
||||
crud.form.depts = depts
|
||||
} else {
|
||||
crud.form.depts = []
|
||||
}
|
||||
return true
|
||||
},
|
||||
// 触发单选
|
||||
handleCurrentChange(val) {
|
||||
if (val) {
|
||||
const _this = this
|
||||
// 清空菜单的选中
|
||||
this.$refs.menu.setCheckedKeys([])
|
||||
// 保存当前的角色id
|
||||
this.currentId = val.id
|
||||
// 初始化默认选中的key
|
||||
this.menuIds = []
|
||||
val.menus.forEach(function(data) {
|
||||
_this.menuIds.push(data.id)
|
||||
})
|
||||
this.showButton = true
|
||||
}
|
||||
},
|
||||
menuChange(menu) {
|
||||
// 获取该节点的所有子节点,id 包含自身
|
||||
getChild(menu.id).then(childIds => {
|
||||
// 判断是否在 menuIds 中,如果存在则删除,否则添加
|
||||
if (this.menuIds.indexOf(menu.id) !== -1) {
|
||||
for (let i = 0; i < childIds.length; i++) {
|
||||
const index = this.menuIds.indexOf(childIds[i])
|
||||
if (index !== -1) {
|
||||
this.menuIds.splice(index, 1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < childIds.length; i++) {
|
||||
const index = this.menuIds.indexOf(childIds[i])
|
||||
if (index === -1) {
|
||||
this.menuIds.push(childIds[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
this.$refs.menu.setCheckedKeys(this.menuIds)
|
||||
})
|
||||
},
|
||||
// 保存菜单
|
||||
saveMenu() {
|
||||
this.menuLoading = true
|
||||
const role = { id: this.currentId, menus: [] }
|
||||
// 得到已选中的 key 值
|
||||
this.menuIds.forEach(function(id) {
|
||||
const menu = { id: id }
|
||||
role.menus.push(menu)
|
||||
})
|
||||
crudRoles.editMenu(role).then(() => {
|
||||
this.crud.notify('保存成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
this.menuLoading = false
|
||||
this.update()
|
||||
}).catch(err => {
|
||||
this.menuLoading = false
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
},
|
||||
// 改变数据
|
||||
update() {
|
||||
// 无刷新更新 表格数据
|
||||
crudRoles.get(this.currentId).then(res => {
|
||||
for (let i = 0; i < this.crud.data.length; i++) {
|
||||
if (res.id === this.crud.data[i].id) {
|
||||
this.crud.data[i] = res
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
// 获取部门数据
|
||||
getDepts() {
|
||||
getDepts({ enabled: true }).then(res => {
|
||||
this.depts = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
})
|
||||
},
|
||||
getSupDepts(depts) {
|
||||
const ids = []
|
||||
depts.forEach(dept => {
|
||||
ids.push(dept.id)
|
||||
})
|
||||
getDeptSuperior(ids).then(res => {
|
||||
const date = res.content
|
||||
this.buildDepts(date)
|
||||
this.depts = date
|
||||
})
|
||||
},
|
||||
buildDepts(depts) {
|
||||
depts.forEach(data => {
|
||||
if (data.children) {
|
||||
this.buildDepts(data.children)
|
||||
}
|
||||
if (data.hasChildren && !data.children) {
|
||||
data.children = null
|
||||
}
|
||||
})
|
||||
},
|
||||
// 获取弹窗内部门数据
|
||||
loadDepts({ action, parentNode, callback }) {
|
||||
if (action === LOAD_CHILDREN_OPTIONS) {
|
||||
getDepts({ enabled: true, pid: parentNode.id }).then(res => {
|
||||
parentNode.children = res.content.map(function(obj) {
|
||||
if (obj.hasChildren) {
|
||||
obj.children = null
|
||||
}
|
||||
return obj
|
||||
})
|
||||
setTimeout(() => {
|
||||
callback()
|
||||
}, 200)
|
||||
})
|
||||
}
|
||||
},
|
||||
// 如果数据权限为自定义则获取部门数据
|
||||
changeScope() {
|
||||
if (this.form.dataScope === '自定义') {
|
||||
this.getDepts()
|
||||
}
|
||||
},
|
||||
checkboxT(row) {
|
||||
return row.level >= this.level
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
.role-span {
|
||||
font-weight: bold;color: #303133;
|
||||
font-size: 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
::v-deep .vue-treeselect__multi-value{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
::v-deep .vue-treeselect__multi-value-item{
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
210
src/views/lakehouse/timing/index.vue
Normal file
210
src/views/lakehouse/timing/index.vue
Normal file
@@ -0,0 +1,210 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--工具栏-->
|
||||
<div class="head-container">
|
||||
<div v-if="crud.props.searchToggle">
|
||||
<!-- 搜索 -->
|
||||
<el-input v-model="query.jobName" clearable size="small" placeholder="输入任务名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<rrOperation />
|
||||
</div>
|
||||
<crudOperation :permission="permission">
|
||||
<!-- 任务日志 -->
|
||||
<el-button
|
||||
slot="right"
|
||||
class="filter-item"
|
||||
size="mini"
|
||||
type="info"
|
||||
icon="el-icon-tickets"
|
||||
@click="doLog"
|
||||
>日志</el-button>
|
||||
</crudOperation>
|
||||
<Log ref="log" />
|
||||
</div>
|
||||
<!--Form表单-->
|
||||
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" append-to-body width="730px">
|
||||
<el-form ref="form" :inline="true" :model="form" :rules="rules" size="small" label-width="100px">
|
||||
<el-form-item label="任务名称" prop="jobName">
|
||||
<el-input v-model="form.jobName" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任务描述" prop="description">
|
||||
<el-input v-model="form.description" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Bean名称" prop="beanName">
|
||||
<el-input v-model="form.beanName" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="执行方法" prop="methodName">
|
||||
<el-input v-model="form.methodName" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Cron表达式" prop="cronExpression">
|
||||
<el-input v-model="form.cronExpression" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="子任务ID">
|
||||
<el-input v-model="form.subTask" placeholder="多个用逗号隔开,按顺序执行" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任务负责人" prop="personInCharge">
|
||||
<el-input v-model="form.personInCharge" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="告警邮箱" prop="email">
|
||||
<el-input v-model="form.email" placeholder="多个邮箱用逗号隔开" style="width: 220px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="失败后暂停">
|
||||
<el-radio-group v-model="form.pauseAfterFailure" style="width: 220px">
|
||||
<el-radio :label="true">是</el-radio>
|
||||
<el-radio :label="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="任务状态">
|
||||
<el-radio-group v-model="form.isPause" style="width: 220px">
|
||||
<el-radio :label="false">启用</el-radio>
|
||||
<el-radio :label="true">暂停</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="参数内容">
|
||||
<el-input v-model="form.params" style="width: 556px;" rows="4" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="crud.cancelCU">取消</el-button>
|
||||
<el-button :loading="crud.status.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!--表格渲染-->
|
||||
<el-table ref="table" v-loading="crud.loading" :data="crud.data" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
|
||||
<el-table-column :selectable="checkboxT" type="selection" width="55" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="id" label="任务ID" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="jobName" label="任务名称" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="beanName" label="Bean名称" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="methodName" label="执行方法" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="params" label="参数" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="cronExpression" label="cron表达式" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="isPause" width="90px" label="状态">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.isPause ? 'warning' : 'success'">{{ scope.row.isPause ? '已暂停' : '运行中' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" prop="description" width="150px" label="描述" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="createTime" width="136px" label="创建日期" />
|
||||
<el-table-column v-if="checkPer(['admin','timing:edit','timing:del'])" label="操作" width="170px" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-permission="['admin','timing:edit']" size="mini" style="margin-right: 3px;" type="text" @click="crud.toEdit(scope.row)">编辑</el-button>
|
||||
<el-button v-permission="['admin','timing:edit']" style="margin-left: -2px" type="text" size="mini" @click="execute(scope.row.id)">执行</el-button>
|
||||
<el-button v-permission="['admin','timing:edit']" style="margin-left: 3px" type="text" size="mini" @click="updateStatus(scope.row.id,scope.row.isPause ? '恢复' : '暂停')">
|
||||
{{ scope.row.isPause ? '恢复' : '暂停' }}
|
||||
</el-button>
|
||||
<el-popover
|
||||
:ref="scope.row.id"
|
||||
v-permission="['admin','timing:del']"
|
||||
placement="top"
|
||||
width="200"
|
||||
>
|
||||
<p>确定停止并删除该任务吗?</p>
|
||||
<div style="text-align: right; margin: 0">
|
||||
<el-button size="mini" type="text" @click="$refs[scope.row.id].doClose()">取消</el-button>
|
||||
<el-button :loading="delLoading" type="primary" size="mini" @click="delMethod(scope.row.id)">确定</el-button>
|
||||
</div>
|
||||
<el-button slot="reference" type="text" size="mini">删除</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<pagination />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crudJob from '@/api/system/timing'
|
||||
import Log from './log'
|
||||
import CRUD, { presenter, header, form, crud } from '@crud/crud'
|
||||
import rrOperation from '@crud/RR.operation'
|
||||
import crudOperation from '@crud/CRUD.operation'
|
||||
import pagination from '@crud/Pagination'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
|
||||
const defaultForm = { id: null, jobName: null, subTask: null, beanName: null, methodName: null, params: null, cronExpression: null, pauseAfterFailure: true, isPause: false, personInCharge: null, email: null, description: null }
|
||||
export default {
|
||||
name: 'Timing',
|
||||
components: { Log, pagination, crudOperation, rrOperation, DateRangePicker },
|
||||
cruds() {
|
||||
return CRUD({ title: '定时任务', url: '/system/api/jobs', crudMethod: { ...crudJob }})
|
||||
},
|
||||
mixins: [presenter(), header(), form(defaultForm), crud()],
|
||||
data() {
|
||||
return {
|
||||
delLoading: false,
|
||||
permission: {
|
||||
add: ['admin', 'timing:add'],
|
||||
edit: ['admin', 'timing:edit'],
|
||||
del: ['admin', 'timing:del']
|
||||
},
|
||||
rules: {
|
||||
jobName: [
|
||||
{ required: true, message: '请输入任务名称', trigger: 'blur' }
|
||||
],
|
||||
description: [
|
||||
{ required: true, message: '请输入任务描述', trigger: 'blur' }
|
||||
],
|
||||
beanName: [
|
||||
{ required: true, message: '请输入Bean名称', trigger: 'blur' }
|
||||
],
|
||||
methodName: [
|
||||
{ required: true, message: '请输入方法名称', trigger: 'blur' }
|
||||
],
|
||||
cronExpression: [
|
||||
{ required: true, message: '请输入Cron表达式', trigger: 'blur' }
|
||||
],
|
||||
personInCharge: [
|
||||
{ required: true, message: '请输入负责人名称', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 执行
|
||||
execute(id) {
|
||||
crudJob.execution(id).then(res => {
|
||||
this.crud.notify('执行成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(err => {
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
},
|
||||
// 改变状态
|
||||
updateStatus(id, status) {
|
||||
if (status === '恢复') {
|
||||
this.updateParams(id)
|
||||
}
|
||||
crudJob.updateIsPause(id).then(res => {
|
||||
this.crud.toQuery()
|
||||
this.crud.notify(status + '成功', CRUD.NOTIFICATION_TYPE.SUCCESS)
|
||||
}).catch(err => {
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
},
|
||||
updateParams(id) {
|
||||
console.log(id)
|
||||
},
|
||||
delMethod(id) {
|
||||
this.delLoading = true
|
||||
crudJob.del([id]).then(() => {
|
||||
this.delLoading = false
|
||||
this.$refs[id].doClose()
|
||||
this.crud.dleChangePage(1)
|
||||
this.crud.delSuccessNotify()
|
||||
this.crud.toQuery()
|
||||
}).catch(() => {
|
||||
this.delLoading = false
|
||||
this.$refs[id].doClose()
|
||||
})
|
||||
},
|
||||
// 显示日志
|
||||
doLog() {
|
||||
this.$refs.log.dialog = true
|
||||
this.$refs.log.doInit()
|
||||
},
|
||||
checkboxT(row, rowIndex) {
|
||||
return row.id !== 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
104
src/views/lakehouse/timing/log.vue
Normal file
104
src/views/lakehouse/timing/log.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<el-dialog :visible.sync="dialog" append-to-body title="执行日志" width="88%">
|
||||
<!-- 搜索 -->
|
||||
<div class="head-container">
|
||||
<el-input v-model="query.jobName" clearable size="small" placeholder="输入任务名称搜索" style="width: 200px;" class="filter-item" @keyup.enter.native="toQuery" />
|
||||
<date-range-picker v-model="query.createTime" class="date-item" />
|
||||
<el-select v-model="query.isSuccess" placeholder="日志状态" clearable size="small" class="filter-item" style="width: 110px" @change="toQuery">
|
||||
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
|
||||
</el-select>
|
||||
<el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="toQuery">搜索</el-button>
|
||||
<!-- 导出 -->
|
||||
<div style="display: inline-block;">
|
||||
<el-button
|
||||
:loading="downloadLoading"
|
||||
size="mini"
|
||||
class="filter-item"
|
||||
type="warning"
|
||||
icon="el-icon-download"
|
||||
@click="downloadMethod"
|
||||
>导出</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!--表格渲染-->
|
||||
<el-table v-loading="loading" :data="data" style="width: 100%;margin-top: -10px;">
|
||||
<el-table-column :show-overflow-tooltip="true" prop="jobName" label="任务名称" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="beanName" label="Bean名称" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="methodName" label="执行方法" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="params" width="120px" label="参数" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="cronExpression" label="cron表达式" />
|
||||
<el-table-column prop="createTime" label="异常详情" width="110px">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-show="scope.row.exceptionDetail" size="mini" type="text" @click="info(scope.row.exceptionDetail)">查看详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" align="center" prop="time" width="100px" label="耗时(毫秒)" />
|
||||
<el-table-column align="center" prop="isSuccess" width="80px" label="状态">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.isSuccess ? 'success' : 'danger'">{{ scope.row.isSuccess ? '成功' : '失败' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" prop="createTime" label="创建日期" />
|
||||
</el-table>
|
||||
<el-dialog :visible.sync="errorDialog" append-to-body title="异常详情" width="85%">
|
||||
<pre>{{ errorInfo }}</pre>
|
||||
</el-dialog>
|
||||
<!--分页组件-->
|
||||
<el-pagination
|
||||
:total="total"
|
||||
:current-page="page + 1"
|
||||
:page-size="6"
|
||||
style="margin-top:8px;"
|
||||
layout="total, prev, pager, next"
|
||||
@size-change="sizeChange"
|
||||
@current-change="pageChange"
|
||||
/>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import crud from '@/mixins/crud'
|
||||
import DateRangePicker from '@/components/DateRangePicker'
|
||||
export default {
|
||||
components: { DateRangePicker },
|
||||
mixins: [crud],
|
||||
data() {
|
||||
return {
|
||||
title: '任务日志',
|
||||
errorInfo: '', errorDialog: false,
|
||||
enabledTypeOptions: [
|
||||
{ key: 'true', display_name: '成功' },
|
||||
{ key: 'false', display_name: '失败' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doInit() {
|
||||
this.$nextTick(() => {
|
||||
this.init()
|
||||
})
|
||||
},
|
||||
// 获取数据前设置好接口地址
|
||||
beforeInit() {
|
||||
this.url = '/system/api/jobs/logs'
|
||||
this.size = 6
|
||||
return true
|
||||
},
|
||||
// 异常详情
|
||||
info(errorInfo) {
|
||||
this.errorInfo = errorInfo
|
||||
this.errorDialog = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.java.hljs{
|
||||
color: #444;
|
||||
background: #ffffff !important;
|
||||
}
|
||||
::v-deep .el-dialog__body{
|
||||
padding: 0 20px 10px 20px !important;
|
||||
}
|
||||
</style>
|
||||
221
src/views/lakehouse/user/center.vue
Normal file
221
src/views/lakehouse/user/center.vue
Normal file
@@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="24" :sm="24" :md="8" :lg="6" :xl="5" style="margin-bottom: 10px">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>个人信息</span>
|
||||
</div>
|
||||
<div>
|
||||
<div style="text-align: center">
|
||||
<div class="el-upload">
|
||||
<img :src="user.avatarName ? baseApi + '/system/avatar/' + user.avatarName : Avatar" title="点击上传头像" class="avatar" @click="toggleShow">
|
||||
<myUpload
|
||||
v-model="show"
|
||||
:headers="headers"
|
||||
:url="updateAvatarApi"
|
||||
@crop-upload-success="cropUploadSuccess"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="user-info">
|
||||
<li><div style="height: 100%"><svg-icon icon-class="login" /> 登录账号<div class="user-right">{{ user.username }}</div></div></li>
|
||||
<li><svg-icon icon-class="user1" /> 用户昵称 <div class="user-right">{{ user.nickName }}</div></li>
|
||||
<li><svg-icon icon-class="dept" /> 所属部门 <div class="user-right"> {{ user.dept.name }}</div></li>
|
||||
<li><svg-icon icon-class="phone" /> 手机号码 <div class="user-right">{{ user.phone }}</div></li>
|
||||
<li><svg-icon icon-class="email" /> 用户邮箱 <div class="user-right">{{ user.email }}</div></li>
|
||||
<li>
|
||||
<svg-icon icon-class="anq" /> 安全设置
|
||||
<div class="user-right">
|
||||
<a @click="$refs.pass.dialog = true">修改密码</a>
|
||||
<a @click="$refs.email.dialog = true">修改邮箱</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="16" :lg="18" :xl="19">
|
||||
<!-- 用户资料 -->
|
||||
<el-card class="box-card">
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick">
|
||||
<el-tab-pane label="用户资料" name="first">
|
||||
<el-form ref="form" :model="form" :rules="rules" style="margin-top: 10px;" size="small" label-width="65px">
|
||||
<el-form-item label="昵称" prop="nickName">
|
||||
<el-input v-model="form.nickName" style="width: 35%" />
|
||||
<span style="color: #C0C0C0;margin-left: 10px;">用户昵称不作为登录使用</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input v-model="form.phone" style="width: 35%;" />
|
||||
<span style="color: #C0C0C0;margin-left: 10px;">手机号码不能重复</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-radio-group v-model="form.gender" style="width: 178px">
|
||||
<el-radio label="男">男</el-radio>
|
||||
<el-radio label="女">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="">
|
||||
<el-button :loading="saveLoading" size="mini" type="primary" @click="doSubmit">保存配置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<!-- 操作日志 -->
|
||||
<el-tab-pane label="操作日志" name="second">
|
||||
<el-table v-loading="loading" :data="data" style="width: 100%;">
|
||||
<el-table-column prop="description" label="行为" />
|
||||
<el-table-column prop="requestIp" label="IP" />
|
||||
<el-table-column :show-overflow-tooltip="true" prop="address" label="IP来源" />
|
||||
<el-table-column prop="browser" label="浏览器" />
|
||||
<el-table-column prop="time" label="请求耗时" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.time <= 300">{{ scope.row.time }}ms</el-tag>
|
||||
<el-tag v-else-if="scope.row.time <= 1000" type="warning">{{ scope.row.time }}ms</el-tag>
|
||||
<el-tag v-else type="danger">{{ scope.row.time }}ms</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="right"
|
||||
>
|
||||
<template slot="header">
|
||||
<div style="display:inline-block;float: right;cursor: pointer" @click="init">创建日期<i class="el-icon-refresh" style="margin-left: 40px" /></div>
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.createTime }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<el-pagination
|
||||
:total="total"
|
||||
:current-page="page + 1"
|
||||
style="margin-top: 8px;"
|
||||
layout="total, prev, pager, next, sizes"
|
||||
@size-change="sizeChange"
|
||||
@current-change="pageChange"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<updateEmail ref="email" :email="user.email" />
|
||||
<updatePass ref="pass" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import myUpload from 'vue-image-crop-upload'
|
||||
import { mapGetters } from 'vuex'
|
||||
import updatePass from './center/updatePass'
|
||||
import updateEmail from './center/updateEmail'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import store from '@/store'
|
||||
import { isvalidPhone } from '@/utils/validate'
|
||||
import crud from '@/mixins/crud'
|
||||
import { editUser } from '@/api/system/user'
|
||||
import Avatar from '@/assets/images/avatar.png'
|
||||
export default {
|
||||
name: 'Center',
|
||||
components: { updatePass, updateEmail, myUpload },
|
||||
mixins: [crud],
|
||||
data() {
|
||||
// 自定义验证
|
||||
const validPhone = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('请输入电话号码'))
|
||||
} else if (!isvalidPhone(value)) {
|
||||
callback(new Error('请输入正确的11位手机号码'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
show: false,
|
||||
Avatar: Avatar,
|
||||
activeName: 'first',
|
||||
saveLoading: false,
|
||||
headers: {
|
||||
'Authorization': getToken()
|
||||
},
|
||||
form: {},
|
||||
rules: {
|
||||
nickName: [
|
||||
{ required: true, message: '请输入用户昵称', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
phone: [
|
||||
{ required: true, trigger: 'blur', validator: validPhone }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'user',
|
||||
'updateAvatarApi',
|
||||
'baseApi'
|
||||
])
|
||||
},
|
||||
created() {
|
||||
this.form = { id: this.user.id, nickName: this.user.nickName, gender: this.user.gender, phone: this.user.phone }
|
||||
store.dispatch('GetInfo').then(() => {})
|
||||
},
|
||||
methods: {
|
||||
toggleShow() {
|
||||
this.show = !this.show
|
||||
},
|
||||
handleClick(tab, event) {
|
||||
if (tab.name === 'second') {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
beforeInit() {
|
||||
this.url = '/system/api/logs/user'
|
||||
return true
|
||||
},
|
||||
cropUploadSuccess(jsonData, field) {
|
||||
store.dispatch('GetInfo').then(() => {})
|
||||
},
|
||||
doSubmit() {
|
||||
if (this.$refs['form']) {
|
||||
this.$refs['form'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.saveLoading = true
|
||||
editUser(this.form).then(() => {
|
||||
this.editSuccessNotify()
|
||||
store.dispatch('GetInfo').then(() => {})
|
||||
this.saveLoading = false
|
||||
}).catch(() => {
|
||||
this.saveLoading = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
.avatar {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.user-info {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
li{
|
||||
border-bottom: 1px solid #F0F3F4;
|
||||
padding: 11px 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
.user-right {
|
||||
float: right;
|
||||
a{
|
||||
color: #317EF3;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
137
src/views/lakehouse/user/center/updateEmail.vue
Normal file
137
src/views/lakehouse/user/center/updateEmail.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<div style="display: inline-block;">
|
||||
<el-dialog :visible.sync="dialog" :close-on-click-modal="false" :before-close="cancel" :title="title" append-to-body width="475px" @close="cancel">
|
||||
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="88px">
|
||||
<el-form-item label="新邮箱" prop="email">
|
||||
<el-input v-model="form.email" auto-complete="on" style="width: 200px;" />
|
||||
<el-button :loading="codeLoading" :disabled="isDisabled" size="small" @click="sendCode">{{ buttonName }}</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="验证码" prop="code">
|
||||
<el-input v-model="form.code" style="width: 320px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="当前密码" prop="pass">
|
||||
<el-input v-model="form.pass" type="password" style="width: 320px;" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="text" @click="cancel">取消</el-button>
|
||||
<el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
import { validEmail } from '@/utils/validate'
|
||||
import { updateEmail } from '@/api/system/user'
|
||||
import { resetEmail } from '@/api/system/code'
|
||||
export default {
|
||||
props: {
|
||||
email: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const validMail = (rule, value, callback) => {
|
||||
if (value === '' || value === null) {
|
||||
callback(new Error('新邮箱不能为空'))
|
||||
} else if (value === this.email) {
|
||||
callback(new Error('新邮箱不能与旧邮箱相同'))
|
||||
} else if (validEmail(value)) {
|
||||
callback()
|
||||
} else {
|
||||
callback(new Error('邮箱格式错误'))
|
||||
}
|
||||
}
|
||||
return {
|
||||
loading: false, dialog: false, title: '修改邮箱', form: { pass: '', email: '', code: '' },
|
||||
user: { email: '', password: '' }, codeLoading: false,
|
||||
buttonName: '获取验证码', isDisabled: false, time: 60,
|
||||
rules: {
|
||||
pass: [
|
||||
{ required: true, message: '当前密码不能为空', trigger: 'blur' }
|
||||
],
|
||||
email: [
|
||||
{ required: true, validator: validMail, trigger: 'blur' }
|
||||
],
|
||||
code: [
|
||||
{ required: true, message: '验证码不能为空', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cancel() {
|
||||
this.resetForm()
|
||||
},
|
||||
sendCode() {
|
||||
if (this.form.email && this.form.email !== this.email) {
|
||||
this.codeLoading = true
|
||||
this.buttonName = '验证码发送中'
|
||||
const _this = this
|
||||
resetEmail(this.form.email).then(res => {
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: '发送成功,验证码有效期5分钟',
|
||||
type: 'success'
|
||||
})
|
||||
this.codeLoading = false
|
||||
this.isDisabled = true
|
||||
this.buttonName = this.time-- + '秒后重新发送'
|
||||
this.timer = window.setInterval(function() {
|
||||
_this.buttonName = _this.time + '秒后重新发送'
|
||||
--_this.time
|
||||
if (_this.time < 0) {
|
||||
_this.buttonName = '重新发送'
|
||||
_this.time = 60
|
||||
_this.isDisabled = false
|
||||
window.clearInterval(_this.timer)
|
||||
}
|
||||
}, 1000)
|
||||
}).catch(err => {
|
||||
this.resetForm()
|
||||
this.codeLoading = false
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
}
|
||||
},
|
||||
doSubmit() {
|
||||
this.$refs['form'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.loading = true
|
||||
updateEmail(this.form).then(res => {
|
||||
this.loading = false
|
||||
this.resetForm()
|
||||
this.$notify({
|
||||
title: '邮箱修改成功',
|
||||
type: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
store.dispatch('GetInfo').then(() => {})
|
||||
}).catch(err => {
|
||||
this.loading = false
|
||||
console.log(err.response.data.message)
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.dialog = false
|
||||
this.$refs['form'].resetFields()
|
||||
window.clearInterval(this.timer)
|
||||
this.time = 60
|
||||
this.buttonName = '获取验证码'
|
||||
this.isDisabled = false
|
||||
this.form = { pass: '', email: '', code: '' }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user