| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- <style scoped lang="scss">
- .system-container {
- :deep(.el-card__body) {
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- flex: 1;
- overflow: auto;
- .el-table {
- flex: 1;
- }
- }
- }
- .page-content {
- margin-bottom: 20px;
- }
- .page-pager {
- background-color: var(--el-color-white);
- height: 24px;
- }
- .qrcode-img {
- width: 260px;
- height: 260px;
- margin: 20px auto;
- display: block;
- border: 1px solid #eee;
- }
- .qrcode-tip {
- text-align: center;
- color: #999;
- font-size: 13px;
- margin-top: 10px;
- }
- .fault-tag {
- margin-right: 8px;
- }
- </style>
- <template>
- <div class="system-container layout-padding">
- <el-card shadow="hover" class="layout-padding-auto">
- <el-tabs v-model="state.activeTab" @tab-click="handleTabClick">
- <!-- ==================== 订阅管理 ==================== -->
- <el-tab-pane label="订阅管理" name="subscriber">
- <el-form :model="state.formQuery" ref="queryRef" size="default" label-width="0px" class="mt5 mb5">
- <ext-select
- v-model="state.formQuery.stationId"
- placeholder="请选择站点"
- url="washStation/list"
- url-method="post"
- label-key="stationName"
- value-key="stationId"
- data-key="list"
- clearable
- class="wd200"
- @change="loadSubscribers(true)"/>
- <el-button class="ml10" plain size="default" type="success" @click="loadSubscribers(true)">
- <SvgIcon name="ele-Search"/>
- 查询
- </el-button>
- <el-button v-if="state.formQuery.stationId" class="ml10" plain size="default" type="primary" @click="handleGenerateQrcode">
- <SvgIcon name="ele-Picture"/>
- 生成绑定二维码
- </el-button>
- </el-form>
- <el-table
- border stripe
- :height="state.tableHeight"
- :data="state.subscriberData"
- v-loading="state.subscriberLoading">
- <template #empty>
- <el-empty description="请选择站点后查询"/>
- </template>
- <el-table-column label="OpenID" prop="openid" width="260" show-overflow-tooltip/>
- <el-table-column label="昵称" prop="nickname" width="150"/>
- <el-table-column label="站点" prop="stationId" width="120"/>
- <el-table-column label="绑定时间" prop="subscribeTime" width="170">
- <template #default="{row}">{{ u.fmt.fmtDateTime(row.subscribeTime) }}</template>
- </el-table-column>
- <el-table-column label="状态" prop="status" width="90">
- <template #default="{row}">
- <el-tag :type="row.status === 1 ? 'success' : 'info'" size="small">
- {{ row.status === 1 ? '已订阅' : '已解绑' }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column label="操作" width="100" align="center" fixed="right">
- <template #default="{row}">
- <el-button v-if="row.status === 1" type="danger" text size="small" @click="handleUnsubscribe(row)">解绑</el-button>
- </template>
- </el-table-column>
- </el-table>
- </el-tab-pane>
- <!-- ==================== 故障记录 ==================== -->
- <el-tab-pane label="故障记录" name="faultRecord">
- <el-form :model="state.faultQuery" size="default" label-width="0px" class="mt5 mb5">
- <ext-select
- v-model="state.faultQuery.stationId"
- placeholder="站点(可选)"
- url="washStation/list"
- url-method="post"
- label-key="stationName"
- value-key="stationId"
- data-key="list"
- clearable
- class="wd200"/>
- <el-select v-model="state.faultQuery.faultType" placeholder="故障类型" clearable class="wd150 ml10">
- <el-option label="设备离线" value="offline"/>
- <el-option label="缺水" value="water_shortage"/>
- <el-option label="缺泡沫" value="foam_shortage"/>
- </el-select>
- <el-select v-model="state.faultQuery.isRecovered" placeholder="恢复状态" clearable class="wd150 ml10">
- <el-option label="未恢复" :value="0"/>
- <el-option label="已恢复" :value="1"/>
- </el-select>
- <el-button class="ml10" plain size="default" type="success" @click="loadFaultRecords(true)">
- <SvgIcon name="ele-Search"/>
- 查询
- </el-button>
- </el-form>
- <el-table
- border stripe
- :height="state.tableHeight"
- :data="state.faultRecordData"
- v-loading="state.faultRecordLoading">
- <template #empty>
- <el-empty description="暂无故障记录"/>
- </template>
- <el-table-column label="站点" prop="stationId" width="100"/>
- <el-table-column label="设备" prop="deviceName" width="150"/>
- <el-table-column label="故障类型" prop="faultType" width="110">
- <template #default="{row}">
- <el-tag class="fault-tag"
- :type="row.faultType === 'offline' ? 'danger' : 'warning'"
- size="small">
- {{ faultTypeLabel(row.faultType) }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column label="故障时间" prop="faultTime" width="170">
- <template #default="{row}">{{ u.fmt.fmtDateTime(row.faultTime) }}</template>
- </el-table-column>
- <el-table-column label="是否通知" prop="isNotified" width="90">
- <template #default="{row}">
- <el-tag :type="row.isNotified === 1 ? 'success' : 'warning'" size="small">
- {{ row.isNotified === 1 ? '已通知' : '未通知' }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column label="通知时间" prop="notifyTime" width="170">
- <template #default="{row}">{{ row.notifyTime ? u.fmt.fmtDateTime(row.notifyTime) : '-' }}</template>
- </el-table-column>
- <el-table-column label="是否恢复" prop="isRecovered" width="90">
- <template #default="{row}">
- <el-tag :type="row.isRecovered === 1 ? 'success' : 'danger'" size="small">
- {{ row.isRecovered === 1 ? '已恢复' : '未恢复' }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column label="恢复时间" prop="recoverTime" width="170">
- <template #default="{row}">{{ row.recoverTime ? u.fmt.fmtDateTime(row.recoverTime) : '-' }}</template>
- </el-table-column>
- </el-table>
- <ext-page class="page-pager" v-model:value="state.faultPageQuery" @change="loadFaultRecords(false)"/>
- </el-tab-pane>
- </el-tabs>
- </el-card>
- <!-- 二维码弹窗 -->
- <el-dialog v-model="state.qrcodeDialogVisible" title="故障通知绑定二维码" width="420px" center>
- <div style="text-align: center">
- <img v-if="state.qrcodeUrl" :src="state.qrcodeUrl" class="qrcode-img" alt="绑定二维码"/>
- <div class="qrcode-tip">
- 请使用微信扫描二维码<br/>
- 扫描后关注公众号即可绑定,再次扫描可解绑<br/>
- 站点:{{ state.formQuery.stationId }}
- </div>
- </div>
- </el-dialog>
- </div>
- </template>
- <script setup lang="ts" name="adminStationFault">
- import {reactive, onMounted, onBeforeMount, ref, nextTick, onBeforeUnmount} from 'vue';
- import {$body, $get} from "/@/utils/request";
- import u from '/@/utils/u'
- import {Msg} from "/@/utils/message";
- import ExtPage from '/@/components/form/ExtPage.vue'
- import ExtSelect from "/@/components/form/ExtSelect.vue";
- import mittBus from '/@/utils/mitt';
- const state = reactive({
- activeTab: 'subscriber',
- tableHeight: 400,
- formQuery: {
- stationId: '' as string
- },
- subscriberData: [] as Array<any>,
- subscriberLoading: false,
- faultQuery: {
- stationId: '' as string,
- faultType: '' as string,
- isRecovered: null as number | null
- },
- faultRecordData: [] as Array<any>,
- faultRecordLoading: false,
- faultPageQuery: {
- pageNum: 1,
- pageSize: 10,
- total: 0
- },
- qrcodeDialogVisible: false,
- qrcodeUrl: '',
- qrcodeTicket: ''
- });
- const faultTypeLabel = (type: string) => {
- switch (type) {
- case 'offline': return '设备离线';
- case 'water_shortage': return '缺水';
- case 'foam_shortage': return '缺泡沫';
- default: return type;
- }
- };
- const handleTabClick = () => {
- if (state.activeTab === 'faultRecord') {
- loadFaultRecords(true);
- }
- };
- // ============ 订阅管理 ============
- const loadSubscribers = (refresh: boolean = false) => {
- if (!state.formQuery.stationId) {
- state.subscriberData = [];
- return;
- }
- state.subscriberLoading = true;
- $get('/faultSubscriber/list', { stationId: state.formQuery.stationId }).then((res: any) => {
- state.subscriberData = res.data || res || [];
- state.subscriberLoading = false;
- }).catch(() => {
- state.subscriberLoading = false;
- });
- };
- const handleGenerateQrcode = () => {
- if (!state.formQuery.stationId) {
- Msg.message('请先选择站点', 'warning');
- return;
- }
- Msg.showLoading('生成中...');
- $body('/faultSubscriber/generateQrcode', { stationId: state.formQuery.stationId }).then((res: any) => {
- Msg.hideLoading();
- const data = res.data || res;
- state.qrcodeUrl = data.url;
- state.qrcodeTicket = data.ticket;
- state.qrcodeDialogVisible = true;
- }).catch(() => {
- Msg.hideLoading();
- Msg.message('生成二维码失败', 'error');
- });
- };
- const handleUnsubscribe = (row: any) => {
- Msg.confirm(`确定要解绑 ${row.openid} 吗?解绑后该用户将不再接收故障通知。`).then(() => {
- $body('/faultSubscriber/unsubscribe', { openid: row.openid, stationId: row.stationId }).then(() => {
- Msg.message('解绑成功', 'success');
- loadSubscribers(false);
- }).catch(() => {
- Msg.message('解绑失败', 'error');
- });
- }).catch(() => {});
- };
- // ============ 故障记录 ============
- const loadFaultRecords = (refresh: boolean = false) => {
- if (refresh) {
- state.faultPageQuery.pageNum = 1;
- }
- state.faultRecordLoading = true;
- const params: any = { ...state.faultPageQuery };
- if (state.faultQuery.stationId) params.stationId = state.faultQuery.stationId;
- if (state.faultQuery.faultType) params.faultType = state.faultQuery.faultType;
- if (state.faultQuery.isRecovered !== null && state.faultQuery.isRecovered !== undefined) {
- params.isRecovered = state.faultQuery.isRecovered;
- }
- $get('/faultSubscriber/faultRecords', params).then((res: any) => {
- const list = res.data || res || [];
- if (Array.isArray(list)) {
- state.faultRecordData = list;
- state.faultPageQuery.total = list.length;
- } else if (list.list) {
- state.faultRecordData = list.list;
- state.faultPageQuery.total = list.total || 0;
- } else {
- state.faultRecordData = list;
- }
- state.faultRecordLoading = false;
- }).catch(() => {
- state.faultRecordLoading = false;
- });
- };
- // ============ 生命周期 ============
- onBeforeMount(() => {
- });
- onMounted(() => {
- nextTick(() => {
- let bodyHeight = document.body.clientHeight;
- state.tableHeight = bodyHeight - 280;
- });
- mittBus.on("faultSubscriber.refresh", () => {
- loadSubscribers(false);
- });
- });
- onBeforeUnmount(() => {
- mittBus.off("faultSubscriber.refresh");
- });
- </script>
|