添加动态页功能

This commit is contained in:
2025-06-05 15:39:57 +08:00
parent c45e65433b
commit 377913b5d4
26 changed files with 1333 additions and 4 deletions

View File

@ -14,6 +14,15 @@
},
"dependencies": {
"@ant-design/icons-vue": "^7.0.1",
"@fortawesome/fontawesome-free": "6.7.2",
"amis": "6.12.0",
"amis-core": "6.12.0",
"amis-editor": "6.12.0",
"amis-editor-core": "6.12.0",
"amis-formula": "6.12.0",
"amis-theme-editor": "2.0.10",
"amis-theme-editor-helper": "2.0.27",
"amis-ui": "6.12.0",
"ant-design-vue": "^4.2.6",
"axios": "^1.8.4",
"dayjs": "^1.11.13",
@ -21,10 +30,16 @@
"jsencrypt": "^3.3.2",
"jwt-decode": "^4.0.0",
"lodash": "^4.17.21",
"mobx": "^4.15.7",
"mobx-react": "^6.3.1",
"mobx-state-tree": "3.17.3",
"pinia": "^3.0.2",
"pinia-plugin-persistedstate": "^4.2.0",
"qs": "^6.14.0",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"tree-lodash": "^0.4.0",
"veaury": "^2.6.2",
"vue": "^3.5.13",
"vue-request": "^2.0.4",
"vue-router": "^4.5.0"
@ -46,6 +61,7 @@
"vite": "^6.3.2",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-html": "^3.2.2",
"vite-plugin-static-copy": "^3.0.0",
"vite-plugin-vue-devtools": "^7.7.5"
}
}

View File

@ -174,7 +174,6 @@ const { data: messages } = useRequest(fetchMessage, {
.content {
height: calc(100vh - 64px - 20px);
margin: 10px;
padding: 24px;
background-color: white;
overflow-y: auto;
}

View File

@ -42,6 +42,11 @@ const router = createRouter({
meta: { needLogin: true, needMenu: true },
component: () => import('@/views/TenantView.vue'),
},
{
path: '/system/page',
meta: {needLogin: true, needMenu: true},
component: () => import('@/views/PageView.vue'),
},
{
path: '/system/logger',
meta: { needLogin: true, needMenu: true },
@ -74,6 +79,14 @@ const router = createRouter({
},
],
},
{
path: '/page/designer',
component: () => import('@/views/lowcode/AmisDesignerView.vue'),
},
{
path: '/page/render',
component: () => import('@/views/lowcode/AmisRenderView.vue'),
},
],
})

View File

@ -552,6 +552,8 @@ const roleOpt = computed({
<style lang="scss" scoped>
.account {
padding: 24px;
.ant-form {
.ant-form-item:nth-last-child(2) {
margin-right: 0;

View File

@ -522,6 +522,8 @@ const pagination = computed(() => ({
<style lang="scss" scoped>
.authority {
padding: 24px;
.ant-form {
.ant-form-item:nth-last-child(2) {
margin-right: 0;

View File

@ -242,6 +242,8 @@ const pagination = computed(() => ({
<style lang="scss" scoped>
.tenant {
padding: 24px;
.ant-form {
.ant-form-item:nth-last-child(2) {
margin-right: 0;

View File

@ -345,6 +345,8 @@ const onSelectRecipient = (option) => {
<style lang="scss" scoped>
.tenant {
padding: 24px;
.ant-form {
.ant-form-item:nth-last-child(2) {
margin-right: 0;

View File

@ -0,0 +1,391 @@
<script setup>
import qs from 'qs'
import dayjs from 'dayjs'
import axios from '@/http/api.js'
import {computed, reactive, ref} from 'vue'
import {message} from 'ant-design-vue'
import {usePagination} from 'vue-request'
import router from '@/router/index.js'
const searchRef = ref()
const editRef = ref()
const state = reactive({
search: {},
searchData: {},
edit: {},
modal: {
title: '',
show: false,
},
data: {
title: '页面数据',
show: false,
},
})
const columns = [
{
title: 'ID',
dataIndex: 'id',
sorter: true,
width: 200,
ellipsis: true,
},
{
title: '描述',
dataIndex: 'description',
maxWidth: 200,
ellipsis: true,
},
{
title: '创建人',
dataIndex: 'createBy',
maxWidth: 100,
ellipsis: true,
},
{
title: '创建时间',
dataIndex: 'createTime',
maxWidth: 150,
ellipsis: true,
customRender: ({text}) => {
return text ? dayjs(text).format('LLL') : ''
},
},
{
title: '修改时间',
dataIndex: 'updateTime',
maxWidth: 150,
ellipsis: true,
customRender: ({text}) => {
return text ? dayjs(text).format('LLL') : ''
},
},
{
title: '操作',
dataIndex: 'action',
align: 'center',
width: 230,
fixed: 'right',
},
]
const rules = {}
const onSearchRest = () => {
searchRef.value.resetFields()
onTableChange()
}
const onEditChange = (record) => {
if (record) {
Object.assign(state.edit, record)
state.modal.title = '修改'
} else {
state.edit = {}
state.modal.title = '新增'
}
state.modal.show = true
}
const onDataChange = (record) => {
state.searchData = {pageId: record.id}
onTableDataChange()
state.data.show = true
}
const fetchPage = (params) => {
return axios
.get('/db/tPage?' + qs.stringify(params, {allowDots: true}))
.then(([, res]) => res.data)
}
const fetchData = (params) => {
return axios
.get('/db/tPageData?' + qs.stringify(params, {allowDots: true}))
.then(([, res]) => res.data)
}
const submitPage = async () => {
editRef.value
.validate()
.then(() => {
if (state.edit.id) {
state.edit.content = null;
axios.put('/db/tPage', state.edit).then(([err, res]) => {
if (err || res.code !== 0) {
return message.error(err.msg || '修改数据失败')
} else {
state.modal.show = false
onTableChange()
return message.success('修改数据成功')
}
})
} else {
axios.post('/db/tPage', state.edit).then(([err, res]) => {
if (err || res.code !== 0) {
return message.error(err.msg || '保存数据失败')
} else {
state.modal.show = false
onTableChange()
return message.success('保存数据成功')
}
})
}
})
.catch(() => {
message.warning('表单校验错误')
})
}
const deletePage = (record) => {
let params = {idList: record.id}
axios.delete('/db/tPage', {params}).then(([err, res]) => {
if (err || res.code !== 0) {
return message.error(err.msg || '删除数据失败')
} else {
onTableChange()
return message.success('删除数据成功')
}
})
}
const deleteData = (record) => {
let params = {idList: record.id}
axios.delete('/db/tPageData', {params}).then(([err, res]) => {
if (err || res.code !== 0) {
return message.error(err.msg || '删除数据失败')
} else {
onTableDataChange()
return message.success('删除数据成功')
}
})
}
const {
data: page,
run,
loading,
current,
pageSize,
total,
} = usePagination(fetchPage, {
pagination: {
currentKey: 'current',
pageSizeKey: 'size',
listKey: 'records',
totalKey: 'total',
},
defaultParams: [
{
current: 1,
size: 10,
},
],
})
const {
data: data,
run: runData,
_,
current: currentData,
pageSize: pageSizeData,
total: totalData,
} = usePagination(fetchData, {
pagination: {
currentKey: 'current',
pageSizeKey: 'size',
listKey: 'records',
totalKey: 'total',
},
defaultParams: [
{
current: 1,
size: 10,
},
],
manual: true,
})
const onTableChange = (pagination, filters, sorter) => {
// 排序条件
let s = {}
if (sorter) {
s.orders = [{column: sorter?.field, asc: sorter?.order === 'ascend'}]
}
// 筛选条件
let f = {}
if (filters) {
Object.entries(filters).forEach(([key, value]) => {
if (value) {
f[key] = value.join(',')
} else {
f[key] = ''
}
})
}
run({
current: pagination?.current || current,
size: pagination?.pageSize || pageSize,
...state.search,
...s,
...f,
})
}
const onTableDataChange = (paginationData, filters, sorter) => {
// 排序条件
let s = {}
if (sorter) {
s.orders = [{column: sorter?.field, asc: sorter?.order === 'ascend'}]
}
// 筛选条件
let f = {}
if (filters) {
Object.entries(filters).forEach(([key, value]) => {
if (value) {
f[key] = value.join(',')
} else {
f[key] = ''
}
})
}
runData({
current: paginationData?.current || current,
size: paginationData?.pageSize || pageSize,
...state.searchData,
...s,
...f,
})
}
const pagination = computed(() => ({
current: current.value,
pageSize: pageSize.value,
total: total.value,
}))
const paginationData = computed(() => ({
current: currentData.value,
pageSize: pageSizeData.value,
total: totalData.value,
}))
const designCustom = (record) => {
if (record?.id) {
router.push({path: '/page/designer', query: {qid: record.id}})
} else {
router.push({path: '/page/designer'})
}
}
const renderCustom = (record) => {
if (record?.pageId && record?.id) {
router.push({
path: '/page/render',
query: {qid: record.pageId, aid: record.id},
})
} else if (record.id) {
router.push({path: '/page/render', query: {qid: record.id}})
} else {
router.push({path: '/page/render'})
}
}
</script>
<template>
<div class="custom">
<a-form ref="searchRef" :labelCol="{ flex: '80px' }" :model="state.search" layout="inline">
<a-form-item label="描述" name="description">
<a-input v-model:value="state.search.description" placeholder="请输入描述"/>
</a-form-item>
<a-form-item>
<a-button html-type="submit" type="primary" @click="onTableChange()">查询</a-button>
<a-button html-type="reset" style="margin-left: 10px" @click="onSearchRest">重置</a-button>
</a-form-item>
<a-form-item>
<a-button v-hasAuth="['system:account:post']" type="primary" @click="designCustom()"
>新增
</a-button>
</a-form-item>
</a-form>
<a-table
:columns="columns"
:data-source="page?.records"
:loading="loading"
:pagination="pagination"
:scroll="{ x: '100%' }"
@change="onTableChange"
>
<template v-slot:bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'action'">
<a-button type="link" @click="renderCustom(record)">查看</a-button>
<a-button type="link" @click="designCustom(record)">设计</a-button>
<a-button type="link" @click="onDataChange(record)">数据</a-button>
<a-button v-hasAuth="['system:account:put']" type="link" @click="onEditChange(record)"
>修改
</a-button>
<a-popconfirm title="确定要删除这条记录吗?" @confirm="deletePage(record)">
<a-button v-hasAuth="['system:logger:del']" danger type="link">删除</a-button>
</a-popconfirm>
</template>
</template>
</a-table>
<!-- 弹出框 -->
<a-modal v-model:open="state.modal.show" :title="state.modal.title" @ok="submitPage">
<a-form ref="editRef" :labelCol="{ flex: '80px' }" :model="state.edit" :rules="rules">
<a-form-item label="描述" name="description">
<a-textarea v-model:value="state.edit.description" placeholder="请输入描述"/>
</a-form-item>
</a-form>
</a-modal>
<a-modal v-model:open="state.data.show" :title="state.data.title" width="80%">
<a-table
:columns="columns"
:data-source="data?.records"
:pagination="paginationData"
:scroll="{ x: '100%' }"
@change="onTableDataChange"
>
<template v-slot:bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'action'">
<a-button type="link" @click="renderCustom(record)">查看</a-button>
<a-popconfirm title="确定要删除这条记录吗?" @confirm="deleteData(record)">
<a-button v-hasAuth="['system:logger:del']" danger type="link">删除</a-button>
</a-popconfirm>
</template>
</template>
</a-table>
</a-modal>
</div>
</template>
<style lang="scss" scoped>
.custom {
padding: 24px;
.ant-form {
.ant-form-item:nth-last-child(2) {
margin-right: 0;
}
.ant-form-item:last-child {
margin-left: auto;
margin-right: 0;
}
}
.ant-btn-link {
padding: 0 4px;
}
:deep(.ant-form-item) {
margin-bottom: 10px;
.ant-row {
flex-flow: nowrap;
}
}
}
</style>

View File

@ -501,6 +501,8 @@ const treeAuth = computed(() => fromArray(authorityOpts.value, { parentKey: 'par
<style lang="scss" scoped>
.role {
padding: 24px;
.ant-form {
.ant-form-item:nth-last-child(2) {
margin-right: 0;

View File

@ -406,6 +406,8 @@ const tenantOpt = computed({
<style lang="scss" scoped>
.tenant {
padding: 24px;
.ant-form {
.ant-form-item:nth-last-child(2) {
margin-right: 0;

View File

@ -0,0 +1,101 @@
<script setup>
import { DesktopOutlined, MobileOutlined } from '@ant-design/icons-vue'
// 引入一些样式依赖
import 'amis-ui/lib/themes/default.css'
import 'amis-ui/lib/themes/antd.css'
import 'amis-editor-core/lib/style.css'
import { applyReactInVue } from 'veaury'
import { Editor } from 'amis-editor' //引入编辑器
import { ref, shallowRef } from 'vue'
import axios from '@/http/api.js'
import { useRequest } from 'vue-request'
import { message } from 'ant-design-vue'
import { useRoute } from 'vue-router'
const qid = useRoute().query.qid
const AmisEditor = applyReactInVue(Editor) //使用编辑器
const previewModel = ref(false) //是否预览,实际开发中如果需要编辑和预览可以写一个change事件来改变这个值的状态
const mobileModel = ref(false) //是否是手机模式
const schema = shallowRef({}) //渲染表单的内容
const editorChanged = (value) => {
schema.value = value
}
// 加载页面结构
const fetchSchema = async (params) => {
if (qid) {
let [err, res] = await axios.get('/db/tPage/' + params.id)
if (err || res.code !== 0) {
message.error(err.msg || '获取页面结构失败')
} else {
schema.value = res.data?.content || {}
}
}
}
useRequest(fetchSchema, { defaultParams: [{ id: qid }] })
// 保存或新增页面结构
const submitSchema = async () => {
if (qid) {
let [err, res] = await axios.put('/db/tPage', { id: qid, content: schema.value })
if (err || res.code !== 0) {
message.error(err.msg || '更新页面结构失败')
} else {
message.success('更新页面结构成功')
}
} else {
let [err, res] = await axios.post('/db/tPage', { content: schema.value })
if (err || res.code !== 0) {
message.error(err.msg || '保存页面结构失败')
} else {
message.success('保存页面结构成功')
}
}
}
</script>
<template>
<a-page-header class="header" title="设计器" @back="() => $router.go(-1)">
<template #extra>
<a-radio-group v-model:value="mobileModel">
<a-radio-button :value="false">
<DesktopOutlined />
</a-radio-button>
<a-radio-button :value="true">
<MobileOutlined />
</a-radio-button>
</a-radio-group>
<a-button v-if="previewModel" @click="previewModel = false">编辑</a-button>
<a-space v-else>
<a-button @click="previewModel = true">预览</a-button>
<a-button type="primary" @click="submitSchema">保存</a-button>
</a-space>
</template>
</a-page-header>
<AmisEditor
theme="antd"
className="amis-editor"
:preview="previewModel"
:isMobile="mobileModel"
:value="schema"
:onChange="editorChanged"
/>
</template>
<style lang="scss" scoped>
:deep(.amis-editor) {
height: calc(100vh - 64px - 20px - 51px);
}
.header {
border-bottom: 1px solid #e8e9eb;
padding: 5px 10px;
}
</style>

View File

@ -0,0 +1,81 @@
<template>
<a-page-header class="header" title="渲染器" @back="() => $router.go(-1)">
<template #extra>
<a-popover>
<template #content>
<a-qrcode ref="qrcodeCanvasRef" :value="qrcode" />
</template>
<a-button @click="dowloadChange">二维码</a-button>
</a-popover>
</template>
</a-page-header>
<a-result v-if="loading" status="warning" :title="tips" />
<div id="root" />
</template>
<script setup>
import 'amis/sdk/sdk.js'
import 'amis-ui/lib/themes/default.css'
import 'amis-ui/lib/themes/antd.css'
import { computed, ref, shallowRef } from 'vue'
import axios from '@/http/api.js'
import { useRoute } from 'vue-router'
const qid = useRoute().query.qid
const aid = useRoute().query.aid
const loading = ref(true)
const tips = ref('加载中,请稍后...')
const qrcode = computed(() => {
return window.location.href
})
const qrcodeCanvasRef = ref()
const dowloadChange = async () => {
const url = await qrcodeCanvasRef.value.toDataURL()
const a = document.createElement('a')
a.download = 'QRCode.png'
a.href = url
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
}
// eslint-disable-next-line no-undef
const amis = amisRequire('amis/embed')
const schema = shallowRef({}) //渲染页面的结构
// 加载页面结构
const fetchSchema = qid ? axios.get('/db/tPage/' + qid) : Promise.resolve(null)
const data = shallowRef({}) //渲染页面的数据
// 加载页面数据
const fetchData = aid ? axios.get('/db/tPageData/' + aid) : Promise.resolve(null)
Promise.all([fetchSchema, fetchData]).then((res) => {
if (res[0] && res[0][1].code === 0) {
schema.value = res[0][1].data?.content || {}
}
if (res[1] && res[1][1].code === 0) {
data.value = res[1][1].data?.content || {}
}
amis.embed('#root', schema.value, data.value, {
theme: 'antd',
tracker: (eventTrack) => {
if (eventTrack.eventType === 'pageLoaded') {
if (Object.keys(schema.value).length === 0) {
tips.value = '空页面'
} else {
loading.value = false
}
}
},
})
})
</script>
<style lang="scss" scoped>
.header {
border-bottom: 1px solid #e8e9eb;
padding: 5px 10px;
}
</style>

View File

@ -10,11 +10,18 @@ import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
import { createHtmlPlugin } from 'vite-plugin-html'
import viteCompression from 'vite-plugin-compression'
import { viteStaticCopy } from 'vite-plugin-static-copy'
// https://vite.dev/config/
export default defineConfig(({ mode }) => {
// eslint-disable-next-line no-undef
const env = loadEnv(mode, process.cwd())
// 生产环境复制 Amis 的 SDK开发环境按需引用
let copes = [{ src: 'node_modules/amis/sdk/*', dest: 'assets' }]
if (mode === 'development')
copes.push({ src: 'node_modules/amis/sdk/*', dest: 'node_modules/.vite/deps' })
return {
base: './',
build: {
@ -70,6 +77,7 @@ export default defineConfig(({ mode }) => {
},
},
}),
viteStaticCopy({ targets: copes }),
],
server: {
proxy: {

View File

@ -27,6 +27,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/cn.dev33/sa-token-spring-boot3-starter -->
<dependency>
@ -111,7 +115,7 @@
<dependency>
<groupId>ltd.llvy</groupId>
<artifactId>handler</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0.2</version>
</dependency>
{% elif cookiecutter.platform == "mysql" -%}
<dependency>

View File

@ -0,0 +1,165 @@
package {{ cookiecutter.__mvn_package }}.rest.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import java.io.Serializable;
import java.util.List;
import {{ cookiecutter.__mvn_package }}.rest.api.ApiController;
import {{ cookiecutter.__mvn_package }}.rest.api.R;
import {{ cookiecutter.__mvn_package }}.rest.entity.Page;
import {{ cookiecutter.__mvn_package }}.rest.service.PageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.*;
/**
* 页面表(Page)表控制层.
*
* @author zweiandlen
* @since 2025-05-26 14:08:36
*/
@Tag(name = "标准接口", description = "由表结构自动生成")
@RestController
@RequestMapping("db/tPage")
public class PageController extends ApiController {
/** 服务对象. */
private final PageService tPageService;
public PageController(PageService tPageService) {
this.tPageService = tPageService;
}
/**
* 分页查询所有数据.
*
* @param page 分页对象
* @param tPage 查询实体
* @return 分页数据
*/
@Operation(
summary = "页面表 - 分页查询所有数据",
parameters = {
@Parameter(name = "page", description = "分页对象"),
@Parameter(name = "tPage", description = "查询实体"),
},
responses = @ApiResponse(description = "分页数据"),
description = "size=-1时查询所有数据orders配合asc排序")
@GetMapping
@SaCheckPermission("db:rest:get")
public R selectAll(@Nullable PageDTO<Page> page, @Nullable Page tPage) {
return success(this.tPageService.page(page, new QueryWrapper<>(tPage)));
}
/**
* 通过主键查询单条数据.
*
* @param id 主键
* @return 单条数据
*/
@Operation(
summary = "页面表 - 通过主键查询单条数据",
parameters = {
@Parameter(name = "id", description = "主键", schema = @Schema(type = "string")),
},
responses = @ApiResponse(description = "单条数据"))
@GetMapping("{id}")
@SaCheckPermission("db:rest:get")
public R selectOne(@PathVariable Serializable id) {
return success(this.tPageService.getById(id));
}
/**
* 新增数据.
*
* @param page 实体对象
* @return 新增结果
*/
@Operation(
summary = "页面表 - 新增数据",
parameters = {
@Parameter(name = "page", description = "实体对象"),
},
responses = @ApiResponse(description = "新增结果"))
@PostMapping
@SaCheckPermission("db:rest:post")
public R insert(@RequestBody Page page) {
return success(this.tPageService.save(page));
}
/**
* 批量新增数据.
*
* @param pages 实体对象
* @return 新增结果
*/
@Operation(
summary = "页面表 - 批量新增数据",
parameters = {
@Parameter(name = "pages", description = "实体对象"),
},
responses = @ApiResponse(description = "新增结果"))
@PostMapping("s")
@SaCheckPermission("db:rest:post")
public R inserts(@RequestBody List<Page> pages) {
return success(this.tPageService.saveBatch(pages));
}
/**
* 修改数据.
*
* @param page 实体对象
* @return 修改结果
*/
@Operation(
summary = "页面表 - 修改数据",
parameters = {@Parameter(name = "page", description = "实体对象")},
responses = @ApiResponse(description = "修改结果"),
description = "不存在的对象会新增")
@PutMapping
@SaCheckPermission("db:rest:put")
public R update(@RequestBody Page page) {
return success(this.tPageService.saveOrUpdate(page));
}
/**
* 批量修改数据.
*
* @param pages 实体对象
* @return 修改结果
*/
@Operation(
summary = "页面表 - 批量修改数据",
parameters = {@Parameter(name = "pages", description = "实体对象")},
responses = @ApiResponse(description = "修改结果"),
description = "不存在的对象会新增")
@PutMapping("s")
@SaCheckPermission("db:rest:put")
public R updates(@RequestBody List<Page> pages) {
return success(this.tPageService.saveOrUpdateBatch(pages));
}
/**
* 删除数据.
*
* @param idList 主键结合
* @return 删除结果
*/
@Operation(
summary = "页面表 - 删除数据",
parameters = {
@Parameter(name = "idList", description = "主键结合", schema = @Schema(type = "string"))
},
responses = @ApiResponse(description = "删除结果"),
description = "主键用逗号拼接")
@DeleteMapping
@SaCheckPermission("db:rest:del")
public R delete(@RequestParam("idList") List<Long> idList) {
return success(this.tPageService.removeByIds(idList));
}
}

View File

@ -0,0 +1,164 @@
package {{ cookiecutter.__mvn_package }}.rest.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import {{ cookiecutter.__mvn_package }}.rest.api.ApiController;
import {{ cookiecutter.__mvn_package }}.rest.api.R;
import {{ cookiecutter.__mvn_package }}.rest.entity.PageData;
import {{ cookiecutter.__mvn_package }}.rest.service.PageDataService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.io.Serializable;
import java.util.List;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.*;
/**
* 页面取值表(PageData)表控制层.
*
* @author zweiandlen
* @since 2025-05-26 14:08:35
*/
@Tag(name = "标准接口", description = "由表结构自动生成")
@RestController
@RequestMapping("db/tPageData")
public class PageDataController extends ApiController {
/** 服务对象. */
private final PageDataService tPageDataService;
public PageDataController(PageDataService tPageDataService) {
this.tPageDataService = tPageDataService;
}
/**
* 分页查询所有数据.
*
* @param page 分页对象
* @param pageData 查询实体
* @return 分页数据
*/
@Operation(
summary = "页面取值表 - 分页查询所有数据",
parameters = {
@Parameter(name = "page", description = "分页对象"),
@Parameter(name = "pageData", description = "查询实体"),
},
responses = @ApiResponse(description = "分页数据"),
description = "size=-1时查询所有数据orders配合asc排序")
@GetMapping
@SaCheckPermission("db:rest:get")
public R selectAll(@Nullable PageDTO<PageData> page, @Nullable PageData pageData) {
return success(this.tPageDataService.page(page, new QueryWrapper<>(pageData)));
}
/**
* 通过主键查询单条数据.
*
* @param id 主键
* @return 单条数据
*/
@Operation(
summary = "页面取值表 - 通过主键查询单条数据",
parameters = {
@Parameter(name = "id", description = "主键", schema = @Schema(type = "string")),
},
responses = @ApiResponse(description = "单条数据"))
@GetMapping("{id}")
@SaCheckPermission("db:rest:get")
public R selectOne(@PathVariable Serializable id) {
return success(this.tPageDataService.getById(id));
}
/**
* 新增数据.
*
* @param pageData 实体对象
* @return 新增结果
*/
@Operation(
summary = "页面取值表 - 新增数据",
parameters = {
@Parameter(name = "pageData", description = "实体对象"),
},
responses = @ApiResponse(description = "新增结果"))
@PostMapping
@SaCheckPermission("db:rest:post")
public R insert(@RequestBody PageData pageData) {
return success(this.tPageDataService.save(pageData));
}
/**
* 批量新增数据.
*
* @param pageData 实体对象
* @return 新增结果
*/
@Operation(
summary = "页面取值表 - 批量新增数据",
parameters = {
@Parameter(name = "pageData", description = "实体对象"),
},
responses = @ApiResponse(description = "新增结果"))
@PostMapping("s")
@SaCheckPermission("db:rest:post")
public R inserts(@RequestBody List<PageData> pageData) {
return success(this.tPageDataService.saveBatch(pageData));
}
/**
* 修改数据.
*
* @param pageData 实体对象
* @return 修改结果
*/
@Operation(
summary = "页面取值表 - 修改数据",
parameters = {@Parameter(name = "pageData", description = "实体对象")},
responses = @ApiResponse(description = "修改结果"),
description = "不存在的对象会新增")
@PutMapping
@SaCheckPermission("db:rest:put")
public R update(@RequestBody PageData pageData) {
return success(this.tPageDataService.saveOrUpdate(pageData));
}
/**
* 批量修改数据.
*
* @param pageData 实体对象
* @return 修改结果
*/
@Operation(
summary = "页面取值表 - 批量修改数据",
parameters = {@Parameter(name = "pageData", description = "实体对象")},
responses = @ApiResponse(description = "修改结果"),
description = "不存在的对象会新增")
@PutMapping("s")
@SaCheckPermission("db:rest:put")
public R updates(@RequestBody List<PageData> pageData) {
return success(this.tPageDataService.saveOrUpdateBatch(pageData));
}
/**
* 删除数据.
*
* @param idList 主键结合
* @return 删除结果
*/
@Operation(
summary = "页面取值表 - 删除数据",
parameters = {
@Parameter(name = "idList", description = "主键结合", schema = @Schema(type = "string"))
},
responses = @ApiResponse(description = "删除结果"),
description = "主键用逗号拼接")
@DeleteMapping
@SaCheckPermission("db:rest:del")
public R delete(@RequestParam("idList") List<Long> idList) {
return success(this.tPageDataService.removeByIds(idList));
}
}

View File

@ -0,0 +1,15 @@
package {{ cookiecutter.__mvn_package }}.rest.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import {{ cookiecutter.__mvn_package }}.rest.entity.Page;
import {{ cookiecutter.__mvn_package }}.web.MybatisCache;
import org.apache.ibatis.annotations.CacheNamespace;
/**
* 页面表(Page)表数据库访问层.
*
* @author zweiandlen
* @since 2025-05-26 14:08:36
*/
@CacheNamespace(implementation = MybatisCache.class, eviction = MybatisCache.class)
public interface PageDao extends BaseMapper<Page> {}

View File

@ -0,0 +1,12 @@
package {{ cookiecutter.__mvn_package }}.rest.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import {{ cookiecutter.__mvn_package }}.rest.entity.PageData;
/**
* 页面取值表(PageData)表数据库访问层.
*
* @author zweiandlen
* @since 2025-05-26 14:08:35
*/
public interface PageDataDao extends BaseMapper<PageData> {}

View File

@ -0,0 +1,100 @@
package {{ cookiecutter.__mvn_package }}.rest.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.time.OffsetDateTime;
import lombok.Data;
import lombok.EqualsAndHashCode;
{% if cookiecutter.platform == "postgis" -%}
import ltd.llvy.postgis.handler.JsonbTypeHandler;
{% endif %}
import org.apache.ibatis.type.JdbcType;
/**
* 页面表(Page)表实体类.
*
* @author zweiandlen
* @since 2025-05-26 14:08:36
*/
@Schema(description = "页面表")
@EqualsAndHashCode(callSuper = true)
@TableName(autoResultMap = true)
@Data
public class Page extends Model<Page> {
// ID
@Schema(description = "ID", type = "string")
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
// 内容
@Schema(description = "内容")
{% if cookiecutter.platform == "postgis" -%}
@TableField(typeHandler = JsonbTypeHandler.class)
{% else %}
@TableField(typeHandler = JacksonTypeHandler.class)
{% endif %}
private JsonNode content;
// 描述
@Schema(description = "描述")
private String description;
// 乐观锁
@Schema(description = "乐观锁")
@Version
private Integer version;
// 逻辑删除
@Schema(description = "逻辑删除")
@TableLogic
private Long deleteTime;
// 创建时间
@Schema(description = "创建时间")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
{% if cookiecutter.platform == "postgis" -%}
@TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.OTHER)
{% else %}
@TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.DATETIMEOFFSET)
{% endif %}
private OffsetDateTime createTime;
// 修改时间
@Schema(description = "修改时间")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
{% if cookiecutter.platform == "postgis" -%}
@TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.OTHER)
{% else %}
@TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.DATETIMEOFFSET)
{% endif %}
private OffsetDateTime updateTime;
// 创建人
@Schema(description = "创建人")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@TableField(fill = FieldFill.INSERT)
private String createBy;
// 修改人
@Schema(description = "修改人")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;
/**
* 获取主键值.
*
* @return 主键值
*/
@Override
public Serializable pkVal() {
return this.id;
}
}

View File

@ -0,0 +1,105 @@
package {{ cookiecutter.__mvn_package }}.rest.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.time.OffsetDateTime;
import lombok.Data;
import lombok.EqualsAndHashCode;
{% if cookiecutter.platform == "postgis" -%}
import ltd.llvy.postgis.handler.JsonbTypeHandler;
{% endif %}
import org.apache.ibatis.type.JdbcType;
/**
* 页面取值表(PageData)表实体类.
*
* @author zweiandlen
* @since 2025-05-26 14:08:35
*/
@Schema(description = "页面取值表")
@EqualsAndHashCode(callSuper = true)
@TableName(autoResultMap = true)
@Data
public class PageData extends Model<PageData> {
// ID
@Schema(description = "ID", type = "string")
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
// 页面ID
@Schema(description = "页面ID", type = "string")
@JsonSerialize(using = ToStringSerializer.class)
private Long pageId;
// 内容
@Schema(description = "内容")
{% if cookiecutter.platform == "postgis" -%}
@TableField(typeHandler = JsonbTypeHandler.class)
{% else %}
@TableField(typeHandler = JacksonTypeHandler.class)
{% endif %}
private JsonNode content;
// 描述
@Schema(description = "描述")
private String description;
// 乐观锁
@Schema(description = "乐观锁")
@Version
private Integer version;
// 逻辑删除
@Schema(description = "逻辑删除")
@TableLogic
private Long deleteTime;
// 创建时间
@Schema(description = "创建时间")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
{% if cookiecutter.platform == "postgis" -%}
@TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.OTHER)
{% else %}
@TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.DATETIMEOFFSET)
{% endif %}
private OffsetDateTime createTime;
// 修改时间
@Schema(description = "修改时间")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
{% if cookiecutter.platform == "postgis" -%}
@TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.OTHER)
{% else %}
@TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.DATETIMEOFFSET)
{% endif %}
private OffsetDateTime updateTime;
// 创建人
@Schema(description = "创建人")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@TableField(fill = FieldFill.INSERT)
private String createBy;
// 修改人
@Schema(description = "修改人")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;
/**
* 获取主键值.
*
* @return 主键值
*/
@Override
public Serializable pkVal() {
return this.id;
}
}

View File

@ -0,0 +1,12 @@
package {{ cookiecutter.__mvn_package }}.rest.service;
import com.baomidou.mybatisplus.extension.service.IService;
import {{ cookiecutter.__mvn_package }}.rest.entity.PageData;
/**
* 页面取值表(PageData)表服务接口.
*
* @author zweiandlen
* @since 2025-05-26 14:08:35
*/
public interface PageDataService extends IService<PageData> {}

View File

@ -0,0 +1,12 @@
package {{ cookiecutter.__mvn_package }}.rest.service;
import com.baomidou.mybatisplus.extension.service.IService;
import {{ cookiecutter.__mvn_package }}.rest.entity.Page;
/**
* 页面表(Page)表服务接口.
*
* @author zweiandlen
* @since 2025-05-26 14:08:36
*/
public interface PageService extends IService<Page> {}

View File

@ -0,0 +1,17 @@
package {{ cookiecutter.__mvn_package }}.rest.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import {{ cookiecutter.__mvn_package }}.rest.dao.PageDataDao;
import {{ cookiecutter.__mvn_package }}.rest.entity.PageData;
import {{ cookiecutter.__mvn_package }}.rest.service.PageDataService;
import org.springframework.stereotype.Service;
/**
* 页面取值表(PageData)表服务实现类.
*
* @author zweiandlen
* @since 2025-05-26 14:08:35
*/
@Service("tPageDataService")
public class PageDataServiceImpl extends ServiceImpl<PageDataDao, PageData>
implements PageDataService {}

View File

@ -0,0 +1,16 @@
package {{ cookiecutter.__mvn_package }}.rest.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import {{ cookiecutter.__mvn_package }}.rest.dao.PageDao;
import {{ cookiecutter.__mvn_package }}.rest.entity.Page;
import {{ cookiecutter.__mvn_package }}.rest.service.PageService;
import org.springframework.stereotype.Service;
/**
* 页面表(Page)表服务实现类.
*
* @author zweiandlen
* @since 2025-05-26 14:08:36
*/
@Service("tPageService")
public class PageServiceImpl extends ServiceImpl<PageDao, Page> implements PageService {}

View File

@ -172,6 +172,10 @@ INSERT INTO authority (id, parent_id, name, value, `path`, link, `type`, status,
create_time, update_time, create_by, update_by, tenant)
VALUES (29, 0, '导出表格', 'export:excel', '', '', 0, 0, '表格导出', 0, 0, '1970-01-01 00:00:00', NULL, '超级管理员',
NULL, '');
INSERT INTO authority (id, parent_id, name, value, `path`, link, `type`, status, description, version, delete_time,
create_time, update_time, create_by, update_by, tenant)
VALUES (30, 6, '动态页', 'system:page', '/system/page', NULL, 1, 0, '系统管理 / 动态页', 1, 0, '1970-01-01 00:00:00',
NULL, '超级管理员', NULL, '');
-- logger definition
@ -282,3 +286,40 @@ CREATE TABLE `tenant`
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci COMMENT ='租户表';
-- design.page definition
CREATE TABLE `page`
(
`id` bigint NOT NULL COMMENT 'ID',
`content` json DEFAULT NULL COMMENT '内容',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '描述',
`version` int NOT NULL DEFAULT '0' COMMENT '乐观锁',
`delete_time` bigint NOT NULL DEFAULT '0' COMMENT '逻辑删除',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
`create_by` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
`update_by` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci COMMENT ='页面表';
-- design.page_data definition
CREATE TABLE `page_data`
(
`id` bigint NOT NULL COMMENT 'ID',
`page_id` bigint NOT NULL COMMENT '页面ID',
`content` json DEFAULT NULL COMMENT '内容',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '描述',
`version` int NOT NULL DEFAULT '0' COMMENT '乐观锁',
`delete_time` bigint NOT NULL DEFAULT '0' COMMENT '逻辑删除',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
`create_by` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
`update_by` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '修改人',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci COMMENT ='页面取值表';

View File

@ -176,7 +176,10 @@ INSERT INTO authority (id, parent_id, "name", value, "path", link, "type", statu
create_time, update_time, create_by, update_by, tenant)
VALUES (29, 0, '导出表格', 'export:excel', '', '', 0, 0, '表格导出', 0, 0, '1970-01-01 00:00:00.000', NULL,
'超级管理员', '', '');
INSERT INTO authority (id, parent_id, "name", value, "path", link, "type", status, description, "version", delete_time,
create_time, update_time, create_by, update_by, tenant)
VALUES (30, 6, '动态页', 'system:page', '/system/page', NULL, 1, 0, '系统管理 / 动态页', 0, 0,
'1970-01-01 00:00:00.000', NULL, '超级管理员', NULL, '');
-- public.logger definition
@ -298,3 +301,45 @@ CREATE TABLE tenant
CONSTRAINT t_organization_pk PRIMARY KEY (id),
CONSTRAINT t_organization_unique_name UNIQUE (name)
);
-- public.page definition
-- Drop table
-- DROP TABLE page;
CREATE TABLE page
(
id int8 NOT NULL,
"content" jsonb NULL,
description text NULL,
"version" int4 DEFAULT 0 NOT NULL,
delete_time int8 DEFAULT 0 NOT NULL,
create_time timestamptz NULL,
update_time timestamptz NULL,
create_by varchar(100) NULL,
update_by varchar(100) NULL,
CONSTRAINT page_pk PRIMARY KEY (id)
);
-- public.page_data definition
-- Drop table
-- DROP TABLE page_data;
CREATE TABLE page_data
(
id int8 NOT NULL,
page_id int8 NOT NULL,
"content" jsonb NULL,
description text NULL,
"version" int4 DEFAULT 0 NOT NULL,
delete_time int8 DEFAULT 0 NOT NULL,
create_time timestamptz NULL,
update_time timestamptz NULL,
create_by varchar(100) NULL,
update_by varchar(100) NULL,
CONSTRAINT page_data_pk PRIMARY KEY (id)
);