index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. const _Http = getApp().globalData.http;
  2. Page({
  3. data: {
  4. quarterMap: {
  5. 1: "一",
  6. 2: "二",
  7. 3: "三",
  8. 4: "四"
  9. },
  10. monthsCopy: [],
  11. totalWidth: 14290,
  12. },
  13. onLoad(options) {
  14. this.setData({
  15. currentYear: new Date().getFullYear(),
  16. currentMonth: new Date().getMonth() + 1,
  17. currentQuarter: Math.ceil((new Date().getMonth() + 1) / 3),
  18. months: wx.getStorageSync('siteP').statistics_months
  19. })
  20. this.getAreaList()
  21. _Http.basic({
  22. "id": "2025103010165903",
  23. "content": {
  24. date: Date.now(),
  25. }
  26. }).then(res => {
  27. console.log("当前业务员区域", res)
  28. if (res.code != 1) return wx.showToast({
  29. title: res.msg,
  30. icon: "none"
  31. });
  32. this.setData({
  33. currentUser: res.data.map(v => {
  34. v.key = v.areaname + (v.name ? '(' + v.name + ")" : '')
  35. return v
  36. })[0]
  37. })
  38. // 查询目标
  39. this.queryTarget()
  40. this.queryPaymentDetails();
  41. })
  42. this.getAreaList();
  43. },
  44. queryTarget() {
  45. _Http.basic({
  46. "id": "2025103010192003",
  47. "content": {
  48. isexport: 0,
  49. date: Date.now(),
  50. "year": this.data.currentYear,
  51. "month": this.data.currentMonth,
  52. months: this.data.months,
  53. where: {
  54. areaname: this.data.currentUser.areaname
  55. }
  56. }
  57. }).then(res => {
  58. console.log('查询目标', res)
  59. if (res.code == 1) {
  60. let data = res.data.find(v => v.areaname == this.data.currentUser.areaname);
  61. this.setData({
  62. target: data
  63. })
  64. }
  65. })
  66. },
  67. // 查询回款明细
  68. queryPaymentDetails() {
  69. const keys = [{
  70. label: '现金账户回款',
  71. key: 'amount',
  72. number: '1',
  73. style: 'width:250rpx;'
  74. },
  75. {
  76. label: '活动预存账户回款',
  77. key: 'amount',
  78. number: '5',
  79. style: 'width:250rpx;'
  80. },
  81. {
  82. label: '返利',
  83. key: 'amount',
  84. number: '3',
  85. style: 'width:150rpx;'
  86. },
  87. {
  88. label: '小计',
  89. key: 'sum',
  90. number: '',
  91. style: 'width:150rpx;'
  92. },
  93. {
  94. label: 'GC工程账户汇款',
  95. key: 'amount',
  96. number: '2',
  97. style: 'width:240rpx;'
  98. },
  99. {
  100. label: '整装工程',
  101. key: 'amount',
  102. number: '6',
  103. style: 'width:200rpx;'
  104. },
  105. {
  106. label: '木制品',
  107. key: 'amount',
  108. number: '4',
  109. style: 'width:150rpx;border-right:1px solid #999;',
  110. class: 'mzp'
  111. }
  112. ];
  113. _Http.basic({
  114. "id": "2025103015304303",
  115. "content": {
  116. "pageNumber": 1,
  117. "pageSize": 20,
  118. "year": 2025,
  119. date123: Date.now(),
  120. months: this.data.months,
  121. "year": this.data.currentYear,
  122. "month": this.data.currentMonth,
  123. "where": {
  124. areaname: this.data.currentUser.areaname == '全国' ? '' : this.data.currentUser.areaname,
  125. "areaname2": "",
  126. "areaname3": this.data.currentUser.areaname == '全国' ? '' : this.data.currentUser.areaname,
  127. "province": "",
  128. "status": "启用"
  129. }
  130. }
  131. }).then(res => {
  132. console.log("回款明细", res)
  133. if (res.code != 1) return wx.showToast({
  134. title: res.msg,
  135. icon: "none"
  136. });
  137. if (res.data.length == 0 && res.code == 1) this.setData({
  138. paymentDetails: []
  139. })
  140. let areaMap = new Map();
  141. // === 构建明细行 ===
  142. let paymentDetails = res.data.map((v, index1) => {
  143. v.cols = [{
  144. label: '区域',
  145. value: v.areaname3,
  146. style: 'width:150rpx;'
  147. },
  148. {
  149. label: '省份',
  150. value: v.province,
  151. style: 'width:120rpx;'
  152. },
  153. {
  154. label: '经销商名称',
  155. value: v.enterprisename,
  156. style: 'width:220rpx; position: sticky; left: 0; border-right:1px solid #999; z-index: 1;'
  157. }
  158. ];
  159. // 查找或创建区域项
  160. let item = areaMap.get(v.areaname2);
  161. if (!item) {
  162. item = {
  163. areaname2: v.areaname2,
  164. row: []
  165. };
  166. areaMap.set(v.areaname2, item);
  167. }
  168. // 遍历12个月
  169. for (let month = 1; month <= 12; month++) {
  170. keys.forEach((s, si) => {
  171. const key = `${s.key}${month}${s.number}`;
  172. const value = Number(v[key]) || 0;
  173. v.cols.push({
  174. ...s,
  175. value: this.CNY(value)
  176. });
  177. delete v[key];
  178. if (!item.row[month - 1]) item.row[month - 1] = [];
  179. item.row[month - 1][si] = (item.row[month - 1][si] || 0) + value;
  180. // 仅对合计项字段参与求和
  181. /* if (['现金账户回款', '小计', '工程账户回款'].includes(s.label)) {
  182. item.row[month - 1][si] = (item.row[month - 1][si] || 0) + value;
  183. } else {
  184. item.row[month - 1][si] = item.row[month - 1][si] || 0;
  185. } */
  186. });
  187. }
  188. return v;
  189. });
  190. // === Map 转数组 ===
  191. const total = Array.from(areaMap.values());
  192. // === 表头 ===
  193. const monthWidth = this.getTotalWidth(keys);
  194. let headerRow = [{
  195. label: '',
  196. value: '',
  197. style: 'width:150rpx;'
  198. },
  199. {
  200. label: '',
  201. value: '',
  202. style: 'width:120rpx;'
  203. },
  204. {
  205. label: '月份',
  206. value: '',
  207. style: 'width:220rpx; position: sticky; left: 0; border-right:1px solid #999; z-index: 1;'
  208. }
  209. ];
  210. for (let month = 1; month <= 12; month++) {
  211. headerRow.push({
  212. label: `${this.data.currentYear}-${month}月`,
  213. value: '',
  214. style: `width: ${monthWidth}rpx; text-align: center; border-top: 1px solid #999;`
  215. });
  216. }
  217. // === ✅ 修复后的合计行插入逻辑 ===
  218. total.forEach((t) => {
  219. t.cols = [{
  220. label: '区域',
  221. value: '',
  222. style: 'width:150rpx;'
  223. },
  224. {
  225. label: '省份',
  226. value: '',
  227. style: 'width:120rpx;'
  228. },
  229. {
  230. label: '经销商名称',
  231. value: `${t.areaname2}合计`,
  232. style: 'width:220rpx; position: sticky; left: 0; border-right:1px solid #999; z-index: 1;'
  233. }
  234. ];
  235. t.row.forEach((r) => {
  236. keys.forEach((s, si) => {
  237. const isSummaryField = ['现金账户回款', '小计', '工程账户回款'].includes(s.label);
  238. const rawValue = r[si] || 0;
  239. //仅合计指定字段
  240. // const displayValue = isSummaryField ? this.CNY(rawValue) : '-';
  241. const displayValue = this.CNY(rawValue);
  242. t.cols.push({
  243. ...s,
  244. value: displayValue
  245. });
  246. });
  247. });
  248. });
  249. // 找出每个区域的最后一条明细所在索引
  250. const inserts = total.map(t => {
  251. const lastIdx = paymentDetails.reduce((acc, p, idx) => {
  252. return (p.areaname2 === t.areaname2) ? idx : acc;
  253. }, -1);
  254. return {
  255. t,
  256. index: lastIdx + 1
  257. };
  258. });
  259. // 按索引从大到小插入,防止前插后移位
  260. inserts.sort((a, b) => b.index - a.index);
  261. inserts.forEach(({
  262. t,
  263. index
  264. }) => {
  265. if (index < 0 || index > paymentDetails.length) {
  266. index = paymentDetails.length;
  267. }
  268. paymentDetails.splice(index, 0, t);
  269. });
  270. // === 计算总宽与表头 ===
  271. this.setData({
  272. totalWidth: paymentDetails.length ? this.getTotalWidth(paymentDetails[0].cols) : this.data.totalWidth,
  273. headerRow: paymentDetails.length ? headerRow : []
  274. });
  275. // === 分批渲染 ===
  276. const BATCH_SIZE = 200;
  277. let renderedData = [];
  278. let currentIndex = 0;
  279. wx.showLoading({
  280. title: '加载中...',
  281. })
  282. const renderBatch = () => {
  283. if (currentIndex < paymentDetails.length) {
  284. const nextBatch = paymentDetails.slice(currentIndex, currentIndex + BATCH_SIZE);
  285. renderedData = renderedData.concat(nextBatch);
  286. this.setData({
  287. paymentDetails: renderedData
  288. });
  289. currentIndex += BATCH_SIZE;
  290. setTimeout(renderBatch, 0);
  291. } else {
  292. wx.hideLoading()
  293. }
  294. };
  295. renderBatch();
  296. });
  297. },
  298. getTotalWidth(cols) {
  299. //计算总宽度
  300. let totalWidth = 0;
  301. cols.forEach(item => {
  302. let width = item.style.match(/width:\s*([\d.]+)rpx/);
  303. if (width && width[1]) {
  304. totalWidth += parseFloat(width[1]);
  305. }
  306. })
  307. return totalWidth
  308. },
  309. //获取当前业务员区域及其下属区域
  310. getAreaList() {
  311. _Http.basic({
  312. "id": "2025103009445603",
  313. "content": {
  314. pageSize: 9999,
  315. date: Date.now(),
  316. }
  317. }).then(res => {
  318. console.log("业务员及其下属区域", res)
  319. if (res.code != 1) return wx.showToast({
  320. title: res.msg,
  321. icon: "none"
  322. });
  323. this.setData({
  324. areaList: res.data.map(v => {
  325. v.key = v.areaname + (v.name ? '(' + v.name + ")" : '')
  326. return v
  327. })
  328. })
  329. })
  330. },
  331. CNY(value) {
  332. if (value === null || value === undefined || isNaN(value)) return '0';
  333. let num = parseFloat(value).toFixed(2);
  334. num = num.replace(/\.?0+$/, '');
  335. const parts = num.split('.');
  336. parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  337. return parts.join('.');
  338. },
  339. // 切换月
  340. changeDate(e) {
  341. let value = e.detail.value.split("-")
  342. this.setData({
  343. currentYear: value[0],
  344. currentMonth: value[1],
  345. currentQuarter: Math.ceil((value[1] - 0 + 1) / 3)
  346. })
  347. this.queryTarget();
  348. this.queryPaymentDetails();
  349. },
  350. // 切换区域
  351. changeSalearea(e) {
  352. let index = e.detail.value;
  353. this.setData({
  354. currentUser: this.data.areaList[index]
  355. })
  356. this.queryTarget();
  357. this.queryPaymentDetails();
  358. },
  359. // 选择月
  360. selectedOption(e) {
  361. let {
  362. index
  363. } = e.currentTarget.dataset,
  364. monthsCopy = this.data.monthsCopy;
  365. if (monthsCopy.includes(index)) {
  366. monthsCopy = monthsCopy.filter(v => v != index)
  367. } else {
  368. monthsCopy.push(index)
  369. monthsCopy.sort((a, b) => a - b)
  370. }
  371. this.setData({
  372. monthsCopy
  373. })
  374. },
  375. onClose() {
  376. this.setData({
  377. monthsCopy: []
  378. })
  379. },
  380. onConfirm() {
  381. this.setData({
  382. months: this.data.monthsCopy
  383. })
  384. this.queryTarget();
  385. },
  386. openDialog() {
  387. this.setData({
  388. monthsCopy: this.data.months
  389. })
  390. }
  391. })