基础功能提交
152
.gitignore
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
# 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
|
||||
|
||||
# CocoaPods
|
||||
#
|
||||
# We recommend against adding the Pods directory to your .gitignore. However
|
||||
# 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
|
||||
#
|
||||
# Pods/
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
||||
# *.xcworkspace
|
||||
|
||||
# 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 repository.
|
||||
# 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/
|
||||
|
||||
# Mac OS X
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# 自定义排除规则
|
||||
# 排除LocationHelper.swift文件
|
||||
IOS_study/Library/LocationHelper.swift
|
||||
|
||||
# 排除编译生成的文件
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.dylib
|
||||
*.framework
|
||||
*.app
|
||||
*.ipa
|
||||
*.dSYM
|
||||
*.xcarchive
|
||||
|
||||
# 排除Xcode用户特定文件
|
||||
*.xcuserstate
|
||||
*.xcuserdata/
|
||||
*.xcworkspace/xcuserdata/
|
||||
|
||||
# 排除临时文件
|
||||
*.tmp
|
||||
*.temp
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# 排除日志文件
|
||||
*.log
|
||||
|
||||
# 排除配置文件中的敏感信息
|
||||
*.plist
|
||||
!IOS-study-Info.plist
|
||||
|
||||
# 排除图片文件(如果需要)
|
||||
# IOS_study/Img/
|
13
IOS-study-Info.plist
Normal file
@ -0,0 +1,13 @@
|
||||
<?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>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>maps</string>
|
||||
<string>iosamap</string>
|
||||
<string>baidumap</string>
|
||||
<string>qqmap</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
750
IOS_study.xcodeproj/project.pbxproj
Normal file
@ -0,0 +1,750 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 77;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
940E13EB2E2E06F70026F8A5 /* MJRefresh in Frameworks */ = {isa = PBXBuildFile; productRef = 940E13EA2E2E06F70026F8A5 /* MJRefresh */; };
|
||||
940E13EE2E2E07110026F8A5 /* LeanCloud in Frameworks */ = {isa = PBXBuildFile; productRef = 940E13ED2E2E07110026F8A5 /* LeanCloud */; };
|
||||
940E13F12E2E07300026F8A5 /* DotLottie in Frameworks */ = {isa = PBXBuildFile; productRef = 940E13F02E2E07300026F8A5 /* DotLottie */; };
|
||||
940E13F42E2E07470026F8A5 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 940E13F32E2E07470026F8A5 /* Kingfisher */; };
|
||||
940E13F82E2F42000026F8A5 /* CompanyGalleryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 940E13F72E2F42000026F8A5 /* CompanyGalleryView.swift */; };
|
||||
940E13FA2E2FA2660026F8A5 /* CompanyGalleryORView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 940E13F92E2FA2660026F8A5 /* CompanyGalleryORView.swift */; };
|
||||
940E13FC2E2FA63B0026F8A5 /* PinchZoomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 940E13FB2E2FA63B0026F8A5 /* PinchZoomView.swift */; };
|
||||
941830A12E252FA90004042F /* DBUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 941830A02E252FA90004042F /* DBUser.swift */; };
|
||||
9418310C2E2533520004042F /* UserManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9418310B2E2533520004042F /* UserManager.swift */; };
|
||||
941831EF2E25E6AC0004042F /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 941831EE2E25E6AC0004042F /* ProfileView.swift */; };
|
||||
941831F12E25E6F10004042F /* AuthViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 941831F02E25E6F10004042F /* AuthViewModel.swift */; };
|
||||
941831F32E2634BF0004042F /* ProfileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 941831F22E2634BF0004042F /* ProfileViewModel.swift */; };
|
||||
941831F52E2638080004042F /* PostCompanyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 941831F42E2638070004042F /* PostCompanyView.swift */; };
|
||||
941831F72E26626F0004042F /* StorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 941831F62E26626F0004042F /* StorageManager.swift */; };
|
||||
943D8EB62E29E2870079191A /* ProfileView+ComUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943D8EB52E29E2870079191A /* ProfileView+ComUI.swift */; };
|
||||
9441C9742E323A7B00805BF3 /* PLJobsView+JobUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9441C9732E323A7B00805BF3 /* PLJobsView+JobUI.swift */; };
|
||||
9441C9762E325A7E00805BF3 /* Reportmanager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9441C9752E325A7E00805BF3 /* Reportmanager.swift */; };
|
||||
9441C9782E325B4800805BF3 /* Report.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9441C9772E325B4800805BF3 /* Report.swift */; };
|
||||
9441C9B82E33432500805BF3 /* PLJobView+RevUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9441C9B72E33432500805BF3 /* PLJobView+RevUI.swift */; };
|
||||
9441C9BA2E3345D100805BF3 /* PostReviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9441C9B92E3345D100805BF3 /* PostReviewView.swift */; };
|
||||
9441C9BD2E33462000805BF3 /* ReviewViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9441C9BC2E33462000805BF3 /* ReviewViewModel.swift */; };
|
||||
9441C9BF2E33465400805BF3 /* Review.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9441C9BE2E33465400805BF3 /* Review.swift */; };
|
||||
9474AFCA2E14CAFC004AA9AB /* PLJobManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9474AFC92E14CAFC004AA9AB /* PLJobManager.swift */; };
|
||||
9474AFCC2E155E22004AA9AB /* LeanCloud+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9474AFCB2E155E22004AA9AB /* LeanCloud+.swift */; };
|
||||
94790B4C2E1179A600BF0A09 /* CJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B342E1179A600BF0A09 /* CJob.swift */; };
|
||||
94790B4D2E1179A600BF0A09 /* CUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B352E1179A600BF0A09 /* CUser.swift */; };
|
||||
94790B4E2E1179A600BF0A09 /* GView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B362E1179A600BF0A09 /* GView.swift */; };
|
||||
94790B4F2E1179A600BF0A09 /* View+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B382E1179A600BF0A09 /* View+.swift */; };
|
||||
94790B502E1179A600BF0A09 /* View+Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B392E1179A600BF0A09 /* View+Label.swift */; };
|
||||
94790B512E1179A600BF0A09 /* View+Style.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B3A2E1179A600BF0A09 /* View+Style.swift */; };
|
||||
94790B522E1179A600BF0A09 /* OverflowGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B3E2E1179A600BF0A09 /* OverflowGrid.swift */; };
|
||||
94790B532E1179A600BF0A09 /* PLJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B402E1179A600BF0A09 /* PLJob.swift */; };
|
||||
94790B542E1179A600BF0A09 /* IOS_studyApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B452E1179A600BF0A09 /* IOS_studyApp.swift */; };
|
||||
94790B552E1179A600BF0A09 /* PLJobCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B462E1179A600BF0A09 /* PLJobCellView.swift */; };
|
||||
94790B562E1179A600BF0A09 /* PLJobsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B472E1179A600BF0A09 /* PLJobsView.swift */; };
|
||||
94790B572E1179A600BF0A09 /* PLJobView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B482E1179A600BF0A09 /* PLJobView.swift */; };
|
||||
94790B582E1179A600BF0A09 /* PostPLJobIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B492E1179A600BF0A09 /* PostPLJobIView.swift */; };
|
||||
94790B592E1179A600BF0A09 /* TaBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B4A2E1179A600BF0A09 /* TaBarView.swift */; };
|
||||
94790B5B2E1179A600BF0A09 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 94790B422E1179A600BF0A09 /* Preview Assets.xcassets */; };
|
||||
94790B5C2E1179A600BF0A09 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 94790B442E1179A600BF0A09 /* Assets.xcassets */; };
|
||||
94790B5E2E117AD800BF0A09 /* PLJobViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B5D2E117AD800BF0A09 /* PLJobViewModel.swift */; };
|
||||
94790B602E11846F00BF0A09 /* Foundation+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B5F2E11846F00BF0A09 /* Foundation+.swift */; };
|
||||
94790B622E118B3600BF0A09 /* PostPLJobIView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B612E118B3600BF0A09 /* PostPLJobIView+.swift */; };
|
||||
94790B642E118CB400BF0A09 /* HUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B632E118CB400BF0A09 /* HUDView.swift */; };
|
||||
94790B662E129CC500BF0A09 /* GType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B652E129CC500BF0A09 /* GType.swift */; };
|
||||
94790B682E137DB300BF0A09 /* PostJobView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B672E137DB300BF0A09 /* PostJobView.swift */; };
|
||||
94790B6A2E13820500BF0A09 /* TaBarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B692E13820500BF0A09 /* TaBarViewModel.swift */; };
|
||||
94790B6C2E13879400BF0A09 /* PLJobView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94790B6B2E13879300BF0A09 /* PLJobView+.swift */; };
|
||||
949F8B282E30C5210013AB3A /* ProfileViewModel+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 949F8B272E30C5210013AB3A /* ProfileViewModel+.swift */; };
|
||||
949F8B2A2E30CCFB0013AB3A /* ProfileView+JobUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 949F8B292E30CCFB0013AB3A /* ProfileView+JobUI.swift */; };
|
||||
94F008672E16345100989E46 /* GFunc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94F008662E16345100989E46 /* GFunc.swift */; };
|
||||
94F008692E16749B00989E46 /* RESTManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94F008682E16749B00989E46 /* RESTManager.swift */; };
|
||||
94F0E24E2E1CFA00003406F0 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94F0E24D2E1CFA00003406F0 /* NetworkMonitor.swift */; };
|
||||
94F0E2562E1F6530003406F0 /* LocationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94F0E2552E1F6530003406F0 /* LocationHelper.swift */; };
|
||||
94F0E2582E1F87D2003406F0 /* OF.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94F0E2572E1F87D2003406F0 /* OF.swift */; };
|
||||
94F0E25A2E1F996A003406F0 /* PLJobsView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94F0E2592E1F996A003406F0 /* PLJobsView+.swift */; };
|
||||
94F0E25D2E1FC171003406F0 /* SignInPhoneView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94F0E25C2E1FC170003406F0 /* SignInPhoneView.swift */; };
|
||||
94F0E25F2E1FC186003406F0 /* SignInPhoneViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94F0E25E2E1FC186003406F0 /* SignInPhoneViewModel.swift */; };
|
||||
94F0E2622E1FC2D5003406F0 /* SettginsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94F0E2612E1FC2D5003406F0 /* SettginsView.swift */; };
|
||||
94F0E2642E1FDAB2003406F0 /* AuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94F0E2632E1FDAB2003406F0 /* AuthManager.swift */; };
|
||||
94F0E2662E1FFE58003406F0 /* SignInPhoneView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94F0E2652E1FFE58003406F0 /* SignInPhoneView+.swift */; };
|
||||
94F0E2682E23FB7E003406F0 /* AgreeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94F0E2672E23FB7E003406F0 /* AgreeView.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
940E13F72E2F42000026F8A5 /* CompanyGalleryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompanyGalleryView.swift; sourceTree = "<group>"; };
|
||||
940E13F92E2FA2660026F8A5 /* CompanyGalleryORView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompanyGalleryORView.swift; sourceTree = "<group>"; };
|
||||
940E13FB2E2FA63B0026F8A5 /* PinchZoomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinchZoomView.swift; sourceTree = "<group>"; };
|
||||
941830A02E252FA90004042F /* DBUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBUser.swift; sourceTree = "<group>"; };
|
||||
9418310B2E2533520004042F /* UserManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserManager.swift; sourceTree = "<group>"; };
|
||||
941831EE2E25E6AC0004042F /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
|
||||
941831F02E25E6F10004042F /* AuthViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthViewModel.swift; sourceTree = "<group>"; };
|
||||
941831F22E2634BF0004042F /* ProfileViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewModel.swift; sourceTree = "<group>"; };
|
||||
941831F42E2638070004042F /* PostCompanyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCompanyView.swift; sourceTree = "<group>"; };
|
||||
941831F62E26626F0004042F /* StorageManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageManager.swift; sourceTree = "<group>"; };
|
||||
943D8EB52E29E2870079191A /* ProfileView+ComUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+ComUI.swift"; sourceTree = "<group>"; };
|
||||
9441C9732E323A7B00805BF3 /* PLJobsView+JobUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PLJobsView+JobUI.swift"; sourceTree = "<group>"; };
|
||||
9441C9752E325A7E00805BF3 /* Reportmanager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reportmanager.swift; sourceTree = "<group>"; };
|
||||
9441C9772E325B4800805BF3 /* Report.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Report.swift; sourceTree = "<group>"; };
|
||||
9441C9B72E33432500805BF3 /* PLJobView+RevUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PLJobView+RevUI.swift"; sourceTree = "<group>"; };
|
||||
9441C9B92E3345D100805BF3 /* PostReviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostReviewView.swift; sourceTree = "<group>"; };
|
||||
9441C9BC2E33462000805BF3 /* ReviewViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewViewModel.swift; sourceTree = "<group>"; };
|
||||
9441C9BE2E33465400805BF3 /* Review.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Review.swift; sourceTree = "<group>"; };
|
||||
9474AFC92E14CAFC004AA9AB /* PLJobManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PLJobManager.swift; sourceTree = "<group>"; };
|
||||
9474AFCB2E155E22004AA9AB /* LeanCloud+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LeanCloud+.swift"; sourceTree = "<group>"; };
|
||||
94790B342E1179A600BF0A09 /* CJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CJob.swift; sourceTree = "<group>"; };
|
||||
94790B352E1179A600BF0A09 /* CUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CUser.swift; sourceTree = "<group>"; };
|
||||
94790B362E1179A600BF0A09 /* GView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GView.swift; sourceTree = "<group>"; };
|
||||
94790B382E1179A600BF0A09 /* View+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+.swift"; sourceTree = "<group>"; };
|
||||
94790B392E1179A600BF0A09 /* View+Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Label.swift"; sourceTree = "<group>"; };
|
||||
94790B3A2E1179A600BF0A09 /* View+Style.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Style.swift"; sourceTree = "<group>"; };
|
||||
94790B3E2E1179A600BF0A09 /* OverflowGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverflowGrid.swift; sourceTree = "<group>"; };
|
||||
94790B402E1179A600BF0A09 /* PLJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PLJob.swift; sourceTree = "<group>"; };
|
||||
94790B422E1179A600BF0A09 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
94790B442E1179A600BF0A09 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
94790B452E1179A600BF0A09 /* IOS_studyApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOS_studyApp.swift; sourceTree = "<group>"; };
|
||||
94790B462E1179A600BF0A09 /* PLJobCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PLJobCellView.swift; sourceTree = "<group>"; };
|
||||
94790B472E1179A600BF0A09 /* PLJobsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PLJobsView.swift; sourceTree = "<group>"; };
|
||||
94790B482E1179A600BF0A09 /* PLJobView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PLJobView.swift; sourceTree = "<group>"; };
|
||||
94790B492E1179A600BF0A09 /* PostPLJobIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostPLJobIView.swift; sourceTree = "<group>"; };
|
||||
94790B4A2E1179A600BF0A09 /* TaBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaBarView.swift; sourceTree = "<group>"; };
|
||||
94790B5D2E117AD800BF0A09 /* PLJobViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PLJobViewModel.swift; sourceTree = "<group>"; };
|
||||
94790B5F2E11846F00BF0A09 /* Foundation+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Foundation+.swift"; sourceTree = "<group>"; };
|
||||
94790B612E118B3600BF0A09 /* PostPLJobIView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PostPLJobIView+.swift"; sourceTree = "<group>"; };
|
||||
94790B632E118CB400BF0A09 /* HUDView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HUDView.swift; sourceTree = "<group>"; };
|
||||
94790B652E129CC500BF0A09 /* GType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GType.swift; sourceTree = "<group>"; };
|
||||
94790B672E137DB300BF0A09 /* PostJobView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostJobView.swift; sourceTree = "<group>"; };
|
||||
94790B692E13820500BF0A09 /* TaBarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaBarViewModel.swift; sourceTree = "<group>"; };
|
||||
94790B6B2E13879300BF0A09 /* PLJobView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PLJobView+.swift"; sourceTree = "<group>"; };
|
||||
947A93B32DEEE882002E0937 /* IOS_study.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IOS_study.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
949F8B272E30C5210013AB3A /* ProfileViewModel+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileViewModel+.swift"; sourceTree = "<group>"; };
|
||||
949F8B292E30CCFB0013AB3A /* ProfileView+JobUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+JobUI.swift"; sourceTree = "<group>"; };
|
||||
94F008662E16345100989E46 /* GFunc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GFunc.swift; sourceTree = "<group>"; };
|
||||
94F008682E16749B00989E46 /* RESTManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RESTManager.swift; sourceTree = "<group>"; };
|
||||
94F0E24D2E1CFA00003406F0 /* NetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = "<group>"; };
|
||||
94F0E2542E1F5F15003406F0 /* IOS-study-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "IOS-study-Info.plist"; sourceTree = SOURCE_ROOT; };
|
||||
94F0E2552E1F6530003406F0 /* LocationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationHelper.swift; sourceTree = "<group>"; };
|
||||
94F0E2572E1F87D2003406F0 /* OF.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OF.swift; sourceTree = "<group>"; };
|
||||
94F0E2592E1F996A003406F0 /* PLJobsView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PLJobsView+.swift"; sourceTree = "<group>"; };
|
||||
94F0E25C2E1FC170003406F0 /* SignInPhoneView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInPhoneView.swift; sourceTree = "<group>"; };
|
||||
94F0E25E2E1FC186003406F0 /* SignInPhoneViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInPhoneViewModel.swift; sourceTree = "<group>"; };
|
||||
94F0E2612E1FC2D5003406F0 /* SettginsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettginsView.swift; sourceTree = "<group>"; };
|
||||
94F0E2632E1FDAB2003406F0 /* AuthManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthManager.swift; sourceTree = "<group>"; };
|
||||
94F0E2652E1FFE58003406F0 /* SignInPhoneView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SignInPhoneView+.swift"; sourceTree = "<group>"; };
|
||||
94F0E2672E23FB7E003406F0 /* AgreeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgreeView.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
947A93B02DEEE882002E0937 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
940E13F42E2E07470026F8A5 /* Kingfisher in Frameworks */,
|
||||
940E13EE2E2E07110026F8A5 /* LeanCloud in Frameworks */,
|
||||
940E13EB2E2E06F70026F8A5 /* MJRefresh in Frameworks */,
|
||||
940E13F12E2E07300026F8A5 /* DotLottie in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
9441C9BB2E3345FE00805BF3 /* Review */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9441C9BC2E33462000805BF3 /* ReviewViewModel.swift */,
|
||||
);
|
||||
path = Review;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9474AFC52E14C6DC004AA9AB /* Job */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94790B5D2E117AD800BF0A09 /* PLJobViewModel.swift */,
|
||||
94790B482E1179A600BF0A09 /* PLJobView.swift */,
|
||||
9441C9732E323A7B00805BF3 /* PLJobsView+JobUI.swift */,
|
||||
94790B6B2E13879300BF0A09 /* PLJobView+.swift */,
|
||||
);
|
||||
path = Job;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9474AFC62E14C6EB004AA9AB /* Post */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94790B612E118B3600BF0A09 /* PostPLJobIView+.swift */,
|
||||
94790B492E1179A600BF0A09 /* PostPLJobIView.swift */,
|
||||
94790B672E137DB300BF0A09 /* PostJobView.swift */,
|
||||
941831F42E2638070004042F /* PostCompanyView.swift */,
|
||||
940E13F72E2F42000026F8A5 /* CompanyGalleryView.swift */,
|
||||
9441C9B92E3345D100805BF3 /* PostReviewView.swift */,
|
||||
940E13F92E2FA2660026F8A5 /* CompanyGalleryORView.swift */,
|
||||
);
|
||||
path = Post;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9474AFC72E14C724004AA9AB /* Jobs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94790B472E1179A600BF0A09 /* PLJobsView.swift */,
|
||||
94F0E2592E1F996A003406F0 /* PLJobsView+.swift */,
|
||||
94790B462E1179A600BF0A09 /* PLJobCellView.swift */,
|
||||
);
|
||||
path = Jobs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9474AFC82E14CAC8004AA9AB /* Manager */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9418310B2E2533520004042F /* UserManager.swift */,
|
||||
9441C9752E325A7E00805BF3 /* Reportmanager.swift */,
|
||||
941831F62E26626F0004042F /* StorageManager.swift */,
|
||||
94F0E2632E1FDAB2003406F0 /* AuthManager.swift */,
|
||||
9474AFC92E14CAFC004AA9AB /* PLJobManager.swift */,
|
||||
94F008682E16749B00989E46 /* RESTManager.swift */,
|
||||
);
|
||||
path = Manager;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
94790B372E1179A600BF0A09 /* Constant */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94790B342E1179A600BF0A09 /* CJob.swift */,
|
||||
94790B352E1179A600BF0A09 /* CUser.swift */,
|
||||
94790B362E1179A600BF0A09 /* GView.swift */,
|
||||
94F008662E16345100989E46 /* GFunc.swift */,
|
||||
94790B652E129CC500BF0A09 /* GType.swift */,
|
||||
);
|
||||
path = Constant;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
94790B3B2E1179A600BF0A09 /* Extension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94790B5F2E11846F00BF0A09 /* Foundation+.swift */,
|
||||
94790B382E1179A600BF0A09 /* View+.swift */,
|
||||
9474AFCB2E155E22004AA9AB /* LeanCloud+.swift */,
|
||||
94790B392E1179A600BF0A09 /* View+Label.swift */,
|
||||
94790B3A2E1179A600BF0A09 /* View+Style.swift */,
|
||||
);
|
||||
path = Extension;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
94790B3F2E1179A600BF0A09 /* Library */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94790B632E118CB400BF0A09 /* HUDView.swift */,
|
||||
94790B3E2E1179A600BF0A09 /* OverflowGrid.swift */,
|
||||
94F0E24D2E1CFA00003406F0 /* NetworkMonitor.swift */,
|
||||
94F0E2552E1F6530003406F0 /* LocationHelper.swift */,
|
||||
940E13FB2E2FA63B0026F8A5 /* PinchZoomView.swift */,
|
||||
);
|
||||
path = Library;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
94790B412E1179A600BF0A09 /* Model */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94790B402E1179A600BF0A09 /* PLJob.swift */,
|
||||
9441C9BE2E33465400805BF3 /* Review.swift */,
|
||||
9441C9772E325B4800805BF3 /* Report.swift */,
|
||||
94F0E2572E1F87D2003406F0 /* OF.swift */,
|
||||
941830A02E252FA90004042F /* DBUser.swift */,
|
||||
);
|
||||
path = Model;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
94790B432E1179A600BF0A09 /* Preview Content */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94790B422E1179A600BF0A09 /* Preview Assets.xcassets */,
|
||||
);
|
||||
path = "Preview Content";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
94790B4B2E1179A600BF0A09 /* IOS_study */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94F0E2542E1F5F15003406F0 /* IOS-study-Info.plist */,
|
||||
94790B452E1179A600BF0A09 /* IOS_studyApp.swift */,
|
||||
94790B4A2E1179A600BF0A09 /* TaBarView.swift */,
|
||||
94790B692E13820500BF0A09 /* TaBarViewModel.swift */,
|
||||
9441C9BB2E3345FE00805BF3 /* Review */,
|
||||
94790B412E1179A600BF0A09 /* Model */,
|
||||
94F0E2602E1FC2AD003406F0 /* Profile */,
|
||||
94F0E25B2E1FC14A003406F0 /* Auth */,
|
||||
9474AFC82E14CAC8004AA9AB /* Manager */,
|
||||
9474AFC72E14C724004AA9AB /* Jobs */,
|
||||
9474AFC52E14C6DC004AA9AB /* Job */,
|
||||
9474AFC62E14C6EB004AA9AB /* Post */,
|
||||
94790B3B2E1179A600BF0A09 /* Extension */,
|
||||
94790B372E1179A600BF0A09 /* Constant */,
|
||||
94790B442E1179A600BF0A09 /* Assets.xcassets */,
|
||||
94790B3F2E1179A600BF0A09 /* Library */,
|
||||
94790B432E1179A600BF0A09 /* Preview Content */,
|
||||
);
|
||||
path = IOS_study;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
947A93AA2DEEE882002E0937 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94790B4B2E1179A600BF0A09 /* IOS_study */,
|
||||
947A93B42DEEE882002E0937 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
947A93B42DEEE882002E0937 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
947A93B32DEEE882002E0937 /* IOS_study.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
94F0E25B2E1FC14A003406F0 /* Auth */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94F0E25E2E1FC186003406F0 /* SignInPhoneViewModel.swift */,
|
||||
94F0E25C2E1FC170003406F0 /* SignInPhoneView.swift */,
|
||||
94F0E2652E1FFE58003406F0 /* SignInPhoneView+.swift */,
|
||||
94F0E2672E23FB7E003406F0 /* AgreeView.swift */,
|
||||
941831F02E25E6F10004042F /* AuthViewModel.swift */,
|
||||
);
|
||||
path = Auth;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
94F0E2602E1FC2AD003406F0 /* Profile */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
941831F22E2634BF0004042F /* ProfileViewModel.swift */,
|
||||
949F8B272E30C5210013AB3A /* ProfileViewModel+.swift */,
|
||||
941831EE2E25E6AC0004042F /* ProfileView.swift */,
|
||||
94F0E2612E1FC2D5003406F0 /* SettginsView.swift */,
|
||||
949F8B292E30CCFB0013AB3A /* ProfileView+JobUI.swift */,
|
||||
9441C9B72E33432500805BF3 /* PLJobView+RevUI.swift */,
|
||||
943D8EB52E29E2870079191A /* ProfileView+ComUI.swift */,
|
||||
);
|
||||
path = Profile;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
947A93B22DEEE882002E0937 /* IOS_study */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 947A93C12DEEE885002E0937 /* Build configuration list for PBXNativeTarget "IOS_study" */;
|
||||
buildPhases = (
|
||||
947A93AF2DEEE882002E0937 /* Sources */,
|
||||
947A93B02DEEE882002E0937 /* Frameworks */,
|
||||
947A93B12DEEE882002E0937 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = IOS_study;
|
||||
packageProductDependencies = (
|
||||
940E13EA2E2E06F70026F8A5 /* MJRefresh */,
|
||||
940E13ED2E2E07110026F8A5 /* LeanCloud */,
|
||||
940E13F02E2E07300026F8A5 /* DotLottie */,
|
||||
940E13F32E2E07470026F8A5 /* Kingfisher */,
|
||||
);
|
||||
productName = IOS_study;
|
||||
productReference = 947A93B32DEEE882002E0937 /* IOS_study.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
947A93AB2DEEE882002E0937 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 1620;
|
||||
LastUpgradeCheck = 1620;
|
||||
TargetAttributes = {
|
||||
947A93B22DEEE882002E0937 = {
|
||||
CreatedOnToolsVersion = 16.2;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 947A93AE2DEEE882002E0937 /* Build configuration list for PBXProject "IOS_study" */;
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 947A93AA2DEEE882002E0937;
|
||||
minimizedProjectReferenceProxies = 1;
|
||||
packageReferences = (
|
||||
940E13E92E2E06F70026F8A5 /* XCRemoteSwiftPackageReference "MJRefresh" */,
|
||||
940E13EC2E2E07110026F8A5 /* XCRemoteSwiftPackageReference "swift-sdk" */,
|
||||
940E13EF2E2E07300026F8A5 /* XCRemoteSwiftPackageReference "dotlottie-ios" */,
|
||||
940E13F22E2E07470026F8A5 /* XCRemoteSwiftPackageReference "Kingfisher" */,
|
||||
);
|
||||
preferredProjectObjectVersion = 77;
|
||||
productRefGroup = 947A93B42DEEE882002E0937 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
947A93B22DEEE882002E0937 /* IOS_study */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
947A93B12DEEE882002E0937 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
94790B5B2E1179A600BF0A09 /* Preview Assets.xcassets in Resources */,
|
||||
94790B5C2E1179A600BF0A09 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
947A93AF2DEEE882002E0937 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
94F0E2582E1F87D2003406F0 /* OF.swift in Sources */,
|
||||
941831EF2E25E6AC0004042F /* ProfileView.swift in Sources */,
|
||||
94F0E2562E1F6530003406F0 /* LocationHelper.swift in Sources */,
|
||||
9418310C2E2533520004042F /* UserManager.swift in Sources */,
|
||||
94790B642E118CB400BF0A09 /* HUDView.swift in Sources */,
|
||||
94790B4C2E1179A600BF0A09 /* CJob.swift in Sources */,
|
||||
94790B662E129CC500BF0A09 /* GType.swift in Sources */,
|
||||
94790B4D2E1179A600BF0A09 /* CUser.swift in Sources */,
|
||||
94790B682E137DB300BF0A09 /* PostJobView.swift in Sources */,
|
||||
9441C9BF2E33465400805BF3 /* Review.swift in Sources */,
|
||||
94F0E2642E1FDAB2003406F0 /* AuthManager.swift in Sources */,
|
||||
9474AFCC2E155E22004AA9AB /* LeanCloud+.swift in Sources */,
|
||||
94790B4E2E1179A600BF0A09 /* GView.swift in Sources */,
|
||||
94F0E24E2E1CFA00003406F0 /* NetworkMonitor.swift in Sources */,
|
||||
94790B4F2E1179A600BF0A09 /* View+.swift in Sources */,
|
||||
94790B502E1179A600BF0A09 /* View+Label.swift in Sources */,
|
||||
9441C9762E325A7E00805BF3 /* Reportmanager.swift in Sources */,
|
||||
94F0E2622E1FC2D5003406F0 /* SettginsView.swift in Sources */,
|
||||
9441C9B82E33432500805BF3 /* PLJobView+RevUI.swift in Sources */,
|
||||
941831F52E2638080004042F /* PostCompanyView.swift in Sources */,
|
||||
9441C9BD2E33462000805BF3 /* ReviewViewModel.swift in Sources */,
|
||||
94F0E25D2E1FC171003406F0 /* SignInPhoneView.swift in Sources */,
|
||||
94790B512E1179A600BF0A09 /* View+Style.swift in Sources */,
|
||||
94790B522E1179A600BF0A09 /* OverflowGrid.swift in Sources */,
|
||||
94790B532E1179A600BF0A09 /* PLJob.swift in Sources */,
|
||||
94790B542E1179A600BF0A09 /* IOS_studyApp.swift in Sources */,
|
||||
9441C9BA2E3345D100805BF3 /* PostReviewView.swift in Sources */,
|
||||
940E13FC2E2FA63B0026F8A5 /* PinchZoomView.swift in Sources */,
|
||||
94F008672E16345100989E46 /* GFunc.swift in Sources */,
|
||||
9474AFCA2E14CAFC004AA9AB /* PLJobManager.swift in Sources */,
|
||||
941831F32E2634BF0004042F /* ProfileViewModel.swift in Sources */,
|
||||
949F8B282E30C5210013AB3A /* ProfileViewModel+.swift in Sources */,
|
||||
94F0E2682E23FB7E003406F0 /* AgreeView.swift in Sources */,
|
||||
94F0E2662E1FFE58003406F0 /* SignInPhoneView+.swift in Sources */,
|
||||
94790B552E1179A600BF0A09 /* PLJobCellView.swift in Sources */,
|
||||
9441C9782E325B4800805BF3 /* Report.swift in Sources */,
|
||||
9441C9742E323A7B00805BF3 /* PLJobsView+JobUI.swift in Sources */,
|
||||
941830A12E252FA90004042F /* DBUser.swift in Sources */,
|
||||
94F0E25A2E1F996A003406F0 /* PLJobsView+.swift in Sources */,
|
||||
94790B6A2E13820500BF0A09 /* TaBarViewModel.swift in Sources */,
|
||||
94790B6C2E13879400BF0A09 /* PLJobView+.swift in Sources */,
|
||||
940E13F82E2F42000026F8A5 /* CompanyGalleryView.swift in Sources */,
|
||||
941831F72E26626F0004042F /* StorageManager.swift in Sources */,
|
||||
94790B562E1179A600BF0A09 /* PLJobsView.swift in Sources */,
|
||||
943D8EB62E29E2870079191A /* ProfileView+ComUI.swift in Sources */,
|
||||
941831F12E25E6F10004042F /* AuthViewModel.swift in Sources */,
|
||||
94F008692E16749B00989E46 /* RESTManager.swift in Sources */,
|
||||
94790B622E118B3600BF0A09 /* PostPLJobIView+.swift in Sources */,
|
||||
949F8B2A2E30CCFB0013AB3A /* ProfileView+JobUI.swift in Sources */,
|
||||
94790B572E1179A600BF0A09 /* PLJobView.swift in Sources */,
|
||||
940E13FA2E2FA2660026F8A5 /* CompanyGalleryORView.swift in Sources */,
|
||||
94790B582E1179A600BF0A09 /* PostPLJobIView.swift in Sources */,
|
||||
94790B592E1179A600BF0A09 /* TaBarView.swift in Sources */,
|
||||
94790B602E11846F00BF0A09 /* Foundation+.swift in Sources */,
|
||||
94F0E25F2E1FC186003406F0 /* SignInPhoneViewModel.swift in Sources */,
|
||||
94790B5E2E117AD800BF0A09 /* PLJobViewModel.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
947A93BF2DEEE885002E0937 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
947A93C02DEEE885002E0937 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
947A93C22DEEE885002E0937 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"IOS_study/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = 6M7MAFWB7K;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "IOS-study-Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Boss_tdcat;
|
||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "仅添加照片,不会读取任何照片";
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "cn.tdcat.IOS-study";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
947A93C32DEEE885002E0937 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"IOS_study/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = 6M7MAFWB7K;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "IOS-study-Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Boss_tdcat;
|
||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "仅添加照片,不会读取任何照片";
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "cn.tdcat.IOS-study";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
947A93AE2DEEE882002E0937 /* Build configuration list for PBXProject "IOS_study" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
947A93BF2DEEE885002E0937 /* Debug */,
|
||||
947A93C02DEEE885002E0937 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
947A93C12DEEE885002E0937 /* Build configuration list for PBXNativeTarget "IOS_study" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
947A93C22DEEE885002E0937 /* Debug */,
|
||||
947A93C32DEEE885002E0937 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
940E13E92E2E06F70026F8A5 /* XCRemoteSwiftPackageReference "MJRefresh" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/CoderMJLee/MJRefresh.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 3.7.9;
|
||||
};
|
||||
};
|
||||
940E13EC2E2E07110026F8A5 /* XCRemoteSwiftPackageReference "swift-sdk" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/leancloud/swift-sdk.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 17.11.0;
|
||||
};
|
||||
};
|
||||
940E13EF2E2E07300026F8A5 /* XCRemoteSwiftPackageReference "dotlottie-ios" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/LottieFiles/dotlottie-ios";
|
||||
requirement = {
|
||||
branch = main;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
940E13F22E2E07470026F8A5 /* XCRemoteSwiftPackageReference "Kingfisher" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/onevcat/Kingfisher.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 8.5.0;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
940E13EA2E2E06F70026F8A5 /* MJRefresh */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 940E13E92E2E06F70026F8A5 /* XCRemoteSwiftPackageReference "MJRefresh" */;
|
||||
productName = MJRefresh;
|
||||
};
|
||||
940E13ED2E2E07110026F8A5 /* LeanCloud */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 940E13EC2E2E07110026F8A5 /* XCRemoteSwiftPackageReference "swift-sdk" */;
|
||||
productName = LeanCloud;
|
||||
};
|
||||
940E13F02E2E07300026F8A5 /* DotLottie */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 940E13EF2E2E07300026F8A5 /* XCRemoteSwiftPackageReference "dotlottie-ios" */;
|
||||
productName = DotLottie;
|
||||
};
|
||||
940E13F32E2E07470026F8A5 /* Kingfisher */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 940E13F22E2E07470026F8A5 /* XCRemoteSwiftPackageReference "Kingfisher" */;
|
||||
productName = Kingfisher;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = 947A93AB2DEEE882002E0937 /* Project object */;
|
||||
}
|
7
IOS_study.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,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1640"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "947A93B22DEEE882002E0937"
|
||||
BuildableName = "IOS_study.app"
|
||||
BlueprintName = "IOS_study"
|
||||
ReferencedContainer = "container:IOS_study.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "947A93B22DEEE882002E0937"
|
||||
BuildableName = "IOS_study.app"
|
||||
BlueprintName = "IOS_study"
|
||||
ReferencedContainer = "container:IOS_study.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "947A93B22DEEE882002E0937"
|
||||
BuildableName = "IOS_study.app"
|
||||
BlueprintName = "IOS_study"
|
||||
ReferencedContainer = "container:IOS_study.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
18
IOS_study/Assets.xcassets/AccentColor.colorset/Contents.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"platform" : "universal",
|
||||
"reference" : "systemIndigoColor"
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"localizable" : true
|
||||
}
|
||||
}
|
36
IOS_study/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "TDCAT..png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "tinted"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
IOS_study/Assets.xcassets/AppIcon.appiconset/TDCAT..png
Normal file
After Width: | Height: | Size: 1.7 MiB |
6
IOS_study/Assets.xcassets/Contents.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
38
IOS_study/Assets.xcassets/aBG.colorset/Contents.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "0.996"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.122",
|
||||
"green" : "0.114",
|
||||
"red" : "0.114"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
38
IOS_study/Assets.xcassets/aBG2.colorset/Contents.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.965",
|
||||
"green" : "0.961",
|
||||
"red" : "0.961"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.078",
|
||||
"green" : "0.075",
|
||||
"red" : "0.075"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
38
IOS_study/Assets.xcassets/aBGR.colorset/Contents.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.122",
|
||||
"green" : "0.114",
|
||||
"red" : "0.114"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "0.996"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
38
IOS_study/Assets.xcassets/aTC 2.colorset/Contents.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.200",
|
||||
"green" : "0.200",
|
||||
"red" : "0.200"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.753",
|
||||
"green" : "0.753",
|
||||
"red" : "0.753"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
38
IOS_study/Assets.xcassets/aTC.colorset/Contents.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.200",
|
||||
"green" : "0.200",
|
||||
"red" : "0.200"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.878",
|
||||
"green" : "0.878",
|
||||
"red" : "0.878"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
38
IOS_study/Assets.xcassets/aTCR.colorset/Contents.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.878",
|
||||
"green" : "0.878",
|
||||
"red" : "0.878"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.200",
|
||||
"green" : "0.200",
|
||||
"red" : "0.200"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
6
IOS_study/Assets.xcassets/jobIcons/Contents.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
21
IOS_study/Assets.xcassets/jobIcons/benefit.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "benefit.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
IOS_study/Assets.xcassets/jobIcons/benefit.imageset/benefit.png
vendored
Normal file
After Width: | Height: | Size: 2.0 KiB |
21
IOS_study/Assets.xcassets/jobIcons/company.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "company.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
IOS_study/Assets.xcassets/jobIcons/company.imageset/company.png
vendored
Normal file
After Width: | Height: | Size: 955 B |
21
IOS_study/Assets.xcassets/jobIcons/content.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "content.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
IOS_study/Assets.xcassets/jobIcons/content.imageset/content.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
21
IOS_study/Assets.xcassets/jobIcons/employ.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "employ.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
IOS_study/Assets.xcassets/jobIcons/employ.imageset/employ.png
vendored
Normal file
After Width: | Height: | Size: 1.3 KiB |
21
IOS_study/Assets.xcassets/jobIcons/leader.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "leader.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
IOS_study/Assets.xcassets/jobIcons/leader.imageset/leader.png
vendored
Normal file
After Width: | Height: | Size: 1.4 KiB |
21
IOS_study/Assets.xcassets/jobIcons/need.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "need.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
IOS_study/Assets.xcassets/jobIcons/need.imageset/need.png
vendored
Normal file
After Width: | Height: | Size: 1.3 KiB |
21
IOS_study/Assets.xcassets/jobIcons/station.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "station.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
IOS_study/Assets.xcassets/jobIcons/station.imageset/station.png
vendored
Normal file
After Width: | Height: | Size: 1.7 KiB |
21
IOS_study/Assets.xcassets/jobIcons/time.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "time.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
IOS_study/Assets.xcassets/jobIcons/time.imageset/time.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
21
IOS_study/Assets.xcassets/jobIcons/want.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "want.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
IOS_study/Assets.xcassets/jobIcons/want.imageset/want.png
vendored
Normal file
After Width: | Height: | Size: 2.0 KiB |
21
IOS_study/Assets.xcassets/view.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "冰岛极光风景4k壁纸3840x2160_彼岸图网.jpg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
IOS_study/Assets.xcassets/view.imageset/冰岛极光风景4k壁纸3840x2160_彼岸图网.jpg
vendored
Normal file
After Width: | Height: | Size: 3.2 MiB |
137
IOS_study/Auth/AgreeView.swift
Normal file
@ -0,0 +1,137 @@
|
||||
//
|
||||
// AgreeView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/13.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct AgreeView: View {
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@Binding var agreeType: Int
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(spacing: 18) {
|
||||
VStack {
|
||||
Button { dismiss() } label: { Image(systemName: "xmark") }.push(to: .trailing).font(.title3).secondary()
|
||||
Text(agreeType == 0 ? "用户协议" : "隐私政策").size18b()
|
||||
}
|
||||
Text(agreeType == 0 ? userAgreement : privacyPolicy).size15().tc2().lineSpacing(6)
|
||||
}.padding()
|
||||
}.bg()
|
||||
}
|
||||
|
||||
let userAgreement = """
|
||||
一、用户协议总则
|
||||
1、本协议双方为 Tian Xiao (以下简称"运营方")和 Boss_tdcat(包括手机APP等产品提供的服务,以下简称"产品和服务")的注册用户(以下简称"用户")。
|
||||
2、用户在注册前请仔细阅读本协议的条款,并按照页面上的提示完成全部注册程序。
|
||||
3、用户在进行注册过程中点击“同意”按钮,即表示用户已充分知悉和完全接受本协议项下全部条款,进而与运营方达成本协议。
|
||||
4、运营方有权不时对本协议项下相关规则作出修改或补充,并于网站公布。用户若继续使用即视为您接受修订后的本协议条款。
|
||||
二、用户服务使用说明
|
||||
1、用户在注册时应按照注册提示填写准确的 手机号码 等相关个人资料,符合完整、准确、真实的要求。
|
||||
2、用户一旦注册成功,便成为运营方合法的注册用户。用户应对其账号安全负全部责任,并应对其用户名下所进行的所有行为和事件承担相应的法律责任。
|
||||
3、用户同意接受运营方通过或其他方式向用户发送有关商业信息。
|
||||
4、运营方不对用户所发布信息的删除或储存失败负责。
|
||||
5、运营方有判定用户的行为是否符合本网站服务条款要求的权利,如果用户违背了服务条款的规定,本网站有权对其用户所提供的网络服务进行中断或停止使用。
|
||||
6、运营方不提供账号删除服务,如果用户需要删除账号,请直接放弃使用即可。
|
||||
三、协议内容的变更和修订
|
||||
1、运营方有权在必要时修改服务条款,修改后的协议可以在运营方上查看。
|
||||
2、用户如果不同意运营方所改动的内容,可自行停止使用本站网络服务。
|
||||
3、如果用户继续享用本站网络服务,则视为同意接受本网站服务条款的变动。
|
||||
4、运营方可随时根据实际情况中断或终止一项或多项网络服务而无需对任何用户或第三方承担任何责任,如用户对一项或多项网络服务的中断或终止有异议,可以行使如下权利:
|
||||
(1)自行停止使用运营方的网络服务。
|
||||
(2)通知运营方停止对该用户的服务。 结束用户服务后,用户使用网络服务的权利立即终止,从终止时起,用户没有权利再进行处理任何未完成的信息或服务,运营方也没有义务为其传送任何未处理的信息或未完成的服务给用户或任何第三方。
|
||||
四、用户隐私保护
|
||||
运营方将严格履行用户个人隐私保密义务,承诺不公开、编辑或透露用户个人信息,但以下特殊情况除外:
|
||||
1、经注册用户事先许可授权;
|
||||
2、遵守国家法律法规或配合相关政府部门的要求;
|
||||
3、遵从运营方合法服务程序;
|
||||
4、为维护社会公众利益以及运营方的合法权益所必须。
|
||||
五、注册用户的权利与义务
|
||||
1、注册用户在使用运营方产品和服务时,必须遵守中华人民共和国相关法律法规的规定,用户应同意将不会利用本服务进行任何违法或不正当的活动,否则用户将自行承担由此产生的一切法律责任。
|
||||
2、用户在账号使用过程中不得上载、展示、张贴、传播或以其它方式传送含有下列内容之一的信息:
|
||||
(1)危害国家安全,泄露国家秘密,颠覆国家政权,破坏国家统一的;
|
||||
(2)损害国家荣誉和利益的;
|
||||
(3)煽动民族仇恨、民族歧视、破坏民族团结的;
|
||||
(4)破坏国家宗教政策,宣扬邪教和封建迷信的;
|
||||
(5)散布谣言,扰乱社会秩序,破坏社会稳定的;
|
||||
(6)散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的;
|
||||
(7)侮辱或者诽谤他人,侵害他人合法权利的;
|
||||
(8)含有虚假、有害、胁迫、侵害他人隐私、骚扰、侵害、中伤、粗俗、猥亵、或其它道德上令人反感的内容。
|
||||
3、不得为任何非法目的而使用网络服务系统。
|
||||
4、不得利用运营方产品和服务故意制作、传播计算机病毒等破坏性程序,或其他从事任何危害计算机信息网络安全的行为。
|
||||
5、若用户行为违反上述约定,运营方有权作出独立判断并立即取消用户的服务账号,用户应对自己网上的行为承担一切法律责任,运营方的系统记录有可能作为用户违反法律的证据提交给相关主管部门。
|
||||
6、用户应同意保障和维护运营方全体成员及其他用户的利益,如因违反本协议或违反有关的法律法规而给运营方或任何第三者造成损失,用户应承担因此产生的法律责任。
|
||||
六、运营方网络服务内容的所有权
|
||||
1、运营方定义的网络服务内容包括但不限于:招聘信息等。该等内容均受《著作权法》、《商标法》、《专利法》、《计算机软件保护条例》及其他相关法律法规的保护。
|
||||
七、免责声明
|
||||
1、用户同意承担使用运营方产品和服务所存在的一切风险以及因使用网络服务而产生的一切后果,运营方对用户不承担任何责任。
|
||||
2、运营方不担保服务一定能满足用户的要求,也不担保服务不会中断,亦对服务的及时性,安全性及可能发生的技术错误均不作任何担保。
|
||||
3、任何由于黑客攻击、计算机病毒侵入或发作、政府管制、硬件故障、不可抗力等非运营方故意或严重过失而造成的用户个人资料泄露、丢失、被盗用、被篡改或服务暂定或终止的,对用户可能造成的风险或损失,运营方不承担法律责任。
|
||||
八、其他约定
|
||||
1、用户同意因本平台服务产生的任何争议均适用中华人民共和国法律,相关争议任何一方可向运营方住所地人民法院提起诉讼解决。
|
||||
2、本协议中的标题仅为方便而设,不影响对于条款本身的解释。本协议中的任何条款无论因何种原因完全或部分无效或不具有执行力,其余条款仍应具有约束力。
|
||||
九、联系我们
|
||||
如果您对本协议或本服务有任何疑问、意见或建议,可通过以下方式与我们联系:
|
||||
(1)拨打客服热线电话13123456789
|
||||
(2)发送电子邮件至:mail@tdcat.cn
|
||||
(3)联系地址:797687,联系人:Tian Xiao
|
||||
(完)
|
||||
"""
|
||||
let privacyPolicy = """
|
||||
欢迎您访问我们的产品。 Boss_tdcat (包括手机APP等产品提供的服务,以下简称“产品和服务”)是由 Tian Xiao (以下简称“我们”)开发并运营的。 确保用户的数据安全和隐私保护是我们的首要任务, 本隐私政策载明了您访问和使用我们的产品和服务时所收集的数据及其处理方式。
|
||||
请您在继续使用我们的产品前务必认真仔细阅读并确认充分理解本隐私政策全部规则和要点, 一旦您选择使用,即视为您同意本隐私政策的全部内容,同意我们按其收集和使用您的相关信息。 如您在在阅读过程中,对本政策有任何疑问,可联系我们的客服咨询, 请通过 mail@tdcat.cn 或产品中的反馈方式与我们取得联系。 如您不同意相关协议或其中的任何条款的,您应停止使用我们的产品和服务。
|
||||
本隐私政策帮助您了解以下内容:
|
||||
一、定义;
|
||||
二、我们如何收集和使用您的个人信息;
|
||||
三、我们如何存储和保护您的个人信息;
|
||||
四、我们如何共享、转让、公开披露您的个人信息;
|
||||
五、本政策如何更新;
|
||||
六、如何联系我们;
|
||||
一、定义
|
||||
我们:指Tian Xiao。
|
||||
个人信息:指以电子或者其他方式记录的与已识别或者可识别的自然人有关的各种信息,不包括匿名化处理后的信息。
|
||||
个人敏感信息:指一旦泄露或者非法使用,可能导致个人受到歧视或者人身、财产受到严重危害的个人信息,包括种族、民族、宗教信仰、个人生物特征、医疗健康、金融账户、个人行踪等信息(我们将在本隐私政策中对具体个人敏感信息以粗体进行显著标识)。
|
||||
未成年人:指不满18周岁的自然人。
|
||||
儿童:指不满14周岁的自然人。
|
||||
二、我们如何收集和使用您的个人信息
|
||||
个人信息是指以电子或者其他方式记录的能够单独或者与其他信息, 结合识别特定自然人身份或者反映特定自然人活动情况的各种信息。 我们根据《中华人民共和国网络安全法》和《信息安全技术个人信息安全规范》(GB/T 35273-2017) 以及其它相关法律法规的要求,并严格遵循正当、合法、必要的原则, 出于您使用我们提供的服务和/或产品等过程中而收集和使用您的个人信息,包括但不限于电话号码等。
|
||||
为接受我们全面的产品服务,您应首先注册一个用户账号,我们将通过它记录相关的数据。您所提供的所有信息均来自于您本人在注册时提供的数据。您准备使用的账户名、密码、您本人的联系方式, 我们可能通过发短信或者邮件的方式来验证您的身份是否有效。
|
||||
三、我们如何存储和保护您的个人信息
|
||||
作为一般规则,我们仅在实现信息收集目的所需的时间内保留您的个人信息。 我们会在对于管理与您之间的关系严格必要的时间内保留您的个人信息 (例如,当您开立帐户,从我们的产品获取服务时)。 出于遵守法律义务或为证明某项权利或合同满足适用的诉讼时效要求的目的, 我们可能需要在上述期限到期后保留您存档的个人信息,并且无法按您的要求删除。 如果您确认不再使用我们的产品和服务,并按照要求主动注销了您的账户,所有信息将被完全删除。
|
||||
我们使用符合业界标准的安全防护措施保护您提供的个人信息,并加密其中的关键数据, 防止其遭到未经授权访问、公开披露、使用、修改、损坏或丢失。我们会采取一切合理可行的措施,保护您的个人信息。 我们会使用加密技术确保数据的保密性;我们会使用受信赖的保护机制防止数据遭到恶意攻击。
|
||||
四、我们如何共享、转让、公开披露您的个人信息
|
||||
在管理我们的日常业务活动所需要时,为追求合法利益以更好地服务客户, 我们将合规且恰当的使用您的个人信息。出于对业务和各个方面的综合考虑,我们仅自身使用这些数据,不与任何第三方分享。
|
||||
我们可能会根据法律法规规定,或按政府主管部门的强制性要求,对外共享您的个人信息。 在符合法律法规的前提下,当我们收到上述披露信息的请求时,我们会要求必须出具与之相应的法律文件,如传票或调查函。 我们坚信,对于要求我们提供的信息,应该在法律允许的范围内尽可能保持透明。
|
||||
在以下情形中,共享、转让、公开披露您的个人信息无需事先征得您的授权同意:
|
||||
1、与国家安全、国防安全直接相关的;
|
||||
2、与犯罪侦查、起诉、审判和判决执行等直接相关的;
|
||||
3、出于维护您或其他个人的生命、财产等重大合法权益但又很难得到本人同意的;
|
||||
4、您自行向社会公众公开的个人信息;
|
||||
5、从合法公开披露的信息中收集个人信息的,如合法的新闻报道、政府信息公开等渠道。
|
||||
6、根据个人信息主体要求签订和履行合同所必需的;
|
||||
7、用于维护所提供的产品或服务的安全稳定运行所必需的,例如发现、处置产品或服务的故障;
|
||||
8、法律法规规定的其他情形。
|
||||
五、本政策如何更新
|
||||
我们的隐私政策可能变更。
|
||||
未经您明确同意,我们不会削减您在本政策项下应享有的权利。我们会在本页面上发布对本政策所作的任何变更。
|
||||
对于重大变更,我们还会提供更为显著的通知(比如官方公告、推送通知、发送短信或者电子邮件),并在本政策更新后您首次登陆时再次获取您的明示同意。若您在本政策更新且接到我们的通知后,点击同意或下一步,这表示您已充分阅读、理解并接受修订后的本政策。
|
||||
本政策所指的重大变更包括但不限于:
|
||||
1、我们的服务模式发生重大变化,比如处理个人信息的类型、目的和方式等;
|
||||
2、我们在所有权结构、组织架构等方面发生重大变化,比如业务调整、并购、破产等引起的所有者变更等;
|
||||
3、个人信息共享、转让或者公开披露的主要对象发生变化;
|
||||
4、您参与个人信息处理方面的权利及其行使方式发生重大变化;
|
||||
5、我们负责处理个人信息安全的责任部门、联络方式及投诉渠道发生变化时;
|
||||
6、个人信息安全影响评估报告表明存在高风险时。
|
||||
我们还会将本政策的旧版本存档,供您查阅。
|
||||
六、如何联系我们
|
||||
1.如果您对本政策有任何疑问、意见或建议,可通过以下方式与我们联系:
|
||||
(1)拨打客服热线电话13123456789
|
||||
(2)发送电子邮件至:mail@tdcat.cn
|
||||
(3)联系地址:797687,联系人:Tian Xiao
|
||||
我们的客服部门将会同个人信息保护部门30天内进行回复,并协助解决您的问题。
|
||||
2.如果您对我们的回复不满意,特别是我们的个人信息处理行为损害了您的合法权益,您还可以通过以下外部途径寻求解决方式:向Tian Xiao所在地人民法院提起诉讼,或向网信、工商、公安等监管部门进行投诉或举报。
|
||||
"""
|
||||
|
||||
}
|
29
IOS_study/Auth/AuthViewModel.swift
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// AuthViewModel.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/15.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import LeanCloud
|
||||
|
||||
@Observable final class AuthViewModel {
|
||||
var isLogin = LCApplication.default.currentUser != nil
|
||||
|
||||
init() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handleLogin), name: .loginSuccess, object: nil)//监听一条通知,nil表示监听所有的通知
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handlelogout), name: .logoutSuccess, object: nil)
|
||||
}
|
||||
|
||||
@objc func handleLogin() {
|
||||
isLogin = true
|
||||
}
|
||||
@objc func handlelogout() {
|
||||
isLogin = false
|
||||
}
|
||||
|
||||
deinit {//销毁当前对象中的所有监听
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
}
|
45
IOS_study/Auth/SignInPhoneView+.swift
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// SignInPhoneView+.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/10.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension SignInPhoneView {
|
||||
func stopTimer() { timer?.upstream.connect().cancel(); timer = nil }//停止定时器
|
||||
func getVerCode() async {//async -> 异步任务
|
||||
guard vm.phoneNum.isPhoneNum else { return }
|
||||
do {
|
||||
//todo 签名和模版要修改为审核通过的名字
|
||||
try await vm.getVerCode(phoneNum: "+86\(vm.phoneNum)", tName: "login", sName: "名称")
|
||||
} catch {
|
||||
print("获取验证码失败:\(error)")
|
||||
hud.show("获取验证码失败:\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func preLogin() {
|
||||
focusedField = nil
|
||||
if isAgree {
|
||||
Task { await login() }
|
||||
} else {
|
||||
showAgreeSheet = true
|
||||
}
|
||||
}
|
||||
//测试手机号:+8613123456789 验证码:797687
|
||||
func login() async {
|
||||
guard vm.phoneNum.isPhoneNum, vm.verCode.isVerCode else { hud.show("手机号或验证码错误"); return }
|
||||
vm.isLogining = true
|
||||
defer { vm.isLogining = false }
|
||||
do {
|
||||
try await vm.login()
|
||||
stopTimer()
|
||||
hud.show("登录成功")
|
||||
} catch {
|
||||
print("登录失败:\(error)")
|
||||
hud.show("登录失败:\(error)")
|
||||
}
|
||||
}
|
||||
}
|
149
IOS_study/Auth/SignInPhoneView.swift
Normal file
@ -0,0 +1,149 @@
|
||||
//
|
||||
// SignInPhoneView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/10.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
private let totalTime = 60
|
||||
|
||||
struct SignInPhoneView: View {
|
||||
enum FocusedField { case phoneNum, verCode }//聚焦在那个case
|
||||
@Environment(SignInPhoneViewModel.self) var vm
|
||||
@Environment(HUD.self) var hud
|
||||
@State var showVerBtn = true
|
||||
@State var timer: Publishers.Autoconnect<Timer.TimerPublisher>?//后面的 ? 表示可选类型
|
||||
@State var timeRemain = totalTime//倒计时
|
||||
@State var isAgree = false //是否同意协议,默认未勾选
|
||||
@State var showAgreeSheet = false//确认协议弹窗
|
||||
@State var showAgreeView = false//展示协议
|
||||
@State var agreeType = 0//0表示用户协议,1表示隐私政策
|
||||
@FocusState var focusedField: FocusedField?//光标聚焦用,设置为可选性,方便赋予nil
|
||||
var enableLoginBtn: Bool { vm.phoneNum.isPhoneNum && vm.verCode.isVerCode }
|
||||
|
||||
var totalNumCount: Int { vm.phoneNum.count + vm.verCode.count }
|
||||
|
||||
var body: some View {
|
||||
@Bindable var vm = vm
|
||||
NavigationStack {
|
||||
ScrollView {
|
||||
VStack(spacing: 33) {
|
||||
HStack {
|
||||
NavigationLink {
|
||||
SettginsView(naviPath: .constant(NavigationPath()), isLogin: false)
|
||||
} label: {
|
||||
Image(systemName: "gearshape")
|
||||
}
|
||||
Button {
|
||||
focusedField = nil//不聚焦光标 -> 收起键盘
|
||||
} label: {
|
||||
Image(systemName: "xmark")
|
||||
}
|
||||
}.push(to: .trailing).font(.title3).secondary()
|
||||
Text("手机号登陆").tc().font(.title).bold()
|
||||
.kerning(1.2)//字间距,默认1
|
||||
|
||||
VStack(spacing: 14) {
|
||||
HStack {
|
||||
TextField("请输入手机号", text: $vm.phoneNum).tc().kerning(1.2)
|
||||
.keyboardType(.numberPad)//数字键盘
|
||||
.focused($focusedField, equals: .phoneNum)
|
||||
if !vm.phoneNum.isEmpty {
|
||||
Image(systemName: "xmark.circle.fill").font(.title2).secondary().onTapGesture { vm.phoneNum = "" }
|
||||
}
|
||||
}
|
||||
exDivider()
|
||||
|
||||
HStack {
|
||||
//797687
|
||||
TextField("请输入验证码", text: $vm.verCode).tc().tc().kerning(1.2).keyboardType(.numberPad)
|
||||
.focused($focusedField, equals: .verCode)
|
||||
if vm.phoneNum.isPhoneNum || !showVerBtn {//是否为中国的手机号
|
||||
if showVerBtn {
|
||||
Button("获取验证码") {
|
||||
timeRemain = totalTime
|
||||
focusedField = .verCode//光标聚焦在case为verCode的区域
|
||||
timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()//每隔一秒,在主循环,自动执行
|
||||
showVerBtn = false
|
||||
Task {
|
||||
await getVerCode()
|
||||
}
|
||||
}.headline()
|
||||
|
||||
} else {
|
||||
if let timer {
|
||||
Text("重新发送(\(timeRemain)s)").headline().secondary()
|
||||
//监听异步任务
|
||||
.onReceive(timer) { _ in
|
||||
timeRemain -= 1
|
||||
if timeRemain <= 0 {
|
||||
stopTimer()
|
||||
showVerBtn = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exDivider()
|
||||
Button {
|
||||
preLogin()
|
||||
} label: {
|
||||
if vm.isLogining {
|
||||
ProgressView().appleStyleP()
|
||||
} else {
|
||||
Text("登录").appleStyle(disabled: !enableLoginBtn)
|
||||
}
|
||||
}.disabled(!enableLoginBtn || vm.isLogining)
|
||||
HStack(spacing: 3) {
|
||||
Button { isAgree.toggle()} label: {
|
||||
Image(systemName: isAgree ? "checkmark.circle.fill" : "circle")
|
||||
}.tint(isAgree ? .accent : .secondary).size16()
|
||||
agreeTextView.size14()
|
||||
}
|
||||
}.font(.title3)
|
||||
}.padding(33)
|
||||
}
|
||||
.scrollDismissesKeyboard(.immediately).bg()//滚动时立刻收起键盘
|
||||
.fullScreenCover(isPresented: $showAgreeView, content: { AgreeView(agreeType: $agreeType) })
|
||||
.sheet(isPresented: $showAgreeSheet) {//弹窗
|
||||
VStack(spacing: 18) {
|
||||
agreeTextView.size15()
|
||||
Button {
|
||||
isAgree = true//同意全部协议
|
||||
showAgreeSheet = false//关闭弹窗
|
||||
Task { await login() }//登录
|
||||
} label: {
|
||||
Text("同意并登录").font(.body).push(to: .center).padding(.vertical, 6)
|
||||
}.buttonStyle(.borderedProminent)
|
||||
}.padding(33)
|
||||
.presentationDetents([.height(200)])//指定弹窗展示多少
|
||||
// .presentationDetents([.medium, .large])//可以拖动弹窗展示多少
|
||||
}
|
||||
}
|
||||
.onChange(of: totalNumCount) {
|
||||
if totalNumCount == 17 {
|
||||
preLogin()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var agreeTextView: some View {
|
||||
HStack(spacing: 3) {
|
||||
Text("我已阅读并同意").secondary()
|
||||
Button("用户协议") {
|
||||
agreeType = 0
|
||||
showAgreeView = true
|
||||
}
|
||||
Text("和").secondary()
|
||||
Button("隐私政策") {
|
||||
agreeType = 1
|
||||
showAgreeView = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
IOS_study/Auth/SignInPhoneViewModel.swift
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// SignInPhoneViewModel.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/10.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@Observable final class SignInPhoneViewModel {
|
||||
// @Environment(AuthViewModel.self) var vm
|
||||
var phoneNum = ""
|
||||
var verCode = ""
|
||||
var isLogining = false
|
||||
|
||||
|
||||
@MainActor func getVerCode(phoneNum: String, tName: String, sName: String) async throws {
|
||||
try await AuthManager.shared.getVerCode(phoneNum: phoneNum, tName: tName, sName: sName)
|
||||
}
|
||||
|
||||
@MainActor func login() async throws {
|
||||
let user = try await AuthManager.shared.login(phoneNum: phoneNum, verCode: verCode)
|
||||
// printf(user.mobilePhoneNumber?.value)
|
||||
|
||||
if let isNewUser = user.get("isNewUser")?.boolValue, !isNewUser {//不是首次登录
|
||||
|
||||
} else {//首次登录
|
||||
try user.set("isNewUser", value: false)
|
||||
try await user.save()//保存信息
|
||||
|
||||
try await UserManager.shared.create(user: user)
|
||||
}
|
||||
NotificationCenter.default.post(name: .loginSuccess, object: nil)//发送一条通知
|
||||
verCode = ""//清空验证码
|
||||
}
|
||||
|
||||
}
|
43
IOS_study/Constant/CJob.swift
Normal file
@ -0,0 +1,43 @@
|
||||
//
|
||||
// CJob.swift
|
||||
// 项目常量
|
||||
//
|
||||
// Created by CC-star on 2025/6/28.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let kJobLimitS = 2
|
||||
let kJobLimit = 10
|
||||
let kTFAndPHeight: CGFloat = 36
|
||||
|
||||
|
||||
let kBusinessArr = [ "互联网", "便利店", "饭馆", "制造业", "金融", "教育", "医疗健康", "零售", "房地产", "物流", "文化传媒", "咨询服务", "政府机关", "公共事业", "新能源", "旅游", "电子商务", "汽车", "餐饮", "IT服务" ]
|
||||
let kCityArr = [
|
||||
"北京", "上海", "广州", "深圳", "杭州",
|
||||
"南京", "武汉", "成都", "西安", "重庆",
|
||||
"天津", "苏州", "郑州", "长沙", "青岛",
|
||||
"大连", "宁波", "厦门", "无锡", "福州"
|
||||
]
|
||||
|
||||
let kTaxArr = ["详谈","报税","不报税"]
|
||||
let kWantNumArr = [1,2,3,4,5,6,7,8,9,10,13,15,16,63,75,856,345]
|
||||
let kNeedExpArr = ["经验不限","1年以内","1-3年","3-5年","5-10年"]
|
||||
let kNeedEduArr = ["中专","高中","大专","本科","硕士研究生","博士研究生","博士后"]
|
||||
let kNeedLanArr = ["Swift","Objective-c","Java","C","Vue","Golong","Kotlin"]
|
||||
let kNeedFrameArr = ["SwiftUI","UIKit","Spring","Jetpack Compose"]
|
||||
let kProvinceArr = [
|
||||
"北京", "天津", "上海", "重庆", // 直辖市
|
||||
"河北", "山西", "辽宁", "吉林", "黑龙江", // 华北、东北
|
||||
"江苏", "浙江", "安徽", "福建", "江西", "山东", // 华东
|
||||
"河南", "湖北", "湖南", // 华中
|
||||
"广东", "海南", // 华南
|
||||
"四川", "贵州", "云南", // 西南
|
||||
"陕西", "甘肃", "青海", // 西北
|
||||
"台湾", // 台湾省
|
||||
"内蒙古", "广西", "西藏", "宁夏", "新疆", // 自治区
|
||||
"香港", "澳门" // 特别行政区
|
||||
]
|
||||
let kContactTypeArr = ["微信","QQ","电话","邮件","其他"]
|
||||
|
||||
let jobReportsKey = "jobReports"//内存中的举报key
|
21
IOS_study/Constant/CUser.swift
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// CUser.swift
|
||||
// 基本常量定义
|
||||
//
|
||||
// Created by CC-star on 2025/6/14.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let kAppName = "数据猫"
|
||||
let kIconSpacing: CGFloat = 3
|
||||
let kStackSpacing: CGFloat = 14
|
||||
let kStackSpacingS: CGFloat = 9
|
||||
|
||||
let KdevContact = "IC_lucky"//客服微信
|
||||
|
||||
//LeanCloud
|
||||
let kAppID = "jHdEBD3pF6YLyDlJQXEn8bK1-gzGzoHsz"
|
||||
let kAppKey = "48dkfFMv5oa0pvtORJtkEZyM"
|
||||
let kServerURLStr = "https://boss.tdcat.cn"
|
||||
|
11
IOS_study/Constant/GFunc.swift
Normal file
@ -0,0 +1,11 @@
|
||||
//
|
||||
// GFunc.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/3.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
func sleep5() async throws { try await Task.sleep(nanoseconds: 5_000_000_000) }//延迟5s
|
||||
func sleep3() async throws { try await Task.sleep(nanoseconds: 3_000_000_000) }
|
19
IOS_study/Constant/GType.swift
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// GType.swift
|
||||
// 导航相关
|
||||
//
|
||||
// Created by CC-star on 2025/6/30.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum Navi1 {
|
||||
case PLJobsToPLJob
|
||||
}
|
||||
|
||||
enum Navi5 {
|
||||
case AddCompany,EditCompany
|
||||
case PLJobsToPLJob
|
||||
case PLJobToEdit
|
||||
case Tosetting
|
||||
}
|
35
IOS_study/Constant/GView.swift
Normal file
@ -0,0 +1,35 @@
|
||||
//
|
||||
// GView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/6/26.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
func exDivider() -> some View{
|
||||
Rectangle().fill(Color.labeBG).frame(height: 1)//分割线[系统自带的Divider有时候不显示]
|
||||
}
|
||||
|
||||
|
||||
//筛选
|
||||
func arrowIcon(_ showView: Bool) -> some View{
|
||||
showView ? Image(systemName: "arrowtriangle.up.fill").size9() : Image(systemName: "arrowtriangle.down.fill").size9()
|
||||
}
|
||||
|
||||
func listBottomEView() -> some View{
|
||||
Color.clear.frame(height: 120).listRowStyle()
|
||||
}
|
||||
|
||||
func postBottomEView() -> some View{
|
||||
Color.clear.frame(height: 120)
|
||||
}
|
||||
|
||||
func BprogressView(text: String = "加载中") -> some View {
|
||||
VStack {
|
||||
ProgressView()
|
||||
Text(text).font(.subheadline).secondary()
|
||||
}.padding(25)
|
||||
.background(.thinMaterial)//毛玻璃效果
|
||||
.radius(6)
|
||||
}
|
94
IOS_study/Extension/Foundation+.swift
Normal file
@ -0,0 +1,94 @@
|
||||
//
|
||||
// Foundation+.swift
|
||||
// 常规扩展
|
||||
//
|
||||
// Created by CC-star on 2025/6/29.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
//以1开头,再加10个数字【\d】(\\d -> 转义字符\ -> \d),并以数字结尾
|
||||
let kPhoneRegEx = "^1\\d{10}$"//中国手机号正则表达式
|
||||
|
||||
let kVerCodeRegEx = "^\\d{6}$"//验证码正则表达式
|
||||
extension NSRegularExpression {
|
||||
convenience init(_ pattern: String) {
|
||||
do {
|
||||
try self.init(pattern: pattern)
|
||||
} catch {
|
||||
fatalError("非法的正则表达式")//因不能确保调用父类的init函数
|
||||
}
|
||||
}
|
||||
func matches(_ string: String) -> Bool {
|
||||
let range = NSRange(location: 0, length: string.utf16.count)
|
||||
return firstMatch(in: string, options: [], range: range) != nil
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
var isBlank: Bool { self.isEmpty || self.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }//没有输入或者输入空格
|
||||
var trimmed: String { self.trimmingCharacters(in: .whitespacesAndNewlines) }
|
||||
var isPhoneNum: Bool {
|
||||
Int(self) != nil && NSRegularExpression(kPhoneRegEx).matches(self)
|
||||
//判断该字符串是否可以转化为int类型 nor 置空 -> 判断用户是否输入的是数字
|
||||
}
|
||||
var isVerCode: Bool { Int(self) != nil && NSRegularExpression(kVerCodeRegEx).matches(self) }
|
||||
//随机字母和数字组合
|
||||
static func randomString(_ length: Int) -> String{
|
||||
let letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
return String((0..<length).map{ _ in letters.randomElement()! })
|
||||
}
|
||||
}
|
||||
|
||||
extension Date {
|
||||
var daysFromNow: Int {
|
||||
Calendar.current.dateComponents([.day], from: self, to: .now).day ?? 0//??空合运算符
|
||||
}
|
||||
|
||||
|
||||
// let date = Date()
|
||||
// print(date.string(withFormat: "yyyy/MM/dd HH:mm")) // 2023/08/24 16:21
|
||||
// print(date.string(withFormat: "yyyy/MM/dd hh:mm")) // 2023/08/24 04:21
|
||||
var toStr: String {
|
||||
let year = Calendar.current.component(.year, from: self)
|
||||
let currentYear = Calendar.current.component(.year, from: Date())
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = (year == currentYear) ? "M-d" : "yyyy-M-d"
|
||||
return dateFormatter.string(from: self)
|
||||
}
|
||||
}
|
||||
|
||||
enum UserError: String, Error {
|
||||
case lcObjToDicError = "lc对象转换字典失败"
|
||||
case saveFileSuccessButNoURL = "保存文件成功,但未能获取文件的地址"
|
||||
case imageToheicDataError = "图片转换成heicData失败"
|
||||
case imageTooBig = "图片太大"
|
||||
case objectIdNotFound = "没找到objectId"
|
||||
}
|
||||
|
||||
enum NetworkError: Error {
|
||||
case invalidURL
|
||||
case notHTTPResponse
|
||||
case requestFailed(String)//枚举关联值
|
||||
}
|
||||
|
||||
|
||||
extension NetworkError: LocalizedError {//扩展NetworkError的中文提示
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .invalidURL:
|
||||
return "请求的网址错误"
|
||||
case .notHTTPResponse:
|
||||
return "响应不能转化为HTTPURLResponse"
|
||||
case .requestFailed(let errorMsg):
|
||||
return "请求失败:\(errorMsg)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Notification.Name {
|
||||
static let loginSuccess = Notification.Name("loginSuccess")
|
||||
static let logoutSuccess = Notification.Name("logoutSuccess")
|
||||
}
|
||||
|
182
IOS_study/Extension/LeanCloud+.swift
Normal file
@ -0,0 +1,182 @@
|
||||
//
|
||||
// LeanCloud+.swift
|
||||
// 对leancloud平台的扩展代码
|
||||
//
|
||||
// Created by CC-star on 2025/7/2.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import LeanCloud
|
||||
|
||||
|
||||
extension LCQuery {
|
||||
func getFirst() async throws -> LCObject {
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
_ = self.getFirst { result in
|
||||
switch result {
|
||||
case .success(object: let lcObject):
|
||||
continuation.resume(returning: lcObject)
|
||||
case .failure(let error):
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 异步获取匹配条件的对象数量
|
||||
func count() async throws -> Int {
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
self.count { result in
|
||||
switch result {
|
||||
case .success(let count):
|
||||
continuation.resume(returning: count)
|
||||
case .failure(let error):
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//返回lcobject数组
|
||||
func find() async throws -> [LCObject] {
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
self.find { result in
|
||||
switch result {
|
||||
case .success(let objects):
|
||||
continuation.resume(returning: objects)
|
||||
case .failure(let error):
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//此方法不用,仅供学习
|
||||
func find<T: Decodable>(as type: T.Type) async throws -> [T] {
|
||||
let lcObjects = try await withCheckedThrowingContinuation { continuation in
|
||||
self.find { result in
|
||||
switch result {
|
||||
case .success(objects: let lcObjects):
|
||||
continuation.resume(returning: lcObjects)
|
||||
case .failure(error: let error):
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
var res: [T] = []
|
||||
for lcObject in lcObjects {
|
||||
//dictionaryValue失效,无法转化成字典类型
|
||||
guard let lcDic = lcObject.dictionaryValue else { throw UserError.lcObjToDicError }
|
||||
let data = try JSONSerialization.data(withJSONObject: lcDic)
|
||||
let object = try JSONDecoder().decode(T.self, from: data)
|
||||
res.append(object)
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension LCObject {
|
||||
//将 LeanCloud 的保存方法操作保存包转为 async 方法
|
||||
func save() async throws {
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
_ = self.save() { result in
|
||||
switch result {
|
||||
case .success:
|
||||
//保存成功
|
||||
continuation.resume()
|
||||
case .failure(let error):
|
||||
//保存失败,抛出错误
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 异步删除方法
|
||||
func delete() async throws {
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
_ = self.delete { result in
|
||||
switch result {
|
||||
case .success:
|
||||
continuation.resume()
|
||||
case .failure(let error):
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 批量删除对象的异步方法
|
||||
static func delete(_ objects: [LCObject]) async throws {
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
self.delete(objects) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
continuation.resume()
|
||||
case .failure(let error):
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension LCSMSClient {
|
||||
//把requestShortMessage(请求验证码)扩展成async方法
|
||||
static func requestShortMessage(
|
||||
mobilePhoneNumber: String,
|
||||
templateName: String,
|
||||
signatureName: String
|
||||
) async throws {
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
LCSMSClient.requestShortMessage(
|
||||
mobilePhoneNumber: mobilePhoneNumber,
|
||||
templateName: templateName,
|
||||
signatureName: signatureName
|
||||
// ,variables: ["name": LCString("BOSS大直招"), "ttl": LCNumber("5")]
|
||||
) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
continuation.resume()
|
||||
case .failure(let error):
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension LCUser {
|
||||
//把signUpOrLogIn(验证码注册或登录)扩展成async方法
|
||||
static func signUpOrLogIn(mobilePhoneNumber: String, verificationCode: String) async throws -> LCUser {
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
_ = LCUser.signUpOrLogIn(mobilePhoneNumber: mobilePhoneNumber, verificationCode: verificationCode) { result in
|
||||
switch result {
|
||||
case .success(object: let user):
|
||||
continuation.resume(returning: user)
|
||||
case .failure(error: let error):
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension LCFile {
|
||||
func save() async throws -> String {
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
_ = self.save { result in
|
||||
switch result {
|
||||
case .success:
|
||||
if let url = self.url?.value {
|
||||
continuation.resume(returning: url) // 保存成功,返回文件 URL
|
||||
} else {
|
||||
let error = UserError.saveFileSuccessButNoURL // 自定义错误
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
case .failure(let error):
|
||||
continuation.resume(throwing: error) // 直接抛出 LeanCloud 的错误
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
99
IOS_study/Extension/View+.swift
Normal file
@ -0,0 +1,99 @@
|
||||
//
|
||||
// View+.swift
|
||||
// 样式扩展
|
||||
//
|
||||
// Created by CC-star on 2025/6/12.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension View{
|
||||
|
||||
//矩形圆角
|
||||
func radius(_ radius: CGFloat = 4) -> some View{ clipShape(RoundedRectangle(cornerRadius: radius)) }
|
||||
// func radius2(_ radius: CGFloat = 4) -> some View{ clipShape(RoundedRectangle(cornerRadius: radius)) }
|
||||
func border(radius: CGFloat = 4, borderColor: Color) -> some View{
|
||||
overlay { RoundedRectangle(cornerRadius: radius).strokeBorder(borderColor, lineWidth: 1) }
|
||||
}
|
||||
|
||||
//颜色
|
||||
func tc() -> some View{ foregroundStyle(.aTC) }
|
||||
func tc2() -> some View{ foregroundStyle(.aTC2) }
|
||||
func secondary() -> some View{ foregroundStyle(.secondary) }
|
||||
func accent() -> some View{ foregroundStyle(.accent) }
|
||||
func white() -> some View{ foregroundStyle(.white) }
|
||||
func red() -> some View{ foregroundStyle(.red) }
|
||||
func gray() -> some View{ foregroundStyle(.gray) }
|
||||
func purple() -> some View{ foregroundStyle(.purple) }
|
||||
func bg() -> some View{ background(Color.aBG) }
|
||||
func bgA() -> some View{ background(Color.accent) }
|
||||
func bg2() -> some View{ background(Color.aBG2) }
|
||||
|
||||
//字体大小
|
||||
func size9() -> some View{ font(.system(size: 9)) }
|
||||
func size11() -> some View{ font(.system(size: 11)) }
|
||||
func size12() -> some View{ font(.system(size: 12)) }
|
||||
func size13() -> some View{ font(.system(size: 13)) }
|
||||
func size13s() -> some View{ size13().secondary() }
|
||||
func size13sb() -> some View{ size13().secondary().bold() }
|
||||
func size14() -> some View{ font(.system(size: 14)) }
|
||||
func size15semib() -> some View{ font(.system(size: 15)).semib() }
|
||||
func size15() -> some View{ font(.system(size: 15)) }
|
||||
func size15sb() -> some View{ font(.system(size: 15)).secondary().bold() }
|
||||
func size15pb() -> some View{ font(.system(size: 15)).foregroundStyle(.pink).bold() }
|
||||
func size16() -> some View{ font(.system(size: 16)) }
|
||||
func size16semib() -> some View{ font(.system(size: 16)).semib() }
|
||||
func size17() -> some View{ font(.system(size: 17)) }
|
||||
func headline() -> some View{ font(.headline) }
|
||||
func size18b() -> some View{ font(.system(size: 18)).bold() }
|
||||
func size18ab() -> some View{ font(.system(size: 18)).bold().accent() }
|
||||
func size20b() -> some View{ font(.title3).bold() }
|
||||
|
||||
//字体粗细
|
||||
func med() -> some View{ fontWeight(.medium) }
|
||||
func semib() -> some View{ fontWeight(.semibold) }
|
||||
|
||||
func push(to alignment: Alignment) -> some View {
|
||||
frame(maxWidth: .infinity, alignment: alignment)
|
||||
}
|
||||
|
||||
//List多行文本必备
|
||||
func allowMulti() -> some View{
|
||||
fixedSize(horizontal: false, vertical: true).textSelection(.enabled).multilineTextAlignment(.leading)
|
||||
}
|
||||
//图片相关
|
||||
func thumbFrame() -> some View {
|
||||
frame(width: 100, height: 100)
|
||||
}
|
||||
func thumbFrameS(aspectRatio: CGFloat = 1) -> some View {
|
||||
frame(width: 40, height: 40 / aspectRatio)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Color{
|
||||
static let labeBG = Color(.systemFill)
|
||||
static let labelBG2 = Color(.systemGray5)
|
||||
static let bg = Color.aBG
|
||||
static let bg2 = Color.aBG2
|
||||
}
|
||||
|
||||
|
||||
// MARK: - 让右滑返回上一页有效
|
||||
extension UINavigationController: @retroactive UIGestureRecognizerDelegate {
|
||||
override open func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
interactivePopGestureRecognizer?.delegate = self
|
||||
}
|
||||
|
||||
public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return viewControllers.count > 1 }
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { true }
|
||||
}
|
||||
|
||||
//收起键盘
|
||||
extension UIApplication {
|
||||
func hideKeyboard() {
|
||||
sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
||||
}
|
||||
}
|
36
IOS_study/Extension/View+Label.swift
Normal file
@ -0,0 +1,36 @@
|
||||
//
|
||||
// View+Label.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/6/12.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension View {
|
||||
func padding1() -> some View { padding(.vertical,2).padding(.horizontal,3) }
|
||||
func padding2() -> some View { padding(.vertical,3).padding(.horizontal,7) }
|
||||
func padding3() -> some View { padding(.vertical,4).padding(.horizontal,7) }
|
||||
func redBorder() -> some View { size12().red().semib().padding1().border(borderColor: Color.red) }
|
||||
func grayBorder() -> some View {size12().gray().semib().padding1().border(borderColor: Color.gray) }
|
||||
|
||||
func accentBorder() -> some View{ size12().accent().semib().padding1().border(borderColor: Color.accent) }
|
||||
|
||||
|
||||
func labelBG() -> some View{ size13().tc().semib().padding3()
|
||||
.background(Color.labeBG)//背景一般先padding,再background
|
||||
.radius()
|
||||
}
|
||||
|
||||
func labelBG2() -> some View{ size13().tc().med().padding3().background(Color.labelBG2).radius()}
|
||||
func labelPP() -> some View{ size13().purple().med().padding3().background(Color.purple.opacity(0.18)).radius() }
|
||||
func labelWA() -> some View{ size11().white().padding1().background(Color.accent).radius() }
|
||||
func labelWAL() -> some View{ size14().white().med().padding3().background(Color.accent).radius() }
|
||||
|
||||
//个人页的一些Label
|
||||
func labelPL() -> some View{ font(.caption).foregroundStyle(.blue).padding2().background(.blue.opacity(0.18)).radius() }
|
||||
//主题色背景 - 已下线、好评
|
||||
func labelTA() -> some View{ size13().tc2().med().padding3().background(.accent.opacity(0.18)).radius() }
|
||||
|
||||
|
||||
}
|
136
IOS_study/Extension/View+Style.swift
Normal file
@ -0,0 +1,136 @@
|
||||
//
|
||||
// View+Style.swift
|
||||
// style扩展
|
||||
//
|
||||
// Created by CC-star on 2025/6/12.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension View{
|
||||
|
||||
func btnStyle() -> some View{ size15().buttonStyle(.borderless).accent() }
|
||||
|
||||
func textStyle() -> some View{ size15().tc().lineSpacing(6) }
|
||||
func textStyle2() -> some View{ size15().tc2().lineSpacing(6) }
|
||||
func oneLineStyleMiddle() -> some View{ lineLimit(1).truncationMode(.middle) }
|
||||
func explainStyle() -> some View { size15sb().frame(maxWidth: .infinity, maxHeight: .infinity).padding() }//问题说明性提示文字
|
||||
//navi
|
||||
func naviBarStyle() -> some View {
|
||||
navigationBarTitleDisplayMode(.inline)
|
||||
.toolbarBackground(.visible, for: .navigationBar)
|
||||
.toolbarBackground(.aBG, for: .navigationBar) }
|
||||
|
||||
//筛选
|
||||
func labelStyleOF() -> some View { tc2().size15semib() }
|
||||
func badgeStyle() -> some View { size13().semib().white().padding(6).bgA()//筛选的个数样式
|
||||
.clipShape(Circle())//clipShape裁剪成圆形
|
||||
}
|
||||
|
||||
@ViewBuilder func selectBtnStyle(_ selected: Bool) -> some View {//ViewBuilder允许返回不同的视图
|
||||
if selected {
|
||||
buttonStyle(.borderedProminent)
|
||||
} else {
|
||||
tint(Color.aTC).buttonStyle(.bordered)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//List里面的东西
|
||||
func subTStyle() -> some View{
|
||||
size16semib().tc()
|
||||
}
|
||||
|
||||
func subTStyleL() -> some View{
|
||||
headline().tc()
|
||||
}
|
||||
|
||||
|
||||
|
||||
//cell
|
||||
func cellPaddingRadius() -> some View{//带radius
|
||||
padding(.vertical)
|
||||
.padding(.horizontal,12)
|
||||
.bg()
|
||||
.radius(8)
|
||||
}
|
||||
func cellOutPadding() -> some View{
|
||||
padding([.top,.horizontal],9)
|
||||
//.padding(.bottom,10)
|
||||
}
|
||||
|
||||
//list
|
||||
func listRowStyle() -> some View{
|
||||
listRowInsets(EdgeInsets())
|
||||
.listRowSeparator(.hidden)
|
||||
.listRowBackground(Color.clear)
|
||||
}
|
||||
|
||||
func listStyle() -> some View{
|
||||
listStyle(.inset)
|
||||
.scrollContentBackground(.hidden)
|
||||
.bg2()
|
||||
.environment(\.defaultMinListRowHeight,0) }
|
||||
|
||||
func listRowStyleLoading2() -> some View {//加载更新数据视图
|
||||
padding(.top).padding(.bottom,100).size13sb().push(to: .center).bg2().listRowStyle()
|
||||
}
|
||||
|
||||
//总的
|
||||
func cellStyle() -> some View{
|
||||
cellPaddingRadius().cellOutPadding().listRowStyle()
|
||||
}
|
||||
|
||||
//Form里面的
|
||||
func littleTStyle() -> some View{
|
||||
size13().tc2().fontWeight(.light)
|
||||
}
|
||||
func pStyle() -> some View{
|
||||
frame(height: kTFAndPHeight).border(borderColor: Color(.systemGray4))
|
||||
}
|
||||
func tfStyle() -> some View{//单行
|
||||
frame(height: kTFAndPHeight).padding(.horizontal,9).border(borderColor: Color(.systemGray4)).submitLabel(.done)
|
||||
}
|
||||
func tfStyleMultiS() -> some View{
|
||||
lineLimit(1...4).frame(minHeight: kTFAndPHeight).padding(.horizontal,9).border(borderColor: Color(.systemGray4)).lineSpacing(4)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
func tfStyleMultiM() -> some View{
|
||||
lineLimit(3...8).frame(minHeight: kTFAndPHeight).padding(.horizontal,9).border(borderColor: Color(.systemGray4)).lineSpacing(4)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
func tfStyleMultiL() -> some View{
|
||||
lineLimit(4...10).frame(minHeight: kTFAndPHeight).padding(.horizontal,9).border(borderColor: Color(.systemGray4)).lineSpacing(4)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
func conlonStyle() -> some View{
|
||||
bold().padding(.horizontal,2).offset(y: -1)
|
||||
}
|
||||
func toggleStyle(isTap: Bool) -> some View{
|
||||
size15().foregroundStyle(isTap ? .white : .aTC).padding(.horizontal,10).frame(height: kTFAndPHeight)
|
||||
.background(isTap ? Color.accent.gradient : Color.bg.gradient).radius()
|
||||
.border(borderColor: isTap ? .clear : .accent)
|
||||
}
|
||||
|
||||
func appleStyleS() -> some View{ font(.body).frame(maxWidth: .infinity).padding(.vertical, 6) }
|
||||
func appleStyle(disabled: Bool = false) -> some View{
|
||||
headline().white().frame(height: 55).frame(maxWidth: .infinity)
|
||||
.background(disabled ? Color.secondary.gradient : Color.accent.gradient).radius() }
|
||||
func appleStyleP() -> some View{ tint(.white).frame(height: 55).frame(maxWidth: .infinity).background(.accent.gradient).radius() }
|
||||
|
||||
|
||||
//个人页
|
||||
//文本
|
||||
func loadingJobStyle() -> some View { size13().white().frame(width: 150, height: 25).background(Color.accent.gradient).radius(6) }
|
||||
//progress
|
||||
func loadingJobStyleP() -> some View { tint(.white).frame(width: 150, height: 25).background(Color.accent.gradient).radius(6) }
|
||||
func settingsStyle() -> some View { tc().size16() }
|
||||
}
|
||||
|
||||
|
||||
extension Image{
|
||||
func iconStyle() -> some View {
|
||||
resizable().frame(width: 16, height: 16)
|
||||
}
|
||||
}
|
||||
|
59
IOS_study/IOS_studyApp.swift
Normal file
@ -0,0 +1,59 @@
|
||||
//
|
||||
// IOS_studyApp.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/6/3.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import LeanCloud
|
||||
|
||||
@main
|
||||
struct IOS_studyApp: App {
|
||||
// init(){
|
||||
//
|
||||
// }
|
||||
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
|
||||
@State var hud = HUD()
|
||||
@State var tabbarVM = TabBarViewModel()
|
||||
@State var signInPhoneVM = SignInPhoneViewModel()
|
||||
@State var reviewVM = ReviewViewModel()
|
||||
@State var ProfileVM = ProfileViewModel()
|
||||
@State var nwMonitor = NetworkMonitor()
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
TaBarView()
|
||||
// PostCompanyView(naviPath: .constant(NavigationPath()))
|
||||
.hud(isPresented: $hud.isPresented){
|
||||
Text(hud.title)
|
||||
}
|
||||
//注入
|
||||
.environment(reviewVM)
|
||||
.environment(ProfileVM)
|
||||
.environment(signInPhoneVM)
|
||||
.environment(hud)
|
||||
.environment(tabbarVM)
|
||||
.environment(nwMonitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
||||
UISegmentedControl.appearance().selectedSegmentTintColor = .accent
|
||||
UISegmentedControl.appearance().setTitleTextAttributes([.font: UIFont.systemFont(ofSize: 17,weight: .medium),.foregroundColor: UIColor(Color.aTC)], for: .normal)
|
||||
UISegmentedControl.appearance().setTitleTextAttributes([.font: UIFont.systemFont(ofSize: 17,weight: .semibold),.foregroundColor: UIColor(Color.white)], for: .selected)
|
||||
|
||||
|
||||
do {
|
||||
try LCApplication.default.set(
|
||||
id: kAppID,
|
||||
key: kAppKey,
|
||||
serverURL: kServerURLStr)
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
BIN
IOS_study/Img/Git/下线确定样式.webp
Normal file
After Width: | Height: | Size: 196 KiB |
BIN
IOS_study/Img/Git/发布公司.webp
Normal file
After Width: | Height: | Size: 203 KiB |
BIN
IOS_study/Img/Git/发布工作样式.webp
Normal file
After Width: | Height: | Size: 137 KiB |
BIN
IOS_study/Img/Git/工作卡片样式.webp
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
IOS_study/Img/Git/工作详情样式.webp
Normal file
After Width: | Height: | Size: 196 KiB |
BIN
IOS_study/Img/Git/弹窗提醒.webp
Normal file
After Width: | Height: | Size: 92 KiB |
BIN
IOS_study/Img/Git/我的页面.webp
Normal file
After Width: | Height: | Size: 231 KiB |
BIN
IOS_study/Img/chongwutubiao04.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
34
IOS_study/Job/PLJobView+.swift
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// PLJobView+.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/1.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension PLJobView {
|
||||
func toggleJobStatus() async {
|
||||
do {
|
||||
try await vm.toggleJobStatus(id: job.id)
|
||||
profileVM.shouldRefreshMyPLJobs = true
|
||||
hud.show(isActive ? "上线成功" : "下线成功")//这里是相反的逻辑,应为之前已经反转过一次了
|
||||
} catch {
|
||||
print("修改pljob的状态失败:\(error)")
|
||||
hud.show("修改失败,请检查网络并重试")
|
||||
vm.job.isActive.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
func removeJob() async {
|
||||
do {
|
||||
try await vm.removeJob(id: job.id)
|
||||
profileVM.shouldRefreshMyPLJobs = true
|
||||
hud.show("删除成功")
|
||||
if !naviPath.isEmpty { naviPath.removeLast() }
|
||||
} catch {
|
||||
print("删除plJob失败:\(error)")
|
||||
hud.show("删除失败,请检查网络并重试")
|
||||
}
|
||||
}
|
||||
}
|
145
IOS_study/Job/PLJobView.swift
Normal file
@ -0,0 +1,145 @@
|
||||
//
|
||||
// PLJobView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/6/13.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Photos
|
||||
|
||||
struct PLJobView: View {
|
||||
var canEdit = false
|
||||
var showBoss = true
|
||||
@Environment(HUD.self) var hud
|
||||
@Environment(PLJobViewModel.self) var vm
|
||||
@Environment(ProfileViewModel.self) var profileVM
|
||||
@Environment(AuthViewModel.self) var authVM
|
||||
@Environment(ReviewViewModel.self) var reviewVM
|
||||
@Environment(\.displayScale) var displayScale//获取屏幕分辨率
|
||||
@State var showActiveDialog = false//是否上/下线
|
||||
@State var showRemoveDialog = false//是否移除
|
||||
@State var showRemoveAlert = false//移除警告
|
||||
@State var saveImage: UIImage?
|
||||
@State var openSetting = false
|
||||
@State var viewDidLoad = false
|
||||
@State var showReportDialog = false
|
||||
@State var showPostReview = false
|
||||
@Binding var naviPath : NavigationPath
|
||||
var job: PLJob { vm.job }
|
||||
var isActive:Bool{ vm.job.isActive }
|
||||
var titleAD: String { isActive ? "" : "[已下线]" }
|
||||
var isIng: Bool { vm.isRemoving || vm.isToggling }
|
||||
var shareImage: Image? { if let saveImage { return Image(uiImage: saveImage) } else { return nil } }
|
||||
var body: some View {
|
||||
List {
|
||||
tilteView//头部视图
|
||||
if showBoss { companyView }//公司视图
|
||||
detailView//工作详情
|
||||
contactView//应聘电话
|
||||
goRevireBtn
|
||||
listBottomEView()
|
||||
}.listStyle().navigationTitle("长期兼职").navigationBarTitleDisplayMode(.inline).navigationBarBackButtonHidden()
|
||||
//顶部返回键
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button {
|
||||
if !naviPath.isEmpty {//检查是否为最后一页
|
||||
naviPath.removeLast()//返回上一页
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "chevron.left").secondary().padding(.horizontal,6)
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
HStack(spacing: 3) {
|
||||
if !canEdit {
|
||||
Button("举报") { showReportDialog = true }
|
||||
.confirmationDialog("确定举报吗?", isPresented: $showReportDialog, titleVisibility: .visible) {
|
||||
Button("确定") {
|
||||
Task {
|
||||
await report()
|
||||
}
|
||||
}
|
||||
Button("取消", role: .cancel) { }
|
||||
}
|
||||
}
|
||||
|
||||
Button { Task { await saveImageToAlbum() } } label: { Text("保存") }
|
||||
if let shareImage {
|
||||
ShareLink(item: shareImage, preview: SharePreview("以长图形式分享", image: shareImage)) { Text("分享") }
|
||||
}
|
||||
}.tc2().size15()
|
||||
}
|
||||
}
|
||||
.alert("请允许保存图片权限", isPresented: $openSetting) {
|
||||
Button("再想想") { openSetting = false }
|
||||
Button("去设置") {
|
||||
if let url = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url) }
|
||||
}
|
||||
} message: { Text("请重新设置照片权限") }
|
||||
.sheet(isPresented: $showPostReview, content: {
|
||||
PostReviewView()
|
||||
})
|
||||
.onAppear {
|
||||
if !viewDidLoad {
|
||||
Task { generateSnapshot() }
|
||||
viewDidLoad = true
|
||||
}
|
||||
}
|
||||
}
|
||||
var shareView: some View {
|
||||
VStack(spacing: 0) {
|
||||
Text("招兼职").size18b()
|
||||
tilteView//头部视图
|
||||
companyView //公司视图
|
||||
detailView//工作详情
|
||||
contactView//应聘电话
|
||||
listBottomEView()
|
||||
Text("\(kAppName)App").accent().bold().padding()
|
||||
}.padding(.top, 60).padding(.bottom, 30)
|
||||
.frame(width: UIScreen.main.bounds.size.width)//按照用户的手机屏幕来得到宽度
|
||||
.bg2()
|
||||
}
|
||||
@MainActor func generateSnapshot() {
|
||||
let renderer = ImageRenderer(content: shareView)
|
||||
renderer.scale = displayScale
|
||||
saveImage = renderer.uiImage
|
||||
}
|
||||
@MainActor func saveImageToAlbum() async {
|
||||
if let saveImage {
|
||||
let currenStatus = PHPhotoLibrary.authorizationStatus(for: .addOnly)
|
||||
if currenStatus == .notDetermined {//未申请过权限
|
||||
let status = await PHPhotoLibrary.requestAuthorization(for: .addOnly)//添加只向相册添加相片的权限
|
||||
if status == .authorized {//第一次同意
|
||||
writeToAlbum(saveImage)
|
||||
} else if currenStatus == .authorized {//之前同意过
|
||||
writeToAlbum(saveImage)
|
||||
} else { openSetting = true }
|
||||
} else { hud.show("相片保存失败,请退出本页后重试") }
|
||||
}
|
||||
}
|
||||
func writeToAlbum(_ saveImage: UIImage) { UIImageWriteToSavedPhotosAlbum(saveImage, nil, nil, nil); hud.show("相片保存成功,请返回相册查看") }
|
||||
// MARK: - 举报
|
||||
func report() async {
|
||||
var reports = (UserDefaults.standard.array(forKey: jobReportsKey) as? [String]) ?? []
|
||||
if reports.contains(job.id) {
|
||||
hud.show("已举报,会尽快处理")
|
||||
} else {
|
||||
let userID = AuthManager.shared.getUserID()
|
||||
if !userID.isEmpty {
|
||||
if reports.count > 50 { reports.removeAll() }
|
||||
do {
|
||||
try await JobReportManger.shared.save(report: JobReport(jobID: job.id, jobType: "兼职", jobTitle: job.title, bossID: job.userID, userID: userID))
|
||||
reports.append(job.id)
|
||||
UserDefaults.standard.set(reports, forKey: jobReportsKey)
|
||||
hud.show("举报成功,请等候处理")
|
||||
} catch {
|
||||
print("举报失败:\(error)")
|
||||
hud.show("举报失败,请检查网络")
|
||||
}
|
||||
} else { hud.show("请先登录") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
103
IOS_study/Job/PLJobViewModel.swift
Normal file
@ -0,0 +1,103 @@
|
||||
//
|
||||
// PLJobViewModel.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/6/29.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
@Observable final class PLJobViewModel {
|
||||
var jobs: [PLJob] = []
|
||||
var currentPage = 0
|
||||
var isFcJobs = false
|
||||
var isJobsLoaded = false
|
||||
|
||||
//分页用
|
||||
var fcJobsErrMsg = ""
|
||||
var isFinshed = false
|
||||
|
||||
var filter = PLFilter()//筛选用,实例化PLFilter
|
||||
var order = PLOrder.updatedAt//排序用
|
||||
var isOF = false //排序筛选完成后滑动到顶部
|
||||
|
||||
var job = PLJob()
|
||||
var draftJob = PLJob()//草稿
|
||||
var isPosting = false//是否在提交
|
||||
var isRemoving = false
|
||||
var isToggling = false//上/下线
|
||||
|
||||
@MainActor func getJobs(isResFresh: Bool = false) async {
|
||||
if isFcJobs { return }//保证当前没有在获取数据状态,防止用户在请求数据时,再次请求数据
|
||||
isFcJobs = true//开始获取数据状态
|
||||
defer{ isFcJobs = false }//最后关闭获取数据状态
|
||||
isJobsLoaded = false
|
||||
fcJobsErrMsg = ""
|
||||
if isResFresh {
|
||||
currentPage = 0
|
||||
isFinshed = false
|
||||
}
|
||||
do {
|
||||
let skip = kJobLimit * currentPage
|
||||
let newJobs = try await PLJobManager.shared.findJobsByREST(limit: kJobLimit, skip: skip, filter: filter, order: order)
|
||||
if isResFresh {
|
||||
jobs = newJobs
|
||||
} else {
|
||||
jobs.append(contentsOf: newJobs)//讲一个数组添加到另外一个数组
|
||||
}
|
||||
currentPage += 1//分页
|
||||
isJobsLoaded = true//防止onappear多次加载
|
||||
|
||||
if newJobs.count < kJobLimit || newJobs.isEmpty {
|
||||
isFinshed = true
|
||||
}
|
||||
} catch {
|
||||
if isResFresh {
|
||||
jobs.removeAll()//置空
|
||||
}
|
||||
fcJobsErrMsg = "\(error)"
|
||||
print("获取plJobs失败:\(error)")
|
||||
}
|
||||
}
|
||||
@MainActor func postJob(isEditing: Bool = false) async throws {
|
||||
job = draftJob
|
||||
// job.updatedAT = Date()
|
||||
|
||||
//处理空格(必填部分)
|
||||
job.title = job.title.trimmed
|
||||
job.placeName = job.placeName.trimmed
|
||||
job.province = job.province.trimmed
|
||||
job.city = job.city.trimmed
|
||||
//处理空格(选填部分)
|
||||
if job.otherTime.isBlank { job.otherTime = "" } else { job.otherTime = job.otherTime.trimmed }
|
||||
if job.otherNeed.isBlank { job.otherNeed = "" } else { job.otherNeed = job.otherNeed.trimmed }
|
||||
if job.workContent.isBlank { job.workContent = "" } else { job.workContent = job.workContent.trimmed }
|
||||
if job.otherBenefit.isBlank { job.otherBenefit = "" } else { job.otherBenefit = job.otherBenefit.trimmed }
|
||||
job.userID = AuthManager.shared.getUserID()
|
||||
|
||||
//存到数据库
|
||||
try await PLJobManager.shared.save(job: job, isEditing: isEditing)
|
||||
// do {
|
||||
// try await PLJobManager.shared.saveByREST(job: job)//使用链接进行存储
|
||||
// } catch {
|
||||
// print(error.localizedDescription)//返回中文的报错
|
||||
// }
|
||||
}
|
||||
|
||||
@MainActor func toggleJobStatus(id: String) async throws {
|
||||
isToggling = true
|
||||
defer {
|
||||
isToggling = false
|
||||
}
|
||||
job.isActive.toggle()
|
||||
try await PLJobManager.shared.toggleJobStatus(id: id, isAictive: job.isActive)
|
||||
}
|
||||
@MainActor func removeJob(id: String) async throws {
|
||||
isRemoving = true
|
||||
defer { isRemoving = false }
|
||||
try await PLJobManager.shared.removeJob(id: id)
|
||||
}
|
||||
|
||||
}
|
||||
|
235
IOS_study/Job/PLJobsView+JobUI.swift
Normal file
@ -0,0 +1,235 @@
|
||||
//
|
||||
// PLJobsView+JobUI.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension PLJobView {
|
||||
// MARK: - 头部视图
|
||||
var tilteView: some View {
|
||||
VStack(alignment: .leading,spacing: kStackSpacing) {
|
||||
//第一层
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Text(job.business)
|
||||
.labelBG2()
|
||||
if job.updatedAt.daysFromNow <= 7 { Text("新到").labelWA() }
|
||||
if job.wantWorkNum > 0 {
|
||||
Text("\(job.wantWorkNum)").size13s()
|
||||
}
|
||||
}
|
||||
Text(job.title + titleAD).tc().size18b()
|
||||
}
|
||||
Spacer()
|
||||
Text("\(job.hourlyWage)").accent().headline()
|
||||
}.padding(.bottom,-2)
|
||||
//第二层
|
||||
HStack{
|
||||
Text(job.placeName)
|
||||
if job.tax == "不报税" {
|
||||
Text(job.tax).accentBorder()
|
||||
} else if job.tax == "报税" {
|
||||
Text(job.tax).redBorder()
|
||||
} else {
|
||||
Text(job.tax).grayBorder()
|
||||
}
|
||||
|
||||
Text("\(job.updatedAt.toStr)发布").secondary()
|
||||
}
|
||||
//第三层
|
||||
HStack(spacing: kStackSpacing){
|
||||
HStack {
|
||||
HStack(spacing: kIconSpacing) {
|
||||
//icon
|
||||
Image("station").iconStyle()
|
||||
Text("\(job.province)-\(job.city)").semib()
|
||||
//导航按钮:一键智能导航
|
||||
Button {
|
||||
let destination = LocationHelper.shared.buildSmartAddress(job: job)
|
||||
LocationHelper.shared.navigateToDestination(destination: destination) { errorMessage in
|
||||
if let message = errorMessage {
|
||||
hud.show(message)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Text("导航")
|
||||
}.btnStyle()
|
||||
}
|
||||
HStack(spacing: kIconSpacing) {
|
||||
Image("want").iconStyle()
|
||||
Text("招\(job.wantNum)人")
|
||||
}
|
||||
|
||||
if canEdit && job.userID == AuthManager.shared.getUserID(){
|
||||
HStack {
|
||||
Button("编辑") {
|
||||
vm.draftJob = job
|
||||
naviPath.append(Navi5.PLJobToEdit)
|
||||
}.buttonStyle(.borderless).accent().disabled(isIng)
|
||||
|
||||
Button { showActiveDialog = true } label: {
|
||||
if vm.isToggling { ProgressView().controlSize(.small) } else { Text(isActive ? "下线" : "上线") }
|
||||
}.buttonStyle(.borderless).accent().disabled(isIng)
|
||||
.confirmationDialog(isActive ? "确定下线吗" : "确定上线吗", isPresented: $showActiveDialog, titleVisibility: .visible) {
|
||||
Button("确定") {
|
||||
Task { await toggleJobStatus() }
|
||||
}
|
||||
Button("再想想", role: .cancel) { }
|
||||
}
|
||||
Button { showRemoveDialog = true } label: {
|
||||
if vm.isRemoving { ProgressView().controlSize(.small) } else { Text("删除") }
|
||||
}.buttonStyle(.borderless).accent().disabled(isIng)
|
||||
.confirmationDialog("确定删除吗", isPresented: $showRemoveDialog, titleVisibility: .visible) {
|
||||
Button("确定", role: .destructive) {
|
||||
showRemoveAlert = true
|
||||
}
|
||||
Button("再想想", role: .cancel) { }
|
||||
}.alert("删除之后不可恢复", isPresented: $showRemoveAlert) {
|
||||
Button("确定", role: .destructive) { Task { await removeJob() } }
|
||||
Button("取消", role: .cancel) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.textStyle2().cellStyle()
|
||||
}
|
||||
// MARK: - 公司视图
|
||||
var companyView: some View {
|
||||
Button {
|
||||
//todo
|
||||
} label: {
|
||||
HStack(spacing: kIconSpacing){
|
||||
Image("company").iconStyle()
|
||||
Text(job.companyName).size16semib().oneLineStyleMiddle()
|
||||
Spacer()
|
||||
Group {
|
||||
Text("更多工作")
|
||||
Image(systemName: "chevron.right")
|
||||
}.secondary()
|
||||
}
|
||||
}.textStyle().cellStyle().buttonStyle(.borderless)
|
||||
}
|
||||
// MARK: - 工作详情
|
||||
var detailView: some View {
|
||||
VStack(alignment: .leading,spacing:kStackSpacingS) {
|
||||
//公司时间
|
||||
VStack(alignment: .leading,spacing: kStackSpacingS) {
|
||||
HStack(spacing: kIconSpacing) {
|
||||
Image("time").iconStyle()
|
||||
Text("工作时间").subTStyle()
|
||||
}
|
||||
|
||||
HStack(spacing: 2) {
|
||||
Text("\(job.startHour)")
|
||||
Text(":").conlonStyle()
|
||||
Text(job.startMin == 0 ? "00" : "\(job.startMin)")
|
||||
Text("-").conlonStyle()
|
||||
Text("\(job.endHour)")
|
||||
Text(":").conlonStyle()
|
||||
Text(job.endMin == 0 ? "00" : "\(job.endMin)")
|
||||
}
|
||||
|
||||
if job.has2 {
|
||||
HStack(spacing: 2) {
|
||||
Text("\(job.startHour2)")
|
||||
Text(":").conlonStyle()
|
||||
Text(job.startMin2 == 0 ? "00" : "\(job.startMin2)")
|
||||
Text("-").conlonStyle()
|
||||
Text("\(job.endHour2)")
|
||||
Text(":").conlonStyle()
|
||||
Text(job.endMin2 == 0 ? "00" : "\(job.endMin2)")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Text("\(job.startHour)-\(job.endHour)").labelBG()
|
||||
// if job.has2 {
|
||||
// Text("\(job.startHour2)-\(job.endHour2)").labelBG()
|
||||
// }
|
||||
}
|
||||
|
||||
// Divider()
|
||||
exDivider()
|
||||
if !job.workContent.isEmpty {
|
||||
//工作内容
|
||||
VStack(alignment: .leading,spacing: kStackSpacingS) {
|
||||
HStack(spacing: kIconSpacing) {
|
||||
Image("content").iconStyle()
|
||||
Text("工作内容").subTStyle()
|
||||
}
|
||||
Text(job.workContent).allowMulti()
|
||||
}
|
||||
exDivider()
|
||||
}
|
||||
//工作要求
|
||||
VStack(alignment: .leading,spacing: kStackSpacingS) {
|
||||
HStack(spacing: kIconSpacing) {
|
||||
Image("need").iconStyle()
|
||||
Text("工作要求").subTStyle()
|
||||
}
|
||||
HStack {
|
||||
Text(job.needExp).labelWAL()
|
||||
Text(job.needEdu).labelWAL()
|
||||
}
|
||||
HStack {
|
||||
Text(job.needLan).labelWAL()
|
||||
Text(job.needFrame).labelWAL()
|
||||
}
|
||||
if !job.otherNeed.isEmpty {
|
||||
Text(job.otherNeed).allowMulti()
|
||||
}
|
||||
}
|
||||
|
||||
//公式福利
|
||||
exDivider()
|
||||
VStack(alignment: .leading,spacing: kStackSpacingS) {
|
||||
HStack(spacing: kIconSpacing) {
|
||||
Image("benefit").iconStyle()
|
||||
Text("公式福利").subTStyle()
|
||||
}
|
||||
HStack {
|
||||
if job.noOverTime {
|
||||
Text("不加班").labelWAL()
|
||||
}
|
||||
if job.moveFee {
|
||||
Text("交通费").labelWAL()
|
||||
}
|
||||
}
|
||||
//更多福利
|
||||
if !job.otherBenefit.isEmpty {
|
||||
Text(job.otherBenefit).allowMulti()
|
||||
}
|
||||
}
|
||||
}.textStyle2().cellStyle()
|
||||
}
|
||||
// MARK: - 应聘电话
|
||||
var contactView: some View {
|
||||
VStack(alignment: .leading,spacing:kStackSpacingS){
|
||||
Text("应聘方式(\(job.contactType))").subTStyleL()
|
||||
HStack {
|
||||
Text("\(job.contact)").size20b().tc().allowMulti()
|
||||
Spacer()
|
||||
Button("复制"){
|
||||
UIPasteboard.general.string = job.contact
|
||||
hud.show("复制成功")
|
||||
//todo弹窗提示
|
||||
}.btnStyle().padding(6)
|
||||
|
||||
|
||||
// Button {
|
||||
// UIPasteboard.general.string = "0423-1230-1241"
|
||||
// //todo弹窗提示
|
||||
// }
|
||||
// } label: {
|
||||
// Text("复制")
|
||||
// }.btnStyle().padding(6)
|
||||
}
|
||||
Text("若问起,请说是在测试软件上看到的").size14().purple()
|
||||
}.cellStyle()
|
||||
}
|
||||
}
|
31
IOS_study/Jobs/FilterView.swift
Normal file
@ -0,0 +1,31 @@
|
||||
//
|
||||
// FilterView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/8.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct FilterView: View {
|
||||
var body: some View {
|
||||
VStack(spacing: 14) {
|
||||
VStack(alignment: .leading) {
|
||||
Text("行业").labelStyleOF()
|
||||
LazyVGrid(columns: [GridItem(.adaptive(minimum: 70))]) {
|
||||
ForEach(kBusinessArr,id: \.self) { business in
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
Text(business).frame(maxWidth: .infinity)
|
||||
}.buttonStyle(.borderless)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.padding()
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
FilterView()
|
||||
}
|
68
IOS_study/Jobs/PLJobCellView.swift
Normal file
@ -0,0 +1,68 @@
|
||||
//
|
||||
// PLJobCellView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/6/12.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct PLJobCellView: View {
|
||||
let job: PLJob
|
||||
var body: some View {
|
||||
VStack(spacing: 8) {
|
||||
//第一层
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Text(job.business)
|
||||
.labelBG2()
|
||||
if job.updatedAt.daysFromNow <= 7 { Text("新到").labelWA() }
|
||||
if job.wantWorkNum > 0 {
|
||||
Text("\(job.wantWorkNum)").size13s()
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
Text(job.title).tc().headline().oneLineStyleMiddle()
|
||||
|
||||
if job.tax == "不报税" {
|
||||
Text(job.tax).accentBorder()
|
||||
} else if job.tax == "报税" {
|
||||
Text(job.tax).redBorder()
|
||||
} else {
|
||||
Text(job.tax).grayBorder()
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
Text("\(job.hourlyWage)").accent().headline()
|
||||
}
|
||||
//第二层
|
||||
OverflowGrid(horizontalSpacing: 5) {
|
||||
Text(job.needExp).labelBG()
|
||||
Text(job.needEdu).labelBG()
|
||||
Text(job.needLan).labelBG()
|
||||
Text(job.needFrame).labelBG()
|
||||
Text("\(job.startHour)-\(job.endHour)").labelBG()
|
||||
if job.has2 {
|
||||
Text("\(job.startHour2)-\(job.endHour2)").labelBG()
|
||||
}
|
||||
if job.moveFee { Text("交通费").labelPP() }
|
||||
if job.noOverTime { Text("不加班").labelPP() }
|
||||
|
||||
}.push(to: .leading)
|
||||
|
||||
//第三层
|
||||
HStack {
|
||||
Text(job.placeName).tc().lineLimit(1)
|
||||
Spacer()
|
||||
Text("\(job.province)-\(job.city)").accent().semib().oneLineStyleMiddle()
|
||||
}.size14()
|
||||
}
|
||||
.cellPaddingRadius().cellOutPadding()
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
PLJobCellView(job: PLJob.job)
|
||||
}
|
118
IOS_study/Jobs/PLJobsView+.swift
Normal file
@ -0,0 +1,118 @@
|
||||
//
|
||||
// PLJobsView+.swift,主要放置筛选、排序
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/10.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension PLJobsView {
|
||||
//筛选
|
||||
var filterBtn: some View {
|
||||
Button {
|
||||
showOrderView = false
|
||||
showFilterView.toggle()//反转
|
||||
} label: {
|
||||
HStack(spacing: 3) {
|
||||
Text("筛选")
|
||||
if vm.filter.totalCount > 0 {
|
||||
Text("\(vm.filter.totalCount)").badgeStyle()
|
||||
}
|
||||
arrowIcon(showFilterView)
|
||||
}.tc2().size15()
|
||||
}.disabled(vm.isFcJobs)
|
||||
}
|
||||
|
||||
var plFilterView: some View {
|
||||
VStack(spacing: 14) {
|
||||
VStack(alignment: .leading) {
|
||||
Text("行业").labelStyleOF()
|
||||
LazyVGrid(columns: [GridItem(.adaptive(minimum: 80))]) {
|
||||
ForEach(kBusinessArr,id: \.self) { business in
|
||||
Button {
|
||||
if vm.filter.business == business {
|
||||
vm.filter.business = nil
|
||||
} else {
|
||||
vm.filter.business = business
|
||||
}
|
||||
} label: {
|
||||
Text(business).frame(maxWidth: .infinity)
|
||||
}.selectBtnStyle(vm.filter.business == business)
|
||||
}
|
||||
}
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
Text("城市").labelStyleOF()
|
||||
LazyVGrid(columns: [GridItem(.adaptive(minimum: 70))]) {
|
||||
ForEach(kCityArr,id: \.self) { city in
|
||||
Button {
|
||||
if vm.filter.city == city {
|
||||
vm.filter.city = nil
|
||||
} else {
|
||||
vm.filter.city = city
|
||||
}
|
||||
} label: {
|
||||
Text(city).frame(maxWidth: .infinity)
|
||||
}.selectBtnStyle(vm.filter.city == city)
|
||||
}
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
Button {
|
||||
vm.filter = PLFilter()
|
||||
} label: {
|
||||
Text("清空").appleStyleS()
|
||||
}.buttonStyle(.bordered)
|
||||
Button {
|
||||
showFilterView = false
|
||||
getJobsByOF()
|
||||
} label: {
|
||||
Text("筛选").appleStyleS()
|
||||
}.tint(.aTC).buttonStyle(.borderedProminent)
|
||||
}.padding(.top, 9)
|
||||
}.size14().padding().bg()
|
||||
}
|
||||
//排序
|
||||
var orderBtn: some View {
|
||||
Button {
|
||||
showFilterView = false
|
||||
showOrderView.toggle()//反转
|
||||
} label: {
|
||||
HStack(spacing: 3) {
|
||||
Text(vm.order.rawValue)
|
||||
arrowIcon(showOrderView)
|
||||
}.tc2().size15()
|
||||
}.disabled(vm.isFcJobs)
|
||||
}
|
||||
//排序视图
|
||||
var plOrderView: some View {
|
||||
VStack(spacing: 14) {
|
||||
ForEach(PLOrder.allCases.indices, id: \.self) { index in
|
||||
let order = PLOrder.allCases[index]
|
||||
HStack {
|
||||
Text(order.rawValue).foregroundStyle(vm.order == order ? .accent : .aTC)
|
||||
Spacer()
|
||||
if vm.order == order { Image(systemName: "checkmark.circle.fill").accent() }
|
||||
}.contentShape(Rectangle())//指定点击区域为整个矩形
|
||||
.onTapGesture {
|
||||
vm.order = order
|
||||
showOrderView = false
|
||||
getJobsByOF()
|
||||
}
|
||||
if order != PLOrder.allCases.last { exDivider() }
|
||||
}
|
||||
}.size15().padding().bg()
|
||||
}
|
||||
|
||||
func getJobsByOF() {//筛选功能
|
||||
vm.isOF.toggle()
|
||||
if vm.filter == lastPLFilter && vm.order == lastPLOrder { return }//如果与上次筛选\排序的选择一样,则不请求
|
||||
lastPLFilter = vm.filter
|
||||
lastPLOrder = vm.order
|
||||
Task {
|
||||
await vm.getJobs(isResFresh: true)//刷新
|
||||
}
|
||||
}
|
||||
|
||||
}
|
158
IOS_study/Jobs/PLJobsView.swift
Normal file
@ -0,0 +1,158 @@
|
||||
//
|
||||
// PLJobsView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/6/12.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import LeanCloud
|
||||
|
||||
struct PLJobsView: View {
|
||||
@State var lastPLFilter = PLFilter()
|
||||
@State var showFilterView = false
|
||||
@State var showOrderView = false
|
||||
@State var lastPLOrder: PLOrder = .updatedAt
|
||||
@Environment(HUD.self) var hud
|
||||
@Environment(PLJobViewModel.self) var vm
|
||||
@Environment(TabBarViewModel.self) var TabBarVM
|
||||
@Environment(NetworkMonitor.self) var nt
|
||||
@State var naviPath = NavigationPath()
|
||||
var body: some View {
|
||||
NavigationStack(path: $naviPath) {
|
||||
ZStack {
|
||||
if !vm.jobs.isEmpty {
|
||||
ScrollViewReader { scrollView in
|
||||
List{
|
||||
Color.clear.frame(height: 1).listRowStyle().id("top")//透明空白视图,无明显影响
|
||||
ForEach(vm.jobs) {job in
|
||||
Button {
|
||||
vm.job = job
|
||||
naviPath.append(Navi1.PLJobsToPLJob)
|
||||
} label: {
|
||||
PLJobCellView(job: job)
|
||||
}.buttonStyle(.borderless)
|
||||
}.listRowStyle()
|
||||
|
||||
// Button("加载更多", action: {
|
||||
// loadMore()
|
||||
// }).buttonStyle(.borderless)
|
||||
Group {
|
||||
if vm.fcJobsErrMsg.isEmpty {
|
||||
if !vm.isFinshed {
|
||||
if vm.isFcJobs { Text("正在加载中...") } else { Text("正在加载中...").onAppear { loadMore() } }
|
||||
} else {
|
||||
if nt.isConnected {
|
||||
Text("已全部加载")
|
||||
} else {
|
||||
Text("当前无网络,无法加载更多数据,请联网后下拉刷新")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
HStack {
|
||||
Text("自动加载数据失败,请手动加载")
|
||||
Button("点击加载", action: loadMore).buttonStyle(.borderless).accent()
|
||||
}
|
||||
}
|
||||
}.listRowStyleLoading2()
|
||||
}.listStyle().refreshable { await refreashable() }
|
||||
.onChange(of: vm.isOF) {//尾随闭包
|
||||
withAnimation { scrollView.scrollTo("top", anchor: .top) }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//首次加载,还没有加载出数据
|
||||
if vm.fcJobsErrMsg.isEmpty {//没有错误
|
||||
if !vm.isFcJobs {
|
||||
if nt.isConnected && vm.filter.totalCount > 0 {
|
||||
//筛选后没有数据todo
|
||||
Text("没有找到符合筛选条件的在招职位,请更改筛选条件").explainStyle()
|
||||
} else {//取完数据后发现没有数据:1.没有联网 2.筛选后没有数据
|
||||
VStack(spacing: 9) {//没有联网【根据后端SDK的不同,有的直接走这里,有的直接抛出错误】
|
||||
Button {
|
||||
refreash()
|
||||
} label: {
|
||||
Text("点击刷新").appleStyle()
|
||||
}
|
||||
Text("网络问题,若刷新失败,请重启App").explainStyle()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Color.bg2//展示空白视图
|
||||
}
|
||||
} else {//有错误
|
||||
VStack(alignment: .leading, spacing: 9) {
|
||||
Button {
|
||||
refreash()
|
||||
} label: {
|
||||
Text("点击刷新").appleStyle()
|
||||
}
|
||||
Text("加载数据失败,请点击刷新")
|
||||
HStack {
|
||||
Text("若刷新失败,请截图并联系客服微信:\(KdevContact)")
|
||||
Button {
|
||||
UIPasteboard.general.string = KdevContact
|
||||
hud.show("复制成功")
|
||||
} label: {
|
||||
Text("复制").accent()
|
||||
}
|
||||
}
|
||||
exDivider()
|
||||
// HStack {
|
||||
Text("错误信息").push(to: .center)
|
||||
Text(vm.fcJobsErrMsg).red()
|
||||
// }
|
||||
}.explainStyle()
|
||||
}
|
||||
}
|
||||
}.naviBarStyle()
|
||||
.navigationDestination(for: Navi1.self) { navi in
|
||||
switch navi {
|
||||
case .PLJobsToPLJob : PLJobView(naviPath: $naviPath)
|
||||
}
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarLeading) { orderBtn }
|
||||
ToolbarItem(placement: .principal) { Text("好工作").tc().bold() }
|
||||
ToolbarItem(placement: .topBarTrailing) { filterBtn }
|
||||
}
|
||||
.overlay {
|
||||
if showFilterView || showOrderView {//展示筛选\排序视图
|
||||
Color.black.opacity(0.3)//半透明阴影视图
|
||||
.onTapGesture {//点击手势
|
||||
showFilterView = false
|
||||
showOrderView = false
|
||||
}
|
||||
}
|
||||
}
|
||||
.overlay(alignment: .top) {
|
||||
if showFilterView { plFilterView } else if showOrderView { plOrderView }
|
||||
}
|
||||
.overlay { if vm.isFcJobs { BprogressView() } }
|
||||
}.animation(.default, value: showFilterView)//对showFilterView使用默认【default】的动画效果
|
||||
.onAppear {
|
||||
getJobsOnce()
|
||||
refreshIFNeed()
|
||||
}
|
||||
}
|
||||
|
||||
func getJobsOnce() {
|
||||
if vm.isJobsLoaded { return }
|
||||
Task { await vm.getJobs() }
|
||||
}
|
||||
func refreshIFNeed() {
|
||||
if vm.isJobsLoaded && TabBarVM.refreshJobs && TabBarVM.jobType == "兼职" {
|
||||
vm.filter = PLFilter()
|
||||
vm.order = .updatedAt
|
||||
refreash()
|
||||
TabBarVM.refreshJobs = false
|
||||
}
|
||||
}
|
||||
func refreashable() async { await vm.getJobs(isResFresh: true) }
|
||||
func refreash() { Task { await refreashable() } }
|
||||
func loadMore() { Task { await vm.getJobs() } }
|
||||
}
|
||||
|
||||
#Preview {
|
||||
PLJobsView()
|
||||
}
|
45
IOS_study/Library/HUDView.swift
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// HUDView.swift
|
||||
// 界面悬浮提示框
|
||||
//
|
||||
// Created by CC-star on 2025/7/15.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
// 悬浮提示框
|
||||
extension View {
|
||||
func hud<Content: View> (isPresented: Binding<Bool>, @ViewBuilder content: () -> Content) -> some View {
|
||||
ZStack {
|
||||
self
|
||||
if isPresented.wrappedValue {
|
||||
HUDView(content: content).zIndex(1)
|
||||
}
|
||||
}.animation(.default, value: isPresented.wrappedValue)
|
||||
}
|
||||
}
|
||||
struct HUDView<Content: View>: View {
|
||||
@ViewBuilder let content: Content
|
||||
var body: some View {
|
||||
content.font(.subheadline).foregroundStyle(Color.aTCR)
|
||||
.padding(.horizontal, 4).padding(12)
|
||||
.background(RoundedRectangle(cornerRadius: 8).fill(Color.aBGR))
|
||||
}
|
||||
}
|
||||
@Observable final class HUD {
|
||||
var isPresented = false
|
||||
private(set) var title: String = ""
|
||||
private var dismissTimer: AnyCancellable? // 用于管理计时器-追加代码-因原版本多次按下后消失不了
|
||||
func show(_ title: String) {
|
||||
self.title = title
|
||||
isPresented = true
|
||||
dismissTimer?.cancel()// 取消之前的计时器,确保每次调用时重置-追加代码
|
||||
// 3秒后自动隐藏-追加代码
|
||||
dismissTimer = Timer.publish(every: 3, on: .main, in: .common)
|
||||
.autoconnect()
|
||||
.sink { [weak self] _ in
|
||||
self?.isPresented = false
|
||||
self?.dismissTimer?.cancel() // 确保计时器在执行后被取消
|
||||
}
|
||||
}
|
||||
}
|
22
IOS_study/Library/NetworkMonitor.swift
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// NetworkMonitor.swift
|
||||
// 网络监控
|
||||
//
|
||||
// Created by CC-star on 2025/7/8.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
|
||||
@Observable final class NetworkMonitor {
|
||||
private let networkMonitor = NWPathMonitor()
|
||||
private let workerQueue = DispatchQueue(label: "Monitor")
|
||||
var isConnected = false
|
||||
|
||||
init() {
|
||||
networkMonitor.pathUpdateHandler = { path in
|
||||
self.isConnected = path.status == .satisfied
|
||||
}
|
||||
networkMonitor.start(queue: workerQueue)
|
||||
}
|
||||
}
|
50
IOS_study/Library/OverflowGrid.swift
Normal file
@ -0,0 +1,50 @@
|
||||
import SwiftUI
|
||||
|
||||
//自动换行
|
||||
//https://stackoverflow.com/questions/58842453/hstack-with-wrap RelativeJoe的回答
|
||||
public struct OverflowGrid: Layout {
|
||||
private var horizontalSpacing: CGFloat
|
||||
private var vericalSpacing: CGFloat
|
||||
public init(horizontalSpacing: CGFloat, vericalSpacing: CGFloat? = nil) {
|
||||
self.horizontalSpacing = horizontalSpacing
|
||||
self.vericalSpacing = vericalSpacing ?? horizontalSpacing
|
||||
}
|
||||
public func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
|
||||
let height = subviews.max(by: {$0.dimensions(in: proposal).height > $1.dimensions(in: proposal).height})?.dimensions(in: proposal).height ?? 0
|
||||
var rows = [CGFloat]()
|
||||
subviews.indices.forEach { index in
|
||||
let rowIndex = rows.count - 1
|
||||
let subViewWidth = subviews[index].dimensions(in: proposal).width
|
||||
guard !rows.isEmpty else {
|
||||
rows.append(subViewWidth)
|
||||
return
|
||||
}
|
||||
let newWidth = rows[rowIndex] + subViewWidth + horizontalSpacing
|
||||
if newWidth < proposal.width ?? 0 {
|
||||
rows[rowIndex] += (rows[rowIndex] > 0 ? horizontalSpacing: 0) + subViewWidth
|
||||
}else {
|
||||
rows.append(subViewWidth)
|
||||
}
|
||||
}
|
||||
let count = CGFloat(rows.count)
|
||||
return CGSize(width: rows.max() ?? 0, height: count * height + (count - 1) * vericalSpacing)
|
||||
|
||||
}
|
||||
public func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
|
||||
let height = subviews.max(by: {$0.dimensions(in: proposal).height > $1.dimensions(in: proposal).height})?.dimensions(in: proposal).height ?? 0
|
||||
guard !subviews.isEmpty else {return}
|
||||
var x = bounds.minX
|
||||
var y = height/2 + bounds.minY
|
||||
subviews.indices.forEach { index in
|
||||
let subView = subviews[index]
|
||||
x += subView.dimensions(in: proposal).width/2
|
||||
subviews[index].place(at: CGPoint(x: x, y: y), anchor: .center, proposal: ProposedViewSize(width: subView.dimensions(in: proposal).width, height: subView.dimensions(in: proposal).height))
|
||||
x += horizontalSpacing + subView.dimensions(in: proposal).width/2
|
||||
if x > bounds.width {
|
||||
x = bounds.minX
|
||||
y += height + vericalSpacing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
139
IOS_study/Library/PinchZoomView.swift
Normal file
@ -0,0 +1,139 @@
|
||||
//
|
||||
// PinchZoomView.swift
|
||||
// 双指捏合放大扩展
|
||||
//
|
||||
// Created by CC-star on 2025/7/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
//图片查看器-用法:
|
||||
//Image("Zoom").pinchToZoom()
|
||||
class PinchZoomView: UIView {
|
||||
weak var delegate: PinchZoomViewDelgate?
|
||||
private(set) var scale: CGFloat = 0 {
|
||||
didSet { delegate?.pinchZoomView(self, didChangeScale: scale) }
|
||||
}
|
||||
|
||||
private(set) var anchor: UnitPoint = .center {
|
||||
didSet { delegate?.pinchZoomView(self, didChangeAnchor: anchor) }
|
||||
}
|
||||
|
||||
private(set) var offset: CGSize = .zero {
|
||||
didSet { delegate?.pinchZoomView(self, didChangeOffset: offset) }
|
||||
}
|
||||
|
||||
private(set) var isPinching: Bool = false {
|
||||
didSet { delegate?.pinchZoomView(self, didChangePinching: isPinching) }
|
||||
}
|
||||
|
||||
private var startLocation: CGPoint = .zero
|
||||
private var location: CGPoint = .zero
|
||||
private var numberOfTouches: Int = 0
|
||||
|
||||
init() {
|
||||
super.init(frame: .zero)
|
||||
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(pinch(gesture:)))
|
||||
pinchGesture.cancelsTouchesInView = false
|
||||
addGestureRecognizer(pinchGesture)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) { fatalError() }
|
||||
|
||||
@objc private func pinch(gesture: UIPinchGestureRecognizer) {
|
||||
switch gesture.state {
|
||||
case .began:
|
||||
isPinching = true
|
||||
startLocation = gesture.location(in: self)
|
||||
anchor = UnitPoint(x: startLocation.x / bounds.width, y: startLocation.y / bounds.height)
|
||||
numberOfTouches = gesture.numberOfTouches
|
||||
case .changed:
|
||||
if gesture.numberOfTouches != numberOfTouches {
|
||||
// If the number of fingers being used changes, the start location needs to be adjusted to avoid jumping.
|
||||
let newLocation = gesture.location(in: self)
|
||||
let jumpDifference = CGSize(width: newLocation.x - location.x, height: newLocation.y - location.y)
|
||||
startLocation = CGPoint(x: startLocation.x + jumpDifference.width, y: startLocation.y + jumpDifference.height)
|
||||
|
||||
numberOfTouches = gesture.numberOfTouches
|
||||
}
|
||||
scale = gesture.scale
|
||||
location = gesture.location(in: self)
|
||||
offset = CGSize(width: location.x - startLocation.x, height: location.y - startLocation.y)
|
||||
case .ended, .cancelled, .failed:
|
||||
isPinching = false
|
||||
//加动画--在源代码上改动的地方
|
||||
//withAnimation(.interactiveSpring(response: 0.2, dampingFraction: 0.75, blendDuration: 0.1))
|
||||
withAnimation(.interactiveSpring(response: 0.2, dampingFraction: 0.65, blendDuration: 0.1)) {
|
||||
scale = 1.0
|
||||
anchor = .center
|
||||
offset = .zero
|
||||
}
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protocol PinchZoomViewDelgate: AnyObject {
|
||||
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangePinching isPinching: Bool)
|
||||
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangeScale scale: CGFloat)
|
||||
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangeAnchor anchor: UnitPoint)
|
||||
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangeOffset offset: CGSize)
|
||||
}
|
||||
|
||||
struct PinchZoom: UIViewRepresentable {
|
||||
@Binding var scale: CGFloat
|
||||
@Binding var anchor: UnitPoint
|
||||
@Binding var offset: CGSize
|
||||
@Binding var isPinching: Bool
|
||||
|
||||
func makeCoordinator() -> Coordinator { Coordinator(self) }
|
||||
func makeUIView(context: Context) -> PinchZoomView {
|
||||
let pinchZoomView = PinchZoomView()
|
||||
pinchZoomView.delegate = context.coordinator
|
||||
return pinchZoomView
|
||||
}
|
||||
func updateUIView(_ pageControl: PinchZoomView, context: Context) { }
|
||||
class Coordinator: NSObject, PinchZoomViewDelgate {
|
||||
var pinchZoom: PinchZoom
|
||||
init(_ pinchZoom: PinchZoom) { self.pinchZoom = pinchZoom }
|
||||
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangePinching isPinching: Bool) {
|
||||
pinchZoom.isPinching = isPinching
|
||||
}
|
||||
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangeScale scale: CGFloat) {
|
||||
pinchZoom.scale = scale
|
||||
}
|
||||
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangeAnchor anchor: UnitPoint) {
|
||||
pinchZoom.anchor = anchor
|
||||
}
|
||||
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangeOffset offset: CGSize) {
|
||||
pinchZoom.offset = offset
|
||||
}
|
||||
}
|
||||
}
|
||||
struct PinchToZoom: ViewModifier {
|
||||
@State var scale: CGFloat = 1.0
|
||||
@State var anchor: UnitPoint = .center
|
||||
@State var offset: CGSize = .zero
|
||||
@State var isPinching: Bool = false
|
||||
func body(content: Content) -> some View {
|
||||
content.scaleEffect(scale, anchor: anchor).offset(offset)
|
||||
//原代码中有这个,但效果不好,需要在上面pinch中加withanimation才行,故注释
|
||||
// .animation(isPinching ? .none : .spring(), value: scale)
|
||||
// .animation(.spring(duration: 1), value: scale)
|
||||
.overlay(PinchZoom(scale: $scale, anchor: $anchor, offset: $offset, isPinching: $isPinching))
|
||||
// .onChange(of: isPinching) {
|
||||
// if !isPinching {
|
||||
// // 在松开手势时触发动画恢复
|
||||
// withAnimation(.spring()) {
|
||||
// scale = 1.0
|
||||
// offset = .zero
|
||||
// anchor = .center
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
extension View {
|
||||
func pinchToZoom() -> some View { self.modifier(PinchToZoom()) }
|
||||
}
|
29
IOS_study/Manager/AuthManager.swift
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// AuthManager.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/10.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import LeanCloud
|
||||
|
||||
final class AuthManager {
|
||||
static let shared = AuthManager()
|
||||
private init() {}
|
||||
|
||||
func getVerCode(phoneNum: String, tName: String, sName: String) async throws {
|
||||
try await LCSMSClient.requestShortMessage(mobilePhoneNumber: phoneNum, templateName: tName, signatureName: sName)
|
||||
}
|
||||
|
||||
func login(phoneNum: String, verCode: String) async throws -> LCUser {
|
||||
try await LCUser.signUpOrLogIn(mobilePhoneNumber: phoneNum, verificationCode: verCode)
|
||||
}
|
||||
func getUserID() -> String {
|
||||
LCApplication.default.currentUser?.objectId?.value ?? ""//获取dbuser的id
|
||||
}
|
||||
func getUser() -> LCUser? { LCApplication.default.currentUser }
|
||||
func logOut() {
|
||||
LCUser.logOut()
|
||||
}
|
||||
}
|
194
IOS_study/Manager/PLJobManager.swift
Normal file
@ -0,0 +1,194 @@
|
||||
//
|
||||
// PLJobManager.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/2.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import LeanCloud
|
||||
|
||||
final class PLJobManager {
|
||||
//单例模式
|
||||
static var shared = PLJobManager()
|
||||
private init() {}
|
||||
private let className = "PLJob"
|
||||
// //仅供学习
|
||||
// func findJobs() async throws -> [PLJob] {
|
||||
// let query = LCQuery(className: className)
|
||||
// query.whereKey(PLJob.CodingKeys.isActive.rawValue,.equalTo(true))//equalTo -> =
|
||||
// query.whereKey("updatedAt", .descending)//降序
|
||||
//
|
||||
// return try await query.find(as: PLJob.self)
|
||||
// }
|
||||
// MARK: - 取出所有工作
|
||||
func findJobsByREST(limit: Int, skip: Int, filter: PLFilter, order: PLOrder) async throws -> [PLJob] {
|
||||
// let urlStr = """
|
||||
// \(kServerURLStr)/1.1/classes/\(className)?where={\(PLJob.CodingKeys.isActive.rawValue): true }&order=-updatedAt
|
||||
// """
|
||||
var components = URLComponents(string: "\(kServerURLStr)/1.1/classes/\(className)")!
|
||||
|
||||
//筛选
|
||||
var whereDic: [String: Any] = [PLJob.CodingKeys.isActive.rawValue: true]
|
||||
if let business = filter.business {//按行业筛选
|
||||
whereDic[PLJob.CodingKeys.business.rawValue] = business
|
||||
}
|
||||
if let city = filter.city {//按城市筛选
|
||||
whereDic[PLJob.CodingKeys.city.rawValue] = city
|
||||
}
|
||||
//转换为字符串
|
||||
let whereData = try JSONSerialization.data(withJSONObject: whereDic)
|
||||
guard let whereStr = String(data: whereData, encoding: .utf8) else { throw URLError(.badURL) }
|
||||
|
||||
//排序
|
||||
var orderStr = ""
|
||||
switch order {
|
||||
case .updatedAt:
|
||||
orderStr = "-updatedAt"
|
||||
case .hourlywage:
|
||||
orderStr = "-hourlywage"
|
||||
}
|
||||
|
||||
components.queryItems = [
|
||||
URLQueryItem(name: "where", value: whereStr),
|
||||
URLQueryItem(name: "order", value: orderStr),
|
||||
URLQueryItem(name: "limit", value: "\(limit)"),
|
||||
URLQueryItem(name: "skip", value: "\(skip)")
|
||||
]
|
||||
guard let urlstr = components.url?.absoluteString else { throw URLError(.badURL) }
|
||||
|
||||
return try await RESTManager.shared.find(form: urlstr)
|
||||
}
|
||||
// MARK: - 取出我发布的工作
|
||||
func findJobsByREST(by userID: String, limit: Int, skip: Int) async throws -> [PLJob] {
|
||||
var components = URLComponents(string: "\(kServerURLStr)/1.1/classes/\(className)")!
|
||||
|
||||
//基础条件
|
||||
let whereDic: [String: Any] = [PLJob.CodingKeys.userID.rawValue: userID]
|
||||
|
||||
//转换为字符串
|
||||
let whereData = try JSONSerialization.data(withJSONObject: whereDic)
|
||||
guard let whereStr = String(data: whereData, encoding: .utf8) else { throw URLError(.badURL) }
|
||||
|
||||
//添加查询参数
|
||||
components.queryItems = [
|
||||
URLQueryItem(name: "where", value: whereStr),
|
||||
URLQueryItem(name: "order", value: "-updatedAt"),
|
||||
URLQueryItem(name: "limit", value: "\(limit)"),
|
||||
URLQueryItem(name: "skip", value: "\(skip)")
|
||||
]
|
||||
guard let urlstr = components.url?.absoluteString else { throw URLError(.badURL) }
|
||||
|
||||
return try await RESTManager.shared.find(form: urlstr)
|
||||
}
|
||||
// MARK: - 取出job数量【已上线/全部】
|
||||
func findJobsCount(by userID: String, onlyActive: Bool = false) async -> Int {
|
||||
let query = LCQuery(className: className)
|
||||
query.whereKey(PLJob.CodingKeys.userID.rawValue, .equalTo(userID))
|
||||
if onlyActive { query.whereKey(PLJob.CodingKeys.isActive.rawValue, .equalTo(true)) }
|
||||
return (try? await query.count()) ?? 0
|
||||
}
|
||||
|
||||
func save(job: PLJob, isEditing: Bool = false) async throws {
|
||||
// 构建对象
|
||||
var lcObject = LCObject(className: "PLJob")
|
||||
|
||||
if isEditing {
|
||||
let query = LCQuery(className: className)
|
||||
query.whereKey("id", .equalTo(job.id))
|
||||
lcObject = try await query.getFirst()
|
||||
}
|
||||
|
||||
// 为属性赋值
|
||||
try lcObject.set(PLJob.CodingKeys.id.rawValue, value: job.id)
|
||||
try lcObject.set(PLJob.CodingKeys.isActive.rawValue, value: job.isActive)
|
||||
try lcObject.set(PLJob.CodingKeys.title.rawValue, value: job.title)
|
||||
try lcObject.set(PLJob.CodingKeys.business.rawValue, value: job.business)
|
||||
try lcObject.set(PLJob.CodingKeys.tax.rawValue, value: job.tax)
|
||||
try lcObject.set(PLJob.CodingKeys.hourlyWage.rawValue, value: job.hourlyWage)
|
||||
try lcObject.set(PLJob.CodingKeys.wantNum.rawValue, value: job.wantNum)
|
||||
try lcObject.set(PLJob.CodingKeys.contactType.rawValue, value: job.contactType)
|
||||
try lcObject.set(PLJob.CodingKeys.contact.rawValue, value: job.contact)
|
||||
|
||||
//工作时间
|
||||
try lcObject.set(PLJob.CodingKeys.startHour.rawValue, value: job.startHour)
|
||||
try lcObject.set(PLJob.CodingKeys.startMin.rawValue, value: job.startMin)
|
||||
try lcObject.set(PLJob.CodingKeys.endHour.rawValue, value: job.endHour)
|
||||
try lcObject.set(PLJob.CodingKeys.endMin.rawValue, value: job.endMin)
|
||||
try lcObject.set(PLJob.CodingKeys.has2.rawValue, value: job.has2)
|
||||
try lcObject.set(PLJob.CodingKeys.startHour2.rawValue, value: job.startHour2)
|
||||
try lcObject.set(PLJob.CodingKeys.startMin2.rawValue, value: job.startMin2)
|
||||
try lcObject.set(PLJob.CodingKeys.endHour2.rawValue, value: job.endHour2)
|
||||
try lcObject.set(PLJob.CodingKeys.endMin2.rawValue, value: job.endMin2)
|
||||
try lcObject.set(PLJob.CodingKeys.otherTime.rawValue, value: job.otherTime)
|
||||
|
||||
//工作地点
|
||||
try lcObject.set(PLJob.CodingKeys.placeName.rawValue, value: job.placeName)
|
||||
try lcObject.set(PLJob.CodingKeys.province.rawValue, value: job.province)
|
||||
try lcObject.set(PLJob.CodingKeys.city.rawValue, value: job.city)
|
||||
try lcObject.set(PLJob.CodingKeys.workContent.rawValue, value: job.workContent)
|
||||
|
||||
//要求
|
||||
try lcObject.set(PLJob.CodingKeys.needExp.rawValue, value: job.needExp)
|
||||
try lcObject.set(PLJob.CodingKeys.needEdu.rawValue, value: job.needEdu)
|
||||
try lcObject.set(PLJob.CodingKeys.needLan.rawValue, value: job.needLan)
|
||||
try lcObject.set(PLJob.CodingKeys.needFrame.rawValue, value: job.needFrame)
|
||||
try lcObject.set(PLJob.CodingKeys.otherNeed.rawValue, value: job.otherNeed)
|
||||
|
||||
//福利
|
||||
try lcObject.set(PLJob.CodingKeys.moveFee.rawValue, value: job.moveFee)
|
||||
try lcObject.set(PLJob.CodingKeys.noOverTime.rawValue, value: job.noOverTime)
|
||||
try lcObject.set(PLJob.CodingKeys.otherBenefit.rawValue, value: job.otherBenefit)
|
||||
|
||||
//其他
|
||||
try lcObject.set(PLJob.CodingKeys.companyName.rawValue, value: job.companyName)
|
||||
try lcObject.set(PLJob.CodingKeys.userID.rawValue, value: job.userID)
|
||||
// try lcObject.set("updatedAT", value: job.updatedAT)
|
||||
|
||||
// 将对象保存到云端
|
||||
// _ = lcObject.save { result in
|
||||
// switch result {
|
||||
// case .success:
|
||||
// // 成功保存之后,执行其他逻辑
|
||||
// break
|
||||
// case .failure(error: let error):
|
||||
// // 异常处理
|
||||
// print(error)
|
||||
// }
|
||||
// }
|
||||
|
||||
try await lcObject.save()
|
||||
}
|
||||
|
||||
func saveByREST(job: PLJob) async throws {
|
||||
//
|
||||
// guard let url = URL(string: "\(kServerURLStr)/1.1/classes/PLJob") else {
|
||||
// throw NetworkError.invalidURL//抛出问题 -> 无效网址
|
||||
// }//有值则赋值给url,没有则进入else
|
||||
//
|
||||
try await RESTManager.shared.save(to: "\(kServerURLStr)/1.1/classes/\(className)", object: job)
|
||||
}
|
||||
// MARK: - 上/下线工作
|
||||
func toggleJobStatus(id: String, isAictive: Bool) async throws {
|
||||
let query = LCQuery(className: className)
|
||||
query.whereKey("id", .equalTo(id))
|
||||
let lcObject = try await query.getFirst()
|
||||
try lcObject.set(PLJob.CodingKeys.isActive.rawValue, value: isAictive)
|
||||
try await lcObject.save()
|
||||
}
|
||||
// MARK: - 删除工作
|
||||
func removeJob(id: String) async throws {
|
||||
let query = LCQuery(className: className)
|
||||
query.whereKey("id", .equalTo(id))
|
||||
let lcObject = try await query.getFirst()
|
||||
try await lcObject.delete()
|
||||
}
|
||||
// MARK: -删除某个用户发布的所有job
|
||||
func removeJobs(by userID: String) async {
|
||||
let query = LCQuery(className: className)
|
||||
query.whereKey(PLJob.CodingKeys.userID.rawValue, .equalTo(userID))
|
||||
guard let jobObjects = try? await query.find() else { return }
|
||||
try? await LCObject.delete(jobObjects)//批量删除
|
||||
}
|
||||
|
||||
}
|
106
IOS_study/Manager/RESTManager.swift
Normal file
@ -0,0 +1,106 @@
|
||||
//
|
||||
// RESTManager.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/3.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
final class RESTManager {
|
||||
static let shared = RESTManager()
|
||||
private init() {}//让RESTManager只在这个class在内部被实例化,不在外部实例化,只能在这个作用域里面被调用
|
||||
//通过REST API获取一条数据
|
||||
func findOne<T: Decodable>(form urlStr: String) async throws -> T {
|
||||
guard let url = URL(string: urlStr) else { throw URLError(.badURL) }
|
||||
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "GET"
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.setValue(kAppID, forHTTPHeaderField: "X-LC-Id")
|
||||
request.setValue(kAppKey, forHTTPHeaderField: "X-LC-Key")
|
||||
|
||||
let (data, response) = try await URLSession.shared.data(for: request)
|
||||
//print(String(data: data, encoding: .utf8) ?? "Invalid data")
|
||||
|
||||
guard let httpRespsonse = response as? HTTPURLResponse, 200..<300 ~= httpRespsonse.statusCode else { throw URLError(.badServerResponse) }
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
//decoder.keyDecodingStrategy = .convertFromSnakeCase//数据库字段是下划线写法的时候
|
||||
//把数据库里面的时间戳字符串转换成Date类型(了解)
|
||||
decoder.dateDecodingStrategy = .custom { decoder -> Date in
|
||||
let container = try decoder.singleValueContainer()
|
||||
let dateString = try container.decode(String.self)
|
||||
let isoFormatter = ISO8601DateFormatter()
|
||||
isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] // 支持毫秒
|
||||
if let date = isoFormatter.date(from: dateString) { return date }
|
||||
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date format: \(dateString)")
|
||||
}
|
||||
return try decoder.decode( T.self, from: data)
|
||||
}
|
||||
|
||||
|
||||
//通过REST API获取数组数据
|
||||
func find<T: Decodable>(form urlStr: String) async throws ->[T] {
|
||||
guard let url = URL(string: urlStr) else { throw URLError(.badURL) }
|
||||
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "GET"
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.setValue(kAppID, forHTTPHeaderField: "X-LC-Id")
|
||||
request.setValue(kAppKey, forHTTPHeaderField: "X-LC-Key")
|
||||
|
||||
let (data,_) = try await URLSession.shared.data(for: request)
|
||||
//print(String(data: data, encoding: .utf8) ?? "Invalid data")
|
||||
|
||||
guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
|
||||
let results = json["results"] as? [[String: Any]] else {
|
||||
throw URLError(.cannotParseResponse)
|
||||
}
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
//decoder.keyDecodingStrategy = .convertFromSnakeCase//数据库字段是下划线写法的时候
|
||||
//把数据库里面的时间戳字符串转换成Date类型(了解)
|
||||
decoder.dateDecodingStrategy = .custom { decoder -> Date in
|
||||
let container = try decoder.singleValueContainer()
|
||||
let dateString = try container.decode(String.self)
|
||||
let isoFormatter = ISO8601DateFormatter()
|
||||
isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] // 支持毫秒
|
||||
if let date = isoFormatter.date(from: dateString) { return date }
|
||||
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date format: \(dateString)")
|
||||
}
|
||||
|
||||
let tData = try JSONSerialization.data(withJSONObject: results)
|
||||
return try decoder.decode([T].self, from: tData)
|
||||
}
|
||||
|
||||
|
||||
//通过REST API上传数据
|
||||
func save<T: Encodable>(to urlStr: String, object: T) async throws {//T:代表通用类型,泛型,Encodable编码
|
||||
guard let url = URL(string: urlStr) else { throw NetworkError.invalidURL//抛出问题 -> 无效网址
|
||||
}//有值则赋值给url,没有则进入else
|
||||
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.setValue(kAppID, forHTTPHeaderField: "X-LC-Id")
|
||||
request.setValue(kAppKey, forHTTPHeaderField: "X-LC-Key")
|
||||
|
||||
let encoder = JSONEncoder()
|
||||
// encoder.keyEncodingStrategy = .convertToSnakeCase//数据字段是下划线的写法的时候
|
||||
request.httpBody = try encoder.encode(object)//注意:使用链接的方式请求LeanCloud REST需要去掉Date字段(或其他处理)
|
||||
|
||||
let (data, response) = try await URLSession.shared.data(for: request)//向服务器请求数据,并返回一个元组类型【一个数据,一个响应】
|
||||
|
||||
guard let httpRespsonse = response as? HTTPURLResponse else { throw NetworkError.notHTTPResponse }
|
||||
|
||||
if !(200..<300).contains(httpRespsonse.statusCode) {//如果不是 2开头的响应码 -> [200,300)
|
||||
let errRespsonse = try JSONSerialization.jsonObject(with: data) as? [String: Any]//Any:字典类型
|
||||
let errMsg = errRespsonse?["error"] as? String ?? "响应数据类型转换失败"
|
||||
//如果errRespsonse没有值,则返回error
|
||||
//如果上面的errRespsonse?["error"] as? String都不满足则返回:响应数据类型转换失败,【??】空壳运算符
|
||||
|
||||
throw NetworkError.requestFailed("错误码:\(httpRespsonse.statusCode),错误信息:\(errMsg)")
|
||||
}
|
||||
}
|
||||
}
|
25
IOS_study/Manager/Reportmanager.swift
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// Reportmanager.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import LeanCloud
|
||||
|
||||
class JobReportManger {
|
||||
static let shared = JobReportManger()
|
||||
private init() {}
|
||||
private let className = "JobReport"
|
||||
|
||||
func save(report: JobReport) async throws {
|
||||
let reportObject = LCObject(className: className)
|
||||
try reportObject.set(JobReport.CodingKeys.jobID.rawValue, value: report.jobID)
|
||||
try reportObject.set(JobReport.CodingKeys.jobType.rawValue, value: report.jobType)
|
||||
try reportObject.set(JobReport.CodingKeys.jobTitle.rawValue, value: report.jobTitle)
|
||||
try reportObject.set(JobReport.CodingKeys.bossID.rawValue, value: report.bossID)
|
||||
try reportObject.set(JobReport.CodingKeys.userID.rawValue, value: report.userID)
|
||||
try await reportObject.save()
|
||||
}
|
||||
}
|
29
IOS_study/Manager/StorageManager.swift
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// StorageManager.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/15.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import LeanCloud
|
||||
|
||||
final class StorageManager {
|
||||
static let shared = StorageManager()
|
||||
private init() { }
|
||||
|
||||
func saveImage(_ image: UIImage) async throws -> String {
|
||||
if let data = image.heicData() {
|
||||
|
||||
if Double(data.count) / (1024 * 1024) >= 4 {//大于4MB
|
||||
throw UserError.imageTooBig
|
||||
}
|
||||
|
||||
let file = LCFile(payload: .data(data: data))
|
||||
file.mimeType = "image/heic"
|
||||
return try await file.save()
|
||||
} else {
|
||||
throw UserError.imageToheicDataError
|
||||
}
|
||||
}
|
||||
}
|
66
IOS_study/Manager/UserManager.swift
Normal file
@ -0,0 +1,66 @@
|
||||
//
|
||||
// UserManager.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/14.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import LeanCloud
|
||||
|
||||
final class UserManager {
|
||||
static var shared = UserManager()
|
||||
private init() {}
|
||||
private let className = "DBUser"
|
||||
|
||||
func findUserByREST() async throws -> DBUser {
|
||||
let query = LCQuery(className: className)
|
||||
query.whereKey("id", .equalTo(AuthManager.shared.getUserID()))
|
||||
guard let objectId = (try await query.getFirst()).objectId?.value else { throw UserError.objectIdNotFound }
|
||||
let urlStr = "\(kServerURLStr)/1.1/classes/\(className)/\(objectId)"
|
||||
return try await RESTManager.shared.findOne(form: urlStr)
|
||||
}
|
||||
|
||||
func create(user: LCUser) async throws {
|
||||
let dbUser = LCObject(className: className)
|
||||
try dbUser.set(DBUser.CodingKeys.id.rawValue, value: user.objectId)
|
||||
var nickName = ""
|
||||
if let objectId = user.objectId?.value {
|
||||
nickName = objectId.suffix(4).uppercased()//取后四位,并字母大写
|
||||
} else {
|
||||
nickName = String.randomString(4)
|
||||
}
|
||||
try dbUser.set(DBUser.CodingKeys.nickName.rawValue, value: "数据猫用户\(nickName)")
|
||||
try await dbUser.save()
|
||||
}
|
||||
|
||||
func update(user: DBUser) async throws {
|
||||
let query = LCQuery(className: className)
|
||||
query.whereKey("id", .equalTo(AuthManager.shared.getUserID()))
|
||||
let userObject = try await query.getFirst()
|
||||
|
||||
try userObject.set(DBUser.CodingKeys.cName.rawValue, value: user.cName)
|
||||
try userObject.set(DBUser.CodingKeys.cLogoURLStr.rawValue, value: user.cLogoURLStr)
|
||||
try userObject.set(DBUser.CodingKeys.cImageURLStrs.rawValue, value: user.cImageURLStrs)
|
||||
try userObject.set(DBUser.CodingKeys.cAboutUS.rawValue, value: user.cAboutUS)
|
||||
try userObject.set(DBUser.CodingKeys.cProvince.rawValue, value: user.cProvince)
|
||||
try userObject.set(DBUser.CodingKeys.cCity.rawValue, value: user.cCity)
|
||||
|
||||
try await userObject.save()
|
||||
}
|
||||
|
||||
func setTo(filed: String, _ value: Bool) async throws {
|
||||
let query = LCQuery(className: className)
|
||||
query.whereKey("id", .equalTo(AuthManager.shared.getUserID()))
|
||||
let userObject = try await query.getFirst()
|
||||
try userObject.set(filed, value: value)
|
||||
try await userObject.save()
|
||||
}
|
||||
|
||||
func removeUser(id: String) async {
|
||||
let query = LCQuery(className: className)
|
||||
query.whereKey("id", .equalTo(id))
|
||||
guard let userObject = try? await query.getFirst() else { return }
|
||||
try? await userObject.delete()
|
||||
}
|
||||
}
|
90
IOS_study/Model/DBUser.swift
Normal file
@ -0,0 +1,90 @@
|
||||
//
|
||||
// DBUser.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/14.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct DBUser: Codable, Identifiable {//用户相关
|
||||
var id = UUID().uuidString//id
|
||||
var nickName = ""//昵称
|
||||
|
||||
var cName = ""//公式名字
|
||||
var cLogoURLStr = ""//公司logo
|
||||
var cImageURLStrs: [String] = []//公司相关图片
|
||||
var cAboutUS = ""//公式简介
|
||||
var cProvince = "重庆"//省份
|
||||
var cCity = ""//城市
|
||||
var cIsUploaded: Bool { !cName.isEmpty }//公司信息是否上传
|
||||
|
||||
var hasPLJobs = false//长期兼职
|
||||
var hasReviews = false//点评
|
||||
|
||||
enum CodingKeys: String,CodingKey {
|
||||
case id
|
||||
case nickName
|
||||
case cName
|
||||
case cLogoURLStr
|
||||
case cImageURLStrs
|
||||
case cAboutUS
|
||||
case cProvince
|
||||
case cCity
|
||||
case hasPLJobs
|
||||
case hasReviews
|
||||
}
|
||||
|
||||
func encode(to encoder: any Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(self.id, forKey: .id)
|
||||
try container.encode(self.nickName, forKey: .nickName)
|
||||
try container.encode(self.cName, forKey: .cName)
|
||||
try container.encode(self.cLogoURLStr, forKey: .cLogoURLStr)
|
||||
try container.encode(self.cImageURLStrs, forKey: .cImageURLStrs)
|
||||
try container.encode(self.cAboutUS, forKey: .cAboutUS)
|
||||
try container.encode(self.cProvince, forKey: .cProvince)
|
||||
try container.encode(self.cCity, forKey: .cCity)
|
||||
try container.encode(self.hasPLJobs, forKey: .hasPLJobs)
|
||||
try container.encode(self.hasReviews, forKey: .hasReviews)
|
||||
}
|
||||
|
||||
init(from decoder: any Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.id = try container.decode(String.self, forKey: .id)
|
||||
self.nickName = try container.decodeIfPresent(String.self, forKey: .nickName) ?? ""
|
||||
self.cName = try container.decodeIfPresent(String.self, forKey: .cName) ?? ""
|
||||
self.cLogoURLStr = try container.decodeIfPresent(String.self, forKey: .cLogoURLStr) ?? ""
|
||||
self.cImageURLStrs = try container.decodeIfPresent([String].self, forKey: .cImageURLStrs) ?? []
|
||||
self.cAboutUS = try container.decodeIfPresent(String.self, forKey: .cAboutUS) ?? ""
|
||||
self.cProvince = try container.decodeIfPresent(String.self, forKey: .cProvince) ?? "重庆"
|
||||
self.cCity = try container.decodeIfPresent(String.self, forKey: .cCity) ?? ""
|
||||
self.hasPLJobs = try container.decodeIfPresent(Bool.self, forKey: .hasPLJobs) ?? false
|
||||
self.hasReviews = try container.decodeIfPresent(Bool.self, forKey: .hasReviews) ?? false
|
||||
}
|
||||
|
||||
//先注释上面的init,在输入init生成下面的
|
||||
init(id: String = UUID().uuidString, nickName: String = "", cName: String = "", cLogoURLStr: String = "", cImageURLStrs: [String] = [], cAboutUS: String = "", cProvince: String = "重庆", cCity: String = "", hasPLJobs: Bool = false, hasReviews: Bool = false) {
|
||||
self.id = id
|
||||
self.nickName = nickName
|
||||
self.cName = cName
|
||||
self.cLogoURLStr = cLogoURLStr
|
||||
self.cImageURLStrs = cImageURLStrs
|
||||
self.cAboutUS = cAboutUS
|
||||
self.cProvince = cProvince
|
||||
self.cCity = cCity
|
||||
self.hasPLJobs = hasPLJobs
|
||||
self.hasReviews = hasReviews
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//struct Company: Codable {//公司相关
|
||||
// var name = ""//公式名字
|
||||
// var logoURLStr = ""//公司logo
|
||||
// var imageURLStrs: [String] = []//公司相关图片
|
||||
// var aboutUS = ""//公式简介
|
||||
// var province = "重庆"//省份
|
||||
// var city = ""//城市
|
||||
// var isUploaded: Bool { !name.isEmpty }//公司信息是否上传
|
||||
//}
|
25
IOS_study/Model/OF.swift
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// OF.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/10.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct PLFilter: Equatable {//Equatable表示可以进行比较
|
||||
var business: String?
|
||||
var city: String?
|
||||
var totalCount:Int {
|
||||
var count = 0
|
||||
if business != nil { count += 1 }
|
||||
if city != nil { count += 1 }
|
||||
return count
|
||||
}
|
||||
}
|
||||
|
||||
enum PLOrder: String, CaseIterable, Identifiable {
|
||||
var id: String { self.rawValue }
|
||||
case updatedAt = "按更新时间"
|
||||
case hourlywage = "按时给"
|
||||
}
|
217
IOS_study/Model/PLJob.swift
Normal file
@ -0,0 +1,217 @@
|
||||
//
|
||||
// PLJob.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/6/28.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
//添加或修改属性之后-->重新生成codingkeys(加上String),修改decode,注释decode,重新生成init,打开decode注释
|
||||
struct PLJob: Identifiable, Codable {//遵循莫个协议,Codable:编/解码合体
|
||||
var id = UUID().uuidString//id
|
||||
var isActive = true//是否上线
|
||||
var title = ""//标题
|
||||
var business = "互联网"//行业
|
||||
var tax = "详谈"//是否报税
|
||||
var hourlyWage = 100//时给
|
||||
var wantNum = 3//招几人
|
||||
var contactType = "微信"//联系方式类型
|
||||
var contact = ""//联系方式
|
||||
|
||||
//工作时间地点内容
|
||||
var startHour = 10//开始时间
|
||||
var startMin = 0//开始分钟
|
||||
var endHour = 20//结束时间
|
||||
var endMin = 0//结束分钟
|
||||
|
||||
//点击状态
|
||||
var has2 = false//是否第二个时间
|
||||
//第二个时间
|
||||
var startHour2 = 10//开始时间
|
||||
var startMin2 = 0//开始分钟
|
||||
var endHour2 = 20//结束时间
|
||||
var endMin2 = 0//结束分钟
|
||||
|
||||
var otherTime = ""//其他时间
|
||||
|
||||
var placeName = ""//完整地址
|
||||
var province = "北京"//省份
|
||||
var city = ""//城市
|
||||
var workContent = ""//工作内容
|
||||
|
||||
//工作要求
|
||||
var needExp = "1-3年"//经验
|
||||
var needEdu = "本科"//学历
|
||||
var needLan = "Swift"//语言
|
||||
var needFrame = "SwiftUI"//框架
|
||||
var otherNeed = ""//其他要求
|
||||
|
||||
//福利
|
||||
var moveFee = true//交通费
|
||||
var noOverTime = true//不加班
|
||||
var otherBenefit = ""//其他福利
|
||||
|
||||
|
||||
//其他
|
||||
var companyName = ""//公式名
|
||||
var userID = ""//用户id
|
||||
//var creatTime = ""
|
||||
var updatedAt = Date()//更新时间
|
||||
var wantWorkNum = 0//几人想去
|
||||
|
||||
enum CodingKeys: String,CodingKey {
|
||||
case id
|
||||
case isActive
|
||||
case title
|
||||
case business
|
||||
case tax
|
||||
case hourlyWage
|
||||
case wantNum
|
||||
case contactType
|
||||
case contact
|
||||
case startHour
|
||||
case startMin
|
||||
case endHour
|
||||
case endMin
|
||||
case has2
|
||||
case startHour2
|
||||
case startMin2
|
||||
case endHour2
|
||||
case endMin2
|
||||
case otherTime
|
||||
case placeName
|
||||
case province
|
||||
case city
|
||||
case workContent
|
||||
case needExp
|
||||
case needEdu
|
||||
case needLan
|
||||
case needFrame
|
||||
case otherNeed
|
||||
case moveFee
|
||||
case noOverTime
|
||||
case otherBenefit
|
||||
case companyName
|
||||
case userID
|
||||
case updatedAt
|
||||
case wantWorkNum
|
||||
}
|
||||
|
||||
init(from decoder: any Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.id = try container.decode(String.self, forKey: .id)
|
||||
self.isActive = try container.decodeIfPresent(Bool.self, forKey: .isActive) ?? true
|
||||
self.title = try container.decodeIfPresent(String.self, forKey: .title) ?? ""
|
||||
self.business = try container.decodeIfPresent(String.self, forKey: .business) ?? "互联网"
|
||||
self.tax = try container.decodeIfPresent(String.self, forKey: .tax) ?? "详谈"
|
||||
self.hourlyWage = try container.decodeIfPresent(Int.self, forKey: .hourlyWage) ?? 100
|
||||
self.wantNum = try container.decodeIfPresent(Int.self, forKey: .wantNum) ?? 3
|
||||
self.contactType = try container.decodeIfPresent(String.self, forKey: .contactType) ?? "微信"
|
||||
self.contact = try container.decodeIfPresent(String.self, forKey: .contact) ?? ""
|
||||
self.startHour = try container.decodeIfPresent(Int.self, forKey: .startHour) ?? 10
|
||||
self.startMin = try container.decodeIfPresent(Int.self, forKey: .startMin) ?? 0
|
||||
self.endHour = try container.decodeIfPresent(Int.self, forKey: .endHour) ?? 20
|
||||
self.endMin = try container.decodeIfPresent(Int.self, forKey: .endMin) ?? 0
|
||||
self.has2 = try container.decodeIfPresent(Bool.self, forKey: .has2) ?? false
|
||||
self.startHour2 = try container.decodeIfPresent(Int.self, forKey: .startHour2) ?? 10
|
||||
self.startMin2 = try container.decodeIfPresent(Int.self, forKey: .startMin2) ?? 0
|
||||
self.endHour2 = try container.decodeIfPresent(Int.self, forKey: .endHour2) ?? 20
|
||||
self.endMin2 = try container.decodeIfPresent(Int.self, forKey: .endMin2) ?? 0
|
||||
self.otherTime = try container.decodeIfPresent(String.self, forKey: .otherTime) ?? ""
|
||||
self.placeName = try container.decodeIfPresent(String.self, forKey: .placeName) ?? ""
|
||||
self.province = try container.decodeIfPresent(String.self, forKey: .province) ?? "浙江"
|
||||
self.city = try container.decodeIfPresent(String.self, forKey: .city) ?? ""
|
||||
self.workContent = try container.decodeIfPresent(String.self, forKey: .workContent) ?? ""
|
||||
self.needExp = try container.decodeIfPresent(String.self, forKey: .needExp) ?? "1-3年"
|
||||
self.needEdu = try container.decodeIfPresent(String.self, forKey: .needEdu) ?? "本科"
|
||||
self.needLan = try container.decodeIfPresent(String.self, forKey: .needLan) ?? "Swift"
|
||||
self.needFrame = try container.decodeIfPresent(String.self, forKey: .needFrame) ?? "SwiftUI"
|
||||
self.otherNeed = try container.decodeIfPresent(String.self, forKey: .otherNeed) ?? ""
|
||||
self.moveFee = try container.decodeIfPresent(Bool.self, forKey: .moveFee) ?? true
|
||||
self.noOverTime = try container.decodeIfPresent(Bool.self, forKey: .noOverTime) ?? true
|
||||
self.otherBenefit = try container.decodeIfPresent(String.self, forKey: .otherBenefit) ?? ""
|
||||
self.companyName = try container.decodeIfPresent(String.self, forKey: .companyName) ?? ""
|
||||
self.userID = try container.decodeIfPresent(String.self, forKey: .userID) ?? ""
|
||||
self.updatedAt = try container.decodeIfPresent(Date.self, forKey: .updatedAt) ?? Date()
|
||||
self.wantWorkNum = try container.decodeIfPresent(Int.self, forKey: .wantWorkNum) ?? 0
|
||||
}
|
||||
|
||||
// func encode(to encoder: any Encoder) throws {
|
||||
// var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
// try container.encode(self.id, forKey: .id)
|
||||
// try container.encode(self.isActive, forKey: .isActive)
|
||||
// try container.encode(self.title, forKey: .title)
|
||||
// try container.encode(self.business, forKey: .business)
|
||||
// try container.encode(self.tax, forKey: .tax)
|
||||
// try container.encode(self.hourlyWage, forKey: .hourlyWage)
|
||||
// try container.encode(self.wantNum, forKey: .wantNum)
|
||||
// try container.encode(self.contactType, forKey: .contactType)
|
||||
// try container.encode(self.contact, forKey: .contact)
|
||||
// try container.encode(self.startHour, forKey: .startHour)
|
||||
// try container.encode(self.startMin, forKey: .startMin)
|
||||
// try container.encode(self.endHour, forKey: .endHour)
|
||||
// try container.encode(self.endMin, forKey: .endMin)
|
||||
// try container.encode(self.has2, forKey: .has2)
|
||||
// try container.encode(self.startHour2, forKey: .startHour2)
|
||||
// try container.encode(self.startMin2, forKey: .startMin2)
|
||||
// try container.encode(self.endHour2, forKey: .endHour2)
|
||||
// try container.encode(self.endMin2, forKey: .endMin2)
|
||||
// try container.encode(self.otherTime, forKey: .otherTime)
|
||||
// try container.encode(self.placeName, forKey: .placeName)
|
||||
// try container.encode(self.province, forKey: .province)
|
||||
// try container.encode(self.city, forKey: .city)
|
||||
// try container.encode(self.workContent, forKey: .workContent)
|
||||
// try container.encode(self.needExp, forKey: .needExp)
|
||||
// try container.encode(self.needEdu, forKey: .needEdu)
|
||||
// try container.encode(self.needLan, forKey: .needLan)
|
||||
// try container.encode(self.needFrame, forKey: .needFrame)
|
||||
// try container.encode(self.otherNeed, forKey: .otherNeed)
|
||||
// try container.encode(self.moveFee, forKey: .moveFee)
|
||||
// try container.encode(self.noOverTime, forKey: .noOverTime)
|
||||
// try container.encode(self.otherBenefit, forKey: .otherBenefit)
|
||||
// try container.encode(self.companyName, forKey: .companyName)
|
||||
// try container.encode(self.userID, forKey: .userID)
|
||||
// try container.encode(self.updatedAt, forKey: .updatedAt)
|
||||
// try container.encode(self.wantWorkNum, forKey: .wantWorkNum)
|
||||
// }
|
||||
|
||||
init(id: String = UUID().uuidString, isActive: Bool = true, title: String = "", business: String = "互联网", tax: String = "详谈", hourlyWage: Int = 100, wantNum: Int = 3, contactType: String = "微信", contact: String = "", startHour: Int = 10, startMin: Int = 0, endHour: Int = 20, endMin: Int = 0, has2: Bool = false, startHour2: Int = 10, startMin2: Int = 0, endHour2: Int = 20, endMin2: Int = 0, otherTime: String = "", placeName: String = "", province: String = "浙江", city: String = "", workContent: String = "", needExp: String = "1-3年", needEdu: String = "本科", needLan: String = "Swift", needFrame: String = "SwiftUI", otherNeed: String = "", moveFee: Bool = true, noOverTime: Bool = true, otherBenefit: String = "", companyName: String = "", userID: String = "", updatedAt: Date = Date(), wantWorkNum: Int = 0) {
|
||||
self.id = id
|
||||
self.isActive = isActive
|
||||
self.title = title
|
||||
self.business = business
|
||||
self.tax = tax
|
||||
self.hourlyWage = hourlyWage
|
||||
self.wantNum = wantNum
|
||||
self.contactType = contactType
|
||||
self.contact = contact
|
||||
self.startHour = startHour
|
||||
self.startMin = startMin
|
||||
self.endHour = endHour
|
||||
self.endMin = endMin
|
||||
self.has2 = has2
|
||||
self.startHour2 = startHour2
|
||||
self.startMin2 = startMin2
|
||||
self.endHour2 = endHour2
|
||||
self.endMin2 = endMin2
|
||||
self.otherTime = otherTime
|
||||
self.placeName = placeName
|
||||
self.province = province
|
||||
self.city = city
|
||||
self.workContent = workContent
|
||||
self.needExp = needExp
|
||||
self.needEdu = needEdu
|
||||
self.needLan = needLan
|
||||
self.needFrame = needFrame
|
||||
self.otherNeed = otherNeed
|
||||
self.moveFee = moveFee
|
||||
self.noOverTime = noOverTime
|
||||
self.otherBenefit = otherBenefit
|
||||
self.companyName = companyName
|
||||
self.userID = userID
|
||||
self.updatedAt = updatedAt
|
||||
self.wantWorkNum = wantWorkNum
|
||||
}
|
||||
|
||||
static let job = PLJob(title: "iOS程序员", tax: "报税", contact: "12345", has2: true, startHour2: 14, endHour2: 22, otherTime: "可以根据实际情况调整", placeName: "阿里巴巴西溪园区", province: "浙江", city: "杭州", workContent: "iOS开发啊啊iOS开发啊啊iOS开发啊啊iOS开发啊啊iOS开发啊啊iOS开发啊啊", otherNeed: "什么都不需要啊啊什么都不需要啊啊什么都不需要啊啊什么都不需要啊啊", otherBenefit: "什么都有啊啊什么都有啊啊什么都有啊啊什么都有啊啊什么都有啊啊",companyName:"阿里巴巴")
|
||||
}
|
31
IOS_study/Model/Report.swift
Normal file
@ -0,0 +1,31 @@
|
||||
//
|
||||
// Report.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct JobReport: Codable, Identifiable {
|
||||
var id = UUID().uuidString
|
||||
//被举报的工作的信息
|
||||
var jobID = ""
|
||||
var jobType = ""
|
||||
var jobTitle = ""
|
||||
var bossID = ""
|
||||
//举报者
|
||||
var userID = ""
|
||||
// var userCompanyName = ""
|
||||
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case jobType
|
||||
case jobID
|
||||
case jobTitle
|
||||
case bossID
|
||||
case userID
|
||||
}
|
||||
|
||||
}
|
18
IOS_study/Model/Review.swift
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// Review.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Review: Identifiable, Codable {
|
||||
var id = UUID().uuidString
|
||||
var userAvatar = "0"//头像
|
||||
var userNickName = ""//昵称
|
||||
var reviewType = "未面试"//点评类型
|
||||
var goods: [String] = []//好评
|
||||
var bads: [String] = []//差评
|
||||
var content = ""
|
||||
}
|
50
IOS_study/Post/CompanyGalleryORView.swift
Normal file
@ -0,0 +1,50 @@
|
||||
//
|
||||
// CompanyGalleryORView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Kingfisher
|
||||
|
||||
struct CompanyGalleryORView: View {
|
||||
let imageURLStrs: [String]
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@State var currentPage: Int
|
||||
@State var dragOffset: CGSize = .zero
|
||||
var body: some View {
|
||||
TabView(selection: $currentPage) {
|
||||
ForEach(imageURLStrs.indices, id: \.self) { i in
|
||||
VStack {
|
||||
KFImage(URL(string: imageURLStrs[i]))
|
||||
.placeholder { ProgressView() }.loadDiskFileSynchronously().fade(duration: 0.15)
|
||||
.resizable().scaledToFit().tag(i)
|
||||
.offset(y: dragOffset.height)
|
||||
}.frame(maxWidth: .infinity, maxHeight: .infinity).pinchToZoom()
|
||||
}
|
||||
}.bg().tabViewStyle(.page)
|
||||
.toolbarVisibility(.hidden, for: .tabBar)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .principal) {
|
||||
Text("\(currentPage + 1)/\(imageURLStrs.count)").tc().bold()
|
||||
}
|
||||
}
|
||||
.gesture(//手势【拖拽】
|
||||
DragGesture()
|
||||
.onChanged({ gesture in
|
||||
if gesture.translation.height > 0 {
|
||||
dragOffset = gesture.translation
|
||||
}
|
||||
})
|
||||
.onEnded { gesture in
|
||||
if gesture.translation.height > 80 {//垂直偏移80 -> 返回上一页
|
||||
dismiss()
|
||||
} else {
|
||||
withAnimation { dragOffset = .zero }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
46
IOS_study/Post/CompanyGalleryView.swift
Normal file
@ -0,0 +1,46 @@
|
||||
//
|
||||
// CompanyGalleryView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct CompanyGalleryView: View {
|
||||
@Environment(ProfileViewModel.self) var vm
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@State var currentPage: Int
|
||||
@State var showConfirm = false
|
||||
var body: some View {
|
||||
TabView(selection: $currentPage) {
|
||||
ForEach(vm.companyUIImages.indices, id: \.self) { i in
|
||||
Image(uiImage: vm.companyUIImages[i]).resizable().scaledToFit().tag(i)
|
||||
}
|
||||
}.bg().tabViewStyle(.page)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .principal) {
|
||||
Text("\(currentPage + 1)/\(vm.companyUIImages.count)").tc().bold()
|
||||
}
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Image(systemName: "trash").onTapGesture {
|
||||
showConfirm = true
|
||||
}
|
||||
}
|
||||
}
|
||||
.confirmationDialog("删除一张图片", isPresented: $showConfirm, actions: {
|
||||
Button {
|
||||
guard currentPage < vm.companyUIImages.count else { return }
|
||||
vm.companyUIImages.remove(at: currentPage)
|
||||
if vm.companyUIImages.isEmpty {
|
||||
dismiss()
|
||||
} else if currentPage >= vm.companyUIImages.count {
|
||||
currentPage = vm.companyUIImages.count - 1
|
||||
}
|
||||
} label: {
|
||||
Text("删除")
|
||||
}
|
||||
}, message: { Text("要删除这张图片吗?") })
|
||||
}
|
||||
}
|
||||
|
152
IOS_study/Post/PostCompanyView.swift
Normal file
@ -0,0 +1,152 @@
|
||||
//
|
||||
// PostCompanyView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/15.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import PhotosUI
|
||||
|
||||
struct PostCompanyView: View {
|
||||
@State var isEditing = false//是否在编辑
|
||||
@Environment(HUD.self) var hud//提示信息功能
|
||||
@Environment(ProfileViewModel.self) var ProfileVM
|
||||
@Binding var naviPath: NavigationPath
|
||||
var draftUser: DBUser { ProfileVM.draftUser }
|
||||
var invalidMsg: String? {
|
||||
if draftUser.cName.isBlank { return "公司名字没有写" }
|
||||
if draftUser.cProvince.isBlank { return "公司所在省份没有写" }
|
||||
if draftUser.cCity.isBlank { return "公司所在城市没有写" }
|
||||
if draftUser.cName.count > 50 { return "公司名字不能超过50字" }
|
||||
if draftUser.cCity.count > 50 { return "公司所在城市名不能超过50字" }
|
||||
if draftUser.cAboutUS.count > 20 { return "公司简介不能超过20字" }
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
var body: some View {
|
||||
@Bindable var vm = ProfileVM
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 14) {
|
||||
HStack {
|
||||
Text("公司名称:").littleTStyle()
|
||||
TextField("请输入完整公司名称", text: $vm.draftUser.cName, axis: .vertical)
|
||||
.tfStyle()//单行
|
||||
}
|
||||
HStack {
|
||||
Text("公司地址:").littleTStyle()
|
||||
Picker("省份", selection: $vm.draftUser.cProvince) {
|
||||
ForEach(kProvinceArr, id: \.self) { Text($0) }
|
||||
}.pStyle()
|
||||
TextField("城市",text: $vm.draftUser.cCity).tfStyle()
|
||||
}
|
||||
VStack(alignment: .leading, spacing: 9) {
|
||||
Text("公司简介(选填)").littleTStyle()
|
||||
TextField("", text: $vm.draftUser.cAboutUS).tfStyleMultiL()
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Text("公司Logo(选填)").littleTStyle()
|
||||
Text(vm.logoImageErrorMsg).size15pb()
|
||||
}
|
||||
PhotosPicker(selection: $vm.logoItem, matching: .images, photoLibrary: .shared()) {
|
||||
if let uiImage = vm.logoUIImage {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()//可改变大小
|
||||
// .scaledToFit()//保持宽高比不变,显示完整图片
|
||||
.scaledToFill()//保持宽高比不变,充满空间,会有裁剪
|
||||
.thumbFrame().radius()
|
||||
} else {
|
||||
addImageBtn
|
||||
}
|
||||
}
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Text("公司图片(选填)").littleTStyle()
|
||||
Text(vm.companyUIImagesErrorMsg).size15pb()
|
||||
}
|
||||
ScrollView(.horizontal) {
|
||||
HStack {
|
||||
if vm.isLoadingCompanyImages {
|
||||
ProgressView().thumbFrame()
|
||||
} else {
|
||||
ForEach(vm.companyUIImages.indices, id: \.self) { i in
|
||||
NavigationLink {
|
||||
CompanyGalleryView(currentPage: i)
|
||||
} label: {
|
||||
Image(uiImage: vm.companyUIImages[i]).resizable().scaledToFill().thumbFrame().radius()
|
||||
}.disabled(!vm.isCompanyImagesLoaded)
|
||||
}
|
||||
}
|
||||
if vm.companyUIImages.count < 6 {
|
||||
PhotosPicker(selection: $vm.companyItems, maxSelectionCount: 6 - vm.companyUIImages.count, matching: .images, photoLibrary: .shared()) { addImageBtn }
|
||||
}
|
||||
}
|
||||
}.scrollIndicators(.hidden)
|
||||
}
|
||||
|
||||
Button {
|
||||
// hud.show("提交成功")
|
||||
Task {//开启异步任务
|
||||
await postCompany()
|
||||
}
|
||||
} label: {
|
||||
if vm.isPostingCompany {
|
||||
ProgressView().appleStyleP()//提交加载
|
||||
}else {
|
||||
Text("提交").appleStyle()
|
||||
}
|
||||
}.disabled(vm.isPostingCompany)
|
||||
|
||||
}.padding()
|
||||
postBottomEView()
|
||||
}.scrollIndicators(.hidden).tc().size17().bg()
|
||||
.navigationBarBackButtonHidden().navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .principal) { Text("公司信息").tc().bold() }
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button {
|
||||
if !naviPath.isEmpty {//检查是否为最后一页
|
||||
naviPath.removeLast()//返回上一页
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "chevron.left").secondary().padding(.horizontal,6)
|
||||
}
|
||||
}
|
||||
}
|
||||
.toolbarVisibility(.hidden, for: .tabBar)
|
||||
.onAppear {
|
||||
if ProfileVM.isCompanyImagesLoaded { return }
|
||||
Task {
|
||||
await vm.loadCompanyLogoAndIamges()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var addImageBtn: some View {
|
||||
Image(systemName: "plus").font(.title).secondary().thumbFrame()
|
||||
.border(borderColor: Color(.systemGray4))
|
||||
}
|
||||
func postCompany() async {
|
||||
if let invalidMsg = invalidMsg {//也可以写成if let invalidMsg
|
||||
hud.show(invalidMsg)
|
||||
return
|
||||
}
|
||||
ProfileVM.isPostingCompany = true
|
||||
defer { ProfileVM.isPostingCompany = false }
|
||||
UIApplication.shared.hideKeyboard()//收起键盘
|
||||
|
||||
do {
|
||||
try await ProfileVM.postCompany()
|
||||
hud.show("发布成功")
|
||||
if !naviPath.isEmpty { naviPath.removeLast() }
|
||||
} catch {
|
||||
print("上传或修改公司信息失败:\(error)")
|
||||
hud.show("上传或修改公司信息失败:\(error)")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
27
IOS_study/Post/PostJobView.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// PostJobView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/6/28.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct PostJobView: View {
|
||||
@Environment(AuthViewModel.self) var authVM
|
||||
@Environment(TabBarViewModel.self) var TabBarVM
|
||||
@State var naviPath = NavigationPath()
|
||||
var body: some View {
|
||||
if authVM.isLogin {
|
||||
NavigationStack(path: $naviPath) {
|
||||
PostPLJobIView(naviPath: $naviPath)
|
||||
}
|
||||
} else {
|
||||
Button {
|
||||
TabBarVM.selectedTab = "profile"
|
||||
} label: {
|
||||
Text("去登录").appleStyle().padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
69
IOS_study/Post/PostPLJobIView+.swift
Normal file
@ -0,0 +1,69 @@
|
||||
//
|
||||
// PostPLJobIView+.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/6/29.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension PostPLJobIView {
|
||||
|
||||
var postBtn: some View {
|
||||
Button {
|
||||
// hud.show("提交成功")
|
||||
Task {//开启异步任务
|
||||
await postJob()
|
||||
}
|
||||
} label: {
|
||||
if vm.isPosting {
|
||||
ProgressView().appleStyleP()//提交加载
|
||||
}else {
|
||||
Text("提交").appleStyle()
|
||||
}
|
||||
}.disabled(vm.isPosting).padding().bg()
|
||||
}
|
||||
|
||||
func postJob() async {
|
||||
if let invalidMsg = invalidMsg {//也可以写成if let invalidMsg
|
||||
hud.show(invalidMsg)
|
||||
return
|
||||
}
|
||||
// print("输入ok")
|
||||
vm.isPosting = true
|
||||
defer {
|
||||
vm.isPosting = false
|
||||
}
|
||||
|
||||
UIApplication.shared.hideKeyboard()//收起键盘
|
||||
|
||||
workPlaceName = draftJob.placeName
|
||||
workProvince = draftJob.province
|
||||
workCity = draftJob.city
|
||||
workContact = draftJob.contact
|
||||
|
||||
vm.draftJob.companyName = profileVM.user.cName
|
||||
|
||||
do {
|
||||
try await vm.postJob(isEditing: isEditing)
|
||||
if !profileVM.user.hasPLJobs {
|
||||
profileVM.user.hasPLJobs = true
|
||||
try await UserManager.shared.setTo(filed: DBUser.CodingKeys.hasPLJobs.rawValue, true)
|
||||
}
|
||||
profileVM.shouldRefreshMyPLJobs = true
|
||||
if isEditing {
|
||||
hud.show("修改成功")
|
||||
if !naviPath.isEmpty { naviPath.removeLast() }
|
||||
} else {
|
||||
hud.show("发布成功")
|
||||
tabbarVM.goJobsAndRefresh("兼职")
|
||||
}
|
||||
scrollView?.scrollTo("title",anchor: .top)
|
||||
vm.draftJob = PLJob()
|
||||
} catch {
|
||||
hud.show("\(error)")
|
||||
print("用户上传或修改兼职失败:\(error)")
|
||||
}
|
||||
// vm.isPosting = false
|
||||
}
|
||||
}
|
320
IOS_study/Post/PostPLJobIView.swift
Normal file
@ -0,0 +1,320 @@
|
||||
//
|
||||
// PostPLJobIView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/6/28.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct PostPLJobIView: View {
|
||||
var isEditing = false //默认新增工作
|
||||
|
||||
@AppStorage("workPlaceName") var workPlaceName = ""
|
||||
@AppStorage("workProvince") var workProvince = ""
|
||||
@AppStorage("workCity") var workCity = ""
|
||||
@AppStorage("workContact") var workContact = ""
|
||||
@Environment(PLJobViewModel.self) var vm
|
||||
@Environment(ProfileViewModel.self) var profileVM
|
||||
@Environment(HUD.self) var hud
|
||||
@Environment(TabBarViewModel.self) var tabbarVM
|
||||
@State var scrollView: ScrollViewProxy?// = nil
|
||||
@Binding var naviPath: NavigationPath
|
||||
@State var WordNum20 = 20
|
||||
@State var WordNum50 = 50
|
||||
@State var WordNum100 = 100
|
||||
@State var WordNum200 = 200
|
||||
@State var WordNum300 = 300
|
||||
@State var WordNum500 = 500
|
||||
|
||||
var draftJob: PLJob{ vm.draftJob }
|
||||
|
||||
var invalidMsg: String? {
|
||||
if !profileVM.user.cIsUploaded { return "需要先上传公司信息" }
|
||||
if draftJob.title.isBlank { return "工作名称未写" }
|
||||
if draftJob.hourlyWage < 20 || draftJob.hourlyWage > 1000 { return "基本时给太低或太高" }
|
||||
if draftJob.placeName.isBlank { return "工作地点未填" }
|
||||
if draftJob.city.isBlank { return "城市未填" }
|
||||
if draftJob.contact.isBlank { return "联系方式未填"}
|
||||
if draftJob.title.count > WordNum50 { return "工作名称不能超过\($WordNum50)" }
|
||||
if draftJob.otherTime.count > WordNum100 { return "时间说明不能超过\($WordNum100)" }
|
||||
if draftJob.otherNeed.count > WordNum300 { return "工作要求不能超过\($WordNum300)" }
|
||||
if draftJob.workContent.count > WordNum500 { return "工作内容不能超过\($WordNum500)" }
|
||||
if draftJob.otherBenefit.count > WordNum300 { return "工作福利不能超过\($WordNum300)" }
|
||||
if draftJob.placeName.count > WordNum50 { return "工作地点名称不能超过\($WordNum50)" }
|
||||
if draftJob.city.count > WordNum20 { return "工作地城市不能超过\($WordNum20)" }
|
||||
if draftJob.contact.count > WordNum200 { return "联系方式不能超过\($WordNum200)" }
|
||||
return nil
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@Bindable var vm = vm
|
||||
ScrollViewReader { scrollView in
|
||||
ScrollView(showsIndicators: false) {
|
||||
VStack(spacing: kStackSpacingS) {
|
||||
VStack {
|
||||
if profileVM.user.cIsUploaded {
|
||||
Text(profileVM.user.cName).subTStyleL()
|
||||
} else {
|
||||
Text("请先至个人页添加公司").size15().red()
|
||||
}
|
||||
}.push(to: .center).padding().bg()
|
||||
// MARK: - 基本信息
|
||||
VStack(alignment: .leading,spacing: kStackSpacing) {
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
Text("工作业种").littleTStyle()
|
||||
Picker("工作业种", selection: $vm.draftJob.business) {
|
||||
ForEach(kBusinessArr, id: \.self){
|
||||
Text($0)
|
||||
}
|
||||
}.pStyle()
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
Text("工作名称").littleTStyle()
|
||||
TextField("IOS程序员", text: $vm.draftJob.title).tfStyle()
|
||||
}
|
||||
}.id("title")
|
||||
VStack(alignment: .leading) {
|
||||
Text("是否报税").littleTStyle()
|
||||
Picker("是否报税", selection: $vm.draftJob.tax){
|
||||
ForEach(kTaxArr, id: \.self){
|
||||
Text($0)
|
||||
}
|
||||
}.pickerStyle(.segmented)
|
||||
}.padding(.bottom,5)
|
||||
HStack {
|
||||
Text("基本时给:").littleTStyle()
|
||||
TextField("", value: $vm.draftJob.hourlyWage, formatter: NumberFormatter())
|
||||
.size18ab().multilineTextAlignment(.center).kerning(2)
|
||||
.keyboardType(.numberPad).tfStyle().frame(width: 88)
|
||||
Text("请填写最低时给").size13s()
|
||||
}
|
||||
HStack {
|
||||
Text("招聘人数:").littleTStyle()
|
||||
Picker("招聘人数", selection: $vm.draftJob.wantNum) { ForEach(kWantNumArr, id: \.self){
|
||||
Text("\($0)人")//字符串插值
|
||||
}
|
||||
}.pStyle()
|
||||
Text("请填写最多招聘人数").size13s()
|
||||
}
|
||||
|
||||
}.padding().bg()
|
||||
// MARK: - 工作时间
|
||||
VStack(alignment: .leading,spacing: kStackSpacingS) {
|
||||
Text("工作时间").littleTStyle()
|
||||
HStack {
|
||||
HStack(spacing: 3) {
|
||||
Picker("起始小时", selection: $vm.draftJob.startHour) { ForEach(0..<25) {Text("\($0)")}
|
||||
}.pStyle()
|
||||
Text(":").conlonStyle()
|
||||
Picker("起始分钟", selection: $vm.draftJob.startMin) { ForEach([0,30], id: \.self) {Text($0 == 0 ? "00":"\($0)")}
|
||||
}.pStyle()
|
||||
|
||||
Image(systemName: "minus")
|
||||
|
||||
Picker("结束小时", selection: $vm.draftJob.endHour) { ForEach(0..<25) {Text("\($0)")}
|
||||
}.pStyle()
|
||||
Text(":").conlonStyle()
|
||||
Picker("结束分钟", selection: $vm.draftJob.endMin) { ForEach([0,30], id: \.self) {Text($0 == 0 ? "00":"\($0)")}
|
||||
}.pStyle()
|
||||
}
|
||||
Button {
|
||||
withAnimation {
|
||||
vm.draftJob.has2.toggle()
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: vm.draftJob.has2 ? "minus.circle.fill" : "plus.circle.fill")
|
||||
.foregroundStyle(vm.draftJob.has2 ? .red : .accent)
|
||||
}.font(.title2).padding(.horizontal,3)
|
||||
}
|
||||
|
||||
if(vm.draftJob.has2){
|
||||
HStack(spacing: 3) {
|
||||
Picker("第二个起始小时", selection: $vm.draftJob.startHour2) { ForEach(0..<25) {Text("\($0)")}
|
||||
}.pStyle()
|
||||
Text(":").conlonStyle()
|
||||
Picker("第二个起始分钟2", selection: $vm.draftJob.startMin2) { ForEach([0,30], id: \.self) {Text($0 == 0 ? "00":"\($0)")}
|
||||
}.pStyle()
|
||||
|
||||
Image(systemName: "minus")
|
||||
|
||||
Picker("第二个结束小时2", selection: $vm.draftJob.endHour2) { ForEach(0..<25) {Text("\($0)")}
|
||||
}.pStyle()
|
||||
Text(":").conlonStyle()
|
||||
Picker("第二个结束分钟2", selection: $vm.draftJob.endMin2) { ForEach([0,30], id: \.self) {Text($0 == 0 ? "00":"\($0)")}
|
||||
}.pStyle()
|
||||
}
|
||||
}
|
||||
|
||||
Text("追加说明").littleTStyle()
|
||||
TextField("具体时间段可以详谈", text: $vm.draftJob.otherTime, axis: .vertical).tfStyleMultiS()
|
||||
}.push(to: .leading).padding().bg()
|
||||
// MARK: - 工作要求
|
||||
VStack(alignment: .leading,spacing: kStackSpacing) {
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
Text("经验要求").littleTStyle()
|
||||
Picker("经验要求", selection: $vm.draftJob.needExp) { ForEach(kNeedExpArr, id: \.self) {Text($0)}
|
||||
}.pStyle()
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
Text("学历要求").littleTStyle()
|
||||
Picker("学历要求", selection: $vm.draftJob.needEdu) { ForEach(kNeedEduArr, id: \.self) {Text($0)}
|
||||
}.pStyle()
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
Text("语言要求").littleTStyle()
|
||||
Picker("学历要求", selection: $vm.draftJob.needLan) { ForEach(kNeedLanArr, id: \.self) {Text($0)}
|
||||
}.pStyle()
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
Text("框架要求").littleTStyle()
|
||||
Picker("学历要求", selection: $vm.draftJob.needFrame) { ForEach(kNeedFrameArr, id: \.self) {Text($0)}
|
||||
}.pStyle()
|
||||
}
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
Text("更多要求/加分项(选填)").littleTStyle()
|
||||
TextField("", text: $vm.draftJob.otherNeed, axis: .vertical).tfStyleMultiM()
|
||||
}
|
||||
}.padding().bg()
|
||||
// MARK: - 工作内容
|
||||
VStack(alignment: .leading) {
|
||||
Text("工作内容(选填)").littleTStyle()
|
||||
TextField("", text: $vm.draftJob.workContent, axis: .vertical).tfStyleMultiM()
|
||||
|
||||
}.padding().bg()
|
||||
// MARK: - 公司福利
|
||||
VStack(alignment: .leading,spacing: kStackSpacing) {
|
||||
Text("公司福利").littleTStyle()
|
||||
HStack {
|
||||
Text("交通费报销").onTapGesture {
|
||||
vm.draftJob.moveFee.toggle()
|
||||
}.toggleStyle(isTap: vm.draftJob.moveFee)
|
||||
Text("不加班").onTapGesture {
|
||||
vm.draftJob.noOverTime.toggle()
|
||||
}.toggleStyle(isTap: vm.draftJob.noOverTime)
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
Text("更多福利(选填)").littleTStyle()
|
||||
TextField("", text: $vm.draftJob.otherBenefit, axis: .vertical).tfStyleMultiL()
|
||||
}
|
||||
}.padding().bg()
|
||||
// MARK: - 工作地点
|
||||
VStack(alignment: .leading,spacing: kStackSpacing) {
|
||||
HStack {
|
||||
Text("省份").littleTStyle()
|
||||
Picker("省份", selection: $vm.draftJob.province) { ForEach(kProvinceArr, id: \.self) {Text($0)}
|
||||
}.pStyle()
|
||||
if !workProvince.isEmpty {
|
||||
HStack(spacing: 3) {
|
||||
Text("上次填的:").size13s()
|
||||
Button {
|
||||
vm.draftJob.province = workProvince
|
||||
} label: {
|
||||
Text(workProvince).labelBG()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
HStack {
|
||||
Text("城市").littleTStyle()
|
||||
TextField("", text: $vm.draftJob.city, axis: .vertical).tfStyle().frame(width: 115)
|
||||
if !workCity.isEmpty {
|
||||
HStack(spacing: 3) {
|
||||
Text("上次填的:").size13s()
|
||||
Button {
|
||||
vm.draftJob.city = workCity
|
||||
} label: {
|
||||
Text(workCity).labelBG()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
Text("工作地点").littleTStyle()
|
||||
TextField("请填写完整工作地址", text: $vm.draftJob.placeName, axis: .vertical).tfStyle()
|
||||
|
||||
if !workPlaceName.isEmpty {
|
||||
HStack(spacing: 3) {
|
||||
Text("上次填的:").size13s()
|
||||
Button {
|
||||
vm.draftJob.placeName = workPlaceName
|
||||
} label: {
|
||||
Text(workPlaceName).labelBG()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Button {
|
||||
// workProvince = province
|
||||
// workCity = city
|
||||
// workProvince = province
|
||||
// } label: {
|
||||
// Text("存本地")
|
||||
// }.btnStyle()
|
||||
//
|
||||
}.padding().bg()
|
||||
// MARK: - 应聘方式
|
||||
VStack(alignment: .leading,spacing: kStackSpacing){
|
||||
Text("联系方式/应聘方式").littleTStyle()
|
||||
HStack {
|
||||
Picker("联系方式种类", selection: $vm.draftJob.contactType) {
|
||||
ForEach(kContactTypeArr, id: \.self) {Text($0)}
|
||||
}.pStyle()
|
||||
TextField("", text: $vm.draftJob.contact, axis: .vertical).tfStyle()
|
||||
}
|
||||
if !workContact.isEmpty {
|
||||
HStack(spacing: 3) {
|
||||
Text("上次填的:").size13s()
|
||||
Button {
|
||||
vm.draftJob.contact = workContact
|
||||
} label: {
|
||||
Text(workContact).labelBG()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text("据调查,99%的人才都喜欢使用微信联系").size13().red()
|
||||
}.padding().bg()
|
||||
|
||||
// MARK: - 提交按钮
|
||||
postBtn
|
||||
//
|
||||
// withAnimation {//withAnimation -> 动画
|
||||
// Button("点击") {
|
||||
// scrollView.scrollTo("title",anchor: .top)//返回到id为title的view的top(id为title的view的顶部)
|
||||
// }
|
||||
// }
|
||||
|
||||
}.tc().bg2()
|
||||
postBottomEView().bg()
|
||||
}.navigationBarBackButtonHidden(isEditing).navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
if isEditing {
|
||||
ToolbarItem(placement: .principal) { Text("编辑兼职").tc().bold() }
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button {
|
||||
if !naviPath.isEmpty {//检查是否为最后一页
|
||||
naviPath.removeLast()//返回上一页
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "chevron.left").secondary().padding(.horizontal,6)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear() {
|
||||
self.scrollView = scrollView
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
PostPLJobIView(naviPath: .constant(NavigationPath()))
|
||||
}
|
18
IOS_study/Post/PostReviewView.swift
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// PostReviewView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct PostReviewView: View {
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
PostReviewView()
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
23
IOS_study/Profile/PLJobView+RevUI.swift
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// PLJobView+RevUI.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension PLJobView {
|
||||
var goRevireBtn: some View {
|
||||
Button {
|
||||
if authVM.isLogin {
|
||||
reviewVM.drafReview = Review()
|
||||
showPostReview = true
|
||||
} else {
|
||||
hud.show("请先登录")
|
||||
}
|
||||
} label: {
|
||||
Text("对这个工作有看法?去点评").appleStyle()
|
||||
}.buttonStyle(.borderless).cellStyle()
|
||||
}
|
||||
}
|
98
IOS_study/Profile/ProfileView+ComUI.swift
Normal file
@ -0,0 +1,98 @@
|
||||
//
|
||||
// ProfileView+ComUI.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/18.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Kingfisher
|
||||
|
||||
extension ProfileView {
|
||||
var myCompanyView: some View {
|
||||
VStack(alignment: .leading, spacing: 14) {
|
||||
HStack {
|
||||
// MARK: - 公司头部信息
|
||||
Text("我的公司").headline().tc()
|
||||
if user.cIsUploaded {
|
||||
Button {
|
||||
ProfileVM.draftUser = user
|
||||
naviPath.append(Navi5.EditCompany)
|
||||
} label: { Text("编辑") }.btnStyle()
|
||||
} else {
|
||||
Button {
|
||||
ProfileVM.draftUser = DBUser()
|
||||
naviPath.append(Navi5.AddCompany)
|
||||
} label: { Text("去添加") }.btnStyle()
|
||||
}
|
||||
}
|
||||
|
||||
if user.cIsUploaded {//上传了公司信息
|
||||
VStack(alignment: .leading, spacing: 9) {
|
||||
HStack {
|
||||
if user.cLogoURLStr.isEmpty {
|
||||
Text("🏬").font(.system(size: 40))
|
||||
} else {
|
||||
Button {
|
||||
showLogo = true
|
||||
} label: {
|
||||
KFImage(URL(string: user.cLogoURLStr))
|
||||
.placeholder { ProgressView() }.loadDiskFileSynchronously().fade(duration: 0.15)
|
||||
.onSuccess { result in
|
||||
let image = result.image
|
||||
logoAspectRatio = image.size.width / image.size.height
|
||||
}
|
||||
.onFailure { error in
|
||||
print("图片加载失败:\(error)")
|
||||
}
|
||||
.resizable().scaledToFit().thumbFrameS(aspectRatio: logoAspectRatio).radius(6)
|
||||
}.buttonStyle(.borderless)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
Text(user.cName).subTStyle()//公司名字
|
||||
Text(user.cProvince + "-" + user.cCity)
|
||||
}
|
||||
}
|
||||
}.textStyle()
|
||||
|
||||
// MARK: - 公司简介
|
||||
if !user.cAboutUS.isEmpty {
|
||||
VStack(alignment: .leading, spacing: 9) {
|
||||
Text("公司简介").subTStyle()
|
||||
if sizeClass == .compact {
|
||||
ZStack(alignment: .bottomTrailing) {
|
||||
Text(user.cAboutUS).allowMulti().lineLimit((user.cAboutUS.count < 88 || isExpand) ? nil : 4)
|
||||
if user.cAboutUS.count > 88 {
|
||||
Text(isExpand ? "收起" : "展开").size14().accent().padding([.leading, .top], 3).bg()
|
||||
.onTapGesture { isExpand.toggle() }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Text(user.cAboutUS).allowMulti()
|
||||
}
|
||||
}.size15().tc2().lineSpacing(6)
|
||||
}
|
||||
// MARK: - 公司相关图片
|
||||
if !user.cImageURLStrs.isEmpty {
|
||||
ScrollView(.horizontal) {
|
||||
HStack {
|
||||
ForEach(user.cImageURLStrs.indices, id: \.self) { i in
|
||||
if i < user.cImageURLStrs.count {
|
||||
NavigationLink {
|
||||
CompanyGalleryORView(imageURLStrs: user.cImageURLStrs, currentPage: i)
|
||||
} label: {
|
||||
KFImage(URL(string: user.cImageURLStrs[i]))
|
||||
.placeholder { ProgressView() }.loadDiskFileSynchronously().fade(duration: 0.15)
|
||||
.resizable().scaledToFill().thumbFrame().radius(6)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}.scrollIndicators(.hidden)
|
||||
}
|
||||
}
|
||||
}.push(to: .leading).cellStyle()
|
||||
}
|
||||
}
|
56
IOS_study/Profile/ProfileView+JobUI.swift
Normal file
@ -0,0 +1,56 @@
|
||||
//
|
||||
// ProfileView+JobUI.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension ProfileView {
|
||||
var myJobsView: some View {
|
||||
VStack(alignment: .leading, spacing: kStackSpacing) {
|
||||
let jobsCount = ProfileVM.pLJobsCount
|
||||
let jobsCountText = jobsCount > 0 ? "(\(ProfileVM.aPLJobsCount)/\(ProfileVM.pLJobsCount))" : ""
|
||||
Text("我发布的工作\(jobsCountText)").headline().tc()
|
||||
if !ProfileVM.plJobs.isEmpty {
|
||||
ForEach(ProfileVM.plJobs) { job in
|
||||
Button {
|
||||
plJobVM.job = job
|
||||
naviPath.append(Navi5.PLJobsToPLJob)
|
||||
} label: {
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: kStackSpacingS) {
|
||||
HStack {
|
||||
Text("兼职").labelPL()
|
||||
Text(job.title).oneLineStyleMiddle()
|
||||
}
|
||||
HStack {
|
||||
Text(job.needExp).labelBG()
|
||||
Text(job.province + "-" + job.city).labelBG()
|
||||
if !job.isActive {
|
||||
Text("已下线").labelTA()
|
||||
}
|
||||
}
|
||||
}.push(to: .leading)
|
||||
|
||||
Text("\(job.hourlyWage)").bold().accent()
|
||||
}
|
||||
}.buttonStyle(.borderless)
|
||||
exDivider()
|
||||
}
|
||||
if !ProfileVM.isPLFinshed {
|
||||
Button {
|
||||
Task {
|
||||
await ProfileVM.getPLJobs()
|
||||
}
|
||||
} label: {
|
||||
if ProfileVM.isFcPLJobs { ProgressView().loadingJobStyleP() } else { Text("加载更多兼职职位").loadingJobStyle() }
|
||||
}.buttonStyle(.borderless).padding(.top, -6).push(to: .center)
|
||||
}
|
||||
} else {
|
||||
Text("暂无发布的工作").secondary()
|
||||
}
|
||||
}.push(to: .leading).textStyle().cellStyle()
|
||||
}
|
||||
}
|
88
IOS_study/Profile/ProfileView.swift
Normal file
@ -0,0 +1,88 @@
|
||||
//
|
||||
// ProfileView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/15.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Kingfisher
|
||||
|
||||
struct ProfileView: View {
|
||||
@Environment(HUD.self) var hud
|
||||
@Environment(AuthViewModel.self) var authVM
|
||||
@Environment(SignInPhoneViewModel.self) var SignInPhoneVM
|
||||
@Environment(ProfileViewModel.self) var ProfileVM
|
||||
@Environment(PLJobViewModel.self) var plJobVM
|
||||
@Environment(\.horizontalSizeClass) var sizeClass
|
||||
@State var naviPath = NavigationPath()
|
||||
@State var logoAspectRatio: CGFloat = 1
|
||||
@State var isExpand = false
|
||||
@State var showLogo = false
|
||||
var user: DBUser { ProfileVM.user }
|
||||
|
||||
var body: some View {
|
||||
if authVM.isLogin {
|
||||
NavigationStack(path: $naviPath) {
|
||||
List {
|
||||
myCompanyView//我的公司
|
||||
myJobsView//我发布的工作
|
||||
}.listStyle().naviBarStyle().refreshable { await getUsersData() }
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .principal) { Text("我的").tc().bold() }
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button {
|
||||
naviPath.append(Navi5.Tosetting)
|
||||
} label: {
|
||||
Text("设置")
|
||||
}.tc2().size15()
|
||||
}
|
||||
}
|
||||
.navigationDestination(isPresented: $showLogo, destination: {
|
||||
KFImage(URL(string: user.cLogoURLStr)).resizable().scaledToFit()
|
||||
.toolbarVisibility(.hidden, for: .tabBar)
|
||||
})
|
||||
.navigationDestination(for: Navi5.self) { navi in
|
||||
switch navi {
|
||||
case .AddCompany: PostCompanyView(naviPath: $naviPath)
|
||||
case .EditCompany: PostCompanyView(isEditing: true, naviPath: $naviPath)
|
||||
case .PLJobsToPLJob: PLJobView(canEdit: true, showBoss: false, naviPath: $naviPath)
|
||||
case .PLJobToEdit: PostPLJobIView(isEditing: true,naviPath: $naviPath)
|
||||
case .Tosetting: SettginsView(naviPath: $naviPath)
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
if ProfileVM.isFcSome {
|
||||
BprogressView()
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
Task {
|
||||
if !ProfileVM.viewDidLoad {
|
||||
await getUsersData()
|
||||
ProfileVM.viewDidLoad = true
|
||||
} else { await refreshIFNeed() }
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SignInPhoneView()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getUsersData() async {
|
||||
do { try await ProfileVM.getUsersData() } catch {
|
||||
hud.show("数据加载失败,请刷新\n错误信息:\n\(error.localizedDescription)")
|
||||
print("个人页有数据加载失败:\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func refreshIFNeed() async {
|
||||
do { try await ProfileVM.refreshIFNeed() } catch {
|
||||
hud.show("数据加载失败,请刷新\n错误信息:\n\(error.localizedDescription)")
|
||||
print("个人页有数据加载失败:\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
91
IOS_study/Profile/ProfileViewModel+.swift
Normal file
@ -0,0 +1,91 @@
|
||||
//
|
||||
// ProfileViewModel+.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension ProfileViewModel {
|
||||
@MainActor func getUser() async throws {
|
||||
if isFcUser { return }
|
||||
isFcUser = true
|
||||
defer { isFcUser = false }
|
||||
|
||||
user = try await UserManager.shared.findUserByREST()
|
||||
}
|
||||
|
||||
@MainActor func getPLJobs(isRefresh: Bool = false) async {
|
||||
if isFcPLJobs { return }
|
||||
isFcPLJobs = true
|
||||
defer { isFcPLJobs = false }
|
||||
if isRefresh {
|
||||
currentPLPage = 0
|
||||
isPLFinshed = false
|
||||
}
|
||||
do {
|
||||
if user.hasPLJobs {
|
||||
let skip = kJobLimitS * currentPLPage
|
||||
let newJobs = try await PLJobManager.shared.findJobsByREST(by: userID, limit: kJobLimitS, skip: skip,)
|
||||
|
||||
// print(newJobs[0].title)
|
||||
// print(newJobs[1].title)
|
||||
if isRefresh { plJobs = newJobs } else {
|
||||
plJobs.append(contentsOf: newJobs)//将一个数组添加到另外一个数组
|
||||
}
|
||||
|
||||
if plJobs.isEmpty {//校准hasPLJobs
|
||||
user.hasPLJobs = false
|
||||
try await UserManager.shared.setTo(filed: DBUser.CodingKeys.hasPLJobs.rawValue, false)
|
||||
}
|
||||
|
||||
currentPLPage += 1//分页
|
||||
if newJobs.count < kJobLimitS || newJobs.isEmpty {
|
||||
isPLFinshed = true
|
||||
}
|
||||
} else {
|
||||
plJobs.removeAll()
|
||||
}
|
||||
} catch {
|
||||
print("获取我发布的plJobs失败:\(error)")
|
||||
}
|
||||
}
|
||||
@MainActor func getPLJobsCount() async {
|
||||
if user.hasPLJobs {
|
||||
async let aCount = PLJobManager.shared.findJobsCount(by: userID, onlyActive: true)
|
||||
async let Count = PLJobManager.shared.findJobsCount(by: userID)
|
||||
|
||||
(aPLJobsCount, pLJobsCount) = await (aCount, Count)
|
||||
}
|
||||
}
|
||||
@MainActor func logOut() {
|
||||
AuthManager.shared.logOut()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {//暂时用此方法,因为立刻reset的话旧画面仍旧会渲染,导致错误
|
||||
self.reset()
|
||||
}
|
||||
}
|
||||
@MainActor func deletUserAll() async {
|
||||
let userID = AuthManager.shared.getUserID()
|
||||
await PLJobManager.shared.removeJobs(by: userID)
|
||||
await UserManager.shared.removeUser(id: userID)
|
||||
|
||||
if let lcuser = AuthManager.shared.getUser() {
|
||||
try? lcuser.set("isNewUser", value: true)
|
||||
try? await lcuser.save()//保存信息
|
||||
}
|
||||
AuthManager.shared.logOut()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { self.reset() }
|
||||
}
|
||||
|
||||
@MainActor func reset() {
|
||||
viewDidLoad = false
|
||||
user = DBUser()
|
||||
plJobs.removeAll()
|
||||
currentPLPage = 0
|
||||
isPLFinshed = false
|
||||
aPLJobsCount = 0
|
||||
pLJobsCount = 0
|
||||
}
|
||||
|
||||
}
|
159
IOS_study/Profile/ProfileViewModel.swift
Normal file
@ -0,0 +1,159 @@
|
||||
//
|
||||
// ProfileViewModel.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/15.
|
||||
//
|
||||
|
||||
import PhotosUI
|
||||
import SwiftUI
|
||||
|
||||
@Observable final class ProfileViewModel {
|
||||
var viewDidLoad = false
|
||||
//user
|
||||
var user = DBUser()
|
||||
var userID: String { user.id }
|
||||
var draftUser = DBUser()
|
||||
var isFcUser = false
|
||||
var isCompanyImagesLoaded = false
|
||||
var isPostingCompany = false
|
||||
//发布的工作
|
||||
var plJobs: [PLJob] = []
|
||||
var aPLJobsCount = 0
|
||||
var pLJobsCount = 0
|
||||
var isFcPLJobs = false
|
||||
var shouldRefreshMyPLJobs = false
|
||||
//分页用
|
||||
var currentPLPage = 0
|
||||
var isPLFinshed = false
|
||||
|
||||
//总的
|
||||
var isFcSome: Bool { isFcUser || isFcPLJobs }
|
||||
|
||||
|
||||
@MainActor func getUsersData() async throws {
|
||||
try await getUser()
|
||||
async let getPLJobs: () = getPLJobs(isRefresh: true)
|
||||
async let getPLJobsCount: () = getPLJobsCount()
|
||||
|
||||
|
||||
let _ = await (getPLJobs, getPLJobsCount)
|
||||
}
|
||||
|
||||
@MainActor func refreshIFNeed() async throws {
|
||||
if shouldRefreshMyPLJobs {
|
||||
async let getPLJobs: () = getPLJobs(isRefresh: true)
|
||||
async let getPLJobsCount: () = getPLJobsCount()
|
||||
let _ = await (getPLJobs, getPLJobsCount)
|
||||
shouldRefreshMyPLJobs = false
|
||||
}
|
||||
}
|
||||
|
||||
var logoUIImage: UIImage?//可选类型,? -> 默认nil
|
||||
var logoImageErrorMsg = ""
|
||||
var logoItem: PhotosPickerItem? {
|
||||
didSet {
|
||||
//guard let logoItem -> guard let logoItem = logoItem
|
||||
guard let logoItem else { return }
|
||||
Task {
|
||||
//把用户选择的item转换成。。。
|
||||
if let data = try? await logoItem.loadTransferable(type: Data.self), let uiImage = UIImage(data: data) {
|
||||
logoImageErrorMsg = ""
|
||||
logoUIImage = uiImage
|
||||
} else {
|
||||
print("PhotosPickerItem转换成图片失败")
|
||||
logoImageErrorMsg = "上传图片失败,请重新上传"
|
||||
}
|
||||
self.logoItem = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
var isLoadingCompanyImages = false
|
||||
var companyUIImages: [UIImage] = []
|
||||
var companyUIImagesErrorMsg = ""
|
||||
var companyItems: [PhotosPickerItem] = [] {
|
||||
didSet {
|
||||
if companyItems.isEmpty { return }
|
||||
Task { @MainActor in
|
||||
isLoadingCompanyImages = true
|
||||
defer {
|
||||
isLoadingCompanyImages = false
|
||||
}
|
||||
var newImages: [UIImage] = []
|
||||
for item in companyItems {
|
||||
if let data = try? await item.loadTransferable(type: Data.self), let uiImage = UIImage(data: data) {
|
||||
newImages.append(uiImage)
|
||||
}
|
||||
}
|
||||
companyUIImages.append(contentsOf: newImages)
|
||||
if newImages.count == companyItems.count {
|
||||
companyUIImagesErrorMsg = ""
|
||||
} else {
|
||||
companyUIImagesErrorMsg = "有图片上传失败,请重新上传"
|
||||
}
|
||||
companyItems.removeAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
@MainActor func loadCompanyLogoAndIamges() async {
|
||||
isCompanyImagesLoaded = false
|
||||
if !user.cLogoURLStr.isEmpty {
|
||||
if let url = URL(string: user.cLogoURLStr),
|
||||
let (data, _) = try? await URLSession.shared.data(from: url),
|
||||
let uiImage = UIImage(data: data) {
|
||||
logoUIImage = uiImage
|
||||
}
|
||||
}
|
||||
if !user.cImageURLStrs.isEmpty {
|
||||
var newImages: [UIImage] = []
|
||||
for cImageURLStr in user.cImageURLStrs {
|
||||
if let url = URL(string: cImageURLStr),
|
||||
let (data, _) = try? await URLSession.shared.data(from: url),
|
||||
let uiImage = UIImage(data: data) {
|
||||
newImages.append(uiImage)
|
||||
}
|
||||
}
|
||||
companyUIImages = newImages
|
||||
}
|
||||
isCompanyImagesLoaded = true
|
||||
}
|
||||
|
||||
@MainActor func postCompany() async throws {
|
||||
user = draftUser
|
||||
//处理空格(必填部分)
|
||||
user.cName = user.cName.trimmed
|
||||
user.cCity = user.cCity.trimmed
|
||||
//处理空格(选填部分)
|
||||
if user.cAboutUS.isBlank {
|
||||
user.cAboutUS = ""
|
||||
} else {
|
||||
user.cAboutUS = user.cAboutUS.trimmed
|
||||
}
|
||||
//并行上传图片,两种方法:async let 和 withThrowingTaskGroup
|
||||
async let logoURLStr: String? = {//闭包
|
||||
guard let logoUIImage else { return nil }
|
||||
return try await StorageManager.shared.saveImage(logoUIImage)
|
||||
}()
|
||||
if !companyUIImages.isEmpty {
|
||||
let companyImageResults = try await withThrowingTaskGroup(of:((Int, String).self)) { group in
|
||||
for (index, uiImage) in companyUIImages.enumerated() {
|
||||
group.addTask {
|
||||
let urlStr = try await StorageManager.shared.saveImage(uiImage)
|
||||
return (index, urlStr)
|
||||
}
|
||||
}
|
||||
var results: [(Int, String)] = []
|
||||
for try await result in group {
|
||||
results.append(result)
|
||||
}
|
||||
return results
|
||||
}
|
||||
user.cImageURLStrs = companyImageResults.sorted(by: { $0.0 < $1.0 }).map { $0.1 }
|
||||
}
|
||||
if let clogoURLStr = try await logoURLStr {
|
||||
user.cLogoURLStr = clogoURLStr
|
||||
}
|
||||
try await UserManager.shared.update(user: user)
|
||||
}
|
||||
|
||||
}
|
84
IOS_study/Profile/SettginsView.swift
Normal file
@ -0,0 +1,84 @@
|
||||
//
|
||||
// SettginsView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/10.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SettginsView: View {
|
||||
@Environment(ProfileViewModel.self) var ProfileVM
|
||||
@Environment(HUD.self) var hud
|
||||
@Binding var naviPath: NavigationPath
|
||||
@State var showSignOutDialog = false
|
||||
@State var showRemoveUserDialog = false
|
||||
@State var showRemoveUserAlert = false
|
||||
var isLogin = true
|
||||
var body: some View {
|
||||
List {
|
||||
Section("客服/商业合作") {
|
||||
Button {
|
||||
UIPasteboard.general.string = KdevContact
|
||||
hud.show("复制成功")
|
||||
} label: {
|
||||
HStack {
|
||||
Text("微信号")
|
||||
Spacer()
|
||||
Text(KdevContact).secondary()
|
||||
}
|
||||
}.settingsStyle()
|
||||
}
|
||||
|
||||
if isLogin {
|
||||
Section("账号") {
|
||||
HStack {
|
||||
Button {
|
||||
showSignOutDialog = true
|
||||
} label: {
|
||||
Text("退出登录").push(to: .leading).settingsStyle()
|
||||
}.settingsStyle()
|
||||
.confirmationDialog("确定退出吗?", isPresented: $showSignOutDialog, titleVisibility: .visible) {
|
||||
Button("确定", role: .destructive) {
|
||||
if !naviPath.isEmpty { naviPath.removeLast() }
|
||||
ProfileVM.logOut()
|
||||
NotificationCenter.default.post(name: .logoutSuccess, object: nil)
|
||||
}
|
||||
Button("取消", role: .cancel) { }
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
Button {
|
||||
showRemoveUserDialog = true
|
||||
} label: {
|
||||
Text("注销账号").push(to: .leading).settingsStyle()
|
||||
}.settingsStyle()
|
||||
.confirmationDialog("确定注销吗?", isPresented: $showRemoveUserDialog, titleVisibility: .visible) {
|
||||
Button("确定", role: .destructive) { showRemoveUserAlert = true }
|
||||
Button("取消", role: .cancel) { }
|
||||
}
|
||||
.alert("注销之后不可恢复,请再次确认", isPresented: $showRemoveUserAlert) {
|
||||
Button("确定", role: .destructive) { Task { await ProfileVM.deletUserAll() } }
|
||||
Button("取消", role: .cancel) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}.listStyle(.grouped)
|
||||
.toolbarBackgroundVisibility(.visible, for: .navigationBar)
|
||||
.navigationBarBackButtonHidden(isLogin).navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .principal) { Text("设置") }
|
||||
if isLogin {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button {
|
||||
if !naviPath.isEmpty { naviPath.removeLast() }
|
||||
} label: {
|
||||
Image(systemName: "chevron.left").secondary().padding(.horizontal,6)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
IOS_study/Review/ReviewViewModel.swift
Normal file
@ -0,0 +1,12 @@
|
||||
//
|
||||
// ReviewViewModel.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/7/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@Observable final class ReviewViewModel {
|
||||
var drafReview = Review()
|
||||
}
|
37
IOS_study/TaBarView.swift
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// TaBarView.swift
|
||||
// IOS_study
|
||||
//
|
||||
// Created by CC-star on 2025/6/29.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct TaBarView: View {
|
||||
@Environment(TabBarViewModel.self) var vm
|
||||
@State var authVM = AuthViewModel()
|
||||
@State var plJobVM1 = PLJobViewModel()
|
||||
@State var plJobVM4 = PLJobViewModel()
|
||||
@State var plJobVM5 = PLJobViewModel()
|
||||
var body: some View {
|
||||
@Bindable var vm = vm
|
||||
TabView(selection: $vm.selectedTab) {
|
||||
Tab("工作", systemImage: "yensign.circle", value: "job") {
|
||||
PLJobsView().toolbarBackground(.visible, for: .tabBar)
|
||||
.environment(plJobVM1)
|
||||
}
|
||||
Tab("雇佣", systemImage: "plus.circle", value: "postJob") {
|
||||
PostJobView().toolbarBackground(.visible, for: .tabBar)
|
||||
.environment(plJobVM4)
|
||||
}
|
||||
Tab("我的", systemImage: "person.circle", value: "profile") {
|
||||
ProfileView().toolbarBackground(.visible, for: .tabBar)
|
||||
.environment(plJobVM5)
|
||||
}
|
||||
}.environment(authVM)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
TaBarView()
|
||||
}
|