From 78314c7574a880a1bccbdf9a8a531d3cf025a6b4 Mon Sep 17 00:00:00 2001 From: 康鲁杰 <60095866+KangLujie@users.noreply.github.com> Date: 星期二, 24 六月 2025 10:13:28 +0800 Subject: [PATCH] 页面设计前后端 --- ruoyi-modules/sc-page-designer/pom.xml | 4 ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerVo.java | 28 + ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerDTO.java | 29 + ruoyi-ui/apps/web-antd/src/views/tool/page-designer/page-drawer.vue | 321 ++++++++++++++++++ ruoyi-ui/apps/web-antd/src/views/tool/page-designer/data.tsx | 113 ++++++ ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/PageDesignerService.java | 29 + ruoyi-ui/apps/web-antd/src/api/tool/page-designer/index.ts | 52 +++ ruoyi-modules/sc-page-designer/src/main/resources/mapper/SysUserMapper.xml | 27 + ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/controller/PageDesignerController.java | 72 ++++ ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/impl/PageDesignerServiceImpl.java | 97 +++++ ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesigner.java | 38 ++ ruoyi-ui/apps/web-antd/src/api/tool/page-designer/model.d.ts | 21 + ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/mapper/PageDesignerMapper.java | 20 + ruoyi-ui/apps/web-antd/src/views/tool/page-designer/index.vue | 151 ++++++++ 14 files changed, 997 insertions(+), 5 deletions(-) diff --git a/ruoyi-modules/sc-page-designer/pom.xml b/ruoyi-modules/sc-page-designer/pom.xml index c77f98f..400f53d 100644 --- a/ruoyi-modules/sc-page-designer/pom.xml +++ b/ruoyi-modules/sc-page-designer/pom.xml @@ -20,5 +20,9 @@ <groupId>org.ruoyi</groupId> <artifactId>ruoyi-common-core</artifactId> </dependency> + <dependency> + <groupId>org.ruoyi</groupId> + <artifactId>ruoyi-system-api</artifactId> + </dependency> </dependencies> </project> diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/controller/PageDesignerController.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/controller/PageDesignerController.java new file mode 100644 index 0000000..16fad07 --- /dev/null +++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/controller/PageDesignerController.java @@ -0,0 +1,72 @@ +package org.ruoyi.pageDesigner.controller; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.ruoyi.common.core.domain.R; +import org.ruoyi.common.web.core.BaseController; +import org.ruoyi.core.page.PageQuery; +import org.ruoyi.core.page.TableDataInfo; +import org.ruoyi.pageDesigner.domain.PageDesigner; +import org.ruoyi.pageDesigner.domain.PageDesignerDTO; +import org.ruoyi.pageDesigner.domain.PageDesignerVo; +import org.ruoyi.pageDesigner.service.PageDesignerService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author kanglujie + * @date 2025-06-23 14:41:24 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/page-designer") +public class PageDesignerController extends BaseController { + + private final PageDesignerService pageDesignerService; + + /** + * 鑾峰彇椤甸潰璁捐鍒楄〃 + */ + @GetMapping("/list") + public TableDataInfo<PageDesignerVo> list(PageDesignerDTO pageDesignerDTO, PageQuery pageQuery) { + return pageDesignerService.selectPagelistAll(pageDesignerDTO, pageQuery); + } + + /** + * 鑾峰彇椤甸潰璁捐璇︽儏 + */ + @GetMapping("/{id}") + public R<PageDesigner> getInfo(@PathVariable Long id) { + return R.ok(pageDesignerService.getDetail(id)); + } + + /** + * 鏂板椤甸潰璁捐 + */ + @PostMapping + public R<Void> add(@Valid @RequestBody PageDesignerDTO dto) { + pageDesignerService.add(dto); + return R.ok("鏂板鎴愬姛"); + } + + /** + * 淇敼椤甸潰璁捐 + */ + @PutMapping + public R<Void> update(@Valid @RequestBody PageDesignerDTO dto) { + pageDesignerService.updatePage(dto); + return R.ok("淇敼鎴愬姛"); + } + + /** + * 鍒犻櫎椤甸潰璁捐 + */ + @DeleteMapping + public R<Void> remove(@RequestBody List<Long> ids) { + pageDesignerService.deleteByIds(ids); + return R.ok("鍒犻櫎鎴愬姛"); + } +} diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesigner.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesigner.java new file mode 100644 index 0000000..0e2916b --- /dev/null +++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesigner.java @@ -0,0 +1,38 @@ +package org.ruoyi.pageDesigner.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.ruoyi.core.domain.BaseEntity; + +/** + * @author kanglujie + * @date 2025-06-23 17:24:05 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("PAGE_DESIGNER") +public class PageDesigner extends BaseEntity { + @TableId(value = "ID") + private Long id; + @TableField("NAME") + private String name; + @TableField("MENU_ID") + private String menuId; // 鍏宠仈鐨勮彍鍗旾D + @TableField("STATUS") + private String status; + @TableField(value = "DEL_FLAG") + @TableLogic + private String delFlag; + @TableField("REMARK") + private String remark; + @TableField("FORM_JSON") + private String formJson; + @TableField("SHOW_COLUMN") + private String showColumn; // 鍙互灏� selectedFields 瀛樺偍涓� JSON 瀛楃涓� + @TableField("ACTIONS_FUNC") + private String actionsFunc; // 鍙互灏� enableActions 瀛樺偍涓� JSON 瀛楃涓� +} diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerDTO.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerDTO.java new file mode 100644 index 0000000..977f6fb --- /dev/null +++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerDTO.java @@ -0,0 +1,29 @@ +package org.ruoyi.pageDesigner.domain; + +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.ruoyi.core.domain.BaseEntity; +import org.ruoyi.system.domain.SysUser; + +import java.util.List; + +/** + * @author kanglujie + * @date 2025-06-23 17:23:35 + */ +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = PageDesigner.class, reverseConvertGenerate = false) +public class PageDesignerDTO extends BaseEntity { + private Long id; // 淇敼鏃朵紶鍏� + private String name; // 椤甸潰鍚嶇О + private String menuId; // 鍏宠仈鑿滃崟 ID + private String status; // 鍚敤鐘舵�� + private String remark; // 澶囨敞 + private String formJson; // 琛ㄥ崟璁捐 JSON 瀛楃涓� + private String showColumn; // 瀛楁鏄剧ず閰嶇疆 + private String actionsFunc; // 鍚敤鐨勬搷浣滈」锛堝鍒犳敼鏌ワ級 +} diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerVo.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerVo.java new file mode 100644 index 0000000..3b98968 --- /dev/null +++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/domain/PageDesignerVo.java @@ -0,0 +1,28 @@ +package org.ruoyi.pageDesigner.domain; + +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.ruoyi.core.domain.BaseEntity; +import org.ruoyi.system.domain.vo.SysMenuVo; + +import java.io.Serializable; + +/** + * @author kanglujie + * @date 2025-06-23 17:23:35 + */ +@Data +@AutoMapper(target = PageDesigner.class) +public class PageDesignerVo implements Serializable { + private Long id; // 淇敼鏃朵紶鍏� + private String name; // 椤甸潰鍚嶇О + private String menuId; // 鍏宠仈鑿滃崟 ID + private String parentId; // 鍏宠仈鑿滃崟 ID + private String status; // 鍚敤鐘舵�� + private String remark; // 澶囨敞 + private String formJson; // 琛ㄥ崟璁捐 JSON 瀛楃涓� + private String showColumn; // 瀛楁鏄剧ず閰嶇疆 + private String actionsFunc; // 鍚敤鐨勬搷浣滈」锛堝鍒犳敼鏌ワ級 + private SysMenuVo menu; // 鍒涘缓浜轰俊鎭� + private SysMenuVo parent; // 鍒涘缓浜轰俊鎭� +} diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/mapper/PageDesignerMapper.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/mapper/PageDesignerMapper.java new file mode 100644 index 0000000..f3757c7 --- /dev/null +++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/mapper/PageDesignerMapper.java @@ -0,0 +1,20 @@ +package org.ruoyi.pageDesigner.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.ruoyi.pageDesigner.domain.PageDesigner; +import org.ruoyi.pageDesigner.domain.PageDesignerVo; + +/** + * @author kanglujie + * @date 2025-06-23 17:34:48 + */ +@Mapper +public interface PageDesignerMapper extends BaseMapper<PageDesigner> { + Page<PageDesignerVo> selectPagelistAll(@Param("page") Page<PageDesigner> page, @Param(Constants.WRAPPER) Wrapper<PageDesigner> queryWrapper); + +} diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/PageDesignerService.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/PageDesignerService.java new file mode 100644 index 0000000..2289ae9 --- /dev/null +++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/PageDesignerService.java @@ -0,0 +1,29 @@ +package org.ruoyi.pageDesigner.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.ruoyi.core.page.PageQuery; +import org.ruoyi.core.page.TableDataInfo; +import org.ruoyi.pageDesigner.domain.PageDesigner; +import org.ruoyi.pageDesigner.domain.PageDesignerDTO; +import org.ruoyi.pageDesigner.domain.PageDesignerVo; + +import java.util.List; + +/** + * @author kanglujie + * @date 2025-06-23 17:35:56 + */ +public interface PageDesignerService extends IService<PageDesigner> { + + void add(PageDesignerDTO dto); + + void updatePage(PageDesignerDTO dto); + + List<PageDesigner> listAll(String keyword); + + PageDesigner getDetail(Long id); + + void deleteByIds(List<Long> ids); + + TableDataInfo<PageDesignerVo> selectPagelistAll(PageDesignerDTO pageDesignerDTO, PageQuery pageQuery); +} diff --git a/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/impl/PageDesignerServiceImpl.java b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/impl/PageDesignerServiceImpl.java new file mode 100644 index 0000000..b8e42d8 --- /dev/null +++ b/ruoyi-modules/sc-page-designer/src/main/java/org/ruoyi/pageDesigner/service/impl/PageDesignerServiceImpl.java @@ -0,0 +1,97 @@ +package org.ruoyi.pageDesigner.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import org.ruoyi.common.core.constant.UserConstants; +import org.ruoyi.common.core.utils.StreamUtils; +import org.ruoyi.common.core.utils.StringUtils; +import org.ruoyi.core.page.PageQuery; +import org.ruoyi.core.page.TableDataInfo; +import org.ruoyi.helper.DataBaseHelper; +import org.ruoyi.pageDesigner.domain.PageDesigner; +import org.ruoyi.pageDesigner.domain.PageDesignerDTO; +import org.ruoyi.pageDesigner.domain.PageDesignerVo; +import org.ruoyi.pageDesigner.mapper.PageDesignerMapper; +import org.ruoyi.pageDesigner.service.PageDesignerService; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * @author kanglujie + * @date 2025-06-23 17:36:29 + */ +@Service +@RequiredArgsConstructor +public class PageDesignerServiceImpl extends ServiceImpl<PageDesignerMapper, PageDesigner> + implements PageDesignerService { + + private final ObjectMapper objectMapper; + + @Override + public void add(PageDesignerDTO dto) { + PageDesigner entity = convertToEntity(dto); + this.save(entity); + } + + @Override + public void updatePage(PageDesignerDTO dto) { + PageDesigner entity = convertToEntity(dto); + this.updateById(entity); + } + + @Override + public TableDataInfo<PageDesignerVo> selectPagelistAll(PageDesignerDTO pageDesignerDTO, PageQuery pageQuery) { + Page<PageDesignerVo> page = baseMapper.selectPagelistAll(pageQuery.build(), this.buildQueryWrapper(pageDesignerDTO)); + return TableDataInfo.build(page); + } + private Wrapper<PageDesigner> buildQueryWrapper(PageDesignerDTO pageDesignerDTO) { + //Map<String, Object> params = pageDesignerDTO.getParams(); + QueryWrapper<PageDesigner> wrapper = Wrappers.query(); + //wrapper.eq("del_flag", UserConstants.USER_NORMAL) + // .eq(ObjectUtil.isNotNull(pageDesignerDTO.getId()), "id", pageDesignerDTO.getId()) + // .like(StringUtils.isNotBlank(pageDesignerDTO.getName()), "name", pageDesignerDTO.getName()) + // .eq(StringUtils.isNotBlank(pageDesignerDTO.getStatus()), "status", pageDesignerDTO.getStatus()) + // .between(params.get("beginTime") != null && params.get("endTime") != null, + // "create_time", params.get("beginTime"), params.get("endTime")) + //; + return wrapper; + } + @Override + public List<PageDesigner> listAll(String keyword) { + LambdaQueryWrapper<PageDesigner> wrapper = new LambdaQueryWrapper<>(); + wrapper.like(keyword != null, PageDesigner::getName, keyword); + return this.list(wrapper); + } + + @Override + public PageDesigner getDetail(Long id) { + return this.getById(id); + } + + @Override + public void deleteByIds(List<Long> ids) { + this.removeByIds(ids); + } + + private PageDesigner convertToEntity(PageDesignerDTO dto) { + PageDesigner entity = new PageDesigner(); + entity.setId(dto.getId()); + entity.setName(dto.getName()); + entity.setMenuId(dto.getMenuId()); + entity.setStatus(dto.getStatus()); + entity.setRemark(dto.getRemark()); + entity.setFormJson(dto.getFormJson()); + entity.setShowColumn(dto.getShowColumn()); + entity.setActionsFunc(dto.getActionsFunc()); + return entity; + } +} diff --git a/ruoyi-modules/sc-page-designer/src/main/resources/mapper/SysUserMapper.xml b/ruoyi-modules/sc-page-designer/src/main/resources/mapper/SysUserMapper.xml new file mode 100644 index 0000000..033657e --- /dev/null +++ b/ruoyi-modules/sc-page-designer/src/main/resources/mapper/SysUserMapper.xml @@ -0,0 +1,27 @@ +<?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="org.ruoyi.pageDesigner.mapper.PageDesignerMapper"> + + <!-- 澶氱粨鏋勫祵濂楄嚜鍔ㄦ槧灏勯渶甯︿笂姣忎釜瀹炰綋鐨勪富閿甶d 鍚﹀垯鏄犲皠浼氬け璐� --> + <resultMap type="org.ruoyi.pageDesigner.domain.PageDesignerVo" id="PageDesignerResult"> + <id property="id" column="id"/> + <result property="name" column="name"/> + <result property="status" column="status"/> + </resultMap> + + + <select id="selectPagelistAll" resultMap="PageDesignerResult"> + select + id, + name, + status + from page_designer + ${ew.getCustomSqlSegment} + </select> + + + + +</mapper> diff --git a/ruoyi-ui/apps/web-antd/src/api/tool/page-designer/index.ts b/ruoyi-ui/apps/web-antd/src/api/tool/page-designer/index.ts new file mode 100644 index 0000000..79fa508 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/api/tool/page-designer/index.ts @@ -0,0 +1,52 @@ +import { requestClient } from '#/api/request'; +import type { + PageDesigner +} from './model'; + +enum Api { + pageAdd = '/page-designer', + pageUpdate = '/page-designer', + pageList = '/page-designer/list', + pageInfo = '/page-designer', + pageRemove = '/page-designer', +} + +/** + * 鏂板椤甸潰璁捐 + */ +export function pageAdd(data: any) { + return requestClient.post(Api.pageAdd, data); +} + +/** + * 淇敼椤甸潰璁捐 + */ +export function pageUpdate(data: any) { + return requestClient.put(Api.pageUpdate, data); +} + +/** + * 鑾峰彇椤甸潰璁捐鍒楄〃 + */ +export function pageList(params?: any) { + return requestClient.get<{ + total: number; + rows: PageDesigner[]; + code: number; + msg: string; + }>(Api.pageList, { params }); +} + +/** + * 鑾峰彇椤甸潰璁捐璇︽儏 + */ +export function pageInfo(id: string | number) { + return requestClient.get<PageDesigner>(`${Api.pageInfo}/${id}`); +} + +/** + * 鍒犻櫎椤甸潰璁捐 + */ +export function pageRemove(ids: string[] | number[]) { + return requestClient.delete(Api.pageRemove, { data: ids }); +} diff --git a/ruoyi-ui/apps/web-antd/src/api/tool/page-designer/model.d.ts b/ruoyi-ui/apps/web-antd/src/api/tool/page-designer/model.d.ts new file mode 100644 index 0000000..88d885d --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/api/tool/page-designer/model.d.ts @@ -0,0 +1,21 @@ +export interface PageDesigner { + id?: string | number; // 涓婚敭锛岀紪杈戞椂鐢� + name: string; // 椤甸潰鍚嶇О + menuId: string | number | null; // 涓婄骇鐩綍锛堣彍鍗旾D锛� + parentId: string | number | null; // 鐖剁骇ID + status: string; // 鐘舵�� + remark?: string | null; // 澶囨敞 + formJson?: string | null; // 琛ㄥ崟璁捐JSON + showColumn?: string | null; // 鏄剧ず瀛楁(JSON瀛楃涓�) + actionsFunc?: string | null; // 鍚敤鍔熻兘(JSON瀛楃涓�) + createTime?: string; // 鍒涘缓鏃堕棿 + updateTime?: string; // 鏇存柊鏃堕棿 + menu?: { // 鍏宠仈鐨勮彍鍗曚俊鎭� + menuId: number; + menuName: string; + } | null; + parent?: { // 鍏宠仈鐨勭埗绾т俊鎭� + id: string | number; + name: string; + } | null; +} diff --git a/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/data.tsx b/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/data.tsx new file mode 100644 index 0000000..eaf43de --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/data.tsx @@ -0,0 +1,113 @@ +import type { FormSchemaGetter } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; +import { h } from 'vue'; +import { FolderIcon, VbenIcon } from '@vben/icons'; + +export const querySchema: FormSchemaGetter = () => [ + { + component: 'Input', + fieldName: 'name', + label: '椤甸潰鍚嶇О', + }, + { + component: 'Select', + componentProps: { + options: [ + { label: '姝e父', value: '1' }, + { label: '鍋滅敤', value: '0' }, + ], + }, + fieldName: 'status', + label: '椤甸潰鐘舵��', + }, +]; + +export const columns: VxeGridProps['columns'] = [ + { + title: '椤甸潰鍚嶇О', + field: 'name', + }, + { + title: '涓婄骇鐩綍', + field: 'parentName', + slots: { + default: ({ row }) => { + return row.parent?.name || row.menu?.menuName || '-'; + }, + }, + }, + { + title: '鐘舵��', + field: 'status', + slots: { + default: ({ row }) => { + return row.status === '1' ? '姝e父' : '鍋滅敤'; + }, + }, + }, + { + title: '鍒涘缓鏃堕棿', + field: 'createTime', + }, + { + field: 'action', + fixed: 'right', + slots: { default: 'action' }, + title: '鎿嶄綔', + }, +]; + +export const drawerSchema = () => [ + { + component: 'Input', + fieldName: 'name', + label: '椤甸潰鍚嶇О', + rules: 'required', + }, + { + component: 'TreeSelect', + fieldName: 'menuId', + label: '涓婄骇鐩綍', + componentProps: { + allowClear: true, + showSearch: true, + }, + rules: 'selectRequired', + }, + { + component: 'Select', + fieldName: 'status', + label: '鐘舵��', + componentProps: { + options: [ + { label: '姝e父', value: '1' }, + { label: '鍋滅敤', value: '0' }, + ], + }, + rules: 'selectRequired', + }, + { + component: 'CheckboxGroup', + fieldName: 'actionsFunc', + label: '鍚敤鍔熻兘', + defaultValue: ['add', 'edit', 'delete', 'query'], + componentProps: { + options: [ + { label: '鏂板', value: 'add' }, + { label: '缂栬緫', value: 'edit' }, + { label: '鍒犻櫎', value: 'delete' }, + { label: '鏌ヨ', value: 'query' }, + ], + }, + rules: 'required', + }, + { + component: 'Input', + fieldName: 'remark', + label: '澶囨敞', + componentProps: { + type: 'textarea', + rows: 2, + }, + }, +]; \ No newline at end of file diff --git a/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/index.vue b/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/index.vue index d4fea39..af1bcf1 100644 --- a/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/index.vue +++ b/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/index.vue @@ -1,13 +1,154 @@ <template> - <div>111</div> + <Page v-if="isAdmin" :auto-content-height="true"> + <BasicTable table-title="椤甸潰璁捐鍣�" > + <template #toolbar-tools> + <Space> + <a-button type="primary" @click="handleAdd">鏂板</a-button> + </Space> + </template> + <template #action="{ row }"> + <Space> + <ghost-button @click="handleEdit(row)">缂栬緫</ghost-button> + <ghost-button class="btn-success" @click="handleSubAdd(row)">鏂板</ghost-button> + <Popconfirm :get-popup-container="getVxePopupContainer" placement="left" title="纭鍒犻櫎锛�" @confirm="handleDelete(row)"> + <ghost-button danger @click.stop="">鍒犻櫎</ghost-button> + </Popconfirm> + </Space> + </template> + </BasicTable> + <PageDrawer ref="pageModalRef" @reload="tableApi.query()" /> + </Page> + <Fallback v-else description="鎮ㄦ病鏈夐〉闈㈢敓鎴愬櫒鐨勮闂潈闄�" status="403" /> </template> -<script> -export default { - name: "index" +<script setup lang="ts"> +import type { VbenFormProps } from '@vben/common-ui'; +import type { VxeGridProps } from '#/adapter/vxe-table'; +import { computed, ref, onMounted } from 'vue'; +import { useAccess } from '@vben/access'; +import { Fallback, Page, useVbenDrawer } from '@vben/common-ui'; +import { eachTree, getVxePopupContainer } from '@vben/utils'; +import { Popconfirm, Space } from 'ant-design-vue'; +import { useVbenVxeGrid } from '#/adapter/vxe-table'; +import { columns, querySchema } from './data'; +import PageDrawer from './page-drawer.vue'; +import FcDesigner from '@form-create/designer'; +import { pageList, pageRemove } from '#/api/tool/page-designer'; + +// 绉婚櫎mock鏁版嵁 +// const pageList = async (params: any) => { ... }; +// const pageRemove = async (ids: number[]) => {}; + +const formOptions: VbenFormProps = { + commonConfig: { + labelWidth: 80, + componentProps: { + allowClear: true, + }, + }, + schema: querySchema(), + wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4', +}; + +const gridOptions: VxeGridProps = { + columns, + height: 'auto', + keepSource: true, + pagerConfig: { + enabled: true, + }, + proxyConfig: { + ajax: { + query: async ({ page }, formValues = {}) => { + console.log('鏌ヨ鍙傛暟:', { page, formValues }); + const resp = await pageList({ + pageNum: page.currentPage, + pageSize: page.pageSize, + ...formValues, + }); + console.log('鎺ュ彛杩斿洖鏁版嵁:', resp); + return { + rows: resp.rows, + total: resp.total, + }; + }, + }, + }, + rowConfig: { + keyField: 'id', + }, + id: 'tool-page-designer-index', + columnConfig: { resizable: true }, +}; + +const [BasicTable, tableApi] = useVbenVxeGrid({ + formOptions, + gridOptions, +}); + +const designer = ref(); +const pageModalRef = ref(); + +function getFormJson() { + // 鑾峰彇璁捐缁撴灉 + const json = designer.value.getRule(); + // 浣犲彲浠ュ皢 json 瀛樺埌鍚庣 } + +function setFormJson(json) { + // 鍔犺浇宸叉湁璁捐 + designer.value.setRule(json); +} + +function handleAdd() { + pageModalRef.value.open({ update: false }); +} + +function handleEdit(record) { + pageModalRef.value.open({ id: record.id, update: true }); +} + +async function handleDelete(row: any) { + await pageRemove([row.id]); + await tableApi.query(); +} + +function handleSubAdd(row) { + pageModalRef.value.open({ id: row.id, update: false }); +} + +const { hasAccessByRoles } = useAccess(); +const isAdmin = computed(() => { + return hasAccessByRoles(['admin', 'superadmin']); +}); </script> <style scoped> - +.designer-page { + background: #f5f6fa; + padding: 16px; + min-height: 100vh; +} +.designer-query-form { + background: #fff; + padding: 16px 16px 0 16px; + border-radius: 6px; + margin-bottom: 12px; + display: flex; + flex-wrap: wrap; + align-items: center; +} +.designer-toolbar { + background: #fff; + padding: 12px 16px; + border-radius: 6px; + margin-bottom: 12px; + display: flex; + gap: 8px; +} +.designer-table { + background: #fff; + border-radius: 6px; + padding: 0 0 16px 0; +} </style> diff --git a/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/page-drawer.vue b/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/page-drawer.vue new file mode 100644 index 0000000..a73c4ee --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/views/tool/page-designer/page-drawer.vue @@ -0,0 +1,321 @@ +<script setup lang="ts"> +import { computed, ref, watch, nextTick, onMounted } from 'vue'; +import { Modal, message, FormItem } from 'ant-design-vue'; +import { $t } from '@vben/locales'; +import { useVbenForm } from '#/adapter/form'; +import { drawerSchema } from './data'; +import FcDesigner from '@form-create/designer'; +import { menuList } from '../../../api/system/menu'; +import { listToTree, addFullName, getPopupContainer } from '@vben/utils'; +import { pageAdd, pageUpdate, pageInfo } from '#/api/tool/page-designer'; + +interface ModalProps { + id?: number | string; + update: boolean; +} + +const emit = defineEmits<{ reload: [] }>(); + +const isUpdate = ref(false); +const title = computed(() => { + return isUpdate.value ? $t('pages.common.edit') : $t('pages.common.add'); +}); + +const [BasicForm, formApi] = useVbenForm({ + commonConfig: { + componentProps: { + class: 'w-full', + }, + formItemClass: 'col-span-1', + labelWidth: 90, + }, + schema: drawerSchema(), + showDefaultActions: false, + wrapperClass: 'grid-cols-5', +}); + +const designer = ref(); +const selectedFields = ref([]); // 澶氶�夋閫変腑鐨勫瓧娈祂ey +const fieldOptions = ref([]); // 璁捐鍖烘墍鏈夊瓧娈� +const modalVisible = ref(false); +const modalLoading = ref(false); +const currentEditId = ref<string | number>(''); // 褰撳墠缂栬緫鐨処D + +// 鎵撳紑寮圭獥 +const open = async (params: ModalProps = { update: false }) => { + try { + modalVisible.value = true; + modalLoading.value = true; + isUpdate.value = params.update; + currentEditId.value = params.id || ''; // 淇濆瓨褰撳墠缂栬緫鐨処D + await setupPageSelect(); + + if (params.id) { + await formApi.setFieldValue('menuId', params.id); + if (params.update) { + // 鑾峰彇璇︽儏鏁版嵁 + const record = await pageInfo(params.id); + console.log('缂栬緫鏁版嵁:', record); + + // 璁剧疆鍩虹琛ㄥ崟鏁版嵁 + await formApi.setValues({ + name: record.name, + menuId: record.menuId || record.parentId || params.id, // 浼樺厛浣跨敤menuId锛屽叾娆arentId + status: record.status, + remark: record.remark, + actionsFunc: record.actionsFunc ? JSON.parse(record.actionsFunc) : ['add', 'edit', 'delete', 'query'] + }); + + // 鍔犺浇琛ㄥ崟璁捐鏁版嵁 + if (record.formJson) { + try { + const formRule = JSON.parse(record.formJson); + console.log('璁捐鍣ㄨ鍒�:', formRule); + designer.value.setRule(formRule); + + // 鏇存柊瀛楁閫夐」 + await nextTick(); + updateFieldOptions(); + + // 鎭㈠閫変腑鐨勫瓧娈� + if (record.showColumn) { + selectedFields.value = JSON.parse(record.showColumn); + console.log('鎭㈠閫変腑瀛楁:', selectedFields.value); + } + } catch (e) { + console.error('鍔犺浇琛ㄥ崟璁捐鏁版嵁澶辫触:', e); + message.error('鍔犺浇琛ㄥ崟璁捐鏁版嵁澶辫触'); + } + } + } + } else { + // 鏂板鏃堕噸缃暟鎹� + designer.value?.setRule([]); + selectedFields.value = []; + await formApi.resetForm(); + } + } catch (error) { + console.error('鎵撳紑寮圭獥澶辫触:', error); + message.error('鍔犺浇鏁版嵁澶辫触'); + } finally { + modalLoading.value = false; + } +}; +const close = () => { + modalVisible.value = false; +}; + +defineExpose({ open, close }); + +async function setupPageSelect() { + // 鑾峰彇鑿滃崟鏁版嵁 + const menuArray = await menuList(); + // 杩囨护鎺夋寜閽被鍨� + const filteredList = menuArray.filter(item => item.menuType !== 'F' && item.menuType !== 'C'); + // 鏀寔i18n + filteredList.forEach(item => { item.menuName = $t(item.menuName); }); + // 杞负鏍戠粨鏋� + const menuTree = listToTree(filteredList, { id: 'menuId', pid: 'parentId' }); + // 鍔犳牴鑺傜偣 + const fullMenuTree = [ + { + menuId: 0, + menuName: $t('menu.root'), + children: menuTree, + }, + ]; + // 鐢熸垚鍏ㄨ矾寰勫悕 + addFullName(fullMenuTree, 'menuName', ' / '); + + formApi.updateSchema([ + { + componentProps: { + fieldNames: { + label: 'menuName', + value: 'menuId', + children: 'children' + }, + getPopupContainer, + listHeight: 300, + showSearch: true, + treeData: fullMenuTree, + treeDefaultExpandAll: false, + treeDefaultExpandedKeys: [0], + treeLine: { showLeafIcon: false }, + treeNodeFilterProp: 'menuName', + treeNodeLabelProp: 'fullName', + }, + fieldName: 'menuId', + }, + ]); +} + + +// 鍚屾鎵�鏈夊瓧娈靛埌閫変腑鐘舵�� +const syncAllFields = () => { + // 鑾峰彇琛ㄥ崟缁勪欢鐨勮鍒欐弿杩� + const formDesc = designer.value?.getFormDescription?.(); + console.log('琛ㄥ崟缁勪欢鎻忚堪:', formDesc); + + if (!formDesc || !Array.isArray(formDesc)) { + message.warning('鏆傛棤璁捐鏁版嵁'); + return; + } + + // 鎻愬彇瀛楁淇℃伅 + const allFields = formDesc + .filter(item => item && item.field && item.title) + .map(item => ({ + title: item.title, + field: item.field + })); + + console.log('鎻愬彇鐨勫瓧娈�:', allFields); + + if (allFields.length === 0) { + message.warning('鏈壘鍒板彲鐢ㄥ瓧娈�'); + return; + } + + // 鏇存柊瀛楁閫夐」 + fieldOptions.value = allFields.map(item => ({ + label: item.title, + value: item.field + })); + + // 閫変腑鎵�鏈夊瓧娈� + selectedFields.value = allFields.map(item => item.field); + message.success(`宸插悓姝� ${allFields.length} 涓瓧娈礰); +}; + +// 澶勭悊璁捐鍣ㄥ彉鍖� +const handleDesignerChange = () => { + console.log('璁捐鍣ㄥ唴瀹瑰彉鍖�'); + nextTick(() => { + updateFieldOptions(); + }); +}; + +// 褰撹璁″櫒鍐呭鍙樺寲鏃舵洿鏂板瓧娈甸�夐」 +const updateFieldOptions = () => { + console.log('updateFieldOptions'); + console.log('designer.value', designer.value); + + // 鑾峰彇琛ㄥ崟缁勪欢鐨勮鍒欐弿杩� + const formDesc = designer.value?.getFormDescription?.(); + if (!formDesc || !Array.isArray(formDesc)) return; + + const fields = formDesc + .filter(item => item && item.field && item.title) + .map(item => ({ + title: item.title, + field: item.field + })); + + fieldOptions.value = fields.map(item => ({ + label: item.title, + value: item.field + })); + + console.log('鏇存柊鍚庣殑瀛楁閫夐」:', fieldOptions.value); +}; + +// 鐩戝惉璁捐鍣ㄥ唴瀹瑰彉鍖� +watch(() => modalVisible.value, (val) => { + if (val) { + nextTick(() => updateFieldOptions()); + } +}); + +async function handleOk() { + try { + modalLoading.value = true; + const { valid } = await formApi.validate(); + if (!valid) { + return; + } + const data = await formApi.getValues(); + + // 濡傛灉鏄紪杈戞ā寮忥紝娣诲姞id瀛楁 + if (isUpdate.value) { + data.id = currentEditId.value; + } + + // 鑾峰彇琛ㄥ崟璁捐 JSON + data.formJson = designer.value.getJson(); + // 娣诲姞閫変腑鐨勫瓧娈� + data.showColumn = JSON.stringify(selectedFields.value); + // 杞崲鍚敤鍔熻兘涓篔SON瀛楃涓� + data.actionsFunc = JSON.stringify(data.actionsFunc); + + // 鍚屾涓�娆″瓧娈靛閫� + updateFieldOptions(); + await (isUpdate.value ? pageUpdate(data) : pageAdd(data)); + emit('reload'); + close(); + message.success('淇濆瓨鎴愬姛'); + } catch (error) { + console.error(error); + } finally { + modalLoading.value = false; + } +} + +function handleCancel() { + close(); +} +</script> + +<template> + <a-modal + v-model:open="modalVisible" + :title="title" + :width="'80vw'" + :confirm-loading="modalLoading" + @ok="handleOk" + @cancel="handleCancel" + :bodyStyle="{ padding: '24px', minHeight: '60vh' }" + destroyOnClose + wrapClassName="page-designer-modal" + > + <template #closeIcon> + <span></span> + </template> + <BasicForm /> + <div style="margin-top: 16px;"> + <FcDesigner + ref="designer" + @update="handleDesignerChange" + @change="handleDesignerChange" + @add-rule="handleDesignerChange" + @remove-rule="handleDesignerChange" + + /> + <div style="margin-top: 8px; display: flex; justify-content: flex-end;"> + <a-button type="primary" ghost @click="syncAllFields"> + 鍚屾璁捐瀛楁鍒拌〃鏍� + </a-button> + </div> + </div> + <FormItem label="琛ㄦ牸瀛楁" style="margin-top: 24px;"> + <a-checkbox-group + v-model:value="selectedFields" + :options="fieldOptions" + style="width:100%;display:flex;flex-wrap:wrap;gap:8px" + /> + </FormItem> + <template #empty> + <div style="padding: 32px 0; color: #999; text-align: center;"> + 鏆傛棤鏁版嵁 + </div> + </template> + </a-modal> +</template> + +<style scoped> +.page-designer-modal .ant-modal { + max-width: 1200px; +} + + +</style> \ No newline at end of file -- Gitblit v1.9.3