Commit 373748e2 by 肖康

Merge branch 'feat/2024/10/29/CustomerCenter' of…

Merge branch 'feat/2024/10/29/CustomerCenter' of http://git.ichunt.net/xiaokang/Europa-web into feat/2024/10/29/CustomerCenter

# Conflicts:
#	hooks/useAuth.tsx
#	pages/login/index.tsx
parents 892e46dd 6a744f3f
Showing with 2028 additions and 288 deletions
import axios from 'axios';
import {API_URL} from '@/configReact'
// 创建 Axios 实例
const api = axios.create({
// baseURL: API_URL, // 替换为你的 API 基础 URL
baseURL: 'http://erpweb.liexindev.net', // 替换为你的 API 基础 URL
timeout: 10000, // 请求超时时间
});
// 请求拦截器
api.interceptors.request.use(
(config) => {
// 可以添加请求头或其他配置
const token = localStorage.getItem('token'); // 示例:从 localStorage 获取 token
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器
api.interceptors.response.use(
(response) => {
// 处理响应数据
return response.data;
},
(error) => {
// 处理错误
const { response } = error;
if (response) {
// 根据不同的状态码处理错误
switch (response.status) {
case 401:
console.error('未授权,请登录');
break;
case 404:
console.error('请求的资源未找到');
break;
default:
console.error('请求失败:', response.data.message);
}
} else {
console.error('请求超时或网络错误');
}
return Promise.reject(error);
}
);
export default api;
...@@ -24,7 +24,7 @@ const NavBig = (props: ResponseTypeCateList) => { ...@@ -24,7 +24,7 @@ const NavBig = (props: ResponseTypeCateList) => {
url: '/contact.html', url: '/contact.html',
name: '联系我们' name: '联系我们'
}, { }, {
url: '/memberCenter.html', url: '/memberCenter',
name: '会员中心' name: '会员中心'
}] }]
......
...@@ -16,10 +16,14 @@ const TopH=()=>{ ...@@ -16,10 +16,14 @@ const TopH=()=>{
<div className={`${styles.con} row bothSide verCenter`}> <div className={`${styles.con} row bothSide verCenter`}>
<div ></div> <div ></div>
<div className='row verCenter'> <div className='row verCenter'>
<Link href='/login.html?type=1'>立即登录</Link> {(!user?.web_user_id) && (
<span className={styles.line}></span> <>
<Link href='/login.html?type=2'>立即注册</Link> <Link href='/login?type=1'>立即登录</Link>
<span className={styles.line}></span> <span className={styles.line}></span>
<Link href='/login?type=2'>立即注册</Link>
<span className={styles.line}></span>
</>
)}
<Link href='/about.html'>关于我们</Link> <Link href='/about.html'>关于我们</Link>
<span className={styles.line}></span> <span className={styles.line}></span>
<Link href='/contact.html'>联系我们</Link> <Link href='/contact.html'>联系我们</Link>
......
...@@ -5,33 +5,11 @@ import { useSeoTitle } from "@/hooks/useSeoTitle" ...@@ -5,33 +5,11 @@ import { useSeoTitle } from "@/hooks/useSeoTitle"
import type {ResponseTypeCateList} from '@/components/Header/components/NavBig/types' import type {ResponseTypeCateList} from '@/components/Header/components/NavBig/types'
import useRequest from "@/hooks/useRequest"
import { useAuth } from "@/hooks/useAuth"
const handleUserInfo = () => {
if (!localStorage.getItem("userKey")) return
const {user, login} = useAuth()
const { request: brandListRequest } = useRequest<any>({ manual: false })
const userData = JSON.parse(localStorage.getItem("userKey") || '')
console.log(userData)
brandListRequest({
url: '/api/userInfo/userInfo',
method: 'get',
headers: userData
}).then(res => {
if (res?.code === 0) {
login(res.data)
console.log(user)
}
})
}
const Header = (props:ResponseTypeCateList) => { const Header = (props:ResponseTypeCateList) => {
const seoTitle=useSeoTitle() const seoTitle=useSeoTitle()
setTimeout(() => {
handleUserInfo()
})
return ( return (
<> <>
......
import { useEffect, useState } from 'react'; import { useState, useEffect } from 'react';
import { useCookies } from "./useCookies"
import useRequest from "./useRequest"
export const useAuth = () => { export const useAuth = () => {
const [user, setUser] = useState<any>(1); const [user, setUser] = useState<any>(null);
useEffect(()=>{ const {getCookie}=useCookies()
localStorage.setItem('aaa',"11") const { request: userRequest } = useRequest<any>({ manual: true,loading:false })
},[])
const login = (userData: any) => { useEffect(() => {
//localStorage.setItem('userKey', JSON.stringify({token: userData.token, webUserId: userData.user.web_user_id}));
setUser(userData); const token = getCookie('token')
}; const userId = getCookie('userId')
if (userId && !user) {
// const logout = () => { userRequest({
// localStorage.removeItem('userKey'); url: '/api/userInfo/userInfo',
// setUser(null); method: 'get',
// }; headers: {
'webUserId': userId,
// const getUserInfo = () => { Authorization: token
},
}).then(res => {
if (res?.code === 0) setUser(res.data)
})
} else {
}
}, [])
return { user, setUser };
};
// }
return { user };
};
...@@ -3,6 +3,8 @@ import { useCallback, useEffect, useRef, useState } from 'react'; ...@@ -3,6 +3,8 @@ import { useCallback, useEffect, useRef, useState } from 'react';
import axios, { AxiosRequestConfig } from "axios" import axios, { AxiosRequestConfig } from "axios"
import { useToast } from './useToast'; import { useToast } from './useToast';
import {API_URL} from '@/configReact' import {API_URL} from '@/configReact'
import { useCookies } from "@/hooks/useCookies"
import { useRouter } from 'next/router';
// 默认请求参数 // 默认请求参数
const defaultRequestConfig = { const defaultRequestConfig = {
...@@ -16,7 +18,10 @@ function useRequest<T>(options: AxiosRequestConfig & { manual?: boolean,loading? ...@@ -16,7 +18,10 @@ function useRequest<T>(options: AxiosRequestConfig & { manual?: boolean,loading?
const loadingRef = useRef(loading); const loadingRef = useRef(loading);
const messageRef = useRef(message); const messageRef = useRef(message);
const {getCookie} = useCookies()
const router = useRouter()
const request = useCallback((requestOption?: AxiosRequestConfig) => { const request = useCallback((requestOption?: AxiosRequestConfig) => {
setData(null) setData(null)
setLoaded(false) setLoaded(false)
...@@ -24,14 +29,21 @@ function useRequest<T>(options: AxiosRequestConfig & { manual?: boolean,loading? ...@@ -24,14 +29,21 @@ function useRequest<T>(options: AxiosRequestConfig & { manual?: boolean,loading?
loadingRef.current() loadingRef.current()
} }
const headers = requestOption?.headers const headers = requestOption?.headers
const userId = getCookie("userId")
const token = getCookie("token")
return axios.request<T>({ return axios.request<T>({
baseURL:API_URL, baseURL:API_URL,
...requestOption, ...requestOption,
headers: localStorage.getItem("token") ? { headers: {
Authorization: localStorage.getItem("token"), Authorization: token,
webUserId: userId,
...headers ...headers
} : headers }
}).then((response) => { }).then((response) => {
if (response.data['code' as keyof typeof data] && response.data['code' as keyof typeof data] === 101) {
router.push('login?type=1')
return
}
setData(response.data) setData(response.data)
return response.data return response.data
}).catch((e: any) => { }).catch((e: any) => {
......
...@@ -15,6 +15,7 @@ export const useSeoTitle = () => { ...@@ -15,6 +15,7 @@ export const useSeoTitle = () => {
'/about.html': { title: '关于我们' }, '/about.html': { title: '关于我们' },
'/contact.html': { title: '联系我们' }, '/contact.html': { title: '联系我们' },
'/notice.html': { title: '公告' }, '/notice.html': { title: '公告' },
'/memberCenter': { title: '会员中心' },
} }
if (Object.keys(titleKeywords).indexOf(router.pathname) !== -1) { if (Object.keys(titleKeywords).indexOf(router.pathname) !== -1) {
titlestr=titleKeywords[router.pathname]['title'] titlestr=titleKeywords[router.pathname]['title']
......
{ {
"name": "europaWeb", "name": "europa-web",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
...@@ -10,11 +10,15 @@ ...@@ -10,11 +10,15 @@
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"antd": "^5.21.6",
"axios": "^1.7.2", "axios": "^1.7.2",
"next": "^13.0.5", "next": "^13.0.5",
"react": "^18", "react": "^18",
"react-dom": "^18", "react-dom": "^18",
"react-paginate": "^8.2.0", "react-paginate": "^8.2.0",
"react-redux": "^9.1.2",
"react-router-dom": "^6.27.0",
"redux": "^5.0.1",
"sass": "^1.77.8", "sass": "^1.77.8",
"swiper": "^11.1.5" "swiper": "^11.1.5"
}, },
......
import React, { use, useState } from 'react';
import useRequest from "@/hooks/useRequest"
import style from './index.module.scss'
import Footer from "@/components/Footer";
import TopH from "@/components/Header/components/TopH"
import SearchH from "@/components/Header/components/SearchH"
import api from '@/axios/index'
import { useAuth } from '@/hooks/useAuth';
const Page = () => {
const [loginType, setLoginType] = useState('1');
const [count, setCount] = useState('testtesttes');
const [password, setPassword] = useState('');
const [area, setArea] = useState('0086');
const [code, setCode] = useState('');
const [showRule, setShowRule] = useState(false);
const handleCountChange = (e) => setCount(e.target.value);
const handlePasswordChange = (e) => setPassword(e.target.value);
const handleAreaChange = (e) => setArea(e.target.value);
const handleCodeChange = (e) => setCode(e.target.value);
const { request: loginRequest } = useRequest<any>({ manual: true })
const {user}=useAuth()
console.log(user)
//发送验证码
const handleCodeSend = async(e:any) => {
if(!count) return
const param = {
send_type: '1',
send_user: count,
send_fun: 'login'
}
loginRequest({ url: '/api/auth/login2313', method: 'post', data: param }).then((res) => {
})
// const response = await api.post('/auth/login', param)
// console.log(response)
}
// 登录
const handleSubmit = async (e) => {
e.preventDefault()
const param = {
contacts_tel: count,
type: loginType,
password: password,
code: code
}
const response = await api.post('/login', param)
console.log(response)
return false
};
const handleTypeChange = (type:string) => {
setLoginType(type)
}
const handleRuleShow = (type:string) => {
if (type === 'Privacy') {
// 隐私 Privacy
} else {
// 用户
}
// close
setShowRule(true)
}
return (
<>
<TopH />
<SearchH />
<div className={style.loginPage}>
<div className={style.loginBox}>
<div className={style.typeBox}>
<span
className={`${style.typeButton} ${style.cursorPointer} ${(loginType === "1") && style.typeButtonChoosen}`}
onClick={() => handleTypeChange('1')}>
账号登录
</span>
<span
className={`${style.typeButton} ${style.cursorPointer} ${(loginType === '2') && style.typeButtonChoosen}`}
onClick={() => handleTypeChange('2')}>
免密登录/注册
</span>
</div>
<form onSubmit={handleSubmit}>
{loginType === "1" ? (
<>
<div>
<input type="contacts_tel" value={count} className={style.loginInput} placeholder='请输入账号' onChange={handleCountChange} required />
</div>
<div>
<input type="password" value={password} className={style.loginInput} placeholder='请输入登录密码' onChange={handlePasswordChange} required />
</div>
<div className={style.forgetButton}>
<span className={style.cursorPointer}>忘记密码?</span>
</div>
<button type="submit" className={style.loginButton} >登录</button>
<div className={style.registerButton}>
<span className={style.cursorPointer} onClick={() => handleTypeChange('2')}>立即注册</span>
</div>
</>
) : (
<>
<div>
<select className={style.areaChoose} value={area} onChange={handleAreaChange} >
<option value="0086">0086(中国大陆)</option>
<option value="00852">00852(中国香港)</option>
<option value="00853">00853(中国澳门)</option>
<option value="00886">00886(中国台湾)</option>
</select>
<input type="contacts_tel" value={count} className={style.areaInput} placeholder='请输入账号' onChange={handleCountChange} required />
</div>
<div>
<input type="password" value={code} className={style.registerInput} placeholder='请输入短信验证码' onChange={handleCodeChange} required />
<button className={style.codeButton} onClick={handleCodeSend}>发送验证码</button>
</div>
<div className={style.registerTips}>未注册的手机号将自动完成注册</div>
<div className={style.registerRule}>
<input type='checkbox' name="ruleCheck" className={style.registerCheck} required></input>
<span className={style.ruleText}>我已阅读并同意</span>
<span className={style.ruleButton} onClick={() => handleRuleShow('user')}>《用户注册协议》</span>
<span className={style.ruleButton} onClick={() => handleRuleShow('Privacy')}>《隐私保护协议》</span>
</div>
<button type="submit" className={style.registerLoginButton}>登录</button>
</>
)}
</form>
</div>
{showRule && (
<>
<div className={style.ruleDialog}>
<div className={style.ruleContext}>
本协议是您与猎芯网(简称"本站",网址:http://www.ichunt.com, 含有“ichunt.com”域名及其子域名的网站等)服务提供者(以下简称为"猎芯网")之间就猎芯网服务等相关事宜所订立的契约,您在使用猎芯网服务前,请您务必仔细阅读、充分理解本用户协议和隐私政策,特别是免除或限制责任的条款、法律适用和争议解决条款。一旦您使用猎芯网服务即表示您已经充分阅读、同意并接受本用户协议和隐私政策中所含的所有条款和条件。阅读本协议的过程中,如果您不同意本协议或其中任何条款约定,您应立即停止注册程序。
第1条 本站服务条款的确认和接纳
1.1本站的各项在线服务的所有权和运作权归猎芯网所有。用户同意所有注册协议条款并完成注册程序,才能成为本站的正式用户。用户确认:本协议条款是处理双方权利义务的契约,始终有效,法律另有强制性规定或双方另有特别约定的,依其规定。
1.2用户点击同意本协议,即视为用户确认自己具有享受在线购买电子元器件及享受服务等相应的权利和行为能力,能够独立承担法律责任。若您不具备前述与您行为相适应的民事行为能力,则您及您的监护人应依照法律规定承担因此而导致的一切后果。
1.3本站保留在中华人民共和国大陆地区施行之法律允许的范围内独自决定拒绝服务、关闭用户账户、清除或编辑内容或取消订单的权利。
第2条 本站服务
2.1猎芯网通过互联网依法为用户提供电子元件产品信息等服务,用户在完全同意本协议及本站规定的情况下,方有权使用本站的相关服务。
第3条 用户信息
3.1用户应本着个人诚信向本站提供注册资料,用户同意其提供的注册资料真实、准确、完整、合法有效。如果用户提供的注册资料不合法、不真实、不准确、不详尽的,用户需承担因此引起的相应责任及后果,并且猎芯网保留终止用户使用猎芯网各项服务的权利。
3.2用户在本站进行浏览、委托交易等活动时,涉及用户及企业真实姓名/名称、通信地址、联系方式、营业执照等隐私信息的,本站将予以严格保密,除非得到用户授权或法律另有规定,本站不会向外界披露用户及企业隐私信息。
3.3用户注册成功后,将产生用户名和密码等账户信息,您可以根据本站规定修改您的密码。用户应谨慎合理的保存、使用其用户名和密码。用户若发现任何非法使用用户账号或存在安全漏洞的情况,请立即通知本站。
3.4用户若将在本站注册获得的账户因用户主动泄露或因用户遭受他人攻击、诈骗等行为导致的损失及后果,本站并不承担责任,用户应通过司法、行政等救济途径向侵权行为
</div>
<button className={style.ruleCloseButton} onClick={() => handleRuleShow('close')}>关闭本页</button>
</div>
</>
)}
</div>
<Footer />
</>
);
};
export default Page;
import React, { useEffect, useState, useRef } from 'react';
import useRequest from "@/hooks/useRequest"
import style from './index.module.scss'
import Footer from "@/components/Footer";
import TopH from "@/components/Header/components/TopH"
import SearchH from "@/components/Header/components/SearchH"
import Link from 'next/link'
import { useRouter } from 'next/router';
import { useToast } from '@/hooks/useToast';
import { useCookies } from '@/hooks/useCookies'
const Page = () => {
const router = useRouter()
const { query } = router
const {setCookie} = useCookies()
const [loginType, setLoginType] = useState(query.type);
const [count, setCount] = useState('');
const [password, setPassword] = useState('');
const [area, setArea] = useState('0086');
const [code, setCode] = useState('');
const [showRule, setShowRule] = useState(false);
const [showMsg, setShowMsg] = useState(false);
const [msg, setMsg] = useState('');
const {message}=useToast()
const messageRef = useRef(message);
const handleCountChange = (e:any) => setCount(e.target.value);
const handlePasswordChange = (e:any) => setPassword(e.target.value);
const handleAreaChange = (e:any) => setArea(e.target.value);
const handleCodeChange = (e:any) => setCode(e.target.value);
const handleTypeChange = (type:string) => {
setLoginType(type)
setCount('')
setShowMsg(false)
setPassword('')
setCode('')
}
const { request: loginRequest } = useRequest<any>({ manual: true })
useEffect(() => {
setLoginType(query.type)
}, [query])
const regex = /^1[3-9]\d{9}$/;
//发送验证码
const handleCodeSend = async(e:any) => {
if(!count) return false
e.preventDefault()
setShowMsg(false)
if (!regex.test(count)) {
setMsg('手机格式不正确,请重新输入!')
setShowMsg(true)
return false
}
const param = {
send_type: '1',
send_user: count,
send_fun: 'login'
}
loginRequest({
url: '/sendMsgCode',
method: 'post',
data: param,
}).then(res => {
messageRef.current(res.msg)
})
return false
}
// 登录
const handleSubmit = async (e:any) => {
e.preventDefault()
setShowMsg(false)
const param = {
contacts_tel: count,
type: loginType,
password: password,
code: code
}
const response = await loginRequest({
url: '/login',
method: 'post',
data: param,
})
if (response.code === 0) {
setCookie('token', response.data.token, 1)
setCookie('userId', response.data.user.web_user_id, 1)
messageRef.current(response.msg)
router.push('memberCenter')
}
else {
setMsg(response.msg)
setShowMsg(true)
}
return false
};
const handleRuleShow = (type:string) => {
let doClose = true
switch (type) {
case 'Privacy':
break;
case 'user':
break;
case 'close':
doClose = false
}
setShowRule(doClose)
}
return (
<>
<TopH />
<SearchH />
<div className={style.loginPage}>
<div className={style.loginBox}>
<div className={style.typeBox}>
<span
className={`${style.typeButton} ${style.cursorPointer} ${(loginType === "1") && style.typeButtonChoosen}`}
onClick={() => handleTypeChange('1')}>
账号登录
</span>
<span
className={`${style.typeButton} ${style.cursorPointer} ${(loginType === '2') && style.typeButtonChoosen}`}
onClick={() => handleTypeChange('2')}>
免密登录/注册
</span>
</div>
<form onSubmit={handleSubmit}>
{loginType === "1" ? (
<>
<div>
<input type="contacts_tel" value={count} className={style.loginInput} placeholder='请输入账号' onChange={handleCountChange} required />
</div>
<div>
<input type="password" value={password} className={style.loginInput} placeholder='请输入登录密码' onChange={handlePasswordChange} required />
</div>
{ showMsg && (
<>
<p className={style.showMsg}>{msg}</p>
</>
)}
<div className={style.forgetButton}>
<Link href="/passwordRecover">
<span className={style.cursorPointer}>忘记密码?</span>
</Link>
</div>
<button type="submit" className={style.loginButton} >登录</button>
<div className={style.registerButton}>
<span className={style.cursorPointer} onClick={() => handleTypeChange('2')}>立即注册</span>
</div>
</>
) : (
<>
<div>
<select className={style.areaChoose} value={area} onChange={() => handleAreaChange} >
<option value="0086">0086(中国大陆)</option>
<option value="00852">00852(中国香港)</option>
<option value="00853">00853(中国澳门)</option>
<option value="00886">00886(中国台湾)</option>
</select>
<input type="contacts_tel" value={count} className={style.areaInput} placeholder='请输入手机号' onChange={handleCountChange} required />
</div>
<div>
<input type="password" value={code} className={style.registerInput} placeholder='请输入短信验证码' onChange={handleCodeChange} required />
<button className={style.codeButton} onClick={handleCodeSend}>发送验证码</button>
</div>
<div className={style.registerTips}>未注册的手机号将自动完成注册</div>
{ showMsg && (
<>
<p className={style.showMsg}>{msg}</p>
</>
)}
<div className={style.registerRule}>
<input type='checkbox' name="ruleCheck" className={style.registerCheck} required></input>
<span className={style.ruleText}>我已阅读并同意</span>
<span className={style.ruleButton} onClick={() => handleRuleShow('user')}>《用户注册协议》</span>
<span className={style.ruleButton} onClick={() => handleRuleShow('Privacy')}>《隐私保护协议》</span>
</div>
<button type="submit" className={style.registerLoginButton}>登录</button>
</>
)}
</form>
</div>
{showRule && (
<>
<div className={style.ruleDialog}>
<div className={style.ruleContext}>
</div>
<button className={style.ruleCloseButton} onClick={() => handleRuleShow('close')}>关闭本页</button>
</div>
</>
)}
</div>
<Footer />
</>
);
};
export default Page;
const Page = () => {
return (
<>
<div>会员中心</div>
</>
)
}
export default Page
\ No newline at end of file
import styles from './index.module.scss'
import {useState, useEffect} from 'react'
import { Cascader , Modal, Form, Input, Pagination } from 'antd';
import { useAuth } from '@/hooks/useAuth'
import useRequest from "@/hooks/useRequest"
interface Area {
region_id?: string | number | null;
region_name: React.ReactNode;
children?: Area[];
isLeaf?: boolean;
region_type?: number
}
const AddressPage = () => {
const [open, setOpen] = useState(false)
const [invoiceList, setInvoiceList] = useState<any>([]);
const [formData, setFormData] = useState<any>({});
const [areaList, setAreaList] = useState<Area[]>([]);
const [current, setCurrent] = useState(1);
const [pageSize, setPageSize] = useState(5);
const [total, setTotal] = useState(0);
const [ form ] = Form.useForm()
const {user} = useAuth()
const { request: userRequest } = useRequest<any>({ manual: true })
useEffect(() => {
handleDataInit()
}, [user])
useEffect(() => {
handleAreaInit(0, 'parentRegionOption')
}, [])
const handleDataInit = async () => {
if(!user && !user?.web_user_id) return
const invoiceListResponse = await userRequest({
url: `/api/userInfo/userAddressList?limit=${pageSize}&page=${current}`,
method: 'get'
})
if (invoiceListResponse.code === 0) {
setInvoiceList(invoiceListResponse.data.list)
setTotal(invoiceListResponse.data.list.length)
}
}
const handleAreaInit = async (parent_id:0, type:string) => {
const areaListResponse = await userRequest({
url: `/api/userInfo/getSelectOption?parent_id=${parent_id}&type=${type}`,
method: 'get'
})
if (areaListResponse.code === 0) {
const areaDisPlayList = areaListResponse.data.map((areaItem:any) => {
areaItem.isLeaf = false
return areaItem
})
setAreaList(areaDisPlayList)
}
}
const handleInvoiceSave = async (isAdd: boolean) => {
if(isAdd) {
console.log(formData)
if (formData.contact_address.length !== 4) return
const [nation_id, province, city, district] = formData.contact_address
formData.nation_id = nation_id
formData.province = province
formData.city = city
formData.district = district
}
const saveInvoiceResponse = await userRequest({
url: '/api/userInfo/saveAddress',
method: 'post',
data: formData
})
if (saveInvoiceResponse.code === 0) {
if (isAdd) {
form.resetFields()
handleModal(false)
setFormData(null)
}
handleDataInit()
}
}
const manualValidate = () => {
form.validateFields().then((value:any) => {
if (formData && formData.web_user_address_id) {
value = Object.assign(formData, value)
}
setFormData(value)
handleInvoiceSave(true)
}).catch((e) => {});
};
const handleModal = (show:boolean) => {
form.resetFields()
setFormData(null)
setOpen(show)
};
const handleInvoiceEdit = (data:any, key:string) => {
if (key) {
data[key] = 1
setFormData(data)
handleInvoiceSave(false)
} else {
setOpen(true)
const disData = JSON.parse(JSON.stringify(data))
disData['contact_address'] = [disData.nation_id, disData.province, disData.city, disData.district]
setFormData(disData)
form.setFieldsValue(disData)
}
}
const validateMessages = {
required: '${label}不能为空!'
};
const loadData = async (selectedOptions: Area[]) => {
const targetOption = selectedOptions[selectedOptions.length - 1];
const areaListResponse = await userRequest({
url: `/api/userInfo/getSelectOption?parent_id=${targetOption.region_id}&type=parentRegionOption`,
method: 'get'
})
if (areaListResponse.code === 0) {
if (!areaListResponse.data || !areaListResponse.data.length) targetOption.isLeaf = true
else {
const areaDisPlayList = areaListResponse.data.map((areaItem:any) => {
areaItem.isLeaf = false
return areaItem
})
targetOption.children = areaDisPlayList
}
setAreaList([...areaList])
}
}
const handlePageChange = (page:number, pageSize:number) => {
setCurrent(page)
setPageSize(pageSize)
}
return (
<>
<div className={styles.invoiceBox}>
<div className={styles.invoiceTitle}>我的地址</div>
<button className={styles.invoiceButton} onClick={() => handleModal(true)}>新增地址</button>
{
invoiceList.map((invoiceItem:any) => {
return (
<div className={styles.invoiceItem} key={invoiceItem.web_user_address_id}>
<div className={styles.invoiceStatus}>
<div className={styles.invoiceCheck}>
{ !invoiceItem.is_default && (<><input type="checkbox" onClick={() => handleInvoiceEdit(invoiceItem, 'is_default')}/>设为默认</>)}
</div>
<div className={styles.rightPart}>
<button className={styles.editButton} onClick={() => handleInvoiceEdit(invoiceItem, '')}>编辑地址</button>
{ !invoiceItem.is_del && (
<button className={styles.invalid} onClick={() => handleInvoiceEdit(invoiceItem, 'is_del')}>删除</button>
)}
</div>
</div>
<div className={styles.invoiceDetail}>
<div className={styles.lineItem}>
<span>联系人:</span>
<span>{invoiceItem.consignee}</span>
</div>
<div className={styles.lineItem}>
<span>联系地址:</span>
<span>
{invoiceItem.nation_id_cn}{invoiceItem.province_cn}{invoiceItem.city_cn}{invoiceItem.district_cn}{invoiceItem.detail_address}
</span>
</div>
<div className={styles.lineItem}>
<span>联系电话:</span>
<span>{invoiceItem.telphone}</span>
</div>
<div className={styles.lineItem}>
<span>电子邮箱:</span>
<span>{invoiceItem.email}</span>
</div>
</div>
</div>
)
})
}
<div className={styles.pagination}>
<Pagination
showQuickJumper
showSizeChanger
current={current}
pageSize={pageSize}
pageSizeOptions={[5, 10]}
total={total}
showTotal={(total) => `共 ${total} 条记录`}
onChange={handlePageChange}/>
</div>
</div>
<Modal
title="新增地址"
open={open}
destroyOnClose
footer={null}
closeIcon={null}
centered
onCancel={() => handleModal(false)}
>
<Form
className={styles.modalContent}
validateMessages={validateMessages}
form={form}
labelCol={{ span: 6 }}
wrapperCol={{ span: 16 }}>
<Form.Item
className={styles.lineItem}
key="consignee"
label="联系人"
name="consignee"
rules={[{ required: true }]}>
<Input/>
</Form.Item>
<Form.Item
className={styles.lineItem}
key="contact_address"
label="联系地址"
name="contact_address"
rules={[{ required: true }]}>
<Cascader
fieldNames={{ label: 'region_name', value: 'region_id', children: 'children' }}
options={areaList}
loadData={loadData}
changeOnSelect />
</Form.Item>
<Form.Item
className={styles.lineItem}
key="detail_address"
label="详细地址"
name="detail_address"
rules={[{ required: true }]}>
<Input/>
</Form.Item>
<Form.Item
className={styles.lineItem}
key="telphone"
label="联系电话"
name="telphone"
rules={[{ required: true }]}>
<Input/>
</Form.Item>
<Form.Item
className={styles.lineItem}
key="email"
label="联系邮箱"
name="email"
rules={[{ required: true }]}>
<Input/>
</Form.Item>
</Form>
<div className={styles.modalFooter}>
<button className={styles.saveButton} onClick={() => manualValidate()}>保存</button>
<button className={styles.cancelButton} onClick={() => handleModal(false)}>取消</button>
</div>
</Modal>
</>
)
}
export default AddressPage
\ No newline at end of file
.invoiceBox {
width: 1022px;
margin-left: 12px;
box-sizing: border-box;
background: #fff;
padding: 20px 30px 100px;
position: relative;
.invoiceTitle {
font-size: 16px;
height: 32px;
color: #1A1A1A;
line-height: 21px;
width: 100%;
border-bottom: 1px solid #F4F4F4;
}
.invoiceButton {
left: 120px;
top: 18px;
position: absolute;
width: 80px;
height: 26px;
background: #FFFFFF;
border-radius: 1px;
color: #FF9A00;
border: 1px solid #FF9A00;
cursor: pointer;
&:hover {
color: #fff;
background-color: #FF9A00;
}
}
.invoiceItem {
height: 240px;
padding-top: 20px;
box-sizing: border-box;
border-bottom: 1px solid #F4F4F4;
.invoiceStatus {
width: 100%;
height: 20px;
display: flex;
line-height: 20px;
text-align: center;
justify-content: space-between;
.invoiceCheck {
display: flex;
align-items: center;
input {
cursor: pointer;
margin-right: 8px;
}
}
.rightPart {
display: flex;
align-items: center;
.editButton {
width: 75px;
height: 20px;
font-size: 12px;
color: #595959;
background: #FFFFFF;
border-radius: 1px;
border: 1px solid #B0B0B0;
cursor: pointer;
}
.invalid {
width: 60px;
height: 20px;
font-size: 12px;
color: #D0121B;
background: #FDF5F5;
border-radius: 1px;
margin-left: 25px;
border: 1px solid #D0121B;
cursor: pointer;
&:hover {
color: #fff;
background-color: #D0121B;
}
}
}
}
.invoiceDetail {
margin-top: 20px;
font-size: 14px;
width: 100%;
.lineItem {
width: calc(100% - 50px);
margin-bottom: 15px;
margin-left: 50px;
}
}
}
.pagination {
position: absolute;
right: 30px;
bottom: 30px;
}
}
.modalContent {
min-height: 450px;
padding: 45px 0;
box-sizing: border-box;
}
.modalFooter {
width: 100%;
display: flex;
margin-top: 20px;
justify-content: space-around;
.saveButton {
width: 195px;
height: 45px;
color: #fff;
background: #FF9A00;
border-radius: 1px;
cursor: pointer;
}
.cancelButton {
width: 195px;
height: 45px;
color: #FF9A00;
background: #fff;
border-radius: 1px;
cursor: pointer;
border: 1px solid #FF9A00;
}
}
\ No newline at end of file
import styles from './index.module.scss'
import {useState, useEffect} from 'react'
import { useAuth } from '@/hooks/useAuth'
const BasicPage = () => {
const {user} = useAuth()
return (
<div className={styles.basicBox}>
<div className={`${styles.basicModule} ${styles.basicHeight}`}>
<div className={styles.basicTitle}>基础信息</div>
<div className={styles.basicContent}>
<div className={styles.basicItem}>
<span>账号ID:</span>
<span>{user?.web_user_account}</span>
<button className={styles.basicButton}>修改账号</button>
</div>
<div className={styles.basicItem}>
<span>绑定手机:</span>
<span>{user?.contacts_tel}</span>
<button className={styles.basicButton}>更换手机</button>
</div>
<div className={styles.basicItem}>
<span>绑定邮箱:</span>
<span>{user?.contacts_email}</span>
<button className={styles.basicButton}>更换邮箱</button>
</div>
<div className={styles.basicItem}>
<span>账号密码:</span>
<button className={styles.basicButton}>修改密码</button>
</div>
<div className={styles.basicItem}>
<span>联系人:</span>
<span>{user?.company_address}</span>
<button className={styles.basicButton}>修改基础信息</button>
</div>
<div className={styles.basicItem}>
<span>联系人岗位:</span>
<span>{user?.company_address}</span>
</div>
<div className={styles.basicItem}>
<span>专属客服:</span>
<span>{user?.company_address}</span>
</div>
<div className={styles.basicItem}>
<span>绑定微信:</span>
<span>{user?.wechat_union_id}</span>
{/* <button className={styles.basicButton}>绑定微信</button> */}
</div>
<div className={styles.basicItem}>
<span>绑定QQ:</span>
<span>{user?.contacts_qq}</span>
{/* <button className={styles.basicButton}>绑定QQ</button> */}
</div>
</div>
</div>
<div className={styles.basicModule}>
<div className={styles.basicTitle}>默认发票</div>
<div style={{padding: '20px 30px 0'}}>
<span>发票种类:</span>
<span>{user?.company_name}</span>
</div>
<div className={styles.basicContent} style={{height: 'calc(100% - 40px)'}}>
<div className={styles.basicItem}>
<span>发票抬头:</span>
<span>{user?.contacts_tel}</span>
</div>
<div className={styles.basicItem}>
<span>开户行:</span>
<span>{user?.contacts_email}</span>
</div>
<div className={styles.basicItem}>
<span>公司电话:</span>
<span>{user?.company_address}</span>
</div>
<div className={styles.basicItem}>
<span>统一税务号:</span>
<span>{user?.company_address}</span>
</div>
<div className={styles.basicItem}>
<span>银行账号:</span>
<span>{user?.company_address}</span>
</div>
<div className={styles.basicItem}>
<span>注册地址:</span>
<span>{user?.company_address}</span>
</div>
</div>
</div>
<div className={styles.basicModule}>
<div className={styles.basicTitle}>默认地址</div>
<div className={styles.basicContent}>
<div className={styles.basicItem}>
<span>联系人:</span>
<span>{user?.company_name}</span>
</div>
<div className={styles.basicItem}>
<span>联系地址:</span>
<span>{user?.contacts_tel}</span>
</div>
<div className={styles.basicItem}>
<span>联系电话:</span>
<span>{user?.contacts_email}</span>
</div>
<div className={styles.basicItem}>
<span>联系邮箱:</span>
<span>{user?.company_address}</span>
</div>
</div>
</div>
</div>
)
}
export default BasicPage
\ No newline at end of file
.basicBox {
width: 1022px;
margin-left: 12px;
box-sizing: border-box;
.basicModule {
width: 100%;
height: 265px;
background-color: #fff;
box-sizing: border-box;
margin-bottom: 10px;
padding: 20px 30px;
.basicTitle {
height: 32px;
font-size: 16px;
line-height: 20px;
border-bottom: 1px solid #F4F4F4;
}
.basicContent {
padding: 20px 30px;
box-sizing: border-box;
height: 100%;
display: flex;
flex-direction: column;
flex-wrap: wrap;
.basicItem {
width: calc(50% - 5px);
display: inline-block;
height: 40px;
.basicButton {
width: 75px;
font-size: 12px;
height: 20px;
color: #595959;
background: #FFFFFF;
border-radius: 1px;
border: 1px solid #B0B0B0;
margin-left: 10px;
cursor: pointer;
&:hover {
color: #000;
}
}
}
}
}
.basicHeight {
height: 390px;
}
}
\ No newline at end of file
import styles from './index.module.scss'
import {useState, useEffect} from 'react'
import { Badge, Table } from "antd";
import type { TableColumnsType } from 'antd';
import { useAuth } from '@/hooks/useAuth'
import useRequest from "@/hooks/useRequest"
import { DataType } from "@/types/orderTypes"
const CenterHome = () => {
const {user} = useAuth()
const { request: userRequest } = useRequest<any>({ manual: true })
const [statusSummaryMap, setStatusSummaryMap] = useState<any>({'1': {count: 0}, '2': {count: 0}, '3': {count: 0}, '4': {count: 0}})
const [goodsList, setGoodsList] = useState<any>([])
const columns: TableColumnsType<DataType> = [
{ title: '序号', dataIndex: 'name', key: 'name', render:(text, record, index) => { return <span>{index + 1}</span>} },
{ title: '型号/品牌', dataIndex: 'age', key: 'age' },
{ title: '数量', dataIndex: 'goods_number', key: 'goods_number' },
{ title: '单价', dataIndex: 'goods_price', key: 'goods_price' },
{ title: '小计', dataIndex: 'goods_amount', key: 'goods_amount' },
{ title: '明细备注', dataIndex: 'web_order_remark', key: 'web_order_remark' }
]
useEffect(() => {
handleDataInit()
}, [user])
const handleDataInit = async () => {
if(!user && !user?.web_user_id) return
const tatusSummaryResponse = await userRequest({
url: '/api/order/webOrderStatusSummary',
method: 'post'
})
if (tatusSummaryResponse.code === 0) setStatusSummaryMap(tatusSummaryResponse.data)
const webOrderListResponse = await userRequest({
url: '/api/order/webOrderList',
method: 'post',
headers: {
'webUserId': user?.web_user_id
},
})
if (webOrderListResponse.code === 0) setGoodsList(webOrderListResponse.data.list)
}
return (
<>
<div className={styles.centerBox}>
<div className={styles.basicBox}>
<div className={styles.baseTitle}>基本信息</div>
<div className={styles.contentBox}>
<img className={styles.headImg} src="/images/logo.png"></img>
<div className={styles.detailLines}>
<div className={styles.lineItem}>
<span>默认发票公司:</span>
<span>{user?.company_name}</span>
</div>
<div className={styles.lineItem}>
<span>绑定手机:</span>
<span>{user?.contacts_tel}</span>
</div>
<div className={styles.lineItem}>
<span>税务登记号:</span>
<span>{user?.tax_no}</span>
</div>
<div className={styles.lineItem}>
<span>绑定邮箱:</span>
<span>{user?.contacts_email}</span>
</div>
<div className={styles.lineItem}>
<span>公司注册地址:</span>
<span>{user?.company_address}</span>
</div>
</div>
<div className={styles.myService}>
<img></img>
<span>我的客服</span>
</div>
<button className={styles.editButton}>修改资料</button>
</div>
</div>
<div className={styles.bottomBox}>
<div className={styles.orderBox}>
<div className={styles.baseTitle}>我的订单</div>
<div className={styles.moreButton}>
查看全部
<span style={{marginLeft: '5px'}}>{'>'}</span>
</div>
<div className={styles.statusBox}>
<div className={`${styles.statusItem} ${styles.pendPreview}`}>
<img src="/images/pendPreview.png"></img>
<div>
<Badge count={statusSummaryMap['1']['count']} offset={[20, 8]} size="small">
<span>待审核</span>
</Badge>
</div>
</div>
<div className={`${styles.statusItem} ${styles.pendPay}`}>
<img src="/images/pendPay.png"></img>
<div>
<Badge count={statusSummaryMap['2']['count']} offset={[20, 8]} size="small">
<span>待付款</span>
</Badge>
</div>
</div>
<div className={`${styles.statusItem} ${styles.pendDiliver}`}>
<img src="/images/pendDiliver.png"></img>
<div>
<Badge count={statusSummaryMap['3']['count']} offset={[20, 8]} size="small">
<span>待发货</span>
</Badge>
</div>
</div>
<div className={`${styles.statusItem} ${styles.completed}`}>
<img src="/images/completed.png"></img>
<div>
<Badge count={statusSummaryMap['4']['count']} offset={[20, 8]} size="small">
<span>已完成</span>
</Badge>
</div>
</div>
</div>
{
goodsList.map((goodItem:any) => {
return (
<div key={goodItem.order_id}>
<div className={styles.orderTitle}>
订单号:{goodItem.order_sn}
</div>
<Table<DataType>
size="small"
bordered={true}
pagination={false}
columns={columns}
rowKey={(record) => record?.rec_id}
expandable={{
expandIcon: (r) => null,
defaultExpandAllRows: true,
expandedRowRender: (record) => {
return (
<p style={{ margin: 0 }}>最新进度:{record.latest_log_time} 状态:{record.latest_log_status_text} {record.latest_log}</p>
)
},
rowExpandable: (record) => record.name !== 'Not Expandable',
}}
dataSource={goodItem.order_items}
/>
</div>
)
})
}
</div>
<div>
<div className={styles.infoBox}>
<div className={styles.baseTitle}>我的信息</div>
<div className={styles.moreButton}>
查看全部
<span style={{marginLeft: '5px'}}>{'>'}</span>
</div>
<div className={styles.messageTable}>
{/* <Table<DataType>
showHeader={false}
bordered={true}
pagination={false}
columns={columns}
dataSource={data}
/> */}
</div>
</div>
{/* <div className={styles.basicBox}><div className={styles.baseTitle}>我的收藏</div></div>
<div className={styles.basicBox}><div className={styles.baseTitle}>我的优惠券</div></div> */}
</div>
</div>
</div>
</>
)
}
export default CenterHome
\ No newline at end of file
.centerBox {
width: 1022px;
margin-left: 12px;
box-sizing: border-box;
.baseTitle {
font-size: 16px;
color: #808080;
}
.contentBox {
width: 100%;
height: calc(100% - 60px);
display: flex;
}
.basicBox {
width: 100%;
height: 226px;
padding: 20px 15px;
background-color: #fff;
box-sizing: border-box;
position: relative;
.headImg {
width: 130px;
height: 130px;
border: 1px solid #E0E0E0;
margin: 20px 30px;
}
.detailLines {
margin-top: 20px;
font-size: 14px;
width: calc(100% - 150px);
.lineItem {
width: calc(50% - 5px);
display: inline-block;
margin-bottom: 15px;
&:nth-child(odd) {
margin-right: 10px;
}
}
}
.myService {
position: absolute;
right: 15px;
top: 10px;
color: #808080;
font-size: 12px;
cursor: pointer;
}
.editButton {
width: 75px;
height: 20px;
background: #FFFFFF;
border-radius: 1px;
border: 1px solid #B0B0B0;
position: absolute;
left: 210px;
bottom: 35px;
font-size: 12px;
color: #595959;
cursor: pointer;
&:hover {
color: #000000;
}
}
}
.bottomBox{
display: flex;
margin-top: 10px;
.orderBox {
width: 660px;
padding: 30px 15px;
background-color: #fff;
box-sizing: border-box;
position: relative;
.statusBox {
width: 100%;
height: 114px;
display: flex;
box-sizing: border-box;
margin-top: 30px;
margin-left: 25px;
.statusItem {
width: 106px;
height: 100%;
border-radius: 18px;
margin-right: 20px;
padding: 15px;
box-sizing: border-box;
}
.pendPreview {
background: linear-gradient( 348deg, #F4F5F8 0%, #F3F8FF 100%);
}
.pendPay {
background: linear-gradient( 348deg, #FFF0EE 0%, #FBF8F6 100%);
}
.pendDiliver {
background: linear-gradient( 348deg, #F9F4FC 0%, #FDF8FF 100%);
}
.completed {
background: linear-gradient( 348deg, #F4F5F8 0%, #F3F8FF 100%);
}
}
.orderTitle {
height: 32px;
line-height: 20px;
font-weight: bold;
font-size: 14px;
color: #313131;
text-decoration-line: underline;
margin-top: 25px;
&::after {
content: "";
display: block;
width: 100%;
height: 1px;
margin-top: 10px;
background-color: #F4F4F4;
}
}
}
.infoBox {
width: 354px;
padding: 30px 15px;
background-color: #fff;
box-sizing: border-box;
position: relative;
margin-left: 10px;
.messageTable {
margin-top: 20px;
}
}
.moreButton {
position: absolute;
right: 10px;
top: 20px;
color: #888888;
cursor: pointer;
}
}
}
\ No newline at end of file
.userCenter {
padding: 10px;
width: 1226px;
margin: 0 auto;
display: flex;
}
\ No newline at end of file
import Footer from "@/components/Footer";
import Header from "@/components/Header";
import Nav from "./navigation/navigation";
import style from './index.module.scss'
import { GetServerSideProps } from 'next';
import { getCateList } from "@/server/getCateList";
import React, {useEffect, useState} from 'react'
import BasicData from './basicData/basicData'
import CenterHome from './centerHome/centerHome'
import InvoicePage from './invoice/invoice'
import AddressPage from './address/address'
import OrderPage from './myOrder/order'
import zhCN from 'antd/locale/zh_CN';
import type {ResponseTypeCateList} from '@/components/Header/components/NavBig/types'
import { ConfigProvider } from 'antd'
import theme from '@/public/antvTheme/theme'
export const getStaticProps: GetServerSideProps = async () => {
return getCateList()
}
const ComponentMap:{[key:string]: any} = {
'CenterHome': CenterHome,
'BasicData': BasicData,
'InvoicePage': InvoicePage,
'AddressPage': AddressPage,
'OrderPage': OrderPage
}
let DisCom = BasicData
const Page = (props:{cateList:ResponseTypeCateList}) => {
const {cateList}=props
const [selectObj, setSelectObj] = useState<any>({father: 'CenterHome', child: {title: '', key: 'CenterHome'}});
const [show, setShow] = useState(false)
useEffect(() => {
setShow(false)
DisCom = ComponentMap[selectObj.child.key];
setTimeout(() => {
setShow(true)
})
}, [selectObj])
const handleNavChange = (nav:any) => {
setSelectObj(nav)
}
return (
<ConfigProvider locale={ zhCN } theme={theme}>
<Header {...cateList}/>
<div className={style.userCenter}>
<Nav onMessageChange={handleNavChange} {...selectObj}></Nav>
{show && <DisCom/>}
</div>
<Footer />
</ConfigProvider>
)
}
export default Page
\ No newline at end of file
.invoiceBox {
width: 1022px;
margin-left: 12px;
box-sizing: border-box;
background: #fff;
padding: 20px 30px 100px;
position: relative;
.invoiceTitle {
font-size: 16px;
height: 32px;
color: #1A1A1A;
line-height: 21px;
width: 100%;
border-bottom: 1px solid #F4F4F4;
}
.invoiceButton {
left: 120px;
top: 18px;
position: absolute;
width: 80px;
height: 26px;
background: #FFFFFF;
border-radius: 1px;
color: #FF9A00;
border: 1px solid #FF9A00;
cursor: pointer;
&:hover {
color: #fff;
background-color: #FF9A00;
}
}
.invoiceItem {
height: 240px;
padding-top: 20px;
box-sizing: border-box;
border-bottom: 1px solid #F4F4F4;
.invoiceStatus {
width: 100%;
height: 20px;
display: flex;
line-height: 20px;
text-align: center;
justify-content: space-between;
.invoiceCheck {
display: flex;
align-items: center;
input {
cursor: pointer;
margin-right: 8px;
}
}
.rightPart {
display: flex;
align-items: center;
.invoiceimg {
display: inline-flex;
align-items: center;
img {
margin-right: 5px;
}
}
.invalid {
width: 60px;
height: 20px;
font-size: 12px;
color: #D0121B;
background: #FDF5F5;
border-radius: 1px;
margin-left: 25px;
border: 1px solid #D0121B;
cursor: pointer;
&:hover {
color: #fff;
background-color: #D0121B;
}
}
}
}
.invoiceDetail {
margin-top: 20px;
font-size: 14px;
width: 100%;
.lineItem {
width: calc(50% - 50px);
display: inline-block;
margin-bottom: 15px;
margin-left: 50px;
// &:nth-child(odd) {
// margin-right: 50px;
// }
}
}
}
.pagination {
position: absolute;
right: 30px;
bottom: 30px;
}
}
.modalContent {
min-height: 450px;
padding: 45px 0;
box-sizing: border-box;
}
.modalFooter {
width: 100%;
display: flex;
margin-top: 20px;
justify-content: space-around;
.saveButton {
width: 195px;
height: 45px;
color: #fff;
background: #FF9A00;
border-radius: 1px;
cursor: pointer;
}
.cancelButton {
width: 195px;
height: 45px;
color: #FF9A00;
background: #fff;
border-radius: 1px;
cursor: pointer;
border: 1px solid #FF9A00;
}
}
\ No newline at end of file
import styles from './index.module.scss'
import {useState, useEffect} from 'react'
import InvoiceTitleList from './invoiceType'
import { Radio , Modal, Form, Input, Pagination} from 'antd';
import { useAuth } from '@/hooks/useAuth'
import useRequest from "@/hooks/useRequest"
const InvoicePage = () => {
const [open, setOpen] = useState(false)
const [value, setValue] = useState(3);
const [invoiceList, setInvoiceList] = useState<any>([]);
const [formData, setFormData] = useState<any>({});
const [current, setCurrent] = useState(1);
const [pageSize, setPageSize] = useState(5);
const [total, setTotal] = useState(0);
const [ form ] = Form.useForm()
const {user} = useAuth()
const { request: userRequest } = useRequest<any>({ manual: true })
useEffect(() => {
handleDataInit()
}, [user])
const handleDataInit = async () => {
if(!user && !user?.web_user_id) return
const invoiceListResponse = await userRequest({
url: `/api/userInfo/userInvoiceList?limit=${pageSize}&page=${current}`,
method: 'get',
headers: {
'webUserId': user?.web_user_id
}
})
if (invoiceListResponse.code === 0) {
setInvoiceList(invoiceListResponse.data.list)
setTotal(invoiceListResponse.data.list.length)
}
}
const handleInvoiceSave = async (isAdd: boolean) => {
if(isAdd) formData.invoice_type = value
const saveInvoiceResponse = await userRequest({
url: '/api/userInfo/saveInvoice',
method: 'post',
data: formData
})
if (saveInvoiceResponse.code === 0) {
if (isAdd) {
form.resetFields()
handleModal(false)
setValue(3)
setFormData(null)
}
handleDataInit()
}
}
const manualValidate = () => {
form.validateFields().then((value:any) => {
setFormData(value)
handleInvoiceSave(true)
}).catch((e) => {});
}
const onChange = (e: any) => {
form.resetFields()
setValue(e.target.value)
}
const handleModal = (show:boolean) => {
setOpen(show);
}
const handleInvoiceEdit = (data:any, key:string) => {
data[key] = 1
setFormData(data)
handleInvoiceSave(false)
}
const validateMessages = {
required: '${label}不能为空!'
}
const handlePageChange = (page:number, pageSize:number) => {
setCurrent(page)
setPageSize(pageSize)
}
return (
<>
<div className={styles.invoiceBox}>
<div className={styles.invoiceTitle}>我的发票</div>
<button className={styles.invoiceButton} onClick={() => handleModal(true)}>新增发票</button>
{
invoiceList.map((invoiceItem:any) => {
return (
<div className={styles.invoiceItem} key={invoiceItem.web_user_invoice_id}>
<div className={styles.invoiceStatus}>
<div className={styles.invoiceCheck}>
{ !invoiceItem.is_default && (<><input type="checkbox" onClick={() => handleInvoiceEdit(invoiceItem, 'is_default')}/>设为默认</>)}
</div>
<div className={styles.rightPart}>
<div className={styles.invoiceimg}>
<img src="/images/success.png"></img>
{invoiceItem.audit_status_cn}
</div>
{ !invoiceItem.is_del && (<><button className={styles.invalid} onClick={() => handleInvoiceEdit(invoiceItem, 'is_del')}>作废</button></>)}
</div>
</div>
<div className={styles.invoiceDetail}>
{ InvoiceTitleList.map(invoiceTitleItem => {
if (invoiceTitleItem.type.includes(invoiceItem.invoice_type))
return (
<div className={styles.lineItem} key={invoiceTitleItem.key}>
<span>{invoiceTitleItem.text}</span>
<span>{invoiceItem[invoiceTitleItem.key as keyof typeof invoiceItem]}</span>
</div>
)
})}
</div>
</div>
)
})
}
<div className={styles.pagination}>
<Pagination
showQuickJumper
showSizeChanger
current={current}
pageSize={pageSize}
pageSizeOptions={[5, 10]}
total={total}
showTotal={(total) => `共 ${total} 条记录`}
onChange={handlePageChange}/>
</div>
</div>
<Modal
title="发票类型:"
open={open}
destroyOnClose
footer={null}
closeIcon={null}
centered
onCancel={() => handleModal(false)}
>
<Radio.Group onChange={onChange} value={value}>
<Radio value={3}>增值税专用发票</Radio>
<Radio value={4}>增值税普通发票</Radio>
<Radio value={2}>个人发票</Radio>
</Radio.Group>
<Form
className={styles.modalContent}
validateMessages={validateMessages}
form={form}
labelCol={{ span: 6 }}
wrapperCol={{ span: 16 }}>
{ InvoiceTitleList.map(invoiceTitleItem => {
if (invoiceTitleItem.type.includes(value))
return (
<Form.Item
className={styles.lineItem}
key={invoiceTitleItem.key}
label={invoiceTitleItem.text}
name={invoiceTitleItem.key}
rules={[{ required: true }]}>
<Input/>
</Form.Item>
)
})}
</Form>
<div className={styles.modalFooter}>
<button className={styles.saveButton} onClick={() => manualValidate()}>保存</button>
<button className={styles.cancelButton} onClick={() => handleModal(false)}>取消</button>
</div>
</Modal>
</>
)
}
export default InvoicePage
\ No newline at end of file
// invoice_type: 2-普通发票(个人发票),3-增值税专用发票,4-增值税普通发票
const InvoiceTitle = [
{
text: '发票抬头',
key: 'tax_title',
type: [2, 3, 4]
},
{
text: '统一税务号',
key: 'tax_no',
type: [3, 4]
},
{
text: '开户行',
key: 'bank_name',
type: [3]
},
{
text: '银行账号',
key: 'bank_account',
type: [3]
},
{
text: '公司电话',
key: 'company_phone',
type: [3]
},
{
text: '注册地址',
key: 'company_address',
type: [3]
}
]
export default InvoiceTitle
\ No newline at end of file
.orderBox {
width: 1022px;
margin-left: 12px;
box-sizing: border-box;
padding: 20px 30px;
background-color: #fff;
box-sizing: border-box;
position: relative;
.baseTitle {
font-size: 16px;
color: #1A1A1A;
}
.moreButton {
position: absolute;
right: 10px;
top: 20px;
color: #888888;
cursor: pointer;
}
.statusBox {
width: 100%;
height: 90px;
display: flex;
box-sizing: border-box;
padding: 30px 0;
margin-top: 10px;
border-top: 1px solid #F4F4F4;
.statusItem {
height: 20px;
line-height: 20px;
box-sizing: border-box;
border-left: 1px solid #E2E2E2;
padding-left: 10px;
cursor: pointer;
&:first-child {
border-left: none;
padding-left: 0;
}
}
.statusItemSelected {
color: #D0121B;
}
}
.orderTitle {
height: 32px;
line-height: 20px;
font-weight: bold;
font-size: 14px;
color: #313131;
text-decoration-line: underline;
margin-top: 25px;
border: 1px solid #f0f0f0;
border-bottom: none;
}
}
import styles from './index.module.scss'
import {useState, useEffect} from 'react'
import { DatePicker, Table } from "antd";
import type { TableColumnsType } from 'antd';
import { useAuth } from '@/hooks/useAuth'
import useRequest from "@/hooks/useRequest"
import { DataType } from "@/types/orderTypes"
const { RangePicker } = DatePicker
const OrderPage = () => {
const {user} = useAuth()
const { request: userRequest } = useRequest<any>({ manual: true })
const [goodsList, setGoodsList] = useState<any>([])
const columns: TableColumnsType<DataType> = [
{ title: '序号', dataIndex: 'name', key: 'name', render:(text, record, index) => { return <span>{index + 1}</span>} },
{ title: '型号/品牌', dataIndex: 'age', key: 'age' },
{ title: '数量', dataIndex: 'goods_number', key: 'goods_number' },
{ title: '单价', dataIndex: 'goods_price', key: 'goods_price' },
{ title: '小计', dataIndex: 'goods_amount', key: 'goods_amount' },
{ title: '明细备注', dataIndex: 'web_order_remark', key: 'web_order_remark' }
]
const [statusSummaryMap, setStatusSummaryMap] = useState<any>({'1': {count: 0}, '2': {count: 0}, '3': {count: 0}, '4': {count: 0}})
useEffect(() => {
handleDataInit()
}, [user])
const handleDataInit = async () => {
if(!user && !user?.web_user_id) return
const tatusSummaryResponse = await userRequest({
url: '/api/order/webOrderStatusSummary',
method: 'post'
})
if (tatusSummaryResponse.code === 0) setStatusSummaryMap(tatusSummaryResponse.data)
const webOrderListResponse = await userRequest({
url: '/api/order/webOrderList',
method: 'post',
headers: {
'webUserId': user?.web_user_id
},
})
if (webOrderListResponse.code === 0) setGoodsList(webOrderListResponse.data.list)
}
return (
<div className={styles.orderBox}>
<div className={styles.baseTitle}>我的订单</div>
<div className={styles.moreButton}>
我的客服
<span style={{marginLeft: '5px'}}>{'>'}</span>
</div>
<div className={styles.statusBox}>
<div className={`${styles.statusItem}`}>
全部订单({statusSummaryMap['1']['count']}
</div>
<div className={`${styles.statusItem}`}>
待审核({statusSummaryMap['1']['count']}
</div>
<div className={`${styles.statusItem}`}>
待付款({statusSummaryMap['1']['count']}
</div>
<div className={`${styles.statusItem}`}>
待发货({statusSummaryMap['1']['count']}
</div>
<div className={`${styles.statusItem}`}>
已完结({statusSummaryMap['1']['count']}
</div>
<div className={`${styles.statusItem}`}>
已取消
</div>
<div style={{marginTop: '-5px', marginLeft: 'auto'}}>
下单时间:<RangePicker />
</div>
</div>
{
goodsList.map((goodItem:any) => {
return (
<div key={goodItem.order_id}>
<div className={styles.orderTitle}>
订单号:{goodItem.order_sn}
</div>
<Table<DataType>
size="small"
bordered={true}
pagination={false}
columns={columns}
rowKey={(record) => record?.rec_id}
expandable={{
expandIcon: (r) => null,
defaultExpandAllRows: true,
expandedRowRender: (record) => {
return (
<p style={{ margin: 0 }}>最新进度:{record.latest_log_time} 状态:{record.latest_log_status_text} {record.latest_log}</p>
)
},
rowExpandable: (record) => record.name !== 'Not Expandable',
}}
dataSource={goodItem.order_items}
/>
</div>
)
})
}
</div>
)
}
export default OrderPage
\ No newline at end of file
.navigation {
width: 190px;
padding-top: 20px;
background: #fff;
overflow-x: hidden;
box-sizing: border-box;
.navTitle {
height: 36px;
padding-left: 15px;
font-size: 16px;
line-height: 20px;
border-bottom: 1px solid #F4F4F4;
margin-bottom: 25px;
position: relative;
cursor: pointer;
.title {
padding-left: 15px;
}
}
.item {
font-size: 16px;
margin-bottom: 35px;
.itemText {
padding-left: 25px;
}
.itemChild {
padding-left: 40px;
font-size: 14px;
color: #808080;
cursor: pointer;
margin-top: 15px;
position: relative;
&:hover {
color: #FF9A00;
}
}
}
.itemSelected {
color: #FF9A00;
&::after {
content: "";
display: block;
position: absolute;
width: 3px;
height: 20px;
left: 0;
top: 0;
background-color: #FF9A00;
}
}
}
\ No newline at end of file
const NavList = [
{
title: '订单中心',
key: 'orderCenter',
children: [
{
title: '我的订单',
key: 'OrderPage'
},
{
title: '我的售后',
key: 'AfterSales'
}
]
},
{
title: '我的钱包',
key: 'myWallet',
children: [
{
title: '我的优惠券',
key: 'MyCoupons'
}
]
},
{
title: '我的信息',
key: 'myInformation',
children: [
{
title: '站内信',
key: 'Mail'
}
]
},
{
title: '资料中心',
key: 'dataCenter',
children: [
{
title: '基础信息',
key: 'BasicData'
},
{
title: '我的发票',
key: 'InvoicePage'
},
{
title: '我的地址',
key: 'AddressPage'
}
]
},
{
title: '我的关注',
key: 'myFollow',
children: [
{
title: '我的收藏',
key: 'MyCollection'
}
]
}
]
import styles from './index.module.scss'
import {useState, useEffect} from 'react'
const Nav = (props:{onMessageChange:any}) => {
const [ selectObj, setSelectObj ] = useState<any>({father: '', child: {}});
const { onMessageChange }=props
useEffect(() => {
setSelectObj(props)
}, [onMessageChange])
const handleSelected = (itemTitle:string, itemChild:Object) => {
setSelectObj({father: itemTitle, child: itemChild})
// props({father: itemTitle, child: itemChild})
props.onMessageChange({father: itemTitle, child: itemChild})
}
return (
<>
<div className={styles.navigation}>
<div className={`${styles.navTitle} ${(selectObj?.father === 'CenterHome') && styles.itemSelected}`} onClick={() => handleSelected('CenterHome', {key: 'CenterHome'})}>
<span className={styles.title}>首页</span>
</div>
{
NavList.map((item) => {
return (
<div className={styles.item} key={item.key}>
<span className={styles.itemText}>{item.title}</span>
{
item.children.map((itemChild) => {
return (
<div className={`${styles.itemChild} ${(selectObj?.child?.key === itemChild.key) && styles.itemSelected}`}
key={itemChild.key} onClick={() => handleSelected(item.title, itemChild)}>
<span className={styles.childText}>{itemChild.title}</span>
</div>
)
})
}
</div>
)
})
}
</div>
</>
)
}
export default Nav
\ No newline at end of file
...@@ -8,9 +8,10 @@ import TopH from "@/components/Header/components/TopH" ...@@ -8,9 +8,10 @@ import TopH from "@/components/Header/components/TopH"
import SearchH from "@/components/Header/components/SearchH" import SearchH from "@/components/Header/components/SearchH"
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useToast } from '@/hooks/useToast'; import { useToast } from '@/hooks/useToast';
import { useCookies } from "@/hooks/useCookies"
const Page = () => { const Page = () => {
const [user, setUser] = useState<any>({}); const [user, setUser] = useState<any>();
const [count, setCount] = useState(''); const [count, setCount] = useState('');
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const [rePassword, setRePassword] = useState(''); const [rePassword, setRePassword] = useState('');
...@@ -22,6 +23,7 @@ const Page = () => { ...@@ -22,6 +23,7 @@ const Page = () => {
const [msg, setMsg] = useState(''); const [msg, setMsg] = useState('');
const {message} = useToast() const {message} = useToast()
const messageRef = useRef(message); const messageRef = useRef(message);
const {setCookie} = useCookies()
const handleCountChange = (e:any) => setCount(e.target.value); const handleCountChange = (e:any) => setCount(e.target.value);
const handlePasswordChange = (e:any) => setPassword(e.target.value); const handlePasswordChange = (e:any) => setPassword(e.target.value);
...@@ -55,7 +57,7 @@ const Page = () => { ...@@ -55,7 +57,7 @@ const Page = () => {
} }
const handleGoLogin = () => { const handleGoLogin = () => {
router.push('login.html?type=1') router.push('login?type=1')
} }
// 登录 // 登录
...@@ -76,8 +78,9 @@ const Page = () => { ...@@ -76,8 +78,9 @@ const Page = () => {
data: param, data: param,
}) })
if (response.code === 0) { if (response.code === 0) {
setCookie('token', response.data.token, 1)
setCookie('userId', response.data.user.web_user_id, 1)
setUser(response.data.user) setUser(response.data.user)
localStorage.setItem("token", response.data.token)
messageRef.current(response.msg) messageRef.current(response.msg)
setStep(2); setStep(2);
} else { } else {
......
import type { ThemeConfig } from 'antd'
const theme: ThemeConfig = {
components: {
Table: {
headerBorderRadius: 0
}
}
}
export default theme
\ No newline at end of file
export type invoiceMapResponseType = {
code: number|string,
data: {
list:Array<invoiceItemType>
},
msg:string
}
export type invoiceItemType={
invoice_type: number,
tax_title:number|string,
tax_no:string,
bank_name:string,
bank_account:string,
company_phone:string,
company_address:string,
}
export interface DataType {
key: React.Key;
name: string;
goods_number: number;
address: string;
goods_price: string;
goods_amount: string;
latest_log: string;
latest_log_time: string;
latest_log_status_text: string;
rec_id: number
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment