diff --git a/package.json b/package.json index 6ff1fe5..e28df5e 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-i18next": "^14.1.0", + "react-if": "^4.1.5", "react-layout-kit": "^1.9.0", "react-rnd": "^10.4.2-test2", "react-use": "^17.5.0", diff --git a/src/components/action/Action.tsx b/src/components/action/Action.tsx new file mode 100644 index 0000000..e2e0ea9 --- /dev/null +++ b/src/components/action/Action.tsx @@ -0,0 +1,23 @@ +import { Button, ButtonProps } from 'antd' +import { useStyle } from './style' + +export interface ActionProps extends ButtonProps { + as?: string +} + +const Action = ({ title, as, children, ...props }: ActionProps) => { + + const { styles } = useStyle() + + const isLink = as === 'a' || props.type === 'link' + + return ( + + + + ) +} + +export default Action \ No newline at end of file diff --git a/src/components/action/index.ts b/src/components/action/index.ts new file mode 100644 index 0000000..4cfc7b6 --- /dev/null +++ b/src/components/action/index.ts @@ -0,0 +1 @@ +export * from './Action' \ No newline at end of file diff --git a/src/components/action/style.ts b/src/components/action/style.ts new file mode 100644 index 0000000..7a663c5 --- /dev/null +++ b/src/components/action/style.ts @@ -0,0 +1,17 @@ +import { createStyles } from '@/theme' + +export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { + const prefix = `${prefixCls}-${token?.proPrefix}-action` + + const container = css` + + ` + const actionA = css` + padding: 0 2px; + ` + + return { + container: cx(prefix, props?.className, container), + actionA, + } +}) \ No newline at end of file diff --git a/src/components/drawer-picker/DrawerPicker.tsx b/src/components/drawer-picker/DrawerPicker.tsx index 6eac557..83f76c5 100644 --- a/src/components/drawer-picker/DrawerPicker.tsx +++ b/src/components/drawer-picker/DrawerPicker.tsx @@ -1,42 +1,60 @@ import { Button, Drawer, DrawerProps } from 'antd' -import React, { useState } from 'react' +import React, { forwardRef, useState, useImperativeHandle, memo } from 'react' import { useStyle } from './style' -import { generateUUID } from '@/utils/uuid.ts' export interface DrawerPickerProps extends DrawerProps { - target?: React.ReactNode + target?: React.ReactNode | boolean children?: React.ReactNode - key?: string + id?: string foreRender?: boolean } -const DrawerPicker = ({ children, target, foreRender, ...props }: DrawerPickerProps) => { +export interface DrawerPickerRef { + open: () => void, + close: () => void +} + +const DrawerPicker = forwardRef(( + { + children, + target, + foreRender, + ...props + }: DrawerPickerProps, ref) => { + + const { styles, cx } = useStyle() - const { styles } = useStyle() + const [ open, setOpen ] = useState(() => false) - const [ open, setOpen ] = useState(false) + useImperativeHandle(ref, () => ({ + open: () => { + setOpen(true) + }, + close: () => { + setOpen(false) + } + }), []) const getTarget = () => { + if (target === false) return null const def = - return {target ?? def} + return
{ + setOpen(true) + }}> + {target ?? def} +
} - return ( -
-
{ - setOpen(true) - }}> - {getTarget()} -
+
+ {getTarget()} { (foreRender || open) && setOpen(false)} >{children} } -
) -} +}) -export default DrawerPicker \ No newline at end of file +export default memo(DrawerPicker) \ No newline at end of file diff --git a/src/components/status/Status.tsx b/src/components/status/Status.tsx new file mode 100644 index 0000000..5415959 --- /dev/null +++ b/src/components/status/Status.tsx @@ -0,0 +1,59 @@ +import { Tag, TagProps } from 'antd' +import { useTranslation } from '@/i18n.ts' +import { SyncOutlined } from '@ant-design/icons' + +export interface StatusProps extends TagProps { + status: string +} + + +const getColor = (status: string) => { + if (status.includes('error') || status.includes('err')) { + return 'danger' + } + switch (status) { + case 'running': + return 'success' + case 'stopped': + return 'danger' + case 'unhealthy': + case 'paused': + case 'exited': + case 'dead': + case 'removing': + return 'warning' + default: + return 'primary' + } +} + +const loadingStatus = [ + 'installing', + 'building', + 'restarting', + 'upgrading', + 'rebuilding', + 'recreating', + 'creating', + 'starting', + 'removing', + 'applying', +] + +const loadingIcon = (status: string): boolean => { + return loadingStatus.indexOf(status) > -1 +} +export const Status = ({ status = 'running', ...props }: StatusProps) => { + + const { t } = useTranslation() + const icon = loadingIcon(status) ? : null + return ( + <> + {t(`status.${status}`, status)} + + ) +} + +export default Status \ No newline at end of file diff --git a/src/components/status/index.ts b/src/components/status/index.ts new file mode 100644 index 0000000..b97fff9 --- /dev/null +++ b/src/components/status/index.ts @@ -0,0 +1 @@ +export * from './Status.tsx' \ No newline at end of file diff --git a/src/locales/lang/en-US.ts b/src/locales/lang/en-US.ts index 592e5e6..1356777 100644 --- a/src/locales/lang/en-US.ts +++ b/src/locales/lang/en-US.ts @@ -48,6 +48,7 @@ export default { news: 'Add newly', add: 'Add', cancel: 'Cancel', + ok: 'OK', edit: 'Edit', delete: 'Delete', batchDel: 'Batch Delete', diff --git a/src/locales/lang/status/zh-CN.ts b/src/locales/lang/status/zh-CN.ts new file mode 100644 index 0000000..9f5ad9a --- /dev/null +++ b/src/locales/lang/status/zh-CN.ts @@ -0,0 +1,41 @@ +export default { + running: '已启动', + done: '已完成', + success: '成功', + waiting: '执行中', + waiting1: '等待中', + failed: '失败', + stopped: '已停止', + error: '失败', + created: '已创建', + restarting: '重启中', + uploading: '上传中', + unhealthy: '异常', + removing: '移除中', + paused: '已暂停', + exited: '已停止', + dead: '已结束', + installing: '安装中', + enabled: '已启用', + disabled: '已停止', + normal: '正常', + building: '制作镜像中', + downloaderr: '下载失败', + upgrading: '升级中', + upgradeerr: '升级失败', + pullerr: '镜像拉取失败', + rebuilding: '重建中', + deny: '已屏蔽', + accept: '已放行', + used: '已使用', + unUsed: '未使用', + starting: '启动中', + recreating: '重建中', + creating: '创建中', + systemrestart: '中断', + init: '等待申请', + ready: '正常', + applying: '申请中', + applyerror: '失败', + syncerr: '失败', +} \ No newline at end of file diff --git a/src/locales/lang/zh-CN.ts b/src/locales/lang/zh-CN.ts index f0340cb..8f72057 100644 --- a/src/locales/lang/zh-CN.ts +++ b/src/locales/lang/zh-CN.ts @@ -1,87 +1,90 @@ import antdZh from 'antd/locale/zh_CN' import menus from './pages/system/menus/zh-CN.ts' import roles from './pages/system/roles/zh-CN.ts' +import status from './status/zh-CN.ts' export default { - ...antdZh, - error: { - '404': { - title: '无法找到', - message: '找不到此页面' + ...antdZh, + status, + error: { + '404': { + title: '无法找到', + message: '找不到此页面' + }, + '403': { + title: '没有权限', + message: '对不起,您没有权限查看此页面。' + }, + 'error': { + title: '错误信息', + }, }, - '403': { - title: '没有权限', - message: '对不起,您没有权限查看此页面。' + route: { + goBack: '返回', }, - 'error': { - title: '错误信息', + app: { + header: { + logout: '退出登录', + } + }, + login: { + title: '账户密码登录', + username: '用户名', + usernameMsg: '请输入用户名', + password: '密码', + passwordMsg: '请输入密码', + code: '验证码', + codeMsg: '请输入验证码', + submit: '登录', + success: '登录成功' + }, + home: { + welcome: '欢迎使用' }, - }, - route: { - goBack: '返回', - }, - app: { - header: { - logout: '退出登录', - } - }, - login: { - title: '账户密码登录', - username: '用户名', - usernameMsg: '请输入用户名', - password: '密码', - passwordMsg: '请输入密码', - code: '验证码', - codeMsg: '请输入验证码', - submit: '登录', - success: '登录成功' - }, - home: { - welcome: '欢迎使用' - }, - system: { - menus, - roles - }, - actions: { - news: '新增', - add: '添加', - edit: '编辑', - cancel: '取消', - delete: '删除', - batchDel: '批量删除', - reset: '重置', - clear: '清空', - close: '关闭', - }, - message: { - infoTitle: '提示', - errorTitle: '错误', - successTitle: '成功', - warningTitle: '警告', - batchDelete: '确定要删除所选数据吗?', - deleteConfirm: '确定要删除吗?', - success: '提交成功', - fail: '提交失败', - saveSuccess: '保存成功', - editSuccess: '修改成功', - deleteSuccess: '删除成功', - saveFail: '保存失败', - emptyData: '暂无数据', - emptyDataAdd: '暂无数据,点击添加', - required: '此项为必填项', - }, - rules: { - required: '此项为必填项', - }, - tabs: { - refresh: '刷新', - maximize: '最大化', - closeCurrent: '关闭当前', - closeLeft: '关闭左侧', - closeRight: '关闭右侧', - closeOther: '关闭其它', - closeAll: '关闭所有' - } + system: { + menus, + roles + }, + actions: { + news: '新增', + add: '添加', + edit: '编辑', + ok: '确定', + cancel: '取消', + delete: '删除', + batchDel: '批量删除', + reset: '重置', + clear: '清空', + close: '关闭', + }, + message: { + infoTitle: '提示', + errorTitle: '错误', + successTitle: '成功', + warningTitle: '警告', + batchDelete: '确定要删除所选数据吗?', + deleteConfirm: '确定要删除吗?', + success: '提交成功', + fail: '提交失败', + saveSuccess: '保存成功', + editSuccess: '修改成功', + deleteSuccess: '删除成功', + saveFail: '保存失败', + emptyData: '暂无数据', + emptyDataAdd: '暂无数据,点击添加', + required: '此项为必填项', + }, + rules: { + required: '此项为必填项', + }, + tabs: { + refresh: '刷新', + maximize: '最大化', + closeCurrent: '关闭当前', + closeLeft: '关闭左侧', + closeRight: '关闭右侧', + closeOther: '关闭其它', + closeAll: '关闭所有' + } } diff --git a/src/pages/websites/ssl/ca/Detail.tsx b/src/pages/websites/ssl/ca/Detail.tsx index b214aa9..7cbf23b 100644 --- a/src/pages/websites/ssl/ca/Detail.tsx +++ b/src/pages/websites/ssl/ca/Detail.tsx @@ -18,7 +18,7 @@ const Detail = (props: DrawerProps) => { const options = useMemo(() => { return [ { label: t(`${prefix}.base`, '机构详情'), value: 'base' }, - { label: t(`${prefix}.src`, 'src'), value: 'csr' }, + { label: t(`${prefix}.csr`, 'csr'), value: 'csr' }, { label: t(`${prefix}.private_key`, '私钥'), value: 'private_key' }, ] }, []) @@ -31,6 +31,15 @@ const Detail = (props: DrawerProps) => { } }, [ copyState ]) + useEffect(() => { + return () => { + setUI({ + open: false, + record: {}, + }) + } + }, []) + const render = useMemo(() => { switch (key) { case 'base': diff --git a/src/pages/websites/ssl/components/Detail.tsx b/src/pages/websites/ssl/components/Detail.tsx new file mode 100644 index 0000000..17b3fe8 --- /dev/null +++ b/src/pages/websites/ssl/components/Detail.tsx @@ -0,0 +1,127 @@ +import { Segmented, Drawer, DrawerProps, Input, Button, Flex, message, Descriptions } from 'antd' +import { useEffect, useMemo, useState } from 'react' +import { useTranslation } from '@/i18n.ts' +import { useAtom } from 'jotai' +import { detailAtom } from './store.ts' +import { useStyle } from './style.ts' +import { useCopyToClipboard } from 'react-use' +import { getProvider } from '@/store/websites/ssl.ts' + +const Detail = (props: DrawerProps) => { + + + const prefix = 'website.ssl.detail' + const { t } = useTranslation() + const { styles } = useStyle() + const [ key, setKey ] = useState('base') + const [ ui, setUI ] = useAtom(detailAtom) + const [ copyState, setCopy ] = useCopyToClipboard() + + const options = useMemo(() => { + return [ + { label: t(`${prefix}.base`, '证书信息'), value: 'base' }, + { label: t(`${prefix}.pem`, '证书'), value: 'pem' }, + { label: t(`${prefix}.private_key`, '私钥'), value: 'private_key' }, + ] + }, []) + + useEffect(() => { + if (copyState.error) { + message.error(t('message.copyError', '复制失败')) + } else if (copyState.value) { + message.success(t('message.copySuccess', '复制成功')) + } + }, [ copyState ]) + + useEffect(() => { + return () => { + setUI({ + open: false, + record: {}, + }) + } + }, []) + + const render = useMemo(() => { + switch (key) { + case 'base': + return
+ + + {ui.record?.primary_domain} + + + {ui.record?.domains} + + + {ui.record?.type} + + + {ui.record?.organization} + + + {ui.record?.start_date} + + + {ui.record?.expire_date} + + + {getProvider(ui.record?.provider)} + + +
+ case 'pem': + return + + + + + + case 'private_key': + return + + + + + + default: + return null + } + }, [ key, ui.record ]) + + return ( + + setUI(prev => ({ + ...prev, + open: false + })) + } + > + +
+ {render} +
+
+ ) +} + +export default Detail \ No newline at end of file diff --git a/src/pages/websites/ssl/components/Upload.tsx b/src/pages/websites/ssl/components/Upload.tsx new file mode 100644 index 0000000..8ea70a6 --- /dev/null +++ b/src/pages/websites/ssl/components/Upload.tsx @@ -0,0 +1,77 @@ +import { Select, Form, Input } from 'antd' +import { useTranslation } from '@/i18n.ts' +import { useAtom } from 'jotai' +import { uploadAtom, uploadType } from './store.ts' +import { Else, If, Then } from 'react-if' +import { MutableRefObject, useEffect } from 'react' +import { FormInstance } from 'antd/lib' + +export interface UploadProps { + form?: FormInstance + formRef?: MutableRefObject +} + +const Upload = (props: UploadProps) => { + const { t } = useTranslation() + const prefix = 'website.ssl.upload' + const [ form ] = Form.useForm() + const [ dto, updateDto ] = useAtom(uploadAtom) + + useEffect(() => { + form.setFieldsValue(dto) + }, []) + + props.formRef!.current = props.form ?? form + + return ( +
{ + updateDto(prev => ({ + ...prev, + ..._change + })) + }} + scrollToFirstError={true} + > + + + + + + + + + + + +
+ ) +} + +export default Upload \ No newline at end of file diff --git a/src/pages/websites/ssl/components/store.ts b/src/pages/websites/ssl/components/store.ts new file mode 100644 index 0000000..36c924a --- /dev/null +++ b/src/pages/websites/ssl/components/store.ts @@ -0,0 +1,23 @@ +import { atom } from 'jotai' +import { WebSite } from '@/types' +import { t } from 'i18next' + +type DetailUI = { + open: boolean, + record?: WebSite.ISSL, + +} + +export const detailAtom = atom({ + open: false, +}) + +export const uploadType = [ + { label: t('website.ssl.upload.type_paste', '粘贴代码'), value: 'paste' }, + { label: t('website.ssl.upload.type_local', '选择服务器文件'), value: 'local' }, +] + + +export const uploadAtom = atom({ + type: 'paste' +}) diff --git a/src/pages/websites/ssl/components/style.ts b/src/pages/websites/ssl/components/style.ts new file mode 100644 index 0000000..57e3d35 --- /dev/null +++ b/src/pages/websites/ssl/components/style.ts @@ -0,0 +1,23 @@ +import { createStyles } from '@/theme' + +export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { + const prefix = `${prefixCls}-${token?.proPrefix}-ssl-detail` + + const container = css` + + ` + + const content = css` + padding: 20px 0; + + .ant-descriptions-item-label{ + font-weight: bold; + width: 50%; + } + ` + + return { + container: cx(prefix, props?.className, container), + content, + } +}) \ No newline at end of file diff --git a/src/pages/websites/ssl/index.tsx b/src/pages/websites/ssl/index.tsx index 8f77f2b..ccebf0b 100644 --- a/src/pages/websites/ssl/index.tsx +++ b/src/pages/websites/ssl/index.tsx @@ -1,4 +1,4 @@ -import { useAtom, useAtomValue } from 'jotai' +import { useAtom, useAtomValue, useSetAtom } from 'jotai' import { deleteSslAtom, getProvider, KeyTypeEnum, @@ -7,15 +7,15 @@ import { saveOrUpdateSslAtom, sslListAtom, sslPageAtom, - sslSearchAtom + sslSearchAtom, uploadSslAtom } from '@/store/websites/ssl.ts' import ListPageLayout from '@/layout/ListPageLayout.tsx' import { BetaSchemaForm, ProColumns, ProFormColumnsType, ProTable } from '@ant-design/pro-components' -import { memo, useMemo, useState } from 'react' +import { memo, useMemo, useRef, useState } from 'react' import { useTranslation } from '@/i18n.ts' -import { Button, Form, Popconfirm } from 'antd' +import { Button, Form, Popconfirm, Space } from 'antd' import { PlusOutlined } from '@ant-design/icons' -import DrawerPicker from '@/components/drawer-picker/DrawerPicker.tsx' +import DrawerPicker, { DrawerPickerRef } from '@/components/drawer-picker/DrawerPicker.tsx' import AcmeList from './acme/AcmeList.tsx' import { acmeListAtom, AcmeType, getAcmeAccountTypeName } from '@/store/websites/acme.ts' import { dnsListAtom, getDNSTypeName } from '@/store/websites/dns.ts' @@ -23,12 +23,20 @@ import DNSList from './dns/DNSList.tsx' import CAList from './ca/CAList.tsx' import { WebSite } from '@/types' import Switch from '@/components/switch' +import { Else, If, Then } from 'react-if' +import Action from '@/components/action/Action.tsx' +import { Status } from '@/components/status' +import SSLDetail from './components/Detail.tsx' +import { detailAtom } from './components/store.ts' +import Upload from './components/Upload.tsx' +import { FormInstance } from 'antd/lib' const SSL = () => { const { t } = useTranslation() const [ form ] = Form.useForm() + const uploadFormRef = useRef() const [ page, setPage ] = useAtom(sslPageAtom) const [ search, setSearch ] = useAtom(sslSearchAtom) const { data: acmeData, isLoading: acmeLoading } = useAtomValue(acmeListAtom) @@ -36,7 +44,9 @@ const SSL = () => { const { data, isLoading, isFetching, refetch } = useAtomValue(sslListAtom) const { mutate: saveOrUpdate, isSuccess, isPending: isSubmitting } = useAtomValue(saveOrUpdateSslAtom) const { mutate: deleteSSL, isPending: isDeleting } = useAtomValue(deleteSslAtom) - + const { mutate: uploadSSL, isSuccess: isUploadSuccess, isPending: isUploading } = useAtomValue(uploadSslAtom) + const updateDetail = useSetAtom(detailAtom) + const uploadDrawerRef = useRef() const [ open, setOpen ] = useState(false) const columns = useMemo[]>(() => { @@ -80,6 +90,14 @@ const SSL = () => { } }, { + title: t('website.ssl.columns.status', '状态'), + dataIndex: 'status', + render: (_, record) => { + return + }, + hideInForm: true, + }, + { title: t('website.ssl.columns.keyType', '密钥算法'), dataIndex: 'key_type', hideInTable: true, @@ -196,19 +214,61 @@ const SSL = () => { { title: t('website.ssl.columns.expire_date', '过期时间'), dataIndex: 'expire_date', - valueType: 'dateTime' + valueType: 'dateTime', + hideInForm: true, }, { title: t('website.ssl.columns.option', '操作'), valueType: 'option', key: 'option', + fixed: 'right', + width: 300, render: (_, record) => [ - { - }} + { + updateDetail({ + open: true, + record + }) + }} > - {t('actions.edit', '编辑')} - , + {t('actions.detail', '详情')} + , + record.status !== 'manual'}> + + { + + }} + > + {t('actions.apply', '申请')} + + + + { + + }} + > + {t('actions.update', '更新')} + + + + , + { + + }} + > + {t('actions.download', '下载')} + , { }> , + , + + + ]} + target={false} + > + + + ) } diff --git a/src/service/websites.ts b/src/service/websites.ts index 90848ef..1683852 100644 --- a/src/service/websites.ts +++ b/src/service/websites.ts @@ -4,7 +4,11 @@ import request from '@/request.ts' const websitesServ = { ssl: { - ...createCURD('/website/ssl') + ...createCURD('/website/ssl'), + upload: async(params: WebSite.SSLUploadDto)=>{ + return request.post('/website/ssl/upload', params) + } + }, acme: { ...createCURD('/website/acme') @@ -17,7 +21,6 @@ const websitesServ = { obtainSsl: async (params: WebSite.ISSLObtainByCA) => { return request.post('/website/ca/obtain_ssl', params) }, - } } diff --git a/src/store/websites/ssl.ts b/src/store/websites/ssl.ts index a020f73..f4fa6f6 100644 --- a/src/store/websites/ssl.ts +++ b/src/store/websites/ssl.ts @@ -4,7 +4,7 @@ import { atomWithMutation, atomWithQuery } from 'jotai-tanstack-query' import websitesServ from '@/service/websites.ts' import { message } from 'antd' import { t } from 'i18next' -import { ISSL, SSLSearchParam } from '@/types/website/ssl' +import { ISSL, SSLSearchParam, SSLUploadDto } from '@/types/website/ssl' export enum ProviderTypeEnum { DnsAccount = 'dnsAccount', @@ -96,4 +96,16 @@ export const deleteSslAtom = atomWithMutation(get => ({ message.success(t('message.deleteSuccess', '删除成功')) get(sslListAtom).refetch() } +})) + +//upload +export const uploadSslAtom = atomWithMutation(get => ({ + mutationKey: [ 'sslUpload' ], + mutationFn: async (data) => { + return await websitesServ.ssl.upload(data) + }, + onSuccess: () => { + message.success(t('message.saveSuccess', '上传成功')) + get(sslListAtom).refetch() + }, })) \ No newline at end of file diff --git a/src/types/index.d.ts b/src/types/index.d.ts index b7e8cf3..6eef07e 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -10,5 +10,5 @@ export namespace WebSite { export { IAcmeAccount } from './website/acme' export { ICA, ISSLObtainByCA } from './website/ca' export { IDnsAccount } from './website/dns' - export { ISSL, ProviderType, SSLSearchParam } from './website/ssl' + export { ISSL, ProviderType, SSLSearchParam, SSLUploadDto } from './website/ssl' } \ No newline at end of file diff --git a/src/types/website/ssl.d.ts b/src/types/website/ssl.d.ts index 4b576f9..c1a2849 100644 --- a/src/types/website/ssl.d.ts +++ b/src/types/website/ssl.d.ts @@ -36,4 +36,14 @@ export type SSLSearchParam = { prop?: string } +export interface SSLUploadDto { + type: 'paste' | 'local' + private_key: string + private_key_path:string + pem: string + pem_path:string + description: string + +} + diff --git a/vite.config.ts b/vite.config.ts index 61fa264..68530dc 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -7,46 +7,45 @@ import jotaiReactRefresh from 'jotai/babel/plugin-react-refresh' //import { TanStackRouterVite } from '@tanstack/router-vite-plugin' - // https://vitejs.dev/config/ export default defineConfig(({ mode }) => { - // 根据当前工作目录中的 `mode` 加载 .env 文件 - // 设置第三个参数为 '' 来加载所有环境变量,而不管是否有 `VITE_` 前缀。 - // @ts-ignore fix process - const env = loadEnv(mode, process.cwd(), '') - return { - //定义别名的路径 - resolve: { - alias: { - '@': '/src', - }, - }, - server: { - proxy: { - '/api': { - target: env.API_URL, - changeOrigin: true, - rewrite: (path) => path - } - } - }, - plugins: [ - react({ - babel: { - presets: [ 'jotai/babel/preset' ], - plugins: [ jotaiDebugLabel, jotaiReactRefresh ] + // 根据当前工作目录中的 `mode` 加载 .env 文件 + // 设置第三个参数为 '' 来加载所有环境变量,而不管是否有 `VITE_` 前缀。 + // @ts-ignore fix process + const env = loadEnv(mode, process.cwd(), '') + return { + //定义别名的路径 + resolve: { + alias: { + '@': '/src', + }, + }, + server: { + proxy: { + '/api': { + target: env.API_URL, + changeOrigin: true, + rewrite: (path) => path + } + } }, - }), - viteMockServe({ - // 是否启用 mock 功能(默认值:process.env.NODE_ENV !== 'production') - enable: false, + plugins: [ + react({ + babel: { + presets: [ 'jotai/babel/preset' ], + plugins: [ jotaiDebugLabel, jotaiReactRefresh ] + }, + }), + viteMockServe({ + // 是否启用 mock 功能(默认值:process.env.NODE_ENV !== 'production') + enable: false, - // mock 文件的根路径,默认值:'mocks' - mockPath: 'mock', - logger: true, - }), - //TanStackRouterVite(), - ], - } + // mock 文件的根路径,默认值:'mocks' + mockPath: 'mock', + logger: true, + }), + //TanStackRouterVite(), + ], + } }) diff --git a/yarn.lock b/yarn.lock index c46f1cb..ee37fe0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3521,6 +3521,11 @@ react-i18next@^14.1.0: "@babel/runtime" "^7.23.9" html-parse-stringify "^3.0.1" +react-if@^4.1.5: + version "4.1.5" + resolved "https://registry.npmmirror.com/react-if/-/react-if-4.1.5.tgz#f23f49277779e07240c61bdc7ab12671ff3fc20f" + integrity sha512-Uk+Ub2gC83PAakuU4+7iLdTEP4LPi2ihNEPCtz/vr8SLGbzkMApbpYbkDZ5z9zYXurd0gg+EK/bpOLFFC1r1eQ== + react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"