提交 1535e021 作者: 余乾开

Merge branch 'master' into feature/chuck

import request, { Response } from "~/api/request"; import request, { Response } from "~/api/request";
import config from "./config";
export interface RegionResp { export interface RegionResp {
childInfo?: RegionResp[] | null; childInfo?: RegionResp[] | null;
...@@ -38,4 +39,8 @@ export default { ...@@ -38,4 +39,8 @@ export default {
userInfo: (params: UserInfoParams): Promise<Response<UserInfoResp>> => { userInfo: (params: UserInfoParams): Promise<Response<UserInfoResp>> => {
return request("/userapp/user-account/info", "get", params, {}); return request("/userapp/user-account/info", "get", params, {});
}, },
//图片上传地址
imgOss: () => {
return config.baseUrl + "/pms/upload/imgOss";
}
}; };
...@@ -93,7 +93,11 @@ export default function NavHeader() { ...@@ -93,7 +93,11 @@ export default function NavHeader() {
</Space> </Space>
{user ? ( {user ? (
<div className={styles.haedImg}> <div className={styles.haedImg}>
<Avatar size={36} style={{ background: "#bdbdbd" }}></Avatar> <Avatar
size={36}
style={{ background: "#bdbdbd" }}
src={user.userImg}
></Avatar>
</div> </div>
) : ( ) : (
<Button <Button
......
...@@ -4,6 +4,7 @@ import styles from "./index.module.scss"; ...@@ -4,6 +4,7 @@ import styles from "./index.module.scss";
import img from "./assets/img.png"; import img from "./assets/img.png";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import api, { ListTagResp } from "./api"; import api, { ListTagResp } from "./api";
import { useRouter } from "next/router";
type Props = { type Props = {
open?: boolean; open?: boolean;
...@@ -11,12 +12,7 @@ type Props = { ...@@ -11,12 +12,7 @@ type Props = {
onCancel?: () => void; onCancel?: () => void;
}; };
export default function JoinModal(props: Props) { export default function JoinModal(props: Props) {
const test = [ const router = useRouter();
{
name: '飞手培训机构',
id: 0
}
];
const [tagList, setTagList] = useState<ListTagResp[]>([]); const [tagList, setTagList] = useState<ListTagResp[]>([]);
useEffect(() => { useEffect(() => {
...@@ -24,6 +20,11 @@ export default function JoinModal(props: Props) { ...@@ -24,6 +20,11 @@ export default function JoinModal(props: Props) {
setTagList(res.result || []); setTagList(res.result || []);
}) })
}, []) }, [])
const onClickTag = (item: ListTagResp) => {
router.replace("/JoinPolicy?tagId=" + item.id);
props.onCancel && props.onCancel();
};
return ( return (
<Modal <Modal
title="申请合作加盟" title="申请合作加盟"
...@@ -40,9 +41,13 @@ export default function JoinModal(props: Props) { ...@@ -40,9 +41,13 @@ export default function JoinModal(props: Props) {
<Col <Col
key={item.id} key={item.id}
style={{ cursor: "pointer", height: 100, padding: 0 }} style={{ cursor: "pointer", height: 100, padding: 0 }}
onClick={() => onClickTag(item)}
> >
<Image src={img} width={100} height={100} alt=""></Image> <Image src={img} width={100} height={100} alt=""></Image>
<div className={styles.identityBtn}>{item.tagName}{">"}</div> <div className={styles.identityBtn}>
{item.tagName}
{">"}
</div>
</Col> </Col>
); );
})} })}
......
import React from "react"; import React, { useEffect } from "react";
import { Empty } from "antd"; import { Empty } from "antd";
import { Box , WaterfallBox } from "./styled"; import { LeftBox , Box , WaterfallBox } from "./styled";
import { leftBoxProps } from "../interface"; import { leftBoxProps } from "../interface";
export default function Left(props: leftBoxProps) { export default function Left(props: leftBoxProps) {
const { boxIndex, leftRenderDom, leftcontentstyle, leftWaterfallDom } = props; const { boxIndex, leftRenderDom, leftcontentstyle, leftWaterfallDom } = props;
return ( return (
<div> <LeftBox>
{leftRenderDom?.columns.map((item) => { {leftRenderDom?.columns.map((item) => {
if (item.noFor) { if (item.noFor) {
return item.element; return item.element;
...@@ -30,7 +30,7 @@ export default function Left(props: leftBoxProps) { ...@@ -30,7 +30,7 @@ export default function Left(props: leftBoxProps) {
return null; return null;
})} })}
{ {
<WaterfallBox index={boxIndex} leftcontentstyle={leftcontentstyle}> leftWaterfallDom?.columns.length ? <WaterfallBox index={boxIndex} leftcontentstyle={leftcontentstyle}>
{ <div className="left-columns"> { <div className="left-columns">
{leftWaterfallDom?.columns.map((item) => { {leftWaterfallDom?.columns.map((item) => {
if (!item.noFor && item.type === "left") { if (!item.noFor && item.type === "left") {
...@@ -47,12 +47,12 @@ export default function Left(props: leftBoxProps) { ...@@ -47,12 +47,12 @@ export default function Left(props: leftBoxProps) {
return null return null
})} })}
</div>} </div>}
</WaterfallBox> </WaterfallBox> : null
} }
{leftRenderDom?.pagination ? leftRenderDom?.pagination : null} {leftRenderDom?.pagination ? leftRenderDom?.pagination : null}
{!leftRenderDom?.columns.length && !leftWaterfallDom?.columns.length? ( {!leftRenderDom?.columns.length && !leftWaterfallDom?.columns.length? (
<Empty description={"暂无数据"} /> <Empty description={"暂无数据"} />
) : null} ) : null}
</div> </LeftBox>
); );
} }
...@@ -12,6 +12,9 @@ export interface BoxProps { ...@@ -12,6 +12,9 @@ export interface BoxProps {
} }
} }
export const LeftBox = styled.div`
box-sizing: border-box;
`
export const Box = styled.div<BoxProps>` export const Box = styled.div<BoxProps>`
box-sizing: border-box; box-sizing: border-box;
...@@ -39,7 +42,7 @@ export const WaterfallBox = styled.div<BoxProps>` ...@@ -39,7 +42,7 @@ export const WaterfallBox = styled.div<BoxProps>`
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
width: ${props => props.leftcontentstyle?.width ? props.leftcontentstyle?.width : "790px"}; width: ${props => props.leftcontentstyle?.width ? props.leftcontentstyle?.width : "790px"};
.item{ .item{
// 每个元素都要设置右边距margin-right(每个元素的左右间隙) // 每个元素都要设置右边距margin-right(每个元素的左右间隙)
// 同时设置下边距margin-bottom(每个元素的上下间隙) // 同时设置下边距margin-bottom(每个元素的上下间隙)
/* margin: 0 24px 15px 0; */ /* margin: 0 24px 15px 0; */
...@@ -49,15 +52,13 @@ export const WaterfallBox = styled.div<BoxProps>` ...@@ -49,15 +52,13 @@ export const WaterfallBox = styled.div<BoxProps>`
// 这里的72px = (分布个数index-1)*间隙20px, 可以根据实际的分布个数和间隙区调整 // 这里的72px = (分布个数index-1)*间隙20px, 可以根据实际的分布个数和间隙区调整
min-width: calc(( 100% - ${props => props.index} * ${props => props.leftcontentstyle?.margin ? props.leftcontentstyle?.margin.right : "24px" }) / ${props => props.index}); min-width: calc(( 100% - ${props => props.index} * ${props => props.leftcontentstyle?.margin ? props.leftcontentstyle?.margin.right : "24px" }) / ${props => props.index});
max-width: calc(( 100% - ${props => props.index} * ${props => props.leftcontentstyle?.margin ? props.leftcontentstyle?.margin.right : "24px" }) / ${props => props.index}); max-width: calc(( 100% - ${props => props.index} * ${props => props.leftcontentstyle?.margin ? props.leftcontentstyle?.margin.right : "24px" }) / ${props => props.index});
// 每行最右侧的那个不设置右外边距
&:nth-child(${props => props.index}n + ${props => props.index}) {
margin-right: 0;
}
} }
.left-columns{ .left-columns{
} }
.right-columns{ .right-columns{
.item{
margin-right: 0;
}
} }
` `
\ No newline at end of file
components/footer/assets/fuwuhao.png

14.1 KB | W: | H:

components/footer/assets/fuwuhao.png

23.9 KB | W: | H:

components/footer/assets/fuwuhao.png
components/footer/assets/fuwuhao.png
components/footer/assets/fuwuhao.png
components/footer/assets/fuwuhao.png
  • 2-up
  • Swipe
  • Onion skin
components/footer/assets/mmc.png

3.8 KB | W: | H:

components/footer/assets/mmc.png

11.7 KB | W: | H:

components/footer/assets/mmc.png
components/footer/assets/mmc.png
components/footer/assets/mmc.png
components/footer/assets/mmc.png
  • 2-up
  • Swipe
  • Onion skin
components/footer/assets/shequn.png

14.8 KB | W: | H:

components/footer/assets/shequn.png

16.6 KB | W: | H:

components/footer/assets/shequn.png
components/footer/assets/shequn.png
components/footer/assets/shequn.png
components/footer/assets/shequn.png
  • 2-up
  • Swipe
  • Onion skin
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { AutoComplete, Modal } from "antd"; import { AutoComplete, Modal } from "antd";
import Image from "next/image"; import Image from "next/image";
import api from "~/api";
type Props = { type Props = {
open: boolean; open: boolean;
...@@ -20,6 +21,8 @@ export default function LoginModal(props: Props) { ...@@ -20,6 +21,8 @@ export default function LoginModal(props: Props) {
style: "", style: "",
href: "", href: "",
}); });
window.setUserId(1);
} }
}, [props.open]); }, [props.open]);
......
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, useState } from "react";
import api, { UserInfoResp } from "~/api";
/*
const fetcher = (url) =>
fetch(url)
.then((r) => r.json())
.then((data) => {
return { user: data?.user || null };
}); */
export function useUser() {
const [user, setUser] = useState(null);
useEffect(() => {
api
.userInfo({
userAccountId: 0,
})
.then((res) => {
setUser(res.result);
});
}, []);
return user;
}
import { useEffect, useState } from "react";
import api, { UserInfoResp } from "~/api";
/*
const fetcher = (url) =>
fetch(url)
.then((r) => r.json())
.then((data) => {
return { user: data?.user || null };
}); */
export function useUser() {
const [user, setUser] = useState<UserInfoResp | null>(null);
const [userAccountId, setUserAccountId] = useState<number | "">('');
useEffect(() => {
setUserAccountId(Number(window.localStorage.getItem('userId')));
window.setUserId = (id) => {
setUserAccountId(id);
window.localStorage.setItem('userId', id);
};
try {
let userInfo = JSON.parse(window.localStorage.getItem('userInfo') || '') || null;
setUser(userInfo);
} catch (e) { }
}, [])
useEffect(() => {
if (!user && userAccountId) {
api
.userInfo({
userAccountId: userAccountId,
})
.then((res) => {
setUser(res.result || null);
window.localStorage.setItem('userInfo', JSON.stringify(res.result || ''));
});
}
}, [userAccountId]);
return user;
}
export function useGeolocation() {
const [position, setPosition] = useState<{
position?: any,
address?: any
} | null>(null);
useEffect(() => {
const AMapLoader = require("@amap/amap-jsapi-loader");
window._AMapSecurityConfig = {
securityJsCode: 'b00440e4bf3989bb2481297acaa05908',
}
AMapLoader.load({
key: "826769d41d66ebd005ffa0b3e0013781", // 申请好的Web端开发者Key,首次调用 load 时必填
version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: ['AMap.Geolocation', "AMap.Geocoder"], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
})
.then(async (AMap: any) => {
//用户定位
const geolocation = new AMap.Geolocation({
enableHighAccuracy: true,//是否使用高精度定位,默认:true
timeout: 10000, //超过10秒后停止定位,默认:5s
position: 'RB', //定位按钮的停靠位置
offset: [10, 20], //定位按钮与设置的停靠位置的偏移量,默认:[10, 20]
zoomToAccuracy: true, //定位成功后是否自动调整地图视野到定位点
});
geolocation.getCurrentPosition(function (status: string, result: any) {
if (status == 'complete') {
onComplete(result)
} else {
onError(result)
}
});
//解析定位结果
async function onComplete(data: any) {
console.log('定位成功', data.position);
setPosition({
position: data.position,
address: null
})
var geocoder = new AMap.Geocoder({
city: '全国' // city 指定进行编码查询的城市,支持传入城市名、adcode 和 citycode
})
geocoder.getAddress([data.position.lng, data.position.lat], function (status: string, result: {info: string, regeocode?: {
formattedAddress: string,
addressComponent: {
adcode: string
}
}}) {
console.log('获取地址结果', result)
if (status === 'complete' && result.info === 'OK') {
// result为对应的地理位置详细信息
setPosition({
...position,
address: result.regeocode
})
}else{
setPosition({
...position,
address: null
})
}
})
}
//解析定位错误信息
async function onError(data: any) {
// message.error(`定位失败
// 失败原因排查信息:${data.message}
// 浏览器返回信息:${data.originMessage}
// `)
}
})
.catch((e: any) => {
console.log(e);
});
}, [])
return position
}
\ No newline at end of file
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;
}
...@@ -35,10 +35,12 @@ ...@@ -35,10 +35,12 @@
"@types/styled-components": "^5.1.26", "@types/styled-components": "^5.1.26",
"babel-plugin-styled-components": "^2.1.1", "babel-plugin-styled-components": "^2.1.1",
"cookie": "^0.5.0", "cookie": "^0.5.0",
"moment": "^2.29.4",
"next-connect": "^1.0.0", "next-connect": "^1.0.0",
"passport": "^0.6.0", "passport": "^0.6.0",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
"pinyin-pro": "^3.14.0", "pinyin-pro": "^3.14.0",
"react-infinite-scroll-component": "^6.1.0",
"styled-components": "^6.0.0-rc.1", "styled-components": "^6.0.0-rc.1",
"swiper": "^9.3.2", "swiper": "^9.3.2",
"swr": "^2.1.5", "swr": "^2.1.5",
......
import request, { Response } from "~/api/request"
export interface CooperationApplyParams {
applyName: string,
applyPhone: string,
remark?: string,
userAccountId: number,
cooperationTagId: number
}
export default {
//请加盟
cooperationApply(params: CooperationApplyParams):Promise<Response<string>>{
return request('/userapp/cooperation/apply', 'post', params)
}
}
\ No newline at end of file
.banner {
width: 100vw;
height: 200px;
background: #f25834;
position: absolute;
left: 50%;
top: 0;
transform: translate(-50%, 0);
}
.font1 {
font-size: 20px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold;
color: #000000;
line-height: 25px;
}
.font2 {
font-size: 18px;
font-family: MicrosoftYaHei;
color: #333333;
line-height: 32px;
letter-spacing: 1px;
}
import { Button, Col, Divider, Form, Input, Row } from "antd";
import { useRouter } from "next/router";
import LayoutView from "~/components/layout";
import api from "./api";
import styles from "./index.module.scss";
export default function JoinPolicy() {
const router = useRouter();
//提交
const onFinish = (values: any) => {
console.log(values);
api
.cooperationApply({
...values,
userAccountId: 1,
cooperationTagId: router.query.tagId,
})
.then((res) => {
console.log("提交结果", res);
if (res.result === "已通过") {
window.messageApi.success(res.result);
setTimeout(() => {
router.push("/");
}, 1500);
} else {
window.messageApi.error(res.message);
}
});
};
return (
<LayoutView>
<div className={styles.banner}></div>
<div
className="page"
style={{
background: "#fff",
position: "relative",
zIndex: 1,
marginTop: 60,
paddingBottom: 63,
}}
>
<div
className={styles.font1}
style={{ textAlign: "center", paddingTop: 40, paddingBottom: 30 }}
>
加盟入驻政策
</div>
<div
className={styles.font2}
style={{ paddingLeft: 50, paddingRight: 50 }}
>
1.每月自动获取一张高额度优惠券
<br /> 2.流量扶持和店铺曝光度
<br />
3成为科比特指定官方生态合作伙伴
<br /> 4.享受不低于1亿制造采购补贴
<br /> 5.无人机产业链资源共享
<br /> 6.享受价值5亿产业链订单
<br /> 7.享受一对一代运营服务
<br /> 8.享受无人机生态圈资源
<br /> 9.成为科比特指定官方生态
<br />
10.享受无人机生态圈资源合作伙伴
<br />
11.受价值5亿产业链订单
<br />
12.成为科比特指定官方生态合作伙伴
</div>
<Divider />
<div
className={styles.font1}
style={{ textAlign: "center", marginBottom: 28 }}
>
申请加盟入驻
</div>
<Row justify={"center"}>
<Col style={{ width: 400 }}>
<Form labelAlign="left" labelCol={{ span: 5 }} onFinish={onFinish}>
<Form.Item
label="姓名"
name="applyName"
rules={[{ required: true }]}
>
<Input placeholder="请输入姓名"></Input>
</Form.Item>
<Form.Item
label="联系方式"
name="applyPhone"
rules={[{ required: true }]}
>
<Input placeholder="请输入手机号"></Input>
</Form.Item>
<Form.Item name="remark">
<Input.TextArea placeholder="其它信息"></Input.TextArea>
</Form.Item>
<Row justify={"center"}>
<Button
type="primary"
htmlType="submit"
style={{ marginTop: 11, width: 200 }}
size="large"
>
提交申请
</Button>
</Row>
</Form>
</Col>
</Row>
</div>
</LayoutView>
);
}
import request, { Response } from "~/api/request"
export interface CompanyAuthParams {
companyName: string;
creditCode: string;
licenseImg: string;
userAccountId: number;
authStatus: 1 | 0
}
export default {
//提交企业认证
companyAuth(params: CompanyAuthParams):Promise<Response<string>>{
return request('/userapp/company-auth/add', 'post', params)
}
}
\ No newline at end of file
.font1 {
font-size: 20px;
font-family: MicrosoftYaHeiUI-Bold;
font-weight: bold;
color: #000000;
line-height: 25px;
}
.font2 {
font-size: 14px;
font-family: MicrosoftYaHei;
color: #989ca7;
line-height: 19px;
}
.banner {
width: 100vw;
height: 200px;
background: #1e3d7d;
position: absolute;
left: 50%;
top: 0;
transform: translate(-50%, 0);
}
.upload {
text-align: center;
margin-top: 40px;
:global .ant-upload {
width: 260px !important;
height: 172px !important;
}
}
\ No newline at end of file
import { LoadingOutlined, PlusOutlined } from "@ant-design/icons";
import { Col, Form, Input, Row, Upload, message, Button, Image } from "antd";
import type { UploadChangeParam } from "antd/es/upload";
import type { RcFile, UploadFile, UploadProps } from "antd/es/upload/interface";
import { useState } from "react";
import config from "~/api/config";
import Layout from "~/components/layout";
import api from "./api";
import styles from "./index.module.scss";
import gApi from '~/api';
const beforeUpload = (file: RcFile) => {
const isJpgOrPng = file.type === "image/jpeg" || file.type === "image/png";
if (!isJpgOrPng) {
message.error("You can only upload JPG/PNG file!");
}
//限制上传10M
const isLt2M = file.size / 1024 / 1024 < 10;
if (!isLt2M) {
message.error("Image must smaller than 2MB!");
}
return isJpgOrPng && isLt2M;
};
const normFile = (e: any) => {
console.log("Upload event:", e);
if (Array.isArray(e)) {
return e;
}
return e?.fileList;
};
export default function Certification() {
const [loading, setLoading] = useState(false);
const [imageUrl, setImageUrl] = useState<string>();
//上传change事件
const handleChange: UploadProps["onChange"] = (
info: UploadChangeParam<UploadFile>
) => {
console.log("uploadChange", info);
if (info.file.status === "uploading") {
setLoading(true);
return;
}
if (info.file.status === "done") {
// Get this url from response in real world.
setLoading(false);
setImageUrl(info.file.response.result?.[0]);
}
};
//提交
const onFinish = (values: any) => {
console.log(values);
api
.companyAuth({
...values,
licenseImg: imageUrl,
userAccountId: 1
})
.then((res) => {
console.log('提交结果', res);
if(res.result === '已通过'){
window.messageApi.success(res.result);
}
});
};
return (
<Layout>
<div className={styles.banner}></div>
<div
className="page"
style={{
background: "#fff",
position: "relative",
zIndex: 1,
marginTop: 60,
}}
>
<div
className={styles.font1}
style={{
padding: "30px 0 23px 31px",
borderBottom: "1px solid RGBA(231, 231, 231, 1)",
}}
>
企业认证{" "}
<span className={styles.font2} style={{ marginLeft: 28 }}>
发布信息需完成以下认证
</span>
</div>
<div>
<Form
style={{ padding: "70px 170px 162px 170px" }}
onFinish={onFinish}
>
<Row justify="space-between">
<Col span={11}>
<Form.Item
label="企业名称"
name="companyName"
rules={[{ required: true }]}
style={{ borderBottom: "1px solid RGBA(243, 243, 243, 1)" }}
>
<Input bordered={false} placeholder="请输入企业名称"></Input>
</Form.Item>
</Col>
<Col span={11}>
<Form.Item
label="企业信用代码"
name="creditCode"
rules={[{ required: true }]}
style={{ borderBottom: "1px solid RGBA(243, 243, 243, 1)" }}
>
<Input
bordered={false}
placeholder="请输入企业信用代码"
></Input>
</Form.Item>
</Col>
</Row>
<Form.Item
name="licenseImg"
rules={[{ required: true }]}
valuePropName="fileList"
getValueFromEvent={normFile}
help={<div style={{ textAlign: "center" }}>请上传营业执照</div>}
>
<Upload
name="uploadFile"
listType="picture-card"
className={styles.upload}
showUploadList={false}
action={gApi.imgOss}
beforeUpload={beforeUpload}
onChange={handleChange}
maxCount={1}
>
{imageUrl ? (
<Image
src={imageUrl}
alt="uploadFile"
style={{ width: "100%" }}
preview={false}
/>
) : (
<div>
{loading ? <LoadingOutlined /> : <PlusOutlined />}
<div style={{ marginTop: 8 }}>上传营业执照</div>
</div>
)}
</Upload>
</Form.Item>
<div className={styles.font2} style={{ marginTop: 56 }}>
1. 请上传最新的营业执照,证件需加盖与主体一致的单位公章。 <br />
2.加盖公章的扫描件或复印件支持jpg.jpeg.bmp.gif.png格式图片,大小不超10M。
</div>
<Row justify="center">
<Button
type="primary"
htmlType="submit"
style={{ marginTop: 20, padding: "0 129px" }}
size="large"
>
提交认证
</Button>
</Row>
</Form>
</div>
</div>
</Layout>
);
}
...@@ -3,10 +3,11 @@ import {useRouter} from 'next/router'; ...@@ -3,10 +3,11 @@ import {useRouter} from 'next/router';
import Layout from "~/components/layout"; import Layout from "~/components/layout";
import {Box} from './styled'; import {Box} from './styled';
import ImagePreview from './components/picture-preview'; import ImagePreview from './components/picture-preview';
import { Button , Image as AImage , Divider } from 'antd'; import { Button , Image as AImage , Divider , Select,Modal ,Tag,Space,Form,message} from 'antd';
import Image from 'next/image'; import Image from 'next/image';
import errImg from "~/assets/errImg"; import errImg from "~/assets/errImg";
import api,{GetWebDeviceDetailResult} from './api'; import api,{GetWebDeviceDetailResult,GetWebDeviceWareSkuById} from './api';
const {CheckableTag } = Tag
export default function EquipmentLeasingDetail() { export default function EquipmentLeasingDetail() {
const router = useRouter(); const router = useRouter();
...@@ -14,6 +15,7 @@ export default function EquipmentLeasingDetail() { ...@@ -14,6 +15,7 @@ export default function EquipmentLeasingDetail() {
const [id, setId] = useState<number | null>(null); const [id, setId] = useState<number | null>(null);
const [detail,setDetail] = useState<GetWebDeviceDetailResult | null>() const [detail,setDetail] = useState<GetWebDeviceDetailResult | null>()
const [wareSkuList,setWareSkuList] = useState<GetWebDeviceWareSkuById[] | undefined>()
useEffect(()=>{ useEffect(()=>{
setId(Number(router.query.id)) setId(Number(router.query.id))
...@@ -28,9 +30,98 @@ export default function EquipmentLeasingDetail() { ...@@ -28,9 +30,98 @@ export default function EquipmentLeasingDetail() {
.then((res) => { .then((res) => {
setDetail(res.result || null); setDetail(res.result || null);
}); });
api
.listWareSkuById({
id:id
})
.then((res) => {
res.result?.map(item=>{
return item
})
setWareSkuList(res.result || undefined);
});
} }
},[id]) },[id])
//租赁弹框
const [isModalOpen, setIsModalOpen] = useState(false);
const [loading, setLoading] = useState(false);
const [form] = Form.useForm();
const tagsData = ['3-7天', '8-15天', '16-30天', '30天以上'];
const [selectedTags, setSelectedTags] = useState<number>();
const [selectedTagsData, setSelectedTagsData] = useState<string>();
const showModal = () => {
setIsModalOpen(true);
if (wareSkuList?.length) {
setSelectedTags(wareSkuList[0].id);
form.setFieldValue("id",wareSkuList[0].id)
setSelectedTagsData("3-7天")
form.setFieldValue("date","3-7天")
}
};
const handleOk = () => {
setLoading(true);
form
.validateFields()
.then(async (values) => {
form.resetFields()
message.success("租赁成功")
setLoading(false);
setIsModalOpen(false);
// try{
// const res = await api.listWareSkuUpdate(values)
// if (res.code === "200") {
// setLoading(false);
// setIsModalOpen(false);
// form.resetFields()
// message.success('租赁成功')
// }else{
// setLoading(false);
// message.error(res.message)
// }
// }catch(e:any){
// message.error(e.message)
// }
}).catch((err) => {
message
.warning({
content: err.errorFields[0].errors[0],
})
.then();
setLoading(false);
});
};
const handleCancel = () => {
setIsModalOpen(false);
};
const handleChange = (tag: number, checked: boolean) => {
if (checked) {
const nextWareSkuList = checked
? tag
: wareSkuList?.filter((t) => t.id !== tag)[0].id;
console.log('You are interested in: ', nextWareSkuList);
setSelectedTags(nextWareSkuList);
form.setFieldValue("id",tag)
}
};
const handleChangeDate = (tag: string, checked: boolean) => {
if (checked) {
const nextSelectedTags = checked
? tag
: tagsData.filter((t) => t !== tag)[0];
console.log('You are interested in: ', nextSelectedTags);
setSelectedTagsData(nextSelectedTags);
form.setFieldValue("date",tag)
}
};
return ( return (
<Layout> <Layout>
<Box> <Box>
...@@ -45,13 +136,36 @@ export default function EquipmentLeasingDetail() { ...@@ -45,13 +136,36 @@ export default function EquipmentLeasingDetail() {
</div>) : (<div className='function not'></div>) </div>) : (<div className='function not'></div>)
} }
<div className='menoy'> <div className='menoy'>
<span className='menoy-left'>¥{detail?.minRent}</span> <span className='menoy-left'>{`¥${detail?.minRent}`}</span>
<span className='menoy-right'>/天起</span> <span className='menoy-right'>/天起</span>
</div> </div>
<div className='classification'></div> <div className='classification'>
<div className='top'>
<div className='left'>
<span className='label'>选择</span>
<span className='value'>商品分类</span>
</div>
<div className='right'>
<Select
className="selectItem"
defaultActiveFirstOption
defaultValue={wareSkuList}
style={{ width: 120 }}
bordered={false}
options={wareSkuList}
fieldNames={{label:"skuTitle",value:"id"}}
placeholder="选择商品"
/>
</div>
</div>
<div className='bottom'>
<span className='label'>发货</span>
<span className='value'>顺丰到付</span>
</div>
</div>
<div className='botton-btn'> <div className='botton-btn'>
<Button className='btn-left' size='small' type="primary">成为渠道商</Button> <Button className='btn-left' size='small' type="primary">成为渠道商</Button>
<Button className='btn-right' size='small' type="primary">立即租赁</Button> <Button className='btn-right' size='small' type="primary" onClick={showModal}>立即租赁</Button>
</div> </div>
</div> </div>
</div> </div>
...@@ -66,6 +180,71 @@ export default function EquipmentLeasingDetail() { ...@@ -66,6 +180,71 @@ export default function EquipmentLeasingDetail() {
detail?.wareDetailContent ? <div style={{ textAlign: "center" }} dangerouslySetInnerHTML={{ __html: detail?.wareDetailContent}}> detail?.wareDetailContent ? <div style={{ textAlign: "center" }} dangerouslySetInnerHTML={{ __html: detail?.wareDetailContent}}>
</div> : <div style={{ textAlign: "center" }} ></div> </div> : <div style={{ textAlign: "center" }} ></div>
} }
{/* 立即租赁 */}
<Modal
wrapClassName='application'
open={isModalOpen}
onOk={handleOk}
onCancel={handleCancel}
getContainer={false}
footer={[
<Button
style={{ width: "100%" ,height: 44 }}
key="submit"
type="primary"
loading={loading}
onClick={handleOk}
>
立即租赁
</Button>,
]}
>
<div className='title'>
<div className="left"></div>
<div className="right">
<div className="top">
<span className='tag'>¥</span>
<span className='money'>{detail?.minRent}</span>
<span className='unit'>/天</span>
</div>
<div className="bottom">渠道免押金</div>
</div>
</div>
<Form
form={form}
layout="vertical"
name="application"
initialValues={{ modifier: 'public' }}
>
<Form.Item style={{flex:1,marginRight:16}} name="id" label="选择商品">
<Space size={[0, 8]} wrap>
{wareSkuList?.map((tag) => (
<CheckableTag
style={{height:28,lineHeight:"28px"}}
key={tag.id}
checked={wareSkuList?.some(item=>tag.id === selectedTags)}
onChange={(checked) => handleChange(tag.id, checked)}
>
{tag.skuTitle}
</CheckableTag>
))}
</Space>
</Form.Item>
<Form.Item style={{flex:1,marginRight:16}} name="date" label="租期天数(拿到和归还当天不算入租期)">
<Space size={[0, 8]} wrap>
{tagsData.map((tag) => (
<CheckableTag
key={tag}
checked={tag === selectedTagsData}
onChange={(checked) => handleChangeDate(tag, checked)}
>
{tag}
</CheckableTag>
))}
</Space>
</Form.Item>
</Form>
</Modal>
</Box> </Box>
</Layout> </Layout>
) )
......
...@@ -34,9 +34,48 @@ export interface GetWebDeviceDetailResult { ...@@ -34,9 +34,48 @@ export interface GetWebDeviceDetailResult {
wareDetailContent: string | TrustedHTML wareDetailContent: string | TrustedHTML
} }
export interface PriceList {
id: number,
wareInfoId: number,
skuInfoId: number,
rentPrice: number,
minDay: number,
maxDay: number,
createTime: null
}
export interface GetWebDeviceWareSkuById {
id: number,
wareInfoId: number,
skuTitle: string,
rentPrice: number | null,
rentDeposit: number,
stockNum: number,
saleNum: number,
createTime: string,
updateTime: null,
skuPriceDTOList: Array<PriceList>,
}
export interface WebDeviceUpdateParams {
id?:number,
inventoryId?:number,
inventoryUsage?:string,
startDay?:string
endDay?:string,
}
export default { export default {
//web-设备租赁-详情 //web-设备租赁-详情
listDetailDeviceInfo: (params: GetWebDeviceDetailParams): Promise<Response<GetWebDeviceDetailResult>> => { listDetailDeviceInfo: (params: GetWebDeviceDetailParams): Promise<Response<GetWebDeviceDetailResult>> => {
return request('/pms/webDevice/detail', 'get', params) return request('/pms/webDevice/detail', 'get', params)
},
//web-设备租赁-商品
listWareSkuById: (params: GetWebDeviceDetailParams): Promise<Response<GetWebDeviceWareSkuById[]>> => {
return request('/pms/appDevice/listWareSkuById', 'get', params)
},
//web-设备租赁-立即租赁
listWareSkuUpdate: (params: WebDeviceUpdateParams): Promise<Response<number>> => {
return request('/pms/appDevice/update', 'post', params)
} }
} }
\ No newline at end of file
...@@ -56,9 +56,40 @@ export const Box = styled.div` ...@@ -56,9 +56,40 @@ export const Box = styled.div`
} }
.classification{ .classification{
margin-top: 28px; margin-top: 28px;
width: 300px; width: 375px;
height: 50px; height: 50px;
background-color: pink; .label{
height: 21px;
font-size: 16px;
font-family: MicrosoftYaHei;
color: #9A9A9A;
line-height: 21px;
margin-right: 36px;
}
.value{
height: 21px;
font-size: 16px;
font-family: MicrosoftYaHei;
color: #151515;
line-height: 21px;
}
.top{
display: flex;
justify-content: space-between;
align-items: center;
.left{
}
.right{
.selectItem{
.ant-select-selection-placeholder {
color: #000;
}
}
}
}
.bottom{
margin-top: 5px;
}
} }
.botton-btn{ .botton-btn{
margin-top: 30px; margin-top: 30px;
...@@ -123,4 +154,49 @@ export const Box = styled.div` ...@@ -123,4 +154,49 @@ export const Box = styled.div`
color: #989898; color: #989898;
} }
} }
.application{
.title{
display: flex;
align-items: center;
padding-bottom: 25px;
.left{
width: 58px;
height: 58px;
background: #D8D8D8;
border-radius: 2px;
}
.right{
margin-left: 15px;
.top{
.tag{
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #FF0F0F;
}
.money{
font-size: 22px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #FF0F0F;
}
.unit{
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #FF0F0F;
}
}
.bottom{
width: 65px;
height: 18px;
font-size: 13px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #121212;
line-height: 18px;
}
}
}
}
` `
\ No newline at end of file
...@@ -59,7 +59,7 @@ export default function EquipmentLeasing(props: Props) { ...@@ -59,7 +59,7 @@ export default function EquipmentLeasing(props: Props) {
const rightDom = (item: Advertisement) => { const rightDom = (item: Advertisement) => {
return ( return (
<div key={item.id} className="right-box-item right-item"> <div key={item.id} className="right-box-item right-item">
<Image src={item.imageUrl} alt="error" width={270} height={422} /> <Image src={item.imageUrl} alt="error" width={180} height={295} />
</div> </div>
); );
}; };
...@@ -69,7 +69,7 @@ export default function EquipmentLeasing(props: Props) { ...@@ -69,7 +69,7 @@ export default function EquipmentLeasing(props: Props) {
const [abort, setAbort] = useState<AbortController | null>(null); //请求中断 const [abort, setAbort] = useState<AbortController | null>(null); //请求中断
const [pageParams, setPageParams] = useState({ const [pageParams, setPageParams] = useState({
pageNo: 1, pageNo: 1,
pageSize: 16, pageSize: 15,
}); //分页器对象 }); //分页器对象
const onPageChange = (page: number, pageSize: number) => { const onPageChange = (page: number, pageSize: number) => {
...@@ -141,9 +141,9 @@ export default function EquipmentLeasing(props: Props) { ...@@ -141,9 +141,9 @@ export default function EquipmentLeasing(props: Props) {
></Filter> ></Filter>
<div style={{ paddingTop: 13 }}> <div style={{ paddingTop: 13 }}>
<ContentBox <ContentBox
boxIndex={4} boxIndex={5}
leftcontentstyle={{ leftcontentstyle={{
width: "916px", width: "1020px",
margin: { top: 0, right: "12px", bottom: "12px", left: 0 }, margin: { top: 0, right: "12px", bottom: "12px", left: 0 },
}} }}
leftRenderDom={{ leftRenderDom={{
......
...@@ -10,16 +10,17 @@ export const Box = styled.div` ...@@ -10,16 +10,17 @@ export const Box = styled.div`
width: 1200px; width: 1200px;
.item { .item {
width: 220px; width: 220px;
height: 205px;
background: #ffffff; background: #ffffff;
border-radius: 6px; border-radius: 6px;
cursor: pointer; cursor: pointer;
transition: all 0.5s; transition: all 0.5s;
&-top { &-top {
display: flex;
justify-content: center;
align-items: center;
height: 145px; height: 145px;
background: #ffffff; background: #ffffff;
border-radius: 6px 6px 0px 0px; border-radius: 6px 6px 0px 0px;
padding: 19px 52px 10px 52px;
&-image { &-image {
width: 116px; width: 116px;
height: 116px; height: 116px;
...@@ -27,9 +28,8 @@ export const Box = styled.div` ...@@ -27,9 +28,8 @@ export const Box = styled.div`
} }
} }
&-bottom { &-bottom {
padding: 10px 13px 14px 18px; padding: 0px 13px 0px 18px;
&-title { &-title {
width: 189px;
height: 24px; height: 24px;
font-size: 14px; font-size: 14px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI; font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
...@@ -65,8 +65,8 @@ export const Box = styled.div` ...@@ -65,8 +65,8 @@ export const Box = styled.div`
} }
} }
.right-item { .right-item {
width: 270px; width: 180px;
height: 422px; height: 295px;
background: #d8d8d8; background: #d8d8d8;
border-radius: 6px; border-radius: 6px;
overflow: hidden; overflow: hidden;
......
import React from "react"; import React, { useEffect, useState } from "react";
import Layout from "~/components/layout"; import Layout from "~/components/layout";
import { Box } from "./styled"; import { Box } from "./styled";
import { Button } from "antd"; import { Button } from "antd";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { ParsedUrlQuery } from "querystring";
interface RouterDetail {
videoUrl:string | '',
curriculumName:string
}
export default function FlyingDetail() { export default function FlyingDetail() {
const router = useRouter(); const router = useRouter();
const [detail,setDetail] =useState<ParsedUrlQuery | RouterDetail>()
useEffect(()=>{
setDetail(router.query)
},[router])
return ( return (
<Layout> <Layout>
<Box> <Box>
<div className="box-top"> <div className="box-top">
<div className="left">科比特行业课程1111</div> <div className="left">{detail?.curriculumName}</div>
<div className="right"> <div className="right">
<Button <Button
type="primary" type="primary"
...@@ -24,7 +35,7 @@ export default function FlyingDetail() { ...@@ -24,7 +35,7 @@ export default function FlyingDetail() {
</div> </div>
</div> </div>
<div className="box-body"> <div className="box-body">
<video className="body-video" src="" /> <video className="body-video" controls src={detail?.videoUrl as string} />
</div> </div>
</Box> </Box>
</Layout> </Layout>
......
...@@ -39,7 +39,15 @@ export default function FlyingHandService() { ...@@ -39,7 +39,15 @@ export default function FlyingHandService() {
<div <div
className="item" className="item"
key={item.id} key={item.id}
onClick={() => router.push(`/flyingHandService/detail/${item.id}`)} onClick={() => {
router.push({
pathname: `/flyingHandService/detail/${item.id}`,
query: {
videoUrl: item.videoUrl ,
curriculumName: item.curriculumName
},
})
} }
> >
<div className="item-top"> <div className="item-top">
<Image <Image
...@@ -193,17 +201,34 @@ export default function FlyingHandService() { ...@@ -193,17 +201,34 @@ export default function FlyingHandService() {
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const handleOk = async (values: any) => { const handleOk = async (values: any) => {
const value = form.getFieldsValue()
setLoading(true); form
try{ .validateFields()
const res = await api.PilotRegistrations(value) .then(async (values) => {
setLoading(true);
try{
const res = await api.PilotRegistrations(values)
if (res.code === "200") {
setLoading(false); setLoading(false);
setIsModalOpen(false); setIsModalOpen(false);
form.resetFields() form.resetFields()
message.success('报名成功') message.success('报名成功')
}else{
setLoading(false);
message.error(res.message)
}
}catch(e:any){ }catch(e:any){
message.error(e.message) message.error(e.message)
} }
}).catch((err) => {
message
.warning({
content: err.errorFields[0].errors[0],
})
.then();
});
}; };
const handleCancel = () => { const handleCancel = () => {
...@@ -309,21 +334,21 @@ export default function FlyingHandService() { ...@@ -309,21 +334,21 @@ export default function FlyingHandService() {
<div style={{display:"flex",justifyContent:"space-between"}}> <div style={{display:"flex",justifyContent:"space-between"}}>
<Form.Item style={{flex:1,marginRight:16}} <Form.Item style={{flex:1,marginRight:16}}
name="name" name="name"
rules={[{ required: true, message: 'Please input the title of collection!' }]} rules={[{ required: true, message: '请输入姓名!' }]}
> >
<Input placeholder="姓名" /> <Input placeholder="姓名" />
</Form.Item> </Form.Item>
<Form.Item style={{flex:1}} name="telephone"> <Form.Item style={{flex:1}} name="telephone" rules={[{ required: true, message: '请输入手机号!' }]}>
<Input placeholder="手机号" /> <Input placeholder="手机号" />
</Form.Item> </Form.Item>
</div> </div>
<Form.Item <Form.Item
name="city" name="city"
rules={[{ required: true, message: 'Please select gender!' }]} rules={[{ required: true, message: '请选择城市!' }]}
> >
<Cascader <Cascader
allowClear allowClear
placeholder="地域" placeholder="城市"
className="selectItem" className="selectItem"
size="large" size="large"
fieldNames={{ fieldNames={{
...@@ -338,16 +363,14 @@ export default function FlyingHandService() { ...@@ -338,16 +363,14 @@ export default function FlyingHandService() {
<Form.Item <Form.Item
name="drivingLicense" name="drivingLicense"
rules={[{ required: true, message: 'Please select gender!' }]}
> >
<Select placeholder="是否有驾照"> <Select allowClear placeholder="是否有驾照">
<Option value="0"></Option> <Option value="0"></Option>
<Option value="1"></Option> <Option value="1"></Option>
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="uavLicenseLevelOne" name="uavLicenseLevelOne"
rules={[{ required: true, message: 'Please select gender!' }]}
> >
<Cascader <Cascader
allowClear allowClear
......
import request, { Response } from "~/api/request"; import request, { Response } from "~/api/request";
export interface DynamicListParams { export interface DynamicListParams {
pageNo: number, pageNo: number;
pageSize: number, pageSize: number;
userId: number userId: number;
} }
export interface Dynamic { export interface Dynamic {
...@@ -31,6 +31,24 @@ export interface DynamicListResp { ...@@ -31,6 +31,24 @@ export interface DynamicListResp {
totalPage: 0; totalPage: 0;
} }
export interface CommentParams {
content: string; //评论内容
dynamicId: number; //动态id
parentId?: number; //父级评论
userId: number; //用户id
}
export interface ByDynamicParams {
id: number;
dynamicId: number;
parentId: number;
userId: number;
content: string;
likesCount: number;
createTime: string;
children: ByDynamicParams[];
}
export default { export default {
/** /**
* 论坛动态列表 * 论坛动态列表
...@@ -40,4 +58,14 @@ export default { ...@@ -40,4 +58,14 @@ export default {
dynamicList(params: DynamicListParams): Promise<Response<DynamicListResp>> { dynamicList(params: DynamicListParams): Promise<Response<DynamicListResp>> {
return request("/release/dynamic/dynamicList", "get", params); return request("/release/dynamic/dynamicList", "get", params);
}, },
//评论
comment(params: CommentParams): Promise<Response<null>> {
return request("/release/dynamic/comment", "post", params);
},
//根据动态查看评论
byDynamic(params: { dynamicId: number }): Promise<Response<Array<ByDynamicParams>>> {
return request("/release/dynamic/byDynamic", "get", params);
},
}; };
import request, { Response } from "~/api/request";
export interface PublishParams {
lat?: number; //纬度
lon?: number; //经度
description: string; //描述
userId: number; //用户id
mediaVO: {
//发布图片
picture: Array<string>;
};
}
export default {
//动态发布
publish(params: PublishParams): Promise<Response<null>> {
return request("/release/dynamic/publish", "post", params);
},
};
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from "@ant-design/icons";
import { Form, Input, Modal, Upload } from "antd"; import { Form, Input, Modal, Upload, Image, Button, Row, Col } from "antd";
import type { RcFile, UploadProps } from "antd/es/upload"; import type { RcFile, UploadProps } from "antd/es/upload";
import type { UploadFile } from "antd/es/upload/interface"; import type { UploadFile } from "antd/es/upload/interface";
import { useState } from "react"; import { useState } from "react";
import Image from "next/image"; import gApi from "~/api";
import NImage from "next/image";
const getBase64 = (file: RcFile): Promise<string> => import api from "./api";
new Promise((resolve, reject) => { import { useGeolocation, useUser } from "~/lib/hooks";
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result as string);
reader.onerror = (error) => reject(error);
});
type Props = { type Props = {
open: boolean; open: boolean;
onCancel: () => void;
onOk?: () => void; onOk?: () => void;
onCancel?: () => void; };
const normFile = (e: any) => {
console.log("Upload event:", e);
if (Array.isArray(e)) {
return e;
}
return e?.fileList;
}; };
export default function PublishMessage(props: Props) { export default function PublishMessage(props: Props) {
...@@ -24,98 +27,110 @@ export default function PublishMessage(props: Props) { ...@@ -24,98 +27,110 @@ export default function PublishMessage(props: Props) {
const [previewOpen, setPreviewOpen] = useState(false); const [previewOpen, setPreviewOpen] = useState(false);
const [previewImage, setPreviewImage] = useState(""); const [previewImage, setPreviewImage] = useState("");
const [previewTitle, setPreviewTitle] = useState(""); const [previewTitle, setPreviewTitle] = useState("");
const [fileList, setFileList] = useState<UploadFile[]>([ const [fileList, setFileList] = useState<UploadFile[]>([]);
{ const [showLoading, setShowLoad] = useState(false);
uid: "-1", const [form] = Form.useForm();
name: "image.png", const user = useUser();
status: "done", const position = useGeolocation();
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 handleOk = () => {
setConfirmLoading(true);
setTimeout(() => {
handleCancel();
setConfirmLoading(false);
}, 2000);
};
const handleCancel = () => {
props.onCancel && props.onCancel();
};
//预览关闭
const handlePreviewCancel = () => setPreviewOpen(false); const handlePreviewCancel = () => setPreviewOpen(false);
//图片预览
const handlePreview = async (file: UploadFile) => { const handlePreview = async (file: UploadFile) => {
if (!file.url && !file.preview) { if (file.url) {
file.preview = await getBase64(file.originFileObj as RcFile); setPreviewImage(file.url);
setPreviewOpen(true);
setPreviewTitle(
file.name || file.url!.substring(file.url!.lastIndexOf("/") + 1)
);
} }
};
setPreviewImage(file.url || (file.preview as string)); //图片上传
setPreviewOpen(true); const handleChange: UploadProps["onChange"] = (info) => {
setPreviewTitle( console.log("uploadChange", info);
file.name || file.url!.substring(file.url!.lastIndexOf("/") + 1) if (info.file.status === "uploading") {
); let find = fileList.find((item) => {
return item.uid === info.file.uid;
});
if (!find) {
setFileList([...fileList, info.file]);
}
return;
}
if (info.file.status === "done") {
// Get this url from response in real world.
if (info.file.response.code === "200" && info.file.response.result) {
let fileList1 = fileList.map((item) => {
if (item.uid === info.file.uid) {
info.file.url = info.file.response.result?.[0];
return info.file;
}
return item;
});
setFileList([...fileList1]);
} else {
window.messageApi.error(info.file.response?.message || "上传失败");
info.fileList = fileList.filter((item) => {
return item.uid !== info.file.uid;
});
setFileList([...info.fileList]);
}
}
}; };
const handleChange: UploadProps["onChange"] = ({ fileList: newFileList }) => //提交
setFileList(newFileList); const onFinish = (values: any) => {
console.log(values);
setShowLoad(true);
api
.publish({
lat: position?.position?.lat, //纬度
lon: position?.position?.lng, //经度
title: '', //标题
description: values.description, //描述
userId: user!.id, //用户id
mediaVO: {
//发布图片
//@ts-ignore
picture: fileList.filter((item) => item.url).map((item) => item.url),
},
})
.then((res) => {
console.log("提交结果", res);
setShowLoad(false);
if (res.code === "200") {
window.messageApi.success("发布成功");
props.onCancel();
props.onOk && props.onOk();
setTimeout(() => {
form.resetFields(["title", "description"]);
setFileList([]);
}, 500);
}
});
};
const uploadButton = (
<div>
<PlusOutlined />
<div style={{ marginTop: 8 }}>Upload</div>
</div>
);
return ( return (
<Modal <Modal
title="" title=""
open={props.open} open={props.open}
onOk={handleOk} onCancel={props.onCancel}
onCancel={handleCancel}
width={500} width={500}
confirmLoading={confirmLoading} confirmLoading={confirmLoading}
okText="发布" footer={null}
okButtonProps={{ style: { height: 37, padding: "0 32px", fontSize: 16 } }} okButtonProps={{ style: { height: 37, padding: "0 32px", fontSize: 16 } }}
cancelButtonProps={{ style: { display: "none" } }} cancelButtonProps={{ style: { display: "none" } }}
maskClosable={false} maskClosable={false}
> >
<Form style={{ paddingTop: 32 }}> <Form style={{ paddingTop: 32 }} onFinish={onFinish} form={form}>
<Form.Item> <Form.Item
<Input placeholder="输入标题" style={{ height: 44 }}></Input> name="description"
</Form.Item> rules={[{ required: true }]}
<Form.Item> help="请输入内容"
>
<Input.TextArea <Input.TextArea
allowClear allowClear
showCount showCount
...@@ -124,25 +139,56 @@ export default function PublishMessage(props: Props) { ...@@ -124,25 +139,56 @@ export default function PublishMessage(props: Props) {
style={{ height: 120, resize: "none" }} style={{ height: 120, resize: "none" }}
></Input.TextArea> ></Input.TextArea>
</Form.Item> </Form.Item>
<Form.Item> <Form.Item valuePropName="picture" getValueFromEvent={normFile}>
<Upload <Upload
action="https://www.mocky.io/v2/5cc8019d300000980a055e76" name="uploadFile"
action={gApi.imgOss}
listType="picture-card" listType="picture-card"
fileList={fileList} fileList={fileList}
onPreview={handlePreview} onPreview={handlePreview}
onChange={handleChange} onChange={handleChange}
maxCount={1}
> >
{fileList.length >= 8 ? null : uploadButton} {fileList.length >= 8 ? null : (
<div>
<PlusOutlined />
<div style={{ marginTop: 8 }}>Upload</div>
</div>
)}
</Upload> </Upload>
<Modal <Modal
open={previewOpen} open={previewOpen}
title={previewTitle} title={previewTitle}
footer={null} footer={null}
onCancel={handlePreviewCancel} onCancel={handlePreviewCancel}
bodyStyle={{ textAlign: "center" }}
> >
<Image alt="example" style={{ width: "100%" }} src={previewImage} /> <Image alt="example" src={previewImage} preview={false} />
</Modal> </Modal>
</Form.Item> </Form.Item>
<Row justify="space-between" align="middle">
<Col>
<NImage
style={{ verticalAlign: "text-top", marginRight: 7 }}
alt=""
src={require("./assets/position.png")}
width={14}
height={17}
/>
{position?.address?.formattedAddress || "位置"}
</Col>
<Col>
<Button
type="primary"
htmlType="submit"
size="large"
style={{ width: 95 }}
loading={showLoading}
>
发布
</Button>
</Col>
</Row>
</Form> </Form>
</Modal> </Modal>
); );
......
...@@ -174,15 +174,27 @@ interface ListTenderNewsInfoParams { ...@@ -174,15 +174,27 @@ interface ListTenderNewsInfoParams {
provinceCode?: number; provinceCode?: number;
} }
export interface TenderApplyType{
tenderInfoId: number,
tenderNewsId: number,
userAccountId: number
}
export const listNewsApi = { export const listNewsApi = {
//新闻列表
listNewsPage: ( listNewsPage: (
params: ListPageNewsInfoParams params: ListPageNewsInfoParams
): Promise<Response<ListPageNewsInfoResp>> => { ): Promise<Response<ListPageNewsInfoResp>> => {
return request("/release/industry-news/listNewsPage", "post", params); return request("/release/industry-news/listNewsPage", "post", params);
}, },
//招标列表
listNewTenderInfo: ( listNewTenderInfo: (
params: ListTenderNewsInfoParams params: ListTenderNewsInfoParams
): Promise<Response<ListTenderNewsInfoResp>> => { ): Promise<Response<ListTenderNewsInfoResp>> => {
return request("/release/tender/listNewTenderInfo", "post", params); return request("/release/tender/listNewTenderInfo", "post", params);
}, },
}; //web-招标-合作申请提交
tenderApply: (params: TenderApplyType): Promise<Response<number>> => {
return request('/release/tender/apply', 'post', params)
}
}
...@@ -63,10 +63,10 @@ export default function MapComponent() { ...@@ -63,10 +63,10 @@ export default function MapComponent() {
} }
//解析定位错误信息 //解析定位错误信息
async function onError(data:any) { async function onError(data:any) {
message.error(`定位失败 // message.error(`定位失败
失败原因排查信息:${data.message} // 失败原因排查信息:${data.message}
浏览器返回信息:${data.originMessage} // 浏览器返回信息:${data.originMessage}
`) // `)
} }
await mapEntiy(0) await mapEntiy(0)
}) })
...@@ -85,7 +85,7 @@ export default function MapComponent() { ...@@ -85,7 +85,7 @@ export default function MapComponent() {
const list = res.result const list = res.result
?.map((item) => item.locationList) ?.map((item) => item.locationList)
.flat() .flat()
.filter((item: { dizhi: string }) => item.dizhi.includes("广东省")); .filter((item: { dizhi: string }) => item.dizhi.includes("省"));
const markerList: any = []; const markerList: any = [];
if (list?.length) { if (list?.length) {
list?.map((item) => { list?.map((item) => {
...@@ -100,7 +100,7 @@ export default function MapComponent() { ...@@ -100,7 +100,7 @@ export default function MapComponent() {
lon: userPositioning?.lon || data?.lon || 113.93029, lon: userPositioning?.lon || data?.lon || 113.93029,
lat: userPositioning?.lat || data?.lat || 22.53291, lat: userPositioning?.lat || data?.lat || 22.53291,
pageNo:1, pageNo:1,
pageSize: pageSize || 10 pageSize: pageSize || 40
}); });
const list = res.result?.list const list = res.result?.list
const markerList: any = []; const markerList: any = [];
...@@ -118,7 +118,7 @@ export default function MapComponent() { ...@@ -118,7 +118,7 @@ export default function MapComponent() {
lon: userPositioning?.lon || data?.lon || 113.93029, lon: userPositioning?.lon || data?.lon || 113.93029,
lat: userPositioning?.lat || data?.lat || 22.53291, lat: userPositioning?.lat || data?.lat || 22.53291,
pageNo:1, pageNo:1,
pageSize: 10 pageSize: 40
}); });
const list = res.result?.list const list = res.result?.list
const markerList: any = []; const markerList: any = [];
...@@ -137,7 +137,7 @@ export default function MapComponent() { ...@@ -137,7 +137,7 @@ export default function MapComponent() {
if (index === 0) { if (index === 0) {
showPositioningInfo(index,data) showPositioningInfo(index,data)
}else if (index === 1) { }else if (index === 1) {
showFlyerBitmap(index,data) showFlyerBitmap(index,data,30)
} else if(index === 2) { } else if(index === 2) {
showUavBitmap(index,data) showUavBitmap(index,data)
}else{ }else{
......
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { Space, Select, Button } from "antd"; import { Space, Select, Button, message } from "antd";
import Image from "next/image"; import Image from "next/image";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { Box } from "./styled"; import { Box } from "./styled";
...@@ -266,6 +266,31 @@ export default function WaterfallFlowBody() { ...@@ -266,6 +266,31 @@ export default function WaterfallFlowBody() {
} }
}; };
const handleTenderApply = async (item:NewsTenderType)=>{
if (item.apply) return;
message.success("申请成功")
item.apply = 1
// let res = await listNewsApi.tenderApply({
// tenderInfoId: item.id,
// tenderNewsId: item.tenderNewsId,
// userAccountId: 0,
// })
// try{
// if (res.code==="200") {
// message.success("申请成功")
// let res8 = await listNewsApi.listNewTenderInfo({
// pageNo: 1,
// pageSize: 6,
// });
// setRightBottomDomList(rightDom2(res8.result?.list!));
// }else{
// message.error(res.message)
// }
// }catch(e){
// console.log(e);
// }
}
const leftDom = ( const leftDom = (
item: ColumnsType, item: ColumnsType,
index: number, index: number,
...@@ -273,7 +298,7 @@ export default function WaterfallFlowBody() { ...@@ -273,7 +298,7 @@ export default function WaterfallFlowBody() {
option: [] option: []
) => { ) => {
return ( return (
<div key={item.title} className="item"> <div key={item.title} className={`item ${index>=3?'right-box':''}`}>
<div className="item-title"> <div className="item-title">
<div className="item-left"> <div className="item-left">
<div className="item-left-label" onClick={() => routerPath(index)}> <div className="item-left-label" onClick={() => routerPath(index)}>
...@@ -355,7 +380,7 @@ export default function WaterfallFlowBody() { ...@@ -355,7 +380,7 @@ export default function WaterfallFlowBody() {
</div> </div>
<div className="body"> <div className="body">
{list?.map((item, index) => ( {list?.map((item, index) => (
<div key={item.id} className="body-item"> <div key={item.id} className="body-item" onClick={()=>router.push(`/projectInfo/newsArticle/${item.id}`)}>
<div <div
className={`item-ranking ${index === 0 ? "one" : ""} ${ className={`item-ranking ${index === 0 ? "one" : ""} ${
index === 1 ? "two" : "" index === 1 ? "two" : ""
...@@ -374,7 +399,7 @@ export default function WaterfallFlowBody() { ...@@ -374,7 +399,7 @@ export default function WaterfallFlowBody() {
}; };
const rightDom2 = (list: Array<NewsTenderType>) => { const rightDom2 = (list: Array<NewsTenderType>) => {
if(!list.length) return; if(!list?.length) return;
return ( return (
<div key={1008} className="right-box-item right-item-second"> <div key={1008} className="right-box-item right-item-second">
<div className="item-box"> <div className="item-box">
...@@ -393,9 +418,12 @@ export default function WaterfallFlowBody() { ...@@ -393,9 +418,12 @@ export default function WaterfallFlowBody() {
{item.tenderContent} {item.tenderContent}
<div className="label-bottom">{item.tenderPrice}</div> <div className="label-bottom">{item.tenderPrice}</div>
</div> </div>
<div className="item-right"> <div className={`item-right ${item.apply ? 'apply' : ''}`} onClick={()=>handleTenderApply(item)}>
<div className="left">{`${item.tenderPrice}W`}</div> <div className="left">{`${item.tenderPrice}W`}</div>
<div className="right">申请合作</div> {
item.apply ? <div className="right">已申请</div> : <>
<div className="right">申请合作</div></>
}
</div> </div>
</div> </div>
))} ))}
......
...@@ -13,7 +13,7 @@ export const Box = styled.div` ...@@ -13,7 +13,7 @@ export const Box = styled.div`
margin: 0 auto; margin: 0 auto;
.item { .item {
transition: all 0.5s; transition: all 0.5s;
border-radius: 6px; border-radius: 6px;
&-title { &-title {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
...@@ -117,7 +117,7 @@ export const Box = styled.div` ...@@ -117,7 +117,7 @@ export const Box = styled.div`
&-label { &-label {
cursor: pointer; cursor: pointer;
font-size: 16px; font-size: 16px;
font-weight: 100; font-weight: bold;
&:hover { &:hover {
color: #ff552d; color: #ff552d;
} }
...@@ -162,6 +162,7 @@ export const Box = styled.div` ...@@ -162,6 +162,7 @@ export const Box = styled.div`
align-items: center; align-items: center;
padding: 0 23px 0 19px; padding: 0 23px 0 19px;
height: 32px; height: 32px;
cursor: pointer;
.item-ranking { .item-ranking {
color: #9295a3; color: #9295a3;
&.one { &.one {
...@@ -184,6 +185,9 @@ export const Box = styled.div` ...@@ -184,6 +185,9 @@ export const Box = styled.div`
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
&:hover {
color: #ff552d;
}
} }
} }
} }
...@@ -200,13 +204,12 @@ export const Box = styled.div` ...@@ -200,13 +204,12 @@ export const Box = styled.div`
height: 48px; height: 48px;
font-size: 16px; font-size: 16px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI; font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold;
color: #000000; color: #000000;
line-height: 25px; line-height: 25px;
&-label { &-label {
cursor: pointer; cursor: pointer;
font-size: 16px; font-size: 16px;
font-weight: 100; font-weight: bold;
&:hover { &:hover {
color: #ff552d; color: #ff552d;
} }
...@@ -282,6 +285,11 @@ export const Box = styled.div` ...@@ -282,6 +285,11 @@ export const Box = styled.div`
height: 22px; height: 22px;
background: url(${button.src}) no-repeat; background: url(${button.src}) no-repeat;
background-size: contain; background-size: contain;
cursor: pointer;
&.apply{
opacity: 0.5;
cursor: not-allowed;
}
.left { .left {
width: 35px; width: 35px;
height: 22px; height: 22px;
......
...@@ -7,6 +7,7 @@ import Layout from "~/components/layout"; ...@@ -7,6 +7,7 @@ import Layout from "~/components/layout";
import newsApi, { Item } from "../components/news/api"; import newsApi, { Item } from "../components/news/api";
import api, { DetailsResp } from "./api"; import api, { DetailsResp } from "./api";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import Moment from 'moment';
export default function CaseArticle() { export default function CaseArticle() {
const router = useRouter(); const router = useRouter();
...@@ -32,13 +33,36 @@ export default function CaseArticle() { ...@@ -32,13 +33,36 @@ export default function CaseArticle() {
newsApi newsApi
.listNewsPage({ .listNewsPage({
pageNo: 1, pageNo: 1,
pageSize: 5, pageSize: 10,
}) })
.then((res) => { .then((res) => {
setNewList(res.result?.list || []); setNewList(res.result?.list || []);
}); });
}, []); }, []);
const fontColor = (i: number) => {
switch (i) {
case 0:
return {
color: "#ff2c46",
};
case 1:
return {
color: "#FF6602",
};
case 2:
return {
color: "#FAA910",
};
default:
return {
color: "#9295A3",
};
}
};
return ( return (
<Layout layoutStyle={{ backgroundColor: "#fff" }}> <Layout layoutStyle={{ backgroundColor: "#fff" }}>
<div style={{ paddingTop: 29 }}> <div style={{ paddingTop: 29 }}>
...@@ -49,7 +73,7 @@ export default function CaseArticle() { ...@@ -49,7 +73,7 @@ export default function CaseArticle() {
className={styles.font2} className={styles.font2}
style={{ marginTop: 18, marginBottom: 41 }} style={{ marginTop: 18, marginBottom: 41 }}
> >
{data?.createTime} · {data?.caseAuthor} {data?.createTime && Moment(data?.createTime).format('YYYY-MM-DD')} · {data?.caseAuthor}
</div> </div>
<div <div
style={{ lineHeight: 1.5 }} style={{ lineHeight: 1.5 }}
...@@ -61,14 +85,14 @@ export default function CaseArticle() { ...@@ -61,14 +85,14 @@ export default function CaseArticle() {
<Row <Row
className={styles.font4} className={styles.font4}
align="middle" align="middle"
style={{ paddingTop: 24, paddingLeft: 24 }} style={{ paddingTop: 16, paddingLeft: 20 }}
> >
行业新闻 行业新闻
<RightOutlined style={{ fontSize: 16, marginLeft: 15 }} /> <RightOutlined style={{ fontSize: 16, marginLeft: 9 }} />
</Row> </Row>
<Row gutter={10} style={{ marginTop: 18 }}> <Row gutter={10} style={{ marginTop: 8 }}>
<Col span={24}> <Col span={24}>
{newsList.map((item) => { {newsList.map((item, i) => {
return ( return (
<Row <Row
key={item.id} key={item.id}
...@@ -80,20 +104,20 @@ export default function CaseArticle() { ...@@ -80,20 +104,20 @@ export default function CaseArticle() {
}} }}
> >
<Col <Col
className={`${styles.font3} ${styles.ellipse2}`} className={`${styles.font3}`}
style={{ width: 217, padding: "13px 0 17px 20px" }} style={{ margin: "6px 23px 6px 19px", width: "100%" }}
> >
{item.newsTitle} <Row wrap={false}>
</Col> <Col
<Col style={{ paddingRight: 16 }}> span={2}
<Image style={{ textAlign: "center", ...fontColor(i) }}
src={item.surfaceImg} >
width={90} {i + 1}
height={60} </Col>
preview={false} <Col span={22} className={styles.ellipse1}>
fallback={errImg} {item.newsTitle}
style={{ borderRadius: 6 }} </Col>
></Image> </Row>
</Col> </Col>
</Row> </Row>
); );
......
...@@ -16,27 +16,24 @@ ...@@ -16,27 +16,24 @@
} }
.font3 { .font3 {
font-size: 16px; font-size: 14px;
font-family: MicrosoftYaHei; font-family: MicrosoftYaHei;
color: #323232; color: #323232;
line-height: 21px; line-height: 19px;
} }
.font4 { .font4 {
font-size: 20px; font-size: 16px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI; font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold; font-weight: bold;
color: #000000; color: #000000;
line-height: 25px; line-height: 20px;
} }
.newsBox { .newsBox {
width: 384px; width: 384px;
height: 491px;
background-image: url("./assets/bk.png");
background-size: 100% 100%;
} }
.ellipse2 { .ellipse1 {
@include ellipsis(2); @include ellipsis(1);
} }
import { Button } from "antd"; import { Button, Empty, Pagination, Spin } from "antd";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import api, { Item } from "./api"; import api, { Item } from "./api";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
...@@ -16,10 +16,11 @@ export default function Bids(props: Props) { ...@@ -16,10 +16,11 @@ export default function Bids(props: Props) {
const [list, setList] = useState<Array<Item>>([]); const [list, setList] = useState<Array<Item>>([]);
const [pageParams, setPageParams] = useState({ const [pageParams, setPageParams] = useState({
pageNo: 1, pageNo: 1,
pageSize: 5, pageSize: 10,
}); });
const [count, setCount] = useState(0); const [count, setCount] = useState(0);
const [abort, setAbort] = useState<AbortController | null>(null); const [abort, setAbort] = useState<AbortController | null>(null);
const [loading, setLoading] = useState(false);
useEffect(() => { useEffect(() => {
//中断前一次请求 //中断前一次请求
...@@ -31,6 +32,7 @@ export default function Bids(props: Props) { ...@@ -31,6 +32,7 @@ export default function Bids(props: Props) {
if (!abort) { if (!abort) {
return; return;
} }
setLoading(true);
api api
.listNewTenderInfo( .listNewTenderInfo(
{ {
...@@ -44,35 +46,58 @@ export default function Bids(props: Props) { ...@@ -44,35 +46,58 @@ export default function Bids(props: Props) {
.then((res) => { .then((res) => {
setList(res.result?.list || []); setList(res.result?.list || []);
setCount(res.result?.totalCount || 0); setCount(res.result?.totalCount || 0);
setLoading(false);
}); });
}, [abort]); }, [abort]);
const onPageChange = (page: number, pageSize: number) => {
setPageParams({
...pageParams,
pageNo: page,
});
};
return ( return (
<div className={styles.bids}> <Spin spinning={loading} delay={500}>
{list.map((item) => { <div className={styles.bids} style={{ height: 610 }}>
return ( {list.map((item) => {
<div className={styles.item} key={item.id}> return (
<div className={styles.info}> <div className={styles.item} key={item.id}>
<div className={styles.title}>项目需求:{item.tenderContent}</div> <div className={styles.info}>
<div className={styles.title}>
{item.tenderContent}
</div>
</div>
{item.apply ? (
<Button
type="primary"
disabled
className={`${styles.btn} ${styles.disabled}`}
>
<div className={styles.text1}>{item.tenderPrice}</div>
<div className={styles.text2}>已申请</div>
</Button>
) : (
<Button type="primary" className={styles.btn}>
<div className={styles.text1}>{item.tenderPrice}</div>
<div className={styles.text2}>申请合作</div>
</Button>
)}
</div> </div>
{item.apply ? ( );
<Button })}
type="primary" {list.length === 0 && <Empty></Empty>}
disabled </div>
className={`${styles.btn} ${styles.disabled}`} <Pagination
> current={pageParams.pageNo}
<div className={styles.text1}>{item.tenderPrice}</div> defaultPageSize={pageParams.pageSize}
<div className={styles.text2}>已申请</div> showSizeChanger={false}
</Button> showQuickJumper
) : ( total={count}
<Button type="primary" className={styles.btn}> onChange={onPageChange}
<div className={styles.text1}>{item.tenderPrice}</div> hideOnSinglePage={true}
<div className={styles.text2}>申请合作</div> style={{ marginTop: 20 }}
</Button> />
)} </Spin>
</div>
);
})}
</div>
); );
} }
...@@ -2,14 +2,13 @@ ...@@ -2,14 +2,13 @@
//项目需求 //项目需求
.casas { .casas {
padding-top: 20px;
.item { .item {
padding: 39px 14px 39px 16px; padding: 7px 14px 9px 16px;
display: flex; display: flex;
border-bottom: 1px dashed RGBA(222, 222, 222, 1); border-bottom: 1px dashed RGBA(222, 222, 222, 1);
border-bottom: 1px dashed RGBA(222, 222, 222, 1); border-bottom: 1px dashed RGBA(222, 222, 222, 1);
justify-content: space-between; justify-content: space-between;
align-items: center;
.info { .info {
.title { .title {
...@@ -30,12 +29,10 @@ ...@@ -30,12 +29,10 @@
.btn { .btn {
width: 120px; width: 120px;
height: 40px; height: 34px;
border-radius: 6px; border-radius: 6px;
font-size: 14px; font-size: 14px;
font-family: MicrosoftYaHei; font-family: MicrosoftYaHei;
color: #ff552d;
border: 1px solid #ff552d;
} }
.btnDisabled { .btnDisabled {
......
import { Button } from "antd"; import { Button, Empty, Pagination, Spin } from "antd";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import api, { Item } from "./api"; import api, { Item } from "./api";
...@@ -17,10 +17,12 @@ export default function Cases(props: Props) { ...@@ -17,10 +17,12 @@ export default function Cases(props: Props) {
const [list, setList] = useState<Array<Item>>([]); const [list, setList] = useState<Array<Item>>([]);
const [pageParams, setPageParams] = useState({ const [pageParams, setPageParams] = useState({
pageNo: 1, pageNo: 1,
pageSize: 5, pageSize: 12,
}); });
const [count, setCount] = useState(0); const [count, setCount] = useState(0);
const [abort, setAbort] = useState<AbortController | null>(null); const [abort, setAbort] = useState<AbortController | null>(null);
const [loading, setLoading] = useState(false);
const Router = useRouter(); const Router = useRouter();
useEffect(() => { useEffect(() => {
...@@ -33,6 +35,7 @@ export default function Cases(props: Props) { ...@@ -33,6 +35,7 @@ export default function Cases(props: Props) {
if (!abort) { if (!abort) {
return; return;
} }
setLoading(true);
api api
.listCasePage( .listCasePage(
{ {
...@@ -46,26 +49,50 @@ export default function Cases(props: Props) { ...@@ -46,26 +49,50 @@ export default function Cases(props: Props) {
.then((res) => { .then((res) => {
setList(res.result?.list || []); setList(res.result?.list || []);
setCount(res.result?.totalCount || 0); setCount(res.result?.totalCount || 0);
setLoading(false);
}); });
}, [abort]); }, [abort]);
const onPageChange = (page: number, pageSize: number) => {
setPageParams({
...pageParams,
pageNo: page,
});
};
return ( return (
<div className={styles.casas}> <Spin spinning={loading} delay={500}>
{list.map((item) => { <div className={styles.casas} style={{ height: 612 }}>
return ( {list.map((item) => {
<div className={styles.item} key={item.id}> return (
<div className={styles.info}> <div className={styles.item} key={item.id}>
<div className={styles.title}>{item.caseTitle}</div> <div className={styles.info}>
<div className={styles.title}>{item.caseTitle}</div>
</div>
<Button
type="primary"
className={styles.btn}
onClick={() =>
Router.push("/projectInfo/caseArticle/" + item.id)
}
>
申请合作
</Button>
</div> </div>
<Button );
className={styles.btn} })}
onClick={() => Router.push("/projectInfo/caseArticle/" + item.id)} {list.length === 0 && <Empty></Empty>}
> </div>
查看案例 <Pagination
</Button> current={pageParams.pageNo}
</div> defaultPageSize={pageParams.pageSize}
); showSizeChanger={false}
})} showQuickJumper
</div> total={count}
onChange={onPageChange}
hideOnSinglePage={true}
style={{ marginTop: 20 }}
/>
</Spin>
); );
} }
...@@ -5,11 +5,8 @@ ...@@ -5,11 +5,8 @@
} }
.new { .new {
padding-top: 20px;
width: 750px;
.item { .item {
cursor: pointer;
padding: 10px 12px; padding: 10px 12px;
display: flex; display: flex;
border-bottom: 1px dashed RGBA(222, 222, 222, 1); border-bottom: 1px dashed RGBA(222, 222, 222, 1);
...@@ -20,19 +17,39 @@ ...@@ -20,19 +17,39 @@
height: 80px; height: 80px;
background: #d8d8d8; background: #d8d8d8;
border-radius: 6px; border-radius: 6px;
margin-right: 22px; margin-right: 17px;
@include ellipsis(1); @include ellipsis(1);
flex-shrink: 0; flex-shrink: 0;
} }
.info { .info {
padding: 3px 0 2px; padding-left: 3px;
padding-right: 83px;
.title { .title {
font-size: 16px; font-size: 16px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI; font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold; font-weight: bold;
color: #000000; color: #000000;
line-height: 22px;
@include ellipsis(1);
}
.desc {
font-size: 14px;
font-family: MicrosoftYaHei;
color: #878787;
line-height: 22px;
margin-top: 6px;
@include ellipsis(1);
}
.date {
font-size: 12px;
font-family: MicrosoftYaHei;
color: #adadad;
line-height: 16px;
margin-top: 5px;
} }
} }
} }
......
import { RightOutlined } from "@ant-design/icons"; import { RightOutlined } from "@ant-design/icons";
import { Col, Row } from "antd"; import { Button, Col, Empty, Pagination, Row, Spin } from "antd";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import Image from "next/image"; import Image from "next/image";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import api, { Item } from "./api"; import api, { Item } from "./api";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Moment from "moment";
type Props = { type Props = {
params?: { params?: {
...@@ -16,10 +17,11 @@ type Props = { ...@@ -16,10 +17,11 @@ type Props = {
}; };
export default function News(props: Props) { export default function News(props: Props) {
const [loading, setLoading] = useState(false);
const [list, setList] = useState<Array<Item>>([]); const [list, setList] = useState<Array<Item>>([]);
const [pageParams, setPageParams] = useState({ const [pageParams, setPageParams] = useState({
pageNo: 1, pageNo: 1,
pageSize: 5, pageSize: 6,
}); });
const [count, setCount] = useState(0); const [count, setCount] = useState(0);
const [abort, setAbort] = useState<AbortController | null>(null); const [abort, setAbort] = useState<AbortController | null>(null);
...@@ -35,6 +37,7 @@ export default function News(props: Props) { ...@@ -35,6 +37,7 @@ export default function News(props: Props) {
if (!abort) { if (!abort) {
return; return;
} }
setLoading(true);
api api
.listNewsPage( .listNewsPage(
{ {
...@@ -48,33 +51,63 @@ export default function News(props: Props) { ...@@ -48,33 +51,63 @@ export default function News(props: Props) {
.then((res) => { .then((res) => {
setList(res.result?.list || []); setList(res.result?.list || []);
setCount(res.result?.totalCount || 0); setCount(res.result?.totalCount || 0);
setLoading(false);
}); });
}, [abort]); }, [abort]);
const onPageChange = (page: number, pageSize: number) => {
setPageParams({
...pageParams,
pageNo: page,
});
};
return ( return (
<Row justify="space-between"> <Spin spinning={loading} delay={500}>
<Col className={styles.new}> <Row justify="space-between" style={{ height: 606 }}>
{list.map((item) => { <Col className={styles.new}>
return ( {list.map((item) => {
<div return (
className={styles.item} <div className={styles.item} key={item.id}>
key={item.id} <Image
onClick={() => router.push("/projectInfo/newsArticle/" + item.id)} className={styles.logo}
> src={item.surfaceImg}
<Image alt=""
className={styles.logo} width={120}
src={item.surfaceImg} height={80}
alt="" ></Image>
width={120} <div className={styles.info}>
height={80} <div className={styles.title}>{item.newsTitle}</div>
></Image> <div className={styles.desc}>{item.newsContents}</div>
<div className={styles.info}> <div className={styles.date}>
<div className={styles.title}>{item.newsTitle}</div> {Moment().format("yyyy-MM-DD")} · {item.newsAuthor}
</div>
</div>
<Button
type="primary"
style={{ width: 120, height: 40, flexShrink: 0 }}
onClick={() =>
router.push("/projectInfo/newsArticle/" + item.id)
}
>
申请合作
</Button>
</div> </div>
</div> );
); })}
})} </Col>
</Col> </Row>
</Row> {list.length === 0 && <Empty></Empty>}
<Pagination
current={pageParams.pageNo}
defaultPageSize={pageParams.pageSize}
showSizeChanger={false}
showQuickJumper
total={count}
onChange={onPageChange}
hideOnSinglePage={true}
style={{ marginTop: 20 }}
/>
</Spin>
); );
} }
...@@ -29,6 +29,12 @@ export interface Item { ...@@ -29,6 +29,12 @@ export interface Item {
updateTime?: string; updateTime?: string;
} }
export interface SolveRequireParams {
requirementsInfoId: number, //需求id
userAccountId: number //用户id
}
export default { export default {
/** /**
* 需求发布列表 * 需求发布列表
...@@ -37,5 +43,13 @@ export default { ...@@ -37,5 +43,13 @@ export default {
*/ */
listPublishPage(params: ListPublishPageParams, options = {}): Promise<Response<ListPublishPageResp>> { listPublishPage(params: ListPublishPageParams, options = {}): Promise<Response<ListPublishPageResp>> {
return request('/release/requirements/listPublishPage', 'post', params, options); return request('/release/requirements/listPublishPage', 'post', params, options);
},
/**
* 需求已解决
* @param params
* @returns
*/
solveRequire(params: SolveRequireParams): Promise<Response<null>>{
return request('/release/requirements/solveRequire', 'get', params);
} }
} }
\ No newline at end of file
@import "~/styles/mixins.scss";
//项目需求 //项目需求
.requirements { .requirements {
.item { .item {
cursor: pointer;
padding: 15px 15px; padding: 15px 15px;
display: flex; display: flex;
border-bottom: 1px dashed RGBA(222, 222, 222, 1); border-bottom: 1px dashed RGBA(222, 222, 222, 1);
align-items: center;
.logo { .logo {
width: 50px; width: 50px;
...@@ -13,10 +15,12 @@ ...@@ -13,10 +15,12 @@
margin-right: 24px; margin-right: 24px;
background: url('./assets/resolved.png') no-repeat; background: url('./assets/resolved.png') no-repeat;
background-size: 100% 100%; background-size: 100% 100%;
flex-shrink: 0;
} }
.info { .info {
padding: 3px 0 2px; padding: 3px 0 2px;
flex: auto;
.title { .title {
white-space: nowrap; /* 防止换行 */ white-space: nowrap; /* 防止换行 */
...@@ -27,6 +31,7 @@ ...@@ -27,6 +31,7 @@
font-weight: bold; font-weight: bold;
margin-bottom: 6px; margin-bottom: 6px;
color: RGBA(0, 0, 0, 0.4); color: RGBA(0, 0, 0, 0.4);
@include ellipsis(1);
} }
.desc { .desc {
...@@ -37,6 +42,7 @@ ...@@ -37,6 +42,7 @@
font-family: MicrosoftYaHei; font-family: MicrosoftYaHei;
color: RGBA(135, 135, 135, 0.4); color: RGBA(135, 135, 135, 0.4);
line-height: 1; line-height: 1;
@include ellipsis(1);
} }
} }
......
import { Empty, Pagination, Spin } from "antd"; import { Button, Empty, Pagination, Popconfirm, Spin } from "antd";
import router from "next/router";
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import api, { Item } from "./api"; import api, { Item } from "./api";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
...@@ -21,12 +22,18 @@ export default function Requirements(props: Props) { ...@@ -21,12 +22,18 @@ export default function Requirements(props: Props) {
}); });
const [count, setCount] = useState(0); const [count, setCount] = useState(0);
const [abort, setAbort] = useState<AbortController | null>(null); const [abort, setAbort] = useState<AbortController | null>(null);
const [userId, setUserId] = useState(-1);
const [reload, setReload] = useState(false);
useEffect(() => {
setUserId(Number(window.localStorage.getItem("userId") || -1));
}, []);
useEffect(() => { useEffect(() => {
//中断前一次请求 //中断前一次请求
abort?.abort(); abort?.abort();
setAbort(new AbortController()); setAbort(new AbortController());
}, [pageParams, props.params]); }, [pageParams, props.params, reload]);
useEffect(() => { useEffect(() => {
if (!abort) { if (!abort) {
...@@ -57,6 +64,24 @@ export default function Requirements(props: Props) { ...@@ -57,6 +64,24 @@ export default function Requirements(props: Props) {
}); });
}; };
/**
* 确认解决
* @param e
*/
const confirmSolved = (
item: Item
) => {
api.solveRequire({
requirementsInfoId: item.id,
userAccountId: userId
}).then(res => {
if(res.code === '200'){
window.messageApi.success('提交完成');
setReload(!reload);
}
})
};
return ( return (
<Spin spinning={loading} delay={500}> <Spin spinning={loading} delay={500}>
<div className={styles.requirements} style={{ height: 635 }}> <div className={styles.requirements} style={{ height: 635 }}>
...@@ -75,6 +100,24 @@ export default function Requirements(props: Props) { ...@@ -75,6 +100,24 @@ export default function Requirements(props: Props) {
具体需求:{item.requireDescription} 具体需求:{item.requireDescription}
</div> </div>
</div> </div>
{item.userAccountId === userId && (
<Popconfirm
title="提示"
description="确认该需求已经解决了吗?"
onConfirm={() => confirmSolved(item)}
okText="是"
cancelText="否"
disabled={!!item.solved}
>
<Button
type="primary"
style={{ width: 120, height: 40, flexShrink: 0 }}
disabled={!!item.solved}
>
已解决
</Button>
</Popconfirm>
)}
</div> </div>
); );
})} })}
......
...@@ -7,6 +7,7 @@ import Layout from "~/components/layout"; ...@@ -7,6 +7,7 @@ import Layout from "~/components/layout";
import newsApi, { Item } from "../components/news/api"; import newsApi, { Item } from "../components/news/api";
import api, { DetailsResp } from "./api"; import api, { DetailsResp } from "./api";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import Moment from "moment";
export default function CaseArticle() { export default function CaseArticle() {
const router = useRouter(); const router = useRouter();
...@@ -32,13 +33,36 @@ export default function CaseArticle() { ...@@ -32,13 +33,36 @@ export default function CaseArticle() {
newsApi newsApi
.listNewsPage({ .listNewsPage({
pageNo: 1, pageNo: 1,
pageSize: 5, pageSize: 10,
}) })
.then((res) => { .then((res) => {
setNewList(res.result?.list || []); setNewList(res.result?.list || []);
}); });
}, []); }, []);
const fontColor = (i: number) => {
switch (i) {
case 0:
return {
color: "#ff2c46",
};
case 1:
return {
color: "#FF6602",
};
case 2:
return {
color: "#FAA910",
};
default:
return {
color: "#9295A3",
};
}
};
return ( return (
<Layout layoutStyle={{ backgroundColor: "#fff" }}> <Layout layoutStyle={{ backgroundColor: "#fff" }}>
<div style={{ paddingTop: 29 }}> <div style={{ paddingTop: 29 }}>
...@@ -49,10 +73,12 @@ export default function CaseArticle() { ...@@ -49,10 +73,12 @@ export default function CaseArticle() {
className={styles.font2} className={styles.font2}
style={{ marginTop: 18, marginBottom: 41 }} style={{ marginTop: 18, marginBottom: 41 }}
> >
{data?.createTime} · {data?.newsAuthor} {data?.createTime &&
Moment(data?.createTime).format("YYYY-MM-DD")}{" "}
· {data?.newsAuthor}
</div> </div>
<div <div
style={{lineHeight: 1.5}} style={{ lineHeight: 1.5 }}
dangerouslySetInnerHTML={{ __html: data?.newsContents || "" }} dangerouslySetInnerHTML={{ __html: data?.newsContents || "" }}
></div> ></div>
</Col> </Col>
...@@ -61,14 +87,14 @@ export default function CaseArticle() { ...@@ -61,14 +87,14 @@ export default function CaseArticle() {
<Row <Row
className={styles.font4} className={styles.font4}
align="middle" align="middle"
style={{ paddingTop: 24, paddingLeft: 24 }} style={{ paddingTop: 16, paddingLeft: 20 }}
> >
行业新闻 行业新闻
<RightOutlined style={{ fontSize: 16, marginLeft: 15 }} /> <RightOutlined style={{ fontSize: 16, marginLeft: 9 }} />
</Row> </Row>
<Row gutter={10} style={{ marginTop: 18 }}> <Row gutter={10} style={{ marginTop: 8 }}>
<Col span={24}> <Col span={24}>
{newsList.map((item) => { {newsList.map((item, i) => {
return ( return (
<Row <Row
key={item.id} key={item.id}
...@@ -80,20 +106,20 @@ export default function CaseArticle() { ...@@ -80,20 +106,20 @@ export default function CaseArticle() {
}} }}
> >
<Col <Col
className={`${styles.font3} ${styles.ellipse2}`} className={`${styles.font3}`}
style={{ width: 217, padding: "13px 0 17px 20px" }} style={{ margin: "6px 23px 6px 19px", width: "100%" }}
> >
{item.newsTitle} <Row wrap={false}>
</Col> <Col
<Col style={{ paddingRight: 16 }}> span={2}
<Image style={{ textAlign: "center", ...fontColor(i) }}
src={item.surfaceImg} >
width={90} {i + 1}
height={60} </Col>
preview={false} <Col span={22} className={styles.ellipse1}>
fallback={errImg} {item.newsTitle}
style={{ borderRadius: 6 }} </Col>
></Image> </Row>
</Col> </Col>
</Row> </Row>
); );
......
...@@ -16,27 +16,24 @@ ...@@ -16,27 +16,24 @@
} }
.font3 { .font3 {
font-size: 16px; font-size: 14px;
font-family: MicrosoftYaHei; font-family: MicrosoftYaHei;
color: #323232; color: #323232;
line-height: 21px; line-height: 19px;
} }
.font4 { .font4 {
font-size: 20px; font-size: 16px;
font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI; font-family: MicrosoftYaHeiUI-Bold, MicrosoftYaHeiUI;
font-weight: bold; font-weight: bold;
color: #000000; color: #000000;
line-height: 25px; line-height: 20px;
} }
.newsBox { .newsBox {
width: 384px; width: 384px;
height: 491px;
background-image: url("./assets/bk.png");
background-size: 100% 100%;
} }
.ellipse2 { .ellipse1 {
@include ellipsis(2); @include ellipsis(1);
} }
\ No newline at end of file
...@@ -2,7 +2,9 @@ import { MessageInstance } from 'antd/es/message/interface'; ...@@ -2,7 +2,9 @@ import { MessageInstance } from 'antd/es/message/interface';
declare global { declare global {
interface Window { interface Window {
messageApi: MessageInstance; messageApi: MessageInstance; //全局消息提示api
WxLogin: any; WxLogin: any; //微信登录对象
setUserId: (number) => void; //获取用户信息需要的userId
_AMapSecurityConfig: { securityJsCode: string }; //高德地图api密钥配置
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论