crowedPackage.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  1. <template>
  2. <div class="crowedPackage">
  3. <div class="optHeader">
  4. <div class="top">
  5. <span class="optItem">
  6. <a-tree-select
  7. v-model="treeValue"
  8. show-search
  9. style="width: 600px;height:30px"
  10. :dropdownMatchSelectWidth='true'
  11. :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
  12. tree-checkable
  13. searchPlaceholder='请输入账户关键字搜索'
  14. :show-checked-strategy="SHOW_PARENT"
  15. placeholder="请选择账户或搜索账户关键字"
  16. treeNodeFilterProp="title"
  17. :allowClear="true"
  18. @change="treeChange"
  19. @search="treeSearch"
  20. @select="treeSelect"
  21. :maxTagCount='1'
  22. :tree-data="treeData"
  23. :filterOption='filterOption'
  24. >
  25. </a-tree-select>
  26. </span>
  27. <span class="optItem">
  28. <a-date-picker v-model="dateValue" @change="dateChange" />
  29. </span>
  30. </div>
  31. <div class="bottom">
  32. <div class="optItem">
  33. <a-select size="default" placeholder='覆盖数量' v-model="coverageQuantity" style="width: 200px" @change="coverChange">
  34. <a-select-option v-for="i in 25" :key="(i + 9).toString(36) + i">
  35. {{ (i + 9).toString(36) + i }}
  36. </a-select-option>
  37. </a-select>
  38. </div>
  39. <div class="optItem">
  40. <a-select size="default" placeholder='类别' v-model="category" style="width: 200px" @change="categoryChange">
  41. <a-select-option value='' >
  42. 全部
  43. </a-select-option>
  44. <a-select-option v-for="(item,index) in categoryArr" :key="index" :value='item.value' >
  45. {{ item.title }}
  46. </a-select-option>
  47. </a-select>
  48. </div>
  49. <div class="optItem">
  50. <a-input-search placeholder="按名称搜索" v-model="nameSearch" style="width: 200px" @search="onSearch" />
  51. </div>
  52. <div class="optionBtn">
  53. <a-button class="search" type='primary' @click="search">查询</a-button>
  54. <a-button class="search" type='link' @click="clearAll">清空全部</a-button>
  55. </div>
  56. </div>
  57. </div>
  58. <div class="tableBOX">
  59. <div class="tableOpt">
  60. <a-button class="search" type='primary' @click="updatePackage">上传人群包</a-button>
  61. <a-button class="search" type='primary' @click="synchro">同步</a-button>
  62. <a-select size="default" placeholder='批量操作' v-model="batchValue" style="width: 200px" @change="batchChange">
  63. <a-select-option value='push'>
  64. 推送
  65. </a-select-option>
  66. <a-select-option value='delete'>
  67. 删除
  68. </a-select-option>
  69. </a-select>
  70. <a-button class="addCol" type='primary' @click="search">自定义列</a-button>
  71. </div>
  72. <div class="tableArea">
  73. <a-table
  74. :columns="columns"
  75. :data-source="dataSource"
  76. :row-selection="rowSelection"
  77. bordered
  78. >
  79. <template slot="campaignName" slot-scope="text,record">
  80. <editableCell :text="text" slotName='campaignName' @change="onCellChange(record.key, 'campaignName', $event)" />
  81. </template>
  82. <span slot="groupBudget" slot-scope="text,record">
  83. <editableCell :text="text" slotName='groupBudget' @change="onCellChange(record.key, 'groupBudget', $event)" />
  84. </span>
  85. <span slot="status" slot-scope="text,record">
  86. <span v-if="record.status">
  87. <span style='color:#33CC33'>可用</span>
  88. <span style="display:block">推送完成</span>
  89. </span>
  90. <span v-else>
  91. <span style='color:#CC3333'>不可用</span>
  92. <span style="display:block">推送失败</span>
  93. </span>
  94. </span>
  95. <span slot="operation" slot-scope="text,record">
  96. <a href="javascript:;" style="margin-right:10px">推送</a>
  97. <a-popconfirm
  98. v-if="dataSource.length"
  99. title="确定要删除吗?"
  100. @confirm="() => onDelete(record.key)"
  101. >
  102. <a href="javascript:;">删除</a>
  103. </a-popconfirm>
  104. </span>
  105. <span slot="parentGroup" slot-scope="text">
  106. <a href="javascript:;" >{{text}}</a>
  107. </span>
  108. <span slot="parentPlan" slot-scope="text">
  109. <a href="javascript:;" >{{text}}</a>
  110. </span>
  111. <span slot="cost" slot-scope="cost">
  112. {{cost | formatCurrency }}
  113. </span>
  114. </a-table>
  115. </div>
  116. </div>
  117. <!-- 上传人群包的界面 -->
  118. <a-modal v-model="updatePackageVisiable" @ok="updatePackageOk" width="750px" okText='上传'>
  119. <div id="updatePackageOk">
  120. <p class="title">上传人群包</p>
  121. <div class="updatePackagebody" >
  122. <p class="item">
  123. <span class="desc">匹配类型 <span class="redCircular"></span></span>
  124. <a-select size="default" placeholder='请选择匹配类型' v-model="matchValue" style="width: 400px" @change="matchChange">
  125. <a-select-option v-for="i in 25" :key="(i + 9).toString(36) + i">
  126. {{ (i + 9).toString(36) + i }}
  127. </a-select-option>
  128. </a-select>
  129. <span class="tips">大部分是15位数字,如123456789012345(电信是14位)</span>
  130. </p>
  131. <p class="item">
  132. <span class="desc">人群包名称 <span class="redCircular"></span></span>
  133. <a-input placeholder="请输入人群包名称" v-model="crowedName" style="width: 400px" />
  134. </p>
  135. <p class="item">
  136. <span class="desc">类型匹配 <span class="Circular"></span></span>
  137. <a-radio-group name="radioGroup" v-model="typeMatch">
  138. <a-radio :value="1">
  139. 设备号匹配
  140. </a-radio>
  141. <a-radio :value="2">
  142. uid匹配
  143. </a-radio>
  144. </a-radio-group>
  145. <span class="tips">
  146. <span class="tipsinfo">设备号匹配:定向/排除时直接按照设备号圈选,数据相对量少而精确</span>
  147. <span class="tipsinfo">UID匹配:定向/排除时圈选在该设备上登录的全部UID数据,数据相对量多而广泛</span>
  148. </span>
  149. </p>
  150. <p class="item">
  151. <span class="desc">人群分组 <span class="redCircular"></span></span>
  152. <span class="crowedGroup">
  153. <a-input-group compact >
  154. <a-input style="width: 290px" placeholder='请输入内容' v-model="populationGroup"/>
  155. <a-button type='default' @click="showGroupPage">选择已有<a-icon type="down" /></a-button>
  156. </a-input-group>
  157. <div class="crowedGroupPage" v-show="showcrowedPackage">
  158. <ul @click.stop="selectgroupName">
  159. <li class="crowedGroupPageItem">系统推荐</li>
  160. <li class="crowedGroupPageItem">哈哈哈</li>
  161. <li class="crowedGroupPageItem">嘿嘿</li>
  162. <li class="crowedGroupPageItem">魏志佳编辑</li>
  163. <li class="crowedGroupPageItem">1</li>
  164. <li class="crowedGroupPageItem">1</li>
  165. </ul>
  166. </div>
  167. </span>
  168. </p>
  169. <p class="item">
  170. <span class="desc">上传文件 <span class="redCircular"></span></span>
  171. <a-upload action="https://www.mocky.io/v2/5cc8019d300000980a055e76" directory>
  172. <a-button> <a-icon type="upload" /> 点击上传ZIP包 </a-button>
  173. </a-upload>
  174. <span class="tips">
  175. <span class="tipsTitle">文件格式说明</span>
  176. <span class="tipsinfo">1.安卓和IOS设备号,imei小写后MD5加密,IDFA大写后MD5加密,32位MD5均可</span>
  177. <span class="tipsinfo">2.上传文件内,每行一个号码,号码数量不多于3千万</span>
  178. <span class="tipsinfo">3.文件类型为单个或多个txt文件经过zip格式压缩得到,压缩后大小不超过1G</span>
  179. <span class="tipsinfo">4.上传过程中需要一定的格式校验时间,请耐心等待</span>
  180. </span>
  181. </p>
  182. </div>
  183. </div>
  184. </a-modal>
  185. </div>
  186. </template>
  187. <script>
  188. import moment from 'moment';
  189. import { getAction, postAction, downFile, downFilePost } from '@/api/manage';
  190. import editableCell from './editableCell'
  191. import { TreeSelect } from 'ant-design-vue';
  192. const SHOW_PARENT = TreeSelect.SHOW_PARENT;
  193. const columns = [
  194. {
  195. title: '名称',
  196. dataIndex: 'userName',
  197. align: 'center',
  198. key:'userName',
  199. scopedSlots: { customRender: 'userName' }
  200. },
  201. {
  202. title: '类别',
  203. dataIndex: 'department',
  204. align: 'center',
  205. key: 'department',
  206. scopedSlots: { customRender: 'department' }
  207. },
  208. {
  209. title: '创建时间',
  210. dataIndex: 'roleName',
  211. align: 'center',
  212. key: 'roleName',
  213. scopedSlots: { customRender: 'roleName' }
  214. },
  215. {
  216. title: '全部覆盖人数',
  217. dataIndex: 'cost',
  218. align: 'center',
  219. scopedSlots: { customRender: 'cost' },
  220. sorter:()=>{}
  221. },
  222. {
  223. title: '账户名称',
  224. dataIndex: 'accountName',
  225. align: 'center',
  226. scopedSlots: { customRender: 'accountName' },
  227. },
  228. {
  229. title: '状态',
  230. dataIndex: 'status',
  231. align: 'center',
  232. scopedSlots: { customRender: 'status' },
  233. },
  234. {
  235. title: '操作',
  236. dataIndex: 'operation',
  237. align: 'center',
  238. scopedSlots: { customRender: 'operation' },
  239. }
  240. ]
  241. const categoryArr=[
  242. {
  243. title:'规则',
  244. value:'guize',
  245. key:'guize'
  246. },
  247. {
  248. title:'运算',
  249. value:'yunsuan',
  250. key:'yuansuan'
  251. },
  252. {
  253. title:'上传',
  254. value:'update',
  255. key:'update'
  256. },
  257. {
  258. title:'扩展',
  259. value:'extend',
  260. key:'extend'
  261. },
  262. {
  263. title:'API',
  264. value:'API',
  265. key:'API'
  266. },
  267. {
  268. title:'付费',
  269. value:'pay',
  270. key:'pay'
  271. },
  272. {
  273. title:'主题人群',
  274. value:'crowd',
  275. key:'crowd'
  276. },
  277. ]
  278. export default {
  279. components: {
  280. editableCell,
  281. },
  282. data() {
  283. return {
  284. //treeselect的参数
  285. treeData: [],
  286. SHOW_PARENT,
  287. accountId:[],
  288. //主页的表格
  289. dataSource:[
  290. {
  291. userName:'人群纠错包1',
  292. department:'运算',
  293. roleName:'2020-6-10',
  294. cost:16897546322,
  295. accountName:'伊对-01',
  296. status:true,
  297. key:0
  298. },
  299. {
  300. userName:'人群纠错包2',
  301. department:'运算',
  302. roleName:'2020-6-10',
  303. cost:16897322,
  304. accountName:'伊对-02',
  305. status:false,
  306. key:1
  307. },
  308. {
  309. userName:'人群纠错包3',
  310. department:'运算',
  311. roleName:'2020-6-10',
  312. cost:16322,
  313. accountName:'伊对-03',
  314. status:true,
  315. key:2
  316. },
  317. ],
  318. categoryArr,
  319. columns,
  320. selectedRowKeys:[],
  321. rowSelection:{
  322. onChange: (selectedRowKeys, selectedRows) => {
  323. console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
  324. console.log(selectedRowKeys);
  325. this.selectedRowKeys=selectedRowKeys;
  326. },
  327. onSelect: (record, selected, selectedRows) => {
  328. console.log(record, selected, selectedRows);
  329. },
  330. onSelectAll: (selected, selectedRows, changeRows) => {
  331. console.log(selected, selectedRows, changeRows);
  332. },
  333. },
  334. editID:0,
  335. //查询条件
  336. treeValue: [],
  337. coverageQuantity:undefined,
  338. category:undefined,
  339. dateValue:null,//日期选择器
  340. nameSearch:undefined,
  341. batchValue:undefined, //批量操作的选中值
  342. updatePackageVisiable:false,//上传人群包的弹出框
  343. matchValue:undefined, //上传人群包的匹配框
  344. crowedName:undefined,//上传人群包名称
  345. typeMatch:2,//类型匹配
  346. showcrowedPackage:false,
  347. populationGroup:undefined,//上传人群包---人群分组
  348. }
  349. },
  350. methods: {
  351. //项目改变后的事件
  352. treeChange(val) {
  353. // console.log(this.treeData)
  354. console.log(val)
  355. this.accountId = [];
  356. //选择一个后禁用其他
  357. if(val.length==1){
  358. if(val[0]*1){
  359. let selectindex=0
  360. this.treeData.forEach((ele,index)=>{
  361. if(ele.children){
  362. ele.children.map((item,ind)=>{
  363. if(item.value==val[0]){
  364. selectindex=index;
  365. }
  366. })
  367. }
  368. });
  369. console.log(selectindex)
  370. if(selectindex ||selectindex==0){
  371. this.treeData.forEach((ele,index)=>{
  372. if(index!=selectindex){
  373. ele.disableCheckbox = true;
  374. if(ele.children){
  375. ele.children.map((item,ind)=>{
  376. item.disableCheckbox = true;
  377. })
  378. }
  379. }
  380. })
  381. }
  382. }else{
  383. // console.log(1)
  384. this.treeData.forEach((ele,index)=>{
  385. if(ele.value!=val[0]){
  386. ele.disableCheckbox = true;
  387. if(ele.children){
  388. ele.children.forEach((item,ind)=>{
  389. item.disableCheckbox = true;
  390. })
  391. }
  392. }
  393. })
  394. }
  395. }
  396. if(val.length==0){
  397. this.treeData.forEach((ele,index)=>{
  398. ele.disableCheckbox = false;
  399. this.mediaId=ele.mediaId;
  400. if(ele.children){
  401. ele.children.forEach((item,ind)=>{
  402. item.disableCheckbox = false;
  403. })
  404. }
  405. });
  406. }
  407. if(val){
  408. val.forEach((ele,index)=>{
  409. if(ele*1){
  410. this.accountId.push(ele.value)
  411. }else{
  412. let projectArr=this.treeData.filter((item)=>{
  413. return item.value==ele
  414. });
  415. console.log(projectArr)
  416. projectArr[0].children.forEach((element)=>{
  417. this.accountId.push(element.value)
  418. })
  419. }
  420. })
  421. }
  422. console.log(this.accountId)
  423. },
  424. treeSearch(){
  425. console.log(arguments);
  426. },
  427. treeSelect(){
  428. // console.log(arguments);
  429. // this.treeValue=arguments[0].value
  430. },
  431. coverChange(){
  432. },
  433. categoryChange(){
  434. },
  435. //日期改变
  436. dateChange(date, dateString) {
  437. console.log(date, dateString);
  438. this.dateValue=date;
  439. },
  440. //按名字搜索
  441. onSearch(value) {
  442. console.log(value);
  443. },
  444. //批量操作
  445. batchChange(val){
  446. console.log(val)
  447. this.batchValue=val
  448. },
  449. //查询按钮
  450. search(){
  451. },
  452. //清空全部
  453. clearAll(){
  454. this.treeValue=[];
  455. this.coverageQuantity=undefined;
  456. this.category=undefined;
  457. this.dateValue=null;//日期选择器
  458. this.nameSearch=undefined;
  459. this.batchValue=undefined; //批量操作的选中值
  460. },
  461. //表格里的删除按钮
  462. onDelete(key) {
  463. console.log(key)
  464. const dataSource = [...this.dataSource];
  465. this.dataSource = dataSource.filter(item => item.key !== key);
  466. },
  467. //上传人群包
  468. updatePackage(){
  469. this.updatePackageVisiable=true;
  470. },
  471. //同步人群包
  472. synchro(){
  473. },
  474. updatePackageOk(){
  475. this.updatePackageVisiable = false;
  476. },
  477. //匹配类型
  478. matchChange(val){
  479. console.log(val)
  480. },
  481. //上传人群包--选择已有的人群分组
  482. showGroupPage(){
  483. this.showcrowedPackage=!this.showcrowedPackage
  484. },
  485. //人群分组选中事件
  486. selectgroupName(e){
  487. // console.log(e)
  488. this.populationGroup=e.target.innerText;
  489. this.showcrowedPackage=!this.showcrowedPackage
  490. },
  491. filterOption(input, option){
  492. console.log(input, option)
  493. return (
  494. option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
  495. );
  496. },
  497. },
  498. filters: {
  499. toPercentage(val) {
  500. if(val){
  501. if(typeof val != 'string'){
  502. return (val * 100).toFixed(2) + '%'
  503. }else{
  504. return val
  505. }
  506. }else if(typeof val =='string'){
  507. return val
  508. }
  509. else{
  510. return 0
  511. }
  512. },
  513. //保留两位小数并且变成货币格式
  514. decimalsHandle(val){
  515. if(val && typeof val !='string'){
  516. if(val>=0){
  517. val=parseFloat(val).toFixed(2);
  518. let numberStr = val.toString()
  519. let str = numberStr.split('.')
  520. let str0=str[0].split('').reverse();
  521. for (let i = 0; i < str0.length; i++) {
  522. if ((i + 1) % 4 === 0) {
  523. str0.splice(i, 0, ',')
  524. }
  525. }
  526. str0.reverse()
  527. let handleResult = ''
  528. for (let j = 0; j < str0.length; j++) {
  529. handleResult += str0[j]
  530. }
  531. return handleResult+'.'+str[1]
  532. }else{
  533. val=parseFloat(val).toFixed(2);
  534. let numberStr = val.toString()
  535. let str = numberStr.split('.')
  536. let str0=str[0].substr(1).split('').reverse();
  537. for (let i = 0; i < str0.length; i++) {
  538. if ((i + 1) % 4 === 0) {
  539. str0.splice(i, 0, ',')
  540. }
  541. }
  542. str0.reverse()
  543. let handleResult = ''
  544. for (let j = 0; j < str0.length; j++) {
  545. handleResult += str0[j]
  546. }
  547. return '-'+handleResult+'.'+str[1]
  548. }
  549. }else if(typeof val =='string'){
  550. return val
  551. }
  552. else{
  553. return 0
  554. }
  555. },
  556. //变成货币格式
  557. formatCurrency(val){
  558. if((val||val==0)&& typeof val !='string'){
  559. let numberStr = val.toString()
  560. let str = numberStr.split('').reverse()
  561. for (let i = 0; i < str.length; i++) {
  562. if ((i + 1) % 4 === 0) {
  563. str.splice(i, 0, ',')
  564. }
  565. }
  566. str.reverse()
  567. let handleResult = ''
  568. for (let j = 0; j < str.length; j++) {
  569. handleResult += str[j]
  570. }
  571. return handleResult
  572. }else if(typeof val =='string'){
  573. return val
  574. }
  575. else{
  576. return '-'
  577. }
  578. }
  579. }
  580. }
  581. </script>
  582. <style lang="scss">
  583. #updatePackageOk{
  584. .title{
  585. font-size: 18px;
  586. font-weight: 700;
  587. }
  588. .desc{
  589. width: 100px;
  590. display: inline-block;
  591. text-align: right;
  592. margin-right: 10px;
  593. .redCircular,.Circular{
  594. display: inline-block;
  595. margin-left: 5px;
  596. width: 5px;
  597. height: 5px;
  598. border-radius: 50%;
  599. background-color: red;
  600. position: relative;
  601. top: -2px;
  602. }
  603. .Circular{
  604. background-color: transparent;
  605. }
  606. }
  607. .updatePackagebody{
  608. .crowedGroup{
  609. display: inline-block;
  610. width: 500px;
  611. position: relative;
  612. .crowedGroupPage{
  613. position: absolute;
  614. width: 400px;
  615. height: 250px;
  616. top: 32px;
  617. left: 0;
  618. background-color: #fff;
  619. border: 1px solid #ddd;
  620. border-top: null;
  621. border-radius: 0 0 10px 10px ;
  622. overflow:auto;
  623. z-index: 3;
  624. ul{
  625. padding: 0;
  626. margin: 0em;
  627. }
  628. .crowedGroupPageItem{
  629. height: 30px;
  630. line-height: 30px;
  631. padding: 0 15px;
  632. list-style: none;
  633. }
  634. .crowedGroupPageItem:hover{
  635. background-color: rgba(0,0,0,.2);
  636. }
  637. }
  638. }
  639. .tips{
  640. display: block;
  641. font-size: 12px;
  642. color: #999;
  643. margin-left: 110px;
  644. margin-top: 10px;
  645. .tipsinfo{
  646. display: block;
  647. margin-bottom: 6px;
  648. }
  649. .tipsTitle{
  650. display: block;
  651. color: #777;
  652. }
  653. }
  654. }
  655. }
  656. </style>
  657. <style lang="scss" scoped>
  658. .crowedPackage{
  659. .optHeader{
  660. margin: 15px 0;
  661. padding: 15px 15px;
  662. background: #fff;
  663. .optItem{
  664. display: inline-block;
  665. margin-right: 15px;
  666. .ok{
  667. margin-right: 15px;
  668. }
  669. }
  670. .top{
  671. margin-bottom: 15px;
  672. }
  673. .optionBtn{
  674. // display: inline-block;
  675. float: right;
  676. .searchBtn{
  677. margin-right: 10px;
  678. }
  679. }
  680. }
  681. .optHeader:hover{
  682. box-shadow: 0 0 15px rgba(0,0,0,.2);
  683. border-radius: 10px;
  684. }
  685. .tableBOX{
  686. background: #fff;
  687. padding: 15px 15px;
  688. .tableOpt{
  689. margin-bottom: 10px;
  690. .search{
  691. margin-right: 15px;
  692. }
  693. .addCol{
  694. float:right;
  695. }
  696. }
  697. }
  698. }
  699. </style>