Commit 2dae56e1 by HoMeTown

feat&chore: 功能补充+打包优化

parent f2729c4c
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
"build:sit": "npx rsbuild dev --env-mode sit", "build:sit": "npx rsbuild dev --env-mode sit",
"build": "rsbuild build", "build": "rsbuild build",
"doctor": "RSDOCTOR=true pnpm build", "doctor": "RSDOCTOR=true pnpm build",
"preview": "rsbuild preview", "preview": "npx rsbuild preview --env-mode production",
"lint": "eslint .", "lint": "eslint .",
"lint:fix": "eslint . --fix", "lint:fix": "eslint . --fix",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
...@@ -39,8 +39,10 @@ ...@@ -39,8 +39,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@heroui/popover": "^2.3.10",
"@nextui-org/react": "^2.4.6", "@nextui-org/react": "^2.4.6",
"@reduxjs/toolkit": "^2.2.7", "@reduxjs/toolkit": "^2.2.7",
"@rsbuild/plugin-image-compress": "^1.1.0",
"ahooks": "^3.8.0", "ahooks": "^3.8.0",
"axios": "^1.7.3", "axios": "^1.7.3",
"clsx": "^2.1.1", "clsx": "^2.1.1",
...@@ -69,7 +71,7 @@ ...@@ -69,7 +71,7 @@
"@rsbuild/plugin-less": "1.0.1-beta.9", "@rsbuild/plugin-less": "1.0.1-beta.9",
"@rsbuild/plugin-react": "1.0.1-beta.9", "@rsbuild/plugin-react": "1.0.1-beta.9",
"@rsbuild/plugin-svgr": "1.0.1-beta.9", "@rsbuild/plugin-svgr": "1.0.1-beta.9",
"@rsbuild/plugin-typed-css-modules": "^1.0.1", "@rsbuild/plugin-typed-css-modules": "^1.0.2",
"@rsdoctor/rspack-plugin": "^0.3.10", "@rsdoctor/rspack-plugin": "^0.3.10",
"@types/node": "^22.0.2", "@types/node": "^22.0.2",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
......
...@@ -4,6 +4,7 @@ import { pluginSvgr } from '@rsbuild/plugin-svgr' ...@@ -4,6 +4,7 @@ import { pluginSvgr } from '@rsbuild/plugin-svgr'
import { pluginLess } from '@rsbuild/plugin-less' import { pluginLess } from '@rsbuild/plugin-less'
import { pluginTypedCSSModules } from '@rsbuild/plugin-typed-css-modules' import { pluginTypedCSSModules } from '@rsbuild/plugin-typed-css-modules'
import { RsdoctorRspackPlugin } from '@rsdoctor/rspack-plugin' import { RsdoctorRspackPlugin } from '@rsdoctor/rspack-plugin'
import { pluginImageCompress } from '@rsbuild/plugin-image-compress'
import type { EnvConfKey } from './src/config/env' import type { EnvConfKey } from './src/config/env'
import { envConf } from './src/config/env' // https://rsdoctor.dev/zh/guide/start/quick-start import { envConf } from './src/config/env' // https://rsdoctor.dev/zh/guide/start/quick-start
...@@ -21,8 +22,10 @@ export default defineConfig({ ...@@ -21,8 +22,10 @@ export default defineConfig({
pluginLess(), pluginLess(),
pluginTypedCSSModules(), pluginTypedCSSModules(),
pluginSvgr(), pluginSvgr(),
pluginImageCompress(),
], ],
dev: { dev: {
lazyCompilation: true,
assetPrefix: '/sdream-ai', assetPrefix: '/sdream-ai',
// 与本地开发有关的选项 // 与本地开发有关的选项
}, },
...@@ -55,6 +58,8 @@ export default defineConfig({ ...@@ -55,6 +58,8 @@ export default defineConfig({
}, },
}, },
output: { output: {
target: 'web',
polyfill: 'usage',
assetPrefix: '/sdream-ai', assetPrefix: '/sdream-ai',
// 与构建产物有关的选项 // 与构建产物有关的选项
distPath: { distPath: {
...@@ -67,6 +72,10 @@ export default defineConfig({ ...@@ -67,6 +72,10 @@ export default defineConfig({
cssModules: { cssModules: {
localIdentName: isProd ? '[hash:base64]' : '[local]', localIdentName: isProd ? '[hash:base64]' : '[local]',
}, },
dataUriLimit: {
image: 5000,
media: 0,
},
}, },
source: { source: {
// 与源代码解析、编译方式相关的选项 // 与源代码解析、编译方式相关的选项
...@@ -89,6 +98,17 @@ export default defineConfig({ ...@@ -89,6 +98,17 @@ export default defineConfig({
}, },
performance: { performance: {
// 与构建性能、运行时性能有关的选项 // 与构建性能、运行时性能有关的选项
chunkSplit: {
strategy: 'split-by-experience',
forceSplitting: {
hastUtilRaw: /node_modules[\\/]hast-util-raw/,
reactPhotoView: /node_modules[\\/]react-photo-view/,
reactJoyride: '/node_modules[\\/].pnpm[\\/]react-joyride/',
reactMarkdown: '/node_modules[\\/]react-markdown/',
reactHotToast: '/node_modules[\\/]react-hot-toast/',
nextUi: /node_modules[\\/]@nextui-org/,
},
},
}, },
environments: { environments: {
// 为每个环境定义不同的 Rsbuild 配置 // 为每个环境定义不同的 Rsbuild 配置
......
...@@ -112,6 +112,8 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth, ...@@ -112,6 +112,8 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
<div <div
ref={editorRef} ref={editorRef}
contentEditable contentEditable
enterKeyHint="send"
role="textbox"
translate="no" translate="no"
className="w-full min-h-[40px] max-h-[200px] p-2 rounded overflow-y-auto outline-none" className="w-full min-h-[40px] max-h-[200px] p-2 rounded overflow-y-auto outline-none"
onInput={handleInput} onInput={handleInput}
...@@ -124,7 +126,7 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth, ...@@ -124,7 +126,7 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
}} }}
/> />
<Tooltip isOpen={Boolean(token) && showContentTips && !isAsking && !content} color="foreground" content="请输入您的问题📖" placement="top"> <Tooltip isOpen={Boolean(token) && showContentTips && !isAsking && !content} color="foreground" content="请输入您的问题📖" placement="top-end">
<Button className="ask-send" onClick={handleSubmit} radius="full" isDisabled={!content || isAsking} isIconOnly color="primary"> <Button className="ask-send" onClick={handleSubmit} radius="full" isDisabled={!content || isAsking} isIconOnly color="primary">
<SendIcon /> <SendIcon />
</Button> </Button>
......
...@@ -9,7 +9,7 @@ export const envConf = { ...@@ -9,7 +9,7 @@ export const envConf = {
}, },
prod: { prod: {
apiUrl: 'https://sit-sdream.insurbank.cn/sdream-api', apiUrl: 'https://sit-sdream.insurbank.cn/sdream-api',
proxyUrl: '', proxyUrl: 'https://sit-sdream.insurbank.cn/sdream-api',
}, },
} }
......
...@@ -2,6 +2,7 @@ import type React from 'react' ...@@ -2,6 +2,7 @@ import type React from 'react'
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useLocation } from 'react-router-dom' import { useLocation } from 'react-router-dom'
import { useSessionStorageState } from 'ahooks'
import { Navbar } from '../Navbar' import { Navbar } from '../Navbar'
import { HistoryBar } from '../HistoryBar/HistoryBar' import { HistoryBar } from '../HistoryBar/HistoryBar'
import styles from './MainLayout.module.less' import styles from './MainLayout.module.less'
...@@ -40,10 +41,16 @@ const contentVariants = { ...@@ -40,10 +41,16 @@ const contentVariants = {
export const MainLayout: React.FC<MainLayoutProps> = ({ children }) => { export const MainLayout: React.FC<MainLayoutProps> = ({ children }) => {
const { showLoginModal, toggleLoginModal } = useAuth() const { showLoginModal, toggleLoginModal } = useAuth()
const [isHistoryVisible, setHistoryVisible] = useState(false) const [isHistoryVisible, setHistoryVisible] = useState(false)
const [isNavBarVisible, setNavBarvisible] = useState(true)
const location = useLocation() const location = useLocation()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const token = window.localStorage.getItem('__TOKEN__') const token = window.localStorage.getItem('__TOKEN__')
const [navBarVisibleLocal] = useSessionStorageState<string | undefined>(
'__NAV_BAR_VISIBLE_LOCAL__',
{
defaultValue: '0',
listenStorageChange: true,
},
)
useEffect(() => { useEffect(() => {
if (token) { if (token) {
...@@ -61,7 +68,7 @@ export const MainLayout: React.FC<MainLayoutProps> = ({ children }) => { ...@@ -61,7 +68,7 @@ export const MainLayout: React.FC<MainLayoutProps> = ({ children }) => {
<motion.main className={styles.layoutMain}> <motion.main className={styles.layoutMain}>
{/* hidden */} {/* hidden */}
<motion.div <motion.div
animate={isNavBarVisible ? isHistoryVisible ? 'shrunk' : 'expanded' : 'navTween'} animate={navBarVisibleLocal === '0' ? isHistoryVisible ? 'shrunk' : 'expanded' : 'navTween'}
variants={contentVariants} variants={contentVariants}
className={`fixed right-[-12px] top-[10px] z-[49] h-auto sm:relative flex sm:h-full pl-[12px] items-center ${isHistoryVisible && !isMobile() ? 'w-[340px]' : 'w-[90px]'} box-border`} className={`fixed right-[-12px] top-[10px] z-[49] h-auto sm:relative flex sm:h-full pl-[12px] items-center ${isHistoryVisible && !isMobile() ? 'w-[340px]' : 'w-[90px]'} box-border`}
> >
...@@ -69,8 +76,6 @@ export const MainLayout: React.FC<MainLayoutProps> = ({ children }) => { ...@@ -69,8 +76,6 @@ export const MainLayout: React.FC<MainLayoutProps> = ({ children }) => {
<Navbar <Navbar
isHistoryVisible={isHistoryVisible} isHistoryVisible={isHistoryVisible}
onSetHistoryVisible={setHistoryVisible} onSetHistoryVisible={setHistoryVisible}
isNavBarVisible={isNavBarVisible}
onSetNavBarVisible={setNavBarvisible}
/> />
<HistoryBar isVisible={isHistoryVisible} onSetHistoryVisible={setHistoryVisible} /> <HistoryBar isVisible={isHistoryVisible} onSetHistoryVisible={setHistoryVisible} />
...@@ -92,7 +97,7 @@ export const MainLayout: React.FC<MainLayoutProps> = ({ children }) => { ...@@ -92,7 +97,7 @@ export const MainLayout: React.FC<MainLayoutProps> = ({ children }) => {
</motion.div> </motion.div>
<motion.div <motion.div
variants={contentVariants} variants={contentVariants}
animate={isNavBarVisible ? '' : 'mainTween'} animate={navBarVisibleLocal === '0' ? '' : 'mainTween'}
className={`${styles.layoutContent}`} className={`${styles.layoutContent}`}
> >
{children} {children}
......
...@@ -40,13 +40,13 @@ ...@@ -40,13 +40,13 @@
&:hover { &:hover {
&::before { &::before {
background-color: #29B6FD; background-color: #29B6FD;
border-radius: 10px !important; border-radius: 10px;
transform: rotate(-17deg) scaleY(1.2) !important; transform: rotate(-17deg) scaleY(1.2);
} }
&::after { &::after {
background-color: #29B6FD; background-color: #29B6FD;
border-radius: 10px !important; border-radius: 10px;
transform: rotate(17deg) scaleY(1.2) !important; transform: rotate(17deg) scaleY(1.2);
} }
} }
&::before { &::before {
......
...@@ -2,7 +2,7 @@ import type React from 'react' ...@@ -2,7 +2,7 @@ import type React from 'react'
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { useClickAway } from 'ahooks' import { useClickAway, useSessionStorageState } from 'ahooks'
import styles from './Navbar.module.less' import styles from './Navbar.module.less'
import { NavBarItem } from './components/NavBarItem' import { NavBarItem } from './components/NavBarItem'
import { User } from './components/User' import { User } from './components/User'
...@@ -18,11 +18,9 @@ import JoyrideStep from '@/components/JoyrideStep' ...@@ -18,11 +18,9 @@ import JoyrideStep from '@/components/JoyrideStep'
interface NavbarProps { interface NavbarProps {
isHistoryVisible: boolean isHistoryVisible: boolean
onSetHistoryVisible: (visible: boolean) => void onSetHistoryVisible: (visible: boolean) => void
isNavBarVisible: boolean
onSetNavBarVisible: (visible: boolean) => void
} }
const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, checkAuth, onSetHistoryVisible, isNavBarVisible, onSetNavBarVisible }) => { const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, checkAuth, onSetHistoryVisible }) => {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const navigate = useNavigate() const navigate = useNavigate()
...@@ -75,8 +73,16 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c ...@@ -75,8 +73,16 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c
navigate('/') navigate('/')
} }
const [navBarVisibleLocal, setNavBarVisibleLocal] = useSessionStorageState<string | undefined>(
'__NAV_BAR_VISIBLE_LOCAL__',
{
defaultValue: '0',
listenStorageChange: true,
},
)
const toggleNavBarVisible = () => { const toggleNavBarVisible = () => {
onSetNavBarVisible(!isNavBarVisible) setNavBarVisibleLocal(navBarVisibleLocal === '1' ? '0' : '1')
} }
useEffect(() => { useEffect(() => {
...@@ -95,7 +101,7 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c ...@@ -95,7 +101,7 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c
<> <>
<motion.nav <motion.nav
ref={navRef} ref={navRef}
animate={isNavBarVisible ? '' : 'hidden'} animate={navBarVisibleLocal === '0' ? 'hidden' : ''}
className="h-full flex-shrink-0 flex flex-col items-center justify-center relative" className="h-full flex-shrink-0 flex flex-col items-center justify-center relative"
> >
{/* hidden */} {/* hidden */}
...@@ -109,11 +115,10 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c ...@@ -109,11 +115,10 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c
})} })}
<User onLogout={handleLogout} /> <User onLogout={handleLogout} />
</motion.div> </motion.div>
<div onClick={toggleNavBarVisible} className={`${styles.sidebarAction} ${isNavBarVisible ? 'open' : ''}`}></div> <div onClick={toggleNavBarVisible} className={`${styles.sidebarAction} ${navBarVisibleLocal === '0' ? styles.open : ''}`}></div>
</motion.nav> </motion.nav>
<JoyrideStep /> {/* <JoyrideStep /> */}
</> </>
) )
} }
......
import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger, Tooltip } from '@nextui-org/react' import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger, Tooltip } from '@nextui-org/react'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { useSessionStorageState, useTimeout } from 'ahooks'
import { useEffect, useState } from 'react'
import { withAuth } from '@/auth/withAuth' import { withAuth } from '@/auth/withAuth'
import type { WithAuthProps } from '@/auth/withAuth' import type { WithAuthProps } from '@/auth/withAuth'
import UserIcon from '@/assets/svg/user.svg?react' import UserIcon from '@/assets/svg/user.svg?react'
...@@ -65,8 +67,30 @@ export const UserLogin: React.FC<UserProps> = ({ onLogout }) => { ...@@ -65,8 +67,30 @@ export const UserLogin: React.FC<UserProps> = ({ onLogout }) => {
} }
const UserNotLoginBase: React.FC<WithAuthProps> = ({ showLoginTip, checkAuth }) => { const UserNotLoginBase: React.FC<WithAuthProps> = ({ showLoginTip, checkAuth }) => {
const [navBarVisibleLocal] = useSessionStorageState<string | undefined>(
'__NAV_BAR_VISIBLE_LOCAL__',
{
defaultValue: '0',
listenStorageChange: true,
},
)
const [navBarVisible, setNavBarVisible] = useState(navBarVisibleLocal === '0')
useEffect(() => {
if (navBarVisibleLocal === '0') {
let timer = null as any
timer = setTimeout(() => {
setNavBarVisible(true)
clearTimeout(timer)
timer = null
}, 300)
}
if (navBarVisibleLocal === '1') {
setNavBarVisible(false)
}
}, [navBarVisibleLocal])
return ( return (
<Tooltip isOpen={showLoginTip && !isMobile()} color="foreground" content="登录体验更多功能" placement="right"> <Tooltip isOpen={navBarVisible && showLoginTip && !isMobile()} color="foreground" content="登录体验更多功能" placement="right">
<Button onClick={checkAuth} variant="light" isIconOnly aria-label="Like"> <Button onClick={checkAuth} variant="light" isIconOnly aria-label="Like">
<UserIcon /> <UserIcon />
</Button> </Button>
......
...@@ -60,9 +60,9 @@ export const Home: React.FC = () => { ...@@ -60,9 +60,9 @@ export const Home: React.FC = () => {
<Slogan /> <Slogan />
{/* 欢迎语 */} {/* 欢迎语 */}
<div className="gap-[20px] flex justify-center flex-row flex-wrap mt-[32px] sm:mt-[62px] lg:mt-[112px]"> <div className="gap-[20px] flex justify-center flex-row flex-wrap mt-[26px] sm:mt-[62px] lg:mt-[112px]">
<motion.div className="w-full mb-[32px] sm:mb-[0px] sm:w-auto" {...getAnimationProps(1)}><WelcomeWord /></motion.div> <motion.div className="w-full mb-[12px] sm:mb-[0px] sm:w-auto" {...getAnimationProps(1)}><WelcomeWord /></motion.div>
<motion.div className="w-full hidden sm:block sm:w-auto" {...getAnimationProps(2)}><QuestionList questions={RECOMMEND_QUESTIONS_PRODUCT} dotColor="#D4CCFF" title="产品问答" iconImg={HomeIcon1} /></motion.div> <motion.div className="w-full hidden sm:block sm:w-auto" {...getAnimationProps(2)}><QuestionList questions={RECOMMEND_QUESTIONS_PRODUCT} dotColor="#D4CCFF" title="产品问答" iconImg={HomeIcon1} /></motion.div>
<motion.div className="w-full sm:w-auto" {...getAnimationProps(3)}><QuestionList questions={RECOMMEND_QUESTIONS_OTHER} dotColor="#CBECFF" title="您可以试着问我" iconImg={HomeIcon2} /></motion.div> <motion.div className="w-full sm:w-auto" {...getAnimationProps(3)}><QuestionList questions={RECOMMEND_QUESTIONS_OTHER} dotColor="#CBECFF" title="您可以试着问我" iconImg={HomeIcon2} /></motion.div>
</div> </div>
......
...@@ -38,7 +38,7 @@ const BotEye: React.FC = () => { ...@@ -38,7 +38,7 @@ const BotEye: React.FC = () => {
const BotAnimateBox: React.FC = () => { const BotAnimateBox: React.FC = () => {
return ( return (
<div className="w-[180px] absolute right-0 top-[-18px]"> <div className="w-[180px] absolute right-0 top-[-18px] scale-75 translate-x-6 sm:scale-100 sm:translate-x-0">
<img className="w-full" src={BotImg} alt="" /> <img className="w-full" src={BotImg} alt="" />
<motion.img <motion.img
animate={{ y: '-4px', transition: { duration: 0.5, repeat: Infinity, repeatType: 'reverse', ease: 'easeInOut' } }} animate={{ y: '-4px', transition: { duration: 0.5, repeat: Infinity, repeatType: 'reverse', ease: 'easeInOut' } }}
...@@ -74,10 +74,10 @@ const WelcomeWordBase: React.FC<WithAuthProps> = ({ checkAuth }) => { ...@@ -74,10 +74,10 @@ const WelcomeWordBase: React.FC<WithAuthProps> = ({ checkAuth }) => {
<div className="w-full h-auto flex-shrink-0 relative sm:w-[360px] sm:h-[276px]"> <div className="w-full h-auto flex-shrink-0 relative sm:w-[360px] sm:h-[276px]">
<img className="absolute z-[-1] top-0 left-0" src={BotBgImg} alt="" /> <img className="absolute z-[-1] top-0 left-0" src={BotBgImg} alt="" />
<BotAnimateBox /> <BotAnimateBox />
<div className="relative z-[1] box-border px-[24px] pt-[68px]"> <div className="px-[18px] relative z-[1] box-border sm:px-[24px] pt-[68px]">
<SayHi /> <SayHi />
<p className="text-[#27353C] text-[15px] mt-[24px] mb-[16px]">做为您的智能保险伙伴,您有各类专业相关的问题都可以抛给我哟~让我们互相帮助共同成长吧~</p> <p className="mt-[16px] text-[#27353C] text-[15px] sm:mt-[24px] mb-[16px]">做为您的智能保险伙伴,您有各类专业相关的问题都可以抛给我哟~让我们互相帮助共同成长吧~</p>
<Button className="bg-white text-[#20ABD9] font-medium rounded-[20px]" onClick={handleGo}>立即前往 ➔</Button> <Button className="bg-white w-full text-[#20ABD9] font-medium rounded-[20px] sm:w-auto" onClick={handleGo}>立即前往 ➔</Button>
</div> </div>
</div> </div>
) )
......
import { Virtuoso } from 'react-virtuoso'
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import styles from './Tools.module.less' import styles from './Tools.module.less'
import { Slogan } from './components/Slogan/Slogan' import { Slogan } from './components/Slogan/Slogan'
...@@ -53,10 +52,6 @@ export const Tools: React.FC = () => { ...@@ -53,10 +52,6 @@ export const Tools: React.FC = () => {
<div className="h-full w-full"> <div className="h-full w-full">
<div className="box flex flex-col h-full w-full"> <div className="box flex flex-col h-full w-full">
<div className="flex-1"> <div className="flex-1">
<Virtuoso
style={{ height: '100%' }}
totalCount={1}
itemContent={() => (
<div className="px-[24px] pb-[24px] pt-[42px] sm:pt-[80px] lg:pt-[180px] sm:px-0"> <div className="px-[24px] pb-[24px] pt-[42px] sm:pt-[80px] lg:pt-[180px] sm:px-0">
<Slogan /> <Slogan />
<div> <div>
...@@ -80,8 +75,6 @@ export const Tools: React.FC = () => { ...@@ -80,8 +75,6 @@ export const Tools: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
)}
/>
</div> </div>
</div> </div>
</div> </div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment