update
This commit is contained in:
parent
52d3d768e3
commit
56f4ce81f7
@ -11,6 +11,18 @@ const proxy = {
|
|||||||
proxyRes.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization';
|
proxyRes.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization';
|
||||||
proxyRes.headers['Access-Control-Allow-Credentials'] = 'true';
|
proxyRes.headers['Access-Control-Allow-Credentials'] = 'true';
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'/kaipiao': {
|
||||||
|
target: 'https://cosmic-sandbox.piaozone.com/xhcamzchwbgonerr/',
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: false,
|
||||||
|
pathRewrite: { '^/kaipiao': '' },
|
||||||
|
onProxyRes: function (proxyRes) {
|
||||||
|
proxyRes.headers['Access-Control-Allow-Origin'] = '*';
|
||||||
|
proxyRes.headers['Access-Control-Allow-Methods'] = 'GET,PUT,POST,DELETE,OPTIONS';
|
||||||
|
proxyRes.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, X-Requested-With';
|
||||||
|
proxyRes.headers['Access-Control-Allow-Credentials'] = 'true';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,11 @@ export default [
|
|||||||
path: '/DigitalElectronics/index',
|
path: '/DigitalElectronics/index',
|
||||||
name: '开票管理',
|
name: '开票管理',
|
||||||
component: "@/pages/DigitalElectronics/index",
|
component: "@/pages/DigitalElectronics/index",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/InvoiceSearch/index',
|
||||||
|
name: '开票查询',
|
||||||
|
component: "@/pages/InvoiceSearch/index",
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -227,6 +227,18 @@ const UserModel: UserModelType = {
|
|||||||
name: '开票管理',
|
name: '开票管理',
|
||||||
component: "@/pages/Invoicing/index",
|
component: "@/pages/Invoicing/index",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/DigitalElectronics/index',
|
||||||
|
redirect: '',
|
||||||
|
name: '开票管理',
|
||||||
|
component: "@/pages/DigitalElectronics/index",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/InvoiceSearch/index',
|
||||||
|
redirect: '',
|
||||||
|
name: '开票查询',
|
||||||
|
component: "@/pages/InvoiceSearch/index",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,7 +308,9 @@ const UserModel: UserModelType = {
|
|||||||
authority: [
|
authority: [
|
||||||
'/test/index',
|
'/test/index',
|
||||||
'/cloudMenu/test/index',
|
'/cloudMenu/test/index',
|
||||||
"/Invoicing/index"
|
"/Invoicing/index",
|
||||||
|
"/DigitalElectronics/index",
|
||||||
|
"/InvoiceSearch/index"
|
||||||
// '/examine/index',
|
// '/examine/index',
|
||||||
// '/examine/modal',
|
// '/examine/modal',
|
||||||
// '/examine/question',
|
// '/examine/question',
|
||||||
|
|||||||
483
src/pages/DigitalElectronics/index.tsx
Normal file
483
src/pages/DigitalElectronics/index.tsx
Normal file
@ -0,0 +1,483 @@
|
|||||||
|
// 数电的开票
|
||||||
|
import ProTable, { ActionType } from "@ant-design/pro-table";
|
||||||
|
import { FormInstance, Button, message, Tag } from "antd";
|
||||||
|
import { useRef, useState } from "react";
|
||||||
|
import { connect } from "umi";
|
||||||
|
import { Datum } from "@/types/invoice";
|
||||||
|
import { handleGetAppToken, handleGetKaiPiao, handleGetSDToken } from "./service";
|
||||||
|
|
||||||
|
interface ConnectState {
|
||||||
|
user: {
|
||||||
|
data: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const DigitalElectronics: React.FC<{ currentUser: any }> = (props) => {
|
||||||
|
const actionRef = useRef<ActionType>();
|
||||||
|
const formRef = useRef<FormInstance>();
|
||||||
|
|
||||||
|
// 第一个接口的token
|
||||||
|
const [appToken, setAppToken] = useState<any>()
|
||||||
|
// 第二个接口的token
|
||||||
|
const [accessToken, setAccessToken] = useState<any>()
|
||||||
|
|
||||||
|
// 开票处理函数
|
||||||
|
const handleInvoice = async (record: Datum) => {
|
||||||
|
try {
|
||||||
|
message.loading({ content: '正在发起开票...', key: 'invoice' });
|
||||||
|
|
||||||
|
// 检查是否有access_token
|
||||||
|
if (!accessToken) {
|
||||||
|
message.error({ content: '缺少访问令牌,请先刷新页面重新获取', key: 'invoice' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造原始数据
|
||||||
|
const originalData = [record]; // 将record作为data数组的一项
|
||||||
|
|
||||||
|
// 将data字段进行base64加密
|
||||||
|
const dataString = JSON.stringify(originalData);
|
||||||
|
const encryptedData = btoa(unescape(encodeURIComponent(dataString))); // base64编码,支持中文
|
||||||
|
|
||||||
|
// 构造开票请求数据
|
||||||
|
const invoiceData = {
|
||||||
|
businessSystemCode: "BUSINESS_YCIC", // 来源系统编码
|
||||||
|
interfaceCode: "BILL.PUSH", // 接口业务编码
|
||||||
|
requestId: Date.now().toString(), // 时间戳作为请求ID
|
||||||
|
data: encryptedData // 加密后的data字符串
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('原始数据:', originalData);
|
||||||
|
console.log('JSON字符串:', dataString);
|
||||||
|
console.log('Base64加密后:', encryptedData);
|
||||||
|
console.log('最终请求数据:', invoiceData);
|
||||||
|
console.log('访问令牌:', accessToken);
|
||||||
|
|
||||||
|
const response = await handleGetKaiPiao(invoiceData, accessToken);
|
||||||
|
console.log('开票响应:', response);
|
||||||
|
|
||||||
|
if (response && response.code === 200) {
|
||||||
|
message.success({ content: `单据 ${record.billNo} 开票申请已提交成功`, key: 'invoice' });
|
||||||
|
} else {
|
||||||
|
message.error({ content: `开票申请失败: ${response?.message || '未知错误'}`, key: 'invoice' });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('开票错误:', error);
|
||||||
|
message.error({ content: '开票申请失败,请重试', key: 'invoice' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns: any = [
|
||||||
|
{
|
||||||
|
title: '单据编号',
|
||||||
|
dataIndex: 'billNo',
|
||||||
|
key: 'billNo',
|
||||||
|
width: 180,
|
||||||
|
ellipsis: true,
|
||||||
|
search: true,
|
||||||
|
hideInSearch: false,
|
||||||
|
onHeaderCell: () => ({
|
||||||
|
style: { textAlign: 'center' }
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '购方名称',
|
||||||
|
dataIndex: 'buyerName',
|
||||||
|
key: 'buyerName',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
search: false,
|
||||||
|
hideInSearch: true,
|
||||||
|
onHeaderCell: () => ({
|
||||||
|
style: { textAlign: 'center' }
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '购方税号',
|
||||||
|
dataIndex: 'buyerTaxpayerId',
|
||||||
|
key: 'buyerTaxpayerId',
|
||||||
|
width: 180,
|
||||||
|
ellipsis: true,
|
||||||
|
search: false,
|
||||||
|
hideInSearch: true,
|
||||||
|
onHeaderCell: () => ({
|
||||||
|
style: { textAlign: 'center' }
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '销方名称',
|
||||||
|
dataIndex: 'sellerName',
|
||||||
|
key: 'sellerName',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
search: false,
|
||||||
|
hideInSearch: true,
|
||||||
|
onHeaderCell: () => ({
|
||||||
|
style: { textAlign: 'center' }
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '销方税号',
|
||||||
|
dataIndex: 'sellerTaxpayerId',
|
||||||
|
key: 'sellerTaxpayerId',
|
||||||
|
width: 180,
|
||||||
|
ellipsis: true,
|
||||||
|
search: true,
|
||||||
|
hideInSearch: false,
|
||||||
|
onHeaderCell: () => ({
|
||||||
|
style: { textAlign: 'center' }
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '单据日期',
|
||||||
|
dataIndex: 'billDate',
|
||||||
|
key: 'billDate',
|
||||||
|
width: 120,
|
||||||
|
search: false,
|
||||||
|
hideInSearch: true,
|
||||||
|
onHeaderCell: () => ({
|
||||||
|
style: { textAlign: 'center' }
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '单据金额',
|
||||||
|
dataIndex: 'totalAmount',
|
||||||
|
key: 'totalAmount',
|
||||||
|
width: 120,
|
||||||
|
search: false,
|
||||||
|
hideInSearch: true,
|
||||||
|
render: (text: number) => text ? `¥${text.toFixed(2)}` : '-',
|
||||||
|
onHeaderCell: () => ({
|
||||||
|
style: { textAlign: 'center' }
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '含税标识',
|
||||||
|
dataIndex: 'includeTaxFlag',
|
||||||
|
key: 'includeTaxFlag',
|
||||||
|
width: 100,
|
||||||
|
search: false,
|
||||||
|
hideInSearch: true,
|
||||||
|
render: (text: string) => (
|
||||||
|
<Tag color={text === '1' ? 'green' : 'blue'}>
|
||||||
|
{text === '1' ? '含税' : '不含税'}
|
||||||
|
</Tag>
|
||||||
|
),
|
||||||
|
onHeaderCell: () => ({
|
||||||
|
style: { textAlign: 'center' }
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '发票性质',
|
||||||
|
dataIndex: 'invoiceProperty',
|
||||||
|
key: 'invoiceProperty',
|
||||||
|
width: 100,
|
||||||
|
search: true,
|
||||||
|
hideInSearch: false,
|
||||||
|
valueType: 'select',
|
||||||
|
valueEnum: {
|
||||||
|
'0': { text: '蓝票', status: 'Success' },
|
||||||
|
'1': { text: '红票', status: 'Error' },
|
||||||
|
},
|
||||||
|
render: (text: string) => (
|
||||||
|
<Tag color={text === '1' ? 'red' : 'green'}>
|
||||||
|
{text === '1' ? '红票' : '蓝票'}
|
||||||
|
</Tag>
|
||||||
|
),
|
||||||
|
onHeaderCell: () => ({
|
||||||
|
style: { textAlign: 'center' }
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '自动开票',
|
||||||
|
dataIndex: 'autoInvoice',
|
||||||
|
key: 'autoInvoice',
|
||||||
|
width: 100,
|
||||||
|
search: false,
|
||||||
|
hideInSearch: true,
|
||||||
|
render: (text: string) => (
|
||||||
|
<Tag color={text === '1' ? 'green' : 'default'}>
|
||||||
|
{text === '1' ? '是' : '否'}
|
||||||
|
</Tag>
|
||||||
|
),
|
||||||
|
onHeaderCell: () => ({
|
||||||
|
style: { textAlign: 'center' }
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '备注',
|
||||||
|
dataIndex: 'remark',
|
||||||
|
key: 'remark',
|
||||||
|
width: 150,
|
||||||
|
ellipsis: true,
|
||||||
|
search: false,
|
||||||
|
hideInSearch: true,
|
||||||
|
onHeaderCell: () => ({
|
||||||
|
style: { textAlign: 'center' }
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
width: 120,
|
||||||
|
fixed: 'center',
|
||||||
|
search: false,
|
||||||
|
hideInSearch: true,
|
||||||
|
render: (text: any, record: Datum) => (
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
onClick={() => handleInvoice(record)}
|
||||||
|
>
|
||||||
|
发起开票
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
onHeaderCell: () => ({
|
||||||
|
style: { textAlign: 'center' }
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<style>
|
||||||
|
{`
|
||||||
|
.ant-table-tbody > tr > td {
|
||||||
|
text-align: left !important;
|
||||||
|
}
|
||||||
|
.ant-table-thead > tr > th {
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
</style>
|
||||||
|
<ProTable
|
||||||
|
actionRef={actionRef}
|
||||||
|
formRef={formRef}
|
||||||
|
columns={columns}
|
||||||
|
bordered
|
||||||
|
expandable={{
|
||||||
|
expandRowByClick: true
|
||||||
|
}}
|
||||||
|
rowKey={(record) => {
|
||||||
|
return `${record?.billNo}`
|
||||||
|
}}
|
||||||
|
scroll={{ x: "100%", y: 'calc(100vh - 400px)' }}
|
||||||
|
headerTitle={<span style={{ color: "#1890ff", fontSize: 14, fontWeight: 600 }}>待开票列表</span>}
|
||||||
|
search={{
|
||||||
|
span: 8,
|
||||||
|
labelWidth: 100,
|
||||||
|
searchText: '查询',
|
||||||
|
resetText: '重置',
|
||||||
|
collapseRender: false,
|
||||||
|
collapsed: false
|
||||||
|
}}
|
||||||
|
request={async (params: any) => {
|
||||||
|
let req: any = {
|
||||||
|
appId: "FPY001",
|
||||||
|
appSecret: "FPY001fpy@2023***",
|
||||||
|
accountId: "2280459335882518528",
|
||||||
|
tenantid: "xhcamzchwbgonerr",
|
||||||
|
language: ""
|
||||||
|
}
|
||||||
|
const token = await handleGetAppToken(req)
|
||||||
|
console.log('tokentokentoken', token);
|
||||||
|
|
||||||
|
let appToken: any = token.data.app_token
|
||||||
|
|
||||||
|
let tokenReq: any = {
|
||||||
|
user: "18620126214",
|
||||||
|
apptoken: appToken,
|
||||||
|
tenantid: "xhcamzchwbgonerr",
|
||||||
|
accountId: "2280459335882518528",
|
||||||
|
usertype: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessToken: any = await handleGetSDToken(tokenReq)
|
||||||
|
console.log('accessTokenaccessTokenaccessToken', accessToken);
|
||||||
|
|
||||||
|
setAppToken(appToken)
|
||||||
|
setAccessToken(accessToken.data.access_token)
|
||||||
|
|
||||||
|
|
||||||
|
// 模拟数据
|
||||||
|
const mockData: any = [
|
||||||
|
{
|
||||||
|
billNo: "SRM_20250820001",
|
||||||
|
billDate: "2023-05-17",
|
||||||
|
totalAmount: 11300,
|
||||||
|
autoInvoice: "0",
|
||||||
|
invoiceType: "08xdp",
|
||||||
|
buyerName: "深圳市顺丰快运有限公司test01",
|
||||||
|
buyerProperty: "0",
|
||||||
|
sellerTaxpayerId: "91530112MA7MQ2JR9U",
|
||||||
|
sellerName: "云南交投集团经营开发有限公司彩云驿商业管理分公司",
|
||||||
|
sellerBankAndAccount: "13710884704",
|
||||||
|
sellerAddressAndTel: "高新技术产业园南区科技南十二路58996989",
|
||||||
|
drawer: "开票人",
|
||||||
|
billDetail: [
|
||||||
|
{
|
||||||
|
amount: 11300,
|
||||||
|
detailId: "1194121661989796666",
|
||||||
|
goodsName: "蜂胶口腔膜",
|
||||||
|
lineProperty: 2,
|
||||||
|
price: 11300,
|
||||||
|
quantity: 1,
|
||||||
|
revenueCode: "1070304990000000000",
|
||||||
|
taxRate: "0.13",
|
||||||
|
units: "个"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
billNo: "SRM_20250820002",
|
||||||
|
billDate: "2025-08-20",
|
||||||
|
buyerName: "深圳电子有限公司",
|
||||||
|
buyerTaxpayerId: "91440300123456789C",
|
||||||
|
sellerName: "云南交投集团经营开发有限公司彩云驿商业管理分公司",
|
||||||
|
sellerTaxpayerId: "91530112MA7MQ2JR9U",
|
||||||
|
totalAmount: 1.13,
|
||||||
|
includeTaxFlag: "1",
|
||||||
|
buyerProperty: "0",
|
||||||
|
sellerBankAndAccount: "",
|
||||||
|
sellerAddressAndTel: "",
|
||||||
|
pushMatchRules: "3",
|
||||||
|
drawer: "",
|
||||||
|
invoiceProperty: "0",
|
||||||
|
autoInvoice: "0",
|
||||||
|
invoiceType: "08xdp",
|
||||||
|
buyerRecipientPhone: "18158132615",
|
||||||
|
buyerRecipientMail: "",
|
||||||
|
remark: "设备采购款",
|
||||||
|
billDetail: [
|
||||||
|
{
|
||||||
|
amount: 1,
|
||||||
|
detailId: "1194121661989796666",
|
||||||
|
goodsName: "蜂胶口腔膜",
|
||||||
|
lineProperty: 1,
|
||||||
|
price: 11300,
|
||||||
|
quantity: 1,
|
||||||
|
revenueCode: "1070304990000000000",
|
||||||
|
taxRate: "0.13",
|
||||||
|
units: "个"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
billNo: "SRM_20250820003",
|
||||||
|
billDate: "2025-08-19",
|
||||||
|
buyerName: "杭州互联网有限公司",
|
||||||
|
buyerTaxpayerId: "91330100123456789E",
|
||||||
|
sellerName: "云南交投集团经营开发有限公司彩云驿商业管理分公司",
|
||||||
|
sellerTaxpayerId: "91530112MA7MQ2JR9U",
|
||||||
|
totalAmount: 1.13,
|
||||||
|
includeTaxFlag: "1",
|
||||||
|
buyerProperty: "0",
|
||||||
|
sellerBankAndAccount: "",
|
||||||
|
pushMatchRules: "3",
|
||||||
|
sellerAddressAndTel: "",
|
||||||
|
drawer: "",
|
||||||
|
invoiceProperty: "0",
|
||||||
|
autoInvoice: "1",
|
||||||
|
invoiceType: "08xdp",
|
||||||
|
buyerRecipientPhone: "18158132615",
|
||||||
|
buyerRecipientMail: "",
|
||||||
|
remark: "技术咨询服务",
|
||||||
|
billDetail: [
|
||||||
|
{
|
||||||
|
amount: 1,
|
||||||
|
detailId: "1194121661989796666",
|
||||||
|
goodsName: "蜂胶口腔膜",
|
||||||
|
lineProperty: 2,
|
||||||
|
price: 1,
|
||||||
|
quantity: 1,
|
||||||
|
revenueCode: "1070304990000000000",
|
||||||
|
taxRate: "0.13",
|
||||||
|
units: "个"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
billNo: "SRM_20250820004",
|
||||||
|
billDate: "2025-08-18",
|
||||||
|
buyerName: "成都科技有限公司",
|
||||||
|
buyerTaxpayerId: "91510100123456789G",
|
||||||
|
sellerName: "云南交投集团经营开发有限公司彩云驿商业管理分公司",
|
||||||
|
sellerTaxpayerId: "91530112MA7MQ2JR9U",
|
||||||
|
totalAmount: 1.13,
|
||||||
|
includeTaxFlag: "1",
|
||||||
|
buyerProperty: "0",
|
||||||
|
sellerBankAndAccount: "",
|
||||||
|
sellerAddressAndTel: "",
|
||||||
|
pushMatchRules: "3",
|
||||||
|
drawer: "",
|
||||||
|
invoiceProperty: "1",
|
||||||
|
autoInvoice: "0",
|
||||||
|
invoiceType: "08xdp",
|
||||||
|
buyerRecipientPhone: "18158132615",
|
||||||
|
buyerRecipientMail: "",
|
||||||
|
remark: "物流服务费红冲",
|
||||||
|
billDetail: [
|
||||||
|
{
|
||||||
|
amount: 1,
|
||||||
|
detailId: "1194121661989796666",
|
||||||
|
goodsName: "蜂胶口腔膜",
|
||||||
|
lineProperty: 1,
|
||||||
|
price: 11300,
|
||||||
|
quantity: 1,
|
||||||
|
revenueCode: "1070304990000000000",
|
||||||
|
taxRate: "0.13",
|
||||||
|
units: "个"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
billNo: "SRM_20250820005",
|
||||||
|
billDate: "2025-08-17",
|
||||||
|
buyerName: "西安制造有限公司",
|
||||||
|
buyerTaxpayerId: "91610100123456789I",
|
||||||
|
sellerName: "云南交投集团经营开发有限公司彩云驿商业管理分公司",
|
||||||
|
sellerTaxpayerId: "91530112MA7MQ2JR9U",
|
||||||
|
totalAmount: 1.13,
|
||||||
|
includeTaxFlag: "1",
|
||||||
|
buyerProperty: "0",
|
||||||
|
sellerBankAndAccount: "",
|
||||||
|
sellerAddressAndTel: "",
|
||||||
|
pushMatchRules: "3",
|
||||||
|
drawer: "",
|
||||||
|
invoiceProperty: "0",
|
||||||
|
autoInvoice: "1",
|
||||||
|
invoiceType: "08xdp",
|
||||||
|
buyerRecipientPhone: "18158132615",
|
||||||
|
buyerRecipientMail: "",
|
||||||
|
remark: "原材料采购",
|
||||||
|
billDetail: [
|
||||||
|
{
|
||||||
|
amount: 1,
|
||||||
|
detailId: "1194121661989796666",
|
||||||
|
goodsName: "蜂胶口腔膜",
|
||||||
|
lineProperty: 2,
|
||||||
|
price: 1,
|
||||||
|
quantity: 1,
|
||||||
|
revenueCode: "1070304990000000000",
|
||||||
|
taxRate: "0.13",
|
||||||
|
units: "个"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: mockData,
|
||||||
|
success: true,
|
||||||
|
total: mockData.length
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default connect(({ user }: ConnectState) => ({
|
||||||
|
currentUser: user.data
|
||||||
|
}))(DigitalElectronics);
|
||||||
44
src/pages/DigitalElectronics/service.ts
Normal file
44
src/pages/DigitalElectronics/service.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import requestKaiPiao from "@/utils/requestKaiPiao"
|
||||||
|
|
||||||
|
|
||||||
|
// 获取app_token
|
||||||
|
export async function handleGetAppToken(params?: any) {
|
||||||
|
const data = await requestKaiPiao.post('/api/getAppToken.do', params)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 token
|
||||||
|
export async function handleGetSDToken(params?: any) {
|
||||||
|
const data = await requestKaiPiao.post('/api/login.do', params)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开票申请
|
||||||
|
export async function handleGetKaiPiao(bodyData: any, accessToken: string) {
|
||||||
|
const data = await requestKaiPiao.post('/kapi/app/sim/openApi', bodyData, {
|
||||||
|
params: {
|
||||||
|
access_token: accessToken
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'access_token': accessToken
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 开票申请单发票查询
|
||||||
|
export async function handleGetInvoiceSearch(bodyData: any, accessToken: string) {
|
||||||
|
const data = await requestKaiPiao.post('/kapi/app/sim/openApi', bodyData, {
|
||||||
|
params: {
|
||||||
|
access_token: accessToken
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'access_token': accessToken
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
2
src/pages/InvoiceSearch/index.tsx
Normal file
2
src/pages/InvoiceSearch/index.tsx
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// 开票查询
|
||||||
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
// 数电的开票
|
|
||||||
import ProTable, { ActionType } from "@ant-design/pro-table";
|
|
||||||
import { FormInstance } from "antd";
|
|
||||||
import { useRef } from "react";
|
|
||||||
import { connect } from "umi";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const DigitalElectronics: React.FC<{ currentUser: any }> = (props) => {
|
|
||||||
const actionRef = useRef<ActionType>();
|
|
||||||
const formRef = useRef<FormInstance>();
|
|
||||||
|
|
||||||
const columns: any = []
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<ProTable
|
|
||||||
actionRef={actionRef}
|
|
||||||
formRef={formRef}
|
|
||||||
columns={columns}
|
|
||||||
bordered
|
|
||||||
expandable={{
|
|
||||||
expandRowByClick: true
|
|
||||||
}}
|
|
||||||
rowKey={(record) => {
|
|
||||||
return `${record?.id}`
|
|
||||||
}}
|
|
||||||
scroll={{ x: "100%", y: 'calc(100vh - 400px)' }}
|
|
||||||
headerTitle={<span style={{ color: "#1890ff", fontSize: 14, fontWeight: 600 }}>待开票列表</span>}
|
|
||||||
search={{ span: 6 }}
|
|
||||||
request={async (params: any) => {
|
|
||||||
|
|
||||||
if (tableData.list && tableData.list.length > 0) {
|
|
||||||
return {
|
|
||||||
data: tableData.list,
|
|
||||||
success: true,
|
|
||||||
total: tableData.total, // 总数据条数
|
|
||||||
current: tableData.pageNo, // 当前页码
|
|
||||||
pageSize: tableData.pageSize, // 每页条数
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { data: [], success: true }
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default connect(({ user }: ConnectState) => ({
|
|
||||||
currentUser: user.data
|
|
||||||
}))(DigitalElectronics);
|
|
||||||
@ -1,6 +1,8 @@
|
|||||||
const authority: PageAuthority = {
|
const authority: PageAuthority = {
|
||||||
'/test/index': ['/test/index'],
|
'/test/index': ['/test/index'],
|
||||||
'/Invoicing/index': ['/Invoicing/index'],
|
'/Invoicing/index': ['/Invoicing/index'],
|
||||||
|
'/DigitalElectronics/index': ['/DigitalElectronics/index'],
|
||||||
|
'/InvoiceSearch/index': ['/InvoiceSearch/index'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
148
src/types/invoice.ts
Normal file
148
src/types/invoice.ts
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
// 开票相关接口定义
|
||||||
|
export interface ApifoxModel {
|
||||||
|
/**
|
||||||
|
* 来源系统;,对接不同系统来源编码,用于区分不同系统的回调接口;例如SRM系统,你可以传入SRM
|
||||||
|
*/
|
||||||
|
businessSystemCode: string;
|
||||||
|
/**
|
||||||
|
* 开票请求参数,需要把data的值加密成字符串,默认为base64
|
||||||
|
*/
|
||||||
|
data: Datum[];
|
||||||
|
/**
|
||||||
|
* 接口业务编码,接口业务编码,使用BILL.PUSH
|
||||||
|
*/
|
||||||
|
interfaceCode: InterfaceCode;
|
||||||
|
/**
|
||||||
|
* 时间戳,请求唯一ID,可使用时间戳或UUID如:1624601109096
|
||||||
|
*/
|
||||||
|
requestId: string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Datum {
|
||||||
|
/**
|
||||||
|
* 数电账号,[系统]数电账号,数电票时可以传入,非数电传入会清空,发票云版本6.0.2可用
|
||||||
|
*/
|
||||||
|
account?: string;
|
||||||
|
/**
|
||||||
|
* 申请方,[红字]0:购方申请-发票已抵扣; 1:购方申请-发票未抵扣;2:销方申请; 默认为销方申请【长度:10】
|
||||||
|
*/
|
||||||
|
applicant?: string;
|
||||||
|
/**
|
||||||
|
* 自动开票标记,[系统]自动开票,0-否,1-是,默认0
|
||||||
|
*/
|
||||||
|
autoInvoice?: string;
|
||||||
|
/**
|
||||||
|
* 自动合并标记,[系统]自动合并 0-否,1-是,自动开票为1时生效
|
||||||
|
*/
|
||||||
|
autoMerge?: string;
|
||||||
|
/**
|
||||||
|
* 单据日期,格式:yyyy-MM-dd);如果为空,则默认为当天日期
|
||||||
|
*/
|
||||||
|
billDate?: string;
|
||||||
|
/**
|
||||||
|
* 单据明细,单据明细
|
||||||
|
*/
|
||||||
|
billDetail?: BillDetail[];
|
||||||
|
/**
|
||||||
|
* 整单折扣金额,整单折扣金额
|
||||||
|
*/
|
||||||
|
billDiscountAmount?: number;
|
||||||
|
/**
|
||||||
|
* 单据编号,参数限制:数字、英文、下划线、-如:Srm_20250518001
|
||||||
|
*/
|
||||||
|
billNo: string;
|
||||||
|
/**
|
||||||
|
* 购买方名称,[购方]根据购方名称匹配税号、地址电话、开户行账号(如有),不传且需自动开票时,提示:购方名称为空,请补充,不传且不自动开票时,保存为开票申请单
|
||||||
|
*/
|
||||||
|
buyerName?: string;
|
||||||
|
/**
|
||||||
|
* 购方税号,[购方](专票必填) 当购买方性质为企业填写税号,当购方性质为个人填写身份证
|
||||||
|
*/
|
||||||
|
buyerTaxpayerId?: string;
|
||||||
|
/**
|
||||||
|
* 含税标识,含税标识,0-不含税,1-含税【长度:1】,默认为0
|
||||||
|
*/
|
||||||
|
includeTaxFlag?: string;
|
||||||
|
/**
|
||||||
|
* 发票性质,0:蓝票;1:红票。不填默认蓝票
|
||||||
|
*/
|
||||||
|
invoiceProperty?: string;
|
||||||
|
/**
|
||||||
|
* 发票种类,未配置票种管理策略时必填枚举值
|
||||||
|
*/
|
||||||
|
invoiceType?: string;
|
||||||
|
/**
|
||||||
|
* 销方名称,[销方]销方名称,销方税号或销方二选一必填,两者都填,以销方税号为准
|
||||||
|
*/
|
||||||
|
sellerName?: string;
|
||||||
|
/**
|
||||||
|
* 销方税号,[销方]销方税号。组织编号、销方名称/税号三选一必传 (1)传组织编号,销方信息未传时,根据组织编号找销方信息。 (2)销方税号/销方二选一,两者都填,以销方税号为准。
|
||||||
|
*/
|
||||||
|
sellerTaxpayerId?: string;
|
||||||
|
/**
|
||||||
|
* 备注,GBK编码230字节【长度:230】
|
||||||
|
*/
|
||||||
|
remark?: string;
|
||||||
|
/**
|
||||||
|
* 单据金额,单据金额,根据含税标记区分,含税标志是不含税时为不含税金额,含税是含税金额,不传使用明细金额累加。
|
||||||
|
*/
|
||||||
|
totalAmount?: number;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BillDetail {
|
||||||
|
/**
|
||||||
|
* 明细金额,明细金额,含税标识includeTaxFlag=1时该金额为含税金额
|
||||||
|
*/
|
||||||
|
amount: number;
|
||||||
|
/**
|
||||||
|
* 业务系统明细id,业务系统明细id,用于反写回原业务系统明细 【长度:50】
|
||||||
|
*/
|
||||||
|
detailId: string;
|
||||||
|
/**
|
||||||
|
* 商品编码,商品编码,与商品名称2填1,需提前在系统维护
|
||||||
|
*/
|
||||||
|
goodsCode?: string;
|
||||||
|
/**
|
||||||
|
* 商品名称,商品名称,与商品编码2填1,需提前在系统维护【长度:GBK92字节】
|
||||||
|
*/
|
||||||
|
goodsName?: string;
|
||||||
|
/**
|
||||||
|
* 行性质,行性质,1折扣行(折扣行必须紧跟被折扣的正常商品行),2正常商品行
|
||||||
|
*/
|
||||||
|
lineProperty: string;
|
||||||
|
/**
|
||||||
|
* 单价,单价【长度:整数位加小数位最大16位,小数位最多13,小数点占1位】,price有值则quantity必填,含税标识includeTaxFlag=1时该金额为含税金额
|
||||||
|
*/
|
||||||
|
price?: string;
|
||||||
|
/**
|
||||||
|
* 数量,数量【长度:整数位加小数位最大16位,小数位最多13,小数点占1位】,quantity有值则price必填
|
||||||
|
*/
|
||||||
|
quantity?: string;
|
||||||
|
/**
|
||||||
|
* 税收分类编码,税收分类编码,开票项补全或映射会补全税收分类编码,未走开票项补全或映射必填
|
||||||
|
*/
|
||||||
|
revenueCode?: string;
|
||||||
|
/**
|
||||||
|
* 规格型号,规格型号,【长度:GBK40字节】
|
||||||
|
*/
|
||||||
|
specification?: string;
|
||||||
|
/**
|
||||||
|
* 税额,税额【长度:(14,2)】,可不传,系统自动计算
|
||||||
|
*/
|
||||||
|
taxAmount?: string;
|
||||||
|
/**
|
||||||
|
* 税率,税率,【0.13,0.09,0.06等】。不传时根据开票项匹配税率
|
||||||
|
*/
|
||||||
|
taxRate?: string;
|
||||||
|
/**
|
||||||
|
* 计量单位,计量单位,【长度:GBK22字节】
|
||||||
|
*/
|
||||||
|
units?: string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum InterfaceCode {
|
||||||
|
BillPush = "BILL.PUSH",
|
||||||
|
}
|
||||||
162
src/utils/requestKaiPiao.ts
Normal file
162
src/utils/requestKaiPiao.ts
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import { getDvaApp } from 'umi';
|
||||||
|
import { notification } from 'antd';
|
||||||
|
import type { AxiosRequestHeaders } from 'axios/index';
|
||||||
|
import CryptoJS from "crypto-js";
|
||||||
|
|
||||||
|
const { UMI_APP_BASEURL } = process.env;
|
||||||
|
|
||||||
|
// const instance = axios.create({ baseURL: UMI_APP_BASEURL });
|
||||||
|
// const instance = axios.create({ baseURL: 'https://api.eshangtech.com/EShangApiMain' });
|
||||||
|
|
||||||
|
|
||||||
|
// const instance = axios.create({ baseURL: 'http://home.robot-z.cn:7001/' });
|
||||||
|
// 修改baseURL为完整的API地址,确保在生产环境中正确访问
|
||||||
|
// 从直接访问外部URL
|
||||||
|
// const instance = axios.create({ baseURL: 'https://cosmic-sandbox.piaozone.com/jdpjykjyxgs' });
|
||||||
|
|
||||||
|
// 改为使用代理路径
|
||||||
|
const instance = axios.create({ baseURL: '/kaipiao' });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
instance.interceptors.request.use(
|
||||||
|
(config) => {
|
||||||
|
// 对data数据进行加密
|
||||||
|
// if (config.data) {
|
||||||
|
// config.data = preprocessData(JSON.stringify(config.data)); // 调用预处理函数
|
||||||
|
// }
|
||||||
|
console.log('config', config);
|
||||||
|
|
||||||
|
const isUpload = config.url?.includes("/oss/upload");
|
||||||
|
|
||||||
|
config.headers = {
|
||||||
|
...config.headers,
|
||||||
|
Authorization: `Bearer ${localStorage.getItem('Authorization') || ''}`,
|
||||||
|
"Content-Type": isUpload ? "multipart/form-data" : "application/json;charset=utf-8",
|
||||||
|
} as AxiosRequestHeaders;
|
||||||
|
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
(error) => Promise.reject(error),
|
||||||
|
);
|
||||||
|
|
||||||
|
instance.interceptors.response.use(
|
||||||
|
//状态码为2xx的时候执行
|
||||||
|
(response) => {
|
||||||
|
const { data } = response;
|
||||||
|
|
||||||
|
// if (data.code !== 200) {
|
||||||
|
// notification.error({
|
||||||
|
// message: data.message,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
const timestamp = getFormattedDate()
|
||||||
|
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
//状态码不是2xx的时候执行
|
||||||
|
(error) => {
|
||||||
|
const { response } = error;
|
||||||
|
|
||||||
|
if (response && response.status === 401) {
|
||||||
|
// // 清除本地存储的token
|
||||||
|
// localStorage.removeItem('Authorization');
|
||||||
|
// // 重定向到登录页
|
||||||
|
// window.location.href = '/user/login';
|
||||||
|
// notification.error({
|
||||||
|
// message: response?.data?.message || '请求失败',
|
||||||
|
// description: error.message
|
||||||
|
// });
|
||||||
|
} else {
|
||||||
|
notification.error({
|
||||||
|
message: response?.data?.message || '请求失败',
|
||||||
|
description: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject({
|
||||||
|
code: response?.status || 500,
|
||||||
|
message: response?.data?.message || '请求失败'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// 加密
|
||||||
|
const encryptAESECB = (data: string, key: string) => {
|
||||||
|
// const cipher = CryptoJS.createCipheriv('aes-128-ecb', key, null); // ECB 模式不需要 IV
|
||||||
|
const newKey = CryptoJS.enc.Utf8.parse(key); // 密钥必须是 16 字节
|
||||||
|
const cipher = CryptoJS.AES.encrypt(data, newKey, {
|
||||||
|
mode: CryptoJS.mode.ECB,
|
||||||
|
padding: CryptoJS.pad.Pkcs7
|
||||||
|
});
|
||||||
|
let encrypted = cipher.ciphertext.toString(CryptoJS.enc.Hex);
|
||||||
|
// let encrypted = cipher.update(data, 'utf8', 'hex');
|
||||||
|
// encrypted += cipher.final('hex');
|
||||||
|
return encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
const decryptAESECB = (data: string, key: string) => {
|
||||||
|
// const decipher = CryptoJS.createDecipheriv('aes-128-ecb', key, null);
|
||||||
|
// let decrypted = decipher.update(data, 'hex', 'utf8');
|
||||||
|
// decrypted += decipher.final('utf8');
|
||||||
|
const newKey = CryptoJS.enc.Utf8.parse(key);
|
||||||
|
|
||||||
|
const encryptedData = CryptoJS.enc.Hex.parse(data);
|
||||||
|
|
||||||
|
// 解密操作
|
||||||
|
const decrypted = CryptoJS.AES.decrypt({ ciphertext: encryptedData }, newKey, {
|
||||||
|
mode: CryptoJS.mode.ECB, // ECB 模式
|
||||||
|
padding: CryptoJS.pad.Pkcs7 // PKCS7 填充方式
|
||||||
|
});
|
||||||
|
// 将解密后的结果转为 UTF-8 字符串
|
||||||
|
const decryptedText = decrypted.toString(CryptoJS.enc.Utf8);
|
||||||
|
return decryptedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
// md5 签名
|
||||||
|
const md5 = (key: string, data: string, timestamp: string) => {
|
||||||
|
const text = "s" + key + data + timestamp;
|
||||||
|
return CryptoJS.MD5(text).toString(CryptoJS.enc.Hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成签名戳
|
||||||
|
const getFormattedDate = () => {
|
||||||
|
const date = new Date();
|
||||||
|
const year = date.getFullYear(); // 获取年份 (yyyy)
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0'); // 获取月份 (MM)
|
||||||
|
const day = String(date.getDate()).padStart(2, '0'); // 获取日期 (dd)
|
||||||
|
const hours = String(date.getHours()).padStart(2, '0'); // 获取小时 (HH)
|
||||||
|
return `es0${year}${month}${day}${hours}0es`; // 拼接成 yyyyMMddHH 格式
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密方法
|
||||||
|
const preprocessData = (data: string) => {
|
||||||
|
console.log('data', data);
|
||||||
|
// YYYYMMDD
|
||||||
|
let timestamp = getFormattedDate()
|
||||||
|
console.log('timestamp', timestamp);
|
||||||
|
// 秒为单位的时间戳
|
||||||
|
let timeSecond = parseInt((new Date().getTime() / 1000).toString())
|
||||||
|
console.log('timeSecond', timeSecond);
|
||||||
|
// 数据的加密
|
||||||
|
let encryptionData = encryptAESECB(data, timestamp)
|
||||||
|
console.log('encryptionData', encryptionData);
|
||||||
|
// md5签名方法
|
||||||
|
let md5Data = md5(timestamp, encryptionData, timestamp)
|
||||||
|
console.log('md5Data', md5Data);
|
||||||
|
|
||||||
|
let res = {
|
||||||
|
data: encryptionData,
|
||||||
|
timestamp: timeSecond,
|
||||||
|
sign: md5Data
|
||||||
|
}
|
||||||
|
console.log('res', res);
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export default instance;
|
||||||
Loading…
x
Reference in New Issue
Block a user