company.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. <template>
  2. <div v-loading="loading">
  3. <div class="search clear" style="align-items: center;;">
  4. <div style="float:left;line-height:30px;font-size:16px">
  5. 月度完成情况
  6. </div>
  7. <div style="float:right">
  8. <el-select size="small" style="margin-right:10px;width:140px" v-model="companyNo" @change="requestData"
  9. :disabled="isEmpty">
  10. <el-option v-for="company in cp_companies" :key="company.value" :value="company.value" :label="company.label" />
  11. </el-select>
  12. <el-date-picker class="fr picker no-padding" v-model="daytime" style=";width:95px" value-format="yyyy-MM-dd"
  13. :editable="false" :disabled="isEmpty" :clearable="false" :size="'small'" format="yyyy-MM-dd" type="date"
  14. align="right" placeholder="选择日期"
  15. :picker-options="{ disabledDate(time) { return time.getTime() > Date.now(); } }" @change="requestData" />
  16. </div>
  17. </div>
  18. <el-row style="margin-top:10px;margin-top:10px" v-if="!isEmpty">
  19. <el-table border size="mini" :data="tableData" :header-cell-class-name="setHeaderClassName"
  20. :cell-class-name="setCellClassName">
  21. <el-table-column fixed="left" label="公司" prop="companyName" align="center" width="65px">
  22. <template slot-scope="scope">
  23. <div v-if="scope.row.companyName.indexOf('普润') === -1">
  24. {{ mapCompany[scope.row.companyName] || scope.row.companyName }}
  25. </div>
  26. <div v-else>
  27. <el-popover placement="top" width="200" trigger="hover" content="普润&锦兴&知事">
  28. <template #reference>
  29. <i class="el-icon-warning-outline" style="font-size:14px;cursor:pointer"></i>
  30. 普润
  31. </template>
  32. </el-popover>
  33. </div>
  34. </template>
  35. </el-table-column>
  36. <el-table-column label="当日营业收入" align="center" width="95px">
  37. <template slot-scope="scope">
  38. {{ unit2TenThousand(scope.row.sale_total, isTenThound) }}
  39. </template>
  40. </el-table-column>
  41. <el-table-column label="营收目标" align="center" min-width="95px">
  42. <template slot-scope="scope">
  43. {{ unit2TenThousand(scope.row.total_tips, isTenThound) }}
  44. </template>
  45. </el-table-column>
  46. <el-table-column label="营业收入(净)" align="center" min-width="115px">
  47. <template slot-scope="scope">
  48. <div style="display:flex;flex-direction: column;">
  49. <el-popover placement="top" :width="200" trigger="hover">
  50. <div class="table-size">
  51. <p>直营/自营: {{ unit2TenThousand(scope.row.info[0][0].monthinfo.msale_total, isTenThound) }}</p>
  52. <p>支付渠道: {{ unit2TenThousand(scope.row.info[0][1].monthinfo.msale_total, isTenThound) }}</p>
  53. <p>供应商端: {{ unit2TenThousand(scope.row.info[0][2].monthinfo.msale_total, isTenThound) }}</p>
  54. </div>
  55. <template #reference>
  56. <div :style="`${getCurrentValueStyle(scope.row.msale_total, scope.row.total_tips)}`">
  57. <i class="el-icon-warning-outline" style="font-size:16px;cursor:pointer"></i>
  58. {{ unit2TenThousand(scope.row.msale_total, isTenThound) }}
  59. </div>
  60. </template>
  61. </el-popover>
  62. </div>
  63. </template>
  64. </el-table-column>
  65. <el-table-column label="营收完成%" align="center" min-width="85px">
  66. <template slot-scope="scope">
  67. <div :style="`${getCurrentRateStyle(scope.row.completion_rate)}`">{{ scope.row.completion_rate + "%" }}
  68. </div>
  69. </template>
  70. </el-table-column>
  71. <template v-if="costField">
  72. <el-table-column label="毛利目标" align="center" min-width="90px">
  73. <template slot-scope="scope">
  74. {{ unit2TenThousand(scope.row.cost_tips, isTenThound) }}
  75. </template>
  76. </el-table-column>
  77. <el-table-column label="毛利完成" align="center" min-width="90px">
  78. <template slot-scope="scope">
  79. <div :style="`${getCurrentValueStyle(scope.row.gross_completion, scope.row.cost_tips)}`">
  80. {{ unit2TenThousand(scope.row.gross_completion, isTenThound) }}
  81. </div>
  82. </template>
  83. </el-table-column>
  84. <el-table-column label="毛利完成%" align="center" min-width="85px">
  85. <template slot-scope="scope">
  86. <div :style="getCurrentRateStyle(scope.row.gross_completion_rate)">
  87. {{ scope.row.gross_completion_rate + '%' }}
  88. </div>
  89. </template>
  90. </el-table-column>
  91. <el-table-column label="成本合计" align="center" min-width="125px">
  92. <template slot-scope="scope">
  93. <div style="display:flex;flex-direction: column;">
  94. <el-popover placement="top" :width="200" trigger="hover">
  95. <div class="table-size">
  96. <p>直营/自营: {{ unit2TenThousand(scope.row.info[0][0].monthinfo.mcost_total, isTenThound) }}</p>
  97. <p>支付渠道: {{ unit2TenThousand(scope.row.info[0][1].monthinfo.mcost_total, isTenThound) }}</p>
  98. <p>供应商端: {{ unit2TenThousand(scope.row.info[0][2].monthinfo.mcost_total, isTenThound) }}</p>
  99. </div>
  100. <template #reference>
  101. <i class="el-icon-warning-outline" style="font-size:16px;cursor:pointer"></i>
  102. {{ unit2TenThousand(scope.row.mcost_total, isTenThound) }}
  103. </template>
  104. </el-popover>
  105. </div>
  106. </template>
  107. </el-table-column>
  108. <el-table-column label="实际毛利率" align="center" width="85px">
  109. <template slot-scope="scope">
  110. <!-- <div :style="`${getCurrentRateStyle(scope.row.gross_sale_completion_rate)}`"> -->
  111. {{ unit2TenThousand(scope.row.gross_sale_completion_rate) + "%" }}
  112. <!-- </div> -->
  113. </template>
  114. </el-table-column>
  115. </template>
  116. </el-table>
  117. </el-row>
  118. <template v-else>
  119. <div style="text-align:center;line-height:60px;user-select:none">当前账号没有访问权限</div>
  120. </template>
  121. </div>
  122. </template>
  123. <script>
  124. import { addition, unit2TenThousand, subtraction, multiplication, division } from "../newReport/src/_utils";
  125. import asyncRequest from "@/api/newResults";
  126. import { mapCompany } from "./mapCompany";
  127. import dayjs from "dayjs"
  128. export default {
  129. props: ['companies', 'costField', 'isTenThound'],
  130. data() {
  131. return {
  132. daytime: "",
  133. loading: false,
  134. tableData: [],
  135. companyNo: "",
  136. cp_companies: [],
  137. isEmpty: false,
  138. mapCompany
  139. }
  140. },
  141. mounted() {
  142. const hasCompose = false;
  143. const jxIndex = this.companies.findIndex((item) => item.label === "北京锦兴弘昌科技有限公司");
  144. const prIndex = this.companies.findIndex((item) => item.label === "北京普润心堂商贸有限公司");
  145. if (jxIndex !== -1 || prIndex !== -1) this.hasCompose = true;
  146. this.cp_companies = this.companies.filter((item) => item.label !== "北京锦兴弘昌科技有限公司" && item.label !== "北京普润心堂商贸有限公司")
  147. if (this.hasCompose) this.cp_companies = [...this.cp_companies, { value: "GS2302231124114965", label: "普润&锦兴&知事" }]
  148. if (this.cp_companies.length === 4) this.cp_companies = [{ value: "", label: "所有公司" }, ...this.cp_companies];
  149. this.isEmpty = this.cp_companies.length === 0;
  150. if (this.isEmpty) return;
  151. this.cp_companies = this.cp_companies.map((item, index) => {
  152. let label = '';
  153. if (item.label === '所有公司') {
  154. label = '所有公司'
  155. }
  156. if (item.label === '北京万宇恒通国际科贸有限公司') {
  157. label = '平台公司: 万宇'
  158. }
  159. if (item.label === "普润&锦兴&知事") label = `业务公司: 普润&锦兴&知事`
  160. if (label === '') label = `业务公司: ${mapCompany[item.label]}`;
  161. return ({ ...item, label })
  162. })
  163. let list = ['所有公司','平台公司: 万宇','业务公司: 百辰','业务公司: 泓源','业务公司: 普润&锦兴&知事']
  164. const cp_list = this.cp_companies.map((item) => item.label);
  165. list = list.filter(item => cp_list.includes(item));
  166. this.cp_companies = list.map((item) => this.cp_companies.find((cp_item) => cp_item.label === item));
  167. this.companyNo = this.cp_companies[0].value;
  168. this.daytime = this.transformTime();
  169. this.requestData();
  170. },
  171. methods: {
  172. unit2TenThousand,
  173. getCurrentRateStyle(current) {
  174. const days = dayjs().daysInMonth();
  175. const oneDay = Number(division(100, days)).toFixed(2);
  176. const currentDay = dayjs().date();
  177. const currentTotalTip = Number(multiplication(currentDay, oneDay)).toFixed(2);
  178. return Number(currentTotalTip) > Number(current) ? 'color:red' : ''
  179. },
  180. getCurrentValueStyle(current, total) {
  181. const days = dayjs().daysInMonth();
  182. const oneDay = Number(division(total, days)).toFixed(2);
  183. const currentDay = dayjs().date();
  184. const currentTotalTip = Number(multiplication(currentDay, oneDay)).toFixed(2);
  185. return Number(currentTotalTip) > Number(current) ? 'color: red' : '';
  186. },
  187. addDataToCompany(c1, c2) {
  188. // Number(addition(c1.total_tips,c2.total_tips)).toFixed(2)
  189. return {
  190. companyName: '普润',
  191. cost_tips: Number(c1.cost_tips).toFixed(2),
  192. total_tips: Number(c1.total_tips).toFixed(2),
  193. monthinfo: c1.monthinfo.map((item, index) => ({
  194. mcgd_th_total: Number(addition(item.mcgd_th_total, c2.monthinfo[index].mcgd_th_total)).toFixed(2),
  195. mcgd_total: Number(addition(item.mcgd_total, c2.monthinfo[index].mcgd_total)).toFixed(2),
  196. msale_total: Number(addition(item.msale_total, c2.monthinfo[index].msale_total)).toFixed(2),
  197. mth_total: Number(addition(item.mth_total, c2.monthinfo[index].mth_total)).toFixed(2),
  198. type: item.type
  199. })),
  200. dayinfo: c1.dayinfo.map((item, index) => {
  201. return {
  202. cgd_th_total: Number(addition(item.cgd_th_total, c2.dayinfo[index].cgd_th_total)).toFixed(2),
  203. cgd_total: Number(addition(item.cgd_total, c2.dayinfo[index].cgd_total)).toFixed(2),
  204. sale_total: Number(addition(item.sale_total, c2.dayinfo[index].sale_total)).toFixed(2),
  205. th_total: Number(addition(item.th_total, c2.dayinfo[index].th_total)).toFixed(2),
  206. type: item.type
  207. }
  208. }),
  209. }
  210. },
  211. setCellClassName({ column ,row }) {
  212. const { label } = column;
  213. let base = ''
  214. if(row.companyName === "北京万宇恒通国际科贸有限公司") {
  215. base += 'font-bold '
  216. }
  217. if (label === "营收目标") {
  218. base += "bg__success"
  219. return base
  220. }
  221. if ((label.indexOf('营业') !== -1 || label.indexOf('营收') !== -1) && label !== '当日营业收入') {
  222. base += "bg__success_1"
  223. return base;
  224. }
  225. if (label === "毛利目标") {
  226. base += "bg__primary"
  227. return base
  228. }
  229. if (label.indexOf('毛利') !== -1) {
  230. base += "bg__primary_1"
  231. return base;
  232. }
  233. if (label.indexOf('成本') !== -1) {
  234. base += "bg__warning_1"
  235. return base;
  236. }
  237. return base
  238. },
  239. setHeaderClassName({ column }) {
  240. const { label } = column;
  241. if (label === "营收目标") return "bg__success"
  242. if ((label.indexOf('营业') !== -1 || label.indexOf('营收') !== -1) && label !== '当日营业收入') return "bg__success_1"
  243. if (label === "毛利目标") return "bg__primary"
  244. if (label.indexOf('毛利') !== -1) return "bg__primary_1"
  245. if (label.indexOf('成本') !== -1) return "bg__warning_1"
  246. },
  247. transformTime() {
  248. let time = new Date();
  249. let y = time.getFullYear();
  250. let M = time.getMonth() + 1;
  251. let d = time.getDate();
  252. return y + "-" + (M < 10 ? "0" + M : M) + "-" + (d < 10 ? "0" + d : d);
  253. },
  254. async requestData() {
  255. this.loading = true;
  256. this.tableData = [];
  257. const res = await asyncRequest.companyEveryMonth({ daytime: this.daytime, companyNo: this.companyNo });
  258. // const jxIndex = res.data.findIndex((item) => item.companyName === "北京锦兴弘昌科技有限公司");
  259. // let prIndex = res.data.findIndex((item) => item.companyName === "北京普润心堂商贸有限公司");
  260. // let zsIndex = res.data.findIndex((item) => item.companyName === "北京知事文化产业发展有限公司")
  261. // if (jxIndex >= 0 && prIndex >= 0) {
  262. // const jxItem = res.data[jxIndex];
  263. // res.data[prIndex] = this.addDataToCompany(jxItem, res.data[prIndex]);
  264. // res.data.splice(jxIndex, 1);
  265. // }
  266. // prIndex = res.data.findIndex((item) => item.companyName === "北京普润心堂商贸有限公司");
  267. // zsIndex = res.data.findIndex((item) => item.companyName === "北京知事文化产业发展有限公司")
  268. // if (zsIndex >= 0 && prIndex >= 0) {
  269. // const zsItem = res.data[zsIndex];
  270. // res.data[prIndex] = this.addDataToCompany(zsItem, res.data[prIndex]);
  271. // res.data.splice(zsIndex, 1);
  272. // }
  273. // if (this.companyNo === "GS2302231124114965") {
  274. // const jxResult = await asyncRequest.companyEveryMonth({ daytime: this.daytime, companyNo: "GS2304031312553746" });
  275. // const zsResult = await asyncRequest.companyEveryMonth({ daytime: this.daytime, companyNo: "GS2401181650538135" });
  276. // res.data[prIndex] = this.addDataToCompany(jxResult.data[0], res.data[prIndex]);
  277. // if(zsResult.data.length !== 0) {
  278. // res.data[prIndex] = this.addDataToCompany(zsResult.data[0], res.data[prIndex]);
  279. // }
  280. // }
  281. // const isBeforeDate = this.getDiffDay() < 0 && this.companyNo === "GS2302231323386950"
  282. if (res.code === 0 && res.data && res.data.length > 0) {
  283. res.data.forEach(({ companyName, monthinfo, dayinfo, total_tips, cost_tips }) => {
  284. // monthinfo[1] = {
  285. // ...(isBeforeDate ? {
  286. // msale_total:addition(monthinfo[1].msale_total,monthinfo[2].msale_total),
  287. // mth_total:addition(monthinfo[1].mth_total,monthinfo[2].mth_total),
  288. // type:'2'
  289. // } : monthinfo[1])
  290. // }
  291. // monthinfo[0] = {
  292. // ...(isBeforeDate ? {
  293. // msale_total:0,
  294. // mth_total:0,
  295. // type:'1'
  296. // } : monthinfo[0])
  297. // }
  298. // monthinfo[2] = {
  299. // ...(isBeforeDate ? {
  300. // msale_total:0,
  301. // mth_total:0,
  302. // type:'3'
  303. // } : monthinfo[2])
  304. // }
  305. // this.companyName = companyName
  306. const mapResponseType = { '1': '自营', '2': '渠道', '3': '供应商端' }
  307. const types = Object.keys(mapResponseType)
  308. let prev_sale_total = 0;
  309. let prev_msale_total = 0;
  310. let prev_cost_total = 0;
  311. let prev_mcost_total = 0;
  312. const item = types.map(type => {
  313. const _monthinfo = monthinfo.find(month => String(month.type) === type) || {}
  314. const _dayinfo = dayinfo.find(day => String(day.type) === type) || {}
  315. /** 月营业收入 = 月销售额 - 月退货额 */
  316. const msale_total = subtraction(_monthinfo.msale_total, _monthinfo.mth_total) || 0
  317. /** 日营业收入 = 日销售额 - 日退货额 */
  318. const sale_total = subtraction(_dayinfo.sale_total, _dayinfo.th_total) || 0;
  319. /** 日成本 = 日采购额 - 日退货额 */
  320. const cost_total = subtraction(_dayinfo.cgd_total, _dayinfo.cgd_th_total) || 0;
  321. /** 月成本 = 月采购额 - 月退货额 */
  322. const mcost_total = subtraction(_monthinfo.mcgd_total, _monthinfo.mcgd_th_total) || 0;
  323. prev_sale_total = Number(addition(prev_sale_total, sale_total)).toFixed(2);
  324. prev_msale_total = Number(addition(prev_msale_total, msale_total)).toFixed(2);
  325. prev_cost_total = Number(addition(prev_cost_total, cost_total)).toFixed(2);
  326. prev_mcost_total = Number(addition(prev_mcost_total, mcost_total)).toFixed(2);
  327. return {
  328. type: mapResponseType[type],
  329. dayinfo: { ..._dayinfo, sale_total },
  330. monthinfo: { ...monthinfo, msale_total, mcost_total }
  331. }
  332. })
  333. const gross_completion = Number(subtraction(prev_msale_total, prev_mcost_total)).toFixed(2) //当月毛利完成 = 月营收 - 月成本
  334. this.tableData = [
  335. ...this.tableData,
  336. {
  337. companyName, // 公司名称
  338. cost_tips, // 当月成本指标
  339. total_tips, // 当月营收目标
  340. info: [{ ...item }], // 直营..渠道 销售
  341. sale_total: prev_sale_total, // 当日营业收入
  342. cost_total: prev_cost_total, // 当日成本总额
  343. msale_total: prev_msale_total, // 当月营业收入
  344. mcost_total: prev_mcost_total,// 当月成本总额
  345. gross_completion, //当月毛利完成 = 月营收 - 月成本
  346. completion_rate: multiplication(division(prev_msale_total, total_tips), 100).toFixed(2), // 当月应收完成率
  347. gross_completion_rate: Number(cost_tips) === 0 ? 0 : multiplication(division(gross_completion, cost_tips) || 0, 100).toFixed(2), // 当月毛利完成率
  348. gross_sale_completion_rate: Number(multiplication(division(gross_completion, prev_msale_total) || 0, 100)).toFixed(2), // 本月毛利率 = 当月毛利完成 / 当月营业收入
  349. }
  350. ]
  351. // const wanyuIndex = this.tableData.findIndex(item => item.companyName === "北京万宇恒通国际科贸有限公司")
  352. // if(wanyuIndex !== -1) {
  353. // const item = this.tableData.splice(wanyuIndex, 1);
  354. // this.tableData = [...item,...this.tableData]
  355. // }
  356. })
  357. let i = ["北京万宇恒通国际科贸有限公司","北京百辰荣达国际科贸有限公司","北京泓源广诚国际商贸有限公司",'北京普润心堂商贸有限公司'];
  358. const l = this.tableData.map(item => item.companyName);
  359. i = i.filter(item => l.includes(item));
  360. this.tableData = i.map(item => this.tableData.find(l => l.companyName === item));
  361. } else {
  362. this.tableData = [];
  363. }
  364. // this.getHeight();
  365. this.loading = false;
  366. }
  367. }
  368. }
  369. </script>
  370. <style lang="scss" scoped>
  371. .new-results {
  372. .search {
  373. height: 36px;
  374. padding: 0px 10px;
  375. margin-top: 10px;
  376. flex-wrap: wrap;
  377. }
  378. }
  379. .table-size {
  380. display: flex;
  381. width: 200px;
  382. border: 1px solid #ebeef5;
  383. flex-direction: column;
  384. p {
  385. flex: 1;
  386. border-bottom: 1px solid #ebeef5;
  387. padding: 5px 10px;
  388. margin: 0px;
  389. &:last-child {
  390. border: none;
  391. }
  392. }
  393. }
  394. </style>