crowd-control.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. <template>
  2. <div class="crowd-control-content">
  3. <div class="control-con-header">
  4. <a-form
  5. class="grid-form" layout="inline"
  6. :form="form"
  7. hide-required-mark
  8. @submit="handleQueryList"
  9. >
  10. <a-row class="grid-form-options">
  11. <a-form-item
  12. class="grid-form-item"
  13. label="名称"
  14. :colon="false"
  15. >
  16. <a-input
  17. v-decorator="['name']" type="text"
  18. placeholder="请输入"
  19. />
  20. </a-form-item>
  21. <a-form-item
  22. class="grid-form-item" label="账户"
  23. :colon="false"
  24. >
  25. <acount-search class="acount-search-class" :appId.sync="acountId" :multiple="false"></acount-search>
  26. </a-form-item>
  27. <a-form-item
  28. class="grid-form-item grid-form-type" label="状态"
  29. :colon="false"
  30. >
  31. <a-select
  32. v-decorator="['status']"
  33. placeholder="请选择"
  34. allow-clear
  35. >
  36. <a-select-option v-for="item in statusTasks" :key="item.label">
  37. {{ item.value }}
  38. </a-select-option>
  39. </a-select>
  40. </a-form-item>
  41. <a-form-item class="form-handle-btn">
  42. <a-button type="default" class="reset-class" @click="handleResetForm">重置</a-button>
  43. <a-button type="primary" html-type="submit">查询</a-button>
  44. </a-form-item>
  45. </a-row>
  46. </a-form>
  47. <a-button type="primary" class="upload-person" @click="handleUploadPerson">
  48. 上传人群包
  49. </a-button>
  50. </div>
  51. <div class="control-con-table">
  52. <a-table
  53. ref="table"
  54. size="middle"
  55. bordered
  56. :columns="columns"
  57. :dataSource="dataSource"
  58. :pagination="false"
  59. >
  60. <span slot="action" slot-scope="text, record">
  61. <a @click="handleEdit(record)">推送</a>
  62. <a-divider type="vertical" />
  63. <a @click="handleDelete(record)">删除</a>
  64. </span>
  65. <span slot="cause" slot-scope="text, record">
  66. <a class="cause-text-class" @click="handleCause(record)">{{ text }}</a>
  67. </span>
  68. <span slot="depRelax" slot-scope="text, record">
  69. <a @click="handleAcountRelax('dep', record)">{{ text }}</a>
  70. </span>
  71. <span slot="acountRelax" slot-scope="text, record">
  72. <a @click="handleAcountRelax('acount', record)">{{ text }}</a>
  73. </span>
  74. </a-table>
  75. <a-pagination
  76. class="pagin-table-class"
  77. :total="totalAll"
  78. :show-total="total => `共 ${totalAll} 条`"
  79. size="small"
  80. show-size-changer
  81. show-quick-jumper
  82. @change="onShowSizeChange"
  83. @showSizeChange="onShowSizeChange"
  84. />
  85. </div>
  86. <a-modal
  87. v-if="personVisible"
  88. title="上传人群包"
  89. class="person-modal"
  90. :visible="personVisible"
  91. :confirm-loading="confirmLoading"
  92. @ok="handleOk"
  93. @cancel="handleCancel"
  94. >
  95. <div>
  96. <a-form-model
  97. ref="ruleForm"
  98. :model="personForm"
  99. :rules="personRules"
  100. :label-col="labelCol"
  101. :wrapper-col="wrapperCol"
  102. >
  103. <a-form-model-item label="匹配类型" prop="resource">
  104. <a-radio-group v-model="personForm.resource">
  105. <a-radio v-for="item in mateTypeTasks" :key="item.label" :value="item.label">{{ item.value }}</a-radio>
  106. </a-radio-group>
  107. </a-form-model-item>
  108. <a-form-model-item ref="name" label="人群包名称" prop="name">
  109. <a-input
  110. v-model="personForm.name"
  111. placeholder="请输入人群包名称"
  112. />
  113. </a-form-model-item>
  114. <a-form-model-item label="选择账户">
  115. <acount-search class="acount-search-class" :appId.sync="uploadAcountIds" :multiple="false"></acount-search>
  116. </a-form-model-item>
  117. <a-form-model-item label="上传人群包">
  118. <a-upload
  119. name="file"
  120. :file-list="fileList"
  121. :before-upload="beforeUpload"
  122. >
  123. <a-button><a-icon type="upload" />文件上传</a-button>
  124. </a-upload>
  125. <p class="upload-txt-rules">1、文件格式:支持上传*.txt(utf-8)文本;也支持将单个/多个*.txt文件经过Zip格式压缩上传</p>
  126. <p class="upload-txt-rules">2、文件大小:单个文件大小不能超过1G</p>
  127. <p class="upload-txt-rules">3、内容编排:每行一条记录</p>
  128. </a-form-model-item>
  129. </a-form-model>
  130. </div>
  131. </a-modal>
  132. <a-modal
  133. title="推送账户"
  134. v-if="pushVisible"
  135. :visible="pushVisible"
  136. :confirm-loading="pushConfirmLoading"
  137. dialog-class="push-modal"
  138. @ok="handlePushSure"
  139. @cancel="handlePushCancel"
  140. >
  141. <div>
  142. <div class="acount-title">
  143. <div class="acount-title-left">已选人群包</div>
  144. <div class="acount-title-right">{{ tableSelectList.orientationName }}</div>
  145. </div>
  146. <div class="acount-title push-plate">
  147. <div class="acount-title-left">推送平台</div>
  148. <div class="acount-title-right">
  149. <a-radio-group default-value="a" button-style="solid">
  150. <a-radio-button value="a">效果</a-radio-button>
  151. <a-radio-button value="b" disabled>品牌</a-radio-button>
  152. </a-radio-group>
  153. </div>
  154. </div>
  155. <tree-select @applyType="handleApplyType"></tree-select>
  156. </div>
  157. </a-modal>
  158. <a-modal
  159. title="相关项目"
  160. v-if="causeVisible"
  161. :visible="causeVisible"
  162. :confirm-loading="causeConfirmLoading"
  163. :footer="null"
  164. dialog-class="cause-modal"
  165. @cancel="handleCauseCancel"
  166. >
  167. <div>
  168. <div class="cause-modal-table-class">
  169. <a-table :columns="causeColumns" :data-source="causeData" bordered :pagination="false"></a-table>
  170. </div>
  171. <a-pagination
  172. class="pagin-table-class"
  173. :total="totalAllCause"
  174. size="small"
  175. @change="handleCauseSizeChange"
  176. />
  177. </div>
  178. </a-modal>
  179. <a-modal
  180. title="相关项目"
  181. v-if="relaxVisible"
  182. :visible="relaxVisible"
  183. :confirm-loading="relaxConfirmLoading"
  184. :footer="null"
  185. dialog-class="relax-modal"
  186. @cancel="handleRelaxCancel"
  187. >
  188. <div>
  189. <div class="acount-title">
  190. <div class="acount-title-left">人群包名称</div>
  191. <div class="acount-title-right">{{ acountRelaxDetail.orientationName }}</div>
  192. </div>
  193. <div class="acount-title push-plate">
  194. <div class="acount-title-left">目标账户</div>
  195. <div class="acount-title-right">{{ acountRelaxDetail.accountName }}</div>
  196. </div>
  197. <template v-if="relaxStatus === 'dep'">
  198. <a-table :columns="depColumns" :data-source="depData" bordered>
  199. </a-table>
  200. </template>
  201. <template v-if="relaxStatus === 'acount'">
  202. <a-table :columns="acountColumns" :data-source="acountData" bordered></a-table>
  203. </template>
  204. </div>
  205. </a-modal>
  206. </div>
  207. </template>
  208. <script>
  209. import TreeSelect from './components/tree-select/tree-select';
  210. import {getAction, postAction} from '@/api/manage';
  211. import AcountSearch from '@/views/modules/Statistics/components/Treeselect.vue';
  212. import AppMarketService from './crowd-control-service';
  213. let COS = require('cos-js-sdk-v5');
  214. let cos = new COS({
  215. SecretId: 'AKIDE6IpMi8fJQRCg1iuWzFajjRs43kbbets',
  216. SecretKey: 'tXzuwMfplTTK3c9GFUyETilasvQfePu9'
  217. });
  218. export default {
  219. name: 'crowd-control',
  220. components: {
  221. TreeSelect,
  222. AcountSearch
  223. },
  224. data() {
  225. let that = this;
  226. return {
  227. totalAllCause: 0,
  228. acountId: undefined, // 搜索的账户ID
  229. uploadAcountIds: '',
  230. totalAll: 10,
  231. finallyRightTree: [],
  232. fileList: [],
  233. relaxVisible: false,
  234. relaxConfirmLoading: false,
  235. pushVisible: false,
  236. pushConfirmLoading: false,
  237. causeVisible: false,
  238. causeConfirmLoading: false,
  239. labelCol: {span: 4},
  240. wrapperCol: {span: 14},
  241. personVisible: false,
  242. confirmLoading: false,
  243. mateTypeTasks: AppMarketService.setMateTypeTasks(), // 匹配类型
  244. populationTypeTasks: AppMarketService.setPopulationTypeTasks(), // 人群包类型
  245. statusTasks: AppMarketService.setStatusTasks(), // 状态枚举
  246. relaxStatus: '',
  247. causeData: [], // 原因的table列表数据,
  248. depData: [], // 相关项目的table列表数据,
  249. acountData: [], // 相关账户的table列表数据,
  250. acountColumns: AppMarketService.setAcountColumns(), // 相关账户的table列表
  251. depColumns: AppMarketService.setDepColumns(), // 相关项目的table列表
  252. causeColumns: AppMarketService.setCauseColumns(), // 原因的table列表
  253. dataSource: [],
  254. columns: [
  255. {
  256. title: '人群包名称',
  257. align: 'center',
  258. width: 200,
  259. ellipsis: true,
  260. dataIndex: 'orientationName'
  261. },
  262. {
  263. title: '人群包类型',
  264. align: 'center',
  265. dataIndex: 'populationType',
  266. customRender(t) {
  267. return t ? that.populationTypeTasks.find(item => item.label === Number(t)).value : '-';
  268. }
  269. },
  270. {
  271. title: '匹配类型',
  272. align: 'center',
  273. dataIndex: 'typeStr'
  274. },
  275. {
  276. title: '上传时间',
  277. dataIndex: 'statDate',
  278. align: 'center'
  279. },
  280. {
  281. title: '覆盖数量',
  282. dataIndex: 'coverNum',
  283. align: 'center'
  284. },
  285. {
  286. title: '状态',
  287. dataIndex: 'status',
  288. align: 'center',
  289. customRender(t) {
  290. return t ? that.statusTasks.find(item => item.label === t).value : '-';
  291. }
  292. },
  293. {
  294. title: '原因',
  295. dataIndex: 'statusStr',
  296. align: 'center',
  297. scopedSlots: {customRender: 'cause'}
  298. },
  299. {
  300. title: '目标账户',
  301. dataIndex: 'accountName',
  302. align: 'center'
  303. },
  304. {
  305. title: '相关项目',
  306. dataIndex: 'projectCount',
  307. align: 'center',
  308. scopedSlots: {customRender: 'depRelax'}
  309. },
  310. {
  311. title: '相关账户',
  312. dataIndex: 'accountCount',
  313. align: 'center',
  314. scopedSlots: {customRender: 'acountRelax'}
  315. },
  316. {
  317. title: '操作',
  318. dataIndex: 'action',
  319. align: 'center',
  320. scopedSlots: {customRender: 'action'}
  321. }
  322. ],
  323. personForm: {
  324. name: '',
  325. resource: ''
  326. },
  327. personRules: AppMarketService.setPersonRules(), // 上传人群包表单校验规则
  328. tablePag: {
  329. page: 1,
  330. size: 10
  331. },
  332. // table 原因的分页
  333. causePag: {
  334. page: 1,
  335. size: 10
  336. },
  337. tableSelectList: {},
  338. acountRelaxDetail: {},
  339. causeDetailData: {}
  340. };
  341. },
  342. created() {
  343. this.form = this.$form.createForm(this);
  344. },
  345. mounted() {
  346. this.handleGetTableList({});
  347. },
  348. methods: {
  349. onShowSizeChange(current, pageSize) {
  350. const paramsData = this.form.getFieldsValue();
  351. this.tablePag = {
  352. page: current,
  353. size: pageSize
  354. };
  355. this.handleGetTableList(paramsData);
  356. },
  357. handleCauseSizeChange(current, pageSize) {
  358. this.causePag = {
  359. page: current,
  360. size: pageSize
  361. };
  362. this.handleCauseTableData();
  363. },
  364. handleGetTableList(data) {
  365. const paramsData = {
  366. orientationName: data.name,
  367. accountName: this.acountId,
  368. status: data.status,
  369. pageNo: this.tablePag.page,
  370. pageSize: this.tablePag.size
  371. };
  372. getAction('/kuaishouCrowdPack/queryPageList', paramsData).then(result => {
  373. if (result.code === 200) {
  374. this.dataSource = result.result.list || [];
  375. this.totalAll = result.result.total || 0;
  376. }
  377. }).catch(error => {
  378. console.log(error, 'eeee');
  379. });
  380. },
  381. handleDelete(data) {
  382. this.$confirm({
  383. title: '删除提示',
  384. content: '确定要删除这一条吗?',
  385. onOk() {
  386. getAction('/kuaishouCrowdPack/delete', {id: data.id}).then(result => {
  387. if (result.code === 200) {
  388. this.handleGetTableList({});
  389. }
  390. }).catch(error => {
  391. console.log(error, 'eeee');
  392. });
  393. }
  394. });
  395. },
  396. handleAcountRelaxTable() {
  397. const paramsData = {
  398. signature: this.acountRelaxDetail.signature,
  399. accountId: this.acountRelaxDetail.accountId,
  400. type: this.relaxStatus === 'dep' ? 1 : 2
  401. };
  402. getAction('/kuaishouCrowdPack/queryRel', paramsData).then(result => {
  403. if (result.code === 200) {
  404. if (this.relaxStatus === 'dep') {
  405. this.depData = result.result.list;
  406. }
  407. else if (this.relaxStatus === 'acount') {
  408. this.acountData = result.result.list;
  409. }
  410. }
  411. }).catch(error => {
  412. console.log(error, 'eeee');
  413. });
  414. },
  415. handleAcountRelax(txt, recd) {
  416. if (txt === 'dep') {
  417. this.relaxStatus = 'dep';
  418. }
  419. else if (txt === 'acount') {
  420. this.relaxStatus = 'acount';
  421. }
  422. this.acountRelaxDetail = recd;
  423. this.handleAcountRelaxTable();
  424. this.relaxVisible = true;
  425. },
  426. handleRelaxCancel() {
  427. this.relaxVisible = false;
  428. },
  429. handleCause(list) {
  430. this.causeVisible = true;
  431. this.causeDetailData = list;
  432. this.handleCauseTableData();
  433. },
  434. handleCauseTableData() {
  435. const paramsData = {
  436. signature: this.causeDetailData.signature,
  437. pageNo: this.causePag.page,
  438. pageSize: this.causePag.size
  439. };
  440. getAction('/kuaishouCrowdPack/queryFailReason', paramsData).then(result => {
  441. if (result.code === 200) {
  442. this.causeData = result.result.list || [];
  443. this.totalAllCause = result.result.total || 0;
  444. }
  445. }).catch(error => {
  446. console.log(error, 'eeee');
  447. });
  448. },
  449. handleApplyType(list) {
  450. this.finallyRightTree = list;
  451. },
  452. handleEdit(val) {
  453. this.tableSelectList = val;
  454. this.pushVisible = true;
  455. },
  456. handleCauseCancel() {
  457. this.causeVisible = false;
  458. },
  459. handlePushSure() {
  460. const paramsData = {
  461. accountIds: this.finallyRightTree.map(item => item.key),
  462. orientationId: this.tableSelectList.orientationId,
  463. accountId: this.tableSelectList.accountId
  464. };
  465. postAction('/kuaishouCrowdPack/accountPush', paramsData).then(result => {
  466. if (result.code === 200) {
  467. this.pushVisible = false;
  468. this.handleGetTableList({});
  469. }
  470. }).catch(error => {
  471. console.log(error, 'eeeeeee');
  472. });
  473. },
  474. handlePushCancel() {
  475. this.pushVisible = false;
  476. },
  477. beforeUpload(file) {
  478. const isZip = file.type.includes('zip') || file.type.includes('text');
  479. const fileOvesize = file.size > (1024 * 1024 * 1024);
  480. if (!isZip) {
  481. this.fileList = [];
  482. this.$message.error('只能上传zip格式的文件或者txt文件');
  483. return false;
  484. }
  485. if (fileOvesize) {
  486. this.$message.error('文件大小不能超过1G');
  487. return;
  488. }
  489. this.cosUpload(file);
  490. },
  491. cosUpload(file) {
  492. let that = this;
  493. let date = new Date();
  494. let y = date.getFullYear();
  495. let MM = date.getMonth() + 1;
  496. MM = MM < 10 ? '0' + MM : MM;
  497. let d = date.getDate();
  498. d = d < 10 ? '0' + d : d;
  499. let timeElse = new Date().getTime();
  500. let arr = file.name.split('.');
  501. let str = '';
  502. for (let i = 0; i < arr.length; i++) {
  503. if (i === arr.length - 1) {
  504. }
  505. else {
  506. if (i === arr.length - 2) {
  507. str += arr[i];
  508. }
  509. else {
  510. str += arr[i] + '.';
  511. }
  512. }
  513. }
  514. cos.putObject(
  515. {
  516. Bucket: 'media-1301855440',
  517. Region: 'ap-chongqing',
  518. // 存储桶所在地域,必须字段
  519. Key: that.uploadType + '/' + y + '-' + MM + '-' + d + '/' + str + '-' + timeElse + '.' + arr[arr.length - 1],
  520. StorageClass: 'STANDARD',
  521. Body: file // 上传文件对象
  522. },
  523. function (err, data) {
  524. if (err) {
  525. that.$message.error('上传失败!!!' + err);
  526. return;
  527. }
  528. that.loadingElse = false;
  529. that.fileList.push({
  530. uid: file.name,
  531. name: file.name,
  532. status: 'done',
  533. url: '//' + data.Location
  534. });
  535. if (that.fileList.length > 1) {
  536. that.fileList = that.fileList.slice(-1);
  537. }
  538. }
  539. );
  540. },
  541. handleQueryList(event) {
  542. event.preventDefault();
  543. const paramsData = this.form.getFieldsValue();
  544. this.handleGetTableList(paramsData);
  545. },
  546. handleResetForm() {
  547. this.form.resetFields();
  548. this.acountId = undefined;
  549. this.handleGetTableList({});
  550. },
  551. handleUploadPerson() {
  552. this.personVisible = true;
  553. },
  554. handleOk() {
  555. this.$refs.ruleForm.validate(valid => {
  556. if (valid) {
  557. if (!this.uploadAcountIds) {
  558. this.$message.error('请选择账户');
  559. return;
  560. }
  561. if (!this.fileList.length) {
  562. this.$message.error('请上传文件');
  563. return;
  564. }
  565. this.handleUploadRuleForm();
  566. }
  567. else {
  568. return false;
  569. }
  570. });
  571. },
  572. handleUploadRuleForm() {
  573. const paramsData = {
  574. type: this.personForm.resource,
  575. orientationName: this.personForm.name,
  576. accountId: this.uploadAcountIds,
  577. url: this.fileList[0].url
  578. };
  579. postAction('/kuaishouCrowdPack/uploadCrowdPack', paramsData).then(result => {
  580. if (result.code === 200) {
  581. this.personVisible = false;
  582. this.handleGetTableList({});
  583. }
  584. }).catch(error => {
  585. console.log(error, 'eeee');
  586. });
  587. },
  588. handleCancel() {
  589. this.personVisible = false;
  590. this.$refs.ruleForm.resetFields();
  591. this.uploadAcountIds = '';
  592. }
  593. }
  594. };
  595. </script>
  596. <style lang="less" scoped>
  597. @import "crowd-control";
  598. </style>