<template>
|
<div class="app-container">
|
<div class="search_form">
|
<el-form :model="searchForm" :inline="true">
|
<el-form-item label="区域:">
|
<el-input v-model="searchForm.area" placeholder="请输入" clearable prefix-icon="Search" @keyup.enter="handleQuery" />
|
</el-form-item>
|
<el-form-item label="办事处:">
|
<el-input v-model="searchForm.office" placeholder="请输入" clearable prefix-icon="Search" @keyup.enter="handleQuery" />
|
</el-form-item>
|
<el-form-item label="驻省人员:">
|
<el-input v-model="searchForm.residentStaff" placeholder="请输入" clearable prefix-icon="Search" @keyup.enter="handleQuery" />
|
</el-form-item>
|
<el-form-item label="预警状态:">
|
<el-select v-model="searchForm.warnStatus" placeholder="请选择" clearable style="width: 140px">
|
<el-option label="正常" value="normal" />
|
<el-option label="即将到期" value="expiring" />
|
<el-option label="已逾期" value="overdue" />
|
</el-select>
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary" @click="handleQuery">搜索</el-button>
|
<el-button @click="resetSearch">重置</el-button>
|
</el-form-item>
|
</el-form>
|
</div>
|
|
<div class="table_list">
|
<div class="actions">
|
<div></div>
|
<div>
|
<el-button type="primary" @click="openForm('add')">新增宽带</el-button>
|
<el-button type="primary" plain @click="handleExport">导出</el-button>
|
<el-button type="danger" plain @click="handleDelete">删除</el-button>
|
</div>
|
</div>
|
<el-table
|
:data="tableData"
|
border
|
v-loading="tableLoading"
|
@selection-change="handleSelectionChange"
|
style="width: 100%"
|
height="calc(100vh - 18.5em)"
|
>
|
<el-table-column align="center" type="selection" width="55" fixed="left" />
|
<el-table-column align="center" label="序号" type="index" width="60" fixed="left" />
|
<el-table-column label="区域" prop="area" width="100" show-overflow-tooltip />
|
<el-table-column label="办事处" prop="office" width="120" show-overflow-tooltip />
|
<el-table-column label="驻省人员" prop="residentStaff" width="100" show-overflow-tooltip />
|
<el-table-column label="地址" prop="address" min-width="180" show-overflow-tooltip />
|
<el-table-column label="安装日期" prop="installDate" width="110" show-overflow-tooltip />
|
<el-table-column label="安装费(元)" prop="installFee" width="110" align="right" :formatter="formatAmountColumn" />
|
<el-table-column label="缴费周期" prop="paymentCycleText" width="90" align="center" />
|
<el-table-column label="缴费方式" prop="paymentMethod" width="100" align="center" />
|
<el-table-column label="预存(元)" prop="prepaidAmount" width="110" align="right" :formatter="formatAmountColumn" />
|
<el-table-column label="资费(元)" prop="monthlyFee" width="100" align="right" :formatter="formatAmountColumn" />
|
<el-table-column label="套餐内容" prop="packageContent" min-width="160" show-overflow-tooltip />
|
<el-table-column label="下次缴费日期" prop="nextPaymentDate" width="120" align="center">
|
<template #default="scope">
|
<span :class="{ 'warn-date': isExpiring(scope.row), 'overdue-date': isOverdue(scope.row) }">
|
{{ scope.row.nextPaymentDate || "-" }}
|
</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="预警状态" width="120" align="center">
|
<template #default="scope">
|
<el-tag :type="getWarnStatus(scope.row).type">{{ getWarnStatus(scope.row).text }}</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column fixed="right" label="操作" width="150" align="center">
|
<template #default="scope">
|
<el-button link type="primary" @click="openForm('view', scope.row)">详情</el-button>
|
<el-button link type="primary" @click="openForm('edit', scope.row)">编辑</el-button>
|
<el-button link type="danger" @click="handleRowDelete(scope.row)">删除</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
<pagination
|
v-show="total > 0"
|
:total="total"
|
layout="total, sizes, prev, pager, next, jumper"
|
:page="page.current"
|
:limit="page.size"
|
@pagination="paginationChange"
|
/>
|
</div>
|
|
<!-- 新增/编辑/详情弹窗 -->
|
<FormDialog
|
v-model="dialogVisible"
|
:title="
|
operationType === 'add'
|
? '新增宽带'
|
: operationType === 'edit'
|
? '编辑宽带'
|
: '宽带详情'
|
"
|
:width="'60%'"
|
:operation-type="operationType"
|
@close="closeDialog"
|
@confirm="submitForm"
|
@cancel="closeDialog"
|
>
|
<el-form :model="form" :rules="rules" ref="formRef" label-width="140px" label-position="top">
|
<el-divider content-position="left">基础信息</el-divider>
|
<el-row :gutter="30">
|
<el-col :span="8">
|
<el-form-item label="区域:" prop="area">
|
<el-input v-model="form.area" placeholder="请输入区域" clearable :disabled="operationType === 'view'" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="办事处:" prop="office">
|
<el-input v-model="form.office" placeholder="请输入办事处" clearable :disabled="operationType === 'view'" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="驻省人员:" prop="residentStaff">
|
<el-input v-model="form.residentStaff" placeholder="请输入驻省人员" clearable :disabled="operationType === 'view'" />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :gutter="30">
|
<el-col :span="16">
|
<el-form-item label="地址:" prop="address">
|
<el-input v-model="form.address" placeholder="请输入地址" clearable :disabled="operationType === 'view'" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="安装日期:" prop="installDate">
|
<el-date-picker
|
v-model="form.installDate"
|
value-format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
type="date"
|
placeholder="请选择"
|
clearable
|
style="width: 100%"
|
:disabled="operationType === 'view'"
|
@change="calcNextPaymentDate"
|
/>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-divider content-position="left">费用与缴费</el-divider>
|
<el-row :gutter="30">
|
<el-col :span="8">
|
<el-form-item label="安装费(元):">
|
<el-input-number v-model="form.installFee" :min="0" :precision="2" :step="0.01" controls-position="right" style="width: 100%" :disabled="operationType === 'view'" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="缴费周期:" prop="paymentCycle">
|
<el-select v-model="form.paymentCycle" placeholder="请选择" clearable style="width: 100%" :disabled="operationType === 'view'" @change="calcNextPaymentDate">
|
<el-option v-for="item in paymentCycleOptions" :key="item.value" :label="item.label" :value="item.value" />
|
</el-select>
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="缴费方式:" prop="paymentMethod">
|
<el-select v-model="form.paymentMethod" placeholder="请选择" clearable style="width: 100%" :disabled="operationType === 'view'">
|
<el-option v-for="item in paymentMethodOptions" :key="item.value" :label="item.label" :value="item.value" />
|
</el-select>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :gutter="30">
|
<el-col :span="8">
|
<el-form-item label="预存(元):">
|
<el-input-number v-model="form.prepaidAmount" :min="0" :precision="2" :step="0.01" controls-position="right" style="width: 100%" :disabled="operationType === 'view'" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="资费(元):">
|
<el-input-number v-model="form.monthlyFee" :min="0" :precision="2" :step="0.01" controls-position="right" style="width: 100%" :disabled="operationType === 'view'" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="下次缴费日期:" prop="nextPaymentDate">
|
<el-date-picker
|
v-model="form.nextPaymentDate"
|
value-format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
type="date"
|
placeholder="自动计算"
|
clearable
|
style="width: 100%"
|
:disabled="operationType === 'view'"
|
/>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-divider content-position="left">套餐信息</el-divider>
|
<el-row :gutter="30">
|
<el-col :span="24">
|
<el-form-item label="套餐内容:">
|
<el-input v-model="form.packageContent" type="textarea" :rows="3" placeholder="请输入套餐内容" :disabled="operationType === 'view'" />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</el-form>
|
</FormDialog>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, getCurrentInstance, onMounted } from "vue";
|
import { ElMessageBox } from "element-plus";
|
import dayjs from "dayjs";
|
import pagination from "@/components/PIMTable/Pagination.vue";
|
import FormDialog from "@/components/Dialog/FormDialog.vue";
|
import {
|
broadbandListPage,
|
getBroadbandDetail,
|
addOrUpdateBroadband,
|
delBroadband,
|
} from "@/api/collaborativeApproval/broadbandCommunication.js";
|
|
const { proxy } = getCurrentInstance();
|
|
// 缴费周期选项(月数)
|
const paymentCycleOptions = [
|
{ label: "月付", value: 1 },
|
{ label: "季付", value: 3 },
|
{ label: "半年付", value: 6 },
|
{ label: "年付", value: 12 },
|
];
|
|
// 缴费方式选项
|
const paymentMethodOptions = [
|
{ label: "现金", value: "现金" },
|
{ label: "银行转账", value: "银行转账" },
|
{ label: "支付宝", value: "支付宝" },
|
{ label: "微信", value: "微信" },
|
];
|
|
const tableData = ref([]);
|
const tableLoading = ref(false);
|
const total = ref(0);
|
const selectedRows = ref([]);
|
const page = reactive({ current: 1, size: 10 });
|
|
const searchForm = reactive({
|
area: "",
|
office: "",
|
residentStaff: "",
|
warnStatus: "",
|
});
|
|
const operationType = ref("");
|
const dialogVisible = ref(false);
|
const currentId = ref("");
|
const formRef = ref();
|
|
const form = reactive({
|
area: "",
|
office: "",
|
residentStaff: "",
|
address: "",
|
installDate: "",
|
installFee: undefined,
|
paymentCycle: undefined,
|
paymentMethod: "",
|
prepaidAmount: undefined,
|
monthlyFee: undefined,
|
packageContent: "",
|
nextPaymentDate: "",
|
});
|
|
const rules = {
|
area: [{ required: true, message: "请输入区域", trigger: "blur" }],
|
office: [{ required: true, message: "请输入办事处", trigger: "blur" }],
|
residentStaff: [{ required: true, message: "请输入驻省人员", trigger: "blur" }],
|
address: [{ required: true, message: "请输入地址", trigger: "blur" }],
|
installDate: [{ required: true, message: "请选择安装日期", trigger: "change" }],
|
paymentCycle: [{ required: true, message: "请选择缴费周期", trigger: "change" }],
|
paymentMethod: [{ required: true, message: "请选择缴费方式", trigger: "change" }],
|
};
|
|
const formatAmount = (val) => {
|
if (val === undefined || val === null || val === "") return "0.00";
|
const num = Number(val);
|
return Number.isNaN(num) ? "0.00" : num.toFixed(2);
|
};
|
const formatAmountColumn = (row, column, cellValue) => formatAmount(cellValue);
|
|
// 缴费周期文本映射
|
const getPaymentCycleText = (cycle) => {
|
const item = paymentCycleOptions.find((i) => Number(i.value) === Number(cycle));
|
return item ? item.label : cycle || "-";
|
};
|
|
// 下次缴费日期自动计算 = 安装日期 + 缴费周期
|
const calcNextPaymentDate = () => {
|
if (form.installDate && form.paymentCycle) {
|
form.nextPaymentDate = dayjs(form.installDate)
|
.add(Number(form.paymentCycle), "month")
|
.format("YYYY-MM-DD");
|
}
|
};
|
|
// 剩余天数(下次缴费日期 - 今天)
|
const getRemainingDays = (row) => {
|
if (!row.nextPaymentDate) return null;
|
return dayjs(row.nextPaymentDate).startOf("day").diff(dayjs().startOf("day"), "day");
|
};
|
|
const isOverdue = (row) => {
|
const days = getRemainingDays(row);
|
return days !== null && days < 0;
|
};
|
|
const isExpiring = (row) => {
|
const days = getRemainingDays(row);
|
return days !== null && days >= 0 && days <= 15;
|
};
|
|
// 预警状态:正常 / 即将到期(15天内)/ 已逾期
|
const getWarnStatus = (row) => {
|
const days = getRemainingDays(row);
|
if (days === null) return { text: "-", type: "info" };
|
if (days < 0) return { text: `已逾期${Math.abs(days)}天`, type: "danger" };
|
if (days <= 15) return { text: `即将到期(${days}天)`, type: "warning" };
|
return { text: "正常", type: "success" };
|
};
|
|
const resetSearch = () => {
|
searchForm.area = "";
|
searchForm.office = "";
|
searchForm.residentStaff = "";
|
searchForm.warnStatus = "";
|
page.current = 1;
|
getList();
|
};
|
|
const handleQuery = () => {
|
page.current = 1;
|
getList();
|
};
|
|
const paginationChange = (obj) => {
|
page.current = obj.page;
|
page.size = obj.limit;
|
getList();
|
};
|
|
const getList = () => {
|
tableLoading.value = true;
|
const params = { ...searchForm, current: page.current, size: page.size };
|
return broadbandListPage(params)
|
.then((res) => {
|
tableLoading.value = false;
|
const list = res.records || res.data?.records || [];
|
// 前端计算缴费周期文本,供列表展示
|
tableData.value = list.map((item) => ({
|
...item,
|
paymentCycleText: getPaymentCycleText(item.paymentCycle),
|
}));
|
total.value = res.total ?? res.data?.total ?? 0;
|
})
|
.catch(() => {
|
tableLoading.value = false;
|
});
|
};
|
|
const handleSelectionChange = (selection) => {
|
selectedRows.value = selection;
|
};
|
|
const resetFormData = () => {
|
Object.keys(form).forEach((key) => {
|
form[key] = "";
|
});
|
form.installFee = undefined;
|
form.paymentCycle = undefined;
|
form.prepaidAmount = undefined;
|
form.monthlyFee = undefined;
|
};
|
|
const openForm = (type, row) => {
|
operationType.value = type;
|
resetFormData();
|
if (type === "add") {
|
dialogVisible.value = true;
|
} else {
|
currentId.value = row.id;
|
getBroadbandDetail({ id: row.id }).then((res) => {
|
const detail = res.data || res;
|
Object.keys(form).forEach((key) => {
|
if (detail[key] !== undefined && detail[key] !== null) {
|
form[key] = detail[key];
|
}
|
});
|
dialogVisible.value = true;
|
});
|
}
|
};
|
|
const submitForm = () => {
|
proxy.$refs["formRef"].validate((valid) => {
|
if (!valid) return;
|
addOrUpdateBroadband(form).then(() => {
|
proxy.$modal.msgSuccess("保存成功");
|
closeDialog();
|
getList();
|
});
|
});
|
};
|
|
const closeDialog = () => {
|
proxy.resetForm("formRef");
|
dialogVisible.value = false;
|
};
|
|
const handleDelete = () => {
|
if (selectedRows.value.length === 0) {
|
proxy.$modal.msgWarning("请选择数据");
|
return;
|
}
|
const ids = selectedRows.value.map((item) => item.id);
|
ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "提示", {
|
confirmButtonText: "确认",
|
cancelButtonText: "取消",
|
type: "warning",
|
})
|
.then(() => {
|
delBroadband(ids).then(() => {
|
proxy.$modal.msgSuccess("删除成功");
|
getList();
|
});
|
})
|
.catch(() => {});
|
};
|
|
const handleRowDelete = (row) => {
|
ElMessageBox.confirm("该宽带记录将被删除,是否确认删除?", "提示", {
|
confirmButtonText: "确认",
|
cancelButtonText: "取消",
|
type: "warning",
|
})
|
.then(() => {
|
delBroadband([row.id]).then(() => {
|
proxy.$modal.msgSuccess("删除成功");
|
getList();
|
});
|
})
|
.catch(() => {});
|
};
|
|
const handleExport = () => {
|
proxy.download("/broadband/export", { ...searchForm }, "宽带与通信管理.xlsx");
|
};
|
|
onMounted(() => {
|
getList();
|
});
|
</script>
|
|
<style scoped lang="scss">
|
.search_form {
|
margin-bottom: 10px;
|
}
|
|
.actions {
|
display: flex;
|
justify-content: space-between;
|
margin-bottom: 10px;
|
}
|
|
.warn-date {
|
color: #e6a23c;
|
font-weight: 600;
|
}
|
|
.overdue-date {
|
color: #f56c6c;
|
font-weight: 600;
|
}
|
</style>
|