Browse Source

(*)回款和业绩明细数据导入功能; 解决冲突

master
wuqun 2 years ago
parent
commit
c38cfc95b3
  1. 5
      .vscode/launch.json
  2. 13
      api/app/lib/controllers/auth/index.js
  3. 23
      api/app/lib/controllers/customerContactsFollup/index.js
  4. 55
      api/app/lib/controllers/report/achievement.js
  5. 65
      api/app/lib/models/customerContactsFollup.js
  6. 8
      api/app/lib/routes/customerContactsFollup/index.js
  7. 5
      api/app/lib/routes/report/index.js
  8. 67
      web/client/src/layout/containers/layout/index.jsx
  9. 10
      web/client/src/sections/auth/actions/auth.js
  10. 25
      web/client/src/sections/business/actions/achievement-report.js
  11. 50
      web/client/src/sections/business/containers/performanceReport/contractDetails.jsx
  12. 53
      web/client/src/sections/business/containers/performanceReport/invoicingDetails.jsx
  13. 5
      web/client/src/utils/webapi.js
  14. 6
      web/config.js
  15. 3
      web/package.json
  16. 3
      web/routes/attachment/index.js

5
.vscode/launch.json

@ -43,9 +43,10 @@
// //
// //
"-g postgres://FashionAdmin:123456@10.8.30.36:5432/data_center", "-g postgres://FashionAdmin:123456@10.8.30.36:5432/data_center",
"--redisHost localhost", "--redisHost 10.8.30.112",
"--redisPort 6379", "--redisPort 6379",
"--apiEmisUrl http://10.8.30.161:1111", "--apiEmisUrl http://10.8.30.112:14000", //
// "--apiEmisUrl http://10.8.30.161:1111", //
// //
// //
"--qnak XuDgkao6cL0HidoMAPnA5OB10Mc_Ew08mpIfRJK5", "--qnak XuDgkao6cL0HidoMAPnA5OB10Mc_Ew08mpIfRJK5",

13
api/app/lib/controllers/auth/index.js

@ -9,9 +9,18 @@ async function login (ctx, next) {
try { try {
const params = ctx.request.body; const params = ctx.request.body;
const emisLoginRes = await ctx.app.fs.emisRequest.post('login', { let emisLoginRes = null
data: params if (params.username && params.password) {
emisLoginRes = await ctx.app.fs.emisRequest.post('login', {
data: { ...params, }
})
} else if (params.token) {
emisLoginRes = await ctx.app.fs.emisRequest.get('user-info', {
query: {
token: params.token,
}
}) })
}
if (!emisLoginRes) { if (!emisLoginRes) {
throw "无此用户,请使用正确的登录信息" throw "无此用户,请使用正确的登录信息"

23
api/app/lib/controllers/customerContactsFollup/index.js

@ -0,0 +1,23 @@
'use strict';
// 查询储备项目统计表
async function getCustomerContactsFollowup(ctx, next) {
const { type } = ctx.params;
let rslt = null;
try {
rslt = await ctx.fs.dc.models.ReserveItemReport.findAll({
order: [['id', 'DESC']],
// where: { type: type }
})
ctx.status = 200
ctx.body = rslt
} catch (error) {
ctx.fs.logger.error(`path:${ctx.path},error:${error}`)
ctx.status = 400;
ctx.body = { name: 'FindAllError', message: '获取失败' }
}
}
module.exports = {
getCustomerContactsFollowup
}

55
api/app/lib/controllers/report/achievement.js

@ -324,6 +324,59 @@ async function importAchieveDetails(ctx) {
} }
} }
/**
* 查询合同明细表数据
* @param {*} ctx ctx ctx.query:{keywordTarget-关键字项keyword-关键字内容limit-页宽, page-页码}
*/
async function getContractDetail(ctx) {
try {
const { models } = ctx.fs.dc;
const { keywordTarget, keyword, limit, page } = ctx.query;
const where = {};
if (keywordTarget && keyword) {
where[keywordTarget] = { $iLike: `%${keyword}%` };
}
let contractDetail = await models.ContractDetail.findAndCountAll({
where: where,
offset: Number(page) * Number(limit),
limit: Number(limit),
order: [['id', 'DESC']]
});
ctx.status = 200
ctx.body = contractDetail;
} catch (error) {
ctx.fs.logger.error(`path:${ctx.path},error:${error}`)
ctx.status = 400;
ctx.body = { name: 'FindError', message: '查询合同明细表数据失败' }
}
}
/**
* 查询开票明细表数据
* @param {*} ctx ctx ctx.query:{keywordTarget-关键字项keyword-关键字内容limit-页宽, page-页码}
*/
async function getInvoicingDetail(ctx) {
try {
const { models } = ctx.fs.dc;
const { keywordTarget, keyword, limit, page } = ctx.query;
const where = {};
if (keywordTarget && keyword) {
where[keywordTarget] = { $iLike: `%${keyword}%` };
}
let invoiceDetail = await models.InvoiceDetail.findAndCountAll({
where: where,
offset: Number(page) * Number(limit),
limit: Number(limit),
order: [['id', 'DESC']]
});
ctx.status = 200
ctx.body = invoiceDetail;
} catch (error) {
ctx.fs.logger.error(`path:${ctx.path},error:${error}`)
ctx.status = 400;
ctx.body = { name: 'FindError', message: '查询开票明细表数据失败' }
}
}
module.exports = { module.exports = {
getReceivedDetail,//回款 getReceivedDetail,//回款
getAchievementDetail,//业绩 getAchievementDetail,//业绩
@ -331,4 +384,6 @@ module.exports = {
getReceivedNumbers,//查询回款明细表 已有的所有编号 getReceivedNumbers,//查询回款明细表 已有的所有编号
importBackDetails,//导入回款明细 importBackDetails,//导入回款明细
importAchieveDetails,//导入业绩明细 importAchieveDetails,//导入业绩明细
getContractDetail,
getInvoicingDetail
} }

65
api/app/lib/models/customerContactsFollup.js

@ -0,0 +1,65 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const ReserveItemReport = sequelize.define("reserveItemReport", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
field: "id",
autoIncrement: true,
},
customer: {
type: DataTypes.STRING,
allowNull: true,
field: "customer",
},
items: {
type: DataTypes.STRING,
allowNull: true,
field: "items",
},
department: {
type: DataTypes.STRING,
allowNull: false,
field: "department",
},
sale: {
type: DataTypes.STRING,
allowNull: true,
field: "sale",
},
updatetime: {
type: DataTypes.DATE,
allowNull: false,
field: "updatetime",
},
customerContacts: {
type: DataTypes.STRING,
allowNull: true,
field: "customer_contacts",
},
phone: {
type: DataTypes.STRING,
allowNull: true,
field: "phone",
},
visitStyle: {
type: DataTypes.STRING,
allowNull: true,
field: "visit_style",
},
itemText: {
type: DataTypes.STRING,
allowNull: true,
field: "item_text",
}
}, {
tableName: "customer_contacts_followup",
});
dc.models.ReserveItemReport = ReserveItemReport;
return ReserveItemReport;
};

8
api/app/lib/routes/customerContactsFollup/index.js

@ -0,0 +1,8 @@
'use strict';
const report = require('../../controllers/customerContactsFollup');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/customerContactsFollup'] = { content: '客户联系人对接跟进', visible: false };
router.get('/customerContactsFollup', report.getReserveItemReport);
};

5
api/app/lib/routes/report/index.js

@ -27,4 +27,9 @@ module.exports = function (app, router, opts) {
app.fs.api.logAttr['POST/add/achievement/bulk'] = { content: '导入业绩明细', visible: true }; app.fs.api.logAttr['POST/add/achievement/bulk'] = { content: '导入业绩明细', visible: true };
router.post('/add/achievement/bulk', achieve.importAchieveDetails); router.post('/add/achievement/bulk', achieve.importAchieveDetails);
app.fs.api.logAttr['GET/contract/detail'] = { content: '查询合同明细表', visible: false };
router.get('/contract/detail', achieve.getContractDetail);
app.fs.api.logAttr['GET/invoicing/detail'] = { content: '查询开票明细表', visible: false };
router.get('/invoicing/detail', achieve.getInvoicingDetail);
}; };

67
web/client/src/layout/containers/layout/index.jsx

@ -10,6 +10,10 @@ import { resize } from '../../actions/global';
import * as NProgress from 'nprogress'; import * as NProgress from 'nprogress';
import PerfectScrollbar from 'perfect-scrollbar'; import PerfectScrollbar from 'perfect-scrollbar';
import { useLocation } from "react-router"; import { useLocation } from "react-router";
import { RouteTable } from '$utils';
import { RouteRequest } from '@peace/utils';
import Cookie from 'js-cookie';
import { login, LOGIN_SUCCESS } from '../../../sections/auth/actions/auth';
NProgress.configure({ NProgress.configure({
template: ` template: `
@ -23,11 +27,14 @@ NProgress.configure({
}); });
let scrollbar let scrollbar
let requestUser = true
let requestlogout = false;
// const location111 = useLocation(); // const location111 = useLocation();
const LayoutContainer = props => { const LayoutContainer = props => {
const { const {
dispatch, msg, user, copyright, children, sections, clientWidth, clientHeight, dispatch,actions, msg, user, copyright, children, sections, clientWidth, clientHeight,
location, match, routes, history, socket, location, match, routes, history, socket, apiRoot
} = props } = props
const [collapsed, setCollapsed] = useState(false) const [collapsed, setCollapsed] = useState(false)
@ -39,7 +46,7 @@ const LayoutContainer = props => {
document.getElementById('DrApp').clientWidth - (collapsed ? 120 : 240) document.getElementById('DrApp').clientWidth - (collapsed ? 120 : 240)
)); ));
} }
function deepCopy(data) {// function deepCopy (data) {//
//string,number,bool,null,undefined,symbol //string,number,bool,null,undefined,symbol
//object,array,date //object,array,date
if (data && typeof data === "object") { if (data && typeof data === "object") {
@ -165,7 +172,8 @@ const LayoutContainer = props => {
useEffect(() => { useEffect(() => {
NProgress.done(); NProgress.done();
if ((!user || !user.authorized)) { if ((!user || !user.authorized)) {
history.push('/signin'); // history.push('/signin');
getUserInfoByToken()
} }
if (msg) { if (msg) {
if (msg.done) { if (msg.done) {
@ -195,6 +203,52 @@ const LayoutContainer = props => {
} }
}) })
const getUserInfoByToken = () => {
if (requestUser) {
requestUser = false;
console.log(RouteTable.apiRoot);
RouteRequest.get(RouteTable.apiRoot).then(res => {
let token = Cookie.get('pepToken', { domain: res.domain });
console.log(token);
dispatch(login({ token })).then(res => {
console.log(res);
if (res.type == 'LOGIN_SUCCESS') {
const data = res.payload?.user || {}
history.push('/businessManagement/pmReport/reserveItemsReporting')
localStorage.setItem('poms_open_sider', JSON.stringify(["pmReport"]))
localStorage.setItem('poms_selected_sider', JSON.stringify(["businessManagement"]))
localStorage.setItem('word', JSON.stringify('')) //
dispatch(actions.layout.initWebSocket({ ioUrl: apiRoot, token: data.token, hrUserId: data.hrUserInfo && hrUserInfo.id }))
} else {
redirectToLogin(true);
}
}, error => {
redirectToLogin(true);
})
}, error => {
message.error('鉴权失败', 5);
redirectToLogin(true);
})
}
}
const redirectToLogin = (toLogin = false) => {
//
if (!requestlogout) {
requestlogout = true;//退redirectToLoginwindow.location.hrefmessage.warning
RouteRequest.get(RouteTable.getPepWebUrl).then(res => {
if (res.url) {
if (toLogin) {
window.location.href = `${res.url}/signin`;
}
else {
window.location.href = `${res.url}/signin?isLogout=true`;
}
}
});
}
}
// websocket 使 // websocket 使
useEffect(() => { useEffect(() => {
@ -307,7 +361,7 @@ const LayoutContainer = props => {
) )
} }
function mapStateToProps(state) { function mapStateToProps (state) {
const { global, auth, ajaxResponse, webSocket } = state; const { global, auth, ajaxResponse, webSocket } = state;
return { return {
title: global.title, title: global.title,
@ -318,7 +372,8 @@ function mapStateToProps(state) {
clientHeight: global.clientHeight, clientHeight: global.clientHeight,
msg: ajaxResponse.msg, msg: ajaxResponse.msg,
user: auth.user, user: auth.user,
socket: webSocket.socket socket: webSocket.socket,
apiRoot: global.apiRoot,
}; };
} }

10
web/client/src/sections/auth/actions/auth.js

@ -4,7 +4,7 @@ import { ApiTable, AxyRequest, EmisRequest } from '$utils'
import { Request } from '@peace/utils'; import { Request } from '@peace/utils';
export const INIT_AUTH = 'INIT_AUTH'; export const INIT_AUTH = 'INIT_AUTH';
export function initAuth(userData) { export function initAuth (userData) {
const sessionUser = JSON.parse(sessionStorage.getItem('dcUser')) const sessionUser = JSON.parse(sessionStorage.getItem('dcUser'))
const user = userData || sessionUser || {}; const user = userData || sessionUser || {};
if (user.authorized && !sessionUser) { if (user.authorized && !sessionUser) {
@ -21,11 +21,11 @@ export function initAuth(userData) {
export const REQUEST_LOGIN = 'REQUEST_LOGIN'; export const REQUEST_LOGIN = 'REQUEST_LOGIN';
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'; export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGIN_ERROR = 'LOGIN_ERROR'; export const LOGIN_ERROR = 'LOGIN_ERROR';
export function login(username, password) { export function login ({ username, password, token }) {
return dispatch => { return dispatch => {
dispatch({ type: REQUEST_LOGIN }); dispatch({ type: REQUEST_LOGIN });
if (!username || !password) { if ((!username || !password) && !token) {
dispatch({ dispatch({
type: LOGIN_ERROR, type: LOGIN_ERROR,
payload: { error: '请输入账号名和密码' } payload: { error: '请输入账号名和密码' }
@ -43,7 +43,7 @@ export function login(username, password) {
// }, // },
// }); // });
return Request.post(ApiTable.login, { username, password, code: 'HR' }) return Request.post(ApiTable.login, { username, password, token, code: 'HR' })
.then(user => { .then(user => {
sessionStorage.setItem('dcUser', JSON.stringify(user)); sessionStorage.setItem('dcUser', JSON.stringify(user));
return dispatch({ return dispatch({
@ -63,7 +63,7 @@ export function login(username, password) {
} }
export const LOGOUT = 'LOGOUT'; export const LOGOUT = 'LOGOUT';
export function logout() { export function logout () {
const user = JSON.parse(sessionStorage.getItem('dcUser')) const user = JSON.parse(sessionStorage.getItem('dcUser'))
user && user.token ? user && user.token ?
Request.put(ApiTable.logout, { Request.put(ApiTable.logout, {

25
web/client/src/sections/business/actions/achievement-report.js

@ -62,3 +62,28 @@ export function importAchieveDetails(values) {
msg: { option: '导入业绩明细' }, msg: { option: '导入业绩明细' },
}); });
} }
//查询合同明细表
export function getContractDetail(query) {
return (dispatch) => basicAction({
type: "get",
dispatch: dispatch,
actionType: "GET_CONTRACT_DETAIL",
query: query,
url: `${ApiTable.getContractDetail}`,
msg: { option: "查询合同明细表" },
reducer: { name: "ContractDetail", params: { noClear: true } },
});
}
//查询开票明细表
export function getInvoicingDetail(query) {
return (dispatch) => basicAction({
type: "get",
dispatch: dispatch,
actionType: "GET_INVOICING_DETAIL",
query: query,
url: `${ApiTable.getInvoicingDetail}`,
msg: { option: "查询开票明细表" },
reducer: { name: "InvoicingDetail", params: { noClear: true } },
});
}

50
web/client/src/sections/business/containers/performanceReport/contractDetails.jsx

@ -1,6 +1,6 @@
import React, { useEffect, useState, useRef, useMemo } from 'react'; import React, { useEffect, useState, useRef, useMemo } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Select, Input, Button, Table, Pagination, Skeleton } from '@douyinfe/semi-ui'; import { Select, Input, Button, Table, Pagination, Skeleton, Toast, } from '@douyinfe/semi-ui';
import { SkeletonScreen, Setup } from "$components"; import { SkeletonScreen, Setup } from "$components";
import { IconSearch } from '@douyinfe/semi-icons'; import { IconSearch } from '@douyinfe/semi-icons';
import ImportContractDetailsModal from './importContractDetailsModal'; import ImportContractDetailsModal from './importContractDetailsModal';
@ -10,7 +10,7 @@ import moment from 'moment'
const ContractDetails = (props) => { const ContractDetails = (props) => {
const { dispatch, actions } = props const { dispatch, actions } = props
const { } = actions; const { businessManagement } = actions;
const [keywordTarget, setKeywordTarget] = useState('contractNo'); const [keywordTarget, setKeywordTarget] = useState('contractNo');
const [keyword, setKeyword] = useState('');// const [keyword, setKeyword] = useState('');//
const [limits, setLimits] = useState()// const [limits, setLimits] = useState()//
@ -20,6 +20,11 @@ const ContractDetails = (props) => {
const [setup, setSetup] = useState(false);// const [setup, setSetup] = useState(false);//
const [setupp, setSetupp] = useState([]);// const [setupp, setSetupp] = useState([]);//
const [exportUrl, setExportUrl] = useState('');
const page = useRef(query.page);
const scroll = useMemo(() => ({}), []);
const CONTRACTDETAILS = "contractDetails"; const CONTRACTDETAILS = "contractDetails";
const renderColumns = (columnKeys) => { const renderColumns = (columnKeys) => {
let columns = []; let columns = [];
@ -52,6 +57,7 @@ const ContractDetails = (props) => {
) )
: ""; : "";
attribute(); attribute();
getContractDetailData();
}, []); }, []);
// //
function attribute() { function attribute() {
@ -66,9 +72,16 @@ const ContractDetails = (props) => {
} }
setSetupp(newColumns); setSetupp(newColumns);
} }
useEffect(() => {
}, [query]) function getContractDetailData(param) {
let queryParam = param || query;
dispatch(businessManagement.getContractDetail({ keywordTarget, keyword, ...queryParam })).then(r => {
if (r.success) {
setTableData(r.payload?.data?.rows);
setLimits(r.payload?.data?.count);
}
})
}
function handleRow(record, index) {// function handleRow(record, index) {//
if (index % 2 === 0) { if (index % 2 === 0) {
return { return {
@ -80,7 +93,22 @@ const ContractDetails = (props) => {
return {}; return {};
} }
} }
const scroll = useMemo(() => ({}), []);
const exportAllData = () => {
dispatch(businessManagement.getContractDetail({ limit: 1, page: 0 })).then((res) => {
if (res.success) {
if (res.payload.data.count) {
let url = `export/contract/detail?token=${user.token}&timestamp=${moment().valueOf()}`
setExportUrl(url);
} else {
Toast.info({
content: '暂无可导出的数据',
duration: 3,
})
}
}
})
}
return ( return (
<> <>
<div style={{ padding: '0px 12px' }}> <div style={{ padding: '0px 12px' }}>
@ -121,6 +149,7 @@ const ContractDetails = (props) => {
<Button theme='solid' type='primary' style={{ width: 80, borderRadius: 2, height: 32, background: '#DBECFF', color: '#005ABD' }} <Button theme='solid' type='primary' style={{ width: 80, borderRadius: 2, height: 32, background: '#DBECFF', color: '#005ABD' }}
onClick={() => { onClick={() => {
setQuery({ limit: 10, page: 0 }) setQuery({ limit: 10, page: 0 })
getContractDetailData({ limit: 10, page: 0 })
}}>查询</Button> }}>查询</Button>
</div> </div>
<div style={{ display: 'flex', marginRight: 20 }}> <div style={{ display: 'flex', marginRight: 20 }}>
@ -131,7 +160,10 @@ const ContractDetails = (props) => {
onClick={() => { setImportModalV(true); }}> onClick={() => { setImportModalV(true); }}>
导入 导入
</div> </div>
<div style={{ padding: '6px 20px', background: '#00BA85', color: '#FFFFFF', fontSize: 14, marginLeft: 18 }}> <div style={{ padding: '6px 20px', background: '#00BA85', color: '#FFFFFF', fontSize: 14, marginLeft: 18, cursor: "pointer" }}
onClick={() => {
exportAllData()
}}>
导出全部 导出全部
</div> </div>
</div> </div>
@ -172,7 +204,8 @@ const ContractDetails = (props) => {
pageSizeOpts={[10, 20, 30, 40]} pageSizeOpts={[10, 20, 30, 40]}
onChange={(currentPage, pageSize) => { onChange={(currentPage, pageSize) => {
setQuery({ limit: pageSize, page: currentPage - 1 }); setQuery({ limit: pageSize, page: currentPage - 1 });
page.current = currentPage - 1 getContractDetailData({ limit: pageSize, page: currentPage - 1 });
page.current = currentPage - 1;
}} }}
/> />
</div> </div>
@ -186,6 +219,9 @@ const ContractDetails = (props) => {
setImportModalV(false); setImportModalV(false);
}} /> : '' }} /> : ''
} }
{
exportUrl ? <iframe src={`/_api/${exportUrl}`} style={{ display: 'none' }} /> : ''
}
</div> </div>
{setup ? ( {setup ? (
<Setup <Setup

53
web/client/src/sections/business/containers/performanceReport/invoicingDetails.jsx

@ -1,6 +1,6 @@
import React, { useEffect, useState, useRef, useMemo } from 'react'; import React, { useEffect, useState, useRef, useMemo } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Select, Input, Button, Table, Pagination, Skeleton } from '@douyinfe/semi-ui'; import { Select, Input, Button, Table, Pagination, Skeleton, Toast, } from '@douyinfe/semi-ui';
import { SkeletonScreen, Setup } from "$components"; import { SkeletonScreen, Setup } from "$components";
import { IconSearch } from '@douyinfe/semi-icons'; import { IconSearch } from '@douyinfe/semi-icons';
import ImportInvoicingDetailsModal from './importInvoicingDetailsModal'; import ImportInvoicingDetailsModal from './importInvoicingDetailsModal';
@ -10,7 +10,7 @@ import moment from 'moment'
const InvoicingDetails = (props) => { const InvoicingDetails = (props) => {
const { dispatch, actions } = props const { dispatch, actions } = props
const { } = actions; const { businessManagement } = actions;
const [keywordTarget, setKeywordTarget] = useState('contractNo'); const [keywordTarget, setKeywordTarget] = useState('contractNo');
const [keyword, setKeyword] = useState('');// const [keyword, setKeyword] = useState('');//
const [limits, setLimits] = useState()// const [limits, setLimits] = useState()//
@ -20,6 +20,11 @@ const InvoicingDetails = (props) => {
const [setup, setSetup] = useState(false);// const [setup, setSetup] = useState(false);//
const [setupp, setSetupp] = useState([]);// const [setupp, setSetupp] = useState([]);//
const [exportUrl, setExportUrl] = useState('');
const page = useRef(query.page);
const scroll = useMemo(() => ({}), []);
const INVOICINGDETAILS = "invoicingDetails"; const INVOICINGDETAILS = "invoicingDetails";
const renderColumns = (columnKeys) => { const renderColumns = (columnKeys) => {
let columns = []; let columns = [];
@ -52,6 +57,7 @@ const InvoicingDetails = (props) => {
) )
: ""; : "";
attribute(); attribute();
getInvoicingDetailData();
}, []); }, []);
// //
function attribute() { function attribute() {
@ -66,9 +72,16 @@ const InvoicingDetails = (props) => {
} }
setSetupp(newColumns); setSetupp(newColumns);
} }
useEffect(() => {
}, [query]) function getInvoicingDetailData(param) {
let queryParam = param || query;
dispatch(businessManagement.getInvoicingDetail({ keywordTarget, keyword, ...queryParam })).then(r => {
if (r.success) {
setTableData(r.payload?.data?.rows);
setLimits(r.payload?.data?.count);
}
})
}
function handleRow(record, index) {// function handleRow(record, index) {//
if (index % 2 === 0) { if (index % 2 === 0) {
return { return {
@ -80,7 +93,22 @@ const InvoicingDetails = (props) => {
return {}; return {};
} }
} }
const scroll = useMemo(() => ({}), []);
const exportAllData = () => {
dispatch(businessManagement.getInvoicingDetail({ limit: 1, page: 0 })).then((res) => {
if (res.success) {
if (res.payload.data.count) {
let url = `export/invoicing/detail?token=${user.token}&timestamp=${moment().valueOf()}`
setExportUrl(url);
} else {
Toast.info({
content: '暂无可导出的数据',
duration: 3,
})
}
}
})
}
return ( return (
<> <>
<div style={{ padding: '0px 12px' }}> <div style={{ padding: '0px 12px' }}>
@ -120,6 +148,7 @@ const InvoicingDetails = (props) => {
<Button theme='solid' type='primary' style={{ width: 80, borderRadius: 2, height: 32, background: '#DBECFF', color: '#005ABD' }} <Button theme='solid' type='primary' style={{ width: 80, borderRadius: 2, height: 32, background: '#DBECFF', color: '#005ABD' }}
onClick={() => { onClick={() => {
setQuery({ limit: 10, page: 0 }) setQuery({ limit: 10, page: 0 })
getInvoicingDetailData({ limit: 10, page: 0 })
}}>查询</Button> }}>查询</Button>
</div> </div>
<div style={{ display: 'flex', marginRight: 20 }}> <div style={{ display: 'flex', marginRight: 20 }}>
@ -130,7 +159,10 @@ const InvoicingDetails = (props) => {
onClick={() => { setImportModalV(true); }}> onClick={() => { setImportModalV(true); }}>
导入 导入
</div> </div>
<div style={{ padding: '6px 20px', background: '#00BA85', color: '#FFFFFF', fontSize: 14, marginLeft: 18 }}> <div style={{ padding: '6px 20px', background: '#00BA85', color: '#FFFFFF', fontSize: 14, marginLeft: 18, cursor: "pointer" }}
onClick={() => {
exportAllData()
}}>
导出全部 导出全部
</div> </div>
</div> </div>
@ -185,8 +217,12 @@ const InvoicingDetails = (props) => {
setImportModalV(false); setImportModalV(false);
}} /> : '' }} /> : ''
} }
{
exportUrl ? <iframe src={`/_api/${exportUrl}`} style={{ display: 'none' }} /> : ''
}
</div> </div>
{setup ? ( {
setup ? (
<Setup <Setup
tableType={INVOICINGDETAILS} tableType={INVOICINGDETAILS}
tableList={tableList} tableList={tableList}
@ -198,7 +234,8 @@ const InvoicingDetails = (props) => {
/> />
) : ( ) : (
"" ""
)} )
}
</> </>
) )
} }

5
web/client/src/utils/webapi.js

@ -26,7 +26,10 @@ export const ApiTable = {
getReceivedNumbers: 'detail/received/numbers', getReceivedNumbers: 'detail/received/numbers',
importBackDetails: 'add/received/back/bulk', importBackDetails: 'add/received/back/bulk',
importAchieveDetails: 'add/achievement/bulk' importAchieveDetails: 'add/achievement/bulk',
getContractDetail: 'contract/detail',
getInvoicingDetail: 'invoicing/detail',
}; };
export const RouteTable = { export const RouteTable = {
apiRoot: "/api/root", apiRoot: "/api/root",

6
web/config.js

@ -12,6 +12,8 @@ dev && console.log('\x1B[33m%s\x1b[0m', '请遵循并及时更新 readme.md,
args.option(['p', 'port'], '启动端口'); args.option(['p', 'port'], '启动端口');
args.option(['u', 'api-url'], 'webapi的URL'); args.option(['u', 'api-url'], 'webapi的URL');
args.option('apiHrUrl', 'webapi的URL 外网可访问'); args.option('apiHrUrl', 'webapi的URL 外网可访问');
args.option(['d', 'domain'], 'web domain');
// 七牛 // 七牛
args.option('qnak', 'qiniuAccessKey'); args.option('qnak', 'qiniuAccessKey');
@ -24,6 +26,7 @@ const flags = args.parse(process.argv);
const API_URL = process.env.API_URL || flags.apiUrl; const API_URL = process.env.API_URL || flags.apiUrl;
const API_DC_URL = process.env.API_DC_URL || flags.apiHrUrl; const API_DC_URL = process.env.API_DC_URL || flags.apiHrUrl;
const FS_REPORT_DOMAIN = process.FS_REPORT_DOMAIN || flags.domain;
// 七牛 // 七牛
const ANXINCLOUD_QINIU_AK = process.env.ANXINCLOUD_QINIU_ACCESSKEY || flags.qnak; const ANXINCLOUD_QINIU_AK = process.env.ANXINCLOUD_QINIU_ACCESSKEY || flags.qnak;
@ -76,7 +79,8 @@ const product = {
}, },
service: { service: {
url: ANXINCLOUD_PM_SERVICES url: ANXINCLOUD_PM_SERVICES
} },
domain: FS_REPORT_DOMAIN
} }
}, { }, {
entry: require('./client').entry,// 静态信息 entry: require('./client').entry,// 静态信息

3
web/package.json

@ -7,7 +7,7 @@
"test": "mocha", "test": "mocha",
"start-vite": "cross-env NODE_ENV=developmentVite npm run start-params", "start-vite": "cross-env NODE_ENV=developmentVite npm run start-params",
"start": "cross-env NODE_ENV=development npm run start-params", "start": "cross-env NODE_ENV=development npm run start-params",
"start-params": "node server -p 5700 -u http://localhost:4700 --apiHrUrl http://localhost:4700 --qnak 5XrM4wEB9YU6RQwT64sPzzE6cYFKZgssdP5Kj3uu --qnsk w6j2ixR_i-aelc6I7S3HotKIX-ukMzcKmDfH6-M5 --qnbkt pep-process-report --qndmn https://pepsource.anxinyun.cn --pmrs http://10.8.30.109:14000", "start-params": "node server -p 5700 -u http://localhost:4700 --apiHrUrl http://localhost:4700 -d localhost --qnak 5XrM4wEB9YU6RQwT64sPzzE6cYFKZgssdP5Kj3uu --qnsk w6j2ixR_i-aelc6I7S3HotKIX-ukMzcKmDfH6-M5 --qnbkt pep-process-report --qndmn https://pepsource.anxinyun.cn --pmrs http://10.8.30.109:14000",
"deploy": "export NODE_ENV=production&& npm run build && node server", "deploy": "export NODE_ENV=production&& npm run build && node server",
"build-dev": "cross-env NODE_ENV=development&&webpack --config webpack.config.js", "build-dev": "cross-env NODE_ENV=development&&webpack --config webpack.config.js",
"build": "cross-env NODE_ENV=production&&webpack --config webpack.config.prod.js" "build": "cross-env NODE_ENV=production&&webpack --config webpack.config.prod.js"
@ -60,6 +60,7 @@
"ezuikit-js": "^0.6.1", "ezuikit-js": "^0.6.1",
"fs-attachment": "^1.0.0", "fs-attachment": "^1.0.0",
"fs-web-server-scaffold": "^1.0.6", "fs-web-server-scaffold": "^1.0.6",
"js-cookie": "^3.0.1",
"js-export-excel": "^1.1.4", "js-export-excel": "^1.1.4",
"koa-better-http-proxy": "^0.2.5", "koa-better-http-proxy": "^0.2.5",
"koa-proxy": "^1.0.0-alpha.3", "koa-proxy": "^1.0.0-alpha.3",

3
web/routes/attachment/index.js

@ -20,11 +20,12 @@ module.exports = {
entry: function (app, router, opts) { entry: function (app, router, opts) {
const getApiRoot = async function (ctx) { const getApiRoot = async function (ctx) {
const { apiUrl } = opts; const { apiUrl,domain } = opts;
ctx.status = 200; ctx.status = 200;
ctx.body = { ctx.body = {
root: apiUrl, root: apiUrl,
domain:domain
}; };
}; };

Loading…
Cancel
Save