From 8c771abbb851ddda35947b95f709a57d194a90b3 Mon Sep 17 00:00:00 2001 From: puz <13060209078@163.com> Date: Mon, 22 Jun 2026 10:09:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=82=E6=95=B0=E7=AE=A1=E7=90=86=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 + frontend/src/locales/en-US.json | 9 +- frontend/src/locales/zh-CN.json | 9 +- frontend/src/pages/business/Meetings.tsx | 4 +- .../src/pages/system/sys-params/index.less | 18 ++ .../src/pages/system/sys-params/index.tsx | 189 +++++++++++++++--- 6 files changed, 205 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index 39ba585..e6a8075 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,11 @@ backend/src/main/resources/application-local.yml *.log !backend/.env.example .omx/ +/backend/target/ +/.idea +/.vscode +/.codegraph +/.agents /backend/.env.example /backend/.mvn-settings-ali.xml /backend/.mvn-settings-codex.xml diff --git a/frontend/src/locales/en-US.json b/frontend/src/locales/en-US.json index 330669a..7fe0daf 100644 --- a/frontend/src/locales/en-US.json +++ b/frontend/src/locales/en-US.json @@ -427,7 +427,14 @@ "paramKeyPlaceholder": "sys.config.example", "paramValuePlaceholder": "Enter parameter value", "systemHint": "System parameters are usually protected from deletion and used directly by the platform.", - "descriptionPlaceholder": "Describe the purpose of this parameter" + "descriptionPlaceholder": "Describe the purpose of this parameter", + "numberPlaceholder": "Enter a number", + "numberInvalid": "Please enter a valid number", + "booleanTrue": "Yes", + "booleanFalse": "No", + "jsonPlaceholder": "{ \"key\": \"value\" }", + "jsonFormat": "Format", + "jsonInvalid": "Invalid JSON, please check the syntax" }, "orgsExt": { "enabled": "Enabled", diff --git a/frontend/src/locales/zh-CN.json b/frontend/src/locales/zh-CN.json index c0a183c..1f9afcd 100644 --- a/frontend/src/locales/zh-CN.json +++ b/frontend/src/locales/zh-CN.json @@ -425,7 +425,14 @@ "paramKeyPlaceholder": "sys.config.example", "paramValuePlaceholder": "请输入参数值", "systemHint": "系统参数通常受保护,并会被平台直接使用。", - "descriptionPlaceholder": "请描述该参数的用途" + "descriptionPlaceholder": "请描述该参数的用途", + "numberPlaceholder": "请输入数字", + "numberInvalid": "请输入有效的数字", + "booleanTrue": "是", + "booleanFalse": "否", + "jsonPlaceholder": "{ \"key\": \"value\" }", + "jsonFormat": "格式化", + "jsonInvalid": "JSON 格式不正确,请检查" }, "orgsExt": { "enabled": "启用", diff --git a/frontend/src/pages/business/Meetings.tsx b/frontend/src/pages/business/Meetings.tsx index 44a35c3..2580d5a 100644 --- a/frontend/src/pages/business/Meetings.tsx +++ b/frontend/src/pages/business/Meetings.tsx @@ -776,7 +776,7 @@ const Meetings: React.FC = () => { render: (text: string) => {text || "未知"}, }, { - title: "鏉ユ簮", + title: "来源", dataIndex: "meetingSource", key: "meetingSource", width: 80, @@ -789,7 +789,7 @@ const Meetings: React.FC = () => { render: (text: string) => {text || "无参会人员"}, }, { - title: "鎿嶄綔", + title: "操作", key: "action", width: 220, render: (_: unknown, record: MeetingVO) => ( diff --git a/frontend/src/pages/system/sys-params/index.less b/frontend/src/pages/system/sys-params/index.less index 5171246..cacf6d0 100644 --- a/frontend/src/pages/system/sys-params/index.less +++ b/frontend/src/pages/system/sys-params/index.less @@ -31,3 +31,21 @@ .param-type-tag { border-radius: 4px; } + +.param-boolean-segmented { + .ant-segmented-item-selected { + background: var(--app-primary-color) !important; + color: #fff !important; + box-shadow: 0 2px 6px rgba(22, 119, 255, 0.35); + } + + .ant-segmented-item { + color: var(--app-text-secondary, rgba(0, 0, 0, 0.65)); + transition: all 0.2s; + + &:hover:not(.ant-segmented-item-selected) { + color: var(--app-primary-color); + background: rgba(22, 119, 255, 0.06); + } + } +} diff --git a/frontend/src/pages/system/sys-params/index.tsx b/frontend/src/pages/system/sys-params/index.tsx index 8b2f966..bc444dd 100644 --- a/frontend/src/pages/system/sys-params/index.tsx +++ b/frontend/src/pages/system/sys-params/index.tsx @@ -1,4 +1,4 @@ -import { Button, Card, Col, Drawer, Form, Input, Popconfirm, Row, Select, Space, Switch, Tag, Tooltip, Typography, message } from "antd"; +import { Button, Card, Col, Drawer, Form, Input, Popconfirm, Row, Segmented, Select, Space, Switch, Tag, Tooltip, Typography, message } from "antd"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { DeleteOutlined, EditOutlined, InfoCircleOutlined, PlusOutlined, SearchOutlined, SettingOutlined } from "@ant-design/icons"; @@ -14,6 +14,10 @@ import "./index.less"; const { Text } = Typography; +/** Normalize paramType to title-case for case-insensitive comparison */ +const isType = (actual: string | undefined, expected: string) => + (actual || "").toLowerCase() === expected.toLowerCase(); + export default function SysParams() { const { t } = useTranslation(); const { can } = usePermission(); @@ -26,7 +30,55 @@ export default function SysParams() { const [queryParams, setQueryParams] = useState({ pageNum: 1, pageSize: 10 }); const [drawerOpen, setDrawerOpen] = useState(false); const [editing, setEditing] = useState(null); + const [jsonError, setJsonError] = useState(null); const [form] = Form.useForm(); + const paramType = Form.useWatch("paramType", form) as string | undefined; + + // Per-type validation rules. The submitted paramValue is always a string + // (backend stores TEXT), so validators operate on string values. + const valueRules = (() => { + const required = [{ required: true, message: t("sysParams.paramValue") }]; + if (isType(paramType, "Number")) { + return [ + ...required, + { + validator: (_: unknown, value: string) => + value === "" || value == null || !Number.isNaN(Number(value)) + ? Promise.resolve() + : Promise.reject(new Error(t("sysParamsExt.numberInvalid"))) + } + ]; + } + if (isType(paramType, "JSON")) { + return [ + ...required, + { + validator: (_: unknown, value: string) => { + if (value === "" || value == null) return Promise.resolve(); + try { + JSON.parse(value); + return Promise.resolve(); + } catch { + return Promise.reject(new Error(t("sysParamsExt.jsonInvalid"))); + } + } + } + ]; + } + return required; + })(); + + const formatJson = () => { + const raw = form.getFieldValue("paramValue"); + if (!raw) return; + try { + const parsed = JSON.parse(raw); + form.setFieldValue("paramValue", JSON.stringify(parsed, null, 2)); + setJsonError(null); + } catch { + setJsonError(t("sysParamsExt.jsonInvalid")); + } + }; const loadData = useCallback(async (query = queryParams) => { setLoading(true); @@ -48,7 +100,6 @@ export default function SysParams() { }; const handleReset = () => { - form.resetFields(); setQueryParams({ pageNum: 1, pageSize: 10 }); }; @@ -58,6 +109,7 @@ export default function SysParams() { const openCreate = () => { setEditing(null); + setJsonError(null); form.resetFields(); form.setFieldsValue({ isSystem: 0, status: 1 }); setDrawerOpen(true); @@ -65,6 +117,7 @@ export default function SysParams() { const openEdit = (record: SysParamVO) => { setEditing(record); + setJsonError(null); form.setFieldsValue(record); setDrawerOpen(true); }; @@ -112,23 +165,50 @@ export default function SysParams() { dataIndex: "paramValue", key: "paramValue", width: 360, - ellipsis: { showTitle: false }, - render: (text: string) => ( - - - {text} - - - ) + render: (text: string, record: SysParamVO) => { + const type = record.paramType; + // Boolean: 彩色标签显示 是/否 + if (isType(type, "Boolean")) { + const isTrue = text === "true"; + return {isTrue ? t("sysParamsExt.booleanTrue") : t("sysParamsExt.booleanFalse")}; + } + // JSON: 等宽字体,以纯字符串显示 + if (isType(type, "JSON")) { + return ( + + + {text} + + + ); + } + // Number / String: 等宽代码文本 + Tooltip + return ( + + + {text} + + + ); + } }, { title: t("sysParams.paramType"), @@ -170,7 +250,7 @@ export default function SysParams() { title={t("sysParams.title")} subtitle={t("sysParams.subtitle")} headerExtra={ - } @@ -214,7 +294,7 @@ export default function SysParams() { {editing ? t("sysParams.drawerTitleEdit") : t("sysParams.drawerTitleCreate")}} + title={{editing ? t("sysParams.drawerTitleEdit") : t("sysParams.drawerTitleCreate")}} open={drawerOpen} onClose={() => setDrawerOpen(false)} width={500} @@ -225,13 +305,13 @@ export default function SysParams() { - - - - ({ label: item.itemLabel, value: item.itemValue }))} onChange={(value) => { + form.setFieldValue("paramValue", isType(value, "Boolean") ? "true" : ""); + setJsonError(null); + }} /> @@ -240,6 +320,65 @@ export default function SysParams() { + {isType(paramType, "JSON") && ( +
+ +
+ )} + { + const v = e && typeof e === "object" && "target" in (e as { target?: { value?: unknown } }) + ? (e as { target: { value?: unknown } }).target.value + : e; + return typeof v === "number" ? String(v) : (v ?? ""); + }} + extra={isType(paramType, "JSON") && jsonError ? {jsonError} : undefined} + > + {isType(paramType, "Number") ? ( + + ) : isType(paramType, "Boolean") ? ( + + ) : isType(paramType, "JSON") ? ( + { + const v = form.getFieldValue("paramValue"); + if (!v) form.setFieldValue("paramValue", JSON.stringify({ key: "value" })); + }} + onChange={(e) => { + const v = e.target.value; + if (!v) { + setJsonError(null); + return; + } + try { + JSON.parse(v); + setJsonError(null); + } catch { + setJsonError(t("sysParamsExt.jsonInvalid")); + } + }} + /> + ) : ( + + )} + {t("sysParams.isSystem")}} name="isSystem"