index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. <script lang="ts" setup>
  2. import { ref, reactive, onMounted, watchEffect } from 'vue';
  3. import TrendCard from './components/TrendCard.vue';
  4. import SimpleCard from './components/SimpleCard.vue';
  5. import Divider from './components/Divider.vue';
  6. import Echarts from '@/components/ReEcharts/index.vue';
  7. import { EChartsOption, use } from 'echarts';
  8. import { dayjs } from 'element-plus';
  9. import { useStore } from "../../store/index"
  10. const { userInfo } = useStore()
  11. import {
  12. getSummaryData,
  13. getBulletinData,
  14. BulletinData,
  15. getStageData,
  16. RequestProps,
  17. StageData,
  18. SummaryData
  19. } from './api';
  20. const dataAnalysisFlag = userInfo?.moduleList?.filter((item: any) => item.path == '/analysis') || []
  21. const isExistBusiness = sessionStorage.getItem("isExistBusiness");
  22. const businessLabel = isExistBusiness === "1" ? "商机" : "项目";
  23. const permissionOptions = dataAnalysisFlag.length > 0 ? [
  24. {
  25. label: '仅本人',
  26. value: 0
  27. },
  28. {
  29. label: '本人及下属',
  30. value: 1
  31. },
  32. {
  33. label: '仅本部门',
  34. value: 2
  35. },
  36. {
  37. label: '本部门及下属部门',
  38. value: 3
  39. }
  40. ] : [{
  41. label: '仅本人',
  42. value: 0
  43. },]
  44. const dateOptions = [
  45. {
  46. label: '本月',
  47. value: 0
  48. },
  49. {
  50. label: '本周',
  51. value: 1
  52. },
  53. {
  54. label: '本年',
  55. value: 2
  56. }
  57. ];
  58. type PromptType = {
  59. permission?: 0 | 1 | 2 | 3;
  60. date?: 0 | 1 | 2 | any;
  61. sliceDate: [Date, Date];
  62. };
  63. const select1 = ref<HTMLDivElement>();
  64. const select2 = ref<HTMLDivElement>();
  65. const select3 = ref<HTMLDivElement>();
  66. const bulletinPrompt = reactive<PromptType>({
  67. permission: 0,
  68. date: 0,
  69. sliceDate: [new Date(), new Date()]
  70. });
  71. const summaryPrompt = reactive<PromptType>({
  72. permission: 0,
  73. date: 0,
  74. sliceDate: [new Date(), new Date()]
  75. });
  76. const stagePrompt = reactive<PromptType>({
  77. permission: 0,
  78. date: 0,
  79. sliceDate: [new Date(), new Date()]
  80. });
  81. const requestData = reactive<{
  82. bulletin: BulletinData['data'] | null;
  83. summary: SummaryData['data'] | null;
  84. stage: StageData['data'] | null;
  85. }>({
  86. summary: null,
  87. stage: null,
  88. bulletin: null
  89. });
  90. const chartOptions: EChartsOption = reactive({
  91. grid: { top: 0, bottom: 20, left: 60 },
  92. yAxis: {
  93. type: 'category'
  94. },
  95. xAxis: {
  96. type: 'value'
  97. },
  98. series: [
  99. {
  100. barWidth: 20,
  101. data: [10, 30, 30, 30, 30],
  102. type: 'bar'
  103. }
  104. ]
  105. });
  106. const queryBulletin = async (payload?: RequestProps) => {
  107. const bulletinResult = await getBulletinData(payload);
  108. requestData.bulletin = bulletinResult.data;
  109. };
  110. const querySummary = async (payload?: RequestProps) => {
  111. const summaryResult = await getSummaryData(payload);
  112. requestData.summary = summaryResult.data;
  113. };
  114. const queryStage = async (payload?: RequestProps) => {
  115. const stageResult = await getStageData(payload);
  116. const data = stageResult.data;
  117. // @ts-ignore
  118. chartOptions.yAxis.data = data.dataMap.map((map) => map.stageName);
  119. // @ts-ignore
  120. chartOptions.series[0].data = data.dataMap.map((map) => map.num);
  121. };
  122. watchEffect(() => {
  123. queryBulletin({
  124. ...(bulletinPrompt.date === ('ignore' as any)
  125. ? {
  126. startDate: dayjs(bulletinPrompt.sliceDate[0]).format('YYYY-MM-DD'),
  127. endDate: dayjs(bulletinPrompt.sliceDate[1]).format('YYYY-MM-DD')
  128. }
  129. : { dateType: bulletinPrompt.date }),
  130. queryType: bulletinPrompt.permission
  131. });
  132. });
  133. watchEffect(() => {
  134. queryStage({
  135. ...(stagePrompt.date === ('ignore' as any)
  136. ? {
  137. startDate: dayjs(stagePrompt.sliceDate[0]).format('YYYY-MM-DD'),
  138. endDate: dayjs(stagePrompt.sliceDate[1]).format('YYYY-MM-DD')
  139. }
  140. : { dateType: stagePrompt.date }),
  141. queryType: stagePrompt.permission
  142. });
  143. });
  144. watchEffect(() => {
  145. querySummary({
  146. ...(summaryPrompt.date === ('ignore' as any)
  147. ? {
  148. startDate: dayjs(summaryPrompt.sliceDate[0]).format('YYYY-MM-DD'),
  149. endDate: dayjs(summaryPrompt.sliceDate[1]).format('YYYY-MM-DD')
  150. }
  151. : { dateType: summaryPrompt.date }),
  152. queryType: summaryPrompt.permission
  153. });
  154. });
  155. watchEffect(() => {
  156. console.log(bulletinPrompt.date, '-----', bulletinPrompt.sliceDate, '88888888---watchEffect');
  157. });
  158. </script>
  159. <template>
  160. <div class="m-5 bg-white min-h-full p-4 rounded">
  161. <section>
  162. <!-- <div class="h-[800px] w-full bg-[red] mb-5">
  163. <iframe
  164. src="http://47.101.180.183:9080/webapp/agent"
  165. style="border: none;"
  166. class="w-full h-full"
  167. />
  168. </div> -->
  169. <div class="flex gap-3 mb-4">
  170. <div class="w-40">
  171. <el-select
  172. size="small"
  173. :model-value="bulletinPrompt.permission"
  174. @change="(value: any) => (bulletinPrompt.permission = value)"
  175. >
  176. <el-option
  177. v-for="permission in permissionOptions"
  178. :label="permission.label"
  179. :value="permission.value"
  180. />
  181. </el-select>
  182. </div>
  183. <div class="w-40">
  184. <el-select
  185. ref="select1"
  186. size="small"
  187. :model-value="bulletinPrompt.date"
  188. @change="(value: any) => (bulletinPrompt.date = value)"
  189. >
  190. <el-option v-for="date in dateOptions" :label="date.label" :value="date.value" />
  191. <el-option label="自定义" value="ignore" disabled>
  192. <div class="flex gap-2 w-80">
  193. <el-date-picker
  194. size="small"
  195. :clearable="false"
  196. type="daterange"
  197. class="w-12"
  198. v-model="bulletinPrompt.sliceDate"
  199. start-placeholder="开始日期"
  200. end-placeholder="结束日期"
  201. />
  202. <el-button
  203. size="small"
  204. type="primary"
  205. @click="
  206. () => {
  207. select1?.blur();
  208. bulletinPrompt.date = 'ignore';
  209. }
  210. "
  211. >确认</el-button
  212. >
  213. </div>
  214. </el-option>
  215. </el-select>
  216. </div>
  217. </div>
  218. <div class="border-gray-200 border rounded p-3">
  219. <div class="flex gap-1.5 items-center mb-3">
  220. <img width="16" src="../../assets/trend.png" />
  221. <div class="text-sm font-medium">销售简报</div>
  222. <el-icon><QuestionFilled class="text-gray-500 text-sm" /></el-icon>
  223. </div>
  224. <div class="grid xl:grid-cols-4 lg:grid-cols-3 grid-cols-2 gap-4">
  225. <TrendCard
  226. title="新增客户"
  227. unit="人"
  228. :selectVal="bulletinPrompt"
  229. :number="requestData?.bulletin?.custom.customCount"
  230. :compare="requestData?.bulletin?.custom.customPromote"
  231. />
  232. <TrendCard
  233. title="新增联系人"
  234. unit="人"
  235. :selectVal="bulletinPrompt"
  236. :number="requestData?.bulletin?.contacts.contactsCount"
  237. :compare="requestData?.bulletin?.contacts.contactsPromote"
  238. />
  239. <TrendCard
  240. :title="`新增${businessLabel}`"
  241. unit="个"
  242. :selectVal="bulletinPrompt"
  243. :number="requestData?.bulletin?.businessOpportunity.businessOpportunityCount"
  244. :compare="requestData?.bulletin?.businessOpportunity.businessOpportunityPromote"
  245. />
  246. <TrendCard
  247. title="新增销售订单"
  248. unit="个"
  249. :selectVal="bulletinPrompt"
  250. :number="requestData?.bulletin?.salesOrder.salesOrderCount"
  251. :compare="requestData?.bulletin?.salesOrder.salesOrderPromote"
  252. />
  253. <TrendCard
  254. title="销售订单金额"
  255. unit="元"
  256. :selectVal="bulletinPrompt"
  257. :number="requestData?.bulletin?.salesOrdersPrice.salesOrdersPrice"
  258. :compare="requestData?.bulletin?.salesOrdersPrice.salesOrderPricePromote"
  259. />
  260. <TrendCard
  261. :title="`${businessLabel}金额`"
  262. unit="元"
  263. :selectVal="bulletinPrompt"
  264. :number="requestData?.bulletin?.businessOpportunityPrice.businessOpportunityPrice"
  265. :compare="requestData?.bulletin?.businessOpportunityPrice.businessOpportunityPromote"
  266. />
  267. <TrendCard
  268. title="新增线索"
  269. unit="个"
  270. :selectVal="bulletinPrompt"
  271. :number="requestData?.bulletin?.clue.clueCount"
  272. :compare="requestData?.bulletin?.clue.cluePromote"
  273. />
  274. </div>
  275. </div>
  276. <div class="my-8 flex gap-4 items-start">
  277. <div class="border-gray-200 border rounded p-3 flex-1">
  278. <div class="text-sm font-medium">数据汇总</div>
  279. <div class="flex gap-3 mb-8 mt-2">
  280. <div class="w-40">
  281. <el-select
  282. clearable
  283. size="small"
  284. :model-value="summaryPrompt.permission"
  285. @change="(value: any) => (summaryPrompt.permission = value)"
  286. >
  287. <el-option
  288. v-for="permission in permissionOptions"
  289. :label="permission.label"
  290. :value="permission.value"
  291. />
  292. </el-select>
  293. </div>
  294. <div class="w-40">
  295. <el-select
  296. ref="select2"
  297. clearable
  298. size="small"
  299. :model-value="summaryPrompt.date"
  300. @change="(value: any) => (summaryPrompt.date = value)"
  301. >
  302. <el-option v-for="date in dateOptions" :label="date.label" :value="date.value" />
  303. <el-option label="自定义" value="ignore" disabled>
  304. <div class="flex gap-2 w-80">
  305. <el-date-picker
  306. size="small"
  307. :clearable="false"
  308. type="daterange"
  309. class="w-12"
  310. v-model="summaryPrompt.sliceDate"
  311. start-placeholder="开始日期"
  312. end-placeholder="结束日期"
  313. />
  314. <el-button
  315. size="small"
  316. type="primary"
  317. @click="
  318. () => {
  319. select2?.blur();
  320. summaryPrompt.date = 'ignore';
  321. }
  322. "
  323. >确认</el-button
  324. >
  325. </div>
  326. </el-option>
  327. </el-select>
  328. </div>
  329. </div>
  330. <Divider title="客户汇总" />
  331. <div class="my-6 grid grid-cols-4 gap-2">
  332. <SimpleCard
  333. title="新增客户"
  334. unit="个"
  335. :number="requestData.summary?.customDataSummary.newNum"
  336. />
  337. <SimpleCard
  338. title="转成交客户"
  339. unit="个"
  340. :number="requestData.summary?.customDataSummary.closeDealNum"
  341. v-if="false"
  342. />
  343. </div>
  344. <Divider :title="`${businessLabel}汇总`" />
  345. <div class="my-6 grid grid-cols-4 gap-2">
  346. <SimpleCard
  347. :title="`新增${businessLabel}`"
  348. unit="个"
  349. :number="requestData.summary?.businessOpportunityDataSummary.newNum"
  350. />
  351. <SimpleCard
  352. :title="`赢单${businessLabel}`"
  353. unit="个"
  354. :number="requestData.summary?.businessOpportunityDataSummary.winning"
  355. />
  356. <SimpleCard
  357. :title="`输单${businessLabel}`"
  358. unit="个"
  359. :number="requestData.summary?.businessOpportunityDataSummary.losting"
  360. />
  361. <SimpleCard
  362. :title="`${businessLabel}总金额`"
  363. unit="元"
  364. :number="requestData.summary?.businessOpportunityDataSummary.allAmountOfMoney"
  365. />
  366. </div>
  367. <Divider title="线索汇总" />
  368. <div class="my-6 grid grid-cols-4 gap-2">
  369. <SimpleCard
  370. title="新增线索"
  371. unit="个"
  372. :number="requestData.summary?.clueDataSummary.newNum"
  373. />
  374. <SimpleCard
  375. :title="`线索转${businessLabel}`"
  376. unit="个"
  377. :number="requestData.summary?.clueDataSummary.changeNum"
  378. />
  379. </div>
  380. </div>
  381. <div class="border-gray-200 border rounded p-3 flex-1">
  382. <div class="text-sm font-medium">{{ `${businessLabel}阶段` }}</div>
  383. <div class="flex gap-3 mb-8 mt-2">
  384. <div class="w-40">
  385. <el-select
  386. clearable
  387. size="small"
  388. :model-value="stagePrompt.permission"
  389. @change="(value: any) => (stagePrompt.permission = value)"
  390. >
  391. <el-option
  392. v-for="permission in permissionOptions"
  393. :label="permission.label"
  394. :value="permission.value"
  395. />
  396. </el-select>
  397. </div>
  398. <div class="w-40">
  399. <el-select
  400. ref="select3"
  401. clearable
  402. size="small"
  403. :model-value="stagePrompt.date"
  404. @change="(value: any) => (stagePrompt.date = value)"
  405. >
  406. <el-option v-for="date in dateOptions" :label="date.label" :value="date.value" />
  407. <el-option label="自定义" value="ignore" disabled>
  408. <div class="flex gap-2 w-80">
  409. <el-date-picker
  410. size="small"
  411. :clearable="false"
  412. type="daterange"
  413. class="w-12"
  414. v-model="stagePrompt.sliceDate"
  415. start-placeholder="开始日期"
  416. end-placeholder="结束日期"
  417. />
  418. <el-button
  419. size="small"
  420. type="primary"
  421. @click="
  422. () => {
  423. select3?.blur();
  424. stagePrompt.date = 'ignore';
  425. }
  426. "
  427. >确认</el-button
  428. >
  429. </div>
  430. </el-option>
  431. </el-select>
  432. </div>
  433. </div>
  434. <div class="h-60">
  435. <Echarts :option="chartOptions"></Echarts>
  436. </div>
  437. </div>
  438. </div>
  439. </section>
  440. </div>
  441. </template>