songzhen 1 ay önce
ebeveyn
işleme
5bffe08995

+ 1 - 1
.env.staging

@@ -1,7 +1,7 @@
 VITE_APP_ENV=staging
 
 # VITE_APP_BASE_URL=https://api.ztzhipin.com/api/
-VITE_APP_BASE_URL=http://101.126.146.250:8088/api/
+VITE_APP_BASE_URL=http://101.126.146.250:8084/
 
 VITE_APP_IM_USER_SUFFIX=''
 

+ 172 - 11
src/assets/iconfont/demo_index.html

@@ -55,12 +55,54 @@
           <ul class="icon_lists dib-box">
           
             <li class="dib">
+              <span class="icon iconfont">&#xe78f;</span>
+                <div class="name">eye</div>
+                <div class="code-name">&amp;#xe78f;</div>
+              </li>
+          
+            <li class="dib">
               <span class="icon iconfont">&#xe7d6;</span>
-                <div class="name">star</div>
+                <div class="name">share</div>
                 <div class="code-name">&amp;#xe7d6;</div>
               </li>
           
             <li class="dib">
+              <span class="icon iconfont">&#xe7e3;</span>
+                <div class="name">Thumbs-up</div>
+                <div class="code-name">&amp;#xe7e3;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7de;</span>
+                <div class="name">chatgroup</div>
+                <div class="code-name">&amp;#xe7de;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7e0;</span>
+                <div class="name">unlock</div>
+                <div class="code-name">&amp;#xe7e0;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7e1;</span>
+                <div class="name">more</div>
+                <div class="code-name">&amp;#xe7e1;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7e2;</span>
+                <div class="name">location</div>
+                <div class="code-name">&amp;#xe7e2;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe7dd;</span>
+                <div class="name">lock</div>
+                <div class="code-name">&amp;#xe7dd;</div>
+              </li>
+          
+            <li class="dib">
               <span class="icon iconfont">&#xe7d7;</span>
                 <div class="name">edit-1</div>
                 <div class="code-name">&amp;#xe7d7;</div>
@@ -378,10 +420,10 @@
 <pre><code class="language-css"
 >@font-face {
   font-family: 'iconfont';
-  src: url('iconfont.woff2?t=1736823174249') format('woff2'),
-       url('iconfont.woff?t=1736823174249') format('woff'),
-       url('iconfont.ttf?t=1736823174249') format('truetype'),
-       url('iconfont.svg?t=1736823174249#iconfont') format('svg');
+  src: url('iconfont.woff2?t=1737861799720') format('woff2'),
+       url('iconfont.woff?t=1737861799720') format('woff'),
+       url('iconfont.ttf?t=1737861799720') format('truetype'),
+       url('iconfont.svg?t=1737861799720#iconfont') format('svg');
 }
 </code></pre>
           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -408,11 +450,74 @@
         <ul class="icon_lists dib-box">
           
           <li class="dib">
-            <span class="icon iconfont icon-star1"></span>
+            <span class="icon iconfont icon-eye"></span>
             <div class="name">
-              star
+              eye
             </div>
-            <div class="code-name">.icon-star1
+            <div class="code-name">.icon-eye
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-share"></span>
+            <div class="name">
+              share
+            </div>
+            <div class="code-name">.icon-share
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-Thumbs-up"></span>
+            <div class="name">
+              Thumbs-up
+            </div>
+            <div class="code-name">.icon-Thumbs-up
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-chatgroup"></span>
+            <div class="name">
+              chatgroup
+            </div>
+            <div class="code-name">.icon-chatgroup
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-unlock"></span>
+            <div class="name">
+              unlock
+            </div>
+            <div class="code-name">.icon-unlock
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-more"></span>
+            <div class="name">
+              more
+            </div>
+            <div class="code-name">.icon-more
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-location"></span>
+            <div class="name">
+              location
+            </div>
+            <div class="code-name">.icon-location
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-lock"></span>
+            <div class="name">
+              lock
+            </div>
+            <div class="code-name">.icon-lock
             </div>
           </li>
           
@@ -895,10 +1000,66 @@
           
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#icon-star1"></use>
+                  <use xlink:href="#icon-eye"></use>
                 </svg>
-                <div class="name">star</div>
-                <div class="code-name">#icon-star1</div>
+                <div class="name">eye</div>
+                <div class="code-name">#icon-eye</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-share"></use>
+                </svg>
+                <div class="name">share</div>
+                <div class="code-name">#icon-share</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-Thumbs-up"></use>
+                </svg>
+                <div class="name">Thumbs-up</div>
+                <div class="code-name">#icon-Thumbs-up</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-chatgroup"></use>
+                </svg>
+                <div class="name">chatgroup</div>
+                <div class="code-name">#icon-chatgroup</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-unlock"></use>
+                </svg>
+                <div class="name">unlock</div>
+                <div class="code-name">#icon-unlock</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-more"></use>
+                </svg>
+                <div class="name">more</div>
+                <div class="code-name">#icon-more</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-location"></use>
+                </svg>
+                <div class="name">location</div>
+                <div class="code-name">#icon-location</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-lock"></use>
+                </svg>
+                <div class="name">lock</div>
+                <div class="code-name">#icon-lock</div>
             </li>
           
             <li class="dib">

+ 33 - 5
src/assets/iconfont/iconfont.css

@@ -1,9 +1,9 @@
 @font-face {
   font-family: "iconfont"; /* Project id 4723464 */
-  src: url('iconfont.woff2?t=1736823174249') format('woff2'),
-       url('iconfont.woff?t=1736823174249') format('woff'),
-       url('iconfont.ttf?t=1736823174249') format('truetype'),
-       url('iconfont.svg?t=1736823174249#iconfont') format('svg');
+  src: url('iconfont.woff2?t=1737861799720') format('woff2'),
+       url('iconfont.woff?t=1737861799720') format('woff'),
+       url('iconfont.ttf?t=1737861799720') format('truetype'),
+       url('iconfont.svg?t=1737861799720#iconfont') format('svg');
 }
 
 .iconfont {
@@ -14,10 +14,38 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
-.icon-star1:before {
+.icon-eye:before {
+  content: "\e78f";
+}
+
+.icon-share:before {
   content: "\e7d6";
 }
 
+.icon-Thumbs-up:before {
+  content: "\e7e3";
+}
+
+.icon-chatgroup:before {
+  content: "\e7de";
+}
+
+.icon-unlock:before {
+  content: "\e7e0";
+}
+
+.icon-more:before {
+  content: "\e7e1";
+}
+
+.icon-location:before {
+  content: "\e7e2";
+}
+
+.icon-lock:before {
+  content: "\e7dd";
+}
+
 .icon-edit-1:before {
   content: "\e7d7";
 }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
src/assets/iconfont/iconfont.js


+ 52 - 3
src/assets/iconfont/iconfont.json

@@ -6,13 +6,62 @@
   "description": "",
   "glyphs": [
     {
-      "icon_id": "43103288",
-      "name": "star",
-      "font_class": "star1",
+      "icon_id": "4765896",
+      "name": "eye",
+      "font_class": "eye",
+      "unicode": "e78f",
+      "unicode_decimal": 59279
+    },
+    {
+      "icon_id": "43223178",
+      "name": "share",
+      "font_class": "share",
       "unicode": "e7d6",
       "unicode_decimal": 59350
     },
     {
+      "icon_id": "43223177",
+      "name": "Thumbs-up",
+      "font_class": "Thumbs-up",
+      "unicode": "e7e3",
+      "unicode_decimal": 59363
+    },
+    {
+      "icon_id": "43210372",
+      "name": "chatgroup",
+      "font_class": "chatgroup",
+      "unicode": "e7de",
+      "unicode_decimal": 59358
+    },
+    {
+      "icon_id": "43209870",
+      "name": "unlock",
+      "font_class": "unlock",
+      "unicode": "e7e0",
+      "unicode_decimal": 59360
+    },
+    {
+      "icon_id": "43209869",
+      "name": "more",
+      "font_class": "more",
+      "unicode": "e7e1",
+      "unicode_decimal": 59361
+    },
+    {
+      "icon_id": "43209868",
+      "name": "location",
+      "font_class": "location",
+      "unicode": "e7e2",
+      "unicode_decimal": 59362
+    },
+    {
+      "icon_id": "43209895",
+      "name": "lock",
+      "font_class": "lock",
+      "unicode": "e7dd",
+      "unicode_decimal": 59357
+    },
+    {
       "icon_id": "43103287",
       "name": "edit-1",
       "font_class": "edit-1",

+ 15 - 1
src/assets/iconfont/iconfont.svg

@@ -14,7 +14,21 @@
     />
       <missing-glyph />
       
-      <glyph glyph-name="star1" unicode="&#59350;" d="M610.688 487.68l-98.56 199.68-98.56-199.68-220.48-32L352.64 300.16l-37.632-219.52 197.12 103.68 197.12-103.68-37.632 219.52 159.488 155.52-220.352 32z m319.36 18.24a19.2 19.2 0 0 0 10.688-32.768l-200.384-195.328 47.36-275.84a19.2 19.2 0 0 0-27.904-20.224l-247.68 130.24-247.68-130.24a19.2 19.2 0 0 0-27.904 20.224l47.296 275.84-200.384 195.328a19.2 19.2 0 0 0 10.624 32.768l276.928 40.256 123.84 250.88a19.2 19.2 0 0 0 34.432 0l123.904-250.88 276.928-40.256z"  horiz-adv-x="1024" />
+      <glyph glyph-name="eye" unicode="&#59279;" d="M942.2 409.8C847.4 609.5 704.1 710 512 710c-192.2 0-335.4-100.5-430.2-300.3-7.7-16.2-7.7-35.2 0-51.5C176.6 158.5 319.9 58 512 58c192.2 0 335.4 100.5 430.2 300.3 7.7 16.2 7.7 35 0 51.5zM512 130c-161.3 0-279.4 81.8-362.7 254C232.6 556.2 350.7 638 512 638c161.3 0 279.4-81.8 362.7-254C791.5 211.8 673.4 130 512 130zM508 560c-97.2 0-176-78.8-176-176s78.8-176 176-176 176 78.8 176 176-78.8 176-176 176z m0-288c-61.9 0-112 50.1-112 112s50.1 112 112 112 112-50.1 112-112-50.1-112-112-112z"  horiz-adv-x="1024" />
+      
+      <glyph glyph-name="share" unicode="&#59350;" d="M635.628308 553.078154a148.322462 148.322462 0 1 1-32.295385 55.256615l-214.961231-125.44a148.322462 148.322462 0 1 1 0-201.176615l215.04-125.282462a148.322462 148.322462 0 1 1 32.33477 55.217231l-215.11877 125.361231a148.204308 148.204308 0 0 1 0 90.584615l215.000616 125.479385z m108.97723 184.910769a84.283077 84.283077 0 1 0 0-168.605538 84.283077 84.283077 0 0 0 0 168.605538z m-393.373538-311.532308a32.216615 32.216615 0 0 1 1.969231-3.387077c6.695385-12.091077 10.476308-25.993846 10.476307-40.763076a83.889231 83.889231 0 0 0-12.445538-44.11077 84.283077 84.283077 0 0 0-156.16 44.11077 84.283077 84.283077 0 0 0 156.16 44.110769z m309.051077-315.510153c0 13.902769 3.387077 27.057231 9.373538 38.596923a32.610462 32.610462 0 0 1 4.529231 7.758769 84.283077 84.283077 0 1 0-13.902769-46.395077z"  horiz-adv-x="1024" />
+      
+      <glyph glyph-name="Thumbs-up" unicode="&#59363;" d="M446.293333 780.117333a29.866667 29.866667 0 0 0 27.306667 17.749334 145.066667 145.066667 0 0 0 145.066667-145.066667v-123.733333h200.362666a106.666667 106.666667 0 0 0 106.496-122.666667l-52.992-345.6a106.666667 106.666667 0 0 0-106.453333-90.666667H320a29.866667 29.866667 0 0 0-29.866667 29.866667v422.4a29.866667 29.866667 0 0 0 2.56 12.117333l153.6 345.6z m45.824-43.989333L349.866667 416.085333V29.866667h416.682666a46.933333 46.933333 0 0 1 46.933334 39.893333l52.992 345.6a46.976 46.976 0 0 1-46.933334 53.973333H588.8a29.866667 29.866667 0 0 0-29.866667 29.866667V652.8a85.333333 85.333333 0 0 1-66.816 83.328zM217.728 456.533333H320a29.866667 29.866667 0 0 0 29.866667-29.866666v-426.666667a29.866667 29.866667 0 0 0-29.866667-29.866667H217.728a118.613333 118.613333 0 0 0-119.338667 102.656 29.866667 29.866667 0 0 0-0.256 4.010667v268.8a29.866667 29.866667 0 0 0 0.256 4.010667c7.808 57.514667 57.386667 107.861333 119.338667 106.922666zM157.866667 343.424v-264.405333c4.778667-28.672 29.866667-49.664 59.093333-49.152H290.133333v366.933333H216.96c-27.392 0.469333-54.058667-22.698667-59.093333-53.376z"  horiz-adv-x="1024" />
+      
+      <glyph glyph-name="chatgroup" unicode="&#59358;" d="M224 618.688a181.312 181.312 0 1 0 362.688 0 181.312 181.312 0 0 0-362.688 0zM405.312 736a117.312 117.312 0 1 1 0-234.688 117.312 117.312 0 0 1 0 234.688zM668.224 763.136a32 32 0 0 0 43.904 10.88 181.248 181.248 0 0 0 0-310.784 32 32 0 1 0-33.024 54.848 117.248 117.248 0 0 1 0 201.152 32 32 0 0 0-10.88 43.904zM288 256A160 160 0 0 1 128 96v-64h544v64A160 160 0 0 1 512 256H288zM64 96A224 224 0 0 0 288 320H512a224 224 0 0 0 224-224v-128H64v128zM741.376 305.728a32 32 0 0 0 44.352 8.896c50.56-33.728 88.96-70.464 108.352-123.712 19.008-52.224 24-83.84 7.552-165.76a32 32 0 1 0-62.72 12.48c15.552 78.016 8 95.68-4.992 131.456-12.608 34.752-38.208 62.016-83.648 92.288a32 32 0 0 0-8.896 44.352z"  horiz-adv-x="1024" />
+      
+      <glyph glyph-name="unlock" unicode="&#59360;" d="M170.688 394.688A10.688 10.688 0 0 1 160 384v-384c0-5.888 4.8-10.688 10.688-10.688h682.624A10.688 10.688 0 0 1 864 0V384a10.688 10.688 0 0 1-10.688 10.688H170.688zM96 384c0 41.216 33.408 74.688 74.688 74.688h682.624c41.28 0 74.688-33.472 74.688-74.688v-384c0-41.28-33.408-74.688-74.688-74.688H170.688A74.688 74.688 0 0 0 96 0V384zM512 778.688a181.312 181.312 0 0 1-181.312-181.376v-170.688h-64V597.312a245.312 245.312 0 0 0 490.624 0h-64A181.312 181.312 0 0 1 512 778.624zM480 128v128h64v-128h-64z"  horiz-adv-x="1024" />
+      
+      <glyph glyph-name="more" unicode="&#59361;" d="M96 725.312c0 41.28 33.408 74.688 74.688 74.688H384c41.216 0 74.688-33.408 74.688-74.688V512c0-41.216-33.472-74.688-74.688-74.688H170.688A74.688 74.688 0 0 0 96 512V725.312zM170.688 736a10.688 10.688 0 0 1-10.688-10.688V512c0-5.888 4.8-10.688 10.688-10.688H384A10.688 10.688 0 0 1 394.688 512V725.312A10.688 10.688 0 0 1 384 736H170.688zM96 256c0 41.216 33.408 74.624 74.688 74.624H384c41.216 0 74.688-33.408 74.688-74.624v-213.376c0-41.216-33.472-74.624-74.688-74.624H170.688A74.688 74.688 0 0 0 96 42.624V256z m74.688 10.624A10.688 10.688 0 0 1 160 256v-213.376c0-5.824 4.8-10.624 10.688-10.624H384a10.688 10.688 0 0 1 10.688 10.624V256A10.688 10.688 0 0 1 384 266.624H170.688zM565.312 725.312c0 41.28 33.472 74.688 74.688 74.688h213.312c41.28 0 74.688-33.408 74.688-74.688V512c0-41.216-33.408-74.688-74.688-74.688H640A74.688 74.688 0 0 0 565.312 512V725.312zM640 736a10.688 10.688 0 0 1-10.688-10.688V512c0-5.888 4.8-10.688 10.688-10.688h213.312A10.688 10.688 0 0 1 864 512V725.312a10.688 10.688 0 0 1-10.688 10.688H640zM565.312 256c0 41.216 33.472 74.624 74.688 74.624h213.312c41.28 0 74.688-33.408 74.688-74.624v-213.376c0-41.216-33.408-74.624-74.688-74.624H640a74.688 74.688 0 0 0-74.688 74.624V256zM640 266.624a10.688 10.688 0 0 1-10.688-10.624v-213.376c0-5.888 4.8-10.624 10.688-10.624h213.312a10.688 10.688 0 0 1 10.688 10.624V256a10.688 10.688 0 0 1-10.688 10.624H640z"  horiz-adv-x="1024" />
+      
+      <glyph glyph-name="location" unicode="&#59362;" d="M672 512a160 160 0 1 0-320 0 160 160 0 0 0 320 0z m-64 0a96 96 0 1 1-192 0 96 96 0 0 1 192 0zM535.168-29.952a28.032 28.032 0 0 0-46.336 0L246.784 325.632a320.896 320.896 0 1 0 530.496 0l-242.112-355.584z m189.184 391.616a256.896 256.896 0 1 1-424.704 0L512 49.728l212.352 311.936z"  horiz-adv-x="1024" />
+      
+      <glyph glyph-name="lock" unicode="&#59357;" d="M170.688 394.688A10.688 10.688 0 0 1 160 384v-384c0-5.888 4.8-10.688 10.688-10.688h682.624A10.688 10.688 0 0 1 864 0V384a10.688 10.688 0 0 1-10.688 10.688H170.688zM96 384c0 41.216 33.408 74.688 74.688 74.688h682.624c41.28 0 74.688-33.472 74.688-74.688v-384c0-41.28-33.408-74.688-74.688-74.688H170.688A74.688 74.688 0 0 0 96 0V384zM512 778.688a181.312 181.312 0 0 1-181.312-181.376v-170.688h-64V597.312a245.312 245.312 0 0 0 490.624 0v-170.688h-64V597.312A181.312 181.312 0 0 1 512 778.624zM480 128v128h64v-128h-64z"  horiz-adv-x="1024" />
       
       <glyph glyph-name="edit-1" unicode="&#59351;" d="M904.448 579.2L696.96 786.752 742.144 832l207.552-207.552-45.248-45.248zM382.208 56.96l-231.232-46.272a19.2 19.2 0 0 0-22.592 22.592l46.272 231.232 467.008 467.008 207.616-207.552-467.072-467.072z m376.512 467.008l-117.056 117.056L233.6 232.96l-29.312-146.368 146.368 29.248 408.064 408.128zM960 192v-64h-256v64h256zM960 64v-64H544v64H960z"  horiz-adv-x="1024" />
       

BIN
src/assets/iconfont/iconfont.ttf


BIN
src/assets/iconfont/iconfont.woff


BIN
src/assets/iconfont/iconfont.woff2


BIN
src/assets/img/note-create/img_upload.png


+ 331 - 0
src/components/NoteCreate/Form.vue

@@ -0,0 +1,331 @@
+<template>
+  <div>
+    <NoteCreateGroupHeader title="正文内容" />
+    <div>
+      <el-input
+        v-model="projectTitle"
+        style="width: 325px"
+        :maxlength="50"
+        placeholder="请输入游记标题"
+        show-word-limit
+      />
+    </div>
+    <div class="mt-15">
+      <el-mention
+        v-model="value"
+        type="textarea"
+        :autosize="{ minRows: 8, maxRows: 100 }"
+        :options="mentionOptions"
+        :prefix="['@', '#']"
+        style="width: 100%"
+        :maxlength="10000"
+        :loading="mentionLoading"
+        whole
+        :check-is-whole="checkIsWhole"
+        placeholder="请输入游记内容"
+        @search="handleMentionSearch"
+        @select="handleMentionSelect"
+      >
+        <template #label="{ item }">
+          <div v-if="item.prefix == '@'" class="flex items-center space-x-5">
+            <img :src="item.headImageUrl" class="h-20 w-20 rounded-full" />
+            <span class="text-base text-black-3">{{ item.label }}</span>
+          </div>
+          <div
+            v-else-if="item.prefix == '#'"
+            class="flex items-center space-x-5 text-black-3"
+          >
+            <span>#</span>
+            <span class="w-160 truncate text-sm text-black-3">{{
+              item.label
+            }}</span>
+            <span class="text-sm text-black-9">421.1万次浏览</span>
+          </div>
+        </template>
+      </el-mention>
+    </div>
+    <div class="mt-20 flex items-center space-x-10">
+      <span class="text-base text-black-3">选择地区</span>
+      <el-cascader
+        v-model="endPlace"
+        :options="placeOptions"
+        :show-all-levels="false"
+        clearable
+        :props="placeOptionProps"
+        placeholder="选择地区"
+        style="width: 280px"
+      />
+    </div>
+    <div class="mt-20 flex items-center space-x-10">
+      <span class="text-base text-black-3">可见范围</span>
+      <el-select
+        v-model="visibleRange"
+        placeholder="是否公开"
+        style="width: 280px"
+      >
+        <el-option
+          v-for="item in visibleRangeOptions"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value"
+        />
+      </el-select>
+    </div>
+    <div class="mt-20 flex items-center space-x-10">
+      <span class="text-base text-black-3">关联群聊</span>
+      <el-select
+        v-model="tourImGroupId"
+        value-key="id"
+        filterable
+        placeholder="关联群聊"
+        remote
+        remote-show-suffix
+        :remote-method="getGroupList"
+        style="width: 280px"
+      >
+        <el-option
+          v-for="item in groupOptions"
+          :key="item.value.id"
+          :label="item.value.groupName"
+          :value="item.value"
+        >
+          <div class="flex items-center space-x-5">
+            <MultiHeader :size="30" :imgUrls="item.value.memberHeadImg" />
+            <span class="text-sm text-black-3">{{ item.value.groupName }}</span>
+            <span class="text-sm text-black-9">({{ item.value.count }}人)</span>
+          </div>
+        </el-option>
+      </el-select>
+    </div>
+    <NoteCreateGroupHeader title="更多参数" class="mt-15" />
+    <div class="mt-15 flex items-center space-x-10">
+      <span class="text-base text-black-3">出发时间</span>
+      <el-date-picker
+        v-model="departureTime"
+        type="date"
+        :editable="false"
+        :clearable="false"
+        value-format="YYYY-MM-DD"
+        placeholder="请选择时间"
+        style="width: 280px"
+        :disabled-date="(d) => $dayjs(d).isAfter($dayjs(), 'day')"
+      />
+    </div>
+    <div class="mt-20 flex items-center space-x-10">
+      <span class="text-base text-black-3">出行方式</span>
+      <el-select
+        v-model="travelMode"
+        placeholder="请选择出行方式"
+        style="width: 280px"
+      >
+        <el-option
+          v-for="item in travelModeOptions"
+          :key="item.id"
+          :label="item.name"
+          :value="item.id"
+        />
+      </el-select>
+    </div>
+    <div class="mt-20 flex items-center space-x-10">
+      <span class="text-base text-black-3">出行天数</span>
+      <el-input
+        v-model="countTimes"
+        style="width: 280px"
+        :maxlength="10"
+        placeholder="请输入天数"
+      />
+    </div>
+    <div class="mt-20 flex items-center space-x-10">
+      <span class="text-base text-black-3">游玩人数</span>
+      <el-input
+        v-model="travelNumber"
+        style="width: 280px"
+        :maxlength="10"
+        placeholder="请输入游玩人数"
+      />
+    </div>
+    <div class="mt-20 flex items-center space-x-10">
+      <span class="text-base text-black-3">人物关系</span>
+      <el-input
+        v-model="role"
+        style="width: 280px"
+        :maxlength="10"
+        placeholder="请输入游玩人数"
+      />
+    </div>
+    <div class="mt-20 flex items-center space-x-10">
+      <span class="text-base text-black-3">平均费用</span>
+      <el-input
+        v-model="role"
+        style="width: 280px"
+        :maxlength="10"
+        placeholder="请输入平均费用"
+      />
+    </div>
+    <div class="mt-20 flex items-center space-x-10">
+      <span class="text-base text-black-3">推荐指数</span>
+      <el-rate v-model="recommendationRate" size="large" />
+    </div>
+  </div>
+</template>
+
+<script setup>
+const projectTitle = defineModel('projectTitle')
+const endPlace = defineModel('endPlace')
+const visibleRange = defineModel('visibleRange')
+const tourImGroupId = defineModel('tourImGroupId')
+const departureTime = defineModel('departureTime')
+const travelMode = defineModel('travelMode')
+const countTimes = defineModel('countTimes')
+const travelNumber = defineModel('travelNumber')
+const role = defineModel('role')
+const recommendationRate = defineModel('recommendationRate')
+
+const value = ref('')
+
+const mentionOptions = ref()
+const mentionLoading = ref(false)
+const selectedMentionList = ref([])
+
+async function getAtUserList() {
+  mentionLoading.value = true
+  const { data } = await request(
+    '/website/tourism/publishTravelNotes/getFouceEachFriendsByName',
+    {
+      query: {
+        pageNum: 1,
+        pageSize: 10
+      }
+    }
+  )
+  mentionOptions.value = data.map((e) => {
+    return {
+      prefix: '@',
+      label: e.showName,
+      value: e.showName,
+      userId: e.userId,
+      headImageUrl: e.headImageUrl
+    }
+  })
+  mentionLoading.value = false
+}
+
+async function getTopicList() {
+  mentionLoading.value = true
+  const { data } = await request(
+    '/website/tourism/publishTravelNotes/getTopicListByName',
+    {
+      query: {
+        pageNum: 1,
+        pageSize: 10
+      }
+    }
+  )
+  mentionOptions.value = data.map((e) => {
+    return {
+      prefix: '#',
+      label: e.name,
+      value: e.name,
+      id: e.id
+    }
+  })
+  mentionLoading.value = false
+}
+
+watch(value, () => {
+  // console.log(value.value)
+})
+
+function handleMentionSearch(val, prefix) {
+  if (prefix === '@') {
+    getAtUserList()
+  } else if (prefix === '#') {
+    getTopicList()
+  }
+}
+function handleMentionSelect(option, prefix) {
+  console.log('handleMentionSelect', option)
+}
+
+function checkIsWhole(pattern, prefix) {
+  console.log(pattern)
+  return true
+}
+
+watch(tourImGroupId, () => {
+  // console.log(tourImGroupId.value)
+})
+
+const placeOptionProps = {
+  value: 'id',
+  label: 'menuName',
+  children: 'tourWriteBelongTabVoList',
+  emitPath: false
+}
+
+// 地区
+const placeOptions = ref([])
+async function getPlaceOptions() {
+  const { data } = await request(
+    '/website/tourism/publishTravelNotes/getWriteBelongTab'
+  )
+  placeOptions.value = data
+}
+
+// 可见范围
+const visibleRangeOptions = [
+  {
+    value: 0,
+    label: '全部可见'
+  },
+  {
+    value: 1,
+    label: '仅自己可见'
+  }
+]
+
+// 群聊
+const groupOptions = ref([])
+async function getGroupList(name) {
+  const { data } = await request(
+    '/website/tourism/publishTravelNotes/getTakePartImGroupListByName',
+    {
+      query: {
+        pageNum: 1,
+        pageSize: 20,
+        name
+      }
+    }
+  )
+  groupOptions.value = data.map((item) => {
+    return {
+      lable: item.groupName,
+      value: {
+        id: item.id,
+        groupName: item.groupName,
+        count: item.count,
+        memberHeadImg: item.memberHeadImg
+      }
+    }
+  })
+}
+
+// 旅行方式
+const travelModeOptions = ref([])
+async function getTravelModeOptions() {
+  const { data } = await request(
+    '/admin/app/extra/listDict?dictCode=TourTravelMode'
+  )
+  travelModeOptions.value = data
+}
+
+onMounted(() => {
+  // getAtUserList()
+  // getTopicList()
+  // getPlaceOptions()
+  // getTravelModeOptions()
+  // getGroupList()
+})
+</script>
+
+<style lang="scss" scoped></style>

+ 14 - 0
src/components/NoteCreate/GroupHeader.vue

@@ -0,0 +1,14 @@
+<template>
+  <div class="flex items-center py-10 text-base font-semibold text-black-3">
+    <span>{{ title }}</span>
+    <slot name="right"></slot>
+  </div>
+</template>
+
+<script setup>
+defineProps({
+  title: String
+})
+</script>
+
+<style lang="scss" scoped></style>

+ 117 - 0
src/components/NoteCreate/Pic.vue

@@ -0,0 +1,117 @@
+<template>
+  <div>
+    <div class="flex items-center space-x-5 py-20">
+      <span class="text-base font-semibold text-black-3">图片编辑</span>
+      <span class="text-sm text-black-9">({{ fileList.length }}/16)</span>
+    </div>
+    <el-upload
+      v-model:file-list="fileList"
+      name="uploadFile"
+      :limit="16"
+      :headers="{
+        Authorization: token
+      }"
+      :data="{
+        asImage: true,
+        fieldName: 'tourismTavelNotesUrl'
+      }"
+      multiple
+      :auto-upload="true"
+      :action="uploadUrl"
+      list-type="picture-card"
+    >
+      <div class="flex flex-col items-center text-black-9">
+        <span class="iconfont icon-plus" style="font-size: 20px"></span>
+        <span class="text-sm">添加</span>
+      </div>
+      <template #file="{ file }">
+        <div
+          class="relative flex h-full w-full cursor-pointer items-center justify-center"
+        >
+          <img :src="file.url" class="object-cover" alt="" />
+          <div
+            class="absolute bottom-0 left-0 right-0 top-0 flex flex-col items-center bg-black text-sm text-white opacity-0 transition-all hover:opacity-50"
+          >
+            <div class="mt-40 flex items-center space-x-20">
+              <span
+                @click="handlePreview(file)"
+                class="iconfont icon-eye"
+                style="font-size: 20px"
+              ></span>
+              <span
+                @click="handleDelete(file)"
+                class="iconfont icon-delete-one"
+                style="font-size: 20px"
+              ></span>
+            </div>
+            <div @click="handleSetCover" class="mt-25">设为首页</div>
+          </div>
+        </div>
+      </template>
+    </el-upload>
+    <el-dialog v-model="previewImgShow">
+      <div class="flex items-center justify-center">
+        <img class="h-[400px] object-cover" :src="previewImg" />
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+const useAuth = useAuthStore()
+const { token } = storeToRefs(useAuth)
+
+const route = useRoute()
+
+const imgUrls = computed(() => {
+  try {
+    return JSON.parse(route.query.imgUrls)
+  } catch (error) {
+    return []
+  }
+})
+
+const uploadUrl = `${import.meta.env.VITE_APP_BASE_URL}/admin/app/tourismProjectTravelNotesWrite/upload`
+
+const fileList = ref([])
+
+watch(
+  () => imgUrls.value,
+  () => {
+    fileList.value.push(...imgUrls.value.map((e) => ({ url: e })))
+  },
+  { immediate: true }
+)
+
+watch(
+  () => fileList.value,
+  () => {
+    console.log(fileList.value)
+  }
+)
+
+const previewImgShow = ref(false)
+const previewImg = ref('')
+function handlePreview(file) {
+  previewImgShow.value = true
+  previewImg.value = file.url
+}
+
+function handleDelete() {}
+
+function handleSetCover() {}
+</script>
+
+<style lang="scss" scoped>
+:deep(.el-upload--picture-card) {
+  --el-upload-picture-card-size: 120px;
+  --el-upload-list-picture-card-size: 120px;
+}
+:deep(.el-upload-list--picture-card) {
+  --el-upload-picture-card-size: 120px;
+  --el-upload-list-picture-card-size: 120px;
+}
+:deep(.el-upload-list--picture-card .el-upload-list__item) {
+  margin-right: 20px;
+}
+</style>

+ 2 - 1
src/middleware/create-note.global.js

@@ -14,7 +14,8 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
       return navigateTo({
         path: '/note-create',
         query: {
-          id
+          id,
+          ...query
         },
         replace: true
       })

+ 340 - 0
src/pages/note-create-old/index.client.vue

@@ -0,0 +1,340 @@
+<template>
+  <div v-if="!loading">
+    <CreateNoteHeaderBanner v-model:bannerUrl="noteJson.travelNotesBanner" />
+    <CreateNoteHeaderTitle v-model="noteJson.projectTitle" />
+    <CreateNoteForm
+      class="mt-40"
+      v-model:departureTime="noteJson.departureTime"
+      v-model:countTimes="noteJson.countTimes"
+      v-model:endPlace="noteJson.endPlace"
+      v-model:role="noteJson.role"
+      v-model:travelMode="noteJson.travelMode"
+      v-model:averageCost="noteJson.averageCost"
+      v-model:recommendationRate="noteJson.recommendationRate"
+      v-model:travelNumber="noteJson.travelNumber"
+    />
+    <div
+      class="flex justify-center bg-[url('~/assets/img/note-create/note_create_content_bg.png')] bg-contain bg-bottom bg-no-repeat"
+    >
+      <div class="mx-auto mt-30 flex w-wrap space-x-50">
+        <div class="min-h-360 w-[860px] pb-80">
+          <div>
+            <VueDraggable v-model="noteJson.travelNotesContent">
+              <template
+                v-for="(item, index) in noteJson.travelNotesContent"
+                :key="item.tmpId"
+              >
+                <CreateNoteInsertTitleSection
+                  v-if="item.type === defaultSectionTitle.type"
+                  :title="item.content"
+                  @onEdit="handleInsertOrEditTitle(index)"
+                  @onDelete="handleDeleteTitle(index)"
+                />
+                <template v-else-if="item.type === defaultSectionContent.type">
+                  <CreateNoteInsertContentSection
+                    v-model="item.content"
+                    @on-delete="handleDeleteContent(index)"
+                  />
+                </template>
+                <template v-else-if="item.type === defaultSectionImage.type">
+                  <CreateNoteInsertImageSection
+                    :url="item.content"
+                    @on-save-cover="handleSaveCover(item)"
+                    @on-delete="handleDeleteImage(index)"
+                  />
+                </template>
+              </template>
+            </VueDraggable>
+            <CreateNoteBottomActions
+              class="mt-50"
+              :publishLoading="publishLoading"
+              @on-preview="handlePreview"
+              @on-publish="handlePublish"
+            />
+          </div>
+        </div>
+        <el-affix :offset="20">
+          <CreateNoteLeftActions
+            @on-insert-title="handleInsertOrEditTitle"
+            @on-insert-content="handleInsertContent"
+            @on-insert-img="handleInsertImage"
+            @on-save-draft="handleSaveDraft"
+          />
+        </el-affix>
+      </div>
+    </div>
+
+    <CreateNoteInsertTitleModal
+      v-model:visible="insertTilteOptions.show"
+      :title="insertTilteOptions.content"
+      @on-ok="handleInsertOrEditTitleOk"
+    />
+    <CreateNoteInsertImageModal
+      v-model:visible="insertImageOptions.show"
+      @on-ok="handleInsertImageOk"
+    />
+    <CreateNotePreviewModal
+      v-model:visible="previewOptions.show"
+      :data="noteJson"
+    />
+    <CreateNoteUserInfoModal
+      v-model:visible="userInfoOptions.show"
+      @submit-ok="handleCollectUserInfoOk"
+    />
+    <CreateNotePublishResultModal
+      v-model:visible="publishResultModalOptions.show"
+    />
+  </div>
+</template>
+
+<script setup>
+import { cloneDeep } from 'lodash-es'
+import { VueDraggable } from 'vue-draggable-plus'
+import { nanoid } from 'nanoid'
+
+const { loading, setLoading } = useLoading()
+loading.value = false
+
+const defaultSectionTitle = {
+  type: 'sectionTitle',
+  content: ''
+}
+const defaultSectionContent = {
+  type: 'sectionContent',
+  content: ''
+}
+const defaultSectionImage = {
+  type: 'image',
+  content: ''
+}
+
+const defaultNoteJson = {
+  travelNotesBanner: null,
+  projectTitle: null,
+  departureTime: null,
+  countTimes: null,
+  endPlace: null,
+  role: null,
+  travelMode: null,
+  averageCost: null,
+  recommendationRate: null,
+  travelNumber: null,
+  travelNotesContent: []
+}
+const noteJson = reactive(defaultNoteJson)
+
+watch(noteJson, () => {}, { deep: true })
+
+const id = useRouteQuery('id')
+
+watch(
+  id,
+  () => {
+    getNoteDetail()
+  },
+  { immediate: true }
+)
+
+// 获取草稿详情
+async function getNoteDetail() {
+  try {
+    setLoading(true)
+    const res = await request(
+      `/website/tourism/publishTravelNotes/getDraftDetail?writeId=${id.value}`
+    )
+    const data = res.data ?? {}
+    Object.keys(noteJson).forEach((key) => {
+      noteJson[key] = data[key]
+      noteJson.travelNotesContent = data.travelNotesContent ?? []
+      if (noteJson.travelNotesContent.length === 0) {
+        noteJson.travelNotesContent.push({
+          type: defaultSectionContent.type,
+          content: '',
+          tmpId: nanoid()
+        })
+      }
+    })
+
+    setLoading(false)
+  } catch (error) {
+    setLoading(false)
+  }
+}
+
+/************ 插入段落标题逻辑 ********** */
+
+const insertTilteOptions = reactive({
+  show: false,
+  content: null,
+  editIndex: null
+})
+
+// 点击编辑或者新增段落标题,弹出dialog
+function handleInsertOrEditTitle(index) {
+  if (index === null || index === undefined) {
+    // 新增
+    insertTilteOptions.editIndex = null
+    insertTilteOptions.content = null
+  } else {
+    // 编辑
+    insertTilteOptions.editIndex = index
+    insertTilteOptions.content = noteJson.travelNotesContent[index].content
+  }
+  insertTilteOptions.show = true
+}
+
+// 确认编辑或者新增段落标题
+function handleInsertOrEditTitleOk(newTitle) {
+  if (insertTilteOptions.editIndex === null) {
+    noteJson.travelNotesContent.push({
+      type: defaultSectionTitle.type,
+      content: newTitle,
+      tmpId: nanoid()
+    })
+  } else {
+    noteJson.travelNotesContent[insertTilteOptions.editIndex].content = newTitle
+  }
+}
+
+// 删除段落标题
+function handleDeleteTitle(index) {
+  noteJson.travelNotesContent.splice(index, 1)
+}
+
+/******************插入正文相关逻辑*******************/
+
+function handleInsertContent() {
+  noteJson.travelNotesContent.push(
+    cloneDeep({
+      ...defaultSectionContent,
+      tmpId: nanoid()
+    })
+  )
+}
+
+function handleDeleteContent(index) {
+  noteJson.travelNotesContent.splice(index, 1)
+}
+
+/******************插入图片逻辑*******************/
+const insertImageOptions = reactive({
+  show: false
+})
+function handleInsertImage() {
+  insertImageOptions.show = true
+}
+
+function handleInsertImageOk(fileUrlList) {
+  const imageList = fileUrlList.map((e) => ({
+    type: defaultSectionImage.type,
+    content: e.fileUrl,
+    tmpId: nanoid()
+  }))
+  noteJson.travelNotesContent = (noteJson.travelNotesContent ?? []).concat(
+    imageList
+  )
+}
+
+function handleDeleteImage(index) {
+  if (noteJson.travelNotesContent[index].type === 'image') {
+    noteJson.travelNotesContent.splice(index, 1)
+  }
+}
+
+// 设为封面图
+function handleSaveCover(item) {
+  noteJson.travelNotesContent.forEach((e) => {
+    if (e.type === 'image') {
+      e.cover = 0
+    }
+    item.cover = 1
+  })
+  ElMessage.success('设置封面成功')
+}
+
+//保存为草稿
+async function handleSaveDraft() {
+  try {
+    await request('/website/tourism/publishTravelNotes/saveDraft', {
+      method: 'post',
+      body: {
+        ...noteJson,
+        id: id.value
+      }
+    })
+    ElMessage.success('草稿保存成功')
+  } finally {
+  }
+}
+
+// 预览
+const previewOptions = reactive({
+  show: false
+})
+function handlePreview() {
+  previewOptions.show = true
+}
+
+// 收集个人信息
+const userInfoOptions = reactive({
+  show: false
+})
+
+function handleCollectUserInfoOk() {
+  requestPublish()
+}
+
+const publishResultModalOptions = reactive({
+  show: false
+})
+
+// 发布
+async function handlePublish() {
+  if (!noteJson.travelNotesBanner) {
+    ElMessage.warning('请设置游记头图')
+    return
+  }
+  if (!noteJson.projectTitle) {
+    ElMessage.warning('请输入游记标题')
+    return
+  }
+  if (!noteJson.endPlace) {
+    ElMessage.warning('请选择目的地')
+    return
+  }
+  if (noteJson.travelNotesContent.length === 0) {
+    ElMessage.warning('游记内容不能为空')
+    return
+  }
+
+  const { data: isPerfect } = await request(
+    '/website/tourism/publishTravelNotes/isPerfect'
+  )
+  if (isPerfect === 0) {
+    // 需要收集个人信息
+    userInfoOptions.show = true
+  } else {
+    requestPublish()
+  }
+}
+
+const publishLoading = ref(false)
+async function requestPublish() {
+  try {
+    publishLoading.value = true
+    await request('/website/tourism/publishTravelNotes/publishDraft', {
+      method: 'post',
+      body: {
+        ...noteJson,
+        id: id.value
+      }
+    })
+    publishResultModalOptions.show = true
+    publishLoading.value = false
+  } catch (error) {
+    publishLoading.value = false
+  }
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 12 - 332
src/pages/note-create/index.client.vue

@@ -1,340 +1,20 @@
 <template>
-  <div v-if="!loading">
-    <CreateNoteHeaderBanner v-model:bannerUrl="noteJson.travelNotesBanner" />
-    <CreateNoteHeaderTitle v-model="noteJson.projectTitle" />
-    <CreateNoteForm
-      class="mt-40"
-      v-model:departureTime="noteJson.departureTime"
-      v-model:countTimes="noteJson.countTimes"
-      v-model:endPlace="noteJson.endPlace"
-      v-model:role="noteJson.role"
-      v-model:travelMode="noteJson.travelMode"
-      v-model:averageCost="noteJson.averageCost"
-      v-model:recommendationRate="noteJson.recommendationRate"
-      v-model:travelNumber="noteJson.travelNumber"
-    />
-    <div
-      class="flex justify-center bg-[url('~/assets/img/note-create/note_create_content_bg.png')] bg-contain bg-bottom bg-no-repeat"
-    >
-      <div class="mx-auto mt-30 flex w-wrap space-x-50">
-        <div class="min-h-360 w-[860px] pb-80">
-          <div>
-            <VueDraggable v-model="noteJson.travelNotesContent">
-              <template
-                v-for="(item, index) in noteJson.travelNotesContent"
-                :key="item.tmpId"
-              >
-                <CreateNoteInsertTitleSection
-                  v-if="item.type === defaultSectionTitle.type"
-                  :title="item.content"
-                  @onEdit="handleInsertOrEditTitle(index)"
-                  @onDelete="handleDeleteTitle(index)"
-                />
-                <template v-else-if="item.type === defaultSectionContent.type">
-                  <CreateNoteInsertContentSection
-                    v-model="item.content"
-                    @on-delete="handleDeleteContent(index)"
-                  />
-                </template>
-                <template v-else-if="item.type === defaultSectionImage.type">
-                  <CreateNoteInsertImageSection
-                    :url="item.content"
-                    @on-save-cover="handleSaveCover(item)"
-                    @on-delete="handleDeleteImage(index)"
-                  />
-                </template>
-              </template>
-            </VueDraggable>
-            <CreateNoteBottomActions
-              class="mt-50"
-              :publishLoading="publishLoading"
-              @on-preview="handlePreview"
-              @on-publish="handlePublish"
-            />
-          </div>
-        </div>
-        <el-affix :offset="20">
-          <CreateNoteLeftActions
-            @on-insert-title="handleInsertOrEditTitle"
-            @on-insert-content="handleInsertContent"
-            @on-insert-img="handleInsertImage"
-            @on-save-draft="handleSaveDraft"
-          />
-        </el-affix>
+  <div class="flex items-center justify-center bg-[#EEF1F8] pb-200 pt-20">
+    <div class="shadow-[0_4px_4px_0px_#e4e7ef w-wrap rounded-lg bg-white pb-20">
+      <div
+        class="flex h-50 items-center space-x-5 border-b border-[#E7E7E7] px-15 text-xl font-semibold text-black-3"
+      >
+        <span class="iconfont icon-left text-black-9"></span>
+        <span>发布图文</span>
+      </div>
+      <div class="px-20">
+        <NoteCreatePic />
+        <NoteCreateForm />
       </div>
     </div>
-
-    <CreateNoteInsertTitleModal
-      v-model:visible="insertTilteOptions.show"
-      :title="insertTilteOptions.content"
-      @on-ok="handleInsertOrEditTitleOk"
-    />
-    <CreateNoteInsertImageModal
-      v-model:visible="insertImageOptions.show"
-      @on-ok="handleInsertImageOk"
-    />
-    <CreateNotePreviewModal
-      v-model:visible="previewOptions.show"
-      :data="noteJson"
-    />
-    <CreateNoteUserInfoModal
-      v-model:visible="userInfoOptions.show"
-      @submit-ok="handleCollectUserInfoOk"
-    />
-    <CreateNotePublishResultModal
-      v-model:visible="publishResultModalOptions.show"
-    />
   </div>
 </template>
 
-<script setup>
-import { cloneDeep } from 'lodash-es'
-import { VueDraggable } from 'vue-draggable-plus'
-import { nanoid } from 'nanoid'
-
-const { loading, setLoading } = useLoading()
-loading.value = false
-
-const defaultSectionTitle = {
-  type: 'sectionTitle',
-  content: ''
-}
-const defaultSectionContent = {
-  type: 'sectionContent',
-  content: ''
-}
-const defaultSectionImage = {
-  type: 'image',
-  content: ''
-}
-
-const defaultNoteJson = {
-  travelNotesBanner: null,
-  projectTitle: null,
-  departureTime: null,
-  countTimes: null,
-  endPlace: null,
-  role: null,
-  travelMode: null,
-  averageCost: null,
-  recommendationRate: null,
-  travelNumber: null,
-  travelNotesContent: []
-}
-const noteJson = reactive(defaultNoteJson)
-
-watch(noteJson, () => {}, { deep: true })
-
-const id = useRouteQuery('id')
-
-watch(
-  id,
-  () => {
-    getNoteDetail()
-  },
-  { immediate: true }
-)
-
-// 获取草稿详情
-async function getNoteDetail() {
-  try {
-    setLoading(true)
-    const res = await request(
-      `/website/tourism/publishTravelNotes/getDraftDetail?writeId=${id.value}`
-    )
-    const data = res.data ?? {}
-    Object.keys(noteJson).forEach((key) => {
-      noteJson[key] = data[key]
-      noteJson.travelNotesContent = data.travelNotesContent ?? []
-      if (noteJson.travelNotesContent.length === 0) {
-        noteJson.travelNotesContent.push({
-          type: defaultSectionContent.type,
-          content: '',
-          tmpId: nanoid()
-        })
-      }
-    })
-
-    setLoading(false)
-  } catch (error) {
-    setLoading(false)
-  }
-}
-
-/************ 插入段落标题逻辑 ********** */
-
-const insertTilteOptions = reactive({
-  show: false,
-  content: null,
-  editIndex: null
-})
-
-// 点击编辑或者新增段落标题,弹出dialog
-function handleInsertOrEditTitle(index) {
-  if (index === null || index === undefined) {
-    // 新增
-    insertTilteOptions.editIndex = null
-    insertTilteOptions.content = null
-  } else {
-    // 编辑
-    insertTilteOptions.editIndex = index
-    insertTilteOptions.content = noteJson.travelNotesContent[index].content
-  }
-  insertTilteOptions.show = true
-}
-
-// 确认编辑或者新增段落标题
-function handleInsertOrEditTitleOk(newTitle) {
-  if (insertTilteOptions.editIndex === null) {
-    noteJson.travelNotesContent.push({
-      type: defaultSectionTitle.type,
-      content: newTitle,
-      tmpId: nanoid()
-    })
-  } else {
-    noteJson.travelNotesContent[insertTilteOptions.editIndex].content = newTitle
-  }
-}
-
-// 删除段落标题
-function handleDeleteTitle(index) {
-  noteJson.travelNotesContent.splice(index, 1)
-}
-
-/******************插入正文相关逻辑*******************/
-
-function handleInsertContent() {
-  noteJson.travelNotesContent.push(
-    cloneDeep({
-      ...defaultSectionContent,
-      tmpId: nanoid()
-    })
-  )
-}
-
-function handleDeleteContent(index) {
-  noteJson.travelNotesContent.splice(index, 1)
-}
-
-/******************插入图片逻辑*******************/
-const insertImageOptions = reactive({
-  show: false
-})
-function handleInsertImage() {
-  insertImageOptions.show = true
-}
-
-function handleInsertImageOk(fileUrlList) {
-  const imageList = fileUrlList.map((e) => ({
-    type: defaultSectionImage.type,
-    content: e.fileUrl,
-    tmpId: nanoid()
-  }))
-  noteJson.travelNotesContent = (noteJson.travelNotesContent ?? []).concat(
-    imageList
-  )
-}
-
-function handleDeleteImage(index) {
-  if (noteJson.travelNotesContent[index].type === 'image') {
-    noteJson.travelNotesContent.splice(index, 1)
-  }
-}
-
-// 设为封面图
-function handleSaveCover(item) {
-  noteJson.travelNotesContent.forEach((e) => {
-    if (e.type === 'image') {
-      e.cover = 0
-    }
-    item.cover = 1
-  })
-  ElMessage.success('设置封面成功')
-}
-
-//保存为草稿
-async function handleSaveDraft() {
-  try {
-    await request('/website/tourism/publishTravelNotes/saveDraft', {
-      method: 'post',
-      body: {
-        ...noteJson,
-        id: id.value
-      }
-    })
-    ElMessage.success('草稿保存成功')
-  } finally {
-  }
-}
-
-// 预览
-const previewOptions = reactive({
-  show: false
-})
-function handlePreview() {
-  previewOptions.show = true
-}
-
-// 收集个人信息
-const userInfoOptions = reactive({
-  show: false
-})
-
-function handleCollectUserInfoOk() {
-  requestPublish()
-}
-
-const publishResultModalOptions = reactive({
-  show: false
-})
-
-// 发布
-async function handlePublish() {
-  if (!noteJson.travelNotesBanner) {
-    ElMessage.warning('请设置游记头图')
-    return
-  }
-  if (!noteJson.projectTitle) {
-    ElMessage.warning('请输入游记标题')
-    return
-  }
-  if (!noteJson.endPlace) {
-    ElMessage.warning('请选择目的地')
-    return
-  }
-  if (noteJson.travelNotesContent.length === 0) {
-    ElMessage.warning('游记内容不能为空')
-    return
-  }
-
-  const { data: isPerfect } = await request(
-    '/website/tourism/publishTravelNotes/isPerfect'
-  )
-  if (isPerfect === 0) {
-    // 需要收集个人信息
-    userInfoOptions.show = true
-  } else {
-    requestPublish()
-  }
-}
-
-const publishLoading = ref(false)
-async function requestPublish() {
-  try {
-    publishLoading.value = true
-    await request('/website/tourism/publishTravelNotes/publishDraft', {
-      method: 'post',
-      body: {
-        ...noteJson,
-        id: id.value
-      }
-    })
-    publishResultModalOptions.show = true
-    publishLoading.value = false
-  } catch (error) {
-    publishLoading.value = false
-  }
-}
-</script>
+<script setup></script>
 
 <style lang="scss" scoped></style>

+ 115 - 0
src/pages/note-create/pic.vue

@@ -0,0 +1,115 @@
+<template>
+  <div
+    class="flex items-center justify-center bg-[#EEF1F8] pb-200 pt-20 text-base"
+  >
+    <div class="shadow-[0_4px_4px_0px_#e4e7ef w-wrap rounded-lg bg-white pb-20">
+      <div
+        class="flex h-50 items-center border-b border-[#E7E7E7] px-15 text-xl font-semibold text-black-3"
+      >
+        上传图片
+      </div>
+      <div
+        v-loading="loading"
+        ref="dropZoneRef"
+        class="m-20 flex h-450 flex-col items-center justify-center rounded-lg border border-[#DBDBDB] bg-[#F7F7F7]"
+      >
+        <img src="~/assets/img/note-create/img_upload.png" class="h-89 w-138" />
+        <span class="mt-15 text-base text-black-3">拖图片到此或点击上传</span>
+        <span class="text-sm text-black-9">(最多支持上传16张)</span>
+        <el-button type="primary" class="mt-15" @click="handleOpenFileDialog"
+          >上传图片</el-button
+        >
+      </div>
+      <div class="grid grid-cols-3 items-center px-30 text-sm text-black-9">
+        <div>
+          <div class="text-base text-black">图片大小</div>
+          <div class="mt-5">支持上传的图片大小,最大20MB的图片文件</div>
+        </div>
+        <div>
+          <div class="text-base text-black">图片格式</div>
+          <div class="mt-5">
+            支持上传的图片格式,推荐使用png、ipg、jpeg、webp,不支持gif、live及其转化后的图片
+          </div>
+        </div>
+        <div>
+          <div class="text-base text-black">图片分辨率</div>
+          <div class="mt-5">
+            推荐上传宽高比为3-4、分辨率不低于720*960的照片,
+            超过1280P的图片用网页铭上传西质更清晰
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { useDropZone, useFileDialog } from '@vueuse/core'
+
+const loading = ref(false)
+
+const dropZoneRef = ref()
+
+const avaliableFormats = ['png', 'jpg', 'jpeg', 'webp']
+
+const { open: handleOpenFileDialog, onChange } = useFileDialog({
+  accept: '.png,.jpeg,.JPG,.webp' // Set to accept only image files
+})
+
+onChange((files) => {
+  uploadFiles(files)
+})
+
+function onDrop(files) {
+  uploadFiles(files)
+}
+
+async function uploadFiles(files) {
+  console.log(files)
+  for (let i = 0; i < files.length; i++) {
+    const file = files[i]
+    const format = file.name.split('.').pop()
+    if (!avaliableFormats.includes(format)) {
+      ElMessage.error('图片格式不正确,请重新选择')
+      return
+    }
+  }
+  // 循环上传
+  const imgUrls = []
+  try {
+    loading.value = true
+    for (let i = 0; i < files.length; i++) {
+      const file = files[i]
+      const formData = new FormData()
+      formData.append('uploadFile', file)
+      formData.append('asImage', true)
+      formData.append('fieldName', 'tourismTavelNotesUrl')
+      const res = await request(
+        '/admin/app/tourismProjectTravelNotesWrite/upload',
+        {
+          method: 'post',
+          body: formData
+        }
+      )
+      const url = res.data.fileUrl
+      imgUrls.push(url)
+    }
+    navigateTo({
+      path: '/note-create',
+      query: {
+        imgUrls: JSON.stringify(imgUrls)
+      }
+    })
+    loading.value = false
+  } catch (error) {
+    loading.value = false
+  }
+}
+
+const { isOverDropZone } = useDropZone(dropZoneRef, {
+  onDrop,
+  multiple: true
+})
+</script>
+
+<style lang="scss" scoped></style>

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor