Compare commits
32 Commits
Author | SHA1 | Date |
---|---|---|
nili | 4b4ed2753b | 4 months ago |
nili | 9c96c32eaa | 4 months ago |
nili | 4b3bd4183a | 5 months ago |
nili | 3936c14914 | 5 months ago |
nili | e807fae293 | 5 months ago |
nili | c47c7a2fd4 | 5 months ago |
nili | 3f425c7e6a | 5 months ago |
nili | 4a570f095f | 5 months ago |
nili | b1753932bf | 5 months ago |
nili | d8fc93870f | 5 months ago |
nili | 323a4a432e | 6 months ago |
nili | 41382e6d63 | 6 months ago |
nili | 3d0e402d60 | 6 months ago |
nili | d83dd20a4b | 6 months ago |
nili | 1a501d7a71 | 6 months ago |
nili | 754ef9d79f | 6 months ago |
nili | 4e68217a6d | 6 months ago |
nili | 3a0e509073 | 6 months ago |
nili | 63a8a998cb | 6 months ago |
nili | 17edac139d | 6 months ago |
nili | 2a4bfbdf7c | 6 months ago |
nili | a5116d8b8b | 6 months ago |
nili | 346c62510c | 6 months ago |
nili | fee12c8f86 | 6 months ago |
nili | c430459158 | 6 months ago |
nili | acb998f415 | 6 months ago |
nili | 74c844f580 | 6 months ago |
nili | cb0683c49e | 6 months ago |
nili | 85960dd63d | 6 months ago |
nili | c0f8fbfdbd | 6 months ago |
nili | e482f2bbd5 | 6 months ago |
nili | 7a9c574cad | 6 months ago |
@ -0,0 +1 @@ |
|||
!function(){"use strict";var t="/".replace(/([^/])$/,"$1/"),e=location.pathname,n=e.startsWith(t)&&decodeURI("/".concat(e.slice(t.length)));if(n){var a=document,c=a.head,r=a.createElement.bind(a),i=function(t,e,n){var a,c=e.r[t]||(null===(a=Object.entries(e.r).find((function(e){var n=e[0];return new RegExp("^".concat(n.replace(/\/:[^/]+/g,"/[^/]+").replace("/*","/.+"),"$")).test(t)})))||void 0===a?void 0:a[1]);return null==c?void 0:c.map((function(t){var a=e.f[t][1],c=e.f[t][0];return{type:c.split(".").pop(),url:"".concat(n.publicPath).concat(c),attrs:[["data-".concat(e.b),"".concat(e.p,":").concat(a)]]}}))}(n,{"p":"ant-design-pro","b":"webpack","f":[["p__AppManagement.26abbc6f.async.js",39],["51.a2ea1b3b.async.js",51],["60.7143e42f.async.js",60],["89.ee83384e.async.js",89],["134.6fc81d91.async.js",134],["169.57647c7d.async.js",169],["p__Welcome.8c10044c.async.js",185],["t__plugin-layout__Layout.6cae69f5.chunk.css",301],["t__plugin-layout__Layout.bb80f58e.async.js",301],["335.78ea2fbf.async.js",335],["364.782d5fa1.async.js",364],["p__User__Login__index.0020071b.async.js",366],["p__DeviceOwnerApp.ede954ff.async.js",371],["390.41467286.async.js",390],["397.fb5f72c1.async.js",397],["491.ddc0d25a.async.js",491],["p__Bind.bfb7c4f6.async.js",557],["559.016bfdbb.async.js",559],["p__404.0c100574.async.js",571],["p__DeviceOverview.80620b07.async.js",581],["p__Overview.f1f0c4a9.async.js",628],["750.d34920f3.async.js",750],["p__AdminManagement.13887fed.async.js",816],["822.ac7ab1f5.async.js",822],["p__AdvRecordList.6031b104.async.js",857],["905.6e225d1b.async.js",905],["930.fca7adbf.async.js",930]],"r":{"/*":[18,25],"/":[3,4,7,8,17,25],"/welcome":[2,6,3,4,7,8,17,25],"/overview":[2,4,5,10,15,17,20,21,23,26,3,7,8,25],"/advList":[1,2,4,5,9,10,21,23,24,25,26,3,7,8,17],"/appList":[1,2,4,5,9,10,12,21,23,25,26,3,7,8,17],"/incomeOverview":[2,4,5,10,15,17,19,21,23,26,3,7,8,25],"/adminList":[1,5,22,23,3,4,7,8,17,25],"/bind":[4,5,14,16,17,26,3,7,8,25],"/appManage":[0,1,2,4,5,9,10,14,21,23,25,26,3,7,8,17],"/user/login":[2,4,5,10,11,23]}},{publicPath:"/"});null==i||i.forEach((function(t){var e,n=t.type,a=t.url;if("js"===n)(e=r("script")).src=a,e.async=!0;else{if("css"!==n)return;(e=r("link")).href=a,e.rel="preload",e.as="style"}t.attrs.forEach((function(t){e.setAttribute(t[0],t[1]||"")})),c.appendChild(e)}))}}(); |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 41 KiB |
@ -1,13 +1,17 @@ |
|||
<!DOCTYPE html><html><head> |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> |
|||
<meta http-equiv="X-UA-Compatible" content="ie=edge"> |
|||
<link rel="shortcut icon" href="logo.png"> |
|||
<title>Ant Design Pro</title> |
|||
<link rel="stylesheet" href="/umi.1ca9308c.css"> |
|||
<script async="" src="/scripts/loading.js"></script> |
|||
<link rel="stylesheet" href="/umi.f9018446.css"> |
|||
<script async src="/scripts/loading.js"></script> |
|||
<script src="/_umi_route_preload_helper.f438c131.js"></script> |
|||
</head> |
|||
<body> |
|||
<div id="root"></div> |
|||
<script src="/umi.cecb10de.js"></script> |
|||
|
|||
</body></html> |
|||
<script src="/umi.ee9dc4cd.js"></script> |
|||
</body> |
|||
</html> |
After Width: | Height: | Size: 41 KiB |
@ -1 +0,0 @@ |
|||
"use strict";(self.webpackChunkant_design_pro=self.webpackChunkant_design_pro||[]).push([[857],{46701:function(D,s,e){e.r(s);var l=e(15009),d=e.n(l),o=e(99289),m=e.n(o),c=e(48357),v=e(90930),p=e(76104),x=e(35312),E=e(67294),u=e(85893),I=function(){var h=(0,E.useRef)(),T=(0,x.useIntl)(),f=[{title:"\u8BBE\u5907Id",dataIndex:"deviceId",valueType:"textarea",ellipsis:!0,copyable:!0},{title:"\u5E94\u7528\u540D\u79F0",hideInSearch:!0,dataIndex:"appName",valueType:"textarea"},{title:"\u5E73\u53F0",dataIndex:"platform",valueEnum:{1:{text:"\u7A7F\u5C71\u7532"},2:{text:"\u817E\u8BAF"},3:{text:"\u767E\u5EA6\u8054\u76DF"},4:{text:"Mintegral"},5:{text:"\u5FEB\u624B"},6:{text:"\u6E38\u53EF\u8D62"},7:{text:"Sigmob"},8:{text:"Admob"}}},{title:"\u5E7F\u544A\u7C7B\u578B",dataIndex:"advType",valueEnum:{1:{text:"\u6A2A\u5E45",status:"Default"},2:{text:"\u63D2\u9875",status:"Processing"},3:{text:"\u6FC0\u52B1\u89C6\u9891",status:"Success"}}},{title:"ecpm(\u5143)",dataIndex:"ecpm",hideInSearch:!0,renderText:function(a){return a/100}},{title:"\u8BBE\u5907\u54C1\u724C",dataIndex:"deviceBrand",hideInSearch:!0,valueType:"textarea"},{title:"\u8BBE\u5907\u540D",dataIndex:"deviceName",hideInSearch:!0,valueType:"textarea"},{title:"ip",dataIndex:"ip",hideInSearch:!0,valueType:"textarea"},{title:"\u65F6\u95F4",hideInSearch:!0,dataIndex:"createdAt",valueType:"dateTime"},{title:"\u65F6\u95F4",hideInTable:!0,dataIndex:"createdAt",valueType:"dateRange"}];return(0,u.jsx)(v._z,{children:(0,u.jsx)(p.Z,{headerTitle:T.formatMessage({id:"pages.searchTable.title",defaultMessage:"Enquiry form"}),actionRef:h,rowKey:"key",search:{labelWidth:120},request:function(){var i=m()(d()().mark(function a(M){var _,r,n;return d()().wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,(0,c._5)(M);case 2:return n=t.sent,t.abrupt("return",{data:(_=n.data)===null||_===void 0?void 0:_.data,total:(r=n.data)===null||r===void 0?void 0:r.total,success:!0});case 4:case"end":return t.stop()}},a)}));return function(a){return i.apply(this,arguments)}}(),columns:f})})};s.default=I}}]); |
@ -0,0 +1 @@ |
|||
"use strict";(self.webpackChunkant_design_pro=self.webpackChunkant_design_pro||[]).push([[39],{40110:function(y,c,e){var E=e(1413),s=e(67294),P=e(509),m=e(91146),h=function(D,O){return s.createElement(m.Z,(0,E.Z)((0,E.Z)({},D),{},{ref:O,icon:P.Z}))},u=s.forwardRef(h);c.Z=u},84702:function(y,c,e){e.r(c);var E=e(15009),s=e.n(E),P=e(99289),m=e.n(P),h=e(5574),u=e.n(h),A=e(90930),D=e(25335),O=e(35312),i=e(8232),o=e(55102),Z=e(42075),M=e(14726),L=e(71230),I=e(10397),W=e(55241),g=e(17788),p=e(67294),x=e(48357),F=e(40110),n=e(85893),S=function(){var b=(0,p.useState)(!1),U=u()(b,2),V=U[0],v=U[1],$=i.Z.useForm(),z=u()($,1),f=z[0],G=(0,p.useState)([]),T=u()(G,2),N=T[0],J=T[1],X=(0,O.useModel)("@@initialState"),j=X.initialState,d=j==null?void 0:j.currentUser,H=d&&d.role&&d.role<2,Q=function(r){f.setFieldsValue(r),v(!0)},B=[{title:"\u6E38\u620F",dataIndex:"name",filterDropdown:function(r){var a=r.setSelectedKeys,l=r.selectedKeys,t=r.confirm,ne=r.clearFilters;return(0,n.jsxs)("div",{style:{padding:8},children:[(0,n.jsx)(o.Z,{placeholder:"\u641C\u7D22\u8D26\u53F7",value:l[0],onChange:function(R){return a(R.target.value?[R.target.value]:[])},onPressEnter:function(){return t()},style:{marginBottom:8,display:"block"}}),(0,n.jsxs)(Z.Z,{children:[(0,n.jsx)(M.ZP,{onClick:function(){return t()},type:"primary",children:"\u641C\u7D22"}),(0,n.jsx)(M.ZP,{onClick:function(){return ne()},type:"link",children:"\u6E05\u9664"})]})]})},onFilter:function(r,a){var l;return a==null||(l=a.name)===null||l===void 0?void 0:l.includes(r)},filterIcon:function(r){return(0,n.jsx)(F.Z,{style:{color:r?"#1890ff":void 0},onPointerEnterCapture:void 0,onPointerLeaveCapture:void 0})}},{title:"\u4E0B\u8F7D\u5730\u5740",dataIndex:"url",renderText:function(r){return r&&(0,n.jsxs)(L.Z,{align:"middle",children:[(0,n.jsx)(I.Z,{size:80,value:r||"",bordered:!1}),(0,n.jsx)(W.Z,{overlayInnerStyle:{padding:0},content:(0,n.jsx)(I.Z,{size:300,value:r||"",bordered:!1}),children:(0,n.jsx)("a",{children:"\u67E5\u770B\u5927\u56FE"})})]})}}],Y=[].concat(B,[{title:"secret",dataIndex:"secret",ellipsis:!0,copyable:!0},{title:"\u64CD\u4F5C",width:80,renderText:function(r){return(0,n.jsx)("a",{onClick:function(){return Q(r)},children:"\u7F16\u8F91"},"edit")}}]),k=function(){f.submit()},w=function(){v(!1)},K=function(){var _=m()(s()().mark(function r(){var a;return s()().wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,(0,x.ln)();case 2:a=t.sent,a.data&&J(a.data);case 4:case"end":return t.stop()}},r)}));return function(){return _.apply(this,arguments)}}();(0,p.useEffect)(function(){K()},[]);var q=function(){f.resetFields(),v(!0)},ee=function(){var _=m()(s()().mark(function r(a){return s()().wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return t.prev=0,t.next=3,(0,x.XJ)(a);case 3:t.next=8;break;case 5:return t.prev=5,t.t0=t.catch(0),t.abrupt("return");case 8:v(!1),K();case 10:case"end":return t.stop()}},r,null,[[0,5]])}));return function(a){return _.apply(this,arguments)}}();return(0,n.jsxs)(A._z,{children:[d&&d.role&&d.role<2&&(0,n.jsx)(M.ZP,{style:{marginBottom:"20px"},onClick:q,children:"\u65B0\u5EFA\u5E94\u7528"}),(0,n.jsx)(D.Z,{search:!1,columns:H?Y:B,dataSource:N}),(0,n.jsx)(g.Z,{title:"\u7F16\u8F91",visible:V,onOk:k,onCancel:w,children:(0,n.jsxs)(i.Z,{form:f,onFinish:ee,children:[(0,n.jsx)(i.Z.Item,{name:"id",style:{display:"none"},children:(0,n.jsx)(o.Z,{type:"hidden"})}),(0,n.jsx)(i.Z.Item,{name:"code",style:{display:"none"},children:(0,n.jsx)(o.Z,{type:"hidden"})}),(0,n.jsx)(i.Z.Item,{label:"\u5E94\u7528\u540D",name:"name",children:(0,n.jsx)(o.Z,{})}),(0,n.jsx)(i.Z.Item,{label:"\u4E0B\u8F7D\u5730\u5740",name:"url",children:(0,n.jsx)(o.Z,{})})]})})]})};c.default=S}}]); |
@ -0,0 +1 @@ |
|||
"use strict";(self.webpackChunkant_design_pro=self.webpackChunkant_design_pro||[]).push([[628],{58451:function(_e,p,_){_.r(p);var U=_(97857),A=_.n(U),C=_(15009),r=_.n(C),T=_(99289),E=_.n(T),R=_(5574),u=_.n(R),f=_(48357),I=_(92713),x=_(90930),m=_(39615),B=_(57470),v=_(64317),W=_(34540),g=_(27484),K=_.n(g),L=_(9220),i=_(67294),y=_(96974),a=_(85893),S=function(){var F=(0,i.useState)(),D=u()(F,2),o=D[0],Z=D[1],V=(0,i.useState)([]),P=u()(V,2),O=P[0],z=P[1],G=(0,i.useState)([]),h=u()(G,2),$=h[0],H=h[1],J=(0,i.useState)(!1),M=u()(J,2),N=M[0],Q=M[1],X=(0,y.UO)(),j=X.code,Y=function(){var t=E()(r()().mark(function e(){var n;return r()().wrap(function(s){for(;;)switch(s.prev=s.next){case 0:return s.next=2,(0,f.d1)({appCode:j});case 2:n=s.sent,n.data&&Z(n.data);case 4:case"end":return s.stop()}},e)}));return function(){return t.apply(this,arguments)}}(),w=function(){var t=E()(r()().mark(function e(){var n;return r()().wrap(function(s){for(;;)switch(s.prev=s.next){case 0:return s.next=2,(0,f.ln)();case 2:n=s.sent,n.data&&H(n.data);case 4:case"end":return s.stop()}},e)}));return function(){return t.apply(this,arguments)}}(),b=function(){var t=E()(r()().mark(function e(n){var l;return r()().wrap(function(d){for(;;)switch(d.prev=d.next){case 0:return d.next=2,(0,f.Zb)(n);case 2:l=d.sent,l.data&&z(l.data);case 4:case"end":return d.stop()}},e)}));return function(n){return t.apply(this,arguments)}}();(0,i.useEffect)(function(){Y(),w(),b({code:j})},[]);var c=function(e){return e?parseFloat((e/1e5).toFixed(2)):0},k=function(e){var n=e+"";return n.slice(4,6)+"/"+n.slice(6,8)},q=O.map(function(t){return{date:k(t.date),income:c(t.income)}}),ee={data:q,xField:"date",yField:"income",tooltip:{name:"\u6536\u5165",field:"income"},axis:{date:{title:"\u65E5\u671F"},income:{title:"\u6536\u5165\uFF08\u5143\uFF09"}},title:"\u533A\u95F4\u7D2F\u8BA1"+c(O.reduce(function(t,e){return t+(e.income?e.income:0)},0))+"\u5143",height:400,label:{text:function(e){return e.income>0?e.income:""},textBaseline:"bottom",visible:!0,type:"point"},line:{style:{stroke:"#eb0fcc",strokeWidth:2},shapeField:"smooth"},shapeField:"smooth",style:{fill:"linear-gradient(-90deg, white 0%, #eb0fcc 100%)"}};return(0,a.jsxs)(x._z,{children:[(0,a.jsx)(L.Z,{onResize:function(e){Q(e.width<596)},children:(0,a.jsx)(m.Z,{children:(0,a.jsxs)(m.Z.Group,{direction:N?"column":"row",children:[(0,a.jsx)(m.Z,{statistic:{title:"\u7D2F\u8BA1\u6536\u5165\uFF08\u5143\uFF09",value:c(o==null?void 0:o.totalIncome)}}),(0,a.jsx)(m.Z,{statistic:{title:"\u6628\u65E5\u6536\u5165\uFF08\u5143\uFF09",value:c(o==null?void 0:o.yesterdayIncome)}})]})})},"resize-observer"),(0,a.jsxs)("div",{style:{marginTop:"20px",padding:"40px",backgroundColor:"white"},children:[(0,a.jsxs)(B.t,{defaultCollapsed:!0,split:!0,onFinish:b,children:[(0,a.jsx)(v.Z,{name:"code",label:"\u5E94\u7528",options:$.map(function(t){return{label:t.name,value:t.code}})}),(0,a.jsx)(v.Z,{label:"\u5E73\u53F0",name:"platform",valueEnum:{1:"\u7A7F\u5C71\u7532",2:"\u817E\u8BAF",5:"\u5FEB\u624B",7:"Sigmob"}}),(0,a.jsx)(v.Z,{name:"advType",label:"\u5E7F\u544A\u7C7B\u578B",valueEnum:{1:"\u6A2A\u5E45",2:"\u63D2\u9875",3:"\u6FC0\u52B1\u89C6\u9891"}}),(0,a.jsx)(W.Z,{fieldProps:{disabledDate:function(e){return e&&e>=K()().startOf("day")}},name:"date",label:"\u65F6\u95F4"})]}),(0,a.jsx)(I.Z,A()({},ee))]})]})};p.default=S}}]); |
After Width: | Height: | Size: 528 KiB |
After Width: | Height: | Size: 13 KiB |
@ -1 +0,0 @@ |
|||
html,body,#root{height:100%;margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.colorWeak{filter:invert(80%)}.ant-layout{min-height:100vh}.ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed{left:unset}canvas{display:block}body{text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ul,ol{list-style:none}@media (max-width: 768px){.ant-table{width:100%;overflow-x:auto}.ant-table-thead>tr>th,.ant-table-tbody>tr>th,.ant-table-thead>tr>td,.ant-table-tbody>tr>td{white-space:pre}.ant-table-thead>tr>th>span,.ant-table-tbody>tr>th>span,.ant-table-thead>tr>td>span,.ant-table-tbody>tr>td>span{display:block}}html,body{width:100%;height:100%}input::-ms-clear,input::-ms-reveal{display:none}*,*:before,*:after{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{margin:0}[tabindex="-1"]:focus{outline:none}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5em;font-weight:500}p{margin-top:0;margin-bottom:1em}abbr[title],abbr[data-original-title]{text-decoration:underline;text-decoration:underline dotted;border-bottom:0;cursor:help}address{margin-bottom:1em;font-style:normal;line-height:inherit}input[type=text],input[type=password],input[type=number],textarea{-webkit-appearance:none}ol,ul,dl{margin-top:0;margin-bottom:1em}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:500}dd{margin-bottom:.5em;margin-left:0}blockquote{margin:0 0 1em}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}pre,code,kbd,samp{font-size:1em;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace}pre{margin-top:0;margin-bottom:1em;overflow:auto}figure{margin:0 0 1em}img{vertical-align:middle;border-style:none}a,area,button,[role=button],input:not([type="range"]),label,select,summary,textarea{touch-action:manipulation}table{border-collapse:collapse}caption{padding-top:.75em;padding-bottom:.3em;text-align:left;caption-side:bottom}input,button,select,optgroup,textarea{margin:0;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}button,html [type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{padding:0;border-style:none}input[type=radio],input[type=checkbox]{box-sizing:border-box;padding:0}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;margin:0;padding:0;border:0}legend{display:block;width:100%;max-width:100%;margin-bottom:.5em;padding:0;color:inherit;font-size:1.5em;line-height:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item}template{display:none}[hidden]{display:none!important}mark{padding:.2em;background-color:#feffe6} |
@ -0,0 +1 @@ |
|||
html,body,#root{height:100%;margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.colorWeak{filter:invert(80%)}.ant-layout{min-height:100vh}.ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed{left:unset}.ant-menu-item-selected{background:#f0f8ff!important}canvas{display:block}body{text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ul,ol{list-style:none}@media (max-width: 768px){.ant-table{width:100%;overflow-x:auto}.ant-table-thead>tr>th,.ant-table-tbody>tr>th,.ant-table-thead>tr>td,.ant-table-tbody>tr>td{white-space:pre}.ant-table-thead>tr>th>span,.ant-table-tbody>tr>th>span,.ant-table-thead>tr>td>span,.ant-table-tbody>tr>td>span{display:block}}html,body{width:100%;height:100%}input::-ms-clear,input::-ms-reveal{display:none}*,*:before,*:after{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{margin:0}[tabindex="-1"]:focus{outline:none}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5em;font-weight:500}p{margin-top:0;margin-bottom:1em}abbr[title],abbr[data-original-title]{text-decoration:underline;text-decoration:underline dotted;border-bottom:0;cursor:help}address{margin-bottom:1em;font-style:normal;line-height:inherit}input[type=text],input[type=password],input[type=number],textarea{-webkit-appearance:none}ol,ul,dl{margin-top:0;margin-bottom:1em}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:500}dd{margin-bottom:.5em;margin-left:0}blockquote{margin:0 0 1em}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}pre,code,kbd,samp{font-size:1em;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace}pre{margin-top:0;margin-bottom:1em;overflow:auto}figure{margin:0 0 1em}img{vertical-align:middle;border-style:none}a,area,button,[role=button],input:not([type="range"]),label,select,summary,textarea{touch-action:manipulation}table{border-collapse:collapse}caption{padding-top:.75em;padding-bottom:.3em;text-align:left;caption-side:bottom}input,button,select,optgroup,textarea{margin:0;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}button,html [type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{padding:0;border-style:none}input[type=radio],input[type=checkbox]{box-sizing:border-box;padding:0}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;margin:0;padding:0;border:0}legend{display:block;width:100%;max-width:100%;margin-bottom:.5em;padding:0;color:inherit;font-size:1.5em;line-height:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item}template{display:none}[hidden]{display:none!important}mark{padding:.2em;background-color:#feffe6} |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 41 KiB |
@ -1,9 +1,11 @@ |
|||
/** |
|||
* @see https://umijs.org/docs/max/access#access
|
|||
* */ |
|||
export default function access(initialState: { currentUser?: API.CurrentUser } | undefined) { |
|||
export default function access(initialState: { currentUser?: API.MatrixAdmin } | undefined) { |
|||
const { currentUser } = initialState ?? {}; |
|||
return { |
|||
canAdmin: currentUser && currentUser.access === 'admin', |
|||
let data = { |
|||
canAdmin: currentUser && currentUser.role && currentUser.role <= 3, |
|||
canDeviceOwner: currentUser && currentUser.role && currentUser.role > 3, |
|||
}; |
|||
return data; |
|||
} |
|||
|
After Width: | Height: | Size: 528 KiB |
After Width: | Height: | Size: 13 KiB |
@ -0,0 +1,68 @@ |
|||
import { Modal, Switch, Table } from 'antd'; |
|||
import { ColumnsType } from 'antd/es/table'; |
|||
import React from 'react'; |
|||
import { changeAdminHide } from '../../services/matrix/admin'; |
|||
|
|||
export type AdminHideFormProps = { |
|||
onSubmit: () => void; |
|||
data: API.MatrixAdminBo[]; |
|||
}; |
|||
|
|||
const AdminHideForm: React.FC<AdminHideFormProps> = (props) => { |
|||
const handleOk = () => { |
|||
props.onSubmit(); |
|||
}; |
|||
|
|||
const columns: ColumnsType<API.MatrixAdminBo> = [ |
|||
{ |
|||
title: '账号', |
|||
dataIndex: 'name', |
|||
ellipsis: true, |
|||
}, |
|||
{ |
|||
title: '角色', |
|||
dataIndex: 'role', |
|||
render: (r: number) => { |
|||
let roleText = ''; |
|||
switch (r) { |
|||
case 1: |
|||
roleText = '超级管理员'; |
|||
break; |
|||
case 2: |
|||
roleText = '管理员'; |
|||
break; |
|||
case 3: |
|||
roleText = '普通账号'; |
|||
break; |
|||
case 4: |
|||
roleText = '口子'; |
|||
break; |
|||
default: |
|||
roleText = ''; |
|||
} |
|||
return roleText; |
|||
}, |
|||
}, |
|||
{ |
|||
title: '隐藏', |
|||
render: (record: API.MatrixAdminBo) => ( |
|||
<Switch |
|||
defaultValue={record.hide !== 0} |
|||
onClick={(val) => { |
|||
if (record.id) { |
|||
changeAdminHide({ adminId: record.id, hide: val ? 1 : 0 }); |
|||
} |
|||
}} |
|||
/> |
|||
), |
|||
}, |
|||
]; |
|||
|
|||
return ( |
|||
<Modal title="批量编辑" visible footer={[]} onCancel={handleOk}> |
|||
<Table columns={columns} dataSource={props.data} /> |
|||
</Modal> |
|||
); |
|||
}; |
|||
|
|||
export default AdminHideForm; |
@ -0,0 +1,375 @@ |
|||
import { useModel } from '@umijs/max'; |
|||
import { |
|||
Button, |
|||
Col, |
|||
Form, |
|||
Input, |
|||
InputNumber, |
|||
Modal, |
|||
Row, |
|||
Select, |
|||
Space, |
|||
Table, |
|||
Tabs, |
|||
Tag, |
|||
} from 'antd'; |
|||
import { ColumnsType } from 'antd/es/table'; |
|||
import { useEffect, useState } from 'react'; |
|||
|
|||
import { SearchOutlined } from '@ant-design/icons'; |
|||
import TabPane from 'antd/es/tabs/TabPane'; |
|||
import { adminList, appList, saveAdmin } from '../services/matrix/admin'; |
|||
import AdminHideForm from './Admin/AdminHideForm'; |
|||
|
|||
const AdminManagement = () => { |
|||
const [data, setData] = useState<API.MatrixAdminBo[]>([]); |
|||
const [visible, setVisible] = useState(false); |
|||
const [hideFormVisible, setHideFormVisible] = useState(false); |
|||
const [editing, setEditing] = useState(false); |
|||
const [role, setRole] = useState<number>(0); |
|||
const [form] = Form.useForm(); |
|||
const [appArr, setAppArr] = useState<API.MatrixApp[]>([]); |
|||
const { initialState } = useModel('@@initialState'); |
|||
const currentUser = initialState?.currentUser; |
|||
|
|||
const formatIncome = (v: number | undefined) => { |
|||
if (!v) { |
|||
return 0; |
|||
} |
|||
return parseFloat((v / 1000_00).toFixed(2)); |
|||
}; |
|||
|
|||
const getAppNameById = (appId: number) => { |
|||
const app = appArr.find((app) => app.id === appId); |
|||
return app ? app.name : ''; |
|||
}; |
|||
|
|||
const handleEdit = (record: API.MatrixAdminBo) => { |
|||
form.setFieldsValue(record); |
|||
setVisible(true); |
|||
setEditing(true); |
|||
setRole(record.role ? record.role : 0); |
|||
}; |
|||
|
|||
const columns: ColumnsType<API.MatrixAdminBo> = [ |
|||
{ |
|||
title: '账号', |
|||
dataIndex: 'name', |
|||
ellipsis: true, |
|||
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => ( |
|||
<div style={{ padding: 8 }}> |
|||
<Input |
|||
placeholder="搜索账号" |
|||
value={selectedKeys[0]} |
|||
onChange={(e) => setSelectedKeys(e.target.value ? [e.target.value] : [])} |
|||
onPressEnter={() => confirm()} |
|||
style={{ marginBottom: 8, display: 'block' }} |
|||
/> |
|||
<Space> |
|||
<Button onClick={() => confirm()} type="primary"> |
|||
搜索 |
|||
</Button> |
|||
<Button onClick={clearFilters} type="link"> |
|||
清除 |
|||
</Button> |
|||
</Space> |
|||
</div> |
|||
), |
|||
onFilter: (value, record) => (record?.name && record.name.includes(String(value))) || false, |
|||
filterIcon: (filtered) => ( |
|||
<SearchOutlined |
|||
style={{ color: filtered ? '#1890ff' : undefined }} |
|||
onPointerOverCapture={undefined} |
|||
onPointerMoveCapture={undefined} |
|||
/> |
|||
), |
|||
}, |
|||
|
|||
{ |
|||
title: '游戏', |
|||
dataIndex: 'appIds', |
|||
render: (text: []) => ( |
|||
<div> |
|||
{text?.map((appId) => ( |
|||
<Tag color="blue" key={appId}> |
|||
{getAppNameById(appId)} |
|||
</Tag> |
|||
))} |
|||
</div> |
|||
), |
|||
}, |
|||
{ |
|||
title: '角色', |
|||
dataIndex: 'role', |
|||
render: (r: number) => { |
|||
let roleText = ''; |
|||
switch (r) { |
|||
case 1: |
|||
roleText = '超级管理员'; |
|||
break; |
|||
case 2: |
|||
roleText = '管理员'; |
|||
break; |
|||
case 3: |
|||
roleText = '普通账号'; |
|||
break; |
|||
case 4: |
|||
roleText = '口子'; |
|||
break; |
|||
default: |
|||
roleText = ''; |
|||
} |
|||
return roleText; |
|||
}, |
|||
}, |
|||
{ |
|||
title: '操作', |
|||
render: (record: API.MatrixAdminBo) => [ |
|||
<a key="edit" onClick={() => handleEdit(record)}> |
|||
编辑 |
|||
</a>, |
|||
], |
|||
}, |
|||
]; |
|||
|
|||
const deviceColumns: ColumnsType<API.MatrixAdminBo> = [ |
|||
{ |
|||
title: '账号', |
|||
dataIndex: 'name', |
|||
ellipsis: true, |
|||
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => ( |
|||
<div style={{ padding: 8 }}> |
|||
<Input |
|||
placeholder="搜索账号" |
|||
value={selectedKeys[0]} |
|||
onChange={(e) => setSelectedKeys(e.target.value ? [e.target.value] : [])} |
|||
onPressEnter={() => confirm()} |
|||
style={{ marginBottom: 8, display: 'block' }} |
|||
/> |
|||
<Space> |
|||
<Button onClick={() => confirm()} type="primary"> |
|||
搜索 |
|||
</Button> |
|||
<Button onClick={clearFilters} type="link"> |
|||
清除 |
|||
</Button> |
|||
</Space> |
|||
</div> |
|||
), |
|||
onFilter: (value, record) => (record?.name && record.name.includes(String(value))) || false, |
|||
filterIcon: (filtered) => ( |
|||
<SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} /> |
|||
), |
|||
}, |
|||
|
|||
{ |
|||
title: '游戏', |
|||
dataIndex: 'appIds', |
|||
render: (text: []) => ( |
|||
<div> |
|||
{text?.map((appId) => ( |
|||
<Tag color="blue" key={appId}> |
|||
{getAppNameById(appId)} |
|||
</Tag> |
|||
))} |
|||
</div> |
|||
), |
|||
}, |
|||
{ |
|||
title: '角色', |
|||
dataIndex: 'role', |
|||
render: (r: number) => { |
|||
let roleText = ''; |
|||
switch (r) { |
|||
case 1: |
|||
roleText = '超级管理员'; |
|||
break; |
|||
case 2: |
|||
roleText = '管理员'; |
|||
break; |
|||
case 3: |
|||
roleText = '普通账号'; |
|||
break; |
|||
case 4: |
|||
roleText = '口子'; |
|||
break; |
|||
default: |
|||
roleText = ''; |
|||
} |
|||
return roleText; |
|||
}, |
|||
}, |
|||
{ |
|||
title: '本月收入(元)', |
|||
dataIndex: 'incomeThisMonth', |
|||
render: (_, r: API.MatrixAdminBo) => { |
|||
return formatIncome(r.overview?.thisMonthIncome); |
|||
}, |
|||
}, |
|||
{ |
|||
title: '上月收入(元)', |
|||
dataIndex: 'incomeLastMonth', |
|||
render: (_, r: API.MatrixAdminBo) => { |
|||
return formatIncome(r.overview?.lastMonthIncome); |
|||
}, |
|||
}, |
|||
{ |
|||
title: '累计收入(元)', |
|||
dataIndex: 'incomeTotal', |
|||
render: (_, r: API.MatrixAdminBo) => { |
|||
return formatIncome(r.overview?.totalIncome); |
|||
}, |
|||
}, |
|||
{ |
|||
title: '当前分成比例', |
|||
dataIndex: 'incomeRate', |
|||
}, |
|||
{ |
|||
title: '操作', |
|||
render: (record: API.MatrixAdminBo) => [ |
|||
<a key="edit" onClick={() => handleEdit(record)}> |
|||
编辑 |
|||
</a>, |
|||
], |
|||
}, |
|||
]; |
|||
|
|||
const handleOk = () => { |
|||
form.submit(); |
|||
}; |
|||
|
|||
const handleCancel = () => { |
|||
setVisible(false); |
|||
}; |
|||
|
|||
const fetchData = async () => { |
|||
const res = await adminList(); |
|||
if (res.data) { |
|||
setData(res.data); |
|||
} |
|||
}; |
|||
|
|||
const fetchApp = async () => { |
|||
const res = await appList(); |
|||
if (res.data) { |
|||
setAppArr(res.data); |
|||
} |
|||
}; |
|||
|
|||
useEffect(() => { |
|||
fetchData(); |
|||
fetchApp(); |
|||
}, []); |
|||
|
|||
const handleNew = () => { |
|||
form.resetFields(); // 重置表单字段
|
|||
setVisible(true); |
|||
setEditing(false); |
|||
setRole(0); |
|||
}; |
|||
|
|||
const handleSaveAdmin = async (values: API.MatrixAdminBo) => { |
|||
try { |
|||
await saveAdmin(values); |
|||
} catch (e) { |
|||
return; |
|||
} |
|||
setVisible(false); |
|||
fetchData(); |
|||
}; |
|||
|
|||
return ( |
|||
<div> |
|||
<Row align="stretch" gutter={20}> |
|||
<Col> |
|||
<Button onClick={handleNew}>新建账号</Button> |
|||
</Col> |
|||
<Col> |
|||
<Button |
|||
onClick={() => { |
|||
setHideFormVisible(true); |
|||
}} |
|||
> |
|||
批量编辑 |
|||
</Button> |
|||
</Col> |
|||
</Row> |
|||
<Tabs centered style={{ backgroundColor: 'white', padding: '20px' }}> |
|||
{currentUser?.role && currentUser.role < 3 && ( |
|||
<TabPane tab="普通账号" key="1"> |
|||
<Table |
|||
columns={columns} |
|||
dataSource={data.filter((x) => x.role === 3 && x.hide === 0)} |
|||
/> |
|||
</TabPane> |
|||
)} |
|||
<TabPane tab="口子" key="2"> |
|||
<Table |
|||
columns={deviceColumns} |
|||
dataSource={data.filter((x) => x.role === 4 && x.hide === 0)} |
|||
/> |
|||
</TabPane> |
|||
</Tabs> |
|||
|
|||
<Modal title="编辑" visible={visible} onOk={handleOk} onCancel={handleCancel}> |
|||
<Form form={form} onFinish={handleSaveAdmin}> |
|||
<Form.Item name="id" style={{ display: 'none' }}> |
|||
<Input type="hidden" /> |
|||
</Form.Item> |
|||
<Form.Item label="账号" name="name"> |
|||
<Input disabled={editing} maxLength={32} /> |
|||
</Form.Item> |
|||
<Form.Item label="密码" name="password"> |
|||
<Input.Password disabled={editing} /> |
|||
</Form.Item> |
|||
<Form.Item label="游戏" name="appIds"> |
|||
<Select mode="multiple"> |
|||
{appArr.map((app) => ( |
|||
<Select.Option key={app.id} value={app.id}> |
|||
{app.name} |
|||
</Select.Option> |
|||
))} |
|||
</Select> |
|||
</Form.Item> |
|||
<Form.Item label="角色" name="role"> |
|||
<Select disabled={editing} onSelect={setRole}> |
|||
<Select.Option disabled={currentUser?.role && currentUser.role > 1} value={2}> |
|||
管理员 |
|||
</Select.Option> |
|||
<Select.Option disabled={currentUser?.role && currentUser.role > 2} value={3}> |
|||
普通账号 |
|||
</Select.Option> |
|||
<Select.Option value={4}>口子</Select.Option> |
|||
</Select> |
|||
</Form.Item> |
|||
{role > 3 && ( |
|||
<Row> |
|||
<Col span={12}> |
|||
<Form.Item label="收入比例" name="incomeRate"> |
|||
<InputNumber placeholder="1-100整数" style={{ width: '80%' }} /> |
|||
</Form.Item> |
|||
</Col> |
|||
<Col span={12}> |
|||
<Form.Item label="设备数" name="deviceCnt"> |
|||
<InputNumber style={{ width: '80%' }} /> |
|||
</Form.Item> |
|||
</Col> |
|||
</Row> |
|||
)} |
|||
</Form> |
|||
</Modal> |
|||
|
|||
{hideFormVisible && ( |
|||
<AdminHideForm |
|||
data={data} |
|||
onSubmit={() => { |
|||
setHideFormVisible(false); |
|||
fetchData(); |
|||
}} |
|||
/> |
|||
)} |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default AdminManagement; |
@ -1,148 +1,206 @@ |
|||
import { advList } from '@/services/matrix/admin'; |
|||
import { PageContainer, ProColumns, ProTable } from '@ant-design/pro-components'; |
|||
import { useIntl } from '@umijs/max'; |
|||
import React, { useRef } from 'react'; |
|||
import { ColumnsState, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components'; |
|||
import { ProFieldRequestData, RequestOptionsType } from '@ant-design/pro-utils'; |
|||
import { Tag } from 'antd'; |
|||
import React, { useRef, useState } from 'react'; |
|||
import { adminList } from '../services/matrix/admin'; |
|||
|
|||
import { advList, appList } from '@/services/matrix/admin'; |
|||
import type { ActionType } from '@ant-design/pro-components'; |
|||
|
|||
const AdvRecordList: React.FC = () => { |
|||
|
|||
const actionRef = useRef<ActionType>(); |
|||
|
|||
/** |
|||
* @en-US International configuration |
|||
* @zh-CN 国际化配置 |
|||
* */ |
|||
const intl = useIntl(); |
|||
|
|||
const columns: ProColumns<API.MatrixAdvRecordBo>[] = [ |
|||
{ |
|||
title: "设备Id", |
|||
dataIndex: 'deviceId', |
|||
valueType: 'textarea', |
|||
ellipsis: true, |
|||
copyable: true |
|||
}, |
|||
{ |
|||
title: "应用名称", |
|||
hideInSearch: true, |
|||
dataIndex: 'appName', |
|||
valueType: 'textarea', |
|||
}, |
|||
{ |
|||
title: "平台", |
|||
dataIndex: 'platform', |
|||
valueEnum: { |
|||
1: { |
|||
text:"穿山甲" , |
|||
}, |
|||
2: { |
|||
text: "腾讯", |
|||
}, |
|||
3: { |
|||
text: "百度联盟", |
|||
}, |
|||
4: { |
|||
text:"Mintegral", |
|||
}, |
|||
5: { |
|||
text:"快手", |
|||
}, |
|||
6: { |
|||
text:"游可赢", |
|||
}, |
|||
7: { |
|||
text:"Sigmob", |
|||
}, |
|||
8: { |
|||
text:"Admob", |
|||
}, |
|||
}, |
|||
}, |
|||
{ |
|||
title: "广告类型", |
|||
dataIndex: 'advType', |
|||
valueEnum: { |
|||
1: { |
|||
text:"横幅" , |
|||
status: 'Default', |
|||
}, |
|||
2: { |
|||
text: "插页", |
|||
status: 'Processing', |
|||
}, |
|||
3: { |
|||
text: "激励视频", |
|||
status: 'Success', |
|||
}, |
|||
}, |
|||
}, |
|||
{ |
|||
title: "ecpm(元)", |
|||
dataIndex: 'ecpm', |
|||
hideInSearch: true, |
|||
renderText: (x) => { |
|||
return x / 100; |
|||
} |
|||
}, |
|||
{ |
|||
title: "设备品牌", |
|||
dataIndex: 'deviceBrand', |
|||
hideInSearch: true, |
|||
valueType: 'textarea', |
|||
}, |
|||
{ |
|||
title: "设备名", |
|||
dataIndex: 'deviceName', |
|||
hideInSearch: true, |
|||
valueType: 'textarea', |
|||
}, |
|||
{ |
|||
title: "ip", |
|||
dataIndex: 'ip', |
|||
hideInSearch: true, |
|||
valueType: 'textarea', |
|||
}, |
|||
{ |
|||
title: "时间", |
|||
hideInSearch: true, |
|||
dataIndex: 'createdAt', |
|||
valueType: 'dateTime' |
|||
}, |
|||
{ |
|||
title: "时间", |
|||
hideInTable: true, |
|||
dataIndex: 'createdAt', |
|||
valueType: 'dateRange' |
|||
}, |
|||
]; |
|||
|
|||
|
|||
return ( |
|||
<PageContainer> |
|||
<ProTable<API.MatrixAdvRecordBo, API.AdvRecordQuery> |
|||
headerTitle={intl.formatMessage({ |
|||
id: 'pages.searchTable.title', |
|||
defaultMessage: 'Enquiry form', |
|||
})} |
|||
actionRef={actionRef} |
|||
rowKey="key" |
|||
search={{ |
|||
labelWidth: 120, |
|||
}} |
|||
request={async (params: any) => { |
|||
const res = await advList(params); |
|||
return { |
|||
data: res.data?.data, |
|||
total: res.data?.total, |
|||
success: true |
|||
} |
|||
}} |
|||
columns={columns} |
|||
/> |
|||
</PageContainer> |
|||
); |
|||
const actionRef = useRef<ActionType>(); |
|||
const [income, setIncome] = useState<number | undefined>(undefined); |
|||
|
|||
const appNameMap: ProFieldRequestData = async () => { |
|||
let res = await appList(); |
|||
let data: RequestOptionsType[] = []; |
|||
|
|||
res.data?.forEach((x: API.MatrixApp) => { |
|||
data.push({ label: x.name, value: x.code }); |
|||
}); |
|||
return data; |
|||
}; |
|||
|
|||
const adminNameMap: ProFieldRequestData = async () => { |
|||
let res = await adminList(); |
|||
let data: RequestOptionsType[] = []; |
|||
|
|||
res.data?.forEach((x: API.MatrixAdminBo) => { |
|||
if (x.role !== 4) { |
|||
return; |
|||
} |
|||
data.push({ label: x.name, value: x.id }); |
|||
}); |
|||
return data; |
|||
}; |
|||
|
|||
const isMobile = () => { |
|||
return window.innerWidth <= 768; |
|||
}; |
|||
|
|||
export default AdvRecordList; |
|||
|
|||
const [columnsState, setColumnsState] = useState<Record<string, ColumnsState>>({ |
|||
deviceId: { show: !isMobile() }, |
|||
appName: { show: !isMobile() }, |
|||
platform: { show: !isMobile() }, |
|||
device: { show: !isMobile() }, |
|||
ip: { show: !isMobile() }, |
|||
advType: { show: !isMobile() }, |
|||
}); |
|||
|
|||
const columns: ProColumns<API.MatrixAdvRecordBo>[] = [ |
|||
{ |
|||
title: '应用名称', |
|||
hideInTable: true, |
|||
dataIndex: 'code', |
|||
valueType: 'select', |
|||
request: appNameMap, |
|||
}, |
|||
{ |
|||
title: '口子', |
|||
hideInTable: true, |
|||
dataIndex: 'adminId', |
|||
valueType: 'select', |
|||
request: adminNameMap, |
|||
}, |
|||
{ |
|||
title: '设备id', |
|||
dataIndex: 'deviceId', |
|||
valueType: 'textarea', |
|||
ellipsis: true, |
|||
copyable: true, |
|||
}, |
|||
{ |
|||
title: '应用名称', |
|||
hideInSearch: true, |
|||
dataIndex: 'appName', |
|||
valueType: 'textarea', |
|||
}, |
|||
{ |
|||
title: '平台', |
|||
dataIndex: 'platform', |
|||
valueEnum: { |
|||
1: { |
|||
text: '穿山甲', |
|||
}, |
|||
2: { |
|||
text: '腾讯', |
|||
}, |
|||
5: { |
|||
text: '快手', |
|||
}, |
|||
7: { |
|||
text: 'Sigmob', |
|||
}, |
|||
}, |
|||
}, |
|||
{ |
|||
title: '广告类型', |
|||
dataIndex: 'advType', |
|||
valueEnum: { |
|||
1: { |
|||
text: '横幅', |
|||
// status: 'Default',
|
|||
}, |
|||
2: { |
|||
text: '插页', |
|||
// status: 'Processing',
|
|||
}, |
|||
3: { |
|||
text: '激励视频', |
|||
// status: 'Success',
|
|||
}, |
|||
}, |
|||
}, |
|||
{ |
|||
title: 'ecpm(元)', |
|||
dataIndex: 'ecpm', |
|||
hideInSearch: true, |
|||
renderText: (x) => { |
|||
return x / 100; |
|||
}, |
|||
}, |
|||
{ |
|||
title: '设备', |
|||
dataIndex: 'device', |
|||
hideInSearch: true, |
|||
valueType: 'textarea', |
|||
renderText: (_, r) => { |
|||
return ( |
|||
<p> |
|||
{r.deviceBrand} <br /> {r.deviceName} |
|||
</p> |
|||
); |
|||
}, |
|||
}, |
|||
{ |
|||
title: 'ip', |
|||
dataIndex: 'ip', |
|||
hideInSearch: true, |
|||
valueType: 'textarea', |
|||
}, |
|||
{ |
|||
title: '口子', |
|||
dataIndex: 'adminName', |
|||
hideInSearch: true, |
|||
valueType: 'textarea', |
|||
}, |
|||
{ |
|||
title: '状态', |
|||
dataIndex: 'status', |
|||
hideInSearch: true, |
|||
renderText: () => { |
|||
return <Tag color="#eb0fcc">成功</Tag>; |
|||
}, |
|||
}, |
|||
{ |
|||
title: '时间', |
|||
hideInSearch: true, |
|||
dataIndex: 'createdAt', |
|||
valueType: 'dateTime', |
|||
}, |
|||
{ |
|||
title: '时间', |
|||
hideInTable: true, |
|||
dataIndex: 'createdAt', |
|||
valueType: 'dateRange', |
|||
}, |
|||
]; |
|||
|
|||
const formatIncome = (v: number | undefined) => { |
|||
if (!v) { |
|||
return 0; |
|||
} |
|||
return parseFloat((v / 1000_00).toFixed(2)); |
|||
}; |
|||
|
|||
return ( |
|||
<PageContainer> |
|||
<ProTable<API.MatrixAdvRecordBo, API.AdvRecordQuery> |
|||
headerTitle={income ? '表格累计:' + formatIncome(income) + '元' : ''} |
|||
actionRef={actionRef} |
|||
rowKey="key" |
|||
search={{ |
|||
labelWidth: 120, |
|||
}} |
|||
request={async (params: API.AdvRecordQuery) => { |
|||
const res = await advList(params); |
|||
if (params.current === 1) { |
|||
setIncome(res.data?.sum); |
|||
} |
|||
return { |
|||
data: res.data?.data, |
|||
total: res.data?.total, |
|||
success: true, |
|||
}; |
|||
}} |
|||
columns={columns} |
|||
columnsState={{ |
|||
value: columnsState, |
|||
onChange: (newState) => { |
|||
setColumnsState(newState); |
|||
}, |
|||
}} |
|||
/> |
|||
</PageContainer> |
|||
); |
|||
}; |
|||
|
|||
export default AdvRecordList; |
|||
|
@ -0,0 +1,162 @@ |
|||
import { PageContainer, ProColumns, ProTable } from '@ant-design/pro-components'; |
|||
import { useModel } from '@umijs/max'; |
|||
import { Button, Form, Input, Modal, Popover, QRCode, Row, Space } from 'antd'; |
|||
import { useEffect, useState } from 'react'; |
|||
|
|||
import { appList, saveApp } from '@/services/matrix/admin'; |
|||
import { SearchOutlined } from '@ant-design/icons'; |
|||
|
|||
const AppManagement = () => { |
|||
const [visible, setVisible] = useState(false); |
|||
const [form] = Form.useForm(); |
|||
const [appArr, setAppArr] = useState<API.MatrixApp[]>([]); |
|||
const { initialState } = useModel('@@initialState'); |
|||
const currentUser = initialState?.currentUser; |
|||
const canUpdate = currentUser && currentUser.role && currentUser.role < 2; |
|||
|
|||
const handleEdit = (record: API.MatrixApp) => { |
|||
form.setFieldsValue(record); |
|||
setVisible(true); |
|||
}; |
|||
|
|||
const columns: ProColumns<API.MatrixApp>[] = [ |
|||
{ |
|||
title: '游戏', |
|||
dataIndex: 'name', |
|||
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => ( |
|||
<div style={{ padding: 8 }}> |
|||
<Input |
|||
placeholder="搜索账号" |
|||
value={selectedKeys[0]} |
|||
onChange={(e) => setSelectedKeys(e.target.value ? [e.target.value] : [])} |
|||
onPressEnter={() => confirm()} |
|||
style={{ marginBottom: 8, display: 'block' }} |
|||
/> |
|||
<Space> |
|||
<Button onClick={() => confirm()} type="primary"> |
|||
搜索 |
|||
</Button> |
|||
<Button onClick={() => clearFilters()} type="link"> |
|||
清除 |
|||
</Button> |
|||
</Space> |
|||
</div> |
|||
), |
|||
onFilter: (value, record) => record?.name?.includes(value), |
|||
filterIcon: (filtered) => ( |
|||
<SearchOutlined |
|||
style={{ color: filtered ? '#1890ff' : undefined }} |
|||
onPointerEnterCapture={undefined} |
|||
onPointerLeaveCapture={undefined} |
|||
/> |
|||
), |
|||
}, |
|||
// {
|
|||
// title: 'code',
|
|||
// dataIndex: 'code',
|
|||
// },
|
|||
{ |
|||
title: '下载地址', |
|||
dataIndex: 'url', |
|||
renderText: (url: string) => |
|||
url && ( |
|||
<Row align="middle"> |
|||
<QRCode size={80} value={url || ''} bordered={false} /> |
|||
<Popover |
|||
overlayInnerStyle={{ padding: 0 }} |
|||
content={<QRCode size={300} value={url || ''} bordered={false} />} |
|||
> |
|||
<a>查看大图</a> |
|||
</Popover> |
|||
</Row> |
|||
), |
|||
}, |
|||
]; |
|||
|
|||
const columnsWithOperation: ProColumns<API.MatrixApp>[] = [ |
|||
...columns, |
|||
{ |
|||
title: 'secret', |
|||
dataIndex: 'secret', |
|||
ellipsis: true, |
|||
copyable: true, |
|||
}, |
|||
{ |
|||
title: '操作', |
|||
width: 80, |
|||
renderText: (record: API.MatrixApp) => ( |
|||
<a key="edit" onClick={() => handleEdit(record)}> |
|||
编辑 |
|||
</a> |
|||
), |
|||
}, |
|||
]; |
|||
|
|||
const handleOk = () => { |
|||
form.submit(); |
|||
}; |
|||
|
|||
const handleCancel = () => { |
|||
setVisible(false); |
|||
}; |
|||
|
|||
const fetchApp = async () => { |
|||
const res = await appList(); |
|||
if (res.data) { |
|||
setAppArr(res.data); |
|||
} |
|||
}; |
|||
|
|||
useEffect(() => { |
|||
fetchApp(); |
|||
}, []); |
|||
|
|||
const handleNew = () => { |
|||
form.resetFields(); // 重置表单字段
|
|||
setVisible(true); |
|||
}; |
|||
|
|||
const handleSaveApp = async (values: API.MatrixApp) => { |
|||
try { |
|||
await saveApp(values); |
|||
} catch (e) { |
|||
return; |
|||
} |
|||
setVisible(false); |
|||
fetchApp(); |
|||
}; |
|||
|
|||
return ( |
|||
<PageContainer> |
|||
{currentUser && currentUser.role && currentUser.role < 2 && ( |
|||
<Button style={{ marginBottom: '20px' }} onClick={handleNew}> |
|||
新建应用 |
|||
</Button> |
|||
)} |
|||
<ProTable |
|||
search={false} |
|||
columns={canUpdate ? columnsWithOperation : columns} |
|||
dataSource={appArr} |
|||
/> |
|||
|
|||
<Modal title="编辑" visible={visible} onOk={handleOk} onCancel={handleCancel}> |
|||
<Form form={form} onFinish={handleSaveApp}> |
|||
<Form.Item name="id" style={{ display: 'none' }}> |
|||
<Input type="hidden" /> |
|||
</Form.Item> |
|||
<Form.Item name="code" style={{ display: 'none' }}> |
|||
<Input type="hidden" /> |
|||
</Form.Item> |
|||
<Form.Item label="应用名" name="name"> |
|||
<Input /> |
|||
</Form.Item> |
|||
<Form.Item label="下载地址" name="url"> |
|||
<Input /> |
|||
</Form.Item> |
|||
</Form> |
|||
</Modal> |
|||
</PageContainer> |
|||
); |
|||
}; |
|||
|
|||
export default AppManagement; |
@ -0,0 +1,19 @@ |
|||
import { PageContainer } from '@ant-design/pro-components'; |
|||
import { useModel } from '@umijs/max'; |
|||
import { Card, QRCode } from 'antd'; |
|||
|
|||
const Bind: React.FC = () => { |
|||
const { initialState } = useModel('@@initialState'); |
|||
const currentUser = initialState?.currentUser; |
|||
return ( |
|||
<PageContainer> |
|||
<Card> |
|||
<QRCode style={{ margin: 'auto' }} value={currentUser?.name || ''} /> |
|||
|
|||
<p style={{ textAlign: 'center', marginTop: '20px' }}>请使用游戏app扫码绑定设备</p> |
|||
</Card> |
|||
</PageContainer> |
|||
); |
|||
}; |
|||
|
|||
export default Bind; |
@ -0,0 +1,173 @@ |
|||
import { appList } from '@/services/matrix/admin'; |
|||
import { incomeDaily1, incomeOverview1 } from '@/services/matrix/device'; |
|||
import { Area } from '@ant-design/charts'; |
|||
import { |
|||
PageContainer, |
|||
ProFormDateRangePicker, |
|||
ProFormSelect, |
|||
QueryFilter, |
|||
StatisticCard, |
|||
} from '@ant-design/pro-components'; |
|||
import moment from 'moment'; |
|||
import RcResizeObserver from 'rc-resize-observer'; |
|||
import { useEffect, useState } from 'react'; |
|||
import { useParams } from 'react-router-dom'; |
|||
|
|||
const Overview: React.FC = () => { |
|||
const [overview, setOverview] = useState<API.OverviewBo>(); |
|||
const [daily, setDaily] = useState<API.DateIncome[]>([]); |
|||
const [appArr, setAppArr] = useState<API.MatrixApp[]>([]); |
|||
const [responsive, setResponsive] = useState(false); |
|||
|
|||
const { code } = useParams(); |
|||
|
|||
const fetchData = async () => { |
|||
const res = await incomeOverview1({ appCode: code }); |
|||
if (res.data) { |
|||
setOverview(res.data); |
|||
} |
|||
}; |
|||
|
|||
const fetchApp = async () => { |
|||
const res = await appList(); |
|||
if (res.data) { |
|||
setAppArr(res.data); |
|||
} |
|||
}; |
|||
|
|||
const fetchDaily = async (params: API.IncomeQuery) => { |
|||
const res = await incomeDaily1(params); |
|||
if (res.data) { |
|||
setDaily(res.data); |
|||
} |
|||
}; |
|||
|
|||
useEffect(() => { |
|||
fetchData(); |
|||
fetchApp(); |
|||
fetchDaily({ code: code }); |
|||
}, []); |
|||
|
|||
const formatIncome = (v: number | undefined) => { |
|||
if (!v) { |
|||
return 0; |
|||
} |
|||
return parseFloat((v / 1000_00).toFixed(2)); |
|||
}; |
|||
|
|||
const formatDate = (x: number | undefined) => { |
|||
const str = x + ''; |
|||
return str.slice(4, 6) + '/' + str.slice(6, 8); |
|||
}; |
|||
|
|||
// 在渲染折线图组件时,对数据进行处理
|
|||
const processedChartData: { date: string | number; income: number }[] = daily.map((item) => ({ |
|||
date: formatDate(item.date), |
|||
income: formatIncome(item.income), |
|||
})); |
|||
|
|||
const config = { |
|||
data: processedChartData, |
|||
// theme: 'Light',
|
|||
xField: 'date', |
|||
yField: 'income', |
|||
// theme: 'classicDark',
|
|||
tooltip: { |
|||
name: '收入', |
|||
field: 'income', |
|||
}, |
|||
axis: { |
|||
date: { |
|||
title: '日期', |
|||
}, |
|||
income: { |
|||
title: '收入(元)', |
|||
}, |
|||
}, |
|||
title: |
|||
'区间累计' + |
|||
formatIncome(daily.reduce((acc, data) => acc + (data.income ? data.income : 0), 0)) + |
|||
'元', |
|||
height: 400, |
|||
label: { |
|||
text: (d: any) => (d.income > 0 ? d.income : ''), |
|||
textBaseline: 'bottom', |
|||
visible: true, |
|||
type: 'point', |
|||
}, |
|||
line: { |
|||
style: { |
|||
stroke: '#eb0fcc', |
|||
strokeWidth: 2, |
|||
}, |
|||
shapeField: 'smooth', |
|||
}, |
|||
shapeField: 'smooth', |
|||
style: { |
|||
fill: 'linear-gradient(-90deg, white 0%, #eb0fcc 100%)', |
|||
}, |
|||
// point: { visible: true },
|
|||
}; |
|||
|
|||
return ( |
|||
<PageContainer> |
|||
<RcResizeObserver |
|||
key="resize-observer" |
|||
onResize={(offset) => { |
|||
setResponsive(offset.width < 596); |
|||
}} |
|||
> |
|||
<StatisticCard> |
|||
<StatisticCard.Group direction={responsive ? 'column' : 'row'}> |
|||
<StatisticCard |
|||
statistic={{ |
|||
title: '累计收入(元)', |
|||
value: formatIncome(overview?.totalIncome), |
|||
}} |
|||
/> |
|||
|
|||
<StatisticCard |
|||
statistic={{ |
|||
title: '昨日收入(元)', |
|||
value: formatIncome(overview?.yesterdayIncome), |
|||
}} |
|||
/> |
|||
</StatisticCard.Group> |
|||
</StatisticCard> |
|||
</RcResizeObserver> |
|||
|
|||
<div style={{ marginTop: '20px', padding: '40px', backgroundColor: 'white' }}> |
|||
<QueryFilter defaultCollapsed split onFinish={fetchDaily}> |
|||
<ProFormSelect |
|||
name="code" |
|||
label="应用" |
|||
options={appArr.map((x) => ({ |
|||
label: x.name, |
|||
value: x.code, |
|||
}))} |
|||
/> |
|||
<ProFormSelect |
|||
label="平台" |
|||
name="platform" |
|||
valueEnum={{ |
|||
1: '穿山甲', |
|||
2: '腾讯', |
|||
5: '快手', |
|||
7: 'Sigmob', |
|||
}} |
|||
/> |
|||
<ProFormDateRangePicker |
|||
fieldProps={{ |
|||
disabledDate: (current: any) => current && current >= moment().startOf('day'), |
|||
}} |
|||
name="date" |
|||
label="时间" |
|||
/> |
|||
</QueryFilter> |
|||
<Area {...config} /> |
|||
</div> |
|||
</PageContainer> |
|||
); |
|||
}; |
|||
|
|||
export default Overview; |
@ -0,0 +1,296 @@ |
|||
import { advList, appList } from '@/services/matrix/admin'; |
|||
import { |
|||
ColumnsState, |
|||
PageContainer, |
|||
ProColumns, |
|||
ProFieldRequestData, |
|||
ProTable, |
|||
RequestOptionsType, |
|||
} from '@ant-design/pro-components'; |
|||
import { useIntl, useModel } from '@umijs/max'; |
|||
import { Tabs } from 'antd'; |
|||
import TabPane from 'antd/es/tabs/TabPane'; |
|||
import React, { useRef, useState } from 'react'; |
|||
import { online } from '../services/matrix/device'; |
|||
|
|||
// import { RequestOptionsType, ProFieldRequestData } from "@ant-design/pro-utils";
|
|||
|
|||
import { deviceList, offline } from '@/services/matrix/device'; |
|||
import type { ActionType } from '@ant-design/pro-components'; |
|||
const DeviceOwnerApp: React.FC = () => { |
|||
const actionRef = useRef<ActionType>(); |
|||
const deviceRef = useRef<ActionType>(); |
|||
const [usedDeviceCnt, setUsedDeviceCnt] = useState(0); |
|||
/** |
|||
* @en-US International configuration |
|||
* @zh-CN 国际化配置 |
|||
* */ |
|||
const intl = useIntl(); |
|||
|
|||
const { initialState } = useModel('@@initialState'); |
|||
const currentUser = initialState?.currentUser; |
|||
|
|||
const appNameMap: ProFieldRequestData = async () => { |
|||
let res = await appList(); |
|||
let data: RequestOptionsType[] = []; |
|||
|
|||
res.data?.forEach((x) => { |
|||
data.push({ label: x.name, value: x.code }); |
|||
}); |
|||
return data; |
|||
}; |
|||
|
|||
const isMobile = () => { |
|||
return window.innerWidth <= 768; |
|||
}; |
|||
|
|||
const fetchDevice = async (params: any) => { |
|||
if (!params.appName) { |
|||
return { |
|||
data: [], |
|||
total: 0, |
|||
success: true, |
|||
}; |
|||
} |
|||
const res = await deviceList({ appCode: params.appName ? params.appName : '' }); |
|||
if (res.data) { |
|||
setUsedDeviceCnt(res.data?.filter((device) => device.status === 0).length); |
|||
} |
|||
return { |
|||
data: res.data, |
|||
total: res.data?.length, |
|||
success: true, |
|||
}; |
|||
}; |
|||
|
|||
const handleSetOffline = async (deviceId: string | undefined) => { |
|||
if (!deviceId) { |
|||
return; |
|||
} |
|||
await offline({ deviceId: deviceId }); |
|||
deviceRef.current?.reload(); |
|||
}; |
|||
|
|||
const handleSetOnline = async (deviceId: string | undefined) => { |
|||
if (!deviceId) { |
|||
return; |
|||
} |
|||
try { |
|||
await online({ deviceId: deviceId }); |
|||
deviceRef.current?.reload(); |
|||
} catch {} |
|||
}; |
|||
|
|||
const [columnsState, setColumnsState] = useState<Record<string, ColumnsState>>({ |
|||
deviceId: { show: !isMobile() }, |
|||
appName: { show: !isMobile() }, |
|||
platform: { show: !isMobile() }, |
|||
deviceBrand: { show: !isMobile() }, |
|||
deviceName: { show: !isMobile() }, |
|||
ip: { show: !isMobile() }, |
|||
}); |
|||
|
|||
const columns: ProColumns<API.MatrixAdvRecordBo>[] = [ |
|||
{ |
|||
title: '应用名称', |
|||
dataIndex: 'appName', |
|||
valueType: 'textarea', |
|||
request: appNameMap, |
|||
}, |
|||
{ |
|||
title: '设备id', |
|||
dataIndex: 'deviceId', |
|||
valueType: 'textarea', |
|||
ellipsis: true, |
|||
copyable: true, |
|||
}, |
|||
{ |
|||
title: '平台', |
|||
dataIndex: 'platform', |
|||
valueEnum: { |
|||
1: { |
|||
text: '穿山甲', |
|||
}, |
|||
2: { |
|||
text: '腾讯', |
|||
}, |
|||
5: { |
|||
text: '快手', |
|||
}, |
|||
7: { |
|||
text: 'Sigmob', |
|||
}, |
|||
}, |
|||
}, |
|||
{ |
|||
title: 'ecpm(元)', |
|||
dataIndex: 'ecpm', |
|||
hideInSearch: true, |
|||
renderText: (x) => { |
|||
return x / 100; |
|||
}, |
|||
}, |
|||
{ |
|||
title: 'ecpm-抽成后(元)', |
|||
dataIndex: 'deductEcpm', |
|||
hideInSearch: true, |
|||
renderText: (x, record) => { |
|||
if (currentUser?.incomeRate && record.ecpm) { |
|||
return Math.floor((record.ecpm * currentUser.incomeRate) / 100) / 100; |
|||
} |
|||
return '-'; |
|||
}, |
|||
}, |
|||
{ |
|||
title: '设备品牌', |
|||
dataIndex: 'deviceBrand', |
|||
hideInSearch: true, |
|||
valueType: 'textarea', |
|||
}, |
|||
{ |
|||
title: '设备名', |
|||
dataIndex: 'deviceName', |
|||
hideInSearch: true, |
|||
valueType: 'textarea', |
|||
}, |
|||
{ |
|||
title: 'ip', |
|||
dataIndex: 'ip', |
|||
hideInSearch: true, |
|||
valueType: 'textarea', |
|||
}, |
|||
{ |
|||
title: '时间', |
|||
hideInSearch: true, |
|||
dataIndex: 'createdAt', |
|||
valueType: 'dateTime', |
|||
}, |
|||
{ |
|||
title: '时间', |
|||
hideInTable: true, |
|||
dataIndex: 'createdAt', |
|||
valueType: 'dateRange', |
|||
}, |
|||
// {
|
|||
// title: "应用名称",
|
|||
// hideInTable: true,
|
|||
// dataIndex: 'appId',
|
|||
// valueType: "select",
|
|||
// request: appNameMap
|
|||
// }
|
|||
]; |
|||
|
|||
const deviceColumns: ProColumns<API.MatrixAdminDevice>[] = [ |
|||
{ |
|||
title: '应用名称', |
|||
dataIndex: 'appName', |
|||
valueType: 'textarea', |
|||
request: appNameMap, |
|||
hideInTable: true, |
|||
}, |
|||
{ |
|||
title: '设备id', |
|||
dataIndex: 'deviceId', |
|||
valueType: 'text', |
|||
ellipsis: true, |
|||
width: 400, |
|||
copyable: true, |
|||
hideInSearch: true, |
|||
}, |
|||
{ |
|||
title: '状态', |
|||
dataIndex: 'status', |
|||
valueEnum: { |
|||
0: { |
|||
text: '正常', |
|||
}, |
|||
'-1': { |
|||
text: '已下线', |
|||
}, |
|||
}, |
|||
filters: true, |
|||
onFilter: true, |
|||
hideInSearch: true, |
|||
}, |
|||
{ |
|||
title: '绑定时间', |
|||
hideInSearch: true, |
|||
dataIndex: 'createdAt', |
|||
valueType: 'dateTime', |
|||
}, |
|||
{ |
|||
title: '操作', |
|||
dataIndex: 'operation', |
|||
hideInSearch: true, |
|||
render: (_, record) => { |
|||
if (record.status === 0) { |
|||
return <a onClick={() => handleSetOffline(record.deviceId)}>下线</a>; |
|||
} |
|||
if (record.status === -1) { |
|||
return <a onClick={() => handleSetOnline(record.deviceId)}>上线</a>; |
|||
} |
|||
return null; |
|||
}, |
|||
}, |
|||
]; |
|||
|
|||
const fetchData = async (params: any) => { |
|||
if (!params.appName) { |
|||
return { |
|||
data: [], |
|||
total: 0, |
|||
success: true, |
|||
}; |
|||
} |
|||
const res = await advList({ ...params, code: params.appName }); |
|||
return { |
|||
data: res.data?.data, |
|||
total: res.data?.total, |
|||
success: true, |
|||
}; |
|||
}; |
|||
|
|||
return ( |
|||
<PageContainer> |
|||
<Tabs defaultActiveKey="1" centered style={{ backgroundColor: 'white', padding: '20px' }}> |
|||
<TabPane tab="设备列表" key="1"> |
|||
<p style={{ textAlign: 'center' }}> |
|||
{'正常使用设备数:' + |
|||
usedDeviceCnt + |
|||
',剩余可绑定设备数:' + |
|||
Math.max(0, (currentUser?.deviceCnt ? currentUser.deviceCnt : 0) - usedDeviceCnt)} |
|||
</p> |
|||
<ProTable<API.MatrixAdminDevice> |
|||
actionRef={deviceRef} |
|||
rowKey="id" |
|||
request={fetchDevice} |
|||
columns={deviceColumns} |
|||
/> |
|||
</TabPane> |
|||
<TabPane tab="广告详情" key="2"> |
|||
<ProTable<API.MatrixAdvRecordBo, API.AdvRecordQuery> |
|||
headerTitle={intl.formatMessage({ |
|||
id: 'pages.searchTable.title', |
|||
defaultMessage: 'Enquiry form', |
|||
})} |
|||
actionRef={actionRef} |
|||
rowKey="id" |
|||
search={{ |
|||
labelWidth: 120, |
|||
}} |
|||
request={fetchData} |
|||
columns={columns} |
|||
columnsState={{ |
|||
value: columnsState, |
|||
onChange: (newState) => { |
|||
setColumnsState(newState); |
|||
}, |
|||
}} |
|||
/> |
|||
</TabPane> |
|||
</Tabs> |
|||
</PageContainer> |
|||
); |
|||
}; |
|||
|
|||
export default DeviceOwnerApp; |
@ -0,0 +1,181 @@ |
|||
import { appList, incomeDaily, incomeOverview } from '@/services/matrix/admin'; |
|||
import { Area } from '@ant-design/charts'; |
|||
import { |
|||
PageContainer, |
|||
ProFormDateRangePicker, |
|||
ProFormSelect, |
|||
QueryFilter, |
|||
StatisticCard, |
|||
} from '@ant-design/pro-components'; |
|||
import moment from 'moment'; |
|||
import RcResizeObserver from 'rc-resize-observer'; |
|||
import { useEffect, useState } from 'react'; |
|||
import { useParams } from 'react-router-dom'; |
|||
|
|||
const Overview: React.FC = () => { |
|||
const [overview, setOverview] = useState<API.OverviewBo>(); |
|||
const [daily, setDaily] = useState<API.DateIncome[]>([]); |
|||
const [appArr, setAppArr] = useState<API.MatrixApp[]>([]); |
|||
const [responsive, setResponsive] = useState(false); |
|||
|
|||
const { code } = useParams(); |
|||
|
|||
const fetchData = async () => { |
|||
const res = await incomeOverview({ appCode: code }); |
|||
if (res.data) { |
|||
setOverview(res.data); |
|||
} |
|||
}; |
|||
|
|||
const fetchApp = async () => { |
|||
const res = await appList(); |
|||
if (res.data) { |
|||
setAppArr(res.data); |
|||
} |
|||
}; |
|||
|
|||
const fetchDaily = async (params: API.IncomeQuery) => { |
|||
const res = await incomeDaily(params); |
|||
if (res.data) { |
|||
setDaily(res.data); |
|||
} |
|||
}; |
|||
|
|||
useEffect(() => { |
|||
fetchData(); |
|||
fetchApp(); |
|||
fetchDaily({ code: code }); |
|||
}, []); |
|||
|
|||
const formatIncome = (v: number | undefined) => { |
|||
if (!v) { |
|||
return 0; |
|||
} |
|||
return parseFloat((v / 1000_00).toFixed(2)); |
|||
}; |
|||
|
|||
const formatDate = (x: number | undefined) => { |
|||
const str = x + ''; |
|||
return str.slice(4, 6) + '/' + str.slice(6, 8); |
|||
}; |
|||
|
|||
// 在渲染折线图组件时,对数据进行处理
|
|||
const processedChartData: { date: string | number; income: number }[] = daily.map((item) => ({ |
|||
date: formatDate(item.date), |
|||
income: formatIncome(item.income), |
|||
})); |
|||
|
|||
const config = { |
|||
data: processedChartData, |
|||
// theme: 'Light',
|
|||
xField: 'date', |
|||
yField: 'income', |
|||
// theme: 'classicDark',
|
|||
tooltip: { |
|||
name: '收入', |
|||
field: 'income', |
|||
}, |
|||
axis: { |
|||
date: { |
|||
title: '日期', |
|||
}, |
|||
income: { |
|||
title: '收入(元)', |
|||
}, |
|||
}, |
|||
title: |
|||
'区间累计' + |
|||
formatIncome(daily.reduce((acc, data) => acc + (data.income ? data.income : 0), 0)) + |
|||
'元', |
|||
height: 400, |
|||
label: { |
|||
text: (d: any) => (d.income > 0 ? d.income : ''), |
|||
textBaseline: 'bottom', |
|||
visible: true, |
|||
type: 'point', |
|||
}, |
|||
line: { |
|||
style: { |
|||
stroke: '#eb0fcc', |
|||
strokeWidth: 2, |
|||
}, |
|||
shapeField: 'smooth', |
|||
}, |
|||
shapeField: 'smooth', |
|||
style: { |
|||
fill: 'linear-gradient(-90deg, white 0%, #eb0fcc 100%)', |
|||
}, |
|||
// point: { visible: true },
|
|||
}; |
|||
|
|||
return ( |
|||
<PageContainer> |
|||
<RcResizeObserver |
|||
key="resize-observer" |
|||
onResize={(offset) => { |
|||
setResponsive(offset.width < 596); |
|||
}} |
|||
> |
|||
<StatisticCard> |
|||
<StatisticCard.Group direction={responsive ? 'column' : 'row'}> |
|||
<StatisticCard |
|||
statistic={{ |
|||
title: '累计收入(元)', |
|||
value: formatIncome(overview?.totalIncome), |
|||
}} |
|||
/> |
|||
|
|||
<StatisticCard |
|||
statistic={{ |
|||
title: '昨日收入(元)', |
|||
value: formatIncome(overview?.yesterdayIncome), |
|||
}} |
|||
/> |
|||
</StatisticCard.Group> |
|||
</StatisticCard> |
|||
</RcResizeObserver> |
|||
|
|||
<div style={{ marginTop: '20px', padding: '40px', backgroundColor: 'white' }}> |
|||
<QueryFilter defaultCollapsed split onFinish={fetchDaily}> |
|||
<ProFormSelect |
|||
name="code" |
|||
label="应用" |
|||
options={appArr.map((x) => ({ |
|||
label: x.name, |
|||
value: x.code, |
|||
}))} |
|||
/> |
|||
<ProFormSelect |
|||
label="平台" |
|||
name="platform" |
|||
valueEnum={{ |
|||
1: '穿山甲', |
|||
2: '腾讯', |
|||
5: '快手', |
|||
7: 'Sigmob', |
|||
}} |
|||
/> |
|||
<ProFormSelect |
|||
name="advType" |
|||
label="广告类型" |
|||
valueEnum={{ |
|||
1: '横幅', |
|||
2: '插页', |
|||
3: '激励视频', |
|||
}} |
|||
/> |
|||
<ProFormDateRangePicker |
|||
fieldProps={{ |
|||
disabledDate: (current: any) => current && current >= moment().startOf('day'), |
|||
}} |
|||
name="date" |
|||
label="时间" |
|||
/> |
|||
</QueryFilter> |
|||
<Area {...config} /> |
|||
</div> |
|||
</PageContainer> |
|||
); |
|||
}; |
|||
|
|||
export default Overview; |
@ -1,18 +0,0 @@ |
|||
// @ts-ignore
|
|||
/* eslint-disable */ |
|||
import { request } from '@umijs/max'; |
|||
|
|||
/** 此处后端没有提供注释 GET /api/game/app/info */ |
|||
export async function getAppInfo( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.getAppInfoParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RAppInfo>('/api/game/app/info', { |
|||
method: 'GET', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
@ -0,0 +1,90 @@ |
|||
// @ts-ignore
|
|||
/* eslint-disable */ |
|||
import { request } from '@umijs/max'; |
|||
|
|||
/** 此处后端没有提供注释 GET /api/admin/device/calc */ |
|||
export async function calc( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.calcParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RVoid>('/api/admin/device/calc', { |
|||
method: 'GET', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 POST /api/admin/device/incomeDaily */ |
|||
export async function incomeDaily1(body: API.IncomeQuery, options?: { [key: string]: any }) { |
|||
return request<API.RListDateIncome>('/api/admin/device/incomeDaily', { |
|||
method: 'POST', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
data: body, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 GET /api/admin/device/incomeOverview */ |
|||
export async function incomeOverview1( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.incomeOverview1Params, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.ROverviewBo>('/api/admin/device/incomeOverview', { |
|||
method: 'GET', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 GET /api/admin/device/list */ |
|||
export async function deviceList( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.deviceListParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RListMatrixAdminDevice>('/api/admin/device/list', { |
|||
method: 'GET', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 POST /api/admin/device/offline */ |
|||
export async function offline( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.offlineParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RString>('/api/admin/device/offline', { |
|||
method: 'POST', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 POST /api/admin/device/online */ |
|||
export async function online( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.onlineParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RString>('/api/admin/device/online', { |
|||
method: 'POST', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
@ -1,114 +0,0 @@ |
|||
// @ts-ignore
|
|||
/* eslint-disable */ |
|||
import { request } from '@umijs/max'; |
|||
|
|||
/** 此处后端没有提供注释 GET /api/game/auth/current */ |
|||
export async function currentUser(options?: { [key: string]: any }) { |
|||
return request<API.RCurrentUser>('/api/game/auth/current', { |
|||
method: 'GET', |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 POST /api/game/auth/login */ |
|||
export async function wxLogin( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.wxLoginParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RString>('/api/game/auth/login', { |
|||
method: 'POST', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 POST /api/game/auth/loginV2 */ |
|||
export async function wxLoginV2( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.wxLoginV2Params, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RLoginBo>('/api/game/auth/loginV2', { |
|||
method: 'POST', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 POST /api/game/auth/loginV3 */ |
|||
export async function loginV3(body: API.LoginReq, options?: { [key: string]: any }) { |
|||
return request<API.RLoginBo>('/api/game/auth/loginV3', { |
|||
method: 'POST', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
data: body, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 GET /api/game/auth/myInviteRecord */ |
|||
export async function myInviteRecord(options?: { [key: string]: any }) { |
|||
return request<API.RInviteInfo>('/api/game/auth/myInviteRecord', { |
|||
method: 'GET', |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 GET /api/game/auth/rankList */ |
|||
export async function rankList( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.rankListParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RRankList>('/api/game/auth/rankList', { |
|||
method: 'GET', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 GET /api/game/auth/test */ |
|||
export async function setInviteCode(options?: { [key: string]: any }) { |
|||
return request<API.RVoid>('/api/game/auth/test', { |
|||
method: 'GET', |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 POST /api/game/auth/updateScore */ |
|||
export async function updateScore( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.updateScoreParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RVoid>('/api/game/auth/updateScore', { |
|||
method: 'POST', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 POST /api/game/auth/updateUserInfo */ |
|||
export async function updateUserInfo( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.updateUserInfoParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RVoid>('/api/game/auth/updateUserInfo', { |
|||
method: 'POST', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
@ -0,0 +1,170 @@ |
|||
// @ts-ignore
|
|||
/* eslint-disable */ |
|||
import { request } from '@umijs/max'; |
|||
|
|||
/** 此处后端没有提供注释 GET /api/admin/app/aliPayConfig */ |
|||
export async function getAliPayConfig( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.getAliPayConfigParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RAliPayConfigBo>('/api/admin/app/aliPayConfig', { |
|||
method: 'GET', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 POST /api/admin/app/aliPayConfig */ |
|||
export async function saveAliPayConfig( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.saveAliPayConfigParams, |
|||
body: API.AliPayConfigBo, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RVoid>('/api/admin/app/aliPayConfig', { |
|||
method: 'POST', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
params: { |
|||
...params, |
|||
}, |
|||
data: body, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 GET /api/admin/app/basicConfig */ |
|||
export async function getBasicConfig( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.getBasicConfigParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RAppBasicConfig>('/api/admin/app/basicConfig', { |
|||
method: 'GET', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 POST /api/admin/app/basicConfig */ |
|||
export async function saveBasicConfig(body: API.AppBasicConfig, options?: { [key: string]: any }) { |
|||
return request<API.RVoid>('/api/admin/app/basicConfig', { |
|||
method: 'POST', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
data: body, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 GET /api/admin/app/normalConfig */ |
|||
export async function getAppNormalConfig( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.getAppNormalConfigParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RAppNormalConfig>('/api/admin/app/normalConfig', { |
|||
method: 'GET', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 POST /api/admin/app/normalConfig */ |
|||
export async function saveNormalConfig( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.saveNormalConfigParams, |
|||
body: API.AppNormalConfig, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RVoid>('/api/admin/app/normalConfig', { |
|||
method: 'POST', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
params: { |
|||
...params, |
|||
}, |
|||
data: body, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 GET /api/admin/app/uMengConfig */ |
|||
export async function getUMengConfig( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.getUMengConfigParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RUmengConfigBo>('/api/admin/app/uMengConfig', { |
|||
method: 'GET', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 POST /api/admin/app/uMengConfig */ |
|||
export async function saveUMengConfig( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.saveUMengConfigParams, |
|||
body: API.UmengConfigBo, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RVoid>('/api/admin/app/uMengConfig', { |
|||
method: 'POST', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
params: { |
|||
...params, |
|||
}, |
|||
data: body, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 GET /api/admin/app/wxConfig */ |
|||
export async function getWxConfig( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.getWxConfigParams, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RWxConfig>('/api/admin/app/wxConfig', { |
|||
method: 'GET', |
|||
params: { |
|||
...params, |
|||
}, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 POST /api/admin/app/wxConfig */ |
|||
export async function saveWxConfig( |
|||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|||
params: API.saveWxConfigParams, |
|||
body: API.WxConfig, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RVoid>('/api/admin/app/wxConfig', { |
|||
method: 'POST', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
params: { |
|||
...params, |
|||
}, |
|||
data: body, |
|||
...(options || {}), |
|||
}); |
|||
} |
@ -1,18 +0,0 @@ |
|||
// @ts-ignore
|
|||
/* eslint-disable */ |
|||
import { request } from '@umijs/max'; |
|||
|
|||
/** 此处后端没有提供注释 POST /api/game/matrix/saveAdvRecord */ |
|||
export async function saveAdvRecord( |
|||
body: API.MatrixAdvRecordEditBo, |
|||
options?: { [key: string]: any }, |
|||
) { |
|||
return request<API.RVoid>('/api/game/matrix/saveAdvRecord', { |
|||
method: 'POST', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
data: body, |
|||
...(options || {}), |
|||
}); |
|||
} |
@ -0,0 +1,27 @@ |
|||
// @ts-ignore
|
|||
/* eslint-disable */ |
|||
import { request } from '@umijs/max'; |
|||
|
|||
/** 此处后端没有提供注释 POST /api/admin/money/audit */ |
|||
export async function audit(body: API.MoneyBo, options?: { [key: string]: any }) { |
|||
return request<API.RVoid>('/api/admin/money/audit', { |
|||
method: 'POST', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
data: body, |
|||
...(options || {}), |
|||
}); |
|||
} |
|||
|
|||
/** 此处后端没有提供注释 POST /api/admin/money/list */ |
|||
export async function moneyApplyList(body: API.MoneyQuery, options?: { [key: string]: any }) { |
|||
return request<API.RPageResultMoneyBo>('/api/admin/money/list', { |
|||
method: 'POST', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
data: body, |
|||
...(options || {}), |
|||
}); |
|||
} |
@ -0,0 +1,15 @@ |
|||
// @ts-ignore
|
|||
/* eslint-disable */ |
|||
import { request } from '@umijs/max'; |
|||
|
|||
/** 此处后端没有提供注释 POST /api/admin/user/list */ |
|||
export async function list(body: API.UserQuery, options?: { [key: string]: any }) { |
|||
return request<API.RPageResultUserBo>('/api/admin/user/list', { |
|||
method: 'POST', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
data: body, |
|||
...(options || {}), |
|||
}); |
|||
} |