buddhism/pages/memorial/addMemorial.vue

934 lines
20 KiB
Vue
Raw Normal View History

2025-10-16 17:34:44 +08:00
<template>
<view class="page">
<base-background />
<!-- 使用自定义导航栏组件 -->
2025-10-17 11:07:52 +08:00
<custom-navbar ref="customNavbar" title="添加牌位" />
2025-10-16 17:34:44 +08:00
<view class="content">
<!-- 扫码获取SN和MAC -->
<view class="scan-section">
<view class="section-title">设备信息</view>
<view class="scan-container">
<view class="scan-btn" @click="handleScanCode">
<view class="qrcode-icon">
<image
mode="aspectFit"
src="https://api.ccttiot.com/smartmeter/img/static/uy7BNwAMIKwvstqFnRhs"
/>
</view>
<view class="scan-text">扫码获取设备信息</view>
</view>
</view>
<!-- 显示SN和MAC信息 -->
<view v-if="deviceInfo.sn || deviceInfo.mac" class="device-info">
2025-10-17 09:04:21 +08:00
<view v-if="deviceInfo.code" class="info-item">
<view class="info-label">牌位编号</view>
<view class="info-value"
>{{ deviceInfo.code }}(牌位已添加编号)
</view>
</view>
<view v-if="deviceInfo.mac" class="info-item">
<view class="info-label">mac</view>
<view class="info-value">{{ deviceInfo.mac }}</view>
</view>
<view v-if="deviceInfo.sn" class="info-item">
<view class="info-label">sn</view>
<view class="info-value">{{ deviceInfo.sn }}</view>
2025-10-16 17:34:44 +08:00
</view>
</view>
</view>
<!-- 牌位信息表单 -->
<view class="form-section">
<view class="section-title">牌位信息</view>
<view class="form-container">
<!-- 牌位编号 -->
<view class="form-item">
<view class="label"
>牌位编号
<text class="required">*</text>
</view>
<input
v-model="formData.code"
class="input"
maxlength="20"
placeholder="请输入牌位编号"
/>
</view>
<!-- 区域选择 -->
<view class="form-item">
<view class="label"
>所属区域
<text class="required">*</text>
</view>
2025-10-16 17:47:18 +08:00
<view class="picker" @click="showRegionSelect">
2025-10-16 17:34:44 +08:00
<view class="picker-text">
2025-10-16 17:47:18 +08:00
{{ selectedRegionText || "请选择区域" }}
2025-10-16 17:34:44 +08:00
</view>
2025-10-16 17:47:18 +08:00
<view class="picker-arrow">></view>
</view>
2025-10-16 17:34:44 +08:00
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="button-container">
<view class="btn cancel-btn" @click="handleCancel">取消</view>
<view
:class="{ disabled: !canSubmit }"
class="btn confirm-btn"
@click="handleConfirm"
>确认添加
</view>
</view>
2025-10-17 11:07:52 +08:00
<!-- 未添加设备列表 -->
2025-10-17 10:26:33 +08:00
<view class="device-list-section">
<view class="section-title">
2025-10-17 11:07:52 +08:00
<text>未添加牌位的设备列表</text>
2025-10-17 10:26:33 +08:00
<view v-if="winB_Pagination.total" class="count-badge">
{{ winB_Pagination.total }}
</view>
</view>
<!-- 空状态 -->
<view v-if="winB_List.length === 0" class="empty-state">
<view class="empty-icon">📱</view>
2025-10-17 11:07:52 +08:00
<view class="empty-text">暂无未添加设备</view>
2025-10-17 11:28:16 +08:00
<view class="empty-tip">请先进行录入设备</view>
2025-10-17 10:26:33 +08:00
</view>
<!-- 设备列表 -->
<view v-else class="device-list">
<view
v-for="(item, index) in winB_List"
:key="index"
class="device-item"
@click="selectDevice(item)"
>
<view class="device-header">
<view class="device-sn">SN:{{ item.sn || "未知SN" }}</view>
<view class="select-hint">点击选择</view>
</view>
<view class="device-sn">MAC:{{ item.mac || "未知MAC" }}</view>
<view v-if="item.code" class="device-sn"
>MAC:{{ item.code || "未知code" }}
</view>
</view>
</view>
</view>
2025-10-16 17:34:44 +08:00
</view>
2025-10-16 17:47:18 +08:00
<!-- 区域选择器 -->
<u-select
v-model="showRegionPicker"
:list="regionList"
mode="mutil-column-auto"
@cancel="onRegionCancel"
@confirm="onRegionConfirm"
></u-select>
2025-10-16 17:34:44 +08:00
</view>
</template>
<script>
2025-10-17 09:31:23 +08:00
import { createPagination } from "../../composables/winB_Pagination";
import { getMemorialList } from "../../api/memorial/memorial";
2025-10-17 09:31:23 +08:00
2025-10-16 17:34:44 +08:00
export default {
2025-10-17 09:31:23 +08:00
mixins: [
createPagination({
fetchData: getMemorialList,
2025-10-17 09:31:23 +08:00
mode: "loadMore", // 或 "pager"
2025-10-17 10:26:33 +08:00
pageSize: 2,
2025-10-17 09:31:23 +08:00
autoLoad: false, // 手动触发时设为 false
}),
],
created() {
// 条件就绪后触发首次加载
this.winB_UpdateParams();
},
2025-10-16 17:34:44 +08:00
data() {
return {
// 设备信息
deviceInfo: {
sn: "",
mac: "",
2025-10-17 09:04:21 +08:00
code: "",
2025-10-16 17:34:44 +08:00
id: "",
},
// 表单数据
formData: {
code: "",
2025-10-17 09:04:21 +08:00
id: "",
2025-10-16 17:34:44 +08:00
regionId: "",
},
2025-10-16 17:47:18 +08:00
// 区域相关数据
regionList: [], // 区域树形数据
showRegionPicker: false, // 控制区域选择器显示
selectedRegion: null, // 选中的区域信息
selectedRegionText: "", // 选中的区域显示文本
2025-10-16 17:34:44 +08:00
// 加载状态
loading: false,
};
},
computed: {
// 是否可以提交
canSubmit() {
return (
this.deviceInfo.sn &&
this.deviceInfo.mac &&
this.formData.code.trim() &&
2025-10-16 17:47:18 +08:00
this.selectedRegion
2025-10-16 17:34:44 +08:00
);
},
},
2025-10-17 09:31:23 +08:00
onReachBottom() {
this.winB_LoadMore();
},
2025-10-16 17:47:18 +08:00
async onLoad() {
// 页面加载时获取区域数据
await this.loadRegionData();
// 尝试从缓存中恢复区域选择
this.loadCachedRegion();
},
2025-10-16 17:34:44 +08:00
methods: {
// 扫码获取SN
async handleScanCode() {
try {
uni.showLoading({ title: "扫码中...", mask: true });
// 调用扫码功能
const res = await new Promise((resolve, reject) => {
uni.scanCode({
onlyFromCamera: true,
scanType: ["qrCode"],
success: (result) => {
resolve(result);
},
fail: (error) => {
reject(error);
},
});
});
uni.hideLoading();
if (res.result) {
// 解析二维码内容提取SN参数
let sn = null;
let queryParams = res.result.split("?")[1];
if (queryParams) {
let params = queryParams.split("&");
params.forEach((param) => {
let [key, value] = param.split("=");
if (key === "s") {
sn = value;
}
});
}
// 如果没有从URL参数中获取到SN尝试直接使用结果
if (!sn) {
sn = res.result.trim();
}
console.log("扫码获取到SN:", sn);
console.log("原始扫码结果:", res.result);
if (sn) {
// 通过SN获取设备信息
await this.getDeviceInfoBySn(sn);
} else {
uni.showToast({
title: "未找到有效的SN码",
icon: "none",
});
}
}
} catch (error) {
uni.hideLoading();
console.error("扫码失败:", error);
if (error.errMsg && error.errMsg.includes("cancel")) {
// 用户取消扫码,不显示错误提示
return;
}
uni.showToast({
title: "扫码失败,请重试",
icon: "none",
});
}
},
// 通过SN获取设备信息
async getDeviceInfoBySn(sn) {
try {
uni.showLoading({ title: "获取设备信息...", mask: true });
// 调用API获取设备信息
const res = await this.$request.get("/bst/memorial/getBySn", {
sn: sn,
});
uni.hideLoading();
console.log("@@@@@@@@@@@@@@@@@@@@", res);
if (res) {
const deviceData = res;
this.deviceInfo = {
sn: deviceData.sn || sn,
mac: deviceData.mac || "",
2025-10-17 09:04:21 +08:00
code: deviceData.code || "",
2025-10-16 17:34:44 +08:00
id: deviceData.id || "",
};
uni.showToast({
title: "设备信息获取成功",
icon: "success",
});
console.log("设备信息:", this.deviceInfo);
} else {
throw new Error(res?.msg || "获取设备信息失败");
}
} catch (error) {
uni.hideLoading();
console.error("获取设备信息失败:", error);
uni.showToast({
title: error.message || "获取设备信息失败",
icon: "none",
});
}
},
2025-10-16 17:47:18 +08:00
// 加载区域数据
async loadRegionData() {
try {
// 从缓存中获取templeId如果没有则使用默认值
const templeId = uni.getStorageSync("templeId") || "12";
const res = await this.$request.get(`/bst/region/listTree/${templeId}`);
if (res && res.data) {
// 转换数据格式为u-select需要的格式
this.regionList = this.transformRegionData(res.data);
console.log("区域数据加载成功:", this.regionList);
} else {
throw new Error("获取区域数据失败");
}
} catch (error) {
console.error("加载区域数据失败:", error);
uni.showToast({
title: "加载区域数据失败",
icon: "none",
});
}
},
// 转换区域数据格式
transformRegionData(data) {
if (!data || !data.children) return [];
2025-10-17 11:07:52 +08:00
return data.children
.filter((floor) => floor.id !== "41") // 过滤掉 value 等于 "41" 的项目
.map((floor) => ({
label: floor.label,
value: floor.id,
children: floor.children
? floor.children.map((area) => ({
label: area.label,
value: area.id,
}))
: [],
}));
2025-10-16 17:47:18 +08:00
},
// 显示区域选择器
showRegionSelect() {
if (this.regionList.length === 0) {
uni.showToast({
title: "区域数据加载中,请稍后",
icon: "none",
});
return;
}
this.showRegionPicker = true;
},
// 区域选择确认
onRegionConfirm(e) {
console.log("区域选择结果:", e);
if (e && e.length >= 2) {
2025-10-17 09:04:21 +08:00
const [floor, area] = e;
2025-10-16 17:47:18 +08:00
this.selectedRegion = {
floorId: floor.value,
floorName: floor.label,
areaId: area.value,
areaName: area.label,
};
this.selectedRegionText = `${floor.label} - ${area.label}`;
this.formData.regionId = area.value;
// 缓存区域选择
this.cacheRegionSelection();
uni.showToast({
title: "区域选择成功",
icon: "success",
});
}
},
// 区域选择取消
onRegionCancel() {
this.showRegionPicker = false;
},
// 缓存区域选择
cacheRegionSelection() {
if (this.selectedRegion) {
uni.setStorageSync("lastSelectedRegion", this.selectedRegion);
uni.setStorageSync("lastSelectedRegionText", this.selectedRegionText);
}
},
// 从缓存加载区域选择
loadCachedRegion() {
try {
const cachedRegion = uni.getStorageSync("lastSelectedRegion");
const cachedRegionText = uni.getStorageSync("lastSelectedRegionText");
if (cachedRegion && cachedRegionText) {
this.selectedRegion = cachedRegion;
this.selectedRegionText = cachedRegionText;
this.formData.regionId = cachedRegion.areaId;
console.log("已恢复缓存的区域选择:", cachedRegion);
}
} catch (error) {
console.error("加载缓存区域失败:", error);
}
2025-10-16 17:34:44 +08:00
},
// 取消操作
handleCancel() {
uni.navigateBack();
},
// 确认添加
async handleConfirm() {
if (!this.canSubmit) {
uni.showToast({
title: "请完善必填信息",
icon: "none",
});
return;
}
// 表单验证
if (!this.validateForm()) {
return;
}
try {
uni.showLoading({ title: "添加中...", mask: true });
// 构建请求数据
const requestData = {
id: this.deviceInfo.id, // 使用设备ID
code: this.formData.code,
2025-10-16 17:47:18 +08:00
regionId: this.selectedRegion ? this.selectedRegion.areaId : "",
2025-10-16 17:34:44 +08:00
};
console.log("提交数据:", requestData);
// 调用API添加牌位
const res = await this.$request.put("/bst/memorial", requestData);
uni.hideLoading();
if (res && (res.code === 200 || res.status === 200)) {
uni.showToast({
title: "添加成功",
icon: "success",
});
2025-10-17 11:28:16 +08:00
this.winB_Refresh();
2025-10-16 17:34:44 +08:00
} else {
throw new Error(res?.msg || "添加失败");
}
} catch (error) {
uni.hideLoading();
console.error("添加牌位失败:", error);
uni.showToast({
title: error.message || "添加失败,请重试",
icon: "none",
});
}
},
// 表单验证
validateForm() {
if (!this.formData.code.trim()) {
uni.showToast({
title: "请输入牌位编号",
icon: "none",
});
return false;
}
2025-10-16 17:47:18 +08:00
if (!this.selectedRegion) {
2025-10-16 17:34:44 +08:00
uni.showToast({
title: "请选择所属区域",
icon: "none",
});
return false;
}
return true;
},
2025-10-17 10:26:33 +08:00
// 选择设备
selectDevice(item) {
console.log("选择设备:", item);
// 将设备信息填充到顶部设备信息中
this.deviceInfo = {
sn: item.sn || "",
mac: item.mac || "",
code: item.code || "",
id: item.id || "",
};
// 如果设备有编号,也填充到表单中
if (item.code) {
this.formData.code = item.code;
}
// 显示成功提示
uni.showToast({
title: "设备信息已填充",
icon: "success",
duration: 1500,
});
// 滚动到顶部
uni.pageScrollTo({
scrollTop: 0,
duration: 300,
});
},
2025-10-16 17:34:44 +08:00
},
};
</script>
<style lang="scss" scoped>
.page {
width: 100%;
min-height: 100vh;
background: #f5f5f5;
}
.content {
padding: 20rpx;
padding-bottom: 120rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
padding-left: 10rpx;
border-left: 6rpx solid #4a90e2;
2025-10-17 10:26:33 +08:00
display: flex;
align-items: center;
justify-content: space-between;
}
.count-badge {
background: #4a90e2;
color: #fff;
font-size: 20rpx;
padding: 4rpx 12rpx;
border-radius: 20rpx;
min-width: 32rpx;
text-align: center;
line-height: 1.2;
2025-10-16 17:34:44 +08:00
}
.scan-section {
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}
.scan-container {
margin-bottom: 30rpx;
}
.scan-btn {
2025-10-17 10:26:33 +08:00
padding: 30rpx;
2025-10-16 17:34:44 +08:00
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
2025-10-17 10:26:33 +08:00
//height: 200rpx;
2025-10-16 17:34:44 +08:00
border: 3rpx dashed #4a90e2;
border-radius: 16rpx;
background: #f8fbff;
transition: all 0.3s ease;
&:active {
background: #e8f4ff;
transform: scale(0.98);
}
}
.qrcode-icon {
width: 60rpx;
height: 60rpx;
margin-bottom: 10rpx;
image {
width: 100%;
height: 100%;
}
}
.scan-text {
font-size: 28rpx;
color: #4a90e2;
font-weight: 500;
}
.device-info {
background: #f8f9fa;
border-radius: 12rpx;
padding: 20rpx;
border: 2rpx solid #e9ecef;
}
.info-item {
display: flex;
align-items: center;
margin-bottom: 15rpx;
&:last-child {
margin-bottom: 0;
}
}
.info-label {
font-size: 26rpx;
color: #666;
width: 140rpx;
flex-shrink: 0;
}
.info-value {
font-size: 26rpx;
color: #333;
font-weight: 500;
flex: 1;
word-break: break-all;
}
.form-section {
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}
.form-container {
// 表单容器样式
}
.form-item {
margin-bottom: 30rpx;
&:last-child {
margin-bottom: 0;
}
}
.label {
font-size: 28rpx;
color: #666;
margin-bottom: 12rpx;
font-weight: 500;
}
.required {
color: #ff4757;
margin-left: 4rpx;
}
.input {
width: 100%;
height: 80rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
background: #fff;
&:focus {
border-color: #4a90e2;
}
}
.picker {
width: 100%;
height: 80rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
display: flex;
align-items: center;
padding: 0 20rpx;
background: #fff;
box-sizing: border-box;
}
.picker-text {
font-size: 28rpx;
color: #333;
flex: 1;
}
2025-10-16 17:47:18 +08:00
.picker-arrow {
color: #999;
font-size: 24rpx;
transform: rotate(90deg);
}
2025-10-16 17:34:44 +08:00
.textarea {
width: 100%;
min-height: 120rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
padding: 20rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
background: #fff;
resize: none;
&:focus {
border-color: #4a90e2;
}
}
.button-container {
display: flex;
gap: 20rpx;
padding: 0 20rpx;
}
.btn {
flex: 1;
height: 88rpx;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: bold;
transition: all 0.3s ease;
}
.cancel-btn {
background: #f5f5f5;
color: #666;
border: 2rpx solid #e5e5e5;
&:active {
background: #e8e8e8;
}
}
.confirm-btn {
background: #4a90e2;
color: #fff;
box-shadow: 0 4rpx 12rpx rgba(74, 144, 226, 0.3);
&:active {
background: #357abd;
}
&.disabled {
background: #ccc;
color: #999;
box-shadow: none;
&:active {
background: #ccc;
}
}
}
2025-10-17 10:26:33 +08:00
// 设备列表样式
.device-list-section {
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin: 30rpx 0;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}
.empty-state {
text-align: center;
padding: 60rpx 20rpx;
color: #999;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.empty-text {
font-size: 28rpx;
color: #666;
margin-bottom: 10rpx;
}
.empty-tip {
font-size: 24rpx;
color: #999;
}
.device-list {
// 设备列表容器
}
.device-item {
background: #f8f9fa;
border: 2rpx solid #e9ecef;
border-radius: 12rpx;
padding: 20rpx;
margin-bottom: 20rpx;
transition: all 0.3s ease;
cursor: pointer;
&:last-child {
margin-bottom: 0;
}
&:hover {
border-color: #4a90e2;
background: #f0f7ff;
transform: translateY(-2rpx);
box-shadow: 0 6rpx 20rpx rgba(74, 144, 226, 0.15);
}
&:active {
transform: translateY(0);
box-shadow: 0 2rpx 8rpx rgba(74, 144, 226, 0.2);
}
}
.device-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15rpx;
}
.device-sn {
font-size: 28rpx;
font-weight: bold;
color: #333;
background: #e3f2fd;
padding: 6rpx 12rpx;
border-radius: 6rpx;
border-left: 4rpx solid #4a90e2;
}
.select-hint {
font-size: 22rpx;
color: #4a90e2;
background: #e8f4ff;
padding: 4rpx 8rpx;
border-radius: 4rpx;
}
.device-info {
margin-bottom: 15rpx;
}
.info-row {
display: flex;
align-items: center;
margin-bottom: 8rpx;
&:last-child {
margin-bottom: 0;
}
}
.info-label {
font-size: 24rpx;
color: #666;
width: 120rpx;
flex-shrink: 0;
}
.info-value {
font-size: 24rpx;
color: #333;
font-weight: 500;
flex: 1;
background: #fff;
padding: 4rpx 8rpx;
border-radius: 4rpx;
border-left: 3rpx solid #4a90e2;
word-break: break-all;
}
.device-actions {
display: flex;
justify-content: flex-end;
}
.action-btn {
padding: 8rpx 16rpx;
border-radius: 6rpx;
font-size: 24rpx;
transition: all 0.3s ease;
}
.select-btn {
background: #4a90e2;
color: #fff;
&:hover {
background: #357abd;
transform: scale(1.05);
}
&:active {
transform: scale(0.95);
}
}
.btn-text {
font-size: 24rpx;
font-weight: 500;
}
2025-10-16 17:34:44 +08:00
</style>