第三十二章 接下来我们开始做`灭菌整板`页面

新建 SterilizeWholeBoardPage 空页面

swift 复制代码
class SterilizeWholeBoardPageViewModel: BaseViewModel {
    
}
swift 复制代码
struct SterilizeWholeBoardPage: View {
    @StateObject private var viewModel = SterilizeWholeBoardPageViewModel()
    var body: some View {
        PageContentView(title: "灭菌整板", viewModel: viewModel) {
            EmptyView()
        }
        .makeToDetailPage()
    }
}

添加 【灭菌批号】【栈版号】【箱号】

swift 复制代码
class SterilizeWholeBoardPageViewModel: BaseViewModel {
    /// 灭菌批号
    @Published var sterilizationLotNumber:String = ""
    /// 栈版号
    @Published var stackVersionNumber:String = ""
    /// 箱号
    @Published var caseNumber:String = ""
}
swift 复制代码
struct SterilizeWholeBoardPage: View {
    ...
    var body: some View {
        PageContentView(title: "灭菌整板", viewModel: viewModel) {
            VStack(spacing: 0) {
                Spacer()
                    .frame(height: 5)
                VStack(spacing: 0) {
                    ScanTextView(title: "灭菌批号",
                                 prompt: "请输入灭菌批号",
                                 text: $viewModel.sterilizationLotNumber)
                    Divider()
                        .padding(.leading, 10)
                    ScanTextView(title: "栈版号",
                                 prompt: "请输入栈版号",
                                 text: $viewModel.stackVersionNumber)
                    Divider()
                        .padding(.leading, 10)
                    ScanTextView(title: "箱号",
                                 prompt: "请输入箱号",
                                 text: $viewModel.caseNumber)
                }
                .background(.white)
                
                Spacer()
            }
        }
        ...
    }
}

添加 【栈板序号】【物料总体积】【箱数】

swift 复制代码
struct SterilizeWholeBoardPage: View {
   ...
    var body: some View {
        PageContentView(title: "灭菌整板", viewModel: viewModel) {
            VStack(spacing: 0) {
                ...
                ...
                VStack {
                    HStack(spacing: 0) {
                        Text("栈板序号")
                            .frame(width: 100, alignment: .leading)
                        Text("1")
                    }
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .padding(EdgeInsets(top: 15, leading: 22, bottom: 15, trailing: 22))
                    Divider()
                        .padding(.leading, 10)
                    VStack(spacing: 10) {
                        HStack(spacing: 0) {
                            Text("物料总体积")
                                .frame(width: 100, alignment: .leading)
                            Text("120.86 m³")
                        }
                        .frame(maxWidth: .infinity, alignment: .leading)

                        HStack(spacing: 0) {
                            Text("箱数")
                                .frame(width: 100, alignment: .leading)
                            Text("12")
                        }
                        .frame(maxWidth: .infinity, alignment: .leading)
                    }
                    .padding(EdgeInsets(top: 15, leading: 22, bottom: 15, trailing: 22))
                }
                ...
            }
        }
        ...
    }
}

使用 environment 规范 Title 文本的宽度

swift 复制代码
.frame(width: 100, alignment: .leading)

大量这种代码我们实在受够了,一个页面如果很多元素,或者其他界面一样的这种对齐呢?不过我们可以通过 environment 进行设置。

新增 TitleWidthEnvironmentKey

swift 复制代码
struct TitleWidthEnvironmentKey: EnvironmentKey {
  	/// 设置默认为100 
    static var defaultValue: CGFloat = 100
}

给 EnvironemtValues 扩展属性 titleWidth

swift 复制代码
extension EnvironmentValues {
    var titleWidth: CGFloat {
        get { self[TitleWidthEnvironmentKey.self] }
        set { self[TitleWidthEnvironmentKey.self] = newValue }
    }
}

将 ScanTextView 中的宽度限制 修改为 titleWidth

swift 复制代码
struct ScanTextView: View {
   ...
    /// 默认为 100
    @Environment(\.titleWidth) private var titleWidth:CGFloat
    
    init(title:String, prompt:String, text:Binding<String>) {
        ...
    }
    ...
}

封装组件 LimitLeadingWidthView

swift 复制代码
struct LimitLeadingWidthView<Leading:View, Treading:View>: View {
    @Environment(\.titleWidth) private var leadingLimitWidth:CGFloat
    private let leading:Leading
    private let treading:Treading
    init(@ViewBuilder leading:() -> Leading, @ViewBuilder treading:() -> Treading) {
        self.leading = leading()
        self.treading = treading()
    }
    var body: some View {
        HStack(spacing: 0) {
            leading
                .frame(width: leadingLimitWidth, alignment: .leading)
            treading
                .frame(maxWidth: .infinity, alignment: .leading)
        }
        .frame(maxWidth: .infinity, alignment: .leading)
    }
}
swift 复制代码
struct LimitLeadingWidthView_Previews: PreviewProvider {
    static var previews: some View {
        LimitLeadingWidthView(leading: {
            Text("我是左侧文本")
        }, treading: {
            Text("我是右侧文本")
        })
            .previewLayout(.sizeThatFits)
    }
}

将【栈板序号】【物料总体积】【箱数】更换为 LimitLeadingWidthView 组件

swift 复制代码
struct SterilizeWholeBoardPage: View {
    ...
    var body: some View {
        PageContentView(title: "灭菌整板", viewModel: viewModel) {
            VStack(spacing: 0) {
               ...
                VStack {
                    LimitLeadingWidthView(leading: {
                        Text("栈板序号")
                    }, treading: {
                        Text("1")
                    })
                    ...
                    VStack(spacing: 10) {
                        LimitLeadingWidthView {
                            Text("物料总体积")
                        } treading: {
                            Text("120.86 m³")
                        }
                        LimitLeadingWidthView {
                            Text("箱数")
                        } treading: {
                            Text("12")
                        }
                    }
                    ...
                }
                ...
            }
        }
        ...
    }
}

将 ScanTextView 内部使用 LimitLeadingWidthView 组件

swift 复制代码
struct ScanTextView: View {
    ...
    var body: some View {
        LimitLeadingWidthView {
            HStack {
                Text("*")
                    .foregroundColor(Color(uiColor: appColor.c_e68181))
                Text(title)
                Spacer()
            }
        } treading: {
            HStack {
                TextField(prompt, text: $text)
                    .frame(height:33)
                Image("scan_icon", bundle: .main)
            }
        }
        ...
    }
}

扩展 View 新增 limitLeadingWidth 方法

虽然默认值100已经在当前页面足够的展示左侧的内容,我们想要在当前页面根本修改全部LimitLeadingWidthView左侧宽度为110

z

swift 复制代码
struct SterilizeWholeBoardPage: View {
    ...
    var body: some View {
        PageContentView(title: "灭菌整板", viewModel: viewModel) {
            ...
        }
        .environment(\.titleWidth, 110)
    }
}

对于.environment(\.titleWidth, 110)这样的方式不是很优雅,使用者还要关心对应Key是什么?我们可以给View做一下扩展。

swift 复制代码
extension View {
    func limitLeadingWidth(_ width:CGFloat) -> some View {
        self.environment(\.titleWidth, width)
    }
}

此时我们上面的代码就可以变成下面

swift 复制代码
struct SterilizeWholeBoardPage: View {
    ...
    var body: some View {
        PageContentView(title: "灭菌整板", viewModel: viewModel) {
            ...
        }
        .limitLeadingWidth(110)
    }
}

这样的写法可以明确用意。

获取栈板序号

栈板序号的值来源于通过灭菌批号查询

新增 @Published 栈板序号用于更新页面

swift 复制代码
class SterilizeWholeBoardPageViewModel: BaseViewModel {
    ...
    /// 栈板序号
    @Published var palletSerialNumber:String = ""
}

根据【灭菌批号】获取【栈板序号】

swift 复制代码
class SterilizeWholeBoardPageViewModel: BaseViewModel {
    ...
    
    /// 请求栈板序号
    func requestPalletNumber() async {
        guard !sterilizationLotNumber.isEmpty else {
            showHUDMessage(message: "灭菌批号为空!")
            return
        }
        let api = GetSterilizationSequenceApi(sterilizeBatch: sterilizationLotNumber)
        let model:BaseModel<Int> = await request(api: api)
        guard model._isSuccess, let data = model.data else {
            return
        }
        palletNumber = "\(data)"
    }
}

输入完毕【灭菌批号】获取【栈板序号】

swift 复制代码
struct SterilizeWholeBoardPage: View {
    ...
    var body: some View {
        PageContentView(title: "灭菌整板", viewModel: viewModel) {
            VStack(spacing: 0) {
                ...
                VStack(spacing: 0) {
                    ScanTextView(title: "灭菌批号",
                                 prompt: "请输入灭菌批号",
                                 text: $viewModel.sterilizationLotNumber)
                        .onSubmit {
                            Task {
                                await viewModel.requestPalletNumber()
                            }
                        }
                    ...
                }
                ...
            }
        }
        ...
    }
}

设置 TabBar 的背景颜色

突然发现,我们的TabrBar变成了这个样子,应该是我们修改SafeArea导致的。

swift 复制代码
struct TabPage: View {
    ...
    
    init() {
        ...
        UITabBar.appearance().backgroundColor = .white
    }
    ...
}

获取【物料总体积】【箱数】

继承 PalletBindBoxNumberPageViewModel

物料总体积箱数来源于根据栈版号获取的箱子列表拿到的数据。这个页面输入栈版号箱号是一样的逻辑,我们不如将SterilizeWholeBoardPageViewModel继承于PalletBindBoxNumberPageViewModel

swift 复制代码
class SterilizeWholeBoardPageViewModel: PalletBindBoxNumberPageViewModel {
    /// 灭菌批号
    @Published var sterilizationLotNumber:String = ""
    /// 栈板序号
    @Published var palletSerialNumber:String = ""
    
    ....
}
swift 复制代码
struct SterilizeWholeBoardPage: View {
    ...
    var body: some View {
        PageContentView(title: "灭菌整板", viewModel: viewModel) {
            VStack(spacing: 0) {
                ...
                VStack(spacing: 0) {
                    ...
                    ScanTextView(title: "栈版号",
                                 prompt: "请输入栈版号",
                                 text: $viewModel.palletNumber)
                    ...
                    ScanTextView(title: "箱号",
                                 prompt: "请输入箱号",
                                 text: $viewModel.boxNumber)
                }
                ...
                VStack {
                    LimitLeadingWidthView(leading: {
                        Text("栈板序号")
                    }, treading: {
                        Text(viewModel.palletSerialNumber)
                    })
                    ...
            }
        }
        ...
    }
}

新增 @Published 变量显示【箱号总体积】【箱数】

swift 复制代码
class SterilizeWholeBoardPageViewModel: PalletBindBoxNumberPageViewModel {
    ...
  	/// 总体积
    @Published var totalCapacity:String = ""
    /// 箱数
    @Published var totalBox:String = ""
    ...
}

因为箱子总体积箱数的数据来源于单条的BoxDetailModel里面的数据,我们监听boxDetailModels的变化,获取第一条元素进行获取。

swift 复制代码
class SterilizeWholeBoardPageViewModel: PalletBindBoxNumberPageViewModel {
    ...
    /// 存储 Publisher 取消
    private var cancellabels:Set<AnyCancellable> = []
    
    override init() {
        super.init()
        $boxDetailModels.sink {[weak self] models in
            guard let self = self else {return}
            self.totalCapacity = models.first.flatMap({$0.volume}).map({"\($0)"}) ?? ""
            self.totalBox = models.first.flatMap({$0.total}).map({"\($0)"}) ?? ""
        }
        .store(in: &cancellabels)
    }
    
    ...
}

查询栈板箱子列表和新增删除箱号

swift 复制代码
struct SterilizeWholeBoardPage: View {
    ...
    var body: some View {
        PageContentView(title: "灭菌整板", viewModel: viewModel) {
            VStack(spacing: 0) {
                ...
                VStack(spacing: 0) {
                    ...
                    ScanTextView(title: "栈版号",
                                 prompt: "请输入栈版号",
                                 text: $viewModel.palletNumber)
                        .onSubmit {
                            Task {
                                await viewModel.requestBoxDetailList()
                            }
                        }
                    ...
                    ScanTextView(title: "箱号",
                                 prompt: "请输入箱号",
                                 text: $viewModel.boxNumber)
                        .onSubmit {
                            Task {
                                await viewModel.addOrRemoveBox()
                            }
                        }
                }
                ...
            }
        }
        ...
    }
}

提炼箱号列表

灭菌整板箱子列表和托盘绑定箱号的箱子列表是一样的,所以,我们可以将灭菌整板的箱子列表进行提炼。

swift 复制代码
struct BoxListView: View {
    private let models:[BoxDetailModel]
    init(models:[BoxDetailModel]) {
        self.models = models
    }
    var body: some View {
        List {
            ForEach(models) { model in
                BoxDetailView(model: model)
                    .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 5, trailing: 0))
                    .listRowBackground(Color.clear)
                    .listRowSeparator(.hidden)
            }
        }
        .listStyle(.plain)
    }
}

修改托盘绑定箱号页面

swift 复制代码
struct PalletBindBoxNumberPage: View {
    ...
    var body: some View {
        PageContentView(title: "托盘绑定箱号", viewModel: viewModel) {
            VStack(spacing:0) {
                ...
                BoxListView(models: viewModel.boxDetailModels)
            }
        }
        ...
    }
}

灭菌整板页面新增BoxListView

swift 复制代码
struct SterilizeWholeBoardPage: View {
    ...
    var body: some View {
        PageContentView(title: "灭菌整板", viewModel: viewModel) {
            VStack(spacing: 0) {
               ...
                Spacer()
                    .frame(height: 10)
                BoxListView(models: viewModel.boxDetailModels)
            }
        }
        ...
    }
}

添加 【重置】 【提交】按钮

打印需要调用蓝牙和硬件交互,我们就把打印替换成重置

封装 TextButton

在登录页面,我们有一个类似的登录按钮,决定按照登录按钮的样式封装按钮,方便后面的使用。

swift 复制代码
struct TextButton: View {
    @StateObject private var appColor = AppColor.share
    private let title:String
    private let action:() -> Void
    init(title:String, action:@escaping () -> Void) {
        self.title = title
        self.action = action
    }
    var body: some View {
        Button(action: action) {
            Text(title)
                .font(.system(size: 16))
                .frame(maxWidth:.infinity)
                .frame(height: 45)
                .background(Color(uiColor: appColor.c_209090))
                .foregroundColor(.white)
                .cornerRadius(5)
        }
        
    }
}

struct TextButton_Previews: PreviewProvider {
    static var previews: some View {
        TextButton(title: "登录", action: {})
            .previewLayout(.sizeThatFits)
    }
}

通过 TextButton 添加 【重置】【提交】

通过 Stack 进行叠加布局

swift 复制代码
struct SterilizeWholeBoardPage: View {
    ...
    var body: some View {
        PageContentView(title: "灭菌整板", viewModel: viewModel) {
            ZStack {
                ...
                VStack {
                    Spacer()
                    HStack {
                        TextButton(title: "重置") {
                            
                        }
                        TextButton(title: "提交") {
                            
                        }
                    }
                    .padding()
                }
            }
        }
        ...
    }
}

重置界面数据

点击重置按钮需要将界面所有的数据清空,界面恢复到刚打开的状态。

swift 复制代码
class SterilizeWholeBoardPageViewModel: PalletBindBoxNumberPageViewModel {
    ...
    func reset() {
        sterilizationLotNumber = ""
        palletSerialNumber = ""
        palletNumber = ""
        boxNumber = ""
        totalCapacity = ""
        totalBox = ""
        boxDetailModels = []
    }
}
swift 复制代码
struct SterilizeWholeBoardPage: View {
    ...
    var body: some View {
        PageContentView(title: "灭菌整板", viewModel: viewModel) {
            ZStack {
                ...
                VStack {
                    ...
                    HStack {
                        TextButton(title: "重置") {
                            viewModel.reset()
                        }
                        ...
                    }
                    ...
                }
            }
        }
        ...
    }
}

【提交】灭菌整板

相关推荐
君赏3 小时前
第三十章 接下来我们写首页的功能,首先是我们的`托盘绑定箱号`。
swiftui
君赏3 小时前
第三十一章 完善箱号列表
swiftui
君赏3 小时前
第二十五章 完善登录逻辑
swiftui
君赏3 小时前
第二十六章 Focused
swiftui
君赏3 小时前
第 二十章 @Published sink
swiftui
君赏3 小时前
第二十一章 @ViewBuilder默认实现|Toggle|我的页面封装
swiftui
君赏3 小时前
第二十九章 修复首页 PopMenuView 显示问题
swiftui
君赏3 小时前
第二十八章 重置 ObservableObject 模型数据
swiftui
君赏3 小时前
第二十七章 UINavigationBarAppearance|Divider
swiftui