buddhism/pages/memorial/addMemorial.vue
2025-11-25 11:13:46 +08:00

939 lines
21 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="page">
<base-background />
<!-- 使用自定义导航栏组件 -->
<custom-navbar ref="customNavbar" title="添加牌位" />
<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">
<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>
</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>
<view class="picker" @click="showRegionSelect">
<view class="picker-text">
{{ selectedRegionText || "请选择区域" }}
</view>
<view class="picker-arrow">></view>
</view>
</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>
<!-- 未添加设备列表 -->
<view class="device-list-section">
<view class="section-title">
<text>未添加牌位的设备列表</text>
<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>
<view class="empty-text">暂无未添加设备</view>
<view class="empty-tip">请先进行录入设备</view>
</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>
</view>
<!-- 区域选择器 -->
<u-select
v-model="showRegionPicker"
:list="regionList"
mode="mutil-column-auto"
@cancel="onRegionCancel"
@confirm="onRegionConfirm"
></u-select>
</view>
</template>
<script>
import { createPagination } from "../../composables/winB_Pagination";
import { getMemorialList } from "../../api/memorial/memorial";
export default {
mixins: [
createPagination({
fetchData: getMemorialList,
mode: "loadMore", // 或 "pager"
pageSize: 2,
autoLoad: false, // 手动触发时设为 false
}),
],
created() {
// 条件就绪后触发首次加载
this.winB_UpdateParams();
},
data() {
return {
// 设备信息
deviceInfo: {
sn: "",
mac: "",
code: "",
id: "",
},
// 表单数据
formData: {
code: "",
id: "",
regionId: "",
},
// 区域相关数据
regionList: [], // 区域树形数据
showRegionPicker: false, // 控制区域选择器显示
selectedRegion: null, // 选中的区域信息
selectedRegionText: "", // 选中的区域显示文本
// 加载状态
loading: false,
};
},
computed: {
// 是否可以提交
canSubmit() {
return (
this.deviceInfo.sn &&
this.deviceInfo.mac &&
this.formData.code.trim() &&
this.selectedRegion
);
},
},
onReachBottom() {
this.winB_LoadMore();
},
async onLoad() {
// 页面加载时获取区域数据
await this.loadRegionData();
// 尝试从缓存中恢复区域选择
this.loadCachedRegion();
},
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 || "",
code: deviceData.code || "",
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",
});
}
},
// 加载区域数据
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 [];
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,
}))
: [],
}));
},
// 显示区域选择器
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) {
const [floor, area] = e;
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);
}
},
// 取消操作
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,
regionId: this.selectedRegion ? this.selectedRegion.areaId : "",
};
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",
});
this.winB_Refresh();
} 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;
}
if (!this.selectedRegion) {
uni.showToast({
title: "请选择所属区域",
icon: "none",
});
return false;
}
return true;
},
// 选择设备
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,
});
},
},
};
</script>
<style lang="scss" scoped>
.page {
width: 100%;
min-height: 100vh;
background: #fff1dd;
}
.content {
padding: 20rpx;
padding-bottom: 120rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #522510;
margin-bottom: 20rpx;
padding-left: 10rpx;
border-left: 6rpx solid #c7a26d;
display: flex;
align-items: center;
justify-content: space-between;
}
.count-badge {
background: #c7a26d;
color: #fff;
font-size: 20rpx;
padding: 4rpx 12rpx;
border-radius: 20rpx;
min-width: 32rpx;
text-align: center;
line-height: 1.2;
}
.scan-section {
background: #fff1dd;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 12rpx rgba(82, 37, 16, 0.08);
border: 1rpx solid rgba(199, 162, 109, 0.3);
}
.scan-container {
margin-bottom: 30rpx;
}
.scan-btn {
padding: 30rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
//height: 200rpx;
border: 3rpx dashed #c7a26d;
border-radius: 16rpx;
background: rgba(255, 241, 221, 0.5);
transition: all 0.3s ease;
&:active {
background: rgba(199, 162, 109, 0.1);
transform: scale(0.98);
}
}
.qrcode-icon {
width: 60rpx;
height: 60rpx;
margin-bottom: 10rpx;
image {
width: 100%;
height: 100%;
}
}
.scan-text {
font-size: 28rpx;
color: #c7a26d;
font-weight: 500;
}
.device-info {
background: rgba(255, 241, 221, 0.6);
border-radius: 12rpx;
padding: 20rpx;
border: 2rpx solid rgba(199, 162, 109, 0.3);
}
.info-item {
display: flex;
align-items: center;
margin-bottom: 15rpx;
&:last-child {
margin-bottom: 0;
}
}
.info-label {
font-size: 26rpx;
color: #522510;
opacity: 0.8;
width: 140rpx;
flex-shrink: 0;
}
.info-value {
font-size: 26rpx;
color: #522510;
font-weight: 500;
flex: 1;
word-break: break-all;
}
.form-section {
background: #fff1dd;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 12rpx rgba(82, 37, 16, 0.08);
border: 1rpx solid rgba(199, 162, 109, 0.3);
}
.form-item {
margin-bottom: 30rpx;
&:last-child {
margin-bottom: 0;
}
}
.label {
font-size: 28rpx;
color: #522510;
opacity: 0.8;
margin-bottom: 12rpx;
font-weight: 500;
}
.required {
color: #c7a26d;
margin-left: 4rpx;
}
.input {
width: 100%;
height: 80rpx;
border: 2rpx solid rgba(199, 162, 109, 0.3);
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
color: #522510;
box-sizing: border-box;
background: #fff1dd;
&:focus {
border-color: #c7a26d;
}
}
.picker {
width: 100%;
height: 80rpx;
border: 2rpx solid rgba(199, 162, 109, 0.3);
border-radius: 8rpx;
display: flex;
align-items: center;
padding: 0 20rpx;
background: #fff1dd;
box-sizing: border-box;
}
.picker-text {
font-size: 28rpx;
color: #522510;
flex: 1;
}
.picker-arrow {
color: #522510;
opacity: 0.6;
font-size: 24rpx;
transform: rotate(90deg);
}
.textarea {
width: 100%;
min-height: 120rpx;
border: 2rpx solid rgba(199, 162, 109, 0.3);
border-radius: 8rpx;
padding: 20rpx;
font-size: 28rpx;
color: #522510;
box-sizing: border-box;
background: #fff1dd;
resize: none;
&:focus {
border-color: #c7a26d;
}
}
.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: rgba(255, 241, 221, 0.8);
color: #522510;
opacity: 0.8;
border: 2rpx solid rgba(199, 162, 109, 0.3);
&:active {
background: rgba(199, 162, 109, 0.1);
}
}
.confirm-btn {
background: linear-gradient(135deg, #c7a26d, #b8956a);
color: #fff;
box-shadow: 0 4rpx 12rpx rgba(199, 162, 109, 0.3);
&:active {
background: linear-gradient(135deg, #b8956a, #a8855f);
}
&.disabled {
background: #ccc;
color: #999;
box-shadow: none;
&:active {
background: #ccc;
}
}
}
// 设备列表样式
.device-list-section {
background: #fff1dd;
border-radius: 16rpx;
padding: 30rpx;
margin: 30rpx 0;
box-shadow: 0 4rpx 12rpx rgba(82, 37, 16, 0.08);
border: 1rpx solid rgba(199, 162, 109, 0.3);
}
.empty-state {
text-align: center;
padding: 60rpx 20rpx;
color: #522510;
opacity: 0.6;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.empty-text {
font-size: 28rpx;
color: #522510;
opacity: 0.8;
margin-bottom: 10rpx;
}
.empty-tip {
font-size: 24rpx;
color: #522510;
opacity: 0.6;
}
.device-item {
background: rgba(255, 241, 221, 0.6);
border: 2rpx solid rgba(199, 162, 109, 0.3);
border-radius: 12rpx;
padding: 20rpx;
margin-bottom: 20rpx;
transition: all 0.3s ease;
cursor: pointer;
&:last-child {
margin-bottom: 0;
}
&:hover {
border-color: #c7a26d;
background: rgba(199, 162, 109, 0.1);
transform: translateY(-2rpx);
box-shadow: 0 6rpx 20rpx rgba(199, 162, 109, 0.15);
}
&:active {
transform: translateY(0);
box-shadow: 0 2rpx 8rpx rgba(199, 162, 109, 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: #522510;
background: rgba(199, 162, 109, 0.1);
padding: 6rpx 12rpx;
border-radius: 6rpx;
border-left: 4rpx solid #c7a26d;
}
.select-hint {
font-size: 22rpx;
color: #c7a26d;
background: rgba(199, 162, 109, 0.15);
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: #522510;
opacity: 0.8;
width: 120rpx;
flex-shrink: 0;
}
.info-value {
font-size: 24rpx;
color: #522510;
font-weight: 500;
flex: 1;
background: rgba(255, 241, 221, 0.8);
padding: 4rpx 8rpx;
border-radius: 4rpx;
border-left: 3rpx solid #c7a26d;
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: #c7a26d;
color: #fff;
&:hover {
background: #b8956a;
transform: scale(1.05);
}
&:active {
transform: scale(0.95);
}
}
.btn-text {
font-size: 24rpx;
font-weight: 500;
}
</style>