medicine.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. <template>
  2. <view class="container">
  3. <!-- 搜索栏 -->
  4. <view class="search-container">
  5. <view class="search-box">
  6. <image src="/static/icons/search.png" mode="aspectFit" class="search-icon"></image>
  7. <input
  8. type="text"
  9. placeholder="请输入药品名称或症状"
  10. v-model="searchKeyword"
  11. @input="handleInput"
  12. @confirm="searchMedicine"
  13. class="search-input"
  14. >
  15. <image
  16. src="/static/icons/voice.png"
  17. mode="aspectFit"
  18. class="voice-icon"
  19. @click="startVoiceSearch"
  20. ></image>
  21. </view>
  22. <view class="search-btn" @click="searchMedicine">搜索</view>
  23. </view>
  24. <!-- 热门搜索 -->
  25. <view class="hot-search">
  26. <text class="hot-title">热门搜索</text>
  27. <view class="hot-tags">
  28. <view class="tag" @click="selectHotTag('感冒')">感冒</view>
  29. <view class="tag" @click="selectHotTag('退烧药')">退烧药</view>
  30. <view class="tag" @click="selectHotTag('胃药')">胃药</view>
  31. <view class="tag" @click="selectHotTag('消炎药')">消炎药</view>
  32. <view class="tag" @click="selectHotTag('降压药')">降压药</view>
  33. </view>
  34. </view>
  35. <!-- 药品分类 -->
  36. <view class="category-container">
  37. <text class="category-title">药品分类</text>
  38. <scroll-view class="category-scroll" scroll-x="true">
  39. <view class="category-item" v-for="(category, index) in categories" :key="index"
  40. :class="{active: activeCategory === index}" @click="changeCategory(index)">
  41. <view class="category-icon">
  42. </view>
  43. <text>{{category.name}}</text>
  44. </view>
  45. </scroll-view>
  46. </view>
  47. <!-- 药品列表 -->
  48. <view class="medicine-list">
  49. <view class="list-header">
  50. <text class="list-title">推荐药品</text>
  51. <text class="view-all" @click="viewAllMedicine">查看全部</text>
  52. </view>
  53. <view class="medicine-cards">
  54. <view class="medicine-card" v-for="(medicine, index) in medicineList" :key="index" @click="goToMedicineDetail(medicine.id)">
  55. <view class="card-image">
  56. <image :src="medicine.image" mode="aspectFit"></image>
  57. <view class="badge" v-if="medicine.badge">{{medicine.badge}}</view>
  58. </view>
  59. <view class="card-content">
  60. <text class="card-title">{{medicine.name}}</text>
  61. <text class="card-subtitle">{{medicine.brand}}</text>
  62. <view class="card-info">
  63. <text class="card-price">¥{{medicine.price}}</text>
  64. <text class="card-rating">
  65. <image src="/static/icons/star.png" mode="aspectFit"></image>
  66. {{medicine.rating}}
  67. </text>
  68. </view>
  69. </view>
  70. </view>
  71. </view>
  72. </view>
  73. <!-- 搜索结果 -->
  74. <view class="search-results" v-show="showSearchResults">
  75. <view class="result-header">
  76. <text class="result-count">找到 {{searchResults.length}} 个结果</text>
  77. <text class="close-results" @click="closeSearchResults">关闭</text>
  78. </view>
  79. <view class="result-list">
  80. <view class="result-item" v-for="(result, index) in searchResults" :key="index" @click="goToMedicineDetail(result.id)">
  81. <image :src="result.image" mode="aspectFit" class="result-image"></image>
  82. <view class="result-content">
  83. <text class="result-title">{{result.name}}</text>
  84. <text class="result-subtitle">{{result.brand}}</text>
  85. <text class="result-price">¥{{result.price}}</text>
  86. </view>
  87. </view>
  88. </view>
  89. </view>
  90. <!-- 语音搜索对话框 -->
  91. <view class="voice-dialog" v-show="showVoiceDialog">
  92. <view class="dialog-mask" @click="closeVoiceDialog"></view>
  93. <view class="dialog-content">
  94. <text class="dialog-title">语音搜索</text>
  95. <view class="voice-icon-container">
  96. <image src="/static/icons/mic.png" mode="aspectFit" class="voice-mic"></image>
  97. </view>
  98. <text class="dialog-text">{{voiceText}}</text>
  99. <view class="dialog-buttons">
  100. <view class="cancel-btn" @click="closeVoiceDialog">取消</view>
  101. <view class="confirm-btn" @click="confirmVoiceSearch">搜索</view>
  102. </view>
  103. </view>
  104. </view>
  105. </view>
  106. </template>
  107. <script>
  108. export default {
  109. data() {
  110. return {
  111. searchKeyword: '',
  112. activeCategory: 0,
  113. showSearchResults: false,
  114. searchResults: [],
  115. showVoiceDialog: false,
  116. voiceText: '',
  117. // 药品分类数据
  118. categories: [
  119. { name: '全部' },
  120. { name: '感冒药'},
  121. { name: '退烧药' },
  122. { name: '胃药' },
  123. { name: '消炎药' },
  124. { name: '降压药' },
  125. { name: '维生素'},
  126. { name: '处方药'}
  127. ],
  128. // 药品列表数据
  129. medicineList: [
  130. {
  131. id: 1,
  132. name: '复方感冒灵颗粒',
  133. brand: '白云山',
  134. price: 19.8,
  135. rating: 4.8,
  136. image: 'https://ts1.tc.mm.bing.net/th/id/OIP-C.yRpM0KgEQAAoYchF1zOCugHaHa?w=176&h=185&c=8&rs=1&qlt=90&o=6&dpr=1.3&pid=3.1&rm=2',
  137. badge: '热销'
  138. },
  139. {
  140. id: 2,
  141. name: '布洛芬缓释胶囊',
  142. brand: '仁和',
  143. price: 25.5,
  144. rating: 4.7,
  145. image: 'https://ts1.tc.mm.bing.net/th/id/OIP-C.cTH6c2LYnbJa_trONrQ9ugHaHa?w=176&h=185&c=8&rs=1&qlt=90&o=6&dpr=1.3&pid=3.1&rm=2',
  146. badge: '推荐'
  147. },
  148. {
  149. id: 3,
  150. name: '奥美拉唑肠溶胶囊',
  151. brand: '修正',
  152. price: 32.0,
  153. rating: 4.9,
  154. image: 'https://ts1.tc.mm.bing.net/th/id/OIP-C.-noX8xNBWk0hg1PzApO7RwHaHa?w=176&h=185&c=8&rs=1&qlt=90&o=6&dpr=1.3&pid=3.1&rm=2'
  155. },
  156. {
  157. id: 4,
  158. name: '阿莫西林胶囊',
  159. brand: '三九',
  160. price: 28.5,
  161. rating: 4.6,
  162. image: 'https://ts1.tc.mm.bing.net/th/id/OIP-C.-IsXcevM3wDYT7pawrYzYgHaHa?w=176&h=185&c=8&rs=1&qlt=90&o=6&dpr=1.3&pid=3.1&rm=2',
  163. badge: '新品'
  164. },
  165. {
  166. id: 5,
  167. name: '硝苯地平缓释片',
  168. brand: '同仁堂',
  169. price: 45.0,
  170. rating: 4.8,
  171. image: 'https://ts1.tc.mm.bing.net/th/id/OIP-C.Ub5_rVws4JdzjnYw9R9k0AHaHa?w=176&h=185&c=8&rs=1&qlt=90&o=6&dpr=1.3&pid=3.1&rm=2'
  172. },
  173. {
  174. id: 6,
  175. name: '维生素C咀嚼片',
  176. brand: '汤臣倍健',
  177. price: 68.0,
  178. rating: 4.7,
  179. image: 'https://tse2-mm.cn.bing.net/th/id/OIP-C.12L6lrIdl2T1mqPeGWDc1gHaHa?w=203&h=203&c=7&r=0&o=5&dpr=1.3&pid=1.7'
  180. }
  181. ]
  182. }
  183. },
  184. methods: {
  185. // 处理输入事件
  186. handleInput() {
  187. // 实时搜索逻辑
  188. if (this.searchKeyword.length > 0) {
  189. this.searchMedicine()
  190. } else {
  191. this.showSearchResults = false
  192. }
  193. },
  194. // 搜索药品
  195. searchMedicine() {
  196. if (!this.searchKeyword.trim()) {
  197. uni.showToast({
  198. title: '请输入搜索关键词',
  199. icon: 'none'
  200. })
  201. return
  202. }
  203. // 模拟搜索结果
  204. this.searchResults = this.medicineList.filter(medicine =>
  205. medicine.name.includes(this.searchKeyword) ||
  206. medicine.brand.includes(this.searchKeyword)
  207. )
  208. this.showSearchResults = true
  209. },
  210. // 选择热门标签
  211. selectHotTag(tag) {
  212. this.searchKeyword = tag
  213. this.searchMedicine()
  214. },
  215. // 切换分类
  216. changeCategory(index) {
  217. this.activeCategory = index
  218. // 这里可以添加分类切换后的逻辑
  219. uni.showToast({
  220. title: `已切换到${this.categories[index].name}`,
  221. icon: 'none'
  222. })
  223. },
  224. // 查看全部药品
  225. viewAllMedicine() {
  226. uni.navigateTo({
  227. url: '/pages/medicine/all-medicine'
  228. })
  229. },
  230. // 跳转到药品详情页
  231. goToMedicineDetail(id) {
  232. uni.navigateTo({
  233. url: `/pages/medicine/detail?id=${id}`
  234. })
  235. },
  236. // 关闭搜索结果
  237. closeSearchResults() {
  238. this.showSearchResults = false
  239. },
  240. // 开始语音搜索
  241. startVoiceSearch() {
  242. this.showVoiceDialog = true
  243. this.voiceText = '正在识别...'
  244. // 模拟语音识别
  245. setTimeout(() => {
  246. this.voiceText = '布洛芬缓释胶囊'
  247. }, 1500)
  248. },
  249. // 关闭语音对话框
  250. closeVoiceDialog() {
  251. this.showVoiceDialog = false
  252. },
  253. // 确认语音搜索
  254. confirmVoiceSearch() {
  255. if (this.voiceText && this.voiceText !== '正在识别...') {
  256. this.searchKeyword = this.voiceText
  257. this.showVoiceDialog = false
  258. this.searchMedicine()
  259. }
  260. }
  261. },
  262. onLoad() {
  263. // 页面加载时的逻辑
  264. },
  265. onShow() {
  266. // 页面显示时的逻辑
  267. }
  268. }
  269. </script>
  270. <style>
  271. /* 全局样式 */
  272. page {
  273. background-color: #f5f5f5;
  274. font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;
  275. }
  276. .container {
  277. padding: 20rpx;
  278. }
  279. /* 搜索栏样式 */
  280. .search-container {
  281. display: flex;
  282. align-items: center;
  283. margin-bottom: 20rpx;
  284. }
  285. .search-box {
  286. flex: 1;
  287. height: 80rpx;
  288. background-color: #fff;
  289. border-radius: 40rpx;
  290. display: flex;
  291. align-items: center;
  292. padding: 0 20rpx;
  293. margin-right: 20rpx;
  294. }
  295. .search-icon {
  296. width: 36rpx;
  297. height: 36rpx;
  298. margin-right: 10rpx;
  299. }
  300. .search-input {
  301. flex: 1;
  302. height: 100%;
  303. font-size: 28rpx;
  304. }
  305. .voice-icon {
  306. width: 36rpx;
  307. height: 36rpx;
  308. margin-left: 10rpx;
  309. }
  310. .search-btn {
  311. width: 160rpx;
  312. height: 80rpx;
  313. background-color: #1677ff;
  314. color: #fff;
  315. text-align: center;
  316. line-height: 80rpx;
  317. border-radius: 40rpx;
  318. font-size: 30rpx;
  319. }
  320. /* 热门搜索样式 */
  321. .hot-search {
  322. margin-bottom: 30rpx;
  323. }
  324. .hot-title {
  325. font-size: 28rpx;
  326. color: #333;
  327. margin-bottom: 15rpx;
  328. }
  329. .hot-tags {
  330. display: flex;
  331. flex-wrap: wrap;
  332. }
  333. .tag {
  334. padding: 10rpx 20rpx;
  335. background-color: #f0f0f0;
  336. border-radius: 20rpx;
  337. font-size: 26rpx;
  338. color: #666;
  339. margin-right: 20rpx;
  340. margin-bottom: 15rpx;
  341. }
  342. .tag:hover {
  343. background-color: #e6f4ff;
  344. color: #1677ff;
  345. }
  346. /* 药品分类样式 */
  347. .category-container {
  348. margin-bottom: 30rpx;
  349. }
  350. .category-title {
  351. font-size: 28rpx;
  352. color: #333;
  353. margin-bottom: 15rpx;
  354. }
  355. .category-scroll {
  356. white-space: nowrap;
  357. padding-bottom: 10rpx;
  358. }
  359. .category-item {
  360. display: inline-block;
  361. text-align: center;
  362. margin-right: 40rpx;
  363. }
  364. .category-item.active text {
  365. color: #1677ff;
  366. }
  367. .category-icon {
  368. width: 80rpx;
  369. height: 15rpx;
  370. background-color: #f5f5f5;
  371. border-radius: 40rpx;
  372. display: flex;
  373. align-items: center;
  374. justify-content: center;
  375. margin-bottom: 10rpx;
  376. }
  377. .category-icon image {
  378. width: 48rpx;
  379. height: 48rpx;
  380. }
  381. .category-item text {
  382. font-size: 24rpx;
  383. color: #666;
  384. }
  385. /* 药品列表样式 */
  386. .medicine-list {
  387. margin-bottom: 30rpx;
  388. }
  389. .list-header {
  390. display: flex;
  391. justify-content: space-between;
  392. align-items: center;
  393. margin-bottom: 20rpx;
  394. }
  395. .list-title {
  396. font-size: 30rpx;
  397. font-weight: bold;
  398. color: #333;
  399. }
  400. .view-all {
  401. font-size: 26rpx;
  402. color: #1677ff;
  403. }
  404. .medicine-cards {
  405. display: flex;
  406. flex-wrap: wrap;
  407. justify-content: space-between;
  408. }
  409. .medicine-card {
  410. width: 345rpx;
  411. background-color: #fff;
  412. border-radius: 16rpx;
  413. margin-bottom: 20rpx;
  414. overflow: hidden;
  415. box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
  416. }
  417. .card-image {
  418. position: relative;
  419. width: 100%;
  420. height: 345rpx;
  421. overflow: hidden;
  422. }
  423. .card-image image {
  424. width: 100%;
  425. height: 100%;
  426. object-fit: cover;
  427. }
  428. .badge {
  429. position: absolute;
  430. top: 15rpx;
  431. left: 15rpx;
  432. background-color: #ff4d4f;
  433. color: #fff;
  434. font-size: 22rpx;
  435. padding: 5rpx 10rpx;
  436. border-radius: 8rpx;
  437. }
  438. .card-content {
  439. padding: 15rpx;
  440. }
  441. .card-title {
  442. font-size: 28rpx;
  443. font-weight: bold;
  444. color: #333;
  445. margin-bottom: 8rpx;
  446. display: -webkit-box;
  447. -webkit-box-orient: vertical;
  448. -webkit-line-clamp: 1;
  449. overflow: hidden;
  450. }
  451. .card-subtitle {
  452. font-size: 24rpx;
  453. color: #666;
  454. margin-bottom: 12rpx;
  455. }
  456. .card-info {
  457. display: flex;
  458. justify-content: space-between;
  459. align-items: center;
  460. }
  461. .card-price {
  462. font-size: 30rpx;
  463. font-weight: bold;
  464. color: #ff4d4f;
  465. }
  466. .card-rating {
  467. display: flex;
  468. align-items: center;
  469. font-size: 24rpx;
  470. color: #ff7d00;
  471. }
  472. .card-rating image {
  473. width: 24rpx;
  474. height: 24rpx;
  475. margin-right: 5rpx;
  476. }
  477. /* 搜索结果样式 */
  478. .search-results {
  479. position: fixed;
  480. top: 260rpx;
  481. left: 0;
  482. right: 0;
  483. bottom: 0;
  484. background-color: #fff;
  485. z-index: 100;
  486. padding: 20rpx;
  487. overflow-y: auto;
  488. }
  489. .result-header {
  490. display: flex;
  491. justify-content: space-between;
  492. align-items: center;
  493. margin-bottom: 20rpx;
  494. padding-bottom: 20rpx;
  495. border-bottom: 1rpx solid #eee;
  496. }
  497. .result-count {
  498. font-size: 28rpx;
  499. color: #333;
  500. }
  501. .close-results {
  502. font-size: 26rpx;
  503. color: #999;
  504. }
  505. .result-list {
  506. padding-bottom: 20rpx;
  507. }
  508. .result-item {
  509. display: flex;
  510. align-items: center;
  511. padding: 20rpx 0;
  512. border-bottom: 1rpx solid #eee;
  513. }
  514. .result-image {
  515. width: 120rpx;
  516. height: 120rpx;
  517. border-radius: 12rpx;
  518. margin-right: 20rpx;
  519. }
  520. .result-content {
  521. flex: 1;
  522. }
  523. .result-title {
  524. font-size: 28rpx;
  525. font-weight: bold;
  526. color: #333;
  527. margin-bottom: 8rpx;
  528. }
  529. .result-subtitle {
  530. font-size: 24rpx;
  531. color: #666;
  532. margin-bottom: 10rpx;
  533. }
  534. .result-price {
  535. font-size: 30rpx;
  536. font-weight: bold;
  537. color: #ff4d4f;
  538. }
  539. /* 语音搜索对话框样式 */
  540. .voice-dialog {
  541. position: fixed;
  542. top: 0;
  543. left: 0;
  544. right: 0;
  545. bottom: 0;
  546. z-index: 200;
  547. }
  548. .dialog-mask {
  549. position: absolute;
  550. top: 0;
  551. left: 0;
  552. right: 0;
  553. bottom: 0;
  554. background-color: rgba(0, 0, 0, 0.5);
  555. }
  556. .dialog-content {
  557. position: absolute;
  558. top: 50%;
  559. left: 50%;
  560. transform: translate(-50%, -50%);
  561. width: 600rpx;
  562. background-color: #fff;
  563. border-radius: 24rpx;
  564. padding: 40rpx;
  565. text-align: center;
  566. }
  567. .dialog-title {
  568. font-size: 32rpx;
  569. font-weight: bold;
  570. color: #333;
  571. margin-bottom: 40rpx;
  572. }
  573. .voice-icon-container {
  574. width: 180rpx;
  575. height: 180rpx;
  576. background-color: #e6f4ff;
  577. border-radius: 90rpx;
  578. display: flex;
  579. align-items: center;
  580. justify-content: center;
  581. margin: 0 auto 40rpx;
  582. }
  583. .voice-mic {
  584. width: 80rpx;
  585. height: 80rpx;
  586. }
  587. .dialog-text {
  588. font-size: 30rpx;
  589. color: #666;
  590. margin-bottom: 60rpx;
  591. }
  592. .dialog-buttons {
  593. display: flex;
  594. justify-content: space-between;
  595. align-items: center;
  596. }
  597. .cancel-btn {
  598. width: 250rpx;
  599. height: 80rpx;
  600. background-color: #f0f0f0;
  601. color: #666;
  602. text-align: center;
  603. line-height: 80rpx;
  604. border-radius: 40rpx;
  605. font-size: 30rpx;
  606. }
  607. .confirm-btn {
  608. width: 250rpx;
  609. height: 80rpx;
  610. background-color: #1677ff;
  611. color: #fff;
  612. text-align: center;
  613. line-height: 80rpx;
  614. border-radius: 40rpx;
  615. font-size: 30rpx;
  616. }
  617. </style>