web/src/pages/post/media/index.vue

199 lines
4.7 KiB
Vue

<template>
<BreadPage>
<div class="overflow-hidden h-full grid grid-cols-[auto_1fr] gap-4">
<ani-group :current="current" @change="onCategoryChange"></ani-group>
<div>
<file-table>
<template #action>
<ani-upload></ani-upload>
<a-button type="primary" status="danger" :disabled="!selected.length" @click="onDeleteMany">
</a-button>
</template>
</file-table>
<a-image-preview v-model:visible="visible" :src="image"></a-image-preview>
</div>
</div>
</BreadPage>
</template>
<script setup lang="tsx">
import { FileCategory, api } from "@/api";
import { createColumn, updateColumn, useAniTable } from "@/components";
import { delConfirm } from "@/utils";
import { Message } from "@arco-design/web-vue";
import numeral from "numeral";
import AniGroup from "./components/group.vue";
import AniUpload from "./components/upload.vue";
const visible = ref(false);
const image = ref("");
const selected = ref<number[]>([]);
const current = ref<FileCategory>();
const preview = (record: any) => {
if (!record.mimetype.startsWith("image")) {
window.open(record.path, "_blank");
return;
}
image.value = record.path;
visible.value = true;
};
const onDeleteMany = async () => {
await delConfirm();
const res = await api.file.delFiles(selected.value as any[]);
selected.value = [];
Message.success(res.data.message);
fileCtx.refresh();
};
const onCategoryChange = (category: FileCategory) => {
if (fileCtx.props.search?.model) {
fileCtx.props.search.model.categoryId = category.id;
}
current.value = category;
fileCtx.refresh();
};
const getIcon = (mimetype: string) => {
if (mimetype.startsWith("image")) {
return "icon-file-iimage";
}
if (mimetype.startsWith("video")) {
return "icon-file-ivideo";
}
if (mimetype.startsWith("text")) {
return "icon-file-itxt";
}
if (mimetype.startsWith("audio")) {
return "icon-file-iaudio";
}
return "icon-file-iunknown";
};
const [fileTable, fileCtx] = useAniTable({
data: async (model, paging) => {
return api.file.getFiles({ ...model, ...paging });
},
tableProps: {
rowSelection: {
showCheckedAll: true,
},
onSelectionChange(rowKeys) {
selected.value = rowKeys as number[];
},
},
columns: [
{
title: "文件名称",
dataIndex: "name",
render({ record }) {
return (
<div class="flex items-center gap-2">
<div>
{record.mimetype.startsWith("image") ? (
<a-avatar size={32} shape="square">
<img src={record.path}></img>
</a-avatar>
) : (
<i class={`${getIcon(record.mimetype)} text-3xl mr-2`}></i>
)}
</div>
<div class="flex flex-col overflow-hidden">
<span
class="hover:text-brand-500 hover:decoration-underline underline-offset-2 cursor-pointer"
onClick={() => preview(record)}
>
{record.name}
</span>
<span class="text-gray-400 text-xs truncate">{numeral(record.size).format("0 b")}</span>
</div>
</div>
);
},
},
createColumn,
updateColumn,
{
type: "button",
title: "操作",
width: 120,
buttons: [
{
type: "modify",
text: "修改",
},
{
type: "delete",
text: "删除",
onClick({ record }) {
return api.file.delFile(record.id);
},
},
],
},
],
search: {
button: false,
model: {
categoryId: undefined,
},
items: [
{
field: "name",
label: "文件名称",
type: "search",
searchable: true,
enterable: true,
itemProps: {
hideLabel: true,
},
nodeProps: {
placeholder: "素材名称",
},
},
],
},
modify: {
title: "修改素材",
modalProps: {
width: 580,
},
items: [
{
field: "categoryId",
label: "分类",
type: "select",
options: () => api.fileCategory.getFileCategorys({ size: 0 }),
},
{
field: "name",
label: "名称",
type: "input",
},
{
field: "description",
label: "描述",
type: "textarea",
},
],
submit: ({ model }) => {
console.log(model);
return api.file.setFile(model.id, model);
},
},
});
</script>
<style scoped></style>
<route lang="json">
{
"meta": {
"sort": 10305,
"title": "素材管理",
"icon": "icon-park-outline-movie-board"
}
}
</route>