adActivityDialog.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. <script lang="ts" setup>
  2. /**
  3. * @Name: adActivityDialog.vue
  4. * @Description:广告关联活动弹窗
  5. * @Author: xinyan
  6. */
  7. import { computed, onMounted, reactive, ref, toRefs, watch } from 'vue';
  8. import { getAdGroupList, getRelationCampaign, updateAdCampaign } from '/@/views/efTools/automation/api';
  9. import { storeToRefs } from 'pinia';
  10. import { useShopInfo } from '/@/stores/shopInfo';
  11. import { ElMessage } from 'element-plus';
  12. import TargetRuleDialog from '/@/views/efTools/automation/components/targetRuleDialog.vue';
  13. import { DocumentAdd } from '@element-plus/icons-vue';
  14. const props = defineProps({
  15. templateId: {
  16. type: [String, Number],
  17. required: true,
  18. },
  19. activeModel: {
  20. type: String,
  21. },
  22. modelValue: {
  23. type: Boolean,
  24. required: true,
  25. },
  26. });
  27. const emits = defineEmits(['update:modelValue', 'confirmSuccess']);
  28. const shopInfo = useShopInfo();
  29. const { profile } = storeToRefs(shopInfo);
  30. const { templateId } = toRefs(props);
  31. const { activeModel } = toRefs(props);
  32. const dialogVisible = ref(false);
  33. const targetRuleDialogVisible = ref(false);
  34. // 定向规则
  35. //const adGroupId = ref('')
  36. //const campaignId = ref('')
  37. const selectedTargetedRow = ref(null);
  38. const targetLength = ref(0);
  39. let selectedGroups = [];
  40. // 筛选条件
  41. const searchAdCampaign = ref('');
  42. const selectedCampaignType = ref('sp');
  43. const selectedAdGroup = ref('');
  44. const selectedStatus = ref('');
  45. const campaignType = [
  46. { value: 'sb', label: 'SB' },
  47. { value: 'sp', label: 'SP' },
  48. { value: 'sd', label: 'SD' },
  49. ];
  50. const adGroups = ref([]);
  51. const campaignStatus = [
  52. { value: 0, label: '未存档' },
  53. { value: 'ENABLED', label: '已启用' },
  54. { value: 'PAUSED', label: '已暂停' },
  55. ];
  56. // 表格
  57. const currentPage = ref(1);
  58. const pageSize = ref(25);
  59. const total = ref(0);
  60. const loading = ref(false);
  61. const xGridOne = ref(null);
  62. const xGridTwo = ref(null);
  63. let selectedAds = ref([]);
  64. const selectedColumns = computed(() => [
  65. {
  66. field: 'campaignName',
  67. title: '广告活动',
  68. slots: { default: 'campaignName_default' },
  69. treeNode: activeModel.value == 'specified' || activeModel.value == 'adGroup'
  70. },
  71. { title: '操作', width: 100, align: 'center', slots: { header: 'header_operation', default: 'default_operation' } }
  72. ]);
  73. const treeProps = computed(() => {
  74. activeModel.value = 'specified';
  75. if (activeModel.value === 'adGroup' || activeModel.value === 'specified') {
  76. return {
  77. rowField: 'campaignId',
  78. childrenField: 'campaignGroupInfo', // 子节点字段
  79. expandAll: true,
  80. };
  81. }
  82. return {};
  83. });
  84. const gridOptions = reactive({
  85. border: 'inner',
  86. height: 500,
  87. rowConfig: {
  88. isHover: true,
  89. keyField: 'campaignId',
  90. },
  91. treeConfig: treeProps,
  92. checkboxConfig: {
  93. labelField: 'name',
  94. reserve: true,
  95. checkStrictly: false,
  96. checkMethod: ({ row }) => {
  97. if (activeModel.value === 'specified') {
  98. // 如果是 specified 模式,只有已选择的行可以被取消选中
  99. return row.isSelected;
  100. }
  101. return true; // 其他模式下都可以选中
  102. }
  103. },
  104. columns: computed(() => [
  105. {
  106. field: 'campaignName',
  107. title: '广告活动',
  108. slots: { default: 'campaignName_default' },
  109. treeNode: activeModel.value == 'specified' || activeModel.value == 'adGroup'
  110. },
  111. {
  112. type: 'checkbox',
  113. width: 55,
  114. fixed: 'right',
  115. slots: activeModel.value == 'specified' ? { header: 'checkbox_header', checkbox: 'checkbox_cell' } : ''
  116. },
  117. ]),
  118. data: []
  119. });
  120. function handleCurrentChange(newPage) {
  121. currentPage.value = newPage;
  122. loading.value = true;
  123. fetchAdCampaign();
  124. }
  125. // 处理分页器每页显示条目数变化
  126. function handleSizeChange(newSize) {
  127. pageSize.value = newSize;
  128. currentPage.value = 1;
  129. fetchAdCampaign();
  130. }
  131. function handleAdCampaignChange() {
  132. localStorage.setItem('searchAdCampaign', JSON.stringify(searchAdCampaign.value));
  133. fetchAdCampaign();
  134. }
  135. async function fetchAdCampaign() {
  136. const savedAdCampaign = localStorage.getItem('searchAdCampaign');
  137. if (savedAdCampaign) {
  138. searchAdCampaign.value = JSON.parse(savedAdCampaign);
  139. }
  140. try {
  141. loading.value = true;
  142. const cachedSelectedAds = [...selectedAds.value];
  143. if (profile.value.profile_id && templateId.value){
  144. const resp = await getRelationCampaign({
  145. profileId: profile.value.profile_id,
  146. templateId: templateId.value,
  147. campaignName: searchAdCampaign.value,
  148. portfolioId: selectedAdGroup.value,
  149. campaignStatus: selectedStatus.value,
  150. campaignType: selectedCampaignType.value,
  151. page: currentPage.value,
  152. limit: pageSize.value,
  153. });
  154. gridOptions.data = resp.data;
  155. total.value = resp.total;
  156. currentPage.value = resp.page;
  157. }
  158. } catch (error) {
  159. ElMessage.error('请求广告活动数据失败');
  160. } finally {
  161. loading.value = false;
  162. }
  163. }
  164. // 处理表格复选框选择变化(是否为树形结构)
  165. // const handleGridChange = (event) => {
  166. // if (activeModel.value == 'specified' || activeModel.value == 'adGroup') {
  167. // handleSelectionChange(event);
  168. // } else {
  169. // handelSelect(event);
  170. // }
  171. // };
  172. function handleGridChange({ records, row, checked }) {
  173. if (activeModel.value === 'specified') {
  174. if (row) {
  175. if (!checked) {
  176. row.isSelected = false;
  177. updateSelectedAds();
  178. }
  179. } else {
  180. // 全选/取消全选
  181. records.forEach(record => {
  182. if (record.isSelected) {
  183. record.isSelected = checked;
  184. }
  185. });
  186. updateSelectedAds();
  187. }
  188. } else if (activeModel.value === 'adGroup') {
  189. handleSelectionChange({ records });
  190. } else {
  191. handelSelect({ records });
  192. }
  193. }
  194. function toggleCheckboxEvent(row) {
  195. if (activeModel.value === 'specified') {
  196. if (row.isSelected) {
  197. // 只有已选择的行可以被取消选中
  198. xGridOne.value.setCheckboxRow(row, false);
  199. handleGridChange({ records: [row], row, checked: false });
  200. }
  201. } else {
  202. // 其他模式下正常切换复选框状态
  203. xGridOne.value.toggleCheckboxRow(row);
  204. }
  205. }
  206. // 非树形结构的表格选择变化
  207. function handelSelect({ records }) {
  208. selectedAds.value = [
  209. ...selectedAds.value.filter(ad => ad.page !== currentPage.value),
  210. ...records.map(ad => ({ ...ad, page: currentPage.value }))
  211. ];
  212. }
  213. function updateSelectedAds() {
  214. selectedAds.value = gridOptions.data
  215. .filter(ad => ad.campaignGroupInfo && ad.campaignGroupInfo.some(group => group.isSelected))
  216. .map(ad => ({
  217. ...ad,
  218. campaignGroupInfo: ad.campaignGroupInfo.filter(group => group.isSelected),
  219. page: currentPage.value
  220. }));
  221. console.log('selectedAds.value', selectedAds.value);
  222. }
  223. // 树形结构的表格选择变化
  224. function handleSelectionChange({ records }) {
  225. selectedGroups = selectedGroups.filter(group => {
  226. return records.some(record => record.adGroupId === group.adGroupId);
  227. });
  228. let updatedRecords = [];
  229. const parentCampaignMap = new Map();
  230. records.forEach(record => {
  231. if (record.adGroupId) {
  232. // 这是一个广告组(子节点)
  233. const parentCampaign = gridOptions.data.find(campaign =>
  234. campaign.campaignGroupInfo.some(group => group.adGroupId === record.adGroupId)
  235. );
  236. if (parentCampaign) {
  237. // 如果父节点已经存在,则将子节点添加到父节点的 campaignGroupInfo 中
  238. if (parentCampaignMap.has(parentCampaign.campaignId)) {
  239. parentCampaignMap.get(parentCampaign.campaignId).campaignGroupInfo.push(record);
  240. } else {
  241. // 如果父节点不存在,则创建一个新的父节点对象
  242. parentCampaignMap.set(parentCampaign.campaignId, {
  243. campaignType: parentCampaign.campaignType,
  244. campaignId: parentCampaign.campaignId,
  245. campaignName: parentCampaign.campaignName,
  246. campaignGroupInfo: [record]
  247. });
  248. }
  249. }
  250. }
  251. });
  252. // 将所有父节点对象添加到 updatedRecords
  253. updatedRecords = Array.from(parentCampaignMap.values());
  254. // 更新选中的广告
  255. selectedAds.value = [
  256. ...updatedRecords.map(ad => ({ ...ad, page: currentPage.value })),
  257. ];
  258. }
  259. // 选择定向按钮
  260. function handleSelectTarget(row) {
  261. // 获取父节点数据
  262. const parent = gridOptions.data.find(campaign =>
  263. campaign.campaignGroupInfo.some(group => group.adGroupId === row.adGroupId)
  264. );
  265. selectedTargetedRow.value = {
  266. campaignType: parent.campaignType,
  267. campaignId: parent.campaignId,
  268. adGroupId: row.adGroupId,
  269. isSelected: row.isSelected || false,
  270. keywordInfo: row.keywordInfo || [],
  271. campaignTargetInfo: row.campaignTargetInfo || [],
  272. };
  273. targetRuleDialogVisible.value = true;
  274. }
  275. // 选择定向弹窗确认按钮
  276. function handleConfirm({ campaignInfo, targetType }) {
  277. if (!selectedTargetedRow.value) return;
  278. // 找到父级广告活动
  279. const parentCampaign = gridOptions.data.find(campaign =>
  280. campaign.campaignGroupInfo.some(group => group.adGroupId === selectedTargetedRow.value.adGroupId)
  281. );
  282. if (parentCampaign) {
  283. // 更新子节点(广告组)的信息
  284. const group = parentCampaign.campaignGroupInfo.find(group => group.adGroupId === selectedTargetedRow.value.adGroupId);
  285. if (group) {
  286. if (targetType === 'keyword') {
  287. group.keywordInfo = campaignInfo;
  288. } else if (targetType === 'target') {
  289. group.campaignTargetInfo = campaignInfo;
  290. }
  291. group.isSelected = true; // 更新子节点的选择状态
  292. group.targetLength = (group.keywordInfo?.length || 0) + (group.campaignTargetInfo?.length || 0);
  293. }
  294. // 勾选表格1中的对应行,只有在定向大于0时进行勾选
  295. if (group && group.targetLength > 0) {
  296. if (xGridOne.value) {
  297. xGridOne.value.toggleCheckboxRow(group, true); // 手动勾选复选框
  298. }
  299. }
  300. }
  301. // 更新选中的广告
  302. updateSelectedAds();
  303. }
  304. // 删除选中的广告
  305. const removeSelectedAd = async (row) => {
  306. const $grid = xGridTwo.value;
  307. if ($grid) {
  308. if (row.adGroupId) {
  309. // 删除子节点(广告组)
  310. selectedAds.value = selectedAds.value.map(ad => {
  311. if (ad.campaignGroupInfo) {
  312. return {
  313. ...ad,
  314. campaignGroupInfo: ad.campaignGroupInfo.filter(group => group.adGroupId !== row.adGroupId)
  315. };
  316. }
  317. return ad;
  318. }).filter(ad => ad.campaignGroupInfo && ad.campaignGroupInfo.length > 0);
  319. } else {
  320. // 删除父节点(广告活动)
  321. selectedAds.value = selectedAds.value.filter(ad => ad.campaignId !== row.campaignId);
  322. }
  323. await $grid.remove(row);
  324. }
  325. if (xGridOne.value) {
  326. await xGridOne.value.toggleCheckboxRow(row);
  327. }
  328. };
  329. function removeAllSelectedAds() {
  330. selectedAds.value = [];
  331. const $grid = xGridOne.value;
  332. if ($grid) {
  333. $grid.clearCheckboxRow();
  334. }
  335. }
  336. function cancel() {
  337. dialogVisible.value = false;
  338. }
  339. //TODO: 确认按钮-adGroupInfo
  340. async function confirm() {
  341. const campaignItems = selectedAds.value.map(ad => ({
  342. campaignId: ad.campaignId,
  343. campaignType: ad.campaignType
  344. }));
  345. let campaignKeywordInfo = [];
  346. let campaignTargetInfo = [];
  347. selectedAds.value.forEach(campaign => {
  348. campaign.campaignGroupInfo.forEach(group => {
  349. if (group.keywordInfo && group.keywordInfo.length > 0) {
  350. campaignKeywordInfo = campaignKeywordInfo.concat(
  351. group.keywordInfo.map(keyword => ({
  352. keywordId: keyword.keywordId,
  353. adGroup_id: group.adGroupId,
  354. bid: keyword.bid
  355. }))
  356. );
  357. }
  358. if (group.campaignTargetInfo && group.campaignTargetInfo.length > 0) {
  359. campaignTargetInfo = campaignTargetInfo.concat(
  360. group.campaignTargetInfo.map(target => ({
  361. targetId: target.targetId,
  362. adGroup_id: group.adGroupId,
  363. bid: target.bid
  364. }))
  365. );
  366. }
  367. });
  368. });
  369. const requestData = {
  370. profileId: profile.value.profile_id,
  371. templateId: templateId.value,
  372. campaignItems: campaignItems,
  373. adGroupInfo: [],
  374. campaignTargetInfo,
  375. campaignKeywordInfo
  376. };
  377. console.log('requestData', requestData);
  378. try {
  379. const response = await updateAdCampaign(requestData);
  380. if (response.code === 2000) {
  381. dialogVisible.value = false;
  382. emits('confirmSuccess');
  383. ElMessage({ message: '创建成功', type: 'success', });
  384. }
  385. } catch (error) {
  386. console.error('API error:', error);
  387. ElMessage({ message: '创建失败', type: 'error', });
  388. }
  389. }
  390. // 获取广告组下拉框
  391. async function fetchAdGroupList() {
  392. try {
  393. const resp = await getAdGroupList({
  394. profileId: profile.value.profile_id,
  395. });
  396. adGroups.value = resp.data.map((item: any) => {
  397. return {
  398. label: item.name,
  399. value: item.portfolioId
  400. };
  401. });
  402. } catch (error) {
  403. ElMessage.error('请求失败');
  404. }
  405. }
  406. const headerCellStyle = (args) => {
  407. if (args.rowIndex === 0) {
  408. return {
  409. backgroundColor: 'rgba(245, 245, 245, 0.9)',
  410. fontWeight: '500',
  411. };
  412. }
  413. };
  414. const cellStyle = () => {
  415. return {
  416. fontSize: '13px',
  417. //fontWeight: '500',
  418. };
  419. };
  420. //监控筛选条件变化
  421. watch(selectedCampaignType, () => {
  422. fetchAdCampaign();
  423. });
  424. watch(selectedAdGroup, (newVal) => {
  425. if (newVal) {
  426. selectedAdGroup.value = newVal;
  427. fetchAdCampaign();
  428. }
  429. });
  430. watch(selectedStatus, () => {
  431. fetchAdCampaign();
  432. });
  433. watch(templateId, () => {
  434. fetchAdCampaign();
  435. fetchAdGroupList();
  436. });
  437. watch(() => props.modelValue, (newValue) => {
  438. dialogVisible.value = newValue;
  439. });
  440. watch(dialogVisible, (newValue) => {
  441. emits('update:modelValue', newValue);
  442. });
  443. onMounted(() => {
  444. // fetchAdGroupList();
  445. // fetchAdCampaign();
  446. });
  447. </script>
  448. <template>
  449. <el-dialog
  450. v-model="dialogVisible"
  451. style="border-radius: 10px;"
  452. title="关联广告活动"
  453. width="1158px"
  454. >
  455. <div class="container">
  456. <div class="container-left">
  457. <el-input v-model="searchAdCampaign" clearable placeholder="请输入广告活动" style="width: 100%;"
  458. @change="handleAdCampaignChange()"></el-input>
  459. <div class="custom-inline">
  460. <el-select v-model="selectedCampaignType" placeholder="选择广告类型">
  461. <el-option v-for="item in campaignType"
  462. :key="item.value"
  463. :label="item.label"
  464. :value="item.value"
  465. ></el-option>
  466. </el-select>
  467. <el-select v-model="selectedAdGroup" clearable placeholder="广告组合" style="margin-bottom: 10px;">
  468. <el-option
  469. v-for="item in adGroups"
  470. :key="item.value"
  471. :label="item.label"
  472. :value="item.value"
  473. ></el-option>
  474. </el-select>
  475. <el-select v-model="selectedStatus" placeholder="状态" style="margin-bottom: 10px;">
  476. <el-option
  477. v-for="item in campaignStatus"
  478. :key="item.value"
  479. :label="item.label"
  480. :value="item.value"
  481. ></el-option>
  482. </el-select>
  483. </div>
  484. <vxe-grid ref="xGridOne" :cell-style="cellStyle" :header-cell-style="headerCellStyle" v-bind="gridOptions"
  485. @checkbox-change="handleGridChange" @checkbox-all="handleGridChange">
  486. <template #campaignName_default="{ row }">
  487. <el-tag
  488. v-if="row.campaignType"
  489. :color="row.campaignType === 'sb' ? '#0163d2' : (row.campaignType === 'sp' ? '#ff7424' : '#365672')"
  490. class="campaign-type"
  491. disable-transitions
  492. round>
  493. {{ row.campaignType }}
  494. </el-tag>
  495. <span> {{ row.campaignName }}</span>
  496. <span class="flex-container">
  497. {{ row.adGroupName }}
  498. <el-button
  499. v-if="row.adGroupName && activeModel==='specified'&&!row.isSelected"
  500. class="btn-link"
  501. link
  502. @click="handleSelectTarget(row)">
  503. 选择定向
  504. </el-button>
  505. <span v-else-if="row.adGroupName">已选择定向</span>
  506. </span>
  507. </template>
  508. <template #checkbox_header="{ checked, indeterminate }">
  509. <span class="custom-checkbox" @click.stop="toggleAllCheckboxEvent">
  510. <i v-if="indeterminate" class="vxe-icon-square-minus-fill" style="color: #0d84ff"></i>
  511. <i v-else-if="checked" class="vxe-icon-square-checked-fill" style="color: #0d84ff"></i>
  512. <i v-else class="vxe-icon-checkbox-unchecked" style="color: #0d84ff"></i>
  513. </span>
  514. </template>
  515. <template #checkbox_cell="{ row, checked, indeterminate }">
  516. <span class="custom-checkbox" @click.stop="toggleCheckboxEvent(row)">
  517. <i v-if="indeterminate" class="vxe-icon-square-minus-fill" style="color: #0d84ff"></i>
  518. <i v-else-if="checked" class="vxe-icon-square-checked-fill" style="color: #0d84ff"></i>
  519. <el-tooltip v-else
  520. class="box-item"
  521. content="请选择定向"
  522. effect="dark"
  523. placement="top"
  524. >
  525. <i class="vxe-icon-checkbox-unchecked" style="color: #0d84ff"></i>
  526. </el-tooltip>
  527. </span>
  528. </template>
  529. </vxe-grid>
  530. <div class="pagination-container mt-4">
  531. <el-pagination
  532. v-model:current-page="currentPage"
  533. :page-size="pageSize"
  534. :page-sizes="[10, 25, 50,100,200]"
  535. :total="total"
  536. background
  537. layout="total,sizes,prev, next, jumper"
  538. small
  539. @size-change="handleSizeChange"
  540. @current-change="handleCurrentChange"
  541. />
  542. </div>
  543. </div>
  544. <div class="container-right">
  545. <h3>已选择({{ selectedAds.length }})</h3>
  546. <vxe-grid ref="xGridTwo"
  547. :cell-style="cellStyle"
  548. :columns="selectedColumns"
  549. :data="selectedAds"
  550. :header-cell-style="headerCellStyle"
  551. :tree-config="treeProps"
  552. border="inner"
  553. height="484">
  554. <template #campaignName_default="{ row }">
  555. <template v-if="!row.adGroupId">
  556. <!-- 父节点(广告活动) -->
  557. <el-tag
  558. v-if="row.campaignType"
  559. :color="row.campaignType === 'sb' ? '#0163d2' : (row.campaignType === 'sp' ? '#ff7424' : '#365672')"
  560. class="campaign-type"
  561. disable-transitions
  562. round>
  563. {{ row.campaignType }}
  564. </el-tag>
  565. <span>{{ row.campaignName }}</span>
  566. </template>
  567. <template v-else>
  568. <!-- 子节点(广告组) -->
  569. <div class="flex-container">
  570. <span>{{ row.adGroupName }}</span>
  571. <el-button
  572. v-if="row.isSelected"
  573. :icon="DocumentAdd"
  574. class="btn-link"
  575. link
  576. @click="handleSelectTarget(row)">
  577. 共{{ row.targetLength }}个定向规则
  578. </el-button>
  579. </div>
  580. </template>
  581. </template>
  582. <template #header_operation>
  583. <el-button link size="default" style="color: #2077d7" @click="removeAllSelectedAds">删除全部</el-button>
  584. </template>
  585. <template #default_operation="{row}">
  586. <el-button type="text" @click="removeSelectedAd(row)">
  587. <CircleClose style="width:16px;color:#4b5765" />
  588. </el-button>
  589. </template>
  590. </vxe-grid>
  591. </div>
  592. </div>
  593. <template #footer>
  594. <div class="dialog-footer">
  595. <el-button @click="cancel">取消</el-button>
  596. <el-button type="primary" @click="confirm">确定</el-button>
  597. </div>
  598. </template>
  599. </el-dialog>
  600. <!--选择定向弹窗-->
  601. <TargetRuleDialog v-if="activeModel === 'specified'"
  602. v-model="targetRuleDialogVisible"
  603. :selectedTargetedRow="selectedTargetedRow"
  604. @confirm:targetRule="handleConfirm"
  605. ></TargetRuleDialog>
  606. </template>
  607. <style scoped>
  608. .pagination-container {
  609. display: flex;
  610. flex-direction: row-reverse;
  611. }
  612. .custom-inline {
  613. display: flex;
  614. justify-content: space-around;
  615. margin: 12px 0;
  616. gap: 4px;
  617. }
  618. .campaign-type {
  619. /* width: 35px; */
  620. /* text-align: center; */
  621. /* height: 22px; */
  622. /* font-size: 13px; */
  623. /* font-weight: 400; */
  624. color: #fff;
  625. border-color: #fff;
  626. /* line-height: 21px; */
  627. border-radius: 12px;
  628. margin-right: 4px;
  629. }
  630. .container {
  631. width: 100%;
  632. height: 100%;
  633. border: 1px solid #d6dbe2;
  634. border-radius: 4px;
  635. display: flex;
  636. overflow: hidden;
  637. align-content: center;
  638. flex-wrap: nowrap;
  639. flex-direction: row;
  640. /* padding: 10px; */
  641. }
  642. .container-left {
  643. width: 50%;
  644. border-right: 1px solid #d6dbe2;
  645. padding: 15px
  646. }
  647. .container-right {
  648. flex: 1;
  649. padding: 15px
  650. }
  651. .btn-link {
  652. font-size: 13px;
  653. color: #0085ff;
  654. /* ling-heigt: 23px; */
  655. }
  656. .flex-container {
  657. display: flex;
  658. justify-content: space-between;
  659. }
  660. </style>