Files
SEONexusAdmin/src/views/novel/index.vue
2025-04-06 21:51:01 +08:00

335 lines
10 KiB
Vue

<template>
<div class="table-box">
<ProTable
ref="proTable"
highlight-current-row
:columns="columns"
:request-api="getTableList"
:data-callback="dataCallback"
>
<!-- 表格 header 按钮 -->
<template #tableHeader="scope">
<el-button type="primary" :icon="CirclePlus" @click="openSetDrawerRef(true)">批量设置</el-button>
<el-button type="primary" style="display: none" :icon="CirclePlus" @click="openDrawer(true)">新增</el-button>
<el-button
style="display: none"
type="danger"
:icon="Delete"
plain
:disabled="!scope.isSelected"
@click="batchDelete(scope.selectedList)"
>
批量删除小说
</el-button>
</template>
<!-- 表格操作 -->
<template #operation="scope">
<el-button type="success" link :icon="ChatLineSquare" @click="openChapterDrawer(scope.row)">章节</el-button>
<el-button type="primary" style="display: none" link :icon="EditPen" @click="openDrawer(false, scope.row)">
编辑
</el-button>
<el-button type="danger" style="display: none" link :icon="Delete" @click="deleteAccount(scope.row)">
删除
</el-button>
</template>
</ProTable>
<Drawer ref="drawerRef" />
<SetDrawer ref="setDrawerRef" />
<ChapterDrawer ref="chapterDrawerRef" />
</div>
</template>
<script setup lang="tsx" name="complexProTable">
import { defineComponent, onMounted, h } from "vue";
import { reactive, ref } from "vue";
import { NovelList } from "@/api/interface/novel/list";
import { useHandleData } from "@/hooks/useHandleData";
import ProTable from "@/components/ProTable/index.vue";
import { Delete, EditPen, CirclePlus, ChatLineSquare } from "@element-plus/icons-vue";
import { ProTableInstance, ColumnProps } from "@/components/ProTable/interface";
import { getList, saveData, deleteItem,levelSet } from "@/api/modules/novel/list";
import Drawer from "./drawer.vue";
import SetDrawer from "./setDrawer.vue";
import ChapterDrawer from "./components/chapter.vue";
import { getAllList } from "@/api/modules/novel/fenlei";
// import { LOSE_STR_KEY, LOGIN_URL } from "@/config";
import router from "@/routers";
import { getEndpointAllApi } from "@/api/modules/system/endpoint";
//端点列表
const Endpoints = ref([] as any);
// ProTable 实例
const proTable = ref<ProTableInstance>();
//小说分类
// const getFenLeiList = ref([] as any);
// getAllList().then(res => {
// if (res.code === LOSE_STR_KEY) {
// router.replace(LOGIN_URL);
// return;
// }
// getFenLeiList.value = res.data.items;
// });
const initParam = reactive({
limit: 15,
page: 1
});
const dataCallback = (data: any) => {
console.log(data)
// data.items.forEach((item: any) => {
// const DataString = JSON.parse(item.xsxq_feng_mian);
// const found = Endpoints.value.find(item2 => item2.zydd_code === DataString.code);
// if (found && found.zydd_domain) {
// item["https_cover_url"] = found.zydd_domain + DataString.uri;
// } else {
// item["https_cover_url"] = import.meta.env.VITE_API_URL + DataString.uri;
// }
// });
return {
items: data.data,
total: data.total,
total_page: data.pages,
page: initParam.page,
limit: initParam.limit
};
};
const getTableList = async (params: any) => {
// 获取 getEndpointAllApi 的返回值
// const Configs = await getEndpointAllApi();
// Endpoints.value = Configs.data.items;
let newParams = JSON.parse(JSON.stringify(params));
initParam.page = newParams.page;
initParam.limit = newParams.limit;
return getList(newParams);
};
/**
created_at
:
{$date: {…}}
*/
// 表格配置项
const columns = reactive<ColumnProps<NovelList.ResList>[]>([
{ type: "selection", fixed: "left", width: 50 },
{
prop: "key",
label: "搜索内容",
isShow: false,
search: {
el: "input"
}
},
// {
// prop: "n_id",
// label: "id",
// width: 100
// },
// {
// prop: "n_source_id",
// label: "源id",
// width: 100
// },
{
prop: "og_novel_book_name",
label: "名称",
width: 120
},
{
prop: "n_level",
label: "推荐等级",
width: 120
},
{
prop: "og_novel_pin_yin",
label: "拼音",
width: 120
},
{
prop: "n_cover_path",
label: "封面",
width: 120,
render(scope) {
return <img src={'http://tp2.novel2.com'+scope.row.n_cover_path } alt="封面" style="width: 100%; height: auto;" />;
}
},
{
prop: "og_novel_status",
label: "状态"
},
{
prop: "og_novel_author",
label: "作者"
},
{
prop: "og_novel_category",
label: "分类",
width: 120,
//fieldNames: { label: "xsfl_name", value: "xsfl_id" },
//enum: getFenLeiList
},
{
prop: "n_page_description",
label: "描述"
},
// {
// prop: "n_site_name",
// label: "源站"
// },
{
prop: "og_novel_latest_chapter_name",
label: "最新章节"
},
// { prop: "xsxq_source_code", label: "源码", width: 200 },
// { prop: "xsxq_zi_shu", label: "字数", width: 150 },
// { prop: "xsxq_jie_shao", label: "描述", width: 200 },
// { prop: "xsxq_geng_xin_shi_jian", label: "最后更新时间", width: 170 },
// { prop: "created_at", label: "添加时间", width: 170 },
{ prop: "og_novel_update_time", label: "更新时间", width: 170 },
// { prop: "operation", label: "操作", fixed: "right", width: 100 }
]);
// 删除小说信息
const deleteAccount = async (params: NovelList.ResList) => {
await useHandleData(deleteItem, { xsxq_id: params.xsxq_id }, "删除所选信息");
proTable.value?.getTableList();
};
// 批量删除小说信息
const batchDelete = async (id: any[]) => {
const ids = id.map((item: NovelList.ResList) => item.xsxq_id);
await useHandleData(deleteItem, { xsxq_id: ids.join(",") }, "删除所选信息");
proTable.value?.clearSelection();
proTable.value?.getTableList();
};
// 打开 drawer(新增、编辑)
const drawerRef = ref<InstanceType<typeof Drawer> | null>(null);
const openDrawer = (isEdit: boolean, row: Partial<NovelList.ResList> = {}) => {
const params = {
title: isEdit ? "新增小说" : "编辑小说",
row: { ...row },
api: saveData,
getTableList: proTable.value?.getTableList
};
drawerRef.value?.acceptParams(params as any);
};
// 批量设置推荐等级
const setDrawerRef = ref<InstanceType<typeof SetDrawer> | null>(null);
const openSetDrawerRef = (isEdit: boolean, row: Partial<NovelList.ResList> = {}) => {
const params = {
title: "批量设置",
row: { ...row },
api: levelSet,
getTableList: proTable.value?.getTableList
};
setDrawerRef.value?.acceptParams(params as any);
};
// 章节
const chapterDrawerRef = ref<InstanceType<typeof ChapterDrawer> | null>(null);
const openChapterDrawer = (row: Partial<NovelList.ResList> = {}) => {
chapterDrawerRef.value?.acceptParams(row.xsxq_id!);
};
// 异步加载图片组件
const AsyncImageComponent = defineComponent({
props: {
url: {
type: String,
required: true
}
},
setup(props) {
const imageUrl = ref("");
const isLoading = ref(true);
const error = ref(null);
onMounted(async () => {
try {
const data = await fetchImageData(props.url);
const decodedImage = decode(data);
imageUrl.value = decodedImage;
isLoading.value = false;
} catch (err) {
console.error("Error converting image to base64:", err);
error.value = err;
isLoading.value = false;
}
});
return () => {
if (isLoading.value) {
return h("el-image", { src: "Loading..." });
}
if (error.value) {
return h("el-image", { src: "无法加载" });
}
return (
<el-image
style="width: 98%; height: 30px;display: flex;align-items: center;"
src={imageUrl.value}
preview-src-list={[imageUrl.value]}
hide-on-click-modal={true}
z-index={999999999}
preview-teleported={true}
// fit="contain"
fit="cover"
/>
);
};
}
});
// 获取图片数据
async function fetchImageData(url) {
const response = await fetch(url);
const data = await response.arrayBuffer();
return data;
}
// 解码图片函数
function decode(data, key = 0x88) {
let binary = "";
let bytes = new Uint8Array(data);
let len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i] ^ key);
}
let src = window.btoa(binary);
let image = "data:image/jpeg;base64," + src;
return image;
}
</script>
<style lang="scss">
.el-table .warning-row,
.el-table .warning-row .el-table-fixed-column--right,
.el-table .warning-row .el-table-fixed-column--left {
background-color: var(--el-color-warning-light-9);
}
.el-table .success-row,
.el-table .success-row .el-table-fixed-column--right,
.el-table .success-row .el-table-fixed-column--left {
background-color: var(--el-color-success-light-9);
}
.tag {
margin-right: 10px;
margin-bottom: 10px;
}
</style>