mirror of
https://github.com/labring/sealos.git
synced 2026-09-19 02:23:07 +08:00
feat(desktop): image hub list paging & pagination component (#2437)
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
"github-markdown-css": "^5.1.0",
|
||||
"immer": "^9.0.16",
|
||||
"js-yaml": "^4.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"next": "13.1.0",
|
||||
"next-pwa": "^5.6.0",
|
||||
"qrcode.react": "^3.1.0",
|
||||
@@ -48,6 +49,7 @@
|
||||
"devDependencies": {
|
||||
"@svgr/webpack": "^6.5.1",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/node": "18.11.17",
|
||||
"@types/react": "18.0.26",
|
||||
"@types/react-dom": "18.0.9",
|
||||
|
||||
Generated
+17
-5
@@ -13,6 +13,7 @@ specifiers:
|
||||
'@svgr/webpack': ^6.5.1
|
||||
'@tanstack/react-query': ^4.20.4
|
||||
'@types/js-yaml': ^4.0.5
|
||||
'@types/lodash': ^4.14.191
|
||||
'@types/node': 18.11.17
|
||||
'@types/react': 18.0.26
|
||||
'@types/react-dom': 18.0.9
|
||||
@@ -26,6 +27,7 @@ specifiers:
|
||||
github-markdown-css: ^5.1.0
|
||||
immer: ^9.0.16
|
||||
js-yaml: ^4.1.0
|
||||
lodash: ^4.17.21
|
||||
next: 13.1.0
|
||||
next-pwa: ^5.6.0
|
||||
postcss: ^8.4.20
|
||||
@@ -62,6 +64,7 @@ dependencies:
|
||||
github-markdown-css: registry.npmmirror.com/github-markdown-css/5.1.0
|
||||
immer: 9.0.16
|
||||
js-yaml: 4.1.0
|
||||
lodash: registry.npmmirror.com/lodash/4.17.21
|
||||
next: 13.1.0_3duxcpitbtplz62feflag7fwby
|
||||
next-pwa: 5.6.0_next@13.1.0
|
||||
qrcode.react: 3.1.0_react@18.2.0
|
||||
@@ -79,6 +82,7 @@ dependencies:
|
||||
devDependencies:
|
||||
'@svgr/webpack': 6.5.1
|
||||
'@types/js-yaml': 4.0.5
|
||||
'@types/lodash': registry.npmmirror.com/@types/lodash/4.14.191
|
||||
'@types/node': 18.11.17
|
||||
'@types/react': 18.0.26
|
||||
'@types/react-dom': 18.0.9
|
||||
@@ -5963,10 +5967,6 @@ packages:
|
||||
resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==}
|
||||
dev: false
|
||||
|
||||
/lodash/4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
dev: false
|
||||
|
||||
/loose-envify/1.4.0:
|
||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||
hasBin: true
|
||||
@@ -7884,7 +7884,7 @@ packages:
|
||||
fast-json-stable-stringify: 2.1.0
|
||||
fs-extra: 9.1.0
|
||||
glob: 7.2.3
|
||||
lodash: 4.17.21
|
||||
lodash: registry.npmmirror.com/lodash/4.17.21
|
||||
pretty-bytes: 5.6.0
|
||||
rollup: 2.79.1
|
||||
rollup-plugin-terser: 7.0.2_rollup@2.79.1
|
||||
@@ -8219,6 +8219,12 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
registry.npmmirror.com/@types/lodash/4.14.191:
|
||||
resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.191.tgz}
|
||||
name: '@types/lodash'
|
||||
version: 4.14.191
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/fsevents/2.3.2:
|
||||
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz}
|
||||
name: fsevents
|
||||
@@ -8242,6 +8248,12 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
registry.npmmirror.com/lodash/4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz}
|
||||
name: lodash
|
||||
version: 4.17.21
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/openid-client/5.3.1:
|
||||
resolution: {integrity: sha512-RLfehQiHch9N6tRWNx68cicf3b1WR0x74bJWHRc25uYIbSRwjxYcTFaRnzbbpls5jroLAaB/bFIodTgA5LJMvw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/openid-client/-/openid-client-5.3.1.tgz}
|
||||
name: openid-client
|
||||
|
||||
@@ -27,6 +27,17 @@ $background-max-hover: rgba(255, 255, 255, 0.65);
|
||||
}
|
||||
}
|
||||
|
||||
.customScrollStyle {
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #7291be;
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.backgroundWrap {
|
||||
background-image: url('/images/appstore/background.png');
|
||||
background-size: cover;
|
||||
|
||||
@@ -78,17 +78,3 @@ export function getSelectLabels(labels: TImageLabels[]): string {
|
||||
.filter((item) => item)
|
||||
.join(',');
|
||||
}
|
||||
|
||||
export function sortByName(arr: TAppInfo[]) {
|
||||
const map = new Map();
|
||||
const result = [];
|
||||
for (const item of arr) {
|
||||
map.set(item.name, item);
|
||||
}
|
||||
let mapKeys = [...map.keys()];
|
||||
mapKeys.sort();
|
||||
for (const name of mapKeys) {
|
||||
result.push(map.get(name));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
.pageContainer {
|
||||
display: flex;
|
||||
font-size: 12px;
|
||||
align-items: center;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.pageWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
li.pageItemActive {
|
||||
color: #239bf2;
|
||||
border-color: #239bf2;
|
||||
}
|
||||
}
|
||||
|
||||
.pageWrapper li {
|
||||
cursor: pointer;
|
||||
border: 1px transparent solid;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 4px;
|
||||
margin-left: 4px;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
border-radius: 8px;
|
||||
|
||||
color: #0d1a2d;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.pageWrapper li:hover {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.pageWrapper li.disabled {
|
||||
color: #0d1a2d;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
import clsx from 'clsx';
|
||||
import { useState } from 'react';
|
||||
import styles from './pgination.module.scss';
|
||||
import Iconfont from 'components/iconfont';
|
||||
|
||||
export type TPagination = {
|
||||
defaultCurrent: number; // current page count
|
||||
pageSize: number; // quantity per page
|
||||
total: number; // total
|
||||
showTotal?: boolean;
|
||||
onChange: (page: number, pageSize: number) => void; // Callback that change the page number or pageSize
|
||||
special?: boolean; //can only take one step back
|
||||
};
|
||||
|
||||
function Pagination(props: TPagination) {
|
||||
const { defaultCurrent = 1, pageSize, total, showTotal, onChange, special } = props;
|
||||
const [currentPage, setCurrentPage] = useState(defaultCurrent);
|
||||
const totalPageSize = Math.ceil(total / pageSize) || 1;
|
||||
|
||||
let centerPages: number[] = [];
|
||||
const centerSize = 5;
|
||||
const jumpSize = centerSize;
|
||||
const startEllipsisSize = centerSize + 3;
|
||||
|
||||
const updateCenterPage = () => {
|
||||
let centerPage = currentPage;
|
||||
if (currentPage > totalPageSize - 3) {
|
||||
centerPage = totalPageSize - 3;
|
||||
}
|
||||
if (currentPage < 4) {
|
||||
centerPage = 4;
|
||||
}
|
||||
if (totalPageSize <= centerSize + 2) {
|
||||
let temp = [];
|
||||
for (let i = 2, len = totalPageSize; i < len; i++) {
|
||||
temp.push(i);
|
||||
}
|
||||
centerPages = temp;
|
||||
} else {
|
||||
centerPages = [centerPage - 2, centerPage - 1, centerPage, centerPage + 1, centerPage + 2];
|
||||
}
|
||||
};
|
||||
|
||||
updateCenterPage();
|
||||
|
||||
const pageChange = (cur: number) => {
|
||||
if (currentPage === cur) return;
|
||||
setCurrentPage(cur);
|
||||
onChange(cur, pageSize);
|
||||
};
|
||||
|
||||
// next page
|
||||
const nextPageChange = () => {
|
||||
if (currentPage === totalPageSize) return;
|
||||
pageChange(currentPage + 1);
|
||||
};
|
||||
|
||||
// previous page
|
||||
const prePageChange = () => {
|
||||
if (currentPage === 1) return;
|
||||
pageChange(currentPage - 1);
|
||||
};
|
||||
|
||||
// First 5 pages
|
||||
const preOmitPageChange = () => {
|
||||
let newPage = currentPage - jumpSize;
|
||||
if (newPage < 1) {
|
||||
newPage = 1;
|
||||
}
|
||||
pageChange(newPage);
|
||||
};
|
||||
|
||||
// Back 5 pages
|
||||
const nextOmitPageChange = () => {
|
||||
let newPage = currentPage + jumpSize;
|
||||
if (newPage > totalPageSize) {
|
||||
newPage = totalPageSize;
|
||||
}
|
||||
pageChange(newPage);
|
||||
};
|
||||
|
||||
const firstPage = (
|
||||
<li
|
||||
key={'first'}
|
||||
className={clsx({ [styles.pageItemActive]: currentPage === 1 })}
|
||||
onClick={() => {
|
||||
pageChange(1);
|
||||
}}
|
||||
>
|
||||
1
|
||||
</li>
|
||||
);
|
||||
|
||||
const lastPage = (
|
||||
<li
|
||||
key={'last'}
|
||||
onClick={() => pageChange(totalPageSize)}
|
||||
className={clsx({ [styles.pageItemActive]: currentPage === totalPageSize })}
|
||||
>
|
||||
{totalPageSize}
|
||||
</li>
|
||||
);
|
||||
|
||||
const pageItem = (currentValue: number) => {
|
||||
return (
|
||||
<li
|
||||
onClick={() => pageChange(currentValue)}
|
||||
className={clsx({ [styles.pageItemActive]: currentPage === currentValue })}
|
||||
key={'item' + currentValue}
|
||||
>
|
||||
{currentValue}
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
const PageOmit = (props: any) => {
|
||||
return (
|
||||
<li title={props.title} onClick={props.onClick}>
|
||||
...
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
const initPage = () => {
|
||||
return (
|
||||
<>
|
||||
{firstPage}
|
||||
|
||||
{currentPage >= centerSize && totalPageSize >= startEllipsisSize && (
|
||||
<PageOmit key={'omit-pre'} title="向前5页" onClick={preOmitPageChange} />
|
||||
)}
|
||||
|
||||
{centerPages.map((page) => pageItem(page))}
|
||||
|
||||
{currentPage <= totalPageSize - centerSize + 1 && totalPageSize >= startEllipsisSize && (
|
||||
<PageOmit key={'omit-next'} title="向后5页" onClick={nextOmitPageChange} />
|
||||
)}
|
||||
|
||||
{lastPage}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const initSecialPage = () => {
|
||||
const smallPage = Array.from({ length: currentPage }).map((_, i) => pageItem(i + 1));
|
||||
const largePage = (
|
||||
<>
|
||||
{firstPage}
|
||||
<PageOmit key={'omit-pre'} title="向前5页" onClick={preOmitPageChange} />
|
||||
{Array.from({ length: 3 }).map((_, i) => pageItem(i + currentPage - 2))}
|
||||
</>
|
||||
);
|
||||
return <>{currentPage < 6 ? smallPage : largePage}</>;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.pageContainer}>
|
||||
<ul className={styles.pageWrapper}>
|
||||
<li
|
||||
key={'<'}
|
||||
onClick={prePageChange}
|
||||
className={clsx({ [styles.disabled]: currentPage === 1 })}
|
||||
>
|
||||
<Iconfont iconName="icon-left" color="#0D1A2D" width={28} height={28} />
|
||||
</li>
|
||||
{special ? initSecialPage() : initPage()}
|
||||
<li
|
||||
key={'>'}
|
||||
onClick={nextPageChange}
|
||||
className={clsx({ [styles.disabled]: currentPage === totalPageSize })}
|
||||
>
|
||||
<Iconfont iconName="icon-right" color="#0D1A2D" width={28} height={28} />
|
||||
</li>
|
||||
</ul>
|
||||
{showTotal && (
|
||||
<span style={{ color: `#333`, marginRight: `.5em`, marginLeft: `1em` }}>共{total}条</span>
|
||||
)}
|
||||
{special && '共' + totalPageSize + '页'}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Pagination;
|
||||
@@ -6,11 +6,12 @@ import MarkDown from 'components/markdown';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import request from 'services/request';
|
||||
import useSessionStore from 'stores/session';
|
||||
import { EPageType, formattedSize, handleImageName, TAppDetail, TTag } from './app_store_common';
|
||||
import { EPageType, formattedSize, handleImageName, TAppDetail } from './app_store_common';
|
||||
import Button from './components/button';
|
||||
import styles from './detail.module.scss';
|
||||
import { useAppStoreContext } from './index';
|
||||
import { CardLoading, ImageMarkdownLoading, ImageTagsLoading } from './components/imagehub_loading';
|
||||
import { throttle } from 'lodash';
|
||||
|
||||
export default function DetailPage() {
|
||||
const { toPage, detailAppName } = useAppStoreContext();
|
||||
@@ -23,6 +24,15 @@ export default function DetailPage() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const handleScrollEvent = () => {
|
||||
const scrollTop = appRef.current?.scrollTop;
|
||||
setFixed(Boolean(scrollTop && scrollTop > 20));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
appRef.current?.addEventListener('scroll', throttle(handleScrollEvent));
|
||||
}, []);
|
||||
|
||||
const { data, isLoading, isSuccess } = useQuery(
|
||||
['getAppDetail', fullImageName],
|
||||
async () => {
|
||||
@@ -59,18 +69,6 @@ export default function DetailPage() {
|
||||
navigator.clipboard.writeText(value);
|
||||
};
|
||||
|
||||
const handleScrollEvent = () => {
|
||||
const scrollTop = appRef.current?.scrollTop;
|
||||
setFixed(Boolean(scrollTop && scrollTop > 20));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
appRef.current?.addEventListener('scroll', handleScrollEvent);
|
||||
return () => {
|
||||
appRef.current?.removeEventListener('scroll', handleScrollEvent);
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className={clsx(styles.backgroundWrap, 'flex flex-col grow h-full p-8 pb-0')}>
|
||||
@@ -84,7 +82,7 @@ export default function DetailPage() {
|
||||
<CardLoading width={400} height={110} />
|
||||
</div>
|
||||
<div className={clsx('flex grow my-4')}>
|
||||
<div className={clsx(styles.baseCard, styles.mainLeft, 'py-8 px-6')}>
|
||||
<div ref={appRef} className={clsx(styles.baseCard, styles.mainLeft, 'py-8 px-6')}>
|
||||
<div className={clsx('flex items-center mb-4')}>
|
||||
<div className={styles.iconBtn}>
|
||||
<Iconfont iconName="icon-overview" />
|
||||
@@ -128,7 +126,7 @@ export default function DetailPage() {
|
||||
<div>{fullImageName}</div>
|
||||
<div className={styles.fingerPrint}>
|
||||
<Iconfont iconName="icon-hash" color="#239BF2" />
|
||||
<span className={styles.imageId}>{appDetail.ID?.substring(0, 12)}</span>
|
||||
<span className={styles.imageId}>{appDetail?.ID?.substring(0, 12)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className={styles.text}>{appDetail?.description}</p>
|
||||
@@ -172,9 +170,14 @@ export default function DetailPage() {
|
||||
<div className={clsx('flex grow my-4')}>
|
||||
<div
|
||||
ref={appRef}
|
||||
className={clsx(styles.baseCard, styles.mainLeft, styles.hiddenScrollWrap)}
|
||||
className={clsx(
|
||||
styles.baseCard,
|
||||
styles.mainLeft,
|
||||
styles.pageWrapperScroll,
|
||||
styles.hiddenScrollWrap
|
||||
)}
|
||||
>
|
||||
<div className="absolute w-full py-8 px-6 ">
|
||||
<div className="absolute w-full py-8 px-6">
|
||||
<div className={clsx('flex items-center mb-4')}>
|
||||
<div className={styles.iconBtn}>
|
||||
<Iconfont iconName="icon-overview" />
|
||||
@@ -211,7 +214,7 @@ export default function DetailPage() {
|
||||
className={clsx(styles.tag, { [styles.activeTag]: selectTag === item.name })}
|
||||
onClick={() => setSelectTag(item.name)}
|
||||
>
|
||||
<div className={clsx('w-12')}>
|
||||
<div className={clsx('w-8')}>
|
||||
{selectTag === item.name && (
|
||||
<Iconfont iconName="icon-checked" color="#239BF2" />
|
||||
)}
|
||||
|
||||
@@ -29,7 +29,7 @@ export default function AppStore() {
|
||||
|
||||
return (
|
||||
<ApppStoreContext.Provider value={{ toPage, detailAppName }}>
|
||||
<div className={clsx(styles.backgroundWrap, 'flex grow flex-col h-full p-6 pb-0')}>
|
||||
<div className={clsx(styles.backgroundWrap, 'flex grow flex-col h-full pt-6 pl-6')}>
|
||||
{pageId === EPageType.StorePage && <StorePage />}
|
||||
{pageId === EPageType.OrganizationPage && <OrganizationPage />}
|
||||
{pageId === EPageType.MinePage && <MinePage />}
|
||||
|
||||
@@ -14,8 +14,7 @@ import {
|
||||
ImagehubLabels,
|
||||
TImageLabels,
|
||||
getSelectLabels,
|
||||
TAppInfo,
|
||||
sortByName
|
||||
TAppInfo
|
||||
} from './app_store_common';
|
||||
import Button from './components/button';
|
||||
import Labels from './components/labels';
|
||||
@@ -23,6 +22,8 @@ import Error from './components/error';
|
||||
import { useAppStoreContext } from './index';
|
||||
import styles from './store_page.module.scss';
|
||||
import { ListContextLoading } from './components/imagehub_loading';
|
||||
import Pagination, { TPagination } from './components/pgination';
|
||||
import { sortBy } from 'lodash';
|
||||
|
||||
function StorePage() {
|
||||
const { toPage } = useAppStoreContext();
|
||||
@@ -30,13 +31,33 @@ function StorePage() {
|
||||
const { kubeconfig } = useSessionStore((state) => state.getSession());
|
||||
const selectedLabels = getSelectLabels(imageLabels);
|
||||
const queryClient = useQueryClient();
|
||||
const [test, setTest] = useState(1);
|
||||
const [pagination, setPagination] = useState({
|
||||
current: 1,
|
||||
total: 0,
|
||||
pageSize: 10,
|
||||
continueKey: ['']
|
||||
});
|
||||
|
||||
const onChangePagination = (page: number, pageSize: number) => {
|
||||
setPagination(
|
||||
produce((draft) => {
|
||||
draft.current = page;
|
||||
if (page === 1) {
|
||||
draft.continueKey = [''];
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const { data, isLoading, isSuccess, isError } = useQuery(
|
||||
['getAppLists', selectedLabels],
|
||||
['getAppLists', selectedLabels, pagination.current],
|
||||
async () => {
|
||||
const res = await request.post('/api/image_hub/get_list', {
|
||||
kubeconfig,
|
||||
labels: selectedLabels
|
||||
labels: selectedLabels,
|
||||
limit: pagination.pageSize,
|
||||
_continue: pagination.continueKey[pagination.current - 1]
|
||||
});
|
||||
return res;
|
||||
},
|
||||
@@ -45,12 +66,24 @@ function StorePage() {
|
||||
if (data.status === 201) {
|
||||
queryClient.invalidateQueries({ queryKey: ['getAppLists', selectedLabels] });
|
||||
}
|
||||
if (data.status === 200) {
|
||||
setPagination(
|
||||
produce((draft) => {
|
||||
draft.total =
|
||||
(data.data.metadata.remainingItemCount || 0) + draft.pageSize * draft.current;
|
||||
if (!draft.continueKey.includes(data.data.metadata.continue)) {
|
||||
draft.continueKey.push(data.data.metadata.continue);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let appLists: TAppInfo[] = [];
|
||||
if (data?.status === 200) {
|
||||
appLists = sortByName(data.data);
|
||||
appLists = sortBy(data.data.data, (o) => o.name);
|
||||
}
|
||||
|
||||
const handleLabelsChange = (value: string) => {
|
||||
@@ -91,7 +124,7 @@ function StorePage() {
|
||||
<Error />
|
||||
</div>
|
||||
)}
|
||||
<div className={clsx(styles.pageWrapperScroll, styles.hiddenScrollWrap)}>
|
||||
<div className={clsx(styles.pageWrapperScroll, styles.customScrollStyle)}>
|
||||
{isLoading && <ListContextLoading />}
|
||||
{isSuccess && (
|
||||
<div className="absolute w-full space-y-4 pb-10">
|
||||
@@ -154,6 +187,17 @@ function StorePage() {
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<div className="float-right mr-8">
|
||||
<Pagination
|
||||
special
|
||||
defaultCurrent={pagination.current}
|
||||
total={pagination.total}
|
||||
pageSize={pagination.pageSize}
|
||||
onChange={(page, pageSize) => {
|
||||
onChangePagination(page, pageSize);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,7 @@ const AddPage = () => {
|
||||
const { infraName, toPage } = useScpContext();
|
||||
const { kubeconfig } = useSessionStore((state) => state.getSession());
|
||||
const [image1, setImage1] = useState('labring/kubernetes:v1.25.5');
|
||||
const [image2, setImage2] = useState('labring/calico:v3.22.1');
|
||||
const [image2, setImage2] = useState('labring/calico:v3.24.1');
|
||||
const [yamlTemplate, setYamlTemplate] = useState('');
|
||||
const [scpPrice, setScpPrice] = useState(0);
|
||||
const [inputNameErr, setInputNameErr] = useState(false);
|
||||
|
||||
@@ -109,7 +109,7 @@ function DetailPage() {
|
||||
</span>
|
||||
<span className="mt-4">
|
||||
<SvgIcon src="/images/infraicon/scp_image.svg" />
|
||||
kuberentes:v1.24.0
|
||||
{clusterInfo?.data?.spec?.image[0]}
|
||||
</span>
|
||||
<span className="mt-4">
|
||||
<SvgIcon src="/images/infraicon/scp_user.svg" />
|
||||
@@ -120,7 +120,7 @@ function DetailPage() {
|
||||
ref={sshRef}
|
||||
onClick={() => {
|
||||
positioningRef.current?.setTarget(sshRef.current as any);
|
||||
copyContext(scpInfo?.data.spec.ssh?.pkData);
|
||||
copyContext(scpInfo?.data?.spec?.ssh?.pkData);
|
||||
}}
|
||||
>
|
||||
<SvgIcon src="/images/infraicon/scp_ssh.svg" />
|
||||
|
||||
@@ -44,7 +44,7 @@ spec:
|
||||
- roles: [master]
|
||||
count: ${infraForm.masterCount}
|
||||
flavor: ${infraForm.masterType}
|
||||
image: "ami-073b7de61877e5b7b"
|
||||
image: "ami-09123565dfe16d662"
|
||||
disks:
|
||||
- capacity: ${infraForm.masterDisk}
|
||||
volumeType: ${infraForm.masterDiskType}
|
||||
@@ -54,7 +54,7 @@ spec:
|
||||
- roles: [ node ]
|
||||
count: ${infraForm.nodeCount}
|
||||
flavor: ${infraForm.nodeType}
|
||||
image: "ami-073b7de61877e5b7b"
|
||||
image: "ami-09123565dfe16d662"
|
||||
disks:
|
||||
- capacity: ${infraForm.nodeDisk}
|
||||
volumeType: ${infraForm.nodeDiskType}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
type TIconfont = {
|
||||
iconName: string;
|
||||
color?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
|
||||
function Iconfont(props: TIconfont) {
|
||||
const { iconName, color } = props;
|
||||
const { iconName, color, width, height } = props;
|
||||
const style = {
|
||||
fill: color
|
||||
fill: color,
|
||||
width,
|
||||
height
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -17,7 +17,7 @@ spec:
|
||||
- roles: [ master ]
|
||||
count: {{ .masterCount }}
|
||||
flavor: {{ .masterType }}
|
||||
image: "ami-073b7de61877e5b7b"
|
||||
image: "ami-09123565dfe16d662"
|
||||
disks:
|
||||
- capacity: {{.masterDisk}}
|
||||
volumeType: {{.masterDiskType}}
|
||||
@@ -25,7 +25,7 @@ spec:
|
||||
- roles: [ node ]
|
||||
count: {{ .nodeCount }}
|
||||
flavor: {{ .nodeType }}
|
||||
image: "ami-073b7de61877e5b7b"
|
||||
image: "ami-09123565dfe16d662"
|
||||
disks:
|
||||
- capacity: {{.nodeDisk}}
|
||||
volumeType: {{.nodeDiskType}}
|
||||
|
||||
@@ -8,7 +8,7 @@ const installedApps: TApp[] = [
|
||||
icon: '/images/sealos.svg',
|
||||
type: APPTYPE.IFRAME,
|
||||
data: {
|
||||
url: 'https://www.sealos.io/docs/Intro',
|
||||
url: 'https://www.sealos.io/docs/cloud/Intro',
|
||||
desc: 'Sealos Documents'
|
||||
},
|
||||
gallery: [
|
||||
|
||||
@@ -34,6 +34,11 @@ type repositoryStatus = {
|
||||
};
|
||||
};
|
||||
}[];
|
||||
metadata: {
|
||||
continue: string;
|
||||
remainingItemCount: number;
|
||||
resourceVersion: string;
|
||||
};
|
||||
};
|
||||
|
||||
type DataPackDesc = {
|
||||
@@ -49,8 +54,7 @@ enum DataPackStatus {
|
||||
}
|
||||
|
||||
export default async function handler(req: NextApiRequest, resp: NextApiResponse) {
|
||||
const { kubeconfig, labels } = req.body;
|
||||
|
||||
const { kubeconfig, labels, _continue, limit } = req.body;
|
||||
if (kubeconfig === '') {
|
||||
return UnprocessableResp('kubeconfig or user empty', resp);
|
||||
}
|
||||
@@ -64,10 +68,12 @@ export default async function handler(req: NextApiRequest, resp: NextApiResponse
|
||||
|
||||
let images_names: Array<string> = [];
|
||||
|
||||
let repositories_metadata = {};
|
||||
try {
|
||||
const repositoryDesc = await ListClusterObject(kc, RepositoryMeta, labels);
|
||||
const repositoryDesc = await ListClusterObject(kc, RepositoryMeta, labels, limit, _continue);
|
||||
if (repositoryDesc?.body) {
|
||||
const result = repositoryDesc.body as repositoryStatus;
|
||||
repositories_metadata = result.metadata;
|
||||
for (const item of result.items) {
|
||||
images_names.push(item.spec.name + ':' + item.status.latestTag.name);
|
||||
}
|
||||
@@ -87,7 +93,7 @@ export default async function handler(req: NextApiRequest, resp: NextApiResponse
|
||||
const datapackDesc = dataDesc.body.status as DataPackDesc;
|
||||
if (datapackDesc.codes === DataPackStatus.Ok) {
|
||||
let result = Object.values(datapackDesc.datas);
|
||||
return JsonResp(result, resp);
|
||||
return JsonResp({ data: result, metadata: repositories_metadata }, resp);
|
||||
}
|
||||
return JsonResp(datapackDesc, resp);
|
||||
}
|
||||
|
||||
@@ -225,7 +225,9 @@ export async function ListSecret(
|
||||
export async function ListClusterObject(
|
||||
kc: k8s.KubeConfig,
|
||||
meta: CRDMeta,
|
||||
labelSelector: string
|
||||
labelSelector: string,
|
||||
limit: number,
|
||||
_continue: string
|
||||
): Promise<{
|
||||
response: http.IncomingMessage;
|
||||
body: object;
|
||||
@@ -238,9 +240,10 @@ export async function ListClusterObject(
|
||||
meta.plural,
|
||||
undefined,
|
||||
undefined,
|
||||
_continue,
|
||||
undefined,
|
||||
undefined,
|
||||
labelSelector
|
||||
labelSelector,
|
||||
limit
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user