index.vue 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637
  1. <template>
  2. <div class="app-container">
  3. <el-row :gutter="10" class="mb8">
  4. <el-col :span="1.5">
  5. <el-button
  6. plain
  7. type="primary"
  8. icon="el-icon-plus"
  9. size="mini"
  10. @click="handleAdd"
  11. >新增</el-button>
  12. </el-col>
  13. <el-col :span="1.5">
  14. <el-button
  15. plain
  16. type="success"
  17. icon="el-icon-refresh"
  18. size="mini"
  19. @click="getList"
  20. >刷新</el-button>
  21. </el-col>
  22. </el-row>
  23. <el-table border v-loading="loading" :data="siteList">
  24. <el-table-column label="ID" align="center" prop="id" width="80" />
  25. <el-table-column label="站点名称" align="center" prop="siteName" min-width="150" show-overflow-tooltip />
  26. <el-table-column label="投放类型" align="center" prop="launchType" width="100" />
  27. <el-table-column label="配置回传" align="center" prop="configCallback" width="100">
  28. <template slot-scope="scope">
  29. <el-tag :type="scope.row.configCallback === 1 ? 'success' : 'info'" size="small">
  30. {{ scope.row.configCallback === 1 ? '是' : '否' }}
  31. </el-tag>
  32. </template>
  33. </el-table-column>
  34. <el-table-column label="站点状态" align="center" prop="status" width="100">
  35. <template slot-scope="scope">
  36. <el-tag :type="scope.row.status === 1 ? 'success' : 'info'" size="small">
  37. {{ scope.row.status === 1 ? '启用' : '停用' }}
  38. </el-tag>
  39. </template>
  40. </el-table-column>
  41. <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  42. <template slot-scope="scope">
  43. <span>{{ parseTime(scope.row.createTime) }}</span>
  44. </template>
  45. </el-table-column>
  46. <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="420" fixed="right">
  47. <template slot-scope="scope">
  48. <el-button
  49. size="mini"
  50. type="text"
  51. icon="el-icon-view"
  52. @click="handleDetail(scope.row)"
  53. >详情</el-button>
  54. <el-button
  55. size="mini"
  56. type="text"
  57. icon="el-icon-edit"
  58. @click="handleUpdate(scope.row)"
  59. >修改</el-button>
  60. <el-button
  61. size="mini"
  62. type="text"
  63. icon="el-icon-data-line"
  64. @click="handleStatistics(scope.row)"
  65. >统计</el-button>
  66. <el-button
  67. size="mini"
  68. type="text"
  69. :icon="scope.row.status === 1 ? 'el-icon-video-pause' : 'el-icon-video-play'"
  70. @click="handleEnable(scope.row)"
  71. >{{ scope.row.status === 1 ? '停用' : '启用' }}</el-button>
  72. <el-button
  73. size="mini"
  74. type="text"
  75. icon="el-icon-delete"
  76. @click="handleDelete(scope.row)"
  77. >删除</el-button>
  78. <el-button
  79. size="mini"
  80. type="text"
  81. icon="el-icon-link"
  82. @click="handleGenerateUrl(scope.row)"
  83. >生成投放url</el-button>
  84. </template>
  85. </el-table-column>
  86. </el-table>
  87. <!-- 添加或修改站点对话框 -->
  88. <el-dialog :title="title" :visible.sync="open" width="680px" append-to-body class="site-dialog">
  89. <el-form ref="form" :model="form" :rules="rules" label-width="130px" class="elegant-form">
  90. <!-- 基础信息 -->
  91. <div class="form-section">
  92. <div class="section-title">
  93. <i class="el-icon-info"></i>
  94. <span>基础信息</span>
  95. </div>
  96. <el-form-item label="站点名称" prop="siteName">
  97. <el-input
  98. v-model="form.siteName"
  99. placeholder="请输入站点名称"
  100. prefix-icon="el-icon-notebook-2"
  101. clearable
  102. :disabled="isDetail"
  103. />
  104. </el-form-item>
  105. <el-form-item label="渠道" prop="channelId">
  106. <el-select
  107. v-model="form.channelId"
  108. placeholder="请选择渠道"
  109. style="width: 100%"
  110. filterable
  111. @change="handleChannelChange"
  112. :disabled="isDetail"
  113. >
  114. <el-option
  115. v-for="item in channelList"
  116. :key="item.id"
  117. :label="item.channelName"
  118. :value="item.id"
  119. />
  120. </el-select>
  121. </el-form-item>
  122. <el-form-item label="项目" prop="projectId">
  123. <el-select
  124. v-model="form.projectId"
  125. placeholder="请选择项目"
  126. style="width: 100%"
  127. filterable
  128. @change="handleProjectChange"
  129. :disabled="isDetail"
  130. >
  131. <el-option
  132. v-for="item in projectList"
  133. :key="item.id"
  134. :label="item.projectName"
  135. :value="item.id"
  136. />
  137. </el-select>
  138. </el-form-item>
  139. </div>
  140. <!-- 投放配置 -->
  141. <div class="form-section">
  142. <div class="section-title">
  143. <i class="el-icon-setting"></i>
  144. <span>投放配置</span>
  145. </div>
  146. <el-form-item label="投放类型" prop="launchType">
  147. <el-select
  148. v-model="form.launchType"
  149. placeholder="请选择投放类型"
  150. style="width: 100%"
  151. prefix-icon="el-icon-s-flag"
  152. @change="handleLaunchTypeChange"
  153. :disabled="isDetail"
  154. >
  155. <el-option
  156. v-for="item in launchTypeOptions"
  157. :key="item.value"
  158. :label="item.label"
  159. :value="item.value"
  160. />
  161. </el-select>
  162. </el-form-item>
  163. <el-form-item label="广告类型" prop="adType">
  164. <el-select
  165. v-model="form.adType"
  166. placeholder="请选择广告类型"
  167. style="width: 100%"
  168. :disabled="isDetail"
  169. >
  170. <el-option
  171. v-for="item in adTypeOptions"
  172. :key="item.value"
  173. :label="item.label"
  174. :value="item.value"
  175. />
  176. </el-select>
  177. </el-form-item>
  178. <!-- 企微分配规则 -->
  179. <el-form-item label="企微分配规则" prop="allocationRule">
  180. <el-radio-group v-model="form.allocationRule" @change="handleAllocationRuleChange" :disabled="!form.launchType || isDetail">
  181. <el-radio
  182. label="1"
  183. :border="true"
  184. >个人码分配</el-radio>
  185. <el-radio
  186. label="0"
  187. :border="true"
  188. >活码分配</el-radio>
  189. </el-radio-group>
  190. </el-form-item>
  191. <!-- 根据选择的分配规则显示不同的下拉框 -->
  192. <el-form-item
  193. v-if="form.allocationRule === '1'"
  194. label="企业微信"
  195. prop="allocationRuleId"
  196. class="slide-fade"
  197. >
  198. <el-select
  199. v-model="form.allocationRuleId"
  200. placeholder="请选择企业微信"
  201. style="width: 100%"
  202. filterable
  203. :disabled="isDetail"
  204. >
  205. <el-option
  206. v-for="item in allocationRuleList"
  207. :key="item.id"
  208. :label="item.ruleName"
  209. :value="item.id"
  210. />
  211. </el-select>
  212. </el-form-item>
  213. <el-form-item
  214. v-if="form.allocationRule === '0'"
  215. :label="form.launchType === 1 ? '群活码' : '企业微信活码'"
  216. prop="allocationRuleId"
  217. class="slide-fade"
  218. >
  219. <el-select
  220. v-model="form.allocationRuleId"
  221. :placeholder="form.launchType === 1 ? '请选择群活码' : '请选择企业微信活码'"
  222. style="width: 100%"
  223. filterable
  224. :disabled="isDetail"
  225. >
  226. <el-option
  227. v-for="item in form.launchType === 1 ? groupActiveList : contactWayList"
  228. :key="item.id"
  229. :label="form.launchType === 1 ? item.groupName : item.name"
  230. :value="item.id"
  231. />
  232. </el-select>
  233. </el-form-item>
  234. </div>
  235. <!-- 广告商信息 -->
  236. <div class="form-section">
  237. <div class="section-title">
  238. <i class="el-icon-office-building"></i>
  239. <span>广告商信息</span>
  240. </div>
  241. <el-form-item label="投放广告商" prop="advertiserId">
  242. <el-select
  243. v-model="form.advertiserId"
  244. placeholder="请先选择投放类型"
  245. style="width: 100%"
  246. @change="handleAdvertiserChange"
  247. filterable
  248. :disabled="!form.launchType || isDetail"
  249. >
  250. <el-option
  251. v-for="item in advertiserList"
  252. :key="item.id"
  253. :label="item.advertiserName"
  254. :value="item.id"
  255. />
  256. </el-select>
  257. </el-form-item>
  258. <el-form-item label="投放广告商账号" prop="promotionAccountId">
  259. <el-select
  260. v-model="form.promotionAccountId"
  261. placeholder="请选择投放广告商账号"
  262. style="width: 100%"
  263. @change="handlePromotionAccountChange"
  264. filterable
  265. :disabled="!form.advertiserId || isDetail"
  266. >
  267. <el-option
  268. v-for="item in promotionAccountList"
  269. :key="item.id"
  270. :label="item.accountName"
  271. :value="item.id"
  272. />
  273. </el-select>
  274. </el-form-item>
  275. </div>
  276. <!-- 页面和来源 -->
  277. <div class="form-section">
  278. <div class="section-title">
  279. <i class="el-icon-monitor"></i>
  280. <span>页面和来源</span>
  281. </div>
  282. <el-form-item label="投放页面" prop="launchPageId">
  283. <el-select
  284. v-model="form.launchPageId"
  285. placeholder="请选择投放页面"
  286. style="width: 100%"
  287. @change="handleLaunchPageChange"
  288. filterable
  289. :disabled="isDetail"
  290. >
  291. <el-option
  292. v-for="item in landingPageTemplateList"
  293. :key="item.id"
  294. :label="item.templateName"
  295. :value="item.id"
  296. />
  297. </el-select>
  298. </el-form-item>
  299. <el-form-item label="投放域名" prop="launchDomain">
  300. <el-select
  301. v-model="form.launchDomain"
  302. placeholder="请选择投放域名"
  303. style="width: 100%"
  304. filterable
  305. :disabled="isDetail"
  306. >
  307. <el-option
  308. v-for="item in launchDomainList"
  309. :key="item.id"
  310. :label="item.domain"
  311. :value="item.domain"
  312. />
  313. </el-select>
  314. </el-form-item>
  315. </div>
  316. <!-- 回传配置 -->
  317. <div class="form-section">
  318. <div class="section-title">
  319. <i class="el-icon-share"></i>
  320. <span>回传配置</span>
  321. </div>
  322. <el-form-item label="是否配置回传" prop="configCallback">
  323. <el-radio-group v-model="form.configCallback" @change="handleConfigCallbackChange" :disabled="isDetail">
  324. <el-radio
  325. v-for="item in configCallbackOptions"
  326. :key="item.value"
  327. :label="item.value"
  328. :border="true"
  329. >{{ item.label }}</el-radio>
  330. </el-radio-group>
  331. </el-form-item>
  332. <el-form-item
  333. v-if="form.configCallback === 1"
  334. label="回传账号"
  335. prop="callbackAccountId"
  336. class="slide-fade"
  337. >
  338. <el-select
  339. v-model="form.callbackAccountId"
  340. placeholder="请选择回传账号"
  341. style="width: 100%"
  342. @change="handleCallbackAccountChange"
  343. filterable
  344. :disabled="isDetail"
  345. >
  346. <el-option
  347. v-for="item in callbackAccountList"
  348. :key="item.id"
  349. :label="item.accountName"
  350. :value="item.id"
  351. />
  352. </el-select>
  353. </el-form-item>
  354. <!-- 转换类型配置区域 -->
  355. <div v-if="form.configCallback === 1 && form.callbackAccountId" class="conversion-config-section">
  356. <div class="config-header">
  357. <span class="config-title">转换类型配置</span>
  358. <el-button type="primary" size="small" icon="el-icon-plus" @click="addConversionEvent">添加事件</el-button>
  359. </div>
  360. <div v-if="conversionEvents.length === 0" class="empty-tip">
  361. 暂无转换事件,请点击“添加事件”按钮添加
  362. </div>
  363. <div v-for="(event, index) in conversionEvents" :key="index" class="conversion-event-item">
  364. <div class="event-header">
  365. <span class="event-title">事件{{ index + 1 }}</span>
  366. <el-button
  367. type="danger"
  368. size="mini"
  369. icon="el-icon-delete"
  370. circle
  371. @click="removeConversionEvent(index)"
  372. ></el-button>
  373. </div>
  374. <el-form label-width="130px" style="margin-top: 10px;">
  375. <el-form-item label="广告商转化类型">
  376. <el-select
  377. v-model="event.advertiserEventType"
  378. placeholder="请选择广告商转化类型"
  379. style="width: 100%"
  380. @change="handleAdvertiserEventChange(index, $event)"
  381. >
  382. <el-option
  383. v-for="item in advertiserEventOptions"
  384. :key="item.eventType"
  385. :label="item.eventName"
  386. :value="item.eventType"
  387. />
  388. </el-select>
  389. </el-form-item>
  390. <el-form-item label="回传数据类型">
  391. <el-select
  392. v-model="event.systemEventType"
  393. placeholder="请选择回传数据类型"
  394. style="width: 100%"
  395. @change="handleSystemEventChange(index, $event)"
  396. >
  397. <el-option
  398. v-for="item in systemEventOptions"
  399. :key="item.eventType"
  400. :label="item.eventName"
  401. :value="item.eventType"
  402. />
  403. </el-select>
  404. </el-form-item>
  405. </el-form>
  406. </div>
  407. </div>
  408. </div>
  409. </el-form>
  410. <div slot="footer" class="dialog-footer">
  411. <el-button @click="cancel">取 消</el-button>
  412. <el-button v-if="!isDetail" type="primary" @click="submitForm" icon="el-icon-check">确 定</el-button>
  413. </div>
  414. </el-dialog>
  415. <!-- 站点详情对话框 -->
  416. <el-dialog title="站点详情" :visible.sync="detailOpen" width="900px" append-to-body>
  417. <el-descriptions :column="2" border>
  418. <el-descriptions-item label="站点名称">{{ detail.siteName || '-' }}</el-descriptions-item>
  419. <el-descriptions-item label="站点URL" :span="2">{{ detail.siteUrl || '-' }}</el-descriptions-item>
  420. <el-descriptions-item label="投放类型">{{ detail.launchType || '-' }}</el-descriptions-item>
  421. <el-descriptions-item label="广告类型">{{ detail.adType || '-' }}</el-descriptions-item>
  422. <el-descriptions-item label="广告商名称" :span="2">{{ detail.advertiserName || '-' }}</el-descriptions-item>
  423. <el-descriptions-item label="推广账户名称" :span="2">{{ detail.promotionAccountName || '-' }}</el-descriptions-item>
  424. <el-descriptions-item label="投放页面名称" :span="2">{{ detail.launchPageName || '-' }}</el-descriptions-item>
  425. <el-descriptions-item label="投放域名" :span="2">{{ detail.launchDomain || '-' }}</el-descriptions-item>
  426. <el-descriptions-item label="一级分配规则">
  427. <span v-if="detail.allocationRule === '1'">选择员工"个人码"分配规则</span>
  428. <span v-else-if="detail.allocationRule === '0'">选择员工"活码"分配规则</span>
  429. <span v-else>-</span>
  430. </el-descriptions-item>
  431. <el-descriptions-item label="二级分配汇总">{{ detail.allocationRuleId || '-' }}</el-descriptions-item>
  432. <el-descriptions-item label="项目名称">{{ detail.projectName || '-' }}</el-descriptions-item>
  433. <el-descriptions-item label="配置回传">
  434. <el-tag :type="detail.configCallback === 1 ? 'success' : 'info'" size="small">
  435. {{ detail.configCallback === 1 ? '是' : '否' }}
  436. </el-tag>
  437. </el-descriptions-item>
  438. <el-descriptions-item label="回传账号名称">{{ detail.callbackAccountName || '-' }}</el-descriptions-item>
  439. <el-descriptions-item label="站点状态">
  440. <el-tag :type="detail.status === 1 ? 'success' : 'info'" size="small">
  441. {{ detail.status === 1 ? '启用' : '停用' }}
  442. </el-tag>
  443. </el-descriptions-item>
  444. <el-descriptions-item label="创建人">{{ detail.creator || '-' }}</el-descriptions-item>
  445. <el-descriptions-item label="创建时间">{{ parseTime(detail.createTime) || '-' }}</el-descriptions-item>
  446. <el-descriptions-item label="更新人">{{ detail.updater || '-' }}</el-descriptions-item>
  447. <el-descriptions-item label="更新时间" :span="2">{{ parseTime(detail.updateTime) || '-' }}</el-descriptions-item>
  448. </el-descriptions>
  449. <div slot="footer" class="dialog-footer">
  450. <el-button @click="detailOpen = false">关 闭</el-button>
  451. </div>
  452. </el-dialog>
  453. <!-- 站点统计对话框 -->
  454. <el-dialog title="站点统计" :visible.sync="statisticsOpen" width="800px" append-to-body>
  455. <el-descriptions :column="2" border v-if="statistics">
  456. <el-descriptions-item label="站点ID">{{ statistics.siteId }}</el-descriptions-item>
  457. <el-descriptions-item label="访问量">{{ statistics.visitCount || 0 }}</el-descriptions-item>
  458. <el-descriptions-item label="访客数">{{ statistics.visitorCount || 0 }}</el-descriptions-item>
  459. <el-descriptions-item label="转化数">{{ statistics.conversionCount || 0 }}</el-descriptions-item>
  460. <el-descriptions-item label="转化率">
  461. {{ statistics.conversionRate ? (statistics.conversionRate * 100).toFixed(2) + '%' : '0%' }}
  462. </el-descriptions-item>
  463. <el-descriptions-item label="统计时间">{{ parseTime(statistics.updateTime) }}</el-descriptions-item>
  464. </el-descriptions>
  465. <div v-else style="text-align: center; padding: 40px 0;">
  466. <el-empty description="暂无统计数据"></el-empty>
  467. </div>
  468. <div slot="footer" class="dialog-footer">
  469. <el-button @click="statisticsOpen = false">关 闭</el-button>
  470. </div>
  471. </el-dialog>
  472. <!-- 生成投放url对话框 -->
  473. <el-dialog title="生成投放url" :visible.sync="urlDialogOpen" width="500px" append-to-body>
  474. <el-form label-width="100px">
  475. <el-form-item label="投放链接">
  476. <div class="url-container">
  477. <el-input
  478. v-model="launchUrl"
  479. readonly
  480. class="url-input"
  481. />
  482. <el-button
  483. type="primary"
  484. size="small"
  485. icon="el-icon-document-copy"
  486. @click="copyUrl"
  487. >复制</el-button>
  488. </div>
  489. </el-form-item>
  490. <el-form-item label="二维码">
  491. <div id="qrcode" class="qrcode-container"></div>
  492. <el-button
  493. type="primary"
  494. size="small"
  495. icon="el-icon-download"
  496. @click="downloadQrcode"
  497. style="margin-top: 10px;"
  498. >下载</el-button>
  499. </el-form-item>
  500. </el-form>
  501. <div slot="footer" class="dialog-footer">
  502. <el-button @click="urlDialogOpen = false">关 闭</el-button>
  503. </div>
  504. </el-dialog>
  505. </div>
  506. </template>
  507. <script>
  508. import { listSite, getSite, addSite, updateSite, delSite, getSiteStatistics, enableSite } from "@/api/adv/site";
  509. import { pageAdvertiser } from "@/api/adv/advertiser";
  510. import { pagePromotionAccount } from "@/api/adv/promotionAccount";
  511. import { pageTemplate } from "@/api/adv/landingPageTemplate";
  512. import QRCode from 'qrcode';
  513. import { pageCallbackAccount, getCallbackAccount, queryEventType, saveEventType } from "@/api/adv/callbackAccount";
  514. import { pageDomain } from "@/api/adv/domain";
  515. import { pageProject as pageChannel } from "@/api/adv/channel";
  516. import { pageProject } from "@/api/adv/project";
  517. import { pageAssignRule } from "@/api/qw/assignRule";
  518. import { pageGroupLiveCode } from "@/api/qw/groupLiveCode";
  519. import { listContactWay } from "@/api/qw/contactWay";
  520. export default {
  521. name: "Site",
  522. data() {
  523. return {
  524. // 遮罩层
  525. loading: true,
  526. // 站点表格数据
  527. siteList: [],
  528. // 弹出层标题
  529. title: "",
  530. // 是否显示弹出层
  531. open: false,
  532. // 是否为详情模式(只读)
  533. isDetail: false,
  534. // 是否显示详情对话框
  535. detailOpen: false,
  536. // 详情数据
  537. detail: {},
  538. // 是否显示统计弹出层
  539. statisticsOpen: false,
  540. // 统计数据
  541. statistics: null,
  542. // 表单参数
  543. form: {},
  544. // 投放类型选项
  545. launchTypeOptions: [
  546. { label: "线上投放", value: 1 },
  547. { label: "线下投放", value: 2 }
  548. ],
  549. // 广告类型选项
  550. adTypeOptions: [
  551. { label: "信息流广告", value: "信息流广告" }
  552. ],
  553. // 是否配置回传选项
  554. configCallbackOptions: [
  555. { label: "否", value: 0 },
  556. { label: "是", value: 1 }
  557. ],
  558. // 广告商列表
  559. advertiserList: [],
  560. // 广告商账号列表
  561. promotionAccountList: [],
  562. // 落地页模板列表
  563. landingPageTemplateList: [],
  564. // 企业微信分配规则列表
  565. allocationRuleList: [],
  566. // 群活码列表
  567. groupActiveList: [],
  568. // 企业微信活码列表
  569. contactWayList: [],
  570. // 回传账号列表
  571. callbackAccountList: [],
  572. // 投放域名列表
  573. launchDomainList: [],
  574. // 渠道列表
  575. channelList: [],
  576. // 项目列表
  577. projectList: [],
  578. // 转换事件列表
  579. conversionEvents: [],
  580. // 广告商事件选项(systemBuiltin='0')
  581. advertiserEventOptions: [],
  582. // 系统事件选项(systemBuiltin='1')
  583. systemEventOptions: [],
  584. // 生成url对话框
  585. urlDialogOpen: false,
  586. // 投放链接
  587. launchUrl: "",
  588. // 表单校验
  589. rules: {
  590. siteName: [
  591. { required: true, message: "站点名称不能为空", trigger: "blur" }
  592. ],
  593. siteUrl: [
  594. { required: true, message: "站点地址不能为空", trigger: "blur" }
  595. ],
  596. launchType: [
  597. { required: true, message: "请选择投放类型", trigger: "change" }
  598. ],
  599. adType: [
  600. { required: true, message: "请选择广告类型", trigger: "change" }
  601. ],
  602. advertiserId: [
  603. { required: true, message: "请选择投放广告商", trigger: "change" }
  604. ],
  605. promotionAccountId: [
  606. { required: true, message: "请选择投放广告商账号", trigger: "change" }
  607. ],
  608. launchPageId: [
  609. { required: true, message: "请选择投放页面", trigger: "change" }
  610. ],
  611. launchDomain: [
  612. { required: true, message: "请选择投放域名", trigger: "change" }
  613. ],
  614. configCallback: [
  615. { required: true, message: "请选择是否配置回传", trigger: "change" }
  616. ],
  617. callbackAccountId: [
  618. {
  619. validator: (rule, value, callback) => {
  620. if (this.form.configCallback === 1 && !value) {
  621. callback(new Error('请选择回传账号'));
  622. } else {
  623. callback();
  624. }
  625. },
  626. trigger: "change"
  627. }
  628. ]
  629. }
  630. };
  631. },
  632. created() {
  633. this.getList();
  634. },
  635. methods: {
  636. /** 查询站点列表 */
  637. getList() {
  638. this.loading = true;
  639. listSite().then(response => {
  640. this.siteList = response.data;
  641. this.loading = false;
  642. });
  643. },
  644. // 取消按钮
  645. cancel() {
  646. this.open = false;
  647. this.reset();
  648. },
  649. // 表单重置
  650. reset() {
  651. this.form = {
  652. id: undefined,
  653. siteName: undefined,
  654. siteUrl: undefined,
  655. launchType: undefined,
  656. adType: undefined,
  657. advertiserId: undefined,
  658. advertiserName: undefined,
  659. promotionAccountId: undefined,
  660. promotionAccountName: undefined,
  661. launchPageId: undefined,
  662. launchPageName: undefined,
  663. projectId: undefined,
  664. projectName: undefined,
  665. channelId: undefined,
  666. channelName: undefined,
  667. launchDomain: undefined,
  668. configCallback: 0,
  669. callbackAccountId: undefined,
  670. callbackAccountName: undefined,
  671. allocationRule: undefined,
  672. allocationRuleId: undefined
  673. };
  674. this.promotionAccountList = [];
  675. this.launchDomainList = [];
  676. this.allocationRuleList = [];
  677. this.groupActiveList = [];
  678. this.contactWayList = [];
  679. this.conversionEvents = [];
  680. this.advertiserEventOptions = [];
  681. this.systemEventOptions = [];
  682. this.resetForm("form");
  683. },
  684. /** 新增按钮操作 */
  685. handleAdd() {
  686. this.reset();
  687. // 不再预加载广告商列表,等待选择投放类型后再加载
  688. this.loadCommonSelectOptions();
  689. this.isDetail = false;
  690. this.open = true;
  691. this.title = "添加站点";
  692. },
  693. /** 修改按钮操作 */
  694. handleUpdate(row) {
  695. this.reset();
  696. this.loadCommonSelectOptions();
  697. this.isDetail = false;
  698. getSite(row.id).then(response => {
  699. this.form = response.data;
  700. // 确保 allocationRule 是字符串类型
  701. if (this.form.allocationRule !== undefined && this.form.allocationRule !== null) {
  702. this.form.allocationRule = String(this.form.allocationRule);
  703. }
  704. // 根据投放类型加载广告商列表
  705. if (this.form.launchType) {
  706. this.loadAdvertiserList(this.form.launchType);
  707. }
  708. // 如果有广告商ID,加载对应的广告商账号列表
  709. if (this.form.advertiserId) {
  710. this.loadPromotionAccountList(this.form.advertiserId);
  711. // 如果配置了回传,加载回传账号列表
  712. if (this.form.configCallback === 1) {
  713. this.loadCallbackAccountList(this.form.advertiserId);
  714. // 如果有回传账号,加载事件类型和转换事件
  715. if (this.form.callbackAccountId) {
  716. this.loadEventTypesAndConversionEvents(this.form.advertiserId, this.form.callbackAccountId);
  717. }
  718. }
  719. }
  720. // 如果设置了企微分配规则,加载对应的数据
  721. if (this.form.allocationRule) {
  722. if (this.form.allocationRule === '1') {
  723. // 加载企业微信分配规则列表
  724. this.loadAllocationRuleList();
  725. } else if (this.form.allocationRule === '0') {
  726. // 加载活码列表(根据投放类型)
  727. if (this.form.launchType === 1) {
  728. this.loadGroupActiveList();
  729. } else if (this.form.launchType === 2) {
  730. this.loadContactWayList();
  731. }
  732. }
  733. }
  734. this.$nextTick(() => {
  735. this.open = true;
  736. this.title = "修改站点";
  737. });
  738. });
  739. },
  740. /** 查看统计按钮操作 */
  741. handleStatistics(row) {
  742. this.$router.push({
  743. path: '/adv/new-adv/statistics',
  744. query: { siteId: row.id }
  745. });
  746. },
  747. /** 详情按钮操作 */
  748. handleDetail(row) {
  749. this.detail = {};
  750. this.detailOpen = true;
  751. getSite(row.id).then(response => {
  752. this.detail = response.data;
  753. });
  754. },
  755. /** 提交按钮 */
  756. submitForm() {
  757. this.$refs["form"].validate(valid => {
  758. if (valid) {
  759. // 如果配置了回传且有转换事件,校验转换事件
  760. if (this.form.configCallback === 1 && this.form.callbackAccountId && this.conversionEvents.length > 0) {
  761. for (let i = 0; i < this.conversionEvents.length; i++) {
  762. const event = this.conversionEvents[i];
  763. if (!event.systemEventType || !event.advertiserEventType) {
  764. this.msgError(`请完善事件${i + 1}的配置`);
  765. return;
  766. }
  767. }
  768. }
  769. if (this.form.id != undefined) {
  770. // 修改
  771. updateSite(this.form.id, this.form).then(response => {
  772. if (response.code === 200) {
  773. // 如果配置了回传,保存转换事件
  774. if (this.form.configCallback === 1 && this.form.callbackAccountId) {
  775. this.saveConversionEvents();
  776. } else {
  777. this.msgSuccess("修改成功");
  778. this.open = false;
  779. this.getList();
  780. }
  781. }
  782. });
  783. } else {
  784. // 新增
  785. addSite(this.form).then(response => {
  786. if (response.code === 200) {
  787. // 如果配置了回传,保存转换事件
  788. if (this.form.configCallback === 1 && this.form.callbackAccountId) {
  789. this.saveConversionEvents();
  790. } else {
  791. this.msgSuccess("新增成功");
  792. this.open = false;
  793. this.getList();
  794. }
  795. }
  796. });
  797. }
  798. }
  799. });
  800. },
  801. /** 删除按钮操作 */
  802. handleDelete(row) {
  803. this.$confirm('是否确认删除站点"' + row.siteName + '"?', "警告", {
  804. confirmButtonText: "确定",
  805. cancelButtonText: "取消",
  806. type: "warning"
  807. }).then(() => {
  808. return delSite(row.id);
  809. }).then(() => {
  810. this.getList();
  811. this.msgSuccess("删除成功");
  812. }).catch(function() {});
  813. },
  814. /** 启用/停用按鑵操作 */
  815. handleEnable(row) {
  816. const statusText = row.status === 1 ? '停用' : '启用';
  817. this.$confirm(`是否确认${statusText}站点"${row.siteName}"?`, "提示", {
  818. confirmButtonText: "确定",
  819. cancelButtonText: "取消",
  820. type: "warning"
  821. }).then(() => {
  822. return enableSite(row.id);
  823. }).then(() => {
  824. this.getList();
  825. this.msgSuccess(`${statusText}成功`);
  826. }).catch(function() {});
  827. },
  828. /** 生成投放url */
  829. handleGenerateUrl(row) {
  830. this.launchUrl = row.siteUrl || '';
  831. this.urlDialogOpen = true;
  832. this.$nextTick(() => {
  833. this.generateQrcode();
  834. });
  835. },
  836. /** 生成二维码 */
  837. generateQrcode() {
  838. const qrcodeElement = document.getElementById('qrcode');
  839. if (qrcodeElement) {
  840. qrcodeElement.innerHTML = '';
  841. // 创建 canvas 元素
  842. const canvas = document.createElement('canvas');
  843. QRCode.toCanvas(canvas, this.launchUrl, {
  844. errorCorrectionLevel: 'H',
  845. type: 'image/jpeg',
  846. quality: 0.95,
  847. margin: 1,
  848. width: 200
  849. }).then(() => {
  850. qrcodeElement.appendChild(canvas);
  851. }).catch(error => {
  852. console.error('生成二维码失败:', error);
  853. this.msgError('生成二维码失败');
  854. });
  855. }
  856. },
  857. /** 复制url */
  858. copyUrl() {
  859. if (!this.launchUrl) {
  860. this.msgError('投放链接为空');
  861. return;
  862. }
  863. navigator.clipboard.writeText(this.launchUrl).then(() => {
  864. this.msgSuccess('复制成功');
  865. }).catch(() => {
  866. // 平台不支持新API,改成传统方法
  867. const textarea = document.createElement('textarea');
  868. textarea.value = this.launchUrl;
  869. document.body.appendChild(textarea);
  870. textarea.select();
  871. document.execCommand('copy');
  872. document.body.removeChild(textarea);
  873. this.msgSuccess('复制成功');
  874. });
  875. },
  876. /** 下载二维码 */
  877. downloadQrcode() {
  878. const qrcodeCanvas = document.querySelector('#qrcode canvas');
  879. if (qrcodeCanvas) {
  880. const link = document.createElement('a');
  881. link.href = qrcodeCanvas.toDataURL();
  882. link.download = `qrcode_${new Date().getTime()}.png`;
  883. link.click();
  884. this.msgSuccess('下载成功');
  885. } else {
  886. this.msgError('没有找到二维码');
  887. }
  888. },
  889. /** 加载下拉选项数据 */
  890. loadSelectOptions() {
  891. // 加载广告商列表(已废弃,改用loadAdvertiserList)
  892. pageAdvertiser({ pageNum: 1, pageSize: 1000 }).then(response => {
  893. this.advertiserList = response.data.records;
  894. }).catch(error => {
  895. console.error('加载广告商列表失败:', error);
  896. this.advertiserList = [];
  897. });
  898. // 加载落地页模板列表
  899. pageTemplate({ pageNum: 1, pageSize: 1000, status: 1 }).then(response => {
  900. this.landingPageTemplateList = response.data.records;
  901. }).catch(error => {
  902. console.error('加载落地页模板失败:', error);
  903. this.landingPageTemplateList = [];
  904. });
  905. // 加载域名列表
  906. pageDomain({ pageNum: 1, pageSize: 1000, status: 1 }).then(response => {
  907. this.launchDomainList = response.data.records;
  908. }).catch(error => {
  909. console.error('加载域名失败:', error);
  910. this.launchDomainList = [];
  911. });
  912. },
  913. /** 加载公共下拉选项数据(不包含广告商) */
  914. loadCommonSelectOptions() {
  915. // 加载落地页模板列表
  916. pageTemplate({ pageNum: 1, pageSize: 1000, status: 1 }).then(response => {
  917. this.landingPageTemplateList = response.data.records;
  918. }).catch(error => {
  919. console.error('加载落地页模板失败:', error);
  920. this.landingPageTemplateList = [];
  921. });
  922. // 加载域名列表
  923. pageDomain({ pageNum: 1, pageSize: 1000, status: 1 }).then(response => {
  924. this.launchDomainList = response.data.records;
  925. }).catch(error => {
  926. console.error('加载域名失败:', error);
  927. this.launchDomainList = [];
  928. });
  929. // 加载渠道列表
  930. pageChannel({ pageNum: 1, pageSize: 1000 }).then(response => {
  931. this.channelList = response.data.records || [];
  932. }).catch(error => {
  933. console.error('加载渠道列表失败:', error);
  934. this.channelList = [];
  935. });
  936. // 加载项目列表
  937. pageProject({ pageNum: 1, pageSize: 1000 }).then(response => {
  938. this.projectList = response.data.records || [];
  939. }).catch(error => {
  940. console.error('加载项目列表失败:', error);
  941. this.projectList = [];
  942. });
  943. },
  944. /** 投放类型变化时 */
  945. handleLaunchTypeChange(launchType) {
  946. // 重置广告商相关数据
  947. this.form.advertiserId = undefined;
  948. this.form.advertiserName = "";
  949. this.form.promotionAccountId = undefined;
  950. this.form.promotionAccountName = "";
  951. this.advertiserList = [];
  952. this.promotionAccountList = [];
  953. // 重置回传账号相关数据
  954. this.form.callbackAccountId = undefined;
  955. this.form.callbackAccountName = "";
  956. this.callbackAccountList = [];
  957. this.conversionEvents = [];
  958. this.advertiserEventOptions = [];
  959. this.systemEventOptions = [];
  960. // 重置企业微信分配规则相关数据
  961. this.form.allocationRule = undefined;
  962. this.form.allocationRuleId = undefined;
  963. this.allocationRuleList = [];
  964. this.groupActiveList = [];
  965. this.contactWayList = [];
  966. // 根据投放类型加载广告商列表
  967. if (launchType) {
  968. this.loadAdvertiserList(launchType);
  969. }
  970. // 根据投放类型重新加载活码数据(如果已选择活码分配)
  971. if (this.form.allocationRule === '0') {
  972. if (launchType === 1) {
  973. this.loadGroupActiveList();
  974. } else if (launchType === 2) {
  975. this.loadContactWayList();
  976. }
  977. }
  978. },
  979. /** 加载广告商列表 */
  980. loadAdvertiserList(launchType) {
  981. if (!launchType) return;
  982. // launchType: 1=线上投放, 2=线下投放
  983. // custom: 1=线上广告商, 2=自定义广告商
  984. const customValue = launchType; // launchType值直接对应custom值
  985. pageAdvertiser({
  986. pageNum: 1,
  987. pageSize: 1000,
  988. enabled: 1,
  989. custom: customValue
  990. }).then(response => {
  991. this.advertiserList = response.data.records;
  992. }).catch(error => {
  993. console.error('加载广告商列表失败:', error);
  994. this.advertiserList = [];
  995. });
  996. },
  997. /** 广告商变化时 */
  998. handleAdvertiserChange(advertiserId) {
  999. this.form.advertiserName = "";
  1000. this.form.promotionAccountId = undefined;
  1001. this.form.promotionAccountName = "";
  1002. this.promotionAccountList = [];
  1003. // 重置回传账号相关数据
  1004. this.form.callbackAccountId = undefined;
  1005. this.form.callbackAccountName = "";
  1006. this.callbackAccountList = [];
  1007. if (advertiserId) {
  1008. const advertiser = this.advertiserList.find(item => item.id === advertiserId);
  1009. if (advertiser) {
  1010. this.form.advertiserName = advertiser.advertiserName;
  1011. }
  1012. // 加载对应的广告商账号列表
  1013. this.loadPromotionAccountList(advertiserId);
  1014. // 如果已选择配置回传,加载回传账号列表
  1015. if (this.form.configCallback === 1) {
  1016. this.loadCallbackAccountList(advertiserId);
  1017. }
  1018. }
  1019. },
  1020. /** 加载广告商账号列表 */
  1021. loadPromotionAccountList(advertiserId) {
  1022. if (!advertiserId) return;
  1023. pagePromotionAccount({ pageNum: 1, pageSize: 1000, advertiserId: advertiserId }).then(response => {
  1024. this.promotionAccountList = response.data.records;
  1025. }).catch(error => {
  1026. console.error('加载广告商账号失败:', error);
  1027. this.promotionAccountList = [];
  1028. });
  1029. },
  1030. /** 广告商账号变化时 */
  1031. handlePromotionAccountChange(promotionAccountId) {
  1032. this.form.promotionAccountName = "";
  1033. if (promotionAccountId) {
  1034. const account = this.promotionAccountList.find(item => item.id === promotionAccountId);
  1035. if (account) {
  1036. this.form.promotionAccountName = account.accountName;
  1037. }
  1038. }
  1039. },
  1040. /** 投放页面变化时 */
  1041. handleLaunchPageChange(launchPageId) {
  1042. this.form.launchPageName = "";
  1043. if (launchPageId) {
  1044. const template = this.landingPageTemplateList.find(item => item.id === launchPageId);
  1045. if (template) {
  1046. this.form.launchPageName = template.templateName;
  1047. }
  1048. }
  1049. },
  1050. /** 渠道变化时 */
  1051. handleChannelChange(channelId) {
  1052. this.form.channelName = "";
  1053. if (channelId) {
  1054. const channel = this.channelList.find(item => item.id === channelId);
  1055. if (channel) {
  1056. this.form.channelName = channel.channelName;
  1057. }
  1058. }
  1059. },
  1060. /** 项目变化时 */
  1061. handleProjectChange(projectId) {
  1062. this.form.projectName = "";
  1063. if (projectId) {
  1064. const project = this.projectList.find(item => item.id === projectId);
  1065. if (project) {
  1066. this.form.projectName = project.projectName;
  1067. }
  1068. }
  1069. },
  1070. /** 回传账号变化时 */
  1071. handleCallbackAccountChange(callbackAccountId) {
  1072. this.form.callbackAccountName = "";
  1073. this.conversionEvents = [];
  1074. this.advertiserEventOptions = [];
  1075. this.systemEventOptions = [];
  1076. if (callbackAccountId) {
  1077. const account = this.callbackAccountList.find(item => item.id === callbackAccountId);
  1078. if (account) {
  1079. this.form.callbackAccountName = account.accountName;
  1080. // 加载事件类型和转换事件
  1081. this.loadEventTypesAndConversionEvents(this.form.advertiserId, callbackAccountId);
  1082. }
  1083. }
  1084. },
  1085. /** 配置回传变化时 */
  1086. handleConfigCallbackChange(value) {
  1087. // 重置回传账号
  1088. this.form.callbackAccountId = undefined;
  1089. this.form.callbackAccountName = "";
  1090. this.callbackAccountList = [];
  1091. this.conversionEvents = [];
  1092. this.advertiserEventOptions = [];
  1093. this.systemEventOptions = [];
  1094. // 重置企微分配规则相关数据
  1095. this.form.allocationRule = undefined;
  1096. this.form.allocationRuleId = undefined;
  1097. this.allocationRuleList = [];
  1098. this.groupActiveList = [];
  1099. // 如果选择配置回传,且已选择广告商,加载回传账号列表
  1100. if (value === 1 && this.form.advertiserId) {
  1101. this.loadCallbackAccountList(this.form.advertiserId);
  1102. }
  1103. },
  1104. /** 加载回传账号列表 */
  1105. loadCallbackAccountList(advertiserId) {
  1106. if (!advertiserId) return;
  1107. pageCallbackAccount({ pageNum: 1, pageSize: 1000, advertiserId: advertiserId }).then(response => {
  1108. this.callbackAccountList = response.data.records;
  1109. }).catch(error => {
  1110. console.error('加载回传账号列表失败:', error);
  1111. this.callbackAccountList = [];
  1112. });
  1113. },
  1114. /** 加载事件类型和转换事件 */
  1115. loadEventTypesAndConversionEvents(advertiserId, callbackAccountId) {
  1116. // 加载事件类型选项
  1117. queryEventType(advertiserId).then(response => {
  1118. const eventTypes = response.data || [];
  1119. // systemBuiltin='0' 为广告商事件
  1120. this.advertiserEventOptions = eventTypes.filter(item => item.systemBuiltin === '0');
  1121. // systemBuiltin='1' 为系统事件
  1122. this.systemEventOptions = eventTypes.filter(item => item.systemBuiltin === '1');
  1123. }).catch(error => {
  1124. console.error('加载事件类型失败:', error);
  1125. });
  1126. // 加载已有的转换事件
  1127. getCallbackAccount(callbackAccountId).then(response => {
  1128. const account = response.data;
  1129. this.parseConversionEvents(account.conversionEvent);
  1130. }).catch(error => {
  1131. console.error('加载回传账号详情失败:', error);
  1132. });
  1133. },
  1134. /** 解析转换事件 */
  1135. parseConversionEvents(conversionEvent) {
  1136. try {
  1137. if (conversionEvent && typeof conversionEvent === 'string') {
  1138. this.conversionEvents = JSON.parse(conversionEvent);
  1139. } else if (Array.isArray(conversionEvent)) {
  1140. this.conversionEvents = conversionEvent;
  1141. } else {
  1142. this.conversionEvents = [];
  1143. }
  1144. } catch (error) {
  1145. console.error('解析转换事件失败:', error);
  1146. this.conversionEvents = [];
  1147. }
  1148. },
  1149. /** 添加转换事件 */
  1150. addConversionEvent() {
  1151. this.conversionEvents.push({
  1152. systemEventType: '',
  1153. systemEventTypeName: '',
  1154. advertiserEventType: '',
  1155. advertiserEventName: ''
  1156. });
  1157. },
  1158. /** 删除转换事件 */
  1159. removeConversionEvent(index) {
  1160. this.conversionEvents.splice(index, 1);
  1161. },
  1162. /** 广告商事件变化 */
  1163. handleAdvertiserEventChange(index, eventType) {
  1164. const event = this.advertiserEventOptions.find(item => item.eventType === eventType);
  1165. if (event) {
  1166. this.conversionEvents[index].advertiserEventName = event.eventName;
  1167. }
  1168. },
  1169. /** 系统事件变化 */
  1170. handleSystemEventChange(index, eventType) {
  1171. const event = this.systemEventOptions.find(item => item.eventType === eventType);
  1172. if (event) {
  1173. this.conversionEvents[index].systemEventTypeName = event.eventName;
  1174. }
  1175. },
  1176. /** 企微分配规则变化 */
  1177. handleAllocationRuleChange(allocationRule) {
  1178. // 重置需要加载的数据列表
  1179. this.form.allocationRuleId = undefined;
  1180. this.allocationRuleList = [];
  1181. this.groupActiveList = [];
  1182. this.contactWayList = [];
  1183. // 根据不同的分配规则加载对应数据
  1184. if (allocationRule === '1') {
  1185. // 加载企业微信分配规则列表
  1186. this.loadAllocationRuleList();
  1187. } else if (allocationRule === '0') {
  1188. // 根据投放类型加载群活码或企业微信活码
  1189. if (this.form.launchType === 1) {
  1190. this.loadGroupActiveList();
  1191. } else if (this.form.launchType === 2) {
  1192. this.loadContactWayList();
  1193. }
  1194. }
  1195. },
  1196. /** 加载企业微信分配规则列表 */
  1197. loadAllocationRuleList() {
  1198. pageAssignRule({
  1199. pageNum: 1,
  1200. pageSize: 1000,
  1201. status: 1
  1202. }).then(response => {
  1203. this.allocationRuleList = response.data.records || [];
  1204. }).catch(error => {
  1205. console.error('加载企业微信分配规则列表失败:', error);
  1206. this.allocationRuleList = [];
  1207. });
  1208. },
  1209. /** 加载群活码列表 */
  1210. loadGroupActiveList() {
  1211. pageGroupLiveCode({
  1212. pageNum: 1,
  1213. pageSize: 1000,
  1214. status: 1
  1215. }).then(response => {
  1216. this.groupActiveList = response.data.records || [];
  1217. }).catch(error => {
  1218. console.error('加载群活码列表失败:', error);
  1219. this.groupActiveList = [];
  1220. });
  1221. },
  1222. /** 加载企业微信活码列表 */
  1223. loadContactWayList() {
  1224. // 调用/qw/contactWay/list接口获取企业微信活码数据
  1225. // 注意:此接口返回rows字段,不是data.records
  1226. listContactWay().then(response => {
  1227. this.contactWayList = response.rows || [];
  1228. }).catch(error => {
  1229. console.error('加载企业微信活码列表失败:', error);
  1230. this.contactWayList = [];
  1231. });
  1232. },
  1233. /** 保存转换事件 */
  1234. saveConversionEvents() {
  1235. // 调用保存接口
  1236. saveEventType(this.form.callbackAccountId, this.conversionEvents).then(response => {
  1237. if (response.code === 200) {
  1238. this.msgSuccess('保存成功');
  1239. this.open = false;
  1240. this.getList();
  1241. }
  1242. }).catch(error => {
  1243. console.error('保存失败:', error);
  1244. this.msgError('保存失败');
  1245. });
  1246. }
  1247. }
  1248. };
  1249. </script>
  1250. <style lang="scss" scoped>
  1251. .app-container {
  1252. padding: 20px;
  1253. }
  1254. // 对话框样式
  1255. ::v-deep .site-dialog {
  1256. .el-dialog__header {
  1257. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  1258. padding: 20px;
  1259. margin: 0;
  1260. border-radius: 4px 4px 0 0;
  1261. .el-dialog__title {
  1262. color: #fff;
  1263. font-size: 18px;
  1264. font-weight: 500;
  1265. }
  1266. .el-dialog__headerbtn .el-dialog__close {
  1267. color: #fff;
  1268. font-size: 20px;
  1269. &:hover {
  1270. color: #f0f0f0;
  1271. }
  1272. }
  1273. }
  1274. .el-dialog__body {
  1275. padding: 25px 30px;
  1276. background-color: #f8f9fa;
  1277. }
  1278. .el-dialog__footer {
  1279. padding: 15px 30px;
  1280. border-top: 1px solid #e8e8e8;
  1281. background-color: #fff;
  1282. }
  1283. }
  1284. // 表单样式
  1285. .elegant-form {
  1286. .form-section {
  1287. background: #fff;
  1288. border-radius: 8px;
  1289. padding: 20px;
  1290. margin-bottom: 20px;
  1291. box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
  1292. transition: all 0.3s ease;
  1293. &:hover {
  1294. box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  1295. transform: translateY(-2px);
  1296. }
  1297. &:last-child {
  1298. margin-bottom: 0;
  1299. }
  1300. }
  1301. .section-title {
  1302. display: flex;
  1303. align-items: center;
  1304. margin-bottom: 20px;
  1305. padding-bottom: 12px;
  1306. border-bottom: 2px solid #e8e8e8;
  1307. font-size: 15px;
  1308. font-weight: 600;
  1309. color: #303133;
  1310. i {
  1311. font-size: 18px;
  1312. margin-right: 8px;
  1313. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  1314. -webkit-background-clip: text;
  1315. -webkit-text-fill-color: transparent;
  1316. background-clip: text;
  1317. }
  1318. span {
  1319. position: relative;
  1320. &::after {
  1321. content: '';
  1322. position: absolute;
  1323. left: 0;
  1324. bottom: -12px;
  1325. width: 0;
  1326. height: 2px;
  1327. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  1328. transition: width 0.3s ease;
  1329. }
  1330. }
  1331. }
  1332. .form-section:hover .section-title span::after {
  1333. width: 100%;
  1334. }
  1335. .el-form-item {
  1336. margin-bottom: 20px;
  1337. &:last-child {
  1338. margin-bottom: 0;
  1339. }
  1340. }
  1341. ::v-deep .el-form-item__label {
  1342. color: #606266;
  1343. font-weight: 500;
  1344. font-size: 14px;
  1345. }
  1346. ::v-deep .el-input__inner,
  1347. ::v-deep .el-textarea__inner {
  1348. border-radius: 6px;
  1349. border: 1px solid #dcdfe6;
  1350. transition: all 0.3s ease;
  1351. &:hover {
  1352. border-color: #c0c4cc;
  1353. }
  1354. &:focus {
  1355. border-color: #667eea;
  1356. box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
  1357. }
  1358. }
  1359. ::v-deep .el-select {
  1360. .el-input__inner {
  1361. padding-left: 15px;
  1362. }
  1363. }
  1364. ::v-deep .el-radio {
  1365. margin-right: 20px;
  1366. &.is-bordered {
  1367. border-radius: 6px;
  1368. padding: 10px 20px;
  1369. transition: all 0.3s ease;
  1370. &:hover {
  1371. border-color: #667eea;
  1372. }
  1373. &.is-checked {
  1374. border-color: #667eea;
  1375. background-color: rgba(102, 126, 234, 0.05);
  1376. }
  1377. }
  1378. }
  1379. // 回传账号显示动画
  1380. .slide-fade {
  1381. animation: slideDown 0.3s ease;
  1382. }
  1383. }
  1384. @keyframes slideDown {
  1385. from {
  1386. opacity: 0;
  1387. transform: translateY(-10px);
  1388. }
  1389. to {
  1390. opacity: 1;
  1391. transform: translateY(0);
  1392. }
  1393. }
  1394. // 按钮样式优化
  1395. .dialog-footer {
  1396. text-align: right;
  1397. .el-button {
  1398. padding: 10px 24px;
  1399. border-radius: 6px;
  1400. font-weight: 500;
  1401. transition: all 0.3s ease;
  1402. &.el-button--primary {
  1403. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  1404. border: none;
  1405. &:hover {
  1406. transform: translateY(-2px);
  1407. box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
  1408. }
  1409. &:active {
  1410. transform: translateY(0);
  1411. }
  1412. }
  1413. &.el-button--default {
  1414. &:hover {
  1415. color: #667eea;
  1416. border-color: #667eea;
  1417. background-color: rgba(102, 126, 234, 0.05);
  1418. }
  1419. }
  1420. }
  1421. }
  1422. // 表格样式优化
  1423. .el-table {
  1424. border-radius: 8px;
  1425. overflow: hidden;
  1426. box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
  1427. ::v-deep .el-table__header {
  1428. th {
  1429. background-color: #f5f7fa;
  1430. color: #606266;
  1431. font-weight: 600;
  1432. font-size: 14px;
  1433. }
  1434. }
  1435. ::v-deep .el-table__row {
  1436. transition: all 0.3s ease;
  1437. &:hover {
  1438. background-color: rgba(102, 126, 234, 0.05);
  1439. }
  1440. }
  1441. }
  1442. // 顶部按钮组样式
  1443. .mb8 {
  1444. margin-bottom: 15px;
  1445. .el-button {
  1446. border-radius: 6px;
  1447. padding: 10px 20px;
  1448. font-weight: 500;
  1449. transition: all 0.3s ease;
  1450. &.el-button--primary {
  1451. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  1452. border: none;
  1453. &:hover {
  1454. transform: translateY(-2px);
  1455. box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
  1456. }
  1457. }
  1458. &.el-button--success {
  1459. &:hover {
  1460. transform: translateY(-2px);
  1461. box-shadow: 0 4px 12px rgba(103, 194, 58, 0.4);
  1462. }
  1463. }
  1464. }
  1465. }
  1466. // 转换类型配置区域样式
  1467. .conversion-config-section {
  1468. margin-top: 20px;
  1469. padding: 20px;
  1470. background: #f8f9fa;
  1471. border-radius: 8px;
  1472. border: 1px solid #e8e8e8;
  1473. .config-header {
  1474. display: flex;
  1475. justify-content: space-between;
  1476. align-items: center;
  1477. margin-bottom: 15px;
  1478. .config-title {
  1479. font-size: 15px;
  1480. font-weight: 600;
  1481. color: #303133;
  1482. }
  1483. }
  1484. .empty-tip {
  1485. text-align: center;
  1486. padding: 30px 0;
  1487. color: #909399;
  1488. font-size: 14px;
  1489. }
  1490. }
  1491. .conversion-event-item {
  1492. margin-bottom: 15px;
  1493. padding: 15px;
  1494. border: 1px solid #e8e8e8;
  1495. border-radius: 6px;
  1496. background-color: #fff;
  1497. transition: all 0.3s ease;
  1498. &:hover {
  1499. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  1500. }
  1501. .event-header {
  1502. display: flex;
  1503. justify-content: space-between;
  1504. align-items: center;
  1505. margin-bottom: 10px;
  1506. padding-bottom: 10px;
  1507. border-bottom: 1px solid #f0f0f0;
  1508. .event-title {
  1509. font-weight: 600;
  1510. font-size: 14px;
  1511. color: #303133;
  1512. }
  1513. }
  1514. }
  1515. // 投放链接对话框样式
  1516. .url-container {
  1517. display: flex;
  1518. gap: 10px;
  1519. align-items: center;
  1520. .url-input {
  1521. flex: 1;
  1522. }
  1523. }
  1524. .qrcode-container {
  1525. display: flex;
  1526. justify-content: center;
  1527. padding: 20px 0;
  1528. canvas {
  1529. border: 1px solid #e0e0e0;
  1530. border-radius: 4px;
  1531. }
  1532. }
  1533. </style>