feat: 增加修改面板属性的功能

pull/81/head
moonrailgun 4 years ago
parent b243f64227
commit 4c9f5ec5f2

@ -1,4 +1,4 @@
import React, { useMemo, useState } from 'react'; import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import _isNil from 'lodash/isNil'; import _isNil from 'lodash/isNil';
import _fromPairs from 'lodash/fromPairs'; import _fromPairs from 'lodash/fromPairs';
@ -22,6 +22,7 @@ export interface FastFormProps {
schema?: ObjectSchema<any>; // yup schame object 用于表单校验 schema?: ObjectSchema<any>; // yup schame object 用于表单校验
layout?: 'horizontal' | 'vertical'; // 布局方式(默认水平) layout?: 'horizontal' | 'vertical'; // 布局方式(默认水平)
submitLabel?: string; // 提交按钮的标签名 submitLabel?: string; // 提交按钮的标签名
initialValues?: any;
onSubmit: (values: any) => Promise<void> | void; // 点击提交按钮的回调 onSubmit: (values: any) => Promise<void> | void; // 点击提交按钮的回调
onChange?: (values: any) => void; // 数据更新回调 onChange?: (values: any) => void; // 数据更新回调
} }
@ -32,13 +33,21 @@ export interface FastFormProps {
*/ */
export const FastForm: React.FC<FastFormProps> = React.memo((props) => { export const FastForm: React.FC<FastFormProps> = React.memo((props) => {
const initialValues = useMemo(() => { const initialValues = useMemo(() => {
return _fromPairs( return {
props.fields.map((field) => [field.name, field.defaultValue ?? '']) ..._fromPairs(
); props.fields.map((field) => [field.name, field.defaultValue ?? ''])
}, [props.fields]); ),
...props.initialValues,
};
}, [props.fields, props.initialValues]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
useLayoutEffect(() => {
// 加载时提交一次initialValues
typeof props.onChange === 'function' && props.onChange(initialValues);
}, []);
const formik = useFormik({ const formik = useFormik({
initialValues, initialValues,
validationSchema: props.schema, validationSchema: props.schema,

@ -110,6 +110,7 @@ export {
applyGroupInvite, applyGroupInvite,
modifyGroupField, modifyGroupField,
createGroupPanel, createGroupPanel,
modifyGroupPanel,
deleteGroupPanel, deleteGroupPanel,
} from './model/group'; } from './model/group';
export type { GroupPanel, GroupInfo, GroupBasicInfo } from './model/group'; export type { GroupPanel, GroupInfo, GroupBasicInfo } from './model/group';

@ -170,6 +170,27 @@ export async function createGroupPanel(
}); });
} }
/**
*
*/
export async function modifyGroupPanel(
groupId: string,
panelId: string,
options: {
name: string;
parentId?: string;
provider?: string;
pluginPanelName?: string;
meta?: Record<string, unknown>;
}
) {
await request.post('/api/group/modifyGroupPanel', {
...options,
groupId,
panelId,
});
}
/** /**
* *
* @param groupId Id * @param groupId Id

@ -10,6 +10,8 @@ import type { DataNode } from 'antd/lib/tree';
import { buildTreeDataWithGroupPanel } from './utils'; import { buildTreeDataWithGroupPanel } from './utils';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import { useGroupPanelTreeDrag } from './useGroupPanelTreeDrag'; import { useGroupPanelTreeDrag } from './useGroupPanelTreeDrag';
import { closeModal, openModal } from '@/components/Modal';
import { ModalModifyGroupPanel } from '../../GroupPanel/ModifyGroupPanel';
interface GroupPanelTree { interface GroupPanelTree {
groupId: string; groupId: string;
@ -25,6 +27,19 @@ export const GroupPanelTree: React.FC<GroupPanelTree> = React.memo((props) => {
[props.groupPanels] [props.groupPanels]
); );
const handleModifyPanel = useCallback(
(panelId: string) => {
const key = openModal(
<ModalModifyGroupPanel
groupId={props.groupId}
groupPanelId={panelId}
onSuccess={() => closeModal(key)}
/>
);
},
[props.groupId]
);
const handleDeletePanel = useCallback( const handleDeletePanel = useCallback(
(panelId: string, panelName: string, isGroup: boolean) => { (panelId: string, panelName: string, isGroup: boolean) => {
showAlert({ showAlert({
@ -47,6 +62,17 @@ export const GroupPanelTree: React.FC<GroupPanelTree> = React.memo((props) => {
<div className="flex group"> <div className="flex group">
<span>{node.title}</span> <span>{node.title}</span>
<div className="opacity-0 group-hover:opacity-100"> <div className="opacity-0 group-hover:opacity-100">
<Button
type="text"
size="small"
icon={<Icon className="anticon" icon="mdi:pencil-outline" />}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
handleModifyPanel(String(node.key));
}}
/>
<Button <Button
type="text" type="text"
size="small" size="small"

@ -52,7 +52,7 @@ export const GroupPanel: React.FC<{
const key = openModal( const key = openModal(
<ModalCreateGroupPanel <ModalCreateGroupPanel
groupId={groupId} groupId={groupId}
onCreateSuccess={() => { onSuccess={() => {
closeModal(key); closeModal(key);
isEditingRef.current = false; isEditingRef.current = false;
}} }}

@ -28,7 +28,7 @@ const schema = createFastFormSchema({
*/ */
export const ModalCreateGroupPanel: React.FC<{ export const ModalCreateGroupPanel: React.FC<{
groupId: string; groupId: string;
onCreateSuccess: () => void; onSuccess?: () => void;
}> = React.memo((props) => { }> = React.memo((props) => {
const [currentValues, setValues] = useState<Partial<GroupPanelValues>>({}); const [currentValues, setValues] = useState<Partial<GroupPanelValues>>({});
@ -36,9 +36,9 @@ export const ModalCreateGroupPanel: React.FC<{
async (values: GroupPanelValues) => { async (values: GroupPanelValues) => {
await createGroupPanel(props.groupId, buildDataFromValues(values)); await createGroupPanel(props.groupId, buildDataFromValues(values));
showToasts(t('创建成功'), 'success'); showToasts(t('创建成功'), 'success');
props.onCreateSuccess(); typeof props.onSuccess === 'function' && props.onSuccess();
}, },
[props.groupId, props.onCreateSuccess] [props.groupId, props.onSuccess]
); );
const fields = useGroupPanelFields(props.groupId, currentValues); const fields = useGroupPanelFields(props.groupId, currentValues);

@ -0,0 +1,69 @@
import { LoadingSpinner } from '@/components/LoadingSpinner';
import React, { useEffect, useLayoutEffect, useState } from 'react';
import {
t,
useAsyncRequest,
modifyGroupPanel,
createFastFormSchema,
fieldSchema,
showToasts,
useGroupPanel,
} from 'tailchat-shared';
import { ModalWrapper } from '../../Modal';
import { WebFastForm } from '../../WebFastForm';
import { buildDataFromValues, pickValuesFromGroupPanelInfo } from './helper';
import type { GroupPanelValues } from './types';
import { useGroupPanelFields } from './useGroupPanelFields';
import _omit from 'lodash/omit';
const schema = createFastFormSchema({
name: fieldSchema
.string()
.required(t('面板名不能为空'))
.max(20, t('面板名过长')),
type: fieldSchema.string().required(t('面板类型不能为空')),
});
/**
*
*/
export const ModalModifyGroupPanel: React.FC<{
groupId: string;
groupPanelId: string;
onSuccess?: () => void;
}> = React.memo((props) => {
const groupPanelInfo = useGroupPanel(props.groupId, props.groupPanelId);
const [currentValues, setValues] = useState<Partial<GroupPanelValues>>({});
const [, handleSubmit] = useAsyncRequest(
async (values: GroupPanelValues) => {
await modifyGroupPanel(
props.groupId,
props.groupPanelId,
_omit(buildDataFromValues(values), 'type') // 发送时不传type
);
showToasts(t('修改成功'), 'success');
typeof props.onSuccess === 'function' && props.onSuccess();
},
[props.groupId, props.groupPanelId, props.onSuccess]
);
const fields = useGroupPanelFields(props.groupId, currentValues);
if (!groupPanelInfo) {
return <LoadingSpinner />;
}
return (
<ModalWrapper title={t('创建群组面板')} style={{ maxWidth: 440 }}>
<WebFastForm
schema={schema}
fields={fields.filter((f) => f.type !== 'type')} // 变更时不显示类型
initialValues={pickValuesFromGroupPanelInfo(groupPanelInfo)}
onChange={setValues}
onSubmit={handleSubmit}
/>
</ModalWrapper>
);
});
ModalModifyGroupPanel.displayName = 'ModalModifyGroupPanel';

@ -1,5 +1,5 @@
import { findPluginPanelInfoByName } from '@/utils/plugin-helper'; import { findPluginPanelInfoByName } from '@/utils/plugin-helper';
import { GroupPanelType } from 'tailchat-shared'; import { GroupPanel, GroupPanelType } from 'tailchat-shared';
import type { GroupPanelValues } from './types'; import type { GroupPanelValues } from './types';
/** /**
@ -32,3 +32,19 @@ export function buildDataFromValues(values: GroupPanelValues) {
meta, meta,
}; };
} }
/**
*
*/
export function pickValuesFromGroupPanelInfo(
groupPanelInfo: GroupPanel
): GroupPanelValues {
return {
...groupPanelInfo.meta,
name: groupPanelInfo.name,
type:
groupPanelInfo.type === GroupPanelType.PLUGIN
? String(groupPanelInfo.pluginPanelName)
: groupPanelInfo.type,
};
}

@ -2,6 +2,6 @@ import type { GroupPanelType } from 'tailchat-shared';
export interface GroupPanelValues { export interface GroupPanelValues {
name: string; name: string;
type: string | GroupPanelType; type: string | GroupPanelType.TEXT | GroupPanelType.GROUP;
[key: string]: unknown; [key: string]: unknown;
} }

Loading…
Cancel
Save