App.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <script setup>
  2. import { ref } from 'vue'
  3. const ticker = ref('')
  4. const tickers = ref([
  5. { name: 'DEMO', price: '-' },
  6. { name: 'DEMO', price: '-' },
  7. { name: 'DEMO', price: '-' },
  8. ])
  9. fetch(url).then(res => {
  10. // здесь res строка, поэтому необходимо преобразовать ее в json-объект
  11. if (res.ok) {
  12. // метод json тоже асинхронный, поэтому мы возвращаем очередной промис
  13. return res.json()
  14. }
  15. else throw new Error('Что-то пошло не так')
  16. }).then(json => {
  17. return json
  18. }).catch(error => {
  19. // можем извлечь текст ошибки и показать клиенту
  20. }).finally(() => {
  21. // тут мы можем погасить какую-нибудь "крутилку", если запускали её перед fetch
  22. })
  23. onMounted(async () => {
  24. const f = await fetch(
  25. 'https://min-api.cryptocompare.com/data/all/coinlist?summary=true'
  26. )
  27. const data = await f.json()
  28. if (data.Response == 'Success') {
  29. valutes.value = Object.keys(data.Data)
  30. }
  31. })
  32. function add() {
  33. const newTicker = {
  34. name: ticker.value.toUpperCase(),
  35. price: '-',
  36. }
  37. tickers.value.push(newTicker)
  38. setInterval(async () => {
  39. const f = await fetch(
  40. `https://min-api.cryptocompare.com/data/price?fsym=${newTicker.name}&tsyms=USD&api_key=ce3fd966e7a1d10d65f907b20bf000552158fd3ed1bd614110baa0ac6cb57a7e`,
  41. )
  42. const data = await f.json()
  43. if (typeof data.USD != 'undefined') {
  44. if (sel.value?.name === newTicker.name) {
  45. graph.value.push(data.USD)
  46. }
  47. tickers.value.find(t => t.name === newTicker.name).price =
  48. data.USD > 1 ? data.USD.toFixed(2) : data.USD.toPrecision(2)
  49. }
  50. }, 3000)
  51. }
  52. function handleDelete(tickerToRemove) {
  53. tickers.value = tickers.value.filter(t => t != tickerToRemove)
  54. }
  55. const sel = ref(null)
  56. const graph = ref([])
  57. function normalizeGraph() {
  58. const maxValue = Math.max(...graph.value)
  59. const minValue = Math.min(...graph.value)
  60. return graph.value.map(
  61. price => 5 + ((price - minValue) * 95) / (maxValue - minValue),
  62. )
  63. }
  64. function select(ticker) {
  65. sel.value = ticker
  66. graph.value = []
  67. }
  68. </script>
  69. <template>
  70. <div class="container mx-auto flex flex-col items-center bg-gray-100 p-4">
  71. <!--<div class="fixed w-100 h-100 opacity-80 bg-purple-800 inset-0 z-50 flex items-center justify-center">
  72. <svg class="animate-spin -ml-1 mr-3 h-12 w-12 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
  73. <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
  74. <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
  75. </svg>
  76. </div> -->
  77. <div class="container">
  78. <section>
  79. <div class="flex">
  80. <div class="max-w-xs">
  81. <label for="wallet" class="block text-sm font-medium text-gray-700"
  82. >Тикер {{ ticker }}</label
  83. >
  84. <div class="mt-1 relative rounded-md shadow-md">
  85. <input
  86. v-model="ticker"
  87. v-on:keydown.enter="add"
  88. type="text"
  89. name="wallet"
  90. id="wallet"
  91. class="block w-full pr-10 border-gray-300 text-gray-900 focus:outline-none focus:ring-gray-500 focus:border-gray-500 sm:text-sm rounded-md"
  92. placeholder="Например DOGE"
  93. />
  94. </div>
  95. <div
  96. class="flex bg-white shadow-md p-1 rounded-md shadow-md flex-wrap"
  97. >
  98. <span
  99. class="inline-flex items-center px-2 m-1 rounded-md text-xs font-medium bg-gray-300 text-gray-800 cursor-pointer"
  100. >
  101. BTC
  102. </span>
  103. <span
  104. class="inline-flex items-center px-2 m-1 rounded-md text-xs font-medium bg-gray-300 text-gray-800 cursor-pointer"
  105. >
  106. DOGE
  107. </span>
  108. <span
  109. class="inline-flex items-center px-2 m-1 rounded-md text-xs font-medium bg-gray-300 text-gray-800 cursor-pointer"
  110. >
  111. BCH
  112. </span>
  113. <span
  114. class="inline-flex items-center px-2 m-1 rounded-md text-xs font-medium bg-gray-300 text-gray-800 cursor-pointer"
  115. >
  116. CHD
  117. </span>
  118. </div>
  119. <div class="text-sm text-red-600">Такой тикер уже добавлен</div>
  120. </div>
  121. </div>
  122. <button
  123. @click="add"
  124. type="button"
  125. class="my-4 inline-flex items-center py-2 px-4 border border-transparent shadow-sm text-sm leading-4 font-medium rounded-full text-white bg-gray-600 hover:bg-gray-700 transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
  126. >
  127. <!-- Heroicon name: solid/mail -->
  128. <svg
  129. class="-ml-0.5 mr-2 h-6 w-6"
  130. xmlns="http://www.w3.org/2000/svg"
  131. width="30"
  132. height="30"
  133. viewBox="0 0 24 24"
  134. fill="#ffffff"
  135. >
  136. <path
  137. d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"
  138. ></path>
  139. </svg>
  140. Добавить
  141. </button>
  142. </section>
  143. <hr
  144. v-if="tickers.length > 0"
  145. class="w-full border-t border-gray-600 my-4"
  146. />
  147. {{ sel }}
  148. <dl class="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
  149. <div
  150. v-for="t in tickers"
  151. :key="t"
  152. @click="sel = t"
  153. :class="sel == t ? 'border-4' : ''"
  154. class="bg-white overflow-hidden shadow rounded-lg border-purple-800 border-solid cursor-pointer"
  155. >
  156. <div class="px-4 py-5 sm:p-6 text-center">
  157. <dt class="text-sm font-medium text-gray-500 truncate">
  158. {{ t.name }} - USD
  159. </dt>
  160. <dd class="mt-1 text-3xl font-semibold text-gray-900">
  161. {{ t.price }}
  162. </dd>
  163. </div>
  164. <div class="w-full border-t border-gray-200"></div>
  165. <button
  166. @click.stop="handleDelete(t)"
  167. class="flex items-center justify-center font-medium w-full bg-gray-100 px-4 py-4 sm:px-6 text-md text-gray-500 hover:text-gray-600 hover:bg-gray-200 hover:opacity-20 transition-all focus:outline-none"
  168. >
  169. <svg
  170. class="h-5 w-5"
  171. xmlns="http://www.w3.org/2000/svg"
  172. viewBox="0 0 20 20"
  173. fill="#718096"
  174. aria-hidden="true"
  175. >
  176. <path
  177. fill-rule="evenodd"
  178. d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
  179. clip-rule="evenodd"
  180. ></path></svg
  181. >Удалить
  182. </button>
  183. </div>
  184. </dl>
  185. <hr
  186. v-if="tickers.length > 0"
  187. class="w-full border-t border-gray-600 my-4"
  188. />
  189. <section v-if="sel" class="relative">
  190. <h3 class="text-lg leading-6 font-medium text-gray-900 my-8">
  191. {{ sel.name }} - USD
  192. </h3>
  193. <div class="flex items-end border-gray-600 border-b border-l h-64">
  194. <div
  195. v-for="(bar, idx) in normalizeGraph()"
  196. :key="idx"
  197. :style="{ height: `${bar}%` }"
  198. class="bg-purple-800 border w-10"
  199. ></div>
  200. </div>
  201. <button
  202. @click="sel = null"
  203. type="button"
  204. class="absolute top-0 right-0"
  205. >
  206. <svg
  207. xmlns="http://www.w3.org/2000/svg"
  208. xmlns:xlink="http://www.w3.org/1999/xlink"
  209. xmlns:svgjs="http://svgjs.com/svgjs"
  210. version="1.1"
  211. width="30"
  212. height="30"
  213. x="0"
  214. y="0"
  215. viewBox="0 0 511.76 511.76"
  216. style="enable-background: new 0 0 512 512"
  217. xml:space="preserve"
  218. >
  219. <g>
  220. <path
  221. d="M436.896,74.869c-99.84-99.819-262.208-99.819-362.048,0c-99.797,99.819-99.797,262.229,0,362.048 c49.92,49.899,115.477,74.837,181.035,74.837s131.093-24.939,181.013-74.837C536.715,337.099,536.715,174.688,436.896,74.869z M361.461,331.317c8.341,8.341,8.341,21.824,0,30.165c-4.16,4.16-9.621,6.251-15.083,6.251c-5.461,0-10.923-2.091-15.083-6.251 l-75.413-75.435l-75.392,75.413c-4.181,4.16-9.643,6.251-15.083,6.251c-5.461,0-10.923-2.091-15.083-6.251 c-8.341-8.341-8.341-21.845,0-30.165l75.392-75.413l-75.413-75.413c-8.341-8.341-8.341-21.845,0-30.165 c8.32-8.341,21.824-8.341,30.165,0l75.413,75.413l75.413-75.413c8.341-8.341,21.824-8.341,30.165,0 c8.341,8.32,8.341,21.824,0,30.165l-75.413,75.413L361.461,331.317z"
  222. fill="#718096"
  223. data-original="#000000"
  224. ></path>
  225. </g>
  226. </svg>
  227. </button>
  228. </section>
  229. </div>
  230. </div>
  231. </template>