新加入图片资源管理功能,已更新到线上地址,并开通了批量上传图片权限
This commit is contained in:
parent
34248f3c95
commit
ee4f4612fe
@ -0,0 +1,185 @@
|
||||
<script setup lang="ts">
|
||||
import { Form } from '@/components/Form'
|
||||
import { useForm } from '@/hooks/web/useForm'
|
||||
import { PropType, reactive, watch, ref } from 'vue'
|
||||
import { useValidator } from '@/hooks/web/useValidator'
|
||||
import { schema } from './images.data'
|
||||
import { ElUpload } from 'element-plus'
|
||||
import { ElMessage, ElImageViewer } from 'element-plus'
|
||||
import type {
|
||||
UploadProps,
|
||||
UploadUserFile,
|
||||
UploadRequestOptions,
|
||||
UploadFile,
|
||||
UploadFiles
|
||||
} from 'element-plus'
|
||||
|
||||
const { required } = useValidator()
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const imgIndex = ref(0)
|
||||
const maxLimit = ref(50)
|
||||
|
||||
const props = defineProps({
|
||||
currentRow: {
|
||||
type: Object as PropType<Nullable<any>>,
|
||||
default: () => null
|
||||
}
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
name: [required()]
|
||||
})
|
||||
|
||||
const { register, methods, elFormRef } = useForm({
|
||||
schema: schema
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.currentRow,
|
||||
(currentRow) => {
|
||||
if (!currentRow) return
|
||||
const { setValues } = methods
|
||||
setValues(currentRow)
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
const { setValue } = methods
|
||||
|
||||
const fileList = ref<UploadUserFile[]>([])
|
||||
|
||||
const beforeImageUpload: UploadProps['beforeUpload'] = (rawFile) => {
|
||||
const isIMAGE = ['image/jpeg', 'image/gif', 'image/png'].includes(rawFile.type)
|
||||
const isLtSize = rawFile.size / 1024 / 1024 < 3
|
||||
|
||||
if (!isIMAGE) {
|
||||
ElMessage.error('上传图片素材必须是 JPG/PNG/ 格式!')
|
||||
}
|
||||
if (!isLtSize) {
|
||||
ElMessage.error('上传图片素材大小不能超过 3MB!')
|
||||
}
|
||||
|
||||
return isIMAGE && isLtSize
|
||||
}
|
||||
|
||||
// 上传成功的钩子函数
|
||||
const handleUploadSuccess: UploadProps['onSuccess'] = (
|
||||
response: any,
|
||||
uploadFile: UploadFile,
|
||||
uploadFiles: UploadFiles
|
||||
) => {
|
||||
fileList.value = uploadFiles
|
||||
setValue('images', uploadFiles)
|
||||
}
|
||||
|
||||
// 自定义上传
|
||||
const handleHttpRequest: UploadProps['httpRequest'] = (options: UploadRequestOptions) => {
|
||||
return new Promise((resolve) => {
|
||||
resolve(options)
|
||||
})
|
||||
}
|
||||
|
||||
const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile: UploadFile) => {
|
||||
imgIndex.value = fileList.value.findIndex((item) => item.uid === uploadFile.uid)
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleCloseViewer = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const handleExceedLimit = () => {
|
||||
ElMessage.error('上传失败,超出图片最大数量限制!')
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
elFormRef,
|
||||
getFormData: methods.getFormData
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form :rules="rules" label-position="top" @register="register">
|
||||
<template #images>
|
||||
<div class="flex justify-between w-[100%]">
|
||||
<span>图片资源</span>
|
||||
<span>最大数量限制:{{ fileList.length }}/{{ maxLimit }}</span>
|
||||
</div>
|
||||
<ElUpload
|
||||
class="resource-image-uploader"
|
||||
action="#"
|
||||
:http-request="handleHttpRequest"
|
||||
v-model:file-list="fileList"
|
||||
:show-file-list="true"
|
||||
:multiple="true"
|
||||
:before-upload="beforeImageUpload"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:on-exceed="handleExceedLimit"
|
||||
accept="image/jpeg,image/png"
|
||||
name="file"
|
||||
list-type="picture-card"
|
||||
:limit="maxLimit"
|
||||
:drag="true"
|
||||
:disabled="maxLimit <= fileList.length"
|
||||
>
|
||||
<div v-if="fileList.length < maxLimit">
|
||||
<div class="resource-image-uploader-icon">
|
||||
<Icon icon="akar-icons:plus" :size="23" />
|
||||
<span class="text-[12px] mt-4">点击或拖拽到这里</span>
|
||||
<span class="text-[12px] mt-4">{{ fileList.length }}/{{ maxLimit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="resource-image-uploader-icon">
|
||||
<span class="text-[12px]">已到最大限制</span>
|
||||
<span class="text-[12px] mt-4">{{ fileList.length }}/{{ maxLimit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</ElUpload>
|
||||
</template>
|
||||
</Form>
|
||||
|
||||
<ElImageViewer
|
||||
v-if="dialogVisible"
|
||||
:z-index="9999"
|
||||
@close="handleCloseViewer"
|
||||
:url-list="fileList.map((item) => item.url as string)"
|
||||
:initial-index="imgIndex"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.resource-image-uploader .el-upload {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
|
||||
.el-upload-dragger {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.resource-image-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 148px;
|
||||
height: 148px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.resource-image-uploader .el-upload:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
</style>
|
@ -0,0 +1,103 @@
|
||||
import { FormSchema } from '@/types/form'
|
||||
import { TableColumn } from '@/types/table'
|
||||
import { reactive } from 'vue'
|
||||
|
||||
export const columns = reactive<TableColumn[]>([
|
||||
{
|
||||
field: 'id',
|
||||
label: '编号',
|
||||
show: true,
|
||||
disabled: false,
|
||||
width: '120px',
|
||||
span: 24
|
||||
},
|
||||
{
|
||||
field: 'image_url',
|
||||
label: '图片',
|
||||
show: true,
|
||||
disabled: true,
|
||||
minWidth: '90px',
|
||||
span: 24
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
label: '备注',
|
||||
show: false,
|
||||
disabled: false,
|
||||
span: 24
|
||||
},
|
||||
{
|
||||
field: 'update_datetime',
|
||||
label: '更新时间',
|
||||
show: false,
|
||||
width: '180px',
|
||||
span: 24
|
||||
},
|
||||
{
|
||||
field: 'create_datetime',
|
||||
label: '创建时间',
|
||||
width: '180px',
|
||||
show: true,
|
||||
span: 24
|
||||
},
|
||||
{
|
||||
field: 'create_user.name',
|
||||
label: '创建人',
|
||||
show: false,
|
||||
span: 24
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
label: '操作',
|
||||
show: true,
|
||||
disabled: false,
|
||||
width: '260px',
|
||||
fixed: 'right',
|
||||
span: 24
|
||||
}
|
||||
])
|
||||
|
||||
export const searchSchema = reactive<FormSchema[]>([
|
||||
{
|
||||
field: 'filename',
|
||||
label: '文件名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
clearable: true,
|
||||
style: {
|
||||
width: '214px'
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
export const schema = reactive<FormSchema[]>([
|
||||
{
|
||||
field: 'upload_method',
|
||||
label: '上传方式',
|
||||
colProps: {
|
||||
span: 24
|
||||
},
|
||||
component: 'Radio',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '同时上传',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
label: '按顺序上传',
|
||||
value: '2'
|
||||
}
|
||||
]
|
||||
},
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
field: 'images',
|
||||
label: '',
|
||||
colProps: {
|
||||
span: 24
|
||||
}
|
||||
}
|
||||
])
|
218
kinit-admin/src/views/vadmin/resource/images/index.vue
Normal file
218
kinit-admin/src/views/vadmin/resource/images/index.vue
Normal file
@ -0,0 +1,218 @@
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'ResourceImages'
|
||||
}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { Table } from '@/components/Table'
|
||||
import { addImagesApi, getImagesListApi, delImagesListApi } from '@/api/vadmin/resource/images'
|
||||
import { useTable } from '@/hooks/web/useTable'
|
||||
import { columns, searchSchema } from './components/images.data'
|
||||
import { ref, watch, nextTick, unref } from 'vue'
|
||||
import { ElRow, ElButton, ElCol, ElImage, ElMessage } from 'element-plus'
|
||||
import { RightToolbar } from '@/components/RightToolbar'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
import { useRouter } from 'vue-router'
|
||||
import Write from './components/Write.vue'
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Search } from '@/components/Search'
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
|
||||
const { wsCache } = useCache()
|
||||
const { t } = useI18n()
|
||||
|
||||
const { register, elTableRef, tableObject, methods } = useTable({
|
||||
getListApi: getImagesListApi,
|
||||
delListApi: delImagesListApi,
|
||||
response: {
|
||||
data: 'data',
|
||||
count: 'count'
|
||||
}
|
||||
})
|
||||
|
||||
const { getList, setSearchParams } = methods
|
||||
|
||||
const tableSize = ref('default')
|
||||
|
||||
watch(tableSize, (val) => {
|
||||
tableSize.value = val
|
||||
})
|
||||
|
||||
const route = useRouter()
|
||||
const cacheTableHeadersKey = route.currentRoute.value.fullPath
|
||||
|
||||
watch(
|
||||
columns,
|
||||
async (val) => {
|
||||
wsCache.set(cacheTableHeadersKey, JSON.stringify(val))
|
||||
await nextTick()
|
||||
elTableRef.value?.doLayout()
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
const loading = ref(false)
|
||||
const actionType = ref('')
|
||||
|
||||
// 新增事件
|
||||
const addAction = async () => {
|
||||
dialogTitle.value = '新增图片素材'
|
||||
tableObject.currentRow = null
|
||||
dialogVisible.value = true
|
||||
actionType.value = 'add'
|
||||
}
|
||||
|
||||
// 删除事件
|
||||
const delAction = async (row: any) => {
|
||||
const { delListApi } = methods
|
||||
if (row) {
|
||||
await delListApi(true, [row.id])
|
||||
} else {
|
||||
await delListApi(true)
|
||||
}
|
||||
}
|
||||
|
||||
const writeRef = ref<ComponentRef<typeof Write>>()
|
||||
|
||||
// 提交事件
|
||||
const save = async () => {
|
||||
const write = unref(writeRef)
|
||||
await write?.elFormRef?.validate(async (isValid) => {
|
||||
if (isValid) {
|
||||
loading.value = true
|
||||
let data = await write?.getFormData()
|
||||
data?.images.forEach((item) => (item.status = 'uploading'))
|
||||
if (data?.upload_method === '2') {
|
||||
// 按顺序上传文件
|
||||
for (const item of data?.images) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', item.raw)
|
||||
await addImagesApi(formData)
|
||||
item.status = 'success'
|
||||
}
|
||||
} else if (data?.upload_method === '1') {
|
||||
// 同时上传文件
|
||||
const uploadPromises = data?.images.map(async (item) => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', item.raw)
|
||||
await addImagesApi(formData)
|
||||
item.status = 'success'
|
||||
})
|
||||
await Promise.all(uploadPromises)
|
||||
}
|
||||
// 全部上传完成后执行的代码
|
||||
getList()
|
||||
dialogVisible.value = false
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 复制数据到剪切板
|
||||
const toCopy = async (value: string) => {
|
||||
// 复制功能打包部署到线上后,需要线上地址使用 https 才可使用
|
||||
const { copy } = useClipboard()
|
||||
await copy(value)
|
||||
return ElMessage.success('复制成功')
|
||||
}
|
||||
|
||||
getList()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<Search :schema="searchSchema" @search="setSearchParams" @reset="setSearchParams" />
|
||||
|
||||
<div class="mb-8px flex justify-between">
|
||||
<ElRow :gutter="10">
|
||||
<ElCol :span="1.5" v-shop>
|
||||
<ElButton type="primary" @click="addAction">新增图片素材</ElButton>
|
||||
</ElCol>
|
||||
<ElCol :span="1.5" v-shop>
|
||||
<ElButton type="danger" @click="delAction(null)">批量删除选中的图片素材</ElButton>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<RightToolbar
|
||||
@get-list="getList"
|
||||
v-model:table-size="tableSize"
|
||||
v-model:columns="columns"
|
||||
:cache-table-headers-key="cacheTableHeadersKey"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
v-model:limit="tableObject.limit"
|
||||
v-model:page="tableObject.page"
|
||||
:columns="columns"
|
||||
:data="tableObject.tableData"
|
||||
:loading="tableObject.loading"
|
||||
:selection="true"
|
||||
:size="tableSize"
|
||||
:border="true"
|
||||
:pagination="{
|
||||
total: tableObject.count
|
||||
}"
|
||||
@register="register"
|
||||
>
|
||||
<template #image_url="{ row, $index }">
|
||||
<div class="resource-image-name flex items-center">
|
||||
<div>
|
||||
<ElImage
|
||||
:src="`${row.image_url}?x-oss-process=image/resize,m_fixed,h_100`"
|
||||
:zoom-rate="1.2"
|
||||
:preview-src-list="tableObject.tableData.map((item) => item.image_url)"
|
||||
:preview-teleported="true"
|
||||
:initial-index="$index"
|
||||
style="height: 60px; display: block"
|
||||
fit="cover"
|
||||
/>
|
||||
</div>
|
||||
<div class="leading-[35px] ml-2 truncate">
|
||||
<span>{{ row.filename }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #action="{ row }">
|
||||
<ElButton type="primary" link size="small" @click="toCopy(row.id)"> 复制图片编号 </ElButton>
|
||||
<ElButton type="primary" link size="small" @click="toCopy(row.image_url)">
|
||||
复制图片链接
|
||||
</ElButton>
|
||||
<ElButton v-shop type="danger" link size="small" @click="delAction(row)">
|
||||
{{ t('exampleDemo.del') }}
|
||||
</ElButton>
|
||||
</template>
|
||||
</Table>
|
||||
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle" width="996px" height="600px" top="3vh">
|
||||
<Write ref="writeRef" :current-row="tableObject.currentRow" />
|
||||
|
||||
<template #footer>
|
||||
<ElButton type="primary" :loading="loading" @click="save">
|
||||
{{ t('exampleDemo.save') }}
|
||||
</ElButton>
|
||||
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||
</template>
|
||||
</Dialog>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.resource-image .image-slot {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 60px;
|
||||
height: 35px;
|
||||
background: var(--el-fill-color-light);
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
@ -253,7 +253,7 @@ def __dict_filter(self, **kwargs) -> list[BinaryExpression]:
|
||||
查询所有用户id为1或2或 4或6,并且email不为空,并且名称包括李:
|
||||
|
||||
```python
|
||||
users = UserDal(db).get_datas(limit=0, id=("in", [1,2,4,6]), email=("not None"), name=("like", "李"))
|
||||
users = UserDal(db).get_datas(limit=0, id=("in", [1,2,4,6]), email=("not None", ), name=("like", "李"))
|
||||
|
||||
# limit=0:表示返回所有结果数据
|
||||
# 这里的 get_datas 默认返回的是 pydantic 模型数据
|
||||
@ -261,7 +261,7 @@ users = UserDal(db).get_datas(limit=0, id=("in", [1,2,4,6]), email=("not None"),
|
||||
users = UserDal(db).get_datas(
|
||||
limit=0,
|
||||
id=("in", [1,2,4,6]),
|
||||
email=("not None"),
|
||||
email=("not None", ),
|
||||
name=("like", "李"),
|
||||
v_return_objs=True
|
||||
)
|
||||
|
@ -11,10 +11,10 @@ from fastapi.security import OAuth2PasswordBearer
|
||||
"""
|
||||
系统版本
|
||||
"""
|
||||
VERSION = "2.0.0"
|
||||
VERSION = "2.1.0"
|
||||
|
||||
"""安全警告: 不要在生产中打开调试运行!"""
|
||||
DEBUG = True
|
||||
DEBUG = False
|
||||
|
||||
"""是否开启演示功能:取消所有POST,DELETE,PUT操作权限"""
|
||||
DEMO = not DEBUG
|
||||
@ -24,6 +24,7 @@ DEMO_WHITE_LIST_PATH = [
|
||||
"/auth/token/refresh",
|
||||
"/auth/wx/login",
|
||||
"/vadmin/system/dict/types/details",
|
||||
"/vadmin/resource/images",
|
||||
"/vadmin/auth/user/export/query/list/to/excel"
|
||||
]
|
||||
|
||||
|
@ -12,6 +12,7 @@ from apps.vadmin.record.views import app as vadmin_record_app
|
||||
from apps.vadmin.workplace.views import app as vadmin_workplace_app
|
||||
from apps.vadmin.analysis.views import app as vadmin_analysis_app
|
||||
from apps.vadmin.help.views import app as vadmin_help_app
|
||||
from apps.vadmin.resource.views import app as vadmin_resource_app
|
||||
|
||||
|
||||
# 引入应用中的路由
|
||||
@ -23,4 +24,5 @@ urlpatterns = [
|
||||
{"ApiRouter": vadmin_workplace_app, "prefix": "/vadmin/workplace", "tags": ["工作区管理"]},
|
||||
{"ApiRouter": vadmin_analysis_app, "prefix": "/vadmin/analysis", "tags": ["数据分析管理"]},
|
||||
{"ApiRouter": vadmin_help_app, "prefix": "/vadmin/help", "tags": ["帮助中心管理"]},
|
||||
{"ApiRouter": vadmin_resource_app, "prefix": "/vadmin/resource", "tags": ["资源管理"]},
|
||||
]
|
||||
|
@ -9,7 +9,7 @@
|
||||
from sqlalchemy.orm import relationship, Mapped, mapped_column
|
||||
from apps.vadmin.auth.models import VadminUser
|
||||
from db.db_base import BaseModel
|
||||
from sqlalchemy import String, Boolean, Integer, ForeignKey
|
||||
from sqlalchemy import String, Boolean, Integer, ForeignKey, Text
|
||||
|
||||
|
||||
class VadminIssueCategory(BaseModel):
|
||||
@ -42,7 +42,7 @@ class VadminIssue(BaseModel):
|
||||
category: Mapped[list["VadminIssueCategory"]] = relationship(foreign_keys=category_id, back_populates='issues')
|
||||
|
||||
title: Mapped[str] = mapped_column(String(255), index=True, nullable=False, comment="标题")
|
||||
content: Mapped[str] = mapped_column(String(5000), comment="内容")
|
||||
content: Mapped[str] = mapped_column(Text, comment="内容")
|
||||
view_number: Mapped[int] = mapped_column(Integer, default=0, comment="查看次数")
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否可见")
|
||||
|
||||
|
@ -14,7 +14,7 @@ from apps.vadmin.auth.utils.validation import LoginForm, WXLoginForm
|
||||
from utils.ip_manage import IPManage
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from db.db_base import BaseModel
|
||||
from sqlalchemy import String, Boolean
|
||||
from sqlalchemy import String, Boolean, Text
|
||||
from fastapi import Request
|
||||
from starlette.requests import Request as StarletteRequest
|
||||
from user_agents import parse
|
||||
@ -39,8 +39,8 @@ class VadminLoginRecord(BaseModel):
|
||||
area_code: Mapped[str | None] = mapped_column(String(255), comment="地区区号")
|
||||
browser: Mapped[str | None] = mapped_column(String(50), comment="浏览器")
|
||||
system: Mapped[str | None] = mapped_column(String(50), comment="操作系统")
|
||||
response: Mapped[str | None] = mapped_column(String(5000), comment="响应信息")
|
||||
request: Mapped[str | None] = mapped_column(String(5000), comment="请求信息")
|
||||
response: Mapped[str | None] = mapped_column(Text, comment="响应信息")
|
||||
request: Mapped[str | None] = mapped_column(Text, comment="请求信息")
|
||||
|
||||
@classmethod
|
||||
async def create_login_record(
|
||||
|
0
kinit-api/apps/vadmin/resource/__init__.py
Normal file
0
kinit-api/apps/vadmin/resource/__init__.py
Normal file
17
kinit-api/apps/vadmin/resource/crud.py
Normal file
17
kinit-api/apps/vadmin/resource/crud.py
Normal file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/8/25 13:15
|
||||
# @File : crud.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from core.crud import DalBase
|
||||
from . import models, schemas
|
||||
|
||||
|
||||
class ImagesDal(DalBase):
|
||||
|
||||
def __init__(self, db: AsyncSession):
|
||||
super(ImagesDal, self).__init__(db, models.VadminImages, schemas.ImagesSimpleOut)
|
1
kinit-api/apps/vadmin/resource/models/__init__.py
Normal file
1
kinit-api/apps/vadmin/resource/models/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .images import VadminImages
|
27
kinit-api/apps/vadmin/resource/models/images.py
Normal file
27
kinit-api/apps/vadmin/resource/models/images.py
Normal file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/8/25 13:41
|
||||
# @File : images.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 图片素材表
|
||||
|
||||
from sqlalchemy.orm import relationship, Mapped, mapped_column
|
||||
from apps.vadmin.auth.models import VadminUser
|
||||
from db.db_base import BaseModel
|
||||
from sqlalchemy import String, ForeignKey, Integer
|
||||
|
||||
|
||||
class VadminImages(BaseModel):
|
||||
__tablename__ = "vadmin_resource_images"
|
||||
__table_args__ = ({'comment': '图片素材表'})
|
||||
|
||||
filename: Mapped[str] = mapped_column(String(255), nullable=False, comment="原图片名称")
|
||||
image_url: Mapped[str] = mapped_column(String(500), nullable=False, comment="图片链接")
|
||||
|
||||
create_user_id: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("vadmin_auth_user.id", ondelete='RESTRICT'),
|
||||
comment="创建人"
|
||||
)
|
||||
create_user: Mapped[VadminUser] = relationship(foreign_keys=create_user_id)
|
1
kinit-api/apps/vadmin/resource/params/__init__.py
Normal file
1
kinit-api/apps/vadmin/resource/params/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .images import ImagesParams
|
27
kinit-api/apps/vadmin/resource/params/images.py
Normal file
27
kinit-api/apps/vadmin/resource/params/images.py
Normal file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/8/25 14:59
|
||||
# @File : images.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
|
||||
from fastapi import Depends
|
||||
from core.dependencies import Paging, QueryParams
|
||||
|
||||
|
||||
class ImagesParams(QueryParams):
|
||||
"""
|
||||
列表分页
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
filename: str = None,
|
||||
params: Paging = Depends()
|
||||
):
|
||||
super().__init__(params)
|
||||
self.filename = ('like', filename)
|
||||
self.v_order = "desc"
|
||||
self.v_order_field = "create_datetime"
|
1
kinit-api/apps/vadmin/resource/schemas/__init__.py
Normal file
1
kinit-api/apps/vadmin/resource/schemas/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .images import Images, ImagesOut, ImagesSimpleOut
|
33
kinit-api/apps/vadmin/resource/schemas/images.py
Normal file
33
kinit-api/apps/vadmin/resource/schemas/images.py
Normal file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/8/25 14:49
|
||||
# @File : images.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from core.data_types import DatetimeStr
|
||||
from apps.vadmin.auth.schemas import UserSimpleOut
|
||||
|
||||
|
||||
class Images(BaseModel):
|
||||
filename: str
|
||||
image_url: str
|
||||
|
||||
create_user_id: int
|
||||
|
||||
|
||||
class ImagesSimpleOut(Images):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
create_datetime: DatetimeStr
|
||||
update_datetime: DatetimeStr
|
||||
|
||||
|
||||
class ImagesOut(ImagesSimpleOut):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
create_user: UserSimpleOut
|
60
kinit-api/apps/vadmin/resource/views.py
Normal file
60
kinit-api/apps/vadmin/resource/views.py
Normal file
@ -0,0 +1,60 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# @version : 1.0
|
||||
# @Create Time : 2023/8/25 9:29
|
||||
# @File : views.py
|
||||
# @IDE : PyCharm
|
||||
# @desc : 简要说明
|
||||
|
||||
from fastapi import APIRouter, Depends, UploadFile
|
||||
from sqlalchemy.orm import joinedload
|
||||
from core.dependencies import IdList
|
||||
from utils.file.aliyun_oss import AliyunOSS, BucketConf
|
||||
from utils.response import SuccessResponse
|
||||
from . import schemas, crud, params, models
|
||||
from apps.vadmin.auth.utils.current import FullAdminAuth
|
||||
from apps.vadmin.auth.utils.validation.auth import Auth
|
||||
from application.settings import ALIYUN_OSS
|
||||
|
||||
app = APIRouter()
|
||||
|
||||
|
||||
###########################################################
|
||||
# 图片资源管理
|
||||
###########################################################
|
||||
@app.get("/images", summary="获取图片列表")
|
||||
async def get_images_list(p: params.ImagesParams = Depends(), auth: Auth = Depends(FullAdminAuth())):
|
||||
model = models.VadminImages
|
||||
v_options = [joinedload(model.create_user)]
|
||||
v_schema = schemas.ImagesOut
|
||||
datas, count = await crud.ImagesDal(auth.db).get_datas(
|
||||
**p.dict(),
|
||||
v_options=v_options,
|
||||
v_schema=v_schema,
|
||||
v_return_count=True
|
||||
)
|
||||
return SuccessResponse(datas, count=count)
|
||||
|
||||
|
||||
@app.post("/images", summary="创建图片")
|
||||
async def create_images(file: UploadFile, auth: Auth = Depends(FullAdminAuth())):
|
||||
filepath = f"/resource/images/"
|
||||
result = await AliyunOSS(BucketConf(**ALIYUN_OSS)).upload_image(filepath, file)
|
||||
data = schemas.Images(
|
||||
filename=file.filename,
|
||||
image_url=result,
|
||||
create_user_id=auth.user.id
|
||||
)
|
||||
|
||||
return SuccessResponse(await crud.ImagesDal(auth.db).create_data(data=data))
|
||||
|
||||
|
||||
@app.delete("/images", summary="删除图片", description="硬删除")
|
||||
async def delete_images(ids: IdList = Depends(), auth: Auth = Depends(FullAdminAuth())):
|
||||
await crud.ImagesDal(auth.db).delete_datas(ids.ids, v_soft=False)
|
||||
return SuccessResponse("删除成功")
|
||||
|
||||
|
||||
@app.get("/images/{data_id}", summary="获取图片信息")
|
||||
async def get_images(data_id: int, auth: Auth = Depends(FullAdminAuth())):
|
||||
return SuccessResponse(await crud.ImagesDal(auth.db).get_data(data_id, v_schema=schemas.ImagesSimpleOut))
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user