Bladeren bron

feat:创意列表

pudongliang 4 maanden geleden
bovenliggende
commit
35f2303b74
39 gewijzigde bestanden met toevoegingen van 538 en 18 verwijderingen
  1. 1 1
      portal/dist/assets/accountGroup-o2TMGi0-.js
  2. 1 1
      portal/dist/assets/adAutoConf-BvQ6rQgj.js
  3. 1 1
      portal/dist/assets/adPlanConf-BuaxzpOd.js
  4. 1 1
      portal/dist/assets/auth-LssFMn2U.js
  5. 1 1
      portal/dist/assets/conversion-DdtioKgr.js
  6. 0 0
      portal/dist/assets/detail-C4OvjD9p.js
  7. 1 1
      portal/dist/assets/drama-uAeJxkrC.js
  8. 0 0
      portal/dist/assets/edit-BxVMg_u5.js
  9. 1 0
      portal/dist/assets/index-2SbHmQf5.css
  10. 0 0
      portal/dist/assets/index-2bICbFTD.js
  11. 0 0
      portal/dist/assets/index-BC3pSRFl.js
  12. 0 0
      portal/dist/assets/index-BTCCaLao.js
  13. 0 0
      portal/dist/assets/index-BpFMexG4.js
  14. 0 1
      portal/dist/assets/index-BytCFd6m.css
  15. 0 0
      portal/dist/assets/index-C0yFolE6.js
  16. 0 0
      portal/dist/assets/index-CK6L4xgh.js
  17. 0 0
      portal/dist/assets/index-CPE9aQfF.js
  18. 0 0
      portal/dist/assets/index-CS8T-sLp.js
  19. 0 0
      portal/dist/assets/index-CWUw7RCO.js
  20. 0 1
      portal/dist/assets/index-CpBVbeR3.js
  21. 0 0
      portal/dist/assets/index-D0rwIyKq.js
  22. 0 0
      portal/dist/assets/index-DHgX2Crn.js
  23. 0 0
      portal/dist/assets/index-DVvZbeFL.js
  24. 0 0
      portal/dist/assets/index-Dc6Eztqx.js
  25. 0 0
      portal/dist/assets/index-DdkWUI_W.js
  26. 0 0
      portal/dist/assets/index-DgfJYLLx.js
  27. 1 0
      portal/dist/assets/index-DrYVoRf5.css
  28. 0 0
      portal/dist/assets/index-t2y4mQRD.js
  29. 0 0
      portal/dist/assets/index-xFXc34Sm.js
  30. 1 1
      portal/dist/assets/loadPageConf-mewJwKWM.js
  31. 1 1
      portal/dist/assets/ossFile-p0pxVp9Z.js
  32. 0 0
      portal/dist/assets/request-Dql5Uaj8.js
  33. 1 1
      portal/dist/assets/user-Cp5uET7T.js
  34. 1 1
      portal/dist/index.html
  35. 42 0
      portal/src/api/adPlanCreative.js
  36. 6 0
      portal/src/router/index.js
  37. 434 0
      portal/src/views/ad-plan-creative/index.vue
  38. 22 1
      portal/src/views/oss-file/index.vue
  39. 22 5
      src/main/java/com/moka/gdtauto/controller/AdPlanCreativeController.java

+ 1 - 1
portal/dist/assets/accountGroup-D-MPNWHp.js → portal/dist/assets/accountGroup-o2TMGi0-.js

@@ -1 +1 @@
-import{r as u}from"./request-Bfw9KpS4.js";function c(t){return u({url:"/api/account-group/create",method:"post",data:t})}function e(t){return u({url:"/api/account-group/update",method:"post",data:t})}function n(t){return u({url:`/api/account-group/${t}`,method:"delete"})}function a(t=1,o=10){return u({url:"/api/account-group/list",method:"get",params:{pageNum:t,pageSize:o}})}function p(){return u({url:"/api/account-group/all",method:"get"})}function i(t){return u({url:`/api/account-group/${t}/accounts`,method:"get"})}export{i as a,p as b,c,n as d,a as g,e as u};
+import{r as u}from"./request-Dql5Uaj8.js";function c(t){return u({url:"/api/account-group/create",method:"post",data:t})}function e(t){return u({url:"/api/account-group/update",method:"post",data:t})}function n(t){return u({url:`/api/account-group/${t}`,method:"delete"})}function a(t=1,o=10){return u({url:"/api/account-group/list",method:"get",params:{pageNum:t,pageSize:o}})}function p(){return u({url:"/api/account-group/all",method:"get"})}function i(t){return u({url:`/api/account-group/${t}/accounts`,method:"get"})}export{i as a,p as b,c,n as d,a as g,e as u};

+ 1 - 1
portal/dist/assets/adAutoConf-BeDx3cat.js → portal/dist/assets/adAutoConf-BvQ6rQgj.js

@@ -1 +1 @@
-import{r as e}from"./request-Bfw9KpS4.js";function u(t){return e({url:"/api/ad-auto-conf/list",method:"get",params:t})}function n(t){return e({url:`/api/ad-auto-conf/${t}`,method:"get"})}function a(t){return e({url:"/api/ad-auto-conf",method:"post",data:t})}function r(t){return e({url:"/api/ad-auto-conf/update",method:"post",data:t})}function i(t){return e({url:`/api/ad-auto-conf/execute?id=${t}`,method:"post"})}function d(t){return e({url:"/api/execute-log/list",method:"get",params:t})}function c(t){return e({url:"/api/execute-log/getChildLog",method:"get",params:t})}export{c as a,d as b,n as c,i as e,u as g,a as s,r as u};
+import{r as e}from"./request-Dql5Uaj8.js";function u(t){return e({url:"/api/ad-auto-conf/list",method:"get",params:t})}function n(t){return e({url:`/api/ad-auto-conf/${t}`,method:"get"})}function a(t){return e({url:"/api/ad-auto-conf",method:"post",data:t})}function r(t){return e({url:"/api/ad-auto-conf/update",method:"post",data:t})}function i(t){return e({url:`/api/ad-auto-conf/execute?id=${t}`,method:"post"})}function d(t){return e({url:"/api/execute-log/list",method:"get",params:t})}function c(t){return e({url:"/api/execute-log/getChildLog",method:"get",params:t})}export{c as a,d as b,n as c,i as e,u as g,a as s,r as u};

+ 1 - 1
portal/dist/assets/adPlanConf-CXLfsqbn.js → portal/dist/assets/adPlanConf-BuaxzpOd.js

@@ -1 +1 @@
-import{r as a}from"./request-Bfw9KpS4.js";function d(n){return a({url:"/api/ad-plan-conf/reCreate",method:"post",data:n})}function o(n){return a({url:"/api/ad-plan-conf/generate-plan",method:"post",data:n})}function u(n){return a({url:`/api/ad-plan-conf/reGeneratePlan/${n}`,method:"get"})}function l(n){return a({url:`/api/ad-plan-conf/syncSystemStatus/${n}`,method:"get"})}function p(n){return a({url:"/api/ad-plan-conf/create",method:"post",data:n})}function s(n){return a({url:"/api/ad-plan-conf/update",method:"post",data:n})}function i(n=1,e=10,t={}){return a({url:"/api/ad-plan-conf/list",method:"get",params:{pageNum:n,pageSize:e,...t}})}function f(n){return a({url:`/api/ad-plan-conf/${n}`,method:"get"})}function c(n){return a({url:"/api/ad-plan/list",method:"get",params:n})}function g(n){return a({url:`/api/ad-plan-creative/plan/${n}`,method:"get"})}function m(n){return a({url:"/api/ad-plan-conf/gdtSuspendByConfId",params:{confId:n},method:"get"})}function h(n){return a({url:"/api/ad-plan-conf/gdtDeleteByConfId",params:{confId:n},method:"get"})}function C(n,e){return a({url:`/api/ad-plan-conf/gdtSuspendByPlanIds/${n}`,method:"post",data:e})}function P(n,e){return a({url:`/api/ad-plan-conf/gdtDeleteByPlanIds/${n}`,method:"post",data:e})}function y(n=[]){return a({url:"/api/ad-plan-conf/getOneCreativeImage",method:"post",data:n})}function I(){return a({url:"/api/ad-plan-conf/getOneBrandImage",method:"get"})}function B(n=[]){return a({url:"/api/ad-plan-conf/getOneCreativeVideo",method:"post",data:n})}export{y as a,I as b,B as c,o as d,p as e,f,i as g,c as h,h as i,C as j,P as k,l,u as m,g as n,d as r,m as s,s as u};
+import{r as a}from"./request-Dql5Uaj8.js";function d(n){return a({url:"/api/ad-plan-conf/reCreate",method:"post",data:n})}function o(n){return a({url:"/api/ad-plan-conf/generate-plan",method:"post",data:n})}function u(n){return a({url:`/api/ad-plan-conf/reGeneratePlan/${n}`,method:"get"})}function l(n){return a({url:`/api/ad-plan-conf/syncSystemStatus/${n}`,method:"get"})}function p(n){return a({url:"/api/ad-plan-conf/create",method:"post",data:n})}function s(n){return a({url:"/api/ad-plan-conf/update",method:"post",data:n})}function i(n=1,e=10,t={}){return a({url:"/api/ad-plan-conf/list",method:"get",params:{pageNum:n,pageSize:e,...t}})}function f(n){return a({url:`/api/ad-plan-conf/${n}`,method:"get"})}function c(n){return a({url:"/api/ad-plan/list",method:"get",params:n})}function g(n){return a({url:`/api/ad-plan-creative/plan/${n}`,method:"get"})}function m(n){return a({url:"/api/ad-plan-conf/gdtSuspendByConfId",params:{confId:n},method:"get"})}function h(n){return a({url:"/api/ad-plan-conf/gdtDeleteByConfId",params:{confId:n},method:"get"})}function C(n,e){return a({url:`/api/ad-plan-conf/gdtSuspendByPlanIds/${n}`,method:"post",data:e})}function P(n,e){return a({url:`/api/ad-plan-conf/gdtDeleteByPlanIds/${n}`,method:"post",data:e})}function y(n=[]){return a({url:"/api/ad-plan-conf/getOneCreativeImage",method:"post",data:n})}function I(){return a({url:"/api/ad-plan-conf/getOneBrandImage",method:"get"})}function B(n=[]){return a({url:"/api/ad-plan-conf/getOneCreativeVideo",method:"post",data:n})}export{y as a,I as b,B as c,o as d,p as e,f,i as g,c as h,h as i,C as j,P as k,l,u as m,g as n,d as r,m as s,s as u};

+ 1 - 1
portal/dist/assets/auth-C4PLwktH.js → portal/dist/assets/auth-LssFMn2U.js

@@ -1 +1 @@
-import{r as t}from"./request-Bfw9KpS4.js";function i(){return t({url:"/api/gdt/auth/authorize-url",method:"get"})}function n(r){return t({url:"/api/gdt/auth/refresh",method:"post",params:r})}function u(){return t({url:"/api/gdt/auth/list",method:"get"})}function a(){return u()}function o(){return t({url:"/api/gdt/auth/expiring",method:"get"})}export{o as a,a as b,i as g,u as l,n as r};
+import{r as t}from"./request-Dql5Uaj8.js";function i(){return t({url:"/api/gdt/auth/authorize-url",method:"get"})}function n(r){return t({url:"/api/gdt/auth/refresh",method:"post",params:r})}function u(){return t({url:"/api/gdt/auth/list",method:"get"})}function a(){return u()}function o(){return t({url:"/api/gdt/auth/expiring",method:"get"})}export{o as a,a as b,i as g,u as l,n as r};

+ 1 - 1
portal/dist/assets/conversion-DzOFNVGJ.js → portal/dist/assets/conversion-DdtioKgr.js

@@ -1 +1 @@
-import{r as t}from"./request-Bfw9KpS4.js";function e(n,o){return t({url:"/api/gdt/conversion/sync",method:"post",params:{orgAccountId:n,accountId:o}})}function s(n=1,o=20){return t({url:"/api/gdt/conversion/list",method:"get",params:{page:n,pageSize:o}})}function i(n){return t({url:"/api/gdt/conversion/detail",method:"get",params:{conversionId:n}})}function a(){return t({url:"/api/gdt/conversion/all",method:"get"})}export{s as a,i as b,a as g,e as s};
+import{r as t}from"./request-Dql5Uaj8.js";function e(n,o){return t({url:"/api/gdt/conversion/sync",method:"post",params:{orgAccountId:n,accountId:o}})}function s(n=1,o=20){return t({url:"/api/gdt/conversion/list",method:"get",params:{page:n,pageSize:o}})}function i(n){return t({url:"/api/gdt/conversion/detail",method:"get",params:{conversionId:n}})}function a(){return t({url:"/api/gdt/conversion/all",method:"get"})}export{s as a,i as b,a as g,e as s};

File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/detail-C4OvjD9p.js


+ 1 - 1
portal/dist/assets/drama-hUw3D0hV.js → portal/dist/assets/drama-uAeJxkrC.js

@@ -1 +1 @@
-import{r as t}from"./request-Bfw9KpS4.js";function n(a){return t({url:"/api/gdt/asset/drama/sync",method:"post",params:{accountId:a}})}function m(a=1,s=20,r=""){return t({url:"/api/gdt/asset/drama/list",method:"get",params:{dramaName:r,page:a,pageSize:s}})}function d(a){return t({url:"/api/gdt/asset/drama/myDramaAssets",method:"get",params:{dramaName:a}})}function o(){return t({url:"/api/gdt/asset/drama/sync/conf/myConf",method:"get"})}function u(a){return t({url:"/api/gdt/asset/drama/sync/conf/add",method:"post",data:a})}function i(a){return t({url:"/api/gdt/asset/drama/sync/conf/delete",method:"post",params:{id:a}})}export{o as a,u as b,i as d,m as g,d as m,n as s};
+import{r as t}from"./request-Dql5Uaj8.js";function n(a){return t({url:"/api/gdt/asset/drama/sync",method:"post",params:{accountId:a}})}function m(a=1,s=20,r=""){return t({url:"/api/gdt/asset/drama/list",method:"get",params:{dramaName:r,page:a,pageSize:s}})}function d(a){return t({url:"/api/gdt/asset/drama/myDramaAssets",method:"get",params:{dramaName:a}})}function o(){return t({url:"/api/gdt/asset/drama/sync/conf/myConf",method:"get"})}function u(a){return t({url:"/api/gdt/asset/drama/sync/conf/add",method:"post",data:a})}function i(a){return t({url:"/api/gdt/asset/drama/sync/conf/delete",method:"post",params:{id:a}})}export{o as a,u as b,i as d,m as g,d as m,n as s};

File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/edit-BxVMg_u5.js


+ 1 - 0
portal/dist/assets/index-2SbHmQf5.css

@@ -0,0 +1 @@
+[data-v-1336c792] .el-descriptions__label{font-weight:600}

File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-2bICbFTD.js


File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-BC3pSRFl.js


File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-BTCCaLao.js


File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-BpFMexG4.js


+ 0 - 1
portal/dist/assets/index-BytCFd6m.css

@@ -1 +0,0 @@
-.btn-primary[data-v-dfb828c0]{background:linear-gradient(135deg,#667eea,#764ba2);border:none;color:#fff}.btn-primary[data-v-dfb828c0]:hover{background:linear-gradient(135deg,#764ba2,#667eea)}.btn-secondary[data-v-dfb828c0]{border-color:#e5e7eb;color:#6b7280}.btn-secondary[data-v-dfb828c0]:hover{background-color:#f9fafb}[data-v-dfb828c0] .el-upload-dragger{border:2px dashed #d1d5db;border-radius:8px;background-color:#f9fafb;transition:all .3s}[data-v-dfb828c0] .el-upload-dragger:hover{border-color:#667eea;background-color:#f3f4f6}

File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-C0yFolE6.js


File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-CK6L4xgh.js


File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-CPE9aQfF.js


File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-CS8T-sLp.js


File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-CWUw7RCO.js


File diff suppressed because it is too large
+ 0 - 1
portal/dist/assets/index-CpBVbeR3.js


File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-D0rwIyKq.js


File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-DHgX2Crn.js


File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-DVvZbeFL.js


File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-Dc6Eztqx.js


File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-DdkWUI_W.js


File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-DgfJYLLx.js


+ 1 - 0
portal/dist/assets/index-DrYVoRf5.css

@@ -0,0 +1 @@
+.btn-primary[data-v-a53e3f41]{background:linear-gradient(135deg,#667eea,#764ba2);border:none;color:#fff}.btn-primary[data-v-a53e3f41]:hover{background:linear-gradient(135deg,#764ba2,#667eea)}.btn-secondary[data-v-a53e3f41]{border-color:#e5e7eb;color:#6b7280}.btn-secondary[data-v-a53e3f41]:hover{background-color:#f9fafb}[data-v-a53e3f41] .el-upload-dragger{border:2px dashed #d1d5db;border-radius:8px;background-color:#f9fafb;transition:all .3s}[data-v-a53e3f41] .el-upload-dragger:hover{border-color:#667eea;background-color:#f3f4f6}

File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-t2y4mQRD.js


File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/index-xFXc34Sm.js


+ 1 - 1
portal/dist/assets/loadPageConf-C6d2hMTN.js → portal/dist/assets/loadPageConf-mewJwKWM.js

@@ -1 +1 @@
-import{r as o}from"./request-Bfw9KpS4.js";function t(a){return o({url:"/api/load-page-conf/list",method:"get",params:a})}function n(a){return o({url:`/api/load-page-conf/${a}`,method:"get"})}function r(a){return o({url:"/api/load-page-conf",method:"post",data:a})}function u(a){return o({url:"/api/load-page-conf",method:"put",data:a})}function d(){return o({url:"/api/load-page-conf/all",method:"get"})}export{t as a,n as b,r as c,d as g,u};
+import{r as o}from"./request-Dql5Uaj8.js";function t(a){return o({url:"/api/load-page-conf/list",method:"get",params:a})}function n(a){return o({url:`/api/load-page-conf/${a}`,method:"get"})}function r(a){return o({url:"/api/load-page-conf",method:"post",data:a})}function u(a){return o({url:"/api/load-page-conf",method:"put",data:a})}function d(){return o({url:"/api/load-page-conf/all",method:"get"})}export{t as a,n as b,r as c,d as g,u};

+ 1 - 1
portal/dist/assets/ossFile-BGMq37z8.js → portal/dist/assets/ossFile-p0pxVp9Z.js

@@ -1 +1 @@
-import{r as t}from"./request-Bfw9KpS4.js";function i(e){return t({timeout:12e5,url:"/api/oss-file/upload",method:"post",data:e,headers:{"Content-Type":"multipart/form-data"}})}function o(e){return t({url:"/api/oss-file/list",method:"get",params:e})}function r(e){return t({url:`/api/oss-file/${e}`,method:"delete"})}function l(e){return t({url:"/api/oss-file/batch",method:"delete",params:{fileIds:e}})}export{l as b,r as d,o as g,i as u};
+import{r as t}from"./request-Dql5Uaj8.js";function i(e){return t({timeout:12e5,url:"/api/oss-file/upload",method:"post",data:e,headers:{"Content-Type":"multipart/form-data"}})}function o(e){return t({url:"/api/oss-file/list",method:"get",params:e})}function r(e){return t({url:`/api/oss-file/${e}`,method:"delete"})}function l(e){return t({url:"/api/oss-file/batch",method:"delete",params:{fileIds:e}})}export{l as b,r as d,o as g,i as u};

File diff suppressed because it is too large
+ 0 - 0
portal/dist/assets/request-Dql5Uaj8.js


+ 1 - 1
portal/dist/assets/user-Dgem4UUU.js → portal/dist/assets/user-Cp5uET7T.js

@@ -1 +1 @@
-import{r as e}from"./request-Bfw9KpS4.js";function t(){return e({url:"/api/user/list",method:"get"})}function u(r){return e({url:"/api/user/info",method:"get",params:r})}function n(r){return e({url:"/api/user/user-token-url",method:"get",params:r})}function o(r){return e({url:"/api/user/login",method:"post",params:r})}function a(r){return e({url:"/api/user/change-password",method:"post",params:r})}function i(r){return e({url:"/api/user/save",method:"post",params:r})}export{u as a,t as b,a as c,n as g,o as l,i as s};
+import{r as e}from"./request-Dql5Uaj8.js";function t(){return e({url:"/api/user/list",method:"get"})}function u(r){return e({url:"/api/user/info",method:"get",params:r})}function n(r){return e({url:"/api/user/user-token-url",method:"get",params:r})}function o(r){return e({url:"/api/user/login",method:"post",params:r})}function a(r){return e({url:"/api/user/change-password",method:"post",params:r})}function i(r){return e({url:"/api/user/save",method:"post",params:r})}export{u as a,t as b,a as c,n as g,o as l,i as s};

+ 1 - 1
portal/dist/index.html

@@ -5,7 +5,7 @@
     <link rel="icon" type="image/svg+xml" href="/vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>腾讯广告自动化管理后台</title>
-    <script type="module" crossorigin src="/assets/index-BM0R1d-c.js"></script>
+    <script type="module" crossorigin src="/assets/index-CpBVbeR3.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-5iqyiYFJ.css">
   </head>
   <body>

+ 42 - 0
portal/src/api/adPlanCreative.js

@@ -0,0 +1,42 @@
+import request from '../utils/request'
+
+/**
+ * 获取创意列表
+ */
+export function getCreativeList(params) {
+  return request({
+    url: '/api/ad-plan-creative/list',
+    method: 'get',
+    params
+  })
+}
+
+/**
+ * 根据计划ID获取创意列表
+ */
+export function getCreativesByPlanId(planId) {
+  return request({
+    url: `/api/ad-plan-creative/plan/${planId}`,
+    method: 'get'
+  })
+}
+
+/**
+ * 获取创意详情
+ */
+export function getCreativeDetail(creativeId) {
+  return request({
+    url: `/api/ad-plan-creative/${creativeId}`,
+    method: 'get'
+  })
+}
+
+/**
+ * 删除创意
+ */
+export function deleteCreative(creativeId) {
+  return request({
+    url: `/api/ad-plan-creative/${creativeId}`,
+    method: 'delete'
+  })
+}

+ 6 - 0
portal/src/router/index.js

@@ -60,6 +60,12 @@ const routes = [
         meta: { title: 'OSS文件管理', icon: 'FolderOpened' }
       },
       {
+        path: 'ad-plan-creative',
+        name: 'AdPlanCreative',
+        component: () => import('@/views/ad-plan-creative/index.vue'),
+        meta: { title: '广告计划创意', icon: 'Picture', noCache: true }
+      },
+      {
         path: 'profile',
         name: 'Profile',
         component: () => import('@/views/profile/index.vue'),

+ 434 - 0
portal/src/views/ad-plan-creative/index.vue

@@ -0,0 +1,434 @@
+<template>
+  <div class="space-y-6">
+    <div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
+      <div class="border-b border-gray-200 pb-4 mb-6">
+        <h2 class="text-xl font-semibold text-gray-800 flex items-center gap-2">
+          <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
+          </svg>
+          广告计划创意列表
+        </h2>
+      </div>
+
+      <!-- 筛选条件 -->
+      <div class="mb-6 p-4 bg-gray-50 rounded-lg">
+        <el-form :inline="true" :model="queryForm" class="flex flex-wrap gap-4">
+          <el-form-item label="配置ID">
+            <el-input v-model="queryForm.accountId" placeholder="请输入账户ID" clearable style="width: 150px"></el-input>
+          </el-form-item>
+
+          <el-form-item label="计划ID">
+            <el-input v-model="queryForm.adgroupId" placeholder="请输入计划ID" clearable style="width: 150px"></el-input>
+          </el-form-item>
+
+          <el-form-item label="计划ID">
+            <el-input v-model="queryForm.creativeId" placeholder="请输入计划ID" clearable style="width: 150px"></el-input>
+          </el-form-item>
+
+          <el-form-item label="MD5">
+            <el-input v-model="queryForm.md5" placeholder="请输入MD5" clearable style="width: 200px"></el-input>
+          </el-form-item>
+
+          <el-form-item label="状态">
+            <el-select v-model="queryForm.status" placeholder="全部状态" clearable style="width: 150px">
+              <el-option label="待执行" :value="1"></el-option>
+              <el-option label="执行中" :value="2"></el-option>
+              <el-option label="失败" :value="3"></el-option>
+              <el-option label="已结束" :value="4"></el-option>
+            </el-select>
+          </el-form-item>
+
+          <el-form-item>
+            <el-button type="primary" @click="handleQuery">查询</el-button>
+            <el-button @click="handleReset">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+
+      <!-- 创意列表表格 -->
+      <el-table
+        :data="creativeList"
+        style="width: 100%"
+        v-loading="loading"
+        stripe
+        :header-cell-style="{ background: '#f9fafb', color: '#374151', fontWeight: '600' }"
+      >
+        <el-table-column prop="id" label="ID" width="80"></el-table-column>
+        <el-table-column prop="accountId" label="账户ID" width="150">
+            <template #default="{ row }">
+            {{ row.accountId }}
+            <el-button v-if="row.accountId > 0" size="small"  link  @click="goToTencentByAccountId(row)">
+               <el-icon color="#409efc" class="no-inherit">
+                <Share />
+              </el-icon>
+              </el-button>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="adgroupId" label="广告组ID" width="150">
+            <template #default="{ row }">
+            {{ row.adgroupId }}
+            <el-button v-if="row.adgroupId > 0" size="small"  link  @click="goToTencentByAdgroupId(row)">
+               <el-icon color="#409efc" class="no-inherit">
+                <Share />
+              </el-icon>
+              </el-button>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="creativeId" label="创意ID" width="150">
+            <template #default="{ row }">
+            {{ row.creativeId }}
+            <el-button v-if="row.creativeId > 0" size="small"  link  @click="gotToTencentCreative(row)">
+               <el-icon color="#409efc" class="no-inherit">
+                <Share />
+              </el-icon>
+              </el-button>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="creativeName" label="创意名称" min-width="200" show-overflow-tooltip></el-table-column>
+        
+        <el-table-column label="创意文件" width="120">
+          <template #default="{ row: creative }">
+                    <el-image
+                      v-if="creative.creativeFileType === 'image' && creative.creativeFileUrl"
+                      :src="creative.creativeFileUrl"
+                      :preview-src-list="[creative.creativeFileUrl]"
+                      :preview-teleported="true"
+                      style="width: 60px; height: 60px"
+                      class="cursor-pointer rounded"
+                    />
+                    <video v-else-if="creative.creativeFileType === 'video' && creative.creativeFileUrl" controls>
+                      <source :src="creative.creativeFileUrl" type="video/mp4">
+                    </video>
+                  </template>
+        </el-table-column>
+
+        <el-table-column prop="creativeFileMd5" label="文件MD5" width="150" show-overflow-tooltip></el-table-column>
+        
+        <el-table-column prop="status" label="状态" width="100">
+          <template #default="{ row }">
+            <el-tag :type="getStatusType(row.status)" size="small">
+              {{ getStatusLabel(row.status) }}
+            </el-tag>
+          </template>
+        </el-table-column>
+
+        <el-table-column prop="approvalStatus" label="审核状态" width="100">
+          <template #default="{ row }">
+            <el-tag :type="getApprovalStatusType(row.approvalStatus)" size="small">
+              {{ getApprovalStatusLabel(row.approvalStatus) }}
+            </el-tag>
+          </template>
+        </el-table-column>
+
+        <el-table-column prop="viewCount" label="曝光次数" width="120"></el-table-column>
+        <el-table-column prop="validClickCount" label="点击次数" width="120"></el-table-column>
+        <el-table-column prop="ctr" label="点击率" width="100">
+          <template #default="{ row }">
+            {{ row.ctr ? (row.ctr * 100).toFixed(2) + '%' : '-' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="cost" label="花费(分)" width="120"></el-table-column>
+        
+        <el-table-column prop="createTime" label="创建时间" width="180">
+          <template #default="{ row }">
+            {{ formatDateTime(row.createTime) }}
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 分页 -->
+      <div class="mt-6 flex justify-end">
+        <el-pagination
+          v-model:current-page="pagination.page"
+          v-model:page-size="pagination.pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          :total="pagination.total"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </div>
+
+    <!-- 详情对话框 -->
+    <el-dialog v-model="detailDialogVisible" title="创意详情" width="700px">
+      <el-descriptions :column="2" border v-if="currentCreative">
+        <el-descriptions-item label="创意ID">{{ currentCreative.creativeId }}</el-descriptions-item>
+        <el-descriptions-item label="创意名称">{{ currentCreative.creativeName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="计划ID">{{ currentCreative.adPlanId }}</el-descriptions-item>
+        <el-descriptions-item label="广告组ID">{{ currentCreative.adgroupId }}</el-descriptions-item>
+        <el-descriptions-item label="账户ID">{{ currentCreative.accountId }}</el-descriptions-item>
+        <el-descriptions-item label="配置ID">{{ currentCreative.adPlanConfId }}</el-descriptions-item>
+        <el-descriptions-item label="状态">
+          <el-tag :type="getStatusType(currentCreative.status)" size="small">
+            {{ getStatusLabel(currentCreative.status) }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="审核状态">
+          <el-tag :type="getApprovalStatusType(currentCreative.approvalStatus)" size="small">
+            {{ currentCreative.approvalStatus || '-' }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="曝光次数">{{ currentCreative.viewCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="点击次数">{{ currentCreative.validClickCount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="点击率">{{ currentCreative.ctr ? (currentCreative.ctr * 100).toFixed(2) + '%' : '-' }}</el-descriptions-item>
+        <el-descriptions-item label="花费">{{ currentCreative.cost ? (currentCreative.cost / 100).toFixed(2) + ' 元' : '-' }}</el-descriptions-item>
+        <el-descriptions-item label="转化成本">{{ currentCreative.conversionsCost ? (currentCreative.conversionsCost / 100).toFixed(2) + ' 元' : '-' }}</el-descriptions-item>
+        <el-descriptions-item label="文件MD5">{{ currentCreative.creativeFileMd5 || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="创建时间" :span="2">{{ formatDateTime(currentCreative.createTime) }}</el-descriptions-item>
+        <el-descriptions-item label="更新时间" :span="2">{{ formatDateTime(currentCreative.updateTime) }}</el-descriptions-item>
+        <el-descriptions-item label="失败原因" :span="2" v-if="currentCreative.failReason">
+          <span class="text-red-500">{{ currentCreative.failReason }}</span>
+        </el-descriptions-item>
+      </el-descriptions>
+      
+      <template #footer>
+        <el-button @click="detailDialogVisible = false">关闭</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 图片预览对话框 -->
+    <el-dialog v-model="previewVisible" title="图片预览" width="800px">
+      <div class="flex justify-center">
+        <img :src="previewUrl" alt="预览" style="max-width: 100%; max-height: 600px;">
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+defineOptions({
+  name: 'AdPlanCreative'
+})
+
+import { ref, reactive, onMounted } from 'vue'
+import { useRoute } from 'vue-router'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { getCreativeList, getCreativeDetail, deleteCreative } from '@/api/adPlanCreative'
+import dayjs from 'dayjs'
+
+const route = useRoute()
+
+// 查询表单
+const queryForm = reactive({
+  accountId: '',
+  adgroupId: '',
+  creativeId: '',
+  md5: '',
+  status: 4
+})
+
+// 创意列表
+const creativeList = ref([])
+const loading = ref(false)
+
+// 分页
+const pagination = reactive({
+  page: 1,
+  pageSize: 10,
+  total: 0
+})
+
+// 详情对话框
+const detailDialogVisible = ref(false)
+const currentCreative = ref(null)
+
+// 预览
+const previewVisible = ref(false)
+const previewUrl = ref('')
+
+// 获取状态标签
+const getStatusLabel = (status) => {
+  const labels = {
+    1: '待执行',
+    2: '执行中',
+    3: '失败',
+    4: '已结束'
+  }
+  return labels[status] || '未知'
+}
+
+// 获取状态类型
+const getStatusType = (status) => {
+  const types = {
+    1: 'info',
+    2: 'warning',
+    3: 'danger',
+    4: 'success'
+  }
+  return types[status] || 'info'
+}
+
+/**
+ * 
+ * @param status ]     * CREATIVE_SET_APPROVAL_STATUS_UNKNOWN, // 未知
+     * CREATIVE_SET_APPROVAL_STATUS_PENDING, // 审核中
+     * CREATIVE_SET_APPROVAL_STATUS_NORMAL, // 投放中
+     * CREATIVE_SET_APPROVAL_STATUS_DENIED, // 审核拒绝
+     * CREATIVE_SET_APPROVAL_STATUS_PARTIAL_NORMAL, // 部分投放中
+ */
+// 获取审核状态类型
+const getApprovalStatusType = (status) => {
+  switch (status) {
+    case 'CREATIVE_SET_APPROVAL_STATUS_UNKNOWN': return 'info'
+    case 'CREATIVE_SET_APPROVAL_STATUS_PENDING': return 'warning'
+    case 'CREATIVE_SET_APPROVAL_STATUS_NORMAL': return 'success'
+    case 'CREATIVE_SET_APPROVAL_STATUS_DENIED': return 'danger'
+    case 'CREATIVE_SET_APPROVAL_STATUS_PARTIAL_NORMAL': return 'success'
+    default: return 'info'
+  }
+}
+// 获取审核状态标签
+const getApprovalStatusLabel = (status) => {
+  const labels = {
+    'CREATIVE_SET_APPROVAL_STATUS_UNKNOWN': '未知',
+    'CREATIVE_SET_APPROVAL_STATUS_PENDING': '审核中',
+    'CREATIVE_SET_APPROVAL_STATUS_NORMAL': '投放中',
+    'CREATIVE_SET_APPROVAL_STATUS_DENIED': '审核拒绝',
+    'CREATIVE_SET_APPROVAL_STATUS_PARTIAL_NORMAL': '部分投放中'
+  }
+  return labels[status] || '未知'
+}
+
+const goToTencentByAccountId  = (row) => {
+  const date = dayjs().format('YYYY-MM-DD')
+  window.open(`https://ad.qq.com/atlas/${row.accountId}/admanage/index?start_date=${date}&end_date=${date}&tab=adgroup&query={%22operation_status%22:[%22CALCULATE_STATUS_EXCLUDE_DEL%22],%22system_status%22:[]}`, '_blank')
+}
+
+const goToTencentByAdgroupId = (row) => {
+  const date = dayjs().format('YYYY-MM-DD')
+  window.open(`https://ad.qq.com/atlas/${row.accountId}/admanage/index?start_date=${date}&end_date=${date}&tab=adgroup&query={%22operation_status%22:[%22CALCULATE_STATUS_EXCLUDE_DEL%22],%22system_status%22:[],%22search_name%22:%22${row.adgroupId}%22}`, '_blank')
+}
+
+const gotToTencentCreative = (row) => {
+  window.open(`https://ad.qq.com/atlas/${row.accountId}/admanage/index?tab=dynamic_creative&query={%22operation_status%22:[],%22created_time%22:%22-89%22,%22fuzzy_name%22:%22${row.creativeId}%22}`, '_blank')
+}
+
+// 格式化日期时间
+const formatDateTime = (dateTime) => {
+  if (!dateTime) return '-'
+  return dateTime.replace('T', ' ')
+}
+
+// 加载创意列表
+const loadCreativeList = async () => {
+  loading.value = true
+  try {
+    const params = {
+      pageNum: pagination.page,
+      pageSize: pagination.pageSize,
+      adPlanConfId: queryForm.adPlanConfId || undefined,
+      planId: queryForm.planId || undefined,
+      md5: queryForm.md5 || undefined,
+      status: queryForm.status || undefined
+    }
+    const res = await getCreativeList(params)
+    if (res.success) {
+      creativeList.value = res.data.records || []
+      pagination.total = res.data.total || 0
+    } else {
+      ElMessage.error(res.message || '查询失败')
+    }
+  } catch (error) {
+    console.error('查询失败:', error)
+    ElMessage.error('查询失败')
+  } finally {
+    loading.value = false
+  }
+}
+
+// 查询
+const handleQuery = () => {
+  pagination.page = 1
+  loadCreativeList()
+}
+
+// 重置查询
+const handleReset = () => {
+  queryForm.adPlanConfId = ''
+  queryForm.planId = ''
+  queryForm.md5 = ''
+  queryForm.status = null
+  handleQuery()
+}
+
+// 查看详情
+const handleViewDetail = async (row) => {
+  try {
+    const res = await getCreativeDetail(row.id)
+    if (res.success) {
+      currentCreative.value = res.data
+      detailDialogVisible.value = true
+    } else {
+      ElMessage.error(res.message || '查询详情失败')
+    }
+  } catch (error) {
+    console.error('查询详情失败:', error)
+    ElMessage.error('查询详情失败')
+  }
+}
+
+// 删除创意
+const handleDelete = (row) => {
+  ElMessageBox.confirm('确定要删除该创意吗?', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(async () => {
+    try {
+      const res = await deleteCreative(row.id)
+      if (res.success) {
+        ElMessage.success('删除成功')
+        loadCreativeList()
+      } else {
+        ElMessage.error(res.message || '删除失败')
+      }
+    } catch (error) {
+      console.error('删除失败:', error)
+      ElMessage.error('删除失败')
+    }
+  })
+}
+
+// 预览文件
+const previewFile = (url) => {
+  if (!url) return
+  if (url.match(/\.(jpg|jpeg|png|gif|webp)$/i)) {
+    previewUrl.value = url
+    previewVisible.value = true
+  } else {
+    window.open(url, '_blank')
+  }
+}
+
+// 分页大小改变
+const handleSizeChange = (size) => {
+  pagination.pageSize = size
+  pagination.page = 1
+  loadCreativeList()
+}
+
+// 当前页改变
+const handleCurrentChange = (page) => {
+  pagination.page = page
+  loadCreativeList()
+}
+
+// 初始化
+onMounted(() => {
+  // 从路由参数获取MD5筛选条件
+  if (route.query.md5) {
+    queryForm.md5 = route.query.md5
+  }
+  loadCreativeList()
+})
+</script>
+
+<style scoped>
+:deep(.el-descriptions__label) {
+  font-weight: 600;
+}
+</style>

+ 22 - 1
portal/src/views/oss-file/index.vue

@@ -165,7 +165,13 @@
             {{ formatFileSize(row.fileSize) }}
           </template>
         </el-table-column>
-        <el-table-column prop="fileMd5" label="MD5" width="150" show-overflow-tooltip></el-table-column>
+        <el-table-column prop="fileMd5" label="MD5" width="150" show-overflow-tooltip>
+          <template #default="{ row }">
+            <el-button type="primary" link size="small" @click="goToCreativeList(row.fileMd5)">
+              {{ row.fileMd5 }}
+            </el-button>
+          </template>
+        </el-table-column>
         
         <el-table-column prop="folder" label="存储路径" width="150" show-overflow-tooltip></el-table-column>
         
@@ -217,10 +223,13 @@ defineOptions({
 })
 
 import { ref, reactive, computed, onMounted } from 'vue'
+import { useRouter } from 'vue-router'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { UploadFilled } from '@element-plus/icons-vue'
 import { uploadFile, getFileList, deleteFile, batchDeleteFiles } from '@/api/ossFile'
 
+const router = useRouter()
+
 // 上传表单
 const uploadFormRef = ref(null)
 const uploadRef = ref(null)
@@ -624,6 +633,18 @@ const formatDateTime = (dateTime) => {
   return dateTime.replace('T', ' ')
 }
 
+// 跳转到创意列表
+const goToCreativeList = (md5) => {
+  if (!md5) {
+    ElMessage.warning('MD5为空,无法跳转')
+    return
+  }
+  router.push({
+    path: '/ad-plan-creative',
+    query: { md5: md5 }
+  })
+}
+
 // 初始化
 onMounted(() => {
   loadFileList()

+ 22 - 5
src/main/java/com/moka/gdtauto/controller/AdPlanCreativeController.java

@@ -1,18 +1,26 @@
 package com.moka.gdtauto.controller;
 
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.moka.gdtauto.common.Constant;
 import com.moka.gdtauto.common.Result;
 import com.moka.gdtauto.entity.AdPlanCreative;
 import com.moka.gdtauto.service.AdPlanCreativeService;
+
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
 
 /**
  * 广告计划创意管理控制器
@@ -76,8 +84,12 @@ public class AdPlanCreativeController {
     @Operation(summary = "获取广告计划创意列表", description = "分页获取广告计划创意列表,可按计划ID和状态筛选")
     @GetMapping("/list")
     public Result<Page<AdPlanCreative>> getAdPlanCreatives(
+            @Parameter(description = "配置ID,可选", example = "1")
+            @RequestParam(required = false) Long adPlanConfId,
             @Parameter(description = "计划ID,可选", example = "1")
             @RequestParam(required = false) Long planId,
+            @Parameter(description = "md5", example = "1")
+            @RequestParam(required = false) String md5,
             @Parameter(description = "状态,可选:1-待执行,2-执行中,3-失败,4-已结束", example = "1")
             @RequestParam(required = false) Integer status,
             @Parameter(description = "页码", example = "1")
@@ -85,8 +97,13 @@ public class AdPlanCreativeController {
             @Parameter(description = "每页大小", example = "10")
             @RequestParam(defaultValue = "10") Integer pageSize) {
         try {
-            Page<AdPlanCreative> page = adPlanCreativeService.getAdPlanCreatives(
-                    planId, status, pageNum, pageSize);
+            Page<AdPlanCreative> page = adPlanCreativeService.lambdaQuery()
+                    .eq(adPlanConfId != null, AdPlanCreative::getAdPlanConfId, adPlanConfId)
+                    .eq(planId != null, AdPlanCreative::getAdPlanId, planId)
+                    .eq(StringUtils.isNotBlank(md5), AdPlanCreative::getCreativeFileMd5, md5)
+                    .eq(status != null, AdPlanCreative::getStatus, status)
+                    .orderByDesc(AdPlanCreative::getId)
+                    .page(new Page<>(pageNum, pageSize));
             return Result.success(page, "查询成功");
         } catch (Exception e) {
             log.error("查询广告计划创意列表失败", e);

Some files were not shown because too many files changed in this diff