robotRanking.vue 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134
  1. <template>
  2. <div class="robotRanking">
  3. <div class="sales-rank-view">
  4. <!-- 头部 -->
  5. <s-header
  6. :name="$t('robotRanking.machineSalesRanking')"
  7. :noback="true"
  8. class="modern-header"
  9. />
  10. <!-- 主体内容 -->
  11. <main class="rank-container">
  12. <!-- 筛选栏 -->
  13. <div class="filter-card">
  14. <div class="card-header">
  15. <h3 class="card-title">
  16. <span class="title-accent"></span>
  17. {{ $t("robotRanking.machineSalesRanking") }}
  18. </h3>
  19. <van-icon name="filter-o" class="filter-icon" @click="noticeClk" />
  20. </div>
  21. </div>
  22. <!-- 时间维度卡片 -->
  23. <div
  24. v-for="(period, index) in timePeriods"
  25. :key="index"
  26. class="rank-card"
  27. >
  28. <div class="period-header">
  29. <h3 class="period-title">
  30. <span class="period-icon"></span>
  31. {{ $t(`robotRanking.${period.titleKey}`) }}
  32. </h3>
  33. <kTabs
  34. :tabList="period.tabList"
  35. @tabclk="(e) => tabclk(e, period.type)"
  36. class="type-tabs"
  37. />
  38. </div>
  39. <div class="chart-wrapper">
  40. <div
  41. v-if="!totalNoData(period.type)"
  42. :ref="(el) => setChartRef(el, period.type)"
  43. class="chart-box"
  44. ></div>
  45. <kNoData v-else class="no-data-tip" />
  46. </div>
  47. </div>
  48. </main>
  49. </div>
  50. <!-- 筛选弹窗 -->
  51. <kDialog
  52. :dialogTitle="$t('orderExport.searchPop.title')"
  53. :confirmBtnTxt="$t('orderExport.searchPop.filter')"
  54. ref="kDialogRef"
  55. @confirmclk="confirmClk"
  56. >
  57. <template #content>
  58. <div class="cust_vantBorder">
  59. <van-field
  60. @click-input="changeTypeInpClk"
  61. readonly
  62. clearable
  63. v-model="searchForm.changeTypeName"
  64. :placeholder="$t('robotRanking.changeTypePlace')"
  65. :label="$t('robotRanking.changeType')"
  66. >
  67. <template #right-icon>
  68. <div class="l-flex-RC">
  69. <van-icon
  70. v-if="searchForm.changeTypeName"
  71. @click="
  72. searchForm.changeTypeName = '';
  73. searchForm.changeType = '';
  74. "
  75. class="o-mr-6"
  76. name="clear"
  77. />
  78. <van-icon @click="changeTypeInpClk" name="arrow-down" />
  79. </div>
  80. </template>
  81. </van-field>
  82. <van-field
  83. @click-input="machineTypeInpClk"
  84. readonly
  85. clearable
  86. v-model="searchForm.machineTypeName"
  87. :placeholder="$t('robotRanking.machineTypePlace')"
  88. :label="$t('robotRanking.machineType')"
  89. >
  90. <template #right-icon>
  91. <div class="l-flex-RC">
  92. <van-icon
  93. v-if="searchForm.machineTypeName"
  94. @click="
  95. searchForm.machineTypeName = '';
  96. searchForm.machineType = '';
  97. "
  98. class="o-mr-6"
  99. name="clear"
  100. />
  101. <van-icon @click="machineTypeInpClk" name="arrow-down" />
  102. </div>
  103. </template>
  104. </van-field>
  105. <van-field
  106. v-if="isAdmin()"
  107. @click-input="companyTypeInpClk"
  108. readonly
  109. clearable
  110. v-model="searchForm.companyTypeName"
  111. :placeholder="$t('robotRanking.companyTypePlaceholder')"
  112. :label="$t('robotRanking.companyTypeLabel')"
  113. >
  114. <template #right-icon>
  115. <div class="l-flex-RC">
  116. <van-icon
  117. v-if="searchForm.companyTypeName"
  118. @click="
  119. searchForm.companyTypeName = '';
  120. searchForm.companyType = '';
  121. "
  122. class="o-mr-6"
  123. name="clear"
  124. />
  125. <van-icon @click="companyTypeInpClk" name="arrow-down" />
  126. </div>
  127. </template>
  128. </van-field>
  129. </div>
  130. </template>
  131. </kDialog>
  132. <!-- 统计方式弹窗 -->
  133. <van-popup v-model:show="changeTypeShow" position="bottom">
  134. <van-picker
  135. :default-index="chgTypeDefaIdx"
  136. :title="$t('robotRanking.changeTypePlace')"
  137. :columns="changeTypePopList"
  138. :columns-field-names="yearPopFieldName"
  139. @confirm="changeTypePopConfirm"
  140. @cancel="changeTypeShow = false"
  141. />
  142. </van-popup>
  143. <!-- 设备类型弹窗 -->
  144. <van-popup v-model:show="machineTypeShow" position="bottom">
  145. <van-picker
  146. :default-index="maTypeDefaIdx"
  147. :title="$t('robotRanking.machineTypePlace')"
  148. :columns="machineTypePopList"
  149. :columns-field-names="yearPopFieldName"
  150. @confirm="machineTypePopConfirm"
  151. @cancel="machineTypeShow = false"
  152. />
  153. </van-popup>
  154. <!-- 公司平台弹窗 -->
  155. <van-popup v-model:show="companyTypeShow" position="bottom">
  156. <van-picker
  157. :default-index="maTypeDefaIdx"
  158. :title="$t('robotRanking.companyTypePlaceholder')"
  159. :columns="companyTypePopList"
  160. :columns-field-names="yearPopFieldName"
  161. @confirm="companyTypePopConfirm"
  162. @cancel="companyTypeShow = false"
  163. />
  164. </van-popup>
  165. <!-- 日的日期选择弹窗 -->
  166. <van-calendar
  167. @confirm="calendarDayConfirm"
  168. v-model:show="dayShow"
  169. color="#4d6add"
  170. :min-date="minDate"
  171. :max-date="maxDate"
  172. :show-confirm="false"
  173. />
  174. <!-- 周的日期选择弹窗 -->
  175. <van-calendar
  176. @confirm="
  177. (e) => {
  178. calendarWeekOrMonConfirm(e, 1);
  179. }
  180. "
  181. v-model:show="weekShow"
  182. :allow-same-day="true"
  183. color="#4d6add"
  184. :min-date="minDate"
  185. :max-date="maxDate"
  186. type="range"
  187. :max-range="7"
  188. />
  189. <!-- 月的日期选择弹窗 -->
  190. <van-calendar
  191. @confirm="
  192. (e) => {
  193. calendarWeekOrMonConfirm(e, 2);
  194. }
  195. "
  196. v-model:show="monthShow"
  197. :allow-same-day="true"
  198. color="#4d6add"
  199. :min-date="minDate"
  200. :max-date="maxDate"
  201. type="range"
  202. :max-range="31"
  203. />
  204. <!-- 年的日期选择弹窗 -->
  205. <van-popup v-model:show="yearShow" position="bottom">
  206. <van-picker
  207. :title="$t('robotRanking.yearPopTitle')"
  208. :columns="yearPopList"
  209. :columns-field-names="yearPopFieldName"
  210. @confirm="yearPopConfirm"
  211. @cancel="yearShow = false"
  212. />
  213. </van-popup>
  214. </div>
  215. </template>
  216. <script>
  217. // 导入无数据组件
  218. import kNoData from "../components/commom/kNoData/index.vue";
  219. import { onMounted, ref, reactive, computed, nextTick } from "vue";
  220. import sHeader from "../components/SimpleHeader";
  221. // import navBar from "../components/NavBar";
  222. import { getRankingList } from "../service/robotRanking";
  223. import { getLoginUser } from "../common/js/utils";
  224. import kTabs from "../components/commom/kTabs/index.vue";
  225. import kDialog from "../components/commom/kDialog/index.vue";
  226. import moment from "moment";
  227. import { showToast } from "vant";
  228. import { useI18n } from "vue-i18n";
  229. export default {
  230. setup() {
  231. // 引入语言
  232. const { t } = useI18n();
  233. // 日周月年的数据
  234. const totalObj = reactive({
  235. dayData: {},
  236. weekData: {},
  237. monthData: {},
  238. yearData: {},
  239. });
  240. // 日周月年图表没有数据
  241. const totalNoData = computed(() => (idx) => {
  242. let dataObj;
  243. switch (idx) {
  244. case "day":
  245. dataObj = totalObj.dayData;
  246. break;
  247. case "week":
  248. dataObj = totalObj.weekData;
  249. break;
  250. case "month":
  251. dataObj = totalObj.monthData;
  252. break;
  253. case "year":
  254. dataObj = totalObj.yearData;
  255. break;
  256. }
  257. if (!dataObj || dataObj?.categories?.length === 0) {
  258. return true;
  259. }
  260. return false;
  261. });
  262. // 统计方式弹窗
  263. const changeTypeInpClk = () => {
  264. changeTypeShow.value = true;
  265. };
  266. // 是否管理员
  267. const isAdmin = () => {
  268. return user && user.type === 0;
  269. };
  270. // 公司平台弹窗
  271. const companyTypeInpClk = () => {
  272. companyTypeShow.value = true;
  273. };
  274. // 设备类型弹窗
  275. const machineTypeInpClk = () => {
  276. machineTypeShow.value = true;
  277. };
  278. const changeTypeShow = ref(false);
  279. const machineTypeShow = ref(false);
  280. const companyTypeShow = ref(false);
  281. // 默认选中
  282. const chgTypeDefaIdx = ref(1);
  283. const maTypeDefaIdx = ref("");
  284. const changeTypePopList = [
  285. {
  286. name: t("robotRanking.patternStatistics"),
  287. id: 1,
  288. },
  289. {
  290. name: t("robotRanking.salesStatistics"),
  291. id: 0,
  292. },
  293. ];
  294. const machineTypePopList = [
  295. {
  296. name: t("robotRanking.whole"),
  297. id: "",
  298. },
  299. {
  300. name: t("robotRanking.MG"),
  301. id: "0",
  302. },
  303. {
  304. name: t("robotRanking.POP"),
  305. id: "1",
  306. },
  307. ];
  308. const companyTypePopList = [
  309. {
  310. name: t("robotRanking.whole"),
  311. id: "",
  312. },
  313. {
  314. name: t("robotRanking.sz"),
  315. id: "0",
  316. },
  317. {
  318. name: t("robotRanking.sc"),
  319. id: "1",
  320. },
  321. ];
  322. const changeTypePopConfirm = ({ selectedOptions }) => {
  323. searchForm.changeType = selectedOptions[0].id;
  324. searchForm.changeTypeName = selectedOptions[0].name;
  325. changeTypeShow.value = false;
  326. };
  327. const machineTypePopConfirm = ({ selectedOptions }) => {
  328. searchForm.machineType = selectedOptions[0].id;
  329. searchForm.machineTypeName = selectedOptions[0].name;
  330. machineTypeShow.value = false;
  331. };
  332. const companyTypePopConfirm = ({ selectedOptions }) => {
  333. searchForm.companyType = selectedOptions[0].id;
  334. searchForm.companyTypeName = selectedOptions[0].name;
  335. companyTypeShow.value = false;
  336. };
  337. // 年的日期选择弹窗
  338. // 点击确定
  339. const yearPopConfirm = ({ selectedOptions }) => {
  340. const year = selectedOptions[0].id;
  341. const startDate =
  342. moment().year(year).startOf("year").format("YYYY-MM-DD") + " 00:00:00";
  343. const endDate =
  344. moment().year(year).endOf("year").format("YYYY-MM-DD") + " 23:59:59";
  345. getEquipRankList("year", startDate, endDate);
  346. yearShow.value = false;
  347. };
  348. // 获取当前年前4年后1年
  349. let yearPopList = [];
  350. for (let i = 0; i < 3; i++) {
  351. const year = moment().subtract(i, "years").format("YYYY");
  352. yearPopList.push({
  353. name: year,
  354. id: year,
  355. });
  356. }
  357. const yearPopFieldName = reactive({
  358. text: "name",
  359. value: "id",
  360. });
  361. const yearShow = ref(false);
  362. const currentDate = ref(new Date());
  363. const yearFormatter = (type, val) => {
  364. if (type === "year") {
  365. return val;
  366. }
  367. return val;
  368. };
  369. // 周和月的日期选择弹窗
  370. const weekShow = ref(false);
  371. const monthShow = ref(false);
  372. const calendarWeekOrMonConfirm = (e, idx) => {
  373. const startDate = e[0];
  374. const endDate = e[1];
  375. let weekOrMont = "month";
  376. // 如果是周计算开始和结束日期相差多少天,因为周不能超过7天
  377. if (idx === 1) {
  378. weekOrMont = "week";
  379. const differDays = moment(endDate).diff(moment(startDate), "days");
  380. if (differDays >= 7) {
  381. showToast(t("robotRanking.seleWeekDateRangeNotSeveDays"));
  382. return;
  383. }
  384. }
  385. const startTime = moment(e[0]).format("YYYY-MM-DD") + " 00:00:00";
  386. const endTime = moment(e[1]).format("YYYY-MM-DD") + " 23:59:59";
  387. getEquipRankList(weekOrMont, startTime, endTime);
  388. if (idx === 1) {
  389. weekShow.value = false;
  390. } else {
  391. monthShow.value = false;
  392. }
  393. };
  394. // 日的日期选择弹窗
  395. // 日历选择触发
  396. const calendarDayConfirm = (e) => {
  397. const startDate = moment(e).format("YYYY-MM-DD") + " 00:00:00";
  398. const endDate = moment(e).format("YYYY-MM-DD") + " 23:59:59";
  399. getEquipRankList("day", startDate, endDate);
  400. dayShow.value = false;
  401. };
  402. // 最小日期选择
  403. const minDate = new Date(2022, 1, 1);
  404. const maxDate = new Date();
  405. const dayShow = ref(false);
  406. // 日,周,月,年的tab栏数据
  407. const tabDayList = ref([
  408. {
  409. name: t("robotRanking.today"),
  410. icon: "",
  411. id: 1,
  412. },
  413. {
  414. name: t("robotRanking.yesterday"),
  415. icon: "",
  416. id: 2,
  417. },
  418. {
  419. name: t("robotRanking.other"),
  420. icon: "arrow-down",
  421. id: 3,
  422. },
  423. ]);
  424. const tabWeekList = ref([
  425. {
  426. name: t("robotRanking.thisWeek"),
  427. icon: "",
  428. id: 1,
  429. },
  430. {
  431. name: t("robotRanking.lastWeek"),
  432. icon: "",
  433. id: 2,
  434. },
  435. {
  436. name: t("robotRanking.other"),
  437. icon: "arrow-down",
  438. id: 3,
  439. },
  440. ]);
  441. const tabMonthList = ref([
  442. {
  443. name: t("robotRanking.thisMonth"),
  444. icon: "",
  445. id: 1,
  446. },
  447. {
  448. name: t("robotRanking.lastMonth"),
  449. icon: "",
  450. id: 2,
  451. },
  452. {
  453. name: t("robotRanking.other"),
  454. icon: "arrow-down",
  455. id: 3,
  456. },
  457. ]);
  458. const tabYearList = ref([
  459. {
  460. name: t("robotRanking.thisYear"),
  461. icon: "",
  462. id: 1,
  463. },
  464. {
  465. name: t("robotRanking.lastYear"),
  466. icon: "",
  467. id: 2,
  468. },
  469. {
  470. name: t("robotRanking.other"),
  471. icon: "arrow-down",
  472. id: 3,
  473. },
  474. ]);
  475. const timePeriods = ref([
  476. {
  477. type: "day",
  478. titleKey: "dailySalesRanking",
  479. tabList: tabDayList.value,
  480. },
  481. {
  482. type: "week",
  483. titleKey: "salesRankingThisWeek",
  484. tabList: tabWeekList.value,
  485. },
  486. {
  487. type: "month",
  488. titleKey: "salesRankingThisMonth",
  489. tabList: tabMonthList.value,
  490. },
  491. {
  492. type: "year",
  493. titleKey: "salesRankingThisYear",
  494. tabList: tabYearList.value,
  495. },
  496. ]);
  497. // 默认进来的时间
  498. const defaultTime = reactive({
  499. // 今日
  500. todayStart: moment().format("YYYY-MM-DD") + " 00:00:00",
  501. todayEnd: moment().format("YYYY-MM-DD") + " 23:59:59",
  502. // 昨日
  503. colastdayStart:
  504. moment()
  505. .day(moment().day() - 1)
  506. .format("YYYY-MM-DD") + " 00:00:00",
  507. colastdayEnd:
  508. moment()
  509. .day(moment().day() - 1)
  510. .format("YYYY-MM-DD") + " 23:59:59",
  511. // 这周
  512. weekStart: moment().weekday(1).format("YYYY-MM-DD") + " 00:00:00",
  513. weekEnd: moment().weekday(7).format("YYYY-MM-DD") + " 23:59:59",
  514. // 上周
  515. lastWeekStart:
  516. moment()
  517. .week(moment().week() - 1)
  518. .startOf("week")
  519. .add(1, "days")
  520. .format("YYYY-MM-DD") + " 00:00:00",
  521. lastWeekEnd:
  522. moment()
  523. .week(moment().week() - 1)
  524. .endOf("week")
  525. .add(1, "days")
  526. .format("YYYY-MM-DD") + " 23:59:59",
  527. // 这个月
  528. monthStart: moment().startOf("month").format("YYYY-MM-DD") + " 00:00:00",
  529. monthEnd: moment().endOf("month").format("YYYY-MM-DD") + " 23:59:59",
  530. // 上个月
  531. lastmonthStart:
  532. moment()
  533. .month(moment().month() - 1)
  534. .startOf("month")
  535. .format("YYYY-MM-DD") + " 00:00:00",
  536. lastmonthEnd:
  537. moment()
  538. .month(moment().month() - 1)
  539. .endOf("month")
  540. .format("YYYY-MM-DD") + " 23:59:59",
  541. // 今年
  542. yearStart: moment().startOf("year").format("YYYY-MM-DD") + " 00:00:00",
  543. yearEnd: moment().endOf("year").format("YYYY-MM-DD") + " 23:59:59",
  544. // 上年
  545. lastYearStart:
  546. moment()
  547. .year(moment().year() - 1)
  548. .startOf("year")
  549. .format("YYYY-MM-DD") + " 00:00:00",
  550. lastYearEnd:
  551. moment()
  552. .year(moment().year() - 1)
  553. .endOf("year")
  554. .format("YYYY-MM-DD") + " 23:59:59",
  555. });
  556. // 点击tab
  557. const tabclk = (e, type) => {
  558. let startDate = "";
  559. let endDate = "";
  560. switch (type) {
  561. case "day":
  562. switch (e.name) {
  563. case 0:
  564. startDate = defaultTime.todayStart;
  565. endDate = defaultTime.todayEnd;
  566. break;
  567. case 1:
  568. startDate = defaultTime.colastdayStart;
  569. endDate = defaultTime.colastdayEnd;
  570. break;
  571. case 2:
  572. // 打开日历弹窗
  573. dayShow.value = true;
  574. break;
  575. }
  576. break;
  577. case "week":
  578. switch (e.name) {
  579. case 0:
  580. startDate = defaultTime.weekStart;
  581. endDate = defaultTime.weekEnd;
  582. break;
  583. case 1:
  584. startDate = defaultTime.lastWeekStart;
  585. endDate = defaultTime.lastWeekEnd;
  586. break;
  587. case 2:
  588. // 打开周的日期选择弹窗
  589. weekShow.value = true;
  590. break;
  591. }
  592. break;
  593. case "month":
  594. switch (e.name) {
  595. case 0:
  596. startDate = defaultTime.monthStart;
  597. endDate = defaultTime.monthEnd;
  598. break;
  599. case 1:
  600. startDate = defaultTime.lastmonthStart;
  601. endDate = defaultTime.lastmonthEnd;
  602. break;
  603. case 2:
  604. // 打开月的日期选择弹窗
  605. monthShow.value = true;
  606. break;
  607. }
  608. break;
  609. case "year":
  610. switch (e.name) {
  611. case 0:
  612. startDate = defaultTime.yearStart;
  613. endDate = defaultTime.yearEnd;
  614. break;
  615. case 1:
  616. startDate = defaultTime.lastYearStart;
  617. endDate = defaultTime.lastYearEnd;
  618. break;
  619. case 2:
  620. // 打开年的日期弹窗
  621. yearShow.value = true;
  622. break;
  623. }
  624. break;
  625. }
  626. // 如果是点击其他,不用请求
  627. if (e.name !== 2) {
  628. // 获取日、周、月、年的排行数据
  629. getEquipRankList(type, startDate, endDate);
  630. }
  631. };
  632. const changeType = ref(0);
  633. const companyType = ref(""); // 公司平台
  634. const machineType = ref(""); // 设备类型
  635. let chartObj = {
  636. day: null,
  637. week: null,
  638. month: null,
  639. year: null,
  640. };
  641. // 获取日、周、月、年的排行数据
  642. const getEquipRankList = (chartType, startDate, endDate) => {
  643. let param = {
  644. adminId: user.id,
  645. ifForeign: user.ifForeign,
  646. changeType: changeType.value, //必填,默认为一0:销售额统计,1:花型统计
  647. companyType: companyType.value,
  648. machineType: machineType.value,
  649. chartType,
  650. startDate,
  651. endDate,
  652. };
  653. getRankingList(param).then(async (res) => {
  654. const { data } = res;
  655. switch (chartType) {
  656. case "day":
  657. totalObj.dayData = data.data;
  658. break;
  659. case "week":
  660. totalObj.weekData = data.data;
  661. break;
  662. case "month":
  663. totalObj.monthData = data.data;
  664. break;
  665. case "year":
  666. totalObj.yearData = data.data;
  667. break;
  668. }
  669. // 有数据返回则处理
  670. if (data.code && data.data) {
  671. // echarts的配置
  672. const chartOption = {
  673. tooltip: {
  674. trigger: "axis",
  675. axisPointer: {
  676. type: "shadow",
  677. },
  678. borderWidth: 1,
  679. textStyle: {
  680. fontSize: 12,
  681. },
  682. },
  683. // 固定屏幕显示多少个,其余的滑动
  684. dataZoom: [
  685. // {
  686. // type: 'slider',
  687. // xAxisIndex: 0,
  688. // filterMode: 'none',
  689. // // 开始的值
  690. // startValue: null,
  691. // // 结束的值
  692. // endValue: null,
  693. // // 锁定滑动的区域
  694. // // zoomLock:true,
  695. // },
  696. {
  697. type: "inside",
  698. xAxisIndex: 0,
  699. filterMode: "none",
  700. startValue: null,
  701. endValue: null,
  702. zoomLock: true,
  703. },
  704. ],
  705. grid: {
  706. left: "5%",
  707. right: "5%",
  708. bottom: "10%",
  709. containLabel: true,
  710. },
  711. legend: {
  712. bottom: 0,
  713. right: 10,
  714. itemWidth: 10,
  715. itemHeight: 10,
  716. icon: "rect",
  717. },
  718. yAxis: { type: "value" },
  719. xAxis: {
  720. type: "category",
  721. axisLabel: {
  722. rotate: 20,
  723. },
  724. data: [],
  725. },
  726. series: [],
  727. };
  728. await nextTick();
  729. let chartBox;
  730. switch (chartType) {
  731. case "day":
  732. chartBox = dayChartBox.value;
  733. break;
  734. case "week":
  735. chartBox = weekChartBox.value;
  736. break;
  737. case "month":
  738. chartBox = monthChartBox.value;
  739. break;
  740. case "year":
  741. chartBox = yearChartBox.value;
  742. break;
  743. }
  744. if (chartBox) {
  745. const dayChartOption = Object.assign({}, chartOption);
  746. dayChartOption.xAxis.data = data.data.categories;
  747. dayChartOption.series = [
  748. {
  749. ...data.data.series[0],
  750. type: "bar",
  751. itemStyle: { color: "#e59a6d" },
  752. name: t("home.productNum"),
  753. label: {
  754. show: true,
  755. position: "top",
  756. },
  757. },
  758. {
  759. ...data.data.series[1],
  760. type: "bar",
  761. itemStyle: { color: "#4d6add" },
  762. name: t("home.salesAmount"),
  763. label: {
  764. show: true,
  765. position: "top",
  766. },
  767. },
  768. ];
  769. dayChartOption.dataZoom[0]["startValue"] = data.data.categories[0];
  770. dayChartOption.dataZoom[0]["endValue"] = data.data.categories[3];
  771. // 解决resize不起作用,配合onMounted里的方法使用
  772. switch (chartType) {
  773. case "day":
  774. chartObj.day = window.echarts.init(chartBox, null, {
  775. renderer: "canvas",
  776. useDirtyRect: false,
  777. });
  778. chartObj.day && chartObj.day.setOption(dayChartOption);
  779. break;
  780. case "week":
  781. chartObj.week = window.echarts.init(chartBox, null, {
  782. renderer: "canvas",
  783. useDirtyRect: false,
  784. });
  785. chartObj.week && chartObj.week.setOption(dayChartOption);
  786. break;
  787. case "month":
  788. chartObj.month = window.echarts.init(chartBox, null, {
  789. renderer: "canvas",
  790. useDirtyRect: false,
  791. });
  792. chartObj.month && chartObj.month.setOption(dayChartOption);
  793. break;
  794. case "year":
  795. chartObj.year = window.echarts.init(chartBox, null, {
  796. renderer: "canvas",
  797. useDirtyRect: false,
  798. });
  799. chartObj.year && chartObj.year.setOption(dayChartOption);
  800. break;
  801. }
  802. }
  803. }
  804. });
  805. };
  806. const user = getLoginUser();
  807. // 日、周、月、年的图表
  808. const dayChartBox = ref();
  809. const weekChartBox = ref();
  810. const monthChartBox = ref();
  811. const yearChartBox = ref();
  812. // 初始化页面获取列表
  813. onMounted(async () => {
  814. // 默认筛选选中
  815. searchForm.changeTypeName = t("robotRanking.salesStatistics");
  816. searchForm.changeType = 0;
  817. searchForm.companyTypeName = t("robotRanking.whole");
  818. searchForm.machineTypeName = t("robotRanking.whole");
  819. init();
  820. window.addEventListener("resize", () => {
  821. chartObj.day && chartObj.day.resize();
  822. chartObj.week && chartObj.week.resize();
  823. chartObj.month && chartObj.month.resize();
  824. chartObj.year && chartObj.year.resize();
  825. });
  826. });
  827. // 刚进页面获取
  828. const init = () => {
  829. // 获取日的排行数据
  830. getEquipRankList("day", defaultTime.todayStart, defaultTime.todayEnd);
  831. // 获取周的排行数据
  832. getEquipRankList("week", defaultTime.weekStart, defaultTime.weekEnd);
  833. // 获取月的排行数据
  834. getEquipRankList("month", defaultTime.monthStart, defaultTime.monthEnd);
  835. // 获取年的排行数据
  836. getEquipRankList("year", defaultTime.yearStart, defaultTime.yearEnd);
  837. };
  838. const searchForm = reactive({
  839. businessName: "",
  840. changeTypeName: "",
  841. changeType: "",
  842. machineTypeName: "",
  843. machineType: "",
  844. companyTypeName: "",
  845. companyType: "",
  846. });
  847. const kDialogRef = ref(null);
  848. const busiPopShow = ref(false);
  849. const busiPopList = reactive([
  850. {
  851. name: "商户1",
  852. id: "1",
  853. },
  854. {
  855. name: "商户2",
  856. id: "2",
  857. },
  858. ]);
  859. const busiPopFieldName = reactive({
  860. text: "name",
  861. value: "id",
  862. });
  863. // 点击筛选
  864. const noticeClk = () => {
  865. kDialogRef.value.openDialog();
  866. };
  867. // 点击右侧按钮
  868. const confirmClk = () => {
  869. if (searchForm.changeType === "") {
  870. changeType.value = 1;
  871. } else {
  872. changeType.value = searchForm.changeType;
  873. }
  874. companyType.value = searchForm.companyType;
  875. machineType.value = searchForm.machineType;
  876. init();
  877. };
  878. // 筛选弹窗
  879. // 点击商家输入框
  880. const busiInpClk = () => {
  881. busiPopShow.value = true;
  882. };
  883. const busiPopConfirm = (e) => {
  884. busiPopShow.value = false;
  885. searchForm.businessName = e.name;
  886. };
  887. const busiPopCancel = () => {
  888. busiPopShow.value = false;
  889. };
  890. const setChartRef = (el, type) => {
  891. if (el) {
  892. switch (type) {
  893. case "day":
  894. dayChartBox.value = el;
  895. break;
  896. case "week":
  897. weekChartBox.value = el;
  898. break;
  899. case "month":
  900. monthChartBox.value = el;
  901. break;
  902. case "year":
  903. yearChartBox.value = el;
  904. break;
  905. }
  906. }
  907. };
  908. return {
  909. dayChartBox,
  910. weekChartBox,
  911. monthChartBox,
  912. yearChartBox,
  913. tabclk,
  914. tabDayList,
  915. tabWeekList,
  916. tabMonthList,
  917. tabYearList,
  918. noticeClk,
  919. kDialogRef,
  920. busiInpClk,
  921. busiPopShow,
  922. busiPopConfirm,
  923. busiPopList,
  924. busiPopFieldName,
  925. busiPopCancel,
  926. confirmClk,
  927. searchForm,
  928. dayShow,
  929. minDate,
  930. maxDate,
  931. calendarDayConfirm,
  932. weekShow,
  933. monthShow,
  934. calendarWeekOrMonConfirm,
  935. yearFormatter,
  936. currentDate,
  937. yearShow,
  938. yearPopList,
  939. yearPopConfirm,
  940. yearPopFieldName,
  941. changeTypeShow,
  942. changeTypePopList,
  943. changeTypePopConfirm,
  944. changeTypeInpClk,
  945. companyTypeInpClk,
  946. companyTypePopConfirm,
  947. companyTypePopList,
  948. companyTypeShow,
  949. machineTypeInpClk,
  950. machineTypePopConfirm,
  951. machineTypePopList,
  952. machineTypeShow,
  953. totalNoData,
  954. maTypeDefaIdx,
  955. chgTypeDefaIdx,
  956. isAdmin,
  957. timePeriods,
  958. setChartRef,
  959. };
  960. },
  961. components: { sHeader, kTabs, kDialog, kNoData },
  962. };
  963. </script>
  964. <style lang="less" scoped>
  965. @primary-color: #4d6add;
  966. @text-primary: #2d3542;
  967. @text-secondary: #64748b;
  968. @border-color: #e4e7ec;
  969. @card-bg: #ffffff;
  970. .sales-rank-view {
  971. background: #f8f9fb;
  972. height: calc(100% - 50px);
  973. overflow: auto;
  974. overflow-x: hidden;
  975. }
  976. .modern-header {
  977. box-shadow: 0 2px 12px rgba(@primary-color, 0.1);
  978. }
  979. .rank-container {
  980. padding: 0 10px;
  981. }
  982. .filter-card {
  983. background: @card-bg;
  984. border-radius: 12px;
  985. box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
  986. overflow: hidden;
  987. margin: 10px 0;
  988. }
  989. .card-header {
  990. padding: 16px 24px;
  991. display: flex;
  992. align-items: center;
  993. justify-content: space-between;
  994. }
  995. .card-title {
  996. display: flex;
  997. align-items: center;
  998. margin: 0;
  999. font-size: 16px;
  1000. color: @text-primary;
  1001. font-weight: 600;
  1002. .title-accent {
  1003. width: 4px;
  1004. height: 18px;
  1005. background: @primary-color;
  1006. border-radius: 2px;
  1007. margin-right: 12px;
  1008. }
  1009. }
  1010. .filter-icon {
  1011. font-size: 22px;
  1012. color: @text-primary;
  1013. transition: all 0.3s ease;
  1014. padding: 8px;
  1015. cursor: pointer;
  1016. &:hover {
  1017. color: @primary-color;
  1018. transform: rotate(90deg) scale(1.1);
  1019. }
  1020. &:active {
  1021. transform: rotate(90deg) scale(0.9);
  1022. }
  1023. }
  1024. @media (max-width: 768px) {
  1025. .card-header {
  1026. padding: 12px 16px;
  1027. }
  1028. .filter-icon {
  1029. font-size: 18px;
  1030. padding: 6px;
  1031. }
  1032. }
  1033. .rank-card {
  1034. background: white;
  1035. border-radius: 12px;
  1036. box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
  1037. margin-bottom: 10px;
  1038. height: 400px;
  1039. overflow: hidden;
  1040. .period-header {
  1041. padding: 16px;
  1042. .period-title {
  1043. display: flex;
  1044. align-items: center;
  1045. margin: 0 0 12px;
  1046. font-size: 16px;
  1047. color: @text-primary;
  1048. .period-icon {
  1049. width: 6px;
  1050. height: 6px;
  1051. background: @primary-color;
  1052. border-radius: 50%;
  1053. margin-right: 10px;
  1054. }
  1055. }
  1056. .type-tabs {
  1057. width: 100% !important;
  1058. }
  1059. }
  1060. .chart-wrapper {
  1061. padding: 0 16px 16px;
  1062. height: 400px;
  1063. .chart-box {
  1064. height: 300px;
  1065. width: 100%;
  1066. border-radius: 8px;
  1067. overflow: hidden;
  1068. }
  1069. .no-data-tip {
  1070. height: 300px;
  1071. }
  1072. }
  1073. }
  1074. /* 响应式设计 */
  1075. @media (max-width: 768px) {
  1076. .rank-container {
  1077. padding: 0 10px;
  1078. }
  1079. .rank-card {
  1080. margin-bottom: 10px;
  1081. .chart-wrapper {
  1082. height: 250px;
  1083. padding: 0 12px 12px;
  1084. }
  1085. }
  1086. .filter-container {
  1087. .rank-title {
  1088. font-size: 16px;
  1089. }
  1090. .filter-btn {
  1091. padding: 6px 16px;
  1092. font-size: 13px;
  1093. }
  1094. }
  1095. }
  1096. </style>