setProjectBudget.vue 24 KB

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