This commit is contained in:
archer
2023-07-25 21:53:26 +08:00
parent c5f50b65c9
commit b367082d38
25 changed files with 230 additions and 276 deletions

View File

@@ -1,199 +0,0 @@
import React, { useEffect, useMemo, useRef } from 'react';
import * as echarts from 'echarts';
import { useGlobalStore } from '@/store/global';
const LineChart = ({
type,
limit = 1000000,
data
}: {
type: 'blue' | 'deepBlue' | 'green' | 'purple';
limit: number;
data: number[];
}) => {
const { screenWidth } = useGlobalStore();
const Dom = useRef<HTMLDivElement>(null);
const myChart = useRef<echarts.ECharts>();
const map = {
blue: {
backgroundColor: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(3, 190, 232, 0.42)' // 0% 处的颜色
},
{
offset: 1,
color: 'rgba(0, 182, 240, 0)'
}
],
global: false // 缺省为 false
},
lineColor: '#36ADEF'
},
deepBlue: {
backgroundColor: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(47, 112, 237, 0.42)' // 0% 处的颜色
},
{
offset: 1,
color: 'rgba(94, 159, 235, 0)'
}
],
global: false
},
lineColor: '#3293EC'
},
purple: {
backgroundColor: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(211, 190, 255, 0.42)' // 0% 处的颜色
},
{
offset: 1,
color: 'rgba(52, 60, 255, 0)'
}
],
global: false // 缺省为 false
},
lineColor: '#8172D8'
},
green: {
backgroundColor: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(4, 209, 148, 0.42)' // 0% 处的颜色
},
{
offset: 1,
color: 'rgba(19, 217, 181, 0)'
}
],
global: false // 缺省为 false
},
lineColor: '#00A9A6',
max: 100
}
};
const option = useMemo(
() => ({
xAxis: {
type: 'category',
show: false,
boundaryGap: false,
data: data.map((_, i) => i)
},
yAxis: {
type: 'value',
boundaryGap: false,
splitNumber: 2,
max: 100,
min: 0
},
grid: {
show: false,
left: 0,
right: 0,
top: 0,
bottom: 2
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'line'
},
formatter: (e: any[]) => `${e[0]?.value || 0}%`
},
series: [
{
data: new Array(data.length).fill(0),
type: 'line',
showSymbol: false,
smooth: true,
animationDuration: 300,
animationEasingUpdate: 'linear',
areaStyle: {
color: map[type].backgroundColor
},
lineStyle: {
width: '1',
color: map[type].lineColor
},
itemStyle: {
width: 1.5,
color: map[type].lineColor
},
emphasis: {
// highlight
disabled: true
}
}
]
}),
[limit, type]
);
// init chart
useEffect(() => {
if (!Dom.current || myChart?.current?.getOption()) return;
myChart.current = echarts.init(Dom.current);
myChart.current && myChart.current.setOption(option);
}, [Dom]);
// data changed, update
useEffect(() => {
if (!myChart.current || !myChart?.current?.getOption()) return;
const uniData = data.map((item) => ((item / limit) * 100).toFixed(2));
const x = option.xAxis.data;
option.xAxis.data = [...x.slice(1), x[x.length - 1] + 1];
option.series[0].data = uniData;
myChart.current.setOption(option);
}, [data, limit]);
// limit changed, update
useEffect(() => {
if (!myChart.current || !myChart?.current?.getOption()) return;
myChart.current.setOption(option);
}, [limit, option, type]);
// resize chart
useEffect(() => {
if (!myChart.current || !myChart.current.getOption()) return;
myChart.current.resize();
}, [screenWidth]);
return <div ref={Dom} style={{ width: '100%', height: '100%' }} />;
};
export default React.memo(LineChart);

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1690290001439" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2340" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M579.584 478.208h-9.728l37.376 207.872c24.576-8.704 47.104-22.016 67.072-39.424-20.48-24.576-36.864-52.224-49.664-81.408l39.424-5.12c10.752 22.016 23.04 41.984 37.376 58.88 29.184-35.328 51.2-82.432 67.072-141.312l-188.928 0.512z m148.992 168.448c22.528 19.456 48.128 33.792 76.8 42.496l17.92 5.632-10.752 38.4-17.92-5.632c-34.304-10.752-66.048-28.672-93.184-52.736-25.088 22.528-54.784 39.936-87.04 50.688l25.088 140.288H489.984L470.016 957.44h469.504c22.016 0 39.936-17.92 39.936-39.936V238.592c0-22.016-17.92-39.936-39.936-39.936h-419.328l31.232 174.08-1.024-0.512 3.584 18.944 0.512-2.56 8.704 49.664h96.768v-39.936h74.752v39.936h124.928v39.936h-52.224c-17.408 70.144-44.032 126.464-78.848 168.448zM449.024 865.28H84.992C40.96 865.28 5.12 829.44 5.12 785.408V107.008C5.12 62.464 40.96 27.136 84.992 27.136h399.36L508.928 158.72h430.08c44.032 0 79.872 35.84 79.872 79.872v678.912c0 44.032-35.84 79.872-79.872 79.872H420.352l28.672-132.096z m-188.416-307.2v-41.984H181.76V454.144h73.216v-41.472H181.76V359.936h78.848v-41.472H135.68V558.08h124.928z m192.512 0V438.272c0-21.504-5.12-38.4-14.848-50.176-9.728-11.776-24.576-17.408-44.032-17.408-11.264 0-21.504 2.048-30.208 6.656s-15.872 11.776-20.48 20.48h-2.56l-6.144-23.552h-34.816V558.08h45.056V471.552c0-21.504 3.072-36.864 8.704-46.592 5.632-9.216 14.848-13.824 27.648-13.824 9.216 0 15.872 3.072 20.48 9.728 4.096 6.656 6.656 16.384 6.656 29.696v107.52h44.544z" p-id="2341"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1690290025571" class="icon" viewBox="0 0 1070 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2494" xmlns:xlink="http://www.w3.org/1999/xlink" width="66.875" height="64"><path d="M232.58156522 358.13286957C244.86956522 394.4626087 265.17147826 425.984 293.48730435 453.76556522c24.04173913-26.17878261 42.2066087-58.23443478 53.96034782-95.63269565H232.58156522z" p-id="2495"></path><path d="M981.61530435 143.36h-448.77913044L507.19165217 6.05495652h-416.72347826c-45.94643478 0-83.34469565 37.39826087-83.34469565 83.34469565v708.42991305c0 45.94643478 37.39826087 83.34469565 83.34469565 83.34469565h379.85947826l-30.45286956 137.30504348h541.74052174c45.94643478 0 83.34469565-37.39826087 83.34469565-83.34469565V226.70469565c0-45.94643478-37.39826087-83.34469565-83.34469565-83.34469565zM415.83304348 564.35756522c-49.152-18.16486957-89.75582609-41.13808696-122.34573913-67.85113044-34.19269565 30.45286957-76.93356522 52.89182609-126.61982609 66.7826087l-17.09634783-28.31582609c48.61773913-12.82226087 89.22156522-32.05565217 121.2772174-59.30295652-33.12417391-33.65843478-56.0973913-72.65947826-68.91965218-117.00313044h-46.48069565v-32.05565217H276.92521739c-7.47965217-13.89078261-17.09634783-27.24730435-28.31582609-40.06956522l32.05565218-11.75373913c11.21947826 14.42504348 21.37043478 31.5213913 30.45286956 51.28904348h115.9346087v32.05565218h-46.48069565c-14.95930435 45.94643478-36.32973913 84.41321739-64.64556522 115.40034782 31.5213913 25.11026087 71.05669565 45.94643478 117.5373913 63.04278261l-17.63060869 27.78156522z m607.45460869 370.24278261c0 22.97321739-18.69913043 41.67234783-41.67234782 41.67234782H492.23234783l20.83617391-95.63269565h156.53843478l-89.22156522-497.39686957-0.53426087 2.67130435-3.73982608-19.76765217 1.06852174 0.53426087-32.58991305-181.64869565H982.14956522c22.97321739 0 41.67234783 18.69913043 41.67234782 41.67234782v707.89565218z" p-id="2496"></path><path d="M684.56626087 541.38434783h114.86608696v-30.45286957h-114.86608696V450.02573913h122.34573913v-30.45286956h-158.14121739v219.04695652h162.94956522V608.16695652h-127.15408696v-66.78260869z m239.88313043-65.71408696c-9.61669565 0-18.16486957 1.60278261-26.1787826 5.87686956-7.47965217 3.73982609-14.95930435 9.61669565-20.83617392 17.09634783V479.94434783h-34.72695652v158.67547826h34.72695652v-95.63269566c1.06852174-12.82226087 5.3426087-22.43895652 12.82226087-29.38434782 6.41113043-5.87686957 13.89078261-9.08243478 22.43895652-9.08243478 24.04173913 0 35.79547826 12.82226087 35.79547826 39.00104347v94.56417392h34.72695653v-97.76973913c1.06852174-43.27513043-19.2333913-64.64556522-58.76869566-64.64556522z" p-id="2497"></path></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -68,7 +68,9 @@ const map = {
informLight: require('./icons/light/inform.svg').default,
payRecordLight: require('./icons/light/payRecord.svg').default,
loginoutLight: require('./icons/light/loginout.svg').default,
chatModelTag: require('./icons/light/chatModelTag.svg').default
chatModelTag: require('./icons/light/chatModelTag.svg').default,
language_en: require('./icons/language/en.svg').default,
language_zh: require('./icons/language/zh.svg').default
};
export type IconName = keyof typeof map;

View File

@@ -0,0 +1,60 @@
import React, { useState } from 'react';
import { Menu, MenuButton, MenuItem, MenuList, MenuButtonProps } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import { getLangStore, LangEnum, setLangStore } from '@/utils/i18n';
import MyIcon from '@/components/Icon';
const langMap = {
[LangEnum.en]: {
label: 'English',
icon: 'language_en'
},
[LangEnum.zh]: {
label: '简体中文',
icon: 'language_zh'
}
};
const Language = (props: MenuButtonProps) => {
const router = useRouter();
const [language, setLanguage] = useState<`${LangEnum}`>(getLangStore());
return (
<Menu autoSelect={false}>
<MenuButton
{...props}
sx={{
'& span': {
flex: 0
}
}}
>
<MyIcon name={langMap[language].icon as any} w={['18px', '22px']} />
</MenuButton>
<MenuList w="max-content" minW="120px">
{Object.entries(langMap).map(([key, lang]) => (
<MenuItem
key={key}
display="flex"
alignItems="center"
onClick={() => {
const lang = key as `${LangEnum}`;
setLangStore(lang);
setLanguage(lang);
router.replace({
query: {
...router.query,
lang
}
});
}}
>
{lang.label}
</MenuItem>
))}
</MenuList>
</Menu>
);
};
export default React.memo(Language);

View File

@@ -9,6 +9,8 @@ import NextLink from 'next/link';
import Badge from '../Badge';
import Avatar from '../Avatar';
import MyIcon from '../Icon';
import Language from '../Language';
import { useTranslation } from 'next-i18next';
export enum NavbarTypeEnum {
normal = 'normal',
@@ -16,50 +18,54 @@ export enum NavbarTypeEnum {
}
const Navbar = ({ unread }: { unread: number }) => {
const { t } = useTranslation();
const router = useRouter();
const { userInfo } = useUserStore();
const { lastChatAppId, lastChatId } = useChatStore();
const navbarList = [
{
label: '聊天',
icon: 'chatLight',
activeIcon: 'chatFill',
link: `/chat?appId=${lastChatAppId}&chatId=${lastChatId}`,
activeLink: ['/chat']
},
{
label: '应用',
icon: 'appLight',
activeIcon: 'appFill',
link: `/app/list`,
activeLink: ['/app/list', '/app/detail']
},
{
label: '知识库',
icon: 'dbLight',
activeIcon: 'dbFill',
link: `/kb/list`,
activeLink: ['/kb/list', '/kb/detail']
},
...(feConfigs?.show_appStore
? [
{
label: '市场',
icon: 'appStoreLight',
activeIcon: 'appStoreFill',
link: '/appStore',
activeLink: ['/appStore']
}
]
: []),
{
label: '账号',
icon: 'meLight',
activeIcon: 'meFill',
link: '/number',
activeLink: ['/number']
}
];
const navbarList = useMemo(
() => [
{
label: t('navbar.Chat'),
icon: 'chatLight',
activeIcon: 'chatFill',
link: `/chat?appId=${lastChatAppId}&chatId=${lastChatId}`,
activeLink: ['/chat']
},
{
label: t('navbar.Apps'),
icon: 'appLight',
activeIcon: 'appFill',
link: `/app/list`,
activeLink: ['/app/list', '/app/detail']
},
{
label: t('navbar.Datasets'),
icon: 'dbLight',
activeIcon: 'dbFill',
link: `/kb/list`,
activeLink: ['/kb/list', '/kb/detail']
},
...(feConfigs?.show_appStore
? [
{
label: t('navbar.Store'),
icon: 'appStoreLight',
activeIcon: 'appStoreFill',
link: '/appStore',
activeLink: ['/appStore']
}
]
: []),
{
label: t('navbar.Account'),
icon: 'meLight',
activeIcon: 'meFill',
link: '/account',
activeLink: ['/account']
}
],
[lastChatAppId, lastChatId, t]
);
const itemStyles: any = {
my: 3,
@@ -94,7 +100,7 @@ const Navbar = ({ unread }: { unread: number }) => {
borderRadius={'50%'}
overflow={'hidden'}
cursor={'pointer'}
onClick={() => router.push('/number')}
onClick={() => router.push('/account')}
>
<Avatar w={'36px'} h={'36px'} src={userInfo?.avatar} fallbackSrc={HUMAN_ICON} />
</Box>
@@ -133,25 +139,31 @@ const Navbar = ({ unread }: { unread: number }) => {
</Box>
{unread > 0 && (
<Box>
<Link as={NextLink} {...itemStyles} href={`/number?type=inform`} mb={0} color={'#9096a5'}>
<Link
as={NextLink}
{...itemStyles}
href={`/account?type=inform`}
mb={0}
color={'#9096a5'}
>
<Badge count={unread}>
<MyIcon name={'inform'} width={'22px'} height={'22px'} />
</Badge>
</Link>
</Box>
)}
<Language {...itemStyles} />
{feConfigs?.show_git && (
<Box>
<Link
as={NextLink}
href="https://github.com/labring/FastGPT"
target={'_blank'}
{...itemStyles}
color={'#9096a5'}
>
<MyIcon name={'git'} width={'22px'} height={'22px'} />
</Link>
</Box>
<Link
as={NextLink}
href="https://github.com/labring/FastGPT"
target={'_blank'}
{...itemStyles}
mt={0}
color={'#9096a5'}
>
<MyIcon name={'git'} width={'22px'} height={'22px'} />
</Link>
)}
</Flex>
);

View File

@@ -2,44 +2,46 @@ import React, { useMemo } from 'react';
import { useRouter } from 'next/router';
import { Flex, Box } from '@chakra-ui/react';
import { useChatStore } from '@/store/chat';
import { useTranslation } from 'react-i18next';
import Badge from '../Badge';
import MyIcon from '../Icon';
const NavbarPhone = ({ unread }: { unread: number }) => {
const router = useRouter();
const { t } = useTranslation();
const { lastChatAppId, lastChatId } = useChatStore();
const navbarList = useMemo(
() => [
{
label: '聊天',
label: t('navbar.Chat'),
icon: 'chatLight',
link: `/chat?appId=${lastChatAppId}&chatId=${lastChatId}`,
activeLink: ['/chat'],
unread: 0
},
{
label: '应用',
label: t('navbar.Apps'),
icon: 'tabbarModel',
link: `/app/list`,
activeLink: ['/app/list', '/app/detail'],
unread: 0
},
{
label: '工具',
label: t('navbar.Tools'),
icon: 'tabbarMore',
link: '/tools',
activeLink: ['/tools'],
unread: 0
},
{
label: '我的',
label: t('navbar.Account'),
icon: 'tabbarMe',
link: '/number',
activeLink: ['/number'],
link: '/account',
activeLink: ['/account'],
unread
}
],
[lastChatId, lastChatAppId, unread]
[t, lastChatAppId, lastChatId, unread]
);
return (

View File

@@ -10,6 +10,7 @@ import PageContainer from '@/components/PageContainer';
import SideTabs from '@/components/SideTabs';
import Tabs from '@/components/Tabs';
import UserInfo from './components/Info';
import { serviceSideProps } from '@/utils/i18n';
const BillTable = dynamic(() => import('./components/BillTable'), {
ssr: false
@@ -29,7 +30,7 @@ enum TabEnum {
'loginout' = 'loginout'
}
const NumberSetting = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
const tabList = useRef([
{ icon: 'meLight', label: '个人信息', id: TabEnum.info, Component: <BillTable /> },
{ icon: 'billRecordLight', label: '消费记录', id: TabEnum.bill, Component: <BillTable /> },
@@ -115,12 +116,13 @@ const NumberSetting = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
);
};
export async function getServerSideProps({ query }: any) {
export async function getServerSideProps(content: any) {
return {
props: {
currentTab: query?.currentTab || TabEnum.info
currentTab: content?.query?.currentTab || TabEnum.info,
...(await serviceSideProps(content))
}
};
}
export default NumberSetting;
export default Account;

View File

@@ -14,6 +14,7 @@ import MyIcon from '@/components/Icon';
import PageContainer from '@/components/PageContainer';
import Loading from '@/components/Loading';
import BasicEdit from './components/BasicEdit';
import { serviceSideProps } from '@/utils/i18n';
const AdEdit = dynamic(() => import('./components/AdEdit'), {
ssr: false,
@@ -188,7 +189,7 @@ export async function getServerSideProps(context: any) {
const currentTab = context?.query?.currentTab || TabEnum.basicEdit;
return {
props: { currentTab }
props: { currentTab, ...(await serviceSideProps(context)) }
};
}

View File

@@ -16,17 +16,21 @@ import { AddIcon } from '@chakra-ui/icons';
import { delModelById } from '@/api/app';
import { useToast } from '@/hooks/useToast';
import { useConfirm } from '@/hooks/useConfirm';
import dynamic from 'next/dynamic';
import { serviceSideProps } from '@/utils/i18n';
import { useTranslation } from 'next-i18next';
import dynamic from 'next/dynamic';
import MyIcon from '@/components/Icon';
import PageContainer from '@/components/PageContainer';
import Avatar from '@/components/Avatar';
const CreateModal = dynamic(() => import('./component/CreateModal'));
import styles from './index.module.scss';
import MyTooltip from '@/components/MyTooltip';
const CreateModal = dynamic(() => import('./component/CreateModal'));
import styles from './index.module.scss';
const MyApps = () => {
const { toast } = useToast();
const { t } = useTranslation();
const theme = useTheme();
const router = useRouter();
const { myApps, loadMyModels } = useUserStore();
@@ -69,7 +73,7 @@ const MyApps = () => {
<PageContainer>
<Flex pt={3} px={5} alignItems={'center'}>
<Box flex={1} className="textlg" letterSpacing={1} fontSize={'24px'} fontWeight={'bold'}>
{t('app.My Apps')}
</Box>
<Button leftIcon={<AddIcon />} variant={'base'} onClick={onOpenCreateModal}>
@@ -167,4 +171,12 @@ const MyApps = () => {
);
};
export async function getServerSideProps(content: any) {
return {
props: {
...(await serviceSideProps(content))
}
};
}
export default MyApps;

View File

@@ -7,6 +7,7 @@ import type { ShareAppItem } from '@/types/app';
import { useUserStore } from '@/store/user';
import ShareModelList from './components/list';
import styles from './index.module.scss';
import { serviceSideProps } from '@/utils/i18n';
const modelList = () => {
const { Loading } = useLoading();
@@ -89,4 +90,12 @@ const modelList = () => {
);
};
export async function getServerSideProps(content: any) {
return {
props: {
...(await serviceSideProps(content))
}
};
}
export default modelList;

View File

@@ -17,6 +17,7 @@ import SideTabs from '@/components/SideTabs';
import PageContainer from '@/components/PageContainer';
import Avatar from '@/components/Avatar';
import Info from './components/Info';
import { serviceSideProps } from '@/utils/i18n';
const ImportData = dynamic(() => import('./components/Import'), {
ssr: false
@@ -159,7 +160,7 @@ export async function getServerSideProps(context: any) {
const kbId = context?.query?.kbId;
return {
props: { currentTab, kbId }
props: { currentTab, kbId, ...(await serviceSideProps(content)) }
};
}

View File

@@ -12,6 +12,7 @@ import { useRequest } from '@/hooks/useRequest';
import Avatar from '@/components/Avatar';
import MyIcon from '@/components/Icon';
import Tag from '@/components/Tag';
import { serviceSideProps } from '@/utils/i18n';
const Kb = () => {
const theme = useTheme();
@@ -156,4 +157,12 @@ const Kb = () => {
);
};
export async function getServerSideProps(content: any) {
return {
props: {
...(await serviceSideProps(content))
}
};
}
export default Kb;

View File

@@ -4,6 +4,7 @@ import { ChevronRightIcon } from '@chakra-ui/icons';
import MyIcon from '@/components/Icon';
import { useRouter } from 'next/router';
import { feConfigs } from '@/store/static';
import { serviceSideProps } from '@/utils/i18n';
const list = [
{
@@ -57,4 +58,12 @@ const Tools = () => {
);
};
export async function getServerSideProps(content: any) {
return {
props: {
...(await serviceSideProps(content))
}
};
}
export default Tools;

View File

@@ -29,6 +29,7 @@ export const clientInitData = async (): Promise<InitDateResponse> => {
beianText = res.systemEnv?.beianText;
googleClientVerKey = res.systemEnv?.googleClientVerKey;
baiduTongji = res.systemEnv?.baiduTongji;
console.log(res.feConfigs);
return res;
} catch (error) {

13
client/src/types/i18n.d.ts vendored Normal file
View File

@@ -0,0 +1,13 @@
import 'i18next';
import common from '../../public/locales/en/common.json';
interface I18nNamespaces {
common: typeof common;
}
declare module 'i18next' {
interface CustomTypeOptions {
defaultNS: 'common';
resources: I18nNamespaces;
}
}

View File

@@ -12,11 +12,7 @@ export const setLangStore = (value: `${LangEnum}`) => {
};
export const getLangStore = () => {
return Cookies.get(LANG_KEY) || LangEnum.zh;
};
export const removeLangStore = () => {
Cookies.remove(LANG_KEY);
return (Cookies.get(LANG_KEY) as `${LangEnum}`) || LangEnum.zh;
};
export const serviceSideProps = (content: any) => {