feat: 去掉antd

This commit is contained in:
2025-03-20 21:47:50 +08:00
parent c206add7eb
commit cfd263a1e7
36 changed files with 1369 additions and 769 deletions

View File

@@ -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

View File

@@ -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');

View File

@@ -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');
}
},
};
});

View File

@@ -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>

View File

@@ -16,4 +16,3 @@ export const App = () => {
);
};
export * from './module/Select';

View File

@@ -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);
// }}
/>
);
};

View File

@@ -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]);

View File

@@ -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}

View File

@@ -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>

View File

@@ -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();

View File

@@ -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}