|
|
@@ -0,0 +1,628 @@
|
|
|
+"use client";
|
|
|
+
|
|
|
+import {
|
|
|
+ EditOutlined,
|
|
|
+ MinusCircleOutlined,
|
|
|
+ PlusOutlined,
|
|
|
+ ReloadOutlined,
|
|
|
+ SearchOutlined,
|
|
|
+ UndoOutlined,
|
|
|
+} from "@ant-design/icons";
|
|
|
+import {
|
|
|
+ App,
|
|
|
+ Button,
|
|
|
+ Descriptions,
|
|
|
+ Form,
|
|
|
+ Image,
|
|
|
+ Input,
|
|
|
+ InputNumber,
|
|
|
+ Modal,
|
|
|
+ Select,
|
|
|
+ Space,
|
|
|
+ Switch,
|
|
|
+ Table,
|
|
|
+ Tag,
|
|
|
+ Tooltip,
|
|
|
+} from "antd";
|
|
|
+import type { ColumnsType, TablePaginationConfig } from "antd/es/table";
|
|
|
+import type React from "react";
|
|
|
+import { useEffect, useMemo, useState } from "react";
|
|
|
+import ImageUpload from "@/components/ImageUpload";
|
|
|
+import {
|
|
|
+ createBizCategoryConstant,
|
|
|
+ getBizCategoryConstantPage,
|
|
|
+ updateBizCategoryConstant,
|
|
|
+} from "@/services/bizCategoryConstant";
|
|
|
+import type {
|
|
|
+ BizCategoryConstantAdminDTO,
|
|
|
+ BizCategoryConstantAdminQuery,
|
|
|
+ BizCategoryConstantSub,
|
|
|
+ I18nTextSub,
|
|
|
+} from "@/types/api";
|
|
|
+import { formatTimestamp } from "@/utils/date";
|
|
|
+
|
|
|
+const enabledOptions = [
|
|
|
+ { label: "全部", value: "all" },
|
|
|
+ { label: "启用", value: "true" },
|
|
|
+ { label: "禁用", value: "false" },
|
|
|
+];
|
|
|
+
|
|
|
+function parseEnabled(v: unknown): boolean | undefined {
|
|
|
+ if (v === "true") return true;
|
|
|
+ if (v === "false") return false;
|
|
|
+ return undefined;
|
|
|
+}
|
|
|
+
|
|
|
+function normalizeI18ns(raw: unknown): I18nTextSub[] | undefined {
|
|
|
+ if (!Array.isArray(raw)) return undefined;
|
|
|
+ const list = raw
|
|
|
+ .map((it) => {
|
|
|
+ const obj = it as Record<string, unknown>;
|
|
|
+ const lang = typeof obj.lang === "string" ? obj.lang.trim() : "";
|
|
|
+ const text = typeof obj.text === "string" ? obj.text.trim() : "";
|
|
|
+ if (!lang && !text) return null;
|
|
|
+ return { lang, text };
|
|
|
+ })
|
|
|
+ .filter((x): x is I18nTextSub => Boolean(x));
|
|
|
+ return list;
|
|
|
+}
|
|
|
+
|
|
|
+function normalizeConstants(
|
|
|
+ raw: unknown,
|
|
|
+): BizCategoryConstantSub[] | undefined {
|
|
|
+ if (!Array.isArray(raw)) return undefined;
|
|
|
+ const list = raw
|
|
|
+ .map((it) => {
|
|
|
+ const obj = it as Record<string, unknown>;
|
|
|
+ const value = typeof obj.value === "string" ? obj.value.trim() : "";
|
|
|
+ const icon = typeof obj.icon === "string" ? obj.icon.trim() : "";
|
|
|
+ const rawPriority = obj.priority;
|
|
|
+ const priority =
|
|
|
+ typeof rawPriority === "number" && Number.isFinite(rawPriority)
|
|
|
+ ? rawPriority
|
|
|
+ : typeof rawPriority === "string" && rawPriority.trim() !== ""
|
|
|
+ ? Number(rawPriority)
|
|
|
+ : undefined;
|
|
|
+ const valueI18ns = normalizeI18ns(obj.valueI18ns);
|
|
|
+ if (
|
|
|
+ !value &&
|
|
|
+ !icon &&
|
|
|
+ typeof priority !== "number" &&
|
|
|
+ !valueI18ns?.length
|
|
|
+ ) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ const out: BizCategoryConstantSub = {};
|
|
|
+ if (value) out.value = value;
|
|
|
+ if (icon) out.icon = icon;
|
|
|
+ if (typeof priority === "number" && Number.isFinite(priority)) {
|
|
|
+ out.priority = priority;
|
|
|
+ }
|
|
|
+ if (valueI18ns?.length) out.valueI18ns = valueI18ns;
|
|
|
+ return out;
|
|
|
+ })
|
|
|
+ .filter((x): x is BizCategoryConstantSub => x !== null);
|
|
|
+ return list.length ? list : [];
|
|
|
+}
|
|
|
+
|
|
|
+const BizCategoryConstantPage: React.FC = () => {
|
|
|
+ const { message } = App.useApp();
|
|
|
+ const [searchForm] = Form.useForm();
|
|
|
+ const [editForm] = Form.useForm();
|
|
|
+
|
|
|
+ const [loading, setLoading] = useState(false);
|
|
|
+ const [dataSource, setDataSource] = useState<BizCategoryConstantAdminDTO[]>(
|
|
|
+ [],
|
|
|
+ );
|
|
|
+ const [total, setTotal] = useState(0);
|
|
|
+ const [queryParams, setQueryParams] = useState<BizCategoryConstantAdminQuery>(
|
|
|
+ {
|
|
|
+ pageSize: 20,
|
|
|
+ pageIndex: 1,
|
|
|
+ },
|
|
|
+ );
|
|
|
+
|
|
|
+ const [modalVisible, setModalVisible] = useState(false);
|
|
|
+ const [editMode, setEditMode] = useState(false);
|
|
|
+ const [editLoading, setEditLoading] = useState(false);
|
|
|
+ const [currentRecord, setCurrentRecord] =
|
|
|
+ useState<BizCategoryConstantAdminDTO | null>(null);
|
|
|
+
|
|
|
+ const loadPageData = async () => {
|
|
|
+ setLoading(true);
|
|
|
+ try {
|
|
|
+ const response = await getBizCategoryConstantPage(queryParams);
|
|
|
+ setDataSource(response.items || []);
|
|
|
+ setTotal(response.total || 0);
|
|
|
+ } catch (error) {
|
|
|
+ console.error("Failed to load bizcategory constant:", error);
|
|
|
+ message.error("加载品类常量失败");
|
|
|
+ } finally {
|
|
|
+ setLoading(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // biome-ignore lint/correctness/useExhaustiveDependencies: loadPageData is stable and doesn't need to be in dependencies
|
|
|
+ useEffect(() => {
|
|
|
+ loadPageData();
|
|
|
+ }, [queryParams]);
|
|
|
+
|
|
|
+ const handleSearch = () => {
|
|
|
+ const values = searchForm.getFieldsValue();
|
|
|
+ setQueryParams({
|
|
|
+ ...queryParams,
|
|
|
+ pageIndex: 1,
|
|
|
+ key: values.key?.trim() || undefined,
|
|
|
+ enabled: parseEnabled(values.enabled),
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleReset = () => {
|
|
|
+ searchForm.resetFields();
|
|
|
+ setQueryParams({
|
|
|
+ pageSize: 20,
|
|
|
+ pageIndex: 1,
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleRefresh = () => loadPageData();
|
|
|
+
|
|
|
+ const handleTableChange = (pagination: TablePaginationConfig) => {
|
|
|
+ setQueryParams({
|
|
|
+ ...queryParams,
|
|
|
+ pageIndex: pagination.current || 1,
|
|
|
+ pageSize: pagination.pageSize || 20,
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ const openModal = (
|
|
|
+ mode: "create" | "edit",
|
|
|
+ record?: BizCategoryConstantAdminDTO,
|
|
|
+ ) => {
|
|
|
+ const isEdit = mode === "edit";
|
|
|
+ setEditMode(isEdit);
|
|
|
+ setCurrentRecord(record ?? null);
|
|
|
+ editForm.resetFields();
|
|
|
+
|
|
|
+ if (isEdit && record) {
|
|
|
+ editForm.setFieldsValue({
|
|
|
+ key: record.key,
|
|
|
+ name: record.name,
|
|
|
+ enabled: record.enabled ?? true,
|
|
|
+ constants: (record.constants ?? []).map((c) => ({
|
|
|
+ value: c.value,
|
|
|
+ icon: c.icon,
|
|
|
+ priority: c.priority ?? 0,
|
|
|
+ valueI18ns: (c.valueI18ns ?? []).map((i) => ({
|
|
|
+ lang: i.lang,
|
|
|
+ text: i.text,
|
|
|
+ })),
|
|
|
+ })),
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ editForm.setFieldsValue({
|
|
|
+ enabled: true,
|
|
|
+ constants: [
|
|
|
+ {
|
|
|
+ priority: 0,
|
|
|
+ valueI18ns: [],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ setModalVisible(true);
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleAdd = () => openModal("create");
|
|
|
+ const handleEdit = (record: BizCategoryConstantAdminDTO) =>
|
|
|
+ openModal("edit", record);
|
|
|
+
|
|
|
+ const handleSubmit = async () => {
|
|
|
+ try {
|
|
|
+ const values = await editForm.validateFields();
|
|
|
+ setEditLoading(true);
|
|
|
+
|
|
|
+ const submitData: BizCategoryConstantAdminDTO = {
|
|
|
+ key: values.key?.trim(),
|
|
|
+ name: values.name?.trim(),
|
|
|
+ enabled: Boolean(values.enabled),
|
|
|
+ constants: normalizeConstants(values.constants),
|
|
|
+ createdAt: currentRecord?.createdAt,
|
|
|
+ updatedAt: currentRecord?.updatedAt,
|
|
|
+ version: currentRecord?.version,
|
|
|
+ };
|
|
|
+
|
|
|
+ if (editMode && currentRecord?.id) {
|
|
|
+ submitData.id = currentRecord.id;
|
|
|
+ await updateBizCategoryConstant({
|
|
|
+ ...currentRecord,
|
|
|
+ ...submitData,
|
|
|
+ });
|
|
|
+ message.success("更新成功");
|
|
|
+ } else {
|
|
|
+ delete submitData.id;
|
|
|
+ delete submitData.createdAt;
|
|
|
+ delete submitData.updatedAt;
|
|
|
+ delete submitData.version;
|
|
|
+ await createBizCategoryConstant(submitData);
|
|
|
+ message.success("创建成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ setModalVisible(false);
|
|
|
+ loadPageData();
|
|
|
+ } catch (error) {
|
|
|
+ console.error("Failed to submit bizcategory constant:", error);
|
|
|
+ } finally {
|
|
|
+ setEditLoading(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const renderConstants = useMemo(() => {
|
|
|
+ return (record: BizCategoryConstantAdminDTO) => {
|
|
|
+ const list = record.constants ?? [];
|
|
|
+ if (!list.length) return <div className="text-gray-500">无常量配置</div>;
|
|
|
+
|
|
|
+ return (
|
|
|
+ <Descriptions bordered size="small" column={1}>
|
|
|
+ <Descriptions.Item label="常量列表">
|
|
|
+ <div className="space-y-3">
|
|
|
+ {list.map((c, idx) => {
|
|
|
+ const i18ns = c.valueI18ns ?? [];
|
|
|
+ return (
|
|
|
+ <div
|
|
|
+ key={`${record.id ?? record.key ?? "row"}-${idx}-${c.value ?? ""}`}
|
|
|
+ className="border rounded p-3"
|
|
|
+ >
|
|
|
+ <div className="flex flex-wrap items-center gap-2">
|
|
|
+ <div className="min-w-[220px]">
|
|
|
+ <b>value:</b>
|
|
|
+ <span>{c.value ?? "-"}</span>
|
|
|
+ </div>
|
|
|
+ <div className="min-w-[140px]">
|
|
|
+ <b>priority:</b>
|
|
|
+ <span>
|
|
|
+ {typeof c.priority === "number" ? c.priority : "-"}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div className="flex items-center gap-2">
|
|
|
+ <b>icon:</b>
|
|
|
+ {c.icon ? (
|
|
|
+ <Image
|
|
|
+ src={c.icon}
|
|
|
+ alt="icon"
|
|
|
+ width={32}
|
|
|
+ height={32}
|
|
|
+ style={{ objectFit: "cover", borderRadius: 4 }}
|
|
|
+ />
|
|
|
+ ) : (
|
|
|
+ <span>-</span>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div className="mt-2">
|
|
|
+ <b>i18n:</b>
|
|
|
+ {i18ns.length ? (
|
|
|
+ <div className="mt-1 flex flex-wrap gap-2">
|
|
|
+ {i18ns.map((it, i) => (
|
|
|
+ <Tag key={`${it.lang}-${i}`}>
|
|
|
+ {it.lang}:{it.text}
|
|
|
+ </Tag>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ ) : (
|
|
|
+ <span className="text-gray-500"> - </span>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ })}
|
|
|
+ </div>
|
|
|
+ </Descriptions.Item>
|
|
|
+ </Descriptions>
|
|
|
+ );
|
|
|
+ };
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ const columns: ColumnsType<BizCategoryConstantAdminDTO> = [
|
|
|
+ {
|
|
|
+ title: "常量Key",
|
|
|
+ dataIndex: "key",
|
|
|
+ key: "key",
|
|
|
+ width: 220,
|
|
|
+ render: (v?: string) => v || "-",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "名称",
|
|
|
+ dataIndex: "name",
|
|
|
+ key: "name",
|
|
|
+ width: 220,
|
|
|
+ render: (v?: string) => v || "-",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "启用",
|
|
|
+ dataIndex: "enabled",
|
|
|
+ key: "enabled",
|
|
|
+ width: 90,
|
|
|
+ render: (v?: boolean) => (
|
|
|
+ <Switch checked={Boolean(v)} disabled size="small" />
|
|
|
+ ),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "常量数",
|
|
|
+ key: "constantsCount",
|
|
|
+ width: 90,
|
|
|
+ render: (_, record) => record.constants?.length ?? 0,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "更新时间",
|
|
|
+ dataIndex: "updatedAt",
|
|
|
+ key: "updatedAt",
|
|
|
+ width: 180,
|
|
|
+ render: (ts?: number) => (ts ? formatTimestamp(ts) : "-"),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "创建时间",
|
|
|
+ dataIndex: "createdAt",
|
|
|
+ key: "createdAt",
|
|
|
+ width: 180,
|
|
|
+ render: (ts?: number) => (ts ? formatTimestamp(ts) : "-"),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "操作",
|
|
|
+ key: "action",
|
|
|
+ width: 120,
|
|
|
+ fixed: "right",
|
|
|
+ render: (_, record) => (
|
|
|
+ <Button
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ icon={<EditOutlined />}
|
|
|
+ onClick={() => handleEdit(record)}
|
|
|
+ >
|
|
|
+ 编辑
|
|
|
+ </Button>
|
|
|
+ ),
|
|
|
+ },
|
|
|
+ ];
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className="p-6">
|
|
|
+ <div className="bg-white p-4 rounded-lg shadow mb-4">
|
|
|
+ <Form form={searchForm} layout="inline" className="gap-x-2 gap-y-4">
|
|
|
+ <Form.Item label="常量Key" name="key">
|
|
|
+ <Input placeholder="请输入" allowClear style={{ width: 220 }} />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item label="状态" name="enabled" initialValue="all">
|
|
|
+ <Select
|
|
|
+ style={{ width: 160 }}
|
|
|
+ options={enabledOptions}
|
|
|
+ allowClear={false}
|
|
|
+ />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item style={{ marginLeft: "auto" }}>
|
|
|
+ <Space>
|
|
|
+ <Button
|
|
|
+ type="primary"
|
|
|
+ icon={<SearchOutlined />}
|
|
|
+ onClick={handleSearch}
|
|
|
+ >
|
|
|
+ 搜索
|
|
|
+ </Button>
|
|
|
+ <Button icon={<UndoOutlined />} onClick={handleReset}>
|
|
|
+ 重置
|
|
|
+ </Button>
|
|
|
+ <Button icon={<ReloadOutlined />} onClick={handleRefresh}>
|
|
|
+ 刷新
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ type="primary"
|
|
|
+ icon={<PlusOutlined />}
|
|
|
+ onClick={handleAdd}
|
|
|
+ style={{ backgroundColor: "#52c41a" }}
|
|
|
+ >
|
|
|
+ 新增
|
|
|
+ </Button>
|
|
|
+ </Space>
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div className="bg-white p-4 rounded-lg shadow">
|
|
|
+ <Table
|
|
|
+ columns={columns}
|
|
|
+ dataSource={dataSource}
|
|
|
+ rowKey={(record) =>
|
|
|
+ record.id ?? record.key ?? `${record.createdAt ?? ""}`
|
|
|
+ }
|
|
|
+ loading={loading}
|
|
|
+ pagination={{
|
|
|
+ current: queryParams.pageIndex,
|
|
|
+ pageSize: queryParams.pageSize,
|
|
|
+ total,
|
|
|
+ showSizeChanger: true,
|
|
|
+ showTotal: (t) => `共 ${t} 条`,
|
|
|
+ pageSizeOptions: ["10", "20", "30", "40", "50", "100", "200"],
|
|
|
+ }}
|
|
|
+ onChange={handleTableChange}
|
|
|
+ expandable={{
|
|
|
+ expandedRowRender: renderConstants,
|
|
|
+ }}
|
|
|
+ scroll={{ x: 1100 }}
|
|
|
+ size="small"
|
|
|
+ bordered
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <Modal
|
|
|
+ title={editMode ? "编辑品类常量" : "新增品类常量"}
|
|
|
+ open={modalVisible}
|
|
|
+ onOk={handleSubmit}
|
|
|
+ onCancel={() => setModalVisible(false)}
|
|
|
+ confirmLoading={editLoading}
|
|
|
+ width={860}
|
|
|
+ destroyOnHidden
|
|
|
+ forceRender
|
|
|
+ >
|
|
|
+ <Form form={editForm} layout="vertical" style={{ marginTop: 16 }}>
|
|
|
+ <Form.Item
|
|
|
+ label="常量Key"
|
|
|
+ name="key"
|
|
|
+ rules={[
|
|
|
+ { required: true, message: "请输入常量Key" },
|
|
|
+ { max: 128, message: "Key 过长" },
|
|
|
+ ]}
|
|
|
+ tooltip={editMode ? "Key 设定后不可修改" : undefined}
|
|
|
+ >
|
|
|
+ <Input placeholder="请输入常量Key" disabled={editMode} />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item
|
|
|
+ label="常量名称"
|
|
|
+ name="name"
|
|
|
+ rules={[{ required: true, message: "请输入常量名称" }]}
|
|
|
+ >
|
|
|
+ <Input placeholder="请输入常量名称" />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item label="是否启用" name="enabled" valuePropName="checked">
|
|
|
+ <Switch />
|
|
|
+ </Form.Item>
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ label="常量配置(constants)"
|
|
|
+ tooltip="支持列表动态新增/删除"
|
|
|
+ >
|
|
|
+ <Form.List name="constants">
|
|
|
+ {(fields, { add, remove }) => (
|
|
|
+ <div className="space-y-3">
|
|
|
+ {fields.map((field) => (
|
|
|
+ <div
|
|
|
+ key={field.key}
|
|
|
+ className="border rounded p-4 bg-gray-50"
|
|
|
+ >
|
|
|
+ <div className="flex items-start justify-between gap-2">
|
|
|
+ <div className="flex-1">
|
|
|
+ <div className="grid grid-cols-2 gap-3">
|
|
|
+ <Form.Item
|
|
|
+ label="常量值"
|
|
|
+ name={[field.name, "value"]}
|
|
|
+ rules={[
|
|
|
+ { required: true, message: "请输入常量值" },
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ <Input placeholder="请输入常量值" />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item
|
|
|
+ label="排序优先级"
|
|
|
+ name={[field.name, "priority"]}
|
|
|
+ >
|
|
|
+ <InputNumber
|
|
|
+ style={{ width: "100%" }}
|
|
|
+ min={0}
|
|
|
+ step={1}
|
|
|
+ placeholder="排序"
|
|
|
+ />
|
|
|
+ </Form.Item>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <Form.Item label="图标" name={[field.name, "icon"]}>
|
|
|
+ <ImageUpload
|
|
|
+ maxCount={1}
|
|
|
+ dir="bizcategory/constant/icon"
|
|
|
+ />
|
|
|
+ </Form.Item>
|
|
|
+
|
|
|
+ <Form.Item
|
|
|
+ label="国际化"
|
|
|
+ tooltip="可选;用于不同语言的显示文本"
|
|
|
+ >
|
|
|
+ <Form.List name={[field.name, "valueI18ns"]}>
|
|
|
+ {(i18nFields, i18nOps) => (
|
|
|
+ <div className="space-y-2">
|
|
|
+ {i18nFields.map((i18nField) => (
|
|
|
+ <div
|
|
|
+ key={i18nField.key}
|
|
|
+ className="flex gap-2 items-start"
|
|
|
+ >
|
|
|
+ <Form.Item
|
|
|
+ label="语言代码"
|
|
|
+ name={[i18nField.name, "lang"]}
|
|
|
+ className="mb-0 flex-1"
|
|
|
+ rules={[
|
|
|
+ {
|
|
|
+ required: true,
|
|
|
+ message: "请输入语言代码",
|
|
|
+ },
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ <Input placeholder="en / in / zh" />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item
|
|
|
+ label="文本"
|
|
|
+ name={[i18nField.name, "text"]}
|
|
|
+ className="mb-0 flex-2"
|
|
|
+ rules={[
|
|
|
+ {
|
|
|
+ required: true,
|
|
|
+ message: "请输入文本",
|
|
|
+ },
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ <Input placeholder="请输入文本" />
|
|
|
+ </Form.Item>
|
|
|
+ <Tooltip title="删除 i18n">
|
|
|
+ <Button
|
|
|
+ danger
|
|
|
+ type="text"
|
|
|
+ icon={<MinusCircleOutlined />}
|
|
|
+ onClick={() =>
|
|
|
+ i18nOps.remove(i18nField.name)
|
|
|
+ }
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+ <Button
|
|
|
+ type="dashed"
|
|
|
+ icon={<PlusOutlined />}
|
|
|
+ onClick={() => i18nOps.add({})}
|
|
|
+ >
|
|
|
+ 新增国际化文本
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </Form.List>
|
|
|
+ </Form.Item>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <Tooltip title="删除该常量项">
|
|
|
+ <Button
|
|
|
+ danger
|
|
|
+ type="text"
|
|
|
+ icon={<MinusCircleOutlined />}
|
|
|
+ onClick={() => remove(field.name)}
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+
|
|
|
+ <Button
|
|
|
+ type="dashed"
|
|
|
+ icon={<PlusOutlined />}
|
|
|
+ onClick={() => add({ priority: 0, valueI18ns: [] })}
|
|
|
+ block
|
|
|
+ >
|
|
|
+ 新增常量项
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </Form.List>
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </Modal>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+export default BizCategoryConstantPage;
|