1
3
.gitignore
vendored
@ -38,7 +38,8 @@ playground.xcworkspace
|
|||||||
# you should judge for yourself, the pros and cons are mentioned at:
|
# you should judge for yourself, the pros and cons are mentioned at:
|
||||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||||
#
|
#
|
||||||
# Pods/
|
Pods/
|
||||||
|
Podfile.lock
|
||||||
#
|
#
|
||||||
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
||||||
# *.xcworkspace
|
# *.xcworkspace
|
||||||
|
|||||||
24
AppleParty/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Version**
|
||||||
|
- OS:
|
||||||
|
- AppleParty:
|
||||||
14
AppleParty/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: enhancement
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Tell us how we can improve AppleParty**
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
|
||||||
|
**What would you like to see? How would you like it to work?**
|
||||||
99
AppleParty/.gitignore
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# Xcode
|
||||||
|
#
|
||||||
|
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||||
|
|
||||||
|
## User settings
|
||||||
|
xcuserdata/
|
||||||
|
|
||||||
|
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
|
||||||
|
*.xcscmblueprint
|
||||||
|
*.xccheckout
|
||||||
|
|
||||||
|
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
|
||||||
|
build/
|
||||||
|
DerivedData/
|
||||||
|
*.moved-aside
|
||||||
|
*.pbxuser
|
||||||
|
!default.pbxuser
|
||||||
|
*.mode1v3
|
||||||
|
!default.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
!default.mode2v3
|
||||||
|
*.perspectivev3
|
||||||
|
!default.perspectivev3
|
||||||
|
|
||||||
|
## Obj-C/Swift specific
|
||||||
|
*.hmap
|
||||||
|
|
||||||
|
## App packaging
|
||||||
|
*.ipa
|
||||||
|
*.dSYM.zip
|
||||||
|
*.dSYM
|
||||||
|
|
||||||
|
## Playgrounds
|
||||||
|
timeline.xctimeline
|
||||||
|
playground.xcworkspace
|
||||||
|
|
||||||
|
# Swift Package Manager
|
||||||
|
#
|
||||||
|
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
||||||
|
# Packages/
|
||||||
|
# Package.pins
|
||||||
|
# Package.resolved
|
||||||
|
# *.xcodeproj
|
||||||
|
#
|
||||||
|
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
|
||||||
|
# hence it is not needed unless you have added a package configuration file to your project
|
||||||
|
# .swiftpm
|
||||||
|
|
||||||
|
.build/
|
||||||
|
|
||||||
|
# CocoaPods
|
||||||
|
#
|
||||||
|
# We recommend against adding the Pods directory to your .gitignore. However
|
||||||
|
# you should judge for yourself, the pros and cons are mentioned at:
|
||||||
|
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||||
|
#
|
||||||
|
Pods/
|
||||||
|
|
||||||
|
|
||||||
|
# Carthage
|
||||||
|
#
|
||||||
|
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||||
|
# Carthage/Checkouts
|
||||||
|
|
||||||
|
Carthage/Build
|
||||||
|
|
||||||
|
|
||||||
|
# Accio dependency management
|
||||||
|
Dependencies/
|
||||||
|
.accio/
|
||||||
|
|
||||||
|
# fastlane
|
||||||
|
#
|
||||||
|
# It is recommended to not store the screenshots in the git repo.
|
||||||
|
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
|
||||||
|
# For more information about the recommended setup visit:
|
||||||
|
# https://docs.fastlane.tools/best-practices/source-control/#source-control
|
||||||
|
|
||||||
|
fastlane/report.xml
|
||||||
|
fastlane/Preview.html
|
||||||
|
fastlane/screenshots/**/*.png
|
||||||
|
fastlane/test_output
|
||||||
|
|
||||||
|
# Code Injection
|
||||||
|
#
|
||||||
|
# After new code Injection tools there's a generated folder /iOSInjectionProject
|
||||||
|
# https://github.com/johnno1962/injectionforxcode
|
||||||
|
|
||||||
|
iOSInjectionProject/
|
||||||
|
|
||||||
|
|
||||||
|
## Other
|
||||||
|
*.xcuserstate
|
||||||
|
|
||||||
|
## Obj-C/Swift specific
|
||||||
|
*.hmap
|
||||||
|
*.ipa
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
1368
AppleParty/AppleParty.xcodeproj/project.pbxproj
Normal file
7
AppleParty/AppleParty.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
10
AppleParty/AppleParty.xcworkspace/contents.xcworkspacedata
generated
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:AppleParty.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
{
|
||||||
|
"originHash" : "9d0d9b5cf3459d54c921a64b1bb7a803edf7e8255bc0e8227a135b3820aeee10",
|
||||||
|
"pins" : [
|
||||||
|
{
|
||||||
|
"identity" : "appstoreconnect-swift-sdk",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/AvdLee/appstoreconnect-swift-sdk.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "78b2be2f68f30141fca2f7bce45ca7866535cf28",
|
||||||
|
"version" : "4.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "bluecryptor",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/Kitura/BlueCryptor.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "cec97c24b111351e70e448972a7d3fe68a756d6d",
|
||||||
|
"version" : "2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "bluesocket",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/Kitura/BlueSocket.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "7b23a867008e0027bfd6f4d398d44720707bc8ca",
|
||||||
|
"version" : "2.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "bluesslservice",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/Kitura/BlueSSLService.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "b27a94d063962dfa1bba9f79814c4ef202cf33a4",
|
||||||
|
"version" : "2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "loggerapi",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/Kitura/LoggerAPI.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "e82d34eab3f0b05391082b11ea07d3b70d2f65bb",
|
||||||
|
"version" : "1.9.200"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "swift-asn1",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/apple/swift-asn1.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "f70225981241859eb4aa1a18a75531d26637c8cc",
|
||||||
|
"version" : "1.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "swift-crypto",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/apple/swift-crypto.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "95ba0316a9b733e92bb6b071255ff46263bbe7dc",
|
||||||
|
"version" : "3.15.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "swift-log",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/apple/swift-log.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "6fe203dc33195667ce1759bf0182975e4653ba1c",
|
||||||
|
"version" : "1.4.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "swift-smtp",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/Kitura/Swift-SMTP",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "4b7666bb8cee33f0cb367786af17b9a2ebb63047",
|
||||||
|
"version" : "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "urlqueryencoder",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/CreateAPI/URLQueryEncoder.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "4ce950479707ea109f229d7230ec074a133b15d7",
|
||||||
|
"version" : "0.2.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version" : 3
|
||||||
|
}
|
||||||
52
AppleParty/AppleParty/AppDelegate.swift
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
//
|
||||||
|
// AppDelegate.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/3/10.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import Sparkle
|
||||||
|
|
||||||
|
@main
|
||||||
|
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
|
var mainWindow: NSWindow?
|
||||||
|
@IBOutlet weak var updaterController: SPUStandardUpdaterController!
|
||||||
|
|
||||||
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
|
// 后台检查更新
|
||||||
|
updaterController.updater.checkForUpdatesInBackground()
|
||||||
|
}
|
||||||
|
|
||||||
|
func applicationWillTerminate(_ aNotification: Notification) {
|
||||||
|
// Insert code here to tear down your application
|
||||||
|
}
|
||||||
|
|
||||||
|
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 当关闭最后一个窗口时,退出app
|
||||||
|
/// - Parameter sender:
|
||||||
|
/// - Returns: true-窗口程序两者都关闭,false-只关闭窗口
|
||||||
|
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 应用窗口重新打开时
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - sender:
|
||||||
|
/// - flag:
|
||||||
|
/// - Returns:
|
||||||
|
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func showHelp(_ sender: Any) {
|
||||||
|
let url = URL(string: kApplePartyWiKi)
|
||||||
|
NSWorkspace.shared.open(url!)
|
||||||
|
}
|
||||||
|
}
|
||||||
76
AppleParty/AppleParty/AppListView/APAppListAdapter.swift
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
//
|
||||||
|
// APAppListAdapter.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/3/17.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class APAppListAdapter: NSObject {
|
||||||
|
|
||||||
|
public var purchseHandle: ((_ app: App) -> Void)?
|
||||||
|
public var screenshotHandle: ((_ app: App) -> Void)?
|
||||||
|
|
||||||
|
fileprivate static let numberOfSections = 1
|
||||||
|
fileprivate static let itemId = "APAppListCell"
|
||||||
|
|
||||||
|
fileprivate var items = [App]() {
|
||||||
|
didSet {
|
||||||
|
collectionView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private var collectionView: NSCollectionView
|
||||||
|
|
||||||
|
init(collectionView: NSCollectionView) {
|
||||||
|
self.collectionView = collectionView
|
||||||
|
super.init()
|
||||||
|
self.collectionView.dataSource = self
|
||||||
|
self.collectionView.delegate = self
|
||||||
|
self.collectionView.register(APAppListCell.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: APAppListAdapter.itemId))
|
||||||
|
|
||||||
|
let itemWidth = CGFloat(350.0)
|
||||||
|
let itemHeight = CGFloat(150.0)
|
||||||
|
let itemSpacing = CGFloat(80.0)
|
||||||
|
let itemPadding = CGFloat(30.0)
|
||||||
|
|
||||||
|
let flowLayout = NSCollectionViewFlowLayout()
|
||||||
|
flowLayout.scrollDirection = .vertical
|
||||||
|
flowLayout.itemSize = NSMakeSize(itemWidth, itemHeight)
|
||||||
|
flowLayout.minimumInteritemSpacing = itemSpacing
|
||||||
|
flowLayout.minimumLineSpacing = itemSpacing
|
||||||
|
flowLayout.sectionInset = NSEdgeInsetsMake(itemPadding, itemPadding, itemPadding, itemPadding)
|
||||||
|
self.collectionView.collectionViewLayout = flowLayout
|
||||||
|
}
|
||||||
|
|
||||||
|
func set(items: [App]) {
|
||||||
|
self.items = items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension APAppListAdapter: NSCollectionViewDataSource, NSCollectionViewDelegate {
|
||||||
|
func numberOfSectionsInCollectionView(collectionView: NSCollectionView) -> Int {
|
||||||
|
return APAppListAdapter.numberOfSections
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return items.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
|
||||||
|
let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: APAppListAdapter.itemId), for: indexPath)
|
||||||
|
guard let collectionViewItem = item as? APAppListCell else { return item }
|
||||||
|
|
||||||
|
collectionViewItem.configure(app: items[indexPath.item])
|
||||||
|
collectionViewItem.purchseHandle = purchseHandle
|
||||||
|
collectionViewItem.screenshotHandle = screenshotHandle
|
||||||
|
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
|
||||||
|
collectionView.deselectItems(at: indexPaths)
|
||||||
|
}
|
||||||
|
}
|
||||||
44
AppleParty/AppleParty/AppListView/APAppListCell.swift
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//
|
||||||
|
// APAppListCell.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/3/17.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class APAppListCell: NSCollectionViewItem {
|
||||||
|
|
||||||
|
public var purchseHandle: ((_ app: App) -> Void)?
|
||||||
|
public var screenshotHandle: ((_ app: App) -> Void)?
|
||||||
|
|
||||||
|
@IBOutlet weak var imgView: NSImageView!
|
||||||
|
@IBOutlet weak var nameView: NSTextField!
|
||||||
|
|
||||||
|
private var app: App?
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
nameView.maximumNumberOfLines = 2
|
||||||
|
imgView.wantsLayer = true
|
||||||
|
imgView.layer?.cornerRadius = 22
|
||||||
|
imgView.layer?.masksToBounds = true
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedPurchseItem(_ sender: Any) {
|
||||||
|
purchseHandle?(app!)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedScreenshotItem(_ sender: Any) {
|
||||||
|
screenshotHandle?(app!)
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure(app: App) {
|
||||||
|
self.app = app
|
||||||
|
nameView.stringValue = app.appName
|
||||||
|
imgView?.showWebImage(app.iconUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
83
AppleParty/AppleParty/AppListView/APAppListCell.xib
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="APAppListCell" customModule="AppleParty" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="imgView" destination="ua3-NY-53n" id="kAD-Bq-3qV"/>
|
||||||
|
<outlet property="nameView" destination="66e-57-Sbq" id="3Bf-jl-Gem"/>
|
||||||
|
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<customView id="Hz6-mo-xeY">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="404" height="182"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ua3-NY-53n">
|
||||||
|
<rect key="frame" x="10" y="20" width="142" height="142"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" secondItem="ua3-NY-53n" secondAttribute="height" id="HYa-6B-qMQ"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="PlaceholderIcon" id="WgU-9p-su6"/>
|
||||||
|
</imageView>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="66e-57-Sbq">
|
||||||
|
<rect key="frame" x="170" y="141" width="216" height="21"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="charWrapping" truncatesLastVisibleLine="YES" allowsUndo="NO" title="App" id="0Xh-fB-Cb2">
|
||||||
|
<font key="font" metaFont="systemBold" size="18"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aV2-cM-e0o">
|
||||||
|
<rect key="frame" x="165" y="64" width="132" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="ZLF-9M-xkM"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="上传商店图/视频" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="bPm-Fb-nKp">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" size="13" name="STSongti-SC-Regular"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedScreenshotItem:" target="-2" id="8Nl-TD-kpN"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="osx-a5-YMW">
|
||||||
|
<rect key="frame" x="165" y="99" width="114" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="7Jd-yY-gR9"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="上传内购项目" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="w9k-1j-6eh">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedPurchseItem:" target="-2" id="Rze-yt-gng"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="ua3-NY-53n" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="20" id="51T-Sw-w41"/>
|
||||||
|
<constraint firstItem="aV2-cM-e0o" firstAttribute="leading" secondItem="66e-57-Sbq" secondAttribute="leading" id="VlU-Rb-hoR"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="66e-57-Sbq" secondAttribute="trailing" constant="20" id="dic-eb-ugT"/>
|
||||||
|
<constraint firstItem="66e-57-Sbq" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="20" symbolic="YES" id="gaf-lX-5U5"/>
|
||||||
|
<constraint firstItem="ua3-NY-53n" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="10" id="j8M-Z4-uEW"/>
|
||||||
|
<constraint firstItem="osx-a5-YMW" firstAttribute="top" secondItem="66e-57-Sbq" secondAttribute="bottom" constant="15" id="kpc-Ag-sOu"/>
|
||||||
|
<constraint firstItem="66e-57-Sbq" firstAttribute="leading" secondItem="ua3-NY-53n" secondAttribute="trailing" constant="20" id="tvr-pZ-tsU"/>
|
||||||
|
<constraint firstItem="osx-a5-YMW" firstAttribute="leading" secondItem="66e-57-Sbq" secondAttribute="leading" id="uFZ-bD-xHJ"/>
|
||||||
|
<constraint firstItem="ua3-NY-53n" firstAttribute="centerY" secondItem="Hz6-mo-xeY" secondAttribute="centerY" id="uo4-mG-tIg"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="ua3-NY-53n" secondAttribute="bottom" constant="20" id="wJA-Fd-dWN"/>
|
||||||
|
<constraint firstItem="aV2-cM-e0o" firstAttribute="top" secondItem="osx-a5-YMW" secondAttribute="bottom" constant="15" id="zua-6z-GtP"/>
|
||||||
|
</constraints>
|
||||||
|
<point key="canvasLocation" x="116" y="129"/>
|
||||||
|
</customView>
|
||||||
|
</objects>
|
||||||
|
<resources>
|
||||||
|
<image name="PlaceholderIcon" width="141" height="141"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
||||||
83
AppleParty/AppleParty/AppListView/APAppListModel.swift
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
//
|
||||||
|
// APAppListModel.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/3/17.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// MARK: - 游戏列表
|
||||||
|
struct AppList {
|
||||||
|
var games: [App]
|
||||||
|
|
||||||
|
init(body: [String: Any]) {
|
||||||
|
games = [App]()
|
||||||
|
let included = dictionaryArray(body["included"])
|
||||||
|
let apps = dictionaryArray(body["data"])
|
||||||
|
for software in apps {
|
||||||
|
var game = App()
|
||||||
|
let attributes = dictionary(software["attributes"])
|
||||||
|
game.appId = string(from: software["id"])
|
||||||
|
game.appName = string(from: attributes["name"])
|
||||||
|
game.platforms = string(from: attributes["distributionType"])
|
||||||
|
game.bundleId = string(from: attributes["bundleId"])
|
||||||
|
game.sku = string(from: attributes["sku"])
|
||||||
|
game.primaryLocale = string(from: attributes["primaryLocale"])
|
||||||
|
// icon 处理
|
||||||
|
let appVersion = dictionaryArray( dictionary( dictionary(software["relationships"])["appStoreVersions"])["data"]).first
|
||||||
|
if let version = appVersion {
|
||||||
|
let vid = string(from: version["id"])
|
||||||
|
for info in included {
|
||||||
|
let iid = string(from: info["id"])
|
||||||
|
if vid == iid, vid.count > 0 {
|
||||||
|
let info_att = dictionary(info["attributes"])
|
||||||
|
let storeIcon = dictionary(info_att["storeIcon"])
|
||||||
|
let templateUrl = string(from: storeIcon["templateUrl"])
|
||||||
|
if templateUrl.count > 0 {
|
||||||
|
game.iconUrl = templateUrl.replacingOccurrences(of: "{w}x{h}bb.{f}", with: "500x500bb.png")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
games.append(game)
|
||||||
|
}
|
||||||
|
games = games.sorted(by: { (g1, g2) -> Bool in
|
||||||
|
g1.appName < g2.appName
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct App {
|
||||||
|
var appId: String = ""
|
||||||
|
var appName: String = ""
|
||||||
|
var platforms: String = ""
|
||||||
|
var iconUrl: String = ""
|
||||||
|
var bundleId: String = ""
|
||||||
|
var sku: String = ""
|
||||||
|
var primaryLocale: String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AppInfo {
|
||||||
|
var name: String = ""
|
||||||
|
var bundleId: String = ""
|
||||||
|
var bundleIdReferenceName: String = ""
|
||||||
|
var distributionType: String = ""
|
||||||
|
var educationDiscountType: String = ""
|
||||||
|
var sku: String = ""
|
||||||
|
var primaryLocale: String = ""
|
||||||
|
|
||||||
|
init(body: [String: Any]) {
|
||||||
|
let data = dictionary(body["data"])
|
||||||
|
let attributes = dictionary(data["attributes"])
|
||||||
|
name = string(from: attributes["name"])
|
||||||
|
bundleId = string(from: attributes["bundleId"])
|
||||||
|
bundleIdReferenceName = string(from: attributes["bundleIdReferenceName"])
|
||||||
|
distributionType = string(from: attributes["distributionType"])
|
||||||
|
educationDiscountType = string(from: attributes["educationDiscountType"])
|
||||||
|
sku = string(from: attributes["sku"])
|
||||||
|
primaryLocale = string(from: attributes["primaryLocale"])
|
||||||
|
}
|
||||||
|
}
|
||||||
58
AppleParty/AppleParty/AppListView/APAppListVC.swift
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
//
|
||||||
|
// APAppListVC.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/3/17.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class APAppListVC: NSViewController {
|
||||||
|
|
||||||
|
fileprivate var adapter: APAppListAdapter?
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
configureCollectionView()
|
||||||
|
fetchAppList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 配置显示的功能列表
|
||||||
|
func configureCollectionView() {
|
||||||
|
let colview = APCollectionView()
|
||||||
|
colview.configure(superView: view)
|
||||||
|
adapter = APAppListAdapter(collectionView: colview.collectionView)
|
||||||
|
adapter?.purchseHandle = { [weak self] app in
|
||||||
|
let sb = NSStoryboard(name: "APInAppPurchseVC", bundle: nil)
|
||||||
|
let wc = sb.instantiateController(withIdentifier: "APInAppPurchseVC") as! NSWindowController
|
||||||
|
let vc = wc.contentViewController as! APInAppPurchseVC
|
||||||
|
vc.currentApp = app
|
||||||
|
wc.showWindow(self)
|
||||||
|
}
|
||||||
|
adapter?.screenshotHandle = { [weak self] app in
|
||||||
|
let sb = NSStoryboard(name: "ScreenShotUpload", bundle: nil)
|
||||||
|
let wc = sb.instantiateController(withIdentifier: "ScreenShotUploadVC") as! NSWindowController
|
||||||
|
let vc = wc.contentViewController as! ScreenShotUploadVC
|
||||||
|
vc.currentApp = app
|
||||||
|
wc.showWindow(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - 网络请求
|
||||||
|
extension APAppListVC {
|
||||||
|
|
||||||
|
func fetchAppList() {
|
||||||
|
APClient.appList(status: .filter(nil)).request(showLoading: true, inView: self.view) { [weak self] result, response, error in
|
||||||
|
guard let err = error else {
|
||||||
|
let gamelist = AppList(body: result)
|
||||||
|
self?.adapter?.set(items: gamelist.games)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
APHUD.hide(message: err.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="f9w-9S-vuR">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Window Controller-->
|
||||||
|
<scene sceneID="W73-xg-4SH">
|
||||||
|
<objects>
|
||||||
|
<windowController storyboardIdentifier="AppList" id="f9w-9S-vuR" sceneMemberID="viewController">
|
||||||
|
<window key="window" title="My Apps" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="mcp-fI-0bQ">
|
||||||
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
|
<rect key="contentRect" x="270" y="342" width="600" height="450"/>
|
||||||
|
<rect key="screenRect" x="0.0" y="0.0" width="1800" height="1125"/>
|
||||||
|
<value key="minSize" type="size" width="900" height="560"/>
|
||||||
|
<view key="contentView" id="ukO-uZ-d7I">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="f9w-9S-vuR" id="AQB-f7-cDr"/>
|
||||||
|
</connections>
|
||||||
|
</window>
|
||||||
|
<connections>
|
||||||
|
<segue destination="xCt-xq-Ud0" kind="relationship" relationship="window.shadowedContentViewController" id="xMD-q7-vQF"/>
|
||||||
|
</connections>
|
||||||
|
</windowController>
|
||||||
|
<customObject id="LJV-vg-xy1" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-188" y="160"/>
|
||||||
|
</scene>
|
||||||
|
<!--App ListVC-->
|
||||||
|
<scene sceneID="RSl-ja-SD9">
|
||||||
|
<objects>
|
||||||
|
<viewController id="xCt-xq-Ud0" customClass="APAppListVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" id="JzO-os-HSp">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="nCT-dm-ck1" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="515" y="151"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
||||||
@ -0,0 +1,158 @@
|
|||||||
|
//
|
||||||
|
// APInAppPurchseCell.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/3/28.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class APInAppPurchseCell: NSTableCellView {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImageViewCell: NSTableCellView {
|
||||||
|
|
||||||
|
@IBOutlet weak var imgSel: NSImageView!
|
||||||
|
|
||||||
|
override func awakeFromNib() {
|
||||||
|
super.awakeFromNib()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UploadCell: NSTableCellView {
|
||||||
|
|
||||||
|
var row: Int = 0
|
||||||
|
|
||||||
|
@IBOutlet weak var imgSel: NSImageView!
|
||||||
|
@IBOutlet weak var dragView: DragView!
|
||||||
|
@IBOutlet weak var dragBox: NSView!
|
||||||
|
|
||||||
|
typealias CallBackFunc = (_ path: String, _ row: Int) -> Void
|
||||||
|
var callBackFunc: CallBackFunc?
|
||||||
|
|
||||||
|
override func awakeFromNib() {
|
||||||
|
super.awakeFromNib()
|
||||||
|
dragView.delegate = self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UploadCell: DragViewDelegate {
|
||||||
|
func dragView(_ path: String?) {
|
||||||
|
if let path = path {
|
||||||
|
debugPrint(path)
|
||||||
|
imgSel.image = NSImage(contentsOfFile: path)
|
||||||
|
if let callBackFunc = callBackFunc {
|
||||||
|
callBackFunc(path, row)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
imgSel.image = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum ColumnIdetifier: String {
|
||||||
|
case id
|
||||||
|
case productID
|
||||||
|
case productName
|
||||||
|
case priceLevel
|
||||||
|
case appleid
|
||||||
|
case price
|
||||||
|
case type
|
||||||
|
case state
|
||||||
|
|
||||||
|
// list
|
||||||
|
case productPds
|
||||||
|
case level
|
||||||
|
case status
|
||||||
|
case screenshot
|
||||||
|
case language
|
||||||
|
case upload
|
||||||
|
case picname
|
||||||
|
|
||||||
|
var columnValue: NSUserInterfaceItemIdentifier {
|
||||||
|
return NSUserInterfaceItemIdentifier(rawValue: self.rawValue+"Column")
|
||||||
|
}
|
||||||
|
var cellValue: NSUserInterfaceItemIdentifier {
|
||||||
|
return NSUserInterfaceItemIdentifier(rawValue: self.rawValue+"Cell")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NSUserInterfaceItemIdentifier {
|
||||||
|
func stringValue() -> String {
|
||||||
|
switch self {
|
||||||
|
case ColumnIdetifier.id.columnValue:
|
||||||
|
return ColumnIdetifier.id.rawValue
|
||||||
|
case ColumnIdetifier.productID.columnValue:
|
||||||
|
return ColumnIdetifier.productID.rawValue
|
||||||
|
case ColumnIdetifier.productName.columnValue:
|
||||||
|
return ColumnIdetifier.productName.rawValue
|
||||||
|
case ColumnIdetifier.price.columnValue:
|
||||||
|
return ColumnIdetifier.price.rawValue
|
||||||
|
case ColumnIdetifier.type.columnValue:
|
||||||
|
return ColumnIdetifier.type.rawValue
|
||||||
|
case ColumnIdetifier.state.columnValue:
|
||||||
|
return ColumnIdetifier.state.rawValue
|
||||||
|
case ColumnIdetifier.productPds.columnValue:
|
||||||
|
return ColumnIdetifier.productPds.rawValue
|
||||||
|
case ColumnIdetifier.level.columnValue:
|
||||||
|
return ColumnIdetifier.level.rawValue
|
||||||
|
case ColumnIdetifier.status.columnValue:
|
||||||
|
return ColumnIdetifier.status.rawValue
|
||||||
|
case ColumnIdetifier.appleid.columnValue:
|
||||||
|
return ColumnIdetifier.appleid.rawValue
|
||||||
|
case ColumnIdetifier.priceLevel.columnValue:
|
||||||
|
return ColumnIdetifier.priceLevel.rawValue
|
||||||
|
case ColumnIdetifier.screenshot.columnValue:
|
||||||
|
return ColumnIdetifier.screenshot.rawValue
|
||||||
|
case ColumnIdetifier.picname.columnValue:
|
||||||
|
return ColumnIdetifier.picname.rawValue
|
||||||
|
case ColumnIdetifier.upload.columnValue:
|
||||||
|
return ColumnIdetifier.upload.rawValue
|
||||||
|
case ColumnIdetifier.language.columnValue:
|
||||||
|
return ColumnIdetifier.language.rawValue
|
||||||
|
default:
|
||||||
|
return "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func enumValue() -> NSUserInterfaceItemIdentifier {
|
||||||
|
switch self {
|
||||||
|
case ColumnIdetifier.id.columnValue:
|
||||||
|
return ColumnIdetifier.id.cellValue
|
||||||
|
case ColumnIdetifier.productID.columnValue:
|
||||||
|
return ColumnIdetifier.productID.cellValue
|
||||||
|
case ColumnIdetifier.productName.columnValue:
|
||||||
|
return ColumnIdetifier.productName.cellValue
|
||||||
|
case ColumnIdetifier.price.columnValue:
|
||||||
|
return ColumnIdetifier.price.cellValue
|
||||||
|
case ColumnIdetifier.type.columnValue:
|
||||||
|
return ColumnIdetifier.type.cellValue
|
||||||
|
case ColumnIdetifier.state.columnValue:
|
||||||
|
return ColumnIdetifier.state.cellValue
|
||||||
|
case ColumnIdetifier.productPds.columnValue:
|
||||||
|
return ColumnIdetifier.productPds.cellValue
|
||||||
|
case ColumnIdetifier.level.columnValue:
|
||||||
|
return ColumnIdetifier.level.cellValue
|
||||||
|
case ColumnIdetifier.status.columnValue:
|
||||||
|
return ColumnIdetifier.status.cellValue
|
||||||
|
case ColumnIdetifier.appleid.columnValue:
|
||||||
|
return ColumnIdetifier.appleid.cellValue
|
||||||
|
case ColumnIdetifier.priceLevel.columnValue:
|
||||||
|
return ColumnIdetifier.priceLevel.cellValue
|
||||||
|
case ColumnIdetifier.screenshot.columnValue:
|
||||||
|
return ColumnIdetifier.screenshot.cellValue
|
||||||
|
case ColumnIdetifier.picname.columnValue:
|
||||||
|
return ColumnIdetifier.picname.cellValue
|
||||||
|
case ColumnIdetifier.upload.columnValue:
|
||||||
|
return ColumnIdetifier.upload.cellValue
|
||||||
|
case ColumnIdetifier.language.columnValue:
|
||||||
|
return ColumnIdetifier.language.cellValue
|
||||||
|
default:
|
||||||
|
return NSUserInterfaceItemIdentifier(rawValue: "none")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,475 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23727"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Window Controller-->
|
||||||
|
<scene sceneID="yaf-bM-ro6">
|
||||||
|
<objects>
|
||||||
|
<windowController storyboardIdentifier="APInAppPurchseVC" id="GBA-jI-3If" sceneMemberID="viewController">
|
||||||
|
<window key="window" title="内购买项目管理" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="nwi-c6-OjX">
|
||||||
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
|
<rect key="contentRect" x="425" y="462" width="600" height="450"/>
|
||||||
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
|
||||||
|
<value key="minSize" type="size" width="600" height="450"/>
|
||||||
|
<view key="contentView" id="eYM-5O-yjP">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="GBA-jI-3If" id="5Vc-2R-wtG"/>
|
||||||
|
</connections>
|
||||||
|
</window>
|
||||||
|
<connections>
|
||||||
|
<segue destination="WHe-rZ-F7O" kind="relationship" relationship="window.shadowedContentViewController" id="KEB-Al-cX7"/>
|
||||||
|
</connections>
|
||||||
|
</windowController>
|
||||||
|
<customObject id="fxq-MF-Z4G" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-193" y="236"/>
|
||||||
|
</scene>
|
||||||
|
<!--In App PurchseVC-->
|
||||||
|
<scene sceneID="tfY-7B-aIS">
|
||||||
|
<objects>
|
||||||
|
<viewController id="WHe-rZ-F7O" customClass="APInAppPurchseVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" id="9ub-G9-EoX">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="790" height="225"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<scrollView autohidesScrollers="YES" horizontalLineScroll="52" horizontalPageScroll="10" verticalLineScroll="52" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="V54-fz-e6e">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="790" height="164"/>
|
||||||
|
<clipView key="contentView" ambiguous="YES" id="i9I-WP-piD">
|
||||||
|
<rect key="frame" x="1" y="1" width="788" height="162"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<outlineView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="50" rowSizeStyle="automatic" headerView="Vci-E4-hZF" viewBased="YES" indentationPerLevel="16" outlineTableColumn="kmX-0Y-Prt" id="Yd4-ve-MqM">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="788" height="134"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<size key="intercellSpacing" width="3" height="2"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<tableViewGridLines key="gridStyleMask" vertical="YES" horizontal="YES"/>
|
||||||
|
<color key="gridColor" red="0.91372549020000005" green="0.91372549020000005" blue="0.91372549020000005" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<tableColumns>
|
||||||
|
<tableColumn identifier="idColumn" width="60" minWidth="60" maxWidth="70" id="kmX-0Y-Prt">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="ID">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="GpP-s7-Ouk">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="idCell" id="lp2-R8-h3a">
|
||||||
|
<rect key="frame" x="1" y="1" width="65" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="6Wx-lc-fpC">
|
||||||
|
<rect key="frame" x="6" y="17" width="16" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="aNd-6b-TqP"/>
|
||||||
|
<constraint firstAttribute="width" constant="16" id="mLS-PQ-s6k"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="unselect" id="woP-Wq-aBW"/>
|
||||||
|
</imageView>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="aNG-P7-E3Y">
|
||||||
|
<rect key="frame" x="23" y="17" width="96" height="16"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="hjI-ir-7iT">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="aNG-P7-E3Y" firstAttribute="centerY" secondItem="6Wx-lc-fpC" secondAttribute="centerY" id="CMf-lo-HE5"/>
|
||||||
|
<constraint firstItem="6Wx-lc-fpC" firstAttribute="centerY" secondItem="lp2-R8-h3a" secondAttribute="centerY" id="RE7-tC-iFF"/>
|
||||||
|
<constraint firstItem="6Wx-lc-fpC" firstAttribute="leading" secondItem="lp2-R8-h3a" secondAttribute="leading" constant="6" id="VKE-G2-7hp"/>
|
||||||
|
<constraint firstItem="aNG-P7-E3Y" firstAttribute="leading" secondItem="6Wx-lc-fpC" secondAttribute="trailing" constant="3" id="yxl-Wx-leE"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="aNG-P7-E3Y" id="eZN-eA-lug"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="productIDColumn" width="149.5" minWidth="40" maxWidth="1000" id="dWg-Q6-LNF">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Product ID">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="coU-2a-usX">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="productIDCell" id="HMV-5V-AY4">
|
||||||
|
<rect key="frame" x="69" y="1" width="150" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6sM-uW-9cW">
|
||||||
|
<rect key="frame" x="0.0" y="17" width="149" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="5d1-mj-cla">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="6sM-uW-9cW" id="jSl-Zp-v33"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="productNameColumn" width="101.5" minWidth="40" maxWidth="1000" id="cu0-Fg-efY">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="品项名称">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="DUN-WZ-MnU">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="productNameCell" id="GlU-7h-ss9">
|
||||||
|
<rect key="frame" x="221.5" y="1" width="101" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9VL-cf-yHZ">
|
||||||
|
<rect key="frame" x="-1" y="17" width="101" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Q3a-OZ-g6W">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="9VL-cf-yHZ" id="2Lk-ZX-d07"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="priceLevelColumn" width="54.5" minWidth="40" maxWidth="100" id="QvX-dg-tZC">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="销售范围">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<comboBoxCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" alignment="center" buttonBordered="NO" completes="NO" numberOfVisibleItems="5" id="KhK-VP-9gv">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<objectValues>
|
||||||
|
<string>6</string>
|
||||||
|
<string>12</string>
|
||||||
|
<string>18</string>
|
||||||
|
</objectValues>
|
||||||
|
</comboBoxCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="priceLevelCell" id="Ixq-9W-hrr">
|
||||||
|
<rect key="frame" x="326" y="1" width="55" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nh9-yz-azS">
|
||||||
|
<rect key="frame" x="-1" y="17" width="55" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="CfZ-Ol-bRD">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="nh9-yz-azS" id="HB2-QL-ySt"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="priceColumn" width="78" minWidth="60" maxWidth="100" id="vDg-bl-irs">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="价格机制">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<comboBoxCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" alignment="center" buttonBordered="NO" completes="NO" numberOfVisibleItems="5" id="Qh1-4g-d40">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<objectValues>
|
||||||
|
<string>6</string>
|
||||||
|
<string>12</string>
|
||||||
|
<string>18</string>
|
||||||
|
</objectValues>
|
||||||
|
</comboBoxCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="priceCell" id="fHK-u3-suB">
|
||||||
|
<rect key="frame" x="383.5" y="1" width="78" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="JSr-Bq-gso">
|
||||||
|
<rect key="frame" x="-1" y="17" width="78" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="XMd-gw-vxG">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="JSr-Bq-gso" id="SkZ-iA-u7Q"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="appleidColumn" width="120" minWidth="40" maxWidth="130" id="FFJ-KU-Lsi">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Apple ID">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<comboBoxCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" alignment="center" buttonBordered="NO" completes="NO" numberOfVisibleItems="5" id="89a-ev-30f">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<objectValues>
|
||||||
|
<string>6</string>
|
||||||
|
<string>12</string>
|
||||||
|
<string>18</string>
|
||||||
|
</objectValues>
|
||||||
|
</comboBoxCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="appleidCell" id="2r2-YN-gxc">
|
||||||
|
<rect key="frame" x="464.5" y="1" width="120" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Zr2-bI-CNv">
|
||||||
|
<rect key="frame" x="-1" y="17" width="120" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="rHF-4a-zo7">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="Zr2-bI-CNv" id="R0Z-ey-UBH"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="typeColumn" width="99.5" minWidth="40" maxWidth="100" id="BAk-nA-FHU">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="品项类型">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="ePF-D6-oMG">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="typeCell" id="Lym-V5-iX8">
|
||||||
|
<rect key="frame" x="587.5" y="1" width="99" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KmJ-Eh-Ikj">
|
||||||
|
<rect key="frame" x="-1" y="17" width="99" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="TQy-ks-kac">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="KmJ-Eh-Ikj" id="U29-Bd-kYT"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="stateColumn" width="58.5" minWidth="40" maxWidth="110" id="zNJ-vs-M9B">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="状态">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="Ny4-hA-TaW">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="stateCell" id="JqD-IF-M9E">
|
||||||
|
<rect key="frame" x="690" y="1" width="63" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bt8-9h-N0G">
|
||||||
|
<rect key="frame" x="0.0" y="17" width="62" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="hXq-cj-HG6">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="bt8-9h-N0G" id="ZlI-AB-lkJ"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
</tableColumns>
|
||||||
|
</outlineView>
|
||||||
|
</subviews>
|
||||||
|
</clipView>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="150" id="ysI-Go-iNV"/>
|
||||||
|
</constraints>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="oPx-Do-j7O">
|
||||||
|
<rect key="frame" x="1" y="35" width="1113" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="yaM-yh-Nqt">
|
||||||
|
<rect key="frame" x="804" y="29" width="15" height="42"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<tableHeaderView key="headerView" wantsLayer="YES" id="Vci-E4-hZF">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="788" height="28"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</tableHeaderView>
|
||||||
|
</scrollView>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="RWS-I4-Hj5">
|
||||||
|
<rect key="frame" x="18" y="189" width="72" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="b1e-La-oHG"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="AppName" id="ocn-p7-7tY">
|
||||||
|
<font key="font" metaFont="systemBold" size="14"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="g1s-qC-zZv">
|
||||||
|
<rect key="frame" x="248" y="172" width="94" height="48"/>
|
||||||
|
<buttonCell key="cell" type="push" title="刷新" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="PBX-Aw-4gO">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="80" id="KLz-QE-MgQ"/>
|
||||||
|
<constraint firstAttribute="height" constant="36" id="VRS-Wp-jqM"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="reloadIAPs:" target="WHe-rZ-F7O" id="NKR-Dj-n4M"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="g4b-K9-FLu">
|
||||||
|
<rect key="frame" x="338" y="172" width="114" height="48"/>
|
||||||
|
<buttonCell key="cell" type="push" title="导入表格" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="vsX-vy-Zvo">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="100" id="duV-PA-5xP"/>
|
||||||
|
<constraint firstAttribute="height" constant="36" id="wU1-by-aaT"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="importExcel:" target="WHe-rZ-F7O" id="NyY-A2-rYY"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="DoY-Xj-sox">
|
||||||
|
<rect key="frame" x="448" y="172" width="114" height="48"/>
|
||||||
|
<buttonCell key="cell" type="push" title="导出表格" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Qpw-5P-Y95">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="36" id="S2N-Zb-bUT"/>
|
||||||
|
<constraint firstAttribute="width" constant="100" id="SQi-d8-LOD"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="outputExcel:" target="WHe-rZ-F7O" id="07F-Jd-GLD"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pDs-nU-YDZ">
|
||||||
|
<rect key="frame" x="668" y="172" width="114" height="48"/>
|
||||||
|
<buttonCell key="cell" type="push" title="下载表格示例" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="cwE-He-2kf">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="36" id="6T9-y8-fcw"/>
|
||||||
|
<constraint firstAttribute="width" constant="100" id="cHT-mK-iEg"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="downloadExcel:" target="WHe-rZ-F7O" id="Hdd-v5-BPn"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fCY-Bo-Sre">
|
||||||
|
<rect key="frame" x="558" y="172" width="114" height="48"/>
|
||||||
|
<buttonCell key="cell" type="push" title="导出品项ID" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="cwm-HN-x0f">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="100" id="LMH-kg-AeM"/>
|
||||||
|
<constraint firstAttribute="height" constant="36" id="RFy-br-N3a"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="outputProductID:" target="WHe-rZ-F7O" id="JyB-hQ-Tn0"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="V54-fz-e6e" firstAttribute="top" secondItem="g4b-K9-FLu" secondAttribute="bottom" constant="15" id="3V5-A4-VMG"/>
|
||||||
|
<constraint firstItem="fCY-Bo-Sre" firstAttribute="centerY" secondItem="g1s-qC-zZv" secondAttribute="centerY" id="3wr-s7-mlV"/>
|
||||||
|
<constraint firstItem="fCY-Bo-Sre" firstAttribute="leading" secondItem="DoY-Xj-sox" secondAttribute="trailing" constant="10" id="657-v3-Nxa"/>
|
||||||
|
<constraint firstItem="RWS-I4-Hj5" firstAttribute="leading" secondItem="9ub-G9-EoX" secondAttribute="leading" constant="20" symbolic="YES" id="6NV-i6-UqO"/>
|
||||||
|
<constraint firstItem="RWS-I4-Hj5" firstAttribute="centerY" secondItem="g1s-qC-zZv" secondAttribute="centerY" id="8pU-c6-L46"/>
|
||||||
|
<constraint firstItem="DoY-Xj-sox" firstAttribute="centerY" secondItem="g1s-qC-zZv" secondAttribute="centerY" id="9f0-vD-ZSY"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="V54-fz-e6e" secondAttribute="bottom" id="BQe-7k-3BG"/>
|
||||||
|
<constraint firstItem="g4b-K9-FLu" firstAttribute="top" secondItem="9ub-G9-EoX" secondAttribute="top" constant="10" id="Loj-uR-Xw3"/>
|
||||||
|
<constraint firstItem="g4b-K9-FLu" firstAttribute="leading" secondItem="g1s-qC-zZv" secondAttribute="trailing" constant="10" id="QdO-2i-xQc"/>
|
||||||
|
<constraint firstItem="V54-fz-e6e" firstAttribute="leading" secondItem="9ub-G9-EoX" secondAttribute="leading" id="RJg-65-WD6"/>
|
||||||
|
<constraint firstItem="g1s-qC-zZv" firstAttribute="top" secondItem="9ub-G9-EoX" secondAttribute="top" constant="10" id="TmA-QA-knT"/>
|
||||||
|
<constraint firstItem="pDs-nU-YDZ" firstAttribute="leading" secondItem="fCY-Bo-Sre" secondAttribute="trailing" constant="10" id="al9-zg-mfC"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="pDs-nU-YDZ" secondAttribute="trailing" constant="15" id="ems-r3-Hzs"/>
|
||||||
|
<constraint firstItem="pDs-nU-YDZ" firstAttribute="centerY" secondItem="g1s-qC-zZv" secondAttribute="centerY" id="f8v-tI-hgZ"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="V54-fz-e6e" secondAttribute="trailing" id="gDP-cl-PDs"/>
|
||||||
|
<constraint firstItem="DoY-Xj-sox" firstAttribute="leading" secondItem="g4b-K9-FLu" secondAttribute="trailing" constant="10" id="oVv-xx-EqB"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="appNameView" destination="RWS-I4-Hj5" id="B2s-1y-ClK"/>
|
||||||
|
<outlet property="outlineView" destination="Yd4-ve-MqM" id="gzd-Pc-XWm"/>
|
||||||
|
<outlet property="outputIAPListBtn" destination="DoY-Xj-sox" id="Age-qN-Qlj"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="LWD-js-20w" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="651" y="228.5"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
<resources>
|
||||||
|
<image name="unselect" width="16" height="16"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
||||||
@ -0,0 +1,302 @@
|
|||||||
|
//
|
||||||
|
// APInAppPurchseVC.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/3/28.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class APInAppPurchseVC: NSViewController {
|
||||||
|
|
||||||
|
public var currentApp: App? {
|
||||||
|
didSet {
|
||||||
|
fetchIAPs()
|
||||||
|
appNameView.stringValue = currentApp?.appName ?? ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var iapList: [IAPList.IAP] = []
|
||||||
|
private var countryCode = ""
|
||||||
|
private var checkPrice = [String: String]()
|
||||||
|
|
||||||
|
@IBOutlet weak var outputIAPListBtn: NSButton!
|
||||||
|
@IBOutlet weak var appNameView: NSTextField!
|
||||||
|
@IBOutlet weak var outlineView: NSOutlineView!
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
self.outlineView.delegate = self
|
||||||
|
self.outlineView.dataSource = self
|
||||||
|
self.outlineView.columnAutoresizingStyle = .uniformColumnAutoresizingStyle
|
||||||
|
self.outlineView.selectionHighlightStyle = .none
|
||||||
|
self.outlineView.allowsMultipleSelection = true
|
||||||
|
self.outlineView.sizeToFit()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func reloadIAPs(_ sender: Any) {
|
||||||
|
fetchIAPs()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func importExcel(_ sender: Any) {
|
||||||
|
let openPanel = NSOpenPanel()
|
||||||
|
openPanel.canChooseFiles = true
|
||||||
|
openPanel.canChooseDirectories = true
|
||||||
|
openPanel.allowsMultipleSelection = false
|
||||||
|
openPanel.beginSheetModal(for: self.view.window!) { [self] (modalResponse: NSApplication.ModalResponse) in
|
||||||
|
if modalResponse == .OK, let filePath = openPanel.url {
|
||||||
|
handelExcel(filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@IBAction func outputExcel(_ sender: Any) {
|
||||||
|
|
||||||
|
guard iapList.count > 0 else {
|
||||||
|
NSAlert.show("当前商品为空~")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建格式
|
||||||
|
var iaps = "productId, 商品名称, 价格等级, 价格(\(countryCode)), AppleID, 商品类型, 状态, 送审图片\n"
|
||||||
|
let separator = "\",\""
|
||||||
|
iaps += iapList.map { item -> String in
|
||||||
|
return "\"" + item.vendorId + separator + item.referenceName + separator + item.priceTier + separator + (checkPrice[item.priceTier] ?? "-") + separator + item.adamId
|
||||||
|
+ separator + item.addOnType.CNValue() + separator + item.iTunesConnectStatus.statusValue.0 + separator + item.reviewScreenshot + "\""
|
||||||
|
}.joined(separator: "\n")
|
||||||
|
|
||||||
|
// 保存文件
|
||||||
|
let dateFormatter : DateFormatter = DateFormatter()
|
||||||
|
dateFormatter.dateFormat = "yyyyMMdd_HHmm_"
|
||||||
|
let currentDate = dateFormatter.string(from: Date())
|
||||||
|
let mySave = NSSavePanel()
|
||||||
|
mySave.allowedFileTypes = ["csv"]
|
||||||
|
mySave.nameFieldStringValue = "内购列表" + currentDate + currentApp!.appName.replacingOccurrences(of: " ", with: "-")
|
||||||
|
mySave.begin { (result) -> Void in
|
||||||
|
if result == .OK {
|
||||||
|
let filePath = mySave.url
|
||||||
|
do {
|
||||||
|
// 含有中文,excel在打开CSV文件时默认用ASNI打开,无BOM头的unicode文件会出现乱码
|
||||||
|
var data = Data([0xEF, 0xBB, 0xBF])
|
||||||
|
data.append(contentsOf: iaps.data(using: .utf8) ?? Data())
|
||||||
|
try data.write(to: filePath!)
|
||||||
|
} catch {
|
||||||
|
NSAlert.show("导出失败:\(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@IBAction func outputProductID(_ sender: Any) {
|
||||||
|
let mainStoryboard = NSStoryboard(name: "InAppPurchseView", bundle: Bundle(for: self.classForCoder))
|
||||||
|
let outputVC = mainStoryboard.instantiateController(withIdentifier: "OutputExcelVCID") as? OutputExcelVC
|
||||||
|
outputVC?.iapList = iapList
|
||||||
|
presentAsSheet(outputVC!)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func downloadExcel(_ sender: Any) {
|
||||||
|
XMLManager.copySimpleExel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - 网络请求
|
||||||
|
extension APInAppPurchseVC {
|
||||||
|
|
||||||
|
// 请求商品列表
|
||||||
|
func fetchIAPs() {
|
||||||
|
APClient.iaps(appid: currentApp!.appId).request(showLoading: true, inView: self.view) { [weak self] result, response, error in
|
||||||
|
guard let err = error else {
|
||||||
|
guard let app = self?.currentApp else { return } //请求过程关闭页面可能导致为空
|
||||||
|
let iapL = IAPList(body:result, app: app)
|
||||||
|
self?.iapList = iapL.iapList
|
||||||
|
self?.outlineView.reloadData()
|
||||||
|
self?.updateRowInfo()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
APHUD.hide(message: err.localizedDescription, view: self?.view ?? currentView())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateRowInfo() {
|
||||||
|
// TODO: 接口请求失败
|
||||||
|
return;
|
||||||
|
|
||||||
|
guard self.iapList.count > 0 else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
outputIAPListBtn.isEnabled = false
|
||||||
|
let group = DispatchGroup()
|
||||||
|
for i in 0..<self.iapList.count {
|
||||||
|
group.enter()
|
||||||
|
let iapid = iapList[i].adamId
|
||||||
|
// 商品详细
|
||||||
|
APClient.inAppPurchaseDetail(iapid: iapid).request { [weak self] result, response, error in
|
||||||
|
group.leave()
|
||||||
|
guard let err = error else {
|
||||||
|
if var newModel = self?.iapList[i] {
|
||||||
|
newModel.updateDetail(body: result)
|
||||||
|
self?.iapList[i] = newModel
|
||||||
|
self?.outlineView.reloadData()
|
||||||
|
group.enter()
|
||||||
|
// 商品价格
|
||||||
|
APClient.inAppPurchasePrices(iapid: iapid).request { [weak self] result, response, error in
|
||||||
|
group.leave()
|
||||||
|
guard let err = error else {
|
||||||
|
if var newModel = self?.iapList[i] {
|
||||||
|
newModel.updatePrices(body: result)
|
||||||
|
self?.iapList[i] = newModel
|
||||||
|
self?.outlineView.reloadData()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
APHUD.hide(message: err.localizedDescription, view: self?.view ?? currentView())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
APHUD.hide(message: err.localizedDescription, view: self?.view ?? currentView())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group.notify(queue: .main) {
|
||||||
|
self.outputIAPListBtn.isEnabled = true
|
||||||
|
self.outlineView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 内部方法
|
||||||
|
extension APInAppPurchseVC {
|
||||||
|
|
||||||
|
|
||||||
|
func handelExcel(_ excelFilePath: URL) {
|
||||||
|
let iaps = IAPExcelParser.parser(excelFilePath)
|
||||||
|
|
||||||
|
// 检查内购品项是否后台已经创建过
|
||||||
|
if iapList.isNotEmpty {
|
||||||
|
let pids = iaps.map { $0.productId }
|
||||||
|
let iaps = iapList.map { $0.vendorId }
|
||||||
|
let warns = pids.filter { iaps.contains($0) }
|
||||||
|
if warns.isNotEmpty {
|
||||||
|
NSAlert.show("‼️警告:已经存在相同商品id的品项!请检查:\(warns)。\n⚠️提示:如果继续上传,将会覆盖已有品项的信息!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查商品同名,苹果后台不允许存在同名
|
||||||
|
let iapNames = iaps.map { $0.name }
|
||||||
|
let iapUniNames = Array(Set(iapNames))
|
||||||
|
if iapNames.isNotEmpty, iapNames.count != iapUniNames.count {
|
||||||
|
NSAlert.show("‼️警告:存在相同参考名字的商品!请检查~\n⚠️提示:如果继续上传,同名的商品只会有一个能创建成功!")
|
||||||
|
}
|
||||||
|
|
||||||
|
let sb = NSStoryboard(name: "InAppPurchseView", bundle: Bundle(for: self.classForCoder))
|
||||||
|
let wc = sb.instantiateController(withIdentifier: "InputExcelList") as? NSWindowController
|
||||||
|
let vc = wc?.contentViewController as? APUploadIAPListVC
|
||||||
|
vc?.currentApp = currentApp
|
||||||
|
vc?.iaps = iaps
|
||||||
|
wc?.showWindow(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func setModels(models: [IAPList.IAP]) {
|
||||||
|
iapList = models
|
||||||
|
outlineView.reloadData()
|
||||||
|
outlineView.sizeToFit()
|
||||||
|
updateRowInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - NSOutlineViewDelegate
|
||||||
|
extension APInAppPurchseVC: NSOutlineViewDelegate, NSOutlineViewDataSource {
|
||||||
|
|
||||||
|
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
|
||||||
|
if let item = item as? IAPList.IAP {
|
||||||
|
switch tableColumn?.identifier.enumValue() {
|
||||||
|
case ColumnIdetifier.id.cellValue:
|
||||||
|
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.id.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = String(item.curid)
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.productID.cellValue:
|
||||||
|
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.productID.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = item.vendorId
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.productName.cellValue:
|
||||||
|
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.productName.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = item.referenceName
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.price.cellValue:
|
||||||
|
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.price.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = checkPrice[item.priceTier] ?? "-"
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.priceLevel.cellValue:
|
||||||
|
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.priceLevel.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = item.priceTier
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.appleid.cellValue:
|
||||||
|
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.appleid.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = item.adamId
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.type.cellValue:
|
||||||
|
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.type.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = item.addOnType.CNValue()
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.state.cellValue:
|
||||||
|
let cell = outlineView.makeView(withIdentifier: ColumnIdetifier.state.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = item.iTunesConnectStatus.statusValue.0
|
||||||
|
cell?.textField?.textColor = item.iTunesConnectStatus.statusValue.1
|
||||||
|
return cell
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}else if let item = item as? String {
|
||||||
|
switch tableColumn?.identifier.enumValue() {
|
||||||
|
case ColumnIdetifier.productID.cellValue:
|
||||||
|
let imgView = NSImageView()
|
||||||
|
imgView.showWebImage(item)
|
||||||
|
return imgView
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
|
||||||
|
if item is IAPList.IAP {
|
||||||
|
return 1
|
||||||
|
}else {
|
||||||
|
return iapList.count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
|
||||||
|
if item is IAPList.IAP {
|
||||||
|
return true
|
||||||
|
}else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
|
||||||
|
if let item = item as? IAPList.IAP {
|
||||||
|
return item.reviewScreenshot as Any
|
||||||
|
}
|
||||||
|
return iapList[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat {
|
||||||
|
if item is IAPList.IAP {
|
||||||
|
return 35
|
||||||
|
}else {
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner"/>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
</objects>
|
||||||
|
</document>
|
||||||
@ -0,0 +1,806 @@
|
|||||||
|
//
|
||||||
|
// InputTableListVC.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by 易承 on 2020/12/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class APUploadIAPListVC: NSViewController {
|
||||||
|
|
||||||
|
@IBOutlet weak var tableView: NSTableView!
|
||||||
|
@IBOutlet weak var enterBtn: NSButton!
|
||||||
|
@IBOutlet weak var preserveCurrentPriceBtn: NSButton!
|
||||||
|
@IBOutlet weak var showApiRateLimitLogsBtn: NSButton!
|
||||||
|
|
||||||
|
public var currentApp: App? {
|
||||||
|
didSet {
|
||||||
|
setupUI()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public var iaps = [IAPProduct]() {
|
||||||
|
didSet {
|
||||||
|
self.tableView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var screenshotPaths = [String: String]()
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
self.tableView.columnAutoresizingStyle = .uniformColumnAutoresizingStyle
|
||||||
|
self.tableView.selectionHighlightStyle = .none
|
||||||
|
self.tableView.sizeToFit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupUI() {
|
||||||
|
self.view.window?.title = "批量内购买项目上传 - " + (currentApp?.appName ?? "")
|
||||||
|
self.tableView.reloadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
func showUploadView() {
|
||||||
|
// 不能同时 present 出来
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
|
||||||
|
let mainStoryboard = NSStoryboard(name: "InAppPurchseView", bundle: Bundle(for: self.classForCoder))
|
||||||
|
let upVC = mainStoryboard.instantiateController(withIdentifier: "IAPUploadVCID") as? IAPUploadImageVC
|
||||||
|
let screenshot = self.iaps.filter({ $0.reviewScreenshot.count > 0 }).map({ $0.reviewScreenshot })
|
||||||
|
// 去重后的图片名
|
||||||
|
let uniquedshot = screenshot.enumerated().filter { (index, value) -> Bool in
|
||||||
|
return screenshot.firstIndex(of: value) == index
|
||||||
|
}.map { $0.element }
|
||||||
|
upVC?.picnames = uniquedshot
|
||||||
|
upVC?.callBackFunc = { paths in
|
||||||
|
self.screenshotPaths = paths
|
||||||
|
self.tableView.reloadData()
|
||||||
|
}
|
||||||
|
self.presentAsSheet(upVC!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedUploadShotBtn(_ sender: Any) {
|
||||||
|
showUploadView()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedSPasswordBtn(_ sender: Any) {
|
||||||
|
let vc = APASCKeysSettingVC()
|
||||||
|
presentAsSheet(vc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func createIAP(_ sender: Any) {
|
||||||
|
let list = self.iaps
|
||||||
|
guard list.count > 0 else {
|
||||||
|
APHUD.hide(message: "当前 App 无上传的内购商品!", delayTime: 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
guard let appid = currentApp?.appId else {
|
||||||
|
APHUD.hide(message: "当前 App 的 appleid 为空!", delayTime: 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
enterBtn.isEnabled = false
|
||||||
|
APHUD.show(message: "上传中", view: self.view)
|
||||||
|
let uploadIAPs: ((AppStoreConnectKey) -> Void) = { [weak self] ascKey in
|
||||||
|
// 上传数据
|
||||||
|
self?.updateInAppPurchse(iaps: list, appId: appid, ascKey: ascKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let ascKey = InfoCenter.shared.currentASCKey else {
|
||||||
|
let vc = APASCKeysSettingVC()
|
||||||
|
vc.updateCompletion = { password in
|
||||||
|
if let ascKey = password {
|
||||||
|
uploadIAPs(ascKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
presentAsSheet(vc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadIAPs(ascKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 网络请求
|
||||||
|
extension APUploadIAPListVC {
|
||||||
|
|
||||||
|
func updateInAppPurchse(iaps: [IAPProduct], appId: String, ascKey: AppStoreConnectKey) {
|
||||||
|
let showApiRateLimit = showApiRateLimitLogsBtn.state.rawValue == 1
|
||||||
|
let ascAPI = APASCAPI.init(issuerID: ascKey.issuerID,
|
||||||
|
privateKeyID: ascKey.privateKeyID,
|
||||||
|
privateKey: ascKey.privateKey,
|
||||||
|
showApiRateLimit: showApiRateLimit)
|
||||||
|
ascAPI.addMessage("密钥信息:\(ascKey.issuerID), \(ascKey.privateKeyID), \(ascKey.privateKey)")
|
||||||
|
|
||||||
|
Task {
|
||||||
|
// 1、获取当前账号下 app,判断是否包含当前 提交商品的 app
|
||||||
|
guard let apps = await ascAPI.apps() else {
|
||||||
|
self.enterBtn.isEnabled = true
|
||||||
|
APHUD.hide()
|
||||||
|
APHUD.hide(message: "当前请求异常,请检查密钥是否正确~", delayTime: 2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let app = apps.filter { $0.id == appId }
|
||||||
|
guard app.count > 0 else {
|
||||||
|
self.enterBtn.isEnabled = true
|
||||||
|
APHUD.hide()
|
||||||
|
APHUD.hide(message: "当前的密钥没有查到App: \(appId),请检查~", delayTime: 2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 同步显示进度日志
|
||||||
|
let sb = NSStoryboard(name: "APDebugVC", bundle: Bundle(for: self.classForCoder))
|
||||||
|
let newWC = sb.instantiateController(withIdentifier: "APDebugWC") as? NSWindowController
|
||||||
|
newWC?.window?.title = "内购批量上传日志"
|
||||||
|
let logVC = newWC?.contentViewController as? APDebugVC
|
||||||
|
logVC?.debugLog = "开始上传"
|
||||||
|
newWC?.showWindow(self)
|
||||||
|
ascAPI.updateMsg = { messages in
|
||||||
|
logVC?.debugLog = messages.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
ascAPI.addMessage("开始处理内购商品,获取现有商品中...")
|
||||||
|
// 3、获取所有的内购商品,如果存在的商品就直接修改,如果不存在就创建
|
||||||
|
let oldIAPs = await ascAPI.fetchInAppPurchasesList(appId: appId)
|
||||||
|
|
||||||
|
// 4、遍历所有要上传的商品
|
||||||
|
for product in iaps {
|
||||||
|
// 订阅类型与非订单类型不一样的处理逻辑
|
||||||
|
if product.inAppPurchaseType == .AUTO_RENEWABLE {
|
||||||
|
await createRenewSubscription(appId: appId, product: product, ascAPI: ascAPI)
|
||||||
|
} else {
|
||||||
|
await createInAppPurchase(appId: appId, product: product, oldIAPs: oldIAPs, ascAPI: ascAPI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.enterBtn.isEnabled = true
|
||||||
|
APHUD.hide()
|
||||||
|
ascAPI.addMessage("完成全部内购商品,可稍后在苹果后台查看!✅✅✅")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 上传内购类型商品
|
||||||
|
|
||||||
|
/// 创建内购商品
|
||||||
|
func createInAppPurchase(appId: String, product: IAPProduct, oldIAPs: [ASCInAppPurchaseV2], ascAPI: APASCAPI) async {
|
||||||
|
ascAPI.addMessage("开始上传内购商品:\(product.productId),\(product.name) ")
|
||||||
|
// 检查是否已经存在此商品,如果存在就修改信息,如果不存在就创建
|
||||||
|
let iaps = oldIAPs.filter({ $0.attributes?.productID == product.productId })
|
||||||
|
if let iap = iaps.first {
|
||||||
|
ascAPI.addMessage("内购已经存在:\(product.productId) ,开始更新信息中...")
|
||||||
|
// 0. 审核备注如果原来有值,而新字段无值,则使用原值
|
||||||
|
var product = product
|
||||||
|
if let note = iap.attributes?.reviewNote, product.reviewNote.isEmpty {
|
||||||
|
product.reviewNote = note
|
||||||
|
}
|
||||||
|
// 1.修改原商品信息
|
||||||
|
guard let iap = await ascAPI.updateInAppPurchases(iapId: iap.id, product: product) else {
|
||||||
|
// 修改失败
|
||||||
|
ascAPI.addMessage("内购已经存在:\(product.productId) ,更新信息失败!❌ ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 2. 商品价格档位
|
||||||
|
await updateIAPPricePoint(iapId: iap.id, product: product, ascAPI: ascAPI)
|
||||||
|
|
||||||
|
// 3. 商品本地化语言
|
||||||
|
ascAPI.addMessage("开始更新内购本地化版本:\(product.productId)")
|
||||||
|
let localizations = await ascAPI.fetchInAppPurchasesLocalizations(iapId: iap.id)
|
||||||
|
for localization in product.localizations {
|
||||||
|
// 如果已经存在本地化语言,则更新
|
||||||
|
if let locale = localizations.filter({ $0.attributes?.locale == localization.locale }).first {
|
||||||
|
// 更新
|
||||||
|
ascAPI.addMessage("内购已存在本地化版本:\(localization.locale),开始更新信息中...")
|
||||||
|
if (await ascAPI.updateInAppPurchasesLocalization(iapLocaleId: locale.id, localization: localization)) != nil {
|
||||||
|
// 本地化语言更新成功
|
||||||
|
ascAPI.addMessage("内购本地化版本:\(localization.locale) ,更新语言成功!✅ ")
|
||||||
|
} else {
|
||||||
|
// 本地化语言更新失败
|
||||||
|
ascAPI.addMessage("内购本地化版本:\(localization.locale) ,更新语言失败!❌ ")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 创建
|
||||||
|
await createIAPLocalization(iapId: iap.id, localization: localization, product: product, ascAPI: ascAPI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 商品截图
|
||||||
|
await createIAPScreenshot(iapId: iap.id, product: product, ascAPI: ascAPI)
|
||||||
|
|
||||||
|
// 5. 销售国家或地区
|
||||||
|
await updateIAPAvailableTerritories(iapId: iap.id, product: product, ascAPI: ascAPI)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 1. 创建新的商品
|
||||||
|
guard let iap = await ascAPI.createInAppPurchases(appId: appId, product: product) else {
|
||||||
|
// 创建失败
|
||||||
|
ascAPI.addMessage("内购商品:\(product.productId) ,创建失败!❌ ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 2. 商品价格档位
|
||||||
|
await updateIAPPricePoint(iapId: iap.id, product: product, ascAPI: ascAPI)
|
||||||
|
|
||||||
|
// 3. 商品本地化语言
|
||||||
|
for localization in product.localizations {
|
||||||
|
await createIAPLocalization(iapId: iap.id, localization: localization, product: product, ascAPI: ascAPI)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 商品截图
|
||||||
|
await createIAPScreenshot(iapId: iap.id, product: product, ascAPI: ascAPI)
|
||||||
|
|
||||||
|
// 5. 销售国家或地区
|
||||||
|
await updateIAPAvailableTerritories(iapId: iap.id, product: product, ascAPI: ascAPI)
|
||||||
|
}
|
||||||
|
|
||||||
|
ascAPI.addMessage("内购商品:\(product.productId),\(product.name) ,上传完成!\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 创建内购商品价格档位
|
||||||
|
func updateIAPPricePoint(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
|
||||||
|
guard let schedule = product.priceSchedules else {
|
||||||
|
ascAPI.addMessage("无价格计划表:\(product.productId) ,请确认!❌ ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let baseTerritory = schedule.baseTerritory
|
||||||
|
let baseCustomerPrice = schedule.baseCustomerPrice.normalizePrice()
|
||||||
|
|
||||||
|
ascAPI.addMessage("开始更新价格计划表:\(product.productId),\(baseTerritory),\(baseCustomerPrice) \n")
|
||||||
|
|
||||||
|
let points = await ascAPI.fetchPricePoints(iapId: iapId, territory: [baseTerritory])
|
||||||
|
if let point = points.filter({ $0.attributes?.customerPrice!.normalizePrice() == baseCustomerPrice }).first {
|
||||||
|
var manualPrices: [Any] = []
|
||||||
|
var included: [Any] = []
|
||||||
|
|
||||||
|
ascAPI.addMessage("开始构建基准国家和自定价格:")
|
||||||
|
// base Territory
|
||||||
|
manualPrices.append(["id": "${\(baseTerritory)-\(included.count)}", "type": "inAppPurchasePrices"])
|
||||||
|
included.append(ascAPI.fetchInAppPurchasePriceSchedule(scheduleId: baseTerritory, pricePointId: point.id, iapId: iapId, index: included.count))
|
||||||
|
|
||||||
|
// customerPrice
|
||||||
|
for pricePoint in schedule.manualPrices {
|
||||||
|
let territory = pricePoint.territory
|
||||||
|
let customerPrice = pricePoint.customerPrice.normalizePrice()
|
||||||
|
let points = await ascAPI.fetchPricePoints(iapId: iapId, territory: [territory])
|
||||||
|
if let point = points.filter({ $0.attributes?.customerPrice!.normalizePrice() == customerPrice }).first {
|
||||||
|
manualPrices.append(["id": "${\(territory)-\(included.count)}", "type": "inAppPurchasePrices"])
|
||||||
|
included.append(ascAPI.fetchInAppPurchasePriceSchedule(scheduleId: territory, pricePointId: point.id, iapId: iapId, index: included.count))
|
||||||
|
} else {
|
||||||
|
ascAPI.addMessage("自定价格的内购价格点:\(territory),\(customerPrice) ,未找到此档位!❌ ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ascAPI.saveLogs(log: "内购的基准国家和自定价格:\(manualPrices),\(included)")
|
||||||
|
|
||||||
|
if (await ascAPI.updateInAppPurchasePricePoint(iapId: iapId, baseTerritoryId: baseTerritory, manualPrices: manualPrices, included: included)) != nil {
|
||||||
|
// 价格档位配置成功
|
||||||
|
ascAPI.addMessage("内购价格点:\(baseTerritory),\(baseCustomerPrice) ,更新价格成功!✅ ")
|
||||||
|
} else {
|
||||||
|
// 价格档位配置失败
|
||||||
|
ascAPI.addMessage("内购价格点:\(baseTerritory),\(baseCustomerPrice) ,更新价格失败!❌ ")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 找不到价格档位
|
||||||
|
ascAPI.addMessage("基准国家的内购价格点:\(baseTerritory),\(baseCustomerPrice) ,未找到此档位!❌ ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// 创建内购商品本地化信息
|
||||||
|
func createIAPLocalization(iapId: String, localization: IAPLocalization, product: IAPProduct, ascAPI: APASCAPI) async {
|
||||||
|
ascAPI.addMessage("开始更新本地化版本:\(product.productId),\(localization.locale)")
|
||||||
|
if (await ascAPI.createInAppPurchasesLocalization(iapId: iapId, localization: localization)) != nil {
|
||||||
|
// 本地化语言配置成功
|
||||||
|
ascAPI.addMessage("内购本地化版本:\(localization.locale) ,更新语言成功!✅ ")
|
||||||
|
} else {
|
||||||
|
// 本地化语言配置失败
|
||||||
|
ascAPI.addMessage("内购本地化版本:\(localization.locale) ,更新语言失败!❌ ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 更新内购商品的送审截图
|
||||||
|
func createIAPScreenshot(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
|
||||||
|
ascAPI.addMessage("开始更新内购商品的送审截图:\(product.productId),\(product.reviewScreenshot)")
|
||||||
|
let imgName = product.reviewScreenshot
|
||||||
|
guard let imgPath = screenshotPaths[imgName] else {
|
||||||
|
ascAPI.addMessage("内购商品:\(product.productId) 无送审截图或未上传截图~")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let imaUrl = URL.init(fileURLWithPath: imgPath)
|
||||||
|
guard let fileMD5 = URL.init(fileURLWithPath: imgPath).fileMD5() else {
|
||||||
|
ascAPI.addMessage("内购商品截图文件错误:\(imgPath) ,无法生成 md5 值~")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let oldShot = await ascAPI.fetchInAppPurchasesScreenshot(iapId: iapId)
|
||||||
|
// 存在需要删除,避免文件名不一样或者过期文件
|
||||||
|
if let ost = oldShot {
|
||||||
|
ascAPI.addMessage("删除旧的送审截图:\(ost.attributes?.fileName ?? "")")
|
||||||
|
let status = await ascAPI.deleteInAppPurchasesScreenshot(iapShotId: ost.id)
|
||||||
|
if status != 204 {
|
||||||
|
ascAPI.addMessage("内购商品截图创建失败:\(imgName) ,无法删除旧截图~")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ascAPI.addMessage("创建新的送审截图:\(product.reviewScreenshot)")
|
||||||
|
// 创建截图
|
||||||
|
let imaSize = imaUrl.fileSizeInt()
|
||||||
|
guard let shot = await ascAPI.createInAppPurchasesScreenshot(iapId: iapId, fileName: imgName, fileSize: imaSize) else {
|
||||||
|
// 创建失败
|
||||||
|
ascAPI.addMessage("内购商品:\(product.productId) ,创建送审截图失败!❌ ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据苹果接口返回的上传接口上传
|
||||||
|
guard let method = shot.attributes?.uploadOperations?.first?.method,
|
||||||
|
let url = shot.attributes?.uploadOperations?.first?.url,
|
||||||
|
let requestHeaders = shot.attributes?.uploadOperations?.first?.requestHeaders,
|
||||||
|
let baseURL = URL(string: url) else {
|
||||||
|
ascAPI.addMessage("内购商品:\(product.productId) ,创建送审截图失败!苹果参数异常~ ❌ ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = URLRequest(url: baseURL)
|
||||||
|
request.httpMethod = method
|
||||||
|
for header in requestHeaders {
|
||||||
|
request.headers[header.name ?? ""] = header.value ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
ascAPI.addMessage("上传新的送审截图:\(product.reviewScreenshot)")
|
||||||
|
// 上传图片
|
||||||
|
guard let response = try? await URLSession.shared.upload(for: request, fromFile: imaUrl) else {
|
||||||
|
ascAPI.addMessage("内购商品:\(product.productId) ,创建送审截图失败!上传图片异常~ ❌ ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let responseCode = (response.1 as? HTTPURLResponse)?.statusCode, responseCode == 200 else {
|
||||||
|
ascAPI.addMessage("内购商品:\(product.productId) ,创建送审截图失败!上传图片异常 \(response.1.description)~ ❌ ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ascAPI.addMessage("提交新的送审截图:\(product.reviewScreenshot)")
|
||||||
|
// 确认图片
|
||||||
|
if ((await ascAPI.updateInAppPurchasesScreenshot(iapShotId: shot.id, fileMD5: fileMD5)) != nil) {
|
||||||
|
ascAPI.addMessage("内购商品:\(product.productId) ,送审截图上传成功!✅ ")
|
||||||
|
} else {
|
||||||
|
ascAPI.addMessage("内购商品:\(product.productId) ,送审截图可能上传失败! ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 销售国家或地区
|
||||||
|
func updateIAPAvailableTerritories(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
|
||||||
|
let inAll = product.territories.availableInAllTerritories
|
||||||
|
let inNew = product.territories.availableInNewTerritories
|
||||||
|
let summary = territoryInfo(product: product)
|
||||||
|
let newTerritory = inNew ? "将来新国家(地区)时自动提供!" : "将来新国家(地区)时不自动提供!"
|
||||||
|
ascAPI.addMessage("开始更新内购商品的销售国家/地区:\(summary)")
|
||||||
|
|
||||||
|
guard !inAll else {
|
||||||
|
var allTerritories: [[String: String]] = []
|
||||||
|
if let territories = await ascAPI.territories() {
|
||||||
|
territories.forEach { territory in
|
||||||
|
allTerritories.append([
|
||||||
|
"type": "territories",
|
||||||
|
"id": territory.id
|
||||||
|
])
|
||||||
|
}
|
||||||
|
// 更新全部国家地区
|
||||||
|
if (await ascAPI.updateInAppPurchasesAvailabilityTerritories(iapId: iapId, availableTerritories: allTerritories, availableInNewTerritories: inNew)) != nil {
|
||||||
|
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),更新成功!✅ ")
|
||||||
|
} else {
|
||||||
|
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),更新失败!❌ ")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),无法设置!获取国家标识码失败!❌ ")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 选择销售的国家或地区
|
||||||
|
var territories: [[String: String]] = []
|
||||||
|
product.territories.territories?.forEach({ territory in
|
||||||
|
territories.append([
|
||||||
|
"type": "territories",
|
||||||
|
"id": territory.id
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
let customerTerritory = product.territories.territories?.map({ $0.id }).joined(separator: ",") ?? "无"
|
||||||
|
if (await ascAPI.updateInAppPurchasesAvailabilityTerritories(iapId: iapId, availableTerritories: territories, availableInNewTerritories: inNew)) != nil {
|
||||||
|
ascAPI.addMessage("内购商品的销售国家/地区:\(customerTerritory) ,更新成功!✅ ")
|
||||||
|
} else {
|
||||||
|
ascAPI.addMessage("内购商品的销售国家/地区:\(customerTerritory) ,更新失败!❌ ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - 上传订阅商品
|
||||||
|
|
||||||
|
/// 订阅商品创建或更新
|
||||||
|
func createRenewSubscription(appId: String, product: IAPProduct, ascAPI: APASCAPI) async {
|
||||||
|
let groupName = product.groupName
|
||||||
|
var currentSubGroup: ASCSubscriptionGroup?
|
||||||
|
// 1、是否有订阅组,没有时要先创建
|
||||||
|
var subGroups = await ascAPI.fetchSubscriptionGroups(appId: appId)
|
||||||
|
// if subGroups.isEmpty {
|
||||||
|
// }
|
||||||
|
|
||||||
|
for subGroup in subGroups {
|
||||||
|
if subGroup.attributes?.referenceName == groupName {
|
||||||
|
currentSubGroup = subGroup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建订阅组
|
||||||
|
if currentSubGroup == nil, let group = await ascAPI.createSubscriptionGroups(appId: appId, groupName: groupName) {
|
||||||
|
currentSubGroup = group
|
||||||
|
subGroups.append(group)
|
||||||
|
|
||||||
|
for localization in product.localizations {
|
||||||
|
let _ = await ascAPI.createSubscriptionGroupLocalizations(iapGroupId: group.id, name: localization.name, locale: localization.locale, customAppName: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//订阅组设置国际化
|
||||||
|
if let group = currentSubGroup {
|
||||||
|
for localization in product.localizations {
|
||||||
|
let _ = await ascAPI.createSubscriptionGroupLocalizations(iapGroupId: group.id, name: localization.name, locale: localization.locale, customAppName: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2、有订阅组,获取所有订阅组的所有订阅商品
|
||||||
|
var subscriptions = [ASCSubscription]()
|
||||||
|
for subGroup in subGroups {
|
||||||
|
let subs = await ascAPI.fetchSubscriptionGroupSubscriptions(iapGroupId: subGroup.id)
|
||||||
|
subscriptions.append(contentsOf: subs)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 3、查看是否存在订阅商品,不存在就创建,存在就更新
|
||||||
|
let subs = subscriptions.filter({ $0.attributes?.productID == product.productId })
|
||||||
|
if let sub = subs.first {
|
||||||
|
ascAPI.addMessage("订阅商品已经存在:\(product.productId) ,开始更新信息中...")
|
||||||
|
// 0. 审核备注如果原来有值,而新字段无值,则使用原值
|
||||||
|
var product = product
|
||||||
|
if let note = sub.attributes?.reviewNote, product.reviewNote.isEmpty {
|
||||||
|
product.reviewNote = note
|
||||||
|
}
|
||||||
|
// 1.修改原商品信息
|
||||||
|
guard let iap = await ascAPI.updateSubscription(iapId: sub.id, product: product) else {
|
||||||
|
// 修改失败
|
||||||
|
ascAPI.addMessage("订阅商品已经存在:\(product.productId) ,更新信息失败!❌ ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 商品本地化语言
|
||||||
|
ascAPI.addMessage("开始更新订阅商品本地化版本:\(product.productId)")
|
||||||
|
let localizations = await ascAPI.fetchSubscriptionLocalizations(iapId: iap.id)
|
||||||
|
for localization in product.localizations {
|
||||||
|
// 如果已经存在本地化语言,则更新
|
||||||
|
if let locale = localizations.filter({ $0.attributes?.locale == localization.locale }).first {
|
||||||
|
// 更新
|
||||||
|
ascAPI.addMessage("订阅商品已存在本地化版本:\(localization.locale),开始更新信息中...")
|
||||||
|
if (await ascAPI.updateSubscriptionLocalization(iapLocaleId: locale.id, localization: localization)) != nil {
|
||||||
|
// 本地化语言更新成功
|
||||||
|
ascAPI.addMessage("订阅商品本地化版本:\(localization.locale) ,更新语言成功!✅ ")
|
||||||
|
} else {
|
||||||
|
// 本地化语言更新失败
|
||||||
|
ascAPI.addMessage("订阅商品本地化版本:\(localization.locale) ,更新语言失败!❌ ")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 创建
|
||||||
|
await createSubscriptionLocalization(iapId: iap.id, localization: localization, product: product, ascAPI: ascAPI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 商品价格档位
|
||||||
|
await updateSubscriptionPricePoint(iapId: iap.id, product: product, ascAPI: ascAPI)
|
||||||
|
|
||||||
|
// 4. 商品截图
|
||||||
|
await createSubscriptionScreenshot(iapId: iap.id, product: product, ascAPI: ascAPI)
|
||||||
|
|
||||||
|
// 5. 销售国家或地区
|
||||||
|
await updateSubscriptionAvailableTerritories(iapId: iap.id, product: product, ascAPI: ascAPI)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 1. 创建新的商品
|
||||||
|
guard let iapGroupId = currentSubGroup?.id, let iap = await ascAPI.createSubscription(iapGroupId: iapGroupId, product: product) else {
|
||||||
|
// 创建失败
|
||||||
|
ascAPI.addMessage("订阅商品:\(product.productId) ,创建失败!❌ ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 商品本地化语言
|
||||||
|
for localization in product.localizations {
|
||||||
|
await createSubscriptionLocalization(iapId: iap.id, localization: localization, product: product, ascAPI: ascAPI)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 商品价格档位
|
||||||
|
await updateSubscriptionPricePoint(iapId: iap.id, product: product, ascAPI: ascAPI)
|
||||||
|
|
||||||
|
// 4. 商品截图
|
||||||
|
await createSubscriptionScreenshot(iapId: iap.id, product: product, ascAPI: ascAPI)
|
||||||
|
|
||||||
|
// 5. 销售国家或地区
|
||||||
|
await updateSubscriptionAvailableTerritories(iapId: iap.id, product: product, ascAPI: ascAPI)
|
||||||
|
}
|
||||||
|
|
||||||
|
ascAPI.addMessage("订阅商品:\(product.productId),\(product.name) ,上传完成!\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// 更新订阅商品的价格档位
|
||||||
|
func updateSubscriptionPricePoint(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
|
||||||
|
guard let schedule = product.priceSchedules else {
|
||||||
|
ascAPI.addMessage("无价格计划表:\(product.productId) ,请确认!❌ ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let baseTerritory = schedule.baseTerritory
|
||||||
|
let baseCustomerPrice = schedule.baseCustomerPrice.normalizePrice()
|
||||||
|
|
||||||
|
ascAPI.addMessage("开始更新订阅商品价格点,基准国家:\(product.productId),\(baseTerritory),\(baseCustomerPrice) \n")
|
||||||
|
|
||||||
|
let isPreservePrice = preserveCurrentPriceBtn.state.rawValue == 1
|
||||||
|
ascAPI.addMessage("保留自动续期订阅者现有定价:\(isPreservePrice ? "是" : "否")")
|
||||||
|
|
||||||
|
let points = await ascAPI.fetchSubscriptionPricePoints(iapId: iapId, territory: [baseTerritory])
|
||||||
|
if let point = points.filter({ $0.attributes?.customerPrice!.normalizePrice() == baseCustomerPrice }).first {
|
||||||
|
|
||||||
|
ascAPI.addMessage("开始更新自定价格:")
|
||||||
|
// 自定价格的国家或地区, 基准国家也算是自定价格
|
||||||
|
var customerPriceSchedules = schedule.manualPrices
|
||||||
|
customerPriceSchedules.append(IAPPricePoint(territory: baseTerritory, customerPrice: baseCustomerPrice))
|
||||||
|
let manualPricesTerritory: [String] = customerPriceSchedules.map({ $0.territory })
|
||||||
|
// 设置自定价格
|
||||||
|
for pricePoint in customerPriceSchedules {
|
||||||
|
let territory = pricePoint.territory
|
||||||
|
let customerPrice = pricePoint.customerPrice.normalizePrice()
|
||||||
|
let points = await ascAPI.fetchSubscriptionPricePoints(iapId: iapId, territory: [territory])
|
||||||
|
if let point = points.filter({ $0.attributes?.customerPrice!.normalizePrice() == customerPrice }).first {
|
||||||
|
if (await ascAPI.updateSubscriptionPricePoint(iapId: iapId, pricePointId: point.id, preserveCurrentPrice: isPreservePrice)) != nil {
|
||||||
|
ascAPI.addMessage("自定价格的订阅商品的价格点:\(territory),\(customerPrice) ,更新价格成功!✅ ")
|
||||||
|
} else {
|
||||||
|
ascAPI.addMessage("自定价格的订阅商品的价格点:\(territory),\(customerPrice) ,更新价格失败!❌ ")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ascAPI.addMessage("自定价格的订阅商品价格点:\(territory),\(customerPrice) ,未找到此档位!❌ ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ascAPI.addMessage("开始更新全球均衡价格:")
|
||||||
|
// 剩余的所有的国家地区的订阅价格点,然后一个一个设置。API不支持全部国家一次配置
|
||||||
|
let allPoints = await ascAPI.fetchSubscriptionPricePointsEqualizations(pointId: point.id, territory: nil)
|
||||||
|
for apoint in allPoints {
|
||||||
|
let territory = apoint.relationships?.territory?.data?.id ?? ""
|
||||||
|
// 自定价格的国家跳过
|
||||||
|
if manualPricesTerritory.contains(territory) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let customerPrice = apoint.attributes?.customerPrice ?? ""
|
||||||
|
if (await ascAPI.updateSubscriptionPricePoint(iapId: iapId, pricePointId: apoint.id, preserveCurrentPrice: isPreservePrice)) != nil {
|
||||||
|
// 价格档位配置成功
|
||||||
|
ascAPI.addMessage("全球均衡价格的订阅商品的价格点:\(territory),\(customerPrice) ,更新价格成功!✅ ")
|
||||||
|
} else {
|
||||||
|
// 价格档位配置失败
|
||||||
|
ascAPI.addMessage("全球均衡价格的订阅商品的价格点:\(territory),\(customerPrice) ,更新价格失败!❌ ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 找不到价格档位
|
||||||
|
ascAPI.addMessage("基准国家的订阅商品价格点:\(baseTerritory),\(baseCustomerPrice) ,未找到此档位!❌ ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// 更新订阅商品的本地化信息
|
||||||
|
func createSubscriptionLocalization(iapId: String, localization: IAPLocalization, product: IAPProduct, ascAPI: APASCAPI) async {
|
||||||
|
ascAPI.addMessage("开始更新订阅商品本地化版本:\(product.productId),\(localization.locale)")
|
||||||
|
if (await ascAPI.createSubscriptionLocalization(iapId: iapId, localization: localization)) != nil {
|
||||||
|
// 本地化语言配置成功
|
||||||
|
ascAPI.addMessage("订阅商品本地化版本:\(localization.locale) ,更新语言成功!✅ ")
|
||||||
|
} else {
|
||||||
|
// 本地化语言配置失败
|
||||||
|
ascAPI.addMessage("订阅商品本地化版本:\(localization.locale) ,更新语言失败!❌ ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 更新订阅商品的送审截图
|
||||||
|
func createSubscriptionScreenshot(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
|
||||||
|
ascAPI.addMessage("开始更新订阅商品的送审截图:\(product.productId),\(product.reviewScreenshot)")
|
||||||
|
let imgName = product.reviewScreenshot
|
||||||
|
guard let imgPath = screenshotPaths[imgName] else {
|
||||||
|
ascAPI.addMessage("订阅商品:\(product.productId) 无送审截图或未上传截图~")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let imaUrl = URL.init(fileURLWithPath: imgPath)
|
||||||
|
guard let fileMD5 = URL.init(fileURLWithPath: imgPath).fileMD5() else {
|
||||||
|
ascAPI.addMessage("订阅商品截图文件错误:\(imgPath) ,无法生成 md5 值~")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let oldShot = await ascAPI.fetchSubscriptionScreenshot(iapId: iapId)
|
||||||
|
// 存在需要删除,避免文件名不一样或者过期文件
|
||||||
|
if let ost = oldShot {
|
||||||
|
ascAPI.addMessage("删除旧的送审截图:\(ost.attributes?.fileName ?? "")")
|
||||||
|
let status = await ascAPI.deleteSubscriptionScreenshot(iapShotId: ost.id)
|
||||||
|
if status != 204 {
|
||||||
|
ascAPI.addMessage("订阅商品截图创建失败:\(imgName) ,无法删除旧截图~")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ascAPI.addMessage("创建新的送审截图:\(product.reviewScreenshot)")
|
||||||
|
// 创建截图
|
||||||
|
let imaSize = imaUrl.fileSizeInt()
|
||||||
|
guard let shot = await ascAPI.createSubscriptionScreenshot(iapId: iapId, fileName: imgName, fileSize: imaSize) else {
|
||||||
|
// 创建失败
|
||||||
|
ascAPI.addMessage("订阅商品:\(product.productId) ,创建送审截图失败!❌ ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据苹果接口返回的上传接口上传
|
||||||
|
guard let method = shot.attributes?.uploadOperations?.first?.method,
|
||||||
|
let url = shot.attributes?.uploadOperations?.first?.url,
|
||||||
|
let requestHeaders = shot.attributes?.uploadOperations?.first?.requestHeaders,
|
||||||
|
let baseURL = URL(string: url) else {
|
||||||
|
ascAPI.addMessage("订阅商品:\(product.productId) ,创建送审截图失败!苹果参数异常~ ❌ ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = URLRequest(url: baseURL)
|
||||||
|
request.httpMethod = method
|
||||||
|
for header in requestHeaders {
|
||||||
|
request.headers[header.name ?? ""] = header.value ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
ascAPI.addMessage("上传新的送审截图:\(product.reviewScreenshot)")
|
||||||
|
// 上传图片
|
||||||
|
guard let response = try? await URLSession.shared.upload(for: request, fromFile: imaUrl) else {
|
||||||
|
ascAPI.addMessage("订阅商品:\(product.productId) ,创建送审截图失败!上传图片异常~ ❌ ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let responseCode = (response.1 as? HTTPURLResponse)?.statusCode, responseCode == 200 else {
|
||||||
|
ascAPI.addMessage("订阅商品:\(product.productId) ,创建送审截图失败!上传图片异常 \(response.1.description)~ ❌ ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ascAPI.addMessage("提交新的送审截图:\(product.reviewScreenshot)")
|
||||||
|
// 确认图片
|
||||||
|
if ((await ascAPI.updateSubscriptionScreenshot(iapShotId: shot.id, fileMD5: fileMD5)) != nil) {
|
||||||
|
ascAPI.addMessage("订阅商品:\(product.productId) ,送审截图上传成功!✅ ")
|
||||||
|
} else {
|
||||||
|
ascAPI.addMessage("订阅商品:\(product.productId) ,送审截图可能上传失败! ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 销售国家或地区
|
||||||
|
func updateSubscriptionAvailableTerritories(iapId: String, product: IAPProduct, ascAPI: APASCAPI) async {
|
||||||
|
let inAll = product.territories.availableInAllTerritories
|
||||||
|
let inNew = product.territories.availableInNewTerritories
|
||||||
|
let summary = territoryInfo(product: product)
|
||||||
|
let newTerritory = inNew ? "将来新国家(地区)时自动提供!" : "将来新国家(地区)时不自动提供!"
|
||||||
|
ascAPI.addMessage("开始更新订阅商品的销售国家/地区:\(summary)")
|
||||||
|
|
||||||
|
guard !inAll else {
|
||||||
|
var allTerritories: [[String: String]] = []
|
||||||
|
if let territories = await ascAPI.territories() {
|
||||||
|
territories.forEach { territory in
|
||||||
|
allTerritories.append([
|
||||||
|
"type": "territories",
|
||||||
|
"id": territory.id
|
||||||
|
])
|
||||||
|
}
|
||||||
|
// 更新全部国家地区
|
||||||
|
if (await ascAPI.updateSubscriptionAvailabilityTerritories(iapId: iapId, availableTerritories: allTerritories, availableInNewTerritories: inNew)) != nil {
|
||||||
|
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),更新成功!✅ ")
|
||||||
|
} else {
|
||||||
|
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),更新失败!❌ ")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ascAPI.addMessage("选择:所有国家(地区)销售,\(newTerritory),无法设置!获取国家标识码失败!❌ ")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 选择销售的国家或地区
|
||||||
|
var territories: [[String: String]] = []
|
||||||
|
product.territories.territories?.forEach({ territory in
|
||||||
|
territories.append([
|
||||||
|
"type": "territories",
|
||||||
|
"id": territory.id
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
let customerTerritory = product.territories.territories?.map({ $0.id }).joined(separator: ",") ?? "无"
|
||||||
|
if (await ascAPI.updateSubscriptionAvailabilityTerritories(iapId: iapId, availableTerritories: territories, availableInNewTerritories: inNew)) != nil {
|
||||||
|
ascAPI.addMessage("订阅商品的销售国家/地区:\(customerTerritory) ,更新成功!✅ ")
|
||||||
|
} else {
|
||||||
|
ascAPI.addMessage("订阅商品的销售国家/地区:\(customerTerritory) ,更新失败!❌ ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Privacy Method
|
||||||
|
extension APUploadIAPListVC {
|
||||||
|
|
||||||
|
func territoryInfo(product: IAPProduct) -> String {
|
||||||
|
let inAll = product.territories.availableInAllTerritories
|
||||||
|
let inNew = product.territories.availableInNewTerritories
|
||||||
|
let customerTerritory = product.territories.territories?.map({ $0.id }).joined(separator: ",") ?? ""
|
||||||
|
let off = !inAll && !inNew && (product.territories.territories?.isEmpty ?? true)
|
||||||
|
let territory = off ? "下架" : (customerTerritory.isEmpty ? (inAll ? "全部" : "当前下架") : customerTerritory)
|
||||||
|
let stringValue = "在所有国家/地区销售:'\(inAll ? "是" : "否")'\n将来新国家/地区自动提供:'\(inNew ? "是" : "否")'\n指定国家/地区销售:\(territory)"
|
||||||
|
return stringValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - NSTableViewDelegate
|
||||||
|
extension APUploadIAPListVC: NSTableViewDelegate, NSTableViewDataSource {
|
||||||
|
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||||
|
return iaps.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||||
|
let iap = iaps[row]
|
||||||
|
switch tableColumn?.identifier.enumValue() {
|
||||||
|
case ColumnIdetifier.id.cellValue:
|
||||||
|
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.id.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = String(row+1)
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.productID.cellValue:
|
||||||
|
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.productID.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = iap.productId
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.productName.cellValue:
|
||||||
|
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.productName.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = iap.name
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.price.cellValue:
|
||||||
|
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.price.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = territoryInfo(product: iap)
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.level.cellValue:
|
||||||
|
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.level.cellValue, owner: self) as? NSTableCellView
|
||||||
|
let territory = iap.priceSchedules?.baseTerritory ?? "-"
|
||||||
|
let price = iap.priceSchedules?.baseCustomerPrice ?? "-"
|
||||||
|
let customerPrice = iap.priceSchedules?.manualPrices.map({ pp in
|
||||||
|
"{'国家:'\(pp.territory)', '自定价格':'\(pp.customerPrice)'}\n"
|
||||||
|
}).joined() ?? "-"
|
||||||
|
cell?.textField?.stringValue = "基准国家:'\(territory)'\n基准价格:'\(price)'\n\(customerPrice)"
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.productPds.cellValue:
|
||||||
|
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.productPds.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = iap.reviewNote
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.state.cellValue:
|
||||||
|
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.state.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = iap.inAppPurchaseType.CNValue()
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.screenshot.cellValue:
|
||||||
|
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.screenshot.cellValue, owner: self) as? ImageViewCell
|
||||||
|
let file_name = iap.reviewScreenshot
|
||||||
|
let imgPath = screenshotPaths[file_name] ?? ""
|
||||||
|
cell?.imgSel.image = NSImage(contentsOfFile: imgPath)
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.language.cellValue:
|
||||||
|
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.language.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = iap.localizations.map({ lz in
|
||||||
|
"{'locale:'\(lz.locale)', 'title':'\(lz.name)', 'desc':'\(lz.description)'}\n"
|
||||||
|
}).joined()
|
||||||
|
return cell
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
|
||||||
|
let prices = iaps[row].priceSchedules?.manualPrices.count ?? 0
|
||||||
|
let count = max(prices + 2, 3)
|
||||||
|
return count > 10 ? CGFloat(20 * count) : CGFloat(25 * count)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
//
|
||||||
|
// DragView.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by 易承 on 2020/12/16.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
protocol DragViewDelegate {
|
||||||
|
func dragView(_ path: String?)
|
||||||
|
}
|
||||||
|
|
||||||
|
class DragView: NSView {
|
||||||
|
|
||||||
|
var delegate: DragViewDelegate?
|
||||||
|
|
||||||
|
private var fileTypeIsOk = false
|
||||||
|
let NSFilenamesPboardType = NSPasteboard.PasteboardType("NSFilenamesPboardType")
|
||||||
|
let fileTypes = ["jpg", "jpeg", "png"]
|
||||||
|
var droppedFilePath: String?
|
||||||
|
|
||||||
|
override func draw(_ dirtyRect: NSRect) {
|
||||||
|
super.draw(dirtyRect)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
super.init(coder: coder)
|
||||||
|
// Declare and register an array of accepted types
|
||||||
|
registerForDraggedTypes([NSPasteboard.PasteboardType(kUTTypeFileURL as String),
|
||||||
|
NSPasteboard.PasteboardType(kUTTypeItem as String)])
|
||||||
|
}
|
||||||
|
|
||||||
|
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
|
||||||
|
fileTypeIsOk = checkExtension(drag: sender)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation {
|
||||||
|
return fileTypeIsOk ? .link : []
|
||||||
|
}
|
||||||
|
|
||||||
|
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
|
||||||
|
if let board = sender.draggingPasteboard.propertyList(forType: NSFilenamesPboardType) as? NSArray, let imagePath = board[0] as? String {
|
||||||
|
// THIS IS WERE YOU GET THE PATH FOR THE DROPPED FILE
|
||||||
|
droppedFilePath = imagePath
|
||||||
|
if fileTypeIsOk {
|
||||||
|
delegate?.dragView(droppedFilePath)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func checkExtension(drag: NSDraggingInfo) -> Bool {
|
||||||
|
if let board = drag.draggingPasteboard.propertyList(forType: NSFilenamesPboardType) as? NSArray, let path = board[0] as? String {
|
||||||
|
let url = NSURL(fileURLWithPath: path)
|
||||||
|
if let fileExtension = url.pathExtension?.lowercased() {
|
||||||
|
return fileTypes.contains(fileExtension)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,105 @@
|
|||||||
|
//
|
||||||
|
// UploadVC.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by 易承 on 2021/6/3.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class IAPUploadImageVC: NSViewController {
|
||||||
|
|
||||||
|
@IBOutlet weak var tableView: NSTableView!
|
||||||
|
|
||||||
|
@IBOutlet weak var cancelBtm: NSButton!
|
||||||
|
@IBOutlet weak var submitBtm: NSButton!
|
||||||
|
@IBOutlet weak var tipLb: NSTextField!
|
||||||
|
var picnames = [String]()
|
||||||
|
var resultPaths = [String: String]()
|
||||||
|
|
||||||
|
typealias CallBackFunc = (_ paths: [String: String]) -> Void
|
||||||
|
var callBackFunc: CallBackFunc?
|
||||||
|
|
||||||
|
fileprivate lazy var fileTypes: [String] = {
|
||||||
|
return ["jpg", "jpeg", "png"]
|
||||||
|
}()
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
tableView.delegate = self
|
||||||
|
tableView.dataSource = self
|
||||||
|
tipLb.stringValue = "需要上传\(picnames.count)张图片"
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedBatchUploadBtn(_ sender: Any) {
|
||||||
|
let openPanel = NSOpenPanel()
|
||||||
|
openPanel.canChooseFiles = true
|
||||||
|
openPanel.canChooseDirectories = false
|
||||||
|
openPanel.allowsMultipleSelection = true
|
||||||
|
openPanel.allowedFileTypes = fileTypes
|
||||||
|
openPanel.beginSheetModal(for: self.view.window!) { (modalResponse: NSApplication.ModalResponse) in
|
||||||
|
if modalResponse == .OK {
|
||||||
|
openPanel.urls.forEach { url in
|
||||||
|
let picname = url.lastPathComponent
|
||||||
|
debugPrint(picname)
|
||||||
|
if self.picnames.contains(picname) {
|
||||||
|
debugPrint("contains")
|
||||||
|
self.resultPaths[picname] = url.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.tableView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@IBAction func cancel(_ sender: Any) {
|
||||||
|
dismiss(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func submit(_ sender: Any) {
|
||||||
|
guard picnames.count == resultPaths.keys.count else {
|
||||||
|
APHUD.hide(message: "必须图片数量不正确!", view: self.view)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dismiss(self)
|
||||||
|
if let callBackFunc = callBackFunc {
|
||||||
|
callBackFunc(resultPaths)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension IAPUploadImageVC: NSTableViewDelegate, NSTableViewDataSource {
|
||||||
|
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||||
|
return picnames.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||||
|
switch tableColumn?.identifier.enumValue() {
|
||||||
|
case ColumnIdetifier.picname.cellValue:
|
||||||
|
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.picname.cellValue, owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = picnames[row]
|
||||||
|
return cell
|
||||||
|
case ColumnIdetifier.upload.cellValue:
|
||||||
|
let cell = tableView.makeView(withIdentifier: ColumnIdetifier.upload.cellValue, owner: self) as? UploadCell
|
||||||
|
cell?.row = row
|
||||||
|
cell?.dragView(resultPaths[picnames[row]])
|
||||||
|
cell?.callBackFunc = { path,crow in
|
||||||
|
self.resultPaths[self.picnames[crow]] = path
|
||||||
|
}
|
||||||
|
return cell
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
|
||||||
|
100
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,993 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23727"/>
|
||||||
|
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Window Controller-->
|
||||||
|
<scene sceneID="6Md-ET-vaH">
|
||||||
|
<objects>
|
||||||
|
<windowController storyboardIdentifier="InputExcelList" id="x3B-Ma-He7" sceneMemberID="viewController">
|
||||||
|
<window key="window" title="批量内购买项目上传" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="baa-hG-CGu">
|
||||||
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
|
<rect key="contentRect" x="425" y="432" width="600" height="400"/>
|
||||||
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
|
||||||
|
<value key="minSize" type="size" width="600" height="380"/>
|
||||||
|
<view key="contentView" id="iWX-hs-skU">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="400"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="x3B-Ma-He7" id="e65-q9-nQL"/>
|
||||||
|
</connections>
|
||||||
|
</window>
|
||||||
|
<connections>
|
||||||
|
<segue destination="HcW-sO-g2Q" kind="relationship" relationship="window.shadowedContentViewController" id="HBc-rj-vrU"/>
|
||||||
|
</connections>
|
||||||
|
</windowController>
|
||||||
|
<customObject id="3MN-ty-i6n" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-950" y="1930"/>
|
||||||
|
</scene>
|
||||||
|
<!--批量内购买项目上传-->
|
||||||
|
<scene sceneID="biu-hN-rXF">
|
||||||
|
<objects>
|
||||||
|
<viewController title="批量内购买项目上传" id="HcW-sO-g2Q" customClass="APUploadIAPListVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" misplaced="YES" id="K0H-Tj-XIL">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="816" height="363"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<scrollView autohidesScrollers="YES" horizontalLineScroll="40" horizontalPageScroll="10" verticalLineScroll="40" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2iO-iU-0Fy">
|
||||||
|
<rect key="frame" x="0.0" y="100" width="796" height="263"/>
|
||||||
|
<clipView key="contentView" ambiguous="YES" id="TPK-e2-6v9">
|
||||||
|
<rect key="frame" x="1" y="1" width="794" height="261"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<tableView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowHeight="40" headerView="5s8-Sc-F8G" viewBased="YES" id="FfT-VW-kGY">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="794" height="236"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<tableViewGridLines key="gridStyleMask" vertical="YES" horizontal="YES"/>
|
||||||
|
<color key="gridColor" red="0.93333333330000001" green="0.93333333330000001" blue="0.93333333330000001" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<tableColumns>
|
||||||
|
<tableColumn identifier="idColumn" width="30" minWidth="30" maxWidth="40" id="vMK-OX-shk">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="id">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="EXT-G7-gaH">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="idCell" id="ANH-fd-8Ev">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="36" height="40"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="QOn-S3-Fa5">
|
||||||
|
<rect key="frame" x="0.0" y="12" width="36" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="1" id="LsW-Zq-hB0">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="QOn-S3-Fa5" id="adV-BV-fd5"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="screenshotColumn" width="75" minWidth="40" maxWidth="120" id="B7q-XM-l6y">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="截图">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="OtQ-JF-R0L">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="screenshotCell" id="aFa-Mz-jnj" customClass="ImageViewCell" customModule="AppleParty" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="36" y="0.0" width="75" height="40"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="hys-p0-19y">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="75" height="40"/>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="C9E-lZ-VJ9"/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="hys-p0-19y" firstAttribute="leading" secondItem="aFa-Mz-jnj" secondAttribute="leading" id="5aT-8N-sLh"/>
|
||||||
|
<constraint firstItem="hys-p0-19y" firstAttribute="top" secondItem="aFa-Mz-jnj" secondAttribute="top" id="PcE-OE-rpA"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="hys-p0-19y" secondAttribute="trailing" id="QaH-hj-2yB"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="hys-p0-19y" secondAttribute="bottom" id="SDW-Qd-ZWN"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="imgSel" destination="hys-p0-19y" id="FJd-2g-vzv"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="productIDColumn" width="118" minWidth="40" maxWidth="150" id="AaY-pT-reC">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Product ID">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="urO-vI-Sw4">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="productIDCell" id="3o0-NI-ewo">
|
||||||
|
<rect key="frame" x="111" y="0.0" width="118" height="40"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="c8C-RK-lNM">
|
||||||
|
<rect key="frame" x="0.0" y="12" width="118" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="OzZ-QS-Xrv">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="c8C-RK-lNM" id="yrw-fd-VbS"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="stateColumn" width="60" minWidth="40" maxWidth="90" id="K81-yv-X5l">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="类型">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="zey-VL-KI9">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="stateCell" id="hKB-hg-TrY">
|
||||||
|
<rect key="frame" x="229" y="0.0" width="60" height="40"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="D1n-4P-b0Q">
|
||||||
|
<rect key="frame" x="0.0" y="12" width="60" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="1" id="sq1-BL-Rcf">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="D1n-4P-b0Q" id="uHy-rg-Ef7"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="productNameColumn" width="102" minWidth="40" maxWidth="150" id="9em-uk-bzW">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="参考名字">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="mik-0H-hQ1">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="productNameCell" id="Wp7-Z8-fyG">
|
||||||
|
<rect key="frame" x="289" y="0.0" width="102" height="40"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="V05-T9-An4">
|
||||||
|
<rect key="frame" x="0.0" y="12" width="102" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Fyk-ea-jSK">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="V05-T9-An4" id="eHR-Tz-Q5j"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="priceColumn" width="112" minWidth="40" maxWidth="400" id="dHE-xa-6WE">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="销售范围">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="9cd-hH-o3v">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="priceCell" id="M23-59-mE6">
|
||||||
|
<rect key="frame" x="391" y="0.0" width="112" height="40"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LdV-Er-PG6">
|
||||||
|
<rect key="frame" x="0.0" y="12" width="112" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="charWrapping" sendsActionOnEndEditing="YES" title="1" id="k5H-G7-RvE">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<allowedInputSourceLocales>
|
||||||
|
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||||
|
</allowedInputSourceLocales>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="LdV-Er-PG6" id="hKk-WY-If1"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="levelColumn" width="102" minWidth="40" maxWidth="400" id="RAv-kj-cld">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="价格机制">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="2so-VG-q75">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="levelCell" id="Pz4-4W-nO5">
|
||||||
|
<rect key="frame" x="503" y="0.0" width="102" height="40"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="e8p-aX-EOQ">
|
||||||
|
<rect key="frame" x="0.0" y="12" width="102" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="charWrapping" sendsActionOnEndEditing="YES" title="1" id="txV-L7-8pc">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<allowedInputSourceLocales>
|
||||||
|
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||||
|
</allowedInputSourceLocales>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="e8p-aX-EOQ" id="cFO-zR-V7E"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="languageColumn" width="100" minWidth="40" maxWidth="550" id="cPw-xm-W9c">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="本地化语言">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="S22-0C-i35">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="languageCell" id="qG2-Ag-GoS">
|
||||||
|
<rect key="frame" x="605" y="0.0" width="100" height="40"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4pw-S3-VJ2">
|
||||||
|
<rect key="frame" x="0.0" y="3" width="100" height="34"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="charWrapping" sendsActionOnEndEditing="YES" title="1" id="cgN-Ef-H9K">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<allowedInputSourceLocales>
|
||||||
|
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||||
|
</allowedInputSourceLocales>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="4pw-S3-VJ2" id="yZO-no-fs9"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="productPdsColumn" width="38" minWidth="30" maxWidth="150" id="Lic-uH-erC">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="审核备注(可选)">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="6Ts-zi-iQ8">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="productPdsCell" id="tSv-x2-iSQ">
|
||||||
|
<rect key="frame" x="705" y="0.0" width="44" height="40"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="CcF-6d-Y0Y">
|
||||||
|
<rect key="frame" x="0.0" y="12" width="44" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="charWrapping" sendsActionOnEndEditing="YES" title="1" id="RdH-e2-lmI">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="CcF-6d-Y0Y" id="epa-re-I4A"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
</tableColumns>
|
||||||
|
<connections>
|
||||||
|
<outlet property="dataSource" destination="HcW-sO-g2Q" id="TAQ-cE-1Hb"/>
|
||||||
|
<outlet property="delegate" destination="HcW-sO-g2Q" id="cV7-Es-JKZ"/>
|
||||||
|
</connections>
|
||||||
|
</tableView>
|
||||||
|
</subviews>
|
||||||
|
</clipView>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="250" id="88B-Ob-G5p"/>
|
||||||
|
</constraints>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="KNl-rP-gjC">
|
||||||
|
<rect key="frame" x="1" y="246" width="794" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="z1V-UM-aHa">
|
||||||
|
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<tableHeaderView key="headerView" identifier="languageColumn" wantsLayer="YES" id="5s8-Sc-F8G">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="794" height="25"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</tableHeaderView>
|
||||||
|
</scrollView>
|
||||||
|
<box boxType="custom" cornerRadius="4" translatesAutoresizingMaskIntoConstraints="NO" id="DFI-Sf-hCb">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="796" height="100"/>
|
||||||
|
<view key="contentView" id="6rW-KE-ixB">
|
||||||
|
<rect key="frame" x="1" y="1" width="794" height="98"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="yl7-CE-urn">
|
||||||
|
<rect key="frame" x="340" y="25" width="114" height="47"/>
|
||||||
|
<buttonCell key="cell" type="push" title="提交" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ysI-I3-a0g">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
DQ
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<color key="contentTintColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="35" id="L7q-rE-qUa"/>
|
||||||
|
<constraint firstAttribute="width" constant="100" id="luq-WV-0zV"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="createIAP:" target="HcW-sO-g2Q" id="mZJ-aY-eOM"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Wiy-jc-2Pw">
|
||||||
|
<rect key="frame" x="700" y="13" width="86" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="设置密钥" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="AHB-ou-qRW">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="Sbb-sq-gsR"/>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="gLt-9i-Kpk"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedSPasswordBtn:" target="HcW-sO-g2Q" id="4WZ-cz-seF"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gwi-GP-uQf">
|
||||||
|
<rect key="frame" x="184" y="8" width="426" height="16"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="注:请检查数据与表格是否一致,提交成功后,商品id和类型不能再更改!" id="04V-rH-2el">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="systemOrangeColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sv7-52-2Cw">
|
||||||
|
<rect key="frame" x="700" y="45" width="86" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="上传截图" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="n9B-2Z-TXF">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="Os2-cR-YSb"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedUploadShotBtn:" target="HcW-sO-g2Q" id="pfM-0y-Fwy"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mWR-IS-Cxn">
|
||||||
|
<rect key="frame" x="18" y="56" width="194" height="18"/>
|
||||||
|
<buttonCell key="cell" type="check" title="保留自动续期订阅者现有定价" bezelStyle="regularSquare" imagePosition="left" inset="2" id="rcb-F8-Wjv">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="jCn-pM-TqY"/>
|
||||||
|
</constraints>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="O5k-L7-xuw">
|
||||||
|
<rect key="frame" x="18" y="30" width="200" height="18"/>
|
||||||
|
<buttonCell key="cell" type="check" title="显示 ASC API 请求速率的阈值" bezelStyle="regularSquare" imagePosition="left" inset="2" id="7jz-Eq-MTQ">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="UTk-E1-F8x"/>
|
||||||
|
</constraints>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="gwi-GP-uQf" secondAttribute="bottom" constant="8" id="446-fY-M9B"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="Wiy-jc-2Pw" secondAttribute="bottom" constant="20" symbolic="YES" id="7js-1K-u7U"/>
|
||||||
|
<constraint firstItem="O5k-L7-xuw" firstAttribute="top" secondItem="mWR-IS-Cxn" secondAttribute="bottom" constant="10" id="8dK-sn-ZXM"/>
|
||||||
|
<constraint firstItem="yl7-CE-urn" firstAttribute="centerY" secondItem="6rW-KE-ixB" secondAttribute="centerY" id="ITd-YX-Trm"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="sv7-52-2Cw" secondAttribute="trailing" constant="15" id="QUP-CD-c2a"/>
|
||||||
|
<constraint firstItem="Wiy-jc-2Pw" firstAttribute="top" secondItem="sv7-52-2Cw" secondAttribute="bottom" constant="12" id="Rp0-lh-D4G"/>
|
||||||
|
<constraint firstItem="Wiy-jc-2Pw" firstAttribute="trailing" secondItem="sv7-52-2Cw" secondAttribute="trailing" id="a9w-yY-EWd"/>
|
||||||
|
<constraint firstItem="yl7-CE-urn" firstAttribute="centerX" secondItem="6rW-KE-ixB" secondAttribute="centerX" id="efU-0I-QhV"/>
|
||||||
|
<constraint firstItem="mWR-IS-Cxn" firstAttribute="leading" secondItem="6rW-KE-ixB" secondAttribute="leading" constant="20" symbolic="YES" id="hjT-ey-2ZV"/>
|
||||||
|
<constraint firstItem="O5k-L7-xuw" firstAttribute="leading" secondItem="6rW-KE-ixB" secondAttribute="leading" constant="20" symbolic="YES" id="htV-cS-Gxk"/>
|
||||||
|
<constraint firstItem="gwi-GP-uQf" firstAttribute="centerX" secondItem="6rW-KE-ixB" secondAttribute="centerX" id="o1O-9Y-VOc"/>
|
||||||
|
<constraint firstItem="mWR-IS-Cxn" firstAttribute="top" secondItem="6rW-KE-ixB" secondAttribute="top" constant="25" id="yx3-wU-dQT"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="100" id="Nfj-Ag-iuW"/>
|
||||||
|
</constraints>
|
||||||
|
<color key="borderColor" name="quaternaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</box>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="2iO-iU-0Fy" secondAttribute="trailing" id="0jj-T0-SZ6"/>
|
||||||
|
<constraint firstItem="2iO-iU-0Fy" firstAttribute="top" secondItem="K0H-Tj-XIL" secondAttribute="top" id="Dh4-Ma-QQZ"/>
|
||||||
|
<constraint firstItem="2iO-iU-0Fy" firstAttribute="leading" secondItem="K0H-Tj-XIL" secondAttribute="leading" id="GhX-65-sYx"/>
|
||||||
|
<constraint firstItem="DFI-Sf-hCb" firstAttribute="top" secondItem="2iO-iU-0Fy" secondAttribute="bottom" id="U03-9h-hLB"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="DFI-Sf-hCb" secondAttribute="bottom" id="X11-LN-u3M"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="DFI-Sf-hCb" secondAttribute="trailing" id="YC4-6m-bim"/>
|
||||||
|
<constraint firstItem="DFI-Sf-hCb" firstAttribute="leading" secondItem="K0H-Tj-XIL" secondAttribute="leading" id="ieA-uv-pUf"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="enterBtn" destination="yl7-CE-urn" id="fxo-dC-Eo4"/>
|
||||||
|
<outlet property="preserveCurrentPriceBtn" destination="mWR-IS-Cxn" id="kC5-n4-ddM"/>
|
||||||
|
<outlet property="showApiRateLimitLogsBtn" destination="O5k-L7-xuw" id="NkA-Kq-jRr"/>
|
||||||
|
<outlet property="tableView" destination="FfT-VW-kGY" id="eo0-Hf-anq"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="oPL-uS-kEl" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-175" y="1918.5"/>
|
||||||
|
</scene>
|
||||||
|
<!--Upload ImageVC-->
|
||||||
|
<scene sceneID="l5Q-5j-Ieg">
|
||||||
|
<objects>
|
||||||
|
<viewController storyboardIdentifier="IAPUploadVCID" id="ZGW-X2-eY0" customClass="IAPUploadImageVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" id="XlO-n7-hEA">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="449" height="241"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<box boxType="custom" cornerRadius="4" title="Box" translatesAutoresizingMaskIntoConstraints="NO" id="4UC-ET-dPZ">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="449" height="241"/>
|
||||||
|
<view key="contentView" id="bXG-f2-WGM">
|
||||||
|
<rect key="frame" x="1" y="1" width="447" height="239"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rfb-ot-DNY">
|
||||||
|
<rect key="frame" x="13" y="193" width="105" height="16"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="需要传入x张图片" id="oMa-Eg-4Q4">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" red="0.90980392160000001" green="0.4431372549" blue="0.28235294119999998" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kcS-un-E1x">
|
||||||
|
<rect key="frame" x="353" y="192" width="86" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="批量上传" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="W6k-qu-hFp">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="Nq6-G6-yns"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedBatchUploadBtn:" target="ZGW-X2-eY0" id="s3T-H7-xH7"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<scrollView autohidesScrollers="YES" horizontalLineScroll="102" horizontalPageScroll="10" verticalLineScroll="102" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="92G-BP-MIt">
|
||||||
|
<rect key="frame" x="15" y="80" width="417" height="103"/>
|
||||||
|
<clipView key="contentView" ambiguous="YES" id="KqA-NU-fUc">
|
||||||
|
<rect key="frame" x="1" y="1" width="415" height="101"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<tableView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" alternatingRowBackgroundColors="YES" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowHeight="100" headerView="9ym-TL-xUJ" viewBased="YES" id="Pkx-YF-hF7">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="415" height="76"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<size key="intercellSpacing" width="3" height="2"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<tableViewGridLines key="gridStyleMask" vertical="YES" horizontal="YES"/>
|
||||||
|
<color key="gridColor" red="0.9137254901960784" green="0.9137254901960784" blue="0.9137254901960784" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<tableColumns>
|
||||||
|
<tableColumn identifier="picnameColumn" width="151" minWidth="40" maxWidth="1000" id="vzw-Wm-X2f">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="图片名称">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="nPA-Qe-y92">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="picnameCell" id="nHX-Ef-hlo">
|
||||||
|
<rect key="frame" x="1" y="1" width="156" height="17"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="hmR-mk-1ol">
|
||||||
|
<rect key="frame" x="-2" y="-1" width="160" height="20"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="Seh-ch-rSz"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="center" title="Table View Cell" id="hoj-H0-NhX">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="hmR-mk-1ol" firstAttribute="leading" secondItem="nHX-Ef-hlo" secondAttribute="leading" id="BBN-E4-pLd"/>
|
||||||
|
<constraint firstItem="hmR-mk-1ol" firstAttribute="centerX" secondItem="nHX-Ef-hlo" secondAttribute="centerX" id="Otu-Lw-wvf"/>
|
||||||
|
<constraint firstItem="hmR-mk-1ol" firstAttribute="centerY" secondItem="nHX-Ef-hlo" secondAttribute="centerY" id="fK5-ug-JU3"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="hmR-mk-1ol" id="Ufw-70-c7l"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="uploadColumn" width="220" minWidth="40" maxWidth="1000" id="E8R-ck-u3Z">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="上传图片">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="b6z-Gv-phs">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="uploadCell" id="kSW-b0-iBl" customClass="UploadCell" customModule="AppleParty" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="160" y="1" width="224" height="100"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="fOf-F6-66M">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="224" height="100"/>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="YKV-PN-c9B"/>
|
||||||
|
</imageView>
|
||||||
|
<view id="Ml3-Ia-Ewi">
|
||||||
|
<rect key="frame" x="86" y="24" width="121" height="52"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<box boxType="custom" cornerRadius="4" title="Box" translatesAutoresizingMaskIntoConstraints="NO" id="y7w-3d-1iH">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="121" height="52"/>
|
||||||
|
<view key="contentView" id="dTZ-YH-cOp">
|
||||||
|
<rect key="frame" x="1" y="1" width="119" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
</view>
|
||||||
|
<color key="borderColor" red="0.97254901959999995" green="0.97254901959999995" blue="0.97254901959999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<color key="fillColor" name="selectedTextBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</box>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="8nS-Sr-Tez">
|
||||||
|
<rect key="frame" x="45" y="16.5" width="32.5" height="40"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="32" id="8ms-3v-6Vf"/>
|
||||||
|
<constraint firstAttribute="height" constant="32" id="oxZ-DL-wE0"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSAddTemplate" id="qFS-6U-ZiY"/>
|
||||||
|
</imageView>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8Zb-BZ-4UZ">
|
||||||
|
<rect key="frame" x="20" y="4" width="82" height="16"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="拖拽图片上传" id="0LG-sI-2vV">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<customView translatesAutoresizingMaskIntoConstraints="NO" id="zSP-IZ-bba" customClass="DragView" customModule="AppleParty" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="121" height="52"/>
|
||||||
|
</customView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="y7w-3d-1iH" firstAttribute="top" secondItem="Ml3-Ia-Ewi" secondAttribute="top" id="6r8-S0-IOe"/>
|
||||||
|
<constraint firstItem="8Zb-BZ-4UZ" firstAttribute="top" secondItem="8nS-Sr-Tez" secondAttribute="bottom" id="A3W-gz-1Gn"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="y7w-3d-1iH" secondAttribute="bottom" id="Jpg-KB-D6M"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="zSP-IZ-bba" secondAttribute="trailing" id="MyV-Rc-dU6"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="y7w-3d-1iH" secondAttribute="trailing" id="N7y-lw-ncn"/>
|
||||||
|
<constraint firstItem="8nS-Sr-Tez" firstAttribute="centerX" secondItem="Ml3-Ia-Ewi" secondAttribute="centerX" id="Uce-u0-e1b"/>
|
||||||
|
<constraint firstItem="8Zb-BZ-4UZ" firstAttribute="centerX" secondItem="8nS-Sr-Tez" secondAttribute="centerX" id="hEt-M5-oOZ"/>
|
||||||
|
<constraint firstItem="y7w-3d-1iH" firstAttribute="leading" secondItem="Ml3-Ia-Ewi" secondAttribute="leading" id="uZv-kV-R1v"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="zSP-IZ-bba" secondAttribute="bottom" id="vDY-zD-VN0"/>
|
||||||
|
<constraint firstItem="zSP-IZ-bba" firstAttribute="leading" secondItem="Ml3-Ia-Ewi" secondAttribute="leading" id="wHv-9r-cPP"/>
|
||||||
|
<constraint firstItem="zSP-IZ-bba" firstAttribute="top" secondItem="Ml3-Ia-Ewi" secondAttribute="top" id="xDb-7m-ju4"/>
|
||||||
|
<constraint firstItem="8nS-Sr-Tez" firstAttribute="top" secondItem="Ml3-Ia-Ewi" secondAttribute="top" id="zaV-gB-RH9"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="fOf-F6-66M" firstAttribute="top" secondItem="kSW-b0-iBl" secondAttribute="top" id="7Sz-wg-BVW"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="fOf-F6-66M" secondAttribute="trailing" id="IZf-Mm-zGq"/>
|
||||||
|
<constraint firstItem="fOf-F6-66M" firstAttribute="leading" secondItem="kSW-b0-iBl" secondAttribute="leading" id="h7c-Qw-WYp"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="fOf-F6-66M" secondAttribute="bottom" id="yaU-zc-Om0"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="dragBox" destination="Ml3-Ia-Ewi" id="QV5-l0-sSy"/>
|
||||||
|
<outlet property="dragView" destination="zSP-IZ-bba" id="Lmb-dX-AQf"/>
|
||||||
|
<outlet property="imgSel" destination="fOf-F6-66M" id="cNh-TV-TVQ"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
</tableColumns>
|
||||||
|
</tableView>
|
||||||
|
</subviews>
|
||||||
|
</clipView>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="100" id="ycy-R4-dvl"/>
|
||||||
|
</constraints>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="uRq-Iv-4qH">
|
||||||
|
<rect key="frame" x="1" y="383" width="415" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="Icy-PU-IhP">
|
||||||
|
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<tableHeaderView key="headerView" wantsLayer="YES" id="9ym-TL-xUJ">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="415" height="25"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</tableHeaderView>
|
||||||
|
</scrollView>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="QQe-WG-p4R">
|
||||||
|
<rect key="frame" x="227" y="13" width="94" height="52"/>
|
||||||
|
<buttonCell key="cell" type="push" title="提交" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="4u8-R4-iJl">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
DQ
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<color key="contentTintColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="80" id="5lo-X9-l3L"/>
|
||||||
|
<constraint firstAttribute="height" constant="40" id="t3W-Aj-MWR"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="submit:" target="ZGW-X2-eY0" id="XBS-Cc-kwA"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="4yB-HB-Sko">
|
||||||
|
<rect key="frame" x="132" y="13" width="94" height="52"/>
|
||||||
|
<buttonCell key="cell" type="push" title="取消" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="pTB-tq-Xcl">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
Gw
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<color key="contentTintColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="80" id="Vxa-b9-goA"/>
|
||||||
|
<constraint firstAttribute="height" constant="40" id="uMw-Ja-95b"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="cancel:" target="ZGW-X2-eY0" id="giL-cF-Fnw"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="QQe-WG-p4R" firstAttribute="leading" secondItem="4yB-HB-Sko" secondAttribute="trailing" constant="15" id="0by-Cs-rAn"/>
|
||||||
|
<constraint firstItem="kcS-un-E1x" firstAttribute="top" secondItem="bXG-f2-WGM" secondAttribute="top" constant="20" symbolic="YES" id="AJN-8G-5DG"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="92G-BP-MIt" secondAttribute="bottom" constant="80" id="Fg8-sp-FRb"/>
|
||||||
|
<constraint firstItem="4yB-HB-Sko" firstAttribute="centerY" secondItem="QQe-WG-p4R" secondAttribute="centerY" id="JID-Ip-h5y"/>
|
||||||
|
<constraint firstItem="92G-BP-MIt" firstAttribute="top" secondItem="rfb-ot-DNY" secondAttribute="bottom" constant="10" id="N6h-wM-NNs"/>
|
||||||
|
<constraint firstItem="QQe-WG-p4R" firstAttribute="centerX" secondItem="bXG-f2-WGM" secondAttribute="centerX" constant="50" id="TD1-kT-Y8m"/>
|
||||||
|
<constraint firstItem="rfb-ot-DNY" firstAttribute="leading" secondItem="92G-BP-MIt" secondAttribute="leading" id="fZ3-jp-zmA"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="92G-BP-MIt" secondAttribute="trailing" constant="15" id="hN7-OK-yLE"/>
|
||||||
|
<constraint firstItem="rfb-ot-DNY" firstAttribute="top" secondItem="bXG-f2-WGM" secondAttribute="top" constant="30" id="hWX-u6-nVQ"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="kcS-un-E1x" secondAttribute="trailing" constant="15" id="nPY-XB-593"/>
|
||||||
|
<constraint firstItem="92G-BP-MIt" firstAttribute="leading" secondItem="bXG-f2-WGM" secondAttribute="leading" constant="15" id="ohu-Ls-Ekd"/>
|
||||||
|
<constraint firstItem="QQe-WG-p4R" firstAttribute="top" secondItem="92G-BP-MIt" secondAttribute="bottom" constant="20" id="zVx-Ed-CpA"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<color key="borderColor" name="quaternaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</box>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="4UC-ET-dPZ" firstAttribute="leading" secondItem="XlO-n7-hEA" secondAttribute="leading" id="7fN-p4-G0s"/>
|
||||||
|
<constraint firstItem="4UC-ET-dPZ" firstAttribute="top" secondItem="XlO-n7-hEA" secondAttribute="top" id="RtL-aw-X7P"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="4UC-ET-dPZ" secondAttribute="bottom" id="pkF-XL-mVD"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="4UC-ET-dPZ" secondAttribute="trailing" id="uMi-vb-NaW"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="cancelBtm" destination="4yB-HB-Sko" id="cPP-RC-on7"/>
|
||||||
|
<outlet property="submitBtm" destination="QQe-WG-p4R" id="fhA-8S-Xdr"/>
|
||||||
|
<outlet property="tableView" destination="Pkx-YF-hF7" id="PfU-Tz-mHg"/>
|
||||||
|
<outlet property="tipLb" destination="rfb-ot-DNY" id="K0Q-YO-BoS"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="s7R-ka-Qj0" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-148" y="2436"/>
|
||||||
|
</scene>
|
||||||
|
<!--导出excel-->
|
||||||
|
<scene sceneID="v0g-L8-OB0">
|
||||||
|
<objects>
|
||||||
|
<viewController title="导出excel" storyboardIdentifier="OutputExcelVCID" id="MF2-R2-2hO" customClass="OutputExcelVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" id="4gc-AE-KbR">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="500" height="600"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<box boxType="custom" cornerRadius="4" translatesAutoresizingMaskIntoConstraints="NO" id="C5N-3t-BHc">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="500" height="600"/>
|
||||||
|
<view key="contentView" id="vgI-Wv-mhT">
|
||||||
|
<rect key="frame" x="1" y="1" width="498" height="598"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="FdO-Rj-ruG">
|
||||||
|
<rect key="frame" x="28" y="553" width="40" height="15"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="商品id" id="QYB-Zw-eHV">
|
||||||
|
<font key="font" metaFont="cellTitle"/>
|
||||||
|
<color key="textColor" name="AccentColor"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<scrollView borderType="line" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UeB-cG-eE4">
|
||||||
|
<rect key="frame" x="30" y="150" width="200" height="393"/>
|
||||||
|
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="IQM-Fb-jKv">
|
||||||
|
<rect key="frame" x="1" y="1" width="198" height="391"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="ceg-vF-fQf">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="198" height="391"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<size key="minSize" width="198" height="391"/>
|
||||||
|
<size key="maxSize" width="248" height="10000000"/>
|
||||||
|
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textView>
|
||||||
|
</subviews>
|
||||||
|
</clipView>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="200" id="FOJ-YZ-qvi"/>
|
||||||
|
</constraints>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="rFr-Dg-h0l">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="EPk-ve-LJd">
|
||||||
|
<rect key="frame" x="183" y="1" width="16" height="391"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="s72-vm-I76">
|
||||||
|
<rect key="frame" x="237" y="552" width="86" height="15"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="对应的AppleID" id="avW-ms-Mra">
|
||||||
|
<font key="font" metaFont="cellTitle"/>
|
||||||
|
<color key="textColor" name="AccentColor"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<scrollView hidden="YES" borderType="line" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YhA-gX-xjC">
|
||||||
|
<rect key="frame" x="239" y="150" width="229" height="392"/>
|
||||||
|
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="mqr-je-Omp">
|
||||||
|
<rect key="frame" x="1" y="1" width="227" height="390"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="Z0K-AM-ybS">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="227" height="390"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<size key="minSize" width="227" height="390"/>
|
||||||
|
<size key="maxSize" width="248" height="10000000"/>
|
||||||
|
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textView>
|
||||||
|
</subviews>
|
||||||
|
</clipView>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Jg2-AF-jRX">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="xVd-kl-KcI">
|
||||||
|
<rect key="frame" x="212" y="1" width="16" height="390"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="HH6-Lm-CXI">
|
||||||
|
<rect key="frame" x="380" y="23" width="94" height="52"/>
|
||||||
|
<buttonCell key="cell" type="push" title="提交" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="wti-uT-BJP">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
DQ
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<color key="contentTintColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="80" id="7Pu-0Y-cZl"/>
|
||||||
|
<constraint firstAttribute="height" constant="40" id="Tl3-IB-jTG"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="commit:" target="MF2-R2-2hO" id="zUs-9M-dat"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="voo-el-aSb">
|
||||||
|
<rect key="frame" x="195" y="131" width="36" height="15"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="0/100" id="Rvu-t4-mqS">
|
||||||
|
<font key="font" metaFont="cellTitle"/>
|
||||||
|
<color key="textColor" red="0.80000000000000004" green="0.80000000000000004" blue="0.80000000000000004" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fJh-Pa-GRs">
|
||||||
|
<rect key="frame" x="433" y="131" width="36" height="15"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="0/100" id="xGg-Co-KSE">
|
||||||
|
<font key="font" metaFont="cellTitle"/>
|
||||||
|
<color key="textColor" red="0.80000000000000004" green="0.80000000000000004" blue="0.80000000000000004" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gHe-JD-W0l">
|
||||||
|
<rect key="frame" x="285" y="23" width="94" height="52"/>
|
||||||
|
<buttonCell key="cell" type="push" title="关闭" bezelStyle="rounded" alignment="center" borderStyle="border" inset="2" id="mdF-cu-Xk3">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
Gw
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="80" id="DGd-OO-fun"/>
|
||||||
|
<constraint firstAttribute="height" constant="40" id="aeW-Ej-VAh"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="close:" target="MF2-R2-2hO" id="4dP-Wn-RS1"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<scrollView borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" horizontalScrollElasticity="none" verticalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="mqs-KO-hcm">
|
||||||
|
<rect key="frame" x="31" y="81" width="436" height="40"/>
|
||||||
|
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="Na5-JS-qH5">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="436" height="40"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textView editable="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="ccx-Sz-Ndo">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="436" height="40"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="textColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<size key="minSize" width="436" height="40"/>
|
||||||
|
<size key="maxSize" width="438" height="10000000"/>
|
||||||
|
<attributedString key="textStorage">
|
||||||
|
<fragment content="操作说明:将excel表格中商品id列的数据粘贴到输入框,点击提交后自动生成对应的AppleID。(商品不存在时为空行)">
|
||||||
|
<attributes>
|
||||||
|
<color key="NSColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<font key="NSFont" size="12" name="PingFangSC-Regular"/>
|
||||||
|
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
|
||||||
|
</attributes>
|
||||||
|
</fragment>
|
||||||
|
</attributedString>
|
||||||
|
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textView>
|
||||||
|
</subviews>
|
||||||
|
</clipView>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="40" id="3kc-P8-Mm2"/>
|
||||||
|
</constraints>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="clV-B4-N7M">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="tQq-xq-oSv">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="15" height="40"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="YhA-gX-xjC" firstAttribute="top" secondItem="s72-vm-I76" secondAttribute="bottom" constant="10" id="7Jq-tR-fOe"/>
|
||||||
|
<constraint firstItem="UeB-cG-eE4" firstAttribute="leading" secondItem="FdO-Rj-ruG" secondAttribute="leading" id="AjD-0r-0jb"/>
|
||||||
|
<constraint firstItem="YhA-gX-xjC" firstAttribute="leading" secondItem="ceg-vF-fQf" secondAttribute="trailing" constant="10" id="H8P-yH-SL9"/>
|
||||||
|
<constraint firstItem="UeB-cG-eE4" firstAttribute="top" secondItem="FdO-Rj-ruG" secondAttribute="bottom" constant="10" id="KCv-Ah-SLf"/>
|
||||||
|
<constraint firstItem="mqs-KO-hcm" firstAttribute="leading" secondItem="ceg-vF-fQf" secondAttribute="leading" id="NHT-nW-xzZ"/>
|
||||||
|
<constraint firstItem="gHe-JD-W0l" firstAttribute="centerY" secondItem="HH6-Lm-CXI" secondAttribute="centerY" id="RMB-mz-vBd"/>
|
||||||
|
<constraint firstItem="YhA-gX-xjC" firstAttribute="leading" secondItem="s72-vm-I76" secondAttribute="leading" id="Sgp-QJ-OXe"/>
|
||||||
|
<constraint firstItem="HH6-Lm-CXI" firstAttribute="top" secondItem="mqs-KO-hcm" secondAttribute="bottom" constant="11" id="Up0-1N-Qrl"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="YhA-gX-xjC" secondAttribute="trailing" constant="30" id="ViH-MG-fzQ"/>
|
||||||
|
<constraint firstItem="voo-el-aSb" firstAttribute="trailing" secondItem="ceg-vF-fQf" secondAttribute="trailing" id="WWa-Sf-kRh"/>
|
||||||
|
<constraint firstItem="fJh-Pa-GRs" firstAttribute="trailing" secondItem="xVd-kl-KcI" secondAttribute="trailing" id="Wak-yX-deV"/>
|
||||||
|
<constraint firstItem="voo-el-aSb" firstAttribute="top" secondItem="EPk-ve-LJd" secondAttribute="bottom" constant="5" id="XPn-up-SMl"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="HH6-Lm-CXI" secondAttribute="bottom" constant="30" id="aen-UO-NPm"/>
|
||||||
|
<constraint firstItem="FdO-Rj-ruG" firstAttribute="leading" secondItem="vgI-Wv-mhT" secondAttribute="leading" constant="30" id="cH1-sV-aTc"/>
|
||||||
|
<constraint firstItem="mqs-KO-hcm" firstAttribute="trailing" secondItem="Z0K-AM-ybS" secondAttribute="trailing" id="dkZ-4E-thk"/>
|
||||||
|
<constraint firstItem="FdO-Rj-ruG" firstAttribute="top" secondItem="vgI-Wv-mhT" secondAttribute="top" constant="30" id="epk-Ii-nZC"/>
|
||||||
|
<constraint firstItem="HH6-Lm-CXI" firstAttribute="leading" secondItem="gHe-JD-W0l" secondAttribute="trailing" constant="15" id="fZ1-bH-ib2"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="YhA-gX-xjC" secondAttribute="bottom" constant="150" id="q9r-vD-dwI"/>
|
||||||
|
<constraint firstItem="HH6-Lm-CXI" firstAttribute="trailing" secondItem="fJh-Pa-GRs" secondAttribute="trailing" id="wlF-3S-KCy"/>
|
||||||
|
<constraint firstItem="YhA-gX-xjC" firstAttribute="top" secondItem="ceg-vF-fQf" secondAttribute="top" id="ymP-Xb-sKJ"/>
|
||||||
|
<constraint firstItem="fJh-Pa-GRs" firstAttribute="top" secondItem="xVd-kl-KcI" secondAttribute="bottom" constant="5" id="ywL-9o-fuz"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="UeB-cG-eE4" secondAttribute="bottom" constant="150" id="zen-rj-VXk"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<color key="borderColor" name="quaternaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</box>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="C5N-3t-BHc" secondAttribute="trailing" id="4eZ-yr-AoV"/>
|
||||||
|
<constraint firstItem="C5N-3t-BHc" firstAttribute="top" secondItem="4gc-AE-KbR" secondAttribute="top" id="7U4-CP-ET9"/>
|
||||||
|
<constraint firstItem="C5N-3t-BHc" firstAttribute="leading" secondItem="4gc-AE-KbR" secondAttribute="leading" id="bPx-xX-ROK"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="C5N-3t-BHc" secondAttribute="bottom" id="cFe-Zn-SIN"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="inputCount" destination="voo-el-aSb" id="u2W-KU-hpu"/>
|
||||||
|
<outlet property="inputText" destination="ceg-vF-fQf" id="Xg7-8E-VkG"/>
|
||||||
|
<outlet property="outputCount" destination="fJh-Pa-GRs" id="E51-EA-iaR"/>
|
||||||
|
<outlet property="outputText" destination="Z0K-AM-ybS" id="uJz-Fy-lhw"/>
|
||||||
|
<outlet property="outputView" destination="YhA-gX-xjC" id="wKl-if-GTQ"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="JEX-pt-NDP" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-887" y="2554"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
<resources>
|
||||||
|
<image name="NSAddTemplate" width="18" height="17"/>
|
||||||
|
<namedColor name="AccentColor">
|
||||||
|
<color red="0.0" green="0.46000000000000002" blue="0.89000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</namedColor>
|
||||||
|
</resources>
|
||||||
|
</document>
|
||||||
@ -0,0 +1,421 @@
|
|||||||
|
//
|
||||||
|
// IAPExcelParser.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/11/10.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CoreXLSX
|
||||||
|
|
||||||
|
|
||||||
|
/// 上传的商品类型
|
||||||
|
enum IAPType: String {
|
||||||
|
case CONSUMABLE = "CONSUMABLE"
|
||||||
|
case NON_CONSUMABLE = "NON_CONSUMABLE"
|
||||||
|
case NON_RENEWING_SUBSCRIPTION = "NON_RENEWING_SUBSCRIPTION"
|
||||||
|
case AUTO_RENEWABLE = "auto-renewable"
|
||||||
|
case UNKNOW = "unknown"
|
||||||
|
|
||||||
|
static func type(name: String) -> IAPType {
|
||||||
|
switch name {
|
||||||
|
case "消耗型":
|
||||||
|
return .CONSUMABLE
|
||||||
|
case "非消耗型":
|
||||||
|
return .NON_CONSUMABLE
|
||||||
|
case "非续期订阅":
|
||||||
|
return .NON_RENEWING_SUBSCRIPTION
|
||||||
|
case "自动续期订阅":
|
||||||
|
return .AUTO_RENEWABLE
|
||||||
|
default:
|
||||||
|
return .UNKNOW
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CNValue() -> String {
|
||||||
|
switch self {
|
||||||
|
case .CONSUMABLE:
|
||||||
|
return "消耗型"
|
||||||
|
case .NON_CONSUMABLE:
|
||||||
|
return "非消耗型"
|
||||||
|
case .NON_RENEWING_SUBSCRIPTION:
|
||||||
|
return "非续期订阅"
|
||||||
|
case .AUTO_RENEWABLE:
|
||||||
|
return "自动续期订阅"
|
||||||
|
default:
|
||||||
|
return "未知"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 本地化名称和描述
|
||||||
|
struct IAPLocalization {
|
||||||
|
var name: String = ""
|
||||||
|
var description: String = ""
|
||||||
|
var locale: String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅类型的子字段
|
||||||
|
struct IAPSubscriptions {
|
||||||
|
var groupLevel: Int = 1
|
||||||
|
var subscriptionPeriod: String = "ONE_MONTH" //ONE_WEEK, ONE_MONTH, TWO_MONTHS, THREE_MONTHS, SIX_MONTHS, ONE_YEAR
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 内购统一模型
|
||||||
|
struct IAPProduct {
|
||||||
|
var name: String = ""
|
||||||
|
var groupName: String = ""
|
||||||
|
var productId: String = ""
|
||||||
|
var reviewNote: String = ""
|
||||||
|
var reviewScreenshot: String = ""
|
||||||
|
var familySharable: Bool = false
|
||||||
|
var inAppPurchaseType: IAPType = .UNKNOW //# CONSUMABLE、NON_CONSUMABLE、NON_RENEWING_SUBSCRIPTION
|
||||||
|
var localizations: [IAPLocalization] = []
|
||||||
|
// 订阅类型的特有
|
||||||
|
var subscriptions: IAPSubscriptions?
|
||||||
|
// 价格计划表
|
||||||
|
var priceSchedules: IAPPriceSchedules?
|
||||||
|
// 销售的国家或地区
|
||||||
|
var territories: IAPTerritories = IAPTerritories()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 价格计划表
|
||||||
|
struct IAPPriceSchedules {
|
||||||
|
var productId: String = ""
|
||||||
|
var baseTerritory: String = ""
|
||||||
|
var baseCustomerPrice: String = ""
|
||||||
|
var manualPrices: [IAPPricePoint] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 价格点
|
||||||
|
struct IAPPricePoint {
|
||||||
|
var territory: String
|
||||||
|
var customerPrice: String
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 销售的国家或地区
|
||||||
|
struct IAPTerritories {
|
||||||
|
var productId: String = ""
|
||||||
|
/// 所有国家或地区销售(包括将来新国家或地区)
|
||||||
|
var availableInAllTerritories: Bool = true
|
||||||
|
/// 将来新国家/地区自动提供销售
|
||||||
|
var availableInNewTerritories: Bool = true
|
||||||
|
var territories: [IAPTerritory]?
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IAPTerritory {
|
||||||
|
var id: String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct IAPExcelParser {
|
||||||
|
|
||||||
|
static func parser(_ filePath: URL) -> [IAPProduct] {
|
||||||
|
guard let file = XLSXFile(filepath: filePath.path) else {
|
||||||
|
fatalError("XLSX file at \(filePath.path) is corrupted or does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先读取价格计划表
|
||||||
|
let priceSchedules = parserPricePoints(file)
|
||||||
|
// 销售的国家或地区
|
||||||
|
let territories = parserTerritories(file)
|
||||||
|
|
||||||
|
var result: [IAPProduct] = []
|
||||||
|
for wbk in try! file.parseWorkbooks() {
|
||||||
|
for (name, path) in try! file.parseWorksheetPathsAndNames(workbook: wbk) {
|
||||||
|
if let worksheetName = name {
|
||||||
|
print("This worksheet has a name: \(worksheetName)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != "AppleParty" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let worksheet = try? file.parseWorksheet(at: path),
|
||||||
|
let sharedStrings = try! file.parseSharedStrings() else {
|
||||||
|
print("This worksheet/sharedStrings is null")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
var index = 0
|
||||||
|
var columnTitles = [String]()
|
||||||
|
var columnIndexs = [String]()
|
||||||
|
for row in worksheet.data?.rows ?? [] {
|
||||||
|
var columnValues = [String: String]()
|
||||||
|
index += 1
|
||||||
|
for cell in row.cells {
|
||||||
|
let key = cell.reference.column.value
|
||||||
|
var columnStrings = cell.stringValue(sharedStrings) ?? ""
|
||||||
|
// 富文本读取
|
||||||
|
var richStr = ""
|
||||||
|
let richColumnCString = cell.richStringValue(sharedStrings)
|
||||||
|
for richChar in richColumnCString {
|
||||||
|
richStr += string(from: richChar.text)
|
||||||
|
}
|
||||||
|
columnStrings += richStr
|
||||||
|
// 第一行作为标识行,用于多语言标识,默认信任此字段
|
||||||
|
if index == 1 {
|
||||||
|
columnTitles.append(columnStrings)
|
||||||
|
columnIndexs.append(key)
|
||||||
|
} else {
|
||||||
|
columnValues[key] = columnStrings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if index == 1 {
|
||||||
|
print(columnTitles)
|
||||||
|
print(columnIndexs)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Product ID 参考名字 内购买类型 审核截图(可选) 审核备注(可选) zh-Hans zh-Hans ja ja ko ko
|
||||||
|
var iap = IAPProduct()
|
||||||
|
iap.groupName = columnValues["A"] ?? ""
|
||||||
|
iap.productId = columnValues["B"] ?? ""
|
||||||
|
iap.name = columnValues["C"] ?? ""
|
||||||
|
let productType = columnValues["D"] ?? ""
|
||||||
|
iap.inAppPurchaseType = IAPType.type(name: productType)
|
||||||
|
|
||||||
|
// 订阅类型默认的字段
|
||||||
|
if iap.inAppPurchaseType == .AUTO_RENEWABLE {
|
||||||
|
let period = columnValues["E"] ?? ""
|
||||||
|
iap.subscriptions = IAPSubscriptions()
|
||||||
|
iap.subscriptions?.subscriptionPeriod = period
|
||||||
|
}
|
||||||
|
|
||||||
|
iap.reviewScreenshot = columnValues["F"] ?? ""
|
||||||
|
iap.reviewNote = columnValues["G"] ?? ""
|
||||||
|
|
||||||
|
// 非法的行
|
||||||
|
if iap.productId.isEmpty, iap.name.isEmpty {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 【产品 ID】 可以由字母、数字、下划线(_)和句点(.)构成。 2 ~ 100 个字符)
|
||||||
|
if iap.productId.count < 2 || iap.productId.count > 100 {
|
||||||
|
NSAlert.show("Product ID 长度为:2~100 字符!")
|
||||||
|
}
|
||||||
|
// 参考名字
|
||||||
|
if iap.name.count < 2 || iap.name.count > 64 {
|
||||||
|
NSAlert.show("\(iap.productId):“参考名字”长度超过 2~64 字符!")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 价格计划表
|
||||||
|
let schedules = priceSchedules.filter({ $0.productId == iap.productId })
|
||||||
|
if let schedule = schedules.first {
|
||||||
|
iap.priceSchedules = schedule
|
||||||
|
}
|
||||||
|
|
||||||
|
//销售国家和地区
|
||||||
|
let territorys = territories.filter({ $0.productId == iap.productId })
|
||||||
|
if let territory = territorys.first {
|
||||||
|
iap.territories = territory
|
||||||
|
}
|
||||||
|
|
||||||
|
// 商品本地化名称和描述
|
||||||
|
var localizations: [IAPLocalization] = []
|
||||||
|
// 本地化的标识,从下标7开始,奇数遍历,成对出现的
|
||||||
|
let columeMax = columnIndexs.count
|
||||||
|
let columeEndIndex = columnIndexs.count - 1
|
||||||
|
for idx in stride(from: 7, to: columeEndIndex, by: 2){
|
||||||
|
if idx + 1 <= columeMax {
|
||||||
|
let locale = columnTitles[idx]
|
||||||
|
let key1 = columnIndexs[idx]
|
||||||
|
let key2 = columnIndexs[idx+1]
|
||||||
|
let name = columnValues[key1] ?? ""
|
||||||
|
let description = columnValues[key2] ?? ""
|
||||||
|
if !name.isEmpty && !description.isEmpty {
|
||||||
|
var localization = IAPLocalization()
|
||||||
|
localization.locale = locale
|
||||||
|
localization.name = name
|
||||||
|
localization.description = description
|
||||||
|
localizations.append(localization)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iap.localizations = localizations
|
||||||
|
result.append(iap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 公共方法
|
||||||
|
fileprivate static func handleRowContents(_ row: Row, _ sharedStrings: SharedStrings, _ index: Int, _ columnIndexs: inout [String], _ columnValues: inout [String : String]) {
|
||||||
|
for cell in row.cells {
|
||||||
|
let key = cell.reference.column.value
|
||||||
|
var columnStrings = cell.stringValue(sharedStrings) ?? ""
|
||||||
|
// 富文本读取
|
||||||
|
var richStr = ""
|
||||||
|
let richColumnCString = cell.richStringValue(sharedStrings)
|
||||||
|
for richChar in richColumnCString {
|
||||||
|
richStr += string(from: richChar.text)
|
||||||
|
}
|
||||||
|
columnStrings += richStr
|
||||||
|
// 第一行作为标识行,用于多语言标识,默认信任此字段
|
||||||
|
if index == 1 {
|
||||||
|
columnIndexs.append(key)
|
||||||
|
} else {
|
||||||
|
columnValues[key] = columnStrings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 读取价格计划表
|
||||||
|
static func parserPricePoints(_ file: XLSXFile) -> [IAPPriceSchedules] {
|
||||||
|
|
||||||
|
var result: [IAPPriceSchedules] = []
|
||||||
|
for wbk in try! file.parseWorkbooks() {
|
||||||
|
for (name, path) in try! file.parseWorksheetPathsAndNames(workbook: wbk) {
|
||||||
|
if let worksheetName = name {
|
||||||
|
print("This worksheet has a name: \(worksheetName)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != "PricePoints" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let worksheet = try? file.parseWorksheet(at: path),
|
||||||
|
let sharedStrings = try! file.parseSharedStrings() else {
|
||||||
|
print("This worksheet/sharedStrings is null")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
var index = 0
|
||||||
|
var columnIndexs = [String]()
|
||||||
|
for row in worksheet.data?.rows ?? [] {
|
||||||
|
var columnValues = [String: String]()
|
||||||
|
index += 1
|
||||||
|
handleRowContents(row, sharedStrings, index, &columnIndexs, &columnValues)
|
||||||
|
|
||||||
|
// 第一行是标题行,忽视
|
||||||
|
if index == 1 {
|
||||||
|
print(columnIndexs)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Product ID 基准国家(代码) 基准国价格 自定价格国家1 自定价格1 自定价格国家2 自定价格2
|
||||||
|
let productId = columnValues["A"] ?? ""
|
||||||
|
let baseTerritory = columnValues["B"] ?? ""
|
||||||
|
let baseCustomerPrice = (columnValues["C"] ?? "").twoDecimalPrice()
|
||||||
|
|
||||||
|
// 非法的行
|
||||||
|
if productId.isEmpty, baseTerritory.isEmpty, baseCustomerPrice.isEmpty {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 【产品 ID】 可以由字母、数字、下划线(_)和句点(.)构成。 2 ~ 100 个字符)
|
||||||
|
if productId.count < 2 || productId.count > 100 {
|
||||||
|
NSAlert.show("PricePoints Product ID 长度为:2~100 字符!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 价格计划表
|
||||||
|
var schedule = IAPPriceSchedules(productId: productId, baseTerritory: baseTerritory, baseCustomerPrice: baseCustomerPrice)
|
||||||
|
|
||||||
|
// 自定价格的国家和价格
|
||||||
|
var manualPrices: [IAPPricePoint] = []
|
||||||
|
// 自定价格,从下标3开始,奇数遍历,成对出现的
|
||||||
|
let columeMax = columnValues.count
|
||||||
|
let columeEndIndex = columnValues.count - 1
|
||||||
|
for idx in stride(from: 3, to: columeEndIndex, by: 2) {
|
||||||
|
if idx + 1 <= columeMax, columnIndexs.count > idx+1 {
|
||||||
|
let key1 = columnIndexs[idx]
|
||||||
|
let key2 = columnIndexs[idx+1]
|
||||||
|
let name = columnValues[key1] ?? ""
|
||||||
|
let price = (columnValues[key2] ?? "").twoDecimalPrice()
|
||||||
|
if !name.isEmpty && !price.isEmpty {
|
||||||
|
let pricePoint = IAPPricePoint(territory: name, customerPrice: price)
|
||||||
|
manualPrices.append(pricePoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
schedule.manualPrices = manualPrices
|
||||||
|
result.append(schedule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// 读取销售的国家或地区
|
||||||
|
static func parserTerritories(_ file: XLSXFile) -> [IAPTerritories] {
|
||||||
|
|
||||||
|
var result: [IAPTerritories] = []
|
||||||
|
for wbk in try! file.parseWorkbooks() {
|
||||||
|
for (name, path) in try! file.parseWorksheetPathsAndNames(workbook: wbk) {
|
||||||
|
if let worksheetName = name {
|
||||||
|
print("This worksheet has a name: \(worksheetName)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != "Territories" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let worksheet = try? file.parseWorksheet(at: path),
|
||||||
|
let sharedStrings = try! file.parseSharedStrings() else {
|
||||||
|
print("This worksheet/sharedStrings is null")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
var index = 0
|
||||||
|
var columnIndexs = [String]()
|
||||||
|
for row in worksheet.data?.rows ?? [] {
|
||||||
|
var columnValues = [String: String]()
|
||||||
|
index += 1
|
||||||
|
handleRowContents(row, sharedStrings, index, &columnIndexs, &columnValues)
|
||||||
|
|
||||||
|
// 第一行是标题行,忽视
|
||||||
|
if index == 1 {
|
||||||
|
print(columnIndexs)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Product ID 在所有国家/地区销售(1是,0否) 将来新国家/地区自动提供(1是,0否) 销售1 销售2 ...
|
||||||
|
let productId = columnValues["A"] ?? ""
|
||||||
|
let availableInAllTerritories = columnValues["B"] ?? ""
|
||||||
|
let availableInNewTerritories = columnValues["C"] ?? ""
|
||||||
|
let isInAll = availableInAllTerritories == "1" ? true : false
|
||||||
|
let isInNew = availableInNewTerritories == "1" ? true : false
|
||||||
|
|
||||||
|
// 非法的行
|
||||||
|
if productId.isEmpty {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 【产品 ID】 可以由字母、数字、下划线(_)和句点(.)构成。 2 ~ 100 个字符)
|
||||||
|
if productId.count < 2 || productId.count > 100 {
|
||||||
|
NSAlert.show("Territories Product ID 长度为:2~100 字符!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 销售的国家或地区
|
||||||
|
var territory = IAPTerritories(productId: productId, availableInAllTerritories: isInAll, availableInNewTerritories: isInNew)
|
||||||
|
|
||||||
|
// 如果在所有国家或地区销售,则不在读取自定销售国家或地区
|
||||||
|
if isInAll {
|
||||||
|
territory.availableInNewTerritories = true
|
||||||
|
result.append(territory)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定销售的国家或地区
|
||||||
|
var territories: [IAPTerritory] = []
|
||||||
|
// 从下标3开始
|
||||||
|
let columeMax = columnValues.count
|
||||||
|
for idx in stride(from: 3, to: columeMax, by: 1) {
|
||||||
|
let key = columnIndexs[idx]
|
||||||
|
let name = columnValues[key] ?? ""
|
||||||
|
if !name.isEmpty {
|
||||||
|
let pricePoint = IAPTerritory(id: name)
|
||||||
|
territories.append(pricePoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
territory.territories = territories
|
||||||
|
result.append(territory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,341 @@
|
|||||||
|
//
|
||||||
|
// ITCResponseModel.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by 易承 on 2021/5/13.
|
||||||
|
//
|
||||||
|
// HTTP请求返回的数据解析类
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
|
||||||
|
/* 消耗型项目consumable
|
||||||
|
非消耗型non-consumable
|
||||||
|
自动续期订阅auto-renewable
|
||||||
|
非续期订阅subscription
|
||||||
|
*/
|
||||||
|
// 上传的商品类型
|
||||||
|
enum InAppPurchaseType: String {
|
||||||
|
case CONSUMABLE = "consumable"
|
||||||
|
case NON_CONSUMABLE = "non-consumable"
|
||||||
|
case AUTO_RENEWABLE = "auto-renewable"
|
||||||
|
case FREE_SUBSCRIPTION = "free-subscription"
|
||||||
|
case SUBSCRIPTION = "subscription"
|
||||||
|
case UNKNOW = "unknown"
|
||||||
|
|
||||||
|
func CNValue() -> String {
|
||||||
|
switch self {
|
||||||
|
case .CONSUMABLE:
|
||||||
|
return "消耗型"
|
||||||
|
case .NON_CONSUMABLE:
|
||||||
|
return "非消耗型"
|
||||||
|
case .SUBSCRIPTION:
|
||||||
|
return "非续订型"
|
||||||
|
case .AUTO_RENEWABLE:
|
||||||
|
return "自动续订型"
|
||||||
|
case .FREE_SUBSCRIPTION:
|
||||||
|
return "免费订阅型"
|
||||||
|
default:
|
||||||
|
return "未知"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
"ITC.addons.type.consumable": "消耗型项目",
|
||||||
|
"ITC.addons.type.freeSubscription": "免费订阅",
|
||||||
|
"ITC.addons.type.nonConsumable": "非消耗型项目",
|
||||||
|
"ITC.addons.type.recurring": "自动续期订阅",
|
||||||
|
"ITC.addons.type.subscription": "非续期订阅",
|
||||||
|
*/
|
||||||
|
// 苹果后台商品类型
|
||||||
|
enum ITCAddOnType: String {
|
||||||
|
case consumable = "ITC.addons.type.consumable"
|
||||||
|
case subscription = "ITC.addons.type.subscription"
|
||||||
|
case free_subscription = "ITC.addons.type.freeSubscription"
|
||||||
|
case non_consumable = "ITC.addons.type.nonConsumable"
|
||||||
|
case auto_renewable = "ITC.addons.type.recurring"
|
||||||
|
case unknown
|
||||||
|
|
||||||
|
func CNValue() -> String {
|
||||||
|
switch self {
|
||||||
|
case .consumable:
|
||||||
|
return "消耗型项目"
|
||||||
|
case .non_consumable:
|
||||||
|
return "非消耗型项目"
|
||||||
|
case .subscription:
|
||||||
|
return "非续期订阅"
|
||||||
|
case .auto_renewable:
|
||||||
|
return "自动续期订阅"
|
||||||
|
case .free_subscription:
|
||||||
|
return "免费订阅"
|
||||||
|
default:
|
||||||
|
return "未知"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
"approved": "已批准",
|
||||||
|
"created": "已创建",
|
||||||
|
"deleted": "已删除",
|
||||||
|
"deletePending": "正在删除",
|
||||||
|
"developerActionNeeded": "需要开发人员操作",
|
||||||
|
"developerRemovedFromSale": "被开发人员下架",
|
||||||
|
"developerSignedOff": "开发人员签名",
|
||||||
|
"inReview": "正在审核",
|
||||||
|
"missingMetadata": "元数据丢失",
|
||||||
|
"pendingBinaryApproval": "正在审核",
|
||||||
|
"pendingDeveloperRelease": "等待开发人员发布",
|
||||||
|
"pendingScreenshot": "正在等待屏幕快照",
|
||||||
|
"prepareForSubmission": "准备提交",
|
||||||
|
"processingContentUpload": "正在处理",
|
||||||
|
"readyForSale": "已批准",
|
||||||
|
"readyToSubmit": "准备提交",
|
||||||
|
"rejected": "被拒绝",
|
||||||
|
"removedFromSale": "被下架",
|
||||||
|
"replaced": "被替换",
|
||||||
|
"waitingForContentUpload": "正在等待上传",
|
||||||
|
"waitingForReview": "正在等待审核",
|
||||||
|
*/
|
||||||
|
// iap状态
|
||||||
|
enum InAppPurchaseState: String {
|
||||||
|
case readyForSale
|
||||||
|
case missingMetadata
|
||||||
|
case developerActionNeeded
|
||||||
|
case developerRemovedFromSale
|
||||||
|
case readyToSubmit
|
||||||
|
case prepareForSubmission
|
||||||
|
case waitingForReview
|
||||||
|
case waitingForContentUpload
|
||||||
|
case inReview
|
||||||
|
case pendingBinaryApproval
|
||||||
|
case pendingDeveloperRelease
|
||||||
|
case rejected
|
||||||
|
case removedFromSale
|
||||||
|
case approved
|
||||||
|
case created
|
||||||
|
case deleted
|
||||||
|
case deletePending
|
||||||
|
case developerSignedOff
|
||||||
|
case pendingScreenshot
|
||||||
|
case processingContentUpload
|
||||||
|
case replaced
|
||||||
|
case unknown
|
||||||
|
|
||||||
|
var statusValue: (String, NSColor) {
|
||||||
|
switch self {
|
||||||
|
case .readyForSale:
|
||||||
|
return ("可供销售", NSColor(calibratedRed: 0.23, green: 0.64, blue: 0.40, alpha: 1.00))
|
||||||
|
case .missingMetadata:
|
||||||
|
return ("元数据丢失", NSColor(calibratedRed: 0.97, green: 0.50, blue: 0.19, alpha: 1.00))
|
||||||
|
case .developerActionNeeded:
|
||||||
|
return ("需要开发人员操作", NSColor(calibratedRed: 0.95, green: 0.00, blue: 0.13, alpha: 1.00))
|
||||||
|
case .developerRemovedFromSale:
|
||||||
|
return ("被开发人员下架", NSColor(calibratedRed: 0.95, green: 0.00, blue: 0.13, alpha: 1.00))
|
||||||
|
case .readyToSubmit, .prepareForSubmission:
|
||||||
|
return ("准备提交", NSColor(calibratedRed: 0.23, green: 0.64, blue: 0.40, alpha: 1.00))
|
||||||
|
case .waitingForReview:
|
||||||
|
return ("正在等待审核", NSColor(calibratedRed: 0.900, green:0.658, blue:0.625, alpha:1.000))
|
||||||
|
case .waitingForContentUpload:
|
||||||
|
return ("正在等待上传", NSColor(calibratedRed: 0.23, green: 0.64, blue: 0.40, alpha: 1.00))
|
||||||
|
case .inReview, .pendingBinaryApproval:
|
||||||
|
return ("正在审核", NSColor(calibratedRed: 0.999, green:0.775, blue:0.031, alpha:1.000))
|
||||||
|
case .pendingDeveloperRelease:
|
||||||
|
return ("等待开发人员发布", NSColor(calibratedRed: 0.23, green: 0.64, blue: 0.40, alpha: 1.00))
|
||||||
|
case .rejected:
|
||||||
|
return ("被拒绝", NSColor(calibratedRed:0.548, green:0.145, blue:0.781, alpha:1.000))
|
||||||
|
case .removedFromSale:
|
||||||
|
return ("被下架", NSColor(calibratedRed:0.906, green:0.148, blue:0.155, alpha:1.000))
|
||||||
|
default:
|
||||||
|
return (self.rawValue, NSColor.secondaryLabelColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 内购列表-新
|
||||||
|
struct Product {
|
||||||
|
var type: String = ""
|
||||||
|
var id: String = ""
|
||||||
|
var referenceName: String = ""
|
||||||
|
var productId: String = ""
|
||||||
|
var inAppPurchaseType: InAppPurchaseType = .UNKNOW
|
||||||
|
var state: InAppPurchaseState = .unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ProductList {
|
||||||
|
var products: [Product]
|
||||||
|
|
||||||
|
init(body: [String: Any]) {
|
||||||
|
products = [Product]()
|
||||||
|
let data = dictionaryArray(body["data"])
|
||||||
|
for temp in data {
|
||||||
|
var product = Product()
|
||||||
|
product.type = string(from: temp["type"])
|
||||||
|
product.id = string(from: temp["id"])
|
||||||
|
let attributes = dictionary(temp["attributes"])
|
||||||
|
product.referenceName = string(from: attributes["referenceName"])
|
||||||
|
product.productId = string(from: attributes["productId"])
|
||||||
|
product.inAppPurchaseType = InAppPurchaseType(rawValue: string(from: attributes["inAppPurchaseType"])) ?? .UNKNOW
|
||||||
|
product.state = InAppPurchaseState(rawValue: string(from: attributes["state"])) ?? .unknown
|
||||||
|
products.append(product)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 内购列表-旧
|
||||||
|
struct IAPList {
|
||||||
|
struct IAP {
|
||||||
|
struct Version {
|
||||||
|
var screenshotUrl: String = ""
|
||||||
|
var itunesConnectStatus: String = ""
|
||||||
|
var issuesCount: Int = 0
|
||||||
|
var canSubmit: Bool = false
|
||||||
|
|
||||||
|
init(dict: [String: Any]) {
|
||||||
|
screenshotUrl = string(from: dict["screenshotUrl"])
|
||||||
|
itunesConnectStatus = string(from: dict["itunesConnectStatus"])
|
||||||
|
issuesCount = int(from: dict["issuesCount"]) ?? 0
|
||||||
|
canSubmit = bool(from: dict["canSubmit"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var familyReferenceName: String = ""
|
||||||
|
var durationDays: Int = 0
|
||||||
|
var numberOfCodes: Int = 0
|
||||||
|
var maximumNumberOfCodes: Int = 0
|
||||||
|
var appMaximumNumberOfCodes: Int = 0
|
||||||
|
var isEditable: Bool = false
|
||||||
|
var isRequired: Bool = false
|
||||||
|
var canDeleteAddOn: Bool = false
|
||||||
|
var errorKeys: String = ""
|
||||||
|
var itcsubmitNextVersion: Bool = false
|
||||||
|
var isEmptyValue: Bool = false
|
||||||
|
var adamId: String = "" // appleid
|
||||||
|
var referenceName: String = "" // 商品名称
|
||||||
|
var vendorId: String = "" // 商品id
|
||||||
|
var addOnType: ITCAddOnType = .unknown // 商品类型
|
||||||
|
var versions: [Version] = []
|
||||||
|
var purpleSoftwareAdamIds: [String] = []
|
||||||
|
var lastModifiedDate: String = ""
|
||||||
|
var isNewsSubscription: Bool = false
|
||||||
|
var iTunesConnectStatus: InAppPurchaseState = .unknown
|
||||||
|
|
||||||
|
// detail
|
||||||
|
var status: String = "" // 状态
|
||||||
|
var familySharable: Bool = false // 家庭共享
|
||||||
|
var availableInAllTerritories: Bool = true // 可供销售
|
||||||
|
var reviewNote: String = "" // 截图
|
||||||
|
var reviewScreenshot: String = "" // 截图
|
||||||
|
var localizations: [IAPLocalization] = [] // 本地化描述
|
||||||
|
|
||||||
|
mutating func updateDetail(body: [String: Any]) {
|
||||||
|
let data = dictionary(body["data"])
|
||||||
|
status = string(from: dictionary(dictionary(data)["attributes"])["state"])
|
||||||
|
reviewNote = string(from: dictionary(dictionary(data)["attributes"])["reviewNote"])
|
||||||
|
familySharable = bool(from: dictionary(dictionary(data)["attributes"])["familySharable"])
|
||||||
|
|
||||||
|
let included = dictionaryArray(body["included"])
|
||||||
|
for include in included {
|
||||||
|
guard let type = include["type"] as? String else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if type == "inAppPurchaseLocalizations" {
|
||||||
|
let attr = dictionary(include["attributes"])
|
||||||
|
let name = string(from: attr["name"])
|
||||||
|
let locale = string(from: attr["locale"])
|
||||||
|
let description = string(from: attr["description"])
|
||||||
|
localizations.append(IAPLocalization(name: name, description: description, locale: locale))
|
||||||
|
}
|
||||||
|
|
||||||
|
if type == "inAppPurchaseAppStoreReviewScreenshots" {
|
||||||
|
let attr = dictionary(include["attributes"])
|
||||||
|
let asset = dictionary(attr["imageAsset"])
|
||||||
|
let width = string(from: asset["width"])
|
||||||
|
let height = string(from: asset["height"])
|
||||||
|
let templateUrl = string(from: asset["templateUrl"])
|
||||||
|
// {w}x{h}bb.{f}
|
||||||
|
let reviewScreenshot = templateUrl
|
||||||
|
.replacingOccurrences(of: "{w}", with: width)
|
||||||
|
.replacingOccurrences(of: "{h}", with: height)
|
||||||
|
.replacingOccurrences(of: "{f}", with: "png")
|
||||||
|
self.reviewScreenshot = reviewScreenshot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// price
|
||||||
|
var priceTier: String = "" // 价格等级
|
||||||
|
|
||||||
|
mutating func updatePrices(body: [String: Any]) {
|
||||||
|
let included = dictionaryArray(body["included"])
|
||||||
|
for include in included {
|
||||||
|
let attr = dictionary(include["attributes"])
|
||||||
|
self.priceTier = string(from: attr["priceTier"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 本地属性
|
||||||
|
var isSelected = false // 是否标记为批量选中
|
||||||
|
var curid = 0 // 数组中的序号
|
||||||
|
|
||||||
|
var app: App
|
||||||
|
init(app: App) {
|
||||||
|
self.app = app
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var iapList: [IAP]
|
||||||
|
var app: App
|
||||||
|
|
||||||
|
init(body: [String: Any], app: App) {
|
||||||
|
iapList = [IAP]()
|
||||||
|
let data = dictionaryArray(body["data"])
|
||||||
|
for i in 0..<data.count {
|
||||||
|
let temp = data[i]
|
||||||
|
var iap = IAP(app: app)
|
||||||
|
iap.curid = i+1
|
||||||
|
iap.familyReferenceName = string(from: temp["familyReferenceName"])
|
||||||
|
iap.durationDays = int(from: temp["durationDays"]) ?? 0
|
||||||
|
iap.numberOfCodes = int(from: temp["numberOfCodes"]) ?? 0
|
||||||
|
iap.maximumNumberOfCodes = int(from: temp["maximumNumberOfCodes"]) ?? 0
|
||||||
|
iap.appMaximumNumberOfCodes = int(from: temp["appMaximumNumberOfCodes"]) ?? 0
|
||||||
|
iap.isEditable = bool(from: temp["isEditable"])
|
||||||
|
iap.isRequired = bool(from: temp["isRequired"])
|
||||||
|
iap.canDeleteAddOn = bool(from: temp["canDeleteAddOn"])
|
||||||
|
iap.errorKeys = string(from: temp["errorKeys"])
|
||||||
|
iap.itcsubmitNextVersion = bool(from: temp["itcsubmitNextVersion"])
|
||||||
|
iap.isEmptyValue = bool(from: temp["isEmptyValue"])
|
||||||
|
iap.adamId = string(from: temp["adamId"])
|
||||||
|
iap.referenceName = string(from: temp["referenceName"])
|
||||||
|
iap.vendorId = string(from: temp["vendorId"])
|
||||||
|
iap.addOnType = ITCAddOnType(rawValue: string(from: temp["addOnType"])) ?? .unknown
|
||||||
|
|
||||||
|
let verArr = dictionaryArray(temp["versions"])
|
||||||
|
for ver in verArr {
|
||||||
|
let newVer = IAP.Version(dict: ver)
|
||||||
|
iap.versions.append(newVer)
|
||||||
|
}
|
||||||
|
iap.purpleSoftwareAdamIds = stringArray(temp["purpleSoftwareAdamIds"])
|
||||||
|
|
||||||
|
iap.lastModifiedDate = string(from: temp["lastModifiedDate"])
|
||||||
|
iap.isNewsSubscription = bool(from: temp["isNewsSubscription"])
|
||||||
|
iap.iTunesConnectStatus = InAppPurchaseState(rawValue: string(from: temp["iTunesConnectStatus"])) ?? .unknown
|
||||||
|
iapList.append(iap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 简单排序
|
||||||
|
iapList = iapList.sorted(by: { (iap1, iap2) -> Bool in
|
||||||
|
let nonDigits = CharacterSet.decimalDigits.inverted
|
||||||
|
let numStr1 = iap1.vendorId.trimmingCharacters(in: nonDigits)
|
||||||
|
let numStr2 = iap2.vendorId.trimmingCharacters(in: nonDigits)
|
||||||
|
return int(from: numStr1) ?? 0 < int(from: numStr2) ?? 0
|
||||||
|
})
|
||||||
|
self.app = app
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,364 @@
|
|||||||
|
//
|
||||||
|
// XMLModel.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by 易承 on 2021/5/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SWXMLHash
|
||||||
|
|
||||||
|
|
||||||
|
struct In_App_Purchase {
|
||||||
|
|
||||||
|
var product_id = "" // 商品id
|
||||||
|
var reference_name = "" // 商品名称
|
||||||
|
var type: InAppPurchaseType = .UNKNOW // 商品类型
|
||||||
|
var wholesale_price_tier = 0 // 价格等级
|
||||||
|
var title = "" // 本地化title
|
||||||
|
var description = "" // 本地化描述
|
||||||
|
var file_name = "" // 截图文件名
|
||||||
|
var size = "" // 截图大小
|
||||||
|
var checksum = "" // 截图md5
|
||||||
|
var review_notes = "" // 商品描述
|
||||||
|
var lang = "zh-Hans" // 本地化语言
|
||||||
|
|
||||||
|
var inputPrice = "" // 表格的价格
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Screen_Shot {
|
||||||
|
var file_name = "" // 文件名
|
||||||
|
var size = "" // 大小
|
||||||
|
var checksum = "" // md5
|
||||||
|
var position = "0" // 位置
|
||||||
|
var preview_time = "00:00:05:00" //设置默认视频时间节点
|
||||||
|
}
|
||||||
|
|
||||||
|
struct XMLModel {
|
||||||
|
var provider = UserCenter.shared.developerTeamId // 公司名称
|
||||||
|
var team_id = UserCenter.shared.developerTeamId // 开发者teamid
|
||||||
|
var vendor_id = "" // sku 套装id
|
||||||
|
|
||||||
|
// 内购品项
|
||||||
|
var iaps: [In_App_Purchase] = []
|
||||||
|
|
||||||
|
// 商店截图
|
||||||
|
var app_locale = "zh-Hans" // 本地化语言
|
||||||
|
var app_platform = "ios" // ios 或 osx
|
||||||
|
var app_version = "" //当前版本
|
||||||
|
var app_title = "" //商店应用名
|
||||||
|
var shots: [String: [Screen_Shot]] = [:]
|
||||||
|
var videos: [String: [Screen_Shot]] = [:]
|
||||||
|
|
||||||
|
// 上传ipa文件
|
||||||
|
var apple_id = "" // apple id
|
||||||
|
var archive_type = "bundle" //上传文件类型
|
||||||
|
var ipa_name = "ipa.ipa" //默认包名
|
||||||
|
var ipa_size = ""
|
||||||
|
var ipa_md5 = ""
|
||||||
|
|
||||||
|
// 需要复制的文件 [fileName: fileURL]
|
||||||
|
var filePaths: [String: String] = [:]
|
||||||
|
|
||||||
|
func createIAP(directoryPath: String) {
|
||||||
|
// 根标签
|
||||||
|
let root = GDataXMLNode.element(withName: "package")
|
||||||
|
// package属性
|
||||||
|
let version = GDataXMLNode.attribute(withName: "version", stringValue: "software5.11") as? GDataXMLNode
|
||||||
|
let xmlns = GDataXMLNode.attribute(withName: "xmlns", stringValue: "http://apple.com/itunes/importer") as? GDataXMLNode
|
||||||
|
root?.addAttribute(version)
|
||||||
|
root?.addAttribute(xmlns)
|
||||||
|
// provider\team_id
|
||||||
|
let pro = GDataXMLNode.element(withName: "provider", stringValue: provider)
|
||||||
|
let tid = GDataXMLNode.element(withName: "team_id", stringValue: team_id)
|
||||||
|
root?.addChild(pro)
|
||||||
|
root?.addChild(tid)
|
||||||
|
// software
|
||||||
|
// vendor_id
|
||||||
|
let software = GDataXMLNode.element(withName: "software")
|
||||||
|
let vid = GDataXMLNode.element(withName: "vendor_id", stringValue: vendor_id)
|
||||||
|
software?.addChild(vid)
|
||||||
|
// software_metadata/in_app_purchases
|
||||||
|
let software_metadata = GDataXMLNode.element(withName: "software_metadata")
|
||||||
|
let in_app_purchases = GDataXMLNode.element(withName: "in_app_purchases")
|
||||||
|
// in_app_purchase array
|
||||||
|
for iap in iaps {
|
||||||
|
let in_app_purchase = GDataXMLNode.element(withName: "in_app_purchase")
|
||||||
|
// product_id/reference_name/type
|
||||||
|
let product_id = GDataXMLNode.element(withName: "product_id", stringValue: iap.product_id)
|
||||||
|
let reference_name = GDataXMLNode.element(withName: "reference_name", stringValue: iap.reference_name)
|
||||||
|
let type = GDataXMLNode.element(withName: "type", stringValue: iap.type.rawValue)
|
||||||
|
// products
|
||||||
|
let products = GDataXMLNode.element(withName: "products")
|
||||||
|
let product = GDataXMLNode.element(withName: "product")
|
||||||
|
let cleared_for_sale = GDataXMLNode.element(withName: "cleared_for_sale", stringValue: "true")
|
||||||
|
let wholesale_price_tier = GDataXMLNode.element(withName: "wholesale_price_tier", stringValue: String(iap.wholesale_price_tier))
|
||||||
|
product?.addChild(cleared_for_sale)
|
||||||
|
product?.addChild(wholesale_price_tier)
|
||||||
|
products?.addChild(product)
|
||||||
|
// locales
|
||||||
|
let locales = GDataXMLNode.element(withName: "locales")
|
||||||
|
let locale = GDataXMLNode.element(withName: "locale")
|
||||||
|
let name = GDataXMLNode.attribute(withName: "name", stringValue: iap.lang) as? GDataXMLNode
|
||||||
|
locale?.addAttribute(name)
|
||||||
|
let title = GDataXMLNode.element(withName: "title", stringValue: iap.title)
|
||||||
|
let description = GDataXMLNode.element(withName: "description", stringValue: iap.description)
|
||||||
|
locale?.addChild(title)
|
||||||
|
locale?.addChild(description)
|
||||||
|
locales?.addChild(locale)
|
||||||
|
// review_screenshot
|
||||||
|
let review_screenshot = GDataXMLNode.element(withName: "review_screenshot")
|
||||||
|
let file_name = GDataXMLNode.element(withName: "file_name", stringValue: iap.file_name)
|
||||||
|
let size = GDataXMLNode.element(withName: "size", stringValue: iap.size)
|
||||||
|
let checksum = GDataXMLNode.element(withName: "checksum", stringValue: iap.checksum)
|
||||||
|
let checksum_type = GDataXMLNode.attribute(withName: "type", stringValue: "md5") as? GDataXMLNode
|
||||||
|
checksum?.addAttribute(checksum_type)
|
||||||
|
review_screenshot?.addChild(file_name)
|
||||||
|
review_screenshot?.addChild(size)
|
||||||
|
review_screenshot?.addChild(checksum)
|
||||||
|
// review_notes
|
||||||
|
let review_notes = GDataXMLNode.element(withName: "review_notes", stringValue: iap.review_notes)
|
||||||
|
// 合并到in_app_purchase
|
||||||
|
in_app_purchase?.addChild(product_id)
|
||||||
|
in_app_purchase?.addChild(reference_name)
|
||||||
|
in_app_purchase?.addChild(type)
|
||||||
|
in_app_purchase?.addChild(products)
|
||||||
|
in_app_purchase?.addChild(locales)
|
||||||
|
in_app_purchase?.addChild(review_screenshot)
|
||||||
|
in_app_purchase?.addChild(review_notes)
|
||||||
|
in_app_purchases?.addChild(in_app_purchase)
|
||||||
|
}
|
||||||
|
software_metadata?.addChild(in_app_purchases)
|
||||||
|
software?.addChild(software_metadata)
|
||||||
|
root?.addChild(software)
|
||||||
|
// 生成xml文件
|
||||||
|
let xmlDoc = GDataXMLDocument(rootElement: root)
|
||||||
|
let data = xmlDoc?.xmlData()
|
||||||
|
// let xmlString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
|
||||||
|
// 保存文件
|
||||||
|
// 创建文件夹
|
||||||
|
if !FileManager.default.fileExists(atPath: directoryPath) {
|
||||||
|
do {
|
||||||
|
try FileManager.default.createDirectory(atPath: directoryPath, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
} catch {
|
||||||
|
print(error.localizedDescription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 创建文件
|
||||||
|
let filePath = directoryPath + "/metadata.xml"
|
||||||
|
debugPrint(filePath)
|
||||||
|
if !FileManager.default.fileExists(atPath: filePath) {
|
||||||
|
FileManager.default.createFile(atPath: filePath, contents: data, attributes: nil)
|
||||||
|
}else {
|
||||||
|
try? FileManager.default.removeItem(atPath: filePath)
|
||||||
|
FileManager.default.createFile(atPath: filePath, contents: data, attributes: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
for name in filePaths.keys {
|
||||||
|
let path = filePaths[name] ?? ""
|
||||||
|
try? FileManager.default.copyItem(atPath: path, toPath: directoryPath + "/" + name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func createShots(directoryPath: String) {
|
||||||
|
// 根标签
|
||||||
|
let root = GDataXMLNode.element(withName: "package")
|
||||||
|
// package属性
|
||||||
|
let version = GDataXMLNode.attribute(withName: "version", stringValue: "software5.11") as? GDataXMLNode
|
||||||
|
let xmlns = GDataXMLNode.attribute(withName: "xmlns", stringValue: "http://apple.com/itunes/importer") as? GDataXMLNode
|
||||||
|
root?.addAttribute(version)
|
||||||
|
root?.addAttribute(xmlns)
|
||||||
|
// provider\team_id
|
||||||
|
let pro = GDataXMLNode.element(withName: "provider", stringValue: provider)
|
||||||
|
let tid = GDataXMLNode.element(withName: "team_id", stringValue: team_id)
|
||||||
|
root?.addChild(pro)
|
||||||
|
root?.addChild(tid)
|
||||||
|
// software
|
||||||
|
// vendor_id
|
||||||
|
let software = GDataXMLNode.element(withName: "software")
|
||||||
|
let vid = GDataXMLNode.element(withName: "vendor_id", stringValue: vendor_id)
|
||||||
|
software?.addChild(vid)
|
||||||
|
// software_metadata/in_app_purchases
|
||||||
|
let software_metadata = GDataXMLNode.element(withName: "software_metadata")
|
||||||
|
let app_platform = GDataXMLNode.attribute(withName: "app_platform", stringValue: app_platform) as? GDataXMLNode
|
||||||
|
software_metadata?.addAttribute(app_platform)
|
||||||
|
// versions
|
||||||
|
let s_versions = GDataXMLNode.element(withName: "versions")
|
||||||
|
let s_version = GDataXMLNode.element(withName: "version")
|
||||||
|
let app_version = GDataXMLNode.attribute(withName: "string", stringValue: app_version) as? GDataXMLNode
|
||||||
|
s_version?.addAttribute(app_version)
|
||||||
|
// locales
|
||||||
|
let locales = GDataXMLNode.element(withName: "locales")
|
||||||
|
let locale = GDataXMLNode.element(withName: "locale")
|
||||||
|
let locale_name = GDataXMLNode.attribute(withName: "name", stringValue: app_locale) as? GDataXMLNode
|
||||||
|
locale?.addAttribute(locale_name)
|
||||||
|
let locale_title = GDataXMLNode.element(withName: "title", stringValue: app_title)
|
||||||
|
locale?.addChild(locale_title)
|
||||||
|
|
||||||
|
// app_previews
|
||||||
|
if videos.filter({ $0.value.count > 0 }).count > 0 {
|
||||||
|
let app_previews = GDataXMLNode.element(withName: "app_previews")
|
||||||
|
videos.forEach { (key: String, value: [Screen_Shot]) in
|
||||||
|
// app_preview
|
||||||
|
value.forEach { video in
|
||||||
|
let app_preview = GDataXMLNode.element(withName: "app_preview")
|
||||||
|
// display_target
|
||||||
|
let display_target = GDataXMLNode.attribute(withName: "display_target", stringValue: key) as? GDataXMLNode
|
||||||
|
app_preview?.addAttribute(display_target)
|
||||||
|
let position = GDataXMLNode.attribute(withName: "position", stringValue: video.position) as? GDataXMLNode
|
||||||
|
app_preview?.addAttribute(position)
|
||||||
|
// data_file
|
||||||
|
let data_file = GDataXMLNode.element(withName: "data_file")
|
||||||
|
let file_role = GDataXMLNode.attribute(withName: "role", stringValue: "source") as? GDataXMLNode
|
||||||
|
data_file?.addAttribute(file_role)
|
||||||
|
let file_size = GDataXMLNode.element(withName: "size", stringValue: video.size)
|
||||||
|
data_file?.addChild(file_size)
|
||||||
|
let file_name = GDataXMLNode.element(withName: "file_name", stringValue: video.file_name)
|
||||||
|
data_file?.addChild(file_name)
|
||||||
|
let checksum = GDataXMLNode.element(withName: "checksum", stringValue: video.checksum)
|
||||||
|
data_file?.addChild(checksum)
|
||||||
|
// data_file
|
||||||
|
let preview_image_time = GDataXMLNode.element(withName: "preview_image_time", stringValue: video.preview_time)
|
||||||
|
let preview_format = GDataXMLNode.attribute(withName: "format", stringValue: "30/1:1/nonDrop") as? GDataXMLNode
|
||||||
|
preview_image_time?.addAttribute(preview_format)
|
||||||
|
// 最后添加
|
||||||
|
app_preview?.addChild(data_file)
|
||||||
|
app_preview?.addChild(preview_image_time)
|
||||||
|
app_previews?.addChild(app_preview)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
locale?.addChild(app_previews)
|
||||||
|
}
|
||||||
|
|
||||||
|
if shots.filter({ $0.value.count > 0 }).count > 0 {
|
||||||
|
let app_screenshots = GDataXMLNode.element(withName: "software_screenshots")
|
||||||
|
shots.forEach { (key: String, value: [Screen_Shot]) in
|
||||||
|
// app_preview
|
||||||
|
value.forEach { video in
|
||||||
|
let app_screenshot = GDataXMLNode.element(withName: "software_screenshot")
|
||||||
|
// display_target
|
||||||
|
let display_target = GDataXMLNode.attribute(withName: "display_target", stringValue: key) as? GDataXMLNode
|
||||||
|
app_screenshot?.addAttribute(display_target)
|
||||||
|
let position = GDataXMLNode.attribute(withName: "position", stringValue: video.position) as? GDataXMLNode
|
||||||
|
app_screenshot?.addAttribute(position)
|
||||||
|
// data_file
|
||||||
|
let file_size = GDataXMLNode.element(withName: "size", stringValue: video.size)
|
||||||
|
app_screenshot?.addChild(file_size)
|
||||||
|
let file_name = GDataXMLNode.element(withName: "file_name", stringValue: video.file_name)
|
||||||
|
app_screenshot?.addChild(file_name)
|
||||||
|
let checksum = GDataXMLNode.element(withName: "checksum", stringValue: video.checksum)
|
||||||
|
app_screenshot?.addChild(checksum)
|
||||||
|
let checksum_type = GDataXMLNode.attribute(withName: "type", stringValue: "md5") as? GDataXMLNode
|
||||||
|
checksum?.addAttribute(checksum_type)
|
||||||
|
// 最后添加
|
||||||
|
app_screenshots?.addChild(app_screenshot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
locale?.addChild(app_screenshots)
|
||||||
|
}
|
||||||
|
|
||||||
|
locales?.addChild(locale)
|
||||||
|
s_version?.addChild(locales)
|
||||||
|
s_versions?.addChild(s_version)
|
||||||
|
software_metadata?.addChild(s_versions)
|
||||||
|
software?.addChild(software_metadata)
|
||||||
|
root?.addChild(software)
|
||||||
|
|
||||||
|
// 生成xml文件
|
||||||
|
let xmlDoc = GDataXMLDocument(rootElement: root)
|
||||||
|
let data = xmlDoc?.xmlData()
|
||||||
|
//let xmlString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
|
||||||
|
// 创建文件夹
|
||||||
|
if !FileManager.default.fileExists(atPath: directoryPath) {
|
||||||
|
do {
|
||||||
|
try FileManager.default.createDirectory(atPath: directoryPath, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
} catch {
|
||||||
|
print(error.localizedDescription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建文件
|
||||||
|
let filePath = directoryPath + "/metadata.xml"
|
||||||
|
debugPrint(filePath)
|
||||||
|
if !FileManager.default.fileExists(atPath: filePath) {
|
||||||
|
FileManager.default.createFile(atPath: filePath, contents: data, attributes: nil)
|
||||||
|
}else {
|
||||||
|
try? FileManager.default.removeItem(atPath: filePath)
|
||||||
|
FileManager.default.createFile(atPath: filePath, contents: data, attributes: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件复制
|
||||||
|
for name in filePaths.keys {
|
||||||
|
let path = filePaths[name] ?? ""
|
||||||
|
try? FileManager.default.copyItem(atPath: path, toPath: directoryPath + "/" + name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func createIpaFile(directoryPath: String) {
|
||||||
|
// 根标签
|
||||||
|
let root = GDataXMLNode.element(withName: "package")
|
||||||
|
// package属性
|
||||||
|
let version = GDataXMLNode.attribute(withName: "version", stringValue: "software5.11") as? GDataXMLNode
|
||||||
|
let xmlns = GDataXMLNode.attribute(withName: "xmlns", stringValue: "http://apple.com/itunes/importer") as? GDataXMLNode
|
||||||
|
root?.addAttribute(version)
|
||||||
|
root?.addAttribute(xmlns)
|
||||||
|
// software_assets
|
||||||
|
let software_assets = GDataXMLNode.element(withName: "software_assets")
|
||||||
|
let apple_id = GDataXMLNode.attribute(withName: "apple_id", stringValue: apple_id) as? GDataXMLNode
|
||||||
|
let app_platform = GDataXMLNode.attribute(withName: "app_platform", stringValue: app_platform) as? GDataXMLNode
|
||||||
|
software_assets?.addAttribute(apple_id)
|
||||||
|
software_assets?.addAttribute(app_platform)
|
||||||
|
// asset
|
||||||
|
let asset = GDataXMLNode.element(withName: "asset")
|
||||||
|
let asset_type = GDataXMLNode.attribute(withName: "type", stringValue: archive_type) as? GDataXMLNode
|
||||||
|
asset?.addAttribute(asset_type)
|
||||||
|
//data_file
|
||||||
|
let data_file = GDataXMLNode.element(withName: "data_file")
|
||||||
|
let size = GDataXMLNode.element(withName: "size", stringValue: ipa_size)
|
||||||
|
let file_name = GDataXMLNode.element(withName: "file_name", stringValue: ipa_name)
|
||||||
|
let checksum = GDataXMLNode.element(withName: "checksum", stringValue: ipa_md5)
|
||||||
|
let checksum_type = GDataXMLNode.attribute(withName: "type", stringValue: "md5") as? GDataXMLNode
|
||||||
|
checksum?.addAttribute(checksum_type)
|
||||||
|
// 逆序添加
|
||||||
|
data_file?.addChild(size)
|
||||||
|
data_file?.addChild(file_name)
|
||||||
|
data_file?.addChild(checksum)
|
||||||
|
asset?.addChild(data_file)
|
||||||
|
software_assets?.addChild(asset)
|
||||||
|
root?.addChild(software_assets)
|
||||||
|
|
||||||
|
// 生成xml文件
|
||||||
|
let xmlDoc = GDataXMLDocument(rootElement: root)
|
||||||
|
let data = xmlDoc?.xmlData()
|
||||||
|
let xmlString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
|
||||||
|
print(xmlString as Any)
|
||||||
|
// 创建文件夹
|
||||||
|
if !FileManager.default.fileExists(atPath: directoryPath) {
|
||||||
|
do {
|
||||||
|
try FileManager.default.createDirectory(atPath: directoryPath, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
} catch {
|
||||||
|
print(error.localizedDescription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建文件
|
||||||
|
let filePath = directoryPath + "/metadata.xml"
|
||||||
|
debugPrint(filePath)
|
||||||
|
if !FileManager.default.fileExists(atPath: filePath) {
|
||||||
|
FileManager.default.createFile(atPath: filePath, contents: data, attributes: nil)
|
||||||
|
}else {
|
||||||
|
try? FileManager.default.removeItem(atPath: filePath)
|
||||||
|
FileManager.default.createFile(atPath: filePath, contents: data, attributes: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件复制
|
||||||
|
for name in filePaths.keys {
|
||||||
|
let path = filePaths[name] ?? ""
|
||||||
|
try? FileManager.default.copyItem(atPath: path, toPath: directoryPath + "/" + name)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
//
|
||||||
|
// OutputExcelVC.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by 易承 on 2020/12/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class OutputExcelVC: NSViewController, NSTextViewDelegate {
|
||||||
|
|
||||||
|
@IBOutlet var inputText: NSTextView!
|
||||||
|
@IBOutlet weak var outputView: NSScrollView!
|
||||||
|
@IBOutlet var outputText: NSTextView!
|
||||||
|
|
||||||
|
@IBOutlet weak var inputCount: NSTextField!
|
||||||
|
@IBOutlet weak var outputCount: NSTextField!
|
||||||
|
|
||||||
|
var inputs = [String]()
|
||||||
|
var outputs = [String]()
|
||||||
|
|
||||||
|
var iapList: [IAPList.IAP] = []
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
inputText.delegate = self
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func close(_ sender: Any) {
|
||||||
|
dismiss(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func commit(_ sender: Any) {
|
||||||
|
outputs.removeAll()
|
||||||
|
for i in 0..<inputs.count {
|
||||||
|
for iap in iapList {
|
||||||
|
if inputs[i] == iap.vendorId {
|
||||||
|
outputs.append(iap.adamId)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 没有找到对应的商品id
|
||||||
|
if outputs.count != i+1 {
|
||||||
|
outputs.append("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outputView.isHidden = false
|
||||||
|
outputText.string = outputs.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func textViewDidChangeSelection(_ notification: Notification) {
|
||||||
|
inputs = inputText.string.components(separatedBy: CharacterSet(["\r", "\n"]))
|
||||||
|
inputs = inputs.filter { $0.count > 0 }
|
||||||
|
inputCount.stringValue = String(inputs.count)+"/100"
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkCount() -> Bool {
|
||||||
|
guard inputs.count == outputs.count else {
|
||||||
|
inputCount.textColor = NSColor.red
|
||||||
|
outputCount.textColor = NSColor.red
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
inputCount.textColor = NSColor.lightGray
|
||||||
|
outputCount.textColor = NSColor.lightGray
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// ScreenShotHelpPopoverVC.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/2/28.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class ScreenShotHelpPopoverVC: NSViewController {
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
// Do view setup here.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,526 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Window Controller-->
|
||||||
|
<scene sceneID="ere-Be-a90">
|
||||||
|
<objects>
|
||||||
|
<windowController storyboardIdentifier="ScreenShotUploadVC" id="3W3-GI-a0f" sceneMemberID="viewController">
|
||||||
|
<window key="window" title="App 预览和屏幕快照" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="BM8-gz-zq1">
|
||||||
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
|
<rect key="contentRect" x="425" y="462" width="651" height="582"/>
|
||||||
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
|
||||||
|
<value key="minSize" type="size" width="651" height="582"/>
|
||||||
|
<view key="contentView" id="uHV-KU-1f4">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="651" height="582"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="3W3-GI-a0f" id="dJN-a3-pmC"/>
|
||||||
|
</connections>
|
||||||
|
</window>
|
||||||
|
<connections>
|
||||||
|
<segue destination="0Dc-hX-r4A" kind="relationship" relationship="window.shadowedContentViewController" id="gH6-kg-BmL"/>
|
||||||
|
</connections>
|
||||||
|
</windowController>
|
||||||
|
<customObject id="Iam-wp-ls0" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-206" y="-129"/>
|
||||||
|
</scene>
|
||||||
|
<!--Screen Shot UploadVC-->
|
||||||
|
<scene sceneID="sHf-r7-vGK">
|
||||||
|
<objects>
|
||||||
|
<viewController id="0Dc-hX-r4A" customClass="ScreenShotUploadVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" identifier="fileName" misplaced="YES" id="85F-uq-hwS">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="878" height="549"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Mxb-rl-K9i">
|
||||||
|
<rect key="frame" x="18" y="470" width="294" height="27"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="27" id="Lv9-ug-a94"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="App 预览和屏幕快照批量上传" id="uj6-Ck-fci">
|
||||||
|
<font key="font" textStyle="title1" name=".SFNS-Regular"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3MQ-6H-3p4">
|
||||||
|
<rect key="frame" x="28" y="439" width="72" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="MSZ-z2-cOa"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="App Name:" id="IWG-xW-Lzs">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZlO-v7-4q0">
|
||||||
|
<rect key="frame" x="28" y="411" width="62" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="Bss-QQ-gWA"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="App SKU:" id="Esn-Cx-dqY">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="20s-B0-gsg">
|
||||||
|
<rect key="frame" x="28" y="382" width="57" height="17"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="17" id="ekU-ib-I7r"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="Team ID:" id="28f-6H-Z8f">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="UtW-mq-cag">
|
||||||
|
<rect key="frame" x="28" y="343" width="82" height="27"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="27" id="3eX-Ed-aLC"/>
|
||||||
|
<constraint firstAttribute="width" constant="78" id="8Sb-z0-LeY"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="本地化语言:" id="i7o-yB-2hn">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KuX-1c-GZT">
|
||||||
|
<rect key="frame" x="694" y="366" width="142" height="48"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="128" id="Rmb-yW-0Aa"/>
|
||||||
|
<constraint firstAttribute="height" constant="36" id="eTA-Jd-hvB"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="提交上传" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="hrB-5Z-Y0F">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
DQ
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedUploadButton:" target="0Dc-hX-r4A" id="u4G-Sv-SxO"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Jvd-lw-qhr" userLabel="Line Label">
|
||||||
|
<rect key="frame" x="20" y="327" width="829" height="1"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="0.5" id="aJu-0a-X2Z"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" borderStyle="bezel" drawsBackground="YES" id="Xa3-zH-NIv">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="spK-09-Iv9">
|
||||||
|
<rect key="frame" x="28" y="251" width="167" height="19"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="19" id="tEG-yM-eor"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="iPhone 5.5 英寸显示屏" id="wxW-1V-RfC">
|
||||||
|
<font key="font" metaFont="system" size="16"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dep-lv-PPg">
|
||||||
|
<rect key="frame" x="206" y="251" width="206" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="a0I-Ni-9u6"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="0/3 个 App 预览 | 0/10 张屏幕快照" id="5fc-UX-tt6">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" red="0.90980392160000001" green="0.4431372549" blue="0.28235294119999998" alpha="1" colorSpace="calibratedRGB"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<scrollView misplaced="YES" autohidesScrollers="YES" horizontalLineScroll="46" horizontalPageScroll="10" verticalLineScroll="46" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aVs-J6-lEK">
|
||||||
|
<rect key="frame" x="26" y="15" width="817" height="228"/>
|
||||||
|
<clipView key="contentView" id="p4Q-t6-wzE">
|
||||||
|
<rect key="frame" x="1" y="1" width="815" height="226"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" autosaveColumns="NO" rowHeight="46" rowSizeStyle="automatic" headerView="Ku2-Vf-JWY" viewBased="YES" id="p1m-uP-OeG">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="815" height="198"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<size key="intercellSpacing" width="17" height="0.0"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<tableViewGridLines key="gridStyleMask" horizontal="YES"/>
|
||||||
|
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<tableColumns>
|
||||||
|
<tableColumn identifier="review" width="103" minWidth="10" maxWidth="3.4028234663852886e+38" id="fHy-Ho-wZH">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="预览">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="ha0-0M-Ffe">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView id="KdO-NE-A5Z">
|
||||||
|
<rect key="frame" x="8" y="0.0" width="101" height="17"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="NAG-NQ-ohy">
|
||||||
|
<rect key="frame" x="3" y="5" width="95" height="16"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Aph-Kz-rKF">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="NAG-NQ-ohy" firstAttribute="leading" secondItem="KdO-NE-A5Z" secondAttribute="leading" constant="5" id="Qzd-pY-YNu"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="NAG-NQ-ohy" secondAttribute="trailing" constant="5" id="Z7l-cP-soS"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="NAG-NQ-ohy" secondAttribute="bottom" constant="5" id="mqp-Cr-oTB"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="NAG-NQ-ohy" id="JDc-27-na2"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="fileName" width="107" minWidth="10" maxWidth="3.4028234663852886e+38" id="XoH-de-TQc">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="文件名">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="PAy-jN-amB">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView id="8kw-Oh-5eV">
|
||||||
|
<rect key="frame" x="126" y="0.0" width="107" height="46"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="ZpI-yX-cwI">
|
||||||
|
<rect key="frame" x="8" y="15" width="91" height="16"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Nuc-wp-Y8q">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="ZpI-yX-cwI" secondAttribute="bottom" constant="15" id="9CM-5H-EDv"/>
|
||||||
|
<constraint firstItem="ZpI-yX-cwI" firstAttribute="top" secondItem="8kw-Oh-5eV" secondAttribute="top" constant="15" id="XJl-uJ-BTy"/>
|
||||||
|
<constraint firstItem="ZpI-yX-cwI" firstAttribute="leading" secondItem="8kw-Oh-5eV" secondAttribute="leading" constant="10" id="og2-26-a3E"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="ZpI-yX-cwI" secondAttribute="trailing" constant="10" id="zHv-Br-7Ec"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="ZpI-yX-cwI" id="y0B-aw-4ha"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="setting" width="401" minWidth="40" maxWidth="1000" id="3KK-up-tOz">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="设置">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="yaD-XC-Wsc">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView id="cyo-FF-nHa" customClass="ScreenShotUploadCell" customModule="AppleParty" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="250" y="0.0" width="401" height="77"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="E01-10-bG2">
|
||||||
|
<rect key="frame" x="8" y="47" width="97" height="20"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="93" id="SSF-7C-Gu5"/>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="ejh-gc-2QL"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="显示排序位置:" id="cfR-We-ve3">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kZA-xD-IWD">
|
||||||
|
<rect key="frame" x="8" y="12" width="97" height="20"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="jsB-v9-1VC"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="默认海报帧:" id="IYp-rd-BXS">
|
||||||
|
<font key="font" size="13" name="STSongti-SC-Regular"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField verticalHuggingPriority="750" tag="1" translatesAutoresizingMaskIntoConstraints="NO" id="uxr-F9-mJi">
|
||||||
|
<rect key="frame" x="108" y="46" width="273" height="21"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="21" id="5pH-Pj-fbY"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="显示顺序,从1开始排序" drawsBackground="YES" id="L6y-Sf-TMN">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField verticalHuggingPriority="750" tag="2" translatesAutoresizingMaskIntoConstraints="NO" id="YcP-ee-RZT">
|
||||||
|
<rect key="frame" x="108" y="6" width="273" height="21"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="21" id="gdz-We-LIn"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" title="00:00" placeholderString="时间格式:SS:mm 秒:毫秒 数值为:0~29间。例如:16:07 、05:29,最高为 30:00。不能高于视频最长时间。" drawsBackground="YES" id="dzm-Es-S5j">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="kZA-xD-IWD" firstAttribute="leading" secondItem="cyo-FF-nHa" secondAttribute="leading" constant="10" id="3B9-4Z-nZw"/>
|
||||||
|
<constraint firstItem="uxr-F9-mJi" firstAttribute="top" secondItem="E01-10-bG2" secondAttribute="top" id="7f7-M1-GGN"/>
|
||||||
|
<constraint firstItem="kZA-xD-IWD" firstAttribute="width" secondItem="E01-10-bG2" secondAttribute="width" id="DQC-79-ehS"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="YcP-ee-RZT" secondAttribute="trailing" constant="20" id="EYn-dY-rP9"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="uxr-F9-mJi" secondAttribute="trailing" constant="20" id="Jn6-44-sqT"/>
|
||||||
|
<constraint firstItem="YcP-ee-RZT" firstAttribute="leading" secondItem="kZA-xD-IWD" secondAttribute="trailing" constant="5" id="JtC-OE-Kwq"/>
|
||||||
|
<constraint firstItem="E01-10-bG2" firstAttribute="leading" secondItem="cyo-FF-nHa" secondAttribute="leading" constant="10" id="Q03-Qy-7Q0"/>
|
||||||
|
<constraint firstItem="YcP-ee-RZT" firstAttribute="top" secondItem="kZA-xD-IWD" secondAttribute="top" constant="5" id="ZYC-Gh-ASH"/>
|
||||||
|
<constraint firstItem="kZA-xD-IWD" firstAttribute="top" secondItem="E01-10-bG2" secondAttribute="bottom" constant="15" id="cFz-HX-QOp"/>
|
||||||
|
<constraint firstItem="E01-10-bG2" firstAttribute="top" secondItem="cyo-FF-nHa" secondAttribute="top" constant="10" id="fqy-H3-EiP"/>
|
||||||
|
<constraint firstItem="uxr-F9-mJi" firstAttribute="leading" secondItem="E01-10-bG2" secondAttribute="trailing" constant="5" id="kGR-Bo-qFu"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="cellTopConstraint" destination="fqy-H3-EiP" id="emc-ci-ZXC"/>
|
||||||
|
<outlet property="sortField" destination="uxr-F9-mJi" id="VMZ-ff-1bN"/>
|
||||||
|
<outlet property="videoField" destination="YcP-ee-RZT" id="gMs-cb-IYj"/>
|
||||||
|
<outlet property="videoTitleField" destination="kZA-xD-IWD" id="I0e-oS-eCl"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="operation" width="107" minWidth="10" maxWidth="3.4028234663852886e+38" id="cMb-Af-EgP">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="操作">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="Jwz-5G-aFH">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView id="hhC-jQ-V0O" customClass="ScreenShotDeleteCell" customModule="AppleParty" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="668" y="0.0" width="104" height="46"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="JVG-Tc-6tD">
|
||||||
|
<rect key="frame" x="8" y="15" width="88" height="16"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" id="bHJ-JZ-aNx">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Bwd-Sl-ANk">
|
||||||
|
<rect key="frame" x="22" y="13" width="60" height="19"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="60" id="HYq-OQ-l58"/>
|
||||||
|
<constraint firstAttribute="height" constant="18" id="s97-ed-fpd"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="roundRect" title="删除" bezelStyle="roundedRect" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="GjW-md-LGa">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="cellTitle"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedDeleteBtn:" target="hhC-jQ-V0O" id="LT8-Wz-RW8"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="Bwd-Sl-ANk" firstAttribute="centerX" secondItem="JVG-Tc-6tD" secondAttribute="centerX" id="2HE-Jv-Gsk"/>
|
||||||
|
<constraint firstItem="JVG-Tc-6tD" firstAttribute="leading" secondItem="hhC-jQ-V0O" secondAttribute="leading" constant="10" id="2Yv-Im-yn9"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="JVG-Tc-6tD" secondAttribute="bottom" constant="15" id="Dwk-rZ-aMz"/>
|
||||||
|
<constraint firstItem="JVG-Tc-6tD" firstAttribute="top" secondItem="hhC-jQ-V0O" secondAttribute="top" constant="15" id="ITt-0w-o5i"/>
|
||||||
|
<constraint firstItem="Bwd-Sl-ANk" firstAttribute="centerY" secondItem="JVG-Tc-6tD" secondAttribute="centerY" id="M8u-c0-abZ"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="JVG-Tc-6tD" secondAttribute="trailing" constant="10" id="WTf-Uj-Jbw"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="deleteBtn" destination="Bwd-Sl-ANk" id="F67-Zi-wkL"/>
|
||||||
|
<outlet property="textField" destination="JVG-Tc-6tD" id="rsf-6P-99d"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
</tableColumns>
|
||||||
|
</tableView>
|
||||||
|
</subviews>
|
||||||
|
</clipView>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Hkn-Y6-aEG">
|
||||||
|
<rect key="frame" x="1" y="166" width="737" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="NZV-o5-AMa">
|
||||||
|
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<tableHeaderView key="headerView" wantsLayer="YES" id="Ku2-Vf-JWY">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="815" height="28"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</tableHeaderView>
|
||||||
|
</scrollView>
|
||||||
|
<button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nCH-Zn-5hw">
|
||||||
|
<rect key="frame" x="816" y="470" width="25" height="25"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="F5P-Yf-BiP"/>
|
||||||
|
<constraint firstAttribute="width" constant="20" id="YYQ-Ev-d7y"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="help" bezelStyle="helpButton" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="jfD-lj-NmL">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedHelp:" target="0Dc-hX-r4A" id="tfT-3q-gD0"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ehn-6X-54w">
|
||||||
|
<rect key="frame" x="115" y="341" width="97" height="30"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="90" id="Fno-Uj-HPM"/>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="a5m-i1-Yfm"/>
|
||||||
|
</constraints>
|
||||||
|
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" arrowPosition="arrowAtCenter" id="g9M-Bc-Q1m">
|
||||||
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="menu"/>
|
||||||
|
<menu key="menu" id="zbe-7T-qiI"/>
|
||||||
|
</popUpButtonCell>
|
||||||
|
</popUpButton>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="at4-iG-YQR">
|
||||||
|
<rect key="frame" x="30" y="227" width="801" height="14"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="14" id="cqp-a6-Vxq"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="图片:1242 x 2208、2208 x 1242,视频:1080 x 1920、1920 x 1080" id="DWg-8k-ghA">
|
||||||
|
<font key="font" metaFont="smallSystem"/>
|
||||||
|
<color key="textColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Nej-FK-Dzi">
|
||||||
|
<rect key="frame" x="222" y="340" width="109" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="Gzp-sC-fV6"/>
|
||||||
|
<constraint firstAttribute="width" constant="95" id="eev-KL-t4w"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="刷新App信息" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="CAs-4h-gHg">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="reloadAppData:" target="0Dc-hX-r4A" id="SCO-to-s2U"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" tag="100" translatesAutoresizingMaskIntoConstraints="NO" id="PIn-kO-4pl">
|
||||||
|
<rect key="frame" x="338" y="340" width="114" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="IrN-eM-S0v"/>
|
||||||
|
<constraint firstAttribute="width" constant="100" id="hIt-id-j80"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="素材一键导入" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Ziv-5L-rRw">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="uploadFiles:" target="0Dc-hX-r4A" id="Zfc-Qu-PD1"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<segmentedControl verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hHc-Te-K7g">
|
||||||
|
<rect key="frame" x="30" y="284" width="801" height="24"/>
|
||||||
|
<segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="selectOne" id="mBw-8Z-Jk1">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<segments>
|
||||||
|
<segment label="iPhone 5.5 英寸显示屏" width="197" selected="YES"/>
|
||||||
|
<segment label="iPhone 6.5 英寸显示屏" width="196" tag="1"/>
|
||||||
|
<segment label="iPad Pro 12.9 英寸显示屏" width="195" tag="2"/>
|
||||||
|
</segments>
|
||||||
|
</segmentedCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="changeSegmentedControl:" target="0Dc-hX-r4A" id="Djb-aJ-oLH"/>
|
||||||
|
</connections>
|
||||||
|
</segmentedControl>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="spK-09-Iv9" firstAttribute="top" secondItem="hHc-Te-K7g" secondAttribute="bottom" constant="15" id="4cX-f4-l1P"/>
|
||||||
|
<constraint firstItem="ZlO-v7-4q0" firstAttribute="top" secondItem="3MQ-6H-3p4" secondAttribute="bottom" constant="12" id="5Ps-J2-pOj"/>
|
||||||
|
<constraint firstItem="aVs-J6-lEK" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="26" id="AFt-L6-42t"/>
|
||||||
|
<constraint firstItem="at4-iG-YQR" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="32" id="BOm-qs-DjO"/>
|
||||||
|
<constraint firstItem="20s-B0-gsg" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="30" id="Btu-xj-Wg4"/>
|
||||||
|
<constraint firstItem="ZlO-v7-4q0" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="30" id="C9X-av-LFu"/>
|
||||||
|
<constraint firstItem="hHc-Te-K7g" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="32" id="CuY-Lx-weE"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="Jvd-lw-qhr" secondAttribute="trailing" constant="20" symbolic="YES" id="EyO-Yp-0aB"/>
|
||||||
|
<constraint firstItem="ehn-6X-54w" firstAttribute="top" secondItem="UtW-mq-cag" secondAttribute="top" id="Ez2-k9-idp"/>
|
||||||
|
<constraint firstItem="hHc-Te-K7g" firstAttribute="top" secondItem="Jvd-lw-qhr" secondAttribute="bottom" constant="20" id="FUG-WX-bgc"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="aVs-J6-lEK" secondAttribute="bottom" constant="15" id="FbI-zp-JSu"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="hHc-Te-K7g" secondAttribute="trailing" constant="40" id="Fxj-LZ-Q4z"/>
|
||||||
|
<constraint firstItem="dep-lv-PPg" firstAttribute="leading" secondItem="spK-09-Iv9" secondAttribute="trailing" constant="15" id="NtE-hZ-KKZ"/>
|
||||||
|
<constraint firstItem="Nej-FK-Dzi" firstAttribute="leading" secondItem="ehn-6X-54w" secondAttribute="trailing" constant="21" id="P8h-PO-Y8M"/>
|
||||||
|
<constraint firstItem="3MQ-6H-3p4" firstAttribute="top" secondItem="Mxb-rl-K9i" secondAttribute="bottom" constant="15" id="PRO-Jm-Ssn"/>
|
||||||
|
<constraint firstItem="20s-B0-gsg" firstAttribute="top" secondItem="ZlO-v7-4q0" secondAttribute="bottom" constant="12" id="PsX-tY-Q46"/>
|
||||||
|
<constraint firstItem="Mxb-rl-K9i" firstAttribute="top" secondItem="85F-uq-hwS" secondAttribute="top" constant="20" symbolic="YES" id="Rkh-Lj-Ck2"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="KuX-1c-GZT" secondAttribute="trailing" constant="40" id="T5P-Xc-38I"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="aVs-J6-lEK" secondAttribute="trailing" constant="26" id="UAF-fF-Sik"/>
|
||||||
|
<constraint firstItem="at4-iG-YQR" firstAttribute="top" secondItem="spK-09-Iv9" secondAttribute="bottom" constant="10" id="VAg-k8-pvN"/>
|
||||||
|
<constraint firstItem="PIn-kO-4pl" firstAttribute="centerY" secondItem="Nej-FK-Dzi" secondAttribute="centerY" id="Vsj-Qj-KvF"/>
|
||||||
|
<constraint firstItem="aVs-J6-lEK" firstAttribute="top" secondItem="at4-iG-YQR" secondAttribute="bottom" constant="12" id="b8f-1e-2KH"/>
|
||||||
|
<constraint firstItem="Mxb-rl-K9i" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="20" symbolic="YES" id="cmx-sk-H0h"/>
|
||||||
|
<constraint firstItem="Nej-FK-Dzi" firstAttribute="centerY" secondItem="ehn-6X-54w" secondAttribute="centerY" id="cye-es-vQJ"/>
|
||||||
|
<constraint firstItem="dep-lv-PPg" firstAttribute="bottom" secondItem="spK-09-Iv9" secondAttribute="bottom" id="fHM-de-aYs"/>
|
||||||
|
<constraint firstItem="Jvd-lw-qhr" firstAttribute="top" secondItem="UtW-mq-cag" secondAttribute="bottom" constant="15" id="iEn-ye-6b5"/>
|
||||||
|
<constraint firstItem="3MQ-6H-3p4" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="30" id="igN-zI-akK"/>
|
||||||
|
<constraint firstItem="UtW-mq-cag" firstAttribute="top" secondItem="20s-B0-gsg" secondAttribute="bottom" constant="12" id="l7B-Bm-WY9"/>
|
||||||
|
<constraint firstItem="UtW-mq-cag" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="30" id="pW0-Ng-65T"/>
|
||||||
|
<constraint firstItem="PIn-kO-4pl" firstAttribute="leading" secondItem="Nej-FK-Dzi" secondAttribute="trailing" constant="21" id="qIZ-Gq-oEG"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="nCH-Zn-5hw" secondAttribute="trailing" constant="30" id="rtJ-UI-jgY"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="at4-iG-YQR" secondAttribute="trailing" constant="40" id="s3i-uE-Ywh"/>
|
||||||
|
<constraint firstItem="nCH-Zn-5hw" firstAttribute="top" secondItem="85F-uq-hwS" secondAttribute="top" constant="23" id="tLD-px-sAZ"/>
|
||||||
|
<constraint firstItem="spK-09-Iv9" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="30" id="wLU-yw-vve"/>
|
||||||
|
<constraint firstItem="ehn-6X-54w" firstAttribute="leading" secondItem="UtW-mq-cag" secondAttribute="trailing" constant="10" id="wkp-Bu-SXk"/>
|
||||||
|
<constraint firstItem="Jvd-lw-qhr" firstAttribute="leading" secondItem="85F-uq-hwS" secondAttribute="leading" constant="20" symbolic="YES" id="ydi-DL-qcW"/>
|
||||||
|
<constraint firstItem="KuX-1c-GZT" firstAttribute="bottom" secondItem="UtW-mq-cag" secondAttribute="bottom" constant="-30" id="zjy-Sp-N01"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="appName" destination="3MQ-6H-3p4" id="Wbh-6s-F6V"/>
|
||||||
|
<outlet property="appSKU" destination="ZlO-v7-4q0" id="Jzc-ud-uAe"/>
|
||||||
|
<outlet property="appleID" destination="20s-B0-gsg" id="f9M-XH-Bu0"/>
|
||||||
|
<outlet property="localButton" destination="ehn-6X-54w" id="rmS-y1-F1h"/>
|
||||||
|
<outlet property="tableView" destination="p1m-uP-OeG" id="xmk-vt-0eI"/>
|
||||||
|
<outlet property="tips_count" destination="dep-lv-PPg" id="mR6-By-bUY"/>
|
||||||
|
<outlet property="tips_desc" destination="at4-iG-YQR" id="C2L-ly-Vhd"/>
|
||||||
|
<outlet property="tips_title" destination="spK-09-Iv9" id="h4X-0x-ewy"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="Ih9-u3-myc" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="714" y="-140.5"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
//
|
||||||
|
// ScreenShotUploadCell.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/2/25.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
class ScreenShotDeleteCell: NSTableCellView {
|
||||||
|
|
||||||
|
typealias CallFunc = (_ row: Int) -> Void
|
||||||
|
var deleteCell: CallFunc?
|
||||||
|
var row: Int = 0
|
||||||
|
|
||||||
|
@IBOutlet weak var deleteBtn: NSButton!
|
||||||
|
|
||||||
|
@IBAction func clickedDeleteBtn(_ sender: NSButton) {
|
||||||
|
if let callBack = deleteCell {
|
||||||
|
callBack(row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScreenShotUploadCell: NSTableCellView {
|
||||||
|
|
||||||
|
typealias CallBackHandler = (_ value: String, _ row: Int) -> Void
|
||||||
|
var changeSortIndex: CallBackHandler?
|
||||||
|
var changeVideoFrame: CallBackHandler?
|
||||||
|
var row: Int = 0
|
||||||
|
|
||||||
|
@IBOutlet weak var sortField: NSTextField!
|
||||||
|
@IBOutlet weak var videoField: NSTextField!
|
||||||
|
@IBOutlet weak var videoTitleField: NSTextField!
|
||||||
|
|
||||||
|
@IBOutlet weak var cellTopConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
|
override func awakeFromNib() {
|
||||||
|
super.awakeFromNib()
|
||||||
|
|
||||||
|
sortField.delegate = self
|
||||||
|
videoField.delegate = self
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateData(sort: String, frame: String) {
|
||||||
|
sortField.stringValue = sort
|
||||||
|
videoField.stringValue = frame
|
||||||
|
}
|
||||||
|
|
||||||
|
func showVideoView(_ show: Bool) {
|
||||||
|
videoField.isHidden = !show
|
||||||
|
videoTitleField.isHidden = !show
|
||||||
|
cellTopConstraint.constant = show ? 10.0 : 20.0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ScreenShotUploadCell: NSTextFieldDelegate {
|
||||||
|
/// 内容改变
|
||||||
|
func controlTextDidChange(_ obj: Notification) {
|
||||||
|
let textField = obj.object as! NSTextField
|
||||||
|
let value = textField.stringValue
|
||||||
|
if textField.tag == sortField.tag, let callBack = changeSortIndex {
|
||||||
|
callBack(value, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
if textField.tag == videoField.tag, let callBack = changeVideoFrame {
|
||||||
|
callBack(value, row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,515 @@
|
|||||||
|
//
|
||||||
|
// ScreenShotUploadVC.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/2/25.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import AVFoundation
|
||||||
|
|
||||||
|
enum ScreenShotType: Int {
|
||||||
|
case iOS5_5 = 0 //iPhone 5.5 英寸显示屏
|
||||||
|
case iOS6_5 = 1 //iPhone 6.5 英寸显示屏
|
||||||
|
case iPad_Pro = 2 //iPad Pro 12.9 英寸显示屏
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScreenShotUploadVC: NSViewController {
|
||||||
|
|
||||||
|
public var currentApp: App? {
|
||||||
|
didSet {
|
||||||
|
fetchAppInfo()
|
||||||
|
fetchAppVersionData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public var appInfo: AppInfo?
|
||||||
|
|
||||||
|
@IBOutlet weak var appName: NSTextField!
|
||||||
|
@IBOutlet weak var appSKU: NSTextField!
|
||||||
|
@IBOutlet weak var appleID: NSTextField!
|
||||||
|
@IBOutlet weak var localButton: NSPopUpButton!
|
||||||
|
|
||||||
|
@IBOutlet weak var tableView: NSTableView!
|
||||||
|
@IBOutlet weak var tips_title: NSTextField!
|
||||||
|
@IBOutlet weak var tips_count: NSTextField!
|
||||||
|
@IBOutlet weak var tips_desc: NSTextField!
|
||||||
|
|
||||||
|
private var selectTag = 0
|
||||||
|
private var filesData = [[[String:String]]](repeating: [[String:String]](), count: 3)
|
||||||
|
|
||||||
|
private let imageTypes = ["jpg", "jpeg", "png", "JPG", "JPEG", "PNG"]
|
||||||
|
private let videoTypes = ["mov", "m4v", "mp4", "MOV", "M4V", "MP4"]
|
||||||
|
private let locales = ["zh-Hans", "zh-Hant", "ko", "ja"]
|
||||||
|
|
||||||
|
private let imageSizes = ["0": ["1242x2208", "2208x1242"],
|
||||||
|
"1": ["1242x2688", "2688x1242"],
|
||||||
|
"2": ["2048x2732", "2732x2048"]]
|
||||||
|
|
||||||
|
private let videoSizes = ["0": ["1080x1920", "1920x1080"],
|
||||||
|
"1": ["886x1920", "1920x886"],
|
||||||
|
"2": ["1200x1600", "1600x1200"]]
|
||||||
|
|
||||||
|
private var app_name = ""
|
||||||
|
private var app_version = ""
|
||||||
|
private var uploadModel = XMLModel()
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
setupView()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupView() {
|
||||||
|
|
||||||
|
tableView.delegate = self
|
||||||
|
tableView.dataSource = self
|
||||||
|
tableView.selectionHighlightStyle = .none
|
||||||
|
|
||||||
|
localButton.removeAllItems()
|
||||||
|
for locale in locales {
|
||||||
|
localButton.addItem(withTitle: locale)
|
||||||
|
}
|
||||||
|
localButton.selectItem(at: 0)
|
||||||
|
|
||||||
|
let teamId = UserCenter.shared.developerTeamId
|
||||||
|
if teamId.isEmpty {
|
||||||
|
fetchAccountTeamInfo()
|
||||||
|
} else {
|
||||||
|
appleID.stringValue = "Team ID: " + UserCenter.shared.developerTeamId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedUploadButton(_ sender: NSButton) {
|
||||||
|
|
||||||
|
guard filesData.filter({ $0.count > 0 }).count > 0 else {
|
||||||
|
APHUD.hide(message: "图片或视频不能为空!", view: self.view, delayTime: 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard app_name.count > 0, app_version.count > 0 else {
|
||||||
|
APHUD.hide(message: "应用名和版本获取失败!请刷新重试~", view: self.view, delayTime: 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let sp = UserCenter.shared.currentSPassword else {
|
||||||
|
let vc = APSPasswordSettingVC()
|
||||||
|
vc.updateCompletion = { [weak self] spassword in
|
||||||
|
if let sp = spassword {
|
||||||
|
self?.uploadData(sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
presentAsSheet(vc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadData(sp)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func reloadAppData(_ sender: Any) {
|
||||||
|
fetchAppVersionData()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedHelp(_ sender: NSButton) {
|
||||||
|
let vc = ScreenShotHelpPopoverVC()
|
||||||
|
self.present(vc, asPopoverRelativeTo: sender.frame, of: self.view, preferredEdge: .maxX, behavior:.transient)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@IBAction func changeSegmentedControl(_ sender: NSSegmentedControl) {
|
||||||
|
selectTag = sender.selectedTag()
|
||||||
|
reloadTableView(ScreenShotType.init(rawValue: selectTag) ?? .iOS5_5)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func uploadFiles(_ sender: NSButton) {
|
||||||
|
|
||||||
|
let openPanel = NSOpenPanel()
|
||||||
|
openPanel.canChooseFiles = true
|
||||||
|
openPanel.canChooseDirectories = true
|
||||||
|
openPanel.allowsMultipleSelection = true
|
||||||
|
openPanel.allowedFileTypes = imageTypes + videoTypes
|
||||||
|
|
||||||
|
openPanel.beginSheetModal(for: self.view.window!) { [self] (modalResponse) in
|
||||||
|
if modalResponse == .OK {
|
||||||
|
handleAutoImages(openPanel.urls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 网络请求
|
||||||
|
extension ScreenShotUploadVC {
|
||||||
|
|
||||||
|
func fetchAccountTeamInfo() {
|
||||||
|
// 获取开发者 Team id 信息
|
||||||
|
APClient.ascProvider.request { [weak self] result, response, error in
|
||||||
|
guard let err = error else {
|
||||||
|
self?.appleID.stringValue = "Team ID: " + UserCenter.shared.developerTeamId
|
||||||
|
return
|
||||||
|
}
|
||||||
|
APHUD.hide(message: err.localizedDescription, delayTime: 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchAppInfo(_ replay: Int = 3) {
|
||||||
|
guard let appid = currentApp?.appId else {
|
||||||
|
APHUD.hide(message: "当前 App 的 appleid 为空!", delayTime: 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
APClient.appInfo(appid: appid).request(showLoading: true) { [weak self] result, response, error in
|
||||||
|
if let err = error {
|
||||||
|
if replay > 0 {
|
||||||
|
self?.fetchAppInfo(replay-1)
|
||||||
|
} else {
|
||||||
|
NSAlert.show(err.localizedDescription)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let info = AppInfo(body: result)
|
||||||
|
self?.appInfo = info
|
||||||
|
self?.appSKU.stringValue = "App SKU: \(info.sku)"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchAppVersionData(_ replay: Int = 3) {
|
||||||
|
|
||||||
|
guard let appid = currentApp?.appId else {
|
||||||
|
APHUD.hide(message: "当前 App 的 appleid 为空!", delayTime: 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
APClient.appVersion(appid: appid).request { [weak self] data, response, error in
|
||||||
|
|
||||||
|
if let err = error {
|
||||||
|
if replay > 0 {
|
||||||
|
self?.fetchAppVersionData(replay-1)
|
||||||
|
} else {
|
||||||
|
NSAlert.show(err.localizedDescription)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = data["data"] as? [String: Any]
|
||||||
|
var dict = [String: Any]()
|
||||||
|
if let data = data, let platforms = data["platforms"] as? [[String: Any]] {
|
||||||
|
platforms.forEach { pf in
|
||||||
|
let platformString = pf["platformString"] as! String
|
||||||
|
dict[platformString] = pf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let ios = dict["ios"] as? [String: Any], let inFlightVersion = ios["inFlightVersion"] as? [String: Any] else {
|
||||||
|
NSAlert.show("当前 App 无待送审的版本,请检查确认!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self?.app_version = inFlightVersion["version"] as! String
|
||||||
|
|
||||||
|
if let data = data, let titles = data["localizedMetadata"] as? [[String: Any]] {
|
||||||
|
if titles.count > 0 {
|
||||||
|
// 这里只读取第一个
|
||||||
|
self?.app_name = titles[0]["name"] as! String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
self?.appName.stringValue = "App Name: \(self!.app_name) (\(self!.app_version))"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - NSTableViewDelegate
|
||||||
|
extension ScreenShotUploadVC: NSTableViewDelegate, NSTableViewDataSource {
|
||||||
|
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||||
|
return filesData[selectTag].count
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||||
|
|
||||||
|
var data = filesData[selectTag][row]
|
||||||
|
let isImg = data["type"]! == "1"
|
||||||
|
|
||||||
|
switch tableColumn!.identifier.rawValue {
|
||||||
|
case "review":
|
||||||
|
let imgView = NSImageView()
|
||||||
|
if let imageRef = NSImage(byReferencingFile: data["url"]!) {
|
||||||
|
imgView.image = imageRef
|
||||||
|
}
|
||||||
|
if !isImg {
|
||||||
|
let url = URL(fileURLWithPath: data["url"]!)
|
||||||
|
let videoImg = previewImageForLocalVideo(url)
|
||||||
|
imgView.image = videoImg
|
||||||
|
}
|
||||||
|
return imgView
|
||||||
|
case "fileName":
|
||||||
|
let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "fileName"), owner: self) as? NSTableCellView
|
||||||
|
cell?.textField?.stringValue = "\(isImg ? "图片" : "视频"):\n\(data["name"]!)"
|
||||||
|
return cell
|
||||||
|
case "setting":
|
||||||
|
let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "setting"), owner: self) as? ScreenShotUploadCell
|
||||||
|
cell?.row = row
|
||||||
|
cell?.showVideoView(!isImg)
|
||||||
|
cell?.updateData(sort: data["index"] ?? "", frame: data["frame"] ?? "00:00")
|
||||||
|
cell?.changeSortIndex = { index, crow in
|
||||||
|
data["index"] = index
|
||||||
|
self.filesData[self.selectTag][row] = data
|
||||||
|
}
|
||||||
|
cell?.changeVideoFrame = { frame, crow in
|
||||||
|
data["frame"] = frame
|
||||||
|
self.filesData[self.selectTag][row] = data
|
||||||
|
}
|
||||||
|
return cell
|
||||||
|
case "operation":
|
||||||
|
let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "operation"), owner: self) as? ScreenShotDeleteCell
|
||||||
|
cell?.row = row
|
||||||
|
cell?.deleteCell = { row in
|
||||||
|
self.filesData[self.selectTag].remove(at: row)
|
||||||
|
self.tableView.reloadData()
|
||||||
|
self.reloadTableView(ScreenShotType(rawValue: self.selectTag) ?? .iOS5_5)
|
||||||
|
}
|
||||||
|
return cell
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
|
||||||
|
80
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
|
||||||
|
// macOS 11 以下,如果不能点击,textfield 也不能点击
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Upload
|
||||||
|
extension ScreenShotUploadVC {
|
||||||
|
|
||||||
|
func uploadData(_ sp: SPassword) {
|
||||||
|
|
||||||
|
APHUD.show(message: "上传中", view: self.view)
|
||||||
|
|
||||||
|
let localIndex = localButton.indexOfSelectedItem
|
||||||
|
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async { [self] in
|
||||||
|
|
||||||
|
uploadModel = XMLModel()
|
||||||
|
|
||||||
|
guard let info = appInfo else {
|
||||||
|
fetchAppInfo()
|
||||||
|
APHUD.hide(message: "加载数据异常,请刷新后重试~")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadModel.vendor_id = info.sku
|
||||||
|
|
||||||
|
// 获取创建 itms 文件的路径
|
||||||
|
let filePath = XMLManager.getShotsPath(currentApp!.appId)
|
||||||
|
|
||||||
|
// 先删除旧的文档
|
||||||
|
XMLManager.deleteITMS(filePath)
|
||||||
|
|
||||||
|
// 数据转模型
|
||||||
|
handleDataToModel(localIndex)
|
||||||
|
|
||||||
|
uploadModel.createShots(directoryPath: filePath)
|
||||||
|
|
||||||
|
let result = XMLManager.uploadITMS(account: sp.account, pwd: sp.password, filePath: filePath)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
APHUD.hide()
|
||||||
|
self.closeSelfAndCallBack(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleDataToModel(_ localIndex: Int) {
|
||||||
|
|
||||||
|
let locale = locales[localIndex]
|
||||||
|
uploadModel.app_locale = locale
|
||||||
|
uploadModel.app_title = app_name
|
||||||
|
uploadModel.app_version = app_version
|
||||||
|
|
||||||
|
let iOS5_5 = filesData[0]
|
||||||
|
let iOS6_5 = filesData[1]
|
||||||
|
let iPad = filesData[2]
|
||||||
|
|
||||||
|
//iPhone 5.5 英寸
|
||||||
|
let (img5_5, video5_5, set5_5) = getScreenShotModel(iOS5_5)
|
||||||
|
uploadModel.shots["iOS-5.5-in"] = img5_5
|
||||||
|
uploadModel.videos["iOS-5.5-in"] = video5_5
|
||||||
|
|
||||||
|
//iPhone 6.5 英寸
|
||||||
|
let (img6_5, video6_5, set6_5) = getScreenShotModel(iOS6_5)
|
||||||
|
uploadModel.shots["iOS-6.5-in"] = img6_5
|
||||||
|
uploadModel.videos["iOS-6.5-in"] = video6_5
|
||||||
|
|
||||||
|
let (imgiPad, videoiPad, setiPad) = getScreenShotModel(iPad)
|
||||||
|
//iPad Pro(第2代) 12.9 英寸
|
||||||
|
uploadModel.shots["iOS-iPad-Pro"] = imgiPad
|
||||||
|
uploadModel.videos["iOS-iPad-Pro"] = videoiPad
|
||||||
|
//iPad Pro(第3代) 12.9 英寸
|
||||||
|
uploadModel.shots["iOS-iPad-Pro-2018"] = imgiPad
|
||||||
|
uploadModel.videos["iOS-iPad-Pro-2018"] = videoiPad
|
||||||
|
|
||||||
|
// 图片和视频资源
|
||||||
|
let fileURLs = set5_5.union(set6_5).union(setiPad)
|
||||||
|
var dict = [String: String]()
|
||||||
|
fileURLs.forEach { value in
|
||||||
|
value.forEach { dict[$0] = $1 }
|
||||||
|
}
|
||||||
|
uploadModel.filePaths = dict
|
||||||
|
}
|
||||||
|
|
||||||
|
func getScreenShotModel(_ shots: [[String: String]]) -> ([Screen_Shot], [Screen_Shot], Set<[String:String]>) {
|
||||||
|
var imageList = [Screen_Shot]()
|
||||||
|
var videoList = [Screen_Shot]()
|
||||||
|
var urlList: Set<[String:String]> = []
|
||||||
|
shots.forEach { data in
|
||||||
|
let isImg = data["type"]! == "1"
|
||||||
|
let url = data["url"] ?? ""
|
||||||
|
let kind = data["kind"] ?? ""
|
||||||
|
let name = kind + (data["name"] ?? "") //kind+name:避免不同尺寸使用同一个图片名字,导致替换的问题
|
||||||
|
let index = data["index"] ?? ""
|
||||||
|
let frame = "00:00:" + (data["frame"] ?? "00:00")
|
||||||
|
let size = URL.init(fileURLWithPath: url).fileSize()
|
||||||
|
let md5 = URL.init(fileURLWithPath: url).fileMD5()
|
||||||
|
let item = Screen_Shot(file_name: name, size: size, checksum: md5 ?? "", position: index, preview_time: frame)
|
||||||
|
urlList.insert([name: url])
|
||||||
|
isImg ? imageList.append(item) : videoList.append(item)
|
||||||
|
}
|
||||||
|
return (imageList, videoList, urlList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeSelfAndCallBack(_ result: (Int32, String?)) {
|
||||||
|
|
||||||
|
if result.0 == 0 {
|
||||||
|
NSAlert.show("上传成功!稍后可在苹果后台查看~")
|
||||||
|
// 删除旧的文档,避免占用空间过大
|
||||||
|
let filePath = XMLManager.getShotsPath(currentApp!.appId)
|
||||||
|
XMLManager.deleteITMS(filePath)
|
||||||
|
} else {
|
||||||
|
let sb = NSStoryboard(name: "APDebugVC", bundle: Bundle(for: self.classForCoder))
|
||||||
|
let newWC = sb.instantiateController(withIdentifier: "APDebugWC") as? NSWindowController
|
||||||
|
let logVC = newWC?.contentViewController as? APDebugVC
|
||||||
|
newWC?.window?.title = "上传错误日志"
|
||||||
|
logVC?.debugLog = result.1 ?? ""
|
||||||
|
newWC?.showWindow(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 内部方法
|
||||||
|
extension ScreenShotUploadVC {
|
||||||
|
|
||||||
|
func handleAutoImages(_ files: [URL]) {
|
||||||
|
files.forEach { url in
|
||||||
|
// 如果是文件夹,则递归
|
||||||
|
if url.hasDirectoryPath {
|
||||||
|
let urls = subFilesInDirectory(url: url)
|
||||||
|
handleAutoImages(urls)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var size = "0x0"
|
||||||
|
var sizes = [String: [String]]()
|
||||||
|
var type = "1"
|
||||||
|
let fileType = url.pathExtension
|
||||||
|
if imageTypes.contains(fileType) {
|
||||||
|
let image = NSImage(contentsOf: url)
|
||||||
|
guard let rep = image?.representations.first as? NSBitmapImageRep else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
size = "\(rep.pixelsWide)x\(rep.pixelsHigh)"
|
||||||
|
sizes = imageSizes
|
||||||
|
type = "1"
|
||||||
|
} else if videoTypes.contains(fileType) {
|
||||||
|
guard let track = AVURLAsset(url: url).tracks(withMediaType: AVMediaType.video).first else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let rep = track.naturalSize.applying(track.preferredTransform)
|
||||||
|
size = "\(Int(rep.width))x\(Int(rep.height))"
|
||||||
|
sizes = videoSizes
|
||||||
|
type = "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
sizes.forEach { (index: String, value: [String]) in
|
||||||
|
if value.contains(size) {
|
||||||
|
// 处理数据
|
||||||
|
var dict = [String: String]()
|
||||||
|
dict["url"] = url.path
|
||||||
|
dict["name"] = url.lastPathComponent
|
||||||
|
dict["type"] = type
|
||||||
|
dict["kind"] = index
|
||||||
|
// 更新数据
|
||||||
|
var value = filesData[Int(index)!]
|
||||||
|
value.append(dict)
|
||||||
|
filesData[Int(index)!] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 刷新列表
|
||||||
|
reloadTableView(ScreenShotType.init(rawValue: selectTag) ?? .iOS5_5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func subFilesInDirectory(url: URL) -> [URL] {
|
||||||
|
var urls = [URL]()
|
||||||
|
// 迭代器,包含子目录
|
||||||
|
let files = FileManager.default.enumerator(atPath: url.path)
|
||||||
|
while let file = files?.nextObject() {
|
||||||
|
if let file = file as? String {
|
||||||
|
let nextFile = url.appendingPathComponent(file)
|
||||||
|
if !nextFile.hasDirectoryPath {
|
||||||
|
urls.append(nextFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return urls
|
||||||
|
}
|
||||||
|
|
||||||
|
func reloadTableView(_ index: ScreenShotType) {
|
||||||
|
let list = filesData[index.rawValue]
|
||||||
|
let images = list.filter({ $0["type"] == "1" })
|
||||||
|
tips_count.stringValue = "\(list.count - images.count)/3 个 App 预览 | \(images.count)/10 张屏幕快照"
|
||||||
|
switch index {
|
||||||
|
case .iOS5_5:
|
||||||
|
tips_title.stringValue = "iPhone 5.5 英寸显示屏"
|
||||||
|
tips_desc.stringValue = "图片:1242 x 2208、2208 x 1242,视频:1080 x 1920、1920 x 1080"
|
||||||
|
case .iOS6_5:
|
||||||
|
tips_title.stringValue = "iPhone 6.5 英寸显示屏"
|
||||||
|
tips_desc.stringValue = "图片:1242 x 2688、2688 x 1242,视频:886 x 1920、1920 x 886"
|
||||||
|
case .iPad_Pro:
|
||||||
|
tips_title.stringValue = "iPad Pro 12.9 英寸显示屏"
|
||||||
|
tips_desc.stringValue = "图片:2048 x 2732、2732 x 2048,视频:1200 x 1600、1600 x 1200"
|
||||||
|
}
|
||||||
|
|
||||||
|
tableView.reloadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
func previewImageForLocalVideo(_ url: URL) -> NSImage? {
|
||||||
|
let asset = AVAsset(url: url)
|
||||||
|
let imageGenerator = AVAssetImageGenerator(asset: asset)
|
||||||
|
imageGenerator.appliesPreferredTrackTransform = true
|
||||||
|
var time = asset.duration
|
||||||
|
//If possible - take not the first frame (it could be completely black or white on camara's videos)
|
||||||
|
time.value = min(time.value, 2)
|
||||||
|
do {
|
||||||
|
let imageRef = try imageGenerator.copyCGImage(at: time, actualTime: nil)
|
||||||
|
let track = asset.tracks(withMediaType: AVMediaType.video).first
|
||||||
|
let rep = track?.naturalSize.applying(track?.preferredTransform ?? CGAffineTransform())
|
||||||
|
return NSImage(cgImage: imageRef, size: NSSize(width: rep?.width ?? 120, height: rep?.height ?? 60))
|
||||||
|
}
|
||||||
|
catch let error as NSError
|
||||||
|
{
|
||||||
|
print("Image generation failed with error \(error)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
/* Class = "NSWindow"; title = "My Apps"; ObjectID = "mcp-fI-0bQ"; */
|
||||||
|
"mcp-fI-0bQ.title" = "我的 App";
|
||||||
49
AppleParty/AppleParty/AppSettingView/APSettingVC.swift
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// SettingVC.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/3/25.
|
||||||
|
// Copyright © 2021 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class APSettingVC: NSViewController {
|
||||||
|
|
||||||
|
var isLoginViewShow: Bool {
|
||||||
|
get { return false }
|
||||||
|
set {
|
||||||
|
sPasswordBtn.isHidden = newValue
|
||||||
|
clearCacheBtn.isHidden = !newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBOutlet weak var trusDeviceBtn: NSButton!
|
||||||
|
@IBOutlet weak var sPasswordBtn: NSButton!
|
||||||
|
@IBOutlet weak var clearCacheBtn: NSButton!
|
||||||
|
|
||||||
|
@IBAction func clickedTrusDeviceBtn(_ sender: NSButton) {
|
||||||
|
InfoCenter.shared.trusDevice = sender.state == .on ? true : false
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedSPasswordBtn(_ sender: Any) {
|
||||||
|
let vc = APSPasswordSettingVC()
|
||||||
|
presentAsSheet(vc)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@IBAction func clickedClearCacheBtn(_ sender: NSButton) {
|
||||||
|
// 清掉缓存
|
||||||
|
HTTPCookieStorage.shared.cookies?.forEach(HTTPCookieStorage.shared.deleteCookie)
|
||||||
|
InfoCenter.shared.cookies = []
|
||||||
|
APHUD.hide(message: "清掉缓存成功", view: self.view)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
title = "App设置"
|
||||||
|
trusDeviceBtn.state = InfoCenter.shared.trusDevice ? .on : .off
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
102
AppleParty/AppleParty/AppSettingView/APSettingVC.xib
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="APSettingVC">
|
||||||
|
<connections>
|
||||||
|
<outlet property="clearCacheBtn" destination="CIQ-yY-qAH" id="Mk4-R7-k6m"/>
|
||||||
|
<outlet property="sPasswordBtn" destination="OeI-Rx-XtW" id="qOB-78-ZuO"/>
|
||||||
|
<outlet property="trusDeviceBtn" destination="nK7-5f-UIm" id="bjH-fk-npo"/>
|
||||||
|
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<customView id="Hz6-mo-xeY">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="479" height="376"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BKC-66-mUu">
|
||||||
|
<rect key="frame" x="23" y="333" width="98" height="28"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="28" id="vQz-oQ-tcK"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="设置选项" id="kek-10-Cgd">
|
||||||
|
<font key="font" metaFont="system" size="23"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nK7-5f-UIm">
|
||||||
|
<rect key="frame" x="23" y="287" width="106" height="22"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="hLE-Pd-isf"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="check" title="信任当前设备" bezelStyle="regularSquare" imagePosition="left" lineBreakMode="truncatingTail" state="on" inset="2" id="xCh-9D-HiS">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedTrusDeviceBtn:" target="-2" id="n1j-ha-ysp"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OeI-Rx-XtW">
|
||||||
|
<rect key="frame" x="147" y="281" width="114" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="QVA-uj-MiU"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="设置专用密码" bezelStyle="rounded" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="8of-Bf-BU2">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedSPasswordBtn:" target="-2" id="et4-Z4-s9t"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="TXz-pa-Bnd" userLabel="Line Label">
|
||||||
|
<rect key="frame" x="15" y="252" width="449" height="1"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="1" id="Hb5-8R-DdZ"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" borderStyle="bezel" drawsBackground="YES" id="FWN-Oq-vFj">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button hidden="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CIQ-yY-qAH">
|
||||||
|
<rect key="frame" x="272" y="281" width="114" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="J5b-3X-pyU"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="清除缓存数据" bezelStyle="rounded" alignment="center" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ylL-yw-axd">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedClearCacheBtn:" target="-2" id="Sy2-qH-JPk"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="OeI-Rx-XtW" firstAttribute="centerY" secondItem="nK7-5f-UIm" secondAttribute="centerY" id="1fH-HN-CXg"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="TXz-pa-Bnd" secondAttribute="trailing" constant="15" id="7d6-CI-IjY"/>
|
||||||
|
<constraint firstItem="BKC-66-mUu" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="25" id="9hM-UB-Bbc"/>
|
||||||
|
<constraint firstItem="CIQ-yY-qAH" firstAttribute="leading" secondItem="nK7-5f-UIm" secondAttribute="trailing" priority="550" constant="25" id="FAu-ZM-maQ"/>
|
||||||
|
<constraint firstItem="TXz-pa-Bnd" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="15" id="GHp-tK-8s3"/>
|
||||||
|
<constraint firstItem="CIQ-yY-qAH" firstAttribute="centerY" secondItem="nK7-5f-UIm" secondAttribute="centerY" id="Koe-bl-rog"/>
|
||||||
|
<constraint firstItem="TXz-pa-Bnd" firstAttribute="top" secondItem="nK7-5f-UIm" secondAttribute="bottom" constant="35" id="Nue-2l-Tec"/>
|
||||||
|
<constraint firstItem="BKC-66-mUu" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="15" id="bcl-Ls-nqp"/>
|
||||||
|
<constraint firstItem="nK7-5f-UIm" firstAttribute="top" secondItem="BKC-66-mUu" secondAttribute="bottom" constant="25" id="l1u-Xr-4yX"/>
|
||||||
|
<constraint firstItem="nK7-5f-UIm" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="25" id="n3v-kC-eX9"/>
|
||||||
|
<constraint firstItem="OeI-Rx-XtW" firstAttribute="leading" secondItem="nK7-5f-UIm" secondAttribute="trailing" constant="25" id="qUw-wS-OCL"/>
|
||||||
|
<constraint firstItem="CIQ-yY-qAH" firstAttribute="leading" secondItem="OeI-Rx-XtW" secondAttribute="trailing" priority="750" constant="25" id="wnS-ya-Ulb"/>
|
||||||
|
</constraints>
|
||||||
|
<point key="canvasLocation" x="48.5" y="194"/>
|
||||||
|
</customView>
|
||||||
|
</objects>
|
||||||
|
</document>
|
||||||
7
AppleParty/AppleParty/AppleParty-Bridging-Header.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
//
|
||||||
|
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "GDataXMLNode.h"
|
||||||
|
#import "QrcodeUtil.h"
|
||||||
|
#import "MBProgressHUD.h"
|
||||||
5
AppleParty/AppleParty/AppleParty.entitlements
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict/>
|
||||||
|
</plist>
|
||||||
61
AppleParty/AppleParty/EmailToolView/EmailSettingVC.swift
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
//
|
||||||
|
// EmailSettingVC.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/3/29.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
|
||||||
|
struct EamilConfigs {
|
||||||
|
var name: String = ""
|
||||||
|
var addr: String = ""
|
||||||
|
var pwd: String = ""
|
||||||
|
var smtp: String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var eamilConfigs: EamilConfigs? {
|
||||||
|
get {
|
||||||
|
let value = (try? APUtil.keychain.getString("APEmailSetting_Key")) ?? ""
|
||||||
|
let arr = value.components(separatedBy: "|")
|
||||||
|
guard arr.count == 4 else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return EamilConfigs(name: arr[0], addr: arr[1], pwd: arr[2], smtp: arr[3])
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
guard let newValue = newValue else { return }
|
||||||
|
let value = "\(newValue.name)|\(newValue.addr)|\(newValue.pwd)|\(newValue.smtp)"
|
||||||
|
try? APUtil.keychain.set(value, key: "APEmailSetting_Key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EmailSettingVC: NSViewController {
|
||||||
|
|
||||||
|
public var closeHandle: (() -> Void)?
|
||||||
|
|
||||||
|
@IBOutlet weak var emailNameView: NSTextField!
|
||||||
|
@IBOutlet weak var emailAddrView: NSTextField!
|
||||||
|
@IBOutlet weak var emailPwdView: NSTextField!
|
||||||
|
@IBOutlet weak var emailSMTPView: NSTextField!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
// Do view setup here.
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedCancelBtn(_ sender: Any) {
|
||||||
|
closeHandle?()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedSubmitBtn(_ sender: Any) {
|
||||||
|
|
||||||
|
eamilConfigs = EamilConfigs(name: emailNameView.stringValue, addr: emailAddrView.stringValue, pwd: emailPwdView.stringValue, smtp: emailSMTPView.stringValue)
|
||||||
|
closeHandle?()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
131
AppleParty/AppleParty/EmailToolView/EmailSettingVC.xib
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="EmailSettingVC" customModule="AppleParty" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="emailAddrView" destination="xC8-OP-5OB" id="iA5-lT-uGT"/>
|
||||||
|
<outlet property="emailNameView" destination="eDA-H4-wtu" id="ggR-LW-T6u"/>
|
||||||
|
<outlet property="emailPwdView" destination="VVK-4v-kKh" id="gkL-zS-b4P"/>
|
||||||
|
<outlet property="emailSMTPView" destination="Fnq-BH-an4" id="c0U-Xb-4Ak"/>
|
||||||
|
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<customView id="Hz6-mo-xeY">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="319" height="328"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="UGV-LT-zwz">
|
||||||
|
<rect key="frame" x="8" y="283" width="303" height="25"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="MTZ-og-I8m"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="设置邮箱账户" id="Txf-cs-igw">
|
||||||
|
<font key="font" metaFont="systemBold" size="17"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fOt-av-hwc">
|
||||||
|
<rect key="frame" x="206" y="18" width="80" height="34"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="22" id="8Vn-vC-kMW"/>
|
||||||
|
<constraint firstAttribute="width" constant="66" id="II3-ZH-6L6"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="设置" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="P5t-QJ-zKH">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedSubmitBtn:" target="-2" id="5bV-hs-YGt"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LT7-p4-ggn">
|
||||||
|
<rect key="frame" x="33" y="18" width="80" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="66" id="Le4-RM-yje"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="取消" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="zzW-EO-bUm">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" size="13" name="STSongti-SC-Regular"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedCancelBtn:" target="-2" id="Fdm-gw-h3c"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="eDA-H4-wtu">
|
||||||
|
<rect key="frame" x="40" y="219" width="239" height="34"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="34" id="Pha-T8-v9U"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="发送人名字" bezelStyle="round" id="u8o-KC-IxS">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xC8-OP-5OB">
|
||||||
|
<rect key="frame" x="40" y="173" width="239" height="34"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="34" id="80Y-rx-GcW"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="发送邮箱地址" bezelStyle="round" id="g7M-ao-gse">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="VVK-4v-kKh">
|
||||||
|
<rect key="frame" x="40" y="127" width="239" height="34"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="34" id="kT1-yB-MMR"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="发送邮箱密码" bezelStyle="round" id="Doe-al-eff">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Fnq-BH-an4">
|
||||||
|
<rect key="frame" x="40" y="81" width="239" height="34"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="34" id="zSX-jX-gJu"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="邮箱服务地址(如 smtp.xx.com)" bezelStyle="round" id="otT-Sn-TWW">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="VVK-4v-kKh" firstAttribute="top" secondItem="xC8-OP-5OB" secondAttribute="bottom" constant="12" id="5gY-FH-ljk"/>
|
||||||
|
<constraint firstItem="xC8-OP-5OB" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="40" id="Ax7-4Z-zIa"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="eDA-H4-wtu" secondAttribute="trailing" constant="40" id="BaG-jk-2M6"/>
|
||||||
|
<constraint firstItem="Fnq-BH-an4" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="40" id="FCR-Fd-als"/>
|
||||||
|
<constraint firstItem="Fnq-BH-an4" firstAttribute="top" secondItem="VVK-4v-kKh" secondAttribute="bottom" constant="12" id="Lsf-Gg-Hi5"/>
|
||||||
|
<constraint firstItem="LT7-p4-ggn" firstAttribute="leading" secondItem="Fnq-BH-an4" secondAttribute="leading" id="NqC-bU-xow"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="LT7-p4-ggn" secondAttribute="bottom" constant="25" id="Pgl-yp-65W"/>
|
||||||
|
<constraint firstItem="fOt-av-hwc" firstAttribute="trailing" secondItem="Fnq-BH-an4" secondAttribute="trailing" id="TNu-gl-01i"/>
|
||||||
|
<constraint firstItem="VVK-4v-kKh" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="40" id="TeH-8x-GIb"/>
|
||||||
|
<constraint firstItem="eDA-H4-wtu" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="40" id="UtA-LQ-pGW"/>
|
||||||
|
<constraint firstItem="UGV-LT-zwz" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="20" symbolic="YES" id="XAE-zG-uBj"/>
|
||||||
|
<constraint firstItem="xC8-OP-5OB" firstAttribute="top" secondItem="eDA-H4-wtu" secondAttribute="bottom" constant="12" id="fG3-IY-ptx"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="Fnq-BH-an4" secondAttribute="trailing" constant="40" id="oJO-fs-eS0"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="VVK-4v-kKh" secondAttribute="trailing" constant="40" id="olk-5z-Atb"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="fOt-av-hwc" secondAttribute="bottom" constant="25" id="qUL-ii-wn5"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="UGV-LT-zwz" secondAttribute="trailing" constant="10" id="rew-BG-Egu"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="xC8-OP-5OB" secondAttribute="trailing" constant="40" id="z5o-8S-Uvi"/>
|
||||||
|
<constraint firstItem="eDA-H4-wtu" firstAttribute="top" secondItem="UGV-LT-zwz" secondAttribute="bottom" constant="30" id="zDm-hN-QrO"/>
|
||||||
|
<constraint firstItem="UGV-LT-zwz" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="10" id="zVD-UO-S2w"/>
|
||||||
|
</constraints>
|
||||||
|
<point key="canvasLocation" x="58.5" y="182"/>
|
||||||
|
</customView>
|
||||||
|
</objects>
|
||||||
|
</document>
|
||||||
251
AppleParty/AppleParty/EmailToolView/EmailTool.storyboard
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Window Controller-->
|
||||||
|
<scene sceneID="6Wf-BN-a9j">
|
||||||
|
<objects>
|
||||||
|
<windowController storyboardIdentifier="EmailTool" id="T4u-uh-EbA" sceneMemberID="viewController">
|
||||||
|
<window key="window" title="邮件助手" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="qJz-u6-Hg1">
|
||||||
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
|
<rect key="contentRect" x="359" y="388" width="600" height="450"/>
|
||||||
|
<rect key="screenRect" x="0.0" y="0.0" width="2240" height="1235"/>
|
||||||
|
<view key="contentView" id="mmv-OD-ZTq">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="T4u-uh-EbA" id="dxr-DV-lYU"/>
|
||||||
|
</connections>
|
||||||
|
</window>
|
||||||
|
<connections>
|
||||||
|
<segue destination="hI8-xu-11y" kind="relationship" relationship="window.shadowedContentViewController" id="XrI-xM-H9W"/>
|
||||||
|
</connections>
|
||||||
|
</windowController>
|
||||||
|
<customObject id="a65-QT-Nbx" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-249" y="220"/>
|
||||||
|
</scene>
|
||||||
|
<!--邮件助手-->
|
||||||
|
<scene sceneID="NVw-xI-A8X">
|
||||||
|
<objects>
|
||||||
|
<viewController title="邮件助手" id="hI8-xu-11y" customClass="EmailToolVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" id="fZy-4A-sDv">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="557"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="RWF-3X-mT6">
|
||||||
|
<rect key="frame" x="20" y="505" width="377" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="32" id="XIR-wi-l2X"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="收件人邮箱地址" bezelStyle="round" id="j19-N4-QQn">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="czV-Gg-3Ri">
|
||||||
|
<rect key="frame" x="20" y="453" width="467" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="32" id="Osc-bj-ZTE"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="邮件标题" bezelStyle="round" id="GwA-zk-eAs">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aBl-I5-nCZ">
|
||||||
|
<rect key="frame" x="495" y="447" width="92" height="42"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="30" id="1zZ-G3-6Qa"/>
|
||||||
|
<constraint firstAttribute="width" constant="78" id="s1Y-ba-LVb"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="发送" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="msG-2W-3ir">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="sendEmailButton:" target="hI8-xu-11y" id="6D3-P8-7tz"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<scrollView borderType="line" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bZ7-lQ-t1B">
|
||||||
|
<rect key="frame" x="20" y="300" width="560" height="125"/>
|
||||||
|
<clipView key="contentView" drawsBackground="NO" id="EkG-rr-J9X">
|
||||||
|
<rect key="frame" x="1" y="1" width="558" height="123"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" allowsUndo="YES" spellingCorrection="YES" smartInsertDelete="YES" id="4ah-H5-AbK">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="558" height="123"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<size key="minSize" width="558" height="123"/>
|
||||||
|
<size key="maxSize" width="758" height="10000000"/>
|
||||||
|
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textView>
|
||||||
|
</subviews>
|
||||||
|
</clipView>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="125" id="DGW-ts-Y3w"/>
|
||||||
|
</constraints>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="w5S-wA-NZW">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="I17-we-bph">
|
||||||
|
<rect key="frame" x="543" y="1" width="16" height="123"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
<scrollView hidden="YES" borderType="line" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Dd7-vC-Tdz">
|
||||||
|
<rect key="frame" x="20" y="30" width="560" height="220"/>
|
||||||
|
<clipView key="contentView" drawsBackground="NO" id="nJr-qZ-vEo">
|
||||||
|
<rect key="frame" x="1" y="1" width="558" height="218"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textView editable="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="egz-HU-sRx">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="558" height="218"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<size key="minSize" width="558" height="218"/>
|
||||||
|
<size key="maxSize" width="758" height="10000000"/>
|
||||||
|
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textView>
|
||||||
|
</subviews>
|
||||||
|
</clipView>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="C94-21-myA">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="qOY-uP-fav">
|
||||||
|
<rect key="frame" x="543" y="1" width="16" height="218"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hPQ-8D-VUS">
|
||||||
|
<rect key="frame" x="18" y="268" width="87" height="18"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="85" id="CGO-Yn-TVv"/>
|
||||||
|
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="16" id="Kbs-f6-qvx"/>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="MYL-95-hpC"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="check" title="多个附件" bezelStyle="regularSquare" imagePosition="left" inset="2" id="cMY-nK-eFc">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="ChangeMultipleFiles:" target="hI8-xu-11y" id="SS4-P3-ac7"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button hidden="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YDc-3K-JgG">
|
||||||
|
<rect key="frame" x="113" y="255" width="114" height="42"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="100" id="cHP-vM-kak"/>
|
||||||
|
<constraint firstAttribute="height" constant="30" id="iaX-PB-BFX"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="批量选择附件" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="b4z-Tm-zdY">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="selectFiles:" target="hI8-xu-11y" id="cJV-aH-jrm"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button hidden="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fkW-LQ-FK3">
|
||||||
|
<rect key="frame" x="473" y="255" width="114" height="42"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="30" id="Dc4-0C-chh"/>
|
||||||
|
<constraint firstAttribute="width" constant="100" id="UkJ-G9-xpX"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="清空所有附件" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Eol-UM-Fq4">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" size="13" name="Times-Roman"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clearnAllFiles:" target="hI8-xu-11y" id="aQ3-JN-lkH"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nEa-8J-VHH">
|
||||||
|
<rect key="frame" x="510" y="511" width="78" height="19"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="18" id="SNh-ap-QDY"/>
|
||||||
|
<constraint firstAttribute="width" constant="78" id="akd-Tt-PbM"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="roundRect" title="邮箱设置" bezelStyle="roundedRect" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="5t6-n1-Yfi">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="cellTitle"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedEmailSettingButton:" target="hI8-xu-11y" id="KkT-rB-A5p"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wKD-68-g5N">
|
||||||
|
<rect key="frame" x="410" y="512" width="80" height="18"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="Am0-qN-l34"/>
|
||||||
|
<constraint firstAttribute="width" constant="78" id="C2k-Ct-lk8"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="check" title="记住邮箱" bezelStyle="regularSquare" imagePosition="left" inset="2" id="XZa-0z-9Wx">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="rememberEmail:" target="hI8-xu-11y" id="Yk7-qe-oOx"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="fkW-LQ-FK3" firstAttribute="centerY" secondItem="YDc-3K-JgG" secondAttribute="centerY" id="3vf-mh-NPN"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="aBl-I5-nCZ" secondAttribute="trailing" constant="20" symbolic="YES" id="D6v-Dw-Hce"/>
|
||||||
|
<constraint firstItem="czV-Gg-3Ri" firstAttribute="top" secondItem="RWF-3X-mT6" secondAttribute="bottom" constant="20" id="EUY-Fa-byW"/>
|
||||||
|
<constraint firstItem="hPQ-8D-VUS" firstAttribute="leading" secondItem="fZy-4A-sDv" secondAttribute="leading" constant="20" symbolic="YES" id="Gff-XY-Gge"/>
|
||||||
|
<constraint firstItem="bZ7-lQ-t1B" firstAttribute="top" secondItem="czV-Gg-3Ri" secondAttribute="bottom" constant="28" id="GhT-KF-C9V"/>
|
||||||
|
<constraint firstItem="aBl-I5-nCZ" firstAttribute="leading" secondItem="czV-Gg-3Ri" secondAttribute="trailing" constant="15" id="Ie7-SA-119"/>
|
||||||
|
<constraint firstItem="Dd7-vC-Tdz" firstAttribute="leading" secondItem="fZy-4A-sDv" secondAttribute="leading" constant="20" symbolic="YES" id="V6i-qP-2gR"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="Dd7-vC-Tdz" secondAttribute="bottom" constant="30" id="Wb4-eP-RbL"/>
|
||||||
|
<constraint firstItem="YDc-3K-JgG" firstAttribute="centerY" secondItem="hPQ-8D-VUS" secondAttribute="centerY" id="XCM-Ec-Gh8"/>
|
||||||
|
<constraint firstItem="bZ7-lQ-t1B" firstAttribute="leading" secondItem="fZy-4A-sDv" secondAttribute="leading" constant="20" symbolic="YES" id="Z0A-we-uv2"/>
|
||||||
|
<constraint firstItem="nEa-8J-VHH" firstAttribute="centerY" secondItem="RWF-3X-mT6" secondAttribute="centerY" id="c43-YH-VpN"/>
|
||||||
|
<constraint firstItem="nEa-8J-VHH" firstAttribute="leading" secondItem="wKD-68-g5N" secondAttribute="trailing" constant="20" id="dAP-vZ-QuW"/>
|
||||||
|
<constraint firstItem="aBl-I5-nCZ" firstAttribute="centerY" secondItem="czV-Gg-3Ri" secondAttribute="centerY" id="eOU-ym-ifE"/>
|
||||||
|
<constraint firstItem="RWF-3X-mT6" firstAttribute="top" secondItem="fZy-4A-sDv" secondAttribute="top" constant="20" symbolic="YES" id="gTw-GK-wmY"/>
|
||||||
|
<constraint firstItem="wKD-68-g5N" firstAttribute="leading" secondItem="RWF-3X-mT6" secondAttribute="trailing" constant="15" id="iq6-W1-gyS"/>
|
||||||
|
<constraint firstItem="czV-Gg-3Ri" firstAttribute="leading" secondItem="fZy-4A-sDv" secondAttribute="leading" constant="20" symbolic="YES" id="lRZ-eR-gSE"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="fkW-LQ-FK3" secondAttribute="trailing" constant="20" symbolic="YES" id="lTW-1f-YpI"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="bZ7-lQ-t1B" secondAttribute="trailing" constant="20" symbolic="YES" id="ldL-eS-Nkc"/>
|
||||||
|
<constraint firstItem="hPQ-8D-VUS" firstAttribute="top" secondItem="bZ7-lQ-t1B" secondAttribute="bottom" constant="15" id="lhC-BK-8Tf"/>
|
||||||
|
<constraint firstItem="YDc-3K-JgG" firstAttribute="leading" secondItem="hPQ-8D-VUS" secondAttribute="trailing" constant="15" id="nVw-TE-aXd"/>
|
||||||
|
<constraint firstItem="wKD-68-g5N" firstAttribute="centerY" secondItem="RWF-3X-mT6" secondAttribute="centerY" id="uBR-yz-DpK"/>
|
||||||
|
<constraint firstItem="RWF-3X-mT6" firstAttribute="leading" secondItem="fZy-4A-sDv" secondAttribute="leading" constant="20" symbolic="YES" id="vI6-lv-Ec5"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="Dd7-vC-Tdz" secondAttribute="trailing" constant="20" symbolic="YES" id="wyE-Sc-s5e"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="nEa-8J-VHH" secondAttribute="trailing" constant="12" id="xMj-dk-PSK"/>
|
||||||
|
<constraint firstItem="Dd7-vC-Tdz" firstAttribute="top" secondItem="YDc-3K-JgG" secondAttribute="bottom" constant="12" id="xRd-FW-dKv"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="clearnAllFilesButton" destination="fkW-LQ-FK3" id="xYy-yP-YKJ"/>
|
||||||
|
<outlet property="emailContentTextView" destination="4ah-H5-AbK" id="Khn-UX-lUg"/>
|
||||||
|
<outlet property="emailRecipientTF" destination="RWF-3X-mT6" id="yKO-Jx-mde"/>
|
||||||
|
<outlet property="emailSendButton" destination="aBl-I5-nCZ" id="Vw5-h1-kJe"/>
|
||||||
|
<outlet property="emailTitleTF" destination="czV-Gg-3Ri" id="nXa-9E-AF0"/>
|
||||||
|
<outlet property="emialContentView" destination="bZ7-lQ-t1B" id="xQU-qv-3Af"/>
|
||||||
|
<outlet property="multipleFilesButton" destination="hPQ-8D-VUS" id="mCJ-3B-Fve"/>
|
||||||
|
<outlet property="rememberEmailButton" destination="wKD-68-g5N" id="Gwn-wI-TB3"/>
|
||||||
|
<outlet property="selectFilesButton" destination="YDc-3K-JgG" id="HJv-eO-apA"/>
|
||||||
|
<outlet property="selectFilesView" destination="Dd7-vC-Tdz" id="BXM-HP-Y0A"/>
|
||||||
|
<outlet property="selectilesTextView" destination="egz-HU-sRx" id="7kr-5W-ahZ"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="NPu-9T-cfy" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="455" y="211.5"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
||||||
247
AppleParty/AppleParty/EmailToolView/EmailToolVC.swift
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
//
|
||||||
|
// EmailToolVC.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by iHTC on 20211025.
|
||||||
|
// Copyright © 2021 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
|
||||||
|
class EmailToolVC: NSViewController {
|
||||||
|
|
||||||
|
var emailTitle: String? {
|
||||||
|
didSet {
|
||||||
|
if let text = emailTitle {
|
||||||
|
emailTitleTF.stringValue = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var emailContent: String? {
|
||||||
|
didSet {
|
||||||
|
if let text = emailContent {
|
||||||
|
emailContentTextView.string = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var attachmentFileUrl: URL? {
|
||||||
|
didSet {
|
||||||
|
if let url = attachmentFileUrl {
|
||||||
|
fileURLs?.append(url)
|
||||||
|
fileDropZoneView.setFile(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBOutlet weak var emailRecipientTF: NSTextField!
|
||||||
|
@IBOutlet weak var rememberEmailButton: NSButton!
|
||||||
|
@IBOutlet weak var emailTitleTF: NSTextField!
|
||||||
|
@IBOutlet weak var emailSendButton: NSButton!
|
||||||
|
@IBOutlet weak var emailContentTextView: NSTextView!
|
||||||
|
@IBOutlet weak var emialContentView: NSScrollView!
|
||||||
|
@IBOutlet weak var multipleFilesButton: NSButton!
|
||||||
|
@IBOutlet weak var selectFilesButton: NSButton!
|
||||||
|
@IBOutlet weak var clearnAllFilesButton: NSButton!
|
||||||
|
@IBOutlet weak var selectFilesView: NSScrollView!
|
||||||
|
@IBOutlet weak var selectilesTextView: NSTextView!
|
||||||
|
|
||||||
|
private var fileDropZoneView = DropZoneView(fileTypes: [], text: "点击或拖拽文件到这里")
|
||||||
|
private var fileURLs: [URL]?
|
||||||
|
// 邮件地址
|
||||||
|
private var emailsString: String {
|
||||||
|
get { string(from: UserDefaults.standard.object(forKey: "EmailToolVC_RememberEmailString")) }
|
||||||
|
set { UserDefaults.standard.setValue(newValue, forKey: "EmailToolVC_RememberEmailString") }
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var settingPopover: NSPopover = {
|
||||||
|
let settingPopover = NSPopover()
|
||||||
|
let vc = EmailSettingVC()
|
||||||
|
vc.closeHandle = {
|
||||||
|
settingPopover.close()
|
||||||
|
}
|
||||||
|
settingPopover.contentViewController = vc
|
||||||
|
return settingPopover
|
||||||
|
}()
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func setupUI() {
|
||||||
|
|
||||||
|
if emailsString.count > 0 {
|
||||||
|
emailRecipientTF.stringValue = emailsString
|
||||||
|
rememberEmailButton.state = .on
|
||||||
|
} else {
|
||||||
|
rememberEmailButton.state = .off
|
||||||
|
}
|
||||||
|
|
||||||
|
fileDropZoneView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
fileDropZoneView.delegate = self
|
||||||
|
view.addSubview(fileDropZoneView)
|
||||||
|
fileDropZoneView.snp.makeConstraints { (make) in
|
||||||
|
make.top.equalTo(multipleFilesButton.snp.bottom).offset(15)
|
||||||
|
make.left.equalToSuperview().offset(20)
|
||||||
|
make.right.equalToSuperview().offset(-20)
|
||||||
|
make.bottom.equalToSuperview().offset(-30)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func validEmail() -> [String]? {
|
||||||
|
|
||||||
|
let recipient = emailRecipientTF.stringValue
|
||||||
|
guard recipient.count > 0 else {
|
||||||
|
APHUD.hide(message: "收件人邮箱不能为空!", view: self.view, delayTime: 2)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let allEmails = recipient.components(separatedBy: [";", ";", ",", ","]).filter({!$0.isEmpty})
|
||||||
|
let emails = allEmails.filter({ isEmailValid($0) })
|
||||||
|
if emails.isEmpty {
|
||||||
|
APHUD.hide(message: "收件人邮箱格式不正确!", view: self.view, delayTime: 2)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return emails
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedEmailSettingButton(_ sender: NSButton) {
|
||||||
|
|
||||||
|
if settingPopover.isShown {
|
||||||
|
settingPopover.performClose(self)
|
||||||
|
}else {
|
||||||
|
settingPopover.show(relativeTo: sender.bounds, of: sender, preferredEdge: NSRectEdge.minY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@IBAction func sendEmailButton(_ sender: NSButton) {
|
||||||
|
guard let config = eamilConfigs else {
|
||||||
|
APHUD.hide(message: "请先设置邮箱服务器信息!", view: self.view, delayTime: 2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收件人
|
||||||
|
guard let emails = validEmail() else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sender.isEnabled = false
|
||||||
|
|
||||||
|
APHUD.show(message: "发送中...", view: self.view)
|
||||||
|
|
||||||
|
var title = emailTitleTF.stringValue
|
||||||
|
if title.isEmpty {
|
||||||
|
title = "邮件助手"
|
||||||
|
}
|
||||||
|
|
||||||
|
let contents = "<p style='white-space:pre-wrap;'>\(emailContentTextView.textStorage?.string ?? "")</p>".replacingOccurrences(of: "\n", with: "<br>")
|
||||||
|
|
||||||
|
var files = [String]()
|
||||||
|
fileURLs?.forEach({ url in
|
||||||
|
files.append(url.path)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 发送邮件
|
||||||
|
EmailUtils.autoSendAtts(subject: "AppleParty — \(title)", recipients: emails, htmlContent: contents, attachmentFiles: files, config: config) { error in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
sender.isEnabled = true
|
||||||
|
APHUD.hide()
|
||||||
|
debugPrint(error as Any)
|
||||||
|
var msg = "邮箱发送成功~"
|
||||||
|
if let err = error {
|
||||||
|
msg = "邮箱发送失败:\(String(describing: err))"
|
||||||
|
}
|
||||||
|
NSAlert.show(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func rememberEmail(_ sender: Any) {
|
||||||
|
// 收件人
|
||||||
|
guard validEmail() != nil else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
emailsString = emailRecipientTF.stringValue
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func ChangeMultipleFiles(_ sender: NSButton) {
|
||||||
|
|
||||||
|
clearnAllFiles(clearnAllFilesButton)
|
||||||
|
|
||||||
|
if sender.state == .on {
|
||||||
|
fileDropZoneView.isHidden = true
|
||||||
|
selectFilesButton.isHidden = false
|
||||||
|
clearnAllFilesButton.isHidden = false
|
||||||
|
selectFilesView.isHidden = false
|
||||||
|
|
||||||
|
} else {
|
||||||
|
fileDropZoneView.reset()
|
||||||
|
fileDropZoneView.isHidden = false
|
||||||
|
selectFilesButton.isHidden = true
|
||||||
|
clearnAllFilesButton.isHidden = true
|
||||||
|
selectFilesView.isHidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clearnAllFiles(_ sender: NSButton) {
|
||||||
|
fileURLs = []
|
||||||
|
selectilesTextView.string = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func selectFiles(_ sender: Any) {
|
||||||
|
let openPanel = NSOpenPanel()
|
||||||
|
openPanel.canChooseFiles = true
|
||||||
|
openPanel.canChooseDirectories = false
|
||||||
|
openPanel.allowsMultipleSelection = true
|
||||||
|
|
||||||
|
openPanel.beginSheetModal(for: self.view.window!) { [self] (modalResponse) in
|
||||||
|
if modalResponse == .OK {
|
||||||
|
self.fileURLs?.append(contentsOf: openPanel.urls)
|
||||||
|
self.updateFilesView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateFilesView() {
|
||||||
|
fileURLs?.forEach({ file in
|
||||||
|
let path = file.lastPathComponent
|
||||||
|
selectilesTextView.string.append(path + "\n")
|
||||||
|
})
|
||||||
|
|
||||||
|
selectilesTextView.scrollRangeToVisible(NSMakeRange(selectilesTextView.string.count, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - DropZoneViewDelegate
|
||||||
|
extension EmailToolVC: DropZoneViewDelegate {
|
||||||
|
|
||||||
|
func receivedFile(dropZoneView: DropZoneView, fileURL: URL) {
|
||||||
|
fileURLs = [fileURL]
|
||||||
|
}
|
||||||
|
|
||||||
|
func receivedMouseDown(dropZoneView: DropZoneView, theEvent: NSEvent) {
|
||||||
|
let openPanel = NSOpenPanel()
|
||||||
|
openPanel.canChooseFiles = true
|
||||||
|
openPanel.canChooseDirectories = false
|
||||||
|
openPanel.allowsMultipleSelection = false
|
||||||
|
|
||||||
|
openPanel.beginSheetModal(for: self.view.window!) { (modalResponse) in
|
||||||
|
if modalResponse == .OK {
|
||||||
|
if let fileURL = openPanel.url {
|
||||||
|
self.fileURLs = [fileURL]
|
||||||
|
dropZoneView.setFile(fileURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
174
AppleParty/AppleParty/IPAUpload/APIPAUploadVC.swift
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
//
|
||||||
|
// APIPAUploadVC.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/5/12.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class APIPAUploadVC: NSViewController {
|
||||||
|
|
||||||
|
@IBOutlet weak var appIdTextView: NSTextField!
|
||||||
|
@IBOutlet weak var appIdTextField: NSTextField!
|
||||||
|
@IBOutlet weak var spasswordLbl: NSTextField!
|
||||||
|
@IBOutlet weak var submitBtn: NSButton!
|
||||||
|
|
||||||
|
//通过外界传入的 apple id时,不需要用户填写
|
||||||
|
var apple_id: String? {
|
||||||
|
didSet {
|
||||||
|
if let appId = apple_id {
|
||||||
|
appIdTextView.stringValue = appId
|
||||||
|
appIdTextView.isHidden = false
|
||||||
|
appIdTextField.isHidden = true
|
||||||
|
} else {
|
||||||
|
appIdTextView.stringValue = ""
|
||||||
|
appIdTextView.isHidden = true
|
||||||
|
appIdTextField.isHidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var ipaFileURL: URL?
|
||||||
|
private var fileDropZoneView = DropZoneView(fileTypes: [".ipa"], text: "点击或拖拽IPA到这里")
|
||||||
|
private var uploadModel = XMLModel()
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
setupUI()
|
||||||
|
updateSPasswordUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupUI() {
|
||||||
|
|
||||||
|
fileDropZoneView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
fileDropZoneView.delegate = self
|
||||||
|
view.addSubview(fileDropZoneView)
|
||||||
|
fileDropZoneView.snp.makeConstraints { (make) in
|
||||||
|
make.top.equalTo(submitBtn.snp.bottom).offset(15)
|
||||||
|
make.left.equalToSuperview().offset(20)
|
||||||
|
make.right.equalToSuperview().offset(-20)
|
||||||
|
make.bottom.equalToSuperview().offset(-30)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateSPasswordUI() {
|
||||||
|
if let sp = UserCenter.shared.currentSPassword {
|
||||||
|
spasswordLbl.stringValue = "(当前选择:\(sp.account))"
|
||||||
|
} else {
|
||||||
|
spasswordLbl.stringValue = "(错误:当前未指定专用密码!)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedSPasswordBtn(_ sender: NSButton) {
|
||||||
|
let vc = APSPasswordSettingVC()
|
||||||
|
vc.updateCompletion = { [weak self] ps in
|
||||||
|
self?.updateSPasswordUI()
|
||||||
|
}
|
||||||
|
presentAsSheet(vc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedSubmitBtn(_ sender: NSButton) {
|
||||||
|
uploadIpaFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private Method
|
||||||
|
extension APIPAUploadVC {
|
||||||
|
|
||||||
|
private func uploadIpaFile() {
|
||||||
|
|
||||||
|
var appId = appIdTextField.stringValue
|
||||||
|
if let appleId = apple_id {
|
||||||
|
appId = appleId
|
||||||
|
}
|
||||||
|
guard !appId.isEmpty else {
|
||||||
|
APHUD.hide(message: "请先填写 app id ~", delayTime: 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let sp = UserCenter.shared.currentSPassword else {
|
||||||
|
let vc = APSPasswordSettingVC()
|
||||||
|
vc.updateCompletion = { [weak self] spassword in
|
||||||
|
self?.uploadIpaFile()
|
||||||
|
}
|
||||||
|
presentAsSheet(vc)
|
||||||
|
APHUD.hide(message: "请先设置或指定专用密码~", delayTime: 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let ipaFileURL = ipaFileURL else {
|
||||||
|
APHUD.hide(message: "请先上传 ipa 文件~", delayTime: 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
APHUD.show(message: "上传中", view: self.view)
|
||||||
|
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async { [self] in
|
||||||
|
|
||||||
|
uploadModel = XMLModel()
|
||||||
|
uploadModel.apple_id = appId
|
||||||
|
uploadModel.ipa_size = ipaFileURL.fileSize()
|
||||||
|
uploadModel.ipa_md5 = ipaFileURL.fileMD5() ?? ""
|
||||||
|
uploadModel.filePaths = ["ipa.ipa": ipaFileURL.path]
|
||||||
|
|
||||||
|
// 获取创建 itms 文件的路径
|
||||||
|
let filePath = XMLManager.getIpaPath(appId)
|
||||||
|
|
||||||
|
// 先删除旧的文档
|
||||||
|
XMLManager.deleteITMS(filePath)
|
||||||
|
|
||||||
|
uploadModel.createIpaFile(directoryPath: filePath)
|
||||||
|
|
||||||
|
let result = XMLManager.uploadITMS(account: sp.account, pwd: sp.password, filePath: filePath)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
APHUD.hide()
|
||||||
|
self.closeSelfAndCallBack(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func closeSelfAndCallBack(_ result: (Int32, String?)) {
|
||||||
|
if result.0 == 0 {
|
||||||
|
NSAlert.show("ipa文件上传成功!稍后可在苹果后台查看~")
|
||||||
|
}else {
|
||||||
|
let sb = NSStoryboard(name: "APDebugVC", bundle: Bundle(for: self.classForCoder))
|
||||||
|
let newWC = sb.instantiateController(withIdentifier: "APDebugWC") as? NSWindowController
|
||||||
|
let logVC = newWC?.contentViewController as? APDebugVC
|
||||||
|
newWC?.window?.title = "ipa上传错误日志"
|
||||||
|
logVC?.debugLog = result.1 ?? ""
|
||||||
|
newWC?.showWindow(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - DropZoneViewDelegate
|
||||||
|
extension APIPAUploadVC: DropZoneViewDelegate {
|
||||||
|
|
||||||
|
func receivedFile(dropZoneView: DropZoneView, fileURL: URL) {
|
||||||
|
ipaFileURL = fileURL
|
||||||
|
}
|
||||||
|
|
||||||
|
func receivedMouseDown(dropZoneView: DropZoneView, theEvent: NSEvent) {
|
||||||
|
let openPanel = NSOpenPanel()
|
||||||
|
openPanel.canChooseFiles = true
|
||||||
|
openPanel.canChooseDirectories = false
|
||||||
|
openPanel.allowsMultipleSelection = false
|
||||||
|
openPanel.allowedFileTypes = ["ipa"]
|
||||||
|
|
||||||
|
openPanel.beginSheetModal(for: self.view.window!) { (modalResponse) in
|
||||||
|
if modalResponse == .OK {
|
||||||
|
if let fileURL = openPanel.url {
|
||||||
|
self.ipaFileURL = fileURL
|
||||||
|
dropZoneView.setFile(fileURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
159
AppleParty/AppleParty/IPAUpload/IPAUpload.storyboard
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Window Controller-->
|
||||||
|
<scene sceneID="BXd-4M-62l">
|
||||||
|
<objects>
|
||||||
|
<windowController storyboardIdentifier="IPAUpload" id="l7J-hr-7bm" sceneMemberID="viewController">
|
||||||
|
<window key="window" title="包体工具" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="5Sf-fH-lyY">
|
||||||
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
|
<rect key="contentRect" x="425" y="462" width="600" height="450"/>
|
||||||
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
|
||||||
|
<view key="contentView" id="UUK-JK-BBb">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="l7J-hr-7bm" id="Vt0-Yd-g5X"/>
|
||||||
|
</connections>
|
||||||
|
</window>
|
||||||
|
<connections>
|
||||||
|
<segue destination="xPR-tb-wUt" kind="relationship" relationship="window.shadowedContentViewController" id="eDd-Pz-xpA"/>
|
||||||
|
</connections>
|
||||||
|
</windowController>
|
||||||
|
<customObject id="D9C-vY-7M4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-31" y="405"/>
|
||||||
|
</scene>
|
||||||
|
<!--UploadVC-->
|
||||||
|
<scene sceneID="VOc-VL-qdq">
|
||||||
|
<objects>
|
||||||
|
<viewController id="xPR-tb-wUt" customClass="APIPAUploadVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" id="ctU-VA-UqD">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Te0-Hx-wTO">
|
||||||
|
<rect key="frame" x="33" y="406" width="82" height="19"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="19" id="Rgk-aT-Iwk"/>
|
||||||
|
<constraint firstAttribute="width" constant="78" id="qg2-Md-a3M"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="Apple ID:" id="fkc-Rs-IVT">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField verticalHuggingPriority="750" contentType="oneTimeCode" translatesAutoresizingMaskIntoConstraints="NO" id="Lhz-Ap-f59">
|
||||||
|
<rect key="frame" x="123" y="405" width="210" height="21"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="21" id="n3H-wV-TtL"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="0Lr-Jh-f79">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Jq1-9w-hV3">
|
||||||
|
<rect key="frame" x="121" y="405" width="214" height="21"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="21" id="6lr-jv-iUD"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="Label" id="uJY-N7-9LD">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZET-hK-4Dw">
|
||||||
|
<rect key="frame" x="33" y="362" width="82" height="19"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="19" id="IIt-f5-FPd"/>
|
||||||
|
<constraint firstAttribute="width" constant="78" id="u0I-9g-53R"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="专用密码:" id="bhu-jM-Adb">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="F8M-wi-RjZ">
|
||||||
|
<rect key="frame" x="116" y="355" width="92" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="设置" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="pSe-lK-tax">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedSPasswordBtn:" target="xPR-tb-wUt" id="0Uf-7X-evH"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qR2-3g-X5d">
|
||||||
|
<rect key="frame" x="445" y="347" width="142" height="48"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="128" id="PrA-4z-KZy"/>
|
||||||
|
<constraint firstAttribute="height" constant="36" id="xi3-s8-gg0"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="提交上传" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="nTI-pN-mGw">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
DQ
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedSubmitBtn:" target="xPR-tb-wUt" id="xf5-GT-zom"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KEp-13-nh2">
|
||||||
|
<rect key="frame" x="209" y="364" width="237" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="NPB-8q-bdI"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="Label" id="Ecv-9j-HZC">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="qR2-3g-X5d" secondAttribute="trailing" constant="20" symbolic="YES" id="0Uy-Lw-xpg"/>
|
||||||
|
<constraint firstItem="Jq1-9w-hV3" firstAttribute="centerY" secondItem="Te0-Hx-wTO" secondAttribute="centerY" id="6dM-w3-SvM"/>
|
||||||
|
<constraint firstItem="Lhz-Ap-f59" firstAttribute="leading" secondItem="Te0-Hx-wTO" secondAttribute="trailing" constant="10" id="8Ge-q1-Fzi"/>
|
||||||
|
<constraint firstItem="Jq1-9w-hV3" firstAttribute="width" secondItem="ctU-VA-UqD" secondAttribute="width" multiplier="0.35" id="FJr-Ge-3pT"/>
|
||||||
|
<constraint firstItem="Jq1-9w-hV3" firstAttribute="leading" secondItem="Te0-Hx-wTO" secondAttribute="trailing" constant="10" id="I4f-um-gMO"/>
|
||||||
|
<constraint firstItem="F8M-wi-RjZ" firstAttribute="centerY" secondItem="ZET-hK-4Dw" secondAttribute="centerY" id="ISv-kZ-IpT"/>
|
||||||
|
<constraint firstItem="F8M-wi-RjZ" firstAttribute="leading" secondItem="ZET-hK-4Dw" secondAttribute="trailing" constant="10" id="KHu-g2-Cm7"/>
|
||||||
|
<constraint firstItem="qR2-3g-X5d" firstAttribute="centerY" secondItem="F8M-wi-RjZ" secondAttribute="centerY" id="Nje-9G-sfa"/>
|
||||||
|
<constraint firstItem="Lhz-Ap-f59" firstAttribute="centerY" secondItem="Te0-Hx-wTO" secondAttribute="centerY" id="dKg-Mn-cCx"/>
|
||||||
|
<constraint firstItem="qR2-3g-X5d" firstAttribute="leading" secondItem="KEp-13-nh2" secondAttribute="trailing" constant="8" symbolic="YES" id="emp-Rf-Ffv"/>
|
||||||
|
<constraint firstItem="Te0-Hx-wTO" firstAttribute="top" secondItem="ctU-VA-UqD" secondAttribute="top" constant="25" id="fW0-CL-6J3"/>
|
||||||
|
<constraint firstItem="F8M-wi-RjZ" firstAttribute="width" secondItem="ZET-hK-4Dw" secondAttribute="width" id="kKn-eJ-49J"/>
|
||||||
|
<constraint firstItem="Lhz-Ap-f59" firstAttribute="width" secondItem="ctU-VA-UqD" secondAttribute="width" multiplier="0.35" id="kUG-Gm-30o"/>
|
||||||
|
<constraint firstItem="KEp-13-nh2" firstAttribute="centerY" secondItem="F8M-wi-RjZ" secondAttribute="centerY" id="rtO-eP-MzZ"/>
|
||||||
|
<constraint firstItem="ZET-hK-4Dw" firstAttribute="leading" secondItem="ctU-VA-UqD" secondAttribute="leading" constant="35" id="uFt-Iq-dPR"/>
|
||||||
|
<constraint firstItem="ZET-hK-4Dw" firstAttribute="top" secondItem="Te0-Hx-wTO" secondAttribute="bottom" constant="25" id="uXz-9Y-XME"/>
|
||||||
|
<constraint firstItem="Te0-Hx-wTO" firstAttribute="leading" secondItem="ctU-VA-UqD" secondAttribute="leading" constant="35" id="zPb-Pz-oFy"/>
|
||||||
|
<constraint firstItem="KEp-13-nh2" firstAttribute="leading" secondItem="F8M-wi-RjZ" secondAttribute="trailing" constant="10" id="zpc-3A-CaC"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="appIdTextField" destination="Lhz-Ap-f59" id="hfK-xk-cJS"/>
|
||||||
|
<outlet property="appIdTextView" destination="Jq1-9w-hV3" id="SFH-1T-gQ9"/>
|
||||||
|
<outlet property="spasswordLbl" destination="KEp-13-nh2" id="m2o-KO-BYO"/>
|
||||||
|
<outlet property="submitBtn" destination="qR2-3g-X5d" id="bA7-h8-oEq"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="NBS-xt-Oic" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="664" y="405"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
||||||
15
AppleParty/AppleParty/Info.plist
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>SUFeedURL</key>
|
||||||
|
<string>https://raw.githubusercontent.com/37iOS/AppleParty/main/AppleParty/SparkleUpdate/update.xml</string>
|
||||||
|
<key>SUPublicEDKey</key>
|
||||||
|
<string>ItVUtr4L9w9VfMGlzg7+cIcvSkruiygDcarlq8PTF7I=</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
199
AppleParty/AppleParty/LoginView/APLogin2FAVC.swift
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
//
|
||||||
|
// APLogin2FAVC.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/3/17.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class APLogin2FAVC: NSViewController {
|
||||||
|
|
||||||
|
public var cancelHandle: (() -> Void)?
|
||||||
|
public var successHandle: (() -> Void)?
|
||||||
|
|
||||||
|
@IBOutlet weak var phoneListBtn: NSPopUpButton!
|
||||||
|
@IBOutlet weak var sendCodeBtn: NSButton!
|
||||||
|
@IBOutlet weak var voiceCodeBtn: NSButton!
|
||||||
|
@IBOutlet weak var phoneCodeView: NSTextField!
|
||||||
|
@IBOutlet weak var tipsWarningView: NSTextField!
|
||||||
|
@IBOutlet weak var trusDeviceBtn: NSButton!
|
||||||
|
@IBOutlet weak var indicatorView: NSProgressIndicator!
|
||||||
|
@IBOutlet weak var verifyBtn: NSButton!
|
||||||
|
|
||||||
|
private var numbers: [PNumber] = [] //验证手机号码列表
|
||||||
|
private var isPhoneSecurity = false //是否通过手机验证码来验证
|
||||||
|
// 验证码倒计时
|
||||||
|
private var verifyCodeTimer: Timer?
|
||||||
|
private var lastTime: Int = 30
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
phoneCodeView.delegate = self
|
||||||
|
fetchPhoneList()
|
||||||
|
trusDeviceBtn.state = InfoCenter.shared.trusDevice ? .on : .off
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedCancelBtn(_ sender: NSButton) {
|
||||||
|
closeView()
|
||||||
|
cancelHandle?()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedSendCodeBtn(_ sender: NSButton) {
|
||||||
|
submitSecurityCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@IBAction func changeVoiceCodeBtn(_ sender: NSButton) {
|
||||||
|
sendCodeBtn.title = sender.state == .on ? "拨打语音验证码" : "发送短信验证码"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@IBAction func clickedVerifyBtn(_ sender: NSButton) {
|
||||||
|
verifySecurityCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedTrusDeviceBtn(_ sender: NSButton) {
|
||||||
|
InfoCenter.shared.trusDevice = sender.state == .on ? true : false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - 网络请求
|
||||||
|
extension APLogin2FAVC {
|
||||||
|
|
||||||
|
func fetchPhoneList() {
|
||||||
|
APClient.verifySecurityPhone(mode: "sms", phoneid: 0).request(showLoading: true) { [weak self] result, response, error in
|
||||||
|
if let err = error, let type = APClientErrorCode(rawValue: err.code) {
|
||||||
|
switch type {
|
||||||
|
case .privacyAcknowledgementRequired:
|
||||||
|
// 传了无效phoneid,进入选择手机号的流程
|
||||||
|
self?.phoneListBtn.removeAllItems()
|
||||||
|
let model = PhoneNumbers(body: result)
|
||||||
|
self?.numbers = model.numbers
|
||||||
|
for number in model.numbers {
|
||||||
|
self?.phoneListBtn.addItem(withTitle: number.num)
|
||||||
|
}
|
||||||
|
self?.phoneListBtn.selectItem(at: 0)
|
||||||
|
self?.showTips("一条包含验证码的信息已发送至您的设备。可输入设备验证码后点击验证以继续。\n或者点击“发送短信验证码”获取短信验证码。")
|
||||||
|
default:
|
||||||
|
APHUD.hide(message: err.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func submitSecurityCode() {
|
||||||
|
isPhoneSecurity = true
|
||||||
|
let mode = voiceCodeBtn.state == .on ? "voice" : "sms"
|
||||||
|
let phoneId = numbers[phoneListBtn.indexOfSelectedItem].id
|
||||||
|
APClient.verifySecurityPhone(mode: mode, phoneid: phoneId).request(showLoading: true) { [weak self] result, response, error in
|
||||||
|
let code = response?.statusCode
|
||||||
|
if [200, 423].contains(code) {
|
||||||
|
let msg = self?.voiceCodeBtn.state == .on ? "请求拨打语音电话,请收听~" : "验证码已发送,请查收~"
|
||||||
|
self?.showTips(msg)
|
||||||
|
self?.verifyCodeCountdown()
|
||||||
|
} else {
|
||||||
|
self?.showTips("\(code ?? 0),\(error.debugDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifySecurityCode() {
|
||||||
|
let code = phoneCodeView.stringValue
|
||||||
|
let phoneId = numbers[phoneListBtn.indexOfSelectedItem].id
|
||||||
|
let mode = voiceCodeBtn.state == .on ? "voice" : "sms"
|
||||||
|
let type = isPhoneSecurity ? APClient.SecurityCode.sms(code: code, phoneNumberId: phoneId, mode: mode) : APClient.SecurityCode.device(code: code)
|
||||||
|
|
||||||
|
viewEnabled(false)
|
||||||
|
APClient.submitSecurityCode(code: type).request { [weak self] result, response, error in
|
||||||
|
self?.viewEnabled(true)
|
||||||
|
let code = response?.statusCode
|
||||||
|
switch code {
|
||||||
|
case 200, 201, 202, 203, 204:
|
||||||
|
self?.validateSession()
|
||||||
|
case 400:
|
||||||
|
let errors = dictionaryArray(result["service_errors"])
|
||||||
|
let msg = string(from: errors.first?["message"])
|
||||||
|
self?.showTips(msg)
|
||||||
|
default:
|
||||||
|
self?.showTips("\(code ?? 0),\(error.debugDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateSession() {
|
||||||
|
viewEnabled(false)
|
||||||
|
APClient.signInSession.request { [weak self] result, response, error in
|
||||||
|
self?.viewEnabled(true)
|
||||||
|
let code = response?.statusCode
|
||||||
|
switch code {
|
||||||
|
case 200, 201:
|
||||||
|
UserCenter.shared.isAuthorized = true
|
||||||
|
self?.successHandle?()
|
||||||
|
self?.closeView()
|
||||||
|
default:
|
||||||
|
let errors = dictionaryArray(result["serviceErrors"])
|
||||||
|
let msg = string(from: errors.first?["message"])
|
||||||
|
self?.showTips(msg.isEmpty ? error.debugDescription : msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 内部方法
|
||||||
|
extension APLogin2FAVC {
|
||||||
|
|
||||||
|
func closeView() {
|
||||||
|
guard let window = view.window, let parent = window.sheetParent
|
||||||
|
else { return }
|
||||||
|
parent.endSheet(window)
|
||||||
|
}
|
||||||
|
|
||||||
|
func showTips(_ text: String) {
|
||||||
|
if text.isEmpty {
|
||||||
|
tipsWarningView.isHidden = true
|
||||||
|
tipsWarningView.stringValue = ""
|
||||||
|
} else {
|
||||||
|
tipsWarningView.stringValue = text
|
||||||
|
tipsWarningView.isHidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func viewEnabled(_ isEnabled: Bool) {
|
||||||
|
showTips("")
|
||||||
|
verifyBtn.isEnabled = isEnabled
|
||||||
|
isEnabled ? indicatorView.stopAnimation(nil) : indicatorView.startAnimation(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyCodeCountdown() {
|
||||||
|
self.lastTime = 30
|
||||||
|
self.sendCodeBtn.title = "\(self.lastTime)s 后重试"
|
||||||
|
self.sendCodeBtn.isEnabled = false
|
||||||
|
self.verifyCodeTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.verifyCodeTime), userInfo: nil, repeats: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证码倒计时
|
||||||
|
@objc func verifyCodeTime() {
|
||||||
|
lastTime -= 1
|
||||||
|
sendCodeBtn.title = "\(self.lastTime)s 后重试"
|
||||||
|
if lastTime <= 0 {
|
||||||
|
sendCodeBtn.title = "重新发送验证码"
|
||||||
|
sendCodeBtn.isEnabled = true
|
||||||
|
verifyCodeTimer?.invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - NSTextFieldDelegate
|
||||||
|
extension APLogin2FAVC: NSTextFieldDelegate {
|
||||||
|
|
||||||
|
func controlTextDidChange(_ obj: Notification) {
|
||||||
|
if phoneCodeView.stringValue.count == 6 {
|
||||||
|
verifyBtn.isEnabled = true
|
||||||
|
} else {
|
||||||
|
verifyBtn.isEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
201
AppleParty/AppleParty/LoginView/APLogin2FAVC.xib
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="APLogin2FAVC" customModule="AppleParty" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="indicatorView" destination="rsC-Tg-KKs" id="OOV-8V-cF0"/>
|
||||||
|
<outlet property="phoneCodeView" destination="npy-jX-GQb" id="aXK-hj-Jg3"/>
|
||||||
|
<outlet property="phoneListBtn" destination="YPd-lO-U4e" id="Sg5-Hb-iUb"/>
|
||||||
|
<outlet property="sendCodeBtn" destination="rx9-Oh-Gtq" id="cYT-hl-Kc7"/>
|
||||||
|
<outlet property="tipsWarningView" destination="ijo-CH-sfg" id="PLO-uS-eBh"/>
|
||||||
|
<outlet property="trusDeviceBtn" destination="8SV-WX-fvn" id="kyK-aJ-vJK"/>
|
||||||
|
<outlet property="verifyBtn" destination="E5o-kz-kvG" id="kcn-Ez-LwY"/>
|
||||||
|
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
|
||||||
|
<outlet property="voiceCodeBtn" destination="kff-Qr-scI" id="khK-sS-hCc"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<customView id="Hz6-mo-xeY">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="511" height="352"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BiF-4T-JA0">
|
||||||
|
<rect key="frame" x="53" y="8" width="94" height="37"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="43L-Br-dDy"/>
|
||||||
|
<constraint firstAttribute="width" constant="80" id="OjC-db-Fqd"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="取消" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="IIa-Xh-1A4">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
Gw
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedCancelBtn:" target="-2" id="KIQ-L6-769"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="E5o-kz-kvG">
|
||||||
|
<rect key="frame" x="364" y="8" width="94" height="37"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="8Om-Qw-Vr5"/>
|
||||||
|
<constraint firstAttribute="width" constant="80" id="vyG-oy-cDO"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="验证" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" inset="2" id="HKe-2K-33K">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
DQ
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedVerifyBtn:" target="-2" id="9hq-Pv-iKB"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<progressIndicator maxValue="100" displayedWhenStopped="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="rsC-Tg-KKs">
|
||||||
|
<rect key="frame" x="427" y="20" width="16" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="16" id="P2w-80-9Ta"/>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="Qu4-Tu-Jfl"/>
|
||||||
|
<constraint firstAttribute="width" constant="16" id="WQE-Yc-Xrv"/>
|
||||||
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="16" id="dQk-O7-QHd"/>
|
||||||
|
</constraints>
|
||||||
|
</progressIndicator>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BC7-jj-els">
|
||||||
|
<rect key="frame" x="28" y="307" width="455" height="25"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="25" id="gUT-eG-Utf"/>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="s9O-0v-3Ni"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="双重认证" id="FQq-Av-0eD">
|
||||||
|
<font key="font" metaFont="system" size="18"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YPd-lO-U4e">
|
||||||
|
<rect key="frame" x="187" y="248" width="268" height="30"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="yKX-Nl-Hdr"/>
|
||||||
|
</constraints>
|
||||||
|
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" arrowPosition="arrowAtCenter" id="vpb-by-gTu">
|
||||||
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="menu"/>
|
||||||
|
<menu key="menu" id="msT-eh-QNY"/>
|
||||||
|
</popUpButtonCell>
|
||||||
|
</popUpButton>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="vqk-uI-9AU">
|
||||||
|
<rect key="frame" x="58" y="257" width="124" height="15"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="15" id="DII-jQ-Tud"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="选择用手机短信接收:" id="Lbc-GQ-7wH">
|
||||||
|
<font key="font" metaFont="cellTitle"/>
|
||||||
|
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="npy-jX-GQb">
|
||||||
|
<rect key="frame" x="60" y="127" width="391" height="35"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="35" id="5C0-ZB-9O0"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" focusRingType="none" alignment="center" placeholderString="请输入验证码" bezelStyle="round" id="Ukz-vL-Zpn">
|
||||||
|
<font key="font" metaFont="systemBold" size="20"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rx9-Oh-Gtq">
|
||||||
|
<rect key="frame" x="324" y="210" width="134" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="120" id="D0A-gg-DAF"/>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="bE0-bG-WSX"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="发送短信验证码" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="54B-tN-4ys">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedSendCodeBtn:" target="-2" id="UE7-hg-Lnj"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kff-Qr-scI">
|
||||||
|
<rect key="frame" x="344" y="186" width="107" height="22"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="105" id="MxK-Vd-EtE"/>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="OqC-X2-qa4"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="check" title="拨打语音来电" bezelStyle="regularSquare" imagePosition="left" inset="2" id="gVy-us-tZh">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="changeVoiceCodeBtn:" target="-2" id="8jt-HG-YUL"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ijo-CH-sfg">
|
||||||
|
<rect key="frame" x="58" y="101" width="395" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="500" id="53u-f7-HK1"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" truncatesLastVisibleLine="YES" allowsUndo="NO" title="请输入正确的验证码" id="omC-g0-Aha">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="systemOrangeColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8SV-WX-fvn">
|
||||||
|
<rect key="frame" x="283" y="20" width="80" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="mX6-l7-rZj"/>
|
||||||
|
<constraint firstAttribute="width" constant="78" id="vDy-Dk-GSE"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="check" title="信任设备" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="VBA-AP-I7T">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" size="13" name="STSongti-SC-Regular"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedTrusDeviceBtn:" target="-2" id="iIm-cY-7Z6"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="BiF-4T-JA0" secondAttribute="bottom" constant="15" id="1Fq-AL-3Ws"/>
|
||||||
|
<constraint firstItem="E5o-kz-kvG" firstAttribute="centerY" secondItem="BiF-4T-JA0" secondAttribute="centerY" id="612-VZ-uMP"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="YPd-lO-U4e" secondAttribute="trailing" constant="60" id="EJc-Vs-Afo"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="rx9-Oh-Gtq" secondAttribute="trailing" constant="60" id="Euw-SM-wUx"/>
|
||||||
|
<constraint firstItem="YPd-lO-U4e" firstAttribute="leading" secondItem="vqk-uI-9AU" secondAttribute="trailing" constant="10" id="G4u-U5-hRW"/>
|
||||||
|
<constraint firstItem="vqk-uI-9AU" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="Kat-r9-QXI"/>
|
||||||
|
<constraint firstItem="8SV-WX-fvn" firstAttribute="centerY" secondItem="E5o-kz-kvG" secondAttribute="centerY" id="Pfn-8j-tgF"/>
|
||||||
|
<constraint firstItem="E5o-kz-kvG" firstAttribute="leading" secondItem="8SV-WX-fvn" secondAttribute="trailing" constant="8" id="Q3C-bW-QSW"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="kff-Qr-scI" secondAttribute="trailing" constant="60" id="Sxo-QH-rVU"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="npy-jX-GQb" secondAttribute="trailing" constant="60" id="WBf-2q-NVj"/>
|
||||||
|
<constraint firstItem="npy-jX-GQb" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="WxQ-Kb-Jj4"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="BC7-jj-els" secondAttribute="trailing" constant="30" id="cTr-XH-o6b"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="ijo-CH-sfg" secondAttribute="trailing" constant="60" id="ccV-Qd-svA"/>
|
||||||
|
<constraint firstItem="YPd-lO-U4e" firstAttribute="centerY" secondItem="vqk-uI-9AU" secondAttribute="centerY" id="eus-h0-cM0"/>
|
||||||
|
<constraint firstItem="npy-jX-GQb" firstAttribute="top" secondItem="kff-Qr-scI" secondAttribute="bottom" constant="25" id="fDH-b7-v0g"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="E5o-kz-kvG" secondAttribute="trailing" constant="60" id="gdM-HS-dPr"/>
|
||||||
|
<constraint firstItem="rsC-Tg-KKs" firstAttribute="trailing" secondItem="E5o-kz-kvG" secondAttribute="trailing" constant="-8" id="hFD-yR-GoO"/>
|
||||||
|
<constraint firstItem="BiF-4T-JA0" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="nQA-ny-dXA"/>
|
||||||
|
<constraint firstItem="kff-Qr-scI" firstAttribute="top" secondItem="rx9-Oh-Gtq" secondAttribute="bottom" constant="10" id="oaD-ya-2cr"/>
|
||||||
|
<constraint firstItem="ijo-CH-sfg" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="p0O-Ua-B6c"/>
|
||||||
|
<constraint firstItem="vqk-uI-9AU" firstAttribute="top" secondItem="BC7-jj-els" secondAttribute="bottom" constant="35" id="p9I-Me-6fq"/>
|
||||||
|
<constraint firstItem="BC7-jj-els" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="20" symbolic="YES" id="qEA-eh-nav"/>
|
||||||
|
<constraint firstItem="rsC-Tg-KKs" firstAttribute="centerY" secondItem="E5o-kz-kvG" secondAttribute="centerY" id="rlF-J0-c0j"/>
|
||||||
|
<constraint firstItem="ijo-CH-sfg" firstAttribute="top" secondItem="npy-jX-GQb" secondAttribute="bottom" constant="10" id="voh-Sr-6pf"/>
|
||||||
|
<constraint firstItem="rx9-Oh-Gtq" firstAttribute="top" secondItem="YPd-lO-U4e" secondAttribute="bottom" constant="15" id="vvK-es-FLv"/>
|
||||||
|
<constraint firstItem="BC7-jj-els" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="30" id="yL4-AI-CiL"/>
|
||||||
|
</constraints>
|
||||||
|
<point key="canvasLocation" x="154.5" y="194"/>
|
||||||
|
</customView>
|
||||||
|
</objects>
|
||||||
|
</document>
|
||||||
234
AppleParty/AppleParty/LoginView/APLoginVC.swift
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
//
|
||||||
|
// APLoginVC.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/3/17.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class APLoginVC: NSViewController {
|
||||||
|
|
||||||
|
public var cancelHandle: (() -> Void)?
|
||||||
|
public var successHandle: (() -> Void)?
|
||||||
|
|
||||||
|
@IBOutlet weak var accountView: NSTextField!
|
||||||
|
@IBOutlet weak var passwordView: NSSecureTextField!
|
||||||
|
// 历史账号
|
||||||
|
@IBOutlet weak var historyBox: NSBox!
|
||||||
|
@IBOutlet weak var tableView: NSTableView!
|
||||||
|
|
||||||
|
@IBOutlet weak var tipsWarningView: NSTextField!
|
||||||
|
@IBOutlet weak var autoLoginBtn: NSButton!
|
||||||
|
@IBOutlet weak var indicatorView: NSProgressIndicator!
|
||||||
|
@IBOutlet weak var loginBtn: NSButton!
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
accountView.delegate = self
|
||||||
|
passwordView.delegate = self
|
||||||
|
tipsWarningView.maximumNumberOfLines = 5
|
||||||
|
|
||||||
|
// 最近登录的账号
|
||||||
|
let user = UserCenter.shared.loginedUser
|
||||||
|
let name = user.appleid
|
||||||
|
let pwd = user.password
|
||||||
|
guard name.count > 0, pwd.count > 0 else { return }
|
||||||
|
accountView.stringValue = name
|
||||||
|
passwordView.stringValue = pwd
|
||||||
|
viewEnabled(true)
|
||||||
|
|
||||||
|
// 如果需要自动登录
|
||||||
|
if UserCenter.shared.isAutoLogin || UserCenter.shared.isFirstTime {
|
||||||
|
loginAccount()
|
||||||
|
UserCenter.shared.isFirstTime = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedCancelBtn(_ sender: NSButton) {
|
||||||
|
closeView()
|
||||||
|
cancelHandle?()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@IBAction func showAccountHistoryList(_ sender: Any) {
|
||||||
|
if historyBox.isHidden {
|
||||||
|
tableView.delegate = self
|
||||||
|
tableView.dataSource = self
|
||||||
|
tableView.reloadData()
|
||||||
|
}
|
||||||
|
historyBox.isHidden = !historyBox.isHidden
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedLoginBtn(_ sender: NSButton) {
|
||||||
|
loginAccount()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - 网络请求
|
||||||
|
extension APLoginVC {
|
||||||
|
|
||||||
|
func loginAccount() {
|
||||||
|
if accountView.stringValue.isEmpty {
|
||||||
|
showTips("苹果账号不能为空!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if passwordView.stringValue.isEmpty {
|
||||||
|
showTips("密码不能为空!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
viewEnabled(false)
|
||||||
|
|
||||||
|
let account = accountView.stringValue
|
||||||
|
let pwd = passwordView.stringValue
|
||||||
|
|
||||||
|
APClient.signIn(account: account, password: pwd).request { [weak self] result, response, error in
|
||||||
|
self?.viewEnabled(true)
|
||||||
|
if let err = error, let type = APClientErrorCode(rawValue: err.code) {
|
||||||
|
switch type {
|
||||||
|
case .notAuthorized:
|
||||||
|
self?.showTips("Apple ID 或密码不正确")
|
||||||
|
case .twoStepOrFactor:
|
||||||
|
// 保存账号密码
|
||||||
|
if self?.autoLoginBtn.state == .on {
|
||||||
|
UserCenter.shared.isAutoLogin = true
|
||||||
|
UserCenter.shared.loginedUser = User(appleid: account, password: pwd)
|
||||||
|
}
|
||||||
|
// 双重认证
|
||||||
|
let vc = APLogin2FAVC()
|
||||||
|
vc.cancelHandle = { [weak self] in
|
||||||
|
self?.viewEnabled(true)
|
||||||
|
}
|
||||||
|
vc.successHandle = { [weak self] in
|
||||||
|
self?.trusDevice()
|
||||||
|
self?.successHandle?()
|
||||||
|
self?.closeView()
|
||||||
|
}
|
||||||
|
let pannel = NSPanel(contentViewController: vc)
|
||||||
|
pannel.setFrame(NSRect(origin: .zero, size: NSSize(width: 500, height: 360)), display: true)
|
||||||
|
self?.view.window?.beginSheet(pannel, completionHandler: nil)
|
||||||
|
default:
|
||||||
|
self?.showTips(err.localizedDescription)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let code = response?.statusCode
|
||||||
|
// 登陆态有效
|
||||||
|
if code == 200 {
|
||||||
|
// 保存账号密码
|
||||||
|
if self?.autoLoginBtn.state == .on {
|
||||||
|
UserCenter.shared.isAutoLogin = true
|
||||||
|
UserCenter.shared.loginedUser = User(appleid: account, password: pwd)
|
||||||
|
}
|
||||||
|
self?.validateSession()
|
||||||
|
} else {
|
||||||
|
self?.showTips("\(code ?? 0),\(error.debugDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateSession() {
|
||||||
|
viewEnabled(false)
|
||||||
|
APClient.signInSession.request { [weak self] result, response, error in
|
||||||
|
self?.viewEnabled(true)
|
||||||
|
let code = response?.statusCode
|
||||||
|
switch code {
|
||||||
|
case 200, 201:
|
||||||
|
UserCenter.shared.isAuthorized = true
|
||||||
|
self?.trusDevice()
|
||||||
|
self?.successHandle?()
|
||||||
|
self?.closeView()
|
||||||
|
default:
|
||||||
|
let errors = dictionaryArray(result["serviceErrors"])
|
||||||
|
let msg = string(from: errors.first?["message"])
|
||||||
|
self?.showTips(msg.isEmpty ? error.debugDescription : msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func trusDevice() {
|
||||||
|
guard InfoCenter.shared.trusDevice else { return}
|
||||||
|
|
||||||
|
APClient.trusDevice(isTrus: true).request { result, response, error in
|
||||||
|
if response?.statusCode == 204 {
|
||||||
|
debugPrint("信任设备成功~")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - 内部方法
|
||||||
|
extension APLoginVC {
|
||||||
|
|
||||||
|
func showTips(_ text: String) {
|
||||||
|
if text.isEmpty {
|
||||||
|
tipsWarningView.isHidden = true
|
||||||
|
tipsWarningView.stringValue = ""
|
||||||
|
} else {
|
||||||
|
tipsWarningView.stringValue = text
|
||||||
|
tipsWarningView.isHidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func viewEnabled(_ isEnabled: Bool) {
|
||||||
|
showTips("")
|
||||||
|
loginBtn.isEnabled = isEnabled
|
||||||
|
historyBox.isHidden = true
|
||||||
|
isEnabled ? indicatorView.stopAnimation(nil) : indicatorView.startAnimation(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeView() {
|
||||||
|
guard let window = view.window, let parent = window.sheetParent
|
||||||
|
else { return }
|
||||||
|
parent.endSheet(window)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - NSTextFieldDelegate
|
||||||
|
extension APLoginVC: NSTextFieldDelegate {
|
||||||
|
|
||||||
|
func controlTextDidChange(_ obj: Notification) {
|
||||||
|
if !accountView.stringValue.isEmpty && !passwordView.stringValue.isEmpty {
|
||||||
|
loginBtn.isEnabled = true
|
||||||
|
} else {
|
||||||
|
loginBtn.isEnabled = false
|
||||||
|
showTips("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - NSTableViewDelegate
|
||||||
|
extension APLoginVC: NSTableViewDelegate, NSTableViewDataSource {
|
||||||
|
|
||||||
|
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||||
|
return UserCenter.shared.historyUser.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||||
|
if tableColumn?.identifier == NSUserInterfaceItemIdentifier(rawValue: "nameColumn") {
|
||||||
|
let cellIdentifier = NSUserInterfaceItemIdentifier(rawValue: "nameCell")
|
||||||
|
guard let cellView = tableView.makeView(withIdentifier: cellIdentifier, owner: self) as? NSTableCellView else { return nil }
|
||||||
|
cellView.textField?.stringValue = UserCenter.shared.historyUser[row].appleid
|
||||||
|
return cellView
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableViewSelectionDidChange(_ notification: Notification){
|
||||||
|
let tableView = notification.object as! NSTableView
|
||||||
|
let clickedRow = tableView.selectedRow
|
||||||
|
guard clickedRow >= 0 else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tableView.deselectRow(clickedRow)
|
||||||
|
accountView.stringValue = UserCenter.shared.historyUser[clickedRow].appleid
|
||||||
|
passwordView.stringValue = UserCenter.shared.historyUser[clickedRow].password
|
||||||
|
showAccountHistoryList(clickedRow)
|
||||||
|
}
|
||||||
|
}
|
||||||
258
AppleParty/AppleParty/LoginView/APLoginVC.xib
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="APLoginVC" customModule="AppleParty" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="accountView" destination="7Gg-CB-YAd" id="HvI-ne-iPZ"/>
|
||||||
|
<outlet property="autoLoginBtn" destination="aZC-zX-iLA" id="QDv-Kf-saw"/>
|
||||||
|
<outlet property="historyBox" destination="w4L-Kl-k0V" id="4gE-iK-bgm"/>
|
||||||
|
<outlet property="indicatorView" destination="3JL-7f-Xfx" id="ebS-gn-1fo"/>
|
||||||
|
<outlet property="loginBtn" destination="Idm-D5-Bl1" id="V3g-00-OAu"/>
|
||||||
|
<outlet property="passwordView" destination="Jin-lZ-ObE" id="sWU-2K-aBK"/>
|
||||||
|
<outlet property="tableView" destination="Hst-cR-jaZ" id="PWz-Me-Trw"/>
|
||||||
|
<outlet property="tipsWarningView" destination="A1W-oM-xUt" id="O66-oN-G3J"/>
|
||||||
|
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<customView id="Hz6-mo-xeY">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="480" height="301"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sBv-Ga-3Ig">
|
||||||
|
<rect key="frame" x="8" y="256" width="464" height="25"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="dMh-mt-gPl"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="App Store Connect" id="4jB-x9-D95">
|
||||||
|
<font key="font" metaFont="systemBold" size="18"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField focusRingType="none" verticalHuggingPriority="750" textCompletion="NO" contentType="username" translatesAutoresizingMaskIntoConstraints="NO" id="7Gg-CB-YAd">
|
||||||
|
<rect key="frame" x="60" y="176" width="360" height="45"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="45" id="S1v-qX-ZK3"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" focusRingType="none" alignment="left" placeholderString="请输入苹果账号" usesSingleLineMode="YES" bezelStyle="round" id="EKF-vP-tSd">
|
||||||
|
<font key="font" metaFont="system" size="16"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button translatesAutoresizingMaskIntoConstraints="NO" id="oho-cw-blu">
|
||||||
|
<rect key="frame" x="379" y="186" width="26.5" height="26"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="26" id="20z-6h-Nzr"/>
|
||||||
|
<constraint firstAttribute="height" constant="26" id="tTj-x3-L8V"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="NSTouchBarGoDownTemplate" imagePosition="only" alignment="center" state="on" imageScaling="proportionallyUpOrDown" inset="2" id="6R3-hY-fkM">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="showAccountHistoryList:" target="-2" id="KJ0-C0-1ma"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<secureTextField focusRingType="none" verticalHuggingPriority="750" contentType="password" translatesAutoresizingMaskIntoConstraints="NO" id="Jin-lZ-ObE">
|
||||||
|
<rect key="frame" x="60" y="106" width="360" height="45"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="45" id="wdb-sF-BFJ"/>
|
||||||
|
</constraints>
|
||||||
|
<secureTextFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" focusRingType="none" placeholderString="请输入密码" usesSingleLineMode="YES" bezelStyle="round" id="TVN-dn-g47">
|
||||||
|
<font key="font" metaFont="system" size="16"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<allowedInputSourceLocales>
|
||||||
|
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||||
|
</allowedInputSourceLocales>
|
||||||
|
</secureTextFieldCell>
|
||||||
|
</secureTextField>
|
||||||
|
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="A1W-oM-xUt">
|
||||||
|
<rect key="frame" x="58" y="80" width="364" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="500" id="ErD-KS-kJh"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" truncatesLastVisibleLine="YES" allowsUndo="NO" title="请输入正确的苹果账号" id="Los-Ya-nHe">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="systemOrangeColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Idm-D5-Bl1">
|
||||||
|
<rect key="frame" x="333" y="13" width="94" height="37"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="80" id="VNU-X5-3RU"/>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="edt-sA-1o8"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="登陆" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" inset="2" id="ack-oU-iyj">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
DQ
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedLoginBtn:" target="-2" id="4L9-TV-8kK"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<progressIndicator maxValue="100" displayedWhenStopped="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="3JL-7f-Xfx">
|
||||||
|
<rect key="frame" x="396" y="25" width="16" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="Ayq-6g-553"/>
|
||||||
|
<constraint firstAttribute="width" constant="16" id="La3-z9-Sui"/>
|
||||||
|
</constraints>
|
||||||
|
</progressIndicator>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xXW-Zy-0Qc">
|
||||||
|
<rect key="frame" x="53" y="13" width="94" height="37"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="80" id="ADG-NQ-ah3"/>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="J7q-04-O9h"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="取消" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="jWN-Ih-eRv">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
Gw
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedCancelBtn:" target="-2" id="hIR-7f-UuB"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<box hidden="YES" boxType="custom" cornerRadius="3" translatesAutoresizingMaskIntoConstraints="NO" id="w4L-Kl-k0V">
|
||||||
|
<rect key="frame" x="60" y="89" width="360" height="87"/>
|
||||||
|
<view key="contentView" id="h0y-kI-zOZ">
|
||||||
|
<rect key="frame" x="1" y="1" width="358" height="85"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<scrollView autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="57z-B0-8Z4">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="358" height="85"/>
|
||||||
|
<clipView key="contentView" ambiguous="YES" id="cZ6-vs-KNu">
|
||||||
|
<rect key="frame" x="1" y="1" width="356" height="83"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<tableView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" viewBased="YES" id="Hst-cR-jaZ">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="356" height="83"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<size key="intercellSpacing" width="3" height="2"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="gridColor" name="keyboardFocusIndicatorColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<tableColumns>
|
||||||
|
<tableColumn identifier="nameColumn" width="293" minWidth="40" maxWidth="1000" id="Elj-Mz-Pc2">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="7J8-oV-aGc">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="nameCell" id="vOy-rZ-Yvz">
|
||||||
|
<rect key="frame" x="1" y="1" width="302" height="17"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="VBI-EM-0Az">
|
||||||
|
<rect key="frame" x="0.0" y="1" width="302" height="16"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="lel-L2-fmK">
|
||||||
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="VBI-EM-0Az" firstAttribute="centerY" secondItem="vOy-rZ-Yvz" secondAttribute="centerY" id="b5F-uT-Da1"/>
|
||||||
|
<constraint firstItem="VBI-EM-0Az" firstAttribute="centerX" secondItem="vOy-rZ-Yvz" secondAttribute="centerX" id="mCL-MA-lFT"/>
|
||||||
|
<constraint firstItem="VBI-EM-0Az" firstAttribute="leading" secondItem="vOy-rZ-Yvz" secondAttribute="leading" constant="2" id="qWM-YN-llR"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="textField" destination="VBI-EM-0Az" id="yNq-5s-Bj4"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
</tableColumns>
|
||||||
|
</tableView>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</clipView>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="85" id="3Ms-uE-pue"/>
|
||||||
|
</constraints>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="NQa-5q-5Ii">
|
||||||
|
<rect key="frame" x="1" y="118" width="238" height="16"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="JCe-qU-DMF">
|
||||||
|
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="57z-B0-8Z4" secondAttribute="trailing" id="n5T-rg-MeV"/>
|
||||||
|
<constraint firstItem="57z-B0-8Z4" firstAttribute="top" secondItem="h0y-kI-zOZ" secondAttribute="top" id="sUR-ay-LmY"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="57z-B0-8Z4" secondAttribute="bottom" id="su9-ZN-m2r"/>
|
||||||
|
<constraint firstItem="57z-B0-8Z4" firstAttribute="leading" secondItem="h0y-kI-zOZ" secondAttribute="leading" id="w9o-Jf-QKB"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<color key="borderColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="fillColor" name="keyboardFocusIndicatorColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</box>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aZC-zX-iLA">
|
||||||
|
<rect key="frame" x="252" y="24" width="80" height="18"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="L2B-IE-Iw5"/>
|
||||||
|
<constraint firstAttribute="width" constant="78" id="UAX-pP-1Ux"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="check" title="自动登陆" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="1M1-Hc-ZVP">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" size="13" name=".PingFangSC-Regular"/>
|
||||||
|
</buttonCell>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="Idm-D5-Bl1" secondAttribute="bottom" constant="20" symbolic="YES" id="2tD-oH-o3G"/>
|
||||||
|
<constraint firstItem="sBv-Ga-3Ig" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="20" symbolic="YES" id="4dv-jZ-P43"/>
|
||||||
|
<constraint firstItem="A1W-oM-xUt" firstAttribute="top" secondItem="Jin-lZ-ObE" secondAttribute="bottom" constant="10" id="9xE-bu-hFg"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="sBv-Ga-3Ig" secondAttribute="trailing" constant="10" id="AkC-oy-LGi"/>
|
||||||
|
<constraint firstItem="oho-cw-blu" firstAttribute="centerY" secondItem="7Gg-CB-YAd" secondAttribute="centerY" id="AmX-5A-xy6"/>
|
||||||
|
<constraint firstItem="xXW-Zy-0Qc" firstAttribute="leading" secondItem="7Gg-CB-YAd" secondAttribute="leading" id="Bpi-sa-Ze6"/>
|
||||||
|
<constraint firstItem="aZC-zX-iLA" firstAttribute="centerY" secondItem="Idm-D5-Bl1" secondAttribute="centerY" id="CBg-Vo-SEB"/>
|
||||||
|
<constraint firstItem="7Gg-CB-YAd" firstAttribute="top" secondItem="sBv-Ga-3Ig" secondAttribute="bottom" constant="35" id="Jcm-UL-9NZ"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="A1W-oM-xUt" secondAttribute="trailing" constant="60" id="QMh-YH-RXq"/>
|
||||||
|
<constraint firstItem="w4L-Kl-k0V" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="RNm-RF-ypF"/>
|
||||||
|
<constraint firstItem="7Gg-CB-YAd" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="RdU-Dr-MaJ"/>
|
||||||
|
<constraint firstItem="Idm-D5-Bl1" firstAttribute="leading" secondItem="aZC-zX-iLA" secondAttribute="trailing" constant="8" id="SeL-eF-Ry3"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="7Gg-CB-YAd" secondAttribute="trailing" constant="60" id="TSS-Ed-8Xq"/>
|
||||||
|
<constraint firstItem="w4L-Kl-k0V" firstAttribute="top" secondItem="7Gg-CB-YAd" secondAttribute="bottom" id="U5l-fh-Scp"/>
|
||||||
|
<constraint firstItem="xXW-Zy-0Qc" firstAttribute="centerY" secondItem="Idm-D5-Bl1" secondAttribute="centerY" id="UD7-om-oum"/>
|
||||||
|
<constraint firstItem="Idm-D5-Bl1" firstAttribute="trailing" secondItem="7Gg-CB-YAd" secondAttribute="trailing" id="cvS-ic-j7R"/>
|
||||||
|
<constraint firstItem="3JL-7f-Xfx" firstAttribute="centerY" secondItem="Idm-D5-Bl1" secondAttribute="centerY" id="dyq-1d-Hfr"/>
|
||||||
|
<constraint firstItem="A1W-oM-xUt" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="eP5-pI-Qi5"/>
|
||||||
|
<constraint firstItem="oho-cw-blu" firstAttribute="trailing" secondItem="7Gg-CB-YAd" secondAttribute="trailing" constant="-15" id="f7e-fo-miL"/>
|
||||||
|
<constraint firstItem="3JL-7f-Xfx" firstAttribute="trailing" secondItem="Idm-D5-Bl1" secondAttribute="trailing" constant="-8" id="mnS-Xu-Lmx"/>
|
||||||
|
<constraint firstItem="sBv-Ga-3Ig" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="10" id="qrQ-Bw-EaS"/>
|
||||||
|
<constraint firstItem="Jin-lZ-ObE" firstAttribute="top" secondItem="7Gg-CB-YAd" secondAttribute="bottom" constant="25" id="sd5-U5-yuf"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="w4L-Kl-k0V" secondAttribute="trailing" constant="60" id="veQ-tN-yXm"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="Jin-lZ-ObE" secondAttribute="trailing" constant="60" id="zBA-Lg-g3u"/>
|
||||||
|
<constraint firstItem="Jin-lZ-ObE" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="60" id="zzz-se-IG0"/>
|
||||||
|
</constraints>
|
||||||
|
<point key="canvasLocation" x="-202" y="175.5"/>
|
||||||
|
</customView>
|
||||||
|
</objects>
|
||||||
|
<resources>
|
||||||
|
<image name="NSTouchBarGoDownTemplate" width="16" height="9"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
||||||
137
AppleParty/AppleParty/LoginView/APWebLoginVC.swift
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
//
|
||||||
|
// APWebLoginVC.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2024/10/29.
|
||||||
|
// Copyright © 2024 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class APWebLoginVC: NSViewController {
|
||||||
|
|
||||||
|
public var cancelHandle: (() -> Void)?
|
||||||
|
public var successHandle: (() -> Void)?
|
||||||
|
private var webCore: AppleWebLoginCore? = nil
|
||||||
|
|
||||||
|
@IBOutlet weak var loginBtn: NSButton!
|
||||||
|
@IBOutlet weak var cancelBtn: NSButton!
|
||||||
|
@IBOutlet weak var indicatorView: NSProgressIndicator!
|
||||||
|
@IBOutlet weak var tipsWarningView: NSTextField!
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
// Do view setup here.
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedCancelBtn(_ sender: NSButton) {
|
||||||
|
closeView()
|
||||||
|
cancelHandle?()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func clickedLoginBtn(_ sender: NSButton) {
|
||||||
|
validateSession()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是登陆态是否过期
|
||||||
|
func validateSession() {
|
||||||
|
viewEnabled(false)
|
||||||
|
APClient.signInSession.request { [weak self] result, response, error in
|
||||||
|
self?.viewEnabled(true)
|
||||||
|
let code = response?.statusCode
|
||||||
|
switch code {
|
||||||
|
case 200, 201:
|
||||||
|
UserCenter.shared.isAuthorized = true
|
||||||
|
self?.successHandle?()
|
||||||
|
self?.closeView()
|
||||||
|
default:
|
||||||
|
let errors = dictionaryArray(result["serviceErrors"])
|
||||||
|
let msg = string(from: errors.first?["message"])
|
||||||
|
self?.showTips(msg.isEmpty ? error.debugDescription : msg)
|
||||||
|
// 隐藏按钮透视显示
|
||||||
|
self?.cancelBtn.isEnabled = false
|
||||||
|
self?.loginBtn.isEnabled = false
|
||||||
|
self?.loginWithWeb()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loginWithWeb() {
|
||||||
|
let appleWebLoginCore = AppleWebLoginCore()
|
||||||
|
// 将 webView 添加到视图层次结构中
|
||||||
|
self.view.addSubview(appleWebLoginCore.webView)
|
||||||
|
|
||||||
|
let closeButton = NSButton(title: "取消", target: self, action: #selector(closeButtonClicked))
|
||||||
|
closeButton.attributedTitle = NSAttributedString(string: "取消", attributes: [NSAttributedString.Key.foregroundColor: NSColor.gray])
|
||||||
|
closeButton.keyEquivalent = "\u{1B}" // `esc` 快捷键
|
||||||
|
appleWebLoginCore.webView.addSubview(closeButton)
|
||||||
|
|
||||||
|
// 设置 webView 的约束以适应视图
|
||||||
|
appleWebLoginCore.webView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
closeButton.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
appleWebLoginCore.webView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
|
||||||
|
appleWebLoginCore.webView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
|
||||||
|
appleWebLoginCore.webView.topAnchor.constraint(equalTo: self.view.topAnchor),
|
||||||
|
appleWebLoginCore.webView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
|
||||||
|
|
||||||
|
closeButton.trailingAnchor.constraint(equalTo: appleWebLoginCore.webView.trailingAnchor, constant: -10),
|
||||||
|
closeButton.topAnchor.constraint(equalTo: appleWebLoginCore.webView.topAnchor, constant: 10)
|
||||||
|
])
|
||||||
|
|
||||||
|
// 关闭按钮
|
||||||
|
|
||||||
|
|
||||||
|
appleWebLoginCore.installFirstLoadCompleteTrap {
|
||||||
|
// 处理首次加载完成的逻辑
|
||||||
|
print("First load complete")
|
||||||
|
}
|
||||||
|
|
||||||
|
appleWebLoginCore.installCredentialPopulationTrap { token, cookies in
|
||||||
|
// 处理凭据填充的逻辑
|
||||||
|
print("Received cookies: \(cookies)")
|
||||||
|
print("Received token: \(token)")
|
||||||
|
|
||||||
|
if let cks = APClientSession.shared.config.httpCookieStorage?.cookies {
|
||||||
|
for ck in cks {
|
||||||
|
APClientSession.shared.config.httpCookieStorage?.deleteCookie(ck)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for cookie in cookies {
|
||||||
|
APClientSession.shared.config.httpCookieStorage?.setCookie(cookie)
|
||||||
|
}
|
||||||
|
// APClientSession.shared.config.headers.update(name: "Cookie", value: "myacinfo=\(token);")
|
||||||
|
self.validateSession()
|
||||||
|
}
|
||||||
|
self.webCore = appleWebLoginCore
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func closeButtonClicked() {
|
||||||
|
// 处理关闭按钮的点击事件
|
||||||
|
print("关闭按钮被点击")
|
||||||
|
closeView()
|
||||||
|
}
|
||||||
|
|
||||||
|
func viewEnabled(_ isEnabled: Bool) {
|
||||||
|
showTips("")
|
||||||
|
loginBtn.isEnabled = isEnabled
|
||||||
|
isEnabled ? indicatorView.stopAnimation(nil) : indicatorView.startAnimation(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func showTips(_ text: String) {
|
||||||
|
if text.isEmpty {
|
||||||
|
tipsWarningView.isHidden = true
|
||||||
|
tipsWarningView.stringValue = ""
|
||||||
|
} else {
|
||||||
|
tipsWarningView.stringValue = text
|
||||||
|
tipsWarningView.isHidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeView() {
|
||||||
|
guard let window = view.window, let parent = window.sheetParent
|
||||||
|
else { return }
|
||||||
|
parent.endSheet(window)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
109
AppleParty/AppleParty/LoginView/APWebLoginVC.xib
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23094" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23094"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="APWebLoginVC" customModule="AppleParty" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="cancelBtn" destination="qxM-hm-PWT" id="UAd-lY-N0a"/>
|
||||||
|
<outlet property="indicatorView" destination="dtJ-5J-DxP" id="zQo-tM-9d5"/>
|
||||||
|
<outlet property="loginBtn" destination="umT-YS-2Tw" id="oY4-Wq-xzj"/>
|
||||||
|
<outlet property="tipsWarningView" destination="3FV-OQ-nKf" id="NkT-JR-Dnu"/>
|
||||||
|
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<customView id="Hz6-mo-xeY">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="480" height="300"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="1rM-sz-rlk">
|
||||||
|
<rect key="frame" x="8" y="260" width="464" height="25"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="tve-kf-cij"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="App Store Connect" id="VHb-gH-1GO">
|
||||||
|
<font key="font" metaFont="systemBold" size="18"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="umT-YS-2Tw">
|
||||||
|
<rect key="frame" x="193" y="65" width="94" height="37"/>
|
||||||
|
<buttonCell key="cell" type="push" title="登录" bezelStyle="rounded" alignment="center" borderStyle="border" inset="2" id="ReJ-dw-xtp">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
DQ
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="80" id="Ebj-j4-bbZ"/>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="erd-oj-9Nc"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedLoginBtn:" target="-2" id="bzx-ik-z4B"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qxM-hm-PWT">
|
||||||
|
<rect key="frame" x="193" y="28" width="94" height="37"/>
|
||||||
|
<buttonCell key="cell" type="push" title="取消" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Fhv-gb-seN">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
Gw
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="Zo8-yT-7mB"/>
|
||||||
|
<constraint firstAttribute="width" constant="80" id="aWd-Lq-3SZ"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<action selector="clickedCancelBtn:" target="-2" id="Y33-Od-paG"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3FV-OQ-nKf">
|
||||||
|
<rect key="frame" x="13" y="120" width="454" height="80"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="550" id="Fad-vK-Ru4"/>
|
||||||
|
<constraint firstAttribute="height" constant="80" id="tdV-WD-iKH"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" truncatesLastVisibleLine="YES" enabled="NO" allowsUndo="NO" alignment="center" id="i97-j7-mQi">
|
||||||
|
<font key="font" size="14" name=".PingFangUITextSC-Regular"/>
|
||||||
|
<string key="title">提示:
|
||||||
|
登录通过 https://appstoreconnect.apple.com 网页进行授权</string>
|
||||||
|
<color key="textColor" name="systemOrangeColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<progressIndicator maxValue="100" displayedWhenStopped="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="dtJ-5J-DxP">
|
||||||
|
<rect key="frame" x="262" y="77" width="16" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="5Gc-Ad-yiX"/>
|
||||||
|
<constraint firstAttribute="width" constant="16" id="l8L-wm-OxQ"/>
|
||||||
|
</constraints>
|
||||||
|
</progressIndicator>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="dtJ-5J-DxP" firstAttribute="centerY" secondItem="umT-YS-2Tw" secondAttribute="centerY" id="2ql-XU-Oy4"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="qxM-hm-PWT" secondAttribute="bottom" constant="35" id="6oR-TQ-5jE"/>
|
||||||
|
<constraint firstItem="3FV-OQ-nKf" firstAttribute="centerY" secondItem="Hz6-mo-xeY" secondAttribute="centerY" constant="-10" id="Zus-xv-zaI"/>
|
||||||
|
<constraint firstItem="dtJ-5J-DxP" firstAttribute="centerX" secondItem="Hz6-mo-xeY" secondAttribute="centerX" constant="30" id="azK-M6-XaT"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="3FV-OQ-nKf" secondAttribute="trailing" constant="15" id="div-BZ-Amk"/>
|
||||||
|
<constraint firstItem="1rM-sz-rlk" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="10" id="dld-UK-NGQ"/>
|
||||||
|
<constraint firstItem="1rM-sz-rlk" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="15" id="err-Ny-q6f"/>
|
||||||
|
<constraint firstItem="3FV-OQ-nKf" firstAttribute="centerX" secondItem="Hz6-mo-xeY" secondAttribute="centerX" id="f7R-Fn-bmc"/>
|
||||||
|
<constraint firstItem="umT-YS-2Tw" firstAttribute="centerX" secondItem="Hz6-mo-xeY" secondAttribute="centerX" id="fsS-52-w7f"/>
|
||||||
|
<constraint firstItem="3FV-OQ-nKf" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="15" id="fyj-Ld-adi"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="1rM-sz-rlk" secondAttribute="trailing" constant="10" id="g6G-H2-CGw"/>
|
||||||
|
<constraint firstItem="qxM-hm-PWT" firstAttribute="centerX" secondItem="Hz6-mo-xeY" secondAttribute="centerX" id="l1Y-kM-2vx"/>
|
||||||
|
<constraint firstItem="qxM-hm-PWT" firstAttribute="top" secondItem="umT-YS-2Tw" secondAttribute="bottom" constant="12" id="ul4-6v-5hV"/>
|
||||||
|
</constraints>
|
||||||
|
<point key="canvasLocation" x="69" y="154"/>
|
||||||
|
</customView>
|
||||||
|
</objects>
|
||||||
|
</document>
|
||||||
@ -0,0 +1,127 @@
|
|||||||
|
//
|
||||||
|
// AppleWebLoginCore.swift
|
||||||
|
// AppleWebLogin
|
||||||
|
//
|
||||||
|
// Created by 秋星桥 on 2024/10/23.
|
||||||
|
// ref: https://github.com/Lakr233/AppleWebLogin
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
@preconcurrency import WebKit
|
||||||
|
|
||||||
|
//private let loginURL = URL(string: "https://account.apple.com/sign-in")!
|
||||||
|
private let loginURL = URL(string: "https://appstoreconnect.apple.com/login")!
|
||||||
|
|
||||||
|
public class AppleWebLoginCore: NSObject, WKUIDelegate, WKNavigationDelegate {
|
||||||
|
var webView: WKWebView {
|
||||||
|
associatedWebView
|
||||||
|
}
|
||||||
|
|
||||||
|
private let associatedWebView: WKWebView
|
||||||
|
private var dataPopulationTimer: Timer? = nil
|
||||||
|
private var firstLoadComplete = false
|
||||||
|
|
||||||
|
public private(set) var onFirstLoadComplete: (() -> Void)?
|
||||||
|
public var onCredentialPopulation: ((String, [HTTPCookie]) -> Void)?
|
||||||
|
|
||||||
|
override public init() {
|
||||||
|
let contentController = WKUserContentController()
|
||||||
|
let configuration = WKWebViewConfiguration()
|
||||||
|
configuration.defaultWebpagePreferences.allowsContentJavaScript = true
|
||||||
|
configuration.userContentController = contentController
|
||||||
|
configuration.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
|
||||||
|
configuration.websiteDataStore = .nonPersistent()
|
||||||
|
|
||||||
|
associatedWebView = .init(
|
||||||
|
frame: CGRect(x: 0, y: 0, width: 1920, height: 1080),
|
||||||
|
configuration: configuration
|
||||||
|
)
|
||||||
|
associatedWebView.isHidden = true
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
associatedWebView.uiDelegate = self
|
||||||
|
associatedWebView.navigationDelegate = self
|
||||||
|
|
||||||
|
associatedWebView.load(.init(url: loginURL))
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
if associatedWebView.responds(to: Selector(("setInspectable:"))) {
|
||||||
|
associatedWebView.perform(Selector(("setInspectable:")), with: true)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
let dataPopulationTimer = Timer(timeInterval: 1, repeats: true) { [weak self] _ in
|
||||||
|
guard let self else { return }
|
||||||
|
removeUnwantedElements()
|
||||||
|
populateData()
|
||||||
|
}
|
||||||
|
RunLoop.main.add(dataPopulationTimer, forMode: .common)
|
||||||
|
self.dataPopulationTimer = dataPopulationTimer
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
dataPopulationTimer?.invalidate()
|
||||||
|
onCredentialPopulation = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
public func webView(_: WKWebView, didFinish _: WKNavigation!) {
|
||||||
|
guard !firstLoadComplete else { return }
|
||||||
|
defer { firstLoadComplete = true }
|
||||||
|
associatedWebView.isHidden = false
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||||
|
self.onFirstLoadComplete?()
|
||||||
|
self.onFirstLoadComplete = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping @MainActor (WKNavigationActionPolicy, WKWebpagePreferences) -> Void) {
|
||||||
|
// let request = navigationAction.request
|
||||||
|
// if let headers = request.allHTTPHeaderFields {
|
||||||
|
// print(request.url?.absoluteString)
|
||||||
|
// print("headers: \(headers)")
|
||||||
|
// if let scntValue = headers["scnt"] {
|
||||||
|
// print("scnt value: \(scntValue)")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// decisionHandler(.allow, preferences)
|
||||||
|
// }
|
||||||
|
|
||||||
|
public func installFirstLoadCompleteTrap(_ block: @escaping () -> Void) {
|
||||||
|
onFirstLoadComplete = block
|
||||||
|
}
|
||||||
|
|
||||||
|
public func installCredentialPopulationTrap(_ block: @escaping (String, [HTTPCookie]) -> Void) {
|
||||||
|
onCredentialPopulation = block
|
||||||
|
}
|
||||||
|
|
||||||
|
private func removeUnwantedElements() {
|
||||||
|
let removeElements = """
|
||||||
|
Element.prototype.remove = function() {
|
||||||
|
this.parentElement.removeChild(this);
|
||||||
|
}
|
||||||
|
NodeList.prototype.remove = HTMLCollection.prototype.remove = function() {
|
||||||
|
for(var i = this.length - 1; i >= 0; i--) {
|
||||||
|
if(this[i] && this[i].parentElement) {
|
||||||
|
this[i].parentElement.removeChild(this[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.getElementById("header").remove();
|
||||||
|
document.getElementsByClassName('landing__animation').remove();
|
||||||
|
"""
|
||||||
|
associatedWebView.evaluateJavaScript(removeElements) { _, _ in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func populateData() {
|
||||||
|
guard let onCredentialPopulation else { return }
|
||||||
|
associatedWebView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in
|
||||||
|
//print(cookies)
|
||||||
|
for cookie in cookies where cookie.name == "myacinfo" {
|
||||||
|
let value = cookie.value
|
||||||
|
onCredentialPopulation(value, cookies)
|
||||||
|
self.onCredentialPopulation = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
AppleParty/AppleParty/LoginView/PhoneNumbers.swift
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// PhoneNumbers.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by 易承 on 2021/6/2.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct PNumber {
|
||||||
|
var num: String
|
||||||
|
var id: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 双重绑定手机号码
|
||||||
|
struct PhoneNumbers {
|
||||||
|
var numbers: [PNumber]
|
||||||
|
|
||||||
|
init(body: [String: Any]) {
|
||||||
|
numbers = [PNumber]()
|
||||||
|
let trustedPhoneNumbers = dictionaryArray(body["trustedPhoneNumbers"])
|
||||||
|
for phone in trustedPhoneNumbers {
|
||||||
|
numbers.append(PNumber(num: string(from: phone["numberWithDialCode"], defaultValue: "未知手机号"),
|
||||||
|
id: int(from: phone["id"]) ?? 0 ))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
271
AppleParty/AppleParty/QRcodeView/APQRcode.storyboard
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="f4w-gm-NjX">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Window Controller-->
|
||||||
|
<scene sceneID="Dz3-j1-bnD">
|
||||||
|
<objects>
|
||||||
|
<customObject id="Wso-0F-bWE" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
<windowController storyboardIdentifier="APQRcode" id="f4w-gm-NjX" sceneMemberID="viewController">
|
||||||
|
<window key="window" title="二维码工具" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="r7n-lN-NFg">
|
||||||
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
|
<rect key="contentRect" x="425" y="462" width="475" height="364"/>
|
||||||
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
|
||||||
|
<view key="contentView" id="EJY-HM-BUX">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="475" height="364"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="f4w-gm-NjX" id="C4p-du-RcV"/>
|
||||||
|
</connections>
|
||||||
|
</window>
|
||||||
|
<connections>
|
||||||
|
<segue destination="JMg-NC-UdZ" kind="relationship" relationship="window.shadowedContentViewController" id="989-Yb-rd2"/>
|
||||||
|
</connections>
|
||||||
|
</windowController>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-344.5" y="156"/>
|
||||||
|
</scene>
|
||||||
|
<!--二维码工具-->
|
||||||
|
<scene sceneID="9pW-9x-UcR">
|
||||||
|
<objects>
|
||||||
|
<viewController title="二维码工具" id="JMg-NC-UdZ" customClass="APQRcodeVC" customModule="AppleParty" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" id="YL6-zb-Bcj">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="698" height="403"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ug2-kb-Ht1">
|
||||||
|
<rect key="frame" x="20" y="341" width="552" height="42"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="42" id="5u9-zd-zBc"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="left" placeholderString="粘贴需要生成二维码的内容" drawsBackground="YES" id="Eeo-iN-PpZ">
|
||||||
|
<font key="font" metaFont="system" size="15"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kXS-QG-QKh">
|
||||||
|
<rect key="frame" x="573" y="345" width="109" height="33"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="95" id="37D-fG-MjE"/>
|
||||||
|
<constraint firstAttribute="height" constant="21" id="H9R-dc-W0a"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="生成二维码" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Tw0-oT-hAN">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
DQ
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="createQrcode:" target="JMg-NC-UdZ" id="eqd-Jm-wdw"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aIK-jb-PcK">
|
||||||
|
<rect key="frame" x="411" y="201" width="214" height="37"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="9OI-Oe-yQA"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="复制二维码" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ucb-TM-gYn">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="copyQrcode:" target="JMg-NC-UdZ" id="m9E-O6-wh4"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<scrollView hidden="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="RMS-8O-hMW">
|
||||||
|
<rect key="frame" x="28" y="16" width="345" height="299"/>
|
||||||
|
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="Rb9-Io-7Mc">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="345" height="299"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="g7E-G6-qr7">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="345" height="299"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<size key="minSize" width="345" height="299"/>
|
||||||
|
<size key="maxSize" width="345" height="10000000"/>
|
||||||
|
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textView>
|
||||||
|
</subviews>
|
||||||
|
</clipView>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Esa-gt-bwB">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="225" height="15"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="6mG-be-32t">
|
||||||
|
<rect key="frame" x="329" y="0.0" width="16" height="299"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="FZk-nU-Hui">
|
||||||
|
<rect key="frame" x="50" y="15" width="301" height="301"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" secondItem="FZk-nU-Hui" secondAttribute="height" multiplier="1:1" id="Gbh-Ch-V3c"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="QRcode" id="gvL-Ja-sIo"/>
|
||||||
|
</imageView>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Fdf-Fu-jC2">
|
||||||
|
<rect key="frame" x="411" y="153" width="214" height="37"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="n0o-Oe-hbP"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="保存二维码" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ixZ-mG-dr1">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="saveQrcode:" target="JMg-NC-UdZ" id="NMx-l9-AQT"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="DO5-oD-0Zz">
|
||||||
|
<rect key="frame" x="411" y="105" width="214" height="37"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="IBz-lN-MrF"/>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="OlD-PH-AMy"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="分享二维码" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="OFP-an-Kde">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="shareQrcode:" target="JMg-NC-UdZ" id="E7u-EL-760"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="bhy-Kt-6FM">
|
||||||
|
<rect key="frame" x="411" y="57" width="214" height="37"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="EK2-O2-r9Z"/>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="LdC-mT-hfw"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="AirDrop分享" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Ss3-R8-8ZV">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="shareQrcodeByAirDrop:" target="JMg-NC-UdZ" id="qck-Qz-CDH"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kqV-kg-e6O">
|
||||||
|
<rect key="frame" x="515" y="252" width="107" height="25"/>
|
||||||
|
<popUpButtonCell key="cell" type="push" title="1024x1024" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="1024" imageScaling="proportionallyDown" inset="2" selectedItem="Ezh-Eq-5N3" id="tDM-tj-Cnk">
|
||||||
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="menu"/>
|
||||||
|
<menu key="menu" title="分辨率" id="iDl-wk-ACt">
|
||||||
|
<items>
|
||||||
|
<menuItem title="300x300" tag="300" id="cd3-Tx-sIl"/>
|
||||||
|
<menuItem title="666x666" tag="680" id="J4k-L2-eI9"/>
|
||||||
|
<menuItem title="1024x1024" state="on" tag="1024" id="Ezh-Eq-5N3"/>
|
||||||
|
<menuItem title="1680x1680" tag="1680" id="yKW-h3-FzF"/>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</popUpButtonCell>
|
||||||
|
</popUpButton>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nkA-C8-cXN">
|
||||||
|
<rect key="frame" x="416" y="258" width="94" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="90" id="H24-4r-oi6"/>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="svz-da-sgJ"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" title="二维码分辨率:" id="lpe-Oy-Zj8">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="oFq-OZ-56q">
|
||||||
|
<rect key="frame" x="418" y="307" width="202" height="16"/>
|
||||||
|
<textFieldCell key="cell" id="M8S-NJ-r4H">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Vpq-0F-Rre">
|
||||||
|
<rect key="frame" x="411" y="9" width="214" height="37"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="DX1-8F-PWN"/>
|
||||||
|
<constraint firstAttribute="height" constant="25" id="RZy-Lp-VQi"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="扫描屏幕上的二维码" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="IE9-17-MVr">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="scanQrcode:" target="JMg-NC-UdZ" id="nKQ-rX-scz"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="RMS-8O-hMW" firstAttribute="height" secondItem="FZk-nU-Hui" secondAttribute="height" multiplier="0.996337" id="0lC-La-Fe2"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="Vpq-0F-Rre" secondAttribute="trailing" constant="80" id="3HB-cl-Bo7"/>
|
||||||
|
<constraint firstItem="kqV-kg-e6O" firstAttribute="leading" secondItem="nkA-C8-cXN" secondAttribute="trailing" constant="10" id="5Hd-0w-UDL"/>
|
||||||
|
<constraint firstItem="aIK-jb-PcK" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="5pi-bF-JY8"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="aIK-jb-PcK" secondAttribute="trailing" constant="80" id="5xg-F6-DUj"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="DO5-oD-0Zz" secondAttribute="trailing" constant="80" id="7vu-Wm-qhQ"/>
|
||||||
|
<constraint firstItem="Ug2-kb-Ht1" firstAttribute="top" secondItem="YL6-zb-Bcj" secondAttribute="top" constant="20" symbolic="YES" id="81I-ze-g3K"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="Fdf-Fu-jC2" secondAttribute="trailing" constant="80" id="8PQ-BB-8bV"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="Vpq-0F-Rre" secondAttribute="trailing" constant="80" id="GiS-So-8mk"/>
|
||||||
|
<constraint firstItem="bhy-Kt-6FM" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="LF9-2I-3uN"/>
|
||||||
|
<constraint firstItem="FZk-nU-Hui" firstAttribute="centerX" secondItem="RMS-8O-hMW" secondAttribute="centerX" id="NMm-m2-f84"/>
|
||||||
|
<constraint firstItem="oFq-OZ-56q" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="69" id="PzN-pg-eqG"/>
|
||||||
|
<constraint firstItem="FZk-nU-Hui" firstAttribute="centerY" secondItem="RMS-8O-hMW" secondAttribute="centerY" id="QxO-Wo-srO"/>
|
||||||
|
<constraint firstItem="Fdf-Fu-jC2" firstAttribute="top" secondItem="aIK-jb-PcK" secondAttribute="bottom" constant="23" id="V0h-t7-6Gj"/>
|
||||||
|
<constraint firstItem="FZk-nU-Hui" firstAttribute="leading" secondItem="YL6-zb-Bcj" secondAttribute="leading" constant="50" id="VDe-Q8-9PN"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="oFq-OZ-56q" secondAttribute="trailing" constant="80" id="XJ6-yE-Ntt"/>
|
||||||
|
<constraint firstItem="Vpq-0F-Rre" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="Y7x-4H-RjY"/>
|
||||||
|
<constraint firstItem="kqV-kg-e6O" firstAttribute="top" secondItem="Ug2-kb-Ht1" secondAttribute="bottom" constant="65" id="YEL-BY-ut6"/>
|
||||||
|
<constraint firstItem="DO5-oD-0Zz" firstAttribute="top" secondItem="Fdf-Fu-jC2" secondAttribute="bottom" constant="23" id="Ypl-LZ-Mq5"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="bhy-Kt-6FM" secondAttribute="trailing" constant="80" id="a0a-Oa-9kH"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="kqV-kg-e6O" secondAttribute="trailing" constant="80" id="dBG-ER-u2w"/>
|
||||||
|
<constraint firstItem="nkA-C8-cXN" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="feM-R3-Qk4"/>
|
||||||
|
<constraint firstItem="oFq-OZ-56q" firstAttribute="top" secondItem="Ug2-kb-Ht1" secondAttribute="bottom" constant="18" id="g4b-8B-jEz"/>
|
||||||
|
<constraint firstItem="Vpq-0F-Rre" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="gBB-bU-bqb"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="kXS-QG-QKh" secondAttribute="trailing" constant="23" id="gz3-mW-f9A"/>
|
||||||
|
<constraint firstItem="kXS-QG-QKh" firstAttribute="centerY" secondItem="Ug2-kb-Ht1" secondAttribute="centerY" id="iya-pl-bDW"/>
|
||||||
|
<constraint firstItem="RMS-8O-hMW" firstAttribute="width" secondItem="FZk-nU-Hui" secondAttribute="width" multiplier="1.14286" id="l0c-4A-01h"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="FZk-nU-Hui" secondAttribute="bottom" constant="15" id="ohm-Zu-1WD"/>
|
||||||
|
<constraint firstItem="Vpq-0F-Rre" firstAttribute="top" secondItem="bhy-Kt-6FM" secondAttribute="bottom" constant="23" id="ooP-O8-GCs"/>
|
||||||
|
<constraint firstItem="bhy-Kt-6FM" firstAttribute="top" secondItem="DO5-oD-0Zz" secondAttribute="bottom" constant="23" id="ovb-Bw-TRf"/>
|
||||||
|
<constraint firstItem="kqV-kg-e6O" firstAttribute="centerY" secondItem="nkA-C8-cXN" secondAttribute="centerY" id="q3U-Le-a73"/>
|
||||||
|
<constraint firstItem="FZk-nU-Hui" firstAttribute="top" secondItem="Ug2-kb-Ht1" secondAttribute="bottom" constant="25" id="qQE-gB-m3H"/>
|
||||||
|
<constraint firstItem="aIK-jb-PcK" firstAttribute="top" secondItem="kqV-kg-e6O" secondAttribute="bottom" constant="23" id="rWH-hA-TD5"/>
|
||||||
|
<constraint firstItem="Fdf-Fu-jC2" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="sSR-g2-znd"/>
|
||||||
|
<constraint firstItem="Vpq-0F-Rre" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="tKR-17-heC"/>
|
||||||
|
<constraint firstItem="Ug2-kb-Ht1" firstAttribute="leading" secondItem="YL6-zb-Bcj" secondAttribute="leading" constant="20" id="tMb-EV-T0G"/>
|
||||||
|
<constraint firstItem="DO5-oD-0Zz" firstAttribute="leading" secondItem="FZk-nU-Hui" secondAttribute="trailing" constant="67" id="wNM-xz-93l"/>
|
||||||
|
<constraint firstItem="kXS-QG-QKh" firstAttribute="leading" secondItem="Ug2-kb-Ht1" secondAttribute="trailing" constant="8" id="xPZ-gg-ekQ"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="Vpq-0F-Rre" secondAttribute="trailing" constant="80" id="zbv-Ik-hNs"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="copyQrcodeBtn" destination="aIK-jb-PcK" id="gof-pb-gv8"/>
|
||||||
|
<outlet property="createQrcodeBtn" destination="kXS-QG-QKh" id="4d9-ha-E2M"/>
|
||||||
|
<outlet property="inputTextField" destination="Ug2-kb-Ht1" id="dIn-HQ-wDi"/>
|
||||||
|
<outlet property="messageLbl" destination="oFq-OZ-56q" id="xKI-yE-tXi"/>
|
||||||
|
<outlet property="qrcodeImageView" destination="FZk-nU-Hui" id="bbk-Km-JV9"/>
|
||||||
|
<outlet property="qrcodeSizeBtn" destination="kqV-kg-e6O" id="Lac-Nf-7aT"/>
|
||||||
|
<outlet property="saveQrcodeBtn" destination="Fdf-Fu-jC2" id="DBR-Bz-crE"/>
|
||||||
|
<outlet property="scanQrcodeBtn" destination="Vpq-0F-Rre" id="Ew1-De-IaS"/>
|
||||||
|
<outlet property="shareQrcodeBtn" destination="DO5-oD-0Zz" id="bvQ-kp-gDU"/>
|
||||||
|
<outlet property="shareQrcodeByAirDropBtn" destination="bhy-Kt-6FM" id="xVg-Aw-9xp"/>
|
||||||
|
<outlet property="textScrollView" destination="RMS-8O-hMW" id="0yW-gv-qtS"/>
|
||||||
|
<outlet property="textView" destination="g7E-G6-qr7" id="vJy-Tg-oLR"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="3RX-1S-TLr" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="372" y="156.5"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
<resources>
|
||||||
|
<image name="QRcode" width="145" height="145"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
||||||
199
AppleParty/AppleParty/QRcodeView/APQRcodeVC.swift
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
//
|
||||||
|
// APQRcodeVC.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/3/24.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class APQRcodeVC: NSViewController {
|
||||||
|
|
||||||
|
@IBOutlet weak var inputTextField: NSTextField!
|
||||||
|
@IBOutlet weak var qrcodeSizeBtn: NSPopUpButton!
|
||||||
|
@IBOutlet weak var createQrcodeBtn: NSButton!
|
||||||
|
@IBOutlet weak var qrcodeImageView: NSImageView!
|
||||||
|
@IBOutlet weak var copyQrcodeBtn: NSButton!
|
||||||
|
@IBOutlet weak var saveQrcodeBtn: NSButton!
|
||||||
|
@IBOutlet weak var shareQrcodeBtn: NSButton!
|
||||||
|
@IBOutlet weak var shareQrcodeByAirDropBtn: NSButton!
|
||||||
|
@IBOutlet weak var scanQrcodeBtn: NSButton!
|
||||||
|
@IBOutlet weak var messageLbl: NSTextField!
|
||||||
|
@IBOutlet weak var textScrollView: NSScrollView!
|
||||||
|
@IBOutlet weak var textView: NSTextView!
|
||||||
|
|
||||||
|
@IBAction func createQrcode(_ sender: Any) {
|
||||||
|
let str = inputTextField!.stringValue
|
||||||
|
if str.isEmpty {
|
||||||
|
enableQrcode(false)
|
||||||
|
statusMessage("")
|
||||||
|
NSAlert.show("请输出需要生成二维码的文本!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let img = createQRImage(str, NSMakeSize(360, 360))
|
||||||
|
qrcodeImageView.image = img
|
||||||
|
enableQrcode(true)
|
||||||
|
statusMessage("二维码生成成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func copyQrcode(_ sender: Any) {
|
||||||
|
let str = inputTextField!.stringValue
|
||||||
|
if !str.isEmpty {
|
||||||
|
let img = createQRImage(str, getImageSize())
|
||||||
|
let pb = NSPasteboard.general
|
||||||
|
pb.clearContents()
|
||||||
|
if pb.writeObjects([img as NSPasteboardWriting]) {
|
||||||
|
statusMessage("Copy QRCode to clipboard")
|
||||||
|
} else {
|
||||||
|
statusMessage("Failed to copy QRCode to clipboard")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@IBAction func saveQrcode(_ sender: Any) {
|
||||||
|
let str = inputTextField!.stringValue
|
||||||
|
if str.isEmpty {
|
||||||
|
return statusMessage("请填写有效的文本内容!")
|
||||||
|
}
|
||||||
|
|
||||||
|
let savePanel = NSSavePanel()
|
||||||
|
savePanel.title = "Save QRCode As File"
|
||||||
|
savePanel.canCreateDirectories = true
|
||||||
|
savePanel.allowedFileTypes = ["png"]
|
||||||
|
savePanel.isExtensionHidden = false
|
||||||
|
savePanel.nameFieldStringValue = getImgaeName() + ".png"
|
||||||
|
savePanel.becomeKey()
|
||||||
|
let result = savePanel.runModal()
|
||||||
|
if (result == .OK && (savePanel.url) != nil) {
|
||||||
|
let img = createQRImage(str, getImageSize())
|
||||||
|
let imgRep = NSBitmapImageRep(data: img.tiffRepresentation!)
|
||||||
|
let data = imgRep?.representation(using: NSBitmapImageRep.FileType.png, properties: [:])
|
||||||
|
try! data?.write(to: savePanel.url!)
|
||||||
|
statusMessage("Save QRCode to \(savePanel.url!.absoluteString)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func shareQrcode(_ sender: Any) {
|
||||||
|
let str = inputTextField!.stringValue
|
||||||
|
if str.isEmpty {
|
||||||
|
return statusMessage("请填写有效的文本内容!")
|
||||||
|
}
|
||||||
|
|
||||||
|
let img = createQRImage(str, getImageSize())
|
||||||
|
let picker = NSSharingServicePicker(items: [img])
|
||||||
|
picker.delegate = self
|
||||||
|
picker.show(relativeTo: .zero, of: sender as! NSView, preferredEdge: .maxX)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func shareQrcodeByAirDrop(_ sender: Any) {
|
||||||
|
let str = inputTextField!.stringValue
|
||||||
|
if str.isEmpty {
|
||||||
|
return statusMessage("请填写有效的文本内容!")
|
||||||
|
}
|
||||||
|
|
||||||
|
let img = createQRImage(str, getImageSize())
|
||||||
|
let service = NSSharingService(named: .sendViaAirDrop)!
|
||||||
|
let items: [NSImage] = [img]
|
||||||
|
if service.canPerform(withItems: items) {
|
||||||
|
service.delegate = self
|
||||||
|
service.perform(withItems: items)
|
||||||
|
} else {
|
||||||
|
statusMessage("Cannot perform AirDrop!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func scanQrcode(_ sender: Any) {
|
||||||
|
enableQrcode(false)
|
||||||
|
qrcodeImageView.isHidden = true
|
||||||
|
textView.string = ""
|
||||||
|
textScrollView.isHidden = false
|
||||||
|
// scan QRCode
|
||||||
|
let dict = scanQRCodeOnScreen() as! [String:Any]
|
||||||
|
let data = dict["qrcode"] as! Array<String>
|
||||||
|
if data.isEmpty {
|
||||||
|
textView.string = "Not found valid QRCode of screen!"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// output message
|
||||||
|
appendToTextView("识别到二维码个数:\(data.count)\n", coreText: "\(data.count)")
|
||||||
|
for (index, element) in data.enumerated() {
|
||||||
|
let k = index + 1
|
||||||
|
appendToTextView("\n第\(k)个二维码内容:\n【\n\(element)\n】\n", coreText: element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear() {
|
||||||
|
super.viewDidAppear()
|
||||||
|
inputTextField.becomeFirstResponder()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupUI() {
|
||||||
|
inputTextField.delegate = self
|
||||||
|
enableQrcode(false)
|
||||||
|
qrcodeImageView.image = NSImage(named: "QRcode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func enableQrcode(_ enable: Bool) {
|
||||||
|
copyQrcodeBtn.isEnabled = enable
|
||||||
|
saveQrcodeBtn.isEnabled = enable
|
||||||
|
shareQrcodeBtn.isEnabled = enable
|
||||||
|
shareQrcodeByAirDropBtn.isEnabled = enable
|
||||||
|
textScrollView.isHidden = true
|
||||||
|
qrcodeImageView.isHidden = false
|
||||||
|
if !enable {
|
||||||
|
statusMessage("")
|
||||||
|
qrcodeImageView.image = NSImage(named: "QRcode")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func statusMessage(_ msg: String) {
|
||||||
|
messageLbl.stringValue = msg.count == 0 ? "" : "提示:\(msg)"
|
||||||
|
}
|
||||||
|
|
||||||
|
func getImageSize() -> NSSize {
|
||||||
|
let wh = CGFloat(qrcodeSizeBtn?.selectedTag() ?? 500)
|
||||||
|
let size = NSMakeSize(wh, wh)
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
func getImgaeName() -> String {
|
||||||
|
let dateFormatter : DateFormatter = DateFormatter()
|
||||||
|
dateFormatter.dateFormat = "HH-mm-ss"
|
||||||
|
let date = Date()
|
||||||
|
let dateString = dateFormatter.string(from: date)
|
||||||
|
return "AppleParty_qrcode-" + dateString
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendToTextView(_ text: String, coreText: String) {
|
||||||
|
let attributes = [NSAttributedString.Key.foregroundColor: NSColor.labelColor]
|
||||||
|
let secondAttributes = [NSAttributedString.Key.foregroundColor: #colorLiteral(red: 0.3211918473, green: 0.7199308276, blue: 1, alpha: 1)]
|
||||||
|
let attr = NSMutableAttributedString.init(string: text, attributes: attributes)
|
||||||
|
attr.addAttributes(secondAttributes, range: (text as NSString).range(of: coreText))
|
||||||
|
textView.textStorage?.append(attr)
|
||||||
|
textView.scrollRangeToVisible(NSMakeRange(textView.string.count, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension APQRcodeVC: NSTextFieldDelegate {
|
||||||
|
func controlTextDidChange(_ obj: Notification) {
|
||||||
|
let textField = obj.object as! NSTextField
|
||||||
|
if textField.stringValue.isEmpty {
|
||||||
|
enableQrcode(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension APQRcodeVC: NSSharingServicePickerDelegate, NSSharingServiceDelegate {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
BIN
AppleParty/AppleParty/Resources/Assets.xcassets/37MobileGames/37M-logo.imageset/37M-logo.png
vendored
Normal file
|
After Width: | Height: | Size: 132 KiB |
21
AppleParty/AppleParty/Resources/Assets.xcassets/37MobileGames/37M-logo.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "37M-logo.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
AppleParty/AppleParty/Resources/Assets.xcassets/37MobileGames/37M-slogan.imageset/37M-slogan.png
vendored
Normal file
|
After Width: | Height: | Size: 80 KiB |
21
AppleParty/AppleParty/Resources/Assets.xcassets/37MobileGames/37M-slogan.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "37M-slogan.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 78 KiB |
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "37iOSTeam-Round.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"size" : "16x16",
|
||||||
|
"filename" : "icon-16.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"size" : "16x16",
|
||||||
|
"filename" : "icon-32.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"size" : "32x32",
|
||||||
|
"filename" : "icon-32.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"size" : "32x32",
|
||||||
|
"filename" : "icon-64.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"size" : "128x128",
|
||||||
|
"filename" : "icon-128.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"size" : "128x128",
|
||||||
|
"filename" : "icon-256.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"size" : "256x256",
|
||||||
|
"filename" : "icon-256.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"size" : "256x256",
|
||||||
|
"filename" : "icon-512.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"size" : "512x512",
|
||||||
|
"filename" : "icon-512.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"size" : "512x512",
|
||||||
|
"filename" : "icon-1024.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
22
AppleParty/AppleParty/Resources/Assets.xcassets/ApplePartyIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "icon-256.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "icon-512.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
AppleParty/AppleParty/Resources/Assets.xcassets/ApplePartyIcon.imageset/icon-256.png
vendored
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
AppleParty/AppleParty/Resources/Assets.xcassets/ApplePartyIcon.imageset/icon-512.png
vendored
Normal file
|
After Width: | Height: | Size: 89 KiB |
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
AppleParty/AppleParty/Resources/Assets.xcassets/RootView/AppAnalytics.imageset/AppAnalytics@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
21
AppleParty/AppleParty/Resources/Assets.xcassets/RootView/AppAnalytics.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "AppAnalytics@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
AppleParty/AppleParty/Resources/Assets.xcassets/RootView/Apps.imageset/Apps@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
21
AppleParty/AppleParty/Resources/Assets.xcassets/RootView/Apps.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Apps@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
21
AppleParty/AppleParty/Resources/Assets.xcassets/RootView/FinancialReports.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "FinancialReports@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 5.0 KiB |
21
AppleParty/AppleParty/Resources/Assets.xcassets/RootView/IPAUpload.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "IPAUpload@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
AppleParty/AppleParty/Resources/Assets.xcassets/RootView/IPAUpload.imageset/IPAUpload@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 20 KiB |
52
AppleParty/AppleParty/Resources/Assets.xcassets/RootView/PlaceholderIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "PlaceholderIcon@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filename" : "PlaceholderIcon_white@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 28 KiB |
21
AppleParty/AppleParty/Resources/Assets.xcassets/RootView/QRcode.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "QRcode@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
AppleParty/AppleParty/Resources/Assets.xcassets/RootView/QRcode.imageset/QRcode@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 12 KiB |
21
AppleParty/AppleParty/Resources/Assets.xcassets/RootView/SendEmail.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "SendEmail@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
AppleParty/AppleParty/Resources/Assets.xcassets/RootView/SendEmail.imageset/SendEmail@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 13 KiB |
21
AppleParty/AppleParty/Resources/Assets.xcassets/RootView/VerifyReceipt.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "VerifyReceipt@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
AppleParty/AppleParty/Resources/Assets.xcassets/RootView/VerifyReceipt.imageset/VerifyReceipt@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
AppleParty/AppleParty/Resources/InAppPurchase/example.xlsx
Normal file
119
AppleParty/AppleParty/Resources/Transporter/iap_metadata.xml
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<package xmlns="http://apple.com/itunes/importer" version="software5.9">
|
||||||
|
<provider>{team_id}</provider>
|
||||||
|
<software>
|
||||||
|
<vendor_id>{SKU}</vendor_id>
|
||||||
|
<software_metadata>
|
||||||
|
<in_app_purchases>
|
||||||
|
<in_app_purchase>
|
||||||
|
<!-- 可以由字母、数字、下划线(_)和句点(.)构成,最多 255 个字符 -->
|
||||||
|
<product_id>com.app.1usd</product_id>
|
||||||
|
<reference_name>1ud(2~64 个字符)</reference_name>
|
||||||
|
<!-- 消耗型项目consumable 非消耗型non-consumable 自动续期订阅auto-renewable 非续期订阅subscription -->
|
||||||
|
<type>consumable</type>
|
||||||
|
<products>
|
||||||
|
<product>
|
||||||
|
<cleared_for_sale>true</cleared_for_sale>
|
||||||
|
<wholesale_price_tier>3</wholesale_price_tier>
|
||||||
|
</product>
|
||||||
|
</products>
|
||||||
|
<locales>
|
||||||
|
<locale name="en-US">
|
||||||
|
<title>2~30个字符</title>
|
||||||
|
<description>至少10~45个字符</description>
|
||||||
|
</locale>
|
||||||
|
<locale name="zh-Hans">
|
||||||
|
<title>中文2~30个字符</title>
|
||||||
|
<description>至少10~45个字符</description>
|
||||||
|
</locale>
|
||||||
|
</locales>
|
||||||
|
<review_screenshot>
|
||||||
|
<!-- Supported dimensions are: 1334x750, 750x1334, 1024x768, 1024x748, 768x1024, 768x1004, 2048x1536, 2048x1496, 1536x2048, 1536x2008, 2224x1668, 1668x2224, 960x640, 960x600, 640x960, 640x920, 2208x1242, 1242x2208, 2436x1125, 1125x2436, 312x390, 1136x640, 1136x600, 640x1136, 640x1096, 2732x2048, 2048x2732" -->
|
||||||
|
<size>636132</size>
|
||||||
|
<file_name>IMG_5180.PNG</file_name>
|
||||||
|
<checksum type="md5">xxxxx</checksum>
|
||||||
|
</review_screenshot>
|
||||||
|
<review_notes>Some notes for the reviewer.(2~4000字符)</review_notes>
|
||||||
|
</in_app_purchase>
|
||||||
|
<in_app_purchase>
|
||||||
|
<!-- 可以由字母、数字、下划线(_)和句点(.)构成,最多 255 个字符 -->
|
||||||
|
<product_id>com.app.3usd</product_id>
|
||||||
|
<reference_name>3ud(2~64 个字符)</reference_name>
|
||||||
|
<type>non-consumable</type>
|
||||||
|
<products>
|
||||||
|
<product>
|
||||||
|
<cleared_for_sale>true</cleared_for_sale>
|
||||||
|
<wholesale_price_tier>3</wholesale_price_tier>
|
||||||
|
</product>
|
||||||
|
</products>
|
||||||
|
<locales>
|
||||||
|
<locale name="ko">
|
||||||
|
<title>2~30个字符</title>
|
||||||
|
<description>至少10~45个字符</description>
|
||||||
|
</locale>
|
||||||
|
</locales>
|
||||||
|
<review_screenshot>
|
||||||
|
<!-- Supported dimensions are: 1334x750, 750x1334, 1024x768, 1024x748, 768x1024, 768x1004, 2048x1536, 2048x1496, 1536x2048, 1536x2008, 2224x1668, 1668x2224, 960x640, 960x600, 640x960, 640x920, 2208x1242, 1242x2208, 2436x1125, 1125x2436, 312x390, 1136x640, 1136x600, 640x1136, 640x1096, 2732x2048, 2048x2732" -->
|
||||||
|
<size>636132</size>
|
||||||
|
<file_name>IMG_5180.PNG</file_name>
|
||||||
|
<checksum type="md5">xxxxx</checksum>
|
||||||
|
</review_screenshot>
|
||||||
|
<review_notes>Some notes for the reviewer.(2~4000字符)</review_notes>
|
||||||
|
</in_app_purchase>
|
||||||
|
<subscription_group name="订阅群组参考名称">
|
||||||
|
<locales>
|
||||||
|
<locale name="en-US">
|
||||||
|
<title>订阅群组显示名称(2-75)</title>
|
||||||
|
<app_name>App 名称显示选项:使用自定义名称(2-30)</app_name>
|
||||||
|
</locale>
|
||||||
|
</locales>
|
||||||
|
<in_app_purchase>
|
||||||
|
<product_id>every_movie_in_the_world_plus_1month</product_id>
|
||||||
|
<reference_name>参考名称</reference_name>
|
||||||
|
<type>auto-renewable</type>
|
||||||
|
<duration>1 Month</duration>
|
||||||
|
<bonus_duration>7 Days</bonus_duration>
|
||||||
|
<cleared_for_sale>true</cleared_for_sale>
|
||||||
|
<locales>
|
||||||
|
<locale name="en-US">
|
||||||
|
<title>订阅显示名称</title>
|
||||||
|
<description>订阅描述Every movie ever plus.</description>
|
||||||
|
</locale>
|
||||||
|
</locales>
|
||||||
|
<review_screenshot>
|
||||||
|
<size>636132</size>
|
||||||
|
<file_name>IMG_5180.PNG</file_name>
|
||||||
|
<checksum type="md5">xxxxx</checksum>
|
||||||
|
</review_screenshot>
|
||||||
|
<review_notes>Some notes for the reviewer.(2~4000字符)</review_notes>
|
||||||
|
</in_app_purchase>
|
||||||
|
</subscription_group>
|
||||||
|
<in_app_purchase>
|
||||||
|
<!-- 可以由字母、数字、下划线(_)和句点(.)构成,最多 255 个字符 -->
|
||||||
|
<product_id>com.app.6usd</product_id>
|
||||||
|
<reference_name>6ud(2~64 个字符)</reference_name>
|
||||||
|
<type>subscription</type>
|
||||||
|
<products>
|
||||||
|
<product>
|
||||||
|
<cleared_for_sale>true</cleared_for_sale>
|
||||||
|
<wholesale_price_tier>49</wholesale_price_tier>
|
||||||
|
</product>
|
||||||
|
</products>
|
||||||
|
<locales>
|
||||||
|
<locale name="ko">
|
||||||
|
<title>2~30个字符</title>
|
||||||
|
<description>至少10~45个字符</description>
|
||||||
|
</locale>
|
||||||
|
</locales>
|
||||||
|
<review_screenshot>
|
||||||
|
<!-- Supported dimensions are: 1334x750, 750x1334, 1024x768, 1024x748, 768x1024, 768x1004, 2048x1536, 2048x1496, 1536x2048, 1536x2008, 2224x1668, 1668x2224, 960x640, 960x600, 640x960, 640x920, 2208x1242, 1242x2208, 2436x1125, 1125x2436, 312x390, 1136x640, 1136x600, 640x1136, 640x1096, 2732x2048, 2048x2732" -->
|
||||||
|
<size>636132</size>
|
||||||
|
<file_name>IMG_5180.PNG</file_name>
|
||||||
|
<checksum type="md5">xxxxx</checksum>
|
||||||
|
</review_screenshot>
|
||||||
|
<review_notes>Some notes for the reviewer.(2~4000字符)</review_notes>
|
||||||
|
</in_app_purchase>
|
||||||
|
</in_app_purchases>
|
||||||
|
</software_metadata>
|
||||||
|
</software>
|
||||||
|
</package>
|
||||||
12
AppleParty/AppleParty/Resources/Transporter/ipa_metadata.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<package version="software5.10" xmlns="http://apple.com/itunes/importer">
|
||||||
|
<software_assets apple_id="{apple_id}" app_platform="{app_platform}">
|
||||||
|
<asset type="{archive_type}">
|
||||||
|
<data_file>
|
||||||
|
<size>{file_size}</size>
|
||||||
|
<file_name>{file_name}</file_name>
|
||||||
|
<checksum type="md5">{file_md5}</checksum>
|
||||||
|
</data_file>
|
||||||
|
</asset>
|
||||||
|
</software_assets>
|
||||||
|
</package>
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<package version="software5.10" xmlns="http://apple.com/itunes/importer">
|
||||||
|
<provider>{provider}</provider>
|
||||||
|
<team_id>{provider}</team_id>
|
||||||
|
<software>
|
||||||
|
<vendor_id>{vendor_id}</vendor_id>
|
||||||
|
<software_metadata app_platform="{app_platform}">
|
||||||
|
<versions>
|
||||||
|
<version string="{app_version}">
|
||||||
|
<locales>
|
||||||
|
<locale name="{app_locale}">
|
||||||
|
<title>{title}</title>
|
||||||
|
<app_previews>
|
||||||
|
<app_preview display_target="{iOS-5.5-in}" position="{1}">
|
||||||
|
<data_file role="source">
|
||||||
|
<size>{video_size}</size>
|
||||||
|
<file_name>{video_name}</file_name>
|
||||||
|
<checksum>{video_md5}</checksum>
|
||||||
|
</data_file>
|
||||||
|
<preview_image_time format="30/1:1/nonDrop">{00:00:05:00}</preview_image_time>
|
||||||
|
</app_preview>
|
||||||
|
</app_previews>
|
||||||
|
<software_screenshots>
|
||||||
|
<software_screenshot display_target="{iOS-5.5-in}" position="{1}">
|
||||||
|
<size>{image_size}</size>
|
||||||
|
<file_name>{image_name}</file_name>
|
||||||
|
<checksum type="md5">{image_md5}</checksum>
|
||||||
|
</software_screenshot>
|
||||||
|
</software_screenshots>
|
||||||
|
</locale>
|
||||||
|
</locales>
|
||||||
|
</version>
|
||||||
|
</versions>
|
||||||
|
</software_metadata>
|
||||||
|
</software>
|
||||||
|
</package>
|
||||||
61
AppleParty/AppleParty/RootView/APRootCollectionAdapter.swift
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
//
|
||||||
|
// APRootCollectionAdapter.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/3/14.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class APRootCollectionAdapter: NSObject {
|
||||||
|
fileprivate static let numberOfSections = 1
|
||||||
|
fileprivate static let itemId = "APRootCollectionCell"
|
||||||
|
|
||||||
|
fileprivate var items = [APRootCollectionModel]() {
|
||||||
|
didSet {
|
||||||
|
collectionView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private var collectionView: NSCollectionView
|
||||||
|
|
||||||
|
init(collectionView: NSCollectionView) {
|
||||||
|
self.collectionView = collectionView
|
||||||
|
super.init()
|
||||||
|
self.collectionView.dataSource = self
|
||||||
|
self.collectionView.delegate = self
|
||||||
|
self.collectionView.register(APRootCollectionCell.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: APRootCollectionAdapter.itemId))
|
||||||
|
}
|
||||||
|
|
||||||
|
func set(items: [APRootCollectionModel]) {
|
||||||
|
self.items = items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension APRootCollectionAdapter: NSCollectionViewDataSource, NSCollectionViewDelegate {
|
||||||
|
func numberOfSectionsInCollectionView(collectionView: NSCollectionView) -> Int {
|
||||||
|
return APRootCollectionAdapter.numberOfSections
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return items.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
|
||||||
|
let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: APRootCollectionAdapter.itemId), for: indexPath)
|
||||||
|
guard let collectionViewItem = item as? APRootCollectionCell else { return item }
|
||||||
|
|
||||||
|
let name = items[indexPath.item].name
|
||||||
|
let icon = items[indexPath.item].icon
|
||||||
|
collectionViewItem.configure(name: name, icon: icon)
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
|
||||||
|
collectionView.deselectItems(at: indexPaths)
|
||||||
|
if let item = indexPaths.first?.item, let handler = items[item].handler {
|
||||||
|
handler()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
AppleParty/AppleParty/RootView/APRootCollectionCell.swift
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// APRootCollectionCell.swift
|
||||||
|
// AppleParty
|
||||||
|
//
|
||||||
|
// Created by HTC on 2022/3/14.
|
||||||
|
// Copyright © 2022 37 Mobile Games. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class APRootCollectionCell: NSCollectionViewItem {
|
||||||
|
|
||||||
|
@IBOutlet weak var imgView: NSImageView!
|
||||||
|
@IBOutlet weak var nameView: NSTextField!
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure(name: String, icon: String) {
|
||||||
|
nameView.stringValue = name
|
||||||
|
imgView?.image = NSImage(named: icon)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||