提交 9fb83ac4 作者: 翁进城

Initial commit

上级
{
"extends": "next/core-web-vitals"
}
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
.idea/
.vscode/
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# lock files
package-lock.json
yarn.lock
pnpm-lock.yaml
public/antd.min.css
\ No newline at end of file
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
.navHeader {
width: 100%;
height: 68px;
background: linear-gradient(360deg, #002157 0%, #00102c 100%);
display: flex;
align-items: center;
justify-content: center;
}
.logo {
width: 148px;
height: 68px;
background-image: url(./assets/logo.png);
background-size: 100% 100%;
}
.tabs {
height: 100%;
margin-right: 58px;
:global .ant-tabs-nav {
height: 100%;
margin-bottom: 0;
&::before {
border: 0;
}
}
:global .ant-tabs-tab {
& + .ant-tabs-tab {
margin-left: 0px;
}
.ant-tabs-tab-btn {
font-size: 16px;
color: #fff;
width: 90px;
text-align: center;
font-family: MicrosoftYaHei;
}
}
:global .ant-tabs-tab-active {
.ant-tabs-tab-btn {
color: #91c8ff !important;
}
}
:global .ant-tabs-ink-bar {
display: none;
}
}
.btns {
margin-right: 25px;
}
.btn1 {
width: 120px;
height: 40px;
background: linear-gradient(90deg, #278eff 0%, #0052da 100%);
border-radius: 6px;
border: 0;
font-size: 16px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold;
color: #ffffff;
&:hover {
opacity: 0.8;
}
}
.btn2 {
box-sizing: border-box;
width: 120px;
height: 40px;
border-radius: 6px;
border: 1px solid #ffb02c;
font-size: 16px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold;
color: #ffb02c;
background: none;
}
.headImg {
width: 48px;
height: 48px;
background: #ffffff;
}
import React, { useState } from 'react';
import { Avatar, Button, Space, Tabs } from 'antd';
import type { TabsProps } from 'antd';
import styles from './index.module.scss';
import { useRouter } from "next/router";
const items: TabsProps['items'] = [
{
key: '/home',
label: ` 首页 `,
},
{
key: '1',
label: `作业服务`,
},
{
key: '2',
label: `设备租赁`,
},
{
key: '3',
label: `飞手服务`,
},
{
key: '/mall',
label: `产品商城`,
},
{
key: '/projectInfo',
label: `项目资讯`,
},
{
key: '6',
label: `社区论坛`,
},
];
export default function NavHeader() {
const router = useRouter();
const currentPath = router.asPath;
console.log("currentHash", currentPath);
const onChange = (key: string) => {
console.log(key);
router.push(key);
};
return (
<div className={styles.navHeader}>
<div className={styles.logo}></div>
<Tabs
className={styles.tabs}
defaultActiveKey={currentPath}
items={items}
onChange={onChange}
/>
<Space size={16} className={styles.btns}>
<Button type='primary' className={styles.btn1}>
+ 发布信息
</Button>
<Button className={styles.btn2}>入驻加盟</Button>
</Space>
<div className={styles.haedImg}>
<Avatar size={48} style={{ background: '#fff' }}></Avatar>
</div>
</div>
);
}
// 颜色变量
$color-primary: #f4b700;
$color-primary-hover: #f7c800;
$color-primary-active: #ffcc32;
$color-primary-border: #f4b700;
.brand-select-search-view {
position: relative;
width: 100%;
box-sizing: border-box;
.brand-select-item {
min-height: 50px;
background: #fff;
display: flex;
align-items: center;
justify-content: flex-start;
box-sizing: border-box;
position: relative;
padding: 12px 20px 12px 20px;
border-bottom: 1px solid #f2f4fa;
.item-label {
font-size: 14px;
color: #979aa8;
margin-right: 16px;
min-width: 32px;
}
.item-content {
display: flex;
align-items: center;
justify-content: flex-start;
flex-wrap: wrap;
width: 100%;
.alphabet-list, .select-list {
width: 100%;
display: flex;
align-items: center;
justify-content: flex-start;
margin-bottom: 10px;
flex-wrap: wrap;
.list-item {
position: relative;
height: 24px;
width: 20px;
font-size: 14px;
line-height: 20px;
margin: 0 16px 0 0;
border-radius: 2px;
text-align: center;
border: 1px solid transparent;
cursor: pointer;
user-select: none;
}
.list-item:hover {
color: $color-primary;
}
.list-item:hover:after {
position: absolute;
content: '';
width: 35%;
height: 1px;
background: $color-primary;
left: calc((100% - 35%) / 2);
bottom: 0;
}
.item-active {
background: $color-primary-active;
border: 1px solid $color-primary;
}
.item-active:hover {
color: #000000;
}
.item-active:hover:after {
display: none;
}
}
.select-list {
margin-bottom: 0;
.list-item {
width: 80px;
margin: 0 10px 0 0;
}
}
.selected {
margin-bottom: -10px;
.list-item {
border: 1px solid $color-primary;
color: $color-primary;
margin-bottom: 10px;
}
.list-item:hover {
background: #f7f8fc;
}
.list-item:hover:after {
display: none;
}
}
.alphabet-content {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
padding: 2px 12px 12px;
background-color: #f7f8fc;
width: 100%;
min-height: 50px;
box-sizing: border-box;
.content-item {
position: relative;
width: 80px;
margin: 10px 8px 0 0;
font-size: 14px;
overflow: hidden;
background: #ffffff;
text-align: center;
height: 28px;
line-height: 26px;
border: 1px solid transparent;
user-select: none;
}
.content-item:hover {
color: #f4b700;
}
.content-item:hover:after {
position: absolute;
content: '';
width: 35%;
height: 1px;
background: #f4b700;
left: calc((100% - 35%) / 2);
bottom: 0;
}
.item-active {
background: $color-primary-active;
border: 1px solid $color-primary;
}
.item-active:hover {
color: #000000;
}
.item-active:hover:after {
display: none;
}
}
.select-region {
.region-label {
margin: 0 12px;
}
.region-label:first-child{
margin-left: 0;
}
}
}
}
.brand-select-item:last-child {
border-bottom: none;
}
}
import React, { useEffect, useState } from 'react';
import './index.scss';
import { pinyin } from 'pinyin-pro';
import { Select } from 'antd';
// 搜索列表的类型
interface searchColumns {
type: 'Alphabet' | 'Brand' | 'Price' | 'Industry' | 'Product';
options: { label: string; value: any; children?: { label: string; value: any }[] }[];
name: string;
label: string;
subLabel?: string;
}
// 传参类型
interface propType {
columns: searchColumns[];
selected: boolean;
region: boolean;
}
const BrandSelectItem: React.FC<{ label: string; children: any }> = (props) => {
const { label, children } = props;
return (
<div className={'brand-select-item'}>
<div className={'item-label'}>{label}</div>
<div className={'item-content'}>{children}</div>
</div>
);
};
const BrandSelectSearch: React.FC<propType> = (props) => {
const { columns, selected, region } = props;
// 字母列表转换
const [alphabetList, setAlphabetList] = useState<{ label?: string; children: any }[]>([]);
// 字母列表索引
const [alphabetIndex, setAlphabetIndex] = useState<number>(0);
// 字母列表详情索引
const [alphabetContentIndex, setAlphabetContentIndex] = useState<number>(0);
// 普通筛选索引
const [optionsIndex, setOptionsIndex] = useState<{ index: number; subIndex?: number }[]>([]);
// 转换过后的普通筛选列表
const [columnsList, setColumnsList] = useState<searchColumns[]>([]);
// 获取字母列表 (将传入的列表转成以拼音开头的数组)
const getAlphabetList = () => {
// 如果没有字母列表则不执行
if (!columns.some((i) => i.type === 'Alphabet')) return;
// 获取字母列表
const options = columns.find((i) => i.type === 'Alphabet')?.options;
// 获取开头字母列表(去重)
const arr = [
...new Set(
options?.map((i) =>
pinyin(i.label, { toneType: 'none', type: 'array' })?.at(0)?.at(0)?.toUpperCase(),
),
),
].sort();
// 转换成展示列表
setAlphabetList(
arr?.map((i) => {
const children =
options?.filter(
(j) =>
pinyin(j.label, { toneType: 'none', type: 'array' })?.at(0)?.at(0)?.toUpperCase() ===
i,
) || [];
return {
label: i,
children: [{ label: '不限', value: 'all' }, ...children],
};
}),
);
};
// 获取普通列表
const getOptionsList = (options: { label: string; value: any }[]) => {
return [{ label: '不限', value: 'all' }, ...options];
};
// componentDidMount
useEffect(() => {
if (!columns) return;
getAlphabetList();
// 初始化索引
setOptionsIndex(columns.map((i) => (i.subLabel ? { index: 0, subIndex: 0 } : { index: 0 })));
// 初始化列表
setColumnsList(columns?.map((i) => ({ ...i, options: getOptionsList(i.options) })));
}, [columns]);
// componentDidUpdate
useEffect(() => {
// 如果没有普通筛选列表则不执行
if (!optionsIndex.length) return;
// 如果普通筛选列表全为0则不执行
if (!optionsIndex.some((i) => i.index !== 0)) return;
console.log('optionsIndex --->', optionsIndex);
console.log(
'componentDidUpdate -->',
optionsIndex.map((i, j) => columnsList[j].options[i.index]),
);
}, [optionsIndex]);
return (
<div className={'brand-select-search-view'}>
{region && (
<BrandSelectItem label={'地区'}>
<div className={'select-region'}>
<span className={'region-label'}></span>
<Select placeholder={'请选择省份'} options={[]} size={'small'} />
<span className={'region-label'}></span>
<Select placeholder={'请选择城市'} options={[]} size={'small'} />
</div>
</BrandSelectItem>
)}
{columnsList.map((i, j) => {
if (i.type === 'Alphabet') {
return (
<BrandSelectItem label={'品牌'} key={j}>
<div className='alphabet-list'>
{alphabetList.map((n, m) => (
<div
className={`list-item ${alphabetIndex === m && 'item-active'}`}
key={m}
onClick={() => {
setAlphabetIndex(m);
setAlphabetContentIndex(0);
}}
>
{n.label}
</div>
))}
</div>
<div className='alphabet-content'>
{alphabetList[alphabetIndex]?.children?.map((n: any, m: number) => (
<div
className={`content-item ${alphabetContentIndex === m && 'item-active'}`}
key={m}
onClick={() => {
setAlphabetContentIndex(alphabetContentIndex === m ? 0 : m);
const index =
columnsList
.find((i) => i.type === 'Alphabet')
?.options?.findIndex((i) => i.label === n.label) || 0;
// setOptionsIndex(optionsIndex?.map((i, k) => (k === j ? index : i)));
setOptionsIndex(optionsIndex?.map((i, k) => (k === j ? { index } : i)));
}}
>
{n.label}
</div>
))}
</div>
</BrandSelectItem>
);
}
if (['Industry', 'Brand', 'Price', 'Product'].includes(i.type)) {
return (
<div key={j}>
<BrandSelectItem label={i.label}>
<div className='select-list'>
{i.options?.map((n, m) => (
<div
className={`list-item ${optionsIndex[j].index === m && 'item-active'}`}
key={m}
onClick={() => {
setOptionsIndex(
optionsIndex.map((l, k) =>
k === j
? optionsIndex[j].index === m
? i.subLabel
? { index: 0, subIndex: 0 }
: { index: 0 }
: i.subLabel
? { index: m, subIndex: 0 }
: { index: m }
: l,
),
);
}}
>
{n.label}
</div>
))}
</div>
</BrandSelectItem>
{!!i.subLabel && (
<BrandSelectItem label={i.subLabel}>
<div className='select-list'>
{getOptionsList(i.options[optionsIndex[j].index]?.children || [])?.map(
(n, m) => (
<div
className={`list-item ${optionsIndex[j].subIndex === m && 'item-active'}`}
key={m}
onClick={() => {
setOptionsIndex(
optionsIndex.map((i, k) =>
k === j ? { ...i, subIndex: m === i.subIndex ? 0 : m } : i,
),
);
}}
>
{n.label}
</div>
),
)}
</div>
</BrandSelectItem>
)}
</div>
);
}
})}
{selected && (
<BrandSelectItem label={'已选'}>
<div className='select-list selected'>
{optionsIndex.map(
(n, m) =>
columnsList[m].options[n.index].label !== '不限' && (
<div
className={`list-item`}
key={m}
onClick={() => {
// setOptionsIndex(
// optionsIndex.map((i, k) => (k === j ? (optionsIndex[j] === m ? 0 : m) : i)),
// );
}}
>
{columnsList[m].options[n.index].label}
</div>
),
)}
</div>
</BrandSelectItem>
)}
</div>
);
};
export default BrandSelectSearch;
.footer {
height: 200px;
background: linear-gradient(360deg, #001c49 0%, #021741 100%);
box-shadow: 0px 2px 12px 0px rgba(146, 146, 146, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.logo {
width: 125px;
height: 89px;
background: url('./assets/logo.png') no-repeat;
background-size: 100% 100%;
margin-right: 104px;
}
.qrcodeList {
display: flex;
margin-right: 101px;
.qrcodeItem {
margin-right: 87px;
&:nth-last-child(1) {
margin-right: 0;
}
.qrcodeImg {
width: 100px;
height: 100px;
border-radius: 8px;
overflow: hidden;
background-color: #fff;
}
.qrcodeTitle {
margin-top: 12px;
font-size: 12px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold;
color: #ffffff;
text-align: center;
}
}
}
.rightText {
.number {
text-align: center;
font-size: 58px;
font-family: Arial-Black, Arial;
font-weight: 900;
color: RGBA(255, 176, 45, 1);
line-height: 82px;
}
.text {
font-size: 16px;
font-family: MicrosoftYaHei;
color: #fff;
line-height: 21px;
}
}
import styles from './index.module.scss';
const qrcodeList = [
{
img: '',
title: '科比特官网',
},
{
img: '',
title: '云享飞服务号',
},
{
img: '',
title: '云享飞小程序',
},
{
img: '',
title: '官方社群',
},
];
export default function Footer() {
return (
<div className={styles.footer}>
<div className={styles.logo}></div>
<div className={styles.qrcodeList}>
{
qrcodeList.map((item, i) => {
return (
<div className={styles.qrcodeItem} key={i}>
<img className={styles.qrcodeImg} src={item.img}></img>
<div className={styles.qrcodeTitle}>{item.title}</div>
</div>
);
})
}
</div>
<div className={styles.rightText}>
<div className={styles.number}>100W</div>
<div className={styles.text}>无人机新媒体矩阵等你来关注</div>
</div>
</div>
);
}
import React, { Suspense } from 'react';
import { Layout, Space } from 'antd';
import NavHeader from '~/components/NavHeader';
import FooterView from '~/components/footer';
const { Header, Footer, Content } = Layout;
const headerStyle: React.CSSProperties = {
height: 'auto',
background: 'none',
padding: 0,
lineHeight: '1',
position: 'relative'
};
const contentStyle: React.CSSProperties = {
minHeight: 120,
lineHeight: '1',
color: '',
backgroundColor: '',
position: 'relative',
};
const footerStyle: React.CSSProperties = {
color: '',
backgroundColor: '',
lineHeight: '1',
padding: 0,
position: 'relative'
};
type Props = {
children: React.ReactNode;
};
export default function LayoutView(props: Props) {
return (
<Space direction='vertical' style={{ width: '100%' }} size={[0, 48]}>
<Layout style={{ minHeight: '100vh' }}>
<Header style={headerStyle}>
<NavHeader />
</Header>
<Content style={contentStyle}>
{props.children}
</Content>
<Footer style={footerStyle}>
<FooterView></FooterView>
</Footer>
</Layout>
</Space>
);
}
.uploader-view{
.ant-upload-wrapper .ant-upload-list .ant-upload-list-item-container{
//width: 200px !important;
}
}
import React, { useState } from 'react';
import { message, Upload, UploadProps } from 'antd';
// import { UploadFile } from "antd/es/upload/interface";
import './index.scss';
interface PropsType {
listType?: 'text' | 'picture' | 'picture-card'; // 上传列表的内建样式
fileSize?: number; // 文件大小
fileType?: string[]; // 上传文件类型
fileLength?: number; // 最大上传文件数量
children: React.ReactNode; // 上传按钮
onChange?: (fileList: any[]) => void; // 上传文件改变时的状态
onRemove?: (fileList: any[]) => void;
}
export const Uploader: React.FC<PropsType> = (props) => {
Uploader.defaultProps = {
listType: 'text',
fileSize: 2,
fileLength: 1,
fileType: ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/bmp'],
// eslint-disable-next-line @typescript-eslint/no-empty-function
onChange: () => {},
onRemove: () => {},
};
const { fileType, children, listType, fileSize, fileLength, onChange, onRemove } = props;
const [fileList, setFileList] = useState<any[]>([]);
// 上传文件配置
const uploadProps: UploadProps = {
listType,
fileList,
beforeUpload: (res) => {
const isType = fileType?.includes(res.type);
const isSize = res.size / 1024 / 1024 < (fileSize || 2);
if (!isType) {
message.error('请上传正确的格式!').then();
return isType;
}
if (!isSize) {
message.error('文件最大2M,请压缩后上传!').then();
return isSize;
}
},
customRequest: (res) => {
if (fileList.length >= (fileLength || 1)) {
message.error(`最多上传${fileLength || 1}个文件`).then();
return;
}
setFileList([...fileList, res.file]);
onChange?.([...fileList, res.file]);
},
onRemove: (res) => {
const newFileList = fileList.filter((item) => item.uid !== res.uid);
setFileList(newFileList);
onRemove?.(newFileList);
},
// onPreview: { onPreview },
};
return (
<div className='uploader-view'>
<Upload {...uploadProps} style={{ width: '100%' }}>
<>{fileList.length < (fileLength || 1) && children}</>
</Upload>
</div>
);
};
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
transpilePackages: ['antd'],
redirects() {
return [
{
source: "/",
destination: "/home",
permanent: true,
},
];
}
};
module.exports = nextConfig;
{
"name": "create-next-app-antd",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"predev": "ts-node --project ./tsconfig.node.json ./scripts/genAntdCss.tsx",
"prebuild": "cross-env NODE_ENV=production ts-node --project ./tsconfig.node.json ./scripts/genAntdCss.tsx"
},
"devDependencies": {
"@ant-design/cssinjs": "^1.3.0",
"@ant-design/static-style-extract": "~1.0.1",
"@next/font": "^13.1.1",
"@types/node": "^18.11.17",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
"antd": "^5.1.0",
"cross-env": "^7.0.3",
"eslint": "^8.30.0",
"eslint-config-next": "^13.1.1",
"next": "^13.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"ts-node": "^10.9.1",
"tslib": "^2.5.0",
"typescript": "^4.9.4",
"sass": "^1.62.1"
},
"dependencies": {}
}
import '../public/antd.min.css';
import '../styles/index.scss';
import type { AppProps } from 'next/app';
import withTheme from '../theme';
export default function App({ Component, pageProps }: AppProps) {
return withTheme(<Component {...pageProps} />);
}
import Document, {
Html,
Head,
Main,
NextScript,
DocumentContext,
} from 'next/document';
export default class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
</>
),
};
}
render() {
return (
<Html lang='en'>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'
type Data = {
name: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
res.status(200).json({ name: 'John Doe' })
}
import {
Form,
Select,
InputNumber,
DatePicker,
Switch,
Slider,
Button,
Rate,
Typography,
Space,
Divider,
} from 'antd';
const { Option } = Select;
const { Title } = Typography;
export default function Home() {
return (
<>
<section style={{ textAlign: 'center', marginTop: 48, marginBottom: 40 }}>
<Space align='start'>
<img
style={{ width: 40, height: 40 }}
src='https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg'
alt='Ant Design'
/>
<Title level={2} style={{ marginBottom: 0 }}>
Ant Design
</Title>
</Space>
</section>
<Divider style={{ marginBottom: 60 }}>Form</Divider>
<Form labelCol={{ span: 8 }} wrapperCol={{ span: 8 }}>
<Form.Item label='数字输入框'>
<InputNumber min={1} max={10} defaultValue={3} />
<span className='ant-form-text'> 台机器</span>
<a href='https://ant.design'>链接文字</a>
</Form.Item>
<Form.Item label='开关'>
<Switch defaultChecked />
</Form.Item>
<Form.Item label='滑动输入条'>
<Slider defaultValue={70} />
</Form.Item>
<Form.Item label='选择器'>
<Select defaultValue='lucy' style={{ width: 192 }}>
<Option value='jack'>jack</Option>
<Option value='lucy'>lucy</Option>
<Option value='disabled' disabled>
disabled
</Option>
<Option value='yiminghe'>yiminghe</Option>
</Select>
</Form.Item>
<Form.Item label='日期选择框'>
<DatePicker />
</Form.Item>
<Form.Item label='日期范围选择框'>
<DatePicker.RangePicker />
</Form.Item>
<Form.Item label='评分'>
<Rate defaultValue={5} />
</Form.Item>
<Form.Item wrapperCol={{ span: 8, offset: 8 }}>
<Space>
<Button type='primary' htmlType='submit'>
Submit
</Button>
<Button>Cancel</Button>
</Space>
</Form.Item>
</Form>
</>
);
}
.productList {
.title {
padding: 20px 10px 18px;
text-align: left;
}
.main {
display: flex;
.ad {
flex-shrink: 0;
width: 260px;
height: 420px;
margin-left: 16px;
background-color: gray;
}
}
.listWrap {
display: flex;
flex-wrap: wrap;
margin-left: -14px;
.item {
width: 220px;
height: 326px;
background: #ffffff;
border-radius: 6px;
margin-bottom: 15px;
display: flex;
flex-direction: column;
justify-content: space-between;
padding-bottom: 18px;
margin-left: 14px;
.img {
width: 100%;
height: 220px;
}
.title {
padding: 0 18px;
font-size: 15px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold;
color: #333333;
line-height: 24px;
height: 48px;
display: -webkit-box; /* 使用弹性盒子布局 */
-webkit-line-clamp: 2; /* 显示行数 */
-webkit-box-orient: vertical; /* 垂直排列 */
overflow: hidden; /* 隐藏溢出部分 */
text-overflow: ellipsis; /* 使用省略号代替溢出部分 */
}
.sellCount {
font-size: 14px;
font-family: MicrosoftYaHei;
color: #ff552d;
padding-left: 20px;
}
}
}
}
\ No newline at end of file
import React, { useEffect, useState } from "react";
import { Button, Select, Space, Tag } from "antd";
import Layout from "~/components/layout";
import styles from "./index.module.scss";
export default function Mall() {
const [productList, setProductList] = useState(Array<{}>);
useEffect(() => {
setProductList([{}, {}, {}, {}, {}, {}]);
}, []);
const onProvinceChange = (value: string) => {
console.log("省", value);
};
const onCityChange = (value: string) => {
console.log("市", value);
};
const onMoreChange = (value: string) => {
console.log("更多", value);
};
const onCloseTag = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
console.log("删除", e);
};
return (
<Layout>
<div className="page" style={{ paddingTop: "29px" }}>
<div className="filter-wrap" style={{ marginBottom: "11px" }}>
<div className="filter-item">
<div className="filter-item__title">地域:</div>
<div className="filter-item-main">
<Space size={40}>
<Select
bordered={false}
dropdownMatchSelectWidth={false}
placeholder="选择省"
onChange={onProvinceChange}
options={[
{ value: "jack", label: "Jack" },
{ value: "lucy", label: "Lucy" },
{ value: "Yiminghe", label: "yiminghe" },
{ value: "disabled", label: "Disabled", disabled: true },
]}
/>
<Select
bordered={false}
dropdownMatchSelectWidth={false}
placeholder="选择市"
onChange={onCityChange}
options={[
{ value: "jack", label: "Jack" },
{ value: "lucy", label: "Lucy" },
{ value: "Yiminghe", label: "yiminghe" },
{ value: "disabled", label: "Disabled", disabled: true },
]}
/>
</Space>
</div>
</div>
</div>
<div className="filter-wrap">
<div className="filter-item">
<div className="filter-item__title">类目:</div>
<div className="filter-item-main">
<Space size={40}>
<Button type="link">不限</Button>
<Button type="link">无人机</Button>
<Button type="link">挂载</Button>
<Button type="link">地面站</Button>
</Space>
<Select
placeholder="更多"
onChange={onMoreChange}
bordered={false}
dropdownMatchSelectWidth={false}
options={[
{ value: "jack", label: "Jack" },
{ value: "lucy", label: "Lucy" },
{ value: "Yiminghe", label: "yiminghe" },
{ value: "disabled", label: "Disabled", disabled: true },
]}
/>
</div>
</div>
<div className="filter-item">
<div className="filter-item__title">品牌:</div>
<div className="filter-item-main">
<Space size={40}>
<Button type="link">不限</Button>
<Button type="link">无人机</Button>
<Button type="link">挂载</Button>
<Button type="link">地面站</Button>
</Space>
<Select
placeholder="更多"
onChange={onMoreChange}
bordered={false}
dropdownMatchSelectWidth={false}
options={[
{ value: "jack", label: "Jack" },
{ value: "lucy", label: "Lucy" },
{ value: "Yiminghe", label: "yiminghe" },
{ value: "disabled", label: "Disabled", disabled: true },
]}
/>
</div>
</div>
<div className="filter-item">
<div className="filter-item__title">部件:</div>
<div className="filter-item-main">
<Space size={40}>
<Button type="link">不限</Button>
<Button type="link">无人机</Button>
<Button type="link">挂载</Button>
<Button type="link">地面站</Button>
</Space>
<Select
placeholder="更多"
onChange={onMoreChange}
bordered={false}
dropdownMatchSelectWidth={false}
options={[
{ value: "jack", label: "Jack" },
{ value: "lucy", label: "Lucy" },
{ value: "Yiminghe", label: "yiminghe" },
{ value: "disabled", label: "Disabled", disabled: true },
]}
/>
</div>
</div>
<div className="filter-item">
<div className="filter-item__title">型号:</div>
<div className="filter-item-main">
<Space size={40}>
<Button type="link">不限</Button>
<Button type="link">无人机</Button>
<Button type="link">挂载</Button>
<Button type="link">地面站</Button>
</Space>
<Select
placeholder="更多"
onChange={onMoreChange}
bordered={false}
dropdownMatchSelectWidth={false}
options={[
{ value: "jack", label: "Jack" },
{ value: "lucy", label: "Lucy" },
{ value: "Yiminghe", label: "yiminghe" },
{ value: "disabled", label: "Disabled", disabled: true },
]}
/>
</div>
</div>
<div className="filter-item">
<div className="filter-item__title">成色:</div>
<div className="filter-item-main">
<Space size={40}>
<Button type="link">不限</Button>
<Button type="link">无人机</Button>
<Button type="link">挂载</Button>
<Button type="link">地面站</Button>
</Space>
</div>
</div>
<div className="filter-item">
<div className="filter-item__title">已选:</div>
<div className="filter-item-main">
<Space size={10}>
<Tag closable onClose={onCloseTag}>
无人机
</Tag>
<Tag closable onClose={onCloseTag}>
无人机
</Tag>
</Space>
</div>
</div>
</div>
<div className={styles.productList}>
<div className={styles.title}>四旋翼无人机</div>
<div className={styles.main}>
<ul className={styles.listWrap}>
{productList.map((item) => {
return (
<li className={styles.item}>
<img className={styles.img}></img>
<div className={styles.title}>
入云龙ll 1550入云龙ll 1550入云龙ll 1550入云龙ll 1550
</div>
<div className={styles.sellCount}>半年售12987</div>
</li>
);
})}
</ul>
<div className={styles.ad}></div>
</div>
</div>
</div>
</Layout>
);
}
.banner {
height: 180px;
width: 1920px;
position: absolute;
top: 0;
left: 0;
background: url('./assets/banner.png') no-repeat;
background-size: 100% 100%;
}
.tabs {
height: 60px;
background: #f4f5f7;
border: 1px solid #f0f0f0;
padding-right: 25px;
:global .ant-tabs-nav {
height: 100%;
.ant-tabs-tab-btn {
width: 100px;
text-align: center;
font-size: 18px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold;
color: #3c3e42;
}
.ant-tabs-tab-active {
.ant-tabs-tab-btn {
color: #3c3e42;
}
}
.ant-tabs-ink-bar {
width: 40px !important;
height: 4px;
background: #0060ff;
border-radius: 2px;
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 { Tabs, Button, Cascader, Space, DatePicker, DatePickerProps } from 'antd';
import styles from './index.module.scss';
import Layout from '~/components/layout';
interface Option {
value: string | number;
label: string;
children?: Option[];
}
const options: Option[] = [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
children: [
{
value: 'xihu',
label: 'West Lake',
},
],
},
],
},
{
value: 'jiangsu',
label: 'Jiangsu',
children: [
{
value: 'nanjing',
label: 'Nanjing',
children: [
{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
},
],
},
],
},
];
const onChange = (value: string[]) => {
console.log(value);
};
const onDateChange: DatePickerProps['onChange'] = (date, dateString) => {
console.log(date, dateString);
};
const operations = (
<Space size={8}>
<Cascader options={options} onChange={onChange} placeholder='Please select' />
<DatePicker onChange={onDateChange} />
</Space>
);
//项目需求
const requirements = function () {
return (
<Layout>
<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>
</Layout>
);
};
const items = ['项目需求', '招投标项目', '业务案例', '行业新闻'].map((value) => {
let children: JSX.Element | string = <></>;
switch(value){
case '项目需求':
children = requirements();
break;
default:
children = `Content of tab ${value}`;
}
return {
label: `${value}`,
key: value,
children: children,
};
});
export default function Mall() {
return (
<div style={{ paddingTop: 180, backgroundColor: '#fff', minHeight: 820 }}>
<div className='page'>
<div className={styles.banner}></div>
<Tabs
className={styles.tabs}
tabBarExtraContent={operations}
items={items}
tabBarGutter={41}
/>
</div>
</div>
);
}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="31" fill="none"><g opacity=".9"><path fill="url(#a)" d="M13 .4v29.3H7V6.3h-.2L0 10.5V5L7.2.4H13Z"/><path fill="url(#b)" d="M28.8 30.1c-2.2 0-4-.3-5.7-1-1.7-.8-3-1.8-4-3.1a7.7 7.7 0 0 1-1.4-4.6h6.2c0 .8.3 1.4.7 2 .4.5 1 .9 1.7 1.2.7.3 1.6.4 2.5.4 1 0 1.7-.2 2.5-.5.7-.3 1.3-.8 1.7-1.4.4-.6.6-1.2.6-2s-.2-1.5-.7-2.1c-.4-.6-1-1-1.8-1.4-.8-.4-1.8-.5-2.9-.5h-2.7v-4.6h2.7a6 6 0 0 0 2.5-.5 4 4 0 0 0 1.7-1.3c.4-.6.6-1.3.6-2a3.5 3.5 0 0 0-2-3.3 5.6 5.6 0 0 0-4.5 0 4 4 0 0 0-1.7 1.2c-.4.6-.6 1.2-.6 2h-6c0-1.7.6-3.2 1.5-4.5 1-1.3 2.2-2.3 3.8-3C25 .4 26.8 0 28.8 0s3.8.4 5.3 1.1c1.5.7 2.7 1.7 3.6 3a7.2 7.2 0 0 1 1.2 4.2c0 1.6-.5 3-1.5 4a7 7 0 0 1-4 2.2v.2c2.2.3 3.8 1 5 2.2a6.4 6.4 0 0 1 1.6 4.6c0 1.7-.5 3.1-1.4 4.4a9.7 9.7 0 0 1-4 3.1c-1.7.8-3.7 1.1-5.8 1.1Z"/></g><defs><linearGradient id="a" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient><linearGradient id="b" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient></defs></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
\ No newline at end of file
import fs from "fs";
import { extractStyle } from "@ant-design/static-style-extract";
import withTheme from "../theme";
const outputPath = "./public/antd.min.css";
// 1. default theme
// const css = extractStyle();
// 2. With custom theme
const css = extractStyle(withTheme);
fs.writeFileSync(outputPath, css);
console.log(`🎉 Antd CSS generated at ${outputPath}`);
@import './reset.scss';
body {
background-color: rgba(239, 241, 244, 1);
}
body::-webkit-scrollbar {
width: 0;
}
//页面基础属性
.page {
width: 1200px;
margin: auto;
padding-bottom: 90px;
}
.filter-wrap {
padding: 0px 32px;
background: #ffffff;
border-radius: 6px;
*{
font-size: 14px !important;
}
.filter-item {
padding: 11px 0;
border-bottom: 1px dashed RGBA(222, 222, 222, 1);
display: flex;
align-items: center;
&:nth-last-child(1) {
border-bottom: none;
}
.filter-item__title {
width: 80px;
margin-right: 8px;
font-family: MicrosoftYaHei;
color: rgba(153, 153, 153, 1);
line-height: 20px;
}
.filter-item-main {
flex: 1;
display: flex;
justify-content: space-between;
}
.ant-select-selector {
padding: 0 12px 0 0;
.ant-select-selection-item,
.ant-select-selection-placeholder {
font-size: 16px;
font-family: MicrosoftYaHei;
color: #3e4149;
}
}
.ant-select-arrow {
color: #3e4149;
}
.ant-btn-link {
font-size: 16px;
font-family: MicrosoftYaHei;
color: #3e4149;
padding: 0;
}
.ant-tag {
padding: 4px 9px;
}
}
}
.ant-tag-close-icon {
font-size: 10px !important;
}
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
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;
}
\ No newline at end of file
import React from "react";
import { ConfigProvider } from "antd";
const testGreenColor = "#52c41a";
const testRedColor = "#ff0000";
const withTheme = (node: JSX.Element) => (
<>
<ConfigProvider
theme={{
token: {
colorPrimary: '#52c41a',
},
}}
>
<ConfigProvider
theme={{
token: {
borderRadius: 16,
},
}}
>
{node}
</ConfigProvider>
</ConfigProvider>
</>
)
export default withTheme;
\ No newline at end of file
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"paths": {
"~/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
{
"compilerOptions": {
"strictNullChecks": true,
"module": "NodeNext",
"jsx": "react",
"esModuleInterop": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论