department.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. <template>
  2. <div v-loading="loading">
  3. <div class="search clear">
  4. <div style="float:left;line-height:30px;display:flex;align-items:center">
  5. <el-popover placement="top" width="200" trigger="hover" content="不含业务公司供应商端数据">
  6. <template #reference>
  7. <i
  8. class="el-icon-warning-outline"
  9. style="font-size:16px;cursor:pointer;padding-top:5px;padding-right:5px;display:inline-block"
  10. ></i>
  11. </template>
  12. </el-popover>部门完成情况
  13. </div>
  14. <div style="float:right">
  15. <el-select
  16. size="small"
  17. style="margin-right:10px;width:135px"
  18. v-model="companyNo"
  19. @change="requestData"
  20. :disabled="isEmpty"
  21. >
  22. <el-option
  23. v-for="depart in cp_companies"
  24. :key="depart.value"
  25. :label="depart.label"
  26. :value="depart.value"
  27. />
  28. </el-select>
  29. <el-date-picker
  30. :disabled="isEmpty"
  31. class="fr picker no-padding"
  32. v-model="daytime"
  33. :picker-options="{ disbaledData(time) { return time.getTime() > Date.now(); } }"
  34. style=";width:100px"
  35. value-format="yyyy-MM-dd"
  36. format="yyyy-MM-dd"
  37. :editable="false"
  38. :clearable="false"
  39. placeholder="选择日期"
  40. :size="'small'"
  41. align="right"
  42. type="date"
  43. />
  44. </div>
  45. </div>
  46. <el-row style="margin-top: 10px; display: flex;width:100%;margin-top:10px" v-if="!isEmpty">
  47. <el-table
  48. border
  49. size="mini"
  50. :data="list"
  51. :header-cell-class-name="setHeaderClassName"
  52. :cell-class-name="setCellClassName"
  53. >
  54. <!-- <el-table-column fixed="left" label="公司" prop="company" align="center" width="45px" /> -->
  55. <el-table-column fixed="left" label="部门" prop="depart" align="center" width="60px">
  56. <template slot-scope="scope">
  57. <p
  58. style="margin:0px"
  59. v-for="(chunk, index) in scope.row.depart.split('@')"
  60. :key="index"
  61. >{{ chunk }}</p>
  62. </template>
  63. </el-table-column>
  64. <el-table-column label="当日营业收入" align="center" min-width="105px">
  65. <template
  66. slot-scope="scope"
  67. >{{ unit2TenThousand(scope.row.dayinfo.sale_total, isTenThound) }}</template>
  68. </el-table-column>
  69. <el-table-column label="当月营收目标" align="center" min-width="115px">
  70. <template slot-scope="scope">{{ unit2TenThousand(scope.row.total_tips, isTenThound) }}</template>
  71. </el-table-column>
  72. <el-table-column label="当月营业收入(净)" align="center" min-width="120px">
  73. <template slot-scope="scope">
  74. <div style="display:flex;flex-direction: column;">
  75. <div style="display:flex;justify-content:center;align-items:center">
  76. {{ unit2TenThousand(scope.row.monthinfo.monthNetSales, isTenThound) }}
  77. <el-popover placement="right" :width="200" trigger="click">
  78. <div class="table-size">
  79. <p>直营/自营: {{ unit2TenThousand(scope.row.currentMonthPure[0].zy, isTenThound) }}</p>
  80. <p>支付渠道: {{ unit2TenThousand(scope.row.currentMonthPure[0].qd, isTenThound) }}</p>
  81. </div>
  82. <template #reference>
  83. <p
  84. :style="`text-align: center;${getCurrentValueStyle(scope.row.monthinfo.monthNetSales, scope.row.total_tips)}`"
  85. >
  86. <i
  87. class="el-icon-warning-outline trigger-hover"
  88. style="font-size:16px;cursor:pointer;margin-left:5px"
  89. ></i>
  90. </p>
  91. </template>
  92. </el-popover>
  93. </div>
  94. </div>
  95. </template>
  96. </el-table-column>
  97. <el-table-column label="当月成本" align="center" min-width="120px">
  98. <template slot-scope="scope">
  99. <div style="display:flex;justify-content:center;align-items:center" v-if="costField">
  100. {{
  101. unit2TenThousand(Number(addition(scope.row.zy_cost, scope.row.qd_cost)).toFixed(2), isTenThound) }}
  102. <el-popover placement="right" :width="200" trigger="click">
  103. <div class="table-size">
  104. <p>直营/自营: {{ unit2TenThousand(scope.row.zy_cost, isTenThound) }}</p>
  105. <p>渠道: {{ unit2TenThousand(scope.row.qd_cost, isTenThound) }}</p>
  106. </div>
  107. <template #reference>
  108. <p style="text-align: center;">
  109. <i
  110. class="el-icon-warning-outline trigger-hover"
  111. style="font-size:16px;cursor:pointer;margin-left:5px"
  112. ></i>
  113. </p>
  114. </template>
  115. </el-popover>
  116. </div>
  117. <p v-else>***</p>
  118. </template>
  119. </el-table-column>
  120. <el-table-column align="center" label="当月毛利" min-width="120px">
  121. <template slot-scope="scope">
  122. <div style="display:flex;justify-content:center;align-items:center" v-if="costField">
  123. {{unit2TenThousand(Number(addition(scope.row.zy_gross, scope.row.qd_gross)).toFixed(2), isTenThound)}}
  124. <el-popover placement="right" :width="200" trigger="click">
  125. <div class="table-size">
  126. <p>直营/自营: {{ unit2TenThousand(scope.row.zy_gross, isTenThound) }}</p>
  127. <p>支付渠道: {{ unit2TenThousand(scope.row.qd_gross, isTenThound) }}</p>
  128. </div>
  129. <template #reference>
  130. <p style="text-align: center;">
  131. <i class="el-icon-warning-outline trigger-hover" style="font-size:16px;cursor:pointer;margin-left:5px"></i>
  132. </p>
  133. </template>
  134. </el-popover>
  135. </div>
  136. <p v-else>***</p>
  137. </template>
  138. </el-table-column>
  139. </el-table>
  140. </el-row>
  141. <template v-else>
  142. <div style="text-align:center;line-height:60px;user-select:none">当前账号没有访问权限</div>
  143. </template>
  144. </div>
  145. </template>
  146. <script>
  147. import asyncRequest from "@/api/newResults";
  148. import setHeight from "@/mixins/index";
  149. import { mapCompany } from "./mapCompany";
  150. import dayjs from "dayjs";
  151. import {
  152. addition,
  153. division,
  154. subtraction,
  155. multiplication,
  156. unit2TenThousand
  157. } from "../newReport/src/_utils";
  158. export default {
  159. mixins: [setHeight],
  160. props: ["companies", "costField", "isTenThound"],
  161. data() {
  162. return {
  163. list: [],
  164. daytime: "",
  165. companyNo: "",
  166. loading: false,
  167. isEmpty: false,
  168. cp_companies: []
  169. };
  170. },
  171. computed: {
  172. currentCompanyName() {
  173. return this.companies.find(item => item.value === this.companyNo).label;
  174. }
  175. },
  176. mounted() {
  177. this.isEmpty = this.companies.length === 0;
  178. if (this.isEmpty) return;
  179. this.cp_companies = this.companies.map(item => ({
  180. ...item,
  181. label:
  182. mapCompany[item.label] === "万宇"
  183. ? `平台公司: 万宇`
  184. : `业务公司: ${mapCompany[item.label]}`
  185. }));
  186. let list = [
  187. "平台公司: 万宇",
  188. "业务公司: 百辰",
  189. "业务公司: 泓源",
  190. "业务公司: 普润",
  191. "业务公司: 锦兴",
  192. "业务公司: 知事"
  193. ];
  194. const cp_list = this.cp_companies.map(item => item.label);
  195. list = list.filter(item => cp_list.includes(item));
  196. this.cp_companies = list.map(item =>
  197. this.cp_companies.find(cp_item => cp_item.label === item)
  198. );
  199. this.companyNo = this.cp_companies[0].value;
  200. this.companyNo = this.cp_companies[0].value;
  201. this.daytime = this.transformTime();
  202. this.requestData();
  203. },
  204. watch: {
  205. daytime: {
  206. handler() {
  207. this.requestData();
  208. }
  209. }
  210. },
  211. methods: {
  212. addition,
  213. unit2TenThousand,
  214. setCellClassName({ column, row }) {
  215. const { label } = column;
  216. let base = "";
  217. if (row.depart.indexOf("万宇") !== -1) {
  218. base += "font-bold ";
  219. }
  220. if (label === "当月营收目标") {
  221. base += "bg__success";
  222. return base;
  223. }
  224. if (
  225. (label.indexOf("营业") !== -1 || label.indexOf("营收") !== -1) &&
  226. label !== "当日营业收入"
  227. ) {
  228. base += "bg__success_1";
  229. return base;
  230. }
  231. if (label === "毛利目标") {
  232. base += "bg__primary";
  233. return base;
  234. }
  235. if (label.indexOf("毛利") !== -1) {
  236. base += "bg__primary_1";
  237. return base;
  238. }
  239. if (label.indexOf("成本") !== -1) {
  240. base += "bg__warning_1";
  241. return base;
  242. }
  243. return base;
  244. },
  245. setHeaderClassName({ column }) {
  246. const { label } = column;
  247. if (label === "当月营收目标") return "bg__success";
  248. if (
  249. (label.indexOf("营业") !== -1 || label.indexOf("营收") !== -1) &&
  250. label !== "当日营业收入"
  251. )
  252. return "bg__success_1";
  253. if (label === "毛利目标") return "bg__primary";
  254. if (label.indexOf("毛利") !== -1) return "bg__primary_1";
  255. if (label.indexOf("成本") !== -1) return "bg__warning_1";
  256. },
  257. getCurrentRateStyle(current) {
  258. const days = dayjs(this.daytime).daysInMonth();
  259. const oneDay = Number(division(100, days)).toFixed(2);
  260. const currentDay = dayjs(this.daytime).date();
  261. const currentTotalTip = Number(
  262. multiplication(currentDay, oneDay)
  263. ).toFixed(2);
  264. return Number(currentTotalTip) > Number(current) ? "color:red" : "";
  265. },
  266. getCurrentValueStyle(current, total) {
  267. const days = dayjs(this.daytime).daysInMonth();
  268. const oneDay = Number(division(total, days)).toFixed(2);
  269. const currentDay = dayjs(this.daytime).date();
  270. const currentTotalTip = Number(
  271. multiplication(currentDay, oneDay)
  272. ).toFixed(2);
  273. return Number(currentTotalTip) > Number(current) ? "color: red" : "";
  274. },
  275. transformTime() {
  276. let time = new Date();
  277. let y = time.getFullYear();
  278. let M = time.getMonth() + 1;
  279. let d = time.getDate();
  280. return y + "-" + (M < 10 ? "0" + M : M) + "-" + (d < 10 ? "0" + d : d);
  281. },
  282. /* 表格合并列和行 */
  283. spanMethod({ rowIndex, columnIndex }) {
  284. if (columnIndex === 0) {
  285. const _row = this.flitterData(this.list).one[rowIndex];
  286. const _col = _row > 0 ? 1 : 0;
  287. return { rowspan: _row, colspan: _col };
  288. }
  289. },
  290. /**合并表格的第一列,处理表格数据 */
  291. flitterData(arr) {
  292. let spanOneArr = [];
  293. let concatOne = 0;
  294. arr.forEach((item, index) => {
  295. if (index === 0) {
  296. spanOneArr.push(1);
  297. } else {
  298. //注意这里的quarterly是表格绑定的字段,根据自己的需求来改
  299. if (item.company === arr[index - 1].company) {
  300. //第一列需合并相同内容的判断条件
  301. spanOneArr[concatOne] += 1;
  302. spanOneArr.push(0);
  303. } else {
  304. spanOneArr.push(1);
  305. concatOne = index;
  306. }
  307. }
  308. });
  309. return { one: spanOneArr };
  310. },
  311. async requestData() {
  312. this.loading = true;
  313. this.list = [];
  314. const res = await asyncRequest.departmentEveryDay({
  315. daytime: this.daytime,
  316. companyNo: this.companyNo
  317. });
  318. if (res.code === 0 && res.data && res.data.length > 0) {
  319. // 非万宇 营业目标重取
  320. // if (this.companyNo !== "GS2203161855277894") {
  321. // const r = await asyncRequest.companyEveryMonth({ daytime: this.daytime, companyNo: this.companyNo });
  322. // const { total_tips, companyName } = r.data[0] || {};
  323. // res.data.forEach(item => {
  324. // const index = companyName.indexOf(item.depart);
  325. // item.total_tips = (index === -1 && !(item.depart === "普润" && this.companyNo === "GS2304031312553746")) ? '0.00' : total_tips;
  326. // })
  327. // }
  328. let list = (res.data || []).map(
  329. ({
  330. depart,
  331. msale_total,
  332. mth_total,
  333. sale_total,
  334. th_total,
  335. total_tips,
  336. mzy_sale_total,
  337. mchannel_sale_total,
  338. channel_cost_total,
  339. zy_cost_total,
  340. mzy_cost_total,
  341. mchannel_cost_total
  342. }) => {
  343. return {
  344. depart,
  345. total_tips,
  346. mzy_sale_total,
  347. mchannel_sale_total,
  348. channel_cost_total,
  349. zy_cost_total,
  350. mchannel_cost_total,
  351. mzy_cost_total,
  352. dayinfo: { sale_total, th_total },
  353. monthinfo: { msale_total, mth_total }
  354. };
  355. }
  356. );
  357. list = list.filter(item => Number(item.monthinfo.msale_total) !== 0);
  358. this.total = list.reduce(
  359. (prev, current) => {
  360. const { total_tips = 0, day = 0, month = 0 } = current;
  361. return {
  362. total_tips: addition(total_tips, prev.total_tips),
  363. month: addition(month, prev.month),
  364. day: addition(day, prev.day)
  365. };
  366. },
  367. { total_tips: 0, month: 0, day: 0 }
  368. );
  369. let mapDepart = list.map(({ depart }) => depart);
  370. list = mapDepart.map(d => list.find(({ depart }) => depart === d));
  371. const mapToDepartment = {
  372. 百辰: "客服部@百辰",
  373. 泓源: "网络部@泓源",
  374. 普润: " 项目部@普润",
  375. 平台: " 平台部@万宇"
  376. };
  377. const company = this.companies.find(
  378. item => item.value === this.companyNo
  379. );
  380. this.list = list.map(
  381. ({
  382. depart,
  383. total_tips,
  384. dayinfo,
  385. monthinfo,
  386. mchannel_sale_total,
  387. mzy_sale_total,
  388. mchannel_cost_total,
  389. mzy_cost_total
  390. }) => {
  391. // mzy_sale_total = Number(addition(Number(multiplication(mzy_sale_total,0.05)).toFixed(2),mzy_sale_total)).toFixed(2);
  392. // mchannel_sale_total = Number(addition(Number(multiplication(mchannel_sale_total,0.05)).toFixed(2),mchannel_sale_total)).toFixed(2);
  393. /* 月净销售 = 月销售 - 月退货 **/
  394. let monthNetSales = subtraction(
  395. monthinfo.msale_total,
  396. monthinfo.mth_total
  397. );
  398. // monthNetSales = Number(addition(Number(multiplication(monthNetSales,0.05)).toFixed(2),monthNetSales)).toFixed(2);
  399. /* 月经销售完成率 = 月净销售 / 销售指标 **/
  400. const monthProportion = multiplication(
  401. division(monthNetSales, total_tips),
  402. 100
  403. ).toFixed(2);
  404. return {
  405. total_tips, // 营收目标
  406. zy_cost: mzy_cost_total, // 自营成本
  407. qd_cost: mchannel_cost_total, // 渠道成本
  408. depart: mapToDepartment[depart],
  409. company: company.label,
  410. currentMonthPure: [
  411. {
  412. zy: mzy_sale_total, // 加%5税率
  413. qd: mchannel_sale_total
  414. }
  415. ], // 当月营业收入(净)
  416. zy_gross: Number(
  417. subtraction(mzy_sale_total, mzy_cost_total)
  418. ).toFixed(2), // 自营毛利 = 自营营收 - 自营成本
  419. qd_gross: Number(
  420. subtraction(mchannel_sale_total, mchannel_cost_total)
  421. ).toFixed(2), // 渠道毛利 = 渠道营收 - 渠道成本
  422. dayinfo: {
  423. ...dayinfo,
  424. /** 日销售额 = 日销售额 - 日退货额 */ sale_total: subtraction(
  425. dayinfo.sale_total,
  426. dayinfo.th_total
  427. )
  428. },
  429. /* 占比 = (当前部门销售额 / 各部门总销售额) / 100 **/
  430. proportion:
  431. multiplication(
  432. division(monthinfo.msale_total, this.total.month) || 0,
  433. 100
  434. ) || 0,
  435. monthinfo: { monthNetSales, monthProportion: monthProportion }
  436. };
  437. }
  438. );
  439. // const wanyuIndex = this.list.findIndex(item => item.depart.indexOf('万宇') !== -1);
  440. // if(wanyuIndex !== -1){
  441. // const item = this.list.splice(wanyuIndex,1);
  442. // this.list.unshift(item[0]);
  443. // }
  444. let l = ["万宇", "百辰", "泓源", "普润"];
  445. const cl = this.list.map(item => item.depart);
  446. l = l.filter(item => {
  447. return cl.findIndex(i => i.indexOf(item)) !== -1;
  448. });
  449. this.list = l.map(item => {
  450. return this.list.find(i => i.depart.indexOf(item) !== -1);
  451. });
  452. } else {
  453. this.list = [];
  454. }
  455. this.getHeight();
  456. this.loading = false;
  457. }
  458. }
  459. };
  460. </script>
  461. <style lang="scss" scoped>
  462. .new-results {
  463. .search {
  464. height: 36px;
  465. padding: 0px 10px;
  466. margin-top: 10px;
  467. width: 100%;
  468. box-sizing: border-box;
  469. justify-content: space-between;
  470. }
  471. }
  472. .table-size {
  473. display: flex;
  474. width: 200px;
  475. border: 1px solid #ebeef5;
  476. flex-direction: column;
  477. p {
  478. flex: 1;
  479. border-bottom: 1px solid #ebeef5;
  480. padding: 5px 10px;
  481. margin: 0px;
  482. &:last-child {
  483. border: none;
  484. }
  485. }
  486. }
  487. </style>