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
.contactUs-body{
width: 100%;
min-height: 100vh;
background: #000;
}
.option-size{
font-size: .2rem !important;
}
.contactUs{
min-height: calc(100vh - 0.3rem);
.cu-container{
display: flex;
flex-direction: column;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
transition: .5s;
.itemBox{
height: 4rem;
position: relative;
border-top: 1px dashed rgba(255,255,255,.5);
transition: .5s;
overflow: hidden;
&:first-child{
border-top: 0;
}
.wrapper{
-webkit-transform: translate3d(-50%,-50%,0);
transform: translate3d(-50%,-50%,0);
text-align: center;
position: absolute;
top: 50%;
left: 50%;
width: 100%;
color: #fff;
font-family: "宋体",'宋体-简',"常规体";
.title{
font-size: .35rem;
margin-bottom: 0.3rem;
line-height: 1;
font-weight: normal;
white-space: nowrap;
}
.content{
-webkit-transition: all .5s cubic-bezier(.39,.575,.565,1);
transition: all .5s cubic-bezier(.39,.575,.565,1);
opacity: 1;
-webkit-transform: translateY(0.5px);
-ms-transform: translateY(.5px);
transform: translateY(0.5px);
height: 1.5rem;
p{
font-size: .18rem;
}
.mg-25{
margin: 0.15rem 0 0.25rem;
}
}
}
}
}
.contact-form{
min-height: 4rem;
background-color: #fff;
.title{
font-family: "宋体",'宋体-简',"常规体";
padding-top: .1rem;
text-align: center;
font-size: .35rem;
color: #000;
margin-bottom: 0.2rem;
position: relative;
padding-top: .2rem;
span{
display: none;
font-size: .16rem;
color: #999;
position: absolute;
top: 50%;
font-weight: bold;
transform: translateY(-50%);
left: 0;
}
}
:global {
.ant-select:not(.ant-select-customize-input) .ant-select-selector {
border: none;
border-bottom: 1px solid #eee;
font-size: .14rem;
padding: 3px 0;
span{
padding: 0;
}
}
.ant-form-item .ant-form-item-label >label{
font-size: .2rem;
}
.ant-select-arrow{
font-size: .15rem;
right: .2rem;
}
.ant-select-clear{
font-size: .15rem;
right: .2rem;
}
}
.txt{
border: none;
border-bottom: 1px solid #eee;
font-size: .14rem;
padding: 3px 0;
span{
padding: 0;
}
}
.cbox{
display: flex;
justify-content: space-between;
align-items: center;
.sub{
display: block;
font-size: .2rem;
color: #000;
border-color: #fff;
bottom: 0 !important;
&:after{
background-color: #000;
}
&:before{
background-color: #000;
}
}
}
}
.c-btn{
font-size: .2rem;
cursor: pointer;
position: relative;
padding: 0.03rem 0;
display: inline-block;
bottom: .5rem;
&: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);
}
}
}
.contactUs-top{
display: none;
}
@keyframes enlarge {
0%{
right: -60%;
}
100%{
right: 0;
}
}
@keyframes cu-narrow {
0%{
height: calc(100vh - 0.8rem);
}
100%{
height: 6.8rem;
}
}
@keyframes narrow {
0%{
width: 33.3%;
}
100%{
width: 0;
}
}
@keyframes selectDiv{
0%{
width: 33.3%;
}
100%{
width: 40%;
}
}
@keyframes enlarge-leave {
0%{
right: 0;
}
100%{
right: -60%;
}
}
@keyframes narrow-leave {
0%{
flex: 0;
}
100%{
flex: 1;
}
}
@media screen and (max-width: 750px) {
.contactUs-top{
display: block;
height: 1.5rem;
width: 100vw;
background: #000;
}
.hide{
overflow: hidden;
position: relative;
}
.option-size{
font-size: .25rem !important;
}
.contactUs{
min-height: calc(100vh - 2.1rem);
background-color: #000;
.cu-narrow{
animation-name: cu-narrow !important;
}
.cu-container{
// padding-top: 1.4rem;
background: none !important;
position: relative;
height: calc(100vh - 0.8rem);
display: flex;
overflow: hidden;
animation-name: none;
animation-duration: 1s;
animation-fill-mode: forwards;
.narrow{
flex: 0 !important;
}
.itemBox{
flex: 1;
width: 100%;
border: none !important;
.bgBox{
// position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
display: block;
background-size: cover;
background-position: center;
}
.hideWarp{
display: none;
}
.wrapper{
.title{
font-size: .4rem;
margin-bottom: .5rem;
}
.txt{
p{
font-size: .25rem !important;
}
}
.c-btn{
font-size: .3rem !important;
bottom: 0;
}
}
}
}
.showForm{
position: static !important;
}
.contact-form{
background-color: #fff;
position: absolute;
top: calc(100vh + .8rem);
width: 100%;
transition: .5s;
.txt{
font-size: .25rem;
}
:global {
.ant-select:not(.ant-select-customize-input) .ant-select-selector {
font-size: .25rem;
}
.ant-form-item .ant-form-item-label >label{
font-size: .25rem;
}
.ant-select-arrow{
font-size: .25rem;
}
}
.cbox{
.sub{
font-size: .3rem;
}
}
}
}
}
@media screen and (min-width: 1024px) {
.option-size{
font-size: .15rem !important;
}
.contactUs{
.cu-container{
overflow: hidden;
flex-direction: row;
.enlarge{
animation-name: enlarge !important;
padding: 1% .2rem;
}
.narrow{
animation-name: narrow !important;
}
.selectDiv{
animation-name: selectDiv !important;
}
.enlarge-leave{
animation-name: enlarge-leave !important;
}
.narrow-leave{
animation-name: narrow-leave !important;
}
.showWrapper{
-webkit-transition-delay: 0s;
transition-delay: 0s;
-webkit-transform: translate3d(-50%,-50%,0) !important;
transform: translate3d(-50%,-50%,0) !important;
}
.showContent{
-webkit-transition-delay: 0s;
transition-delay: 0s;
opacity: 1 !important;
max-height: none;
}
.itemBox{
width: 33.3%;
height: 100vh;
border-top: 0;
border-left: 1px solid hsla(0,0%,100%,.2);
animation-name: none;
animation-duration: 1s;
animation-fill-mode: forwards;
&:first-child{
border-left: 0;
}
&:hover .wrapper {
-webkit-transition-delay: 0s;
transition-delay: 0s;
-webkit-transform: translate3d(-50%,-50%,0);
transform: translate3d(-50%,-50%,0);
}
&:hover .wrapper .content {
-webkit-transition-delay: 0s;
transition-delay: 0s;
opacity: 1 !important;
max-height: none;
}
.wrapper{
text-align: center;
-webkit-transform: translate(-50%);
-ms-transform: translate(-50%);
/* transform: translate(-50%); */
position: absolute;
top: 50%;
left: 50%;
width: 100%;
color: #fff;
-webkit-transition: all .5s cubic-bezier(.39,.575,.565,1);
transition: all .5s cubic-bezier(.39,.575,.565,1);
.title{
font-size: .4rem !important;
line-height: 1;
margin-bottom: .8rem;
font-weight: normal;
white-space: nowrap;
}
.content{
-webkit-transition: all .5s cubic-bezier(.39,.575,.565,1);
transition: all .5s cubic-bezier(.39,.575,.565,1);
opacity: 0;
-webkit-transform: translateY(0.5px);
-ms-transform: translateY(.5px);
transform: translateY(0.5px);
.mg-25{
margin: 0.25rem 0 0.5rem;
}
p{
font-size: .14rem;
}
.c-btn{
font-size: .16rem;
}
}
}
}
}
.contact-form{
width: calc(60% - .4rem);
height: 100vh;
position: absolute;
right: -60%;
background-color: #fff;
animation-name: none;
animation-duration: 1s;
animation-fill-mode: forwards;
span{
display: inline-block !important;
z-index: 999;
cursor: pointer;
}
:global {
.ant-select:not(.ant-select-customize-input) .ant-select-selector {
font-size: .15rem;
}
.ant-form-item .ant-form-item-label >label{
font-size: .2rem;
}
.ant-select .ant-select-dropdown-menu-item{
font-size: 1rem;
}
}
}
}
}
\ No newline at end of file
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
import React,{useState,useEffect} from 'react'
import styles from './Story.module.scss'
import { getBrandInfos } from '../../store/modules/home'
import Header from '../../components/Header/Header'
import Footer from '../../components/Footer/Footer'
import SlideInFromBottom from '../../components/SlideInFromBottom/SlideInFromBottom'
export default function Story() {
interface RuleData {
title: string
intro: string
brand_img: string
brief:string
founder_title:string
founder_intro:string
founder_img:string
founder_message:string
bottom_img:string
}
const [storyData, setStoryData] = useState<RuleData>({
title: '',
intro: '',
brand_img: '',
brief:'',
founder_title:'',
founder_intro:'',
founder_img:'',
founder_message:'',
bottom_img:''
})
useEffect(() => {
getBrandInfos().then(res => {
if (res.data.code === 200) {
const { title, intro, brand_img,brief,founder_title,founder_intro,founder_img,founder_message,bottom_img} = res.data.data
setStoryData({
title, intro, brand_img,brief,founder_title,founder_intro,founder_img,founder_message,bottom_img
})
}
})
}, [])
// const [isVisible, setIsVisible] = useState(false);
// setTimeout(()=>{
// setIsVisible(true)
// },2000)
return (
<div className={styles.storyBody}>
<Header titleObj={{title:'品牌故事',name:'BRAND STORY'}}></Header>
<div className={styles['story-banner']}>
<h5>
<span>BRAND STORY</span>
<br/>
品牌故事
</h5>
</div>
<div className={styles['story-warp']}>
<SlideInFromBottom isVisible={true}>
<div className={styles['story-brand']}>
<div className={styles['brand-text']}>
<div className={styles['brand-title']}>
<h1 dangerouslySetInnerHTML={{ __html: storyData.title }}>
</h1>
<h2>
品牌故事<br/>
BRAND STORY
</h2>
</div>
</div>
<div className={styles['brand-btn']}>
<div className={styles['btn-img']}>
<img src={storyData.brand_img} alt="" />
</div>
<div className={styles['btn-txtBox']}>
<p dangerouslySetInnerHTML={{ __html: storyData.intro }}>
</p>
{/* <p>
Christine Dai的珠宝艺术品,融合了古董珠宝的玫瑰切割工艺和18世纪欧洲传统的古典蜡雕技术。无论是在金属的质感、宝石的色彩还是镶嵌结构上,都彰显出Christine Dai对于珠宝艺术的极致追求。
</p>
<p>
Christine Dai’s jewelry artworks integrate the rose-cut techniques of antique jewelry with the traditional wax carving techniques of 18th-century Europe. Whether in metal texture, gem’s colors, or the structure of the setting, they all reflect Christine Dai’s ultimate pursuit of jewelry art.
</p>
<p>
Christine Dai汲取传统和当代的艺术文化之美,跨越时间和空间的边界,将宝石的魅力以更饱满的艺术形式呈现。所创造的每一件珠宝作品都具有独特的生命力和艺术灵性,成为承载着历史、文化与美的典藏艺术品。
</p>
<p>
Christine Dai draws on the aesthetics of traditional and contemporary art, and culture and crosses the boundaries of time and space to present the charm of gemstones in a more comprehensive art form. With unique vitality and artistic spirituality, each piece of jewelry of her creation has turned into a collectable artwork and carrier of history, culture, and aesthetics.
</p>
<p>
在全球珠宝界中,Christine Dai凭借着独特的艺术风格和至臻工艺,备受艺术品藏家的关注。无论是在艺术的广度还是工艺深度上,Christine Dai的珠宝都展现出瑰丽典藏、隽永于世的价值。
</p>
<p>
Across the global jewelry art field, Christine Dai is highly sought after by art collectors for her unique artistic style and craftsmanship. In terms of artistic breadth and depth, Christine Dai’s jewelry comprises a magnificent collection of timeless value.
</p> */}
</div>
</div>
</div>
</SlideInFromBottom>
<div className={styles['story-word']} dangerouslySetInnerHTML={{ __html: storyData.brief }}>
</div>
<div className={styles['story-art']}>
<div className={styles['art-text']}>
<div className={styles['art-title']}>
<h1 dangerouslySetInnerHTML={{ __html: storyData.founder_title }}>
</h1>
<h2>
艺术家<br/>
ARTIST
</h2>
</div>
</div>
<div className={styles['art-btn']}>
<div className={styles['art-btn-img']}>
<img src={storyData.founder_img || require('../../assets/images/art.jpg')} alt="" />
</div>
<div className={styles['art-txtBox']} dangerouslySetInnerHTML={{ __html: storyData.founder_intro }}>
{/* <p>
Christine Dai,一位宝石艺术家,出生于中国香港。自幼沉浸于艺术氛围的熏陶。她深感艺术的力量,以宝石多维度的美传递了生命的张力。为了表达对自然与艺术之美的追求,她以宝石为载体,呈现艺术的无限可能性。
</p>
<p>
Born in Hong Kong, China, Christine Dai is a gemstone art who has grown up in an artistic atmosphere. She deeply believes in the power of art to transmit the multi-dimensional beauty of gemstones and the tension of life. To express the pursuit of beauty in nature and art, she uses gemstones as a vehicle to present the infinite possibilities of art.
</p>
<p>
Christine Dai巧妙地将这些自然魅力融入到宝石创作之中。她善于运用材料的质感,探索并突破多种精湛工艺,关注每一颗宝石的生长结构。以宝石为画笔,诠释着自己对艺术和生命的理解。Christine Dai的作品融合了古朴的玫瑰切割工艺和18世纪欧洲传统的古典蜡雕技术。无论是在金属的质感、宝石的色彩还是镶嵌结构上,都呈现了作品的光影感、雕塑感和生命力。
</p>
<p>
Christine Dai skillfully incorporates the charms of nature into her gemstone creations. She uses the texture of the material, explores and breaks through the various techniques, and pays attention to the growth structure of each gemstone. Using gemstones as a brush to interpret her understanding of art and life, Christine Dai's work combines the rose-cut techniques of antique jewelry with the classical wax carving techniques of 18th century European tradition. Whether in the texture of the metal, the color of the gemstones or the structure of the setting, the pieces present a sense of light, sculpture and life.
</p>
<p>
2021年,Christine Dai开设了中国大陆第间艺术珠宝私人鉴赏室,为热爱艺术的人们提供了沉浸式感官体验,近距离欣赏珠宝艺术之绮华绚丽。
</p>
<p>
In 2021, Christine Dai opened the first Art Jewellery Private Appreciation Room in Mainland China, offering art lovers an immersive sensory experience and a close-up view of the splendour of jewellery art.
</p> */}
</div>
</div>
<div className={styles['art-intr']}>
<div className={styles['intr-txt']}>
<p dangerouslySetInnerHTML={{ __html: storyData.founder_message }}>
</p>
<div className={styles['intr-name']}>
<div dangerouslySetInnerHTML={{ __html: storyData.founder_title }}></div>
</div>
<p>
<br/>
</p>
</div>
</div>
</div>
<div className={styles['story-bom']}>
<img src={storyData.bottom_img} alt="" />
</div>
</div>
<Footer></Footer>
</div>
)
}
.TheJewelry{
width: 100%;
min-height: 100vh;
display: flex;
}
.theJewelry-left{
min-height: 100vh;
width: 50%;
background: #000;
box-sizing: border-box;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
}
.theJewelry-left2{
min-height: 100vh;
width: 50%;
background: #26122b;
box-sizing: border-box;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
}
.theJewelry-right{
min-height: 100vh;
width: 50%;
background: #fff;
box-sizing: border-box;
color: #000;
display: flex;
justify-content: center;
align-items: center;
}
.theJewelry-txt{
text-align: right;
cursor: pointer;
h3{
font-family: Baskerville;
font-size: 0.56rem;
line-height: 64px;
letter-spacing: 2px;
font-style: normal;
}
h4{
font-family: "宋体",'宋体-简',"常规体";
font-size: 0.36rem;
letter-spacing: 1px;
font-style: normal;
line-height: 50px;
}
}
@media screen and (max-width: 750px) {
.TheJewelry{
display: flex;
flex-direction: column;
}
.theJewelry-left,
.theJewelry-left2,
.theJewelry-right{
width: 100%;
min-height: 50vh;
}
}
\ No newline at end of file
import React,{ useEffect, useState } from 'react'
import styles from './TheJewelry.module.scss'
import Header from '../../components/Header/Header'
import { useNavigate,useParams } from 'react-router-dom';
import { getProductCat } from '../../store/modules/jewelry'
import Footer from '../../components/Footer/Footer'
export default function TheJewelry() {
interface DataItem {
id: string,
title:string,
title_en: string
}
const [leftObj, setLeftObj] = useState<DataItem>({
id: '',
title:'',
title_en: ''
})
const [RightObj, setRightObj] = useState<DataItem>({
id: '',
title:'',
title_en: ''
})
const { id } = useParams();
const navigate = useNavigate();
const handleButtonClick = (url:string) => {
// 使用 navigate() 方法进行路由跳转
navigate(url);
};
useEffect(() => {
getProductCat(id as string).then(res => {
if (res.data.code === 200) {
if(res.data.data[0]){
const {id,title,title_en} = res.data.data[0]
setLeftObj({id,title,title_en})
}else{
setLeftObj({id:'',title:'',title_en:''})
}
if(res.data.data[1]){
const {id,title,title_en} = res.data.data[1]
setRightObj({id,title,title_en})
}else{
setRightObj({id:'',title:'',title_en:''})
}
}
})
}, [id])
return (
<div>
<Header type></Header>
<div className={styles.TheJewelry}>
<div className={id==='3'?styles['theJewelry-left2']:styles['theJewelry-left']}>
<div onClick={()=>handleButtonClick(`/JewelryList/${leftObj.id}`)} className={styles['theJewelry-txt']}>
<h3>{leftObj.title_en}</h3>
<h4>{leftObj.title}</h4>
</div>
</div>
<div className={styles['theJewelry-right']}>
<div onClick={()=>handleButtonClick(`/JewelryList/${RightObj.id}`)} className={styles['theJewelry-txt']}>
<h3>{RightObj.title_en}</h3>
<h4>{RightObj.title}</h4>
</div>
</div>
</div>
<Footer></Footer>
</div>
)
}
.videoBody{
background-color: #000;
.bgVideo{
width: 100%;
height: 100vh;
top: 0;
left: 0;
-o-object-fit: cover;
object-fit: cover;
opacity: 1;
position: absolute;
background-color: #000;
z-index: 1;
figure{
width: 100%;
height: 100%;
top: 0;
left: 0;
/* opacity: .5; */
position: absolute;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
z-index: 3;
}
}
.video-container{
padding: 0 6%;
margin: .5rem 0 auto;
display: flex;
width: 100%;
justify-content:space-between;
box-sizing: border-box;
min-height: calc(100vh - 0.5rem);
z-index: 2;
align-items: center;
.box{
width: 30%;
z-index: 2;
margin-top: 0.2rem;
&:last-child{
margin-right: 0;
}
.imgBox{
cursor: pointer;
overflow: hidden;
figure{
padding-bottom: 2.34rem;
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
transition: 4s;
}
figure:hover {
transform: scale(1.2); /* 鼠标移入时将图片放大为原来的1.2倍 */
}
}
h3{
width: 100%;
margin: 0.15rem 0 0.05rem;
font-size: .22rem;
color: #fff;
position: relative;
text-align: center;
font-family: "宋体",'宋体-简',"常规体";
}
h4{
width: 100%;
font-size: .16rem;
color: #8d7249;
position: relative;
text-align: center;
font-family: "宋体",'宋体-简',"常规体";
}
}
}
}
@media screen and (max-width: 750px) {
.video-container{
min-height: 100vh !important;
flex-direction: column;
margin-top: 0 !important;
// padding-bottom: 1rem !important;
justify-content: center !important;
.box{
width: 100% !important;
margin-right: 0 !important;
margin-bottom: 5%;
.imgBox{
figure{
padding-bottom: 39% !important;
background-size: contain;
}
}
}
}
}
\ No newline at end of file
import React, { useState, useEffect } from 'react'
import styles from './Video.module.scss'
import Header from '../../components/Header/Header'
import Footer from '../../components/Footer/Footer'
import { useNavigate } from 'react-router-dom';
import { getVideoCat } from '../../store/modules/video'
type videoType = {
id: number,
parent_id: number,
cover: string,
title_en: string,
title: string
}
export default function Video() {
const navigate = useNavigate();
const handleButtonClick = (url:string) => {
// 使用 navigate() 方法进行路由跳转
navigate(url);
};
const [bgWord, setBgWord] = useState({
backgroundImage: 'url('+require('../../assets/images/video-gb.png')+')'
})
const [videoData, setVideoData] = useState<videoType[]>([])
useEffect(()=>{
getVideoCat().then(res=>{
if(res.data.code===200){
console.log(res.data.data);
setVideoData([...res.data.data])
}
})
},[])
return (
<div className={styles.videoBody}>
<Header titleObj={{title:'品牌视频',name:'VIDEO'}}></Header>
<div className={styles.bgVideo}>
<video className={styles.bgVideo} autoPlay loop muted>
<source src={require('../../assets/images/v-bg-video.mp4')} type="video/mp4" />
您的浏览器不支持视频标签。
</video>
{/* <figure style={bgWord}></figure> */}
</div>
<div className={styles['video-container']}>
{ videoData.length > 0 && videoData.map((item: videoType,index: number)=>(
<div onClick={()=>handleButtonClick(`/WholeVideo/${item.id}`)} className={styles.box} key={index}>
<div className={styles.imgBox}>
<figure style={{backgroundImage: `url(${item.cover})`}}></figure>
</div>
<h3>{item.title}</h3>
<h4>{item.title_en}</h4>
</div>
)) }
</div>
{/* <Footer></Footer> */}
</div>
)
}
.videoBody{
width: 100%;
min-height: 100vh;
background: #000;
}
.videoDetail{
background: #000;
padding: 1.5rem 5% 0.5rem;
h3{
// margin: .7rem 0;
color: #fff;
font-size: .2rem;
line-height: 1.5;
}
}
.videoPlay{
video{
background-color: #000;
max-width: 100%;
width: 100%;
box-sizing: border-box;
margin-bottom: 0.5rem;
height: 6rem;
}
}
@media screen and (max-width: 750px) {
.videoDetail{
background: #000;
padding: 2.5rem 5% 0.5rem;
}
.videoDetail{
min-height: calc(100vh - 5.1rem);
}
}
import React from 'react'
import styles from './VideoDetail.module.scss'
import Header from '../../components/Header/Header'
import Footer from '../../components/Footer/Footer'
export default function VideoDetail() {
const searchParams = new URLSearchParams(window.location.search);
const url = searchParams.get('url') as string;
return (
<div className={styles.videoBody}>
<Header></Header>
<div className={styles.videoDetail}>
<div className={styles.videoPlay}>
<video controls autoPlay loop muted>
<source src={url} />
您的浏览器不支持视频标签。
</video>
</div>
{/* <h3>
“瑰丽蝴蝶”启幕|巴黎装饰博物馆
<br/>
CHRISTINE DAI's "Magnificent Butterfly"
<br/>
exhibition |Musee des arts decoratifs
</h3> */}
</div>
<Footer></Footer>
</div>
)
}
.wholeVideo{
box-sizing: border-box;
.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;
}
}
}
.wv-main{
padding: 1rem 8%;
display: grid;
grid-template-columns: repeat(3, 1fr); /* 12 columns */
gap: 10px; /* Grid gap */
background: #000;
}
.wv-container{
padding: 1rem 4%;
background: #000;
.itemBox{
// border-left: 1px solid #fff;
padding: 0.3rem 0 0.3rem 0.55rem;
margin-bottom: 1rem;
.title{
padding: 0.2rem 0;
position: relative;
font-size: .4rem;
color: #fff;
font-weight: bold;
font-family: "宋体",'宋体-简',"常规体";
// &:before{
// width: 0.29rem;
// height: 0.26rem;
// content: '';
// background: url(../../assets/images/zs.png) no-repeat;
// background-size: contain;
// background-position: center center;
// position: absolute;
// left: -0.7rem;
// top: 0;
// }
}
.listBox{
display: flex;
flex-wrap: wrap;
}
}
}
.wv-container2{
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
li{
display: flex;
width: 62%;
justify-content: center;
cursor: pointer;
margin-bottom: 1.1rem;
}
.video-left{
background: #fff;
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;
}
.video-text{
font-size: 22px;
line-height: 3.15rem;
text-align: center;
}
}
.video-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;
.video-date{
font-family: "宋体",'宋体-简',"常规体";
font-weight: bold;
font-size: 0.42rem;
color: #8E734A;
line-height: 93px;
letter-spacing: 4px;
text-align: left;
font-style: normal;
}
.video-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;
}
}
.video-icon{
width: 0.3rem;
height: 0.3rem;
}
// .itemBox{
// // border-left: 1px solid #fff;
// padding: 0.3rem 0 0.3rem 0.55rem;
// margin-bottom: 1rem;
// .title{
// padding: 0.2rem 0;
// position: relative;
// font-size: .4rem;
// color: #fff;
// font-weight: bold;
// font-family: "宋体",'宋体-简',"常规体";
// // &:before{
// // width: 0.29rem;
// // height: 0.26rem;
// // content: '';
// // background: url(../../assets/images/zs.png) no-repeat;
// // background-size: contain;
// // background-position: center center;
// // position: absolute;
// // left: -0.7rem;
// // top: 0;
// // }
// }
// .listBox{
// display: flex;
// flex-wrap: wrap;
// }
// }
}
.card{
width: 90%;
// margin-right: 2%;
margin-top: 0.2rem;
cursor: pointer;
&:hover h5{
color: #8d7249;
}
&:hover .imgBox:after {
top: 45%;
}
// &:nth-child(3n){
// margin-right: 0;
// }
.imgBox{
position: relative;
margin-bottom: 0.4rem;
&:before{
width: 100%;
height: 100%;
content: '';
background: rgba(0,0,0,.5);
position: absolute;
top: 0;
left: 0;
}
&:after{
width: 0.34rem;
height: 0.38rem;
content: '';
background: url(../../assets//images//play.png) no-repeat;
background-size: contain;
background-position: center center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition: .5s;
}
img{
border: 0;
width: 100%;
height: 1.8rem;
vertical-align: middle;
}
}
h4{
min-height: 1.2em;
margin-bottom: 0.05rem;
text-align: center;
font-weight: normal;
color: #8d7249;
font-size: .18rem;
white-space: nowrap;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
overflow: hidden;
white-space: pre-wrap;
}
h5{
color: #fff;
font-size: .16rem;
white-space: nowrap;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
overflow: hidden;
white-space: pre-wrap;
line-height: 1.7;
}
}
.card2{
width: 28%;
margin-right: 3%;
margin-top: 0.2rem;
cursor: pointer;
&:hover h5{
color: #8d7249;
}
&:hover .imgBox:after {
top: 45%;
}
// &:nth-child(3n){
// margin-right: 0;
// }
.imgBox{
position: relative;
margin-bottom: 0.4rem;
&:before{
width: 100%;
height: 100%;
content: '';
background: rgba(0,0,0,.5);
position: absolute;
top: 0;
left: 0;
}
&:after{
width: 0.34rem;
height: 0.38rem;
content: '';
background: url(../../assets//images//play.png) no-repeat;
background-size: contain;
background-position: center center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition: .5s;
}
img{
border: 0;
width: 100%;
height: 1.8rem;
vertical-align: middle;
}
}
h4{
min-height: 1.2em;
margin-bottom: 0.05rem;
text-align: center;
font-weight: normal;
color: #8d7249;
font-size: .18rem;
white-space: nowrap;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
overflow: hidden;
white-space: pre-wrap;
}
h5{
color: #fff;
font-size: .16rem;
line-height: 1.5;
white-space: nowrap;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
overflow: hidden;
white-space: pre-wrap;
font-family: "宋体",'宋体-简',"常规体";
}
}
.m-b-2{
margin-bottom: 0.2rem !important;
}
.m-b-4{
margin-bottom: 0.4rem !important;
}
.center{
text-align: center;
}
}
.ContainerBody{
position: relative;
}
.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;
height: 0.2rem;
width: 1rem;
color: #fff;
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;
color: #fff;
height: 0.2rem;
width: 1rem;
color: #fff;
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%);
}
}
.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;
height: 0.2rem;
width: 1rem;
color: #fff;
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;
color: #fff;
height: 0.2rem;
width: 1rem;
color: #fff;
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) {
.doc-nav{
display: none !important;
}
.doc-nav-fix{
display: none !important;
}
.wv-container{
min-height: calc(100vh - 8rem);
display: flex;
flex-direction: column;
}
.wv-container2{
min-height: calc(100vh - 8rem);
display: flex;
flex-direction: column;
}
.wv-main{
min-height: calc(100vh - 8rem);
display: grid;
grid-template-columns: repeat(2, 1fr) !important; /* 两列,每列自动宽度 */
grid-gap: 10px; /* 格子间隔 */
padding: 10px; /* 容器内边距 */
}
.card{
max-width: 3rem;
}
.card2{
width: 42% !important;
.imgBox{
img{
height: 1.4rem !important;
}
}
}
}
\ No newline at end of file
import React, { useState, useEffect } from 'react'
import styles from './WholeVideo.module.scss'
import Header from '../../components/Header/Header'
import Footer from '../../components/Footer/Footer'
import { useParams } from 'react-router-dom'
import { useNavigate } from 'react-router-dom';
import { getVideoList,getVideoEventList } from '../../store/modules/video'
import { Console } from 'console'
interface videoData{
title: string,
title_en:string,
cover_img: string,
video_url: string,
event_date?:string,
top?: number
}
interface classData<T>{
catname: string,
list: T[],
top?: number
}
export default function WholeVideo() {
const { id } = useParams();
const [docList, setDocList] = useState<classData<videoData>[]>([])
const [docList2, setDocList2] = useState<videoData[]>([])
const [eventList, setEventList] = useState<videoData[]>([])
const [navNumber,setNavNumber] = useState<Number>(0)
const [isFix,setIsFix] = useState<boolean>(false)
const [bannerData, setBannerData] = useState({
title: '',
word: ''
})
const navigate = useNavigate();
const handleButtonClick = (url:string) => {
// 使用 navigate() 方法进行路由跳转
navigate(`/videoDetail?url=${url}`);
};
let docListBak: classData<videoData>[] = []
let docListBak2: videoData[] = []
const [videoData, setVideoData] = useState<classData<videoData>[]>([])
const cutNav = (i:number, top: number) =>{
console.log(i,top,'top')
window.scrollTo({
top,
behavior: 'smooth'
})
setTimeout(() => {
setNavNumber(i)
}, 700)
}
const getElementPageY = () => {
const docUI: HTMLUListElement = document.querySelector('#containerUI') 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 + 200
}
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 + 200) >= scrollY) {
setNavNumber(i)
return false
}
}
}
const getElementPageY2 = () => {
const docUI: HTMLUListElement = document.querySelector('#containerUI2') as HTMLUListElement
const children = docUI.querySelectorAll('li')
for (let i = 0; i < children.length; i++ ) {
docListBak2[i].top = children[i].offsetTop + 200
}
setDocList2(docListBak2)
}
const listenerULList2 = () => {
const scrollY = window.scrollY
const len = docListBak2.length
if(scrollY>300){
setIsFix(true)
}else{
setIsFix(false)
}
for (let i = 0; i < len; i++) {
if (((docListBak2[i].top) as number + 200) >= scrollY) {
setNavNumber(i)
return false
}
}
}
const ContainerBody = () =>{
return (
<div className={styles.ContainerBody}>
<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)} key={index} className={navNumber === index?styles.active:styles.navItem}>
<div className={styles.activeText}>{item.catname}</div>
</li>)
})}
</ul>
</div>
<ul className={styles['wv-container']} id="containerUI">
{ videoData.length > 0?videoData.map((item,index)=>(
<li className={styles.itemBox} key={index}>
<div className={styles.title}>{ item.catname }</div>
<div className={styles.listBox} >
{ item.list.length > 0 ? item.list.map((em, i)=>(
<div onClick={()=>handleButtonClick(em.video_url)} className={styles.card2} key={i}>
<div className={`${styles.imgBox} ${styles['m-b-2']}`}>
<img src={em.cover_img} alt=""/>
</div>
<h5>{em.title}</h5>
<h5>{em.title_en}</h5>
</div>
)): <div className={styles.card2}>
<h5>敬请期待</h5>
</div>}
</div>
</li>
)) :<li className={styles.itemBox}>
<div className={styles.title}>敬请期待</div>
</li>}
</ul>
</div>
)
};
const RecordBody = () =>{ //纪事视频
console.log(navNumber,'navNumber')
return (
<div className={styles.ContainerBody}>
<div className={isFix?styles['doc-nav-fix']:styles['doc-nav']}>
<div className={styles['nav-line']}></div>
<ul className={styles['nav-list']}>
{docList2.map((item,index)=>{
return(<li onClick={()=>cutNav(index, item.top as number)} key={index} className={navNumber === index?styles.active:styles.navItem}>
<div className={styles.activeText}>{item.event_date}</div>
</li>)
})}
</ul>
</div>
<ul className={styles['wv-container2']} id="containerUI2">
{ eventList.length > 0?eventList.map((item,index)=>(
<li onClick={()=>handleButtonClick(item.video_url)} className={styles.itemBox} key={index}>
<div className={styles['doc-left']}>
<img src={item.cover_img} alt="" />
</div>
<div className={styles['video-right']}>
<p className={styles['video-date']}>{item.event_date}</p>
<div className={styles['video-title']} dangerouslySetInnerHTML={{ __html: item.title }}></div>
<img className={styles['video-icon']} src={require('../../assets/images/rightIcon.png')} alt="" />
</div>
</li>
)) :
<li className={styles.itemBox}>
<div className={styles.title}>敬请期待</div>
</li>}
</ul>
</div>
)
};
useEffect(()=>{
if(id==='3'){
getVideoEventList().then(res=>{
const {data} = res.data || []
docListBak2 = [...data]
setDocList2([...data])
setEventList([...data])
setTimeout(() => {
getElementPageY2()
window.addEventListener('scroll', listenerULList2, false)
})
})
}else{
getVideoList({id}).then(res=>{
if(res.data.code===200){
const { con, title, title_en } = res.data.data
docListBak = [...con]
setDocList([...con])
setVideoData([...con])
setBannerData({
title,
word: title_en
})
if(id==='1'){
setTimeout(() => {
getElementPageY()
window.addEventListener('scroll', listenerULList, false)
})
}
}
})
}
return () => {
window.removeEventListener('scroll', listenerULList, false)
}
},[id])
return (
<div className={styles.wholeVideo}>
<Header titleObj={{title:'品牌视频',name:'VIDEO'}}></Header>
<div className={styles.banner}>
<h5>
<span>{bannerData.word}</span>
<br/>
{bannerData.title}
</h5>
</div>
{
id==='4' ?
<div className={styles['wv-main']}>
{ videoData[0]?.list.length > 0 && videoData[0].list.map((em, i)=>(
<div onClick={()=>handleButtonClick(em.video_url)} className={`${styles.card} ${styles['m-b-4']}`} key={i}>
<div className={`${styles.imgBox} ${styles['m-b-2']}`}>
<img src={em.cover_img} alt=""/>
</div>
<h5>{em.title}</h5>
<h5>{em.title_en}</h5>
</div>
)) }
</div> :
id==='1'?ContainerBody():RecordBody()
}
<Footer></Footer>
</div>
)
}
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
]
}
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