TL;DR
IIS Rewrite (ALL) URIs with Trailing Slash & preserve Fragment and Query Strings
<rule name="AddTrailingSlash" stopProcessing="true">
  <match url="^([^/]+:\/\/[^/#?]+|[^?#]+?)\/?((?:[^/?#]+\.[^/?#]+)?(?:[?#].*)?$)" />
  <action type="Redirect" url="{R:1}/{R:2}" redirectType="Permanent" />
</rule>
IIS use ECMAScript so you can Try it here : https://regexr.com/6ele7
Update
IIS Rewrite (Considered) URIs with Trailing Slash & preserve Fragment and Query Strings
<rule name="AddTrailingSlash" stopProcessing="true">
  <match url="^([^/]+:\/\/[^/#?]+|[^?#]+\/[^/.?#]+)([?#].*)?$" />
  <action type="Redirect" url="{R:1}/{R:2}" redirectType="Permanent" />
</rule>
Try it here : https://regexr.com/6fk3g
http://127.0.0.1  -->  http://127.0.0.1/
https://localhost  -->  https://localhost/
https://localhost?  -->  https://localhost/?
https://localhost/  -->  https://localhost/
https://my.site.com  -->  https://my.site.com/
https://my.site.com:443?  -->  https://my.site.com:443/?
https://my.site.com/  -->  https://my.site.com/
https://my.site.com/about.php  -->  https://my.site.com/about.php
https://my.site.com/about.php?  -->  https://my.site.com/about.php?
https://my.site.com/about  -->  https://my.site.com/about/
https://my.site.com/about?  -->  https://my.site.com/about/?
https://my.site.com/about/  -->  https://my.site.com/about/
https://my.site.com/about/?  -->  https://my.site.com/about/?
https://my.site.com/about?query  -->  https://my.site.com/about/?query
https://my.site.com/about/?query  -->  https://my.site.com/about/?query
https://my.site.com/about.php?query  -->  https://my.site.com/about.php?query
https://my.site.com/about#hash  -->  https://my.site.com/about/#hash
https://my.site.com/about/#hash  -->  https://my.site.com/about/#hash
https://my.site.com/about.php#hash  -->  https://my.site.com/about.php#hash
https://my.site.com/about?query#hash  -->  https://my.site.com/about/?query#hash
https://my.site.com/about/?query#hash  -->  https://my.site.com/about/?query#hash
https://my.site.com/folder.name/about?query  -->  https://my.site.com/folder.name/about/?query
https://my.site.com/about?query#hash:http://test.com?q  -->  https://my.site.com/about/?query#hash:http://test.com?q
Explaination (All)
- Level 1 - Lets just think about your examples:
^([^?#]+?)\/?([?#].*)?$
Group #1: ^ In first, [^?#] Any character except ?/#, Go much but lazy +? (Stop on first possible, by looking to next)
Ignore: \/? Then if a / exist or not
Group #2: [?#] = ?/# And .* Any much character next to that till $ End, (...)? If exist
It work well. But it will deal not right with:
https://my.site.com/about.php?query  -->  https://my.site.com/about.php/?query  !!!
So let's add an exception...
- Level 2 - How if we take possible file name Name.name.name.extas Group #2?
^([^?#]+?)\/?((?:[^/?#]+\.[^/?#]+)?(?:[?#].*)?)$
(?:...) Non-Capturing group
([^/?#]+\.[^/?#]+)? Look for any possible file name or (?:[?#].*)? Any possible query or anchor strings
Now everything is OK, except this:
https://my.site.com?  -->  https://my.site.com?  !!!
So we need another exception in Group #1
- Level 3 - Take just domain URI as an alternative
^([^/]+:\/\/[^/#?]+|[^?#]+?)\/?((?:[^/?#]+\.[^/?#]+)?(?:[?#].*)?$)
(...|...) Alternative
[^/]+:\/\/[^/#?]+ First check if (not lazy) any pattern like ...://... till not / # ? exist?
Now it work great!
+ Explaination (Considered)
- Level 4 - How if we just add a Not-Accepting .&/character set in first group to just match considered URIs and ignore others?
^([^/]+:\/\/[^/#?]+|[^?#]+\/[^/.?#]+)([?#].*)?$
\/[^/.?#]+ Check if after last / the set of characters be not /.?#
Now it is even smaller and faster!
Analyzing other method
As @károly-szabó answered well here, instead of looking for Not-Accepted character sets, we can look for matched pattern.
So if we want to use the method but in simpler way (2 Groups) (+ Some minor optimization), the regex will be:
^(https?:\/\/[\w.:-]+\/?(?:[\w.-]+\/)*[\w-]+(?!\/))([?#].*)?$
But URI path Accepted characters are more.
So a wider version of that Regex can be:
^(https?:\/\/[\w.:-]+\/?(?:[\w!#-)+-.;=@~]+\/)*[\w!#-);=@~+,-]+(?!\/))([?#].*)?$
Try it here: https://regexr.com/6elea
Note: Still "multibyte Unicode as domain name is allowed" but i ignored that in this method.
P.S.
Actually i don't think that we should rewrite it on IIS, because of these reasons:
- Anchors char #can be part of a folder name (by%23)
- A file name can have no extension
- IIS/Browsers usually will (/should) handle Anchors/Queries
 Ref:
I Mean:
https://my.site.com/  -->  (=Call root)
https://my.site.com/about  -->  (=Call root > Folder/File name about) 
https://my.site.com/about/  -->  (=Call root > Folder name about) 
https://my.site.com/about?query  -->  (=Call root > Folder/File name about + Query)
https://my.site.com/about/?query  -->  (=Call root > Folder name about + Query)
https://my.site.com/about.php?query  -->  (=Call root > File name about.php + Query)
[When browser strip it:]
https://my.site.com/about#hash  -->  (=Call root > Folder/File name about + Anchor)
https://my.site.com/about/#hash  -->  (=Call root > Folder name about + Anchor)
https://my.site.com/about.php#hash  -->  (=Call root > File name about.php + Anchor)
[If not?]
https://my.site.com/folder#name/?query#hash
https://my.site.com/folder.name/about.php?query=one/two