Skip to content

Commit 8a93dd5

Browse files
committed
add:管理页面页码+Banner配置
1 parent 1c93894 commit 8a93dd5

File tree

4 files changed

+221
-27
lines changed

4 files changed

+221
-27
lines changed

database.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import datetime
2-
from sqlalchemy import Boolean, Column, Integer, String, DateTime
2+
from sqlalchemy import Boolean, Column, Integer, String, DateTime, JSON
33
from sqlalchemy.ext.declarative import declarative_base
44
from sqlalchemy.ext.asyncio import create_async_engine
55
from sqlalchemy.ext.asyncio.session import AsyncSession
@@ -20,6 +20,13 @@ async def get_session():
2020
yield s
2121

2222

23+
class Values(Base):
24+
__tablename__ = 'values'
25+
id = Column(Integer, primary_key=True, index=True)
26+
key = Column(String, unique=True)
27+
value = Column(JSON)
28+
29+
2330
class Codes(Base):
2431
__tablename__ = "codes"
2532
id = Column(Integer, primary_key=True, index=True)

main.py

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44
from pathlib import Path
55

66
from fastapi import FastAPI, Depends, UploadFile, Form, File, HTTPException, BackgroundTasks
7+
from starlette.requests import Request
78
from starlette.responses import HTMLResponse, FileResponse
89
from starlette.staticfiles import StaticFiles
910

10-
from sqlalchemy import select, update
11+
from sqlalchemy import select, update, func
1112
from sqlalchemy.ext.asyncio.session import AsyncSession
1213

1314
import settings
1415
from utils import delete_expire_files, storage, get_code, error_ip_limit, upload_ip_limit
15-
from database import get_session, Codes, init_models
16+
from database import get_session, Codes, init_models, Values
1617
from depends import admin_required
1718

1819
# 实例化FastAPI
@@ -48,16 +49,33 @@ async def startup():
4849
.replace('{{keywords}}', settings.KEYWORDS)
4950

5051

51-
@app.get(f'/{settings.ADMIN_ADDRESS}', description='管理页面', response_class=HTMLResponse)
52+
@app.get(f'/{settings.ADMIN_ADDRESS}', description='管理页面')
5253
async def admin():
5354
return HTMLResponse(admin_html)
5455

5556

5657
@app.post(f'/{settings.ADMIN_ADDRESS}', dependencies=[Depends(admin_required)], description='查询数据库列表')
57-
async def admin_post(s: AsyncSession = Depends(get_session)):
58-
# 查询数据库列表
59-
codes = (await s.execute(select(Codes))).scalars().all()
60-
return {'detail': '查询成功', 'data': codes}
58+
async def admin_post(page: int = Form(default=1), size: int = Form(default=10), s: AsyncSession = Depends(get_session)):
59+
codes = (await s.execute(select(Codes).offset((page - 1) * size).limit(size))).scalars().all()
60+
total = (await s.execute(select(func.count(Codes.id)))).scalar()
61+
return {'detail': '查询成功', 'data': codes, 'paginate': {
62+
'page': page,
63+
'size': size,
64+
'total': total
65+
}}
66+
67+
68+
@app.patch(f'/{settings.ADMIN_ADDRESS}', dependencies=[Depends(admin_required)], description='修改数据库数据')
69+
async def admin_patch(request: Request, s: AsyncSession = Depends(get_session)):
70+
# 从数据库获取系统配置
71+
# 如果不存在config这个key,就创建一个
72+
config = (await s.execute(select(Values).filter(Values.key == 'config'))).scalar_one_or_none()
73+
if not config:
74+
s.add(Values(key='config', value=await request.json()))
75+
else:
76+
await s.execute(update(Values).where(Values.key == 'config').values(value=await request.json()))
77+
await s.commit()
78+
return {'detail': '修改成功'}
6179

6280

6381
@app.delete(f'/{settings.ADMIN_ADDRESS}', dependencies=[Depends(admin_required)], description='删除数据库记录')
@@ -76,16 +94,40 @@ async def admin_delete(code: str, s: AsyncSession = Depends(get_session)):
7694
return {'detail': '删除成功'}
7795

7896

97+
@app.get('/config', description='获取系统配置', dependencies=[Depends(admin_required)])
98+
async def config(s: AsyncSession = Depends(get_session)):
99+
# 从数据库获取系统配置
100+
data = (await s.execute(select(Values).filter(Values.key == 'config'))).scalar_one_or_none()
101+
return {'detail': '获取成功', 'data': data.value}
102+
103+
79104
@app.get('/')
80105
async def index():
81106
return HTMLResponse(index_html)
82107

83108

84109
@app.get('/banner')
85-
async def banner():
110+
async def banner(s: AsyncSession = Depends(get_session)):
111+
# 数据库查询config
112+
config = (await s.execute(select(Values).filter(Values.key == 'config'))).scalar_one_or_none()
113+
# 如果存在config,就返回config的value
114+
if config and config.value.get('banners'):
115+
return {
116+
'detail': '查询成功',
117+
'data': config.value['banners']
118+
}
119+
# 如果不存在config,就返回默认的banner
86120
return {
87121
'detail': 'banner',
88-
'data': settings.UPLOAD_BANNERS.split(',')
122+
'data': [{
123+
'text': 'FileCodeBox',
124+
'url': 'https://github.com/vastsa/FileCodeBox',
125+
'src': '/static/banners/img_1.png'
126+
}, {
127+
'text': 'FileCodeBox',
128+
'url': 'https://www.lanol.cn',
129+
'src': '/static/banners/img_2.png'
130+
}]
89131
}
90132

91133

templates/admin.html

Lines changed: 160 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,26 @@
1212
<meta name="keywords" content="{{keywords}}"/>
1313
<meta name="generator" content="FileCodeBox"/>
1414
<meta name="template" content="Lan"/>
15-
<script src="/static/asserts/vue.min.js"></script>
15+
<script src="/static/asserts/vue.js"></script>
1616
<script src="/static/asserts/index.js"></script>
17+
<style>
18+
.el-menu-demo {
19+
text-align: center;
20+
}
21+
</style>
1722
</head>
1823
<body>
1924
<div id="app">
2025
<el-row v-if="login" :gutter="10">
21-
<el-col :xs="0" :sm="4" :md="4" :lg="4" :xl="4">
22-
&nbsp;
26+
<el-col :span="24" style="text-align: center">
27+
<el-menu :default-active="activeIndex" class="el-menu-demo" mode="horizontal" @select="handleSelect">
28+
<el-menu-item index="2">文件管理</el-menu-item>
29+
<el-menu-item index="3">系统配置</el-menu-item>
30+
</el-menu>
2331
</el-col>
24-
<el-col :xs="24" :sm="16" :md="16" :lg="16" :xl="16">
25-
<el-card>
26-
<el-empty v-if="files.length===0" description="暂时还没有文件"></el-empty>
32+
<el-col v-if="activeIndex === '2'">
33+
<el-card style="height: 80vh;overflow: scroll;margin-bottom: 1rem">
34+
<el-empty v-if="files.length === 0" description="暂时还没有文件"></el-empty>
2735
<el-card v-for="file in files" :key="file.code">
2836
<el-row>
2937
<el-col :span="20">
@@ -49,8 +57,87 @@
4957
</el-row>
5058
</el-card>
5159
</el-card>
60+
<el-pagination
61+
background
62+
style="text-align: center"
63+
layout="prev, pager, next, sizes"
64+
:page-size="paginate.page_size"
65+
@current-change="loginAdmin"
66+
@size-change="updateSize"
67+
:total="paginate.total"
68+
>
69+
</el-pagination>
70+
</el-col>
71+
<el-col v-if="activeIndex === '3'">
72+
<el-form ref="form" style="width: 50%;margin:2rem auto" :model="config" label-width="200px">
73+
<!--? <el-form-item label="错误限制">-->
74+
<!--? <div>-->
75+
<!--? 间隔:-->
76+
<!--? <el-input-number v-model="config.error_count" :min="1"></el-input-number>-->
77+
<!--? 分钟-->
78+
<!--? </div>-->
79+
<!--? <div>-->
80+
<!--? 数量:-->
81+
<!--? <el-input-number v-model="config.error_minute" :min="1"></el-input-number>-->
82+
<!--? 个-->
83+
<!--? </div>-->
84+
<!--? </el-form-item>-->
85+
<!--? <el-form-item label="上传限制">-->
86+
<!--? <div>-->
87+
<!--? 开启上传:-->
88+
<!--? <el-switch-->
89+
<!--? v-model="config.enable_upload"-->
90+
<!--? active-color="#13ce66"-->
91+
<!--? inactive-color="#ff4949">-->
92+
<!--? </el-switch>-->
93+
<!--? </div>-->
94+
<!--? <div>-->
95+
<!--? 间隔:-->
96+
<!--? <el-input-number v-model="config.upload_minute" :min="1"></el-input-number>-->
97+
<!--? 分钟-->
98+
<!--? </div>-->
99+
<!--? <div>-->
100+
<!--? 数量:-->
101+
<!--? <el-input-number v-model="config.upload_count" :min="1"></el-input-number>-->
102+
<!--? 个-->
103+
<!--? </div>-->
104+
<!--? <div>-->
105+
<!--? 最长:-->
106+
<!--? <el-input-number v-model="config.max_days" :min="1"></el-input-number>-->
107+
<!--? 天-->
108+
<!--? </div>-->
109+
<!--? <div>-->
110+
<!--? 大小:-->
111+
<!--? <el-input-number v-model="config.file_size_limit" :min="1"></el-input-number>-->
112+
<!--? MB-->
113+
<!--? </div>-->
114+
<!--? </el-form-item>-->
115+
<!--? <el-form-item label="删除间隔">-->
116+
<!--? 时长:-->
117+
<!--? <el-input-number v-model="config.delete_expire_files_interval"></el-input-number>-->
118+
<!--? 分钟-->
119+
<!--? </el-form-item>-->
120+
<!--? <el-form-item label="管理员密码">-->
121+
<!--? <el-input v-model="config.admin_password" type="password"></el-input>-->
122+
<!--? </el-form-item>-->
123+
<el-form-item v-for="(banner,index) in config.banners" :label="'Banner'+ (index+1)">
124+
<div style="width: 50%;display: inline-block">
125+
<el-input v-model="banner.text" placeholder="文本"></el-input>
126+
<el-input v-model="banner.url" placeholder="跳转链接"></el-input>
127+
<el-input v-model="banner.src" placeholder="图片路径"></el-input>
128+
</div>
129+
<div style="display: inline-block">
130+
<el-button type="danger" @click="deleteBanner(index)">删除</el-button>
131+
</div>
132+
</el-form-item>
133+
<el-form-item>
134+
<el-button size="mini" @click="addBanner" type="primary">新增Banner</el-button>
135+
</el-form-item>
136+
<el-form-item>
137+
<el-button @click="updateConfig" type="primary">更新</el-button>
138+
</el-form-item>
139+
</el-form>
52140
</el-col>
53-
<el-col :xs="0" :sm="4" :md="4" :lg="4" :xl="4">&nbsp;</el-col>
54141
</el-row>
55142
<div v-else style="width: 250px;text-align: center;margin: 40vh auto">
56143
<el-input v-model="pwd" placeholder="请输入登录密码">
@@ -69,33 +156,91 @@
69156
return {
70157
login: false,
71158
pwd: '',
72-
files: [],
159+
activeIndex: '2',
73160
config: {
74-
error_count: 0,
75-
file_size: 0,
76-
admin_pwd: 'admin'
77-
}
161+
// enable_upload: true,
162+
// max_days: 7,
163+
// error_count: 3,
164+
// error_minute: 10,
165+
// upload_count: 10,
166+
// upload_minute: 10,
167+
// delete_expire_files_interval: 10,
168+
// admin_password: 'admin',
169+
// file_size_limit: 10,
170+
banners: []
171+
},
172+
files: [],
173+
paginate: {
174+
page: 1,
175+
size: 10,
176+
total: 0,
177+
},
78178
};
79179
},
80180
mounted: function () {
81181
const pwd = localStorage.getItem('pwd');
82182
if (pwd) {
83183
login = true;
84184
this.pwd = pwd;
185+
axios.get('/config', {
186+
headers: {
187+
pwd: pwd
188+
}
189+
}).then(res => {
190+
console.log(res.data.data)
191+
this.config = res.data.data;
192+
});
85193
this.loginAdmin();
86194
}
87195
},
88196
methods: {
89-
loginAdmin: function () {
90-
axios.post('', {}, {'headers': {'pwd': this.pwd}}).then(res => {
91-
this.files = res.data.data;
197+
updateSize: function (value) {
198+
this.paginate.size = value;
199+
this.loginAdmin();
200+
},
201+
deleteBanner: function (index) {
202+
this.config.banners.splice(index, 1);
203+
},
204+
addBanner: function () {
205+
this.config.banners.push({
206+
'url': '',
207+
'src': '',
208+
'text': '',
209+
})
210+
},
211+
updateConfig: function () {
212+
axios.patch('', this.config, {
213+
'headers': {
214+
'pwd': this.pwd,
215+
'Content-Type': 'multipart/json'
216+
}
217+
}).then(res => {
218+
this.$message({
219+
message: res.data.detail,
220+
type: 'success'
221+
});
222+
})
223+
},
224+
loginAdmin: function (current_page = 1) {
225+
this.paginate.page = current_page;
226+
axios.post('', this.paginate, {
227+
'headers': {
228+
'pwd': this.pwd,
229+
'Content-Type': 'multipart/form-data'
230+
}
231+
}).then(res => {
232+
this.paginate = res.data.paginate;
233+
this.files = res.data.data
92234
this.login = true;
93235
localStorage.setItem('pwd', this.pwd);
94236
}).catch(e => {
95237
localStorage.clear()
96238
this.$message({'message': e.response.data.detail, 'type': 'error'});
97239
});
98240
},
241+
handleSelect(key, keyPath) {
242+
this.activeIndex = key;
243+
},
99244
deleteFile: function (code) {
100245
axios.delete('?code=' + code, {'headers': {'pwd': this.pwd}}).then(res => {
101246
this.files = this.files.filter(item => item.code !== code)

templates/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@
9393
<div class="block" style="margin-bottom: 1rem">
9494
<el-carousel height="150px">
9595
<el-carousel-item v-for="item in banners" :key="item">
96-
<a href="" target="_blank">
97-
<img class="image" style="width: 400px" :src="item" alt="1">
96+
<a :href="item.url" target="_blank">
97+
<img class="image" style="width: 400px" :src="item.src" :alt="item.text">
9898
</a>
9999
</el-carousel-item>
100100
</el-carousel>

0 commit comments

Comments
 (0)