utm.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. <script lang="ts">
  2. import Vue from 'vue'
  3. import * as echarts from 'echarts'
  4. import { getUtmList, getUtmDetail, saveUtmDetail, deleteUtm, getExpoList, getMyExpoInfo } from '@/api/expo'
  5. export default Vue.extend({
  6. name: 'Utm',
  7. data() {
  8. return {
  9. showAdd: false,
  10. isAdd: true,
  11. expoList: [],
  12. page: 1,
  13. total: 0,
  14. currentExpo: '',
  15. loading: false,
  16. searchData: {
  17. campaign_name: '',
  18. campaign_id: ''
  19. },
  20. addData: {
  21. id: '',
  22. website_url: '',
  23. campaign_name: '',
  24. campaign_id: '',
  25. campaign_source: '',
  26. campaign_term: '',
  27. campaign_medium: '',
  28. campaign_content: ''
  29. },
  30. adList: [],
  31. currentAd: {},
  32. echartsData: {
  33. tooltip: { trigger: 'item' },
  34. graphic: [
  35. {
  36. type: 'text',
  37. left: 'center',
  38. top: '38%',
  39. style: {
  40. text: '3147',
  41. fontSize: 36,
  42. fontWeight: 'bold',
  43. fill: '#666',
  44. textAlign: 'center'
  45. }
  46. },
  47. {
  48. type: 'text',
  49. left: 'center',
  50. top: '56%',
  51. style: {
  52. text: '人均pv',
  53. fontSize: 16,
  54. fill: '#111111',
  55. textAlign: 'center'
  56. }
  57. }
  58. ],
  59. series: [
  60. {
  61. type: 'pie',
  62. radius: ['60%', '100%'],
  63. avoidLabelOverlap: false,
  64. padAngle: 5,
  65. itemStyle: { borderRadius: 10, color: function(params) {
  66. const colorList = ['#72cdcd', '#d8ba87']
  67. return colorList[params.dataIndex]
  68. } },
  69. label: { show: false },
  70. left: 8, right: 8, top: 8, bottom: 8,
  71. data: [
  72. { value: 0, name: '访客数' },
  73. { value: 0, name: '浏览量' }
  74. ]
  75. }
  76. ]
  77. }
  78. }
  79. },
  80. mounted() {
  81. this.getExpoListData()
  82. this.getUtmData()
  83. },
  84. methods: {
  85. openAdd() {
  86. this.addData = {
  87. id: '',
  88. website_url: '',
  89. campaign_name: '',
  90. campaign_id: '',
  91. campaign_source: '',
  92. campaign_term: '',
  93. campaign_medium: '',
  94. campaign_content: ''
  95. }
  96. this.currentExpo = ''
  97. this.isAdd = true
  98. this.showAdd = true
  99. },
  100. getUtmData() {
  101. if (this.loading) {
  102. return
  103. }
  104. this.loading = true
  105. getUtmList(this.page, 20, this.searchData.campaign_name, this.searchData.campaign_id)
  106. .then(res => {
  107. this.adList = res.data.data
  108. this.total = res.data.total
  109. this.page = res.data.current_page
  110. this.loading = false
  111. })
  112. },
  113. inputSearch() {
  114. if (this.timer) {
  115. clearTimeout(this.timer)
  116. }
  117. this.timer = setTimeout(() => {
  118. this.getUtmData()
  119. }, 500)
  120. },
  121. changePage(page) {
  122. this.page = page
  123. this.getUtmData()
  124. },
  125. editAd() {
  126. this.addData = Object.assign({}, this.currentAd)
  127. this.isAdd = false
  128. this.showAdd = true
  129. },
  130. addAd() {
  131. if (this.addData.campaign_name === '') {
  132. this.$notify({
  133. title: '提示',
  134. message: '请填写广告系列名称',
  135. type: 'warning'
  136. })
  137. return
  138. }
  139. if (this.addData.website_url === '') {
  140. this.$notify({
  141. title: '提示',
  142. message: '请选择展会',
  143. type: 'warning'
  144. })
  145. return
  146. }
  147. if (this.loading) {
  148. return
  149. }
  150. saveUtmDetail(this.addData.id, this.addData.website_url, this.addData.campaign_name, this.addData.campaign_id, this.addData.campaign_source, this.addData.campaign_term, this.addData.campaign_medium, this.addData.campaign_content)
  151. .then(res => {
  152. this.$notify({
  153. title: '提示',
  154. message: '添加成功',
  155. type: 'success'
  156. })
  157. this.getUtmData()
  158. this.loading = false
  159. })
  160. .catch(err => {
  161. this.$notify({
  162. title: '提示',
  163. message: '添加失败:' + err,
  164. type: 'error'
  165. })
  166. this.loading = false
  167. })
  168. this.showAdd = false
  169. },
  170. getExpoListData() {
  171. getExpoList(1, 100, '').then(res => {
  172. this.expoList = res.data.data
  173. })
  174. },
  175. getExpoDetail() {
  176. this.loading = true
  177. getMyExpoInfo(this.currentExpo).then(res => {
  178. const host = window.location.protocol + '//' + window.location.host + '/register/'
  179. this.addData.website_url = host + res.data.urla
  180. this.loading = false
  181. }).catch(err => {
  182. this.$notify({
  183. title: '提示',
  184. message: '获取展会信息失败:' + err,
  185. type: 'error'
  186. })
  187. this.loading = false
  188. })
  189. },
  190. rowClick(row) {
  191. console.log(row)
  192. this.currentAd = {}
  193. this.echartsData.series[0].data[1].value = row.pv
  194. this.echartsData.series[0].data[0].value = row.uv
  195. this.echartsData.graphic[0].style.text = ((row.pv / row.uv) || 0).toFixed(2)
  196. if (this.myChart) {
  197. this.myChart.setOption(this.echartsData)
  198. } else {
  199. const dom = document.getElementById('form')
  200. this.myChart = echarts.init(dom)
  201. this.myChart.setOption(this.echartsData)
  202. }
  203. setTimeout(() => {
  204. this.currentAd = row
  205. }, 0)
  206. },
  207. shareAd(id) {
  208. this.$router.push({ path: '/share&utm/share?utmid=' + id })
  209. },
  210. deleteAd(id) {
  211. this.$confirm('确定要删除吗?', '提示', {
  212. callback: (action) => {
  213. if (action === 'confirm') {
  214. deleteUtm(id).then(res => {
  215. this.$notify({
  216. title: '提示',
  217. message: '删除成功',
  218. type: 'success'
  219. })
  220. this.currentAd = {}
  221. this.getUtmData()
  222. }).catch(err => {
  223. this.$notify({
  224. title: '提示',
  225. message: '删除失败:' + err,
  226. type: 'error'
  227. })
  228. })
  229. }
  230. }
  231. })
  232. }
  233. }
  234. })
  235. </script>
  236. <template>
  237. <div class="utm">
  238. <div class="list">
  239. <div class="head">
  240. <el-input v-model="searchData.campaign_name" class="search" placeholder="活动名称搜索" @input="inputSearch()" />
  241. <el-input v-model="searchData.campaign_id" class="search" placeholder="系列ID搜索" @input="inputSearch()" />
  242. <el-button class="add" icon="el-icon-plus" type="primary" @click="openAdd()">新建广告</el-button>
  243. </div>
  244. <div v-loading="loading" class="body">
  245. <el-table :data="adList" height="100%" class="table" @row-click="rowClick">
  246. <el-table-column label="ID" prop="id" width="60" />
  247. <el-table-column label="广告系列" prop="campaign_name" width="200" />
  248. <el-table-column label="浏览量" prop="pv" width="80" />
  249. <el-table-column label="访客数" prop="uv" width="80" />
  250. <el-table-column label="广告系列ID" prop="campaign_id" width="200" />
  251. <el-table-column label="广告来源" prop="campaign_source" width="200" />
  252. <el-table-column label="广告媒介" prop="campaign_medium" width="200" />
  253. <el-table-column label="广告内容" prop="campaign_content" width="200" />
  254. <el-table-column label="广告字段" prop="campaign_term" width="200" />
  255. <el-table-column label="更新时间" prop="update_time" width="200" />
  256. </el-table>
  257. </div>
  258. <div class="foot">
  259. <el-pagination
  260. background
  261. :page-size="10"
  262. layout="total"
  263. :total="total"
  264. />
  265. <el-pagination
  266. v-permission="'exhibitor.handel'"
  267. background
  268. :page-size="10"
  269. layout="prev, pager, next"
  270. :total="total"
  271. @current-change="changePage"
  272. />
  273. </div>
  274. </div>
  275. <div class="view-cont">
  276. <div class="view">
  277. <div class="head">
  278. <div class="name">{{ currentAd.campaign_name || '请选择广告' }}</div>
  279. </div>
  280. <div :class="['ad-info',currentAd.id?'show':'hide']">
  281. <el-button v-show="currentAd.id" class="edit" icon="el-icon-edit-outline" circle @click="editAd()" />
  282. <div>广告信息</div>
  283. <div class="info-list">
  284. <div class="item">
  285. <div class="label">ID:</div>
  286. <div class="value">{{ currentAd.id }}</div>
  287. </div>
  288. <div class="item">
  289. <div class="label">浏览量:</div>
  290. <div class="value">{{ currentAd.pv }}</div>
  291. </div>
  292. <div class="item">
  293. <div class="label">访客数:</div>
  294. <div class="value">{{ currentAd.uv }}</div>
  295. </div>
  296. <div class="item">
  297. <div class="label">原链接:</div>
  298. <div class="value">{{ currentAd.website_url }}</div>
  299. </div>
  300. <div class="item">
  301. <div class="label">短链接:</div>
  302. <div class="value">{{ currentAd.short_url }}</div>
  303. </div>
  304. <div class="item">
  305. <div class="label">系列ID:</div>
  306. <div class="value">{{ currentAd.campaign_id }}</div>
  307. </div>
  308. <div class="item">
  309. <div class="label">广告来源:</div>
  310. <div class="value">{{ currentAd.campaign_source }}</div>
  311. </div>
  312. <div class="item">
  313. <div class="label">广告媒介:</div>
  314. <div class="value">{{ currentAd.campaign_medium }}</div>
  315. </div>
  316. <div class="item">
  317. <div class="label">广告内容:</div>
  318. <div class="value">{{ currentAd.campaign_content }}</div>
  319. </div>
  320. <div class="item">
  321. <div class="label">广告字段:</div>
  322. <div class="value">{{ currentAd.campaign_term }}</div>
  323. </div>
  324. <div class="item">
  325. <div class="label">更新时间:</div>
  326. <div class="value">{{ currentAd.update_time }}</div>
  327. </div>
  328. </div>
  329. </div>
  330. <div :class="['info-card',currentAd.id?'show':'hide']">
  331. <div>人均浏览量</div>
  332. <div id="form" class="charts" />
  333. <div class="desc">
  334. <span v-if="currentAd.pv/currentAd.uv < 1.5">
  335. 大部分访客仅浏览一个页面后就离开了,也许您需要优化展会的宣传内容。
  336. </span>
  337. <span v-else-if="currentAd.pv/currentAd.uv > 1.5 && currentAd.pv/currentAd.uv < 3">
  338. 部分访客进入网站后又浏览了其它页面,看来访客对您的展会有兴趣!
  339. </span>
  340. <span v-else-if="currentAd.pv/currentAd.uv > 3">
  341. 进入网站的访客平均浏览了三个以上的页面,访客对您的展会非常有兴趣!
  342. </span>
  343. <span v-else>
  344. 网站暂时还没有访客数据,请耐心等待。
  345. </span>
  346. </div>
  347. </div>
  348. <div :class="['handel-info',currentAd.id?'show':'hide']">
  349. <div>操作面板</div>
  350. <div class="button-list">
  351. <el-button icon="el-icon-share" type="primary" @click="shareAd(currentAd.id)">分享广告</el-button>
  352. <el-button icon="el-icon-edit-outline" type="primary" @click="editAd()">编辑广告</el-button>
  353. <el-button icon="el-icon-delete" type="danger" @click="deleteAd(currentAd.id)">删除广告</el-button>
  354. </div>
  355. </div>
  356. </div>
  357. </div>
  358. <el-dialog :title="isAdd?'添加广告':'编辑广告'" custom-class="utm-dialog" :close-on-click-modal="false" :append-to-body="true" :visible.sync="showAdd">
  359. <div class="form">
  360. <div v-show="isAdd" class="item">
  361. <span class="label">展会(必选)</span>
  362. <el-select v-model="currentExpo" @change="getExpoDetail()">
  363. <el-option
  364. v-for="item in expoList"
  365. :key="item.id"
  366. :label="item.expo_name"
  367. :value="item.id"
  368. />
  369. </el-select>
  370. </div>
  371. <div v-show="isAdd" class="item">
  372. <span class="label">展会网址(自动填写)</span>
  373. <el-input :value="addData.website_url" />
  374. </div>
  375. <div class="item">
  376. <span class="label">广告系列名称(必填)</span>
  377. <el-input v-model="addData.campaign_name" />
  378. </div>
  379. <div class="item">
  380. <span class="label">广告来源</span>
  381. <el-input v-model="addData.campaign_source" />
  382. </div>
  383. <div class="item">
  384. <span class="label">宣传媒介</span>
  385. <el-input v-model="addData.campaign_medium" />
  386. </div>
  387. <div class="item">
  388. <span class="label">广告内容</span>
  389. <el-input v-model="addData.campaign_content" />
  390. </div>
  391. <div class="item">
  392. <span class="label">广告系列ID</span>
  393. <el-input v-model="addData.campaign_id" />
  394. </div>
  395. <div class="item">
  396. <span class="label">广告字段</span>
  397. <el-input v-model="addData.campaign_term" />
  398. </div>
  399. </div>
  400. <span slot="footer" class="dialog-footer">
  401. <el-button @click="showAdd=false">取 消</el-button>
  402. <el-button v-loading="loading" type="primary" @click="addAd()">确 定</el-button>
  403. </span>
  404. </el-dialog>
  405. </div>
  406. </template>
  407. <style scoped>
  408. .utm{
  409. overflow: hidden;
  410. padding: 0 !important;
  411. display: grid;
  412. width: 100%;
  413. height: 100%;
  414. grid-template-columns: 1fr 360px;
  415. .list{
  416. display: grid;
  417. grid-gap: 16px;
  418. grid-template-rows: auto 1fr auto;
  419. z-index: 1;
  420. position: relative;
  421. padding: 16px;
  422. box-shadow: 0 1px 4px 0 #00000022;
  423. .head{
  424. gap: 16px;
  425. display: flex;
  426. .search{
  427. width: 30%;
  428. }
  429. .add{
  430. margin-left: auto;
  431. }
  432. }
  433. .body{
  434. height: 100%;
  435. position: relative;
  436. .table{
  437. width: 100%;
  438. height: 100%;
  439. position: absolute;
  440. top: 0;
  441. left: 0;
  442. }
  443. }
  444. .foot{
  445. display: flex;
  446. justify-content: space-between;
  447. }
  448. }
  449. .view-cont{
  450. position: relative;
  451. width: 100%;
  452. height: 100%;
  453. }
  454. .view{
  455. padding: 16px;
  456. border-radius: 0 16px 16px 0;
  457. position: absolute;
  458. top: 0;
  459. left: 0;
  460. width: 100%;
  461. height: 100%;
  462. overflow: hidden;
  463. overflow-y: auto;
  464. background-color: #F9FAFB;
  465. .ad-info{
  466. position: relative;
  467. margin-top: 16px;
  468. background-color: #FFFFFF;
  469. box-shadow: 0 1px 4px 0 #00000011;
  470. border-radius: 16px;
  471. padding: 16px;
  472. transition-duration: 300ms;
  473. &.hide{
  474. transition-duration: 0ms;
  475. opacity: 0;
  476. transform: translateY(10px);
  477. }
  478. .edit{
  479. position: absolute;
  480. right: 8px;
  481. top: 8px;
  482. }
  483. .info-list{
  484. color: grey;
  485. margin-top: 8px;
  486. .item{
  487. display: flex;
  488. font-size: 14px;
  489. .label{
  490. width: 80px;
  491. text-align: right;
  492. }
  493. .value{
  494. width: 215px;
  495. overflow: hidden;
  496. }
  497. }
  498. }
  499. }
  500. .handel-info{
  501. background-color: #FFFFFF;
  502. box-shadow: 0 1px 4px 0 #00000011;
  503. border-radius: 16px;
  504. padding: 16px;
  505. position: relative;
  506. margin-top: 16px;
  507. width: 100%;
  508. transition-delay: 200ms;
  509. transition-duration: 300ms;
  510. .button-list{
  511. display: flex;
  512. flex-direction: column;
  513. gap: 8px;
  514. margin-top: 8px;
  515. .el-button{
  516. margin: 0;
  517. }
  518. }
  519. &.hide{
  520. transition-delay: 0ms;
  521. transition-duration: 0ms;
  522. opacity: 0;
  523. transform: translateY(10px);
  524. }
  525. }
  526. .info-card{
  527. background-color: #FFFFFF;
  528. box-shadow: 0 1px 4px 0 #00000011;
  529. border-radius: 16px;
  530. padding: 16px;
  531. position: relative;
  532. margin-top: 16px;
  533. width: 100%;
  534. transition-delay: 100ms;
  535. transition-duration: 300ms;
  536. &.hide{
  537. transition-delay: 0ms;
  538. transition-duration: 0ms;
  539. opacity: 0;
  540. transform: translateY(10px);
  541. }
  542. .charts{
  543. position: relative;
  544. width: 100%;
  545. height: 260px;
  546. }
  547. .desc{
  548. margin-top: 16px;
  549. font-size: 14px;
  550. color: grey;
  551. }
  552. }
  553. .head{
  554. display: flex;
  555. align-items: center;
  556. .name{
  557. font-size: 18px;
  558. font-weight: bold;
  559. color: #111111;
  560. overflow: hidden;
  561. flex: 1;
  562. margin-right: 16px;
  563. text-overflow: ellipsis;
  564. white-space: nowrap;
  565. display: -webkit-box;
  566. -webkit-box-orient: vertical;
  567. -webkit-line-clamp: 1;
  568. }
  569. }
  570. }
  571. }
  572. </style>
  573. <style>
  574. .utm-dialog{
  575. .form{
  576. display: flex;
  577. flex-direction: column;
  578. gap: 12px;
  579. .item{
  580. display: flex;
  581. align-items: center;
  582. .label{
  583. width: 160px;
  584. }
  585. .el-select{
  586. width: 100%;
  587. }
  588. }
  589. }
  590. }
  591. </style>