SeoPdfExport.vue 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109
  1. <template>
  2. <div class="content">
  3. <a-card :bordered="false" :body-style="{padding:0}">
  4. <div class="step1" v-if="flag">
  5. <a-divider>SEO月报生成及下载</a-divider>
  6. <a-form
  7. ref="formRef"
  8. :model="formState"
  9. :rules="rules"
  10. :label-col="labelCol"
  11. :wrapper-col="wrapperCol"
  12. style="width: 80%"
  13. >
  14. <a-button type="primary" @click="handleEdit" preIcon="ant-design:edit-outlined"
  15. class="btn3">修改话术
  16. </a-button>
  17. <SeoPdfExportModal @register="registerModal" @success="handleSuccess"></SeoPdfExportModal>
  18. <a-form-item label="站点名称" name="siteName">
  19. <a-select
  20. label-in-value
  21. v-model:value="formState.siteName"
  22. placeholder="请选择站点"
  23. showSearch
  24. style="width: 100%"
  25. :options="siteOptions"
  26. :filter-option="filterOption"
  27. @change="handleChange"
  28. :field-names="{ label: 'name', value: 'code' }"
  29. ></a-select>
  30. </a-form-item>
  31. <a-form-item label="客户名称" name="customerName">
  32. <a-input placeholder="请输入客户名称" v-model:value="formState.customerName"
  33. allowClear></a-input>
  34. </a-form-item>
  35. <a-form-item label="查询时间" name="range">
  36. <a-date-picker v-model:value="formState.range"
  37. :disabled-date="disabledDate"
  38. placeholder="请选择月份"
  39. style="width: 100%;"
  40. format="YYYY-MM"
  41. picker="month" />
  42. </a-form-item>
  43. <a-form-item style="text-align: center" :wrapper-col="{ span: 20, offset: 4 }">
  44. <a-button @click="onSubmit" type="primary" preIcon="ant-design:search-outlined">查询
  45. </a-button>
  46. <a-button @click="resetForm" type="default" preIcon="ant-design:reload-outlined"
  47. style="margin-left: 8px">
  48. 重置
  49. </a-button>
  50. </a-form-item>
  51. </a-form>
  52. </div>
  53. <div class="step2" v-if="!flag">
  54. <a-spin :spinning="infoLoading">
  55. <section class="top">
  56. <h1>{{ formState.customerName }} - SEO月度报告
  57. <a-button type="default" @click="handleEdit" preIcon="ant-design:edit-outlined"
  58. class="btn6">修改话术
  59. </a-button>
  60. <a-button type="primary" preIcon="ant-design:file-pdf" @click="handleExportPdf"
  61. class="btn1">导出pdf
  62. </a-button>
  63. <a-button type="default" preIcon="ant-design:reload" @click="goBack" class="btn2">
  64. 返回
  65. </a-button>
  66. </h1>
  67. <p style='margin-top: 15px'>
  68. 生成周期:{{ dayjs(formState.range).startOf("month").format("YYYY-MM-DD")
  69. }}至{{ dayjs(formState.range).endOf("month").format("YYYY-MM-DD")
  70. }}</p>
  71. </section>
  72. <section class="s1">
  73. <p class="p1" style="font-size: 15px;margin-top: 15px">
  74. 尊敬的【{{ formState.siteName.label
  75. }}】:</p>
  76. <p class="p1" style="text-indent: 28px">
  77. 我非常高兴向您呈上{{ dayjs(formState.range).format("YYYY-MM")
  78. }}月的SEO月报,为您提供网站在SEO中的详尽数据和分析。作为您的SEO团队,一直努力追求卓越的结果,以确保您的在线业务能够获得更大的曝光度和持续增长.</p>
  79. <p class="p1" style="text-indent: 28px">
  80. 在过去的一个月里,我们积极地致力于提升您的网站的可见性和排名,并进行了一系列优化策略的实施。具体细节请参考以下报告细节。</p>
  81. <h2 class="title mb">当月优化总结<span class="red">*</span></h2>
  82. <p class="tips" style="margin: -10px 0 10px;">
  83. 在过去的1个月里,我们致力于为您的网站进行全面的SEO优化工作。通过对关键词排名、网站结构、内容优化以及用户体验等方面的深入分析和优化,以下是我们的SEO优化总结:</p>
  84. <div class="num-suffix">
  85. <a-textarea :style="detailInfo.thisMonth.length > 1000 ? 'border-color:red':''"
  86. auto-size placeholder="请输入"
  87. v-model:value="detailInfo.thisMonth"></a-textarea>
  88. <span :style="detailInfo.thisMonth.length > 1000 ? 'color:red':''"
  89. class="suffix-self">
  90. {{ detailInfo.thisMonth.length }}/1000
  91. </span>
  92. </div>
  93. <h2 class="title mb">下月优化计划<span class="red">*</span></h2>
  94. <p class="tips" style="margin: -10px 0 10px;">
  95. 感谢您对我们的SEO优化工作的信任。在下个月,我们计划继续改进和优化您的网站,以进一步提升其在搜索引擎中的表现。以下是我们的下月优化计划:</p>
  96. <div class="num-suffix">
  97. <a-textarea :style="detailInfo.nextMonth.length > 1000 ? 'border-color:red':''"
  98. auto-size placeholder="请输入"
  99. v-model:value="detailInfo.nextMonth"></a-textarea>
  100. <span :style="detailInfo.nextMonth.length > 1000 ? 'color:red':''"
  101. class="suffix-self">
  102. {{ detailInfo.nextMonth.length }}/1000
  103. </span>
  104. </div>
  105. <p class="tips" style="margin: 10px 0 10px;">
  106. 我们将按照以上优化计划,不断努力提升您的网站在搜索引擎中的表现。如果您有任何疑问或需要进一步讨论,请随时与我们联系。感谢您的合作,我们期待着实现更大的SEO成功!</p>
  107. </section>
  108. <section class="s2">
  109. <h2 class="title mb">网站流量</h2>
  110. <p class="tips" style="margin: -5px 0 5px;">
  111. 在本月的SEO月报中,我们会向您展示关于网站流量数据及其他相关的数据,如页面浏览量、跳出率、平均访问时长等,以帮助您更全面地了解网站流量情况和用户行为。这些数据将以图表、表格或其他可视化方式呈现,以便您更直观地理解和分析。</p>
  112. <p class="tips">以下是一些关键的流量指标和相应的数据展示:</p>
  113. <a-row class="r5-1">
  114. <a-col :span="24">
  115. <area-chart v-if="coreDataChart.x.length > 0"
  116. :dataSource="coreDataChart"></area-chart>
  117. <a-empty v-else style="float: right;width: 100%;margin-top: 110px;"></a-empty>
  118. </a-col>
  119. <a-col :span="24">
  120. <div class="item">
  121. <p><img src="@/assets/pdfExport/uvicon.svg" />访客数(UV)</p>
  122. <p>{{ statistics.averageVisit }}</p>
  123. </div>
  124. <div class="item">
  125. <p><img src="@/assets/pdfExport/pvicon.svg" />浏览量(PV)</p>
  126. <p>{{ 0 }}</p>
  127. </div>
  128. <div class="item">
  129. <p><img src="@/assets/pdfExport/unReadEnquiryNum.svg" />日均访问量</p>
  130. <p>{{ statistics.averageVisit }}</p>
  131. </div>
  132. <div class="item">
  133. <p><img src="@/assets/pdfExport/keywordsrank3.svg" />平均访问时长</p>
  134. <p>{{ statistics.averageVisitDuration }}</p>
  135. </div>
  136. <div class="item">
  137. <p><img src="@/assets/pdfExport/conversionRate.svg" />平均访问页面数</p>
  138. <p>{{ statistics.averageVisitPage }}</p>
  139. </div>
  140. </a-col>
  141. </a-row>
  142. <h2 class="title mb">最多访问TOP10</h2>
  143. <a-row>
  144. <a-col :span="24">
  145. <a-table
  146. :columns="mostAccessColumns"
  147. :data-source="mostAccessDatasource"
  148. size="middle"
  149. :pagination="false">
  150. <template #bodyCell="{ column, text }">
  151. <template v-if="column.key ==='pagePathSlot' ">
  152. <a-popover>
  153. <template slot="content">
  154. {{ text }}
  155. </template>
  156. <a :href="text" target="_blank">
  157. <div
  158. style="width: 700px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis">
  159. {{ text }}
  160. </div>
  161. </a>
  162. </a-popover>
  163. </template>
  164. <template v-if="column.key ==='centerSlot' ">
  165. <span style="margin-left: 20px;">{{ text }}</span>
  166. </template>
  167. <template v-if="column.key ==='avgTimeOnPageSlot' ">
  168. <span style="margin-left: 30px;">{{ text }} s</span>
  169. </template>
  170. </template>
  171. </a-table>
  172. </a-col>
  173. </a-row>
  174. </section>
  175. <section class="s3">
  176. <h2 class="title mb">网站排名</h2>
  177. <p class="tips" style="margin: -5px 0 5px;">
  178. 在本月的SEO月报中,我们向您展示关于您网站排名的部分数据。因关键词排名数据量较大且实时变化,这边仅展示前50个关键词排名细节,您可登录后台查看全部数据。以下是一些的排名指标和相应的数据展示:</p>
  179. <a-row
  180. v-if="appointKeywordNum == null && longTailKeywordNum == null">
  181. <a-col>
  182. <a-empty style="padding-top: 50px" />
  183. </a-col>
  184. </a-row>
  185. <a-row type="flex">
  186. <a-col :span="3" v-if="appointKeywordNum">
  187. <div class="item">
  188. <img src="@/assets/pdfExport/zhidingci.svg" />
  189. <p>指定词</p>
  190. <p>{{ appointKeywordNum }}</p>
  191. </div>
  192. </a-col>
  193. <a-col :span="longTailKeywordNum ? 9 : 21"
  194. v-if="appointKeywordNum">
  195. <div class="wrap">
  196. <p class="t1">指定词排名</p>
  197. <div class="content">
  198. <div class="d1"><img src="@/assets/pdfExport/NO1.svg" />1-10位</div>
  199. <div class="d2">{{ rankInfo.appointKeyword.firstNum }}个
  200. </div>
  201. </div>
  202. <div class="content">
  203. <div class="d1"><img src="@/assets/pdfExport/NO2.svg" />11-30位</div>
  204. <div class="d2">{{ rankInfo.appointKeyword.secondNum }}个
  205. </div>
  206. </div>
  207. <div class="content">
  208. <div class="d1"><img src="@/assets/pdfExport/NO3.svg" />31-100位</div>
  209. <div class="d2">{{ rankInfo.appointKeyword.thirdType }}个
  210. </div>
  211. </div>
  212. </div>
  213. </a-col>
  214. <a-col :span="3" v-if="longTailKeywordNum">
  215. <div class="item">
  216. <img src="@/assets/pdfExport/changweici.svg" />
  217. <p>关键词</p>
  218. <p>{{ longTailKeywordNum }}</p>
  219. </div>
  220. </a-col>
  221. <a-col :span="appointKeywordNum ? 9 : 21" v-if="longTailKeywordNum">
  222. <div class="wrap">
  223. <p class="t1">关键词排名</p>
  224. <div class="content">
  225. <div class="d1"><img src="../../../assets/seo/NO1.svg" />1-10位</div>
  226. <div class="d2">{{ rankInfo.longTailKeyword.firstNum }}个
  227. </div>
  228. </div>
  229. <div class="content">
  230. <div class="d1"><img src="@/assets/pdfExport/NO2.svg" />11-30位</div>
  231. <div class="d2">{{ rankInfo.longTailKeyword.secondNum }}个
  232. </div>
  233. </div>
  234. <div class="content">
  235. <div class="d1"><img src="@/assets/pdfExport/NO3.svg" />31-100位</div>
  236. <div class="d2">{{ rankInfo.longTailKeyword.thirdType }}个
  237. </div>
  238. </div>
  239. </div>
  240. </a-col>
  241. <a-col :span="24" v-if="appointKeywordsList.length > 0">
  242. <h2 class="title mb">指定词排名列表</h2>
  243. <a-table
  244. ref="table"
  245. size="middle"
  246. :scroll="{x:true}"
  247. :columns="appointKeywordPageListColumns"
  248. :pagination="ipagination"
  249. :dataSource="appointKeywordsList"
  250. >
  251. <template #bodyCell="{ column, record, index, text }">
  252. <template v-if="column.key === 'rowIndex'">
  253. {{ index + 1 }}
  254. </template>
  255. <template class="tag" v-if="column.key === 'lastRank'">
  256. {{ record.lastRank }}
  257. </template>
  258. <template v-if="column.key === 'positionUrl'">
  259. <a :href="text" target="_blank">{{ text }}</a>
  260. </template>
  261. </template>
  262. </a-table>
  263. </a-col>
  264. <a-col :span="24" v-if="longTailKeywordsList.length > 0">
  265. <h2 class="title mb">关键词排名列表</h2>
  266. <a-table
  267. ref="table"
  268. size="middle"
  269. :scroll="{x:true}"
  270. :columns="longTailPageListColumns"
  271. :pagination="ipagination"
  272. :dataSource="longTailKeywordsList"
  273. >
  274. <template #bodyCell="{ column, record, index, text }">
  275. <template v-if="column.key ==='rowIndex'">
  276. {{ index + 1 }}
  277. </template>
  278. <template class="tag" v-if="column.key ==='lastRank'">
  279. {{ record.lastRank }}
  280. </template>
  281. <template v-if="column.key ==='positionUrl'">
  282. <a :href="text" target="_blank">{{ text }}</a>
  283. </template>
  284. </template>
  285. </a-table>
  286. </a-col>
  287. </a-row>
  288. </section>
  289. <section class="s4">
  290. <h2 class="title mb">网站外链</h2>
  291. <a-row>
  292. <a-col :span="24">
  293. <div class="item">
  294. <p>外链总数</p>
  295. <a-input placeholder="请输入" v-model="detailInfo.outlink1" />
  296. </div>
  297. <div class="item">
  298. <p>外链域</p>
  299. <a-input placeholder="请输入" v-model="detailInfo.outlink2" />
  300. </div>
  301. <div class="item">
  302. <p>外链发布数</p>
  303. <a-input placeholder="请输入" v-model="detailInfo.outlink3" />
  304. </div>
  305. </a-col>
  306. </a-row>
  307. <a-row :gutter="12">
  308. <a-col :span="12">
  309. <p class="sub-title">外链总数趋势</p>
  310. <a-col :span="24" style="margin: 10px 0px">
  311. <j-upload bizPath="seopdf/image" :multiple="false" :returnUrl="false" v-model:file-list="detailInfo.outLinkImg"/>
  312. </a-col>
  313. <div class="img-wrap">
  314. <template v-if="detailInfo.outLinkImg.length > 0">
  315. <img class="img" :src="detailInfo.outLinkImg[0].url "/>
  316. </template>
  317. <a-empty v-else>
  318. <span slot="description">请上传图片</span>
  319. </a-empty>
  320. </div>
  321. <div v-if="detailInfo.outLinkImg.length > 0">
  322. <a-textarea
  323. :style="detailInfo.outLinkImgDetail1.length > 1000 ? 'border-color:red':''"
  324. auto-size placeholder="请输入"
  325. v-model="detailInfo.outLinkImgDetail1"></a-textarea>
  326. <span :style="detailInfo.outLinkImgDetail1.length > 1000 ? 'color:red':''"
  327. class="suffix-self">
  328. {{ detailInfo.outLinkImgDetail1.length }}/1000
  329. </span>
  330. </div>
  331. </a-col>
  332. <a-col :span="12">
  333. <p class="sub-title">外链变化趋势</p>
  334. <a-col :span="24" style="margin: 10px 0px">
  335. <j-upload bizPath="seopdf/image" :returnUrl="true" :multiple="false" v-model:file-list="detailInfo.outLinkImg2"/>
  336. </a-col>
  337. <div class="img-wrap">
  338. <template v-if="detailInfo.outLinkImg2.length > 0">
  339. <img class="img" :src="detailInfo.outLinkImg2[0].url" />
  340. </template>
  341. <a-empty v-else>
  342. <span slot="description">请上传图片</span>
  343. </a-empty>
  344. </div>
  345. <div v-if="detailInfo.outLinkImg2.length > 0">
  346. <a-textarea
  347. :style="detailInfo.outLinkImgDetail2.length > 1000 ? 'border-color:red':''"
  348. auto-size placeholder="请输入"
  349. v-model="detailInfo.outLinkImgDetail2"></a-textarea>
  350. <span :style="detailInfo.outLinkImgDetail2.length > 1000 ? 'color:red':''"
  351. class="suffix-self">
  352. {{ detailInfo.outLinkImgDetail2.length }}/1000
  353. </span>
  354. </div>
  355. </a-col>
  356. </a-row>
  357. </section>
  358. <section class="s5">
  359. <h2 class="title mb">结语</h2>
  360. <p style="text-indent: 28px">
  361. 我们深知SEO是一个持续的过程,需要不断地监测、调整和改进。通过本月报告,我们与您分享我们的工作成果,并提供专业的建议和策略,以进一步提升您的网站在搜索引擎中的表现。</p>
  362. <p style="text-indent: 28px">
  363. 我们期待帮助您更好地理解和利用SEO对您的业务带来的巨大潜力。如果您对报告中的任何内容有疑问或需要进一步的解释,我们将随时为您提供支持和指导。</p>
  364. <p style="text-indent: 28px">
  365. 感谢您对我们的信任和合作。我们将继续竭尽全力,为您的网站取得更大的成功。</p>
  366. <p style="text-indent: 28px">祝您生意兴隆!</p>
  367. <p class="right">诚挚的问候</p>
  368. <p class="right">SEO团队</p>
  369. <p class="tips">
  370. 注:本次报告的SEO数据仅反映一段时间内的网站表现,但要注意数据的可变性和动态性,建议结合长期趋势和综合指标来评估网站SEO的整体表现。</p>
  371. </section>
  372. </a-spin>
  373. </div>
  374. </a-card>
  375. </div>
  376. </template>
  377. <script setup lang="ts">
  378. import { onMounted, reactive, ref } from "vue";
  379. import { getAction, postAction } from "/@/api/manage/manage";
  380. import dayjs from "dayjs";
  381. import SeoPdfExportModal from "@/views/adweb/seo/components/SeoPdfExportModal.vue";
  382. import { useModal } from "@/components/Modal";
  383. //注册model
  384. const [registerModal, { openModal }] = useModal();
  385. import { useMessage } from "@/hooks/web/useMessage";
  386. import AreaChart from "@/views/adweb/data/chart/areaChart.vue";
  387. import { JUpload } from "@/components/Form/src/jeecg/components/JUpload";
  388. import { PageEnum } from "@/enums/pageEnum";
  389. import { router } from "@/router";
  390. const fileList = ref([]);
  391. const appointKeywordPageListColumns = ref([
  392. {
  393. title: "序号",
  394. dataIndex: "rowIndex",
  395. key: "rowIndex",
  396. width: 100,
  397. align: "center",
  398. fixed: "left",
  399. },
  400. {
  401. title: "关键词",
  402. dataIndex: "keywords",
  403. key: "keywords",
  404. fixed: "left",
  405. width: 300
  406. },
  407. {
  408. title: "网址",
  409. dataIndex: "positionUrl",
  410. key: "positionUrl",
  411. fixed: "left",
  412. width: 600
  413. },
  414. {
  415. title: "排名",
  416. dataIndex: "lastRank",
  417. key: "lastRank",
  418. width: 100,
  419. }
  420. ]);
  421. const longTailPageListColumns = ref([
  422. {
  423. title: "序号",
  424. dataIndex: "rowIndex",
  425. key: "rowIndex",
  426. width: 100,
  427. align: "center",
  428. fixed: "left",
  429. },
  430. {
  431. title: "关键词",
  432. dataIndex: "keywords",
  433. key: "keywords",
  434. fixed: "left",
  435. width: 300
  436. },
  437. {
  438. title: "网址",
  439. dataIndex: "positionUrl",
  440. key: "positionUrl",
  441. fixed: "left",
  442. width: 600
  443. },
  444. {
  445. title: "排名",
  446. dataIndex: "lastRank",
  447. key: "lastRank",
  448. width: 100,
  449. }
  450. ]);
  451. const { createMessage } = useMessage();
  452. /**
  453. * 编辑事件
  454. */
  455. function handleEdit(record: Recordable) {
  456. openModal(true, {
  457. record,
  458. isUpdate: true,
  459. showFooter: true
  460. });
  461. }
  462. /**
  463. * 成功回调
  464. */
  465. function handleSuccess() {
  466. }
  467. const formState = reactive({
  468. siteName: ref(undefined),
  469. customerName: ref(undefined),
  470. range: ref(undefined)
  471. });
  472. const formRef = ref();
  473. const rules = ref({
  474. siteName: [
  475. { required: true, message: "请选择站点", trigger: "change" }
  476. ],
  477. customerName: [
  478. { required: true, message: "请输入客户名称", trigger: "blur" }
  479. ],
  480. range: [
  481. { required: true, message: "请选择查询时间范围", trigger: "change" }
  482. ]
  483. });
  484. const labelCol = ref({ span: 6 });
  485. const wrapperCol = ref({ span: 18 });
  486. const flag = ref(true);
  487. const siteOptions = ref([]);
  488. const handleChange = (value: string) => {
  489. formState.customerName = value.label;
  490. };
  491. const filterOption = (input: string, option: any) => {
  492. return option.name.toLowerCase().indexOf(input.toLowerCase()) >= 0;
  493. };
  494. const getSiteOption = () => {
  495. getAction("/adweb/adwebSite/getSiteListByUid", {}).then(function(res) {
  496. if (res.code == 200) {
  497. siteOptions.value = res.result;
  498. }
  499. });
  500. };
  501. const disabledDate = (current) => {
  502. return current > dayjs().subtract(1, "months");
  503. };
  504. const onSubmit = () => {
  505. formRef.value.validate().then(() => {
  506. flag.value = false;
  507. getInfo();
  508. getMostAccessList();
  509. getComprehenInfo();
  510. getRankInfo();
  511. getSeoMonthPlanContent();
  512. }).catch(error => {
  513. return false;
  514. });
  515. };
  516. const infoLoading = ref(false);
  517. const coreDataChart = ref({
  518. x: [],
  519. pv: [],
  520. uv: [],
  521. enquiry: []
  522. });
  523. const statistics = ref({
  524. uv: 0,
  525. pv: 0,
  526. averageVisit: 0,
  527. averageVisitDuration: 0,
  528. averageVisitPage: 0,
  529. bounceRate: "0%",
  530. conversionRate: "0%"
  531. });
  532. const mostAccessDatasource = ref([]);
  533. const appointKeywordsList = ref([]);
  534. const longTailKeywordsList = ref([]);
  535. const ipagination = ref({
  536. pageSize: 50
  537. });
  538. const getInfo = () => {
  539. let queryParam = {
  540. siteCode: formState.siteName.value,
  541. start: dayjs(formState.range).startOf("month").format("YYYY-MM-DD"),
  542. end: dayjs(formState.range).endOf("month").format("YYYY-MM-DD")
  543. };
  544. infoLoading.value = true;
  545. try {
  546. getAction("/dmp-data/site-overview/stats", queryParam).then(function(res) {
  547. if (!res.result) {
  548. coreDataChart.value = {
  549. x: [],
  550. uv: [],
  551. pv: [],
  552. enquiry: []
  553. };
  554. statistics.value = {
  555. uv: 0,
  556. pv: 0,
  557. averageVisit: 0,
  558. averageVisitDuration: 0,
  559. averageVisitPage: 0,
  560. bounceRate: "0%",
  561. conversionRate: "0%"
  562. };
  563. infoLoading.value = false;
  564. return;
  565. }
  566. const r = res.result.dailyStats;
  567. const x = [], pv = [], uv = [], enquiry = [];
  568. if (r != null && r.length > 0) {
  569. for (let item of r) {
  570. x.push(item.date);
  571. pv.push(item.pageViews);
  572. uv.push(item.totalUsers);
  573. enquiry.push(item.enquires);
  574. }
  575. }
  576. coreDataChart.value.x = x;
  577. coreDataChart.value.pv = pv;
  578. coreDataChart.value.uv = uv;
  579. coreDataChart.value.enquiry = enquiry;
  580. statistics.value.uv = res.result.totalUsers;
  581. statistics.value.pv = res.result.pageViews;
  582. statistics.value.averageVisit = res.result.dailyTotalUsers;
  583. statistics.value.averageVisitDuration = res.result.avgTimeOnPage;
  584. statistics.value.averageVisitPage = res.result.pageViewsPerSession;
  585. statistics.value.bounceRate = res.result.bounceRate;
  586. statistics.value.conversionRate = res.result.enquiryConversionRate;
  587. infoLoading.value = false;
  588. });
  589. } catch (error) {
  590. console.error(error);
  591. }
  592. };
  593. // 最多访问TOP10列表
  594. const mostAccessColumns = ref([
  595. {
  596. title: "来源",
  597. dataIndex: "pagePath",
  598. scopedSlots: {
  599. customRender: "pagePathSlot"
  600. }
  601. },
  602. {
  603. title: "浏览量(PV)",
  604. dataIndex: "pageViews",
  605. defaultSortOrder: "descend",
  606. sorter: (a, b) => a.pageViews - b.pageViews,
  607. width: 160,
  608. scopedSlots: {
  609. customRender: "centerSlot"
  610. }
  611. },
  612. {
  613. title: "浏览量占比",
  614. dataIndex: "pvProportion",
  615. width: 160,
  616. scopedSlots: {
  617. customRender: "centerSlot"
  618. }
  619. }
  620. ]);
  621. const getMostAccessList = async () => {
  622. try {
  623. let queryParam = {
  624. siteCode: formState.siteName.value,
  625. limit: 10,
  626. start: dayjs(formState.range).startOf("month").format("YYYY-MM-DD"),
  627. end: dayjs(formState.range).endOf("month").format("YYYY-MM-DD")
  628. };
  629. const res = await getAction("/dmp-data/page-path/stats", queryParam);
  630. if (res.code == 200) {
  631. mostAccessDatasource.value = res.result;
  632. } else {
  633. mostAccessDatasource.value = [];
  634. }
  635. } catch (error) {
  636. console.error(error);
  637. }
  638. };
  639. const appointKeywordNum = ref(null);
  640. const longTailKeywordNum = ref(null);
  641. const getComprehenInfo = async () => {
  642. let d = {
  643. siteCode: formState.siteName.value
  644. };
  645. await getAction("/seo/seoKeywordsRank/comprehensiveInfo", d).then(res => {
  646. if (res.code == 200) {
  647. appointKeywordNum.value = res.result.appointKeywordNum;
  648. longTailKeywordNum.value = res.result.longTailKeywordNum;
  649. }
  650. });
  651. };
  652. const detailInfo = ref({
  653. thisMonth: "",
  654. nextMonth: "",
  655. outlink1: "",
  656. outlink2: "",
  657. outlink3: "",
  658. outLinkImg: [],
  659. outLinkImg2: [],
  660. outLinkImgDetail1: "",
  661. outLinkImgDetail2: ""
  662. });
  663. const rankInfo = ref({
  664. appointKeyword: ref({}),
  665. longTailKeyword: ref({})
  666. });
  667. //获取三个list的数据
  668. const getRankInfo = () => {
  669. let d = {
  670. siteCode: formState.siteName.value
  671. };
  672. getAction("/seo/seoKeywordsRank/getRankInfo", d).then(res => {
  673. if (res.code == 200) {
  674. rankInfo.value.appointKeyword = res.result.appointKeyword;
  675. rankInfo.value.longTailKeyword = res.result.longTailKeyword;
  676. }
  677. });
  678. let d2 = {
  679. siteCode: formState.siteName.value,
  680. exportMonth: formState.range.format("YYYY-MM")
  681. };
  682. getAction("seo/seoMonthPdf/getSeoKeywordsRank", d2).then(res => {
  683. if (res.code == 200) {
  684. appointKeywordsList.value = res.result.appointKeywordsInfoList;
  685. longTailKeywordsList.value = res.result.longTailKeywordsInfoList;
  686. }
  687. });
  688. };
  689. const getSeoMonthPlanContent = () => {
  690. getAction("/seo/seoMonthPdf/getSeoMonthPlanContent", {}).then(function(res) {
  691. detailInfo.value.thisMonth = res.result.currentMonthPlan;
  692. detailInfo.value.nextMonth = res.result.nextMonthPlan;
  693. });
  694. };
  695. function handleExportPdf() {
  696. const data = {
  697. customerName: formState.customerName,
  698. monthStr: dayjs(formState.range).format('YYYY-MM'),
  699. detailInfo: detailInfo.value,
  700. rankInfo: rankInfo.value,
  701. appointKeywordNum: appointKeywordNum.value,
  702. longTailKeywordNum: longTailKeywordNum.value,
  703. appointKeywordsList: appointKeywordsList.value,
  704. longTailKeywordsList: longTailKeywordsList.value,
  705. coreDataChart: coreDataChart.value,
  706. statistics: statistics.value,
  707. mostAccessDatasource: mostAccessDatasource.value
  708. };
  709. // Store data in localStorage
  710. localStorage.setItem('pdfExportData', JSON.stringify(data));
  711. // Open the Pdf page in a new tab
  712. const url = router.resolve({
  713. name: 'Pdf'
  714. }).href;
  715. window.open(url, '_blank');
  716. }
  717. function goBack() {
  718. // Clear localStorage data
  719. localStorage.removeItem('pdfExportData');
  720. flag.value = true;
  721. coreDataChart.value.x = [];
  722. coreDataChart.value.pv = [];
  723. coreDataChart.value.uv = [];
  724. detailInfo.value.thisMonth = '';
  725. detailInfo.value.nextMonth = '';
  726. detailInfo.value.outlink1 = '';
  727. detailInfo.value.outlink2 = '';
  728. detailInfo.value.outlink3 = '';
  729. detailInfo.value.outLinkImg = [];
  730. detailInfo.value.outLinkImg2 = [];
  731. detailInfo.value.outLinkImgDetail1 = '';
  732. detailInfo.value.outLinkImgDetail2 = '';
  733. formState.siteName = undefined;
  734. formState.customerName = '';
  735. appointKeywordsList.value = [];
  736. longTailKeywordsList.value = [];
  737. formState.range = dayjs().subtract(1, "months");
  738. }
  739. const resetForm = () => {
  740. formRef.value.resetFields();
  741. };
  742. onMounted(() => {
  743. getSiteOption();
  744. });
  745. </script>
  746. <style scoped lang="less">
  747. .content {
  748. /deep/ .ant-table-pagination.ant-pagination {
  749. display: none;
  750. }
  751. .tip {
  752. color: #666;
  753. }
  754. .ant-card {
  755. /deep/ .ant-card-body {
  756. .step1 {
  757. min-height: 90vh;
  758. display: flex;
  759. justify-content: center;
  760. align-items: center;
  761. width: 720px;
  762. margin: 0 auto;
  763. flex-direction: column;
  764. .ant-form-item {
  765. margin-bottom: 15px;
  766. }
  767. }
  768. }
  769. }
  770. .btn3 {
  771. position: fixed;
  772. right: 100px;
  773. top: 180px;
  774. z-index: 999;
  775. }
  776. .step2 {
  777. padding: 24px;
  778. }
  779. .top {
  780. text-align: center;
  781. h1 {
  782. font-size: 27px;
  783. font-weight: 500;
  784. line-height: 1;
  785. margin: 0;
  786. }
  787. p {
  788. font-size: 14px;
  789. color: #333;
  790. }
  791. .btn1 {
  792. position: fixed;
  793. right: 150px;
  794. top: 180px;
  795. z-index: 999;
  796. }
  797. .btn2 {
  798. position: fixed;
  799. right: 50px;
  800. top: 180px;
  801. z-index: 999;
  802. }
  803. .btn4 {
  804. position: fixed;
  805. right: 265px;
  806. top: 180px;
  807. z-index: 999;
  808. }
  809. .btn6 {
  810. position: fixed;
  811. right: 265px;
  812. top: 180px;
  813. z-index: 999;
  814. }
  815. }
  816. .title {
  817. border-left: 4px solid #544beb;
  818. line-height: 1;
  819. padding-left: 5px;
  820. margin: 40px 0 0;
  821. &.mb {
  822. margin-bottom: 20px;
  823. }
  824. .red {
  825. color: red;
  826. position: relative;
  827. top: 4px;
  828. left: 4px;
  829. }
  830. }
  831. .s1 {
  832. .p1 {
  833. color: #333;
  834. font-size: 15px;
  835. }
  836. textarea {
  837. min-height: 170px;
  838. }
  839. .num-suffix {
  840. position: relative;
  841. .suffix-self {
  842. position: absolute;
  843. right: 10px;
  844. bottom: 10px;
  845. }
  846. }
  847. }
  848. .s2 {
  849. .r5-1 {
  850. .item {
  851. width: 18%;
  852. margin: 40px 1% 0;
  853. float: left;
  854. padding: 20px;
  855. box-shadow: 3px 2px 10px -6px #544beb;
  856. border-radius: 10px;
  857. p {
  858. text-align: center;
  859. font-size: 15px;
  860. &:nth-child(2) {
  861. font-size: 20px;
  862. font-weight: 600;
  863. }
  864. img {
  865. width: 20px;
  866. margin-top: -5px;
  867. margin-right: 10px;
  868. }
  869. }
  870. }
  871. .fr {
  872. float: right;
  873. line-height: 2;
  874. span {
  875. margin-right: 30px;
  876. i {
  877. display: inline-block;
  878. width: 25px;
  879. height: 3px;
  880. background: #544BEB;
  881. position: relative;
  882. top: -4px;
  883. margin-right: 20px;
  884. }
  885. &:last-child i {
  886. background: #F0B358;
  887. }
  888. }
  889. }
  890. }
  891. }
  892. .s3 {
  893. .item {
  894. padding: 20px;
  895. height: 100%;
  896. border-radius: 10px;
  897. text-align: center;
  898. img {
  899. width: 80px;
  900. margin: 20px auto;
  901. }
  902. p {
  903. text-align: center;
  904. font-size: 15px;
  905. }
  906. }
  907. .wrap {
  908. padding: 20px;
  909. background: #fff;
  910. border-radius: 10px;
  911. margin-bottom: 10px;
  912. .t1 {
  913. color: #000;
  914. font-size: 14px;
  915. font-weight: 500;
  916. }
  917. .content {
  918. display: block;
  919. overflow: hidden;
  920. line-height: 1;
  921. border-bottom: 1px solid #ddd;
  922. padding: 20px 0;
  923. &:last-child {
  924. border-bottom: none;
  925. padding-bottom: 0;
  926. }
  927. .d1 {
  928. float: left;
  929. width: 50%;
  930. line-height: 25px;
  931. color: #000;
  932. img {
  933. width: 15px;
  934. margin: -4px 5px 0 0;
  935. }
  936. }
  937. .d2 {
  938. float: right;
  939. width: 50%;
  940. text-align: right;
  941. &.d3 {
  942. span {
  943. color: #000;
  944. }
  945. }
  946. a {
  947. font-size: 25px;
  948. font-weight: 500;
  949. }
  950. }
  951. }
  952. }
  953. }
  954. .s4 {
  955. .item {
  956. width: 32.33%;
  957. margin: 0 0.5%;
  958. border: 1px solid #e6e6e6;
  959. float: left;
  960. padding: 10px;
  961. border-radius: 10px;
  962. p {
  963. font-weight: 600;
  964. font-size: 15px;
  965. text-align: center;
  966. margin-bottom: 5px;
  967. }
  968. input {
  969. border: none;
  970. border-bottom: 1px solid #d9d9d9;
  971. border-radius: 0;
  972. text-align: center;
  973. &:focus {
  974. box-shadow: none;
  975. }
  976. }
  977. }
  978. .sub-title {
  979. font-size: 19px;
  980. color: #333;
  981. padding: 0 0 0 10px;
  982. border-left: 4px solid #544beb;
  983. line-height: 1;
  984. padding-left: 5px;
  985. margin: 20px 0 5px 0;
  986. }
  987. .img-wrap {
  988. height: 340px;
  989. display: flex;
  990. flex-direction: column;
  991. justify-content: center;
  992. border: 1px solid #d9d9d9;
  993. border-radius: 5px;
  994. .img {
  995. width: 100%;
  996. height: 100%;
  997. object-fit: contain;
  998. }
  999. }
  1000. textarea.ant-input {
  1001. margin: 10px 0 0;
  1002. min-height: 80px;
  1003. }
  1004. .suffix-self {
  1005. position: absolute;
  1006. right: 10px;
  1007. bottom: 10px;
  1008. }
  1009. }
  1010. .s5 {
  1011. p {
  1012. font-size: 15px;
  1013. color: #333;
  1014. &.right {
  1015. text-align: right;
  1016. padding-right: 50px;
  1017. }
  1018. &.tips {
  1019. color: #999;
  1020. }
  1021. }
  1022. }
  1023. }
  1024. </style>