OriginalityPage.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. <!-- 创意列表页 -->
  2. <template>
  3. <el-form :inline="true">
  4. <el-form-item label="名称">
  5. <el-input v-model="page.keyword" placeholder="输入创意关键词" clearable />
  6. </el-form-item>
  7. <el-form-item label="广告主">
  8. <el-select
  9. filterable
  10. v-model="page.advertiserId"
  11. placeholder="选择广告主"
  12. clearable
  13. style="width: 240px"
  14. >
  15. <el-option
  16. v-for="item in AdvertiserList"
  17. :key="item.advertiserId"
  18. :label="item.enterpriseName"
  19. :value="item.advertiserId"
  20. />
  21. </el-select>
  22. </el-form-item>
  23. <el-form-item>
  24. <el-button type="primary" @click="getOriginality">搜索</el-button>
  25. <el-button type="primary" link @click="() => creatEditPlan()">创建</el-button>
  26. </el-form-item>
  27. </el-form>
  28. <el-table :data="tableData" stripe style="width: 100%">
  29. <el-table-column prop="creativeName" label="名称">
  30. <template #default="scope">
  31. <el-button text @click="() => linkSourceMaterial(scope.row.creativeId, true)">{{
  32. scope.row.creativeName
  33. }}</el-button>
  34. </template>
  35. </el-table-column>
  36. <el-table-column prop="statusDesc" label="排期状态" width="80" />
  37. <el-table-column prop="publishStatusDesc" label="发布状态" width="80" />
  38. <el-table-column prop="startDate" label="生效日期" width="120" />
  39. <el-table-column prop="endDate" label="结束日期" width="120" />
  40. <el-table-column label="操作">
  41. <template #default="scope">
  42. <el-button
  43. v-if="scope.row.creativeId"
  44. link
  45. size="small"
  46. type="primary"
  47. @click="() => linkSourceMaterial(scope.row.creativeId)"
  48. >
  49. 创建素材
  50. </el-button>
  51. <el-popconfirm
  52. title="确定删除该创意吗?"
  53. @confirm="deleteOriginalityFun(scope.row.creativeId)"
  54. >
  55. <template #reference>
  56. <el-button link type="primary" size="small">删除</el-button>
  57. </template>
  58. </el-popconfirm>
  59. <div v-if="scope.row.status !== 7" style="display: inline-block">
  60. <el-popconfirm
  61. title="确定上线该创意吗?"
  62. v-if="scope.row.publishStatus == 6 || scope.row.publishStatus == 0"
  63. @confirm="publishAsTreeFunc(scope.row.creativeId)"
  64. >
  65. <template #reference>
  66. <el-button link type="primary" size="small">上线</el-button>
  67. </template>
  68. </el-popconfirm>
  69. <el-popconfirm
  70. title="确定下线该创意吗?"
  71. v-if="scope.row.publishStatus == 10"
  72. @confirm="changeStatus(scope.row.creativeId, 6, '下线成功')"
  73. >
  74. <template #reference>
  75. <el-button link type="primary" size="small">下线</el-button>
  76. </template>
  77. </el-popconfirm>
  78. </div>
  79. <el-button link type="primary" size="small" @click="() => creatEditPlan(scope.row)">
  80. 修改
  81. </el-button>
  82. </template>
  83. </el-table-column>
  84. </el-table>
  85. <br />
  86. <el-pagination
  87. background
  88. layout="prev, pager, next"
  89. :default-page-size="page.pageSize"
  90. :total="page.total"
  91. @current-change="changePage"
  92. />
  93. <el-dialog v-model="dialogVisible" title="创意" width="700">
  94. <el-form :model="form" label-width="auto" ref="ruleFormRef" :rules="rules">
  95. <el-form-item label="创意名称" prop="creativeName">
  96. <el-input v-model="form.creativeName" prop="creativeName" />
  97. </el-form-item>
  98. <el-form-item label="广告主" prop="advertiserId">
  99. <el-select filterable clearable v-model="form.advertiserId" placeholder="选择广告主">
  100. <el-option
  101. v-for="item in AdvertiserList"
  102. :key="item.advertiserId"
  103. :label="item.enterpriseName"
  104. :value="item.advertiserId"
  105. />
  106. </el-select>
  107. </el-form-item>
  108. <el-form-item label="周期" prop="dates">
  109. <el-date-picker
  110. v-model="form.dates"
  111. type="daterange"
  112. @change="() => getAs()"
  113. format="YYYY-MM-DD"
  114. value-format="YYYY-MM-DD"
  115. range-separator="到"
  116. start-placeholder="开始时间"
  117. end-placeholder="结束时间"
  118. />
  119. </el-form-item>
  120. <el-form-item label="站点" prop="mediaIds">
  121. <el-checkbox-group @change="() => getAs()" v-model="form.mediaIds">
  122. <el-checkbox
  123. :value="v.mediaId"
  124. disabled
  125. size="large"
  126. v-for="v in siteList"
  127. :key="v.mediaId"
  128. >{{ v.mediaName }}</el-checkbox
  129. >
  130. </el-checkbox-group>
  131. </el-form-item>
  132. <el-form-item label="投放形式" prop="showType">
  133. <el-select filterable v-model="form.showType">
  134. <el-option
  135. v-for="item in typeOptions"
  136. :key="item.value"
  137. :label="item.label"
  138. :value="item.value"
  139. />
  140. </el-select>
  141. </el-form-item>
  142. <el-form-item
  143. v-if="form.showType !== 3"
  144. :label="form.showType == 1 ? '轮播间隔' : '交替时长'"
  145. prop="showIntervalTime"
  146. >
  147. <el-input-number v-model="form.showIntervalTime" :min="0" />
  148. {{ form.showType == 1 ? '秒' : '分钟' }}
  149. </el-form-item>
  150. <el-form-item label="创意排期" prop="intervalType">
  151. <div>
  152. <el-radio-group
  153. v-model="form.intervalType"
  154. @change="(val: number) => changeIntervalType(val)"
  155. >
  156. <el-radio :value="1" size="large">全时段</el-radio>
  157. <el-radio :value="2" size="large">自定义</el-radio>
  158. </el-radio-group>
  159. <el-table v-if="form.intervalType === 2" :data="tableSchedulingData" style="width: 500px">
  160. <el-table-column fixed="left" prop="weekName" label="" width="60" />
  161. <el-table-column fixed="left" prop="selectAll" label="全选" width="60">
  162. <template #default="scope">
  163. <el-checkbox
  164. @change="(val: boolean) => selectAll(val, scope.row, scope.$index)"
  165. v-model="scope.row.selectAll"
  166. size="large"
  167. />
  168. </template>
  169. </el-table-column>
  170. <el-table-column :label="i - 1 + '时'" v-for="i in 24" center :key="i" width="60">
  171. <template #default="scope">
  172. <el-checkbox
  173. @change="(val: boolean) => select(val, scope.row, scope.$index)"
  174. v-model="scope.row[i - 1]"
  175. size="large"
  176. />
  177. </template>
  178. </el-table-column>
  179. </el-table>
  180. </div>
  181. </el-form-item>
  182. <el-form-item label="广告位" prop="slot">
  183. <el-select
  184. filterable
  185. multiple
  186. collapse-tags
  187. v-model="form.slot"
  188. placeholder="选择广告位"
  189. clearable
  190. >
  191. <el-option
  192. v-for="item in options"
  193. :key="item.slotId"
  194. :label="item.slotName"
  195. :value="item.slotId"
  196. />
  197. </el-select>
  198. </el-form-item>
  199. <el-form-item>
  200. <el-button type="primary" @click="() => createOrUpdate(ruleFormRef)">确定</el-button>
  201. </el-form-item>
  202. </el-form>
  203. </el-dialog>
  204. </template>
  205. <script setup lang="ts">
  206. import type {
  207. OriginalityItem,
  208. OriginalityList,
  209. TimeInterval,
  210. TimeIntervalList,
  211. AdvertiserItem,
  212. } from '@/types/AdListPage'
  213. import type { AsSpaceItem } from '@/types/AsSpace'
  214. import type { Pages } from '@/types/Tool'
  215. import type { SiteList } from '@/types/Site'
  216. import {
  217. getOriginalityList,
  218. getSiteList,
  219. getOriginalityDetail,
  220. getAsList,
  221. deleteOriginality,
  222. getAdvertiserList,
  223. createOriginality,
  224. updateOriginality,
  225. updateOriginalityStatus,
  226. onlineOriginality,
  227. } from '@/api/index'
  228. import { ElLoading, ElMessage } from 'element-plus'
  229. import { ref } from 'vue'
  230. import { useRouter } from 'vue-router'
  231. import type { FormInstance } from 'element-plus'
  232. import { formatDate } from '@/tool/index'
  233. const router = useRouter()
  234. const dialogVisible = ref(false)
  235. const form = ref<OriginalityItem>({})
  236. const page = ref<Pages>({
  237. pageNum: 1,
  238. pageSize: 20,
  239. total: 0,
  240. keyword: '',
  241. })
  242. const ruleFormRef = ref<FormInstance>()
  243. const AdvertiserList = ref<AdvertiserItem[]>()
  244. const options = ref<AsSpaceItem[]>([])
  245. const tableSchedulingData = ref<TimeIntervalList>([])
  246. const typeOptions = [
  247. {
  248. value: 1,
  249. label: '轮播',
  250. },
  251. {
  252. value: 2,
  253. label: '交替',
  254. },
  255. {
  256. value: 3,
  257. label: '单素材投放',
  258. },
  259. ]
  260. const tableData = ref<OriginalityList>([])
  261. const siteList = ref<SiteList>([])
  262. const rules = {
  263. creativeName: [
  264. {
  265. required: true,
  266. message: '请输入创意名称',
  267. trigger: 'blur',
  268. },
  269. ],
  270. advertiserId: [
  271. {
  272. required: true,
  273. message: '请输选择广告主',
  274. trigger: 'change',
  275. },
  276. ],
  277. dates: [
  278. {
  279. required: true,
  280. message: '请输选择广告主',
  281. trigger: 'change',
  282. },
  283. ],
  284. mediaIds: [
  285. {
  286. required: true,
  287. message: '请输选择站点',
  288. trigger: 'change',
  289. },
  290. ],
  291. showType: [
  292. {
  293. required: true,
  294. message: '请输选择投放形式',
  295. trigger: 'change',
  296. },
  297. ],
  298. intervalType: [
  299. {
  300. required: true,
  301. message: '请输选择广告位排期',
  302. trigger: 'change',
  303. },
  304. ],
  305. slot: [
  306. {
  307. required: true,
  308. message: '请输选择广告位',
  309. trigger: 'change',
  310. },
  311. ],
  312. }
  313. // 获取站点信息
  314. getSiteList().then((res) => {
  315. siteList.value = res.data
  316. })
  317. // 广告主列表
  318. getAdvertiserList({
  319. pageNum: 1,
  320. pageSize: 999999,
  321. }).then(({ data }) => {
  322. AdvertiserList.value = data && data.records && data.records.length ? data.records : []
  323. })
  324. // 获取广告位
  325. const getAs = () => {
  326. const data = form.value
  327. let timeInterval = ''
  328. for (let i = 0; i < tableSchedulingData.value.length; i++) {
  329. const v = tableSchedulingData.value[i]
  330. for (let i = 0; i < 24; i++) {
  331. timeInterval += v[i] ? '1' : '0'
  332. }
  333. }
  334. getAsList({
  335. mediaIds: data.mediaIds ? data.mediaIds : [],
  336. startDate: data.dates ? data.dates[0] : '',
  337. endDate: data.dates ? data.dates[1] : '',
  338. intervalType: data.intervalType ? data.intervalType : undefined,
  339. creativeId: data.creativeId,
  340. timeInterval,
  341. }).then(({ data }) => {
  342. options.value = data
  343. })
  344. }
  345. const changeIntervalType = (val: number) => {
  346. if (val === 2) return
  347. tableSchedulingData.value = new Array(7).fill(new Array(24).fill(true))
  348. getAs()
  349. }
  350. const deleteOriginalityFun = (id: number) => {
  351. deleteOriginality(id).then(() => {
  352. getOriginality()
  353. })
  354. }
  355. const changePage = (val: number) => {
  356. page.value.pageNum = val
  357. getOriginality()
  358. }
  359. const selectAll = (val: boolean, item: TimeInterval, index: number) => {
  360. const v: TimeInterval = tableSchedulingData.value[index]
  361. for (let i = 0; i < 24; i++) {
  362. v[i] = val
  363. }
  364. getAs()
  365. }
  366. const select = (val: boolean, item: TimeInterval, index: number) => {
  367. const v: TimeInterval = tableSchedulingData.value[index]
  368. let all = true
  369. for (let i = 0; i < 24; i++) {
  370. const iV = v[i] ? true : false
  371. all = all && iV
  372. if (!all) break
  373. }
  374. if (all !== v.selectAll) v.selectAll = all
  375. getAs()
  376. }
  377. const creatEditPlan = async (data?: OriginalityItem) => {
  378. const date_ = formatDate(new Date().getTime())
  379. let D: OriginalityItem = {
  380. intervalType: 1,
  381. dates: [
  382. date_.fmtYear + '-' + date_.fmtMonth + '-' + date_.fmtDay,
  383. date_.fmtYear + '-' + date_.fmtMonth + '-' + date_.fmtDay,
  384. ],
  385. timeInterval:
  386. '111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111',
  387. }
  388. if (data != undefined) {
  389. const loading = ElLoading.service({
  390. lock: true,
  391. text: '加载中',
  392. background: 'rgba(0, 0, 0, 0.7)',
  393. })
  394. try {
  395. const res = await getOriginalityDetail(Number(data.creativeId))
  396. D = res.data || {}
  397. loading.close()
  398. const slot = D.slotsList ? D.slotsList.map((v) => v.slotId) : D.slotIds ? D.slotIds : []
  399. form.value = {
  400. ...D,
  401. slot,
  402. showIntervalTime: 1,
  403. dates: [D.startDate ? D.startDate : '', D.endDate ? D.endDate : ''],
  404. mediaIds:
  405. D.mediaList && typeof D.mediaList == 'object'
  406. ? D.mediaList.map((v) => (typeof v !== 'number' ? v.mediaId : -1))
  407. : [],
  408. }
  409. } catch (err) {
  410. console.error(err)
  411. loading.close()
  412. form.value = {
  413. ...D,
  414. }
  415. }
  416. } else {
  417. form.value = {
  418. ...D,
  419. showIntervalTime: 1,
  420. showType: 3,
  421. }
  422. if (typeof siteList.value[0].mediaId === 'number')
  423. form.value.mediaIds = [siteList.value[0].mediaId]
  424. }
  425. const weeks: string[] = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
  426. const scheduling: TimeIntervalList = []
  427. for (let i = 0; i < 7; i++) {
  428. const times = (D.timeInterval || '').slice(i * 24, i * 24 + 24).split('')
  429. const p: TimeInterval = {
  430. selectAll: true,
  431. weekName: weeks[i],
  432. }
  433. for (let i = 0; i < 24; i++) {
  434. p[i] = times[i] === '1' ? true : false
  435. if (!p[i]) p.selectAll = false
  436. }
  437. scheduling.push(p)
  438. }
  439. tableSchedulingData.value = scheduling
  440. dialogVisible.value = !dialogVisible.value
  441. getAs()
  442. }
  443. const getOriginality = () => {
  444. getOriginalityList(page.value).then((res) => {
  445. const data = res.data || {}
  446. page.value.total = data.total
  447. tableData.value = data.records
  448. })
  449. }
  450. // 创建或更新创意
  451. const createOrUpdate = (formEl: FormInstance | undefined) => {
  452. if (!formEl) return
  453. formEl.validate((valid) => {
  454. if (valid) {
  455. const Func = form.value.creativeId ? updateOriginality : createOriginality
  456. let timeInterval = ''
  457. for (let i = 0; i < tableSchedulingData.value.length; i++) {
  458. const v = tableSchedulingData.value[i]
  459. for (let i = 0; i < 24; i++) {
  460. timeInterval += v[i] ? '1' : '0'
  461. }
  462. }
  463. const p: OriginalityItem = {
  464. creativeId: form.value.creativeId,
  465. advertiserId: form.value.advertiserId,
  466. creativeName: form.value.creativeName,
  467. intervalType: form.value.intervalType,
  468. timeInterval,
  469. startDate: form.value.dates ? form.value.dates[0] : '',
  470. endDate: form.value.dates ? form.value.dates[1] : '',
  471. showType: form.value.showType,
  472. mediaIds: form.value.mediaIds,
  473. slotIds: form.value.slot,
  474. showIntervalTime: form.value.showIntervalTime || 1,
  475. }
  476. Func(p).then(() => {
  477. ElMessage.success(form.value.creativeId ? '更新成功' : '创建成功')
  478. dialogVisible.value = false
  479. getOriginality()
  480. })
  481. }
  482. })
  483. }
  484. const changeStatus = (id: number, status: number, text: string) => {
  485. updateOriginalityStatus({ id, status }).then(() => {
  486. ElMessage.success(text)
  487. getOriginality()
  488. })
  489. }
  490. const linkSourceMaterial = (id: string | number, show = false) => {
  491. // 跳转到相关素材
  492. router.push('/authorized/sourceMaterial?creativeId=' + id + '&show=' + show)
  493. }
  494. const publishAsTreeFunc = (id: number) => {
  495. onlineOriginality(id).then(() => {
  496. ElMessage({
  497. message: '发布成功',
  498. type: 'success',
  499. })
  500. getOriginality()
  501. })
  502. }
  503. getOriginality()
  504. </script>
  505. <style></style>