setProjectBudget.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. <template>
  2. <div class="newPlan">
  3. <div class="moduler">
  4. <div v-if="projectInfo.length>0">
  5. <span style="margin-right:8px"> 项目:<strong>{{projectInfo[0].name||''}}</strong></span>
  6. <span style="margin-right:8px"> 推广目的:<strong>{{projectInfo[0].landingType=='APP'?'应用推广':'销售线索收集'||''}}</strong></span>
  7. <span style="margin-right:8px"> 投放媒体:<strong>{{projectInfo[0].deliveryRange=="DEFAULT"?'默认':'穿山甲'||''}}</strong></span>
  8. <span style="margin-right:8px"> 投放方式:
  9. <strong v-if="projectInfo[0].adTarget=='CPA'">优先成本</strong>
  10. <strong v-if="projectInfo[0].adTarget=='COST'">优先跑量</strong>
  11. <strong v-if="projectInfo[0].adTarget=='PUSH'">激进抢量</strong>
  12. </span>
  13. </div>
  14. </div>
  15. <div class="moduler">
  16. <a-form :form="form">
  17. <div class="moduler-title">预算与出价</div>
  18. <div class="ad-range">
  19. <div class="row-item" >
  20. <div class="hint-item"></div>
  21. <div class="label-item">
  22. <div class="text-item">投放时间</div>
  23. <!-- <div class="required-item"></div> -->
  24. </div>
  25. <div class="input-item">
  26. <a-form-item>
  27. <a-radio-group v-decorator="['scheduleType', { initialValue:'SCHEDULE_FROM_NOW' }]" @change="scheduleTypeChange">
  28. <a-radio-button value="SCHEDULE_FROM_NOW">从今天起长期投放</a-radio-button>
  29. <a-radio-button value="SCHEDULE_START_END">设置开始和结束时间</a-radio-button>
  30. </a-radio-group>
  31. <p style="margin-top:15px">
  32. <a-range-picker
  33. @change="dateChange"
  34. v-model="dateRange"
  35. :disabled-date="disabledDate"
  36. v-if="getFormData('scheduleType')=='SCHEDULE_START_END'"
  37. />
  38. </p>
  39. </a-form-item>
  40. </div>
  41. </div>
  42. <div class="row-item">
  43. <div class="hint-item"></div>
  44. <div class="label-item">
  45. <div class="text-item">投放时段</div>
  46. <!-- <div class="required-item"></div> -->
  47. </div>
  48. <div class="input-item">
  49. <a-form-item>
  50. <a-radio-group v-decorator="['scheduleTime', { initialValue:'' }]" >
  51. <a-radio-button value=''>不限</a-radio-button>
  52. <a-radio-button value="SpecifiedTImePeriod">指定时间段</a-radio-button>
  53. </a-radio-group>
  54. <div
  55. v-show="getFormData('scheduleTime')=='SpecifiedTImePeriod'"
  56. style="margin-top:15px"
  57. >
  58. <selectCheckAll
  59. style="width:100%"
  60. :scheduleTime.sync="scheduleTimeData"
  61. ref="selectCheck"
  62. />
  63. </div>
  64. </a-form-item>
  65. </div>
  66. </div>
  67. <div class="row-item">
  68. <div class="hint-item">
  69. <a-tooltip>
  70. <template slot="title">
  71. <span>单次预算修改幅度不小于100元,日修改预算不超过20次。如果是总预算广告,且设了起始日期,将要求您预算设置需要大于或等于[投放天数 * 100],即满足每日最低预算300的要求</span>
  72. </template>
  73. <a-icon type="question-circle" />
  74. </a-tooltip>
  75. </div>
  76. <div class="label-item">
  77. <div class="text-item">预算</div>
  78. <div class="required-item"></div>
  79. </div>
  80. <div class="input-item">
  81. <a-form-item>
  82. <a-input-group compact>
  83. <a-select :disabled="true" v-decorator="['budgetMode', { initialValue:'DAILY' }]">
  84. <a-select-option value="DAILY" >日预算</a-select-option>
  85. <!-- <a-select-option
  86. value="TOTAL"
  87. v-if="getFormData('smartBidType')=='SMART_BID_CUSTOM'"
  88. >总预算</a-select-option> -->
  89. </a-select>
  90. <a-input style="width: 25%" v-decorator="['budget', { initialValue:300 }]" />
  91. </a-input-group>
  92. <span class="tip" v-if="getFormData('budget')<300">请输入预算,不少于300元,不超过9999999.99元</span>
  93. </a-form-item>
  94. </div>
  95. </div>
  96. <div class="row-item" >
  97. <div class="hint-item"></div>
  98. <div class="label-item">
  99. <div class="text-item">付费方式</div>
  100. <div class="required-item"></div>
  101. </div>
  102. <div class="input-item">
  103. <a-form-item>
  104. <a-radio-group v-decorator="['pricing', { initialValue:'PRICING_OCPM' }]">
  105. <a-radio-button value="PRICING_OCPM" >按展示付费(oCPM)</a-radio-button>
  106. <!-- <a-radio-button value="PRICING_CPC" >按点击付费(CPC)</a-radio-button>
  107. <a-radio-button value="PRICING_CPM" >按展示付费(CPM)</a-radio-button>
  108. <a-radio-button value="PRICING_CPA">按转化付费(CPA)</a-radio-button>
  109. <a-radio-button value="PRICING_OCPC">按点击付费(oCPC)</a-radio-button> -->
  110. </a-radio-group>
  111. </a-form-item>
  112. </div>
  113. </div>
  114. <div class="row-item" >
  115. <div class="hint-item">
  116. <a-tooltip>
  117. <template slot="title">
  118. <span>请填入您当前预算下,每个转化事件的真实预期成本,预期成本过低可能影响模型的优化能力。此成本仅作为投放参考,实际扣费仍以后台展示为准。</span>
  119. </template>
  120. <a-icon type="question-circle" />
  121. </a-tooltip>
  122. </div>
  123. <div class="label-item">
  124. <div class="text-item">预期成本</div>
  125. <div class="required-item"></div>
  126. </div>
  127. <div class="input-item">
  128. <a-form-item>
  129. <a-input-number
  130. style="width:200px"
  131. :max="10000"
  132. :step="0.1"
  133. v-decorator="['bid', {rules:validationRules.bid} ]"
  134. />
  135. <span style="margin-left:5px">元</span>
  136. <!-- <span style="margin-left:20px">请填入必填字段,获取建议出价</span> -->
  137. </a-form-item>
  138. <span
  139. class="tip"
  140. v-if="getFormData('bid')<0.1"
  141. >请输入预期成本,不少于0.1元,不超过10000元</span>
  142. </div>
  143. </div>
  144. <div class="row-item" >
  145. <div class="hint-item"></div>
  146. <div class="label-item">
  147. <div class="text-item"></div>
  148. <!-- <div class="required-item"></div> -->
  149. </div>
  150. <div class="input-item">
  151. <a-button type='primary' @click="isSetDeepConvert" > 是否设置深度转化 </a-button>
  152. </div>
  153. </div>
  154. <div class="row-item" v-if="isDeepConvert">
  155. <div class="hint-item"> </div>
  156. <div class="label-item">
  157. <div class="text-item">深度优化方式</div>
  158. <div class="required-item"></div>
  159. </div>
  160. <div class="input-item">
  161. <a-form-item>
  162. <a-radio-group
  163. v-decorator="['deepBidType', { initialValue:'DEEP_BID_MIN' }]"
  164. >
  165. <a-radio-button value="DEEP_BID_MIN" >自定义双出价</a-radio-button>
  166. </a-radio-group>
  167. </a-form-item>
  168. </div>
  169. </div>
  170. <div class="row-item" v-if="isDeepConvert">
  171. <div class="hint-item"> </div>
  172. <div class="label-item">
  173. <div class="text-item">深度转化出价</div>
  174. <div class="required-item"></div>
  175. </div>
  176. <div class="input-item">
  177. <a-form-item>
  178. <a-input-number
  179. style="width:200px"
  180. :max="10000"
  181. :step="0.1"
  182. :min='0.1'
  183. v-decorator="['deepBid', {rules:validationRules.bid} ]"
  184. />
  185. <span style="margin-left:5px">元</span>
  186. </a-form-item>
  187. <span
  188. class="tip"
  189. v-if="getFormData('pricing')!='PRICING_CPA'&&getFormData('deepCpabid')<0.1"
  190. >请输入深度转化出价,不少于0.1元,不超过10000元</span>
  191. </div>
  192. </div>
  193. <div class="row-item">
  194. <div class="hint-item"></div>
  195. <div class="label-item">
  196. <div class="text-item">超成本自动暂停</div>
  197. <div class="required-item"></div>
  198. </div>
  199. <div class="input-item">
  200. <a-form-item>
  201. <a-radio-group v-decorator="['autoStop', { initialValue:0 }]">
  202. <a-radio-button :value="1">开启</a-radio-button>
  203. <a-radio-button :value="0">关闭</a-radio-button>
  204. </a-radio-group>
  205. </a-form-item>
  206. </div>
  207. </div>
  208. </div>
  209. </a-form>
  210. </div>
  211. <!-- 当前账户 -->
  212. <div class="moduler">
  213. <div class="adName">
  214. <span>当前账户:</span>
  215. <div class="nameList">
  216. <strong v-for="item in LHKaccountInfo" :key="item.value" class="item">{{item.title+'('+item.value+')'}}</strong>
  217. </div>
  218. </div>
  219. </div>
  220. <!-- 操作 -->
  221. <div class="moduler">
  222. <!-- <div class="moduler-title">计划名称</div> -->
  223. <div class="opt">
  224. <a-button type="default" style="margin-right:15px" @click="goBack">取消</a-button>
  225. <a-button type="primary" @click="goNext" :loading="goNextloading">开始投放</a-button>
  226. </div>
  227. </div>
  228. <!-- 投放链接 -->
  229. <div class="infoModal">
  230. <a-modal v-model="visible" title="提示" @ok="handleOk" :keyboard='false' :maskClosable='false'>
  231. <ul>
  232. <li v-for="(item,index) in errorInfo" :key="index">
  233. <span>账户{{LHKaccountInfo[index].title}}:</span>{{item.message || '设置预算与出价信息成功'}}
  234. </li>
  235. </ul>
  236. <template slot="footer">
  237. <a-button key="back" @click="handleCancel">
  238. 返回本页
  239. </a-button>
  240. <a-button key="submit" type="primary" @click="handleOk">
  241. 返回投放管家页面
  242. </a-button>
  243. </template>
  244. </a-modal>
  245. </div>
  246. </div>
  247. </template>
  248. <script>
  249. import moment from 'moment'
  250. import { getAction, postAction, downFile, downFilePost } from '@/api/manage'
  251. import selectCheckAll from '../BatchCreate/toutiaoTime'
  252. import qs from 'qs'
  253. var str = ""
  254. for (let i = 0; i < 7*48; i++) {
  255. str+='0'
  256. }
  257. export default {
  258. components: {
  259. selectCheckAll,
  260. // newDirectedPackage,
  261. },
  262. data() {
  263. return {
  264. userInfo:{},
  265. isDeepConvert:false,
  266. projectInfo:[],
  267. LHKaccountInfo:[],
  268. form: this.$form.createForm(this),
  269. showIndex:0,
  270. goNextloading:false,
  271. goPlanShowloading:false,
  272. dateRange: [moment(), moment()],
  273. scheduleTime: '',
  274. scheduleTimeData:str,
  275. visible:false,
  276. errorInfo:[],
  277. validationRules: {
  278. url: [
  279. {
  280. required: true,
  281. validator: this.validateURL,
  282. },
  283. ],
  284. name:[{
  285. required: true,
  286. validator: this.validateName,
  287. }],
  288. bid:[{
  289. required: true,
  290. message:'出价信息必须填写',
  291. // validator: this.validateBid,
  292. }],
  293. roi:[{
  294. required: true,
  295. message:'ROI系数必须填写',
  296. // validator: this.validateBid,
  297. }],
  298. },
  299. currentConvertTypes:[],
  300. convertIds:[],
  301. }
  302. },
  303. watch: {
  304. },
  305. methods: {
  306. // 投放日期改变
  307. scheduleTypeChange(val){
  308. console.log(val)
  309. if(val=='SCHEDULE_FROM_NOW'){
  310. this.dateRange=[moment(),moment().add(10,'years')]
  311. }else{
  312. this.dateRange=[moment(),moment().add(10,'days')]
  313. }
  314. },
  315. callback(val) {
  316. console.log(val);
  317. },
  318. isSetDeepConvert(){
  319. this.isDeepConvert=!this.isDeepConvert
  320. },
  321. handleOk(e) {
  322. this.visible=false;
  323. // this.$router.push('/LaunchHousekeeper/setProjectDirectional')
  324. this.$router.push({path:'/modules/LaunchHousekeeper/LaunchHousekeeperList'})
  325. this.$bus.$emit('remove', '/LaunchHousekeeper/setProjectConvert')
  326. },
  327. handleCancel(e) {
  328. this.visible = false;
  329. },
  330. //获取表单的某一个属性值
  331. getFormData(className) {
  332. return this.form.getFieldValue(className)
  333. },
  334. dateChange(date, dateString) {
  335. // console.log(date,dateString)
  336. this.form.getFieldDecorator('startTime')
  337. this.form.getFieldDecorator('endTime')
  338. this.form.setFieldsValue({
  339. startTime: date[0].format('YYYY-MM-DD'),
  340. endTime: date[1].format('YYYY-MM-DD'),
  341. })
  342. },
  343. // 验证网址
  344. IsURL(res) {
  345. // var strRegex = /^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/|www\.)(([A-Za-z0-9-~]+)\.)+([A-Za-z0-9-~\/])+$/
  346. var strRegex = /^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(([A-Za-z0-9-~]+)\.)+([A-Za-z0-9-~\/])+(.+[A-Za-z0-9-~\/])+$/
  347. var re = new RegExp(strRegex)
  348. // console.log(re.test(res))
  349. if (re.test(res)) {
  350. return true
  351. } else {
  352. return false
  353. }
  354. },
  355. validateURL(rule, value, callback) {
  356. // console.log(rule, value, callback);
  357. if (this.IsURL(value)) {
  358. callback()
  359. } else if (value && !this.IsURL(value)) {
  360. callback('链接地址无法访问,请正确输入')
  361. } else {
  362. callback('链接必须填写')
  363. }
  364. },
  365. validateName(rule, value, callback){
  366. // console.log(rule, value, callback);
  367. if (value) {
  368. callback()
  369. } else {
  370. callback('应用包名称必须填写')
  371. }
  372. },
  373. validateBid(rule, value, callback){
  374. // console.log(rule, value, callback);
  375. if (value) {
  376. callback()
  377. } else {
  378. callback('出价必须填写')
  379. }
  380. },
  381. disabledDate(current) {
  382. // console.log(current)
  383. return current && current < moment()
  384. },
  385. goBack() {
  386. this.$router.replace('/modules/LaunchHousekeeper/LaunchHousekeeperList')
  387. this.$bus.$emit('remove', '/LaunchHousekeeper/setProjectBudget')
  388. },
  389. // 保存并新建创意
  390. goNext() {
  391. this.$nextTick(()=>{
  392. this.form.validateFields((err, values) => {
  393. if(this.getFormData('bid')<this.getFormData('budget')){
  394. if (!err) {
  395. console.log('Received values of form: ', values)
  396. this.goNextloading=true;
  397. if(this.getFormData('scheduleTime')!=''){
  398. values.weekTime=this.scheduleTimeData
  399. }else{
  400. values.weekTime=''
  401. }
  402. if(this.getFormData('scheduleType')=='SCHEDULE_FROM_NOW'){
  403. values.startTime=this.dateRange[0].format('YYYY-MM-DD');
  404. values.endTime= this.dateRange[1].add(10,"years").format('YYYY-MM-DD');
  405. }else{
  406. values.startTime=this.dateRange[0].format('YYYY-MM-DD');
  407. values.endTime= this.dateRange[1].format('YYYY-MM-DD');
  408. }
  409. let params=[];
  410. params=this.projectInfo.map((item,index)=>{
  411. return {
  412. ...item,
  413. ...values,
  414. userId:this.userInfo.value.id
  415. }
  416. })
  417. console.log(values)
  418. postAction('/adlab/Project/setBudget', params).then((res) => {
  419. console.log(res)
  420. if (res.success) {
  421. this.goNextloading=false;
  422. if(res.result){
  423. this.errorInfo=res.result;
  424. this.visible=true;
  425. }
  426. } else {
  427. this.goNextloading=false;
  428. this.$message.error(res.message)
  429. }
  430. })
  431. }else{
  432. this.$message.error('请正确填写必填项目')
  433. }
  434. }else{
  435. this.$message.error('出价不得高于组预算')
  436. }
  437. })
  438. })
  439. },
  440. EchoPageInfo(){
  441. },
  442. },
  443. mounted() {
  444. this.$bus.$emit('remove', '/LaunchHousekeeper/setProjectCreate')
  445. let projectInfo=sessionStorage.getItem('projectInfo');
  446. if(projectInfo){
  447. this.projectInfo=JSON.parse(projectInfo)
  448. }
  449. let LHKaccountInfo=sessionStorage.getItem('LHKaccountInfo');
  450. if(LHKaccountInfo){
  451. this.LHKaccountInfo=JSON.parse(LHKaccountInfo)
  452. }
  453. let editLHKProjectInfo=sessionStorage.getItem('editLHKProjectInfo');
  454. if(editLHKProjectInfo){
  455. this.LHKaccountInfo.push(editLHKProjectInfo);
  456. }
  457. let userInfo=localStorage.getItem('pro__Login_Userinfo')
  458. if(userInfo){
  459. this.userInfo=JSON.parse(userInfo)
  460. }
  461. },
  462. }
  463. </script>
  464. <style lang="scss">
  465. .newPlan{
  466. .ant-btn-danger {
  467. background-color: #ff4d4f !important;
  468. }
  469. }
  470. .ant-tooltip-inner,
  471. .ant-tooltip,
  472. .ant-tooltip-arrow::before {
  473. background-color: #fff;
  474. color: #333;
  475. }
  476. .ant-tooltip-inner {
  477. span {
  478. display: block;
  479. }
  480. }
  481. .ant-tooltip {
  482. max-width: 450px;
  483. }
  484. .newPlan {
  485. .ant-radio-button-wrapper {
  486. min-width: 80px;
  487. text-align: center;
  488. }
  489. .ant-form-item {
  490. -webkit-box-sizing: border-box;
  491. box-sizing: border-box;
  492. margin: 0;
  493. padding: 0;
  494. color: rgba(0, 0, 0, 0.65);
  495. font-size: 14px;
  496. font-variant: tabular-nums;
  497. line-height: 1.5;
  498. list-style: none;
  499. -webkit-font-feature-settings: 'tnum';
  500. font-feature-settings: 'tnum';
  501. vertical-align: top;
  502. }
  503. .directedBody {
  504. .ant-form-item {
  505. margin-bottom: 20px;
  506. }
  507. }
  508. }
  509. </style>
  510. <style lang="scss" scoped>
  511. .newPlan {
  512. font-family: PingFangSC-Regular, tahoma, arial, 'Hiragino Sans GB', 'Microsoft yahei', sans-serif;
  513. color: #333;
  514. ul,
  515. li {
  516. margin: 0;
  517. padding: 0;
  518. }
  519. .moduler {
  520. background: #fff;
  521. margin-bottom: 16px;
  522. position: relative;
  523. padding: 32px 24px 48px 32px;
  524. border-radius: 4px;
  525. box-shadow: 0px 1px 6px 0px rgba(0, 0, 0, 0.05);
  526. .moduler-title {
  527. display: flex;
  528. align-items: center;
  529. font-size: 22px;
  530. margin-bottom: 20px;
  531. }
  532. .row-item {
  533. display: flex;
  534. justify-content: flex-start;
  535. align-items: center;
  536. margin-bottom: 32px;
  537. }
  538. .row-item:last-of-type {
  539. margin-bottom: 0;
  540. }
  541. .row-item .hint-item {
  542. width: 24px;
  543. min-width: 24px;
  544. align-self: flex-start;
  545. line-height: 20px;
  546. height: 20px;
  547. margin-top: 9px;
  548. }
  549. .row-item .label-item {
  550. position: relative;
  551. align-items: flex-start;
  552. align-self: flex-start;
  553. line-height: 20px;
  554. height: 20px;
  555. margin-top: 10px;
  556. width: 80px;
  557. min-width: 80px;
  558. .text-item {
  559. font-size: 14px;
  560. width: 60px;
  561. }
  562. }
  563. .row-item .required-item {
  564. width: 4px;
  565. height: 4px;
  566. border-radius: 2px;
  567. background: #f45858;
  568. position: absolute;
  569. right: 10px;
  570. top: 50%;
  571. transform: translateY(-50%);
  572. }
  573. .row-item .input-item {
  574. min-width: 480px;
  575. position: relative;
  576. // height: 35px;
  577. .tip {
  578. font-size: 12px;
  579. color: tomato;
  580. }
  581. }
  582. .adName{
  583. display: flex;
  584. .nameList{
  585. width: 90%;
  586. }
  587. .item{
  588. display: inline-block;
  589. width: 30%;
  590. margin-left: 15px;
  591. margin-bottom: 10px;
  592. }
  593. }
  594. }
  595. .opt {
  596. text-align: right;
  597. }
  598. }
  599. </style>