提交 2733a9dc 作者: 曹云

Merge branch 'master' of ssh://git.mmcuav.cn:8222/root/sharefly-web-nextjs into caoyun

import useSWR, { SWRResponse } from "swr";
/**
* 请求封装
* @param url 请求url
* @param method 请求方法类型
* @param data 请求的参数
* @returns Promise<Response>
*/
export default function request(url: string, method: String = 'get', data: any): SWRResponse {
let options = {};
switch (method.toLowerCase()) {
case 'get':
let params = new URLSearchParams();
if (data) {
Object.keys(data).forEach((key) => {
params.append(key, data[key]);
})
url += params;
}
break;
case 'post':
options = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}
break;
}
const fetcher = (url:string) => fetch(url, options)
.then((r) => r.json())
.then((data) => {
return data;
});
return useSWR(url, fetcher)
}
\ No newline at end of file
export default "data:image/png;base64,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";
.navHeader { .navHeader {
width: 100%; width: 100%;
height: 68px; height: 68px;
background: linear-gradient(360deg, #002157 0%, #00102c 100%); background: #fff;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
.ant-tabs-tab-btn { .ant-tabs-tab-btn {
font-size: 16px; font-size: 16px;
color: #fff; color: #000;
width: 90px; width: 90px;
text-align: center; text-align: center;
font-family: MicrosoftYaHei; font-family: MicrosoftYaHei;
...@@ -49,7 +49,6 @@ ...@@ -49,7 +49,6 @@
:global .ant-tabs-tab-active { :global .ant-tabs-tab-active {
.ant-tabs-tab-btn { .ant-tabs-tab-btn {
color: #91c8ff !important;
} }
} }
...@@ -65,29 +64,23 @@ ...@@ -65,29 +64,23 @@
.btn1 { .btn1 {
width: 120px; width: 120px;
height: 40px; height: 40px;
background: linear-gradient(90deg, #278eff 0%, #0052da 100%); border-radius: 0px;
border-radius: 6px;
border: 0;
font-size: 16px; font-size: 16px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI; font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold; font-weight: bold;
color: #ffffff; color: #ffffff;
&:hover {
opacity: 0.8;
}
} }
.btn2 { .btn2 {
box-sizing: border-box; box-sizing: border-box;
width: 120px; width: 120px;
height: 40px; height: 40px;
border-radius: 6px; border-radius: 0px;
border: 1px solid #ffb02c; border: 1px solid rgba(255, 85, 45, 1);
font-size: 16px; font-size: 16px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI; font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold; font-weight: bold;
color: #ffb02c; color: rgba(255, 85, 45, 1);
background: none; background: none;
} }
......
import React, { useState } from 'react'; import React, { useState } from "react";
import { Avatar, Button, Space, Tabs } from 'antd'; import { Avatar, Button, Space, Tabs } from "antd";
import type { TabsProps } from 'antd'; import type { TabsProps } from "antd";
import styles from './index.module.scss'; import styles from "./index.module.scss";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import LoginModal from "~/components/loginModal";
import { useUser } from "~/lib/hooks";
const items: TabsProps['items'] = [ const items: TabsProps["items"] = [
{ {
key: '/home', key: "/home",
label: ` 首页 `, label: ` 首页 `,
}, },
{ {
key: '/jobServices', key: "/jobServices",
label: `作业服务`, label: `作业服务`,
}, },
{ {
key: '/equipmentLeasing', key: "/equipmentLeasing",
label: `设备租赁`, label: `设备租赁`,
}, },
{ {
key: '/flyingHandService', key: "/flyingHandService",
label: `飞手服务`, label: `飞手服务`,
}, },
{ {
key: '/mall', key: "/mall",
label: `产品商城`, label: `产品商城`,
}, },
{ {
key: '/projectInfo', key: "/projectInfo",
label: `项目资讯`, label: `项目资讯`,
}, },
{ {
key: '6', key: "/forum",
label: `社区论坛`, label: `社区论坛`,
}, },
]; ];
...@@ -38,33 +40,55 @@ const items: TabsProps['items'] = [ ...@@ -38,33 +40,55 @@ const items: TabsProps['items'] = [
export default function NavHeader() { export default function NavHeader() {
const router = useRouter(); const router = useRouter();
const currentPath = router.asPath; const currentPath = router.asPath;
const user = useUser();
console.log("currentHash", currentPath); console.log("currentHash", currentPath);
const onChange = (key: string) => { const onChange = (key: string) => {
console.log(key); console.log(key);
router.push(key); router.push(key);
}; };
const [isModalOpen, setIsModalOpen] = useState(false);
const showModal = () => {
setIsModalOpen(true);
};
const handleCancel = () => {
setIsModalOpen(false);
};
return ( return (
<div className={styles.navHeader}> <div className={styles.navHeader}>
<div className={styles.nav}> <div className={styles.nav}>
<div className={styles.logo}></div> <div className={styles.logo}></div>
<Tabs <Tabs
className={styles.tabs} className={styles.tabs}
defaultActiveKey={currentPath} defaultActiveKey={currentPath}
items={items} items={items}
onChange={onChange} onChange={onChange}
/> />
<Space size={16} className={styles.btns}> <Space size={16} className={styles.btns}>
<Button type='primary' className={styles.btn1}> <Button type="primary" className={styles.btn1}>
+ 发布信息 + 发布需求
</Button> </Button>
<Button className={styles.btn2}>入驻加盟</Button> <Button className={styles.btn2}>入驻加盟</Button>
</Space> </Space>
<div className={styles.haedImg}> {user ? (
<Avatar size={48} style={{ background: '#fff' }}></Avatar> <div className={styles.haedImg}>
</div> <Avatar size={48} style={{ background: "#fff" }}></Avatar>
</div>
) : (
<Button
type="text"
onClick={showModal}
style={{ fontWeight: "bold", fontSize: 16 }}
>
登录
</Button>
)}
</div> </div>
<LoginModal open={isModalOpen} onCancel={handleCancel}></LoginModal>
</div> </div>
); );
} }
import React, { Suspense } from 'react'; import React, { Suspense } from "react";
import { Layout, Space } from 'antd'; import { Layout, Space } from "antd";
import NavHeader from '~/components/NavHeader'; import NavHeader from "~/components/NavHeader";
import FooterView from '~/components/footer'; import FooterView from "~/components/footer";
const { Header, Footer, Content } = Layout; const { Header, Footer, Content } = Layout;
const headerStyle: React.CSSProperties = { const headerStyle: React.CSSProperties = {
height: 'auto', height: "auto",
background: 'none', background: "none",
padding: 0, padding: 0,
lineHeight: '1', lineHeight: "1",
position: 'relative' position: "relative",
}; };
const contentStyle: React.CSSProperties = { const contentStyle: React.CSSProperties = {
minHeight: 120, minHeight: 120,
lineHeight: '1', lineHeight: "1",
color: '', color: "",
backgroundColor: '', backgroundColor: "",
// width:1200, width: 1200,
position: 'relative', position: "relative",
margin: "0 auto" margin: "0 auto",
}; };
const footerStyle: React.CSSProperties = { const footerStyle: React.CSSProperties = {
color: '', color: "",
backgroundColor: '', backgroundColor: "",
lineHeight: '1', lineHeight: "1",
padding: 0, padding: 0,
position: 'relative' position: "relative",
marginTop: 78,
}; };
type Props = { type Props = {
children: React.ReactNode; children?: React.ReactNode;
layoutStyle?: React.CSSProperties;
}; };
export default function LayoutView(props: Props) { export default function LayoutView(props: Props) {
return ( return (
<Space direction='vertical' style={{ width: '100%' }} size={[0, 48]}> <Space direction="vertical" style={{ width: "100%" }} size={[0, 48]}>
<Layout style={{ minHeight: '100vh' }}> <Layout style={Object.assign({ minHeight: "100vh" }, props.layoutStyle)}>
<Header style={headerStyle}> <Header style={headerStyle}>
<NavHeader /> <NavHeader />
</Header> </Header>
<Content style={contentStyle}> <Content style={contentStyle}>{props.children}</Content>
{props.children}
</Content>
<Footer style={footerStyle}> <Footer style={footerStyle}>
<FooterView></FooterView> <FooterView></FooterView>
</Footer> </Footer>
......
import React, { useState } from "react";
import { Modal } from "antd";
import Image from "next/image";
type Props = {
open: boolean;
onCancel: () => void;
};
export default function loginModal(props: Props) {
return (
<>
<Modal
open={props.open}
onCancel={props.onCancel}
width={400}
footer={null}
>
<div
style={{
fontSize: 20,
fontFamily: "MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI",
fontWeight: "bold",
color: "#000",
marginTop: 17,
marginBottom: 48,
textAlign: "center",
}}
>
欢迎来到云享飞
</div>
<Image
alt=""
src=""
width={160}
height={160}
style={{ margin: "auto", display: "block" }}
></Image>
<div
style={{
marginTop: 39,
marginBottom: 52,
fontSize: 14,
fontFamily: "MicrosoftYaHei",
color: "#3E454D",
textAlign: "center",
}}
>
打开微信扫一扫
</div>
</Modal>
</>
);
}
import { serialize, parse } from "cookie";
const TOKEN_NAME = "token";
export const MAX_AGE = 60 * 60 * 8; // 8 hours
export function setTokenCookie(res, token) {
const cookie = serialize(TOKEN_NAME, token, {
maxAge: MAX_AGE,
expires: new Date(Date.now() + MAX_AGE * 1000),
httpOnly: true,
secure: process.env.NODE_ENV === "production",
path: "/",
sameSite: "lax",
});
res.setHeader("Set-Cookie", cookie);
}
export function removeTokenCookie(res) {
const cookie = serialize(TOKEN_NAME, "", {
maxAge: -1,
path: "/",
});
res.setHeader("Set-Cookie", cookie);
}
export function parseCookies(req) {
// For API Routes we don't need to parse the cookies.
if (req.cookies) return req.cookies;
// For pages we do need to parse the cookies.
const cookie = req.headers?.cookie;
return parse(cookie || "");
}
export function getTokenCookie(req) {
const cookies = parseCookies(req);
return cookies[TOKEN_NAME];
}
import Iron from "@hapi/iron";
import { MAX_AGE, setTokenCookie, getTokenCookie } from "./auth-cookies";
const TOKEN_SECRET = process.env.TOKEN_SECRET;
export async function setLoginSession(res, session) {
const createdAt = Date.now();
// Create a session object with a max age that we can validate later
const obj = { ...session, createdAt, maxAge: MAX_AGE };
const token = await Iron.seal(obj, TOKEN_SECRET, Iron.defaults);
setTokenCookie(res, token);
}
export async function getLoginSession(req) {
const token = getTokenCookie(req);
if (!token) return;
const session = await Iron.unseal(token, TOKEN_SECRET, Iron.defaults);
const expiresAt = session.createdAt + session.maxAge * 1000;
// Validate the expiration date of the session
if (Date.now() > expiresAt) {
throw new Error("Session expired");
}
return session;
}
import { useEffect } from "react";
import Router from "next/router";
import request from '~/api/request';
/*
const fetcher = (url) =>
fetch(url)
.then((r) => r.json())
.then((data) => {
return { user: data?.user || null };
}); */
export function useUser({ redirectTo, redirectIfFound } = {}) {
const { data, error } = request("/api/user");
const user = data?.user;
const finished = Boolean(data);
const hasUser = Boolean(user);
useEffect(() => {
if (!redirectTo || !finished) return;
if (
// If redirectTo is set, redirect if the user was not found.
(redirectTo && !redirectIfFound && !hasUser) ||
// If redirectIfFound is also set, redirect if the user was found
(redirectIfFound && hasUser)
) {
Router.push(redirectTo);
}
}, [redirectTo, redirectIfFound, finished, hasUser]);
return error ? null : user;
}
import Local from "passport-local";
import { findUser, validatePassword } from "./user";
export const localStrategy = new Local.Strategy(function (
username,
password,
done
) {
findUser({ username })
.then((user) => {
if (user && validatePassword(user, password)) {
done(null, user);
} else {
done(new Error("Invalid username and password combination"));
}
})
.catch((error) => {
done(error);
});
});
import crypto from "crypto";
import { v4 as uuidv4 } from "uuid";
/**
* User methods. The example doesn't contain a DB, but for real applications you must use a
* db here, such as MongoDB, Fauna, SQL, etc.
*/
const users = [];
export async function createUser({ username, password }) {
// Here you should create the user and save the salt and hashed password (some dbs may have
// authentication methods that will do it for you so you don't have to worry about it):
const salt = crypto.randomBytes(16).toString("hex");
const hash = crypto
.pbkdf2Sync(password, salt, 1000, 64, "sha512")
.toString("hex");
const user = {
id: uuidv4(),
createdAt: Date.now(),
username,
hash,
salt,
};
// This is an in memory store for users, there is no data persistence without a proper DB
users.push(user);
return { username, createdAt: Date.now() };
}
// Here you should lookup for the user in your DB
export async function findUser({ username }) {
// This is an in memory store for users, there is no data persistence without a proper DB
return users.find((user) => user.username === username);
}
// Compare the password of an already fetched user (using `findUser`) and compare the
// password for a potential match
export function validatePassword(user, inputPassword) {
const inputHash = crypto
.pbkdf2Sync(inputPassword, user.salt, 1000, 64, "sha512")
.toString("hex");
const passwordsMatch = user.hash === inputHash;
return passwordsMatch;
}
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
"devDependencies": { "devDependencies": {
"@ant-design/cssinjs": "^1.3.0", "@ant-design/cssinjs": "^1.3.0",
"@ant-design/static-style-extract": "~1.0.1", "@ant-design/static-style-extract": "~1.0.1",
"@next/font": "^13.1.1",
"@types/node": "^18.11.17", "@types/node": "^18.11.17",
"@types/react": "^18.0.26", "@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10", "@types/react-dom": "^18.0.10",
...@@ -31,7 +30,15 @@ ...@@ -31,7 +30,15 @@
}, },
"dependencies": { "dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1", "@amap/amap-jsapi-loader": "^1.0.1",
"@ant-design/icons": "^5.0.1",
"@hapi/iron": "^7.0.1",
"babel-plugin-styled-components": "^2.1.1", "babel-plugin-styled-components": "^2.1.1",
"styled-components": "^6.0.0-rc.1" "cookie": "^0.5.0",
"next-connect": "^1.0.0",
"passport": "^0.6.0",
"passport-local": "^1.0.0",
"styled-components": "^6.0.0-rc.1",
"swr": "^2.1.5",
"uuid": "^9.0.0"
} }
} }
import { PlusOutlined } from "@ant-design/icons";
import { Form, Input, Modal, Upload } from "antd";
import type { RcFile, UploadProps } from "antd/es/upload";
import type { UploadFile } from "antd/es/upload/interface";
import { useState } from "react";
const getBase64 = (file: RcFile): Promise<string> =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result as string);
reader.onerror = (error) => reject(error);
});
type Props = {
open: boolean;
onOk?: () => void;
onCancel?: () => void;
};
export default function publishMessage(props: Props) {
const [confirmLoading, setConfirmLoading] = useState(false);
const handleOk = () => {
setConfirmLoading(true);
setTimeout(() => {
handleCancel();
setConfirmLoading(false);
}, 2000);
};
const handleCancel = () => {
props.onCancel && props.onCancel();
};
const [previewOpen, setPreviewOpen] = useState(false);
const [previewImage, setPreviewImage] = useState("");
const [previewTitle, setPreviewTitle] = useState("");
const [fileList, setFileList] = useState<UploadFile[]>([
{
uid: "-1",
name: "image.png",
status: "done",
url: "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png",
},
{
uid: "-2",
name: "image.png",
status: "done",
url: "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png",
},
{
uid: "-3",
name: "image.png",
status: "done",
url: "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png",
},
{
uid: "-4",
name: "image.png",
status: "done",
url: "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png",
},
{
uid: "-xxx",
percent: 50,
name: "image.png",
status: "uploading",
url: "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png",
},
{
uid: "-5",
name: "image.png",
status: "error",
},
]);
const handlePreviewCancel = () => setPreviewOpen(false);
const handlePreview = async (file: UploadFile) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj as RcFile);
}
setPreviewImage(file.url || (file.preview as string));
setPreviewOpen(true);
setPreviewTitle(
file.name || file.url!.substring(file.url!.lastIndexOf("/") + 1)
);
};
const handleChange: UploadProps["onChange"] = ({ fileList: newFileList }) =>
setFileList(newFileList);
const uploadButton = (
<div>
<PlusOutlined />
<div style={{ marginTop: 8 }}>Upload</div>
</div>
);
return (
<Modal
title=""
open={props.open}
onOk={handleOk}
onCancel={handleCancel}
width={500}
confirmLoading={confirmLoading}
okText="发布"
okButtonProps={{ style: { height: 37, padding: "0 32px", fontSize: 16 } }}
cancelButtonProps={{ style: { display: "none" } }}
maskClosable={false}
>
<Form style={{ paddingTop: 32 }}>
<Form.Item>
<Input placeholder="输入标题" style={{ height: 44 }}></Input>
</Form.Item>
<Form.Item>
<Input.TextArea
allowClear
showCount
placeholder="输入内容"
maxLength={100}
style={{ height: 120, resize: "none" }}
></Input.TextArea>
</Form.Item>
<Form.Item>
<Upload
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
listType="picture-card"
fileList={fileList}
onPreview={handlePreview}
onChange={handleChange}
>
{fileList.length >= 8 ? null : uploadButton}
</Upload>
<Modal
open={previewOpen}
title={previewTitle}
footer={null}
onCancel={handlePreviewCancel}
>
<img alt="example" style={{ width: "100%" }} src={previewImage} />
</Modal>
</Form.Item>
</Form>
</Modal>
);
}
.forum {
width: 690px;
margin: 18px auto 88px;
}
.header {
padding: 14px 20px 14px 24px;
background-color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 5px;
.title {
font-size: 20px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold;
color: #000000;
}
.btn {
font-size: 14px;
font-family: MicrosoftYaHei;
color: #ffffff;
padding: 0px 24px;
height: 38px;
}
}
.item {
padding: 18px 0 0;
background: #ffffff;
.headImg {
margin-left: 16px;
width: 52px;
height: 52px;
background: #ced1d6;
margin-right: 10px;
border-radius: 50%;
overflow: hidden;
flex-shrink: 0;
}
.info {
.name {
font-size: 15px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold;
color: #000000;
margin-top: 7px;
margin-bottom: 16px;
}
.desc {
font-size: 15px;
font-family: MicrosoftYaHei;
color: #000000;
margin-bottom: 16px;
}
}
.imgs {
margin-bottom: 18px;
:global .img {
border-radius: 6px;
overflow: hidden;
}
}
.ctrls {
display: flex;
gap: 20px;
margin-bottom: 18px;
.ctrlsItem {
font-size: 12px;
font-family: MicrosoftYaHei;
color: #8590a6;
display: flex;
gap: 8px;
align-items: center;
cursor: pointer;
.ctrlsItemIcon {
width: 16px;
height: 16px;
background-size: 100% 100%;
&.iconComment {
background-image: url("./assets/comment.png");
}
&.iconPraise {
background-image: url("./assets/praise.png");
&.active {
background-image: url("./assets/praiseActive.png");
}
}
}
}
}
.commentWrap {
.draftWrap {
padding: 11px 27px 0 26px;
display: flex;
gap: 18px;
align-items: center;
border-top: 1px RGBA(246, 247, 249, 1) solid;
.commentHeadImg {
width: 34px;
height: 34px;
background: #ced1d6;
display: flex;
justify-content: center;
align-items: center;
font-size: 12px;
font-family: MicrosoftYaHei;
color: #828589;
border-radius: 50%;
overflow: hidden;
}
.commentInput {
width: 585px;
height: 38px;
background: #f2f3f4;
}
}
.btnCommentWrap {
margin-top: 7px;
margin-bottom: 13px;
text-align: right;
padding-right: 30px;
}
.btnComment {
height: 33px;
padding: 0 24px;
font-size: 13px;
font-family: MicrosoftYaHei;
}
.comments {
border-top: 1px RGBA(246, 247, 249, 1) solid;
border-bottom: 1px RGBA(246, 247, 249, 1) solid;
.commentItem {
padding: 8px 26px;
display: flex;
gap: 18px;
.commentHeadImg {
width: 34px;
height: 34px;
background: #ced1d6;
border-radius: 50%;
overflow: hidden;
flex-shrink: 0;
}
.info {
.nameWrap {
display: flex;
.commentName {
font-size: 14px;
font-family: MicrosoftYaHei;
color: #ff552d;
flex-shrink: 0;
line-height: 19px;
.date {
margin-top: 1px;
font-size: 12px;
font-family: MicrosoftYaHei;
color: #a6aab1;
line-height: 16px;
}
}
.commentContent {
font-size: 14px;
font-family: MicrosoftYaHei;
color: #000000;
line-height: 19px;
}
}
}
}
}
}
.showAll {
height: 50px;
font-size: 13px;
font-family: MicrosoftYaHei;
color: #000000;
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
cursor: pointer;
}
}
import { Button, Image, Space, Input, Modal, Form } from "antd";
import Layout from "~/components/layout";
import styles from "./index.module.scss";
import errImg from "~/assets/errImg";
import { RightOutlined } from "@ant-design/icons";
import { useState } from "react";
import PublishMessage from './components/publishMessage'
export default function forum() {
const [isModalOpen, setIsModalOpen] = useState(false);
const showModal = () => {
setIsModalOpen(true);
};
const onPublishCancel = () => {
setIsModalOpen(false);
};
return (
<Layout>
<div className={styles.forum}>
<div className={styles.header}>
<div className={styles.title}>最新讨论</div>
<Button className={styles.btn} type="primary" onClick={showModal}>
发布动态
</Button>
</div>
<Space direction="vertical" size={8}>
<div className={styles.item}>
<Space size={10} align="start">
<img className={styles.headImg}></img>
<div className={styles.info}>
<div className={styles.name}>给**的</div>
<div className={styles.desc}>
今天飞的航拍效果,喜欢的给我点个赞
</div>
<div className={styles.imgs}>
<Image.PreviewGroup
preview={{
onChange: (current, prev) =>
console.log(
`current index: ${current}, prev index: ${prev}`
),
}}
>
<Space size={6} wrap>
<Image
className="img"
width={132}
height={132}
src="error"
fallback={errImg}
/>
<Image
className="img"
width={132}
height={132}
src="error"
fallback={errImg}
/>
<Image
className="img"
width={132}
height={132}
src="errImg"
fallback={errImg}
/>
<Image
className="img"
width={132}
height={132}
src="errImg"
fallback={errImg}
/>
<Image
className="img"
width={132}
height={132}
src="errImg"
fallback={errImg}
/>
<Image
className="img"
width={132}
height={132}
src="errImg"
fallback={errImg}
/>
<Image
className="img"
width={132}
height={132}
src="errImg"
fallback={errImg}
/>
</Space>
</Image.PreviewGroup>
</div>
<div className={styles.ctrls}>
<div className={styles.ctrlsItem}>
<div
className={`${styles.ctrlsItemIcon} ${styles.iconComment}`}
></div>
15评论
</div>
<div className={styles.ctrlsItem}>
<div
className={`${styles.ctrlsItemIcon} ${styles.iconPraise} ${styles.active}`}
></div>
11赞
</div>
</div>
</div>
</Space>
<div className={styles.commentWrap}>
<div className={styles.draftWrap}>
<div className={styles.commentHeadImg}>自已</div>
<Input
className={styles.commentInput}
placeholder="说点什么吧,万一火了呢~"
></Input>
</div>
<div className={styles.btnCommentWrap}>
<Button type="primary" className="btnComment">
评论
</Button>
</div>
<div className={styles.comments}>
<div className={styles.commentItem}>
<div className={styles.commentHeadImg}></div>
<div className={styles.info}>
<div className={styles.nameWrap}>
<div className={styles.commentName}>
无人机爱好者111:
<div className={styles.date}>05-16</div>
</div>
<div className={styles.commentContent}>
无人机记录生活
</div>
</div>
</div>
</div>
<div className={styles.commentItem}>
<div className={styles.commentHeadImg}></div>
<div className={styles.info}>
<div className={styles.nameWrap}>
<div className={styles.commentName}>
无人机爱好者111:
<div className={styles.date}>05-16</div>
</div>
<div className={styles.commentContent}>
无人机记录生活
</div>
</div>
</div>
</div>
</div>
<div className={styles.showAll}>
查看全部15条评论
<RightOutlined size={14} />
</div>
</div>
</div>
</Space>
</div>
<PublishMessage
open={isModalOpen}
onCancel={onPublishCancel}
></PublishMessage>
</Layout>
);
}
import styles from "./index.module.scss";
import Layout from "~/components/layout";
import { Space, Image as AImage, Row, Col, Button, Divider, Badge } from "antd";
import { useState } from "react";
import { RightOutlined } from "@ant-design/icons";
import Image from "next/image";
import errImg from "~/assets/errImg";
import { useRouter } from "next/router";
export default function MallDetail() {
const [visible, setVisible] = useState(false);
const router = useRouter();
const { pid } = router.query;
return (
<Layout>
<div className="page" style={{ marginTop: 20, backgroundColor: "#fff" }}>
<div style={{ display: "none" }}>
<AImage.PreviewGroup
preview={{ visible, onVisibleChange: (vis) => setVisible(vis) }}
>
<AImage src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp" />
<AImage src="https://gw.alipayobjects.com/zos/antfincdn/cV16ZqzMjW/photo-1473091540282-9b846e7965e3.webp" />
<AImage src="https://gw.alipayobjects.com/zos/antfincdn/x43I27A55%26/photo-1438109491414-7198515b166b.webp" />
</AImage.PreviewGroup>
</div>
<Space size={30} style={{ padding: "22px 24px 0" }}>
{/* 商品图 */}
<AImage
preview={{ visible: false }}
width={200}
src="https://gw.alipayobjects.com/zos/antfincdn/LlvErxo8H9/photo-1503185912284-5271ff81b9a8.webp"
onClick={() => setVisible(true)}
/>
<Space direction="vertical" size={17}>
<div className={`${styles.font1} ${styles.ellipsis}`}>
入云龙1550
</div>
<div className={`${styles.font2} ${styles.ellipsis}`}>
交通事故处理、违章取证、指挥调度、日常巡
</div>
<Space
size={24}
direction="vertical"
style={{
padding: "24px 40px 24px 23px",
border: "1px solid #EDEDED",
width: 470,
}}
>
<Row wrap={false}>
<Col flex="60px" className={styles.font3}>
选择
</Col>
<Col
flex="auto"
className={styles.font4}
style={{ cursor: "pointer" }}
>
已选:1件
</Col>
<Col style={{ cursor: "pointer" }}>
<RightOutlined />
</Col>
</Row>
<Row wrap={false}>
<Col flex="60px" className={styles.font3}>
送至
</Col>
<Col flex="auto" style={{ cursor: "pointer" }}>
<Space size={5}>
<Image
alt=""
width={18}
height={18}
src={require("./assets/locate.png")}
></Image>
<span className={`${styles.font3} ${styles.ellipsis}`}>
广东深圳市南山区万科云城创新谷6栋A座…
</span>
</Space>
<div className={styles.font4} style={{ marginTop: 7 }}>
现货,22:00前下单,预计后天(8月30日)送达
</div>
</Col>
</Row>
</Space>
<Space size={12} style={{ marginTop: 20 }}>
<Button className={styles.btn1}>加入购物车</Button>
<Button className={styles.btn2} type="primary">
提交意向
</Button>
<Space size={20} style={{ marginLeft: 19 }}>
<div style={{ textAlign: "center", cursor: "pointer" }}>
<Image
alt=""
src={require("./assets/phone.png")}
width={20}
height={20}
></Image>
<div className={styles.font5} style={{ marginTop: 5 }}>
电话
</div>
</div>
<div style={{ textAlign: "center", cursor: "pointer" }}>
<Badge count={55} size="small">
<Image
alt=""
src={require("./assets/car.png")}
width={20}
height={20}
></Image>
</Badge>
<div className={styles.font5} style={{ marginTop: 5 }}>
购物车
</div>
</div>
</Space>
</Space>
</Space>
</Space>
<Divider className={styles.divider}>商品详情</Divider>
<div style={{ textAlign: "center" }}>
<AImage fallback={errImg} width={1080}></AImage>
</div>
</div>
</Layout>
);
}
@import "~/styles/mixins.scss";
.ellipsis {
@include ellipsis(1);
}
.font1 {
font-size: 28px;
font-weight: bold;
color: #090909;
font-family: PingFangSC-Regular, PingFang SC;
}
.font2 {
font-size: 16px;
font-weight: 400;
color: #5e5e5e;
font-family: PingFangSC-Regular, PingFang SC;
}
.font3 {
font-size: 16px;
font-weight: 400;
color: #8e8e8e;
font-family: PingFangSC-Regular, PingFang SC;
}
.font4 {
font-size: 16px;
font-weight: 400;
color: #000000;
font-family: PingFangSC-Regular, PingFang SC;
}
.font5 {
font-size: 12px;
transform: scale(0.83);
font-weight: 400;
color: #585858;
font-family: PingFangSC-Regular, PingFang SC;
}
.btn1 {
height: 40px;
width: 207px;
background: #ffe4d1;
border: 1px solid #ebbaaf;
border-radius: 0;
font-size: 16px;
font-family: MicrosoftYaHei;
color: #ff552d;
}
.btn2 {
height: 40px;
width: 207px;
border-radius: 0;
font-size: 16px;
font-family: MicrosoftYaHei;
color: #ffffff;
}
.divider {
display: flex;
justify-content: center;
margin: 46px 0 30px !important;
&::before {
width: 65px !important;
}
&::after {
width: 65px !important;
}
:global .ant-divider-inner-text {
font-size: 16px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #989898;
}
}
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
margin-left: -14px; margin-left: -14px;
.item { .item {
cursor: pointer;
width: 220px; width: 220px;
height: 326px; height: 326px;
background: #ffffff; background: #ffffff;
......
...@@ -2,8 +2,11 @@ import React, { useEffect, useState } from "react"; ...@@ -2,8 +2,11 @@ import React, { useEffect, useState } from "react";
import { Button, Select, Space, Tag } from "antd"; import { Button, Select, Space, Tag } from "antd";
import Layout from "~/components/layout"; import Layout from "~/components/layout";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import { useRouter } from "next/router";
import Image from 'next/image';
export default function Mall() { export default function Mall() {
const router = useRouter();
const [productList, setProductList] = useState(Array<{}>); const [productList, setProductList] = useState(Array<{}>);
useEffect(() => { useEffect(() => {
...@@ -186,8 +189,8 @@ export default function Mall() { ...@@ -186,8 +189,8 @@ export default function Mall() {
<ul className={styles.listWrap}> <ul className={styles.listWrap}>
{productList.map((item) => { {productList.map((item) => {
return ( return (
<li className={styles.item}> <li className={styles.item} onClick={() => router.push('/mall/detail/1')}>
<img className={styles.img}></img> <Image className={styles.img}></Image>
<div className={styles.title}> <div className={styles.title}>
入云龙ll 1550入云龙ll 1550入云龙ll 1550入云龙ll 1550 入云龙ll 1550入云龙ll 1550入云龙ll 1550入云龙ll 1550
</div> </div>
......
@import "~/styles/mixins.scss";
//项目需求
.bids {
.item {
padding: 24px 17px 24px 16px;
display: flex;
border-bottom: 1px dashed RGBA(222, 222, 222, 1);
justify-content: space-between;
align-items: center;
.info {
.title {
font-size: 16px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold;
color: #3c3e42;
width: 649px;
@include ellipsis(1);
}
.desc {
font-size: 16px;
font-family: MicrosoftYaHei;
color: #ff552d;
margin-top: 9px;
}
}
.btn {
width: 168px;
height: 40px;
border-radius: 6px;
font-size: 14px;
font-family: MicrosoftYaHei;
color: #ffffff;
background: url("./assets/btn.png");
background-size: 100% 100%;
border: none;
display: flex;
justify-content: space-between;
padding: 0;
align-items: center;
.text1 {
width: 80px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #ff4500;
text-align: left;
padding-left: 8px;
}
.text2 {
flex: 1;
text-align: center;
font-size: 16px;
font-family: MicrosoftYaHei;
color: #ffffff;
}
&.disabled {
opacity: 0.6;
}
}
}
}
import { Button } from "antd";
import styles from "./index.module.scss";
const list = [
{
isApply: false,
},
{
isApply: true,
},
];
export default function requirements() {
return (
<div className={styles.bids}>
{list.map((item, i) => {
return (
<div className={styles.item} key={i}>
<div className={styles.info}>
<div className={styles.title}>项目需求:电力巡检需要5名飞手</div>
</div>
{item.isApply ? (
<Button
type="primary"
disabled
className={`${styles.btn} ${styles.disabled}`}
>
<div className={styles.text1}>155W</div>
<div className={styles.text2}>已申请</div>
</Button>
) : (
<Button type="primary" className={styles.btn}>
<div className={styles.text1}>155W</div>
<div className={styles.text2}>申请合作</div>
</Button>
)}
</div>
);
})}
</div>
);
}
@import "~/styles/mixins.scss";
//项目需求
.casas {
padding-top: 20px;
.item {
padding: 39px 14px 39px 16px;
display: flex;
border-bottom: 1px dashed RGBA(222, 222, 222, 1);
border-bottom: 1px dashed RGBA(222, 222, 222, 1);
justify-content: space-between;
.info {
.title {
font-size: 16px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold;
color: #3c3e42;
width: 649px;
@include ellipsis(1);
}
.desc {
font-size: 16px;
font-family: MicrosoftYaHei;
color: #ff552d;
margin-top: 9px;
}
}
.btn {
width: 120px;
height: 40px;
border-radius: 6px;
font-size: 14px;
font-family: MicrosoftYaHei;
color: #ff552d;
border: 1px solid #ff552d;
}
.btnDisabled {
width: 120px;
height: 40px;
border-radius: 6px;
color: #c7cacf;
border: 1px solid #c7cacf;
}
}
}
import { Button } from "antd";
import styles from "./index.module.scss";
const list = [
{
isApply: false,
},
{
isApply: true,
},
];
export default function requirements() {
return (
<div className={styles.casas}>
{list.map((item, i) => {
return (
<div className={styles.item} key={i}>
<div className={styles.info}>
<div className={styles.title}>项目需求:电力巡检需要5名飞手</div>
</div>
{item.isApply ? (
<Button disabled className={styles.btnDisabled}>
已申请
</Button>
) : (
<Button className={styles.btn}>
申请合作
</Button>
)}
</div>
);
})}
</div>
);
}
@import "~/styles/mixins.scss";
.layout {
display: flex;
}
.new {
padding-top: 20px;
width: 750px;
.item {
cursor: pointer;
padding: 10px 12px;
display: flex;
border-bottom: 1px dashed RGBA(222, 222, 222, 1);
align-items: center;
.logo {
width: 120px;
height: 80px;
background: #d8d8d8;
border-radius: 6px;
margin-right: 22px;
@include ellipsis(1);
}
.info {
padding: 3px 0 2px;
.title {
font-size: 16px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold;
color: #000000;
}
}
}
}
.newsBox {
margin-top: 24px;
width: 384px;
height: 491px;
background: url("./assets/bk.png");
background-size: 100% 100%;
.newsHeader {
padding: 24px;
display: flex;
align-items: center;
gap: 16px;
.newsHeaderTitle {
font-size: 20px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold;
color: #000000;
}
}
.newList {
.newItem {
cursor: pointer;
padding: 5px 16px 5px 20px;
display: flex;
justify-content: space-between;
align-items: center;
.newItemTitle {
font-size: 16px;
font-family: MicrosoftYaHei;
color: #323232;
line-height: 21px;
@include ellipsis(2);
width: 217px;
}
.newItemImg {
width: 90px;
height: 60px;
background: #d8d8d8;
border-radius: 6px;
}
}
}
}
import { RightOutlined } from "@ant-design/icons";
import { Col, Row } from "antd";
import styles from "./index.module.scss";
import Image from "next/image";
export default function requirements() {
return (
<Row justify="space-between">
<Col className={styles.new}>
<div className={styles.item}>
<div className={styles.logo}></div>
<div className={styles.info}>
<div className={styles.title}>项目需求:电力巡检需要5名飞手</div>
</div>
</div>
</Col>
<Col className={styles.newsBox}>
<div className={styles.newsHeader}>
<div className={styles.newsHeaderTitle}>行业新闻</div>
<RightOutlined style={{ fontSize: 16 }} />
</div>
<ul className={styles.newList}>
<li className={styles.newItem}>
<div className={styles.newItemTitle}>
中国超音速无人机雨燕试飞成功好棒呀
</div>
<Image className={styles.newItemImg} src="" alt=""></Image>
</li>
</ul>
</Col>
</Row>
);
}
//项目需求
.requirements {
padding-top: 20px;
.item {
cursor: pointer;
padding: 20px 19px;
display: flex;
border-bottom: 1px dashed RGBA(222, 222, 222, 1);
.logo {
width: 60px;
height: 60px;
margin-right: 24px;
background: url('./assets/resolved.png') no-repeat;
background-size: 100% 100%;
}
.info {
padding: 3px 0 2px;
.title {
white-space: nowrap; /* 防止换行 */
overflow: hidden; /* 隐藏溢出部分 */
text-overflow: ellipsis; /* 使用省略号代替溢出部分 */
font-size: 18px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold;
margin-bottom: 11px;
color: RGBA(127, 127, 127, 1);
}
.desc {
white-space: nowrap; /* 防止换行 */
overflow: hidden; /* 隐藏溢出部分 */
text-overflow: ellipsis; /* 使用省略号代替溢出部分 */
font-size: 16px;
font-family: MicrosoftYaHei;
color: RGBA(169, 170, 171, 1);
line-height: 22px;
}
}
&.noResolve {
.logo {
background-image: url('./assets//noResolve.png');
}
.info {
.title{
color: #000;
}
.desc {
color: #3C3E42;
}
}
}
}
}
\ No newline at end of file
import styles from './index.module.scss';
export default function requirements(){
return (
<div className={styles.requirements}>
<div className={styles.item}>
<div className={styles.logo}></div>
<div className={styles.info}>
<div className={styles.title}>项目需求:电力巡检需要5名飞手</div>
<div className={styles.desc}>
具体需求:电力巡检需要5名飞手,山东临沂
</div>
</div>
</div>
<div className={`${styles.item} ${styles.noResolve}`}>
<div className={styles.logo}></div>
<div className={styles.info}>
<div className={styles.title}>项目需求:电力巡检需要5名飞手</div>
<div className={styles.desc}>
具体需求:电力巡检需要5名飞手,山东临沂
</div>
</div>
</div>
</div>
);
}
\ No newline at end of file
.banner { .bannerWrap{
height: 180px; height: 180px;
position: relative;
}
.banner {
height: 100%;
width: 1920px; width: 1920px;
position: absolute; position: absolute;
top: 0; top: 50%;
left: 0; left: 50%;
background: url('./assets/banner.png') no-repeat; transform: translate(-50%, -50%);
background: url("./assets/banner.png") no-repeat;
background-size: 100% 100%; background-size: 100% 100%;
} }
...@@ -12,10 +19,10 @@ ...@@ -12,10 +19,10 @@
height: 60px; height: 60px;
background: #f4f5f7; background: #f4f5f7;
border: 1px solid #f0f0f0; border: 1px solid #f0f0f0;
padding-right: 25px;
:global .ant-tabs-nav { :global .ant-tabs-nav {
height: 100%; height: 100%;
margin: 0;
.ant-tabs-tab-btn { .ant-tabs-tab-btn {
width: 100px; width: 100px;
...@@ -35,69 +42,8 @@ ...@@ -35,69 +42,8 @@
.ant-tabs-ink-bar { .ant-tabs-ink-bar {
width: 40px !important; width: 40px !important;
height: 4px; height: 4px;
background: #0060ff;
border-radius: 2px; border-radius: 2px;
transform: translateX(30px); transform: translateX(30px);
} }
} }
} }
//项目需求
.requirements {
padding-top: 20px;
.item {
padding: 20px 19px;
display: flex;
border-bottom: 1px dashed RGBA(222, 222, 222, 1);
.logo {
width: 60px;
height: 60px;
margin-right: 24px;
background: url('./assets/resolved.png') no-repeat;
background-size: 100% 100%;
}
.info {
padding: 3px 0 2px;
.title {
white-space: nowrap; /* 防止换行 */
overflow: hidden; /* 隐藏溢出部分 */
text-overflow: ellipsis; /* 使用省略号代替溢出部分 */
font-size: 18px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold;
margin-bottom: 11px;
color: RGBA(127, 127, 127, 1);
}
.desc {
white-space: nowrap; /* 防止换行 */
overflow: hidden; /* 隐藏溢出部分 */
text-overflow: ellipsis; /* 使用省略号代替溢出部分 */
font-size: 16px;
font-family: MicrosoftYaHei;
color: RGBA(169, 170, 171, 1);
line-height: 22px;
}
}
&.noResolve {
.logo {
background-image: url('./assets//noResolve.png');
}
.info {
.title{
color: #000;
}
.desc {
color: #3C3E42;
}
}
}
}
}
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from "react";
import { Tabs, Button, Cascader, Space, DatePicker, DatePickerProps } from 'antd'; import {
import styles from './index.module.scss'; Tabs,
import Layout from '~/components/layout'; Button,
Cascader,
Space,
DatePicker,
DatePickerProps,
} from "antd";
import styles from "./index.module.scss";
import Layout from "~/components/layout";
import requirements from "./components/requirements"; //项目需求
import bids from "./components/bids"; //招投标项目
import cases from "./components/cases"; //业务案例
import news from "./components/news"; //行业新闻
interface Option { interface Option {
value: string | number; value: string | number;
...@@ -11,32 +22,32 @@ interface Option { ...@@ -11,32 +22,32 @@ interface Option {
const options: Option[] = [ const options: Option[] = [
{ {
value: 'zhejiang', value: "zhejiang",
label: 'Zhejiang', label: "Zhejiang",
children: [ children: [
{ {
value: 'hangzhou', value: "hangzhou",
label: 'Hangzhou', label: "Hangzhou",
children: [ children: [
{ {
value: 'xihu', value: "xihu",
label: 'West Lake', label: "West Lake",
}, },
], ],
}, },
], ],
}, },
{ {
value: 'jiangsu', value: "jiangsu",
label: 'Jiangsu', label: "Jiangsu",
children: [ children: [
{ {
value: 'nanjing', value: "nanjing",
label: 'Nanjing', label: "Nanjing",
children: [ children: [
{ {
value: 'zhonghuamen', value: "zhonghuamen",
label: 'Zhong Hua Men', label: "Zhong Hua Men",
}, },
], ],
}, },
...@@ -48,76 +59,68 @@ const onChange = (value: string[]) => { ...@@ -48,76 +59,68 @@ const onChange = (value: string[]) => {
console.log(value); console.log(value);
}; };
const onDateChange: DatePickerProps['onChange'] = (date, dateString) => { const onDateChange: DatePickerProps["onChange"] = (date, dateString) => {
console.log(date, dateString); console.log(date, dateString);
}; };
const operations = ( const operations = (
<Space size={8}> <Space size={8} style={{ marginRight: 25 }}>
<Cascader options={options} onChange={onChange} placeholder='Please select' /> <Cascader
options={options}
onChange={onChange}
placeholder="Please select"
borderRadiusSM={6}
/>
<DatePicker onChange={onDateChange} /> <DatePicker onChange={onDateChange} />
</Space> </Space>
); );
//项目需求 const items = ["项目需求", "招投标项目", "业务案例", "行业新闻"].map(
const requirements = function () { (value) => {
return ( let children: JSX.Element | string = <></>;
<Layout>
<div className={styles.requirements}> switch (value) {
<div className={styles.item}> case "项目需求":
<div className={styles.logo}></div> children = requirements();
<div className={styles.info}> break;
<div className={styles.title}>项目需求:电力巡检需要5名飞手</div>
<div className={styles.desc}>
具体需求:电力巡检需要5名飞手,山东临沂
</div>
</div>
</div>
<div className={`${styles.item} ${styles.noResolve}`}>
<div className={styles.logo}></div>
<div className={styles.info}>
<div className={styles.title}>项目需求:电力巡检需要5名飞手</div>
<div className={styles.desc}>
具体需求:电力巡检需要5名飞手,山东临沂
</div>
</div>
</div>
</div>
</Layout>
);
};
const items = ['项目需求', '招投标项目', '业务案例', '行业新闻'].map((value) => { case "招投标项目":
let children: JSX.Element | string = <></>; children = bids();
break;
switch(value){ case "业务案例":
case '项目需求': children = cases();
children = requirements(); break;
break;
default: case "行业新闻":
children = `Content of tab ${value}`; children = news();
break;
}
return {
label: `${value}`,
key: value,
children: children,
};
} }
return { );
label: `${value}`,
key: value,
children: children,
};
});
export default function Mall() { export default function Mall() {
return ( return (
<div style={{ paddingTop: 180, backgroundColor: '#fff', minHeight: 820 }}> <Layout layoutStyle={{ backgroundColor: "#fff" }}>
<div className='page'> <div style={{ backgroundColor: "#fff", minHeight: 820 }}>
<div className={styles.banner}></div> <div className="page">
<Tabs <div className={styles.bannerWrap}>
className={styles.tabs} <div className={styles.banner}></div>
tabBarExtraContent={operations} </div>
items={items}
tabBarGutter={41} <Tabs
/> className={styles.tabs}
tabBarExtraContent={operations}
items={items}
tabBarGutter={41}
/>
</div>
</div> </div>
</div> </Layout>
); );
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论