Commit 2dae56e1 by HoMeTown

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

parent f2729c4c
......@@ -9,7 +9,7 @@
"build:sit": "npx rsbuild dev --env-mode sit",
"build": "rsbuild build",
"doctor": "RSDOCTOR=true pnpm build",
"preview": "rsbuild preview",
"preview": "npx rsbuild preview --env-mode production",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"typecheck": "tsc --noEmit",
......@@ -39,8 +39,10 @@
}
},
"dependencies": {
"@heroui/popover": "^2.3.10",
"@nextui-org/react": "^2.4.6",
"@reduxjs/toolkit": "^2.2.7",
"@rsbuild/plugin-image-compress": "^1.1.0",
"ahooks": "^3.8.0",
"axios": "^1.7.3",
"clsx": "^2.1.1",
......@@ -69,7 +71,7 @@
"@rsbuild/plugin-less": "1.0.1-beta.9",
"@rsbuild/plugin-react": "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",
"@types/node": "^22.0.2",
"@types/react": "^18.3.3",
......
......@@ -4,6 +4,7 @@ import { pluginSvgr } from '@rsbuild/plugin-svgr'
import { pluginLess } from '@rsbuild/plugin-less'
import { pluginTypedCSSModules } from '@rsbuild/plugin-typed-css-modules'
import { RsdoctorRspackPlugin } from '@rsdoctor/rspack-plugin'
import { pluginImageCompress } from '@rsbuild/plugin-image-compress'
import type { EnvConfKey } from './src/config/env'
import { envConf } from './src/config/env' // https://rsdoctor.dev/zh/guide/start/quick-start
......@@ -21,8 +22,10 @@ export default defineConfig({
pluginLess(),
pluginTypedCSSModules(),
pluginSvgr(),
pluginImageCompress(),
],
dev: {
lazyCompilation: true,
assetPrefix: '/sdream-ai',
// 与本地开发有关的选项
},
......@@ -55,6 +58,8 @@ export default defineConfig({
},
},
output: {
target: 'web',
polyfill: 'usage',
assetPrefix: '/sdream-ai',
// 与构建产物有关的选项
distPath: {
......@@ -67,6 +72,10 @@ export default defineConfig({
cssModules: {
localIdentName: isProd ? '[hash:base64]' : '[local]',
},
dataUriLimit: {
image: 5000,
media: 0,
},
},
source: {
// 与源代码解析、编译方式相关的选项
......@@ -89,6 +98,17 @@ export default defineConfig({
},
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: {
// 为每个环境定义不同的 Rsbuild 配置
......
......@@ -112,6 +112,8 @@ const ChatEditorBase: React.FC<ChatEditorProps & WithAuthProps> = ({ checkAuth,
<div
ref={editorRef}
contentEditable
enterKeyHint="send"
role="textbox"
translate="no"
className="w-full min-h-[40px] max-h-[200px] p-2 rounded overflow-y-auto outline-none"
onInput={handleInput}
......@@ -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">
<SendIcon />
</Button>
......
......@@ -9,7 +9,7 @@ export const envConf = {
},
prod: {
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'
import { motion } from 'framer-motion'
import { useEffect, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { useSessionStorageState } from 'ahooks'
import { Navbar } from '../Navbar'
import { HistoryBar } from '../HistoryBar/HistoryBar'
import styles from './MainLayout.module.less'
......@@ -40,10 +41,16 @@ const contentVariants = {
export const MainLayout: React.FC<MainLayoutProps> = ({ children }) => {
const { showLoginModal, toggleLoginModal } = useAuth()
const [isHistoryVisible, setHistoryVisible] = useState(false)
const [isNavBarVisible, setNavBarvisible] = useState(true)
const location = useLocation()
const dispatch = useAppDispatch()
const token = window.localStorage.getItem('__TOKEN__')
const [navBarVisibleLocal] = useSessionStorageState<string | undefined>(
'__NAV_BAR_VISIBLE_LOCAL__',
{
defaultValue: '0',
listenStorageChange: true,
},
)
useEffect(() => {
if (token) {
......@@ -61,7 +68,7 @@ export const MainLayout: React.FC<MainLayoutProps> = ({ children }) => {
<motion.main className={styles.layoutMain}>
{/* hidden */}
<motion.div
animate={isNavBarVisible ? isHistoryVisible ? 'shrunk' : 'expanded' : 'navTween'}
animate={navBarVisibleLocal === '0' ? isHistoryVisible ? 'shrunk' : 'expanded' : 'navTween'}
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`}
>
......@@ -69,8 +76,6 @@ export const MainLayout: React.FC<MainLayoutProps> = ({ children }) => {
<Navbar
isHistoryVisible={isHistoryVisible}
onSetHistoryVisible={setHistoryVisible}
isNavBarVisible={isNavBarVisible}
onSetNavBarVisible={setNavBarvisible}
/>
<HistoryBar isVisible={isHistoryVisible} onSetHistoryVisible={setHistoryVisible} />
......@@ -92,7 +97,7 @@ export const MainLayout: React.FC<MainLayoutProps> = ({ children }) => {
</motion.div>
<motion.div
variants={contentVariants}
animate={isNavBarVisible ? '' : 'mainTween'}
animate={navBarVisibleLocal === '0' ? '' : 'mainTween'}
className={`${styles.layoutContent}`}
>
{children}
......
......@@ -40,13 +40,13 @@
&:hover {
&::before {
background-color: #29B6FD;
border-radius: 10px !important;
transform: rotate(-17deg) scaleY(1.2) !important;
border-radius: 10px;
transform: rotate(-17deg) scaleY(1.2);
}
&::after {
background-color: #29B6FD;
border-radius: 10px !important;
transform: rotate(17deg) scaleY(1.2) !important;
border-radius: 10px;
transform: rotate(17deg) scaleY(1.2);
}
}
&::before {
......
......@@ -2,7 +2,7 @@ import type React from 'react'
import { motion } from 'framer-motion'
import { useEffect, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useClickAway } from 'ahooks'
import { useClickAway, useSessionStorageState } from 'ahooks'
import styles from './Navbar.module.less'
import { NavBarItem } from './components/NavBarItem'
import { User } from './components/User'
......@@ -18,11 +18,9 @@ import JoyrideStep from '@/components/JoyrideStep'
interface NavbarProps {
isHistoryVisible: boolean
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 navigate = useNavigate()
......@@ -75,8 +73,16 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c
navigate('/')
}
const [navBarVisibleLocal, setNavBarVisibleLocal] = useSessionStorageState<string | undefined>(
'__NAV_BAR_VISIBLE_LOCAL__',
{
defaultValue: '0',
listenStorageChange: true,
},
)
const toggleNavBarVisible = () => {
onSetNavBarVisible(!isNavBarVisible)
setNavBarVisibleLocal(navBarVisibleLocal === '1' ? '0' : '1')
}
useEffect(() => {
......@@ -95,7 +101,7 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c
<>
<motion.nav
ref={navRef}
animate={isNavBarVisible ? '' : 'hidden'}
animate={navBarVisibleLocal === '0' ? 'hidden' : ''}
className="h-full flex-shrink-0 flex flex-col items-center justify-center relative"
>
{/* hidden */}
......@@ -109,11 +115,10 @@ const NavbarBase: React.FC<NavbarProps & WithAuthProps> = ({ isHistoryVisible, c
})}
<User onLogout={handleLogout} />
</motion.div>
<div onClick={toggleNavBarVisible} className={`${styles.sidebarAction} ${isNavBarVisible ? 'open' : ''}`}></div>
<div onClick={toggleNavBarVisible} className={`${styles.sidebarAction} ${navBarVisibleLocal === '0' ? styles.open : ''}`}></div>
</motion.nav>
<JoyrideStep />
{/* <JoyrideStep /> */}
</>
)
}
......
import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger, Tooltip } from '@nextui-org/react'
import { useNavigate } from 'react-router-dom'
import { useSessionStorageState, useTimeout } from 'ahooks'
import { useEffect, useState } from 'react'
import { withAuth } from '@/auth/withAuth'
import type { WithAuthProps } from '@/auth/withAuth'
import UserIcon from '@/assets/svg/user.svg?react'
......@@ -65,8 +67,30 @@ export const UserLogin: React.FC<UserProps> = ({ onLogout }) => {
}
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 (
<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">
<UserIcon />
</Button>
......
......@@ -60,9 +60,9 @@ export const Home: React.FC = () => {
<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 sm:w-auto" {...getAnimationProps(3)}><QuestionList questions={RECOMMEND_QUESTIONS_OTHER} dotColor="#CBECFF" title="您可以试着问我" iconImg={HomeIcon2} /></motion.div>
</div>
......
......@@ -38,7 +38,7 @@ const BotEye: React.FC = () => {
const BotAnimateBox: React.FC = () => {
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="" />
<motion.img
animate={{ y: '-4px', transition: { duration: 0.5, repeat: Infinity, repeatType: 'reverse', ease: 'easeInOut' } }}
......@@ -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]">
<img className="absolute z-[-1] top-0 left-0" src={BotBgImg} alt="" />
<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 />
<p className="text-[#27353C] text-[15px] mt-[24px] mb-[16px]">做为您的智能保险伙伴,您有各类专业相关的问题都可以抛给我哟~让我们互相帮助共同成长吧~</p>
<Button className="bg-white text-[#20ABD9] font-medium rounded-[20px]" onClick={handleGo}>立即前往 ➔</Button>
<p className="mt-[16px] text-[#27353C] text-[15px] sm:mt-[24px] mb-[16px]">做为您的智能保险伙伴,您有各类专业相关的问题都可以抛给我哟~让我们互相帮助共同成长吧~</p>
<Button className="bg-white w-full text-[#20ABD9] font-medium rounded-[20px] sm:w-auto" onClick={handleGo}>立即前往 ➔</Button>
</div>
</div>
)
......
import { Virtuoso } from 'react-virtuoso'
import { motion } from 'framer-motion'
import styles from './Tools.module.less'
import { Slogan } from './components/Slogan/Slogan'
......@@ -53,35 +52,29 @@ export const Tools: React.FC = () => {
<div className="h-full w-full">
<div className="box flex flex-col h-full w-full">
<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">
<Slogan />
<div>
<div className="max-w-[912px] flex mx-auto mt-[64px] text-18px text-[#5AA9D0] font-light">全部工具</div>
<div className="max-w-[912px] mx-auto gap-[20px] flex flex-col flex-wrap mt-[22px] sm:flex-row">
{tools.map((item, index) => (
<motion.div className="flex-1" key={index} onClick={handleClickToolItem} {...getAnimationProps(index + 1)}>
<ShineBorder
borderRadius={20}
className="rounded-[20px] bg-[#fff] flex-1 h-[100px] p-[24px] cursor-pointer"
innerClassName="flex items-center"
>
<img className="w-[52px]" src={item.icon} alt="" />
<div className="ml-[20px]">
<h3 className="font-bold">{item.name}</h3>
<p className="text-[14px] text-[#27353CCC] mt-[3px]">{item.desc}</p>
</div>
</ShineBorder>
</motion.div>
)) }
</div>
</div>
<div className="px-[24px] pb-[24px] pt-[42px] sm:pt-[80px] lg:pt-[180px] sm:px-0">
<Slogan />
<div>
<div className="max-w-[912px] flex mx-auto mt-[64px] text-18px text-[#5AA9D0] font-light">全部工具</div>
<div className="max-w-[912px] mx-auto gap-[20px] flex flex-col flex-wrap mt-[22px] sm:flex-row">
{tools.map((item, index) => (
<motion.div className="flex-1" key={index} onClick={handleClickToolItem} {...getAnimationProps(index + 1)}>
<ShineBorder
borderRadius={20}
className="rounded-[20px] bg-[#fff] flex-1 h-[100px] p-[24px] cursor-pointer"
innerClassName="flex items-center"
>
<img className="w-[52px]" src={item.icon} alt="" />
<div className="ml-[20px]">
<h3 className="font-bold">{item.name}</h3>
<p className="text-[14px] text-[#27353CCC] mt-[3px]">{item.desc}</p>
</div>
</ShineBorder>
</motion.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