btn.onclick = showMessage 与 btn.onclick = showMessage()

本文探讨了两种处理按钮点击事件的方法:一种是直接为按钮的onclick属性分配事件处理函数;另一种则是通过初始化设置事件,但之后按钮的onclick属性仍为null。此外还提到了使用匿名函数作为事件处理的方式。

第一种是赋予btn.onclick一个事件
第二种则只会初始化执行一次事件,完后btn.onclick 依然是null,
也可以用匿名函数

这是代码import UIKit protocol CustomMenuItem { } protocol CustomMenuView: UIView { associatedtype MenuItemView: CustomMenuItemView var items: [MenuItemView.MenuItem] { get } func itemClickCallback(index: Int) } protocol CustomMenuItemView: UIView { associatedtype MenuItem: CustomMenuItem init(item: MenuItem) var action: (() -> Void)? { get set } } extension CustomMenuView { func setup() { backgroundColor = .tpbCard let count = items.count let container = UIView() addSubview(container) container.frame = bounds.inset(by: UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)) container.autoresizingMask = [.flexibleWidth, .flexibleHeight] var previousView: UIView? = nil var height: CGFloat = 0 for i in 0..<count { let menuView = MenuItemView(item: items[i]) menuView.translatesAutoresizingMaskIntoConstraints = false container.addSubview(menuView) let cons1 = NSLayoutConstraint(item: menuView, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1, constant: 0) let cons2 = NSLayoutConstraint(item: menuView, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1, constant: 0) let cons3: NSLayoutConstraint if let prev = previousView { cons3 = NSLayoutConstraint(item: menuView, attribute: .top, relatedBy: .equal, toItem: prev, attribute: .bottom, multiplier: 1, constant: 0) } else { cons3 = NSLayoutConstraint(item: menuView, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1, constant: 0) } menuView.action = { [weak self] in self?.itemClickCallback(index: i) } NSLayoutConstraint.activate([cons1, cons2, cons3]) menuView.layoutIfNeeded() height += menuView.frame.size.height previousView = menuView } frame.size.height = 8 + height + 8 } } class AnyMenuItemView<MenuItem: CustomMenuItem>: TapGestureSwallowingControl, CustomMenuItemView { var action: (() -> Void)? let item: MenuItem required init(item: MenuItem) { self.item = item super.init(frame: .zero) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { backgroundColor = .tpbBackgroundInCard return true } override func endTracking(_ touch: UITouch?, with event: UIEvent?) { stopTracking(with: event) } override func cancelTracking(with event: UIEvent?) { stopTracking(with: event) } private func stopTracking(with event: UIEvent?) { backgroundColor = .tpbCard if let loc = event?.touches(for: self)?.first?.location(in: self), bounds.contains(loc) { action?() } } } class DeviceGroupMenuView: UIView, CustomMenuView { typealias MenuItemView = DeviceGroupMenuItemView enum Item: Int { //Fix: 隐藏Home/Away Mode // case alarmMode = 0 case viewMode = 0 case reorder case search } //Fix: 隐藏Home/Away Mode // let items: [DeviceGroupMenuItem] = [.alarmMode, .viewMode, .reorder, .search] var items: [DeviceGroupMenuItem] = [.viewMode, .reorder, .search] { didSet { setup() } } var action: ((Item) -> Void)? override init(frame: CGRect) { super.init(frame: frame) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func itemClickCallback(index: Int) { if items[index] == .viewMode { action?(.viewMode) } else if items[index] == .reorder { action?(.reorder) } else if items[index] == .search { action?(.search) } } } class DeviceGroupMenuItem: NSObject, CustomMenuItem { @objc dynamic var image: UIImage @objc dynamic var title: String @objc dynamic var subtitle: String? @objc dynamic var enabled: Bool init(image: UIImage, title: String, subtitle: String? = nil, enabled: Bool = true) { self.image = image self.title = title self.subtitle = subtitle self.enabled = enabled super.init() } //Fix: 隐藏Home/Away Mode // static let alarmMode: DeviceGroupMenuItem = DeviceGroupMenuItem(image: TPImageLiteral("devicelist_home_active").withRenderingMode(.alwaysOriginal), title: LocalizedString(key: deviceListModeHome), subtitle: LocalizedString(key: deviceListSwitchModeToOut)) static let viewMode: DeviceGroupMenuItem = DeviceGroupMenuItem(image: TPImageLiteral("deviceList_display_bigPic").withRenderingMode(.alwaysOriginal), title: LocalizedString(key: deviceListSwitchView), subtitle: "") static let reorder: DeviceGroupMenuItem = DeviceGroupMenuItem(image: TPImageLiteral("devicelist_nvr_channel_reorder"), title: LocalizedString(key: deviceListReorder)) static let search: DeviceGroupMenuItem = DeviceGroupMenuItem(image: TPImageLiteral("devicelist_search"), title: LocalizedString(key: deviceListSearch)) } class DeviceGroupMenuItemView: AnyMenuItemView<DeviceGroupMenuItem> { var observers = [NSKeyValueObservation]() required init(item: DeviceGroupMenuItem) { super.init(item: item) setup() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var intrinsicContentSize: CGSize { return CGSize(width: super.intrinsicContentSize.width, height: 44) } override func didMoveToSuperview() { super.didMoveToSuperview() if superview == nil { observers = [] } else { observers = [ item.observe(\.image, options: .initial) { [weak self] (object, _) in self?.imageView.image = object.image }, item.observe(\.title, options: .initial) { [weak self] (object, _) in self?.titleLabel.text = object.title }, item.observe(\.enabled, options: .initial) { [weak self] (object, _) in self?.isEnabled = object.enabled }, item.observe(\.subtitle, options: .initial) { [weak self] (object, _) in self?.informationLabel.text = object.subtitle } ] } } private lazy var imageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFit return imageView }() private lazy var titleLabel: UILabel = { let titleLabel = UILabel() titleLabel.textColor = UIColor.tpbTextPrimary titleLabel.font = UIFont.projectFont(ofSize: 16) titleLabel.textAlignment = .left titleLabel.numberOfLines = 1 // titleLabel.lineBreakMode = .byWordWrapping return titleLabel }() private lazy var informationLabel: UILabel = { let label = UILabel(frame: .zero) label.textColor = UIColor.tpbTextThirdContent label.font = UIFont.projectFont(ofSize: 13) label.textAlignment = .right label.numberOfLines = 0 label.lineBreakMode = .byWordWrapping return label }() private func setup() { backgroundColor = .tpbCard let container = UIView() container.translatesAutoresizingMaskIntoConstraints = false addSubview(container) let cons1 = NSLayoutConstraint(item: container, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 16) let cons2 = NSLayoutConstraint(item: container, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0) let cons3 = NSLayoutConstraint(item: container, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: -16) let cons4 = NSLayoutConstraint(item: container, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0) NSLayoutConstraint.activate([cons1, cons2, cons3, cons4]) imageView.translatesAutoresizingMaskIntoConstraints = false container.addSubview(imageView) let imageViewCons1 = NSLayoutConstraint(item: imageView, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1, constant: 4) let imageViewCons2 = NSLayoutConstraint(item: imageView, attribute: .centerY, relatedBy: .equal, toItem: container, attribute: .centerY, multiplier: 1, constant: 0) let imageViewCons3 = NSLayoutConstraint(item: imageView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: 24) let imageViewCons4 = NSLayoutConstraint(item: imageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 24) NSLayoutConstraint.activate([imageViewCons1, imageViewCons2, imageViewCons3, imageViewCons4]) titleLabel.translatesAutoresizingMaskIntoConstraints = false container.addSubview(titleLabel) let titleLabelCons1 = NSLayoutConstraint(item: titleLabel, attribute: .leading, relatedBy: .equal, toItem: imageView, attribute: .trailing, multiplier: 1, constant: 4) let titleLabelCons2 = NSLayoutConstraint(item: titleLabel, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1, constant: 0) let titleLabelCons3 = NSLayoutConstraint(item: titleLabel, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1, constant: 0) NSLayoutConstraint.activate([titleLabelCons1, titleLabelCons2, titleLabelCons3]) informationLabel.translatesAutoresizingMaskIntoConstraints = false container.addSubview(informationLabel) let informationLabelCons1 = NSLayoutConstraint(item: informationLabel, attribute: .leading, relatedBy: .equal, toItem: titleLabel, attribute: .trailing, multiplier: 1, constant: 5) let informationLabelCons2 = NSLayoutConstraint(item: informationLabel, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1, constant: 0) let informationLabelCons3 = NSLayoutConstraint(item: informationLabel, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1, constant: 0) let informationLabelCons4 = NSLayoutConstraint(item: informationLabel, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1, constant: 0) NSLayoutConstraint.activate([informationLabelCons1, informationLabelCons2, informationLabelCons3, informationLabelCons4]) titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal) container.isUserInteractionEnabled = false } } // MARK: GeneralMenuView class GeneralMenuView: UIView, CustomMenuView { typealias MenuItemView = GeneralMenuItemView var items: [GeneralMenuItem] = [] { didSet { setup() } } /// 点击后的动作 /// index: 第几个menuItem var action: ((_ itemId: String) -> Void)? override init(frame: CGRect) { super.init(frame: frame) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func itemClickCallback(index: Int) { action?(items[safe: index]?.itemId ?? "") } } class GeneralMenuItem: NSObject, CustomMenuItem { var itemId: String @objc dynamic var image: UIImage @objc dynamic var title: String @objc dynamic var subtitle: String? @objc dynamic var enabled: Bool init(itemId: String, image: UIImage, title: String, subtitle: String? = nil, enabled: Bool = true) { self.itemId = itemId self.image = image self.title = title self.subtitle = subtitle self.enabled = enabled super.init() } } class GeneralMenuItemView: AnyMenuItemView<GeneralMenuItem> { var observers = [NSKeyValueObservation]() var informationLabelWidthConstraint: NSLayoutConstraint? required init(item: GeneralMenuItem) { super.init(item: item) setup() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var intrinsicContentSize: CGSize { return CGSize(width: super.intrinsicContentSize.width, height: 44) } override func didMoveToSuperview() { super.didMoveToSuperview() if superview == nil { observers = [] } else { observers = [ item.observe(\.image, options: .initial) { [weak self] (object, _) in self?.imageView.image = object.image }, item.observe(\.title, options: .initial) { [weak self] (object, _) in self?.titleLabel.text = object.title }, item.observe(\.enabled, options: .initial) { [weak self] (object, _) in self?.isEnabled = object.enabled }, item.observe(\.subtitle, options: .initial) { [weak self] (object, _) in if object.subtitle == nil || object.subtitle == "" { self?.informationLabelWidthConstraint?.isActive = true } else { self?.informationLabelWidthConstraint?.isActive = false self?.informationLabel.text = object.subtitle } } ] } } private lazy var imageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFit return imageView }() private lazy var titleLabel: UILabel = { let titleLabel = UILabel() titleLabel.textColor = UIColor.tpbTextPrimary titleLabel.font = UIFont.projectFont(ofSize: 16) titleLabel.textAlignment = .left titleLabel.numberOfLines = 0 titleLabel.lineBreakMode = .byWordWrapping return titleLabel }() private lazy var informationLabel: UILabel = { let label = UILabel(frame: .zero) label.textColor = UIColor.tpbTextThirdContent label.font = UIFont.projectFont(ofSize: 13) label.textAlignment = .right label.numberOfLines = 0 label.lineBreakMode = .byWordWrapping return label }() private func setup() { backgroundColor = .tpbCard let container = UIView() container.translatesAutoresizingMaskIntoConstraints = false addSubview(container) let cons1 = NSLayoutConstraint(item: container, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 16) let cons2 = NSLayoutConstraint(item: container, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0) let cons3 = NSLayoutConstraint(item: container, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: -16) let cons4 = NSLayoutConstraint(item: container, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0) NSLayoutConstraint.activate([cons1, cons2, cons3, cons4]) imageView.translatesAutoresizingMaskIntoConstraints = false container.addSubview(imageView) let imageViewCons1 = NSLayoutConstraint(item: imageView, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1, constant: 4) let imageViewCons2 = NSLayoutConstraint(item: imageView, attribute: .centerY, relatedBy: .equal, toItem: container, attribute: .centerY, multiplier: 1, constant: 0) let imageViewCons3 = NSLayoutConstraint(item: imageView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: 24) let imageViewCons4 = NSLayoutConstraint(item: imageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 24) NSLayoutConstraint.activate([imageViewCons1, imageViewCons2, imageViewCons3, imageViewCons4]) titleLabel.translatesAutoresizingMaskIntoConstraints = false container.addSubview(titleLabel) let titleLabelCons1 = NSLayoutConstraint(item: titleLabel, attribute: .leading, relatedBy: .equal, toItem: imageView, attribute: .trailing, multiplier: 1, constant: 12) let titleLabelCons2 = NSLayoutConstraint(item: titleLabel, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1, constant: 0) let titleLabelCons3 = NSLayoutConstraint(item: titleLabel, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1, constant: 0) NSLayoutConstraint.activate([titleLabelCons1, titleLabelCons2, titleLabelCons3]) informationLabel.translatesAutoresizingMaskIntoConstraints = false container.addSubview(informationLabel) let informationLabelCons1 = NSLayoutConstraint(item: informationLabel, attribute: .leading, relatedBy: .equal, toItem: titleLabel, attribute: .trailing, multiplier: 1, constant: 5) let informationLabelCons2 = NSLayoutConstraint(item: informationLabel, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1, constant: 0) let informationLabelCons3 = NSLayoutConstraint(item: informationLabel, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1, constant: 0) let informationLabelCons4 = NSLayoutConstraint(item: informationLabel, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1, constant: 0) informationLabelWidthConstraint = NSLayoutConstraint(item: informationLabel, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 0) NSLayoutConstraint.activate([informationLabelCons1, informationLabelCons2, informationLabelCons3, informationLabelCons4]) titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal) container.isUserInteractionEnabled = false } } class DeviceListMenuView: UIView, CustomMenuView { enum Item: Int { case alarmMode = 0 //Fix: 隐藏云存储 // case cloudStorage //Fix: 隐藏设备分享 // case share case upgrade // case reonboard case unbind case setting case collect } typealias MenuItemView = DeviceListMenuItemView let items: [DeviceListMenuItem] = DeviceListMenuItem.allItems static var deviceChannel: (device: TPSSDeviceForDeviceList, channel: TPSSChannelInfo?)? var action: ((Item, TPSSDeviceForDeviceList, TPSSChannelInfo?) -> Void)? = nil override init(frame: CGRect) { super.init(frame: frame) setup() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func itemClickCallback(index: Int) { action?(Item(rawValue: index)!, DeviceListMenuView.deviceChannel!.device, DeviceListMenuView.deviceChannel!.channel) } } class DeviceListMenuItem: NSObject, CustomMenuItem { @objc dynamic var image: UIImage @objc dynamic var title: String @objc dynamic var subtitle: String? @objc dynamic var enabled: Bool @objc dynamic var hidden: Bool @objc dynamic var subtitleTextColor: UIColor @objc dynamic var subtitleBackgroundColor: UIColor @objc dynamic var alpha: Float init(image: UIImage, title: String, subtitle: String? = nil, enabled: Bool = true, hidden: Bool = false, subtitleTextColor: UIColor = .tpbTextPrimary, subtitleBackgroundColor: UIColor = .tpbCard, alpha: Float = 1) { self.image = image self.title = title self.subtitle = subtitle self.enabled = enabled self.hidden = hidden self.subtitleTextColor = subtitleTextColor self.subtitleBackgroundColor = subtitleBackgroundColor self.alpha = alpha super.init() } static let alarmMode: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("devicelist_alarm_on").withRenderingMode(.alwaysOriginal), title: LocalizedString(key: deviceListEventAlarmOn)) //Fix: 隐藏云存储 // static let cloudStorage: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("cloud_service"), title: LocalizedString(key: deviceListCloudStorage), subtitle: LocalizedString(key: deviceListSwitchViewToList)) //Fix: 隐藏设备分享 // static let share: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("devicelist_share_nor"), title: LocalizedString(key: deviceListShare)) static let upgrade: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("devicelist_update_reddot_nor"), title: LocalizedString(key: deviceListUpgrade)) // static let reonboard: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("common_reonboarding_wifi"), title: LocalizedString(key: deviceListReonboarding)) static let setting: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("common_setting_nor"), title: LocalizedString(key: deviceListMoreSetting)) static let unbind: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("devicelist_unbind"), title: LocalizedString(key: deviceUnbindButtonTitle)) static let collect: DeviceListMenuItem = DeviceListMenuItem(image: TPImageLiteral("device_not_collect_list"), title: LocalizedString(key: deviceCollectFavorite)) //Fix: 隐藏云存储 //Fix: 隐藏设备分享 // static let allItems: [DeviceListMenuItem] = [.alarmMode, .cloudStorage, .share, .upgrade, .reonboard, .setting] static let allItems: [DeviceListMenuItem] = [.alarmMode, .upgrade, unbind, .setting, .collect] } extension Array where Element == DeviceListMenuItem { var preferredheight: CGFloat { return CGFloat(filter {!$0.hidden}.count) * 44 + 16 } } class DeviceListMenuItemView: AnyMenuItemView<DeviceListMenuItem> { var observers = [NSKeyValueObservation]() required init(item: DeviceListMenuItem) { super.init(item: item) setup() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var intrinsicContentSize: CGSize { return CGSize(width: super.intrinsicContentSize.width, height: item.hidden ? 0 : 44) } override func didMoveToSuperview() { super.didMoveToSuperview() if superview == nil { observers = [] } else { observers = [ item.observe(\.image, options: .initial) { [weak self] (object, _) in self?.imageView.image = object.image }, item.observe(\.title, options: .initial) { [weak self] (object, _) in self?.titleLabel.text = object.title }, item.observe(\.enabled, options: .initial) { [weak self] (object, _) in self?.isEnabled = object.enabled }, item.observe(\.subtitle, options: .initial) { [weak self] (object, _) in if let subtitle = object.subtitle { self?.informationButton.isHidden = false self?.informationLabel.text = subtitle } else { self?.informationButton.isHidden = true self?.hasInformationLayout(false) } }, item.observe(\.subtitleTextColor, options: .initial) { [weak self] (object, _) in self?.informationLabel.textColor = object.subtitleTextColor }, item.observe(\.subtitleBackgroundColor, options: .initial) { [weak self] (object, _) in self?.informationButton.backgroundColor = object.subtitleBackgroundColor }, item.observe(\.hidden, options: .initial) { [weak self] (object, _) in self?.isHidden = object.hidden self?.invalidateIntrinsicContentSize() }, item.observe(\.alpha, options: .initial) { [weak self] (object, _) in self?.alpha = CGFloat(object.alpha) } ] } } private lazy var imageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFit return imageView }() private lazy var titleLabel: UILabel = { let titleLabel = UILabel() titleLabel.textColor = UIColor.tpbTextPrimary titleLabel.font = UIFont.projectFont(ofSize: 16) titleLabel.textAlignment = .left titleLabel.numberOfLines = 0 titleLabel.lineBreakMode = .byWordWrapping titleLabel.setContentCompressionResistancePriority(.defaultLow, for: .vertical) titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal) return titleLabel }() private lazy var informationButton: UIButton = { let btn = UIButton(type: .system) btn.tintColor = .tpbTextPrimary btn.backgroundColor = .tpbCard btn.titleLabel?.font = UIFont.projectFont(ofSize: 13) btn.layer.cornerRadius = 12 // btn.contentEdgeInsets = UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12) NSLayoutConstraint(item: btn, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: 24).isActive = true return btn }() private lazy var informationLabel: UILabel = { let informationLabel = UILabel() informationLabel.textColor = UIColor.tpbTextPrimary informationLabel.font = UIFont.projectFont(ofSize: 13) informationLabel.textAlignment = .left informationLabel.numberOfLines = 1 // informationLabel.setContentCompressionResistancePriority(.required, for: .horizontal) return informationLabel }() private func hasInformationLayout(_ hasInfo: Bool) { let titleLabelCons4 = NSLayoutConstraint(item: titleLabel, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: 0) titleLabelCons4.isActive = !hasInfo } private func setup() { backgroundColor = .tpbCard let container = UIView() container.translatesAutoresizingMaskIntoConstraints = false addSubview(container) let cons1 = NSLayoutConstraint(item: container, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 16) let cons2 = NSLayoutConstraint(item: container, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0) let cons3 = NSLayoutConstraint(item: container, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: -22) let cons4 = NSLayoutConstraint(item: container, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0) NSLayoutConstraint.activate([cons1, cons2, cons3, cons4]) imageView.translatesAutoresizingMaskIntoConstraints = false container.addSubview(imageView) let imageViewCons1 = NSLayoutConstraint(item: imageView, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1, constant: 4) let imageViewCons2 = NSLayoutConstraint(item: imageView, attribute: .centerY, relatedBy: .equal, toItem: container, attribute: .centerY, multiplier: 1, constant: 0) let imageViewCons3 = NSLayoutConstraint(item: imageView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: 24) let imageViewCons4 = NSLayoutConstraint(item: imageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 24) NSLayoutConstraint.activate([imageViewCons1, imageViewCons2, imageViewCons3, imageViewCons4]) titleLabel.translatesAutoresizingMaskIntoConstraints = false container.addSubview(titleLabel) let titleLabelCons1 = NSLayoutConstraint(item: titleLabel, attribute: .leading, relatedBy: .equal, toItem: imageView, attribute: .trailing, multiplier: 1, constant: 4) let titleLabelCons2 = NSLayoutConstraint(item: titleLabel, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1, constant: 0) let titleLabelCons3 = NSLayoutConstraint(item: titleLabel, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1, constant: 0) NSLayoutConstraint.activate([titleLabelCons1, titleLabelCons2, titleLabelCons3]) informationButton.translatesAutoresizingMaskIntoConstraints = false container.addSubview(informationButton) informationLabel.translatesAutoresizingMaskIntoConstraints = false informationButton.addSubview(informationLabel) let informationLabelCons1 = NSLayoutConstraint(item: informationLabel, attribute: .leading, relatedBy: .equal, toItem: titleLabel, attribute: .trailing, multiplier: 1, constant: 12) let informationLabelCons2 = NSLayoutConstraint(item: informationLabel, attribute: .trailing, relatedBy: .equal, toItem: informationButton, attribute: .trailing, multiplier: 1, constant: -12) let informationLabelCons3 = NSLayoutConstraint(item: informationLabel, attribute: .top, relatedBy: .equal, toItem: informationButton, attribute: .top, multiplier: 1, constant: 0) let informationLabelCons4 = NSLayoutConstraint(item: informationLabel, attribute: .bottom, relatedBy: .equal, toItem: informationButton, attribute: .bottom, multiplier: 1, constant: 0) let informationLabelCons5 = NSLayoutConstraint(item: informationLabel, attribute: .width, relatedBy: .greaterThanOrEqual, toItem: nil, attribute: .width, multiplier: 1, constant: 40) NSLayoutConstraint.activate([informationLabelCons1, informationLabelCons2, informationLabelCons3, informationLabelCons4, informationLabelCons5]) let informationButtonCons1 = NSLayoutConstraint(item: informationButton, attribute: .leading, relatedBy: .equal, toItem: informationLabel, attribute: .leading, multiplier: 1, constant: -12) let informationButtonCons2 = NSLayoutConstraint(item: informationButton, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1, constant: 0) let informationButtonCons3 = NSLayoutConstraint(item: informationButton, attribute: .centerY, relatedBy: .equal, toItem: container, attribute: .centerY, multiplier: 1, constant: 0) NSLayoutConstraint.activate([informationButtonCons1, informationButtonCons2, informationButtonCons3]) container.isUserInteractionEnabled = false } } extension DeviceListMenuView { private class func setDeviceListMenuItemAlpha(_ alpha: Float, for itemList: [DeviceListMenuItem]) { if alpha >= 0 && alpha <= 1 { itemList.forEach({$0.alpha = alpha}) } } class func configure(device: TPSSDeviceForDeviceList, channel: TPSSChannelInfo?, displayMode: TPSSDeviceGroupDisplayMode = .card) { deviceChannel = (device, channel) //OnlineDeviceListViewController调用configure函数是配置两个item title以满足多语言切换刷新文案 DeviceListMenuItem.upgrade.title = LocalizedString(key: deviceListUpgrade) DeviceListMenuItem.setting.title = LocalizedString(key: deviceListMoreSetting) DeviceListMenuItem.collect.title = LocalizedString(key: deviceCollectFavorite) DeviceListMenuItem.unbind.title = LocalizedString(key: deviceUnbindButtonTitle) //太阳能\NVR\local;card模式此处不显示收藏,老版本vms也不支持 let isVms = TPAppContextFactory.shared().isVmsLogin DeviceListMenuItem.collect.hidden = (device.deviceType == .solar || device.listType == .local || displayMode == .card || (TPSSAppContext.shared.loginType == .personal ? false : TPSSAppContext.shared.isVmsDeviceListFavoriteOldVersion) || ( device.deviceType == .NVR && channel == nil) ) ? true : false //根据设备收否被收藏来设置收藏按钮状态 var isCollected = false if device.deviceType == .IPC { isCollected = isVms ? device.isVMSFavorited : device.isCollected } else { if channel != nil { isCollected = isVms ? channel!.isVMSFavorited : device.isCollected && device.collectChannels.contains(channel!.channelId as NSNumber) } else { isCollected = isVms ? device.isVMSFavorited : device.isCollected } } DeviceListMenuItem.collect.image = isCollected ? TPImageLiteral("device_have_collect_list") : TPImageLiteral("device_not_collect_list") //太阳能设备适配 if device.deviceType == .IPC || device.deviceType == .solar { // 消息报警 var showMessage = device.listType == .remote && device.supportMessage && device.displayOnline && !device.isSharedDevice if let cn = channel { showMessage = cn.supportsMessagePush && cn.online && device.displayOnline && !device.isSharedDevice } //太阳能设备适配 if device.deviceType == .solar { //太阳能设备不支持通知开关 showMessage = false // showMessage = device.listType == .remote && device.displayOnline } if !showMessage { DeviceListMenuItem.alarmMode.hidden = true } else { DeviceListMenuItem.alarmMode.hidden = false let messageEnable = channel?.messageEnable ?? device.messageEnable DeviceListMenuItem.alarmMode.image = messageEnable ? TPImageLiteral("devicelist_alarm_on").withRenderingMode(.alwaysOriginal) : TPImageLiteral("devicelist_alarm_off") DeviceListMenuItem.alarmMode.title = messageEnable ? LocalizedString(key: deviceListEventAlarmOn) : LocalizedString(key: deviceListEventAlarmOff) } if device.deviceSubType == .doorBellCamera { DeviceListMenuItem.alarmMode.hidden = true } // 如果是分享的设备且没有管理权限, cell设为透明, 不许设置 if device.isSharedFromOthers && device.shareDevicePermissionInfo.deviceSettingWrite == false { setDeviceListMenuItemAlpha(0.5, for: [.alarmMode, .upgrade, .unbind]) } else { setDeviceListMenuItemAlpha(1, for: [.alarmMode, .upgrade, .unbind]) } // 云存储 //Fix: 隐藏云存储 // if !device.supportCloudStorage || device.isSharedDevice { // DeviceListMenuItem.cloudStorage.hidden = true // } else { // let cloudStorageService = appContext.currentServiceInfo(forDevice: device.cloudDeviceId, // channel: max(0, channelID), // serviceType: .cloudStorage) // // DeviceListMenuItem.cloudStorage.hidden = false // // if cloudStorageService.isVaild { // DeviceListMenuItem.cloudStorage.subtitle = cloudStorageService.cloudStorageStateText // DeviceListMenuItem.cloudStorage.subtitleTextColor = cloudStorageService.stateColor // DeviceListMenuItem.cloudStorage.subtitleBackgroundColor = cloudStorageService.stateBackgroundColor // } else { // DeviceListMenuItem.cloudStorage.subtitle = nil // } // } // 分享 //Fix: 隐藏设备分享 // let appContext = TPSSAppContext.shared // // let channelID = channel?.channelId.intValue ?? -1 // if device.isSharedDevice || !device.supportsShare { // DeviceListMenuItem.share.hidden = true // } else { // DeviceListMenuItem.share.hidden = false // let isEnterpriseShare = appContext.isValidEnterpriseShareDevice(device.cloudDeviceId, channelD: max(0, channelID)) // let shareService: TPSSCloudStorageService // if isEnterpriseShare { // shareService = appContext.enterpriseShareService // } else { // shareService = appContext.currentServiceInfo(forDevice: device.cloudDeviceId, // channel: max(0, channelID), // serviceType: .paidShare) // } // let stateText = shareService.shareStateText(isEnterpriseShare: isEnterpriseShare) // if shareService.isVaild && !stateText.isEmpty && (shareService.serviceState != .expired || UserDefaults.standard.needShowRenewalHint(for: device.cloudDeviceId, channelID: max(0, channelID), isEnterpriseShare: isEnterpriseShare)) { // DeviceListMenuItem.share.subtitle = stateText // DeviceListMenuItem.share.subtitleTextColor = shareService.stateColor // DeviceListMenuItem.share.subtitleBackgroundColor = shareService.stateBackgroundColor // } else { // DeviceListMenuItem.share.subtitle = nil // } // } // // // 重新配置WiFi // if (device.displayOnline || !device.supportsReonboarding) || channel != nil { // DeviceListMenuItem.reonboard.hidden = true // } else { // DeviceListMenuItem.reonboard.hidden = false // } // 固件升级 if device.listType != .remote { DeviceListMenuItem.upgrade.hidden = true } else { if let cn = channel { if let version = cn.shortReleaseFirmwareVersion, cn.hasNewFirmware && !device.isSharedDevice { DeviceListMenuItem.upgrade.hidden = false DeviceListMenuItem.upgrade.image = device.showUpdateForChannel(channel: channel) ? TPImageLiteral("devicelist_update_reddot_nor") : TPImageLiteral("devicelist_update_nor") DeviceListMenuItem.upgrade.subtitle = version DeviceListMenuItem.upgrade.subtitleTextColor = .tpbRed DeviceListMenuItem.upgrade.subtitleBackgroundColor = .tpbRedLight } else { DeviceListMenuItem.upgrade.hidden = true } } else { if let version = device.shortReleaseFirmwareVersion, device.online && device.needUpgrade && !device.isSharedDevice { DeviceListMenuItem.upgrade.hidden = false DeviceListMenuItem.upgrade.image = device.showUpdateForChannel(channel: nil) ? TPImageLiteral("devicelist_update_reddot_nor") : TPImageLiteral("devicelist_update_nor") DeviceListMenuItem.upgrade.subtitle = version DeviceListMenuItem.upgrade.subtitleTextColor = .tpbRed DeviceListMenuItem.upgrade.subtitleBackgroundColor = .tpbRedLight } else { DeviceListMenuItem.upgrade.hidden = true } } } //未初始化设备只有非本地设备首页可显示解绑按钮 if device.listType == .local || device.factoryStatus != .yes || !device.online { DeviceListMenuItem.unbind.hidden = true } else { DeviceListMenuItem.unbind.hidden = false } } else if device.deviceType == .NVR { // DeviceListMenuItem.alarmMode.hidden = true // DeviceListMenuItem.upgrade.hidden = true guard let channelInfo = channel else { DeviceListMenuItem.alarmMode.hidden = true DeviceListMenuItem.upgrade.hidden = true if device.listType == .local || device.factoryStatus != .yes || !device.online { DeviceListMenuItem.unbind.hidden = true } else { DeviceListMenuItem.unbind.hidden = false } return } // 如果是分享的通道且没有管理权限, cell设为透明, 不许设置 if channelInfo.isSharedFromOthers && channelInfo.shareDevicePermissionInfo.deviceSettingWrite == false { setDeviceListMenuItemAlpha(0.5, for: [.alarmMode, .upgrade, .unbind]) } else { setDeviceListMenuItemAlpha(1, for: [.alarmMode, .upgrade, .unbind]) } // 消息报警 let showMessage = (device.listType == .remote) && channelInfo.supportsMessagePush && channelInfo.online && device.displayOnline && !device.isSharedDevice && channelInfo.active if !showMessage { DeviceListMenuItem.alarmMode.hidden = true } else { DeviceListMenuItem.alarmMode.hidden = false let messageEnable = channelInfo.messageEnable DeviceListMenuItem.alarmMode.image = messageEnable ? TPImageLiteral("devicelist_alarm_on").withRenderingMode(.alwaysOriginal) : TPImageLiteral("devicelist_alarm_off") DeviceListMenuItem.alarmMode.title = messageEnable ? LocalizedString(key: deviceListEventAlarmOn) : LocalizedString(key: deviceListEventAlarmOff) } if device.deviceSubType == .doorBellCamera { DeviceListMenuItem.alarmMode.hidden = true } // 固件升级 if device.listType != .remote { DeviceListMenuItem.upgrade.hidden = true } else { if let version = channelInfo.shortReleaseFirmwareVersion, channelInfo.hasNewFirmware && !device.isSharedDevice && channelInfo.active { DeviceListMenuItem.upgrade.hidden = false DeviceListMenuItem.upgrade.image = device.showUpdateForChannel(channel: channelInfo) ? TPImageLiteral("devicelist_update_reddot_nor") : TPImageLiteral("devicelist_update_nor") DeviceListMenuItem.upgrade.subtitle = version DeviceListMenuItem.upgrade.subtitleTextColor = .tpbRed DeviceListMenuItem.upgrade.subtitleBackgroundColor = .tpbRedLight } else { DeviceListMenuItem.upgrade.hidden = true } } //未初始化设备只有非本地设备首页可显示解绑按钮;!channelInfo.active仅针对初始化状态,channel都没设备,不显示解绑(非初始化状态device.factoryStatus != .yes直接隐藏unbind) if device.listType == .local || device.factoryStatus != .yes || !device.online || !channelInfo.active { DeviceListMenuItem.unbind.hidden = true } else { DeviceListMenuItem.unbind.hidden = false } } } }
最新发布
12-06
下面的代码是干什么用的,请生成说明注释,同时还有什么改进: 【import sys from PyQt6.QtWidgets import QGridLayout from PyQt5.QtWidgets import QMainWindow,QWidget,QAction,QApplication,QListWidget,QLabel,QGridLayout class MainWindow(QMainWindow): def __init__(self): super().__init__() self.title = "自适应大小UI" self.desktop = QApplication.desktop() self.screenRect = self.desktop.screenGeometry() self.screenheight = self.screenRect.height() self.screenwidth = self.screenRect.width() self.height = int(self.screenheight * 0.7) self.width = int(self.screenwidth * 0.7) print("Screen height {}".format(self.screenheight)) print("Screen width {}".format(self.screenwidth)) self.resize(self.width, self.height) self.wid = QWidget(self) self.setCentralWidget(self.wid) self.setWindowTitle(self.title) self.initUI() self.show() def initUI(self): self.layout = QGridLayout() # 预览四个边都预留20pixs的边界 self.layout.setContentsMargins(20, 20, 20, 20) # 网格之间设置10pixs的间隔 self.layout.setSpacing(10) self.initMenu() self.initBrowser() self.initImageWindow() self.statusBar().showMessage("准备就绪") self.wid.setLayout(self.layout) def initMenu(self): openImageFolderAct = QAction("打开", self) openImageFolderAct.setStatusTip("选择一个文件夹,开始标注") openImageFolderAct.setShortcut("Ctrl+O") menubar = self.menuBar() fileMenu = menubar.addMenu('&文件') fileMenu.addAction(openImageFolderAct) def initBrowser(self): self.imageBrowser = QListWidget(self) self.imageBrowserWidth = self.width * 0.1 self.imageBrowserHeight = self.height self.imageBrowser.setMinimumSize(self.imageBrowserWidth, self.imageBrowserHeight) self.layout.addWidget(self.imageBrowser, 0, 0) def initImageWindow(self): self.imageWindow = QLabel("Hello World!", self) # self.imageWindow.resize(800,750) # self.imageWindow.move(250,30) self.imageWindow.setStyleSheet("background-color: darkgray;border: 1px solid black;") self.imageWindowWidth = self.width * 0.9 self.imageWindowHeight = self.height self.imageWindow.setMinimumSize(self.imageWindowWidth, self.imageWindowHeight) # 将imageBrowser放置到网格的第0行,第1列 self.layout.addWidget(self.imageWindow, 0, 1) if __name__ == '__main__': #把窗口实例化 app = QApplication(sys.argv) #使其显示 win = MainWindow() sys.exit(app.exec())】
07-04
// 全局变量 const API_BASE = 'jsp/'; // DOM加载完成后执行 document.addEventListener('DOMContentLoaded', function() { // 根据页面ID加载不同内容 const bodyId = document.body.id; if (bodyId === 'home-page') { loadQuestions(); } else if (bodyId === 'question-detail-page') { const urlParams = new URLSearchParams(window.location.search); const questionId = urlParams.get('id'); if (questionId) { document.getElementById('question-id').value = questionId; loadQuestionDetail(questionId); loadAnswers(questionId); } else { showMessage('无效的问题ID', 'error'); setTimeout(() => { window.location.href = 'index.html'; }, 2000); } } else if (bodyId === 'post-question-page') { setupCharCounters(); } // 设置表单提交事件 setupFormHandlers(); }); // 设置字符计数器 function setupCharCounters() { const titleInput = document.getElementById('title'); const contentInput = document.getElementById('content'); const answerContentInput = document.getElementById('answer-content'); if (titleInput) { titleInput.addEventListener('input', function() { document.getElementById('title-char-count').textContent = this.value.length; }); } if (contentInput) { contentInput.addEventListener('input', function() { document.getElementById('content-char-count').textContent = this.value.length; }); } if (answerContentInput) { answerContentInput.addEventListener('input', function() { document.getElementById('answer-char-count').textContent = this.value.length; }); } } // 加载问题列表 function loadQuestions() { const questionList = document.getElementById('question-list'); if (!questionList) return; showLoader(questionList); fetch(API_BASE + 'get_questions.jsp') .then(response => { if (!response.ok) { throw new Error('网络响应不正常'); } return response.json(); }) .then(data => { hideLoader(questionList); if (data.error) { showMessage(data.error, 'error'); return; } if (data.length === 0) { questionList.innerHTML = ` <div class="card empty-state"> <p>暂无问题</p> <p><a href="post_question.html" class="btn">提出第一个问题</a></p> </div> `; return; } let html = ''; data.forEach(question => { html += ` <div class="card question-item" onclick="window.location.href='question_detail.html?id=${question.id}'"> <h2 class="question-title"> ${escapeHtml(question.title)} </h2> <div class="question-content"> ${escapeHtml(question.content.length > 150 ? question.content.substring(0, 150) + '...' : question.content)} </div> <div class="question-meta"> <span class="meta-item"> <i class="icon-time"></i> ${formatDate(question.create_time)} </span> </div> </div> `; }); questionList.innerHTML = html; }) .catch(error => { hideLoader(questionList); showMessage('加载问题失败: ' + error.message, 'error'); console.error('Error:', error); }); } // 加载问题详情 function loadQuestionDetail(questionId) { const questionDetail = document.getElementById('question-detail'); if (!questionDetail) return; showLoader(questionDetail); fetch(API_BASE + 'get_question_detail.jsp?id=' + encodeURIComponent(questionId)) .then(response => { if (!response.ok) { throw new Error('网络响应不正常'); } return response.json(); }) .then(data => { hideLoader(questionDetail); if (data.error) { showMessage(data.error, 'error'); if (data.error.includes('不存在')) { setTimeout(() => { window.location.href = 'index.html'; }, 2000); } return; } const html = ` <div class="card question-detail"> <h1 class="question-title">${escapeHtml(data.title)}</h1> <div class="question-content"> ${escapeHtml(data.content).replace(/\n/g, '<br>')} </div> <div class="question-meta"> <span class="meta-item"> <i class="icon-time"></i> ${formatDate(data.create_time)} </span> </div> </div> `; questionDetail.innerHTML = html; document.title = `${data.title} - 简易论坛`; }) .catch(error => { hideLoader(questionDetail); showMessage('加载问题详情失败: ' + error.message, 'error'); console.error('Error:', error); }); } // 加载回答列表 function loadAnswers(questionId) { const answerList = document.getElementById('answer-list'); const answerCount = document.getElementById('answer-count'); if (!answerList) return; showLoader(answerList); fetch(API_BASE + 'get_answers.jsp?question_id=' + encodeURIComponent(questionId)) .then(response => { if (!response.ok) { throw new Error('网络响应不正常'); } return response.json(); }) .then(data => { hideLoader(answerList); if (data.error) { showMessage(data.error, 'error'); return; } if (answerCount) { answerCount.textContent = `${data.length} 个回答`; } if (data.length === 0) { answerList.innerHTML = ` <div class="card empty-state"> <p>暂无回答</p> <p>成为第一个回答者吧!</p> </div> `; return; } let html = ''; data.forEach(answer => { html += ` <div class="card answer-item"> <div class="answer-content"> ${escapeHtml(answer.content).replace(/\n/g, '<br>')} </div> <div class="answer-meta"> <span class="meta-item"> <i class="icon-time"></i> ${formatDate(answer.create_time)} </span> </div> </div> `; }); answerList.innerHTML = html; }) .catch(error => { hideLoader(answerList); showMessage('加载回答失败: ' + error.message, 'error'); console.error('Error:', error); }); } // 设置表单处理程序 function setupFormHandlers() { // 提问表单 const questionForm = document.getElementById('question-form'); if (questionForm) { questionForm.addEventListener('submit', function(e) { e.preventDefault(); submitQuestionForm(this); }); } // 回答表单 const answerForm = document.getElementById('answer-form'); if (answerForm) { answerForm.addEventListener('submit', function(e) { e.preventDefault(); submitAnswerForm(this); }); } } // 提交问题表单 function submitQuestionForm(form) { const formData = new FormData(form); const submitBtn = form.querySelector('button[type="submit"]'); const originalText = submitBtn.textContent; // 禁用提交按钮并显示加载状态 submitBtn.disabled = true; submitBtn.textContent = '提交中...'; fetch(API_BASE + 'do_post_question.jsp', { method: 'POST', body: formData }) .then(response => { if (!response.ok) { throw new Error('网络响应不正常'); } return response.json(); }) .then(data => { // 恢复提交按钮 submitBtn.disabled = false; submitBtn.textContent = originalText; if (data.success) { showMessage('问题提交成功!', 'success'); setTimeout(() => { if (data.id) { window.location.href = 'question_detail.html?id=' + data.id; } else { window.location.href = 'index.html'; } }, 1500); } else { showMessage('提交失败: ' + (data.error || '未知错误'), 'error'); } }) .catch(error => { // 恢复提交按钮 submitBtn.disabled = false; submitBtn.textContent = originalText; showMessage('提交失败: ' + error.message, 'error'); console.error('Error:', error); }); } // 提交回答表单 function submitAnswerForm(form) { const formData = new FormData(form); const submitBtn = form.querySelector('button[type="submit"]'); const originalText = submitBtn.textContent; // 获取问题ID const questionId = formData.get('question_id'); // 禁用提交按钮并显示加载状态 submitBtn.disabled = true; submitBtn.textContent = '提交中...'; fetch(API_BASE + 'do_post_answer.jsp', { method: 'POST', body: formData }) .then(response => { if (!response.ok) { throw new Error('网络响应不正常'); } return response.json(); }) .then(data => { // 恢复提交按钮 submitBtn.disabled = false; submitBtn.textContent = originalText; if (data.success) { showMessage('回答提交成功!', 'success'); form.reset(); document.getElementById('answer-char-count').textContent = '0'; // 重新加载回答列表 if (questionId) { loadAnswers(questionId); } } else { showMessage('提交失败: ' + (data.error || '未知错误'), 'error'); } }) .catch(error => { // 恢复提交按钮 submitBtn.disabled = false; submitBtn.textContent = originalText; showMessage('提交失败: ' + error.message, 'error'); console.error('Error:', error); }); } // 显示消息 function showMessage(message, type) { // 移除现有的消息 const existingAlert = document.querySelector('.alert'); if (existingAlert) { existingAlert.remove(); } // 创建新消息 const alert = document.createElement('div'); alert.className = `alert alert-${type}`; alert.textContent = message; // 将消息插入到页面中 const container = document.querySelector('.container') || document.body; container.insertBefore(alert, container.firstChild); // 5秒后自动消失 setTimeout(() => { if (alert.parentNode) { alert.remove(); } }, 5000); } // 显示加载器 function showLoader(container) { const loader = document.createElement('div'); loader.className = 'loader'; container.innerHTML = ''; container.appendChild(loader); } // 隐藏加载器 function hideLoader(container) { const loader = container.querySelector('.loader'); if (loader) { loader.remove(); } } // 转义HTML特殊字符 function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // 格式化日期 function formatDate(dateString) { try { const date = new Date(dateString); if (isNaN(date.getTime())) { return dateString; } return date.toLocaleString('zh-CN'); } catch (e) { return dateString; } } 这是完整的js代码,如何修改
09-16
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值