Compare commits

9 Commits

Author SHA1 Message Date
tianyongbao
6613a0651b fix:功能进一步优化。 2026-01-10 02:02:28 +08:00
tianyongbao
bccb7d93e3 fix: 功能完善。 2026-01-10 02:02:12 +08:00
tianyongbao
621fee41a4 Merge branch 'dev' into test 2025-12-14 23:17:30 +08:00
tianyongbao
d3403d02f9 feat: 新功能开发,页面开发。 2025-11-05 00:45:11 +08:00
tianyongbao
6eefc67046 feat: 新功能开发,设备实时数据查看等。 2025-11-05 00:45:07 +08:00
tianyongbao
7e8ef7aa0f feat: 新功能开发,设备告警信息、设备实时数据查看等。 2025-10-30 11:22:19 +08:00
tianyongbao
6da46f87ad fix: 登录页,去掉租户选择。 2025-10-24 20:42:33 +08:00
tianyongbao
e9be8f13ed fix: 首页功能优化。 2025-10-24 20:42:13 +08:00
tianyongbao
3b39f9f3f3 feat:功能优化,设备报警信息、微信缓存用户,操作记录信息,代码提交。 2025-10-24 20:42:09 +08:00
29 changed files with 228 additions and 2537 deletions

View File

@@ -84,36 +84,6 @@ export interface CallNoticeVO {
*/
voiceType: string;
/**
* 呼叫时间
*/
callTime?: string;
/**
* 用户名(关联查询)
*/
userName?: string;
/**
* 设备编号(关联查询)
*/
serialNum?: string;
/**
* 设备类型(关联查询)
*/
deviceType?: number;
/**
* 告警类型
*/
warnType?: number;
/**
* 告警标题
*/
warnTitle?: string;
}
export interface CallNoticeForm extends BaseEntity {

View File

@@ -16,19 +16,6 @@ export const listDevice = (query?: DeviceQuery): AxiosPromise<DeviceVO[]> => {
});
};
/**
* 查询太阳能网控设备列表
* @param query
* @returns {*}
*/
export const listSolarDevice = (query?: DeviceQuery): AxiosPromise<DeviceVO[]> => {
return request({
url: '/fishery/device/solar/list',
method: 'get',
params: query
});
};
/**
* 查询设备管理详细
* @param id
@@ -74,25 +61,3 @@ export const delDevice = (id: string | number | Array<string | number>) => {
method: 'delete'
});
};
/**
* 硬删除设备(彻底删除)
* @param ids
*/
export const hardDeleteDevice = (ids: string | number | Array<string | number>) => {
return request({
url: '/fishery/device/hard_delete/' + (Array.isArray(ids) ? ids.join(',') : ids),
method: 'delete'
});
};
/**
* 数据清零
* @param id
*/
export const resetTestData = (id: string | number) => {
return request({
url: '/fishery/device/resetTestData/' + id,
method: 'put'
});
};

View File

@@ -184,11 +184,6 @@ export interface DeviceVO {
*/
voltageWarnOpen: number;
/**
* 控制器故障电话报警开关(断电不受此开关限制)
*/
controllerFaultCallOpen: number;
/**
* 备注
*/
@@ -219,31 +214,6 @@ export interface DeviceVO {
*/
batteryWarnLower: number;
/**
* 用户名(关联查询)
*/
userName?: string;
/**
* 手机号(关联查询)
*/
mobilePhone?: string;
/**
* 塘口名称(关联查询)
*/
pondName?: string;
/**
* 在线状态online/offline
*/
onlineStatus?: string;
/**
* 创建时间(激活时间)
*/
createTime?: string;
}
export interface DeviceForm extends BaseEntity {
@@ -432,11 +402,6 @@ export interface DeviceForm extends BaseEntity {
*/
voltageWarnOpen?: number;
/**
* 控制器故障电话报警开关(断电不受此开关限制)
*/
controllerFaultCallOpen?: number;
/**
* 备注
*/

View File

@@ -69,11 +69,6 @@ export interface MessageWarnVO {
*/
createTime?: string;
/**
* 电话通知次数
*/
callNoticeCount?: number;
}
export interface MessageWarnForm extends BaseEntity {

View File

@@ -61,15 +61,3 @@ export const delPayOrder = (id: string | number | Array<string | number>) => {
method: 'delete'
});
};
/**
* 主动向微信确认并修复支付订单
* @param orderId 订单ID
*/
export const confirmPayOrder = (orderId: string | number) => {
return request({
url: '/weixin/pay/confirm_order',
method: 'post',
data: { orderId }
});
};

View File

@@ -1,38 +0,0 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
export interface SolarPropertyCommand {
id: string | number;
properties: Record<string, any>;
cache?: boolean;
}
export interface SolarPropertyCommandResult {
deviceId: string | number;
serialNum: string;
online: boolean;
statusCode?: number;
properties: Record<string, any>;
mode: 'direct' | 'cached';
cached: boolean;
sent: boolean;
}
export const getDeviceStatus = (devicetype: number, serialnum: string): AxiosPromise<number> => {
return request({
url: '/iot/device/status',
method: 'get',
params: {
devicetype,
serialnum
}
});
};
export const sendSolarPropertyCommand = (data: SolarPropertyCommand): AxiosPromise<SolarPropertyCommandResult> => {
return request({
url: '/iot/device/solar/property-command',
method: 'post',
data
});
};

View File

@@ -19,7 +19,6 @@ declare module 'vue' {
ElButton: typeof import('element-plus/es')['ElButton']
ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCol: typeof import('element-plus/es')['ElCol']
ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']

View File

@@ -3,12 +3,12 @@
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" style="white-space: nowrap">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="用户名" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名" clearable style="width: 160px" @keyup.enter="handleQuery" />
<el-input v-model="queryParams.userName" placeholder="请输入用户名" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="手机号" prop="mobilePhone">
<el-input v-model="queryParams.mobilePhone" placeholder="请输入手机号" clearable style="width: 160px" @keyup.enter="handleQuery" />
<el-input v-model="queryParams.mobilePhone" placeholder="请输入手机号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<!-- <el-form-item label="省份" prop="province">
<el-input v-model="queryParams.province" placeholder="请输入省份" clearable @keyup.enter="handleQuery" />
@@ -20,13 +20,7 @@
<el-input v-model="queryParams.district" placeholder="请输入区县" clearable @keyup.enter="handleQuery" />
</el-form-item> -->
<el-form-item label="报警电话" prop="warnPhoneJson">
<el-input v-model="queryParams.warnPhoneJson" placeholder="请输入报警电话" clearable style="width: 160px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="是否管理员" prop="isManager">
<el-select v-model="queryParams.isManager" placeholder="请选择" clearable style="width: 120px">
<el-option label="是" :value="1" />
<el-option label="否" :value="0" />
</el-select>
<el-input v-model="queryParams.warnPhoneJson" placeholder="请输入报警电话" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
@@ -65,13 +59,6 @@
<el-table-column label="主键id" align="center" prop="id" v-if="false" />
<el-table-column label="用户名" align="center" prop="userName" />
<el-table-column label="手机号" align="center" prop="mobilePhone" />
<el-table-column label="是否管理员" align="center" prop="isManager" width="100">
<template #default="scope">
<el-tag :type="scope.row.isManager === 1 ? 'success' : 'info'" size="small">
{{ scope.row.isManager === 1 ? '是' : '否' }}
</el-tag>
</template>
</el-table-column>
<!-- <el-table-column label="省份" align="center" prop="province" />
<el-table-column label="城市" align="center" prop="city" />
<el-table-column label="区县" align="center" prop="district" /> -->
@@ -135,10 +122,7 @@
<el-input v-model="form.wxSessionKey" placeholder="请输入微信SessionKey" />
</el-form-item>
<el-form-item label="是否管理员" prop="isManager">
<el-select v-model="form.isManager" placeholder="请选择是否管理员" clearable style="width: 200px">
<el-option label="是" :value="1" />
<el-option label="否" :value="0" />
</el-select>
<el-input v-model="form.isManager" placeholder="请输入是否管理员" />
</el-form-item>
<el-form-item label="微信unionId" prop="wxUnionId">
<el-input v-model="form.wxUnionId" placeholder="请输入微信unionId" />
@@ -470,15 +454,6 @@
</el-col>
</el-row>
<el-row :gutter="10" class="mt-3">
<el-col :span="6">
<div class="param-item">
<div class="param-label">控制器故障电话告警</div>
<dict-tag :options="[{label:'关',value:'0'},{label:'开',value:'1'}]" :value="String(device.controllerFaultCallOpen ?? 1)"/>
</div>
</el-col>
</el-row>
<el-row :gutter="10" class="mt-3">
<el-col :span="6">
<div class="param-item">
@@ -690,15 +665,6 @@
</el-col>
</el-row>
<el-row :gutter="10" class="mt-3">
<el-col :span="6">
<div class="param-item">
<div class="param-label">控制器故障电话告警</div>
<dict-tag :options="[{label:'关',value:'0'},{label:'开',value:'1'}]" :value="String(device.controllerFaultCallOpen ?? 1)"/>
</div>
</el-col>
</el-row>
<el-row :gutter="10" class="mt-3">
<el-col :span="6">
<div class="param-item">
@@ -907,7 +873,6 @@ const data = reactive<PageData<AquUserForm, AquUserQuery>>({
city: undefined,
district: undefined,
warnPhoneJson: undefined,
isManager: undefined,
params: {}
},
rules: {

View File

@@ -20,9 +20,6 @@
<el-option v-for="dict in warn_type" :key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="告警标题" prop="params.warnTitle">
<el-input v-model="queryParams.params.warnTitle" placeholder="请输入告警标题" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="呼叫电话" prop="mobilePhone">
<el-input v-model="queryParams.mobilePhone" placeholder="请输入呼叫电话" clearable @keyup.enter="handleQuery" />
</el-form-item>
@@ -59,7 +56,7 @@
</el-row>
</template>
<el-table v-loading="loading" border :data="callNoticeList" @selection-change="handleSelectionChange" class="hidden-sm-and-down">
<el-table v-loading="loading" border :data="callNoticeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="用户名" align="center" prop="userName" width="120"/>
<!-- <el-table-column label="手机号" align="center" prop="mobilePhone" width="120"/> -->
@@ -111,64 +108,6 @@
</el-table-column>
</el-table>
<!-- 移动端卡片展示 -->
<div v-loading="loading" class="mobile-card-list hidden-sm-and-up">
<div v-if="callNoticeList.length === 0" class="no-data">
<el-empty description="暂无数据" />
</div>
<el-card v-for="item in callNoticeList" :key="item.id" class="mobile-notice-card" shadow="hover">
<!-- 卡片头部 -->
<div class="card-header">
<div class="header-left">
<dict-tag :options="call_status" :value="item.callStatus" class="status-tag"/>
<span class="call-time">{{ item.callTime || '-' }}</span>
</div>
<el-checkbox :model-value="ids.includes(item.id)" @change="handleMobileSelect(item, $event)" />
</div>
<!-- 基本信息 -->
<div class="info-section">
<div class="info-list">
<div class="info-row"><span class="label">用户名</span><span class="value">{{ item.userName || '-' }}</span></div>
<div class="info-row"><span class="label">设备编号</span><span class="value">{{ item.serialNum || '-' }}</span></div>
<div class="info-row"><span class="label">设备类型</span><span class="value"><dict-tag :options="aqu_device_type" :value="item.deviceType"/></span></div>
<div class="info-row"><span class="label">呼叫电话</span><span class="value highlight">{{ item.mobilePhone || '-' }}</span></div>
<div class="info-row"><span class="label">主叫号码</span><span class="value">{{ item.caller || '-' }}</span></div>
</div>
</div>
<!-- 告警信息 -->
<div class="info-section warn-section">
<div class="section-title">告警信息</div>
<div class="warn-grid">
<div class="warn-item">
<span class="warn-label">告警类型</span>
<span class="warn-value"><dict-tag :options="warn_type" :value="item.warnType"/></span>
</div>
<div class="warn-item">
<span class="warn-label">告警标题</span>
<span class="warn-value">{{ item.warnTitle || '-' }}</span>
</div>
</div>
</div>
<!-- 结果信息 -->
<div class="info-section" v-if="item.statusMsg">
<div class="section-title">呼叫结果</div>
<div class="result-box">
<span class="result-text">{{ item.statusMsg }}</span>
</div>
</div>
<!-- 操作按钮 -->
<div class="card-footer">
<el-button type="danger" size="small" plain @click="handleDelete(item)" v-hasPermi="['fishery:callNotice:remove']">
<el-icon><Delete /></el-icon>删除
</el-button>
</div>
</el-card>
</div>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改告警电话通知记录对话框 -->
@@ -397,19 +336,6 @@ const handleSelectionChange = (selection: CallNoticeVO[]) => {
multiple.value = !selection.length;
}
/** 移动端选择 */
const handleMobileSelect = (item: CallNoticeVO, checked: boolean | string | number) => {
if (checked) {
if (!ids.value.includes(item.id)) {
ids.value.push(item.id);
}
} else {
ids.value = ids.value.filter(id => id !== item.id);
}
single.value = ids.value.length != 1;
multiple.value = !ids.value.length;
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
@@ -464,185 +390,3 @@ onMounted(() => {
getList();
});
</script>
<style lang="scss" scoped>
// 移动端卡片样式
.mobile-card-list {
.no-data {
padding: 40px 0;
}
.mobile-notice-card {
margin-bottom: 12px;
border-radius: 8px;
:deep(.el-card__body) {
padding: 12px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 10px;
border-bottom: 1px solid #ebeef5;
.header-left {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
min-width: 0;
.status-tag {
flex-shrink: 0;
}
.call-time {
font-size: 12px;
color: #909399;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
.info-section {
padding: 10px 0;
border-bottom: 1px solid #ebeef5;
&:last-of-type {
border-bottom: none;
}
.section-title {
font-size: 12px;
font-weight: 600;
color: #e6a23c;
margin-bottom: 8px;
padding-left: 8px;
border-left: 3px solid #e6a23c;
}
.info-list {
background-color: #fafafa;
border-radius: 6px;
padding: 4px 0;
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
font-size: 13px;
border-bottom: 1px dashed #ebeef5;
&:last-child {
border-bottom: none;
}
.label {
color: #909399;
flex-shrink: 0;
}
.value {
color: #303133;
font-weight: 500;
text-align: right;
word-break: break-all;
flex: 1;
margin-left: 12px;
&.highlight {
color: #409eff;
font-weight: 600;
}
}
}
}
.warn-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
.warn-item {
display: flex;
flex-direction: column;
padding: 8px 12px;
background: linear-gradient(135deg, #fdf6ec 0%, #faecd8 100%);
border-radius: 6px;
border: 1px solid #f5dab1;
.warn-label {
font-size: 11px;
color: #909399;
margin-bottom: 4px;
}
.warn-value {
font-size: 13px;
font-weight: 500;
color: #303133;
}
}
}
.result-box {
padding: 10px 12px;
background-color: #f5f7fa;
border-radius: 6px;
.result-text {
font-size: 13px;
color: #606266;
line-height: 1.5;
}
}
}
.card-footer {
display: flex;
justify-content: flex-end;
gap: 8px;
padding-top: 12px;
border-top: 1px solid #ebeef5;
margin-top: 4px;
.el-button {
display: flex;
align-items: center;
justify-content: center;
padding: 8px 16px;
font-size: 13px;
border-radius: 8px;
transition: all 0.2s;
.el-icon {
font-size: 16px;
margin-right: 4px;
}
&:active {
transform: scale(0.96);
}
}
}
}
}
// 响应式隐藏
.hidden-sm-and-down {
@media (max-width: 768px) {
display: none !important;
}
}
.hidden-sm-and-up {
@media (min-width: 769px) {
display: none !important;
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -32,15 +32,15 @@
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<!-- <el-col :span="1.5">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['fishery:deviceBindRecord:add']">新增</el-button>
</el-col> -->
<!-- <el-col :span="1.5">
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['fishery:deviceBindRecord:edit']">修改</el-button>
</el-col> -->
<!-- <el-col :span="1.5">
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['fishery:deviceBindRecord:remove']">删除</el-button>
</el-col> -->
</el-col>
<!-- <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['fishery:deviceBindRecord:export']">导出</el-button>
</el-col> -->
@@ -58,14 +58,14 @@
</template>
</el-table-column>
<el-table-column label="设备编号" align="center" prop="serialNum" />
<el-table-column label="绑定/解绑" align="center" prop="isBind">
<template #default="scope">
<dict-tag :options="is_bind" :value="scope.row.isBind"/>
</template>
</el-table-column>
<el-table-column label="操作时间" align="center" prop="createTime" />
<!-- <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['fishery:deviceBindRecord:edit']"></el-button>
@@ -74,7 +74,7 @@
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['fishery:deviceBindRecord:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column> -->
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
@@ -83,10 +83,10 @@
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="deviceBindRecordFormRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="用户" prop="userId">
<el-input
:value="selectedUser ? `${selectedUser.userName} (${selectedUser.mobilePhone})` : ''"
placeholder="请选择用户"
readonly
<el-input
:value="selectedUser ? `${selectedUser.userName} (${selectedUser.mobilePhone})` : ''"
placeholder="请选择用户"
readonly
@click="openUserSelect"
style="cursor: pointer;"
>
@@ -108,9 +108,9 @@
<el-form-item label="设备编号" prop="serialNum">
<el-input v-model="form.serialNum" placeholder="请输入设备编号" />
</el-form-item>
<!-- <el-form-item label="物联网IotId" prop="iotId">
<el-form-item label="物联网IotId" prop="iotId">
<el-input v-model="form.iotId" placeholder="请输入物联网IotId" />
</el-form-item> -->
</el-form-item>
<el-form-item label="绑定/解绑" prop="isBind">
<el-select v-model="form.isBind" placeholder="请选择绑定/解绑" style="width: 100%">
<el-option
@@ -129,25 +129,25 @@
</div>
</template>
</el-dialog>
<!-- 用户选择对话框 -->
<el-dialog title="选择用户" v-model="userSelectVisible" width="900px" append-to-body>
<!-- 搜索条件 -->
<el-form :model="userQueryParams" :inline="true" class="mb-4">
<el-form-item label="用户名">
<el-input
v-model="userQueryParams.userName"
placeholder="请输入用户名"
clearable
<el-input
v-model="userQueryParams.userName"
placeholder="请输入用户名"
clearable
style="width: 180px"
@keyup.enter="handleUserQuery"
/>
</el-form-item>
<el-form-item label="手机号">
<el-input
v-model="userQueryParams.mobilePhone"
placeholder="请输入手机号"
clearable
<el-input
v-model="userQueryParams.mobilePhone"
placeholder="请输入手机号"
clearable
style="width: 180px"
@keyup.enter="handleUserQuery"
/>
@@ -157,10 +157,10 @@
<el-button icon="Refresh" @click="resetUserQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 用户表格 -->
<el-table
:data="aquUserList"
<el-table
:data="aquUserList"
highlight-current-row
height="400px"
border
@@ -172,9 +172,9 @@
<el-table-column label="区县" align="center" prop="district" />
<el-table-column label="操作" align="center" width="100">
<template #default="scope">
<el-button
type="primary"
size="small"
<el-button
type="primary"
size="small"
@click="handleUserSelect(scope.row)"
>
选择
@@ -182,17 +182,17 @@
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
v-show="userTotal > 0"
:total="userTotal"
v-model:page="userQueryParams.pageNum"
v-model:limit="userQueryParams.pageSize"
<pagination
v-show="userTotal > 0"
:total="userTotal"
v-model:page="userQueryParams.pageNum"
v-model:limit="userQueryParams.pageSize"
@pagination="handleUserPaginationChange"
class="mt-4"
/>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancelUserSelect"> </el-button>
@@ -345,7 +345,7 @@ const handleUpdate = async (row?: DeviceBindRecordVO) => {
const _id = row?.id || ids.value[0]
const res = await getDeviceBindRecord(_id);
Object.assign(form.value, res.data);
// 回显用户信息
if (form.value.userId) {
try {
@@ -368,7 +368,7 @@ const handleUpdate = async (row?: DeviceBindRecordVO) => {
} else {
selectedUser.value = null;
}
dialog.visible = true;
dialog.title = "修改设备绑定记录";
}

View File

@@ -30,9 +30,9 @@
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['fishery:deviceCorrectRecord:add']">新增</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['fishery:deviceCorrectRecord:edit']">修改</el-button>
</el-col> -->
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['fishery:deviceCorrectRecord:remove']">删除</el-button>
</el-col>

View File

@@ -31,9 +31,9 @@
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['fishery:deviceErrorCode:add']">新增</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['fishery:deviceErrorCode:edit']">修改</el-button>
</el-col> -->
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['fishery:deviceErrorCode:remove']">删除</el-button>
</el-col>

View File

@@ -28,9 +28,9 @@
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['fishery:deviceSwitch:add']">新增</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['fishery:deviceSwitch:edit']">修改</el-button>
</el-col> -->
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['fishery:deviceSwitch:remove']">删除</el-button>
</el-col>
@@ -271,15 +271,10 @@
<el-dialog
:title="`定时控制 - ${currentSwitch?.switchName || ''} (序号:${currentSwitch?.index || ''})`"
v-model="timingCtrlDialogVisible"
width="1080px"
width="800px"
append-to-body
@close="closeTimingCtrlDialog"
>
<!-- 工具栏 -->
<div style="margin-bottom: 12px;">
<el-button type="primary" plain icon="Plus" size="small" @click="handleTimingCtrlAdd" v-hasPermi="['fishery:timingCtrl:add']">新增</el-button>
</div>
<el-table
v-loading="timingCtrlLoading"
:data="timingCtrlList"
@@ -298,17 +293,6 @@
<dict-tag :options="open_close" :value="scope.row.isOpen"/>
</template>
</el-table-column>
<!-- <el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip /> -->
<el-table-column label="操作" align="center" width="120" fixed="right">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleTimingCtrlUpdate(scope.row)" v-hasPermi="['fishery:timingCtrl:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="danger" icon="Delete" @click="handleTimingCtrlDelete(scope.row)" v-hasPermi="['fishery:timingCtrl:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<template #footer>
@@ -317,49 +301,6 @@
</div>
</template>
</el-dialog>
<!-- 定时控制编辑对话框 -->
<el-dialog :title="timingCtrlEditDialog.title" v-model="timingCtrlEditDialog.visible" width="500px" append-to-body>
<el-form ref="timingCtrlFormRef" :model="timingCtrlForm" :rules="timingCtrlRules" label-width="90px">
<el-form-item label="开启时间" prop="openTime">
<el-date-picker clearable
v-model="timingCtrlForm.openTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择开启时间"
style="width: 100%">
</el-date-picker>
</el-form-item>
<el-form-item label="关闭时间" prop="closeTime">
<el-date-picker clearable
v-model="timingCtrlForm.closeTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择关闭时间"
style="width: 100%">
</el-date-picker>
</el-form-item>
<el-form-item label="循环类型" prop="loopType">
<el-select v-model="timingCtrlForm.loopType" placeholder="请选择循环类型" style="width: 100%">
<el-option v-for="dict in loop_type" :key="dict.value" :label="dict.label" :value="parseInt(dict.value)"></el-option>
</el-select>
</el-form-item>
<el-form-item label="是否启用" prop="isOpen">
<el-select v-model="timingCtrlForm.isOpen" placeholder="请选择是否启用" style="width: 100%">
<el-option v-for="dict in open_close" :key="dict.value" :label="dict.label" :value="parseInt(dict.value)"></el-option>
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="timingCtrlForm.remark" type="textarea" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="timingCtrlEditLoading" type="primary" @click="submitTimingCtrlForm"> </el-button>
<el-button @click="timingCtrlEditDialog.visible = false"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
@@ -368,8 +309,8 @@ import { listDeviceSwitch, getDeviceSwitch, delDeviceSwitch, addDeviceSwitch, up
import { DeviceSwitchVO, DeviceSwitchQuery, DeviceSwitchForm } from '@/api/fishery/deviceSwitch/types';
import { listDevice } from '@/api/fishery/device';
import { DeviceVO } from '@/api/fishery/device/types';
import { listTimingCtrl, getTimingCtrl, addTimingCtrl, updateTimingCtrl, delTimingCtrl } from '@/api/fishery/timingCtrl';
import { TimingCtrlVO, TimingCtrlForm } from '@/api/fishery/timingCtrl/types';
import { listTimingCtrl } from '@/api/fishery/timingCtrl';
import { TimingCtrlVO } from '@/api/fishery/timingCtrl/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { aqu_device_type, open_close, connect_voltage_type, is_linked_ctrl, loop_type } = toRefs<any>(proxy?.useDict('aqu_device_type', 'open_close', 'connect_voltage_type', 'is_linked_ctrl', 'loop_type'));
@@ -700,79 +641,6 @@ const closeTimingCtrlDialog = () => {
timingCtrlDialogVisible.value = false;
currentSwitch.value = null;
timingCtrlList.value = [];
getList();
};
// 定时控制编辑对话框
const timingCtrlEditDialog = reactive<DialogOption>({ visible: false, title: '' });
const timingCtrlFormRef = ref<ElFormInstance>();
const timingCtrlEditLoading = ref(false);
const initTimingCtrlForm: TimingCtrlForm = {
id: undefined,
switchId: undefined,
openTime: undefined,
closeTime: undefined,
loopType: undefined,
isOpen: undefined,
remark: undefined
};
const timingCtrlForm = ref<TimingCtrlForm>({ ...initTimingCtrlForm });
const timingCtrlRules = {
openTime: [{ required: true, message: '开启时间不能为空', trigger: 'blur' }],
closeTime: [{ required: true, message: '关闭时间不能为空', trigger: 'blur' }],
loopType: [{ required: true, message: '循环类型不能为空', trigger: 'change' }],
isOpen: [{ required: true, message: '是否启用不能为空', trigger: 'change' }]
};
/** 新增定时控制 */
const handleTimingCtrlAdd = () => {
timingCtrlForm.value = { ...initTimingCtrlForm, switchId: currentSwitch.value?.id };
timingCtrlFormRef.value?.resetFields();
timingCtrlEditDialog.title = '新增定时控制';
timingCtrlEditDialog.visible = true;
};
/** 修改定时控制 */
const handleTimingCtrlUpdate = async (row: TimingCtrlVO) => {
const res = await getTimingCtrl(row.id);
timingCtrlForm.value = { ...res.data };
timingCtrlFormRef.value?.resetFields();
timingCtrlEditDialog.title = '修改定时控制';
timingCtrlEditDialog.visible = true;
};
/** 提交定时控制表单 */
const submitTimingCtrlForm = () => {
timingCtrlFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
timingCtrlEditLoading.value = true;
try {
if (timingCtrlForm.value.id) {
await updateTimingCtrl(timingCtrlForm.value);
} else {
await addTimingCtrl(timingCtrlForm.value);
}
proxy?.$modal.msgSuccess('操作成功');
timingCtrlEditDialog.visible = false;
// 刷新定时控制列表
if (currentSwitch.value) {
await getTimingCtrlList(currentSwitch.value.id);
}
} finally {
timingCtrlEditLoading.value = false;
}
}
});
};
/** 删除定时控制 */
const handleTimingCtrlDelete = async (row: TimingCtrlVO) => {
await proxy?.$modal.confirm('是否确认删除该定时控制?');
await delTimingCtrl(row.id);
proxy?.$modal.msgSuccess('删除成功');
if (currentSwitch.value) {
await getTimingCtrlList(currentSwitch.value.id);
}
};
onMounted(() => {

View File

@@ -25,11 +25,11 @@
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['fishery:deviceThreshold:add']">新增</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['fishery:deviceThreshold:edit']"
>修改</el-button
>
</el-col> -->
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['fishery:deviceThreshold:remove']"
>删除</el-button

View File

@@ -27,9 +27,9 @@
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['fishery:fish:add']">新增</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['fishery:fish:edit']">修改</el-button>
</el-col> -->
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['fishery:fish:remove']"
>删除</el-button

View File

@@ -40,9 +40,9 @@
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['fishery:linkedCtrl:add']">新增</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['fishery:linkedCtrl:edit']">修改</el-button>
</el-col> -->
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['fishery:linkedCtrl:remove']">删除</el-button>
</el-col>

View File

@@ -25,15 +25,15 @@
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['fishery:mapMessageWarnCallNotice:add']">新增</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['fishery:mapMessageWarnCallNotice:edit']">修改</el-button>
</el-col> -->
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['fishery:mapMessageWarnCallNotice:remove']">删除</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['fishery:mapMessageWarnCallNotice:export']">导出</el-button>
</el-col> -->
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>

View File

@@ -36,9 +36,9 @@
<!-- <el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['fishery:messageOpRecord:edit']">修改</el-button>
</el-col> -->
<!-- <el-col :span="1.5">
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['fishery:messageOpRecord:remove']">删除</el-button>
</el-col> -->
</el-col>
<!-- <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['fishery:messageOpRecord:export']">导出</el-button>
</el-col> -->
@@ -63,9 +63,9 @@
<!-- <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['fishery:messageOpRecord:edit']"></el-button>
</el-tooltip> -->
<!-- <el-tooltip content="删除" placement="top">
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['fishery:messageOpRecord:remove']"></el-button>
</el-tooltip> -->
</el-tooltip>
</template>
</el-table-column>
</el-table>

View File

@@ -9,16 +9,12 @@
</el-form-item>
<el-form-item label="设备信息" prop="params.deviceKeyword">
<el-input v-model="queryParams.params.deviceKeyword" placeholder="请输入设备名称或设备编号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="告警标题" prop="title">
<el-input v-model="queryParams.title" placeholder="请输入告警标题" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="告警类型" prop="warnType">
<el-select v-model="queryParams.warnType" placeholder="请选择告警类型" clearable >
<el-option v-for="dict in warn_type" :key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="是否已读" prop="isRead">
<el-select v-model="queryParams.isRead" placeholder="请选择是否已读" clearable >
<el-option v-for="dict in yes_no" :key="dict.value" :label="dict.label" :value="dict.value"/>
@@ -52,7 +48,7 @@
</el-row>
</template>
<el-table v-loading="loading" border :data="messageWarnList" @selection-change="handleSelectionChange" class="hidden-sm-and-down">
<el-table v-loading="loading" border :data="messageWarnList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="用户名" align="center" prop="userName" width="120"/>
<el-table-column label="手机号" align="center" prop="mobilePhone" width="120"/>
@@ -96,55 +92,6 @@
</el-table-column>
</el-table>
<!-- 移动端卡片展示 -->
<div v-loading="loading" class="mobile-card-list hidden-sm-and-up">
<div v-if="messageWarnList.length === 0" class="no-data">
<el-empty description="暂无数据" />
</div>
<el-card v-for="item in messageWarnList" :key="item.id" class="mobile-warn-card" shadow="hover">
<!-- 卡片头部 -->
<div class="card-header">
<dict-tag :options="warn_type" :value="item.warnType" class="warn-type-tag"/>
<el-checkbox :model-value="ids.includes(item.id)" @change="handleMobileSelect(item, $event)" />
</div>
<!-- 基本信息 -->
<div class="info-section">
<div class="info-list">
<div class="info-row"><span class="label">告警标题</span><span class="value">{{ item.title || '-' }}</span></div>
<div class="info-row"><span class="label">告警时间</span><span class="value">{{ item.createTime || '-' }}</span></div>
<div class="info-row"><span class="label">是否已读</span><dict-tag :options="yes_no" :value="item.isRead" /></div>
<div class="info-row"><span class="label">用户名</span><span class="value">{{ item.userName || '-' }}</span></div>
<div class="info-row"><span class="label">手机号</span><span class="value">{{ item.mobilePhone || '-' }}</span></div>
<div class="info-row"><span class="label">设备编号</span><span class="value">{{ item.serialNum || '-' }}</span></div>
<div class="info-row"><span class="label">设备名称</span><span class="value">{{ item.deviceName || '-' }}</span></div>
</div>
</div>
<!-- 告警内容 -->
<div class="message-section">
<div class="message-label">告警内容</div>
<div class="message-content">{{ item.message || '-' }}</div>
</div>
<!-- 电话通知 -->
<div class="notice-section" v-if="item.callNoticeCount && item.callNoticeCount > 0">
<span class="notice-label">电话通知</span>
<span class="notice-count" @click="handleViewCallNotice(item)">{{ item.callNoticeCount }}</span>
</div>
<!-- 操作按钮 -->
<div class="card-footer">
<el-button type="primary" size="small" plain @click="handleView(item)" v-hasPermi="['fishery:messageWarn:query']">
<el-icon><View /></el-icon>查看
</el-button>
<el-button type="danger" size="small" plain @click="handleDelete(item)" v-hasPermi="['fishery:messageWarn:remove']">
<el-icon><Delete /></el-icon>删除
</el-button>
</div>
</el-card>
</div>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 查看设备告警记录对话框 -->
@@ -392,19 +339,6 @@ const handleSelectionChange = (selection: MessageWarnVO[]) => {
multiple.value = !selection.length;
}
/** 移动端选择 */
const handleMobileSelect = (item: MessageWarnVO, checked: boolean | string | number) => {
if (checked) {
if (!ids.value.includes(item.id)) {
ids.value.push(item.id);
}
} else {
ids.value = ids.value.filter(id => id !== item.id);
}
single.value = ids.value.length != 1;
multiple.value = !ids.value.length;
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
@@ -469,150 +403,3 @@ onMounted(() => {
getList();
});
</script>
<style lang="scss" scoped>
// 移动端卡片样式
.mobile-card-list {
.no-data {
padding: 40px 0;
}
.mobile-warn-card {
margin-bottom: 12px;
border-radius: 8px;
:deep(.el-card__body) {
padding: 12px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 8px;
border-bottom: 1px solid #ebeef5;
}
.info-section {
padding: 10px 0;
border-bottom: 1px solid #ebeef5;
.info-list {
background-color: #fafafa;
border-radius: 6px;
padding: 4px 0;
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 12px;
font-size: 12px;
border-bottom: 1px dashed #ebeef5;
&:last-child {
border-bottom: none;
}
.label {
color: #909399;
flex-shrink: 0;
}
.value {
color: #303133;
font-weight: 500;
text-align: right;
word-break: break-all;
flex: 1;
margin-left: 12px;
}
}
}
}
.message-section {
padding: 10px 0;
border-bottom: 1px solid #ebeef5;
.message-label {
font-size: 12px;
color: #909399;
margin-bottom: 6px;
}
.message-content {
font-size: 13px;
color: #606266;
line-height: 1.6;
padding: 8px 12px;
background-color: #fef0f0;
border-radius: 6px;
border-left: 3px solid #f56c6c;
}
}
.notice-section {
display: flex;
align-items: center;
padding: 8px 0;
font-size: 12px;
.notice-label {
color: #909399;
}
.notice-count {
color: #409eff;
font-weight: 600;
margin-left: 8px;
text-decoration: underline;
cursor: pointer;
}
}
.card-footer {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
padding-top: 12px;
border-top: 1px solid #ebeef5;
margin-top: 4px;
.el-button {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: auto;
padding: 10px 8px;
font-size: 12px;
border-radius: 8px;
transition: all 0.2s;
.el-icon {
font-size: 18px;
margin-bottom: 4px;
}
&:active {
transform: scale(0.96);
}
}
}
}
}
// 响应式隐藏
.hidden-sm-and-down {
@media (max-width: 768px) {
display: none !important;
}
}
.hidden-sm-and-up {
@media (min-width: 769px) {
display: none !important;
}
}
</style>

View File

@@ -42,7 +42,6 @@
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
<el-button type="warning" plain icon="Download" :disabled="historyData.length === 0" @click="handleExport">导出 Excel</el-button>
</el-form-item>
</el-form>
</el-card>
@@ -185,7 +184,6 @@
import { ref, reactive, onMounted, nextTick, computed } from 'vue';
import * as echarts from 'echarts';
import type { EChartsOption } from 'echarts';
import FileSaver from 'file-saver';
import { getHistoryData } from '@/api/fishery/monitorHistory';
import type { DeviceSensorDataQueryBo, DeviceSensorData } from '@/api/fishery/monitorHistory/types';
import { listDevice } from '@/api/fishery/device';
@@ -264,73 +262,6 @@ const tabConfig = {
battery: { label: '电量', unit: '%', color: '#9a60b4' }
};
const exportColumns: Array<{ label: string; unit?: string; getValue: (item: DeviceSensorData) => unknown }> = [
{ label: '监测时间', getValue: item => item.time },
{ label: '塘口', getValue: item => (item as DeviceSensorData & { pondName?: string }).pondName || selectedDevice.value?.pondName || '' },
{ label: '设备名称', getValue: item => item.deviceName || selectedDevice.value?.deviceName || '' },
{ label: '设备编号', getValue: item => item.serialNum || selectedDevice.value?.serialNum || queryParams.value.serialNum || '' },
{ label: '溶解氧', unit: 'Mg/L', getValue: item => item.dissolvedOxygen },
{ label: '水温', unit: '℃', getValue: item => item.temperature },
{ label: '饱和度', unit: '%', getValue: item => item.saturability }
];
const escapeHtml = (value: unknown) => {
if (value === null || value === undefined) {
return '';
}
return String(value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
};
const getExportFileName = () => {
const serialNum = queryParams.value.serialNum || 'all';
const startTime = queryParams.value.startTime || dateRange.value?.[0] || '';
const endTime = queryParams.value.endTime || dateRange.value?.[1] || '';
const dateText = startTime && endTime ? `_${startTime}_${endTime}` : '';
return `监测历史记录_${serialNum}${dateText}_${new Date().getTime()}.xls`;
};
const getExportLimit = () => {
const intervalType = Number(queryParams.value.intervalType || 1);
if (intervalType === 1) {
return { months: 1, message: '实时数据最多只能导出1个月内的数据' };
}
if (intervalType === 2 || intervalType === 3) {
return { months: 3, message: '10分钟和30分钟数据最多只能导出3个月内的数据' };
}
return { months: 6, message: '1小时以上数据最多只能导出6个月内的数据' };
};
const validateExportDateRange = () => {
const startTime = queryParams.value.startTime || dateRange.value?.[0];
const endTime = queryParams.value.endTime || dateRange.value?.[1];
if (!startTime || !endTime) {
proxy?.$modal.msgWarning('请先选择导出时间范围');
return false;
}
const startDate = new Date(`${startTime} 00:00:00`.replace(/-/g, '/'));
const endDate = new Date(`${endTime} 23:59:59`.replace(/-/g, '/'));
if (Number.isNaN(startDate.getTime()) || Number.isNaN(endDate.getTime()) || startDate > endDate) {
proxy?.$modal.msgWarning('导出时间范围不正确,请重新选择');
return false;
}
const limit = getExportLimit();
const maxEndDate = new Date(startDate);
maxEndDate.setMonth(maxEndDate.getMonth() + limit.months);
maxEndDate.setHours(23, 59, 59, 999);
if (endDate > maxEndDate) {
proxy?.$modal.msgWarning(limit.message);
return false;
}
return true;
};
/** 查询历史数据 */
const getHistoryList = async () => {
// 验证是否选择了设备
@@ -400,63 +331,6 @@ const resetQuery = () => {
endTime: '',
intervalType: '3'
};
historyData.value = [];
updateChart();
};
/** 导出查询结果 */
const handleExport = () => {
if (historyData.value.length === 0) {
proxy?.$modal.msgWarning('暂无可导出的历史数据,请先查询');
return;
}
if (!validateExportDateRange()) {
return;
}
const headerHtml = exportColumns
.map(column => `<th>${escapeHtml(column.unit ? `${column.label}(${column.unit})` : column.label)}</th>`)
.join('');
const rowsHtml = historyData.value
.map(item => {
const cells = exportColumns
.map(column => `<td style="mso-number-format:'\\@';">${escapeHtml(column.getValue(item))}</td>`)
.join('');
return `<tr>${cells}</tr>`;
})
.join('');
const worksheet = `<!DOCTYPE html>
<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel">
<head>
<meta charset="UTF-8" />
<!--[if gte mso 9]>
<xml>
<x:ExcelWorkbook>
<x:ExcelWorksheets>
<x:ExcelWorksheet>
<x:Name>监测历史记录</x:Name>
<x:WorksheetOptions></x:WorksheetOptions>
</x:ExcelWorksheet>
</x:ExcelWorksheets>
</x:ExcelWorkbook>
</xml>
<![endif]-->
<style>
table { border-collapse: collapse; }
th, td { border: 1px solid #dcdfe6; padding: 6px 10px; white-space: nowrap; }
th { background: #f5f7fa; font-weight: bold; }
</style>
</head>
<body>
<table>
<thead><tr>${headerHtml}</tr></thead>
<tbody>${rowsHtml}</tbody>
</table>
</body>
</html>`;
const blob = new Blob([worksheet], { type: 'application/vnd.ms-excel;charset=utf-8' });
FileSaver.saveAs(blob, getExportFileName());
};
/** 标签页切换 */
@@ -581,8 +455,6 @@ const updateChart = () => {
type: chartType.value,
data: yData,
smooth: chartType.value === 'line',
symbol: 'circle', // 使用圆形标记
symbolSize: chartType.value === 'line' ? 0 : 6, // 折线图默认不显示点,柱状图不受影响
itemStyle: {
color: config.color
},
@@ -591,17 +463,6 @@ const updateChart = () => {
} : undefined,
areaStyle: chartType.value === 'line' ? {
opacity: 0.1
} : undefined,
emphasis: chartType.value === 'line' ? {
symbol: 'circle',
symbolSize: 12,
itemStyle: {
color: config.color,
borderColor: '#fff',
borderWidth: 3,
shadowBlur: 8,
shadowColor: config.color
}
} : undefined
}
]

View File

@@ -509,9 +509,9 @@ const data = reactive<PageData<PayDeviceForm, PayDeviceQuery>>({
}
},
rules: {
// userId: [
// { required: true, message: "用户不能为空", trigger: "blur" }
// ],
userId: [
{ required: true, message: "用户不能为空", trigger: "blur" }
],
serialNum: [
{ required: true, message: "设备编号不能为空", trigger: "blur" }
],

View File

@@ -55,18 +55,7 @@
<dict-tag :options="profit_status" :value="scope.row.profitStatus"/>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="160"/>
<el-table-column label="操作" align="center" width="140" fixed="right">
<template #default="scope">
<el-button
v-if="scope.row.orderStatus !== 2"
link
type="primary"
icon="Refresh"
@click="handleConfirmPay(scope.row)"
>修复支付状态</el-button>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="160"/>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
@@ -75,7 +64,7 @@
</template>
<script setup name="PayOrder" lang="ts">
import { confirmPayOrder, listPayOrder } from '@/api/fishery/payOrder';
import { listPayOrder } from '@/api/fishery/payOrder';
import { PayOrderVO, PayOrderQuery } from '@/api/fishery/payOrder/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -131,23 +120,6 @@ const handleExport = () => {
}, `payOrder_${new Date().getTime()}.xlsx`)
}
/** 主动确认并修复微信支付状态 */
const handleConfirmPay = async (row: PayOrderVO) => {
await proxy?.$modal.confirm('是否向微信支付查询并修复该订单状态?');
loading.value = true;
try {
const res = await confirmPayOrder(row.id);
if (res.code === 200) {
proxy?.$modal.msgSuccess('修复成功');
} else {
proxy?.$modal.msgWarning(res.msg || '微信侧尚未确认支付成功');
}
await getList();
} finally {
loading.value = false;
}
}
onMounted(() => {
getList();
});

View File

@@ -37,9 +37,9 @@
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['fishery:pond:add']">新增</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['fishery:pond:edit']">修改</el-button>
</el-col> -->
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['fishery:pond:remove']"
>删除</el-button

View File

@@ -1,586 +0,0 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
<el-form-item label="用户信息" prop="params.userKeyword">
<el-input v-model="queryParams.params.userKeyword" placeholder="请输入用户名或手机号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="设备编号" prop="serialNum">
<el-input v-model="queryParams.serialNum" placeholder="请输入设备编号" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="设备名称" prop="deviceName">
<el-input v-model="queryParams.deviceName" placeholder="请输入设备名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="塘口名称" prop="params.pondKeyword">
<el-input v-model="queryParams.params.pondKeyword" placeholder="请输入塘口名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="在线状态" prop="params.onlineStatus">
<el-select v-model="queryParams.params.onlineStatus" placeholder="请选择在线状态" clearable style="width: 180px" @change="handleQuery">
<el-option v-for="item in onlineStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" border :data="deviceList" row-key="id">
<el-table-column label="用户名" align="center" prop="userName" min-width="120" />
<el-table-column label="手机号" align="center" prop="mobilePhone" min-width="120" />
<el-table-column label="设备编号" align="center" prop="serialNum" min-width="150" />
<el-table-column label="设备名称" align="center" prop="deviceName" min-width="130" />
<el-table-column label="塘口名称" align="center" prop="pondName" min-width="120" />
<el-table-column label="在线状态" align="center" width="120">
<template #default="scope">
<el-tag :type="getStatusTagType(scope.row)" effect="light">
{{ getStatusText(scope.row) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="盐度补偿" align="center" prop="salinityCompensation" width="120">
<template #default="scope">
{{ formatValue(scope.row.salinityCompensation) }}
</template>
</el-table-column>
<!-- <el-table-column label="线性补偿" align="center" prop="phaseCompensation" width="120">
<template #default="scope">
{{ formatValue(scope.row.phaseCompensation) }}
</template>
</el-table-column>
<el-table-column label="相位差补偿" align="center" prop="phasedifCompensation" width="120">
<template #default="scope">
{{ formatValue(scope.row.phasedifCompensation) }}
</template>
</el-table-column>
<el-table-column label="温度补偿" align="center" prop="temperatureCompensation" width="120">
<template #default="scope">
{{ formatValue(scope.row.temperatureCompensation) }}
</template>
</el-table-column> -->
<el-table-column label="最后上数时间" align="center" prop="lastReportTime" min-width="170">
<template #default="scope">
<span>{{ parseTime(scope.row.lastReportTime, '{y}-{m}-{d} {h}:{i}:{s}') || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" width="260">
<template #default="scope">
<el-button link type="primary" icon="Refresh" :loading="statusLoadingId === scope.row.id" @click="refreshStatus(scope.row)">刷新状态</el-button>
<el-button link type="primary" icon="Position" @click="openCommand(scope.row)" v-hasPermi="['fishery:solarControl:command']">下发指令</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<el-dialog v-model="commandOpen" :title="commandTitle" width="680px" append-to-body destroy-on-close>
<template v-if="currentDevice">
<div class="device-summary">
<div>
<div class="device-summary__name">{{ currentDevice.deviceName || '-' }}</div>
<div class="device-summary__serial">{{ currentDevice.serialNum }}</div>
</div>
<el-tag :type="getStatusTagType(currentDevice)" size="large">{{ getStatusText(currentDevice) }}</el-tag>
</div>
<el-form ref="commandFormRef" :model="commandForm" :rules="commandRules" label-width="130px">
<el-form-item label="下发属性" prop="identifier">
<el-select v-model="commandForm.identifier" filterable placeholder="请选择属性" style="width: 100%" @change="handlePropertyChange">
<el-option-group v-for="group in propertyGroups" :key="group.label" :label="group.label">
<el-option v-for="item in group.options" :key="item.identifier" :label="item.name" :value="item.identifier">
<span>{{ item.name }}</span>
<span class="property-option-code">{{ item.identifier }}</span>
</el-option>
</el-option-group>
</el-select>
</el-form-item>
<template v-if="selectedProperty">
<el-form-item v-if="selectedProperty.type === 'enum'" :label="selectedProperty.name" prop="value">
<el-select v-model="commandForm.value" placeholder="请选择值" style="width: 100%">
<el-option v-for="option in selectedProperty.options" :key="String(option.value)" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
<template v-else-if="selectedProperty.type === 'struct'">
<el-form-item
v-for="field in selectedProperty.fields"
:key="field.identifier"
:label="field.name"
:prop="`structValue.${field.identifier}`"
:rules="[{ required: true, message: `${field.name}不能为空`, trigger: 'blur' }]"
>
<el-input-number
v-model="commandForm.structValue[field.identifier]"
:min="field.min"
:max="field.max"
:step="field.step"
:precision="field.precision"
controls-position="right"
style="width: 100%"
/>
</el-form-item>
</template>
<el-form-item v-else :label="selectedProperty.name" prop="value">
<el-input-number
v-model="commandForm.value"
:min="selectedProperty.min"
:max="selectedProperty.max"
:step="selectedProperty.step"
:precision="selectedProperty.precision"
controls-position="right"
style="width: 100%"
/>
</el-form-item>
<div class="property-meta">
<span>{{ selectedProperty.identifier }}</span>
<span v-if="selectedProperty.rangeText">{{ selectedProperty.rangeText }}</span>
<span v-if="selectedProperty.unit">{{ selectedProperty.unit }}</span>
</div>
</template>
</el-form>
</template>
<template #footer>
<div class="command-footer">
<el-button @click="commandOpen = false">取消</el-button>
<el-button type="primary" icon="Position" :loading="submitLoading" @click="submitCommand">下发</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="SolarControl" lang="ts">
import { listSolarDevice } from '@/api/fishery/device';
import { DeviceQuery, DeviceVO } from '@/api/fishery/device/types';
import { getDeviceStatus, sendSolarPropertyCommand } from '@/api/iot';
type PropertyType = 'float' | 'double' | 'int' | 'enum' | 'struct';
interface PropertyOption {
identifier: string;
name: string;
type: PropertyType;
min?: number;
max?: number;
step?: number;
precision?: number;
unit?: string;
rangeText?: string;
options?: Array<{ label: string; value: number }>;
fields?: Array<Required<Pick<PropertyOption, 'identifier' | 'name' | 'min' | 'max' | 'step' | 'precision'>> & { unit?: string }>;
defaultValue?: number;
}
interface SolarDeviceVO extends DeviceVO {
online?: boolean;
statusCode?: number;
}
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const queryFormRef = ref<ElFormInstance>();
const commandFormRef = ref<ElFormInstance>();
const deviceList = ref<SolarDeviceVO[]>([]);
const loading = ref(false);
const showSearch = ref(true);
const total = ref(0);
const commandOpen = ref(false);
const currentDevice = ref<SolarDeviceVO>();
const submitLoading = ref(false);
const statusLoadingId = ref<string | number>();
const onlineStatusOptions = [
{ label: '在线', value: 'online' },
{ label: '离线', value: 'offline' }
];
const data = reactive<PageData<{}, DeviceQuery>>({
form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
serialNum: undefined,
deviceName: undefined,
deviceType: 3,
params: {
userKeyword: undefined,
onlineStatus: undefined,
pondKeyword: undefined
}
},
rules: {}
});
const { queryParams } = toRefs(data);
const propertyGroups = [
{
label: '常用设置',
options: [
numberProperty('salinitySet', '盐度设置', 'float', 0, 100, 0.01, undefined, 0),
enumProperty('PowerMode', '电源模式', {
0: '太阳能网控常驻模式',
1: '太阳能网控节能模式',
2: '适配器网控'
}),
numberProperty('WakeUpTime', '休眠时长', 'int', 1, 2147483647, 1, 'min', 10),
numberProperty('BootToSleepTimeout', '工作时长', 'int', 2, 2147483647, 1, 'min', 2),
numberProperty('updatatime', '数据上报时间', 'int', 0, 65535, 1, 's', 60),
numberProperty('FirstBootToSleepTimeout', '首次上电工作时间', 'int', 3, 2147483647, 1, 'min', 3)
]
},
{
label: '校准与补偿',
options: [
enumProperty('correct', '校准', {
1: '下发溶解氧传感器校准指令',
2: '下发盐度传感器校准指令',
11: '氧探头饱和校准',
12: '氧探头无氧校准',
13: '氧探头低温系数设置',
14: '氧探头高温系数设置',
31: '下发pH传感器校准第1步',
32: '下发pH传感器校准第2步'
}),
numberProperty('phasedif_compensation', '相位差补偿', 'float', -10, 100, 0.001, '°', 0),
numberProperty('phase_compensation', '线性补偿', 'float', 0, 10, 0.0001, undefined, 1),
numberProperty('temperature_compensation', '温度补偿', 'float', -10, 10, 0.001, '°C', 0),
numberProperty('stdsatphase', '标准饱和相位差', 'double', 0, 360, 0.01, '°', 0),
numberProperty('stdphaseinterval', '标准相位区间', 'double', 0, 360, 0.01, '°', 0)
]
},
{
label: '高级参数',
options: [
numberProperty('SaturationPoint', '饱和相位差测量值', 'float', -1000, 1000, 0.01, '°', 0),
numberProperty('AnaerobicPoint', '无氧相位差测量值', 'float', -1000, 1000, 0.01, '°', 0),
numberProperty('LowtempCOEF', '低温系数', 'float', -1000, 1000, 0.0001, undefined, 0),
numberProperty('HightempCOEF', '高温系数', 'float', -1000, 1000, 0.0001, undefined, 0),
numberProperty('ammonia_nitrogen', '氨氮', 'float', 0, 1000, 0.01, 'mg/L', 0),
numberProperty('TempPobeOffSet1', '温度偏移量通道1', 'float', -12.7, 12.7, 0.1, '°C', 0),
numberProperty('TempPobeOffSet2', '温度偏移量通道2', 'float', -12.7, 12.7, 1, '°C', 0),
structProperty('CorrectCharge', '电量校准', [
numberProperty('lowvoltage', '低压', 'float', 0, 10, 0.01, 'V', 0),
numberProperty('highvoltage', '高压', 'float', 0, 10, 0.01, 'V', 0)
])
]
}
];
const propertyList = computed(() => propertyGroups.flatMap((group) => group.options));
const selectedProperty = computed(() => propertyList.value.find((item) => item.identifier === commandForm.identifier));
const commandTitle = computed(() => `太阳能网控指令 - ${currentDevice.value?.deviceName || ''}`);
const commandForm = reactive({
identifier: 'PowerMode',
value: undefined as number | undefined,
structValue: {} as Record<string, number | undefined>
});
const commandRules = {
identifier: [{ required: true, message: '请选择下发属性', trigger: 'change' }],
value: [{ required: true, message: '属性值不能为空', trigger: 'blur' }]
};
function numberProperty(
identifier: string,
name: string,
type: Extract<PropertyType, 'float' | 'double' | 'int'>,
min: number,
max: number,
step: number,
unit?: string,
defaultValue?: number
): PropertyOption {
return {
identifier,
name,
type,
min,
max,
step,
unit,
precision: getPrecision(step),
rangeText: `${min} - ${max}`,
defaultValue
};
}
function enumProperty(identifier: string, name: string, options: Record<number, string>): PropertyOption {
const values = Object.entries(options).map(([value, label]) => ({ value: Number(value), label }));
return {
identifier,
name,
type: 'enum',
options: values,
defaultValue: values[0]?.value
};
}
function structProperty(identifier: string, name: string, fields: PropertyOption[]): PropertyOption {
return {
identifier,
name,
type: 'struct',
fields: fields.map((field) => ({
identifier: field.identifier,
name: field.name,
min: field.min ?? 0,
max: field.max ?? 0,
step: field.step ?? 1,
precision: field.precision ?? 0,
unit: field.unit
}))
};
}
function getPrecision(step: number) {
const text = String(step);
if (!text.includes('.')) {
return 0;
}
return text.split('.')[1].length;
}
const getList = async () => {
loading.value = true;
try {
queryParams.value.deviceType = 3;
const res = await listSolarDevice(queryParams.value);
deviceList.value = ((res.rows || []) as SolarDeviceVO[]).map(initStatusFromList);
total.value = res.total;
await refreshVisibleStatuses();
sortDeviceListByOnline();
} finally {
loading.value = false;
}
};
const initStatusFromList = (item: SolarDeviceVO) => {
if (item.onlineStatus?.toLowerCase() === 'online') {
item.online = true;
item.statusCode = 1;
} else {
item.online = false;
item.statusCode = 3;
}
return item;
};
const refreshVisibleStatuses = async () => {
await Promise.all(
deviceList.value.map(async (item) => {
try {
await refreshStatus(item, false);
} catch {
// 列表已有后端状态,单台云端状态刷新失败时不影响整页展示。
}
})
);
};
const refreshStatus = async (row: SolarDeviceVO, showMessage = true) => {
if (!row.serialNum) {
row.online = false;
row.statusCode = 0;
return;
}
statusLoadingId.value = row.id;
try {
const res = await getDeviceStatus(3, row.serialNum);
row.statusCode = res.data;
row.online = res.data === 1;
if (currentDevice.value?.id === row.id) {
currentDevice.value = row;
}
sortDeviceListByOnline();
if (showMessage) {
ElMessage.success('状态已刷新');
}
} finally {
statusLoadingId.value = undefined;
}
};
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
const resetQuery = () => {
queryFormRef.value?.resetFields();
queryParams.value.deviceType = 3;
queryParams.value.params = {
userKeyword: undefined,
onlineStatus: undefined,
pondKeyword: undefined
};
getList();
};
const openCommand = async (row: SolarDeviceVO) => {
currentDevice.value = row;
resetCommandForm();
commandOpen.value = true;
await refreshStatus(row, false);
};
const resetCommandForm = () => {
commandForm.identifier = 'PowerMode';
handlePropertyChange('PowerMode');
nextTick(() => commandFormRef.value?.clearValidate());
};
const handlePropertyChange = (identifier: string) => {
const property = propertyList.value.find((item) => item.identifier === identifier);
commandForm.value = property?.defaultValue;
commandForm.structValue = {};
if (property?.type === 'struct') {
property.fields?.forEach((field) => {
commandForm.structValue[field.identifier] = 0;
});
}
};
const submitCommand = async () => {
if (!currentDevice.value || !selectedProperty.value) {
return;
}
await commandFormRef.value?.validate();
const property = selectedProperty.value;
const value = property.type === 'struct' ? { ...commandForm.structValue } : commandForm.value;
submitLoading.value = true;
try {
await sendSolarPropertyCommand({
id: currentDevice.value.id,
properties: {
[property.identifier]: value
}
});
ElMessage.success('下发成功');
commandOpen.value = false;
await getList();
} finally {
submitLoading.value = false;
}
};
const getStatusText = (row: SolarDeviceVO) => {
if (row.online) {
return '在线';
}
if (row.statusCode === 3) {
return '离线';
}
if (row.statusCode === 8) {
return '禁用';
}
if (row.statusCode === 0) {
return '未激活';
}
return '未知';
};
const getStatusTagType = (row: SolarDeviceVO) => {
if (row.online) {
return 'success';
}
if (row.statusCode === 3) {
return 'warning';
}
if (row.statusCode === 8) {
return 'danger';
}
return 'info';
};
const formatValue = (value: unknown) => {
if (value === undefined || value === null || value === '') {
return '-';
}
return value;
};
const sortDeviceListByOnline = () => {
deviceList.value = deviceList.value
.map((item, index) => ({ item, index }))
.sort((a, b) => {
const onlineDiff = Number(Boolean(b.item.online)) - Number(Boolean(a.item.online));
if (onlineDiff !== 0) {
return onlineDiff;
}
return a.index - b.index;
})
.map(({ item }) => item);
};
onMounted(() => {
getList();
});
</script>
<style scoped lang="scss">
.device-summary {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 12px 14px;
margin-bottom: 12px;
background: #f6f8fb;
border: 1px solid #e4e7ed;
border-radius: 6px;
}
.device-summary__name {
font-size: 16px;
font-weight: 600;
color: #303133;
}
.device-summary__serial {
margin-top: 4px;
font-size: 13px;
color: #606266;
}
.property-option-code {
float: right;
margin-left: 16px;
color: #909399;
font-size: 12px;
}
.property-meta {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-left: 130px;
margin-bottom: 16px;
color: #909399;
font-size: 12px;
}
.command-footer {
display: flex;
justify-content: flex-end;
gap: 8px;
}
</style>

View File

@@ -38,9 +38,9 @@
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['fishery:timingCtrl:add']">新增</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['fishery:timingCtrl:edit']">修改</el-button>
</el-col> -->
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['fishery:timingCtrl:remove']">删除</el-button>
</el-col>

View File

@@ -25,15 +25,15 @@
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['fishery:userRelation:add']">新增</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['fishery:userRelation:edit']">修改</el-button>
</el-col> -->
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['fishery:userRelation:remove']">删除</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['fishery:userRelation:export']">导出</el-button>
</el-col> -->
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>

View File

@@ -6,10 +6,10 @@
<lang-select />
</div> -->
<el-form-item v-if="tenantEnabled" prop="tenantId">
<el-select v-model="loginForm.tenantId" filterable :placeholder="proxy.$t('login.selectPlaceholder')" style="width: 100%">
<el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option>
<template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
</el-select>
<!-- <el-select v-model="loginForm.tenantId" filterable :placeholder="proxy.$t('login.selectPlaceholder')" style="width: 100%">-->
<!-- <el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option>-->
<!-- <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>-->
<!-- </el-select>-->
</el-form-item>
<el-form-item prop="username">
<el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" :placeholder="proxy.$t('login.username')">
@@ -87,8 +87,8 @@ const { t } = useI18n();
const loginForm = ref<LoginData>({
tenantId: '111111',
username: 'admin',
password: 'admin123',
username: 'fishery',
password: 'fishery@123',
rememberMe: false,
code: '',
uuid: ''
@@ -216,7 +216,7 @@ const doSocialLogin = (type: string) => {
onMounted(() => {
getCode();
initTenantList();
// initTenantList();
getLoginData();
});
</script>
@@ -301,7 +301,7 @@ onMounted(() => {
backdrop-filter: blur(2px);
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.8);
z-index: 2;
span {
padding: 5px 15px;
border-radius: 15px;
@@ -309,12 +309,12 @@ onMounted(() => {
backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
a {
color:rgb(255, 255, 255) !important;
text-decoration: none;
transition: color 0.3s ease;
&:hover {
color: #66b1ff !important;
text-shadow: 0 0 5px rgba(64, 158, 255, 0.5);

View File

@@ -25,7 +25,7 @@ export default defineConfig(({ mode, command }) => {
proxy: {
[env.VITE_APP_BASE_API]: {
// target: 'http://localhost:8080',
target: 'https://api.yuceyun.cn/prod-api/',
target: 'https://www.qdintc.com/fishery-api/',
changeOrigin: true,
ws: true,
rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')