mirror of
				https://github-proxy.zxj.im/abearxiong/app-show-list
				synced 2025-11-04 17:28:33 +08:00 
			
		
		
		
	feat: add uploads app
This commit is contained in:
		
							
								
								
									
										5
									
								
								.npmrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.npmrc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					//npm.xiongxiao.me/:_authToken=${ME_NPM_TOKEN}
 | 
				
			||||||
 | 
					@abearxiong:registry=https://npm.pkg.github.com
 | 
				
			||||||
 | 
					//registry.npmjs.org/:_authToken=${NPM_TOKEN}
 | 
				
			||||||
 | 
					@build:registry=https://npm.xiongxiao.me
 | 
				
			||||||
 | 
					@kevisual:registry=https://npm.xiongxiao.me
 | 
				
			||||||
							
								
								
									
										18
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								README.md
									
									
									
									
									
								
							@@ -1,4 +1,20 @@
 | 
				
			|||||||
# app-template
 | 
					# app-template
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`/system/lib/app.js` 包函的模块是 `QueryRouterServer` 和 `Page` 和 `useConfigKey`
 | 
					`/system/lib/app.js` 包函的模块是 `QueryRouterServer` 和 `Page` 和 `useConfigKey`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## deploy app的过程
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `envision pack -p`
 | 
				
			||||||
 | 
					- `curl` 请求
 | 
				
			||||||
 | 
					```sh
 | 
				
			||||||
 | 
					curl https://envision.xiongxiao.me/api/router?path=micro-app&key=deploy -d '{
 | 
				
			||||||
 | 
					    "data": {
 | 
				
			||||||
 | 
					        "id": "17d105b8-9b6b-4cfc-9447-c815f24fe3d7",
 | 
				
			||||||
 | 
					        "key": "mark"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}'
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					- 启动 `https://kevisual.xiongxiao.me/api/router?path=local-apps&key=updateStatus&appKey=mark&status=start`
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								package.json
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "app-template",
 | 
					  "name": "app-show",
 | 
				
			||||||
  "version": "0.0.1",
 | 
					  "version": "0.0.1",
 | 
				
			||||||
  "description": "",
 | 
					  "description": "",
 | 
				
			||||||
  "main": "index.js",
 | 
					  "main": "index.js",
 | 
				
			||||||
@@ -7,8 +7,8 @@
 | 
				
			|||||||
    "dev": "cross-env WEB_DEV=true vite",
 | 
					    "dev": "cross-env WEB_DEV=true vite",
 | 
				
			||||||
    "build": "vite build",
 | 
					    "build": "vite build",
 | 
				
			||||||
    "preview": "vite preview",
 | 
					    "preview": "vite preview",
 | 
				
			||||||
    "prepub": "envision switchOrg user",
 | 
					    "prepub": "envision switchOrg system",
 | 
				
			||||||
    "pub": "envision deploy ./dist -k app-template -v 0.0.1"
 | 
					    "pub": "envision deploy ./dist -k app-show -v 0.0.1"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "keywords": [],
 | 
					  "keywords": [],
 | 
				
			||||||
  "author": "abearxiong <xiongxiao@xiongxiao.me>",
 | 
					  "author": "abearxiong <xiongxiao@xiongxiao.me>",
 | 
				
			||||||
@@ -19,23 +19,33 @@
 | 
				
			|||||||
    "@emotion/styled": "^11.14.0",
 | 
					    "@emotion/styled": "^11.14.0",
 | 
				
			||||||
    "@kevisual/query": "0.0.7-alpha.3",
 | 
					    "@kevisual/query": "0.0.7-alpha.3",
 | 
				
			||||||
    "@kevisual/system-ui": "^0.0.3",
 | 
					    "@kevisual/system-ui": "^0.0.3",
 | 
				
			||||||
 | 
					    "@mui/icons-material": "^6.3.0",
 | 
				
			||||||
    "@mui/material": "^6.3.0",
 | 
					    "@mui/material": "^6.3.0",
 | 
				
			||||||
 | 
					    "autoprefixer": "^10.4.20",
 | 
				
			||||||
 | 
					    "clsx": "^2.1.1",
 | 
				
			||||||
    "dayjs": "^1.11.13",
 | 
					    "dayjs": "^1.11.13",
 | 
				
			||||||
    "lodash-es": "^4.17.21",
 | 
					    "lodash-es": "^4.17.21",
 | 
				
			||||||
    "react-dom": "^19.0.0",
 | 
					    "react-dom": "^19.0.0",
 | 
				
			||||||
 | 
					    "react-router": "^7.1.1",
 | 
				
			||||||
 | 
					    "react-router-dom": "^7.1.1",
 | 
				
			||||||
 | 
					    "tailwindcss": "^3.4.17",
 | 
				
			||||||
    "zustand": "^5.0.2"
 | 
					    "zustand": "^5.0.2"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
 | 
					    "@build/tailwind": "1.0.2-alpha-2",
 | 
				
			||||||
    "@emotion/css": "^11.13.5",
 | 
					    "@emotion/css": "^11.13.5",
 | 
				
			||||||
    "@kevisual/router": "0.0.6-alpha-4",
 | 
					    "@kevisual/router": "0.0.6-alpha-4",
 | 
				
			||||||
    "@kevisual/store": "0.0.1-alpha.9",
 | 
					    "@kevisual/store": "0.0.1-alpha.9",
 | 
				
			||||||
    "@kevisual/types": "^0.0.5",
 | 
					    "@kevisual/types": "^0.0.5",
 | 
				
			||||||
 | 
					    "@tailwindcss/aspect-ratio": "^0.4.2",
 | 
				
			||||||
 | 
					    "@tailwindcss/typography": "^0.5.15",
 | 
				
			||||||
    "@types/react": "^19.0.2",
 | 
					    "@types/react": "^19.0.2",
 | 
				
			||||||
    "@types/react-dom": "^19.0.2",
 | 
					    "@types/react-dom": "^19.0.2",
 | 
				
			||||||
    "@vitejs/plugin-basic-ssl": "^1.2.0",
 | 
					    "@vitejs/plugin-basic-ssl": "^1.2.0",
 | 
				
			||||||
    "@vitejs/plugin-react": "^4.3.4",
 | 
					    "@vitejs/plugin-react": "^4.3.4",
 | 
				
			||||||
    "cross-env": "^7.0.3",
 | 
					    "cross-env": "^7.0.3",
 | 
				
			||||||
    "react": "^19.0.0",
 | 
					    "react": "^19.0.0",
 | 
				
			||||||
 | 
					    "tailwindcss-animate": "^1.0.7",
 | 
				
			||||||
    "vite": "^6.0.6"
 | 
					    "vite": "^6.0.6"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										870
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										870
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,10 +1,15 @@
 | 
				
			|||||||
import { createRoot } from 'react-dom/client';
 | 
					import { BrowserRouter, Route, Routes } from 'react-router';
 | 
				
			||||||
export const App = () => {
 | 
					import { List } from './pages/List';
 | 
				
			||||||
 | 
					import { LayoutMain } from './layouts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ReactApp = () => {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className='flex justify-center items-center h-screen bg-gray-200'>
 | 
					    <BrowserRouter basename='/system/app-show'>
 | 
				
			||||||
      <h1 className='text-4xl font-bold text-gray-800'>Hello Vite + React!</h1>
 | 
					      <Routes>
 | 
				
			||||||
    </div>
 | 
					        <Route element={<LayoutMain />}>
 | 
				
			||||||
 | 
					          <Route path='/show-home' element={<List />} />
 | 
				
			||||||
 | 
					        </Route>
 | 
				
			||||||
 | 
					      </Routes>
 | 
				
			||||||
 | 
					    </BrowserRouter>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
createRoot(document.getElementById('root')!).render(<App />);
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9
									
								
								src/app.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/app.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					import { useContextKey } from '@kevisual/store/config';
 | 
				
			||||||
 | 
					import { Page } from "@kevisual/store/page";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const page = useContextKey('page', () => {
 | 
				
			||||||
 | 
					  return new Page({
 | 
				
			||||||
 | 
					    basename: '/system/app-show',
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@@ -1,44 +0,0 @@
 | 
				
			|||||||
import { useStore } from '@/store/app';
 | 
					 | 
				
			||||||
import { useEffect } from 'react';
 | 
					 | 
				
			||||||
import { css } from '@emotion/css';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const containerStyle = css`
 | 
					 | 
				
			||||||
  padding: 20px;
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const itemStyle = css`
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  min-width: 240px;
 | 
					 | 
				
			||||||
  flex-direction: column;
 | 
					 | 
				
			||||||
  padding: 20px;
 | 
					 | 
				
			||||||
  margin-bottom: 20px;
 | 
					 | 
				
			||||||
  border: 1px solid #ccc;
 | 
					 | 
				
			||||||
  border-radius: 8px;
 | 
					 | 
				
			||||||
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
 | 
					 | 
				
			||||||
  background-color: #fff;
 | 
					 | 
				
			||||||
`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const List = () => {
 | 
					 | 
				
			||||||
  const store = useStore();
 | 
					 | 
				
			||||||
  useEffect(() => {
 | 
					 | 
				
			||||||
    store.init();
 | 
					 | 
				
			||||||
  }, []);
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <div className={containerStyle}>
 | 
					 | 
				
			||||||
      <div>
 | 
					 | 
				
			||||||
        {store.list.map((item) => {
 | 
					 | 
				
			||||||
          return (
 | 
					 | 
				
			||||||
            <div className={itemStyle} key={item.key}>
 | 
					 | 
				
			||||||
              <span>{item.key}</span>
 | 
					 | 
				
			||||||
              <span>{item.description}</span>
 | 
					 | 
				
			||||||
              <span>{item.status}</span>
 | 
					 | 
				
			||||||
              <span>{item.type}</span>
 | 
					 | 
				
			||||||
              <span>{item.version}</span>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
        })}
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
							
								
								
									
										13
									
								
								src/layouts/LayoutMain.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/layouts/LayoutMain.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					import { Outlet } from 'react-router-dom';
 | 
				
			||||||
 | 
					import { LayoutMenu } from './LayoutMenu';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const LayoutMain = () => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className='bg-slate-200' style={{ display: 'flex', height: '100%' }}>
 | 
				
			||||||
 | 
					      <LayoutMenu />
 | 
				
			||||||
 | 
					      <main style={{ flexGrow: 1, height: '100%', overflow: 'auto' }}>
 | 
				
			||||||
 | 
					        <Outlet />
 | 
				
			||||||
 | 
					      </main>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/layouts/LayoutMenu.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/layouts/LayoutMenu.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { Menu, MenuItem, ListItemIcon, ListItemText } from '@mui/material';
 | 
				
			||||||
 | 
					import InboxIcon from '@mui/icons-material/Inbox';
 | 
				
			||||||
 | 
					import MailIcon from '@mui/icons-material/Mail';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ...existing code...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const menuItems = [
 | 
				
			||||||
 | 
					  { text: 'Inbox', icon: <InboxIcon /> },
 | 
				
			||||||
 | 
					  { text: 'Mail', icon: <MailIcon /> },
 | 
				
			||||||
 | 
					  // 可以根据需要添加更多菜单项
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const LayoutMenu = () => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Menu open={false} anchorOrigin={{ vertical: 'top', horizontal: 'left' }}>
 | 
				
			||||||
 | 
					      {menuItems.map((item, index) => (
 | 
				
			||||||
 | 
					        <MenuItem key={index}>
 | 
				
			||||||
 | 
					          <ListItemIcon>{item.icon}</ListItemIcon>
 | 
				
			||||||
 | 
					          <ListItemText primary={item.text} />
 | 
				
			||||||
 | 
					        </MenuItem>
 | 
				
			||||||
 | 
					      ))}
 | 
				
			||||||
 | 
					    </Menu>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										1
									
								
								src/layouts/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/layouts/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					export * from './LayoutMain'
 | 
				
			||||||
							
								
								
									
										48
									
								
								src/main.tsx
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								src/main.tsx
									
									
									
									
									
								
							@@ -1,29 +1,41 @@
 | 
				
			|||||||
import { useContextKey } from '@kevisual/store/config';
 | 
					import { useContextKey } from '@kevisual/store/config';
 | 
				
			||||||
import { Page } from '@kevisual/store/page';
 | 
					 | 
				
			||||||
import { QueryRouterServer } from '@kevisual/router';
 | 
					import { QueryRouterServer } from '@kevisual/router';
 | 
				
			||||||
import { createRoot } from 'react-dom/client';
 | 
					import { createRoot } from 'react-dom/client';
 | 
				
			||||||
import { List } from './app/List';
 | 
					import { List } from './pages/List';
 | 
				
			||||||
 | 
					import { List as UploadAppList } from './pages/upload-apps/List';
 | 
				
			||||||
 | 
					import { page } from './app';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import '@build/tailwind/main.css';
 | 
				
			||||||
 | 
					// import './tailwind.css';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const initRoot = (renderRoot?: any) => {
 | 
				
			||||||
 | 
					  return useContextKey('root', () => {
 | 
				
			||||||
 | 
					    if (!renderRoot) {
 | 
				
			||||||
 | 
					      console.error('renderRoot is required');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return createRoot(renderRoot);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
export const render = ({ renderRoot }) => {
 | 
					export const render = ({ renderRoot }) => {
 | 
				
			||||||
  // renderRoot.innerHTML = `
 | 
					  const root = initRoot(renderRoot);
 | 
				
			||||||
  //   <h1>Hello, World!</h1>
 | 
					 | 
				
			||||||
  // `;
 | 
					 | 
				
			||||||
  const root = createRoot(renderRoot);
 | 
					 | 
				
			||||||
  // @ts-ignore
 | 
					 | 
				
			||||||
  root.render(<List />);
 | 
					  root.render(<List />);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const page = useContextKey('page', () => {
 | 
					 | 
				
			||||||
  return new Page({
 | 
					 | 
				
			||||||
    basename: '',
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if (page) {
 | 
					if (page) {
 | 
				
			||||||
  page.addPage('/app-template', 'home');
 | 
					  initRoot(document.getElementById('ai-root'));
 | 
				
			||||||
 | 
					  page.addPage('/', 'home');
 | 
				
			||||||
 | 
					  page.addPage('/local-apps', 'local-apps');
 | 
				
			||||||
 | 
					  page.subscribe('local-apps', () => {
 | 
				
			||||||
 | 
					    const root = initRoot();
 | 
				
			||||||
 | 
					    root.render(<List />);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  page.addPage('/upload-apps', 'upload-apps');
 | 
				
			||||||
 | 
					  page.subscribe('upload-apps', () => {
 | 
				
			||||||
 | 
					    const root = initRoot();
 | 
				
			||||||
 | 
					    root.render(<UploadAppList />);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
  page.subscribe('home', () => {
 | 
					  page.subscribe('home', () => {
 | 
				
			||||||
    render({
 | 
					    page.navigate('/local-apps');
 | 
				
			||||||
      renderRoot: document.getElementById('ai-root'),
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -34,7 +46,7 @@ const app = useContextKey('app', () => {
 | 
				
			|||||||
if (app) {
 | 
					if (app) {
 | 
				
			||||||
  app
 | 
					  app
 | 
				
			||||||
    .route({
 | 
					    .route({
 | 
				
			||||||
      path: 'app-template',
 | 
					      path: 'show-home',
 | 
				
			||||||
      key: 'render',
 | 
					      key: 'render',
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    .define(async (ctx) => {
 | 
					    .define(async (ctx) => {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										105
									
								
								src/pages/List.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/pages/List.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
				
			|||||||
 | 
					import { useStore } from '@/store/app';
 | 
				
			||||||
 | 
					import { useEffect } from 'react';
 | 
				
			||||||
 | 
					import { css } from '@emotion/css';
 | 
				
			||||||
 | 
					import clsx from 'clsx';
 | 
				
			||||||
 | 
					import { Page } from '@kevisual/store/page';
 | 
				
			||||||
 | 
					import { page } from '@/app';
 | 
				
			||||||
 | 
					import { useOperateStore } from '@/store/operate';
 | 
				
			||||||
 | 
					import { message } from '@/modules/message';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const containerStyle = css`
 | 
				
			||||||
 | 
					  padding: 20px;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const itemStyle = css`
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  min-width: 240px;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  padding: 20px;
 | 
				
			||||||
 | 
					  margin-bottom: 20px;
 | 
				
			||||||
 | 
					  border: 1px solid #ccc;
 | 
				
			||||||
 | 
					  border-radius: 8px;
 | 
				
			||||||
 | 
					  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
 | 
				
			||||||
 | 
					  background-color: #fff;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const List = () => {
 | 
				
			||||||
 | 
					  const store = useStore();
 | 
				
			||||||
 | 
					  const operateStore = useOperateStore();
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    store.init();
 | 
				
			||||||
 | 
					  }, []);
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className={clsx(containerStyle, 'h-full w-full ')}>
 | 
				
			||||||
 | 
					      <div
 | 
				
			||||||
 | 
					        onClick={() => {
 | 
				
			||||||
 | 
					          page.navigate('/upload-apps');
 | 
				
			||||||
 | 
					        }}>
 | 
				
			||||||
 | 
					        To Upload Apps
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <h1 className='text-2xl font-bold'>Local Apps</h1>
 | 
				
			||||||
 | 
					      <div>
 | 
				
			||||||
 | 
					        <div className='local-apps-headers flex flex-row-reverse'>
 | 
				
			||||||
 | 
					          <button
 | 
				
			||||||
 | 
					            className='px-4 py-2 mt-2 text-white bg-blue-500 rounded hover:bg-blue-700'
 | 
				
			||||||
 | 
					            onClick={async () => {
 | 
				
			||||||
 | 
					              const res = await operateStore.detect();
 | 
				
			||||||
 | 
					              if (res.code === 200) {
 | 
				
			||||||
 | 
					                store.init();
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }}>
 | 
				
			||||||
 | 
					            Detect
 | 
				
			||||||
 | 
					          </button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div className='flex flex-row flex-wrap gap-4'>
 | 
				
			||||||
 | 
					          {store.list.map((item) => {
 | 
				
			||||||
 | 
					            return (
 | 
				
			||||||
 | 
					              <div className={itemStyle} key={item.key}>
 | 
				
			||||||
 | 
					                <span>{item.key}</span>
 | 
				
			||||||
 | 
					                <span>{item.description}</span>
 | 
				
			||||||
 | 
					                <span>{item.status}</span>
 | 
				
			||||||
 | 
					                <span>{item.type}</span>
 | 
				
			||||||
 | 
					                <span>{item.version}</span>
 | 
				
			||||||
 | 
					                <div className='flex flex-row gap-2'>
 | 
				
			||||||
 | 
					                  <button
 | 
				
			||||||
 | 
					                    className='px-4 py-2 mt-2 text-white bg-blue-500 rounded hover:bg-blue-700'
 | 
				
			||||||
 | 
					                    onClick={async () => {
 | 
				
			||||||
 | 
					                      if (item.status !== 'stop') {
 | 
				
			||||||
 | 
					                        const res = await operateStore.updateStatus({ status: 'stop', appKey: item.key });
 | 
				
			||||||
 | 
					                        if (res.code === 200) {
 | 
				
			||||||
 | 
					                          store.init();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                    }}>
 | 
				
			||||||
 | 
					                    Stop
 | 
				
			||||||
 | 
					                  </button>
 | 
				
			||||||
 | 
					                  <button
 | 
				
			||||||
 | 
					                    className='px-4 py-2 mt-2 text-white bg-blue-500 rounded hover:bg-blue-700'
 | 
				
			||||||
 | 
					                    onClick={async () => {
 | 
				
			||||||
 | 
					                      if (item.status !== 'running') {
 | 
				
			||||||
 | 
					                        const res = await operateStore.updateStatus({ status: 'start', appKey: item.key });
 | 
				
			||||||
 | 
					                        if (res.code === 200) {
 | 
				
			||||||
 | 
					                          store.init();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                    }}>
 | 
				
			||||||
 | 
					                    Start
 | 
				
			||||||
 | 
					                  </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  <button
 | 
				
			||||||
 | 
					                    className='px-4 py-2 mt-2 text-white bg-blue-500 rounded hover:bg-blue-700'
 | 
				
			||||||
 | 
					                    onClick={async () => {
 | 
				
			||||||
 | 
					                      const res = await store.deleteData(item.key, { refresh: true });
 | 
				
			||||||
 | 
					                      //
 | 
				
			||||||
 | 
					                    }}>
 | 
				
			||||||
 | 
					                    Delete
 | 
				
			||||||
 | 
					                  </button>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          })}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										54
									
								
								src/pages/upload-apps/List.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/pages/upload-apps/List.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					import { useAppUploadStore } from '@/store/app-upload';
 | 
				
			||||||
 | 
					import { useEffect } from 'react';
 | 
				
			||||||
 | 
					import { KeyEditModal } from './modal/Edit';
 | 
				
			||||||
 | 
					import { page } from '@/app';
 | 
				
			||||||
 | 
					import dayjs from 'dayjs';
 | 
				
			||||||
 | 
					export const List = () => {
 | 
				
			||||||
 | 
					  const store = useAppUploadStore();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    store.init();
 | 
				
			||||||
 | 
					  }, []);
 | 
				
			||||||
 | 
					  const onDeploy = (item: { id: string; [key: string]: any }) => {
 | 
				
			||||||
 | 
					    console.log(item);
 | 
				
			||||||
 | 
					    store.setFormData({
 | 
				
			||||||
 | 
					      id: item.id,
 | 
				
			||||||
 | 
					      key: '',
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    store.setShowModal(true);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className='p-4'>
 | 
				
			||||||
 | 
					      <div
 | 
				
			||||||
 | 
					        onClick={() => {
 | 
				
			||||||
 | 
					          page.navigate('/local-apps');
 | 
				
			||||||
 | 
					        }}>
 | 
				
			||||||
 | 
					        To Local Apps
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <h1 className='text-2xl font-bold'>Upload Apps</h1>
 | 
				
			||||||
 | 
					      <div className='flex flex-wrap gap-4'>
 | 
				
			||||||
 | 
					        {store.list.map((item) => {
 | 
				
			||||||
 | 
					          return (
 | 
				
			||||||
 | 
					            <div className='p-4 mb-4 border rounded shadow bg-white w-[300px] flex flex-col' key={item.id}>
 | 
				
			||||||
 | 
					              <span>{item.title}</span>
 | 
				
			||||||
 | 
					              <span>{item.description}</span>
 | 
				
			||||||
 | 
					              <span>{item.tags.join(', ')}</span>
 | 
				
			||||||
 | 
					              <span>{item.type}</span>
 | 
				
			||||||
 | 
					              <span>{dayjs(item.updatedTime).format('YYYY-MM-DD HH:mm:ss')}</span>
 | 
				
			||||||
 | 
					              <div>
 | 
				
			||||||
 | 
					                <button
 | 
				
			||||||
 | 
					                  className='px-4 py-2 mt-2 text-white bg-blue-500 rounded hover:bg-blue-700'
 | 
				
			||||||
 | 
					                  onClick={() => {
 | 
				
			||||||
 | 
					                    onDeploy(item);
 | 
				
			||||||
 | 
					                  }}>
 | 
				
			||||||
 | 
					                  Deploy
 | 
				
			||||||
 | 
					                </button>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        })}
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <KeyEditModal />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										40
									
								
								src/pages/upload-apps/modal/Edit.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/pages/upload-apps/modal/Edit.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					import { useAppUploadStore } from '@/store/app-upload';
 | 
				
			||||||
 | 
					import { Modal } from '@mui/material';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const KeyEditModal = () => {
 | 
				
			||||||
 | 
					  const store = useAppUploadStore();
 | 
				
			||||||
 | 
					  const onSave = () => {
 | 
				
			||||||
 | 
					    console.log('save');
 | 
				
			||||||
 | 
					    store.setShowModal(false);
 | 
				
			||||||
 | 
					    const fromData = store.formData;
 | 
				
			||||||
 | 
					    store.pubApp(fromData);
 | 
				
			||||||
 | 
					    console.log(fromData);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Modal
 | 
				
			||||||
 | 
					      className='fixed flex items-center justify-center'
 | 
				
			||||||
 | 
					      open={store.showModal}
 | 
				
			||||||
 | 
					      onClose={() => store.setShowModal(false)}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <div className='p-4 bg-white w-[400px]'>
 | 
				
			||||||
 | 
					        <h1 className='text-2xl font-bold'>Edit Key</h1>
 | 
				
			||||||
 | 
					        <div className='mt-4'>
 | 
				
			||||||
 | 
					          <label className='block'>Key</label>
 | 
				
			||||||
 | 
					          <input
 | 
				
			||||||
 | 
					            type='text'
 | 
				
			||||||
 | 
					            className='w-full p-2 border rounded'
 | 
				
			||||||
 | 
					            onChange={(e) => {
 | 
				
			||||||
 | 
					              const value = e.target.value;
 | 
				
			||||||
 | 
					              store.setFormData({ ...store.formData, key: value });
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div className='mt-4'>
 | 
				
			||||||
 | 
					          <button className='px-4 py-2 text-white bg-blue-500 rounded hover:bg-blue-700' onClick={onSave}>
 | 
				
			||||||
 | 
					            Deploy
 | 
				
			||||||
 | 
					          </button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </Modal>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										107
									
								
								src/store/app-upload.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								src/store/app-upload.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
				
			|||||||
 | 
					import { create } from 'zustand';
 | 
				
			||||||
 | 
					import { query } from '@/modules/query';
 | 
				
			||||||
 | 
					import { message } from '@/modules/message';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Store = {
 | 
				
			||||||
 | 
					  list: any[];
 | 
				
			||||||
 | 
					  setList: (list: any[]) => void;
 | 
				
			||||||
 | 
					  data: any;
 | 
				
			||||||
 | 
					  setData: (data: any) => void;
 | 
				
			||||||
 | 
					  loading: boolean;
 | 
				
			||||||
 | 
					  setLoading: (loading: boolean) => void;
 | 
				
			||||||
 | 
					  formData: any;
 | 
				
			||||||
 | 
					  setFormData: (data: any) => void;
 | 
				
			||||||
 | 
					  getList: () => Promise<any>;
 | 
				
			||||||
 | 
					  init: () => Promise<void>;
 | 
				
			||||||
 | 
					  getData: (id: number) => Promise<any>;
 | 
				
			||||||
 | 
					  updateData: (data: any, opts?: { refresh?: boolean }) => Promise<any>;
 | 
				
			||||||
 | 
					  deleteData: (id: number, opts?: { refresh?: boolean }) => Promise<any>;
 | 
				
			||||||
 | 
					  showModal: boolean;
 | 
				
			||||||
 | 
					  setShowModal: (showModal: boolean) => void;
 | 
				
			||||||
 | 
					  pubApp: (data: any) => Promise<any>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export const useAppUploadStore = create<Store>((set, get) => ({
 | 
				
			||||||
 | 
					  list: [],
 | 
				
			||||||
 | 
					  setList: (list) => set({ list }),
 | 
				
			||||||
 | 
					  data: null,
 | 
				
			||||||
 | 
					  setData: (data) => set({ data }),
 | 
				
			||||||
 | 
					  loading: false,
 | 
				
			||||||
 | 
					  setLoading: (loading) => set({ loading }),
 | 
				
			||||||
 | 
					  formData: null,
 | 
				
			||||||
 | 
					  setFormData: (formData) => set({ formData }),
 | 
				
			||||||
 | 
					  getList: async () => {
 | 
				
			||||||
 | 
					    set({ loading: true });
 | 
				
			||||||
 | 
					    const res = await query.post({ path: 'micro-app-upload', key: 'list' });
 | 
				
			||||||
 | 
					    set({ loading: false });
 | 
				
			||||||
 | 
					    if (res.code === 200) {
 | 
				
			||||||
 | 
					      set({ list: res.data });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  init: async () => {
 | 
				
			||||||
 | 
					    await get().getList();
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  getData: async (id) => {
 | 
				
			||||||
 | 
					    set({ loading: true });
 | 
				
			||||||
 | 
					    const res = await query.post({
 | 
				
			||||||
 | 
					      path: 'micro-app-upload',
 | 
				
			||||||
 | 
					      key: 'get',
 | 
				
			||||||
 | 
					      id,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    set({ loading: false });
 | 
				
			||||||
 | 
					    if (res.code === 200) {
 | 
				
			||||||
 | 
					      const data = res.data;
 | 
				
			||||||
 | 
					      set({ data });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  updateData: async (data, opts = { refresh: true }) => {
 | 
				
			||||||
 | 
					    set({ loading: true });
 | 
				
			||||||
 | 
					    const res = await query.post({
 | 
				
			||||||
 | 
					      path: 'micro-app-upload',
 | 
				
			||||||
 | 
					      key: 'update',
 | 
				
			||||||
 | 
					      data,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    set({ loading: false });
 | 
				
			||||||
 | 
					    if (res.code === 200) {
 | 
				
			||||||
 | 
					      set({ data: res.data });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (opts.refresh) {
 | 
				
			||||||
 | 
					      await get().getList();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  deleteData: async (id, opts = { refresh: true }) => {
 | 
				
			||||||
 | 
					    set({ loading: true });
 | 
				
			||||||
 | 
					    const res = await query.post({
 | 
				
			||||||
 | 
					      path: 'micro-app-upload',
 | 
				
			||||||
 | 
					      key: 'delete',
 | 
				
			||||||
 | 
					      id,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    set({ loading: false });
 | 
				
			||||||
 | 
					    if (res.code === 200) {
 | 
				
			||||||
 | 
					      set({ data: null });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (opts.refresh) {
 | 
				
			||||||
 | 
					      await get().getList();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  showModal: false,
 | 
				
			||||||
 | 
					  setShowModal: (showModal) => set({ showModal }),
 | 
				
			||||||
 | 
					  pubApp: async (data) => {
 | 
				
			||||||
 | 
					    set({ loading: true });
 | 
				
			||||||
 | 
					    const res = await query.post({
 | 
				
			||||||
 | 
					      path: 'micro-app',
 | 
				
			||||||
 | 
					      key: 'deploy',
 | 
				
			||||||
 | 
					      data,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    set({ loading: false });
 | 
				
			||||||
 | 
					    if (res.code === 200) {
 | 
				
			||||||
 | 
					      message.success('发布成功');
 | 
				
			||||||
 | 
					      set({ data: null });
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      message.error(res.message || '发布失败');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
@@ -72,7 +72,7 @@ export const useStore = create<Store>((set, get) => ({
 | 
				
			|||||||
    const res = await query.post({
 | 
					    const res = await query.post({
 | 
				
			||||||
      path: 'local-apps',
 | 
					      path: 'local-apps',
 | 
				
			||||||
      key: 'delete',
 | 
					      key: 'delete',
 | 
				
			||||||
      id,
 | 
					      appKey: id,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    set({ loading: false });
 | 
					    set({ loading: false });
 | 
				
			||||||
    if (res.code === 200) {
 | 
					    if (res.code === 200) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,53 +2,66 @@ import { create } from 'zustand';
 | 
				
			|||||||
import { query } from '@/modules/query';
 | 
					import { query } from '@/modules/query';
 | 
				
			||||||
import { message } from '@/modules/message';
 | 
					import { message } from '@/modules/message';
 | 
				
			||||||
interface OperateStore {
 | 
					interface OperateStore {
 | 
				
			||||||
  updateStatus: () => Promise<void>;
 | 
					  updateStatus: (data: { status: 'stop' | 'start'; appKey: string }) => Promise<any>;
 | 
				
			||||||
  detect: () => Promise<void>;
 | 
					  detect: () => Promise<any>;
 | 
				
			||||||
  download: () => Promise<void>;
 | 
					  download: () => Promise<any>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * 微应用的上传和卸载和部署
 | 
					   * 微应用的上传和卸载和部署
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  upload: () => Promise<void>;
 | 
					  upload: () => Promise<any>;
 | 
				
			||||||
  unload: () => Promise<void>;
 | 
					  unload: () => Promise<any>;
 | 
				
			||||||
  deploy: () => Promise<void>;
 | 
					  deploy: () => Promise<any>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const useOperateStore = create<OperateStore>((set) => ({
 | 
					export const useOperateStore = create<OperateStore>((set) => ({
 | 
				
			||||||
  updateStatus: async () => {
 | 
					  updateStatus: async (data) => {
 | 
				
			||||||
    const res = await query.post({
 | 
					    const res = await query.post({
 | 
				
			||||||
      path: 'local-apps',
 | 
					      path: 'local-apps',
 | 
				
			||||||
      key: 'updateStatus',
 | 
					      key: 'updateStatus',
 | 
				
			||||||
 | 
					      status: data.status,
 | 
				
			||||||
 | 
					      appKey: data.appKey,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    if (res.code === 200) {
 | 
				
			||||||
 | 
					      message.success('操作成功');
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      message.error(res.message || '操作失败');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  detect: async () => {
 | 
					  detect: async () => {
 | 
				
			||||||
    const res = await query.post({
 | 
					    const res = await query.post({
 | 
				
			||||||
      path: 'local-apps',
 | 
					      path: 'local-apps',
 | 
				
			||||||
      key: 'detect',
 | 
					      key: 'detect',
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  download: async () => {
 | 
					  download: async () => {
 | 
				
			||||||
    const res = await query.post({
 | 
					    const res = await query.post({
 | 
				
			||||||
      path: 'local-apps',
 | 
					      path: 'local-apps',
 | 
				
			||||||
      key: 'download',
 | 
					      key: 'download',
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  upload: async () => {
 | 
					  upload: async () => {
 | 
				
			||||||
    const res = await query.post({
 | 
					    const res = await query.post({
 | 
				
			||||||
      path: 'micro-apps',
 | 
					      path: 'micro-apps',
 | 
				
			||||||
      key: 'upload',
 | 
					      key: 'upload',
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  unload: async () => {
 | 
					  unload: async () => {
 | 
				
			||||||
    const res = await query.post({
 | 
					    const res = await query.post({
 | 
				
			||||||
      path: 'micro-apps',
 | 
					      path: 'micro-apps',
 | 
				
			||||||
      key: 'unload',
 | 
					      key: 'unload',
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  deploy: async () => {
 | 
					  deploy: async () => {
 | 
				
			||||||
    const res = await query.post({
 | 
					    const res = await query.post({
 | 
				
			||||||
      path: 'micro-apps',
 | 
					      path: 'micro-apps',
 | 
				
			||||||
      key: 'deploy',
 | 
					      key: 'deploy',
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								src/tailwind.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/tailwind.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					@tailwind base;
 | 
				
			||||||
 | 
					@tailwind components;
 | 
				
			||||||
 | 
					@tailwind utilities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										47
									
								
								tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					import path from 'path';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const root = path.resolve(process.cwd());
 | 
				
			||||||
 | 
					const contents = ['./src/**/*.{ts,tsx,html}', './src/**/*.css']
 | 
				
			||||||
 | 
					const content = contents.map((item) => path.join(root, item));
 | 
				
			||||||
 | 
					/** @type {import('tailwindcss').Config} */
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  // darkMode: ['class'],
 | 
				
			||||||
 | 
					  content: content,
 | 
				
			||||||
 | 
					  plugins: [
 | 
				
			||||||
 | 
					    require('@tailwindcss/aspect-ratio'), //
 | 
				
			||||||
 | 
					    require('@tailwindcss/typography'),
 | 
				
			||||||
 | 
					    require('tailwindcss-animate'),
 | 
				
			||||||
 | 
					    require('@build/tailwind'),
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  theme: {
 | 
				
			||||||
 | 
					    extend: {
 | 
				
			||||||
 | 
					      fontFamily: {
 | 
				
			||||||
 | 
					        mon: ['Montserrat', 'sans-serif'], // 定义自定义字体族
 | 
				
			||||||
 | 
					        rob: ['Roboto', 'sans-serif'],
 | 
				
			||||||
 | 
					        int: ['Inter', 'sans-serif'],
 | 
				
			||||||
 | 
					        orb: ['Orbitron', 'sans-serif'],
 | 
				
			||||||
 | 
					        din: ['DIN', 'sans-serif'],
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    screen: {
 | 
				
			||||||
 | 
					      sm: '640px',
 | 
				
			||||||
 | 
					      // => @media (min-width: 640px) { ... }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      md: '768px',
 | 
				
			||||||
 | 
					      // => @media (min-width: 768px) { ... }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      lg: '1024px',
 | 
				
			||||||
 | 
					      // => @media (min-width: 1024px) { ... }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      xl: '1280px',
 | 
				
			||||||
 | 
					      // => @media (min-width: 1280px) { ... }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      '2xl': '1536px',
 | 
				
			||||||
 | 
					      // => @media (min-width: 1536px) { ... }
 | 
				
			||||||
 | 
					      '3xl': '1920px',
 | 
				
			||||||
 | 
					      // => @media (min-width: 1920) { ... }
 | 
				
			||||||
 | 
					      '4xl': '2560px',
 | 
				
			||||||
 | 
					      // => @media (min-width: 2560) { ... }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -3,6 +3,9 @@ import react from '@vitejs/plugin-react'
 | 
				
			|||||||
// import basicSsl from '@vitejs/plugin-basic-ssl';
 | 
					// import basicSsl from '@vitejs/plugin-basic-ssl';
 | 
				
			||||||
import dayjs from 'dayjs';
 | 
					import dayjs from 'dayjs';
 | 
				
			||||||
import path from 'path';
 | 
					import path from 'path';
 | 
				
			||||||
 | 
					import tailwindcss from 'tailwindcss';
 | 
				
			||||||
 | 
					import autoprefixer from 'autoprefixer';
 | 
				
			||||||
 | 
					import nesting from 'tailwindcss/nesting';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isDev = process.env.NODE_ENV === 'development';
 | 
					const isDev = process.env.NODE_ENV === 'development';
 | 
				
			||||||
const BUILD_TIME = dayjs().format('YYYY-MM-DD HH:mm:ss');
 | 
					const BUILD_TIME = dayjs().format('YYYY-MM-DD HH:mm:ss');
 | 
				
			||||||
@@ -10,6 +13,13 @@ console.log('process', isDev, process.env.WEB_DEV)
 | 
				
			|||||||
export default defineConfig({
 | 
					export default defineConfig({
 | 
				
			||||||
  // plugins: [basicSsl()],
 | 
					  // plugins: [basicSsl()],
 | 
				
			||||||
  plugins: [react()],
 | 
					  plugins: [react()],
 | 
				
			||||||
 | 
					  css: {
 | 
				
			||||||
 | 
					    postcss: {
 | 
				
			||||||
 | 
					      // @ts-ignore
 | 
				
			||||||
 | 
					      plugins: [nesting, tailwindcss, autoprefixer],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  base: isDev ? '/' : './',
 | 
				
			||||||
  resolve: {
 | 
					  resolve: {
 | 
				
			||||||
    alias: {
 | 
					    alias: {
 | 
				
			||||||
      '@': path.resolve(__dirname, './src'),
 | 
					      '@': path.resolve(__dirname, './src'),
 | 
				
			||||||
@@ -32,13 +42,13 @@ export default defineConfig({
 | 
				
			|||||||
    proxy: {
 | 
					    proxy: {
 | 
				
			||||||
      '/api': {
 | 
					      '/api': {
 | 
				
			||||||
        target: 'https://kevisual.xiongxiao.me',
 | 
					        target: 'https://kevisual.xiongxiao.me',
 | 
				
			||||||
 | 
					        // target: 'http://localhost:9787',
 | 
				
			||||||
        changeOrigin: true,
 | 
					        changeOrigin: true,
 | 
				
			||||||
        rewrite: (path) => path.replace(/^\/api/, '/api'),
 | 
					        rewrite: (path) => path.replace(/^\/api/, '/api'),
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      '/system': {
 | 
					      '/system/lib': {
 | 
				
			||||||
        target: 'https://kevisual.xiongxiao.me',
 | 
					        target: 'https://kevisual.xiongxiao.me',
 | 
				
			||||||
        changeOrigin: true,
 | 
					        changeOrigin: true,
 | 
				
			||||||
        rewrite: (path) => path.replace(/^\/system/, '/system'),
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user