This commit is contained in:
tangzh 2025-05-11 20:57:12 +08:00
parent 1154eaab0b
commit 2d39c81ef7
307 changed files with 3096 additions and 13513 deletions

View File

@ -5,7 +5,7 @@
"author": "Zheng Jie", "author": "Zheng Jie",
"license": "Apache-2.0", "license": "Apache-2.0",
"scripts": { "scripts": {
"dev": "vue-cli-service serve", "dev": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve",
"build:prod": "vue-cli-service build", "build:prod": "vue-cli-service build",
"build:stage": "vue-cli-service build --mode staging", "build:stage": "vue-cli-service build --mode staging",
"preview": "node build/index.js --preview", "preview": "node build/index.js --preview",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 KiB

After

Width:  |  Height:  |  Size: 805 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 889 KiB

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div style="display: inline-block">
<el-button v-permission="permission.edit" :loading="crud.status.cu === 2" :disabled="disabledEdit" size="mini" type="primary" icon="el-icon-edit" @click.stop="crud.toEdit(data)" /> <el-button v-permission="permission.edit" :loading="crud.status.cu === 2" :disabled="disabledEdit" size="mini" type="primary" icon="el-icon-edit" @click.stop="crud.toEdit(data)" />
<el-popover v-model="pop" v-permission="permission.del" placement="top" width="180" trigger="manual" @show="onPopoverShow" @hide="onPopoverHide"> <el-popover v-model="pop" v-permission="permission.del" placement="top" width="180" trigger="manual" @show="onPopoverShow" @hide="onPopoverHide">
<p>{{ msg }}</p> <p>{{ msg }}</p>

View File

@ -6,6 +6,7 @@
import echarts from 'echarts' import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils' import { debounce } from '@/utils'
import { getPieChatData } from '@/api/dashboard/dashboard'
export default { export default {
props: { props: {
@ -24,7 +25,9 @@ export default {
}, },
data() { data() {
return { return {
chart: null chart: null,
dataList: [],
dataTitle: [],
} }
}, },
mounted() { mounted() {
@ -45,9 +48,22 @@ export default {
this.chart = null this.chart = null
}, },
methods: { methods: {
initChart() { async initChart() {
this.chart = echarts.init(this.$el, 'macarons') let iDataList = [];
let iDataTitle = [];
await getPieChatData().then(res => {
res.forEach(function (rItem) {
let name = rItem.text;
let value = rItem.value;
let item = { name: name, value: value }
iDataTitle.push(name)
iDataList.push(item)
});
})
this.dataList = iDataList;
this.dataTitle = iDataTitle;
this.chart = echarts.init(this.$el, 'macarons')
this.chart.setOption({ this.chart.setOption({
tooltip: { tooltip: {
trigger: 'item', trigger: 'item',
@ -56,29 +72,23 @@ export default {
legend: { legend: {
left: 'center', left: 'center',
bottom: '10', bottom: '10',
data: ['Industries', 'Technology', 'Forex', 'Gold', 'Forecasts'] data: this.dataTitle
}, },
calculable: true, calculable: true,
series: [ series: [
{ {
name: 'WEEKLY WRITE ARTICLES', name: '主治医生文档数',
type: 'pie', type: 'pie',
roseType: 'radius', roseType: 'radius',
radius: [15, 95], radius: [15, 95],
center: ['50%', '38%'], center: ['50%', '38%'],
data: [ data: this.dataList,
{ value: 320, name: 'Industries' },
{ value: 240, name: 'Technology' },
{ value: 149, name: 'Forex' },
{ value: 100, name: 'Gold' },
{ value: 59, name: 'Forecasts' }
],
animationEasing: 'cubicInOut', animationEasing: 'cubicInOut',
animationDuration: 2600 animationDuration: 2600
} }
] ]
}) })
} },
} }
} }
</script> </script>

View File

@ -8,17 +8,17 @@
<template v-if="device!=='mobile'"> <template v-if="device!=='mobile'">
<search id="header-search" class="right-menu-item" /> <search id="header-search" class="right-menu-item" />
<el-tooltip content="项目文档" effect="dark" placement="bottom"> <!-- <el-tooltip content="项目文档" effect="dark" placement="bottom">-->
<Doc class="right-menu-item hover-effect" /> <!-- <Doc class="right-menu-item hover-effect" />-->
</el-tooltip> <!-- </el-tooltip>-->
<el-tooltip content="全屏缩放" effect="dark" placement="bottom"> <el-tooltip content="全屏缩放" effect="dark" placement="bottom">
<screenfull id="screenfull" class="right-menu-item hover-effect" /> <screenfull id="screenfull" class="right-menu-item hover-effect" />
</el-tooltip> </el-tooltip>
<el-tooltip content="布局设置" effect="dark" placement="bottom"> <!-- <el-tooltip content="布局设置" effect="dark" placement="bottom">-->
<size-select id="size-select" class="right-menu-item hover-effect" /> <!-- <size-select id="size-select" class="right-menu-item hover-effect" />-->
</el-tooltip> <!-- </el-tooltip>-->
</template> </template>

View File

@ -25,7 +25,7 @@ export default {
}, },
data() { data() {
return { return {
title: 'ELADMIN-后台管理', title: '健康档案中心',
logo: Logo logo: Logo
} }
} }

View File

@ -2,7 +2,7 @@ module.exports = {
/** /**
* @description 网站标题 * @description 网站标题
*/ */
title: 'ELADMIN', title: '健康档案中心',
/** /**
* @description 是否显示 tagsView * @description 是否显示 tagsView
*/ */
@ -34,7 +34,7 @@ module.exports = {
/** /**
* 是否显示设置的底部信息 * 是否显示设置的底部信息
*/ */
showFooter: true, showFooter: false,
/** /**
* 底部文字支持html语法 * 底部文字支持html语法
*/ */

View File

@ -61,10 +61,10 @@ export default {
this.chart = echarts.init(this.$el, 'macarons') this.chart = echarts.init(this.$el, 'macarons')
this.setOptions(this.chartData) this.setOptions(this.chartData)
}, },
setOptions({ expectedData, actualData } = {}) { setOptions({ data } = {}) { // , actualData
this.chart.setOption({ this.chart.setOption({
xAxis: { xAxis: {
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], data: ['今天', '昨天', '前天', '大前天'],
boundaryGap: false, boundaryGap: false,
axisTick: { axisTick: {
show: false show: false
@ -90,10 +90,10 @@ export default {
} }
}, },
legend: { legend: {
data: ['expected', 'actual'] data: ['数量'] // ,'actual'
}, },
series: [{ series: [{
name: 'expected', itemStyle: { name: '数量', itemStyle: {
normal: { normal: {
color: '#FF005A', color: '#FF005A',
lineStyle: { lineStyle: {
@ -104,30 +104,31 @@ export default {
}, },
smooth: true, smooth: true,
type: 'line', type: 'line',
data: expectedData, data: data,
animationDuration: 2800, animationDuration: 2800,
animationEasing: 'cubicInOut' animationEasing: 'cubicInOut'
}, },
{ // {
name: 'actual', // name: 'actual',
smooth: true, // smooth: true,
type: 'line', // type: 'line',
itemStyle: { // itemStyle: {
normal: { // normal: {
color: '#3888fa', // color: '#3888fa',
lineStyle: { // lineStyle: {
color: '#3888fa', // color: '#3888fa',
width: 2 // width: 2
}, // },
areaStyle: { // areaStyle: {
color: '#f3f8ff' // color: '#f3f8ff'
} // }
} // }
}, // },
data: actualData, // data: actualData,
animationDuration: 2800, // animationDuration: 2800,
animationEasing: 'quadraticOut' // animationEasing: 'quadraticOut'
}] // }
]
}) })
} }
} }

View File

@ -1,54 +1,54 @@
<template> <template>
<el-row :gutter="40" class="panel-group"> <el-row :gutter="40" class="panel-group">
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('newVisitis')"> <div class="card-panel" @click="handleSetLineChartData('user')">
<div class="card-panel-icon-wrapper icon-people"> <div class="card-panel-icon-wrapper icon-people">
<svg-icon icon-class="peoples" class-name="card-panel-icon" /> <svg-icon icon-class="peoples" class-name="card-panel-icon" />
</div> </div>
<div class="card-panel-description"> <div class="card-panel-description">
<div class="card-panel-text"> <div class="card-panel-text">
New Visits 用户统计
</div> </div>
<count-to :start-val="0" :end-val="102400" :duration="2600" class="card-panel-num" /> <count-to :start-val="0" :end-val="groupData.user" :duration="2600" class="card-panel-num" />
</div> </div>
</div> </div>
</el-col> </el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('messages')"> <div class="card-panel" @click="handleSetLineChartData('dept')">
<div class="card-panel-icon-wrapper icon-message"> <div class="card-panel-icon-wrapper icon-message">
<svg-icon icon-class="message" class-name="card-panel-icon" /> <svg-icon icon-class="dept" class-name="card-panel-icon" />
</div> </div>
<div class="card-panel-description"> <div class="card-panel-description">
<div class="card-panel-text"> <div class="card-panel-text">
Messages 部门统计
</div> </div>
<count-to :start-val="0" :end-val="81212" :duration="3000" class="card-panel-num" /> <count-to :start-val="0" :end-val="groupData.dept" :duration="3000" class="card-panel-num" />
</div> </div>
</div> </div>
</el-col> </el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('purchases')"> <div class="card-panel" @click="handleSetLineChartData('role')">
<div class="card-panel-icon-wrapper icon-money"> <div class="card-panel-icon-wrapper icon-money">
<svg-icon icon-class="money" class-name="card-panel-icon" /> <svg-icon icon-class="role" class-name="card-panel-icon" />
</div> </div>
<div class="card-panel-description"> <div class="card-panel-description">
<div class="card-panel-text"> <div class="card-panel-text">
Purchases 角色统计
</div> </div>
<count-to :start-val="0" :end-val="9280" :duration="3200" class="card-panel-num" /> <count-to :start-val="0" :end-val="groupData.role" :duration="3200" class="card-panel-num" />
</div> </div>
</div> </div>
</el-col> </el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col"> <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('shoppings')"> <div class="card-panel" @click="handleSetLineChartData('law')">
<div class="card-panel-icon-wrapper icon-shopping"> <div class="card-panel-icon-wrapper icon-shopping">
<svg-icon icon-class="shopping" class-name="card-panel-icon" /> <svg-icon icon-class="server" class-name="card-panel-icon" />
</div> </div>
<div class="card-panel-description"> <div class="card-panel-description">
<div class="card-panel-text"> <div class="card-panel-text">
Shoppings 执法数据
</div> </div>
<count-to :start-val="0" :end-val="13600" :duration="3600" class="card-panel-num" /> <count-to :start-val="0" :end-val="groupData.law" :duration="3600" class="card-panel-num" />
</div> </div>
</div> </div>
</el-col> </el-col>
@ -57,15 +57,34 @@
<script> <script>
import CountTo from 'vue-count-to' import CountTo from 'vue-count-to'
import {getCardData} from '@/api/dashboard/dashboard'
export default { export default {
components: { components: {
CountTo CountTo
}, },
data() {
return {
groupData: {
"user": 0,
"dept": 0,
"role": 0,
"law": 0,
}
}
},
created() {
this.getGroup();
},
methods: { methods: {
getGroup() {
getCardData().then(res => {
this.groupData = res
})
},
handleSetLineChartData(type) { handleSetLineChartData(type) {
this.$emit('handleSetLineChartData', type) this.$emit('handleSetLineChartData', type)
} },
} }
} }
</script> </script>

View File

@ -1,79 +1,82 @@
<template> <template>
<div class="dashboard-container"> <div class="dashboard-container">
<div class="dashboard-editor-container"> <div class="dashboard-editor-container">
<github-corner class="github-corner" />
<panel-group @handleSetLineChartData="handleSetLineChartData" /> <panel-group @handleSetLineChartData="handleSetLineChartData" />
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;"> <el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
<line-chart :chart-data="lineChartData" /> <line-chart :chart-data="lineChartData" :chartData="lineChartData" />
</el-row> </el-row>
<el-row :gutter="32"> <el-row :gutter="32">
<el-col :xs="24" :sm="24" :lg="8"> <!-- <el-col :xs="24" :sm="24" :lg="8">-->
<div class="chart-wrapper"> <!-- <div class="chart-wrapper">-->
<radar-chart /> <!-- <radar-chart />-->
</div> <!-- </div>-->
</el-col> <!-- </el-col>-->
<el-col :xs="24" :sm="24" :lg="8"> <el-col :xs="24" :sm="24" :lg="24">
<div class="chart-wrapper"> <div class="chart-wrapper">
<pie-chart /> <pie-chart />
</div> </div>
</el-col> </el-col>
<el-col :xs="24" :sm="24" :lg="8"> <!-- <el-col :xs="24" :sm="24" :lg="12">-->
<div class="chart-wrapper"> <!-- <div class="chart-wrapper">-->
<bar-chart /> <!-- <bar-chart />-->
</div> <!-- </div>-->
</el-col> <!-- </el-col>-->
</el-row> </el-row>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import GithubCorner from '@/components/GithubCorner'
import PanelGroup from './dashboard/PanelGroup' import PanelGroup from './dashboard/PanelGroup'
import LineChart from './dashboard/LineChart' import LineChart from './dashboard/LineChart'
import RadarChart from '@/components/Echarts/RadarChart' // import RadarChart from '@/components/Echarts/RadarChart'
import PieChart from '@/components/Echarts/PieChart' import PieChart from '@/components/Echarts/PieChart'
import BarChart from '@/components/Echarts/BarChart' // import BarChart from '@/components/Echarts/BarChart'
import { getLineChart} from '@/api/dashboard/dashboard'
const lineChartData = { const lineChartData = {
newVisitis: { user: {
expectedData: [100, 120, 161, 134, 105, 160, 165], data: [100, 120, 161, 134, 105, 160, 165],
actualData: [120, 82, 91, 154, 162, 140, 145] // actualData: [120, 82, 91, 154, 162, 140, 145]
}, },
messages: { dept: {
expectedData: [200, 192, 120, 144, 160, 130, 140], data: [200, 192, 120, 144, 160, 130, 140],
actualData: [180, 160, 151, 106, 145, 150, 130] // actualData: [180, 160, 151, 106, 145, 150, 130]
}, },
purchases: { role: {
expectedData: [80, 100, 121, 104, 105, 90, 100], data: [80, 100, 121, 104, 105, 90, 100],
actualData: [120, 90, 100, 138, 142, 130, 130] // actualData: [120, 90, 100, 138, 142, 130, 130]
}, },
shoppings: { law: {
expectedData: [130, 140, 141, 142, 145, 150, 160], data: [130, 140, 141, 142, 145, 150, 160],
actualData: [120, 82, 91, 154, 162, 140, 130] // actualData: [120, 82, 91, 154, 162, 140, 130]
} }
} }
export default { export default {
name: 'Dashboard', name: 'Dashboard',
components: { components: {
GithubCorner,
PanelGroup, PanelGroup,
LineChart, LineChart,
RadarChart, // RadarChart,
PieChart, PieChart,
BarChart // BarChart
}, },
data() { data() {
return { return {
lineChartData: lineChartData.newVisitis lineChartData: lineChartData.user
} }
}, },
created() {
this.handleSetLineChartData("user");
},
methods: { methods: {
handleSetLineChartData(type) { handleSetLineChartData(type) {
this.lineChartData = lineChartData[type] getLineChart(type).then(res => {
this.lineChartData.data = res
})
} }
} }
} }

View File

@ -2,7 +2,7 @@
<div class="login" :style="'background-image:url('+ Background +');'"> <div class="login" :style="'background-image:url('+ Background +');'">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" label-position="left" label-width="0px" class="login-form"> <el-form ref="loginForm" :model="loginForm" :rules="loginRules" label-position="left" label-width="0px" class="login-form">
<h3 class="title"> <h3 class="title">
ELADMIN 后台管理系统 健康档案管理中心
</h3> </h3>
<el-form-item prop="username"> <el-form-item prop="username">
<el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号"> <el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号">
@ -186,6 +186,7 @@ export default {
background: #ffffff; background: #ffffff;
width: 385px; width: 385px;
padding: 25px 25px 5px 25px; padding: 25px 25px 5px 25px;
opacity: 0.9;
.el-input { .el-input {
height: 38px; height: 38px;
input { input {

View File

@ -1,144 +0,0 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.name" 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">
<el-button
slot="left"
v-permission="['admin','app:add']"
:disabled="!currentRow"
class="filter-item"
size="mini"
type="primary"
icon="el-icon-plus"
@click="copy"
>复制</el-button>
</crudOperation>
</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="800px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="100px">
<el-form-item label="应用名称" prop="name">
<el-input v-model="form.name" style="width: 670px" placeholder="部署后的文件或者目录名称,用于备份" />
</el-form-item>
<el-form-item label="应用端口" prop="port">
<el-input-number v-model.number="form.port" placeholder="例如8080" />
</el-form-item>
<el-form-item label="上传目录" prop="uploadPath">
<el-input v-model="form.uploadPath" style="width: 670px" placeholder="例如: /opt/upload" />
</el-form-item>
<el-form-item label="部署目录" prop="deployPath">
<el-input v-model="form.deployPath" style="width: 670px" placeholder="例如: /opt/app" />
</el-form-item>
<el-form-item label="备份目录" prop="backupPath">
<el-input v-model="form.backupPath" style="width: 670px" placeholder="例如: /opt/backup" />
</el-form-item>
<el-form-item label="部署脚本" prop="deployScript">
<el-input v-model="form.deployScript" :rows="3" type="textarea" autosize style="width: 670px" placeholder="" />
</el-form-item>
<el-form-item label="启动脚本" prop="startScript">
<el-input v-model="form.startScript" :rows="3" type="textarea" autosize style="width: 670px" 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" :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 prop="name" label="应用名称" />
<el-table-column prop="port" label="端口号" />
<el-table-column prop="uploadPath" label="上传目录" />
<el-table-column prop="deployPath" label="部署目录" />
<el-table-column prop="backupPath" label="备份目录" />
<el-table-column prop="createTime" label="创建日期" />
<el-table-column v-if="checkPer(['admin','app:edit','app:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import crudApp from '@/api/maint/app'
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'
const defaultForm = { id: null, name: null, port: 8080, uploadPath: '/opt/upload', deployPath: '/opt/app', backupPath: '/opt/backup', startScript: null, deployScript: null }
export default {
name: 'App',
components: { pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
cruds() {
return CRUD({ title: '应用', url: 'api/app', crudMethod: { ...crudApp }})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
currentRow: null,
permission: {
add: ['admin', 'app:add'],
edit: ['admin', 'app:edit'],
del: ['admin', 'app:del']
},
rules: {
name: [
{ required: true, message: '请输入应用名称', trigger: 'blur' }
],
port: [
{ required: true, message: '请输入应用端口', trigger: 'blur', type: 'number' }
],
uploadPath: [
{ required: true, message: '请输入上传目录', trigger: 'blur' }
],
deployPath: [
{ required: true, message: '请输入部署目录', trigger: 'blur' }
],
backupPath: [
{ required: true, message: '请输入备份目录', trigger: 'blur' }
],
startScript: [
{ required: true, message: '请输入启动脚本', trigger: 'blur' }
],
deployScript: [
{ required: true, message: '请输入部署脚本', trigger: 'blur' }
]
}
}
},
methods: {
copy() {
for (const key in this.currentRow) {
this.form[key] = this.currentRow[key]
}
this.form.id = null
this.form.createTime = null
this.crud.toAdd()
},
handleCurrentChange(row) {
this.currentRow = JSON.parse(JSON.stringify(row))
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,86 +0,0 @@
<template>
<el-dialog append-to-body :close-on-click-modal="false" :visible.sync="dialog" title="执行脚本" width="400px">
<el-form ref="form" :rules="rules" size="small">
<el-upload
:action="databaseUploadApi"
:data="databaseInfo"
:headers="headers"
:on-success="handleSuccess"
:on-error="handleError"
class="upload-demo"
drag
>
<i class="el-icon-upload" />
<div class="el-upload__text">
将文件拖到此处
<em>点击上传</em>
</div>
<div slot="tip" class="el-upload__tip">上传后系统会自动执行SQL脚本</div>
</el-upload>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="cancel">关闭</el-button>
</div>
</el-dialog>
</template>
<script>
import { mapGetters } from 'vuex'
import { getToken } from '@/utils/auth'
export default {
props: {
databaseInfo: {
type: Object,
default() {
return {}
}
}
},
data() {
return {
loading: false,
dialog: false,
headers: {
Authorization: getToken()
},
rules: {}
}
},
computed: {
...mapGetters(['databaseUploadApi'])
},
mounted() {
},
methods: {
cancel() {
this.dialog = false
},
handleSuccess(response, file, fileList) {
if (response === 'success') {
this.$notify({
title: '执行成功',
type: 'success',
duration: 2500
})
} else {
this.$notify({
title: response,
type: 'error',
duration: 0
})
}
},
handleError(e, file, fileList) {
const msg = JSON.parse(e.message)
this.$notify({
title: msg.message,
type: 'error',
duration: 0
})
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,148 +0,0 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.blurry" 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">
<el-button
slot="right"
v-permission="['admin','database:add']"
:disabled="!selectIndex"
class="filter-item"
size="mini"
type="warning"
icon="el-icon-upload"
@click="execute"
>执行脚本
</el-button>
</crudOperation>
</div>
<!--表单组件-->
<eForm ref="execute" :database-info="currentRow" />
<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="530px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="100px">
<el-form-item label="连接名称" prop="name">
<el-input v-model="form.name" style="width: 370px" />
</el-form-item>
<el-form-item label="JDBC地址" prop="jdbcUrl">
<el-input v-model="form.jdbcUrl" style="width: 300px" />
<el-button :loading="loading" type="success" @click="testConnectDatabase">测试</el-button>
</el-form-item>
<el-form-item label="用户" prop="userName">
<el-input v-model="form.userName" style="width: 370px" />
</el-form-item>
<el-form-item label="密码" prop="pwd">
<el-input v-model="form.pwd" type="password" 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 stripe style="width: 100%" @selection-change="handleCurrentChange">
<el-table-column type="selection" width="55" />
<el-table-column prop="name" width="130px" label="数据库名称" />
<el-table-column prop="jdbcUrl" label="连接地址" />
<el-table-column prop="userName" width="200px" label="用户名" />
<el-table-column prop="createTime" width="200px" label="创建日期" />
<el-table-column v-if="checkPer(['admin','database:edit','database:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import crudDatabase from '@/api/maint/database'
import { testDbConnect } from '@/api/maint/connect'
import eForm from './execute'
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'
const defaultForm = { id: null, name: null, jdbcUrl: 'jdbc:mysql://', userName: null, pwd: null }
export default {
name: 'DataBase',
components: { eForm, pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
cruds() {
return CRUD({ title: '数据库', url: 'api/database', crudMethod: { ...crudDatabase }})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
currentRow: {},
selectIndex: '',
databaseInfo: '',
loading: false,
permission: {
add: ['admin', 'database:add'],
edit: ['admin', 'database:edit'],
del: ['admin', 'database:del']
},
rules: {
name: [
{ required: true, message: '请输入数据库名称', trigger: 'blur' }
],
jdbcUrl: [
{ required: true, message: '请输入数据库连接地址', trigger: 'blur' }
],
userName: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
pwd: [
{ required: true, message: '请输入数据库密码', trigger: 'blur' }
]
}
}
},
methods: {
testConnectDatabase() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.loading = true
testDbConnect(this.form).then((res) => {
this.loading = false
this.crud.notify(res ? '连接成功' : '连接失败', res ? 'success' : 'error')
}).catch(() => {
this.loading = false
})
}
})
},
execute() {
this.$refs.execute.dialog = true
},
handleCurrentChange(selection) {
this.crud.selections = selection
if (selection.length === 1) {
const row = selection[0]
this.selectIndex = row.id
this.currentRow = row
} else {
this.currentRow = {}
this.selectIndex = ''
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,190 +0,0 @@
<template>
<el-dialog append-to-body :close-on-click-modal="false" :visible.sync="dialog" title="应用部署" width="400px">
<el-form ref="form" :model="form" :rules="rules" size="small">
<el-upload
:action="deployUploadApi"
:data="deployInfo"
:headers="headers"
:on-success="handleSuccess"
:on-error="handleError"
class="upload-demo"
drag
>
<i class="el-icon-upload" />
<div class="el-upload__text">
将文件拖到此处
<em>点击上传</em>
</div>
<div slot="tip" class="el-upload__tip">多个应用上传文件名称为all.zip,数据库更新脚本扩展名为.sql,上传成功后系统自动部署系统</div>
</el-upload>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="cancel">关闭</el-button>
</div>
</el-dialog>
</template>
<script>
import { add, edit, getApps, getServers } from '@/api/maint/deploy'
import { mapGetters } from 'vuex'
import { getToken } from '@/utils/auth'
export default {
props: {},
data() {
return {
loading: false,
dialog: false,
apps: [],
servers: [],
headers: {
Authorization: getToken()
},
deployInfo: {},
form: {
id: '',
appId: '',
ip: '',
selectIp: []
},
rules: {}
}
},
computed: {
...mapGetters(['deployUploadApi'])
},
created() {
this.initWebSocket()
},
mounted() {
this.initSelect()
},
methods: {
cancel() {
this.resetForm()
},
doSubmit() {
this.loading = true
if (this.isAdd) {
this.doAdd()
} else {
this.doEdit()
}
},
joinIp() {
this.form.ip = ''
this.form.selectIp.forEach(ip => {
if (this.form.ip !== '') {
this.form.ip += ','
}
this.form.ip += ip
})
},
doAdd() {
this.joinIp()
add(this.form)
.then(res => {
this.resetForm()
this.$notify({
title: '添加成功',
type: 'success',
duration: 2500
})
this.loading = false
this.$parent.init()
})
.catch(err => {
this.loading = false
console.log(err.response.data.message)
})
},
doEdit() {
this.joinIp()
edit(this.form)
.then(res => {
this.resetForm()
this.$notify({
title: '修改成功',
type: 'success',
duration: 2500
})
this.loading = false
this.$parent.init()
})
.catch(err => {
this.loading = false
console.log(err.response.data.message)
})
},
resetForm() {
this.dialog = false
this.$refs['form'].resetFields()
this.form = {
id: '',
appId: '',
ip: '',
selectIp: []
}
},
initSelect() {
getApps().then(res => {
this.apps = res.content
})
getServers().then(res => {
this.servers = res.content
})
},
handleSuccess(response, file, fileList) {
this.cancel()
},
//
handleError(e, file, fileList) {
const msg = JSON.parse(e.message)
this.$notify({
title: msg.message,
type: 'error',
duration: 2500
})
},
initWebSocket() {
const wsUri = (process.env.VUE_APP_WS_API === '/' ? '/' : (process.env.VUE_APP_WS_API + '/')) + 'webSocket/deploy'
this.websock = new WebSocket(wsUri)
this.websock.onerror = this.webSocketOnError
this.websock.onmessage = this.webSocketOnMessage
},
webSocketOnError(e) {
this.$notify({
title: 'WebSocket连接发生错误',
type: 'error',
duration: 0
})
},
webSocketOnMessage(e) {
const data = JSON.parse(e.data)
if (data.msgType === 'INFO') {
this.$notify({
title: '',
message: data.msg,
type: 'success',
dangerouslyUseHTMLString: true,
duration: 5500
})
} else if (data.msgType === 'ERROR') {
this.$notify({
title: '',
message: data.msg,
dangerouslyUseHTMLString: true,
type: 'error',
duration: 0
})
}
},
webSocketSend(agentData) {
this.websock.send(agentData)
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,229 +0,0 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.appName" 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">
<template slot="right">
<el-button
v-permission="['admin','deploy:add']"
:disabled="!selectIndex"
class="filter-item"
size="mini"
type="primary"
icon="el-icon-upload"
@click="sysRestore"
>系统还原
</el-button>
<el-button
v-permission="['admin','deploy:add']"
:disabled="!selectIndex"
class="filter-item"
size="mini"
type="primary"
icon="el-icon-upload"
@click="serverStatus"
>状态查询
</el-button>
<el-button
v-permission="['admin','deploy:add']"
:disabled="!selectIndex"
class="filter-item"
size="mini"
type="success"
icon="el-icon-upload"
@click="startServer"
>启动
</el-button>
<el-button
v-permission="['admin','deploy:add']"
:disabled="!selectIndex"
class="filter-item"
size="mini"
type="danger"
icon="el-icon-upload"
@click="stopServer"
>停止
</el-button>
<el-button
v-permission="['admin','deploy:add']"
:disabled="!selectIndex"
class="filter-item"
size="mini"
type="warning"
icon="el-icon-upload"
@click="deploy"
>一键部署
</el-button>
</template>
</crudOperation>
</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" :model="form" :rules="rules" size="small" label-width="80px">
<el-form-item label="应用" prop="app.id">
<el-select v-model.number="form.app.id" placeholder="请选择" style="width: 370px">
<el-option v-for="item in apps" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="服务器" prop="deploys">
<el-select v-model="form.deploys" multiple placeholder="请选择" style="width: 370px">
<el-option v-for="item in servers" :key="item.id" :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>
<!--统还原组件-->
<fForm ref="sysRestore" :key="times" :app-name="appName" />
<dForm ref="deploy" />
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" highlight-current-row stripe style="width: 100%" @selection-change="handleCurrentChange">
<el-table-column type="selection" width="55" />
<el-table-column prop="app.name" label="应用名称" />
<el-table-column prop="servers" label="服务器列表" />
<el-table-column prop="createTime" label="部署日期" />
<el-table-column v-if="checkPer(['admin','deploy:edit','deploy:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import crudDeploy from '@/api/maint/deploy'
import dForm from './deploy'
import fForm from './sysRestore'
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'
const defaultForm = { id: null, app: { id: null }, deploys: [] }
export default {
name: 'Deploy',
components: { dForm, fForm, pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
cruds() {
return CRUD({ title: '部署', url: 'api/deploy', crudMethod: { ...crudDeploy }})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
currentRow: {}, selectIndex: '', appName: '', urlHistory: '',
times: 0, appId: '', deployId: '', apps: [], servers: [],
permission: {
add: ['admin', 'deploy:add'],
edit: ['admin', 'deploy:edit'],
del: ['admin', 'deploy:del']
},
rules: {
'app.id': [
{ required: true, message: '应用不能为空', trigger: 'blur', type: 'number' }
],
deploys: [
{ required: true, message: '服务器不能为空', trigger: 'blur' }
]
}
}
},
methods: {
[CRUD.HOOK.beforeRefresh]() {
this.selectIndex = ''
return true
},
//
[CRUD.HOOK.beforeToCU](crud, form) {
this.initSelect()
const deploys = []
form.deploys.forEach(function(deploy, index) {
deploys.push(deploy.id)
})
this.form.deploys = deploys
},
//
[CRUD.HOOK.beforeSubmit]() {
const deploys = []
this.form.deploys.forEach(function(data, index) {
const deploy = { id: data }
deploys.push(deploy)
})
this.form.deploys = deploys
return true
},
deploy() {
this.$refs.deploy.dialog = true
this.$refs.deploy.deployInfo = this.currentRow
},
sysRestore() {
this.$refs.sysRestore.dialog = true
},
handleCurrentChange(selection) {
this.crud.selections = selection
if (selection.length === 1) {
const row = selection[0]
this.selectIndex = row.id
this.currentRow = row
this.appName = row.app.name
this.times = this.times + 1
this.appId = row.appId
this.deployId = row.id
} else {
this.currentRow = {}
this.selectIndex = ''
}
},
startServer() {
crudDeploy.startServer(JSON.stringify(this.currentRow))
.then(res => {
})
.catch(err => {
console.log('error:' + err.response.data.message)
})
},
stopServer() {
crudDeploy.stopServer(JSON.stringify(this.currentRow))
.then(res => {
})
.catch(err => {
console.log('error:' + err.response.data.message)
})
},
serverStatus() {
crudDeploy.serverStatus(JSON.stringify(this.currentRow))
.then(res => {
})
.catch(err => {
console.log('error:' + err.response.data.message)
})
},
initSelect() {
crudDeploy.getApps().then(res => {
this.apps = res.content
})
crudDeploy.getServers().then(res => {
this.servers = res.content
})
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,107 +0,0 @@
<template>
<el-dialog append-to-body :close-on-click-modal="false" :visible.sync="dialog" title="系统还原" width="800px">
<!--工具栏-->
<div class="head-container">
<date-range-picker v-model="query.createTime" class="date-item" />
<el-button class="filter-item" size="mini" type="success" icon="el-icon-search" @click="toQuery">搜索</el-button>
</div>
<el-form size="small" label-width="80px">
<!--表格渲染-->
<el-table v-loading="loading" :data="data" style="width: 100%" @row-click="showRow">
<el-table-column width="30px">
<template slot-scope="scope">
<el-radio v-model="radio" :label="scope.$index" />
</template>
</el-table-column>
<el-table-column prop="appName" label="应用名称" />
<el-table-column prop="ip" label="部署IP" />
<el-table-column prop="deployDate" label="部署时间" />
<el-table-column prop="deployUser" label="部署人员" />
</el-table>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="cancel">取消</el-button>
<el-button v-permission="['admin','deploy:add']" :loading="submitLoading" type="primary" @click="doSubmit">确认</el-button>
</div>
<!--分页组件-->
<el-pagination
:total="total"
:current-page="page"
style="margin-top: 8px"
layout="total, prev, pager, next, sizes"
@size-change="sizeChange"
@current-change="pageChange"
/>
</el-dialog>
</template>
<script>
import crud from '@/mixins/crud'
import { reducte } from '@/api/maint/deployHistory'
import DateRangePicker from '@/components/DateRangePicker'
export default {
components: { DateRangePicker },
mixins: [crud],
props: {
appName: {
type: String,
default: ''
}
},
data() {
return {
submitLoading: false,
dialog: false,
history: [],
radio: '',
appNames: '',
selectIndex: ''
}
},
created() {
this.$nextTick(() => {
this.init()
})
},
methods: {
beforeInit() {
this.url = 'api/deployHistory'
this.deployId = this.$parent.deployId
if (this.deployId === '') {
return false
}
this.params['deployId'] = this.deployId
return true
},
showRow(row) {
this.radio = this.data.indexOf(row)
this.selectIndex = row.id
},
cancel() {
this.dialog = false
this.submitLoading = false
},
doSubmit() {
if (this.selectIndex === '') {
this.$message.error('请选择要还原的备份')
} else {
this.submitLoading = true
reducte(JSON.stringify(this.data[this.radio]))
.then(res => {
this.dialog = false
this.submitLoading = false
this.appNames = ''
this.$parent.crud.toQuery()
})
.catch(err => {
this.submitLoading = false
console.log('error:' + err.response.data.message)
})
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,93 +0,0 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.blurry" clearable placeholder="输入搜索内容" style="width: 200px" class="filter-item" @keyup.enter.native="crud.toQuery" />
<date-range-picker v-model="query.deployDate" class="date-item" />
<rrOperation />
</div>
<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="appName" label="应用名称" />
<el-table-column prop="ip" label="部署IP" />
<el-table-column prop="deployUser" label="部署人员" />
<el-table-column prop="deployDate" label="部署时间" />
<el-table-column v-if="checkPer(['admin','deployHistory:del'])" label="操作" width="100px" align="center">
<template slot-scope="scope">
<el-popover
:ref="scope.row.id"
v-permission="['admin','deployHistory:del']"
placement="top"
width="180"
>
<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="danger" icon="el-icon-delete" size="mini" />
</el-popover>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import { del } from '@/api/maint/deployHistory'
import CRUD, { presenter, header } 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'
export default {
name: 'DeployHistory',
components: { pagination, crudOperation, rrOperation, DateRangePicker },
cruds() {
return CRUD({ title: '部署历史', url: 'api/deployHistory', crudMethod: { del }})
},
mixins: [presenter(), header()],
data() {
return {
delLoading: false,
permission: {
del: ['admin', 'deployHistory:del']
}
}
},
created() {
this.crud.optShow = {
add: false,
edit: false,
del: true,
download: true
}
},
methods: {
delMethod(id) {
this.delLoading = true
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()
})
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,136 +0,0 @@
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.blurry" clearable placeholder="输入名称或IP搜索" 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="470px">
<el-form ref="form" :model="form" :rules="rules" size="small" label-width="55px">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" style="width: 370px" />
</el-form-item>
<el-form-item label="IP" prop="ip">
<el-input v-model="form.ip" style="width: 370px" />
</el-form-item>
<el-form-item label="端口" prop="port">
<el-input-number v-model.number="form.port" controls-position="right" style="width: 370px;" />
</el-form-item>
<el-form-item label="账号" prop="account">
<el-input v-model="form.account" style="width: 370px" />
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="form.password" type="password" style="width: 200px" />
<el-button :loading="loading" type="success" style="align: right;" @click="testConnectServer">测试连接</el-button>
</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 type="selection" width="55" />
<el-table-column prop="name" label="名称" />
<el-table-column prop="ip" label="IP" />
<el-table-column prop="port" label="端口" />
<el-table-column prop="account" label="账号" />
<el-table-column prop="createTime" label="创建日期" />
<el-table-column v-if="checkPer(['admin','serverDeploy:edit','serverDeploy:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</template>
<script>
import crudServer from '@/api/maint/serverDeploy'
import { testServerConnect } from '@/api/maint/connect'
import { validateIP } from '@/utils/validate'
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'
const defaultForm = { id: null, name: null, ip: null, port: 22, account: 'root', password: null }
export default {
name: 'Server',
components: { pagination, crudOperation, rrOperation, udOperation, DateRangePicker },
cruds() {
return CRUD({ title: '服务器', url: 'api/serverDeploy', crudMethod: { ...crudServer }})
},
mixins: [presenter(), header(), form(defaultForm), crud()],
data() {
return {
accountList: [],
accountMap: {},
loading: false,
permission: {
add: ['admin', 'serverDeploy:add'],
edit: ['admin', 'serverDeploy:edit'],
del: ['admin', 'serverDeploy:del']
},
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' }
],
ip: [
{ required: true, message: '请输入IP', trigger: 'blur' },
{ validator: validateIP, trigger: 'change' }
],
port: [
{ required: true, message: '请输入端口', trigger: 'blur', type: 'number' }
],
account: [
{ required: true, message: '请输入账号', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' }
]
}
}
},
methods: {
testConnectServer() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.loading = true
testServerConnect(this.form).then((res) => {
this.loading = false
this.$notify({
title: res ? '连接成功' : '连接失败',
type: res ? 'success' : 'error',
duration: 2500
})
}).catch(() => {
this.loading = false
})
}
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-input-number .el-input__inner {
text-align: left;
}
</style>

View File

@ -1,36 +0,0 @@
<template>
<div class="app-container">
<el-alert :closable="false" title="三级菜单1" type="success" />
<el-form label-width="170px" style="margin-top: 20px">
<el-form-item label="三级菜单缓存功能测试区">
<el-input v-model="input" placeholder="请输入内容" style="width: 360px;" />
</el-form-item>
</el-form>
<div>
<blockquote class="my-blockquote"> 三级菜单缓存配置教程</blockquote>
<pre class="my-code">
1将前后端代码更新为最新版版本或对照提交记录修改,点击查看-> <a href="https://gitee.com/elunez/eladmin/commit/43d1a63577f9d5347924355708429a2d210e29f7" target="_blank">提交(1)</a><a href="https://gitee.com/elunez/eladmin/commit/46393875148fcca5eaa327d4073f72edb3752f5c" target="_blank">提交(2)</a><a href="https://gitee.com/elunez/eladmin-web/commit/c93c99d8921abbb2c52afc806635f5ca08d6bda8" target="_blank">提交(3)</a>
2 二级菜单 菜单类型 设置为 目录 级别并且原有的 组件路径 需要清空
3 三级菜单 菜单缓存 设置为 最后将 组件名称 填写正确
4具体设置可参考 菜单管理 多级菜单 配置进行进行相应的修改
</pre>
<blockquote class="my-blockquote">更多帮助</blockquote>
<pre class="my-code">QQ交流群一群891137268二群947578238三群659622532</pre>
</div>
</div>
</template>
<script>
export default {
name: 'Test',
data() {
return {
input: ''
}
}
}
</script>
<style scoped>
.my-code a{
color:#009688;
}
</style>

View File

@ -1,5 +0,0 @@
<template>
<div style="padding:30px;">
<el-alert :closable="false" title="三级菜单2" type="success" />
</div>
</template>

View File

@ -1,5 +0,0 @@
<template>
<div style="padding:30px;">
<el-alert :closable="false" title="二级菜单" />
</div>
</template>

View File

@ -21,7 +21,7 @@
<ul class="user-info"> <ul class="user-info">
<li><div style="height: 100%"><svg-icon icon-class="login" /> 登录账号<div class="user-right">{{ user.username }}</div></div></li> <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="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="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="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="email" /> 用户邮箱 <div class="user-right">{{ user.email }}</div></li>
<li> <li>

View File

@ -86,32 +86,32 @@
<el-form-item label="邮箱" prop="email"> <el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" /> <el-input v-model="form.email" />
</el-form-item> </el-form-item>
<el-form-item label="部门" prop="dept.id"> <el-form-item label="归属" prop="dept.id">
<treeselect <treeselect
v-model="form.dept.id" v-model="form.dept.id"
:options="depts" :options="depts"
:load-options="loadDepts" :load-options="loadDepts"
style="width: 173px" style="width: 173px"
placeholder="选择部门" placeholder="选择医院"
/> />
</el-form-item> </el-form-item>
<el-form-item label="岗位" prop="jobDatas" class="is-required"> <!-- <el-form-item label="岗位" prop="jobDatas" class="is-required">-->
<el-select <!-- <el-select-->
v-model="jobDatas" <!-- v-model="jobDatas"-->
style="width: 172px" <!-- style="width: 172px"-->
multiple <!-- multiple-->
placeholder="请选择" <!-- placeholder="请选择"-->
@remove-tag="deleteTag" <!-- @remove-tag="deleteTag"-->
@change="changeJob" <!-- @change="changeJob"-->
> <!-- >-->
<el-option <!-- <el-option-->
v-for="item in jobs" <!-- v-for="item in jobs"-->
:key="item.name" <!-- :key="item.name"-->
:label="item.name" <!-- :label="item.name"-->
:value="item.id" <!-- :value="item.id"-->
/> <!-- />-->
</el-select> <!-- </el-select>-->
</el-form-item> <!-- </el-form-item>-->
<el-form-item label="性别"> <el-form-item label="性别">
<el-radio-group v-model="form.gender" style="width: 178px"> <el-radio-group v-model="form.gender" style="width: 178px">
<el-radio label="男"></el-radio> <el-radio label="男"></el-radio>
@ -291,6 +291,8 @@ export default {
value = this.roleDatas value = this.roleDatas
if (!value || value.length === 0) { if (!value || value.length === 0) {
callback(new Error('请选择至少一个角色')) callback(new Error('请选择至少一个角色'))
} else if (value.length > 1) {
callback(new Error('只能选择一个角色'))
} else { } else {
callback() callback()
} }

View File

@ -1,98 +0,0 @@
<template>
<el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="100px">
<el-form-item label="appID" prop="appId">
<el-input v-model="form.appId" style="width: 40%" />
<span style="color: #C0C0C0;margin-left: 10px;">应用APPID,收款账号既是APPID对应支付宝账号</span>
</el-form-item>
<el-form-item label="商家账号" prop="sysServiceProviderId">
<el-input v-model="form.sysServiceProviderId" style="width: 40%;" />
<span style="color: #C0C0C0;margin-left: 10px;">商家账号</span>
</el-form-item>
<el-form-item label="商户私钥" prop="privateKey">
<el-input v-model="form.privateKey" type="password" style="width: 40%;" />
<span style="color: #C0C0C0;margin-left: 10px;">商户私钥你的PKCS8格式RSA2私钥</span>
</el-form-item>
<el-form-item label="支付宝公钥" prop="publicKey">
<el-input v-model="form.publicKey" type="password" style="width: 40%;" />
<span style="color: #C0C0C0;margin-left: 10px;">支付宝公钥</span>
</el-form-item>
<el-form-item label="回调地址" prop="returnUrl">
<el-input v-model="form.returnUrl" style="width: 40%;" />
<span style="color: #C0C0C0;margin-left: 10px;">订单完成后返回的地址</span>
</el-form-item>
<el-form-item label="异步通知" prop="notifyUrl">
<el-input v-model="form.notifyUrl" style="width: 40%;" />
<span style="color: #C0C0C0;margin-left: 10px;">支付结果异步通知地址</span>
</el-form-item>
<el-form-item label="">
<el-button :loading="loading" size="medium" type="primary" @click="doSubmit">保存配置</el-button>
</el-form-item>
</el-form>
</template>
<script>
import { get, update } from '@/api/tools/alipay'
export default {
name: 'Config',
data() {
return {
loading: false,
form: { appId: '', sysServiceProviderId: '', privateKey: '', publicKey: '', returnUrl: '', notifyUrl: '' },
rules: {
appId: [
{ required: true, message: '请输入appID', trigger: 'blur' }
],
sysServiceProviderId: [
{ required: true, message: '请输入商家账号', trigger: 'blur' }
],
privateKey: [
{ required: true, message: '商户私钥不能为空', trigger: 'blur' }
],
publicKey: [
{ required: true, message: '支付宝公钥不能为空', trigger: 'blur' }
],
returnUrl: [
{ required: true, message: '回调地址不能为空', trigger: 'blur' }
],
notifyUrl: [
{ required: true, message: '回调地址不能为空', trigger: 'blur' }
]
}
}
},
created() {
this.init()
},
methods: {
init() {
get().then(res => {
this.form = res
})
},
doSubmit() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.loading = true
update(this.form).then(res => {
this.$notify({
title: '修改成功',
type: 'success',
duration: 2500
})
this.loading = false
}).catch(err => {
this.loading = false
console.log(err.response.data.message)
})
} else {
return false
}
})
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,48 +0,0 @@
<template>
<el-tabs v-model="activeName" style="padding-left: 5px;">
<el-tab-pane label="参数配置" name="first">
<Config />
</el-tab-pane>
<el-tab-pane label="支付测试" name="second">
<ToPay />
</el-tab-pane>
<el-tab-pane label="使用说明" name="third">
<div>
<blockquote class="my-blockquote">注意</blockquote>
<pre class="my-code">
测试所用参数都是沙箱环境仅供测试使用申请地址<a style="color: #00a0e9" href="https://openhome.alipay.com/platform/appDaily.htm?tab=info" target="_blank">支付宝开发平台</a>
如需付款测试请使用
账号uuxesw9745@sandbox.com
密码与支付密码111111</pre>
<blockquote class="my-blockquote"> 支付设置</blockquote>
<pre class="my-code">
//
// PC使
if (/(Android)/i.test(navigator.userAgent)){ // Android
url = "/aliPay/toPayAsWeb"
}else if(/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)){ //
url = "/aliPay/toPayAsWeb"
} else {
url = "/aliPay/toPayAsPC"
}</pre>
</div>
</el-tab-pane>
</el-tabs>
</template>
<script>
import Config from './config'
import ToPay from './toPay'
export default {
name: 'AliPay',
components: { Config, ToPay },
data() {
return {
activeName: 'second'
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,86 +0,0 @@
<template>
<div>
<el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="90px">
<el-form-item label="商品名称" prop="subject">
<el-input v-model="form.subject" style="width: 35%" />
</el-form-item>
<el-form-item label="商品价格" prop="totalAmount">
<el-input v-model="form.totalAmount" style="width: 35%" />
<span style="color: #C0C0C0;margin-left: 10px;">测试允许区间(0,5000]</span>
</el-form-item>
<el-form-item label="商品描述" prop="body">
<el-input v-model="form.body" style="width: 35%" rows="8" type="textarea" />
</el-form-item>
<el-form-item label="">
<el-button :loading="loading" size="medium" type="primary" @click="doSubmit">去支付</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { toAliPay } from '@/api/tools/alipay'
export default {
data() {
return {
url: '',
//
newWin: null,
loading: false, form: { subject: '', totalAmount: '', body: '' },
rules: {
subject: [
{ required: true, message: '商品名称不能为空', trigger: 'blur' }
],
totalAmount: [
{ required: true, message: '商品价格不能为空', trigger: 'blur' }
],
body: [
{ required: true, message: '商品描述不能为空', trigger: 'blur' }
]
}
}
},
watch: {
url(newVal, oldVal) {
if (newVal && this.newWin) {
this.newWin.sessionStorage.clear()
this.newWin.location.href = newVal
// urlnewWin
this.url = ''
this.newWin = null
}
}
},
methods: {
doSubmit() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.loading = true
//
this.newWin = window.open()
let url = ''
if (/(Android)/i.test(navigator.userAgent)) { // Android
url = 'aliPay/toPayAsWeb'
} else if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) { //
url = 'aliPay/toPayAsWeb'
} else {
url = 'aliPay/toPayAsPC'
}
toAliPay(url, this.form).then(res => {
this.loading = false
this.url = res
}).catch(err => {
this.loading = false
console.log(err.response.data.message)
})
} else {
return false
}
})
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,91 +0,0 @@
<template>
<el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="100px">
<el-form-item label="发件人邮箱" prop="fromUser">
<el-input v-model="form.fromUser" style="width: 40%" />
<span style="color: #C0C0C0;margin-left: 10px;">Sender mailbox</span>
</el-form-item>
<el-form-item label="发件用户名" prop="user">
<el-input v-model="form.user" style="width: 40%;" />
<span style="color: #C0C0C0;margin-left: 10px;">Sender usernamex</span>
</el-form-item>
<el-form-item label="邮箱密码" prop="pass">
<el-input v-model="form.pass" type="password" style="width: 40%;" />
<span style="color: #C0C0C0;margin-left: 10px;">email Password</span>
</el-form-item>
<el-form-item label="SMTP地址" prop="host">
<el-input v-model="form.host" style="width: 40%;" />
<span style="color: #C0C0C0;margin-left: 10px;">SMTP address</span>
</el-form-item>
<el-form-item label="SMTP端口" prop="port">
<el-input v-model="form.port" style="width: 40%;" />
<span style="color: #C0C0C0;margin-left: 10px;">SMTP port</span>
</el-form-item>
<el-form-item label="">
<el-button :loading="loading" size="medium" type="primary" @click="doSubmit">保存配置</el-button>
</el-form-item>
</el-form>
</template>
<script>
import { get, update } from '@/api/tools/email'
export default {
name: 'Config',
data() {
return {
loading: false, form: { id: 1, fromUser: '', user: '', pass: '', host: '', port: '', sslEnable: '' },
rules: {
fromUser: [
{ required: true, message: '请输入发件人邮箱', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
],
user: [
{ required: true, message: '请输入发件用户名', trigger: 'blur' }
],
pass: [
{ required: true, message: '密码不能为空', trigger: 'blur' }
],
host: [
{ required: true, message: 'SMTP地址不能为空', trigger: 'blur' }
],
port: [
{ required: true, message: 'SMTP端口不能为空', trigger: 'blur' }
]
}
}
},
created() {
this.init()
},
methods: {
init() {
get().then(res => {
this.form = res
})
},
doSubmit() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.loading = true
update(this.form).then(res => {
this.$notify({
title: '修改成功',
type: 'success',
duration: 2500
})
this.loading = false
}).catch(err => {
this.loading = false
console.log(err.response.data.message)
})
} else {
return false
}
})
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,41 +0,0 @@
<template>
<el-tabs v-model="activeName" style="padding-left: 8px;">
<el-tab-pane label="邮箱配置" name="first">
<Config />
</el-tab-pane>
<el-tab-pane label="发送邮件" name="second">
<Send />
</el-tab-pane>
<el-tab-pane label="使用说明" name="third">
<div>
<blockquote class="my-blockquote"> 邮件服务器配置</blockquote>
<pre class="my-code">
# 邮件服务器的SMTP地址可选默认为smtp
# 邮件服务器的SMTP端口可选默认465或者25
# 发件人必须正确否则发送失败
# 用户名默认为发件人邮箱前缀
# 密码注意某些邮箱需要为SMTP服务单独设置密码如QQ和163等等
# 是否开启ssl默认开启</pre>
<blockquote class="my-blockquote">更多帮助</blockquote>
<pre class="my-code">更多帮助请查看文档<a style="color:#009688" href="http://hutool.mydoc.io/#text_319499" target="_black">hutool工具包</a></pre>
</div>
</el-tab-pane>
</el-tabs>
</template>
<script>
import Config from './config'
import Send from './send'
export default {
name: 'Email',
components: { Config, Send },
data() {
return {
activeName: 'second'
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,98 +0,0 @@
<template>
<div>
<el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="100px">
<el-form-item label="邮件标题" prop="subject">
<el-input v-model="form.subject" style="width: 646px" placeholder="请输入邮件标题,标题不能为空" />
</el-form-item>
<el-form-item label="收件地址" prop="tos">
<el-input v-model="form.tos" style="width: 646px" placeholder="请输入收件地址,多个地址英文逗号,隔开" />
</el-form-item>
<div ref="editor" class="editor" />
<el-button :loading="loading" style="margin-left:1.6%;margin-bottom: 30px" size="medium" type="primary" @click="doSubmit">发送邮件</el-button>
</el-form>
</div>
</template>
<script>
import { send } from '@/api/tools/email'
import { upload } from '@/utils/upload'
import { mapGetters } from 'vuex'
import E from 'wangeditor'
export default {
name: 'Index',
data() {
return {
loading: false, form: { subject: '', tos: '', content: '' },
rules: {
subject: [
{ required: true, message: '标题不能为空', trigger: 'blur' }
],
tos: [
{ required: true, message: '收件人不能为空', trigger: 'blur' }
]
}
}
},
computed: {
...mapGetters([
'imagesUploadApi',
'baseApi'
])
},
mounted() {
const _this = this
var editor = new E(this.$refs.editor)
//
editor.config.zIndex = 10
//
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.form.content = html
}
editor.create()
},
methods: {
doSubmit() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.loading = true
send(this.form).then(res => {
this.$notify({
title: '发送成功',
type: 'success',
duration: 2500
})
this.loading = false
}).catch(err => {
this.loading = false
console.log(err.response.data.message)
})
} else {
return false
}
})
}
}
}
</script>
<style scoped>
.editor{
text-align:left;
margin: 20px;
width: 730px;
}
::v-deep .w-e-text-container {
height: 360px !important;
}
</style>

View File

@ -3,18 +3,19 @@
<el-tab-pane label="本地存储" name="first"> <el-tab-pane label="本地存储" name="first">
<Local ref="local" /> <Local ref="local" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="七牛云存储" name="second"> <!-- <el-tab-pane label="七牛云存储" name="second">-->
<QiNiu ref="qiNiu" /> <!-- <QiNiu ref="qiNiu" />-->
</el-tab-pane> <!-- </el-tab-pane>-->
</el-tabs> </el-tabs>
</template> </template>
<script> <script>
import QiNiu from './qiniu/index' // import QiNiu from './qiniu/index'
import Local from './local/index' import Local from './local/index'
export default { export default {
name: 'Storage', name: 'Storage',
components: { QiNiu, Local }, components: { Local },
// components: { QiNiu, Local },
data() { data() {
return { return {
activeName: 'first' activeName: 'first'

View File

@ -1,98 +0,0 @@
<template>
<el-dialog :visible.sync="dialog" :close-on-click-modal="false" title="七牛云配置" append-to-body width="580px">
<el-form ref="form" :model="form" :rules="rules" style="margin-top: 6px;" size="small" label-width="110px">
<el-form-item label="Access Key" prop="accessKey">
<el-input v-model="form.accessKey" style="width: 95%" placeholder="accessKey在安全中心秘钥管理中查看" />
</el-form-item>
<el-form-item label="Secret Key" prop="secretKey">
<el-input v-model="form.secretKey" type="password" style="width: 95%;" placeholder="secretKey在安全中心秘钥管理中查看" />
</el-form-item>
<el-form-item label="空间名称" prop="bucket">
<el-input v-model="form.bucket" style="width: 95%;" placeholder="存储空间名称作为唯一的 Bucket 识别符" />
</el-form-item>
<el-form-item label="外链域名" prop="host">
<el-input v-model="form.host" style="width: 95%;" placeholder="外链域名,可自定义,需在七牛云绑定" />
</el-form-item>
<el-form-item label="存储区域">
<el-select v-model="form.zone" placeholder="请选择存储区域">
<el-option
v-for="item in zones"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
<el-form-item label="空间类型" prop="type">
<el-radio v-model="form.type" label="公开">公开</el-radio>
<el-radio v-model="form.type" label="私有">私有</el-radio>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="dialog = false">取消</el-button>
<el-button :loading="loading" type="primary" @click="doSubmit">确认</el-button>
</div>
</el-dialog>
</template>
<script>
import { get, update } from '@/api/tools/qiniu'
export default {
data() {
return {
zones: ['华东', '华北', '华南', '北美', '东南亚'], dialog: false,
loading: false, form: { accessKey: '', secretKey: '', bucket: '', host: '', zone: '', type: '' },
rules: {
accessKey: [
{ required: true, message: '请输入accessKey', trigger: 'blur' }
],
secretKey: [
{ required: true, message: '请输入secretKey', trigger: 'blur' }
],
bucket: [
{ required: true, message: '请输入空间名称', trigger: 'blur' }
],
host: [
{ required: true, message: '请输入外链域名', trigger: 'blur' }
],
type: [
{ required: true, message: '空间类型不能为空', trigger: 'blur' }
]
}
}
},
methods: {
init() {
get().then(res => {
this.form = res
})
},
doSubmit() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.loading = true
update(this.form).then(res => {
this.$notify({
title: '修改成功',
type: 'success',
duration: 2500
})
this.$parent.crud.toQuery()
this.loading = false
this.dialog = false
}).catch(err => {
this.loading = false
console.log(err.response.data.message)
})
} else {
return false
}
})
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,189 +0,0 @@
<template>
<div class="app-container" style="padding: 8px;">
<!--表单组件-->
<eForm ref="form" />
<!-- 工具栏 -->
<div class="head-container">
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<el-input v-model="query.key" 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">
<template slot="left">
<!-- 上传 -->
<el-button class="filter-item" size="mini" type="primary" icon="el-icon-upload" @click="dialog = true">上传</el-button>
<!-- 同步 -->
<el-button :icon="icon" class="filter-item" size="mini" type="warning" @click="synchronize">同步</el-button>
<!-- 配置 -->
<el-button
class="filter-item"
size="mini"
type="success"
icon="el-icon-s-tools"
@click="doConfig"
>配置</el-button>
</template>
</crudOperation>
<!-- 文件上传 -->
<el-dialog :visible.sync="dialog" :close-on-click-modal="false" append-to-body width="500px" @close="doSubmit">
<el-upload
:before-remove="handleBeforeRemove"
:on-success="handleSuccess"
:on-error="handleError"
:file-list="fileList"
:headers="headers"
:action="qiNiuUploadApi"
class="upload-demo"
multiple
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" style="display: block;" class="el-upload__tip">请勿上传违法文件且文件不超过15M</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="doSubmit">确认</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 type="selection" width="55" />
<el-table-column prop="name" :show-overflow-tooltip="true" label="文件名">
<template slot-scope="scope">
<a href="JavaScript:" class="el-link el-link--primary" target="_blank" type="primary" @click="download(scope.row.id)">{{ scope.row.key }}</a>
</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" prop="suffix" label="文件类型" @selection-change="crud.selectionChangeHandler" />
<el-table-column prop="bucket" label="空间名称" />
<el-table-column prop="size" label="文件大小" />
<el-table-column prop="type" label="空间类型" />
<el-table-column prop="updateTime" label="创建日期" />
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crudQiNiu from '@/api/tools/qiniu'
import { mapGetters } from 'vuex'
import { getToken } from '@/utils/auth'
import eForm from './form'
import CRUD, { presenter, header, 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'
export default {
components: { eForm, pagination, crudOperation, rrOperation, DateRangePicker },
cruds() {
return CRUD({ title: '七牛云文件', url: 'api/qiNiuContent', crudMethod: { ...crudQiNiu }})
},
mixins: [presenter(), header(), crud()],
data() {
return {
permission: {
del: ['admin', 'storage:del']
},
title: '文件', dialog: false,
icon: 'el-icon-refresh',
url: '', headers: { 'Authorization': getToken() },
dialogImageUrl: '', dialogVisible: false, fileList: [], files: [], newWin: null
}
},
computed: {
...mapGetters([
'qiNiuUploadApi'
])
},
watch: {
url(newVal, oldVal) {
if (newVal && this.newWin) {
this.newWin.sessionStorage.clear()
this.newWin.location.href = newVal
// urlnewWin
this.url = ''
this.newWin = null
}
}
},
created() {
this.crud.optShow.add = false
this.crud.optShow.edit = false
},
methods: {
//
doConfig() {
const _this = this.$refs.form
_this.init()
_this.dialog = true
},
handleSuccess(response, file, fileList) {
const uid = file.uid
const id = response.id
this.files.push({ uid, id })
},
handleBeforeRemove(file, fileList) {
for (let i = 0; i < this.files.length; i++) {
if (this.files[i].uid === file.uid) {
crudQiNiu.del([this.files[i].id]).then(res => {})
return true
}
}
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url
this.dialogVisible = true
},
//
doSubmit() {
this.fileList = []
this.dialogVisible = false
this.dialogImageUrl = ''
this.dialog = false
this.crud.toQuery()
},
//
handleError(e, file, fileList) {
const msg = JSON.parse(e.message)
this.crud.notify(msg.message, CRUD.NOTIFICATION_TYPE.ERROR)
},
//
download(id) {
this.downloadLoading = true
//
this.newWin = window.open()
crudQiNiu.download(id).then(res => {
this.downloadLoading = false
this.url = res.url
}).catch(err => {
this.downloadLoading = false
console.log(err.response.data.message)
})
},
//
synchronize() {
this.icon = 'el-icon-loading'
crudQiNiu.sync().then(res => {
this.icon = 'el-icon-refresh'
this.$message({
showClose: true,
message: '数据同步成功',
type: 'success',
duration: 1500
})
this.crud.toQuery()
}).catch(err => {
this.icon = 'el-icon-refresh'
console.log(err.response.data.message)
})
}
}
}
</script>
<style scoped>
</style>

View File

@ -176,7 +176,7 @@ recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within the same "printed page" as the copyright notice for easier identification within
third-party archives. third-party archives.
Copyright 2019-2025 Zheng Jie Copyright 2019-2025 Tz
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -1,26 +1,17 @@
<h1 style="text-align: center">ELADMIN 后台管理系统</h1> <h1 style="text-align: center">区块链医疗数据共享平台管理</h1>
#### 项目简介 #### 项目简介
一个基于 Spring Boot 2.7.18 、 Mybatis-Plus、 JWT、Spring Security、Redis、Vue的前后端分离的后台管理系统 一个基于 Spring Boot 2.7.18 、 Mybatis-Plus、 JWT、Spring Security、Redis、Vue的前后端分离的后台管理系统
**开发文档:** [https://eladmin.vip](https://eladmin.vip)
**体验地址:** [https://eladmin.vip/demo](https://eladmin.vip/demo)
**账号密码:** `admin / 123456` **账号密码:** `admin / 123456`
#### 项目源码 #### 项目结构
项目采用按功能分模块的开发方式,结构如下
| github | gitee | - `eladmin-common` 为系统的公共模块,各种工具类,公共配置存在该模块
|--------------------------------------| --- |
| https://github.com/elunez/eladmin-mp | https://gitee.com/elunez/eladmin-mp |
#### VPS推荐 - `eladmin-system` 为系统核心模块也是项目入口模块,也是最终需要打包部署的模块
<a href="https://bwh81.net/aff.php?aff=70876" target="_blank">
<img src="https://eladmin.vip/images/banner/side.jpeg" style="width: 435px;border-radius: 2px;" alt="帮瓦工">
</a>
使用优惠码: `BWHNCXNVXV`,可获得 6.81% 的折扣, [查看介绍](https://eladmin.vip/pages/040101/)
#### 主要特性 #### 主要特性
- 使用最新技术栈,社区资源丰富。 - 使用最新技术栈,社区资源丰富。
- 高效率开发,代码生成器可一键生成前后端代码 - 高效率开发,代码生成器可一键生成前后端代码
@ -41,14 +32,7 @@
- 岗位管理:配置各个部门的职位 - 岗位管理:配置各个部门的职位
- 字典管理:可维护常用一些固定的数据,如:状态,性别等 - 字典管理:可维护常用一些固定的数据,如:状态,性别等
- 系统日志:记录用户操作日志与异常日志,方便开发人员定位排错 - 系统日志:记录用户操作日志与异常日志,方便开发人员定位排错
- SQL监控采用druid 监控数据库访问性能默认用户名admin密码123456 - 档案管理:医院对用户档案的隐私保护和档案上链追溯管理,授权查看等权限限制
- 定时任务整合Quartz做定时任务加入任务日志任务运行情况一目了然
- 代码生成:高灵活度生成前后端代码,减少大量重复的工作任务
- 邮件工具配合富文本发送html格式的邮件
- 七牛云存储:可同步七牛云存储的数据到系统,无需登录七牛云直接操作云数据
- 支付宝支付:整合了支付宝支付并且提供了测试账号,可自行测试
- 服务监控:监控服务器的负载情况
- 运维管理:一键部署你的应用
#### 项目结构 #### 项目结构
项目采用按功能分模块的开发方式,结构如下 项目采用按功能分模块的开发方式,结构如下
@ -57,12 +41,6 @@
- `eladmin-system` 为系统核心模块也是项目入口模块,也是最终需要打包部署的模块 - `eladmin-system` 为系统核心模块也是项目入口模块,也是最终需要打包部署的模块
- `eladmin-logging` 为系统的日志模块,其他模块如果需要记录日志需要引入该模块
- `eladmin-tools` 为第三方工具模块,包含:邮件、七牛云存储、本地存储、支付宝
- `eladmin-generator` 为系统的代码生成模块支持生成前后端CRUD代码
#### 详细结构 #### 详细结构
``` ```
@ -89,26 +67,4 @@
- sysrunner 程序启动后处理数据 - sysrunner 程序启动后处理数据
- modules 系统相关模块(登录授权、系统监控、定时任务、系统模块、运维模块) - modules 系统相关模块(登录授权、系统监控、定时任务、系统模块、运维模块)
- eladmin-logging 系统日志模块 - eladmin-logging 系统日志模块
- eladmin-tools 系统第三方工具模块 ```
- email 邮件工具
- qiniu 七牛云存储工具
- alipay 支付宝支付工具
- local-storage 本地存储工具
- eladmin-generator 系统代码生成模块
```
#### 特别鸣谢
- 感谢 [PanJiaChen](https://github.com/PanJiaChen/vue-element-admin) 大佬提供的前端模板
- 感谢 [Moxun](https://github.com/moxun1639) 大佬提供的前端 Curd 通用组件
- 感谢 [zhy6599](https://gitee.com/zhy6599) 大佬提供的后端运维管理相关功能
- 感谢 [j.yao.SUSE](https://github.com/everhopingandwaiting) 大佬提供的匿名接口与Redis限流等功能
#### 项目捐赠
项目的发展离不开你的支持,请作者喝杯咖啡吧☕ [Donate](https://eladmin.vip/pages/030101/)
#### 反馈交流
- QQ交流群891137268 、947578238、659622532

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -30,7 +30,7 @@ import java.sql.Timestamp;
/** /**
* 通用字段 is_del 根据需求自行添加 * 通用字段 is_del 根据需求自行添加
* @author Zheng Jie * @author Tz
* @date 2019年10月24日20:46:32 * @date 2019年10月24日20:46:32
*/ */
@Getter @Getter

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -26,7 +26,7 @@ import java.util.concurrent.atomic.AtomicInteger;
/** /**
* 创建自定义的线程池 * 创建自定义的线程池
* @author Zheng Jie * @author Tz
* @description * @description
* @date 2023-06-08 * @date 2023-06-08
**/ **/

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,7 +23,7 @@ import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* @author Zheng Jie * @author Tz
*/ */
@Service(value = "el") @Service(value = "el")
public class AuthorityConfig { public class AuthorityConfig {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -44,7 +44,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* @author Zheng Jie * @author Tz
* @date 2025-01-13 * @date 2025-01-13
*/ */
@Slf4j @Slf4j

View File

@ -15,7 +15,7 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
/** /**
* @author Zheng Jie * @author Tz
* @description * @description
* @date 2025-01-11 * @date 2025-01-11
**/ **/

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,7 +20,7 @@ import com.p6spy.engine.spy.appender.MessageFormattingStrategy;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
/** /**
* @author Zheng Jie * @author Tz
* @description 自定义 p6spy sql输出格式 * @description 自定义 p6spy sql输出格式
* @date 2024-12-26 * @date 2024-12-26
**/ **/

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,7 +23,7 @@ import org.springframework.stereotype.Component;
import java.sql.Timestamp; import java.sql.Timestamp;
/** /**
* @author Zheng Jie * @author Tz
* @description * @description
* @date 2023-06-13 * @date 2023-06-13
**/ **/

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
/** /**
* @author Zheng Jie * @author Tz
* @description * @description
* @date 2023-06-12 * @date 2023-06-12
**/ **/

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,7 +21,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
/** /**
* @author Zheng Jie * @author Tz
*/ */
@Data @Data
@Configuration @Configuration

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,7 +20,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* @author Zheng Jie * @author Tz
* @website <a href="https://eladmin.vip">...</a> * @website <a href="https://eladmin.vip">...</a>
* @description * @description
* @date 2020-05-18 * @date 2020-05-18

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -37,7 +37,7 @@ import java.util.List;
/** /**
* WebMvcConfigurer * WebMvcConfigurer
* *
* @author Zheng Jie * @author Tz
* @date 2018-11-30 * @date 2018-11-30
*/ */
@Configuration @Configuration

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2023 Zheng Jie * Copyright 2019-2023 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -47,7 +47,7 @@ import java.util.stream.Collectors;
/** /**
* api页面 /doc.html * api页面 /doc.html
* @author Zheng Jie * @author Tz
* @date 2018-11-23 * @date 2018-11-23
*/ */
@Configuration @Configuration

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,7 +20,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter; import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/** /**
* @author ZhangHouYing * @author Tz
* @date 2019-08-24 15:44 * @date 2019-08-24 15:44
*/ */
@Configuration @Configuration

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,7 +20,7 @@ import org.springframework.http.HttpStatus;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.BAD_REQUEST;
/** /**
* @author Zheng Jie * @author Tz
* @date 2018-11-23 * @date 2018-11-23
* 统一异常处理 * 统一异常处理
*/ */

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,7 +18,7 @@ package me.zhengjie.exception;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
* @author Zheng Jie * @author Tz
* @date 2018-11-23 * @date 2018-11-23
*/ */
public class EntityExistException extends RuntimeException { public class EntityExistException extends RuntimeException {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,7 +18,7 @@ package me.zhengjie.exception;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
* @author Zheng Jie * @author Tz
* @date 2018-11-23 * @date 2018-11-23
*/ */
public class EntityNotFoundException extends RuntimeException { public class EntityNotFoundException extends RuntimeException {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,7 +18,7 @@ package me.zhengjie.exception.handler;
import lombok.Data; import lombok.Data;
/** /**
* @author Zheng Jie * @author Tz
* @date 2018-11-23 * @date 2018-11-23
*/ */
@Data @Data

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -31,7 +31,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
import static org.springframework.http.HttpStatus.*; import static org.springframework.http.HttpStatus.*;
/** /**
* @author Zheng Jie * @author Tz
* @date 2018-11-23 * @date 2018-11-23
*/ */
@Slf4j @Slf4j

View File

@ -25,7 +25,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
import java.util.*; import java.util.*;
/** /**
* @author Zheng Jie * @author Tz
* @description 匿名标记工具 * @description 匿名标记工具
* @date 2025-01-13 * @date 2025-01-13
**/ **/

View File

@ -19,7 +19,7 @@ import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
/** /**
* @author Zheng Jie * @author Tz
* @description 计算类 * @description 计算类
* @date 2024-12-27 * @date 2024-12-27
**/ **/

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,7 +18,7 @@ package me.zhengjie.utils;
import java.io.Closeable; import java.io.Closeable;
/** /**
* @author Zheng Jie * @author Tz
* @description 用于关闭各种连接缺啥补啥 * @description 用于关闭各种连接缺啥补啥
* @date 2021-03-05 * @date 2021-03-05
**/ **/

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,7 +18,7 @@ package me.zhengjie.utils;
/** /**
* 常用静态常量 * 常用静态常量
* *
* @author Zheng Jie * @author Tz
* @date 2018-12-26 * @date 2018-12-26
*/ */
public class ElConstant { public class ElConstant {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -24,7 +24,7 @@ import java.nio.charset.StandardCharsets;
/** /**
* 加密 * 加密
* @author Zheng Jie * @author Tz
* @date 2018-11-23 * @date 2018-11-23
*/ */
public class EncryptUtils { public class EncryptUtils {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -38,7 +38,7 @@ import java.util.stream.Collectors;
/** /**
* File工具类扩展 hutool 工具包 * File工具类扩展 hutool 工具包
* *
* @author Zheng Jie * @author Tz
* @date 2018-12-27 * @date 2018-12-27
*/ */
@Slf4j @Slf4j

View File

@ -6,7 +6,7 @@ import java.util.List;
/** /**
* 分页结果封装类 * 分页结果封装类
* @author Zheng Jie * @author Tz
* @date 2018-11-23 * @date 2018-11-23
* @param <T> * @param <T>
*/ */

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,7 +20,7 @@ import java.util.*;
/** /**
* 分页工具 * 分页工具
* @author Zheng Jie * @author Tz
* @date 2018-12-10 * @date 2018-12-10
*/ */
public class PageUtil extends cn.hutool.core.util.PageUtil { public class PageUtil extends cn.hutool.core.util.PageUtil {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@ import java.util.Objects;
/** /**
* 获取 HttpServletRequest * 获取 HttpServletRequest
* @author Zheng Jie * @author Tz
* @date 2018-11-24 * @date 2018-11-24
*/ */
public class RequestHolder { public class RequestHolder {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -35,7 +35,7 @@ import java.util.Objects;
/** /**
* 获取当前登录的用户 * 获取当前登录的用户
* @author Zheng Jie * @author Tz
* @date 2019-01-17 * @date 2019-01-17
*/ */
@Slf4j @Slf4j

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -28,7 +28,7 @@ import java.net.UnknownHostException;
import java.util.*; import java.util.*;
/** /**
* @author Zheng Jie * @author Tz
* 字符串工具类, 继承org.apache.commons.lang3.StringUtils类 * 字符串工具类, 继承org.apache.commons.lang3.StringUtils类
*/ */
@Slf4j @Slf4j

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,7 +20,7 @@ import java.io.StringWriter;
/** /**
* 异常工具 2019-01-06 * 异常工具 2019-01-06
* @author Zheng Jie * @author Tz
*/ */
public class ThrowableUtil { public class ThrowableUtil {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@ import lombok.Getter;
* <p> * <p>
* 验证码业务场景 * 验证码业务场景
* </p> * </p>
* @author Zheng Jie * @author Tz
* @date 2020-05-02 * @date 2020-05-02
*/ */
@Getter @Getter

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@ import lombok.Getter;
* <p> * <p>
* 验证码业务场景对应的 Redis 中的 key * 验证码业务场景对应的 Redis 中的 key
* </p> * </p>
* @author Zheng Jie * @author Tz
* @date 2020-05-02 * @date 2020-05-02
*/ */
@Getter @Getter

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@ import lombok.Getter;
* <p> * <p>
* 数据权限枚举 * 数据权限枚举
* </p> * </p>
* @author Zheng Jie * @author Tz
* @date 2020-05-07 * @date 2020-05-07
*/ */
@Getter @Getter

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2025 Zheng Jie * Copyright 2019-2025 Tz
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,7 +19,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
/** /**
* @author Zheng Jie * @author Tz
* @website https://eladmin.vip * @website https://eladmin.vip
* @description * @description
* @date 2020-06-10 * @date 2020-06-10

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>eladmin</artifactId>
<groupId>me.zhengjie</groupId>
<version>1.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eladmin-generator</artifactId>
<name>代码生成模块</name>
<properties>
<configuration.version>1.10</configuration.version>
</properties>
<dependencies>
<dependency>
<groupId>me.zhengjie</groupId>
<artifactId>eladmin-common</artifactId>
<version>1.1</version>
</dependency>
<!--模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-configuration/commons-configuration -->
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>${configuration.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,77 +0,0 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.io.Serializable;
/**
* 列的数据信息
* @author Zheng Jie
* @date 2019-01-02
*/
@Getter
@Setter
@NoArgsConstructor
@TableName("code_column")
public class ColumnInfo implements Serializable {
@ApiModelProperty(value = "ID", hidden = true)
@TableId(value = "column_id", type = IdType.AUTO)
private Long id;
@ApiModelProperty(value = "表名")
private String tableName;
@ApiModelProperty(value = "数据库字段名称")
private String columnName;
@ApiModelProperty(value = "数据库字段类型")
private String columnType;
@ApiModelProperty(value = "数据库字段键类型")
private String keyType;
@ApiModelProperty(value = "字段额外的参数")
private String extra;
@ApiModelProperty(value = "数据库字段描述")
private String remark;
@ApiModelProperty(value = "是否必填")
private Boolean notNull;
@ApiModelProperty(value = "是否在列表显示")
private Boolean listShow = true;
@ApiModelProperty(value = "是否表单显示")
private Boolean formShow = true;
@ApiModelProperty(value = "表单类型")
private String formType;
@ApiModelProperty(value = "查询 1:模糊 2精确")
private String queryType;
@ApiModelProperty(value = "字典名称")
private String dictName;
}

View File

@ -1,78 +0,0 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* 代码生成配置
* @author Zheng Jie
* @date 2019-01-03
*/
@Getter
@Setter
@NoArgsConstructor
@TableName("code_config")
public class GenConfig implements Serializable {
public GenConfig(String tableName) {
this.tableName = tableName;
}
@ApiModelProperty(value = "ID", hidden = true)
@TableId(value = "config_id", type = IdType.AUTO)
private Long id;
@NotBlank
@ApiModelProperty(value = "表名")
private String tableName;
@ApiModelProperty(value = "接口名称")
private String apiAlias;
@NotBlank
@ApiModelProperty(value = "包路径")
private String pack;
@NotBlank
@ApiModelProperty(value = "模块名")
private String moduleName;
@NotBlank
@ApiModelProperty(value = "前端文件路径")
private String path;
@ApiModelProperty(value = "前端文件路径")
private String apiPath;
@ApiModelProperty(value = "作者")
private String author;
@ApiModelProperty(value = "表前缀")
private String prefix;
@ApiModelProperty(value = "是否覆盖")
private Boolean cover = false;
}

View File

@ -1,49 +0,0 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.domain.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 表的数据信息
* @author Zheng Jie
* @date 2019-01-02
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TableInfo {
@ApiModelProperty(value = "表名称")
private Object tableName;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "创建日期yyyy-MM-dd HH:mm:ss")
private Object createTime;
@ApiModelProperty(value = "数据库引擎")
private Object engine;
@ApiModelProperty(value = "编码集")
private Object coding;
@ApiModelProperty(value = "备注")
private Object remark;
}

View File

@ -1,39 +0,0 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import me.zhengjie.domain.ColumnInfo;
import me.zhengjie.domain.dto.TableInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author Zheng Jie
* @date 2023-06-26
*/
@Mapper
public interface ColumnInfoMapper extends BaseMapper<ColumnInfo> {
IPage<TableInfo> getTables(@Param("tableName") String tableName, Page<Object> page);
List<ColumnInfo> findByTableNameOrderByIdAsc(@Param("tableName") String tableName);
List<ColumnInfo> getColumns(@Param("tableName") String tableName);
}

View File

@ -1,51 +0,0 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.rest;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import me.zhengjie.domain.GenConfig;
import me.zhengjie.service.GenConfigService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* @author Zheng Jie
* @date 2019-01-14
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/genConfig")
@Api(tags = "系统:代码生成器配置管理")
public class GenConfigController {
private final GenConfigService genConfigService;
@ApiOperation("查询")
@GetMapping(value = "/{tableName}")
public ResponseEntity<GenConfig> queryGenConfig(@PathVariable String tableName){
return new ResponseEntity<>(genConfigService.find(tableName), HttpStatus.OK);
}
@PutMapping
@ApiOperation("修改")
public ResponseEntity<GenConfig> updateGenConfig(@Validated @RequestBody GenConfig genConfig){
return new ResponseEntity<>(genConfigService.update(genConfig.getTableName(), genConfig),HttpStatus.OK);
}
}

View File

@ -1,101 +0,0 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.rest;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import me.zhengjie.domain.ColumnInfo;
import me.zhengjie.domain.dto.TableInfo;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.service.GenConfigService;
import me.zhengjie.service.GeneratorService;
import me.zhengjie.utils.PageResult;
import me.zhengjie.utils.PageUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* @author Zheng Jie
* @date 2019-01-02
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/generator")
@Api(tags = "系统:代码生成管理")
public class GeneratorController {
private final GeneratorService generatorService;
private final GenConfigService genConfigService;
@Value("${generator.enabled}")
private Boolean generatorEnabled;
@ApiOperation("查询数据库数据")
@GetMapping(value = "/tables")
public ResponseEntity<PageResult<TableInfo>> queryTables(@RequestParam(defaultValue = "") String name, @RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer size){
return new ResponseEntity<>(generatorService.getTables(name, new Page<>(page, size)), HttpStatus.OK);
}
@ApiOperation("查询字段数据")
@GetMapping(value = "/columns")
public ResponseEntity<PageResult<ColumnInfo>> queryColumns(@RequestParam String tableName){
List<ColumnInfo> columnInfos = generatorService.getColumns(tableName);
return new ResponseEntity<>(PageUtil.toPage(columnInfos), HttpStatus.OK);
}
@ApiOperation("保存字段数据")
@PutMapping
public ResponseEntity<HttpStatus> saveColumn(@RequestBody List<ColumnInfo> columnInfos){
generatorService.save(columnInfos);
return new ResponseEntity<>(HttpStatus.OK);
}
@ApiOperation("同步字段数据")
@PostMapping(value = "sync")
public ResponseEntity<HttpStatus> syncColumn(@RequestBody List<String> tables){
for (String table : tables) {
generatorService.sync(generatorService.getColumns(table), generatorService.query(table));
}
return new ResponseEntity<>(HttpStatus.OK);
}
@ApiOperation("生成代码")
@PostMapping(value = "/{tableName}/{type}")
public ResponseEntity<Object> generatorCode(@PathVariable String tableName, @PathVariable Integer type, HttpServletRequest request, HttpServletResponse response){
if(!generatorEnabled && type == 0){
throw new BadRequestException("此环境不允许生成代码,请选择预览或者下载查看!");
}
switch (type){
// 生成代码
case 0: generatorService.generator(genConfigService.find(tableName), generatorService.getColumns(tableName));
break;
// 预览
case 1: return generatorService.preview(genConfigService.find(tableName), generatorService.getColumns(tableName));
// 打包
case 2: generatorService.download(genConfigService.find(tableName), generatorService.getColumns(tableName), request, response);
break;
default: throw new BadRequestException("没有这个选项");
}
return new ResponseEntity<>(HttpStatus.OK);
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.service;
import com.baomidou.mybatisplus.extension.service.IService;
import me.zhengjie.domain.GenConfig;
/**
* @author Zheng Jie
* @date 2019-01-14
*/
public interface GenConfigService extends IService<GenConfig> {
/**
* 查询表配置
* @param tableName 表名
* @return 表配置
*/
GenConfig find(String tableName);
/**
* 更新表配置
* @param tableName 表名
* @param genConfig 表配置
* @return 表配置
*/
GenConfig update(String tableName, GenConfig genConfig);
}

View File

@ -1,94 +0,0 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import me.zhengjie.domain.GenConfig;
import me.zhengjie.domain.ColumnInfo;
import me.zhengjie.domain.dto.TableInfo;
import me.zhengjie.utils.PageResult;
import org.springframework.http.ResponseEntity;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* @author Zheng Jie
* @date 2019-01-02
*/
public interface GeneratorService extends IService<ColumnInfo> {
/**
* 查询数据库元数据
*
* @param name 表名
* @param page 分页参数
* @return /
*/
PageResult<TableInfo> getTables(String name, Page<Object> page);
/**
* 得到数据表的元数据
* @param name 表名
* @return /
*/
List<ColumnInfo> getColumns(String name);
/**
* 同步表数据
* @param columnInfos /
* @param columnInfoList /
*/
void sync(List<ColumnInfo> columnInfos, List<ColumnInfo> columnInfoList);
/**
* 保持数据
* @param columnInfos /
*/
void save(List<ColumnInfo> columnInfos);
/**
* 代码生成
* @param genConfig 配置信息
* @param columns 字段信息
*/
void generator(GenConfig genConfig, List<ColumnInfo> columns);
/**
* 预览
* @param genConfig 配置信息
* @param columns 字段信息
* @return /
*/
ResponseEntity<Object> preview(GenConfig genConfig, List<ColumnInfo> columns);
/**
* 打包下载
* @param genConfig 配置信息
* @param columns 字段信息
* @param request /
* @param response /
*/
void download(GenConfig genConfig, List<ColumnInfo> columns, HttpServletRequest request, HttpServletResponse response);
/**
* 查询数据库的表字段数据数据
* @param table /
* @return /
*/
List<ColumnInfo> query(String table);
}

View File

@ -1,69 +0,0 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import me.zhengjie.domain.GenConfig;
import me.zhengjie.mapper.GenConfigMapper;
import me.zhengjie.service.GenConfigService;
import org.springframework.stereotype.Service;
import java.io.File;
/**
* @author Zheng Jie
* @date 2019-01-14
*/
@Service
@RequiredArgsConstructor
@SuppressWarnings({"unchecked","all"})
public class GenConfigServiceImpl extends ServiceImpl<GenConfigMapper, GenConfig> implements GenConfigService {
private final GenConfigMapper genConfigMapper;
@Override
public GenConfig find(String tableName) {
GenConfig genConfig = genConfigMapper.findByTableName(tableName);
if(genConfig == null){
return new GenConfig(tableName);
}
return genConfig;
}
@Override
public GenConfig update(String tableName, GenConfig genConfig) {
String separator = File.separator;
String[] paths;
String symbol = "\\";
if (symbol.equals(separator)) {
paths = genConfig.getPath().split("\\\\");
} else {
paths = genConfig.getPath().split(File.separator);
}
StringBuilder api = new StringBuilder();
for (String path : paths) {
api.append(path);
api.append(separator);
if ("src".equals(path)) {
api.append("api");
break;
}
}
genConfig.setApiPath(api.toString());
saveOrUpdate(genConfig);
return genConfig;
}
}

View File

@ -1,161 +0,0 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ZipUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.domain.GenConfig;
import me.zhengjie.domain.ColumnInfo;
import me.zhengjie.domain.dto.TableInfo;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.mapper.ColumnInfoMapper;
import me.zhengjie.service.GeneratorService;
import me.zhengjie.utils.*;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author Zheng Jie
* @date 2019-01-02
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class GeneratorServiceImpl extends ServiceImpl<ColumnInfoMapper, ColumnInfo> implements GeneratorService {
private final ColumnInfoMapper columnInfoMapper;
private final String CONFIG_MESSAGE = "请先配置生成器";
@Override
public PageResult<TableInfo> getTables(String name, Page<Object> page) {
return PageUtil.toPage(columnInfoMapper.getTables(name, page));
}
@Override
@Transactional(rollbackFor = Exception.class)
public List<ColumnInfo> getColumns(String tableName) {
List<ColumnInfo> columnInfos = columnInfoMapper.findByTableNameOrderByIdAsc(tableName);
if (CollectionUtil.isNotEmpty(columnInfos)) {
return columnInfos;
} else {
columnInfos = query(tableName);
saveBatch(columnInfos);
return columnInfos;
}
}
@Override
public List<ColumnInfo> query(String tableName) {
List<ColumnInfo> columnInfos = columnInfoMapper.getColumns(tableName);
for (ColumnInfo columnInfo : columnInfos) {
columnInfo.setTableName(tableName);
if(GenUtil.PK.equalsIgnoreCase(columnInfo.getKeyType())
&& GenUtil.EXTRA.equalsIgnoreCase(columnInfo.getExtra())){
columnInfo.setNotNull(false);
}
}
return columnInfos;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void sync(List<ColumnInfo> columnInfos, List<ColumnInfo> columnInfoList) {
// 第一种情况数据库类字段改变或者新增字段
for (ColumnInfo columnInfo : columnInfoList) {
// 根据字段名称查找
List<ColumnInfo> columns = columnInfos.stream().filter(c -> c.getColumnName().equals(columnInfo.getColumnName())).collect(Collectors.toList());
// 如果能找到就修改部分可能被字段
if (CollectionUtil.isNotEmpty(columns)) {
ColumnInfo column = columns.get(0);
column.setColumnType(columnInfo.getColumnType());
column.setExtra(columnInfo.getExtra());
column.setKeyType(columnInfo.getKeyType());
if (StringUtils.isBlank(column.getRemark())) {
column.setRemark(columnInfo.getRemark());
}
saveOrUpdate(column);
} else {
// 如果找不到则保存新字段信息
save(columnInfo);
}
}
// 第二种情况数据库字段删除了
for (ColumnInfo columnInfo : columnInfos) {
// 根据字段名称查找
List<ColumnInfo> columns = columnInfoList.stream().filter(c -> c.getColumnName().equals(columnInfo.getColumnName())).collect(Collectors.toList());
// 如果找不到就代表字段被删除了则需要删除该字段
if (CollectionUtil.isEmpty(columns)) {
removeById(columnInfo);
}
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void save(List<ColumnInfo> columnInfos) {
saveOrUpdateBatch(columnInfos);
}
@Override
public void generator(GenConfig genConfig, List<ColumnInfo> columns) {
if (genConfig.getId() == null) {
throw new BadRequestException(CONFIG_MESSAGE);
}
try {
GenUtil.generatorCode(columns, genConfig);
} catch (IOException e) {
log.error(e.getMessage(), e);
throw new BadRequestException("生成失败,请手动处理已生成的文件");
}
}
@Override
public ResponseEntity<Object> preview(GenConfig genConfig, List<ColumnInfo> columns) {
if (genConfig.getId() == null) {
throw new BadRequestException(CONFIG_MESSAGE);
}
List<Map<String, Object>> genList = GenUtil.preview(columns, genConfig);
return new ResponseEntity<>(genList, HttpStatus.OK);
}
@Override
public void download(GenConfig genConfig, List<ColumnInfo> columns, HttpServletRequest request, HttpServletResponse response) {
if (genConfig.getId() == null) {
throw new BadRequestException(CONFIG_MESSAGE);
}
try {
File file = new File(GenUtil.download(columns, genConfig));
String zipPath = file.getPath() + ".zip";
ZipUtil.zip(file.getPath(), zipPath);
FileUtil.downloadFile(request, response, new File(zipPath), true);
} catch (IOException e) {
throw new BadRequestException("打包失败");
}
}
}

View File

@ -1,54 +0,0 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.utils;
import org.apache.commons.configuration.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* sql字段转java
*
* @author Zheng Jie
* @date 2019-01-03
*/
public class ColUtil {
private static final Logger log = LoggerFactory.getLogger(ColUtil.class);
/**
* 转换mysql数据类型为java数据类型
*
* @param type 数据库字段类型
* @return String
*/
static String cloToJava(String type) {
Configuration config = getConfig();
assert config != null;
return config.getString(type, "unknowType");
}
/**
* 获取配置信息
*/
public static PropertiesConfiguration getConfig() {
try {
return new PropertiesConfiguration("gen.properties");
} catch (ConfigurationException e) {
log.error(e.getMessage(), e);
}
return null;
}
}

View File

@ -1,417 +0,0 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhengjie.utils;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.template.*;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.domain.GenConfig;
import me.zhengjie.domain.ColumnInfo;
import org.springframework.util.ObjectUtils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.time.LocalDate;
import java.util.*;
import static me.zhengjie.utils.FileUtil.SYS_TEM_DIR;
/**
* 代码生成
*
* @author Zheng Jie
* @date 2019-01-02
*/
@Slf4j
@SuppressWarnings({"unchecked", "all"})
public class GenUtil {
private static final String TIMESTAMP = "Timestamp";
private static final String BIGDECIMAL = "BigDecimal";
public static final String PK = "PRI";
public static final String EXTRA = "auto_increment";
/**
* 获取后端代码模板名称
*
* @return List
*/
private static List<String> getAdminTemplateNames() {
List<String> templateNames = new ArrayList<>();
templateNames.add("Entity");
templateNames.add("Controller");
templateNames.add("QueryCriteria");
templateNames.add("Service");
templateNames.add("ServiceImpl");
templateNames.add("Mapper");
templateNames.add("Mapper-xml");
return templateNames;
}
/**
* 获取前端代码模板名称
*
* @return List
*/
private static List<String> getFrontTemplateNames() {
List<String> templateNames = new ArrayList<>();
templateNames.add("index");
templateNames.add("api");
return templateNames;
}
public static List<Map<String, Object>> preview(List<ColumnInfo> columns, GenConfig genConfig) {
Map<String, Object> genMap = getGenMap(columns, genConfig);
List<Map<String, Object>> genList = new ArrayList<>();
// 获取后端模版
List<String> templates = getAdminTemplateNames();
TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
for (String templateName : templates) {
Map<String, Object> map = new HashMap<>(1);
Template template = engine.getTemplate("admin/" + templateName + ".ftl");
map.put("content", template.render(genMap));
map.put("name", templateName.replace("-xml", ".xml"));
genList.add(map);
}
// 获取前端模版
templates = getFrontTemplateNames();
for (String templateName : templates) {
Map<String, Object> map = new HashMap<>(1);
Template template = engine.getTemplate("front/" + templateName + ".ftl");
map.put(templateName, template.render(genMap));
map.put("content", template.render(genMap));
map.put("name", templateName);
genList.add(map);
}
return genList;
}
public static String download(List<ColumnInfo> columns, GenConfig genConfig) throws IOException {
// 拼接的路径/tmpeladmin-gen-temp/这个路径在Linux下需要root用户才有权限创建,非root用户会权限错误而失败更改为 /tmp/eladmin-gen-temp/
// String tempPath =SYS_TEM_DIR + "eladmin-gen-temp" + File.separator + genConfig.getTableName() + File.separator;
String tempPath = SYS_TEM_DIR + "eladmin-gen-temp" + File.separator + genConfig.getTableName() + File.separator;
Map<String, Object> genMap = getGenMap(columns, genConfig);
TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
// 生成后端代码
List<String> templates = getAdminTemplateNames();
for (String templateName : templates) {
Template template = engine.getTemplate("admin/" + templateName + ".ftl");
String filePath = getAdminFilePath(templateName, genConfig, genMap.get("className").toString(), tempPath + "eladmin" + File.separator);
assert filePath != null;
File file = new File(filePath);
// 如果非覆盖生成
if (!genConfig.getCover() && FileUtil.exist(file)) {
continue;
}
// 生成代码
genFile(file, template, genMap);
}
// 生成前端代码
templates = getFrontTemplateNames();
for (String templateName : templates) {
Template template = engine.getTemplate("front/" + templateName + ".ftl");
String path = tempPath + "eladmin-web" + File.separator;
String apiPath = path + "src" + File.separator + "api" + File.separator;
String srcPath = path + "src" + File.separator + "views" + File.separator + genMap.get("changeClassName").toString() + File.separator;
String filePath = getFrontFilePath(templateName, apiPath, srcPath, genMap.get("changeClassName").toString());
assert filePath != null;
File file = new File(filePath);
// 如果非覆盖生成
if (!genConfig.getCover() && FileUtil.exist(file)) {
continue;
}
// 生成代码
genFile(file, template, genMap);
}
return tempPath;
}
public static void generatorCode(List<ColumnInfo> columnInfos, GenConfig genConfig) throws IOException {
Map<String, Object> genMap = getGenMap(columnInfos, genConfig);
TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
// 生成后端代码
List<String> templates = getAdminTemplateNames();
for (String templateName : templates) {
Template template = engine.getTemplate("admin/" + templateName + ".ftl");
String rootPath = System.getProperty("user.dir");
String filePath = getAdminFilePath(templateName, genConfig, genMap.get("className").toString(), rootPath);
assert filePath != null;
File file = new File(filePath);
// 如果非覆盖生成
if (!genConfig.getCover() && FileUtil.exist(file)) {
continue;
}
// 生成代码
genFile(file, template, genMap);
}
// 生成前端代码
templates = getFrontTemplateNames();
for (String templateName : templates) {
Template template = engine.getTemplate("front/" + templateName + ".ftl");
String filePath = getFrontFilePath(templateName, genConfig.getApiPath(), genConfig.getPath(), genMap.get("changeClassName").toString());
assert filePath != null;
File file = new File(filePath);
// 如果非覆盖生成
if (!genConfig.getCover() && FileUtil.exist(file)) {
continue;
}
// 生成代码
genFile(file, template, genMap);
}
}
// 获取模版数据
private static Map<String, Object> getGenMap(List<ColumnInfo> columnInfos, GenConfig genConfig) {
// 存储模版字段数据
Map<String, Object> genMap = new HashMap<>(16);
// 接口别名
genMap.put("apiAlias", genConfig.getApiAlias());
// 包名称
genMap.put("package", genConfig.getPack());
// 模块名称
genMap.put("moduleName", genConfig.getModuleName());
// 作者
genMap.put("author", genConfig.getAuthor());
// 创建日期
genMap.put("date", LocalDate.now().toString());
// 表名
genMap.put("tableName", genConfig.getTableName());
// 大写开头的类名
String className = StringUtils.toCapitalizeCamelCase(genConfig.getTableName());
// 小写开头的类名
String changeClassName = StringUtils.toCamelCase(genConfig.getTableName());
// 判断是否去除表前缀
if (StringUtils.isNotEmpty(genConfig.getPrefix())) {
className = StringUtils.toCapitalizeCamelCase(StrUtil.removePrefix(genConfig.getTableName(), genConfig.getPrefix()));
changeClassName = StringUtils.toCamelCase(StrUtil.removePrefix(genConfig.getTableName(), genConfig.getPrefix()));
changeClassName = StringUtils.uncapitalize(changeClassName);
}
// 保存类名
genMap.put("className", className);
// 保存小写开头的类名
genMap.put("changeClassName", changeClassName);
// 存在 Timestamp 字段
genMap.put("hasTimestamp", false);
// 查询类中存在 Timestamp 字段
genMap.put("queryHasTimestamp", false);
// 存在 BigDecimal 字段
genMap.put("hasBigDecimal", false);
// 查询类中存在 BigDecimal 字段
genMap.put("queryHasBigDecimal", false);
// 是否需要创建查询
genMap.put("hasQuery", false);
// 自增主键
genMap.put("auto", false);
// 存在字典
genMap.put("hasDict", false);
// 存在日期注解
genMap.put("hasDateAnnotation", false);
// 存储主键字段名
genMap.put("pkIdName", "none");
// 存储符号
genMap.put("symbol", "#");
// 保存字段信息
List<Map<String, Object>> columns = new ArrayList<>();
// 保存查询字段的信息
List<Map<String, Object>> queryColumns = new ArrayList<>();
// 存储字典信息
List<String> dicts = new ArrayList<>();
// 存储 between 信息
List<Map<String, Object>> betweens = new ArrayList<>();
// 存储不为空的字段信息
List<Map<String, Object>> isNotNullColumns = new ArrayList<>();
for (ColumnInfo column : columnInfos) {
Map<String, Object> listMap = new HashMap<>(16);
// 字段描述
listMap.put("remark", column.getRemark());
// 字段类型
listMap.put("columnKey", column.getKeyType());
// 主键类型
String colType = ColUtil.cloToJava(column.getColumnType());
// 小写开头的字段名
String changeColumnName = StringUtils.toCamelCase(column.getColumnName());
// 大写开头的字段名
String capitalColumnName = StringUtils.toCapitalizeCamelCase(column.getColumnName());
if (PK.equals(column.getKeyType())) {
// 存储主键类型
genMap.put("pkColumnType", colType);
// 存储小写开头的字段名
genMap.put("pkChangeColName", changeColumnName);
// 存储大写开头的字段名
genMap.put("pkCapitalColName", capitalColumnName);
// 存储主键字段名
genMap.put("pkIdName", column.getColumnName());
}
// 是否存在 Timestamp 类型的字段
if (TIMESTAMP.equals(colType)) {
genMap.put("hasTimestamp", true);
}
// 是否存在 BigDecimal 类型的字段
if (BIGDECIMAL.equals(colType)) {
genMap.put("hasBigDecimal", true);
}
// 主键是否自增
if (EXTRA.equals(column.getExtra())) {
genMap.put("auto", true);
}
// 主键存在字典
if (StringUtils.isNotBlank(column.getDictName())) {
genMap.put("hasDict", true);
if(!dicts.contains(column.getDictName()))
dicts.add(column.getDictName());
}
// 存储字段类型
listMap.put("columnType", colType);
// 存储字原始段名称
listMap.put("columnName", column.getColumnName());
// 不为空
listMap.put("istNotNull", column.getNotNull());
// 字段列表显示
listMap.put("columnShow", column.getListShow());
// 表单显示
listMap.put("formShow", column.getFormShow());
// 表单组件类型
listMap.put("formType", StringUtils.isNotBlank(column.getFormType()) ? column.getFormType() : "Input");
// 小写开头的字段名称
listMap.put("changeColumnName", changeColumnName);
//大写开头的字段名称
listMap.put("capitalColumnName", capitalColumnName);
// 字典名称
listMap.put("dictName", column.getDictName());
// 添加非空字段信息
if (column.getNotNull()) {
isNotNullColumns.add(listMap);
}
// 判断是否有查询如有则把查询的字段set进columnQuery
if (!StringUtils.isBlank(column.getQueryType())) {
// 查询类型
listMap.put("queryType", column.getQueryType());
// 是否存在查询
genMap.put("hasQuery", true);
if (TIMESTAMP.equals(colType)) {
// 查询中存储 Timestamp 类型
genMap.put("queryHasTimestamp", true);
}
if (BIGDECIMAL.equals(colType)) {
// 查询中存储 BigDecimal 类型
genMap.put("queryHasBigDecimal", true);
}
if ("between".equalsIgnoreCase(column.getQueryType())) {
betweens.add(listMap);
} else {
// 添加到查询列表中
queryColumns.add(listMap);
}
}
// 添加到字段列表中
columns.add(listMap);
}
// 保存字段列表
genMap.put("columns", columns);
// 保存查询列表
genMap.put("queryColumns", queryColumns);
// 保存字段列表
genMap.put("dicts", dicts);
// 保存查询列表
genMap.put("betweens", betweens);
// 保存非空字段信息
genMap.put("isNotNullColumns", isNotNullColumns);
return genMap;
}
/**
* 定义后端文件路径以及名称
*/
private static String getAdminFilePath(String templateName, GenConfig genConfig, String className, String rootPath) {
String projectPath = rootPath + File.separator + genConfig.getModuleName();
String packagePath = projectPath + File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator;
String mpXmlPath = projectPath + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator;
if (!ObjectUtils.isEmpty(genConfig.getPack())) {
packagePath += genConfig.getPack().replace(".", File.separator) + File.separator;
}
if ("Entity".equals(templateName)) {
return packagePath + "domain" + File.separator + className + ".java";
}
if ("Controller".equals(templateName)) {
return packagePath + "rest" + File.separator + className + "Controller.java";
}
if ("Service".equals(templateName)) {
return packagePath + "service" + File.separator + className + "Service.java";
}
if ("ServiceImpl".equals(templateName)) {
return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
}
if ("QueryCriteria".equals(templateName)) {
return packagePath + "domain" + File.separator + "dto" + File.separator + className + "QueryCriteria.java";
}
if ("Mapper".equals(templateName)) {
return packagePath + "mapper" + File.separator + className + "Mapper.java";
}
if ("Mapper-xml".equals(templateName)) {
return mpXmlPath + "mapper" + File.separator + className + "Mapper.xml";
}
return null;
}
/**
* 定义前端文件路径以及名称
*/
private static String getFrontFilePath(String templateName, String apiPath, String path, String apiName) {
if ("api".equals(templateName)) {
return apiPath + File.separator + apiName + ".js";
}
if ("index".equals(templateName)) {
return path + File.separator + "index.vue";
}
return null;
}
private static void genFile(File file, Template template, Map<String, Object> map) throws IOException {
// 生成目标文件
Writer writer = null;
try {
FileUtil.touch(file);
writer = new FileWriter(file);
template.render(map, writer);
} catch (TemplateException | IOException e) {
throw new RuntimeException(e);
} finally {
assert writer != null;
writer.close();
}
}
}

View File

@ -1,27 +0,0 @@
#数据库类型转Java类型
tinyint=Integer
smallint=Integer
mediumint=Integer
int=Integer
integer=Integer
bigint=Long
float=Float
double=Double
decimal=BigDecimal
bit=Boolean
char=String
varchar=String
tinytext=String
text=String
mediumtext=String
longtext=String
date=Timestamp
datetime=Timestamp
timestamp=Timestamp

View File

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="me.zhengjie.mapper.ColumnInfoMapper">
<resultMap id="BaseResultMap" type="me.zhengjie.domain.ColumnInfo">
<id column="column_id" property="id"/>
<result column="table_name" property="tableName"/>
<result column="column_name" property="columnName"/>
<result column="column_type" property="columnType"/>
<result column="key_type" property="keyType"/>
<result column="extra" property="extra"/>
<result column="remark" property="remark"/>
<result column="not_null" property="notNull"/>
<result column="list_show" property="listShow"/>
<result column="form_show" property="formShow"/>
<result column="form_type" property="formType"/>
<result column="query_type" property="queryType"/>
<result column="dict_name" property="dictName"/>
</resultMap>
<sql id="Base_Column_List">
column_id, table_name, column_name, column_type, key_type, extra, remark, not_null, list_show, form_show, form_type, query_type, dict_name
</sql>
<select id="getTables" resultType="me.zhengjie.domain.dto.TableInfo">
select table_name, create_time, engine, table_collation as coding, table_comment as remark
from information_schema.tables
where table_schema = (select database())
and table_name like concat('%',#{tableName},'%')
order by create_time desc
</select>
<select id="findByTableNameOrderByIdAsc" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from code_column
where table_name = #{tableName}
order by column_id
</select>
<select id="getColumns" resultMap="BaseResultMap">
select column_name, if(is_nullable = 'NO', 1, 0) not_null,
data_type as column_type, column_comment as remark,
column_key key_type, extra
from information_schema.columns
where table_name = #{tableName}
and table_schema = (select database())
order by ordinal_position
</select>
</mapper>

View File

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="me.zhengjie.mapper.GenConfigMapper">
<resultMap id="BaseResultMap" type="me.zhengjie.domain.GenConfig">
<id column="config_id" property="id"/>
<result column="table_name" property="tableName"/>
<result column="api_alias" property="apiAlias"/>
<result column="pack" property="pack"/>
<result column="module_name" property="moduleName"/>
<result column="path" property="path"/>
<result column="api_path" property="apiPath"/>
<result column="author" property="author"/>
<result column="prefix" property="prefix"/>
<result column="cover" property="cover"/>
</resultMap>
<sql id="Base_Column_List">
config_id, table_name, api_alias, pack, module_name, path, api_path, author, prefix, cover
</sql>
<select id="findByTableName" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM code_config
WHERE table_name = #{tableName}
</select>
</mapper>

View File

@ -1,88 +0,0 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ${package}.rest;
import me.zhengjie.annotation.Log;
import ${package}.domain.${className};
import ${package}.service.${className}Service;
import ${package}.domain.dto.${className}QueryCriteria;
import lombok.RequiredArgsConstructor;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import io.swagger.annotations.*;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import me.zhengjie.utils.PageResult;
/**
* @author ${author}
* @date ${date}
**/
@RestController
@RequiredArgsConstructor
@Api(tags = "${apiAlias}")
@RequestMapping("/api/${changeClassName}")
public class ${className}Controller {
private final ${className}Service ${changeClassName}Service;
@ApiOperation("导出数据")
@GetMapping(value = "/download")
@PreAuthorize("@el.check('${changeClassName}:list')")
public void export${className}(HttpServletResponse response, ${className}QueryCriteria criteria) throws IOException {
${changeClassName}Service.download(${changeClassName}Service.queryAll(criteria), response);
}
@GetMapping
@ApiOperation("查询${apiAlias}")
@PreAuthorize("@el.check('${changeClassName}:list')")
public ResponseEntity<PageResult<${className}>> query${className}(${className}QueryCriteria criteria){
Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize());
return new ResponseEntity<>(${changeClassName}Service.queryAll(criteria,page),HttpStatus.OK);
}
@PostMapping
@Log("新增${apiAlias}")
@ApiOperation("新增${apiAlias}")
@PreAuthorize("@el.check('${changeClassName}:add')")
public ResponseEntity<Object> create${className}(@Validated @RequestBody ${className} resources){
${changeClassName}Service.create(resources);
return new ResponseEntity<>(HttpStatus.CREATED);
}
@PutMapping
@Log("修改${apiAlias}")
@ApiOperation("修改${apiAlias}")
@PreAuthorize("@el.check('${changeClassName}:edit')")
public ResponseEntity<Object> update${className}(@Validated @RequestBody ${className} resources){
${changeClassName}Service.update(resources);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@DeleteMapping
@Log("删除${apiAlias}")
@ApiOperation("删除${apiAlias}")
@PreAuthorize("@el.check('${changeClassName}:del')")
public ResponseEntity<Object> delete${className}(@ApiParam(value = "传ID数组[]") @RequestBody List<${pkColumnType}> ids) {
${changeClassName}Service.deleteAll(ids);
return new ResponseEntity<>(HttpStatus.OK);
}
}

View File

@ -1,87 +0,0 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ${package}.domain;
import lombok.Data;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
import cn.hutool.core.bean.copier.CopyOptions;
<#if hasTimestamp>
import java.sql.Timestamp;
</#if>
<#if hasBigDecimal>
import java.math.BigDecimal;
</#if>
<#assign notBlankUsed = false>
<#assign notNullUsed = false>
<#if columns??>
<#list columns as column>
<#if column.istNotNull && column.columnKey != 'PRI'>
<#if column.columnType = 'String'>
<#assign notBlankUsed = true>
<#else>
<#assign notNullUsed = true>
</#if>
</#if>
</#list>
</#if>
<#if notBlankUsed>
import javax.validation.constraints.NotBlank;
</#if>
<#if notNullUsed>
import javax.validation.constraints.NotNull;
</#if>
import java.io.Serializable;
<#if auto>
import com.baomidou.mybatisplus.annotation.IdType;
</#if>
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
/**
* @description /
* @author ${author}
* @date ${date}
**/
@Data
@TableName("${tableName}")
public class ${className} implements Serializable {
<#if columns??>
<#list columns as column>
<#if column.columnKey = 'PRI'>
@TableId(value = "${column.columnName}"<#if auto>, type = IdType.AUTO</#if>)
</#if>
<#if column.istNotNull && column.columnKey != 'PRI'>
<#if column.columnType = 'String'>
@NotBlank
<#else>
@NotNull
</#if>
</#if>
<#if column.remark != ''>
@ApiModelProperty(value = "${column.remark}")
<#else>
@ApiModelProperty(value = "${column.changeColumnName}")
</#if>
private ${column.columnType} ${column.changeColumnName};
</#list>
</#if>
public void copy(${className} source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

View File

@ -1,62 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="${package}.mapper.${className}Mapper">
<#if columns??>
<resultMap id="BaseResultMap" type="${package}.domain.${className}">
<#list columns as column>
<#if column.columnKey = 'PRI'>
<id column="${column.columnName}" property="${column.changeColumnName}"/>
</#if>
<#if column.columnKey != 'PRI'>
<result column="${column.columnName}" property="${column.changeColumnName}"/>
</#if>
</#list>
</resultMap>
<sql id="Base_Column_List">
<#list columns as column>${column.columnName}<#if column_has_next>, </#if></#list>
</sql>
</#if>
<select id="findAll" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from ${tableName}
<#if queryColumns??>
<where>
<#list queryColumns as column>
<if test="criteria.${column.changeColumnName} != null">
<#if column.queryType = '='>
and ${column.columnName} = ${symbol}{criteria.${column.changeColumnName}}
</#if>
<#if column.queryType = 'Like'>
and ${column.columnName} like concat('%',${symbol}{criteria.${column.changeColumnName}},'%')
</#if>
<#if column.queryType = '!='>
and ${column.columnName} != ${symbol}{criteria.${column.changeColumnName}}
</#if>
<#if column.queryType = 'NotNull'>
and ${column.columnName} is not null
</#if>
<#if column.queryType = '>='>
and ${column.columnName} &gt;= ${symbol}{criteria.${column.changeColumnName}}
</#if>
<#if column.queryType = '<='>
and ${column.columnName} &lt;= ${symbol}{criteria.${column.changeColumnName}}
</#if>
</if>
</#list>
<#if betweens??>
<#list betweens as column>
<if test="criteria.${column.changeColumnName} != null and criteria.${column.changeColumnName}.size() > 0">
AND ${column.columnName} BETWEEN ${symbol}{criteria.${column.changeColumnName}[0]} AND ${symbol}{criteria.${column.changeColumnName}[1]}
</if>
</#list>
</#if>
</where>
</#if>
<#if pkIdName != 'none'>
order by ${pkIdName} desc
</#if>
</select>
</mapper>

View File

@ -1,105 +0,0 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ${package}.service.impl;
import ${package}.domain.${className};
<#if columns??>
<#list columns as column>
<#if column.columnKey = 'UNI'>
<#if column_index = 1>
import me.zhengjie.exception.EntityExistException;
</#if>
</#if>
</#list>
</#if>
import me.zhengjie.utils.FileUtil;
import lombok.RequiredArgsConstructor;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import ${package}.service.${className}Service;
import ${package}.domain.dto.${className}QueryCriteria;
import ${package}.mapper.${className}Mapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import me.zhengjie.utils.PageUtil;
import java.util.List;
import java.util.Map;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import me.zhengjie.utils.PageResult;
/**
* @description 服务实现
* @author ${author}
* @date ${date}
**/
@Service
@RequiredArgsConstructor
public class ${className}ServiceImpl extends ServiceImpl<${className}Mapper, ${className}> implements ${className}Service {
private final ${className}Mapper ${changeClassName}Mapper;
@Override
public PageResult<${className}> queryAll(${className}QueryCriteria criteria, Page<Object> page){
return PageUtil.toPage(${changeClassName}Mapper.findAll(criteria, page));
}
@Override
public List<${className}> queryAll(${className}QueryCriteria criteria){
return ${changeClassName}Mapper.findAll(criteria);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void create(${className} resources) {
${changeClassName}Mapper.insert(resources);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void update(${className} resources) {
${className} ${changeClassName} = getById(resources.get${pkCapitalColName}());
${changeClassName}.copy(resources);
${changeClassName}Mapper.updateById(${changeClassName});
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteAll(List<${pkColumnType}> ids) {
${changeClassName}Mapper.deleteBatchIds(ids);
}
@Override
public void download(List<${className}> all, HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = new ArrayList<>();
for (${className} ${changeClassName} : all) {
Map<String,Object> map = new LinkedHashMap<>();
<#list columns as column>
<#if column.columnKey != 'PRI'>
<#if column.remark != ''>
map.put("${column.remark}", ${changeClassName}.get${column.capitalColumnName}());
<#else>
map.put(" ${column.changeColumnName}", ${changeClassName}.get${column.capitalColumnName}());
</#if>
</#if>
</#list>
list.add(map);
}
FileUtil.downloadExcel(list, response);
}
}

View File

@ -1,27 +0,0 @@
import request from '@/utils/request'
export function add(data) {
return request({
url: 'api/${changeClassName}',
method: 'post',
data
})
}
export function del(ids) {
return request({
url: 'api/${changeClassName}/',
method: 'delete',
data: ids
})
}
export function edit(data) {
return request({
url: 'api/${changeClassName}',
method: 'put',
data
})
}
export default { add, edit, del }

View File

@ -1,169 +0,0 @@
<#--noinspection ALL-->
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<#if hasQuery>
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<#if queryColumns??>
<#list queryColumns as column>
<#if column.queryType != 'BetWeen'>
<label class="el-form-item-label"><#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if></label>
<el-input v-model="query.${column.changeColumnName}" clearable placeholder="<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
</#if>
</#list>
</#if>
<#if betweens??>
<#list betweens as column>
<#if column.queryType = 'BetWeen'>
<date-range-picker
v-model="query.${column.changeColumnName}"
start-placeholder="${column.changeColumnName}Start"
end-placeholder="${column.changeColumnName}Start"
class="date-item"
/>
</#if>
</#list>
</#if>
<rrOperation :crud="crud" />
</div>
</#if>
<!--如果想在工具栏加入更多按钮,可以使用插槽方式, slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
<!--表单组件-->
<el-dialog :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" :model="form" <#if isNotNullColumns??>:rules="rules"</#if> size="small" label-width="80px">
<#if columns??>
<#list columns as column>
<#if column.formShow>
<el-form-item label="<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>"<#if column.istNotNull> prop="${column.changeColumnName}"</#if>>
<#if column.formType = 'Input'>
<el-input v-model="form.${column.changeColumnName}" style="width: 370px;" />
<#elseif column.formType = 'Textarea'>
<el-input v-model="form.${column.changeColumnName}" :rows="3" type="textarea" style="width: 370px;" />
<#elseif column.formType = 'Radio'>
<#if (column.dictName)?? && (column.dictName)!="">
<el-radio v-model="form.${column.changeColumnName}" v-for="item in dict.${column.dictName}" :key="item.id" :label="item.value">{{ item.label }}</el-radio>
<#else>
未设置字典,请手动设置 Radio
</#if>
<#elseif column.formType = 'Select'>
<#if (column.dictName)?? && (column.dictName)!="">
<el-select v-model="form.${column.changeColumnName}" filterable placeholder="请选择">
<el-option
v-for="item in dict.${column.dictName}"
:key="item.id"
:label="item.label"
:value="item.value" />
</el-select>
<#else>
未设置字典,请手动设置 Select
</#if>
<#else>
<el-date-picker v-model="form.${column.changeColumnName}" type="datetime" style="width: 370px;" />
</#if>
</el-form-item>
</#if>
</#list>
</#if>
</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" size="small" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<#if columns??>
<#list columns as column>
<#if column.columnShow>
<#if (column.dictName)?? && (column.dictName)!="">
<el-table-column prop="${column.changeColumnName}" label="<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>">
<template slot-scope="scope">
{{ dict.label.${column.dictName}[scope.row.${column.changeColumnName}] }}
</template>
</el-table-column>
<#else>
<el-table-column prop="${column.changeColumnName}" label="<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>" />
</#if>
</#if>
</#list>
</#if>
<el-table-column v-if="checkPer(['admin','${changeClassName}:edit','${changeClassName}:del'])" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crud${className} from '@/api/${changeClassName}'
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'
const defaultForm = { <#if columns??><#list columns as column>${column.changeColumnName}: null<#if column_has_next>, </#if></#list></#if> }
export default {
name: '${className}',
components: { pagination, crudOperation, rrOperation, udOperation },
mixins: [presenter(), header(), form(defaultForm), crud()],
<#if hasDict>
dicts: [<#if hasDict??><#list dicts as dict>'${dict}'<#if dict_has_next>, </#if></#list></#if>],
</#if>
cruds() {
return CRUD({ title: '${apiAlias}', url: 'api/${changeClassName}', idField: '${pkChangeColName}', sort: '${pkChangeColName},desc', crudMethod: { ...crud${className} }})
},
data() {
return {
permission: {
add: ['admin', '${changeClassName}:add'],
edit: ['admin', '${changeClassName}:edit'],
del: ['admin', '${changeClassName}:del']
},
rules: {
<#if isNotNullColumns??>
<#list isNotNullColumns as column>
<#if column.istNotNull>
${column.changeColumnName}: [
{ required: true, message: '<#if column.remark != ''>${column.remark}</#if>不能为空', trigger: 'blur' }
]<#if column_has_next>,</#if>
</#if>
</#list>
</#if>
}<#if hasQuery>,
queryTypeOptions: [
<#if queryColumns??>
<#list queryColumns as column>
<#if column.queryType != 'BetWeen'>
{ key: '${column.changeColumnName}', display_name: '<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>' }<#if column_has_next>,</#if>
</#if>
</#list>
</#if>
]
</#if>
}
},
methods: {
// 钩子在获取表格数据之前执行false 则代表不获取数据
[CRUD.HOOK.beforeRefresh]() {
return true
}
}
}
</script>
<style scoped>
</style>

Some files were not shown because too many files have changed in this diff Show More