department.vue 15 KB

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