391 lines
13 KiB
Vue
391 lines
13 KiB
Vue
<template>
|
||
<div>
|
||
<div class="card">
|
||
<div class="card-body">
|
||
<h5 class="card-title text-center card-header">{{ title }}</h5>
|
||
<div id="zero-conf_wrapper" class="dataTables_wrapper dt-bootstrap4">
|
||
<div class="row align-items-center">
|
||
<div class="col-sm-12 col-md-6 d-flex align-items-center">
|
||
<div class="dataTables_length" id="zero-conf_length">
|
||
<label class="d-flex align-items-center" style="white-space: nowrap">
|
||
展示
|
||
<select
|
||
name="zero-conf_length"
|
||
aria-controls="zero-conf"
|
||
class="custom-select custom-select-sm form-control form-control-sm mx-2"
|
||
v-model="pageSize"
|
||
>
|
||
<option value="10">10</option>
|
||
<option value="25">25</option>
|
||
<option value="50">50</option>
|
||
<option value="100">100</option>
|
||
</select>
|
||
条数据
|
||
</label>
|
||
</div>
|
||
<div class="ms-3">
|
||
<label class="d-flex align-items-center" style="white-space: nowrap">
|
||
搜索:
|
||
<input
|
||
type="search"
|
||
class="form-control form-control-sm ms-2"
|
||
placeholder="请输入关键字"
|
||
aria-controls="zero-conf"
|
||
/>
|
||
<button class="btn btn-sm btn-secondary ms-2" @click="handleSearch">
|
||
搜索
|
||
</button>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="col-sm-12 col-md-6 text-center">
|
||
<button class="btn btn-sm btn-primary ms-2" @click="handleAddDate">
|
||
添加
|
||
</button>
|
||
<button class="btn btn-sm btn-danger ms-2" @click="handleBatchDelete">
|
||
批量删除
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="col-sm-12">
|
||
<table
|
||
id="zero-conf"
|
||
class="display dataTable"
|
||
style="width: 100%"
|
||
role="grid"
|
||
aria-describedby="zero-conf_info"
|
||
>
|
||
<thead>
|
||
<tr role="row">
|
||
<th
|
||
class="sorting_asc"
|
||
tabindex="0"
|
||
aria-controls="zero-conf"
|
||
rowspan="1"
|
||
colspan="1"
|
||
aria-sort="ascending"
|
||
aria-label="Name: activate to sort column descending"
|
||
style="width: 85.5469px"
|
||
v-for="(item, index) in headers"
|
||
:key="index"
|
||
>
|
||
{{ item.text }}
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr
|
||
role="row"
|
||
v-for="(item, rowIndex) in rows"
|
||
:key="rowIndex"
|
||
:class="rowIndex % 2 ? 'even' : 'odd'"
|
||
>
|
||
<td v-for="(header, colIndex) in headers" :key="colIndex">
|
||
<div v-if="header.type == null">
|
||
<span>{{ item[header.value] }}</span>
|
||
</div>
|
||
<div v-if="header.type == 'bool'">
|
||
<span
|
||
:class="[
|
||
'badge',
|
||
item[header.value] ? 'bg-danger' : 'bg-success',
|
||
]"
|
||
>{{ item[header.value] ? "禁用" : "正常" }}</span
|
||
>
|
||
</div>
|
||
<div v-if="header.type == 'arrObj'">
|
||
<span
|
||
class="badge bg-info"
|
||
v-for="(objItem, objIndex) in item[header.value]"
|
||
:key="objIndex"
|
||
>
|
||
{{ objItem[header.child] }}
|
||
</span>
|
||
</div>
|
||
<div v-if="header.type == 'money'">
|
||
<span>{{ item[header.value].toFixed(2) }}</span>
|
||
</div>
|
||
<div v-if="header.type == 'datetime'">
|
||
<span>{{ formatDateTime(item[header.value]) }}</span>
|
||
</div>
|
||
<div v-if="header.type == 'method'">
|
||
<span
|
||
:class="[
|
||
'badge',
|
||
'bg-warning',
|
||
]"
|
||
>{{ callMethod[item[header.value]] }}</span
|
||
>
|
||
</div>
|
||
<div v-if="header.type == 'find'">
|
||
<span class="badge bg-info">
|
||
{{ header.findValue.find(x => x.id == item[header.value]).name }}
|
||
</span>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<button
|
||
class="btn btn-outline-secondary m-b-xs"
|
||
@click="modify(item.id)"
|
||
>
|
||
修改
|
||
</button>
|
||
<button
|
||
class="btn btn-outline-danger m-b-xs"
|
||
@click="deleteData(item.id)"
|
||
>
|
||
删除
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
<tfoot>
|
||
<tr>
|
||
<th
|
||
rowspan="1"
|
||
colspan="1"
|
||
v-for="(header, index) in headers"
|
||
:key="index"
|
||
>
|
||
{{ header.value }}
|
||
</th>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="col-sm-12 col-md-5">
|
||
<div
|
||
class="dataTables_info"
|
||
id="zero-conf_info"
|
||
role="status"
|
||
aria-live="polite"
|
||
>
|
||
正在展示 {{ currentPageIndex * pageSize - (pageSize - 1) }} 到
|
||
{{ currentPageIndex * pageSize }} 条数据 总计 {{ dataCount }} 条
|
||
</div>
|
||
</div>
|
||
<div class="col-sm-12 col-md-7">
|
||
<div
|
||
class="dataTables_paginate paging_simple_numbers"
|
||
id="zero-conf_paginate"
|
||
>
|
||
<ul class="pagination">
|
||
<li
|
||
:class="[
|
||
'paginate_button',
|
||
'page-item',
|
||
'previous',
|
||
currentPageIndex == 1 ? 'disabled' : '',
|
||
]"
|
||
id="zero-conf_previous"
|
||
>
|
||
<a
|
||
href="#"
|
||
aria-controls="zero-conf"
|
||
data-dt-idx="0"
|
||
tabindex="0"
|
||
class="page-link"
|
||
@click.prevent="previousPage"
|
||
>上一页</a
|
||
>
|
||
</li>
|
||
<li
|
||
:class="[
|
||
'paginate_button',
|
||
'page-item',
|
||
item.index == currentPageIndex ? 'active' : '',
|
||
]"
|
||
v-for="(item, index) in pageBtn"
|
||
:key="index"
|
||
>
|
||
<a
|
||
href="#"
|
||
aria-controls="zero-conf"
|
||
:data-dt-idx="item.index"
|
||
tabindex="0"
|
||
class="page-link"
|
||
@click.prevent="currentPageIndex = item.index"
|
||
>{{ item.text }}</a
|
||
>
|
||
</li>
|
||
<li
|
||
:class="[
|
||
'paginate_button',
|
||
'page-item',
|
||
'next',
|
||
currentPageIndex == pageCount ? 'disabled' : '',
|
||
]"
|
||
id="zero-conf_next"
|
||
>
|
||
<a
|
||
href="#"
|
||
aria-controls="zero-conf"
|
||
data-dt-idx="7"
|
||
tabindex="0"
|
||
class="page-link"
|
||
@click.prevent="nextPage"
|
||
>下一页</a
|
||
>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
name: "DataTable",
|
||
props: {
|
||
title: {
|
||
type: String,
|
||
default: "标题",
|
||
},
|
||
headers: {
|
||
// 表头数组 [{ text: '名字', value: 'name'}, ...]
|
||
type: Array,
|
||
required: true,
|
||
},
|
||
rows: {
|
||
// 数据数组 [{ name: '佐藤爱理', position: '会计', office: '东京', age: 33, startDate: '2008/11/28', salary: '162,700 元' }, ...]
|
||
type: Array,
|
||
required: true,
|
||
},
|
||
dataCount: {
|
||
type: Number,
|
||
required: true,
|
||
},
|
||
},
|
||
data() {
|
||
return {
|
||
//当前页索引
|
||
currentPageIndex: 1,
|
||
//单页大小
|
||
pageSize: 10,
|
||
//总页数
|
||
pageCount: 0,
|
||
//分页按钮数据,[{text:'1',index:1},.....]
|
||
pageBtn: [],
|
||
//调用方法
|
||
callMethod:['GET','POST']
|
||
};
|
||
},
|
||
mounted() {},
|
||
methods: {
|
||
//下一页
|
||
nextPage() {
|
||
if (this.currentPageIndex < this.pageCount) this.currentPageIndex++;
|
||
},
|
||
//上一页
|
||
previousPage() {
|
||
if (this.currentPageIndex > 1) {
|
||
this.currentPageIndex--;
|
||
}
|
||
},
|
||
changePage(pageIndex) {
|
||
this.currentPageIndex == pageIndex;
|
||
},
|
||
//更新总页数
|
||
updatePageCount(newPageSize) {
|
||
this.pageCount = Math.floor(this.dataCount / newPageSize);
|
||
this.pageCount += this.dataCount % newPageSize == 0 ? 0 : 1;
|
||
},
|
||
//更新分页按钮数据
|
||
updatePageBtn(newPageIndex) {
|
||
this.pageBtn = [];
|
||
//初始化首页
|
||
this.pageBtn.push({ text: "1", index: 1 });
|
||
//总页数小于8时显示全部页码
|
||
if (this.pageCount <= 8) {
|
||
for (let i = 2; i < this.pageCount; i++) {
|
||
this.pageBtn.push({ text: `${i}`, index: i });
|
||
}
|
||
} else {
|
||
//当前页左侧显示2格页码,当显示最小页码距离首页中间间隔大于1时隐藏间隔页面
|
||
if (newPageIndex - 2 > 3) {
|
||
this.pageBtn.push({ text: "...", index: newPageIndex });
|
||
}
|
||
//渲染当前页码左右各两格页码
|
||
const numleft = newPageIndex - 2 < 2 ? newPageIndex - 2 : 2;
|
||
const numright =
|
||
newPageIndex + 2 < this.pageCount ? 2 : this.pageCount - newPageIndex - 1;
|
||
for (let i = newPageIndex - numleft; i <= newPageIndex + numright; i++) {
|
||
this.pageBtn.push({ text: `${i}`, index: i });
|
||
}
|
||
//当前页右侧显示2格页码,当显示最大页码距离尾页中间间隔大于1时隐藏间隔页面
|
||
if (newPageIndex + 2 < this.pageCount - 2) {
|
||
this.pageBtn.push({ text: "...", index: newPageIndex });
|
||
}
|
||
}
|
||
//渲染尾页
|
||
this.pageBtn.push({ text: `${this.pageCount}`, index: this.pageCount });
|
||
},
|
||
formatDateTime(str) {
|
||
if (!str) return "-";
|
||
|
||
const date = new Date(str);
|
||
if (isNaN(date)) return "-"; // 防止 Safari 报 Invalid Date
|
||
|
||
const pad = (n) => n.toString().padStart(2, "0");
|
||
|
||
const Y = date.getFullYear();
|
||
const M = pad(date.getMonth() + 1);
|
||
const D = pad(date.getDate());
|
||
const h = pad(date.getHours());
|
||
const m = pad(date.getMinutes());
|
||
const s = pad(date.getSeconds());
|
||
|
||
return `${Y}-${M}-${D} ${h}:${m}:${s}`;
|
||
},
|
||
modify(id) {
|
||
this.$emit("dataModify", id);
|
||
},
|
||
deleteData(id) {
|
||
this.$emit("dataDelete", id);
|
||
},
|
||
handleSearch() {},
|
||
handleAddDate() {
|
||
this.$emit("dateAdd", {});
|
||
},
|
||
handleBatchDelete() {},
|
||
},
|
||
watch: {
|
||
//监听分页大小变化
|
||
pageSize: {
|
||
handler(newVal) {
|
||
this.updatePageCount(newVal);
|
||
//单页显示数据量改变时重置当前索引,防止数据以及控件异常
|
||
this.currentPageIndex = 1;
|
||
this.$emit("pageChanged", { pageIndex: this.currentPageIndex, pageSize: newVal });
|
||
this.updatePageBtn(this.currentPageIndex);
|
||
},
|
||
immediate: true,
|
||
},
|
||
//监听页索引,用于切换数据
|
||
currentPageIndex: {
|
||
handler(newVal) {
|
||
this.$emit("pageChanged", { pageIndex: newVal, pageSize: this.pageSize });
|
||
this.updatePageBtn(newVal);
|
||
},
|
||
immediate: true,
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 可自定义样式 */
|
||
.card-title {
|
||
font-size: 1.8rem;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
</style>
|