Commit 1d7099e8 authored by FE-安焕焕's avatar FE-安焕焕 👣

Merge branch 'gyl' into 'master'

Gyl

See merge request !6
parents 18119c04 27f2888f
/lambda/
/scripts
/config
.history
\ No newline at end of file
.history
/src/utils/qiniu.min.js
......@@ -117,6 +117,11 @@ export default {
name: 'reconciliationDetail',
component: './reconciliation/detail/index',
},
{
path: '/settleManage',
name: 'settleManage',
component: './settleManage',
},
{
component: './404',
},
......
const isProduction = process.env.NODE_ENV === 'production';
let envAPi = {
api: '//backstms-vcc3.liangkebang.net',
// api: '//backstms-vcc3.liangkebang.net',
api: '//backstms-vcc2.liangkebang.net',
// kdspApi: '//yapi.quantgroups.com/mock/351',
// kdspApi: 'http://192.168.28.172:8042',
kdspApi: 'https://kdsp-operation-vcc3.liangkebang.net',
// kdspApi: 'https://kdsp-operation-vcc3.liangkebang.net',
kdspApi: 'https://kdsp-operation-vcc2.liangkebang.net',
qiniuHost: 'https://appsync.lkbang.net',
opapiHost: 'https://opapi-vcc2.liangkebang.net',
};
let prodApi = {
api: '//backstms.q-gp.com',
kdspApi: '//kdsp-operation.q-gp.com',
qiniuHost: 'https://appsync.lkbang.net',
opapiHost: 'https://opapi.xyqb.com',
};
let exportApi;
......
......@@ -28,7 +28,15 @@ ul,
ol {
list-style: none;
}
.mr20 {
margin-right: 20px;
}
.mr10 {
margin-right: 10px;
}
.mt10 {
margin-top: 10px;
}
@media (max-width: @screen-xs) {
.ant-table {
width: 100%;
......
import { Upload, Icon, Modal, message } from 'antd';
import React from 'react';
import config from '../../../config/env.config';
import { qiniuToken } from '@/services/qiniu';
const qiniu = require('@/utils/qiniu.min.js');
const { qiniuHost } = config;
function getBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
let token = null;
class PicturesWall extends React.Component {
state = {
previewVisible: false,
previewImage: '',
fileList: [],
};
async componentDidMount() {
token = await qiniuToken();
}
handleCancel = () => this.setState({ previewVisible: false });
handlePreview = async file => {
const fileC = file;
if (!file.url && !file.preview) {
fileC.preview = await getBase64(file.originFileObj);
}
this.setState({
previewImage: fileC.url || fileC.preview,
previewVisible: true,
});
};
handleChange = info => {
const lastFile = (info.fileList.length && info.fileList[info.fileList.length - 1]) || [];
const types = ['application/pdf', 'image/png', 'image/jpeg'];
if (lastFile && types.indexOf(lastFile.type) === -1) {
message.error('文件格式错误!');
}
const fileList = info.fileList.filter(item => types.indexOf(item.type) !== -1);
this.setState({ fileList });
};
customRequest = ({ file, onError, onSuccess }) => {
const observable = qiniu.upload(file, null, token);
const observer = {
next() {
// ...
},
error() {
onError(file);
// ...
},
complete(res) {
const comFile = file;
const url = `${qiniuHost}/${res.hash}`;
comFile.url = url;
onSuccess(comFile);
// ...
},
};
observable.subscribe(observer); // 上传开始
};
getFileList = () => {
const fileList = this.state.fileList.map(item => item.response?.url);
return fileList;
};
clearFileList = () => {
this.setState({
fileList: [],
});
};
render() {
const { previewVisible, previewImage, fileList } = this.state;
console.log('fileList---render', fileList);
const uploadButton = (
<div>
<Icon type="plus" />
<div className="ant-upload-text">上传图片</div>
</div>
);
const { max } = this.props;
return (
<div className="clearfix">
<Upload
customRequest={this.customRequest}
listType="picture-card"
fileList={fileList}
onPreview={this.handlePreview}
onChange={this.handleChange}
{...this.props}
>
{max && fileList.length >= max ? null : uploadButton}
</Upload>
<Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
</div>
);
}
}
export default PicturesWall;
......@@ -186,15 +186,10 @@ const TableList = props => {
},
{
title: '订单状态',
dataIndex: 'orderStatus',
key: 'orderStatus',
dataIndex: 'orderStatusDesc',
key: 'orderStatusDesc',
width: 120,
hideInSearch: true,
valueEnum: {
12: '待发货',
13: '待收货',
21: '订单完成',
},
},
{
title: '订单开始时间',
......
export const settleStatus = {
0: { text: '全部' },
1: { text: '待提交' },
2: { text: '待商家确认' },
3: { text: '商家已确认' },
4: { text: '商家已拒绝' },
5: { text: '财务审核中' },
6: { text: '待付款' },
7: { text: '已付款' },
8: { text: '驳回' },
};
export const actionStatus = {
1: { text: '待操作' },
2: { text: '已确认' },
3: { text: '已拒绝' },
};
import { Modal, Button, Table } from 'antd';
import React, { Component } from 'react';
import styles from '../style.less';
class fileModal extends Component {
state = {
imgModal: false,
selectImg: '',
};
render() {
const { visible, fileData = [] } = this.props;
const imgData = fileData.filter(item => item.docType === 2);
const tableData = fileData.filter(item => item.docType === 1);
const tableColumns = [
{
title: '发票号',
dataIndex: 'invoiceNo',
},
{
title: '物流单号',
dataIndex: 'deliveryNo',
},
];
return (
<Modal
title="附件详情"
visible={visible}
footer={null}
onCancel={this.props.onCancel}
width="700px"
>
<div className={styles.imgWrap}>
{imgData.map(item => (
<img
onClick={() => {
this.setState({ imgModal: true, selectImg: item.downloadUrl });
}}
key={item.id}
width={200}
alt=""
src={item.downloadUrl}
style={{ margin: 5 }}
></img>
))}
</div>
<Table
pagination={{ pageSize: 5 }}
dataSource={tableData}
columns={tableColumns}
rowKey={record => record.id}
scroll={{ x: '100%', y: 300 }}
/>
<Button type="primary" onClick={this.props.onCancel} className={styles.fileBtn}>
关闭
</Button>
<Modal
title="附件详情"
visible={this.state.imgModal}
footer={null}
onCancel={() => this.setState({ imgModal: false })}
width="800px"
>
<img width="700px" src={this.state.selectImg} alt=""></img>
</Modal>
</Modal>
);
}
}
export default fileModal;
import React, { useState, useRef } from 'react';
import ProTable from '@ant-design/pro-table';
import { Button, Popconfirm, notification } from 'antd';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { actionStatus, settleStatus } from './data';
import style from './style.less';
import { query, updateStatus, download, docQuery } from './service';
import FileModal from './fileModal';
import UploadModal from './uploadModal';
import RemarkModal from './remarkModal';
export default () => {
const protable = useRef();
const remarkRef = useRef();
const [showViewFile, setShowViewFile] = useState(false);
const [viewFileData, setViewFileData] = useState([]);
const [showViewUpload, setShowViewUpload] = useState(false);
const [settlementNoV, setSettlementNo] = useState('');
const [remarkShow, setRemarkShow] = useState(false);
const [rowInfo, setRowInfo] = useState({});
const reload = () => {
if (protable?.current?.reload) {
protable.current.reload();
}
};
const confirmText = '您是否已确认结算金额是否正确?确认后无法撤回';
const confirmAction = async (r, isRefuse, refuse) => {
if (isRefuse && !refuse) {
notification.error({ message: '请输入拒绝原因' });
return;
}
const data = await updateStatus({
id: r.id,
status: isRefuse ? 4 : 3,
remarks: isRefuse && refuse,
});
if (data.businessCode === '0000') {
notification.success({ message: '操作成功' });
reload();
}
};
const uploadInvoice = ({ settlementNo }) => {
setSettlementNo(settlementNo);
setShowViewUpload(true);
};
const downDetail = ({ settlementNo }) => {
// type 1非财务 2财务
download({ settlementNo, type: 1 });
};
const clearRefuseContent = r => {
setRowInfo(r);
if (remarkRef.current.init) {
remarkRef.current.reset();
}
setRemarkShow(true);
};
const viewAccessory = async ({ settlementNo }) => {
const data = await docQuery(settlementNo);
setViewFileData(data);
setShowViewFile(true);
};
const tableRenderBtn = r => (
<div>
<p>
<Popconfirm
placement="topLeft"
title={confirmText}
onConfirm={() => confirmAction(r)}
okText="确认"
cancelText="取消"
disabled={r.settlementStatus !== 2}
>
<Button type="primary" className="mr10" disabled={r.settlementStatus !== 2}>
确认
</Button>
</Popconfirm>
<Button
type="primary"
onClick={() => clearRefuseContent(r)}
className="mr10"
disabled={r.settlementStatus !== 2}
>
拒绝
</Button>
<Button type="primary" onClick={() => uploadInvoice(r)} disabled={r.settlementStatus !== 6}>
上传发票
</Button>
</p>
<Button type="primary" onClick={() => downDetail(r)} className="mr20">
下载明细
</Button>
<Button
type="primary"
onClick={() => viewAccessory(r)}
disabled={!(r.settlementStatus === 6 && r.isUploadDoc)}
>
查看附件
</Button>
</div>
);
const columns = [
{
title: '排序',
dataIndex: 'index',
valueType: 'index',
width: 100,
},
{
title: '结算订单号',
dataIndex: 'settlementNo',
width: 200,
},
{
title: '结算周期',
dataIndex: 'settlementTime',
valueType: 'dateRange',
hideInTable: true,
width: 200,
},
{
title: '结算周期',
dataIndex: 'settlementDate',
hideInSearch: true,
width: 200,
},
{
title: '待结算金额',
dataIndex: 'settlementAmount',
hideInSearch: true,
width: 200,
},
{
title: '货款结算状态',
dataIndex: 'settlementStatus',
valueEnum: settleStatus,
width: 200,
},
{
title: '操作状态',
dataIndex: 'supplierOperateStatus',
valueEnum: actionStatus,
width: 200,
},
{
title: '原因',
dataIndex: 'remarktrgyurew',
hideInSearch: true,
width: 200,
},
{
title: '操作',
valueType: 'option',
width: 280,
fixed: 'right',
render: (_, r) => tableRenderBtn(r),
},
];
return (
<PageHeaderWrapper>
<ProTable
size="small"
columns={columns}
className={style.table}
request={query}
search={{ collapsed: false }}
rowKey="id"
pagination={{
defaultCurrent: 1,
}}
actionRef={protable}
scroll={{ x: '100%' }}
/>
<FileModal
visible={showViewFile}
fileData={viewFileData}
onCancel={() => {
setShowViewFile(false);
}}
/>
<UploadModal
visible={showViewUpload}
settlementNo={settlementNoV}
onCancel={() => {
setShowViewUpload(false);
reload();
}}
/>
<RemarkModal
visible={remarkShow}
submit={remark => {
confirmAction(rowInfo, true, remark);
}}
onCancel={() => setRemarkShow(false)}
ref={remarkRef}
/>
</PageHeaderWrapper>
);
};
import { Modal, Input } from 'antd';
import React, { Component } from 'react';
const { TextArea } = Input;
// eslint-disable-next-line react/prefer-stateless-function
class fileModal extends Component {
state = {
remarks: '',
};
inputChange = ({ target: { value } }) => {
this.setState({ remarks: value });
};
reset = () => {
this.setState({ remarks: '' });
};
render() {
const { visible } = this.props;
return (
<Modal
title="驳回原因"
visible={visible}
onOk={() => {
this.props.submit(this.state.remarks);
}}
onCancel={this.props.onCancel}
width="500px"
>
<div>
<TextArea value={this.state.remarks} rows={4} onChange={this.inputChange} />
</div>
</Modal>
);
}
}
export default fileModal;
import { stringify } from 'querystring';
import _ from 'lodash';
import request from '@/utils/request';
import { saveAs } from 'file-saver';
import { format } from 'date-fns';
import config from '../../../config/env.config';
// 分页查询
export async function query(data) {
const params = {
...data,
pageNo: data.current,
startDate: data.settlementTime?.[0],
endDate: data.settlementTime?.[1],
pageFlag: 4,
};
try {
const {
data: { current, records, total, size },
} = await request.get('/api/kdsp/settlement/wait-confirm/page/query', {
prefix: config.kdspApi,
params: _.omitBy(params, v => !v),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return {
current,
data: records.map(v => ({ ...v, logisticsStatus: `_${v.logisticsStatus}` })),
total,
pageSize: size,
};
} catch (error) {
return {};
}
}
export async function updateStatus(params) {
const data = await request.post('/api/kdsp/settlement/status/update', {
prefix: config.kdspApi,
data: stringify(_.omitBy(params, v => !v)),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return data;
}
export async function uploadFile(file, settlementNo) {
const params = new FormData();
params.append('file', file);
params.append('settlementNo', settlementNo);
const data = await request.post('/api/kdsp/settlement/doc/upload', {
data: params,
prefix: config.kdspApi,
});
return data;
}
export async function download(params) {
const data = await request.post('/api/kdsp/settlement/detail/download/v1', {
data: stringify(_.omitBy(params, v => !v)),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
prefix: config.kdspApi,
responseType: 'arrayBuffer',
});
const blob = new Blob([data]);
saveAs(blob, `商户货款结算明细-${format(new Date(), 'yyyyMMddHHmmss')}.xlsx`);
}
// 附件信息
export async function docQuery(settlementNo) {
const data = await request.get(`/api/kdsp/settlement/doc/query?settlementNo=${settlementNo}`, {
prefix: config.kdspApi,
});
if (data.businessCode === '0000') {
return data.data;
}
return [];
}
export async function uploadPic(params) {
const data = await request.post('/api/kdsp/settlement/invoice/img/upload', {
prefix: config.kdspApi,
data: stringify(_.omitBy(params, v => !v)),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return data;
}
.table {
:global {
.ant-table-wrapper {
margin: 20px;
}
}
}
.fileBtn {
display: block;
margin: 10px auto;
}
.imgWrap {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
max-height: 300px;
overflow: auto;
}
.tab {
min-height: 400px;
}
.down {
float: right;
margin-bottom: 10px;
text-decoration: 1px solid;
}
.clearfloat {
clear: both;
}
import React, { useRef, useState, useEffect } from 'react';
import { Modal, Button, Tabs, Upload, notification, Icon } from 'antd';
import styles from '../style.less';
import { uploadFile, uploadPic } from '../service';
import UploadC from '../../components/upload';
const { Dragger } = Upload;
const { TabPane } = Tabs;
export default props => {
const { visible, settlementNo } = props;
const [tabValue, setTabValue] = useState('1');
const [fileList, setFileList] = useState([]);
const picUpload = useRef();
const uploadProps = {
name: 'file',
beforeUpload(file) {
setFileList([file]);
return false;
},
accept: '.xlsx,.xls,.csv',
showUploadList: true,
maxCount: 1,
fileList,
onRemove() {
setFileList([]);
},
};
const initPicUpload = () => {
if (picUpload?.current?.clearFileList) {
picUpload.current.clearFileList();
}
};
useEffect(() => {
initPicUpload();
setFileList([]);
}, [visible]);
const uploadPicFn = async () => {
// 上传图片的接口
const upLoadFileList = picUpload?.current?.getFileList?.();
if (!upLoadFileList.length) {
notification.warning({ message: '请先上传图片' });
return;
}
const data = await uploadPic({
imgUrl: upLoadFileList.join(','),
settlementNo,
});
if (data.code === '0000') {
notification.success({ message: '上传成功' });
initPicUpload();
}
};
const uploadFileFn = async () => {
if (!fileList.length) {
notification.warning({ message: '请先上传附件' });
return;
}
const result = await uploadFile(fileList[0], settlementNo);
if (result.businessCode === '0000') {
notification.success({ message: '导入成功' });
setFileList([]);
}
};
const uploadFn = () => {
if (tabValue === '1') {
uploadPicFn();
} else {
uploadFileFn();
}
};
const changeTab = tabV => {
setTabValue(tabV);
};
return (
<Modal title="上传发票" visible={visible} footer={null} onCancel={props.onCancel} width="700px">
<Tabs defaultActiveKey="1" className={styles.tab} onChange={changeTab} activeKey={tabValue}>
<TabPane tab="上传凭证" key="1">
<UploadC ref={picUpload} />
</TabPane>
<TabPane tab="上传附件" key="2">
<a
href="https://kdspstatic.q-gp.com/%E5%8F%91%E7%A5%A8%E4%B8%8A%E4%BC%A0%E6%A8%A1%E6%9D%BF.xlsx"
className={styles.down}
>
模板下载
</a>
<Dragger {...uploadProps} className={styles.clearfloat}>
<p>
<Icon type="upload" />
</p>
<p>点击上传附件</p>
</Dragger>
</TabPane>
</Tabs>
<Button type="primary" onClick={uploadFn} className={styles.logBtn}>
{tabValue === '1' ? '确定上传图片' : '确定上传附件'}
</Button>
</Modal>
);
};
import request from '@/utils/request';
import config from '../../config/env.config';
export async function qiniuToken() {
const data = await request.get('/upload/getToken', {
prefix: config.opapiHost,
});
return data?.uptoken;
}
This diff is collapsed.
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 to comment