Commit 6a744f3f by 孙志鹏

feat: 打包测试

parent 485ca146
Showing with 1551 additions and 118 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) => {
url: '/contact.html',
name: '联系我们'
}, {
url: '/memberCenter.html',
url: '/memberCenter',
name: '会员中心'
}]
......
......@@ -16,10 +16,14 @@ const TopH=()=>{
<div className={`${styles.con} row bothSide verCenter`}>
<div ></div>
<div className='row verCenter'>
<Link href='/login.html?type=1'>立即登录</Link>
{(!user?.web_user_id) && (
<>
<Link href='/login?type=1'>立即登录</Link>
<span className={styles.line}></span>
<Link href='/login.html?type=2'>立即注册</Link>
<Link href='/login?type=2'>立即注册</Link>
<span className={styles.line}></span>
</>
)}
<Link href='/about.html'>关于我们</Link>
<span className={styles.line}></span>
<Link href='/contact.html'>联系我们</Link>
......
......@@ -5,33 +5,11 @@ import { useSeoTitle } from "@/hooks/useSeoTitle"
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 seoTitle=useSeoTitle()
setTimeout(() => {
handleUserInfo()
})
return (
<>
......
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { useCookies } from "./useCookies"
import useRequest from "./useRequest"
export const useAuth = () => {
const [user, setUser] = useState(null);
const login = (userData: any) => {
localStorage.setItem('userKey', JSON.stringify({token: userData.token, webUserId: userData.user.web_user_id}));
setUser(userData);
};
const logout = () => {
localStorage.removeItem('userKey');
setUser(null);
};
const getUserInfo = () => {
const [user, setUser] = useState<any>(null);
const {getCookie}=useCookies()
const { request: userRequest } = useRequest<any>({ manual: true,loading:false })
useEffect(() => {
const token = getCookie('token')
const userId = getCookie('userId')
if (userId && !user) {
userRequest({
url: '/api/userInfo/userInfo',
method: 'get',
headers: {
'webUserId': userId,
Authorization: token
},
}).then(res => {
if (res?.code === 0) setUser(res.data)
})
} else {
}
}, [])
return { user, login, logout };
return { user, setUser };
};
......@@ -3,6 +3,8 @@ import { useCallback, useEffect, useRef, useState } from 'react';
import axios, { AxiosRequestConfig } from "axios"
import { useToast } from './useToast';
import {API_URL} from '@/configReact'
import { useCookies } from "@/hooks/useCookies"
import { useRouter } from 'next/router';
// 默认请求参数
const defaultRequestConfig = {
......@@ -16,6 +18,9 @@ function useRequest<T>(options: AxiosRequestConfig & { manual?: boolean,loading?
const loadingRef = useRef(loading);
const messageRef = useRef(message);
const {getCookie} = useCookies()
const router = useRouter()
const request = useCallback((requestOption?: AxiosRequestConfig) => {
setData(null)
......@@ -24,14 +29,21 @@ function useRequest<T>(options: AxiosRequestConfig & { manual?: boolean,loading?
loadingRef.current()
}
const headers = requestOption?.headers
const userId = getCookie("userId")
const token = getCookie("token")
return axios.request<T>({
baseURL:API_URL,
...requestOption,
headers: localStorage.getItem("token") ? {
Authorization: localStorage.getItem("token"),
headers: {
Authorization: token,
webUserId: userId,
...headers
} : headers
}
}).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)
return response.data
}).catch((e: any) => {
......
......@@ -15,6 +15,7 @@ export const useSeoTitle = () => {
'/about.html': { title: '关于我们' },
'/contact.html': { title: '联系我们' },
'/notice.html': { title: '公告' },
'/memberCenter': { title: '会员中心' },
}
if (Object.keys(titleKeywords).indexOf(router.pathname) !== -1) {
titlestr=titleKeywords[router.pathname]['title']
......
{
"name": "europaWeb",
"name": "europa-web",
"version": "0.1.0",
"private": true,
"scripts": {
......@@ -9,11 +9,15 @@
"lint": "next lint"
},
"dependencies": {
"antd": "^5.21.6",
"axios": "^1.7.2",
"next": "^13.0.5",
"react": "^18",
"react-dom": "^18",
"react-paginate": "^8.2.0",
"react-redux": "^9.1.2",
"react-router-dom": "^6.27.0",
"redux": "^5.0.1",
"sass": "^1.77.8",
"swiper": "^11.1.5"
},
......
......@@ -9,12 +9,13 @@ import SearchH from "@/components/Header/components/SearchH"
import Link from 'next/link'
import { useRouter } from 'next/router';
import { useToast } from '@/hooks/useToast';
import { useAuth } from "@/hooks/useAuth"
import { useCookies } from '@/hooks/useCookies'
const Page = () => {
const router = useRouter()
const { query } = router
const { login } = useAuth()
const {setCookie} = useCookies()
const [loginType, setLoginType] = useState(query.type);
const [count, setCount] = useState('');
......@@ -88,9 +89,10 @@ const Page = () => {
data: param,
})
if (response.code === 0) {
login(response.data)
setCookie('token', response.data.token, 1)
setCookie('userId', response.data.user.web_user_id, 1)
messageRef.current(response.msg)
router.push('memberCenter.html')
router.push('memberCenter')
}
else {
setMsg(response.msg)
......@@ -145,7 +147,7 @@ const Page = () => {
</>
)}
<div className={style.forgetButton}>
<Link href="/passwordRecover.html">
<Link href="/passwordRecover">
<span className={style.cursorPointer}>忘记密码?</span>
</Link>
</div>
......
const Page = () => {
return (
<>
<div>会员中心</div>
</>
)
}
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;
.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"
import SearchH from "@/components/Header/components/SearchH"
import { useRouter } from 'next/router';
import { useToast } from '@/hooks/useToast';
import { useCookies } from "@/hooks/useCookies"
const Page = () => {
const [user, setUser] = useState<any>({});
const [user, setUser] = useState<any>();
const [count, setCount] = useState('');
const [password, setPassword] = useState('');
const [rePassword, setRePassword] = useState('');
......@@ -22,6 +23,7 @@ const Page = () => {
const [msg, setMsg] = useState('');
const {message} = useToast()
const messageRef = useRef(message);
const {setCookie} = useCookies()
const handleCountChange = (e:any) => setCount(e.target.value);
const handlePasswordChange = (e:any) => setPassword(e.target.value);
......@@ -55,7 +57,7 @@ const Page = () => {
}
const handleGoLogin = () => {
router.push('login.html?type=1')
router.push('login?type=1')
}
// 登录
......@@ -76,8 +78,9 @@ const Page = () => {
data: param,
})
if (response.code === 0) {
setCookie('token', response.data.token, 1)
setCookie('userId', response.data.user.web_user_id, 1)
setUser(response.data.user)
localStorage.setItem("token", response.data.token)
messageRef.current(response.msg)
setStep(2);
} 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