feat: 去掉antd
This commit is contained in:
19
src/App.tsx
19
src/App.tsx
@@ -10,19 +10,30 @@ import { Redirect } from './modules/Redirect';
|
||||
import { CustomThemeProvider } from '@kevisual/center-components/theme/index.tsx';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import 'dayjs/locale/zh-cn';
|
||||
import 'dayjs/locale/en';
|
||||
import zhCN from 'antd/locale/zh_CN';
|
||||
import enUS from 'antd/locale/en_US';
|
||||
import ConfigProvider from 'antd/es/config-provider';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const AntProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const theme = useTheme();
|
||||
const primaryColor = theme.palette.primary.main;
|
||||
const secondaryColor = theme.palette.secondary.main;
|
||||
const { i18n } = useTranslation();
|
||||
const [locale, setLocale] = useState(zhCN);
|
||||
useEffect(() => {
|
||||
if (i18n.language === 'en') {
|
||||
setLocale(enUS);
|
||||
} else {
|
||||
setLocale(zhCN);
|
||||
}
|
||||
}, [i18n.language]);
|
||||
return (
|
||||
<ConfigProvider
|
||||
locale={zhCN}
|
||||
locale={locale}
|
||||
theme={{
|
||||
token: {
|
||||
colorPrimary: primaryColor,
|
||||
@@ -43,7 +54,7 @@ const AntProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
},
|
||||
Tooltip: {
|
||||
zIndexPopupBase: 2000,
|
||||
}
|
||||
},
|
||||
},
|
||||
}}>
|
||||
{children}
|
||||
|
||||
@@ -57,8 +57,9 @@ h3 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.cm-editor {
|
||||
@apply h-full;
|
||||
}
|
||||
#for-message {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
@@ -1,3 +1,2 @@
|
||||
import { message } from '@kevisual/system-ui/dist/message';
|
||||
|
||||
export { message };
|
||||
export { message };
|
||||
@@ -2,8 +2,14 @@ import { useNavigation, useParams } from 'react-router';
|
||||
import { useAppVersionStore } from '../store';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Form, Input, Modal, Tooltip } from 'antd';
|
||||
import { CloudUploadOutlined, DeleteOutlined, EditOutlined, FileOutlined, LeftOutlined, LinkOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import CloudUploadOutlined from '@ant-design/icons/CloudUploadOutlined';
|
||||
import DeleteOutlined from '@ant-design/icons/DeleteOutlined';
|
||||
import FileOutlined from '@ant-design/icons/FileOutlined';
|
||||
import LeftOutlined from '@ant-design/icons/LeftOutlined';
|
||||
import LinkOutlined from '@ant-design/icons/LinkOutlined';
|
||||
import PlusOutlined from '@ant-design/icons/PlusOutlined';
|
||||
import { useModal } from '@kevisual/center-components/modal/Confirm.tsx';
|
||||
import { Tooltip } from '@mui/material';
|
||||
import { isObjectNull } from '@/utils/is-null';
|
||||
import { FileUpload } from '../modules/FileUpload';
|
||||
import clsx from 'clsx';
|
||||
@@ -13,9 +19,13 @@ import { Button } from '@mui/material';
|
||||
import { Dialog, DialogContent, DialogTitle, ButtonGroup } from '@mui/material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { IconButton } from '@kevisual/center-components/button/index.tsx';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { TextField } from '@mui/material';
|
||||
import { pick } from 'lodash-es';
|
||||
|
||||
const FormModal = () => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const { control, handleSubmit, reset } = useForm();
|
||||
const containerStore = useAppVersionStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
@@ -26,62 +36,56 @@ const FormModal = () => {
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const open = containerStore.showEdit;
|
||||
if (open) {
|
||||
if (open) {
|
||||
const isNull = isObjectNull(containerStore.formData);
|
||||
if (isNull) {
|
||||
form.setFieldsValue({});
|
||||
} else form.setFieldsValue(containerStore.formData);
|
||||
const isNull = isObjectNull(containerStore.formData);
|
||||
if (isNull) {
|
||||
reset({});
|
||||
} else {
|
||||
reset(containerStore.formData);
|
||||
}
|
||||
}
|
||||
}, [containerStore.showEdit]);
|
||||
|
||||
const onFinish = async (values: any) => {
|
||||
containerStore.updateData(values);
|
||||
const pickValues = pick(values, ['id', 'key', 'version']);
|
||||
containerStore.updateData(pickValues);
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
containerStore.setShowEdit(false);
|
||||
form.resetFields();
|
||||
reset();
|
||||
};
|
||||
|
||||
const isEdit = containerStore.formData.id;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={isEdit ? 'Edit' : 'Add'}
|
||||
<Dialog
|
||||
open={containerStore.showEdit}
|
||||
onClose={() => containerStore.setShowEdit(false)}
|
||||
destroyOnClose
|
||||
footer={false}
|
||||
width={800}
|
||||
onCancel={onClose}>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={onFinish}
|
||||
labelCol={{
|
||||
span: 4,
|
||||
}}
|
||||
wrapperCol={{
|
||||
span: 20,
|
||||
}}>
|
||||
<Form.Item name='id' hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name='key' label='key'>
|
||||
<Input disabled />
|
||||
</Form.Item>
|
||||
<Form.Item name='version' label='version'>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label=' ' colon={false}>
|
||||
<Button type='submit' variant='contained' color='primary'>
|
||||
{t('submit')}
|
||||
</Button>
|
||||
<Button className='ml-2' onClick={onClose}>
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
sx={{
|
||||
'& .MuiDialog-paper': {
|
||||
width: '800px',
|
||||
},
|
||||
}}>
|
||||
<DialogTitle>{isEdit ? 'Edit' : 'Add'}</DialogTitle>
|
||||
<DialogContent>
|
||||
<form className='flex flex-col gap-6' onSubmit={handleSubmit(onFinish)}>
|
||||
<Controller name='key' control={control} defaultValue='' render={({ field }) => <TextField label='key' {...field} disabled />} />
|
||||
<Controller name='version' control={control} defaultValue='' render={({ field }) => <TextField label='version' {...field} />} />
|
||||
<div>
|
||||
<Button type='submit' variant='contained' color='primary'>
|
||||
{t('submit')}
|
||||
</Button>
|
||||
<Button className='ml-2' onClick={onClose}>
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -105,7 +109,7 @@ export const AppVersionList = () => {
|
||||
}),
|
||||
);
|
||||
const navigate = useNewNavigate();
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
const [modal, contextHolder] = useModal();
|
||||
const [isUpload, setIsUpload] = useState(false);
|
||||
useEffect(() => {
|
||||
// fetch app version list
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useUserAppStore } from '../store';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Form, Input, Modal, Select, Switch } from 'antd';
|
||||
import { useModal } from '@kevisual/center-components/modal/Confirm.tsx';
|
||||
|
||||
import DeleteOutlined from '@ant-design/icons/DeleteOutlined';
|
||||
import EditOutlined from '@ant-design/icons/EditOutlined';
|
||||
import LinkOutlined from '@ant-design/icons/LinkOutlined';
|
||||
@@ -9,26 +10,43 @@ import PlusOutlined from '@ant-design/icons/PlusOutlined';
|
||||
import UnorderedListOutlined from '@ant-design/icons/UnorderedListOutlined';
|
||||
import CodeOutlined from '@ant-design/icons/CodeOutlined';
|
||||
import ShareAltOutlined from '@ant-design/icons/ShareAltOutlined';
|
||||
|
||||
import { FormControlLabel, Switch } from '@mui/material';
|
||||
import { isObjectNull } from '@/utils/is-null';
|
||||
import { useNewNavigate } from '@/modules';
|
||||
import { DialogActions, Tooltip } from '@mui/material';
|
||||
import { marked } from 'marked';
|
||||
import clsx from 'clsx';
|
||||
import { IconButton } from '@kevisual/center-components/button/index.tsx';
|
||||
import { Select } from '@kevisual/center-components/select/index.tsx';
|
||||
import { iText } from '@kevisual/resources/index.ts';
|
||||
import { PermissionManager } from '@kevisual/resources/pages/file/modules/PermissionManager.tsx';
|
||||
import { Button } from '@mui/material';
|
||||
import { message } from '@/modules/message';
|
||||
import { Dialog, DialogContent, DialogTitle, ButtonGroup } from '@mui/material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TextField, InputAdornment } from '@mui/material';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { pick } from 'lodash-es';
|
||||
|
||||
const FormModal = () => {
|
||||
const [form] = Form.useForm();
|
||||
const defaultValues = {
|
||||
id: '',
|
||||
title: '',
|
||||
domain: '',
|
||||
key: '',
|
||||
description: '',
|
||||
proxy: true,
|
||||
status: 'running',
|
||||
};
|
||||
const { control, handleSubmit, reset } = useForm({
|
||||
defaultValues,
|
||||
});
|
||||
const containerStore = useUserAppStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
showEdit: state.showEdit,
|
||||
setShowEdit: state.setShowEdit,
|
||||
formData: state.formData,
|
||||
userApp: state.userApp,
|
||||
updateData: state.updateData,
|
||||
};
|
||||
}),
|
||||
@@ -36,121 +54,113 @@ const FormModal = () => {
|
||||
useEffect(() => {
|
||||
const open = containerStore.showEdit;
|
||||
if (open) {
|
||||
if (open) {
|
||||
const isNull = isObjectNull(containerStore.formData);
|
||||
if (isNull) {
|
||||
form.setFieldsValue({});
|
||||
} else form.setFieldsValue(containerStore.formData);
|
||||
const isNull = isObjectNull(containerStore.userApp);
|
||||
console.log('isNull', containerStore.userApp);
|
||||
if (isNull) {
|
||||
reset(defaultValues);
|
||||
} else {
|
||||
reset(containerStore.userApp);
|
||||
}
|
||||
}
|
||||
}, [containerStore.showEdit]);
|
||||
}, [containerStore.showEdit, containerStore.userApp]);
|
||||
const onFinish = async (values: any) => {
|
||||
containerStore.updateData(values);
|
||||
const pickValues = pick(values, ['id', 'title', 'domain', 'key', 'description', 'proxy', 'status']);
|
||||
containerStore.updateData(pickValues);
|
||||
};
|
||||
const onClose = () => {
|
||||
containerStore.setShowEdit(false);
|
||||
form.resetFields();
|
||||
reset();
|
||||
};
|
||||
const isEdit = containerStore.formData.id;
|
||||
const isEdit = containerStore?.userApp?.id;
|
||||
return (
|
||||
<Dialog
|
||||
title={isEdit ? 'Edit' : 'Add'}
|
||||
open={containerStore.showEdit}
|
||||
onClose={() => containerStore.setShowEdit(false)}
|
||||
sx={{
|
||||
'& .MuiDialog-paper': {
|
||||
width: '800px',
|
||||
width: '1000px',
|
||||
},
|
||||
}}>
|
||||
<DialogTitle>{isEdit ? 'Edit' : 'Add'}</DialogTitle>
|
||||
<DialogContent>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={onFinish}
|
||||
initialValues={{
|
||||
proxy: true,
|
||||
}}
|
||||
labelCol={{
|
||||
span: 4,
|
||||
}}
|
||||
wrapperCol={{
|
||||
span: 20,
|
||||
}}>
|
||||
<Form.Item name='id' hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name='title' label='title'>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name='domain' label='domain' tooltip='域名自定义绑定'>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name='key' label='key'>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name='description' label='description'>
|
||||
<Input.TextArea rows={4} />
|
||||
</Form.Item>
|
||||
<Form.Item name='proxy' label='proxy' tooltip='设置为true,则后端直接代理请求minio服务进行转发,不会缓存下载到服务器。'>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item name='status' label='status'>
|
||||
<Select
|
||||
options={[
|
||||
{ label: 'Running', value: 'running' },
|
||||
{ label: 'Stop', value: 'stop' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label=' ' colon={false}>
|
||||
<form className='flex flex-col gap-4 pt-2' onSubmit={handleSubmit(onFinish)}>
|
||||
<Controller name='title' control={control} render={({ field }) => <TextField {...field} label='title' fullWidth />} />
|
||||
<Controller
|
||||
name='domain'
|
||||
control={control}
|
||||
render={({ field }) => <TextField {...field} label='domain' variant='outlined' helperText='域名自定义绑定' />}
|
||||
/>
|
||||
<Controller name='key' control={control} render={({ field }) => <TextField {...field} label='key' fullWidth />} />
|
||||
<Controller name='description' control={control} render={({ field }) => <TextField {...field} label='description' multiline rows={4} fullWidth />} />
|
||||
<Controller name='proxy' control={control} render={({ field }) => <Switch {...field} checked={field.value} />} />
|
||||
<Controller
|
||||
name='status'
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
{...field}
|
||||
sx={{
|
||||
width: '100%',
|
||||
}}
|
||||
options={[
|
||||
{ label: 'Running', value: 'running' },
|
||||
{ label: 'Stop', value: 'stop' },
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<div>
|
||||
<Button type='submit'>提交</Button>
|
||||
<Button className='ml-2' type='reset' onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
const ShareModal = () => {
|
||||
const [form] = Form.useForm();
|
||||
const [permission, setPermission] = useState<any>(null);
|
||||
const [runtime, setRuntime] = useState<string[]>([]);
|
||||
const containerStore = useUserAppStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
showEdit: state.showShareEdit,
|
||||
setShowEdit: state.setShowShareEdit,
|
||||
formData: state.formData,
|
||||
updateData: state.updateData,
|
||||
userApp: state.userApp,
|
||||
};
|
||||
}),
|
||||
);
|
||||
useEffect(() => {
|
||||
const open = containerStore.showEdit;
|
||||
if (open) {
|
||||
// form.setFieldsValue(containerStore.formData);
|
||||
const permission = containerStore.formData?.data?.permission || {};
|
||||
const permission = containerStore.userApp?.data?.permission || {};
|
||||
const runtime = containerStore.userApp?.data?.runtime || [];
|
||||
if (isObjectNull(permission)) {
|
||||
setPermission(null);
|
||||
} else {
|
||||
setPermission(permission);
|
||||
}
|
||||
setRuntime(runtime);
|
||||
}
|
||||
}, [containerStore.showEdit]);
|
||||
}, [containerStore.showEdit, containerStore.userApp]);
|
||||
const onFinish = async () => {
|
||||
const values = {
|
||||
...containerStore.formData,
|
||||
id: containerStore.userApp.id,
|
||||
data: {
|
||||
permission,
|
||||
runtime,
|
||||
},
|
||||
};
|
||||
containerStore.updateData(values);
|
||||
};
|
||||
const onClose = () => {
|
||||
containerStore.setShowEdit(false);
|
||||
form.resetFields();
|
||||
};
|
||||
const { t } = useTranslation();
|
||||
console.log('runtime', runtime);
|
||||
return (
|
||||
<Dialog
|
||||
open={containerStore.showEdit}
|
||||
@@ -159,27 +169,52 @@ const ShareModal = () => {
|
||||
}}>
|
||||
<DialogTitle>{iText.share.title}</DialogTitle>
|
||||
<DialogContent>
|
||||
<PermissionManager
|
||||
value={permission}
|
||||
onChange={(value) => {
|
||||
setPermission(value);
|
||||
}}
|
||||
/>
|
||||
<DialogActions>
|
||||
<Button type='submit' variant='contained' onClick={onFinish}>
|
||||
提交
|
||||
</Button>
|
||||
<Button className='ml-2' type='reset' onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
</DialogActions>
|
||||
<div className='flex flex-col gap-2 w-[400px] '>
|
||||
<PermissionManager
|
||||
value={permission}
|
||||
onChange={(value) => {
|
||||
setPermission(value);
|
||||
}}
|
||||
/>
|
||||
<FormControlLabel
|
||||
label={t('app.runtime')}
|
||||
control={
|
||||
<Select
|
||||
multiple
|
||||
size='small'
|
||||
value={runtime}
|
||||
onChange={(e) => {
|
||||
setRuntime(e.target.value as string[]);
|
||||
}}
|
||||
options={[
|
||||
{
|
||||
label: 'Node',
|
||||
value: 'node',
|
||||
},
|
||||
{
|
||||
label: 'Browser',
|
||||
value: 'browser',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button type='submit' variant='contained' onClick={onFinish}>
|
||||
{t('Submit')}
|
||||
</Button>
|
||||
<Button className='ml-2' type='reset' onClick={onClose}>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
export const List = () => {
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
|
||||
const [modal, contextHolder] = useModal();
|
||||
const { t } = useTranslation();
|
||||
const userAppStore = useUserAppStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
@@ -190,6 +225,7 @@ export const List = () => {
|
||||
setFormData: state.setFormData,
|
||||
deleteData: state.deleteData,
|
||||
setShowShareEdit: state.setShowShareEdit,
|
||||
getUserApp: state.getUserApp,
|
||||
};
|
||||
}),
|
||||
);
|
||||
@@ -206,6 +242,7 @@ export const List = () => {
|
||||
padding: '8px',
|
||||
}}
|
||||
onClick={() => {
|
||||
userAppStore.setFormData({});
|
||||
userAppStore.setShowEdit(true);
|
||||
}}>
|
||||
<PlusOutlined />
|
||||
@@ -242,8 +279,14 @@ export const List = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{item.domain && <div className='text-xs'>访问域名: {item.domain}</div>}
|
||||
<div className='text-xs'>version: {item.version}</div>
|
||||
{item.domain && (
|
||||
<div className='text-xs'>
|
||||
{t('app.domain')}: {item.domain}
|
||||
</div>
|
||||
)}
|
||||
<div className='text-xs'>
|
||||
{t('app.version')}: {item.version}
|
||||
</div>
|
||||
<div className={clsx('text-sm border border-gray-200 p-2 max-h-[140px] scrollbar my-1', !hasDescription && 'hidden')}>
|
||||
<div dangerouslySetInnerHTML={{ __html: content }}></div>
|
||||
</div>
|
||||
@@ -273,6 +316,7 @@ export const List = () => {
|
||||
<Tooltip title={iText.share.tips}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
userAppStore.getUserApp(item.id);
|
||||
userAppStore.setFormData(item);
|
||||
userAppStore.setShowShareEdit(true);
|
||||
}}>
|
||||
@@ -294,7 +338,7 @@ export const List = () => {
|
||||
if (DEV_SERVER) {
|
||||
baseUri = 'http://localhost:3005';
|
||||
}
|
||||
const link = new URL(`/${item.user}/${item.key}`, baseUri);
|
||||
const link = new URL(`/${item.user}/${item.key}/`, baseUri);
|
||||
window.open(link.toString(), '_blank');
|
||||
} else {
|
||||
message.error('The app is not running');
|
||||
|
||||
@@ -14,6 +14,9 @@ type UserAppStore = {
|
||||
deleteData: (id: string) => Promise<void>;
|
||||
showShareEdit: boolean;
|
||||
setShowShareEdit: (showShareEdit: boolean) => void;
|
||||
userApp: any;
|
||||
setUserApp: (userApp: any) => void;
|
||||
getUserApp: (id: string) => Promise<void>;
|
||||
};
|
||||
export const useUserAppStore = create<UserAppStore>((set, get) => {
|
||||
return {
|
||||
@@ -69,5 +72,20 @@ export const useUserAppStore = create<UserAppStore>((set, get) => {
|
||||
},
|
||||
showShareEdit: false,
|
||||
setShowShareEdit: (showShareEdit) => set({ showShareEdit }),
|
||||
userApp: {},
|
||||
setUserApp: (userApp) => set({ userApp }),
|
||||
getUserApp: async (id) => {
|
||||
set({ userApp: null });
|
||||
const res = await query.post({
|
||||
path: 'user-app',
|
||||
key: 'get',
|
||||
id,
|
||||
});
|
||||
if (res.code === 200) {
|
||||
set({ userApp: res.data });
|
||||
} else {
|
||||
message.error(res.message || 'Request failed');
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { Input, Modal, Select } from 'antd';
|
||||
import { Fragment, Suspense, useEffect, useState } from 'react';
|
||||
import { TextArea } from '../components/TextArea';
|
||||
import { useContainerStore } from '../store';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { Form } from 'antd';
|
||||
// import copy from 'copy-to-clipboard';
|
||||
import { useNewNavigate } from '@/modules';
|
||||
import { message } from '@/modules/message';
|
||||
@@ -19,10 +16,16 @@ import { isObjectNull } from '@/utils/is-null';
|
||||
import { CardBlank } from '@/components/card/CardBlank';
|
||||
import { Settings } from 'lucide-react';
|
||||
import React from 'react';
|
||||
|
||||
import { useModal } from '@kevisual/center-components/modal/Confirm.tsx';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { TextField } from '@mui/material';
|
||||
import { pick } from 'lodash-es';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TagsInput } from '@kevisual/center-components/select/TagsInput.tsx';
|
||||
const DrawEdit = React.lazy(() => import('../module/DrawEdit'));
|
||||
|
||||
const FormModal = () => {
|
||||
const [form] = Form.useForm();
|
||||
const { control, handleSubmit, reset, setValue } = useForm();
|
||||
const containerStore = useContainerStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
@@ -33,70 +36,75 @@ const FormModal = () => {
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const open = containerStore.showEdit;
|
||||
if (open) {
|
||||
if (open) {
|
||||
const isNull = isObjectNull(containerStore.formData);
|
||||
if (isNull) {
|
||||
form.setFieldsValue({});
|
||||
} else form.setFieldsValue(containerStore.formData);
|
||||
const isNull = isObjectNull(containerStore.formData);
|
||||
if (isNull) {
|
||||
reset({});
|
||||
} else {
|
||||
Object.keys(containerStore.formData).forEach((key) => {
|
||||
setValue(key, containerStore.formData[key]);
|
||||
});
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
reset({});
|
||||
};
|
||||
}, [containerStore.showEdit]);
|
||||
|
||||
const onFinish = async (values: any) => {
|
||||
containerStore.updateData(values);
|
||||
const pickValues = pick(values, ['id', 'title', 'description', 'tags', 'code']);
|
||||
containerStore.updateData(pickValues);
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
containerStore.setShowEdit(false);
|
||||
form.resetFields();
|
||||
reset();
|
||||
};
|
||||
|
||||
const isEdit = containerStore.formData.id;
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Dialog open={containerStore.showEdit} onClose={() => containerStore.setShowEdit(false)}>
|
||||
<Dialog open={containerStore.showEdit} onClose={onClose}>
|
||||
<DialogTitle>{isEdit ? 'Edit' : 'Add'}</DialogTitle>
|
||||
<DialogContent sx={{ padding: '20px', minWidth: '600px' }}>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={onFinish}
|
||||
labelCol={{
|
||||
span: 4,
|
||||
}}
|
||||
wrapperCol={{
|
||||
span: 20,
|
||||
}}>
|
||||
<Form.Item name='id' hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name='title' label='title'>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name='description' label='description'>
|
||||
<Input.TextArea rows={4} />
|
||||
</Form.Item>
|
||||
<Form.Item name='tags' label='tags'>
|
||||
<Select mode='tags' />
|
||||
</Form.Item>
|
||||
<form className='flex flex-col gap-6 pt-2' onSubmit={handleSubmit(onFinish)}>
|
||||
<Controller name='title' control={control} defaultValue='' render={({ field }) => <TextField {...field} label='Title' fullWidth />} />
|
||||
<Controller
|
||||
name='description'
|
||||
control={control}
|
||||
defaultValue=''
|
||||
render={({ field }) => <TextField {...field} label='Description' multiline rows={4} fullWidth />}
|
||||
/>
|
||||
<Controller
|
||||
name='tags'
|
||||
control={control}
|
||||
defaultValue={[]}
|
||||
render={({ field }) => {
|
||||
return <TagsInput key={'tags'} label='Tags' placeholder='添加标签' value={field.value} onChange={(value) => field.onChange(value)} />;
|
||||
}}
|
||||
/>
|
||||
{!isEdit && (
|
||||
<Form.Item name='code' label='code'>
|
||||
<TextArea />
|
||||
</Form.Item>
|
||||
<Controller name='code' control={control} defaultValue='' render={({ field }) => <TextField {...field} label='Code' multiline fullWidth />} />
|
||||
)}
|
||||
<Form.Item label=' ' colon={false}>
|
||||
<div>
|
||||
<Button variant='contained' type='submit'>
|
||||
提交
|
||||
{t('Submit')}
|
||||
</Button>
|
||||
<Button className='ml-2' onClick={onClose}>
|
||||
取消
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
const PublishFormModal = () => {
|
||||
const [form] = Form.useForm();
|
||||
const { control, handleSubmit, reset, setValue, getValues } = useForm();
|
||||
const { t } = useTranslation();
|
||||
const containerStore = useContainerStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
@@ -107,20 +115,25 @@ const PublishFormModal = () => {
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const open = containerStore.showEdit;
|
||||
if (open) {
|
||||
if (open) {
|
||||
const isNull = isObjectNull(containerStore.formData);
|
||||
if (isNull) {
|
||||
form.setFieldsValue({});
|
||||
} else form.setFieldsValue(containerStore.formData);
|
||||
const isNull = isObjectNull(containerStore.formData);
|
||||
if (isNull) {
|
||||
reset({});
|
||||
} else {
|
||||
Object.keys(containerStore.formData).forEach((key) => {
|
||||
setValue(key, containerStore.formData[key]);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [containerStore.showEdit]);
|
||||
|
||||
const onFinish = async () => {
|
||||
const values = form.getFieldsValue();
|
||||
const containerRes = await containerStore.updateData(values, { closePublish: false });
|
||||
const values = getValues();
|
||||
const pickValues = pick(values, ['id', 'publish.key', 'publish.version', 'publish.fileName', 'publish.description']);
|
||||
const containerRes = await containerStore.updateData(pickValues, { closePublish: false });
|
||||
if (containerRes.code === 200) {
|
||||
const code = containerRes.data?.code || '-';
|
||||
const fileName = values['publish']?.['fileName'];
|
||||
@@ -139,12 +152,11 @@ const PublishFormModal = () => {
|
||||
const key = values['publish']['key'];
|
||||
const version = values['publish']['version'];
|
||||
const file = toFile(code, directoryAndName.name);
|
||||
const res = await uploadFileChunked(file, {
|
||||
const res = (await uploadFileChunked(file, {
|
||||
appKey: key,
|
||||
version,
|
||||
directory: directoryAndName.directory,
|
||||
});
|
||||
// @ts-ignore
|
||||
})) as any;
|
||||
if (res.code === 200) {
|
||||
message.success('upload success');
|
||||
} else {
|
||||
@@ -152,66 +164,67 @@ const PublishFormModal = () => {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onUpdate = async () => {
|
||||
const values = form.getFieldsValue();
|
||||
const values = getValues();
|
||||
containerStore.updateData(values);
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
containerStore.setShowEdit(false);
|
||||
form.resetFields();
|
||||
reset();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={containerStore.showEdit} onClose={() => containerStore.setShowEdit(false)}>
|
||||
<Dialog open={containerStore.showEdit} onClose={onClose}>
|
||||
<DialogTitle>Publish</DialogTitle>
|
||||
<DialogContent sx={{ padding: '10px', minWidth: '600px' }}>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={onFinish}
|
||||
labelCol={{
|
||||
span: 6,
|
||||
}}
|
||||
wrapperCol={{
|
||||
span: 18,
|
||||
}}>
|
||||
<Form.Item name='id' hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name={['publish', 'key']} label='App key' required>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name={['publish', 'version']} label='App Version' required>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name={['publish', 'fileName']} label='Filename' tooltip='可以是文件夹格式,比如(directory/a.name)' required>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name={['publish', 'description']} label='Description'>
|
||||
<Input.TextArea rows={4} />
|
||||
</Form.Item>
|
||||
<Form.Item label=' ' colon={false}>
|
||||
<div className='flex gap-3'>
|
||||
<Tooltip
|
||||
placement='top'
|
||||
title='根据文件名和code的字符串的内容,自动生成文件。并保存。如果是其他文件类型,转成base64上传。比如图片以类似data:image/jpeg;开头'>
|
||||
<Button variant='contained' color='primary' type='submit'>
|
||||
上传
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Button variant='contained' onClick={onUpdate}>
|
||||
保存
|
||||
<form className='flex flex-col gap-6 pt-2' onSubmit={handleSubmit(onFinish)}>
|
||||
<Controller
|
||||
name='publish.key'
|
||||
control={control}
|
||||
defaultValue=''
|
||||
render={({ field }) => <TextField {...field} label='App key' required fullWidth />}
|
||||
/>
|
||||
<Controller
|
||||
name='publish.version'
|
||||
control={control}
|
||||
defaultValue=''
|
||||
render={({ field }) => <TextField {...field} label='App Version' required fullWidth />}
|
||||
/>
|
||||
<Controller
|
||||
name='publish.fileName'
|
||||
control={control}
|
||||
defaultValue=''
|
||||
render={({ field }) => <TextField {...field} label='Filename' required fullWidth />}
|
||||
/>
|
||||
<Controller
|
||||
name='publish.description'
|
||||
control={control}
|
||||
defaultValue=''
|
||||
render={({ field }) => <TextField {...field} label='Description' multiline rows={4} fullWidth />}
|
||||
/>
|
||||
<div className='flex gap-3'>
|
||||
<Tooltip
|
||||
placement='top'
|
||||
title='根据文件名和code的字符串的内容,自动生成文件。并保存。如果是其他文件类型,转成base64上传。比如图片以类似data:image/jpeg;开头'>
|
||||
<Button variant='contained' color='primary' type='submit'>
|
||||
{t('Upload')}
|
||||
</Button>
|
||||
<Button onClick={onClose}>取消</Button>
|
||||
</div>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Tooltip>
|
||||
<Button variant='contained' onClick={onUpdate}>
|
||||
{t('Submit')}
|
||||
</Button>
|
||||
<Button onClick={onClose}>{t('Cancel')}</Button>
|
||||
</div>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
export const ContainerList = () => {
|
||||
const navicate = useNewNavigate();
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
const [modal, contextHolder] = useModal();
|
||||
const containerStore = useContainerStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
@@ -271,7 +284,7 @@ export const ContainerList = () => {
|
||||
e.stopPropagation();
|
||||
}}>
|
||||
{item.title || '-'}
|
||||
<div className='font-thin card-key ml-3'>{item.tags ? item.tags.join(', ') : ''}</div>
|
||||
<div className='font-thin card-key ml-3'>{item.tags ? item.tags?.join?.(', ') : ''}</div>
|
||||
</div>
|
||||
<div className='font-light text-xs mt-2'>{item.description ? item.description : '-'}</div>
|
||||
</div>
|
||||
|
||||
@@ -16,4 +16,3 @@ export const App = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export * from './module/Select';
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { query } from '@/modules';
|
||||
import { Select as AntSelect, SelectProps } from 'antd';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { message } from '@/modules/message';
|
||||
export const Select = (props: SelectProps) => {
|
||||
const [options, setOptions] = useState<{ value: string; id: string }[]>([]);
|
||||
useEffect(() => {
|
||||
fetch();
|
||||
}, []);
|
||||
const fetch = async () => {
|
||||
const res = await query.post({
|
||||
path: 'container',
|
||||
key: 'list',
|
||||
});
|
||||
if (res.code !== 200) {
|
||||
message.error(res.message || '获取容器列表失败');
|
||||
return;
|
||||
}
|
||||
const data = res.data || [];
|
||||
setOptions(
|
||||
data.map((item: any) => {
|
||||
return {
|
||||
label: item.title,
|
||||
value: item.id,
|
||||
};
|
||||
}),
|
||||
);
|
||||
};
|
||||
return (
|
||||
<AntSelect
|
||||
{...props}
|
||||
options={options}
|
||||
// onChange={(e) => {
|
||||
// const labelValue = options.find((item) => item.value === e);
|
||||
// props.onChange?.(e, options);
|
||||
// }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -4,13 +4,17 @@ import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import path from 'path-browserify';
|
||||
import prettyBytes from 'pretty-bytes';
|
||||
import clsx from 'clsx';
|
||||
import { isObjectNull } from '@/utils/is-null';
|
||||
import FileOutlined from '@ant-design/icons/FileOutlined';
|
||||
import FolderOutlined from '@ant-design/icons/FolderOutlined';
|
||||
import { IconButton } from '@kevisual/center-components/button/index.tsx';
|
||||
import { render, unmount } from '@kevisual/resources/pages/Bootstrap.tsx';
|
||||
import UploadOutlined from '@ant-design/icons/lib/icons/UploadOutlined';
|
||||
import { Tooltip } from '@mui/material';
|
||||
import { useResourceFileStore } from '@kevisual/resources/pages/store/resource-file.ts';
|
||||
import { FileDrawerApp } from '@kevisual/resources/pages/file/draw/FileDrawer.tsx';
|
||||
import { RefreshCw, Upload } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { UploadButton } from '@kevisual/resources/pages/upload/index.tsx';
|
||||
export const CardPath = ({ children }: any) => {
|
||||
const userAppStore = useFileStore(
|
||||
useShallow((state) => {
|
||||
@@ -32,28 +36,62 @@ export const CardPath = ({ children }: any) => {
|
||||
userAppStore.setPath(prefix.replace('root/', '') + '/');
|
||||
userAppStore.getList();
|
||||
};
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [usrname, appKey, version] = paths;
|
||||
const onUloadFinish = (res: any) => {
|
||||
console.log(res);
|
||||
userAppStore.getList();
|
||||
};
|
||||
return (
|
||||
<div className='border border-gray-200 rounded'>
|
||||
<div className='p-2'>
|
||||
<div className='flex flex-col'>
|
||||
<div className=' flex gap-2 py-2'>
|
||||
<div>Path: </div>
|
||||
<div className='flex'>
|
||||
{paths.map((item, index) => {
|
||||
const isLast = index === paths.length - 1;
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={clsx('select-none', !isLast && 'hover:text-gray-400 cursor-pointer')}
|
||||
onClick={(e) => {
|
||||
if (!isLast) {
|
||||
onDirectoryClick(paths.slice(0, index + 1).join('/'));
|
||||
}
|
||||
}}>
|
||||
{item}/
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<div className='flex justify-between'>
|
||||
<div className='flex gap-2 py-2'>
|
||||
<div>Path: </div>
|
||||
<div className='flex'>
|
||||
{paths.map((item, index) => {
|
||||
const isLast = index === paths.length - 1;
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={clsx('select-none', !isLast && 'hover:text-gray-400 cursor-pointer')}
|
||||
onClick={(e) => {
|
||||
if (!isLast) {
|
||||
onDirectoryClick(paths.slice(0, index + 1).join('/'));
|
||||
}
|
||||
}}>
|
||||
{item}/
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<Tooltip title={t('refresh')} placement='bottom'>
|
||||
<IconButton
|
||||
color='primary'
|
||||
onClick={() => {
|
||||
userAppStore.getList();
|
||||
}}>
|
||||
<RefreshCw />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
{version && (
|
||||
<>
|
||||
<Tooltip title={t('uploadDirectory')} placement='bottom'>
|
||||
<IconButton color='primary'>
|
||||
<UploadButton onlyIcon uploadDirectory icon={<Upload />} appKey={appKey} version={version} username={usrname} onUpload={onUloadFinish} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={t('upload')} placement='bottom'>
|
||||
<IconButton color='primary'>
|
||||
<UploadButton onlyIcon appKey={appKey} version={version} username={usrname} onUpload={onUloadFinish} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -65,6 +103,17 @@ export const CardPath = ({ children }: any) => {
|
||||
export const List = () => {
|
||||
const [tab, setTab] = useState<'folder' | 'upload'>('folder');
|
||||
const uploadRef = useRef<HTMLDivElement>(null);
|
||||
const { setOpenDrawer, setPrefix, setResource, getStatFile, setOnce } = useResourceFileStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
setOpenDrawer: state.setOpenDrawer,
|
||||
setPrefix: state.setPrefix,
|
||||
setResource: state.setResource,
|
||||
getStatFile: state.getStatFile,
|
||||
setOnce: state.setOnce,
|
||||
};
|
||||
}),
|
||||
);
|
||||
const userAppStore = useFileStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
@@ -79,6 +128,9 @@ export const List = () => {
|
||||
);
|
||||
useEffect(() => {
|
||||
userAppStore.getList();
|
||||
return () => {
|
||||
setOnce(null);
|
||||
};
|
||||
}, []);
|
||||
const onDirectoryClick = (prefix: string) => {
|
||||
userAppStore.setPath(prefix);
|
||||
@@ -114,7 +166,15 @@ export const List = () => {
|
||||
className=' border-t border-gray-200 flex gap-2 p-2'
|
||||
key={index}
|
||||
onClick={() => {
|
||||
userAppStore.getFile(item.name);
|
||||
setPrefix(item.name);
|
||||
setResource(item);
|
||||
getStatFile();
|
||||
setOpenDrawer(true);
|
||||
setOnce((data: any) => {
|
||||
// console.log(data);
|
||||
userAppStore.getList();
|
||||
});
|
||||
console.log(item);
|
||||
}}>
|
||||
<div>
|
||||
<FileOutlined />
|
||||
@@ -126,9 +186,7 @@ export const List = () => {
|
||||
})}
|
||||
</div>
|
||||
</CardPath>
|
||||
<div>
|
||||
<pre>{!isObjectNull(userAppStore.file) && JSON.stringify(userAppStore.file, null, 2)}</pre>
|
||||
</div>
|
||||
<FileDrawerApp />
|
||||
</>
|
||||
);
|
||||
}, [userAppStore.list, userAppStore.path]);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { Input, Modal } from 'antd';
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
import { useOrgStore } from '../store';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { Form } from 'antd';
|
||||
import { useNewNavigate } from '@/modules';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Tooltip, Button, ButtonGroup, Dialog, DialogTitle, DialogContent } from '@mui/material';
|
||||
@@ -18,10 +16,14 @@ import clsx from 'clsx';
|
||||
import { isObjectNull } from '@/utils/is-null';
|
||||
import { CardBlank } from '@/components/card/CardBlank';
|
||||
import { useLayoutStore } from '@/modules/layout/store';
|
||||
import { useModal } from '@kevisual/center-components/modal/Confirm.tsx';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { TextField } from '@mui/material';
|
||||
import { pick } from 'lodash-es';
|
||||
|
||||
const FormModal = () => {
|
||||
const [form] = Form.useForm();
|
||||
const { t } = useTranslation();
|
||||
const { control, handleSubmit, reset } = useForm();
|
||||
const userStore = useOrgStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
@@ -33,57 +35,59 @@ const FormModal = () => {
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const open = userStore.showEdit;
|
||||
if (open) {
|
||||
const isNull = isObjectNull(userStore.formData);
|
||||
if (isNull) {
|
||||
form.setFieldsValue({});
|
||||
} else form.setFieldsValue(userStore.formData);
|
||||
reset({});
|
||||
} else reset(userStore.formData);
|
||||
}
|
||||
}, [userStore.showEdit, userStore.formData]);
|
||||
return () => {
|
||||
reset({});
|
||||
};
|
||||
}, [userStore.showEdit, userStore.formData, reset]);
|
||||
|
||||
const onFinish = async (values: any) => {
|
||||
userStore.updateData(values);
|
||||
const pickValues = pick(values, ['id', 'username', 'description']);
|
||||
userStore.updateData(pickValues);
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
userStore.setShowEdit(false);
|
||||
form.setFieldsValue({});
|
||||
reset({});
|
||||
userStore.setFormData({});
|
||||
};
|
||||
|
||||
const isEdit = userStore.formData.id;
|
||||
|
||||
return (
|
||||
<Dialog open={userStore.showEdit} onClose={() => userStore.setShowEdit(false)}>
|
||||
<DialogTitle>{isEdit ? 'Edit' : 'Add'}</DialogTitle>
|
||||
<DialogContent sx={{ padding: '20px', minWidth: '600px' }}>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={onFinish}
|
||||
labelCol={{
|
||||
span: 4,
|
||||
}}
|
||||
wrapperCol={{
|
||||
span: 20,
|
||||
}}>
|
||||
<Form.Item name='id' hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name='username' label='username'>
|
||||
<Input disabled={isEdit} />
|
||||
</Form.Item>
|
||||
<Form.Item name='description' label='description'>
|
||||
<Input.TextArea rows={4} />
|
||||
</Form.Item>
|
||||
<Form.Item label=' ' colon={false}>
|
||||
<div className='flex gap-2'>
|
||||
<Button variant='contained' type='submit'>
|
||||
{t('Submit')}
|
||||
</Button>
|
||||
<Button className='ml-2' onClick={onClose}>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
</div>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<form onSubmit={handleSubmit(onFinish)}>
|
||||
<Controller
|
||||
name='username'
|
||||
control={control}
|
||||
defaultValue=''
|
||||
render={({ field }) => <TextField {...field} label='username' disabled={isEdit} fullWidth margin='normal' />}
|
||||
/>
|
||||
<Controller
|
||||
name='description'
|
||||
control={control}
|
||||
defaultValue=''
|
||||
render={({ field }) => <TextField {...field} label='description' multiline rows={4} fullWidth margin='normal' />}
|
||||
/>
|
||||
<div className='flex gap-2'>
|
||||
<Button variant='contained' type='submit'>
|
||||
{t('Submit')}
|
||||
</Button>
|
||||
<Button className='ml-2' onClick={onClose}>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
@@ -111,9 +115,7 @@ export const List = () => {
|
||||
};
|
||||
}),
|
||||
);
|
||||
const [codeEdit, setCodeEdit] = useState(false);
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
const [code, setCode] = useState('');
|
||||
const [modal, contextHolder] = useModal();
|
||||
useEffect(() => {
|
||||
userStore.getList();
|
||||
}, []);
|
||||
@@ -143,9 +145,7 @@ export const List = () => {
|
||||
className='flex text-sm gap flex-col w-[400px] max-h-[400px] bg-white p-4 rounded-lg'
|
||||
key={item.id}
|
||||
onClick={() => {
|
||||
setCode(item.code);
|
||||
userStore.setFormData(item);
|
||||
setCodeEdit(true);
|
||||
// userStore.setFormData(item);
|
||||
}}>
|
||||
<div className='px-4 cursor-pointer'>
|
||||
<div
|
||||
@@ -168,7 +168,6 @@ export const List = () => {
|
||||
onClick={(e) => {
|
||||
userStore.setFormData(item);
|
||||
userStore.setShowEdit(true);
|
||||
setCodeEdit(false);
|
||||
e.stopPropagation();
|
||||
}}>
|
||||
<EditOutlined />
|
||||
@@ -214,35 +213,6 @@ export const List = () => {
|
||||
<CardBlank className='w-[400px]' />
|
||||
</div>
|
||||
</div>
|
||||
<div className={clsx('bg-gray-100 border-gray-200 border-bg-slate-300 w-[600px] shark-0', !codeEdit && 'hidden', 'hidden')}>
|
||||
<div className='bg-white p-2'>
|
||||
<div className='mt-2 ml-2 flex gap-2'>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCodeEdit(false);
|
||||
userStore.setFormData({});
|
||||
}}>
|
||||
<LeftOutlined />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
userStore.updateData({ ...userStore.formData, code });
|
||||
}}>
|
||||
<SaveOutlined />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='h-[94%] p-2 rounded-2 shadow-xs'>
|
||||
<Input.TextArea
|
||||
value={code}
|
||||
onChange={(value) => {
|
||||
// setCode(value);
|
||||
}}
|
||||
className='h-full max-h-full scrollbar'
|
||||
style={{ overflow: 'auto' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FormModal />
|
||||
{contextHolder}
|
||||
|
||||
@@ -2,22 +2,24 @@ import { useShallow } from 'zustand/react/shallow';
|
||||
import { useOrgStore } from '../store';
|
||||
import { useParams } from 'react-router';
|
||||
import { useEffect } from 'react';
|
||||
import { Input, Modal, Select } from 'antd';
|
||||
import { message } from '@/modules/message';
|
||||
import DeleteOutlined from '@ant-design/icons/DeleteOutlined';
|
||||
import LeftOutlined from '@ant-design/icons/LeftOutlined';
|
||||
import PlusOutlined from '@ant-design/icons/PlusOutlined';
|
||||
import { Tooltip, Button, ButtonGroup, Dialog, DialogTitle, DialogContent } from '@mui/material';
|
||||
import { IconButton } from '@kevisual/center-components/button/index.tsx';
|
||||
import { Form } from 'antd';
|
||||
import { useNewNavigate } from '@/modules';
|
||||
import { isObjectNull } from '@/utils/is-null';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import clsx from 'clsx';
|
||||
import { useModal } from '@kevisual/center-components/modal/Confirm.tsx';
|
||||
import { TextField } from '@mui/material';
|
||||
import { Select } from '@kevisual/center-components/select/index.tsx';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import EditOutlined from '@ant-design/icons/EditOutlined';
|
||||
|
||||
const FormModal = () => {
|
||||
const [form] = Form.useForm();
|
||||
const { control, handleSubmit, reset } = useForm();
|
||||
const userStore = useOrgStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
@@ -29,17 +31,21 @@ const FormModal = () => {
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const open = userStore.showEdit;
|
||||
if (open) {
|
||||
const isNull = isObjectNull(userStore.formData);
|
||||
if (isNull) {
|
||||
form.setFieldsValue({});
|
||||
} else form.setFieldsValue(userStore.formData);
|
||||
reset({});
|
||||
} else reset(userStore.formData);
|
||||
}
|
||||
}, [userStore.showEdit, userStore.formData]);
|
||||
return () => {
|
||||
reset({});
|
||||
};
|
||||
}, [userStore.showEdit, userStore.formData, reset]);
|
||||
|
||||
const onFinish = async (values: any) => {
|
||||
//
|
||||
console.log(values);
|
||||
const username = values.username;
|
||||
const role = values.role;
|
||||
@@ -47,57 +53,55 @@ const FormModal = () => {
|
||||
message.error('username is required');
|
||||
return;
|
||||
}
|
||||
userStore.addUser({ username: username, role });
|
||||
const res = await userStore.addUser({ username: username, role });
|
||||
if (res?.code === 200) {
|
||||
userStore.setShowEdit(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
userStore.setShowEdit(false);
|
||||
userStore.setFormData({});
|
||||
};
|
||||
|
||||
const isEdit = userStore.formData.id;
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Dialog open={userStore.showEdit} onClose={() => userStore.setShowEdit(false)}>
|
||||
<DialogTitle>{isEdit ? 'Edit' : 'Add'}</DialogTitle>
|
||||
<DialogContent sx={{ padding: '20px', minWidth: '600px' }}>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={onFinish}
|
||||
labelCol={{
|
||||
span: 4,
|
||||
}}
|
||||
wrapperCol={{
|
||||
span: 20,
|
||||
}}>
|
||||
<Form.Item name='id' hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name='username' label='username'>
|
||||
<Input disabled={isEdit} />
|
||||
</Form.Item>
|
||||
<Form.Item name='role' label='role'>
|
||||
<Select
|
||||
options={[
|
||||
{
|
||||
label: 'admin',
|
||||
value: 'admin',
|
||||
},
|
||||
{
|
||||
label: 'member',
|
||||
value: 'member',
|
||||
},
|
||||
]}></Select>
|
||||
</Form.Item>
|
||||
<Form.Item label=' ' colon={false}>
|
||||
<div className='flex gap-2'>
|
||||
<Button variant='contained' type='submit'>
|
||||
{t('Submit')}
|
||||
</Button>
|
||||
<Button className='ml-2' onClick={onClose}>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
</div>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<form className='flex flex-col gap-6' onSubmit={handleSubmit(onFinish)}>
|
||||
<Controller
|
||||
name='username'
|
||||
control={control}
|
||||
defaultValue=''
|
||||
render={({ field }) => <TextField {...field} label='username' disabled={isEdit} fullWidth margin='normal' />}
|
||||
/>
|
||||
<Controller
|
||||
name='role'
|
||||
control={control}
|
||||
defaultValue=''
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
{...field}
|
||||
options={[
|
||||
{ label: 'admin', value: 'admin' },
|
||||
{ label: 'user', value: 'user' },
|
||||
]}
|
||||
fullWidth
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<div className='flex gap-2'>
|
||||
<Button variant='contained' type='submit'>
|
||||
{t('Submit')}
|
||||
</Button>
|
||||
<Button className='ml-2' onClick={onClose}>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
@@ -106,7 +110,7 @@ const FormModal = () => {
|
||||
export const UserList = () => {
|
||||
const param = useParams();
|
||||
const navicate = useNewNavigate();
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
const [modal, contextHolder] = useModal();
|
||||
const { t } = useTranslation();
|
||||
const orgStore = useOrgStore(
|
||||
useShallow((state) => {
|
||||
@@ -173,8 +177,18 @@ export const UserList = () => {
|
||||
variant='contained'
|
||||
color='primary'
|
||||
sx={{ color: 'white', '& .MuiButton-root': { color: 'white', minWidth: '32px', width: '32px', height: '32px', padding: '6px' } }}>
|
||||
{/* <Tooltip title='edit'>
|
||||
<Button
|
||||
disabled={isOwner}
|
||||
onClick={() => {
|
||||
orgStore.setUserFormData(item);
|
||||
orgStore.setShowUserEdit(true);
|
||||
}}>
|
||||
<EditOutlined />
|
||||
</Button>
|
||||
</Tooltip> */}
|
||||
<Tooltip title='delete'>
|
||||
<IconButton
|
||||
<Button
|
||||
disabled={isOwner}
|
||||
onClick={() => {
|
||||
modal.confirm({
|
||||
@@ -186,7 +200,7 @@ export const UserList = () => {
|
||||
});
|
||||
}}>
|
||||
<DeleteOutlined />
|
||||
</IconButton>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,7 @@ type OrgStore = {
|
||||
orgId: string;
|
||||
setOrgId: (orgId: string) => void;
|
||||
getOrg: () => Promise<any>;
|
||||
addUser: (data: { userId?: string; username?: string; role?: string }) => Promise<void>;
|
||||
addUser: (data: { userId?: string; username?: string; role?: string }) => Promise<any>;
|
||||
removeUser: (userId: string) => Promise<void>;
|
||||
};
|
||||
export const useOrgStore = create<OrgStore>((set, get) => {
|
||||
@@ -113,6 +113,7 @@ export const useOrgStore = create<OrgStore>((set, get) => {
|
||||
} else {
|
||||
message.error(res.message || 'Request failed');
|
||||
}
|
||||
return res
|
||||
},
|
||||
removeUser: async (userId: string) => {
|
||||
const { orgId } = get();
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { Input, Modal } from 'antd';
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
import { useUserStore } from '../store';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { Form } from 'antd';
|
||||
import EditOutlined from '@ant-design/icons/EditOutlined';
|
||||
import SaveOutlined from '@ant-design/icons/SaveOutlined';
|
||||
import DeleteOutlined from '@ant-design/icons/DeleteOutlined';
|
||||
@@ -14,8 +12,13 @@ import { CardBlank } from '@kevisual/center-components/card/CardBlank.tsx';
|
||||
import { Dialog, ButtonGroup, Button, DialogContent, DialogTitle } from '@mui/material';
|
||||
import { IconButton } from '@kevisual/center-components/button/index.tsx';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useModal } from '@kevisual/center-components/modal/Confirm.tsx';
|
||||
import { TextField } from '@mui/material';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { pick } from 'lodash-es';
|
||||
|
||||
const FormModal = () => {
|
||||
const [form] = Form.useForm();
|
||||
const { control, handleSubmit, reset } = useForm();
|
||||
const userStore = useUserStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
@@ -27,25 +30,34 @@ const FormModal = () => {
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const open = userStore.showEdit;
|
||||
if (open) {
|
||||
const isNull = isObjectNull(userStore.formData);
|
||||
if (isNull) {
|
||||
form.setFieldsValue({});
|
||||
} else form.setFieldsValue(userStore.formData);
|
||||
reset({});
|
||||
} else reset(userStore.formData);
|
||||
}
|
||||
return () => {
|
||||
reset({});
|
||||
};
|
||||
}, [userStore.showEdit]);
|
||||
const onFinish = async (values: any) => {
|
||||
userStore.updateData(values);
|
||||
|
||||
const onFinish = (values: any) => {
|
||||
const pickValues = pick(values, ['id', 'username', 'description']);
|
||||
userStore.updateData(pickValues);
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
userStore.setShowEdit(false);
|
||||
form.setFieldsValue({});
|
||||
reset({});
|
||||
userStore.setFormData({});
|
||||
};
|
||||
|
||||
const isEdit = userStore.formData.id;
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={userStore.showEdit}
|
||||
@@ -57,39 +69,30 @@ const FormModal = () => {
|
||||
}}>
|
||||
<DialogTitle>{isEdit ? t('Edit') : t('Add')}</DialogTitle>
|
||||
<DialogContent>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={onFinish}
|
||||
labelCol={{
|
||||
span: 4,
|
||||
}}
|
||||
wrapperCol={{
|
||||
span: 20,
|
||||
}}>
|
||||
<Form.Item name='id' hidden>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name='username' label='username'>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name='description' label='description'>
|
||||
<Input.TextArea rows={4} />
|
||||
</Form.Item>
|
||||
<Form.Item label=' ' colon={false}>
|
||||
<form onSubmit={handleSubmit(onFinish)}>
|
||||
<Controller name='username' control={control} defaultValue='' render={({ field }) => <TextField {...field} label='username' fullWidth />} />
|
||||
<Controller
|
||||
name='description'
|
||||
control={control}
|
||||
defaultValue=''
|
||||
render={({ field }) => <TextField {...field} label='description' multiline rows={4} fullWidth />}
|
||||
/>
|
||||
<div>
|
||||
<Button type='submit' variant='contained'>
|
||||
{t('Submit')}
|
||||
</Button>
|
||||
<Button className='ml-2' type='reset' onClick={onClose}>
|
||||
<Button className='ml-2' type='button' onClick={onClose}>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export const List = () => {
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
const [modal, contextHolder] = useModal();
|
||||
const userStore = useUserStore(
|
||||
useShallow((state) => {
|
||||
return {
|
||||
@@ -104,8 +107,6 @@ export const List = () => {
|
||||
};
|
||||
}),
|
||||
);
|
||||
const [codeEdit, setCodeEdit] = useState(false);
|
||||
const [code, setCode] = useState('');
|
||||
useEffect(() => {
|
||||
userStore.getList();
|
||||
}, []);
|
||||
@@ -132,9 +133,7 @@ export const List = () => {
|
||||
className='flex text-sm gap flex-col w-[400px] max-h-[400px] bg-white p-4 rounded-lg'
|
||||
key={item.id}
|
||||
onClick={() => {
|
||||
setCode(item.code);
|
||||
userStore.setFormData(item);
|
||||
setCodeEdit(true);
|
||||
// userStore.setFormData(item);
|
||||
}}>
|
||||
<div className='px-4 cursor-pointer'>
|
||||
<div
|
||||
@@ -156,7 +155,6 @@ export const List = () => {
|
||||
onClick={(e) => {
|
||||
userStore.setFormData(item);
|
||||
userStore.setShowEdit(true);
|
||||
setCodeEdit(false);
|
||||
e.stopPropagation();
|
||||
}}>
|
||||
<EditOutlined />
|
||||
@@ -188,36 +186,6 @@ export const List = () => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={clsx('bg-gray-100 border-l-gray-200 border-bg-slate-300 w-[600px] shark-0', !codeEdit && 'hidden', 'hidden')}>
|
||||
<div className='bg-white p-2'>
|
||||
<div className='mt-2 ml-2 flex gap-2'>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCodeEdit(false);
|
||||
userStore.setFormData({});
|
||||
}}>
|
||||
<LeftOutlined />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
console.log('save', userStore.formData);
|
||||
userStore.updateData({ ...userStore.formData, code });
|
||||
}}>
|
||||
<SaveOutlined />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='h-[94%] p-2 rounded-2 shadow-xs'>
|
||||
<Input.TextArea
|
||||
value={code}
|
||||
onChange={(value) => {
|
||||
// setCode(value);
|
||||
}}
|
||||
className='h-full max-h-full scrollbar'
|
||||
style={{ overflow: 'auto' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FormModal />
|
||||
{contextHolder}
|
||||
|
||||
Reference in New Issue
Block a user