335 lines
10 KiB
Vue
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>
|