Commit 2f7f1e56 by lizhilin

更新

parents
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.\
You will also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "chrisdai",
"version": "0.1.0",
"private": true,
"dependencies": {
"@ant-design/icons": "^5.3.5",
"@reduxjs/toolkit": "^2.2.2",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.91",
"@types/react": "^18.2.71",
"@types/react-dom": "^18.2.22",
"antd": "^5.15.4",
"axios": "^1.6.8",
"http-proxy-middleware": "^2.0.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^9.1.0",
"react-router-dom": "^6.22.3",
"react-scripts": "5.0.1",
"redux": "^5.0.1",
"redux-persist": "^6.0.0",
"sass": "^1.72.0",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
],
"rules": {
"@typescript-eslint/no-unused-vars": "off"
}
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/redux-persist": "^4.3.1"
}
}
<!DOCTYPE html>
<html lang="en" id="html">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="keywords" content="Christine Dai艺术珠宝 上海嘉柏润企业管理有限公司">
<meta name="description" content="Christine Dai艺术珠宝 上海嘉柏润企业管理有限公司"/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/favicon.ico" />
<script>
var oHtml = document.getElementById('html');
function resize () {
var clientW = document.documentElement.clientWidth;
if (clientW > 750) {
clientW = 750;
}
oHtml.style.fontSize = clientW / 750 * 100 + 'px';
}
resize();
window.onresize = function () {resize();};
</script>
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Christine Dai 艺术珠宝</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
{
"short_name": "ChrisDai",
"name": "Christine Dai",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
@font-face{
font-family: 'Baskerville'; /* 重命名字体名 */
src: url('./Baskerville.ttc');
font-weight: normal;
font-style: normal;
}
\ No newline at end of file
.ReactNode{
width: 100vw;
height: 100vh;
background-color: #000;
display: flex;
justify-content: center;
align-items: center;
}
.ReactImg{
width: 300px;
height: 75px;
}
\ No newline at end of file
import React,{Suspense} from 'react'
import { useLocation, matchRoutes, Navigate } from 'react-router-dom'
import { routes } from '../../router'
import styles from './BeforeEach.module.scss'
interface BeforeEachProps {
children?: React.ReactNode
}
export default function BeforeEach(props: BeforeEachProps) {
const location = useLocation()
const matchs = matchRoutes(routes, location)
// if (Array.isArray(matchs)) {
// const meta = matchs[matchs.length - 1].route.meta
// if(meta?.auth) {
// return <Navigate to="/"></Navigate>
// }
// }
const ReactNode = ()=>{
return (
<div className={styles.ReactNode}>
{/* <img className={styles.ReactImg} src={require('../../assets/images/header-logo.png')} alt="" /> */}
</div>
)
}
return (
<>
<Suspense fallback={ReactNode()}>
{
props.children
}
</Suspense>
</>
)
}
.footerBody{
width: 100%;
background-color: #000;
}
.footer-top{
padding: .5rem .4rem;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #666666;
.footer-nav{
width: 74.5%;
}
.footer-nav ul{
display: flex;
justify-content: space-between;
align-items: center;
font-size: .18rem;
color: #666666;
li{
font-family: "宋体",'宋体-简',"常规体";
cursor: pointer;
}
li:hover{
color: #fff;
}
}
.footer-soc{
width: 19%;
height: 0.20rem;
border-left: 1px solid #666;
display: flex;
justify-content: center;
align-items: center;
.footer-wx{
display: flex;
align-items: center;
position: relative;
padding: 0.1rem 0;
cursor: pointer;
.footer-ewm{
display: none;
position: absolute;
left: 50%;
bottom: 30px;
width: 140px;
padding: 10px;
background-color: #fff;
transform: translateX(-50%);
box-sizing: border-box;
z-index: 3;
img{
margin: 0;
width: 100%;
height: 100%;
border: 0;
vertical-align: middle;
}
}
}
.footer-wx:hover{
.footer-ewm{
display: block;
}
}
img {
height: 0.15rem;
margin: 0 0.08rem;
cursor: pointer;
}
}
}
.footer-copy{
font-size: 0.12rem;
height: .8rem;
display: flex;
text-align: center;
justify-content: center;
align-items: center;
color: #fff;
background-color: #000;
font-family: "宋体",'宋体-简',"常规体";
.footer-line{
margin: 0 0.05rem;
}
}
.footer-copy2{
font-size: 0.12rem;
font-family: "宋体",'宋体-简',"常规体";
cursor: pointer;
}
@media screen and (max-width: 750px) {
.footer-top{
padding:0.4rem;
.footer-nav{
display: none;
}
.footer-soc{
width: 100%;
height: 0.28rem;
padding: 0 4%;
box-sizing: border-box;
display: flex;
justify-content:space-between;
border: none;
img {
height: 0.28rem;
margin: 0 0.08rem;
cursor: pointer;
}
}
}
}
import React, { useEffect,useState }from 'react'
import { useSelector } from 'react-redux'
import styles from './Footer.module.scss'
import { useNavigate } from 'react-router-dom';
import type { RootState } from '../../store'
import {HomeKey,getCachedObject} from '../../utils/auth'
export default function Footer() {
interface RuleData {
bg_video: string
logo_img: string
placard: string
weibo:string
xiaohongshu:string
ins:string
wxqr:string
}
const [footerData, setFooterData] = useState<RuleData>({
bg_video: '',
logo_img: '',
placard: '',
weibo:'',
xiaohongshu:'',
ins:'',
wxqr:''
})
useEffect(() => {
if(getCachedObject(HomeKey)){
const footerObject = getCachedObject(HomeKey);
setFooterData(footerObject)
}
}, [])
const toUrl = (url:string) => {
// 使用 navigate() 方法进行路由跳转
window.open(url)
};
const toLink = (url:string) => {
// 使用 navigate() 方法进行路由跳转
window.open(url)
};
const year = useSelector((state: RootState) => state.story.year)
const navigate = useNavigate();
const handleButtonClick = (url:string) => {
// 使用 navigate() 方法进行路由跳转
navigate(url);
};
return (
<div className={styles.footerBody}>
<div className={styles['footer-top']}>
<div className={styles['footer-nav']}>
<ul>
<li onClick={()=>handleButtonClick('/')}>首页</li>
<li onClick={()=>handleButtonClick('/story')}>品牌故事</li>
<li onClick={()=>handleButtonClick('/jewelry')}>艺术珠宝</li>
<li onClick={()=>handleButtonClick('/documentary')}>品牌纪事</li>
<li onClick={()=>handleButtonClick('/video')}>视频</li>
<li onClick={()=>handleButtonClick('/globalMedia')}>全球媒体</li>
<li onClick={()=>handleButtonClick('/contactUs')}>联系我们</li>
</ul>
</div>
<div className={styles['footer-soc']}>
<img onClick={(()=>toUrl(footerData.weibo))} src={require('../../assets/images/wb.png')} alt='微博'/>
<div className={styles['footer-wx']}>
<img src={require('../../assets/images/wx.png')} alt='微信'/>
<div className={styles['footer-ewm']}>
<img src={footerData.wxqr} alt="" />
</div>
</div>
<img onClick={(()=>toUrl(footerData.xiaohongshu))} src={require('../../assets/images/xhs.png')} alt='小红书'/>
<img onClick={(()=>toUrl(footerData.ins))} src={require('../../assets/images/instagram.png')} alt=''/>
</div>
</div>
<div className={styles['footer-copy']}>
<div onClick={()=>toLink('https://beian.miit.gov.cn/')} className={styles['footer-copy2']}>
<span>沪ICP备2022010059号-1</span>
<span className={styles['footer-line']}>|</span>
Copyright © {year} Chirstine Dai
</div>
</div>
</div>
)
}
\ No newline at end of file
.headerBody{
display: flex;
position: fixed;
width: 100%;
top: 0;
left: 0;
justify-content:space-between;
align-items: center;
transition: .3s;
z-index: 99;
box-sizing: border-box;
padding: 0.7rem 0.8rem 0.7rem 0.4rem;
height: 1rem;
}
.onFix{
background: #000;
}
.header-top{
display: flex;
align-items: center;
justify-content: center;
color: #fff;
}
.header-logo{
max-width: 1.62rem;
min-height: .35rem;
cursor: pointer;
margin-left: 0.4rem;
}
.header-return{
width: 0.29rem;
height: 0.29rem;
cursor: pointer;
}
.header-right{
display: flex;
justify-content: space-between;
align-items: center;
}
.header-name{
color: #fff;
// font-weight: bold;
font-size: .17rem;
line-height: .25rem;
font-family: Baskerville;
}
.header-nav{
position: relative;
}
.header-nav:hover{
position: relative;
.nav-ul{
display: block;
}
}
.header-nav::after{
display: block;
width: 100%;
height: 0.1rem;
content: "";
}
.nav-ul{
display: none;
position: absolute;
left: 0;
top: 0.35rem;
background: #fff;
width: 1rem;
padding: 0.1rem 0;
transition:all .7s;
li{
font-size: 0.16rem;
text-align: center;
line-height: 2;
color: #b0b0b0;
cursor: pointer;
}
li:hover {
color: #000;
}
}
.header-menu{
margin-left: .3rem;
width: .3rem;
height: .26rem;
background-image: url(../../assets/images/menu.png);
background-repeat: no-repeat;
background-size: contain;
background-position: center center;
transition: .3s;
cursor: pointer;
&:hover {
background-image: url(../../assets/images/menu_a.png);
}
}
.header-menu2{
margin-left: .3rem;
width: .3rem;
height: .26rem;
background-image: url(../../assets/images/menu2.png);
background-repeat: no-repeat;
background-size: contain;
background-position: center center;
transition: .3s;
cursor: pointer;
}
// .header-menu:hover{
// background-image: url(../../assets/images/menu_a.png);
// }
.retBtn{
color: #fff;
position: relative;
padding: .03rem 0;
color: #fff;
font-size: 0.14rem;
line-height: 1.42857143;
font-family: Baskerville;
cursor: pointer;
:hover {
color: #fff;
}
&:before {
-webkit-transform: scaleX(0);
-ms-transform: scaleX(0);
transform: scaleX(0);
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
-webkit-transition-delay: 0s;
transition-delay: 0s;
}
&:hover:before {
-webkit-transform: scaleX(1);
-ms-transform: scaleX(1);
transform: scaleX(1);
-webkit-transition-delay: .3s;
transition-delay: .3s;
}
&:after{
-webkit-transform: scaleX(1);
-ms-transform: scaleX(1);
transform: scaleX(1);
-webkit-transform-origin: 100% 0;
-ms-transform-origin: 100% 0;
transform-origin: 100% 0;
-webkit-transition-delay: .3s;
transition-delay: .3s;
}
&:hover:after {
-webkit-transform: scaleX(0);
-ms-transform: scaleX(0);
transform: scaleX(0);
-webkit-transition-delay: 0s;
transition-delay: 0s;
}
&:before, &:after {
height: 1px;
width: 100%;
position: absolute;
background-color: #fff;
bottom: -3px;
left: 0;
content: "";
-webkit-transition: -webkit-transform .3s cubic-bezier(.39,.575,.565,1);
transition: -webkit-transform .3s cubic-bezier(.39,.575,.565,1);
transition: transform .3s cubic-bezier(.39,.575,.565,1);
transition: transform .3s cubic-bezier(.39,.575,.565,1),-webkit-transform .3s cubic-bezier(.39,.575,.565,1);
}
}
.retBtn::after,
.retBtn::before {
height: 1px;
width: 100%;
position: absolute;
background-color: #fff;
bottom: -3px;
left: 0;
content: "";
-webkit-transition: -webkit-transform .3s cubic-bezier(.39,.575,.565,1);
transition: -webkit-transform .3s cubic-bezier(.39,.575,.565,1);
transition: transform .3s cubic-bezier(.39,.575,.565,1);
transition: transform .3s cubic-bezier(.39,.575,.565,1), -webkit-transform .3s cubic-bezier(.39,.575,.565,1);
}
@media screen and (max-width: 750px) {
.header-menu{
width: .4rem;
height: .4rem;
}
.retBtn{
color: #fff;
position: relative;
padding: .03rem 0;
color: #fff;
font-size: 0.20rem;
line-height: 1.42857143;
cursor: pointer;
}
}
\ No newline at end of file
import React,{ useState, useEffect }from 'react'
import styles from './Header.module.scss'
import { useNavigate } from 'react-router-dom';
import { MyContext } from '../../views/Layout/Layout'
interface HeaderProps {
type?:Boolean,
go?:Boolean,
titleObj?:{
title:string,
name:string
}
}
interface RuleContext {
[key: string]: unknown
myMethod: () => void
}
export default function Hrader(props:HeaderProps) {
const [isScrolled, setIsScrolled] = useState(false);
const navigate = useNavigate();
const context = React.useContext(MyContext) as RuleContext;
const handleButtonClick = (url:string) => {
// 使用 navigate() 方法进行路由跳转
navigate(url);
// window.location.reload();
};
const goback = () => {
navigate(-1);
}
const BackUrl = ()=>{
navigate(-1);
}
useEffect(() => {
const handleScroll = () => {
const scrollTop = document.documentElement.scrollTop;
setIsScrolled(scrollTop > 50); // 设置滚动高度的阈值
};
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
let { type,titleObj,go} = props;
return (
<div className={(type&&!isScrolled)||!isScrolled?`${styles.headerBody}`:`${styles.headerBody} ${styles.onFix}`}>
<div className={styles['header-top']} >
<img onClick={()=>handleButtonClick('/')} className={styles['header-return']} src={require('../../assets/images/return.png')} alt="" />
<img onClick={()=>handleButtonClick('/')} className={styles['header-logo']} src={require('../../assets/images/header-logo.png')} alt="" />
</div>
<div className={styles['header-right']}>
{go?<div onClick={()=>BackUrl()} className={styles.retBtn}>
返回上一页
<br/>
Return
</div>:''}
<div className={styles['header-nav']}>
<p onClick={goback} className={styles['header-menu']}></p>
</div>
</div>
</div>
)
}
\ No newline at end of file
.homeBody {
height: 100vh;
width: 100vw;
background-color: #000;
.bgVideo {
width: 100%;
height: 100vh;
top: 0;
left: 0;
-o-object-fit: cover;
object-fit: cover;
opacity: .5;
position: absolute;
}
}
.home-container {
position: absolute;
width: 100vw;
height: 100vh;
z-index: 10;
.home-content {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
.home-inner {
display: flex;
align-items: center;
.intro-left {
margin-right: 2.3rem;
img {
width: 5rem;
max-height: 2rem;
}
.intro-head {
display: flex;
justify-content: center;
align-items: center;
font-size: 0.14rem;
color: #8d7249;
margin-top: 0.3rem;
p:nth-child(1){
margin: 0 0.2rem;
font-size: 0.34rem;
line-height: 1.5;
display: flex;
justify-content: space-around;
}
p:nth-child(2){
font-size: 0.14rem;
margin-bottom: 0.2rem;
}
span {
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
margin: 0 0.2rem;
font-family: "宋体",'宋体-简',"常规体";
b {
font-family: "宋体",'宋体-简',"常规体";
font-size: 0.34rem;
margin-bottom: 0.1rem;
}
}
}
}
.intro-right {
div {
cursor: pointer;
display: flex;
flex-direction: column;
text-align: center;
font-size: 0.24rem;
margin-bottom: 0.2rem;
b {
transition: opacity .7s;
font-family: "宋体",'宋体-简',"常规体";
color: #fff;
margin-bottom: 0.08rem;
min-width: 1.28rem;
font-size: 0.32rem;
}
i {
transition: opacity .7s;
font-family: Baskerville;
color: #8d7249;
font-size: 0.12rem;
}
}
.right-show{
b{
opacity: 1;
}
}
.right-hide{
b{
opacity: .3;
}
i{
opacity: .3;
}
}
}
}
}
.home-bottom {
position: absolute;
width: 100%;
bottom: 0.3rem;
color: #fff;
box-sizing: border-box;
font-size: 0.12rem;
padding: 0 0.5rem;
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: row-reverse;
.home-wx{
display: inline-block;
position: relative;
padding: 0.1rem 0;
cursor: pointer;
.home-ewm{
display: none;
position: absolute;
left: 50%;
bottom: 30px;
width: 140px;
padding: 10px;
background-color: #fff;
transform: translateX(-50%);
box-sizing: border-box;
z-index: 3;
img{
margin: 0;
width: 100%;
height: 100%;
border: 0;
vertical-align: middle;
}
}
}
.home-wx:hover{
.home-ewm{
display: block;
}
}
.shortlink {
img {
height: 0.12rem;
margin: 0 0.08rem;
cursor: pointer;
}
}
.home-span{
cursor: pointer;
}
}
}
@media screen and (max-width: 750px) {
.home-container {
.home-content {
.home-inner {
display: block;
.intro-left {
margin-right: 0;
margin-bottom: 0.5rem;
}
.intro-right {
div {
font-size: 0.38rem;
i {
font-size: 0.18rem;
}
}
}
}
}
.home-bottom {
display: block;
text-align: center;
span {
font-size: 0.18rem;
}
.shortlink {
margin-bottom: 0.2rem;
img {
height: 0.30rem;
margin: 0 0.2rem;
}
}
}
}
}
@media screen and (min-width: 750px) and (max-width: 768px) {
.home-container {
.home-content {
.home-inner {
.intro-left {
margin-right: 1.2rem;
}
}
}
}
}
\ No newline at end of file
import React, { useEffect, useState } from 'react'
import styles from './Nav.module.scss'
import { useSelector } from 'react-redux'
import { getHomeInfos } from '../../store/modules/home'
import { useAppDispatch } from '../../store'
import type { RootState } from '../../store'
import { useNavigate } from 'react-router-dom';
import {HomeKey,cacheObject,getCachedObject} from '../../utils/auth'
interface NavProps {
onChange:Function
}
export default function Nav(props:NavProps) {
interface RuleData {
bg_video: string
logo_img: string
placard: string
weibo:string
xiaohongshu:string
ins:string
wxqr:string
}
const year = useSelector((state: RootState) => state.story.year)
const dispatch = useAppDispatch()
const navigate = useNavigate();
const [homeData, setHomeData] = useState<RuleData>({
bg_video: '',
logo_img: '',
placard: '',
weibo:'',
xiaohongshu:'',
ins:'',
wxqr:''
})
const [hoveredIndex, setHoveredIndex] = useState<null|number>(null);
const handleMouseEnter = (index:number) => {
setHoveredIndex(index);
};
const handleMouseLeave = () => {
setHoveredIndex(null);
};
const closeOpen = ()=>{
// 修改父组件的open值为false
props.onChange(false);
}
const handleButtonClick = (url:string) => {
// 使用 navigate() 方法进行路由跳转
navigate(url);
// window.location.reload();
closeOpen()
};
const toLink = (url:string) => {
// 使用 navigate() 方法进行路由跳转
window.open(url)
};
useEffect(() => {
if(getCachedObject(HomeKey)){
const homeObject = getCachedObject(HomeKey);
setHomeData(homeObject)
}
getHomeInfos().then(res => {
if (res.data.code === 200) {
const { bg_video, placard, logo_img,weibo,xiaohongshu,ins,wxqr} = res.data.data
cacheObject(HomeKey,{
bg_video, logo_img, placard,weibo,xiaohongshu,ins,wxqr
})
// 获取缓存的对象
const homeObject = getCachedObject(HomeKey);
setHomeData(homeObject)
}
})
}, [])
return (
<div className={styles.homeBody}>
<video src={homeData.bg_video} className={styles.bgVideo} autoPlay loop muted>
<source type="video/mp4"/>
您的浏览器不支持视频标签。
</video>
<div className={styles['home-container']}>
<div className={styles['home-content']}>
<div className={styles['home-inner']}>
<div className={styles['intro-left']}>
<div className={styles['intro-logo']}><img src={homeData.logo_img} alt=""/></div>
<div className={styles['intro-head']}>
<span>
<b>光影感</b>
<i>LUMINOSITY</i>
</span>
<span>
<b>雕塑感</b>
<i>SCULPTURE</i>
</span>
<span>
<b>⽣命⼒</b>
<i>VITALITY</i>
</span>
</div>
</div>
<div className={styles['intro-right']}>
<div
className={ hoveredIndex !== 1&&hoveredIndex !== null?styles['right-hide']:''}
onMouseEnter={() => handleMouseEnter(1)}
onMouseLeave={() => handleMouseLeave()}
onClick={()=>handleButtonClick('/story')}
><b>品牌故事</b><i>BRAND STORY</i></div>
<div
className={ hoveredIndex !== 2&&hoveredIndex !== null?styles['right-hide']:''}
onMouseEnter={() => handleMouseEnter(2)}
onMouseLeave={() => handleMouseLeave()}
onClick={()=>handleButtonClick('/jewelry')}
><b>艺术珠宝</b><i>ART JEWEL</i></div>
<div
className={ hoveredIndex !== 3&&hoveredIndex !== null?styles['right-hide']:''}
onMouseEnter={() => handleMouseEnter(3)}
onMouseLeave={() => handleMouseLeave()}
onClick={()=>handleButtonClick('/documentary')}
><b>品牌纪事</b><i>BRAND HERITAGE</i></div>
<div
className={ hoveredIndex !== 4&&hoveredIndex !== null?styles['right-hide']:''}
onMouseEnter={() => handleMouseEnter(4)}
onMouseLeave={() => handleMouseLeave()}
onClick={()=>handleButtonClick('/video')}
><b>视频</b><i>VIDEO</i></div>
<div
className={ hoveredIndex !== 5&&hoveredIndex !== null?styles['right-hide']:''}
onMouseEnter={() => handleMouseEnter(5)}
onMouseLeave={() => handleMouseLeave()}
onClick={()=>handleButtonClick('/globalMedia')}
><b>全球媒体</b><i>GLOBAL EVENTS</i></div>
<div
className={ hoveredIndex !== 6&&hoveredIndex !== null?styles['right-hide']:''}
onMouseEnter={() => handleMouseEnter(6)}
onMouseLeave={() => handleMouseLeave()}
onClick={()=>handleButtonClick('/contactUs')}
><b>联系我们</b><i>CONTACT US</i></div>
</div>
</div>
</div>
<div className={styles['home-bottom']}>
<div className={styles.shortlink}>
<a href={homeData.weibo} target="_blank" rel="noreferrer">
<img src={require('../../assets/images/wb.png')} alt='微博'/>
</a>
<div className={styles['home-wx']}>
<img src={require('../../assets/images/wx.png')} alt='微信'/>
<div className={styles['home-ewm']}>
<img src={homeData.wxqr} alt="" />
</div>
</div>
<a href={homeData.xiaohongshu} target="_blank" rel="noreferrer">
<img src={require('../../assets/images/xhs.png')} alt='小红书'/>
</a>
<a href={homeData.ins} target="_blank" rel="noreferrer">
<img src={require('../../assets/images/instagram.png')} alt=''/>
</a>
</div>
<span onClick={()=>toLink('https://beian.miit.gov.cn/')} className={styles['home-span']}>沪ICP备2022010059号-1 | Copyright © {year} Chirstine Dai</span>
</div>
</div>
</div>
)
}
\ No newline at end of file
.slideInFrom {
opacity: 0;
margin-top: 200px;
transition: 0.5s;
}
.visible {
opacity: 1;
margin-top: 0;
}
\ No newline at end of file
import React, { ReactNode } from 'react';
import styles from './SlideInFromBottom.module.scss'
interface SlideInFromBottomProps {
children:ReactNode,
isVisible: boolean;
}
const SlideInFromBottom: React.FC<SlideInFromBottomProps> = ({ children, isVisible }) => {
return (
<div>
<div className={isVisible?`${styles.slideInFrom} ${styles.visible}`:`${styles.slideInFrom}`}>{children}</div>
</div>
);
};
export default SlideInFromBottom;
\ No newline at end of file
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
@font-face{
font-family: 'Baskerville'; /* 重命名字体名 */
src: url('./assets/styles/Baskerville.ttc');
font-weight: normal;
font-style: normal;
}
import React, { Suspense } from 'react';
import ReactDOM from 'react-dom/client';
import '../src/assets/styles/reset.scss'
import '../src/assets/styles/common.scss'
import reportWebVitals from './reportWebVitals';
import { RouterProvider } from 'react-router-dom';
import router from './router'
import { Provider } from 'react-redux'
import store from './store'
import { StyleProvider } from '@ant-design/cssinjs'
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<Suspense>
<Provider store={store}>
<StyleProvider hashPriority="high">
<RouterProvider router={router}></RouterProvider>
</StyleProvider>
</Provider>
</Suspense>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
\ No newline at end of file
/// <reference types="react-scripts" />
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;
import { createHashRouter, createBrowserRouter } from 'react-router-dom'
import type { RouteObject } from 'react-router-dom'
import React, { lazy } from 'react'
const Story = lazy(() => import('../views/Story/Story'))
const Jewelry = lazy(() => import('../views/Jewelry/Jewelry'))
const Documentary = lazy(() => import('../views/Documentary/Documentary'))
const Video = lazy(() => import('../views/Video/Video'))
const GlobalMedia = lazy(() => import('../views/GlobalMedia/GlobalMedia'))
const ContactUs = lazy(() => import('../views/ContactUs/ContactUs'))
const TheJewelry = lazy(() => import('../views/TheJewelry/TheJewelry'))
const JewelryList = lazy(() => import('../views/JewelryList/JewelryList'))
const JewelryDetail = lazy(() => import('../views/JewelryDetail/JewelryDetail'))
const WholeVideo = lazy(() => import('../views/WholeVideo/WholeVideo'))
const MediaDetail = lazy(() => import('../views/MediaDetail/MediaDetail'))
const VideoDetail = lazy(() => import('../views/VideoDetail/VideoDetail'))
const Layout = lazy(() => import('../views/Layout/Layout'))
const BeforeEach = lazy(() => import('../components/BeforeEach/BeforeEach'))
declare module 'react-router-dom' {
interface IndexRouteObject {
meta?: {
menu?: boolean
title?: string
icon?: React.ReactNode
auth?: boolean
}
}
interface NonIndexRouteObject {
meta?: {
menu?: boolean // 是否显示当前菜单
title?: string // 菜单名称
icon?: React.ReactNode // 菜单对应小图标
auth?: boolean // 菜单是否拥有权限
}
}
}
export const routes: RouteObject[] = [
{
path: '/',
element: React.createElement(BeforeEach, null, React.createElement(Layout)),
children: [
{
path: 'story',
element: React.createElement(Story),
meta: {
menu: true,
title: '品牌故事',
auth: true
}
},
{
path: 'jewelry',
element: React.createElement(Jewelry),
meta: {
menu: true,
title: '艺术珠宝'
}
},
{
path: 'documentary',
element: React.createElement(Documentary),
meta: {
menu: true,
title: '品牌纪事'
}
},
{
path: 'video',
element: React.createElement(Video),
meta: {
menu: true,
title: '视频'
}
},
{
path: 'globalMedia',
element: React.createElement(GlobalMedia),
meta: {
menu: true,
title: '全球媒体'
}
},
{
path: 'contactUs',
element: React.createElement(ContactUs),
meta: {
title: '联系我们'
}
},
{
path: 'theJewelry/:id',
element: React.createElement(TheJewelry),
meta: {
title: '珠宝分类'
}
},
{
path: 'jewelryList/:id',
element: React.createElement(JewelryList),
meta: {
title: '珠宝列表'
}
},
{
path: 'jewelryDetail',
element: React.createElement(JewelryDetail),
meta: {
title: '珠宝详情'
}
},
{
path: 'wholeVideo/:id',
element: React.createElement(WholeVideo),
meta: {
title: '视频分类'
}
},
{
path: 'mediaDetail',
element: React.createElement(MediaDetail),
meta: {
title: '媒体详情'
}
},
{
path: 'videoDetail',
element: React.createElement(VideoDetail),
meta: {
title: '视频详情'
}
}
]
}
]
const router = createBrowserRouter(routes)
export default router
\ No newline at end of file
const { createProxyMiddleware: proxy } = require('http-proxy-middleware')
module.exports = function (app) {
app.use(
proxy('/api', {
// target: 'http://jbrpc.yyinhong.cn/api',
target: 'http://web-api.chris-dai.com/api',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
})
)
}
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
import { configureStore } from '@reduxjs/toolkit'
import storyReducer from './modules/story'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import { useDispatch } from 'react-redux'
import type { RuleStory } from './modules/story'
import type { PersistPartial } from 'redux-persist/es/persistReducer'
import type { Reducer, UnknownAction } from '@reduxjs/toolkit'
const persistConfig = {
key: 'root',
storage,
whiteList: ['infos']
}
const store = configureStore({
reducer: {
story: persistReducer(persistConfig, storyReducer) as Reducer<RuleStory & PersistPartial, UnknownAction>
},
middleware: (buildGetDefaultMiddleware) =>
buildGetDefaultMiddleware({
serializableCheck: false
})
})
persistStore(store)
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export const useAppDispatch: () => AppDispatch = useDispatch
export default store
import http from '../../utils/http'
export type ruleData = {
province: string,
name: string,
email: string,
phone: string,
msg: string
}
export const getContactUs = () => {
return http.get('/contact-us', {})
}
export const getMessage = (data: ruleData) => {
return http.post('/message', data)
}
export const getCityList = () => {
return http.post('/get-city', {})
}
\ No newline at end of file
import { createAsyncThunk } from '@reduxjs/toolkit'
import http from '../../utils/http'
export const getHomeInfos = () => {
return http.get('/get-index-setting', {})
}
export const getBrandInfos = () => {
return http.get('/brand-info', {})
}
export const getBrandEvent = () => {
return http.get('/brand-event', {})
}
import http from '../../utils/http'
export const getProductCat = (pid:string) => {
let obj = {
pid
}
return http.get('/product-cat', obj)
}
export const getProductList = (cat_id:string) => {
let obj = {
cat_id
}
return http.get('/product-list', obj)
}
export const getProductInfo = (itemid:string) => {
let obj = {
itemid
}
return http.get('/product-info', obj)
}
\ No newline at end of file
import http from '../../utils/http'
export const getMediaList = () => {
return http.get('/media-list', {})
}
\ No newline at end of file
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
import http from '../../utils/http'
type title = string
type year = string
type Infos = {
[key: string]: unknown
}
export type RuleStory = {
title: title
infos: Infos
year: year
}
type InfosParams = {
id: string | number
}
const storySlice = createSlice({
name: 'story',
initialState: {
title: '',
year: new Date().getFullYear().toString(),
infos: {}
} as RuleStory,
reducers: {
setTitle(state, action: PayloadAction<title>) {
state.title = action.payload
},
updateInfos(state, action: PayloadAction<Infos>) {
state.infos = action.payload
},
clearTitle(state) {
state.title = ''
}
}
})
export const getStoryInfos = createAsyncThunk('story/getInfos', async (payload: InfosParams) => {
const ret = await http.get('/captchaImage')
return ret
})
export const { setTitle, updateInfos, clearTitle } = storySlice.actions
export default storySlice.reducer
import http from '../../utils/http'
type InfosParams = {
id: string | number | undefined
}
export const getVideoCat = () => {
return http.get('/video-cat', {})
}
export const getVideoList = (data: InfosParams) => {
return http.get('/video-list', data)
}
export const getVideoEventList = () => {
return http.get('/video-event',{})
}
\ No newline at end of file
// 示例使用
const HomeKey = 'myHomeKey';
// 缓存对象到localStorage
function cacheObject(key:string, object:object) {
localStorage.setItem(key, JSON.stringify(object));
}
// 从localStorage读取缓存的对象
function getCachedObject(key:string) {
const objectString = localStorage.getItem(key);
if (objectString) {
return JSON.parse(objectString);
}
return null;
}
export {
HomeKey,
cacheObject,
getCachedObject
}
\ No newline at end of file
import axios from 'axios'
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
declare module 'axios' {
interface AxiosResponse<T = any> {
code?: string | number
message?: string
}
export function create(config?: AxiosRequestConfig): AxiosInstance
}
let baseURL = process.env.NODE_ENV === 'development' ? '/api' : 'http://web-api.chris-dai.com/api'
const instance = axios.create({
baseURL,
timeout: 60000
})
instance.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bear ${token}`
}
return config
},
(error) => {
return Promise.reject(error)
}
)
instance.interceptors.response.use(
(response) => {
return response
},
(error) => {
return Promise.reject(error)
}
)
interface Data {
[key: string]: unknown
}
interface Http {
get: (url: string, data?: Data, config?: AxiosRequestConfig) => Promise<AxiosResponse>
getUrl: (url: string, data: string) => Promise<AxiosResponse>
post: (url: string, data?: Data, config?: AxiosRequestConfig) => Promise<AxiosResponse>
put: (url: string, data?: Data, config?: AxiosRequestConfig) => Promise<AxiosResponse>
delete: (url: string, data: string) => Promise<AxiosResponse>
}
const http: Http = {
get(url, data, config) {
return instance.get(url, {
params: data,
...config
})
},
getUrl(url, data) {
return instance.get(`${url}/${data}`)
},
post(url, data, config) {
return instance.post(url, data, config)
},
delete(url, data) {
return instance.delete(`${url}/${data}`)
},
put(url, data, config) {
return instance.put(url, data, config)
}
}
export default http
import React, { useState, useEffect} from 'react'
import styles from './ContactUs.module.scss'
import Header from '../../components/Header/Header'
import Footer from '../../components/Footer/Footer'
import { Col, Row, Button, Form, type FormProps, type CheckboxProps, Input, Checkbox, Select } from 'antd';
import { getContactUs, getMessage, getCityList, type ruleData } from '../../store/modules/contactUs'
const { TextArea } = Input;
const { Option } = Select;
interface typeData{
id: number,
title: string,
title_en: String,
content: string,
contact: string,
bg_cover: string,
created_at: string,
updated_at: string,
length?: number
}
interface typeCityData{
city_id: number,
name: string,
parent_id: number
}
export default function ContactUs() {
const [form] = Form.useForm();
const [ activeIndex, setactiveIndex ] = useState<number>(0)
const [ selectIndex, setSelectIndex ] = useState<number|null>(null)
const [isCheck, setIsCheck] = useState<boolean>(false)
const [bgData, setBgData] = useState<typeData[]>([])
const [cityData, setCityData] = useState<typeCityData[]>([])
const [col, setCol] = useState(12);
const [sw, setSw] = useState(true)
const [loading, setLoading] = useState(false)
const handleWindowSizeChange = () => { //监听页面大小变化
setCol(window.innerWidth > 750 ? 12 : 24)
!selectIndex && setSw(window.innerWidth > 750 ? true : false)
};
const handleMouseEnter = (index: number) => {
setactiveIndex(index)
}
const openContactForm = (index: number) => {
setSelectIndex(index)
col===24 && setSw(true)
}
const onBack = () => {
setSelectIndex(0)
}
const checkData = (val: ruleData): Boolean=>{
if(!val.email){
alert('电子邮箱不能为空')
setLoading(false)
return false
}else if(!val.name){
alert('姓名不能为空')
setLoading(false)
return false
}else if(!val.phone){
alert('电话不能为空')
setLoading(false)
return false
}else if(!val.province){
alert('地区不能为空')
setLoading(false)
return false
}else if(!val.msg){
alert('留言不能为空')
setLoading(false)
return false
}else{
return true
}
}
const onFinish: FormProps<ruleData>["onFinish"] = (values) => {
setLoading(true)
if(isCheck){
checkData(values) && getMessage(values).then(res=>{
if(res.data.code===200){
alert('发送成功!')
setSelectIndex(0)
setSw(false)
form.resetFields();
}else{
alert(res.data.message)
}
}).finally(()=>{
setLoading(false)
})
}else{
alert('请勾选阅读隐私权条款')
setLoading(false)
}
};
const onGenderChange = (val: string)=>{}
const onChangeCheck: CheckboxProps['onChange'] = (e) => {
setIsCheck(e.target.checked)
}
const divForm = () => {
return (
<Form name="trigger"
style={{ padding: 20 }}
layout="vertical"
autoComplete="off"
labelCol={{ span: 8 }}
onFinish={onFinish}
form={form}
>
<Row gutter={16}>
<Col span={col}>
<Form.Item
label="姓名*"
name="name"
rules={[{ max: 10 }]}
validateTrigger="onBlur"
>
<Input placeholder="姓名" className={styles.txt} />
</Form.Item>
</Col>
<Col span={col}>
<Form.Item
label="电子邮箱*"
name="email"
validateTrigger="onBlur"
rules={[
{
type: 'email',
message: '输入的邮箱格式不正确!',
}
]}
>
<Input placeholder="电子邮箱" className={styles.txt} />
</Form.Item>
</Col>
<Col span={col}>
<Form.Item
label="电话*"
name="phone"
validateTrigger="onBlur"
rules={[
{
pattern: /^1[3-9]\d{9}$/,
message: '手机号码格式不正确!',
},
]}
>
<Input placeholder="电话" className={styles.txt} />
</Form.Item>
</Col>
<Col span={col}>
<Form.Item
label="地区*"
name="province"
validateTrigger="onBlur"
>
<Select
placeholder="地区"
onChange={onGenderChange}
allowClear
>
{ cityData.length > 0 && cityData.map(item=>(
<Option className={styles['option-size']} value={item.city_id} key={item.city_id}>{item.name}</Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
label="您的留言*"
name="msg"
validateTrigger="onBlur"
rules={[{ max: 100 }]}
>
<TextArea rows={4} placeholder="" maxLength={200} />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item>
<div className={styles.cbox}>
<Checkbox onChange={onChangeCheck} checked={isCheck}>我已阅读并接受CHRISTINE DAI网站的隐私权条款</Checkbox>
<Button loading={loading} htmlType="submit" className={`${styles['c-btn']} ${styles.sub}`}>发送</Button>
</div>
</Form.Item>
</Col>
</Row>
</Form>
)
}
useEffect(() => {
getContactUs().then(res=>{
if(res.data.code===200){
setBgData([...res.data.data])
}
})
getCityList().then(res=>{
if(res.data.code===200){
setCityData([...res.data.data])
}
})
handleWindowSizeChange()
window.addEventListener('resize', handleWindowSizeChange);
return () => {
window.removeEventListener('resize', handleWindowSizeChange);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
},[]);
return (
<div className={ styles['contactUs-body'] }>
<Header titleObj={{title:'联系我们',name:'CONTACT US'}}></Header>
<div className={ styles['contactUs-top'] }></div>
<div className={ `${styles.contactUs} ${!selectIndex ? styles.hide : ''}` }>
{ bgData.length > 0 && <div className={ `${styles['cu-container']} ${ selectIndex ? styles['cu-narrow'] : ''}` } style={{backgroundImage: 'url('+bgData[activeIndex]?.bg_cover+')'}}>
{ bgData.map((item,index)=>(
<div className={`${styles.itemBox} ${selectIndex && selectIndex !== index+1 ? styles.narrow : styles.selectDiv}`} key={index} onMouseEnter={()=>handleMouseEnter(index)}>
{ col===24 && <div className={styles.bgBox} style={{backgroundImage: 'url('+item.bg_cover+')'}}></div>}
<div className={`${styles.wrapper} ${selectIndex === index+1 ? styles.showWrapper : ''}`}>
<div className={styles.title}><span>{item.title}</span><br/><span>{item.title_en}</span></div>
<div className={`${styles.content } ${selectIndex === index+1 ? styles.showContent : ''}`}>
<div className={`${styles.txt} ${selectIndex ? styles.showWarp : styles.hideWarp}`} dangerouslySetInnerHTML={{ __html: item.content }}></div>
<p className={styles['c-btn']} onClick={()=>openContactForm(index+1)}>{item.contact}</p>
</div>
</div>
</div>
))
}
{ col===12 && <div className={`${styles['contact-form']} ${selectIndex ? styles.enlarge : (selectIndex === 0 ? styles['enlarge-leave'] : '')}`}>
<div className={styles.title}>联络我们 <span onClick={()=>onBack()}>返回上一页</span></div>
{ divForm() }
</div>}
</div>}
{ bgData.length > 0 && col===24 && <div className={`${styles['contact-form']} ${selectIndex ? styles.enlarge : (selectIndex === 0 ? styles['enlarge-leave'] : '')} ${ selectIndex ? styles.showForm : '' }`}>
<div className={styles.title}>联络我们 <span onClick={()=>onBack()}>返回上一页</span></div>
{ divForm() }
</div>}
</div>
<Footer></Footer>
</div>
)
}
.doc-banner{
background-color: #000;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
padding-bottom: 4.1rem;
position: relative;
}
.doc-banner h5{
font-family: "宋体",'宋体-简',"常规体";
color: #fff;
position: absolute;
bottom: 33%;
left: 7%;
font-size: .4rem;
line-height: 1.4;
box-sizing: border-box;
span{
color: #8D7249;
}
}
.DocBody{
width: 100%;
min-height: calc(100vh - 6rem);
box-sizing: border-box;
background: #000;
padding: 7% 4%;
position: relative;
overflow: visible;
}
.doc-list{
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.doc-item{
display: flex;
width: 62%;
justify-content: center;
cursor: pointer;
margin-bottom: 1.1rem;
.doc-left{
width: 58%;
height: 3.15rem;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
img{
border: 0;
width: 100%;
height: 100%;
min-height: 100%;
vertical-align: middle;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
}
.doc-right{
width: 42%;
font-weight: 400;
font-size: 22px;
line-height: 1.4;
background-color: #fff;
box-shadow: 1px 1px 10px 0px rgba(0, 0, 0, 0.2);
color: #8d7249;
padding: 3% 3% 0 7%;
box-sizing: border-box;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
.doc-date{
font-family: "宋体",'宋体-简',"常规体";
font-weight: bold;
font-size: 0.42rem;
color: #8E734A;
line-height: 93px;
letter-spacing: 4px;
text-align: left;
font-style: normal;
}
.doc-title{
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 16px;
color: #000000;
line-height: 34px;
text-align: left;
font-style: normal;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
text-overflow: ellipsis;
}
}
.doc-icon{
width: 0.3rem;
height: 0.3rem;
}
}
.doc-nav{
position: absolute;
right: 0.5rem;
top: 0.7rem;
.nav-line{
position: absolute;
width: 1px;
min-height: 100%;
left: 50%;
transform: translateX(-50%);
background: #8d7249;
}
.nav-list{
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}
.navItem{
z-index: 1;
position: relative;
background-color: #fff;
width: 10px;
height: 10px;
border:1px solid #8d7249;
border-radius: 50%;
cursor: pointer;
margin-top: 0.3rem;
.activeText{
display: none;
color: #fff;
}
}
.active:first-child,
.navItem:first-child {
margin-top: 0;
}
.active{
position: relative;
cursor: pointer;
z-index: 1;
width: 20px;
height: 20px;
background-color: #fff;
border:1px solid #8d7249;
border-radius: 50%;
margin-top: 0.3rem;
.activeText{
display: block;
position: absolute;
font-size: 0.2rem;
left: -90px;
top: 0;
color: #fff;
}
}
.active::before{
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 8px;
height: 8px;
background-color: black;
border-radius: 50%; /* 将矩形变成圆形 */
transform: translate(-50%, -50%);
}
}
.doc-nav-fix{
position: fixed;
right: 0.5rem;
top: 1.7rem;
.nav-line{
position: absolute;
width: 1px;
min-height: 100%;
left: 50%;
transform: translateX(-50%);
background: #8d7249;
}
.nav-list{
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}
.navItem{
z-index: 1;
position: relative;
background-color: #fff;
width: 10px;
height: 10px;
border:1px solid #8d7249;
border-radius: 50%;
cursor: pointer;
margin-top: 0.3rem;
.activeText{
display: none;
color: #fff;
}
}
.active:first-child,
.navItem:first-child {
margin-top: 0;
}
.active{
position: relative;
cursor: pointer;
z-index: 1;
width: 20px;
height: 20px;
background-color: #fff;
border:1px solid #8d7249;
border-radius: 50%;
margin-top: 0.3rem;
.activeText{
display: block;
position: absolute;
font-size: 0.2rem;
left: -90px;
top: 0;
color: #fff;
}
}
.active::before{
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 8px;
height: 8px;
background-color: black;
border-radius: 50%; /* 将矩形变成圆形 */
transform: translate(-50%, -50%);
}
}
@media screen and (max-width: 750px) {
.DocBody{
padding: 7% 2%;
}
.doc-nav{
display: none;
}
.doc-nav-fix{
display: none;
}
.doc-item{
width: 80%;
display: flex;
flex-direction: column;
justify-content: center;
border-radius: 10px;
.doc-left{
width: 100%;
border-bottom-left-radius: 0;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
img{
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
}
.doc-right{
width: 100%;
border-top-right-radius: 0;
border-bottom-right-radius: 10px;
border-bottom-left-radius: 10px;
}
}
}
\ No newline at end of file
import React,{useState,useEffect} from 'react'
import styles from './Documentary.module.scss'
import Header from '../../components/Header/Header'
import Footer from '../../components/Footer/Footer'
import { useNavigate } from 'react-router-dom';
import { getBrandEvent } from '../../store/modules/home'
export default function Documentary() {
interface DataItem {
id: number;
event_date: string;
title: string;
brand_img: string;
linkurl: string;
created_at: string;
updated_at: string;
top?: number
}
const [docList, setDocList] = useState<DataItem[]>([])
const [navNumber,setNavNumber] = useState<Number>(0)
const [isFix,setIsFix] = useState<boolean>(false)
const navigate = useNavigate();
let docListBak: DataItem[] = []
const handleButtonClick = (url:string) => {
// 使用 navigate() 方法进行路由跳转
navigate(url);
};
const toUrl = (url:string) => {
// 使用 navigate() 方法进行路由跳转
window.location.href = url
};
const cutNav = (i:number, top: number) =>{
window.scrollTo({
top,
behavior: 'smooth'
})
setTimeout(() => {
setNavNumber(i)
}, 700)
}
const getElementPageY = () => {
const docUI: HTMLUListElement = document.querySelector('#docUI') as HTMLUListElement
console.log(docUI, 'docUI')
const children = docUI.querySelectorAll('li')
for (let i = 0; i < children.length; i++ ) {
docListBak[i].top = children[i].offsetTop + 270
}
setDocList(docListBak)
}
const listenerULList = () => {
const scrollY = window.scrollY
const len = docListBak.length
if(scrollY>300){
setIsFix(true)
}else{
setIsFix(false)
}
for (let i = 0; i < len; i++) {
if (((docListBak[i].top) as number + 270) >= scrollY) {
setNavNumber(i)
return false
}
}
}
useEffect(() => {
getBrandEvent().then(res => {
if (res.data.code === 200) {
const list = res.data.data
docListBak = list
setDocList(list)
setTimeout(() => {
getElementPageY()
window.addEventListener('scroll', listenerULList, false)
})
}
})
return () => {
window.removeEventListener('scroll', listenerULList, false)
}
}, [])
return (
<div>
<Header></Header>
<div className={styles['doc-banner']}>
<h5>
<span>BRAND HERITAGE</span>
<br/>
品牌纪事
</h5>
</div>
<div className={styles.DocBody}>
<div className={isFix?styles['doc-nav-fix']:styles['doc-nav']}>
<div className={styles['nav-line']}></div>
<ul className={styles['nav-list']}>
{docList.map((item,index)=>{
return(<li onClick={()=>cutNav(index, item.top as number)} className={navNumber === index?styles.active:styles.navItem} key={item.id}>
<div className={styles.activeText}>{item.event_date}</div>
</li>)
})}
</ul>
</div>
<ul className={styles['doc-list']} id="docUI">
{docList.map((item)=>{
return(<li onClick={()=>toUrl(item.linkurl)} className={styles['doc-item']} key={item.id}>
<div className={styles['doc-left']}>
<img src={item.brand_img} alt="" />
</div>
<div className={styles['doc-right']}>
<p className={styles['doc-date']}>{item.event_date}</p>
<div className={styles['doc-title']} dangerouslySetInnerHTML={{ __html: item.title }}></div>
<img className={styles['doc-icon']} src={require('../../assets/images/rightIcon.png')} alt="" />
</div>
</li>)
})}
</ul>
</div>
<Footer></Footer>
</div>
)
}
.GlobalMedia{
.banner{
background-color: #000;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
padding-bottom: 4.1rem;
position: relative;
h5{
font-family: "宋体",'宋体-简',"常规体";
color: #fff;
position: absolute;
bottom: 33%;
left: 7%;
font-size: .4rem;
line-height: 1.4;
box-sizing: border-box;
span{
color: #8D7249;
}
}
}
.gw-container{
min-height: calc(100vh - 6.2rem);
padding: 0.5rem 0;
background: #000;
position: relative;
.content-box{
padding: 0 16%;
display: grid;
grid-template-columns: repeat(3, 1fr); /* 12 columns */
gap: 20px; /* Grid gap */
.card{
width: 90%;
// height: 316px;
// margin-right: 5.33%;
margin-bottom: 0.2rem;
cursor: pointer;
&:nth-child(3n){
margin-right: 0;
}
.imgBox{
overflow: hidden;
img{
transition: 4s;
width: 100%;
height: 2.69rem;
vertical-align: middle;
border-top-left-radius: 20px;
border-top-right-radius: 20px;
}
}
h5{
overflow: hidden;
height: 0.87rem;
background-color: #fff;
border-bottom-left-radius: 20px;
border-bottom-right-radius: 20px;
display: flex;
flex-direction: column;
justify-content:center;
}
h5 p{
font-size: .14rem;
color: #000;
text-align: center;
font-weight: normal;
line-height: 1.5;
box-sizing: border-box;
}
}
.card:hover{
.imgBox{
img{
transform: scale(1.2);
border-top-left-radius: 20px;
border-top-right-radius: 20px;
}
}
h5 p{
color: #8d7249;
}
}
}
}
.w-1400{
max-width: 1400px;
margin: 0 auto;
}
}
.global-nav{
position: absolute;
right: 0.5rem;
top: 0.7rem;
.nav-line{
position: absolute;
width: 1px;
min-height: 100%;
left: 50%;
transform: translateX(-50%);
background: #8d7249;
}
.nav-list{
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}
.navItem{
z-index: 1;
position: relative;
background-color: #fff;
width: 10px;
height: 10px;
border:1px solid #8d7249;
border-radius: 50%;
cursor: pointer;
margin-top: 0.3rem;
.activeText{
display: none;
color: #fff;
height: 0.2rem;
width: 1rem;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
p{
width: 100%;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
}
}
}
.active:first-child,
.navItem:first-child {
margin-top: 0;
}
.active{
position: relative;
cursor: pointer;
z-index: 1;
width: 20px;
height: 20px;
background-color: #fff;
border:1px solid #8d7249;
border-radius: 50%;
margin-top: 0.3rem;
.activeText{
display: block;
position: absolute;
font-size: 0.2rem;
left: -100px;
top: 0;
height: 0.2rem;
width: 1rem;
color: #fff;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
p{
width: 100%;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
}
}
}
.active::before{
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 8px;
height: 8px;
background-color: black;
border-radius: 50%; /* 将矩形变成圆形 */
transform: translate(-50%, -50%);
}
}
.global-nav-fix{
position: fixed;
right: 0.5rem;
top: 1.7rem;
.nav-line{
position: absolute;
width: 1px;
min-height: 100%;
left: 50%;
transform: translateX(-50%);
background: #8d7249;
}
.nav-list{
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}
.navItem{
z-index: 1;
position: relative;
background-color: #fff;
width: 10px;
height: 10px;
border:1px solid #8d7249;
border-radius: 50%;
cursor: pointer;
margin-top: 0.3rem;
.activeText{
display: none;
color: #fff;
height: 0.2rem;
width: 1rem;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
p{
width: 100%;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
}
}
}
.active:first-child,
.navItem:first-child {
margin-top: 0;
}
.active{
position: relative;
cursor: pointer;
z-index: 1;
width: 20px;
height: 20px;
background-color: #fff;
border:1px solid #8d7249;
border-radius: 50%;
margin-top: 0.3rem;
.activeText{
display: block;
position: absolute;
font-size: 0.2rem;
left: -100px;
top: 0;
height: 0.2rem;
width: 1rem;
color: #fff;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
p{
width: 100%;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
}
}
}
.active::before{
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 8px;
height: 8px;
background-color: black;
border-radius: 50%; /* 将矩形变成圆形 */
transform: translate(-50%, -50%);
}
}
@media screen and (max-width: 750px) {
.GlobalMedia{
.gw-container{
min-height: calc(100vh - 8rem);
.content-box{
display: grid;
grid-template-columns:repeat(2, 1fr) !important; /* 两列,每列自动宽度 */
grid-gap: 20px; /* 格子间隔 */
padding: 10px 30px !important; /* 容器内边距 */
.card{
width: 100%;
margin-right: 2% !important;
&:nth-child(3n){
margin-right: 2% !important;
}
&:nth-child(2n){
margin-right: 0;
}
}
}
}
}
}
@media screen and (min-width: 750px) and (max-width: 900px) {
.GlobalMedia{
.gw-container{
min-height: calc(100vh - 8rem);
.content-box{
display: grid;
grid-template-columns:repeat(2, 1fr) !important; /* 两列,每列自动宽度 */
grid-gap: 20px; /* 格子间隔 */
padding: 10px; /* 容器内边距 */
.card{
width: 100%;
margin-right: 2% !important;
&:nth-child(3n){
margin-right: 2% !important;
}
&:nth-child(2n){
margin-right: 0;
}
}
}
}
}
.global-nav{
display: none !important;
}
.global-nav-fix{
display: none !important;
}
}
@media screen and (max-width: 750px) {
.GlobalMedia{
.gw-container{
min-height: calc(100vh - 7rem);
.content-box{
display: grid;
grid-template-columns:repeat(2, 1fr) !important; /* 两列,每列自动宽度 */
grid-gap: 20px; /* 格子间隔 */
padding: 10px; /* 容器内边距 */
.card{
width: 100%;
margin-right: 2% !important;
&:nth-child(3n){
margin-right: 2% !important;
}
&:nth-child(2n){
margin-right: 0;
}
}
}
}
}
.global-nav{
display: none !important;
}
.global-nav-fix{
display: none !important;
}
}
\ No newline at end of file
import React, { useState, useEffect } from 'react'
import styles from './GlobalMedia.module.scss'
import Header from '../../components/Header/Header'
import Footer from '../../components/Footer/Footer'
import { useNavigate } from 'react-router-dom';
import { getMediaList } from '../../store/modules/meida'
interface typeData{
id: number,
title: string,
media_img: string,
linkurl: string,
created_at: string,
top?: number
}
export default function GlobalMedia() {
const navigate = useNavigate();
const [isFix,setIsFix] = useState<boolean>(false)
const [navNumber,setNavNumber] = useState<Number>(0)
let globalListBak: typeData[] = []
const handleButtonClick = (url:string) => {
// 使用 navigate() 方法进行路由跳转
window.location.href = url
};
const [mediaData, setMediaData] = useState<typeData[]>([])
const [mediaData2, setMediaData2] = useState<typeData[]>([])
const cutNav = (i:number, top: number) =>{
window.scrollTo({
top,
behavior: 'smooth'
})
setTimeout(() => {
setNavNumber(i)
}, 700)
}
const getElementPageY = () => {
const docUI: HTMLUListElement = document.querySelector('#docUI') as HTMLUListElement
const children = docUI.querySelectorAll('li')
// 使用Set来存储唯一的offsetTop值
const seenOffsets = new Set<number>();
const uniqueChildren: HTMLLIElement[] = [];
// 遍历children并去重
children.forEach(child => {
const offsetTop = child.offsetTop;
if (!seenOffsets.has(offsetTop)) {
seenOffsets.add(offsetTop);
uniqueChildren.push(child);
}
});
// 更新globalListBak
uniqueChildren.forEach((child, i) => {
globalListBak[i].top = child.offsetTop + 220;
});
setMediaData2(globalListBak)
}
const listenerULList = () => {
const scrollY = window.scrollY
const len = globalListBak.length
if(scrollY>300){
setIsFix(true)
}else{
setIsFix(false)
}
for (let i = 0; i < len; i++) {
if (((globalListBak[i].top) as number + 220) >= scrollY) {
setNavNumber(i)
return false
}
}
}
useEffect(()=>{
getMediaList().then(res=>{
if(res.data.code===200){
const list = res.data.data.list
var screenWidth = window.screen.width;
// console.log(screenWidth,'screenWidth')
setMediaData(list)
// for (let i = 0; i < list.length; i += 3) {
// globalListBak.push({
// ...list[i],
// top:0
// });
// }
// if(screenWidth<900){
// }else{
// setTimeout(() => {
// getElementPageY()
// window.addEventListener('scroll', listenerULList, false)
// },500)
// }
}
})
},[])
return (
<div className={styles.GlobalMedia}>
<Header></Header>
<div className={styles.banner}>
<h5>
<span>GLOBAL EVENTS</span>
<br/>
全球媒体
</h5>
</div>
<div className={styles['gw-container']}>
<div className={isFix?styles['global-nav-fix']:styles['global-nav']}>
<div className={styles['nav-line']}></div>
{/* <ul className={styles['nav-list']}>
{mediaData2.map((item,index)=>{
return(<li onClick={()=>cutNav(index, item.top as number)} className={navNumber === index?styles.active:styles.navItem} key={item.id}>
<div className={styles.activeText} dangerouslySetInnerHTML={{ __html: item.title }}></div>
</li>)
})}
</ul> */}
</div>
<div className={styles['w-1400']}>
<ul className={styles['content-box']} id="docUI">
{ mediaData.length > 0 && mediaData.map(item=>(
<li onClick={()=>handleButtonClick(item.linkurl)} className={styles.card} key={item.id}>
<div className={styles.imgBox}>
<img src={item.media_img} alt=""/>
</div>
<h5 dangerouslySetInnerHTML={{ __html: item.title }}></h5>
</li>
))}
</ul>
</div>
</div>
<Footer></Footer>
</div>
)
}
\ No newline at end of file
.homeBody {
height: 100vh;
width: 100%;
background-color: #000;
.bgVideo {
width: 100%;
height: 100vh;
top: 0;
left: 0;
-o-object-fit: cover;
object-fit: cover;
opacity: .5;
position: absolute;
}
}
.home-container {
position: absolute;
width: 100%;
height: 100vh;
z-index: 10;
.home-content {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
.home-inner {
display: flex;
align-items: center;
.intro-left {
margin-right: 1.8rem;
img {
width: 5rem;
max-height: 2rem;
}
.intro-head {
display: flex;
justify-content: center;
align-items: center;
font-size: 0.14rem;
color: #8d7249;
margin-top: 0.3rem;
p:nth-child(1){
margin: 0 0.2rem;
font-size: 0.34rem;
line-height: 1.5;
display: flex;
justify-content: space-around;
}
p:nth-child(2){
font-size: 0.14rem;
margin-bottom: 0.2rem;
}
span {
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
margin: 0 0.2rem;
font-family: "宋体",'宋体-简',"常规体";
b {
font-family: "宋体",'宋体-简',"常规体";
font-size: 0.34rem;
margin-bottom: 0.1rem;
}
}
}
}
.intro-right {
div {
cursor: pointer;
display: flex;
flex-direction: column;
text-align: center;
margin-bottom: 0.2rem;
b {
transition: opacity .7s;
font-family: "宋体",'宋体-简',"常规体";
color: #fff;
margin-bottom: 0.08rem;
min-width: 1.28rem;
font-size: 0.32rem;
}
i {
transition: opacity .7s;
font-family: Baskerville;
color: #8d7249;
font-size: 0.12rem;
}
}
.right-show{
b{
opacity: 1;
}
}
.right-hide{
b{
opacity: .3;
}
i{
opacity: .3;
}
}
}
}
}
.home-bottom {
position: absolute;
width: 100%;
bottom: 0.3rem;
color: #fff;
box-sizing: border-box;
font-size: 0.12rem;
padding: 0 0.5rem;
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: row-reverse;
.home-wx{
display: inline-block;
position: relative;
padding: 0.1rem 0;
cursor: pointer;
.home-ewm{
display: none;
position: absolute;
left: 50%;
bottom: 30px;
width: 140px;
padding: 10px;
background-color: #fff;
transform: translateX(-50%);
box-sizing: border-box;
z-index: 3;
img{
margin: 0;
width: 100%;
height: 100%;
border: 0;
vertical-align: middle;
}
}
}
.home-wx:hover{
.home-ewm{
display: block;
}
}
.shortlink {
img {
height: 0.12rem;
margin: 0 0.08rem;
cursor: pointer;
}
}
}
}
@media screen and (max-width: 750px) {
.home-container {
.home-content {
.home-inner {
display: block;
.intro-left {
margin-right: 0;
margin-bottom: 0.5rem;
}
.intro-right {
div {
font-size: 0.38rem;
i {
font-size: 0.18rem;
}
}
}
}
}
.home-bottom {
display: block;
text-align: center;
span {
font-size: 0.18rem;
}
.shortlink {
margin-bottom: 0.2rem;
img {
height: 0.30rem;
margin: 0 0.2rem;
}
}
}
}
}
@media screen and (min-width: 750px) and (max-width: 768px) {
.home-container {
.home-content {
.home-inner {
.intro-left {
margin-right: 1.2rem;
}
}
}
}
}
\ No newline at end of file
import React, { useEffect, useState } from 'react'
import styles from './Home.module.scss'
import { useSelector } from 'react-redux'
import { getHomeInfos } from '../../store/modules/home'
import { useAppDispatch } from '../../store'
import type { RootState } from '../../store'
import { useNavigate } from 'react-router-dom';
import {HomeKey,cacheObject,getCachedObject} from '../../utils/auth'
export default function Home() {
interface RuleData {
bg_video: string
logo_img: string
placard: string
weibo:string
xiaohongshu:string
ins:string
wxqr:string
}
const year = useSelector((state: RootState) => state.story.year)
const dispatch = useAppDispatch()
const navigate = useNavigate();
const [homeData, setHomeData] = useState<RuleData>({
bg_video: '',
logo_img: '',
placard: '',
weibo:'',
xiaohongshu:'',
ins:'',
wxqr:''
})
const [hoveredIndex, setHoveredIndex] = useState<null|number>(null);
const handleMouseEnter = (index:number) => {
setHoveredIndex(index);
};
const handleMouseLeave = () => {
setHoveredIndex(null);
};
const handleButtonClick = (url:string) => {
// 使用 navigate() 方法进行路由跳转
navigate(url);
// window.location.reload();
};
useEffect(() => {
if(getCachedObject(HomeKey)){
const homeObject = getCachedObject(HomeKey);
setHomeData(homeObject)
}
getHomeInfos().then(res => {
if (res.data.code === 200) {
const { bg_video, placard, logo_img,weibo,xiaohongshu,ins,wxqr} = res.data.data
cacheObject(HomeKey,{
bg_video, logo_img, placard,weibo,xiaohongshu,ins,wxqr
})
// 获取缓存的对象
const homeObject = getCachedObject(HomeKey);
setHomeData(homeObject)
}
})
}, [])
return (
<div className={styles.homeBody}>
Home
</div>
)
}
\ No newline at end of file
.Jewelry{
background-color: #000;
width: 100%;
min-height: 100vh;
box-sizing: border-box;
}
.JewelryBody{
display: flex;
align-items: center;
background-color: #000;
height: calc(100vh - 0.3rem);
}
.jewe-box{
width: 100%;
padding: 0 4%;
margin: 0 auto;
box-sizing: border-box;
}
.jewe-list{
width: 100%;
display: flex;
color: #fff;
justify-content: space-around;
box-sizing: border-box;
.jewe-item{
width: 29%;
display: flex;
align-items: center;
flex-direction:column;
justify-content:space-between;
position: relative;
.jewe-text{
cursor: pointer;
white-space: nowrap;
margin: 0.5rem 1rem 1.4rem 1rem;
.jewe-the{
font-size: 0.24rem;
font-family: Baskerville;
font-style: italic;
margin-bottom: 0.16rem;
}
h5{
font-size: 0.48rem;
font-family: Baskerville;
}
}
.jewe-icon{
display: flex;
align-items: center;
justify-content: space-between;
img{
width: 0.2rem;
height: 0.2rem;
}
}
}
.jewe-item:not(:last-child)::after {
content: '';
position: absolute;
top: 24%;
right: 0;
width: 2px;
height: 0.74rem;
background-color: #ccc;
opacity: 0.4;
transform:rotate(-25deg);
}
.jewe-down{
color: #8D7249;
font-size: 0.18rem;
cursor: pointer;
text-decoration: none;
font-family: AlibabaPuHuiTi, AlibabaPuHuiTi;
&:hover{
.jewe-line{
&:before {
-webkit-transform: scaleX(1);
-ms-transform: scaleX(1);
transform: scaleX(1);
-webkit-transition-delay: .3s;
transition-delay: .3s;
}
&:after {
-webkit-transform: scaleX(0);
-ms-transform: scaleX(0);
transform: scaleX(0);
-webkit-transition-delay: 0s;
transition-delay: 0s;
}
}
}
}
.jewe-line{
width: 100%;
height: 1px;
margin: 0.05rem 0 0.1rem 0;
position: relative;
&:before {
-webkit-transform: scaleX(0);
-ms-transform: scaleX(0);
transform: scaleX(0);
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
-webkit-transition-delay: 0s;
transition-delay: 0s;
}
&:after{
-webkit-transform: scaleX(1);
-ms-transform: scaleX(1);
transform: scaleX(1);
-webkit-transform-origin: 100% 0;
-ms-transform-origin: 100% 0;
transform-origin: 100% 0;
-webkit-transition-delay: .3s;
transition-delay: .3s;
}
&:before, &:after {
height: 1px;
width: 100%;
position: absolute;
background-color: #8d7249;
bottom: -3px;
left: 0;
content: "";
-webkit-transition: -webkit-transform .3s cubic-bezier(.39,.575,.565,1);
transition: -webkit-transform .3s cubic-bezier(.39,.575,.565,1);
transition: transform .3s cubic-bezier(.39,.575,.565,1);
transition: transform .3s cubic-bezier(.39,.575,.565,1),-webkit-transform .3s cubic-bezier(.39,.575,.565,1);
}
}
.jewe-txt{
margin-right: 0.1rem;
}
.jewe-top{
display: none;
}
}
@media screen and (max-width: 750px) {
.jewe-top{
display: block;
height: 1.5rem;
width: 100vw;
background: #000;
}
.JewelryBody{
height: calc(100vh - 1.8rem);
}
.jewe-list{
display: flex;
flex-direction: column;
.jewe-item{
width: 100%;
margin-bottom: 0.3rem;
.jewe-text{
margin: 0.5rem 1rem 1rem 1rem;
}
}
.jewe-item::after{
display: none;
}
}
}
@media screen and (min-width: 750px) and (max-width: 768px) {
.jewe-top{
display: block;
height: 1.5rem;
width: 100vw;
background: #000;
}
.JewelryBody{
height: calc(100vh - 2.06rem);
}
.jewe-list{
.jewe-item{
.jewe-text{
margin: 0.5rem 1rem 1rem 1rem;
}
h5{
font-size: 0.38rem;
margin-top: 0.3rem;
}
.jewe-icon{
display: flex;
align-items: center;
justify-content: space-between;
img{
width: 0.3rem;
height: 0.3rem;
}
}
}
.jewe-item::after{
display: none;
}
}
}
\ No newline at end of file
import React from 'react'
import styles from './Jewelry.module.scss'
import Header from '../../components/Header/Header'
import Footer from '../../components/Footer/Footer'
import { useNavigate } from 'react-router-dom';
export default function Jewelry() {
const navigate = useNavigate();
const handleButtonClick = (url:string) => {
// 使用 navigate() 方法进行路由跳转
navigate(url);
};
return (
<div className={styles.Jewelry}>
<Header></Header>
<div className={ styles['jewe-top'] }></div>
<div className={styles.JewelryBody}>
<div className={styles['jewe-box']}>
<ul className={styles['jewe-list']}>
<li className={styles['jewe-item']}>
<div onClick={()=>handleButtonClick('/theJewelry/1')} className={styles['jewe-text']}>
<p className={styles['jewe-the']}>The</p>
<h5>High Jewel</h5>
</div>
<div onClick={()=>handleButtonClick('/theJewelry/1')} className={styles['jewe-down']}>
<div className={styles['jewe-icon']}>
即刻探索
<img src={require('../../assets/images/rightIcon.png')} alt="" />
</div>
<div className={styles['jewe-line']}></div>
<div><span className={styles['jewe-txt']}>Series</span>collection</div>
</div>
</li>
<li className={styles['jewe-item']}>
<div onClick={()=>handleButtonClick('/theJewelry/3')} className={styles['jewe-text']}>
<p className={styles['jewe-the']}>The</p>
<h5>Art Jewel</h5>
</div>
<div onClick={()=>handleButtonClick('/theJewelry/3')} className={styles['jewe-down']}>
<div className={styles['jewe-icon']}>
即刻探索
<img src={require('../../assets/images/rightIcon.png')} alt="" />
</div>
<div className={styles['jewe-line']}></div>
<div><span className={styles['jewe-txt']}>Series</span>collection</div>
</div>
</li>
<li className={styles['jewe-item']}>
<div onClick={()=>handleButtonClick('/theJewelry/4')} className={styles['jewe-text']}>
<p className={styles['jewe-the']}>The</p>
<h5>Fine Jewel</h5>
</div>
<div onClick={()=>handleButtonClick('/theJewelry/4')} className={styles['jewe-down']}>
<div className={styles['jewe-icon']}>
即刻探索
<img src={require('../../assets/images/rightIcon.png')} alt="" />
</div>
<div className={styles['jewe-line']}></div>
<div><span className={styles['jewe-txt']}>Series</span>collection</div>
</div>
</li>
</ul>
</div>
</div>
<Footer></Footer>
</div>
)
}
\ No newline at end of file
.detailBody{
width: 100%;
// min-height: 100vh;
background: #000;
}
.detail-top{
background-color: #000;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
padding-bottom: 4.1rem;
position: relative;
}
.detail-top2{
background-color: #000;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
padding-bottom: 4.1rem;
position: relative;
}
.detail-img{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.detail-prevBtn{
z-index: 1;
width: .3rem;
padding-left: .3rem;
text-align: center;
font-size: .16rem;
display: block;
position: absolute;
left: .28rem;
top: 50%;
transform: translateY(-50%);
color: #fff;
line-height: 1.5;
cursor: pointer;
}
.detail-prevBtn::before{
width: .19rem;
height: .3rem;
content: '';
background: url(../../assets/images/i3.png) no-repeat;
background-size: contain;
position: absolute;
left: 0;
top: 50%;
margin-top: -.15rem;
transition: .3s;
}
.detail-prevBtn:hover:before{
left: -0.05rem;
}
.detail-nextBtn{
width: .3rem;
padding-right: .3rem;
text-align: center;
font-size: .16rem;
display: block;
position: absolute;
right: .28rem;
top: 50%;
transform: translateY(-50%);
color: #fff;
line-height: 1.5;
cursor: pointer;
z-index: 1;
}
.detail-nextBtn::before {
width: .19rem;
height: .3rem;
content: '';
background: url(../../assets/images/i3.png) no-repeat;
background-size: contain;
position: absolute;
right: 0;
top: 50%;
margin-top: -.15rem;
transition: .3s;
transform: rotate(180deg);
}
.detail-nextBtn:hover:before{
right: -0.05rem;
}
.detail-txtBox{
color: #fff;
position: absolute;
top: 55%;
left: 10%;
font-size: .4rem;
transform: translateY(-50%);
line-height: 1.5;
font-family: "宋体",'宋体-简',"常规体";
.detail-title{
margin-top: 0.3rem;
}
}
.detail-text-brief{
font-family: Baskerville;
width: 80%;
font-size: 0.14rem;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 4;
overflow: hidden;
text-overflow: ellipsis;
font-family: "宋体",'宋体-简',"常规体";
color: #fff;
}
.detail-brief{
font-family: Baskerville;
font-size: .24rem;
line-height: .4rem;
color: #000;
margin-bottom: 7%;
font-weight: 500;
margin-top: 0.5rem;
}
.detail-box{
min-height: calc(100vh - 6.2rem);
padding: 1% 8%;
background: #fff;
}
.detail-box2{
min-height: calc(100vh - 6.2rem);
padding: 1% 8%;
background: #000;
}
.en{
font-size: .3rem;
margin-bottom: .2rem;
line-height: 1.5;
}
.title{
font-size: .44rem;
position: relative;
padding-bottom: .3rem;
margin-bottom: .3rem;
font-weight: bold;
line-height: 0.48rem;
font-family: "宋体","粗体";
p:nth-child(1){
font-size: .28rem;
margin-bottom: .2rem;
line-height: 1.5;
font-weight: 400;
font-family: "宋体",'宋体-简',"常规体";
}
}
.title:before {
width: .92rem;
height: .04rem;
content: '';
background: #856B44;
position: absolute;
font-weight: bold;
left: 0;
bottom: 0;
}
.des{
font-size: .22rem;
line-height: .34rem;
color: #666666;
font-style: normal;
font-family: "宋体",'宋体-简',"常规体";
}
.detail-demo1{
width: 100%;
display: flex;
justify-content: space-between;
margin: 0.5rem 0;
align-items: center;
.demo1-imgBox{
width: 50%;
margin: 0 0 0.4rem;
img{
border: 0;
width: 100%;
max-height: 100%;
vertical-align: middle;
}
}
.demo1-txtBox{
color: #000;
width: 33.3%;
}
}
.detail-demo3{
margin: 3% 0;
display: flex;
justify-content: space-between;
align-items: center;
.demo3-left{
margin-bottom: 5%;
width: 31%;
img{
border: 0;
max-width: 100%;
vertical-align: middle;
}
}
.demo3-txtBox{
text-align: center;
width: 31%;
}
.title:before {
left: 50%;
margin-left: -.46rem;
}
.demo3-right{
margin-top: 22%;
width: 31%;
text-align: right;
img{
border: 0;
max-width: 100%;
vertical-align: middle;
}
}
}
.detail-demo4{
margin: 3% 0;
display: flex;
justify-content: space-between;
flex-direction: column;
align-items: center;
.demo4-down{
margin-top: 3%;
width: 31%;
img{
border: 0;
max-width: 100%;
vertical-align: middle;
}
}
.demo4-txtBox{
text-align: center;
width: 31%;
}
.title:before {
left: 50%;
margin-left: -.46rem;
}
.demo4-top{
width: 31%;
text-align: right;
img{
border: 0;
max-width: 100%;
vertical-align: middle;
}
}
}
.detail-demo5{
margin: 3% 0;
display: flex;
justify-content: space-between;
flex-direction: column;
align-items: center;
box-sizing: border-box;
.demo5-txtBox{
word-wrap: break-word;
text-align: center;
width: 31%;
}
.title:before {
left: 50%;
margin-left: -.46rem;
}
.demo5-top{
width: 31%;
text-align: right;
img{
border: 0;
max-width: 100%;
vertical-align: middle;
}
}
.des{
text-align: center;
}
}
.detail-demo7{
margin: 3% 0;
display: flex;
align-items: center;
.demo7-left{
width: 50%;
height: 100%;
box-sizing: border-box;
background: #f5f5f5;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 8% 0;
box-sizing: border-box;
}
.demo7-txtBox{
width: 60%;
}
.demo7-right{
width: 50%;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
img{
border: 0;
max-width: 80%;
vertical-align: middle;
}
}
}
.Default{
margin: 10% 0;
display: flex;
justify-content: center;
align-items: center;
.default-box{
font-weight: 500;
h5{
font-size: 32px;
}
p{
font-size: 32px;
}
}
}
@media screen and (max-width: 750px) {
.detail-top{
padding-top: 2.1rem;
padding-bottom: 4.1rem;
}
.detail-txtBox{
color: #fff;
position: absolute;
top: 55%;
left: 50%;
text-align: center;
transform: translate(-50%,-50%);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.detail-brief{
font-size: .20rem !important;
line-height: .38rem !important;
color: #000;
margin-bottom: 7%;
}
.detail-demo1{
display: flex;
flex-direction: column;
align-items: center;
padding: 3% 0;
.demo1-txtBox{
width: 100%;
text-align: center;
}
.demo1-imgBox{
width: 100%;
}
.title:before {
left: 50%;
margin-left: -.46rem;
}
}
.detail-demo3{
display: flex;
flex-direction: column;
padding: 3% 0;
.demo3-left{
width: 100%;
}
.demo3-right{
width: 100%;
}
.demo3-txtBox{
width: 100%;
text-align: center;
}
.title:before {
left: 50%;
margin-left: -.46rem;
}
}
.detail-demo4{
padding: 3% 0;
.demo4-txtBox{
width: 100%;
text-align: center;
}
.title:before {
left: 50%;
margin-left: -.46rem;
}
.demo4-down{
width: 100%;
}
.demo4-top{
width: 100%;
}
}
.detail-demo5{
padding: 3% 0;
.demo5-txtBox{
width: 100%;
text-align: center;
}
.title:before {
left: 50%;
margin-left: -.46rem;
}
.demo5-top{
width: 100%;
}
}
.detail-demo7{
display: flex;
flex-direction: column;
padding: 3% 0;
.demo7-left{
width: 100%;
}
.demo7-right{
width: 100%;
margin: 5% 0;
}
}
.Default{
min-height: calc(100vh - 7.6rem);
}
}
import React,{ useState,useEffect }from 'react'
import styles from './JewelryDetail.module.scss'
import Header from '../../components/Header/Header'
import Footer from '../../components/Footer/Footer'
import { useParams,useNavigate } from 'react-router-dom';
import { getProductInfo,getProductList } from '../../store/modules/jewelry'
interface TempItem {
template: number,
title: string,
content: string,
image: string,
component: Function
}
interface RuleData {
temp: TempItem[],
catname: string,
title: string,
brief: string,
is_bg: number,
header_img: string,
brief_position:number
priv: number,
next: number
}
interface DataItem {
current_page: string,
total: number,
total_page: number,
list: {
id: number,
title: string,
title_en: string,
cat_id: number,
is_line: number
}[]
catname: string
}
export default function JewelryDetail() {
const navigate = useNavigate();
const searchParams = new URLSearchParams(window.location.search);
const id = searchParams.get('id') as string;
const lcid = searchParams.get('lcid') as string;
const [listData, setListData] = useState<DataItem>({
current_page: '',
total: 0,
total_page: 0,
list: [],
catname: ''
})
const [DetailData, setDetailData] = useState<RuleData>({
temp: [],
catname: '',
title: '',
brief: '',
is_bg: 0,
header_img: '',
brief_position:0,
priv: 0,
next: 0
})
const DetailDemo1 = (obj:TempItem)=>{
return(
<div className={styles['detail-demo1']}>
<div className={styles['demo1-txtBox']}>
{/* <div className={styles['en']}>RurikoinTemple· Maple</div>
<h5 className={styles['title']}>琉璃光院·枫 手镯</h5> */}
<div className={styles['title']} dangerouslySetInnerHTML={{ __html: obj.title }}></div>
<div className={styles['des']} dangerouslySetInnerHTML={{ __html: obj.content }}>
</div>
</div>
<div className={styles['demo1-imgBox']}>
<img src={obj.image[0]} alt="" />
</div>
</div>
)
}
const DetailDemo2 = (obj:TempItem)=>{
return(
<div className={styles['detail-demo1']}>
<div className={styles['demo1-imgBox']}>
<img src={obj.image[0]} alt="" />
</div>
<div className={styles['demo1-txtBox']}>
<div className={styles['title']} dangerouslySetInnerHTML={{ __html: obj.title }}></div>
<div className={styles['des']} dangerouslySetInnerHTML={{ __html: obj.content }}>
</div>
</div>
</div>
)
}
const DetailDemo3 = (obj:TempItem)=>{
return(
<div className={styles['detail-demo5']}>
<div className={styles['demo5-top']}>
<img src={obj.image[0]} alt="" />
</div>
<div className={styles['demo5-txtBox']}>
<div className={styles['title']} dangerouslySetInnerHTML={{ __html: obj.title }}></div>
<div className={styles['des']} dangerouslySetInnerHTML={{ __html: obj.content }}>
</div>
</div>
</div>
)
}
const DetailDemo4 = (obj:TempItem)=>{
return(
<div className={styles['detail-demo3']}>
<div className={styles['demo3-left']}>
<img src={obj.image[0]} alt="" />
</div>
<div className={styles['demo3-txtBox']}>
<div className={styles['title']} dangerouslySetInnerHTML={{ __html: obj.title }}></div>
<div className={styles['des']} dangerouslySetInnerHTML={{ __html: obj.content }}>
</div>
</div>
<div className={styles['demo3-right']}>
<img src={obj.image[1]} alt="" />
</div>
</div>
)
}
const DetailDemo5 = (obj:TempItem)=>{
return(
<div className={styles['detail-demo7']}>
<div className={styles['demo7-left']}>
<div className={styles['demo7-txtBox']}>
<div className={styles['title']} dangerouslySetInnerHTML={{ __html: obj.title }}></div>
<div className={styles['des']} dangerouslySetInnerHTML={{ __html: obj.content }}>
</div>
</div>
</div>
<div className={styles['demo7-right']}>
<img src={obj.image[0]} alt="" />
</div>
</div>
)
}
const DetailDemo6 = (obj:TempItem)=>{
return(
<div className={styles['detail-demo5']}>
<div className={styles['demo5-txtBox']}>
<div className={styles['title']} dangerouslySetInnerHTML={{ __html: obj.title }}></div>
</div>
<div className={styles['demo5-top']}>
<img src={obj.image[0]} alt="" />
</div>
<div className={styles['des']} dangerouslySetInnerHTML={{ __html: obj.content }}>
</div>
</div>
)
}
const DetailDemo7 = (obj:TempItem)=>{
return(
<div className={styles['detail-demo4']}>
<div className={styles['demo4-top']}>
<img src={obj.image[0]} alt="" />
</div>
<div className={styles['demo4-txtBox']}>
<div className={styles['title']} dangerouslySetInnerHTML={{ __html: obj.title }}></div>
<div className={styles['des']} dangerouslySetInnerHTML={{ __html: obj.content }}>
</div>
</div>
<div className={styles['demo4-down']}>
<img src={obj.image[1]} alt="" />
</div>
</div>
)
}
const Default = ()=>{
return(
<div className={styles.Default}>
<div className={styles['default-box']}>
<h5>敬请期待</h5>
<p>Please stay tuned</p>
</div>
</div>
)
}
const arr = [
{
type:1,
component:DetailDemo1
},
{
type:2,
component:DetailDemo2
},
{
type:3,
component:DetailDemo3
},
{
type:4,
component:DetailDemo4
},
{
type:5,
component:DetailDemo5
},
{
type:6,
component:DetailDemo6
},
{
type:7,
component:DetailDemo7
},
{
type:0,
component:Default
},
]
const NextPage =()=>{ //下一页
if(listData.list.length <= 1) return
const index = listData.list.findIndex((item:any)=>item.id === Number(id))
let addNum = index + 1
if(addNum >= listData.list.length){
addNum = 0
}
navigate(`/JewelryDetail?id=${listData.list[addNum].id}&lcid=${lcid}`);
}
const PrevPage =()=>{ //上一页
if(listData.list.length <= 1) return
const index = listData.list.findIndex((item:any)=>item.id === Number(id))
let addNum = index - 1
if(addNum < 0){
addNum = listData.list.length-1
}
navigate(`/JewelryDetail?id=${listData.list[addNum].id}&lcid=${lcid}`);
}
useEffect(() => {
getProductInfo(id as string).then(res => {
if (res.data.code === 200) {
let { temp,catname,title,brief,is_bg,header_img,
brief_position
,priv,next} = res.data.data
temp = temp.map((item:TempItem)=>({
...item,
component:arr.filter(itcm=>itcm.type === item.template)[0]?.component || Default
}))
setDetailData({
temp,catname,title,brief,is_bg,header_img,
brief_position
,priv,next
})
}
})
getProductList(lcid as string).then(res => {
if (res.data.code === 200) {
const { current_page,total,total_page,list,catname} = res.data.data
setListData({
current_page,total,total_page,list,catname
})
}
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [id])
return (
<div className={styles.detailBody}>
<Header></Header>
<div className={styles['detail-top2']}>
{DetailData.header_img?<img className={styles['detail-img']} src={DetailData.header_img} alt="" />:
''}
{listData.list.length <= 1?'':<div onClick={()=>PrevPage()} className={styles['detail-prevBtn']}>
</div>}
{listData.list.length <= 1?'':<div onClick={()=>NextPage()} className={styles['detail-nextBtn']}>
</div>}
<div className={styles['detail-txtBox']}>
{DetailData.catname}
<p>{DetailData.title}</p>
{DetailData.brief_position !==0?<div className={styles['detail-text-brief']} dangerouslySetInnerHTML={{ __html: DetailData.brief }}></div>:''}
</div>
</div>
<div className={DetailData.is_bg === 0?styles['detail-box']:styles['detail-box2']}>
{DetailData.brief_position !==0?'':<div className={styles['detail-brief']} dangerouslySetInnerHTML={{ __html: DetailData.brief }}></div>}
{DetailData.temp.length === 0?DetailData.catname?Default():'':DetailData.temp.map((item,index)=>{
return (<div key={index}>{item.component(item)}</div>)
})}
</div>
<Footer></Footer>
</div>
)
}
.listBody{
width: 100%;
min-height: 100vh;
background: #000;
}
.jewelry-top{
background-color: #000;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
padding-bottom: 4.1rem;
}
.jewelry-brandVid{
min-height: calc(100vh - 8.2rem);
padding: 1rem 4%;
background: #fff;
}
.jewelry-box{
border-left: 1px solid #000;
padding: .3rem 0 .3rem .55rem;
}
.jewelry-year{
padding: .2rem 0;
position: relative;
font-size: .4rem;
color: #000;
font-weight: bold;
font-family: "宋体",'宋体-简',"常规体";
}
.jewelry-year::before {
width: .29rem;
height: .26rem;
content: '';
background: url(../../assets/images/zs.png) no-repeat;
background-size: contain;
background-position: center center;
position: absolute;
left: -.7rem;
top: 0;
z-index: 1;
}
.jewelry-year::after {
width: .29rem;
height: .20rem;
content: '';
background-size: contain;
background-position: center center;
position: absolute;
left: -.7rem;
top: 0.03rem;
background: #fff;
}
.jewelry-listBox{
li{
float: none;
width: 100%;
margin-bottom: 2%;
padding-bottom: 2%;
border-bottom: 1px solid #ddd;
margin-right: 8%;
margin-top: .2rem;
font-family: "宋体",'宋体-简',"常规体";
}
h5 {
font-weight: normal;
color: #000;
font-size: .2rem;
cursor: pointer;
overflow: hidden; /* 确保超出的内容会被裁剪 */
white-space: nowrap; /* 确保文本在一行内显示 */
text-overflow: ellipsis; /* 超出的文本部分显示为省略号 */
font-family: "宋体",'宋体-简',"常规体";
}
h5:hover{
color: #8d7249;
}
}
@media screen and (max-width: 750px){
.jewelry-brandVid{
padding: 1rem 4%;
background: #fff;
min-height: calc(100vh - 8.2rem);
}
}
\ No newline at end of file
import React, { useEffect, useState } from 'react'
import styles from './JewelryList.module.scss'
import Header from '../../components/Header/Header'
import Footer from '../../components/Footer/Footer'
import { useNavigate,useParams } from 'react-router-dom';
import { getProductList } from '../../store/modules/jewelry'
export default function JewelryList() {
const { id } = useParams();
interface DataItem {
current_page: string,
total: number,
total_page: number,
list: {
id: number,
title: string,
title_en: string,
cat_id: number,
is_line: number
}[]
catname: string
}
const [listData, setListData] = useState<DataItem>({
current_page: '',
total: 0,
total_page: 0,
list: [],
catname: ''
})
const navigate = useNavigate();
const handleButtonClick = (url:string) => {
// 使用 navigate() 方法进行路由跳转
navigate(url);
};
useEffect(() => {
getProductList(id as string).then(res => {
if (res.data.code === 200) {
const { current_page,total,total_page,list,catname} = res.data.data
setListData({
current_page,total,total_page,list,catname
})
}
})
}, [id])
return (
<div className={styles.listBody}>
<Header titleObj={{title:'艺术珠宝',name:'ART JEWEL'}}></Header>
<div className={styles['jewelry-top']}>
</div>
<div className={styles['jewelry-brandVid']}>
<div className={styles['jewelry-box']}>
<div className={styles['jewelry-year']}>
{listData.catname}
</div>
<ul className={styles['jewelry-listBox']}>
{listData.list.length>0?listData.list.map(item=>{
return(
<li onClick={()=>handleButtonClick(`/JewelryDetail?id=${item.id}&lcid=${id}`)} key={item.id}>
<h5>{item.title}·{item.title_en}</h5>
</li>
)
}):listData.catname?<li>
<h5>敬请期待</h5>
</li>:''
}
</ul>
</div>
</div>
<Footer></Footer>
</div>
)
}
.LayoutBody{
position: relative;
overflow: hidden;
background: #000;
}
.Outlet{
box-sizing: border-box;
}
.rightBody{
width: 100vw;
position: fixed;
left: 0;
top: 0;
z-index:999;
height: 100vh;
overflow: hidden;
transition:height 0.6s,width 0.6s;
}
.rightBody2{
position: fixed;
left: 0;
top: 0;
z-index:999;
height: 0;
}
.rightBody3{
position: fixed;
left: 0;
top: 0;
z-index:999;
width:0;
}
.leftBody{
transition:left 0.6s;
width: 80px;
position: fixed;
left: 0px;
top: 0;
z-index:1000;
height: 100vh;
background-color: #000;
cursor: pointer;
&:hover{
.burger-burger{
width: 0;
height: 0;
&:before{
top:-4px;
width: 16px;
transform: rotate(-30deg);
}
&:after{
top:4px;
width: 16px;
transform: rotate(30deg);
}
}
}
}
.leftBody2{
position: fixed;
left: -80px;
top: 0;
}
.return{
font-size: 0.20rem;
color: #fff;
text-align: center;
line-height: 4;
cursor: pointer;
}
.burger-container{
position: absolute;
width: 25px;
height: 25px;
top: 17px;
left: 28px;
pointer-events: all;
}
.burger-logo{
position: absolute;
top: 67px;
left: 28px;
color: #fff;
font-size: 0.32rem;
font-family: Baskerville;
}
.burger-burger{
position: absolute;
top: 15px;
left: 0;
width: 20px;
height: 1px;
background-color: #8d7249;
transition: all .5s;
&:before,
&:after {
position: absolute;
top: 15px;
left: 0;
width: 20px;
height: 1px;
background-color: #8d7249;
transition: all .5s;
}
&:before{
top: -6px;
content: "";
width: 25px;
display: block;
}
&:after{
top: 6px;
content: "";
width: 25px;
display: block;
}
}
.burger-cross-box{
position: fixed;
width: 25px;
height: 25px;
top: 17px;
left: 28px;
pointer-events: all;
z-index: 1000;
cursor: pointer;
}
.burger-cross{
position: fixed;
top: 17px;
left: 28px;
width: 20px;
height: 0px;
background-color: #8d7249;
&:before,
&:after {
position: absolute;
top: 15px;
left: 0;
width: 20px;
height: 1px;
background-color: #8d7249;
transition: all .5s;
}
&:before{
top: 15px;
left: 0;
-webkit-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
transform: rotate(-45deg);
content: "";
width: 25px;
display: block;
}
&:after{
top: 15px;
left: 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
content: "";
width: 25px;
display: block;
}
}
.burger-text{
font-size: 0.14rem;
color: #fff;
position: absolute;
top: 50%;
left: 0;
transform: rotate(-90deg) translate(-50%,0);
text-align: center;
width: 100%;
font-family: "宋体",'宋体-简',"常规体";
}
@media screen and (max-width: 750px) {
.rightBody{
width: 100%;
position: fixed;
left: 0px;
top: 0px;
z-index:999;
height: calc(100vh - 70px);
overflow-y: auto;
transition:top 0.5s;
height: 100vh;
}
.Outlet{
box-sizing: border-box;
padding-left: 0;
}
.leftBody{
transition:top 0.5s;
width: 100%;
position: fixed;
left: 0px;
top: 0;
z-index:1000;
height: 70px;
background-color: #000;
}
.rightBody2{
position: fixed;
left: 0;
top: 100%;
z-index:999;
}
.leftBody2{
position: fixed;
left: 0;
top: -70px;
}
.burger-logo{
display: none;
}
.burger-text{
font-size: 0.14rem;
color: #fff;
text-align: center;
position: absolute;
top: 0;
left: 0;
line-height: 70px;
width: 100%;
font-family: "宋体",'宋体-简',"常规体";
transform: rotate(0) translate(0,0);
}
}
\ No newline at end of file
import React,{useRef,useState, useEffect} from 'react'
import { Outlet } from 'react-router-dom'
import styles from './Layout.module.scss'
import Nav from '../../components/Nav/Nav'
import { useLocation,matchRoutes } from 'react-router-dom';
import { routes } from '../../router'
export let MyContext:any;
export default function Layout() {
const location = useLocation();
const [open, setOpen] = useState<boolean>(location.pathname==='/');
const [open2, setOpen2] = useState<boolean>(false);
const [name, setName] = useState<string>('');
const divRef = useRef<HTMLDivElement>(null);
interface MethodsFunc {
myMethod: Function
}
const myMethod = () => {
setOpen(true);
}
MyContext = React.createContext<MethodsFunc>({ myMethod });
const openChange = (newValue:boolean) => {
setTimeout(() => {
setOpen(newValue);
}, 300);
};
useEffect(() => {
let time:any= undefined
// if (divRef.current) {
// // 修改样式
// divRef.current.style.opacity = '1';
// }
// if(location.pathname === '/'){
// setOpen(true);
// setOpen2(false)
// }else{
// time = setTimeout(()=>{
// if (divRef.current) {//把Nav组件变成none
// // 修改样式
// divRef.current.style.opacity = '0';
// }
// setOpen2(true)
// },1000) //一秒后Nav组件从宽为0改成高为0
// }
if(location.pathname === '/'){
setOpen(true);
}
const matchs = matchRoutes(routes, location)
if (Array.isArray(matchs)) {
const title = matchs[matchs.length - 1].route.meta?.title as string
setName(title)
}
return () => {
clearTimeout(time)
}
}, [location, location.pathname])
return (
<div className={styles.LayoutBody}>
<div className={styles.Outlet}>
<Outlet></Outlet>
</div>
{/* {!open?'':<div onClick={()=>{setOpen(false)}} className={styles['burger-cross-box']}>
{location.pathname==='/'?'':<div className={styles['burger-cross']}></div>}
</div>} */}
{/* <div onClick={()=>setOpen(true)} className={!open?`${styles.leftBody}`:`${styles.leftBody} ${styles.leftBody2}`}>
<div className={styles['burger-container']}>
<div className={styles['burger-burger']}></div>
</div>
<div className={styles['burger-logo']}>
C
</div>
<div className={styles['burger-text']}>
{name}
</div>
</div>
<div ref={divRef} className={open?`${styles.rightBody}`:open2?`${styles.rightBody} ${styles.rightBody2}`:`${styles.rightBody} ${styles.rightBody3}`}>
<Nav onChange={openChange}></Nav>
</div> */}
<div className={open?`${styles.rightBody}`:`${styles.rightBody} ${styles.rightBody2}`}>
<Nav onChange={openChange}></Nav>
</div>
</div>
)
}
\ No newline at end of file
.mediaDetail{
width: 100%;
padding: 20px 0;
.container{
max-width: 677px;
margin: 0 auto;
position: relative;
.title{
font-size: 22px;
line-height: 1.4;
margin-bottom: 14px;
}
.info{
margin-bottom: 22px;
line-height: 20px;
font-size: 0;
word-wrap: break-word;
-webkit-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
.meta{
color: rgba(0, 0, 0, .3);
display: inline-block;
vertical-align: middle;
margin: 0 10px 10px 0;
font-size: 15px;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
.richName{
display: inline-block;
vertical-align: middle;
margin: 0 10px 10px 0;
font-size: 15px;
-webkit-tap-highlight-color: rgba(0,0,0,0);
position: relative;
cursor: pointer;
}
}
.content{
width: 100%;
background-color: #000;
min-height: 300px;
img{
border: 0;
width: 100%;
min-height: 100%;
vertical-align: middle;
}
}
.wx-fixed{
position: fixed;
right: 230px;
top: 60px;
width: 140px;
padding: 16px;
border: 1px solid #D9DADC;
background-color: #fff;
word-wrap: break-word;
-webkit-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
box-sizing: border-box;
img{
width: 102px;
height: 102px;
}
p{
font-size: 14px;
line-height: 20px;
text-align: center;
}
}
}
}
.profile_inner{
background-color: #fff;
min-height: 100px;
overflow: hidden;
width: 535px;
.left{
float: left;
img{
width: 100px;
height: 100px;
}
}
.right{
float: right;
width: 400px;
.profile_name{
display: block;
margin-top: 12px;
font-size: 16px;
font-weight: 400;
}
.profile_meta{
overflow: hidden;
.label{
float: left;
width: 4.3em;
margin-right: 1em;
}
.val{
display: block;
overflow: hidden;
color: #adadad;
}
}
}
}
@media screen and (max-width: 750px) {
.mediaDetail{
width: 100%;
padding: 20px 20px;
box-sizing: border-box;
}
.wx-fixed{
display: none;
}
}
\ No newline at end of file
import React, { useState, useEffect } from 'react'
import styles from './MediaDetail.module.scss'
import { Popover } from 'antd';
export default function MediaDetail() {
const [pcShow, setPcShow] = useState(true)
const content = (
<div className={styles.profile_inner}>
<div className={styles.left}>
<img src={require('../../assets/images/wx-1.bmp')} alt="" />
</div>
<div className={styles.right}>
<div className={styles.profile_name}>CHRISTINE DAI 艺术珠宝</div>
<div className={styles.profile_meta}>
<span className={styles.label}>微信号</span>
<span className={styles.val}>gh_0a5ef2c4f94a</span>
</div>
<div className={styles.profile_meta}>
<span className={styles.label}>功能介绍</span>
<span className={styles.val}>Christine Dai致力于“自然绮境,瑰丽典藏”的艺术珠宝,将光影感、雕塑感、生命力诠释于作品之中。宝石的瑰影塑造出雕塑的质感,这是继承与创作的交融,生长与生命的延续,Christine Dai的作品呈现着艺术的无限可能性。</span>
</div>
</div>
</div>
);
const handleWindowResize = () => {
setPcShow(window.innerWidth < 1024 ? false : true)
};
useEffect(() => {
window.addEventListener('resize', handleWindowResize); //监听页面变化控制显示隐藏
return () => window.removeEventListener('resize', handleWindowResize);
}, []);
return (
<div className={styles.mediaDetail}>
<div className={styles.container}>
<div className={styles.title}>巴黎时装周|CHRISTINE DAI 艺术珠宝再续璀璨篇章</div>
<div className={styles.info}>
<span className={styles.meta}>CHRISTINE DAI</span>
{ pcShow ?
<Popover placement="bottom" content={content} trigger="click">
<span className={`${styles.richName}`}>CHRISTINE DAI 艺术珠宝</span>
</Popover> :
<span className={`${styles.richName}`}>CHRISTINE DAI 艺术珠宝</span>
}
<span className={styles.meta}>2023-10-04 21:00</span>
<span className={styles.meta}>上海</span>
</div>
<div className={styles.content}>
<img src={require('../../assets/images/docImg.jpg')} alt="" />
</div>
{ pcShow && <div className={styles['wx-fixed']}>
<img src={require('../../assets/images/wx-2.jpg')} alt=""/>
<p>
微信扫一扫<br/>
关注该公众号</p>
</div> }
</div>
</div>
)
}
.storyBody{
width: 100%;
min-height: 100vh;
box-sizing: border-box;
}
.story-banner{
background-color: #000;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
padding-bottom: 4.1rem;
position: relative;
}
.story-banner h5{
font-family: "宋体",'宋体-简',"常规体";
color: #fff;
position: absolute;
bottom: 33%;
left: 7%;
font-size: .4rem;
line-height: 1.4;
box-sizing: border-box;
span{
color: #8D7249;
}
}
.story-warp{
background: #fff;
padding: 7% 0 0 0;
}
.story-brand{
width: 100%;
padding: 0 4%;
box-sizing: border-box;
font-size: 0.14rem;
margin-bottom: 3%;
// font-weight: bold;
.brand-text{
width: 28%;
margin-left: auto;
}
.brand-title{
padding-top: .5rem;
position: relative;
margin-bottom: .4rem;
font-family: "宋体",'宋体-简',"常规体";
}
.brand-title:before {
width: .6rem;
height: .06rem;
content: '';
background: #333333;
position: absolute;
top: 0;
left: 0;
}
.brand-title h1{
display: block;
font-size: 2em;
font-weight: bold;
unicode-bidi: isolate;
line-height: 2;
}
.brand-title h2{
display: block;
font-size: 1.5em;
line-height: 1.5;
font-weight: bold;
unicode-bidi: isolate;
font-family: "宋体",'宋体-简',"常规体";
}
.brand-btn{
display: flex;
justify-content: space-between;
.btn-img{
width: 64%;
margin-top: 0.05rem;
img{
border: 0;
width: 100%;
vertical-align: middle;
overflow-clip-margin: content-box;
overflow: clip;
}
}
.btn-txtBox{
width: 28%;
p{
color: #333;
font-size:0.13rem;
line-height:1.5;
margin-bottom: 0.3rem;
}
}
}
}
.story-word{
width: 100%;
position: relative;
padding: 1.4rem 0.8rem;
margin-bottom: 7%;
background: #f9f9f9;
box-sizing: border-box;
font-size: .4rem;
position: relative;
display: flex;
align-items: center;
justify-content: center;
font-weight: 400;
color: #806c48;
// img{
// border: 0;
// max-width: 100%;
// vertical-align: middle;
// }
}
.story-art{
width: 100%;
padding: 0 4%;
box-sizing: border-box;
font-size: 0.14rem;
margin-bottom: 5%;
.art-text{
width: 73%;
margin-left: auto;
}
.art-title{
padding-top: .5rem;
position: relative;
margin-bottom: .4rem;
}
.art-title:before {
width: .6rem;
height: .06rem;
content: '';
background: #333333;
position: absolute;
top: 0;
left: 0;
}
.art-title h1{
display: block;
font-size: 2em;
font-weight: bold;
unicode-bidi: isolate;
line-height: 2;
}
.art-title h2{
display: block;
font-size: 1.5em;
line-height: 1.5;
font-weight: bold;
unicode-bidi: isolate;
}
.art-btn{
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
.art-btn-img{
width: 19.5%;
margin-top: 0.05rem;
img{
border: 0;
max-width: 100%;
vertical-align: middle;
overflow-clip-margin: content-box;
overflow: clip;
}
}
.art-txtBox{
width: 73%;
p{
color: #333;
font-size:0.13rem;
line-height:1.5;
margin-bottom: 0.3rem;
}
}
}
.art-intr{
padding: .8rem 16%;
background: #f9f9f9;
}
.intr-txt{
position: relative;
padding: 0 .8rem;
font-size: 0.18rem;
line-height: .4rem;
text-align: center;
color: #666666;
:after{
width: .51rem;
height: .39rem;
content: '';
position: absolute;
background: url(../../assets/images/i2.png) no-repeat;
background-size: contain;
bottom: 0;
right: 0;
transform: rotate(180deg);
}
:before{
top: 0;
left: 0;
width: .51rem;
height: .39rem;
content: '';
position: absolute;
background: url(../../assets/images/i2.png) no-repeat;
background-size: contain;
}
p{
color: #8b7149;
}
.intr-name{
color: #8b7149;
text-align: right;
display: flex;
justify-content: flex-end;
}
}
}
.story-bom{
width: 100%;
height:3.5rem;
img{
border: 0;
max-width: 100%;
min-width: 100%;
max-height: 100%;
min-height: 100%;
vertical-align: middle;
overflow-clip-margin: content-box;
overflow: clip;
}
}
@media screen and (max-width: 750px) {
.story-brand{
.brand-text{
width: 100%;
margin-left: auto;
}
.brand-btn{
display: block;
.btn-img{
width: 100%;
text-align: center;
margin-bottom: .3rem;
}
.btn-txtBox{
width: 100%;
p{
color: #333;
font-size:12px;
line-height:1.5;
margin-bottom: 0.3rem;
}
}
}
}
.story-word{
width: 90%;
padding: 0.4rem;
margin: 0 5% 7%;
box-sizing: border-box;
font-size: 0.18rem;
display: flex;
text-align: center;
justify-content: center;
}
.story-art{
.art-text{
width: 100%;
margin-left: auto;
}
// .art-title h2{
// font-size: 2.5em;
// }
// .art-title h2{
// font-size: 1.8em;
// }
.art-btn{
display: block;
.art-btn-img{
width: 100%;
text-align: center;
margin-bottom: .3rem;
}
.art-txtBox{
width: 100%;
p{
color: #333;
font-size:12px;
line-height:1.5;
margin-bottom: 0.3rem;
}
}
}
.art-intr{
padding: .4rem;
background: #f9f9f9;
.intr-txt{
position: relative;
padding: 0 1.4rem;
font-size: 12px;
line-height: .50rem;
text-align: left;
:after{
width: .71rem;
height: .59rem;
content: '';
position: absolute;
background: url(../../assets/images/i2.png) no-repeat;
background-size: contain;
bottom: 0;
right: 0.3rem;
transform: rotate(180deg);
}
:before{
top: 0;
left: 0.3rem;
width: .71rem;
height: .59rem;
content: '';
position: absolute;
background: url(../../assets/images/i2.png) no-repeat;
background-size: contain;
}
}
.intr-name{
font-size: 12px;
}
}
}
.story-bom{
width: 100%;
height:1.8rem;
img{
border: 0;
max-width: 100%;
min-width: 100%;
max-height: 100%;
min-height: 100%;
vertical-align: middle;
overflow-clip-margin: content-box;
overflow: clip;
}
}
}
\ No newline at end of file
This diff is collapsed. Click to expand it.
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