Merge branch 'pollex-dev' into sit
刪除1個檔案
修改100個檔案
新增116個檔案
修改14個檔案名稱
| | |
| | | <link rel="stylesheet" href="style.css"></head> |
| | | <body> |
| | | <div class="bgc1 clearfix"> |
| | | <h1 class="mhmm mvm"><span class="fgc1">Font Name:</span> icomoon <small class="fgc1">(Glyphs: 34)</small></h1> |
| | | <h1 class="mhmm mvm"><span class="fgc1">Font Name:</span> icomoon <small class="fgc1">(Glyphs: 39)</small></h1> |
| | | </div> |
| | | <div class="clearfix mhl ptl"> |
| | | <h1 class="mvm mtn fgc1">Grid Size: Unknown</h1> |
| | | <div class="glyph fs1"> |
| | | <div class="clearfix bshadow0 pbs"> |
| | | <span class="icon-sort-decrease"></span> |
| | | <span class="mls"> icon-sort-decrease</span> |
| | | </div> |
| | | <fieldset class="fs0 size1of1 clearfix hidden-false"> |
| | | <input type="text" readonly value="e922" class="unit size1of2" /> |
| | | <input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" /> |
| | | </fieldset> |
| | | <div class="fs0 bshadow0 clearfix hidden-true"> |
| | | <span class="unit pvs fgc1">liga: </span> |
| | | <input type="text" readonly value="" class="liga unitRight" /> |
| | | </div> |
| | | </div> |
| | | <div class="glyph fs1"> |
| | | <div class="clearfix bshadow0 pbs"> |
| | | <span class="icon-sort-add"></span> |
| | | <span class="mls"> icon-sort-add</span> |
| | | </div> |
| | | <fieldset class="fs0 size1of1 clearfix hidden-false"> |
| | | <input type="text" readonly value="e923" class="unit size1of2" /> |
| | | <input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" /> |
| | | </fieldset> |
| | | <div class="fs0 bshadow0 clearfix hidden-true"> |
| | | <span class="unit pvs fgc1">liga: </span> |
| | | <input type="text" readonly value="" class="liga unitRight" /> |
| | | </div> |
| | | </div> |
| | | <div class="glyph fs1"> |
| | | <div class="clearfix bshadow0 pbs"> |
| | | <span class="icon-expand"></span> |
| | | <span class="mls"> icon-expand</span> |
| | | </div> |
| | | <fieldset class="fs0 size1of1 clearfix hidden-false"> |
| | | <input type="text" readonly value="e924" class="unit size1of2" /> |
| | | <input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" /> |
| | | </fieldset> |
| | | <div class="fs0 bshadow0 clearfix hidden-true"> |
| | | <span class="unit pvs fgc1">liga: </span> |
| | | <input type="text" readonly value="" class="liga unitRight" /> |
| | | </div> |
| | | </div> |
| | | <div class="glyph fs1"> |
| | | <div class="clearfix bshadow0 pbs"> |
| | | <span class="icon-sex-female"></span> |
| | | <span class="mls"> icon-sex-female</span> |
| | | </div> |
| | | <fieldset class="fs0 size1of1 clearfix hidden-false"> |
| | | <input type="text" readonly value="e925" class="unit size1of2" /> |
| | | <input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" /> |
| | | </fieldset> |
| | | <div class="fs0 bshadow0 clearfix hidden-true"> |
| | | <span class="unit pvs fgc1">liga: </span> |
| | | <input type="text" readonly value="" class="liga unitRight" /> |
| | | </div> |
| | | </div> |
| | | <div class="glyph fs1"> |
| | | <div class="clearfix bshadow0 pbs"> |
| | | <span class="icon-sex-male"></span> |
| | | <span class="mls"> icon-sex-male</span> |
| | | </div> |
| | | <fieldset class="fs0 size1of1 clearfix hidden-false"> |
| | | <input type="text" readonly value="e926" class="unit size1of2" /> |
| | | <input type="text" maxlength="1" readonly value="" class="unitRight size1of2 talign-right" /> |
| | | </fieldset> |
| | | <div class="fs0 bshadow0 clearfix hidden-true"> |
| | | <span class="unit pvs fgc1">liga: </span> |
| | | <input type="text" readonly value="" class="liga unitRight" /> |
| | | </div> |
| | | </div> |
| | | <div class="glyph fs1"> |
| | | <div class="clearfix bshadow0 pbs"> |
| | | <span class="icon-decrease"></span> |
| | | <span class="mls"> icon-decrease</span> |
| | | </div> |
| | |
| | | <glyph unicode="" glyph-name="time" d="M511.488 960c-282.55-0.292-511.488-229.41-511.488-512 0-282.77 229.23-512 512-512s512 229.23 512 512v0c0 0.076 0 0.166 0 0.257 0 282.628-229.116 511.744-511.744 511.744-0.27 0-0.54 0-0.81-0.001h0.042zM512 38.4c-226.216 0-409.6 183.384-409.6 409.6s183.384 409.6 409.6 409.6c226.216 0 409.6-183.384 409.6-409.6v0c0-226.216-183.384-409.6-409.6-409.6v0zM537.6 704h-76.8v-307.2l268.8-161.28 38.4 63.147-230.4 136.533z" /> |
| | | <glyph unicode="" glyph-name="top" d="M563.115 736.768l407.851-408.107c13.137-12.992 21.272-31.018 21.272-50.943 0-19.756-7.997-37.645-20.932-50.605l-68.138-68.138c-12.998-13.055-30.985-21.133-50.859-21.133-19.725 0-37.592 7.958-50.564 20.838l-289.532 289.788-289.195-289.195c-12.998-13.055-30.985-21.133-50.859-21.133-19.725 0-37.592 7.958-50.564 20.838l0.004-0.004-0.213 0.299-68.267 67.499c-13.075 12.989-21.168 30.978-21.168 50.859 0 19.71 7.956 37.563 20.831 50.521l408.231 408.316c13.040 13.276 31.18 21.503 51.243 21.503 19.596 0 37.358-7.849 50.315-20.575l-0.011 0.010z" /> |
| | | <glyph unicode="" glyph-name="trophy" horiz-adv-x="1152" d="M1104.017 831.982h-207.987v80.017c0 26.51-21.491 48.001-48.001 48.001v0h-544.040c-26.51 0-48.001-21.491-48.001-48.001v0-80.017h-207.987c-26.51 0-48.001-21.491-48.001-48.001v0-111.986c7.076-85.462 54.363-158.548 122.682-200.96l1.112-0.643c61.778-44.226 136.678-74.099 217.854-83.23l2.133-0.195c34.389-59.486 80.159-108.69 134.709-145.998l1.518-0.98v-144.002h-96.001c-3.854 0.457-8.318 0.718-12.843 0.718-62.746 0-113.776-50.163-115.172-112.574l-0.002-0.13v-24c0-13.255 10.745-24 24-24v0h592.041c13.255 0 24 10.745 24 24v0 24c-1.399 62.54-52.429 112.704-115.175 112.704-4.525 0-8.989-0.261-13.378-0.768l0.535 0.050h-96.001v144.002c56.068 38.356 101.826 87.64 135.041 145.086l1.137 2.132c83.314 9.166 158.252 38.973 221.451 84.147l-1.416-0.962c69.366 43.121 116.621 116.171 123.729 200.646l0.065 0.957v111.986c0 26.51-21.491 48.001-48.001 48.001v0zM198.627 574.41c-35.829 20.914-61.682 55.601-70.437 96.621l-0.172 0.964v32.016h128.402c1.638-61.997 10.873-121.145 26.808-177.479l-1.223 5.061c-31.815 11.002-59.407 25.499-84.329 43.47l0.951-0.653zM1024 671.995c-9.695-41.665-35.309-76.091-69.932-97.202l-0.677-0.383c-24.040-17.319-51.713-31.817-81.346-42.129l-2.271-0.688c14.711 51.273 23.946 110.421 25.564 171.467l0.020 0.952h128.594z" /> |
| | | <glyph unicode="" glyph-name="sort-decrease" d="M482.987 159.915h115.371c15.835 0 28.672-12.837 28.672-28.672v-58.027c0-15.835-12.837-28.672-28.672-28.672h-115.371c-15.835 0-28.672 12.837-28.672 28.672v58.027c0 15.835 12.837 28.672 28.672 28.672zM244.395 842.923c-5.317 5.071-12.534 8.192-20.48 8.192s-15.163-3.121-20.492-8.203l0.012 0.011-143.701-173.056c-5.035-5.211-8.137-12.317-8.137-20.148 0-15.764 12.572-28.592 28.237-29.003l0.038-0.001h86.357v-548.181c0-0.001 0-0.001 0-0.002 0-15.835 12.837-28.672 28.672-28.672 0.12 0 0.24 0.001 0.36 0.002h57.326c0.101-0.001 0.221-0.002 0.341-0.002 15.835 0 28.672 12.837 28.672 28.672 0 0.001 0 0.001 0 0.002v0 546.133h86.357c0.001 0 0.001 0 0.002 0 15.835 0 28.672 12.837 28.672 28.672 0 0.12-0.001 0.24-0.002 0.36v-0.018c-0.093 7.822-3.197 14.902-8.203 20.15l0.011-0.012zM482.987 620.715h345.771c15.835 0 28.672-12.837 28.672-28.672v-58.027c0-15.835-12.837-28.672-28.672-28.672h-345.771c-15.835 0-28.672 12.837-28.672 28.672v58.027c0 15.835 12.837 28.672 28.672 28.672zM482.987 390.315h230.741c15.835 0 28.672-12.837 28.672-28.672v-58.027c0-15.835-12.837-28.672-28.672-28.672h-230.741c-15.835 0-28.672 12.837-28.672 28.672v58.027c0 15.835 12.837 28.672 28.672 28.672zM482.987 851.115h461.141c15.835 0 28.672-12.837 28.672-28.672v-58.027c0-15.835-12.837-28.672-28.672-28.672h-461.141c-15.835 0-28.672 12.837-28.672 28.672v58.027c0 15.835 12.837 28.672 28.672 28.672z" /> |
| | | <glyph unicode="" glyph-name="sort-add" d="M482.987 851.115h115.371c15.835 0 28.672-12.837 28.672-28.672v-58.027c0-15.835-12.837-28.672-28.672-28.672h-115.371c-15.835 0-28.672 12.837-28.672 28.672v58.027c0 15.835 12.837 28.672 28.672 28.672zM482.987 620.715h230.741c15.835 0 28.672-12.837 28.672-28.672v-58.027c0-15.835-12.837-28.672-28.672-28.672h-230.741c-15.835 0-28.672 12.837-28.672 28.672v58.027c0 15.835 12.837 28.672 28.672 28.672zM482.987 159.915h461.141c15.835 0 28.672-12.837 28.672-28.672v-58.027c0-15.835-12.837-28.672-28.672-28.672h-461.141c-15.835 0-28.672 12.837-28.672 28.672v58.027c0 15.835 12.837 28.672 28.672 28.672zM482.987 390.315h345.771c15.835 0 28.672-12.837 28.672-28.672v-58.027c0-15.835-12.837-28.672-28.672-28.672h-345.771c-15.835 0-28.672 12.837-28.672 28.672v58.027c0 15.835 12.837 28.672 28.672 28.672zM367.957 275.285h-86.357v548.181c0 0.001 0 0.001 0 0.002 0 15.835-12.837 28.672-28.672 28.672-0.12 0-0.24-0.001-0.36-0.002h-57.326c-0.101 0.001-0.221 0.002-0.341 0.002-15.835 0-28.672-12.837-28.672-28.672 0-0.001 0-0.001 0-0.002v0-546.133h-86.357c-15.876-0.193-28.672-13.108-28.672-29.011 0-0.001 0-0.001 0-0.002v0c0-0.005 0-0.011 0-0.017 0-7.894 3.268-15.026 8.526-20.114l0.008-0.007 145.067-175.104c5.264-5.050 12.423-8.16 20.309-8.16s15.045 3.11 20.319 8.169l-0.010-0.010 144.043 173.056c5.21 5.192 8.433 12.374 8.433 20.309s-3.223 15.117-8.432 20.309l-0.001 0.001c-5.098 5.284-12.242 8.566-20.153 8.566-0.475 0-0.948-0.012-1.417-0.035l0.066 0.003z" /> |
| | | <glyph unicode="" glyph-name="expand" d="M769.024 444.928c-28.277 0-51.2-22.923-51.2-51.2v0-266.581c-0.191-20.659-16.888-37.355-37.528-37.547h-489.149c-20.736 0-37.547 16.81-37.547 37.547v0 489.131c0.571 20.291 17.156 36.523 37.533 36.523 0.005 0 0.010 0 0.015 0h266.581c28.277 0 51.2 22.923 51.2 51.2s-22.923 51.2-51.2 51.2v0h-266.581c-77.212-0.194-139.753-62.734-139.947-139.928v-488.126c0.194-77.212 62.734-139.753 139.928-139.947h489.15c76.772 0.771 138.733 63.091 138.923 139.928v266.599c0 0.003 0 0.007 0 0.010 0 27.917-22.343 50.615-50.122 51.189l-0.054 0.001zM969.045 876.373v0c-5.53 12.604-15.385 22.459-27.646 27.855l-0.344 0.135c-5.708 2.654-12.373 4.272-19.397 4.436l-0.059 0.001h-266.581c-28.277 0-51.2-22.923-51.2-51.2s22.923-51.2 51.2-51.2v0h143.019l-401.408-401.408c-9.335-9.225-15.116-22.028-15.116-36.181s5.781-26.956 15.111-36.176l0.005-0.005c9.263-9.252 22.054-14.974 36.181-14.974s26.918 5.722 36.182 14.974l401.407 401.407v-143.019c0-28.277 22.923-51.2 51.2-51.2s51.2 22.923 51.2 51.2v0 266.581c-0.029 6.778-1.406 13.226-3.877 19.101l0.122-0.328z" /> |
| | | <glyph unicode="" glyph-name="sex-female" d="M793.941 584.533c3.545 16.844 5.574 36.198 5.574 56.026 0 157.031-127.299 284.331-284.331 284.331-142.597 0-260.676-104.971-281.179-241.856l-0.192-1.562c-2.833-14.767-4.453-31.752-4.453-49.114 0-53.505 15.388-103.416 41.981-145.548l-0.664 1.126c41.637-71.349 112.064-122.023 194.943-136.296l1.665-0.237v-74.069h-149.845c-0.607 0.027-1.319 0.043-2.035 0.043-26.769 0-48.469-21.7-48.469-48.469 0-8.664 2.273-16.798 6.256-23.836l-0.126 0.241c8.178-14.971 23.811-24.956 41.777-24.956 0.673 0 1.343 0.014 2.009 0.042l-0.095-0.003h153.941v-57.344c0-32.085 0-64.512 0-96.597 0-0.060 0-0.132 0-0.203 0-13.24 5.346-25.231 13.997-33.932l-0.002 0.002c8.546-9.046 20.625-14.677 34.019-14.677 0.040 0 0.080 0 0.121 0h-0.006c0.001 0 0.003 0 0.005 0 26.529 0 48.080 21.313 48.464 47.75v0.036c0 22.869 0 45.739 0 68.267v87.381h153.259c0.924-0.164 1.987-0.258 3.072-0.258s2.148 0.094 3.182 0.274l-0.11-0.016c25.517 1.545 45.626 22.617 45.626 48.386 0 14.226-6.129 27.021-15.89 35.888l-0.040 0.036c-8.301 7.658-19.435 12.353-31.666 12.353-0.868 0-1.73-0.024-2.587-0.070l0.119 0.005c-36.523 0-72.704 0-109.227 0h-44.373v75.776c115.769 23.213 205.016 114.415 225.036 229.387l0.244 1.695zM704.171 635.733c0.001-0.273 0.002-0.597 0.002-0.92 0-51.896-21.29-98.82-55.611-132.513l-0.028-0.028c-34.195-34.070-81.368-55.135-133.461-55.135s-99.267 21.064-133.467 55.14l0.006-0.006c-34.070 34.195-55.135 81.368-55.135 133.461s21.064 99.267 55.14 133.467l-0.006-0.006c33.592 33.54 79.971 54.282 131.195 54.282 0.677 0 1.353-0.004 2.028-0.011l-0.103 0.001c0.001 0 0.002 0 0.003 0 103.888 0 188.173-83.927 188.754-187.678v-0.055z" /> |
| | | <glyph unicode="" glyph-name="sex-male" d="M565.248 556.885v189.781l137.899-138.24c2.383-2.598 5.074-4.855 8.030-6.731l0.162-0.096c7.571-5.13 16.909-8.19 26.961-8.19 3.507 0 6.926 0.372 10.221 1.080l-0.318-0.057c13.076 2.557 24.025 10.155 30.95 20.643l0.111 0.179c5.394 7.775 8.618 17.412 8.618 27.801 0 14.575-6.344 27.668-16.42 36.669l-0.048 0.042-219.819 219.819c-1.695 2.13-3.484 4.027-5.418 5.765l-0.043 0.038c-8.628 7.35-19.905 11.822-32.226 11.822-1.633 0-3.248-0.079-4.841-0.232l0.203 0.016c-13.699-0.917-25.744-7.317-34.079-17.002l-0.054-0.064-216.405-216.405c-3.559-3.176-6.796-6.522-9.776-10.089l-0.122-0.151c-5.953-8.072-9.528-18.216-9.528-29.195 0-14.889 6.574-28.242 16.978-37.315l0.059-0.050c8.443-7.322 19.539-11.784 31.678-11.784 1.465 0 2.915 0.065 4.347 0.192l-0.185-0.013c13.664 0.478 25.785 6.705 34.082 16.323l0.051 0.061 136.533 136.533v-190.805c-137.566-25.119-240.475-144.041-240.475-286.998 0-144.662 105.377-264.711 243.558-287.575l1.695-0.232c14.016-2.329 30.205-3.692 46.701-3.754h0.062c160.99 0 291.499 130.508 291.499 291.499 0 142.407-102.118 260.963-237.121 286.436l-1.813 0.284zM671.744 156.843l-13.312-16.725-7.509-9.216c-34.834-35.13-83.115-56.879-136.476-56.879-47.528 0-91.026 17.254-124.572 45.84l0.27-0.224c-43.547 33.976-71.285 86.461-71.285 145.424 0 21.257 3.605 41.672 10.237 60.666l-0.392-1.29c20.237 73.614 82.804 128.246 159.267 136.462l0.818 0.071h51.883c94.474-13.866 166.186-94.355 166.186-191.594 0-41.636-13.148-80.202-35.519-111.771l0.405 0.602z" /> |
| | | </font></defs></svg> |
| | |
| | | {"IcoMoonType":"selection","icons":[{"icon":{"paths":["M947.023 423.724h-834.737c-42.513 0-76.977 34.464-76.977 76.977s34.464 76.977 76.977 76.977h834.737c42.513 0 76.977-34.464 76.977-76.977s-34.464-76.977-76.977-76.977h0z"],"attrs":[{}],"width":1059,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["decrease"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":2,"id":34,"name":"decrease","prevSize":32,"code":59648},"setIdx":0,"setId":2,"iconIdx":0},{"icon":{"paths":["M944.305 432.195h-352.501v-352.501c-0.083-44.019-35.777-79.674-79.803-79.695l-0.002-0c-43.994 0.083-79.632 35.735-79.695 79.725l-0 0.006v352.464h-352.537c-44.055 0-79.768 35.713-79.768 79.768s35.713 79.768 79.768 79.768v0h352.501v352.574c0 44.055 35.713 79.768 79.768 79.768s79.768-35.713 79.768-79.768l-0 0v-352.574h352.501c44.055 0 79.768-35.713 79.768-79.768s-35.713-79.768-79.768-79.768l0 0z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["add"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":3,"id":33,"name":"add","prevSize":32,"code":59649},"setIdx":0,"setId":2,"iconIdx":1},{"icon":{"paths":["M358.4 0c-0.076-0-0.166-0-0.256-0-197.797 0-358.144 160.347-358.144 358.144 0 0.090 0 0.18 0 0.27l-0-0.014c0 268.8 358.4 665.6 358.4 665.6s358.4-396.8 358.4-665.6c0-0.076 0-0.166 0-0.256 0-197.797-160.347-358.144-358.144-358.144-0.090 0-0.18 0-0.27 0l0.014-0zM358.4 486.4c-70.692 0-128-57.308-128-128s57.308-128 128-128c70.692 0 128 57.308 128 128v0c0 70.692-57.308 128-128 128h0z"],"attrs":[{}],"width":717,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["address"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":4,"id":32,"name":"address","prevSize":32,"code":59650},"setIdx":0,"setId":2,"iconIdx":2},{"icon":{"paths":["M447.993 511.981c141.38 0 255.991-114.611 255.991-255.991s-114.611-255.991-255.991-255.991c-141.38 0-255.991 114.611-255.991 255.991v-0c0 141.38 114.611 255.991 255.991 255.991v-0zM639.585 577.164l-95.609 382.81-63.988-272.006 63.988-111.998h-191.965l63.988 111.998-63.988 272.006-95.609-382.773c-142.815 6.542-256.155 123.796-256.401 267.577l-0 0.024v179.197h895.986v-179.197c-0.246-143.806-113.586-261.059-255.814-267.58l-0.587-0.021z"],"attrs":[{}],"width":896,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["agent"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":5,"id":31,"name":"agent","prevSize":32,"code":59651},"setIdx":0,"setId":2,"iconIdx":3},{"icon":{"paths":["M889.022 7.054l-831.526 383.772c-34.358 15.411-57.857 49.317-57.857 88.711 0 53.291 43.002 96.539 96.2 96.936l0.038 0h351.649v351.801c0.219 53.372 43.536 96.553 96.938 96.553 39.559 0 73.583-23.696 88.654-57.668l0.245-0.618 383.621-831.564c3.826-10.169 6.041-21.922 6.041-34.192 0-55.109-44.674-99.783-99.783-99.783-12.281 0-24.044 2.219-34.909 6.277l0.689-0.226z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["arrow"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":14,"id":30,"name":"arrow","prevSize":32,"code":59652},"setIdx":0,"setId":2,"iconIdx":4},{"icon":{"paths":["M448.963 503.181c138.95 0 251.59-112.641 251.59-251.59s-112.641-251.59-251.59-251.59v-0c-138.95 0-251.59 112.641-251.59 251.59s112.641 251.59 251.59 251.59v0zM448.963 119.488v0c72.958-0 132.103 59.144 132.103 132.103s-59.144 132.103-132.103 132.103c-72.958 0-132.103-59.144-132.103-132.103 0-0.013 0-0.026 0-0.039l-0 0.002c-0-72.938 59.128-132.065 132.065-132.065 0.013 0 0.026 0 0.039 0l-0.002-0z","M448.963 566.069c-167.938 0-448.963 50.013-448.963 217.355v240.576h893.721v-240.576c0-167.305-276.82-217.355-444.758-217.355zM774.233 904.475h-654.931v-121.051c0-40.263 142.671-97.83 329.475-97.83v0c186.804 0 325.27 57.567 325.27 97.83z"],"attrs":[{},{}],"width":894,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["avatar"],"colorPermutations":{"12552552551":[{},{}]}},"attrs":[{},{}],"properties":{"order":15,"id":29,"name":"avatar","prevSize":32,"code":59653},"setIdx":0,"setId":2,"iconIdx":5},{"icon":{"paths":["M908.486 692.817c-1.1-0.834-112.139-84.872-112.139-362.886 0-182.216-147.715-329.931-329.931-329.931s-329.931 147.715-329.931 329.931v0c0 278.014-111.039 362.052-111.191 362.242-15.359 10.347-25.326 27.674-25.326 47.328 0 31.414 25.465 56.881 56.878 56.885l819.14 0c25.031-0.194 46.179-16.601 53.474-39.229l0.112-0.401c1.696-5.184 2.675-11.15 2.675-17.344 0-19.089-9.291-36.008-23.598-46.482l-0.162-0.113zM174.901 682.616c47.923-94.131 76-205.282 76-322.99 0-10.443-0.221-20.835-0.659-31.17l0.050 1.475c0-119.383 96.779-216.162 216.162-216.162s216.162 96.779 216.162 216.162l0 0c-0.387 8.856-0.608 19.244-0.608 29.684 0 117.708 28.062 228.862 77.862 327.136l-1.9-4.135z","M573.853 872.459c-8.181-4.825-18.026-7.675-28.536-7.675-20.904 0-39.175 11.276-49.061 28.077l-0.145 0.266c-6.013 10.266-16.989 17.051-29.55 17.051-6.311 0-12.222-1.713-17.293-4.699l0.16 0.087c-5.181-3.025-9.376-7.22-12.314-12.241l-0.086-0.16c-10.022-17.142-28.338-28.474-49.3-28.474-31.434 0-56.916 25.482-56.916 56.916 0 10.472 2.828 20.283 7.762 28.711l-0.145-0.269c26.074 44.41 73.596 73.753 127.972 73.753s101.897-29.344 127.594-73.059l0.377-0.694c4.808-8.17 7.648-17.998 7.648-28.489 0-20.831-11.197-39.047-27.901-48.958l-0.263-0.144z"],"attrs":[{},{}],"width":932,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["bell"],"colorPermutations":{"12552552551":[{},{}]}},"attrs":[{},{}],"properties":{"order":16,"id":28,"name":"bell","prevSize":32,"code":59654},"setIdx":0,"setId":2,"iconIdx":6},{"icon":{"paths":["M307.2 460.8h-102.4v102.4h102.4zM512 460.8h-102.4v102.4h102.4zM716.8 460.8h-102.4v102.4h102.4zM819.2 102.4h-51.2v-102.4h-102.4v102.4h-409.6v-102.4h-102.4v102.4h-51.2c-56.277 0.024-101.889 45.651-101.889 101.931 0 0.165 0 0.33 0.001 0.495l-0-0.025-0.512 716.8c0 56.554 45.846 102.4 102.4 102.4v0h716.8c56.486-0.169 102.231-45.914 102.4-102.384l0-0.016v-716.8c-0.169-56.486-45.914-102.231-102.384-102.4l-0.016-0zM819.2 921.6h-716.8v-563.2h716.8z"],"attrs":[{}],"width":922,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["calender"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":17,"id":27,"name":"calender","prevSize":32,"code":59655},"setIdx":0,"setId":2,"iconIdx":7},{"icon":{"paths":["M910.222 0h-796.444c-62.838 0-113.778 50.94-113.778 113.778v0 796.444c0 62.838 50.94 113.778 113.778 113.778v0h796.444c62.838 0 113.778-50.94 113.778-113.778v0-796.444c0-62.838-50.94-113.778-113.778-113.778v0zM398.222 796.444l-284.444-284.444 80.213-80.213 204.231 203.662 431.787-431.787 80.213 80.782z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["checkbox-1"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":18,"id":26,"name":"checkbox-1","prevSize":32,"code":59656},"setIdx":0,"setId":2,"iconIdx":8},{"icon":{"paths":["M910.222 113.778v796.444h-796.444v-796.444h796.444zM910.222 0h-796.444c-62.76 0.193-113.584 51.018-113.778 113.759l-0 0.019v796.444c0.193 62.76 51.018 113.584 113.759 113.778l0.019 0h796.444c62.76-0.193 113.584-51.018 113.778-113.759l0-0.019v-796.444c-0.193-62.76-51.018-113.584-113.759-113.778l-0.019-0z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["checkbox"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":19,"id":25,"name":"checkbox","prevSize":32,"code":59657},"setIdx":0,"setId":2,"iconIdx":9},{"icon":{"paths":["M512 1024c-282.77 0-512-229.23-512-512s229.23-512 512-512c282.77 0 512 229.23 512 512v0c0 282.77-229.23 512-512 512h-0zM276.821 547.072l279.723 279.723c8.937 8.992 21.312 14.557 34.987 14.557s26.050-5.565 34.984-14.554l0.002-0.002 35.115-35.115c8.992-8.937 14.557-21.312 14.557-34.987s-5.565-26.050-14.554-34.984l-0.002-0.002-209.749-209.749 209.749-209.749c8.992-8.937 14.557-21.312 14.557-34.987s-5.565-26.050-14.554-34.984l-0.002-0.002-35.115-35.115c-8.937-8.992-21.312-14.557-34.987-14.557s-26.050 5.565-34.984 14.554l-0.002 0.002-279.723 279.765c-8.993 8.976-14.557 21.385-14.557 35.093s5.564 26.118 14.556 35.093l0.001 0.001z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["circle-left"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":20,"id":24,"name":"circle-left","prevSize":32,"code":59658},"setIdx":0,"setId":2,"iconIdx":10},{"icon":{"paths":["M512 1024c282.77 0 512-229.23 512-512s-229.23-512-512-512c-282.77 0-512 229.23-512 512v0c0 282.77 229.23 512 512 512v0zM747.136 547.115l-279.723 279.723c-8.937 8.992-21.312 14.557-34.987 14.557s-26.050-5.565-34.984-14.554l-0.002-0.002-35.115-35.115c-8.992-8.937-14.557-21.312-14.557-34.987s5.565-26.050 14.554-34.984l0.002-0.002 209.749-209.749-209.792-209.792c-8.992-8.937-14.557-21.312-14.557-34.987s5.565-26.050 14.554-34.984l0.002-0.002 35.115-35.115c8.937-8.992 21.312-14.557 34.987-14.557s26.050 5.565 34.984 14.554l0.002 0.002 279.723 279.723c9.019 8.98 14.6 21.406 14.6 35.136 0 13.709-5.564 26.118-14.556 35.093l-0.001 0.001z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["circle-right"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":21,"id":23,"name":"circle-right","prevSize":32,"code":59659},"setIdx":0,"setId":2,"iconIdx":11},{"icon":{"paths":["M843.123 30.996l-331.098 331.098-331.098-331.098c-19.185-19.146-45.668-30.985-74.916-30.985s-55.731 11.839-74.918 30.987l0.002-0.002c-19.117 19.18-30.937 45.643-30.937 74.868s11.82 55.687 30.94 74.87l-0.003-0.003 331.050 331.098-331.050 331.244c-19.191 19.173-31.063 45.67-31.063 74.94 0 58.504 47.427 105.93 105.93 105.93 29.234 0 55.702-11.842 74.868-30.991l-0.001 0.001 331.098-331.098 331.196 331.147c19.046 18.46 45.049 29.839 73.71 29.839 58.521 0 105.961-47.44 105.961-105.961 0-28.661-11.379-54.663-29.865-73.737l0.027 0.028-331.196-331.196 331.098-331.098c18.46-19.046 29.839-45.049 29.839-73.71 0-58.521-47.44-105.961-105.961-105.961-28.661 0-54.663 11.379-73.737 29.865l0.028-0.027z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["close"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":22,"id":22,"name":"close","prevSize":32,"code":59660},"setIdx":0,"setId":2,"iconIdx":12},{"icon":{"paths":["M895.851 0h-767.872c-70.681 0-127.979 57.298-127.979 127.979v0 575.904c0 70.681 57.298 127.979 127.979 127.979v0h191.968v167.993c-0 0.025-0 0.056-0 0.086 0 13.288 10.772 24.060 24.060 24.060 5.399 0 10.382-1.778 14.397-4.781l-0.063 0.045 249.558-187.403h287.952c70.681 0 127.979-57.298 127.979-127.979v0-575.904c0-70.681-57.298-127.979-127.979-127.979l0 0zM604.699 575.904h-371.821v-127.979h371.821zM791.42 296.697h-558.84v-127.979h558.84z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["comment"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":23,"id":21,"name":"comment","prevSize":32,"code":59661},"setIdx":0,"setId":2,"iconIdx":13},{"icon":{"paths":["M646.741 485.035v-323.371l-161.707-161.664-161.664 161.664v107.819h-323.371v754.517h970.112v-538.965zM215.595 916.224h-107.819v-107.819h107.819zM215.595 700.629h-107.819v-107.776h107.819zM215.595 485.035h-107.819v-107.776h107.819zM538.965 916.181h-107.819v-107.776h107.776zM538.965 700.587h-107.819v-107.733h107.776zM538.965 484.992h-107.819v-107.733h107.776zM538.965 269.397h-107.819v-107.733h107.776zM862.336 916.139h-107.819v-107.733h107.776zM862.336 700.544h-107.819v-107.691h107.776z"],"attrs":[{}],"width":970,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["company"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":24,"id":20,"name":"company","prevSize":32,"code":59662},"setIdx":0,"setId":2,"iconIdx":14},{"icon":{"paths":["M587.199 55.924l-55.924-55.924h-279.618l-55.924 55.924h-195.733v111.847h782.932v-111.847z","M33.107 870.508c1.405 85.070 70.697 153.493 155.97 153.493 0.217 0 0.434-0 0.65-0.001l-0.034 0h403.396c0.182 0.001 0.398 0.001 0.614 0.001 85.338 0 154.671-68.528 155.971-153.557l0.001-0.122v-647.895h-716.569zM144.954 332.112h493.023v538.023c-0.295 24.483-20.211 44.217-44.736 44.217-0.001 0-0.002-0-0.003-0l-403.21 0c-0.101 0.001-0.22 0.001-0.34 0.001-24.499 0-44.399-19.692-44.735-44.112l-0-0.032z","M288.939 834.903c30.886 0 55.924-25.038 55.924-55.924v0-316.901c0-30.886-25.038-55.924-55.924-55.924s-55.924 25.038-55.924 55.924v0 316.901c0 30.886 25.038 55.924 55.924 55.924v0z","M493.993 834.903c30.886 0 55.924-25.038 55.924-55.924v0-316.901c0-30.886-25.038-55.924-55.924-55.924s-55.924 25.038-55.924 55.924v0 316.901c0 30.886 25.038 55.924 55.924 55.924v0z"],"attrs":[{},{},{},{}],"width":783,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["delet"],"colorPermutations":{"12552552551":[{},{},{},{}]}},"attrs":[{},{},{},{}],"properties":{"order":6,"id":18,"name":"delet","prevSize":32,"code":59663},"setIdx":0,"setId":2,"iconIdx":15},{"icon":{"paths":["M563.115 854.229l407.851-408.107c13.086-12.984 21.186-30.975 21.186-50.858 0-19.714-7.963-37.568-20.848-50.521l0.003 0.003-68.139-68.139c-12.998-13.055-30.985-21.133-50.859-21.133-19.725 0-37.592 7.958-50.564 20.838l0.004-0.004-289.536 289.621-289.195-289.195c-13.008-13.118-31.038-21.24-50.965-21.24-19.778 0-37.688 8.001-50.669 20.943l0.002-0.002-0.299 0.299-67.968 67.499c-13.075 12.989-21.168 30.978-21.168 50.859 0 19.71 7.956 37.563 20.831 50.521l-0.004-0.004 408.235 408.32c13.040 13.276 31.18 21.503 51.243 21.503 19.596 0 37.358-7.849 50.315-20.575l-0.011 0.010z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["down"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":7,"id":17,"name":"down","prevSize":32,"code":59664},"setIdx":0,"setId":2,"iconIdx":16},{"icon":{"paths":["M864 0c88.366 0 160 71.634 160 160 0 36.2-12.022 69.593-32.292 96.402l0.292-0.402-64 64-224-224 64-64c26.324-19.976 59.637-32 95.758-32 0.085 0 0.17 0 0.255 0l-0.013-0zM64 736l-64 288 288-64 592.171-592-224.171-224.128zM715.563 363.563l-448 448-55.168-55.125 448-448z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["edit"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":8,"id":16,"name":"edit","prevSize":32,"code":59665},"setIdx":0,"setId":2,"iconIdx":17},{"icon":{"paths":["M1123.235 921.346l-217.634-208.847c5.132 1.805 11.050 2.848 17.211 2.848 15.851 0 30.089-6.902 39.875-17.864l0.046-0.052c37.481-38.161 71.036-80.412 99.866-125.951l1.835-3.103c5.666-8.976 9.029-19.899 9.029-31.606 0-11.056-2.999-21.412-8.227-30.299l0.153 0.28c-7.952-13.359-194.813-327.802-528.499-327.802h-1.312c-35.996 0.005-71.127 3.696-105.040 10.716l3.34-0.578c-18.092 3.787-32.895 15.391-40.954 31.036l-0.156 0.333-212.306-203.798c-10.7-10.287-25.264-16.622-41.308-16.622-32.938 0-59.64 26.702-59.64 59.64 0 16.894 7.024 32.148 18.312 42.999l0.020 0.019 162.768 156.208c-5.287 1.648-9.884 3.809-14.111 6.501l0.235-0.14c-95.97 62.868-175.328 142.911-235.543 236.445l-1.89 3.135c-5.835 9.056-9.303 20.115-9.303 31.985 0 11.035 2.997 21.371 8.222 30.236l-0.152-0.279c7.952 13.359 194.813 327.802 529.175 327.802 107.604-0.9 207.542-32.95 291.456-87.555l-2.1 1.281c1.312-0.835 2.385-1.908 3.618-2.863l210.398 201.929c10.7 10.287 25.264 16.622 41.308 16.622 32.938 0 59.64-26.702 59.64-59.64 0-16.894-7.024-32.148-18.312-42.999l-0.020-0.019zM536.729 298.223c210.716 0 354.679 168.573 405.529 238.546-20.464 28.603-41.631 53.756-64.584 77.15l0.097-0.1c-10.193 10.583-16.471 24.997-16.471 40.878 0 6.315 0.993 12.399 2.831 18.103l-0.116-0.417-385.213-369.747c17.213-2.809 37.053-4.414 57.267-4.414 0.232 0 0.464 0 0.696 0.001l-0.036-0zM480.591 505.758c6.937-5.309 12.58-11.943 16.621-19.556l0.156-0.323 89.972 86.354c-14.804 12.070-33.904 19.382-54.711 19.382-7.266 0-14.323-0.892-21.069-2.571l0.598 0.126c-21.583-3.776-39.081-18.284-47.003-37.713l-0.149-0.414c-0.942-3.344-1.484-7.185-1.484-11.153 0-13.911 6.658-26.266 16.961-34.054l0.107-0.078zM536.729 775.315c-210.716 0-354.4-168.215-405.211-238.546 49.32-68.027 109.037-125.034 177.432-169.904l2.551-1.571c11.947-7.883 20.761-19.68 24.668-33.535l0.101-0.418 78.482 75.301c-2.135 1.142-3.89 2.209-5.584 3.359l0.217-0.139c-39.593 30.204-64.883 77.409-64.883 130.521 0 16.497 2.44 32.424 6.979 47.441l-0.301-1.159c21.213 61.143 72.589 106.713 135.851 119.32l1.154 0.192c13.578 3.209 29.168 5.048 45.187 5.048 51.658 0 98.848-19.131 134.872-50.694l-0.235 0.202c1.923-1.788 3.692-3.67 5.327-5.665l0.080-0.1 67.389 64.686c-58.645 34.055-128.851 54.577-203.762 55.657l-0.314 0.004z"],"attrs":[{}],"width":1142,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["eye-1"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":9,"id":15,"name":"eye-1","prevSize":32,"code":59666},"setIdx":0,"setId":2,"iconIdx":18},{"icon":{"paths":["M1343.806 478.457c-9.762-19.575-244.11-478.457-667.948-478.457s-658.136 458.883-667.948 478.457c-4.994 9.78-7.92 21.332-7.92 33.568s2.926 23.787 8.117 33.994l-0.197-0.427c10.013 19.575 244.11 478.407 667.948 478.407s658.186-458.832 667.948-478.407c4.994-9.78 7.92-21.332 7.92-33.568s-2.926-23.787-8.117-33.994l0.197 0.427zM675.858 873.96c-272.446 0-457.030-266.138-514.553-361.809 57.523-95.671 242.107-361.809 514.553-361.809s457.13 266.288 514.553 361.809c-57.523 95.521-242.157 361.659-514.553 361.659z","M675.858 211.619c-165.896 0-300.381 134.485-300.381 300.381s134.485 300.381 300.381 300.381c165.896 0 300.381-134.485 300.381-300.381v0c-0.199-165.816-134.565-300.182-300.362-300.381l-0.020-0zM675.858 662.191c-82.948 0-150.191-67.243-150.191-150.191s67.243-150.191 150.191-150.191c82.948 0 150.191 67.243 150.191 150.191v0c0 82.948-67.243 150.191-150.191 150.191v0z"],"attrs":[{},{}],"width":1352,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["eye"],"colorPermutations":{"12552552551":[{},{}]}},"attrs":[{},{}],"properties":{"order":25,"id":14,"name":"eye","prevSize":32,"code":59667},"setIdx":0,"setId":2,"iconIdx":19},{"icon":{"paths":["M470.315 379.861v136.192c52.181 11.819 98.603 31.189 147.2 44.587v-136.405c-52.011-11.605-98.773-30.976-147.2-44.416zM916.907 133.888c-67.939 35.792-147.729 58.874-232.376 63.923l-1.608 0.077c-107.008-0.213-195.584-69.76-330.368-69.76-0.457-0.002-0.998-0.003-1.539-0.003-48.265 0-94.476 8.794-137.118 24.867l2.678-0.885c4.705-11.905 7.433-25.695 7.433-40.122 0-61.856-50.144-112-112-112s-112 50.144-112 112c0 37.882 18.807 71.371 47.593 91.64l0.355 0.237v772.267c-0 0.038-0 0.083-0 0.128 0 26.439 21.433 47.872 47.872 47.872 0.045 0 0.090-0 0.135-0l-0.007 0h32c0.038 0 0.083 0 0.128 0 26.439 0 47.872-21.433 47.872-47.872 0-0.045-0-0.090-0-0.135l0 0.007v-188.928c64.268-28.012 139.144-44.31 217.827-44.31 3.851 0 7.693 0.039 11.525 0.117l-0.573-0.009c107.179 0 195.584 69.589 330.368 69.589 92.324-0.765 177.356-31.352 246.099-82.581l-1.107 0.789c16.771-11.705 27.605-30.909 27.605-52.644 0-0.047-0-0.094-0-0.141l0 0.007v-486.144c-0.078-35.287-28.702-63.863-64-63.863-9.72 0-18.935 2.167-27.186 6.044l0.391-0.165zM323.157 651.008c-54.473 5.68-104.346 17.389-151.58 34.589l4.38-1.394v-140.8c42.966-16.959 92.83-29.258 144.754-34.612l2.446-0.204zM911.957 382.208c-42.344 20.263-91.614 36.787-143.125 47.106l-4.075 0.681v141.867c55.26-7.286 105.051-25.69 148.773-52.924l-1.573 0.913v140.8c-41.573 28.653-91.64 48.031-145.739 54.055l-1.461 0.132v-142.976c-14.605 2.234-31.455 3.51-48.603 3.51-35.177 0-69.1-5.37-100.994-15.334l2.396 0.645v134.912c-39.685-15.094-88.92-29.775-139.395-41.183l-7.805-1.483v-136.875c-28.906-6.694-62.098-10.532-96.186-10.532-17.976 0-35.702 1.067-53.12 3.141l2.106-0.204v-140.032c-55.565 9.583-104.997 24.104-151.88 43.532l4.68-1.718v-140.8c42.614-19.83 92.097-35.166 143.91-43.55l3.29-0.439v142.976c14.417-2.165 31.054-3.401 47.98-3.401 35.4 0 69.537 5.408 101.63 15.442l-2.41-0.649v-134.741c39.858 15.224 89.098 29.908 139.605 41.235l7.595 1.431v136.704c26.239 6.45 56.363 10.149 87.35 10.149 21.13 0 41.859-1.72 62.054-5.028l-2.204 0.298v-143.872c55.763-10.757 105.071-26.328 151.746-46.79l-4.546 1.777z"],"attrs":[{}],"width":1008,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["flag"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":26,"id":13,"name":"flag","prevSize":32,"code":59668},"setIdx":0,"setId":2,"iconIdx":20},{"icon":{"paths":["M460.771 358.396h102.293v-102.251h-102.293zM511.917 921.749c-0.012 0-0.027 0-0.041 0-226.23 0-409.625-183.395-409.625-409.625s183.395-409.625 409.625-409.625c226.215 0 409.601 183.372 409.625 409.581l0 0.002c0 226.23-183.395 409.625-409.625 409.625v0zM511.876-0.207c-282.717 0.071-511.876 229.274-511.876 512 0 282.77 229.23 512 512 512 282.697 0 511.882-229.113 512-511.782l0-0.011c0-282.77-229.23-512-512-512v0zM460.854 768.021h102.21v-307.25h-102.21z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["information"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":27,"id":12,"name":"information","prevSize":32,"code":59669},"setIdx":0,"setId":2,"iconIdx":21},{"icon":{"paths":["M22.52 566.594l435.159 434.886c13.85 13.942 33.032 22.572 54.23 22.572s40.38-8.63 54.226-22.567l0.004-0.004 72.291-72.291c13.931-13.855 22.553-33.035 22.553-54.23s-8.622-40.375-22.549-54.227l-0.003-0.003-308.501-308.41 308.365-308.365c13.931-13.855 22.553-33.035 22.553-54.23s-8.622-40.375-22.549-54.227l-0.003-0.003-71.973-72.792c-13.85-13.942-33.032-22.572-54.23-22.572s-40.38 8.63-54.226 22.567l-0.004 0.004-435.023 434.932c-14.154 13.904-22.927 33.246-22.927 54.637 0 21.234 8.643 40.449 22.604 54.319l0.004 0.004z"],"attrs":[{}],"width":661,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["left"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":28,"id":11,"name":"left","prevSize":32,"code":59670},"setIdx":0,"setId":2,"iconIdx":22},{"icon":{"paths":["M558.549 0.469l-465.451-0.469c-51.358 0.145-92.954 41.74-93.099 93.085l-0 0.014v837.803c0.145 51.358 41.74 92.954 93.085 93.099l0.014 0h465.451c51.358-0.145 92.954-41.74 93.099-93.085l0-0.014v-837.803c-0.194-51.199-41.744-92.629-92.97-92.629-0.045 0-0.090 0-0.136 0l0.007-0zM558.549 837.803h-465.451v-651.605h465.451z"],"attrs":[{}],"width":652,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["phone"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":29,"id":10,"name":"phone","prevSize":32,"code":59671},"setIdx":0,"setId":2,"iconIdx":23},{"icon":{"paths":["M384 0c-0.789-0.017-1.719-0.027-2.651-0.027-34.738 0-66.285 13.665-89.558 35.914l0.049-0.046c-22.202 23.224-35.867 54.77-35.867 89.509 0 0.932 0.010 1.862 0.029 2.789l-0.002-0.138c5.836 34.821 18.412 65.879 36.393 93.049l-0.553-0.889c2.672 8.237 4.213 17.715 4.213 27.553 0 2.918-0.136 5.803-0.401 8.652l0.027-0.365h-295.68v768h295.68c0.053-1.37 0.084-2.978 0.084-4.594 0-11.101-1.431-21.868-4.119-32.126l0.196 0.88c-17.428-26.281-30.005-57.34-35.644-90.756l-0.196-1.404c-0.017-0.789-0.027-1.719-0.027-2.651 0-34.738 13.665-66.285 35.914-89.558l-0.046 0.049c23.224-22.202 54.77-35.867 89.509-35.867 0.932 0 1.862 0.010 2.789 0.029l-0.138-0.002c0.789-0.017 1.719-0.027 2.651-0.027 34.738 0 66.285 13.665 89.558 35.914l-0.049-0.046c22.202 23.224 35.867 54.77 35.867 89.509 0 0.932-0.010 1.862-0.029 2.789l0.002-0.138c-5.836 34.821-18.412 65.879-36.393 93.049l0.553-0.889c-2.672 8.237-4.213 17.715-4.213 27.553 0 2.918 0.136 5.803 0.401 8.652l-0.027-0.365h295.68v-295.68c1.37-0.053 2.978-0.084 4.594-0.084 11.101 0 21.868 1.431 32.126 4.119l-0.88-0.196c26.281 17.428 57.34 30.005 90.756 35.644l1.404 0.196c70.692 0 128-57.308 128-128s-57.308-128-128-128v0c-34.821 5.836-65.879 18.412-93.049 36.393l0.889-0.553c-8.237 2.672-17.715 4.213-27.553 4.213-2.918 0-5.803-0.136-8.652-0.401l0.365 0.027v-295.68h-295.68c-0.053-1.37-0.084-2.978-0.084-4.594 0-11.101 1.431-21.868 4.119-32.126l-0.196 0.88c17.428-26.281 30.005-57.34 35.644-90.756l0.196-1.404c0.017-0.789 0.027-1.719 0.027-2.651 0-34.738-13.665-66.285-35.914-89.558l0.046 0.049c-23.224-22.202-54.77-35.867-89.509-35.867-0.932 0-1.862 0.010-2.789 0.029l0.138-0.002z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["puzzle"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":30,"id":9,"name":"puzzle","prevSize":32,"code":59672},"setIdx":0,"setId":2,"iconIdx":24},{"icon":{"paths":["M638.368 566.619l-435.042 434.86c-13.855 13.932-33.037 22.554-54.232 22.554s-40.377-8.622-54.229-22.55l-0.003-0.003-72.295-72.295c-13.932-13.855-22.554-33.037-22.554-54.232s8.622-40.377 22.55-54.229l0.003-0.003 308.378-308.378-308.378-308.378c-13.932-13.855-22.554-33.037-22.554-54.232s8.622-40.377 22.55-54.229l0.003-0.003 71.976-72.795c13.855-13.932 33.037-22.554 54.232-22.554s40.377 8.622 54.229 22.55l0.003 0.003 435.042 434.723c14.1 13.896 22.836 33.203 22.836 54.549 0 21.189-8.607 40.368-22.515 54.232l-0.002 0.002z"],"attrs":[{}],"width":661,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["right"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":31,"id":8,"name":"right","prevSize":32,"code":59673},"setIdx":0,"setId":2,"iconIdx":25},{"icon":{"paths":["M227.579 579.116v227.579l398.211 217.305 398.211-217.305v-227.579l-398.211 217.358zM625.789-0l-625.789 341.316 625.789 341.316 512-279.311v393.1h113.789v-455.105z"],"attrs":[{}],"width":1252,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["school"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":32,"id":7,"name":"school","prevSize":32,"code":59674},"setIdx":0,"setId":2,"iconIdx":26},{"icon":{"paths":["M1009.922 885.314l-199.391-199.391c-8.672-8.647-20.639-13.993-33.854-13.993-0.096 0-0.192 0-0.288 0.001l0.015-0h-32.592c54.858-69.783 87.982-158.908 87.982-255.769 0-229.618-186.142-415.76-415.76-415.76s-415.76 186.142-415.76 415.76c0 229.618 186.142 415.76 415.76 415.76 96.861 0 185.986-33.123 256.666-88.661l-0.897 0.679v32.464c-0 0.081-0.001 0.177-0.001 0.273 0 13.216 5.346 25.182 13.994 33.855l-0.001-0.001 199.391 199.391c8.657 8.711 20.645 14.103 33.893 14.103s25.236-5.392 33.891-14.1l0.002-0.002 56.609-56.609c8.774-8.732 14.204-20.817 14.204-34.171 0-13.184-5.292-25.131-13.869-33.834l0.006 0.006zM415.973 672.016c-141.361 0-255.957-114.596-255.957-255.957s114.596-255.957 255.957-255.957c141.361 0 255.957 114.596 255.957 255.957v0c-0.049 141.325-114.626 255.872-255.957 255.872l-0-0z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["search"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":33,"id":6,"name":"search","prevSize":32,"code":59675},"setIdx":0,"setId":2,"iconIdx":27},{"icon":{"paths":["M264.124 1024l-3.988-0.315c-45.874-4.728-81.349-43.172-81.349-89.9 0-9.949 1.608-19.523 4.579-28.476l-0.183 0.638 89.58-260.661-235.092-165.554c-19.618-13.599-33.145-34.797-36.332-59.244l-0.046-0.43c-0.823-4.244-1.294-9.125-1.294-14.115 0-0.018 0-0.036 0-0.054l-0 0.003c0.197-48.36 39.308-87.52 87.63-87.796l0.026-0h289.553l86.747-258.842c12.356-34.779 44.982-59.235 83.32-59.235 37.789 0 70.029 23.761 82.593 57.157l0.201 0.608 87.272 260.311h287.525c0.011-0 0.025-0 0.038-0 47.863 0 86.99 37.407 89.741 84.58l0.011 0.243v3.848l-0.315 3.883c-2.124 22.565-12.304 42.418-27.594 56.944l-0.039 0.036-11.963 10.074-233.098 167.233 89.755 261.011c3.161 8.633 4.99 18.602 4.99 28.997 0 27.516-12.812 52.041-32.795 67.933l-0.177 0.136c-13.364 11.803-30.658 19.438-49.692 20.728l-0.258 0.014-3.183 0.21-3.148-0.175c-18.973-1.079-36.226-7.647-50.416-18.125l0.256 0.181-229.565-163.77-230.369 164.12c-13.682 9.851-30.475 16.214-48.672 17.473l-0.298 0.017zM812.414 940.051v0.28zM282.243 939.876v0zM838.403 920.498v0zM139.39 423.032l258.003 181.889-97.94 285.006 247.929-176.642 247.649 176.502-97.94-284.586 254.015-182.169h-309.246l-94.442-282.243-94.722 282.243zM1004.972 423.032v0zM97.346 393.405l0.56 0.42z"],"attrs":[{}],"width":1095,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["star-1"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":34,"id":5,"name":"star-1","prevSize":32,"code":59676},"setIdx":0,"setId":2,"iconIdx":28},{"icon":{"paths":["M1060.923 354.485h-362.082l-110.034-328.386c-5.769-15.366-20.337-26.102-37.413-26.102s-31.644 10.737-37.322 25.828l-0.091 0.275-110.034 328.386h-364.574c-21.717 0.069-39.304 17.656-39.374 39.367l-0 0.007c-0 0.051-0.001 0.111-0.001 0.172 0 2.3 0.268 4.537 0.775 6.682l-0.039-0.196c1.082 11.588 7.245 21.545 16.211 27.732l0.127 0.083 297.753 209.856-114.363 332.062c-1.356 3.823-2.14 8.232-2.14 12.824 0 12.816 6.104 24.206 15.563 31.422l0.096 0.070c5.913 5.322 13.556 8.829 21.991 9.588l0.147 0.011c9.263-0.701 17.658-3.918 24.649-8.961l-0.142 0.098 290.483-207.038 290.483 207.038c6.807 5.047 15.229 8.284 24.374 8.857l0.132 0.007c8.688-0.56 16.438-4.176 22.272-9.773l-0.012 0.012c9.602-7.187 15.75-18.533 15.75-31.315 0-4.66-0.817-9.13-2.316-13.273l0.086 0.272-114.159-331.898 295.139-211.694 7.148-6.167c7.108-6.72 11.823-15.905 12.772-26.182l0.012-0.163c-1.204-22.063-19.388-39.497-41.643-39.497-0.078 0-0.156 0-0.234 0.001l0.012-0z"],"attrs":[{}],"width":1103,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["star"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":35,"id":4,"name":"star","prevSize":32,"code":59677},"setIdx":0,"setId":2,"iconIdx":29},{"icon":{"paths":["M662.613 361.941c-7.552-20.181 201.344-206.72 79.531-357.888-28.501-35.328-125.227 169.259-262.528 261.845-75.776 51.2-252.075 159.872-252.075 219.989v389.461c0 72.32 279.552 148.864 491.989 148.864 77.867 0 190.677-487.851 190.677-565.333s-240.128-76.459-247.467-96.725zM170.624 367.36c-0.681-0.010-1.486-0.016-2.292-0.016-93.126 0-168.619 75.493-168.619 168.619 0 3.187 0.088 6.354 0.263 9.498l-0.019-0.437v275.797c-0.161 2.71-0.252 5.878-0.252 9.068 0 90.015 72.972 162.987 162.987 162.987 2.79 0 5.564-0.070 8.32-0.209l-0.387 0.016c37.376 0-56.875-32.555-56.875-128.555v-362.667c0-100.565 94.251-134.187 56.875-134.187z"],"attrs":[{}],"width":910,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["thumbs-up"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":10,"id":3,"name":"thumbs-up","prevSize":32,"code":59678},"setIdx":0,"setId":2,"iconIdx":30},{"icon":{"paths":["M511.488 0c-282.55 0.292-511.488 229.41-511.488 512 0 282.77 229.23 512 512 512s512-229.23 512-512l-0 0c0-0.076 0-0.166 0-0.257 0-282.628-229.116-511.744-511.744-511.744-0.27 0-0.54 0-0.81 0.001l0.042-0zM512 921.6c-226.216 0-409.6-183.384-409.6-409.6s183.384-409.6 409.6-409.6c226.216 0 409.6 183.384 409.6 409.6v0c0 226.216-183.384 409.6-409.6 409.6v0zM537.6 256h-76.8v307.2l268.8 161.28 38.4-63.147-230.4-136.533z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["time"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":11,"id":2,"name":"time","prevSize":32,"code":59679},"setIdx":0,"setId":2,"iconIdx":31},{"icon":{"paths":["M563.115 223.232l407.851 408.107c13.137 12.992 21.272 31.018 21.272 50.943 0 19.756-7.997 37.645-20.932 50.605l0.001-0.001-68.139 68.139c-12.998 13.055-30.985 21.133-50.859 21.133-19.725 0-37.592-7.958-50.564-20.838l0.004 0.004-289.536-289.792-289.195 289.195c-12.998 13.055-30.985 21.133-50.859 21.133-19.725 0-37.592-7.958-50.564-20.838l0.004 0.004-0.213-0.299-68.267-67.499c-13.075-12.989-21.168-30.978-21.168-50.859 0-19.71 7.956-37.563 20.831-50.521l-0.004 0.004 408.235-408.32c13.040-13.276 31.18-21.503 51.243-21.503 19.596 0 37.358 7.849 50.315 20.575l-0.011-0.010z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["top"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":12,"id":1,"name":"top","prevSize":32,"code":59680},"setIdx":0,"setId":2,"iconIdx":32},{"icon":{"paths":["M1104.017 128.018h-207.987v-80.017c0-26.51-21.491-48.001-48.001-48.001v0h-544.040c-26.51 0-48.001 21.491-48.001 48.001v0 80.017h-207.987c-26.51 0-48.001 21.491-48.001 48.001v0 111.986c7.076 85.462 54.363 158.548 122.682 200.96l1.112 0.643c61.778 44.226 136.678 74.099 217.854 83.23l2.133 0.195c34.389 59.486 80.159 108.69 134.709 145.998l1.518 0.98v144.002h-96.001c-3.854-0.457-8.318-0.718-12.843-0.718-62.746 0-113.776 50.163-115.172 112.574l-0.002 0.13v24c0 13.255 10.745 24 24 24v0h592.041c13.255 0 24-10.745 24-24v0-24c-1.399-62.54-52.429-112.704-115.175-112.704-4.525 0-8.989 0.261-13.378 0.768l0.535-0.050h-96.001v-144.002c56.068-38.356 101.826-87.64 135.041-145.086l1.137-2.132c83.314-9.166 158.252-38.973 221.451-84.147l-1.416 0.962c69.366-43.121 116.621-116.171 123.729-200.646l0.065-0.957v-111.986c0-26.51-21.491-48.001-48.001-48.001v0zM198.627 385.59c-35.829-20.914-61.682-55.601-70.437-96.621l-0.172-0.964v-32.016h128.402c1.638 61.997 10.873 121.145 26.808 177.479l-1.223-5.061c-31.815-11.002-59.407-25.499-84.329-43.47l0.951 0.653zM1024 288.005c-9.695 41.665-35.309 76.091-69.932 97.202l-0.677 0.383c-24.040 17.319-51.713 31.817-81.346 42.129l-2.271 0.688c14.711-51.273 23.946-110.421 25.564-171.467l0.020-0.952h128.594z"],"attrs":[{}],"width":1152,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["trophy"],"colorPermutations":{"12552552551":[{}]}},"attrs":[{}],"properties":{"order":13,"id":0,"name":"trophy","prevSize":32,"code":59681},"setIdx":0,"setId":2,"iconIdx":33}],"height":1024,"metadata":{"name":"icomoon"},"preferences":{"showGlyphs":true,"showQuickUse":true,"showQuickUse2":true,"showSVGs":true,"fontPref":{"prefix":"icon-","metadata":{"fontFamily":"icomoon"},"metrics":{"emSize":1024,"baseline":6.25,"whitespace":50},"embed":false},"imagePref":{"prefix":"icon-","png":true,"useClassSelector":true,"color":0,"bgColor":16777215,"classSelector":".icon","name":"icomoon"},"historySize":50,"showCodes":true,"gridSize":16}} |
| | | {"IcoMoonType":"selection","icons":[{"icon":{"paths":["M482.987 800.085h115.371c15.835 0 28.672 12.837 28.672 28.672v58.027c0 15.835-12.837 28.672-28.672 28.672h-115.371c-15.835 0-28.672-12.837-28.672-28.672v-58.027c0-15.835 12.837-28.672 28.672-28.672z","M244.395 117.077c-5.317-5.071-12.534-8.192-20.48-8.192s-15.163 3.121-20.492 8.203l0.012-0.011-143.701 173.056c-5.035 5.211-8.137 12.317-8.137 20.148 0 15.764 12.572 28.592 28.237 29.003l0.038 0.001h86.357v548.181c-0 0.001-0 0.001-0 0.002 0 15.835 12.837 28.672 28.672 28.672 0.12 0 0.24-0.001 0.36-0.002l-0.018 0h57.344c0.101 0.001 0.221 0.002 0.341 0.002 15.835 0 28.672-12.837 28.672-28.672 0-0.001-0-0.001-0-0.002l0 0v-546.133h86.357c0.001 0 0.001 0 0.002 0 15.835 0 28.672-12.837 28.672-28.672 0-0.12-0.001-0.24-0.002-0.36l0 0.018c-0.093-7.822-3.197-14.902-8.203-20.15l0.011 0.012z","M482.987 339.285h345.771c15.835 0 28.672 12.837 28.672 28.672v58.027c0 15.835-12.837 28.672-28.672 28.672h-345.771c-15.835 0-28.672-12.837-28.672-28.672v-58.027c0-15.835 12.837-28.672 28.672-28.672z","M482.987 569.685h230.741c15.835 0 28.672 12.837 28.672 28.672v58.027c0 15.835-12.837 28.672-28.672 28.672h-230.741c-15.835 0-28.672-12.837-28.672-28.672v-58.027c0-15.835 12.837-28.672 28.672-28.672z","M482.987 108.885h461.141c15.835 0 28.672 12.837 28.672 28.672v58.027c0 15.835-12.837 28.672-28.672 28.672h-461.141c-15.835 0-28.672-12.837-28.672-28.672v-58.027c0-15.835 12.837-28.672 28.672-28.672z"],"attrs":[{},{},{},{},{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sort-decrease"]},"attrs":[{},{},{},{},{}],"properties":{"order":2,"id":38,"name":"sort-decrease","prevSize":32,"code":59682},"setIdx":0,"setId":2,"iconIdx":0},{"icon":{"paths":["M482.987 108.885h115.371c15.835 0 28.672 12.837 28.672 28.672v58.027c0 15.835-12.837 28.672-28.672 28.672h-115.371c-15.835 0-28.672-12.837-28.672-28.672v-58.027c0-15.835 12.837-28.672 28.672-28.672z","M482.987 339.285h230.741c15.835 0 28.672 12.837 28.672 28.672v58.027c0 15.835-12.837 28.672-28.672 28.672h-230.741c-15.835 0-28.672-12.837-28.672-28.672v-58.027c0-15.835 12.837-28.672 28.672-28.672z","M482.987 800.085h461.141c15.835 0 28.672 12.837 28.672 28.672v58.027c0 15.835-12.837 28.672-28.672 28.672h-461.141c-15.835 0-28.672-12.837-28.672-28.672v-58.027c0-15.835 12.837-28.672 28.672-28.672z","M482.987 569.685h345.771c15.835 0 28.672 12.837 28.672 28.672v58.027c0 15.835-12.837 28.672-28.672 28.672h-345.771c-15.835 0-28.672-12.837-28.672-28.672v-58.027c0-15.835 12.837-28.672 28.672-28.672z","M367.957 684.715h-86.357v-548.181c0-0.001 0-0.001 0-0.002 0-15.835-12.837-28.672-28.672-28.672-0.12 0-0.24 0.001-0.36 0.002l0.018-0h-57.344c-0.101-0.001-0.221-0.002-0.341-0.002-15.835 0-28.672 12.837-28.672 28.672 0 0.001 0 0.001 0 0.002l-0-0v546.133h-86.357c-15.876 0.193-28.672 13.108-28.672 29.011 0 0.001 0 0.001 0 0.002l-0-0c-0 0.005-0 0.011-0 0.017 0 7.894 3.268 15.026 8.526 20.114l0.008 0.007 145.067 175.104c5.264 5.050 12.423 8.16 20.309 8.16s15.045-3.11 20.319-8.169l-0.010 0.010 144.043-173.056c5.21-5.192 8.433-12.374 8.433-20.309s-3.223-15.117-8.432-20.309l-0.001-0.001c-5.098-5.284-12.242-8.566-20.153-8.566-0.475 0-0.948 0.012-1.417 0.035l0.066-0.003z"],"attrs":[{},{},{},{},{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sort-add"]},"attrs":[{},{},{},{},{}],"properties":{"order":3,"id":37,"name":"sort-add","prevSize":32,"code":59683},"setIdx":0,"setId":2,"iconIdx":1},{"icon":{"paths":["M769.024 515.072c-28.277 0-51.2 22.923-51.2 51.2v0 266.581c-0.191 20.659-16.888 37.355-37.528 37.547l-0.018 0h-489.131c-20.736 0-37.547-16.81-37.547-37.547v0-489.131c0.571-20.291 17.156-36.523 37.533-36.523 0.005 0 0.010 0 0.015 0l266.581-0c28.277 0 51.2-22.923 51.2-51.2s-22.923-51.2-51.2-51.2v0h-266.581c-77.212 0.194-139.753 62.734-139.947 139.928l-0 0.019v488.107c0.194 77.212 62.734 139.753 139.928 139.947l0.019 0h489.131c76.772-0.771 138.733-63.091 138.923-139.928l0-0.018v-266.581c0-0.003 0-0.007 0-0.010 0-27.917-22.343-50.615-50.122-51.189l-0.054-0.001z","M969.045 83.627v0c-5.53-12.604-15.385-22.459-27.646-27.855l-0.344-0.135c-5.708-2.654-12.373-4.272-19.397-4.436l-0.059-0.001h-266.581c-28.277 0-51.2 22.923-51.2 51.2s22.923 51.2 51.2 51.2v0h143.019l-401.408 401.408c-9.335 9.225-15.116 22.028-15.116 36.181s5.781 26.956 15.111 36.176l0.005 0.005c9.263 9.252 22.054 14.974 36.181 14.974s26.918-5.722 36.182-14.974l-0.001 0.001 401.408-401.408v143.019c0 28.277 22.923 51.2 51.2 51.2s51.2-22.923 51.2-51.2v0-266.581c-0.029-6.778-1.406-13.226-3.877-19.101l0.122 0.328z"],"attrs":[{},{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["expand"]},"attrs":[{},{}],"properties":{"order":4,"id":36,"name":"expand","prevSize":32,"code":59684},"setIdx":0,"setId":2,"iconIdx":2},{"icon":{"paths":["M793.941 375.467c3.545-16.844 5.574-36.198 5.574-56.026 0-157.031-127.299-284.331-284.331-284.331-142.597 0-260.676 104.971-281.179 241.856l-0.192 1.562c-2.833 14.767-4.453 31.752-4.453 49.114 0 53.505 15.388 103.416 41.981 145.548l-0.664-1.126c41.637 71.349 112.064 122.023 194.943 136.296l1.665 0.237v74.069h-149.845c-0.607-0.027-1.319-0.043-2.035-0.043-26.769 0-48.469 21.7-48.469 48.469 0 8.664 2.273 16.798 6.256 23.836l-0.126-0.241c8.178 14.971 23.811 24.956 41.777 24.956 0.673 0 1.343-0.014 2.009-0.042l-0.095 0.003h153.941v57.344c0 32.085 0 64.512 0 96.597-0 0.060-0 0.132-0 0.203 0 13.24 5.346 25.231 13.997 33.932l-0.002-0.002c8.546 9.046 20.625 14.677 34.019 14.677 0.040 0 0.080-0 0.121-0l-0.006 0c0.001 0 0.003 0 0.005 0 26.529 0 48.080-21.313 48.464-47.75l0-0.036c0-22.869 0-45.739 0-68.267v-87.381h153.259c0.924 0.164 1.987 0.258 3.072 0.258s2.148-0.094 3.182-0.274l-0.11 0.016c25.517-1.545 45.626-22.617 45.626-48.386 0-14.226-6.129-27.021-15.89-35.888l-0.040-0.036c-8.301-7.658-19.435-12.353-31.666-12.353-0.868 0-1.73 0.024-2.587 0.070l0.119-0.005c-36.523 0-72.704 0-109.227 0h-44.373v-75.776c115.769-23.213 205.016-114.415 225.036-229.387l0.244-1.695zM704.171 324.267c0.001 0.273 0.002 0.597 0.002 0.92 0 51.896-21.29 98.82-55.611 132.513l-0.028 0.028c-34.195 34.070-81.368 55.135-133.461 55.135s-99.267-21.064-133.467-55.14l0.006 0.006c-34.070-34.195-55.135-81.368-55.135-133.461s21.064-99.267 55.14-133.467l-0.006 0.006c33.592-33.54 79.971-54.282 131.195-54.282 0.677 0 1.353 0.004 2.028 0.011l-0.103-0.001c0.001-0 0.002-0 0.003-0 103.888 0 188.173 83.927 188.754 187.678l0 0.055z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sex-female"]},"attrs":[{}],"properties":{"order":5,"id":35,"name":"sex-female","prevSize":32,"code":59685},"setIdx":0,"setId":2,"iconIdx":3},{"icon":{"paths":["M565.248 403.115v-189.781l137.899 138.24c2.383 2.598 5.074 4.855 8.030 6.731l0.162 0.096c7.571 5.13 16.909 8.19 26.961 8.19 3.507 0 6.926-0.372 10.221-1.080l-0.318 0.057c13.076-2.557 24.025-10.155 30.95-20.643l0.111-0.179c5.394-7.775 8.618-17.412 8.618-27.801 0-14.575-6.344-27.668-16.42-36.669l-0.048-0.042-219.819-219.819c-1.695-2.13-3.484-4.027-5.418-5.765l-0.043-0.038c-8.628-7.35-19.905-11.822-32.226-11.822-1.633 0-3.248 0.079-4.841 0.232l0.203-0.016c-13.699 0.917-25.744 7.317-34.079 17.002l-0.054 0.064-216.405 216.405c-3.559 3.176-6.796 6.522-9.776 10.089l-0.122 0.151c-5.953 8.072-9.528 18.216-9.528 29.195 0 14.889 6.574 28.242 16.978 37.315l0.059 0.050c8.443 7.322 19.539 11.784 31.678 11.784 1.465 0 2.915-0.065 4.347-0.192l-0.185 0.013c13.664-0.478 25.785-6.705 34.082-16.323l0.051-0.061 136.533-136.533v190.805c-137.566 25.119-240.475 144.041-240.475 286.998 0 144.662 105.377 264.711 243.558 287.575l1.695 0.232c14.016 2.329 30.205 3.692 46.701 3.754l0.062 0c160.99 0 291.499-130.508 291.499-291.499 0-142.407-102.118-260.963-237.121-286.436l-1.813-0.284zM671.744 803.157l-13.312 16.725-7.509 9.216c-34.834 35.13-83.115 56.879-136.476 56.879-47.528 0-91.026-17.254-124.572-45.84l0.27 0.224c-43.547-33.976-71.285-86.461-71.285-145.424 0-21.257 3.605-41.672 10.237-60.666l-0.392 1.29c20.237-73.614 82.804-128.246 159.267-136.462l0.818-0.071h51.883c94.474 13.866 166.186 94.355 166.186 191.594 0 41.636-13.148 80.202-35.519 111.771l0.405-0.602z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sex-male"]},"attrs":[{}],"properties":{"order":6,"id":34,"name":"sex-male","prevSize":32,"code":59686},"setIdx":0,"setId":2,"iconIdx":4},{"icon":{"paths":["M947.023 423.724h-834.737c-42.513 0-76.977 34.464-76.977 76.977s34.464 76.977 76.977 76.977h834.737c42.513 0 76.977-34.464 76.977-76.977s-34.464-76.977-76.977-76.977h0z"],"width":1059,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["decrease"],"grid":0},"attrs":[{}],"properties":{"order":2,"id":0,"name":"decrease","prevSize":32,"code":59648},"setIdx":0,"setId":2,"iconIdx":5},{"icon":{"paths":["M944.305 432.195h-352.501v-352.501c-0.083-44.019-35.777-79.674-79.803-79.695l-0.002-0c-43.994 0.083-79.632 35.735-79.695 79.725l-0 0.006v352.464h-352.537c-44.055 0-79.768 35.713-79.768 79.768s35.713 79.768 79.768 79.768v0h352.501v352.574c0 44.055 35.713 79.768 79.768 79.768s79.768-35.713 79.768-79.768l-0 0v-352.574h352.501c44.055 0 79.768-35.713 79.768-79.768s-35.713-79.768-79.768-79.768l0 0z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["add"],"grid":0},"attrs":[{}],"properties":{"order":3,"id":1,"name":"add","prevSize":32,"code":59649},"setIdx":0,"setId":2,"iconIdx":6},{"icon":{"paths":["M358.4 0c-0.076-0-0.166-0-0.256-0-197.797 0-358.144 160.347-358.144 358.144 0 0.090 0 0.18 0 0.27l-0-0.014c0 268.8 358.4 665.6 358.4 665.6s358.4-396.8 358.4-665.6c0-0.076 0-0.166 0-0.256 0-197.797-160.347-358.144-358.144-358.144-0.090 0-0.18 0-0.27 0l0.014-0zM358.4 486.4c-70.692 0-128-57.308-128-128s57.308-128 128-128c70.692 0 128 57.308 128 128v0c0 70.692-57.308 128-128 128h0z"],"width":717,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["address"],"grid":0},"attrs":[{}],"properties":{"order":4,"id":2,"name":"address","prevSize":32,"code":59650},"setIdx":0,"setId":2,"iconIdx":7},{"icon":{"paths":["M447.993 511.981c141.38 0 255.991-114.611 255.991-255.991s-114.611-255.991-255.991-255.991c-141.38 0-255.991 114.611-255.991 255.991v-0c0 141.38 114.611 255.991 255.991 255.991v-0zM639.585 577.164l-95.609 382.81-63.988-272.006 63.988-111.998h-191.965l63.988 111.998-63.988 272.006-95.609-382.773c-142.815 6.542-256.155 123.796-256.401 267.577l-0 0.024v179.197h895.986v-179.197c-0.246-143.806-113.586-261.059-255.814-267.58l-0.587-0.021z"],"width":896,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["agent"],"grid":0},"attrs":[{}],"properties":{"order":5,"id":3,"name":"agent","prevSize":32,"code":59651},"setIdx":0,"setId":2,"iconIdx":8},{"icon":{"paths":["M889.022 7.054l-831.526 383.772c-34.358 15.411-57.857 49.317-57.857 88.711 0 53.291 43.002 96.539 96.2 96.936l0.038 0h351.649v351.801c0.219 53.372 43.536 96.553 96.938 96.553 39.559 0 73.583-23.696 88.654-57.668l0.245-0.618 383.621-831.564c3.826-10.169 6.041-21.922 6.041-34.192 0-55.109-44.674-99.783-99.783-99.783-12.281 0-24.044 2.219-34.909 6.277l0.689-0.226z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["arrow"],"grid":0},"attrs":[{}],"properties":{"order":14,"id":4,"name":"arrow","prevSize":32,"code":59652},"setIdx":0,"setId":2,"iconIdx":9},{"icon":{"paths":["M448.963 503.181c138.95 0 251.59-112.641 251.59-251.59s-112.641-251.59-251.59-251.59v-0c-138.95 0-251.59 112.641-251.59 251.59s112.641 251.59 251.59 251.59v0zM448.963 119.488v0c72.958-0 132.103 59.144 132.103 132.103s-59.144 132.103-132.103 132.103c-72.958 0-132.103-59.144-132.103-132.103 0-0.013 0-0.026 0-0.039l-0 0.002c-0-72.938 59.128-132.065 132.065-132.065 0.013 0 0.026 0 0.039 0l-0.002-0z","M448.963 566.069c-167.938 0-448.963 50.013-448.963 217.355v240.576h893.721v-240.576c0-167.305-276.82-217.355-444.758-217.355zM774.233 904.475h-654.931v-121.051c0-40.263 142.671-97.83 329.475-97.83v0c186.804 0 325.27 57.567 325.27 97.83z"],"width":894,"attrs":[{},{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{},{}]},"tags":["avatar"],"grid":0},"attrs":[{},{}],"properties":{"order":15,"id":5,"name":"avatar","prevSize":32,"code":59653},"setIdx":0,"setId":2,"iconIdx":10},{"icon":{"paths":["M908.486 692.817c-1.1-0.834-112.139-84.872-112.139-362.886 0-182.216-147.715-329.931-329.931-329.931s-329.931 147.715-329.931 329.931v0c0 278.014-111.039 362.052-111.191 362.242-15.359 10.347-25.326 27.674-25.326 47.328 0 31.414 25.465 56.881 56.878 56.885l819.14 0c25.031-0.194 46.179-16.601 53.474-39.229l0.112-0.401c1.696-5.184 2.675-11.15 2.675-17.344 0-19.089-9.291-36.008-23.598-46.482l-0.162-0.113zM174.901 682.616c47.923-94.131 76-205.282 76-322.99 0-10.443-0.221-20.835-0.659-31.17l0.050 1.475c0-119.383 96.779-216.162 216.162-216.162s216.162 96.779 216.162 216.162l0 0c-0.387 8.856-0.608 19.244-0.608 29.684 0 117.708 28.062 228.862 77.862 327.136l-1.9-4.135z","M573.853 872.459c-8.181-4.825-18.026-7.675-28.536-7.675-20.904 0-39.175 11.276-49.061 28.077l-0.145 0.266c-6.013 10.266-16.989 17.051-29.55 17.051-6.311 0-12.222-1.713-17.293-4.699l0.16 0.087c-5.181-3.025-9.376-7.22-12.314-12.241l-0.086-0.16c-10.022-17.142-28.338-28.474-49.3-28.474-31.434 0-56.916 25.482-56.916 56.916 0 10.472 2.828 20.283 7.762 28.711l-0.145-0.269c26.074 44.41 73.596 73.753 127.972 73.753s101.897-29.344 127.594-73.059l0.377-0.694c4.808-8.17 7.648-17.998 7.648-28.489 0-20.831-11.197-39.047-27.901-48.958l-0.263-0.144z"],"width":932,"attrs":[{},{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{},{}]},"tags":["bell"],"grid":0},"attrs":[{},{}],"properties":{"order":16,"id":6,"name":"bell","prevSize":32,"code":59654},"setIdx":0,"setId":2,"iconIdx":11},{"icon":{"paths":["M307.2 460.8h-102.4v102.4h102.4zM512 460.8h-102.4v102.4h102.4zM716.8 460.8h-102.4v102.4h102.4zM819.2 102.4h-51.2v-102.4h-102.4v102.4h-409.6v-102.4h-102.4v102.4h-51.2c-56.277 0.024-101.889 45.651-101.889 101.931 0 0.165 0 0.33 0.001 0.495l-0-0.025-0.512 716.8c0 56.554 45.846 102.4 102.4 102.4v0h716.8c56.486-0.169 102.231-45.914 102.4-102.384l0-0.016v-716.8c-0.169-56.486-45.914-102.231-102.384-102.4l-0.016-0zM819.2 921.6h-716.8v-563.2h716.8z"],"width":922,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["calender"],"grid":0},"attrs":[{}],"properties":{"order":17,"id":7,"name":"calender","prevSize":32,"code":59655},"setIdx":0,"setId":2,"iconIdx":12},{"icon":{"paths":["M910.222 0h-796.444c-62.838 0-113.778 50.94-113.778 113.778v0 796.444c0 62.838 50.94 113.778 113.778 113.778v0h796.444c62.838 0 113.778-50.94 113.778-113.778v0-796.444c0-62.838-50.94-113.778-113.778-113.778v0zM398.222 796.444l-284.444-284.444 80.213-80.213 204.231 203.662 431.787-431.787 80.213 80.782z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["checkbox-1"],"grid":0},"attrs":[{}],"properties":{"order":18,"id":8,"name":"checkbox-1","prevSize":32,"code":59656},"setIdx":0,"setId":2,"iconIdx":13},{"icon":{"paths":["M910.222 113.778v796.444h-796.444v-796.444h796.444zM910.222 0h-796.444c-62.76 0.193-113.584 51.018-113.778 113.759l-0 0.019v796.444c0.193 62.76 51.018 113.584 113.759 113.778l0.019 0h796.444c62.76-0.193 113.584-51.018 113.778-113.759l0-0.019v-796.444c-0.193-62.76-51.018-113.584-113.759-113.778l-0.019-0z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["checkbox"],"grid":0},"attrs":[{}],"properties":{"order":19,"id":9,"name":"checkbox","prevSize":32,"code":59657},"setIdx":0,"setId":2,"iconIdx":14},{"icon":{"paths":["M512 1024c-282.77 0-512-229.23-512-512s229.23-512 512-512c282.77 0 512 229.23 512 512v0c0 282.77-229.23 512-512 512h-0zM276.821 547.072l279.723 279.723c8.937 8.992 21.312 14.557 34.987 14.557s26.050-5.565 34.984-14.554l0.002-0.002 35.115-35.115c8.992-8.937 14.557-21.312 14.557-34.987s-5.565-26.050-14.554-34.984l-0.002-0.002-209.749-209.749 209.749-209.749c8.992-8.937 14.557-21.312 14.557-34.987s-5.565-26.050-14.554-34.984l-0.002-0.002-35.115-35.115c-8.937-8.992-21.312-14.557-34.987-14.557s-26.050 5.565-34.984 14.554l-0.002 0.002-279.723 279.765c-8.993 8.976-14.557 21.385-14.557 35.093s5.564 26.118 14.556 35.093l0.001 0.001z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["circle-left"],"grid":0},"attrs":[{}],"properties":{"order":20,"id":10,"name":"circle-left","prevSize":32,"code":59658},"setIdx":0,"setId":2,"iconIdx":15},{"icon":{"paths":["M512 1024c282.77 0 512-229.23 512-512s-229.23-512-512-512c-282.77 0-512 229.23-512 512v0c0 282.77 229.23 512 512 512v0zM747.136 547.115l-279.723 279.723c-8.937 8.992-21.312 14.557-34.987 14.557s-26.050-5.565-34.984-14.554l-0.002-0.002-35.115-35.115c-8.992-8.937-14.557-21.312-14.557-34.987s5.565-26.050 14.554-34.984l0.002-0.002 209.749-209.749-209.792-209.792c-8.992-8.937-14.557-21.312-14.557-34.987s5.565-26.050 14.554-34.984l0.002-0.002 35.115-35.115c8.937-8.992 21.312-14.557 34.987-14.557s26.050 5.565 34.984 14.554l0.002 0.002 279.723 279.723c9.019 8.98 14.6 21.406 14.6 35.136 0 13.709-5.564 26.118-14.556 35.093l-0.001 0.001z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["circle-right"],"grid":0},"attrs":[{}],"properties":{"order":21,"id":11,"name":"circle-right","prevSize":32,"code":59659},"setIdx":0,"setId":2,"iconIdx":16},{"icon":{"paths":["M843.123 30.996l-331.098 331.098-331.098-331.098c-19.185-19.146-45.668-30.985-74.916-30.985s-55.731 11.839-74.918 30.987l0.002-0.002c-19.117 19.18-30.937 45.643-30.937 74.868s11.82 55.687 30.94 74.87l-0.003-0.003 331.050 331.098-331.050 331.244c-19.191 19.173-31.063 45.67-31.063 74.94 0 58.504 47.427 105.93 105.93 105.93 29.234 0 55.702-11.842 74.868-30.991l-0.001 0.001 331.098-331.098 331.196 331.147c19.046 18.46 45.049 29.839 73.71 29.839 58.521 0 105.961-47.44 105.961-105.961 0-28.661-11.379-54.663-29.865-73.737l0.027 0.028-331.196-331.196 331.098-331.098c18.46-19.046 29.839-45.049 29.839-73.71 0-58.521-47.44-105.961-105.961-105.961-28.661 0-54.663 11.379-73.737 29.865l0.028-0.027z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["close"],"grid":0},"attrs":[{}],"properties":{"order":22,"id":12,"name":"close","prevSize":32,"code":59660},"setIdx":0,"setId":2,"iconIdx":17},{"icon":{"paths":["M895.851 0h-767.872c-70.681 0-127.979 57.298-127.979 127.979v0 575.904c0 70.681 57.298 127.979 127.979 127.979v0h191.968v167.993c-0 0.025-0 0.056-0 0.086 0 13.288 10.772 24.060 24.060 24.060 5.399 0 10.382-1.778 14.397-4.781l-0.063 0.045 249.558-187.403h287.952c70.681 0 127.979-57.298 127.979-127.979v0-575.904c0-70.681-57.298-127.979-127.979-127.979l0 0zM604.699 575.904h-371.821v-127.979h371.821zM791.42 296.697h-558.84v-127.979h558.84z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["comment"],"grid":0},"attrs":[{}],"properties":{"order":23,"id":13,"name":"comment","prevSize":32,"code":59661},"setIdx":0,"setId":2,"iconIdx":18},{"icon":{"paths":["M646.741 485.035v-323.371l-161.707-161.664-161.664 161.664v107.819h-323.371v754.517h970.112v-538.965zM215.595 916.224h-107.819v-107.819h107.819zM215.595 700.629h-107.819v-107.776h107.819zM215.595 485.035h-107.819v-107.776h107.819zM538.965 916.181h-107.819v-107.776h107.776zM538.965 700.587h-107.819v-107.733h107.776zM538.965 484.992h-107.819v-107.733h107.776zM538.965 269.397h-107.819v-107.733h107.776zM862.336 916.139h-107.819v-107.733h107.776zM862.336 700.544h-107.819v-107.691h107.776z"],"width":970,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["company"],"grid":0},"attrs":[{}],"properties":{"order":24,"id":14,"name":"company","prevSize":32,"code":59662},"setIdx":0,"setId":2,"iconIdx":19},{"icon":{"paths":["M587.199 55.924l-55.924-55.924h-279.618l-55.924 55.924h-195.733v111.847h782.932v-111.847z","M33.107 870.508c1.405 85.070 70.697 153.493 155.97 153.493 0.217 0 0.434-0 0.65-0.001l-0.034 0h403.396c0.182 0.001 0.398 0.001 0.614 0.001 85.338 0 154.671-68.528 155.971-153.557l0.001-0.122v-647.895h-716.569zM144.954 332.112h493.023v538.023c-0.295 24.483-20.211 44.217-44.736 44.217-0.001 0-0.002-0-0.003-0l-403.21 0c-0.101 0.001-0.22 0.001-0.34 0.001-24.499 0-44.399-19.692-44.735-44.112l-0-0.032z","M288.939 834.903c30.886 0 55.924-25.038 55.924-55.924v0-316.901c0-30.886-25.038-55.924-55.924-55.924s-55.924 25.038-55.924 55.924v0 316.901c0 30.886 25.038 55.924 55.924 55.924v0z","M493.993 834.903c30.886 0 55.924-25.038 55.924-55.924v0-316.901c0-30.886-25.038-55.924-55.924-55.924s-55.924 25.038-55.924 55.924v0 316.901c0 30.886 25.038 55.924 55.924 55.924v0z"],"width":783,"attrs":[{},{},{},{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{},{},{},{}]},"tags":["delet"],"grid":0},"attrs":[{},{},{},{}],"properties":{"order":6,"id":15,"name":"delet","prevSize":32,"code":59663},"setIdx":0,"setId":2,"iconIdx":20},{"icon":{"paths":["M563.115 854.229l407.851-408.107c13.086-12.984 21.186-30.975 21.186-50.858 0-19.714-7.963-37.568-20.848-50.521l0.003 0.003-68.139-68.139c-12.998-13.055-30.985-21.133-50.859-21.133-19.725 0-37.592 7.958-50.564 20.838l0.004-0.004-289.536 289.621-289.195-289.195c-13.008-13.118-31.038-21.24-50.965-21.24-19.778 0-37.688 8.001-50.669 20.943l0.002-0.002-0.299 0.299-67.968 67.499c-13.075 12.989-21.168 30.978-21.168 50.859 0 19.71 7.956 37.563 20.831 50.521l-0.004-0.004 408.235 408.32c13.040 13.276 31.18 21.503 51.243 21.503 19.596 0 37.358-7.849 50.315-20.575l-0.011 0.010z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["down"],"grid":0},"attrs":[{}],"properties":{"order":7,"id":16,"name":"down","prevSize":32,"code":59664},"setIdx":0,"setId":2,"iconIdx":21},{"icon":{"paths":["M864 0c88.366 0 160 71.634 160 160 0 36.2-12.022 69.593-32.292 96.402l0.292-0.402-64 64-224-224 64-64c26.324-19.976 59.637-32 95.758-32 0.085 0 0.17 0 0.255 0l-0.013-0zM64 736l-64 288 288-64 592.171-592-224.171-224.128zM715.563 363.563l-448 448-55.168-55.125 448-448z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["edit"],"grid":0},"attrs":[{}],"properties":{"order":8,"id":17,"name":"edit","prevSize":32,"code":59665},"setIdx":0,"setId":2,"iconIdx":22},{"icon":{"paths":["M1123.235 921.346l-217.634-208.847c5.132 1.805 11.050 2.848 17.211 2.848 15.851 0 30.089-6.902 39.875-17.864l0.046-0.052c37.481-38.161 71.036-80.412 99.866-125.951l1.835-3.103c5.666-8.976 9.029-19.899 9.029-31.606 0-11.056-2.999-21.412-8.227-30.299l0.153 0.28c-7.952-13.359-194.813-327.802-528.499-327.802h-1.312c-35.996 0.005-71.127 3.696-105.040 10.716l3.34-0.578c-18.092 3.787-32.895 15.391-40.954 31.036l-0.156 0.333-212.306-203.798c-10.7-10.287-25.264-16.622-41.308-16.622-32.938 0-59.64 26.702-59.64 59.64 0 16.894 7.024 32.148 18.312 42.999l0.020 0.019 162.768 156.208c-5.287 1.648-9.884 3.809-14.111 6.501l0.235-0.14c-95.97 62.868-175.328 142.911-235.543 236.445l-1.89 3.135c-5.835 9.056-9.303 20.115-9.303 31.985 0 11.035 2.997 21.371 8.222 30.236l-0.152-0.279c7.952 13.359 194.813 327.802 529.175 327.802 107.604-0.9 207.542-32.95 291.456-87.555l-2.1 1.281c1.312-0.835 2.385-1.908 3.618-2.863l210.398 201.929c10.7 10.287 25.264 16.622 41.308 16.622 32.938 0 59.64-26.702 59.64-59.64 0-16.894-7.024-32.148-18.312-42.999l-0.020-0.019zM536.729 298.223c210.716 0 354.679 168.573 405.529 238.546-20.464 28.603-41.631 53.756-64.584 77.15l0.097-0.1c-10.193 10.583-16.471 24.997-16.471 40.878 0 6.315 0.993 12.399 2.831 18.103l-0.116-0.417-385.213-369.747c17.213-2.809 37.053-4.414 57.267-4.414 0.232 0 0.464 0 0.696 0.001l-0.036-0zM480.591 505.758c6.937-5.309 12.58-11.943 16.621-19.556l0.156-0.323 89.972 86.354c-14.804 12.070-33.904 19.382-54.711 19.382-7.266 0-14.323-0.892-21.069-2.571l0.598 0.126c-21.583-3.776-39.081-18.284-47.003-37.713l-0.149-0.414c-0.942-3.344-1.484-7.185-1.484-11.153 0-13.911 6.658-26.266 16.961-34.054l0.107-0.078zM536.729 775.315c-210.716 0-354.4-168.215-405.211-238.546 49.32-68.027 109.037-125.034 177.432-169.904l2.551-1.571c11.947-7.883 20.761-19.68 24.668-33.535l0.101-0.418 78.482 75.301c-2.135 1.142-3.89 2.209-5.584 3.359l0.217-0.139c-39.593 30.204-64.883 77.409-64.883 130.521 0 16.497 2.44 32.424 6.979 47.441l-0.301-1.159c21.213 61.143 72.589 106.713 135.851 119.32l1.154 0.192c13.578 3.209 29.168 5.048 45.187 5.048 51.658 0 98.848-19.131 134.872-50.694l-0.235 0.202c1.923-1.788 3.692-3.67 5.327-5.665l0.080-0.1 67.389 64.686c-58.645 34.055-128.851 54.577-203.762 55.657l-0.314 0.004z"],"width":1142,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["eye-1"],"grid":0},"attrs":[{}],"properties":{"order":9,"id":18,"name":"eye-1","prevSize":32,"code":59666},"setIdx":0,"setId":2,"iconIdx":23},{"icon":{"paths":["M1343.806 478.457c-9.762-19.575-244.11-478.457-667.948-478.457s-658.136 458.883-667.948 478.457c-4.994 9.78-7.92 21.332-7.92 33.568s2.926 23.787 8.117 33.994l-0.197-0.427c10.013 19.575 244.11 478.407 667.948 478.407s658.186-458.832 667.948-478.407c4.994-9.78 7.92-21.332 7.92-33.568s-2.926-23.787-8.117-33.994l0.197 0.427zM675.858 873.96c-272.446 0-457.030-266.138-514.553-361.809 57.523-95.671 242.107-361.809 514.553-361.809s457.13 266.288 514.553 361.809c-57.523 95.521-242.157 361.659-514.553 361.659z","M675.858 211.619c-165.896 0-300.381 134.485-300.381 300.381s134.485 300.381 300.381 300.381c165.896 0 300.381-134.485 300.381-300.381v0c-0.199-165.816-134.565-300.182-300.362-300.381l-0.020-0zM675.858 662.191c-82.948 0-150.191-67.243-150.191-150.191s67.243-150.191 150.191-150.191c82.948 0 150.191 67.243 150.191 150.191v0c0 82.948-67.243 150.191-150.191 150.191v0z"],"width":1352,"attrs":[{},{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{},{}]},"tags":["eye"],"grid":0},"attrs":[{},{}],"properties":{"order":25,"id":19,"name":"eye","prevSize":32,"code":59667},"setIdx":0,"setId":2,"iconIdx":24},{"icon":{"paths":["M470.315 379.861v136.192c52.181 11.819 98.603 31.189 147.2 44.587v-136.405c-52.011-11.605-98.773-30.976-147.2-44.416zM916.907 133.888c-67.939 35.792-147.729 58.874-232.376 63.923l-1.608 0.077c-107.008-0.213-195.584-69.76-330.368-69.76-0.457-0.002-0.998-0.003-1.539-0.003-48.265 0-94.476 8.794-137.118 24.867l2.678-0.885c4.705-11.905 7.433-25.695 7.433-40.122 0-61.856-50.144-112-112-112s-112 50.144-112 112c0 37.882 18.807 71.371 47.593 91.64l0.355 0.237v772.267c-0 0.038-0 0.083-0 0.128 0 26.439 21.433 47.872 47.872 47.872 0.045 0 0.090-0 0.135-0l-0.007 0h32c0.038 0 0.083 0 0.128 0 26.439 0 47.872-21.433 47.872-47.872 0-0.045-0-0.090-0-0.135l0 0.007v-188.928c64.268-28.012 139.144-44.31 217.827-44.31 3.851 0 7.693 0.039 11.525 0.117l-0.573-0.009c107.179 0 195.584 69.589 330.368 69.589 92.324-0.765 177.356-31.352 246.099-82.581l-1.107 0.789c16.771-11.705 27.605-30.909 27.605-52.644 0-0.047-0-0.094-0-0.141l0 0.007v-486.144c-0.078-35.287-28.702-63.863-64-63.863-9.72 0-18.935 2.167-27.186 6.044l0.391-0.165zM323.157 651.008c-54.473 5.68-104.346 17.389-151.58 34.589l4.38-1.394v-140.8c42.966-16.959 92.83-29.258 144.754-34.612l2.446-0.204zM911.957 382.208c-42.344 20.263-91.614 36.787-143.125 47.106l-4.075 0.681v141.867c55.26-7.286 105.051-25.69 148.773-52.924l-1.573 0.913v140.8c-41.573 28.653-91.64 48.031-145.739 54.055l-1.461 0.132v-142.976c-14.605 2.234-31.455 3.51-48.603 3.51-35.177 0-69.1-5.37-100.994-15.334l2.396 0.645v134.912c-39.685-15.094-88.92-29.775-139.395-41.183l-7.805-1.483v-136.875c-28.906-6.694-62.098-10.532-96.186-10.532-17.976 0-35.702 1.067-53.12 3.141l2.106-0.204v-140.032c-55.565 9.583-104.997 24.104-151.88 43.532l4.68-1.718v-140.8c42.614-19.83 92.097-35.166 143.91-43.55l3.29-0.439v142.976c14.417-2.165 31.054-3.401 47.98-3.401 35.4 0 69.537 5.408 101.63 15.442l-2.41-0.649v-134.741c39.858 15.224 89.098 29.908 139.605 41.235l7.595 1.431v136.704c26.239 6.45 56.363 10.149 87.35 10.149 21.13 0 41.859-1.72 62.054-5.028l-2.204 0.298v-143.872c55.763-10.757 105.071-26.328 151.746-46.79l-4.546 1.777z"],"width":1008,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["flag"],"grid":0},"attrs":[{}],"properties":{"order":26,"id":20,"name":"flag","prevSize":32,"code":59668},"setIdx":0,"setId":2,"iconIdx":25},{"icon":{"paths":["M460.771 358.396h102.293v-102.251h-102.293zM511.917 921.749c-0.012 0-0.027 0-0.041 0-226.23 0-409.625-183.395-409.625-409.625s183.395-409.625 409.625-409.625c226.215 0 409.601 183.372 409.625 409.581l0 0.002c0 226.23-183.395 409.625-409.625 409.625v0zM511.876-0.207c-282.717 0.071-511.876 229.274-511.876 512 0 282.77 229.23 512 512 512 282.697 0 511.882-229.113 512-511.782l0-0.011c0-282.77-229.23-512-512-512v0zM460.854 768.021h102.21v-307.25h-102.21z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["information"],"grid":0},"attrs":[{}],"properties":{"order":27,"id":21,"name":"information","prevSize":32,"code":59669},"setIdx":0,"setId":2,"iconIdx":26},{"icon":{"paths":["M22.52 566.594l435.159 434.886c13.85 13.942 33.032 22.572 54.23 22.572s40.38-8.63 54.226-22.567l0.004-0.004 72.291-72.291c13.931-13.855 22.553-33.035 22.553-54.23s-8.622-40.375-22.549-54.227l-0.003-0.003-308.501-308.41 308.365-308.365c13.931-13.855 22.553-33.035 22.553-54.23s-8.622-40.375-22.549-54.227l-0.003-0.003-71.973-72.792c-13.85-13.942-33.032-22.572-54.23-22.572s-40.38 8.63-54.226 22.567l-0.004 0.004-435.023 434.932c-14.154 13.904-22.927 33.246-22.927 54.637 0 21.234 8.643 40.449 22.604 54.319l0.004 0.004z"],"width":661,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["left"],"grid":0},"attrs":[{}],"properties":{"order":28,"id":22,"name":"left","prevSize":32,"code":59670},"setIdx":0,"setId":2,"iconIdx":27},{"icon":{"paths":["M558.549 0.469l-465.451-0.469c-51.358 0.145-92.954 41.74-93.099 93.085l-0 0.014v837.803c0.145 51.358 41.74 92.954 93.085 93.099l0.014 0h465.451c51.358-0.145 92.954-41.74 93.099-93.085l0-0.014v-837.803c-0.194-51.199-41.744-92.629-92.97-92.629-0.045 0-0.090 0-0.136 0l0.007-0zM558.549 837.803h-465.451v-651.605h465.451z"],"width":652,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["phone"],"grid":0},"attrs":[{}],"properties":{"order":29,"id":23,"name":"phone","prevSize":32,"code":59671},"setIdx":0,"setId":2,"iconIdx":28},{"icon":{"paths":["M384 0c-0.789-0.017-1.719-0.027-2.651-0.027-34.738 0-66.285 13.665-89.558 35.914l0.049-0.046c-22.202 23.224-35.867 54.77-35.867 89.509 0 0.932 0.010 1.862 0.029 2.789l-0.002-0.138c5.836 34.821 18.412 65.879 36.393 93.049l-0.553-0.889c2.672 8.237 4.213 17.715 4.213 27.553 0 2.918-0.136 5.803-0.401 8.652l0.027-0.365h-295.68v768h295.68c0.053-1.37 0.084-2.978 0.084-4.594 0-11.101-1.431-21.868-4.119-32.126l0.196 0.88c-17.428-26.281-30.005-57.34-35.644-90.756l-0.196-1.404c-0.017-0.789-0.027-1.719-0.027-2.651 0-34.738 13.665-66.285 35.914-89.558l-0.046 0.049c23.224-22.202 54.77-35.867 89.509-35.867 0.932 0 1.862 0.010 2.789 0.029l-0.138-0.002c0.789-0.017 1.719-0.027 2.651-0.027 34.738 0 66.285 13.665 89.558 35.914l-0.049-0.046c22.202 23.224 35.867 54.77 35.867 89.509 0 0.932-0.010 1.862-0.029 2.789l0.002-0.138c-5.836 34.821-18.412 65.879-36.393 93.049l0.553-0.889c-2.672 8.237-4.213 17.715-4.213 27.553 0 2.918 0.136 5.803 0.401 8.652l-0.027-0.365h295.68v-295.68c1.37-0.053 2.978-0.084 4.594-0.084 11.101 0 21.868 1.431 32.126 4.119l-0.88-0.196c26.281 17.428 57.34 30.005 90.756 35.644l1.404 0.196c70.692 0 128-57.308 128-128s-57.308-128-128-128v0c-34.821 5.836-65.879 18.412-93.049 36.393l0.889-0.553c-8.237 2.672-17.715 4.213-27.553 4.213-2.918 0-5.803-0.136-8.652-0.401l0.365 0.027v-295.68h-295.68c-0.053-1.37-0.084-2.978-0.084-4.594 0-11.101 1.431-21.868 4.119-32.126l-0.196 0.88c17.428-26.281 30.005-57.34 35.644-90.756l0.196-1.404c0.017-0.789 0.027-1.719 0.027-2.651 0-34.738-13.665-66.285-35.914-89.558l0.046 0.049c-23.224-22.202-54.77-35.867-89.509-35.867-0.932 0-1.862 0.010-2.789 0.029l0.138-0.002z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["puzzle"],"grid":0},"attrs":[{}],"properties":{"order":30,"id":24,"name":"puzzle","prevSize":32,"code":59672},"setIdx":0,"setId":2,"iconIdx":29},{"icon":{"paths":["M638.368 566.619l-435.042 434.86c-13.855 13.932-33.037 22.554-54.232 22.554s-40.377-8.622-54.229-22.55l-0.003-0.003-72.295-72.295c-13.932-13.855-22.554-33.037-22.554-54.232s8.622-40.377 22.55-54.229l0.003-0.003 308.378-308.378-308.378-308.378c-13.932-13.855-22.554-33.037-22.554-54.232s8.622-40.377 22.55-54.229l0.003-0.003 71.976-72.795c13.855-13.932 33.037-22.554 54.232-22.554s40.377 8.622 54.229 22.55l0.003 0.003 435.042 434.723c14.1 13.896 22.836 33.203 22.836 54.549 0 21.189-8.607 40.368-22.515 54.232l-0.002 0.002z"],"width":661,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["right"],"grid":0},"attrs":[{}],"properties":{"order":31,"id":25,"name":"right","prevSize":32,"code":59673},"setIdx":0,"setId":2,"iconIdx":30},{"icon":{"paths":["M227.579 579.116v227.579l398.211 217.305 398.211-217.305v-227.579l-398.211 217.358zM625.789-0l-625.789 341.316 625.789 341.316 512-279.311v393.1h113.789v-455.105z"],"width":1252,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["school"],"grid":0},"attrs":[{}],"properties":{"order":32,"id":26,"name":"school","prevSize":32,"code":59674},"setIdx":0,"setId":2,"iconIdx":31},{"icon":{"paths":["M1009.922 885.314l-199.391-199.391c-8.672-8.647-20.639-13.993-33.854-13.993-0.096 0-0.192 0-0.288 0.001l0.015-0h-32.592c54.858-69.783 87.982-158.908 87.982-255.769 0-229.618-186.142-415.76-415.76-415.76s-415.76 186.142-415.76 415.76c0 229.618 186.142 415.76 415.76 415.76 96.861 0 185.986-33.123 256.666-88.661l-0.897 0.679v32.464c-0 0.081-0.001 0.177-0.001 0.273 0 13.216 5.346 25.182 13.994 33.855l-0.001-0.001 199.391 199.391c8.657 8.711 20.645 14.103 33.893 14.103s25.236-5.392 33.891-14.1l0.002-0.002 56.609-56.609c8.774-8.732 14.204-20.817 14.204-34.171 0-13.184-5.292-25.131-13.869-33.834l0.006 0.006zM415.973 672.016c-141.361 0-255.957-114.596-255.957-255.957s114.596-255.957 255.957-255.957c141.361 0 255.957 114.596 255.957 255.957v0c-0.049 141.325-114.626 255.872-255.957 255.872l-0-0z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["search"],"grid":0},"attrs":[{}],"properties":{"order":33,"id":27,"name":"search","prevSize":32,"code":59675},"setIdx":0,"setId":2,"iconIdx":32},{"icon":{"paths":["M264.124 1024l-3.988-0.315c-45.874-4.728-81.349-43.172-81.349-89.9 0-9.949 1.608-19.523 4.579-28.476l-0.183 0.638 89.58-260.661-235.092-165.554c-19.618-13.599-33.145-34.797-36.332-59.244l-0.046-0.43c-0.823-4.244-1.294-9.125-1.294-14.115 0-0.018 0-0.036 0-0.054l-0 0.003c0.197-48.36 39.308-87.52 87.63-87.796l0.026-0h289.553l86.747-258.842c12.356-34.779 44.982-59.235 83.32-59.235 37.789 0 70.029 23.761 82.593 57.157l0.201 0.608 87.272 260.311h287.525c0.011-0 0.025-0 0.038-0 47.863 0 86.99 37.407 89.741 84.58l0.011 0.243v3.848l-0.315 3.883c-2.124 22.565-12.304 42.418-27.594 56.944l-0.039 0.036-11.963 10.074-233.098 167.233 89.755 261.011c3.161 8.633 4.99 18.602 4.99 28.997 0 27.516-12.812 52.041-32.795 67.933l-0.177 0.136c-13.364 11.803-30.658 19.438-49.692 20.728l-0.258 0.014-3.183 0.21-3.148-0.175c-18.973-1.079-36.226-7.647-50.416-18.125l0.256 0.181-229.565-163.77-230.369 164.12c-13.682 9.851-30.475 16.214-48.672 17.473l-0.298 0.017zM812.414 940.051v0.28zM282.243 939.876v0zM838.403 920.498v0zM139.39 423.032l258.003 181.889-97.94 285.006 247.929-176.642 247.649 176.502-97.94-284.586 254.015-182.169h-309.246l-94.442-282.243-94.722 282.243zM1004.972 423.032v0zM97.346 393.405l0.56 0.42z"],"width":1095,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["star-1"],"grid":0},"attrs":[{}],"properties":{"order":34,"id":28,"name":"star-1","prevSize":32,"code":59676},"setIdx":0,"setId":2,"iconIdx":33},{"icon":{"paths":["M1060.923 354.485h-362.082l-110.034-328.386c-5.769-15.366-20.337-26.102-37.413-26.102s-31.644 10.737-37.322 25.828l-0.091 0.275-110.034 328.386h-364.574c-21.717 0.069-39.304 17.656-39.374 39.367l-0 0.007c-0 0.051-0.001 0.111-0.001 0.172 0 2.3 0.268 4.537 0.775 6.682l-0.039-0.196c1.082 11.588 7.245 21.545 16.211 27.732l0.127 0.083 297.753 209.856-114.363 332.062c-1.356 3.823-2.14 8.232-2.14 12.824 0 12.816 6.104 24.206 15.563 31.422l0.096 0.070c5.913 5.322 13.556 8.829 21.991 9.588l0.147 0.011c9.263-0.701 17.658-3.918 24.649-8.961l-0.142 0.098 290.483-207.038 290.483 207.038c6.807 5.047 15.229 8.284 24.374 8.857l0.132 0.007c8.688-0.56 16.438-4.176 22.272-9.773l-0.012 0.012c9.602-7.187 15.75-18.533 15.75-31.315 0-4.66-0.817-9.13-2.316-13.273l0.086 0.272-114.159-331.898 295.139-211.694 7.148-6.167c7.108-6.72 11.823-15.905 12.772-26.182l0.012-0.163c-1.204-22.063-19.388-39.497-41.643-39.497-0.078 0-0.156 0-0.234 0.001l0.012-0z"],"width":1103,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["star"],"grid":0},"attrs":[{}],"properties":{"order":35,"id":29,"name":"star","prevSize":32,"code":59677},"setIdx":0,"setId":2,"iconIdx":34},{"icon":{"paths":["M662.613 361.941c-7.552-20.181 201.344-206.72 79.531-357.888-28.501-35.328-125.227 169.259-262.528 261.845-75.776 51.2-252.075 159.872-252.075 219.989v389.461c0 72.32 279.552 148.864 491.989 148.864 77.867 0 190.677-487.851 190.677-565.333s-240.128-76.459-247.467-96.725zM170.624 367.36c-0.681-0.010-1.486-0.016-2.292-0.016-93.126 0-168.619 75.493-168.619 168.619 0 3.187 0.088 6.354 0.263 9.498l-0.019-0.437v275.797c-0.161 2.71-0.252 5.878-0.252 9.068 0 90.015 72.972 162.987 162.987 162.987 2.79 0 5.564-0.070 8.32-0.209l-0.387 0.016c37.376 0-56.875-32.555-56.875-128.555v-362.667c0-100.565 94.251-134.187 56.875-134.187z"],"width":910,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["thumbs-up"],"grid":0},"attrs":[{}],"properties":{"order":10,"id":30,"name":"thumbs-up","prevSize":32,"code":59678},"setIdx":0,"setId":2,"iconIdx":35},{"icon":{"paths":["M511.488 0c-282.55 0.292-511.488 229.41-511.488 512 0 282.77 229.23 512 512 512s512-229.23 512-512l-0 0c0-0.076 0-0.166 0-0.257 0-282.628-229.116-511.744-511.744-511.744-0.27 0-0.54 0-0.81 0.001l0.042-0zM512 921.6c-226.216 0-409.6-183.384-409.6-409.6s183.384-409.6 409.6-409.6c226.216 0 409.6 183.384 409.6 409.6v0c0 226.216-183.384 409.6-409.6 409.6v0zM537.6 256h-76.8v307.2l268.8 161.28 38.4-63.147-230.4-136.533z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["time"],"grid":0},"attrs":[{}],"properties":{"order":11,"id":31,"name":"time","prevSize":32,"code":59679},"setIdx":0,"setId":2,"iconIdx":36},{"icon":{"paths":["M563.115 223.232l407.851 408.107c13.137 12.992 21.272 31.018 21.272 50.943 0 19.756-7.997 37.645-20.932 50.605l0.001-0.001-68.139 68.139c-12.998 13.055-30.985 21.133-50.859 21.133-19.725 0-37.592-7.958-50.564-20.838l0.004 0.004-289.536-289.792-289.195 289.195c-12.998 13.055-30.985 21.133-50.859 21.133-19.725 0-37.592-7.958-50.564-20.838l0.004 0.004-0.213-0.299-68.267-67.499c-13.075-12.989-21.168-30.978-21.168-50.859 0-19.71 7.956-37.563 20.831-50.521l-0.004 0.004 408.235-408.32c13.040-13.276 31.18-21.503 51.243-21.503 19.596 0 37.358 7.849 50.315 20.575l-0.011-0.010z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["top"],"grid":0},"attrs":[{}],"properties":{"order":12,"id":32,"name":"top","prevSize":32,"code":59680},"setIdx":0,"setId":2,"iconIdx":37},{"icon":{"paths":["M1104.017 128.018h-207.987v-80.017c0-26.51-21.491-48.001-48.001-48.001v0h-544.040c-26.51 0-48.001 21.491-48.001 48.001v0 80.017h-207.987c-26.51 0-48.001 21.491-48.001 48.001v0 111.986c7.076 85.462 54.363 158.548 122.682 200.96l1.112 0.643c61.778 44.226 136.678 74.099 217.854 83.23l2.133 0.195c34.389 59.486 80.159 108.69 134.709 145.998l1.518 0.98v144.002h-96.001c-3.854-0.457-8.318-0.718-12.843-0.718-62.746 0-113.776 50.163-115.172 112.574l-0.002 0.13v24c0 13.255 10.745 24 24 24v0h592.041c13.255 0 24-10.745 24-24v0-24c-1.399-62.54-52.429-112.704-115.175-112.704-4.525 0-8.989 0.261-13.378 0.768l0.535-0.050h-96.001v-144.002c56.068-38.356 101.826-87.64 135.041-145.086l1.137-2.132c83.314-9.166 158.252-38.973 221.451-84.147l-1.416 0.962c69.366-43.121 116.621-116.171 123.729-200.646l0.065-0.957v-111.986c0-26.51-21.491-48.001-48.001-48.001v0zM198.627 385.59c-35.829-20.914-61.682-55.601-70.437-96.621l-0.172-0.964v-32.016h128.402c1.638 61.997 10.873 121.145 26.808 177.479l-1.223-5.061c-31.815-11.002-59.407-25.499-84.329-43.47l0.951 0.653zM1024 288.005c-9.695 41.665-35.309 76.091-69.932 97.202l-0.677 0.383c-24.040 17.319-51.713 31.817-81.346 42.129l-2.271 0.688c14.711-51.273 23.946-110.421 25.564-171.467l0.020-0.952h128.594z"],"width":1152,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"colorPermutations":{"12552552551":[{}]},"tags":["trophy"],"grid":0},"attrs":[{}],"properties":{"order":13,"id":33,"name":"trophy","prevSize":32,"code":59681},"setIdx":0,"setId":2,"iconIdx":38}],"height":1024,"metadata":{"name":"icomoon"},"preferences":{"showGlyphs":true,"showQuickUse":true,"showQuickUse2":true,"showSVGs":true,"fontPref":{"prefix":"icon-","metadata":{"fontFamily":"icomoon"},"metrics":{"emSize":1024,"baseline":6.25,"whitespace":50},"embed":false},"imagePref":{"prefix":"icon-","png":true,"useClassSelector":true,"color":0,"bgColor":16777215,"classSelector":".icon","name":"icomoon"},"historySize":50,"showCodes":true,"gridSize":16}} |
| | |
| | | @font-face { |
| | | font-family: 'icomoon'; |
| | | src: url('fonts/icomoon.eot?vrc42b'); |
| | | src: url('fonts/icomoon.eot?vrc42b#iefix') format('embedded-opentype'), |
| | | url('fonts/icomoon.ttf?vrc42b') format('truetype'), |
| | | url('fonts/icomoon.woff?vrc42b') format('woff'), |
| | | url('fonts/icomoon.svg?vrc42b#icomoon') format('svg'); |
| | | src: url('fonts/icomoon.eot?xbg0m9'); |
| | | src: url('fonts/icomoon.eot?xbg0m9#iefix') format('embedded-opentype'), |
| | | url('fonts/icomoon.ttf?xbg0m9') format('truetype'), |
| | | url('fonts/icomoon.woff?xbg0m9') format('woff'), |
| | | url('fonts/icomoon.svg?xbg0m9#icomoon') format('svg'); |
| | | font-weight: normal; |
| | | font-style: normal; |
| | | font-display: block; |
| | |
| | | -moz-osx-font-smoothing: grayscale; |
| | | } |
| | | |
| | | .icon-sort-decrease:before { |
| | | content: "\e922"; |
| | | } |
| | | .icon-sort-add:before { |
| | | content: "\e923"; |
| | | } |
| | | .icon-expand:before { |
| | | content: "\e924"; |
| | | } |
| | | .icon-sex-female:before { |
| | | content: "\e925"; |
| | | } |
| | | .icon-sex-male:before { |
| | | content: "\e926"; |
| | | } |
| | | .icon-decrease:before { |
| | | content: "\e900"; |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <svg id="Component_29" data-name="Component 29" xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"> |
| | | <circle id="Ellipse_726" data-name="Ellipse 726" cx="50" cy="50" r="50" fill="#feecdc"/> |
| | | <g id="Group_3685" data-name="Group 3685" transform="translate(-4269 -1812)"> |
| | | <circle id="Ellipse_723" data-name="Ellipse 723" cx="50" cy="50" r="50" transform="translate(4269 1812)" fill="#feecdc"/> |
| | | <path id="Intersection_4" data-name="Intersection 4" d="M8376.456,8861.851v-2.594c0-14.428,24.235-18.738,38.724-18.738s38.367,4.313,38.367,18.738v2.59A49.9,49.9,0,0,1,8415.013,8880H8415A49.892,49.892,0,0,1,8376.456,8861.851Zm17.021-48.454a21.7,21.7,0,0,1,21.7-21.686h.014a21.693,21.693,0,1,1-21.716,21.686Z" transform="translate(-4095.456 -6968)" fill="#d0d0ce" opacity="0.789"/> |
| | | <circle id="Ellipse_724" data-name="Ellipse 724" cx="15" cy="15" r="15" transform="translate(4334 1882)" fill="none"/> |
| | | <path id="female" d="M14.817,13.279l1.116,1.11c.457.457.92.914,1.377,1.383a.856.856,0,0,1-1.235,1.187c-.665-.653-1.324-1.318-1.983-1.977l-.516-.522a.938.938,0,0,1-.107.16l-2.339,2.339a.837.837,0,0,1-.867.231.813.813,0,0,1-.594-.641.843.843,0,0,1,.261-.819l2.321-2.315a.771.771,0,0,1,.125-.095l-1.395-1.4a5.9,5.9,0,0,1-5.028.968,5.734,5.734,0,0,1-3.383-2.333,5.936,5.936,0,1,1,9.669.107l1.431,1.4c.071-.083.142-.184.231-.273.736-.736,1.466-1.472,2.208-2.2A.859.859,0,1,1,17.3,10.822l-1.6,1.6C15.4,12.7,15.114,12.965,14.817,13.279ZM11.683,7.124a4.244,4.244,0,1,0-4.256,4.232A4.244,4.244,0,0,0,11.683,7.124Z" transform="matrix(0.719, 0.695, -0.695, 0.719, 4348.543, 1882.728)" fill="none"/> |
| | | </g> |
| | | </svg> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <svg id="notification_mob" data-name="notification â mob" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="375" height="120" viewBox="0 0 375 120"> |
| | | <defs> |
| | | <clipPath id="clip-path"> |
| | | <rect id="Rectangle_4042" data-name="Rectangle 4042" width="375" height="120" fill="none"/> |
| | | </clipPath> |
| | | <linearGradient id="linear-gradient" x1="-1.128" y1="2.281" x2="-0.128" y2="2.281" gradientUnits="objectBoundingBox"> |
| | | <stop offset="0" stop-color="#edc9c7"/> |
| | | <stop offset="1" stop-color="#f3e2e1"/> |
| | | </linearGradient> |
| | | </defs> |
| | | <g id="Group_3388" data-name="Group 3388" transform="translate(1 -1)"> |
| | | <g id="Group_3387" data-name="Group 3387" transform="translate(-1 1)" clip-path="url(#clip-path)"> |
| | | <g id="Group_3386" data-name="Group 3386" transform="translate(-3.5 -50.5)"> |
| | | <g id="Group_3383" data-name="Group 3383" transform="translate(0 42.5)"> |
| | | <rect id="Rectangle_4025" data-name="Rectangle 4025" width="134.5" height="398" transform="translate(0 134.5) rotate(-90)" fill="url(#linear-gradient)"/> |
| | | </g> |
| | | <rect id="Rectangle_4026" data-name="Rectangle 4026" width="225" height="72.5" transform="translate(161 111.5)" fill="#f09491"/> |
| | | <rect id="Rectangle_4027" data-name="Rectangle 4027" width="375" height="120" transform="translate(4.5 50.5)" fill="none"/> |
| | | <path id="Path_8048" data-name="Path 8048" d="M505.6,13.343,480.284-95.912a5.277,5.277,0,0,0-6.332-3.95L183.977-32.675a5.278,5.278,0,0,0-3.951,6.332l8.8,38.431L173,24.641a2.912,2.912,0,0,0,1.188,5.127l19.738,4.31,11.418,48.835a5.278,5.278,0,0,0,6.333,3.95L501.649,19.675A5.277,5.277,0,0,0,505.6,13.343Z" transform="translate(-81.946 100)" fill="#f09491"/> |
| | | <path id="Path_8049" data-name="Path 8049" d="M472.4,135.243l-74.942,34.674a8.661,8.661,0,0,1-6.521-.636L359.2,143.793a2.154,2.154,0,0,1,.792-3.589l80.219-29.133a5.109,5.109,0,0,1,4.938.636l27.519,19.947C473.986,132.974,473.733,134.648,472.4,135.243Z" transform="translate(-175.259 -5.352)" fill="#1b365d" opacity="0.5"/> |
| | | <path id="Path_8050" data-name="Path 8050" d="M473.873,114.133l-74.941,34.674a8.667,8.667,0,0,1-6.522-.636l-31.741-25.488c-.168-.112-.838-.452-.792-1.056.264-3.43.007-6.92,1.055-7.388L441.68,89.962a5.106,5.106,0,0,1,4.938.636l28.574,15.725C475.72,108.433,475.2,113.538,473.873,114.133Z" transform="translate(-175.937 5.203)" fill="#a7a8aa"/> |
| | | <path id="Path_8051" data-name="Path 8051" d="M474.514,103.05l-74.942,34.674a8.663,8.663,0,0,1-6.521-.636L361.309,111.6a2.154,2.154,0,0,1,.792-3.589l80.22-29.132a5.108,5.108,0,0,1,4.938.636l27.519,19.947C476.1,100.781,475.844,102.455,474.514,103.05Z" transform="translate(-176.314 10.745)" fill="#feecdc"/> |
| | | <path id="Path_8052" data-name="Path 8052" d="M495.549,118.372l-14.578,6.789a5.2,5.2,0,0,1-4.223-.825l-21.506-16.67a1.6,1.6,0,0,1,.313-2.61l15.366-5.78a3.132,3.132,0,0,1,3.206.739l21.4,15.717C496.443,116.78,496.371,118,495.549,118.372Z" transform="translate(-223.36 0.453)" fill="#fff"/> |
| | | <path id="Path_8053" data-name="Path 8053" d="M437,142.121l-14.578,6.789a5.2,5.2,0,0,1-4.223-.824l-22.006-17.17a1.6,1.6,0,0,1,.314-2.61l15.866-5.28a3.133,3.133,0,0,1,3.206.739l21.4,15.717C437.889,140.529,437.817,141.753,437,142.121Z" transform="translate(-193.833 -11.422)" fill="#fff"/> |
| | | <path id="Path_8054" data-name="Path 8054" d="M540.751,104.906l-1.5,1.383a1.669,1.669,0,0,1-1.353-.264l-6.625-5.4a.512.512,0,0,1,.1-.836l1.492-1a1,1,0,0,1,1.027.237l6.863,5C541.045,104.365,541.015,104.558,540.751,104.906Z" transform="translate(-261.551 0.635)" fill="#a7a8aa"/> |
| | | <path id="Path_8055" data-name="Path 8055" d="M391.314,173.552l-.712.939a.715.715,0,0,1-.871-.148L383.4,169.33a.634.634,0,0,1-.146-.807l.674-.776c.263-.118.725-.3,1.055,0l6.069,4.75C391.344,172.832,391.578,173.2,391.314,173.552Z" transform="translate(-187.583 -33.791)" fill="#fff"/> |
| | | <rect id="Rectangle_4028" data-name="Rectangle 4028" width="24.277" height="24.541" rx="5.278" transform="translate(131.955 109.942)" fill="#5cb8b2"/> |
| | | <rect id="Rectangle_4029" data-name="Rectangle 4029" width="30.61" height="30.943" rx="5.278" transform="translate(178.662 59.277)" fill="#fff" opacity="0.7"/> |
| | | <rect id="Rectangle_4030" data-name="Rectangle 4030" width="30.61" height="30.943" rx="5.278" transform="translate(236.452 67.194)" fill="#f2c75c" opacity="0.7"/> |
| | | <path id="Path_8056" data-name="Path 8056" d="M149.422,69.681l-6.879,10.753,7.88,6.207-9.28,9.1,5.192-8.723-7.1-5.9Z" transform="translate(-65.616 15.16)" fill="#fff"/> |
| | | <path id="Path_8057" data-name="Path 8057" d="M187.495,180.835,175.68,176l-4.688,8.868-10.621-7.492,9.515,3.539,4.53-8.048Z" transform="translate(-76.186 -36.434)" fill="#fff"/> |
| | | <g id="Group_3384" data-name="Group 3384" transform="translate(21.25 101.584)"> |
| | | <ellipse id="Ellipse_684" data-name="Ellipse 684" cx="4.354" cy="4.882" rx="4.354" ry="4.882" transform="translate(18.449 43.12) rotate(-89.901)" fill="#d0d0ce"/> |
| | | <path id="Path_8058" data-name="Path 8058" d="M67.989,173.6s-1.589,3.163-.278,8.179,3.676,11.09,3.676,11.09,1.052,1.849-.268,2.374-7.129,2.627-7.129,2.627-2.641,1.051-3.43-.534-8.145-20.6-8.145-20.6Z" transform="translate(-43.457 -138.383)" fill="#a7a8aa"/> |
| | | <circle id="Ellipse_685" data-name="Ellipse 685" cx="6.333" cy="6.333" r="6.333" transform="translate(40.134 9.656)" fill="#f09491"/> |
| | | <circle id="Ellipse_686" data-name="Ellipse 686" cx="7.784" cy="7.784" r="7.784" transform="translate(0 22.255)" fill="#68737a"/> |
| | | <path id="Path_8059" data-name="Path 8059" d="M42.159,132.015s-4.736-7.66,9-13.442,16.637-7.1,21.922-11.837,6.34-4.211,7.656-1.57,8.929,25.084,9.451,28.251-1.328,5.011-6.869,4.738-19.267,1.814-25.6,3.65S44.771,147.06,42.159,132.015Z" transform="translate(-37.934 -103.169)" fill="#fff"/> |
| | | <path id="Path_8060" data-name="Path 8060" d="M52.376,131.871l7.244,23.674s-7.028,4.033-11.511,2.706-6.318-8.983-6.318-8.983-3.677-10.033,2.134-13.19A91.3,91.3,0,0,1,52.376,131.871Z" transform="translate(-37.571 -117.52)" fill="#f09491"/> |
| | | </g> |
| | | <rect id="Rectangle_4031" data-name="Rectangle 4031" width="21.374" height="3.43" transform="translate(183.28 71.811)" fill="#d0d0ce"/> |
| | | <rect id="Rectangle_4032" data-name="Rectangle 4032" width="21.374" height="3.43" transform="translate(183.28 65.214)" fill="#d0d0ce"/> |
| | | <rect id="Rectangle_4033" data-name="Rectangle 4033" width="13.986" height="3.43" transform="translate(183.28 78.409)" fill="#d0d0ce"/> |
| | | <rect id="Rectangle_4034" data-name="Rectangle 4034" width="21.374" height="3.43" transform="translate(240.806 73.659)" fill="#d0d0ce"/> |
| | | <rect id="Rectangle_4035" data-name="Rectangle 4035" width="13.986" height="3.43" transform="translate(240.806 80.256)" fill="#d0d0ce"/> |
| | | <rect id="Rectangle_4036" data-name="Rectangle 4036" width="17.416" height="3.43" transform="translate(135.254 114.824)" fill="#d0d0ce"/> |
| | | <rect id="Rectangle_4037" data-name="Rectangle 4037" width="11.396" height="3.43" transform="translate(135.254 121.421)" fill="#d0d0ce"/> |
| | | <g id="Group_3385" data-name="Group 3385" transform="translate(312.602 82.875)"> |
| | | <rect id="Rectangle_4038" data-name="Rectangle 4038" width="37.851" height="38.263" rx="5.278" fill="#fff"/> |
| | | <rect id="Rectangle_4039" data-name="Rectangle 4039" width="30.346" height="3.43" transform="translate(4.09 12.27)" fill="#d0d0ce"/> |
| | | <rect id="Rectangle_4040" data-name="Rectangle 4040" width="30.346" height="3.43" transform="translate(4.09 5.673)" fill="#d0d0ce"/> |
| | | <rect id="Rectangle_4041" data-name="Rectangle 4041" width="19.856" height="3.43" transform="translate(4.09 18.867)" fill="#d0d0ce"/> |
| | | </g> |
| | | </g> |
| | | </g> |
| | | </g> |
| | | </svg> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <svg id="notification_web" data-name="notification â web" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1280" height="150" viewBox="0 0 1280 150"> |
| | | <defs> |
| | | <clipPath id="clip-path"> |
| | | <rect id="Rectangle_3981" data-name="Rectangle 3981" width="1280" height="150" transform="translate(0 1)" fill="none"/> |
| | | </clipPath> |
| | | <linearGradient id="linear-gradient" x1="-9.872" y1="-2.825" x2="-8.872" y2="-2.825" gradientUnits="objectBoundingBox"> |
| | | <stop offset="0" stop-color="#edc9c7"/> |
| | | <stop offset="1" stop-color="#f3e2e1"/> |
| | | </linearGradient> |
| | | </defs> |
| | | <g id="Group_3422" data-name="Group 3422" transform="translate(0 -1)"> |
| | | <g id="Group_3421" data-name="Group 3421" clip-path="url(#clip-path)"> |
| | | <g id="Group_3420" data-name="Group 3420"> |
| | | <g id="Group_3415" data-name="Group 3415"> |
| | | <rect id="Rectangle_3964" data-name="Rectangle 3964" width="269" height="796" transform="translate(-6 247) rotate(-90)" fill="url(#linear-gradient)"/> |
| | | </g> |
| | | <rect id="Rectangle_3965" data-name="Rectangle 3965" width="751.302" height="155.546" transform="translate(541.045 18.832)" fill="#f09491"/> |
| | | <rect id="Rectangle_3966" data-name="Rectangle 3966" width="750" height="240" transform="translate(3 -6)" fill="none"/> |
| | | <path id="Path_8017" data-name="Path 8017" d="M1299.375,29.165l-38.229-164.992a7.971,7.971,0,0,0-9.564-5.966L451.993-1.82a7.971,7.971,0,0,0-5.966,9.563L459.318,65.78,435.41,84.735a4.4,4.4,0,0,0,1.794,7.743l29.808,6.509,17.244,73.748a7.971,7.971,0,0,0,9.564,5.966L1293.409,38.728A7.971,7.971,0,0,0,1299.375,29.165Z" fill="#f09491"/> |
| | | <path id="Path_8018" data-name="Path 8018" d="M746.643,92.665,633.469,145.029c-2.009.9-7.173.8-9.848-.961l-47.934-38.491c-1.993-1.993-.813-4.521,1.2-5.419L698.026,56.163a7.716,7.716,0,0,1,7.458.96l41.558,30.123C749.034,89.238,748.652,91.767,746.643,92.665Z" fill="#1b365d" opacity="0.5"/> |
| | | <path id="Path_8019" data-name="Path 8019" d="M747.838,76.725,634.665,129.088c-2.009.9-7.173.8-9.848-.96L576.882,89.637c-.253-.169-1.265-.684-1.2-1.594.4-5.181.011-10.45,1.594-11.158L699.222,40.223a7.712,7.712,0,0,1,7.457.96L749.831,64.93C750.628,68.118,749.848,75.827,747.838,76.725Z" fill="#a7a8aa"/> |
| | | <path id="Path_8020" data-name="Path 8020" d="M748.237,68.357,635.063,120.72c-2.009.9-7.173.8-9.848-.96L577.281,81.268c-1.993-1.992-.813-4.521,1.2-5.419L699.62,31.854a7.714,7.714,0,0,1,7.458.961l41.558,30.122C750.628,64.93,750.246,67.458,748.237,68.357Z" fill="#feecdc"/> |
| | | <path id="Path_8021" data-name="Path 8021" d="M708.958,75.952,686.943,86.205c-1.241.555-4.563.2-6.377-1.245L648.089,59.786c-1.388-1.582-.768-3.388.473-3.943l23.205-8.728a4.73,4.73,0,0,1,4.841,1.115L708.92,71.966C710.308,73.548,710.2,75.4,708.958,75.952Z" fill="#fff"/> |
| | | <path id="Path_8022" data-name="Path 8022" d="M665.123,93.885l-22.015,10.253c-1.241.555-4.563.2-6.377-1.246L603.5,76.963c-1.388-1.582-.768-3.387.473-3.942l23.96-7.974a4.73,4.73,0,0,1,4.841,1.116L665.085,89.9C666.473,91.481,666.364,93.329,665.123,93.885Z" fill="#fff"/> |
| | | <path id="Path_8023" data-name="Path 8023" d="M719.545,55.892l-2.271,2.089a2.518,2.518,0,0,1-2.042-.4l-10.006-8.15a.774.774,0,0,1,.152-1.263l2.253-1.515a1.515,1.515,0,0,1,1.551.357l10.363,7.558C719.99,55.076,719.944,55.366,719.545,55.892Z" fill="#a7a8aa"/> |
| | | <path id="Path_8024" data-name="Path 8024" d="M605.574,107.569l-1.075,1.42a1.082,1.082,0,0,1-1.316-.224l-9.564-7.572a.959.959,0,0,1-.22-1.219l1.017-1.172c.4-.177,1.095-.455,1.594,0l9.166,7.173C605.62,106.482,605.974,107.044,605.574,107.569Z" fill="#fff"/> |
| | | <rect id="Rectangle_3967" data-name="Rectangle 3967" width="36.662" height="37.061" rx="3.985" transform="translate(497.182 62.539)" fill="#5cb8b2"/> |
| | | <path id="Path_8025" data-name="Path 8025" d="M424.469,22.631,414.081,38.869l11.9,9.373L411.966,61.985l7.841-13.174L409.082,39.9Z" fill="#fff"/> |
| | | <path id="Path_8026" data-name="Path 8026" d="M466,112.577l-17.842-7.3-7.08,13.392-16.039-11.314,14.369,5.344,6.841-12.154Z" fill="#fff"/> |
| | | <g id="Group_3416" data-name="Group 3416"> |
| | | <ellipse id="Ellipse_678" data-name="Ellipse 678" cx="6.575" cy="7.372" rx="6.575" ry="7.372" transform="translate(357.861 113.036) rotate(-89.901)" fill="#d0d0ce"/> |
| | | <path id="Path_8027" data-name="Path 8027" d="M367.047,101.1s-2.4,4.778-.42,12.353,5.55,16.747,5.55,16.747,1.589,2.792-.4,3.586-10.767,3.966-10.767,3.966-3.988,1.587-5.179-.806-12.3-31.1-12.3-31.1Z" fill="#a7a8aa"/> |
| | | <circle id="Ellipse_679" data-name="Ellipse 679" cx="9.564" cy="9.564" r="9.564" transform="translate(390.609 62.499)" fill="#f09491"/> |
| | | <circle id="Ellipse_680" data-name="Ellipse 680" cx="11.756" cy="11.756" r="11.756" transform="translate(330 81.526)" fill="#68737a"/> |
| | | <path id="Path_8028" data-name="Path 8028" d="M336.379,91.479s-7.153-11.569,13.584-20.3S375.087,60.463,383.07,53.3s9.575-6.36,11.56-2.371S408.114,88.814,408.9,93.6s-2.006,7.568-10.374,7.155-29.1,2.739-38.664,5.512S340.325,114.2,336.379,91.479Z" fill="#fff"/> |
| | | <path id="Path_8029" data-name="Path 8029" d="M352.357,69.589,363.3,105.342s-10.613,6.091-17.384,4.086-9.54-13.565-9.54-13.565-5.553-15.153,3.222-19.92A137.735,137.735,0,0,1,352.357,69.589Z" fill="#f09491"/> |
| | | </g> |
| | | <g id="Group_3417" data-name="Group 3417"> |
| | | <rect id="Rectangle_3968" data-name="Rectangle 3968" width="33.608" height="33.973" rx="2.897" transform="translate(560.717 13.027)" fill="#fff" opacity="0.7"/> |
| | | <rect id="Rectangle_3969" data-name="Rectangle 3969" width="23.468" height="3.766" transform="translate(565.787 26.789)" fill="#d0d0ce"/> |
| | | <rect id="Rectangle_3970" data-name="Rectangle 3970" width="23.468" height="3.766" transform="translate(565.787 19.546)" fill="#d0d0ce"/> |
| | | <rect id="Rectangle_3971" data-name="Rectangle 3971" width="15.355" height="3.766" transform="translate(565.787 34.032)" fill="#d0d0ce"/> |
| | | </g> |
| | | <g id="Group_3418" data-name="Group 3418"> |
| | | <rect id="Rectangle_3972" data-name="Rectangle 3972" width="46.226" height="46.729" rx="3.985" transform="translate(649.988 8.982)" fill="#f2c75c" opacity="0.7"/> |
| | | <rect id="Rectangle_3973" data-name="Rectangle 3973" width="32.279" height="5.181" transform="translate(656.564 18.745)" fill="#d0d0ce"/> |
| | | <rect id="Rectangle_3974" data-name="Rectangle 3974" width="21.121" height="5.181" transform="translate(656.564 28.708)" fill="#d0d0ce"/> |
| | | </g> |
| | | <rect id="Rectangle_3975" data-name="Rectangle 3975" width="26.301" height="5.181" transform="translate(502.163 69.911)" fill="#d0d0ce"/> |
| | | <rect id="Rectangle_3976" data-name="Rectangle 3976" width="17.209" height="5.181" transform="translate(502.163 79.874)" fill="#d0d0ce"/> |
| | | <g id="Group_3419" data-name="Group 3419"> |
| | | <rect id="Rectangle_3977" data-name="Rectangle 3977" width="57.161" height="57.783" rx="3.985" transform="translate(769.988 32.663)" fill="#fff"/> |
| | | <rect id="Rectangle_3978" data-name="Rectangle 3978" width="45.828" height="5.181" transform="translate(776.164 51.193)" fill="#d0d0ce"/> |
| | | <rect id="Rectangle_3979" data-name="Rectangle 3979" width="45.828" height="5.181" transform="translate(776.164 41.231)" fill="#d0d0ce"/> |
| | | <rect id="Rectangle_3980" data-name="Rectangle 3980" width="29.986" height="5.181" transform="translate(776.164 61.156)" fill="#d0d0ce"/> |
| | | </g> |
| | | </g> |
| | | </g> |
| | | </g> |
| | | </svg> |
| | |
| | | line-height: 40px; |
| | | padding-right: 10px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .remind-container{ |
| | | margin-top: 13px; |
| | | margin-bottom: 20px; |
| | | display: flex; |
| | | .remind-date{ |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | font-weight: bold; |
| | | width: 70px; |
| | | border-radius: 6px; |
| | | border-bottom: 1px solid #CCCCCC; |
| | | border-right: 1px solid #CCCCCC; |
| | | border-left: 1px solid #CCCCCC; |
| | | } |
| | | .remind-content-txt{ |
| | | display: flex; |
| | | flex-direction: column; |
| | | border: 1px solid #CCCCCC; |
| | | flex:1; |
| | | border-radius: 5px; |
| | | padding: 10px; |
| | | } |
| | | .mb-3{ |
| | | margin-bottom: 3px; |
| | | } |
| | | .mt-2{ |
| | | margin-top:2px; |
| | | } |
| | | .date-year{ |
| | | color: #fff; |
| | | align-items: center; |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | .bgc-primary-red{ |
| | | background-color:$PRIMARY_RED; |
| | | width: 70.5px; |
| | | border-top-left-radius:6px; |
| | | border-top-right-radius:6px; |
| | | border: 1px solid #CCCCCC; |
| | | } |
| | | } |
| | |
| | | $SKY_BLUE: #009CBD; |
| | | $LIGHT_BLUE: #8DB9CA; |
| | | $DARK_BLUE: #1B365D; |
| | | $LIGHT_RED: #DA3738; |
| | | $BEIGE: #A89968; |
| | | $PRUDENTIAL_GREY: #68737A; |
| | | $LIGHT_GREY: #D0D0CE; |
| | |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .lighter { |
| | | font-weight: lighter; |
| | | } |
| | | |
| | | .smTxt_bold { |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .xxsTxt { |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .text--bold { |
| | | font-weight: bold !important; |
| | | } |
| | | |
| | | .text--regular { |
| | | font-weight: normal !important; |
| | | font-weight: lighter; |
| | | } |
| | | |
| | | .text--lighter { |
| | | font-weight: lighter; |
| | | } |
| | | |
| | | .text--center { |
| | |
| | | text-align: left; |
| | | } |
| | | |
| | | .text--right { |
| | | text-align: right; |
| | | } |
| | | |
| | | .text--primary { |
| | | color: $PRIMARY_RED; |
| | | } |
| | | |
| | | .text--black{ |
| | | color: $PRIMARY_BLACK; |
| | | } |
| | | |
| | | .text--orange { |
| | | color: $ORANGE; |
| | | } |
| | | |
| | | .text--black{ |
| | | color: $PRIMARY_BLACK; |
| | | } |
| | | |
| | | .text--dark-blue { |
| | |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .text--center { |
| | | text-align: center; |
| | | } |
| | | |
| | | .text--underline { |
| | | text-decoration: underline; |
| | | } |
| | | |
| | | .pam-link-button { |
| | | @extend .fix-chrome-click--issue; |
| | | @extend .smTxt; |
| | | @extend .text--bold; |
| | | @extend .text--primary; |
| | | @extend .cursor--pointer; |
| | | } |
| | | |
| | | .pam-link-button--lg { |
| | | @extend .fix-chrome-click--issue; |
| | | @extend .mdTxt; |
| | | @extend .text--bold; |
| | | @extend .text--primary; |
| | | @extend .cursor--pointer; |
| | | } |
| | |
| | | color: $YELLOW; |
| | | } |
| | | } |
| | | |
| | | .down-icon { |
| | | color: #ED1B2E; |
| | | font-size: 25px; |
| | | align-self: center; |
| | | } |
| | |
| | | margin-bottom: 50px; |
| | | } |
| | | |
| | | .mt-50 { |
| | | margin-top: 50px; |
| | | } |
| | | |
| | | .mt-30 { |
| | | margin-top: 30px; |
| | | } |
| | |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | |
| | | .mt-10 { |
| | | margin-top: 10px; |
| | | } |
| | |
| | | |
| | | .mb-10 { |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .ml-5 { |
| | | margin-left: 5px; |
| | | } |
| | | |
| | | .ml-10{ |
| | |
| | | |
| | | .mr-30{ |
| | | margin-right: 30px; |
| | | } |
| | | |
| | | .my-10 { |
| | | margin:10px 0; |
| | | } |
| | | |
| | | .pt-30 { |
| | |
| | | font-size: #{$fontSize} + 'px'; |
| | | } |
| | | } |
| | | |
| | | .text--break-all { |
| | | word-break: break-all |
| | | } |
| | |
| | | @import './elementUI/messageBox'; |
| | | @import './elementUI/input'; |
| | | @import './elementUI/tree'; |
| | | @import './elementUI/upload'; |
| | | @import './elementUI/textarea'; |
| | | @import './elementUI/dateTimePicker'; |
| | | @import './elementUI/select'; |
¤ñ¹ï·sÀÉ®× |
| | |
| | | .pam-date.el-input,.pam-time.el-input { |
| | | .el-input__inner { |
| | | padding-left: 10px; |
| | | height: 46px; |
| | | border: 1px solid #707070; |
| | | } |
| | | |
| | | .el-input__prefix { |
| | | left:auto; |
| | | right: 10px; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | color: $PRIMARY_BLACK; |
| | | } |
| | | .el-input__suffix { |
| | | right: 25px; |
| | | } |
| | | } |
| | | |
| | | .pam-date.el-input { |
| | | width: 58%; |
| | | } |
| | | .pam-time.el-input { |
| | | width: 38%; |
| | | } |
| | | |
| | | .pam-time-popper { |
| | | .time-select-item { |
| | | font-size: 18px; |
| | | |
| | | &.selected { |
| | | color: $CORAL; |
| | | } |
| | | } |
| | | } |
| | | .pam-date-popper { |
| | | .el-date-picker__header { |
| | | .el-picker-panel__icon-btn:hover, .el-date-picker__header-label:hover { |
| | | color: $CORAL; |
| | | } |
| | | } |
| | | .el-picker-panel__body { |
| | | .el-picker-panel__content { |
| | | .el-date-table { |
| | | .el-date-table__row { |
| | | td.today span { |
| | | color: $CORAL; |
| | | } |
| | | td.current span { |
| | | background-color: $CORAL; |
| | | } |
| | | td.available:hover { |
| | | color: $CORAL; |
| | | } |
| | | td.today.current span { |
| | | color: $PRIMARY_WHITE; |
| | | } |
| | | } |
| | | } |
| | | .el-year-table { |
| | | td.current .cell { |
| | | color: $CORAL; |
| | | } |
| | | td.today .cell { |
| | | color: $CORAL; |
| | | } |
| | | td.available .cell:hover { |
| | | color: $CORAL; |
| | | } |
| | | } |
| | | .el-month-table { |
| | | td.current .cell { |
| | | color: $CORAL; |
| | | } |
| | | td.today .cell { |
| | | color: $CORAL; |
| | | } |
| | | td .cell:hover { |
| | | color: $CORAL; |
| | | } |
| | | } |
| | | |
| | | } |
| | | } |
| | | } |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | .pam-dialog-review { |
| | | .review-content { |
| | | display: flex; |
| | | flex-direction: row; |
| | | justify-content: space-evenly; |
| | | } |
| | | .review-text { |
| | | width: 60%; |
| | | line-height: 28px; |
| | | @extend .p; |
| | | @extend .text--lighter; |
| | | } |
| | | .review-score { |
| | | display: flex; |
| | | justify-content: center; |
| | | margin-bottom: 30px; |
| | | } |
| | | .review-btn { |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | } |
| | | |
| | | .pam-dialog-reserved { |
| | | .reserved-info { |
| | | font-size: 20px; |
| | | overflow-y:scroll; |
| | | height: 400px; |
| | | } |
| | | .reserved-btn { |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | } |
| | |
| | | |
| | | .input-radius > .el-input__inner { |
| | | border-radius: 10px; |
| | | } |
| | | |
| | | .pam-appointment-textarea { |
| | | &.is-disabled { |
| | | .el-textarea__inner { |
| | | color: #222222; |
| | | border: 1px solid #707070; |
| | | |
| | | &::placeholder { |
| | | color: #A7A8AA; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .el-textarea__inner { |
| | | border: 1px solid #707070; |
| | | padding: 10px 20px; |
| | | box-sizing: border-box; |
| | | border-radius: 5px; |
| | | font-size: 18px; |
| | | |
| | | &:focus { |
| | | outline: none; |
| | | border: solid 1px $SKY_BLUE; |
| | | } |
| | | } |
| | | } |
| | |
| | | .pam-quickFilter-rate { |
| | | .pam-rate { |
| | | height: auto; |
| | | margin-top: 30px; |
| | | display: flex; |
| | | justify-content: center; |
| | | @extend .fix-chrome-click--issue; |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | .pam-satisfaction-rate { |
| | | height: auto; |
| | | display: flex; |
| | | justify-content: center; |
| | | margin-top: 10px; |
| | | @extend .fix-chrome-click--issue; |
| | | .el-rate__item { |
| | | .el-rate__icon { |
| | | font-size: 30px; |
| | | } |
| | | |
| | | .el-icon-star-off { |
| | | color: $PRIMARY_BLACK !important; |
| | | } |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | .pam-select.el-select { |
| | | width: 100%; |
| | | |
| | | .el-input { |
| | | |
| | | &__inner { |
| | | padding-left: 20px; |
| | | height: 46px; |
| | | border: 1px solid #707070; |
| | | border-radius: 5px; |
| | | font-size: 20px; |
| | | } |
| | | &__icon { |
| | | color: #ED1B2E; |
| | | font-size: 25px; |
| | | transform: none; |
| | | } |
| | | } |
| | | .is-focus { |
| | | .el-input__inner { |
| | | border-color: #707070; |
| | | } |
| | | } |
| | | .el-icon-arrow-up { |
| | | font-family: 'icomoon' !important; |
| | | speak: never; |
| | | font-style: normal; |
| | | font-weight: normal; |
| | | font-variant: normal; |
| | | text-transform: none; |
| | | line-height: 1; |
| | | -webkit-font-smoothing: antialiased; |
| | | -moz-osx-font-smoothing: grayscale; |
| | | &:before { |
| | | content: "\e910"; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .el-select-dropdown__item { |
| | | padding: 0 10px; |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | .el-textarea__inner { |
| | | border: 1px solid #707070; |
| | | padding: 10px 20px; |
| | | font-size: 20px; |
| | | &::placeholder { |
| | | font-size: 20px; |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | .pam-avatar-uploader{ |
| | | display: flex; |
| | | justify-content: center; |
| | | margin-top: 10px; |
| | | .el-upload{ |
| | | @extend .fix-chrome-click--issue; |
| | | .pam-avatar-uploader--title{ |
| | | font-size: 20px; |
| | | letter-spacing: 2px; |
| | | } |
| | | &:focus{ |
| | | border-color: $PRIMARY_BLACK; |
| | | color: $PRIMARY_BLACK; |
| | | } |
| | | } |
| | | } |
| | |
| | | class="p mt-10 cursor--pointer input-radius" |
| | | tabindex="-1" |
| | | v-model="keyWord" |
| | | @change="searchDistrict" |
| | | @input="searchDistrict" |
| | | placeholder="è«è¼¸å
¥ééµå" |
| | | ></el-input> |
| | | <Ui-ScrollPicker |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <section class="close-appointment-detail"> |
| | | |
| | | <div class="close-appointment-detail-nav"> |
| | | <div class="mdTxt">çµæ¡æ¹å¼</div> |
| | | <div class="mdTxt text--primary text--underline cursor--pointer" @click="editAppointmentHasClosed">編輯</div> |
| | | </div> |
| | | <span class="mt-10 mb-30">{{ displayClosedType }}</span> |
| | | |
| | | <template v-if="appointmentDetail.appointmentClosedInfo.policyholderIdentityId"> |
| | | <div class="mdTxt mb-10">ä¿æ¶èº«ä»½èåè</div> |
| | | <div class="mb-30">{{ appointmentDetail.appointmentClosedInfo.policyholderIdentityId }}</div> |
| | | </template> |
| | | |
| | | <template v-if="appointmentDetail.appointmentClosedInfo.planCode"> |
| | | <div class="mdTxt mb-10">åå代碼Plan Code</div> |
| | | <div class="mb-30">{{ appointmentDetail.appointmentClosedInfo.planCode }}</div> |
| | | </template> |
| | | |
| | | <template v-if="appointmentDetail.appointmentClosedInfo.closedReason"> |
| | | <div class="mdTxt mb-10">æªæäº¤åå </div> |
| | | <div >{{ appointmentDetail.appointmentClosedInfo.closedReason | toFailReasonLabel }}</div> |
| | | <div v-if="appointmentDetail.appointmentClosedInfo.closedOtherReason" class="mt-10">{{ appointmentDetail.appointmentClosedInfo.closedOtherReason }}</div> |
| | | <div class="mb-30"></div> |
| | | </template> |
| | | |
| | | <template v-if="appointmentDetail.appointmentClosedInfo.policyEntryDate"> |
| | | <div class="mdTxt mb-10">é²ä»¶æé</div> |
| | | <div class="mb-30">{{ appointmentDetail.appointmentClosedInfo.policyEntryDate | formatDate }}</div> |
| | | </template> |
| | | |
| | | <div class="mdTxt mb-10">å註</div> |
| | | <div>{{ appointmentDetail.appointmentClosedInfo.remark || 'ç¡' }}</div> |
| | | |
| | | </section> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop } from 'nuxt-property-decorator'; |
| | | |
| | | import { Appointment } from '~/shared/models/appointment.model'; |
| | | import { ContactStatus } from '~/shared/models/enum/contact-status'; |
| | | |
| | | @Component |
| | | export default class AppointmentClosedInfo extends Vue { |
| | | |
| | | @Prop() |
| | | appointmentDetail!: Appointment; |
| | | |
| | | contactStatus = ContactStatus; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | editAppointmentHasClosed(): void{ |
| | | this.$router.push(`/appointment/${this.appointmentDetail.id}/close`); |
| | | } |
| | | |
| | | get displayClosedType(): string { |
| | | let closedType = 'æäº¤'; |
| | | switch (this.appointmentDetail.communicateStatus) { |
| | | case this.contactStatus.CLOSE: |
| | | closedType = 'æªæäº¤'; |
| | | break; |
| | | case this.contactStatus.CANCEL: |
| | | closedType = 'åæ¶'; |
| | | break; |
| | | } |
| | | return closedType; |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | </style> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <div> |
| | | <div class="interview__header"> |
| | | <div class="mdTxt">ç´è¨ªç´é</div> |
| | | <div class="pam-link-button" |
| | | @click="addInterview">+æ°å¢</div> |
| | | </div> |
| | | <InterviewCard :interviewList="displayList.slice(0, 3)"></InterviewCard> |
| | | |
| | | <section class="text--right mt-30 interview-check-more" v-if="interviewList.length > 3"> |
| | | <div class="pam-link-button" @click="readMoreBtn"> |
| | | å±éçæ´å¤ |
| | | <i class="icon-expand"></i> |
| | | </div> |
| | | </section> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop, Watch } from 'nuxt-property-decorator'; |
| | | import { InterviewRecord } from '~/shared/models/appointment.model'; |
| | | |
| | | @Component |
| | | export default class AppointmentInterviewList extends Vue { |
| | | @Prop() |
| | | interviewList!: InterviewRecord[]; |
| | | |
| | | appointmentId!: string; |
| | | displayList: InterviewRecord[] = []; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | this.appointmentId = this.$route.params.appointmentId; |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | @Watch('interviewList', {immediate: true}) |
| | | updateInterviewList() { |
| | | if (this.interviewList && this.interviewList.length > 0) { |
| | | this.displayList = this.interviewList |
| | | .map((i) => ({ ...i, sortDate: new Date(i.interviewDate)})) |
| | | .sort((preItem, nextItem) => +nextItem.sortDate - +preItem.sortDate); |
| | | } |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | addInterview(): void { |
| | | this.$router.push(`/appointment/${this.appointmentId}/interview/new`); |
| | | } |
| | | |
| | | readMoreBtn() { |
| | | this.$router.push(`/appointment/${this.appointmentId}/interviewList`); |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .interview__header { |
| | | display : flex; |
| | | justify-content: space-between; |
| | | margin-bottom : 10px; |
| | | } |
| | | .interview-check-more{ |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | </style> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <div class="appointment-progress"> |
| | | <div |
| | | class="appointment-progress__indicator" |
| | | :style="{ width: indicatorLineWidth }"> |
| | | <div class="line"></div> |
| | | <div |
| | | class="circle" |
| | | v-for="(step, index) in stepList" |
| | | :class="{ 'activate': index < displayCurrentStep }" |
| | | :key="index"> |
| | | </div> |
| | | </div> |
| | | <div class="appointment-progress__status-label xxsTxt text--bold ml-5"> |
| | | {{ displayStatusLabel }} |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop } from 'nuxt-property-decorator'; |
| | | |
| | | @Component |
| | | export default class AppointmentProgress extends Vue { |
| | | |
| | | @Prop() |
| | | totalStep?: number; |
| | | |
| | | @Prop() |
| | | currentStep!: 'picked' | 'reserved' | 'contacted' | 'done' | 'closed' | 'cancel'; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | get stepList(): number[] { |
| | | const tempList: number[] = []; |
| | | for(let i = 0; i < (this.totalStep || 3); i ++) { |
| | | tempList.push(i); |
| | | } |
| | | return tempList; |
| | | } |
| | | |
| | | get indicatorLineWidth(): string { |
| | | const connectLineGutter = 10; |
| | | return ((this.totalStep || 3) * 10 + connectLineGutter) + 'px'; |
| | | } |
| | | |
| | | get displayCurrentStep(): number { |
| | | let step: number = 1; |
| | | switch (this.currentStep) { |
| | | case 'contacted': |
| | | step = 2; |
| | | break; |
| | | case 'done': |
| | | step = 3; |
| | | break; |
| | | case 'closed': |
| | | step = 3; |
| | | break; |
| | | case 'cancel': |
| | | step = 3; |
| | | break; |
| | | } |
| | | return step; |
| | | } |
| | | |
| | | get displayStatusLabel(): 'æªè¯çµ¡' | 'ç´è¨ªä¸' | 'æäº¤' | 'æªæäº¤' | '已忶' { |
| | | let label: 'æªè¯çµ¡' | 'ç´è¨ªä¸' | 'æäº¤' | 'æªæäº¤' | '已忶' = 'æªè¯çµ¡'; |
| | | switch (this.currentStep) { |
| | | case 'contacted': |
| | | label = 'ç´è¨ªä¸'; |
| | | break; |
| | | case 'done': |
| | | label = 'æäº¤'; |
| | | break; |
| | | case 'closed': |
| | | label = 'æªæäº¤'; |
| | | break; |
| | | case 'cancel': |
| | | label = '已忶'; |
| | | break; |
| | | } |
| | | return label; |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | |
| | | .appointment-progress{ |
| | | display: flex; |
| | | .appointment-progress__indicator { |
| | | align-items : center; |
| | | display : flex; |
| | | justify-content: space-between; |
| | | position : relative; |
| | | .circle { |
| | | background-color: white; |
| | | border : 1px solid #CCCCCC; |
| | | border-radius : 50%; |
| | | height : 8px; |
| | | margin : 0; |
| | | width : 8px; |
| | | z-index : 5; |
| | | &.activate { |
| | | background-color: $BEIGE; |
| | | } |
| | | } |
| | | .line { |
| | | background-color: #707070; |
| | | height : 3px; |
| | | left : 5%; |
| | | position : absolute; |
| | | width : 90%; |
| | | } |
| | | } |
| | | } |
| | | |
| | | </style> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <div class="record-log-component"> |
| | | <div class="mdTxt mt-30 mb-10">系統éç¥ç´é</div> |
| | | |
| | | <InterviewRecordCard :noticeLogsList="displayLogs.slice(0, 3)"></InterviewRecordCard> |
| | | |
| | | <section class="text--center mt-30" v-if="displayLogs.length > 3"> |
| | | <div class="pam-link-button" |
| | | @click="readMoreBtn" |
| | | >å±éçæ´å¤ |
| | | <i class="icon-expand"></i></div> |
| | | </section> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop, Watch } from 'nuxt-property-decorator'; |
| | | import { NoticeLogs } from '~/shared/models/appointment.model'; |
| | | |
| | | @Component |
| | | export default class AppointmentRecordList extends Vue { |
| | | |
| | | @Prop() |
| | | noticeLogs!: NoticeLogs[]; |
| | | |
| | | appointmentId: string = ''; |
| | | displayLogs : NoticeLogs[] = []; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | this.appointmentId = this.$route.params.appointmentId; |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | @Watch('noticeLogs', {immediate: true}) |
| | | onNoticeLogsChange() { |
| | | if (this.noticeLogs.length) { |
| | | this.displayLogs = this.noticeLogs |
| | | .map((i) => ({ ...i, sortDate: new Date(i.createdDate)})) |
| | | .sort((preItem, nextItem) => +nextItem.sortDate - +preItem.sortDate); |
| | | } |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | readMoreBtn() { |
| | | this.$router.push(`/appointment/${this.appointmentId}/recordList`); |
| | | } |
| | | |
| | | } |
| | | </script> |
| | |
| | | import { Vue, Component,} from 'vue-property-decorator'; |
| | | |
| | | import * as _ from 'lodash'; |
| | | |
| | | import { Role } from '~/shared/models/enum/Role'; |
| | | |
| | | const roleStorage = namespace('localStorage'); |
| | | const appointmentStore = namespace('appointment.store'); |
| | | const roleStorage = namespace('localStorage'); |
| | | |
| | | @Component |
| | | export default class UiCarousel extends Vue { |
| | |
| | | @roleStorage.Getter |
| | | currentRole!:string; |
| | | |
| | | @appointmentStore.Getter |
| | | isCloseAppointment!: boolean; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | goBack(): void { |
| | | const pathName = this.$route.name; |
| | | pathName?.includes('myConsultantList') ? this.$router.push('/') : this.$router.go(-1); |
| | | pathName?.includes('myConsultantList') |
| | | ? this.$router.push('/') |
| | | : this.$router.go(-1); |
| | | } |
| | | |
| | | get label(): string { |
| | |
| | | featureLabel = 'æç顧忏
å®'; |
| | | break; |
| | | case 'agentInfo': |
| | | featureLabel = _.isEqual(this.currentRole,Role.ADMIN) ? 'æ¥ç帳èè³è¨' : 'æ¥åå¡è³è¨' |
| | | const agentFeatureLabel = this.$route.name.includes('edit') ? '編輯帳èè³è¨' : 'æ¥ç帳èè³è¨'; |
| | | featureLabel = _.isEqual(this.currentRole,Role.ADMIN) |
| | | ? agentFeatureLabel |
| | | : 'æ¥åå¡è³è¨' |
| | | break; |
| | | default: |
| | | featureLabel = 'åé¦é '; |
| | |
| | | case 'faq': |
| | | featureLabel = 'F&Q 常è¦åé¡'; |
| | | break; |
| | | case 'appointment': |
| | | const appointmentFeatureLabel = this.$route.name.includes('close') |
| | | ? 'çµæ¡' |
| | | : this.isCloseAppointment ? 'çµæ¡æç´°' : 'é ç´è³è¨'; |
| | | const inInterview = this.$route.name.includes('interview'); |
| | | const addNewInterview = this.$route.name.includes('new'); |
| | | const interviewList = this.$route.name.includes('interviewList'); |
| | | const recordList = this.$route.name.includes('recordList'); |
| | | if (interviewList) { |
| | | featureLabel = 'ç´è¨ªç´é'; |
| | | } else if (recordList) { |
| | | featureLabel = '系統éç¥ç´é'; |
| | | } else if (inInterview) { |
| | | featureLabel = addNewInterview |
| | | ? 'æ°å¢ç´è¨ªç´é' |
| | | : '編輯ç´è¨ªç´é'; |
| | | } else { |
| | | featureLabel = appointmentFeatureLabel; |
| | | } |
| | | break; |
| | | } |
| | | return featureLabel; |
| | | } else { |
| | |
| | | class="rowStyle cursor--pointer" |
| | | justify="space-between" |
| | | :class="{'new': newAppointment }" |
| | | @click.native="openDetail" |
| | | @click.native="viewAppointmentDetail" |
| | | > |
| | | <el-col :xs="1" :sm="1" class="unread" align="middle" v-if="isReserved"> |
| | | <div class="test"> |
| | | <div class="unread" v-if="isReserved"> |
| | | <div class="circle" v-if="!isRead"></div> |
| | | </el-col> |
| | | <el-col :xs="5" :sm="3" align="middle"> |
| | | </div> |
| | | |
| | | <div class="pl-10"> |
| | | <div class="smTxt_bold name">{{ client.name || 'NO NAME' }}</div> |
| | | <div v-if="client.communicateStatus === contactStatus.RESERVED" class="my-10 xsTxt">é ç´æå</div> |
| | | <div |
| | | class="xsTxt mb-10 mt-10" |
| | | v-else-if="client.communicateStatus === contactStatus.CONTACTED"> |
| | | ç´è¨ªç´é |
| | | </div> |
| | | <div |
| | | class="xsTxt mb-10 mt-10" |
| | | v-else> |
| | | 滿æåº¦ |
| | | <span v-if="client.satisfactionScore" class="xsTxt text--primary"> |
| | | <UiReviewScore :score="client.satisfactionScore"></UiReviewScore> |
| | | </span> |
| | | <span v-else class="xsTxt text--mid_grey">æªå¡«</span> |
| | | </div> |
| | | <div class="professionals mb-10" v-if="client.communicateStatus === contactStatus.RESERVED"> |
| | | <template v-if="client.requirement"> |
| | | <span |
| | | v-for="(item, index) in requirements" |
| | | :key="index" |
| | | class="professionalsTxt" |
| | | >#{{item}}</span> |
| | | </template> |
| | | <template v-else> |
| | | <span class="professionalsTxt noProfessionalsTxt" |
| | | >(å®¢æ¶æªæä¾éæ±é
ç®)</span> |
| | | </template> |
| | | </div> |
| | | <AppointmentProgress |
| | | :currentStep="client.communicateStatus" |
| | | ></AppointmentProgress> |
| | | </div> |
| | | </div> |
| | | <!-- <el-col :xs="5" :sm="3" align="middle"> |
| | | <el-avatar |
| | | :size="50" |
| | | ></el-avatar> |
| | |
| | | <div class="unfilled">æªå¡«æ»¿æåº¦</div> |
| | | </template> |
| | | </div> |
| | | </el-col> |
| | | <el-col :xs="14" :sm="14" class="pl-10"> |
| | | <div class="smTxt_bold name">{{ client.name }}</div> |
| | | <div class="message">é ç´æå</div> |
| | | <div class="professionals"> |
| | | <template v-if="client.requirement"> |
| | | <span |
| | | v-for="(item, index) in requirements" |
| | | :key="index" |
| | | class="professionalsTxt" |
| | | >#{{item}}</span> |
| | | </template> |
| | | <template v-else> |
| | | <span class="professionalsTxt noProfessionalsTxt" |
| | | >(å®¢æ¶æªæä¾éæ±é
ç®)</span> |
| | | </template> |
| | | </el-col> --> |
| | | |
| | | <div class="flex-column contactInfo" :xs="4" :sm="6"> |
| | | |
| | | <div |
| | | class="invite-msg smTxt_bold" |
| | | @click.stop="showAddInterviewDialog" |
| | | v-if="client.communicateStatus === contactStatus.RESERVED"> |
| | | å³éç´è¨ªéç¥ |
| | | </div> |
| | | </el-col> |
| | | <el-col class="flex-column contactInfo" :xs="4" :sm="6"> |
| | | <div |
| | | class="smTxt_bold fix-chrome-click--issue" |
| | | :class="{'unread-txt': reservedTxt === 'æªè®', 'read-txt': reservedTxt !== 'æªè®'}" |
| | | >{{ reservedTxt }}</div> |
| | | class="invite-msg smTxt_bold" |
| | | @click.stop="navigateToCloseAppointment" |
| | | v-else-if="client.communicateStatus === contactStatus.CONTACTED"> |
| | | çµæ¡ |
| | | </div> |
| | | <div |
| | | class="date xsTxt text--mid_grey" |
| | | class="invite-msg smTxt_bold" |
| | | @click.stop="inviteReview" |
| | | v-else-if="!client.satisfactionScore"> |
| | | ç¼é滿æåº¦ |
| | | </div> |
| | | |
| | | <div |
| | | class="date xsTxt text--black" |
| | | >{{ date }}</div> |
| | | <div |
| | | class="xsTxt text--mid_grey" |
| | | >{{ time }}</div> |
| | | </el-col> |
| | | </div> |
| | | </el-row> |
| | | |
| | | <Ui-Dialog |
| | | :isVisible.sync="isVisibleDialog" |
| | | :isVisible.sync="isShowInformDialog" |
| | | :width="dialogWidth" |
| | | @closeDialog="closeDialog" |
| | | @closeDialog="closeInformDialog" |
| | | class="pam-myDemand-dialog" |
| | | > |
| | | <h5 class="subTitle text--center mb-30" |
| | | >{{isReserved ? 'é ç´è³è¨' : 'å·²è¯çµ¡è³è¨'}}</h5> |
| | | |
| | | <p v-if='isReserved' |
| | | class="smTxt text-right" |
| | | class="smTxt text--right" |
| | | ><span v-if="isRead">{{client.consultantReadTime | formatDate}}</span> å·²è®</p> |
| | | <p |
| | | v-if="!isReserved" |
| | | class="smTxt text-right" |
| | | class="smTxt text--right" |
| | | >{{client.contactTime | formatDate}} è¯çµ¡</p> |
| | | <p class="smTxt">{{client.appointmentDate | formatDate}} é ç´</p> |
| | | |
| | |
| | | <p v-for="(item, index) in hopeContactTime" |
| | | :key="index" |
| | | >é£çµ¡ææ®µ{{index + 1 | formatNumber}}ï¼<span>{{ item | formatHopeContactTime}}</span></p> |
| | | </div> |
| | | <div class="mt-30"> |
| | | <div class="memoTitleStyle"> |
| | | <div class="mdTxt">å
§å®¹æè¿°</div> |
| | | <div |
| | | class="smTxt text--bold text--primary cursor--pointer text--underline edit" |
| | | @click='editMemo' |
| | | >編輯</div> |
| | | </div> |
| | | <div class="mt-30 text--center" v-if="isReserved"> |
| | | <el-button @click="markAppointment">æ¨è¨»çºå·²é£çµ¡</el-button> |
| | | |
| | | <el-input |
| | | class="mt-10 pam-appointment-textarea" |
| | | type="textarea" |
| | | :rows="3" |
| | | maxlength="100" |
| | | placeholder="è«è¼¸å
¥ï¼é100åã" |
| | | :disabled="!isEdit" |
| | | v-model="memo" |
| | | > |
| | | </el-input> |
| | | |
| | | <div class="mt-10 smTxt text--bold text--primary text--right fixed-Height"> |
| | | <template v-if="isEdit"> |
| | | <span class="cursor--pointer" @click="cancelEditMemo">åæ¶</span> |
| | | <span class="pl-20 cursor--pointer" @click="saveMemo">å²å</span> |
| | | </template> |
| | | </div> |
| | | </div> |
| | | <div class="mt-30 text--center" v-if="isReserved"> |
| | | <el-button @click="markAppointment">æ¨è¨»çºå·²é£çµ¡</el-button> |
| | | </div> |
| | | </Ui-Dialog> |
| | | |
| | | <InterviewMsg |
| | | :client="client" |
| | | :isVisible.sync="isShowAddInterviewDialog"> |
| | | </InterviewMsg> |
| | | <PopUpFrame :isOpen.sync="isShowInviteReviewDialog"> |
| | | <div class="text--middle invite-review"> |
| | | <div class="mb-30 mt-10">å·²ç¼é滿æåº¦</div> |
| | | <div class="text--primary text--middle cursor--pointer text--underline" @click="isShowInviteReviewDialog = false" :size="'250px'">æç¥éäº</div> |
| | | </div> |
| | | </PopUpFrame> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop, Action, namespace, Watch } from 'nuxt-property-decorator'; |
| | | import { Vue, Component, Prop, namespace, Watch } from 'nuxt-property-decorator'; |
| | | |
| | | import appointmentService from '~/shared/services/appointment.service'; |
| | | import myConsultantService from '~/shared/services/my-consultant.service'; |
| | | import UtilsService from '~/shared/services/utils.service'; |
| | | import { hideReviews } from '~/shared/const/hide-reviews'; |
| | | import { ClientInfo } from '~/shared/models/client.model'; |
| | | import myConsultantService from '~/shared/services/my-consultant.service'; |
| | | import { ElRow } from 'element-ui/types/row'; |
| | | import { Appointment, AppointmentMemoInfo } from '~/shared/models/appointment.model'; |
| | | import { ContactStatus } from '~/shared/models/enum/contact-status'; |
| | | import reviewsService from '~/shared/services/reviews.service'; |
| | | |
| | | const localStorage = namespace('localStorage'); |
| | | const appointmentStore = namespace('appointment.store'); |
| | | const localStorage = namespace('localStorage'); |
| | | |
| | | @Component({ |
| | | filters: { |
| | | formatNumber(index: number) { |
| | |
| | | } |
| | | }) |
| | | export default class ClientList extends Vue { |
| | | @Action |
| | | updateMyAppointment!: (data: ClientInfo) => void; |
| | | |
| | | @Prop() |
| | | client!: ClientInfo; |
| | | client!: Appointment; |
| | | |
| | | @appointmentStore.Action |
| | | updateMyAppointmentList!: (data: Appointment) => void; |
| | | |
| | | @appointmentStore.Action |
| | | getAppointmentDetail!: (appointmentId: number) => Promise<Appointment>; |
| | | |
| | | @appointmentStore.Action |
| | | updateAppointmentDetail!: (id: number) => Appointment; |
| | | |
| | | @appointmentStore.Getter |
| | | appointmentProgress!: ContactStatus; |
| | | |
| | | @localStorage.Mutation |
| | | storageClearAppointmentIdFromMsg!: () => void; |
| | | |
| | | isVisibleDialog = false; |
| | | dialogWidth = ''; |
| | | hideReviews = hideReviews; |
| | | contactStatus = ContactStatus; |
| | | |
| | | dialogWidth = ''; |
| | | hideReviews = hideReviews; |
| | | isEdit = false; |
| | | isShowAddInterviewDialog = false; |
| | | isShowInformDialog = false; |
| | | isShowInviteReviewDialog = false; |
| | | memo = ''; |
| | | |
| | | memoInfo: AppointmentMemoInfo = { |
| | | appointmentId: 0, |
| | | content : '', |
| | | id : 0 |
| | | }; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | this.memoInfo = this.client.appointmentMemoList.length > 0 |
| | | ? JSON.parse(JSON.stringify(this.client.appointmentMemoList[0])) |
| | | : {appointmentId: 0, content: '', id: 0}; |
| | | this.memo = this.memoInfo.content; |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | viewAppointmentDetail(): void { |
| | | this.getAppointmentDetail(this.client.id).then((_) => { |
| | | const unread = !this.client.consultantReadTime; |
| | | if (unread) { |
| | | this.readAppointment(); |
| | | } |
| | | this.$router.push(`/appointment/${this.client.id}`); |
| | | }); |
| | | } |
| | | |
| | | showAddInterviewDialog(): void { |
| | | this.isShowAddInterviewDialog = true; |
| | | } |
| | | |
| | | navigateToCloseAppointment(): void { |
| | | this.getAppointmentDetail(this.client.id).then((_) => { |
| | | this.$router.push(`/appointment/${this.client.id}/close`); |
| | | }); |
| | | } |
| | | |
| | | inviteReview(): void { |
| | | reviewsService.sendSatisfactionToClient(this.client.id).then(res => { |
| | | this.isShowInviteReviewDialog = true ; |
| | | }) |
| | | } |
| | | |
| | | openDetail() { |
| | | setTimeout(() => { |
| | | (this.$refs.clientCardRef as any).$el.classList.add('currentShowStyle'); |
| | | }, 0) |
| | | this.dialogWidth = UtilsService.isMobileDevice() ? '80%' : ''; |
| | | this.isShowInformDialog = true; |
| | | } |
| | | |
| | | markAppointment() { |
| | | myConsultantService.markAsContact(this.client.id).then(data => { |
| | | this.updateMyAppointmentList(data); |
| | | this.isShowInformDialog = false; |
| | | }) |
| | | } |
| | | |
| | | closeInformDialog(): void { |
| | | this.readAppointment(); |
| | | this.isEdit = false; |
| | | this.clearAppointmentIdFromMsg(); |
| | | } |
| | | |
| | | private readAppointment(): void { |
| | | appointmentService.recordRead(this.client.id).then((_) => { |
| | | const updatedClient = {...this.client}; |
| | | updatedClient.consultantReadTime = new Date().toString(); |
| | | this.updateMyAppointmentList(updatedClient); |
| | | this.updateAppointmentDetail(this.client.id); |
| | | }); |
| | | } |
| | | |
| | | private clearAppointmentIdFromMsg() { |
| | | this.storageClearAppointmentIdFromMsg(); |
| | | this.$router.push({query: {}}); |
| | | setTimeout(() => { |
| | | (this.$refs.clientCardRef as ElRow).$el.classList.remove('currentShowStyle') |
| | | },1000) |
| | | } |
| | | |
| | | saveMemo() { |
| | | if (this.client.appointmentMemoList.length > 0) { |
| | | const params = { |
| | | content: this.memo, |
| | | id: this.client.appointmentMemoList[0].id |
| | | }; |
| | | this.updateMemo(params); |
| | | return; |
| | | } |
| | | |
| | | const params = { |
| | | content: this.memo, |
| | | appointmentId: this.client.id, |
| | | } |
| | | this.createMemo(params); |
| | | } |
| | | |
| | | private createMemo(params) { |
| | | appointmentService.createMemo(params).then(memoRes => { |
| | | this.storeMemo(memoRes); |
| | | }); |
| | | } |
| | | |
| | | private updateMemo(params) { |
| | | appointmentService.updateMemo(params).then(memoRes => { |
| | | this.storeMemo(memoRes); |
| | | }); |
| | | } |
| | | |
| | | private storeMemo(memoRes) { |
| | | this.memoInfo = memoRes; |
| | | this.memo = this.memoInfo.content; |
| | | this.client.appointmentMemoList[0] = this.memoInfo; |
| | | this.isEdit = false; |
| | | } |
| | | |
| | | editMemo() { |
| | | this.isEdit = !this.isEdit; |
| | | this.memo = this.memoInfo.content; |
| | | } |
| | | |
| | | cancelEditMemo() { |
| | | this.isEdit = false; |
| | | this.memo = this.memoInfo.content; |
| | | } |
| | | |
| | | get newAppointment(): boolean { |
| | | return !this.client.consultantViewTime |
| | |
| | | } |
| | | } |
| | | |
| | | get time() { |
| | | const formatDate = (this.$options.filters as any).formatDate(this.displayTime); |
| | | return formatDate.split(' ')[1] |
| | | } |
| | | |
| | | get date() { |
| | | const formatDate = (this.$options.filters as any).formatDate(this.displayTime); |
| | | return formatDate.split(' ')[0]; |
| | | } |
| | | |
| | | openDetail() { |
| | | setTimeout(() => { |
| | | (this.$refs.clientCardRef as any).$el.classList.add('currentShowStyle'); |
| | | }, 0) |
| | | |
| | | this.dialogWidth = UtilsService.isMobileDevice() ? '80%' : ''; |
| | | this.isVisibleDialog = true; |
| | | } |
| | | |
| | | markAppointment() { |
| | | myConsultantService.markAsContact(this.client.id).then(data => { |
| | | // TODO: è¦æ¥å¾å°å³åç updated client è³æ - Ben 2021/11/16 |
| | | const updatedClient = {...this.client}; |
| | | updatedClient.communicateStatus = 'contacted'; |
| | | updatedClient.contactTime = new Date().toString(); |
| | | |
| | | this.updateMyAppointment(updatedClient); |
| | | this.isVisibleDialog = false; |
| | | }) |
| | | } |
| | | |
| | | closeDialog(): void { |
| | | const unread = !this.client.consultantReadTime; |
| | | if (unread) { |
| | | appointmentService.recordRead(this.client.id).then((_) => { |
| | | const updatedClient = {...this.client}; |
| | | updatedClient.consultantReadTime = new Date().toString(); |
| | | this.updateMyAppointment(updatedClient); |
| | | }); |
| | | }; |
| | | this.clearAppointmentIdFromMsg(); |
| | | } |
| | | |
| | | private clearAppointmentIdFromMsg() { |
| | | this.storageClearAppointmentIdFromMsg(); |
| | | this.$router.push({query: {}}); |
| | | setTimeout(() => { |
| | | (this.$refs.clientCardRef as ElRow).$el.classList.remove('currentShowStyle') |
| | | },1000) |
| | | get time() { |
| | | const formatDate = (this.$options.filters as any).formatDate(this.displayTime); |
| | | return formatDate.split(' ')[1] |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | <style lang="scss" scoped> |
| | | .rowStyle { |
| | | padding: 10px 15px 10px 5px; |
| | | background-color: $PRIMARY_WHITE; |
| | | margin-bottom: 10px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | border-left : solid 4px transparent; |
| | | display : flex; |
| | | justify-content : space-between; |
| | | margin-bottom : 10px; |
| | | padding : 10px 15px 10px 5px; |
| | | transition: background-color 0.5s; |
| | | |
| | | &.new { |
| | | border-left: solid 4px $YELLOW; |
| | | border-color: $YELLOW; |
| | | } |
| | | |
| | | &.currentShowStyle { |
| | | background-color: rgba(236, 195, 178, 0.5); |
| | | transition: background-color 0.5s; |
| | | transition : background-color 0.5s; |
| | | } |
| | | |
| | | .unread { |
| | | align-self: center; |
| | | |
| | | .circle { |
| | | width: 10px; |
| | | height: 10px; |
| | | border-radius: 50px; |
| | | background-color: $PRIMARY_RED; |
| | | margin: auto; |
| | | border-radius : 50%; |
| | | height : 10px; |
| | | margin : auto; |
| | | width : 10px; |
| | | } |
| | | } |
| | | |
| | | .satisfaction { |
| | | font-size: 12px; |
| | | font-size : 12px; |
| | | font-weight: bold; |
| | | margin-top: 5px; |
| | | |
| | | margin-top : 5px; |
| | | .unfilled { |
| | | color : $MID_GREY; |
| | | font-weight: lighter; |
| | | color: $MID_GREY; |
| | | } |
| | | } |
| | | |
| | | .message { |
| | | margin:10px 0; |
| | | } |
| | | |
| | | .professionals { |
| | | overflow: hidden; |
| | | white-space: nowrap; |
| | | overflow : hidden; |
| | | text-overflow: ellipsis; |
| | | |
| | | display: -webkit-box; |
| | | -webkit-box-orient: vertical; |
| | | -webkit-line-clamp: 1; |
| | | .professionalsTxt { |
| | | font-size: 12px; |
| | | font-weight: bold; |
| | | font-size : 12px; |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | |
| | | } |
| | | .noProfessionalsTxt { |
| | | color: $PRUDENTIAL_GREY; |
| | | color : $PRUDENTIAL_GREY; |
| | | font-weight: lighter; |
| | | } |
| | | } |
| | | |
| | | .contactInfo { |
| | | text-align: right; |
| | | .date { |
| | | font-size: 12px; |
| | | } |
| | | } |
| | | |
| | | .unread-txt { |
| | | @extend .text--primary; |
| | | } |
| | | |
| | | .read-txt { |
| | | color: $SKY_BLUE; |
| | | } |
| | | |
| | | } |
| | | |
| | | .flex-column { |
| | | display: flex; |
| | | flex-direction: column; |
| | | display : flex; |
| | | flex-direction : column; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .dialogTxt { |
| | | font-size: 20px; |
| | | overflow-y:scroll; |
| | | max-height: 45vh; |
| | | font-size : 20px; |
| | | max-height: 25vh; |
| | | overflow-y: scroll; |
| | | @include desktop { |
| | | height: 400px; |
| | | } |
| | | } |
| | | |
| | | .text-right { |
| | | text-align: right; |
| | | .memoTitleStyle { |
| | | display : flex; |
| | | flex-direction : row; |
| | | justify-content: space-between; |
| | | .edit { |
| | | align-self: flex-end; |
| | | } |
| | | } |
| | | .fixed-Height { |
| | | height: 16px; |
| | | } |
| | | .test{ |
| | | display: flex; |
| | | } |
| | | .invite-msg{ |
| | | width: 96px; |
| | | color: $PRIMARY_RED; |
| | | @extend .text--underline; |
| | | } |
| | | .invite-review{ |
| | | align-items : center; |
| | | display : flex; |
| | | flex-direction: column; |
| | | } |
| | | </style> |
| | |
| | | <script lang='ts'> |
| | | import { Vue, Component, Prop } from 'nuxt-property-decorator'; |
| | | |
| | | import { ClientInfo } from '~/shared/models/client.model'; |
| | | import { Appointment } from '~/shared/models/appointment.model'; |
| | | |
| | | @Component |
| | | export default class ClientList extends Vue { |
| | | @Prop() clients!: ClientInfo[]; |
| | | @Prop() clients!: Appointment[]; |
| | | @Prop() title!: string; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | get noDataPlaceholder(): string { |
| | | return this.title === 'reservedList' |
| | | ? 'æ¨ç®åç¡å·²é ç´å®¢æ¶' |
| | | : 'æ¨ç®åç¡å·²è¯çµ¡å®¢æ¶'; |
| | | let noDataWording = 'æ¨ç®åç¡å·²çµæ¡çé ç´å®'; |
| | | if (this.title === 'contactedList') { |
| | | noDataWording = 'æ¨ç®åç¡ç´è¨ªä¸çé ç´å®'; |
| | | } |
| | | if (this.title === 'reservedList') { |
| | | noDataWording = 'æ¨ç®åç¡æªè¯çµ¡çé ç´å®'; |
| | | } |
| | | return noDataWording; |
| | | } |
| | | } |
| | | </script> |
| | |
| | | <template> |
| | | <div> |
| | | <el-row type="flex" class="rowStyle" :class="{'new': !agentInfo.customerViewTime }"> |
| | | |
| | | <el-col :xs="2" :sm="1"></el-col> |
| | | <el-col :xs="22" :sm="23"> |
| | | <el-row type="flex"> |
| | | <el-col class="flex_column" :xs="5" :sm="3"> |
| | | <UiAvatar |
| | | :size="50" |
| | | :fileName="avatarFileName" |
| | | @click.native="showAgentDetail(agentInfo.agentNo)" |
| | | ></UiAvatar> |
| | | <!-- TODO:é±è滿æåº¦ --> |
| | | <div v-if="!hideReviews"> |
| | | <i class="icon-star pam-icon icon--yellow satisfaction" v-if="notScoreAppointmentYet"></i> |
| | | <span v-if="notScoreAppointmentYet"> |
| | | {{ agentInfo.satisfactionScore }} |
| | | </span> |
| | | <div class="unfilled text--center " |
| | | style="display:flex" |
| | | v-if="notScoreAppointmentYet">æªå¡«<br />滿æåº¦</div> |
| | | <span v-if="agentInfo.contactStatus !== 'contacted'">{{ agentInfo.avgScore }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :xs="10" :sm="15"> |
| | | <div class="smTxt_bold name">{{agentInfo.name}}</div> |
| | | <div class="professionals"> |
| | | <span |
| | | class="professionalsTxt" |
| | | v-for="(expertise, index) in agentInfo.expertise" |
| | | :key="index" |
| | | >#{{expertise}}</span> |
| | | </div> |
| | | <div |
| | | class="delete" |
| | | v-if="showRemoveBtn" |
| | | @click="removeAgent" |
| | | >ç§»é¤</div> |
| | | </el-col> |
| | | <el-col class="flex_column" :xs="9" :sm="6"> |
| | | <el-button |
| | | class="smTxt_bold outline_btn" |
| | | @click="reserveCommunication" |
| | | :class="actionBtnStyle" |
| | | >{{ actionBtnLabel }}</el-button> |
| | | <div |
| | | v-if="notScoreAppointmentYet" |
| | | class="text--primary mt-10 text--center text--underline cursor--pointer" |
| | | @click="reviewsBtn = true">çµ¦äºæ»¿æåº¦è©å</div> |
| | | <div class="updateTime mt-10"> |
| | | {{ agentInfo.updateTime | formatDate }} |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-col class="flex_column pl-5" :xs="5" :sm="3"> |
| | | <UiAvatar |
| | | :size="50" |
| | | :agentNo="agentInfo.agentNo" |
| | | @click.native="showAgentDetail(agentInfo.agentNo)" |
| | | ></UiAvatar> |
| | | <!-- TODO:é±è滿æåº¦ --> |
| | | <div v-if="!hideReviews"> |
| | | <i class="icon-star pam-icon icon--yellow satisfaction" v-if="notScoreAppointmentYet"></i> |
| | | <span v-if="notScoreAppointmentYet"> |
| | | {{ agentInfo.satisfactionScore }} |
| | | </span> |
| | | <div class="unfilled text--center " |
| | | style="display:flex" |
| | | v-if="notScoreAppointmentYet">æªå¡«<br />滿æåº¦</div> |
| | | <span v-if="agentInfo.contactStatus !== 'contacted'">{{ agentInfo.avgScore }}</span> |
| | | </div> |
| | | </el-col> |
| | | <el-col :xs="10" :sm="15"> |
| | | <div class="smTxt_bold name">{{agentInfo.name}}</div> |
| | | <div class="professionals"> |
| | | <span |
| | | class="professionalsTxt" |
| | | v-for="(expertise, index) in agentInfo.expertise" |
| | | :key="index" |
| | | >#{{expertise}}</span> |
| | | </div> |
| | | <div |
| | | class="delete" |
| | | v-if="showRemoveBtn" |
| | | @click="isRemoveAgentPopup = true" |
| | | >ç§»é¤</div> |
| | | <div |
| | | v-if="notScoreAppointmentYet" |
| | | class="text--primary text--underline cursor--pointer xsTxt text--bold" |
| | | @click="reviewsBtn = true">çµ¦äºæ»¿æåº¦è©å</div> |
| | | </el-col> |
| | | <el-col class="flex_column" :xs="9" :sm="6"> |
| | | <el-button |
| | | class="smTxt_bold outline_btn" |
| | | @click="reservedOrShowAppointmentInfo" |
| | | :class="actionBtnStyle" |
| | | >{{ actionBtnLabel }}</el-button> |
| | | <div class="updateTime mt-10"> |
| | | {{ agentInfo.updateTime | formatDate }} |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <Ui-Dialog |
| | | :isVisible.sync="isVisibleDialog" |
| | | :width="width" |
| | | class="pam-myDemand-dialog" |
| | | class="pam-myDemand-dialog pam-dialog-reserved" |
| | | > |
| | | <div v-if="appointmentDetail"> |
| | | <h5 class="subTitle text--center mb-30">é ç´æå</h5> |
| | | <p class="smTxt">{{appointmentDetail.appointmentDate | formatDate}}</p> |
| | | <div class="dialogInfo"> |
| | | <div class="reserved-info"> |
| | | <p>å§åï¼{{appointmentDetail.name}}</p> |
| | | <p>é»è©±ï¼{{appointmentDetail.phone}}</p> |
| | | <p>Emailï¼{{appointmentDetail.email}}</p> |
| | |
| | | </div> |
| | | </div> |
| | | |
| | | <div v-if="notScoreAppointmentYet" class="dialogInfo-btn"> |
| | | <div v-if="notScoreAppointmentYet" class="reserved-btn"> |
| | | <el-button type="primary" |
| | | @click.native="reviewsBtn = true">çµ¦äºæ»¿æåº¦è©å</el-button> |
| | | </div> |
| | |
| | | </div> |
| | | </div> |
| | | </Ui-Dialog> |
| | | <PopUpFrame :isOpen.sync="reviewsBtn"> |
| | | <div class="mdTxt"> |
| | | |
| | | <PopUpFrame :isOpen.sync="reviewsBtn" class="reviewDialog-content"> |
| | | <div class="mdTxt pam-dialog-review"> |
| | | ä¿éªé¡§å滿æåº¦ |
| | | <span class="hint">é¸åææ</span> |
| | | <div class="dialogInfo-score"> |
| | | <el-rate v-model="inputScore" class="pam-quickFilter-rate"></el-rate> |
| | | <div class="mt-30 review-content"> |
| | | <UiAvatar :size="80" :agentNo="agentInfo.agentNo"></UiAvatar> |
| | | <div class="review-text">å°æ¼é¡§å |
| | | <span class="text--primary">{{agentInfo.name}}</span> |
| | | çæ´é«æåï¼æ¨çµ¦äºå¹¾é¡æè©å¹ï¼ |
| | | </div> |
| | | </div> |
| | | <div class="dialogInfo-btn"> |
| | | |
| | | <div class="review-score"> |
| | | <el-rate v-model="inputScore" class="pam-rate mt-30"></el-rate> |
| | | </div> |
| | | |
| | | <div class="review-btn"> |
| | | <el-button |
| | | type="primary" |
| | | :disabled="!inputScore" |
| | |
| | | </div> |
| | | </PopUpFrame> |
| | | |
| | | <PopUpFrame :isOpen.sync="isConfirmPopup"> |
| | | <div class="text--center mdTxt">å·²æååæ¶æ¤çé ç´</div> |
| | | <PopUpFrame :isOpen.sync="isConfirmPopup"> |
| | | <div class="text--center mdTxt">å·²æå{{confirmTxt}}</div> |
| | | <div class="text--center mt-30"> |
| | | <el-button @click="isConfirmPopup = false" type="primary">確å®</el-button> |
| | | </div> |
| | | </PopUpFrame> |
| | | |
| | | <PopUpFrame :isOpen.sync="isRemoveAgentPopup"> |
| | | <div class="text--center mdTxt">æ¯å¦ç§»é¤é¡§å <span class="text--primary">{{agentInfo.name}}</span>ï¼</div> |
| | | <div class="text--center mt-30"> |
| | | <el-button @click="isRemoveAgentPopup = false">å¦</el-button> |
| | | <el-button @click="removeAgent" type="primary">æ¯</el-button> |
| | | </div> |
| | | </PopUpFrame> |
| | | </div> |
| | |
| | | isCancelPopup = false; |
| | | hideReviews = hideReviews; |
| | | isConfirmPopup = false; |
| | | |
| | | isRemoveAgentPopup = false; |
| | | confirmTxt = ''; |
| | | |
| | | appointmentDetail: any = { |
| | | age : '', |
| | |
| | | get notScoreAppointmentYet(): boolean { |
| | | const isAppointment = !!this.agentInfo['appointmentStatus']; |
| | | if (!isAppointment) return false; |
| | | return !this.agentInfo['appointmentScore']; |
| | | return this.agentInfo['appointmentStatus'] !== 'contacted' ? !this.agentInfo['appointmentScore'] : false; |
| | | } |
| | | |
| | | get isAppointment(): boolean { |
| | | return !!this.agentInfo['appointmentStatus'];; |
| | | return !!this.agentInfo['appointmentStatus']; |
| | | } |
| | | |
| | | get latestReservedAppointment(): Appointment { |
| | | get latestNotClosedAppointment(): Appointment { |
| | | return this.agentInfo.appointments! |
| | | .filter((appointment) => appointment.communicateStatus !== 'contacted') |
| | | .filter((appointment) => appointment.communicateStatus === 'reserved' || appointment.communicateStatus === 'contacted') |
| | | .map((reversedAppointment) => { |
| | | return { |
| | | ...reversedAppointment, |
| | |
| | | if (this.agentInfo['appointmentStatus'] === 'reserved') { |
| | | return 'å·²é ç´'; |
| | | } |
| | | if (this.agentInfo['appointmentStatus'] === 'done') { |
| | | return 'å·²æäº¤'; |
| | | } |
| | | if (this.agentInfo['appointmentStatus'] === 'closed') { |
| | | return 'æªæäº¤'; |
| | | } |
| | | } else { |
| | | if (this.agentInfo.contactStatus === 'contacted') { |
| | | return 'å·²è¯çµ¡'; |
| | |
| | | } |
| | | if (this.agentInfo['appointmentStatus'] === 'reserved') { |
| | | return 'reservedBtn'; |
| | | } |
| | | if (this.agentInfo['appointmentStatus'] === 'done') { |
| | | return 'doneBtn'; |
| | | } |
| | | if (this.agentInfo['appointmentStatus'] === 'closed') { |
| | | return 'closedBtn'; |
| | | } |
| | | } else { |
| | | if (this.agentInfo.contactStatus === 'contacted') { |
| | |
| | | @Action |
| | | storeConsultantList!: () => void; |
| | | |
| | | reserveCommunication() { |
| | | reservedOrShowAppointmentInfo() { |
| | | const isAppointment = !!this.agentInfo['appointmentStatus']; |
| | | const contactStatus = this.agentInfo.contactStatus; |
| | | if (!isAppointment && (!contactStatus || contactStatus === 'picked')) { |
| | |
| | | const isAppointment = !!this.agentInfo['appointmentStatus']; |
| | | const appointmentId = isAppointment |
| | | ? this.agentInfo['appointmentId'] |
| | | : this.latestReservedAppointment.id; |
| | | : this.latestNotClosedAppointment.id; |
| | | |
| | | appointmentService.getAppointmentDetail(appointmentId!).then(res => { |
| | | this.appointmentDetail = { |
| | |
| | | |
| | | removeAgent() { |
| | | this.removeFromMyConsultantList(this.agentInfo.agentNo).then((removeOk) => { |
| | | this.isRemoveAgentPopup = false; |
| | | setTimeout(() => { |
| | | this.confirmTxt = 'ç§»é¤é¡§å'; |
| | | this.isConfirmPopup = true; |
| | | }, 300); |
| | | |
| | | }); |
| | | } |
| | | |
| | |
| | | const isAppointment = !!this.agentInfo['appointmentStatus']; |
| | | const appointmentId = isAppointment |
| | | ? this.agentInfo['appointmentId'] |
| | | : this.latestReservedAppointment.id; |
| | | : this.latestNotClosedAppointment.id; |
| | | |
| | | const reviewParams: UserReviewsConsultantsParams = { |
| | | appointmentId: appointmentId, |
| | |
| | | } |
| | | |
| | | cancel() { |
| | | appointmentService.cancelAppointment(this.latestReservedAppointment.id).then(res => { |
| | | appointmentService.cancelAppointment(this.latestNotClosedAppointment.id).then(res => { |
| | | this.storeConsultantList(); |
| | | this.isVisibleDialog = false; |
| | | this.isCancelPopup = false; |
| | | setTimeout(() => { |
| | | this.confirmTxt = 'åæ¶æ¤çé ç´'; |
| | | this.isConfirmPopup = true; |
| | | |
| | | }, 300); |
| | | }); |
| | | } |
| | |
| | | } |
| | | |
| | | .delete { |
| | | display: inline-block; |
| | | color: $PRIMARY_RED; |
| | | font-size: 14px; |
| | | font-weight: bold; |
| | |
| | | } |
| | | } |
| | | |
| | | .doneBtn { |
| | | color: $BEIGE; |
| | | border-color: $BEIGE; |
| | | |
| | | &:focus { |
| | | color: $PRIMARY_WHITE; |
| | | background-color: $BEIGE; |
| | | opacity: 0.5; |
| | | } |
| | | } |
| | | |
| | | .closedBtn { |
| | | color: $PRUDENTIAL_GREY; |
| | | border-color: $PRUDENTIAL_GREY; |
| | | |
| | | &:focus { |
| | | color: $PRIMARY_WHITE; |
| | | background-color: $PRUDENTIAL_GREY; |
| | | opacity: 0.5; |
| | | } |
| | | } |
| | | |
| | | .updateTime { |
| | | font-size: 12px; |
| | | font-weight: bold; |
| | |
| | | flex-direction: column; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .dialogInfo { |
| | | font-size: 20px; |
| | | overflow-y:scroll; |
| | | height: 400px; |
| | | } |
| | | .dialogInfo-btn{ |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | .dialogInfo-score{ |
| | | display: flex; |
| | | justify-content: center; |
| | | margin-bottom: 50px; |
| | | } |
| | | |
| | | </style> |
| | |
| | | :key="index" |
| | | > |
| | | <div class="consultantCardStyle" > |
| | | <UiAvatar :size="80" :fileName="agentInfo.img" class="mb-10"></UiAvatar> |
| | | <UiAvatar |
| | | class="mb-10" |
| | | :size="80" |
| | | :agentNo="agentInfo.agentNo" |
| | | > |
| | | </UiAvatar> |
| | | <div class="name">{{agentInfo.name}}</div> |
| | | <div v-if="!hideReviews"> |
| | | <!-- TODO:é±è滿æåº¦ --> |
| | |
| | | </div> |
| | | </swiper-slide> |
| | | |
| | | <div class="swiper-button-prev" slot="button-prev"></div> |
| | | <div class="swiper-button-next" slot="button-next"></div> |
| | | <div class="swiper-button-prev" slot="button-prev"> |
| | | <i class="icon-left"></i> |
| | | </div> |
| | | <div class="swiper-button-next" slot="button-next"> |
| | | <i class="icon-right"></i> |
| | | </div> |
| | | </swiper> |
| | | </div> |
| | | </template> |
| | |
| | | height: 100%; |
| | | |
| | | &:after { |
| | | display: none; |
| | | } |
| | | |
| | | .icon-right,.icon-left { |
| | | font-size: 20px; |
| | | font-weight: bold; |
| | | color: #707A81; |
| | | color: $CORAL; |
| | | } |
| | | |
| | | &.swiper-button-disabled { |
¤ñ¹ï·sÀÉ®× |
| | |
| | | |
| | | |
| | | <template> |
| | | <div class="dateTime"> |
| | | <UiDatePicker |
| | | @changeDate="changeDateTime($event, 'date')" |
| | | :isPastDateDisabled="isPastDateDisabled" |
| | | :isFutureDateDisabled="isFutureDateDisabled" |
| | | :defaultValue="defaultValue" |
| | | ></UiDatePicker> |
| | | <UiTimePicker |
| | | @changeTime="changeDateTime($event, 'time')" |
| | | :defaultValue="defaultValue" |
| | | :isPastDateDisabled="isPastDateDisabled" |
| | | :isFutureDateDisabled="isFutureDateDisabled" |
| | | :changeDate="changeDate" |
| | | ></UiTimePicker> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Component, Emit, Prop, Vue, Watch } from "nuxt-property-decorator"; |
| | | |
| | | @Component |
| | | export default class DateTimePicker extends Vue { |
| | | changeDate: Date | string = ''; |
| | | changeTime!: string; |
| | | |
| | | @Prop() |
| | | defaultValue!: string; |
| | | |
| | | @Prop() |
| | | isPastDateDisabled!: boolean; |
| | | |
| | | @Prop() |
| | | isFutureDateDisabled!: boolean; |
| | | |
| | | @Emit('changeDateTime') |
| | | changeDateTime(event, type) { |
| | | if (type === 'date') { |
| | | this.changeDate = event; |
| | | } |
| | | if (type === 'time') { |
| | | this.changeTime = event; |
| | | } |
| | | if (this.changeDate && this.changeTime) { |
| | | const hour = this.changeTime.split(':')[0]; |
| | | const minute = this.changeTime.split(':')[1]; |
| | | const interViewTime = new Date(this.changeDate).setHours(+hour, +minute); |
| | | return new Date(interViewTime); |
| | | } |
| | | return ''; |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .dateTime { |
| | | display: flex; |
| | | flex-direction: row; |
| | | justify-content: space-between; |
| | | font-size: 20px; |
| | | } |
| | | </style> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <div class="edit-appointment-record"> |
| | | <div class="edit-appointment-record-date" v-if="interviewId && interviewRecord"> |
| | | <span>{{interviewRecord.createdDate | formatDate}} 建ç«</span> |
| | | <span>{{interviewRecord.lastModifiedDate | formatDate}} æ´æ°</span> |
| | | </div> |
| | | <el-row class="mdTxt mb-10"> |
| | | <el-col :xs="16" :sm="20"> |
| | | <span :class="{'required': !interviewId || isEdit}">ç´è¨ªæé</span> |
| | | </el-col> |
| | | <el-col :xs="8" :sm="4" class="text--right" v-if="interviewId"> |
| | | <span |
| | | v-if="!isEdit" |
| | | class="mr-10 text--primary cursor--pointer" |
| | | @click="showCancelPopUp = true" |
| | | ><i class="icon-delet"></i></span> |
| | | <span |
| | | v-if="!isEdit" |
| | | class="text--primary cursor--pointer" |
| | | @click="isEdit = !isEdit" |
| | | ><i class="icon-edit"></i></span> |
| | | </el-col> |
| | | </el-row> |
| | | <template v-if="!interviewId || isEdit"> |
| | | <DateTimePicker |
| | | @changeDateTime="interviewTime = $event" |
| | | :defaultValue="defaultValue" |
| | | ></DateTimePicker> |
| | | </template> |
| | | <template v-else> |
| | | <div class="fs-20 mt-20"> |
| | | {{formatInterviewDate}} |
| | | </div> |
| | | </template> |
| | | |
| | | <div class="mdTxt mb-10 mt-30" :class="{'required': !interviewId || isEdit}">ç´è¨ªç´é</div> |
| | | <template v-if="!interviewId || isEdit"> |
| | | <el-input |
| | | type="textarea" |
| | | :rows="5" |
| | | placeholder="è«è¼¸å
¥ç´è¨ªç´é" |
| | | resize="none" |
| | | v-model="content" |
| | | > |
| | | </el-input> |
| | | </template> |
| | | <template v-else> |
| | | <div class="fs-20 mt-20"> |
| | | {{content}} |
| | | </div> |
| | | </template> |
| | | <div class="edit-appointment-record-btn" v-if="!interviewId || isEdit"> |
| | | <el-button @click="cancel">åæ¶</el-button> |
| | | <el-button |
| | | :disabled="!interviewTime || !content" |
| | | @click="saveInterviewRecord" |
| | | >確å®</el-button> |
| | | </div> |
| | | |
| | | <PopUpFrame :isOpen.sync="showCancelPopUp" |
| | | @closePopUp="showCancelPopUp = false" |
| | | > |
| | | <div class="text--center mdTxt">æ¯å¦åªé¤æ¤çç´è¨ªè¨éï¼</div> |
| | | <div class="text--center mt-30"> |
| | | <el-button @click="showCancelPopUp = false">å¦</el-button> |
| | | <el-button @click="deleteInterviewRecord" type="primary">æ¯</el-button> |
| | | </div> |
| | | </PopUpFrame> |
| | | |
| | | <PopUpFrame :isOpen.sync="showConfirmPopup" |
| | | @closePopUp="closePopup"> |
| | | <div class="text--center mdTxt">{{confirmTxt}}ï¼</div> |
| | | <div class="text--center mt-30"> |
| | | <el-button @click="closePopup" type="primary">確å®</el-button> |
| | | </div> |
| | | </PopUpFrame> |
| | | |
| | | <PopUpFrame :isOpen.sync="showFutureDateConfirmPopup" |
| | | @closePopUp="closePopup"> |
| | | <div class="text--center mdTxt">{{confirmTxt}}ï¼</div> |
| | | <div class="text--center mdTxt">ç«å³ç¼éç´è¨ªéç¥ï¼</div> |
| | | <div class="text--center mt-30" style="display:flex"> |
| | | <el-button @click="closePopup">å
ä¸ç¼é</el-button> |
| | | <el-button @click="showInterviewMsgPopup = true" type="primary">å³éç´è¨ªéç¥</el-button> |
| | | </div> |
| | | </PopUpFrame> |
| | | |
| | | <InterviewMsg |
| | | :isVisible.sync="showInterviewMsgPopup" |
| | | :client="appointmentDetail" |
| | | :defaultValue="interviewTime" |
| | | @closeDialog="closePopup" |
| | | ></InterviewMsg> |
| | | </div> |
| | | </template> |
| | | <script lang="ts"> |
| | | import { Appointment, InterviewRecord, InterviewRecordInfo } from '~/shared/models/appointment.model'; |
| | | import { Vue, Component, Watch, namespace } from 'nuxt-property-decorator'; |
| | | import appointmentService from '~/shared/services/appointment.service'; |
| | | |
| | | const appointmentStore = namespace('appointment.store'); |
| | | |
| | | @Component |
| | | export default class InterviewAdd extends Vue { |
| | | |
| | | @appointmentStore.State |
| | | appointmentDetail!: Appointment; |
| | | |
| | | @appointmentStore.Action |
| | | updateAppointmentDetail!: (id: number) => Appointment; |
| | | |
| | | interviewTime = ''; |
| | | content = ''; |
| | | |
| | | interviewId = ''; |
| | | appointmentId = ''; |
| | | confirmTxt: 'æ°å¢æå' | '編輯æå' | 'åªé¤æå' = 'æ°å¢æå'; |
| | | |
| | | isEdit = false; |
| | | |
| | | showConfirmPopup = false; |
| | | showCancelPopUp = false; |
| | | showInterviewMsgPopup = false; |
| | | showFutureDateConfirmPopup = false; |
| | | |
| | | defaultValue = ''; |
| | | |
| | | interviewRecord!: InterviewRecord; |
| | | |
| | | //////////////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | this.interviewId = this.$route.params.interviewId; |
| | | this.appointmentId = this.$route.params.appointmentId; |
| | | |
| | | this.onAppointmentDetailChange(); |
| | | } |
| | | |
| | | //////////////////////////////////////////////////////////////////// |
| | | |
| | | @Watch('appointmentDetail', {immediate: true}) |
| | | onAppointmentDetailChange() { |
| | | if (this.appointmentDetail && this.appointmentDetail.id === +this.appointmentId) { |
| | | this.interviewRecord = this.appointmentDetail.interviewRecordDTOs |
| | | .filter(item => item.id === +this.interviewId)[0]; |
| | | |
| | | if (this.interviewRecord && this.interviewId) { |
| | | this.content = this.interviewRecord.content; |
| | | this.defaultValue = this.interviewRecord.interviewDate; |
| | | } |
| | | } |
| | | } |
| | | |
| | | //////////////////////////////////////////////////////////////////// |
| | | |
| | | saveInterviewRecord() { |
| | | const interviewRecordInfo: InterviewRecordInfo = { |
| | | content: this.content, |
| | | interviewDate: this.interviewTime, |
| | | appointmentId: +this.appointmentId |
| | | }; |
| | | if (!this.interviewId) { |
| | | this.createdRecord(interviewRecordInfo); |
| | | } else { |
| | | const updateInterviewRecord = { |
| | | ...interviewRecordInfo, |
| | | id: +this.interviewId |
| | | } |
| | | this.updateRecord(updateInterviewRecord); |
| | | } |
| | | } |
| | | |
| | | private createdRecord(interviewRecordInfo) { |
| | | appointmentService.createInterviewRecord(interviewRecordInfo).then(res => { |
| | | this.showPopUp('æ°å¢æå'); |
| | | }); |
| | | } |
| | | |
| | | private updateRecord(updateInterviewRecord) { |
| | | appointmentService.updateInterviewRecord(updateInterviewRecord).then(res => { |
| | | this.showPopUp('編輯æå'); |
| | | }); |
| | | } |
| | | |
| | | private showPopUp(confirmTxt) { |
| | | this.confirmTxt = confirmTxt; |
| | | this.updateAppointmentDetail(+this.appointmentId); |
| | | |
| | | if (new Date(this.interviewTime).getTime() >= new Date().getTime()) { |
| | | this.showFutureDateConfirmPopup = true; |
| | | } else { |
| | | this.showConfirmPopup = true; |
| | | } |
| | | } |
| | | |
| | | deleteInterviewRecord() { |
| | | appointmentService.deleteInterviewRecord(this.interviewId).then(res => { |
| | | this.confirmTxt = 'åªé¤æå'; |
| | | this.showConfirmPopup = true; |
| | | this.updateAppointmentDetail(+this.appointmentId); |
| | | }); |
| | | } |
| | | |
| | | cancel() { |
| | | if (this.interviewId) { |
| | | this.content = this.interviewRecord.content; |
| | | this.defaultValue = this.interviewRecord.interviewDate; |
| | | this.isEdit = false; |
| | | } else { |
| | | this.$router.go(-1); |
| | | } |
| | | } |
| | | |
| | | closePopup() { |
| | | this.$router.go(-1); |
| | | } |
| | | |
| | | //////////////////////////////////////////////////////////////////// |
| | | |
| | | get formatInterviewDate() { |
| | | const interviewDate = new Date(this.interviewRecord.interviewDate); |
| | | const hours = interviewDate.getHours(); |
| | | const minutes = interviewDate.getMinutes(); |
| | | return `${interviewDate.getFullYear()}/${interviewDate.getMonth() + 1}/${interviewDate.getDate()} ${hours < 10 ? '0' + hours : hours}:${minutes < 10 ? '0' + minutes : minutes}`; |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" > |
| | | .edit-appointment-record { |
| | | padding-left : 10px; |
| | | padding-right: 10px; |
| | | .edit-appointment-record-date{ |
| | | color : #68737A; |
| | | display : flex; |
| | | justify-content: space-between; |
| | | margin-bottom : 26px; |
| | | } |
| | | } |
| | | .edit-appointment-record-btn{ |
| | | margin-top: 30px; |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | .el-textarea__inner { |
| | | border: 1px solid #707070; |
| | | padding: 10px 20px; |
| | | font-size: 20px; |
| | | &::placeholder { |
| | | font-size: 20px; |
| | | } |
| | | } |
| | | .required { |
| | | position: relative; |
| | | |
| | | &::before { |
| | | content: '*'; |
| | | position: absolute; |
| | | color: #FF0000; |
| | | transform: translate(-12px, 0); |
| | | z-index: 5; |
| | | } |
| | | } |
| | | |
| | | </style> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <div> |
| | | <template v-if="!interviewList.length"> |
| | | <span class="record-card record-card--empty" style="display:flex"> |
| | | ç¡ç´è¨ªç´é |
| | | </span> |
| | | </template> |
| | | |
| | | <template v-else> |
| | | <div class="interview--future"> |
| | | <div class="record-card mb-10" |
| | | v-for="(item, index) in futureList" |
| | | :key="index + 'feature'" |
| | | @click="editInterview(item)" |
| | | > |
| | | <div class="remind-container"> |
| | | <div class="remind-date mr-10"> |
| | | <div class="mb-3 smTxt bgc-primary-red date-year"> |
| | | <div class="mb-3 mt-2"> |
| | | <UiDateFormat |
| | | class="date bold" |
| | | :date="item.interviewDate" |
| | | onlyShowSection="YEAR" /> |
| | | </div> |
| | | </div> |
| | | <div class="p mt-5"> |
| | | <UiDateFormat |
| | | class="date bold" |
| | | :date="item.interviewDate" |
| | | onlyShowSection="DATE" /> |
| | | </div> |
| | | </div> |
| | | <div class="remind-content-txt"> |
| | | <div style="display:flex"> |
| | | <UiDateFormat |
| | | class="date bold " |
| | | :date="item.interviewDate" |
| | | onlyShowSection="TIME" /> |
| | | </div> |
| | | <div class="interview-card-content">{{item.content}}</div> |
| | | </div> |
| | | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <section class="interview--past" v-if="pastList.length"> |
| | | <div class="record-card mb-10" |
| | | v-for="(item, index) in pastList" |
| | | :key="index + 'past'" |
| | | @click="editInterview(item)" |
| | | > |
| | | |
| | | <div class="remind-container"> |
| | | <div class="remind-date mr-10"> |
| | | <div class="mb-3 smTxt bgc-primary-red date-year"> |
| | | <div class="mb-3 mt-2"> |
| | | <UiDateFormat |
| | | class="bold date" |
| | | :date="item.interviewDate" |
| | | onlyShowSection="YEAR" /> |
| | | </div> |
| | | </div> |
| | | <div class="p mt-5"> |
| | | <UiDateFormat |
| | | class="mt-5 line-space time" |
| | | :date="item.interviewDate" |
| | | onlyShowSection="DATE" /> |
| | | </div> |
| | | </div> |
| | | <div class="remind-content-txt"> |
| | | <div style="display:flex"> |
| | | <UiDateFormat |
| | | class="mt-5 line-space mb-3 time" |
| | | :date="item.interviewDate" |
| | | onlyShowSection="TIME" /> |
| | | </div> |
| | | <div class="interview-card-content">{{item.content}}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | </template> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Component, Prop, Vue, Watch } from "nuxt-property-decorator"; |
| | | import { InterviewRecord } from "~/shared/models/appointment.model"; |
| | | |
| | | @Component |
| | | export default class InterviewCard extends Vue { |
| | | |
| | | @Prop() |
| | | interviewList!: InterviewRecord[]; |
| | | |
| | | futureList: InterviewRecord[] = []; |
| | | pastList: InterviewRecord[] = []; |
| | | |
| | | appointmentId!: number; |
| | | |
| | | mounted() { |
| | | this.appointmentId = +this.$route.params.appointmentId; |
| | | } |
| | | |
| | | @Watch('interviewList', {immediate: true}) |
| | | onInterviewListChange() { |
| | | if (this.interviewList.length > 0) { |
| | | this.futureList = this.interviewList |
| | | .filter(item => new Date(item.interviewDate).getTime() >= new Date().getTime()) |
| | | .sort((preItem, nextItem) => +new Date(nextItem.interviewDate) - +new Date(preItem.interviewDate)); |
| | | this.pastList = this.interviewList |
| | | .filter(item => new Date(item.interviewDate).getTime() < new Date().getTime()) |
| | | .sort((preItem, nextItem) => +new Date(nextItem.interviewDate) - +new Date(preItem.interviewDate)); |
| | | } |
| | | |
| | | } |
| | | |
| | | editInterview(interviewRecord) { |
| | | this.$router.push(`/appointment/${this.appointmentId}/interview/${interviewRecord.id}`); |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .interview--future{ |
| | | .record{ |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | } |
| | | .interview--past { |
| | | margin-top: 10px; |
| | | border-top: 1px solid #CCCCCC; |
| | | padding-top: 17px; |
| | | margin-top: 17px; |
| | | } |
| | | .record-card { |
| | | height: 64px; |
| | | margin-bottom: 20px; |
| | | .record-card-date{ |
| | | display: flex; |
| | | flex-direction: column; |
| | | margin-left: 10px; |
| | | margin-right: 10px; |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .record-card-content{ |
| | | height: 42px; |
| | | margin-top: 10px; |
| | | margin-right: 10px; |
| | | line-height: 1.2; |
| | | |
| | | } |
| | | &.record-card--empty { |
| | | align-items : center; |
| | | background-color: #fff; |
| | | color : $MID_GREY; |
| | | justify-content : center; |
| | | } |
| | | } |
| | | .interview-card-content{ |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | display: -webkit-box; |
| | | -webkit-box-orient: vertical; |
| | | word-break: break-all; |
| | | word-wrap: break-word; |
| | | -webkit-line-clamp: 2; |
| | | } |
| | | .line-space{ |
| | | letter-spacing: 1px; |
| | | } |
| | | </style> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <div class="interview-msg-component"> |
| | | <el-dialog |
| | | :visible.sync="dialogVisible" |
| | | :width="dialogWidth" |
| | | @close="closeDialog" |
| | | :lock-scroll="false" |
| | | > |
| | | <div class="subTitle msg-dialog-title">ç´è¨ªéç¥</div> |
| | | <div class="send-msg-nav"> |
| | | <div class="mdTxt">éç¥å
§å®¹</div> |
| | | </div> |
| | | |
| | | <el-input |
| | | type="textarea" |
| | | :autosize="true" |
| | | placeholder="ç´è¨ªéç¥" |
| | | resize="none" |
| | | v-model="interviewTxt"> |
| | | </el-input> |
| | | |
| | | <div v-if="client.phone"> |
| | | <div class="mdTxt mt-30 mb-10 required">é è¨ç´è¨ªææ®µ</div> |
| | | <DateTimePicker |
| | | @changeDateTime="interviewTime = $event" |
| | | :isPastDateDisabled="true" |
| | | :defaultValue="defaultValue" |
| | | ></DateTimePicker> |
| | | </div> |
| | | |
| | | <div class="msg-dialog-btn"> |
| | | <el-button @click="addInterview" :disabled="isBtnDisabled">å³é</el-button> |
| | | </div> |
| | | |
| | | </el-dialog> |
| | | |
| | | <PopUpFrame |
| | | :isOpen.sync="isShowSuccessAlert" |
| | | @closePopUp="closeAllDialog"> |
| | | <div class="text--middle invite-review"> |
| | | <div class="mb-30 mt-10">å·²ç¼éç´è¨ªéç¥</div> |
| | | <div class="text--primary text--middle cursor--pointer text--underline" @click="closeAllDialog " :size="'250px'">æç¥éäº</div> |
| | | </div> |
| | | </PopUpFrame> |
| | | </div> |
| | | </template> |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop, PropSync, Emit, namespace } from 'nuxt-property-decorator'; |
| | | |
| | | import appointmentService from '~/shared/services/appointment.service'; |
| | | import { Appointment, ToInformAppointment } from '~/shared/models/appointment.model'; |
| | | import { AgentInfo } from '~/shared/models/agent-info.model'; |
| | | |
| | | const loginStore = namespace('login.store'); |
| | | const appointmentStore = namespace('appointment.store'); |
| | | |
| | | @Component |
| | | export default class InterviewMsg extends Vue { |
| | | |
| | | @appointmentStore.Action |
| | | updateAppointmentDetail!: (id: number) => Appointment; |
| | | |
| | | @appointmentStore.Action |
| | | getMyAppointmentList!: () => Promise<Appointment[]>; |
| | | |
| | | @PropSync('isVisible') |
| | | dialogVisible!: boolean; |
| | | |
| | | @Prop({default:'90%'}) |
| | | dialogWidth!:string; |
| | | |
| | | @Prop() |
| | | client!: Appointment; |
| | | |
| | | @Prop() |
| | | defaultValue!: string; |
| | | |
| | | @Emit('closeDialog') |
| | | closeDialog() { |
| | | return; |
| | | } |
| | | |
| | | @loginStore.State |
| | | loginConsultant!: AgentInfo; |
| | | |
| | | isShowSuccessAlert = false; |
| | | |
| | | interviewTxt = ""; |
| | | interviewTime = ''; |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | if(this.loginConsultant.phoneNumber && this.loginConsultant.email) |
| | | { |
| | | this.interviewTxt = "æ¨å¥½ï¼ææ¯ä¿èª åªåå¹³å°çä¿éªé¡§å" + this.loginConsultant.name + "ï¼æè¬æ¨çé ç´ï¼æé è¨æå¨ä¸è¿°çæéèæ¨è¯ç¹«"+"\n"+"以䏿¯æçé»è©±è碼/Emailï¼"+"\n" + this.loginConsultant.phoneNumber + "\n" + this.loginConsultant.email + "\n"+"è¥æ¤æé䏿¹ä¾¿ï¼è«èæè¯ç¹«ï¼è¬è¬ï¼"} |
| | | else if (!this.loginConsultant.phoneNumber && this.loginConsultant.email) |
| | | { |
| | | this.interviewTxt = "æ¨å¥½ï¼ææ¯ä¿èª åªåå¹³å°çä¿éªé¡§å" + this.loginConsultant.name + "ï¼æè¬æ¨çé ç´ï¼æé è¨æå¨ä¸è¿°çæéèæ¨è¯ç¹«"+"\n"+"以䏿¯æçEmailï¼"+"\n" + this.loginConsultant.email + "\n"+"è¥æ¤æé䏿¹ä¾¿ï¼è«èæè¯ç¹«ï¼è¬è¬ï¼" |
| | | } |
| | | else |
| | | this.interviewTxt = "æ¨å¥½ï¼ææ¯ä¿èª åªåå¹³å°çä¿éªé¡§å" + this.loginConsultant.name + "ï¼æè¬æ¨çé ç´ï¼æé è¨æå¨ä¸è¿°çæéèæ¨è¯ç¹«"+"\n"+"以䏿¯æçé»è©±è碼ï¼"+"\n" + this.loginConsultant.phoneNumber + "\n"+"è¥æ¤æé䏿¹ä¾¿ï¼è«èæè¯ç¹«ï¼è¬è¬ï¼" |
| | | } |
| | | |
| | | addInterview() { |
| | | const appointmentInformation: ToInformAppointment = { |
| | | appointmentId: this.client.id, |
| | | email : this.client?.email, |
| | | interviewDate: this.interviewTime, |
| | | message : this.interviewTxt, |
| | | phone : this.client?.phone, |
| | | }; |
| | | appointmentService.informAppointment(appointmentInformation).then((_) => { |
| | | this.isShowSuccessAlert = true ; |
| | | }); |
| | | } |
| | | |
| | | closeAllDialog() { |
| | | this.isShowSuccessAlert = false ; |
| | | this.dialogVisible = false; |
| | | this.updateAppointmentDetail(this.client.id); |
| | | this.getMyAppointmentList(); |
| | | } |
| | | |
| | | get isBtnDisabled() :Boolean { |
| | | const isFormValid = this.client.phone ? this.interviewTxt && this.interviewTime :this.interviewTxt |
| | | return !isFormValid |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" > |
| | | .interview-msg-component{ |
| | | |
| | | .required { |
| | | position: relative; |
| | | &::before { |
| | | content: '*'; |
| | | position: absolute; |
| | | color: #FF0000; |
| | | transform: translate(-12px, 0); |
| | | } |
| | | } |
| | | .msg-dialog-title{ |
| | | display: flex; |
| | | justify-content: center; |
| | | margin-bottom:30px; |
| | | color: $PRIMARY_BLACK; |
| | | } |
| | | .send-msg-nav{ |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | color: $PRIMARY_BLACK; |
| | | } |
| | | .el-dialog{ |
| | | width:90% |
| | | } |
| | | .el-textarea__inner{ |
| | | font-size: 20px; |
| | | padding:10px; |
| | | text-align: justify; |
| | | font-weight: 600; |
| | | } |
| | | .msg-dialog-btn{ |
| | | margin-top: 30px; |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | .invite-review{ |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | } |
| | | } |
| | | </style> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <div class="record-log-component"> |
| | | <div v-for="(item, index) in noticeLogsList" |
| | | :key="index"> |
| | | <section |
| | | class="record-log-card" |
| | | > |
| | | <div class="record-log-card-date-container"> |
| | | <div class="record-log-card-date-container-circle"> |
| | | <div> |
| | | <UiDateFormat |
| | | class="xxsTxt bold line-height" |
| | | :date="item.createdDate" |
| | | onlyShowSection="YEAR" /> |
| | | </div> |
| | | <div> |
| | | <UiDateFormat |
| | | class="xxsTxt bold line-height" |
| | | :date="item.createdDate" |
| | | onlyShowSection="DATE" /> |
| | | </div> |
| | | <div> |
| | | <UiDateFormat |
| | | class="xxsTxt mt-4 line-space" |
| | | :date="item.createdDate" |
| | | onlyShowSection="TIME" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="record-log-msg"> |
| | | <div>ç¼éç´è¨ªéç¥ |
| | | <span v-if="item.email && item.phone">(ææ©ç°¡è¨ãEmail)</span> |
| | | <span v-else-if="item.email">(Email)</span> |
| | | <span v-else>(ææ©ç°¡è¨)</span> |
| | | </div> |
| | | <div v-if="item.phone" class="mt-10">é ç´{{item.interviewDate | formatDate}}</div> |
| | | </div> |
| | | </section> |
| | | <div class="time-line"></div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Component, Prop, Vue } from "nuxt-property-decorator"; |
| | | import { NoticeLogs } from "~/shared/models/appointment.model"; |
| | | |
| | | @Component |
| | | export default class RecordCard extends Vue { |
| | | @Prop() |
| | | noticeLogsList!: NoticeLogs[]; |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .record-log-component{ |
| | | display: flex; |
| | | flex-direction: column; |
| | | .record-log-card{ |
| | | display: flex; |
| | | .record-log-card-date-container{ |
| | | position:relative; |
| | | .record-log-card-date-container-circle{ |
| | | display: flex; |
| | | flex-direction: column; |
| | | width: 56px; |
| | | height: 56px; |
| | | border-radius: 50%; |
| | | border:1px solid $PRIMARY_BLACK; |
| | | justify-content: center; |
| | | align-items: center; |
| | | align-content: center; |
| | | background-color:#1B365D; |
| | | color: #fff; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .mt-4{ |
| | | margin-top: 4px; |
| | | } |
| | | .line-space{ |
| | | letter-spacing: 1px; |
| | | } |
| | | .line-height{ |
| | | line-height:1.2; |
| | | } |
| | | .time-line{ |
| | | border-left: 1px solid black; |
| | | height: 30px; |
| | | margin-left: 28px; |
| | | |
| | | } |
| | | .record-log-msg{ |
| | | margin-left: 13px; |
| | | margin-top: 10px; |
| | | } |
| | | </style> |
| | |
| | | <div class="pam-header__title--sub">é ç´æç幸ç¦å®è·è
</div> |
| | | </div> |
| | | <div class="pam-header__action-bar"> |
| | | <!-- <i class="icon-bell text--dark-blue cursor--pointer fix-chrome-click--issue" |
| | | @click="$router.push('/notification')"></i> --> |
| | | <i |
| | | v-if="isShowNotification" |
| | | class="icon-bell text--dark-blue cursor--pointer" |
| | | @click="$router.push('/notification')" |
| | | > |
| | | <span :class="{'newNotification': isNewNotification}"></span> |
| | | </i> |
| | | |
| | | <el-dropdown :class="{'is-open':isOpenDropdown}" |
| | | ref="dropdown" |
| | | trigger="click" |
| | | @command="routerNavigateTo"> |
| | | <i class="icon-avatar text--dark-blue cursor--pointer fix-chrome-click--issue" @click="isOpenDropdown =!isOpenDropdown" @blur="isOpenDropdown =false"></i> |
| | | <div |
| | | @click="isOpenDropdown =!isOpenDropdown" |
| | | @blur="isOpenDropdown =false"> |
| | | <template v-if="isAdminLogin"> |
| | | <UiAvatar |
| | | :size="30" |
| | | :agentNo="consultantId" |
| | | class="admin-avatar" |
| | | ></UiAvatar> |
| | | </template> |
| | | <template v-else> |
| | | <i class="icon-avatar text--dark-blue cursor--pointer fix-chrome-click--issue"></i> |
| | | </template> |
| | | </div> |
| | | <el-dropdown-menu |
| | | class="pam-header__dropdown"> |
| | | <template v-for="(item,index) in navBarList"> |
| | |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component } from 'vue-property-decorator'; |
| | | import { namespace } from 'nuxt-property-decorator'; |
| | | import { Action, namespace, State, Watch } from 'nuxt-property-decorator'; |
| | | import { Role } from '~/shared/models/enum/Role'; |
| | | import * as _ from 'lodash'; |
| | | import { NotificationList } from '~/shared/models/reviews.model'; |
| | | import { AppointmentLog } from '~/shared/models/appointment.model'; |
| | | |
| | | const roleStorage = namespace('localStorage'); |
| | | @Component |
| | | export default class NavBar extends Vue { |
| | | |
| | | @roleStorage.Getter idToken!: string | null; |
| | | @roleStorage.Getter currentRole!: string | null; |
| | | @roleStorage.Getter consultantId!: string | null; |
| | | @roleStorage.Getter |
| | | idToken!: string | null; |
| | | |
| | | @roleStorage.Mutation storageClear!: () => void; |
| | | @roleStorage.Getter |
| | | currentRole!: string | null; |
| | | |
| | | @roleStorage.Getter |
| | | consultantId!: string | null; |
| | | |
| | | @roleStorage.Mutation |
| | | storageClear!: () => void; |
| | | |
| | | @roleStorage.Getter |
| | | isAdminLogin!: boolean; |
| | | |
| | | @roleStorage.Getter |
| | | isUserLogin!: boolean; |
| | | |
| | | @Action |
| | | storeMyPersonalNotification!: () => void; |
| | | |
| | | @State |
| | | notificationList!: NotificationList[]; |
| | | |
| | | @Action |
| | | storeMyAppointmentReviewLog!: () => void; |
| | | |
| | | @State |
| | | unReviewLogList!: AppointmentLog[]; |
| | | |
| | | isOpenDropdown = false; |
| | | login_role = Role.NOT_LOGIN; |
| | | |
| | | navBarList = [{ |
| | | authorityOfRoleList: [Role.NOT_LOGIN], |
| | |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | @Watch('$route', {immediate: true}) |
| | | onRouterChange() { |
| | | if (this.currentRole) { |
| | | this.storeMyPersonalNotification(); |
| | | this.storeMyAppointmentReviewLog(); |
| | | } |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | routerNavigateTo(url: string): void { |
| | | (this.$refs.dropdown as any).hide(); |
| | | _.isEqual(url,'') |
| | |
| | | |
| | | get loginRole(): Role { |
| | | return this.idToken && this.currentRole ? (this.currentRole as Role): Role.NOT_LOGIN; |
| | | } |
| | | |
| | | get isShowNotification() { |
| | | if (this.isUserLogin) { |
| | | return this.notificationList.length || this.unReviewLogList.length; |
| | | } |
| | | if (this.isAdminLogin) { |
| | | return this.notificationList.length |
| | | } |
| | | } |
| | | |
| | | get isNewNotification() { |
| | | if (this.currentRole) { |
| | | return this.notificationList.filter(item => !item.readDate).length; |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | i { |
| | | padding: 0px 15px; |
| | | @extend .fix-chrome-click--issue; |
| | | |
| | | @media screen and (max-width: 352px) { |
| | | padding: 0px 10px; |
| | | } |
| | | } |
| | | |
| | | .admin-avatar { |
| | | margin: 0px 15px; |
| | | @media screen and (max-width: 352px) { |
| | | margin: 0px 10px; |
| | | } |
| | | } |
| | | |
| | | .icon-bell { |
| | | position: relative; |
| | | .newNotification { |
| | | position: absolute; |
| | | width: 10px; |
| | | height: 10px; |
| | | top: 0; |
| | | right: 15px; |
| | | border-radius: 50px; |
| | | background: $PRIMARY_RED; |
| | | } |
| | | } |
| | | } |
| | |
| | | height: $DESKTOP_NAV_BAR; |
| | | |
| | | .pam-header__logo { |
| | | width: 180px; |
| | | height: 100%; |
| | | width: 160px; |
| | | height: 70px; |
| | | margin: 0; |
| | | background-image: url('~/assets/images/logo.png'); |
| | | background-size: cover; |
| | | background-size: contain; |
| | | background-repeat: no-repeat; |
| | | background-position: center; |
| | | } |
| | |
| | | > |
| | | <UiAvatar |
| | | :size="200" |
| | | :fileName="item.img" |
| | | :agentNo="item.agentNo" |
| | | class="mx-auto" |
| | | @click.native="showAgentDetail(item.agentNo)" |
| | | ></UiAvatar> |
| | |
| | | <div v-else> |
| | | <el-rate |
| | | v-if="!hideReviews" |
| | | class="pam-quickFilter-rate" |
| | | class="pam-rate mt-30" |
| | | v-model="pickedItem.avgScore" |
| | | ></el-rate> |
| | | </div> |
| | |
| | | </section> |
| | | <div class="user-reviews-page"> |
| | | |
| | | <template v-if="myAppointmentReviewLogList.length"> |
| | | <template v-if="reviewLogList.length"> |
| | | <section class="user-reviews-content"> |
| | | <div |
| | | class="user-reviews-card" |
| | | v-for="(appointmentLog, index) in myAppointmentReviewLogList" |
| | | v-for="(appointmentLog, index) in reviewLogList" |
| | | :key="index"> |
| | | <div class="user-reviews-card-content" v-if="isUserLogin"> |
| | | æ¨å°<span class="mdTxt">{{ ` ${appointmentLog.agentName} ` }}</span>åäº <UiReviewScore :score="appointmentLog.score" /> è©å¹ï¼ |
| | |
| | | </template> |
| | | <script lang="ts"> |
| | | import { AppointmentLog } from '~/shared/models/appointment.model'; |
| | | import { Vue, Component, Prop } from 'nuxt-property-decorator'; |
| | | import { Vue, Component, Prop, Watch } from 'nuxt-property-decorator'; |
| | | import authService from '~/shared/services/auth.service'; |
| | | |
| | | @Component |
| | | export default class ReviewRecords extends Vue{ |
| | | |
| | | @Prop() |
| | | myAppointmentReviewLogList!: AppointmentLog[]; |
| | | reviewLogList!: AppointmentLog[]; |
| | | |
| | | isUserLogin = false; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | this.isUserLogin = authService.isUserLogin(); |
| | | } |
| | |
| | | size!: number; |
| | | |
| | | @Prop() |
| | | fileName!: string; |
| | | agentNo!: string; |
| | | |
| | | get imgSrc() { |
| | | return process.env.BASE_URL + '/consultant/avatar/' + this.fileName; |
| | | return process.env.BASE_URL + '/consultant/avatar/' + this.agentNo; |
| | | } |
| | | } |
| | | </script> |
| | |
| | | date!: Date | string; |
| | | |
| | | @Prop() |
| | | onlyShowSection!: 'DAY' | 'TIME'; |
| | | onlyShowSection!: 'YEAR' | 'DATE' | 'DAY' | 'TIME'; |
| | | |
| | | compareTarget!: Date; |
| | | displayValue = ''; |
| | |
| | | this.displayValue = isThisYear(compareTarget) ? thisYearDayLabel : `${compareTarget.getFullYear()}/${compareTarget.getMonth() + 1}/${compareTarget.getDate()}`; |
| | | } else if (this.onlyShowSection === 'TIME') { |
| | | this.displayValue = `${compareTarget.getHours()}:${minutes}`; |
| | | } else if (this.onlyShowSection === 'DATE') { |
| | | this.displayValue = isThisYear(compareTarget) |
| | | ? thisYearDayLabel |
| | | : `${compareTarget.getMonth() + 1}/${compareTarget.getDate()}`; |
| | | } else if (this.onlyShowSection === 'YEAR') { |
| | | this.displayValue = `${compareTarget.getFullYear()}`; |
| | | } |
| | | |
| | | if (this.onlyShowSection) return; |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <el-date-picker |
| | | class="pam-date cursor--pointer" |
| | | popper-class="pam-date-popper" |
| | | v-model="dateValue" |
| | | :clearable="false" |
| | | type="date" |
| | | :editable="false" |
| | | format="yyyy/MM/dd" |
| | | placeholder="é¸ææ¥æ" |
| | | prefix-icon="icon-down down-icon" |
| | | :picker-options="pickerOptions" |
| | | @change="changeDate" |
| | | > |
| | | </el-date-picker> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Component, Emit, Prop, PropSync, Vue, Watch } from "nuxt-property-decorator"; |
| | | |
| | | @Component |
| | | export default class UiDatePicker extends Vue { |
| | | dateValue: Date | string = ''; |
| | | |
| | | @Prop() |
| | | defaultValue!: string; |
| | | |
| | | @Prop({default: false}) |
| | | isPastDateDisabled!: boolean; |
| | | |
| | | @Prop({default: false}) |
| | | isFutureDateDisabled!: boolean; |
| | | |
| | | @Emit('changeDate') |
| | | changeDate() { |
| | | return this.dateValue; |
| | | } |
| | | |
| | | @Watch('defaultValue', {immediate: true}) |
| | | updateDefault() { |
| | | if (this.defaultValue) { |
| | | this.dateValue = new Date(this.defaultValue); |
| | | this.changeDate(); |
| | | } |
| | | } |
| | | |
| | | get pickerOptions() { |
| | | const date = new Date(); |
| | | const currentDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`; |
| | | |
| | | if (this.isPastDateDisabled) { |
| | | return { |
| | | disabledDate(time: Date) { |
| | | const pickedDate = `${time.getFullYear()}/${time.getMonth() + 1}/${time.getDate()}`; |
| | | return new Date(pickedDate).getTime() < new Date(currentDate).getTime(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (this.isFutureDateDisabled) { |
| | | return { |
| | | disabledDate(time: Date) { |
| | | const pickedDate = `${time.getFullYear()}/${time.getMonth() + 1}/${time.getDate()}`; |
| | | return new Date(pickedDate).getTime() > new Date(currentDate).getTime(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | | </script> |
| | |
| | | v-if="fieldDisplayDevice === 'ALL' |
| | | || fieldDisplayDevice === currentDevice"> |
| | | <div class="pam-field__label"> |
| | | <div class="pam-field__title"><i :class="fieldIcon"></i>{{ fieldLabel }}</div> |
| | | <div class="pam-field__title" :style="{ 'font-size': fieldLabelSize }"><i :class="fieldIcon"></i>{{ fieldLabel }}</div> |
| | | </div> |
| | | <p class="pam-field__content"> |
| | | <slot></slot> |
| | |
| | | content!: string; |
| | | |
| | | @Prop() |
| | | labelSize?: number; |
| | | |
| | | @Prop() |
| | | displayDevice!: 'MOBILE' | 'DESKTOP' | 'ALL'; |
| | | |
| | | currentDevice: 'MOBILE' | 'DESKTOP' = 'MOBILE'; |
| | |
| | | return this.displayDevice || 'ALL'; |
| | | } |
| | | |
| | | get fieldLabelSize(): string { |
| | | return (this.labelSize || 16) + 'px'; |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | |
| | | |
| | | <style lang="scss" scoped> |
| | | .pam-field { |
| | | display: flex; |
| | | display : flex; |
| | | flex-direction: column; |
| | | .pam-field__label { |
| | | display: flex; |
| | | align-items: center; |
| | | display : flex; |
| | | .pam-icon { |
| | | font-size: 12px; |
| | | } |
| | | .pam-field__title { |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | display: flex; |
| | | align-items: center; |
| | | display : flex; |
| | | font-weight: bold; |
| | | } |
| | | } |
| | | .pam-field__content { |
| | | display : flex; |
| | | padding-top: 10px; |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <el-select v-model="syncedCloseReason" class="pam-select" ref="select"> |
| | | <el-option |
| | | v-for="(item,index) in options" |
| | | :key="index" |
| | | :label="item.key" |
| | | :value="item.value"> |
| | | {{ item.key }} |
| | | </el-option> |
| | | </el-select> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, PropSync, Prop} from 'nuxt-property-decorator'; |
| | | |
| | | @Component |
| | | export default class UiSelect extends Vue { |
| | | |
| | | @PropSync('closeReason', { type: String, default: 'other' }) |
| | | syncedCloseReason!: string; |
| | | |
| | | @Prop({ type:Array , default:()=>[] }) |
| | | options!: object[]; |
| | | |
| | | } |
| | | </script> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <el-time-select |
| | | class="pam-time cursor--pointer" |
| | | popper-class="pam-time-popper" |
| | | v-model="timeValue" |
| | | :clearable="false" |
| | | :editable="false" |
| | | :picker-options="pickerOptions" |
| | | placeholder="鏿æé" |
| | | prefix-icon="icon-down down-icon" |
| | | value-format="timestamp" |
| | | @change="changeTime" |
| | | > |
| | | </el-time-select> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Component, Emit, Prop, Vue, Watch } from "nuxt-property-decorator"; |
| | | |
| | | @Component |
| | | export default class UiTimePicker extends Vue { |
| | | timeValue = ''; |
| | | |
| | | @Prop() |
| | | defaultValue!: string; |
| | | |
| | | @Prop({default: ''}) |
| | | changeDate!: Date | string; |
| | | |
| | | @Prop() |
| | | isPastDateDisabled!: boolean; |
| | | |
| | | @Prop() |
| | | isFutureDateDisabled!: boolean; |
| | | |
| | | /////////////////////////////////////////////////////////////////////// |
| | | |
| | | @Emit('changeTime') |
| | | changeTime() { |
| | | return this.timeValue; |
| | | } |
| | | |
| | | @Watch('defaultValue', {immediate: true}) |
| | | updateDefault() { |
| | | if (this.defaultValue) { |
| | | const defaultDate = new Date(this.defaultValue); |
| | | this.timeValue = this.formatTimeString(defaultDate); |
| | | this.changeTime(); |
| | | } |
| | | } |
| | | |
| | | /////////////////////////////////////////////////////////////////////// |
| | | |
| | | get pickerOptions() { |
| | | let minTime = ''; |
| | | let maxTime = ''; |
| | | const currentDate = new Date(); |
| | | |
| | | if (this.changeDate && this.isPickedToday(currentDate)) { |
| | | |
| | | if (this.isPastDateDisabled) { |
| | | minTime = this.formatTimeString(currentDate); |
| | | this.isPickedDisableTime(currentDate, minTime); |
| | | } |
| | | |
| | | if (this.isFutureDateDisabled) { |
| | | maxTime = this.formatTimeString(currentDate); |
| | | this.isPickedDisableTime(currentDate, maxTime); |
| | | } |
| | | |
| | | } |
| | | |
| | | return { |
| | | start: '09:00', |
| | | step: '00:15', |
| | | end: '21:00', |
| | | minTime: minTime, |
| | | maxTime: maxTime |
| | | } |
| | | } |
| | | |
| | | private isPickedDisableTime(currentDate: Date, minMaxTime: string) { |
| | | const currentTime = this.getTimeValue(currentDate, minMaxTime); |
| | | const pickedTime = this.getTimeValue(currentDate, this.timeValue); |
| | | if (this.isPastDateDisabled && pickedTime < currentTime) { |
| | | this.timeValue = ''; |
| | | this.changeTime(); |
| | | } |
| | | if (this.isFutureDateDisabled && currentTime < pickedTime) { |
| | | this.timeValue = ''; |
| | | this.changeTime(); |
| | | } |
| | | } |
| | | |
| | | private isPickedToday(currentDate: Date) { |
| | | const pickedDate = new Date(this.changeDate); |
| | | const today = this.formatDateString(currentDate); |
| | | const picked = this.formatDateString(pickedDate); |
| | | return today === picked; |
| | | } |
| | | |
| | | private getTimeValue(date: Date, time: string) { |
| | | const hour = time.split(':')[0]; |
| | | const minute = time.split(':')[1]; |
| | | return date.setHours(+hour, +minute); |
| | | } |
| | | |
| | | private formatDateString(date: Date) { |
| | | return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`; |
| | | } |
| | | |
| | | private formatTimeString(date: Date) { |
| | | const hours = date.getHours(); |
| | | const minutes = date.getMinutes(); |
| | | return `${hours < 10 ? '0' + hours : hours}:${minutes < 10 ? '0' + minutes : minutes}`; |
| | | } |
| | | } |
| | | </script> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <div> |
| | | |
| | | <el-upload |
| | | class="pam-avatar-uploader" |
| | | ref="upload" |
| | | action="#" |
| | | :auto-upload="false" |
| | | :on-change="handleAvatarUploaded" |
| | | :show-file-list="false" |
| | | accept="image/png, image/jpeg, image/jpg"> |
| | | <el-avatar |
| | | |
| | | :size="150" |
| | | :src="imgSrc" |
| | | class="pam-avatar cursor--pointer fix-chrome-click--issue" |
| | | ></el-avatar> |
| | | <div class="pam-avatar-uploader__action-label mt-10 cursor--pointer" >è¨å®ç¸ç</div> |
| | | </el-upload> |
| | | <div |
| | | v-if="showResetAvatarBtn" |
| | | class="pam-avatar-uploader__action-label text--center mt-10 cursor--pointer" |
| | | @click="resetAvatar"> |
| | | åæ¶ä¸å³ç¸ç |
| | | </div> |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop, PropSync } from 'nuxt-property-decorator'; |
| | | |
| | | import { MessageBox } from 'element-ui'; |
| | | import { MessageBoxData } from 'element-ui/types/message-box'; |
| | | import _ from 'lodash'; |
| | | |
| | | import myConsultantService from '~/shared/services/my-consultant.service'; |
| | | |
| | | @Component |
| | | export default class editConsultantAvatar extends Vue { |
| | | |
| | | @Prop({type:String, default:""}) |
| | | agentNo!:string; |
| | | |
| | | @PropSync('photoBase64',{type:String, default:""}) |
| | | syncPhotoBase64!:string; |
| | | |
| | | _imgSrc: string = ''; |
| | | imgSrc: string=''; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | if(this.agentNo) this.initConsultantAvatar() |
| | | } |
| | | |
| | | private initConsultantAvatar(): void { |
| | | myConsultantService.getConsultantAvatar(this.agentNo) |
| | | .then(base64=> |
| | | this.splitBase64WithCommon(base64) |
| | | ) |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | resetAvatar(): void { |
| | | this.imgSrc = this._imgSrc; |
| | | } |
| | | |
| | | handleAvatarUploaded(file:any): void { |
| | | const isFollowUploadRule =_.includes(file.raw.type,'image/'); |
| | | isFollowUploadRule ? this.getImgSrc(file) : this.showFileUploadErrorMsg() |
| | | } |
| | | |
| | | private getImgSrc(file:any):void{ |
| | | const blob = file.raw; |
| | | this.blobToBase64(blob).then(base64=>{ |
| | | this.splitBase64WithCommon(base64 as string); |
| | | }); |
| | | } |
| | | |
| | | private blobToBase64(blob:File):Promise<string | ArrayBuffer | null> { |
| | | return new Promise((resolve,reject) => { |
| | | const reader = new FileReader(); |
| | | reader.readAsDataURL(blob); |
| | | reader.onloadend = () => { |
| | | resolve(reader.result) |
| | | }; |
| | | }); |
| | | } |
| | | |
| | | private splitBase64WithCommon(base64: string): void { |
| | | const splitBase64= _.split(base64, ','); // çºäºæ data:image , base64 è§£æåé; |
| | | this.syncPhotoBase64 = splitBase64[1]; |
| | | // NOTE: å çºç®å以 agentNO åå¾ avatar æå¤±æï¼ |
| | | // æ
å 䏿¤å¤æ·ä¾é²ç¯ä¸é æé¡¯ç¤º'åæ¶æé'ççæ³ã [Tomas, 2022/1/3] |
| | | if (!this._imgSrc) { |
| | | this._imgSrc = base64; |
| | | } |
| | | this.imgSrc = base64; |
| | | } |
| | | |
| | | private showFileUploadErrorMsg():Promise<MessageBoxData>{ |
| | | return MessageBox({ |
| | | message:`<div class="message-header">ä¸å³æ ¼å¼æèª¤</div> |
| | | <div class="message-content">è«ä¸å³æ£ç¢ºåæª</div>`, |
| | | dangerouslyUseHTMLString: true, |
| | | showClose:false, |
| | | showConfirmButton:true, |
| | | confirmButtonText:'確èª', |
| | | customClass:'pam-message-box', |
| | | closeOnClickModal:false, |
| | | }); |
| | | } |
| | | |
| | | get showResetAvatarBtn(): boolean { |
| | | // NOTE: å çºç®å以 agentNO åå¾ avatar æå¤±æï¼ |
| | | // æ
å 䏿¤å¤æ·ä¾é²ç¯ä¸é æé¡¯ç¤º'åæ¶æé'ççæ³ã [Tomas, 2022/1/3] |
| | | if (!this._imgSrc && !this.imgSrc) return false; |
| | | return this._imgSrc !== this.imgSrc; |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | |
| | | </style> |
| | |
| | | background-color: rgba(#222222, 0.5); |
| | | width: 100%; |
| | | height: 100%; |
| | | z-index: 99; |
| | | z-index: 2500; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Prop, PropSync} from 'vue-property-decorator'; |
| | | import { Vue, Component, Prop, PropSync, Watch} from 'vue-property-decorator'; |
| | | import { OptionBtnDto } from '~/shared/models/optionBtnDto.model'; |
| | | |
| | | @Component |
| | |
| | | @Prop({type:String,default:''}) |
| | | nameOfOtherOption!:string; |
| | | |
| | | @Prop() |
| | | maxLength? : number; |
| | | |
| | | @Watch('syncMutiSelect') |
| | | onMutiSelectChange(): void { |
| | | if (!this.maxLength) return; |
| | | if (this.syncMutiSelect.length > this.maxLength) { |
| | | this.syncMutiSelect.shift(); |
| | | } |
| | | } |
| | | |
| | | isSelectOtherOption=false; |
| | | isSelectAll=false; |
| | | |
| | |
| | | |
| | | // format to {page}-banner or pam-no-banner tag |
| | | private routeFormatBannerClass(route: string): string { |
| | | const needBannerRoutes = ['recommendConsultant', 'quickFilter', 'myConsultantList-consultantList', 'myConsultantList-contactedList', 'myAppointmentList-appointmentList', 'myAppointmentList-contactedList', 'login']; |
| | | const needBannerRoutes = ['recommendConsultant', 'quickFilter', 'myConsultantList-consultantList', 'myConsultantList-contactedList', 'myAppointmentList-appointmentList', 'myAppointmentList-contactedList', 'login', 'notification']; |
| | | return _.includes(needBannerRoutes, route) ? route + '-banner' : 'pam-no-banner'; |
| | | }; |
| | | |
| | | private bannerText: FeatureBannerTitle= { |
| | | [FeaturePage.RECOMMEND_CONSULTANT]: ['輸å
¥åé¡åç', 'ä¾ç
§ä½ çéæ±æ¨è¦å´é¸é¡§å'], |
| | | [FeaturePage.QUICK_FILTER] : ['é»é¸ä¸æ¹é¸é
', 'å°æ¾ä½ çBEST Match'], |
| | | [FeaturePage.RECOMMEND_CONSULTANT]: ['輸å
¥åé¡åç', 'ä¾ç
§æ¨çéæ±æ¨è¦å´é¸é¡§å'], |
| | | [FeaturePage.QUICK_FILTER] : ['é»é¸ä¸æ¹é¸é
', 'å°æ¾æ¨çBEST Match'], |
| | | [FeaturePage.MY_CONSULTANT_LIST] : [], |
| | | [FeaturePage.QUESTIONNAIRE] : [], |
| | | } |
| | |
| | | } |
| | | |
| | | @include desktop { |
| | | .pam-banner { |
| | | height: 150px; |
| | | } |
| | | .mt-navBar { |
| | | margin-top: calc($DESKTOP_NAV_BAR + $MOB_NAV_BAR); |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | .notification { |
| | | &-banner { |
| | | background-image: url('~/assets/images/notification/banner_mob.svg'); |
| | | } |
| | | |
| | | @include desktop { |
| | | &-banner { |
| | | background-image: url('~/assets/images/notification/banner_web.svg'); |
| | | } |
| | | } |
| | | } |
| | | |
| | | </style> |
File was renamed from PAMapp/middleware/errorRouteMiddleware.ts |
| | |
| | | import { Middleware } from '@nuxt/types'; |
| | | |
| | | const errorRouteMiddleware: Middleware = (context) => { |
| | | const errorRoute: Middleware = (context) => { |
| | | |
| | | if (!context.route.name) { |
| | | const isAdminLogin = context.store.getters['localStorage/isAdminLogin']; |
| | | if (isAdminLogin) { |
| | |
| | | } |
| | | } |
| | | |
| | | export default errorRouteMiddleware |
| | | export default errorRoute; |
¤ñ¹ï·sÀÉ®× |
| | |
| | | import { Middleware } from '@nuxt/types'; |
| | | |
| | | const getUrlQuery: Middleware = (context) => { |
| | | const currentRouteName = context.route.name; |
| | | const satisfactionIdFromMsg = context.route.query.appointmentId; |
| | | const queryNotContactAppointmentId = context.route.query.notContactAppointmentId; |
| | | const isUserLogin = context.store.getters['localStorage/isUserLogin']; |
| | | |
| | | if (currentRouteName === 'index' && queryNotContactAppointmentId) { |
| | | context.store.commit('localStorage/storageNotContactAppointmentIdFromMsg', queryNotContactAppointmentId); |
| | | if (!isUserLogin) { |
| | | context.redirect('/login'); |
| | | } |
| | | } |
| | | |
| | | if (currentRouteName === 'index' && satisfactionIdFromMsg) { |
| | | context.store.commit('localStorage/storageSatisfactionIdFromMsg', satisfactionIdFromMsg); |
| | | if (!isUserLogin) { |
| | | context.redirect('/login'); |
| | | } |
| | | } |
| | | |
| | | |
| | | } |
| | | |
| | | export default getUrlQuery |
| | |
| | | '~/plugins/vue-scroll-picker', |
| | | '~/plugins/filters/date.filter.ts', |
| | | '~/plugins/filters/age.filter.ts', |
| | | '~/plugins/filters/appointment-fail-reason.filter.ts', |
| | | ], |
| | | |
| | | // Auto import components: https://go.nuxtjs.dev/config-components |
| | |
| | | }, |
| | | router: { |
| | | base: process.env.ENV === 'uat' ? '/pam/' : '', |
| | | middleware: 'errorRouteMiddleware' |
| | | middleware: ['getUrlQuery', 'errorRoute'] |
| | | } |
| | | } |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <PopUpFrame :isOpen.sync="updateDone"> |
| | | <div class="text--center mdTxt fs-18"> |
| | | <p class="mt-20 text--center ">帳èè³è¨æ´æ°æå</p> |
| | | <el-button |
| | | type="primary" |
| | | @click="updateDone = false" |
| | | class="mt-20" |
| | | >æç¥éäº</el-button> |
| | | </div> |
| | | </PopUpFrame> |
| | | |
| | | <div class="pam-paragraph account-confirm"> |
| | | <el-button :disabled="isSubmitBtnDisabled" |
| | | @click.native="updateAccountSetting"> |
| | |
| | | userPhoneDisabled = true; |
| | | userEmailDisabled = true ; |
| | | onEditMode = false; |
| | | updateDone = false; |
| | | userNameValue = ''; |
| | | phoneValue = '' ; |
| | | emailValue = '' ; |
| | |
| | | accountSettingService.updateAccountSetting(editSettingInfo).then((res: any) => { |
| | | console.log('updateRes:', res); |
| | | this.resetSettingForm(); |
| | | this.updateDone = true; |
| | | |
| | | }); |
| | | } |
| | | |
| | |
| | | <el-row |
| | | type="flex" |
| | | justify="center"> |
| | | <UiAvatar :size="150" :fileName="agentInfo.img"></UiAvatar> |
| | | <UiAvatar |
| | | :size="150" |
| | | :agentNo="agentInfo.agentNo"> |
| | | </UiAvatar> |
| | | </el-row> |
| | | |
| | | <el-row |
| | |
| | | class="pt-10" |
| | | justify="center"> |
| | | <h3 class="mdTxt">{{ agentName }}</h3> |
| | | </el-row> |
| | | |
| | | <el-row |
| | | v-if="currentRole === role.ADMIN" |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <UiField :span="12" icon="phone" label="ææ©è碼"> |
| | | {{ agentInfo.phoneNumber }} |
| | | </UiField> |
| | | </el-row> |
| | | |
| | | <el-row |
| | |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <div class="consultant-edit-btn"> |
| | | <UiField icon="flag" label="æºéé¢¨æ ¼"> |
| | | <div class="text--orange bold pr-10 " |
| | | v-for="(communicationStyle, index) in displayCommunicationStyleList" |
| | | :key="index"> |
| | | #{{ communicationStyle }}</div> |
| | | </UiField> |
| | | </div> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <UiField icon="comment" label="å人ç念"> |
| | | <UiField icon="comment" label="å人ç念" class="agent-info-textarea"> |
| | | {{ agentInfo.concept }} |
| | | </UiField> |
| | | </el-row> |
| | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <UiField icon="school" label="åäººèæ¯"> |
| | | <span v-for="(experience, index) in agentInfo.experiences" :key="index"> |
| | | {{ experience }}<span v-if="index !== agentInfo.experiences.length - 1">, </span> |
| | | <UiField icon="school" label="åäººèæ¯" class="agent-info-textarea"> |
| | | <span> |
| | | {{ agentInfo.experiences }} |
| | | </span> |
| | | </UiField> |
| | | </el-row> |
| | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <UiField icon="trophy" label="å¾çç¶æ·"> |
| | | <UiField icon="trophy" label="å¾çç¶æ·" class="agent-info-textarea"> |
| | | {{ agentInfo.awards }} |
| | | </UiField> |
| | | </el-row> |
| | |
| | | </div> |
| | | </div> |
| | | </PopUpFrame> |
| | | <div class="consultant-edit-btn" v-if="currentRole === role.ADMIN"> |
| | | <el-button type="primary" @click.native="$router.push(`/agentInfo/edit/${agentInfo.agentNo}`)">編輯帳æ¶è³è¨</el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | agentInfo!: AgentInfo; |
| | | role = Role; |
| | | isAlertAddSuccess = false; |
| | | isAlertFieldInfo = false; |
| | | fieldInfoTitle = ''; |
| | | fieldInfoDesc = ''; |
| | | hideReviews = hideReviews ; |
| | | isAlertFieldInfo = false; |
| | | fieldInfoTitle = ''; |
| | | fieldInfoDesc = ''; |
| | | hideReviews = hideReviews ; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | async asyncData(context: Context) { |
| | | const agentNo = context.route.params.agentNo; |
| | | return { |
| | |
| | | } |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | alertAddSuccess(): void { |
| | | this.isAlertAddSuccess = true; |
| | | } |
| | | |
| | | alertFieldInfo(field: string): void { |
| | | this.isAlertFieldInfo = true; |
| | | switch(field) { |
| | |
| | | } |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | get agentName(): string { |
| | | return `${this.agentInfo.name}(${this.agentInfo.role})`; |
| | | } |
| | | |
| | | |
| | | get displayCommunicationStyleList(): string[] { |
| | | return this.agentInfo.communicationStyle.split('ã').filter((item) => item); |
| | | } |
| | | |
| | | } |
| | | |
| | | </script> |
| | |
| | | flex-wrap: wrap; |
| | | line-height: 24px; |
| | | } |
| | | .consultant-edit-btn{ |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .pam-field{ |
| | | display: flex; |
| | | } |
| | | .agent-info-textarea{ |
| | | word-break: break-all; |
| | | word-wrap: break-word; |
| | | } |
| | | </style> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <div class="edit-agent-info-page"> |
| | | <el-row |
| | | type="flex" |
| | | justify="center"> |
| | | <EditConsultantAvatar |
| | | :agentNo="agentInfo.agentNo" |
| | | :photoBase64.sync="editInfoValue.photoBase64"/> |
| | | </el-row> |
| | | <el-row |
| | | type="flex" |
| | | class="pt-10" |
| | | justify="center" |
| | | align="middle" v-if="!hideReviews"> |
| | | <!-- TODO:é±è滿æåº¦ --> |
| | | <i class="pam-icon icon--primary icon-star"></i> |
| | | <h3 class="mdTxt">{{ agentInfo.avgScore }}</h3> |
| | | </el-row> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph" |
| | | justify="center"> |
| | | <el-input class="mdTxt" v-model="editInfoValue.name"></el-input> |
| | | </el-row> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <el-col :span="24" class="pam-field"> |
| | | <div class="pam-field__label pam-progress__label"> |
| | | <div> |
| | | <div class="pam-field__title mb-10"> |
| | | <i class="pam-icon icon-phone" |
| | | ></i>ææ©è碼 |
| | | <span class="hint text--bold" v-show="!phoneValid">ææ©èç¢¼æ ¼å¼æèª¤</span> |
| | | <span class="hint text--bold" v-show="editInfoValue.phoneNumber.length === 0">ææ©è碼çºå¿
å¡«</span> |
| | | </div> |
| | | </div> |
| | | <el-input |
| | | v-model="editInfoValue.phoneNumber" |
| | | :class="{'is-invalid': !phoneValid}" |
| | | maxlength="10" |
| | | minlength="10"></el-input> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <UiField :span="12" icon="agent" label="é é"> |
| | | <el-input v-model="editInfoValue.title"></el-input> |
| | | </UiField> |
| | | </el-row> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <UiField icon="company" label="æåå°å"> |
| | | <el-input v-model="editInfoValue.serveArea"></el-input> |
| | | </UiField> |
| | | </el-row> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <UiField icon="address" label="å
¬å¸å°å"> |
| | | <el-input v-model="editInfoValue.companyAddress"></el-input> |
| | | </UiField> |
| | | </el-row> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <UiField :span="12" icon="time" label="æå¾ä¸ç·æé"> |
| | | {{ agentInfo.latestLoginTime | formatDate }} |
| | | </UiField> |
| | | <UiField :span="12" icon="calender" label="æåè³æ·" style="display:flex"> |
| | | <el-input v-model="editInfoValue.seniorityYear" class="seniority-input"></el-input>å¹´ |
| | | <el-select style="width:60px" v-model="editInfoValue.seniorityMonth" class="seniority-input"> |
| | | <el-option |
| | | v-for="(month) in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" |
| | | :value="month" |
| | | :key="month"> |
| | | {{ month }} |
| | | </el-option> |
| | | </el-select>æ |
| | | </UiField> |
| | | </el-row> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <el-col :span="24" class="pam-field"> |
| | | <div class="pam-field__label pam-progress__label"> |
| | | <div> |
| | | <div class="pam-field__title"> |
| | | <i class="pam-icon icon-thumbs-up" |
| | | ></i>è«®è©¢åº¦è¡¨ç¾ <i class="pl-5 text--primary icon-information" @click="alertFieldInfo('evaluation')"></i> |
| | | </div> |
| | | </div> |
| | | <div class="xsTxt"> |
| | | {{ agentInfo.evaluation }}/50 (è¿ä¸åæ/ç´¯è¨) |
| | | </div> |
| | | </div> |
| | | <div class="pam-field__content pam-field-evaluation pt-10"> |
| | | <el-progress :show-text="false" :stroke-width="15" :percentage="agentInfo.evaluation * 2"></el-progress> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <el-col :span="24" class="pam-field"> |
| | | <div class="pam-field__label pam-progress__label"> |
| | | <div> |
| | | <div class="pam-field__title"> |
| | | <i class="pam-icon icon-flag" |
| | | ></i>æºéé¢¨æ ¼ <span class="hint text--bold">(å¯è¤é¸,æå¤2é
)</span> |
| | | </div> |
| | | </div> |
| | | <MultiSelectBtn class="mt-30" |
| | | :mutiSelect.sync="editInfoValue.communicationStyle" |
| | | :options="agentCommunicationStyleList" |
| | | :maxLength="2" |
| | | > |
| | | </MultiSelectBtn> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <el-col :span="24" class="pam-field"> |
| | | <div class="pam-field__label pam-progress__label"> |
| | | <div> |
| | | <div class="pam-field__title"> |
| | | <i class="pam-icon icon-flag" |
| | | ></i>å°é·é å <span class="hint text--bold">(å¯è¤é¸)</span> |
| | | </div> |
| | | </div> |
| | | <MultiSelectBtn class="mt-30" |
| | | :mutiSelect.sync="editInfoValue.expertise" |
| | | :options="agentExpertList" |
| | | > |
| | | </MultiSelectBtn> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <UiField icon="comment" label="å人ç念"> |
| | | <el-input type="textarea" autosize v-model="editInfoValue.concept"></el-input> |
| | | </UiField> |
| | | </el-row> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <UiField icon="school" label="åäººèæ¯"> |
| | | <el-input type="textarea" autosize v-model="editInfoValue.experiences"></el-input> |
| | | </UiField> |
| | | </el-row> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <UiField icon="trophy" label="å¾çç¶æ·"> |
| | | <el-input type="textarea" autosize style="height:70%" v-model="editInfoValue.awards"></el-input> |
| | | </UiField> |
| | | </el-row> |
| | | |
| | | <PopUpFrame |
| | | :isOpen.sync="isAlertFieldInfo" |
| | | > |
| | | <div class="text--center mdTxt fs-18"> |
| | | <p>{{ fieldInfoTitle }}</p> |
| | | <p class="mt-20 text--left text--regular">{{ fieldInfoDesc }}</p> |
| | | <div class="text--center mt-30"> |
| | | <el-button |
| | | type="primary" |
| | | @click="isAlertFieldInfo = false" |
| | | >æç¥éäº</el-button> |
| | | </div> |
| | | </div> |
| | | </PopUpFrame> |
| | | <PopUpFrame :isOpen.sync="isInfoUpdate"> |
| | | <div class="text--center mdTxt fs-18"> |
| | | <p class="mt-20 text--center ">帳èè³è¨æ´æ°æå</p> |
| | | <el-button |
| | | type="primary" |
| | | @click="backToInfo" |
| | | class="mt-20" |
| | | >æç¥éäº</el-button> |
| | | </div> |
| | | </PopUpFrame> |
| | | <div class="pam-paragraph account-confirm"> |
| | | <el-button :disabled="isSubmitBtnDisabled" |
| | | @click.native="editAgentInfoSetting"> |
| | | éåº |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Context } from '@nuxt/types'; |
| | | import { namespace } from 'nuxt-property-decorator'; |
| | | import { Vue, Component, Prop } from 'vue-property-decorator'; |
| | | import * as _ from "lodash"; |
| | | |
| | | import myConsultantService from '~/shared/services/my-consultant.service'; |
| | | import accountSettingService from '~/shared/services/account-setting.service'; |
| | | |
| | | import { AgentInfo } from '~/shared/models/agent-info.model'; |
| | | import { hideReviews } from '~/shared/const/hide-reviews'; |
| | | import { AgentInfoSetting } from '~/shared/models/account.model'; |
| | | import { Role } from '~/shared/models/enum/Role'; |
| | | import { agentCommunicationStyleList } from '~/shared/const/agent-communication-style-list'; |
| | | |
| | | const localStorageTest = namespace('localStorage'); |
| | | |
| | | @Component |
| | | export default class AgentInfoComponent extends Vue { |
| | | |
| | | @localStorageTest.State('current_role') |
| | | currentRole!:string | null; |
| | | |
| | | _agentInfoSetting!: AgentInfoSetting; |
| | | agentInfo! : AgentInfo |
| | | fieldInfoDesc : string = ''; |
| | | fieldInfoTitle : string = ''; |
| | | hideReviews : boolean = hideReviews ; |
| | | isAlertFieldInfo: boolean = false; |
| | | isInfoUpdate : boolean = false; |
| | | |
| | | editInfoValue = { |
| | | agentNo : '', |
| | | name : '', |
| | | expertise : [] as string[], |
| | | title : '', |
| | | serveArea : '', |
| | | companyAddress : '', |
| | | seniorityYear : 1, |
| | | seniorityMonth : 0, |
| | | concept : '', |
| | | experiences : '', |
| | | awards : '', |
| | | communicationStyle: [] as string[], |
| | | photoBase64 : '', |
| | | phoneNumber : '' |
| | | }; |
| | | |
| | | communicationStyleList: string[] = agentCommunicationStyleList; |
| | | role = Role; |
| | | |
| | | agentExpertList = [ |
| | | { |
| | | title:'å¥åº·èä¿é', |
| | | label:'å¥åº·èä¿é' |
| | | }, |
| | | { |
| | | title:'å女æè²', |
| | | label:'å女æè²' |
| | | }, |
| | | { |
| | | title:'è³ç¢è¦å', |
| | | label:'è³ç¢è¦å' |
| | | }, |
| | | { |
| | | title:'æ¨æ´»éä¼', |
| | | label:'æ¨æ´»éä¼' |
| | | }, |
| | | { |
| | | title:'ä¿å®å¥æª¢/è¦å', |
| | | label:'ä¿å®å¥æª¢/è¦å' |
| | | }, |
| | | { |
| | | title:'åç´
ä¿å®', |
| | | label:'åç´
ä¿å®' |
| | | }]; |
| | | |
| | | agentCommunicationStyleList = [ |
| | | { |
| | | title:'謹æ
å實', |
| | | label:'謹æ
å實' |
| | | }, |
| | | { |
| | | title:'æå¿«ä¸»å', |
| | | label:'æå¿«ä¸»å' |
| | | }, |
| | | { |
| | | title:'èå¿å¾è½', |
| | | label:'èå¿å¾è½' |
| | | }, |
| | | { |
| | | title:'å¥è«é¢¨è¶£', |
| | | label:'å¥è«é¢¨è¶£' |
| | | }]; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | async asyncData(context: Context) { |
| | | const agentNo = context.route.params.agentNo; |
| | | return { |
| | | agentInfo: await myConsultantService.getConsultantDetail(agentNo).then((res) => res) |
| | | } |
| | | } |
| | | |
| | | mounted(){ |
| | | this.setAgentInfo(this.agentInfo); |
| | | } |
| | | |
| | | private setAgentInfo(agentInfo: AgentInfo): void { |
| | | const [agentYear, _yearUnit , agentMonth, _monthUnit] = agentInfo.seniority.split(" "); |
| | | this._agentInfoSetting = { |
| | | agentNo : agentInfo.agentNo||'', |
| | | name : agentInfo.name || '', |
| | | expertise : agentInfo.expertise || [], |
| | | title : agentInfo.title || '', |
| | | role : agentInfo.role||'', |
| | | serveArea : agentInfo.serveArea || '', |
| | | gender : agentInfo.gender||'', |
| | | phoneNumber : agentInfo.phoneNumber||'', |
| | | companyAddress : agentInfo.companyAddress || '', |
| | | seniorityYear : agentYear? +agentYear : 0, |
| | | seniorityMonth : agentMonth ? +agentMonth : 0, |
| | | concept : agentInfo.concept || '', |
| | | experiences : agentInfo.experiences || '', |
| | | awards : agentInfo.awards || '', |
| | | communicationStyle: agentInfo.communicationStyle || '', |
| | | photoBase64 : '' |
| | | }; |
| | | |
| | | this.editInfoValue = { |
| | | ...this._agentInfoSetting, |
| | | expertise : _.cloneDeep(this._agentInfoSetting.expertise), |
| | | // TODO: 確èªå¾ç«¯æ¤æ¬ä½å¾ç«¯ææ¹çºä»¥" , "éé [Tomas, 2021/12/28] |
| | | communicationStyle : this._agentInfoSetting.communicationStyle.split('ã'), |
| | | } |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | editAgentInfoSetting(): void { |
| | | const editSettingInfo: any = { |
| | | ...this.editInfoValue, |
| | | communicationStyle: this.editInfoValue.communicationStyle.join('ã'), |
| | | } |
| | | accountSettingService.editAgentInfoSetting(editSettingInfo).then((res: AgentInfoSetting) => { |
| | | this.isInfoUpdate = true; |
| | | }); |
| | | } |
| | | |
| | | backToInfo() { |
| | | this.isInfoUpdate = false |
| | | this.$router.push(`/agentInfo/${this.agentInfo.agentNo}`); |
| | | } |
| | | |
| | | selectCommunicationStyles(): void { |
| | | if (this.editInfoValue.communicationStyle.length > 2) { |
| | | this.editInfoValue.communicationStyle.shift(); |
| | | } |
| | | } |
| | | |
| | | alertFieldInfo(field: string): void { |
| | | this.isAlertFieldInfo = true; |
| | | switch(field) { |
| | | case 'suitability': |
| | | this.fieldInfoTitle = 'å¹é
度'; |
| | | this.fieldInfoDesc = 'å¹é
度æ¯ééå´é¸é
å°æå¿«é篩é¸å¾ï¼å°æ¯ä¸ä½ä¿éªé¡§åè³æé²è¡æ¯å°å¾æåºæ¨è¦çµ¦æ¨çåªåæ¸å¼ï¼æ¨å¯ä»¥ä½çºé¸æé©åé¡§åçåèå¼ã'; |
| | | break; |
| | | case 'evaluation': |
| | | this.fieldInfoTitle = '諮詢度表ç¾'; |
| | | this.fieldInfoDesc = 'è«®è©¢åº¦è¡¨ç¾æ¯å°æ¯ä¸ä½ä¿éªé¡§åè¿ä¸åæåè¦è«®è©¢æ¸éé²è¡æ¯å°å¾æåºæ¨è¦çµ¦æ¨çåªåæ¸å¼ã'; |
| | | break; |
| | | } |
| | | } |
| | | //////////////////////////////////////////////////////////// |
| | | get phoneValid(): boolean { |
| | | const rule = /^09[0-9]{8}$/; |
| | | return this.editInfoValue.phoneNumber |
| | | ? rule.test(this.editInfoValue.phoneNumber) && _.isEqual(this.editInfoValue.phoneNumber.length,10) |
| | | : true; |
| | | } |
| | | |
| | | get isSubmitBtnDisabled(): boolean { |
| | | const isFormValid = this.editInfoValue.name |
| | | && this.editInfoValue.title |
| | | && this.editInfoValue.companyAddress |
| | | && this.editInfoValue.serveArea |
| | | && this.editInfoValue.concept |
| | | && this.editInfoValue.experiences |
| | | && this.editInfoValue.phoneNumber.length |
| | | && this.editInfoValue.seniorityYear |
| | | && this.editInfoValue.expertise.length |
| | | && this.editInfoValue.communicationStyle.length; |
| | | return !isFormValid |
| | | } |
| | | } |
| | | |
| | | </script> |
| | | |
| | | <style lang="scss" > |
| | | .edit-agent-info-page{ |
| | | .el-textarea__inner{ |
| | | font-size: 15px; |
| | | } |
| | | } |
| | | .pam-icon { |
| | | font-size : 15px; |
| | | padding-right: 8px; |
| | | color : $PRUDENTIAL_GREY; |
| | | &.icon--primary { |
| | | color: $PRIMARY_RED; |
| | | } |
| | | } |
| | | .pam-field { |
| | | display: flex; |
| | | flex-direction: column; |
| | | .pam-field__label { |
| | | display: flex; |
| | | align-items: center; |
| | | .pam-icon { |
| | | font-size: 12px; |
| | | } |
| | | .pam-field__title { |
| | | font-size : 16px; |
| | | font-weight: bold; |
| | | display : flex; |
| | | align-items: center; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .pam-field-suitability { |
| | | .el-progress-bar__inner { |
| | | background-color: $LIGHT_BLUE !important; |
| | | } |
| | | } |
| | | |
| | | .pam-field-evaluation { |
| | | .el-progress-bar__inner { |
| | | background-color: $TEAL_GREEN!important; |
| | | } |
| | | } |
| | | |
| | | .pam-field-experts { |
| | | display : flex; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .pam-progress__label { |
| | | justify-content: space-between; |
| | | flex-wrap : wrap; |
| | | line-height: 24px; |
| | | } |
| | | .account-confirm{ |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .seniority-input{ |
| | | width : 50px; |
| | | margin-right: 5px; |
| | | } |
| | | .el-input--suffix .el-input__inner { |
| | | padding-right: 20px; |
| | | } |
| | | |
| | | .el-textarea__inner{ |
| | | font-size: 18px; |
| | | } |
| | | </style> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <div class="appointment-client-detail-close-page"> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <UiField label="æ¸æªæ¹å¼" :labelSize="20"> |
| | | <SingleSelectBtn class="mt-10" |
| | | :singleSelected.sync="appointmentCloseInfo.selectCloseOption" |
| | | :options="closeOptions" /> |
| | | </UiField> |
| | | </el-row> |
| | | |
| | | <template v-if="appointmentCloseInfo.selectCloseOption === contactStatus.DONE"> |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph" style="flex-direction: column"> |
| | | <UiField label="身åèåè/å±
çèåè" :labelSize="20" class="required"> |
| | | <input |
| | | class="appointment-client-detail-close__input" |
| | | :class="{'is-invalid':!identityIdValid}" |
| | | v-model="appointmentCloseInfo.policyholderIdentityId" |
| | | placeholder="è«è¼¸å
¥" |
| | | type="text"> |
| | | </UiField> |
| | | <div class="error mt-5 mb-5" v-show="!identityIdValid"> |
| | | <span>身åèåèæ ¼å¼æèª¤</span> |
| | | </div> |
| | | </el-row> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <UiField label="åå代碼Plan Code" :labelSize="20" class="required"> |
| | | <input |
| | | class="appointment-client-detail-close__input" |
| | | v-model="appointmentCloseInfo.planCode" |
| | | placeholder="è«è¼¸å
¥" |
| | | type="text"> |
| | | </UiField> |
| | | </el-row> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <UiField label="é²ä»¶æé" :labelSize="20" class="required"> |
| | | <DateTimePicker |
| | | :defaultValue="appointmentCloseInfo.policyEntryDate" |
| | | @changeDateTime="appointmentCloseDate = $event"></DateTimePicker> |
| | | </UiField> |
| | | </el-row> |
| | | </template> |
| | | |
| | | <template v-if="appointmentCloseInfo.selectCloseOption === contactStatus.CLOSE"> |
| | | <el-row |
| | | class="pam-paragraph"> |
| | | <UiField label="æªæäº¤åå " :labelSize="20" class="required"> |
| | | <UiSelect :closeReason.sync="appointmentCloseInfo.closedReason" |
| | | :options="appointmentFailReason"/> |
| | | </UiField> |
| | | <input |
| | | v-if="appointmentCloseInfo.closedReason === 'other' |
| | | || appointmentCloseInfo.closedReason === 'no_suitable_commodity'" |
| | | class="appointment-client-detail-close__input mt-10" |
| | | v-model="appointmentCloseInfo.closedOtherReason" |
| | | placeholder="è«è¼¸å
¥åå ï¼é50åã" |
| | | type="text"> |
| | | </el-row> |
| | | </template> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="pam-paragraph"> |
| | | <UiField label="å註" :labelSize="20"> |
| | | <el-input |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="è«è¼¸å
¥" |
| | | v-model="appointmentCloseInfo.remark" |
| | | resize="none"> |
| | | </el-input> |
| | | </UiField> |
| | | </el-row> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | justify="center" |
| | | class="pam-paragraph"> |
| | | <el-button @click="$router.go(-1)">åæ¶</el-button> |
| | | <el-button @click="closeAppointment" :disabled="isSubmitBtnDisabled">確èª</el-button> |
| | | </el-row> |
| | | |
| | | <PopUpFrame :isOpen.sync="isShowSuccessAlert"> |
| | | <div class="text--middle invite-review"> |
| | | <div class="mb-30 mt-10">çµæ¡æå</div> |
| | | <el-button type="primary" @click="closeAlert">確å®</el-button> |
| | | </div> |
| | | </PopUpFrame> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | |
| | | import { namespace } from 'nuxt-property-decorator'; |
| | | import { Vue, Component } from 'vue-property-decorator'; |
| | | import { Appointment, ToCloseAppointment, ToDoneAppointment } from '~/shared/models/appointment.model'; |
| | | |
| | | import appointmentService from '~/shared/services/appointment.service'; |
| | | import { appointmentFailReasonList } from '~/shared/const/appointment-fail-reason-list'; |
| | | import { ContactStatus } from '~/shared/models/enum/contact-status'; |
| | | |
| | | const appointmentStore = namespace('appointment.store'); |
| | | |
| | | @Component |
| | | export default class AppointmentDetailCloseComponent extends Vue { |
| | | |
| | | @appointmentStore.Action |
| | | updateAppointmentDetail!: (appointmentId: number) => Appointment; |
| | | |
| | | @appointmentStore.State('appointmentDetail') |
| | | appointmentDetail!: Appointment; |
| | | |
| | | contactStatus = ContactStatus; |
| | | |
| | | appointmentCloseDate = ''; |
| | | isShowSuccessAlert = false; |
| | | |
| | | appointmentCloseInfo = { |
| | | closedOtherReason : '', |
| | | closedReason : 'other', |
| | | planCode : '', |
| | | policyEntryDate : '', |
| | | policyholderIdentityId: '', |
| | | remark : '', |
| | | selectCloseOption : this.contactStatus.DONE, |
| | | }; |
| | | |
| | | closeOptions = [ |
| | | { |
| | | title:'æäº¤', |
| | | label: this.contactStatus.DONE, |
| | | }, |
| | | { |
| | | title:'æªæäº¤', |
| | | label: this.contactStatus.CLOSE, |
| | | } |
| | | ]; |
| | | |
| | | appointmentFailReason = appointmentFailReasonList; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | const appointmentId = +this.$route.params.appointmentId; |
| | | const closedInfo = this.appointmentDetail.appointmentClosedInfo; |
| | | if (this.appointmentDetail.id === appointmentId |
| | | && (this.appointmentDetail.communicateStatus === this.contactStatus.DONE |
| | | || this.appointmentDetail.communicateStatus === this.contactStatus.CLOSE |
| | | || this.appointmentDetail.communicateStatus === this.contactStatus.CANCEL) |
| | | ) { |
| | | this.appointmentCloseInfo = { |
| | | closedOtherReason : closedInfo?.closedOtherReason, |
| | | closedReason : closedInfo?.closedReason, |
| | | planCode : closedInfo?.planCode, |
| | | policyEntryDate : closedInfo?.policyEntryDate, |
| | | policyholderIdentityId: closedInfo?.policyholderIdentityId, |
| | | remark : closedInfo?.remark, |
| | | selectCloseOption : this.appointmentDetail.communicateStatus === this.contactStatus.DONE |
| | | ? this.contactStatus.DONE |
| | | : this.contactStatus.CLOSE |
| | | }; |
| | | this.appointmentCloseDate = closedInfo?.policyEntryDate; |
| | | } |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | closeAppointment(): void { |
| | | const appointmentId = +this.$route.params.appointmentId; |
| | | if (this.appointmentCloseInfo.selectCloseOption === this.contactStatus.DONE) { |
| | | const toDoneAppointment: ToDoneAppointment = { |
| | | appointmentId : appointmentId, |
| | | contactStatus : this.contactStatus.DONE, |
| | | planCode : this.appointmentCloseInfo.planCode, |
| | | policyEntryDate : this.appointmentCloseDate, |
| | | policyholderIdentityId: this.appointmentCloseInfo.policyholderIdentityId, |
| | | remark : this.appointmentCloseInfo.remark, |
| | | } |
| | | appointmentService.closeAppointment(toDoneAppointment).then((_) => this.updateAppointmentDetail(appointmentId)); |
| | | this.isShowSuccessAlert = true; |
| | | } else { |
| | | const toCloseAppointment: ToCloseAppointment = { |
| | | appointmentId : appointmentId, |
| | | closedOtherReason: this.appointmentCloseInfo.closedOtherReason, |
| | | closedReason : this.appointmentCloseInfo.closedReason, |
| | | contactStatus : this.contactStatus.CLOSE, |
| | | remark : this.appointmentCloseInfo.remark, |
| | | } |
| | | appointmentService.closeAppointment(toCloseAppointment).then((_) => { |
| | | this.updateAppointmentDetail(appointmentId); |
| | | this.isShowSuccessAlert = true; |
| | | }); |
| | | |
| | | } |
| | | } |
| | | |
| | | closeAlert(){ |
| | | this.isShowSuccessAlert = false ; |
| | | this.$router.push(`/myAppointmentList/contactedList`); |
| | | } |
| | | |
| | | checkIdentityId (id) { |
| | | const tab = "ABCDEFGHJKLMNPQRSTUVXYWZIO"; |
| | | const A1 = [ 1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3 ]; |
| | | const A2 = [ 0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5 ]; |
| | | const Mx = [ 9,8,7,6,5,4,3,2,1,1 ]; |
| | | |
| | | if ( id.length != 10 ) return false; |
| | | let i = tab.indexOf( id.charAt(0) ); |
| | | if ( i === -1 ) return false; |
| | | let sum = A1[i] + A2[i]*9; |
| | | for ( i=1; i<10; i+=1 ) { |
| | | let v = parseInt( id.charAt(i) ); |
| | | if (i === 1 && isNaN(v)) { |
| | | switch(id.charAt(i)) { |
| | | case 'A': |
| | | v = 0; |
| | | break; |
| | | case 'B': |
| | | v = 1; |
| | | break; |
| | | case 'C': |
| | | v = 2; |
| | | break; |
| | | case 'D': |
| | | v = 3; |
| | | break; |
| | | default: |
| | | return false |
| | | } |
| | | } else if ( i === 1 && ([1,2,8,9].indexOf(v) === -1) ) { |
| | | return false |
| | | } |
| | | if ( i > 1 && isNaN(v) ) return false; |
| | | sum = sum + v * Mx[i]; |
| | | } |
| | | |
| | | if ( sum % 10 != 0 ) return false; |
| | | return true; |
| | | } |
| | | |
| | | get isSubmitBtnDisabled() { |
| | | const { |
| | | selectCloseOption, |
| | | policyholderIdentityId, |
| | | planCode, |
| | | closedReason, |
| | | closedOtherReason, |
| | | remark |
| | | } = this.appointmentCloseInfo; |
| | | if (selectCloseOption === this.contactStatus.DONE) { |
| | | return !policyholderIdentityId || !this.identityIdValid || !planCode || !this.appointmentCloseDate |
| | | } else if (closedReason === 'other' || closedReason === 'no_suitable_commodity') { |
| | | return !closedOtherReason |
| | | } |
| | | return false |
| | | } |
| | | |
| | | get identityIdValid() { |
| | | const identityId = this.appointmentCloseInfo.policyholderIdentityId; |
| | | return identityId ? this.checkIdentityId(identityId) : true; |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .appointment-close__remark, |
| | | .appointment-client-detail-close__input { |
| | | border-radius: 5px; |
| | | border : 1px solid #707070; |
| | | font-size: 20px; |
| | | padding : 10px 20px; |
| | | width : 100%; |
| | | box-sizing: border-box; |
| | | &::placeholder { |
| | | color: $MID_GREY; |
| | | } |
| | | &.is-invalid { |
| | | border-color: $PRIMARY_RED !important; |
| | | } |
| | | } |
| | | .invite-review{ |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | } |
| | | .error { |
| | | @extend .smTxt_bold; |
| | | @extend .text--primary; |
| | | height: 16px; |
| | | } |
| | | .required { |
| | | position: relative; |
| | | |
| | | &::before { |
| | | content: '*'; |
| | | font-size: 15px; |
| | | font-weight: bold; |
| | | position: absolute; |
| | | color: #FF0000; |
| | | transform: translateX(-2px); |
| | | z-index: 5; |
| | | } |
| | | } |
| | | </style> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <div class="appointment-client-detail-page"> |
| | | <div class="date-detail"> |
| | | <div>{{ appointmentDetail.appointmentDate | formatDate }}é ç´</div> |
| | | <div>{{ appointmentDetail.consultantReadTime | formatDate }} |
| | | <span v-if="appointmentDetail.consultantReadTime">å·²è®</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <AppointmentProgress |
| | | class="mt-10" |
| | | :currentStep="appointmentProgress" |
| | | ></AppointmentProgress> |
| | | |
| | | <section class="client-detail"> |
| | | |
| | | <div class="client-detail-info mb-30"> |
| | | <div class="client-detail-info__avatar"> |
| | | <div class="circle"> |
| | | {{ appointmentDetail.name || 'NO NAME' }} |
| | | <div class="sm-circle sm-circle-male" v-if="appointmentDetail.gender === 'male'"> |
| | | <i class="icon-sex-male sex-icon"></i> |
| | | </div> |
| | | <div class="sm-circle sm-circle-female" v-if="appointmentDetail.gender === 'female'"> |
| | | <i class="icon-sex-female sex-icon"></i> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="client-detail-info__information"> |
| | | <div>{{ appointmentDetail.age | toAgeLabel }}</div> |
| | | <div>{{ appointmentDetail.phone }}</div> |
| | | <div class="text--underline text--break-all"> |
| | | {{ appointmentDetail.email }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="client-detail-demand mt-10"> |
| | | |
| | | <div class="client-detail-demand__demand-list mb-10"> |
| | | <div class="client-detail-demand__demand-list-label">éæ±</div> |
| | | <div class="client-detail-demand__demand-list-content">{{ appointmentDetail.requirement }}</div> |
| | | </div> |
| | | |
| | | <div class="client-detail-demand__demand-list"> |
| | | <div class="client-detail-demand__demand-list-label">è¯çµ¡<br />ææ®µ</div> |
| | | <div class="client-detail-demand__demand-list-content"> |
| | | <div v-for="(hopeContactTime, index) in hopeContactTimeList" :key="index" |
| | | :class="{'mt-10': index > 0, 'pb-10': true, 'hope-contact-time__line': index + 1 < hopeContactTimeList.length }"> |
| | | <div v-for="(item, index) in getHopeContactTimeContent(hopeContactTime)" :key="index" :class="{'mt-10': index < 0 }"> |
| | | {{ item }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="client-detail-action" v-if="showWhenAppointmentHasClosed"> |
| | | <el-button @click="inviteReview">ç¼é滿æåº¦</el-button> |
| | | </div> |
| | | |
| | | <div class="client-detail-action" v-if="showWhenAppointmentHasContacted"> |
| | | <el-button @click="closeAppointment" >çµæ¡</el-button> |
| | | <el-button @click="sendMsg" style="margin-left: 0px">éç¥ç´è¨ª</el-button> |
| | | </div> |
| | | |
| | | </section> |
| | | |
| | | <div class="client-detail-action btn-center" v-if="showWhenAppointmentHasCreate"> |
| | | <el-button @click="sendMsg">å³éç´è¨ªéç¥</el-button> |
| | | </div> |
| | | |
| | | <template v-if="showWhenAppointmentHasClosed"> |
| | | <AppointmentClosedInfo :appointmentDetail="appointmentDetail" /> |
| | | </template> |
| | | |
| | | <InterviewMsg |
| | | :isVisible.sync="isVisibleDialog" |
| | | :client="appointmentDetail"> |
| | | </InterviewMsg> |
| | | |
| | | |
| | | <section class="mt-30" v-if="!showWhenAppointmentHasCreate"> |
| | | <AppointmentInterviewList :interviewList="appointmentDetail.interviewRecordDTOs" /> |
| | | </section> |
| | | |
| | | <section class="mt-30" v-if="!showWhenAppointmentHasCreate"> |
| | | <AppointmentRecordList :noticeLogs="appointmentDetail.appointmentNoticeLogs" /> |
| | | </section> |
| | | |
| | | <PopUpFrame :isOpen.sync="isShowInviteReviewDialog"> |
| | | <div class="text--middle invite-review"> |
| | | <div class="mb-30 mt-10">å·²ç¼é滿æåº¦</div> |
| | | <div class="text--primary text--middle cursor--pointer text--underline" @click="isShowInviteReviewDialog = false" :size="'250px'">æç¥éäº</div> |
| | | </div> |
| | | </PopUpFrame> |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component } from 'vue-property-decorator'; |
| | | import { namespace } from 'nuxt-property-decorator'; |
| | | |
| | | import { Appointment } from '~/shared/models/appointment.model'; |
| | | import { ContactStatus } from '~/shared/models/enum/contact-status'; |
| | | |
| | | import reviewsService from '~/shared/services/reviews.service'; |
| | | |
| | | |
| | | const appointmentStore = namespace('appointment.store'); |
| | | |
| | | @Component |
| | | export default class AppointmentDetailComponent extends Vue { |
| | | |
| | | @appointmentStore.State('appointmentDetail') |
| | | appointmentDetail!: Appointment; |
| | | |
| | | @appointmentStore.Getter('appointmentProgress') |
| | | appointmentProgress!: ContactStatus; |
| | | |
| | | isVisibleDialog = false; |
| | | isShowInviteReviewDialog = false ; |
| | | interviewTxt = ""; |
| | | contactStatus = ContactStatus; |
| | | |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | closeAppointment(): void { |
| | | this.$router.push(`/appointment/${this.appointmentDetail.id}/close`); |
| | | } |
| | | |
| | | sendMsg():void { |
| | | this.isVisibleDialog = true; |
| | | } |
| | | |
| | | editAppointmentHasClosed(): void{ |
| | | this.$router.push(`/appointment/${this.appointmentDetail.id}/close`); |
| | | } |
| | | |
| | | get showWhenAppointmentHasClosed(): boolean { |
| | | return this.appointmentDetail.communicateStatus === this.contactStatus.DONE |
| | | || this.appointmentDetail.communicateStatus === this.contactStatus.CLOSE |
| | | || this.appointmentDetail.communicateStatus === this.contactStatus.CANCEL; |
| | | } |
| | | |
| | | get showWhenAppointmentHasCreate(): boolean { |
| | | return this.appointmentDetail.communicateStatus === this.contactStatus.PICKED |
| | | || this.appointmentDetail.communicateStatus === this.contactStatus.RESERVED; |
| | | } |
| | | |
| | | get showWhenAppointmentHasContacted() :boolean { |
| | | return this.appointmentDetail.communicateStatus === this.contactStatus.CONTACTED; |
| | | } |
| | | |
| | | get hopeContactTimeList(): any[] { |
| | | return this.appointmentDetail.hopeContactTime.split("','") |
| | | } |
| | | |
| | | getHopeContactTimeContent(hopeContactTimeString: string): string[] { |
| | | const result = hopeContactTimeString.replaceAll("'", '').split('ã'); |
| | | return result; |
| | | } |
| | | |
| | | inviteReview(): void { |
| | | reviewsService.sendSatisfactionToClient(this.appointmentDetail.id).then(res => { |
| | | this.isShowInviteReviewDialog = true; |
| | | }); |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .appointment-client-detail-page{ |
| | | .date-detail{ |
| | | display : flex; |
| | | font-size : 16px; |
| | | color : #68737A; |
| | | justify-content: space-between; |
| | | margin-bottom : 2px; |
| | | } |
| | | .client-detail{ |
| | | background-color: #fff; |
| | | margin-top:10px; |
| | | padding: 17px 21px; |
| | | .client-detail-info { |
| | | display: flex; |
| | | .client-detail-info__avatar{ |
| | | display: flex; |
| | | margin-right: 22px; |
| | | .circle{ |
| | | height: 100px; |
| | | width: 100px; |
| | | border-radius: 50%; |
| | | background-image: url('~/assets/images/appointment/avatar_bg.svg'); |
| | | position: relative; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | .sm-circle{ |
| | | position: absolute; |
| | | height: 30px; |
| | | width: 30px; |
| | | border-radius: 50%; |
| | | background-color: #fff; |
| | | bottom: 0; |
| | | right: 0; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | .sex-icon { |
| | | font-size: 20px; |
| | | &.icon-sex-male{ |
| | | color: $SKY_BLUE; |
| | | } |
| | | &.icon-sex-female{ |
| | | color: $CORAL; |
| | | } |
| | | } |
| | | &-male { |
| | | border: 1px solid $SKY_BLUE; |
| | | } |
| | | &-female { |
| | | border: 1px solid $LIGHT_RED; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .client-detail-info__information{ |
| | | font-size: 20px; |
| | | line-height: 1.6; |
| | | } |
| | | } |
| | | .client-detail-demand{ |
| | | background-color: #fff; |
| | | font-size: 20px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | .client-detail-demand__demand-list{ |
| | | display: flex; |
| | | } |
| | | .client-detail-demand__hope-contact-time{ |
| | | display: flex; |
| | | } |
| | | .client-detail-demand__demand-list-label { |
| | | @extend .mb-10; |
| | | @extend .mdTxt; |
| | | @extend .mr-10; |
| | | line-height: 1.3; |
| | | color : $DARK_BLUE; |
| | | flex-basis: auto; |
| | | min-width : 40px; |
| | | } |
| | | .client-detail-demand__demand-list-content { |
| | | text-align: justify; |
| | | line-height: 1.3; |
| | | text-justify: auto; |
| | | word-break: break-all; |
| | | } |
| | | } |
| | | .client-detail-action { |
| | | margin-left: 50px; |
| | | } |
| | | } |
| | | } |
| | | .invite-review{ |
| | | align-items : center; |
| | | display : flex; |
| | | flex-direction: column; |
| | | } |
| | | .close-appointment-detail{ |
| | | background-color: #fff; |
| | | display: flex; |
| | | margin-top: 30px; |
| | | padding: 20px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | font-size: 20px; |
| | | } |
| | | .close-appointment-detail-nav{ |
| | | display: flex; |
| | | justify-content: space-between; |
| | | flex: 1; |
| | | } |
| | | .hope-contact-time__line { |
| | | border-bottom: 1px solid #CCCCCC; |
| | | } |
| | | .btn-center{ |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | |
| | | </style> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <InterviewAdd></InterviewAdd> |
| | | </template> |
| | | <script lang="ts"> |
| | | import { Vue, Component } from 'nuxt-property-decorator'; |
| | | |
| | | @Component |
| | | export default class EditAppointmentInterview extends Vue { |
| | | |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | |
| | | </style> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <InterviewAdd></InterviewAdd> |
| | | </template> |
| | | <script lang="ts"> |
| | | import { Vue, Component } from 'nuxt-property-decorator'; |
| | | |
| | | @Component |
| | | export default class NewAppointmentInterview extends Vue { |
| | | |
| | | } |
| | | </script> |
| | | <style lang="scss" > |
| | | .edit-appointment-record { |
| | | padding-left : 10px; |
| | | padding-right: 10px; |
| | | .edit-appointment-record-date{ |
| | | color : #68737A; |
| | | display : flex; |
| | | justify-content: space-between; |
| | | margin-bottom : 26px; |
| | | } |
| | | } |
| | | .date-input { |
| | | align-items : center; |
| | | background-color: #fff; |
| | | border : 1px solid #707070; |
| | | border-radius : 5px; |
| | | display : flex; |
| | | font-size : 20px; |
| | | height : 46px; |
| | | margin-bottom : 30px; |
| | | padding-left : 20px; |
| | | padding-right : 20px; |
| | | } |
| | | .icon { |
| | | color : $PRIMARY_RED; |
| | | display : flex; |
| | | flex : 1; |
| | | justify-content: flex-end; |
| | | } |
| | | .edit-appointment-record-btn{ |
| | | margin-top: 30px; |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | .required { |
| | | position: relative; |
| | | &::before { |
| | | content: '*'; |
| | | position: absolute; |
| | | color: #FF0000; |
| | | transform: translate(-12px, 0); |
| | | } |
| | | } |
| | | </style> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | |
| | | <template> |
| | | <div> |
| | | <div class="text--right mb-30"> |
| | | <div class="pam-link-button--lg" |
| | | @click="addInterview">+æ°å¢</div> |
| | | </div> |
| | | <InterviewCard :interviewList="appointmentDetail.interviewRecordDTOs"></InterviewCard> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Component, namespace, Vue } from "nuxt-property-decorator"; |
| | | import { Appointment } from "~/shared/models/appointment.model"; |
| | | |
| | | const appointmentStore = namespace('appointment.store'); |
| | | |
| | | @Component |
| | | export default class InterviewList extends Vue { |
| | | @appointmentStore.State |
| | | appointmentDetail!: Appointment; |
| | | |
| | | appointmentId!: number; |
| | | |
| | | //////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | this.appointmentId = +this.$route.params.appointmentId; |
| | | } |
| | | |
| | | //////////////////////////////////////////////////////// |
| | | |
| | | addInterview(): void { |
| | | this.$router.push(`/appointment/${this.appointmentId}/interview/new`); |
| | | } |
| | | |
| | | } |
| | | </script> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | |
| | | |
| | | <template> |
| | | <InterviewRecordCard :noticeLogsList="displayLogs"></InterviewRecordCard> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Component, namespace, Vue, Watch } from "nuxt-property-decorator"; |
| | | import { Appointment, NoticeLogs } from "~/shared/models/appointment.model"; |
| | | |
| | | const appointmentStore = namespace('appointment.store'); |
| | | |
| | | @Component |
| | | export default class RecordList extends Vue { |
| | | @appointmentStore.State |
| | | appointmentDetail!: Appointment; |
| | | |
| | | displayLogs: NoticeLogs[] = []; |
| | | |
| | | //////////////////////////////////////////////////////// |
| | | |
| | | @Watch('appointmentDetail', {immediate: true}) |
| | | onAppointmentDetailChange() { |
| | | if (this.appointmentDetail?.appointmentNoticeLogs.length) { |
| | | this.displayLogs = this.appointmentDetail?.appointmentNoticeLogs |
| | | .map((i) => ({ ...i, sortDate: new Date(i.createdDate)})) |
| | | .sort((preItem, nextItem) => +nextItem.sortDate - +preItem.sortDate); |
| | | } |
| | | } |
| | | |
| | | } |
| | | </script> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <div> |
| | | <div class="mdTxt">å³å°ç´è¨ªæç¨(3)</div> |
| | | |
| | | <div class="remind-container"> |
| | | <div class="remind-date mr-10"> |
| | | <div class="mb-3 smTxt bgc-primary-red date-year"> |
| | | <div class="mb-3 mt-2">2021</div> |
| | | </div> |
| | | <div class="p mt-5"> |
| | | <div>12/25</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="remind-content-txt"> |
| | | <div class="mb-3">09:00</div> |
| | | <div>ç´è¨ªæå¿é¶</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="remind-container"> |
| | | |
| | | <div class="remind-date mr-10"> |
| | | <div class="mb-3 smTxt bgc-primary-red date-year"> |
| | | <div class="mb-3 mt-2">2021</div> |
| | | </div> |
| | | <div class="p mt-5"> |
| | | <div>12/25</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="remind-content-txt"> |
| | | <div class="mb-3">09:00</div> |
| | | <div>ç´è¨ªæå¿é¶</div> |
| | | </div> |
| | | |
| | | </div> |
| | | <div class="remind-container"> |
| | | |
| | | <div class="remind-date mr-10"> |
| | | <div class="mb-3 smTxt bgc-primary-red date-year"> |
| | | <div class="mb-3 mt-2">2021</div> |
| | | </div> |
| | | <div class="p mt-5"> |
| | | <div>12/25</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="remind-content-txt"> |
| | | <div class="mb-3">09:00</div> |
| | | <div>ç´è¨ªæå¿é¶</div> |
| | | </div> |
| | | |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component } from 'nuxt-property-decorator'; |
| | | |
| | | @Component |
| | | export default class Agenda extends Vue { |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | </style> |
| | |
| | | import { Role } from '~/shared/models/enum/Role'; |
| | | import messageBoxService from '~/shared/services/message-box.service'; |
| | | import loginService from '~/shared/services/login.service' |
| | | import { AgentInfo } from '~/shared/models/agent-info.model'; |
| | | |
| | | const loginStore = namespace('login.store'); |
| | | const roleStorage = namespace('localStorage'); |
| | | |
| | | @Component({ |
| | | layout: 'home' |
| | | }) |
| | | export default class ConsultantLogin extends Vue { |
| | | @roleStorage.Mutation storageIdToken!: (token: string) => void; |
| | | @roleStorage.Mutation storageRole!: (role: string) => void; |
| | | @roleStorage.Mutation storageConsultantId!:(id:string) => void; |
| | | @roleStorage.Mutation |
| | | storageIdToken!: (token: string) => void; |
| | | |
| | | @roleStorage.Mutation |
| | | storageRole!: (role: string) => void; |
| | | |
| | | @roleStorage.Mutation |
| | | storageConsultantId!:(id:string) => void; |
| | | |
| | | @loginStore.Action |
| | | getLoginConsultantDetail!: (agentNo: string) => Promise<AgentInfo>; |
| | | |
| | | consultantDto = { |
| | | password: '', |
| | | username: '', |
| | | }; |
| | | imgSrc = ''; |
| | | isRememberUserName = false; |
| | | isShowPassword = false; |
| | | imgSrc = ''; |
| | | verificationCode=''; |
| | | consultantDto = { |
| | | username: '', |
| | | password: '', |
| | | } |
| | | |
| | | //////////////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | this.getInitUserName(); |
| | | this.regenerateImgOfVerification(); |
| | | }; |
| | | |
| | | get isAlreadyDone():boolean{ |
| | | return !!(this.verificationCode && this.consultantDto.username && this.consultantDto.password); |
| | | private getInitUserName(): void { |
| | | const username = localStorage.getItem('consultantUserName') |
| | | if (username) { |
| | | this.consultantDto.username = username; |
| | | this.isRememberUserName = true; |
| | | } |
| | | } |
| | | |
| | | |
| | | //////////////////////////////////////////////////////////////////// |
| | | |
| | | |
| | | public regenerateImgOfVerification(): void { |
| | | loginService.getImgOfVerification().then( imgOfBase64 => |
| | | this.imgSrc = imgOfBase64 |
| | |
| | | } |
| | | |
| | | public sendInfo():void{ |
| | | this.isAlreadyDone ? this.verify() : messageBoxService.showErrorMessage('è«ç¢ºèªå¸³èãå¯ç¢¼ä»¥åé©è碼æ¯å¦å¡«å¯«å®ç¢'); |
| | | this.isAlreadyDone |
| | | ? this.verify() |
| | | : messageBoxService.showErrorMessage('è«ç¢ºèªå¸³èãå¯ç¢¼ä»¥åé©è碼æ¯å¦å¡«å¯«å®ç¢'); |
| | | } |
| | | |
| | | get isAlreadyDone():boolean{ |
| | | return !!(this.verificationCode && this.consultantDto.username && this.consultantDto.password); |
| | | } |
| | | |
| | | |
| | | //////////////////////////////////////////////////////////////////// |
| | | private getInitUserName(): void { |
| | | const username = localStorage.getItem('consultantUserName') |
| | | if (username) { |
| | | this.consultantDto.username = username; |
| | | this.isRememberUserName = true; |
| | | } |
| | | } |
| | | |
| | | private verify():void{ |
| | | loginService.getVerificationStatus(this.verificationCode).then( verifySuccess => { |
| | | if(verifySuccess.data){ |
| | | this.loginWithConsultant() |
| | | this.loginWithConsultant(); |
| | | }else{ |
| | | this.clearValue(); |
| | | this.regenerateImgOfVerification(); |
| | |
| | | |
| | | private loginWithConsultant(): void { |
| | | loginService.logInToConsultant(this.consultantDto).then(res => { |
| | | this.getLoginConsultantDetail(this.consultantDto.username); |
| | | this.storageIdToken(res.data.id_token); |
| | | this.storageRole(Role.ADMIN); |
| | | this.storageConsultantId(this.consultantDto.username) |
| | |
| | | <ConsultantSwiper :agents="recommendList"></ConsultantSwiper> |
| | | </div> |
| | | </div> |
| | | |
| | | <Ui-Dialog |
| | | :isVisible.sync="isShowAppointmentDialog" |
| | | :width="appointmentDialogWidth" |
| | | class="pam-myDemand-dialog pam-dialog-reserved" |
| | | > |
| | | <div v-if="appointmentDetail"> |
| | | <h5 class="subTitle text--center mb-30">é ç´æå</h5> |
| | | <p class="smTxt">{{appointmentDetail.appointmentDate | formatDate}}</p> |
| | | <div class="reserved-info"> |
| | | <p>å§åï¼{{appointmentDetail.name}}</p> |
| | | <p>é»è©±ï¼{{appointmentDetail.phone}}</p> |
| | | <p>Emailï¼{{appointmentDetail.email}}</p> |
| | | <p>æ§å¥ï¼{{gender}}</p> |
| | | <p>年齡ï¼{{appointmentDetail.age | toAgeLabel }}</p> |
| | | <p>è·æ¥ï¼{{appointmentDetail.job}}</p> |
| | | <p>éæ±ï¼{{appointmentDetail.requirement.split(',').join('ã')}}</p> |
| | | <p |
| | | v-for="(item, index) in hopeContactTime" |
| | | :key="index" |
| | | >é£çµ¡ææ®µ{{index + 1 | formatNumber}}ï¼{{ item | formatHopeContactTime }}</p> |
| | | <div v-if="appointmentDetail.satisfactionScore"> |
| | | <div class="mdTxt mt-10 mb-10">滿æåº¦</div> |
| | | <el-rate |
| | | :value="appointmentDetail.satisfactionScore" |
| | | class="pam-myDemand-dialog__rate" |
| | | disabled> |
| | | </el-rate> |
| | | </div> |
| | | </div> |
| | | |
| | | <div v-if="notScoreAppointmentYet" class="reserved-btn"> |
| | | <el-button type="primary" |
| | | @click.native="isShowReviewDialog = true">çµ¦äºæ»¿æåº¦è©å</el-button> |
| | | </div> |
| | | </div> |
| | | </Ui-Dialog> |
| | | |
| | | <PopUpFrame |
| | | :isOpen.sync="isShowReAppointmentDialog" |
| | | @closePopUp="removeUrlQueryParameter('notContactAppointmentIdFromMsg')" |
| | | > |
| | | <div class="pam-dialog-review"> |
| | | <div class="mt-30 text--middle" v-if="agentInfo"> |
| | | 徿±æï¼æ¨é ç´ç<span class="text--bold">{{ consultantName }}</span>顧忣å¿ç¢ä¸ï¼è«æ¨åæ¶é ç´ä¸¦æ¹é¸å
¶ä»é¡§å |
| | | </div> |
| | | |
| | | <el-row |
| | | type="flex" |
| | | class="mt-50" |
| | | justify="center"> |
| | | <el-button |
| | | type="primary" |
| | | @click="reAppointment">åæ¶é ç´åæ¹é¸å
¶ä»é¡§å</el-button> |
| | | </el-row> |
| | | <el-row |
| | | type="flex" |
| | | class="mt-20" |
| | | justify="center"> |
| | | <el-button |
| | | class="outline_btn" |
| | | @click="cancelAppointment">åæ¶é ç´</el-button> |
| | | </el-row> |
| | | </div> |
| | | </PopUpFrame> |
| | | |
| | | <PopUpFrame |
| | | :isOpen.sync="isShowReviewDialog" |
| | | @closePopUp="removeUrlQueryParameter('appointmentId')" |
| | | > |
| | | <div class="mdTxt pam-dialog-review"> |
| | | ä¿éªé¡§å滿æåº¦ |
| | | <span class="hint">é¸åææ</span> |
| | | <div class="mt-30 review-content" v-if="agentInfo"> |
| | | <UiAvatar :size="80" :agentNo="agentInfo.agentNo"></UiAvatar> |
| | | <div class="review-text">å°æ¼é¡§å |
| | | <span class="text--primary">{{agentInfo.name}}</span> |
| | | çæ´é«æåï¼æ¨çµ¦äºå¹¾é¡æè©å¹ï¼ |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="review-score"> |
| | | <el-rate v-model="inputScore" class="pam-rate mt-30"></el-rate> |
| | | </div> |
| | | |
| | | <div class="review-btn"> |
| | | <el-button |
| | | type="primary" |
| | | :disabled="!inputScore" |
| | | @click="userReviewsConsultants">éåº</el-button> |
| | | </div> |
| | | </div> |
| | | </PopUpFrame> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, State, Action, Watch, namespace } from 'nuxt-property-decorator'; |
| | | import { Appointment, AppointmentClosedInfo } from '~/shared/models/appointment.model'; |
| | | import { Consultant } from '~/shared/models/consultant.model'; |
| | | import { ContactStatus } from '~/shared/models/enum/contact-status'; |
| | | import { UserReviewsConsultantsParams } from '~/shared/models/reviews.model'; |
| | | import { StrictQueryParams } from '~/shared/models/strict-query.model'; |
| | | import appointmentService from '~/shared/services/appointment.service'; |
| | | import reviewsService from '~/shared/services/reviews.service'; |
| | | import UtilsService from '~/shared/services/utils.service'; |
| | | import myConsultantService from '~/shared/services/my-consultant.service'; |
| | | import { AgentInfo } from '~/shared/models/agent-info.model'; |
| | | |
| | | const localStorage = namespace('localStorage'); |
| | | const roleStorage = namespace('localStorage'); |
| | | |
| | | @Component({ |
| | | layout: 'home' |
| | |
| | | @State('myConsultantList') |
| | | myConsultantList!: Consultant[]; |
| | | |
| | | @roleStorage.Getter |
| | | isAdminLogin!: boolean; |
| | | |
| | | @Action |
| | | storeRecommendList!: any; |
| | | |
| | | @Action storeConsultantList!: any; |
| | | @Action |
| | | storeConsultantList!: any; |
| | | |
| | | @localStorage.Mutation |
| | | storageClearQuickFilter!: () => void; |
| | |
| | | @localStorage.Mutation |
| | | storageClearRecommendConsultant!: () => void; |
| | | |
| | | @localStorage.Getter |
| | | currentSatisfactionIdFromMsg!: string; |
| | | |
| | | @localStorage.Getter |
| | | currentNotContactAppointmentIdFromMsg!: string; |
| | | |
| | | @localStorage.Mutation |
| | | storageClearSatisfactionIdFromMsg!: () => void; |
| | | |
| | | @localStorage.Mutation |
| | | storageClearNotContactAppointmentIdFromMsg!: () => void; |
| | | |
| | | @localStorage.Mutation |
| | | storageStrickQueryItem!: (strictQueryDto: StrictQueryParams) => void; |
| | | |
| | | consultantList: Consultant[] = []; |
| | | |
| | | appointmentDialogWidth = ''; |
| | | inputScore = 0; |
| | | isShowAppointmentDialog = false; |
| | | isShowReAppointmentDialog = false; |
| | | isShowReviewDialog = false; |
| | | consultantName = ''; |
| | | contactStatus = ContactStatus; |
| | | |
| | | appointmentDetail: Appointment = { |
| | | age : '', |
| | | agentNo : '', |
| | | appointmentClosedInfo: {} as AppointmentClosedInfo, |
| | | appointmentDate : '', |
| | | appointmentMemoList: [], |
| | | appointmentNoticeLogs: [], |
| | | communicateStatus : this.contactStatus.PICKED, |
| | | consultantReadTime: '', |
| | | consultantViewTime: '', |
| | | contactTime : '', |
| | | contactType : '', |
| | | customerId : 0, |
| | | email : '', |
| | | gender : '', |
| | | hopeContactTime : '', |
| | | interviewRecordDTOs: [], |
| | | id : 0, |
| | | job : '', |
| | | lastModifiedDate : '', |
| | | name : '', |
| | | otherRequirement : '', |
| | | phone : '', |
| | | requirement : '', |
| | | satisfactionScore : 0, |
| | | }; |
| | | |
| | | agentInfo: Consultant = { |
| | | agentNo : '', |
| | | name : '', |
| | | img : '', |
| | | expertise : [], |
| | | avgScore : 0, |
| | | contactStatus : '', |
| | | createTime : '', |
| | | updateTime : '', |
| | | customerViewTime : '', |
| | | role : '', |
| | | seniority : '', |
| | | appointments : [] |
| | | }; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | if (!this.recommendList?.length) { |
| | | this.storeRecommendList(); |
| | | if (this.isAdminLogin) { |
| | | this.$router.push('/myAppointmentList/appointmentList'); |
| | | } else { |
| | | if (!this.recommendList?.length) { |
| | | this.storeRecommendList(); |
| | | } |
| | | |
| | | this.storeConsultantList(); |
| | | this.storageClearQuickFilter(); |
| | | this.storageClearRecommendConsultant(); |
| | | } |
| | | |
| | | this.storeConsultantList(); |
| | | this.storageClearQuickFilter(); |
| | | this.storageClearRecommendConsultant(); |
| | | } |
| | | |
| | | destroyed() { |
| | | this.removeUrlQueryParameter(); |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | |
| | | @Watch('myConsultantList') |
| | | onMyConsultantListChange() { |
| | | this.consultantList = (this.myConsultantList || []) |
| | | .filter(item => item.contactStatus !== 'contacted') |
| | | .map((item) => ({ ...item, formatDate: new Date(item.updateTime || item.createTime)})) |
| | | .sort((preItem, nextItem) => +nextItem.formatDate - +preItem.formatDate) |
| | | .sort((preItem, nextItem) => +nextItem.formatDate - +preItem.formatDate); |
| | | |
| | | if (this.currentNotContactAppointmentIdFromMsg) { |
| | | this.autoOpenAppointmentBy('askReAppointment', +this.currentNotContactAppointmentIdFromMsg); |
| | | return; |
| | | } |
| | | |
| | | if (this.currentSatisfactionIdFromMsg) { |
| | | this.autoOpenAppointmentBy('inviteReviewConsultant',+this.currentSatisfactionIdFromMsg); |
| | | this.storageClearSatisfactionIdFromMsg(); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | private autoOpenAppointmentBy(reason: string, targetAppointmentId: number): void { |
| | | const setAgentInfo = new Promise((resolve, reject) => { |
| | | this.agentInfo = this.myConsultantList.filter(item => { |
| | | const appointmentIndex = item.appointments?.findIndex(i => i.id === targetAppointmentId); |
| | | return appointmentIndex !== undefined && appointmentIndex > -1; |
| | | })[0]; |
| | | if (this.agentInfo) { |
| | | myConsultantService.getConsultantDetail(this.agentInfo.agentNo).then((res) => resolve(res)); |
| | | } |
| | | }); |
| | | |
| | | const setAppointment = new Promise((resolve, reject) => { |
| | | appointmentService.getAppointmentDetail(targetAppointmentId).then((res) => resolve(res)); |
| | | }); |
| | | |
| | | Promise.all([setAgentInfo, setAppointment]).then((values) => { |
| | | const agentInfo = values[0] as AgentInfo; |
| | | const appointmentInfo = values[1] as Appointment; |
| | | this.consultantName = agentInfo.name; |
| | | this.appointmentDetail = appointmentInfo; |
| | | this.appointmentDialogWidth = UtilsService.isMobileDevice() ? '80%' : ''; |
| | | this.isShowAppointmentDialog = true; |
| | | switch (reason) { |
| | | case 'inviteReviewConsultant': |
| | | if (this.notScoreAppointmentYet) { |
| | | setTimeout(() => { |
| | | this.isShowReviewDialog = true; |
| | | }, 500); |
| | | } |
| | | break; |
| | | case 'askReAppointment': |
| | | setTimeout(() => { |
| | | this.isShowReAppointmentDialog = true; |
| | | }, 500); |
| | | break; |
| | | } |
| | | }); |
| | | |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | navigateToRoute(path: string): void { |
| | | this.$router.push(path); |
| | | } |
| | | |
| | | reAppointment(): void { |
| | | appointmentService.cancelAppointment(this.appointmentDetail.id).then(() => { |
| | | const requirements = this.appointmentDetail.requirement.split(','); |
| | | this.storeConsultantList(); |
| | | this.storageStrickQueryItem({ requirements: requirements }); |
| | | this.storageClearNotContactAppointmentIdFromMsg(); |
| | | this.removeUrlQueryParameter('notContactAppointmentIdFromMsg'); |
| | | this.$router.push('/recommendConsultant'); |
| | | }); |
| | | } |
| | | |
| | | cancelAppointment(): void { |
| | | appointmentService.cancelAppointment(this.appointmentDetail.id).then(() => { |
| | | this.storeConsultantList(); |
| | | this.storageClearNotContactAppointmentIdFromMsg(); |
| | | this.removeUrlQueryParameter('notContactAppointmentIdFromMsg'); |
| | | this.$router.push(''); |
| | | }); |
| | | } |
| | | |
| | | userReviewsConsultants() { |
| | | const reviewParams: UserReviewsConsultantsParams = { |
| | | appointmentId: this.appointmentDetail.id, |
| | | score: this.inputScore, |
| | | } |
| | | this.appointmentDetail.satisfactionScore = this.inputScore; |
| | | |
| | | reviewsService.userReviewsConsultants(reviewParams).then((res) => { |
| | | this.isShowReviewDialog = false; |
| | | }); |
| | | } |
| | | |
| | | removeUrlQueryParameter(targetKey?: string): void { |
| | | // NOTE: åªé¤ç¹å®ç query parameter [Tomas, 2022/1/24 11:36] |
| | | // [REF] How to remove a parameter from this.$router.query Nuxt.js? https://reurl.cc/X45aMD |
| | | let newRouteQuery = {}; |
| | | if (targetKey) { |
| | | Object.keys(this.$route.query).forEach((key) => { |
| | | if (key !== targetKey) { |
| | | newRouteQuery[key] = this.$route.query[key] |
| | | } |
| | | }) |
| | | } |
| | | this.$router.push(newRouteQuery); |
| | | } |
| | | |
| | | /////////////////////////////////////////////////////////////////////////////// |
| | | |
| | | get gender() { |
| | | if (this.appointmentDetail.gender) { |
| | | return this.appointmentDetail.gender === 'male' ? 'ç·æ§' : '女æ§'; |
| | | } |
| | | return '' |
| | | } |
| | | |
| | | get hopeContactTime() { |
| | | const contactList = this.appointmentDetail.hopeContactTime |
| | | .split("'").map((item: any) => item.slice(0, item.length)); |
| | | return contactList.filter((item: any) => !!item && item !== ",") |
| | | } |
| | | |
| | | get notScoreAppointmentYet(): boolean { |
| | | if (this.appointmentDetail.communicateStatus === 'closed' || this.appointmentDetail.communicateStatus === 'done') { |
| | | return !this.appointmentDetail.satisfactionScore; |
| | | }; |
| | | return false; |
| | | } |
| | | |
| | | } |
| | |
| | | max-width: 335px; |
| | | } |
| | | } |
| | | |
| | | </style> |
| | |
| | | <el-row type="flex" justify="space-between"> |
| | | <div class="mdTxt">輸å
¥é©è碼</div> |
| | | <div class="otp-count-timer"> |
| | | {{phoneCounter}} |
| | | {{counterTime(otpCounterSec)}} |
| | | </div> |
| | | </el-row> |
| | | |
| | |
| | | <el-row type="flex" justify="space-between"> |
| | | <div class="mdTxt">輸å
¥é©è碼</div> |
| | | <div class="otp-count-timer"> |
| | | {{emailOtpCounter}} |
| | | {{counterTime(emailCounterSec)}} |
| | | </div> |
| | | </el-row> |
| | | |
| | |
| | | :isOpen.sync="registerDialogVisible" |
| | | :dialogWidth="'90%'" |
| | | class="pam-register-dialog" |
| | | @closePopUp="isReadContract = false" |
| | | @closePopUp="isReadContract = false;agreeContract = false" |
| | | > |
| | | <div class="subTitle text--center mb-20">æ¡è¿æ°ä½¿ç¨è
</div> |
| | | <el-row> |
| | |
| | | import { RegisterInfo } from '~/shared/models/registerInfo'; |
| | | import loginService from '~/shared/services/login.service'; |
| | | import messageBoxService from '~/shared/services/message-box.service'; |
| | | import otpService, { OtpStorageName } from '~/shared/services/otp.service'; |
| | | |
| | | const roleStorage = namespace('localStorage'); |
| | | |
| | | @Component |
| | | export default class Login extends Vue { |
| | | @roleStorage.Mutation storageIdToken!: (token:string) => void; |
| | | @roleStorage.Mutation storageRole!: (role:string) => void; |
| | | @roleStorage.Mutation |
| | | storageIdToken!: (token:string) => void; |
| | | |
| | | @roleStorage.Mutation |
| | | storageRole!: (role:string) => void; |
| | | |
| | | @Ref('contract') readonly contract!: any; |
| | | |
| | | connectDevice: 'MOBILE' | 'EMAIL' = 'MOBILE'; |
| | |
| | | otpCounterSec = 300; |
| | | otpResendCounter = 30; |
| | | otpInterval: any; |
| | | phoneOtpInfo!: OtpInfo; |
| | | phoneOtpIndexKey!: string; |
| | | |
| | | email = ''; |
| | | onEmailVerifyResendStatus: 'APPLY_OTP' | 'CAN_RESEND' = 'APPLY_OTP'; |
| | |
| | | emailResendCounter = 30; |
| | | emailOtpCode = ''; |
| | | emailResendInterval: any; |
| | | emailOtpInfo!: OtpInfo; |
| | | emailOtpIndexKey!: string; |
| | | |
| | | autoRedirectCounter = 3; |
| | | autoRedirectInterval: any; |
| | |
| | | previousPath = ''; |
| | | |
| | | ///////////////////////////////////////////////////// |
| | | mounted() { |
| | | const phoneOtpTime = localStorage.getItem('phoneOtpTime'); |
| | | const emailOtpTime = localStorage.getItem('emailOtpTime'); |
| | | const parsePhoneOtpTime = phoneOtpTime ? JSON.parse(phoneOtpTime) : ''; |
| | | const parseEmailOtpTime = emailOtpTime ? JSON.parse(emailOtpTime) : ''; |
| | | if (parsePhoneOtpTime && parsePhoneOtpTime.contactType === 'SMS') { |
| | | this.phoneDiffTime(parsePhoneOtpTime); |
| | | } |
| | | if (parseEmailOtpTime && parseEmailOtpTime.contactType === 'EMAIL') { |
| | | this.emailDiffTime(parseEmailOtpTime); |
| | | } |
| | | } |
| | | |
| | | beforeRouteEnter (to, from, next) { |
| | | next(vm => { |
| | | console.log(from.path, 'beforeRouteEnter'); |
| | | vm.previousPath = from.path; |
| | | }) |
| | | } |
| | | |
| | | mounted() { |
| | | this.parsePhoneOtpTimeFromStorage(); |
| | | this.parseEmailOtpTimeFromStorage(); |
| | | } |
| | | |
| | | private parsePhoneOtpTimeFromStorage() { |
| | | const parsePhoneOtpTime = otpService.parseOtpTime(OtpStorageName.PHONE); |
| | | const diffSecs = otpService.diffOtpTime(OtpStorageName.PHONE, this.otpCounterSec); |
| | | |
| | | if (parsePhoneOtpTime && diffSecs) { |
| | | this.otpResendCounter = diffSecs < 30 ? 30 - diffSecs : 0; |
| | | this.otpCounterSec -= diffSecs; |
| | | this.phoneNumber = parsePhoneOtpTime.phone ? parsePhoneOtpTime.phone : ''; |
| | | this.onPhoneVerifyStep = 'INPUT_OTP'; |
| | | this.phoneOtpIndexKey = parsePhoneOtpTime.indexKey; |
| | | this.startOtpCount('MOBILE'); |
| | | } |
| | | } |
| | | |
| | | private parseEmailOtpTimeFromStorage() { |
| | | const parseEmailOtpTime = otpService.parseOtpTime(OtpStorageName.EMAIL); |
| | | const diffSecs = otpService.diffOtpTime(OtpStorageName.EMAIL, this.emailCounterSec); |
| | | |
| | | if (parseEmailOtpTime && diffSecs) { |
| | | this.emailResendCounter = diffSecs < 30 ? 30 - diffSecs : 0; |
| | | this.emailCounterSec -= diffSecs; |
| | | this.email = parseEmailOtpTime.email ? parseEmailOtpTime.email : ''; |
| | | this.onEmailVerifyResendStatus = 'CAN_RESEND'; |
| | | this.emailOtpIndexKey = parseEmailOtpTime.indexKey; |
| | | this.startOtpCount('EMAIL'); |
| | | } |
| | | } |
| | | |
| | | destroyed() { |
| | |
| | | |
| | | ////////////////////////////////////////////////////////// |
| | | |
| | | detectContractReadStatus(event: any): void { |
| | | const scrollTop = Math.round(event.target.scrollTop); |
| | | const height = event.target.scrollHeight - event.target.clientHeight; |
| | | if (Math.floor(scrollTop/10) === (Math.floor(height/10))) { |
| | | this.isReadContract = true; |
| | | } |
| | | }; |
| | | |
| | | get isSubmitBtnDisabled(): boolean { |
| | | return this.connectDevice === 'MOBILE' |
| | | ? (!this.otpCode || !this.phoneNumber || !this.phoneValid || !this.otpCounterSec) |
| | | : (!this.emailOtpCode || !this.email || !this.emailValid || !this.emailCounterSec) |
| | | } |
| | | |
| | | get phoneCounter() { |
| | | let min = Math.floor(this.otpCounterSec / 60); |
| | | let sec = Math.floor(this.otpCounterSec % 60); |
| | | return `${min < 10 ? '0' + min : min}:${sec < 10 ? '0' + sec : sec}`; |
| | | } |
| | | |
| | | get emailOtpCounter() { |
| | | let min = Math.floor(this.emailCounterSec / 60); |
| | | let sec = Math.floor(this.emailCounterSec % 60); |
| | | return `${min < 10 ? '0' + min : min}:${sec < 10 ? '0' + sec : sec}`; |
| | | } |
| | | |
| | | get showPhoneOtpCodeField(): boolean { |
| | | return this.connectDevice === 'MOBILE' && this.onPhoneVerifyStep === 'INPUT_OTP'; |
| | | }; |
| | | |
| | | get showEmailVerifyField(): boolean { |
| | | return this.connectDevice === 'EMAIL' && this.onEmailVerifyResendStatus === 'CAN_RESEND'; |
| | | }; |
| | | |
| | | get phoneValid() { |
| | | const rule = /^09[0-9]{8}$/; |
| | | return this.phoneNumber ? rule.test(this.phoneNumber) : true; |
| | | } |
| | | |
| | | get emailValid() { |
| | | const rule = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/; |
| | | return this.email ? rule.test(this.email) : true; |
| | | } |
| | | |
| | | applyOtpVerification(type: string): void { |
| | | const isMobile = this.connectDevice === 'MOBILE'; |
| | | const loginInfo: LoginRequest = { |
| | | loginType: isMobile ? 'SMS' : 'EMAIL', |
| | | account: isMobile ? this.phoneNumber : this.email, |
| | | } |
| | | loginService.sendOtp(loginInfo).then(otpInfo => { |
| | | if (otpInfo.success) { |
| | | this.storageOtpTime(type, otpInfo); |
| | | this.startOtpSetting(type); |
| | | this.startOtpCount(type); |
| | | } else { |
| | | const errorMsg = OtpErrorCode[otpInfo.failCode] ? OtpErrorCode[otpInfo.failCode]:'OTP系統é¯èª¤'; |
| | | messageBoxService.showErrorMessage(errorMsg); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | resentOtp(type: string) { |
| | | this.resetOtpSetting(type); |
| | | this.applyOtpVerification(type); |
| | | } |
| | | |
| | | deleteOtpInfo(type: string) { |
| | | this.resetOtpSetting(type); |
| | | if (type === 'MOBILE') { |
| | | this.onPhoneVerifyStep = 'APPLY_OTP'; |
| | | this.phoneNumber = ''; |
| | | this.otpCode = ''; |
| | | } else { |
| | | this.onEmailVerifyResendStatus = 'APPLY_OTP'; |
| | | this.email = ''; |
| | | this.emailOtpCode = ''; |
| | | } |
| | | //////////////////// ç»å
¥ |
| | | login() { |
| | | const login: LoginVerify = this.setLoginInfo(); |
| | | this.removeOtpTime(); |
| | | loginService.loginVerify(login).then(res => { |
| | | this.storageIdToken(res.id_token); |
| | | this.storageRole(Role.USER); |
| | | this.phoneSuccessConfirmVisable = true; |
| | | this.autoRedirect(); |
| | | this.storagePhoneOrEmail(this.setRegisterInfo()); |
| | | }).catch(error => { |
| | | this.checkHttpErrorStatus(error); |
| | | }); |
| | | } |
| | | |
| | | confirmApplySuccess(): void { |
| | | this.phoneSuccessConfirmVisable = false; |
| | | this.registerSuccessConfirmVisable = false; |
| | | this.redirect(); |
| | | } |
| | | |
| | | //////////////////// 註å |
| | | applyAccount(): void { |
| | | if (this.applyAccount_onAction) { |
| | | return ; |
| | |
| | | }); |
| | | }; |
| | | |
| | | confirmApplySuccess(): void { |
| | | this.phoneSuccessConfirmVisable = false; |
| | | this.registerSuccessConfirmVisable = false; |
| | | this.redirect(); |
| | | } |
| | | |
| | | login() { |
| | | const login: LoginVerify = this.setLoginInfo(); |
| | | this.removeOtpTime(); |
| | | loginService.loginVerify(login).then(res => { |
| | | this.storageIdToken(res.id_token); |
| | | this.storageRole(Role.USER); |
| | | this.phoneSuccessConfirmVisable = true; |
| | | this.autoRedirect(); |
| | | this.storagePhoneOrEmail(this.setRegisterInfo()); |
| | | }).catch(error => { |
| | | this.checkHttpErrorStatus(error); |
| | | }); |
| | | } |
| | | |
| | | |
| | | ////////////////////////////////////////////////////////////////// |
| | | private checkHttpErrorStatus(error:any):void{ |
| | | switch (error.response.status) { |
| | | case 401: |
| | | const errorMsg = OtpErrorCode[error.response?.data?.detail] ? OtpErrorCode[error.response?.data?.detail]:'OTP系統é¯èª¤'; |
| | | messageBoxService.showErrorMessage(errorMsg); |
| | | break; |
| | | case 403: |
| | | this.registerDialogVisible = true; |
| | | setTimeout(() => { |
| | | const isScrollBarNeedless = this.contract.scrollHeight <= this.contract.clientHeight; |
| | | if (isScrollBarNeedless) { |
| | | this.isReadContract = true; |
| | | } |
| | | }, 1000); |
| | | break; |
| | | default: |
| | | messageBoxService.showErrorMessage('',error); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | private autoRedirect() { |
| | | this.autoRedirectInterval = setInterval(() => { |
| | | this.autoRedirectCounter -= 1; |
| | |
| | | find > -1 ? this.$router.go(-1) : this.$router.push('/'); |
| | | } |
| | | |
| | | private phoneDiffTime(parseOtpTime: any) { |
| | | const diffSecs = this.calcDiffSecs(parseOtpTime.time); |
| | | |
| | | if (diffSecs < this.otpCounterSec) { |
| | | this.otpResendCounter = diffSecs < 30 ? 30 - diffSecs : 0; |
| | | this.otpCounterSec -= diffSecs; |
| | | this.phoneNumber = parseOtpTime.phone; |
| | | this.onPhoneVerifyStep = 'INPUT_OTP'; |
| | | this.phoneOtpInfo = this.setOtpInfo(parseOtpTime); |
| | | this.startOtpCount('MOBILE'); |
| | | } else { |
| | | localStorage.removeItem('phoneOtpTime'); |
| | | detectContractReadStatus(event: any): void { |
| | | const scrollTop = Math.round(event.target.scrollTop); |
| | | const height = event.target.scrollHeight - event.target.clientHeight; |
| | | if (Math.floor(scrollTop/10) === (Math.floor(height/10))) { |
| | | this.isReadContract = true; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | private emailDiffTime(parseOtpTime: any) { |
| | | const diffSecs = this.calcDiffSecs(parseOtpTime.time); |
| | | //////////////////// ç¼é/éç¼/åªé¤é©è碼 |
| | | |
| | | if (diffSecs < this.emailCounterSec) { |
| | | this.emailResendCounter = diffSecs < 30 ? 30 - diffSecs : 0; |
| | | this.emailCounterSec -= diffSecs; |
| | | this.email = parseOtpTime.email; |
| | | this.onEmailVerifyResendStatus = 'CAN_RESEND'; |
| | | this.emailOtpInfo = this.setOtpInfo(parseOtpTime); |
| | | this.startOtpCount('EMAIL'); |
| | | } else { |
| | | localStorage.removeItem('emailOtpTime'); |
| | | applyOtpVerification(type: string): void { |
| | | const isMobile = this.connectDevice === 'MOBILE'; |
| | | const loginInfo: LoginRequest = { |
| | | loginType: isMobile ? 'SMS' : 'EMAIL', |
| | | account: isMobile ? this.phoneNumber : this.email, |
| | | } |
| | | } |
| | | |
| | | private calcDiffSecs(parseOtpTime) { |
| | | const currentTime = new Date().getTime(); |
| | | const storageTime = new Date(parseOtpTime).getTime(); |
| | | return Math.floor((currentTime - storageTime) / 1000); |
| | | } |
| | | |
| | | private resetOtpSetting(type: string) { |
| | | if (type === 'MOBILE') { |
| | | clearInterval(this.otpInterval); |
| | | this.otpResendCounter = 30; |
| | | this.otpCounterSec = 300; |
| | | } else { |
| | | clearInterval(this.emailResendInterval); |
| | | this.emailResendCounter = 30; |
| | | this.emailCounterSec = 300; |
| | | } |
| | | } |
| | | |
| | | private setOtpInfo(parseOtpTime) { |
| | | return { |
| | | indexKey: parseOtpTime.indexKey, |
| | | success: true, |
| | | failCode: '', |
| | | failReason: '', |
| | | } |
| | | } |
| | | loginService.sendOtp(loginInfo).then(otpInfo => { |
| | | if (otpInfo.success) { |
| | | this.storageOtpTime(type, otpInfo); |
| | | this.startOtpSetting(type); |
| | | this.startOtpCount(type); |
| | | } else { |
| | | const errorMsg = OtpErrorCode[otpInfo.failCode] ? OtpErrorCode[otpInfo.failCode]:'OTP系統é¯èª¤'; |
| | | messageBoxService.showErrorMessage(errorMsg); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | private storageOtpTime(type: string, otpInfo: OtpInfo) { |
| | | type === 'MOBILE' ? this.phoneOtpInfo = otpInfo : this.emailOtpInfo = otpInfo; |
| | | type === 'MOBILE' ? this.phoneOtpIndexKey = otpInfo.indexKey : this.emailOtpIndexKey = otpInfo.indexKey; |
| | | const info = {...this.setRegisterInfo(), time: new Date()} |
| | | type === 'MOBILE' ? localStorage.setItem('phoneOtpTime',JSON.stringify(info)) |
| | | : localStorage.setItem('emailOtpTime',JSON.stringify(info)); |
| | | const storageName = type === 'MOBILE' ? OtpStorageName.PHONE : OtpStorageName.EMAIL; |
| | | otpService.setOtpTimeToStorage(storageName, info); |
| | | } |
| | | |
| | | private startOtpSetting(type: string) { |
| | |
| | | }, 1000) |
| | | } |
| | | |
| | | private setRegisterInfo(): RegisterInfo { |
| | | return this.connectDevice === 'MOBILE' |
| | | ? { |
| | | phone: this.phoneNumber, |
| | | indexKey: this.phoneOtpInfo.indexKey, |
| | | otpCode: this.otpCode, |
| | | name: this.name, |
| | | contactType: 'SMS' |
| | | } |
| | | : { |
| | | email: this.email, |
| | | indexKey: this.emailOtpInfo.indexKey, |
| | | otpCode: this.otpCode, |
| | | name: this.name, |
| | | contactType: 'EMAIL' |
| | | } |
| | | resentOtp(type: string) { |
| | | this.resetOtpSetting(type); |
| | | this.applyOtpVerification(type); |
| | | } |
| | | |
| | | deleteOtpInfo(type: string) { |
| | | this.resetOtpSetting(type); |
| | | if (type === 'MOBILE') { |
| | | this.onPhoneVerifyStep = 'APPLY_OTP'; |
| | | this.phoneNumber = ''; |
| | | this.otpCode = ''; |
| | | } else { |
| | | this.onEmailVerifyResendStatus = 'APPLY_OTP'; |
| | | this.email = ''; |
| | | this.emailOtpCode = ''; |
| | | } |
| | | this.removeOtpTime(); |
| | | } |
| | | |
| | | private resetOtpSetting(type: string) { |
| | | if (type === 'MOBILE') { |
| | | clearInterval(this.otpInterval); |
| | | this.otpResendCounter = 30; |
| | | this.otpCounterSec = 300; |
| | | } else { |
| | | clearInterval(this.emailResendInterval); |
| | | this.emailResendCounter = 30; |
| | | this.emailCounterSec = 300; |
| | | } |
| | | } |
| | | |
| | | counterTime(counterSec) { |
| | | let min = Math.floor(counterSec / 60); |
| | | let sec = Math.floor(counterSec % 60); |
| | | return `${min < 10 ? '0' + min : min}:${sec < 10 ? '0' + sec : sec}`; |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////// |
| | | |
| | | private checkHttpErrorStatus(error:any):void{ |
| | | switch (error.response.status) { |
| | | case 401: |
| | | const errorMsg = OtpErrorCode[error.response?.data?.detail] ? OtpErrorCode[error.response?.data?.detail]:'OTP系統é¯èª¤'; |
| | | messageBoxService.showErrorMessage(errorMsg); |
| | | break; |
| | | case 403: |
| | | this.registerDialogVisible = true; |
| | | setTimeout(() => { |
| | | const isScrollBarNeedless = this.contract.scrollHeight <= this.contract.clientHeight; |
| | | if (isScrollBarNeedless) { |
| | | this.isReadContract = true; |
| | | } |
| | | }, 1000); |
| | | break; |
| | | default: |
| | | messageBoxService.showErrorMessage('',error); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | private storagePhoneOrEmail(registerInfo:RegisterInfo):void{ |
| | |
| | | } |
| | | |
| | | private removeOtpTime() { |
| | | localStorage.removeItem('emailOtpTime'); |
| | | localStorage.removeItem('phoneOtpTime'); |
| | | otpService.removeOtpTimeToStorage(OtpStorageName.PHONE); |
| | | otpService.removeOtpTimeToStorage(OtpStorageName.EMAIL); |
| | | } |
| | | |
| | | |
| | | private setLoginInfo() { |
| | | const isMobile = this.connectDevice === 'MOBILE' |
| | | return { |
| | | account: isMobile ? this.phoneNumber : this.email, |
| | | indexKey: isMobile ? this.phoneOtpInfo.indexKey : this.emailOtpInfo.indexKey, |
| | | indexKey: isMobile ? this.phoneOtpIndexKey : this.emailOtpIndexKey, |
| | | otpCode: isMobile ? this.otpCode : this.emailOtpCode |
| | | } |
| | | } |
| | | |
| | | private setRegisterInfo(): RegisterInfo { |
| | | return this.connectDevice === 'MOBILE' |
| | | ? { |
| | | phone: this.phoneNumber, |
| | | indexKey: this.phoneOtpIndexKey, |
| | | otpCode: this.otpCode, |
| | | name: this.name, |
| | | contactType: 'SMS' |
| | | } |
| | | : { |
| | | email: this.email, |
| | | indexKey: this.emailOtpIndexKey, |
| | | otpCode: this.emailOtpCode, |
| | | name: this.name, |
| | | contactType: 'EMAIL' |
| | | } |
| | | } |
| | | |
| | | get isSubmitBtnDisabled(): boolean { |
| | | return this.connectDevice === 'MOBILE' |
| | | ? (!this.otpCode || !this.phoneNumber || !this.phoneValid || !this.otpCounterSec) |
| | | : (!this.emailOtpCode || !this.email || !this.emailValid || !this.emailCounterSec) |
| | | } |
| | | |
| | | get showPhoneOtpCodeField(): boolean { |
| | | return this.connectDevice === 'MOBILE' && this.onPhoneVerifyStep === 'INPUT_OTP'; |
| | | }; |
| | | |
| | | get showEmailVerifyField(): boolean { |
| | | return this.connectDevice === 'EMAIL' && this.onEmailVerifyResendStatus === 'CAN_RESEND'; |
| | | }; |
| | | |
| | | get phoneValid() { |
| | | const rule = /^09[0-9]{8}$/; |
| | | return this.phoneNumber ? rule.test(this.phoneNumber) : true; |
| | | } |
| | | |
| | | get emailValid() { |
| | | const rule = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/; |
| | | return this.email ? rule.test(this.email) : true; |
| | | } |
| | | } |
| | | </script> |
| | | |
| | |
| | | <template> |
| | | <div> |
| | | <div class="pam-myAppointment-banner"></div> |
| | | <div class="pam-myAppointment-banner"></div> |
| | | <div class="pam-container"> |
| | | <div class="pam-cus-tabs mb-30"> |
| | | <div class="pam-cus-tabs mb-10"> |
| | | <div |
| | | class="cus-tab-item" |
| | | :class="{'is-active': activeTabName === 'appointmentList'}" |
| | | @click="clickTab('appointmentList')" |
| | | >客æ¶é ç´ |
| | | <span class="p">({{appointmentList.length}})</span> |
| | | > |
| | | <span class="smTxt">æªè¯çµ¡({{ appointmentItemSum }})</span> |
| | | </div> |
| | | <div |
| | | class="cus-tab-item" |
| | | :class="{'is-active': activeTabName === 'contactedList'}" |
| | | @click="clickTab('contactedList')" |
| | | >å·²è¯çµ¡ |
| | | <span class="p">({{contactedList.length}})</span> |
| | | > |
| | | <span class="smTxt">ç´è¨ªä¸({{ contactedItemSum }})</span> |
| | | </div> |
| | | <div |
| | | class="cus-tab-item" |
| | | :class="{'is-active': activeTabName === 'closedList'}" |
| | | @click="clickTab('closedList')" |
| | | > |
| | | <span class="smTxt">çµæ¡({{ closedItemSum }})</span> |
| | | </div> |
| | | </div> |
| | | |
| | |
| | | :isOpen.sync="showNewAppointmentHint" |
| | | > |
| | | <div class="text--center mdTxt"> |
| | | <p class="mb-50">ä½ æ <span class="text--primary">{{ newAppointmentSum }}</span> åæ°çé ç´</p> |
| | | <p class="mb-50">æ¨æ <span class="text--primary">{{ newAppointmentSum }}</span> åæ°çé ç´</p> |
| | | <div class="text--center"> |
| | | <el-button |
| | | type="primary" |
| | |
| | | |
| | | import * as _ from 'lodash'; |
| | | |
| | | import { ClientInfo } from '~/shared/models/client.model'; |
| | | import { Appointment } from '~/shared/models/appointment.model'; |
| | | import { ContactStatus } from '~/shared/models/enum/contact-status'; |
| | | |
| | | const localStorage = namespace('localStorage'); |
| | | const appointmentStore = namespace('appointment.store'); |
| | | |
| | | @Component({ |
| | | layout: 'home', |
| | |
| | | }) |
| | | export default class ClientReservedList extends Vue { |
| | | |
| | | @State('myAppointmentList') |
| | | myAppointmentList!: ClientInfo[]; |
| | | @appointmentStore.State('myAppointmentList') |
| | | myAppointmentList!: Appointment[]; |
| | | |
| | | @State('myNewAppointmentSum') |
| | | @appointmentStore.Getter('newAppointmentSum') |
| | | newAppointmentSum!: number; |
| | | |
| | | @Action |
| | | storeMyAppointmentList!: () => Promise<number>; |
| | | @appointmentStore.Getter('appointmentItemSum') |
| | | appointmentItemSum!: number; |
| | | |
| | | @appointmentStore.Getter('contactedItemSum') |
| | | contactedItemSum!: number; |
| | | |
| | | @appointmentStore.Getter('closedItemSum') |
| | | closedItemSum!: number; |
| | | |
| | | @appointmentStore.Action |
| | | getMyAppointmentList!: () => Promise<Appointment[]>; |
| | | |
| | | @localStorage.Mutation |
| | | storageClearAppointmentIdFromMsg!: () => void; |
| | |
| | | @localStorage.Getter |
| | | currentAppointmentIdFromMsg!: string; |
| | | |
| | | activeTabName : string = 'appointmentList'; |
| | | appointmentList : ClientInfo[] = []; |
| | | clients : ClientInfo[] = []; |
| | | contactedList : ClientInfo[] = []; |
| | | showNewAppointmentHint: boolean = false; |
| | | activeTabName : string = 'appointmentList'; |
| | | contactStatus = ContactStatus; |
| | | showNewAppointmentHint: boolean = false; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | this.storeMyAppointmentList(); |
| | | this.getMyAppointmentList(); |
| | | } |
| | | |
| | | destroyed() { |
| | |
| | | |
| | | @Watch('myAppointmentList') |
| | | onMyAppointmentListChange(): void { |
| | | this.contactedList = this.myAppointmentList |
| | | .filter(item => item.communicateStatus === 'contacted'); |
| | | |
| | | this.appointmentList = this.myAppointmentList |
| | | .filter(item => item.communicateStatus !== 'contacted'); |
| | | |
| | | if (this.currentAppointmentIdFromMsg) { |
| | | this.redirectAppointmentStatus(); |
| | | } |
| | |
| | | .findIndex(item => item.id === +this.currentAppointmentIdFromMsg); |
| | | if (currentAppointmentIndex > -1) { |
| | | const communicateStatus = this.myAppointmentList[currentAppointmentIndex].communicateStatus; |
| | | const pathName = communicateStatus === 'reserved' ? 'appointmentList' : 'contactedList'; |
| | | let pathName = 'closedList' |
| | | if (communicateStatus === this.contactStatus.RESERVED || communicateStatus === this.contactStatus.PICKED) { |
| | | pathName = 'contactedList'; |
| | | } |
| | | if (communicateStatus === this.contactStatus.CONTACTED) { |
| | | pathName = 'contactedList'; |
| | | } |
| | | this.$router.push( |
| | | { |
| | | path: '/myAppointmentList/' + pathName, |
| | |
| | | this.showNewAppointmentHint = this.newAppointmentSum > 0; |
| | | } |
| | | |
| | | @Watch('$route') |
| | | @Watch('$route', {immediate: true}) |
| | | onRouteChange() { |
| | | const routeFullName = this.$route.name; |
| | | if (routeFullName) { |
| | |
| | | |
| | | // format to {page}-banner or pam-no-banner tag |
| | | private routeFormatBannerClass(route: string): string { |
| | | const needBannerTags = ['myAppointmentList-appointmentList', 'myAppointmentList-contactedList']; |
| | | const needBannerTags = ['myAppointmentList-appointmentList', 'myAppointmentList-closedList']; |
| | | return _.includes(needBannerTags, route) ? route + '-banner' : 'pam-no-banner'; |
| | | }; |
| | | } |
| | |
| | | placeholder="è«è¼¸å
¥ééµå" |
| | | class="mb-30 pam-clientReserved-input" |
| | | v-model="keyWord" |
| | | @keyup.enter.native="search" |
| | | @input="search" |
| | | > |
| | | <i slot="suffix" class="icon-search search cursor--pointer" @click="search"></i> |
| | | <i slot="suffix" class="icon-search search cursor--pointer"></i> |
| | | </el-input> |
| | | |
| | | <ClientList |
| | |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, State, Watch, namespace } from 'nuxt-property-decorator'; |
| | | import { Vue, Component, Watch, namespace } from 'nuxt-property-decorator'; |
| | | |
| | | import { ClientInfo } from '~/shared/models/client.model'; |
| | | import { Appointment } from '~/shared/models/appointment.model'; |
| | | import { ContactStatus } from '~/shared/models/enum/contact-status'; |
| | | |
| | | const localStorage = namespace('localStorage'); |
| | | |
| | | const localStorage = namespace('localStorage'); |
| | | const appointmentStore = namespace('appointment.store'); |
| | | @Component |
| | | export default class ClientReservedList extends Vue { |
| | | |
| | | @State('myAppointmentList') |
| | | myAppointmentList!: ClientInfo[]; |
| | | @appointmentStore.State('myAppointmentList') |
| | | myAppointmentList!: Appointment[]; |
| | | |
| | | @localStorage.Getter |
| | | currentAppointmentIdFromMsg!: string; |
| | | |
| | | appointmentList: ClientInfo[] = []; |
| | | filterList : ClientInfo[] = []; |
| | | keyWord : string = ''; |
| | | pageList : ClientInfo[] = []; |
| | | currentPage : number = 1; |
| | | @appointmentStore.Action |
| | | getAppointmentDetail!: () => Promise<Appointment>; |
| | | |
| | | appointmentList: Appointment[] = []; |
| | | currentPage : number = 1; |
| | | filterList : Appointment[] = []; |
| | | keyWord : string = ''; |
| | | pageList : Appointment[] = []; |
| | | |
| | | contactStatus = ContactStatus; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | |
| | | |
| | | @Watch('myAppointmentList') |
| | | onMyAppointmentListChange(): void { |
| | | const unViewList = this.myAppointmentList |
| | | .filter((item) => item.communicateStatus !== 'contacted' && !item.consultantViewTime) |
| | | .map((item) => ({ ...item, sortTime: new Date(item.appointmentDate)})) |
| | | this.appointmentList = this.myAppointmentList |
| | | .filter(item => item.communicateStatus === this.contactStatus.RESERVED) |
| | | .map((item) => ({ ...item, sortTime: new Date(item.lastModifiedDate)})) |
| | | .sort((preItem, nextItem) => +nextItem.sortTime - +preItem.sortTime); |
| | | |
| | | const tempViewList = this.myAppointmentList |
| | | .filter(item => item.communicateStatus !== 'contacted' && item.consultantViewTime); |
| | | |
| | | // TODO: å¾çºå¦ééå° unreadList åæ´ç´°ç·»çæåºï¼åéè«å¾ç«¯æä¾å¤æ·ä¾æï¼ä¾å¦ï¼ createTimeï¼ã[Tomas, 2021/12/16];Ã¥ |
| | | const unreadList = tempViewList |
| | | .filter((item) => !item.consultantReadTime); |
| | | const readList = tempViewList |
| | | .filter((item) => item.consultantReadTime) |
| | | .map((item) => ({ ...item, sortTime: new Date(item.consultantReadTime)})) |
| | | .sort((preItem, nextItem) => +nextItem.sortTime - +preItem.sortTime); |
| | | |
| | | this.appointmentList = [...unViewList, ...unreadList, ...readList]; |
| | | this.filterList = this.appointmentList; |
| | | |
| | | this.getCurrentPage(); |
| | |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | search(): void { |
| | | this.filterList = this.appointmentList.filter(item => { |
| | | return item.name.match(this.keyWord) || item.requirement.match(this.keyWord) |
| | | }) |
| | | if (this.keyWord) { |
| | | this.filterList = this.appointmentList.filter(item => { |
| | | return item.name.match(this.keyWord) || item.requirement.match(this.keyWord); |
| | | }) |
| | | } else { |
| | | this.filterList = this.appointmentList; |
| | | } |
| | | |
| | | } |
| | | |
| | | changePage(pageList: ClientInfo[]): void { |
| | | changePage(pageList: Appointment[]): void { |
| | | this.pageList = pageList; |
| | | } |
| | | |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <div class="pam-closed-appointment-list"> |
| | | <el-input |
| | | type="text" |
| | | placeholder="è«è¼¸å
¥ééµå" |
| | | class="mb-10 pam-clientReserved-input" |
| | | v-model="keyWord" |
| | | @input="search" |
| | | > |
| | | <i |
| | | slot="suffix" |
| | | class="icon-search search cursor--pointer" |
| | | ></i> |
| | | </el-input> |
| | | |
| | | <div class="closed-appointment__tag-filter mb-10"> |
| | | <el-radio v-model="selectedClosedCategory" :label="'all'" border>å
¨é¨({{ itemSum }})</el-radio> |
| | | <el-radio v-model="selectedClosedCategory" :label="'done'" border>æäº¤({{ doneItemSum }})</el-radio> |
| | | <el-radio v-model="selectedClosedCategory" :label="'closed'" border>æªæäº¤({{ closedItemSum }})</el-radio> |
| | | </div> |
| | | |
| | | <ClientList |
| | | :clients="pageList" |
| | | :title="'closedList'" |
| | | ></ClientList> |
| | | |
| | | <UiPagination |
| | | :totalList="filterList" |
| | | :currentPage="currentPage" |
| | | @changePage="changePage" |
| | | ></UiPagination> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Watch, namespace } from 'nuxt-property-decorator'; |
| | | |
| | | import { Appointment } from '~/shared/models/appointment.model'; |
| | | import { ContactStatus } from '~/shared/models/enum/contact-status'; |
| | | |
| | | |
| | | const appointmentStore = namespace('appointment.store'); |
| | | const localStorage = namespace('localStorage'); |
| | | |
| | | @Component |
| | | export default class ClientClosedList extends Vue { |
| | | |
| | | @appointmentStore.State('myAppointmentList') |
| | | myAppointmentList!: Appointment[]; |
| | | |
| | | @localStorage.Getter |
| | | currentAppointmentIdFromMsg!: string; |
| | | |
| | | contactStatus= ContactStatus; |
| | | |
| | | closedItemSum = 0; |
| | | currentPage = 1; |
| | | doneItemSum = 0; |
| | | itemSum = 0; |
| | | keyWord = ''; |
| | | |
| | | closedList: Appointment[] = []; |
| | | filterList : Appointment[] = []; |
| | | pageList : Appointment[] = []; |
| | | selectedClosedCategory: 'all' | 'done' | 'closed' = 'all'; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | this.onMyAppointmentListChange(); |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | @Watch('myAppointmentList') |
| | | onMyAppointmentListChange() { |
| | | this.closedList = (this.myAppointmentList || []) |
| | | .filter(item => item.communicateStatus === this.contactStatus.DONE || item.communicateStatus === this.contactStatus.CLOSE || item.communicateStatus === this.contactStatus.CANCEL) |
| | | .map((item) => ({...item, sortTime: new Date(item.lastModifiedDate)})) |
| | | .sort((prevItem, nextItem) => +nextItem.sortTime - +prevItem.sortTime); |
| | | this.filterList = this.closedList; |
| | | this.itemSum = this.closedList.length; |
| | | this.doneItemSum = this.closedList.filter((item) => item.communicateStatus === this.contactStatus.DONE).length; |
| | | this.closedItemSum = this.closedList.filter((item) => item.communicateStatus === this.contactStatus.CLOSE).length; |
| | | this.getCurrentPage(); |
| | | } |
| | | |
| | | private getCurrentPage() { |
| | | const currentIndex = this.filterList.findIndex(item => item.id === +this.currentAppointmentIdFromMsg); |
| | | const pageSize = 5; |
| | | if (currentIndex > -1) { |
| | | this.currentPage = Math.ceil((currentIndex + 1) / pageSize); |
| | | } |
| | | } |
| | | |
| | | @Watch('selectedClosedCategory') |
| | | onSelectedClosedCategoryChanges() { |
| | | this.search(); |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | search(): void { |
| | | if (this.selectedClosedCategory === this.contactStatus.DONE) { |
| | | this.filterList = this.closedList.filter((item) => item.communicateStatus === this.contactStatus.DONE); |
| | | } else if (this.selectedClosedCategory === this.contactStatus.CLOSE) { |
| | | this.filterList = this.closedList.filter((item) => item.communicateStatus === this.contactStatus.CLOSE); |
| | | } else { |
| | | this.filterList = this.closedList; |
| | | } |
| | | |
| | | if (this.keyWord) { |
| | | this.filterList = this.filterList.filter(item => { |
| | | return item?.name?.match(this.keyWord) || item?.requirement?.match(this.keyWord) |
| | | }) |
| | | } |
| | | |
| | | } |
| | | |
| | | changePage(pageList: Appointment[]): void { |
| | | this.pageList = pageList; |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .pam-closed-appointment-list { |
| | | .closed-appointment__tag-filter { |
| | | display: flex; |
| | | .el-radio { |
| | | border-color: $LIGHT_GREY; |
| | | border-radius: 30px; |
| | | border-width: 1px; |
| | | font-size : 16px; |
| | | margin-left : 0 !important; |
| | | margin-right: 10px; |
| | | padding : 10px; |
| | | @extend .fix-chrome-click--issue; |
| | | &.is-checked { |
| | | background-color: $CORAL; |
| | | .el-radio__label { |
| | | color : $PRIMARY_WHITE !important; |
| | | } |
| | | } |
| | | .el-radio__input { |
| | | display: none; |
| | | } |
| | | .el-radio__label { |
| | | color : $PRIMARY_BLACK !important; |
| | | padding: 0px !important; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | placeholder="è«è¼¸å
¥ééµå" |
| | | class="mb-30 pam-clientReserved-input" |
| | | v-model="keyWord" |
| | | @keyup.enter.native="search" |
| | | @input="search" |
| | | > |
| | | <i |
| | | slot="suffix" |
| | | class="icon-search search cursor--pointer" |
| | | @click="search" |
| | | ></i> |
| | | </el-input> |
| | | |
| | | <div class="mb-10 sort-indicator-container"> |
| | | <a class="sort-indicator cursor--pointer" @click="changeSortType"> |
| | | {{ this.sortType === 'DESC' ? 'æ°->è' : 'è->æ°' }} |
| | | <i v-if="isSortType" class="icon-sort-add"></i> |
| | | <i v-else class="icon-sort-decrease"></i> |
| | | </a> |
| | | </div> |
| | | |
| | | <ClientList |
| | | :clients="pageList" |
| | | :title="'contactedList'" |
| | | class="mt-10" |
| | | ></ClientList> |
| | | |
| | | <UiPagination |
| | | v-if="togglePagination" |
| | | :totalList="filterList" |
| | | :currentPage="currentPage" |
| | | @changePage="changePage" |
| | |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Watch, State, namespace } from 'nuxt-property-decorator'; |
| | | import { Vue, Component, Watch, namespace } from 'nuxt-property-decorator'; |
| | | |
| | | import { ClientInfo } from '~/shared/models/client.model'; |
| | | import { Appointment } from '~/shared/models/appointment.model'; |
| | | |
| | | const localStorage = namespace('localStorage'); |
| | | |
| | | const appointmentStore = namespace('appointment.store'); |
| | | const localStorage = namespace('localStorage'); |
| | | |
| | | @Component |
| | | export default class ClientContactedList extends Vue { |
| | | |
| | | @State('myAppointmentList') |
| | | myAppointmentList!: ClientInfo[]; |
| | | @appointmentStore.State('myAppointmentList') |
| | | myAppointmentList!: Appointment[]; |
| | | |
| | | @localStorage.Getter |
| | | currentAppointmentIdFromMsg!: string; |
| | | |
| | | contactedList: ClientInfo[] = []; |
| | | filterList : ClientInfo[] = []; |
| | | keyWord : string = ''; |
| | | pageList : ClientInfo[] = []; |
| | | currentPage : number = 1; |
| | | contactedList : Appointment[] = []; |
| | | currentPage : number = 1; |
| | | filterList : Appointment[] = []; |
| | | keyWord : string = ''; |
| | | pageList : Appointment[] = []; |
| | | sortType : 'ASC' | 'DESC' = 'DESC'; |
| | | togglePagination: boolean = true; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | |
| | | |
| | | @Watch('myAppointmentList') |
| | | onMyAppointmentListChange() { |
| | | this.setContactedList(); |
| | | } |
| | | |
| | | @Watch('sortType') |
| | | onSortTypeChange() { |
| | | this.togglePagination = false; |
| | | setTimeout(() => { |
| | | this.togglePagination = true; |
| | | this.currentPage = 1; |
| | | this.setContactedList(); |
| | | }, 0) |
| | | } |
| | | |
| | | private setContactedList(): void { |
| | | this.contactedList = (this.myAppointmentList || []) |
| | | .filter(item => item.communicateStatus === 'contacted') |
| | | .map((item) => ({...item, sortTime: new Date(item.contactTime)})) |
| | | .sort((prevItem, nextItem) => +nextItem.sortTime - +prevItem.sortTime); |
| | | .map((item) => ({...item, sortTime: new Date(item.lastModifiedDate)})) |
| | | .sort((prevItem, nextItem) => { |
| | | return this.sortType === 'DESC' ? +nextItem.sortTime - +prevItem.sortTime : +prevItem.sortTime - +nextItem.sortTime |
| | | }); |
| | | this.filterList = this.contactedList; |
| | | |
| | | this.getCurrentPage(); |
| | | } |
| | | |
| | | private getCurrentPage() { |
| | | private getCurrentPage(): void { |
| | | const currentIndex = this.filterList.findIndex(item => item.id === +this.currentAppointmentIdFromMsg); |
| | | const pageSize = 5; |
| | | if (currentIndex > -1) { |
| | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | search(): void { |
| | | this.filterList = this.contactedList.filter(item => { |
| | | return item?.name?.match(this.keyWord) || item?.requirement?.match(this.keyWord) |
| | | }) |
| | | if (this.keyWord) { |
| | | this.filterList = this.contactedList.filter(item => { |
| | | return item?.name?.match(this.keyWord) || item?.requirement?.match(this.keyWord) |
| | | }) |
| | | } else { |
| | | this.filterList = this.contactedList; |
| | | } |
| | | } |
| | | |
| | | changePage(pageList: ClientInfo[]): void { |
| | | changePage(pageList: Appointment[]): void { |
| | | this.pageList = pageList; |
| | | } |
| | | |
| | | changeSortType(): void { |
| | | if (this.sortType === 'DESC') { |
| | | this.sortType = 'ASC'; |
| | | } else { |
| | | this.sortType = 'DESC'; |
| | | } |
| | | } |
| | | |
| | | get isSortType () :boolean { |
| | | return this.sortType === 'DESC'; |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | .sort-indicator-container{ |
| | | margin-bottom: 20px; |
| | | } |
| | | .sort-indicator{ |
| | | border-radius:30px; |
| | | border: 1px solid #D0D0CE; |
| | | background-color:#fff; |
| | | padding: 10px 20px; |
| | | color: #68737A; |
| | | } |
| | | </style> |
| | |
| | | |
| | | <script lang='ts'> |
| | | import { Vue, Component, Watch, State, Action } from 'nuxt-property-decorator'; |
| | | |
| | | import authService from '~/shared/services/auth.service'; |
| | | import { Consultant, ConsultantWithAppointmentId } from '~/shared/models/consultant.model'; |
| | | |
| | | @Component |
| | |
| | | .map((item) => ({ ...item, formatDate: new Date(item.updateTime || item.createTime)})) |
| | | .sort((preItem, nextItem) => +nextItem.formatDate - +preItem.formatDate ); |
| | | |
| | | // format contacted list |
| | | this.myConsultantList.filter((consultant) => consultant.appointments!.length) |
| | | .forEach((consultant) => { |
| | | consultant.appointments!.forEach((appointment) => { |
| | | const consultantWithAppointmentId: ConsultantWithAppointmentId = { |
| | | ...consultant, |
| | | appointmentId: appointment.id, |
| | | appointmentDate: appointment.appointmentDate, |
| | | appointmentScore: appointment.satisfactionScore, |
| | | appointmentStatus: appointment.communicateStatus, |
| | | }; |
| | | this.contactedList.push(consultantWithAppointmentId); |
| | | }) |
| | | }); |
| | | if (authService.isUserLogin()) { |
| | | this.myConsultantList.filter((consultant) => consultant.appointments!.length) |
| | | .forEach((consultant) => { |
| | | consultant.appointments!.forEach((appointment) => { |
| | | const consultantWithAppointmentId: ConsultantWithAppointmentId = { |
| | | ...consultant, |
| | | appointmentId: appointment.id, |
| | | appointmentDate: appointment.appointmentDate, |
| | | appointmentScore: appointment.satisfactionScore, |
| | | appointmentStatus: appointment.communicateStatus, |
| | | }; |
| | | this.contactedList.push(consultantWithAppointmentId); |
| | | }) |
| | | }); |
| | | |
| | | this.contactedList = this.contactedList |
| | | .filter((appointment) => appointment['appointmentStatus'] === 'contacted') |
| | | .map((appointment) => ({ ...appointment, sortTime: new Date(appointment.appointmentDate)})) |
| | | .sort((preAppointment, nextAppointment) => +nextAppointment.sortTime - +preAppointment.sortTime); |
| | | this.contactedList = this.contactedList |
| | | .filter((appointment) => appointment['appointmentStatus'] !== 'reserved') |
| | | .map((appointment) => ({ ...appointment, sortTime: new Date(appointment.appointmentDate)})) |
| | | .sort((preAppointment, nextAppointment) => +nextAppointment.sortTime - +preAppointment.sortTime); |
| | | } |
| | | |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | |
| | | <template> |
| | | <div>éç¥åè½ |
| | | <el-button @click="$router.push('/notification/detail')">éç¥ç´°ç¯</el-button> |
| | | <div> |
| | | <div |
| | | v-if="isUserLogin && unReviewLogList.length" |
| | | class="satisfaction-banner my-10 cursor--pointer" |
| | | @click="$router.push('/satisfactionList')" |
| | | > |
| | | <p class="satisfaction-text text--center">è«å¡«å¯«æ»¿æåº¦èª¿æ¥</p> |
| | | </div> |
| | | <el-row |
| | | v-for="(item, index) in notificationList" |
| | | :key="index" |
| | | type="flex" |
| | | justify="space-between" |
| | | align="middle" |
| | | class="notification-card" |
| | | > |
| | | <el-col class="unRead" :span="3" v-if="!item.readDate"></el-col> |
| | | <el-col :span="18"> |
| | | <p class="text">{{item.content}}</p> |
| | | </el-col> |
| | | <el-col :span="3" class="notification-period text--right"> |
| | | <div> |
| | | <UiDateFormat |
| | | class="date" |
| | | :date="item.createdDate" |
| | | onlyShowSection="DAY" /> |
| | | </div> |
| | | <div> |
| | | <UiDateFormat |
| | | class="time" |
| | | :date="item.createdDate" |
| | | onlyShowSection="TIME" /> |
| | | </div> |
| | | |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </template> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Component, State, Vue } from "nuxt-property-decorator"; |
| | | import { AppointmentLog } from "~/shared/models/appointment.model"; |
| | | import { NotificationList } from "~/shared/models/reviews.model"; |
| | | import authService from "~/shared/services/auth.service"; |
| | | import reviewsService from "~/shared/services/reviews.service"; |
| | | |
| | | @Component |
| | | export default class Notification extends Vue { |
| | | |
| | | @State |
| | | unReviewLogList!: AppointmentLog[]; |
| | | |
| | | @State |
| | | notificationList!: NotificationList[]; |
| | | |
| | | isUserLogin = false; |
| | | |
| | | //////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | this.isUserLogin = authService.isUserLogin(); |
| | | reviewsService.readAllMyNotification().then(res => res); |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .satisfaction-banner { |
| | | width: 100%; |
| | | height: 60px; |
| | | background-image: url('~/assets/images/satisfaction/satisfactionBtn_mob.svg'); |
| | | background-repeat: no-repeat; |
| | | background-size: cover; |
| | | background-position: center; |
| | | border-radius: 10px; |
| | | |
| | | .satisfaction-text { |
| | | @extend .mdTxt; |
| | | @extend .text--PRIMARY_WHITE; |
| | | line-height: 60px; |
| | | } |
| | | |
| | | @include desktop { |
| | | height: 110px; |
| | | background-image: url('~/assets/images/satisfaction/satisfactionBtn_web.svg'); |
| | | |
| | | .satisfaction-text { |
| | | font-size: 24px; |
| | | line-height: 110px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .notification-card { |
| | | padding: 10px; |
| | | border-bottom: solid 1px #CCCCCC; |
| | | |
| | | .unRead { |
| | | width: 10px; |
| | | height: 10px; |
| | | border-radius: 50px; |
| | | background-color: $PRIMARY_RED; |
| | | } |
| | | |
| | | .notification-period { |
| | | color: #707070; |
| | | .date { |
| | | font-size: 10px; |
| | | line-height: 12px; |
| | | } |
| | | .time { |
| | | font-size: 12px; |
| | | line-height: 14px; |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | .satisfaction-icon { |
| | | font-size: 24px; |
| | | @extend .cursor--pointer; |
| | | } |
| | | </style> |
| | |
| | | <div class="ques-header__input-block"> |
| | | <span>Emailï¼</span> |
| | | <input class="ques-header__input" |
| | | :class="{ 'is-invalid': !emailValid}" |
| | | placeholder="è«è¼¸å
¥" |
| | | v-model="myRequest.email"> |
| | | </div> |
| | | <div class="error mt-5 mb-5" style="margin-left:65px"> |
| | | <span v-show="!emailValid">Emailæ ¼å¼æèª¤</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | </PopUpFrame> |
| | | |
| | | <PopUpFrame :isOpen.sync="sendReserve" @update:isOpen="closeReservePopUp"> |
| | | <div class="text--middle mt-30 sendReserve-txt">é ç´æåï¼</div> |
| | | <div class="text--middle sendReserve-txt">æ¨é ç´çé¡§åæåéèæ¨è¯çµ¡ï¼</div> |
| | | <div class="mdTxt mt-30 sendReserve-txt">é ç´æåï¼</div> |
| | | <div class="mdTxt sendReserve-txt mb-30">æ¨é ç´çé¡§åæåéèæ¨è¯çµ¡ï¼</div> |
| | | <!-- TODO: æªä¸²æ¥ api, é±è平尿»¿æåº¦ --> |
| | | <!-- <div class="pam-app-review mb-10"> |
| | | <div class="mdTxt mb-10">å°æ¼ |
| | | <span class="mdTxt text--primary text--bold ">æååªå</span> |
| | | å¹³å°çæ´é«æåï¼ |
| | | </div> |
| | | <div class="mdTxt">æ¨çµ¦äºå¹¾é¡æè©å¹ï¼</div> |
| | | </div> |
| | | <el-rate v-model="score" class="pam-satisfaction-rate fix-chrome-click--issue"></el-rate> --> |
| | | <div class="text--center mdTxt"> |
| | | <!-- <el-button @click="closeReservePopUp">ç¥é</el-button> |
| | | <el-button type="primary" |
| | | @click="closeReservePopUp"> |
| | | éåº |
| | | </el-button> --> |
| | | <el-button type="primary" |
| | | @click="closeReservePopUp"> |
| | | æç¥éäº |
| | |
| | | const roleStorage = namespace('localStorage'); |
| | | @Component |
| | | export default class Questionnaire extends Vue { |
| | | @State('myConsultantList') myConsultantList!: Consultant[]; |
| | | @Action storeConsultantList!: () => Promise<number>; |
| | | @roleStorage.Getter isUserLogin!:boolean; |
| | | @roleStorage.State recommendConsultantItem!:string; |
| | | @State('myConsultantList') |
| | | myConsultantList!: Consultant[]; |
| | | |
| | | @Action |
| | | storeConsultantList!: () => Promise<number>; |
| | | |
| | | @roleStorage.Getter |
| | | isUserLogin!:boolean; |
| | | |
| | | @roleStorage.State |
| | | recommendConsultantItem!:string; |
| | | |
| | | score ="" ; |
| | | |
| | | genderOptions=[ |
| | | { |
| | |
| | | }, |
| | | { |
| | | title:'åç´
ä¿å®', |
| | | content:'åç´
ä¿å®æ¯å
¼å
·ã忤颍éªãèãç´
å©å
±äº«ãç¹è²çä¿å®ï¼å
·æä¸å®ç©©å®åº¦ï¼è®ä½ å¯ä»¥åæäº«æå£½éªä¿éåç´
å©ï¼' |
| | | content:'åç´
ä¿å®æ¯å
¼å
·ã忤颍éªãèãç´
å©å
±äº«ãç¹è²çä¿å®ï¼å
·æä¸å®ç©©å®åº¦ï¼è®æ¨å¯ä»¥åæäº«æå£½éªä¿éåç´
å©ï¼' |
| | | } |
| | | ]; |
| | | |
| | |
| | | appointmentId = 0; |
| | | appointmentTime = ''; |
| | | |
| | | //////////////////////////////////////////////////////////////////////////// |
| | | |
| | | beforeRouteEnter(to: any, from: any, next: any) { |
| | | next(vm => { |
| | | const isUserLogin = authService.isUserLogin(); |
| | |
| | | }) |
| | | } |
| | | |
| | | async fetch() { |
| | | if (authService.isUserLogin()) { |
| | | await this.storeConsultantList(); |
| | | }; |
| | | } |
| | | |
| | | mounted(): void { |
| | | if (authService.isUserLogin()) { |
| | | this.storeConsultantList(); |
| | | }; |
| | | this.setMyRequest(); |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | get phoneValid(): boolean { |
| | | const rule = /^09[0-9]{8}$/; |
| | | return this.myRequest.phone |
| | | ? rule.test(this.myRequest.phone) && _.isEqual(this.myRequest.phone.length,10) |
| | | : true; |
| | | //////////////////////////////////////////////////////////////////////////// |
| | | |
| | | @Watch('myConsultantList') |
| | | onMyConsultantListChange() { |
| | | if (authService.isUserLogin() && this.myConsultantList.length > 0) { |
| | | const editAppointment = this.getLatestReserved(this.$route.params.agentNo); |
| | | |
| | | if (editAppointment && editAppointment.agentNo) { |
| | | this.myRequest = JSON.parse(JSON.stringify(editAppointment)); |
| | | if (!this.$route.query || this.$route.query.edit !== 'true') { |
| | | this.isEditPopup = true; |
| | | } |
| | | this.isEditBtn = true; |
| | | } |
| | | } |
| | | } |
| | | |
| | | get userInfo(): RegisterInfo { |
| | | const initUserInfo = JSON.parse(localStorage.getItem('userInfo')!); |
| | | return initUserInfo; |
| | | private getLatestReserved(agentNo) { |
| | | const agentInfo = this.myConsultantList.filter(item => item.agentNo === agentNo); |
| | | const appointmentInfo = agentInfo.length > 0 && agentInfo[0].appointments |
| | | ? agentInfo[0].appointments! |
| | | .filter((appointment) => appointment.communicateStatus === 'reserved') |
| | | .map((reversedAppointment) => ( |
| | | { ...reversedAppointment, |
| | | sortDate: new Date(reversedAppointment.appointmentDate) |
| | | })) |
| | | .sort((preAppointment, nextAppointment) => +nextAppointment.sortDate - +preAppointment.sortDate)[0] |
| | | : null; |
| | | return this.getReservedData(appointmentInfo); |
| | | } |
| | | |
| | | get isDisabledSubmitBtn(): boolean { |
| | | return _.includes(this.myRequest.contactType,ContactType.PHONE) |
| | | ? !this.isHopeContactTimeDone() |
| | | : !this.phoneValid; |
| | | private getReservedData(appointmentInfo) { |
| | | if (appointmentInfo) { |
| | | const hopeContactTime = appointmentInfo!.hopeContactTime.split("'") |
| | | .filter(item => item && item !== ','); |
| | | this.getAppointmentId(appointmentInfo); |
| | | |
| | | return { |
| | | ...appointmentInfo, |
| | | hopeContactTime: hopeContactTime.map(item => { |
| | | const info = item.split('ã'); |
| | | return { |
| | | selectWeekOptions: info[0].split(','), |
| | | selectTimesOptions: info[1].split(',') |
| | | } |
| | | }), |
| | | requirement: appointmentInfo.requirement.split(',') |
| | | } |
| | | } else { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private isHopeContactTimeDone():boolean{ |
| | | return this.myRequest.hopeContactTime[0]?.selectWeekOptions.length >0 && this.myRequest.hopeContactTime[0]?.selectTimesOptions.length >0; |
| | | private getAppointmentId(appointmentInfo) { |
| | | this.appointmentId = appointmentInfo.id; |
| | | this.appointmentTime = appointmentInfo.lastModifiedDate |
| | | ? appointmentInfo.lastModifiedDate |
| | | : appointmentInfo.appointmentDate; |
| | | } |
| | | |
| | | //////////////////////////////////////////////////////////////////////////// |
| | | |
| | | sentDemand() { |
| | | if (this.isEditBtn) { |
| | | this.sentEditAppointmentDemand(); |
| | | this.editAppointmentDemand(); |
| | | } else { |
| | | queryConsultantService.addFavoriteConsultant([this.$route.params.agentNo]).then(res => this.sentAppointmentDemand()); |
| | | } |
| | | } |
| | | |
| | | private editAppointmentDemand() { |
| | | const info = { |
| | | ...this.myRequest, |
| | | requirement: _.map(this.myRequest.requirement,o=>o).toString(), |
| | | hopeContactTime: this.myRequest.phone && this.phoneValid ? this.getHopeContactTime() :'', |
| | | id: this.appointmentId, |
| | | otherRequirement: null |
| | | } |
| | | appointmentService.editAppointment(info).then(res => { |
| | | this.sendReserve = true; |
| | | this.myRequest.hopeContactTime = []; |
| | | setRequestsToStorage(this.myRequest); |
| | | }); |
| | | } |
| | | |
| | | private sentAppointmentDemand() { |
| | |
| | | }); |
| | | } |
| | | |
| | | private sentEditAppointmentDemand() { |
| | | const info = { |
| | | ...this.myRequest, |
| | | requirement: _.map(this.myRequest.requirement,o=>o).toString(), |
| | | hopeContactTime: this.myRequest.phone && this.phoneValid ? this.getHopeContactTime() :'', |
| | | id: this.appointmentId, |
| | | otherRequirement: null |
| | | } |
| | | appointmentService.editAppointment(info).then(res => { |
| | | this.sendReserve = true; |
| | | this.myRequest.hopeContactTime = []; |
| | | setRequestsToStorage(this.myRequest); |
| | | }); |
| | | } |
| | | |
| | | getHopeContactTime() { |
| | | private getHopeContactTime() { |
| | | const selectedHopeContactTime = this.myRequest.hopeContactTime.filter((i) => i.selectWeekOptions?.length && i.selectTimesOptions?.length); |
| | | return selectedHopeContactTime.map(i => { |
| | | return `'${i.selectWeekOptions}ã${i.selectTimesOptions}'`} |
| | |
| | | this.$router.push('/') |
| | | } |
| | | |
| | | private getLatestReserved(agentNo) { |
| | | const agentInfo = this.myConsultantList.filter(item => item.agentNo === agentNo); |
| | | //////////////////////////////////////////////////////////////////////////// |
| | | |
| | | const appointmentInfo = agentInfo.length > 0 && agentInfo[0].appointments |
| | | ? agentInfo[0].appointments! |
| | | .filter((appointment) => appointment.communicateStatus !== 'contacted') |
| | | .map((reversedAppointment) => { |
| | | return { |
| | | ...reversedAppointment, |
| | | sortDate: new Date(reversedAppointment.appointmentDate) |
| | | } |
| | | }) |
| | | .sort((preAppointment, nextAppointment) => +nextAppointment.sortDate - +preAppointment.sortDate)[0] |
| | | : null; |
| | | return this.getReservedData(appointmentInfo); |
| | | get phoneValid(): boolean { |
| | | const rule = /^09[0-9]{8}$/; |
| | | return this.myRequest.phone |
| | | ? rule.test(this.myRequest.phone) && _.isEqual(this.myRequest.phone.length,10) |
| | | : true; |
| | | } |
| | | |
| | | private getReservedData(appointmentInfo) { |
| | | if (appointmentInfo) { |
| | | const hopeContactTime = appointmentInfo!.hopeContactTime.split("'") |
| | | .filter(item => item && item !== ','); |
| | | this.getAppointmentId(appointmentInfo); |
| | | return { |
| | | age: appointmentInfo.age, |
| | | agentNo: appointmentInfo.agentNo, |
| | | contactType: appointmentInfo.contactType, |
| | | email: appointmentInfo.email || '', |
| | | gender: appointmentInfo.gender, |
| | | hopeContactTime: hopeContactTime.map(item => { |
| | | const info = item.split('ã'); |
| | | return { |
| | | selectWeekOptions: info[0].split(','), |
| | | selectTimesOptions: info[1].split(',') |
| | | } |
| | | }), |
| | | job: appointmentInfo.job, |
| | | phone: appointmentInfo.phone || '', |
| | | requirement: appointmentInfo.requirement.split(',') |
| | | } |
| | | } else { |
| | | return null; |
| | | } |
| | | get emailValid() { |
| | | const rule = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/; |
| | | return this.myRequest.email ? rule.test(this.myRequest.email) : true; |
| | | } |
| | | |
| | | private getAppointmentId(appointmentInfo) { |
| | | this.appointmentId = appointmentInfo.id; |
| | | this.appointmentTime = appointmentInfo.lastModifiedDate |
| | | ? appointmentInfo.lastModifiedDate |
| | | : appointmentInfo.appointmentDate; |
| | | get userInfo(): RegisterInfo { |
| | | const initUserInfo = JSON.parse(localStorage.getItem('userInfo')!); |
| | | return initUserInfo; |
| | | } |
| | | @Watch('myConsultantList') onMyConsultantListChange() { |
| | | if (authService.isUserLogin() && this.myConsultantList.length > 0) { |
| | | const editAppointment = this.getLatestReserved(this.$route.params.agentNo); |
| | | |
| | | if (editAppointment && editAppointment.agentNo) { |
| | | this.myRequest = JSON.parse(JSON.stringify(editAppointment)); |
| | | if (!this.$route.query || this.$route.query.edit !== 'true') { |
| | | this.isEditPopup = true; |
| | | } |
| | | this.isEditBtn = true; |
| | | return; |
| | | } |
| | | } |
| | | get isDisabledSubmitBtn(): boolean { |
| | | return _.includes(this.myRequest.contactType,ContactType.PHONE) |
| | | ? !this.isHopeContactTimeDone() || !this.emailValid |
| | | : !this.phoneValid; |
| | | } |
| | | |
| | | private isHopeContactTimeDone():boolean{ |
| | | return this.myRequest.hopeContactTime[0]?.selectWeekOptions.length >0 && this.myRequest.hopeContactTime[0]?.selectTimesOptions.length >0; |
| | | } |
| | | |
| | | } |
| | | </script> |
| | | |
| | |
| | | display: flex; |
| | | justify-content: center; |
| | | margin-top: 10px; |
| | | margin-bottom: 26px; |
| | | } |
| | | |
| | | //draweræåºä¸æåæ¨£å¼ |
| | |
| | | margin: 0px 20px; |
| | | } |
| | | |
| | | .pam-app-review{ |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | |
| | | .pam-satisfaction-rate{ |
| | | margin-bottom: 45px; |
| | | } |
| | | |
| | | @include desktop{ |
| | | .ques-header{ |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | </style> |
| | | |
| | |
| | | }, |
| | | { |
| | | title: 'åç´
ä¿å®', |
| | | content: 'åç´
ä¿å®æ¯å
¼å
·ã忤颍éªãèãç´
å©å
±äº«ãç¹è²çä¿å®ï¼å
·æä¸å®ç©©å®åº¦ï¼è®ä½ å¯ä»¥åæäº«æå£½éªä¿éåç´
å©ï¼' |
| | | content: 'åç´
ä¿å®æ¯å
¼å
·ã忤颍éªãèãç´
å©å
±äº«ãç¹è²çä¿å®ï¼å
·æä¸å®ç©©å®åº¦ï¼è®æ¨å¯ä»¥åæäº«æå£½éªä¿éåç´
å©ï¼' |
| | | } |
| | | ]; |
| | | showDialog = false; |
| | |
| | | } |
| | | |
| | | .down-icon { |
| | | color: #ED1B2E; |
| | | font-size: 25px; |
| | | align-self: center; |
| | | margin-right: 15px; |
| | | } |
| | | |
| | |
| | | <div class="pam-rec-agent-card__content"> |
| | | <div class="pam-rec-agent-card__content-header"> |
| | | <div class="pam-rec-agent-card__avatar"> |
| | | <UiAvatar :fileName="info.img" ></UiAvatar> |
| | | <UiAvatar |
| | | :agentNo="info.agentNo" > |
| | | </UiAvatar> |
| | | </div> |
| | | <div class="pam-rec-agent-card__main-info"> |
| | | <div class="text--middle pt-10 rec-desktop-name">{{ info.name }}</div> |
| | |
| | | <template> |
| | | <div> |
| | | <ReviewRecords |
| | | :myAppointmentReviewLogList="myAppointmentReviewLogList" |
| | | :reviewLogList="reviewLogList" |
| | | ></ReviewRecords> |
| | | </div> |
| | | </template> |
| | |
| | | @Component |
| | | export default class Reviews extends Vue{ |
| | | |
| | | @State('myAppointmentReviewLogList') |
| | | myAppointmentReviewLogList!: AppointmentLog[]; |
| | | |
| | | @Action |
| | | storeMyAppointmentReviewLog!: any; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | mounted() { |
| | | this.storeMyAppointmentReviewLog(); |
| | | } |
| | | @State('reviewLogList') |
| | | reviewLogList!: AppointmentLog[]; |
| | | |
| | | } |
| | | </script> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <template> |
| | | <div> |
| | | <div class="pam-satisfaction-banner"></div> |
| | | <div class="pam-container"> |
| | | <div class="satisfaction-title"> |
| | | <span class="mdTxt">滿æåº¦èª¿æ¥</span> |
| | | <span class="ml-10 text--prudential_grey smTxt_bold">å
± {{mapUnReviewLogList.length}} ç</span> |
| | | </div> |
| | | <template v-if="mapUnReviewLogList.length"> |
| | | <div class="satisfaction-card" v-for="(item, index) in mapUnReviewLogList" :key="index"> |
| | | <div class="satisfaction-card-content"> |
| | | <UiAvatar :size="80" :agentNo="item.agentNo"></UiAvatar> |
| | | <div class="satisfaction-card-text">å°æ¼é¡§å |
| | | <span class="text--primary text--bold">{{item.agentName}}</span> |
| | | çæ´é«æåï¼æ¨çµ¦äºå¹¾é¡æè©å¹ï¼ |
| | | </div> |
| | | </div> |
| | | <el-rate |
| | | v-model="item.satisfaction" |
| | | class="pam-satisfaction-rate mt-10 fix-chrome-click--issue" |
| | | @change="isBtnDisabled = false" |
| | | ></el-rate> |
| | | </div> |
| | | <div class="text--center mt-30" v-if="mapUnReviewLogList.length"> |
| | | <el-button type="primary" :disabled="isBtnDisabled" @click="sent">éåº</el-button> |
| | | </div> |
| | | </template> |
| | | <template v-else> |
| | | <div class="satisfaction-card"> |
| | | <div class="satisfaction-card-content"> |
| | | ç®åæ«ç¡éè¦æ¨å¡«å¯«ç滿æåº¦èª¿æ¥ |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </div> |
| | | |
| | | <PopUpFrame :isOpen.sync="showConfirmPopup" |
| | | @closePopUp="closePopup"> |
| | | <div class="text--center mdTxt">ç¼éæå</div> |
| | | <div class="text--center mt-30"> |
| | | <el-button @click="closePopup" type="primary">確å®</el-button> |
| | | </div> |
| | | </PopUpFrame> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { Vue, Component, Action, State, Watch } from 'nuxt-property-decorator'; |
| | | import { AppointmentLog } from '~/shared/models/appointment.model'; |
| | | import { UserReviewsConsultantsParams } from '~/shared/models/reviews.model'; |
| | | import reviewsService from '~/shared/services/reviews.service'; |
| | | |
| | | @Component({ |
| | | layout: 'home' |
| | | }) |
| | | export default class MySatisfactionList extends Vue { |
| | | |
| | | @State |
| | | unReviewLogList!: AppointmentLog[]; |
| | | |
| | | @Action |
| | | storeMyAppointmentReviewLog!: () => void; |
| | | |
| | | mapUnReviewLogList: AppointmentReviewLog[] = []; |
| | | showConfirmPopup = false; |
| | | isBtnDisabled = true; |
| | | |
| | | /////////////////////////////////////////////////////// |
| | | |
| | | @Watch('unReviewLogList') |
| | | onUnReviewLogListChange() { |
| | | this.mapUnReviewLogList = this.unReviewLogList.map(item => { |
| | | return { |
| | | ...item, |
| | | satisfaction: 0 |
| | | } |
| | | }) |
| | | } |
| | | |
| | | /////////////////////////////////////////////////////// |
| | | |
| | | sent() { |
| | | const reviewParams: UserReviewsConsultantsParams[] = this.mapUnReviewLogList |
| | | .filter(item => item.satisfaction > 0) |
| | | .map(item => { |
| | | return { |
| | | appointmentId: item.appointmentId, |
| | | score: item.satisfaction |
| | | } |
| | | }) |
| | | |
| | | reviewsService.allUserReviewsConsultants(reviewParams).then((res) => { |
| | | this.showConfirmPopup = true; |
| | | }); |
| | | } |
| | | |
| | | closePopup() { |
| | | this.showConfirmPopup = false; |
| | | this.storeMyAppointmentReviewLog(); |
| | | } |
| | | |
| | | } |
| | | |
| | | interface AppointmentReviewLog extends AppointmentLog { |
| | | satisfaction: number; |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .pam-satisfaction-banner { |
| | | width : 100%; |
| | | height : 120px; |
| | | background-size : cover; |
| | | background-repeat : no-repeat; |
| | | background-position: center; |
| | | position : relative; |
| | | background-image : url('~/assets/images/satisfaction/banner_mob.svg'); |
| | | } |
| | | |
| | | @media (min-width: 768px) { |
| | | .pam-satisfaction-banner { |
| | | height : 150px; |
| | | background-image: url('~/assets/images/satisfaction/banner_web.svg'); |
| | | } |
| | | } |
| | | |
| | | .pam-container { |
| | | margin: 30px 20px; |
| | | } |
| | | |
| | | @include desktop { |
| | | .pam-container { |
| | | width : 700px; |
| | | margin: 30px auto; |
| | | } |
| | | } |
| | | .satisfaction-card { |
| | | margin-top : 20px; |
| | | .satisfaction-card-content { |
| | | display : flex; |
| | | flex-direction : row; |
| | | justify-content: space-between; |
| | | .satisfaction-card-text { |
| | | width : 75%; |
| | | line-height : 28px; |
| | | align-self: center; |
| | | font-size: 20px; |
| | | padding-left: 10px; |
| | | } |
| | | |
| | | @include desktop { |
| | | justify-content: flex-start; |
| | | .satisfaction-card-text { |
| | | width : auto; |
| | | padding-left: 30px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div> |
| | | <ReviewRecords |
| | | :myAppointmentReviewLogList="myAppointmentReviewLogList" |
| | | :reviewLogList="reviewLogList" |
| | | ></ReviewRecords> |
| | | </div> |
| | | </template> |
| | |
| | | |
| | | @Component |
| | | export default class UserReviewsRecord extends Vue{ |
| | | @State('myAppointmentReviewLogList') |
| | | myAppointmentReviewLogList!: AppointmentLog[]; |
| | | |
| | | @Action |
| | | storeMyAppointmentReviewLog!: any; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | mounted() { |
| | | this.storeMyAppointmentReviewLog(); |
| | | } |
| | | @State('reviewLogList') |
| | | reviewLogList!: AppointmentLog[]; |
| | | |
| | | } |
| | | </script> |
¤ñ¹ï·sÀÉ®× |
| | |
| | | import Vue from 'vue' |
| | | import { appointmentFailReasonList } from '~/shared/const/appointment-fail-reason-list'; |
| | | |
| | | Vue.filter('toFailReasonLabel', (value: string): string => { |
| | | |
| | | if (!value || typeof value !== 'string') { |
| | | return '--'; |
| | | }; |
| | | |
| | | let failReasonLabel = {}; |
| | | appointmentFailReasonList.forEach((failReason) => { |
| | | failReasonLabel[failReason.value] = failReason.key; |
| | | }); |
| | | |
| | | return failReasonLabel[value]; |
| | | }) |
| | |
| | | if (isThisYear(date)) { |
| | | return isToday(date) |
| | | ? `ä»å¤© ${date.getHours()}:${minutes}` |
| | | : `${date.getMonth() + 1}æ${date.getDate()}æ¥ ${date.getHours()}:${minutes}`; |
| | | : `${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${minutes}`; |
| | | } else { |
| | | return `${date.getFullYear()}å¹´${date.getMonth() + 1}æ${date.getDate()}æ¥ ${date.getHours()}:${minutes}`; |
| | | return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${minutes}`; |
| | | }; |
| | | |
| | | }) |
¤ñ¹ï·sÀÉ®× |
| | |
| | | export const agentCommunicationStyleList = ['謹æ
å實', 'æå¿«ä¸»å', 'èå¿å¾è½', 'å¥è«é¢¨è¶£']; |
¤ñ¹ï·sÀÉ®× |
| | |
| | | export const agentExpertList = ['å¥åº·èä¿é', 'å女æè²', 'è³ç¢è¦å', 'æ¨æ´»éä¼', 'ä¿å®å¥æª¢/è¦å', 'åç´
ä¿å®']; |
¤ñ¹ï·sÀÉ®× |
| | |
| | | export const appointmentFailReasonList = [ |
| | | { |
| | | key: 'ç¡æ³è¯ç¹«å®¢æ¶', |
| | | value: 'cannot_to_contact_customer' |
| | | }, |
| | | { |
| | | key: 'å®ç´è«®è©¢', |
| | | value: 'only_consultation' |
| | | }, |
| | | { |
| | | key: 'ç¡åé©åå', |
| | | value: 'no_suitable_commodity' |
| | | }, |
| | | { |
| | | key: 'æ ¸ä¿åé¡- 髿³ã財åãè·æ¥', |
| | | value: 'prohibited_factors' |
| | | }, |
| | | { |
| | | key: 'ç¶æ¿å ç´ ', |
| | | value: 'economy' |
| | | }, |
| | | { |
| | | key: 'å
¶ä»', |
| | | value: 'other' |
| | | }, |
| | | ]; |
| | |
| | | export interface UserSetting { |
| | | name: string; |
| | | name : string; |
| | | phone?: string; |
| | | email?: string; |
| | | } |
| | | } |
| | | |
| | | export interface AgentInfoSetting{ |
| | | agentNo : string; |
| | | name : string; |
| | | expertise : string[]; |
| | | title : string; |
| | | role : string; |
| | | serveArea : string; |
| | | gender : string; |
| | | phoneNumber : string; |
| | | companyAddress : string; |
| | | seniorityYear : number; |
| | | seniorityMonth : number; |
| | | concept : string; |
| | | experiences : string; |
| | | awards : string; |
| | | communicationStyle: string; |
| | | photoBase64 : string; |
| | | } |
| | |
| | | export interface AgentInfo { |
| | | name : string; |
| | | agentNo : string; |
| | | role : string; |
| | | img : string; |
| | | avgScore : number; |
| | | title : string; |
| | | phoneNumber : string; |
| | | serveArea : string; |
| | | companyAddress : string; |
| | | seniority : string; |
| | | suitability : number; |
| | | evaluation : number; |
| | | expertise : string[]; |
| | | concept : string; |
| | | experiences : string[]; |
| | | awards : string; |
| | | agentNo : string; |
| | | avgScore : number; |
| | | awards : string; |
| | | communicationStyle: string; |
| | | companyAddress : string; |
| | | concept : string; |
| | | email? : string; |
| | | evaluation : number; |
| | | experiences : string; |
| | | expertise : string[]; |
| | | gender : string, |
| | | img : string; |
| | | latestLoginTime : Date ; |
| | | name : string; |
| | | phoneNumber : string; |
| | | role : string; |
| | | seniority : string; |
| | | serveArea : string; |
| | | suitability : number; |
| | | title : string; |
| | | } |
| | |
| | | import { ContactStatus } from "./enum/contact-status"; |
| | | |
| | | export interface AppointmentLog { |
| | | id : number, |
| | | createdDate : Date, |
| | | lastModifiedDate: Date, |
| | | customerId : number, |
| | | agentNo : string, |
| | | status : 'UNFILLED' | 'FILLED', |
| | | score : number, |
| | | agentName : string, |
| | | customerName : string, |
| | | } |
| | | export interface Appointment { |
| | | id : number; |
| | | phone? : string; |
| | | email? : string; |
| | | contactType : string; |
| | | gender : string; |
| | | age : string; |
| | | job : string; |
| | | requirement : string; |
| | | communicateStatus : string; |
| | | hopeContactTime : string; |
| | | otherRequirement? : string; |
| | | appointmentDate : string; |
| | | lastModifiedDate : string; |
| | | agentNo : string; |
| | | customerId : number; |
| | | name : string; |
| | | consultantViewTime?: string; |
| | | consultantReadTime?: string; |
| | | satisfactionScore? : number; |
| | | }; |
| | | export interface AppointmentWithConsultantInfo extends Appointment { |
| | | consultantName : string; |
| | | consultantAvatar : string; |
| | | consultantExpertList: string[] |
| | | updateTime : Date | string; |
| | | contactStatus : string; |
| | | agentName : string; |
| | | agentNo : string; |
| | | appointmentId : number; |
| | | createdDate : string; |
| | | customerId : number; |
| | | customerName : string; |
| | | id : number; |
| | | lastModifiedDate: string; |
| | | score : number; |
| | | status : 'UNFILLED' | 'FILLED'; |
| | | } |
| | | |
| | | export interface AppointmentDetail { |
| | | id : number; |
| | | phone : string; |
| | | email : string; |
| | | contactType : string; |
| | | gender : string; |
| | | age : string; |
| | | job : string; |
| | | requirement : string; |
| | | communicateStatus: string; |
| | | hopeContactTime : string; |
| | | otherRequirement : string; |
| | | appointmentDate : string; |
| | | agentNo : string; |
| | | customerId : number; |
| | | name : string; |
| | | export interface Appointment { |
| | | age : string; |
| | | agentNo : string; |
| | | appointmentClosedInfo: AppointmentClosedInfo; |
| | | appointmentDate : string; |
| | | appointmentMemoList : AppointmentMemoInfo[] |
| | | appointmentNoticeLogs: NoticeLogs[]; |
| | | communicateStatus : ContactStatus; |
| | | consultantReadTime : string; |
| | | consultantViewTime : string; |
| | | contactTime : string; |
| | | contactType : string; |
| | | customerId : number; |
| | | email : string; |
| | | gender : string; |
| | | hopeContactTime : string; |
| | | id : number; |
| | | interviewRecordDTOs : InterviewRecord[]; |
| | | job : string; |
| | | lastModifiedDate : string; |
| | | name : string; |
| | | otherRequirement : string; |
| | | phone : string; |
| | | requirement : string; |
| | | satisfactionScore : number; |
| | | }; |
| | | |
| | | export interface AppointmentClosedInfo { |
| | | appointmentId : number; |
| | | closedOtherReason : string; |
| | | closedReason : string; |
| | | id : number; |
| | | planCode : string; |
| | | policyEntryDate : string; |
| | | policyholderIdentityId: string; |
| | | remark : string; |
| | | } |
| | | |
| | | export interface AppointmentMemoInfo { |
| | | appointmentId: number; |
| | | content : string; |
| | | id : number; |
| | | } |
| | | |
| | | export interface InterviewRecord { |
| | | appointmentId : number; |
| | | content : string; |
| | | createdBy : string; |
| | | createdDate : string; |
| | | id : number; |
| | | interviewDate : string; |
| | | lastModifiedBy : string; |
| | | lastModifiedDate: string; |
| | | } |
| | | |
| | | export interface NoticeLogs { |
| | | appointmentId: number, |
| | | content : string, |
| | | createdDate : string |
| | | email : string, |
| | | id : number, |
| | | phone : string, |
| | | } |
| | | |
| | | export interface AppointmentWithConsultantInfo extends Appointment { |
| | | consultantAvatar : string; |
| | | consultantExpertList: string[]; |
| | | consultantName : string; |
| | | contactStatus : string; |
| | | updateTime : string; |
| | | } |
| | | |
| | | export interface AppointmentParams { |
| | | phone : string; |
| | | email : string; |
| | | contactType : string; |
| | | gender : string; |
| | | age : string; |
| | | job : string; |
| | | requirement : string; |
| | | hopeContactTime: string; |
| | | agentNo : string; |
| | | contactType : string; |
| | | email : string; |
| | | gender : string; |
| | | hopeContactTime: string; |
| | | job : string; |
| | | phone : string; |
| | | requirement : string; |
| | | } |
| | | |
| | | export interface EditAppointmentParams { |
| | | id : number, |
| | | phone : string, |
| | | email : string, |
| | | contactType : string, |
| | | gender : string, |
| | | age : string, |
| | | job : string, |
| | | requirement : string, |
| | | hopeContactTime : string, |
| | | otherRequirement: null |
| | | age : string; |
| | | contactType : string; |
| | | email : string; |
| | | gender : string; |
| | | hopeContactTime : string; |
| | | id : number; |
| | | job : string; |
| | | otherRequirement: null; |
| | | phone : string; |
| | | requirement : string; |
| | | } |
| | | |
| | | export interface AppointmentRequests { |
| | | phone : string, |
| | | email : string, |
| | | contactType : string, |
| | | gender : string, |
| | | age : string, |
| | | job : string, |
| | | requirement : string[], |
| | | hopeContactTime: ContactTime[], |
| | | agentNo : string, |
| | | age : string; |
| | | agentNo : string; |
| | | contactType : string; |
| | | email : string; |
| | | gender : string; |
| | | hopeContactTime: ContactTime[]; |
| | | job : string; |
| | | phone : string; |
| | | requirement : string[]; |
| | | } |
| | | |
| | | export interface ContactTime { |
| | | selectWeekOptions : string[], |
| | | selectTimesOptions: string[] |
| | | selectTimesOptions: string[]; |
| | | selectWeekOptions : string[]; |
| | | } |
| | | |
| | | export interface createdMemoInfo { |
| | | appointmentId: number; |
| | | content : string; |
| | | } |
| | | |
| | | export interface updatedMemoInfo { |
| | | content: string; |
| | | id : number; |
| | | } |
| | | |
| | | export interface ToDoneAppointment { |
| | | appointmentId : number; |
| | | contactStatus : ContactStatus; |
| | | planCode : string; |
| | | policyEntryDate : string; |
| | | policyholderIdentityId: string; |
| | | remark? : string; |
| | | } |
| | | |
| | | export interface ToCloseAppointment { |
| | | appointmentId : number; |
| | | closedOtherReason: string; |
| | | closedReason : string; |
| | | contactStatus : ContactStatus; |
| | | remark? : string; |
| | | } |
| | | |
| | | export interface NoticeLogs { |
| | | appointmentId: number; |
| | | content : string; |
| | | createdDate : string; |
| | | email : string; |
| | | id : number; |
| | | interviewDate: string; |
| | | phone : string; |
| | | } |
| | | |
| | | export interface ToInformAppointment { |
| | | appointmentId: number; |
| | | email : string; |
| | | interviewDate: string; |
| | | message : string; |
| | | phone : string; |
| | | } |
| | | |
| | | export interface InterviewRecordInfo { |
| | | content: string; |
| | | interviewDate: string; |
| | | appointmentId: number; |
| | | } |
| | | |
| | | export interface UpdateInterviewRecordInfo { |
| | | /** interviewRecord id */ |
| | | id: number; |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | export enum ContactStatus { |
| | | PICKED = 'picked', |
| | | RESERVED = 'reserved', |
| | | CONTACTED = 'contacted', |
| | | DONE = 'done', |
| | | CLOSE = 'closed', |
| | | CANCEL = 'cancel', |
| | | } |
| | |
| | | appointmentId: number, |
| | | score : number, |
| | | } |
| | | |
| | | export interface NotificationList { |
| | | id: number; |
| | | /** æ¨é¡ */ |
| | | title: string; |
| | | /** éç¥å
§å®¹ */ |
| | | content: string; |
| | | /** ACTIVITY(å人活åéç¥)ãSYSTEM(系統éç¥) */ |
| | | notificationType: string; |
| | | /** CUSTOMER(客æ¶)ãCONSULTANT(é¡§å) */ |
| | | ownerRole: string; |
| | | /** ç»å
¥è
id */ |
| | | ownerId: number |
| | | createdDate: string; |
| | | /** å·²è®æé */ |
| | | readDate: string; |
| | | } |
| | |
| | | |
| | | export interface StrictQueryParams { |
| | | gender : string; |
| | | avgScore : number; |
| | | status : string; //phase 1 disable |
| | | area : string; |
| | | requirements : string[]; |
| | | otherRequirement: string; |
| | | seniority : string; |
| | | popularTags : string[]; |
| | | otherPopularTags: string; |
| | | gender? : string; |
| | | avgScore? : number; |
| | | status? : string; //phase 1 disable |
| | | area? : string; |
| | | requirements? : string[]; |
| | | otherRequirement?: string; |
| | | seniority? : string; |
| | | popularTags? : string[]; |
| | | otherPopularTags?: string; |
| | | } |
| | | |
| | | export interface AgentOfStrictQuery { |
| | |
| | | import { http } from "./httpClient"; |
| | | |
| | | import { UserSetting } from "~/shared/models/account.model"; |
| | | import { AgentInfoSetting, UserSetting } from "~/shared/models/account.model"; |
| | | |
| | | class AccountSettingService{ |
| | | |
| | |
| | | return http.put('/customer/info', params ).then(res => res.data); |
| | | } |
| | | |
| | | //編輯顧å帳èè³è¨ |
| | | async editAgentInfoSetting(params:any): Promise<AgentInfoSetting>{ |
| | | return http.post('/consultant/edit',params).then(res => res.data); |
| | | } |
| | | |
| | | } |
| | | export default new AccountSettingService(); |
| | |
| | | import { http } from "./httpClient"; |
| | | |
| | | import { ClientInfo } from "~/shared/models/client.model"; |
| | | import { AppointmentDetail, EditAppointmentParams } from "~/shared/models/appointment.model"; |
| | | import { Appointment, AppointmentMemoInfo, createdMemoInfo, EditAppointmentParams, InterviewRecordInfo, ToCloseAppointment, ToDoneAppointment, ToInformAppointment, updatedMemoInfo, UpdateInterviewRecordInfo } from "~/shared/models/appointment.model"; |
| | | |
| | | class AppointmentService { |
| | | |
| | | // å徿æé ç´æ¸
å® |
| | | async getMyAppointmentList(): Promise<ClientInfo[]> { |
| | | // é¡§åå徿æèªå·±çé ç´å®API |
| | | async getMyAppointmentList(): Promise<Appointment[]> { |
| | | return http.get('/consultant/getMyAppointment').then((res) => { |
| | | const hasNewAppointment = res.data.find((appointment: ClientInfo) => !appointment.consultantViewTime); |
| | | const hasNewAppointment = res.data.find((appointment: Appointment) => !appointment.consultantViewTime); |
| | | if (hasNewAppointment) { |
| | | this.viewAllAppointment(); |
| | | } |
| | |
| | | }); |
| | | } |
| | | |
| | | // é¡§åç»å
¥é¡¯ç¤ºæ°é ç´å®çæ¸å¾è§¸ç¼ |
| | | // é¡§åç覽èªå·±ææçé ç´å®ç´é觸ç¼API |
| | | private viewAllAppointment(): void { |
| | | http.post('/consultant/record/allAppointmentsView').then(); |
| | | } |
| | |
| | | } |
| | | |
| | | // åå¾é ç´å®ç´°ç¯ |
| | | async getAppointmentDetail(appointmentId: number):Promise<AppointmentDetail> { |
| | | async getAppointmentDetail(appointmentId: number):Promise<Appointment> { |
| | | return http.get(`/appointment/getDetail/${appointmentId}`).then((res) => res.data); |
| | | } |
| | | |
| | |
| | | return http.put('/appointment', editAppointmentParams); |
| | | } |
| | | |
| | | // æ°å¢è¨»è¨ |
| | | async createMemo(memoInfo: createdMemoInfo): Promise<AppointmentMemoInfo> { |
| | | return http.post('/appointment/memo/create', memoInfo).then(res => res.data); |
| | | } |
| | | |
| | | // ç·¨è¼¯è¨»è¨ |
| | | async updateMemo(memoInfo: updatedMemoInfo): Promise<AppointmentMemoInfo> { |
| | | return http.post('/appointment/memo/update', memoInfo).then(res => res.data); |
| | | } |
| | | |
| | | // åªé¤è¨»è¨ |
| | | deleteMemo(appointmentMemoId: number) { |
| | | return http.delete(`/appointment/memo/${appointmentMemoId}`) |
| | | } |
| | | |
| | | // é ç´å®çµæ¡, æ´æ°çµæ¡æç´° |
| | | async closeAppointment(appointmentInfo: ToDoneAppointment | ToCloseAppointment) { |
| | | return http.post(`/appointment/close`, appointmentInfo).then((res) => res.data); |
| | | } |
| | | |
| | | // ç´è¨ªéç¥ API |
| | | async informAppointment(appointmentInformation: ToInformAppointment) { |
| | | return http.post(`/notice/send`, appointmentInformation).then((res) => res.data); |
| | | } |
| | | |
| | | // æ°å¢ç´è¨ªè¨é |
| | | async createInterviewRecord(interviewRecordInfo: InterviewRecordInfo) { |
| | | return http.post('/interview_record/create', interviewRecordInfo).then(res => res.data); |
| | | } |
| | | |
| | | // ä¿®æ¹ç´è¨ªè¨é |
| | | async updateInterviewRecord(updateInterviewRecordInfo: UpdateInterviewRecordInfo) { |
| | | return http.post('/interview_record/update', updateInterviewRecordInfo) |
| | | } |
| | | |
| | | // åªé¤ç´è¨ªè¨é |
| | | async deleteInterviewRecord(interviewRecordId) { |
| | | return http.delete(`/interview_record/${interviewRecordId}`); |
| | | } |
| | | } |
| | | |
| | | export default new AppointmentService(); |
| | |
| | | withCredentials: true |
| | | }); |
| | | |
| | | let apiNumber = 0; |
| | | |
| | | http.interceptors.request.use( |
| | | (config: AxiosRequestConfig) => { |
| | | apiNumber += 1; |
| | | loadingStart(); |
| | | addHttpHeader(config); |
| | | return config; |
| | |
| | | |
| | | http.interceptors.response.use( |
| | | (response: AxiosResponse) => { |
| | | loadingFinish(); |
| | | apiNumber -= 1; |
| | | if (apiNumber === 0) { |
| | | loadingFinish(); |
| | | } |
| | | return response; |
| | | }, |
| | | (error: AxiosError) => { |
| | | loadingFinish(); |
| | | apiNumber -= 1; |
| | | if (apiNumber === 0) { |
| | | loadingFinish(); |
| | | } |
| | | showErrorMessageBox(error) |
| | | return Promise.reject(error); |
| | | } |
| | |
| | | import { http } from "./httpClient"; |
| | | import _ from "lodash"; |
| | | |
| | | import { http } from "./httpClient"; |
| | | import { AgentInfo } from '~/shared/models/agent-info.model'; |
| | | import { Consultant } from "../models/consultant.model"; |
| | | |
| | | import { Appointment } from "../models/appointment.model"; |
| | | class MyConsultantService { |
| | | |
| | | async getFavoriteConsultantList(): Promise<Consultant[]> { |
| | |
| | | } |
| | | |
| | | // æ¨è¨çºå·²è¯çµ¡ |
| | | markAsContact(appointmentId: number): Promise<void> { |
| | | // TODO: è·å¾ç«¯ç¢ºèªï¼é裡ç API ä¸æè©²å³å void, èæ¯æè©²æ¯æ´æ°å¾çè³æ - Ben 2021/11/16 |
| | | // return http.post('/appointment/markAsContacted/'+appointmentId, undefined, {headers}) |
| | | // .then(res => res.data) |
| | | return http.post(`/appointment/markAsContacted/${appointmentId}`); |
| | | markAsContact(appointmentId: number): Promise<Appointment> { |
| | | return http.post(`/appointment/markAsContacted/${appointmentId}`).then(res => res.data); |
| | | } |
| | | |
| | | // åå¾é¡§åé å |
| | | getConsultantAvatar(agentNo:string):Promise<string>{ |
| | | return http.get(`/consultant/avatar/${agentNo}`,{ responseType : 'arraybuffer' }) |
| | | .then( response => { |
| | | const toBase64 = window.btoa( |
| | | _.reduce( new Uint8Array(response.data),(data,byte) => |
| | | data + String.fromCharCode(byte),'') |
| | | ); |
| | | const imgSrc = `data:image/png;base64,${toBase64}`; |
| | | return imgSrc; |
| | | }); |
| | | } |
| | | } |
| | | |
| | | export default new MyConsultantService(); |
¤ñ¹ï·sÀÉ®× |
| | |
| | | import { RegisterInfo } from "../models/registerInfo"; |
| | | |
| | | class OtpService { |
| | | |
| | | setOtpTimeToStorage(name: string, info) { |
| | | localStorage.setItem(name,JSON.stringify(info)); |
| | | } |
| | | |
| | | getOtpTime(name: string) { |
| | | return localStorage.getItem(name); |
| | | } |
| | | |
| | | removeOtpTimeToStorage(name: string) { |
| | | localStorage.removeItem(name); |
| | | } |
| | | |
| | | parseOtpTime(name): OtpTime | null { |
| | | const otpTime = this.getOtpTime(name); |
| | | return otpTime ? JSON.parse(otpTime) : null; |
| | | } |
| | | |
| | | diffOtpTime(storageName: string, otpCounterSec: number) { |
| | | const parseOtpTime = this.parseOtpTime(storageName); |
| | | |
| | | if (parseOtpTime) { |
| | | const diffSecs = this.calcDiffSecs(parseOtpTime.time); |
| | | |
| | | if (diffSecs < otpCounterSec) { |
| | | return diffSecs; |
| | | } else { |
| | | this.removeOtpTimeToStorage(storageName); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | private calcDiffSecs(parseOtpTime) { |
| | | const currentTime = new Date().getTime(); |
| | | const storageTime = new Date(parseOtpTime).getTime(); |
| | | return Math.floor((currentTime - storageTime) / 1000); |
| | | } |
| | | |
| | | } |
| | | |
| | | export default new OtpService(); |
| | | |
| | | |
| | | export interface OtpTime extends RegisterInfo { |
| | | time: string; |
| | | } |
| | | |
| | | export enum OtpStorageName { |
| | | EMAIL = 'emailOtpTime', |
| | | PHONE = 'phoneOtpTime' |
| | | } |
| | |
| | | import { http } from "./httpClient"; |
| | | |
| | | import { UserReviewsConsultantsParams } from "../models/reviews.model"; |
| | | import { NotificationList, UserReviewsConsultantsParams } from "../models/reviews.model"; |
| | | import { AppointmentLog } from "../models/appointment.model"; |
| | | |
| | | class ReviewsService { |
| | | |
| | | //客æ¶é²è¡æ»¿æåº¦è©å |
| | | //客æ¶é²è¡æ»¿æåº¦è©å(å®ç) |
| | | userReviewsConsultants(data: UserReviewsConsultantsParams) { |
| | | return http.post('/satisfaction/create', data ); |
| | | return http.post('/satisfaction/score', data); |
| | | } |
| | | |
| | | // 客æ¶é²è¡æ»¿æåº¦(å¤ç) |
| | | allUserReviewsConsultants(data: UserReviewsConsultantsParams[]) { |
| | | return http.post('/satisfaction/score/all', data); |
| | | } |
| | | |
| | | |
| | | //å徿æè©åç´é |
| | | async getMyReviewLog(): Promise<AppointmentLog[]> { |
| | | return http.get('/satisfaction/getMySatisfaction').then(res => res.data); |
| | | } |
| | | |
| | | // é¡§å主åç¼é滿æåº¦éç¥ |
| | | sendSatisfactionToClient(appointmentId: number) { |
| | | return http.post(`/consultant/sendSatisfactionToClient/${appointmentId}`).then((res) => res); |
| | | } |
| | | |
| | | // éç¥å°é´éº |
| | | getMyPersonalNotification(): Promise<NotificationList[]> { |
| | | return http.get('/personal_notification/getMyPersonalNotification').then(res => res.data); |
| | | } |
| | | |
| | | // ç®åç»å
¥è
çææå°é´éºéç¥è¨å®çºå·²è® |
| | | readAllMyNotification() { |
| | | return http.post('/personal_notification/readAllMyNotification') |
| | | } |
| | | } |
| | | |
| | | export default new ReviewsService(); |
¤ñ¹ï·sÀÉ®× |
| | |
| | | import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators' |
| | | |
| | | import appointmentService from '~/shared/services/appointment.service'; |
| | | import { ContactStatus } from '~/shared/models/enum/contact-status'; |
| | | import { Appointment } from '~/shared/models/appointment.model'; |
| | | |
| | | @Module |
| | | export default class AppointmentStore extends VuexModule { |
| | | |
| | | contactStatus = ContactStatus; |
| | | |
| | | appointmentDetail: Appointment | null = JSON.parse(localStorage.getItem('appointment')!); |
| | | myAppointmentList: Appointment[] = []; |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | get appointmentProgress(): ContactStatus { |
| | | return this.appointmentDetail!.communicateStatus; |
| | | } |
| | | |
| | | get newAppointmentSum(): number { |
| | | return this.myAppointmentList.filter(item => !item.consultantViewTime || item.consultantViewTime === null).length; |
| | | } |
| | | |
| | | get appointmentItemSum(): number { |
| | | return this.myAppointmentList.filter(item => item.communicateStatus === this.contactStatus.RESERVED).length; |
| | | } |
| | | |
| | | get contactedItemSum(): number { |
| | | return this.myAppointmentList.filter((item) => item.communicateStatus === this.contactStatus.CONTACTED).length; |
| | | } |
| | | |
| | | get closedItemSum(): number { |
| | | return this.myAppointmentList |
| | | .filter(item => item.communicateStatus === this.contactStatus.DONE || item.communicateStatus === this.contactStatus.CLOSE ).length; |
| | | } |
| | | |
| | | get isCloseAppointment(): boolean { |
| | | const closedStatusList = [this.contactStatus.DONE, this.contactStatus.CLOSE, this.contactStatus.CANCEL]; |
| | | return closedStatusList.includes(this.appointmentDetail!.communicateStatus); |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | @Mutation |
| | | SET_MY_APPOINTMENT_LIST(appointmentList: Appointment[]): void { |
| | | this.myAppointmentList = appointmentList; |
| | | } |
| | | |
| | | @Mutation |
| | | SET_APPOINTMENT(appointmentDetail: Appointment): void { |
| | | this.appointmentDetail = appointmentDetail; |
| | | localStorage.setItem('appointment', JSON.stringify(appointmentDetail)); |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | @Action({ commit: 'SET_MY_APPOINTMENT_LIST' }) |
| | | async getMyAppointmentList(): Promise<Appointment[]> { |
| | | return await appointmentService.getMyAppointmentList().then((res) => res); |
| | | } |
| | | |
| | | @Action({ commit: 'SET_MY_APPOINTMENT_LIST' }) |
| | | updateMyAppointmentList(appointment: Appointment): Appointment[] { |
| | | const tempList = this.myAppointmentList.filter((item) => item.id !== appointment.id); |
| | | tempList.unshift(appointment); |
| | | return tempList; |
| | | } |
| | | |
| | | @Action({ commit: 'SET_APPOINTMENT'}) |
| | | async getAppointmentDetail(appointmentId: number): Promise<Appointment> { |
| | | if (this.appointmentDetail && this.appointmentDetail.id === appointmentId) { |
| | | return this.appointmentDetail; |
| | | } else { |
| | | return await appointmentService.getAppointmentDetail(appointmentId).then((res) => res); |
| | | } |
| | | } |
| | | |
| | | @Action({ commit: 'SET_APPOINTMENT'}) |
| | | async updateAppointmentDetail(appointmentId: number): Promise<Appointment> { |
| | | return await appointmentService.getAppointmentDetail(appointmentId).then((res) => res); |
| | | } |
| | | |
| | | } |
| | |
| | | import { StrictQueryParams } from '~/shared/models/strict-query.model'; |
| | | import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators' |
| | | |
| | | import { getFavoriteFromStorage, setFavoriteToStorage } from '~/shared/storageConsultant'; |
| | | |
| | | import myConsultantService from '~/shared/services/my-consultant.service'; |
| | | import queryConsultantService from '~/shared/services/query-consultant.service'; |
| | | import appointmentService from '~/shared/services/appointment.service'; |
| | | import reviewsService from '~/shared/services/reviews.service'; |
| | | |
| | | import { Consultant } from '~/shared/models/consultant.model'; |
| | | import { getFavoriteFromStorage, setFavoriteToStorage } from '~/shared/storageConsultant'; |
| | | import { AppointmentLog } from '~/shared/models/appointment.model'; |
| | | import { ClientInfo } from '~/shared/models/client.model'; |
| | | import { AgentOfStrictQuery } from '~/shared/models/strict-query.model'; |
| | | import { AgentOfStrictQuery, StrictQueryParams } from '~/shared/models/strict-query.model'; |
| | | import { NotificationList } from '~/shared/models/reviews.model'; |
| | | |
| | | @Module |
| | | export default class Store extends VuexModule { |
| | | recommendList: Consultant[] = []; |
| | | strictQueryList: AgentOfStrictQuery[] = []; |
| | | myConsultantList: Consultant[] = []; |
| | | |
| | | myAppointmentList: ClientInfo[] = []; |
| | | myNewAppointmentSum: number = 0; |
| | | |
| | | myAppointmentReviewLogList: AppointmentLog[] = []; |
| | | reviewLogList: AppointmentLog[] = []; |
| | | unReviewLogList: AppointmentLog[] = []; |
| | | notificationList: NotificationList[] = []; |
| | | |
| | | get isUserLogin() { |
| | | return this.context.getters['localStorage/isUserLogin']; |
| | |
| | | |
| | | @Mutation |
| | | updateRecommend(data: Consultant[]) { |
| | | this.recommendList = data; |
| | | this.recommendList = data; |
| | | } |
| | | |
| | | @Mutation |
| | |
| | | } |
| | | |
| | | @Mutation |
| | | updateMyAppointmentList(data: ClientInfo[]) { |
| | | this.myAppointmentList = data; |
| | | updateReviewLog(data: AppointmentLog[]) { |
| | | this.reviewLogList = data; |
| | | } |
| | | |
| | | @Mutation |
| | | updateMyNewAppointmentSum(newAppointmentSum: number) { |
| | | this.myNewAppointmentSum = newAppointmentSum; |
| | | updateUnReviewLog(data: AppointmentLog[]) { |
| | | this.unReviewLogList = data; |
| | | } |
| | | |
| | | @Mutation |
| | | updateMyAppointmentReviewLog(data: AppointmentLog[]) { |
| | | this.myAppointmentReviewLogList = data; |
| | | updateNotification(data: NotificationList[]) { |
| | | this.notificationList = data; |
| | | } |
| | | |
| | | @Action |
| | |
| | | } |
| | | |
| | | @Action |
| | | storeMyAppointmentList(): void { |
| | | appointmentService.getMyAppointmentList().then((data) => { |
| | | const newAppointmentSum = data.filter(item => !item.consultantViewTime || item.consultantViewTime === null).length; |
| | | this.context.commit('updateMyAppointmentList', data); |
| | | this.context.commit('updateMyNewAppointmentSum', newAppointmentSum); |
| | | }); |
| | | } |
| | | |
| | | @Action |
| | | storeMyAppointmentReviewLog() { |
| | | reviewsService.getMyReviewLog().then((data) => { |
| | | const dataWithLatestDate = data.map((item) => { |
| | |
| | | } |
| | | }); |
| | | const sortedData = dataWithLatestDate.sort((a, b) => +b.compareDate - +a.compareDate); |
| | | this.context.commit('updateMyAppointmentReviewLog', sortedData); |
| | | const reviewLog = sortedData.filter(item => item.score); |
| | | const unReviewLog = sortedData.filter(item => !item.score); |
| | | this.context.commit('updateReviewLog', reviewLog); |
| | | this.context.commit('updateUnReviewLog', unReviewLog); |
| | | }); |
| | | } |
| | | |
| | | @Action |
| | | updateMyAppointment(myAppointment: ClientInfo) { |
| | | const data = this.myAppointmentList.filter(item => item.id !== myAppointment.id); |
| | | data.unshift(myAppointment); |
| | | this.context.commit('updateMyAppointmentList', data) |
| | | } |
| | | |
| | | @Action |
| | |
| | | }); |
| | | } |
| | | |
| | | @Action |
| | | storeMyPersonalNotification() { |
| | | reviewsService.getMyPersonalNotification().then(data => { |
| | | const sortData = data |
| | | .sort((preItem, nextItem) => +new Date(nextItem.createdDate) - +new Date(preItem.createdDate)) |
| | | this.context.commit('updateNotification', sortData); |
| | | }) |
| | | } |
| | | |
| | | } |
| | |
| | | import { Module, Mutation, VuexModule ,Action } from 'vuex-module-decorators'; |
| | | import { Role } from '~/shared/models/enum/Role'; |
| | | import { Selected } from '~/shared/models/quick-filter.model'; |
| | | import { StrictQueryParams } from '~/shared/models/strict-query.model'; |
| | | @Module |
| | | export default class LocalStorage extends VuexModule { |
| | | id_token = localStorage.getItem('id_token'); |
| | |
| | | quickFilterSelectedItem = localStorage.getItem('quickFilter'); |
| | | recommendConsultantItem = localStorage.getItem('recommendConsultantItem'); |
| | | appointmentIdFromMsg = localStorage.getItem('appointmentIdFromMsg'); |
| | | satisfactionIdFromMsg = localStorage.getItem('satisfactionIdFromMsg'); |
| | | notContactAppointmentIdFromMsg = localStorage.getItem('notContactAppointmentIdFromMsg'); |
| | | |
| | | get idToken(): string|null { |
| | | return this.id_token; |
| | |
| | | |
| | | get currentAppointmentIdFromMsg(): string|null { |
| | | return this.appointmentIdFromMsg; |
| | | } |
| | | |
| | | get currentSatisfactionIdFromMsg(): string|null { |
| | | return this.satisfactionIdFromMsg; |
| | | } |
| | | |
| | | get currentNotContactAppointmentIdFromMsg(): string|null { |
| | | return this.notContactAppointmentIdFromMsg; |
| | | } |
| | | |
| | | @Mutation storageIdToken(token: string): void { |
| | |
| | | this.appointmentIdFromMsg = localStorage.getItem('appointmentIdFromMsg'); |
| | | } |
| | | |
| | | @Mutation storageSatisfactionIdFromMsg(id: string) { |
| | | localStorage.setItem('satisfactionIdFromMsg', id); |
| | | this.satisfactionIdFromMsg = localStorage.getItem('satisfactionIdFromMsg'); |
| | | } |
| | | |
| | | @Mutation storageNotContactAppointmentIdFromMsg(id: string) { |
| | | localStorage.setItem('notContactAppointmentIdFromMsg', id); |
| | | this.notContactAppointmentIdFromMsg = id; |
| | | } |
| | | |
| | | @Mutation storageClear(): void { |
| | | localStorage.removeItem('myRequests'); |
| | | localStorage.removeItem('userInfo'); |
| | | localStorage.removeItem('id_token'); |
| | | localStorage.removeItem('current_role'); |
| | | localStorage.removeItem('consultant_id'); |
| | | localStorage.removeItem('appointment'); |
| | | localStorage.removeItem('login_consultant'); |
| | | this.id_token = localStorage.getItem('id_token'); |
| | | this.current_role = localStorage.getItem('current_role'); |
| | | this.consultant_id = localStorage.getItem('consultant_id'); |
| | |
| | | this.appointmentIdFromMsg = localStorage.getItem('appointmentIdFromMsg'); |
| | | } |
| | | |
| | | @Mutation storageClearSatisfactionIdFromMsg() { |
| | | localStorage.removeItem('satisfactionIdFromMsg'); |
| | | this.appointmentIdFromMsg = localStorage.getItem('satisfactionIdFromMsg'); |
| | | } |
| | | |
| | | @Mutation storageClearNotContactAppointmentIdFromMsg() { |
| | | localStorage.removeItem('notContactAppointmentIdFromMsg'); |
| | | this.appointmentIdFromMsg = localStorage.getItem('notContactAppointmentIdFromMsg'); |
| | | } |
| | | |
| | | @Mutation storageStrickQueryItem(queryItem: StrictQueryParams): void { |
| | | localStorage.setItem('recommendConsultantItem', JSON.stringify(queryItem)); |
| | | this.recommendConsultantItem = localStorage.getItem('recommendConsultantItem'); |
| | | } |
| | | |
| | | @Action actionStorageClear(): void { |
| | | this.context.commit("storageClear"); |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators' |
| | | |
| | | import myConsultantService from '~/shared/services/my-consultant.service'; |
| | | import { AgentInfo } from '~/shared/models/agent-info.model'; |
| | | |
| | | @Module |
| | | export default class AppointmentStore extends VuexModule { |
| | | |
| | | loginConsultant?: AgentInfo = JSON.parse(localStorage.getItem('login_consultant')!); |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | @Mutation |
| | | SET_LOGIN_CONSULTANT(agentInfo: AgentInfo): void { |
| | | this.loginConsultant = agentInfo; |
| | | localStorage.setItem('login_consultant', JSON.stringify(agentInfo)); |
| | | } |
| | | |
| | | ////////////////////////////////////////////////////////////////////// |
| | | |
| | | @Action({ commit: 'SET_LOGIN_CONSULTANT' }) |
| | | async getLoginConsultantDetail(agentNo: string): Promise<AgentInfo> { |
| | | return await myConsultantService.getConsultantDetail(agentNo).then((res) => res); |
| | | } |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | CREATE TABLE omo.appointment_memo ( |
| | | id bigserial NOT NULL, |
| | | "content" varchar NOT NULL, |
| | | appointment_id int8 NOT NULL, |
| | | created_date timestamp NOT NULL, |
| | | created_by varchar NOT NULL, |
| | | last_modified_date timestamp NOT NULL, |
| | | last_modified_by varchar NOT NULL, |
| | | CONSTRAINT appointment_memo_pkey PRIMARY KEY (id) |
| | | ); |
¤ñ¹ï·sÀÉ®× |
| | |
| | | UPDATE omo.consultant SET photo_path='C://pam_file/consultant_AGAM11249699.jpg' WHERE agent_no='AGAM11249699'; |
| | | UPDATE omo.consultant SET photo_path='C://pam_file/consultant_B282677963.jpg' WHERE agent_no='B282677963'; |
| | | UPDATE omo.consultant SET photo_path='C://pam_file/consultant_AG0101234567.jpg' WHERE agent_no='AG0101234567'; |
| | | UPDATE omo.consultant SET photo_path='C://pam_file/consultant_R221444250.jpg' WHERE agent_no='R221444250'; |
| | | UPDATE omo.consultant SET photo_path='C://pam_file/consultant_AG0109051204.jpg' WHERE agent_no='AG0109051204'; |
| | | UPDATE omo.consultant SET photo_path='C://pam_file/consultant_A183619275.jpg' WHERE agent_no='A183619275'; |
| | | UPDATE omo.consultant SET photo_path='C://pam_file/consultant_X147309614.jpg' WHERE agent_no='X147309614'; |
| | | UPDATE omo.consultant SET photo_path='C://pam_file/consultant_D265260662.jpg' WHERE agent_no='D265260662'; |
| | | UPDATE omo.consultant SET photo_path='C://pam_file/consultant_J149388015.jpg' WHERE agent_no='J149388015'; |
| | | UPDATE omo.consultant SET photo_path='C://pam_file/consultant_Z152717443.jpg' WHERE agent_no='Z152717443'; |
¤ñ¹ï·sÀÉ®× |
| | |
| | | -- æ°å¢é ç´å®éç¥æ·ç¨table |
| | | |
| | | CREATE TABLE public.appointment_notice_log ( |
| | | id bigserial NOT NULL, |
| | | appointment_id bigserial NOT NULL, |
| | | email varchar NULL, |
| | | phone varchar NULL, |
| | | "content" varchar NULL, |
| | | created_date timestamp NULL, |
| | | CONSTRAINT appointment_notice_log_pkey PRIMARY KEY (id) |
| | | ); |
| | | |
| | | |
| | | -- æ°å¢é ç´å®å註table |
| | | |
| | | CREATE TABLE public.appointment_memo ( |
| | | id bigserial NOT NULL, |
| | | "content" varchar NOT NULL, |
| | | appointment_id int8 NOT NULL, |
| | | created_date timestamp NOT NULL, |
| | | created_by varchar NOT NULL, |
| | | last_modified_date timestamp NOT NULL, |
| | | last_modified_by varchar NOT NULL, |
| | | CONSTRAINT appointment_memo_pkey PRIMARY KEY (id) |
| | | ); |
| | | |
| | | |
| | | -- æ°å¢ç´è¨ªç´étable |
| | | |
| | | CREATE TABLE public.interview_record ( |
| | | id bigserial NOT NULL, |
| | | created_date timestamp NOT NULL, |
| | | created_by varchar NOT NULL, |
| | | last_modified_date timestamp NOT NULL, |
| | | last_modified_by varchar NOT NULL, |
| | | "content" varchar NULL, |
| | | interview_date timestamp NULL, |
| | | appointment_id bigserial NOT NULL, |
| | | status varchar NULL, |
| | | CONSTRAINT interview_record_pkey PRIMARY KEY (id) |
| | | ); |
| | | |
| | | -- æ°å¢é ç´å®çµæ¡è³ætable |
| | | |
| | | -- Drop table |
| | | |
| | | -- DROP TABLE public.appointment_closed_info; |
| | | |
| | | -- Drop table |
| | | |
| | | -- DROP TABLE public.appointment_closed_info; |
| | | |
| | | CREATE TABLE public.appointment_closed_info ( |
| | | id bigserial NOT NULL, |
| | | policyholder_identity_id varchar NULL, |
| | | plan_code varchar NULL, |
| | | policy_entry_date date NULL, |
| | | remark varchar NULL, |
| | | closed_reason varchar NULL, |
| | | closed_other_reason varchar NULL, |
| | | appointment_id bigserial NOT NULL, |
| | | CONSTRAINT appointment_closed_info_pkey PRIMARY KEY (id) |
| | | ); |
| | | |
| | | |
| | | |
¤ñ¹ï·sÀÉ®× |
| | |
| | | -- appointment_notice_log æ°å¢ç´è¨ªæéæ¬ä½ |
| | | ALTER TABLE appointment_notice_log ADD interview_date timestamp null; |
¤ñ¹ï·sÀÉ®× |
| | |
| | | -- 建ç«å°é´éºéç¥çtable |
| | | |
| | | CREATE TABLE public.personal_notification ( |
| | | id bigserial NOT NULL, |
| | | title varchar NULL, |
| | | "content" varchar NOT NULL, |
| | | notification_type varchar NOT NULL, |
| | | owner_role varchar NOT NULL, |
| | | owner_id bigserial NOT NULL, |
| | | created_date timestamp NOT NULL, |
| | | read_date timestamp NULL, |
| | | CONSTRAINT personal_notification_pkey PRIMARY KEY (id) |
| | | ); |
¤ñ¹ï·sÀÉ®× |
| | |
| | | -- 調æ´policy_entry_date åæ
|
| | | ALTER TABLE appointment_closed_info |
| | | ALTER COLUMN policy_entry_date TYPE timestamp; |
¤ñ¹ï·sÀÉ®× |
| | |
| | | CREATE TABLE public.appointment_expiring_notify_record ( |
| | | id bigserial NOT NULL, |
| | | appointment_id bigserial NOT NULL, |
| | | send_time timestamp NULL, |
| | | CONSTRAINT appointment_expiring_notify_record_pk PRIMARY KEY (id) |
| | | ); |
¤ñ¹ï·sÀÉ®× |
| | |
| | | truncate public.appointment; |
| | | truncate public.appointment_closed_info; |
| | | truncate public.appointment_expiring_notify_record; |
| | | truncate public.appointment_memo; |
| | | truncate public.appointment_notice_log; |
| | | truncate public.customer; |
| | | truncate public.interview_record; |
| | | truncate public.login_record; |
| | | truncate public.otp_tmp; |
| | | truncate public.personal_notification; |
| | | truncate public.satisfaction; |
| | | truncate public.customer_favorite_consultant; |
¤ñ¹ï·sÀÉ®× |
| | |
| | | http get: |
| | | |
| | | http://localhost:8080/api/personal_notification/getMyPersonalNotification |
| | | |
| | | title: æ¨é¡ |
| | | content: éç¥å
§å®¹ |
| | | notificationType: ACTIVITY(å人活åéç¥)ãSYSTEM(系統éç¥) |
| | | ownerRole: CUSTOMER(客æ¶)ãCONSULTANT(é¡§å) |
| | | ownerId: ç»å
¥è
id |
| | | readDate: å·²è®æé |
| | | |
| | | request body : |
| | | |
| | | [ |
| | | { |
| | | "id": 1, |
| | | "title": "title test", |
| | | "content": "content test", |
| | | "notificationType": "ACTIVITY", |
| | | "ownerRole": "CONSULTANT", |
| | | "ownerId": 11, |
| | | "createdDate": "2022-01-20T10:53:53.022Z", |
| | | "readDate": null |
| | | }, |
| | | { |
| | | "id": 2, |
| | | "title": "title test", |
| | | "content": "content test", |
| | | "notificationType": "ACTIVITY", |
| | | "ownerRole": "CONSULTANT", |
| | | "ownerId": 11, |
| | | "createdDate": "2022-01-20T10:59:17.242Z", |
| | | "readDate": null |
| | | } |
| | | ] |
| | | |
| | | |
| | | |
¤ñ¹ï·sÀÉ®× |
| | |
| | | http post : |
| | | |
| | | å°ç®åç»å
¥è
çææå°é´éºéç¥è¨å®çºå·²è® |
| | | |
| | | http://localhost:8080/api/personal_notification/readAllMyNotification |
| | | |
| | |
| | | http post : |
| | | |
| | | http://localhost:8080/api/satisfaction/create |
| | | http://localhost:8080/api/satisfaction/score |
| | | |
| | | |
| | | 填寫ä¸ç: |
| | | |
| | | request body: |
| | | |
| | |
| | | "agentNo": "admin", |
| | | "status": "UNFILLED", |
| | | "score": 4 |
| | | } |
| | | } |
| | | |
| | | ===================================================== |
| | | |
| | | 填寫å¤ç滿æåº¦: |
| | | |
| | | http post : |
| | | |
| | | http://localhost:8080/api/satisfaction/score/all |
| | | |
| | | request body: |
| | | |
| | | [{ |
| | | "appointmentId": 67, |
| | | "score":4 |
| | | }] |
| | | |
| | | |
| | | |
| | | response body: |
| | | |
| | | |
| | | [{ |
| | | "id": 3, |
| | | "customerId": 2, |
| | | "agentNo": "admin", |
| | | "status": "UNFILLED", |
| | | "score": 4 |
| | | }] |
| | | |
¤ñ¹ï·sÀÉ®× |
| | |
| | | http://localhost:8080/api/consultant/sendSatisfactionToClient/{appointmentId} |
¤ñ¹ï·sÀÉ®× |
| | |
| | | http post : |
| | | |
| | | http://localhost:8080/api/interview_record/update |
| | | |
| | | |
| | | id : interviewRecord id |
| | | content : ç´è¨ªç´éå
§å®¹ |
| | | interviewDate: é è¨ç´è¨ªæé |
| | | appointmentId: é ç´å®id |
| | | |
| | | |
| | | |
| | | request body : |
| | | |
| | | { |
| | | "id":5, |
| | | "content":"test record content2", |
| | | "interviewDate":"2021-01-01T08:00:00.000", |
| | | "appointmentId": 385 |
| | | } |
| | | |
| | | |
| | | |
| | | response body : |
| | | |
| | | { |
| | | "id": 5, |
| | | "content": "test record content2", |
| | | "interviewDate": "2021-01-01T08:00:00.000+00:00", |
| | | "appointmentId": 385 |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | http delete : |
| | | |
| | | http://localhost:8080/api/interview_record/{interviewRecordId} |
| | | |
| | | http://localhost:8080/api/interview_record/5 |
| | | |
¤ñ¹ï·sÀÉ®× |
| | |
| | | http post : |
| | | |
| | | http://localhost:8080/api/interview_record/create |
| | | |
| | | |
| | | interviewDate: é è¨ç´è¨ªæ¥æ |
| | | content : ç´éå
§å®¹ |
| | | appointmentId: é ç´å®id |
| | | |
| | | |
| | | request body : |
| | | |
| | | { |
| | | "content":"test record content", |
| | | "interviewDate":"2021-01-01T08:00:00.000", |
| | | "appointmentId": 385 |
| | | } |
| | | |
| | | |
| | | response body : |
| | | |
| | | { |
| | | "id": 5, |
| | | "content": "test record content", |
| | | "interviewDate": "2021-01-01T08:00:00.000+00:00", |
| | | "appointmentId": 385 |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | http post : |
| | | |
| | | http://localhost:8080/api/notice/send |
| | | |
| | | message: ç¼éçæåå
§å®¹ |
| | | appointmentId : ç´è¨ªéç¥çé ç´å®id |
| | | interviewDate: é è¨ç´è¨ªææ®µ |
| | | |
| | | request body: |
| | | |
| | | { |
| | | "message":"notice customer invterview time", |
| | | "email":"pollex@gmail.com", |
| | | "phone":"0912345678", |
| | | "appointmentId": 406, |
| | | "interviewDate":"2022-11-01T08:00:00.000" |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | http delete: |
| | | |
| | | http://localhost:8080/api/appointment/memo/{appointmentMemoId} |
| | | |
| | | http://localhost:8080/api/appointment/memo/1 |
| | | |
| | |
| | | http://localhost:8080/api/appointment/getDetail/{appointmentId} |
| | | |
| | | |
| | | appointmentNoticeLogs: é ç´å®ç¼ééç¥çæ·ç¨ |
| | | appointmentClosedInfo: é ç´å®çµæ¡è³æ |
| | | |
| | | |
| | | response body: |
| | | { |
| | | "id": 110, |
| | | "phone": "09123456789", |
| | | "email": "", |
| | | "contactType": "mobile", |
| | | "gender": "male", |
| | | "age": "30", |
| | | "id": 385, |
| | | "phone": "0911223344", |
| | | "email": "SDD", |
| | | "contactType": "phone", |
| | | "gender": "female", |
| | | "age": "21-30", |
| | | "job": "å
§å¤", |
| | | "requirement": "å女æè²,è³ç¢è¦å,é²ç«ä¿å®ç¸é", |
| | | "communicateStatus": "contacted", |
| | | "hopeContactTime": "'禮æä¸,禮æäºã12:00~14:00,14:00~18:00'", |
| | | "requirement": "å¥åº·èä¿é", |
| | | "communicateStatus": "done", |
| | | "hopeContactTime": "'ææä¸,ææäº,ææä¸,ææå,ææäº,ææå
,æææ¥ã9:00~12:00,12:00~14:00,14:00~18:00,18:00~21:00'", |
| | | "otherRequirement": null, |
| | | "appointmentDate": "2021-12-02T09:58:58.932Z", |
| | | "lastModifiedDate": "2021-12-02T09:58:58.932Z", |
| | | "agentNo": "AG0109051204", |
| | | "customerId": 70, |
| | | "name": null, |
| | | "consultantViewTime": "2021-12-02T09:58:12.066Z", |
| | | "consultantReadTime": "2021-12-02T09:58:58.930Z", |
| | | "contactTime": "2021-12-02T09:58:58.930Z", |
| | | "satisfactionScore":3 // null 代表該çé ç´å®å°æªå¡«å¯«æ»¿æåº¦ |
| | | } |
| | | "appointmentDate": "2021-12-16T07:11:05.400Z", |
| | | "lastModifiedDate": "2022-01-19T10:57:51.380Z", |
| | | "agentNo": "A568420", |
| | | "customerId": 139, |
| | | "name": "Angula-test", |
| | | "consultantViewTime": "2021-12-27T02:02:18.711Z", |
| | | "consultantReadTime": "2021-12-28T07:16:01.295Z", |
| | | "contactTime": "2021-12-28T07:16:37.004Z", |
| | | "satisfactionScore": null, |
| | | "appointmentMemoList": [], |
| | | "interviewRecordDTOs": [], |
| | | "appointmentNoticeLogs": [ |
| | | { |
| | | "id": 4, |
| | | "phone": "0912345678", |
| | | "email": "pollex@gmail.com", |
| | | "appointmentId": 385, |
| | | "content": "notice customer invterview time", |
| | | "createdDate": "2022-01-11T09:33:57.754Z", |
| | | "interviewDate": null |
| | | }, |
| | | { |
| | | "id": 6, |
| | | "phone": "0912345678", |
| | | "email": "pollex@gmail.com", |
| | | "appointmentId": 385, |
| | | "content": "notice customer invterview time", |
| | | "createdDate": "2022-01-19T10:38:42.187Z", |
| | | "interviewDate": "2022-11-01T08:00:00.000+00:00" |
| | | } |
| | | ], |
| | | "appointmentClosedInfo": { |
| | | "id": 9, |
| | | "policyholderIdentityId": "A123456789", |
| | | "planCode": "ATMdd", |
| | | "policyEntryDate": "2022-01-12T00:00:00.000+00:00", |
| | | "remark": "test remark", |
| | | "closedReason": "other2", |
| | | "closedOtherReason": "å¿æ
ä¸å¥½ä¸æ³è²·2", |
| | | "appointmentId": 385 |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | http get : |
| | | |
| | | http://localhost:8080/api/appointment/customer/expiring/newest |
| | | |
| | | ç°¡è¨åemailæä»¥è©²ç¶²åé²å
¥é¦é -> http://localhost:3000?notContactAppointmentId={ææ°ä¸çæªèçé ç´å®} |
| | | |
| | | response body: è¥ææå³200並給以ä¸è³æï¼è¥ç¡(æªæä»»ä½é¾ææªèçé ç´å®ï¼åæånull) |
| | | { |
| | | "id": 385, |
| | | "phone": "0911223344", |
| | | "email": "SDD", |
| | | "contactType": "phone", |
| | | "gender": "female", |
| | | "age": "21-30", |
| | | "job": "å
§å¤", |
| | | "requirement": "å¥åº·èä¿é", |
| | | "communicateStatus": "done", |
| | | "hopeContactTime": "'ææä¸,ææäº,ææä¸,ææå,ææäº,ææå
,æææ¥ã9:00~12:00,12:00~14:00,14:00~18:00,18:00~21:00'", |
| | | "otherRequirement": null, |
| | | "appointmentDate": "2021-12-16T07:11:05.400Z", |
| | | "lastModifiedDate": "2022-01-19T10:57:51.380Z", |
| | | "agentNo": "A568420", |
| | | "customerId": 139, |
| | | "name": "Angula-test", |
| | | "consultantViewTime": "2021-12-27T02:02:18.711Z", |
| | | "consultantReadTime": "2021-12-28T07:16:01.295Z", |
| | | "contactTime": "2021-12-28T07:16:37.004Z", |
| | | "satisfactionScore": null, |
| | | "appointmentMemoList": [], |
| | | "interviewRecordDTOs": [], |
| | | "appointmentNoticeLogs": [ |
| | | { |
| | | "id": 4, |
| | | "phone": "0912345678", |
| | | "email": "pollex@gmail.com", |
| | | "appointmentId": 385, |
| | | "content": "notice customer invterview time", |
| | | "createdDate": "2022-01-11T09:33:57.754Z", |
| | | "interviewDate": null |
| | | }, |
| | | { |
| | | "id": 6, |
| | | "phone": "0912345678", |
| | | "email": "pollex@gmail.com", |
| | | "appointmentId": 385, |
| | | "content": "notice customer invterview time", |
| | | "createdDate": "2022-01-19T10:38:42.187Z", |
| | | "interviewDate": "2022-11-01T08:00:00.000+00:00" |
| | | } |
| | | ], |
| | | "appointmentClosedInfo": { |
| | | "id": 9, |
| | | "policyholderIdentityId": "A123456789", |
| | | "planCode": "ATMdd", |
| | | "policyEntryDate": "2022-01-12T00:00:00.000+00:00", |
| | | "remark": "test remark", |
| | | "closedReason": "other2", |
| | | "closedOtherReason": "å¿æ
ä¸å¥½ä¸æ³è²·2", |
| | | "appointmentId": 385 |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | http post : |
| | | |
| | | http://localhost:8080/api/appointment/memo/create |
| | | |
| | | |
| | | content : é ç´å®å註å
§å®¹ |
| | | appointmentId : é ç´å®id |
| | | |
| | | request body: |
| | | |
| | | { |
| | | "content":"test", |
| | | "appointmentId":368 |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | http post : |
| | | |
| | | http://localhost:8080/api/appointment/memo/update |
| | | |
| | | id : memo id |
| | | |
| | | |
| | | request body: |
| | | { |
| | | "id": 1 , |
| | | "content":"test4" |
| | | } |
| | |
| | | http post : |
| | | http post : |
| | | http://localhost:8080/api/appointment/markAsContacted/{appointmentId} |
| | | |
| | | response body: |
| | | { |
| | | "id": 385, |
| | | "phone": "0911223344", |
| | | "email": "SDD", |
| | | "contactType": "phone", |
| | | "gender": "female", |
| | | "age": "21-30", |
| | | "job": "å
§å¤", |
| | | "requirement": "å¥åº·èä¿é", |
| | | "communicateStatus": "done", |
| | | "hopeContactTime": "'ææä¸,ææäº,ææä¸,ææå,ææäº,ææå
,æææ¥ã9:00~12:00,12:00~14:00,14:00~18:00,18:00~21:00'", |
| | | "otherRequirement": null, |
| | | "appointmentDate": "2021-12-16T07:11:05.400Z", |
| | | "lastModifiedDate": "2022-01-19T10:57:51.380Z", |
| | | "agentNo": "A568420", |
| | | "customerId": 139, |
| | | "name": "Angula-test", |
| | | "consultantViewTime": "2021-12-27T02:02:18.711Z", |
| | | "consultantReadTime": "2021-12-28T07:16:01.295Z", |
| | | "contactTime": "2021-12-28T07:16:37.004Z", |
| | | "satisfactionScore": null, |
| | | "appointmentMemoList": [], |
| | | "interviewRecordDTOs": [], |
| | | "appointmentNoticeLogs": [ |
| | | { |
| | | "id": 4, |
| | | "phone": "0912345678", |
| | | "email": "pollex@gmail.com", |
| | | "appointmentId": 385, |
| | | "content": "notice customer invterview time", |
| | | "createdDate": "2022-01-11T09:33:57.754Z", |
| | | "interviewDate": null |
| | | }, |
| | | { |
| | | "id": 6, |
| | | "phone": "0912345678", |
| | | "email": "pollex@gmail.com", |
| | | "appointmentId": 385, |
| | | "content": "notice customer invterview time", |
| | | "createdDate": "2022-01-19T10:38:42.187Z", |
| | | "interviewDate": "2022-11-01T08:00:00.000+00:00" |
| | | } |
| | | ], |
| | | "appointmentClosedInfo": { |
| | | "id": 9, |
| | | "policyholderIdentityId": "A123456789", |
| | | "planCode": "ATMdd", |
| | | "policyEntryDate": "2022-01-12T00:00:00.000+00:00", |
| | | "remark": "test remark", |
| | | "closedReason": "other2", |
| | | "closedOtherReason": "å¿æ
ä¸å¥½ä¸æ³è²·2", |
| | | "appointmentId": 385 |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | http post : |
| | | æ°å¢çµæ¡æç´°, æ´æ°çµæ¡æç´°: |
| | | http://localhost:8080/api/appointment/close |
| | | |
| | | |
| | | |
| | | request body : |
| | | |
| | | æäº¤ |
| | | contactStatus: done |
| | | |
| | | { |
| | | "policyholderIdentityId":"A123456789", |
| | | "planCode":"ATM", |
| | | "policyEntryDate":"2022-01-12", |
| | | "contactStatus":"done", |
| | | "appointmentId": 385, |
| | | "remark":"test remark" |
| | | } |
| | | |
| | | |
| | | |
| | | æªæäº¤ |
| | | contactStatus: closed |
| | | |
| | | { |
| | | "contactStatus":"closed", |
| | | "closedReason":"other", |
| | | "closedOtherReason":"å¿æ
ä¸å¥½ä¸æ³è²·", |
| | | "appointmentId": 385, |
| | | "remark":"test remark" |
| | | } |
| | |
| | | |
| | | http://localhost:8080/api/consultant/getMyAppointment |
| | | |
| | | |
| | | appointmentMemoList : åè¨»è³æ |
| | | interviewRecordDTOs : ç´è¨ªç´é |
| | | |
| | | [ |
| | | { |
| | | "id": 110, |
| | | "phone": "09123456789", |
| | | "email": "", |
| | | "contactType": "mobile", |
| | | "gender": "male", |
| | | "age": "30", |
| | | "job": "å
§å¤", |
| | | "requirement": "å女æè²,è³ç¢è¦å,é²ç«ä¿å®ç¸é", |
| | | "communicateStatus": "contacted", |
| | | "hopeContactTime": "'禮æä¸,禮æäºã12:00~14:00,14:00~18:00'", |
| | | "otherRequirement": null, |
| | | "appointmentDate": "2021-12-02T09:58:58.932Z", |
| | | "lastModifiedDate": "2021-12-02T09:58:58.932Z", |
| | | "agentNo": "AG0109051204", |
| | | "customerId": 70, |
| | | "name": null, |
| | | "consultantViewTime": "2021-12-02T09:58:12.066Z", |
| | | "consultantReadTime": "2021-12-02T09:58:58.930Z", |
| | | "contactTime": "2021-12-02T09:58:58.930Z", |
| | | "satisfactionScore":3 // null 代表該çé ç´å®å°æªå¡«å¯«æ»¿æåº¦ |
| | | }, |
| | | { |
| | | "id": 109, |
| | | "phone": "09123456789", |
| | | "email": "", |
| | | "contactType": "mobile", |
| | | "gender": "male", |
| | | "age": "20", |
| | | "job": "å
§å¤", |
| | | "requirement": "å¥åº·èä¿é,å女æè²,ä¿å®å¥æª¢/è¦å,é²ç«ä¿å®ç¸é", |
| | | "communicateStatus": "contacted", |
| | | "hopeContactTime": "'禮æä¸,禮æäº,禮æä¸,禮æå,禮æäº,禮æå
,ç¦®ææ¥ã12:00~14:00,14:00~18:00'", |
| | | "otherRequirement": null, |
| | | "appointmentDate": "2021-12-02T10:12:24.613Z", |
| | | "lastModifiedDate": "2021-12-02T10:12:24.613Z", |
| | | "agentNo": "AG0109051204", |
| | | "customerId": 67, |
| | | "name": null, |
| | | "consultantViewTime": "2021-12-02T09:58:12.066Z", |
| | | "consultantReadTime": "2021-12-02T10:12:24.612Z", |
| | | "contactTime": null, |
| | | "satisfactionScore":3 // null 代表該çé ç´å®å°æªå¡«å¯«æ»¿æåº¦ |
| | | }, |
| | | { |
| | | "id": 114, |
| | | "phone": "09123456789", |
| | | "email": "", |
| | | "contactType": "mobile", |
| | | "gender": "male", |
| | | "age": "70", |
| | | "job": "å
§å¤", |
| | | "requirement": "å¥åº·èä¿é,å女æè²,è³ç¢è¦å,æ¨æ´»éä¼,é²ç«ä¿å®ç¸é", |
| | | "communicateStatus": "contacted", |
| | | "hopeContactTime": "'禮æä¸,禮æäº,禮æä¸,禮æå,禮æäº,禮æå
,ç¦®ææ¥ã12:00~14:00,14:00~18:00'", |
| | | "otherRequirement": null, |
| | | "appointmentDate": "2021-12-02T09:58:12.248Z", |
| | | "lastModifiedDate": "2021-12-02T09:58:12.248Z", |
| | | "agentNo": "AG0109051204", |
| | | "customerId": 71, |
| | | "name": null, |
| | | "consultantViewTime": "2021-12-02T09:58:12.066Z", |
| | | "consultantReadTime": null, |
| | | "contactTime": null, |
| | | "satisfactionScore":3 // null 代表該çé ç´å®å°æªå¡«å¯«æ»¿æåº¦ |
| | | }, |
| | | { |
| | | "id": 121, |
| | | "phone": "09123456789", |
| | | "email": "", |
| | | "contactType": "mobile", |
| | | "gender": "male", |
| | | "age": "20", |
| | | "job": "å
§å¤", |
| | | "requirement": "å女æè²", |
| | | "communicateStatus": "contacted", |
| | | "hopeContactTime": "'禮æä¸,禮æäº,禮æä¸,禮æå,禮æäº,禮æå
,ç¦®ææ¥ã9:00~12:00,12:00~14:00,14:00~18:00,18:00~21:00'", |
| | | "otherRequirement": null, |
| | | "appointmentDate": "2021-12-02T09:58:12.248Z", |
| | | "lastModifiedDate": "2021-12-02T09:58:12.248Z", |
| | | "agentNo": "AG0109051204", |
| | | "customerId": 76, |
| | | "name": "æå²ç¶", |
| | | "consultantViewTime": "2021-12-02T09:58:12.066Z", |
| | | "consultantReadTime": "2021-12-02T09:54:20.872Z", |
| | | "contactTime": null, |
| | | "satisfactionScore":3 // null 代表該çé ç´å®å°æªå¡«å¯«æ»¿æåº¦ |
| | | }, |
| | | { |
| | | "id": 118, |
| | | "phone": "09123456789", |
| | | "email": "", |
| | | "contactType": "mobile", |
| | | "gender": "male", |
| | | "age": "20", |
| | | "job": "å
§å¤", |
| | | "requirement": "å¥åº·èä¿é", |
| | | "communicateStatus": "contacted", |
| | | "hopeContactTime": "'禮æä¸,禮æäº,禮æä¸,禮æå,禮æäº,禮æå
,ç¦®ææ¥ã12:00~14:00,14:00~18:00'", |
| | | "otherRequirement": null, |
| | | "appointmentDate": "2021-12-02T10:02:52.797Z", |
| | | "lastModifiedDate": "2021-12-02T10:02:52.797Z", |
| | | "agentNo": "AG0109051204", |
| | | "customerId": 72, |
| | | "name": null, |
| | | "consultantViewTime": "2021-12-02T09:58:12.066Z", |
| | | "consultantReadTime": "2021-12-02T10:02:52.796Z", |
| | | "contactTime": null, |
| | | "satisfactionScore":3 // null 代表該çé ç´å®å°æªå¡«å¯«æ»¿æåº¦ |
| | | }, |
| | | { |
| | | "id": 180, |
| | | "phone": "0911111111", |
| | | "email": "", |
| | | "id": 385, |
| | | "phone": "0911223344", |
| | | "email": "SDD", |
| | | "contactType": "phone", |
| | | "gender": "female", |
| | | "age": "31-40", |
| | | "age": "21-30", |
| | | "job": "å
§å¤", |
| | | "requirement": "å女æè²,å¥åº·èä¿é", |
| | | "communicateStatus": "contacted", |
| | | "hopeContactTime": "'禮æäº,禮æä¸,禮æå,禮æäº,禮æå
ã9:00~12:00,12:00~14:00,14:00~18:00,18:00~21:00','禮æä¸,禮æäº,禮æä¸,禮æå,禮æäº,ç¦®ææ¥ã12:00~14:00,14:00~18:00,18:00~21:00','禮æä¸,禮æå,禮æå
,ç¦®ææ¥ã14:00~18:00,18:00~21:00'", |
| | | "requirement": "å¥åº·èä¿é", |
| | | "communicateStatus": "done", |
| | | "hopeContactTime": "'ææä¸,ææäº,ææä¸,ææå,ææäº,ææå
,æææ¥ã9:00~12:00,12:00~14:00,14:00~18:00,18:00~21:00'", |
| | | "otherRequirement": null, |
| | | "appointmentDate": "2021-12-02T10:10:53.341Z", |
| | | "lastModifiedDate": "2021-12-03T03:40:16.344Z", |
| | | "agentNo": "AG0109051204", |
| | | "customerId": 77, |
| | | "name": "111", |
| | | "consultantViewTime": "2021-12-02T10:10:46.358Z", |
| | | "consultantReadTime": "2021-12-02T10:10:53.340Z", |
| | | "contactTime": "2021-12-03T03:40:16.215Z", |
| | | "satisfactionScore":3 // null 代表該çé ç´å®å°æªå¡«å¯«æ»¿æåº¦ |
| | | "appointmentDate": "2021-12-16T07:11:05.400Z", |
| | | "lastModifiedDate": "2022-01-19T10:57:51.380Z", |
| | | "agentNo": "A568420", |
| | | "customerId": 139, |
| | | "name": "Angula-test", |
| | | "consultantViewTime": "2021-12-27T02:02:18.711Z", |
| | | "consultantReadTime": "2021-12-28T07:16:01.295Z", |
| | | "contactTime": "2021-12-28T07:16:37.004Z", |
| | | "satisfactionScore": null, |
| | | "appointmentMemoList": [], |
| | | "interviewRecordDTOs": [], |
| | | "appointmentNoticeLogs": [ |
| | | { |
| | | "id": 4, |
| | | "phone": "0912345678", |
| | | "email": "pollex@gmail.com", |
| | | "appointmentId": 385, |
| | | "content": "notice customer invterview time", |
| | | "createdDate": "2022-01-11T09:33:57.754Z", |
| | | "interviewDate": null |
| | | }, |
| | | { |
| | | "id": 6, |
| | | "phone": "0912345678", |
| | | "email": "pollex@gmail.com", |
| | | "appointmentId": 385, |
| | | "content": "notice customer invterview time", |
| | | "createdDate": "2022-01-19T10:38:42.187Z", |
| | | "interviewDate": "2022-11-01T08:00:00.000+00:00" |
| | | } |
| | | ], |
| | | "appointmentClosedInfo": { |
| | | "id": 9, |
| | | "policyholderIdentityId": "A123456789", |
| | | "planCode": "ATMdd", |
| | | "policyEntryDate": "2022-01-12T00:00:00.000+00:00", |
| | | "remark": "test remark", |
| | | "closedReason": "other2", |
| | | "closedOtherReason": "å¿æ
ä¸å¥½ä¸æ³è²·2", |
| | | "appointmentId": 385 |
| | | } |
| | | } |
| | | ] |
¤ñ¹ï·sÀÉ®× |
| | |
| | | http get : |
| | | http://localhost:8080/api/appointment/consultant/pending/sum |
| | | |
| | | response body: |
| | | 2 |
| | |
| | | http get: http://localhost:8080/api/consultant/avatar/{fileName} |
| | | æ ¹æå¾é¡§åè³æè£¡çimgæ¬ä½çæªæ¡å稱ï¼åå¾é¡§åé åçjpgæªæ¡ |
| | | http get: http://localhost:8080/api/consultant/avatar/{agentNo} |
| | | æ ¹æé¡§åçagentNo åå¾é¡§åçç
§ç |
| | | |
| | | example request: |
| | | http://localhost:8080/api/consultant/avatar/avatar1.jpg |
| | | http://localhost:8080/api/consultant/avatar/A568420 |
| | | |
| | | response bodyï¼ äºé²å¶æªæ¡ |
| | | |
| | |
| | | |
| | | response body: |
| | | { |
| | | "name": "測試æ¨è¦æ¥åå¡", |
| | | "agentNo": "12345", |
| | | "role": "ä¿éªç¶ç´äºº", |
| | | "img": "", |
| | | "avgScore": 4.7, |
| | | "title": "å°æ¡ç¶ç", |
| | | "phoneNumber": "0912345678", |
| | | "serveArea": "å°åå¸å°å", |
| | | "companyAddress": "å°åå¸ä¿¡ç¾©åå¿ åæ±è·¯ä¸æ®µ1è", |
| | | "latestLoginTime": "2021-11-29T07:39:22.135Z", // è¥ç¡æå¾ç»å
¥ç´éåæå¸¶null |
| | | "seniority": "4å¹´2åæ", |
| | | "suitability": 0, |
| | | "evaluation": 0, |
| | | "expertise": [ |
| | | "財åè¦å", |
| | | "è³ç¢è½ç§»" |
| | | ], |
| | | "concept": "壽éªè·¯ä¸æ²ææ·å¾ï¼å¯æçµ¦å®¢æ¶ä¿¡ä»»æãå®å
¨æï¼ææ¯æå¥½çæ¹æ³ã徿¥ä»¥ä¾ï¼æä¸ç´ç§æèãå©äººçºå¿«æ¨ä¹æ¬ãç信念å
æèï¼å¥¹ç¸ä¿¡ï¼ä¸åå¥½çæ¥å人å¡ï¼å¿
é æ±æèä¸é¡ç±å¿å©äººçå¿ï¼ææ¯æ°¸çºç¶ç壽éªäºæ¥çä¸äºæ³éã", |
| | | "experiences": [ |
| | | "å°å¤§è²¡éç³»", |
| | | "ç¾èæç²¾ç®å¸«å·æ¿" |
| | | ], |
| | | "awards": "å
¥é¸ï¼2020年伯æ¨å大æä½³æ¥åå¡ ææèç
§ï¼äººèº«ä¿éªæ¥åå¡èç
§ãå¤å¹£æ¶ä»ä¿éªèç
§ã人身ä¿éªä»£ç人èç
§ã財ç¢ä¿éªä»£ç人èç
§" |
| | | } |
| | | "name" : "å´å¯¨", |
| | | "agentNo" : "R221444250", |
| | | "role" : "å°åä¿éªç¶ç´äºº", |
| | | "img" : "avatar10.jpg", |
| | | "avgScore" : 3.1, |
| | | "title" : "èç¶ç(DM)", |
| | | "phoneNumber" : "0987168787", |
| | | "serveArea" : "å
¨å°", |
| | | "companyAddress" : "è±è®ç¸£çéé®ä¸æ£è·¯30è9æ¨", |
| | | "latestLoginTime" : "2021-12-24T08:48:21.497Z", |
| | | "seniority" : "38 å¹´ ", |
| | | "suitability" : 50, |
| | | "evaluation" : 50, |
| | | "expertise" : [ "å¥åº·èä¿é", "å女æè²", "è³ç¢è¦å", "æ¨æ´»éä¼", "ä¿å®å¥æª¢/è¦å", "åç´
ä¿å®" ], |
| | | "concept" : "æ¯ä»½ä¿å®è¦åå¾ãå¿ãåºç¼\r\nç¨å¿ãç±å¿ãè²¼å¿å°å¾
æ¯ä½å®¢æ¶\r\n\r\næçä¸å¤§ä¿¡å¿µ\r\nâ é«cpå¼ ãä½ä¿è²»ãé«ä¿éã\r\nâ ç°¡å®å ãè¼é¬ãææä¿éªã\r\nâ 客製å ãé¸ä¸ä»½é©åçè¦åã\r\n\r\næä¾å®åçè¦åä¿è·æ¨åææç人", |
| | | "experiences" : "麻ççå·¥å¸é¢æè³èéèå¸ç³»,æ¿å¤§EMBA,ä¸å大å¸è²¡éç³»,人身ä¿éªèç
§,財ç¢ä¿éªèç
§,æè³åä¿å®èç
§,å¤å¹£æ¶ä»ä¿éªèç
§,éä¼éé¡ç²¾ç®å¸«èç
§,ç¢ç©ä¿éªï§¤è³ 人å¡èè©¦èæ¸,ç¾å壽éªç®¡ï§¤å¸«èæ¸,ç¾å壽éªï§¤è³ å¸«èæ¸,ç¾å財ç¢åæå¤éªæ¿ä¿å¸«èæ¸", |
| | | "awards" : "20次åéç¹¼çºçç(IQA)\r\n30次ç¾è¬åæ¡åæ(MDRT)æå¡\r\nç財è¦åé¡§åèªè(CFP)", |
| | | "gender" : "female", |
| | | "communicationStyle" : "謹æ
å實ãæå¿«ä¸»åãèå¿å¾è½ãå¥è«é¢¨è¶£" |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | éè¦é¡§åæ¬äººç»å
¥æå¯ä»¥ä¿®æ¹èªå·±çè³æ |
| | | |
| | | http post : |
| | | |
| | | http://localhost:8080/api/consultant/edit |
| | | |
| | | |
| | | request body : |
| | | { |
| | | "agentNo":"A568420", |
| | | "name":"è¬é風", |
| | | "expertise":["å¥åº·èä¿é","å女æè²"], |
| | | "title":"æ¥åå°å¡ (SC)", |
| | | "role":"å°åä¿éªç¶ç´äºº", |
| | | "serveArea":"å°åå¸ã屿±ç¸£", |
| | | "gender":"male", |
| | | "phoneNumber":"09123456789", |
| | | "companyAddress":"å®è縣äºçµéä¸èè·¯3è3æ¨", |
| | | "seniorityYear":40, |
| | | "seniorityMonth":10, |
| | | "concept":"æç§æï¼â¤ä¸å¼·è¿«æ¨é·ä¸¦ä¸æçºçæå!â¤ç«å ´åªå¢æ¿å®¢æ¶æè¡ææçæ¬ç!â¤å¤©åºä¸æ²ææå¥½çä¿éª åªææé©åèªå·±çä¿éª!", |
| | | "experiences":"å¥åº·èä¿é,å女æè²,è³ç¢è¦å", |
| | | "awards":"26次åéç¹¼çºçç(IQA)", |
| | | "communicationStyle":"è°æ", |
| | | "photoBase64":"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" |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.appointment.process; |
| | | |
| | | import java.time.Instant; |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.domain.AppointmentClosedInfo; |
| | | import com.pollex.pam.enums.ContactStatusEnum; |
| | | import com.pollex.pam.repository.AppointmentClosedInfoRepository; |
| | | import com.pollex.pam.repository.AppointmentRepository; |
| | | import com.pollex.pam.service.AppointmentClosedInfoService; |
| | | import com.pollex.pam.service.AppointmentService; |
| | | import com.pollex.pam.service.dto.AbstractAppointmentProcessDTO; |
| | | import com.pollex.pam.service.dto.DoneProcessDTO; |
| | | |
| | | @Service |
| | | public class AppointmentProcess{ |
| | | |
| | | @Autowired |
| | | List<AppointmentProcessInterface> processList; |
| | | |
| | | @Autowired |
| | | AppointmentService appointmentService; |
| | | |
| | | @Autowired |
| | | AppointmentRepository appointmentRepository; |
| | | |
| | | @Autowired |
| | | AppointmentClosedInfoRepository appointmentClosedInfoRepository; |
| | | |
| | | public void process(AbstractAppointmentProcessDTO dto) { |
| | | |
| | | processList.stream().forEach(process ->{ |
| | | if(process.getProcessType() == dto.getContactStatus()) { |
| | | Optional<AppointmentClosedInfo> closedInfoOP = appointmentClosedInfoRepository.findByAppointmentId(dto.getAppointmentId()); |
| | | if(closedInfoOP.isPresent()) { |
| | | process.editClosedInfo(dto, closedInfoOP.get()); |
| | | }else { |
| | | process.create(dto); |
| | | } |
| | | } |
| | | }); |
| | | |
| | | changeAppointmentCommunicateStatus(dto.getAppointmentId(), dto.getContactStatus()); |
| | | } |
| | | |
| | | private void changeAppointmentCommunicateStatus(Long appointmentId, ContactStatusEnum contactStatus) { |
| | | Appointment appointment = appointmentService.findById(appointmentId); |
| | | appointment.setCommunicateStatus(contactStatus); |
| | | appointmentRepository.save(appointment); |
| | | } |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.appointment.process; |
| | | |
| | | import com.pollex.pam.domain.AppointmentClosedInfo; |
| | | import com.pollex.pam.enums.ContactStatusEnum; |
| | | import com.pollex.pam.service.dto.AbstractAppointmentProcessDTO; |
| | | |
| | | public interface AppointmentProcessInterface { |
| | | |
| | | AppointmentClosedInfo create(AbstractAppointmentProcessDTO dto); |
| | | AppointmentClosedInfo editClosedInfo(AbstractAppointmentProcessDTO dto |
| | | , AppointmentClosedInfo closedInfo); |
| | | ContactStatusEnum getProcessType(); |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.appointment.process; |
| | | |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.domain.AppointmentClosedInfo; |
| | | import com.pollex.pam.enums.ContactStatusEnum; |
| | | import com.pollex.pam.repository.AppointmentClosedInfoRepository; |
| | | import com.pollex.pam.service.AppointmentClosedInfoService; |
| | | import com.pollex.pam.service.AppointmentService; |
| | | import com.pollex.pam.service.SatisfactionService; |
| | | import com.pollex.pam.service.dto.AbstractAppointmentProcessDTO; |
| | | import com.pollex.pam.service.dto.ClosedProcessDTO; |
| | | |
| | | @Service |
| | | @Transactional |
| | | public class ClosedProcess implements AppointmentProcessInterface{ |
| | | |
| | | @Autowired |
| | | AppointmentClosedInfoRepository appointmentClosedInfoRepository; |
| | | |
| | | @Autowired |
| | | AppointmentService appointmentService; |
| | | |
| | | @Autowired |
| | | AppointmentClosedInfoService appointmentClosedInfoService; |
| | | |
| | | @Autowired |
| | | SatisfactionService satisfactionService; |
| | | |
| | | @Override |
| | | public AppointmentClosedInfo create(AbstractAppointmentProcessDTO processDTO) { |
| | | ClosedProcessDTO closeProcess = toClosedProcessDTO(processDTO); |
| | | AppointmentClosedInfo closedInfo = new AppointmentClosedInfo(); |
| | | BeanUtils.copyProperties(closeProcess, closedInfo); |
| | | Appointment appointment = appointmentService.findById(processDTO.getAppointmentId()); |
| | | satisfactionService.createSatisfaction(appointment); |
| | | return appointmentClosedInfoRepository.save(closedInfo); |
| | | } |
| | | |
| | | private ClosedProcessDTO toClosedProcessDTO(AbstractAppointmentProcessDTO processDTO) { |
| | | ClosedProcessDTO closeProcess = (ClosedProcessDTO)processDTO; |
| | | BeanUtils.copyProperties(processDTO, closeProcess); |
| | | return closeProcess; |
| | | } |
| | | |
| | | @Override |
| | | public ContactStatusEnum getProcessType() { |
| | | return ContactStatusEnum.CLOSED; |
| | | } |
| | | |
| | | @Override |
| | | public AppointmentClosedInfo editClosedInfo( |
| | | AbstractAppointmentProcessDTO abstractDTO |
| | | , AppointmentClosedInfo closedInfo) { |
| | | ClosedProcessDTO closeProcess = toClosedProcessDTO(abstractDTO); |
| | | BeanUtils.copyProperties(closeProcess, closedInfo); |
| | | return appointmentClosedInfoRepository.save(closedInfo); |
| | | } |
| | | |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.appointment.process; |
| | | |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.domain.AppointmentClosedInfo; |
| | | import com.pollex.pam.enums.ContactStatusEnum; |
| | | import com.pollex.pam.repository.AppointmentClosedInfoRepository; |
| | | import com.pollex.pam.service.AppointmentClosedInfoService; |
| | | import com.pollex.pam.service.AppointmentService; |
| | | import com.pollex.pam.service.SatisfactionService; |
| | | import com.pollex.pam.service.dto.AbstractAppointmentProcessDTO; |
| | | import com.pollex.pam.service.dto.DoneProcessDTO; |
| | | |
| | | @Service |
| | | @Transactional |
| | | public class DoneProcess implements AppointmentProcessInterface{ |
| | | |
| | | @Autowired |
| | | AppointmentClosedInfoRepository appointmentClosedInfoRepository; |
| | | |
| | | @Autowired |
| | | AppointmentClosedInfoService appointmentClosedInfoService; |
| | | |
| | | @Autowired |
| | | SatisfactionService satisfactionService; |
| | | |
| | | @Autowired |
| | | AppointmentService appointmentService; |
| | | |
| | | @Override |
| | | public AppointmentClosedInfo create(AbstractAppointmentProcessDTO processDTO) { |
| | | DoneProcessDTO doneProcess = toDoneProcessDTO(processDTO); |
| | | AppointmentClosedInfo closedInfo = new AppointmentClosedInfo(); |
| | | BeanUtils.copyProperties(doneProcess, closedInfo); |
| | | Appointment appointment = appointmentService.findById(processDTO.getAppointmentId()); |
| | | satisfactionService.createSatisfaction(appointment); |
| | | return appointmentClosedInfoRepository.save(closedInfo); |
| | | } |
| | | |
| | | @Override |
| | | public ContactStatusEnum getProcessType() { |
| | | return ContactStatusEnum.DONE; |
| | | } |
| | | |
| | | @Override |
| | | public AppointmentClosedInfo editClosedInfo( |
| | | AbstractAppointmentProcessDTO abstractDTO |
| | | , AppointmentClosedInfo closedInfo) { |
| | | DoneProcessDTO doneProcess = toDoneProcessDTO(abstractDTO); |
| | | BeanUtils.copyProperties(doneProcess, closedInfo); |
| | | return appointmentClosedInfoRepository.save(closedInfo); |
| | | } |
| | | |
| | | private DoneProcessDTO toDoneProcessDTO(AbstractAppointmentProcessDTO abstractDTO) { |
| | | DoneProcessDTO doneProcess = (DoneProcessDTO)abstractDTO; |
| | | BeanUtils.copyProperties(abstractDTO, doneProcess); |
| | | return doneProcess; |
| | | } |
| | | |
| | | } |
| | |
| | | package com.pollex.pam.config; |
| | | |
| | | import com.pollex.pam.enums.SendEmailMsgMethod; |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | |
| | | /** |
| | |
| | | private String eServiceLoginFunc; |
| | | private String eServiceLoginSys; |
| | | private String frontEndDomain; |
| | | private boolean sendNotifyMsg; |
| | | private SMS sms; |
| | | private Email email; |
| | | private String fileFolderPath; |
| | | |
| | | public boolean isMockLogin() { |
| | | return mockLogin; |
| | |
| | | this.frontEndDomain = frontEndDomain; |
| | | } |
| | | |
| | | public boolean isSendNotifyMsg() { |
| | | return sendNotifyMsg; |
| | | } |
| | | |
| | | public void setSendNotifyMsg(boolean sendNotifyMsg) { |
| | | this.sendNotifyMsg = sendNotifyMsg; |
| | | } |
| | | |
| | | public SMS getSms() { |
| | | return sms; |
| | | } |
| | |
| | | private String sender; |
| | | private String smsType; |
| | | private String subject; |
| | | private boolean sendNotifyMsg; |
| | | |
| | | public String getUrl() { |
| | | return url; |
| | |
| | | public void setSubject(String subject) { |
| | | this.subject = subject; |
| | | } |
| | | |
| | | public boolean isSendNotifyMsg() { |
| | | return sendNotifyMsg; |
| | | } |
| | | |
| | | public void setSendNotifyMsg(boolean sendNotifyMsg) { |
| | | this.sendNotifyMsg = sendNotifyMsg; |
| | | } |
| | | } |
| | | |
| | | public static class Email { |
| | | private String url; |
| | | private String functionId; |
| | | private String senderEmail; |
| | | private boolean sendNotifyMsg; |
| | | private SendEmailMsgMethod method; |
| | | |
| | | public String getUrl() { |
| | | return url; |
| | |
| | | public void setSenderEmail(String senderEmail) { |
| | | this.senderEmail = senderEmail; |
| | | } |
| | | |
| | | public boolean isSendNotifyMsg() { |
| | | return sendNotifyMsg; |
| | | } |
| | | |
| | | public void setSendNotifyMsg(boolean sendNotifyMsg) { |
| | | this.sendNotifyMsg = sendNotifyMsg; |
| | | } |
| | | |
| | | public SendEmailMsgMethod getMethod() { |
| | | return method; |
| | | } |
| | | |
| | | public void setMethod(SendEmailMsgMethod method) { |
| | | this.method = method; |
| | | } |
| | | } |
| | | public String getFileFolderPath() { |
| | | return fileFolderPath; |
| | | } |
| | | |
| | | public void setFileFolderPath(String fileFolderPath) { |
| | | this.fileFolderPath = fileFolderPath; |
| | | } |
| | | |
| | | } |
| | |
| | | public static final String SYSTEM = "system"; |
| | | public static final String DEFAULT_LANGUAGE = "zh-tw"; |
| | | |
| | | /** |
| | | * é»è©±åemailå¨å»ºç«é ç´å®(T)å¾çN天è¦çºæªèçé ç´å® |
| | | * ç®åNçæ«å®çº2 |
| | | */ |
| | | public static final int APPOINTMENT_PENDING_PHONE_INTERVAL = 2; |
| | | public static final int APPOINTMENT_PENDING_EMAIL_INTERVAL = 2; |
| | | |
| | | /** |
| | | * é»è©±åemailå¨å»ºç«é ç´å®(T)å¾çN天æè¢«è¦çºæªèçé ç´å®ï¼ç¶å¤©æ¹æ¬¡æç¼éæé給顧å |
| | | * èå¨å¾ä¸å¤©(T+N+1)ï¼å°±æç¼éæ¹æ¬¡çµ¦å®¢æ¶åç¥ è©²é¡§åå¯è½å¿ç¢ç¡æ³èçï¼æ¯å¦éè¦åæ¶ |
| | | */ |
| | | public static final int APPOINTMENT_EXPIRING_PHONE_INTERVAL = APPOINTMENT_PENDING_PHONE_INTERVAL + 1; |
| | | public static final int APPOINTMENT_EXPIRING_EMAIL_INTERVAL = APPOINTMENT_PENDING_EMAIL_INTERVAL + 1; |
| | | |
| | | /** |
| | | * éç¥å®¢æ¶ç次æ¸éå¶ |
| | | */ |
| | | public static final int SEND_EXPIRING_NOTIFY_LIMIT = 1; |
| | | |
| | | public static final String SPRING_PROFILE_POLLEX_DEVELOPMENT = "pollex"; |
| | | |
| | | private Constants() {} |
| | | } |
| | |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.Instant; |
| | | import java.util.List; |
| | | |
| | | import javax.persistence.*; |
| | | import javax.persistence.CascadeType; |
| | | import javax.persistence.Column; |
| | | import javax.persistence.Entity; |
| | | import javax.persistence.EntityListeners; |
| | | import javax.persistence.EnumType; |
| | | import javax.persistence.Enumerated; |
| | | import javax.persistence.FetchType; |
| | | import javax.persistence.GeneratedValue; |
| | | import javax.persistence.GenerationType; |
| | | import javax.persistence.Id; |
| | | import javax.persistence.JoinColumn; |
| | | import javax.persistence.OneToMany; |
| | | import javax.persistence.OneToOne; |
| | | import javax.persistence.Table; |
| | | |
| | | import com.pollex.pam.enums.AppointmentStatusEnum; |
| | | import com.pollex.pam.enums.ContactStatusEnum; |
| | | import org.springframework.data.annotation.CreatedDate; |
| | | import org.springframework.data.annotation.LastModifiedDate; |
| | | import org.springframework.data.jpa.domain.support.AuditingEntityListener; |
| | | |
| | | import com.pollex.pam.enums.AppointmentStatusEnum; |
| | | import com.pollex.pam.enums.ContactStatusEnum; |
| | | |
| | | @EntityListeners(AuditingEntityListener.class) |
| | | @Entity |
| | |
| | | private Instant appointmentDate = Instant.now(); |
| | | |
| | | @Column(name = "last_modified_date") |
| | | @LastModifiedDate |
| | | private Instant lastModifiedDate = Instant.now(); |
| | | |
| | | @Column(name = "agent_no") |
| | |
| | | @Enumerated(value = EnumType.STRING) |
| | | @Column(name = "status") |
| | | private AppointmentStatusEnum status; |
| | | |
| | | @JoinColumn(name = "appointment_id") |
| | | @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) |
| | | private List<AppointmentMemo> appointmentMemoList; |
| | | |
| | | // @OneToOne(cascade = CascadeType.REMOVE,fetch=FetchType.EAGER) |
| | | //// @JoinColumn(name = "form_authority_id", referencedColumnName = "id") |
| | | // @JoinColumn(name = "appointment_id", referencedColumnName = "id") |
| | | // private AppointmentClosedInfo closedInfo; |
| | | |
| | | // @OneToOne(cascade = CascadeType.REMOVE |
| | | // , mappedBy = "appointment", fetch=FetchType.LAZY) |
| | | // private AppointmentClosedInfo closedInfo; |
| | | |
| | | public Long getId() { |
| | | return id; |
| | |
| | | public void setLastModifiedDate(Instant lastModifiedDate) { |
| | | this.lastModifiedDate = lastModifiedDate; |
| | | } |
| | | |
| | | public List<AppointmentMemo> getAppointmentMemoList() { |
| | | return appointmentMemoList; |
| | | } |
| | | |
| | | public void setAppointmentMemoList(List<AppointmentMemo> appointmentMemoList) { |
| | | this.appointmentMemoList = appointmentMemoList; |
| | | } |
| | | |
| | | // public AppointmentClosedInfo getClosedInfo() { |
| | | // return closedInfo; |
| | | // } |
| | | // |
| | | // public void setClosedInfo(AppointmentClosedInfo closedInfo) { |
| | | // this.closedInfo = closedInfo; |
| | | // } |
| | | |
| | | |
| | | |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.domain; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.Date; |
| | | |
| | | import javax.persistence.Column; |
| | | import javax.persistence.Entity; |
| | | import javax.persistence.GeneratedValue; |
| | | import javax.persistence.GenerationType; |
| | | import javax.persistence.Id; |
| | | import javax.persistence.Table; |
| | | |
| | | @Entity |
| | | @Table(name = "appointment_closed_info") |
| | | public class AppointmentClosedInfo implements Serializable { |
| | | |
| | | /** |
| | | * |
| | | */ |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @Id |
| | | @GeneratedValue(strategy = GenerationType.IDENTITY) |
| | | private Long id; |
| | | |
| | | @Column(name = "policyholder_identity_id") |
| | | private String policyholderIdentityId; |
| | | |
| | | @Column(name = "plan_code") |
| | | private String planCode; |
| | | |
| | | @Column(name = "policy_entry_date") |
| | | private Date policyEntryDate; |
| | | |
| | | @Column(name = "remark") |
| | | private String remark; |
| | | |
| | | @Column(name = "closed_reason") |
| | | private String closedReason; |
| | | |
| | | @Column(name = "closed_other_reason") |
| | | private String closedOtherReason; |
| | | |
| | | @Column(name = "appointment_id") |
| | | private Long appointmentId; |
| | | |
| | | public Long getId() { |
| | | return id; |
| | | } |
| | | |
| | | public void setId(Long id) { |
| | | this.id = id; |
| | | } |
| | | |
| | | public String getPolicyholderIdentityId() { |
| | | return policyholderIdentityId; |
| | | } |
| | | |
| | | public void setPolicyholderIdentityId(String policyholderIdentityId) { |
| | | this.policyholderIdentityId = policyholderIdentityId; |
| | | } |
| | | |
| | | public String getPlanCode() { |
| | | return planCode; |
| | | } |
| | | |
| | | public void setPlanCode(String planCode) { |
| | | this.planCode = planCode; |
| | | } |
| | | |
| | | public Date getPolicyEntryDate() { |
| | | return policyEntryDate; |
| | | } |
| | | |
| | | public void setPolicyEntryDate(Date policyEntryDate) { |
| | | this.policyEntryDate = policyEntryDate; |
| | | } |
| | | |
| | | public String getRemark() { |
| | | return remark; |
| | | } |
| | | |
| | | public void setRemark(String remark) { |
| | | this.remark = remark; |
| | | } |
| | | |
| | | public String getClosedReason() { |
| | | return closedReason; |
| | | } |
| | | |
| | | public void setClosedReason(String closedReason) { |
| | | this.closedReason = closedReason; |
| | | } |
| | | |
| | | public String getClosedOtherReason() { |
| | | return closedOtherReason; |
| | | } |
| | | |
| | | public void setClosedOtherReason(String closedOtherReason) { |
| | | this.closedOtherReason = closedOtherReason; |
| | | } |
| | | |
| | | public Long getAppointmentId() { |
| | | return appointmentId; |
| | | } |
| | | |
| | | public void setAppointmentId(Long appointmentId) { |
| | | this.appointmentId = appointmentId; |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.Instant; |
| | | import java.util.List; |
| | | |
| | | import javax.persistence.CascadeType; |
| | | import javax.persistence.Column; |
| | | import javax.persistence.Entity; |
| | | import javax.persistence.EnumType; |
| | | import javax.persistence.Enumerated; |
| | | import javax.persistence.FetchType; |
| | | import javax.persistence.Id; |
| | | import javax.persistence.JoinColumn; |
| | | import javax.persistence.OneToMany; |
| | | import javax.persistence.Table; |
| | | |
| | | import com.pollex.pam.enums.AppointmentStatusEnum; |
| | |
| | | @Enumerated(value = EnumType.STRING) |
| | | @Column(name = "status") |
| | | private AppointmentStatusEnum status; |
| | | |
| | | @JoinColumn(name = "appointment_id") |
| | | @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.REMOVE) |
| | | private List<AppointmentMemo> appointmentMemoList; |
| | | |
| | | public Long getId() { |
| | | return id; |
| | |
| | | public void setStatus(AppointmentStatusEnum status) { |
| | | this.status = status; |
| | | } |
| | | |
| | | public List<AppointmentMemo> getAppointmentMemoList() { |
| | | return appointmentMemoList; |
| | | } |
| | | |
| | | public void setAppointmentMemoList(List<AppointmentMemo> appointmentMemoList) { |
| | | this.appointmentMemoList = appointmentMemoList; |
| | | } |
| | | |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.domain; |
| | | |
| | | import javax.persistence.*; |
| | | import java.io.Serializable; |
| | | import java.time.Instant; |
| | | |
| | | @Entity |
| | | @Table(name = "appointment_expiring_notify_record") |
| | | public class AppointmentExpiringNotifyRecord implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @Id |
| | | @GeneratedValue(strategy = GenerationType.IDENTITY) |
| | | private Long id; |
| | | |
| | | @Column(name = "appointment_id") |
| | | private Long appointmentId; |
| | | |
| | | @Column(name = "send_time") |
| | | private Instant sendTime; |
| | | |
| | | public Long getId() { |
| | | return id; |
| | | } |
| | | |
| | | public void setId(Long id) { |
| | | this.id = id; |
| | | } |
| | | |
| | | public Long getAppointmentId() { |
| | | return appointmentId; |
| | | } |
| | | |
| | | public void setAppointmentId(Long appointmentId) { |
| | | this.appointmentId = appointmentId; |
| | | } |
| | | |
| | | public Instant getSendTime() { |
| | | return sendTime; |
| | | } |
| | | |
| | | public void setSendTime(Instant sendTime) { |
| | | this.sendTime = sendTime; |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.domain; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | import javax.persistence.Column; |
| | | import javax.persistence.Entity; |
| | | import javax.persistence.GeneratedValue; |
| | | import javax.persistence.GenerationType; |
| | | import javax.persistence.Id; |
| | | import javax.persistence.Table; |
| | | |
| | | @Entity |
| | | @Table(name = "appointment_memo") |
| | | public class AppointmentMemo extends AbstractAuditingEntity implements Serializable { |
| | | |
| | | /** |
| | | * |
| | | */ |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @Id |
| | | @GeneratedValue(strategy = GenerationType.IDENTITY) |
| | | private Long id; |
| | | |
| | | @Column(name = "content") |
| | | private String content; |
| | | |
| | | @Column(name = "appointment_id") |
| | | private Long appointmentId; |
| | | |
| | | public Long getId() { |
| | | return id; |
| | | } |
| | | |
| | | public void setId(Long id) { |
| | | this.id = id; |
| | | } |
| | | |
| | | public String getContent() { |
| | | return content; |
| | | } |
| | | |
| | | public void setContent(String content) { |
| | | this.content = content; |
| | | } |
| | | |
| | | public Long getAppointmentId() { |
| | | return appointmentId; |
| | | } |
| | | |
| | | public void setAppointmentId(Long appointmentId) { |
| | | this.appointmentId = appointmentId; |
| | | } |
| | | |
| | | |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.domain; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.Instant; |
| | | import java.util.Date; |
| | | |
| | | import javax.persistence.Column; |
| | | import javax.persistence.Entity; |
| | | import javax.persistence.GeneratedValue; |
| | | import javax.persistence.GenerationType; |
| | | import javax.persistence.Id; |
| | | import javax.persistence.Table; |
| | | |
| | | import org.springframework.data.annotation.CreatedBy; |
| | | import org.springframework.data.annotation.CreatedDate; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonIgnore; |
| | | |
| | | @Entity |
| | | @Table(name = "appointment_notice_log") |
| | | public class AppointmentNoticeLog implements Serializable { |
| | | |
| | | /** |
| | | * |
| | | */ |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @Id |
| | | @GeneratedValue(strategy = GenerationType.IDENTITY) |
| | | private Long id; |
| | | |
| | | @Column(name = "phone") |
| | | private String phone; |
| | | |
| | | @Column(name = "email") |
| | | private String email; |
| | | |
| | | @Column(name = "appointment_id") |
| | | private Long appointmentId; |
| | | |
| | | // @Column(name = "type") |
| | | // private String type; |
| | | |
| | | @Column(name = "content") |
| | | private String content; |
| | | |
| | | // @CreatedBy |
| | | // @Column(name = "created_by", nullable = false, length = 50, updatable = false) |
| | | // @JsonIgnore |
| | | // private String createdBy; |
| | | |
| | | @CreatedDate |
| | | @Column(name = "created_date", updatable = false) |
| | | private Instant createdDate = Instant.now(); |
| | | |
| | | @Column(name = "interview_date") |
| | | private Date interviewDate; |
| | | |
| | | public Long getId() { |
| | | return id; |
| | | } |
| | | |
| | | public void setId(Long id) { |
| | | this.id = id; |
| | | } |
| | | |
| | | public String getPhone() { |
| | | return phone; |
| | | } |
| | | |
| | | public void setPhone(String phone) { |
| | | this.phone = phone; |
| | | } |
| | | |
| | | public String getEmail() { |
| | | return email; |
| | | } |
| | | |
| | | public void setEmail(String email) { |
| | | this.email = email; |
| | | } |
| | | |
| | | public Long getAppointmentId() { |
| | | return appointmentId; |
| | | } |
| | | |
| | | public void setAppointmentId(Long appointmentId) { |
| | | this.appointmentId = appointmentId; |
| | | } |
| | | |
| | | // public String getType() { |
| | | // return type; |
| | | // } |
| | | // |
| | | // public void setType(String type) { |
| | | // this.type = type; |
| | | // } |
| | | |
| | | public String getContent() { |
| | | return content; |
| | | } |
| | | |
| | | public void setContent(String content) { |
| | | this.content = content; |
| | | } |
| | | |
| | | // public String getCreatedBy() { |
| | | // return createdBy; |
| | | // } |
| | | // |
| | | // public void setCreatedBy(String createdBy) { |
| | | // this.createdBy = createdBy; |
| | | // } |
| | | |
| | | public Instant getCreatedDate() { |
| | | return createdDate; |
| | | } |
| | | |
| | | public void setCreatedDate(Instant createdDate) { |
| | | this.createdDate = createdDate; |
| | | } |
| | | |
| | | public Date getInterviewDate() { |
| | | return interviewDate; |
| | | } |
| | | |
| | | public void setInterviewDate(Date interviewDate) { |
| | | this.interviewDate = interviewDate; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | } |
| | | |
| | | public void setCompanyAddress(String companyAddress) { |
| | | companyAddress = companyAddress; |
| | | this.companyAddress = companyAddress; |
| | | } |
| | | |
| | | public Long getSeniorityYear() { |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.domain; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.Date; |
| | | |
| | | import javax.persistence.Column; |
| | | import javax.persistence.Entity; |
| | | import javax.persistence.EnumType; |
| | | import javax.persistence.Enumerated; |
| | | import javax.persistence.GeneratedValue; |
| | | import javax.persistence.GenerationType; |
| | | import javax.persistence.Id; |
| | | import javax.persistence.Table; |
| | | |
| | | import com.pollex.pam.enums.InterviewRecordStatusEnum; |
| | | |
| | | @Entity |
| | | @Table(name = "interview_record") |
| | | public class InterviewRecord extends AbstractAuditingEntity implements Serializable { |
| | | |
| | | /** |
| | | * |
| | | */ |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @Id |
| | | @GeneratedValue(strategy = GenerationType.IDENTITY) |
| | | private Long id; |
| | | |
| | | @Column(name = "content") |
| | | private String content; |
| | | |
| | | @Column(name = "interview_date") |
| | | private Date interviewDate; |
| | | |
| | | @Column(name = "appointment_id") |
| | | private Long appointmentId; |
| | | |
| | | @Enumerated(EnumType.STRING) |
| | | @Column(name = "status") |
| | | private InterviewRecordStatusEnum status; |
| | | |
| | | |
| | | |
| | | public Long getId() { |
| | | return id; |
| | | } |
| | | |
| | | public void setId(Long id) { |
| | | this.id = id; |
| | | } |
| | | |
| | | public String getContent() { |
| | | return content; |
| | | } |
| | | |
| | | public void setContent(String content) { |
| | | this.content = content; |
| | | } |
| | | |
| | | public Date getInterviewDate() { |
| | | return interviewDate; |
| | | } |
| | | |
| | | public void setInterviewDate(Date interviewDate) { |
| | | this.interviewDate = interviewDate; |
| | | } |
| | | |
| | | public Long getAppointmentId() { |
| | | return appointmentId; |
| | | } |
| | | |
| | | public void setAppointmentId(Long appointmentId) { |
| | | this.appointmentId = appointmentId; |
| | | } |
| | | |
| | | public InterviewRecordStatusEnum getStatus() { |
| | | return status; |
| | | } |
| | | |
| | | public void setStatus(InterviewRecordStatusEnum status) { |
| | | this.status = status; |
| | | } |
| | | |
| | | |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.domain; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.Instant; |
| | | |
| | | import javax.persistence.Column; |
| | | import javax.persistence.Entity; |
| | | import javax.persistence.EnumType; |
| | | import javax.persistence.Enumerated; |
| | | import javax.persistence.GeneratedValue; |
| | | import javax.persistence.GenerationType; |
| | | import javax.persistence.Id; |
| | | import javax.persistence.Table; |
| | | |
| | | import org.springframework.data.annotation.CreatedDate; |
| | | |
| | | import com.pollex.pam.enums.NotificationTypeEnum; |
| | | import com.pollex.pam.enums.PersonalNotificationRoleEnum; |
| | | |
| | | @Entity |
| | | @Table(name = "personal_notification") |
| | | public class PersonalNotification implements Serializable { |
| | | |
| | | /** |
| | | * |
| | | */ |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @Id |
| | | @GeneratedValue(strategy = GenerationType.IDENTITY) |
| | | private Long id; |
| | | |
| | | @Column(name = "title") |
| | | private String title; |
| | | |
| | | @Column(name = "content") |
| | | private String content; |
| | | |
| | | @Enumerated(EnumType.STRING) |
| | | @Column(name = "notification_type") |
| | | private NotificationTypeEnum notificationType; |
| | | |
| | | @Enumerated(EnumType.STRING) |
| | | @Column(name = "owner_role") |
| | | private PersonalNotificationRoleEnum ownerRole; |
| | | |
| | | @Column(name = "owner_id") |
| | | private Long ownerId; |
| | | |
| | | @CreatedDate |
| | | @Column(name = "created_date", updatable = false) |
| | | private Instant createdDate = Instant.now(); |
| | | |
| | | @Column(name = "read_date") |
| | | private Instant readDate; |
| | | |
| | | public Long getId() { |
| | | return id; |
| | | } |
| | | |
| | | public void setId(Long id) { |
| | | this.id = id; |
| | | } |
| | | |
| | | public String getTitle() { |
| | | return title; |
| | | } |
| | | |
| | | public void setTitle(String title) { |
| | | this.title = title; |
| | | } |
| | | |
| | | public String getContent() { |
| | | return content; |
| | | } |
| | | |
| | | public void setContent(String content) { |
| | | this.content = content; |
| | | } |
| | | |
| | | public NotificationTypeEnum getNotificationType() { |
| | | return notificationType; |
| | | } |
| | | |
| | | public void setNotificationType(NotificationTypeEnum notificationType) { |
| | | this.notificationType = notificationType; |
| | | } |
| | | |
| | | public PersonalNotificationRoleEnum getOwnerRole() { |
| | | return ownerRole; |
| | | } |
| | | |
| | | public void setOwnerRole(PersonalNotificationRoleEnum ownerRole) { |
| | | this.ownerRole = ownerRole; |
| | | } |
| | | |
| | | public Long getOwnerId() { |
| | | return ownerId; |
| | | } |
| | | |
| | | public void setOwnerId(Long ownerId) { |
| | | this.ownerId = ownerId; |
| | | } |
| | | |
| | | public Instant getCreatedDate() { |
| | | return createdDate; |
| | | } |
| | | |
| | | public void setCreatedDate(Instant createdDate) { |
| | | this.createdDate = createdDate; |
| | | } |
| | | |
| | | public Instant getReadDate() { |
| | | return readDate; |
| | | } |
| | | |
| | | public void setReadDate(Instant readDate) { |
| | | this.readDate = readDate; |
| | | } |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | RESERVED, |
| | | |
| | | @JsonProperty("contacted") |
| | | CONTACTED |
| | | CONTACTED, |
| | | |
| | | @JsonProperty("done") |
| | | DONE, |
| | | |
| | | @JsonProperty("closed") |
| | | CLOSED, |
| | | |
| | | @JsonProperty("cancel") |
| | | CANCEL |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.enums; |
| | | |
| | | public enum InterviewRecordStatusEnum { |
| | | AVAILABLE, |
| | | DELETED |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.enums; |
| | | |
| | | public enum NotificationTypeEnum { |
| | | SYSTEM, |
| | | ACTIVITY |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.enums; |
| | | |
| | | public enum PersonalNotificationRoleEnum { |
| | | CUSTOMER, |
| | | CONSULTANT |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.enums; |
| | | |
| | | public enum SendEmailMsgMethod { |
| | | PAM_EMAIL_SERVICE, |
| | | POLLEX_GMAIL |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.repository; |
| | | |
| | | import java.util.Optional; |
| | | |
| | | import org.springframework.data.jpa.repository.JpaRepository; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | | import com.pollex.pam.domain.AppointmentClosedInfo; |
| | | |
| | | @Repository |
| | | public interface AppointmentClosedInfoRepository extends JpaRepository<AppointmentClosedInfo, Long>{ |
| | | |
| | | Optional<AppointmentClosedInfo> findByAppointmentId(Long apId); |
| | | |
| | | } |
| | |
| | | |
| | | import java.util.List; |
| | | |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.enums.AppointmentStatusEnum; |
| | | import com.pollex.pam.enums.ContactStatusEnum; |
| | | import org.springframework.data.jpa.repository.JpaRepository; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | |
| | | public interface AppointmentCustomerViewRepository extends JpaRepository<AppointmentCustomerView, Long>{ |
| | | List<AppointmentCustomerView> findByAgentNo(String agentNo); |
| | | List<AppointmentCustomerView> findByAgentNoAndCustomerId(String agentNo, Long customerId); |
| | | List<AppointmentCustomerView> findAllByCommunicateStatusAndStatus(ContactStatusEnum contactStatus, AppointmentStatusEnum status); |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.repository; |
| | | |
| | | import com.pollex.pam.domain.AppointmentExpiringNotifyRecord; |
| | | import org.springframework.data.jpa.repository.JpaRepository; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Repository |
| | | public interface AppointmentExpiringNotifyRecordRepository extends JpaRepository<AppointmentExpiringNotifyRecord, Long> { |
| | | List<AppointmentExpiringNotifyRecord> findAllByAppointmentId(Long appointmentId); |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.repository; |
| | | |
| | | import org.springframework.data.jpa.repository.JpaRepository; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | | import com.pollex.pam.domain.AppointmentMemo; |
| | | |
| | | @Repository |
| | | public interface AppointmentMemoRepository extends JpaRepository<AppointmentMemo, Long>{ |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.repository; |
| | | |
| | | import java.util.List; |
| | | |
| | | import org.springframework.data.jpa.repository.JpaRepository; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | | import com.pollex.pam.domain.AppointmentNoticeLog; |
| | | |
| | | @Repository |
| | | public interface AppointmentNoticeLogRepository extends JpaRepository<AppointmentNoticeLog, Long>{ |
| | | |
| | | List<AppointmentNoticeLog> findByAppointmentId(Long appointmentId); |
| | | |
| | | } |
| | |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | |
| | | import com.pollex.pam.enums.AppointmentStatusEnum; |
| | | import com.pollex.pam.enums.ContactStatusEnum; |
| | | import org.springframework.data.jpa.repository.JpaRepository; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | |
| | | List<Appointment> findByAgentNoAndCustomerId(String agentNo, Long customerId); |
| | | |
| | | Optional<Appointment> findTopByAgentNoAndCustomerIdOrderByAppointmentDateDesc(String agentNo, Long customerId); |
| | | |
| | | List<Appointment> findAllByCommunicateStatusAndStatus(ContactStatusEnum contactStatus, AppointmentStatusEnum status); |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.repository; |
| | | |
| | | import java.util.List; |
| | | |
| | | import org.springframework.data.jpa.repository.JpaRepository; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | | import com.pollex.pam.domain.InterviewRecord; |
| | | import com.pollex.pam.enums.InterviewRecordStatusEnum; |
| | | |
| | | @Repository |
| | | public interface InterviewRecordRepository extends JpaRepository<InterviewRecord, Long>{ |
| | | |
| | | List<InterviewRecord> findByAppointmentId(Long appointmentId); |
| | | |
| | | List<InterviewRecord> findByAppointmentIdAndStatus(Long appointmentId, InterviewRecordStatusEnum status); |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.repository; |
| | | |
| | | import java.util.List; |
| | | |
| | | import org.springframework.data.jpa.repository.JpaRepository; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | | import com.pollex.pam.domain.PersonalNotification; |
| | | import com.pollex.pam.enums.PersonalNotificationRoleEnum; |
| | | |
| | | @Repository |
| | | public interface PersonalNotificationRepository extends JpaRepository<PersonalNotification, Long>{ |
| | | |
| | | List<PersonalNotification> findAllByOwnerRoleAndOwnerId(PersonalNotificationRoleEnum role, Long ownerId); |
| | | |
| | | } |
| | |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | |
| | | import com.pollex.pam.enums.SatisfactionStatusEnum; |
| | | import org.springframework.data.jpa.repository.JpaRepository; |
| | | import org.springframework.data.jpa.repository.Query; |
| | | import org.springframework.data.repository.query.Param; |
| | |
| | | |
| | | Optional<Satisfaction> findOneByAppointmentId(Long appointmentId); |
| | | |
| | | List<Satisfaction> findAllByStatus(SatisfactionStatusEnum status); |
| | | |
| | | @Query(value = "SELECT avg(score) FROM satisfaction where agent_no=:agent_no" |
| | | + " and score is not null" |
| | | , nativeQuery = true) |
| | | Float getAgentScoreAvg(@Param("agent_no") String agentNo); |
| | | } |
| | | |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import com.pollex.pam.domain.AppointmentClosedInfo; |
| | | import com.pollex.pam.repository.AppointmentClosedInfoRepository; |
| | | import com.pollex.pam.web.rest.errors.AppointmentClosedInfoNotFoundException; |
| | | |
| | | @Service |
| | | @Transactional |
| | | public class AppointmentClosedInfoService { |
| | | |
| | | @Autowired |
| | | AppointmentClosedInfoRepository appointmentClosedInfoRepository; |
| | | |
| | | public AppointmentClosedInfo findByAppointmentId(Long apId) { |
| | | return appointmentClosedInfoRepository.findByAppointmentId(apId) |
| | | .orElseThrow(AppointmentClosedInfoNotFoundException::new); |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.domain.AppointmentMemo; |
| | | import com.pollex.pam.repository.AppointmentMemoRepository; |
| | | import com.pollex.pam.repository.AppointmentRepository; |
| | | import com.pollex.pam.security.SecurityUtils; |
| | | import com.pollex.pam.service.dto.AppointmentMemoCreateDTO; |
| | | import com.pollex.pam.service.dto.AppointmentMemoUpdateDTO; |
| | | import com.pollex.pam.service.mapper.AppointmentMemoMapper; |
| | | import com.pollex.pam.web.rest.errors.AppointmentMemoNotFoundException; |
| | | import com.pollex.pam.web.rest.errors.AppointmentNotFoundException; |
| | | |
| | | @Service |
| | | @Transactional |
| | | public class AppointmentMemoService { |
| | | |
| | | @Autowired |
| | | AppointmentMemoRepository appointmentMemoRepository; |
| | | |
| | | @Autowired |
| | | AppointmentMemoMapper appointmentMemoMapper; |
| | | |
| | | @Autowired |
| | | AppointmentRepository appointmentRepository; |
| | | |
| | | public AppointmentMemo create(AppointmentMemoCreateDTO memoDTO) { |
| | | AppointmentMemo memo = appointmentMemoMapper.toAppointmentMemo(memoDTO); |
| | | return appointmentMemoRepository.save(memo); |
| | | } |
| | | |
| | | public void checkPermission(Long appointmentId) { |
| | | Appointment appointment = appointmentRepository.findById(appointmentId) |
| | | .orElseThrow(AppointmentNotFoundException::new); |
| | | if(!appointment.getAgentNo().equals(SecurityUtils.getAgentNo())) { |
| | | throw new IllegalAccessError("not have permission"); |
| | | } |
| | | } |
| | | |
| | | public AppointmentMemo update(AppointmentMemoUpdateDTO memoDTO) { |
| | | AppointmentMemo memo = appointmentMemoRepository |
| | | .findById(memoDTO.getId()) |
| | | .orElseThrow(AppointmentMemoNotFoundException::new); |
| | | checkPermission(memo.getAppointmentId()); |
| | | appointmentMemoMapper.copyToAppointmentMemo(memoDTO, memo); |
| | | return appointmentMemoRepository.save(memo); |
| | | } |
| | | |
| | | public void delete(Long memoId) { |
| | | AppointmentMemo memo = appointmentMemoRepository |
| | | .findById(memoId) |
| | | .orElseThrow(AppointmentMemoNotFoundException::new); |
| | | checkPermission(memo.getAppointmentId()); |
| | | appointmentMemoRepository.delete(memo); |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service; |
| | | |
| | | import java.util.List; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import com.pollex.pam.domain.AppointmentNoticeLog; |
| | | import com.pollex.pam.repository.AppointmentNoticeLogRepository; |
| | | import com.pollex.pam.service.dto.AppointmentNoticeSendDTO; |
| | | import com.pollex.pam.service.mapper.AppointmentNoticeSendMapper; |
| | | |
| | | @Service |
| | | @Transactional |
| | | public class AppointmentNoticeLogService { |
| | | |
| | | @Autowired |
| | | AppointmentNoticeLogRepository appointmentNoticeLogRepository; |
| | | |
| | | @Autowired |
| | | AppointmentNoticeSendMapper appointmentNoticeSendMapper; |
| | | |
| | | public AppointmentNoticeLog create(AppointmentNoticeSendDTO noticeSendDTO) { |
| | | AppointmentNoticeLog appointmentNoticeLog = |
| | | appointmentNoticeSendMapper.toAppointmentNoticeLog(noticeSendDTO); |
| | | return appointmentNoticeLogRepository.save(appointmentNoticeLog); |
| | | } |
| | | |
| | | public List<AppointmentNoticeLog> findByAppointmentId(Long appointmentId){ |
| | | return appointmentNoticeLogRepository.findByAppointmentId(appointmentId); |
| | | } |
| | | } |
| | |
| | | package com.pollex.pam.service; |
| | | |
| | | import java.time.Instant; |
| | | import java.time.LocalDate; |
| | | import java.time.ZoneId; |
| | | import java.util.Comparator; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | import java.util.Optional; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import com.pollex.pam.appointment.process.AppointmentProcess; |
| | | import com.pollex.pam.config.ApplicationProperties; |
| | | import com.pollex.pam.service.dto.AppointmentUpdateDTO; |
| | | import com.pollex.pam.config.Constants; |
| | | import com.pollex.pam.service.dto.*; |
| | | import com.pollex.pam.web.rest.errors.NotFoundExpiringAppointmentException; |
| | | import com.pollex.pam.web.rest.errors.SendEmailFailException; |
| | | import com.pollex.pam.web.rest.errors.SendSMSFailException; |
| | | import io.jsonwebtoken.lang.Assert; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.domain.AppointmentCustomerView; |
| | | import com.pollex.pam.enums.ContactStatusEnum; |
| | | import com.pollex.pam.enums.InterviewRecordStatusEnum; |
| | | import com.pollex.pam.repository.AppointmentCustomerViewRepository; |
| | | import com.pollex.pam.repository.AppointmentRepository; |
| | | import com.pollex.pam.security.SecurityUtils; |
| | | import com.pollex.pam.service.dto.AppointmentCreateDTO; |
| | | import com.pollex.pam.service.dto.AppointmentCustomerViewDTO; |
| | | import com.pollex.pam.service.mapper.AppointmentCustomerViewMapper; |
| | | import com.pollex.pam.service.mapper.AppointmentDTOMapper; |
| | | import com.pollex.pam.web.rest.errors.AppointmentNotFoundException; |
| | |
| | | @Autowired |
| | | SpringTemplateEngine springTemplateEngine; |
| | | |
| | | @Autowired |
| | | InterviewRecordService interviewRecordService; |
| | | |
| | | @Autowired |
| | | AppointmentProcess abstractAppointmentProcess; |
| | | |
| | | @Autowired |
| | | PersonalNotificationService personalNotificationService; |
| | | |
| | | public Appointment customerCreateAppointment(AppointmentCreateDTO appointmentCreateDTO) { |
| | | Appointment appointment = appointmentDTOMapper.toAppointment(appointmentCreateDTO); |
| | | appointment.setStatus(AVAILABLE); |
| | |
| | | return appointmentRepository.save(appointment); |
| | | } |
| | | |
| | | public void updateAppointment(AppointmentUpdateDTO updateAppointmentDTO) { |
| | | public Appointment updateAppointment(AppointmentUpdateDTO updateAppointmentDTO) { |
| | | Appointment appointment = appointmentRepository.findById(updateAppointmentDTO.getId()).get(); |
| | | |
| | | BeanUtils.copyProperties(updateAppointmentDTO, appointment); |
| | | appointment.setPhone(updateAppointmentDTO.getPhone()); |
| | | appointment.setEmail(updateAppointmentDTO.getEmail()); |
| | | appointment.setContactType(updateAppointmentDTO.getContactType()); |
| | |
| | | appointment.setOtherRequirement(updateAppointmentDTO.getOtherRequirement()); |
| | | appointment.setLastModifiedDate(Instant.now()); |
| | | |
| | | appointmentRepository.save(appointment); |
| | | return appointmentRepository.save(appointment); |
| | | } |
| | | |
| | | public void markAppointmentDeleted(Long appointmentId) { |
| | | Appointment appointment = appointmentRepository.findById(appointmentId).get(); |
| | | appointment.setStatus(DELETED); |
| | | appointment.setLastModifiedDate(Instant.now()); |
| | | appointment.setCommunicateStatus(ContactStatusEnum.CANCEL); |
| | | appointmentRepository.save(appointment); |
| | | personalNotificationService.createMarkAppointmentDeletedToConsultant(appointment); |
| | | |
| | | } |
| | | |
| | | public List<Appointment> findByAgentNo(String agentNo) { |
| | |
| | | |
| | | AppointmentCustomerViewDTO dto = appointmentCustomerViewMapper.toAppointmentCustomerViewDTO(appointment); |
| | | setSatisfactionScore(dto, appointmentId); |
| | | // setInterviewRecordDTO(dto); |
| | | return dto; |
| | | } |
| | | |
| | | public List<AppointmentCustomerViewDTO> getConsultantAvailableAppointments(String agentNo) { |
| | | public void setInterviewRecordDTO(AppointmentCustomerViewDTO dto) { |
| | | List<InterviewRecordDTO> interviewRecords = interviewRecordService.findByAppointmentIdAndStatus(dto.getId(), InterviewRecordStatusEnum.AVAILABLE); |
| | | dto.setInterviewRecordDTOs(interviewRecords); |
| | | } |
| | | |
| | | public List<AppointmentCustomerViewDTO> getConsultantAvailableAppointments(String agentNo) { |
| | | return appointmentCustomerViewRepository.findByAgentNo(agentNo).stream() |
| | | .filter(appointment -> appointment.getStatus() == AVAILABLE) |
| | | .map(appointmentCustomerView -> { |
| | |
| | | public void sendAppointmentNotify(Appointment appointment) { |
| | | Assert.notNull(appointment, "appointment entity cannot be null"); |
| | | |
| | | log.debug("is need send appointment notify msg? = {}", applicationProperties.isSendNotifyMsg()); |
| | | if(applicationProperties.isSendNotifyMsg()) { |
| | | log.debug("sending appointment notify, appointmentId = {}", appointment.getId()); |
| | | sendAppointmentNotifyBySMS(appointment); |
| | | sendAppointmentNotifyByHtmlEmail(appointment); |
| | | } |
| | | log.debug("is need send appointment notify msg? sms = {}, email = {}", |
| | | applicationProperties.getSms().isSendNotifyMsg(), applicationProperties.getEmail().isSendNotifyMsg()); |
| | | |
| | | log.debug("sending appointment notify, appointmentId = {}", appointment.getId()); |
| | | sendAppointmentNotifyBySMS(appointment); |
| | | sendAppointmentNotifyByHtmlEmail(appointment); |
| | | } |
| | | |
| | | private void sendAppointmentNotifyBySMS(Appointment appointment) { |
| | |
| | | } |
| | | |
| | | private void sendAppointmentNotifyByHtmlEmail(Appointment appointment) { |
| | | String senderEmail = applicationProperties.getEmail().getSenderEmail(); |
| | | String consultantEmail = consultantService.findByAgentNo(appointment.getAgentNo()).getEmail(); |
| | | String consultantEmail = consultantService.findByAgentNo(appointment.getAgentNo()).getEmail(); |
| | | String customerMobile = appointment.getPhone(); |
| | | String normalContent; |
| | | |
| | |
| | | throw new SendEmailFailException("the consultant does not have email!"); |
| | | } |
| | | |
| | | sendMsgService.sendMsgByEmail(senderEmail, consultantEmail, NOTIFY_EMAIL_SUBJECT, content, true); |
| | | sendMsgService.sendMsgByEmail(consultantEmail, NOTIFY_EMAIL_SUBJECT, content, true); |
| | | } catch (SendEmailFailException e) { |
| | | log.warn("send appointment notify by email was fail, appointment Id = {}", appointment.getId(), e); |
| | | } |
| | |
| | | public String getAppointmentDetailUrl(Long appointmentId) { |
| | | return applicationProperties.getFrontEndDomain() + "/myAppointmentList/contactedList?appointmentId=" + appointmentId; |
| | | } |
| | | |
| | | public Appointment findById(Long id) { |
| | | return appointmentRepository.findById(id) |
| | | .orElseThrow(AppointmentNotFoundException::new); |
| | | } |
| | | |
| | | public void closeAppointment(AppointmentCloseDTO closeDTO) { |
| | | if(closeDTO.getContactStatus() == ContactStatusEnum.DONE) { |
| | | DoneProcessDTO dto = new DoneProcessDTO(); |
| | | BeanUtils.copyProperties(closeDTO, dto); |
| | | abstractAppointmentProcess.process(dto); |
| | | }else if(closeDTO.getContactStatus() == ContactStatusEnum.CLOSED){ |
| | | ClosedProcessDTO dto = new ClosedProcessDTO(); |
| | | BeanUtils.copyProperties(closeDTO, dto); |
| | | abstractAppointmentProcess.process(dto); |
| | | } |
| | | } |
| | | |
| | | public Long getConsultantPendingAppointmentSum(String agentNo) { |
| | | return appointmentCustomerViewRepository.findAllByCommunicateStatusAndStatus(ContactStatusEnum.RESERVED, AVAILABLE) |
| | | .stream() |
| | | .filter(appointment -> agentNo.equals(appointment.getAgentNo())) |
| | | .filter(appointment -> isAppointmentDateNotInIntervalFromNow(appointment, Constants.APPOINTMENT_PENDING_PHONE_INTERVAL, Constants.APPOINTMENT_PENDING_EMAIL_INTERVAL)) |
| | | .count(); |
| | | } |
| | | |
| | | public AppointmentCustomerViewDTO getCustomerNewestExpiringAppointment(Long customerId) { |
| | | return appointmentCustomerViewRepository.findAllByCommunicateStatusAndStatus(ContactStatusEnum.RESERVED, AVAILABLE) |
| | | .stream() |
| | | .filter(appointment -> customerId.equals(appointment.getCustomerId())) |
| | | .filter(appointment -> isAppointmentDateNotInIntervalFromNow(appointment, Constants.APPOINTMENT_EXPIRING_PHONE_INTERVAL, Constants.APPOINTMENT_EXPIRING_EMAIL_INTERVAL)) |
| | | .max(Comparator.comparing(AppointmentCustomerView::getAppointmentDate)) |
| | | .map(appointmentCustomerView -> appointmentCustomerViewMapper.toAppointmentCustomerViewDTO(appointmentCustomerView)) |
| | | .orElse(null); |
| | | } |
| | | |
| | | public boolean isAppointmentDateNotInIntervalFromNow(AppointmentCustomerView appointment, int phoneInterval, int emailInterval) { |
| | | final boolean isHavePhone = StringUtils.hasText(appointment.getPhone()); |
| | | final boolean isHaveEmail = StringUtils.hasText(appointment.getEmail()); |
| | | |
| | | LocalDate appointmentDate = appointment.getAppointmentDate().atZone(ZoneId.systemDefault()).toLocalDate(); |
| | | LocalDate nowDate = Instant.now().atZone(ZoneId.systemDefault()).toLocalDate(); |
| | | long intervalDays = nowDate.toEpochDay() - appointmentDate.toEpochDay(); |
| | | |
| | | final boolean isAppointmentExpiringByPhone = isHavePhone && intervalDays >= phoneInterval; |
| | | final boolean isAppointmentExpiringByEmail = isHaveEmail && intervalDays >= emailInterval; |
| | | |
| | | return isAppointmentExpiringByPhone || isAppointmentExpiringByEmail; |
| | | } |
| | | } |
| | |
| | | package com.pollex.pam.service; |
| | | |
| | | import com.pollex.pam.config.ApplicationProperties; |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.domain.AppointmentCustomerView; |
| | | import com.pollex.pam.domain.Consultant; |
| | | import com.pollex.pam.domain.CustomerFavoriteConsultant; |
| | | import com.pollex.pam.domain.Satisfaction; |
| | | import com.pollex.pam.enums.ContactStatusEnum; |
| | | import com.pollex.pam.enums.LoginResult; |
| | | import com.pollex.pam.repository.ConsultantRepository; |
| | | import com.pollex.pam.repository.CustomerFavoriteConsultantRepository; |
| | | import com.pollex.pam.repository.SatisfactionRepository; |
| | | import com.pollex.pam.security.SecurityUtils; |
| | | import com.pollex.pam.service.dto.*; |
| | | import com.pollex.pam.service.mapper.AppointmentCustomerViewMapper; |
| | | import com.pollex.pam.service.mapper.ConsultantDTOMapper; |
| | | import com.pollex.pam.service.mapper.ConsultantMapper; |
| | | import com.pollex.pam.service.util.FileUtil; |
| | | import com.pollex.pam.web.rest.errors.ConsultantNotFoundException; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | import org.thymeleaf.context.Context; |
| | | import org.thymeleaf.spring5.SpringTemplateEngine; |
| | | |
| | | import java.io.File; |
| | | import java.io.FileInputStream; |
| | | import java.io.FileNotFoundException; |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.math.BigDecimal; |
| | | import java.time.Instant; |
| | | import java.util.Comparator; |
| | | import java.util.List; |
| | |
| | | import static com.pollex.pam.enums.ContactStatusEnum.*; |
| | | |
| | | @Service |
| | | @Transactional |
| | | public class ConsultantService { |
| | | |
| | | private static final Logger log = LoggerFactory.getLogger(ConsultantService.class); |
| | |
| | | @Autowired |
| | | SatisfactionService satisfactionService; |
| | | |
| | | @Autowired |
| | | ConsultantDTOMapper consultantDTOMapper; |
| | | |
| | | @Autowired |
| | | ApplicationProperties applicationProperty; |
| | | |
| | | @Autowired |
| | | SendMsgService sendMsgService; |
| | | |
| | | @Autowired |
| | | SpringTemplateEngine springTemplateEngine; |
| | | |
| | | @Autowired |
| | | ApplicationProperties applicationProperties; |
| | | |
| | | @Autowired |
| | | ConsultantService consultantService; |
| | | |
| | | @Autowired |
| | | SatisfactionRepository satisfactionRepository; |
| | | |
| | | @Autowired |
| | | PersonalNotificationService personalNotificationService; |
| | | |
| | | public List<CustomerFavoriteConsultantDTO> getMyConsultantList() { |
| | | Long customerId = SecurityUtils.getCustomerDBId(); |
| | | |
| | |
| | | .map(relation -> { |
| | | Consultant consultant = relation.getConsultant(); |
| | | CustomerFavoriteConsultantDTO dto = consultantMapper.toCustomerFavoriteConsultantDto(consultant); |
| | | |
| | | dto.setContactStatus(ContactStatusEnum.PICKED); |
| | | dto.setCreateTime(relation.getCreatedDate()); |
| | | dto.setUpdateTime(relation.getCreatedDate()); |
| | | dto.setCustomerViewTime(relation.getViewTime()); |
| | | |
| | | setAvailableAppointmentInfo( |
| | | setInfoByAvailableAppointment( |
| | | dto, |
| | | appointmentService.findAvailableByAgentNoAndCustomerId(consultant.getAgentNo(), customerId) |
| | | ); |
| | | |
| | | appointmentService.findLatestAppointmentByAgentNoAndCustomerId(consultant.getAgentNo(), customerId) |
| | | .ifPresent(latestAppointment -> { |
| | | dto.setUpdateTime(latestAppointment.getLastModifiedDate()); |
| | | }); |
| | | |
| | | if(dto.getUpdateTime().isBefore(relation.getCreatedDate())) { |
| | | dto.setUpdateTime(relation.getCreatedDate()); |
| | | } |
| | | setFavoriteConsultantUpdatedTime(relation, dto); |
| | | |
| | | return dto; |
| | | |
| | | }).collect(Collectors.toList()); |
| | | } |
| | | |
| | | private void setAvailableAppointmentInfo(CustomerFavoriteConsultantDTO customerFavoriteConsultantDTO, List<AppointmentCustomerView> appointmentList) { |
| | | public void setFavoriteConsultantUpdatedTime(CustomerFavoriteConsultant relation, |
| | | CustomerFavoriteConsultantDTO dto) { |
| | | Consultant consultant = relation.getConsultant(); |
| | | appointmentService.findLatestAppointmentByAgentNoAndCustomerId(consultant.getAgentNo(), relation.getCustomerId()) |
| | | .ifPresent(latestAppointment -> { |
| | | dto.setUpdateTime(latestAppointment.getLastModifiedDate()); |
| | | }); |
| | | |
| | | if(dto.getUpdateTime().isBefore(relation.getCreatedDate())) { |
| | | dto.setUpdateTime(relation.getCreatedDate()); |
| | | } |
| | | } |
| | | |
| | | private void setInfoByAvailableAppointment(CustomerFavoriteConsultantDTO customerFavoriteConsultantDTO, List<AppointmentCustomerView> appointmentList) { |
| | | List<AppointmentCustomerView> appointments = appointmentList.stream() |
| | | .sorted(Comparator.comparing(AppointmentCustomerView::getAppointmentDate).reversed()) |
| | | .collect(Collectors.toList()); |
| | | |
| | | List<AppointmentCustomerViewDTO> appointmentCustomerViewDTOS = appointmentCustomerViewMapper.toAppointmentCustomerViewDTO(appointments); |
| | | appointmentCustomerViewDTOS.forEach(appointmentCustomerViewDTO -> { |
| | | appointmentService.setSatisfactionScore(appointmentCustomerViewDTO, appointmentCustomerViewDTO.getId()); |
| | | }); |
| | | customerFavoriteConsultantDTO.setAppointments(appointmentCustomerViewDTOS); |
| | | |
| | | if (!appointments.isEmpty()) { |
| | | AppointmentCustomerView latestAvailableAppointment = appointments.get(0); |
| | | |
| | | if(latestAvailableAppointment.getCommunicateStatus() == RESERVED) |
| | | customerFavoriteConsultantDTO.setContactStatus(RESERVED); |
| | | ContactStatusEnum latestStatus = latestAvailableAppointment.getCommunicateStatus(); |
| | | if(latestStatus != ContactStatusEnum.DONE && latestStatus != ContactStatusEnum.CLOSED) |
| | | customerFavoriteConsultantDTO.setContactStatus(latestStatus); |
| | | else |
| | | customerFavoriteConsultantDTO.setContactStatus(PICKED); |
| | | }else { |
| | | customerFavoriteConsultantDTO.setContactStatus(PICKED); |
| | | } |
| | | } |
| | | |
| | |
| | | public Consultant findByAgentNo(String agentNo) { |
| | | return consultantRepository.findOneByAgentNo(agentNo).get(); |
| | | } |
| | | |
| | | public Consultant editConsultant(ConsultantEditDTO editDTO) { |
| | | Consultant consultant = consultantRepository.findOneByAgentNo(editDTO.getAgentNo()) |
| | | .orElseThrow(ConsultantNotFoundException::new); |
| | | consultantDTOMapper.copyToConsultant(editDTO, consultant); |
| | | FileUtil.base64ToFile(editDTO.getPhotoBase64(), editDTO.getPhotoFileName(), applicationProperty.getFileFolderPath()); |
| | | consultantRepository.save(consultant); |
| | | personalNotificationService.createEditConsultantToConsultant(consultant); |
| | | return consultant; |
| | | } |
| | | |
| | | public InputStream getAvatarImage(String agentNo) { |
| | | Consultant consultant = consultantRepository.findOneByAgentNo(agentNo) |
| | | .orElseThrow(ConsultantNotFoundException::new); |
| | | File file = new File(consultant.getPhotoPath()); |
| | | try { |
| | | InputStream in = new FileInputStream(file); |
| | | return in; |
| | | } catch (FileNotFoundException e) { |
| | | log.error("agent photo not found , agentNo:"+agentNo,e); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | public void sendSatisfactionToClient(Appointment appointment) { |
| | | String subject = "滿æåº¦å¡«å¯«éç¥"; |
| | | |
| | | if(StringUtils.hasText(appointment.getEmail())) { |
| | | String content = genSendSatisfactionEmailContent(appointment); |
| | | sendMsgService.sendMsgByEmail(appointment.getEmail(), subject, content, true); |
| | | |
| | | }if(StringUtils.hasText(appointment.getPhone())) { |
| | | String content = genSendSatisfactionSMSContent(appointment); |
| | | sendMsgService.sendMsgBySMS(appointment.getPhone(), content); |
| | | } |
| | | |
| | | personalNotificationService.createSendSatisfactionToClientToCustomer(appointment); |
| | | } |
| | | |
| | | private String genSendSatisfactionSMSContent(Appointment appointment) { |
| | | String agentNo = appointment.getAgentNo(); |
| | | Consultant consultant = consultantService.findByAgentNo(agentNo); |
| | | String contsultantName = consultant.getName(); |
| | | String content = contsultantName+"é¡§åè«æ¨å¡«å¯«ä¿èª åªåå¹³å°ç滿æåº¦è©æ¯"+getSendSatisfactionToClientUrl(appointment.getId()); |
| | | return content; |
| | | } |
| | | |
| | | private String genSendSatisfactionEmailContent(Appointment appointment) { |
| | | String agentNo = appointment.getAgentNo(); |
| | | Consultant consultant = consultantService.findByAgentNo(agentNo); |
| | | Context context = new Context(); |
| | | context.setVariable("consultantName", consultant.getName()); |
| | | context.setVariable("appointmentUrl", getSendSatisfactionToClientUrl(appointment.getId())); |
| | | String content = springTemplateEngine.process("mail/writeSatisfactionNotice", context); |
| | | return content; |
| | | } |
| | | |
| | | public String getSendSatisfactionToClientUrl(Long appointmentId) { |
| | | return applicationProperties.getFrontEndDomain() + "/?appointmentId=" + appointmentId; |
| | | } |
| | | |
| | | public void setConsultantAvgScore(Satisfaction satisfaction) { |
| | | float avgScore = getAgentAvgScore(satisfaction.getAgentNo()); |
| | | Consultant consultant = consultantRepository.findOneByAgentNo(satisfaction.getAgentNo()) |
| | | .get(); |
| | | consultant.setAvgScore(avgScore); |
| | | consultantRepository.save(consultant); |
| | | } |
| | | |
| | | public float getAgentAvgScore(String agentNo) { |
| | | Float avgScore = satisfactionRepository.getAgentScoreAvg(agentNo); |
| | | if(avgScore==null)return 0; |
| | | BigDecimal bigDecimal = new BigDecimal(avgScore); |
| | | return avgScore = bigDecimal.setScale(1,BigDecimal.ROUND_HALF_UP).floatValue(); |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service; |
| | | |
| | | import java.util.List; |
| | | |
| | | import org.hibernate.boot.model.naming.IllegalIdentifierException; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.domain.InterviewRecord; |
| | | import com.pollex.pam.enums.InterviewRecordStatusEnum; |
| | | import com.pollex.pam.repository.InterviewRecordRepository; |
| | | import com.pollex.pam.security.SecurityUtils; |
| | | import com.pollex.pam.service.dto.InterviewRecordDTO; |
| | | import com.pollex.pam.service.mapper.InterviewRecordMapper; |
| | | import com.pollex.pam.web.rest.errors.InterviewRecordNotFoundException; |
| | | |
| | | @Service |
| | | @Transactional |
| | | public class InterviewRecordService { |
| | | |
| | | @Autowired |
| | | InterviewRecordRepository interviewRecordRepository; |
| | | |
| | | @Autowired |
| | | InterviewRecordMapper interviewRecordMapper; |
| | | |
| | | @Autowired |
| | | AppointmentService appointmentService; |
| | | |
| | | public InterviewRecord create(InterviewRecordDTO dto) { |
| | | if(dto.getId()!=null) { |
| | | throw new IllegalArgumentException(); |
| | | } |
| | | |
| | | InterviewRecord record = interviewRecordMapper.toInterviewRecord(dto); |
| | | checkAuth(record); |
| | | record.setStatus(InterviewRecordStatusEnum.AVAILABLE); |
| | | interviewRecordRepository.save(record); |
| | | return record; |
| | | } |
| | | |
| | | public InterviewRecord update(InterviewRecordDTO dto) { |
| | | if(dto.getId()==null) { |
| | | throw new IllegalArgumentException(); |
| | | } |
| | | |
| | | InterviewRecord record = findById(dto.getId()); |
| | | checkAuth(record); |
| | | interviewRecordMapper.copyToInterviewRecord(dto, record); |
| | | interviewRecordRepository.save(record); |
| | | return record; |
| | | } |
| | | |
| | | public void checkAuth(InterviewRecord record) { |
| | | Appointment appointment = appointmentService.findById(record.getAppointmentId()); |
| | | if(!appointment.getAgentNo().equals(SecurityUtils.getAgentNo())) { |
| | | throw new IllegalAccessError("The account can not edit the appointment"); |
| | | } |
| | | } |
| | | |
| | | public void delete(Long interviewRecordId) { |
| | | InterviewRecord record = findById(interviewRecordId); |
| | | record.setStatus(InterviewRecordStatusEnum.DELETED); |
| | | interviewRecordRepository.save(record); |
| | | } |
| | | |
| | | public InterviewRecord findById(Long id) { |
| | | return interviewRecordRepository.findById(id) |
| | | .orElseThrow(InterviewRecordNotFoundException::new); |
| | | } |
| | | |
| | | public List<InterviewRecordDTO> findByAppointmentId(Long appointmentId) { |
| | | List<InterviewRecord> records = interviewRecordRepository.findByAppointmentId(appointmentId); |
| | | return interviewRecordMapper.toInterviewRecordDTO(records); |
| | | } |
| | | |
| | | public List<InterviewRecordDTO> findByAppointmentIdAndStatus(Long appointmentId, InterviewRecordStatusEnum status) { |
| | | List<InterviewRecord> records = interviewRecordRepository.findByAppointmentIdAndStatus(appointmentId, status); |
| | | return interviewRecordMapper.toInterviewRecordDTO(records); |
| | | } |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service; |
| | | |
| | | import java.util.List; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.domain.AppointmentNoticeLog; |
| | | import com.pollex.pam.enums.ContactStatusEnum; |
| | | import com.pollex.pam.repository.AppointmentRepository; |
| | | import com.pollex.pam.service.dto.AppointmentNoticeSendDTO; |
| | | |
| | | @Service |
| | | @Transactional |
| | | public class NoticeService { |
| | | |
| | | @Autowired |
| | | AppointmentService appointmentService; |
| | | |
| | | @Autowired |
| | | SendMsgService sendMsgService; |
| | | |
| | | @Autowired |
| | | AppointmentNoticeLogService appointmentNoticeLogService; |
| | | |
| | | @Autowired |
| | | AppointmentRepository appointmentRepository; |
| | | |
| | | @Autowired |
| | | PersonalNotificationService personalNotificationService; |
| | | |
| | | |
| | | public void sendNotice(AppointmentNoticeSendDTO dto) { |
| | | String subject = "ä¿èª åªåå¹³å°ç³»çµ±éç¥ï¼é ç´éç¥"; |
| | | |
| | | if(StringUtils.hasText(dto.getEmail())) { |
| | | sendMsgService.sendMsgByEmail(dto.getEmail(), subject, dto.getMessage(), true); |
| | | }if(StringUtils.hasText(dto.getPhone())) { |
| | | sendMsgService.sendMsgBySMS(dto.getPhone(), dto.getMessage()); |
| | | } |
| | | |
| | | List<AppointmentNoticeLog> noticeLogs = |
| | | appointmentNoticeLogService.findByAppointmentId(dto.getAppointmentId()); |
| | | if(noticeLogs.size()==0) { |
| | | Appointment appointment = appointmentService.findById(dto.getAppointmentId()); |
| | | appointment.setCommunicateStatus(ContactStatusEnum.CONTACTED); |
| | | appointmentRepository.save(appointment); |
| | | } |
| | | |
| | | appointmentNoticeLogService.create(dto); |
| | | personalNotificationService.createSendNoticeToCustomer(dto.getAppointmentId()); |
| | | } |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service; |
| | | |
| | | import java.time.Instant; |
| | | import java.util.List; |
| | | |
| | | import javax.management.Notification; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.domain.Consultant; |
| | | import com.pollex.pam.domain.Customer; |
| | | import com.pollex.pam.domain.PersonalNotification; |
| | | import com.pollex.pam.domain.Satisfaction; |
| | | import com.pollex.pam.enums.NotificationTypeEnum; |
| | | import com.pollex.pam.enums.PersonalNotificationRoleEnum; |
| | | import com.pollex.pam.repository.CustomerRepository; |
| | | import com.pollex.pam.repository.PersonalNotificationRepository; |
| | | import com.pollex.pam.security.SecurityUtils; |
| | | import com.pollex.pam.service.dto.AppointmentUpdateDTO; |
| | | |
| | | @Service |
| | | @Transactional |
| | | public class PersonalNotificationService { |
| | | |
| | | @Autowired |
| | | PersonalNotificationRepository personalNotificationRepository; |
| | | |
| | | @Autowired |
| | | ConsultantService consultantService; |
| | | |
| | | @Autowired |
| | | AppointmentService appointmentService; |
| | | |
| | | @Autowired |
| | | CustomerService customerService; |
| | | |
| | | @Autowired |
| | | CustomerRepository customerRepository; |
| | | |
| | | @Autowired |
| | | SatisfactionService satisfactionService; |
| | | |
| | | public List<PersonalNotification> getMyPersonalNotification(Long ownerId, PersonalNotificationRoleEnum role) { |
| | | return personalNotificationRepository.findAllByOwnerRoleAndOwnerId(role, ownerId); |
| | | } |
| | | |
| | | public void createSendSatisfactionToClientToCustomer(Appointment appointment) { |
| | | PersonalNotification entity = new PersonalNotification(); |
| | | Consultant consultant = consultantService.findByAgentNo(appointment.getAgentNo()); |
| | | String content = consultant.getName()+"é¡§åè«æ¨å¡«å¯«æ»¿æåº¦è©æ¯"; |
| | | entity.setContent(content); |
| | | entity.setNotificationType(NotificationTypeEnum.ACTIVITY); |
| | | entity.setOwnerId(appointment.getCustomerId()); |
| | | entity.setOwnerRole(PersonalNotificationRoleEnum.CUSTOMER); |
| | | entity.setTitle("填寫滿æåº¦"); |
| | | personalNotificationRepository.save(entity); |
| | | } |
| | | |
| | | public void createSendNoticeToCustomer(Long appointmentId) { |
| | | Appointment appointment = appointmentService.findById(appointmentId); |
| | | PersonalNotification entity = new PersonalNotification(); |
| | | Consultant consultant = consultantService.findByAgentNo(appointment.getAgentNo()); |
| | | String content = "æ¨æ "+consultant.getName()+"é¡§åçç´è¨ªéç¥"; |
| | | entity.setContent(content); |
| | | entity.setNotificationType(NotificationTypeEnum.ACTIVITY); |
| | | entity.setOwnerId(appointment.getCustomerId()); |
| | | entity.setOwnerRole(PersonalNotificationRoleEnum.CUSTOMER); |
| | | entity.setTitle("é¡§åç´è¨ªéç¥"); |
| | | personalNotificationRepository.save(entity); |
| | | } |
| | | |
| | | public void createNotFillSatisfactionSumToCustomer(Long customerId, int notFillSatisfactionSum) { |
| | | PersonalNotification entity = new PersonalNotification(); |
| | | |
| | | String content = "æ¨æ "+notFillSatisfactionSum+" çé¡§åçæ»¿æåº¦éè¦å¡«å¯«"; |
| | | entity.setContent(content); |
| | | entity.setNotificationType(NotificationTypeEnum.ACTIVITY); |
| | | entity.setOwnerId(customerId); |
| | | entity.setOwnerRole(PersonalNotificationRoleEnum.CUSTOMER); |
| | | entity.setTitle("å®¢æ¶æ»¿æåº¦"); |
| | | personalNotificationRepository.save(entity); |
| | | } |
| | | |
| | | public void createEditConsultantToConsultant(Consultant consultant) { |
| | | PersonalNotification entity = new PersonalNotification(); |
| | | String content = "æ¨çå人帳èè¨å®å·²é²è¡æ´æ°"; |
| | | entity.setContent(content); |
| | | entity.setNotificationType(NotificationTypeEnum.ACTIVITY); |
| | | entity.setOwnerId(consultant.getId()); |
| | | entity.setOwnerRole(PersonalNotificationRoleEnum.CONSULTANT); |
| | | entity.setTitle("è®æ´å¸³èè³æ"); |
| | | personalNotificationRepository.save(entity); |
| | | } |
| | | |
| | | public void createMarkAppointmentDeletedToConsultant(Appointment appointment) { |
| | | PersonalNotification entity = new PersonalNotification(); |
| | | Customer customer = customerRepository.findById(appointment.getCustomerId()).get(); |
| | | Consultant consultant = consultantService.findByAgentNo(appointment.getAgentNo()); |
| | | String content = customer.getName()+"客æ¶å·²åæ¶æ¨çé ç´"; |
| | | entity.setContent(content); |
| | | entity.setNotificationType(NotificationTypeEnum.ACTIVITY); |
| | | entity.setOwnerId(consultant.getId()); |
| | | entity.setOwnerRole(PersonalNotificationRoleEnum.CONSULTANT); |
| | | entity.setTitle("åæ¶é ç´æé"); |
| | | personalNotificationRepository.save(entity); |
| | | } |
| | | |
| | | public void createUpdateAppointmentToConsultant(Appointment appointment) { |
| | | PersonalNotification entity = new PersonalNotification(); |
| | | Customer customer = customerRepository.findById(appointment.getCustomerId()).get(); |
| | | Consultant consultant = consultantService.findByAgentNo(appointment.getAgentNo()); |
| | | String content = customer.getName()+"客æ¶å·²æ´æ°æ¨çé ç´è³è¨"; |
| | | entity.setContent(content); |
| | | entity.setNotificationType(NotificationTypeEnum.ACTIVITY); |
| | | entity.setOwnerId(consultant.getId()); |
| | | entity.setOwnerRole(PersonalNotificationRoleEnum.CONSULTANT); |
| | | entity.setTitle("æ´æ°é ç´æé"); |
| | | personalNotificationRepository.save(entity); |
| | | } |
| | | |
| | | public void createScorefactionToConsultant(Satisfaction satisfaction) { |
| | | PersonalNotification entity = new PersonalNotification(); |
| | | Appointment appointment = appointmentService.findById(satisfaction.getAppointmentId()); |
| | | Customer customer = customerRepository.findById(appointment.getCustomerId()).get(); |
| | | Consultant consultant = consultantService.findByAgentNo(appointment.getAgentNo()); |
| | | String content = customer.getName()+"客æ¶å·²å°æ¨é²è¡æ»¿æåº¦è©æ¯"; |
| | | entity.setContent(content); |
| | | entity.setNotificationType(NotificationTypeEnum.ACTIVITY); |
| | | entity.setOwnerId(consultant.getId()); |
| | | entity.setOwnerRole(PersonalNotificationRoleEnum.CONSULTANT); |
| | | entity.setTitle("å®¢æ¶æ»¿æåº¦"); |
| | | personalNotificationRepository.save(entity); |
| | | } |
| | | |
| | | public void readAllMyNotification() { |
| | | if(StringUtils.hasText(SecurityUtils.getAgentNo())) { |
| | | Long consultantId = consultantService.findByAgentNo(SecurityUtils.getAgentNo()).getId(); |
| | | readAllNotification(PersonalNotificationRoleEnum.CONSULTANT, consultantId); |
| | | }else if(SecurityUtils.getCustomerDBId()!=null){ |
| | | readAllNotification(PersonalNotificationRoleEnum.CUSTOMER, SecurityUtils.getCustomerDBId()); |
| | | } |
| | | } |
| | | |
| | | public void readAllNotification(PersonalNotificationRoleEnum ownerRole |
| | | , Long ownerId) { |
| | | List<PersonalNotification> allNotification = personalNotificationRepository.findAllByOwnerRoleAndOwnerId(ownerRole, ownerId); |
| | | Instant today = Instant.now(); |
| | | allNotification.stream() |
| | | .filter(notification -> notification.getReadDate()==null) |
| | | .forEach(notification ->{ |
| | | notification.setReadDate(today); |
| | | personalNotificationRepository.saveAll(allNotification); |
| | | }); |
| | | } |
| | | } |
| | |
| | | package com.pollex.pam.service; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | |
| | |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.domain.Consultant; |
| | | import com.pollex.pam.domain.Satisfaction; |
| | | import com.pollex.pam.enums.SatisfactionStatusEnum; |
| | | import com.pollex.pam.repository.ConsultantRepository; |
| | | import com.pollex.pam.repository.CustomerRepository; |
| | | import com.pollex.pam.repository.SatisfactionRepository; |
| | | import com.pollex.pam.service.dto.SatisfactionCustomerCreateDTO; |
| | | import com.pollex.pam.service.dto.SatisfactionCustomerScoreDTO; |
| | | import com.pollex.pam.service.dto.SatisfactionDTO; |
| | | import com.pollex.pam.service.mapper.AppointmentMapper; |
| | | import com.pollex.pam.service.mapper.SatisfactionDTOMapper; |
| | | import com.pollex.pam.service.mapper.SatisfactionMapper; |
| | | import com.pollex.pam.web.rest.errors.SatisfactionAlreadyExistException; |
| | | import com.pollex.pam.web.rest.errors.SatisfactionNotFoundException; |
| | | |
| | | @Service |
| | | @Transactional |
| | |
| | | |
| | | @Autowired |
| | | CustomerRepository customerRepository; |
| | | |
| | | |
| | | @Autowired |
| | | ConsultantRepository consultantRepository; |
| | | |
| | | public Satisfaction createSatisfaction(Satisfaction satisfaction) { |
| | | @Autowired |
| | | ConsultantService consultantService; |
| | | |
| | | @Autowired |
| | | PersonalNotificationService personalNotificationService; |
| | | |
| | | public Satisfaction save(Satisfaction satisfaction) { |
| | | satisfaction = satisfactionRepository.save(satisfaction); |
| | | setConsultantAvgScore(satisfaction); |
| | | consultantService.setConsultantAvgScore(satisfaction); |
| | | return satisfaction; |
| | | } |
| | | |
| | | |
| | | |
| | | private void setConsultantAvgScore(Satisfaction satisfaction) { |
| | | float avgScore = getAgentAvgScore(satisfaction); |
| | | Consultant consultant = consultantRepository.findOneByAgentNo(satisfaction.getAgentNo()) |
| | | .get(); |
| | | consultant.setAvgScore(avgScore); |
| | | consultantRepository.save(consultant); |
| | | public Satisfaction scorefaction(SatisfactionCustomerScoreDTO scoreDTO) { |
| | | Optional<Satisfaction> satisfactionOP = getByAppointmentId(scoreDTO.getAppointmentId()); |
| | | Satisfaction satisfaction = satisfactionOP.orElseThrow(SatisfactionNotFoundException::new); |
| | | satisfaction.setScore(scoreDTO.getScore()); |
| | | satisfaction.setStatus(SatisfactionStatusEnum.FILLED); |
| | | save(satisfaction); |
| | | personalNotificationService.createScorefactionToConsultant(satisfaction); |
| | | return satisfaction; |
| | | } |
| | | |
| | | |
| | | |
| | | private float getAgentAvgScore(Satisfaction satisfaction) { |
| | | Float avgScore = satisfactionRepository.getAgentScoreAvg(satisfaction.getAgentNo()); |
| | | BigDecimal bigDecimal = new BigDecimal(avgScore); |
| | | return avgScore = bigDecimal.setScale(1,BigDecimal.ROUND_HALF_UP).floatValue(); |
| | | } |
| | | |
| | | |
| | | |
| | | public Satisfaction createSatisfaction(Appointment appointment) { |
| | | boolean isexist = getByAppointmentId(appointment.getId()).isPresent(); |
| | | if(isexist) { |
| | | throw new SatisfactionAlreadyExistException(); |
| | | } |
| | | Satisfaction satisfaction = appointmentMapper.toSatisfaction(appointment); |
| | | return createSatisfaction(satisfaction); |
| | | return save(satisfaction); |
| | | } |
| | | |
| | | public Satisfaction createSatisfaction(SatisfactionCustomerCreateDTO createDTO) { |
| | | // todo : å°æªæ¨è¨å·²è¯çµ¡çé ç´å®ä¸è©²å¯ä»¥æ°å¢æ»¿æåº¦è©å |
| | | // todo : éèªå·±çé ç´å®ä¸è©²å¯ä»¥é²è¡è©å |
| | | Satisfaction satisfaction = satisfactionDTOMapper.toSatisfaction(createDTO); |
| | | return createSatisfaction(satisfaction); |
| | | } |
| | | // |
| | | // public Satisfaction createSatisfaction(SatisfactionCustomerScoreDTO createDTO) { |
| | | // Satisfaction satisfaction = satisfactionDTOMapper.toSatisfaction(createDTO); |
| | | // return save(satisfaction); |
| | | // } |
| | | |
| | | public List<SatisfactionDTO> getByAgentNo(String agentNo) { |
| | | List<Satisfaction> satisfactionList = satisfactionRepository.findByAgentNo(agentNo); |
| | |
| | | public Optional<Satisfaction> getByAppointmentId(Long appointmentId) { |
| | | return satisfactionRepository.findOneByAppointmentId(appointmentId); |
| | | } |
| | | |
| | | public List<Satisfaction> getByStatus(SatisfactionStatusEnum status) { |
| | | return satisfactionRepository.findAllByStatus(status); |
| | | } |
| | | |
| | | public List<Satisfaction> scoreAllfaction(List<SatisfactionCustomerScoreDTO> scoreDTO) { |
| | | List<Satisfaction> satisfactionList = new ArrayList<>(); |
| | | scoreDTO.stream().forEach(dto ->{ |
| | | satisfactionList.add(scorefaction(dto)); |
| | | }); |
| | | return satisfactionList; |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service; |
| | | |
| | | import com.pollex.pam.config.ApplicationProperties; |
| | | import com.pollex.pam.config.Constants; |
| | | import com.pollex.pam.domain.*; |
| | | import com.pollex.pam.enums.AppointmentStatusEnum; |
| | | import com.pollex.pam.enums.ContactStatusEnum; |
| | | import com.pollex.pam.enums.SatisfactionStatusEnum; |
| | | import com.pollex.pam.repository.AppointmentCustomerViewRepository; |
| | | import com.pollex.pam.repository.AppointmentExpiringNotifyRecordRepository; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | import org.thymeleaf.context.Context; |
| | | import org.thymeleaf.spring5.SpringTemplateEngine; |
| | | |
| | | import java.time.Instant; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Optional; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | @Transactional |
| | | public class ScheduleTaskService { |
| | | |
| | | private static final String NOT_CONTACTED_NOTIFY_SUBJECT = "é ç´å®æªé²è¡è¯ç¹«éç¥"; |
| | | private static final Logger log = LoggerFactory.getLogger(ScheduleTaskService.class); |
| | | |
| | | @Autowired |
| | | ConsultantService consultantService; |
| | | |
| | | @Autowired |
| | | AppointmentService appointmentService; |
| | | |
| | | @Autowired |
| | | AppointmentCustomerViewRepository appointmentCustomerViewRepository; |
| | | |
| | | @Autowired |
| | | SendMsgService sendMsgService; |
| | | |
| | | @Autowired |
| | | SpringTemplateEngine springTemplateEngine; |
| | | |
| | | @Autowired |
| | | ApplicationProperties applicationProperties; |
| | | |
| | | @Autowired |
| | | AppointmentExpiringNotifyRecordRepository appointmentExpiringNotifyRecordRepository; |
| | | |
| | | @Autowired |
| | | SatisfactionService satisfactionService; |
| | | |
| | | @Autowired |
| | | PersonalNotificationService personalNotificationService; |
| | | |
| | | @Scheduled(cron = "0 30 8 * * *") |
| | | public void sendAppointmentPendingNotifyToConsultant() { |
| | | log.info("Starting send appointment pending notify to consultant"); |
| | | |
| | | Map<String, List<AppointmentCustomerView>> consultantWithPendingAppointments = |
| | | appointmentCustomerViewRepository.findAllByCommunicateStatusAndStatus(ContactStatusEnum.RESERVED, AppointmentStatusEnum.AVAILABLE) |
| | | .stream() |
| | | .filter(appointment -> |
| | | appointmentService.isAppointmentDateNotInIntervalFromNow(appointment, Constants.APPOINTMENT_PENDING_PHONE_INTERVAL, Constants.APPOINTMENT_PENDING_EMAIL_INTERVAL) |
| | | ) |
| | | .collect(Collectors.groupingBy(AppointmentCustomerView::getAgentNo)); |
| | | |
| | | consultantWithPendingAppointments.forEach((agentNo, pendingAppointments) -> { |
| | | int pendingAppointmentsSum = pendingAppointments.size(); |
| | | Consultant consultant = consultantService.findByAgentNo(agentNo); |
| | | Optional<String> optionalPhone = Optional.ofNullable(consultant.getPhoneNumber()).filter(StringUtils::hasText); |
| | | Optional<String> optionalEmail = Optional.ofNullable(consultant.getEmail()).filter(StringUtils::hasText); |
| | | |
| | | String emailContent = getAppointmentPendingNotifyEmailContent(pendingAppointmentsSum); |
| | | |
| | | optionalPhone.ifPresent(phone -> { |
| | | sendMsgService.sendMsgBySMS(phone, String.format("æ¨æ%såé ç´å®æªé²è¡è¯ç¹«ï¼è«ç¡éèç", pendingAppointmentsSum)); |
| | | }); |
| | | optionalEmail.ifPresent(email -> { |
| | | sendMsgService.sendMsgByEmail(email, NOT_CONTACTED_NOTIFY_SUBJECT, emailContent, true); |
| | | }); |
| | | }); |
| | | |
| | | log.info("Sending appointment pending notify to consultant finish"); |
| | | } |
| | | |
| | | @Scheduled(cron = "0 30 8 * * *") |
| | | public void sendAppointmentExpiringNotifyToCustomer() { |
| | | log.info("Starting send appointment expiring notify to customer"); |
| | | |
| | | List<AppointmentCustomerView> allByCommunicateStatus = |
| | | appointmentCustomerViewRepository.findAllByCommunicateStatusAndStatus(ContactStatusEnum.RESERVED, AppointmentStatusEnum.AVAILABLE) |
| | | .stream() |
| | | .filter(appointment -> |
| | | appointmentService.isAppointmentDateNotInIntervalFromNow(appointment, Constants.APPOINTMENT_EXPIRING_PHONE_INTERVAL, Constants.APPOINTMENT_EXPIRING_EMAIL_INTERVAL) |
| | | ) |
| | | .filter(this::isAppointmentExpiringNotifyNotOnLimit) |
| | | .collect(Collectors.toList()); |
| | | |
| | | allByCommunicateStatus.forEach(appointment -> { |
| | | Consultant consultant = consultantService.findByAgentNo(appointment.getAgentNo()); |
| | | Optional<String> optionalPhone = Optional.ofNullable(appointment.getPhone()).filter(StringUtils::hasText); |
| | | Optional<String> optionalEmail = Optional.ofNullable(appointment.getEmail()).filter(StringUtils::hasText); |
| | | |
| | | optionalPhone.ifPresent(phone -> |
| | | sendMsgService.sendMsgBySMS(phone, String.format("徿±æï¼æ¨é ç´%s顧忣å¿ç¢ä¸ï¼è«æ¨åæ¶é ç´ä¸¦æ¹é¸å
¶ä»é¡§åï¼è«é»æç¶²åï¼%s" |
| | | , consultant.getName(), getAppointmentExpiringNotifyUrl(appointment.getId()))) |
| | | ); |
| | | optionalEmail.ifPresent(email -> |
| | | sendMsgService.sendMsgByEmail(email, NOT_CONTACTED_NOTIFY_SUBJECT, getAppointmentExpiringNotifyEmail(consultant.getName(), getAppointmentExpiringNotifyUrl(appointment.getId())), true) |
| | | ); |
| | | |
| | | AppointmentExpiringNotifyRecord record = new AppointmentExpiringNotifyRecord(); |
| | | record.setAppointmentId(appointment.getId()); |
| | | record.setSendTime(Instant.now()); |
| | | |
| | | appointmentExpiringNotifyRecordRepository.save(record); |
| | | }); |
| | | |
| | | log.info("Sending appointment expiring notify to customer finish"); |
| | | } |
| | | |
| | | // todo é確èªè©²æé, otis todo=134497 |
| | | @Scheduled(cron = "0 30 8 * * *") |
| | | public void sendNotFillSatisfactionToPersonalNotification() { |
| | | Map<Long, List<Satisfaction>> customerNotFillSatisfactions = satisfactionService.getByStatus(SatisfactionStatusEnum.UNFILLED) |
| | | .stream() |
| | | .collect(Collectors.groupingBy(Satisfaction::getCustomerId)); |
| | | |
| | | customerNotFillSatisfactions.forEach((customerId, notFillSatisfactions) -> |
| | | personalNotificationService.createNotFillSatisfactionSumToCustomer(customerId, notFillSatisfactions.size()) |
| | | ); |
| | | } |
| | | |
| | | private boolean isAppointmentExpiringNotifyNotOnLimit(AppointmentCustomerView appointment) { |
| | | int sendNotifyToCustomerRecordSum = |
| | | appointmentExpiringNotifyRecordRepository.findAllByAppointmentId(appointment.getId()).size(); |
| | | |
| | | return sendNotifyToCustomerRecordSum < Constants.SEND_EXPIRING_NOTIFY_LIMIT; |
| | | } |
| | | |
| | | private String getAppointmentExpiringNotifyUrl(Long appointmentId) { |
| | | return applicationProperties.getFrontEndDomain() + "?notContactAppointmentId=" + appointmentId; |
| | | } |
| | | |
| | | private String getAppointmentPendingNotifyEmailContent(int sum) { |
| | | Context context = new Context(); |
| | | context.setVariable("pendingAppointmentSum", sum); |
| | | return springTemplateEngine.process("mail/appointmentPendingNotifyEmail", context); |
| | | } |
| | | |
| | | private String getAppointmentExpiringNotifyEmail(String consultantName, String notifyUrl) { |
| | | Context context = new Context(); |
| | | context.setVariable("consultantName", consultantName); |
| | | context.setVariable("notifyUrl", notifyUrl); |
| | | return springTemplateEngine.process("mail/appointmentExpiringNotifyEmail", context); |
| | | } |
| | | } |
| | |
| | | |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.pollex.pam.config.ApplicationProperties; |
| | | import com.pollex.pam.config.ApplicationProperties.Email; |
| | | import com.pollex.pam.config.ApplicationProperties.SMS; |
| | | import com.pollex.pam.config.Constants; |
| | | import com.pollex.pam.enums.SendEmailMsgMethod; |
| | | import com.pollex.pam.repository.ConsultantRepository; |
| | | import com.pollex.pam.service.dto.*; |
| | | import com.pollex.pam.service.util.HttpRequestUtil; |
| | |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.core.env.Environment; |
| | | import org.springframework.core.env.Profiles; |
| | | import org.springframework.http.ResponseEntity; |
| | | import org.springframework.stereotype.Service; |
| | | import org.thymeleaf.spring5.SpringTemplateEngine; |
| | | import tech.jhipster.config.JHipsterConstants; |
| | | |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.time.LocalDateTime; |
| | |
| | | @Autowired |
| | | SpringTemplateEngine springTemplateEngine; |
| | | |
| | | @Autowired |
| | | Environment environment; |
| | | |
| | | @Autowired |
| | | MailService mailService; |
| | | |
| | | public SendSMSResponse sendMsgBySMS(String toMobile, String content) throws SendSMSFailException { |
| | | |
| | | SMS smsProperties = applicationProperties.getSms(); |
| | | if(!smsProperties.isSendNotifyMsg()) { |
| | | // return getMockSMSResponse(); |
| | | return null; |
| | | } |
| | | |
| | | SendSMSRequest sendSMSRequest = new SendSMSRequest(); |
| | | sendSMSRequest.setpKey(UUID.randomUUID().toString()); |
| | |
| | | } |
| | | } |
| | | |
| | | public String sendMsgByEmail(String from, String to, String subject, String content, boolean htmlFormat) throws SendEmailFailException{ |
| | | return sendMsgByEmail(from, to, subject, content, htmlFormat, Collections.emptyList(), Collections.emptyList()); |
| | | // private SendSMSResponse getMockSMSResponse() { |
| | | // SendSMSResponse mock = new SendSMSResponse(); |
| | | // mock.set |
| | | // return null; |
| | | // } |
| | | |
| | | public String sendMsgByEmail(String to, String subject, String content, boolean htmlFormat) throws SendEmailFailException{ |
| | | return sendMsgByEmail(to, subject, content, htmlFormat, Collections.emptyList(), Collections.emptyList()); |
| | | } |
| | | |
| | | public String sendMsgByEmail( |
| | | String fromAddress, String toAddress, String subject, String content, boolean htmlFormat, List<String> toCCAddress, |
| | | List<String> attachments) throws SendEmailFailException |
| | | { |
| | | public String sendMsgByEmail(String toAddress, String subject, String content, boolean htmlFormat, List<String> toCCAddress, |
| | | List<String> attachments) throws SendEmailFailException { |
| | | String fromAddress = applicationProperties.getEmail().getSenderEmail(); |
| | | |
| | | SendMailRequest sendMailRequest = new SendMailRequest(); |
| | | sendMailRequest.setSendMailAddresses(Collections.singletonList(toAddress)); |
| | | sendMailRequest.setFrom(fromAddress); |
| | |
| | | } |
| | | |
| | | public String sendMsgByEmail(SendMailRequest sendMailRequest) throws SendEmailFailException{ |
| | | final Email emailProperties = applicationProperties.getEmail(); |
| | | |
| | | if(!emailProperties.isSendNotifyMsg()) { |
| | | return null; |
| | | } |
| | | |
| | | if(emailProperties.getMethod() == SendEmailMsgMethod.POLLEX_GMAIL) { |
| | | return sendMsgByPollexGmail(sendMailRequest); |
| | | } |
| | | else if(emailProperties.getMethod() == SendEmailMsgMethod.PAM_EMAIL_SERVICE) { |
| | | return sendMsgByPamEmailService(sendMailRequest); |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | private String sendMsgByPollexGmail(SendMailRequest sendMailRequest) { |
| | | String subject = sendMailRequest.getSubject(); |
| | | String content = sendMailRequest.getContent(); |
| | | boolean isHtml = sendMailRequest.isHtmlFormat(); |
| | | sendMailRequest.getSendMailAddresses().forEach(receiver -> mailService.sendEmail(receiver, subject, content, false, isHtml)); |
| | | |
| | | return null; |
| | | } |
| | | |
| | | private String sendMsgByPamEmailService(SendMailRequest sendMailRequest) { |
| | | final Email emailProperties = applicationProperties.getEmail(); |
| | | try { |
| | | ResponseEntity<String> responseEntity = |
| | | HttpRequestUtil.postWithJson( applicationProperties.getEmail().getUrl(), sendMailRequest, String.class); |
| | | HttpRequestUtil.postWithJson(emailProperties.getUrl(), sendMailRequest, String.class); |
| | | log.debug("responseEntity = {}", responseEntity); |
| | | |
| | | String rawResponseString = responseEntity.getBody(); |
| | | SendMailResponse sendMailResponse = new ObjectMapper().readValue(rawResponseString, SendMailResponse.class); |
| | | log.debug("sendMailResponse = {}", sendMailResponse); |
| | | |
| | | if(sendMailResponse == null || sendMailResponse.getData() == null || !"ADDED".equalsIgnoreCase(sendMailResponse.getData().getMessageStatus())) { |
| | | if (sendMailResponse == null || sendMailResponse.getData() == null || !"ADDED".equalsIgnoreCase(sendMailResponse.getData().getMessageStatus())) { |
| | | throw new SendEmailFailException("send email service return error msg! raw response string= " + rawResponseString); |
| | | } |
| | | |
| | | return responseEntity.getBody(); |
| | | } |
| | | catch (SendEmailFailException e) { |
| | | } catch (SendEmailFailException e) { |
| | | throw e; |
| | | } catch (Exception e) { |
| | | log.warn("send email fail by other reason", e); |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service.dto; |
| | | |
| | | import com.pollex.pam.enums.ContactStatusEnum; |
| | | |
| | | public abstract class AbstractAppointmentProcessDTO{ |
| | | |
| | | private ContactStatusEnum contactStatus; |
| | | private Long appointmentId; |
| | | // private Long closedInfoId; |
| | | |
| | | public ContactStatusEnum getContactStatus() { |
| | | return contactStatus; |
| | | } |
| | | |
| | | public void setContactStatus(ContactStatusEnum contactStatus) { |
| | | this.contactStatus = contactStatus; |
| | | } |
| | | |
| | | public Long getAppointmentId() { |
| | | return appointmentId; |
| | | } |
| | | |
| | | public void setAppointmentId(Long appointmentId) { |
| | | this.appointmentId = appointmentId; |
| | | } |
| | | |
| | | // public Long getClosedInfoId() { |
| | | // return closedInfoId; |
| | | // } |
| | | // |
| | | // public void setClosedInfoId(Long closedInfoId) { |
| | | // this.closedInfoId = closedInfoId; |
| | | // } |
| | | |
| | | |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service.dto; |
| | | |
| | | import java.util.Date; |
| | | |
| | | import com.pollex.pam.enums.ContactStatusEnum; |
| | | |
| | | public class AppointmentCloseDTO{ |
| | | |
| | | // private Long closedInfoId; |
| | | private String policyholderIdentityId; |
| | | private String planCode; |
| | | private Date policyEntryDate; |
| | | private String remark; |
| | | private String closedReason; |
| | | private String closedOtherReason; |
| | | private ContactStatusEnum contactStatus; |
| | | private Long appointmentId; |
| | | |
| | | public String getPolicyholderIdentityId() { |
| | | return policyholderIdentityId; |
| | | } |
| | | public void setPolicyholderIdentityId(String policyholderIdentityId) { |
| | | this.policyholderIdentityId = policyholderIdentityId; |
| | | } |
| | | public String getPlanCode() { |
| | | return planCode; |
| | | } |
| | | public void setPlanCode(String planCode) { |
| | | this.planCode = planCode; |
| | | } |
| | | public Date getPolicyEntryDate() { |
| | | return policyEntryDate; |
| | | } |
| | | public void setPolicyEntryDate(Date policyEntryDate) { |
| | | this.policyEntryDate = policyEntryDate; |
| | | } |
| | | public String getRemark() { |
| | | return remark; |
| | | } |
| | | public void setRemark(String remark) { |
| | | this.remark = remark; |
| | | } |
| | | public String getClosedReason() { |
| | | return closedReason; |
| | | } |
| | | public void setClosedReason(String closedReason) { |
| | | this.closedReason = closedReason; |
| | | } |
| | | public String getClosedOtherReason() { |
| | | return closedOtherReason; |
| | | } |
| | | public void setClosedOtherReason(String closedOtherReason) { |
| | | this.closedOtherReason = closedOtherReason; |
| | | } |
| | | public ContactStatusEnum getContactStatus() { |
| | | return contactStatus; |
| | | } |
| | | public void setContactStatus(ContactStatusEnum contactStatus) { |
| | | this.contactStatus = contactStatus; |
| | | } |
| | | public Long getAppointmentId() { |
| | | return appointmentId; |
| | | } |
| | | public void setAppointmentId(Long appointmentId) { |
| | | this.appointmentId = appointmentId; |
| | | } |
| | | // public Long getClosedInfoId() { |
| | | // return closedInfoId; |
| | | // } |
| | | // public void setClosedInfoId(Long closedInfoId) { |
| | | // this.closedInfoId = closedInfoId; |
| | | // } |
| | | |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | package com.pollex.pam.service.dto; |
| | | |
| | | import java.time.Instant; |
| | | import java.util.List; |
| | | |
| | | import com.pollex.pam.domain.AppointmentClosedInfo; |
| | | import com.pollex.pam.domain.AppointmentMemo; |
| | | import com.pollex.pam.domain.AppointmentNoticeLog; |
| | | import com.pollex.pam.domain.InterviewRecord; |
| | | import com.pollex.pam.enums.ContactStatusEnum; |
| | | |
| | | public class AppointmentCustomerViewDTO { |
| | |
| | | private Instant consultantReadTime; |
| | | private Instant contactTime; |
| | | private Float satisfactionScore; |
| | | private List<AppointmentMemo> appointmentMemoList; |
| | | private List<InterviewRecordDTO> interviewRecordDTOs; |
| | | private List<AppointmentNoticeLog> appointmentNoticeLogs; |
| | | private AppointmentClosedInfo appointmentClosedInfo; |
| | | |
| | | public Long getId() { |
| | | return id; |
| | |
| | | public void setSatisfactionScore(Float satisfactionScore) { |
| | | this.satisfactionScore = satisfactionScore; |
| | | } |
| | | public List<AppointmentMemo> getAppointmentMemoList() { |
| | | return appointmentMemoList; |
| | | } |
| | | public void setAppointmentMemoList(List<AppointmentMemo> appointmentMemoList) { |
| | | this.appointmentMemoList = appointmentMemoList; |
| | | } |
| | | public List<InterviewRecordDTO> getInterviewRecordDTOs() { |
| | | return interviewRecordDTOs; |
| | | } |
| | | public void setInterviewRecordDTOs(List<InterviewRecordDTO> interviewRecordDTOs) { |
| | | this.interviewRecordDTOs = interviewRecordDTOs; |
| | | } |
| | | public List<AppointmentNoticeLog> getAppointmentNoticeLogs() { |
| | | return appointmentNoticeLogs; |
| | | } |
| | | public void setAppointmentNoticeLogs(List<AppointmentNoticeLog> appointmentNoticeLogs) { |
| | | this.appointmentNoticeLogs = appointmentNoticeLogs; |
| | | } |
| | | public AppointmentClosedInfo getAppointmentClosedInfo() { |
| | | return appointmentClosedInfo; |
| | | } |
| | | public void setAppointmentClosedInfo(AppointmentClosedInfo appointmentClosedInfo) { |
| | | this.appointmentClosedInfo = appointmentClosedInfo; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service.dto; |
| | | |
| | | public class AppointmentMemoCreateDTO { |
| | | |
| | | private String content; |
| | | private Long appointmentId; |
| | | |
| | | public String getContent() { |
| | | return content; |
| | | } |
| | | public void setContent(String content) { |
| | | this.content = content; |
| | | } |
| | | public Long getAppointmentId() { |
| | | return appointmentId; |
| | | } |
| | | public void setAppointmentId(Long appointmentId) { |
| | | this.appointmentId = appointmentId; |
| | | } |
| | | |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service.dto; |
| | | |
| | | public class AppointmentMemoUpdateDTO{ |
| | | |
| | | private Long id; |
| | | private String content; |
| | | |
| | | public Long getId() { |
| | | return id; |
| | | } |
| | | |
| | | public void setId(Long id) { |
| | | this.id = id; |
| | | } |
| | | |
| | | public String getContent() { |
| | | return content; |
| | | } |
| | | |
| | | public void setContent(String content) { |
| | | this.content = content; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service.dto; |
| | | |
| | | import java.util.Date; |
| | | |
| | | public class AppointmentNoticeSendDTO { |
| | | |
| | | private String message; |
| | | private Long appointmentId; |
| | | private String email; |
| | | private String phone; |
| | | // private String noticeType; |
| | | private Date interviewDate; |
| | | |
| | | public String getMessage() { |
| | | return message; |
| | | } |
| | | public void setMessage(String message) { |
| | | this.message = message; |
| | | } |
| | | public Long getAppointmentId() { |
| | | return appointmentId; |
| | | } |
| | | public void setAppointmentId(Long appointmentId) { |
| | | this.appointmentId = appointmentId; |
| | | } |
| | | // public String getNoticeType() { |
| | | // return noticeType; |
| | | // } |
| | | // public void setNoticeType(String noticeType) { |
| | | // this.noticeType = noticeType; |
| | | // } |
| | | public String getEmail() { |
| | | return email; |
| | | } |
| | | public void setEmail(String email) { |
| | | this.email = email; |
| | | } |
| | | public String getPhone() { |
| | | return phone; |
| | | } |
| | | public void setPhone(String phone) { |
| | | this.phone = phone; |
| | | } |
| | | public Date getInterviewDate() { |
| | | return interviewDate; |
| | | } |
| | | public void setInterviewDate(Date interviewDate) { |
| | | this.interviewDate = interviewDate; |
| | | } |
| | | |
| | | |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service.dto; |
| | | |
| | | public class ClosedProcessDTO extends AbstractAppointmentProcessDTO{ |
| | | |
| | | private String remark; |
| | | private String closedReason; |
| | | private String closedOtherReason; |
| | | |
| | | public String getRemark() { |
| | | return remark; |
| | | } |
| | | public void setRemark(String remark) { |
| | | this.remark = remark; |
| | | } |
| | | public String getClosedReason() { |
| | | return closedReason; |
| | | } |
| | | public void setClosedReason(String closedReason) { |
| | | this.closedReason = closedReason; |
| | | } |
| | | public String getClosedOtherReason() { |
| | | return closedOtherReason; |
| | | } |
| | | public void setClosedOtherReason(String closedOtherReason) { |
| | | this.closedOtherReason = closedOtherReason; |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | private Instant updateTime; |
| | | private String role; |
| | | private String seniority; |
| | | private Long latestAppointmentId; |
| | | |
| | | public boolean isNewConsultant() { |
| | | if(updateTime != null){ |
| | |
| | | |
| | | public void setNewConsultant(boolean newConsultant) { |
| | | this.newConsultant = newConsultant; |
| | | } |
| | | |
| | | public Long getLatestAppointmentId() { |
| | | return latestAppointmentId; |
| | | } |
| | | |
| | | public void setLatestAppointmentId(Long latestAppointmentId) { |
| | | this.latestAppointmentId = latestAppointmentId; |
| | | } |
| | | } |
| | |
| | | import java.time.Instant; |
| | | import java.util.List; |
| | | |
| | | import com.pollex.pam.enums.GenderEnum; |
| | | |
| | | public class ConsultantDetailDTO { |
| | | |
| | | private String name; |
| | |
| | | private Number evaluation; |
| | | private List<String> expertise; |
| | | private String concept; |
| | | private List<String> experiences; |
| | | private String experiences; |
| | | private String awards; |
| | | private GenderEnum gender; |
| | | private String communicationStyle; |
| | | private String email; |
| | | |
| | | public String getName() { |
| | | return name; |
| | |
| | | this.concept = concept; |
| | | } |
| | | |
| | | public List<String> getExperiences() { |
| | | public String getExperiences() { |
| | | return experiences; |
| | | } |
| | | |
| | | public void setExperiences(List<String> experiences) { |
| | | public void setExperiences(String experiences) { |
| | | this.experiences = experiences; |
| | | } |
| | | |
| | |
| | | public void setAwards(String awards) { |
| | | this.awards = awards; |
| | | } |
| | | |
| | | public GenderEnum getGender() { |
| | | return gender; |
| | | } |
| | | |
| | | public void setGender(GenderEnum gender) { |
| | | this.gender = gender; |
| | | } |
| | | |
| | | public String getCommunicationStyle() { |
| | | return communicationStyle; |
| | | } |
| | | |
| | | public void setCommunicationStyle(String communicationStyle) { |
| | | this.communicationStyle = communicationStyle; |
| | | } |
| | | |
| | | public String getEmail() { |
| | | return email; |
| | | } |
| | | |
| | | public void setEmail(String email) { |
| | | this.email = email; |
| | | } |
| | | |
| | | |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service.dto; |
| | | |
| | | import java.util.List; |
| | | |
| | | import com.pollex.pam.enums.GenderEnum; |
| | | |
| | | public class ConsultantEditDTO { |
| | | |
| | | // private Long id; |
| | | private String name; |
| | | private List<String> expertise; |
| | | private String title; |
| | | private String role; |
| | | private String serveArea; |
| | | private GenderEnum gender; |
| | | private String phoneNumber; |
| | | private String companyAddress; |
| | | private Long seniorityYear; |
| | | private Long seniorityMonth; |
| | | private String concept; |
| | | private String experiences; |
| | | private String awards; |
| | | private String communicationStyle; |
| | | private String photoBase64; |
| | | private String photoFileName; |
| | | private String agentNo; |
| | | |
| | | // public Long getId() { |
| | | // return id; |
| | | // } |
| | | // public void setId(Long id) { |
| | | // this.id = id; |
| | | // } |
| | | public String getName() { |
| | | return name; |
| | | } |
| | | public void setName(String name) { |
| | | this.name = name; |
| | | } |
| | | public List<String> getExpertise() { |
| | | return expertise; |
| | | } |
| | | public void setExpertise(List<String> expertise) { |
| | | this.expertise = expertise; |
| | | } |
| | | public String getTitle() { |
| | | return title; |
| | | } |
| | | public void setTitle(String title) { |
| | | this.title = title; |
| | | } |
| | | public String getRole() { |
| | | return role; |
| | | } |
| | | public void setRole(String role) { |
| | | this.role = role; |
| | | } |
| | | public String getServeArea() { |
| | | return serveArea; |
| | | } |
| | | public void setServeArea(String serveArea) { |
| | | this.serveArea = serveArea; |
| | | } |
| | | public GenderEnum getGender() { |
| | | return gender; |
| | | } |
| | | public void setGender(GenderEnum gender) { |
| | | this.gender = gender; |
| | | } |
| | | public String getPhoneNumber() { |
| | | return phoneNumber; |
| | | } |
| | | public void setPhoneNumber(String phoneNumber) { |
| | | this.phoneNumber = phoneNumber; |
| | | } |
| | | public String getCompanyAddress() { |
| | | return companyAddress; |
| | | } |
| | | public void setCompanyAddress(String companyAddress) { |
| | | this.companyAddress = companyAddress; |
| | | } |
| | | public Long getSeniorityYear() { |
| | | return seniorityYear; |
| | | } |
| | | public void setSeniorityYear(Long seniorityYear) { |
| | | this.seniorityYear = seniorityYear; |
| | | } |
| | | public Long getSeniorityMonth() { |
| | | return seniorityMonth; |
| | | } |
| | | public void setSeniorityMonth(Long seniorityMonth) { |
| | | this.seniorityMonth = seniorityMonth; |
| | | } |
| | | public String getConcept() { |
| | | return concept; |
| | | } |
| | | public void setConcept(String concept) { |
| | | this.concept = concept; |
| | | } |
| | | public String getExperiences() { |
| | | return experiences; |
| | | } |
| | | public void setExperiences(String experiences) { |
| | | this.experiences = experiences; |
| | | } |
| | | public String getAwards() { |
| | | return awards; |
| | | } |
| | | public void setAwards(String awards) { |
| | | this.awards = awards; |
| | | } |
| | | public String getCommunicationStyle() { |
| | | return communicationStyle; |
| | | } |
| | | public void setCommunicationStyle(String communicationStyle) { |
| | | this.communicationStyle = communicationStyle; |
| | | } |
| | | public String getPhotoBase64() { |
| | | return photoBase64; |
| | | } |
| | | public void setPhotoBase64(String photoBase64) { |
| | | this.photoBase64 = photoBase64; |
| | | } |
| | | public String getPhotoFileName() { |
| | | return "consultant_"+this.getAgentNo()+".jpg"; |
| | | } |
| | | public String getAgentNo() { |
| | | return agentNo; |
| | | } |
| | | public void setAgentNo(String agentNo) { |
| | | this.agentNo = agentNo; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service.dto; |
| | | |
| | | import java.util.Date; |
| | | |
| | | public class DoneProcessDTO extends AbstractAppointmentProcessDTO{ |
| | | |
| | | private String policyholderIdentityId; |
| | | private String planCode; |
| | | private Date policyEntryDate; |
| | | private String remark; |
| | | |
| | | public String getPolicyholderIdentityId() { |
| | | return policyholderIdentityId; |
| | | } |
| | | public void setPolicyholderIdentityId(String policyholderIdentityId) { |
| | | this.policyholderIdentityId = policyholderIdentityId; |
| | | } |
| | | public String getPlanCode() { |
| | | return planCode; |
| | | } |
| | | public void setPlanCode(String planCode) { |
| | | this.planCode = planCode; |
| | | } |
| | | public Date getPolicyEntryDate() { |
| | | return policyEntryDate; |
| | | } |
| | | public void setPolicyEntryDate(Date policyEntryDate) { |
| | | this.policyEntryDate = policyEntryDate; |
| | | } |
| | | public String getRemark() { |
| | | return remark; |
| | | } |
| | | public void setRemark(String remark) { |
| | | this.remark = remark; |
| | | } |
| | | |
| | | |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service.dto; |
| | | |
| | | import java.time.Instant; |
| | | import java.util.Date; |
| | | |
| | | public class InterviewRecordDTO { |
| | | |
| | | private Long id; |
| | | private String content; |
| | | private Instant createdDate; |
| | | private Instant lastModifiedDate; |
| | | private String createdBy; |
| | | private String lastModifiedBy; |
| | | private Date interviewDate; |
| | | private Long appointmentId; |
| | | |
| | | public Long getId() { |
| | | return id; |
| | | } |
| | | public void setId(Long id) { |
| | | this.id = id; |
| | | } |
| | | public String getContent() { |
| | | return content; |
| | | } |
| | | public void setContent(String content) { |
| | | this.content = content; |
| | | } |
| | | public Instant getCreatedDate() { |
| | | return createdDate; |
| | | } |
| | | public void setCreatedDate(Instant createdDate) { |
| | | this.createdDate = createdDate; |
| | | } |
| | | public Instant getLastModifiedDate() { |
| | | return lastModifiedDate; |
| | | } |
| | | public void setLastModifiedDate(Instant lastModifiedDate) { |
| | | this.lastModifiedDate = lastModifiedDate; |
| | | } |
| | | public String getCreatedBy() { |
| | | return createdBy; |
| | | } |
| | | public void setCreatedBy(String createdBy) { |
| | | this.createdBy = createdBy; |
| | | } |
| | | public String getLastModifiedBy() { |
| | | return lastModifiedBy; |
| | | } |
| | | public void setLastModifiedBy(String lastModifiedBy) { |
| | | this.lastModifiedBy = lastModifiedBy; |
| | | } |
| | | public Date getInterviewDate() { |
| | | return interviewDate; |
| | | } |
| | | public void setInterviewDate(Date interviewDate) { |
| | | this.interviewDate = interviewDate; |
| | | } |
| | | public Long getAppointmentId() { |
| | | return appointmentId; |
| | | } |
| | | public void setAppointmentId(Long appointmentId) { |
| | | this.appointmentId = appointmentId; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | } |
File was renamed from pamapi/src/main/java/com/pollex/pam/service/dto/SatisfactionCustomerCreateDTO.java |
| | |
| | | package com.pollex.pam.service.dto; |
| | | |
| | | public class SatisfactionCustomerCreateDTO { |
| | | public class SatisfactionCustomerScoreDTO { |
| | | |
| | | private Long appointmentId; |
| | | private Float score; |
| | |
| | | import static java.util.stream.Collectors.toList; |
| | | |
| | | import java.util.List; |
| | | import java.util.Optional; |
| | | |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import com.pollex.pam.domain.AppointmentClosedInfo; |
| | | import com.pollex.pam.domain.AppointmentCustomerView; |
| | | import com.pollex.pam.domain.AppointmentNoticeLog; |
| | | import com.pollex.pam.repository.AppointmentClosedInfoRepository; |
| | | import com.pollex.pam.service.AppointmentClosedInfoService; |
| | | import com.pollex.pam.service.AppointmentNoticeLogService; |
| | | import com.pollex.pam.service.AppointmentService; |
| | | import com.pollex.pam.service.dto.AppointmentCustomerViewDTO; |
| | | |
| | | @Service |
| | | public class AppointmentCustomerViewMapper { |
| | | |
| | | |
| | | @Autowired |
| | | AppointmentService appointmentService; |
| | | |
| | | @Autowired |
| | | AppointmentNoticeLogService appointmentNoticeLogService; |
| | | |
| | | @Autowired |
| | | AppointmentClosedInfoRepository appointmentClosedInfoRepository; |
| | | |
| | | @Transactional |
| | | public AppointmentCustomerViewDTO toAppointmentCustomerViewDTO(AppointmentCustomerView source) { |
| | | AppointmentCustomerViewDTO target = new AppointmentCustomerViewDTO(); |
| | | BeanUtils.copyProperties(source, target); |
| | | target.setAppointmentMemoList(source.getAppointmentMemoList()); |
| | | appointmentService.setInterviewRecordDTO(target); |
| | | List<AppointmentNoticeLog> noticeLogs = appointmentNoticeLogService.findByAppointmentId(source.getId()); |
| | | target.setAppointmentNoticeLogs(noticeLogs); |
| | | Optional<AppointmentClosedInfo> appointmentClosedInfoOP = appointmentClosedInfoRepository |
| | | .findByAppointmentId(source.getId()); |
| | | if(appointmentClosedInfoOP.isPresent()) { |
| | | target.setAppointmentClosedInfo(appointmentClosedInfoOP.get()); |
| | | } |
| | | appointmentService.setSatisfactionScore(target, source.getId()); |
| | | |
| | | return target; |
| | | } |
| | | |
| | |
| | | import com.pollex.pam.enums.SatisfactionStatusEnum; |
| | | import com.pollex.pam.repository.AppointmentRepository; |
| | | import com.pollex.pam.service.dto.AppointmentDTO; |
| | | import com.pollex.pam.service.dto.SatisfactionCustomerCreateDTO; |
| | | import com.pollex.pam.service.dto.SatisfactionCustomerScoreDTO; |
| | | |
| | | @Service |
| | | public class AppointmentMapper { |
| | |
| | | target.setAppointmentId(appointment.getId()); |
| | | target.setAgentNo(appointment.getAgentNo()); |
| | | target.setCustomerId(appointment.getCustomerId()); |
| | | target.setStatus(SatisfactionStatusEnum.UNFILLED); |
| | | return target; |
| | | } |
| | | |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service.mapper; |
| | | |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import com.pollex.pam.domain.AppointmentMemo; |
| | | import com.pollex.pam.service.dto.AppointmentMemoCreateDTO; |
| | | import com.pollex.pam.service.dto.AppointmentMemoUpdateDTO; |
| | | |
| | | @Service |
| | | public class AppointmentMemoMapper { |
| | | |
| | | public AppointmentMemo toAppointmentMemo(AppointmentMemoCreateDTO source) { |
| | | AppointmentMemo target = new AppointmentMemo(); |
| | | BeanUtils.copyProperties(source, target); |
| | | return target; |
| | | } |
| | | |
| | | public AppointmentMemo copyToAppointmentMemo(AppointmentMemoUpdateDTO source, AppointmentMemo target) { |
| | | BeanUtils.copyProperties(source, target); |
| | | return target; |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service.mapper; |
| | | |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import com.pollex.pam.domain.AppointmentNoticeLog; |
| | | import com.pollex.pam.service.dto.AppointmentNoticeSendDTO; |
| | | |
| | | @Service |
| | | public class AppointmentNoticeSendMapper { |
| | | |
| | | public AppointmentNoticeLog toAppointmentNoticeLog(AppointmentNoticeSendDTO source) { |
| | | AppointmentNoticeLog target = new AppointmentNoticeLog(); |
| | | BeanUtils.copyProperties(source, target); |
| | | // target.setAppointmentId(source.getAppointmentId()); |
| | | target.setContent(source.getMessage()); |
| | | // target.setEmail(source.getEmail()); |
| | | // target.setPhone(source.getPhone()); |
| | | return target; |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service.mapper; |
| | | |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import com.pollex.pam.config.ApplicationProperties; |
| | | import com.pollex.pam.domain.Consultant; |
| | | import com.pollex.pam.service.dto.ConsultantEditDTO; |
| | | import com.pollex.pam.service.util.FileUtil; |
| | | import com.pollex.pam.service.util.StringUtils; |
| | | |
| | | @Service |
| | | public class ConsultantDTOMapper { |
| | | |
| | | @Autowired |
| | | ApplicationProperties applicationProperty; |
| | | |
| | | public void copyToConsultant(ConsultantEditDTO source, Consultant target) { |
| | | BeanUtils.copyProperties(source, target); |
| | | target.setPhotoPath(FileUtil.toPath(source.getPhotoFileName(), applicationProperty.getFileFolderPath())); |
| | | String expertise = StringUtils.convertToString(source.getExpertise(), ","); |
| | | target.setExpertise(expertise); |
| | | target.setAward(source.getAwards()); |
| | | target.setExperience(source.getExperiences()); |
| | | } |
| | | |
| | | |
| | | |
| | | } |
| | |
| | | import com.pollex.pam.service.dto.ConsultantDetailDTO; |
| | | import com.pollex.pam.service.dto.CustomerFavoriteConsultantDTO; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.*; |
| | |
| | | |
| | | consultantDTO.setContactStatus(null); |
| | | consultantDTO.setUpdateTime(null); |
| | | consultantDTO.setLatestAppointmentId(null); |
| | | // consultantDTO.setLatestAppointmentId(null); |
| | | |
| | | return consultantDTO; |
| | | } |
| | | |
| | | public ConsultantDetailDTO toDetailDto(Consultant source) { |
| | | ConsultantDetailDTO consultantDetailDTO = new ConsultantDetailDTO(); |
| | | BeanUtils.copyProperties(source, consultantDetailDTO); |
| | | consultantDetailDTO.setName(source.getName()); |
| | | consultantDetailDTO.setAgentNo(source.getAgentNo()); |
| | | consultantDetailDTO.setRole(source.getRole()); |
| | |
| | | consultantDetailDTO.setAwards(source.getAward()); |
| | | consultantDetailDTO.setImg(source.getPhotoPath()); |
| | | consultantDetailDTO.setExpertise(splitStringWithChar(source.getExpertise())); |
| | | consultantDetailDTO.setExperiences(splitStringWithChar(source.getExperience())); |
| | | consultantDetailDTO.setExperiences(source.getExperience()); |
| | | |
| | | // todo 決å®å¹é
ç¨åº¦ |
| | | consultantDetailDTO.setSuitability(50); |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service.mapper; |
| | | |
| | | import static java.util.stream.Collectors.toList; |
| | | |
| | | import java.util.List; |
| | | |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import com.pollex.pam.domain.InterviewRecord; |
| | | import com.pollex.pam.service.dto.InterviewRecordDTO; |
| | | |
| | | @Service |
| | | public class InterviewRecordMapper { |
| | | |
| | | public InterviewRecord toInterviewRecord(InterviewRecordDTO source) { |
| | | InterviewRecord target = new InterviewRecord(); |
| | | BeanUtils.copyProperties(source, target); |
| | | return target; |
| | | } |
| | | |
| | | public void copyToInterviewRecord(InterviewRecordDTO source, InterviewRecord target) { |
| | | BeanUtils.copyProperties(source, target); |
| | | } |
| | | |
| | | public List<InterviewRecordDTO> toInterviewRecordDTO(List<InterviewRecord> records) { |
| | | return records.stream() |
| | | .map(s-> toInterviewRecordDTO(s)) |
| | | .collect(toList()); |
| | | } |
| | | |
| | | public InterviewRecordDTO toInterviewRecordDTO(InterviewRecord source) { |
| | | InterviewRecordDTO target = new InterviewRecordDTO(); |
| | | BeanUtils.copyProperties(source, target); |
| | | return target; |
| | | } |
| | | } |
| | |
| | | |
| | | import com.pollex.pam.domain.Satisfaction; |
| | | import com.pollex.pam.enums.SatisfactionStatusEnum; |
| | | import com.pollex.pam.service.dto.SatisfactionCustomerCreateDTO; |
| | | import com.pollex.pam.service.dto.SatisfactionCustomerScoreDTO; |
| | | |
| | | @Service |
| | | public class SatisfactionDTOMapper { |
| | |
| | | @Autowired |
| | | AppointmentMapper appointmentMapper; |
| | | |
| | | public Satisfaction toSatisfaction(SatisfactionCustomerCreateDTO source) { |
| | | public Satisfaction toSatisfaction(SatisfactionCustomerScoreDTO source) { |
| | | Satisfaction satisfaction = appointmentMapper.toSatisfaction(source.getAppointmentId()); |
| | | satisfaction.setScore(source.getScore()); |
| | | if(satisfaction.getScore()!=null) { |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service.util; |
| | | |
| | | |
| | | import java.io.BufferedOutputStream; |
| | | import java.io.File; |
| | | import java.io.FileOutputStream; |
| | | import java.io.IOException; |
| | | import java.util.Base64; |
| | | |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | public class FileUtil { |
| | | |
| | | private static final Logger log = LoggerFactory.getLogger(FileUtil.class); |
| | | |
| | | public static File base64ToFile(String base64, String fileName, String folderPath) { |
| | | if(base64 == null){ |
| | | return new File(folderPath); |
| | | } |
| | | File dir=new File(folderPath); |
| | | dir.setWritable(true); |
| | | dir.setReadable(true); |
| | | if (!dir.exists() && !dir.isDirectory()) { |
| | | dir.mkdirs(); |
| | | } |
| | | return base64ToFile(base64, toPath(fileName, folderPath)); |
| | | } |
| | | |
| | | public static File base64ToFile(String base64, String path) { |
| | | if(base64 == null){ |
| | | return new File(path); |
| | | } |
| | | |
| | | BufferedOutputStream bos = null; |
| | | |
| | | try { |
| | | byte[] bytes = Base64.getDecoder().decode(base64); |
| | | File file=new File(path); |
| | | file.setWritable(true); |
| | | file.setReadable(true); |
| | | bos = new BufferedOutputStream(new FileOutputStream(file)); |
| | | bos.write(bytes); |
| | | |
| | | |
| | | return file; |
| | | } catch (Exception e) { |
| | | log.error("upload file base64 to file error",e); |
| | | } finally { |
| | | if (bos != null) { |
| | | try { |
| | | bos.close(); |
| | | } catch (IOException e) { |
| | | log.error("upload file base64 to file error",e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | public static boolean isFileExisted(String fileName, String folderPath) { |
| | | return new File(toPath(fileName, folderPath)).isFile(); |
| | | } |
| | | |
| | | public static String toPath(String fileName, String folderPath) { |
| | | return folderPath + "/" + fileName; |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.service.util; |
| | | |
| | | import java.util.List; |
| | | |
| | | public class StringUtils { |
| | | |
| | | public static String convertToString(List<String> source |
| | | , String separator) { |
| | | StringBuilder result = new StringBuilder(); |
| | | for (String sourceString : source) { |
| | | result.append(sourceString+separator); |
| | | } |
| | | result.deleteCharAt(result.length()-1); |
| | | return result.toString(); |
| | | } |
| | | |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.web.rest; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.http.ResponseEntity; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import com.pollex.pam.domain.AppointmentMemo; |
| | | import com.pollex.pam.security.SecurityUtils; |
| | | import com.pollex.pam.service.AppointmentMemoService; |
| | | import com.pollex.pam.service.dto.AppointmentMemoCreateDTO; |
| | | import com.pollex.pam.service.dto.AppointmentMemoUpdateDTO; |
| | | |
| | | @RestController |
| | | @RequestMapping("/api/appointment/memo") |
| | | public class AppointmentMemoResource { |
| | | |
| | | @Autowired |
| | | AppointmentMemoService appointmentMemoService; |
| | | |
| | | @PostMapping("/create") |
| | | public ResponseEntity<AppointmentMemo> createMemo(@RequestBody AppointmentMemoCreateDTO memoDTO) { |
| | | appointmentMemoService.checkPermission(memoDTO.getAppointmentId()); |
| | | AppointmentMemo memo = appointmentMemoService.create(memoDTO); |
| | | return new ResponseEntity<>(memo, HttpStatus.OK); |
| | | } |
| | | |
| | | @PostMapping("/update") |
| | | public ResponseEntity<AppointmentMemo> updateMemo(@RequestBody AppointmentMemoUpdateDTO memoDTO) { |
| | | AppointmentMemo memo = appointmentMemoService.update(memoDTO); |
| | | return new ResponseEntity<>(memo, HttpStatus.OK); |
| | | } |
| | | |
| | | @DeleteMapping("/{memoId}") |
| | | public void deleteMemo(@PathVariable Long memoId) { |
| | | appointmentMemoService.delete(memoId); |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | package com.pollex.pam.web.rest; |
| | | |
| | | import com.pollex.pam.appointment.process.AppointmentProcess; |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.security.SecurityUtils; |
| | | import com.pollex.pam.service.SendMsgService; |
| | | import com.pollex.pam.service.dto.AppointmentUpdateDTO; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.http.ResponseEntity; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import com.pollex.pam.service.AppointmentService; |
| | | import com.pollex.pam.service.PersonalNotificationService; |
| | | import com.pollex.pam.service.SatisfactionService; |
| | | import com.pollex.pam.service.dto.AppointmentCloseDTO; |
| | | import com.pollex.pam.service.dto.AppointmentCreateDTO; |
| | | import com.pollex.pam.service.dto.AppointmentCustomerViewDTO; |
| | | |
| | | import java.util.Objects; |
| | | |
| | | @RestController |
| | | @RequestMapping("/api/appointment") |
| | |
| | | @Autowired |
| | | SendMsgService sendMsgService; |
| | | |
| | | @Autowired |
| | | AppointmentProcess abstractAppointmentProcess; |
| | | |
| | | @Autowired |
| | | PersonalNotificationService personalNotificationService; |
| | | |
| | | @PutMapping("") |
| | | public ResponseEntity<Void> updateAppointment(@RequestBody AppointmentUpdateDTO appointment) { |
| | | appointmentService.updateAppointment(appointment); |
| | | public ResponseEntity<Void> updateAppointment(@RequestBody AppointmentUpdateDTO dto) { |
| | | Appointment appointment = appointmentService.updateAppointment(dto); |
| | | personalNotificationService.createUpdateAppointmentToConsultant(appointment); |
| | | return ResponseEntity.noContent().build(); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @PostMapping("/markAsContacted/{appointmentId}") |
| | | public void markAsContacted(@PathVariable Long appointmentId) { |
| | | public AppointmentCustomerViewDTO markAsContacted(@PathVariable Long appointmentId) { |
| | | appointmentService.markAsContacted(appointmentId); |
| | | } |
| | | return appointmentService.getAppointmentDetail(appointmentId); |
| | | } |
| | | |
| | | @GetMapping("/getDetail/{appointmentId}") |
| | | public AppointmentCustomerViewDTO getAppointmentDetail(@PathVariable Long appointmentId) { |
| | |
| | | appointmentService.recordConsultantReadTime(appointmentId); |
| | | return ResponseEntity.noContent().build(); |
| | | } |
| | | |
| | | @PostMapping("/close") |
| | | public ResponseEntity<Void> closeAppointment(@RequestBody AppointmentCloseDTO closeDTO) { |
| | | appointmentService.closeAppointment(closeDTO); |
| | | return ResponseEntity.noContent().build(); |
| | | } |
| | | |
| | | @GetMapping("/customer/expiring/newest") |
| | | public ResponseEntity<AppointmentCustomerViewDTO> getNewestExpiringAppointment() { |
| | | Long customerId = SecurityUtils.getCustomerDBId(); |
| | | AppointmentCustomerViewDTO customerNewestExpiringAppointment = appointmentService.getCustomerNewestExpiringAppointment(customerId); |
| | | |
| | | return new ResponseEntity<>(customerNewestExpiringAppointment, HttpStatus.OK); |
| | | } |
| | | |
| | | @GetMapping("/consultant/pending/sum") |
| | | public ResponseEntity<Long> getConsultantPendingAppointmentSum() { |
| | | String agentNo = SecurityUtils.getAgentNo(); |
| | | return new ResponseEntity<>(appointmentService.getConsultantPendingAppointmentSum(agentNo), HttpStatus.OK); |
| | | } |
| | | |
| | | // @PostMapping("/close/info/edit") |
| | | // public ResponseEntity<Void> editAppointmentClosedInfo(@RequestBody AppointmentCloseDTO closeDTO) { |
| | | // |
| | | // if(closeDTO.getContactStatus() == ContactStatusEnum.DONE) { |
| | | // DoneProcessDTO dto = new DoneProcessDTO(); |
| | | // BeanUtils.copyProperties(closeDTO, dto); |
| | | // abstractAppointmentProcess.editClosedInfo(dto); |
| | | // }else if(closeDTO.getContactStatus() == ContactStatusEnum.CLOSED){ |
| | | // ClosedProcessDTO dto = new ClosedProcessDTO(); |
| | | // BeanUtils.copyProperties(closeDTO, dto); |
| | | // abstractAppointmentProcess.editClosedInfo(dto); |
| | | // }else { |
| | | // return ResponseEntity.notFound().build(); |
| | | // } |
| | | // |
| | | // return ResponseEntity.noContent().build(); |
| | | // } |
| | | } |
| | |
| | | package com.pollex.pam.web.rest; |
| | | |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.domain.Consultant; |
| | | import com.pollex.pam.security.SecurityUtils; |
| | | import com.pollex.pam.service.AppointmentService; |
| | | import com.pollex.pam.service.ConsultantService; |
| | | import com.pollex.pam.service.dto.*; |
| | | import org.apache.commons.compress.utils.IOUtils; |
| | |
| | | import org.springframework.http.ResponseEntity; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.io.File; |
| | | import java.io.FileInputStream; |
| | | import java.io.FileNotFoundException; |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.util.List; |
| | | |
| | | @RestController |
| | |
| | | |
| | | @Autowired |
| | | ConsultantService consultantService; |
| | | |
| | | @Autowired |
| | | AppointmentService appointmentService; |
| | | |
| | | @GetMapping("/favorite") |
| | | public ResponseEntity<List<CustomerFavoriteConsultantDTO>> getMyConsultantList() { |
| | |
| | | return consultantService.getMyAppointment(); |
| | | } |
| | | |
| | | @GetMapping(value = "/avatar/{fileName}", produces = MediaType.IMAGE_JPEG_VALUE) |
| | | public ResponseEntity<byte[]> getAvatarImage(@PathVariable String fileName) throws IOException { |
| | | try { |
| | | Resource resource = new ClassPathResource("static/consultant/" + fileName); |
| | | InputStream in = resource.getInputStream(); |
| | | return new ResponseEntity<>(IOUtils.toByteArray(in), HttpStatus.OK); |
| | | } catch (FileNotFoundException e) { |
| | | return new ResponseEntity<>(HttpStatus.NOT_FOUND); |
| | | } |
| | | @GetMapping(value = "/avatar/{agentNo}", produces = MediaType.IMAGE_JPEG_VALUE) |
| | | public ResponseEntity<byte[]> getAvatarImage(@PathVariable String agentNo) throws IOException { |
| | | // try { |
| | | // Resource resource = new ClassPathResource("static/consultant/" + fileName); |
| | | // InputStream in = resource.getInputStream(); |
| | | // return new ResponseEntity<>(IOUtils.toByteArray(in), HttpStatus.OK); |
| | | // } catch (FileNotFoundException e) { |
| | | // return new ResponseEntity<>(HttpStatus.NOT_FOUND); |
| | | // } |
| | | InputStream in = consultantService.getAvatarImage(agentNo); |
| | | if(in!=null) { |
| | | return new ResponseEntity<>(IOUtils.toByteArray(in), HttpStatus.OK); |
| | | }else { |
| | | return new ResponseEntity<>(HttpStatus.NOT_FOUND); |
| | | } |
| | | |
| | | |
| | | } |
| | | |
| | | @PostMapping("/record/allAppointmentsView") |
| | |
| | | consultantService.recordAllAppointmentsView(); |
| | | return ResponseEntity.noContent().build(); |
| | | } |
| | | |
| | | @PostMapping("/edit") |
| | | public ResponseEntity<Consultant> editConsultant(@RequestBody ConsultantEditDTO editDTO) { |
| | | if(!editDTO.getAgentNo().equals(SecurityUtils.getAgentNo())) { |
| | | throw new IllegalAccessError(); |
| | | } |
| | | Consultant editResult = consultantService.editConsultant(editDTO); |
| | | return new ResponseEntity<>(editResult, HttpStatus.OK); |
| | | } |
| | | |
| | | @PostMapping("/sendSatisfactionToClient/{appointmentId}") |
| | | public ResponseEntity<Void> sendSatisfactionToClient(@PathVariable Long appointmentId) { |
| | | Appointment appointment = appointmentService.findById(appointmentId); |
| | | if(!appointment.getAgentNo().equals(SecurityUtils.getAgentNo())) { |
| | | throw new IllegalAccessError(); |
| | | } |
| | | consultantService.sendSatisfactionToClient(appointment); |
| | | return ResponseEntity.noContent().build(); |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.web.rest; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import com.pollex.pam.domain.InterviewRecord; |
| | | import com.pollex.pam.service.InterviewRecordService; |
| | | import com.pollex.pam.service.dto.InterviewRecordDTO; |
| | | |
| | | @RestController |
| | | @RequestMapping("/api/interview_record") |
| | | public class InterviewRecordResource { |
| | | |
| | | @Autowired |
| | | InterviewRecordService interviewRecordService; |
| | | |
| | | @PostMapping("/create") |
| | | public InterviewRecord create(@RequestBody InterviewRecordDTO dto) { |
| | | return interviewRecordService.create(dto); |
| | | } |
| | | |
| | | @PostMapping("/update") |
| | | public InterviewRecord update(@RequestBody InterviewRecordDTO dto) { |
| | | return interviewRecordService.update(dto); |
| | | } |
| | | |
| | | @DeleteMapping("/{interviewRecordId}") |
| | | public void delete(@PathVariable Long interviewRecordId) { |
| | | interviewRecordService.delete(interviewRecordId); |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.web.rest; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import com.pollex.pam.domain.Appointment; |
| | | import com.pollex.pam.domain.AppointmentNoticeLog; |
| | | import com.pollex.pam.security.SecurityUtils; |
| | | import com.pollex.pam.service.AppointmentService; |
| | | import com.pollex.pam.service.NoticeService; |
| | | import com.pollex.pam.service.dto.AppointmentNoticeSendDTO; |
| | | |
| | | @RestController |
| | | @RequestMapping("/api/notice") |
| | | public class NoticeResource { |
| | | |
| | | @Autowired |
| | | NoticeService noticeService; |
| | | |
| | | @Autowired |
| | | AppointmentService appointmentService; |
| | | |
| | | @PostMapping("/send") |
| | | public void sendNotice(@RequestBody AppointmentNoticeSendDTO dto) { |
| | | Appointment appointment = appointmentService.findById(dto.getAppointmentId()); |
| | | if(!appointment.getAgentNo().equals(SecurityUtils.getAgentNo())) { |
| | | throw new IllegalAccessError("The user do not have access permission"); |
| | | } |
| | | noticeService.sendNotice(dto); |
| | | } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.web.rest; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.http.ResponseEntity; |
| | | import org.springframework.util.StringUtils; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import com.pollex.pam.domain.Consultant; |
| | | import com.pollex.pam.domain.PersonalNotification; |
| | | import com.pollex.pam.enums.NotificationTypeEnum; |
| | | import com.pollex.pam.enums.PersonalNotificationRoleEnum; |
| | | import com.pollex.pam.repository.PersonalNotificationRepository; |
| | | import com.pollex.pam.security.SecurityUtils; |
| | | import com.pollex.pam.service.ConsultantService; |
| | | import com.pollex.pam.service.PersonalNotificationService; |
| | | |
| | | @RestController |
| | | @RequestMapping("/api/personal_notification") |
| | | public class PersonalNotificationResource { |
| | | |
| | | @Autowired |
| | | PersonalNotificationService personalNotificationService; |
| | | |
| | | @Autowired |
| | | PersonalNotificationRepository personalNotificationRepository; |
| | | |
| | | @Autowired |
| | | ConsultantService consultantService; |
| | | |
| | | @GetMapping("/getMyPersonalNotification") |
| | | public ResponseEntity<List<PersonalNotification>> getMyPersonalNotification() { |
| | | List<PersonalNotification> personalNotificationList = new ArrayList<>(); |
| | | if(StringUtils.hasText(SecurityUtils.getAgentNo())) { |
| | | Consultant consultant = consultantService.findByAgentNo(SecurityUtils.getAgentNo()); |
| | | personalNotificationList = personalNotificationService.getMyPersonalNotification(consultant.getId(), PersonalNotificationRoleEnum.CONSULTANT); |
| | | }else if(SecurityUtils.getCustomerDBId()!=null){ |
| | | personalNotificationList = personalNotificationService.getMyPersonalNotification(SecurityUtils.getCustomerDBId(), PersonalNotificationRoleEnum.CUSTOMER); |
| | | } |
| | | return new ResponseEntity<>(personalNotificationList, HttpStatus.OK); |
| | | } |
| | | |
| | | @PostMapping("/readAllMyNotification") |
| | | public void readAll() { |
| | | personalNotificationService.readAllMyNotification(); |
| | | } |
| | | } |
| | |
| | | import com.pollex.pam.domain.Satisfaction; |
| | | import com.pollex.pam.security.SecurityUtils; |
| | | import com.pollex.pam.service.SatisfactionService; |
| | | import com.pollex.pam.service.dto.SatisfactionCustomerCreateDTO; |
| | | import com.pollex.pam.service.dto.SatisfactionCustomerScoreDTO; |
| | | import com.pollex.pam.service.dto.SatisfactionDTO; |
| | | import com.pollex.pam.service.dto.SatisfactionUpdateDTO; |
| | | |
| | |
| | | @Autowired |
| | | SatisfactionService satisfactionService; |
| | | |
| | | @PostMapping("/create") |
| | | public Satisfaction createSatisfaction(@RequestBody SatisfactionCustomerCreateDTO createDTO) { |
| | | return satisfactionService.createSatisfaction(createDTO); |
| | | @PostMapping("/score") |
| | | public Satisfaction scorefaction(@RequestBody SatisfactionCustomerScoreDTO scoreDTO) { |
| | | return satisfactionService.scorefaction(scoreDTO); |
| | | } |
| | | |
| | | @PostMapping("/score/all") |
| | | public List<Satisfaction> scoreAllfaction(@RequestBody List<SatisfactionCustomerScoreDTO> scoreDTO) { |
| | | return satisfactionService.scoreAllfaction(scoreDTO); |
| | | } |
| | | |
| | | @GetMapping("/getMySatisfaction") |
| | |
| | | return ResponseEntity.ok(sendMsgService.sendMsgBySMS(toMobile, content)); |
| | | } |
| | | |
| | | @GetMapping("/byEmail") |
| | | public ResponseEntity<String> byEmail( |
| | | @RequestParam String from, |
| | | @RequestParam String to, |
| | | @RequestParam String subject, |
| | | @RequestParam String content, |
| | | @RequestParam boolean htmlFormat |
| | | ) { |
| | | return ResponseEntity.ok(sendMsgService.sendMsgByEmail(from, to, subject, content, htmlFormat)); |
| | | } |
| | | // @GetMapping("/byEmail") |
| | | // public ResponseEntity<String> byEmail( |
| | | // @RequestParam String from, |
| | | // @RequestParam String to, |
| | | // @RequestParam String subject, |
| | | // @RequestParam String content, |
| | | // @RequestParam boolean htmlFormat |
| | | // ) { |
| | | // return ResponseEntity.ok(sendMsgService.sendMsgByEmail(from, to, subject, content, htmlFormat)); |
| | | // } |
| | | // |
| | | // @GetMapping("/byHtmlEmail") |
| | | // public ResponseEntity<String> byHtmlEmail( |
| | | // @RequestParam String from, |
| | | // @RequestParam String to |
| | | // ) { |
| | | // return ResponseEntity.ok(testSendMsgByHtmlTemplateEmail(from, to)); |
| | | // } |
| | | |
| | | @GetMapping("/byHtmlEmail") |
| | | public ResponseEntity<String> byHtmlEmail( |
| | | @RequestParam String from, |
| | | @RequestParam String to |
| | | ) { |
| | | return ResponseEntity.ok(testSendMsgByHtmlTemplateEmail(from, to)); |
| | | } |
| | | |
| | | private String testSendMsgByHtmlTemplateEmail(String from, String to) { |
| | | Context context = new Context(); |
| | | context.setVariable("content", "親æçé¡§åæ¨å¥½ï¼æ¨æä¸çä¾èªä¿èª åªåå¹³å°çæ°é ç´å®\n"); |
| | | context.setVariable("urlHint", appointmentService.getAppointmentDetailUrl(0L)); |
| | | |
| | | String content = springTemplateEngine.process("mail/appointmentNotifyEmail", context); |
| | | return sendMsgService.sendMsgByEmail(from, to, NOTIFY_EMAIL_SUBJECT, content, true); |
| | | } |
| | | // private String testSendMsgByHtmlTemplateEmail(String from, String to) { |
| | | // Context context = new Context(); |
| | | // context.setVariable("content", "親æçé¡§åæ¨å¥½ï¼æ¨æä¸çä¾èªä¿èª åªåå¹³å°çæ°é ç´å®\n"); |
| | | // context.setVariable("urlHint", appointmentService.getAppointmentDetailUrl(0L)); |
| | | // |
| | | // String content = springTemplateEngine.process("mail/appointmentNotifyEmail", context); |
| | | // return sendMsgService.sendMsgByEmail(from, to, NOTIFY_EMAIL_SUBJECT, content, true); |
| | | // } |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.web.rest.errors; |
| | | |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.web.bind.annotation.ResponseStatus; |
| | | |
| | | @ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Appointment close info not found") |
| | | public class AppointmentClosedInfoNotFoundException extends RuntimeException{ |
| | | |
| | | /** |
| | | * |
| | | */ |
| | | private static final long serialVersionUID = 1L; |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.web.rest.errors; |
| | | |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.web.bind.annotation.ResponseStatus; |
| | | |
| | | @ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Appointment memo not found") |
| | | public class AppointmentMemoNotFoundException extends RuntimeException{ |
| | | |
| | | /** |
| | | * |
| | | */ |
| | | private static final long serialVersionUID = 1L; |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.web.rest.errors; |
| | | |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.web.bind.annotation.ResponseStatus; |
| | | |
| | | @ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "InterviewRecord not found") |
| | | public class InterviewRecordNotFoundException extends RuntimeException{ |
| | | |
| | | /** |
| | | * |
| | | */ |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.web.rest.errors; |
| | | |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.web.bind.annotation.ResponseStatus; |
| | | |
| | | @ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "not found any expiring appointment") |
| | | public class NotFoundExpiringAppointmentException extends RuntimeException { |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.web.rest.errors; |
| | | |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.web.bind.annotation.ResponseStatus; |
| | | |
| | | @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR, reason = "Satisfaction already exist") |
| | | public class SatisfactionAlreadyExistException extends RuntimeException{ |
| | | |
| | | /** |
| | | * |
| | | */ |
| | | private static final long serialVersionUID = 1L; |
| | | } |
¤ñ¹ï·sÀÉ®× |
| | |
| | | package com.pollex.pam.web.rest.errors; |
| | | |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.web.bind.annotation.ResponseStatus; |
| | | |
| | | @ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Satisfaction not found") |
| | | public class SatisfactionNotFoundException extends RuntimeException{ |
| | | |
| | | /** |
| | | * |
| | | */ |
| | | private static final long serialVersionUID = 1L; |
| | | } |
| | |
| | | indent-output: true |
| | | datasource: |
| | | type: com.zaxxer.hikari.HikariDataSource |
| | | url: jdbc:postgresql://dev.pollex.com.tw:5433/pam |
| | | #url: jdbc:postgresql://localhost:5432/omo?currentSchema=omo |
| | | url: jdbc:postgresql://dev.pollex.com.tw:5433/pam_p2 |
| | | #url: jdbc:postgresql://localhost:5432/omo?currentSchema=public |
| | | username: pamadmin |
| | | password: pamadmin |
| | | hikari: |
| | |
| | | # Remove 'faker' if you do not want the sample data to be loaded automatically |
| | | contexts: dev, faker |
| | | mail: |
| | | host: localhost |
| | | port: 25 |
| | | username: |
| | | password: |
| | | host: smtp.gmail.com |
| | | port: 587 |
| | | username: pollex.testing@gmail.com |
| | | password: ilismmmhtscppxft |
| | | properties: |
| | | mail: |
| | | smtp: |
| | | auth: true |
| | | starttls: |
| | | enable: true |
| | | messages: |
| | | cache-duration: PT1S # 1 second, see the ISO 8601 standard |
| | | thymeleaf: |
| | |
| | | e-service-login-func: ValidateUsrLogin |
| | | e-service-login-sys: epos |
| | | front-end-domain: http://localhost:3000 |
| | | send-notify-msg: false |
| | | sms: |
| | | send-notify-msg: false |
| | | url: https://localhost:8081/testSMS |
| | | source-code: ePos |
| | | sender: POS |
| | | sms-type: '0017' |
| | | subject: 'åªåå¹³å°éç¥' |
| | | email: |
| | | send-notify-msg: false |
| | | url: https://localhost:8081/testEmail |
| | | function-id: epos |
| | | sender-email: noreply@pcalife.com.tw |
| | | method: 'POLLEX_GMAIL' |
| | | file-folder-path: C://pam_file |
| | |
| | | # Remove 'faker' if you do not want the sample data to be loaded automatically |
| | | contexts: pollex, faker |
| | | mail: |
| | | host: localhost |
| | | port: 25 |
| | | username: |
| | | password: |
| | | host: smtp.gmail.com |
| | | port: 587 |
| | | username: pollex.testing@gmail.com |
| | | password: ilismmmhtscppxft |
| | | properties: |
| | | mail: |
| | | smtp: |
| | | auth: true |
| | | starttls: |
| | | enable: true |
| | | messages: |
| | | cache-duration: PT1S # 1 second, see the ISO 8601 standard |
| | | thymeleaf: |
| | |
| | | e-service-login-func: ValidateUsrLogin |
| | | e-service-login-sys: epos |
| | | front-end-domain: http://dev.pollex.com.tw:5566/pam |
| | | send-notify-msg: false |
| | | sms: |
| | | send-notify-msg: false |
| | | url: https://localhost:8081/testSMS |
| | | source-code: ePos |
| | | sender: POS |
| | | sms-type: '0017' |
| | | subject: 'åªåå¹³å°éç¥' |
| | | email: |
| | | send-notify-msg: true |
| | | url: https://localhost:8081/testEmail |
| | | function-id: epos |
| | | sender-email: noreply@pcalife.com.tw |
| | | method: 'POLLEX_GMAIL' |
| | | file-folder-path: C://pam_file |
| | |
| | | # https://www.jhipster.tech/common-application-properties/ |
| | | # =================================================================== |
| | | |
| | | # application: |
| | | application: |
| | | mock-login: false |
| | | otp-web-service-url: https://vtwlifeopensysuat.pru.intranet.asia/pcalife-otp/ws/otpWebService?wsdl |
| | | otp-web-service-password: es20!%Pass |
| | | otp-web-service-system-type: epos |
| | | e-service-login-url: https://eserviceuat.pcalife.com.tw/sso/chatbotValidate |
| | | e-service-login-func: ValidateUsrLogin |
| | | e-service-login-sys: epos |
| | | front-end-domain: https://vtwlifeopensysuat.pru.intranet.asia/pam |
| | | sms: |
| | | send-notify-msg: true |
| | | url: https://vtwlifeopensysuat.pru.intranet.asia/MesgQueueMgmnt/rest/smsSendMsgResource |
| | | source-code: ePos |
| | | sender: POS |
| | | sms-type: '0017' |
| | | subject: 'åªåå¹³å°éç¥' |
| | | email: |
| | | send-notify-msg: true |
| | | url: https://vtwlifeopensysuat.pru.intranet.asia/tsgw/mq/mqSendMail |
| | | function-id: epos |
| | | sender-email: noreply@pcalife.com.tw |
| | | method: 'PAM_EMAIL_SERVICE' |
| | | file-folder-path: /sfs_omo/vtwlifewpsfs01/SensitiveData4AP$/OMO |
| | |
| | | e-service-login-func: ValidateUsrLogin |
| | | e-service-login-sys: epos |
| | | front-end-domain: https://vtwlifeopensyssit.pru.intranet.asia/pam |
| | | send-notify-msg: true |
| | | sms: |
| | | send-notify-msg: true |
| | | url: https://vtwlifeopensysuat.pru.intranet.asia/MesgQueueMgmnt/rest/smsSendMsgResource |
| | | source-code: ePos |
| | | sender: POS |
| | | sms-type: '0017' |
| | | subject: 'åªåå¹³å°éç¥' |
| | | email: |
| | | send-notify-msg: true |
| | | url: https://vtwlifeopensysuat.pru.intranet.asia/tsgw/mq/mqSendMail |
| | | function-id: epos |
| | | sender-email: noreply@pcalife.com.tw |
| | | method: 'PAM_EMAIL_SERVICE' |
| | | file-folder-path: /sfs_omo/vtwlifewuftp66/sensitivedata4ap$/OMOSIT |
| | |
| | | e-service-login-func: ValidateUsrLogin |
| | | e-service-login-sys: epos |
| | | front-end-domain: https://vtwlifeopensysuat.pru.intranet.asia/pam |
| | | send-notify-msg: true |
| | | sms: |
| | | send-notify-msg: true |
| | | url: https://vtwlifeopensysuat.pru.intranet.asia/MesgQueueMgmnt/rest/smsSendMsgResource |
| | | source-code: ePos |
| | | sender: POS |
| | | sms-type: '0017' |
| | | subject: 'åªåå¹³å°éç¥' |
| | | email: |
| | | send-notify-msg: true |
| | | url: https://vtwlifeopensysuat.pru.intranet.asia/tsgw/mq/mqSendMail |
| | | function-id: epos |
| | | sender-email: noreply@pcalife.com.tw |
| | | method: 'PAM_EMAIL_SERVICE' |
| | | file-folder-path: /sfs_omo/vtwlifewuftp66/sensitivedata4ap$/OMO |
| | |
| | | email.reset.greeting=Dear {0} |
| | | email.reset.text1=For your pamapi account a password reset was requested, please click on the URL below to reset it: |
| | | email.reset.text2=Regards, |
| | | |
| | | # satisfaction write email |
| | | email.write.satisfaction.content={0}\u9867\u554F\u8ACB\u60A8\u586B\u5BEB\u4FDD\u8AA0\u5A92\u5408\u5E73\u53F0\u7684\u6EFF\u610F\u5EA6\u8A55\u6BD4{1} |
| | | |
| | | # appointment pending notify email |
| | | email.write.appointment.pending.content=\u60a8\u6709{0}\u5247\u9810\u7d04\u55ae\u672a\u9032\u884c\u806f\u7e6b\uff0c\u8acb\u76e1\u901f\u8655\u7406 |
| | | |
| | | # appointment expiring notify email |
| | | email.write.appointment.expiring.content=\u5f88\u62b1\u6b49\uff01\u60a8\u9810\u7d04{0}\u9867\u554f\u6b63\u5fd9\u788c\u4e2d\uff0c\u8acb\u60a8\u53d6\u6d88\u9810\u7d04\u4e26\u6539\u9078\u5176\u4ed6\u9867\u554f\uff0c\u8acb\u9ede\u64ca\u7db2\u5740\uff1a{1} |
| | |
| | | email.reset.greeting=\u89AA\u611B\u7684 {0} |
| | | email.reset.text1=\u60A8\u7684 pamapi \u5E33\u865F\u88AB\u8981\u6C42\u91CD\u65B0\u8A2D\u5B9A\u5BC6\u78BC\uFF0C\u8ACB\u9EDE\u4E0B\u5217\u7DB2\u5740\u8A2D\u5B9A: |
| | | email.reset.text2=\u795D\u60A8\u4F7F\u7528\u6109\u5FEB\uFF0C |
| | | |
| | | # satisfaction write email |
| | | email.write.satisfaction.content={0}\u9867\u554F\u8ACB\u60A8\u586B\u5BEB\u4FDD\u8AA0\u5A92\u5408\u5E73\u53F0\u7684\u6EFF\u610F\u5EA6\u8A55\u6BD4{1} |
| | | |
| | | # appointment pending notify email |
| | | email.write.appointment.pending.content=\u60a8\u6709{0}\u5247\u9810\u7d04\u55ae\u672a\u9032\u884c\u806f\u7e6b\uff0c\u8acb\u76e1\u901f\u8655\u7406 |
| | | |
| | | # appointment expiring notify email |
| | | email.write.appointment.expiring.content=\u5f88\u62b1\u6b49\uff01\u60a8\u9810\u7d04{0}\u9867\u554f\u6b63\u5fd9\u788c\u4e2d\uff0c\u8acb\u60a8\u53d6\u6d88\u9810\u7d04\u4e26\u6539\u9078\u5176\u4ed6\u9867\u554f\uff0c\u8acb\u9ede\u64ca\u7db2\u5740\uff1a{1} |
| | | |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <!DOCTYPE html> |
| | | <html xmlns:th="http://www.thymeleaf.org" lang="zh"> |
| | | <head> |
| | | <title>é ç´å®æªèçéç¥</title> |
| | | <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> |
| | | </head> |
| | | <body> |
| | | <p th:text="#{email.write.appointment.expiring.content(${consultantName}, ${notifyUrl})}">徿±æï¼æ¨é ç´xxx顧忣å¿ç¢ä¸ , è«æ¨åæ¶é ç´ä¸¦æ¹é¸å
¶ä»é¡§å</p> |
| | | </body> |
| | | </html> |
| | | |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <!DOCTYPE html> |
| | | <html xmlns:th="http://www.thymeleaf.org" lang="zh"> |
| | | <head> |
| | | <title>é ç´å®æªèçéç¥</title> |
| | | <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> |
| | | </head> |
| | | <body> |
| | | <p th:text="#{email.write.appointment.pending.content(${pendingAppointmentSum})}">æ¨æxåé ç´å®æªé²è¡è¯ç¹«ï¼è«ç¡éèç</p> |
| | | </body> |
| | | </html> |
| | | |
¤ñ¹ï·sÀÉ®× |
| | |
| | | <!DOCTYPE html> |
| | | <html xmlns:th="http://www.thymeleaf.org" lang="zh"> |
| | | <head> |
| | | <title>滿æåº¦å¡«å¯«éç¥</title> |
| | | <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> |
| | | </head> |
| | | <body> |
| | | <p th:text="#{email.write.satisfaction.content(${consultantName},${appointmentUrl})}">é¡§åè«æ¨å¡«å¯«ä¿èª åªåå¹³å°ç滿æåº¦è©æ¯</p> |
| | | </body> |
| | | </html> |