初始版本,目前线上可用

This commit is contained in:
2025-11-19 12:49:16 +08:00
commit cb7f1c45e8
178 changed files with 30336 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
<template>
<div ref="echartDom" style="width: 100%; height: 100%"></div>
</template>
<script lang="ts" setup>
import * as echarts from "echarts";
import { ref, onMounted, onBeforeUnmount, nextTick } from "vue";
let echartDom = ref(<HTMLElement | null>null);
let emits = defineEmits(["update"]);
let props = defineProps({
option: {
type: Object,
default: () => {
return {
title: {
text: "ECharts 入门示例",
},
tooltip: {},
xAxis: {
data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"],
},
yAxis: {},
series: [
{
name: "销量",
type: "bar",
data: [5, 20, 36, 10, 10, 20],
},
],
};
},
},
});
function echartResize() {
let chartInstance = echarts.getInstanceByDom(echartDom.value as HTMLElement);
chartInstance?.resize();
}
let resizeObserver = new ResizeObserver(() => {
echartResize();
});
onMounted(() => {
let chartInstance = echarts.init(echartDom.value);
chartInstance.setOption(props.option);
resizeObserver.observe(echartDom.value as HTMLElement);
});
onBeforeUnmount(() => {
resizeObserver.disconnect();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,34 @@
<template>
<baseDialog v-bind="$attrs">
<slot name="content"> </slot>
<slot></slot>
<template #footer>
<div class="flex-center">
<el-button @click="emits('addConfim')" v-if="showPageType.add">{{
confirmText || "确定新增"
}}</el-button>
<el-button
type="primary"
@click="emits('editConfim')"
v-if="showPageType.edit"
>确认修改</el-button
>
<slot name="footer"></slot>
<el-button type="danger" @click="emits('closeConfirm')">关闭</el-button>
</div>
</template>
</baseDialog>
</template>
<script lang="ts" setup>
import { onMounted } from "vue";
const props = defineProps<{
/**
* 根据增删改查的字段控制按钮的显示和隐藏
*/
showPageType: { [key: string]: boolean };
confirmText?: string;
}>();
const emits = defineEmits(["addConfim", "editConfim", "closeConfirm"]);
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,76 @@
import { ref, reactive, watch } from "vue"
interface titleDict {
add: string;
edit: string;
view: string;
}
class pageVisible {
/**
* 需要操作的页面类型
*/
executeType = ref('');
/**
* 实际渲染的二级页面或者dialog的标题
*/
dialogTitle = ref("");
/**
* 是否显示二级页面或者dialog
*/
show2LevelPage = ref(false); // 是否显示二级页面或者dialog
/**
* 标题的字典,用于在控制页面显示的时候映射对应的标题
*/
dialogTitleDict = ref<{ [key: string]: any }>({});
/**
* 显示页面的类型
*/
showPageType = reactive({
add: false,
edit: false,
view: false,
});
/**
* @param dialogTitleDict 传入一个对象有三个字段分别是add、edit和view页面的标题<br>
* { <br>
* add: '添加页面的标题',<br>
* view: '查看页面的标题',<br>
* edit: '编辑页面的标题'<br>
* }
*/
constructor(dialogTitleDict: titleDict) {
this.dialogTitleDict.value = dialogTitleDict;
// 根据展示的类型更换标题
watch(
() => this.showPageType,
(newVal) => {
for (let [key, value] of Object.entries(newVal)) {
if (value) {
this.show2LevelPage.value = true;
this.dialogTitle.value = this.dialogTitleDict.value[key];
if(['edit', 'add'].includes(key)) this.executeType.value = key;
break;
}
}
},
{ deep: true }
);
// 页面关闭,展示的类型全部不显示
watch(
() => this.show2LevelPage,
(newVal) => {
if (!newVal.value) {
this.showPageType.add = false;
this.showPageType.edit = false;
this.showPageType.view = false;
}
},
{ deep: true }
);
}
}
export default pageVisible;

View File

@@ -0,0 +1,17 @@
<template>
<el-dialog
v-bind="$attrs"
center
class="base-dialog"
width="70%"
align-center
destroy-on-close
:close-on-click-modal="false">
<slot name="header"></slot>
<slot name="default"></slot>
<slot name="footer"></slot>
</el-dialog>
</template>
<script lang="ts" setup></script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,30 @@
<template>
<div>
<el-select
v-model="guide"
style="width: 150px"
filterable
placeholder="请选择引导员">
<el-option
v-for="item in guideOptions"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</div>
</template>
<script lang="ts" setup>
import request from "@/lib/request";
import { ref } from "vue";
let guide = defineModel({ default: "" });
const guideOptions = ref<guideOption[]>([]);
request()
.get("public/guide")
.then((res) => {
guideOptions.value = res.data;
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,30 @@
<template>
<div>
<base-line-title :title="title"></base-line-title>
<div class="infor-card-content">
<slot name="content"></slot><slot></slot>
</div>
</div>
</template>
<script lang="ts" setup>
const props = defineProps({
title: {
type: String,
default: "",
},
});
const title = props.title;
</script>
<style lang="scss" scoped>
.infor-card-content {
padding: 15px;
}
:deep(.base-line-title) {
font-size: 1.1rem;
font-weight: bold;
background-color: #f1f3f4;
border-bottom: none;
}
</style>

View File

@@ -0,0 +1,43 @@
<template>
<div class="base-line-title">
<span class="left-border"></span>
{{ props.title }}
</div>
</template>
<script lang="ts" setup>
import { reactive, onMounted, ref } from "vue";
let props = defineProps({
title: {
type: String,
default: "",
},
});
</script>
<style lang="scss" scoped>
.base-line-title {
font-size: 1.5rem;
display: flex;
align-items: center;
height: 1.8rem;
.left-border {
background-color: var(--el-color-primary);
display: inline-block;
height: 100%;
width: 4px;
margin-right: 10px;
border-radius: 12px;
animation: leftBorder 0.6s ease-in;
animation-fill-mode: forwards;
// animation-delay: 1s;
}
}
@keyframes leftBorder{
0% {
height: 0;
}
100%{
height: 100%;
}
}
</style>

View File

@@ -0,0 +1,304 @@
<!-- PrintTemplate.vue -->
<template>
<div>
<base-dialog v-model="showPrint" title="打印预览" wdith="210mm">
<div v-loading="showLoading" element-loading-text="正在准备打印数据...">
<div ref="printContainer" class="print-container" id="print-container">
<!-- 告知书部分 -->
<div class="title">文明节俭治丧告知书</div>
<div class="content">尊敬的服务对象</div>
<div class="content" style="text-indent: 7rem">
您好感谢您的信任选择到我司办理治丧业务为深化殡葬改革推进移风易俗现就推行文明节俭治丧有关事宜向您倡导如下
</div>
<div
class="content"
style="text-indent: 5rem; padding-right: 15px"
id="first-content">
文明治丧节俭办事革除陈规陋习抵制封建迷信提倡厚养薄葬弘扬精神美德
</div>
<div class="content" style="text-indent: 5rem; padding-right: 15px">
我司为满足个性化需求提供优质的非基本服务和丧葬用品请您在自主选择时杜绝铺张浪费树立文明新风
</div>
<!-- 服务清单表格 -->
<div class="title">殡仪服务项目清单</div>
<table class="service-table service-table-header">
<tr>
<td style="width: 60pt">逝者姓名</td>
<td style="width: 90pt">
{{ deceased.name || deceased.deceased?.name }}
</td>
<td style="width: 60pt">逝者年龄</td>
<td style="width: 70pt">
{{ deceased.age ?? deceased.deceased?.age }}
</td>
<td style="width: 50pt">购买人</td>
<td style="width: 80pt">
{{ deceased.familyName || deceased.deceased?.familyName }}
</td>
<td style="width: 80pt">购买人电话</td>
<td>
{{ deceased.familyPhone ?? deceased.deceased?.familyPhone }}
</td>
</tr>
</table>
<table class="service-table">
<thead>
<tr>
<th style="width: 40pt">序号</th>
<th style="width: 80pt">服务项目</th>
<th style="width: 35pt">单位</th>
<th style="width: 35pt">数量</th>
<th style="width: 60pt">收费标准</th>
<th style="width: 60pt">小计</th>
<th colspan="3">备注</th>
</tr>
</thead>
<tr v-for="(item, index) in services" :key="index">
<td>{{ index + 1 }}</td>
<td>{{ item.name }}</td>
<td>{{ item.unit }}</td>
<td>{{ item.quantity }}</td>
<td>{{ item.price }}</td>
<td>{{ (item.quantity * item.price).toFixed(2) }}</td>
<td colspan="3">{{ item.remark }}</td>
</tr>
<tr>
<td colspan="5">总计{{ total }}</td>
<td colspan="2">服务引导员</td>
<td style="width: 125pt">
{{ deceased.guide || deceased.deceased?.guide }}
</td>
<td style="width: 125px">家属签字</td>
</tr>
</table>
<table class="service-table service-table-footer">
<tr>
<td style="width: 60pt">消费确认</td>
<td>
<div>
<span
class="confirm-text no-border"
v-for="text in confirmText">
{{ text }}
</span>
</div>
<div>
<span class="confirm-text" v-for="text in confirmText2">
{{ text }}
</span>
<span class="confirm-text"></span>
<span
class="confirm-text"
style="position: relative; top: 5px"></span>
<span
class="confirm-text"
style="position: relative; top: 5px"></span>
<span
class="confirm-text"
style="position: relative; top: 5px"></span>
<span class="confirm-text"></span>
</div>
</td>
<td style="width: 125px"></td>
</tr>
</table>
<div style="width: 100%; text-align: right">
服务时间{{ deceased.retail.checkoutDate.split(" ")[0] }}
</div>
<!-- 打印按钮 -->
</div>
<button class="print-btn" v-print="print">打印单据</button>
</div>
</base-dialog>
</div>
</template>
<script setup lang="ts">
import { defaultRetail } from "@/defaultForm/defaultRetail";
import { computed, nextTick, onMounted, ref, watch } from "vue";
import api from "@/lib/request";
let printContainer = ref();
let showLoading = ref(true);
const print = {
id: "print-container",
beforeOpenCallback: () => {
showLoading.value = true;
},
closeCallback() {
showLoading.value = false;
},
};
let services = ref([]);
let total = computed(() => {
const sum = services.value.reduce((sum, item) => {
return sum + parseFloat(parseFloat(item.price).toFixed(2)) * item.quantity;
}, 0);
return parseFloat(sum.toFixed(2)).toFixed(2); // 最终结果保留两位小数
});
let confirmText = "以上消费项目我已经认真审核,无异议。";
let confirmText2 = "以上消费项目我已经认真审核";
let props = withDefaults(
defineProps<{
deceased: RegisForm;
serviceUrl: string;
}>(),
{
deceased: () => defaultRetail(),
serviceUrl: "/deceased-retail/selected-service",
}
);
let showPrint = defineModel();
watch(
() => showPrint.value,
() => {
showLoading.value = true;
api()
.get(props.serviceUrl)
.then((res) => {
if (res.code === 200) {
showLoading.value = false;
services.value = res.data.list;
}
});
},
{
deep: true,
immediate: true,
}
);
</script>
<style scoped lang="scss">
.service-table-header {
tr td {
border-bottom: none !important;
page-break-inside: avoid;
}
margin-bottom: -10pt !important;
}
.service-table-footer {
tr td {
border-top: none !important;
page-break-inside: avoid;
}
margin-top: -10pt !important;
}
/* 基础样式 */
.print-container {
width: 230mm;
margin: 0 auto;
font-size: 14pt;
color: #000 !important;
font-family: Microsoft YaHei, "SimSun", serif !important;
padding: 0 5mm;
box-sizing: border-box; /* 包含边框计算 */
size: auto;
/* 移除可能引起居中的属性 */
display: block !important;
align-items: unset !important;
justify-content: unset !important;
}
.title {
font-size: 20pt;
text-align: center;
margin-top: 5pt;
font-weight: bold;
}
.service-table {
width: 100%;
border-collapse: collapse;
margin: 10pt 0;
td,
th {
border: 1pt solid #000; // 使用pt单位
padding: 0.8mm 1.2mm; // 使用mm单位
font-size: 10.5pt;
line-height: 1.5;
text-align: center;
}
}
.total {
font-weight: bold;
margin: 20px 0;
text-align: right;
}
.print-btn {
display: block;
width: 120px;
margin: 30px auto;
padding: 10px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
/* 打印媒体查询 */
@media print {
/* 重置body和html的布局方式 */
body,
html {
display: block !important;
height: auto !important;
margin: 0 !important;
padding: 0 !important;
}
/* 确保打印容器从顶部开始 */
.print-container {
min-height: 297mm; /* A4纸高度 */
display: block !important;
vertical-align: top !important;
position: relative;
top: 0;
transform: none !important;
}
@page {
padding: 0 !important;
margin: 0 !important;
}
/* 移除可能存在的flex布局影响 */
.el-dialog__wrapper,
.el-dialog,
.el-dialog__body {
display: block !important;
height: auto !important;
}
/* 移除对话框的padding */
:deep(.el-dialog__body) {
padding: 0 !important;
}
}
.confirm-text {
border: 1px solid #000;
margin: 0 3px;
display: inline-block;
width: 20px;
height: 20px;
line-height: 18px;
}
.no-border {
border: 1px solid #fff;
}
:deep(.base-dialog .el-dialog__header) {
padding-bottom: none;
}
</style>

View File

@@ -0,0 +1,272 @@
<!-- PrintTemplate.vue -->
<template>
<div>
<base-dialog v-model="showPrint" title="打印预览" wdith="210mm">
<div v-loading="showLoading" element-loading-text="正在准备打印数据...">
<div ref="printContainer" class="print-container" id="print-container">
<!-- 服务清单表格 -->
<div class="title">零售清单</div>
<table class="service-table service-table-header">
<tr>
<td style="width: 60pt">逝者姓名</td>
<td style="width: 90pt">
{{
deceased.name ||
deceased.deceased?.name ||
deceased.deceasedName
}}
</td>
<td style="width: 60pt">逝者年龄</td>
<td style="width: 70pt">
{{ deceased.age ?? deceased.deceased?.age }}
</td>
<td style="width: 50pt">购买人</td>
<td style="width: 80pt">
{{ deceased.familyName ?? deceased.deceased?.familyName }}
</td>
<td style="width: 80pt">购买人电话</td>
<td>
{{ deceased.familyPhone ?? deceased.deceased?.familyPhone }}
</td>
</tr>
</table>
<table class="service-table">
<thead>
<tr>
<th style="width: 40pt">序号</th>
<th style="width: 80pt">服务项目</th>
<th style="width: 35pt">单位</th>
<th style="width: 35pt">数量</th>
<th style="width: 60pt">收费标准</th>
<th style="width: 60pt">小计</th>
<th colspan="3">备注</th>
</tr>
</thead>
<tr v-for="(item, index) in services" :key="index">
<td>{{ index + 1 }}</td>
<td>{{ item.name }}</td>
<td>{{ item.unit }}</td>
<td>{{ item.quantity }}</td>
<td>{{ item.price }}</td>
<td>{{ (item.quantity * item.price).toFixed(2) }}</td>
<td colspan="3">{{ item.remark }}</td>
</tr>
<tr>
<td colspan="6">总计{{ total }}</td>
<td colspan="2">服务引导员</td>
<td style="width: 125pt">
{{ deceased.guide || deceased.deceased?.guide }}
</td>
</tr>
</table>
<div
style="width: 100%; text-align: right"
v-if="deceased.checkoutDate || deceased.retail">
服务时间{{
deceased.retail
? deceased.retail?.checkoutDate.split(" ")[0]
: deceased.checkoutDate.split(" ")[0]
}}
</div>
<!-- 打印按钮 -->
</div>
<button class="print-btn" v-print="print">打印单据</button>
</div>
</base-dialog>
</div>
</template>
<script setup lang="ts">
import { defaultRetail } from "@/defaultForm/defaultRetail";
import { computed, nextTick, onMounted, ref, watch } from "vue";
import api from "@/lib/request";
let printContainer = ref();
let showLoading = ref(true);
const print = {
printStyle: `
@page { size: auto; margin: 0;padding: 0;}
table { border-collapse: collapse; }
td { padding: 3mm; border: 1px solid #666; }
.title {
margin: 5pt 0;
}
`,
id: "print-container",
beforeOpenCallback: () => {
showLoading.value = true;
},
closeCallback() {
showLoading.value = false;
},
};
let services = ref([]);
let total = computed(() => {
const sum = services.value.reduce((sum, item) => {
return sum + parseFloat(parseFloat(item.price).toFixed(2)) * item.quantity;
}, 0);
return parseFloat(sum.toFixed(2)).toFixed(2); // 最终结果保留两位小数
});
let props = withDefaults(
defineProps<{
deceased: RegisForm;
serviceUrl: string;
}>(),
{
deceased: () => defaultRetail(),
serviceUrl: "/deceased-retail/selected-service",
}
);
let showPrint = defineModel();
watch(
() => showPrint.value,
() => {
showLoading.value = true;
api()
.get(props.serviceUrl)
.then((res) => {
if (res.code === 200) {
showLoading.value = false;
services.value = res.data.list;
console.log(props.deceased);
}
});
},
{
deep: true,
immediate: true,
}
);
</script>
<style scoped lang="scss">
.service-table-header {
tr td {
border-bottom: none !important;
page-break-inside: avoid;
}
margin-bottom: -10pt !important;
}
.service-table-footer {
tr td {
border-top: none !important;
page-break-inside: avoid;
}
margin-top: -10pt !important;
}
/* 基础样式 */
.print-container {
width: 230mm;
margin: 0 auto;
font-size: 14pt;
color: #000 !important;
font-family: Microsoft YaHei, "SimSun", serif !important;
padding: 0 5mm;
box-sizing: border-box; /* 包含边框计算 */
size: auto;
/* 移除可能引起居中的属性 */
display: block !important;
align-items: unset !important;
justify-content: unset !important;
}
.title {
font-size: 20pt;
text-align: center;
margin-top: 5pt;
font-weight: bold;
}
.service-table {
width: 100%;
border-collapse: collapse;
margin: 10pt 0;
td,
th {
border: 1pt solid #000; // 使用pt单位
padding: 0.8mm 1.2mm; // 使用mm单位
font-size: 10.5pt;
line-height: 1.5;
text-align: center;
}
}
.total {
font-weight: bold;
margin: 20px 0;
text-align: right;
}
.print-btn {
display: block;
width: 120px;
margin: 30px auto;
padding: 10px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
/* 打印媒体查询 */
@media print {
/* 重置body和html的布局方式 */
body,
html {
display: block !important;
height: auto !important;
margin: 0 !important;
padding: 0 !important;
}
/* 确保打印容器从顶部开始 */
.print-container {
min-height: 297mm; /* A4纸高度 */
display: block !important;
vertical-align: top !important;
position: relative;
top: 0;
transform: none !important;
}
@page {
padding: 0 !important;
margin: 0 !important;
}
/* 移除可能存在的flex布局影响 */
.el-dialog__wrapper,
.el-dialog,
.el-dialog__body {
display: block !important;
height: auto !important;
}
/* 移除对话框的padding */
:deep(.el-dialog__body) {
padding: 0 !important;
}
}
.confirm-text {
border: 1px solid #000;
margin: 0 3px;
display: inline-block;
width: 20px;
height: 20px;
line-height: 18px;
}
.no-border {
border: 1px solid #fff;
}
:deep(.base-dialog .el-dialog__header) {
padding-bottom: none;
}
</style>

View File

@@ -0,0 +1,69 @@
<template>
<div>
<base-dialog v-model="show" :title="props.title || '零售清单'">
<base-table
:option="props.option"
border
:showToolsBar="false"
@tableUpdate="tableUpdate">
<template #toolsBar>
总金额
<span style="font-size: 18px; font-weight: bold"
>{{ totalNum }}</span
>
</template>
<template #colunm>
<el-table-column
type="index"
label="序号"
width="80"
align="center" />
<el-table-column
prop="name"
label="名称"
width="150"
align="center" />
<el-table-column prop="unit" label="单位" width="60" align="center" />
<el-table-column
prop="quantity"
label="数量"
width="80"
align="center" />
<el-table-column prop="price" label="金额" width="120" align="center">
<template #default="{ row }"> {{ row.price }} </template>
</el-table-column>
<el-table-column prop="remark" label="备注" align="center" />
</template>
</base-table>
</base-dialog>
</div>
</template>
<script lang="ts" setup>
import { tableOptionType } from "@/types/table";
import { computed, ref } from "vue";
let show = defineModel({ default: false });
let props = defineProps<{
option: tableOptionType;
title?: string;
}>();
let tableData = ref([]);
let totalNum = computed(() => {
let value = 0.0;
if (tableData.value.length) {
tableData.value.forEach((item) =>
(value = value + Number(item.price).toFixed(2) * item.quantity).toFixed(2)
);
}
return value.toFixed(2);
});
function tableUpdate(data) {
tableData.value = data;
}
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,42 @@
<template>
<div>
<el-tree-select
check-strictly
:check-on-click-node="true"
v-model="data"
:props="{ label: 'name', value: 'id' }"
:data="categoryOptions"
:render-after-expand="false"
style="width: 240px" />
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from "vue";
import api from "@/lib/request";
import { ElMessage } from "element-plus";
const data = defineModel({
default: 0,
});
const categoryOptions = ref<{ id: number; name: string }[]>([]);
const loadCategoryTree = async () => {
try {
const res = await api().get("/service-category/list");
if (res.data.list.length) {
categoryOptions.value = [{ id: 0, name: "无" }, ...res.data.list];
} else {
categoryOptions.value = [{ id: 0, name: "无" }];
}
} catch (err) {
ElMessage.error("获取分类树失败");
}
};
// 页面加载时获取分类树
onMounted(() => {
loadCategoryTree();
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,238 @@
<template>
<!-- <el-affix :offset="65" v-if="props.showToolsBar">
</el-affix> -->
<BaseToolBar
@refresh="refresh"
style="background-color: #fff; padding: 15px 0; margin-top: -15px">
<!-- <download-excel :data="tableData.data" :fields="fields">
<el-button size="small" style="margin-left: 15px"
><template #icon>
<el-tooltip placement="top" effect="light">
<template #content>导出当前</template>
<el-icon>
<Download />
</el-icon>
</el-tooltip>
</template>
</el-button>
</download-excel>
<download-excel :fetch="requestAllData" :fields="fields">
<el-tooltip placement="top" effect="light">
<template #content>导出全部</template>
<el-button size="small" style="margin-left: 15px"
><template #icon
><i class="base-system base-system-rizhixiazai-xiazaisuoyou"></i>
</template>
</el-button> </el-tooltip
></download-excel> -->
<div style="margin-left: 15px"><slot name="toolsBar"></slot></div>
<template #rightContent>
<el-pagination
background
layout="prev, pager, next, jumper, ->, total"
v-if="showPagination"
:page-size="tableData.pageSize"
:total="tableData.total"
:current-page="tableData.pageNumber"
size="small"
@current-change="currentPageChange" />
</template>
</BaseToolBar>
<el-table
ref="dataTable"
:data="tableData.data"
style="
width: 100%;
min-height: 130px;
padding-top: 15px;
padding-bottom: 20px;
"
v-loading="tableLoadingData.show"
empty-text="暂无数据"
highlight-current-row
:element-loading-text="tableLoadingData.text"
v-bind="$attrs">
<slot></slot>
<slot name="colunm"></slot>
</el-table>
</template>
<script lang="ts" setup>
import {
reactive,
ref,
onMounted,
watch,
nextTick,
onBeforeUnmount,
} from "vue";
import api from "@/lib/request";
import { ElMessage } from "element-plus";
import BaseToolBar from "./toolBar/baseToolBar.vue";
import { tableOptionType, tableDataType } from "@/types/table";
let tableLoadingData = reactive({
show: false,
text: "正在加载数据...",
});
const tableData = ref<tableDataType>({
data: [],
total: 0,
pageNumber: 1,
pageSize: 10,
});
const dataTable = ref<any>(null);
const executeType = ref("");
const methods = {
async setDataType(type: "reset" | "list" | "search") {
nextTick(async () => {
if (executeType.value !== type) {
tableData.value.pageNumber = 1;
tableData.value.pageSize = 10;
}
executeType.value = type;
if (type === "list") await initData();
if (type === "search") await queryData();
if (type === "reset") {
tableData.value.pageNumber = 1;
tableData.value.pageSize = 10;
await initData();
}
emits("update");
});
},
};
defineExpose({ methods, tableData });
const props = withDefaults(
defineProps<{
option: tableOptionType;
showPagination?: boolean;
showToolsBar?: boolean;
resize?: boolean;
fields?: string[];
}>(),
{
option: () => {
return {
url: "",
searchParams: () => {
return {};
},
searchUrl: "",
executeType: "list",
};
},
showToolsBar: true,
showPagination: true,
resize: true,
}
);
const emits = defineEmits(["tableUpdate", "searchUpdate", "update"]);
let tableHeight = ref(300);
const updateTableHeight = () => {
// 计算高度,减去 header 和分页器的高度
const headerHeight = 55; // 你的标题栏高度
const toolBarHeight = 147; // 你的分页器高度
const padding = 150; // 额外的间距
tableHeight.value =
window.innerHeight - headerHeight - toolBarHeight - padding;
};
// watch(
// () => tableData.value.pageNumber,
// () => {
// nextTick(() => {
// methods.setDataType(executeType.value);
// });
// }
// );
// watch(
// () => tableData.value.pageSize,
// () => {
// nextTick(() => {
// methods.setDataType(executeType.value);
// });
// }
// );
onBeforeUnmount(() => {
window.removeEventListener("resize", updateTableHeight);
});
onMounted(async () => {
await initData();
updateTableHeight();
window.addEventListener("resize", updateTableHeight);
});
async function initData() {
tableLoadingData.show = true;
let dataList = await api().get(props.option.url, {
params: {
pageSize: tableData.value.pageSize,
pageNumber: tableData.value.pageNumber,
},
});
if (dataList.code === 200) {
tableData.value.data = dataList.data.list;
tableData.value.total = dataList.data.total;
emits("tableUpdate", tableData.value.data);
}
tableLoadingData.show = false;
}
async function queryData() {
tableLoadingData.show = true;
let { pageNumber, pageSize } = tableData.value;
let dataList = await api().post(props.option.searchUrl, {
...props.option.searchParams,
pageSize,
pageNumber,
});
if (dataList.code === 200) {
tableData.value.data = dataList.data.list;
tableData.value.total = dataList.data.total;
emits("searchUpdate", tableData.value.data);
} else {
ElMessage.error("查询出错了,请稍后重试!");
}
tableLoadingData.show = false;
}
function currentPageChange(newVal: number) {
tableData.value.pageNumber = newVal;
if (executeType.value === "search") queryData();
if (["list", "reset"].includes(executeType.value)) initData();
if (!executeType.value) initData();
}
async function requestAllData() {
let dataList = await api().get(props.option.url, {
params: {
pageSize: 999999,
pageNumber: 1,
...props.option.searchParams,
},
});
return dataList.data.list;
}
function refresh() {
if (executeType.value === "search") queryData();
if (["list", "reset"].includes(executeType.value)) initData();
if (!executeType.value) queryData();
}
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,60 @@
<template>
<el-row class="table-header">
<el-col :span="24">
<el-card>
<template #header>
<base-line-title :title="title"></base-line-title>
</template>
<slot name="content"></slot>
<el-form-item :label-width="labelWidth + 'px'">
<el-button
@click="emits('search')"
size="small"
style="margin-left: 15px">
<template #icon>
<el-icon>
<Search />
</el-icon> </template
>查询</el-button
>
<el-button type="warning" @click="emits('resetSearch')" size="small">
<template #icon>
<el-icon>
<RefreshRight />
</el-icon> </template
>重置</el-button
>
<slot name="operateBtns"></slot>
</el-form-item>
</el-card>
</el-col>
</el-row>
</template>
<script lang="ts" setup>
import { reactive, onMounted, ref } from "vue";
let props = defineProps<{
title: string;
labelWidth?: number | string;
}>();
let emits = defineEmits(["resetSearch", "search"]);
function resetSearch() {
emits("resetSearch");
}
function search() {
emits("search");
}
</script>
<style lang="scss" scoped>
.table-header {
width: 100%;
}
:deep(.el-card__body) {
padding-bottom: 10px;
display: flex;
flex-wrap: wrap;
}
</style>

View File

@@ -0,0 +1,26 @@
<template>
<el-row>
<el-col :span="24" class="tool-bar">
<el-tooltip placement="top" effect="light">
<template #content>刷新</template>
<el-button size="small" @click="emits('refresh')">
<el-icon><Refresh /></el-icon>
</el-button>
</el-tooltip>
<slot></slot>
<div style="position: absolute; right: 15px">
<slot name="rightContent"></slot>
</div>
</el-col>
</el-row>
</template>
<script lang="ts" setup>
const emits = defineEmits(["refresh", "downCurrent", "downAll"]);
</script>
<style lang="scss" scoped>
.tool-bar {
display: flex;
align-items: center;
}
</style>