put-ad-plan.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. <template>
  2. <div class="page-wrap page-wrap-account">
  3. <tool-bar
  4. :text="['ad_id', 'advertiser_id', 'campaign_id']"
  5. :label="['计划ID', '账号', '广告组名称']"
  6. v-model:loading="inSearching"
  7. @confirm="onSearch"
  8. >
  9. <div class="tool-bar-item" v-if="optionList.length > 0">
  10. <p class="label">计划状态</p>
  11. <a-select class="full-width" v-model:value="currentSelect">
  12. <a-select-option
  13. v-for="item in optionList"
  14. :value="item.name"
  15. :key="item.name"
  16. >{{ item.desc }}</a-select-option
  17. >
  18. </a-select>
  19. </div>
  20. </tool-bar>
  21. <div class="table-filter">
  22. <div class="item-right">
  23. <span class="label">数据类型</span>
  24. <a-select class="full-width" v-model:value="currentStats">
  25. <a-select-option
  26. v-for="item in statsList"
  27. :value="item.name"
  28. :key="item.name"
  29. >{{ item.desc }}</a-select-option
  30. >
  31. </a-select>
  32. </div>
  33. <div class="item-right">
  34. <span class="label">筛选条件:</span>
  35. <a-range-picker
  36. v-model:value="pickerFilter"
  37. @change="switchDate"
  38. :ranges="rangePick"
  39. format="YYYY/MM/DD"
  40. />
  41. </div>
  42. <a-button type="primary" @click="openBackDrawer">
  43. 回本数据
  44. </a-button>
  45. </div>
  46. <a-table
  47. :columns="columns"
  48. :data-source="list"
  49. :pagination="tablePageOptions"
  50. :loading="loading.value"
  51. @change="handleTableChange"
  52. rowKey="id"
  53. :scroll="{ x: true }"
  54. >
  55. <template #switch="{ text, record }">
  56. <a-switch
  57. v-model:checked="record.enable"
  58. @change="switchMethod(record)"
  59. />
  60. </template>
  61. <template #external_url="{ text, record }">
  62. <p @click="onGo(record)"><a>前往落地页链接</a></p>
  63. </template>
  64. <template #ad_name="{ text, record }">
  65. <p>账户名:{{ record.account_name }}</p>
  66. <p>广告名:{{ record.ad_name }}</p>
  67. <p>广告ID:{{ record.ad_id }}</p>
  68. </template>
  69. <template #dayt="{ text, record }">
  70. <a-popover title="回传配置" placement="left" trigger="click">
  71. <template #content>
  72. <div class="tab-list" style="width: 220px">
  73. <a-tabs size="small" @change="tabChangeBack">
  74. <a-tab-pane
  75. v-for="(d, i) in popForm"
  76. :key="i"
  77. :tab="d.desc"
  78. ></a-tab-pane>
  79. </a-tabs>
  80. </div>
  81. <div class="hover-content">
  82. <span class="label">回传条件</span
  83. ><a-select
  84. style="width: 150px"
  85. size="small"
  86. v-model:value="popForm[currentTbs].condition"
  87. >
  88. <a-select-option
  89. :value="item.name"
  90. v-for="item in popForm[currentTbs].report_conditions"
  91. >
  92. {{ item.desc }}
  93. </a-select-option>
  94. </a-select>
  95. </div>
  96. <div class="hover-content">
  97. <span class="label">回传开关</span>
  98. <a-switch
  99. v-model:checked="popForm[currentTbs].back_on"
  100. checked-children="开"
  101. un-checked-children="关"
  102. />
  103. </div>
  104. <div class="hover-content">
  105. <span class="label">回传比例</span
  106. ><a-input
  107. v-model:value="popForm[currentTbs].rate"
  108. size="small"
  109. style="width: 150px"
  110. type="number"
  111. placeholder="回传比例"
  112. >
  113. <template #suffix>
  114. <a-tooltip title="比例0-100">
  115. <info-circle-outlined style="color: rgba(0,0,0,.45)" />
  116. </a-tooltip>
  117. </template>
  118. </a-input>
  119. </div>
  120. <div class="hover-content">
  121. <span class="label">回传平台</span
  122. ><a-input
  123. v-model:value="popForm[currentTbs].back_platform"
  124. disabled
  125. size="small"
  126. style="width: 150px"
  127. />
  128. </div>
  129. <div
  130. class="hover-content"
  131. v-if="popForm[currentTbs].report_type == 'recharge'"
  132. >
  133. <span class="label">回传付费最低金额</span
  134. ><a-input
  135. v-model:value="popForm[currentTbs].price"
  136. size="small"
  137. style="width: 150px"
  138. type="number"
  139. placeholder="回传付费最低金额"
  140. />
  141. </div>
  142. <div class="footer-slot">
  143. <a-popconfirm
  144. title="是否要修改回传配置?"
  145. ok-text="是"
  146. cancel-text="否"
  147. @confirm="confirmEdit"
  148. >
  149. <a-button
  150. type="primary"
  151. size="small"
  152. :disabled="!hasPopFormData"
  153. @click="editBackConfig"
  154. >
  155. 修改
  156. </a-button>
  157. </a-popconfirm>
  158. </div>
  159. </template>
  160. <p @click="getBackData(record)"><a>回传</a></p>
  161. </a-popover>
  162. <p @click="getregister(record)"><a>注册用户</a></p>
  163. </template>
  164. <template #cpa_bid="{ text, record }">
  165. <editable-cell
  166. :text="`${text}`"
  167. title="预算"
  168. @change="(val) => onCellChange(record, 'cpa_bid', val)"
  169. />
  170. </template>
  171. <template #budget="{ text, record }">
  172. <editable-cell
  173. :text="`${text}`"
  174. title="出价"
  175. @change="(val) => onCellChange(record, 'budget', val)"
  176. />
  177. </template>
  178. <template #opertate="{ text, record }">
  179. <p @click="openDrawer(record)"><a>操作日志</a></p>
  180. </template>
  181. </a-table>
  182. <a-drawer
  183. title="操作日志"
  184. placement="right"
  185. :closable="false"
  186. v-model:visible="visible"
  187. >
  188. <put-data :id="currentId"></put-data>
  189. </a-drawer>
  190. <a-drawer
  191. title="回本数据"
  192. placement="right"
  193. :closable="false"
  194. v-model:visible="visible1"
  195. >
  196. <put-count
  197. :ids="backData.ids"
  198. :begin_date="backData.begin_date"
  199. :end_date="backData.end_date"
  200. :field="backData.field"
  201. ></put-count>
  202. </a-drawer>
  203. <a-drawer
  204. title="注册用户"
  205. placement="right"
  206. :closable="false"
  207. v-model:visible="registerVisable"
  208. >
  209. <register-datad
  210. :ad_lid="register.ad_lid"
  211. :back_platform="register.back_platform"
  212. ></register-datad>
  213. </a-drawer>
  214. </div>
  215. </template>
  216. <script lang="ts">
  217. import { defineComponent, reactive, toRefs, ref, unref } from "vue";
  218. import moment from "moment";
  219. import ToolBar from "@/components/tool-bar/index.vue";
  220. import PutData from "@/views/put/put-log.vue";
  221. import PutCount from "@/views/put/put-ad-count.vue";
  222. import RegisterDatad from "@/views/put/register-data.vue";
  223. import EditableCell from "@/components/edit-cell/index.vue";
  224. import usePagination from "@/hooks/usePagination";
  225. import { picker } from "@/helper/config/range";
  226. import { InfoCircleOutlined } from "@ant-design/icons-vue";
  227. import {
  228. TableColumnOfPutAdPlan,
  229. ALLCloumnList,
  230. } from "../_pageOptions/table-put";
  231. import {
  232. getADPlanlist,
  233. getCustomColumn,
  234. adChangeMoney,
  235. adChangeCrem,
  236. statusChange,
  237. getAddStatus,
  238. getAdBackPlan,
  239. setBackConfig,
  240. getAdStatus,
  241. } from "@/api";
  242. import { ADPlanItem, PageOptions, PlanBack } from "@/types/api";
  243. const PutAdPlan = defineComponent({
  244. components: {
  245. ToolBar,
  246. EditableCell,
  247. PutData,
  248. PutCount,
  249. InfoCircleOutlined,
  250. RegisterDatad,
  251. },
  252. setup() {
  253. let { loading, meta, tablePageOptions } = usePagination();
  254. let list: any[] = [],
  255. opList: any[] = [],
  256. stList: any = [];
  257. const state = reactive({
  258. platform: "platform1",
  259. list: ref<ADPlanItem[]>([]),
  260. inSearching: false,
  261. loading,
  262. currentSelect: "AD_STATUS_DELIVERY_OK",
  263. picker: [],
  264. currentId: "",
  265. visible: false,
  266. popconfirmShow: false,
  267. showPop: false,
  268. visible1: false,
  269. registerVisable: false,
  270. register: {
  271. ad_lid: 0,
  272. back_platform: "",
  273. },
  274. pickerFilter: [moment(), moment()],
  275. tablePageOptions,
  276. columns: list,
  277. backData: {
  278. ids: "",
  279. begin_date: "",
  280. end_date: "",
  281. field: "",
  282. },
  283. currentTbs: 0,
  284. hasPopFormData: false,
  285. popForm: [
  286. {
  287. id: 0,
  288. back_on: false,
  289. rate: 0,
  290. condition: "",
  291. price: 0,
  292. },
  293. ],
  294. cost_order: 0,
  295. optionList: opList,
  296. statsList: stList,
  297. currentStats: "paid_order_amount",
  298. defaultColumns: TableColumnOfPutAdPlan,
  299. fields: {},
  300. rangePick: picker,
  301. });
  302. getAddStatus().then((res) => {
  303. res.data.unshift({
  304. name: "",
  305. desc: "不限",
  306. });
  307. state.optionList = res.data;
  308. });
  309. const onSearch = async (fields: Record<string, string>) => {
  310. try {
  311. const { ad_id, advertiser_id, campaign_id, status } = fields;
  312. state.fields = fields;
  313. const data = {
  314. ad_id,
  315. advertiser_id,
  316. status: state.currentSelect,
  317. campaign_id,
  318. page: 1,
  319. };
  320. getData(data);
  321. } catch (e) {
  322. console.log(e);
  323. } finally {
  324. state.inSearching = false;
  325. }
  326. };
  327. const switchDate = (date: any, dateString: string) => {
  328. onSearch(state.fields);
  329. };
  330. const getData = (query?: any) => {
  331. const { pickerFilter } = state;
  332. let [begin_dates, end_dates] = pickerFilter;
  333. let begin_date = moment(begin_dates).format("YYYY-MM-DD");
  334. let end_date = moment(end_dates).format("YYYY-MM-DD");
  335. let data = Object.assign(
  336. {
  337. page: 1,
  338. status: "AD_STATUS_DELIVERY_OK",
  339. ...state.fields,
  340. },
  341. query || {},
  342. { begin_date, end_date, status: state.currentSelect },
  343. state.cost_order ? { cost_order:state.cost_order } : {}
  344. );
  345. getADPlanlist(data).then((res) => {
  346. let newList: any[] = res.data.list.map((item) => {
  347. item.enable = item.is_enable == 1 ? true : false;
  348. item.popShow = false;
  349. return item;
  350. });
  351. state.list = newList;
  352. meta.value = res.data.meta;
  353. state.inSearching = false;
  354. });
  355. };
  356. getAdStatus().then((res) => {
  357. state.statsList = res.data;
  358. });
  359. getCustomColumn().then((res) => {
  360. let columns: any[] = [];
  361. let blackList = [
  362. "email",
  363. "ad_name",
  364. "account_name",
  365. "ad_id",
  366. "delivery_platform",
  367. ];
  368. res.data.map((item: { desc: string; name: string }) => {
  369. let lolumnItem: {
  370. title: string;
  371. dataIndex: string;
  372. slots?: any;
  373. width?: string | number;
  374. sorter?: boolean;
  375. } = {
  376. title: item.desc,
  377. dataIndex: item.name,
  378. width: 110,
  379. };
  380. if (item.name == "external_url") {
  381. lolumnItem.slots = { customRender: "external_url" };
  382. }
  383. if (item.name == "cpa_bid" || item.name == "budget") {
  384. lolumnItem.slots = { customRender: item.name };
  385. }
  386. if (item.name == "cost") {
  387. lolumnItem.sorter = true;
  388. }
  389. columns.push(lolumnItem);
  390. });
  391. let newColunms = columns.filter(
  392. (item) => !blackList.includes(item.dataIndex)
  393. );
  394. state.columns = [];
  395. state.columns.push(...state.defaultColumns);
  396. state.columns.push(...newColunms);
  397. state.columns.push({
  398. title: "操作记录",
  399. dataIndex: "opertate",
  400. slots: { customRender: "opertate" },
  401. width: 100,
  402. });
  403. state.columns.push({
  404. title: "日志",
  405. dataIndex: "opertate",
  406. fixed: "right",
  407. slots: { customRender: "dayt" },
  408. width: 100,
  409. });
  410. });
  411. const handleTableChange = (
  412. pagination: PageOptions,
  413. filters: any,
  414. sorter: any
  415. ) => {
  416. if (sorter.columnKey == "cost") {
  417. sorter.order == "ascend"
  418. ? (state.cost_order = 1)
  419. : (state.cost_order = 2);
  420. }
  421. const { current, pageSize, total } = pagination;
  422. getData({ page: current });
  423. };
  424. getData();
  425. return { ...toRefs(state), handleTableChange, onSearch, switchDate };
  426. },
  427. methods: {
  428. moment,
  429. onGo(record: any) {
  430. window.open(record.external_url);
  431. },
  432. openDrawer(record: any) {
  433. this.visible = true;
  434. this.currentId = record.ad_id;
  435. },
  436. handleVisibleChange(visibale: boolean) {
  437. if (!visibale) {
  438. this.popconfirmShow = false;
  439. return;
  440. }
  441. if (!this.hasPopFormData) {
  442. this.popconfirmShow = false;
  443. } else {
  444. this.popconfirmShow = true;
  445. }
  446. },
  447. confirmEdit() {
  448. let { id, back_on, rate, condition, price } = this.popForm[
  449. this.currentTbs
  450. ];
  451. let data = { id, back_on: Number(back_on), rate, condition, price };
  452. setBackConfig(data).then((res) => {
  453. this.$message.success("修改成功!");
  454. });
  455. },
  456. getregister(record: any) {
  457. console.log(record);
  458. this.register.ad_lid = record.id;
  459. this.register.back_platform = record.delivery_platform;
  460. this.registerVisable = true;
  461. },
  462. onCellChange(record: any, dataIndex: string, value: string) {
  463. let ad_id = record.ad_id;
  464. if (dataIndex == "cpa_bid") {
  465. adChangeCrem({ ad_id, bid: Number(value) })
  466. .then((res) => {
  467. this.$message.success("修改成功!");
  468. })
  469. .catch((e) => {
  470. //location.reload();
  471. });
  472. }
  473. if (dataIndex == "budget") {
  474. adChangeMoney({ ad_id, budget: Number(value) })
  475. .then((res) => {
  476. this.$message.success("修改成功!");
  477. })
  478. .catch((e) => {
  479. //location.reload();
  480. });
  481. }
  482. },
  483. tabChangeBack(key: number) {
  484. this.currentTbs = key;
  485. },
  486. editBackConfig() {},
  487. switchMethod(record: any) {
  488. let ad_id = record.ad_id;
  489. statusChange({
  490. ad_id,
  491. status: record.enable ? "disable" : "enable",
  492. }).then((res) => {
  493. this.$message.success("修改广告状态成功!");
  494. });
  495. },
  496. openBackDrawer() {
  497. let ids = "";
  498. this.list.map((item: ADPlanItem) => {
  499. ids = ids + `,${item.id}`;
  500. });
  501. let [begin_dates, end_dates] = this.pickerFilter;
  502. let begin_date = moment(begin_dates).format("YYYY-MM-DD");
  503. let end_date = moment(end_dates).format("YYYY-MM-DD");
  504. this.backData.ids = ids.substring(1);
  505. this.backData.begin_date = begin_date;
  506. this.backData.end_date = end_date;
  507. this.backData.field = this.currentStats;
  508. this.visible1 = true;
  509. },
  510. handleClickChange() {
  511. console.log(this.showPop);
  512. },
  513. backPost() {},
  514. getBackData(record: any) {
  515. getAdBackPlan({
  516. ad_lid: record.id,
  517. back_platform: record.delivery_platform,
  518. }).then((res) => {
  519. let list = res.data.map((r: PlanBack) => {
  520. r.price = r.extra?.price;
  521. r.back_on = !!Number(r.back_on);
  522. return r;
  523. });
  524. list.length > 0
  525. ? (this.hasPopFormData = true)
  526. : (this.hasPopFormData = false);
  527. this.popForm = list;
  528. });
  529. },
  530. },
  531. });
  532. export default PutAdPlan;
  533. </script>
  534. <style lang="scss">
  535. .table-filter {
  536. display: flex;
  537. justify-content: flex-end;
  538. padding: 5px 0 15px;
  539. align-items: center;
  540. }
  541. .ant-drawer-content-wrapper {
  542. width: 80vw !important;
  543. }
  544. .item-right {
  545. margin-right: 10px;
  546. display: flex;
  547. align-items: center;
  548. .ant-select {
  549. width: 100px;
  550. }
  551. .label {
  552. display: inline-block;
  553. min-width: 80px;
  554. }
  555. }
  556. .hover-content {
  557. margin: 8px 0 5px;
  558. display: flex;
  559. align-items: center;
  560. .label {
  561. padding-right: 15px;
  562. display: inline-block;
  563. max-width: 72px;
  564. width: 72px;
  565. }
  566. .ant-switch {
  567. width: 50px;
  568. }
  569. }
  570. .footer-slot {
  571. margin-top: 10px;
  572. button {
  573. margin-right: 10px;
  574. }
  575. }
  576. </style>