feat: 管理后台文件列表支持下载文件,格式化过期时间

This commit is contained in:
Yanlongli
2024-03-29 17:55:18 +08:00
parent fd5673ac68
commit eda5f71fc2
24 changed files with 152 additions and 40 deletions
+24
View File
@@ -56,3 +56,27 @@ async def update_config(data: dict):
for k, v in data.items(): for k, v in data.items():
settings.__setattr__(k, v) settings.__setattr__(k, v)
return APIResponse() return APIResponse()
# 根据code获取文件
async def get_file_by_id(id):
# 查询文件
file_code = await FileCodes.filter(id=id).first()
# 检查文件是否存在
if not file_code:
return False, '文件不存在'
return True, file_code
@admin_api.get('/file/download', dependencies=[Depends(admin_required)])
async def file_download(id: int):
has, file_code = await get_file_by_id(id)
# 检查文件是否存在
if not has:
# 返回API响应
return APIResponse(code=404, detail='文件不存在')
# 如果文件是文本,返回文本内容,否则返回文件响应
if file_code.text:
return APIResponse(detail=file_code.text)
else:
return await file_storage.get_file_response(file_code)
@@ -1 +1 @@
import{d as a,Q as n,F as c,D as e,J as r,H as o,u as s,o as u}from"./index-fd7b9342.js";const i={style:{"text-align":"center"}},l={style:{color:"#333"},href:"https://github.com/vastsa/FileCodeBox"},f=a({__name:"AboutView",setup(_){const{t}=n();return(m,d)=>(u(),c("main",i,[e("span",null,[r(o(s(t)("admin.about.source1"))+" ",1),e("em",null,[e("a",l,o(s(t)("admin.about.source2")),1)])])]))}});export{f as default}; import{d as a,Q as n,F as c,D as e,J as r,H as o,u as s,o as u}from"./index-bdf811f7.js";const i={style:{"text-align":"center"}},l={style:{color:"#333"},href:"https://github.com/vastsa/FileCodeBox"},f=a({__name:"AboutView",setup(_){const{t}=n();return(m,d)=>(u(),c("main",i,[e("span",null,[r(o(s(t)("admin.about.source1"))+" ",1),e("em",null,[e("a",l,o(s(t)("admin.about.source2")),1)])])]))}});export{f as default};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{d as T,k as F,e as j,o as C,s as _,b as N,g as v,p as K,q as E,m as R,r as p,t as u,v as B,j as q,f as J,l as W,n as Y,S as H,w as Q}from"./el-popper-2c6af9ca.js";import{ak as m,am as M,ah as V,al as X,aJ as Z}from"./request-aa27c475.js";function k(t,e){for(var n=-1,o=t==null?0:t.length;++n<o&&e(t[n],n,t)!==!1;);return t}function z(t,e){return t&&T(e,F(e),t)}function tt(t,e){return t&&T(e,j(e),t)}function et(t,e){return T(t,C(t),e)}var rt=Object.getOwnPropertySymbols,nt=rt?function(t){for(var e=[];t;)N(e,C(t)),t=v(t);return e}:_;const x=nt;function at(t,e){return T(t,x(t),e)}function ot(t){return K(t,j,x)}var st=Object.prototype,ct=st.hasOwnProperty;function it(t){var e=t.length,n=new t.constructor(e);return e&&typeof t[0]=="string"&&ct.call(t,"index")&&(n.index=t.index,n.input=t.input),n}function ft(t,e){var n=e?E(t.buffer):t.buffer;return new t.constructor(n,t.byteOffset,t.byteLength)}var gt=/\w*$/;function bt(t){var e=new t.constructor(t.source,gt.exec(t));return e.lastIndex=t.lastIndex,e}var w=m?m.prototype:void 0,I=w?w.valueOf:void 0;function yt(t){return I?Object(I.call(t)):{}}var ut="[object Boolean]",Tt="[object Date]",lt="[object Map]",jt="[object Number]",pt="[object RegExp]",At="[object Set]",dt="[object String]",$t="[object Symbol]",St="[object ArrayBuffer]",mt="[object DataView]",wt="[object Float32Array]",It="[object Float64Array]",Ot="[object Int8Array]",ht="[object Int16Array]",Ft="[object Int32Array]",Ct="[object Uint8Array]",Et="[object Uint8ClampedArray]",Bt="[object Uint16Array]",Mt="[object Uint32Array]";function xt(t,e,n){var o=t.constructor;switch(e){case St:return E(t);case ut:case Tt:return new o(+t);case mt:return ft(t,n);case wt:case It:case Ot:case ht:case Ft:case Ct:case Et:case Bt:case Mt:return R(t,n);case lt:return new o;case jt:case dt:return new o(t);case pt:return bt(t);case At:return new o;case $t:return yt(t)}}var Lt="[object Map]";function Ut(t){return M(t)&&p(t)==Lt}var O=u&&u.isMap,Pt=O?B(O):Ut;const Dt=Pt;var Gt="[object Set]";function _t(t){return M(t)&&p(t)==Gt}var h=u&&u.isSet,Nt=h?B(h):_t;const vt=Nt;var Kt=1,Rt=2,qt=4,L="[object Arguments]",Jt="[object Array]",Wt="[object Boolean]",Yt="[object Date]",Ht="[object Error]",U="[object Function]",Qt="[object GeneratorFunction]",Vt="[object Map]",Xt="[object Number]",P="[object Object]",Zt="[object RegExp]",kt="[object Set]",zt="[object String]",te="[object Symbol]",ee="[object WeakMap]",re="[object ArrayBuffer]",ne="[object DataView]",ae="[object Float32Array]",oe="[object Float64Array]",se="[object Int8Array]",ce="[object Int16Array]",ie="[object Int32Array]",fe="[object Uint8Array]",ge="[object Uint8ClampedArray]",be="[object Uint16Array]",ye="[object Uint32Array]",r={};r[L]=r[Jt]=r[re]=r[ne]=r[Wt]=r[Yt]=r[ae]=r[oe]=r[se]=r[ce]=r[ie]=r[Vt]=r[Xt]=r[P]=r[Zt]=r[kt]=r[zt]=r[te]=r[fe]=r[ge]=r[be]=r[ye]=!0;r[Ht]=r[U]=r[ee]=!1;function l(t,e,n,o,g,s){var a,b=e&Kt,y=e&Rt,D=e&qt;if(n&&(a=g?n(t,o,g,s):n(t)),a!==void 0)return a;if(!V(t))return t;var A=X(t);if(A){if(a=it(t),!b)return q(t,a)}else{var f=p(t),d=f==U||f==Qt;if(J(t))return W(t,b);if(f==P||f==L||d&&!g){if(a=y||d?{}:Y(t),!b)return y?at(t,tt(a,t)):et(t,z(a,t))}else{if(!r[f])return g?t:{};a=xt(t,f,b)}}s||(s=new H);var $=s.get(t);if($)return $;s.set(t,a),vt(t)?t.forEach(function(c){a.add(l(c,e,n,c,t,s))}):Dt(t)&&t.forEach(function(c,i){a.set(i,l(c,e,n,i,t,s))});var G=D?y?ot:Q:y?j:F,S=A?void 0:G(t);return k(S||t,function(c,i){S&&(i=c,c=t[i]),Z(a,i,l(c,e,n,i,t,s))}),a}export{l as b}; import{d as T,k as F,e as j,o as C,s as _,b as N,g as v,p as K,q as E,m as R,r as p,t as u,v as B,j as q,f as J,l as W,n as Y,S as H,w as Q}from"./el-popper-c9bad75e.js";import{ak as m,am as M,ah as V,al as X,aJ as Z}from"./request-e2f6da06.js";function k(t,e){for(var n=-1,o=t==null?0:t.length;++n<o&&e(t[n],n,t)!==!1;);return t}function z(t,e){return t&&T(e,F(e),t)}function tt(t,e){return t&&T(e,j(e),t)}function et(t,e){return T(t,C(t),e)}var rt=Object.getOwnPropertySymbols,nt=rt?function(t){for(var e=[];t;)N(e,C(t)),t=v(t);return e}:_;const x=nt;function at(t,e){return T(t,x(t),e)}function ot(t){return K(t,j,x)}var st=Object.prototype,ct=st.hasOwnProperty;function it(t){var e=t.length,n=new t.constructor(e);return e&&typeof t[0]=="string"&&ct.call(t,"index")&&(n.index=t.index,n.input=t.input),n}function ft(t,e){var n=e?E(t.buffer):t.buffer;return new t.constructor(n,t.byteOffset,t.byteLength)}var gt=/\w*$/;function bt(t){var e=new t.constructor(t.source,gt.exec(t));return e.lastIndex=t.lastIndex,e}var w=m?m.prototype:void 0,I=w?w.valueOf:void 0;function yt(t){return I?Object(I.call(t)):{}}var ut="[object Boolean]",Tt="[object Date]",lt="[object Map]",jt="[object Number]",pt="[object RegExp]",At="[object Set]",dt="[object String]",$t="[object Symbol]",St="[object ArrayBuffer]",mt="[object DataView]",wt="[object Float32Array]",It="[object Float64Array]",Ot="[object Int8Array]",ht="[object Int16Array]",Ft="[object Int32Array]",Ct="[object Uint8Array]",Et="[object Uint8ClampedArray]",Bt="[object Uint16Array]",Mt="[object Uint32Array]";function xt(t,e,n){var o=t.constructor;switch(e){case St:return E(t);case ut:case Tt:return new o(+t);case mt:return ft(t,n);case wt:case It:case Ot:case ht:case Ft:case Ct:case Et:case Bt:case Mt:return R(t,n);case lt:return new o;case jt:case dt:return new o(t);case pt:return bt(t);case At:return new o;case $t:return yt(t)}}var Lt="[object Map]";function Ut(t){return M(t)&&p(t)==Lt}var O=u&&u.isMap,Pt=O?B(O):Ut;const Dt=Pt;var Gt="[object Set]";function _t(t){return M(t)&&p(t)==Gt}var h=u&&u.isSet,Nt=h?B(h):_t;const vt=Nt;var Kt=1,Rt=2,qt=4,L="[object Arguments]",Jt="[object Array]",Wt="[object Boolean]",Yt="[object Date]",Ht="[object Error]",U="[object Function]",Qt="[object GeneratorFunction]",Vt="[object Map]",Xt="[object Number]",P="[object Object]",Zt="[object RegExp]",kt="[object Set]",zt="[object String]",te="[object Symbol]",ee="[object WeakMap]",re="[object ArrayBuffer]",ne="[object DataView]",ae="[object Float32Array]",oe="[object Float64Array]",se="[object Int8Array]",ce="[object Int16Array]",ie="[object Int32Array]",fe="[object Uint8Array]",ge="[object Uint8ClampedArray]",be="[object Uint16Array]",ye="[object Uint32Array]",r={};r[L]=r[Jt]=r[re]=r[ne]=r[Wt]=r[Yt]=r[ae]=r[oe]=r[se]=r[ce]=r[ie]=r[Vt]=r[Xt]=r[P]=r[Zt]=r[kt]=r[zt]=r[te]=r[fe]=r[ge]=r[be]=r[ye]=!0;r[Ht]=r[U]=r[ee]=!1;function l(t,e,n,o,g,s){var a,b=e&Kt,y=e&Rt,D=e&qt;if(n&&(a=g?n(t,o,g,s):n(t)),a!==void 0)return a;if(!V(t))return t;var A=X(t);if(A){if(a=it(t),!b)return q(t,a)}else{var f=p(t),d=f==U||f==Qt;if(J(t))return W(t,b);if(f==P||f==L||d&&!g){if(a=y||d?{}:Y(t),!b)return y?at(t,tt(a,t)):et(t,z(a,t))}else{if(!r[f])return g?t:{};a=xt(t,f,b)}}s||(s=new H);var $=s.get(t);if($)return $;s.set(t,a),vt(t)?t.forEach(function(c){a.add(l(c,e,n,c,t,s))}):Dt(t)&&t.forEach(function(c,i){a.set(i,l(c,e,n,i,t,s))});var G=D?y?ot:Q:y?j:F,S=A?void 0:G(t);return k(S||t,function(c,i){S&&(i=c,c=t[i]),Z(a,i,l(c,e,n,i,t,s))}),a}export{l as b};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{c as S,ax as _,b as P,y as V,u as W,M as k,k as y,_ as M,w as $}from"./request-aa27c475.js";import{d as B,c as H,o as d,F as I,D as m,r as v,b as i,u as l,a as h,w as g,q as C,E as b,G as w,e as T,T as F}from"./index-fd7b9342.js";let f;const A=s=>{var a;if(!S)return 0;if(f!==void 0)return f;const o=document.createElement("div");o.className=`${s}-scrollbar__wrap`,o.style.visibility="hidden",o.style.width="100px",o.style.position="absolute",o.style.top="-9999px",document.body.appendChild(o);const n=o.offsetWidth;o.style.overflow="scroll";const e=document.createElement("div");e.style.width="100%",o.appendChild(e);const c=e.offsetWidth;return(a=o.parentNode)==null||a.removeChild(o),f=n-c,f};function J(s,a){if(!S)return;if(!a){s.scrollTop=0;return}const o=[];let n=a.offsetParent;for(;n!==null&&s!==n&&s.contains(n);)o.push(n),n=n.offsetParent;const e=a.offsetTop+o.reduce((t,u)=>t+u.offsetTop,0),c=e+a.offsetHeight,r=s.scrollTop,p=r+s.clientHeight;e<r?s.scrollTop=e:c>p&&(s.scrollTop=c-s.clientHeight)}const L=s=>["",..._].includes(s),R=P({type:{type:String,values:["success","info","warning","danger",""],default:""},closable:Boolean,disableTransitions:Boolean,hit:Boolean,color:{type:String,default:""},size:{type:String,values:_,default:""},effect:{type:String,values:["dark","light","plain"],default:"light"},round:Boolean}),q={close:s=>s instanceof MouseEvent,click:s=>s instanceof MouseEvent},D=B({name:"ElTag"}),G=B({...D,props:R,emits:q,setup(s,{emit:a}){const o=s,n=V(),e=W("tag"),c=H(()=>{const{type:t,hit:u,effect:E,closable:z,round:N}=o;return[e.b(),e.is("closable",z),e.m(t),e.m(n.value),e.m(E),e.is("hit",u),e.is("round",N)]}),r=t=>{a("close",t)},p=t=>{a("click",t)};return(t,u)=>t.disableTransitions?(d(),I("span",{key:0,class:i(l(c)),style:T({backgroundColor:t.color}),onClick:p},[m("span",{class:i(l(e).e("content"))},[v(t.$slots,"default")],2),t.closable?(d(),h(l(y),{key:0,class:i(l(e).e("close")),onClick:b(r,["stop"])},{default:g(()=>[C(l(k))]),_:1},8,["class","onClick"])):w("v-if",!0)],6)):(d(),h(F,{key:1,name:`${l(e).namespace.value}-zoom-in-center`,appear:""},{default:g(()=>[m("span",{class:i(l(c)),style:T({backgroundColor:t.color}),onClick:p},[m("span",{class:i(l(e).e("content"))},[v(t.$slots,"default")],2),t.closable?(d(),h(l(y),{key:0,class:i(l(e).e("close")),onClick:b(r,["stop"])},{default:g(()=>[C(l(k))]),_:1},8,["class","onClick"])):w("v-if",!0)],6)]),_:3},8,["name"]))}});var K=M(G,[["__file","/home/runner/work/element-plus/element-plus/packages/components/tag/src/tag.vue"]]);const O=$(K);export{O as E,A as g,L as i,J as s,R as t}; import{c as S,ax as _,b as P,y as V,u as W,M as k,k as y,_ as M,w as $}from"./request-e2f6da06.js";import{d as B,c as H,o as d,F as I,D as m,r as v,b as i,u as l,a as h,w as g,q as C,E as b,G as w,e as T,T as F}from"./index-bdf811f7.js";let f;const A=s=>{var a;if(!S)return 0;if(f!==void 0)return f;const o=document.createElement("div");o.className=`${s}-scrollbar__wrap`,o.style.visibility="hidden",o.style.width="100px",o.style.position="absolute",o.style.top="-9999px",document.body.appendChild(o);const n=o.offsetWidth;o.style.overflow="scroll";const e=document.createElement("div");e.style.width="100%",o.appendChild(e);const c=e.offsetWidth;return(a=o.parentNode)==null||a.removeChild(o),f=n-c,f};function J(s,a){if(!S)return;if(!a){s.scrollTop=0;return}const o=[];let n=a.offsetParent;for(;n!==null&&s!==n&&s.contains(n);)o.push(n),n=n.offsetParent;const e=a.offsetTop+o.reduce((t,u)=>t+u.offsetTop,0),c=e+a.offsetHeight,r=s.scrollTop,p=r+s.clientHeight;e<r?s.scrollTop=e:c>p&&(s.scrollTop=c-s.clientHeight)}const L=s=>["",..._].includes(s),R=P({type:{type:String,values:["success","info","warning","danger",""],default:""},closable:Boolean,disableTransitions:Boolean,hit:Boolean,color:{type:String,default:""},size:{type:String,values:_,default:""},effect:{type:String,values:["dark","light","plain"],default:"light"},round:Boolean}),q={close:s=>s instanceof MouseEvent,click:s=>s instanceof MouseEvent},D=B({name:"ElTag"}),G=B({...D,props:R,emits:q,setup(s,{emit:a}){const o=s,n=V(),e=W("tag"),c=H(()=>{const{type:t,hit:u,effect:E,closable:z,round:N}=o;return[e.b(),e.is("closable",z),e.m(t),e.m(n.value),e.m(E),e.is("hit",u),e.is("round",N)]}),r=t=>{a("close",t)},p=t=>{a("click",t)};return(t,u)=>t.disableTransitions?(d(),I("span",{key:0,class:i(l(c)),style:T({backgroundColor:t.color}),onClick:p},[m("span",{class:i(l(e).e("content"))},[v(t.$slots,"default")],2),t.closable?(d(),h(l(y),{key:0,class:i(l(e).e("close")),onClick:b(r,["stop"])},{default:g(()=>[C(l(k))]),_:1},8,["class","onClick"])):w("v-if",!0)],6)):(d(),h(F,{key:1,name:`${l(e).namespace.value}-zoom-in-center`,appear:""},{default:g(()=>[m("span",{class:i(l(c)),style:T({backgroundColor:t.color}),onClick:p},[m("span",{class:i(l(e).e("content"))},[v(t.$slots,"default")],2),t.closable?(d(),h(l(y),{key:0,class:i(l(e).e("close")),onClick:b(r,["stop"])},{default:g(()=>[C(l(k))]),_:1},8,["class","onClick"])):w("v-if",!0)],6)]),_:3},8,["name"]))}});var K=M(G,[["__file","/home/runner/work/element-plus/element-plus/packages/components/tag/src/tag.vue"]]);const O=$(K);export{O as E,A as g,L as i,J as s,R as t};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{X as A,K as N}from"./index-fd7b9342.js";var o=(E=>(E[E.TEXT=1]="TEXT",E[E.CLASS=2]="CLASS",E[E.STYLE=4]="STYLE",E[E.PROPS=8]="PROPS",E[E.FULL_PROPS=16]="FULL_PROPS",E[E.HYDRATE_EVENTS=32]="HYDRATE_EVENTS",E[E.STABLE_FRAGMENT=64]="STABLE_FRAGMENT",E[E.KEYED_FRAGMENT=128]="KEYED_FRAGMENT",E[E.UNKEYED_FRAGMENT=256]="UNKEYED_FRAGMENT",E[E.NEED_PATCH=512]="NEED_PATCH",E[E.DYNAMIC_SLOTS=1024]="DYNAMIC_SLOTS",E[E.HOISTED=-1]="HOISTED",E[E.BAIL=-2]="BAIL",E))(o||{});const e=E=>{const _=A(E)?E:[E],S=[];return _.forEach(T=>{var r;A(T)?S.push(...e(T)):N(T)&&A(T.children)?S.push(...e(T.children)):(S.push(T),N(T)&&((r=T.component)!=null&&r.subTree)&&S.push(...e(T.component.subTree)))}),S};export{o as P,e as f}; import{X as A,K as N}from"./index-bdf811f7.js";var o=(E=>(E[E.TEXT=1]="TEXT",E[E.CLASS=2]="CLASS",E[E.STYLE=4]="STYLE",E[E.PROPS=8]="PROPS",E[E.FULL_PROPS=16]="FULL_PROPS",E[E.HYDRATE_EVENTS=32]="HYDRATE_EVENTS",E[E.STABLE_FRAGMENT=64]="STABLE_FRAGMENT",E[E.KEYED_FRAGMENT=128]="KEYED_FRAGMENT",E[E.UNKEYED_FRAGMENT=256]="UNKEYED_FRAGMENT",E[E.NEED_PATCH=512]="NEED_PATCH",E[E.DYNAMIC_SLOTS=1024]="DYNAMIC_SLOTS",E[E.HOISTED=-1]="HOISTED",E[E.BAIL=-2]="BAIL",E))(o||{});const e=E=>{const _=A(E)?E:[E],S=[];return _.forEach(T=>{var r;A(T)?S.push(...e(T)):N(T)&&A(T.children)?S.push(...e(T.children)):(S.push(T),N(T)&&((r=T.component)!=null&&r.subTree)&&S.push(...e(T.component.subTree)))}),S};export{o as P,e as f};
+1 -1
View File
@@ -8,7 +8,7 @@
<meta name="keywords" content="{{keywords}}" /> <meta name="keywords" content="{{keywords}}" />
<meta name="generator" content="FileCodeBox2.0" /> <meta name="generator" content="FileCodeBox2.0" />
<title>{{title}}</title> <title>{{title}}</title>
<script type="module" crossorigin src="/assets/index-fd7b9342.js"></script> <script type="module" crossorigin src="/assets/index-bdf811f7.js"></script>
<link rel="stylesheet" href="/assets/index-76720517.css"> <link rel="stylesheet" href="/assets/index-76720517.css">
</head> </head>
<body style="background: url('{{background}}') no-repeat center center fixed;background-size: cover;"> <body style="background: url('{{background}}') no-repeat center center fixed;background-size: cover;">
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

+5
View File
@@ -88,6 +88,11 @@ export default {
file_path: 'File Path', file_path: 'File Path',
action: 'Action', action: 'Action',
delete: 'Delete', delete: 'Delete',
delete_success: 'Delete successful',
forever: 'Forever',
unlimited_count: 'Unlimited',
download: 'Download',
download_fail: 'File save failed, please try again later~',
}, },
menu: { menu: {
fileManage: 'File Management', fileManage: 'File Management',
+5
View File
@@ -88,6 +88,11 @@ export default {
file_path: '文件路径', file_path: '文件路径',
action: '操作', action: '操作',
delete: '删除', delete: '删除',
delete_success: '删除成功',
forever: '永久有效',
unlimited_count: '不限次数',
download: '下载',
download_fail: '文件保存失败,请稍后再试~',
}, },
menu: { menu: {
fileManage: '文件管理', fileManage: '文件管理',
+3
View File
@@ -14,6 +14,9 @@ instance.interceptors.request.use(
}); });
instance.interceptors.response.use( instance.interceptors.response.use(
(response:any) => { (response:any) => {
if (response.status === 200 && response.config.url === '/admin/file/download') {
return response;
}
if (response.data.code === 200) { if (response.data.code === 200) {
return response.data; return response.data;
} else { } else {
+11
View File
@@ -0,0 +1,11 @@
export function formatTimestamp(timestamp: string): string {
const date = new Date(timestamp);
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
const seconds = date.getSeconds().toString().padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
+67 -3
View File
@@ -12,7 +12,7 @@
<el-table-column prop="used_count" :label="t('admin.fileView.used_count')" /> <el-table-column prop="used_count" :label="t('admin.fileView.used_count')" />
<el-table-column prop="expired_count" :label="t('admin.fileView.expired_count')"> <el-table-column prop="expired_count" :label="t('admin.fileView.expired_count')">
<template #default="scope"> <template #default="scope">
<span>{{ scope.row.expired_count > -1 ? scope.row.expired_count : '不限次数' }}</span> <span>{{ scope.row.expired_count > -1 ? scope.row.expired_count : t('admin.fileView.unlimited_count') }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="size" :label="t('admin.fileView.size')"> <el-table-column prop="size" :label="t('admin.fileView.size')">
@@ -22,7 +22,7 @@
</el-table-column> </el-table-column>
<el-table-column prop="expired_at" :label="t('admin.fileView.expired_at')"> <el-table-column prop="expired_at" :label="t('admin.fileView.expired_at')">
<template #default="scope"> <template #default="scope">
<span>{{ scope.row.expired_at ? scope.row.expired_at : '永久有效' }}</span> <span>{{ scope.row.expired_at ? formatTimestamp(scope.row.expired_at) : t('admin.fileView.forever') }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="file_path" :label="t('admin.fileView.file_path')" /> <el-table-column prop="file_path" :label="t('admin.fileView.file_path')" />
@@ -32,6 +32,7 @@
</template> </template>
<template #default="scope"> <template #default="scope">
<el-button type="danger" size="small" @click="deleteFile(scope.row.id)">{{ t('admin.fileView.delete') }}</el-button> <el-button type="danger" size="small" @click="deleteFile(scope.row.id)">{{ t('admin.fileView.delete') }}</el-button>
<el-button type="success" size="small" @click="downloadFile(scope.row.id)" v-if="scope.row.file_path">{{ t('admin.fileView.download') }}</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -40,6 +41,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { request } from "@/utils/request"; import { request } from "@/utils/request";
import { formatTimestamp } from "@/utils/timestamp-format"
import { ref } from "vue"; import { ref } from "vue";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
const tableData = ref([]); const tableData = ref([]);
@@ -64,10 +66,72 @@ const deleteFile = (id: number) => {
id, id,
}, },
}).then(() => { }).then(() => {
ElMessage.success('删除成功'); ElMessage.success(t('admin.fileView.delete_success'));
refreshData(); refreshData();
}); });
}; };
const downloadFile = (id: number) => {
request({
url: '/admin/file/download',
method: 'get',
params: {
id,
},
responseType: 'blob'
}).then((response: any) => {
const contentDisposition = response.headers['content-disposition'];
let filename = 'file';
// 使用正则表达式来提取文件名
const filenameMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
if (filenameMatch != null && filenameMatch[1]) {
// 去除文件名周围的引号
filename = filenameMatch[1].replace(/['"]/g, '');
}
// @ts-ignore
if (window.showSaveFilePicker)
saveFileByWebApi(response.data, filename).catch(() => {
// ElMessage.error('文件保存函数不支持您的浏览器');
saveFileByElementA(response.data, filename).catch(() => {
ElMessage.error(t('admin.fileView.download_fail'));
})
})
else
saveFileByElementA(response.data, filename).catch(() => {
ElMessage.error(t('admin.fileView.download_fail'));
})
});
};
async function saveFileByElementA(fileBlob: Blob, filename: string) {
const downloadUrl = window.URL.createObjectURL(fileBlob);
const link = document.createElement('a');
link.href = downloadUrl;
link.download = filename; // 设置下载文件名
document.body.appendChild(link);
link.click();
// 清理并释放URL对象
window.URL.revokeObjectURL(downloadUrl);
document.body.removeChild(link);
}
async function saveFileByWebApi(fileBlob: Blob, filename: string) {
// 创建一个新句柄。
// @ts-ignore
const newHandle = await window.showSaveFilePicker({
suggestedName: filename,
});
// 创建一个 FileSystemWritableFileStream 用于写入。
const writableStream = await newHandle.createWritable();
// 写入我们的文件。
await writableStream.write(fileBlob);
// 关闭文件并将内容写入磁盘。
await writableStream.close();
}
const updatePageSize=(pageSize: number) => { const updatePageSize=(pageSize: number) => {
params.value.size = pageSize; params.value.size = pageSize;
refreshData(); refreshData();